bump product version to 6.3.0.0.beta1
[LibreOffice.git] / emfio / source / reader / emfreader.cxx
blob51a8a6fb029b9ba6974a47b2f5e9ba2e3bde856e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <emfreader.hxx>
21 #include <osl/endian.h>
22 #include <sal/log.hxx>
23 #include <osl/diagnose.h>
24 #include <basegfx/matrix/b2dhommatrix.hxx>
25 #include <vcl/dibtools.hxx>
26 #include <o3tl/safeint.hxx>
27 #include <tools/stream.hxx>
28 #include <memory>
30 #ifdef DBG_UTIL
31 #include <vcl/pngwrite.hxx>
32 #endif
34 using namespace std;
36 // GDI-Array
38 #define EMR_HEADER 1
39 #define EMR_POLYBEZIER 2
40 #define EMR_POLYGON 3
41 #define EMR_POLYLINE 4
42 #define EMR_POLYBEZIERTO 5
43 #define EMR_POLYLINETO 6
44 #define EMR_POLYPOLYLINE 7
45 #define EMR_POLYPOLYGON 8
46 #define EMR_SETWINDOWEXTEX 9
47 #define EMR_SETWINDOWORGEX 10
48 #define EMR_SETVIEWPORTEXTEX 11
49 #define EMR_SETVIEWPORTORGEX 12
50 #define EMR_SETBRUSHORGEX 13
51 #define EMR_EOF 14
52 #define EMR_SETPIXELV 15
53 #define EMR_SETMAPPERFLAGS 16
54 #define EMR_SETMAPMODE 17
55 #define EMR_SETBKMODE 18
56 #define EMR_SETPOLYFILLMODE 19
57 #define EMR_SETROP2 20
58 #define EMR_SETSTRETCHBLTMODE 21
59 #define EMR_SETTEXTALIGN 22
60 #define EMR_SETCOLORADJUSTMENT 23
61 #define EMR_SETTEXTCOLOR 24
62 #define EMR_SETBKCOLOR 25
63 #define EMR_OFFSETCLIPRGN 26
64 #define EMR_MOVETOEX 27
65 #define EMR_SETMETARGN 28
66 #define EMR_EXCLUDECLIPRECT 29
67 #define EMR_INTERSECTCLIPRECT 30
68 #define EMR_SCALEVIEWPORTEXTEX 31
69 #define EMR_SCALEWINDOWEXTEX 32
70 #define EMR_SAVEDC 33
71 #define EMR_RESTOREDC 34
72 #define EMR_SETWORLDTRANSFORM 35
73 #define EMR_MODIFYWORLDTRANSFORM 36
74 #define EMR_SELECTOBJECT 37
75 #define EMR_CREATEPEN 38
76 #define EMR_CREATEBRUSHINDIRECT 39
77 #define EMR_DELETEOBJECT 40
78 #define EMR_ANGLEARC 41
79 #define EMR_ELLIPSE 42
80 #define EMR_RECTANGLE 43
81 #define EMR_ROUNDRECT 44
82 #define EMR_ARC 45
83 #define EMR_CHORD 46
84 #define EMR_PIE 47
85 #define EMR_SELECTPALETTE 48
86 #define EMR_CREATEPALETTE 49
87 #define EMR_SETPALETTEENTRIES 50
88 #define EMR_RESIZEPALETTE 51
89 #define EMR_REALIZEPALETTE 52
90 #define EMR_EXTFLOODFILL 53
91 #define EMR_LINETO 54
92 #define EMR_ARCTO 55
93 #define EMR_POLYDRAW 56
94 #define EMR_SETARCDIRECTION 57
95 #define EMR_SETMITERLIMIT 58
96 #define EMR_BEGINPATH 59
97 #define EMR_ENDPATH 60
98 #define EMR_CLOSEFIGURE 61
99 #define EMR_FILLPATH 62
100 #define EMR_STROKEANDFILLPATH 63
101 #define EMR_STROKEPATH 64
102 #define EMR_FLATTENPATH 65
103 #define EMR_WIDENPATH 66
104 #define EMR_SELECTCLIPPATH 67
105 #define EMR_ABORTPATH 68
107 #define EMR_COMMENT 70 // Contains arbitrary private data.
108 // Comment Identifiers:
109 #define EMR_COMMENT_EMFPLUS 0x2B464D45 // Contains embedded EMF+ records.
110 #define EMR_COMMENT_EMFSPOOL 0x00000000 // Contains embedded EMFSPOOL records.
111 #define EMR_COMMENT_PUBLIC 0x43494447 // Specify extensions to EMF processing.
113 #define EMR_FILLRGN 71
114 #define EMR_FRAMERGN 72
115 #define EMR_INVERTRGN 73
116 #define EMR_PAINTRGN 74
117 #define EMR_EXTSELECTCLIPRGN 75
118 #define EMR_BITBLT 76
119 #define EMR_STRETCHBLT 77
120 #define EMR_MASKBLT 78
121 #define EMR_PLGBLT 79
122 #define EMR_SETDIBITSTODEVICE 80
123 #define EMR_STRETCHDIBITS 81
124 #define EMR_EXTCREATEFONTINDIRECTW 82
125 #define EMR_EXTTEXTOUTA 83
126 #define EMR_EXTTEXTOUTW 84
127 #define EMR_POLYBEZIER16 85
128 #define EMR_POLYGON16 86
129 #define EMR_POLYLINE16 87
130 #define EMR_POLYBEZIERTO16 88
131 #define EMR_POLYLINETO16 89
132 #define EMR_POLYPOLYLINE16 90
133 #define EMR_POLYPOLYGON16 91
134 #define EMR_POLYDRAW16 92
135 #define EMR_CREATEMONOBRUSH 93
136 #define EMR_CREATEDIBPATTERNBRUSHPT 94
137 #define EMR_EXTCREATEPEN 95
138 #define EMR_POLYTEXTOUTA 96
139 #define EMR_POLYTEXTOUTW 97
141 // WINDOWS VERSION >= 0x400
142 #define EMR_SETICMMODE 98
143 #define EMR_CREATECOLORSPACE 99
144 #define EMR_SETCOLORSPACE 100
145 #define EMR_DELETECOLORSPACE 101
146 #define EMR_GLSRECORD 102
147 #define EMR_GLSBOUNDEDRECORD 103
148 #define EMR_PIXELFORMAT 104
150 // WINDOWS VERSION >= 0x500
151 #define EMR_DRAWESCAPE 105
152 #define EMR_EXTESCAPE 106
153 #define EMR_STARTDOC 107
154 #define EMR_SMALLTEXTOUT 108
155 #define EMR_FORCEUFIMAPPING 109
156 #define EMR_NAMEDESCAPE 110
157 #define EMR_COLORCORRECTPALETTE 111
158 #define EMR_SETICMPROFILEA 112
159 #define EMR_SETICMPROFILEW 113
160 #define EMR_ALPHABLEND 114
161 #define EMR_ALPHADIBBLEND 115
162 #define EMR_TRANSPARENTBLT 116
163 #define EMR_TRANSPARENTDIB 117
164 #define EMR_GRADIENTFILL 118
165 #define EMR_SETLINKEDUFIS 119
166 #define EMR_SETTEXTJUSTIFICATION 120
168 namespace
171 const char *
172 record_type_name(sal_uInt32 nRecType)
174 #ifndef SAL_LOG_INFO
175 (void) nRecType;
176 return "";
177 #else
178 switch( nRecType )
180 case EMR_HEADER: return "HEADER";
181 case EMR_POLYBEZIER: return "POLYBEZIER";
182 case EMR_POLYGON: return "POLYGON";
183 case EMR_POLYLINE: return "POLYLINE";
184 case EMR_POLYBEZIERTO: return "POLYBEZIERTO";
185 case EMR_POLYLINETO: return "POLYLINETO";
186 case EMR_POLYPOLYLINE: return "POLYPOLYLINE";
187 case EMR_POLYPOLYGON: return "POLYPOLYGON";
188 case EMR_SETWINDOWEXTEX: return "SETWINDOWEXTEX";
189 case EMR_SETWINDOWORGEX: return "SETWINDOWORGEX";
190 case EMR_SETVIEWPORTEXTEX: return "SETVIEWPORTEXTEX";
191 case EMR_SETVIEWPORTORGEX: return "SETVIEWPORTORGEX";
192 case EMR_SETBRUSHORGEX: return "SETBRUSHORGEX";
193 case EMR_EOF: return "EOF";
194 case EMR_SETPIXELV: return "SETPIXELV";
195 case EMR_SETMAPPERFLAGS: return "SETMAPPERFLAGS";
196 case EMR_SETMAPMODE: return "SETMAPMODE";
197 case EMR_SETBKMODE: return "SETBKMODE";
198 case EMR_SETPOLYFILLMODE: return "SETPOLYFILLMODE";
199 case EMR_SETROP2: return "SETROP2";
200 case EMR_SETSTRETCHBLTMODE: return "SETSTRETCHBLTMODE";
201 case EMR_SETTEXTALIGN: return "SETTEXTALIGN";
202 case EMR_SETCOLORADJUSTMENT: return "SETCOLORADJUSTMENT";
203 case EMR_SETTEXTCOLOR: return "SETTEXTCOLOR";
204 case EMR_SETBKCOLOR: return "SETBKCOLOR";
205 case EMR_OFFSETCLIPRGN: return "OFFSETCLIPRGN";
206 case EMR_MOVETOEX: return "MOVETOEX";
207 case EMR_SETMETARGN: return "SETMETARGN";
208 case EMR_EXCLUDECLIPRECT: return "EXCLUDECLIPRECT";
209 case EMR_INTERSECTCLIPRECT: return "INTERSECTCLIPRECT";
210 case EMR_SCALEVIEWPORTEXTEX: return "SCALEVIEWPORTEXTEX";
211 case EMR_SCALEWINDOWEXTEX: return "SCALEWINDOWEXTEX";
212 case EMR_SAVEDC: return "SAVEDC";
213 case EMR_RESTOREDC: return "RESTOREDC";
214 case EMR_SETWORLDTRANSFORM: return "SETWORLDTRANSFORM";
215 case EMR_MODIFYWORLDTRANSFORM: return "MODIFYWORLDTRANSFORM";
216 case EMR_SELECTOBJECT: return "SELECTOBJECT";
217 case EMR_CREATEPEN: return "CREATEPEN";
218 case EMR_CREATEBRUSHINDIRECT: return "CREATEBRUSHINDIRECT";
219 case EMR_DELETEOBJECT: return "DELETEOBJECT";
220 case EMR_ANGLEARC: return "ANGLEARC";
221 case EMR_ELLIPSE: return "ELLIPSE";
222 case EMR_RECTANGLE: return "RECTANGLE";
223 case EMR_ROUNDRECT: return "ROUNDRECT";
224 case EMR_ARC: return "ARC";
225 case EMR_CHORD: return "CHORD";
226 case EMR_PIE: return "PIE";
227 case EMR_SELECTPALETTE: return "SELECTPALETTE";
228 case EMR_CREATEPALETTE: return "CREATEPALETTE";
229 case EMR_SETPALETTEENTRIES: return "SETPALETTEENTRIES";
230 case EMR_RESIZEPALETTE: return "RESIZEPALETTE";
231 case EMR_REALIZEPALETTE: return "REALIZEPALETTE";
232 case EMR_EXTFLOODFILL: return "EXTFLOODFILL";
233 case EMR_LINETO: return "LINETO";
234 case EMR_ARCTO: return "ARCTO";
235 case EMR_POLYDRAW: return "POLYDRAW";
236 case EMR_SETARCDIRECTION: return "SETARCDIRECTION";
237 case EMR_SETMITERLIMIT: return "SETMITERLIMIT";
238 case EMR_BEGINPATH: return "BEGINPATH";
239 case EMR_ENDPATH: return "ENDPATH";
240 case EMR_CLOSEFIGURE: return "CLOSEFIGURE";
241 case EMR_FILLPATH: return "FILLPATH";
242 case EMR_STROKEANDFILLPATH: return "STROKEANDFILLPATH";
243 case EMR_STROKEPATH: return "STROKEPATH";
244 case EMR_FLATTENPATH: return "FLATTENPATH";
245 case EMR_WIDENPATH: return "WIDENPATH";
246 case EMR_SELECTCLIPPATH: return "SELECTCLIPPATH";
247 case EMR_ABORTPATH: return "ABORTPATH";
248 case EMR_COMMENT: return "COMMENT";
249 case EMR_FILLRGN: return "FILLRGN";
250 case EMR_FRAMERGN: return "FRAMERGN";
251 case EMR_INVERTRGN: return "INVERTRGN";
252 case EMR_PAINTRGN: return "PAINTRGN";
253 case EMR_EXTSELECTCLIPRGN: return "EXTSELECTCLIPRGN";
254 case EMR_BITBLT: return "BITBLT";
255 case EMR_STRETCHBLT: return "STRETCHBLT";
256 case EMR_MASKBLT: return "MASKBLT";
257 case EMR_PLGBLT: return "PLGBLT";
258 case EMR_SETDIBITSTODEVICE: return "SETDIBITSTODEVICE";
259 case EMR_STRETCHDIBITS: return "STRETCHDIBITS";
260 case EMR_EXTCREATEFONTINDIRECTW: return "EXTCREATEFONTINDIRECTW";
261 case EMR_EXTTEXTOUTA: return "EXTTEXTOUTA";
262 case EMR_EXTTEXTOUTW: return "EXTTEXTOUTW";
263 case EMR_POLYBEZIER16: return "POLYBEZIER16";
264 case EMR_POLYGON16: return "POLYGON16";
265 case EMR_POLYLINE16: return "POLYLINE16";
266 case EMR_POLYBEZIERTO16: return "POLYBEZIERTO16";
267 case EMR_POLYLINETO16: return "POLYLINETO16";
268 case EMR_POLYPOLYLINE16: return "POLYPOLYLINE16";
269 case EMR_POLYPOLYGON16: return "POLYPOLYGON16";
270 case EMR_POLYDRAW16: return "POLYDRAW16";
271 case EMR_CREATEMONOBRUSH: return "CREATEMONOBRUSH";
272 case EMR_CREATEDIBPATTERNBRUSHPT: return "CREATEDIBPATTERNBRUSHPT";
273 case EMR_EXTCREATEPEN: return "EXTCREATEPEN";
274 case EMR_POLYTEXTOUTA: return "POLYTEXTOUTA";
275 case EMR_POLYTEXTOUTW: return "POLYTEXTOUTW";
276 case EMR_SETICMMODE: return "SETICMMODE";
277 case EMR_CREATECOLORSPACE: return "CREATECOLORSPACE";
278 case EMR_SETCOLORSPACE: return "SETCOLORSPACE";
279 case EMR_DELETECOLORSPACE: return "DELETECOLORSPACE";
280 case EMR_GLSRECORD: return "GLSRECORD";
281 case EMR_GLSBOUNDEDRECORD: return "GLSBOUNDEDRECORD";
282 case EMR_PIXELFORMAT: return "PIXELFORMAT";
283 case EMR_DRAWESCAPE: return "DRAWESCAPE";
284 case EMR_EXTESCAPE: return "EXTESCAPE";
285 case EMR_STARTDOC: return "STARTDOC";
286 case EMR_SMALLTEXTOUT: return "SMALLTEXTOUT";
287 case EMR_FORCEUFIMAPPING: return "FORCEUFIMAPPING";
288 case EMR_NAMEDESCAPE: return "NAMEDESCAPE";
289 case EMR_COLORCORRECTPALETTE: return "COLORCORRECTPALETTE";
290 case EMR_SETICMPROFILEA: return "SETICMPROFILEA";
291 case EMR_SETICMPROFILEW: return "SETICMPROFILEW";
292 case EMR_ALPHABLEND: return "ALPHABLEND";
293 case EMR_ALPHADIBBLEND: return "ALPHADIBBLEND";
294 case EMR_TRANSPARENTBLT: return "TRANSPARENTBLT";
295 case EMR_TRANSPARENTDIB: return "TRANSPARENTDIB";
296 case EMR_GRADIENTFILL: return "GRADIENTFILL";
297 case EMR_SETLINKEDUFIS: return "SETLINKEDUFIS";
298 case EMR_SETTEXTJUSTIFICATION: return "SETTEXTJUSTIFICATION";
299 default:
300 // Yes, return a pointer to a static buffer. This is a very
301 // local debugging output function, so no big deal.
302 static char buffer[11];
303 sprintf(buffer, "0x%08" SAL_PRIxUINT32, nRecType);
304 return buffer;
306 #endif
309 struct BLENDFUNCTION
311 unsigned char aBlendOperation;
312 unsigned char aBlendFlags;
313 unsigned char aSrcConstantAlpha;
314 unsigned char aAlphaFormat;
316 friend SvStream& operator>>(SvStream& rInStream, BLENDFUNCTION& rBlendFun);
319 SvStream& operator>>(SvStream& rInStream, BLENDFUNCTION& rBlendFun)
321 rInStream.ReadUChar(rBlendFun.aBlendOperation);
322 rInStream.ReadUChar(rBlendFun.aBlendFlags);
323 rInStream.ReadUChar(rBlendFun.aSrcConstantAlpha);
324 rInStream.ReadUChar(rBlendFun.aAlphaFormat);
325 return rInStream;
328 bool ImplReadRegion( tools::PolyPolygon& rPolyPoly, SvStream& rStream, sal_uInt32 nLen )
330 if (nLen == 0)
331 return false;
333 sal_uInt32 nHdSize, nType, nCount, nRgnSize, i;
334 rStream.ReadUInt32(nHdSize);
335 rStream.ReadUInt32(nType);
336 rStream.ReadUInt32(nCount);
337 rStream.ReadUInt32(nRgnSize);
339 if (!rStream.good() || nCount == 0 || nType != RDH_RECTANGLES)
340 return false;
342 sal_uInt32 nSize;
343 if (o3tl::checked_multiply<sal_uInt32>(nCount, 16, nSize))
344 return false;
345 if (o3tl::checked_add<sal_uInt32>(nSize, nHdSize - 16, nSize))
346 return false;
347 if (nLen < nSize)
348 return false;
350 sal_Int32 nx1, ny1, nx2, ny2;
351 for (i = 0; i < nCount; i++)
353 rStream.ReadInt32(nx1);
354 rStream.ReadInt32(ny1);
355 rStream.ReadInt32(nx2);
356 rStream.ReadInt32(ny2);
358 tools::Rectangle aRectangle(Point(nx1, ny1), Point(nx2, ny2));
360 tools::Polygon aPolygon(aRectangle);
361 tools::PolyPolygon aPolyPolyOr1(aPolygon);
362 tools::PolyPolygon aPolyPolyOr2(rPolyPoly);
363 rPolyPoly.GetUnion(aPolyPolyOr1, aPolyPolyOr2);
364 rPolyPoly = aPolyPolyOr2;
366 return true;
369 } // anonymous namespace
371 namespace emfio
373 EmfReader::EmfReader(SvStream& rStream,GDIMetaFile& rGDIMetaFile)
374 : MtfTools(rGDIMetaFile, rStream)
375 , mnRecordCount(0)
376 , mbRecordPath(false)
377 , mbEMFPlus(false)
378 ,mbEMFPlusDualMode(false)
382 EmfReader::~EmfReader()
386 void EmfReader::ReadEMFPlusComment(sal_uInt32 length, bool& bHaveDC)
388 if (!mbEMFPlus)
390 PassEMFPlusHeaderInfo();
392 #if OSL_DEBUG_LEVEL > 1
393 // debug code - write the stream to debug file /tmp/emf-stream.emf
394 sal_uInt64 const pos = mpInputStream->Tell();
395 mpInputStream->Seek(0);
396 SvFileStream file( OUString( "/tmp/emf-stream.emf" ), StreamMode::WRITE | StreamMode::TRUNC );
398 mpInputStream->WriteStream(file);
399 file.Flush();
400 file.Close();
402 mpInputStream->Seek( pos );
403 #endif
407 mbEMFPlus = true;
408 sal_uInt64 const pos = mpInputStream->Tell();
409 auto buffer = std::make_unique<char[]>( length );
410 PassEMFPlus( buffer.get(), mpInputStream->ReadBytes(buffer.get(), length) );
411 buffer.reset();
412 mpInputStream->Seek( pos );
414 bHaveDC = false;
416 // skip in SeekRel if impossibly unavailable
417 sal_uInt32 nRemainder = length;
419 const size_t nRequiredHeaderSize = 12;
420 while (nRemainder >= nRequiredHeaderSize)
422 sal_uInt16 type(0), flags(0);
423 sal_uInt32 size(0), dataSize(0);
425 mpInputStream->ReadUInt16( type ).ReadUInt16( flags ).ReadUInt32( size ).ReadUInt32( dataSize );
426 nRemainder -= nRequiredHeaderSize;
428 SAL_INFO ("emfio", "\t\tEMF+ record type: " << std::hex << type << std::dec);
430 // Get Device Context
431 // TODO We should use EmfPlusRecordType::GetDC instead
432 if( type == 0x4004 )
434 bHaveDC = true;
435 SAL_INFO ("emfio", "\t\tEMF+ lock DC (device context)");
438 // look for the "dual mode" in header
439 // it indicates that either EMF or EMF+ records should be processed
440 // 0x4001 = EMF+ header
441 // flags & 1 = dual mode active
442 if ( type == 0x4001 && flags & 1 )
444 mbEMFPlusDualMode = true;
447 // Get the length of the remaining data of this record based
448 // on the alleged size
449 sal_uInt32 nRemainingRecordData = size >= nRequiredHeaderSize ?
450 size-nRequiredHeaderSize : 0;
451 // clip to available size
452 nRemainingRecordData = std::min(nRemainingRecordData, nRemainder);
453 mpInputStream->SeekRel(nRemainingRecordData);
454 nRemainder -= nRemainingRecordData;
456 mpInputStream->SeekRel(nRemainder);
459 // these are referenced from inside the templates
460 static SvStream& operator >> (SvStream& rStream, sal_Int16 &n)
462 return rStream.ReadInt16(n);
465 static SvStream& operator >> (SvStream& rStream, sal_Int32 &n)
467 return rStream.ReadInt32(n);
471 * Reads polygons from the stream.
472 * The \<class T> parameter is for the type of the points (sal_uInt32 or sal_uInt16).
473 * skipFirst: if the first point read is the 0th point or the 1st point in the array.
474 * */
475 template <class T>
476 tools::Polygon EmfReader::ReadPolygonWithSkip(const bool skipFirst, sal_uInt32 nNextPos)
478 sal_uInt32 nPoints(0), nStartIndex(0);
479 mpInputStream->SeekRel( 16 );
480 mpInputStream->ReadUInt32( nPoints );
481 if (skipFirst)
483 nPoints ++;
484 nStartIndex ++;
487 return ReadPolygon<T>(nStartIndex, nPoints, nNextPos);
491 * Reads polygons from the stream.
492 * The \<class T> parameter is for the type of the points
493 * nStartIndex: which is the starting index in the polygon of the first point read
494 * nPoints: number of points
495 * mpInputStream: the stream containing the polygons
496 * */
497 template <class T>
498 tools::Polygon EmfReader::ReadPolygon(sal_uInt32 nStartIndex, sal_uInt32 nPoints, sal_uInt32 nNextPos)
500 bool bRecordOk = nPoints <= SAL_MAX_UINT16;
501 SAL_WARN_IF(!bRecordOk, "emfio", "polygon record has more polygons than we can handle");
502 if (!bRecordOk || !nPoints)
503 return tools::Polygon();
505 auto nRemainingSize = std::min(nNextPos - mpInputStream->Tell(), mpInputStream->remainingSize());
506 auto nMaxPossiblePoints = nRemainingSize / (sizeof(T) * 2);
507 auto nPointCount = nPoints - nStartIndex;
508 if (nPointCount > nMaxPossiblePoints)
510 SAL_WARN("emfio", "polygon claims more points than record can provide, truncating");
511 nPoints = nMaxPossiblePoints + nStartIndex;
514 tools::Polygon aPolygon(nPoints);
515 for (sal_uInt32 i = nStartIndex ; i < nPoints && mpInputStream->good(); i++ )
517 T nX, nY;
518 *mpInputStream >> nX >> nY;
519 if (!mpInputStream->good())
521 SAL_WARN("emfio", "short read on polygon, truncating");
522 aPolygon.SetSize(i);
523 break;
525 aPolygon[ i ] = Point( nX, nY );
528 return aPolygon;
532 * Reads a polyline from the WMF file and draws it
533 * The \<class T> parameter refers to the type of the points. (e.g. sal_uInt16 or sal_uInt32)
534 * */
535 template <class T>
536 void EmfReader::ReadAndDrawPolyLine(sal_uInt32 nNextPos)
538 sal_uInt32 nPoints;
539 sal_uInt32 i, nNumberOfPolylines( 0 ), nCount( 0 );
540 mpInputStream->SeekRel( 0x10 ); // TODO Skipping Bounds. A 128-bit WMF RectL object (specifies the bounding rectangle in device units.)
541 mpInputStream->ReadUInt32( nNumberOfPolylines );
542 mpInputStream->ReadUInt32( nCount ); // total number of points in all polylines
543 const auto nEndPos = std::min(nNextPos, mnEndPos);
544 if (mpInputStream->Tell() >= nEndPos)
545 return;
547 // taking the amount of points of each polygon, retrieving the total number of points
548 if ( mpInputStream->good() &&
549 ( nNumberOfPolylines < SAL_MAX_UINT32 / sizeof( sal_uInt16 ) ) &&
550 ( nNumberOfPolylines * sizeof( sal_uInt16 ) ) <= ( nEndPos - mpInputStream->Tell() )
553 std::unique_ptr< sal_uInt32[] > pnPolylinePointCount( new sal_uInt32[ nNumberOfPolylines ] );
554 for ( i = 0; i < nNumberOfPolylines && mpInputStream->good(); i++ )
556 mpInputStream->ReadUInt32( nPoints );
557 pnPolylinePointCount[ i ] = nPoints;
559 // Get polyline points:
560 for ( i = 0; ( i < nNumberOfPolylines ) && mpInputStream->good(); i++ )
562 tools::Polygon aPolygon = ReadPolygon<T>(0, pnPolylinePointCount[i], nNextPos);
563 DrawPolyLine(aPolygon, false, mbRecordPath);
569 * Reads a poly polygon from the WMF file and draws it.
570 * The \<class T> parameter refers to the type of the points. (e.g. sal_uInt16 or sal_uInt32)
571 * */
572 template <class T>
573 void EmfReader::ReadAndDrawPolyPolygon(sal_uInt32 nNextPos)
575 sal_uInt32 nPoly(0), nGesPoints(0), nReadPoints(0);
576 mpInputStream->SeekRel( 0x10 );
577 // Number of polygons
578 mpInputStream->ReadUInt32( nPoly ).ReadUInt32( nGesPoints );
579 const auto nEndPos = std::min(nNextPos, mnEndPos);
580 if (mpInputStream->Tell() >= nEndPos)
581 return;
582 if (!mpInputStream->good())
583 return;
584 //check against numeric overflowing
585 if (nGesPoints >= SAL_MAX_UINT32 / sizeof(Point))
586 return;
587 if (nPoly >= SAL_MAX_UINT32 / sizeof(sal_uInt16))
588 return;
589 if (nPoly * sizeof(sal_uInt16) > nEndPos - mpInputStream->Tell())
590 return;
592 // Get number of points in each polygon
593 std::vector<sal_uInt16> aPoints(nPoly);
594 for (sal_uInt32 i = 0; i < nPoly && mpInputStream->good(); ++i)
596 sal_uInt32 nPoints(0);
597 mpInputStream->ReadUInt32( nPoints );
598 aPoints[i] = static_cast<sal_uInt16>(nPoints);
600 if ( mpInputStream->good() && ( nGesPoints * (sizeof(T)+sizeof(T)) ) <= ( nEndPos - mpInputStream->Tell() ) )
602 // Get polygon points
603 tools::PolyPolygon aPolyPoly(nPoly);
604 for (sal_uInt32 i = 0; i < nPoly && mpInputStream->good(); ++i)
606 const sal_uInt16 nPointCount(aPoints[i]);
607 std::vector<Point> aPtAry(nPointCount);
608 for (sal_uInt16 j = 0; j < nPointCount && mpInputStream->good(); ++j)
610 T nX(0), nY(0);
611 *mpInputStream >> nX >> nY;
612 aPtAry[j] = Point( nX, nY );
613 ++nReadPoints;
616 aPolyPoly.Insert(tools::Polygon(aPtAry.size(), aPtAry.data()));
619 DrawPolyPolygon(aPolyPoly, mbRecordPath);
622 OSL_ENSURE(nReadPoints == nGesPoints, "The number Points processed from EMR_POLYPOLYGON is unequal imported number (!)");
625 bool EmfReader::ReadEnhWMF()
627 sal_uInt32 nStretchBltMode = 0;
628 sal_uInt32 nNextPos(0),
629 nW(0), nH(0), nColor(0), nIndex(0),
630 nDat32(0), nNom1(0), nDen1(0), nNom2(0), nDen2(0);
631 sal_Int32 nX32(0), nY32(0), nx32(0), ny32(0);
633 bool bStatus = ReadHeader();
634 bool bHaveDC = false;
636 static bool bEnableEMFPlus = ( getenv( "EMF_PLUS_DISABLE" ) == nullptr );
638 while( bStatus && mnRecordCount-- && mpInputStream->good())
640 sal_uInt32 nRecType(0), nRecSize(0);
641 mpInputStream->ReadUInt32(nRecType).ReadUInt32(nRecSize);
643 if ( !mpInputStream->good() || ( nRecSize < 8 ) || ( nRecSize & 3 ) ) // Parameters are always divisible by 4
645 bStatus = false;
646 break;
649 auto nCurPos = mpInputStream->Tell();
651 if (mnEndPos < nCurPos - 8)
653 bStatus = false;
654 break;
657 const sal_uInt32 nMaxPossibleRecSize = mnEndPos - (nCurPos - 8);
658 if (nRecSize > nMaxPossibleRecSize)
660 bStatus = false;
661 break;
664 nNextPos = nCurPos + (nRecSize - 8);
666 if( !maBmpSaveList.empty()
667 && ( nRecType != EMR_STRETCHBLT )
668 && ( nRecType != EMR_STRETCHDIBITS )
670 ResolveBitmapActions( maBmpSaveList );
673 bool bFlag = false;
675 SAL_INFO ("emfio", "0x" << std::hex << (nNextPos - nRecSize) << "-0x" << nNextPos << " " << record_type_name(nRecType) << " size: " << nRecSize << std::dec);
677 if( bEnableEMFPlus && nRecType == EMR_COMMENT ) {
678 sal_uInt32 length;
680 mpInputStream->ReadUInt32( length );
682 SAL_INFO("emfio", "\tGDI comment, length: " << length);
684 if( mpInputStream->good() && length >= 4 && length <= mpInputStream->remainingSize() ) {
685 sal_uInt32 nCommentId;
687 mpInputStream->ReadUInt32( nCommentId );
689 SAL_INFO ("emfio", "\t\tbegin " << static_cast<char>(nCommentId & 0xff) << static_cast<char>((nCommentId & 0xff00) >> 8) << static_cast<char>((nCommentId & 0xff0000) >> 16) << static_cast<char>((nCommentId & 0xff000000) >> 24) << " id: 0x" << std::hex << nCommentId << std::dec);
691 if( nCommentId == EMR_COMMENT_EMFPLUS && nRecSize >= 12 )
693 // [MS-EMF] 2.3.3: DataSize includes both CommentIdentifier and CommentRecordParm fields.
694 // We have already read 4-byte CommentIdentifier, so reduce length appropriately
695 ReadEMFPlusComment( length-4, bHaveDC );
697 else if( nCommentId == EMR_COMMENT_PUBLIC && nRecSize >= 12 )
699 // TODO: ReadGDIComment()
701 else if( nCommentId == EMR_COMMENT_EMFSPOOL && nRecSize >= 12 )
703 // TODO Implement reading EMFSPOOL comment
706 else
708 SAL_INFO ("emfio", "\t\tunknown id: 0x" << std::hex << nCommentId << std::dec);
712 else if ( !bHaveDC && mbEMFPlusDualMode && nRecType != EMR_HEADER && nRecType != EMR_EOF )
714 // skip content (EMF record) in dual mode
715 // we process only EMR_COMMENT (see above) to access EMF+ data
716 // with 2 exceptions, according to EMF+ specification:
717 // EMR_HEADER and EMR_EOF
718 // if a device context is given (bHaveDC) process the following EMF record, too.
720 else if( !mbEMFPlus || bHaveDC || nRecType == EMR_EOF )
722 switch( nRecType )
724 case EMR_POLYBEZIERTO :
725 DrawPolyBezier(ReadPolygonWithSkip<sal_Int32>(true, nNextPos), true, mbRecordPath);
726 break;
727 case EMR_POLYBEZIER :
728 DrawPolyBezier(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), false, mbRecordPath);
729 break;
731 case EMR_POLYGON :
732 DrawPolygon(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), mbRecordPath);
733 break;
735 case EMR_POLYLINETO :
736 DrawPolyLine(ReadPolygonWithSkip<sal_Int32>(true, nNextPos), true, mbRecordPath);
737 break;
739 case EMR_POLYLINE :
740 DrawPolyLine(ReadPolygonWithSkip<sal_Int32>(false, nNextPos), false, mbRecordPath);
741 break;
743 case EMR_POLYPOLYLINE :
744 ReadAndDrawPolyLine<sal_Int32>(nNextPos);
745 break;
747 case EMR_POLYPOLYGON :
748 ReadAndDrawPolyPolygon<sal_Int32>(nNextPos);
749 break;
751 case EMR_SETWINDOWEXTEX :
753 sal_Int32 w = 0, h = 0;
754 mpInputStream->ReadInt32( w ).ReadInt32( h );
755 SetWinExt( Size( w, h ), true);
757 break;
759 case EMR_SETWINDOWORGEX :
761 mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
762 SetWinOrg( Point( nX32, nY32 ), true);
764 break;
766 case EMR_SCALEWINDOWEXTEX :
768 mpInputStream->ReadUInt32( nNom1 ).ReadUInt32( nDen1 ).ReadUInt32( nNom2 ).ReadUInt32( nDen2 );
769 if (nDen1 != 0 && nDen2 != 0)
770 ScaleWinExt( static_cast<double>(nNom1) / nDen1, static_cast<double>(nNom2) / nDen2 );
771 else
772 SAL_WARN("vcl.emf", "ignoring bogus divide by zero");
774 break;
776 case EMR_SETVIEWPORTORGEX :
778 mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
779 SetDevOrg( Point( nX32, nY32 ) );
781 break;
783 case EMR_SCALEVIEWPORTEXTEX :
785 mpInputStream->ReadUInt32( nNom1 ).ReadUInt32( nDen1 ).ReadUInt32( nNom2 ).ReadUInt32( nDen2 );
786 if (nDen1 != 0 && nDen2 != 0)
787 ScaleDevExt( static_cast<double>(nNom1) / nDen1, static_cast<double>(nNom2) / nDen2 );
788 else
789 SAL_WARN("vcl.emf", "ignoring bogus divide by zero");
791 break;
793 case EMR_SETVIEWPORTEXTEX :
795 sal_Int32 w = 0, h = 0;
796 mpInputStream->ReadInt32( w ).ReadInt32( h );
797 SetDevExt( Size( w, h ) );
799 break;
801 case EMR_EOF :
802 mnRecordCount = 0;
803 break;
805 case EMR_SETPIXELV :
807 mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
808 DrawPixel( Point( nX32, nY32 ), ReadColor() );
810 break;
812 case EMR_SETMAPMODE :
814 sal_uInt32 nMapMode;
815 mpInputStream->ReadUInt32( nMapMode );
816 SetMapMode( nMapMode );
818 break;
820 case EMR_SETBKMODE :
822 mpInputStream->ReadUInt32( nDat32 );
823 SetBkMode( static_cast<BkMode>(nDat32) );
825 break;
827 case EMR_SETPOLYFILLMODE :
828 break;
830 case EMR_SETROP2 :
832 mpInputStream->ReadUInt32( nDat32 );
833 SetRasterOp( static_cast<WMFRasterOp>(nDat32) );
835 break;
837 case EMR_SETSTRETCHBLTMODE :
839 mpInputStream->ReadUInt32( nStretchBltMode );
841 break;
843 case EMR_SETTEXTALIGN :
845 mpInputStream->ReadUInt32( nDat32 );
846 SetTextAlign( nDat32 );
848 break;
850 case EMR_SETTEXTCOLOR :
852 SetTextColor( ReadColor() );
854 break;
856 case EMR_SETBKCOLOR :
858 SetBkColor( ReadColor() );
860 break;
862 case EMR_OFFSETCLIPRGN :
864 mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
865 MoveClipRegion( Size( nX32, nY32 ) );
867 break;
869 case EMR_MOVETOEX :
871 mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
872 MoveTo( Point( nX32, nY32 ), mbRecordPath);
874 break;
876 case EMR_INTERSECTCLIPRECT :
878 mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
879 IntersectClipRect( ReadRectangle( nX32, nY32, nx32, ny32 ) );
881 break;
883 case EMR_SAVEDC :
885 Push();
887 break;
889 case EMR_RESTOREDC :
891 Pop();
893 break;
895 case EMR_SETWORLDTRANSFORM :
897 XForm aTempXForm;
898 *mpInputStream >> aTempXForm;
899 SetWorldTransform( aTempXForm );
901 break;
903 case EMR_MODIFYWORLDTRANSFORM :
905 sal_uInt32 nMode;
906 XForm aTempXForm;
907 *mpInputStream >> aTempXForm;
908 mpInputStream->ReadUInt32( nMode );
909 ModifyWorldTransform( aTempXForm, nMode );
911 break;
913 case EMR_SELECTOBJECT :
915 mpInputStream->ReadUInt32( nIndex );
916 SelectObject( nIndex );
918 break;
920 case EMR_CREATEPEN :
922 mpInputStream->ReadUInt32( nIndex );
923 if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
926 LineInfo aLineInfo;
927 sal_uInt32 nStyle;
928 Size aSize;
929 // #fdo39428 Remove SvStream operator>>(long&)
930 sal_Int32 nTmpW(0), nTmpH(0);
932 mpInputStream->ReadUInt32( nStyle ).ReadInt32( nTmpW ).ReadInt32( nTmpH );
933 aSize.setWidth( nTmpW );
934 aSize.setHeight( nTmpH );
936 if ( aSize.Width() )
937 aLineInfo.SetWidth( aSize.Width() );
939 bool bTransparent = false;
940 switch( nStyle & PS_STYLE_MASK )
942 case PS_DASHDOTDOT :
943 aLineInfo.SetStyle( LineStyle::Dash );
944 aLineInfo.SetDashCount( 1 );
945 aLineInfo.SetDotCount( 2 );
946 break;
947 case PS_DASHDOT :
948 aLineInfo.SetStyle( LineStyle::Dash );
949 aLineInfo.SetDashCount( 1 );
950 aLineInfo.SetDotCount( 1 );
951 break;
952 case PS_DOT :
953 aLineInfo.SetStyle( LineStyle::Dash );
954 aLineInfo.SetDashCount( 0 );
955 aLineInfo.SetDotCount( 1 );
956 break;
957 case PS_DASH :
958 aLineInfo.SetStyle( LineStyle::Dash );
959 aLineInfo.SetDashCount( 1 );
960 aLineInfo.SetDotCount( 0 );
961 break;
962 case PS_NULL :
963 bTransparent = true;
964 aLineInfo.SetStyle( LineStyle::NONE );
965 break;
966 case PS_INSIDEFRAME :
967 case PS_SOLID :
968 default :
969 aLineInfo.SetStyle( LineStyle::Solid );
971 switch( nStyle & PS_ENDCAP_STYLE_MASK )
973 case PS_ENDCAP_ROUND :
974 if ( aSize.Width() )
976 aLineInfo.SetLineCap( css::drawing::LineCap_ROUND );
977 break;
979 [[fallthrough]];
980 case PS_ENDCAP_SQUARE :
981 if ( aSize.Width() )
983 aLineInfo.SetLineCap( css::drawing::LineCap_SQUARE );
984 break;
986 [[fallthrough]];
987 case PS_ENDCAP_FLAT :
988 default :
989 aLineInfo.SetLineCap( css::drawing::LineCap_BUTT );
991 switch( nStyle & PS_JOIN_STYLE_MASK )
993 case PS_JOIN_ROUND :
994 aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Round );
995 break;
996 case PS_JOIN_MITER :
997 aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Miter );
998 break;
999 case PS_JOIN_BEVEL :
1000 aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Bevel );
1001 break;
1002 default :
1003 aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::NONE );
1005 CreateObjectIndexed(nIndex, std::make_unique<WinMtfLineStyle>( ReadColor(), aLineInfo, bTransparent ));
1008 break;
1010 case EMR_EXTCREATEPEN :
1012 sal_Int32 elpHatch;
1013 sal_uInt32 offBmi, cbBmi, offBits, cbBits, nStyle, nWidth, nBrushStyle, elpNumEntries;
1014 Color aColorRef;
1016 mpInputStream->ReadUInt32( nIndex );
1017 if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
1019 mpInputStream->ReadUInt32( offBmi ).ReadUInt32( cbBmi ).ReadUInt32( offBits ).ReadUInt32( cbBits ). ReadUInt32( nStyle ).ReadUInt32( nWidth ).ReadUInt32( nBrushStyle );
1020 aColorRef = ReadColor();
1021 mpInputStream->ReadInt32( elpHatch ).ReadUInt32( elpNumEntries );
1023 LineInfo aLineInfo;
1024 if ( nWidth )
1025 aLineInfo.SetWidth( nWidth );
1027 bool bTransparent = false;
1029 switch( nStyle & PS_STYLE_MASK )
1031 case PS_DASHDOTDOT :
1032 aLineInfo.SetStyle( LineStyle::Dash );
1033 aLineInfo.SetDashCount( 1 );
1034 aLineInfo.SetDotCount( 2 );
1035 break;
1036 case PS_DASHDOT :
1037 aLineInfo.SetStyle( LineStyle::Dash );
1038 aLineInfo.SetDashCount( 1 );
1039 aLineInfo.SetDotCount( 1 );
1040 break;
1041 case PS_DOT :
1042 aLineInfo.SetStyle( LineStyle::Dash );
1043 aLineInfo.SetDashCount( 0 );
1044 aLineInfo.SetDotCount( 1 );
1045 break;
1046 case PS_DASH :
1047 aLineInfo.SetStyle( LineStyle::Dash );
1048 aLineInfo.SetDashCount( 1 );
1049 aLineInfo.SetDotCount( 0 );
1050 break;
1051 case PS_NULL :
1052 bTransparent = true;
1053 aLineInfo.SetStyle( LineStyle::NONE );
1054 break;
1056 case PS_INSIDEFRAME :
1057 case PS_SOLID :
1058 default :
1059 aLineInfo.SetStyle( LineStyle::Solid );
1061 switch( nStyle & PS_ENDCAP_STYLE_MASK )
1063 case PS_ENDCAP_ROUND :
1064 if ( aLineInfo.GetWidth() )
1066 aLineInfo.SetLineCap( css::drawing::LineCap_ROUND );
1067 break;
1069 [[fallthrough]];
1070 case PS_ENDCAP_SQUARE :
1071 if ( aLineInfo.GetWidth() )
1073 aLineInfo.SetLineCap( css::drawing::LineCap_SQUARE );
1074 break;
1076 [[fallthrough]];
1077 case PS_ENDCAP_FLAT :
1078 default :
1079 aLineInfo.SetLineCap( css::drawing::LineCap_BUTT );
1081 switch( nStyle & PS_JOIN_STYLE_MASK )
1083 case PS_JOIN_ROUND :
1084 aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Round );
1085 break;
1086 case PS_JOIN_MITER :
1087 aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Miter );
1088 break;
1089 case PS_JOIN_BEVEL :
1090 aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::Bevel );
1091 break;
1092 default :
1093 aLineInfo.SetLineJoin ( basegfx::B2DLineJoin::NONE );
1095 CreateObjectIndexed(nIndex, std::make_unique<WinMtfLineStyle>( aColorRef, aLineInfo, bTransparent ));
1098 break;
1100 case EMR_CREATEBRUSHINDIRECT :
1102 sal_uInt32 nStyle;
1103 mpInputStream->ReadUInt32( nIndex );
1104 if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
1106 mpInputStream->ReadUInt32( nStyle );
1107 CreateObjectIndexed(nIndex, std::make_unique<WinMtfFillStyle>( ReadColor(), ( nStyle == BS_HOLLOW ) ));
1110 break;
1112 case EMR_DELETEOBJECT :
1114 mpInputStream->ReadUInt32( nIndex );
1115 if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
1116 DeleteObject( nIndex );
1118 break;
1120 case EMR_ELLIPSE :
1122 mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
1123 DrawEllipse( ReadRectangle( nX32, nY32, nx32, ny32 ) );
1125 break;
1127 case EMR_RECTANGLE :
1129 mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 );
1130 DrawRect( ReadRectangle( nX32, nY32, nx32, ny32 ) );
1132 break;
1134 case EMR_ROUNDRECT :
1136 mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nW ).ReadUInt32( nH );
1137 Size aSize( Size( nW, nH ) );
1138 DrawRoundRect( ReadRectangle( nX32, nY32, nx32, ny32 ), aSize );
1140 break;
1142 case EMR_ARC :
1144 sal_uInt32 nStartX, nStartY, nEndX, nEndY;
1145 mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nStartX ).ReadUInt32( nStartY ).ReadUInt32( nEndX ).ReadUInt32( nEndY );
1146 DrawArc( ReadRectangle( nX32, nY32, nx32, ny32 ), Point( nStartX, nStartY ), Point( nEndX, nEndY ) );
1148 break;
1150 case EMR_CHORD :
1152 sal_uInt32 nStartX, nStartY, nEndX, nEndY;
1153 mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nStartX ).ReadUInt32( nStartY ).ReadUInt32( nEndX ).ReadUInt32( nEndY );
1154 DrawChord( ReadRectangle( nX32, nY32, nx32, ny32 ), Point( nStartX, nStartY ), Point( nEndX, nEndY ) );
1156 break;
1158 case EMR_PIE :
1160 sal_uInt32 nStartX, nStartY, nEndX, nEndY;
1161 mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nStartX ).ReadUInt32( nStartY ).ReadUInt32( nEndX ).ReadUInt32( nEndY );
1162 const tools::Rectangle aRect( ReadRectangle( nX32, nY32, nx32, ny32 ));
1164 // #i73608# OutputDevice deviates from WMF
1165 // semantics. start==end means full ellipse here.
1166 if( nStartX == nEndX && nStartY == nEndY )
1167 DrawEllipse( aRect );
1168 else
1169 DrawPie( aRect, Point( nStartX, nStartY ), Point( nEndX, nEndY ) );
1171 break;
1173 case EMR_LINETO :
1175 mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 );
1176 LineTo( Point( nX32, nY32 ), mbRecordPath);
1178 break;
1180 case EMR_ARCTO :
1182 sal_uInt32 nStartX, nStartY, nEndX, nEndY;
1183 mpInputStream->ReadInt32( nX32 ).ReadInt32( nY32 ).ReadInt32( nx32 ).ReadInt32( ny32 ).ReadUInt32( nStartX ).ReadUInt32( nStartY ).ReadUInt32( nEndX ).ReadUInt32( nEndY );
1184 DrawArc( ReadRectangle( nX32, nY32, nx32, ny32 ), Point( nStartX, nStartY ), Point( nEndX, nEndY ), true );
1186 break;
1188 case EMR_BEGINPATH :
1190 ClearPath();
1191 mbRecordPath = true;
1193 break;
1195 case EMR_ABORTPATH :
1196 ClearPath();
1197 [[fallthrough]];
1198 case EMR_ENDPATH :
1199 mbRecordPath = false;
1200 break;
1202 case EMR_CLOSEFIGURE :
1203 ClosePath();
1204 break;
1206 case EMR_FILLPATH :
1207 StrokeAndFillPath( false, true );
1208 break;
1210 case EMR_STROKEANDFILLPATH :
1211 StrokeAndFillPath( true, true );
1212 break;
1214 case EMR_STROKEPATH :
1215 StrokeAndFillPath( true, false );
1216 break;
1218 case EMR_SELECTCLIPPATH :
1220 sal_Int32 nClippingMode(0);
1221 mpInputStream->ReadInt32(nClippingMode);
1222 SetClipPath(GetPathObj(), nClippingMode, true);
1224 break;
1226 case EMR_EXTSELECTCLIPRGN :
1228 sal_Int32 nClippingMode(0), cbRgnData(0);
1229 mpInputStream->ReadInt32(cbRgnData);
1230 mpInputStream->ReadInt32(nClippingMode);
1232 // This record's region data should be ignored if mode
1233 // is RGN_COPY - see EMF spec section 2.3.2.2
1234 if (nClippingMode == RGN_COPY)
1236 SetDefaultClipPath();
1238 else
1240 tools::PolyPolygon aPolyPoly;
1241 if (cbRgnData)
1242 ImplReadRegion(aPolyPoly, *mpInputStream, nRecSize);
1243 SetClipPath(aPolyPoly, nClippingMode, false);
1247 break;
1249 case EMR_ALPHABLEND:
1251 sal_Int32 xDest(0), yDest(0), cxDest(0), cyDest(0);
1253 BLENDFUNCTION aFunc;
1254 sal_Int32 xSrc(0), ySrc(0), cxSrc(0), cySrc(0);
1255 XForm xformSrc;
1256 sal_uInt32 BkColorSrc(0), iUsageSrc(0), offBmiSrc(0);
1257 sal_uInt32 cbBmiSrc(0), offBitsSrc(0), cbBitsSrc(0);
1259 sal_uInt32 nStart = mpInputStream->Tell() - 8;
1260 mpInputStream->SeekRel( 0x10 );
1262 mpInputStream->ReadInt32( xDest ).ReadInt32( yDest ).ReadInt32( cxDest ).ReadInt32( cyDest );
1263 *mpInputStream >> aFunc;
1264 mpInputStream->ReadInt32( xSrc ).ReadInt32( ySrc );
1265 *mpInputStream >> xformSrc;
1266 mpInputStream->ReadUInt32( BkColorSrc ).ReadUInt32( iUsageSrc ).ReadUInt32( offBmiSrc ).ReadUInt32( cbBmiSrc )
1267 .ReadUInt32( offBitsSrc ).ReadUInt32( cbBitsSrc ).ReadInt32( cxSrc ).ReadInt32( cySrc ) ;
1269 if ( (cbBitsSrc > (SAL_MAX_UINT32 - 14)) || ((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc) ||
1270 cxDest == SAL_MAX_INT32 || cyDest == SAL_MAX_INT32 )
1272 bStatus = false;
1274 else
1276 tools::Rectangle aRect(Point(xDest, yDest), Size(cxDest + 1, cyDest + 1));
1278 const sal_uInt32 nSourceSize = cbBmiSrc + cbBitsSrc + 14;
1279 bool bSafeRead = nSourceSize <= (mnEndPos - mnStartPos);
1280 sal_uInt32 nDeltaToDIB5HeaderSize(0);
1281 const bool bReadAlpha(0x01 == aFunc.aAlphaFormat);
1282 if (bSafeRead && bReadAlpha)
1284 // we need to read alpha channel data if AlphaFormat of BLENDFUNCTION is
1285 // AC_SRC_ALPHA (==0x01). To read it, create a temp DIB-File which is ready
1286 // for DIB-5 format
1287 const sal_uInt32 nHeaderSize = getDIBV5HeaderSize();
1288 if (cbBmiSrc > nHeaderSize)
1289 bSafeRead = false;
1290 else
1291 nDeltaToDIB5HeaderSize = nHeaderSize - cbBmiSrc;
1293 if (bSafeRead)
1295 const sal_uInt32 nTargetSize(cbBmiSrc + nDeltaToDIB5HeaderSize + cbBitsSrc + 14);
1296 char* pBuf = new char[ nTargetSize ];
1297 SvMemoryStream aTmp( pBuf, nTargetSize, StreamMode::READ | StreamMode::WRITE );
1299 aTmp.ObjectOwnsMemory( true );
1301 // write BM-Header (14 bytes)
1302 aTmp.WriteUChar( 'B' )
1303 .WriteUChar( 'M' )
1304 .WriteUInt32( cbBitsSrc )
1305 .WriteUInt16( 0 )
1306 .WriteUInt16( 0 )
1307 .WriteUInt32( cbBmiSrc + nDeltaToDIB5HeaderSize + 14 );
1309 // copy DIBInfoHeader from source (cbBmiSrc bytes)
1310 mpInputStream->Seek( nStart + offBmiSrc );
1311 mpInputStream->ReadBytes(pBuf + 14, cbBmiSrc);
1313 if (bReadAlpha)
1315 // need to add values for all stuff that DIBV5Header is bigger
1316 // than DIBInfoHeader, all values are correctly initialized to zero,
1317 // so we can use memset here
1318 memset(pBuf + cbBmiSrc + 14, 0, nDeltaToDIB5HeaderSize);
1321 // copy bitmap data from source (offBitsSrc bytes)
1322 mpInputStream->Seek( nStart + offBitsSrc );
1323 mpInputStream->ReadBytes(pBuf + 14 + nDeltaToDIB5HeaderSize + cbBmiSrc, cbBitsSrc);
1324 aTmp.Seek( 0 );
1326 // prepare to read and fill BitmapEx
1327 BitmapEx aBitmapEx;
1329 if(bReadAlpha)
1331 Bitmap aBitmap;
1332 AlphaMask aAlpha;
1334 if(ReadDIBV5(aBitmap, aAlpha, aTmp))
1336 aBitmapEx = BitmapEx(aBitmap, aAlpha);
1339 else
1341 Bitmap aBitmap;
1343 if(ReadDIB(aBitmap, aTmp, true))
1345 if(0xff != aFunc.aSrcConstantAlpha)
1347 // add const alpha channel
1348 aBitmapEx = BitmapEx(
1349 aBitmap,
1350 AlphaMask(aBitmap.GetSizePixel(), &aFunc.aSrcConstantAlpha));
1352 else
1354 // just use Bitmap
1355 aBitmapEx = BitmapEx(aBitmap);
1360 if(!aBitmapEx.IsEmpty())
1362 // test if it is sensible to crop
1363 if ( ( cxSrc > 0 ) && ( cySrc > 0 ) &&
1364 ( xSrc >= 0 ) && ( ySrc >= 0 ) &&
1365 ( xSrc + cxSrc < aBitmapEx.GetSizePixel().Width() ) &&
1366 ( ySrc + cySrc < aBitmapEx.GetSizePixel().Height() ) )
1368 const tools::Rectangle aCropRect( Point( xSrc, ySrc ), Size( cxSrc, cySrc ) );
1370 aBitmapEx.Crop( aCropRect );
1373 #ifdef DBG_UTIL
1374 static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
1376 if(bDoSaveForVisualControl)
1378 SvFileStream aNew("c:\\metafile_content.png", StreamMode::WRITE|StreamMode::TRUNC);
1379 vcl::PNGWriter aPNGWriter(aBitmapEx);
1380 aPNGWriter.Write(aNew);
1382 #endif
1383 maBmpSaveList.emplace_back(new BSaveStruct(aBitmapEx, aRect, SRCAND|SRCINVERT));
1388 break;
1390 case EMR_BITBLT : // PASSTHROUGH INTENDED
1391 case EMR_STRETCHBLT :
1393 sal_Int32 xDest, yDest, cxDest, cyDest, xSrc, ySrc, cxSrc, cySrc;
1394 sal_uInt32 dwRop, iUsageSrc, offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc;
1395 XForm xformSrc;
1397 sal_uInt32 nStart = mpInputStream->Tell() - 8;
1399 mpInputStream->SeekRel( 0x10 );
1400 mpInputStream->ReadInt32( xDest ).ReadInt32( yDest ).ReadInt32( cxDest ).ReadInt32( cyDest ).ReadUInt32( dwRop ).ReadInt32( xSrc ).ReadInt32( ySrc )
1401 >> xformSrc;
1402 mpInputStream->ReadUInt32( nColor ).ReadUInt32( iUsageSrc ).ReadUInt32( offBmiSrc ).ReadUInt32( cbBmiSrc )
1403 .ReadUInt32( offBitsSrc ).ReadUInt32( cbBitsSrc );
1405 if ( nRecType == EMR_STRETCHBLT )
1406 mpInputStream->ReadInt32( cxSrc ).ReadInt32( cySrc );
1407 else
1408 cxSrc = cySrc = 0;
1410 Bitmap aBitmap;
1411 tools::Rectangle aRect( Point( xDest, yDest ), Size( cxDest, cyDest ) );
1413 if (!mpInputStream->good() || (cbBitsSrc > (SAL_MAX_UINT32 - 14)) || ((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc))
1414 bStatus = false;
1415 else
1417 sal_uInt32 nSize = cbBmiSrc + cbBitsSrc + 14;
1418 if ( nSize <= ( mnEndPos - mnStartPos ) )
1420 char* pBuf = new char[ nSize ];
1421 SvMemoryStream aTmp( pBuf, nSize, StreamMode::READ | StreamMode::WRITE );
1422 aTmp.ObjectOwnsMemory( true );
1423 aTmp.WriteUChar( 'B' )
1424 .WriteUChar( 'M' )
1425 .WriteUInt32( cbBitsSrc )
1426 .WriteUInt16( 0 )
1427 .WriteUInt16( 0 )
1428 .WriteUInt32( cbBmiSrc + 14 );
1429 mpInputStream->Seek( nStart + offBmiSrc );
1430 mpInputStream->ReadBytes(pBuf + 14, cbBmiSrc);
1431 mpInputStream->Seek( nStart + offBitsSrc );
1432 mpInputStream->ReadBytes(pBuf + 14 + cbBmiSrc, cbBitsSrc);
1433 aTmp.Seek( 0 );
1434 ReadDIB(aBitmap, aTmp, true);
1436 // test if it is sensible to crop
1437 if ( (cxSrc > 0) && (cySrc > 0) &&
1438 (xSrc >= 0) && (ySrc >= 0) &&
1439 (aBitmap.GetSizePixel().Width() >= cxSrc) &&
1440 (xSrc <= aBitmap.GetSizePixel().Width() - cxSrc) &&
1441 (aBitmap.GetSizePixel().Height() >= cySrc) &&
1442 (ySrc <= aBitmap.GetSizePixel().Height() - cySrc) )
1444 tools::Rectangle aCropRect( Point( xSrc, ySrc ), Size( cxSrc, cySrc ) );
1445 aBitmap.Crop( aCropRect );
1448 maBmpSaveList.emplace_back(new BSaveStruct(aBitmap, aRect, dwRop));
1452 break;
1454 case EMR_STRETCHDIBITS :
1456 sal_Int32 xDest, yDest, xSrc, ySrc, cxSrc, cySrc, cxDest, cyDest;
1457 sal_uInt32 offBmiSrc, cbBmiSrc, offBitsSrc, cbBitsSrc, iUsageSrc, dwRop;
1458 sal_uInt32 nStart = mpInputStream->Tell() - 8;
1460 mpInputStream->SeekRel( 0x10 );
1461 mpInputStream->ReadInt32( xDest )
1462 .ReadInt32( yDest )
1463 .ReadInt32( xSrc )
1464 .ReadInt32( ySrc )
1465 .ReadInt32( cxSrc )
1466 .ReadInt32( cySrc )
1467 .ReadUInt32( offBmiSrc )
1468 .ReadUInt32( cbBmiSrc )
1469 .ReadUInt32( offBitsSrc )
1470 .ReadUInt32( cbBitsSrc )
1471 .ReadUInt32( iUsageSrc )
1472 .ReadUInt32( dwRop )
1473 .ReadInt32( cxDest )
1474 .ReadInt32( cyDest );
1476 if (!mpInputStream->good() ||
1477 ((SAL_MAX_UINT32 - 14) < cbBitsSrc) ||
1478 ((SAL_MAX_UINT32 - 14) - cbBitsSrc < cbBmiSrc))
1480 bStatus = false;
1482 else
1484 Bitmap aBitmap;
1485 tools::Rectangle aRect(xDest, yDest);
1486 aRect.SaturatingSetSize(Size(cxDest, cyDest));
1488 sal_uInt32 nSize = cbBmiSrc + cbBitsSrc + 14;
1489 if ( nSize <= ( mnEndPos - mnStartPos ) )
1491 char* pBuf = new char[ nSize ];
1492 SvMemoryStream aTmp( pBuf, nSize, StreamMode::READ | StreamMode::WRITE );
1493 aTmp.ObjectOwnsMemory( true );
1494 aTmp.WriteUChar( 'B' )
1495 .WriteUChar( 'M' )
1496 .WriteUInt32( cbBitsSrc )
1497 .WriteUInt16( 0 )
1498 .WriteUInt16( 0 )
1499 .WriteUInt32( cbBmiSrc + 14 );
1500 mpInputStream->Seek( nStart + offBmiSrc );
1501 mpInputStream->ReadBytes(pBuf + 14, cbBmiSrc);
1502 mpInputStream->Seek( nStart + offBitsSrc );
1503 mpInputStream->ReadBytes(pBuf + 14 + cbBmiSrc, cbBitsSrc);
1504 aTmp.Seek( 0 );
1505 ReadDIB(aBitmap, aTmp, true);
1507 // test if it is sensible to crop
1508 if ( (cxSrc > 0) && (cySrc > 0) &&
1509 (xSrc >= 0) && (ySrc >= 0) &&
1510 (aBitmap.GetSizePixel().Width() >= cxSrc) &&
1511 (xSrc <= aBitmap.GetSizePixel().Width() - cxSrc) &&
1512 (aBitmap.GetSizePixel().Height() >= cySrc) &&
1513 (ySrc <= aBitmap.GetSizePixel().Height() - cySrc) )
1515 tools::Rectangle aCropRect( Point( xSrc, ySrc ), Size( cxSrc, cySrc ) );
1516 aBitmap.Crop( aCropRect );
1518 maBmpSaveList.emplace_back(new BSaveStruct(aBitmap, aRect, dwRop));
1522 break;
1524 case EMR_EXTCREATEFONTINDIRECTW :
1526 mpInputStream->ReadUInt32( nIndex );
1527 if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
1529 LOGFONTW aLogFont;
1530 mpInputStream->ReadInt32( aLogFont.lfHeight )
1531 .ReadInt32( aLogFont.lfWidth )
1532 .ReadInt32( aLogFont.lfEscapement )
1533 .ReadInt32( aLogFont.lfOrientation )
1534 .ReadInt32( aLogFont.lfWeight )
1535 .ReadUChar( aLogFont.lfItalic )
1536 .ReadUChar( aLogFont.lfUnderline )
1537 .ReadUChar( aLogFont.lfStrikeOut )
1538 .ReadUChar( aLogFont.lfCharSet )
1539 .ReadUChar( aLogFont.lfOutPrecision )
1540 .ReadUChar( aLogFont.lfClipPrecision )
1541 .ReadUChar( aLogFont.lfQuality )
1542 .ReadUChar( aLogFont.lfPitchAndFamily );
1544 sal_Unicode lfFaceName[LF_FACESIZE+1];
1545 lfFaceName[LF_FACESIZE] = 0;
1546 for (int i = 0; i < LF_FACESIZE; ++i)
1548 sal_uInt16 nChar(0);
1549 mpInputStream->ReadUInt16(nChar);
1550 lfFaceName[i] = nChar;
1552 aLogFont.alfFaceName = OUString( lfFaceName );
1554 // #i123216# Not used in the test case of #121382# (always identity in XForm), also
1555 // no hints in ms docu if FontSize should be scaled with WT. Using with the example
1556 // from #i123216# creates errors, so removing.
1558 // // #i121382# Need to apply WorldTransform to FontHeight/Width; this should be completely
1559 // // changed to basegfx::B2DHomMatrix instead of 'struct XForm', but not now due to time
1560 // // constraints and dangers
1561 // const XForm& rXF = GetWorldTransform();
1562 // const basegfx::B2DHomMatrix aWT(rXF.eM11, rXF.eM21, rXF.eDx, rXF.eM12, rXF.eM22, rXF.eDy);
1563 // const basegfx::B2DVector aTransVec(aWT * basegfx::B2DVector(aLogFont.lfWidth, aLogFont.lfHeight));
1564 // aLogFont.lfWidth = aTransVec.getX();
1565 // aLogFont.lfHeight = aTransVec.getY();
1566 if (mpInputStream->good() && aLogFont.lfHeight != SAL_MIN_INT32 && aLogFont.lfWidth != SAL_MIN_INT32)
1568 CreateObjectIndexed(nIndex, std::make_unique<WinMtfFontStyle>( aLogFont ));
1572 break;
1574 case EMR_EXTTEXTOUTA :
1575 bFlag = true;
1576 [[fallthrough]];
1577 case EMR_EXTTEXTOUTW :
1579 sal_Int32 nLeft, nTop, nRight, nBottom, ptlReferenceX, ptlReferenceY, nGfxMode, nXScale, nYScale;
1580 sal_uInt32 nOffString, nOptions, offDx;
1581 sal_Int32 nLen;
1583 nCurPos = mpInputStream->Tell() - 8;
1585 mpInputStream->ReadInt32( nLeft ).ReadInt32( nTop ).ReadInt32( nRight ).ReadInt32( nBottom ).ReadInt32( nGfxMode ).ReadInt32( nXScale ).ReadInt32( nYScale )
1586 .ReadInt32( ptlReferenceX ).ReadInt32( ptlReferenceY ).ReadInt32( nLen ).ReadUInt32( nOffString ).ReadUInt32( nOptions );
1588 mpInputStream->SeekRel( 0x10 );
1589 mpInputStream->ReadUInt32( offDx );
1591 ComplexTextLayoutFlags nTextLayoutMode = ComplexTextLayoutFlags::Default;
1592 if ( nOptions & ETO_RTLREADING )
1593 nTextLayoutMode = ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft;
1594 SetTextLayoutMode( nTextLayoutMode );
1595 SAL_WARN_IF( ( nOptions & ( ETO_PDY | ETO_GLYPH_INDEX ) ) != 0, "emfio", "SJ: ETO_PDY || ETO_GLYPH_INDEX in EMF" );
1597 Point aPos( ptlReferenceX, ptlReferenceY );
1598 bool bLenSane = nLen > 0 && nLen < static_cast<sal_Int32>( SAL_MAX_UINT32 / sizeof(sal_Int32) );
1599 bool bOffStringSane = nOffString <= mnEndPos - nCurPos;
1600 if (bLenSane && bOffStringSane)
1602 mpInputStream->Seek( nCurPos + nOffString );
1603 OUString aText;
1604 if ( bFlag )
1606 if ( nLen <= static_cast<sal_Int32>( mnEndPos - mpInputStream->Tell() ) )
1608 std::unique_ptr<sal_Char[]> pBuf(new sal_Char[ nLen ]);
1609 mpInputStream->ReadBytes(pBuf.get(), nLen);
1610 aText = OUString(pBuf.get(), nLen, GetCharSet());
1613 else
1615 if ( ( nLen * sizeof(sal_Unicode) ) <= ( mnEndPos - mpInputStream->Tell() ) )
1617 aText = read_uInt16s_ToOUString(*mpInputStream, nLen);
1621 std::unique_ptr<long[]> pDXAry, pDYAry;
1623 sal_Int32 nDxSize;
1624 bool bOverflow = o3tl::checked_multiply<sal_Int32>(nLen, (nOptions & ETO_PDY) ? 8 : 4, nDxSize);
1625 if (!bOverflow && offDx && ((nCurPos + offDx + nDxSize) <= nNextPos ) && nNextPos <= mnEndPos)
1627 mpInputStream->Seek( nCurPos + offDx );
1628 pDXAry.reset( new long[aText.getLength()] );
1629 if (nOptions & ETO_PDY)
1631 pDYAry.reset( new long[aText.getLength()] );
1634 for (sal_Int32 i = 0; i < aText.getLength(); ++i)
1636 sal_Int32 nDxCount = 1;
1637 if (aText.getLength() != nLen)
1639 sal_Unicode cUniChar = aText[i];
1640 OString aTmp(&cUniChar, 1, GetCharSet());
1641 if (aTmp.getLength() > 1)
1643 nDxCount = aTmp.getLength();
1647 sal_Int32 nDx = 0, nDy = 0;
1648 while (nDxCount--)
1650 sal_Int32 nDxTmp = 0;
1651 mpInputStream->ReadInt32(nDxTmp);
1652 nDx += nDxTmp;
1653 if (nOptions & ETO_PDY)
1655 sal_Int32 nDyTmp = 0;
1656 mpInputStream->ReadInt32(nDyTmp);
1657 nDy += nDyTmp;
1661 pDXAry[i] = nDx;
1662 if (nOptions & ETO_PDY)
1664 pDYAry[i] = nDy;
1668 DrawText(aPos, aText, pDXAry.get(), pDYAry.get(), mbRecordPath, nGfxMode);
1671 break;
1673 case EMR_POLYBEZIERTO16 :
1674 DrawPolyBezier(ReadPolygonWithSkip<sal_Int16>(true, nNextPos), true, mbRecordPath);
1675 break;
1677 case EMR_POLYBEZIER16 :
1678 DrawPolyBezier(ReadPolygonWithSkip<sal_Int16>(false, nNextPos), false, mbRecordPath);
1679 break;
1681 case EMR_POLYGON16 :
1682 DrawPolygon(ReadPolygonWithSkip<sal_Int16>(false, nNextPos), mbRecordPath);
1683 break;
1685 case EMR_POLYLINETO16 :
1686 DrawPolyLine(ReadPolygonWithSkip<sal_Int16>(true, nNextPos), true, mbRecordPath);
1687 break;
1689 case EMR_POLYLINE16 :
1690 DrawPolyLine(ReadPolygonWithSkip<sal_Int16>(false, nNextPos), false, mbRecordPath);
1691 break;
1693 case EMR_POLYPOLYLINE16 :
1694 ReadAndDrawPolyLine<sal_Int16>(nNextPos);
1695 break;
1697 case EMR_POLYPOLYGON16 :
1698 ReadAndDrawPolyPolygon<sal_Int16>(nNextPos);
1699 break;
1701 case EMR_FILLRGN :
1703 sal_uInt32 nLen;
1704 tools::PolyPolygon aPolyPoly;
1705 mpInputStream->SeekRel( 0x10 );
1706 mpInputStream->ReadUInt32( nLen ).ReadUInt32( nIndex );
1708 if ( ImplReadRegion( aPolyPoly, *mpInputStream, nRecSize ) )
1710 Push();
1711 SelectObject( nIndex );
1712 DrawPolyPolygon( aPolyPoly );
1713 Pop();
1716 break;
1718 case EMR_CREATEDIBPATTERNBRUSHPT :
1720 sal_uInt32 nStart = mpInputStream->Tell() - 8;
1721 Bitmap aBitmap;
1723 mpInputStream->ReadUInt32( nIndex );
1725 if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
1727 sal_uInt32 usage, offBmi, cbBmi, offBits, cbBits;
1729 mpInputStream->ReadUInt32( usage );
1730 mpInputStream->ReadUInt32( offBmi );
1731 mpInputStream->ReadUInt32( cbBmi );
1732 mpInputStream->ReadUInt32( offBits );
1733 mpInputStream->ReadUInt32( cbBits );
1735 if ( (cbBits > (SAL_MAX_UINT32 - 14)) || ((SAL_MAX_UINT32 - 14) - cbBits < cbBmi) )
1736 bStatus = false;
1737 else if ( offBmi )
1739 sal_uInt32 nSize = cbBmi + cbBits + 14;
1740 if ( nSize <= ( mnEndPos - mnStartPos ) )
1742 char* pBuf = new char[ nSize ];
1744 SvMemoryStream aTmp( pBuf, nSize, StreamMode::READ | StreamMode::WRITE );
1745 aTmp.ObjectOwnsMemory( true );
1746 aTmp.WriteUChar( 'B' )
1747 .WriteUChar( 'M' )
1748 .WriteUInt32( cbBits )
1749 .WriteUInt16( 0 )
1750 .WriteUInt16( 0 )
1751 .WriteUInt32( cbBmi + 14 );
1752 mpInputStream->Seek( nStart + offBmi );
1753 mpInputStream->ReadBytes(pBuf + 14, cbBmi);
1754 mpInputStream->Seek( nStart + offBits );
1755 mpInputStream->ReadBytes(pBuf + 14 + cbBmi, cbBits);
1756 aTmp.Seek( 0 );
1757 ReadDIB(aBitmap, aTmp, true);
1762 CreateObjectIndexed(nIndex, std::make_unique<WinMtfFillStyle>( aBitmap ));
1764 break;
1766 case EMR_MASKBLT : SAL_INFO("emfio", "not implemented 'MaskBlt'"); break;
1767 case EMR_PLGBLT : SAL_INFO("emfio", "not implemented 'PlgBlt'"); break;
1768 case EMR_SETDIBITSTODEVICE : SAL_INFO("emfio", "not implemented 'SetDIBitsToDevice'"); break;
1769 case EMR_FRAMERGN : SAL_INFO("emfio", "not implemented 'FrameRgn'"); break;
1770 case EMR_INVERTRGN : SAL_INFO("emfio", "not implemented 'InvertRgn'"); break;
1771 case EMR_PAINTRGN : SAL_INFO("emfio", "not implemented 'PaintRgn'"); break;
1772 case EMR_FLATTENPATH : SAL_INFO("emfio", "not implemented 'FlattenPath'"); break;
1773 case EMR_WIDENPATH : SAL_INFO("emfio", "not implemented 'WidenPath'"); break;
1774 case EMR_POLYDRAW : SAL_INFO("emfio", "not implemented 'Polydraw'"); break;
1775 case EMR_SETARCDIRECTION : SAL_INFO("emfio", "not implemented 'SetArcDirection'"); break;
1776 case EMR_SETPALETTEENTRIES : SAL_INFO("emfio", "not implemented 'SetPaletteEntries'"); break;
1777 case EMR_RESIZEPALETTE : SAL_INFO("emfio", "not implemented 'ResizePalette'"); break;
1778 case EMR_EXTFLOODFILL : SAL_INFO("emfio", "not implemented 'ExtFloodFill'"); break;
1779 case EMR_ANGLEARC : SAL_INFO("emfio", "not implemented 'AngleArc'"); break;
1780 case EMR_SETCOLORADJUSTMENT : SAL_INFO("emfio", "not implemented 'SetColorAdjustment'"); break;
1781 case EMR_POLYDRAW16 : SAL_INFO("emfio", "not implemented 'PolyDraw16'"); break;
1782 case EMR_POLYTEXTOUTA : SAL_INFO("emfio", "not implemented 'PolyTextOutA'"); break;
1783 case EMR_POLYTEXTOUTW : SAL_INFO("emfio", "not implemented 'PolyTextOutW'"); break;
1784 case EMR_CREATECOLORSPACE : SAL_INFO("emfio", "not implemented 'CreateColorSpace'"); break;
1785 case EMR_SETCOLORSPACE : SAL_INFO("emfio", "not implemented 'SetColorSpace'"); break;
1786 case EMR_DELETECOLORSPACE : SAL_INFO("emfio", "not implemented 'DeleteColorSpace'"); break;
1787 case EMR_GLSRECORD : SAL_INFO("emfio", "not implemented 'GlsRecord'"); break;
1788 case EMR_GLSBOUNDEDRECORD : SAL_INFO("emfio", "not implemented 'GlsBoundRecord'"); break;
1789 case EMR_PIXELFORMAT : SAL_INFO("emfio", "not implemented 'PixelFormat'"); break;
1790 case EMR_DRAWESCAPE : SAL_INFO("emfio", "not implemented 'DrawEscape'"); break;
1791 case EMR_EXTESCAPE : SAL_INFO("emfio", "not implemented 'ExtEscape'"); break;
1792 case EMR_STARTDOC : SAL_INFO("emfio", "not implemented 'StartDoc'"); break;
1793 case EMR_SMALLTEXTOUT : SAL_INFO("emfio", "not implemented 'SmallTextOut'"); break;
1794 case EMR_FORCEUFIMAPPING : SAL_INFO("emfio", "not implemented 'ForceUFIMapping'"); break;
1795 case EMR_NAMEDESCAPE : SAL_INFO("emfio", "not implemented 'NamedEscape'"); break;
1796 case EMR_COLORCORRECTPALETTE : SAL_INFO("emfio", "not implemented 'ColorCorrectPalette'"); break;
1797 case EMR_SETICMPROFILEA : SAL_INFO("emfio", "not implemented 'SetICMProfileA'"); break;
1798 case EMR_SETICMPROFILEW : SAL_INFO("emfio", "not implemented 'SetICMProfileW'"); break;
1799 case EMR_TRANSPARENTBLT : SAL_INFO("emfio", "not implemented 'TransparenBlt'"); break;
1800 case EMR_TRANSPARENTDIB : SAL_INFO("emfio", "not implemented 'TransparenDib'"); break;
1801 case EMR_GRADIENTFILL : SAL_INFO("emfio", "not implemented 'GradientFill'"); break;
1802 case EMR_SETLINKEDUFIS : SAL_INFO("emfio", "not implemented 'SetLinkedUFIS'"); break;
1804 case EMR_SETMAPPERFLAGS : SAL_INFO("emfio", "not implemented 'SetMapperFlags'"); break;
1805 case EMR_SETICMMODE : SAL_INFO("emfio", "not implemented 'SetICMMode'"); break;
1806 case EMR_CREATEMONOBRUSH : SAL_INFO("emfio", "not implemented 'CreateMonoBrush'"); break;
1807 case EMR_SETBRUSHORGEX : SAL_INFO("emfio", "not implemented 'SetBrushOrgEx'"); break;
1808 case EMR_SETMETARGN : SAL_INFO("emfio", "not implemented 'SetMetArgn'"); break;
1809 case EMR_SETMITERLIMIT : SAL_INFO("emfio", "not implemented 'SetMiterLimit'"); break;
1810 case EMR_EXCLUDECLIPRECT : SAL_INFO("emfio", "not implemented 'ExcludeClipRect'"); break;
1811 case EMR_REALIZEPALETTE : SAL_INFO("emfio", "not implemented 'RealizePalette'"); break;
1812 case EMR_SELECTPALETTE : SAL_INFO("emfio", "not implemented 'SelectPalette'"); break;
1813 case EMR_CREATEPALETTE : SAL_INFO("emfio", "not implemented 'CreatePalette'"); break;
1814 case EMR_ALPHADIBBLEND : SAL_INFO("emfio", "not implemented 'AlphaDibBlend'"); break;
1815 case EMR_SETTEXTJUSTIFICATION : SAL_INFO("emfio", "not implemented 'SetTextJustification'"); break;
1817 case EMR_COMMENT :
1818 case EMR_HEADER : // has already been read at ReadHeader()
1819 break;
1821 default : SAL_INFO("emfio", "Unknown Meta Action"); break;
1824 mpInputStream->Seek( nNextPos );
1826 if( !maBmpSaveList.empty() )
1827 ResolveBitmapActions( maBmpSaveList );
1829 if ( bStatus )
1830 mpInputStream->Seek(mnEndPos);
1832 return bStatus;
1835 bool EmfReader::ReadHeader()
1837 // Spare me the METAFILEHEADER here
1838 // Reading the METAHEADER - EMR_HEADER ([MS-EMF] section 2.3.4.2 EMR_HEADER Record Types)
1839 sal_uInt32 nType(0), nHeaderSize(0);
1840 mpInputStream->ReadUInt32(nType).ReadUInt32(nHeaderSize);
1841 if (nType != 0x00000001)
1843 // per [MS-EMF] 2.3.4.2 EMF Header Record Types, type MUST be 0x00000001
1844 SAL_WARN("emfio", "EMF header type is not set to 0x00000001 - possibly corrupted file?");
1845 return false;
1848 // Start reading the EMR_HEADER Header object
1850 // bound size (RectL object, see [MS-WMF] section 2.2.2.19)
1851 tools::Rectangle rclBounds = ReadRectangle(); // rectangle in logical units
1853 // picture frame size (RectL object)
1854 tools::Rectangle rclFrame = ReadRectangle(); // rectangle in device units 1/100th mm
1856 sal_uInt32 nSignature(0);
1857 mpInputStream->ReadUInt32(nSignature);
1859 // nSignature MUST be the ASCII characters "FME", see [WS-EMF] 2.2.9 Header Object
1860 // and 2.1.14 FormatSignature Enumeration
1861 if (nSignature != 0x464d4520)
1863 SAL_WARN("emfio", "EMF\t\tSignature is not 0x464d4520 (\"FME\") - possibly corrupted file?");
1864 return false;
1867 sal_uInt32 nVersion(0);
1868 mpInputStream->ReadUInt32(nVersion); // according to [WS-EMF] 2.2.9, this SHOULD be 0x0001000, however
1869 // Microsoft note that not even Windows checks this...
1870 if (nVersion != 0x00010000)
1872 SAL_WARN("emfio", "EMF\t\tThis really should be 0x00010000, though not absolutely essential...");
1875 mpInputStream->ReadUInt32(mnEndPos); // size of metafile
1876 mnEndPos += mnStartPos;
1878 sal_uInt32 nStrmPos = mpInputStream->Tell(); // checking if mnEndPos is valid
1879 sal_uInt32 nActualFileSize = nStrmPos + mpInputStream->remainingSize();
1881 if ( nActualFileSize < mnEndPos )
1883 SAL_WARN("emfio", "EMF\t\tEMF Header object records number of bytes as " << mnEndPos
1884 << ", however the file size is actually " << nActualFileSize
1885 << " bytes. Possible file corruption?");
1886 mnEndPos = nActualFileSize;
1889 mpInputStream->ReadInt32(mnRecordCount);
1891 if (mnRecordCount <= 0)
1893 SAL_WARN("emfio", "EMF\t\tEMF Header object shows record counter as <= 0! This shouldn't "
1894 "be possible... indicator of possible file corruption?");
1895 return false;
1898 // the number of "handles", or graphics objects used in the metafile
1900 sal_uInt16 nHandlesCount;
1901 mpInputStream->ReadUInt16(nHandlesCount);
1903 // the next 2 bytes are reserved, but according to [MS-EMF] section 2.2.9
1904 // it MUST be 0x000 and MUST be ignored... the thing is, having such a specific
1905 // value is actually pretty useful in checking if there is possible corruption
1907 sal_uInt16 nReserved(0);
1908 mpInputStream->ReadUInt16(nReserved);
1910 if ( nReserved != 0x0000 )
1912 SAL_WARN("emfio", "EMF\t\tEMF Header object's reserved field is NOT 0x0000... possible "
1913 "corruption?");
1916 // The next 4 bytes specifies the number of characters in the metafile description.
1917 // The 4 bytes after that specific the offset from this record that contains the
1918 // metafile description... zero means no description string.
1919 // For now, we ignore it.
1921 mpInputStream->SeekRel(0x8);
1923 sal_uInt32 nPalEntries(0);
1924 mpInputStream->ReadUInt32(nPalEntries);
1925 sal_Int32 nPixX(0), nPixY(0), nMillX(0), nMillY(0);
1926 mpInputStream->ReadInt32(nPixX);
1927 mpInputStream->ReadInt32(nPixY);
1928 mpInputStream->ReadInt32(nMillX);
1929 mpInputStream->ReadInt32(nMillY);
1931 SetrclFrame(rclFrame);
1932 SetrclBounds(rclBounds);
1933 SetRefPix(Size( nPixX, nPixY ) );
1934 SetRefMill(Size( nMillX, nMillY ) );
1936 return checkSeek(*mpInputStream, mnStartPos + nHeaderSize);
1939 tools::Rectangle EmfReader::ReadRectangle()
1941 sal_Int32 nLeft, nTop, nRight, nBottom;
1942 mpInputStream->ReadInt32(nLeft);
1943 mpInputStream->ReadInt32(nTop);
1944 mpInputStream->ReadInt32(nRight);
1945 mpInputStream->ReadInt32(nBottom);
1946 return tools::Rectangle(nLeft, nTop, nRight, nBottom);
1949 tools::Rectangle EmfReader::ReadRectangle( sal_Int32 x1, sal_Int32 y1, sal_Int32 x2, sal_Int32 y2 )
1951 Point aTL(x1, y1);
1952 Point aBR(o3tl::saturating_add<sal_Int32>(x2, -1), o3tl::saturating_add<sal_Int32>(y2, -1));
1953 return tools::Rectangle(aTL, aBR);
1957 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */