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