Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / drawinglayer / source / tools / emfphelperdata.cxx
blobb5b7d911476f58b981b9f93d683feff01a7a05fd
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 EmfPlusRecordTypeDrawClosedCurve: return "EmfPlusRecordTypeDrawClosedCurve";
85 case EmfPlusRecordTypeDrawCurve: return "EmfPlusRecordTypeDrawCurve";
86 case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse";
87 case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse";
88 case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie";
89 case EmfPlusRecordTypeDrawPie: return "EmfPlusRecordTypeDrawPie";
90 case EmfPlusRecordTypeDrawArc: return "EmfPlusRecordTypeDrawArc";
91 case EmfPlusRecordTypeFillRegion: return "EmfPlusRecordTypeFillRegion";
92 case EmfPlusRecordTypeFillPath: return "EmfPlusRecordTypeFillPath";
93 case EmfPlusRecordTypeDrawPath: return "EmfPlusRecordTypeDrawPath";
94 case EmfPlusRecordTypeDrawBeziers: return "EmfPlusRecordTypeDrawBeziers";
95 case EmfPlusRecordTypeDrawImage: return "EmfPlusRecordTypeDrawImage";
96 case EmfPlusRecordTypeDrawImagePoints: return "EmfPlusRecordTypeDrawImagePoints";
97 case EmfPlusRecordTypeDrawString: return "EmfPlusRecordTypeDrawString";
98 case EmfPlusRecordTypeSetRenderingOrigin: return "EmfPlusRecordTypeSetRenderingOrigin";
99 case EmfPlusRecordTypeSetAntiAliasMode: return "EmfPlusRecordTypeSetAntiAliasMode";
100 case EmfPlusRecordTypeSetTextRenderingHint: return "EmfPlusRecordTypeSetTextRenderingHint";
101 case EmfPlusRecordTypeSetTextContrast: return "EmfPlusRecordTypeSetTextContrast";
102 case EmfPlusRecordTypeSetInterpolationMode: return "EmfPlusRecordTypeSetInterpolationMode";
103 case EmfPlusRecordTypeSetPixelOffsetMode: return "EmfPlusRecordTypeSetPixelOffsetMode";
104 case EmfPlusRecordTypeSetCompositingQuality: return "EmfPlusRecordTypeSetCompositingQuality";
105 case EmfPlusRecordTypeSave: return "EmfPlusRecordTypeSave";
106 case EmfPlusRecordTypeRestore: return "EmfPlusRecordTypeRestore";
107 case EmfPlusRecordTypeBeginContainer: return "EmfPlusRecordTypeBeginContainer";
108 case EmfPlusRecordTypeBeginContainerNoParams: return "EmfPlusRecordTypeBeginContainerNoParams";
109 case EmfPlusRecordTypeEndContainer: return "EmfPlusRecordTypeEndContainer";
110 case EmfPlusRecordTypeSetWorldTransform: return "EmfPlusRecordTypeSetWorldTransform";
111 case EmfPlusRecordTypeResetWorldTransform: return "EmfPlusRecordTypeResetWorldTransform";
112 case EmfPlusRecordTypeMultiplyWorldTransform: return "EmfPlusRecordTypeMultiplyWorldTransform";
113 case EmfPlusRecordTypeTranslateWorldTransform: return "EmfPlusRecordTypeTranslateWorldTransform";
114 case EmfPlusRecordTypeScaleWorldTransform: return "EmfPlusRecordTypeScaleWorldTransform";
115 case EmfPlusRecordTypeSetPageTransform: return "EmfPlusRecordTypeSetPageTransform";
116 case EmfPlusRecordTypeResetClip: return "EmfPlusRecordTypeResetClip";
117 case EmfPlusRecordTypeSetClipRect: return "EmfPlusRecordTypeSetClipRect";
118 case EmfPlusRecordTypeSetClipPath: return "EmfPlusRecordTypeSetClipPath";
119 case EmfPlusRecordTypeSetClipRegion: return "EmfPlusRecordTypeSetClipRegion";
120 case EmfPlusRecordTypeOffsetClip: return "EmfPlusRecordTypeOffsetClip";
121 case EmfPlusRecordTypeDrawDriverString: return "EmfPlusRecordTypeDrawDriverString";
123 return "";
126 static OUString emfObjectToName(sal_uInt16 type)
128 switch (type)
130 case EmfPlusObjectTypeBrush: return "EmfPlusObjectTypeBrush";
131 case EmfPlusObjectTypePen: return "EmfPlusObjectTypePen";
132 case EmfPlusObjectTypePath: return "EmfPlusObjectTypePath";
133 case EmfPlusObjectTypeRegion: return "EmfPlusObjectTypeRegion";
134 case EmfPlusObjectTypeImage: return "EmfPlusObjectTypeImage";
135 case EmfPlusObjectTypeFont: return "EmfPlusObjectTypeFont";
136 case EmfPlusObjectTypeStringFormat: return "EmfPlusObjectTypeStringFormat";
137 case EmfPlusObjectTypeImageAttributes: return "EmfPlusObjectTypeImageAttributes";
138 case EmfPlusObjectTypeCustomLineCap: return "EmfPlusObjectTypeCustomLineCap";
140 return "";
143 static OUString PixelOffsetModeToString(sal_uInt16 nPixelOffset)
145 switch (nPixelOffset)
147 case PixelOffsetMode::PixelOffsetModeDefault: return "PixelOffsetModeDefault";
148 case PixelOffsetMode::PixelOffsetModeHighSpeed: return "PixelOffsetModeHighSpeed";
149 case PixelOffsetMode::PixelOffsetModeHighQuality: return "PixelOffsetModeHighQuality";
150 case PixelOffsetMode::PixelOffsetModeNone: return "PixelOffsetModeNone";
151 case PixelOffsetMode::PixelOffsetModeHalf: return "PixelOffsetModeHalf";
153 return "";
156 static OUString SmoothingModeToString(sal_uInt16 nSmoothMode)
158 switch (nSmoothMode)
160 case SmoothingMode::SmoothingModeDefault: return "SmoothingModeDefault";
161 case SmoothingMode::SmoothingModeHighSpeed: return "SmoothModeHighSpeed";
162 case SmoothingMode::SmoothingModeHighQuality: return "SmoothingModeHighQuality";
163 case SmoothingMode::SmoothingModeNone: return "SmoothingModeNone";
164 case SmoothingMode::SmoothingModeAntiAlias8x4: return "SmoothingModeAntiAlias8x4";
165 case SmoothingMode::SmoothingModeAntiAlias8x8: return "SmoothingModeAntiAlias8x8";
167 return "";
170 static OUString TextRenderingHintToString(sal_uInt16 nHint)
172 switch (nHint)
174 case TextRenderingHint::TextRenderingHintSystemDefault: return "TextRenderingHintSystemDefault";
175 case TextRenderingHint::TextRenderingHintSingleBitPerPixelGridFit: return "TextRenderingHintSingleBitPerPixelGridFit";
176 case TextRenderingHint::TextRenderingHintSingleBitPerPixel: return "TextRenderingHintSingleBitPerPixel";
177 case TextRenderingHint::TextRenderingHintAntialiasGridFit: return "TextRenderingHintAntialiasGridFit";
178 case TextRenderingHint::TextRenderingHintAntialias: return "TextRenderingHintAntialias";
179 case TextRenderingHint::TextRenderingHintClearTypeGridFit: return "TextRenderingHintClearTypeGridFit";
181 return "";
184 static OUString InterpolationModeToString(sal_uInt16 nMode)
186 switch (nMode)
188 case InterpolationMode::InterpolationModeDefault: return "InterpolationModeDefault";
189 case InterpolationMode::InterpolationModeLowQuality: return "InterpolationModeLowQuality";
190 case InterpolationMode::InterpolationModeHighQuality: return "InterpolationModeHighQuality";
191 case InterpolationMode::InterpolationModeBilinear: return "InterpolationModeBilinear";
192 case InterpolationMode::InterpolationModeBicubic: return "InterpolationModeBicubic";
193 case InterpolationMode::InterpolationModeNearestNeighbor: return "InterpolationModeNearestNeighbor";
194 case InterpolationMode::InterpolationModeHighQualityBilinear: return "InterpolationModeHighQualityBilinear";
195 case InterpolationMode::InterpolationModeHighQualityBicubic: return "InterpolationModeHighQualityBicubic";
197 return "";
200 OUString UnitTypeToString(sal_uInt16 nType)
202 switch (nType)
204 case UnitTypeWorld: return "UnitTypeWorld";
205 case UnitTypeDisplay: return "UnitTypeDisplay";
206 case UnitTypePixel: return "UnitTypePixel";
207 case UnitTypePoint: return "UnitTypePoint";
208 case UnitTypeInch: return "UnitTypeInch";
209 case UnitTypeDocument: return "UnitTypeDocument";
210 case UnitTypeMillimeter: return "UnitTypeMillimeter";
212 return "";
215 static bool IsBrush(sal_uInt16 flags)
217 return (!((flags >> 15) & 0x0001));
220 static OUString BrushIDToString(sal_uInt16 flags, sal_uInt32 brushid)
222 if (IsBrush(flags))
223 return "EmfPlusBrush ID: " + OUString::number(brushid);
224 else
225 return "ARGB: 0x" + OUString::number(brushid, 16);
228 EMFPObject::~EMFPObject()
232 float EmfPlusHelperData::getUnitToPixelMultiplier(const UnitType aUnitType, const sal_uInt32 aDPI)
234 switch (aUnitType)
236 case UnitTypePixel:
237 return 1.0f;
239 case UnitTypePoint:
240 return aDPI / 72.0;
242 case UnitTypeInch:
243 return aDPI;
245 case UnitTypeMillimeter:
246 return aDPI / 25.4;
248 case UnitTypeDocument:
249 return aDPI / 300.0;
251 case UnitTypeWorld:
252 case UnitTypeDisplay:
253 SAL_WARN("drawinglayer.emf", "EMF+\t Converting to World/Display.");
254 return 1.0f;
256 default:
257 SAL_WARN("drawinglayer.emf", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType);
258 return 1.0f;
262 void EmfPlusHelperData::processObjectRecord(SvMemoryStream& rObjectStream, sal_uInt16 flags, sal_uInt32 dataSize, bool bUseWholeStream)
264 sal_uInt16 objecttype = flags & 0x7f00;
265 sal_uInt16 index = flags & 0xff;
266 SAL_INFO("drawinglayer.emf", "EMF+ Object: " << emfObjectToName(objecttype) << " (0x" << objecttype << ")");
267 SAL_INFO("drawinglayer.emf", "EMF+\tObject slot: " << index);
268 SAL_INFO("drawinglayer.emf", "EMF+\tFlags: " << (flags & 0xff00));
270 switch (objecttype)
272 case EmfPlusObjectTypeBrush:
274 EMFPBrush *brush = new EMFPBrush();
275 maEMFPObjects[index].reset(brush);
276 brush->Read(rObjectStream, *this);
277 break;
279 case EmfPlusObjectTypePen:
281 EMFPPen *pen = new EMFPPen();
282 maEMFPObjects[index].reset(pen);
283 pen->Read(rObjectStream, *this);
284 pen->penWidth = pen->penWidth * getUnitToPixelMultiplier(static_cast<UnitType>(pen->penUnit), mnHDPI);
285 break;
287 case EmfPlusObjectTypePath:
289 sal_uInt32 aVersion, aPathPointCount, aPathPointFlags;
291 rObjectStream.ReadUInt32(aVersion).ReadUInt32(aPathPointCount).ReadUInt32(aPathPointFlags);
292 SAL_INFO("drawinglayer.emf", "EMF+\t\tVersion: 0x" << std::hex << aVersion);
293 SAL_INFO("drawinglayer.emf", "EMF+\t\tNumber of points: " << std::dec << aPathPointCount);
294 SAL_INFO("drawinglayer.emf", "EMF+\t\tPath point flags: 0x" << std::hex << aPathPointFlags << std::dec);
295 EMFPPath *path = new EMFPPath(aPathPointCount);
296 maEMFPObjects[index].reset(path);
297 path->Read(rObjectStream, aPathPointFlags);
298 break;
300 case EmfPlusObjectTypeRegion:
302 EMFPRegion *region = new EMFPRegion();
303 maEMFPObjects[index].reset(region);
304 region->ReadRegion(rObjectStream, *this);
305 break;
307 case EmfPlusObjectTypeImage:
309 EMFPImage *image = new EMFPImage;
310 maEMFPObjects[index].reset(image);
311 image->type = 0;
312 image->width = 0;
313 image->height = 0;
314 image->stride = 0;
315 image->pixelFormat = 0;
316 image->Read(rObjectStream, dataSize, bUseWholeStream);
317 break;
319 case EmfPlusObjectTypeFont:
321 EMFPFont *font = new EMFPFont;
322 maEMFPObjects[index].reset(font);
323 font->emSize = 0;
324 font->sizeUnit = 0;
325 font->fontFlags = 0;
326 font->Read(rObjectStream);
327 // tdf#113624 Convert unit to Pixels
328 font->emSize = font->emSize * getUnitToPixelMultiplier(static_cast<UnitType>(font->sizeUnit), mnHDPI);
330 break;
332 case EmfPlusObjectTypeStringFormat:
334 EMFPStringFormat *stringFormat = new EMFPStringFormat();
335 maEMFPObjects[index].reset(stringFormat);
336 stringFormat->Read(rObjectStream);
337 break;
339 case EmfPlusObjectTypeImageAttributes:
341 EMFPImageAttributes *imageAttributes = new EMFPImageAttributes();
342 maEMFPObjects[index].reset(imageAttributes);
343 imageAttributes->Read(rObjectStream);
344 break;
346 case EmfPlusObjectTypeCustomLineCap:
348 SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object type 'custom line cap' not yet implemented");
349 break;
351 default:
353 SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec);
358 void EmfPlusHelperData::ReadPoint(SvStream& s, float& x, float& y, sal_uInt32 flags)
360 if (flags & 0x800)
362 // specifies a location in the coordinate space that is relative to
363 // the location specified by the previous element in the array. In the case of the first element in
364 // PointData, a previous location at coordinates (0,0) is assumed.
365 SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Relative coordinates bit detected. Implement parse EMFPlusPointR");
368 if (flags & 0x4000)
370 sal_Int16 ix, iy;
372 s.ReadInt16(ix).ReadInt16(iy);
374 x = ix;
375 y = iy;
377 else
379 s.ReadFloat(x).ReadFloat(y);
383 void EmfPlusHelperData::ReadRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed)
385 if (bCompressed)
387 sal_Int16 ix, iy, iw, ih;
389 s.ReadInt16(ix).ReadInt16(iy).ReadInt16(iw).ReadInt16(ih);
391 x = ix;
392 y = iy;
393 width = iw;
394 height = ih;
396 else
398 s.ReadFloat(x).ReadFloat(y).ReadFloat(width).ReadFloat(height);
402 bool EmfPlusHelperData::readXForm(SvStream& rIn, basegfx::B2DHomMatrix& rTarget)
404 rTarget.identity();
406 if (sizeof(float) != 4)
408 OSL_FAIL("EnhWMFReader::sizeof( float ) != 4");
409 return false;
411 else
413 float eM11(0.0);
414 float eM12(0.0);
415 float eM21(0.0);
416 float eM22(0.0);
417 float eDx(0.0);
418 float eDy(0.0);
419 rIn.ReadFloat(eM11).ReadFloat(eM12).ReadFloat(eM21).ReadFloat(eM22).ReadFloat(eDx).ReadFloat(eDy);
420 rTarget = basegfx::B2DHomMatrix(
421 eM11, eM21, eDx,
422 eM12, eM22, eDy);
425 return true;
428 void EmfPlusHelperData::mappingChanged()
430 if (mnPixX == 0 || mnPixY == 0)
432 SAL_WARN("drawinglayer.emf", "dimensions in pixels is 0");
433 return;
435 // Call when mnMmX/mnMmY/mnPixX/mnPixY/mnFrameLeft/mnFrameTop/maWorldTransform/ changes.
436 // Currently not used are mnHDPI/mnVDPI/mnFrameRight/mnFrameBottom. *If* these should
437 // be used in the future, this method will need to be called.
439 // Re-calculate maMapTransform to contain the complete former transformation so that
440 // it can be applied by a single matrix multiplication or be added to an encapsulated
441 // primitive later
443 // To evtl. correct and see where this came from, please compare with the implementations
444 // of EmfPlusHelperData::MapToDevice and EmfPlusHelperData::Map* in prev versions
445 maMapTransform = maWorldTransform;
446 maMapTransform *= basegfx::utils::createScaleTranslateB2DHomMatrix(100.0 * mnMmX / mnPixX, 100.0 * mnMmY / mnPixY,
447 double(-mnFrameLeft), double(-mnFrameTop));
448 maMapTransform *= maBaseTransform;
450 // Used only for performance optimization, to do not calculate it every line draw
451 mdExtractedXScale = std::hypot(maMapTransform.a(), maMapTransform.b());
452 mdExtractedYScale = std::hypot(maMapTransform.c(), maMapTransform.d());
455 ::basegfx::B2DPoint EmfPlusHelperData::Map(double ix, double iy) const
457 // map in one step using complete MapTransform (see mappingChanged)
458 return maMapTransform * ::basegfx::B2DPoint(ix, iy);
461 Color EmfPlusHelperData::EMFPGetBrushColorOrARGBColor(const sal_uInt16 flags, const sal_uInt32 brushIndexOrColor) const {
462 Color color;
463 if (flags & 0x8000) // we use a color
465 color = Color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff,
466 (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
468 else // we use a brush
470 const EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
471 if (brush)
473 color = brush->GetColor();
474 if (brush->type != BrushTypeSolidColor)
475 SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Brush other than solid color is not supported");
478 return color;
481 void EmfPlusHelperData::GraphicStatePush(GraphicStateMap& map, sal_Int32 index)
483 GraphicStateMap::iterator iter = map.find( index );
485 if ( iter != map.end() )
487 map.erase( iter );
488 SAL_INFO("drawinglayer.emf", "EMF+\t\tStack index: " << index << " found and erased");
491 wmfemfhelper::PropertyHolder state = mrPropertyHolders.Current();
492 // tdf#112500 We need to save world transform somehow, during graphic state push
493 state.setTransformation(maWorldTransform);
494 map[ index ] = state;
497 void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index)
499 GraphicStateMap::iterator iter = map.find(index);
501 if (iter != map.end())
503 wmfemfhelper::PropertyHolder state = iter->second;
505 maWorldTransform = state.getTransformation();
506 if (state.getClipPolyPolygonActive())
508 SAL_INFO("drawinglayer.emf",
509 "EMF+\t Restore clipping region to saved in index: " << index);
510 wmfemfhelper::HandleNewClipRegion(state.getClipPolyPolygon(), mrTargetHolders,
511 mrPropertyHolders);
513 else
515 SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping");
516 wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders,
517 mrPropertyHolders);
519 mappingChanged();
520 SAL_INFO("drawinglayer.emf",
521 "EMF+\t\tStack index: " << index
522 << " found, maWorldTransform: " << maWorldTransform);
526 drawinglayer::attribute::LineStartEndAttribute
527 EmfPlusHelperData::CreateLineEnd(const sal_Int32 aCap, const float aPenWidth) const
529 const double pw = mdExtractedYScale * aPenWidth;
530 if (aCap == LineCapTypeSquare)
532 basegfx::B2DPolygon aCapPolygon(
533 { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
534 aCapPolygon.setClosed(true);
535 return drawinglayer::attribute::LineStartEndAttribute(
536 pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
538 else if (aCap == LineCapTypeRound)
540 basegfx::B2DPolygon aCapPolygon(
541 { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.9236, -0.3827},
542 {0.7071, -0.7071}, {0.3827, -0.9236}, {0.0, -1.0}, {-0.3827, -0.9236},
543 {-0.7071, -0.7071}, {-0.9236, -0.3827}, {-1.0, 0.0} });
544 aCapPolygon.setClosed(true);
545 return drawinglayer::attribute::LineStartEndAttribute(
546 pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
548 else if (aCap == LineCapTypeTriangle)
550 basegfx::B2DPolygon aCapPolygon(
551 { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.0, -1.0}, {-1.0, 0.0} });
552 aCapPolygon.setClosed(true);
553 return drawinglayer::attribute::LineStartEndAttribute(
554 pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
556 else if (aCap == LineCapTypeSquareAnchor)
558 basegfx::B2DPolygon aCapPolygon(
559 { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
560 aCapPolygon.setClosed(true);
561 return drawinglayer::attribute::LineStartEndAttribute(
562 1.5 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
564 else if (aCap == LineCapTypeRoundAnchor)
566 const basegfx::B2DPolygon aCapPolygon
567 = ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(0.0, 0.0), 1.0, 1.0);
568 return drawinglayer::attribute::LineStartEndAttribute(
569 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
571 else if (aCap == LineCapTypeDiamondAnchor)
573 basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 0.0}, {0.5, 0.5},
574 {0.5, 1.0}, {-0.5, 1.0}, {-0.5, 0.5},
575 {-1.0, 0.0} });
576 aCapPolygon.setClosed(true);
577 return drawinglayer::attribute::LineStartEndAttribute(
578 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
580 else if (aCap == LineCapTypeArrowAnchor)
582 basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
583 aCapPolygon.setClosed(true);
584 return drawinglayer::attribute::LineStartEndAttribute(
585 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
587 return drawinglayer::attribute::LineStartEndAttribute();
590 void EmfPlusHelperData::EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon,
591 sal_uInt32 penIndex)
593 const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 0xff].get());
594 SAL_WARN_IF(!pen, "drawinglayer.emf", "emf+ missing pen");
596 if (!(pen && polygon.count()))
597 return;
599 const double transformedPenWidth = mdExtractedYScale * pen->penWidth;
600 drawinglayer::attribute::LineAttribute lineAttribute(
601 pen->GetColor().getBColor(), transformedPenWidth, pen->maLineJoin,
602 css::drawing::LineCap_BUTT, //TODO implement PenDataDashedLineCap support here
603 pen->fMiterMinimumAngle);
605 drawinglayer::attribute::LineStartEndAttribute aStart;
606 if (pen->penDataFlags & EmfPlusPenDataStartCap)
608 if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap)
609 && (pen->customStartCap->polygon.begin()->count() > 1))
610 aStart = drawinglayer::attribute::LineStartEndAttribute(
611 pen->customStartCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
612 * pen->customStartCap->widthScale * pen->penWidth,
613 pen->customStartCap->polygon, false);
614 else
615 aStart = EmfPlusHelperData::CreateLineEnd(pen->startCap, pen->penWidth);
618 drawinglayer::attribute::LineStartEndAttribute aEnd;
619 if (pen->penDataFlags & EmfPlusPenDataEndCap)
621 if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap)
622 && (pen->customEndCap->polygon.begin()->count() > 1))
623 aEnd = drawinglayer::attribute::LineStartEndAttribute(
624 pen->customEndCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
625 * pen->customEndCap->widthScale * pen->penWidth,
626 pen->customEndCap->polygon, false);
627 else
628 aEnd = EmfPlusHelperData::CreateLineEnd(pen->endCap, pen->penWidth);
631 if (pen->GetColor().IsTransparent())
633 drawinglayer::primitive2d::Primitive2DContainer aContainer;
634 if (aStart.isDefault() && aEnd.isDefault())
635 aContainer.append(drawinglayer::primitive2d::Primitive2DReference(
636 new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
637 polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale))));
638 else
640 aContainer.resize(polygon.count());
641 for (sal_uInt32 i = 0; i < polygon.count(); i++)
642 aContainer[i] = drawinglayer::primitive2d::Primitive2DReference(
643 new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
644 polygon.getB2DPolygon(i), lineAttribute,
645 pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd));
647 mrTargetHolders.Current().append(
648 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
649 std::move(aContainer), (255 - pen->GetColor().GetAlpha()) / 255.0));
651 else
653 if (aStart.isDefault() && aEnd.isDefault())
654 mrTargetHolders.Current().append(
655 new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
656 polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale)));
657 else
658 for (sal_uInt32 i = 0; i < polygon.count(); i++)
660 mrTargetHolders.Current().append(
661 new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
662 polygon.getB2DPolygon(i), lineAttribute,
663 pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd));
666 mrPropertyHolders.Current().setLineColor(pen->GetColor().getBColor());
667 mrPropertyHolders.Current().setLineColorActive(true);
668 mrPropertyHolders.Current().setFillColorActive(false);
671 void EmfPlusHelperData::EMFPPlusFillPolygonSolidColor(const ::basegfx::B2DPolyPolygon& polygon, Color const& color)
673 if (color.GetAlpha() == 0)
674 return;
676 if (!color.IsTransparent())
678 // not transparent
679 mrTargetHolders.Current().append(
680 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
681 polygon,
682 color.getBColor()));
684 else
686 const drawinglayer::primitive2d::Primitive2DReference aPrimitive(
687 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
688 polygon,
689 color.getBColor()));
691 mrTargetHolders.Current().append(
692 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
693 drawinglayer::primitive2d::Primitive2DContainer { aPrimitive },
694 (255 - color.GetAlpha()) / 255.0));
698 void EmfPlusHelperData::EMFPPlusFillPolygon(const ::basegfx::B2DPolyPolygon& polygon, const bool isColor, const sal_uInt32 brushIndexOrColor)
700 if (!polygon.count())
701 return;
703 if (isColor) // use Color
705 SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, ARGB color: 0x" << std::hex << brushIndexOrColor << std::dec);
707 // EMF Alpha (1 byte): An 8-bit unsigned integer that specifies the transparency of the background,
708 // ranging from 0 for completely transparent to 0xFF for completely opaque.
709 const Color color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
710 EMFPPlusFillPolygonSolidColor(polygon, color);
712 mrPropertyHolders.Current().setFillColor(color.getBColor());
713 mrPropertyHolders.Current().setFillColorActive(true);
714 mrPropertyHolders.Current().setLineColorActive(false);
716 else // use Brush
718 EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
719 SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, brush slot: " << brushIndexOrColor << " (brush type: " << (brush ? brush->GetType() : -1) << ")");
721 // give up in case something wrong happened
722 if( !brush )
723 return;
725 mrPropertyHolders.Current().setFillColorActive(false);
726 mrPropertyHolders.Current().setLineColorActive(false);
728 if (brush->type == BrushTypeSolidColor)
730 Color fillColor = brush->solidColor;
731 EMFPPlusFillPolygonSolidColor(polygon, fillColor);
733 else if (brush->type == BrushTypeHatchFill)
735 // EMF+ like hatching is currently not supported. These are just color blends which serve as an approximation for some of them
736 // for the others the hatch "background" color (secondColor in brush) is used.
738 bool isHatchBlend = true;
739 double blendFactor = 0.0;
741 switch (brush->hatchStyle)
743 case HatchStyle05Percent: blendFactor = 0.05; break;
744 case HatchStyle10Percent: blendFactor = 0.10; break;
745 case HatchStyle20Percent: blendFactor = 0.20; break;
746 case HatchStyle25Percent: blendFactor = 0.25; break;
747 case HatchStyle30Percent: blendFactor = 0.30; break;
748 case HatchStyle40Percent: blendFactor = 0.40; break;
749 case HatchStyle50Percent: blendFactor = 0.50; break;
750 case HatchStyle60Percent: blendFactor = 0.60; break;
751 case HatchStyle70Percent: blendFactor = 0.70; break;
752 case HatchStyle75Percent: blendFactor = 0.75; break;
753 case HatchStyle80Percent: blendFactor = 0.80; break;
754 case HatchStyle90Percent: blendFactor = 0.90; break;
755 default:
756 isHatchBlend = false;
757 break;
759 Color fillColor;
760 if (isHatchBlend)
762 fillColor = brush->solidColor;
763 fillColor.Merge(brush->secondColor, static_cast<sal_uInt8>(255 * blendFactor));
765 else
767 fillColor = brush->secondColor;
769 // temporal solution: create a solid colored polygon
770 // TODO create a 'real' hatching primitive
771 mrTargetHolders.Current().append(
772 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
773 polygon,
774 fillColor.getBColor()));
776 else if (brush->type == BrushTypeTextureFill)
778 SAL_WARN("drawinglayer.emf", "EMF+\tTODO: implement BrushTypeTextureFill brush");
780 else if (brush->type == BrushTypePathGradient || brush->type == BrushTypeLinearGradient)
783 if (brush->type == BrushTypePathGradient && !(brush->additionalFlags & 0x1))
785 SAL_WARN("drawinglayer.emf", "EMF+\t TODO Implement displaying BrushTypePathGradient with Boundary: ");
787 ::basegfx::B2DHomMatrix aTextureTransformation;
789 if (brush->hasTransformation) {
790 aTextureTransformation = brush->brush_transformation;
792 // adjust aTextureTransformation for our world space:
793 // -> revert the mapping -> apply the transformation -> map back
794 basegfx::B2DHomMatrix aInvertedMapTrasform(maMapTransform);
795 aInvertedMapTrasform.invert();
796 aTextureTransformation = maMapTransform * aTextureTransformation * aInvertedMapTrasform;
799 // select the stored colors
800 const basegfx::BColor aStartColor = brush->solidColor.getBColor();
801 const basegfx::BColor aEndColor = brush->secondColor.getBColor();
802 drawinglayer::primitive2d::SvgGradientEntryVector aVector;
804 if (brush->blendPositions)
806 SAL_INFO("drawinglayer.emf", "EMF+\t\tUse blend");
808 // store the blendpoints in the vector
809 for (sal_uInt32 i = 0; i < brush->blendPoints; i++)
811 const double aBlendPoint = brush->blendPositions[i];
812 basegfx::BColor aColor;
813 aColor.setGreen(aStartColor.getGreen() + brush->blendFactors[i] * (aEndColor.getGreen() - aStartColor.getGreen()));
814 aColor.setBlue (aStartColor.getBlue() + brush->blendFactors[i] * (aEndColor.getBlue() - aStartColor.getBlue()));
815 aColor.setRed (aStartColor.getRed() + brush->blendFactors[i] * (aEndColor.getRed() - aStartColor.getRed()));
816 const double aAlpha = brush->solidColor.GetAlpha() + brush->blendFactors[i] * (brush->secondColor.GetAlpha() - brush->solidColor.GetAlpha());
817 aVector.emplace_back(aBlendPoint, aColor, aAlpha / 255.0);
820 else if (brush->colorblendPositions)
822 SAL_INFO("drawinglayer.emf", "EMF+\t\tUse color blend");
824 // store the colorBlends in the vector
825 for (sal_uInt32 i = 0; i < brush->colorblendPoints; i++)
827 const double aBlendPoint = brush->colorblendPositions[i];
828 const basegfx::BColor aColor = brush->colorblendColors[i].getBColor();
829 aVector.emplace_back(aBlendPoint, aColor, brush->colorblendColors[i].GetAlpha() / 255.0);
832 else // ok, no extra points: just start and end
834 aVector.emplace_back(0.0, aStartColor, brush->solidColor.GetAlpha() / 255.0);
835 aVector.emplace_back(1.0, aEndColor, brush->secondColor.GetAlpha() / 255.0);
838 // get the polygon range to be able to map the start/end/center point correctly
839 // therefore, create a mapping and invert it
840 basegfx::B2DRange aPolygonRange= polygon.getB2DRange();
841 basegfx::B2DHomMatrix aPolygonTransformation = basegfx::utils::createScaleTranslateB2DHomMatrix(
842 aPolygonRange.getWidth(),aPolygonRange.getHeight(),
843 aPolygonRange.getMinX(), aPolygonRange.getMinY());
844 aPolygonTransformation.invert();
846 if (brush->type == BrushTypeLinearGradient)
848 // support for public enum EmfPlusWrapMode
849 basegfx::B2DPoint aStartPoint = Map(brush->firstPointX, 0.0);
850 aStartPoint = aPolygonTransformation * aStartPoint;
851 basegfx::B2DPoint aEndPoint = Map(brush->firstPointX + brush->aWidth, 0.0);
852 aEndPoint = aPolygonTransformation * aEndPoint;
854 // support for public enum EmfPlusWrapMode
855 drawinglayer::primitive2d::SpreadMethod aSpreadMethod(drawinglayer::primitive2d::SpreadMethod::Pad);
856 switch(brush->wrapMode)
858 case WrapModeTile:
859 case WrapModeTileFlipY:
861 aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Repeat;
862 break;
864 case WrapModeTileFlipX:
865 case WrapModeTileFlipXY:
867 aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Reflect;
868 break;
870 default:
871 break;
874 // create the same one used for SVG
875 mrTargetHolders.Current().append(
876 new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D(
877 aTextureTransformation,
878 polygon,
879 std::move(aVector),
880 aStartPoint,
881 aEndPoint,
882 false, // do not use UnitCoordinates
883 aSpreadMethod));
885 else // BrushTypePathGradient
886 { // TODO The PathGradient is not implemented, and Radial Gradient is used instead
887 basegfx::B2DPoint aCenterPoint = Map(brush->firstPointX, brush->firstPointY);
888 aCenterPoint = aPolygonTransformation * aCenterPoint;
890 // create the same one used for SVG
891 mrTargetHolders.Current().append(
892 new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D(
893 aTextureTransformation,
894 polygon,
895 std::move(aVector),
896 aCenterPoint,
897 0.7, // relative radius little bigger to cover all elements
898 true, // use UnitCoordinates to stretch the gradient
899 drawinglayer::primitive2d::SpreadMethod::Pad,
900 nullptr));
906 EmfPlusHelperData::EmfPlusHelperData(
907 SvMemoryStream& rMS,
908 wmfemfhelper::TargetHolders& rTargetHolders,
909 wmfemfhelper::PropertyHolders& rPropertyHolders)
910 : mfPageScale(0.0),
911 mnOriginX(0),
912 mnOriginY(0),
913 mnHDPI(0),
914 mnVDPI(0),
915 mbSetTextContrast(false),
916 mnTextContrast(0),
917 mnFrameLeft(0),
918 mnFrameTop(0),
919 mnFrameRight(0),
920 mnFrameBottom(0),
921 mnPixX(0),
922 mnPixY(0),
923 mnMmX(0),
924 mnMmY(0),
925 mbMultipart(false),
926 mMFlags(0),
927 mdExtractedXScale(1.0),
928 mdExtractedYScale(1.0),
929 mrTargetHolders(rTargetHolders),
930 mrPropertyHolders(rPropertyHolders),
931 bIsGetDCProcessing(false)
933 rMS.ReadInt32(mnFrameLeft).ReadInt32(mnFrameTop).ReadInt32(mnFrameRight).ReadInt32(mnFrameBottom);
934 SAL_INFO("drawinglayer.emf", "EMF+ picture frame: " << mnFrameLeft << "," << mnFrameTop << " - " << mnFrameRight << "," << mnFrameBottom);
935 rMS.ReadInt32(mnPixX).ReadInt32(mnPixY).ReadInt32(mnMmX).ReadInt32(mnMmY);
936 SAL_INFO("drawinglayer.emf", "EMF+ ref device pixel size: " << mnPixX << "x" << mnPixY << " mm size: " << mnMmX << "x" << mnMmY);
937 readXForm(rMS, maBaseTransform);
938 SAL_INFO("drawinglayer.emf", "EMF+ base transform: " << maBaseTransform);
939 mappingChanged();
942 EmfPlusHelperData::~EmfPlusHelperData()
946 ::basegfx::B2DPolyPolygon EmfPlusHelperData::combineClip(::basegfx::B2DPolyPolygon const & leftPolygon, int combineMode, ::basegfx::B2DPolyPolygon const & rightPolygon)
948 basegfx::B2DPolyPolygon aClippedPolyPolygon;
949 switch (combineMode)
951 case EmfPlusCombineModeReplace:
953 aClippedPolyPolygon = rightPolygon;
954 break;
956 case EmfPlusCombineModeIntersect:
958 aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon(
959 leftPolygon, rightPolygon, true, false);
960 break;
962 case EmfPlusCombineModeUnion:
964 aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationOr(leftPolygon, rightPolygon);
965 break;
967 case EmfPlusCombineModeXOR:
969 aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationXor(leftPolygon, rightPolygon);
970 break;
972 case EmfPlusCombineModeExclude:
974 // Replaces the existing region with the part of itself that is not in the new region.
975 aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(leftPolygon, rightPolygon);
976 break;
978 case EmfPlusCombineModeComplement:
980 // Replaces the existing region with the part of the new region that is not in the existing region.
981 aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(rightPolygon, leftPolygon);
982 break;
985 return aClippedPolyPolygon;
988 void EmfPlusHelperData::processEmfPlusData(
989 SvMemoryStream& rMS,
990 const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/)
992 sal_uInt64 length = rMS.GetSize();
994 if (length < 12)
995 SAL_WARN("drawinglayer.emf", "length is less than required header size");
997 // 12 is minimal valid EMF+ record size; remaining bytes are padding
998 while (length >= 12)
1000 sal_uInt16 type, flags;
1001 sal_uInt32 size, dataSize;
1002 sal_uInt64 next;
1004 rMS.ReadUInt16(type).ReadUInt16(flags).ReadUInt32(size).ReadUInt32(dataSize);
1006 next = rMS.Tell() + (size - 12);
1008 if (size < 12)
1010 SAL_WARN("drawinglayer.emf", "Size field is less than 12 bytes");
1011 break;
1013 else if (size > length)
1015 SAL_WARN("drawinglayer.emf", "Size field is greater than bytes left");
1016 break;
1019 if (dataSize > (size - 12))
1021 SAL_WARN("drawinglayer.emf", "DataSize field is greater than Size-12");
1022 break;
1025 SAL_INFO("drawinglayer.emf", "EMF+ " << emfTypeToName(type) << " (0x" << std::hex << type << ")" << std::dec);
1026 SAL_INFO("drawinglayer.emf", "EMF+\t record size: " << size);
1027 SAL_INFO("drawinglayer.emf", "EMF+\t flags: 0x" << std::hex << flags << std::dec);
1028 SAL_INFO("drawinglayer.emf", "EMF+\t data size: " << dataSize);
1030 if (bIsGetDCProcessing)
1032 if (aGetDCState.getClipPolyPolygonActive())
1034 SAL_INFO("drawinglayer.emf", "EMF+\t Restore region to GetDC saved");
1035 wmfemfhelper::HandleNewClipRegion(aGetDCState.getClipPolyPolygon(), mrTargetHolders,
1036 mrPropertyHolders);
1038 else
1040 SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping");
1041 wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders,
1042 mrPropertyHolders);
1044 bIsGetDCProcessing = false;
1046 if (type == EmfPlusRecordTypeObject && ((mbMultipart && (flags & 0x7fff) == (mMFlags & 0x7fff)) || (flags & 0x8000)))
1048 if (!mbMultipart)
1050 mbMultipart = true;
1051 mMFlags = flags;
1052 mMStream.Seek(0);
1055 OSL_ENSURE(dataSize >= 4, "No room for TotalObjectSize in EmfPlusContinuedObjectRecord");
1057 // 1st 4 bytes are TotalObjectSize
1058 mMStream.WriteBytes(static_cast<const char *>(rMS.GetData()) + rMS.Tell() + 4, dataSize - 4);
1059 SAL_INFO("drawinglayer.emf", "EMF+ read next object part size: " << size << " type: " << type << " flags: " << flags << " data size: " << dataSize);
1061 else
1063 if (mbMultipart)
1065 SAL_INFO("drawinglayer.emf", "EMF+ multipart record flags: " << mMFlags);
1066 mMStream.Seek(0);
1067 processObjectRecord(mMStream, mMFlags, 0, true);
1070 mbMultipart = false;
1073 if (type != EmfPlusRecordTypeObject || !(flags & 0x8000))
1075 switch (type)
1077 case EmfPlusRecordTypeHeader:
1079 sal_uInt32 header, version;
1081 rMS.ReadUInt32(header).ReadUInt32(version).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI);
1082 SAL_INFO("drawinglayer.emf", "EMF+\tHeader: 0x" << std::hex << header);
1083 SAL_INFO("drawinglayer.emf", "EMF+\tVersion: " << std::dec << version);
1084 SAL_INFO("drawinglayer.emf", "EMF+\tHorizontal DPI: " << mnHDPI);
1085 SAL_INFO("drawinglayer.emf", "EMF+\tVertical DPI: " << mnVDPI);
1086 SAL_INFO("drawinglayer.emf", "EMF+\tDual: " << ((flags & 1) ? "true" : "false"));
1087 break;
1089 case EmfPlusRecordTypeEndOfFile:
1091 break;
1093 case EmfPlusRecordTypeComment:
1095 #if OSL_DEBUG_LEVEL > 1
1096 unsigned char data;
1097 OUString hexdata;
1099 SAL_INFO("drawinglayer.emf", "EMF+\tDatasize: 0x" << std::hex << dataSize << std::dec);
1101 for (sal_uInt32 i=0; i<dataSize; i++)
1103 rMS.ReadUChar(data);
1105 if (i % 16 == 0)
1106 hexdata += "\n";
1108 OUString padding;
1109 if ((data & 0xF0) == 0)
1110 padding = "0";
1112 hexdata += "0x" + padding + OUString::number(data, 16) + " ";
1115 SAL_INFO("drawinglayer.emf", "EMF+\t" << hexdata);
1116 #endif
1117 break;
1119 case EmfPlusRecordTypeGetDC:
1121 bIsGetDCProcessing = true;
1122 aGetDCState = mrPropertyHolders.Current();
1123 SAL_INFO("drawinglayer.emf", "EMF+\tAlready used in svtools wmf/emf filter parser");
1124 break;
1126 case EmfPlusRecordTypeObject:
1128 processObjectRecord(rMS, flags, dataSize);
1129 break;
1131 case EmfPlusRecordTypeFillPie:
1132 case EmfPlusRecordTypeDrawPie:
1133 case EmfPlusRecordTypeDrawArc:
1135 float startAngle, sweepAngle;
1137 // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
1138 sal_uInt32 brushIndexOrColor = 999;
1140 if (type == EmfPlusRecordTypeFillPie)
1142 rMS.ReadUInt32(brushIndexOrColor);
1143 SAL_INFO("drawinglayer.emf", "EMF+\t FillPie colorOrIndex: " << brushIndexOrColor);
1145 else if (type == EmfPlusRecordTypeDrawPie)
1147 SAL_INFO("drawinglayer.emf", "EMF+\t DrawPie");
1149 else
1151 SAL_INFO("drawinglayer.emf", "EMF+\t DrawArc");
1154 rMS.ReadFloat(startAngle).ReadFloat(sweepAngle);
1155 float dx, dy, dw, dh;
1156 ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
1157 SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
1158 startAngle = basegfx::deg2rad(startAngle);
1159 sweepAngle = basegfx::deg2rad(sweepAngle);
1160 float endAngle = startAngle + sweepAngle;
1161 startAngle = fmodf(startAngle, static_cast<float>(M_PI * 2));
1163 if (startAngle < 0.0)
1165 startAngle += static_cast<float>(M_PI * 2.0);
1167 endAngle = fmodf(endAngle, static_cast<float>(M_PI * 2.0));
1169 if (endAngle < 0.0)
1171 endAngle += static_cast<float>(M_PI * 2.0);
1173 if (sweepAngle < 0)
1175 std::swap(endAngle, startAngle);
1178 SAL_INFO("drawinglayer.emf", "EMF+\t Adjusted angles: start " <<
1179 basegfx::rad2deg(startAngle) << ", end: " << basegfx::rad2deg(endAngle) <<
1180 " startAngle: " << startAngle << " sweepAngle: " << sweepAngle);
1181 const ::basegfx::B2DPoint centerPoint(dx + 0.5 * dw, dy + 0.5 * dh);
1182 ::basegfx::B2DPolygon polygon(
1183 ::basegfx::utils::createPolygonFromEllipseSegment(centerPoint,
1184 0.5 * dw, 0.5 * dh,
1185 startAngle, endAngle));
1186 if (type != EmfPlusRecordTypeDrawArc)
1188 polygon.append(centerPoint);
1189 polygon.setClosed(true);
1191 ::basegfx::B2DPolyPolygon polyPolygon(polygon);
1192 polyPolygon.transform(maMapTransform);
1193 if (type == EmfPlusRecordTypeFillPie)
1194 EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor);
1195 else
1196 EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
1198 break;
1199 case EmfPlusRecordTypeFillPath:
1201 sal_uInt32 index = flags & 0xff;
1202 sal_uInt32 brushIndexOrColor;
1203 rMS.ReadUInt32(brushIndexOrColor);
1204 SAL_INFO("drawinglayer.emf", "EMF+ FillPath slot: " << index);
1206 EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[index].get());
1207 if (path)
1208 EMFPPlusFillPolygon(path->GetPolygon(*this), flags & 0x8000, brushIndexOrColor);
1209 else
1210 SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillPath missing path");
1212 break;
1213 case EmfPlusRecordTypeFillRegion:
1215 sal_uInt32 index = flags & 0xff;
1216 sal_uInt32 brushIndexOrColor;
1217 rMS.ReadUInt32(brushIndexOrColor);
1218 SAL_INFO("drawinglayer.emf", "EMF+\t FillRegion slot: " << index);
1220 EMFPRegion* region = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
1221 if (region)
1222 EMFPPlusFillPolygon(region->regionPolyPolygon, flags & 0x8000, brushIndexOrColor);
1223 else
1224 SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillRegion missing region");
1226 break;
1227 case EmfPlusRecordTypeDrawEllipse:
1228 case EmfPlusRecordTypeFillEllipse:
1230 // Intentionally very bogus initial value to avoid MSVC complaining about potentially uninitialized local
1231 // variable. As long as the code stays as intended, this variable will be assigned a (real) value in the case
1232 // when it is later used.
1233 sal_uInt32 brushIndexOrColor = 1234567;
1235 if (type == EmfPlusRecordTypeFillEllipse)
1237 rMS.ReadUInt32(brushIndexOrColor);
1240 SAL_INFO("drawinglayer.emf", "EMF+\t " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff));
1241 float dx, dy, dw, dh;
1242 ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
1243 SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
1244 ::basegfx::B2DPolyPolygon polyPolygon(
1245 ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(dx + 0.5 * dw, dy + 0.5 * dh),
1246 0.5 * dw, 0.5 * dh));
1247 polyPolygon.transform(maMapTransform);
1248 if (type == EmfPlusRecordTypeFillEllipse)
1249 EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor);
1250 else
1251 EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
1253 break;
1254 case EmfPlusRecordTypeFillRects:
1255 case EmfPlusRecordTypeDrawRects:
1257 // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
1258 sal_uInt32 brushIndexOrColor = 999;
1259 ::basegfx::B2DPolyPolygon polyPolygon;
1260 sal_uInt32 rectangles;
1261 float x, y, width, height;
1262 const bool isColor = (flags & 0x8000);
1263 ::basegfx::B2DPolygon polygon;
1265 if (EmfPlusRecordTypeFillRects == type)
1267 SAL_INFO("drawinglayer.emf", "EMF+\t FillRects");
1268 rMS.ReadUInt32(brushIndexOrColor);
1269 SAL_INFO("drawinglayer.emf", "EMF+\t" << (isColor ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
1271 else
1273 SAL_INFO("drawinglayer.emf", "EMF+\t DrawRects");
1276 rMS.ReadUInt32(rectangles);
1277 for (sal_uInt32 i = 0; i < rectangles; i++)
1279 ReadRectangle(rMS, x, y, width, height, bool(flags & 0x4000));
1280 polygon.clear();
1281 polygon.append(Map(x, y));
1282 polygon.append(Map(x + width, y));
1283 polygon.append(Map(x + width, y + height));
1284 polygon.append(Map(x, y + height));
1285 polygon.setClosed(true);
1287 SAL_INFO("drawinglayer.emf", "EMF+\t\t rectangle: " << x << ", "<< y << " " << width << "x" << height);
1288 polyPolygon.append(polygon);
1290 if (type == EmfPlusRecordTypeFillRects)
1291 EMFPPlusFillPolygon(polyPolygon, isColor, brushIndexOrColor);
1292 else
1293 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::B2DPolygon aPolygon;
1344 rMS.ReadUInt32(aCount);
1345 SAL_INFO("drawinglayer.emf", "EMF+\t DrawBeziers slot: " << (flags & 0xff));
1346 SAL_INFO("drawinglayer.emf", "EMF+\t Number of points: " << aCount);
1347 SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer.emf",
1348 "EMF+\t Bezier Draw not support number of points other than 4, 7, "
1349 "10, 13, 16...");
1351 if (aCount < 4)
1353 SAL_WARN("drawinglayer.emf", "EMF+\t Bezier Draw does not support less "
1354 "than 4 points. Number of points: "
1355 << aCount);
1356 break;
1359 ReadPoint(rMS, x1, y1, flags);
1360 // We need to add first starting point
1361 aPolygon.append(Map(x1, y1));
1362 SAL_INFO("drawinglayer.emf",
1363 "EMF+\t Bezier starting point: " << x1 << "," << y1);
1364 for (sal_uInt32 i = 4; i <= aCount; i += 3)
1366 ReadPoint(rMS, x2, y2, flags);
1367 ReadPoint(rMS, x3, y3, flags);
1368 ReadPoint(rMS, x4, y4, flags);
1370 SAL_INFO("drawinglayer.emf",
1371 "EMF+\t Bezier points: " << x2 << "," << y2 << " " << x3 << ","
1372 << y3 << " " << x4 << "," << y4);
1373 aPolygon.appendBezierSegment(Map(x2, y2), Map(x3, y3), Map(x4, y4));
1375 EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff);
1376 break;
1378 case EmfPlusRecordTypeDrawCurve:
1380 sal_uInt32 aOffset, aNumSegments, points;
1381 float aTension;
1382 rMS.ReadFloat(aTension);
1383 rMS.ReadUInt32(aOffset);
1384 rMS.ReadUInt32(aNumSegments);
1385 rMS.ReadUInt32(points);
1386 SAL_WARN("drawinglayer.emf",
1387 "EMF+\t Tension: " << aTension << " Offset: " << aOffset
1388 << " NumSegments: " << aNumSegments
1389 << " Points: " << points);
1391 EMFPPath path(points, true);
1392 path.Read(rMS, flags);
1394 if (points >= 2)
1395 EMFPPlusDrawPolygon(
1396 path.GetCardinalSpline(*this, aTension, aOffset, aNumSegments),
1397 flags & 0xff);
1398 else
1399 SAL_WARN("drawinglayer.emf", "Not enough number of points");
1400 break;
1402 case EmfPlusRecordTypeDrawClosedCurve:
1403 case EmfPlusRecordTypeFillClosedCurve:
1405 // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
1406 sal_uInt32 brushIndexOrColor = 999, points;
1407 float aTension;
1408 if (type == EmfPlusRecordTypeFillClosedCurve)
1410 rMS.ReadUInt32(brushIndexOrColor);
1411 SAL_INFO(
1412 "drawinglayer.emf",
1413 "EMF+\t Fill Mode: " << (flags & 0x2000 ? "Winding" : "Alternate"));
1415 rMS.ReadFloat(aTension);
1416 rMS.ReadUInt32(points);
1417 SAL_WARN("drawinglayer.emf",
1418 "EMF+\t Tension: " << aTension << " Points: " << points);
1419 SAL_INFO("drawinglayer.emf",
1420 "EMF+\t " << (flags & 0x8000 ? "Color" : "Brush index") << " : 0x"
1421 << std::hex << brushIndexOrColor << std::dec);
1422 if (points < 3)
1424 SAL_WARN("drawinglayer.emf", "Not enough number of points");
1425 break;
1427 EMFPPath path(points, true);
1428 path.Read(rMS, flags);
1429 if (type == EmfPlusRecordTypeFillClosedCurve)
1430 EMFPPlusFillPolygon(path.GetClosedCardinalSpline(*this, aTension),
1431 flags & 0x8000, brushIndexOrColor);
1432 else
1433 EMFPPlusDrawPolygon(path.GetClosedCardinalSpline(*this, aTension),
1434 flags & 0xff);
1435 break;
1437 case EmfPlusRecordTypeDrawImage:
1438 case EmfPlusRecordTypeDrawImagePoints:
1440 sal_uInt32 imageAttributesId;
1441 sal_Int32 sourceUnit;
1442 rMS.ReadUInt32(imageAttributesId).ReadInt32(sourceUnit);
1443 SAL_INFO("drawinglayer.emf",
1444 "EMF+\t " << (type == EmfPlusRecordTypeDrawImage ? "DrawImage"
1445 : "DrawImagePoints")
1446 << " image attributes Id: " << imageAttributesId
1447 << " source unit: " << sourceUnit);
1448 SAL_INFO("drawinglayer.emf", "EMF+\t TODO: use image attributes");
1450 // Source unit of measurement type must be 1 pixel
1451 if (EMFPImage* image = sourceUnit == UnitTypePixel ?
1452 dynamic_cast<EMFPImage*>(maEMFPObjects[flags & 0xff].get()) :
1453 nullptr)
1455 float sx, sy, sw, sh;
1456 ReadRectangle(rMS, sx, sy, sw, sh);
1458 ::tools::Rectangle aSource(Point(sx, sy), Size(sw + 1, sh + 1));
1459 SAL_INFO("drawinglayer.emf",
1460 "EMF+\t "
1461 << (type == EmfPlusRecordTypeDrawImage ? "DrawImage"
1462 : "DrawImagePoints")
1463 << " source rectangle: " << sx << "," << sy << " " << sw << "x"
1464 << sh);
1466 float dx(0.), dy(0.), dw(0.), dh(0.);
1467 double fShearX = 0.0;
1468 double fShearY = 0.0;
1469 if (type == EmfPlusRecordTypeDrawImagePoints)
1471 sal_uInt32 aCount;
1472 rMS.ReadUInt32(aCount);
1474 // Number of points used by DrawImagePoints. Exactly 3 points must be specified.
1475 if (aCount != 3)
1477 SAL_WARN("drawinglayer.emf", "EMF+\t Wrong EMF+ file. Expected "
1478 "3 points, received: "
1479 << aCount);
1480 break;
1482 float x1, y1, x2, y2, x3, y3;
1484 ReadPoint(rMS, x1, y1, flags); // upper-left point
1485 ReadPoint(rMS, x2, y2, flags); // upper-right
1486 ReadPoint(rMS, x3, y3, flags); // lower-left
1488 SAL_INFO("drawinglayer.emf", "EMF+\t destination points: "
1489 << x1 << "," << y1 << " " << x2 << ","
1490 << y2 << " " << x3 << "," << y3);
1491 dx = x1;
1492 dy = y2;
1493 dw = x2 - x1;
1494 dh = y3 - y1;
1495 fShearX = x3 - x1;
1496 fShearY = y2 - y1;
1498 else if (type == EmfPlusRecordTypeDrawImage)
1499 ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
1501 SAL_INFO("drawinglayer.emf",
1502 "EMF+\t Rectangle: " << dx << "," << dy << " " << dw << "x" << dh);
1503 Size aSize;
1504 if (image->type == ImageDataTypeBitmap)
1506 aSize = image->graphic.GetBitmapEx().GetSizePixel();
1507 SAL_INFO("drawinglayer.emf", "EMF+\t Bitmap size: " << aSize.Width()
1508 << "x"
1509 << aSize.Height());
1510 if (sx < 0)
1512 // If src position is negative then we need shift image to right
1513 dx = dx + ((-sx) / sw) * dw;
1514 if (sx + sw <= aSize.Width())
1515 dw = ((sw + sx) / sw) * dw;
1516 else
1517 dw = (aSize.Width() / sw) * dw;
1519 else if (sx + sw > aSize.Width())
1520 // If the src image is smaller that what we want to cut, then we need to scale down
1521 dw = ((aSize.Width() - sx) / sw) * dw;
1523 if (sy < 0)
1525 dy = dy + ((-sy) / sh) * dh;
1526 if (sy + sh <= aSize.Height())
1527 dh = ((sh + sy) / sh) * dh;
1528 else
1529 dh = (aSize.Height() / sh) * dh;
1531 else if (sy + sh > aSize.Height())
1532 dh = ((aSize.Height() - sy) / sh) * dh;
1534 else
1535 SAL_INFO(
1536 "drawinglayer.emf",
1537 "EMF+\t TODO: Add support for SrcRect to ImageDataTypeMetafile");
1538 const ::basegfx::B2DPoint aDstPoint(dx, dy);
1539 const ::basegfx::B2DSize aDstSize(dw, dh);
1541 const basegfx::B2DHomMatrix aTransformMatrix
1542 = maMapTransform
1543 * basegfx::B2DHomMatrix(
1544 /* Row 0, Column 0 */ aDstSize.getWidth(),
1545 /* Row 0, Column 1 */ fShearX,
1546 /* Row 0, Column 2 */ aDstPoint.getX(),
1547 /* Row 1, Column 0 */ fShearY,
1548 /* Row 1, Column 1 */ aDstSize.getHeight(),
1549 /* Row 1, Column 2 */ aDstPoint.getY());
1551 if (image->type == ImageDataTypeBitmap)
1553 BitmapEx aBmp(image->graphic.GetBitmapEx());
1554 aBmp.Crop(aSource);
1555 aSize = aBmp.GetSizePixel();
1556 if (aSize.Width() > 0 && aSize.Height() > 0)
1558 mrTargetHolders.Current().append(
1559 new drawinglayer::primitive2d::BitmapPrimitive2D(
1560 aBmp, aTransformMatrix));
1562 else
1563 SAL_WARN("drawinglayer.emf", "EMF+\t warning: empty bitmap");
1565 else if (image->type == ImageDataTypeMetafile)
1567 GDIMetaFile aGDI(image->graphic.GetGDIMetaFile());
1568 aGDI.Clip(aSource);
1569 mrTargetHolders.Current().append(
1570 new drawinglayer::primitive2d::MetafilePrimitive2D(aTransformMatrix,
1571 aGDI));
1574 else
1576 SAL_WARN("drawinglayer.emf",
1577 "EMF+\tDrawImage(Points) Wrong EMF+ file. Only Unit Type Pixel is "
1578 "support by EMF+ specification for DrawImage(Points)");
1580 break;
1582 case EmfPlusRecordTypeDrawString:
1584 sal_uInt32 brushId, formatId, stringLength;
1585 rMS.ReadUInt32(brushId).ReadUInt32(formatId).ReadUInt32(stringLength);
1586 SAL_INFO("drawinglayer.emf", "EMF+\t FontId: " << OUString::number(flags & 0xFF));
1587 SAL_INFO("drawinglayer.emf", "EMF+\t BrushId: " << BrushIDToString(flags, brushId));
1588 SAL_INFO("drawinglayer.emf", "EMF+\t FormatId: " << formatId);
1589 SAL_INFO("drawinglayer.emf", "EMF+\t Length: " << stringLength);
1591 // read the layout rectangle
1592 float lx, ly, lw, lh;
1593 rMS.ReadFloat(lx).ReadFloat(ly).ReadFloat(lw).ReadFloat(lh);
1595 SAL_INFO("drawinglayer.emf", "EMF+\t DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh);
1596 // parse the string
1597 const OUString text = read_uInt16s_ToOUString(rMS, stringLength);
1598 SAL_INFO("drawinglayer.emf", "EMF+\t DrawString string: " << text);
1599 // get the stringFormat from the Object table ( this is OPTIONAL and may be nullptr )
1600 const EMFPStringFormat *stringFormat = dynamic_cast<EMFPStringFormat*>(maEMFPObjects[formatId & 0xff].get());
1601 // get the font from the flags
1602 const EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get());
1603 if (!font)
1605 break;
1607 mrPropertyHolders.Current().setFont(vcl::Font(font->family, Size(font->emSize, font->emSize)));
1609 drawinglayer::attribute::FontAttribute fontAttribute(
1610 font->family, // font family
1611 "", // (no) font style
1612 font->Bold() ? 8u : 1u, // weight: 8 = bold
1613 font->family == "SYMBOL", // symbol
1614 stringFormat && stringFormat->DirectionVertical(), // vertical
1615 font->Italic(), // italic
1616 false, // monospaced
1617 false, // outline = false, no such thing in MS-EMFPLUS
1618 stringFormat && stringFormat->DirectionRightToLeft(), // right-to-left
1619 false); // BiDiStrong
1621 css::lang::Locale locale;
1622 double stringAlignmentHorizontalOffset = 0.0;
1623 double stringAlignmentVerticalOffset = font->emSize;
1624 if (stringFormat)
1626 LanguageTag aLanguageTag(static_cast<LanguageType>(stringFormat->language));
1627 locale = aLanguageTag.getLocale();
1628 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
1630 aTextLayouter.setFontAttribute(fontAttribute, font->emSize,
1631 font->emSize, locale);
1633 double fTextWidth = aTextLayouter.getTextWidth(text, 0, stringLength);
1634 SAL_WARN_IF(stringFormat->DirectionRightToLeft(), "drawinglayer.emf",
1635 "EMF+\t DrawString Alignment TODO For a right-to-left layout rectangle, the origin should be at the upper right.");
1636 if (stringFormat->stringAlignment == StringAlignmentNear)
1637 // Alignment is to the left side of the layout rectangle (lx, ly, lw, lh)
1638 stringAlignmentHorizontalOffset = stringFormat->leadingMargin * font->emSize;
1639 else if (stringFormat->stringAlignment == StringAlignmentCenter)
1640 // Alignment is centered between the origin and extent of the layout rectangle
1641 stringAlignmentHorizontalOffset = 0.5 * lw + (stringFormat->leadingMargin - stringFormat->trailingMargin) * font->emSize - 0.5 * fTextWidth;
1642 else if (stringFormat->stringAlignment == StringAlignmentFar)
1643 // Alignment is to the right side of the layout rectangle
1644 stringAlignmentHorizontalOffset = lw - stringFormat->trailingMargin * font->emSize - fTextWidth;
1646 if (stringFormat->lineAlign == StringAlignmentNear)
1647 stringAlignmentVerticalOffset = font->emSize;
1648 else if (stringFormat->lineAlign == StringAlignmentCenter)
1649 stringAlignmentVerticalOffset = 0.5 * lh + 0.5 * font->emSize;
1650 else if (stringFormat->lineAlign == StringAlignmentFar)
1651 stringAlignmentVerticalOffset = lh;
1653 else
1655 // By default LeadingMargin is 1/6 inch
1656 // TODO for typographic fonts set value to 0.
1657 stringAlignmentHorizontalOffset = 16.0;
1659 // use system default
1660 locale = Application::GetSettings().GetLanguageTag().getLocale();
1663 const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
1664 ::basegfx::B2DVector(font->emSize, font->emSize),
1665 ::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset,
1666 ly + stringAlignmentVerticalOffset));
1668 Color uncorrectedColor = EMFPGetBrushColorOrARGBColor(flags, brushId);
1669 Color color;
1671 if (mbSetTextContrast)
1673 const auto gammaVal = mnTextContrast / 1000;
1674 const basegfx::BColorModifier_gamma gamma(gammaVal);
1676 // gamma correct transparency color
1677 sal_uInt16 alpha = uncorrectedColor.GetAlpha();
1678 alpha = std::clamp(std::pow(alpha, 1.0 / gammaVal), 0.0, 1.0) * 255;
1680 basegfx::BColor modifiedColor(gamma.getModifiedColor(uncorrectedColor.getBColor()));
1681 color.SetRed(modifiedColor.getRed() * 255);
1682 color.SetGreen(modifiedColor.getGreen() * 255);
1683 color.SetBlue(modifiedColor.getBlue() * 255);
1684 color.SetAlpha(alpha);
1686 else
1688 color = uncorrectedColor;
1691 mrPropertyHolders.Current().setTextColor(color.getBColor());
1692 mrPropertyHolders.Current().setTextColorActive(true);
1694 if (color.GetAlpha() > 0)
1696 std::vector<double> emptyVector;
1697 rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText;
1698 if (font->Underline() || font->Strikeout())
1700 pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
1701 transformMatrix,
1702 text,
1703 0, // text always starts at 0
1704 stringLength,
1705 std::move(emptyVector), // EMF-PLUS has no DX-array
1707 fontAttribute,
1708 locale,
1709 color.getBColor(), // Font Color
1710 COL_TRANSPARENT, // Fill Color
1711 color.getBColor(), // OverlineColor
1712 color.getBColor(), // TextlineColor
1713 drawinglayer::primitive2d::TEXT_LINE_NONE,
1714 font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
1715 false,
1716 font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE);
1718 else
1720 pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
1721 transformMatrix,
1722 text,
1723 0, // text always starts at 0
1724 stringLength,
1725 std::move(emptyVector), // EMF-PLUS has no DX-array
1727 fontAttribute,
1728 locale,
1729 color.getBColor());
1731 drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText);
1732 if (color.IsTransparent())
1734 aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
1735 drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText },
1736 (255 - color.GetAlpha()) / 255.0);
1739 mrTargetHolders.Current().append(
1740 new drawinglayer::primitive2d::TransformPrimitive2D(
1741 maMapTransform,
1742 drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } ));
1744 break;
1746 case EmfPlusRecordTypeSetPageTransform:
1748 rMS.ReadFloat(mfPageScale);
1749 SAL_INFO("drawinglayer.emf", "EMF+\t Scale: " << mfPageScale << " unit: " << UnitTypeToString(flags));
1751 if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld))
1753 SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by SetPageTransform in EMF+ specification.");
1755 else
1757 mnMmX *= mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnHDPI);
1758 mnMmY *= mfPageScale * getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnVDPI);
1759 mappingChanged();
1761 break;
1763 case EmfPlusRecordTypeSetRenderingOrigin:
1765 rMS.ReadInt32(mnOriginX).ReadInt32(mnOriginY);
1766 SAL_INFO("drawinglayer.emf", "EMF+\t SetRenderingOrigin, [x,y]: " << mnOriginX << "," << mnOriginY);
1767 break;
1769 case EmfPlusRecordTypeSetTextContrast:
1771 const sal_uInt16 LOWERGAMMA = 1000;
1772 const sal_uInt16 UPPERGAMMA = 2200;
1774 mbSetTextContrast = true;
1775 mnTextContrast = flags & 0xFFF;
1776 SAL_WARN_IF(mnTextContrast > UPPERGAMMA || mnTextContrast < LOWERGAMMA,
1777 "drawinglayer.emf", "EMF+\t Gamma value is not with bounds 1000 to 2200, value is " << mnTextContrast);
1778 mnTextContrast = std::min(mnTextContrast, UPPERGAMMA);
1779 mnTextContrast = std::max(mnTextContrast, LOWERGAMMA);
1780 SAL_INFO("drawinglayer.emf", "EMF+\t Text contrast: " << (mnTextContrast / 1000) << " gamma");
1781 break;
1783 case EmfPlusRecordTypeSetTextRenderingHint:
1785 sal_uInt8 nTextRenderingHint = (flags & 0xFF) >> 1;
1786 SAL_INFO("drawinglayer.emf", "EMF+\t Text rendering hint: " << TextRenderingHintToString(nTextRenderingHint));
1787 SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetTextRenderingHint");
1788 break;
1790 case EmfPlusRecordTypeSetAntiAliasMode:
1792 bool bUseAntiAlias = (flags & 0x0001);
1793 sal_uInt8 nSmoothingMode = (flags & 0xFE00) >> 1;
1794 SAL_INFO("drawinglayer.emf", "EMF+\t Antialiasing: " << (bUseAntiAlias ? "enabled" : "disabled"));
1795 SAL_INFO("drawinglayer.emf", "EMF+\t Smoothing mode: " << SmoothingModeToString(nSmoothingMode));
1796 SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetAntiAliasMode");
1797 break;
1799 case EmfPlusRecordTypeSetInterpolationMode:
1801 sal_uInt16 nInterpolationMode = flags & 0xFF;
1802 SAL_INFO("drawinglayer.emf", "EMF+\t Interpolation mode: " << InterpolationModeToString(nInterpolationMode));
1803 SAL_WARN("drawinglayer.emf", "EMF+\t TODO InterpolationMode");
1804 break;
1806 case EmfPlusRecordTypeSetPixelOffsetMode:
1808 SAL_INFO("drawinglayer.emf", "EMF+\t Pixel offset mode: " << PixelOffsetModeToString(flags));
1809 SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetPixelOffsetMode");
1810 break;
1812 case EmfPlusRecordTypeSetCompositingQuality:
1814 SAL_INFO("drawinglayer.emf", "EMF+\t TODO SetCompositingQuality");
1815 break;
1817 case EmfPlusRecordTypeSave:
1819 sal_uInt32 stackIndex;
1820 rMS.ReadUInt32(stackIndex);
1821 SAL_INFO("drawinglayer.emf", "EMF+\t Save stack index: " << stackIndex);
1823 GraphicStatePush(mGSStack, stackIndex);
1825 break;
1827 case EmfPlusRecordTypeRestore:
1829 sal_uInt32 stackIndex;
1830 rMS.ReadUInt32(stackIndex);
1831 SAL_INFO("drawinglayer.emf", "EMF+\t Restore stack index: " << stackIndex);
1833 GraphicStatePop(mGSStack, stackIndex);
1834 break;
1836 case EmfPlusRecordTypeBeginContainer:
1838 float dx, dy, dw, dh;
1839 ReadRectangle(rMS, dx, dy, dw, dh);
1840 SAL_INFO("drawinglayer.emf", "EMF+\t Dest RectData: " << dx << "," << dy << " " << dw << "x" << dh);
1842 float sx, sy, sw, sh;
1843 ReadRectangle(rMS, sx, sy, sw, sh);
1844 SAL_INFO("drawinglayer.emf", "EMF+\t Source RectData: " << sx << "," << sy << " " << sw << "x" << sh);
1846 sal_uInt32 stackIndex;
1847 rMS.ReadUInt32(stackIndex);
1848 SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container stack index: " << stackIndex << ", PageUnit: " << flags);
1850 if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld))
1852 SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by BeginContainer in EMF+ specification.");
1853 break;
1855 const float aPageScaleX = getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnHDPI);
1856 const float aPageScaleY = getUnitToPixelMultiplier(static_cast<UnitType>(flags), mnVDPI);
1857 GraphicStatePush(mGSContainerStack, stackIndex);
1858 const basegfx::B2DHomMatrix transform = basegfx::utils::createScaleTranslateB2DHomMatrix(
1859 aPageScaleX * ( dw / sw ), aPageScaleY * ( dh / sh ),
1860 aPageScaleX * ( dx - sx ), aPageScaleY * ( dy - sy) );
1861 maWorldTransform *= transform;
1862 mappingChanged();
1863 break;
1865 case EmfPlusRecordTypeBeginContainerNoParams:
1867 sal_uInt32 stackIndex;
1868 rMS.ReadUInt32(stackIndex);
1869 SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container No Params stack index: " << stackIndex);
1871 GraphicStatePush(mGSContainerStack, stackIndex);
1872 break;
1874 case EmfPlusRecordTypeEndContainer:
1876 sal_uInt32 stackIndex;
1877 rMS.ReadUInt32(stackIndex);
1878 SAL_INFO("drawinglayer.emf", "EMF+\t End Container stack index: " << stackIndex);
1880 GraphicStatePop(mGSContainerStack, stackIndex);
1881 break;
1883 case EmfPlusRecordTypeSetWorldTransform:
1885 SAL_INFO("drawinglayer.emf", "EMF+\t SetWorldTransform, Post multiply: " << bool(flags & 0x2000));
1886 readXForm(rMS, maWorldTransform);
1887 mappingChanged();
1888 SAL_INFO("drawinglayer.emf", "EMF+\t\t: " << maWorldTransform);
1889 break;
1891 case EmfPlusRecordTypeResetWorldTransform:
1893 maWorldTransform.identity();
1894 SAL_INFO("drawinglayer.emf", "EMF+\t World transform: " << maWorldTransform);
1895 mappingChanged();
1896 break;
1898 case EmfPlusRecordTypeMultiplyWorldTransform:
1900 SAL_INFO("drawinglayer.emf", "EMF+\t MultiplyWorldTransform, post multiply: " << bool(flags & 0x2000));
1901 basegfx::B2DHomMatrix transform;
1902 readXForm(rMS, transform);
1904 SAL_INFO("drawinglayer.emf",
1905 "EMF+\t Transform matrix: " << transform);
1907 if (flags & 0x2000)
1909 // post multiply
1910 maWorldTransform *= transform;
1912 else
1914 // pre multiply
1915 transform *= maWorldTransform;
1916 maWorldTransform = transform;
1919 mappingChanged();
1921 SAL_INFO("drawinglayer.emf",
1922 "EMF+\t World transform matrix: " << maWorldTransform);
1923 break;
1925 case EmfPlusRecordTypeTranslateWorldTransform:
1927 SAL_INFO("drawinglayer.emf", "EMF+\t TranslateWorldTransform, Post multiply: " << bool(flags & 0x2000));
1929 basegfx::B2DHomMatrix transform;
1930 float eDx, eDy;
1931 rMS.ReadFloat(eDx).ReadFloat(eDy);
1932 transform.set(0, 2, eDx);
1933 transform.set(1, 2, eDy);
1935 SAL_INFO("drawinglayer.emf",
1936 "EMF+\t Translate matrix: " << transform);
1938 if (flags & 0x2000)
1940 // post multiply
1941 maWorldTransform *= transform;
1943 else
1945 // pre multiply
1946 transform *= maWorldTransform;
1947 maWorldTransform = transform;
1950 mappingChanged();
1952 SAL_INFO("drawinglayer.emf",
1953 "EMF+\t World transform matrix: " << maWorldTransform);
1954 break;
1956 case EmfPlusRecordTypeScaleWorldTransform:
1958 basegfx::B2DHomMatrix transform;
1959 float eSx, eSy;
1960 rMS.ReadFloat(eSx).ReadFloat(eSy);
1961 transform.set(0, 0, eSx);
1962 transform.set(1, 1, eSy);
1964 SAL_INFO("drawinglayer.emf", "EMF+\t ScaleWorldTransform Sx: " << eSx <<
1965 " Sy: " << eSy << ", Post multiply:" << bool(flags & 0x2000));
1966 SAL_INFO("drawinglayer.emf",
1967 "EMF+\t World transform matrix: " << maWorldTransform);
1969 if (flags & 0x2000)
1971 // post multiply
1972 maWorldTransform *= transform;
1974 else
1976 // pre multiply
1977 transform *= maWorldTransform;
1978 maWorldTransform = transform;
1981 mappingChanged();
1983 SAL_INFO("drawinglayer.emf",
1984 "EMF+\t World transform matrix: " << maWorldTransform);
1985 break;
1987 case EmfPlusRecordTypeRotateWorldTransform:
1989 // Angle of rotation in degrees
1990 float eAngle;
1991 rMS.ReadFloat(eAngle);
1993 SAL_INFO("drawinglayer.emf", "EMF+\t RotateWorldTransform Angle: " << eAngle <<
1994 ", post multiply: " << bool(flags & 0x2000));
1995 // Skipping flags & 0x2000
1996 // For rotation transformation there is no difference between post and pre multiply
1997 maWorldTransform.rotate(basegfx::deg2rad(eAngle));
1998 mappingChanged();
2000 SAL_INFO("drawinglayer.emf",
2001 "EMF+\t " << maWorldTransform);
2002 break;
2004 case EmfPlusRecordTypeResetClip:
2006 SAL_INFO("drawinglayer.emf", "EMF+ ResetClip");
2007 // We don't need to read anything more, as Size needs to be set 0x0000000C
2008 // and DataSize must be set to 0.
2010 // Resets the current clipping region for the world space to infinity.
2011 HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, mrPropertyHolders);
2012 break;
2014 case EmfPlusRecordTypeSetClipRect:
2015 case EmfPlusRecordTypeSetClipPath:
2016 case EmfPlusRecordTypeSetClipRegion:
2018 int combineMode = (flags >> 8) & 0xf;
2019 ::basegfx::B2DPolyPolygon polyPolygon;
2020 if (type == EmfPlusRecordTypeSetClipRect)
2022 SAL_INFO("drawinglayer.emf", "EMF+\t SetClipRect");
2024 float dx, dy, dw, dh;
2025 ReadRectangle(rMS, dx, dy, dw, dh);
2026 SAL_INFO("drawinglayer.emf",
2027 "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
2028 ::basegfx::B2DPoint mappedPoint1(Map(dx, dy));
2029 ::basegfx::B2DPoint mappedPoint2(Map(dx + dw, dy + dh));
2031 polyPolygon
2032 = ::basegfx::B2DPolyPolygon(::basegfx::utils::createPolygonFromRect(
2033 ::basegfx::B2DRectangle(mappedPoint1.getX(), mappedPoint1.getY(),
2034 mappedPoint2.getX(), mappedPoint2.getY())));
2036 else if (type == EmfPlusRecordTypeSetClipPath)
2038 SAL_INFO("drawinglayer.emf", "EMF+\tSetClipPath " << (flags & 0xff));
2040 EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get());
2041 if (!path)
2043 SAL_WARN("drawinglayer.emf",
2044 "EMF+\t TODO Unable to find path in slot: " << (flags & 0xff));
2045 break;
2047 polyPolygon = path->GetPolygon(*this);
2049 else if (type == EmfPlusRecordTypeSetClipRegion)
2051 SAL_INFO("drawinglayer.emf", "EMF+\t Region in slot: " << (flags & 0xff));
2052 EMFPRegion* region
2053 = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
2054 if (!region)
2056 SAL_WARN(
2057 "drawinglayer.emf",
2058 "EMF+\t TODO Unable to find region in slot: " << (flags & 0xff));
2059 break;
2061 polyPolygon = region->regionPolyPolygon;
2063 SAL_INFO("drawinglayer.emf", "EMF+\t Combine mode: " << combineMode);
2064 ::basegfx::B2DPolyPolygon aClippedPolyPolygon;
2065 if (mrPropertyHolders.Current().getClipPolyPolygonActive())
2067 aClippedPolyPolygon
2068 = combineClip(mrPropertyHolders.Current().getClipPolyPolygon(),
2069 combineMode, polyPolygon);
2071 else
2073 //Combine with infinity
2074 switch (combineMode)
2076 case EmfPlusCombineModeReplace:
2077 case EmfPlusCombineModeIntersect:
2079 aClippedPolyPolygon = polyPolygon;
2080 break;
2082 case EmfPlusCombineModeUnion:
2084 // Disable clipping as the clipping is infinity
2085 aClippedPolyPolygon = ::basegfx::B2DPolyPolygon();
2086 break;
2088 case EmfPlusCombineModeXOR:
2089 case EmfPlusCombineModeComplement:
2091 //TODO It is not correct and it should be fixed
2092 aClippedPolyPolygon = polyPolygon;
2093 break;
2095 case EmfPlusCombineModeExclude:
2097 //TODO It is not correct and it should be fixed
2098 aClippedPolyPolygon = ::basegfx::B2DPolyPolygon();
2099 break;
2103 HandleNewClipRegion(aClippedPolyPolygon, mrTargetHolders, mrPropertyHolders);
2104 break;
2106 case EmfPlusRecordTypeOffsetClip:
2108 float dx, dy;
2109 rMS.ReadFloat(dx).ReadFloat(dy);
2110 SAL_INFO("drawinglayer.emf", "EMF+\tOffset x:" << dx << ", y:" << dy);
2112 basegfx::B2DPolyPolygon aPolyPolygon(
2113 mrPropertyHolders.Current().getClipPolyPolygon());
2115 SAL_INFO("drawinglayer.emf",
2116 "EMF+\t PolyPolygon before translate: " << aPolyPolygon);
2118 basegfx::B2DPoint aOffset = Map(dx, dy);
2119 basegfx::B2DHomMatrix transformMatrix;
2120 transformMatrix.set(0, 2, aOffset.getX());
2121 transformMatrix.set(1, 2, aOffset.getY());
2122 aPolyPolygon.transform(transformMatrix);
2124 SAL_INFO("drawinglayer.emf",
2125 "EMF+\t PolyPolygon after translate: " << aPolyPolygon <<
2126 ", mapped offset x" << aOffset.getX() << ", mapped offset y" << aOffset.getY());
2127 HandleNewClipRegion(aPolyPolygon, mrTargetHolders, mrPropertyHolders);
2128 break;
2130 case EmfPlusRecordTypeDrawDriverString:
2132 sal_uInt32 brushIndexOrColor;
2133 sal_uInt32 optionFlags;
2134 sal_uInt32 hasMatrix;
2135 sal_uInt32 glyphsCount;
2136 rMS.ReadUInt32(brushIndexOrColor).ReadUInt32(optionFlags).ReadUInt32(hasMatrix).ReadUInt32(glyphsCount);
2137 SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
2138 SAL_INFO("drawinglayer.emf", "EMF+\t Option flags: 0x" << std::hex << optionFlags << std::dec);
2139 SAL_INFO("drawinglayer.emf", "EMF+\t Has matrix: " << hasMatrix);
2140 SAL_INFO("drawinglayer.emf", "EMF+\t Glyphs: " << glyphsCount);
2142 if ((optionFlags & 1) && glyphsCount > 0)
2144 std::unique_ptr<float[]> charsPosX(new float[glyphsCount]);
2145 std::unique_ptr<float[]> charsPosY(new float[glyphsCount]);
2146 OUString text = read_uInt16s_ToOUString(rMS, glyphsCount);
2147 SAL_INFO("drawinglayer.emf", "EMF+\t DrawDriverString string: " << text);
2149 for (sal_uInt32 i = 0; i<glyphsCount; i++)
2151 rMS.ReadFloat(charsPosX[i]).ReadFloat(charsPosY[i]);
2152 SAL_INFO("drawinglayer.emf", "EMF+\t\t glyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]);
2155 basegfx::B2DHomMatrix transform;
2157 if (hasMatrix)
2159 readXForm(rMS, transform);
2160 SAL_INFO("drawinglayer.emf", "EMF+\tmatrix: " << transform);
2163 // get the font from the flags
2164 EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get());
2165 if (!font)
2167 break;
2169 // done reading
2171 drawinglayer::attribute::FontAttribute fontAttribute(
2172 font->family, // font family
2173 "", // (no) font style
2174 font->Bold() ? 8u : 1u, // weight: 8 = bold
2175 font->family == "SYMBOL", // symbol
2176 optionFlags & 0x2, // vertical
2177 font->Italic(), // italic
2178 false, // monospaced
2179 false, // outline = false, no such thing in MS-EMFPLUS
2180 false, // right-to-left
2181 false); // BiDiStrong
2183 const Color color = EMFPGetBrushColorOrARGBColor(flags, brushIndexOrColor);
2185 // generate TextSimplePortionPrimitive2Ds or TextDecoratedPortionPrimitive2D
2186 // for all portions of text with the same charsPosY values
2187 sal_uInt32 pos = 0;
2188 while (pos < glyphsCount)
2190 //determine the current length
2191 sal_uInt32 aLength = 1;
2192 while (pos + aLength < glyphsCount && std::abs( charsPosY[pos + aLength] - charsPosY[pos] ) < std::numeric_limits< float >::epsilon())
2193 aLength++;
2195 // generate the DX-Array
2196 std::vector<double> aDXArray;
2197 for (size_t i = 0; i < aLength - 1; i++)
2199 aDXArray.push_back(charsPosX[pos + i + 1] - charsPosX[pos]);
2201 // last entry
2202 aDXArray.push_back(0);
2204 basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
2205 ::basegfx::B2DVector(font->emSize, font->emSize),
2206 ::basegfx::B2DPoint(charsPosX[pos], charsPosY[pos]));
2207 if (hasMatrix)
2208 transformMatrix *= transform;
2209 if (color.GetAlpha() > 0)
2211 rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText;
2212 if (font->Underline() || font->Strikeout())
2214 pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
2215 transformMatrix,
2216 text,
2217 pos, // take character at current pos
2218 aLength, // use determined length
2219 std::move(aDXArray), // generated DXArray
2221 fontAttribute,
2222 Application::GetSettings().GetLanguageTag().getLocale(),
2223 color.getBColor(),
2224 COL_TRANSPARENT,
2225 color.getBColor(),
2226 color.getBColor(),
2227 drawinglayer::primitive2d::TEXT_LINE_NONE,
2228 font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
2229 false,
2230 font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE);
2232 else
2234 pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
2235 transformMatrix,
2236 text,
2237 pos, // take character at current pos
2238 aLength, // use determined length
2239 std::move(aDXArray), // generated DXArray
2241 fontAttribute,
2242 Application::GetSettings().GetLanguageTag().getLocale(),
2243 color.getBColor());
2245 drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText);
2246 if (color.IsTransparent())
2248 aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
2249 drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText },
2250 (255 - color.GetAlpha()) / 255.0);
2252 mrTargetHolders.Current().append(
2253 new drawinglayer::primitive2d::TransformPrimitive2D(
2254 maMapTransform,
2255 drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } ));
2258 // update pos
2259 pos += aLength;
2262 else
2264 SAL_WARN("drawinglayer.emf", "EMF+\tTODO: fonts (non-unicode glyphs chars)");
2266 break;
2268 default:
2270 SAL_WARN("drawinglayer.emf", "EMF+ TODO unhandled record type: 0x" << std::hex << type << std::dec);
2275 rMS.Seek(next);
2277 if (size <= length)
2279 length -= size;
2281 else
2283 SAL_WARN("drawinglayer.emf", "ImplRenderer::processEMFPlus: "
2284 "size " << size << " > length " << length);
2285 length = 0;
2291 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */