calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / drawinglayer / source / tools / emfphelperdata.cxx
blob94c4c32f026f3f7aaccd5c203bdca206a10e1116
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "emfpcustomlinecap.hxx"
21 #include "emfphelperdata.hxx"
22 #include "emfpbrush.hxx"
23 #include "emfppen.hxx"
24 #include "emfppath.hxx"
25 #include "emfpregion.hxx"
26 #include "emfpimage.hxx"
27 #include "emfpimageattributes.hxx"
28 #include "emfpfont.hxx"
29 #include "emfpstringformat.hxx"
30 #include <basegfx/curve/b2dcubicbezier.hxx>
31 #include <wmfemfhelper.hxx>
32 #include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
33 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
34 #include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
35 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
36 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
37 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
38 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
39 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
40 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
41 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
42 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
43 #include <drawinglayer/attribute/fontattribute.hxx>
44 #include <basegfx/color/bcolor.hxx>
45 #include <basegfx/color/bcolormodifier.hxx>
46 #include <basegfx/matrix/b2dhommatrixtools.hxx>
47 #include <basegfx/polygon/b2dpolygonclipper.hxx>
48 #include <basegfx/polygon/b2dpolygontools.hxx>
49 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
50 #include <sal/log.hxx>
51 #include <vcl/svapp.hxx>
52 #include <vcl/settings.hxx>
53 #include <i18nlangtag/languagetag.hxx>
54 #include <toolkit/helper/vclunohelper.hxx>
56 #include <algorithm>
58 namespace emfplushelper
61 enum
63 WrapModeTile = 0x00000000,
64 WrapModeTileFlipX = 0x00000001,
65 WrapModeTileFlipY = 0x00000002,
66 WrapModeTileFlipXY = 0x00000003,
67 WrapModeClamp = 0x00000004
70 const char* emfTypeToName(sal_uInt16 type)
72 switch (type)
74 case EmfPlusRecordTypeHeader: return "EmfPlusRecordTypeHeader";
75 case EmfPlusRecordTypeEndOfFile: return "EmfPlusRecordTypeEndOfFile";
76 case EmfPlusRecordTypeComment: return "EmfPlusRecordTypeComment";
77 case EmfPlusRecordTypeGetDC: return "EmfPlusRecordTypeGetDC";
78 case EmfPlusRecordTypeObject: return "EmfPlusRecordTypeObject";
79 case EmfPlusRecordTypeFillRects: return "EmfPlusRecordTypeFillRects";
80 case EmfPlusRecordTypeDrawRects: return "EmfPlusRecordTypeDrawRects";
81 case EmfPlusRecordTypeFillPolygon: return "EmfPlusRecordTypeFillPolygon";
82 case EmfPlusRecordTypeDrawLines: return "EmfPlusRecordTypeDrawLines";
83 case EmfPlusRecordTypeFillClosedCurve: return "EmfPlusRecordTypeFillClosedCurve";
84 case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse";
85 case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse";
86 case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie";
87 case EmfPlusRecordTypeDrawPie: return "EmfPlusRecordTypeDrawPie";
88 case EmfPlusRecordTypeDrawArc: return "EmfPlusRecordTypeDrawArc";
89 case EmfPlusRecordTypeFillRegion: return "EmfPlusRecordTypeFillRegion";
90 case EmfPlusRecordTypeFillPath: return "EmfPlusRecordTypeFillPath";
91 case EmfPlusRecordTypeDrawPath: return "EmfPlusRecordTypeDrawPath";
92 case EmfPlusRecordTypeDrawBeziers: return "EmfPlusRecordTypeDrawBeziers";
93 case EmfPlusRecordTypeDrawClosedCurve: return "EmfPlusRecordTypeDrawClosedCurve";
94 case EmfPlusRecordTypeDrawImage: return "EmfPlusRecordTypeDrawImage";
95 case EmfPlusRecordTypeDrawImagePoints: return "EmfPlusRecordTypeDrawImagePoints";
96 case EmfPlusRecordTypeDrawString: return "EmfPlusRecordTypeDrawString";
97 case EmfPlusRecordTypeSetRenderingOrigin: return "EmfPlusRecordTypeSetRenderingOrigin";
98 case EmfPlusRecordTypeSetAntiAliasMode: return "EmfPlusRecordTypeSetAntiAliasMode";
99 case EmfPlusRecordTypeSetTextRenderingHint: return "EmfPlusRecordTypeSetTextRenderingHint";
100 case EmfPlusRecordTypeSetTextContrast: return "EmfPlusRecordTypeSetTextContrast";
101 case EmfPlusRecordTypeSetInterpolationMode: return "EmfPlusRecordTypeSetInterpolationMode";
102 case EmfPlusRecordTypeSetPixelOffsetMode: return "EmfPlusRecordTypeSetPixelOffsetMode";
103 case EmfPlusRecordTypeSetCompositingQuality: return "EmfPlusRecordTypeSetCompositingQuality";
104 case EmfPlusRecordTypeSave: return "EmfPlusRecordTypeSave";
105 case EmfPlusRecordTypeRestore: return "EmfPlusRecordTypeRestore";
106 case EmfPlusRecordTypeBeginContainer: return "EmfPlusRecordTypeBeginContainer";
107 case EmfPlusRecordTypeBeginContainerNoParams: return "EmfPlusRecordTypeBeginContainerNoParams";
108 case EmfPlusRecordTypeEndContainer: return "EmfPlusRecordTypeEndContainer";
109 case EmfPlusRecordTypeSetWorldTransform: return "EmfPlusRecordTypeSetWorldTransform";
110 case EmfPlusRecordTypeResetWorldTransform: return "EmfPlusRecordTypeResetWorldTransform";
111 case EmfPlusRecordTypeMultiplyWorldTransform: return "EmfPlusRecordTypeMultiplyWorldTransform";
112 case EmfPlusRecordTypeTranslateWorldTransform: return "EmfPlusRecordTypeTranslateWorldTransform";
113 case EmfPlusRecordTypeScaleWorldTransform: return "EmfPlusRecordTypeScaleWorldTransform";
114 case EmfPlusRecordTypeSetPageTransform: return "EmfPlusRecordTypeSetPageTransform";
115 case EmfPlusRecordTypeResetClip: return "EmfPlusRecordTypeResetClip";
116 case EmfPlusRecordTypeSetClipRect: return "EmfPlusRecordTypeSetClipRect";
117 case EmfPlusRecordTypeSetClipPath: return "EmfPlusRecordTypeSetClipPath";
118 case EmfPlusRecordTypeSetClipRegion: return "EmfPlusRecordTypeSetClipRegion";
119 case EmfPlusRecordTypeOffsetClip: return "EmfPlusRecordTypeOffsetClip";
120 case EmfPlusRecordTypeDrawDriverString: return "EmfPlusRecordTypeDrawDriverString";
122 return "";
125 static OUString emfObjectToName(sal_uInt16 type)
127 switch (type)
129 case EmfPlusObjectTypeBrush: return "EmfPlusObjectTypeBrush";
130 case EmfPlusObjectTypePen: return "EmfPlusObjectTypePen";
131 case EmfPlusObjectTypePath: return "EmfPlusObjectTypePath";
132 case EmfPlusObjectTypeRegion: return "EmfPlusObjectTypeRegion";
133 case EmfPlusObjectTypeImage: return "EmfPlusObjectTypeImage";
134 case EmfPlusObjectTypeFont: return "EmfPlusObjectTypeFont";
135 case EmfPlusObjectTypeStringFormat: return "EmfPlusObjectTypeStringFormat";
136 case EmfPlusObjectTypeImageAttributes: return "EmfPlusObjectTypeImageAttributes";
137 case EmfPlusObjectTypeCustomLineCap: return "EmfPlusObjectTypeCustomLineCap";
139 return "";
142 static OUString PixelOffsetModeToString(sal_uInt16 nPixelOffset)
144 switch (nPixelOffset)
146 case PixelOffsetMode::PixelOffsetModeDefault: return "PixelOffsetModeDefault";
147 case PixelOffsetMode::PixelOffsetModeHighSpeed: return "PixelOffsetModeHighSpeed";
148 case PixelOffsetMode::PixelOffsetModeHighQuality: return "PixelOffsetModeHighQuality";
149 case PixelOffsetMode::PixelOffsetModeNone: return "PixelOffsetModeNone";
150 case PixelOffsetMode::PixelOffsetModeHalf: return "PixelOffsetModeHalf";
152 return "";
155 static OUString SmoothingModeToString(sal_uInt16 nSmoothMode)
157 switch (nSmoothMode)
159 case SmoothingMode::SmoothingModeDefault: return "SmoothingModeDefault";
160 case SmoothingMode::SmoothingModeHighSpeed: return "SmoothModeHighSpeed";
161 case SmoothingMode::SmoothingModeHighQuality: return "SmoothingModeHighQuality";
162 case SmoothingMode::SmoothingModeNone: return "SmoothingModeNone";
163 case SmoothingMode::SmoothingModeAntiAlias8x4: return "SmoothingModeAntiAlias8x4";
164 case SmoothingMode::SmoothingModeAntiAlias8x8: return "SmoothingModeAntiAlias8x8";
166 return "";
169 static OUString TextRenderingHintToString(sal_uInt16 nHint)
171 switch (nHint)
173 case TextRenderingHint::TextRenderingHintSystemDefault: return "TextRenderingHintSystemDefault";
174 case TextRenderingHint::TextRenderingHintSingleBitPerPixelGridFit: return "TextRenderingHintSingleBitPerPixelGridFit";
175 case TextRenderingHint::TextRenderingHintSingleBitPerPixel: return "TextRenderingHintSingleBitPerPixel";
176 case TextRenderingHint::TextRenderingHintAntialiasGridFit: return "TextRenderingHintAntialiasGridFit";
177 case TextRenderingHint::TextRenderingHintAntialias: return "TextRenderingHintAntialias";
178 case TextRenderingHint::TextRenderingHintClearTypeGridFit: return "TextRenderingHintClearTypeGridFit";
180 return "";
183 static OUString InterpolationModeToString(sal_uInt16 nMode)
185 switch (nMode)
187 case InterpolationMode::InterpolationModeDefault: return "InterpolationModeDefault";
188 case InterpolationMode::InterpolationModeLowQuality: return "InterpolationModeLowQuality";
189 case InterpolationMode::InterpolationModeHighQuality: return "InterpolationModeHighQuality";
190 case InterpolationMode::InterpolationModeBilinear: return "InterpolationModeBilinear";
191 case InterpolationMode::InterpolationModeBicubic: return "InterpolationModeBicubic";
192 case InterpolationMode::InterpolationModeNearestNeighbor: return "InterpolationModeNearestNeighbor";
193 case InterpolationMode::InterpolationModeHighQualityBilinear: return "InterpolationModeHighQualityBilinear";
194 case InterpolationMode::InterpolationModeHighQualityBicubic: return "InterpolationModeHighQualityBicubic";
196 return "";
199 OUString UnitTypeToString(sal_uInt16 nType)
201 switch (nType)
203 case UnitTypeWorld: return "UnitTypeWorld";
204 case UnitTypeDisplay: return "UnitTypeDisplay";
205 case UnitTypePixel: return "UnitTypePixel";
206 case UnitTypePoint: return "UnitTypePoint";
207 case UnitTypeInch: return "UnitTypeInch";
208 case UnitTypeDocument: return "UnitTypeDocument";
209 case UnitTypeMillimeter: return "UnitTypeMillimeter";
211 return "";
214 static bool IsBrush(sal_uInt16 flags)
216 return (!((flags >> 15) & 0x0001));
219 static OUString BrushIDToString(sal_uInt16 flags, sal_uInt32 brushid)
221 if (IsBrush(flags))
222 return "EmfPlusBrush ID: " + OUString::number(brushid);
223 else
224 return "ARGB: 0x" + OUString::number(brushid, 16);
227 EMFPObject::~EMFPObject()
231 float EmfPlusHelperData::getUnitToPixelMultiplier(const UnitType aUnitType, const sal_uInt32 aDPI)
233 switch (aUnitType)
235 case UnitTypePixel:
236 return 1.0f;
238 case UnitTypePoint:
239 return aDPI / 72.0;
241 case UnitTypeInch:
242 return aDPI;
244 case UnitTypeMillimeter:
245 return aDPI / 25.4;
247 case UnitTypeDocument:
248 return aDPI / 300.0;
250 case UnitTypeWorld:
251 case UnitTypeDisplay:
252 SAL_WARN("drawinglayer.emf", "EMF+\t Converting to World/Display.");
253 return 1.0f;
255 default:
256 SAL_WARN("drawinglayer.emf", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType);
257 return 1.0f;
261 void EmfPlusHelperData::processObjectRecord(SvMemoryStream& rObjectStream, sal_uInt16 flags, sal_uInt32 dataSize, bool bUseWholeStream)
263 sal_uInt16 objecttype = flags & 0x7f00;
264 sal_uInt16 index = flags & 0xff;
265 SAL_INFO("drawinglayer.emf", "EMF+ Object: " << emfObjectToName(objecttype) << " (0x" << objecttype << ")");
266 SAL_INFO("drawinglayer.emf", "EMF+\tObject slot: " << index);
267 SAL_INFO("drawinglayer.emf", "EMF+\tFlags: " << (flags & 0xff00));
269 switch (objecttype)
271 case EmfPlusObjectTypeBrush:
273 EMFPBrush *brush = new EMFPBrush();
274 maEMFPObjects[index].reset(brush);
275 brush->Read(rObjectStream, *this);
276 break;
278 case EmfPlusObjectTypePen:
280 EMFPPen *pen = new EMFPPen();
281 maEMFPObjects[index].reset(pen);
282 pen->Read(rObjectStream, *this);
283 pen->penWidth = pen->penWidth * getUnitToPixelMultiplier(static_cast<UnitType>(pen->penUnit), mnHDPI);
284 break;
286 case EmfPlusObjectTypePath:
288 sal_uInt32 aVersion, aPathPointCount, aPathPointFlags;
290 rObjectStream.ReadUInt32(aVersion).ReadUInt32(aPathPointCount).ReadUInt32(aPathPointFlags);
291 SAL_INFO("drawinglayer.emf", "EMF+\t\tVersion: 0x" << std::hex << aVersion);
292 SAL_INFO("drawinglayer.emf", "EMF+\t\tNumber of points: " << std::dec << aPathPointCount);
293 SAL_INFO("drawinglayer.emf", "EMF+\t\tPath point flags: 0x" << std::hex << aPathPointFlags << std::dec);
294 EMFPPath *path = new EMFPPath(aPathPointCount);
295 maEMFPObjects[index].reset(path);
296 path->Read(rObjectStream, aPathPointFlags);
297 break;
299 case EmfPlusObjectTypeRegion:
301 EMFPRegion *region = new EMFPRegion();
302 maEMFPObjects[index].reset(region);
303 region->ReadRegion(rObjectStream, *this);
304 break;
306 case EmfPlusObjectTypeImage:
308 EMFPImage *image = new EMFPImage;
309 maEMFPObjects[index].reset(image);
310 image->type = 0;
311 image->width = 0;
312 image->height = 0;
313 image->stride = 0;
314 image->pixelFormat = 0;
315 image->Read(rObjectStream, dataSize, bUseWholeStream);
316 break;
318 case EmfPlusObjectTypeFont:
320 EMFPFont *font = new EMFPFont;
321 maEMFPObjects[index].reset(font);
322 font->emSize = 0;
323 font->sizeUnit = 0;
324 font->fontFlags = 0;
325 font->Read(rObjectStream);
326 // tdf#113624 Convert unit to Pixels
327 font->emSize = font->emSize * getUnitToPixelMultiplier(static_cast<UnitType>(font->sizeUnit), mnHDPI);
329 break;
331 case EmfPlusObjectTypeStringFormat:
333 EMFPStringFormat *stringFormat = new EMFPStringFormat();
334 maEMFPObjects[index].reset(stringFormat);
335 stringFormat->Read(rObjectStream);
336 break;
338 case EmfPlusObjectTypeImageAttributes:
340 EMFPImageAttributes *imageAttributes = new EMFPImageAttributes();
341 maEMFPObjects[index].reset(imageAttributes);
342 imageAttributes->Read(rObjectStream);
343 break;
345 case EmfPlusObjectTypeCustomLineCap:
347 SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object type 'custom line cap' not yet implemented");
348 break;
350 default:
352 SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec);
357 void EmfPlusHelperData::ReadPoint(SvStream& s, float& x, float& y, sal_uInt32 flags)
359 if (flags & 0x800)
361 // specifies a location in the coordinate space that is relative to
362 // the location specified by the previous element in the array. In the case of the first element in
363 // PointData, a previous location at coordinates (0,0) is assumed.
364 SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Relative coordinates bit detected. Implement parse EMFPlusPointR");
367 if (flags & 0x4000)
369 sal_Int16 ix, iy;
371 s.ReadInt16(ix).ReadInt16(iy);
373 x = ix;
374 y = iy;
376 else
378 s.ReadFloat(x).ReadFloat(y);
382 void EmfPlusHelperData::ReadRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed)
384 if (bCompressed)
386 sal_Int16 ix, iy, iw, ih;
388 s.ReadInt16(ix).ReadInt16(iy).ReadInt16(iw).ReadInt16(ih);
390 x = ix;
391 y = iy;
392 width = iw;
393 height = ih;
395 else
397 s.ReadFloat(x).ReadFloat(y).ReadFloat(width).ReadFloat(height);
401 bool EmfPlusHelperData::readXForm(SvStream& rIn, basegfx::B2DHomMatrix& rTarget)
403 rTarget.identity();
405 if (sizeof(float) != 4)
407 OSL_FAIL("EnhWMFReader::sizeof( float ) != 4");
408 return false;
410 else
412 float eM11(0.0);
413 float eM12(0.0);
414 float eM21(0.0);
415 float eM22(0.0);
416 float eDx(0.0);
417 float eDy(0.0);
418 rIn.ReadFloat(eM11).ReadFloat(eM12).ReadFloat(eM21).ReadFloat(eM22).ReadFloat(eDx).ReadFloat(eDy);
419 rTarget = basegfx::B2DHomMatrix(
420 eM11, eM21, eDx,
421 eM12, eM22, eDy);
424 return true;
427 void EmfPlusHelperData::mappingChanged()
429 if (mnPixX == 0 || mnPixY == 0)
431 SAL_WARN("drawinglayer.emf", "dimensions in pixels is 0");
432 return;
434 // Call when mnMmX/mnMmY/mnPixX/mnPixY/mnFrameLeft/mnFrameTop/maWorldTransform/ changes.
435 // Currently not used are mnHDPI/mnVDPI/mnFrameRight/mnFrameBottom. *If* these should
436 // be used in the future, this method will need to be called.
438 // Re-calculate maMapTransform to contain the complete former transformation so that
439 // it can be applied by a single matrix multiplication or be added to an encapsulated
440 // primitive later
442 // To evtl. correct and see where this came from, please compare with the implementations
443 // of EmfPlusHelperData::MapToDevice and EmfPlusHelperData::Map* in prev versions
444 maMapTransform = maWorldTransform;
445 maMapTransform *= basegfx::utils::createScaleTranslateB2DHomMatrix(100.0 * mnMmX / mnPixX, 100.0 * mnMmY / mnPixY,
446 double(-mnFrameLeft), double(-mnFrameTop));
447 maMapTransform *= maBaseTransform;
449 // Used only for performance optimization, to do not calculate it every line draw
450 mdExtractedXScale = std::hypot(maMapTransform.a(), maMapTransform.b());
451 mdExtractedYScale = std::hypot(maMapTransform.c(), maMapTransform.d());
454 ::basegfx::B2DPoint EmfPlusHelperData::Map(double ix, double iy) const
456 // map in one step using complete MapTransform (see mappingChanged)
457 return maMapTransform * ::basegfx::B2DPoint(ix, iy);
460 Color EmfPlusHelperData::EMFPGetBrushColorOrARGBColor(const sal_uInt16 flags, const sal_uInt32 brushIndexOrColor) const {
461 Color color;
462 if (flags & 0x8000) // we use a color
464 color = Color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff,
465 (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
467 else // we use a brush
469 const EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
470 if (brush)
472 color = brush->GetColor();
473 if (brush->type != BrushTypeSolidColor)
474 SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Brush other than solid color is not supported");
477 return color;
480 void EmfPlusHelperData::GraphicStatePush(GraphicStateMap& map, sal_Int32 index)
482 GraphicStateMap::iterator iter = map.find( index );
484 if ( iter != map.end() )
486 map.erase( iter );
487 SAL_INFO("drawinglayer.emf", "EMF+\t\tStack index: " << index << " found and erased");
490 wmfemfhelper::PropertyHolder state = mrPropertyHolders.Current();
491 // tdf#112500 We need to save world transform somehow, during graphic state push
492 state.setTransformation(maWorldTransform);
493 map[ index ] = state;
496 void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index)
498 GraphicStateMap::iterator iter = map.find(index);
500 if (iter != map.end())
502 wmfemfhelper::PropertyHolder state = iter->second;
504 maWorldTransform = state.getTransformation();
505 if (state.getClipPolyPolygonActive())
507 SAL_INFO("drawinglayer.emf",
508 "EMF+\t Restore clipping region to saved in index: " << index);
509 wmfemfhelper::HandleNewClipRegion(state.getClipPolyPolygon(), mrTargetHolders,
510 mrPropertyHolders);
512 else
514 SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping");
515 wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders,
516 mrPropertyHolders);
518 mappingChanged();
519 SAL_INFO("drawinglayer.emf",
520 "EMF+\t\tStack index: " << index
521 << " found, maWorldTransform: " << maWorldTransform);
525 drawinglayer::attribute::LineStartEndAttribute
526 EmfPlusHelperData::CreateLineEnd(const sal_Int32 aCap, const float aPenWidth) const
528 const double pw = mdExtractedYScale * aPenWidth;
529 if (aCap == LineCapTypeSquare)
531 basegfx::B2DPolygon aCapPolygon(
532 { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
533 aCapPolygon.setClosed(true);
534 return drawinglayer::attribute::LineStartEndAttribute(
535 pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
537 else if (aCap == LineCapTypeRound)
539 basegfx::B2DPolygon aCapPolygon(
540 { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.9236, -0.3827},
541 {0.7071, -0.7071}, {0.3827, -0.9236}, {0.0, -1.0}, {-0.3827, -0.9236},
542 {-0.7071, -0.7071}, {-0.9236, -0.3827}, {-1.0, 0.0} });
543 aCapPolygon.setClosed(true);
544 return drawinglayer::attribute::LineStartEndAttribute(
545 pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
547 else if (aCap == LineCapTypeTriangle)
549 basegfx::B2DPolygon aCapPolygon(
550 { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.0, -1.0}, {-1.0, 0.0} });
551 aCapPolygon.setClosed(true);
552 return drawinglayer::attribute::LineStartEndAttribute(
553 pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
555 else if (aCap == LineCapTypeSquareAnchor)
557 basegfx::B2DPolygon aCapPolygon(
558 { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
559 aCapPolygon.setClosed(true);
560 return drawinglayer::attribute::LineStartEndAttribute(
561 1.5 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
563 else if (aCap == LineCapTypeRoundAnchor)
565 const basegfx::B2DPolygon aCapPolygon
566 = ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(0.0, 0.0), 1.0, 1.0);
567 return drawinglayer::attribute::LineStartEndAttribute(
568 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
570 else if (aCap == LineCapTypeDiamondAnchor)
572 basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 0.0}, {0.5, 0.5},
573 {0.5, 1.0}, {-0.5, 1.0}, {-0.5, 0.5},
574 {-1.0, 0.0} });
575 aCapPolygon.setClosed(true);
576 return drawinglayer::attribute::LineStartEndAttribute(
577 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
579 else if (aCap == LineCapTypeArrowAnchor)
581 basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
582 aCapPolygon.setClosed(true);
583 return drawinglayer::attribute::LineStartEndAttribute(
584 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
586 return drawinglayer::attribute::LineStartEndAttribute();
589 void EmfPlusHelperData::EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon,
590 sal_uInt32 penIndex)
592 const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 0xff].get());
593 SAL_WARN_IF(!pen, "drawinglayer.emf", "emf+ missing pen");
595 if (!(pen && polygon.count()))
596 return;
598 const double transformedPenWidth = mdExtractedYScale * pen->penWidth;
599 drawinglayer::attribute::LineAttribute lineAttribute(
600 pen->GetColor().getBColor(), transformedPenWidth, pen->maLineJoin,
601 css::drawing::LineCap_BUTT, //TODO implement PenDataDashedLineCap support here
602 pen->fMiterMinimumAngle);
604 drawinglayer::attribute::LineStartEndAttribute aStart;
605 if (pen->penDataFlags & EmfPlusPenDataStartCap)
607 if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap)
608 && (pen->customStartCap->polygon.begin()->count() > 1))
609 aStart = drawinglayer::attribute::LineStartEndAttribute(
610 pen->customStartCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
611 * pen->customStartCap->widthScale * pen->penWidth,
612 pen->customStartCap->polygon, false);
613 else
614 aStart = EmfPlusHelperData::CreateLineEnd(pen->startCap, pen->penWidth);
617 drawinglayer::attribute::LineStartEndAttribute aEnd;
618 if (pen->penDataFlags & EmfPlusPenDataEndCap)
620 if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap)
621 && (pen->customEndCap->polygon.begin()->count() > 1))
622 aEnd = drawinglayer::attribute::LineStartEndAttribute(
623 pen->customEndCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
624 * pen->customEndCap->widthScale * pen->penWidth,
625 pen->customEndCap->polygon, false);
626 else
627 aEnd = EmfPlusHelperData::CreateLineEnd(pen->endCap, pen->penWidth);
630 if (pen->GetColor().IsTransparent())
632 drawinglayer::primitive2d::Primitive2DContainer aContainer;
633 if (aStart.isDefault() && aEnd.isDefault())
634 aContainer.append(drawinglayer::primitive2d::Primitive2DReference(
635 new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
636 polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale))));
637 else
639 aContainer.resize(polygon.count());
640 for (sal_uInt32 i = 0; i < polygon.count(); i++)
641 aContainer[i] = drawinglayer::primitive2d::Primitive2DReference(
642 new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
643 polygon.getB2DPolygon(i), lineAttribute,
644 pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd));
646 mrTargetHolders.Current().append(
647 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
648 std::move(aContainer), (255 - pen->GetColor().GetAlpha()) / 255.0));
650 else
652 if (aStart.isDefault() && aEnd.isDefault())
653 mrTargetHolders.Current().append(
654 new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
655 polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale)));
656 else
657 for (sal_uInt32 i = 0; i < polygon.count(); i++)
659 mrTargetHolders.Current().append(
660 new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
661 polygon.getB2DPolygon(i), lineAttribute,
662 pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd));
665 mrPropertyHolders.Current().setLineColor(pen->GetColor().getBColor());
666 mrPropertyHolders.Current().setLineColorActive(true);
667 mrPropertyHolders.Current().setFillColorActive(false);
670 void EmfPlusHelperData::EMFPPlusFillPolygonSolidColor(const ::basegfx::B2DPolyPolygon& polygon, Color const& color)
672 if (color.GetAlpha() == 0)
673 return;
675 if (!color.IsTransparent())
677 // not transparent
678 mrTargetHolders.Current().append(
679 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
680 polygon,
681 color.getBColor()));
683 else
685 const drawinglayer::primitive2d::Primitive2DReference aPrimitive(
686 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
687 polygon,
688 color.getBColor()));
690 mrTargetHolders.Current().append(
691 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
692 drawinglayer::primitive2d::Primitive2DContainer { aPrimitive },
693 (255 - color.GetAlpha()) / 255.0));
697 void EmfPlusHelperData::EMFPPlusFillPolygon(const ::basegfx::B2DPolyPolygon& polygon, const bool isColor, const sal_uInt32 brushIndexOrColor)
699 if (!polygon.count())
700 return;
702 if (isColor) // use Color
704 SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, ARGB color: 0x" << std::hex << brushIndexOrColor << std::dec);
706 // EMF Alpha (1 byte): An 8-bit unsigned integer that specifies the transparency of the background,
707 // ranging from 0 for completely transparent to 0xFF for completely opaque.
708 const Color color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
709 EMFPPlusFillPolygonSolidColor(polygon, color);
711 mrPropertyHolders.Current().setFillColor(color.getBColor());
712 mrPropertyHolders.Current().setFillColorActive(true);
713 mrPropertyHolders.Current().setLineColorActive(false);
715 else // use Brush
717 EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
718 SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, brush slot: " << brushIndexOrColor << " (brush type: " << (brush ? brush->GetType() : -1) << ")");
720 // give up in case something wrong happened
721 if( !brush )
722 return;
724 mrPropertyHolders.Current().setFillColorActive(false);
725 mrPropertyHolders.Current().setLineColorActive(false);
727 if (brush->type == BrushTypeSolidColor)
729 Color fillColor = brush->solidColor;
730 EMFPPlusFillPolygonSolidColor(polygon, fillColor);
732 else if (brush->type == BrushTypeHatchFill)
734 // EMF+ like hatching is currently not supported. These are just color blends which serve as an approximation for some of them
735 // for the others the hatch "background" color (secondColor in brush) is used.
737 bool isHatchBlend = true;
738 double blendFactor = 0.0;
740 switch (brush->hatchStyle)
742 case HatchStyle05Percent: blendFactor = 0.05; break;
743 case HatchStyle10Percent: blendFactor = 0.10; break;
744 case HatchStyle20Percent: blendFactor = 0.20; break;
745 case HatchStyle25Percent: blendFactor = 0.25; break;
746 case HatchStyle30Percent: blendFactor = 0.30; break;
747 case HatchStyle40Percent: blendFactor = 0.40; break;
748 case HatchStyle50Percent: blendFactor = 0.50; break;
749 case HatchStyle60Percent: blendFactor = 0.60; break;
750 case HatchStyle70Percent: blendFactor = 0.70; break;
751 case HatchStyle75Percent: blendFactor = 0.75; break;
752 case HatchStyle80Percent: blendFactor = 0.80; break;
753 case HatchStyle90Percent: blendFactor = 0.90; break;
754 default:
755 isHatchBlend = false;
756 break;
758 Color fillColor;
759 if (isHatchBlend)
761 fillColor = brush->solidColor;
762 fillColor.Merge(brush->secondColor, static_cast<sal_uInt8>(255 * blendFactor));
764 else
766 fillColor = brush->secondColor;
768 // temporal solution: create a solid colored polygon
769 // TODO create a 'real' hatching primitive
770 mrTargetHolders.Current().append(
771 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
772 polygon,
773 fillColor.getBColor()));
775 else if (brush->type == BrushTypeTextureFill)
777 SAL_WARN("drawinglayer.emf", "EMF+\tTODO: implement BrushTypeTextureFill brush");
779 else if (brush->type == BrushTypePathGradient || brush->type == BrushTypeLinearGradient)
782 if (brush->type == BrushTypePathGradient && !(brush->additionalFlags & 0x1))
784 SAL_WARN("drawinglayer.emf", "EMF+\t TODO Implement displaying BrushTypePathGradient with Boundary: ");
786 ::basegfx::B2DHomMatrix aTextureTransformation;
788 if (brush->hasTransformation) {
789 aTextureTransformation = brush->brush_transformation;
791 // adjust aTextureTransformation for our world space:
792 // -> revert the mapping -> apply the transformation -> map back
793 basegfx::B2DHomMatrix aInvertedMapTrasform(maMapTransform);
794 aInvertedMapTrasform.invert();
795 aTextureTransformation = maMapTransform * aTextureTransformation * aInvertedMapTrasform;
798 // select the stored colors
799 const basegfx::BColor aStartColor = brush->solidColor.getBColor();
800 const basegfx::BColor aEndColor = brush->secondColor.getBColor();
801 drawinglayer::primitive2d::SvgGradientEntryVector aVector;
803 if (brush->blendPositions)
805 SAL_INFO("drawinglayer.emf", "EMF+\t\tUse blend");
807 // store the blendpoints in the vector
808 for (sal_uInt32 i = 0; i < brush->blendPoints; i++)
810 const double aBlendPoint = brush->blendPositions[i];
811 basegfx::BColor aColor;
812 aColor.setGreen(aStartColor.getGreen() + brush->blendFactors[i] * (aEndColor.getGreen() - aStartColor.getGreen()));
813 aColor.setBlue (aStartColor.getBlue() + brush->blendFactors[i] * (aEndColor.getBlue() - aStartColor.getBlue()));
814 aColor.setRed (aStartColor.getRed() + brush->blendFactors[i] * (aEndColor.getRed() - aStartColor.getRed()));
815 const double aAlpha = brush->solidColor.GetAlpha() + brush->blendFactors[i] * (brush->secondColor.GetAlpha() - brush->solidColor.GetAlpha());
816 aVector.emplace_back(aBlendPoint, aColor, aAlpha / 255.0);
819 else if (brush->colorblendPositions)
821 SAL_INFO("drawinglayer.emf", "EMF+\t\tUse color blend");
823 // store the colorBlends in the vector
824 for (sal_uInt32 i = 0; i < brush->colorblendPoints; i++)
826 const double aBlendPoint = brush->colorblendPositions[i];
827 const basegfx::BColor aColor = brush->colorblendColors[i].getBColor();
828 aVector.emplace_back(aBlendPoint, aColor, brush->colorblendColors[i].GetAlpha() / 255.0);
831 else // ok, no extra points: just start and end
833 aVector.emplace_back(0.0, aStartColor, brush->solidColor.GetAlpha() / 255.0);
834 aVector.emplace_back(1.0, aEndColor, brush->secondColor.GetAlpha() / 255.0);
837 // get the polygon range to be able to map the start/end/center point correctly
838 // therefore, create a mapping and invert it
839 basegfx::B2DRange aPolygonRange= polygon.getB2DRange();
840 basegfx::B2DHomMatrix aPolygonTransformation = basegfx::utils::createScaleTranslateB2DHomMatrix(
841 aPolygonRange.getWidth(),aPolygonRange.getHeight(),
842 aPolygonRange.getMinX(), aPolygonRange.getMinY());
843 aPolygonTransformation.invert();
845 if (brush->type == BrushTypeLinearGradient)
847 // support for public enum EmfPlusWrapMode
848 basegfx::B2DPoint aStartPoint = Map(brush->firstPointX, 0.0);
849 aStartPoint = aPolygonTransformation * aStartPoint;
850 basegfx::B2DPoint aEndPoint = Map(brush->firstPointX + brush->aWidth, 0.0);
851 aEndPoint = aPolygonTransformation * aEndPoint;
853 // support for public enum EmfPlusWrapMode
854 drawinglayer::primitive2d::SpreadMethod aSpreadMethod(drawinglayer::primitive2d::SpreadMethod::Pad);
855 switch(brush->wrapMode)
857 case WrapModeTile:
858 case WrapModeTileFlipY:
860 aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Repeat;
861 break;
863 case WrapModeTileFlipX:
864 case WrapModeTileFlipXY:
866 aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Reflect;
867 break;
869 default:
870 break;
873 // create the same one used for SVG
874 mrTargetHolders.Current().append(
875 new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D(
876 aTextureTransformation,
877 polygon,
878 std::move(aVector),
879 aStartPoint,
880 aEndPoint,
881 false, // do not use UnitCoordinates
882 aSpreadMethod));
884 else // BrushTypePathGradient
885 { // TODO The PathGradient is not implemented, and Radial Gradient is used instead
886 basegfx::B2DPoint aCenterPoint = Map(brush->firstPointX, brush->firstPointY);
887 aCenterPoint = aPolygonTransformation * aCenterPoint;
889 // create the same one used for SVG
890 mrTargetHolders.Current().append(
891 new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D(
892 aTextureTransformation,
893 polygon,
894 std::move(aVector),
895 aCenterPoint,
896 0.7, // relative radius little bigger to cover all elements
897 true, // use UnitCoordinates to stretch the gradient
898 drawinglayer::primitive2d::SpreadMethod::Pad,
899 nullptr));
905 EmfPlusHelperData::EmfPlusHelperData(
906 SvMemoryStream& rMS,
907 wmfemfhelper::TargetHolders& rTargetHolders,
908 wmfemfhelper::PropertyHolders& rPropertyHolders)
909 : mfPageScale(0.0),
910 mnOriginX(0),
911 mnOriginY(0),
912 mnHDPI(0),
913 mnVDPI(0),
914 mbSetTextContrast(false),
915 mnTextContrast(0),
916 mnFrameLeft(0),
917 mnFrameTop(0),
918 mnFrameRight(0),
919 mnFrameBottom(0),
920 mnPixX(0),
921 mnPixY(0),
922 mnMmX(0),
923 mnMmY(0),
924 mbMultipart(false),
925 mMFlags(0),
926 mdExtractedXScale(1.0),
927 mdExtractedYScale(1.0),
928 mrTargetHolders(rTargetHolders),
929 mrPropertyHolders(rPropertyHolders),
930 bIsGetDCProcessing(false)
932 rMS.ReadInt32(mnFrameLeft).ReadInt32(mnFrameTop).ReadInt32(mnFrameRight).ReadInt32(mnFrameBottom);
933 SAL_INFO("drawinglayer.emf", "EMF+ picture frame: " << mnFrameLeft << "," << mnFrameTop << " - " << mnFrameRight << "," << mnFrameBottom);
934 rMS.ReadInt32(mnPixX).ReadInt32(mnPixY).ReadInt32(mnMmX).ReadInt32(mnMmY);
935 SAL_INFO("drawinglayer.emf", "EMF+ ref device pixel size: " << mnPixX << "x" << mnPixY << " mm size: " << mnMmX << "x" << mnMmY);
936 readXForm(rMS, maBaseTransform);
937 SAL_INFO("drawinglayer.emf", "EMF+ base transform: " << maBaseTransform);
938 mappingChanged();
941 EmfPlusHelperData::~EmfPlusHelperData()
945 ::basegfx::B2DPolyPolygon EmfPlusHelperData::combineClip(::basegfx::B2DPolyPolygon const & leftPolygon, int combineMode, ::basegfx::B2DPolyPolygon const & rightPolygon)
947 basegfx::B2DPolyPolygon aClippedPolyPolygon;
948 switch (combineMode)
950 case EmfPlusCombineModeReplace:
952 aClippedPolyPolygon = rightPolygon;
953 break;
955 case EmfPlusCombineModeIntersect:
957 aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon(
958 leftPolygon, rightPolygon, true, false);
959 break;
961 case EmfPlusCombineModeUnion:
963 aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationOr(leftPolygon, rightPolygon);
964 break;
966 case EmfPlusCombineModeXOR:
968 aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationXor(leftPolygon, rightPolygon);
969 break;
971 case EmfPlusCombineModeExclude:
973 // Replaces the existing region with the part of itself that is not in the new region.
974 aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(leftPolygon, rightPolygon);
975 break;
977 case EmfPlusCombineModeComplement:
979 // Replaces the existing region with the part of the new region that is not in the existing region.
980 aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(rightPolygon, leftPolygon);
981 break;
984 return aClippedPolyPolygon;
987 void EmfPlusHelperData::processEmfPlusData(
988 SvMemoryStream& rMS,
989 const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/)
991 sal_uInt64 length = rMS.GetSize();
993 if (length < 12)
994 SAL_WARN("drawinglayer.emf", "length is less than required header size");
996 // 12 is minimal valid EMF+ record size; remaining bytes are padding
997 while (length >= 12)
999 sal_uInt16 type, flags;
1000 sal_uInt32 size, dataSize;
1001 sal_uInt64 next;
1003 rMS.ReadUInt16(type).ReadUInt16(flags).ReadUInt32(size).ReadUInt32(dataSize);
1005 next = rMS.Tell() + (size - 12);
1007 if (size < 12)
1009 SAL_WARN("drawinglayer.emf", "Size field is less than 12 bytes");
1010 break;
1012 else if (size > length)
1014 SAL_WARN("drawinglayer.emf", "Size field is greater than bytes left");
1015 break;
1018 if (dataSize > (size - 12))
1020 SAL_WARN("drawinglayer.emf", "DataSize field is greater than Size-12");
1021 break;
1024 SAL_INFO("drawinglayer.emf", "EMF+ " << emfTypeToName(type) << " (0x" << std::hex << type << ")" << std::dec);
1025 SAL_INFO("drawinglayer.emf", "EMF+\t record size: " << size);
1026 SAL_INFO("drawinglayer.emf", "EMF+\t flags: 0x" << std::hex << flags << std::dec);
1027 SAL_INFO("drawinglayer.emf", "EMF+\t data size: " << dataSize);
1029 if (bIsGetDCProcessing)
1031 if (aGetDCState.getClipPolyPolygonActive())
1033 SAL_INFO("drawinglayer.emf", "EMF+\t Restore region to GetDC saved");
1034 wmfemfhelper::HandleNewClipRegion(aGetDCState.getClipPolyPolygon(), mrTargetHolders,
1035 mrPropertyHolders);
1037 else
1039 SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping");
1040 wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders,
1041 mrPropertyHolders);
1043 bIsGetDCProcessing = false;
1045 if (type == EmfPlusRecordTypeObject && ((mbMultipart && (flags & 0x7fff) == (mMFlags & 0x7fff)) || (flags & 0x8000)))
1047 if (!mbMultipart)
1049 mbMultipart = true;
1050 mMFlags = flags;
1051 mMStream.Seek(0);
1054 OSL_ENSURE(dataSize >= 4, "No room for TotalObjectSize in EmfPlusContinuedObjectRecord");
1056 // 1st 4 bytes are TotalObjectSize
1057 mMStream.WriteBytes(static_cast<const char *>(rMS.GetData()) + rMS.Tell() + 4, dataSize - 4);
1058 SAL_INFO("drawinglayer.emf", "EMF+ read next object part size: " << size << " type: " << type << " flags: " << flags << " data size: " << dataSize);
1060 else
1062 if (mbMultipart)
1064 SAL_INFO("drawinglayer.emf", "EMF+ multipart record flags: " << mMFlags);
1065 mMStream.Seek(0);
1066 processObjectRecord(mMStream, mMFlags, 0, true);
1069 mbMultipart = false;
1072 if (type != EmfPlusRecordTypeObject || !(flags & 0x8000))
1074 switch (type)
1076 case EmfPlusRecordTypeHeader:
1078 sal_uInt32 header, version;
1080 rMS.ReadUInt32(header).ReadUInt32(version).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI);
1081 SAL_INFO("drawinglayer.emf", "EMF+\tHeader: 0x" << std::hex << header);
1082 SAL_INFO("drawinglayer.emf", "EMF+\tVersion: " << std::dec << version);
1083 SAL_INFO("drawinglayer.emf", "EMF+\tHorizontal DPI: " << mnHDPI);
1084 SAL_INFO("drawinglayer.emf", "EMF+\tVertical DPI: " << mnVDPI);
1085 SAL_INFO("drawinglayer.emf", "EMF+\tDual: " << ((flags & 1) ? "true" : "false"));
1086 break;
1088 case EmfPlusRecordTypeEndOfFile:
1090 break;
1092 case EmfPlusRecordTypeComment:
1094 #if OSL_DEBUG_LEVEL > 1
1095 unsigned char data;
1096 OUString hexdata;
1098 SAL_INFO("drawinglayer.emf", "EMF+\tDatasize: 0x" << std::hex << dataSize << std::dec);
1100 for (sal_uInt32 i=0; i<dataSize; i++)
1102 rMS.ReadUChar(data);
1104 if (i % 16 == 0)
1105 hexdata += "\n";
1107 OUString padding;
1108 if ((data & 0xF0) == 0)
1109 padding = "0";
1111 hexdata += "0x" + padding + OUString::number(data, 16) + " ";
1114 SAL_INFO("drawinglayer.emf", "EMF+\t" << hexdata);
1115 #endif
1116 break;
1118 case EmfPlusRecordTypeGetDC:
1120 bIsGetDCProcessing = true;
1121 aGetDCState = mrPropertyHolders.Current();
1122 SAL_INFO("drawinglayer.emf", "EMF+\tAlready used in svtools wmf/emf filter parser");
1123 break;
1125 case EmfPlusRecordTypeObject:
1127 processObjectRecord(rMS, flags, dataSize);
1128 break;
1130 case EmfPlusRecordTypeFillPie:
1131 case EmfPlusRecordTypeDrawPie:
1132 case EmfPlusRecordTypeDrawArc:
1134 float startAngle, sweepAngle;
1136 // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
1137 sal_uInt32 brushIndexOrColor = 999;
1139 if (type == EmfPlusRecordTypeFillPie)
1141 rMS.ReadUInt32(brushIndexOrColor);
1142 SAL_INFO("drawinglayer.emf", "EMF+\t FillPie colorOrIndex: " << brushIndexOrColor);
1144 else if (type == EmfPlusRecordTypeDrawPie)
1146 SAL_INFO("drawinglayer.emf", "EMF+\t DrawPie");
1148 else
1150 SAL_INFO("drawinglayer.emf", "EMF+\t DrawArc");
1153 rMS.ReadFloat(startAngle).ReadFloat(sweepAngle);
1154 float dx, dy, dw, dh;
1155 ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
1156 SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
1157 startAngle = basegfx::deg2rad(startAngle);
1158 sweepAngle = basegfx::deg2rad(sweepAngle);
1159 float endAngle = startAngle + sweepAngle;
1160 startAngle = fmodf(startAngle, static_cast<float>(M_PI * 2));
1162 if (startAngle < 0.0)
1164 startAngle += static_cast<float>(M_PI * 2.0);
1166 endAngle = fmodf(endAngle, static_cast<float>(M_PI * 2.0));
1168 if (endAngle < 0.0)
1170 endAngle += static_cast<float>(M_PI * 2.0);
1172 if (sweepAngle < 0)
1174 std::swap(endAngle, startAngle);
1177 SAL_INFO("drawinglayer.emf", "EMF+\t Adjusted angles: start " <<
1178 basegfx::rad2deg(startAngle) << ", end: " << basegfx::rad2deg(endAngle) <<
1179 " startAngle: " << startAngle << " sweepAngle: " << sweepAngle);
1180 const ::basegfx::B2DPoint centerPoint(dx + 0.5 * dw, dy + 0.5 * dh);
1181 ::basegfx::B2DPolygon polygon(
1182 ::basegfx::utils::createPolygonFromEllipseSegment(centerPoint,
1183 0.5 * dw, 0.5 * dh,
1184 startAngle, endAngle));
1185 if (type != EmfPlusRecordTypeDrawArc)
1187 polygon.append(centerPoint);
1188 polygon.setClosed(true);
1190 ::basegfx::B2DPolyPolygon polyPolygon(polygon);
1191 polyPolygon.transform(maMapTransform);
1192 if (type == EmfPlusRecordTypeFillPie)
1193 EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor);
1194 else
1195 EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
1197 break;
1198 case EmfPlusRecordTypeFillPath:
1200 sal_uInt32 index = flags & 0xff;
1201 sal_uInt32 brushIndexOrColor;
1202 rMS.ReadUInt32(brushIndexOrColor);
1203 SAL_INFO("drawinglayer.emf", "EMF+ FillPath slot: " << index);
1205 EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[index].get());
1206 if (path)
1207 EMFPPlusFillPolygon(path->GetPolygon(*this), flags & 0x8000, brushIndexOrColor);
1208 else
1209 SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillPath missing path");
1211 break;
1212 case EmfPlusRecordTypeFillRegion:
1214 sal_uInt32 index = flags & 0xff;
1215 sal_uInt32 brushIndexOrColor;
1216 rMS.ReadUInt32(brushIndexOrColor);
1217 SAL_INFO("drawinglayer.emf", "EMF+\t FillRegion slot: " << index);
1219 EMFPRegion* region = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
1220 if (region)
1221 EMFPPlusFillPolygon(region->regionPolyPolygon, flags & 0x8000, brushIndexOrColor);
1222 else
1223 SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillRegion missing region");
1225 break;
1226 case EmfPlusRecordTypeDrawEllipse:
1227 case EmfPlusRecordTypeFillEllipse:
1229 // Intentionally very bogus initial value to avoid MSVC complaining about potentially uninitialized local
1230 // variable. As long as the code stays as intended, this variable will be assigned a (real) value in the case
1231 // when it is later used.
1232 sal_uInt32 brushIndexOrColor = 1234567;
1234 if (type == EmfPlusRecordTypeFillEllipse)
1236 rMS.ReadUInt32(brushIndexOrColor);
1239 SAL_INFO("drawinglayer.emf", "EMF+\t " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff));
1240 float dx, dy, dw, dh;
1241 ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
1242 SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
1243 ::basegfx::B2DPolyPolygon polyPolygon(
1244 ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(dx + 0.5 * dw, dy + 0.5 * dh),
1245 0.5 * dw, 0.5 * dh));
1246 polyPolygon.transform(maMapTransform);
1247 if (type == EmfPlusRecordTypeFillEllipse)
1248 EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor);
1249 else
1250 EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
1252 break;
1253 case EmfPlusRecordTypeFillRects:
1254 case EmfPlusRecordTypeDrawRects:
1256 // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
1257 sal_uInt32 brushIndexOrColor = 999;
1258 sal_Int32 rectangles;
1259 const bool isColor = (flags & 0x8000);
1260 ::basegfx::B2DPolygon polygon;
1262 if (EmfPlusRecordTypeFillRects == type)
1264 SAL_INFO("drawinglayer.emf", "EMF+\t FillRects");
1265 rMS.ReadUInt32(brushIndexOrColor);
1266 SAL_INFO("drawinglayer.emf", "EMF+\t" << (isColor ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
1268 else
1270 SAL_INFO("drawinglayer.emf", "EMF+\t DrawRects");
1273 rMS.ReadInt32(rectangles);
1275 for (int i = 0; i < rectangles; i++)
1277 float x, y, width, height;
1278 ReadRectangle(rMS, x, y, width, height, bool(flags & 0x4000));
1279 polygon.clear();
1280 polygon.append(Map(x, y));
1281 polygon.append(Map(x + width, y));
1282 polygon.append(Map(x + width, y + height));
1283 polygon.append(Map(x, y + height));
1284 polygon.setClosed(true);
1286 SAL_INFO("drawinglayer.emf", "EMF+\t\t rectangle: " << x << ", "<< y << " " << width << "x" << height);
1288 ::basegfx::B2DPolyPolygon polyPolygon(polygon);
1289 if (type == EmfPlusRecordTypeFillRects)
1290 EMFPPlusFillPolygon(polyPolygon, isColor, brushIndexOrColor);
1291 else
1292 EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
1294 break;
1296 case EmfPlusRecordTypeFillPolygon:
1298 sal_uInt32 brushIndexOrColor, points;
1300 rMS.ReadUInt32(brushIndexOrColor);
1301 rMS.ReadUInt32(points);
1302 SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points);
1303 SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << " : 0x" << std::hex << brushIndexOrColor << std::dec);
1305 EMFPPath path(points, true);
1306 path.Read(rMS, flags);
1308 EMFPPlusFillPolygon(path.GetPolygon(*this), flags & 0x8000, brushIndexOrColor);
1309 break;
1311 case EmfPlusRecordTypeDrawLines:
1313 sal_uInt32 points;
1314 rMS.ReadUInt32(points);
1315 SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points);
1316 EMFPPath path(points, true);
1317 path.Read(rMS, flags);
1319 // 0x2000 bit indicates whether to draw an extra line between the last point
1320 // and the first point, to close the shape.
1321 EMFPPlusDrawPolygon(path.GetPolygon(*this, true, (flags & 0x2000)), flags);
1323 break;
1325 case EmfPlusRecordTypeDrawPath:
1327 sal_uInt32 penIndex;
1328 rMS.ReadUInt32(penIndex);
1329 SAL_INFO("drawinglayer.emf", "EMF+\t Pen: " << penIndex);
1331 EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get());
1332 if (path)
1333 EMFPPlusDrawPolygon(path->GetPolygon(*this), penIndex);
1334 else
1335 SAL_WARN("drawinglayer.emf", "\t\tEmfPlusRecordTypeDrawPath missing path");
1337 break;
1339 case EmfPlusRecordTypeDrawBeziers:
1341 sal_uInt32 aCount;
1342 float x1, y1, x2, y2, x3, y3, x4, y4;
1343 ::basegfx::B2DPoint aStartPoint, aControlPointA, aControlPointB, aEndPoint;
1344 ::basegfx::B2DPolygon aPolygon;
1345 rMS.ReadUInt32(aCount);
1346 SAL_INFO("drawinglayer.emf", "EMF+\t DrawBeziers slot: " << (flags & 0xff));
1347 SAL_INFO("drawinglayer.emf", "EMF+\t Number of points: " << aCount);
1348 SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer.emf",
1349 "EMF+\t Bezier Draw not support number of points other than 4, 7, "
1350 "10, 13, 16...");
1352 if (aCount < 4)
1354 SAL_WARN("drawinglayer.emf", "EMF+\t Bezier Draw does not support less "
1355 "than 4 points. Number of points: "
1356 << aCount);
1357 break;
1360 ReadPoint(rMS, x1, y1, flags);
1361 // We need to add first starting point
1362 aStartPoint = Map(x1, y1);
1363 aPolygon.append(aStartPoint);
1364 SAL_INFO("drawinglayer.emf",
1365 "EMF+\t Bezier starting point: " << x1 << "," << y1);
1366 for (sal_uInt32 i = 4; i <= aCount; i += 3)
1368 ReadPoint(rMS, x2, y2, flags);
1369 ReadPoint(rMS, x3, y3, flags);
1370 ReadPoint(rMS, x4, y4, flags);
1372 SAL_INFO("drawinglayer.emf",
1373 "EMF+\t Bezier points: " << x2 << "," << y2 << " " << x3 << ","
1374 << y3 << " " << x4 << "," << y4);
1376 aControlPointA = Map(x2, y2);
1377 aControlPointB = Map(x3, y3);
1378 aEndPoint = Map(x4, y4);
1379 aPolygon.appendBezierSegment(aControlPointA, aControlPointB, aEndPoint);
1380 // The ending coordinate of one Bezier curve is the starting coordinate of the next.
1381 aStartPoint = aEndPoint;
1383 EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff);
1384 break;
1386 case EmfPlusRecordTypeDrawClosedCurve:
1387 case EmfPlusRecordTypeFillClosedCurve:
1389 // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
1390 sal_uInt32 brushIndexOrColor = 999, points;
1391 float aTension;
1392 if (type == EmfPlusRecordTypeFillClosedCurve)
1394 rMS.ReadUInt32(brushIndexOrColor);
1395 SAL_INFO("drawinglayer.emf",
1396 "EMF+\t Fill Mode: " << (flags & 0x2000 ? "Winding" : "Alternate"));
1398 rMS.ReadFloat(aTension);
1399 rMS.ReadUInt32(points);
1400 SAL_WARN("drawinglayer.emf",
1401 "EMF+\t Tension: " << aTension << " Points: " << points);
1402 SAL_WARN_IF(aTension != 0, "drawinglayer.emf",
1403 "EMF+\t TODO Add support for tension different than 0");
1404 SAL_INFO("drawinglayer.emf",
1405 "EMF+\t " << (flags & 0x8000 ? "Color" : "Brush index") << " : 0x"
1406 << std::hex << brushIndexOrColor << std::dec);
1408 EMFPPath path(points, true);
1409 path.Read(rMS, flags);
1410 if (type == EmfPlusRecordTypeFillClosedCurve)
1411 EMFPPlusFillPolygon(path.GetPolygon(*this, /* bMapIt */ true,
1412 /*bAddLineToCloseShape */ true),
1413 flags & 0x8000, brushIndexOrColor);
1414 else
1415 EMFPPlusDrawPolygon(path.GetPolygon(*this, /* bMapIt */ true,
1416 /*bAddLineToCloseShape */ true),
1417 flags & 0xff);
1418 break;
1420 case EmfPlusRecordTypeDrawImage:
1421 case EmfPlusRecordTypeDrawImagePoints:
1423 sal_uInt32 imageAttributesId;
1424 sal_Int32 sourceUnit;
1425 rMS.ReadUInt32(imageAttributesId).ReadInt32(sourceUnit);
1426 SAL_INFO("drawinglayer.emf",
1427 "EMF+\t " << (type == EmfPlusRecordTypeDrawImage ? "DrawImage"
1428 : "DrawImagePoints")
1429 << " image attributes Id: " << imageAttributesId
1430 << " source unit: " << sourceUnit);
1431 SAL_INFO("drawinglayer.emf", "EMF+\t TODO: use image attributes");
1433 // Source unit of measurement type must be 1 pixel
1434 if (EMFPImage* image = sourceUnit == UnitTypePixel ?
1435 dynamic_cast<EMFPImage*>(maEMFPObjects[flags & 0xff].get()) :
1436 nullptr)
1438 float sx, sy, sw, sh;
1439 ReadRectangle(rMS, sx, sy, sw, sh);
1441 ::tools::Rectangle aSource(Point(sx, sy), Size(sw + 1, sh + 1));
1442 SAL_INFO("drawinglayer.emf",
1443 "EMF+\t "
1444 << (type == EmfPlusRecordTypeDrawImage ? "DrawImage"
1445 : "DrawImagePoints")
1446 << " source rectangle: " << sx << "," << sy << " " << sw << "x"
1447 << sh);
1449 float dx(0.), dy(0.), dw(0.), dh(0.);
1450 double fShearX = 0.0;
1451 double fShearY = 0.0;
1452 if (type == EmfPlusRecordTypeDrawImagePoints)
1454 sal_uInt32 aCount;
1455 rMS.ReadUInt32(aCount);
1457 // Number of points used by DrawImagePoints. Exactly 3 points must be specified.
1458 if (aCount != 3)
1460 SAL_WARN("drawinglayer.emf", "EMF+\t Wrong EMF+ file. Expected "
1461 "3 points, received: "
1462 << aCount);
1463 break;
1465 float x1, y1, x2, y2, x3, y3;
1467 ReadPoint(rMS, x1, y1, flags); // upper-left point
1468 ReadPoint(rMS, x2, y2, flags); // upper-right
1469 ReadPoint(rMS, x3, y3, flags); // lower-left
1471 SAL_INFO("drawinglayer.emf", "EMF+\t destination points: "
1472 << x1 << "," << y1 << " " << x2 << ","
1473 << y2 << " " << x3 << "," << y3);
1474 dx = x1;
1475 dy = y2;
1476 dw = x2 - x1;
1477 dh = y3 - y1;
1478 fShearX = x3 - x1;
1479 fShearY = y2 - y1;
1481 else if (type == EmfPlusRecordTypeDrawImage)
1482 ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
1484 SAL_INFO("drawinglayer.emf",
1485 "EMF+\t Rectangle: " << dx << "," << dy << " " << dw << "x" << dh);
1486 Size aSize;
1487 if (image->type == ImageDataTypeBitmap)
1489 aSize = image->graphic.GetBitmapEx().GetSizePixel();
1490 SAL_INFO("drawinglayer.emf", "EMF+\t Bitmap size: " << aSize.Width()
1491 << "x"
1492 << aSize.Height());
1493 if (sx < 0)
1495 // If src position is negative then we need shift image to right
1496 dx = dx + ((-sx) / sw) * dw;
1497 if (sx + sw <= aSize.Width())
1498 dw = ((sw + sx) / sw) * dw;
1499 else
1500 dw = (aSize.Width() / sw) * dw;
1502 else if (sx + sw > aSize.Width())
1503 // If the src image is smaller that what we want to cut, then we need to scale down
1504 dw = ((aSize.Width() - sx) / sw) * dw;
1506 if (sy < 0)
1508 dy = dy + ((-sy) / sh) * dh;
1509 if (sy + sh <= aSize.Height())
1510 dh = ((sh + sy) / sh) * dh;
1511 else
1512 dh = (aSize.Height() / sh) * dh;
1514 else if (sy + sh > aSize.Height())
1515 dh = ((aSize.Height() - sy) / sh) * dh;
1517 else
1518 SAL_INFO(
1519 "drawinglayer.emf",
1520 "EMF+\t TODO: Add support for SrcRect to ImageDataTypeMetafile");
1521 ::basegfx::B2DPoint aDstPoint(dx, dy);
1522 ::basegfx::B2DSize aDstSize(dw, dh);
1524 const basegfx::B2DHomMatrix aTransformMatrix
1525 = maMapTransform
1526 * basegfx::B2DHomMatrix(
1527 /* Row 0, Column 0 */ aDstSize.getWidth(),
1528 /* Row 0, Column 1 */ fShearX,
1529 /* Row 0, Column 2 */ aDstPoint.getX(),
1530 /* Row 1, Column 0 */ fShearY,
1531 /* Row 1, Column 1 */ aDstSize.getHeight(),
1532 /* Row 1, Column 2 */ aDstPoint.getY());
1534 if (image->type == ImageDataTypeBitmap)
1536 BitmapEx aBmp(image->graphic.GetBitmapEx());
1537 aBmp.Crop(aSource);
1538 aSize = aBmp.GetSizePixel();
1539 if (aSize.Width() > 0 && aSize.Height() > 0)
1541 mrTargetHolders.Current().append(
1542 new drawinglayer::primitive2d::BitmapPrimitive2D(
1543 aBmp, aTransformMatrix));
1545 else
1546 SAL_WARN("drawinglayer.emf", "EMF+\t warning: empty bitmap");
1548 else if (image->type == ImageDataTypeMetafile)
1550 GDIMetaFile aGDI(image->graphic.GetGDIMetaFile());
1551 aGDI.Clip(aSource);
1552 mrTargetHolders.Current().append(
1553 new drawinglayer::primitive2d::MetafilePrimitive2D(aTransformMatrix,
1554 aGDI));
1557 else
1559 SAL_WARN("drawinglayer.emf",
1560 "EMF+\tDrawImage(Points) Wrong EMF+ file. Only Unit Type Pixel is "
1561 "support by EMF+ specification for DrawImage(Points)");
1563 break;
1565 case EmfPlusRecordTypeDrawString:
1567 sal_uInt32 brushId, formatId, stringLength;
1568 rMS.ReadUInt32(brushId).ReadUInt32(formatId).ReadUInt32(stringLength);
1569 SAL_INFO("drawinglayer.emf", "EMF+\t FontId: " << OUString::number(flags & 0xFF));
1570 SAL_INFO("drawinglayer.emf", "EMF+\t BrushId: " << BrushIDToString(flags, brushId));
1571 SAL_INFO("drawinglayer.emf", "EMF+\t FormatId: " << formatId);
1572 SAL_INFO("drawinglayer.emf", "EMF+\t Length: " << stringLength);
1574 // read the layout rectangle
1575 float lx, ly, lw, lh;
1576 rMS.ReadFloat(lx).ReadFloat(ly).ReadFloat(lw).ReadFloat(lh);
1578 SAL_INFO("drawinglayer.emf", "EMF+\t DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh);
1579 // parse the string
1580 const OUString text = read_uInt16s_ToOUString(rMS, stringLength);
1581 SAL_INFO("drawinglayer.emf", "EMF+\t DrawString string: " << text);
1582 // get the stringFormat from the Object table ( this is OPTIONAL and may be nullptr )
1583 const EMFPStringFormat *stringFormat = dynamic_cast<EMFPStringFormat*>(maEMFPObjects[formatId & 0xff].get());
1584 // get the font from the flags
1585 const EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get());
1586 if (!font)
1588 break;
1590 mrPropertyHolders.Current().setFont(vcl::Font(font->family, Size(font->emSize, font->emSize)));
1592 drawinglayer::attribute::FontAttribute fontAttribute(
1593 font->family, // font family
1594 "", // (no) font style
1595 font->Bold() ? 8u : 1u, // weight: 8 = bold
1596 font->family == "SYMBOL", // symbol
1597 stringFormat && stringFormat->DirectionVertical(), // vertical
1598 font->Italic(), // italic
1599 false, // monospaced
1600 false, // outline = false, no such thing in MS-EMFPLUS
1601 stringFormat && stringFormat->DirectionRightToLeft(), // right-to-left
1602 false); // BiDiStrong
1604 css::lang::Locale locale;
1605 double stringAlignmentHorizontalOffset = 0.0;
1606 double stringAlignmentVerticalOffset = font->emSize;
1607 if (stringFormat)
1609 LanguageTag aLanguageTag(static_cast<LanguageType>(stringFormat->language));
1610 locale = aLanguageTag.getLocale();
1611 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
1613 aTextLayouter.setFontAttribute(fontAttribute, font->emSize,
1614 font->emSize, locale);
1616 double fTextWidth = aTextLayouter.getTextWidth(text, 0, stringLength);
1617 SAL_WARN_IF(stringFormat->DirectionRightToLeft(), "drawinglayer.emf",
1618 "EMF+\t DrawString Alignment TODO For a right-to-left layout rectangle, the origin should be at the upper right.");
1619 if (stringFormat->stringAlignment == StringAlignmentNear)
1620 // Alignment is to the left side of the layout rectangle (lx, ly, lw, lh)
1621 stringAlignmentHorizontalOffset = stringFormat->leadingMargin * font->emSize;
1622 else if (stringFormat->stringAlignment == StringAlignmentCenter)
1623 // Alignment is centered between the origin and extent of the layout rectangle
1624 stringAlignmentHorizontalOffset = 0.5 * lw + (stringFormat->leadingMargin - stringFormat->trailingMargin) * font->emSize - 0.5 * fTextWidth;
1625 else if (stringFormat->stringAlignment == StringAlignmentFar)
1626 // Alignment is to the right side of the layout rectangle
1627 stringAlignmentHorizontalOffset = lw - stringFormat->trailingMargin * font->emSize - fTextWidth;
1629 if (stringFormat->lineAlign == StringAlignmentNear)
1630 stringAlignmentVerticalOffset = font->emSize;
1631 else if (stringFormat->lineAlign == StringAlignmentCenter)
1632 stringAlignmentVerticalOffset = 0.5 * lh + 0.5 * font->emSize;
1633 else if (stringFormat->lineAlign == StringAlignmentFar)
1634 stringAlignmentVerticalOffset = lh;
1636 else
1638 // By default LeadingMargin is 1/6 inch
1639 // TODO for typographic fonts set value to 0.
1640 stringAlignmentHorizontalOffset = 16.0;
1642 // use system default
1643 locale = Application::GetSettings().GetLanguageTag().getLocale();
1646 const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
1647 ::basegfx::B2DVector(font->emSize, font->emSize),
1648 ::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset,
1649 ly + stringAlignmentVerticalOffset));
1651 Color uncorrectedColor = EMFPGetBrushColorOrARGBColor(flags, brushId);
1652 Color color;
1654 if (mbSetTextContrast)
1656 const auto gammaVal = mnTextContrast / 1000;
1657 const basegfx::BColorModifier_gamma gamma(gammaVal);
1659 // gamma correct transparency color
1660 sal_uInt16 alpha = uncorrectedColor.GetAlpha();
1661 alpha = std::clamp(std::pow(alpha, 1.0 / gammaVal), 0.0, 1.0) * 255;
1663 basegfx::BColor modifiedColor(gamma.getModifiedColor(uncorrectedColor.getBColor()));
1664 color.SetRed(modifiedColor.getRed() * 255);
1665 color.SetGreen(modifiedColor.getGreen() * 255);
1666 color.SetBlue(modifiedColor.getBlue() * 255);
1667 color.SetAlpha(alpha);
1669 else
1671 color = uncorrectedColor;
1674 mrPropertyHolders.Current().setTextColor(color.getBColor());
1675 mrPropertyHolders.Current().setTextColorActive(true);
1677 if (color.GetAlpha() > 0)
1679 std::vector<double> emptyVector;
1680 rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText;
1681 if (font->Underline() || font->Strikeout())
1683 pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
1684 transformMatrix,
1685 text,
1686 0, // text always starts at 0
1687 stringLength,
1688 std::move(emptyVector), // EMF-PLUS has no DX-array
1690 fontAttribute,
1691 locale,
1692 color.getBColor(), // Font Color
1693 COL_TRANSPARENT, // Fill Color
1694 color.getBColor(), // OverlineColor
1695 color.getBColor(), // TextlineColor
1696 drawinglayer::primitive2d::TEXT_LINE_NONE,
1697 font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
1698 false,
1699 font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE);
1701 else
1703 pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
1704 transformMatrix,
1705 text,
1706 0, // text always starts at 0
1707 stringLength,
1708 std::move(emptyVector), // EMF-PLUS has no DX-array
1710 fontAttribute,
1711 locale,
1712 color.getBColor());
1714 drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText);
1715 if (color.IsTransparent())
1717 aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
1718 drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText },
1719 (255 - color.GetAlpha()) / 255.0);
1722 mrTargetHolders.Current().append(
1723 new drawinglayer::primitive2d::TransformPrimitive2D(
1724 maMapTransform,
1725 drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } ));
1727 break;
1729 case EmfPlusRecordTypeSetPageTransform:
1731 rMS.ReadFloat(mfPageScale);
1732 SAL_INFO("drawinglayer.emf", "EMF+\t Scale: " << mfPageScale << " unit: " << UnitTypeToString(flags));
1734 if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld))
1736 SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by SetPageTransform in EMF+ specification.");
1738 else
1740 mnMmX *= mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnHDPI);
1741 mnMmY *= mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnVDPI);
1742 mappingChanged();
1744 break;
1746 case EmfPlusRecordTypeSetRenderingOrigin:
1748 rMS.ReadInt32(mnOriginX).ReadInt32(mnOriginY);
1749 SAL_INFO("drawinglayer.emf", "EMF+\t SetRenderingOrigin, [x,y]: " << mnOriginX << "," << mnOriginY);
1750 break;
1752 case EmfPlusRecordTypeSetTextContrast:
1754 const sal_uInt16 LOWERGAMMA = 1000;
1755 const sal_uInt16 UPPERGAMMA = 2200;
1757 mbSetTextContrast = true;
1758 mnTextContrast = flags & 0xFFF;
1759 SAL_WARN_IF(mnTextContrast > UPPERGAMMA || mnTextContrast < LOWERGAMMA,
1760 "drawinglayer.emf", "EMF+\t Gamma value is not with bounds 1000 to 2200, value is " << mnTextContrast);
1761 mnTextContrast = std::min(mnTextContrast, UPPERGAMMA);
1762 mnTextContrast = std::max(mnTextContrast, LOWERGAMMA);
1763 SAL_INFO("drawinglayer.emf", "EMF+\t Text contrast: " << (mnTextContrast / 1000) << " gamma");
1764 break;
1766 case EmfPlusRecordTypeSetTextRenderingHint:
1768 sal_uInt8 nTextRenderingHint = (flags & 0xFF) >> 1;
1769 SAL_INFO("drawinglayer.emf", "EMF+\t Text rendering hint: " << TextRenderingHintToString(nTextRenderingHint));
1770 SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetTextRenderingHint");
1771 break;
1773 case EmfPlusRecordTypeSetAntiAliasMode:
1775 bool bUseAntiAlias = (flags & 0x0001);
1776 sal_uInt8 nSmoothingMode = (flags & 0xFE00) >> 1;
1777 SAL_INFO("drawinglayer.emf", "EMF+\t Antialiasing: " << (bUseAntiAlias ? "enabled" : "disabled"));
1778 SAL_INFO("drawinglayer.emf", "EMF+\t Smoothing mode: " << SmoothingModeToString(nSmoothingMode));
1779 SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetAntiAliasMode");
1780 break;
1782 case EmfPlusRecordTypeSetInterpolationMode:
1784 sal_uInt16 nInterpolationMode = flags & 0xFF;
1785 SAL_INFO("drawinglayer.emf", "EMF+\t Interpolation mode: " << InterpolationModeToString(nInterpolationMode));
1786 SAL_WARN("drawinglayer.emf", "EMF+\t TODO InterpolationMode");
1787 break;
1789 case EmfPlusRecordTypeSetPixelOffsetMode:
1791 SAL_INFO("drawinglayer.emf", "EMF+\t Pixel offset mode: " << PixelOffsetModeToString(flags));
1792 SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetPixelOffsetMode");
1793 break;
1795 case EmfPlusRecordTypeSetCompositingQuality:
1797 SAL_INFO("drawinglayer.emf", "EMF+\t TODO SetCompositingQuality");
1798 break;
1800 case EmfPlusRecordTypeSave:
1802 sal_uInt32 stackIndex;
1803 rMS.ReadUInt32(stackIndex);
1804 SAL_INFO("drawinglayer.emf", "EMF+\t Save stack index: " << stackIndex);
1806 GraphicStatePush(mGSStack, stackIndex);
1808 break;
1810 case EmfPlusRecordTypeRestore:
1812 sal_uInt32 stackIndex;
1813 rMS.ReadUInt32(stackIndex);
1814 SAL_INFO("drawinglayer.emf", "EMF+\t Restore stack index: " << stackIndex);
1816 GraphicStatePop(mGSStack, stackIndex);
1817 break;
1819 case EmfPlusRecordTypeBeginContainer:
1821 float dx, dy, dw, dh;
1822 ReadRectangle(rMS, dx, dy, dw, dh);
1823 SAL_INFO("drawinglayer.emf", "EMF+\t Dest RectData: " << dx << "," << dy << " " << dw << "x" << dh);
1825 float sx, sy, sw, sh;
1826 ReadRectangle(rMS, sx, sy, sw, sh);
1827 SAL_INFO("drawinglayer.emf", "EMF+\t Source RectData: " << sx << "," << sy << " " << sw << "x" << sh);
1829 sal_uInt32 stackIndex;
1830 rMS.ReadUInt32(stackIndex);
1831 SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container stack index: " << stackIndex << ", PageUnit: " << flags);
1833 if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld))
1835 SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by BeginContainer in EMF+ specification.");
1836 break;
1838 const float aPageScaleX = getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnHDPI);
1839 const float aPageScaleY = getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnVDPI);
1840 GraphicStatePush(mGSContainerStack, stackIndex);
1841 const basegfx::B2DHomMatrix transform = basegfx::utils::createScaleTranslateB2DHomMatrix(
1842 aPageScaleX * ( dw / sw ), aPageScaleY * ( dh / sh ),
1843 aPageScaleX * ( dx - sx ), aPageScaleY * ( dy - sy) );
1844 maWorldTransform *= transform;
1845 mappingChanged();
1846 break;
1848 case EmfPlusRecordTypeBeginContainerNoParams:
1850 sal_uInt32 stackIndex;
1851 rMS.ReadUInt32(stackIndex);
1852 SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container No Params stack index: " << stackIndex);
1854 GraphicStatePush(mGSContainerStack, stackIndex);
1855 break;
1857 case EmfPlusRecordTypeEndContainer:
1859 sal_uInt32 stackIndex;
1860 rMS.ReadUInt32(stackIndex);
1861 SAL_INFO("drawinglayer.emf", "EMF+\t End Container stack index: " << stackIndex);
1863 GraphicStatePop(mGSContainerStack, stackIndex);
1864 break;
1866 case EmfPlusRecordTypeSetWorldTransform:
1868 SAL_INFO("drawinglayer.emf", "EMF+\t SetWorldTransform, Post multiply: " << bool(flags & 0x2000));
1869 readXForm(rMS, maWorldTransform);
1870 mappingChanged();
1871 SAL_INFO("drawinglayer.emf", "EMF+\t\t: " << maWorldTransform);
1872 break;
1874 case EmfPlusRecordTypeResetWorldTransform:
1876 maWorldTransform.identity();
1877 SAL_INFO("drawinglayer.emf", "EMF+\t World transform: " << maWorldTransform);
1878 mappingChanged();
1879 break;
1881 case EmfPlusRecordTypeMultiplyWorldTransform:
1883 SAL_INFO("drawinglayer.emf", "EMF+\t MultiplyWorldTransform, post multiply: " << bool(flags & 0x2000));
1884 basegfx::B2DHomMatrix transform;
1885 readXForm(rMS, transform);
1887 SAL_INFO("drawinglayer.emf",
1888 "EMF+\t Transform matrix: " << transform);
1890 if (flags & 0x2000)
1892 // post multiply
1893 maWorldTransform *= transform;
1895 else
1897 // pre multiply
1898 transform *= maWorldTransform;
1899 maWorldTransform = transform;
1902 mappingChanged();
1904 SAL_INFO("drawinglayer.emf",
1905 "EMF+\t World transform matrix: " << maWorldTransform);
1906 break;
1908 case EmfPlusRecordTypeTranslateWorldTransform:
1910 SAL_INFO("drawinglayer.emf", "EMF+\t TranslateWorldTransform, Post multiply: " << bool(flags & 0x2000));
1912 basegfx::B2DHomMatrix transform;
1913 float eDx, eDy;
1914 rMS.ReadFloat(eDx).ReadFloat(eDy);
1915 transform.set(0, 2, eDx);
1916 transform.set(1, 2, eDy);
1918 SAL_INFO("drawinglayer.emf",
1919 "EMF+\t Translate matrix: " << transform);
1921 if (flags & 0x2000)
1923 // post multiply
1924 maWorldTransform *= transform;
1926 else
1928 // pre multiply
1929 transform *= maWorldTransform;
1930 maWorldTransform = transform;
1933 mappingChanged();
1935 SAL_INFO("drawinglayer.emf",
1936 "EMF+\t World transform matrix: " << maWorldTransform);
1937 break;
1939 case EmfPlusRecordTypeScaleWorldTransform:
1941 basegfx::B2DHomMatrix transform;
1942 float eSx, eSy;
1943 rMS.ReadFloat(eSx).ReadFloat(eSy);
1944 transform.set(0, 0, eSx);
1945 transform.set(1, 1, eSy);
1947 SAL_INFO("drawinglayer.emf", "EMF+\t ScaleWorldTransform Sx: " << eSx <<
1948 " Sy: " << eSy << ", Post multiply:" << bool(flags & 0x2000));
1949 SAL_INFO("drawinglayer.emf",
1950 "EMF+\t World transform matrix: " << maWorldTransform);
1952 if (flags & 0x2000)
1954 // post multiply
1955 maWorldTransform *= transform;
1957 else
1959 // pre multiply
1960 transform *= maWorldTransform;
1961 maWorldTransform = transform;
1964 mappingChanged();
1966 SAL_INFO("drawinglayer.emf",
1967 "EMF+\t World transform matrix: " << maWorldTransform);
1968 break;
1970 case EmfPlusRecordTypeRotateWorldTransform:
1972 // Angle of rotation in degrees
1973 float eAngle;
1974 rMS.ReadFloat(eAngle);
1976 SAL_INFO("drawinglayer.emf", "EMF+\t RotateWorldTransform Angle: " << eAngle <<
1977 ", post multiply: " << bool(flags & 0x2000));
1978 // Skipping flags & 0x2000
1979 // For rotation transformation there is no difference between post and pre multiply
1980 maWorldTransform.rotate(basegfx::deg2rad(eAngle));
1981 mappingChanged();
1983 SAL_INFO("drawinglayer.emf",
1984 "EMF+\t " << maWorldTransform);
1985 break;
1987 case EmfPlusRecordTypeResetClip:
1989 SAL_INFO("drawinglayer.emf", "EMF+ ResetClip");
1990 // We don't need to read anything more, as Size needs to be set 0x0000000C
1991 // and DataSize must be set to 0.
1993 // Resets the current clipping region for the world space to infinity.
1994 HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, mrPropertyHolders);
1995 break;
1997 case EmfPlusRecordTypeSetClipRect:
1998 case EmfPlusRecordTypeSetClipPath:
1999 case EmfPlusRecordTypeSetClipRegion:
2001 int combineMode = (flags >> 8) & 0xf;
2002 ::basegfx::B2DPolyPolygon polyPolygon;
2003 if (type == EmfPlusRecordTypeSetClipRect)
2005 SAL_INFO("drawinglayer.emf", "EMF+\t SetClipRect");
2007 float dx, dy, dw, dh;
2008 ReadRectangle(rMS, dx, dy, dw, dh);
2009 SAL_INFO("drawinglayer.emf",
2010 "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
2011 ::basegfx::B2DPoint mappedPoint1(Map(dx, dy));
2012 ::basegfx::B2DPoint mappedPoint2(Map(dx + dw, dy + dh));
2014 polyPolygon
2015 = ::basegfx::B2DPolyPolygon(::basegfx::utils::createPolygonFromRect(
2016 ::basegfx::B2DRectangle(mappedPoint1.getX(), mappedPoint1.getY(),
2017 mappedPoint2.getX(), mappedPoint2.getY())));
2019 else if (type == EmfPlusRecordTypeSetClipPath)
2021 SAL_INFO("drawinglayer.emf", "EMF+\tSetClipPath " << (flags & 0xff));
2023 EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get());
2024 if (!path)
2026 SAL_WARN("drawinglayer.emf",
2027 "EMF+\t TODO Unable to find path in slot: " << (flags & 0xff));
2028 break;
2030 polyPolygon = path->GetPolygon(*this);
2032 else if (type == EmfPlusRecordTypeSetClipRegion)
2034 SAL_INFO("drawinglayer.emf", "EMF+\t Region in slot: " << (flags & 0xff));
2035 EMFPRegion* region
2036 = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
2037 if (!region)
2039 SAL_WARN(
2040 "drawinglayer.emf",
2041 "EMF+\t TODO Unable to find region in slot: " << (flags & 0xff));
2042 break;
2044 polyPolygon = region->regionPolyPolygon;
2046 SAL_INFO("drawinglayer.emf", "EMF+\t Combine mode: " << combineMode);
2047 ::basegfx::B2DPolyPolygon aClippedPolyPolygon;
2048 if (mrPropertyHolders.Current().getClipPolyPolygonActive())
2050 aClippedPolyPolygon
2051 = combineClip(mrPropertyHolders.Current().getClipPolyPolygon(),
2052 combineMode, polyPolygon);
2054 else
2056 //Combine with infinity
2057 switch (combineMode)
2059 case EmfPlusCombineModeReplace:
2060 case EmfPlusCombineModeIntersect:
2062 aClippedPolyPolygon = polyPolygon;
2063 break;
2065 case EmfPlusCombineModeUnion:
2067 // Disable clipping as the clipping is infinity
2068 aClippedPolyPolygon = ::basegfx::B2DPolyPolygon();
2069 break;
2071 case EmfPlusCombineModeXOR:
2072 case EmfPlusCombineModeComplement:
2074 //TODO It is not correct and it should be fixed
2075 aClippedPolyPolygon = polyPolygon;
2076 break;
2078 case EmfPlusCombineModeExclude:
2080 //TODO It is not correct and it should be fixed
2081 aClippedPolyPolygon = ::basegfx::B2DPolyPolygon();
2082 break;
2086 HandleNewClipRegion(aClippedPolyPolygon, mrTargetHolders, mrPropertyHolders);
2087 break;
2089 case EmfPlusRecordTypeOffsetClip:
2091 float dx, dy;
2092 rMS.ReadFloat(dx).ReadFloat(dy);
2093 SAL_INFO("drawinglayer.emf", "EMF+\tOffset x:" << dx << ", y:" << dy);
2095 basegfx::B2DPolyPolygon aPolyPolygon(
2096 mrPropertyHolders.Current().getClipPolyPolygon());
2098 SAL_INFO("drawinglayer.emf",
2099 "EMF+\t PolyPolygon before translate: " << aPolyPolygon);
2101 basegfx::B2DPoint aOffset = Map(dx, dy);
2102 basegfx::B2DHomMatrix transformMatrix;
2103 transformMatrix.set(0, 2, aOffset.getX());
2104 transformMatrix.set(1, 2, aOffset.getY());
2105 aPolyPolygon.transform(transformMatrix);
2107 SAL_INFO("drawinglayer.emf",
2108 "EMF+\t PolyPolygon after translate: " << aPolyPolygon <<
2109 ", mapped offset x" << aOffset.getX() << ", mapped offset y" << aOffset.getY());
2110 HandleNewClipRegion(aPolyPolygon, mrTargetHolders, mrPropertyHolders);
2111 break;
2113 case EmfPlusRecordTypeDrawDriverString:
2115 sal_uInt32 brushIndexOrColor;
2116 sal_uInt32 optionFlags;
2117 sal_uInt32 hasMatrix;
2118 sal_uInt32 glyphsCount;
2119 rMS.ReadUInt32(brushIndexOrColor).ReadUInt32(optionFlags).ReadUInt32(hasMatrix).ReadUInt32(glyphsCount);
2120 SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
2121 SAL_INFO("drawinglayer.emf", "EMF+\t Option flags: 0x" << std::hex << optionFlags << std::dec);
2122 SAL_INFO("drawinglayer.emf", "EMF+\t Has matrix: " << hasMatrix);
2123 SAL_INFO("drawinglayer.emf", "EMF+\t Glyphs: " << glyphsCount);
2125 if ((optionFlags & 1) && glyphsCount > 0)
2127 std::unique_ptr<float[]> charsPosX(new float[glyphsCount]);
2128 std::unique_ptr<float[]> charsPosY(new float[glyphsCount]);
2129 OUString text = read_uInt16s_ToOUString(rMS, glyphsCount);
2130 SAL_INFO("drawinglayer.emf", "EMF+\t DrawDriverString string: " << text);
2132 for (sal_uInt32 i = 0; i<glyphsCount; i++)
2134 rMS.ReadFloat(charsPosX[i]).ReadFloat(charsPosY[i]);
2135 SAL_INFO("drawinglayer.emf", "EMF+\t\t glyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]);
2138 basegfx::B2DHomMatrix transform;
2140 if (hasMatrix)
2142 readXForm(rMS, transform);
2143 SAL_INFO("drawinglayer.emf", "EMF+\tmatrix: " << transform);
2146 // get the font from the flags
2147 EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get());
2148 if (!font)
2150 break;
2152 // done reading
2154 drawinglayer::attribute::FontAttribute fontAttribute(
2155 font->family, // font family
2156 "", // (no) font style
2157 font->Bold() ? 8u : 1u, // weight: 8 = bold
2158 font->family == "SYMBOL", // symbol
2159 optionFlags & 0x2, // vertical
2160 font->Italic(), // italic
2161 false, // monospaced
2162 false, // outline = false, no such thing in MS-EMFPLUS
2163 false, // right-to-left
2164 false); // BiDiStrong
2166 const Color color = EMFPGetBrushColorOrARGBColor(flags, brushIndexOrColor);
2168 // generate TextSimplePortionPrimitive2Ds or TextDecoratedPortionPrimitive2D
2169 // for all portions of text with the same charsPosY values
2170 sal_uInt32 pos = 0;
2171 while (pos < glyphsCount)
2173 //determine the current length
2174 sal_uInt32 aLength = 1;
2175 while (pos + aLength < glyphsCount && std::abs( charsPosY[pos + aLength] - charsPosY[pos] ) < std::numeric_limits< float >::epsilon())
2176 aLength++;
2178 // generate the DX-Array
2179 std::vector<double> aDXArray;
2180 for (size_t i = 0; i < aLength - 1; i++)
2182 aDXArray.push_back(charsPosX[pos + i + 1] - charsPosX[pos]);
2184 // last entry
2185 aDXArray.push_back(0);
2187 basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
2188 ::basegfx::B2DVector(font->emSize, font->emSize),
2189 ::basegfx::B2DPoint(charsPosX[pos], charsPosY[pos]));
2190 if (hasMatrix)
2191 transformMatrix *= transform;
2192 if (color.GetAlpha() > 0)
2194 rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText;
2195 if (font->Underline() || font->Strikeout())
2197 pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
2198 transformMatrix,
2199 text,
2200 pos, // take character at current pos
2201 aLength, // use determined length
2202 std::move(aDXArray), // generated DXArray
2204 fontAttribute,
2205 Application::GetSettings().GetLanguageTag().getLocale(),
2206 color.getBColor(),
2207 COL_TRANSPARENT,
2208 color.getBColor(),
2209 color.getBColor(),
2210 drawinglayer::primitive2d::TEXT_LINE_NONE,
2211 font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
2212 false,
2213 font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE);
2215 else
2217 pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
2218 transformMatrix,
2219 text,
2220 pos, // take character at current pos
2221 aLength, // use determined length
2222 std::move(aDXArray), // generated DXArray
2224 fontAttribute,
2225 Application::GetSettings().GetLanguageTag().getLocale(),
2226 color.getBColor());
2228 drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText);
2229 if (color.IsTransparent())
2231 aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
2232 drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText },
2233 (255 - color.GetAlpha()) / 255.0);
2235 mrTargetHolders.Current().append(
2236 new drawinglayer::primitive2d::TransformPrimitive2D(
2237 maMapTransform,
2238 drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } ));
2241 // update pos
2242 pos += aLength;
2245 else
2247 SAL_WARN("drawinglayer.emf", "EMF+\tTODO: fonts (non-unicode glyphs chars)");
2249 break;
2251 default:
2253 SAL_WARN("drawinglayer.emf", "EMF+ TODO unhandled record type: 0x" << std::hex << type << std::dec);
2258 rMS.Seek(next);
2260 if (size <= length)
2262 length -= size;
2264 else
2266 SAL_WARN("drawinglayer.emf", "ImplRenderer::processEMFPlus: "
2267 "size " << size << " > length " << length);
2268 length = 0;
2274 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */