1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include "emfpcustomlinecap.hxx"
21 #include "emfphelperdata.hxx"
22 #include "emfpbrush.hxx"
23 #include "emfppen.hxx"
24 #include "emfppath.hxx"
25 #include "emfpregion.hxx"
26 #include "emfpimage.hxx"
27 #include "emfpimageattributes.hxx"
28 #include "emfpfont.hxx"
29 #include "emfpstringformat.hxx"
30 #include <basegfx/curve/b2dcubicbezier.hxx>
31 #include <wmfemfhelper.hxx>
32 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
33 #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
34 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
35 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
36 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
37 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
38 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
39 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
40 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
41 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
42 #include <drawinglayer/attribute/fontattribute.hxx>
43 #include <basegfx/color/bcolor.hxx>
44 #include <basegfx/color/bcolormodifier.hxx>
45 #include <basegfx/matrix/b2dhommatrixtools.hxx>
46 #include <basegfx/polygon/b2dpolygonclipper.hxx>
47 #include <basegfx/polygon/b2dpolygontools.hxx>
48 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
49 #include <sal/log.hxx>
50 #include <vcl/svapp.hxx>
51 #include <vcl/settings.hxx>
52 #include <i18nlangtag/languagetag.hxx>
53 #include <toolkit/helper/vclunohelper.hxx>
54 
55 #include <algorithm>
56 
57 namespace emfplushelper
58 {
59 
60     enum
61     {
62         WrapModeTile = 0x00000000,
63         WrapModeTileFlipX = 0x00000001,
64         WrapModeTileFlipY = 0x00000002,
65         WrapModeTileFlipXY = 0x00000003,
66         WrapModeClamp = 0x00000004
67     };
68 
emfTypeToName(sal_uInt16 type)69     const char* emfTypeToName(sal_uInt16 type)
70     {
71         switch (type)
72         {
73             case EmfPlusRecordTypeHeader: return "EmfPlusRecordTypeHeader";
74             case EmfPlusRecordTypeEndOfFile: return "EmfPlusRecordTypeEndOfFile";
75             case EmfPlusRecordTypeComment: return "EmfPlusRecordTypeComment";
76             case EmfPlusRecordTypeGetDC: return "EmfPlusRecordTypeGetDC";
77             case EmfPlusRecordTypeObject: return "EmfPlusRecordTypeObject";
78             case EmfPlusRecordTypeFillRects: return "EmfPlusRecordTypeFillRects";
79             case EmfPlusRecordTypeDrawRects: return "EmfPlusRecordTypeDrawRects";
80             case EmfPlusRecordTypeFillPolygon: return "EmfPlusRecordTypeFillPolygon";
81             case EmfPlusRecordTypeDrawLines: return "EmfPlusRecordTypeDrawLines";
82             case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse";
83             case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse";
84             case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie";
85             case EmfPlusRecordTypeDrawPie: return "EmfPlusRecordTypeDrawPie";
86             case EmfPlusRecordTypeDrawArc: return "EmfPlusRecordTypeDrawArc";
87             case EmfPlusRecordTypeFillRegion: return "EmfPlusRecordTypeFillRegion";
88             case EmfPlusRecordTypeFillPath: return "EmfPlusRecordTypeFillPath";
89             case EmfPlusRecordTypeDrawPath: return "EmfPlusRecordTypeDrawPath";
90             case EmfPlusRecordTypeDrawBeziers: return "EmfPlusRecordTypeDrawBeziers";
91             case EmfPlusRecordTypeDrawImage: return "EmfPlusRecordTypeDrawImage";
92             case EmfPlusRecordTypeDrawImagePoints: return "EmfPlusRecordTypeDrawImagePoints";
93             case EmfPlusRecordTypeDrawString: return "EmfPlusRecordTypeDrawString";
94             case EmfPlusRecordTypeSetRenderingOrigin: return "EmfPlusRecordTypeSetRenderingOrigin";
95             case EmfPlusRecordTypeSetAntiAliasMode: return "EmfPlusRecordTypeSetAntiAliasMode";
96             case EmfPlusRecordTypeSetTextRenderingHint: return "EmfPlusRecordTypeSetTextRenderingHint";
97             case EmfPlusRecordTypeSetTextContrast: return "EmfPlusRecordTypeSetTextContrast";
98             case EmfPlusRecordTypeSetInterpolationMode: return "EmfPlusRecordTypeSetInterpolationMode";
99             case EmfPlusRecordTypeSetPixelOffsetMode: return "EmfPlusRecordTypeSetPixelOffsetMode";
100             case EmfPlusRecordTypeSetCompositingQuality: return "EmfPlusRecordTypeSetCompositingQuality";
101             case EmfPlusRecordTypeSave: return "EmfPlusRecordTypeSave";
102             case EmfPlusRecordTypeRestore: return "EmfPlusRecordTypeRestore";
103             case EmfPlusRecordTypeBeginContainer: return "EmfPlusRecordTypeBeginContainer";
104             case EmfPlusRecordTypeBeginContainerNoParams: return "EmfPlusRecordTypeBeginContainerNoParams";
105             case EmfPlusRecordTypeEndContainer: return "EmfPlusRecordTypeEndContainer";
106             case EmfPlusRecordTypeSetWorldTransform: return "EmfPlusRecordTypeSetWorldTransform";
107             case EmfPlusRecordTypeResetWorldTransform: return "EmfPlusRecordTypeResetWorldTransform";
108             case EmfPlusRecordTypeMultiplyWorldTransform: return "EmfPlusRecordTypeMultiplyWorldTransform";
109             case EmfPlusRecordTypeTranslateWorldTransform: return "EmfPlusRecordTypeTranslateWorldTransform";
110             case EmfPlusRecordTypeScaleWorldTransform: return "EmfPlusRecordTypeScaleWorldTransform";
111             case EmfPlusRecordTypeSetPageTransform: return "EmfPlusRecordTypeSetPageTransform";
112             case EmfPlusRecordTypeResetClip: return "EmfPlusRecordTypeResetClip";
113             case EmfPlusRecordTypeSetClipRect: return "EmfPlusRecordTypeSetClipRect";
114             case EmfPlusRecordTypeSetClipPath: return "EmfPlusRecordTypeSetClipPath";
115             case EmfPlusRecordTypeSetClipRegion: return "EmfPlusRecordTypeSetClipRegion";
116             case EmfPlusRecordTypeOffsetClip: return "EmfPlusRecordTypeOffsetClip";
117             case EmfPlusRecordTypeDrawDriverString: return "EmfPlusRecordTypeDrawDriverString";
118         }
119         return "";
120     }
121 
emfObjectToName(sal_uInt16 type)122     static OUString emfObjectToName(sal_uInt16 type)
123     {
124         switch (type)
125         {
126             case EmfPlusObjectTypeBrush: return "EmfPlusObjectTypeBrush";
127             case EmfPlusObjectTypePen: return "EmfPlusObjectTypePen";
128             case EmfPlusObjectTypePath: return "EmfPlusObjectTypePath";
129             case EmfPlusObjectTypeRegion: return "EmfPlusObjectTypeRegion";
130             case EmfPlusObjectTypeImage: return "EmfPlusObjectTypeImage";
131             case EmfPlusObjectTypeFont: return "EmfPlusObjectTypeFont";
132             case EmfPlusObjectTypeStringFormat: return "EmfPlusObjectTypeStringFormat";
133             case EmfPlusObjectTypeImageAttributes: return "EmfPlusObjectTypeImageAttributes";
134             case EmfPlusObjectTypeCustomLineCap: return "EmfPlusObjectTypeCustomLineCap";
135         }
136         return "";
137     }
138 
PixelOffsetModeToString(sal_uInt16 nPixelOffset)139     static OUString PixelOffsetModeToString(sal_uInt16 nPixelOffset)
140     {
141         switch (nPixelOffset)
142         {
143             case PixelOffsetMode::PixelOffsetModeDefault: return "PixelOffsetModeDefault";
144             case PixelOffsetMode::PixelOffsetModeHighSpeed: return "PixelOffsetModeHighSpeed";
145             case PixelOffsetMode::PixelOffsetModeHighQuality: return "PixelOffsetModeHighQuality";
146             case PixelOffsetMode::PixelOffsetModeNone: return "PixelOffsetModeNone";
147             case PixelOffsetMode::PixelOffsetModeHalf: return "PixelOffsetModeHalf";
148         }
149         return "";
150     }
151 
SmoothingModeToString(sal_uInt16 nSmoothMode)152     static OUString SmoothingModeToString(sal_uInt16 nSmoothMode)
153     {
154         switch (nSmoothMode)
155         {
156             case SmoothingMode::SmoothingModeDefault: return "SmoothingModeDefault";
157             case SmoothingMode::SmoothingModeHighSpeed: return "SmoothModeHighSpeed";
158             case SmoothingMode::SmoothingModeHighQuality: return "SmoothingModeHighQuality";
159             case SmoothingMode::SmoothingModeNone: return "SmoothingModeNone";
160             case SmoothingMode::SmoothingModeAntiAlias8x4: return "SmoothingModeAntiAlias8x4";
161             case SmoothingMode::SmoothingModeAntiAlias8x8: return "SmoothingModeAntiAlias8x8";
162         }
163         return "";
164     }
165 
TextRenderingHintToString(sal_uInt16 nHint)166     static OUString TextRenderingHintToString(sal_uInt16 nHint)
167     {
168         switch (nHint)
169         {
170             case TextRenderingHint::TextRenderingHintSystemDefault: return "TextRenderingHintSystemDefault";
171             case TextRenderingHint::TextRenderingHintSingleBitPerPixelGridFit: return "TextRenderingHintSingleBitPerPixelGridFit";
172             case TextRenderingHint::TextRenderingHintSingleBitPerPixel: return "TextRenderingHintSingleBitPerPixel";
173             case TextRenderingHint::TextRenderingHintAntialiasGridFit: return "TextRenderingHintAntialiasGridFit";
174             case TextRenderingHint::TextRenderingHintAntialias: return "TextRenderingHintAntialias";
175             case TextRenderingHint::TextRenderingHintClearTypeGridFit: return "TextRenderingHintClearTypeGridFit";
176         }
177         return "";
178     }
179 
InterpolationModeToString(sal_uInt16 nMode)180     static OUString InterpolationModeToString(sal_uInt16 nMode)
181     {
182         switch (nMode)
183         {
184             case InterpolationMode::InterpolationModeDefault: return "InterpolationModeDefault";
185             case InterpolationMode::InterpolationModeLowQuality: return "InterpolationModeLowQuality";
186             case InterpolationMode::InterpolationModeHighQuality: return "InterpolationModeHighQuality";
187             case InterpolationMode::InterpolationModeBilinear: return "InterpolationModeBilinear";
188             case InterpolationMode::InterpolationModeBicubic: return "InterpolationModeBicubic";
189             case InterpolationMode::InterpolationModeNearestNeighbor: return "InterpolationModeNearestNeighbor";
190             case InterpolationMode::InterpolationModeHighQualityBilinear: return "InterpolationModeHighQualityBilinear";
191             case InterpolationMode::InterpolationModeHighQualityBicubic: return "InterpolationModeHighQualityBicubic";
192         }
193         return "";
194     }
195 
UnitTypeToString(sal_uInt16 nType)196     OUString UnitTypeToString(sal_uInt16 nType)
197     {
198         switch (nType)
199         {
200             case UnitTypeWorld: return "UnitTypeWorld";
201             case UnitTypeDisplay: return "UnitTypeDisplay";
202             case UnitTypePixel: return "UnitTypePixel";
203             case UnitTypePoint: return "UnitTypePoint";
204             case UnitTypeInch: return "UnitTypeInch";
205             case UnitTypeDocument: return "UnitTypeDocument";
206             case UnitTypeMillimeter: return "UnitTypeMillimeter";
207         }
208         return "";
209     }
210 
IsBrush(sal_uInt16 flags)211     static bool IsBrush(sal_uInt16 flags)
212     {
213         return (!((flags >> 15) & 0x0001));
214     }
215 
BrushIDToString(sal_uInt16 flags,sal_uInt32 brushid)216     static OUString BrushIDToString(sal_uInt16 flags, sal_uInt32 brushid)
217     {
218         if (IsBrush(flags))
219             return "EmfPlusBrush ID: " + OUString::number(brushid);
220         else
221             return "ARGB: 0x" + OUString::number(brushid, 16);
222     }
223 
~EMFPObject()224     EMFPObject::~EMFPObject()
225     {
226     }
227 
getUnitToPixelMultiplier(const UnitType aUnitType,const sal_uInt32 aDPI)228     float EmfPlusHelperData::getUnitToPixelMultiplier(const UnitType aUnitType, const sal_uInt32 aDPI)
229     {
230         switch (aUnitType)
231         {
232             case UnitTypePixel:
233                 return 1.0f;
234 
235             case UnitTypePoint:
236                 return aDPI / 72.0;
237 
238             case UnitTypeInch:
239                 return aDPI;
240 
241             case UnitTypeMillimeter:
242                 return aDPI / 25.4;
243 
244             case UnitTypeDocument:
245                 return aDPI / 300.0;
246 
247             case UnitTypeWorld:
248             case UnitTypeDisplay:
249                 SAL_WARN("drawinglayer.emf", "EMF+\t Converting to World/Display.");
250                 return 1.0f;
251 
252             default:
253                 SAL_WARN("drawinglayer.emf", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType);
254                 return 1.0f;
255         }
256     }
257 
processObjectRecord(SvMemoryStream & rObjectStream,sal_uInt16 flags,sal_uInt32 dataSize,bool bUseWholeStream)258     void EmfPlusHelperData::processObjectRecord(SvMemoryStream& rObjectStream, sal_uInt16 flags, sal_uInt32 dataSize, bool bUseWholeStream)
259     {
260         sal_uInt16 objecttype = flags & 0x7f00;
261         sal_uInt16 index = flags & 0xff;
262         SAL_INFO("drawinglayer.emf", "EMF+ Object: " << emfObjectToName(objecttype) << " (0x" << objecttype << ")");
263         SAL_INFO("drawinglayer.emf", "EMF+\tObject slot: " << index);
264         SAL_INFO("drawinglayer.emf", "EMF+\tFlags: " << (flags & 0xff00));
265 
266         switch (objecttype)
267         {
268             case EmfPlusObjectTypeBrush:
269             {
270                 EMFPBrush *brush = new EMFPBrush();
271                 maEMFPObjects[index].reset(brush);
272                 brush->Read(rObjectStream, *this);
273                 break;
274             }
275             case EmfPlusObjectTypePen:
276             {
277                 EMFPPen *pen = new EMFPPen();
278                 maEMFPObjects[index].reset(pen);
279                 pen->Read(rObjectStream, *this);
280                 pen->penWidth = pen->penWidth * getUnitToPixelMultiplier(static_cast<UnitType>(pen->penUnit), mnHDPI);
281                 break;
282             }
283             case EmfPlusObjectTypePath:
284             {
285                 sal_uInt32 header, pathFlags;
286                 sal_Int32 points;
287 
288                 rObjectStream.ReadUInt32(header).ReadInt32(points).ReadUInt32(pathFlags);
289                 SAL_INFO("drawinglayer.emf", "EMF+\t\tHeader: 0x" << std::hex << header);
290                 SAL_INFO("drawinglayer.emf", "EMF+\t\tPoints: " << std::dec << points);
291                 SAL_INFO("drawinglayer.emf", "EMF+\t\tAdditional flags: 0x" << std::hex << pathFlags << std::dec);
292                 EMFPPath *path = new EMFPPath(points);
293                 maEMFPObjects[index].reset(path);
294                 path->Read(rObjectStream, pathFlags);
295                 break;
296             }
297             case EmfPlusObjectTypeRegion:
298             {
299                 EMFPRegion *region = new EMFPRegion();
300                 maEMFPObjects[index].reset(region);
301                 region->ReadRegion(rObjectStream, *this);
302                 break;
303             }
304             case EmfPlusObjectTypeImage:
305             {
306                 EMFPImage *image = new EMFPImage;
307                 maEMFPObjects[index].reset(image);
308                 image->type = 0;
309                 image->width = 0;
310                 image->height = 0;
311                 image->stride = 0;
312                 image->pixelFormat = 0;
313                 image->Read(rObjectStream, dataSize, bUseWholeStream);
314                 break;
315             }
316             case EmfPlusObjectTypeFont:
317             {
318                 EMFPFont *font = new EMFPFont;
319                 maEMFPObjects[index].reset(font);
320                 font->emSize = 0;
321                 font->sizeUnit = 0;
322                 font->fontFlags = 0;
323                 font->Read(rObjectStream);
324                 // tdf#113624 Convert unit to Pixels
325                 font->emSize = font->emSize * getUnitToPixelMultiplier(static_cast<UnitType>(font->sizeUnit), mnHDPI);
326 
327                 break;
328             }
329             case EmfPlusObjectTypeStringFormat:
330             {
331                 EMFPStringFormat *stringFormat = new EMFPStringFormat();
332                 maEMFPObjects[index].reset(stringFormat);
333                 stringFormat->Read(rObjectStream);
334                 break;
335             }
336             case EmfPlusObjectTypeImageAttributes:
337             {
338                 EMFPImageAttributes *imageAttributes = new EMFPImageAttributes();
339                 maEMFPObjects[index].reset(imageAttributes);
340                 imageAttributes->Read(rObjectStream);
341                 break;
342             }
343             case EmfPlusObjectTypeCustomLineCap:
344             {
345                 SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object type 'custom line cap' not yet implemented");
346                 break;
347             }
348             default:
349             {
350                 SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec);
351             }
352         }
353     }
354 
ReadPoint(SvStream & s,float & x,float & y,sal_uInt32 flags)355     void EmfPlusHelperData::ReadPoint(SvStream& s, float& x, float& y, sal_uInt32 flags)
356     {
357         if (flags & 0x800)
358         {
359             // specifies a location in the coordinate space that is relative to
360             // the location specified by the previous element in the array. In the case of the first element in
361             // PointData, a previous location at coordinates (0,0) is assumed.
362             SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Relative coordinates bit detected. Implement parse EMFPlusPointR");
363         }
364 
365         if (flags & 0x4000)
366         {
367             sal_Int16 ix, iy;
368 
369             s.ReadInt16(ix).ReadInt16(iy);
370 
371             x = ix;
372             y = iy;
373         }
374         else
375         {
376             s.ReadFloat(x).ReadFloat(y);
377         }
378     }
379 
ReadRectangle(SvStream & s,float & x,float & y,float & width,float & height,bool bCompressed)380     void EmfPlusHelperData::ReadRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed)
381     {
382         if (bCompressed)
383         {
384             sal_Int16 ix, iy, iw, ih;
385 
386             s.ReadInt16(ix).ReadInt16(iy).ReadInt16(iw).ReadInt16(ih);
387 
388             x = ix;
389             y = iy;
390             width = iw;
391             height = ih;
392         }
393         else
394         {
395             s.ReadFloat(x).ReadFloat(y).ReadFloat(width).ReadFloat(height);
396         }
397     }
398 
readXForm(SvStream & rIn,basegfx::B2DHomMatrix & rTarget)399     bool EmfPlusHelperData::readXForm(SvStream& rIn, basegfx::B2DHomMatrix& rTarget)
400     {
401         rTarget.identity();
402 
403         if (sizeof(float) != 4)
404         {
405             OSL_FAIL("EnhWMFReader::sizeof( float ) != 4");
406             return false;
407         }
408         else
409         {
410             float eM11(0.0);
411             float eM12(0.0);
412             float eM21(0.0);
413             float eM22(0.0);
414             float eDx(0.0);
415             float eDy(0.0);
416             rIn.ReadFloat(eM11).ReadFloat(eM12).ReadFloat(eM21).ReadFloat(eM22).ReadFloat(eDx).ReadFloat(eDy);
417             rTarget = basegfx::B2DHomMatrix(
418                 eM11, eM21, eDx,
419                 eM12, eM22, eDy);
420         }
421 
422         return true;
423     }
424 
mappingChanged()425     void EmfPlusHelperData::mappingChanged()
426     {
427         if (mnPixX == 0 || mnPixY == 0)
428         {
429             SAL_WARN("drawinglayer.emf", "dimensions in pixels is 0");
430             return;
431         }
432         // Call when mnMmX/mnMmY/mnPixX/mnPixY/mnFrameLeft/mnFrameTop/maWorldTransform/ changes.
433         // Currently not used are mnHDPI/mnVDPI/mnFrameRight/mnFrameBottom. *If* these should
434         // be used in the future, this method will need to be called.
435         //
436         // Re-calculate maMapTransform to contain the complete former transformation so that
437         // it can be applied by a single matrix multiplication or be added to an encapsulated
438         // primitive later
439         //
440         // To evtl. correct and see where this came from, please compare with the implementations
441         // of EmfPlusHelperData::MapToDevice and EmfPlusHelperData::Map* in prev versions
442         maMapTransform = maWorldTransform;
443         maMapTransform *= basegfx::utils::createScaleTranslateB2DHomMatrix(100.0 * mnMmX / mnPixX, 100.0 * mnMmY / mnPixY,
444                                                                            double(-mnFrameLeft), double(-mnFrameTop));
445         maMapTransform *= maBaseTransform;
446     }
447 
Map(double ix,double iy) const448     ::basegfx::B2DPoint EmfPlusHelperData::Map(double ix, double iy) const
449     {
450         // map in one step using complete MapTransform (see mappingChanged)
451         return maMapTransform * ::basegfx::B2DPoint(ix, iy);
452     }
453 
EMFPGetBrushColorOrARGBColor(const sal_uInt16 flags,const sal_uInt32 brushIndexOrColor) const454     Color EmfPlusHelperData::EMFPGetBrushColorOrARGBColor(const sal_uInt16 flags, const sal_uInt32 brushIndexOrColor) const {
455         Color color;
456         if (flags & 0x8000) // we use a color
457         {
458             color = Color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff,
459                           (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
460         }
461         else // we use a brush
462         {
463             const EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
464             if (brush)
465             {
466                 color = brush->GetColor();
467                 if (brush->type != BrushTypeSolidColor)
468                     SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Brush other than solid color is not supported");
469             }
470         }
471         return color;
472     }
473 
GraphicStatePush(GraphicStateMap & map,sal_Int32 index)474     void EmfPlusHelperData::GraphicStatePush(GraphicStateMap& map, sal_Int32 index)
475     {
476         GraphicStateMap::iterator iter = map.find( index );
477 
478         if ( iter != map.end() )
479         {
480             map.erase( iter );
481             SAL_INFO("drawinglayer.emf", "EMF+\t\tStack index: " << index << " found and erased");
482         }
483 
484         wmfemfhelper::PropertyHolder state = mrPropertyHolders.Current();
485         // tdf#112500 We need to save world transform somehow, during graphic state push
486         state.setTransformation(maWorldTransform);
487         map[ index ] = state;
488     }
489 
GraphicStatePop(GraphicStateMap & map,sal_Int32 index,wmfemfhelper::PropertyHolder & rState)490     void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index, wmfemfhelper::PropertyHolder& rState)
491     {
492         GraphicStateMap::iterator iter = map.find( index );
493 
494         if ( iter != map.end() )
495         {
496             wmfemfhelper::PropertyHolder state = iter->second;
497 
498             maWorldTransform = state.getTransformation();
499             rState.setClipPolyPolygon( state.getClipPolyPolygon() );
500             mappingChanged();
501             SAL_INFO("drawinglayer.emf", "EMF+\t\tStack index: " << index << " found, maWorldTransform: " << maWorldTransform);
502         }
503     }
504 
EMFPPlusDrawPolygon(const::basegfx::B2DPolyPolygon & polygon,sal_uInt32 penIndex)505     void EmfPlusHelperData::EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon, sal_uInt32 penIndex)
506     {
507         const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 0xff].get());
508         SAL_WARN_IF(!pen, "drawinglayer.emf", "emf+ missing pen");
509 
510         if (!(pen && polygon.count()))
511             return;
512 
513         // we need a line join attribute
514         basegfx::B2DLineJoin lineJoin = basegfx::B2DLineJoin::Round;
515         if (pen->penDataFlags & EmfPlusPenDataJoin) // additional line join information
516         {
517             lineJoin = static_cast<basegfx::B2DLineJoin>(EMFPPen::lcl_convertLineJoinType(pen->lineJoin));
518         }
519 
520         // we need a line cap attribute
521         css::drawing::LineCap lineCap = css::drawing::LineCap_BUTT;
522         if (pen->penDataFlags & EmfPlusPenDataStartCap) // additional line cap information
523         {
524             lineCap = static_cast<css::drawing::LineCap>(EMFPPen::lcl_convertStrokeCap(pen->startCap));
525             SAL_WARN_IF(pen->startCap != pen->endCap, "drawinglayer.emf", "emf+ pen uses different start and end cap");
526         }
527 
528         const double transformedPenWidth = maMapTransform.get(0, 0) * pen->penWidth;
529         drawinglayer::attribute::LineAttribute lineAttribute(pen->GetColor().getBColor(),
530                                                              transformedPenWidth,
531                                                              lineJoin,
532                                                              lineCap);
533 
534         drawinglayer::attribute::StrokeAttribute aStrokeAttribute;
535         if (pen->penDataFlags & EmfPlusPenDataLineStyle && pen->dashStyle != EmfPlusLineStyleCustom) // pen has a predefined line style
536         {
537             // short writing
538             const double pw = maMapTransform.get(1, 1) * pen->penWidth;
539             // taken from the old cppcanvas implementation and multiplied with pen width
540             const std::vector<double> dash = { 3*pw, 3*pw };
541             const std::vector<double> dot = { pw, 3*pw };
542             const std::vector<double> dashdot = { 3*pw, 3*pw, pw, 3*pw };
543             const std::vector<double> dashdotdot = { 3*pw, 3*pw, pw, 3*pw, pw, 3*pw };
544 
545             switch (pen->dashStyle)
546             {
547                 case EmfPlusLineStyleSolid: // do nothing special, use default stroke attribute
548                     break;
549                 case EmfPlusLineStyleDash:
550                     aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dash);
551                     break;
552                 case EmfPlusLineStyleDot:
553                     aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dot);
554                     break;
555                 case EmfPlusLineStyleDashDot:
556                     aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dashdot);
557                     break;
558                 case EmfPlusLineStyleDashDotDot:
559                     aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(dashdotdot);
560                     break;
561             }
562         }
563         else if (pen->penDataFlags & EmfPlusPenDataDashedLine) // pen has a custom dash line
564         {
565             // StrokeAttribute needs a double vector while the pen provides a float vector
566             std::vector<double> aPattern(pen->dashPattern.size());
567             for (size_t i=0; i<aPattern.size(); i++)
568             {
569                 // convert from float to double and multiply with the adjusted pen width
570                 aPattern[i] = maMapTransform.get(1, 1) * pen->penWidth * pen->dashPattern[i];
571             }
572             aStrokeAttribute = drawinglayer::attribute::StrokeAttribute(aPattern);
573         }
574 
575         if (!pen->GetColor().IsTransparent())
576         {
577             mrTargetHolders.Current().append(
578                 new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
579                     polygon,
580                     lineAttribute,
581                     aStrokeAttribute));
582         }
583         else
584         {
585             const drawinglayer::primitive2d::Primitive2DReference aPrimitive(
586                         new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
587                             polygon,
588                             lineAttribute,
589                             aStrokeAttribute));
590 
591             mrTargetHolders.Current().append(
592                         new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
593                             drawinglayer::primitive2d::Primitive2DContainer { aPrimitive },
594                             (255 - pen->GetColor().GetAlpha()) / 255.0));
595         }
596 
597         if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap) && (pen->customStartCap->polygon.begin()->count() > 1))
598         {
599             SAL_WARN("drawinglayer.emf", "EMF+\tCustom Start Line Cap");
600             ::basegfx::B2DPolyPolygon startCapPolygon(pen->customStartCap->polygon);
601 
602             // get the gradient of the first line in the polypolygon
603             double x1 = polygon.begin()->getB2DPoint(0).getX();
604             double y1 = polygon.begin()->getB2DPoint(0).getY();
605             double x2 = polygon.begin()->getB2DPoint(1).getX();
606             double y2 = polygon.begin()->getB2DPoint(1).getY();
607 
608             if ((x2 - x1) != 0)
609             {
610                 double gradient = (y2 - y1) / (x2 - x1);
611 
612                 // now we get the angle that we need to rotate the arrow by
613                 double angle = (M_PI / 2) - atan(gradient);
614 
615                 // rotate the arrow
616                 startCapPolygon.transform(basegfx::utils::createRotateB2DHomMatrix(angle));
617             }
618 
619             startCapPolygon.transform(maMapTransform);
620 
621             basegfx::B2DHomMatrix tran(pen->penWidth, 0.0, polygon.begin()->getB2DPoint(0).getX(),
622                                        0.0, pen->penWidth, polygon.begin()->getB2DPoint(0).getY());
623             startCapPolygon.transform(tran);
624 
625             if (pen->customStartCap->mbIsFilled)
626             {
627                 mrTargetHolders.Current().append(
628                             new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
629                                 startCapPolygon,
630                                 pen->GetColor().getBColor()));
631             }
632             else
633             {
634                 mrTargetHolders.Current().append(
635                             new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
636                                 startCapPolygon,
637                                 lineAttribute,
638                                 aStrokeAttribute));
639             }
640         }
641 
642         if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap) && (pen->customEndCap->polygon.begin()->count() > 1))
643         {
644             SAL_WARN("drawinglayer.emf", "EMF+\tCustom End Line Cap");
645 
646             ::basegfx::B2DPolyPolygon endCapPolygon(pen->customEndCap->polygon);
647 
648             // get the gradient of the first line in the polypolygon
649             double x1 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getX();
650             double y1 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getY();
651             double x2 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 2).getX();
652             double y2 = polygon.begin()->getB2DPoint(polygon.begin()->count() - 2).getY();
653 
654             if ((x2 - x1) != 0)
655             {
656                 double gradient = (y2 - y1) / (x2 - x1);
657 
658                 // now we get the angle that we need to rotate the arrow by
659                 double angle = (M_PI / 2) - atan(gradient);
660 
661                 // rotate the arrow
662                 endCapPolygon.transform(basegfx::utils::createRotateB2DHomMatrix(angle));
663             }
664 
665             endCapPolygon.transform(maMapTransform);
666             basegfx::B2DHomMatrix tran(pen->penWidth, 0.0, polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getX(),
667                                        0.0, pen->penWidth, polygon.begin()->getB2DPoint(polygon.begin()->count() - 1).getY());
668             endCapPolygon.transform(tran);
669 
670             if (pen->customEndCap->mbIsFilled)
671             {
672                 mrTargetHolders.Current().append(
673                             new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
674                                 endCapPolygon,
675                                 pen->GetColor().getBColor()));
676             }
677             else
678             {
679                 mrTargetHolders.Current().append(
680                             new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
681                                 endCapPolygon,
682                                 lineAttribute,
683                                 aStrokeAttribute));
684             }
685         }
686 
687         mrPropertyHolders.Current().setLineColor(pen->GetColor().getBColor());
688         mrPropertyHolders.Current().setLineColorActive(true);
689         mrPropertyHolders.Current().setFillColorActive(false);
690     }
691 
EMFPPlusFillPolygonSolidColor(const::basegfx::B2DPolyPolygon & polygon,Color const & color)692     void EmfPlusHelperData::EMFPPlusFillPolygonSolidColor(const ::basegfx::B2DPolyPolygon& polygon, Color const& color)
693     {
694         if (color.GetAlpha() == 0)
695             return;
696 
697         if (!color.IsTransparent())
698         {
699             // not transparent
700             mrTargetHolders.Current().append(
701                         new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
702                             polygon,
703                             color.getBColor()));
704         }
705         else
706         {
707             const drawinglayer::primitive2d::Primitive2DReference aPrimitive(
708                         new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
709                             polygon,
710                             color.getBColor()));
711 
712             mrTargetHolders.Current().append(
713                         new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
714                             drawinglayer::primitive2d::Primitive2DContainer { aPrimitive },
715                             (255 - color.GetAlpha()) / 255.0));
716         }
717     }
718 
EMFPPlusFillPolygon(const::basegfx::B2DPolyPolygon & polygon,const bool isColor,const sal_uInt32 brushIndexOrColor)719     void EmfPlusHelperData::EMFPPlusFillPolygon(const ::basegfx::B2DPolyPolygon& polygon, const bool isColor, const sal_uInt32 brushIndexOrColor)
720     {
721         if (!polygon.count())
722           return;
723 
724         if (isColor) // use Color
725         {
726             SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, ARGB color: 0x" << std::hex << brushIndexOrColor << std::dec);
727 
728             // EMF Alpha (1 byte): An 8-bit unsigned integer that specifies the transparency of the background,
729             // ranging from 0 for completely transparent to 0xFF for completely opaque.
730             const Color color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
731             EMFPPlusFillPolygonSolidColor(polygon, color);
732 
733             mrPropertyHolders.Current().setFillColor(color.getBColor());
734             mrPropertyHolders.Current().setFillColorActive(true);
735             mrPropertyHolders.Current().setLineColorActive(false);
736         }
737         else // use Brush
738         {
739             EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
740             SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, brush slot: " << brushIndexOrColor << " (brush type: " << (brush ? brush->GetType() : -1) << ")");
741 
742             // give up in case something wrong happened
743             if( !brush )
744                 return;
745 
746             mrPropertyHolders.Current().setFillColorActive(false);
747             mrPropertyHolders.Current().setLineColorActive(false);
748 
749             if (brush->type == BrushTypeSolidColor)
750             {
751                 Color fillColor = brush->solidColor;
752                 EMFPPlusFillPolygonSolidColor(polygon, fillColor);
753             }
754             else if (brush->type == BrushTypeHatchFill)
755             {
756                 // EMF+ like hatching is currently not supported. These are just color blends which serve as an approximation for some of them
757                 // for the others the hatch "background" color (secondColor in brush) is used.
758 
759                 bool isHatchBlend = true;
760                 double blendFactor = 0.0;
761 
762                 switch (brush->hatchStyle)
763                 {
764                     case HatchStyle05Percent: blendFactor = 0.05; break;
765                     case HatchStyle10Percent: blendFactor = 0.10; break;
766                     case HatchStyle20Percent: blendFactor = 0.20; break;
767                     case HatchStyle25Percent: blendFactor = 0.25; break;
768                     case HatchStyle30Percent: blendFactor = 0.30; break;
769                     case HatchStyle40Percent: blendFactor = 0.40; break;
770                     case HatchStyle50Percent: blendFactor = 0.50; break;
771                     case HatchStyle60Percent: blendFactor = 0.60; break;
772                     case HatchStyle70Percent: blendFactor = 0.70; break;
773                     case HatchStyle75Percent: blendFactor = 0.75; break;
774                     case HatchStyle80Percent: blendFactor = 0.80; break;
775                     case HatchStyle90Percent: blendFactor = 0.90; break;
776                     default:
777                         isHatchBlend = false;
778                         break;
779                 }
780                 Color fillColor;
781                 if (isHatchBlend)
782                 {
783                     fillColor = brush->solidColor;
784                     fillColor.Merge(brush->secondColor, static_cast<sal_uInt8>(255 * blendFactor));
785                 }
786                 else
787                 {
788                     fillColor = brush->secondColor;
789                 }
790                 // temporal solution: create a solid colored polygon
791                 // TODO create a 'real' hatching primitive
792                 mrTargetHolders.Current().append(
793                     new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
794                         polygon,
795                         fillColor.getBColor()));
796             }
797             else if (brush->type == BrushTypeTextureFill)
798             {
799                 SAL_WARN("drawinglayer.emf", "EMF+\tTODO: implement BrushTypeTextureFill brush");
800             }
801             else if (brush->type == BrushTypePathGradient || brush->type == BrushTypeLinearGradient)
802 
803             {
804                 if (brush->type == BrushTypePathGradient && !(brush->additionalFlags & 0x1))
805                 {
806                     SAL_WARN("drawinglayer.emf", "EMF+\t TODO Implement displaying BrushTypePathGradient with Boundary: ");
807                 }
808                 ::basegfx::B2DHomMatrix aTextureTransformation;
809 
810                 if (brush->hasTransformation) {
811                    aTextureTransformation = brush->brush_transformation;
812 
813                    // adjust aTextureTransformation for our world space:
814                    // -> revert the mapping -> apply the transformation -> map back
815                    basegfx::B2DHomMatrix aInvertedMapTrasform(maMapTransform);
816                    aInvertedMapTrasform.invert();
817                    aTextureTransformation =  maMapTransform * aTextureTransformation * aInvertedMapTrasform;
818                 }
819 
820                 // select the stored colors
821                 const basegfx::BColor aStartColor = brush->solidColor.getBColor();
822                 const basegfx::BColor aEndColor = brush->secondColor.getBColor();
823                 drawinglayer::primitive2d::SvgGradientEntryVector aVector;
824 
825                 if (brush->blendPositions)
826                 {
827                     SAL_INFO("drawinglayer.emf", "EMF+\t\tUse blend");
828 
829                     // store the blendpoints in the vector
830                     for (int i = 0; i < brush->blendPoints; i++)
831                     {
832                         double aBlendPoint;
833                         basegfx::BColor aColor;
834                         if (brush->type == BrushTypeLinearGradient)
835                         {
836                             aBlendPoint = brush->blendPositions [i];
837                         }
838                         else
839                         {
840                             // seems like SvgRadialGradientPrimitive2D needs doubled, inverted radius
841                             aBlendPoint = 2. * ( 1. - brush->blendPositions [i] );
842                         }
843                         aColor.setGreen( aStartColor.getGreen() + brush->blendFactors[i] * ( aEndColor.getGreen() - aStartColor.getGreen() ) );
844                         aColor.setBlue ( aStartColor.getBlue()  + brush->blendFactors[i] * ( aEndColor.getBlue() - aStartColor.getBlue() ) );
845                         aColor.setRed  ( aStartColor.getRed()   + brush->blendFactors[i] * ( aEndColor.getRed() - aStartColor.getRed() ) );
846                         const double aAlpha = brush->solidColor.GetAlpha() + brush->blendFactors[i] * ( brush->secondColor.GetAlpha() - brush->solidColor.GetAlpha() );
847                         aVector.emplace_back(aBlendPoint, aColor, aAlpha / 255.0);
848                     }
849                 }
850                 else if (brush->colorblendPositions)
851                 {
852                     SAL_INFO("drawinglayer.emf", "EMF+\t\tUse color blend");
853 
854                     // store the colorBlends in the vector
855                     for (int i = 0; i < brush->colorblendPoints; i++)
856                     {
857                         double aBlendPoint;
858                         basegfx::BColor aColor;
859                         if (brush->type == BrushTypeLinearGradient)
860                         {
861                             aBlendPoint = brush->colorblendPositions [i];
862                         }
863                         else
864                         {
865                             // seems like SvgRadialGradientPrimitive2D needs doubled, inverted radius
866                             aBlendPoint = 2. * ( 1. - brush->colorblendPositions [i] );
867                         }
868                         aColor = brush->colorblendColors[i].getBColor();
869                         aVector.emplace_back(aBlendPoint, aColor, brush->colorblendColors[i].GetAlpha() / 255.0 );
870                     }
871                 }
872                 else // ok, no extra points: just start and end
873                 {
874                     if (brush->type == BrushTypeLinearGradient)
875                     {
876                         aVector.emplace_back(0.0, aStartColor, brush->solidColor.GetAlpha() / 255.0);
877                         aVector.emplace_back(1.0, aEndColor, brush->secondColor.GetAlpha() / 255.0);
878                     }
879                     else // again, here reverse
880                     {
881                         aVector.emplace_back(0.0, aEndColor, brush->secondColor.GetAlpha() / 255.0);
882                         aVector.emplace_back(1.0, aStartColor, brush->solidColor.GetAlpha() / 255.0);
883                     }
884                 }
885 
886                 // get the polygon range to be able to map the start/end/center point correctly
887                 // therefore, create a mapping and invert it
888                 basegfx::B2DRange aPolygonRange= polygon.getB2DRange();
889                 basegfx::B2DHomMatrix aPolygonTransformation = basegfx::utils::createScaleTranslateB2DHomMatrix(
890                     aPolygonRange.getWidth(),aPolygonRange.getHeight(),
891                     aPolygonRange.getMinX(), aPolygonRange.getMinY());
892                 aPolygonTransformation.invert();
893 
894                 if (brush->type == BrushTypeLinearGradient)
895                 {
896                     // support for public enum EmfPlusWrapMode
897                     basegfx::B2DPoint aStartPoint = Map(brush->firstPointX, 0.0);
898                     aStartPoint = aPolygonTransformation * aStartPoint;
899                     basegfx::B2DPoint aEndPoint = Map(brush->firstPointX + brush->aWidth, 0.0);
900                     aEndPoint = aPolygonTransformation * aEndPoint;
901 
902                     // support for public enum EmfPlusWrapMode
903                     drawinglayer::primitive2d::SpreadMethod aSpreadMethod(drawinglayer::primitive2d::SpreadMethod::Pad);
904                     switch(brush->wrapMode)
905                     {
906                         case WrapModeTile:
907                         case WrapModeTileFlipY:
908                         {
909                             aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Repeat;
910                             break;
911                         }
912                         case WrapModeTileFlipX:
913                         case WrapModeTileFlipXY:
914                         {
915                             aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Reflect;
916                             break;
917                         }
918                         default:
919                             break;
920                     }
921 
922                     // create the same one used for SVG
923                     mrTargetHolders.Current().append(
924                         new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D(
925                             aTextureTransformation,
926                             polygon,
927                             aVector,
928                             aStartPoint,
929                             aEndPoint,
930                             false,                  // do not use UnitCoordinates
931                             aSpreadMethod));
932                 }
933                 else // BrushTypePathGradient
934                 {
935                     basegfx::B2DPoint aCenterPoint = Map(brush->firstPointX, brush->firstPointY);
936                     aCenterPoint = aPolygonTransformation * aCenterPoint;
937 
938                     // create the same one used for SVG
939                     mrTargetHolders.Current().append(
940                         new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D(
941                             aTextureTransformation,
942                             polygon,
943                             aVector,
944                             aCenterPoint,
945                             0.5,                   // relative radius
946                             true,                  // use UnitCoordinates to stretch the gradient
947                             drawinglayer::primitive2d::SpreadMethod::Repeat,
948                             nullptr));
949                 }
950             }
951         }
952     }
953 
EmfPlusHelperData(SvMemoryStream & rMS,wmfemfhelper::TargetHolders & rTargetHolders,wmfemfhelper::PropertyHolders & rPropertyHolders)954     EmfPlusHelperData::EmfPlusHelperData(
955         SvMemoryStream& rMS,
956         wmfemfhelper::TargetHolders& rTargetHolders,
957         wmfemfhelper::PropertyHolders& rPropertyHolders)
958     :   maBaseTransform(),
959         maWorldTransform(),
960         maMapTransform(),
961         maEMFPObjects(),
962         mfPageScale(0.0),
963         mnOriginX(0),
964         mnOriginY(0),
965         mnHDPI(0),
966         mnVDPI(0),
967         mbSetTextContrast(false),
968         mnTextContrast(0),
969         mnFrameLeft(0),
970         mnFrameTop(0),
971         mnFrameRight(0),
972         mnFrameBottom(0),
973         mnPixX(0),
974         mnPixY(0),
975         mnMmX(0),
976         mnMmY(0),
977         mbMultipart(false),
978         mMFlags(0),
979         mMStream(),
980         mrTargetHolders(rTargetHolders),
981         mrPropertyHolders(rPropertyHolders),
982         bIsGetDCProcessing(false)
983     {
984         rMS.ReadInt32(mnFrameLeft).ReadInt32(mnFrameTop).ReadInt32(mnFrameRight).ReadInt32(mnFrameBottom);
985         SAL_INFO("drawinglayer.emf", "EMF+ picture frame: " << mnFrameLeft << "," << mnFrameTop << " - " << mnFrameRight << "," << mnFrameBottom);
986         rMS.ReadInt32(mnPixX).ReadInt32(mnPixY).ReadInt32(mnMmX).ReadInt32(mnMmY);
987         SAL_INFO("drawinglayer.emf", "EMF+ ref device pixel size: " << mnPixX << "x" << mnPixY << " mm size: " << mnMmX << "x" << mnMmY);
988         readXForm(rMS, maBaseTransform);
989         SAL_INFO("drawinglayer.emf", "EMF+ base transform: " << maBaseTransform);
990         mappingChanged();
991     }
992 
~EmfPlusHelperData()993     EmfPlusHelperData::~EmfPlusHelperData()
994     {
995     }
996 
combineClip(::basegfx::B2DPolyPolygon const & leftPolygon,int combineMode,::basegfx::B2DPolyPolygon const & rightPolygon)997     ::basegfx::B2DPolyPolygon EmfPlusHelperData::combineClip(::basegfx::B2DPolyPolygon const & leftPolygon, int combineMode, ::basegfx::B2DPolyPolygon const & rightPolygon)
998     {
999         basegfx::B2DPolyPolygon aClippedPolyPolygon;
1000         switch (combineMode)
1001         {
1002         case EmfPlusCombineModeReplace:
1003         {
1004             aClippedPolyPolygon = rightPolygon;
1005             break;
1006         }
1007         case EmfPlusCombineModeIntersect:
1008         {
1009             if (leftPolygon.count())
1010             {
1011                 aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon(
1012                             leftPolygon,
1013                             rightPolygon,
1014                             true,
1015                             false);
1016             }
1017             break;
1018         }
1019         case EmfPlusCombineModeUnion:
1020         {
1021             aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationOr(leftPolygon, rightPolygon);
1022             break;
1023         }
1024         case EmfPlusCombineModeXOR:
1025         {
1026             aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationXor(leftPolygon, rightPolygon);
1027             break;
1028         }
1029         case EmfPlusCombineModeExclude:
1030         {
1031             // Replaces the existing region with the part of itself that is not in the new region.
1032             aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(leftPolygon, rightPolygon);
1033             break;
1034         }
1035         case EmfPlusCombineModeComplement:
1036         {
1037             // Replaces the existing region with the part of the new region that is not in the existing region.
1038             aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(rightPolygon, leftPolygon);
1039             break;
1040         }
1041         }
1042         return aClippedPolyPolygon;
1043     }
1044 
processEmfPlusData(SvMemoryStream & rMS,const drawinglayer::geometry::ViewInformation2D &)1045     void EmfPlusHelperData::processEmfPlusData(
1046         SvMemoryStream& rMS,
1047         const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/)
1048     {
1049         sal_uInt64 length = rMS.GetSize();
1050 
1051         if (length < 12)
1052             SAL_WARN("drawinglayer.emf", "length is less than required header size");
1053 
1054         // 12 is minimal valid EMF+ record size; remaining bytes are padding
1055         while (length >= 12)
1056         {
1057             sal_uInt16 type, flags;
1058             sal_uInt32 size, dataSize;
1059             sal_uInt64 next;
1060 
1061             rMS.ReadUInt16(type).ReadUInt16(flags).ReadUInt32(size).ReadUInt32(dataSize);
1062 
1063             next = rMS.Tell() + (size - 12);
1064 
1065             if (size < 12)
1066             {
1067                 SAL_WARN("drawinglayer.emf", "Size field is less than 12 bytes");
1068                 break;
1069             }
1070             else if (size > length)
1071             {
1072                 SAL_WARN("drawinglayer.emf", "Size field is greater than bytes left");
1073                 break;
1074             }
1075 
1076             if (dataSize > (size - 12))
1077             {
1078                 SAL_WARN("drawinglayer.emf", "DataSize field is greater than Size-12");
1079                 break;
1080             }
1081 
1082             SAL_INFO("drawinglayer.emf", "EMF+ " << emfTypeToName(type) << " (0x" << std::hex << type << ")" << std::dec);
1083             SAL_INFO("drawinglayer.emf", "EMF+\t record size: " << size);
1084             SAL_INFO("drawinglayer.emf", "EMF+\t flags: 0x" << std::hex << flags << std::dec);
1085             SAL_INFO("drawinglayer.emf", "EMF+\t data size: " << dataSize);
1086 
1087             if (bIsGetDCProcessing)
1088             {
1089                 SAL_INFO("drawinglayer.emf", "EMF+\t reset the current clipping region for the world space to infinity.");
1090                 wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, mrPropertyHolders);
1091                 bIsGetDCProcessing = false;
1092             }
1093             if (type == EmfPlusRecordTypeObject && ((mbMultipart && (flags & 0x7fff) == (mMFlags & 0x7fff)) || (flags & 0x8000)))
1094             {
1095                 if (!mbMultipart)
1096                 {
1097                     mbMultipart = true;
1098                     mMFlags = flags;
1099                     mMStream.Seek(0);
1100                 }
1101 
1102                 OSL_ENSURE(dataSize >= 4, "No room for TotalObjectSize in EmfPlusContinuedObjectRecord");
1103 
1104                 // 1st 4 bytes are TotalObjectSize
1105                 mMStream.WriteBytes(static_cast<const char *>(rMS.GetData()) + rMS.Tell() + 4, dataSize - 4);
1106                 SAL_INFO("drawinglayer.emf", "EMF+ read next object part size: " << size << " type: " << type << " flags: " << flags << " data size: " << dataSize);
1107             }
1108             else
1109             {
1110                 if (mbMultipart)
1111                 {
1112                     SAL_INFO("drawinglayer.emf", "EMF+ multipart record flags: " << mMFlags);
1113                     mMStream.Seek(0);
1114                     processObjectRecord(mMStream, mMFlags, 0, true);
1115                 }
1116 
1117                 mbMultipart = false;
1118             }
1119 
1120             if (type != EmfPlusRecordTypeObject || !(flags & 0x8000))
1121             {
1122                 switch (type)
1123                 {
1124                     case EmfPlusRecordTypeHeader:
1125                     {
1126                         sal_uInt32 header, version;
1127 
1128                         rMS.ReadUInt32(header).ReadUInt32(version).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI);
1129                         SAL_INFO("drawinglayer.emf", "EMF+\tHeader: 0x" << std::hex << header);
1130                         SAL_INFO("drawinglayer.emf", "EMF+\tVersion: " << std::dec << version);
1131                         SAL_INFO("drawinglayer.emf", "EMF+\tHorizontal DPI: " << mnHDPI);
1132                         SAL_INFO("drawinglayer.emf", "EMF+\tVertical DPI: " << mnVDPI);
1133                         SAL_INFO("drawinglayer.emf", "EMF+\tDual: " << ((flags & 1) ? "true" : "false"));
1134                         break;
1135                     }
1136                     case EmfPlusRecordTypeEndOfFile:
1137                     {
1138                         break;
1139                     }
1140                     case EmfPlusRecordTypeComment:
1141                     {
1142 #if OSL_DEBUG_LEVEL > 1
1143                         unsigned char data;
1144                         OUString hexdata;
1145 
1146                         SAL_INFO("drawinglayer.emf", "EMF+\tDatasize: 0x" << std::hex << dataSize << std::dec);
1147 
1148                         for (sal_uInt32 i=0; i<dataSize; i++)
1149                         {
1150                             rMS.ReadUChar(data);
1151 
1152                             if (i % 16 == 0)
1153                                 hexdata += "\n";
1154 
1155                             OUString padding;
1156                             if ((data & 0xF0) == 0)
1157                                 padding = "0";
1158 
1159                             hexdata += "0x" + padding + OUString::number(data, 16) + " ";
1160                         }
1161 
1162                         SAL_INFO("drawinglayer.emf", "EMF+\t" << hexdata);
1163 #endif
1164                         break;
1165                     }
1166                     case EmfPlusRecordTypeGetDC:
1167                     {
1168                         bIsGetDCProcessing = true;
1169                         SAL_INFO("drawinglayer.emf", "EMF+\tAlready used in svtools wmf/emf filter parser");
1170                         break;
1171                     }
1172                     case EmfPlusRecordTypeObject:
1173                     {
1174                         processObjectRecord(rMS, flags, dataSize);
1175                         break;
1176                     }
1177                     case EmfPlusRecordTypeFillPie:
1178                     case EmfPlusRecordTypeDrawPie:
1179                     case EmfPlusRecordTypeDrawArc:
1180                     {
1181                         float startAngle, sweepAngle;
1182 
1183                         // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
1184                         sal_uInt32 brushIndexOrColor = 999;
1185 
1186                         if (type == EmfPlusRecordTypeFillPie)
1187                         {
1188                             rMS.ReadUInt32(brushIndexOrColor);
1189                             SAL_INFO("drawinglayer.emf", "EMF+\t FillPie colorOrIndex: " << brushIndexOrColor);
1190                         }
1191                         else if (type == EmfPlusRecordTypeDrawPie)
1192                         {
1193                             SAL_INFO("drawinglayer.emf", "EMF+\t DrawPie");
1194                         }
1195                         else
1196                         {
1197                             SAL_INFO("drawinglayer.emf", "EMF+\t DrawArc");
1198                         }
1199 
1200                         rMS.ReadFloat(startAngle).ReadFloat(sweepAngle);
1201                         float dx, dy, dw, dh;
1202                         ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
1203                         SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
1204                         startAngle = basegfx::deg2rad(startAngle);
1205                         sweepAngle = basegfx::deg2rad(sweepAngle);
1206                         float endAngle = startAngle + sweepAngle;
1207                         startAngle = fmodf(startAngle, static_cast<float>(M_PI * 2));
1208 
1209                         if (startAngle < 0.0)
1210                         {
1211                             startAngle += static_cast<float>(M_PI * 2.0);
1212                         }
1213                         endAngle = fmodf(endAngle, static_cast<float>(M_PI * 2.0));
1214 
1215                         if (endAngle < 0.0)
1216                         {
1217                             endAngle += static_cast<float>(M_PI * 2.0);
1218                         }
1219                         if (sweepAngle < 0)
1220                         {
1221                             std::swap(endAngle, startAngle);
1222                         }
1223 
1224                         SAL_INFO("drawinglayer.emf", "EMF+\t Adjusted angles: start " <<
1225                             basegfx::rad2deg(startAngle) << ", end: " << basegfx::rad2deg(endAngle) <<
1226                             " startAngle: " << startAngle << " sweepAngle: " << sweepAngle);
1227                         const ::basegfx::B2DPoint centerPoint(dx + 0.5 * dw, dy + 0.5 * dh);
1228                         ::basegfx::B2DPolygon polygon(
1229                             ::basegfx::utils::createPolygonFromEllipseSegment(centerPoint,
1230                                                                               0.5 * dw, 0.5 * dh,
1231                                                                               startAngle, endAngle));
1232                         if (type != EmfPlusRecordTypeDrawArc)
1233                         {
1234                             polygon.append(centerPoint);
1235                             polygon.setClosed(true);
1236                         }
1237                         ::basegfx::B2DPolyPolygon polyPolygon(polygon);
1238                         polyPolygon.transform(maMapTransform);
1239                         if (type == EmfPlusRecordTypeFillPie)
1240                             EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor);
1241                         else
1242                             EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
1243                     }
1244                     break;
1245                     case EmfPlusRecordTypeFillPath:
1246                     {
1247                         sal_uInt32 index = flags & 0xff;
1248                         sal_uInt32 brushIndexOrColor;
1249                         rMS.ReadUInt32(brushIndexOrColor);
1250                         SAL_INFO("drawinglayer.emf", "EMF+ FillPath slot: " << index);
1251 
1252                         EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[index].get());
1253                         if (path)
1254                             EMFPPlusFillPolygon(path->GetPolygon(*this), flags & 0x8000, brushIndexOrColor);
1255                         else
1256                             SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillPath missing path");
1257                     }
1258                     break;
1259                     case EmfPlusRecordTypeFillRegion:
1260                     {
1261                         sal_uInt32 index = flags & 0xff;
1262                         sal_uInt32 brushIndexOrColor;
1263                         rMS.ReadUInt32(brushIndexOrColor);
1264                         SAL_INFO("drawinglayer.emf", "EMF+\t FillRegion slot: " << index);
1265 
1266                         EMFPRegion* region = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
1267                         if (region)
1268                             EMFPPlusFillPolygon(region->regionPolyPolygon, flags & 0x8000, brushIndexOrColor);
1269                         else
1270                             SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillRegion missing region");
1271                     }
1272                     break;
1273                     case EmfPlusRecordTypeDrawEllipse:
1274                     case EmfPlusRecordTypeFillEllipse:
1275                     {
1276                         // Intentionally very bogus initial value to avoid MSVC complaining about potentially uninitialized local
1277                         // variable. As long as the code stays as intended, this variable will be assigned a (real) value in the case
1278                         // when it is later used.
1279                         sal_uInt32 brushIndexOrColor = 1234567;
1280 
1281                         if (type == EmfPlusRecordTypeFillEllipse)
1282                         {
1283                             rMS.ReadUInt32(brushIndexOrColor);
1284                         }
1285 
1286                         SAL_INFO("drawinglayer.emf", "EMF+\t " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff));
1287                         float dx, dy, dw, dh;
1288                         ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
1289                         SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
1290                         ::basegfx::B2DPolyPolygon polyPolygon(
1291                             ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(dx + 0.5 * dw, dy + 0.5 * dh),
1292                                                                        0.5 * dw, 0.5 * dh));
1293                         polyPolygon.transform(maMapTransform);
1294                         if (type == EmfPlusRecordTypeFillEllipse)
1295                             EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor);
1296                         else
1297                             EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
1298                     }
1299                     break;
1300                     case EmfPlusRecordTypeFillRects:
1301                     case EmfPlusRecordTypeDrawRects:
1302                     {
1303                         // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
1304                         sal_uInt32 brushIndexOrColor = 999;
1305                         sal_Int32 rectangles;
1306                         const bool isColor = (flags & 0x8000);
1307                         ::basegfx::B2DPolygon polygon;
1308 
1309                         if (EmfPlusRecordTypeFillRects == type)
1310                         {
1311                             SAL_INFO("drawinglayer.emf", "EMF+\t FillRects");
1312                             rMS.ReadUInt32(brushIndexOrColor);
1313                             SAL_INFO("drawinglayer.emf", "EMF+\t" << (isColor ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
1314                         }
1315                         else
1316                         {
1317                             SAL_INFO("drawinglayer.emf", "EMF+\t DrawRects");
1318                         }
1319 
1320                         rMS.ReadInt32(rectangles);
1321 
1322                         for (int i = 0; i < rectangles; i++)
1323                         {
1324                             float x, y, width, height;
1325                             ReadRectangle(rMS, x, y, width, height, bool(flags & 0x4000));
1326                             polygon.clear();
1327                             polygon.append(Map(x, y));
1328                             polygon.append(Map(x + width, y));
1329                             polygon.append(Map(x + width, y + height));
1330                             polygon.append(Map(x, y + height));
1331                             polygon.setClosed(true);
1332 
1333                             SAL_INFO("drawinglayer.emf", "EMF+\t\t rectangle: " << x << ", "<< y << " " << width << "x" << height);
1334 
1335                             ::basegfx::B2DPolyPolygon polyPolygon(polygon);
1336                             if (type == EmfPlusRecordTypeFillRects)
1337                                 EMFPPlusFillPolygon(polyPolygon, isColor, brushIndexOrColor);
1338                             else
1339                                 EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
1340                         }
1341                         break;
1342                     }
1343                     case EmfPlusRecordTypeFillPolygon:
1344                     {
1345                         const sal_uInt8 index = flags & 0xff;
1346                         sal_uInt32 brushIndexOrColor;
1347                         sal_Int32 points;
1348 
1349                         rMS.ReadUInt32(brushIndexOrColor);
1350                         rMS.ReadInt32(points);
1351                         SAL_INFO("drawinglayer.emf", "EMF+\t FillPolygon in slot: " << index << " points: " << points);
1352                         SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << " : 0x" << std::hex << brushIndexOrColor << std::dec);
1353 
1354                         EMFPPath path(points, true);
1355                         path.Read(rMS, flags);
1356 
1357                         EMFPPlusFillPolygon(path.GetPolygon(*this), flags & 0x8000, brushIndexOrColor);
1358 
1359                         break;
1360                     }
1361                     case EmfPlusRecordTypeDrawLines:
1362                     {
1363                         sal_uInt32 points;
1364                         rMS.ReadUInt32(points);
1365                         SAL_INFO("drawinglayer.emf", "EMF+\t DrawLines in slot: " << (flags & 0xff) << " points: " << points);
1366                         EMFPPath path(points, true);
1367                         path.Read(rMS, flags);
1368 
1369                         // 0x2000 bit indicates whether to draw an extra line between the last point
1370                         // and the first point, to close the shape.
1371                         EMFPPlusDrawPolygon(path.GetPolygon(*this, true, (flags & 0x2000)), flags);
1372 
1373                         break;
1374                     }
1375                     case EmfPlusRecordTypeDrawPath:
1376                     {
1377                         sal_uInt32 penIndex;
1378                         rMS.ReadUInt32(penIndex);
1379                         SAL_INFO("drawinglayer.emf", "EMF+\t Pen: " << penIndex);
1380 
1381                         EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get());
1382                         if (path)
1383                             EMFPPlusDrawPolygon(path->GetPolygon(*this), penIndex);
1384                         else
1385                             SAL_WARN("drawinglayer.emf", "\t\tEmfPlusRecordTypeDrawPath missing path");
1386 
1387                         break;
1388                     }
1389                     case EmfPlusRecordTypeDrawBeziers:
1390                     {
1391                         sal_uInt32 aCount;
1392                         float x1, y1, x2, y2, x3, y3, x4, y4;
1393                         ::basegfx::B2DPoint aStartPoint, aControlPointA, aControlPointB, aEndPoint;
1394                         ::basegfx::B2DPolygon aPolygon;
1395                         rMS.ReadUInt32(aCount);
1396                         SAL_INFO("drawinglayer.emf", "EMF+\t DrawBeziers slot: " << (flags & 0xff));
1397                         SAL_INFO("drawinglayer.emf", "EMF+\t Number of points: " << aCount);
1398                         SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer.emf", "EMF+\t Bezier Draw not support number of points other than 4, 7, 10, 13, 16...");
1399 
1400                         if (aCount < 4)
1401                         {
1402                             SAL_WARN("drawinglayer.emf", "EMF+\t Bezier Draw does not support less than 4 points. Number of points: " << aCount);
1403                             break;
1404                         }
1405 
1406                         ReadPoint(rMS, x1, y1, flags);
1407                         // We need to add first starting point
1408                         aStartPoint = Map(x1, y1);
1409                         aPolygon.append(aStartPoint);
1410 
1411                         for (sal_uInt32 i = 4; i <= aCount; i += 3)
1412                         {
1413                             ReadPoint(rMS, x2, y2, flags);
1414                             ReadPoint(rMS, x3, y3, flags);
1415                             ReadPoint(rMS, x4, y4, flags);
1416 
1417                             SAL_INFO("drawinglayer.emf", "EMF+\t Bezier points: " << x1 << "," << y1 << " " << x2 << "," << y2 << " " << x3 << "," << y3 << " " << x4 << "," << y4);
1418 
1419                             aStartPoint = Map(x1, y1);
1420                             aControlPointA = Map(x2, y2);
1421                             aControlPointB = Map(x3, y3);
1422                             aEndPoint = Map(x4, y4);
1423 
1424                             ::basegfx::B2DCubicBezier cubicBezier(aStartPoint, aControlPointA, aControlPointB, aEndPoint);
1425                             cubicBezier.adaptiveSubdivideByDistance(aPolygon, 10.0);
1426 
1427                             EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff);
1428 
1429                             // The ending coordinate of one Bezier curve is the starting coordinate of the next.
1430                             x1 = x4;
1431                             y1 = y4;
1432                         }
1433                         break;
1434                     }
1435                     case EmfPlusRecordTypeDrawImage:
1436                     case EmfPlusRecordTypeDrawImagePoints:
1437                     {
1438                         sal_uInt32 imageAttributesId;
1439                         sal_Int32 sourceUnit;
1440                         rMS.ReadUInt32(imageAttributesId).ReadInt32(sourceUnit);
1441                         SAL_INFO("drawinglayer.emf",
1442                                 "EMF+\t " << (type == EmfPlusRecordTypeDrawImage ? "DrawImage"
1443                                                                                 : "DrawImagePoints")
1444                                         << " image attributes Id: " << imageAttributesId
1445                                         << " source unit: " << sourceUnit);
1446                         SAL_INFO("drawinglayer.emf", "EMF+\t TODO: use image attributes");
1447 
1448                         // Source unit of measurement type must be 1 pixel
1449                         if (EMFPImage* image = sourceUnit == UnitTypePixel ?
1450                                 dynamic_cast<EMFPImage*>(maEMFPObjects[flags & 0xff].get()) :
1451                                 nullptr)
1452                         {
1453                             float sx, sy, sw, sh;
1454                             ReadRectangle(rMS, sx, sy, sw, sh);
1455 
1456                             ::tools::Rectangle aSource(Point(sx, sy), Size(sw + 1, sh + 1));
1457                             SAL_INFO("drawinglayer.emf",
1458                                     "EMF+\t "
1459                                         << (type == EmfPlusRecordTypeDrawImage ? "DrawImage"
1460                                                                                 : "DrawImagePoints")
1461                                         << " source rectangle: " << sx << "," << sy << " " << sw << "x"
1462                                         << sh);
1463 
1464                             float dx(0.), dy(0.), dw(0.), dh(0.);
1465                             double fShearX = 0.0;
1466                             double fShearY = 0.0;
1467                             if (type == EmfPlusRecordTypeDrawImagePoints)
1468                             {
1469                                 sal_uInt32 aCount;
1470                                 rMS.ReadUInt32(aCount);
1471 
1472                                 // Number of points used by DrawImagePoints. Exactly 3 points must be specified.
1473                                 if (aCount != 3)
1474                                 {
1475                                     SAL_WARN("drawinglayer.emf", "EMF+\t Wrong EMF+ file. Expected "
1476                                                                 "3 points, received: "
1477                                                                     << aCount);
1478                                     break;
1479                                 }
1480                                 float x1, y1, x2, y2, x3, y3;
1481 
1482                                 ReadPoint(rMS, x1, y1, flags); // upper-left point
1483                                 ReadPoint(rMS, x2, y2, flags); // upper-right
1484                                 ReadPoint(rMS, x3, y3, flags); // lower-left
1485 
1486                                 SAL_INFO("drawinglayer.emf", "EMF+\t destination points: "
1487                                                                 << x1 << "," << y1 << " " << x2 << ","
1488                                                                 << y2 << " " << x3 << "," << y3);
1489                                 dx = x1;
1490                                 dy = y2;
1491                                 dw = x2 - x1;
1492                                 dh = y3 - y1;
1493                                 fShearX = x3 - x1;
1494                                 fShearY = y2 - y1;
1495                             }
1496                             else if (type == EmfPlusRecordTypeDrawImage)
1497                                 ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
1498 
1499                             SAL_INFO("drawinglayer.emf",
1500                                     "EMF+\t Rectangle: " << dx << "," << dy << " " << dw << "x" << dh);
1501                             Size aSize;
1502                             if (image->type == ImageDataTypeBitmap)
1503                             {
1504                                 aSize = image->graphic.GetBitmapEx().GetSizePixel();
1505                                 SAL_INFO("drawinglayer.emf", "EMF+\t Bitmap size: " << aSize.Width()
1506                                                                                     << "x"
1507                                                                                     << aSize.Height());
1508                                 if (sx < 0)
1509                                 {
1510                                     // If src position is negative then we need shift image to right
1511                                     dx = dx + ((-sx) / sw) * dw;
1512                                     if (sx + sw <= aSize.Width())
1513                                         dw = ((sw + sx) / sw) * dw;
1514                                     else
1515                                         dw = (aSize.Width() / sw) * dw;
1516                                 }
1517                                 else if (sx + sw > aSize.Width())
1518                                     // If the src image is smaller that what we want to cut, then we need to scale down
1519                                     dw = ((aSize.Width() - sx) / sw) * dw;
1520 
1521                                 if (sy < 0)
1522                                 {
1523                                     dy = dy + ((-sy) / sh) * dh;
1524                                     if (sy + sh <= aSize.Height())
1525                                         dh = ((sh + sy) / sh) * dh;
1526                                     else
1527                                         dh = (aSize.Height() / sh) * dh;
1528                                 }
1529                                 else if (sy + sh > aSize.Height())
1530                                     dh = ((aSize.Height() - sy) / sh) * dh;
1531                             }
1532                             else
1533                                 SAL_INFO(
1534                                     "drawinglayer.emf",
1535                                     "EMF+\t TODO: Add support for SrcRect to ImageDataTypeMetafile");
1536                             ::basegfx::B2DPoint aDstPoint(dx, dy);
1537                             ::basegfx::B2DSize aDstSize(dw, dh);
1538 
1539                             const basegfx::B2DHomMatrix aTransformMatrix
1540                                 = maMapTransform
1541                                 * basegfx::B2DHomMatrix(
1542                                     /* Row 0, Column 0 */ aDstSize.getX(),
1543                                     /* Row 0, Column 1 */ fShearX,
1544                                     /* Row 0, Column 2 */ aDstPoint.getX(),
1545                                     /* Row 1, Column 0 */ fShearY,
1546                                     /* Row 1, Column 1 */ aDstSize.getY(),
1547                                     /* Row 1, Column 2 */ aDstPoint.getY());
1548 
1549                             if (image->type == ImageDataTypeBitmap)
1550                             {
1551                                 BitmapEx aBmp(image->graphic.GetBitmapEx());
1552                                 aBmp.Crop(aSource);
1553                                 aSize = aBmp.GetSizePixel();
1554                                 if (aSize.Width() > 0 && aSize.Height() > 0)
1555                                 {
1556                                     mrTargetHolders.Current().append(
1557                                         new drawinglayer::primitive2d::BitmapPrimitive2D(
1558                                             VCLUnoHelper::CreateVCLXBitmap(aBmp), aTransformMatrix));
1559                                 }
1560                                 else
1561                                     SAL_WARN("drawinglayer.emf", "EMF+\t warning: empty bitmap");
1562                             }
1563                             else if (image->type == ImageDataTypeMetafile)
1564                             {
1565                                 GDIMetaFile aGDI(image->graphic.GetGDIMetaFile());
1566                                 aGDI.Clip(aSource);
1567                                 mrTargetHolders.Current().append(
1568                                     new drawinglayer::primitive2d::MetafilePrimitive2D(aTransformMatrix,
1569                                                                                     aGDI));
1570                             }
1571                         }
1572                         else
1573                         {
1574                             SAL_WARN("drawinglayer.emf",
1575                                     "EMF+\tDrawImage(Points) Wrong EMF+ file. Only Unit Type Pixel is "
1576                                     "support by EMF+ specification for DrawImage(Points)");
1577                         }
1578                         break;
1579                     }
1580                     case EmfPlusRecordTypeDrawString:
1581                     {
1582                         sal_uInt32 brushId, formatId, stringLength;
1583                         rMS.ReadUInt32(brushId).ReadUInt32(formatId).ReadUInt32(stringLength);
1584                         SAL_INFO("drawinglayer.emf", "EMF+\t FontId: " << OUString::number(flags & 0xFF));
1585                         SAL_INFO("drawinglayer.emf", "EMF+\t BrushId: " << BrushIDToString(flags, brushId));
1586                         SAL_INFO("drawinglayer.emf", "EMF+\t FormatId: " << formatId);
1587                         SAL_INFO("drawinglayer.emf", "EMF+\t Length: " << stringLength);
1588 
1589                         // read the layout rectangle
1590                         float lx, ly, lw, lh;
1591                         rMS.ReadFloat(lx).ReadFloat(ly).ReadFloat(lw).ReadFloat(lh);
1592 
1593                         SAL_INFO("drawinglayer.emf", "EMF+\t DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh);
1594                         // parse the string
1595                         const OUString text = read_uInt16s_ToOUString(rMS, stringLength);
1596                         SAL_INFO("drawinglayer.emf", "EMF+\t DrawString string: " << text);
1597                         // get the stringFormat from the Object table ( this is OPTIONAL and may be nullptr )
1598                         const EMFPStringFormat *stringFormat = dynamic_cast<EMFPStringFormat*>(maEMFPObjects[formatId & 0xff].get());
1599                         // get the font from the flags
1600                         const EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get());
1601                         if (!font)
1602                         {
1603                             break;
1604                         }
1605                         mrPropertyHolders.Current().setFont(vcl::Font(font->family, Size(font->emSize, font->emSize)));
1606 
1607                         drawinglayer::attribute::FontAttribute fontAttribute(
1608                             font->family,                                          // font family
1609                             "",                                                    // (no) font style
1610                             font->Bold() ? 8u : 1u,                                // weight: 8 = bold
1611                             font->family == "SYMBOL",                              // symbol
1612                             stringFormat && stringFormat->DirectionVertical(),     // vertical
1613                             font->Italic(),                                        // italic
1614                             false,                                                 // monospaced
1615                             false,                                                 // outline = false, no such thing in MS-EMFPLUS
1616                             stringFormat && stringFormat->DirectionRightToLeft(),  // right-to-left
1617                             false);                                                // BiDiStrong
1618 
1619                         css::lang::Locale locale;
1620                         double stringAlignmentHorizontalOffset = 0.0;
1621                         double stringAlignmentVerticalOffset = font->emSize;
1622                         if (stringFormat)
1623                         {
1624                             LanguageTag aLanguageTag(static_cast<LanguageType>(stringFormat->language));
1625                             locale = aLanguageTag.getLocale();
1626                             drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
1627 
1628                             aTextLayouter.setFontAttribute(fontAttribute, font->emSize,
1629                                 font->emSize, locale);
1630 
1631                             double fTextWidth = aTextLayouter.getTextWidth(text, 0, stringLength);
1632                             SAL_WARN_IF(stringFormat->DirectionRightToLeft(), "drawinglayer.emf",
1633                                         "EMF+\t DrawString Alignment TODO For a right-to-left layout rectangle, the origin should be at the upper right.");
1634                             if (stringFormat->stringAlignment == StringAlignmentNear)
1635                                 // Alignment is to the left side of the layout rectangle (lx, ly, lw, lh)
1636                                 stringAlignmentHorizontalOffset = stringFormat->leadingMargin * font->emSize;
1637                             else if (stringFormat->stringAlignment == StringAlignmentCenter)
1638                                 // Alignment is centered between the origin and extent of the layout rectangle
1639                                 stringAlignmentHorizontalOffset = 0.5 * lw + (stringFormat->leadingMargin - stringFormat->trailingMargin) * font->emSize - 0.5 * fTextWidth;
1640                             else if (stringFormat->stringAlignment == StringAlignmentFar)
1641                                 // Alignment is to the right side of the layout rectangle
1642                                 stringAlignmentHorizontalOffset = lw - stringFormat->trailingMargin * font->emSize - fTextWidth;
1643 
1644                             if (stringFormat->lineAlign == StringAlignmentNear)
1645                                 stringAlignmentVerticalOffset = font->emSize;
1646                             else if (stringFormat->lineAlign == StringAlignmentCenter)
1647                                 stringAlignmentVerticalOffset = 0.5 * lh + 0.5 * font->emSize;
1648                             else if (stringFormat->lineAlign == StringAlignmentFar)
1649                                 stringAlignmentVerticalOffset = lh;
1650                         }
1651                         else
1652                         {
1653                             // By default LeadingMargin is 1/6 inch
1654                             // TODO for typographic fonts set value to 0.
1655                             stringAlignmentHorizontalOffset = 16.0;
1656 
1657                             // use system default
1658                             locale = Application::GetSettings().GetLanguageTag().getLocale();
1659                         }
1660 
1661                         const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
1662                                     ::basegfx::B2DSize(font->emSize, font->emSize),
1663                                     ::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset,
1664                                                         ly + stringAlignmentVerticalOffset));
1665 
1666                         Color uncorrectedColor = EMFPGetBrushColorOrARGBColor(flags, brushId);
1667                         Color color;
1668 
1669                         if (mbSetTextContrast)
1670                         {
1671                             const auto gammaVal = mnTextContrast / 1000;
1672                             const basegfx::BColorModifier_gamma gamma(gammaVal);
1673 
1674                             // gamma correct transparency color
1675                             sal_uInt16 alpha = uncorrectedColor.GetAlpha();
1676                             alpha = std::clamp(std::pow(alpha, 1.0 / gammaVal), 0.0, 1.0) * 255;
1677 
1678                             basegfx::BColor modifiedColor(gamma.getModifiedColor(uncorrectedColor.getBColor()));
1679                             color.SetRed(modifiedColor.getRed() * 255);
1680                             color.SetGreen(modifiedColor.getGreen() * 255);
1681                             color.SetBlue(modifiedColor.getBlue() * 255);
1682                             color.SetAlpha(alpha);
1683                         }
1684                         else
1685                         {
1686                             color = uncorrectedColor;
1687                         }
1688 
1689                         mrPropertyHolders.Current().setTextColor(color.getBColor());
1690                         mrPropertyHolders.Current().setTextColorActive(true);
1691 
1692                         if (color.GetAlpha() > 0)
1693                         {
1694                             std::vector<double> emptyVector;
1695                             rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText;
1696                             if (font->Underline() || font->Strikeout())
1697                             {
1698                                 pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
1699                                             transformMatrix,
1700                                             text,
1701                                             0,             // text always starts at 0
1702                                             stringLength,
1703                                             emptyVector,   // EMF-PLUS has no DX-array
1704                                             fontAttribute,
1705                                             locale,
1706                                             color.getBColor(), // Font Color
1707                                             COL_TRANSPARENT,   // Fill Color
1708                                             color.getBColor(), // OverlineColor
1709                                             color.getBColor(), // TextlineColor
1710                                             drawinglayer::primitive2d::TEXT_LINE_NONE,
1711                                             font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
1712                                             false,
1713                                             font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE);
1714                             }
1715                             else
1716                             {
1717                                 pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
1718                                             transformMatrix,
1719                                             text,
1720                                             0,             // text always starts at 0
1721                                             stringLength,
1722                                             emptyVector,   // EMF-PLUS has no DX-array
1723                                             fontAttribute,
1724                                             locale,
1725                                             color.getBColor());
1726                             }
1727                             drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText);
1728                             if (color.IsTransparent())
1729                             {
1730                                 aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
1731                                             drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText },
1732                                             (255 - color.GetAlpha()) / 255.0);
1733                             }
1734 
1735                             mrTargetHolders.Current().append(
1736                                         new drawinglayer::primitive2d::TransformPrimitive2D(
1737                                             maMapTransform,
1738                                             drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } ));
1739                         }
1740                         break;
1741                     }
1742                     case EmfPlusRecordTypeSetPageTransform:
1743                     {
1744                         rMS.ReadFloat(mfPageScale);
1745                         SAL_INFO("drawinglayer.emf", "EMF+\t Scale: " << mfPageScale << " unit: " << UnitTypeToString(flags));
1746 
1747                         if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld))
1748                         {
1749                             SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by SetPageTransform in EMF+ specification.");
1750                         }
1751                         else
1752                         {
1753                             mnMmX *= mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnHDPI);
1754                             mnMmY *= mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnVDPI);
1755                             mappingChanged();
1756                         }
1757                         break;
1758                     }
1759                     case EmfPlusRecordTypeSetRenderingOrigin:
1760                     {
1761                         rMS.ReadInt32(mnOriginX).ReadInt32(mnOriginY);
1762                         SAL_INFO("drawinglayer.emf", "EMF+\t SetRenderingOrigin, [x,y]: " << mnOriginX << "," << mnOriginY);
1763                         break;
1764                     }
1765                     case EmfPlusRecordTypeSetTextContrast:
1766                     {
1767                         const sal_uInt16 LOWERGAMMA = 1000;
1768                         const sal_uInt16 UPPERGAMMA = 2200;
1769 
1770                         mbSetTextContrast = true;
1771                         mnTextContrast = flags & 0xFFF;
1772                         SAL_WARN_IF(mnTextContrast > UPPERGAMMA || mnTextContrast < LOWERGAMMA,
1773                             "drawinglayer.emf", "EMF+\t Gamma value is not with bounds 1000 to 2200, value is " << mnTextContrast);
1774                         mnTextContrast = std::min(mnTextContrast, UPPERGAMMA);
1775                         mnTextContrast = std::max(mnTextContrast, LOWERGAMMA);
1776                         SAL_INFO("drawinglayer.emf", "EMF+\t Text contrast: " << (mnTextContrast / 1000) << " gamma");
1777                         break;
1778                     }
1779                     case EmfPlusRecordTypeSetTextRenderingHint:
1780                     {
1781                         sal_uInt8 nTextRenderingHint = (flags & 0xFF) >> 1;
1782                         SAL_INFO("drawinglayer.emf", "EMF+\t Text rendering hint: " << TextRenderingHintToString(nTextRenderingHint));
1783                         SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetTextRenderingHint");
1784                         break;
1785                     }
1786                     case EmfPlusRecordTypeSetAntiAliasMode:
1787                     {
1788                         bool bUseAntiAlias = (flags & 0x0001);
1789                         sal_uInt8 nSmoothingMode = (flags & 0xFE00) >> 1;
1790                         SAL_INFO("drawinglayer.emf", "EMF+\t Antialiasing: " << (bUseAntiAlias ? "enabled" : "disabled"));
1791                         SAL_INFO("drawinglayer.emf", "EMF+\t Smoothing mode: " << SmoothingModeToString(nSmoothingMode));
1792                         SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetAntiAliasMode");
1793                         break;
1794                     }
1795                     case EmfPlusRecordTypeSetInterpolationMode:
1796                     {
1797                         sal_uInt16 nInterpolationMode = flags & 0xFF;
1798                         SAL_INFO("drawinglayer.emf", "EMF+\t Interpolation mode: " << InterpolationModeToString(nInterpolationMode));
1799                         SAL_WARN("drawinglayer.emf", "EMF+\t TODO InterpolationMode");
1800                         break;
1801                     }
1802                     case EmfPlusRecordTypeSetPixelOffsetMode:
1803                     {
1804                         SAL_INFO("drawinglayer.emf", "EMF+\t Pixel offset mode: " << PixelOffsetModeToString(flags));
1805                         SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetPixelOffsetMode");
1806                         break;
1807                     }
1808                     case EmfPlusRecordTypeSetCompositingQuality:
1809                     {
1810                         SAL_INFO("drawinglayer.emf", "EMF+\t TODO SetCompositingQuality");
1811                         break;
1812                     }
1813                     case EmfPlusRecordTypeSave:
1814                     {
1815                         sal_uInt32 stackIndex;
1816                         rMS.ReadUInt32(stackIndex);
1817                         SAL_INFO("drawinglayer.emf", "EMF+\t Save stack index: " << stackIndex);
1818 
1819                         GraphicStatePush(mGSStack, stackIndex);
1820 
1821                         break;
1822                     }
1823                     case EmfPlusRecordTypeRestore:
1824                     {
1825                         sal_uInt32 stackIndex;
1826                         rMS.ReadUInt32(stackIndex);
1827                         SAL_INFO("drawinglayer.emf", "EMF+\t Restore stack index: " << stackIndex);
1828 
1829                         GraphicStatePop(mGSStack, stackIndex, mrPropertyHolders.Current());
1830                         break;
1831                     }
1832                     case EmfPlusRecordTypeBeginContainer:
1833                     {
1834                         float dx, dy, dw, dh;
1835                         ReadRectangle(rMS, dx, dy, dw, dh);
1836                         SAL_INFO("drawinglayer.emf", "EMF+\t Dest RectData: " << dx << "," << dy << " " << dw << "x" << dh);
1837 
1838                         float sx, sy, sw, sh;
1839                         ReadRectangle(rMS, sx, sy, sw, sh);
1840                         SAL_INFO("drawinglayer.emf", "EMF+\t Source RectData: " << sx << "," << sy << " " << sw << "x" << sh);
1841 
1842                         sal_uInt32 stackIndex;
1843                         rMS.ReadUInt32(stackIndex);
1844                         SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container stack index: " << stackIndex << ", PageUnit: " << flags);
1845 
1846                         if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld))
1847                         {
1848                             SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by BeginContainer in EMF+ specification.");
1849                             break;
1850                         }
1851                         const float aPageScaleX = getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnHDPI);
1852                         const float aPageScaleY = getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnVDPI);
1853                         GraphicStatePush(mGSContainerStack, stackIndex);
1854                         const basegfx::B2DHomMatrix transform = basegfx::utils::createScaleTranslateB2DHomMatrix(
1855                             aPageScaleX * ( dw / sw ), aPageScaleY * ( dh / sh ),
1856                             aPageScaleX * ( dx - sx ), aPageScaleY * ( dy - sy) );
1857                         maWorldTransform *= transform;
1858                         mappingChanged();
1859                         break;
1860                     }
1861                     case EmfPlusRecordTypeBeginContainerNoParams:
1862                     {
1863                         sal_uInt32 stackIndex;
1864                         rMS.ReadUInt32(stackIndex);
1865                         SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container No Params stack index: " << stackIndex);
1866 
1867                         GraphicStatePush(mGSContainerStack, stackIndex);
1868                         break;
1869                     }
1870                     case EmfPlusRecordTypeEndContainer:
1871                     {
1872                         sal_uInt32 stackIndex;
1873                         rMS.ReadUInt32(stackIndex);
1874                         SAL_INFO("drawinglayer.emf", "EMF+\t End Container stack index: " << stackIndex);
1875 
1876                         GraphicStatePop(mGSContainerStack, stackIndex, mrPropertyHolders.Current());
1877                         break;
1878                     }
1879                     case EmfPlusRecordTypeSetWorldTransform:
1880                     {
1881                         SAL_INFO("drawinglayer.emf", "EMF+\t SetWorldTransform, Post multiply: " << bool(flags & 0x2000));
1882                         readXForm(rMS, maWorldTransform);
1883                         mappingChanged();
1884                         SAL_INFO("drawinglayer.emf", "EMF+\t\t: " << maWorldTransform);
1885                         break;
1886                     }
1887                     case EmfPlusRecordTypeResetWorldTransform:
1888                     {
1889                         maWorldTransform.identity();
1890                         SAL_INFO("drawinglayer.emf", "EMF+\t World transform: " << maWorldTransform);
1891                         mappingChanged();
1892                         break;
1893                     }
1894                     case EmfPlusRecordTypeMultiplyWorldTransform:
1895                     {
1896                         SAL_INFO("drawinglayer.emf", "EMF+\t MultiplyWorldTransform, post multiply: " << bool(flags & 0x2000));
1897                         basegfx::B2DHomMatrix transform;
1898                         readXForm(rMS, transform);
1899 
1900                         SAL_INFO("drawinglayer.emf",
1901                                  "EMF+\t Transform matrix: " << transform);
1902 
1903                         if (flags & 0x2000)
1904                         {
1905                             // post multiply
1906                             maWorldTransform *= transform;
1907                         }
1908                         else
1909                         {
1910                             // pre multiply
1911                             transform *= maWorldTransform;
1912                             maWorldTransform = transform;
1913                         }
1914 
1915                         mappingChanged();
1916 
1917                         SAL_INFO("drawinglayer.emf",
1918                                  "EMF+\t World transform matrix: " << maWorldTransform);
1919                         break;
1920                     }
1921                     case EmfPlusRecordTypeTranslateWorldTransform:
1922                     {
1923                         SAL_INFO("drawinglayer.emf", "EMF+\t TranslateWorldTransform, Post multiply: " << bool(flags & 0x2000));
1924 
1925                         basegfx::B2DHomMatrix transform;
1926                         float eDx, eDy;
1927                         rMS.ReadFloat(eDx).ReadFloat(eDy);
1928                         transform.set(0, 2, eDx);
1929                         transform.set(1, 2, eDy);
1930 
1931                         SAL_INFO("drawinglayer.emf",
1932                             "EMF+\t Translate matrix: " << transform);
1933 
1934                         if (flags & 0x2000)
1935                         {
1936                             // post multiply
1937                             maWorldTransform *= transform;
1938                         }
1939                         else
1940                         {
1941                             // pre multiply
1942                             transform *= maWorldTransform;
1943                             maWorldTransform = transform;
1944                         }
1945 
1946                         mappingChanged();
1947 
1948                         SAL_INFO("drawinglayer.emf",
1949                                  "EMF+\t World transform matrix: " << maWorldTransform);
1950                         break;
1951                     }
1952                     case EmfPlusRecordTypeScaleWorldTransform:
1953                     {
1954                         basegfx::B2DHomMatrix transform;
1955                         float eSx, eSy;
1956                         rMS.ReadFloat(eSx).ReadFloat(eSy);
1957                         transform.set(0, 0, eSx);
1958                         transform.set(1, 1, eSy);
1959 
1960                         SAL_INFO("drawinglayer.emf", "EMF+\t ScaleWorldTransform Sx: " << eSx <<
1961                                  " Sy: " << eSy << ", Post multiply:" << bool(flags & 0x2000));
1962                         SAL_INFO("drawinglayer.emf",
1963                                  "EMF+\t World transform matrix: " << maWorldTransform);
1964 
1965                         if (flags & 0x2000)
1966                         {
1967                             // post multiply
1968                             maWorldTransform *= transform;
1969                         }
1970                         else
1971                         {
1972                             // pre multiply
1973                             transform *= maWorldTransform;
1974                             maWorldTransform = transform;
1975                         }
1976 
1977                         mappingChanged();
1978 
1979                         SAL_INFO("drawinglayer.emf",
1980                                  "EMF+\t World transform matrix: " << maWorldTransform);
1981                         break;
1982                     }
1983                     case EmfPlusRecordTypeRotateWorldTransform:
1984                     {
1985                         // Angle of rotation in degrees
1986                         float eAngle;
1987                         rMS.ReadFloat(eAngle);
1988 
1989                         SAL_INFO("drawinglayer.emf", "EMF+\t RotateWorldTransform Angle: " << eAngle <<
1990                                  ", post multiply: " << bool(flags & 0x2000));
1991                         // Skipping flags & 0x2000
1992                         // For rotation transformation there is no difference between post and pre multiply
1993                         maWorldTransform.rotate(basegfx::deg2rad(eAngle));
1994                         mappingChanged();
1995 
1996                         SAL_INFO("drawinglayer.emf",
1997                                 "EMF+\t " << maWorldTransform);
1998                         break;
1999                     }
2000                     case EmfPlusRecordTypeResetClip:
2001                     {
2002                         SAL_INFO("drawinglayer.emf", "EMF+ ResetClip");
2003                         // We don't need to read anything more, as Size needs to be set 0x0000000C
2004                         // and DataSize must be set to 0.
2005 
2006                         // Resets the current clipping region for the world space to infinity.
2007                         HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, mrPropertyHolders);
2008                         break;
2009                     }
2010                     case EmfPlusRecordTypeSetClipRect:
2011                     {
2012                         int combineMode = (flags >> 8) & 0xf;
2013 
2014                         SAL_INFO("drawinglayer.emf", "EMF+\t SetClipRect combine mode: " << combineMode);
2015 
2016                         float dx, dy, dw, dh;
2017                         ReadRectangle(rMS, dx, dy, dw, dh);
2018                         SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
2019                         ::basegfx::B2DPoint mappedPoint1(Map(dx, dy));
2020                         ::basegfx::B2DPoint mappedPoint2(Map(dx + dw, dy + dh));
2021 
2022                         ::basegfx::B2DPolyPolygon polyPolygon(
2023                                 ::basegfx::utils::createPolygonFromRect(
2024                                     ::basegfx::B2DRectangle(
2025                                         mappedPoint1.getX(),
2026                                         mappedPoint1.getY(),
2027                                         mappedPoint2.getX(),
2028                                         mappedPoint2.getY())));
2029 
2030                         HandleNewClipRegion(combineClip(mrPropertyHolders.Current().getClipPolyPolygon(),
2031                                                         combineMode, polyPolygon), mrTargetHolders, mrPropertyHolders);
2032                         break;
2033                     }
2034                     case EmfPlusRecordTypeSetClipPath:
2035                     {
2036                         int combineMode = (flags >> 8) & 0xf;
2037                         SAL_INFO("drawinglayer.emf", "EMF+\t SetClipPath combine mode: " << combineMode);
2038                         SAL_INFO("drawinglayer.emf", "EMF+\t Path in slot: " << (flags & 0xff));
2039 
2040                         EMFPPath *path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get());
2041                         if (!path)
2042                         {
2043                             SAL_WARN("drawinglayer.emf", "EMF+\t TODO Unable to find path in slot: " << (flags & 0xff));
2044                             break;
2045                         }
2046 
2047                         ::basegfx::B2DPolyPolygon& clipPoly(path->GetPolygon(*this));
2048 
2049                         HandleNewClipRegion(combineClip(mrPropertyHolders.Current().getClipPolyPolygon(),
2050                                                         combineMode, clipPoly), mrTargetHolders, mrPropertyHolders);
2051                         break;
2052                     }
2053                     case EmfPlusRecordTypeSetClipRegion:
2054                     {
2055                         int combineMode = (flags >> 8) & 0xf;
2056                         SAL_INFO("drawinglayer.emf", "EMF+\t Region in slot: " << (flags & 0xff));
2057                         SAL_INFO("drawinglayer.emf", "EMF+\t Combine mode: " << combineMode);
2058                         EMFPRegion *region = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
2059                         if (!region)
2060                         {
2061                             SAL_WARN("drawinglayer.emf", "EMF+\t TODO Unable to find region in slot: " << (flags & 0xff));
2062                             break;
2063                         }
2064 
2065                         HandleNewClipRegion(combineClip(mrPropertyHolders.Current().getClipPolyPolygon(),
2066                                                         combineMode, region->regionPolyPolygon), mrTargetHolders, mrPropertyHolders);
2067                         break;
2068                     }
2069                     case EmfPlusRecordTypeOffsetClip:
2070                     {
2071                         float dx, dy;
2072                         rMS.ReadFloat(dx).ReadFloat(dy);
2073                         SAL_INFO("drawinglayer.emf", "EMF+\tOffset x:" << dx << ", y:" << dy);
2074 
2075                         basegfx::B2DPolyPolygon aPolyPolygon(
2076                                     mrPropertyHolders.Current().getClipPolyPolygon());
2077 
2078                         SAL_INFO("drawinglayer.emf",
2079                                  "EMF+\t PolyPolygon before translate: " << aPolyPolygon);
2080 
2081                         basegfx::B2DPoint aOffset = Map(dx, dy);
2082                         basegfx::B2DHomMatrix transformMatrix;
2083                         transformMatrix.set(0, 2, aOffset.getX());
2084                         transformMatrix.set(1, 2, aOffset.getY());
2085                         aPolyPolygon.transform(transformMatrix);
2086 
2087                         SAL_INFO("drawinglayer.emf",
2088                                  "EMF+\t PolyPolygon after translate: " << aPolyPolygon <<
2089                                  ", mapped offset x" << aOffset.getX() << ", mapped offset y" << aOffset.getY());
2090                         HandleNewClipRegion(aPolyPolygon, mrTargetHolders, mrPropertyHolders);
2091                         break;
2092                     }
2093                     case EmfPlusRecordTypeDrawDriverString:
2094                     {
2095                         sal_uInt32 brushIndexOrColor;
2096                         sal_uInt32 optionFlags;
2097                         sal_uInt32 hasMatrix;
2098                         sal_uInt32 glyphsCount;
2099                         rMS.ReadUInt32(brushIndexOrColor).ReadUInt32(optionFlags).ReadUInt32(hasMatrix).ReadUInt32(glyphsCount);
2100                         SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
2101                         SAL_INFO("drawinglayer.emf", "EMF+\t Option flags: 0x" << std::hex << optionFlags << std::dec);
2102                         SAL_INFO("drawinglayer.emf", "EMF+\t Has matrix: " << hasMatrix);
2103                         SAL_INFO("drawinglayer.emf", "EMF+\t Glyphs: " << glyphsCount);
2104 
2105                         if ((optionFlags & 1) && glyphsCount > 0)
2106                         {
2107                             std::unique_ptr<float[]> charsPosX(new float[glyphsCount]);
2108                             std::unique_ptr<float[]> charsPosY(new float[glyphsCount]);
2109                             OUString text = read_uInt16s_ToOUString(rMS, glyphsCount);
2110                             SAL_INFO("drawinglayer.emf", "EMF+\t DrawDriverString string: " << text);
2111 
2112                             for (sal_uInt32 i = 0; i<glyphsCount; i++)
2113                             {
2114                                 rMS.ReadFloat(charsPosX[i]).ReadFloat(charsPosY[i]);
2115                                 SAL_INFO("drawinglayer.emf", "EMF+\t\t glyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]);
2116                             }
2117 
2118                             basegfx::B2DHomMatrix transform;
2119 
2120                             if (hasMatrix)
2121                             {
2122                                 readXForm(rMS, transform);
2123                                 SAL_INFO("drawinglayer.emf", "EMF+\tmatrix: " << transform);
2124                             }
2125 
2126                             // get the font from the flags
2127                             EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get());
2128                             if (!font)
2129                             {
2130                                 break;
2131                             }
2132                             // done reading
2133 
2134                             drawinglayer::attribute::FontAttribute fontAttribute(
2135                                 font->family,                                    // font family
2136                                 "",                                              // (no) font style
2137                                 font->Bold() ? 8u : 1u,                          // weight: 8 = bold
2138                                 font->family == "SYMBOL",                        // symbol
2139                                 optionFlags & 0x2,                               // vertical
2140                                 font->Italic(),                                  // italic
2141                                 false,                                           // monospaced
2142                                 false,                                           // outline = false, no such thing in MS-EMFPLUS
2143                                 false,                                           // right-to-left
2144                                 false);                                          // BiDiStrong
2145 
2146                             const Color color = EMFPGetBrushColorOrARGBColor(flags, brushIndexOrColor);
2147                             std::vector<double> aDXArray; // dummy for DX array (not used)
2148 
2149                             // generate TextSimplePortionPrimitive2Ds or TextDecoratedPortionPrimitive2D
2150                             // for all portions of text with the same charsPosY values
2151                             sal_uInt32 pos = 0;
2152                             while (pos < glyphsCount)
2153                             {
2154                                 //determine the current length
2155                                 sal_uInt32 aLength = 1;
2156                                 while (pos + aLength < glyphsCount && std::abs( charsPosY[pos + aLength] - charsPosY[pos] ) < std::numeric_limits< float >::epsilon())
2157                                     aLength++;
2158 
2159                                 // generate the DX-Array
2160                                 aDXArray.clear();
2161                                 for (size_t i = 0; i < aLength - 1; i++)
2162                                 {
2163                                     aDXArray.push_back(charsPosX[pos + i + 1] - charsPosX[pos]);
2164                                 }
2165                                 // last entry
2166                                 aDXArray.push_back(0);
2167 
2168                                 basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
2169                                             ::basegfx::B2DSize(font->emSize, font->emSize),
2170                                             ::basegfx::B2DPoint(charsPosX[pos], charsPosY[pos]));
2171                                 if (hasMatrix)
2172                                     transformMatrix *= transform;
2173                                 if (color.GetAlpha() > 0)
2174                                 {
2175                                     rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText;
2176                                     if (font->Underline() || font->Strikeout())
2177                                     {
2178                                         pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
2179                                                     transformMatrix,
2180                                                     text,
2181                                                     pos,            // take character at current pos
2182                                                     aLength,        // use determined length
2183                                                     aDXArray,       // generated DXArray
2184                                                     fontAttribute,
2185                                                     Application::GetSettings().GetLanguageTag().getLocale(),
2186                                                     color.getBColor(),
2187                                                     COL_TRANSPARENT,
2188                                                     color.getBColor(),
2189                                                     color.getBColor(),
2190                                                     drawinglayer::primitive2d::TEXT_LINE_NONE,
2191                                                     font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
2192                                                     false,
2193                                                     font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE);
2194                                     }
2195                                     else
2196                                     {
2197                                         pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
2198                                                     transformMatrix,
2199                                                     text,
2200                                                     pos,            // take character at current pos
2201                                                     aLength,        // use determined length
2202                                                     aDXArray,       // generated DXArray
2203                                                     fontAttribute,
2204                                                     Application::GetSettings().GetLanguageTag().getLocale(),
2205                                                     color.getBColor());
2206                                     }
2207                                     drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText);
2208                                     if (color.IsTransparent())
2209                                     {
2210                                         aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
2211                                                     drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText },
2212                                                     (255 - color.GetAlpha()) / 255.0);
2213                                     }
2214                                     mrTargetHolders.Current().append(
2215                                                 new drawinglayer::primitive2d::TransformPrimitive2D(
2216                                                     maMapTransform,
2217                                                     drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } ));
2218                                 }
2219 
2220                                 // update pos
2221                                 pos += aLength;
2222                             }
2223                         }
2224                         else
2225                         {
2226                             SAL_WARN("drawinglayer.emf", "EMF+\tTODO: fonts (non-unicode glyphs chars)");
2227                         }
2228                         break;
2229                     }
2230                     default:
2231                     {
2232                         SAL_WARN("drawinglayer.emf", "EMF+ TODO unhandled record type: 0x" << std::hex << type << std::dec);
2233                     }
2234                 }
2235             }
2236 
2237             rMS.Seek(next);
2238 
2239             if (size <= length)
2240             {
2241                 length -= size;
2242             }
2243             else
2244             {
2245                 SAL_WARN("drawinglayer.emf", "ImplRenderer::processEMFPlus: "
2246                     "size " << size << " > length " << length);
2247                 length = 0;
2248             }
2249         }
2250     }
2251 }
2252 
2253 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2254