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