/src/libreoffice/drawinglayer/source/tools/emfphelperdata.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include "emfpcustomlinecap.hxx" |
21 | | #include "emfphelperdata.hxx" |
22 | | #include "emfpbrush.hxx" |
23 | | #include "emfppen.hxx" |
24 | | #include "emfppath.hxx" |
25 | | #include "emfpregion.hxx" |
26 | | #include "emfpimage.hxx" |
27 | | #include "emfpimageattributes.hxx" |
28 | | #include "emfpfont.hxx" |
29 | | #include "emfpstringformat.hxx" |
30 | | #include <wmfemfhelper.hxx> |
31 | | #include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx> |
32 | | #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> |
33 | | #include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.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 | | |
55 | | #include <algorithm> |
56 | | |
57 | | namespace emfplushelper |
58 | | { |
59 | | |
60 | | enum |
61 | | { |
62 | | WrapModeTile = 0x00000000, |
63 | | WrapModeTileFlipX = 0x00000001, |
64 | | WrapModeTileFlipY = 0x00000002, |
65 | | WrapModeTileFlipXY = 0x00000003, |
66 | | WrapModeClamp = 0x00000004 |
67 | | }; |
68 | | |
69 | | const char* emfTypeToName(sal_uInt16 type) |
70 | 0 | { |
71 | 0 | switch (type) |
72 | 0 | { |
73 | 0 | case EmfPlusRecordTypeHeader: return "EmfPlusRecordTypeHeader"; |
74 | 0 | case EmfPlusRecordTypeEndOfFile: return "EmfPlusRecordTypeEndOfFile"; |
75 | 0 | case EmfPlusRecordTypeComment: return "EmfPlusRecordTypeComment"; |
76 | 0 | case EmfPlusRecordTypeGetDC: return "EmfPlusRecordTypeGetDC"; |
77 | 0 | case EmfPlusRecordTypeObject: return "EmfPlusRecordTypeObject"; |
78 | 0 | case EmfPlusRecordTypeFillRects: return "EmfPlusRecordTypeFillRects"; |
79 | 0 | case EmfPlusRecordTypeDrawRects: return "EmfPlusRecordTypeDrawRects"; |
80 | 0 | case EmfPlusRecordTypeFillPolygon: return "EmfPlusRecordTypeFillPolygon"; |
81 | 0 | case EmfPlusRecordTypeDrawLines: return "EmfPlusRecordTypeDrawLines"; |
82 | 0 | case EmfPlusRecordTypeFillClosedCurve: return "EmfPlusRecordTypeFillClosedCurve"; |
83 | 0 | case EmfPlusRecordTypeDrawClosedCurve: return "EmfPlusRecordTypeDrawClosedCurve"; |
84 | 0 | case EmfPlusRecordTypeDrawCurve: return "EmfPlusRecordTypeDrawCurve"; |
85 | 0 | case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse"; |
86 | 0 | case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse"; |
87 | 0 | case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie"; |
88 | 0 | case EmfPlusRecordTypeDrawPie: return "EmfPlusRecordTypeDrawPie"; |
89 | 0 | case EmfPlusRecordTypeDrawArc: return "EmfPlusRecordTypeDrawArc"; |
90 | 0 | case EmfPlusRecordTypeFillRegion: return "EmfPlusRecordTypeFillRegion"; |
91 | 0 | case EmfPlusRecordTypeFillPath: return "EmfPlusRecordTypeFillPath"; |
92 | 0 | case EmfPlusRecordTypeDrawPath: return "EmfPlusRecordTypeDrawPath"; |
93 | 0 | case EmfPlusRecordTypeDrawBeziers: return "EmfPlusRecordTypeDrawBeziers"; |
94 | 0 | case EmfPlusRecordTypeDrawImage: return "EmfPlusRecordTypeDrawImage"; |
95 | 0 | case EmfPlusRecordTypeDrawImagePoints: return "EmfPlusRecordTypeDrawImagePoints"; |
96 | 0 | case EmfPlusRecordTypeDrawString: return "EmfPlusRecordTypeDrawString"; |
97 | 0 | case EmfPlusRecordTypeSetRenderingOrigin: return "EmfPlusRecordTypeSetRenderingOrigin"; |
98 | 0 | case EmfPlusRecordTypeSetAntiAliasMode: return "EmfPlusRecordTypeSetAntiAliasMode"; |
99 | 0 | case EmfPlusRecordTypeSetTextRenderingHint: return "EmfPlusRecordTypeSetTextRenderingHint"; |
100 | 0 | case EmfPlusRecordTypeSetTextContrast: return "EmfPlusRecordTypeSetTextContrast"; |
101 | 0 | case EmfPlusRecordTypeSetInterpolationMode: return "EmfPlusRecordTypeSetInterpolationMode"; |
102 | 0 | case EmfPlusRecordTypeSetPixelOffsetMode: return "EmfPlusRecordTypeSetPixelOffsetMode"; |
103 | 0 | case EmfPlusRecordTypeSetCompositingQuality: return "EmfPlusRecordTypeSetCompositingQuality"; |
104 | 0 | case EmfPlusRecordTypeSave: return "EmfPlusRecordTypeSave"; |
105 | 0 | case EmfPlusRecordTypeRestore: return "EmfPlusRecordTypeRestore"; |
106 | 0 | case EmfPlusRecordTypeBeginContainer: return "EmfPlusRecordTypeBeginContainer"; |
107 | 0 | case EmfPlusRecordTypeBeginContainerNoParams: return "EmfPlusRecordTypeBeginContainerNoParams"; |
108 | 0 | case EmfPlusRecordTypeEndContainer: return "EmfPlusRecordTypeEndContainer"; |
109 | 0 | case EmfPlusRecordTypeSetWorldTransform: return "EmfPlusRecordTypeSetWorldTransform"; |
110 | 0 | case EmfPlusRecordTypeResetWorldTransform: return "EmfPlusRecordTypeResetWorldTransform"; |
111 | 0 | case EmfPlusRecordTypeMultiplyWorldTransform: return "EmfPlusRecordTypeMultiplyWorldTransform"; |
112 | 0 | case EmfPlusRecordTypeTranslateWorldTransform: return "EmfPlusRecordTypeTranslateWorldTransform"; |
113 | 0 | case EmfPlusRecordTypeScaleWorldTransform: return "EmfPlusRecordTypeScaleWorldTransform"; |
114 | 0 | case EmfPlusRecordTypeSetPageTransform: return "EmfPlusRecordTypeSetPageTransform"; |
115 | 0 | case EmfPlusRecordTypeResetClip: return "EmfPlusRecordTypeResetClip"; |
116 | 0 | case EmfPlusRecordTypeSetClipRect: return "EmfPlusRecordTypeSetClipRect"; |
117 | 0 | case EmfPlusRecordTypeSetClipPath: return "EmfPlusRecordTypeSetClipPath"; |
118 | 0 | case EmfPlusRecordTypeSetClipRegion: return "EmfPlusRecordTypeSetClipRegion"; |
119 | 0 | case EmfPlusRecordTypeOffsetClip: return "EmfPlusRecordTypeOffsetClip"; |
120 | 0 | case EmfPlusRecordTypeDrawDriverString: return "EmfPlusRecordTypeDrawDriverString"; |
121 | 0 | } |
122 | 0 | return ""; |
123 | 0 | } |
124 | | |
125 | | static OUString emfObjectToName(sal_uInt16 type) |
126 | 0 | { |
127 | 0 | switch (type) |
128 | 0 | { |
129 | 0 | case EmfPlusObjectTypeBrush: return u"EmfPlusObjectTypeBrush"_ustr; |
130 | 0 | case EmfPlusObjectTypePen: return u"EmfPlusObjectTypePen"_ustr; |
131 | 0 | case EmfPlusObjectTypePath: return u"EmfPlusObjectTypePath"_ustr; |
132 | 0 | case EmfPlusObjectTypeRegion: return u"EmfPlusObjectTypeRegion"_ustr; |
133 | 0 | case EmfPlusObjectTypeImage: return u"EmfPlusObjectTypeImage"_ustr; |
134 | 0 | case EmfPlusObjectTypeFont: return u"EmfPlusObjectTypeFont"_ustr; |
135 | 0 | case EmfPlusObjectTypeStringFormat: return u"EmfPlusObjectTypeStringFormat"_ustr; |
136 | 0 | case EmfPlusObjectTypeImageAttributes: return u"EmfPlusObjectTypeImageAttributes"_ustr; |
137 | 0 | case EmfPlusObjectTypeCustomLineCap: return u"EmfPlusObjectTypeCustomLineCap"_ustr; |
138 | 0 | } |
139 | 0 | return u""_ustr; |
140 | 0 | } |
141 | | |
142 | | static OUString PixelOffsetModeToString(sal_uInt16 nPixelOffset) |
143 | 0 | { |
144 | 0 | switch (nPixelOffset) |
145 | 0 | { |
146 | 0 | case PixelOffsetMode::PixelOffsetModeDefault: return u"PixelOffsetModeDefault"_ustr; |
147 | 0 | case PixelOffsetMode::PixelOffsetModeHighSpeed: return u"PixelOffsetModeHighSpeed"_ustr; |
148 | 0 | case PixelOffsetMode::PixelOffsetModeHighQuality: return u"PixelOffsetModeHighQuality"_ustr; |
149 | 0 | case PixelOffsetMode::PixelOffsetModeNone: return u"PixelOffsetModeNone"_ustr; |
150 | 0 | case PixelOffsetMode::PixelOffsetModeHalf: return u"PixelOffsetModeHalf"_ustr; |
151 | 0 | } |
152 | 0 | return u""_ustr; |
153 | 0 | } |
154 | | |
155 | | static OUString SmoothingModeToString(sal_uInt16 nSmoothMode) |
156 | 0 | { |
157 | 0 | switch (nSmoothMode) |
158 | 0 | { |
159 | 0 | case SmoothingMode::SmoothingModeDefault: return u"SmoothingModeDefault"_ustr; |
160 | 0 | case SmoothingMode::SmoothingModeHighSpeed: return u"SmoothModeHighSpeed"_ustr; |
161 | 0 | case SmoothingMode::SmoothingModeHighQuality: return u"SmoothingModeHighQuality"_ustr; |
162 | 0 | case SmoothingMode::SmoothingModeNone: return u"SmoothingModeNone"_ustr; |
163 | 0 | case SmoothingMode::SmoothingModeAntiAlias8x4: return u"SmoothingModeAntiAlias8x4"_ustr; |
164 | 0 | case SmoothingMode::SmoothingModeAntiAlias8x8: return u"SmoothingModeAntiAlias8x8"_ustr; |
165 | 0 | } |
166 | 0 | return u""_ustr; |
167 | 0 | } |
168 | | |
169 | | static OUString TextRenderingHintToString(sal_uInt16 nHint) |
170 | 0 | { |
171 | 0 | switch (nHint) |
172 | 0 | { |
173 | 0 | case TextRenderingHint::TextRenderingHintSystemDefault: return u"TextRenderingHintSystemDefault"_ustr; |
174 | 0 | case TextRenderingHint::TextRenderingHintSingleBitPerPixelGridFit: return u"TextRenderingHintSingleBitPerPixelGridFit"_ustr; |
175 | 0 | case TextRenderingHint::TextRenderingHintSingleBitPerPixel: return u"TextRenderingHintSingleBitPerPixel"_ustr; |
176 | 0 | case TextRenderingHint::TextRenderingHintAntialiasGridFit: return u"TextRenderingHintAntialiasGridFit"_ustr; |
177 | 0 | case TextRenderingHint::TextRenderingHintAntialias: return u"TextRenderingHintAntialias"_ustr; |
178 | 0 | case TextRenderingHint::TextRenderingHintClearTypeGridFit: return u"TextRenderingHintClearTypeGridFit"_ustr; |
179 | 0 | } |
180 | 0 | return u""_ustr; |
181 | 0 | } |
182 | | |
183 | | static OUString InterpolationModeToString(sal_uInt16 nMode) |
184 | 0 | { |
185 | 0 | switch (nMode) |
186 | 0 | { |
187 | 0 | case InterpolationMode::InterpolationModeDefault: return u"InterpolationModeDefault"_ustr; |
188 | 0 | case InterpolationMode::InterpolationModeLowQuality: return u"InterpolationModeLowQuality"_ustr; |
189 | 0 | case InterpolationMode::InterpolationModeHighQuality: return u"InterpolationModeHighQuality"_ustr; |
190 | 0 | case InterpolationMode::InterpolationModeBilinear: return u"InterpolationModeBilinear"_ustr; |
191 | 0 | case InterpolationMode::InterpolationModeBicubic: return u"InterpolationModeBicubic"_ustr; |
192 | 0 | case InterpolationMode::InterpolationModeNearestNeighbor: return u"InterpolationModeNearestNeighbor"_ustr; |
193 | 0 | case InterpolationMode::InterpolationModeHighQualityBilinear: return u"InterpolationModeHighQualityBilinear"_ustr; |
194 | 0 | case InterpolationMode::InterpolationModeHighQualityBicubic: return u"InterpolationModeHighQualityBicubic"_ustr; |
195 | 0 | } |
196 | 0 | return u""_ustr; |
197 | 0 | } |
198 | | |
199 | | OUString UnitTypeToString(sal_uInt16 nType) |
200 | 0 | { |
201 | 0 | switch (nType) |
202 | 0 | { |
203 | 0 | case UnitTypeWorld: return u"UnitTypeWorld"_ustr; |
204 | 0 | case UnitTypeDisplay: return u"UnitTypeDisplay"_ustr; |
205 | 0 | case UnitTypePixel: return u"UnitTypePixel"_ustr; |
206 | 0 | case UnitTypePoint: return u"UnitTypePoint"_ustr; |
207 | 0 | case UnitTypeInch: return u"UnitTypeInch"_ustr; |
208 | 0 | case UnitTypeDocument: return u"UnitTypeDocument"_ustr; |
209 | 0 | case UnitTypeMillimeter: return u"UnitTypeMillimeter"_ustr; |
210 | 0 | } |
211 | 0 | return u""_ustr; |
212 | 0 | } |
213 | | |
214 | | static bool IsBrush(sal_uInt16 flags) |
215 | 0 | { |
216 | 0 | return (!((flags >> 15) & 0x0001)); |
217 | 0 | } |
218 | | |
219 | | static OUString BrushIDToString(sal_uInt16 flags, sal_uInt32 brushid) |
220 | 0 | { |
221 | 0 | if (IsBrush(flags)) |
222 | 0 | return "EmfPlusBrush ID: " + OUString::number(brushid); |
223 | 0 | else |
224 | 0 | return "ARGB: 0x" + OUString::number(brushid, 16); |
225 | 0 | } |
226 | | |
227 | | EMFPObject::~EMFPObject() |
228 | 0 | { |
229 | 0 | } |
230 | | |
231 | | double EmfPlusHelperData::unitToPixel(double n, sal_uInt32 aUnitType, Direction d) |
232 | 0 | { |
233 | 0 | switch (static_cast<UnitType>(aUnitType)) |
234 | 0 | { |
235 | 0 | case UnitTypePixel: |
236 | 0 | return n; |
237 | | |
238 | 0 | case UnitTypePoint: |
239 | 0 | return o3tl::convert(n, o3tl::Length::pt, o3tl::Length::in) * DPI(d); |
240 | | |
241 | 0 | case UnitTypeInch: |
242 | 0 | return n * DPI(d); |
243 | | |
244 | 0 | case UnitTypeMillimeter: |
245 | 0 | return o3tl::convert(n, o3tl::Length::mm, o3tl::Length::in) * DPI(d); |
246 | | |
247 | 0 | case UnitTypeDocument: |
248 | 0 | return n * DPI(d) / 300.0; |
249 | | |
250 | 0 | case UnitTypeWorld: |
251 | 0 | case UnitTypeDisplay: |
252 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t Converting to World/Display."); |
253 | 0 | return n; |
254 | | |
255 | 0 | default: |
256 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType); |
257 | 0 | return n; |
258 | 0 | } |
259 | 0 | } |
260 | | |
261 | | void EmfPlusHelperData::processObjectRecord(SvMemoryStream& rObjectStream, sal_uInt16 flags, sal_uInt32 dataSize, bool bUseWholeStream) |
262 | 0 | { |
263 | 0 | sal_uInt16 objecttype = flags & 0x7f00; |
264 | 0 | sal_uInt16 index = flags & 0xff; |
265 | 0 | SAL_INFO("drawinglayer.emf", "EMF+ Object: " << emfObjectToName(objecttype) << " (0x" << objecttype << ")"); |
266 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\tObject slot: " << index); |
267 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\tFlags: " << (flags & 0xff00)); |
268 | | |
269 | 0 | switch (objecttype) |
270 | 0 | { |
271 | 0 | case EmfPlusObjectTypeBrush: |
272 | 0 | { |
273 | 0 | EMFPBrush *brush = new EMFPBrush(); |
274 | 0 | maEMFPObjects[index].reset(brush); |
275 | 0 | brush->Read(rObjectStream, *this); |
276 | 0 | break; |
277 | 0 | } |
278 | 0 | case EmfPlusObjectTypePen: |
279 | 0 | { |
280 | 0 | EMFPPen *pen = new EMFPPen(); |
281 | 0 | maEMFPObjects[index].reset(pen); |
282 | 0 | pen->Read(rObjectStream, *this); |
283 | 0 | pen->penWidth = unitToPixel(pen->penWidth, pen->penUnit, Direction::horizontal); |
284 | 0 | break; |
285 | 0 | } |
286 | 0 | case EmfPlusObjectTypePath: |
287 | 0 | { |
288 | 0 | sal_uInt32 aVersion, aPathPointCount, aPathPointFlags; |
289 | |
|
290 | 0 | rObjectStream.ReadUInt32(aVersion).ReadUInt32(aPathPointCount).ReadUInt32(aPathPointFlags); |
291 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t\tVersion: 0x" << std::hex << aVersion); |
292 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t\tNumber of points: " << std::dec << aPathPointCount); |
293 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t\tPath point flags: 0x" << std::hex << aPathPointFlags << std::dec); |
294 | 0 | EMFPPath *path = new EMFPPath(aPathPointCount); |
295 | 0 | maEMFPObjects[index].reset(path); |
296 | 0 | path->Read(rObjectStream, aPathPointFlags); |
297 | 0 | break; |
298 | 0 | } |
299 | 0 | case EmfPlusObjectTypeRegion: |
300 | 0 | { |
301 | 0 | EMFPRegion *region = new EMFPRegion(); |
302 | 0 | maEMFPObjects[index].reset(region); |
303 | 0 | region->ReadRegion(rObjectStream, *this); |
304 | 0 | break; |
305 | 0 | } |
306 | 0 | case EmfPlusObjectTypeImage: |
307 | 0 | { |
308 | 0 | EMFPImage *image = new EMFPImage; |
309 | 0 | maEMFPObjects[index].reset(image); |
310 | 0 | image->type = 0; |
311 | 0 | image->width = 0; |
312 | 0 | image->height = 0; |
313 | 0 | image->stride = 0; |
314 | 0 | image->pixelFormat = 0; |
315 | 0 | image->Read(rObjectStream, dataSize, bUseWholeStream); |
316 | 0 | break; |
317 | 0 | } |
318 | 0 | case EmfPlusObjectTypeFont: |
319 | 0 | { |
320 | 0 | EMFPFont *font = new EMFPFont; |
321 | 0 | maEMFPObjects[index].reset(font); |
322 | 0 | font->emSize = 0; |
323 | 0 | font->sizeUnit = 0; |
324 | 0 | font->fontFlags = 0; |
325 | 0 | font->Read(rObjectStream); |
326 | | // tdf#113624 Convert unit to Pixels |
327 | 0 | font->emSize = unitToPixel(font->emSize, font->sizeUnit, Direction::horizontal); |
328 | |
|
329 | 0 | break; |
330 | 0 | } |
331 | 0 | case EmfPlusObjectTypeStringFormat: |
332 | 0 | { |
333 | 0 | EMFPStringFormat *stringFormat = new EMFPStringFormat(); |
334 | 0 | maEMFPObjects[index].reset(stringFormat); |
335 | 0 | stringFormat->Read(rObjectStream); |
336 | 0 | break; |
337 | 0 | } |
338 | 0 | case EmfPlusObjectTypeImageAttributes: |
339 | 0 | { |
340 | 0 | EMFPImageAttributes *imageAttributes = new EMFPImageAttributes(); |
341 | 0 | maEMFPObjects[index].reset(imageAttributes); |
342 | 0 | imageAttributes->Read(rObjectStream); |
343 | 0 | break; |
344 | 0 | } |
345 | 0 | case EmfPlusObjectTypeCustomLineCap: |
346 | 0 | { |
347 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object type 'custom line cap' not yet implemented"); |
348 | 0 | break; |
349 | 0 | } |
350 | 0 | default: |
351 | 0 | { |
352 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec); |
353 | 0 | } |
354 | 0 | } |
355 | 0 | } |
356 | | |
357 | | void EmfPlusHelperData::ReadPoint(SvStream& s, float& x, float& y, sal_uInt32 flags) |
358 | 0 | { |
359 | 0 | if (flags & 0x800) |
360 | 0 | { |
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 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Relative coordinates bit detected. Implement parse EMFPlusPointR"); |
365 | 0 | } |
366 | | |
367 | 0 | if (flags & 0x4000) |
368 | 0 | { |
369 | 0 | sal_Int16 ix, iy; |
370 | |
|
371 | 0 | s.ReadInt16(ix).ReadInt16(iy); |
372 | |
|
373 | 0 | x = ix; |
374 | 0 | y = iy; |
375 | 0 | } |
376 | 0 | else |
377 | 0 | { |
378 | 0 | s.ReadFloat(x).ReadFloat(y); |
379 | 0 | } |
380 | 0 | } |
381 | | |
382 | | void EmfPlusHelperData::ReadRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed) |
383 | 0 | { |
384 | 0 | if (bCompressed) |
385 | 0 | { |
386 | 0 | sal_Int16 ix, iy, iw, ih; |
387 | |
|
388 | 0 | s.ReadInt16(ix).ReadInt16(iy).ReadInt16(iw).ReadInt16(ih); |
389 | |
|
390 | 0 | x = ix; |
391 | 0 | y = iy; |
392 | 0 | width = iw; |
393 | 0 | height = ih; |
394 | 0 | } |
395 | 0 | else |
396 | 0 | { |
397 | 0 | s.ReadFloat(x).ReadFloat(y).ReadFloat(width).ReadFloat(height); |
398 | 0 | } |
399 | 0 | } |
400 | | |
401 | | bool EmfPlusHelperData::readXForm(SvStream& rIn, basegfx::B2DHomMatrix& rTarget) |
402 | 0 | { |
403 | 0 | rTarget.identity(); |
404 | |
|
405 | 0 | if (sizeof(float) != 4) |
406 | 0 | { |
407 | 0 | OSL_FAIL("EnhWMFReader::sizeof( float ) != 4"); |
408 | 0 | return false; |
409 | 0 | } |
410 | 0 | else |
411 | 0 | { |
412 | 0 | float eM11(0.0); |
413 | 0 | float eM12(0.0); |
414 | 0 | float eM21(0.0); |
415 | 0 | float eM22(0.0); |
416 | 0 | float eDx(0.0); |
417 | 0 | float eDy(0.0); |
418 | 0 | rIn.ReadFloat(eM11).ReadFloat(eM12).ReadFloat(eM21).ReadFloat(eM22).ReadFloat(eDx).ReadFloat(eDy); |
419 | 0 | rTarget = basegfx::B2DHomMatrix( |
420 | 0 | eM11, eM21, eDx, |
421 | 0 | eM12, eM22, eDy); |
422 | 0 | } |
423 | | |
424 | 0 | return true; |
425 | 0 | } |
426 | | |
427 | | void EmfPlusHelperData::mappingChanged() |
428 | 0 | { |
429 | 0 | if (mnPixX == 0 || mnPixY == 0) |
430 | 0 | { |
431 | 0 | SAL_WARN("drawinglayer.emf", "dimensions in pixels is 0"); |
432 | 0 | return; |
433 | 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. |
437 | | // |
438 | | // Re-calculate maMapTransform to contain the complete former transformation so that |
439 | | // it can be applied by a single matrix multiplication or be added to an encapsulated |
440 | | // primitive later |
441 | | // |
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 | 0 | maMapTransform = maWorldTransform; |
445 | 0 | maMapTransform *= basegfx::utils::createScaleTranslateB2DHomMatrix(100.0 * mnMmX / mnPixX, 100.0 * mnMmY / mnPixY, |
446 | 0 | double(-mnFrameLeft), double(-mnFrameTop)); |
447 | 0 | maMapTransform *= maBaseTransform; |
448 | | |
449 | | // Used only for performance optimization, to do not calculate it every line draw |
450 | 0 | mdExtractedXScale = std::hypot(maMapTransform.a(), maMapTransform.b()); |
451 | 0 | mdExtractedYScale = std::hypot(maMapTransform.c(), maMapTransform.d()); |
452 | 0 | } |
453 | | |
454 | | ::basegfx::B2DPoint EmfPlusHelperData::Map(double ix, double iy) const |
455 | 0 | { |
456 | | // map in one step using complete MapTransform (see mappingChanged) |
457 | 0 | return maMapTransform * ::basegfx::B2DPoint(ix, iy); |
458 | 0 | } |
459 | | |
460 | 0 | Color EmfPlusHelperData::EMFPGetBrushColorOrARGBColor(const sal_uInt16 flags, const sal_uInt32 brushIndexOrColor) const { |
461 | 0 | Color color; |
462 | 0 | if (flags & 0x8000) // we use a color |
463 | 0 | { |
464 | 0 | color = Color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, |
465 | 0 | (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff); |
466 | 0 | } |
467 | 0 | else // we use a brush |
468 | 0 | { |
469 | 0 | const EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get()); |
470 | 0 | if (brush) |
471 | 0 | { |
472 | 0 | color = brush->GetColor(); |
473 | 0 | if (brush->type != BrushTypeSolidColor) |
474 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Brush other than solid color is not supported"); |
475 | 0 | } |
476 | 0 | } |
477 | 0 | return color; |
478 | 0 | } |
479 | | |
480 | | void EmfPlusHelperData::GraphicStatePush(GraphicStateMap& map, sal_Int32 index) |
481 | 0 | { |
482 | 0 | GraphicStateMap::iterator iter = map.find( index ); |
483 | |
|
484 | 0 | if ( iter != map.end() ) |
485 | 0 | { |
486 | 0 | map.erase( iter ); |
487 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t\tStack index: " << index << " found and erased"); |
488 | 0 | } |
489 | | |
490 | 0 | wmfemfhelper::PropertyHolder state = mrPropertyHolders.Current(); |
491 | | // tdf#112500 We need to save world transform somehow, during graphic state push |
492 | 0 | state.setTransformation(maWorldTransform); |
493 | 0 | map[ index ] = std::move(state); |
494 | 0 | } |
495 | | |
496 | | void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index) |
497 | 0 | { |
498 | 0 | GraphicStateMap::iterator iter = map.find(index); |
499 | |
|
500 | 0 | if (iter != map.end()) |
501 | 0 | { |
502 | 0 | wmfemfhelper::PropertyHolder state = iter->second; |
503 | |
|
504 | 0 | maWorldTransform = state.getTransformation(); |
505 | 0 | if (state.getClipPolyPolygonActive()) |
506 | 0 | { |
507 | 0 | SAL_INFO("drawinglayer.emf", |
508 | 0 | "EMF+\t Restore clipping region to saved in index: " << index); |
509 | 0 | wmfemfhelper::HandleNewClipRegion(state.getClipPolyPolygon(), mrTargetHolders, |
510 | 0 | mrPropertyHolders); |
511 | 0 | } |
512 | 0 | else |
513 | 0 | { |
514 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping"); |
515 | 0 | wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, |
516 | 0 | mrPropertyHolders); |
517 | 0 | } |
518 | 0 | mappingChanged(); |
519 | 0 | SAL_INFO("drawinglayer.emf", |
520 | 0 | "EMF+\t\tStack index: " << index |
521 | 0 | << " found, maWorldTransform: " << maWorldTransform); |
522 | 0 | } |
523 | 0 | } |
524 | | |
525 | | drawinglayer::attribute::LineStartEndAttribute |
526 | | EmfPlusHelperData::CreateLineEnd(const sal_Int32 aCap, const float aPenWidth) const |
527 | 0 | { |
528 | 0 | const double pw = mdExtractedYScale * aPenWidth; |
529 | 0 | if (aCap == LineCapTypeSquare) |
530 | 0 | { |
531 | 0 | basegfx::B2DPolygon aCapPolygon( |
532 | 0 | { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} }); |
533 | 0 | aCapPolygon.setClosed(true); |
534 | 0 | return drawinglayer::attribute::LineStartEndAttribute( |
535 | 0 | pw, basegfx::B2DPolyPolygon(aCapPolygon), true); |
536 | 0 | } |
537 | 0 | else if (aCap == LineCapTypeRound) |
538 | 0 | { |
539 | 0 | basegfx::B2DPolygon aCapPolygon( |
540 | 0 | { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.9236, -0.3827}, |
541 | 0 | {0.7071, -0.7071}, {0.3827, -0.9236}, {0.0, -1.0}, {-0.3827, -0.9236}, |
542 | 0 | {-0.7071, -0.7071}, {-0.9236, -0.3827}, {-1.0, 0.0} }); |
543 | 0 | aCapPolygon.setClosed(true); |
544 | 0 | return drawinglayer::attribute::LineStartEndAttribute( |
545 | 0 | pw, basegfx::B2DPolyPolygon(aCapPolygon), true); |
546 | 0 | } |
547 | 0 | else if (aCap == LineCapTypeTriangle) |
548 | 0 | { |
549 | 0 | basegfx::B2DPolygon aCapPolygon( |
550 | 0 | { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.0, -1.0}, {-1.0, 0.0} }); |
551 | 0 | aCapPolygon.setClosed(true); |
552 | 0 | return drawinglayer::attribute::LineStartEndAttribute( |
553 | 0 | pw, basegfx::B2DPolyPolygon(aCapPolygon), true); |
554 | 0 | } |
555 | 0 | else if (aCap == LineCapTypeSquareAnchor) |
556 | 0 | { |
557 | 0 | basegfx::B2DPolygon aCapPolygon( |
558 | 0 | { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} }); |
559 | 0 | aCapPolygon.setClosed(true); |
560 | 0 | return drawinglayer::attribute::LineStartEndAttribute( |
561 | 0 | 1.5 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true); |
562 | 0 | } |
563 | 0 | else if (aCap == LineCapTypeRoundAnchor) |
564 | 0 | { |
565 | 0 | const basegfx::B2DPolygon aCapPolygon |
566 | 0 | = ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(0.0, 0.0), 1.0, 1.0); |
567 | 0 | return drawinglayer::attribute::LineStartEndAttribute( |
568 | 0 | 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true); |
569 | 0 | } |
570 | 0 | else if (aCap == LineCapTypeDiamondAnchor) |
571 | 0 | { |
572 | 0 | basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 0.0}, {0.5, 0.5}, |
573 | 0 | {0.5, 1.0}, {-0.5, 1.0}, {-0.5, 0.5}, |
574 | 0 | {-1.0, 0.0} }); |
575 | 0 | aCapPolygon.setClosed(true); |
576 | 0 | return drawinglayer::attribute::LineStartEndAttribute( |
577 | 0 | 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true); |
578 | 0 | } |
579 | 0 | else if (aCap == LineCapTypeArrowAnchor) |
580 | 0 | { |
581 | 0 | basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} }); |
582 | 0 | aCapPolygon.setClosed(true); |
583 | 0 | return drawinglayer::attribute::LineStartEndAttribute( |
584 | 0 | 2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true); |
585 | 0 | } |
586 | 0 | return drawinglayer::attribute::LineStartEndAttribute(); |
587 | 0 | } |
588 | | |
589 | | void EmfPlusHelperData::EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon, |
590 | | sal_uInt32 penIndex) |
591 | 0 | { |
592 | 0 | const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 0xff].get()); |
593 | 0 | SAL_WARN_IF(!pen, "drawinglayer.emf", "emf+ missing pen"); |
594 | | |
595 | 0 | if (!(pen && polygon.count())) |
596 | 0 | return; |
597 | | |
598 | 0 | const double transformedPenWidth = mdExtractedYScale * pen->penWidth; |
599 | 0 | drawinglayer::attribute::LineAttribute lineAttribute( |
600 | 0 | pen->GetColor().getBColor(), transformedPenWidth, pen->maLineJoin, |
601 | 0 | css::drawing::LineCap_BUTT, //TODO implement PenDataDashedLineCap support here |
602 | 0 | pen->fMiterMinimumAngle); |
603 | |
|
604 | 0 | drawinglayer::attribute::LineStartEndAttribute aStart; |
605 | 0 | if (pen->penDataFlags & EmfPlusPenDataStartCap) |
606 | 0 | { |
607 | 0 | if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap) |
608 | 0 | && (pen->customStartCap->polygon.begin()->count() > 1)) |
609 | 0 | aStart = drawinglayer::attribute::LineStartEndAttribute( |
610 | 0 | pen->customStartCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale |
611 | 0 | * pen->customStartCap->widthScale * pen->penWidth, |
612 | 0 | pen->customStartCap->polygon, false); |
613 | 0 | else |
614 | 0 | aStart = EmfPlusHelperData::CreateLineEnd(pen->startCap, pen->penWidth); |
615 | 0 | } |
616 | |
|
617 | 0 | drawinglayer::attribute::LineStartEndAttribute aEnd; |
618 | 0 | if (pen->penDataFlags & EmfPlusPenDataEndCap) |
619 | 0 | { |
620 | 0 | if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap) |
621 | 0 | && (pen->customEndCap->polygon.begin()->count() > 1)) |
622 | 0 | aEnd = drawinglayer::attribute::LineStartEndAttribute( |
623 | 0 | pen->customEndCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale |
624 | 0 | * pen->customEndCap->widthScale * pen->penWidth, |
625 | 0 | pen->customEndCap->polygon, false); |
626 | 0 | else |
627 | 0 | aEnd = EmfPlusHelperData::CreateLineEnd(pen->endCap, pen->penWidth); |
628 | 0 | } |
629 | |
|
630 | 0 | if (pen->GetColor().IsTransparent()) |
631 | 0 | { |
632 | 0 | drawinglayer::primitive2d::Primitive2DContainer aContainer; |
633 | 0 | if (aStart.isDefault() && aEnd.isDefault()) |
634 | 0 | aContainer.append( |
635 | 0 | new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( |
636 | 0 | polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale))); |
637 | 0 | else |
638 | 0 | { |
639 | 0 | aContainer.resize(polygon.count()); |
640 | 0 | for (sal_uInt32 i = 0; i < polygon.count(); i++) |
641 | 0 | aContainer[i] = |
642 | 0 | new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D( |
643 | 0 | polygon.getB2DPolygon(i), lineAttribute, |
644 | 0 | pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd); |
645 | 0 | } |
646 | 0 | mrTargetHolders.Current().append( |
647 | 0 | new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( |
648 | 0 | std::move(aContainer), (255 - pen->GetColor().GetAlpha()) / 255.0)); |
649 | 0 | } |
650 | 0 | else |
651 | 0 | { |
652 | 0 | if (aStart.isDefault() && aEnd.isDefault()) |
653 | 0 | mrTargetHolders.Current().append( |
654 | 0 | new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D( |
655 | 0 | polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale))); |
656 | 0 | else |
657 | 0 | for (sal_uInt32 i = 0; i < polygon.count(); i++) |
658 | 0 | { |
659 | 0 | mrTargetHolders.Current().append( |
660 | 0 | new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D( |
661 | 0 | polygon.getB2DPolygon(i), lineAttribute, |
662 | 0 | pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd)); |
663 | 0 | } |
664 | 0 | } |
665 | 0 | mrPropertyHolders.Current().setLineColor(pen->GetColor().getBColor()); |
666 | 0 | mrPropertyHolders.Current().setLineColorActive(true); |
667 | 0 | mrPropertyHolders.Current().setFillColorActive(false); |
668 | 0 | } |
669 | | |
670 | | void EmfPlusHelperData::EMFPPlusFillPolygonSolidColor(const ::basegfx::B2DPolyPolygon& polygon, Color const& color) |
671 | 0 | { |
672 | 0 | if (color.GetAlpha() == 0) |
673 | 0 | return; |
674 | | |
675 | 0 | if (!color.IsTransparent()) |
676 | 0 | { |
677 | | // not transparent |
678 | 0 | mrTargetHolders.Current().append( |
679 | 0 | new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( |
680 | 0 | polygon, |
681 | 0 | color.getBColor())); |
682 | 0 | } |
683 | 0 | else |
684 | 0 | { |
685 | | // transparent |
686 | 0 | mrTargetHolders.Current().append( |
687 | 0 | new drawinglayer::primitive2d::PolyPolygonRGBAPrimitive2D( |
688 | 0 | polygon, |
689 | 0 | color.getBColor(), |
690 | 0 | (255 - color.GetAlpha()) / 255.0)); |
691 | 0 | } |
692 | 0 | } |
693 | | |
694 | | void EmfPlusHelperData::EMFPPlusFillPolygon(const ::basegfx::B2DPolyPolygon& polygon, const bool isColor, const sal_uInt32 brushIndexOrColor) |
695 | 0 | { |
696 | 0 | if (!polygon.count()) |
697 | 0 | return; |
698 | | |
699 | 0 | if (isColor) // use Color |
700 | 0 | { |
701 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, ARGB color: 0x" << std::hex << brushIndexOrColor << std::dec); |
702 | | |
703 | | // EMF Alpha (1 byte): An 8-bit unsigned integer that specifies the transparency of the background, |
704 | | // ranging from 0 for completely transparent to 0xFF for completely opaque. |
705 | 0 | const Color color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff); |
706 | 0 | EMFPPlusFillPolygonSolidColor(polygon, color); |
707 | |
|
708 | 0 | mrPropertyHolders.Current().setFillColor(color.getBColor()); |
709 | 0 | mrPropertyHolders.Current().setFillColorActive(true); |
710 | 0 | mrPropertyHolders.Current().setLineColorActive(false); |
711 | 0 | } |
712 | 0 | else // use Brush |
713 | 0 | { |
714 | 0 | EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get()); |
715 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, brush slot: " << brushIndexOrColor << " (brush type: " << (brush ? brush->GetType() : -1) << ")"); |
716 | | |
717 | | // give up in case something wrong happened |
718 | 0 | if( !brush ) |
719 | 0 | return; |
720 | | |
721 | 0 | mrPropertyHolders.Current().setFillColorActive(false); |
722 | 0 | mrPropertyHolders.Current().setLineColorActive(false); |
723 | |
|
724 | 0 | if (brush->type == BrushTypeSolidColor) |
725 | 0 | { |
726 | 0 | Color fillColor = brush->solidColor; |
727 | 0 | EMFPPlusFillPolygonSolidColor(polygon, fillColor); |
728 | 0 | } |
729 | 0 | else if (brush->type == BrushTypeHatchFill) |
730 | 0 | { |
731 | | // EMF+ like hatching is currently not supported. These are just color blends which serve as an approximation for some of them |
732 | | // for the others the hatch "background" color (secondColor in brush) is used. |
733 | |
|
734 | 0 | bool isHatchBlend = true; |
735 | 0 | double blendFactor = 0.0; |
736 | |
|
737 | 0 | switch (brush->hatchStyle) |
738 | 0 | { |
739 | 0 | case HatchStyle05Percent: blendFactor = 0.05; break; |
740 | 0 | case HatchStyle10Percent: blendFactor = 0.10; break; |
741 | 0 | case HatchStyle20Percent: blendFactor = 0.20; break; |
742 | 0 | case HatchStyle25Percent: blendFactor = 0.25; break; |
743 | 0 | case HatchStyle30Percent: blendFactor = 0.30; break; |
744 | 0 | case HatchStyle40Percent: blendFactor = 0.40; break; |
745 | 0 | case HatchStyle50Percent: blendFactor = 0.50; break; |
746 | 0 | case HatchStyle60Percent: blendFactor = 0.60; break; |
747 | 0 | case HatchStyle70Percent: blendFactor = 0.70; break; |
748 | 0 | case HatchStyle75Percent: blendFactor = 0.75; break; |
749 | 0 | case HatchStyle80Percent: blendFactor = 0.80; break; |
750 | 0 | case HatchStyle90Percent: blendFactor = 0.90; break; |
751 | 0 | default: |
752 | 0 | isHatchBlend = false; |
753 | 0 | break; |
754 | 0 | } |
755 | 0 | Color fillColor; |
756 | 0 | if (isHatchBlend) |
757 | 0 | { |
758 | 0 | fillColor = brush->solidColor; |
759 | 0 | fillColor.Merge(brush->secondColor, static_cast<sal_uInt8>(255 * blendFactor)); |
760 | 0 | } |
761 | 0 | else |
762 | 0 | { |
763 | 0 | fillColor = brush->secondColor; |
764 | 0 | } |
765 | | // temporal solution: create a solid colored polygon |
766 | | // TODO create a 'real' hatching primitive |
767 | 0 | mrTargetHolders.Current().append( |
768 | 0 | new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D( |
769 | 0 | polygon, |
770 | 0 | fillColor.getBColor())); |
771 | 0 | } |
772 | 0 | else if (brush->type == BrushTypeTextureFill) |
773 | 0 | { |
774 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\tTODO: implement BrushTypeTextureFill brush"); |
775 | 0 | } |
776 | 0 | else if (brush->type == BrushTypePathGradient || brush->type == BrushTypeLinearGradient) |
777 | | |
778 | 0 | { |
779 | 0 | if (brush->type == BrushTypePathGradient && !(brush->additionalFlags & 0x1)) |
780 | 0 | { |
781 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t TODO Implement displaying BrushTypePathGradient with Boundary: "); |
782 | 0 | } |
783 | 0 | ::basegfx::B2DHomMatrix aTextureTransformation; |
784 | |
|
785 | 0 | if (brush->hasTransformation) { |
786 | 0 | aTextureTransformation = brush->brush_transformation; |
787 | | |
788 | | // adjust aTextureTransformation for our world space: |
789 | | // -> revert the mapping -> apply the transformation -> map back |
790 | 0 | basegfx::B2DHomMatrix aInvertedMapTrasform(maMapTransform); |
791 | 0 | aInvertedMapTrasform.invert(); |
792 | 0 | aTextureTransformation = maMapTransform * aTextureTransformation * aInvertedMapTrasform; |
793 | 0 | } |
794 | | |
795 | | // select the stored colors |
796 | 0 | const basegfx::BColor aStartColor = brush->solidColor.getBColor(); |
797 | 0 | const basegfx::BColor aEndColor = brush->secondColor.getBColor(); |
798 | 0 | drawinglayer::primitive2d::SvgGradientEntryVector aVector; |
799 | |
|
800 | 0 | if (brush->blendPositions) |
801 | 0 | { |
802 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t\tUse blend"); |
803 | | |
804 | | // store the blendpoints in the vector |
805 | 0 | for (sal_uInt32 i = 0; i < brush->blendPoints; i++) |
806 | 0 | { |
807 | 0 | const double aBlendPoint = brush->blendPositions[i]; |
808 | 0 | basegfx::BColor aColor; |
809 | 0 | aColor.setGreen(aStartColor.getGreen() + brush->blendFactors[i] * (aEndColor.getGreen() - aStartColor.getGreen())); |
810 | 0 | aColor.setBlue (aStartColor.getBlue() + brush->blendFactors[i] * (aEndColor.getBlue() - aStartColor.getBlue())); |
811 | 0 | aColor.setRed (aStartColor.getRed() + brush->blendFactors[i] * (aEndColor.getRed() - aStartColor.getRed())); |
812 | 0 | const double aAlpha = brush->solidColor.GetAlpha() + brush->blendFactors[i] * (brush->secondColor.GetAlpha() - brush->solidColor.GetAlpha()); |
813 | 0 | aVector.emplace_back(aBlendPoint, aColor, aAlpha / 255.0); |
814 | 0 | } |
815 | 0 | } |
816 | 0 | else if (brush->colorblendPositions) |
817 | 0 | { |
818 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t\tUse color blend"); |
819 | | |
820 | | // store the colorBlends in the vector |
821 | 0 | for (sal_uInt32 i = 0; i < brush->colorblendPoints; i++) |
822 | 0 | { |
823 | 0 | const double aBlendPoint = brush->colorblendPositions[i]; |
824 | 0 | const basegfx::BColor aColor = brush->colorblendColors[i].getBColor(); |
825 | 0 | aVector.emplace_back(aBlendPoint, aColor, brush->colorblendColors[i].GetAlpha() / 255.0); |
826 | 0 | } |
827 | 0 | } |
828 | 0 | else // ok, no extra points: just start and end |
829 | 0 | { |
830 | 0 | aVector.emplace_back(0.0, aStartColor, brush->solidColor.GetAlpha() / 255.0); |
831 | 0 | aVector.emplace_back(1.0, aEndColor, brush->secondColor.GetAlpha() / 255.0); |
832 | 0 | } |
833 | | |
834 | | // get the polygon range to be able to map the start/end/center point correctly |
835 | | // therefore, create a mapping and invert it |
836 | 0 | basegfx::B2DRange aPolygonRange= polygon.getB2DRange(); |
837 | 0 | basegfx::B2DHomMatrix aPolygonTransformation = basegfx::utils::createScaleTranslateB2DHomMatrix( |
838 | 0 | aPolygonRange.getWidth(),aPolygonRange.getHeight(), |
839 | 0 | aPolygonRange.getMinX(), aPolygonRange.getMinY()); |
840 | 0 | aPolygonTransformation.invert(); |
841 | |
|
842 | 0 | if (brush->type == BrushTypeLinearGradient) |
843 | 0 | { |
844 | | // support for public enum EmfPlusWrapMode |
845 | 0 | basegfx::B2DPoint aStartPoint = Map(brush->firstPointX, 0.0); |
846 | 0 | aStartPoint = aPolygonTransformation * aStartPoint; |
847 | 0 | basegfx::B2DPoint aEndPoint = Map(brush->firstPointX + brush->aWidth, 0.0); |
848 | 0 | aEndPoint = aPolygonTransformation * aEndPoint; |
849 | | |
850 | | // support for public enum EmfPlusWrapMode |
851 | 0 | drawinglayer::primitive2d::SpreadMethod aSpreadMethod(drawinglayer::primitive2d::SpreadMethod::Pad); |
852 | 0 | switch(brush->wrapMode) |
853 | 0 | { |
854 | 0 | case WrapModeTile: |
855 | 0 | case WrapModeTileFlipY: |
856 | 0 | { |
857 | 0 | aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Repeat; |
858 | 0 | break; |
859 | 0 | } |
860 | 0 | case WrapModeTileFlipX: |
861 | 0 | case WrapModeTileFlipXY: |
862 | 0 | { |
863 | 0 | aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Reflect; |
864 | 0 | break; |
865 | 0 | } |
866 | 0 | default: |
867 | 0 | break; |
868 | 0 | } |
869 | | |
870 | | // create the same one used for SVG |
871 | 0 | mrTargetHolders.Current().append( |
872 | 0 | new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D( |
873 | 0 | aTextureTransformation, |
874 | 0 | polygon, |
875 | 0 | std::move(aVector), |
876 | 0 | aStartPoint, |
877 | 0 | aEndPoint, |
878 | 0 | false, // do not use UnitCoordinates |
879 | 0 | aSpreadMethod)); |
880 | 0 | } |
881 | 0 | else // BrushTypePathGradient |
882 | 0 | { // TODO The PathGradient is not implemented, and Radial Gradient is used instead |
883 | 0 | basegfx::B2DPoint aCenterPoint = Map(brush->firstPointX, brush->firstPointY); |
884 | 0 | aCenterPoint = aPolygonTransformation * aCenterPoint; |
885 | | |
886 | | // create the same one used for SVG |
887 | 0 | mrTargetHolders.Current().append( |
888 | 0 | new drawinglayer::primitive2d::SvgRadialGradientPrimitive2D( |
889 | 0 | aTextureTransformation, |
890 | 0 | polygon, |
891 | 0 | std::move(aVector), |
892 | 0 | aCenterPoint, |
893 | 0 | 0.7, // relative radius little bigger to cover all elements |
894 | 0 | true, // use UnitCoordinates to stretch the gradient |
895 | 0 | drawinglayer::primitive2d::SpreadMethod::Pad, |
896 | 0 | nullptr)); |
897 | 0 | } |
898 | 0 | } |
899 | 0 | } |
900 | 0 | } |
901 | | |
902 | | const sal_uInt16 LOWERGAMMA = 1000; |
903 | | const sal_uInt16 UPPERGAMMA = 2200; |
904 | | |
905 | | EmfPlusHelperData::EmfPlusHelperData( |
906 | | SvMemoryStream& rMS, |
907 | | wmfemfhelper::TargetHolders& rTargetHolders, |
908 | | wmfemfhelper::PropertyHolders& rPropertyHolders) |
909 | 0 | : mfPageScale(0.0), |
910 | 0 | mnOriginX(0), |
911 | 0 | mnOriginY(0), |
912 | 0 | mnHDPI(0), |
913 | 0 | mnVDPI(0), |
914 | 0 | mbSetTextContrast(false), |
915 | 0 | mnTextContrast(0), |
916 | 0 | mnFrameLeft(0), |
917 | 0 | mnFrameTop(0), |
918 | 0 | mnFrameRight(0), |
919 | 0 | mnFrameBottom(0), |
920 | 0 | mnPixX(0), |
921 | 0 | mnPixY(0), |
922 | 0 | mnMmX(0), |
923 | 0 | mnMmY(0), |
924 | 0 | mbMultipart(false), |
925 | 0 | mMFlags(0), |
926 | 0 | mdExtractedXScale(1.0), |
927 | 0 | mdExtractedYScale(1.0), |
928 | 0 | mrTargetHolders(rTargetHolders), |
929 | 0 | mrPropertyHolders(rPropertyHolders), |
930 | 0 | bIsGetDCProcessing(false) |
931 | 0 | { |
932 | 0 | rMS.ReadInt32(mnFrameLeft).ReadInt32(mnFrameTop).ReadInt32(mnFrameRight).ReadInt32(mnFrameBottom); |
933 | 0 | SAL_INFO("drawinglayer.emf", "EMF+ picture frame: " << mnFrameLeft << "," << mnFrameTop << " - " << mnFrameRight << "," << mnFrameBottom); |
934 | 0 | rMS.ReadInt32(mnPixX).ReadInt32(mnPixY).ReadInt32(mnMmX).ReadInt32(mnMmY); |
935 | 0 | SAL_INFO("drawinglayer.emf", "EMF+ ref device pixel size: " << mnPixX << "x" << mnPixY << " mm size: " << mnMmX << "x" << mnMmY); |
936 | 0 | readXForm(rMS, maBaseTransform); |
937 | 0 | SAL_INFO("drawinglayer.emf", "EMF+ base transform: " << maBaseTransform); |
938 | 0 | mappingChanged(); |
939 | 0 | } |
940 | | |
941 | | EmfPlusHelperData::~EmfPlusHelperData() |
942 | 0 | { |
943 | 0 | } |
944 | | |
945 | | ::basegfx::B2DPolyPolygon EmfPlusHelperData::combineClip(::basegfx::B2DPolyPolygon const & leftPolygon, int combineMode, ::basegfx::B2DPolyPolygon const & rightPolygon) |
946 | 0 | { |
947 | 0 | basegfx::B2DPolyPolygon aClippedPolyPolygon; |
948 | 0 | switch (combineMode) |
949 | 0 | { |
950 | 0 | case EmfPlusCombineModeReplace: |
951 | 0 | { |
952 | 0 | aClippedPolyPolygon = rightPolygon; |
953 | 0 | break; |
954 | 0 | } |
955 | 0 | case EmfPlusCombineModeIntersect: |
956 | 0 | { |
957 | 0 | aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon( |
958 | 0 | leftPolygon, rightPolygon, true, false); |
959 | 0 | break; |
960 | 0 | } |
961 | 0 | case EmfPlusCombineModeUnion: |
962 | 0 | { |
963 | 0 | aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationOr(leftPolygon, rightPolygon); |
964 | 0 | break; |
965 | 0 | } |
966 | 0 | case EmfPlusCombineModeXOR: |
967 | 0 | { |
968 | 0 | aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationXor(leftPolygon, rightPolygon); |
969 | 0 | break; |
970 | 0 | } |
971 | 0 | case EmfPlusCombineModeExclude: |
972 | 0 | { |
973 | | // Replaces the existing region with the part of itself that is not in the new region. |
974 | 0 | aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(leftPolygon, rightPolygon); |
975 | 0 | break; |
976 | 0 | } |
977 | 0 | case EmfPlusCombineModeComplement: |
978 | 0 | { |
979 | | // Replaces the existing region with the part of the new region that is not in the existing region. |
980 | 0 | aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(rightPolygon, leftPolygon); |
981 | 0 | break; |
982 | 0 | } |
983 | 0 | } |
984 | 0 | return aClippedPolyPolygon; |
985 | 0 | } |
986 | | |
987 | | void EmfPlusHelperData::processEmfPlusData( |
988 | | SvMemoryStream& rMS, |
989 | | const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/) |
990 | 0 | { |
991 | 0 | sal_uInt64 length = rMS.GetSize(); |
992 | |
|
993 | 0 | if (length < 12) |
994 | 0 | SAL_WARN("drawinglayer.emf", "length is less than required header size"); |
995 | | |
996 | | // 12 is minimal valid EMF+ record size; remaining bytes are padding |
997 | 0 | while (length >= 12) |
998 | 0 | { |
999 | 0 | sal_uInt16 type, flags; |
1000 | 0 | sal_uInt32 size, dataSize; |
1001 | 0 | sal_uInt64 next; |
1002 | |
|
1003 | 0 | rMS.ReadUInt16(type).ReadUInt16(flags).ReadUInt32(size).ReadUInt32(dataSize); |
1004 | |
|
1005 | 0 | next = rMS.Tell() + (size - 12); |
1006 | |
|
1007 | 0 | if (size < 12) |
1008 | 0 | { |
1009 | 0 | SAL_WARN("drawinglayer.emf", "Size field is less than 12 bytes"); |
1010 | 0 | break; |
1011 | 0 | } |
1012 | 0 | else if (size > length) |
1013 | 0 | { |
1014 | 0 | SAL_WARN("drawinglayer.emf", "Size field is greater than bytes left"); |
1015 | 0 | break; |
1016 | 0 | } |
1017 | | |
1018 | 0 | if (dataSize > (size - 12)) |
1019 | 0 | { |
1020 | 0 | SAL_WARN("drawinglayer.emf", "DataSize field is greater than Size-12"); |
1021 | 0 | break; |
1022 | 0 | } |
1023 | | |
1024 | 0 | SAL_INFO("drawinglayer.emf", "EMF+ " << emfTypeToName(type) << " (0x" << std::hex << type << ")" << std::dec); |
1025 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t record size: " << size); |
1026 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t flags: 0x" << std::hex << flags << std::dec); |
1027 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t data size: " << dataSize); |
1028 | | |
1029 | 0 | if (bIsGetDCProcessing) |
1030 | 0 | { |
1031 | 0 | if (aGetDCState.getClipPolyPolygonActive()) |
1032 | 0 | { |
1033 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Restore region to GetDC saved"); |
1034 | 0 | wmfemfhelper::HandleNewClipRegion(aGetDCState.getClipPolyPolygon(), mrTargetHolders, |
1035 | 0 | mrPropertyHolders); |
1036 | 0 | } |
1037 | 0 | else |
1038 | 0 | { |
1039 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping"); |
1040 | 0 | wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, |
1041 | 0 | mrPropertyHolders); |
1042 | 0 | } |
1043 | 0 | bIsGetDCProcessing = false; |
1044 | 0 | } |
1045 | 0 | if (type == EmfPlusRecordTypeObject && ((mbMultipart && (flags & 0x7fff) == (mMFlags & 0x7fff)) || (flags & 0x8000))) |
1046 | 0 | { |
1047 | 0 | if (!mbMultipart) |
1048 | 0 | { |
1049 | 0 | mbMultipart = true; |
1050 | 0 | mMFlags = flags; |
1051 | 0 | mMStream.Seek(0); |
1052 | 0 | } |
1053 | |
|
1054 | 0 | OSL_ENSURE(dataSize >= 4, "No room for TotalObjectSize in EmfPlusContinuedObjectRecord"); |
1055 | | |
1056 | | // 1st 4 bytes are TotalObjectSize |
1057 | 0 | mMStream.WriteBytes(static_cast<const char *>(rMS.GetData()) + rMS.Tell() + 4, dataSize - 4); |
1058 | 0 | SAL_INFO("drawinglayer.emf", "EMF+ read next object part size: " << size << " type: " << type << " flags: " << flags << " data size: " << dataSize); |
1059 | 0 | } |
1060 | 0 | else |
1061 | 0 | { |
1062 | 0 | if (mbMultipart) |
1063 | 0 | { |
1064 | 0 | SAL_INFO("drawinglayer.emf", "EMF+ multipart record flags: " << mMFlags); |
1065 | 0 | mMStream.Seek(0); |
1066 | 0 | processObjectRecord(mMStream, mMFlags, 0, true); |
1067 | 0 | } |
1068 | | |
1069 | 0 | mbMultipart = false; |
1070 | 0 | } |
1071 | | |
1072 | 0 | if (type != EmfPlusRecordTypeObject || !(flags & 0x8000)) |
1073 | 0 | { |
1074 | 0 | switch (type) |
1075 | 0 | { |
1076 | 0 | case EmfPlusRecordTypeHeader: |
1077 | 0 | { |
1078 | 0 | sal_uInt32 version, emfPlusFlags; |
1079 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\tDual: " << ((flags & 1) ? "true" : "false")); |
1080 | | |
1081 | 0 | rMS.ReadUInt32(version).ReadUInt32(emfPlusFlags).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI); |
1082 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\tVersion: 0x" << std::hex << version); |
1083 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\tEmf+ Flags: 0x" << emfPlusFlags << std::dec); |
1084 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\tMetafile was recorded with a reference device context for " << ((emfPlusFlags & 1) ? "video display" : "printer")); |
1085 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\tHorizontal DPI: " << mnHDPI); |
1086 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\tVertical DPI: " << mnVDPI); |
1087 | 0 | break; |
1088 | 0 | } |
1089 | 0 | case EmfPlusRecordTypeEndOfFile: |
1090 | 0 | { |
1091 | 0 | break; |
1092 | 0 | } |
1093 | 0 | case EmfPlusRecordTypeComment: |
1094 | 0 | { |
1095 | | #if OSL_DEBUG_LEVEL > 1 |
1096 | | unsigned char data; |
1097 | | OUString hexdata; |
1098 | | |
1099 | | SAL_INFO("drawinglayer.emf", "EMF+\tDatasize: 0x" << std::hex << dataSize << std::dec); |
1100 | | |
1101 | | for (sal_uInt32 i=0; i<dataSize; i++) |
1102 | | { |
1103 | | rMS.ReadUChar(data); |
1104 | | |
1105 | | if (i % 16 == 0) |
1106 | | hexdata += "\n"; |
1107 | | |
1108 | | OUString padding; |
1109 | | if ((data & 0xF0) == 0) |
1110 | | padding = "0"; |
1111 | | |
1112 | | hexdata += "0x" + padding + OUString::number(data, 16) + " "; |
1113 | | } |
1114 | | |
1115 | | SAL_INFO("drawinglayer.emf", "EMF+\t" << hexdata); |
1116 | | #endif |
1117 | 0 | break; |
1118 | 0 | } |
1119 | 0 | case EmfPlusRecordTypeGetDC: |
1120 | 0 | { |
1121 | 0 | bIsGetDCProcessing = true; |
1122 | 0 | aGetDCState = mrPropertyHolders.Current(); |
1123 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\tAlready used in svtools wmf/emf filter parser"); |
1124 | 0 | break; |
1125 | 0 | } |
1126 | 0 | case EmfPlusRecordTypeObject: |
1127 | 0 | { |
1128 | 0 | processObjectRecord(rMS, flags, dataSize); |
1129 | 0 | break; |
1130 | 0 | } |
1131 | 0 | case EmfPlusRecordTypeFillPie: |
1132 | 0 | case EmfPlusRecordTypeDrawPie: |
1133 | 0 | case EmfPlusRecordTypeDrawArc: |
1134 | 0 | { |
1135 | 0 | float startAngle, sweepAngle; |
1136 | | |
1137 | | // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used |
1138 | 0 | sal_uInt32 brushIndexOrColor = 999; |
1139 | |
|
1140 | 0 | if (type == EmfPlusRecordTypeFillPie) |
1141 | 0 | { |
1142 | 0 | rMS.ReadUInt32(brushIndexOrColor); |
1143 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t FillPie colorOrIndex: " << brushIndexOrColor); |
1144 | 0 | } |
1145 | 0 | else if (type == EmfPlusRecordTypeDrawPie) |
1146 | 0 | { |
1147 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t DrawPie"); |
1148 | 0 | } |
1149 | 0 | else |
1150 | 0 | { |
1151 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t DrawArc"); |
1152 | 0 | } |
1153 | | |
1154 | 0 | rMS.ReadFloat(startAngle).ReadFloat(sweepAngle); |
1155 | 0 | float dx, dy, dw, dh; |
1156 | 0 | ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000)); |
1157 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh); |
1158 | 0 | startAngle = basegfx::deg2rad(startAngle); |
1159 | 0 | sweepAngle = basegfx::deg2rad(sweepAngle); |
1160 | 0 | float endAngle = startAngle + sweepAngle; |
1161 | 0 | startAngle = fmodf(startAngle, static_cast<float>(M_PI * 2)); |
1162 | |
|
1163 | 0 | if (startAngle < 0.0) |
1164 | 0 | { |
1165 | 0 | startAngle += static_cast<float>(M_PI * 2.0); |
1166 | 0 | } |
1167 | 0 | endAngle = fmodf(endAngle, static_cast<float>(M_PI * 2.0)); |
1168 | |
|
1169 | 0 | if (endAngle < 0.0) |
1170 | 0 | { |
1171 | 0 | endAngle += static_cast<float>(M_PI * 2.0); |
1172 | 0 | } |
1173 | 0 | if (sweepAngle < 0) |
1174 | 0 | { |
1175 | 0 | std::swap(endAngle, startAngle); |
1176 | 0 | } |
1177 | |
|
1178 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Adjusted angles: start " << |
1179 | 0 | basegfx::rad2deg(startAngle) << ", end: " << basegfx::rad2deg(endAngle) << |
1180 | 0 | " startAngle: " << startAngle << " sweepAngle: " << sweepAngle); |
1181 | 0 | const ::basegfx::B2DPoint centerPoint(dx + 0.5 * dw, dy + 0.5 * dh); |
1182 | 0 | ::basegfx::B2DPolygon polygon( |
1183 | 0 | ::basegfx::utils::createPolygonFromEllipseSegment(centerPoint, |
1184 | 0 | 0.5 * dw, 0.5 * dh, |
1185 | 0 | startAngle, endAngle)); |
1186 | 0 | if (type != EmfPlusRecordTypeDrawArc) |
1187 | 0 | { |
1188 | 0 | polygon.append(centerPoint); |
1189 | 0 | polygon.setClosed(true); |
1190 | 0 | } |
1191 | 0 | ::basegfx::B2DPolyPolygon polyPolygon(polygon); |
1192 | 0 | polyPolygon.transform(maMapTransform); |
1193 | 0 | if (type == EmfPlusRecordTypeFillPie) |
1194 | 0 | EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor); |
1195 | 0 | else |
1196 | 0 | EMFPPlusDrawPolygon(polyPolygon, flags & 0xff); |
1197 | 0 | } |
1198 | 0 | break; |
1199 | 0 | case EmfPlusRecordTypeFillPath: |
1200 | 0 | { |
1201 | 0 | sal_uInt32 index = flags & 0xff; |
1202 | 0 | sal_uInt32 brushIndexOrColor; |
1203 | 0 | rMS.ReadUInt32(brushIndexOrColor); |
1204 | 0 | SAL_INFO("drawinglayer.emf", "EMF+ FillPath slot: " << index); |
1205 | | |
1206 | 0 | EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[index].get()); |
1207 | 0 | if (path) |
1208 | 0 | EMFPPlusFillPolygon(path->GetPolygon(*this), flags & 0x8000, brushIndexOrColor); |
1209 | 0 | else |
1210 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillPath missing path"); |
1211 | 0 | } |
1212 | 0 | break; |
1213 | 0 | case EmfPlusRecordTypeFillRegion: |
1214 | 0 | { |
1215 | 0 | sal_uInt32 index = flags & 0xff; |
1216 | 0 | sal_uInt32 brushIndexOrColor; |
1217 | 0 | rMS.ReadUInt32(brushIndexOrColor); |
1218 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t FillRegion slot: " << index); |
1219 | | |
1220 | 0 | EMFPRegion* region = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get()); |
1221 | 0 | if (region) |
1222 | 0 | EMFPPlusFillPolygon(region->regionPolyPolygon, flags & 0x8000, brushIndexOrColor); |
1223 | 0 | else |
1224 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillRegion missing region"); |
1225 | 0 | } |
1226 | 0 | break; |
1227 | 0 | case EmfPlusRecordTypeDrawEllipse: |
1228 | 0 | case EmfPlusRecordTypeFillEllipse: |
1229 | 0 | { |
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 | 0 | sal_uInt32 brushIndexOrColor = 1234567; |
1234 | |
|
1235 | 0 | if (type == EmfPlusRecordTypeFillEllipse) |
1236 | 0 | { |
1237 | 0 | rMS.ReadUInt32(brushIndexOrColor); |
1238 | 0 | } |
1239 | |
|
1240 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff)); |
1241 | 0 | float dx, dy, dw, dh; |
1242 | 0 | ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000)); |
1243 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh); |
1244 | 0 | ::basegfx::B2DPolyPolygon polyPolygon( |
1245 | 0 | ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(dx + 0.5 * dw, dy + 0.5 * dh), |
1246 | 0 | 0.5 * dw, 0.5 * dh)); |
1247 | 0 | polyPolygon.transform(maMapTransform); |
1248 | 0 | if (type == EmfPlusRecordTypeFillEllipse) |
1249 | 0 | EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor); |
1250 | 0 | else |
1251 | 0 | EMFPPlusDrawPolygon(polyPolygon, flags & 0xff); |
1252 | 0 | } |
1253 | 0 | break; |
1254 | 0 | case EmfPlusRecordTypeFillRects: |
1255 | 0 | case EmfPlusRecordTypeDrawRects: |
1256 | 0 | { |
1257 | | // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used |
1258 | 0 | sal_uInt32 brushIndexOrColor = 999; |
1259 | 0 | ::basegfx::B2DPolyPolygon polyPolygon; |
1260 | 0 | sal_uInt32 rectangles; |
1261 | 0 | float x, y, width, height; |
1262 | 0 | const bool isColor = (flags & 0x8000); |
1263 | 0 | ::basegfx::B2DPolygon polygon; |
1264 | |
|
1265 | 0 | if (EmfPlusRecordTypeFillRects == type) |
1266 | 0 | { |
1267 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t FillRects"); |
1268 | 0 | rMS.ReadUInt32(brushIndexOrColor); |
1269 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t" << (isColor ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec); |
1270 | 0 | } |
1271 | 0 | else |
1272 | 0 | { |
1273 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t DrawRects"); |
1274 | 0 | } |
1275 | | |
1276 | 0 | rMS.ReadUInt32(rectangles); |
1277 | 0 | for (sal_uInt32 i = 0; i < rectangles; i++) |
1278 | 0 | { |
1279 | 0 | ReadRectangle(rMS, x, y, width, height, bool(flags & 0x4000)); |
1280 | 0 | polygon.clear(); |
1281 | 0 | polygon.append(Map(x, y)); |
1282 | 0 | polygon.append(Map(x + width, y)); |
1283 | 0 | polygon.append(Map(x + width, y + height)); |
1284 | 0 | polygon.append(Map(x, y + height)); |
1285 | 0 | polygon.setClosed(true); |
1286 | |
|
1287 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t\t rectangle: " << x << ", "<< y << " " << width << "x" << height); |
1288 | 0 | polyPolygon.append(polygon); |
1289 | 0 | } |
1290 | 0 | if (type == EmfPlusRecordTypeFillRects) |
1291 | 0 | EMFPPlusFillPolygon(polyPolygon, isColor, brushIndexOrColor); |
1292 | 0 | else |
1293 | 0 | EMFPPlusDrawPolygon(polyPolygon, flags & 0xff); |
1294 | 0 | break; |
1295 | 0 | } |
1296 | 0 | case EmfPlusRecordTypeFillPolygon: |
1297 | 0 | { |
1298 | 0 | sal_uInt32 brushIndexOrColor, points; |
1299 | |
|
1300 | 0 | rMS.ReadUInt32(brushIndexOrColor); |
1301 | 0 | rMS.ReadUInt32(points); |
1302 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points); |
1303 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << " : 0x" << std::hex << brushIndexOrColor << std::dec); |
1304 | | |
1305 | 0 | EMFPPath path(points, true); |
1306 | 0 | path.Read(rMS, flags); |
1307 | |
|
1308 | 0 | EMFPPlusFillPolygon(path.GetPolygon(*this), flags & 0x8000, brushIndexOrColor); |
1309 | 0 | break; |
1310 | 0 | } |
1311 | 0 | case EmfPlusRecordTypeDrawLines: |
1312 | 0 | { |
1313 | 0 | sal_uInt32 points; |
1314 | 0 | rMS.ReadUInt32(points); |
1315 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points); |
1316 | 0 | EMFPPath path(points, true); |
1317 | 0 | path.Read(rMS, flags); |
1318 | | |
1319 | | // 0x2000 bit indicates whether to draw an extra line between the last point |
1320 | | // and the first point, to close the shape. |
1321 | 0 | EMFPPlusDrawPolygon(path.GetPolygon(*this, true, (flags & 0x2000)), flags); |
1322 | |
|
1323 | 0 | break; |
1324 | 0 | } |
1325 | 0 | case EmfPlusRecordTypeDrawPath: |
1326 | 0 | { |
1327 | 0 | sal_uInt32 penIndex; |
1328 | 0 | rMS.ReadUInt32(penIndex); |
1329 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Pen: " << penIndex); |
1330 | | |
1331 | 0 | EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get()); |
1332 | 0 | if (path) |
1333 | 0 | EMFPPlusDrawPolygon(path->GetPolygon(*this), penIndex); |
1334 | 0 | else |
1335 | 0 | SAL_WARN("drawinglayer.emf", "\t\tEmfPlusRecordTypeDrawPath missing path"); |
1336 | | |
1337 | 0 | break; |
1338 | 0 | } |
1339 | 0 | case EmfPlusRecordTypeDrawBeziers: |
1340 | 0 | { |
1341 | 0 | sal_uInt32 aCount; |
1342 | 0 | float x1, y1, x2, y2, x3, y3, x4, y4; |
1343 | 0 | ::basegfx::B2DPolygon aPolygon; |
1344 | 0 | rMS.ReadUInt32(aCount); |
1345 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t DrawBeziers slot: " << (flags & 0xff)); |
1346 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Number of points: " << aCount); |
1347 | 0 | SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer.emf", |
1348 | 0 | "EMF+\t Bezier Draw not support number of points other than 4, 7, " |
1349 | 0 | "10, 13, 16..."); |
1350 | | |
1351 | 0 | if (aCount < 4) |
1352 | 0 | { |
1353 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t Bezier Draw does not support less " |
1354 | 0 | "than 4 points. Number of points: " |
1355 | 0 | << aCount); |
1356 | 0 | break; |
1357 | 0 | } |
1358 | | |
1359 | 0 | ReadPoint(rMS, x1, y1, flags); |
1360 | | // We need to add first starting point |
1361 | 0 | aPolygon.append(Map(x1, y1)); |
1362 | 0 | SAL_INFO("drawinglayer.emf", |
1363 | 0 | "EMF+\t Bezier starting point: " << x1 << "," << y1); |
1364 | 0 | for (sal_uInt32 i = 4; i <= aCount; i += 3) |
1365 | 0 | { |
1366 | 0 | ReadPoint(rMS, x2, y2, flags); |
1367 | 0 | ReadPoint(rMS, x3, y3, flags); |
1368 | 0 | ReadPoint(rMS, x4, y4, flags); |
1369 | |
|
1370 | 0 | SAL_INFO("drawinglayer.emf", |
1371 | 0 | "EMF+\t Bezier points: " << x2 << "," << y2 << " " << x3 << "," |
1372 | 0 | << y3 << " " << x4 << "," << y4); |
1373 | 0 | aPolygon.appendBezierSegment(Map(x2, y2), Map(x3, y3), Map(x4, y4)); |
1374 | 0 | } |
1375 | 0 | EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff); |
1376 | 0 | break; |
1377 | 0 | } |
1378 | 0 | case EmfPlusRecordTypeDrawCurve: |
1379 | 0 | { |
1380 | 0 | sal_uInt32 aOffset, aNumSegments, points; |
1381 | 0 | float aTension; |
1382 | 0 | rMS.ReadFloat(aTension); |
1383 | 0 | rMS.ReadUInt32(aOffset); |
1384 | 0 | rMS.ReadUInt32(aNumSegments); |
1385 | 0 | rMS.ReadUInt32(points); |
1386 | 0 | SAL_WARN("drawinglayer.emf", |
1387 | 0 | "EMF+\t Tension: " << aTension << " Offset: " << aOffset |
1388 | 0 | << " NumSegments: " << aNumSegments |
1389 | 0 | << " Points: " << points); |
1390 | | |
1391 | 0 | EMFPPath path(points, true); |
1392 | 0 | path.Read(rMS, flags); |
1393 | |
|
1394 | 0 | if (points >= 2) |
1395 | 0 | EMFPPlusDrawPolygon( |
1396 | 0 | path.GetCardinalSpline(*this, aTension, aOffset, aNumSegments), |
1397 | 0 | flags & 0xff); |
1398 | 0 | else |
1399 | 0 | SAL_WARN("drawinglayer.emf", "Not enough number of points"); |
1400 | 0 | break; |
1401 | 0 | } |
1402 | 0 | case EmfPlusRecordTypeDrawClosedCurve: |
1403 | 0 | case EmfPlusRecordTypeFillClosedCurve: |
1404 | 0 | { |
1405 | | // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used |
1406 | 0 | sal_uInt32 brushIndexOrColor = 999, points; |
1407 | 0 | float aTension; |
1408 | 0 | if (type == EmfPlusRecordTypeFillClosedCurve) |
1409 | 0 | { |
1410 | 0 | rMS.ReadUInt32(brushIndexOrColor); |
1411 | 0 | SAL_INFO( |
1412 | 0 | "drawinglayer.emf", |
1413 | 0 | "EMF+\t Fill Mode: " << (flags & 0x2000 ? "Winding" : "Alternate")); |
1414 | 0 | } |
1415 | 0 | rMS.ReadFloat(aTension); |
1416 | 0 | rMS.ReadUInt32(points); |
1417 | 0 | SAL_WARN("drawinglayer.emf", |
1418 | 0 | "EMF+\t Tension: " << aTension << " Points: " << points); |
1419 | 0 | SAL_INFO("drawinglayer.emf", |
1420 | 0 | "EMF+\t " << (flags & 0x8000 ? "Color" : "Brush index") << " : 0x" |
1421 | 0 | << std::hex << brushIndexOrColor << std::dec); |
1422 | 0 | if (points < 3) |
1423 | 0 | { |
1424 | 0 | SAL_WARN("drawinglayer.emf", "Not enough number of points"); |
1425 | 0 | break; |
1426 | 0 | } |
1427 | 0 | EMFPPath path(points, true); |
1428 | 0 | path.Read(rMS, flags); |
1429 | 0 | if (type == EmfPlusRecordTypeFillClosedCurve) |
1430 | 0 | EMFPPlusFillPolygon(path.GetClosedCardinalSpline(*this, aTension), |
1431 | 0 | flags & 0x8000, brushIndexOrColor); |
1432 | 0 | else |
1433 | 0 | EMFPPlusDrawPolygon(path.GetClosedCardinalSpline(*this, aTension), |
1434 | 0 | flags & 0xff); |
1435 | 0 | break; |
1436 | 0 | } |
1437 | 0 | case EmfPlusRecordTypeDrawImage: |
1438 | 0 | case EmfPlusRecordTypeDrawImagePoints: |
1439 | 0 | { |
1440 | 0 | sal_uInt32 imageAttributesId; |
1441 | 0 | sal_Int32 sourceUnit; |
1442 | 0 | rMS.ReadUInt32(imageAttributesId).ReadInt32(sourceUnit); |
1443 | 0 | SAL_INFO("drawinglayer.emf", |
1444 | 0 | "EMF+\t " << (type == EmfPlusRecordTypeDrawImage ? "DrawImage" |
1445 | 0 | : "DrawImagePoints") |
1446 | 0 | << " image attributes Id: " << imageAttributesId |
1447 | 0 | << " source unit: " << sourceUnit); |
1448 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t TODO: use image attributes"); |
1449 | | |
1450 | | // Source unit of measurement type must be 1 pixel |
1451 | 0 | if (EMFPImage* image = sourceUnit == UnitTypePixel ? |
1452 | 0 | dynamic_cast<EMFPImage*>(maEMFPObjects[flags & 0xff].get()) : |
1453 | 0 | nullptr) |
1454 | 0 | { |
1455 | 0 | float sx, sy, sw, sh; |
1456 | 0 | ReadRectangle(rMS, sx, sy, sw, sh); |
1457 | |
|
1458 | 0 | ::tools::Rectangle aSource(Point(sx, sy), Size(sw + 1, sh + 1)); |
1459 | 0 | SAL_INFO("drawinglayer.emf", |
1460 | 0 | "EMF+\t " |
1461 | 0 | << (type == EmfPlusRecordTypeDrawImage ? "DrawImage" |
1462 | 0 | : "DrawImagePoints") |
1463 | 0 | << " source rectangle: " << sx << "," << sy << " " << sw << "x" |
1464 | 0 | << sh); |
1465 | | |
1466 | 0 | float dx(0.), dy(0.), dw(0.), dh(0.); |
1467 | 0 | double fShearX = 0.0; |
1468 | 0 | double fShearY = 0.0; |
1469 | 0 | if (type == EmfPlusRecordTypeDrawImagePoints) |
1470 | 0 | { |
1471 | 0 | sal_uInt32 aCount; |
1472 | 0 | rMS.ReadUInt32(aCount); |
1473 | | |
1474 | | // Number of points used by DrawImagePoints. Exactly 3 points must be specified. |
1475 | 0 | if (aCount != 3) |
1476 | 0 | { |
1477 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t Wrong EMF+ file. Expected " |
1478 | 0 | "3 points, received: " |
1479 | 0 | << aCount); |
1480 | 0 | break; |
1481 | 0 | } |
1482 | 0 | float x1, y1, x2, y2, x3, y3; |
1483 | |
|
1484 | 0 | ReadPoint(rMS, x1, y1, flags); // upper-left point |
1485 | 0 | ReadPoint(rMS, x2, y2, flags); // upper-right |
1486 | 0 | ReadPoint(rMS, x3, y3, flags); // lower-left |
1487 | |
|
1488 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t destination points: " |
1489 | 0 | << x1 << "," << y1 << " " << x2 << "," |
1490 | 0 | << y2 << " " << x3 << "," << y3); |
1491 | 0 | dx = x1; |
1492 | 0 | dy = y2; |
1493 | 0 | dw = x2 - x1; |
1494 | 0 | dh = y3 - y1; |
1495 | 0 | fShearX = x3 - x1; |
1496 | 0 | fShearY = y2 - y1; |
1497 | 0 | } |
1498 | 0 | else if (type == EmfPlusRecordTypeDrawImage) |
1499 | 0 | ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000)); |
1500 | | |
1501 | 0 | SAL_INFO("drawinglayer.emf", |
1502 | 0 | "EMF+\t Rectangle: " << dx << "," << dy << " " << dw << "x" << dh); |
1503 | 0 | Size aSize; |
1504 | 0 | if (image->type == ImageDataTypeBitmap) |
1505 | 0 | { |
1506 | 0 | aSize = image->graphic.GetBitmap().GetSizePixel(); |
1507 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Bitmap size: " << aSize.Width() |
1508 | 0 | << "x" |
1509 | 0 | << aSize.Height()); |
1510 | 0 | if (sx < 0) |
1511 | 0 | { |
1512 | | // If src position is negative then we need shift image to right |
1513 | 0 | dx = dx + ((-sx) / sw) * dw; |
1514 | 0 | if (sx + sw <= aSize.Width()) |
1515 | 0 | dw = ((sw + sx) / sw) * dw; |
1516 | 0 | else |
1517 | 0 | dw = (aSize.Width() / sw) * dw; |
1518 | 0 | } |
1519 | 0 | 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 | 0 | dw = ((aSize.Width() - sx) / sw) * dw; |
1522 | |
|
1523 | 0 | if (sy < 0) |
1524 | 0 | { |
1525 | 0 | dy = dy + ((-sy) / sh) * dh; |
1526 | 0 | if (sy + sh <= aSize.Height()) |
1527 | 0 | dh = ((sh + sy) / sh) * dh; |
1528 | 0 | else |
1529 | 0 | dh = (aSize.Height() / sh) * dh; |
1530 | 0 | } |
1531 | 0 | else if (sy + sh > aSize.Height()) |
1532 | 0 | dh = ((aSize.Height() - sy) / sh) * dh; |
1533 | 0 | } |
1534 | 0 | else |
1535 | 0 | SAL_INFO( |
1536 | 0 | "drawinglayer.emf", |
1537 | 0 | "EMF+\t TODO: Add support for SrcRect to ImageDataTypeMetafile"); |
1538 | 0 | const ::basegfx::B2DPoint aDstPoint(dx, dy); |
1539 | 0 | const ::basegfx::B2DSize aDstSize(dw, dh); |
1540 | |
|
1541 | 0 | const basegfx::B2DHomMatrix aTransformMatrix |
1542 | 0 | = maMapTransform |
1543 | 0 | * basegfx::B2DHomMatrix( |
1544 | 0 | /* Row 0, Column 0 */ aDstSize.getWidth(), |
1545 | 0 | /* Row 0, Column 1 */ fShearX, |
1546 | 0 | /* Row 0, Column 2 */ aDstPoint.getX(), |
1547 | 0 | /* Row 1, Column 0 */ fShearY, |
1548 | 0 | /* Row 1, Column 1 */ aDstSize.getHeight(), |
1549 | 0 | /* Row 1, Column 2 */ aDstPoint.getY()); |
1550 | |
|
1551 | 0 | if (image->type == ImageDataTypeBitmap) |
1552 | 0 | { |
1553 | 0 | Bitmap aBmp(image->graphic.GetBitmap()); |
1554 | 0 | aBmp.Crop(aSource); |
1555 | 0 | aSize = aBmp.GetSizePixel(); |
1556 | 0 | if (aSize.Width() > 0 && aSize.Height() > 0) |
1557 | 0 | { |
1558 | 0 | mrTargetHolders.Current().append( |
1559 | 0 | new drawinglayer::primitive2d::BitmapPrimitive2D( |
1560 | 0 | aBmp, aTransformMatrix)); |
1561 | 0 | } |
1562 | 0 | else |
1563 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t warning: empty bitmap"); |
1564 | 0 | } |
1565 | 0 | else if (image->type == ImageDataTypeMetafile) |
1566 | 0 | { |
1567 | 0 | GDIMetaFile aGDI(image->graphic.GetGDIMetaFile()); |
1568 | 0 | aGDI.Clip(aSource); |
1569 | 0 | mrTargetHolders.Current().append( |
1570 | 0 | new drawinglayer::primitive2d::MetafilePrimitive2D(aTransformMatrix, |
1571 | 0 | aGDI)); |
1572 | 0 | } |
1573 | 0 | } |
1574 | 0 | else |
1575 | 0 | { |
1576 | 0 | SAL_WARN("drawinglayer.emf", |
1577 | 0 | "EMF+\tDrawImage(Points) Wrong EMF+ file. Only Unit Type Pixel is " |
1578 | 0 | "support by EMF+ specification for DrawImage(Points)"); |
1579 | 0 | } |
1580 | 0 | break; |
1581 | 0 | } |
1582 | 0 | case EmfPlusRecordTypeDrawString: |
1583 | 0 | { |
1584 | 0 | sal_uInt32 brushId, formatId, stringLength; |
1585 | 0 | rMS.ReadUInt32(brushId).ReadUInt32(formatId).ReadUInt32(stringLength); |
1586 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t FontId: " << OUString::number(flags & 0xFF)); |
1587 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t BrushId: " << BrushIDToString(flags, brushId)); |
1588 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t FormatId: " << formatId); |
1589 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Length: " << stringLength); |
1590 | | |
1591 | | // read the layout rectangle |
1592 | 0 | float lx, ly, lw, lh; |
1593 | 0 | rMS.ReadFloat(lx).ReadFloat(ly).ReadFloat(lw).ReadFloat(lh); |
1594 | |
|
1595 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh); |
1596 | | // parse the string |
1597 | 0 | const OUString text = read_uInt16s_ToOUString(rMS, stringLength); |
1598 | 0 | 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 | 0 | const EMFPStringFormat *stringFormat = dynamic_cast<EMFPStringFormat*>(maEMFPObjects[formatId & 0xff].get()); |
1601 | | // get the font from the flags |
1602 | 0 | const EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get()); |
1603 | 0 | if (!font) |
1604 | 0 | { |
1605 | 0 | break; |
1606 | 0 | } |
1607 | 0 | mrPropertyHolders.Current().setFont(vcl::Font(font->family, Size(font->emSize, font->emSize))); |
1608 | |
|
1609 | 0 | drawinglayer::attribute::FontAttribute fontAttribute( |
1610 | 0 | font->family, // font family |
1611 | 0 | u""_ustr, // (no) font style |
1612 | 0 | font->Bold() ? 8u : 1u, // weight: 8 = bold |
1613 | 0 | font->family == "SYMBOL", // symbol |
1614 | 0 | stringFormat && stringFormat->DirectionVertical(), // vertical |
1615 | 0 | font->Italic(), // italic |
1616 | 0 | false, // monospaced |
1617 | 0 | false, // outline = false, no such thing in MS-EMFPLUS |
1618 | 0 | stringFormat && stringFormat->DirectionRightToLeft(), // right-to-left |
1619 | 0 | false); // BiDiStrong |
1620 | |
|
1621 | 0 | css::lang::Locale locale; |
1622 | 0 | double stringAlignmentHorizontalOffset = 0.0; |
1623 | 0 | double stringAlignmentVerticalOffset = font->emSize; |
1624 | 0 | if (stringFormat) |
1625 | 0 | { |
1626 | 0 | LanguageTag aLanguageTag(static_cast<LanguageType>(stringFormat->language)); |
1627 | 0 | locale = aLanguageTag.getLocale(); |
1628 | 0 | drawinglayer::primitive2d::TextLayouterDevice aTextLayouter; |
1629 | |
|
1630 | 0 | aTextLayouter.setFontAttribute(fontAttribute, font->emSize, |
1631 | 0 | font->emSize, locale); |
1632 | |
|
1633 | 0 | double fTextWidth = aTextLayouter.getTextWidth(text, 0, stringLength); |
1634 | 0 | SAL_WARN_IF(stringFormat->DirectionRightToLeft(), "drawinglayer.emf", |
1635 | 0 | "EMF+\t DrawString Alignment TODO For a right-to-left layout rectangle, the origin should be at the upper right."); |
1636 | 0 | if (stringFormat->stringAlignment == StringAlignmentNear) |
1637 | | // Alignment is to the left side of the layout rectangle (lx, ly, lw, lh) |
1638 | 0 | stringAlignmentHorizontalOffset = stringFormat->leadingMargin * font->emSize; |
1639 | 0 | else if (stringFormat->stringAlignment == StringAlignmentCenter) |
1640 | | // Alignment is centered between the origin and extent of the layout rectangle |
1641 | 0 | stringAlignmentHorizontalOffset = 0.5 * lw + (stringFormat->leadingMargin - stringFormat->trailingMargin) * font->emSize - 0.5 * fTextWidth; |
1642 | 0 | else if (stringFormat->stringAlignment == StringAlignmentFar) |
1643 | | // Alignment is to the right side of the layout rectangle |
1644 | 0 | stringAlignmentHorizontalOffset = lw - stringFormat->trailingMargin * font->emSize - fTextWidth; |
1645 | |
|
1646 | 0 | if (stringFormat->lineAlign == StringAlignmentNear) |
1647 | 0 | stringAlignmentVerticalOffset = font->emSize; |
1648 | 0 | else if (stringFormat->lineAlign == StringAlignmentCenter) |
1649 | 0 | stringAlignmentVerticalOffset = 0.5 * lh + 0.5 * font->emSize; |
1650 | 0 | else if (stringFormat->lineAlign == StringAlignmentFar) |
1651 | 0 | stringAlignmentVerticalOffset = lh; |
1652 | 0 | } |
1653 | 0 | else |
1654 | 0 | { |
1655 | | // By default LeadingMargin is 1/6 inch |
1656 | | // TODO for typographic fonts set value to 0. |
1657 | 0 | stringAlignmentHorizontalOffset = 16.0; |
1658 | | |
1659 | | // use system default |
1660 | 0 | locale = Application::GetSettings().GetLanguageTag().getLocale(); |
1661 | 0 | } |
1662 | | |
1663 | 0 | const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix( |
1664 | 0 | ::basegfx::B2DVector(font->emSize, font->emSize), |
1665 | 0 | ::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset, |
1666 | 0 | ly + stringAlignmentVerticalOffset)); |
1667 | |
|
1668 | 0 | Color uncorrectedColor = EMFPGetBrushColorOrARGBColor(flags, brushId); |
1669 | 0 | Color color; |
1670 | |
|
1671 | 0 | if (mbSetTextContrast) |
1672 | 0 | { |
1673 | 0 | sal_uInt16 nTextContrast = std::clamp(mnTextContrast, LOWERGAMMA, UPPERGAMMA); |
1674 | 0 | assert(nTextContrast >= LOWERGAMMA && nTextContrast <= UPPERGAMMA); |
1675 | 0 | const auto gammaVal = nTextContrast / 1000; |
1676 | 0 | assert(gammaVal != 0); |
1677 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Text contrast: " << gammaVal << " gamma"); |
1678 | 0 | const basegfx::BColorModifier_gamma gamma(gammaVal); |
1679 | | |
1680 | | // gamma correct transparency color |
1681 | 0 | sal_uInt16 alpha = uncorrectedColor.GetAlpha(); |
1682 | 0 | alpha = std::clamp(std::pow(alpha, 1.0 / gammaVal), 0.0, 1.0) * 255; |
1683 | |
|
1684 | 0 | basegfx::BColor modifiedColor(gamma.getModifiedColor(uncorrectedColor.getBColor())); |
1685 | 0 | color.SetRed(modifiedColor.getRed() * 255); |
1686 | 0 | color.SetGreen(modifiedColor.getGreen() * 255); |
1687 | 0 | color.SetBlue(modifiedColor.getBlue() * 255); |
1688 | 0 | color.SetAlpha(alpha); |
1689 | 0 | } |
1690 | 0 | else |
1691 | 0 | { |
1692 | 0 | color = uncorrectedColor; |
1693 | 0 | } |
1694 | | |
1695 | 0 | mrPropertyHolders.Current().setTextColor(color.getBColor()); |
1696 | 0 | mrPropertyHolders.Current().setTextColorActive(true); |
1697 | |
|
1698 | 0 | if (color.GetAlpha() > 0) |
1699 | 0 | { |
1700 | 0 | std::vector<double> emptyVector; |
1701 | 0 | rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText; |
1702 | 0 | if (font->Underline() || font->Strikeout()) |
1703 | 0 | { |
1704 | 0 | pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( |
1705 | 0 | transformMatrix, |
1706 | 0 | text, |
1707 | 0 | 0, // text always starts at 0 |
1708 | 0 | stringLength, |
1709 | 0 | std::move(emptyVector), // EMF-PLUS has no DX-array |
1710 | 0 | {}, |
1711 | 0 | std::move(fontAttribute), |
1712 | 0 | locale, |
1713 | 0 | color.getBColor(), // Font Color |
1714 | 0 | COL_TRANSPARENT, // Fill Color |
1715 | 0 | 0, |
1716 | 0 | color.getBColor(), // OverlineColor |
1717 | 0 | color.getBColor(), // TextlineColor |
1718 | 0 | drawinglayer::primitive2d::TEXT_LINE_NONE, |
1719 | 0 | font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE, |
1720 | 0 | false, |
1721 | 0 | font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE); |
1722 | 0 | } |
1723 | 0 | else |
1724 | 0 | { |
1725 | 0 | pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( |
1726 | 0 | transformMatrix, |
1727 | 0 | text, |
1728 | 0 | 0, // text always starts at 0 |
1729 | 0 | stringLength, |
1730 | 0 | std::move(emptyVector), // EMF-PLUS has no DX-array |
1731 | 0 | {}, |
1732 | 0 | std::move(fontAttribute), |
1733 | 0 | std::move(locale), |
1734 | 0 | color.getBColor()); |
1735 | 0 | } |
1736 | 0 | drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText); |
1737 | 0 | if (color.IsTransparent()) |
1738 | 0 | { |
1739 | 0 | aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( |
1740 | 0 | drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText }, |
1741 | 0 | (255 - color.GetAlpha()) / 255.0); |
1742 | 0 | } |
1743 | |
|
1744 | 0 | mrTargetHolders.Current().append( |
1745 | 0 | new drawinglayer::primitive2d::TransformPrimitive2D( |
1746 | 0 | maMapTransform, |
1747 | 0 | drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } )); |
1748 | 0 | } |
1749 | 0 | break; |
1750 | 0 | } |
1751 | 0 | case EmfPlusRecordTypeSetPageTransform: |
1752 | 0 | { |
1753 | 0 | rMS.ReadFloat(mfPageScale); |
1754 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Scale: " << mfPageScale << " unit: " << UnitTypeToString(flags)); |
1755 | | |
1756 | 0 | if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld)) |
1757 | 0 | { |
1758 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by SetPageTransform in EMF+ specification."); |
1759 | 0 | } |
1760 | 0 | else |
1761 | 0 | { |
1762 | 0 | mnMmX = std::round(unitToPixel(static_cast<double>(mnMmX) * mfPageScale, flags, Direction::horizontal)); |
1763 | 0 | mnMmY = std::round(unitToPixel(static_cast<double>(mnMmY) * mfPageScale, flags, Direction::vertical)); |
1764 | 0 | mappingChanged(); |
1765 | 0 | } |
1766 | 0 | break; |
1767 | 0 | } |
1768 | 0 | case EmfPlusRecordTypeSetRenderingOrigin: |
1769 | 0 | { |
1770 | 0 | rMS.ReadInt32(mnOriginX).ReadInt32(mnOriginY); |
1771 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t SetRenderingOrigin, [x,y]: " << mnOriginX << "," << mnOriginY); |
1772 | 0 | break; |
1773 | 0 | } |
1774 | 0 | case EmfPlusRecordTypeSetTextContrast: |
1775 | 0 | { |
1776 | 0 | mbSetTextContrast = true; |
1777 | 0 | mnTextContrast = flags & 0xFFF; |
1778 | 0 | SAL_WARN_IF(mnTextContrast > UPPERGAMMA || mnTextContrast < LOWERGAMMA, |
1779 | 0 | "drawinglayer.emf", "EMF+\t Gamma value is not with bounds 1000 to 2200, value is " << mnTextContrast); |
1780 | 0 | break; |
1781 | 0 | } |
1782 | 0 | case EmfPlusRecordTypeSetTextRenderingHint: |
1783 | 0 | { |
1784 | 0 | sal_uInt8 nTextRenderingHint = (flags & 0xFF) >> 1; |
1785 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Text rendering hint: " << TextRenderingHintToString(nTextRenderingHint)); |
1786 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetTextRenderingHint"); |
1787 | 0 | break; |
1788 | 0 | } |
1789 | 0 | case EmfPlusRecordTypeSetAntiAliasMode: |
1790 | 0 | { |
1791 | 0 | bool bUseAntiAlias = (flags & 0x0001); |
1792 | 0 | sal_uInt8 nSmoothingMode = (flags & 0xFE00) >> 1; |
1793 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Antialiasing: " << (bUseAntiAlias ? "enabled" : "disabled")); |
1794 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Smoothing mode: " << SmoothingModeToString(nSmoothingMode)); |
1795 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetAntiAliasMode"); |
1796 | 0 | break; |
1797 | 0 | } |
1798 | 0 | case EmfPlusRecordTypeSetInterpolationMode: |
1799 | 0 | { |
1800 | 0 | sal_uInt16 nInterpolationMode = flags & 0xFF; |
1801 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Interpolation mode: " << InterpolationModeToString(nInterpolationMode)); |
1802 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t TODO InterpolationMode"); |
1803 | 0 | break; |
1804 | 0 | } |
1805 | 0 | case EmfPlusRecordTypeSetPixelOffsetMode: |
1806 | 0 | { |
1807 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Pixel offset mode: " << PixelOffsetModeToString(flags)); |
1808 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetPixelOffsetMode"); |
1809 | 0 | break; |
1810 | 0 | } |
1811 | 0 | case EmfPlusRecordTypeSetCompositingQuality: |
1812 | 0 | { |
1813 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t TODO SetCompositingQuality"); |
1814 | 0 | break; |
1815 | 0 | } |
1816 | 0 | case EmfPlusRecordTypeSave: |
1817 | 0 | { |
1818 | 0 | sal_uInt32 stackIndex; |
1819 | 0 | rMS.ReadUInt32(stackIndex); |
1820 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Save stack index: " << stackIndex); |
1821 | | |
1822 | 0 | GraphicStatePush(mGSStack, stackIndex); |
1823 | |
|
1824 | 0 | break; |
1825 | 0 | } |
1826 | 0 | case EmfPlusRecordTypeRestore: |
1827 | 0 | { |
1828 | 0 | sal_uInt32 stackIndex; |
1829 | 0 | rMS.ReadUInt32(stackIndex); |
1830 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Restore stack index: " << stackIndex); |
1831 | | |
1832 | 0 | GraphicStatePop(mGSStack, stackIndex); |
1833 | 0 | break; |
1834 | 0 | } |
1835 | 0 | case EmfPlusRecordTypeBeginContainer: |
1836 | 0 | { |
1837 | 0 | float dx, dy, dw, dh; |
1838 | 0 | ReadRectangle(rMS, dx, dy, dw, dh); |
1839 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Dest RectData: " << dx << "," << dy << " " << dw << "x" << dh); |
1840 | | |
1841 | 0 | float sx, sy, sw, sh; |
1842 | 0 | ReadRectangle(rMS, sx, sy, sw, sh); |
1843 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Source RectData: " << sx << "," << sy << " " << sw << "x" << sh); |
1844 | | |
1845 | 0 | sal_uInt32 stackIndex; |
1846 | 0 | rMS.ReadUInt32(stackIndex); |
1847 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container stack index: " << stackIndex << ", PageUnit: " << flags); |
1848 | | |
1849 | 0 | if ((flags == UnitTypeDisplay) || (flags == UnitTypeWorld)) |
1850 | 0 | { |
1851 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\t file error. UnitTypeDisplay and UnitTypeWorld are not supported by BeginContainer in EMF+ specification."); |
1852 | 0 | break; |
1853 | 0 | } |
1854 | 0 | GraphicStatePush(mGSContainerStack, stackIndex); |
1855 | 0 | const basegfx::B2DHomMatrix transform = basegfx::utils::createScaleTranslateB2DHomMatrix( |
1856 | 0 | unitToPixel(static_cast<double>(dw) / sw, flags, Direction::horizontal), |
1857 | 0 | unitToPixel(static_cast<double>(dh) / sh, flags, Direction::vertical), |
1858 | 0 | unitToPixel(static_cast<double>(dx) - sx, flags, Direction::horizontal), |
1859 | 0 | unitToPixel(static_cast<double>(dy) - sy, flags, Direction::vertical)); |
1860 | 0 | maWorldTransform *= transform; |
1861 | 0 | mappingChanged(); |
1862 | 0 | break; |
1863 | 0 | } |
1864 | 0 | case EmfPlusRecordTypeBeginContainerNoParams: |
1865 | 0 | { |
1866 | 0 | sal_uInt32 stackIndex; |
1867 | 0 | rMS.ReadUInt32(stackIndex); |
1868 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container No Params stack index: " << stackIndex); |
1869 | | |
1870 | 0 | GraphicStatePush(mGSContainerStack, stackIndex); |
1871 | 0 | break; |
1872 | 0 | } |
1873 | 0 | case EmfPlusRecordTypeEndContainer: |
1874 | 0 | { |
1875 | 0 | sal_uInt32 stackIndex; |
1876 | 0 | rMS.ReadUInt32(stackIndex); |
1877 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t End Container stack index: " << stackIndex); |
1878 | | |
1879 | 0 | GraphicStatePop(mGSContainerStack, stackIndex); |
1880 | 0 | break; |
1881 | 0 | } |
1882 | 0 | case EmfPlusRecordTypeSetWorldTransform: |
1883 | 0 | { |
1884 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t SetWorldTransform, Post multiply: " << bool(flags & 0x2000)); |
1885 | 0 | readXForm(rMS, maWorldTransform); |
1886 | 0 | mappingChanged(); |
1887 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t\t: " << maWorldTransform); |
1888 | 0 | break; |
1889 | 0 | } |
1890 | 0 | case EmfPlusRecordTypeResetWorldTransform: |
1891 | 0 | { |
1892 | 0 | maWorldTransform.identity(); |
1893 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t World transform: " << maWorldTransform); |
1894 | 0 | mappingChanged(); |
1895 | 0 | break; |
1896 | 0 | } |
1897 | 0 | case EmfPlusRecordTypeMultiplyWorldTransform: |
1898 | 0 | { |
1899 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t MultiplyWorldTransform, post multiply: " << bool(flags & 0x2000)); |
1900 | 0 | basegfx::B2DHomMatrix transform; |
1901 | 0 | readXForm(rMS, transform); |
1902 | |
|
1903 | 0 | SAL_INFO("drawinglayer.emf", |
1904 | 0 | "EMF+\t Transform matrix: " << transform); |
1905 | | |
1906 | 0 | if (flags & 0x2000) |
1907 | 0 | { |
1908 | | // post multiply |
1909 | 0 | maWorldTransform *= transform; |
1910 | 0 | } |
1911 | 0 | else |
1912 | 0 | { |
1913 | | // pre multiply |
1914 | 0 | transform *= maWorldTransform; |
1915 | 0 | maWorldTransform = transform; |
1916 | 0 | } |
1917 | |
|
1918 | 0 | mappingChanged(); |
1919 | |
|
1920 | 0 | SAL_INFO("drawinglayer.emf", |
1921 | 0 | "EMF+\t World transform matrix: " << maWorldTransform); |
1922 | 0 | break; |
1923 | 0 | } |
1924 | 0 | case EmfPlusRecordTypeTranslateWorldTransform: |
1925 | 0 | { |
1926 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t TranslateWorldTransform, Post multiply: " << bool(flags & 0x2000)); |
1927 | | |
1928 | 0 | basegfx::B2DHomMatrix transform; |
1929 | 0 | float eDx, eDy; |
1930 | 0 | rMS.ReadFloat(eDx).ReadFloat(eDy); |
1931 | 0 | transform.set(0, 2, eDx); |
1932 | 0 | transform.set(1, 2, eDy); |
1933 | |
|
1934 | 0 | SAL_INFO("drawinglayer.emf", |
1935 | 0 | "EMF+\t Translate matrix: " << transform); |
1936 | | |
1937 | 0 | if (flags & 0x2000) |
1938 | 0 | { |
1939 | | // post multiply |
1940 | 0 | maWorldTransform *= transform; |
1941 | 0 | } |
1942 | 0 | else |
1943 | 0 | { |
1944 | | // pre multiply |
1945 | 0 | transform *= maWorldTransform; |
1946 | 0 | maWorldTransform = transform; |
1947 | 0 | } |
1948 | |
|
1949 | 0 | mappingChanged(); |
1950 | |
|
1951 | 0 | SAL_INFO("drawinglayer.emf", |
1952 | 0 | "EMF+\t World transform matrix: " << maWorldTransform); |
1953 | 0 | break; |
1954 | 0 | } |
1955 | 0 | case EmfPlusRecordTypeScaleWorldTransform: |
1956 | 0 | { |
1957 | 0 | basegfx::B2DHomMatrix transform; |
1958 | 0 | float eSx, eSy; |
1959 | 0 | rMS.ReadFloat(eSx).ReadFloat(eSy); |
1960 | 0 | transform.set(0, 0, eSx); |
1961 | 0 | transform.set(1, 1, eSy); |
1962 | |
|
1963 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t ScaleWorldTransform Sx: " << eSx << |
1964 | 0 | " Sy: " << eSy << ", Post multiply:" << bool(flags & 0x2000)); |
1965 | 0 | SAL_INFO("drawinglayer.emf", |
1966 | 0 | "EMF+\t World transform matrix: " << maWorldTransform); |
1967 | | |
1968 | 0 | if (flags & 0x2000) |
1969 | 0 | { |
1970 | | // post multiply |
1971 | 0 | maWorldTransform *= transform; |
1972 | 0 | } |
1973 | 0 | else |
1974 | 0 | { |
1975 | | // pre multiply |
1976 | 0 | transform *= maWorldTransform; |
1977 | 0 | maWorldTransform = transform; |
1978 | 0 | } |
1979 | |
|
1980 | 0 | mappingChanged(); |
1981 | |
|
1982 | 0 | SAL_INFO("drawinglayer.emf", |
1983 | 0 | "EMF+\t World transform matrix: " << maWorldTransform); |
1984 | 0 | break; |
1985 | 0 | } |
1986 | 0 | case EmfPlusRecordTypeRotateWorldTransform: |
1987 | 0 | { |
1988 | | // Angle of rotation in degrees |
1989 | 0 | float eAngle; |
1990 | 0 | rMS.ReadFloat(eAngle); |
1991 | |
|
1992 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t RotateWorldTransform Angle: " << eAngle << |
1993 | 0 | ", post multiply: " << bool(flags & 0x2000)); |
1994 | | // Skipping flags & 0x2000 |
1995 | | // For rotation transformation there is no difference between post and pre multiply |
1996 | 0 | maWorldTransform.rotate(basegfx::deg2rad(eAngle)); |
1997 | 0 | mappingChanged(); |
1998 | |
|
1999 | 0 | SAL_INFO("drawinglayer.emf", |
2000 | 0 | "EMF+\t " << maWorldTransform); |
2001 | 0 | break; |
2002 | 0 | } |
2003 | 0 | case EmfPlusRecordTypeResetClip: |
2004 | 0 | { |
2005 | 0 | SAL_INFO("drawinglayer.emf", "EMF+ ResetClip"); |
2006 | | // We don't need to read anything more, as Size needs to be set 0x0000000C |
2007 | | // and DataSize must be set to 0. |
2008 | | |
2009 | | // Resets the current clipping region for the world space to infinity. |
2010 | 0 | HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, mrPropertyHolders); |
2011 | 0 | break; |
2012 | 0 | } |
2013 | 0 | case EmfPlusRecordTypeSetClipRect: |
2014 | 0 | case EmfPlusRecordTypeSetClipPath: |
2015 | 0 | case EmfPlusRecordTypeSetClipRegion: |
2016 | 0 | { |
2017 | 0 | int combineMode = (flags >> 8) & 0xf; |
2018 | 0 | ::basegfx::B2DPolyPolygon polyPolygon; |
2019 | 0 | if (type == EmfPlusRecordTypeSetClipRect) |
2020 | 0 | { |
2021 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t SetClipRect"); |
2022 | | |
2023 | 0 | float dx, dy, dw, dh; |
2024 | 0 | ReadRectangle(rMS, dx, dy, dw, dh); |
2025 | 0 | SAL_INFO("drawinglayer.emf", |
2026 | 0 | "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh); |
2027 | 0 | ::basegfx::B2DPoint mappedPoint1(Map(dx, dy)); |
2028 | 0 | ::basegfx::B2DPoint mappedPoint2(Map(dx + dw, dy + dh)); |
2029 | |
|
2030 | 0 | polyPolygon |
2031 | 0 | = ::basegfx::B2DPolyPolygon(::basegfx::utils::createPolygonFromRect( |
2032 | 0 | ::basegfx::B2DRectangle(mappedPoint1.getX(), mappedPoint1.getY(), |
2033 | 0 | mappedPoint2.getX(), mappedPoint2.getY()))); |
2034 | 0 | } |
2035 | 0 | else if (type == EmfPlusRecordTypeSetClipPath) |
2036 | 0 | { |
2037 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\tSetClipPath " << (flags & 0xff)); |
2038 | | |
2039 | 0 | EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get()); |
2040 | 0 | if (!path) |
2041 | 0 | { |
2042 | 0 | SAL_WARN("drawinglayer.emf", |
2043 | 0 | "EMF+\t TODO Unable to find path in slot: " << (flags & 0xff)); |
2044 | 0 | break; |
2045 | 0 | } |
2046 | 0 | polyPolygon = path->GetPolygon(*this); |
2047 | 0 | } |
2048 | 0 | else if (type == EmfPlusRecordTypeSetClipRegion) |
2049 | 0 | { |
2050 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Region in slot: " << (flags & 0xff)); |
2051 | 0 | EMFPRegion* region |
2052 | 0 | = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get()); |
2053 | 0 | if (!region) |
2054 | 0 | { |
2055 | 0 | SAL_WARN( |
2056 | 0 | "drawinglayer.emf", |
2057 | 0 | "EMF+\t TODO Unable to find region in slot: " << (flags & 0xff)); |
2058 | 0 | break; |
2059 | 0 | } |
2060 | 0 | polyPolygon = region->regionPolyPolygon; |
2061 | 0 | } |
2062 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Combine mode: " << combineMode); |
2063 | 0 | ::basegfx::B2DPolyPolygon aClippedPolyPolygon; |
2064 | 0 | if (mrPropertyHolders.Current().getClipPolyPolygonActive()) |
2065 | 0 | { |
2066 | 0 | aClippedPolyPolygon |
2067 | 0 | = combineClip(mrPropertyHolders.Current().getClipPolyPolygon(), |
2068 | 0 | combineMode, polyPolygon); |
2069 | 0 | } |
2070 | 0 | else |
2071 | 0 | { |
2072 | | //Combine with infinity |
2073 | 0 | switch (combineMode) |
2074 | 0 | { |
2075 | 0 | case EmfPlusCombineModeReplace: |
2076 | 0 | case EmfPlusCombineModeIntersect: |
2077 | 0 | { |
2078 | 0 | aClippedPolyPolygon = polyPolygon; |
2079 | 0 | break; |
2080 | 0 | } |
2081 | 0 | case EmfPlusCombineModeUnion: |
2082 | 0 | { |
2083 | | // Disable clipping as the clipping is infinity |
2084 | 0 | aClippedPolyPolygon = ::basegfx::B2DPolyPolygon(); |
2085 | 0 | break; |
2086 | 0 | } |
2087 | 0 | case EmfPlusCombineModeXOR: |
2088 | 0 | case EmfPlusCombineModeComplement: |
2089 | 0 | { |
2090 | | //TODO It is not correct and it should be fixed |
2091 | 0 | aClippedPolyPolygon = std::move(polyPolygon); |
2092 | 0 | break; |
2093 | 0 | } |
2094 | 0 | case EmfPlusCombineModeExclude: |
2095 | 0 | { |
2096 | | //TODO It is not correct and it should be fixed |
2097 | 0 | aClippedPolyPolygon = ::basegfx::B2DPolyPolygon(); |
2098 | 0 | break; |
2099 | 0 | } |
2100 | 0 | } |
2101 | 0 | } |
2102 | 0 | HandleNewClipRegion(aClippedPolyPolygon, mrTargetHolders, mrPropertyHolders); |
2103 | 0 | break; |
2104 | 0 | } |
2105 | 0 | case EmfPlusRecordTypeOffsetClip: |
2106 | 0 | { |
2107 | 0 | float dx, dy; |
2108 | 0 | rMS.ReadFloat(dx).ReadFloat(dy); |
2109 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\tOffset x:" << dx << ", y:" << dy); |
2110 | | |
2111 | 0 | basegfx::B2DPolyPolygon aPolyPolygon( |
2112 | 0 | mrPropertyHolders.Current().getClipPolyPolygon()); |
2113 | |
|
2114 | 0 | SAL_INFO("drawinglayer.emf", |
2115 | 0 | "EMF+\t PolyPolygon before translate: " << aPolyPolygon); |
2116 | | |
2117 | 0 | basegfx::B2DPoint aOffset = Map(dx, dy); |
2118 | 0 | basegfx::B2DHomMatrix transformMatrix; |
2119 | 0 | transformMatrix.set(0, 2, aOffset.getX()); |
2120 | 0 | transformMatrix.set(1, 2, aOffset.getY()); |
2121 | 0 | aPolyPolygon.transform(transformMatrix); |
2122 | |
|
2123 | 0 | SAL_INFO("drawinglayer.emf", |
2124 | 0 | "EMF+\t PolyPolygon after translate: " << aPolyPolygon << |
2125 | 0 | ", mapped offset x" << aOffset.getX() << ", mapped offset y" << aOffset.getY()); |
2126 | 0 | HandleNewClipRegion(aPolyPolygon, mrTargetHolders, mrPropertyHolders); |
2127 | 0 | break; |
2128 | 0 | } |
2129 | 0 | case EmfPlusRecordTypeDrawDriverString: |
2130 | 0 | { |
2131 | 0 | sal_uInt32 brushIndexOrColor; |
2132 | 0 | sal_uInt32 optionFlags; |
2133 | 0 | sal_uInt32 hasMatrix; |
2134 | 0 | sal_uInt32 glyphsCount; |
2135 | 0 | rMS.ReadUInt32(brushIndexOrColor).ReadUInt32(optionFlags).ReadUInt32(hasMatrix).ReadUInt32(glyphsCount); |
2136 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec); |
2137 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Option flags: 0x" << std::hex << optionFlags << std::dec); |
2138 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Has matrix: " << hasMatrix); |
2139 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t Glyphs: " << glyphsCount); |
2140 | | |
2141 | 0 | if ((optionFlags & 1) && glyphsCount > 0) |
2142 | 0 | { |
2143 | 0 | std::unique_ptr<float[]> charsPosX(new float[glyphsCount]); |
2144 | 0 | std::unique_ptr<float[]> charsPosY(new float[glyphsCount]); |
2145 | 0 | OUString text = read_uInt16s_ToOUString(rMS, glyphsCount); |
2146 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t DrawDriverString string: " << text); |
2147 | | |
2148 | 0 | for (sal_uInt32 i = 0; i<glyphsCount; i++) |
2149 | 0 | { |
2150 | 0 | rMS.ReadFloat(charsPosX[i]).ReadFloat(charsPosY[i]); |
2151 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\t\t glyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]); |
2152 | 0 | } |
2153 | | |
2154 | 0 | basegfx::B2DHomMatrix transform; |
2155 | |
|
2156 | 0 | if (hasMatrix) |
2157 | 0 | { |
2158 | 0 | readXForm(rMS, transform); |
2159 | 0 | SAL_INFO("drawinglayer.emf", "EMF+\tmatrix: " << transform); |
2160 | 0 | } |
2161 | | |
2162 | | // get the font from the flags |
2163 | 0 | EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get()); |
2164 | 0 | if (!font) |
2165 | 0 | { |
2166 | 0 | break; |
2167 | 0 | } |
2168 | | // done reading |
2169 | | |
2170 | 0 | drawinglayer::attribute::FontAttribute fontAttribute( |
2171 | 0 | font->family, // font family |
2172 | 0 | u""_ustr, // (no) font style |
2173 | 0 | font->Bold() ? 8u : 1u, // weight: 8 = bold |
2174 | 0 | font->family == "SYMBOL", // symbol |
2175 | 0 | optionFlags & 0x2, // vertical |
2176 | 0 | font->Italic(), // italic |
2177 | 0 | false, // monospaced |
2178 | 0 | false, // outline = false, no such thing in MS-EMFPLUS |
2179 | 0 | false, // right-to-left |
2180 | 0 | false); // BiDiStrong |
2181 | |
|
2182 | 0 | const Color color = EMFPGetBrushColorOrARGBColor(flags, brushIndexOrColor); |
2183 | | |
2184 | | // generate TextSimplePortionPrimitive2Ds or TextDecoratedPortionPrimitive2D |
2185 | | // for all portions of text with the same charsPosY values |
2186 | 0 | sal_uInt32 pos = 0; |
2187 | 0 | while (pos < glyphsCount) |
2188 | 0 | { |
2189 | | //determine the current length |
2190 | 0 | sal_uInt32 aLength = 1; |
2191 | 0 | while (pos + aLength < glyphsCount && std::abs( charsPosY[pos + aLength] - charsPosY[pos] ) < std::numeric_limits< float >::epsilon()) |
2192 | 0 | aLength++; |
2193 | | |
2194 | | // generate the DX-Array |
2195 | 0 | std::vector<double> aDXArray; |
2196 | 0 | for (size_t i = 0; i < aLength - 1; i++) |
2197 | 0 | { |
2198 | 0 | aDXArray.push_back(charsPosX[pos + i + 1] - charsPosX[pos]); |
2199 | 0 | } |
2200 | | // last entry |
2201 | 0 | aDXArray.push_back(0); |
2202 | |
|
2203 | 0 | basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix( |
2204 | 0 | ::basegfx::B2DVector(font->emSize, font->emSize), |
2205 | 0 | ::basegfx::B2DPoint(charsPosX[pos], charsPosY[pos])); |
2206 | 0 | if (hasMatrix) |
2207 | 0 | transformMatrix *= transform; |
2208 | 0 | if (color.GetAlpha() > 0) |
2209 | 0 | { |
2210 | 0 | rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText; |
2211 | 0 | if (font->Underline() || font->Strikeout()) |
2212 | 0 | { |
2213 | 0 | pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( |
2214 | 0 | transformMatrix, |
2215 | 0 | text, |
2216 | 0 | pos, // take character at current pos |
2217 | 0 | aLength, // use determined length |
2218 | 0 | std::move(aDXArray), // generated DXArray |
2219 | 0 | {}, |
2220 | 0 | fontAttribute, |
2221 | 0 | Application::GetSettings().GetLanguageTag().getLocale(), |
2222 | 0 | color.getBColor(), |
2223 | 0 | COL_TRANSPARENT, |
2224 | 0 | 0, |
2225 | 0 | color.getBColor(), |
2226 | 0 | color.getBColor(), |
2227 | 0 | drawinglayer::primitive2d::TEXT_LINE_NONE, |
2228 | 0 | font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE, |
2229 | 0 | false, |
2230 | 0 | font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE); |
2231 | 0 | } |
2232 | 0 | else |
2233 | 0 | { |
2234 | 0 | pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( |
2235 | 0 | transformMatrix, |
2236 | 0 | text, |
2237 | 0 | pos, // take character at current pos |
2238 | 0 | aLength, // use determined length |
2239 | 0 | std::move(aDXArray), // generated DXArray |
2240 | 0 | {}, |
2241 | 0 | fontAttribute, |
2242 | 0 | Application::GetSettings().GetLanguageTag().getLocale(), |
2243 | 0 | color.getBColor()); |
2244 | 0 | } |
2245 | 0 | drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText); |
2246 | 0 | if (color.IsTransparent()) |
2247 | 0 | { |
2248 | 0 | aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( |
2249 | 0 | drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText }, |
2250 | 0 | (255 - color.GetAlpha()) / 255.0); |
2251 | 0 | } |
2252 | 0 | mrTargetHolders.Current().append( |
2253 | 0 | new drawinglayer::primitive2d::TransformPrimitive2D( |
2254 | 0 | maMapTransform, |
2255 | 0 | drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } )); |
2256 | 0 | } |
2257 | | |
2258 | | // update pos |
2259 | 0 | pos += aLength; |
2260 | 0 | } |
2261 | 0 | } |
2262 | 0 | else |
2263 | 0 | { |
2264 | 0 | SAL_WARN("drawinglayer.emf", "EMF+\tTODO: fonts (non-unicode glyphs chars)"); |
2265 | 0 | } |
2266 | 0 | break; |
2267 | 0 | } |
2268 | 0 | default: |
2269 | 0 | { |
2270 | 0 | SAL_WARN("drawinglayer.emf", "EMF+ TODO unhandled record type: 0x" << std::hex << type << std::dec); |
2271 | 0 | } |
2272 | 0 | } |
2273 | 0 | } |
2274 | | |
2275 | 0 | rMS.Seek(next); |
2276 | |
|
2277 | 0 | if (size <= length) |
2278 | 0 | { |
2279 | 0 | length -= size; |
2280 | 0 | } |
2281 | 0 | else |
2282 | 0 | { |
2283 | 0 | SAL_WARN("drawinglayer.emf", "ImplRenderer::processEMFPlus: " |
2284 | 0 | "size " << size << " > length " << length); |
2285 | 0 | length = 0; |
2286 | 0 | } |
2287 | 0 | } |
2288 | 0 | } |
2289 | | } |
2290 | | |
2291 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |