1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
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>
58 namespace emfplushelper
63 WrapModeTile
= 0x00000000,
64 WrapModeTileFlipX
= 0x00000001,
65 WrapModeTileFlipY
= 0x00000002,
66 WrapModeTileFlipXY
= 0x00000003,
67 WrapModeClamp
= 0x00000004
70 const char* emfTypeToName(sal_uInt16 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";
126 static OUString
emfObjectToName(sal_uInt16 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";
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";
156 static OUString
SmoothingModeToString(sal_uInt16 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";
170 static OUString
TextRenderingHintToString(sal_uInt16 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";
184 static OUString
InterpolationModeToString(sal_uInt16 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";
200 OUString
UnitTypeToString(sal_uInt16 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";
215 static bool IsBrush(sal_uInt16 flags
)
217 return (!((flags
>> 15) & 0x0001));
220 static OUString
BrushIDToString(sal_uInt16 flags
, sal_uInt32 brushid
)
223 return "EmfPlusBrush ID: " + OUString::number(brushid
);
225 return "ARGB: 0x" + OUString::number(brushid
, 16);
228 EMFPObject::~EMFPObject()
232 float EmfPlusHelperData::getUnitToPixelMultiplier(const UnitType aUnitType
, const sal_uInt32 aDPI
)
245 case UnitTypeMillimeter
:
248 case UnitTypeDocument
:
252 case UnitTypeDisplay
:
253 SAL_WARN("drawinglayer.emf", "EMF+\t Converting to World/Display.");
257 SAL_WARN("drawinglayer.emf", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex
<< aUnitType
);
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));
272 case EmfPlusObjectTypeBrush
:
274 EMFPBrush
*brush
= new EMFPBrush();
275 maEMFPObjects
[index
].reset(brush
);
276 brush
->Read(rObjectStream
, *this);
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
);
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
);
300 case EmfPlusObjectTypeRegion
:
302 EMFPRegion
*region
= new EMFPRegion();
303 maEMFPObjects
[index
].reset(region
);
304 region
->ReadRegion(rObjectStream
, *this);
307 case EmfPlusObjectTypeImage
:
309 EMFPImage
*image
= new EMFPImage
;
310 maEMFPObjects
[index
].reset(image
);
315 image
->pixelFormat
= 0;
316 image
->Read(rObjectStream
, dataSize
, bUseWholeStream
);
319 case EmfPlusObjectTypeFont
:
321 EMFPFont
*font
= new EMFPFont
;
322 maEMFPObjects
[index
].reset(font
);
326 font
->Read(rObjectStream
);
327 // tdf#113624 Convert unit to Pixels
328 font
->emSize
= font
->emSize
* getUnitToPixelMultiplier(static_cast<UnitType
>(font
->sizeUnit
), mnHDPI
);
332 case EmfPlusObjectTypeStringFormat
:
334 EMFPStringFormat
*stringFormat
= new EMFPStringFormat();
335 maEMFPObjects
[index
].reset(stringFormat
);
336 stringFormat
->Read(rObjectStream
);
339 case EmfPlusObjectTypeImageAttributes
:
341 EMFPImageAttributes
*imageAttributes
= new EMFPImageAttributes();
342 maEMFPObjects
[index
].reset(imageAttributes
);
343 imageAttributes
->Read(rObjectStream
);
346 case EmfPlusObjectTypeCustomLineCap
:
348 SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object type 'custom line cap' not yet implemented");
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
)
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");
372 s
.ReadInt16(ix
).ReadInt16(iy
);
379 s
.ReadFloat(x
).ReadFloat(y
);
383 void EmfPlusHelperData::ReadRectangle(SvStream
& s
, float& x
, float& y
, float &width
, float& height
, bool bCompressed
)
387 sal_Int16 ix
, iy
, iw
, ih
;
389 s
.ReadInt16(ix
).ReadInt16(iy
).ReadInt16(iw
).ReadInt16(ih
);
398 s
.ReadFloat(x
).ReadFloat(y
).ReadFloat(width
).ReadFloat(height
);
402 bool EmfPlusHelperData::readXForm(SvStream
& rIn
, basegfx::B2DHomMatrix
& rTarget
)
406 if (sizeof(float) != 4)
408 OSL_FAIL("EnhWMFReader::sizeof( float ) != 4");
419 rIn
.ReadFloat(eM11
).ReadFloat(eM12
).ReadFloat(eM21
).ReadFloat(eM22
).ReadFloat(eDx
).ReadFloat(eDy
);
420 rTarget
= basegfx::B2DHomMatrix(
428 void EmfPlusHelperData::mappingChanged()
430 if (mnPixX
== 0 || mnPixY
== 0)
432 SAL_WARN("drawinglayer.emf", "dimensions in pixels is 0");
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
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 {
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());
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");
481 void EmfPlusHelperData::GraphicStatePush(GraphicStateMap
& map
, sal_Int32 index
)
483 GraphicStateMap::iterator iter
= map
.find( index
);
485 if ( iter
!= map
.end() )
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
,
515 SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping");
516 wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders
,
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},
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
,
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()))
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);
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);
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
))));
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));
653 if (aStart
.isDefault() && aEnd
.isDefault())
654 mrTargetHolders
.Current().append(
655 new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
656 polygon
, lineAttribute
, pen
->GetStrokeAttribute(mdExtractedXScale
)));
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)
676 if (!color
.IsTransparent())
679 mrTargetHolders
.Current().append(
680 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
686 const drawinglayer::primitive2d::Primitive2DReference
aPrimitive(
687 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
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())
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);
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
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;
756 isHatchBlend
= false;
762 fillColor
= brush
->solidColor
;
763 fillColor
.Merge(brush
->secondColor
, static_cast<sal_uInt8
>(255 * blendFactor
));
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(
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
)
859 case WrapModeTileFlipY
:
861 aSpreadMethod
= drawinglayer::primitive2d::SpreadMethod::Repeat
;
864 case WrapModeTileFlipX
:
865 case WrapModeTileFlipXY
:
867 aSpreadMethod
= drawinglayer::primitive2d::SpreadMethod::Reflect
;
874 // create the same one used for SVG
875 mrTargetHolders
.Current().append(
876 new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D(
877 aTextureTransformation
,
882 false, // do not use UnitCoordinates
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
,
897 0.7, // relative radius little bigger to cover all elements
898 true, // use UnitCoordinates to stretch the gradient
899 drawinglayer::primitive2d::SpreadMethod::Pad
,
906 EmfPlusHelperData::EmfPlusHelperData(
908 wmfemfhelper::TargetHolders
& rTargetHolders
,
909 wmfemfhelper::PropertyHolders
& rPropertyHolders
)
915 mbSetTextContrast(false),
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
);
942 EmfPlusHelperData::~EmfPlusHelperData()
946 ::basegfx::B2DPolyPolygon
EmfPlusHelperData::combineClip(::basegfx::B2DPolyPolygon
const & leftPolygon
, int combineMode
, ::basegfx::B2DPolyPolygon
const & rightPolygon
)
948 basegfx::B2DPolyPolygon aClippedPolyPolygon
;
951 case EmfPlusCombineModeReplace
:
953 aClippedPolyPolygon
= rightPolygon
;
956 case EmfPlusCombineModeIntersect
:
958 aClippedPolyPolygon
= basegfx::utils::clipPolyPolygonOnPolyPolygon(
959 leftPolygon
, rightPolygon
, true, false);
962 case EmfPlusCombineModeUnion
:
964 aClippedPolyPolygon
= ::basegfx::utils::solvePolygonOperationOr(leftPolygon
, rightPolygon
);
967 case EmfPlusCombineModeXOR
:
969 aClippedPolyPolygon
= ::basegfx::utils::solvePolygonOperationXor(leftPolygon
, rightPolygon
);
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
);
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
);
985 return aClippedPolyPolygon
;
988 void EmfPlusHelperData::processEmfPlusData(
990 const drawinglayer::geometry::ViewInformation2D
& /*rViewInformation*/)
992 sal_uInt64 length
= rMS
.GetSize();
995 SAL_WARN("drawinglayer.emf", "length is less than required header size");
997 // 12 is minimal valid EMF+ record size; remaining bytes are padding
1000 sal_uInt16 type
, flags
;
1001 sal_uInt32 size
, dataSize
;
1004 rMS
.ReadUInt16(type
).ReadUInt16(flags
).ReadUInt32(size
).ReadUInt32(dataSize
);
1006 next
= rMS
.Tell() + (size
- 12);
1010 SAL_WARN("drawinglayer.emf", "Size field is less than 12 bytes");
1013 else if (size
> length
)
1015 SAL_WARN("drawinglayer.emf", "Size field is greater than bytes left");
1019 if (dataSize
> (size
- 12))
1021 SAL_WARN("drawinglayer.emf", "DataSize field is greater than Size-12");
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
,
1040 SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping");
1041 wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders
,
1044 bIsGetDCProcessing
= false;
1046 if (type
== EmfPlusRecordTypeObject
&& ((mbMultipart
&& (flags
& 0x7fff) == (mMFlags
& 0x7fff)) || (flags
& 0x8000)))
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
);
1065 SAL_INFO("drawinglayer.emf", "EMF+ multipart record flags: " << mMFlags
);
1067 processObjectRecord(mMStream
, mMFlags
, 0, true);
1070 mbMultipart
= false;
1073 if (type
!= EmfPlusRecordTypeObject
|| !(flags
& 0x8000))
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"));
1089 case EmfPlusRecordTypeEndOfFile
:
1093 case EmfPlusRecordTypeComment
:
1095 #if OSL_DEBUG_LEVEL > 1
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
);
1109 if ((data
& 0xF0) == 0)
1112 hexdata
+= "0x" + padding
+ OUString::number(data
, 16) + " ";
1115 SAL_INFO("drawinglayer.emf", "EMF+\t" << hexdata
);
1119 case EmfPlusRecordTypeGetDC
:
1121 bIsGetDCProcessing
= true;
1122 aGetDCState
= mrPropertyHolders
.Current();
1123 SAL_INFO("drawinglayer.emf", "EMF+\tAlready used in svtools wmf/emf filter parser");
1126 case EmfPlusRecordTypeObject
:
1128 processObjectRecord(rMS
, flags
, dataSize
);
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");
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));
1171 endAngle
+= static_cast<float>(M_PI
* 2.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
,
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
);
1196 EMFPPlusDrawPolygon(polyPolygon
, flags
& 0xff);
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());
1208 EMFPPlusFillPolygon(path
->GetPolygon(*this), flags
& 0x8000, brushIndexOrColor
);
1210 SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillPath missing path");
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());
1222 EMFPPlusFillPolygon(region
->regionPolyPolygon
, flags
& 0x8000, brushIndexOrColor
);
1224 SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillRegion missing region");
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
);
1251 EMFPPlusDrawPolygon(polyPolygon
, flags
& 0xff);
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
);
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));
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
);
1293 EMFPPlusDrawPolygon(polyPolygon
, flags
& 0xff);
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
);
1311 case EmfPlusRecordTypeDrawLines
:
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
);
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());
1333 EMFPPlusDrawPolygon(path
->GetPolygon(*this), penIndex
);
1335 SAL_WARN("drawinglayer.emf", "\t\tEmfPlusRecordTypeDrawPath missing path");
1339 case EmfPlusRecordTypeDrawBeziers
:
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, "
1353 SAL_WARN("drawinglayer.emf", "EMF+\t Bezier Draw does not support less "
1354 "than 4 points. Number of points: "
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);
1378 case EmfPlusRecordTypeDrawCurve
:
1380 sal_uInt32 aOffset
, aNumSegments
, points
;
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
);
1395 EMFPPlusDrawPolygon(
1396 path
.GetCardinalSpline(*this, aTension
, aOffset
, aNumSegments
),
1399 SAL_WARN("drawinglayer.emf", "Not enough number of points");
1402 case EmfPlusRecordTypeDrawClosedCurve
:
1403 case EmfPlusRecordTypeFillClosedCurve
:
1405 // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
1406 sal_uInt32 brushIndexOrColor
= 999, points
;
1408 if (type
== EmfPlusRecordTypeFillClosedCurve
)
1410 rMS
.ReadUInt32(brushIndexOrColor
);
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
);
1424 SAL_WARN("drawinglayer.emf", "Not enough number of points");
1427 EMFPPath
path(points
, true);
1428 path
.Read(rMS
, flags
);
1429 if (type
== EmfPlusRecordTypeFillClosedCurve
)
1430 EMFPPlusFillPolygon(path
.GetClosedCardinalSpline(*this, aTension
),
1431 flags
& 0x8000, brushIndexOrColor
);
1433 EMFPPlusDrawPolygon(path
.GetClosedCardinalSpline(*this, aTension
),
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()) :
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",
1461 << (type
== EmfPlusRecordTypeDrawImage
? "DrawImage"
1462 : "DrawImagePoints")
1463 << " source rectangle: " << sx
<< "," << sy
<< " " << sw
<< "x"
1466 float dx(0.), dy(0.), dw(0.), dh(0.);
1467 double fShearX
= 0.0;
1468 double fShearY
= 0.0;
1469 if (type
== EmfPlusRecordTypeDrawImagePoints
)
1472 rMS
.ReadUInt32(aCount
);
1474 // Number of points used by DrawImagePoints. Exactly 3 points must be specified.
1477 SAL_WARN("drawinglayer.emf", "EMF+\t Wrong EMF+ file. Expected "
1478 "3 points, received: "
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
);
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
);
1504 if (image
->type
== ImageDataTypeBitmap
)
1506 aSize
= image
->graphic
.GetBitmapEx().GetSizePixel();
1507 SAL_INFO("drawinglayer.emf", "EMF+\t Bitmap size: " << aSize
.Width()
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
;
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
;
1525 dy
= dy
+ ((-sy
) / sh
) * dh
;
1526 if (sy
+ sh
<= aSize
.Height())
1527 dh
= ((sh
+ sy
) / sh
) * dh
;
1529 dh
= (aSize
.Height() / sh
) * dh
;
1531 else if (sy
+ sh
> aSize
.Height())
1532 dh
= ((aSize
.Height() - sy
) / sh
) * dh
;
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
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());
1555 aSize
= aBmp
.GetSizePixel();
1556 if (aSize
.Width() > 0 && aSize
.Height() > 0)
1558 mrTargetHolders
.Current().append(
1559 new drawinglayer::primitive2d::BitmapPrimitive2D(
1560 aBmp
, aTransformMatrix
));
1563 SAL_WARN("drawinglayer.emf", "EMF+\t warning: empty bitmap");
1565 else if (image
->type
== ImageDataTypeMetafile
)
1567 GDIMetaFile
aGDI(image
->graphic
.GetGDIMetaFile());
1569 mrTargetHolders
.Current().append(
1570 new drawinglayer::primitive2d::MetafilePrimitive2D(aTransformMatrix
,
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)");
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
);
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());
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
;
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
;
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
);
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
);
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(
1703 0, // text always starts at 0
1705 std::move(emptyVector
), // EMF-PLUS has no DX-array
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
,
1716 font
->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE
: drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE
);
1720 pBaseText
= new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
1723 0, // text always starts at 0
1725 std::move(emptyVector
), // EMF-PLUS has no DX-array
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(
1742 drawinglayer::primitive2d::Primitive2DContainer
{ aPrimitiveText
} ));
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.");
1757 mnMmX
*= mfPageScale
* getUnitToPixelMultiplier(static_cast<UnitType
>(flags
), mnHDPI
);
1758 mnMmY
*= mfPageScale
* getUnitToPixelMultiplier(static_cast<UnitType
>(flags
), mnVDPI
);
1763 case EmfPlusRecordTypeSetRenderingOrigin
:
1765 rMS
.ReadInt32(mnOriginX
).ReadInt32(mnOriginY
);
1766 SAL_INFO("drawinglayer.emf", "EMF+\t SetRenderingOrigin, [x,y]: " << mnOriginX
<< "," << mnOriginY
);
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");
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");
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");
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");
1806 case EmfPlusRecordTypeSetPixelOffsetMode
:
1808 SAL_INFO("drawinglayer.emf", "EMF+\t Pixel offset mode: " << PixelOffsetModeToString(flags
));
1809 SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetPixelOffsetMode");
1812 case EmfPlusRecordTypeSetCompositingQuality
:
1814 SAL_INFO("drawinglayer.emf", "EMF+\t TODO SetCompositingQuality");
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
);
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
);
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.");
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
;
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
);
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
);
1883 case EmfPlusRecordTypeSetWorldTransform
:
1885 SAL_INFO("drawinglayer.emf", "EMF+\t SetWorldTransform, Post multiply: " << bool(flags
& 0x2000));
1886 readXForm(rMS
, maWorldTransform
);
1888 SAL_INFO("drawinglayer.emf", "EMF+\t\t: " << maWorldTransform
);
1891 case EmfPlusRecordTypeResetWorldTransform
:
1893 maWorldTransform
.identity();
1894 SAL_INFO("drawinglayer.emf", "EMF+\t World transform: " << maWorldTransform
);
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
);
1910 maWorldTransform
*= transform
;
1915 transform
*= maWorldTransform
;
1916 maWorldTransform
= transform
;
1921 SAL_INFO("drawinglayer.emf",
1922 "EMF+\t World transform matrix: " << maWorldTransform
);
1925 case EmfPlusRecordTypeTranslateWorldTransform
:
1927 SAL_INFO("drawinglayer.emf", "EMF+\t TranslateWorldTransform, Post multiply: " << bool(flags
& 0x2000));
1929 basegfx::B2DHomMatrix transform
;
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
);
1941 maWorldTransform
*= transform
;
1946 transform
*= maWorldTransform
;
1947 maWorldTransform
= transform
;
1952 SAL_INFO("drawinglayer.emf",
1953 "EMF+\t World transform matrix: " << maWorldTransform
);
1956 case EmfPlusRecordTypeScaleWorldTransform
:
1958 basegfx::B2DHomMatrix transform
;
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
);
1972 maWorldTransform
*= transform
;
1977 transform
*= maWorldTransform
;
1978 maWorldTransform
= transform
;
1983 SAL_INFO("drawinglayer.emf",
1984 "EMF+\t World transform matrix: " << maWorldTransform
);
1987 case EmfPlusRecordTypeRotateWorldTransform
:
1989 // Angle of rotation in degrees
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
));
2000 SAL_INFO("drawinglayer.emf",
2001 "EMF+\t " << maWorldTransform
);
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
);
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
));
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());
2043 SAL_WARN("drawinglayer.emf",
2044 "EMF+\t TODO Unable to find path in slot: " << (flags
& 0xff));
2047 polyPolygon
= path
->GetPolygon(*this);
2049 else if (type
== EmfPlusRecordTypeSetClipRegion
)
2051 SAL_INFO("drawinglayer.emf", "EMF+\t Region in slot: " << (flags
& 0xff));
2053 = dynamic_cast<EMFPRegion
*>(maEMFPObjects
[flags
& 0xff].get());
2058 "EMF+\t TODO Unable to find region in slot: " << (flags
& 0xff));
2061 polyPolygon
= region
->regionPolyPolygon
;
2063 SAL_INFO("drawinglayer.emf", "EMF+\t Combine mode: " << combineMode
);
2064 ::basegfx::B2DPolyPolygon aClippedPolyPolygon
;
2065 if (mrPropertyHolders
.Current().getClipPolyPolygonActive())
2068 = combineClip(mrPropertyHolders
.Current().getClipPolyPolygon(),
2069 combineMode
, polyPolygon
);
2073 //Combine with infinity
2074 switch (combineMode
)
2076 case EmfPlusCombineModeReplace
:
2077 case EmfPlusCombineModeIntersect
:
2079 aClippedPolyPolygon
= polyPolygon
;
2082 case EmfPlusCombineModeUnion
:
2084 // Disable clipping as the clipping is infinity
2085 aClippedPolyPolygon
= ::basegfx::B2DPolyPolygon();
2088 case EmfPlusCombineModeXOR
:
2089 case EmfPlusCombineModeComplement
:
2091 //TODO It is not correct and it should be fixed
2092 aClippedPolyPolygon
= polyPolygon
;
2095 case EmfPlusCombineModeExclude
:
2097 //TODO It is not correct and it should be fixed
2098 aClippedPolyPolygon
= ::basegfx::B2DPolyPolygon();
2103 HandleNewClipRegion(aClippedPolyPolygon
, mrTargetHolders
, mrPropertyHolders
);
2106 case EmfPlusRecordTypeOffsetClip
:
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
);
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
;
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());
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
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())
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
]);
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
]));
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(
2217 pos
, // take character at current pos
2218 aLength
, // use determined length
2219 std::move(aDXArray
), // generated DXArray
2222 Application::GetSettings().GetLanguageTag().getLocale(),
2227 drawinglayer::primitive2d::TEXT_LINE_NONE
,
2228 font
->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE
: drawinglayer::primitive2d::TEXT_LINE_NONE
,
2230 font
->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE
: drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE
);
2234 pBaseText
= new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
2237 pos
, // take character at current pos
2238 aLength
, // use determined length
2239 std::move(aDXArray
), // generated DXArray
2242 Application::GetSettings().GetLanguageTag().getLocale(),
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(
2255 drawinglayer::primitive2d::Primitive2DContainer
{ aPrimitiveText
} ));
2264 SAL_WARN("drawinglayer.emf", "EMF+\tTODO: fonts (non-unicode glyphs chars)");
2270 SAL_WARN("drawinglayer.emf", "EMF+ TODO unhandled record type: 0x" << std::hex
<< type
<< std::dec
);
2283 SAL_WARN("drawinglayer.emf", "ImplRenderer::processEMFPlus: "
2284 "size " << size
<< " > length " << length
);
2291 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */