1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
19 #include <basegfx/polygon/b2dpolygontools.hxx>
20 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
21 #include <emfreader.hxx>
22 #include <sal/log.hxx>
23 #include <osl/diagnose.h>
24 #include <vcl/dibtools.hxx>
25 #include <o3tl/safeint.hxx>
26 #include <o3tl/sprintf.hxx>
27 #include <tools/stream.hxx>
29 #include <unotools/configmgr.hxx>
30 #include <vcl/graph.hxx>
31 #include <vcl/pdfread.hxx>
32 #include <rtl/bootstrap.hxx>
35 #include <vcl/filter/PngImageWriter.hxx>
41 #define EMR_POLYBEZIER 2
43 #define EMR_POLYLINE 4
44 #define EMR_POLYBEZIERTO 5
45 #define EMR_POLYLINETO 6
46 #define EMR_POLYPOLYLINE 7
47 #define EMR_POLYPOLYGON 8
48 #define EMR_SETWINDOWEXTEX 9
49 #define EMR_SETWINDOWORGEX 10
50 #define EMR_SETVIEWPORTEXTEX 11
51 #define EMR_SETVIEWPORTORGEX 12
52 #define EMR_SETBRUSHORGEX 13
54 #define EMR_SETPIXELV 15
55 #define EMR_SETMAPPERFLAGS 16
56 #define EMR_SETMAPMODE 17
57 #define EMR_SETBKMODE 18
58 #define EMR_SETPOLYFILLMODE 19
59 #define EMR_SETROP2 20
60 #define EMR_SETSTRETCHBLTMODE 21
61 #define EMR_SETTEXTALIGN 22
62 #define EMR_SETCOLORADJUSTMENT 23
63 #define EMR_SETTEXTCOLOR 24
64 #define EMR_SETBKCOLOR 25
65 #define EMR_OFFSETCLIPRGN 26
66 #define EMR_MOVETOEX 27
67 #define EMR_SETMETARGN 28
68 #define EMR_EXCLUDECLIPRECT 29
69 #define EMR_INTERSECTCLIPRECT 30
70 #define EMR_SCALEVIEWPORTEXTEX 31
71 #define EMR_SCALEWINDOWEXTEX 32
73 #define EMR_RESTOREDC 34
74 #define EMR_SETWORLDTRANSFORM 35
75 #define EMR_MODIFYWORLDTRANSFORM 36
76 #define EMR_SELECTOBJECT 37
77 #define EMR_CREATEPEN 38
78 #define EMR_CREATEBRUSHINDIRECT 39
79 #define EMR_DELETEOBJECT 40
80 #define EMR_ANGLEARC 41
81 #define EMR_ELLIPSE 42
82 #define EMR_RECTANGLE 43
83 #define EMR_ROUNDRECT 44
87 #define EMR_SELECTPALETTE 48
88 #define EMR_CREATEPALETTE 49
89 #define EMR_SETPALETTEENTRIES 50
90 #define EMR_RESIZEPALETTE 51
91 #define EMR_REALIZEPALETTE 52
92 #define EMR_EXTFLOODFILL 53
95 #define EMR_POLYDRAW 56
96 #define EMR_SETARCDIRECTION 57
97 #define EMR_SETMITERLIMIT 58
98 #define EMR_BEGINPATH 59
99 #define EMR_ENDPATH 60
100 #define EMR_CLOSEFIGURE 61
101 #define EMR_FILLPATH 62
102 #define EMR_STROKEANDFILLPATH 63
103 #define EMR_STROKEPATH 64
104 #define EMR_FLATTENPATH 65
105 #define EMR_WIDENPATH 66
106 #define EMR_SELECTCLIPPATH 67
107 #define EMR_ABORTPATH 68
109 #define EMR_COMMENT 70 // Contains arbitrary private data.
110 // Comment Identifiers:
111 #define EMR_COMMENT_EMFPLUS 0x2B464D45 // Contains embedded EMF+ records.
112 #define EMR_COMMENT_EMFSPOOL 0x00000000 // Contains embedded EMFSPOOL records.
113 #define EMR_COMMENT_PUBLIC 0x43494447 // Specify extensions to EMF processing.
115 #define EMR_FILLRGN 71
116 #define EMR_FRAMERGN 72
117 #define EMR_INVERTRGN 73
118 #define EMR_PAINTRGN 74
119 #define EMR_EXTSELECTCLIPRGN 75
120 #define EMR_BITBLT 76
121 #define EMR_STRETCHBLT 77
122 #define EMR_MASKBLT 78
123 #define EMR_PLGBLT 79
124 #define EMR_SETDIBITSTODEVICE 80
125 #define EMR_STRETCHDIBITS 81
126 #define EMR_EXTCREATEFONTINDIRECTW 82
127 #define EMR_EXTTEXTOUTA 83
128 #define EMR_EXTTEXTOUTW 84
129 #define EMR_POLYBEZIER16 85
130 #define EMR_POLYGON16 86
131 #define EMR_POLYLINE16 87
132 #define EMR_POLYBEZIERTO16 88
133 #define EMR_POLYLINETO16 89
134 #define EMR_POLYPOLYLINE16 90
135 #define EMR_POLYPOLYGON16 91
136 #define EMR_POLYDRAW16 92
137 #define EMR_CREATEMONOBRUSH 93
138 #define EMR_CREATEDIBPATTERNBRUSHPT 94
139 #define EMR_EXTCREATEPEN 95
140 #define EMR_POLYTEXTOUTA 96
141 #define EMR_POLYTEXTOUTW 97
143 // WINDOWS VERSION >= 0x400
144 #define EMR_SETICMMODE 98
145 #define EMR_CREATECOLORSPACE 99
146 #define EMR_SETCOLORSPACE 100
147 #define EMR_DELETECOLORSPACE 101
148 #define EMR_GLSRECORD 102
149 #define EMR_GLSBOUNDEDRECORD 103
150 #define EMR_PIXELFORMAT 104
152 // WINDOWS VERSION >= 0x500
153 #define EMR_DRAWESCAPE 105
154 #define EMR_EXTESCAPE 106
155 #define EMR_STARTDOC 107
156 #define EMR_SMALLTEXTOUT 108
157 #define EMR_FORCEUFIMAPPING 109
158 #define EMR_NAMEDESCAPE 110
159 #define EMR_COLORCORRECTPALETTE 111
160 #define EMR_SETICMPROFILEA 112
161 #define EMR_SETICMPROFILEW 113
162 #define EMR_ALPHABLEND 114
163 #define EMR_ALPHADIBBLEND 115
164 #define EMR_TRANSPARENTBLT 116
165 #define EMR_TRANSPARENTDIB 117
166 #define EMR_GRADIENTFILL 118
167 #define EMR_SETLINKEDUFIS 119
168 #define EMR_SETTEXTJUSTIFICATION 120
170 #define PDF_SIGNATURE 0x50444620 // "PDF "
172 /* [MS-EMF] - v20210625 - page 28 */
173 constexpr sal_Int32 ARCDIRECTION_CLOCKWISE
= 0x00000002;
179 record_type_name(sal_uInt32 nRecType
)
187 case EMR_HEADER
: return "HEADER";
188 case EMR_POLYBEZIER
: return "POLYBEZIER";
189 case EMR_POLYGON
: return "POLYGON";
190 case EMR_POLYLINE
: return "POLYLINE";
191 case EMR_POLYBEZIERTO
: return "POLYBEZIERTO";
192 case EMR_POLYLINETO
: return "POLYLINETO";
193 case EMR_POLYPOLYLINE
: return "POLYPOLYLINE";
194 case EMR_POLYPOLYGON
: return "POLYPOLYGON";
195 case EMR_SETWINDOWEXTEX
: return "SETWINDOWEXTEX";
196 case EMR_SETWINDOWORGEX
: return "SETWINDOWORGEX";
197 case EMR_SETVIEWPORTEXTEX
: return "SETVIEWPORTEXTEX";
198 case EMR_SETVIEWPORTORGEX
: return "SETVIEWPORTORGEX";
199 case EMR_SETBRUSHORGEX
: return "SETBRUSHORGEX";
200 case EMR_EOF
: return "EOF";
201 case EMR_SETPIXELV
: return "SETPIXELV";
202 case EMR_SETMAPPERFLAGS
: return "SETMAPPERFLAGS";
203 case EMR_SETMAPMODE
: return "SETMAPMODE";
204 case EMR_SETBKMODE
: return "SETBKMODE";
205 case EMR_SETPOLYFILLMODE
: return "SETPOLYFILLMODE";
206 case EMR_SETROP2
: return "SETROP2";
207 case EMR_SETSTRETCHBLTMODE
: return "SETSTRETCHBLTMODE";
208 case EMR_SETTEXTALIGN
: return "SETTEXTALIGN";
209 case EMR_SETCOLORADJUSTMENT
: return "SETCOLORADJUSTMENT";
210 case EMR_SETTEXTCOLOR
: return "SETTEXTCOLOR";
211 case EMR_SETBKCOLOR
: return "SETBKCOLOR";
212 case EMR_OFFSETCLIPRGN
: return "OFFSETCLIPRGN";
213 case EMR_MOVETOEX
: return "MOVETOEX";
214 case EMR_SETMETARGN
: return "SETMETARGN";
215 case EMR_EXCLUDECLIPRECT
: return "EXCLUDECLIPRECT";
216 case EMR_INTERSECTCLIPRECT
: return "INTERSECTCLIPRECT";
217 case EMR_SCALEVIEWPORTEXTEX
: return "SCALEVIEWPORTEXTEX";
218 case EMR_SCALEWINDOWEXTEX
: return "SCALEWINDOWEXTEX";
219 case EMR_SAVEDC
: return "SAVEDC";
220 case EMR_RESTOREDC
: return "RESTOREDC";
221 case EMR_SETWORLDTRANSFORM
: return "SETWORLDTRANSFORM";
222 case EMR_MODIFYWORLDTRANSFORM
: return "MODIFYWORLDTRANSFORM";
223 case EMR_SELECTOBJECT
: return "SELECTOBJECT";
224 case EMR_CREATEPEN
: return "CREATEPEN";
225 case EMR_CREATEBRUSHINDIRECT
: return "CREATEBRUSHINDIRECT";
226 case EMR_DELETEOBJECT
: return "DELETEOBJECT";
227 case EMR_ANGLEARC
: return "ANGLEARC";
228 case EMR_ELLIPSE
: return "ELLIPSE";
229 case EMR_RECTANGLE
: return "RECTANGLE";
230 case EMR_ROUNDRECT
: return "ROUNDRECT";
231 case EMR_ARC
: return "ARC";
232 case EMR_CHORD
: return "CHORD";
233 case EMR_PIE
: return "PIE";
234 case EMR_SELECTPALETTE
: return "SELECTPALETTE";
235 case EMR_CREATEPALETTE
: return "CREATEPALETTE";
236 case EMR_SETPALETTEENTRIES
: return "SETPALETTEENTRIES";
237 case EMR_RESIZEPALETTE
: return "RESIZEPALETTE";
238 case EMR_REALIZEPALETTE
: return "REALIZEPALETTE";
239 case EMR_EXTFLOODFILL
: return "EXTFLOODFILL";
240 case EMR_LINETO
: return "LINETO";
241 case EMR_ARCTO
: return "ARCTO";
242 case EMR_POLYDRAW
: return "POLYDRAW";
243 case EMR_SETARCDIRECTION
: return "SETARCDIRECTION";
244 case EMR_SETMITERLIMIT
: return "SETMITERLIMIT";
245 case EMR_BEGINPATH
: return "BEGINPATH";
246 case EMR_ENDPATH
: return "ENDPATH";
247 case EMR_CLOSEFIGURE
: return "CLOSEFIGURE";
248 case EMR_FILLPATH
: return "FILLPATH";
249 case EMR_STROKEANDFILLPATH
: return "STROKEANDFILLPATH";
250 case EMR_STROKEPATH
: return "STROKEPATH";
251 case EMR_FLATTENPATH
: return "FLATTENPATH";
252 case EMR_WIDENPATH
: return "WIDENPATH";
253 case EMR_SELECTCLIPPATH
: return "SELECTCLIPPATH";
254 case EMR_ABORTPATH
: return "ABORTPATH";
255 case EMR_COMMENT
: return "COMMENT";
256 case EMR_FILLRGN
: return "FILLRGN";
257 case EMR_FRAMERGN
: return "FRAMERGN";
258 case EMR_INVERTRGN
: return "INVERTRGN";
259 case EMR_PAINTRGN
: return "PAINTRGN";
260 case EMR_EXTSELECTCLIPRGN
: return "EXTSELECTCLIPRGN";
261 case EMR_BITBLT
: return "BITBLT";
262 case EMR_STRETCHBLT
: return "STRETCHBLT";
263 case EMR_MASKBLT
: return "MASKBLT";
264 case EMR_PLGBLT
: return "PLGBLT";
265 case EMR_SETDIBITSTODEVICE
: return "SETDIBITSTODEVICE";
266 case EMR_STRETCHDIBITS
: return "STRETCHDIBITS";
267 case EMR_EXTCREATEFONTINDIRECTW
: return "EXTCREATEFONTINDIRECTW";
268 case EMR_EXTTEXTOUTA
: return "EXTTEXTOUTA";
269 case EMR_EXTTEXTOUTW
: return "EXTTEXTOUTW";
270 case EMR_POLYBEZIER16
: return "POLYBEZIER16";
271 case EMR_POLYGON16
: return "POLYGON16";
272 case EMR_POLYLINE16
: return "POLYLINE16";
273 case EMR_POLYBEZIERTO16
: return "POLYBEZIERTO16";
274 case EMR_POLYLINETO16
: return "POLYLINETO16";
275 case EMR_POLYPOLYLINE16
: return "POLYPOLYLINE16";
276 case EMR_POLYPOLYGON16
: return "POLYPOLYGON16";
277 case EMR_POLYDRAW16
: return "POLYDRAW16";
278 case EMR_CREATEMONOBRUSH
: return "CREATEMONOBRUSH";
279 case EMR_CREATEDIBPATTERNBRUSHPT
: return "CREATEDIBPATTERNBRUSHPT";
280 case EMR_EXTCREATEPEN
: return "EXTCREATEPEN";
281 case EMR_POLYTEXTOUTA
: return "POLYTEXTOUTA";
282 case EMR_POLYTEXTOUTW
: return "POLYTEXTOUTW";
283 case EMR_SETICMMODE
: return "SETICMMODE";
284 case EMR_CREATECOLORSPACE
: return "CREATECOLORSPACE";
285 case EMR_SETCOLORSPACE
: return "SETCOLORSPACE";
286 case EMR_DELETECOLORSPACE
: return "DELETECOLORSPACE";
287 case EMR_GLSRECORD
: return "GLSRECORD";
288 case EMR_GLSBOUNDEDRECORD
: return "GLSBOUNDEDRECORD";
289 case EMR_PIXELFORMAT
: return "PIXELFORMAT";
290 case EMR_DRAWESCAPE
: return "DRAWESCAPE";
291 case EMR_EXTESCAPE
: return "EXTESCAPE";
292 case EMR_STARTDOC
: return "STARTDOC";
293 case EMR_SMALLTEXTOUT
: return "SMALLTEXTOUT";
294 case EMR_FORCEUFIMAPPING
: return "FORCEUFIMAPPING";
295 case EMR_NAMEDESCAPE
: return "NAMEDESCAPE";
296 case EMR_COLORCORRECTPALETTE
: return "COLORCORRECTPALETTE";
297 case EMR_SETICMPROFILEA
: return "SETICMPROFILEA";
298 case EMR_SETICMPROFILEW
: return "SETICMPROFILEW";
299 case EMR_ALPHABLEND
: return "ALPHABLEND";
300 case EMR_ALPHADIBBLEND
: return "ALPHADIBBLEND";
301 case EMR_TRANSPARENTBLT
: return "TRANSPARENTBLT";
302 case EMR_TRANSPARENTDIB
: return "TRANSPARENTDIB";
303 case EMR_GRADIENTFILL
: return "GRADIENTFILL";
304 case EMR_SETLINKEDUFIS
: return "SETLINKEDUFIS";
305 case EMR_SETTEXTJUSTIFICATION
: return "SETTEXTJUSTIFICATION";
307 // Yes, return a pointer to a static buffer. This is a very
308 // local debugging output function, so no big deal.
309 static char buffer
[11];
310 o3tl::sprintf(buffer
, "0x%08" SAL_PRIxUINT32
, nRecType
);
318 unsigned char aBlendOperation
;
319 unsigned char aBlendFlags
;
320 unsigned char aSrcConstantAlpha
;
321 unsigned char aAlphaFormat
;
323 friend SvStream
& operator>>(SvStream
& rInStream
, BLENDFUNCTION
& rBlendFun
);
326 SvStream
& operator>>(SvStream
& rInStream
, BLENDFUNCTION
& rBlendFun
)
328 rInStream
.ReadUChar(rBlendFun
.aBlendOperation
);
329 rInStream
.ReadUChar(rBlendFun
.aBlendFlags
);
330 rInStream
.ReadUChar(rBlendFun
.aSrcConstantAlpha
);
331 rInStream
.ReadUChar(rBlendFun
.aAlphaFormat
);
335 bool ImplReadRegion( basegfx::B2DPolyPolygon
& rPolyPoly
, SvStream
& rStream
, sal_uInt32 nLen
, Point aWinOrg
)
337 if (nLen
< 32) // 32 bytes - Size of RegionDataHeader
340 sal_uInt32 nHdSize
, nType
, nCountRects
, nRgnSize
;
341 rStream
.ReadUInt32(nHdSize
);
342 rStream
.ReadUInt32(nType
);
343 rStream
.ReadUInt32(nCountRects
);
344 rStream
.ReadUInt32(nRgnSize
);
346 sal_Int32 nLeft
, nTop
, nRight
, nBottom
;
347 //bounds of the region
348 rStream
.ReadInt32(nLeft
);
349 rStream
.ReadInt32(nTop
);
350 rStream
.ReadInt32(nRight
);
351 rStream
.ReadInt32(nBottom
);
353 if (!rStream
.good() || nCountRects
== 0 || nType
!= emfio::RDH_RECTANGLES
)
356 SAL_INFO("emfio", "\t\tBounds Left: " << nLeft
<< ", top: " << nTop
<< ", right: " << nRight
<< ", bottom: " << nBottom
);
361 if (o3tl::checked_multiply
<sal_uInt32
>(nCountRects
, 16, nSize
))
366 for (sal_uInt32 i
= 0; i
< nCountRects
; ++i
)
368 rStream
.ReadInt32(nLeft
);
369 rStream
.ReadInt32(nTop
);
370 rStream
.ReadInt32(nRight
);
371 rStream
.ReadInt32(nBottom
);
372 nLeft
+= aWinOrg
.X();
373 nRight
+= aWinOrg
.X();
375 nBottom
+= aWinOrg
.Y();
376 rPolyPoly
.append( basegfx::utils::createPolygonFromRect( ::basegfx::B2DRectangle( nLeft
, nTop
, nRight
, nBottom
) ) );
377 SAL_INFO("emfio", "\t\t" << i
<< " Left: " << nLeft
<< ", top: " << nTop
<< ", right: " << nRight
<< ", bottom: " << nBottom
);
379 if (!utl::ConfigManager::IsFuzzing())
381 rPolyPoly
= basegfx::utils::solveCrossovers(rPolyPoly
);
382 rPolyPoly
= basegfx::utils::stripNeutralPolygons(rPolyPoly
);
383 rPolyPoly
= basegfx::utils::stripDispensablePolygons(rPolyPoly
);
388 } // anonymous namespace
392 EmfReader::EmfReader(SvStream
& rStream
,GDIMetaFile
& rGDIMetaFile
)
393 : MtfTools(rGDIMetaFile
, rStream
)
395 , mbRecordPath(false)
397 , mbEMFPlusDualMode(false)
401 EmfReader::~EmfReader()
405 const sal_uInt32 EMR_COMMENT_BEGINGROUP
= 0x00000002;
406 const sal_uInt32 EMR_COMMENT_ENDGROUP
= 0x00000003;
407 const sal_uInt32 EMR_COMMENT_MULTIFORMATS
= 0x40000004;
408 const sal_uInt32 EMR_COMMENT_WINDOWS_METAFILE
= 0x80000001;
410 void EmfReader::ReadGDIComment(sal_uInt32 nCommentId
)
412 sal_uInt32
nPublicCommentIdentifier(0);
413 mpInputStream
->ReadUInt32(nPublicCommentIdentifier
);
415 SAL_INFO("emfio", "\t\tEMR_COMMENT_PUBLIC, id: 0x" << std::hex
<< nCommentId
<< std::dec
);
416 switch (nPublicCommentIdentifier
)
418 case EMR_COMMENT_BEGINGROUP
:
420 SAL_INFO("emfio", "\t\t\tEMR_COMMENT_BEGINGROUP");
421 sal_uInt32 left
, top
, right
, bottom
;
422 mpInputStream
->ReadUInt32(left
).ReadUInt32(top
).ReadUInt32(right
).ReadUInt32(bottom
);
424 SAL_INFO("emfio", "\t\t\t\tBounding rect");
425 SAL_INFO("emfio", "\t\t\t\t\tLeft: " << left
);
426 SAL_INFO("emfio", "\t\t\t\t\tTop: " << top
);
427 SAL_INFO("emfio", "\t\t\t\t\tRight: " << right
);
428 SAL_INFO("emfio", "\t\t\t\t\tBottom: " << bottom
);
430 sal_uInt32
nDescChars(0);
431 mpInputStream
->ReadUInt32(nDescChars
);
434 for (sal_uInt32 i
=0; i
< nDescChars
; i
++)
437 mpInputStream
->ReadUInt16(cChar
);
441 sal_Unicode cUniChar
= static_cast<sal_Unicode
>(cChar
);
442 aDesc
= aDesc
+ OUStringChar(cUniChar
);
445 SAL_INFO("emfio", "\t\tDescription: " << aDesc
);
449 case EMR_COMMENT_ENDGROUP
:
450 SAL_INFO("emfio", "\t\t\tEMR_COMMENT_ENDGROUP");
453 case EMR_COMMENT_MULTIFORMATS
:
454 ReadMultiformatsComment();
457 case EMR_COMMENT_WINDOWS_METAFILE
:
458 SAL_WARN("emfio", "\t\tEMR_COMMENT_WINDOWS_METAFILE not implemented");
462 SAL_WARN("emfio", "\t\tEMR_COMMENT_PUBLIC not implemented, id: 0x" << std::hex
<< nCommentId
<< std::dec
);
467 void EmfReader::ReadMultiformatsComment()
469 tools::Rectangle aOutputRect
= EmfReader::ReadRectangle();
471 sal_uInt32
nCountFormats(0);
472 mpInputStream
->ReadUInt32(nCountFormats
);
473 if (nCountFormats
< 1)
478 // Read the first EmrFormat.
479 sal_uInt32
nSignature(0);
480 mpInputStream
->ReadUInt32(nSignature
);
481 if (nSignature
!= PDF_SIGNATURE
)
486 sal_uInt32
nVersion(0);
487 mpInputStream
->ReadUInt32(nVersion
);
493 sal_uInt32
nSizeData(0);
494 mpInputStream
->ReadUInt32(nSizeData
);
495 if (!nSizeData
|| nSizeData
> mpInputStream
->remainingSize())
500 sal_uInt32
nOffData(0);
501 mpInputStream
->ReadUInt32(nOffData
);
507 std::vector
<char> aPdfData(nSizeData
);
508 mpInputStream
->ReadBytes(aPdfData
.data(), aPdfData
.size());
509 if (!mpInputStream
->good())
514 SvMemoryStream aPdfStream
;
515 aPdfStream
.WriteBytes(aPdfData
.data(), aPdfData
.size());
518 if (!vcl::ImportPDF(aPdfStream
, aGraphic
))
523 // aGraphic will be the only output of the EMF parser, so its size hint can be the same as
525 aGraphic
.getVectorGraphicData()->setSizeHint(maSizeHint
);
527 maBmpSaveList
.emplace_back(
528 aGraphic
.GetBitmapEx(), aOutputRect
, SRCCOPY
, /*bForceAlpha=*/true);
529 const std::shared_ptr
<VectorGraphicData
> pVectorGraphicData
530 = aGraphic
.getVectorGraphicData();
531 if (!pVectorGraphicData
)
536 if (pVectorGraphicData
->getType() != VectorGraphicDataType::Pdf
)
541 mbReadOtherGraphicFormat
= true;
544 void EmfReader::ReadEMFPlusComment(sal_uInt32 length
, bool& bHaveDC
)
548 PassEMFPlusHeaderInfo();
550 #if OSL_DEBUG_LEVEL > 1
551 // debug code - write the stream to debug file /tmp/emf-stream.emf
552 sal_uInt64
const pos
= mpInputStream
->Tell();
553 mpInputStream
->Seek(0);
554 SvFileStream
file( OUString( "/tmp/emf-stream.emf" ), StreamMode::WRITE
| StreamMode::TRUNC
);
556 mpInputStream
->WriteStream(file
);
560 mpInputStream
->Seek( pos
);
566 sal_uInt64
const pos
= mpInputStream
->Tell();
567 auto buffer
= std::make_unique
<char[]>( length
);
568 PassEMFPlus( buffer
.get(), mpInputStream
->ReadBytes(buffer
.get(), length
) );
570 mpInputStream
->Seek( pos
);
574 // skip in SeekRel if impossibly unavailable
575 sal_uInt32 nRemainder
= length
;
577 const size_t nRequiredHeaderSize
= 12;
578 while (nRemainder
>= nRequiredHeaderSize
)
580 sal_uInt16
type(0), flags(0);
581 sal_uInt32
size(0), dataSize(0);
583 mpInputStream
->ReadUInt16( type
).ReadUInt16( flags
).ReadUInt32( size
).ReadUInt32( dataSize
);
584 nRemainder
-= nRequiredHeaderSize
;
586 SAL_INFO("emfio", "\t\tEMF+ record type: 0x" << std::hex
<< type
<< std::dec
);
588 // Get Device Context
589 // TODO We should use EmfPlusRecordType::GetDC instead
593 SAL_INFO("emfio", "\t\tEMF+ lock DC (device context)");
596 // look for the "dual mode" in header
597 // it indicates that either EMF or EMF+ records should be processed
598 // 0x4001 = EMF+ header
599 // flags & 1 = dual mode active
600 if ( type
== 0x4001 && flags
& 1 )
602 mbEMFPlusDualMode
= true;
603 SAL_INFO ("emfio", "\t\tEMF+ dual mode detected");
606 // Get the length of the remaining data of this record based
607 // on the alleged size
608 sal_uInt32 nRemainingRecordData
= size
>= nRequiredHeaderSize
?
609 size
-nRequiredHeaderSize
: 0;
610 // clip to available size
611 nRemainingRecordData
= std::min(nRemainingRecordData
, nRemainder
);
612 mpInputStream
->SeekRel(nRemainingRecordData
);
613 nRemainder
-= nRemainingRecordData
;
615 mpInputStream
->SeekRel(nRemainder
);
618 // these are referenced from inside the templates
619 static SvStream
& operator >> (SvStream
& rStream
, sal_Int16
&n
)
621 return rStream
.ReadInt16(n
);
624 static SvStream
& operator >> (SvStream
& rStream
, sal_Int32
&n
)
626 return rStream
.ReadInt32(n
);
630 * Reads polygons from the stream.
631 * The \<class T> parameter is for the type of the points (sal_uInt32 or sal_uInt16).
632 * skipFirst: if the first point read is the 0th point or the 1st point in the array.
635 tools::Polygon
EmfReader::ReadPolygonWithSkip(const bool skipFirst
, sal_uInt32 nNextPos
)
637 sal_uInt32
nPoints(0), nStartIndex(0);
638 mpInputStream
->SeekRel( 16 );
639 mpInputStream
->ReadUInt32( nPoints
);
646 return ReadPolygon
<T
>(nStartIndex
, nPoints
, nNextPos
);
650 * Reads polygons from the stream.
651 * The \<class T> parameter is for the type of the points
652 * nStartIndex: which is the starting index in the polygon of the first point read
653 * nPoints: number of points
654 * mpInputStream: the stream containing the polygons
657 tools::Polygon
EmfReader::ReadPolygon(sal_uInt32 nStartIndex
, sal_uInt32 nPoints
, sal_uInt32 nNextPos
)
659 SAL_INFO ("emfio", "\t\tPolygon:");
661 bool bRecordOk
= nPoints
<= SAL_MAX_UINT16
;
662 SAL_WARN_IF(!bRecordOk
, "emfio", "polygon record has more polygons than we can handle");
663 if (!bRecordOk
|| !nPoints
)
664 return tools::Polygon();
666 auto nRemainingSize
= std::min(nNextPos
- mpInputStream
->Tell(), mpInputStream
->remainingSize());
667 auto nMaxPossiblePoints
= nRemainingSize
/ (sizeof(T
) * 2);
668 auto nPointCount
= nPoints
- nStartIndex
;
669 if (nPointCount
> nMaxPossiblePoints
)
671 SAL_WARN("emfio", "polygon claims more points than record can provide, truncating");
672 nPoints
= nMaxPossiblePoints
+ nStartIndex
;
675 tools::Polygon
aPolygon(nPoints
);
676 for (sal_uInt32 i
= nStartIndex
; i
< nPoints
&& mpInputStream
->good(); i
++ )
679 *mpInputStream
>> nX
>> nY
;
681 SAL_INFO("emfio", "\t\t\tPoint " << i
<< " of " << nPoints
- 1 << ": " << nX
<< ", " << nY
);
683 if (!mpInputStream
->good())
685 SAL_WARN("emfio", "short read on polygon, truncating");
689 aPolygon
[ i
] = Point( nX
, nY
);
696 * Reads a polyline from the WMF file and draws it
697 * The \<class T> parameter refers to the type of the points. (e.g. sal_uInt16 or sal_uInt32)
700 void EmfReader::ReadAndDrawPolyLine(sal_uInt32 nNextPos
)
702 SAL_INFO("emfio", "\t\tPolyline: ");
704 mpInputStream
->SeekRel( 0x10 ); // TODO Skipping Bounds. A 128-bit WMF RectL object (specifies the bounding rectangle in device units.)
706 sal_uInt32 nNumberOfPolylines
= 0;
707 mpInputStream
->ReadUInt32( nNumberOfPolylines
);
708 SAL_INFO("emfio", "\t\t\tPolylines: " << nNumberOfPolylines
);
710 sal_uInt32 nCount
= 0;
711 mpInputStream
->ReadUInt32( nCount
); // total number of points in all polylines
712 SAL_INFO("emfio", "\t\t\tPoints: " << nCount
);
714 const auto nEndPos
= std::min(nNextPos
, mnEndPos
);
715 if (mpInputStream
->Tell() >= nEndPos
)
718 // taking the amount of points of each polygon, retrieving the total number of points
719 if ( !(mpInputStream
->good() &&
720 ( nNumberOfPolylines
< SAL_MAX_UINT32
/ sizeof( sal_uInt16
) ) &&
721 ( nNumberOfPolylines
* sizeof( sal_uInt16
) ) <= ( nEndPos
- mpInputStream
->Tell() ))
725 std::unique_ptr
< sal_uInt32
[] > pnPolylinePointCount( new sal_uInt32
[ nNumberOfPolylines
] );
726 for ( sal_uInt32 i
= 0; i
< nNumberOfPolylines
&& mpInputStream
->good(); i
++ )
729 mpInputStream
->ReadUInt32( nPoints
);
730 SAL_INFO("emfio", "\t\t\tPoint " << i
<< " of " << nNumberOfPolylines
<< ": " << nPoints
);
731 pnPolylinePointCount
[ i
] = nPoints
;
734 // Get polyline points:
735 for ( sal_uInt32 i
= 0; ( i
< nNumberOfPolylines
) && mpInputStream
->good(); i
++ )
737 tools::Polygon aPolygon
= ReadPolygon
<T
>(0, pnPolylinePointCount
[i
], nNextPos
);
738 DrawPolyLine(std::move(aPolygon
), false, mbRecordPath
);
743 * Reads a poly polygon from the WMF file and draws it.
744 * The \<class T> parameter refers to the type of the points. (e.g. sal_uInt16 or sal_uInt32)
747 void EmfReader::ReadAndDrawPolyPolygon(sal_uInt32 nNextPos
)
749 SAL_INFO("emfio", "\t\tPolygon: ");
750 mpInputStream
->SeekRel( 0x10 ); // RectL bounds
752 sal_uInt32
nPoly(0), nGesPoints(0), nReadPoints(0);
753 // Number of polygons
754 mpInputStream
->ReadUInt32( nPoly
).ReadUInt32( nGesPoints
);
755 SAL_INFO("emfio", "\t\t\tPolygons: " << nPoly
);
756 SAL_INFO("emfio", "\t\t\tPoints: " << nGesPoints
);
758 const auto nEndPos
= std::min(nNextPos
, mnEndPos
);
759 if (mpInputStream
->Tell() >= nEndPos
)
761 if (!mpInputStream
->good())
763 //check against numeric overflowing
764 if (nGesPoints
>= SAL_MAX_UINT32
/ sizeof(Point
))
766 if (nPoly
>= SAL_MAX_UINT32
/ sizeof(sal_uInt16
))
768 if (nPoly
* sizeof(sal_uInt16
) > nEndPos
- mpInputStream
->Tell())
771 // Get number of points in each polygon
772 std::vector
<sal_uInt16
> aPoints(nPoly
);
773 for (sal_uInt32 i
= 0; i
< nPoly
&& mpInputStream
->good(); ++i
)
775 sal_uInt32
nPoints(0);
776 mpInputStream
->ReadUInt32( nPoints
);
778 SAL_INFO("emfio", "\t\t\t\tPolygon " << i
<< " points: " << nPoints
);
780 aPoints
[i
] = static_cast<sal_uInt16
>(nPoints
);
783 if ( mpInputStream
->good() && ( nGesPoints
* (sizeof(T
)+sizeof(T
)) ) <= ( nEndPos
- mpInputStream
->Tell() ) )
785 // Get polygon points
786 tools::PolyPolygon
aPolyPoly(nPoly
);
787 for (sal_uInt32 i
= 0; i
< nPoly
&& mpInputStream
->good(); ++i
)
789 const sal_uInt16
nPointCount(aPoints
[i
]);
790 std::vector
<Point
> aPtAry(nPointCount
);
791 for (sal_uInt16 j
= 0; j
< nPointCount
&& mpInputStream
->good(); ++j
)
794 *mpInputStream
>> nX
>> nY
;
795 aPtAry
[j
] = Point( nX
, nY
);
799 aPolyPoly
.Insert(tools::Polygon(aPtAry
.size(), aPtAry
.data()));
802 DrawPolyPolygon(aPolyPoly
, mbRecordPath
);
805 OSL_ENSURE(nReadPoints
== nGesPoints
, "The number Points processed from EMR_POLYPOLYGON is unequal imported number (!)");
808 bool EmfReader::ReadEnhWMF()
810 sal_uInt32 nStretchBltMode
= 0;
811 sal_uInt32
nNextPos(0),
812 nW(0), nH(0), nColor(0), nIndex(0),
813 nDat32(0), nNom1(0), nDen1(0), nNom2(0), nDen2(0);
814 sal_Int32
nX32(0), nY32(0), nx32(0), ny32(0);
816 bool bStatus
= ReadHeader();
817 bool bHaveDC
= false;
819 OUString aEMFPlusDisable
;
820 rtl::Bootstrap::get("EMF_PLUS_DISABLE", aEMFPlusDisable
);
821 bool bEnableEMFPlus
= aEMFPlusDisable
.isEmpty();
822 if (!mbEnableEMFPlus
)
824 // EMF+ is enabled if neither the bootstrap variable, not the member variable disables
826 bEnableEMFPlus
= mbEnableEMFPlus
;
829 SAL_INFO("emfio", "EMF+ reading is " << (bEnableEMFPlus
? "enabled" : "disabled"));
831 while (bStatus
&& mnRecordCount
-- && mpInputStream
->good() && !mbReadOtherGraphicFormat
)
833 sal_uInt32
nRecType(0), nRecSize(0);
834 mpInputStream
->ReadUInt32(nRecType
).ReadUInt32(nRecSize
);
836 if ( !mpInputStream
->good() || ( nRecSize
< 8 ) || ( nRecSize
& 3 ) ) // Parameters are always divisible by 4
842 auto nCurPos
= mpInputStream
->Tell();
844 if (mnEndPos
< nCurPos
- 8)
850 const sal_uInt32 nMaxPossibleRecSize
= mnEndPos
- (nCurPos
- 8);
851 if (nRecSize
> nMaxPossibleRecSize
)
857 nNextPos
= nCurPos
+ (nRecSize
- 8);
859 if( !maBmpSaveList
.empty()
860 && ( nRecType
!= EMR_STRETCHBLT
)
861 && ( nRecType
!= EMR_STRETCHDIBITS
)
863 ResolveBitmapActions( maBmpSaveList
);
868 SAL_INFO("emfio", "0x" << std::hex
<< (nNextPos
- nRecSize
) << "-0x" << nNextPos
<< " " << record_type_name(nRecType
) << " size: "
869 << std::dec
<< nRecSize
);
871 if( bEnableEMFPlus
&& nRecType
== EMR_COMMENT
)
875 mpInputStream
->ReadUInt32( length
);
877 SAL_INFO("emfio", "\tGDI comment, length: " << length
);
879 if( mpInputStream
->good() && length
>= 4 && length
<= mpInputStream
->remainingSize() ) {
880 sal_uInt32 nCommentId
;
882 mpInputStream
->ReadUInt32( nCommentId
);
884 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
);
886 if( nCommentId
== EMR_COMMENT_EMFPLUS
&& nRecSize
>= 12 )
888 // [MS-EMF] 2.3.3: DataSize includes both CommentIdentifier and CommentRecordParm fields.
889 // We have already read 4-byte CommentIdentifier, so reduce length appropriately
890 ReadEMFPlusComment( length
-4, bHaveDC
);
892 else if( nCommentId
== EMR_COMMENT_PUBLIC
&& nRecSize
>= 12 )
894 ReadGDIComment(nCommentId
);
896 else if( nCommentId
== EMR_COMMENT_EMFSPOOL
&& nRecSize
>= 12 )
898 SAL_WARN("emfio", "\t\tEMFSPOOL not implemented, id: 0x" << std::hex
<< nCommentId
<< std::dec
);
899 // TODO Implement reading EMFSPOOL comment
904 SAL_WARN("emfio", "\t\tunknown id: 0x" << std::hex
<< nCommentId
<< std::dec
);
908 else if ( !bHaveDC
&& mbEMFPlusDualMode
&& nRecType
!= EMR_HEADER
&& nRecType
!= EMR_EOF
)
910 // skip content (EMF record) in dual mode
911 // we process only EMR_COMMENT (see above) to access EMF+ data
912 // with 2 exceptions, according to EMF+ specification:
913 // EMR_HEADER and EMR_EOF
914 // if a device context is given (bHaveDC) process the following EMF record, too.
916 else if( !mbEMFPlus
|| bHaveDC
|| nRecType
== EMR_EOF
)
920 case EMR_POLYBEZIERTO
:
921 DrawPolyBezier(ReadPolygonWithSkip
<sal_Int32
>(true, nNextPos
), true, mbRecordPath
);
923 case EMR_POLYBEZIER
:
924 DrawPolyBezier(ReadPolygonWithSkip
<sal_Int32
>(false, nNextPos
), false, mbRecordPath
);
928 DrawPolygon(ReadPolygonWithSkip
<sal_Int32
>(false, nNextPos
), mbRecordPath
);
931 case EMR_POLYLINETO
:
932 DrawPolyLine(ReadPolygonWithSkip
<sal_Int32
>(true, nNextPos
), true, mbRecordPath
);
936 DrawPolyLine(ReadPolygonWithSkip
<sal_Int32
>(false, nNextPos
), false, mbRecordPath
);
939 case EMR_POLYPOLYLINE
:
940 ReadAndDrawPolyLine
<sal_Int32
>(nNextPos
);
943 case EMR_POLYPOLYGON
:
944 ReadAndDrawPolyPolygon
<sal_Int32
>(nNextPos
);
947 case EMR_SETWINDOWEXTEX
:
949 sal_Int32 w
= 0, h
= 0;
950 mpInputStream
->ReadInt32( w
).ReadInt32( h
);
951 SAL_INFO("emfio", "\t\tWidth: " << w
);
952 SAL_INFO("emfio", "\t\tHeight: " << h
);
953 SetWinExt( Size( w
, h
), true);
957 case EMR_SETWINDOWORGEX
:
959 mpInputStream
->ReadInt32( nX32
).ReadInt32( nY32
);
960 SAL_INFO("emfio", "\t\tPoint: (" << nX32
<< ", " << nY32
<< ")");
961 SetWinOrg( Point( nX32
, nY32
), true);
965 case EMR_SCALEWINDOWEXTEX
:
967 mpInputStream
->ReadUInt32( nNom1
).ReadUInt32( nDen1
).ReadUInt32( nNom2
).ReadUInt32( nDen2
);
968 SAL_INFO("emfio", "\t\tHorizontal scale: " << nNom1
<< " / " << nDen1
);
969 SAL_INFO("emfio", "\t\tVertical scale: " << nNom2
<< " / " << nDen2
);
971 if (nDen1
!= 0 && nDen2
!= 0)
972 ScaleWinExt( static_cast<double>(nNom1
) / nDen1
, static_cast<double>(nNom2
) / nDen2
);
974 SAL_WARN("vcl.emf", "ignoring bogus divide by zero");
978 case EMR_SETVIEWPORTORGEX
:
980 mpInputStream
->ReadInt32( nX32
).ReadInt32( nY32
);
981 SAL_INFO("emfio", "\t\tPoint: (" << nX32
<< ", " << nY32
<< ")");
982 SetDevOrg( Point( nX32
, nY32
) );
986 case EMR_SCALEVIEWPORTEXTEX
:
988 mpInputStream
->ReadUInt32( nNom1
).ReadUInt32( nDen1
).ReadUInt32( nNom2
).ReadUInt32( nDen2
);
989 SAL_INFO("emfio", "\t\tHorizontal scale: " << nNom1
<< " / " << nDen1
);
990 SAL_INFO("emfio", "\t\tVertical scale: " << nNom2
<< " / " << nDen2
);
992 if (nDen1
!= 0 && nDen2
!= 0)
993 ScaleDevExt( static_cast<double>(nNom1
) / nDen1
, static_cast<double>(nNom2
) / nDen2
);
995 SAL_WARN("vcl.emf", "ignoring bogus divide by zero");
999 case EMR_SETVIEWPORTEXTEX
:
1001 sal_Int32 w
= 0, h
= 0;
1002 mpInputStream
->ReadInt32( w
).ReadInt32( h
);
1003 SAL_INFO("emfio", "\t\tWidth: " << w
);
1004 SAL_INFO("emfio", "\t\tHeight: " << h
);
1006 SetDevExt( Size( w
, h
) );
1014 case EMR_SETPIXELV
:
1016 mpInputStream
->ReadInt32( nX32
).ReadInt32( nY32
);
1017 SAL_INFO("emfio", "\t\tPoint: (" << nX32
<< ", " << nY32
<< ")");
1018 DrawPixel( Point( nX32
, nY32
), ReadColor() );
1022 case EMR_SETMAPMODE
:
1024 sal_uInt32
nMapMode(0);
1025 mpInputStream
->ReadUInt32( nMapMode
);
1026 SAL_INFO("emfio", "\t\tMapMode: 0x" << std::hex
<< nMapMode
<< std::dec
);
1027 SetMapMode( static_cast<MappingMode
>(nMapMode
) );
1031 case EMR_SETBKMODE
:
1033 mpInputStream
->ReadUInt32( nDat32
);
1034 SAL_INFO("emfio", "\t\tBkMode: 0x" << std::hex
<< nDat32
<< std::dec
);
1035 SetBkMode( static_cast<BackgroundMode
>(nDat32
) );
1039 case EMR_SETPOLYFILLMODE
:
1044 mpInputStream
->ReadUInt32( nDat32
);
1045 SAL_INFO("emfio", "\t\tROP2: 0x" << std::hex
<< nDat32
<< std::dec
);
1046 SetRasterOp( static_cast<WMFRasterOp
>(nDat32
) );
1050 case EMR_SETSTRETCHBLTMODE
:
1052 mpInputStream
->ReadUInt32( nStretchBltMode
);
1053 SAL_INFO("emfio", "\t\tStretchBltMode: 0x" << std::hex
<< nDat32
<< std::dec
);
1057 case EMR_SETTEXTALIGN
:
1059 mpInputStream
->ReadUInt32( nDat32
);
1060 SAL_INFO("emfio", "\t\tTextAlign: 0x" << std::hex
<< nDat32
<< std::dec
);
1061 SetTextAlign( nDat32
);
1065 case EMR_SETTEXTCOLOR
:
1067 SetTextColor( ReadColor() );
1071 case EMR_SETARCDIRECTION
:
1073 mpInputStream
->ReadUInt32(nIndex
);
1074 SetArcDirection(nIndex
== ARCDIRECTION_CLOCKWISE
);
1078 case EMR_SETBKCOLOR
:
1080 SetBkColor( ReadColor() );
1084 case EMR_OFFSETCLIPRGN
:
1086 mpInputStream
->ReadInt32( nX32
).ReadInt32( nY32
);
1087 SAL_INFO("emfio", "\t\tPoint: (" << nX32
<< ", " << nY32
<< ")");
1088 MoveClipRegion( Size( nX32
, nY32
) );
1094 mpInputStream
->ReadInt32( nX32
).ReadInt32( nY32
);
1095 SAL_INFO("emfio", "\t\tPoint: (" << nX32
<< ", " << nY32
<< ")");
1096 MoveTo( Point( nX32
, nY32
), mbRecordPath
);
1100 case EMR_INTERSECTCLIPRECT
:
1102 mpInputStream
->ReadInt32( nX32
).ReadInt32( nY32
).ReadInt32( nx32
).ReadInt32( ny32
);
1103 IntersectClipRect( ReadRectangle( nX32
, nY32
, nx32
, ny32
) );
1104 SAL_INFO("emfio", "\t\tPoint: (" << nX32
<< ", " << nY32
<< ")");
1105 SAL_INFO("emfio", "\t\tPoint: (" << nx32
<< ", " << ny32
<< ")");
1115 case EMR_RESTOREDC
:
1117 sal_Int32
nSavedDC(0);
1118 mpInputStream
->ReadInt32( nSavedDC
);
1119 SAL_INFO( "emfio", "\t\t SavedDC Index: " << nSavedDC
);
1123 Pop( -1 ); // For RestoreDC values above -1, treat as get last element
1127 case EMR_SETWORLDTRANSFORM
:
1130 *mpInputStream
>> aTempXForm
;
1131 SetWorldTransform( aTempXForm
);
1135 case EMR_MODIFYWORLDTRANSFORM
:
1137 sal_uInt32
nMode(0);
1139 *mpInputStream
>> aTempXForm
;
1140 mpInputStream
->ReadUInt32( nMode
);
1141 ModifyWorldTransform( aTempXForm
, static_cast<ModifyWorldTransformMode
>(nMode
) );
1145 case EMR_SELECTOBJECT
:
1147 mpInputStream
->ReadUInt32( nIndex
);
1148 SelectObject( nIndex
);
1154 mpInputStream
->ReadUInt32(nIndex
);
1155 if ((nIndex
& ENHMETA_STOCK_OBJECT
) == 0)
1157 sal_uInt32
nPenStyle(0);
1158 sal_Int32
nPenWidth(0), nIgnored
;
1159 mpInputStream
->ReadUInt32(nPenStyle
).ReadInt32(nPenWidth
).ReadInt32(nIgnored
);
1160 SAL_INFO("emfio", "\t\tIndex: " << nIndex
<< " Style: 0x" << std::hex
1161 << nPenStyle
<< std::dec
1162 << " PenWidth: " << nPenWidth
);
1163 if ((nPenStyle
& PS_STYLE_MASK
) > PS_INSIDEFRAME
)
1164 nPenStyle
= PS_COSMETIC
;
1165 if ((nPenStyle
& PS_GEOMETRIC
) == 0)
1167 CreateObjectIndexed(nIndex
, std::make_unique
<WinMtfLineStyle
>(ReadColor(), nPenStyle
, nPenWidth
));
1172 case EMR_EXTCREATEPEN
:
1174 mpInputStream
->ReadUInt32(nIndex
);
1175 if ((nIndex
& ENHMETA_STOCK_OBJECT
) == 0)
1177 sal_uInt32 offBmi
, cbBmi
, offBits
, cbBits
, nPenStyle
, nWidth
, nBrushStyle
, elpNumEntries
;
1179 mpInputStream
->ReadUInt32(offBmi
).ReadUInt32(cbBmi
).ReadUInt32(offBits
).ReadUInt32(cbBits
);
1180 mpInputStream
->ReadUInt32(nPenStyle
).ReadUInt32(nWidth
).ReadUInt32(nBrushStyle
);
1181 Color aColorRef
= ReadColor();
1182 mpInputStream
->ReadInt32(elpHatch
).ReadUInt32(elpNumEntries
);
1184 if (!mpInputStream
->good())
1188 SAL_INFO("emfio", "\t\tStyle: 0x" << std::hex
<< nPenStyle
<< std::dec
);
1189 if ((nPenStyle
& PS_STYLE_MASK
) > PS_INSIDEFRAME
)
1190 nPenStyle
= PS_COSMETIC
;
1191 if ((nPenStyle
& PS_GEOMETRIC
) == 0)
1193 SAL_INFO("emfio", "\t\tWidth: " << nWidth
);
1194 CreateObjectIndexed(nIndex
, std::make_unique
<WinMtfLineStyle
>(aColorRef
, nPenStyle
, nWidth
));
1200 case EMR_CREATEBRUSHINDIRECT
:
1202 mpInputStream
->ReadUInt32( nIndex
);
1203 if ( ( nIndex
& ENHMETA_STOCK_OBJECT
) == 0 )
1206 mpInputStream
->ReadUInt32( nStyle
);
1207 BrushStyle eStyle
= static_cast<BrushStyle
>(nStyle
);
1208 CreateObjectIndexed(nIndex
, std::make_unique
<WinMtfFillStyle
>( ReadColor(), ( eStyle
== BrushStyle::BS_HOLLOW
) ));
1213 case EMR_DELETEOBJECT
:
1215 mpInputStream
->ReadUInt32( nIndex
);
1216 if ( ( nIndex
& ENHMETA_STOCK_OBJECT
) == 0 )
1217 DeleteObject( nIndex
);
1223 mpInputStream
->ReadInt32( nX32
).ReadInt32( nY32
).ReadInt32( nx32
).ReadInt32( ny32
);
1224 SAL_INFO("emfio", "\t\t Rectangle, left: " << nX32
<< ", top: " << nY32
<< ", right: " << nx32
<< ", bottom: " << ny32
);
1226 sal_Int32
w(0), h(0);
1227 if (o3tl::checked_sub(nx32
, nX32
, w
) || o3tl::checked_sub(ny32
, nY32
, h
))
1228 SAL_WARN("emfio", "broken ellipse");
1231 tools::Long dw
= w
/ 2;
1232 tools::Long dh
= h
/ 2;
1233 Point
aCenter( nX32
+ dw
, nY32
+ dh
);
1234 tools::Polygon
aPoly( aCenter
, dw
, dh
);
1235 DrawPolygon( std::move(aPoly
), mbRecordPath
);
1240 case EMR_RECTANGLE
:
1242 mpInputStream
->ReadInt32( nX32
).ReadInt32( nY32
).ReadInt32( nx32
).ReadInt32( ny32
);
1243 SAL_INFO("emfio", "\t\t Rectangle, left: " << nX32
<< ", top: " << nY32
<< ", right: " << nx32
<< ", bottom: " << ny32
);
1244 Point aPoints
[] { Point(nX32
, nY32
),
1247 Point(nX32
, ny32
) };
1248 tools::Polygon
aPoly(4, aPoints
);
1249 aPoly
.Optimize( PolyOptimizeFlags::CLOSE
);
1250 DrawPolygon( std::move(aPoly
), mbRecordPath
);
1254 case EMR_ROUNDRECT
:
1256 mpInputStream
->ReadInt32( nX32
).ReadInt32( nY32
).ReadInt32( nx32
).ReadInt32( ny32
).ReadUInt32( nW
).ReadUInt32( nH
);
1257 tools::Polygon
aRoundRectPoly( ReadRectangle( nX32
, nY32
, nx32
, ny32
), nW
, nH
);
1258 DrawPolygon( std::move(aRoundRectPoly
), mbRecordPath
);
1266 sal_Int32 nStartX
, nStartY
, nEndX
, nEndY
;
1267 mpInputStream
->ReadInt32( nX32
).ReadInt32( nY32
).ReadInt32( nx32
).ReadInt32( ny32
).ReadInt32( nStartX
).ReadInt32( nStartY
).ReadInt32( nEndX
).ReadInt32( nEndY
);
1268 if (!mpInputStream
->good())
1272 SAL_INFO( "emfio", "\t\t Bounds: " << nX32
<< ":" << nY32
<< ", " << nx32
<< ":" << ny32
<< ", Start: " << nStartX
<< ":" << nStartY
<< ", End: " << nEndX
<< ":" << nEndY
);
1273 tools::Polygon
aPoly(ReadRectangle(nX32
, nY32
, nx32
, ny32
), Point(nStartX
, nStartY
), Point(nEndX
, nEndY
), PolyStyle::Arc
, IsArcDirectionClockWise());
1275 if ( nRecType
== EMR_CHORD
)
1276 DrawPolygon( std::move(aPoly
), mbRecordPath
);
1278 DrawPolyLine( std::move(aPoly
), nRecType
== EMR_ARCTO
, mbRecordPath
);
1285 sal_Int32 nStartX
, nStartY
, nEndX
, nEndY
;
1286 mpInputStream
->ReadInt32( nX32
).ReadInt32( nY32
).ReadInt32( nx32
).ReadInt32( ny32
).ReadInt32( nStartX
).ReadInt32( nStartY
).ReadInt32( nEndX
).ReadInt32( nEndY
);
1287 if (!mpInputStream
->good())
1291 tools::Polygon
aPoly(ReadRectangle(nX32
, nY32
, nx32
, ny32
), Point(nStartX
, nStartY
), Point(nEndX
, nEndY
), PolyStyle::Pie
, IsArcDirectionClockWise());
1292 DrawPolygon( std::move(aPoly
), mbRecordPath
);
1299 mpInputStream
->ReadInt32( nX32
).ReadInt32( nY32
);
1300 LineTo( Point( nX32
, nY32
), mbRecordPath
);
1304 case EMR_BEGINPATH
:
1307 mbRecordPath
= true;
1311 case EMR_ABORTPATH
:
1315 mbRecordPath
= false;
1318 case EMR_CLOSEFIGURE
:
1323 StrokeAndFillPath( false, true );
1326 case EMR_STROKEANDFILLPATH
:
1327 StrokeAndFillPath( true, true );
1330 case EMR_STROKEPATH
:
1331 StrokeAndFillPath( true, false );
1334 case EMR_SELECTCLIPPATH
:
1336 sal_Int32
nClippingMode(0);
1337 mpInputStream
->ReadInt32(nClippingMode
);
1338 SetClipPath(GetPathObj(), static_cast<RegionMode
>(nClippingMode
), true);
1342 case EMR_EXTSELECTCLIPRGN
:
1344 sal_uInt32 nRemainingRecSize
= nRecSize
- 8;
1345 if (nRemainingRecSize
< 8)
1349 sal_Int32
nClippingMode(0), cbRgnData(0);
1350 mpInputStream
->ReadInt32(cbRgnData
);
1351 mpInputStream
->ReadInt32(nClippingMode
);
1352 nRemainingRecSize
-= 8;
1354 // This record's region data should be ignored if mode
1355 // is RGN_COPY - see EMF spec section 2.3.2.2
1356 if (static_cast<RegionMode
>(nClippingMode
) == RegionMode::RGN_COPY
)
1358 SetDefaultClipPath();
1362 basegfx::B2DPolyPolygon aPolyPoly
;
1364 ImplReadRegion(aPolyPoly
, *mpInputStream
, nRemainingRecSize
, GetWinOrg());
1365 const tools::PolyPolygon
aPolyPolygon(aPolyPoly
);
1366 SetClipPath(aPolyPolygon
, static_cast<RegionMode
>(nClippingMode
), false);
1372 case EMR_ALPHABLEND
:
1374 sal_Int32
xDest(0), yDest(0), cxDest(0), cyDest(0);
1376 BLENDFUNCTION aFunc
;
1377 sal_Int32
xSrc(0), ySrc(0), cxSrc(0), cySrc(0);
1379 sal_uInt32
BkColorSrc(0), iUsageSrc(0), offBmiSrc(0);
1380 sal_uInt32
cbBmiSrc(0), offBitsSrc(0), cbBitsSrc(0);
1382 sal_uInt32 nStart
= mpInputStream
->Tell() - 8;
1383 mpInputStream
->SeekRel( 0x10 );
1385 mpInputStream
->ReadInt32( xDest
).ReadInt32( yDest
).ReadInt32( cxDest
).ReadInt32( cyDest
);
1386 *mpInputStream
>> aFunc
;
1387 mpInputStream
->ReadInt32( xSrc
).ReadInt32( ySrc
);
1388 *mpInputStream
>> xformSrc
;
1389 mpInputStream
->ReadUInt32( BkColorSrc
).ReadUInt32( iUsageSrc
).ReadUInt32( offBmiSrc
).ReadUInt32( cbBmiSrc
)
1390 .ReadUInt32( offBitsSrc
).ReadUInt32( cbBitsSrc
).ReadInt32( cxSrc
).ReadInt32( cySrc
) ;
1392 if ( !mpInputStream
->good() ||
1393 (cbBitsSrc
> (SAL_MAX_UINT32
- 14)) ||
1394 ((SAL_MAX_UINT32
- 14) - cbBitsSrc
< cbBmiSrc
) ||
1395 cxDest
== SAL_MAX_INT32
|| cyDest
== SAL_MAX_INT32
)
1401 tools::Rectangle
aRect(Point(xDest
, yDest
), Size(cxDest
+ 1, cyDest
+ 1));
1403 const sal_uInt32 nSourceSize
= cbBmiSrc
+ cbBitsSrc
+ 14;
1404 bool bSafeRead
= nSourceSize
<= (mnEndPos
- mnStartPos
);
1405 sal_uInt32
nDeltaToDIB5HeaderSize(0);
1406 const bool bReadAlpha(0x01 == aFunc
.aAlphaFormat
);
1407 if (bSafeRead
&& bReadAlpha
)
1409 // we need to read alpha channel data if AlphaFormat of BLENDFUNCTION is
1410 // AC_SRC_ALPHA (==0x01). To read it, create a temp DIB-File which is ready
1412 const sal_uInt32 nHeaderSize
= getDIBV5HeaderSize();
1413 if (cbBmiSrc
> nHeaderSize
)
1416 nDeltaToDIB5HeaderSize
= nHeaderSize
- cbBmiSrc
;
1420 const sal_uInt32
nTargetSize(cbBmiSrc
+ nDeltaToDIB5HeaderSize
+ cbBitsSrc
+ 14);
1421 char* pBuf
= new char[ nTargetSize
];
1422 SvMemoryStream
aTmp( pBuf
, nTargetSize
, StreamMode::READ
| StreamMode::WRITE
);
1424 aTmp
.ObjectOwnsMemory( true );
1426 // write BM-Header (14 bytes)
1427 aTmp
.WriteUChar( 'B' )
1429 .WriteUInt32( cbBitsSrc
)
1432 .WriteUInt32( cbBmiSrc
+ nDeltaToDIB5HeaderSize
+ 14 );
1434 // copy DIBInfoHeader from source (cbBmiSrc bytes)
1435 mpInputStream
->Seek( nStart
+ offBmiSrc
);
1436 char* pWritePos
= pBuf
+ 14;
1437 auto nRead
= mpInputStream
->ReadBytes(pWritePos
, cbBmiSrc
);
1438 if (nRead
!= cbBmiSrc
)
1440 // zero remainder if short read
1441 memset(pWritePos
+ nRead
, 0, cbBmiSrc
- nRead
);
1446 // need to add values for all stuff that DIBV5Header is bigger
1447 // than DIBInfoHeader, all values are correctly initialized to zero,
1448 // so we can use memset here
1449 memset(pBuf
+ cbBmiSrc
+ 14, 0, nDeltaToDIB5HeaderSize
);
1452 // copy bitmap data from source (offBitsSrc bytes)
1453 mpInputStream
->Seek( nStart
+ offBitsSrc
);
1454 pWritePos
= pBuf
+ 14 + nDeltaToDIB5HeaderSize
+ cbBmiSrc
;
1455 nRead
= mpInputStream
->ReadBytes(pWritePos
, cbBitsSrc
);
1456 if (nRead
!= cbBitsSrc
)
1458 // zero remainder if short read
1459 memset(pWritePos
+ nRead
, 0, cbBitsSrc
- nRead
);
1464 // prepare to read and fill BitmapEx
1472 if(ReadDIBV5(aBitmap
, aAlpha
, aTmp
))
1474 aBitmapEx
= BitmapEx(aBitmap
, aAlpha
);
1481 if(ReadDIB(aBitmap
, aTmp
, true))
1483 if(0xff != aFunc
.aSrcConstantAlpha
)
1485 // add const alpha channel
1486 aBitmapEx
= BitmapEx(
1488 AlphaMask(aBitmap
.GetSizePixel(), &aFunc
.aSrcConstantAlpha
));
1493 aBitmapEx
= BitmapEx(aBitmap
);
1498 if(!aBitmapEx
.IsEmpty())
1500 // test if it is sensible to crop
1501 if (cxSrc
> 0 && cySrc
> 0 && xSrc
>= 0 && ySrc
>= 0)
1505 if (!o3tl::checked_add(xSrc
, cxSrc
, xEndSrc
) && xEndSrc
< aBitmapEx
.GetSizePixel().Width() &&
1506 !o3tl::checked_add(ySrc
, cySrc
, yEndSrc
) && yEndSrc
< aBitmapEx
.GetSizePixel().Height())
1508 const tools::Rectangle
aCropRect( Point( xSrc
, ySrc
), Size( cxSrc
, cySrc
) );
1509 aBitmapEx
.Crop( aCropRect
);
1514 static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
1516 if(bDoSaveForVisualControl
)
1518 // VCL_DUMP_BMP_PATH should be like C:/path/ or ~/path/
1519 static const OUString
sDumpPath(OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
1520 if(!sDumpPath
.isEmpty())
1522 SvFileStream
aNew(sDumpPath
+ "metafile_content.png",
1523 StreamMode::WRITE
| StreamMode::TRUNC
);
1524 vcl::PngImageWriter
aPNGWriter(aNew
);
1525 aPNGWriter
.write(aBitmapEx
);
1529 maBmpSaveList
.emplace_back(aBitmapEx
, aRect
, SRCAND
|SRCINVERT
);
1536 case EMR_BITBLT
: // PASSTHROUGH INTENDED
1537 case EMR_STRETCHBLT
:
1539 sal_Int32 xDest
, yDest
, cxDest
, cyDest
, xSrc
, ySrc
, cxSrc
, cySrc
;
1540 sal_uInt32 dwRop
, iUsageSrc
, offBmiSrc
, cbBmiSrc
, offBitsSrc
, cbBitsSrc
;
1543 sal_uInt32 nStart
= mpInputStream
->Tell() - 8;
1545 mpInputStream
->SeekRel( 0x10 );
1546 mpInputStream
->ReadInt32( xDest
).ReadInt32( yDest
).ReadInt32( cxDest
).ReadInt32( cyDest
).ReadUInt32( dwRop
).ReadInt32( xSrc
).ReadInt32( ySrc
)
1548 mpInputStream
->ReadUInt32( nColor
).ReadUInt32( iUsageSrc
).ReadUInt32( offBmiSrc
).ReadUInt32( cbBmiSrc
)
1549 .ReadUInt32( offBitsSrc
).ReadUInt32( cbBitsSrc
);
1551 if ( nRecType
== EMR_STRETCHBLT
)
1552 mpInputStream
->ReadInt32( cxSrc
).ReadInt32( cySrc
);
1556 if (!mpInputStream
->good() || (cbBitsSrc
> (SAL_MAX_UINT32
- 14)) || ((SAL_MAX_UINT32
- 14) - cbBitsSrc
< cbBmiSrc
))
1561 tools::Rectangle
aRect(Point(xDest
, yDest
), Size(cxDest
, cyDest
));
1563 sal_uInt32 nSize
= cbBmiSrc
+ cbBitsSrc
+ 14;
1564 if ( nSize
<= ( mnEndPos
- mnStartPos
) )
1566 char* pBuf
= new char[ nSize
];
1567 SvMemoryStream
aTmp( pBuf
, nSize
, StreamMode::READ
| StreamMode::WRITE
);
1568 aTmp
.ObjectOwnsMemory( true );
1569 aTmp
.WriteUChar( 'B' )
1571 .WriteUInt32( cbBitsSrc
)
1574 .WriteUInt32( cbBmiSrc
+ 14 );
1576 mpInputStream
->Seek( nStart
+ offBmiSrc
);
1577 char* pWritePos
= pBuf
+ 14;
1578 auto nRead
= mpInputStream
->ReadBytes(pWritePos
, cbBmiSrc
);
1579 if (nRead
!= cbBmiSrc
)
1581 // zero remainder if short read
1582 memset(pWritePos
+ nRead
, 0, cbBmiSrc
- nRead
);
1585 mpInputStream
->Seek( nStart
+ offBitsSrc
);
1586 pWritePos
= pBuf
+ 14 + cbBmiSrc
;
1587 nRead
= mpInputStream
->ReadBytes(pWritePos
, cbBitsSrc
);
1588 if (nRead
!= cbBitsSrc
)
1590 // zero remainder if short read
1591 memset(pWritePos
+ nRead
, 0, cbBitsSrc
- nRead
);
1595 ReadDIB(aBitmap
, aTmp
, true);
1597 // test if it is sensible to crop
1598 if ( (cxSrc
> 0) && (cySrc
> 0) &&
1599 (xSrc
>= 0) && (ySrc
>= 0) &&
1600 (aBitmap
.GetSizePixel().Width() >= cxSrc
) &&
1601 (xSrc
<= aBitmap
.GetSizePixel().Width() - cxSrc
) &&
1602 (aBitmap
.GetSizePixel().Height() >= cySrc
) &&
1603 (ySrc
<= aBitmap
.GetSizePixel().Height() - cySrc
) )
1605 tools::Rectangle
aCropRect( Point( xSrc
, ySrc
), Size( cxSrc
, cySrc
) );
1606 aBitmap
.Crop( aCropRect
);
1609 maBmpSaveList
.emplace_back(aBitmap
, aRect
, dwRop
);
1615 case EMR_STRETCHDIBITS
:
1617 sal_Int32 xDest
, yDest
, xSrc
, ySrc
, cxSrc
, cySrc
, cxDest
, cyDest
;
1618 sal_uInt32 offBmiSrc
, cbBmiSrc
, offBitsSrc
, cbBitsSrc
, iUsageSrc
, dwRop
;
1619 sal_uInt32 nStart
= mpInputStream
->Tell() - 8;
1621 mpInputStream
->SeekRel( 0x10 );
1622 mpInputStream
->ReadInt32( xDest
)
1628 .ReadUInt32( offBmiSrc
)
1629 .ReadUInt32( cbBmiSrc
)
1630 .ReadUInt32( offBitsSrc
)
1631 .ReadUInt32( cbBitsSrc
)
1632 .ReadUInt32( iUsageSrc
)
1633 .ReadUInt32( dwRop
)
1634 .ReadInt32( cxDest
)
1635 .ReadInt32( cyDest
);
1637 if (!mpInputStream
->good() ||
1638 ((SAL_MAX_UINT32
- 14) < cbBitsSrc
) ||
1639 ((SAL_MAX_UINT32
- 14) - cbBitsSrc
< cbBmiSrc
))
1646 tools::Rectangle
aRect(xDest
, yDest
);
1647 aRect
.SaturatingSetSize(Size(cxDest
, cyDest
));
1649 sal_uInt32 nSize
= cbBmiSrc
+ cbBitsSrc
+ 14;
1650 if ( nSize
<= ( mnEndPos
- mnStartPos
) )
1652 char* pBuf
= new char[ nSize
];
1653 SvMemoryStream
aTmp( pBuf
, nSize
, StreamMode::READ
| StreamMode::WRITE
);
1654 aTmp
.ObjectOwnsMemory( true );
1655 aTmp
.WriteUChar( 'B' )
1657 .WriteUInt32( cbBitsSrc
)
1660 .WriteUInt32( cbBmiSrc
+ 14 );
1662 mpInputStream
->Seek( nStart
+ offBmiSrc
);
1663 char* pWritePos
= pBuf
+ 14;
1664 auto nRead
= mpInputStream
->ReadBytes(pWritePos
, cbBmiSrc
);
1665 if (nRead
!= cbBmiSrc
)
1667 // zero remainder if short read
1668 memset(pWritePos
+ nRead
, 0, cbBmiSrc
- nRead
);
1671 mpInputStream
->Seek( nStart
+ offBitsSrc
);
1672 pWritePos
= pBuf
+ 14 + cbBmiSrc
;
1673 nRead
= mpInputStream
->ReadBytes(pWritePos
, cbBitsSrc
);
1674 if (nRead
!= cbBitsSrc
)
1676 // zero remainder if short read
1677 memset(pWritePos
+ nRead
, 0, cbBitsSrc
- nRead
);
1681 ReadDIB(aBitmap
, aTmp
, true);
1683 const tools::Long nWidthDiff
= aBitmap
.GetSizePixel().Width() - cxSrc
;
1684 const tools::Long nHeightDiff
= aBitmap
.GetSizePixel().Height() - cySrc
;
1686 // test if it is sensible to crop
1687 if ( (cxSrc
> 0) && (cySrc
> 0) &&
1688 (xSrc
>= 0) && (ySrc
>= 0) &&
1689 (xSrc
<= nWidthDiff
) && (ySrc
<= nHeightDiff
) )
1691 tools::Rectangle
aCropRect( Point( xSrc
, ySrc
), Size( cxSrc
, cySrc
) );
1692 aBitmap
.Crop( aCropRect
);
1694 maBmpSaveList
.emplace_back(aBitmap
, aRect
, dwRop
);
1700 case EMR_EXTCREATEFONTINDIRECTW
:
1702 mpInputStream
->ReadUInt32( nIndex
);
1703 if ( ( nIndex
& ENHMETA_STOCK_OBJECT
) == 0 )
1706 mpInputStream
->ReadInt32( aLogFont
.lfHeight
)
1707 .ReadInt32( aLogFont
.lfWidth
)
1708 .ReadInt32( aLogFont
.lfEscapement
)
1709 .ReadInt32( aLogFont
.lfOrientation
)
1710 .ReadInt32( aLogFont
.lfWeight
)
1711 .ReadUChar( aLogFont
.lfItalic
)
1712 .ReadUChar( aLogFont
.lfUnderline
)
1713 .ReadUChar( aLogFont
.lfStrikeOut
)
1714 .ReadUChar( aLogFont
.lfCharSet
)
1715 .ReadUChar( aLogFont
.lfOutPrecision
)
1716 .ReadUChar( aLogFont
.lfClipPrecision
)
1717 .ReadUChar( aLogFont
.lfQuality
)
1718 .ReadUChar( aLogFont
.lfPitchAndFamily
);
1720 sal_Unicode lfFaceName
[LF_FACESIZE
+1];
1721 lfFaceName
[LF_FACESIZE
] = 0;
1722 for (int i
= 0; i
< LF_FACESIZE
; ++i
)
1724 sal_uInt16
nChar(0);
1725 mpInputStream
->ReadUInt16(nChar
);
1726 lfFaceName
[i
] = nChar
;
1728 aLogFont
.alfFaceName
= OUString( lfFaceName
);
1730 // #i123216# Not used in the test case of #121382# (always identity in XForm), also
1731 // no hints in ms docu if FontSize should be scaled with WT. Using with the example
1732 // from #i123216# creates errors, so removing.
1734 // // #i121382# Need to apply WorldTransform to FontHeight/Width; this should be completely
1735 // // changed to basegfx::B2DHomMatrix instead of 'struct XForm', but not now due to time
1736 // // constraints and dangers
1737 // const XForm& rXF = GetWorldTransform();
1738 // const basegfx::B2DHomMatrix aWT(rXF.eM11, rXF.eM21, rXF.eDx, rXF.eM12, rXF.eM22, rXF.eDy);
1739 // const basegfx::B2DVector aTransVec(aWT * basegfx::B2DVector(aLogFont.lfWidth, aLogFont.lfHeight));
1740 // aLogFont.lfWidth = aTransVec.getX();
1741 // aLogFont.lfHeight = aTransVec.getY();
1742 if (mpInputStream
->good() && aLogFont
.lfHeight
!= SAL_MIN_INT32
&& aLogFont
.lfWidth
!= SAL_MIN_INT32
)
1744 CreateObjectIndexed(nIndex
, std::make_unique
<WinMtfFontStyle
>( aLogFont
));
1750 case EMR_POLYTEXTOUTA
:
1751 case EMR_EXTTEXTOUTA
:
1754 case EMR_POLYTEXTOUTW
:
1755 case EMR_EXTTEXTOUTW
:
1757 sal_Int32 nLeft
, nTop
, nRight
, nBottom
;
1758 sal_uInt32 nGfxMode
;
1759 float nXScale
, nYScale
;
1760 sal_uInt32
ncStrings( 1 );
1761 sal_Int32 ptlReferenceX
, ptlReferenceY
;
1762 sal_uInt32 nLen
, nOffString
, nOptions
, offDx
;
1763 sal_Int32 nLeftRect
, nTopRect
, nRightRect
, nBottomRect
;
1765 nCurPos
= mpInputStream
->Tell() - 8;
1767 mpInputStream
->ReadInt32( nLeft
).ReadInt32( nTop
).ReadInt32( nRight
).ReadInt32( nBottom
)
1768 .ReadUInt32( nGfxMode
).ReadFloat( nXScale
).ReadFloat( nYScale
);
1769 SAL_INFO("emfio", "\t\tBounds: " << nLeft
<< ", " << nTop
<< ", " << nRight
<< ", " << nBottom
);
1770 SAL_INFO("emfio", "\t\tiGraphicsMode: 0x" << std::hex
<< nGfxMode
<< std::dec
);
1771 SAL_INFO("emfio", "\t\t Scale: " << nXScale
<< " x " << nYScale
);
1772 if ( ( nRecType
== EMR_POLYTEXTOUTA
) || ( nRecType
== EMR_POLYTEXTOUTW
) )
1774 mpInputStream
->ReadUInt32( ncStrings
);
1775 SAL_INFO("emfio", "\t\t Number of Text objects: " << ncStrings
);
1776 if ( ncStrings
== 0 )
1779 mpInputStream
->ReadInt32( ptlReferenceX
).ReadInt32( ptlReferenceY
).ReadUInt32( nLen
).ReadUInt32( nOffString
).ReadUInt32( nOptions
);
1780 SAL_INFO("emfio", "\t\tReference: (" << ptlReferenceX
<< ", " << ptlReferenceY
<< ")");
1782 mpInputStream
->ReadInt32( nLeftRect
).ReadInt32( nTopRect
).ReadInt32( nRightRect
).ReadInt32( nBottomRect
);
1783 mpInputStream
->ReadUInt32( offDx
);
1785 if (!mpInputStream
->good())
1789 const tools::Rectangle
aRect( nLeftRect
, nTopRect
, nRightRect
, nBottomRect
);
1790 const BackgroundMode mnBkModeBackup
= mnBkMode
;
1791 if ( nOptions
& ETO_NO_RECT
) // Don't draw the background rectangle and text background
1792 mnBkMode
= BackgroundMode::Transparent
;
1793 else if ( nOptions
& ETO_OPAQUE
)
1794 DrawRectWithBGColor( aRect
);
1796 // ETO_RTLREADING indicates that the characters are laid from right to left
1797 vcl::text::ComplexTextLayoutFlags nTextLayoutMode
= vcl::text::ComplexTextLayoutFlags::Default
;
1798 if ( nOptions
& ETO_RTLREADING
)
1799 nTextLayoutMode
= vcl::text::ComplexTextLayoutFlags::BiDiRtl
| vcl::text::ComplexTextLayoutFlags::TextOriginLeft
;
1800 SetTextLayoutMode( nTextLayoutMode
);
1801 SAL_WARN_IF( ( nOptions
& ( ETO_PDY
| ETO_GLYPH_INDEX
) ) != 0, "emfio", "SJ: ETO_PDY || ETO_GLYPH_INDEX in EMF" );
1803 Point
aPos( ptlReferenceX
, ptlReferenceY
);
1804 bool bOffStringSane
= nOffString
<= mnEndPos
- nCurPos
;
1805 if ( bOffStringSane
)
1807 mpInputStream
->Seek( nCurPos
+ nOffString
);
1811 if ( nLen
<= ( mnEndPos
- mpInputStream
->Tell() ) )
1813 std::vector
<char> pBuf( nLen
);
1814 mpInputStream
->ReadBytes(pBuf
.data(), nLen
);
1815 aText
= OUString(pBuf
.data(), nLen
, GetCharSet());
1820 if ( ( nLen
* sizeof(sal_Unicode
) ) <= ( mnEndPos
- mpInputStream
->Tell() ) )
1822 aText
= read_uInt16s_ToOUString(*mpInputStream
, nLen
);
1826 SAL_INFO("emfio", "\t\tText: " << aText
);
1827 SAL_INFO("emfio", "\t\tDxBuffer:");
1830 std::unique_ptr
<tools::Long
[]> pDYAry
;
1833 sal_Int32 nBytesEach
;
1836 // ETO_PDY flag indicates that we should read twice values
1837 // compared to the number of characters in the output string.
1838 // Values are stored in an array of 32-bit unsigned integers
1839 // named OutputDx, so there will be either 8 bytes or 4 bytes
1840 // each depending on ETO_PDY is set or not.
1841 if (nOptions
& ETO_PDY
)
1846 bool bOverflow
= o3tl::checked_multiply
<sal_Int32
>(nLen
, nBytesEach
, nDxSize
);
1847 if (!bOverflow
&& offDx
&& ((nCurPos
+ offDx
+ nDxSize
) <= nNextPos
) && nNextPos
<= mnEndPos
)
1849 mpInputStream
->Seek( nCurPos
+ offDx
);
1850 aDXAry
.resize(aText
.getLength());
1851 if (nOptions
& ETO_PDY
)
1853 pDYAry
.reset( new tools::Long
[aText
.getLength()] );
1856 for (sal_Int32 i
= 0; i
< aText
.getLength(); ++i
)
1858 sal_Int32 nDxCount
= 1;
1859 if ( static_cast<sal_uInt32
>( aText
.getLength() ) != nLen
)
1861 sal_Unicode cUniChar
= aText
[i
];
1862 OString
aTmp(&cUniChar
, 1, GetCharSet());
1863 if (aTmp
.getLength() > 1)
1865 nDxCount
= aTmp
.getLength();
1870 if (nOptions
& ETO_PDY
)
1877 sal_Int32 nDxTmp
= 0;
1878 mpInputStream
->ReadInt32(nDxTmp
);
1879 aDXAry
.set(i
, o3tl::saturating_add(aDXAry
[i
], nDxTmp
));
1880 if (nOptions
& ETO_PDY
)
1882 sal_Int32 nDyTmp
= 0;
1883 mpInputStream
->ReadInt32(nDyTmp
);
1884 pDYAry
[i
] += nDyTmp
;
1888 SAL_INFO("emfio", "\t\t\tSpacing " << i
<< ": " << aDXAry
[i
]);
1891 if ( nOptions
& ETO_CLIPPED
)
1893 Push(); // Save the current clip. It will be restored after text drawing
1894 IntersectClipRect( aRect
);
1896 DrawText(aPos
, aText
, aDXAry
.empty() ? nullptr : &aDXAry
, pDYAry
.get(), mbRecordPath
, static_cast<GraphicsMode
>(nGfxMode
));
1897 if ( nOptions
& ETO_CLIPPED
)
1900 mnBkMode
= mnBkModeBackup
;
1905 case EMR_POLYBEZIERTO16
:
1906 DrawPolyBezier(ReadPolygonWithSkip
<sal_Int16
>(true, nNextPos
), true, mbRecordPath
);
1909 case EMR_POLYBEZIER16
:
1910 DrawPolyBezier(ReadPolygonWithSkip
<sal_Int16
>(false, nNextPos
), false, mbRecordPath
);
1913 case EMR_POLYGON16
:
1914 DrawPolygon(ReadPolygonWithSkip
<sal_Int16
>(false, nNextPos
), mbRecordPath
);
1917 case EMR_POLYLINETO16
:
1918 DrawPolyLine(ReadPolygonWithSkip
<sal_Int16
>(true, nNextPos
), true, mbRecordPath
);
1921 case EMR_POLYLINE16
:
1922 DrawPolyLine(ReadPolygonWithSkip
<sal_Int16
>(false, nNextPos
), false, mbRecordPath
);
1925 case EMR_POLYPOLYLINE16
:
1926 ReadAndDrawPolyLine
<sal_Int16
>(nNextPos
);
1929 case EMR_POLYPOLYGON16
:
1930 ReadAndDrawPolyPolygon
<sal_Int16
>(nNextPos
);
1935 sal_uInt32 nRemainingRecSize
= nRecSize
- 8;
1936 if (nRemainingRecSize
< 24)
1940 sal_uInt32 nRgnDataSize
;
1941 basegfx::B2DPolyPolygon aPolyPoly
;
1942 mpInputStream
->SeekRel(16); // RectL bounds
1943 mpInputStream
->ReadUInt32( nRgnDataSize
).ReadUInt32( nIndex
);
1944 nRemainingRecSize
-= 24;
1946 if (ImplReadRegion(aPolyPoly
, *mpInputStream
, nRemainingRecSize
, GetWinOrg()))
1949 SelectObject( nIndex
);
1950 tools::PolyPolygon
aPolyPolygon(aPolyPoly
);
1951 DrawPolyPolygon( aPolyPolygon
);
1960 sal_uInt32 nRemainingRecSize
= nRecSize
- 8;
1961 if (nRemainingRecSize
< 20)
1965 sal_uInt32 nRgnDataSize
;
1966 basegfx::B2DPolyPolygon aPolyPoly
;
1967 mpInputStream
->SeekRel(16); // Skipping RectL bounds
1968 mpInputStream
->ReadUInt32( nRgnDataSize
);
1969 nRemainingRecSize
-= 20;
1971 if (ImplReadRegion(aPolyPoly
, *mpInputStream
, nRemainingRecSize
, GetWinOrg()))
1973 tools::PolyPolygon
aPolyPolygon(aPolyPoly
);
1974 DrawPolyPolygon( aPolyPolygon
);
1980 case EMR_CREATEDIBPATTERNBRUSHPT
:
1982 sal_uInt32 nStart
= mpInputStream
->Tell() - 8;
1985 mpInputStream
->ReadUInt32( nIndex
);
1987 if ( ( nIndex
& ENHMETA_STOCK_OBJECT
) == 0 )
1989 sal_uInt32 usage
, offBmi
, cbBmi
, offBits
, cbBits
;
1991 mpInputStream
->ReadUInt32( usage
);
1992 mpInputStream
->ReadUInt32( offBmi
);
1993 mpInputStream
->ReadUInt32( cbBmi
);
1994 mpInputStream
->ReadUInt32( offBits
);
1995 mpInputStream
->ReadUInt32( cbBits
);
1997 if ( !mpInputStream
->good() || (cbBits
> (SAL_MAX_UINT32
- 14)) || ((SAL_MAX_UINT32
- 14) - cbBits
< cbBmi
) )
2001 sal_uInt32 nSize
= cbBmi
+ cbBits
+ 14;
2002 if ( nSize
<= ( mnEndPos
- mnStartPos
) )
2004 char* pBuf
= new char[ nSize
];
2006 SvMemoryStream
aTmp( pBuf
, nSize
, StreamMode::READ
| StreamMode::WRITE
);
2007 aTmp
.ObjectOwnsMemory( true );
2008 aTmp
.WriteUChar( 'B' )
2010 .WriteUInt32( cbBits
)
2013 .WriteUInt32( cbBmi
+ 14 );
2015 mpInputStream
->Seek( nStart
+ offBmi
);
2016 char* pWritePos
= pBuf
+ 14;
2017 auto nRead
= mpInputStream
->ReadBytes(pWritePos
, cbBmi
);
2020 // zero remainder if short read
2021 memset(pWritePos
+ nRead
, 0, cbBmi
- nRead
);
2024 mpInputStream
->Seek( nStart
+ offBits
);
2025 pWritePos
= pBuf
+ 14 + cbBmi
;
2026 nRead
= mpInputStream
->ReadBytes(pWritePos
, cbBits
);
2027 if (nRead
!= cbBits
)
2029 // zero remainder if short read
2030 memset(pWritePos
+ nRead
, 0, cbBits
- nRead
);
2034 ReadDIB(aBitmap
, aTmp
, true);
2039 CreateObjectIndexed(nIndex
, std::make_unique
<WinMtfFillStyle
>( aBitmap
));
2045 case EMR_SETDIBITSTODEVICE
:
2047 case EMR_INVERTRGN
:
2048 case EMR_FLATTENPATH
:
2049 case EMR_WIDENPATH
:
2051 case EMR_SETPALETTEENTRIES
:
2052 case EMR_RESIZEPALETTE
:
2053 case EMR_EXTFLOODFILL
:
2055 case EMR_SETCOLORADJUSTMENT
:
2056 case EMR_POLYDRAW16
:
2057 case EMR_CREATECOLORSPACE
:
2058 case EMR_SETCOLORSPACE
:
2059 case EMR_DELETECOLORSPACE
:
2060 case EMR_GLSRECORD
:
2061 case EMR_GLSBOUNDEDRECORD
:
2062 case EMR_PIXELFORMAT
:
2063 case EMR_DRAWESCAPE
:
2064 case EMR_EXTESCAPE
:
2066 case EMR_SMALLTEXTOUT
:
2067 case EMR_FORCEUFIMAPPING
:
2068 case EMR_NAMEDESCAPE
:
2069 case EMR_COLORCORRECTPALETTE
:
2070 case EMR_SETICMPROFILEA
:
2071 case EMR_SETICMPROFILEW
:
2072 case EMR_TRANSPARENTBLT
:
2073 case EMR_TRANSPARENTDIB
:
2074 case EMR_GRADIENTFILL
:
2075 case EMR_SETLINKEDUFIS
:
2076 case EMR_SETMAPPERFLAGS
:
2077 case EMR_SETICMMODE
:
2078 case EMR_CREATEMONOBRUSH
:
2079 case EMR_SETBRUSHORGEX
:
2080 case EMR_SETMETARGN
:
2081 case EMR_SETMITERLIMIT
:
2082 case EMR_EXCLUDECLIPRECT
:
2083 case EMR_REALIZEPALETTE
:
2084 case EMR_SELECTPALETTE
:
2085 case EMR_CREATEPALETTE
:
2086 case EMR_ALPHADIBBLEND
:
2087 case EMR_SETTEXTJUSTIFICATION
:
2089 SAL_WARN("emfio", "TODO: EMF record not implemented: " << record_type_name(nRecType
));
2094 case EMR_HEADER
: // has already been read at ReadHeader()
2097 default : SAL_INFO("emfio", "Unknown Meta Action"); break;
2100 mpInputStream
->Seek( nNextPos
);
2104 maScaledFontHelper
.applyAlternativeFontScale();
2106 if( !maBmpSaveList
.empty() )
2107 ResolveBitmapActions( maBmpSaveList
);
2110 mpInputStream
->Seek(mnEndPos
);
2115 bool EmfReader::ReadHeader()
2117 // Spare me the METAFILEHEADER here
2118 // Reading the METAHEADER - EMR_HEADER ([MS-EMF] section 2.3.4.2 EMR_HEADER Record Types)
2119 sal_uInt32
nType(0), nHeaderSize(0);
2120 mpInputStream
->ReadUInt32(nType
).ReadUInt32(nHeaderSize
);
2121 SAL_INFO ("emfio", "0x0-0x" << std::hex
<< nHeaderSize
<< " " << record_type_name(nType
) << " size: " << std::dec
<< nHeaderSize
);
2122 if (nType
!= 0x00000001)
2124 // per [MS-EMF] 2.3.4.2 EMF Header Record Types, type MUST be 0x00000001
2125 SAL_WARN("emfio", "EMF header type is not set to 0x00000001 - possibly corrupted file?");
2129 // Start reading the EMR_HEADER Header object
2131 // bound size (RectL object, see [MS-WMF] section 2.2.2.19)
2132 SAL_INFO("emfio", "\tBounding rectangle");
2133 tools::Rectangle rclBounds
= ReadRectangle(); // rectangle in logical units
2135 // picture frame size (RectL object)
2136 SAL_INFO("emfio", "\tPicture frame");
2137 tools::Rectangle rclFrame
= ReadRectangle(); // rectangle in device units 1/100th mm
2139 sal_uInt32
nSignature(0);
2140 mpInputStream
->ReadUInt32(nSignature
);
2141 SAL_INFO("emfio", "\tSignature: 0x" << std::hex
<< nSignature
<< std::dec
);
2143 // nSignature MUST be the ASCII characters "FME", see [WS-EMF] 2.2.9 Header Object
2144 // and 2.1.14 FormatSignature Enumeration
2145 if (nSignature
!= 0x464d4520)
2147 SAL_WARN("emfio", "EMF\t\tSignature is not 0x464d4520 (\"FME\") - possibly corrupted file?");
2151 sal_uInt32
nVersion(0);
2152 mpInputStream
->ReadUInt32(nVersion
); // according to [WS-EMF] 2.2.9, this SHOULD be 0x0001000, however
2153 // Microsoft note that not even Windows checks this...
2154 SAL_INFO("emfio", "\tVersion: 0x" << std::hex
<< nVersion
<< std::dec
);
2155 if (nVersion
!= 0x00010000)
2157 SAL_WARN("emfio", "EMF\t\tThis really should be 0x00010000, though not absolutely essential...");
2160 mpInputStream
->ReadUInt32(mnEndPos
); // size of metafile
2161 SAL_INFO("emfio", "\tMetafile size: " << mnEndPos
);
2162 mnEndPos
+= mnStartPos
;
2164 sal_uInt32 nStrmPos
= mpInputStream
->Tell(); // checking if mnEndPos is valid
2165 sal_uInt32 nActualFileSize
= nStrmPos
+ mpInputStream
->remainingSize();
2167 if ( nActualFileSize
< mnEndPos
)
2169 SAL_WARN("emfio", "EMF\t\tEMF Header object records number of bytes as " << mnEndPos
2170 << ", however the file size is actually " << nActualFileSize
2171 << " bytes. Possible file corruption?");
2172 mnEndPos
= nActualFileSize
;
2175 mpInputStream
->ReadUInt32(mnRecordCount
);
2176 SAL_INFO("emfio", "\tRecords: " << mnRecordCount
);
2178 // the number of "handles", or graphics objects used in the metafile
2180 sal_uInt16 nHandlesCount
;
2181 mpInputStream
->ReadUInt16(nHandlesCount
);
2182 SAL_INFO("emfio", "\tGraphics: " << nHandlesCount
);
2184 // the next 2 bytes are reserved, but according to [MS-EMF] section 2.2.9
2185 // it MUST be 0x000 and MUST be ignored... the thing is, having such a specific
2186 // value is actually pretty useful in checking if there is possible corruption
2188 sal_uInt16
nReserved(0);
2189 mpInputStream
->ReadUInt16(nReserved
);
2190 SAL_INFO("emfio", "\tReserved: 0x" << std::hex
<< nReserved
<< std::dec
);
2192 if ( nReserved
!= 0x0000 )
2194 SAL_WARN("emfio", "EMF\t\tEMF Header object's reserved field is NOT 0x0000... possible "
2198 // The next 4 bytes specifies the number of characters in the metafile description.
2199 // The 4 bytes after that specific the offset from this record that contains the
2200 // metafile description... zero means no description string.
2201 // For now, we ignore it.
2203 mpInputStream
->SeekRel(0x8);
2205 sal_uInt32
nPalEntries(0);
2206 mpInputStream
->ReadUInt32(nPalEntries
);
2207 SAL_INFO("emfio", "\tPalette entries: " << nPalEntries
);
2208 sal_Int32
nPixX(0), nPixY(0), nMillX(0), nMillY(0);
2209 mpInputStream
->ReadInt32(nPixX
);
2210 mpInputStream
->ReadInt32(nPixY
);
2211 SAL_INFO("emfio", "\tRef (pixels): " << nPixX
<< ", " << nPixY
);
2212 mpInputStream
->ReadInt32(nMillX
);
2213 mpInputStream
->ReadInt32(nMillY
);
2214 SAL_INFO("emfio", "\tRef (mm): " << nMillX
<< ", " << nMillY
);
2216 SetrclFrame(rclFrame
);
2217 SetrclBounds(rclBounds
);
2218 SetRefPix(Size( nPixX
, nPixY
) );
2219 SetRefMill(Size( nMillX
, nMillY
) );
2221 return checkSeek(*mpInputStream
, mnStartPos
+ nHeaderSize
);
2224 tools::Rectangle
EmfReader::ReadRectangle()
2226 sal_Int32
nLeft(0), nTop(0), nRight(0), nBottom(0);
2227 mpInputStream
->ReadInt32(nLeft
);
2228 mpInputStream
->ReadInt32(nTop
);
2229 mpInputStream
->ReadInt32(nRight
);
2230 mpInputStream
->ReadInt32(nBottom
);
2232 SAL_INFO("emfio", "\t\tLeft: " << nLeft
<< ", top: " << nTop
<< ", right: " << nRight
<< ", bottom: " << nBottom
);
2233 if (nLeft
> nRight
|| nTop
> nBottom
)
2235 SAL_WARN("emfio", "broken rectangle");
2236 return tools::Rectangle::Normalize(Point(nLeft
, nTop
), Point(nRight
, nBottom
));
2239 return tools::Rectangle(nLeft
, nTop
, nRight
, nBottom
);
2242 tools::Rectangle
EmfReader::ReadRectangle( sal_Int32 x1
, sal_Int32 y1
, sal_Int32 x2
, sal_Int32 y2
)
2245 Point
aBR(o3tl::saturating_add
<sal_Int32
>(x2
, -1), o3tl::saturating_add
<sal_Int32
>(y2
, -1));
2246 return tools::Rectangle(aTL
, aBR
);
2250 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */