Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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/attribute/fillgraphicattribute.hxx>
32
#include <drawinglayer/attribute/fontattribute.hxx>
33
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
34
#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
35
#include <drawinglayer/primitive2d/PolyPolygonRGBAPrimitive2D.hxx>
36
#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
37
#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
38
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
39
#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
40
#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
41
#include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
42
#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
43
#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
44
#include <drawinglayer/primitive2d/textprimitive2d.hxx>
45
#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
46
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
47
#include <basegfx/color/bcolor.hxx>
48
#include <basegfx/color/bcolormodifier.hxx>
49
#include <basegfx/matrix/b2dhommatrixtools.hxx>
50
#include <basegfx/polygon/b2dpolygonclipper.hxx>
51
#include <basegfx/polygon/b2dpolygontools.hxx>
52
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
53
#include <basegfx/vector/b2dsize.hxx>
54
#include <sal/log.hxx>
55
#include <vcl/alpha.hxx>
56
#include <vcl/svapp.hxx>
57
#include <vcl/settings.hxx>
58
#include <vcl/BitmapWriteAccess.hxx>
59
#include <i18nlangtag/languagetag.hxx>
60
61
#include <algorithm>
62
63
namespace emfplushelper
64
{
65
66
    enum
67
    {
68
        WrapModeTile = 0x00000000,
69
        WrapModeTileFlipX = 0x00000001,
70
        WrapModeTileFlipY = 0x00000002,
71
        WrapModeTileFlipXY = 0x00000003,
72
        WrapModeClamp = 0x00000004
73
    };
74
75
    const char* emfTypeToName(sal_uInt16 type)
76
0
    {
77
0
        switch (type)
78
0
        {
79
0
            case EmfPlusRecordTypeHeader: return "EmfPlusRecordTypeHeader";
80
0
            case EmfPlusRecordTypeEndOfFile: return "EmfPlusRecordTypeEndOfFile";
81
0
            case EmfPlusRecordTypeComment: return "EmfPlusRecordTypeComment";
82
0
            case EmfPlusRecordTypeGetDC: return "EmfPlusRecordTypeGetDC";
83
0
            case EmfPlusRecordTypeObject: return "EmfPlusRecordTypeObject";
84
0
            case EmfPlusRecordTypeFillRects: return "EmfPlusRecordTypeFillRects";
85
0
            case EmfPlusRecordTypeDrawRects: return "EmfPlusRecordTypeDrawRects";
86
0
            case EmfPlusRecordTypeFillPolygon: return "EmfPlusRecordTypeFillPolygon";
87
0
            case EmfPlusRecordTypeDrawLines: return "EmfPlusRecordTypeDrawLines";
88
0
            case EmfPlusRecordTypeFillClosedCurve: return "EmfPlusRecordTypeFillClosedCurve";
89
0
            case EmfPlusRecordTypeDrawClosedCurve: return "EmfPlusRecordTypeDrawClosedCurve";
90
0
            case EmfPlusRecordTypeDrawCurve: return "EmfPlusRecordTypeDrawCurve";
91
0
            case EmfPlusRecordTypeFillEllipse: return "EmfPlusRecordTypeFillEllipse";
92
0
            case EmfPlusRecordTypeDrawEllipse: return "EmfPlusRecordTypeDrawEllipse";
93
0
            case EmfPlusRecordTypeFillPie: return "EmfPlusRecordTypeFillPie";
94
0
            case EmfPlusRecordTypeDrawPie: return "EmfPlusRecordTypeDrawPie";
95
0
            case EmfPlusRecordTypeDrawArc: return "EmfPlusRecordTypeDrawArc";
96
0
            case EmfPlusRecordTypeFillRegion: return "EmfPlusRecordTypeFillRegion";
97
0
            case EmfPlusRecordTypeFillPath: return "EmfPlusRecordTypeFillPath";
98
0
            case EmfPlusRecordTypeDrawPath: return "EmfPlusRecordTypeDrawPath";
99
0
            case EmfPlusRecordTypeDrawBeziers: return "EmfPlusRecordTypeDrawBeziers";
100
0
            case EmfPlusRecordTypeDrawImage: return "EmfPlusRecordTypeDrawImage";
101
0
            case EmfPlusRecordTypeDrawImagePoints: return "EmfPlusRecordTypeDrawImagePoints";
102
0
            case EmfPlusRecordTypeDrawString: return "EmfPlusRecordTypeDrawString";
103
0
            case EmfPlusRecordTypeSetRenderingOrigin: return "EmfPlusRecordTypeSetRenderingOrigin";
104
0
            case EmfPlusRecordTypeSetAntiAliasMode: return "EmfPlusRecordTypeSetAntiAliasMode";
105
0
            case EmfPlusRecordTypeSetTextRenderingHint: return "EmfPlusRecordTypeSetTextRenderingHint";
106
0
            case EmfPlusRecordTypeSetTextContrast: return "EmfPlusRecordTypeSetTextContrast";
107
0
            case EmfPlusRecordTypeSetInterpolationMode: return "EmfPlusRecordTypeSetInterpolationMode";
108
0
            case EmfPlusRecordTypeSetPixelOffsetMode: return "EmfPlusRecordTypeSetPixelOffsetMode";
109
0
            case EmfPlusRecordTypeSetCompositingQuality: return "EmfPlusRecordTypeSetCompositingQuality";
110
0
            case EmfPlusRecordTypeSave: return "EmfPlusRecordTypeSave";
111
0
            case EmfPlusRecordTypeRestore: return "EmfPlusRecordTypeRestore";
112
0
            case EmfPlusRecordTypeBeginContainer: return "EmfPlusRecordTypeBeginContainer";
113
0
            case EmfPlusRecordTypeBeginContainerNoParams: return "EmfPlusRecordTypeBeginContainerNoParams";
114
0
            case EmfPlusRecordTypeEndContainer: return "EmfPlusRecordTypeEndContainer";
115
0
            case EmfPlusRecordTypeSetWorldTransform: return "EmfPlusRecordTypeSetWorldTransform";
116
0
            case EmfPlusRecordTypeResetWorldTransform: return "EmfPlusRecordTypeResetWorldTransform";
117
0
            case EmfPlusRecordTypeMultiplyWorldTransform: return "EmfPlusRecordTypeMultiplyWorldTransform";
118
0
            case EmfPlusRecordTypeTranslateWorldTransform: return "EmfPlusRecordTypeTranslateWorldTransform";
119
0
            case EmfPlusRecordTypeScaleWorldTransform: return "EmfPlusRecordTypeScaleWorldTransform";
120
0
            case EmfPlusRecordTypeSetPageTransform: return "EmfPlusRecordTypeSetPageTransform";
121
0
            case EmfPlusRecordTypeResetClip: return "EmfPlusRecordTypeResetClip";
122
0
            case EmfPlusRecordTypeSetClipRect: return "EmfPlusRecordTypeSetClipRect";
123
0
            case EmfPlusRecordTypeSetClipPath: return "EmfPlusRecordTypeSetClipPath";
124
0
            case EmfPlusRecordTypeSetClipRegion: return "EmfPlusRecordTypeSetClipRegion";
125
0
            case EmfPlusRecordTypeOffsetClip: return "EmfPlusRecordTypeOffsetClip";
126
0
            case EmfPlusRecordTypeDrawDriverString: return "EmfPlusRecordTypeDrawDriverString";
127
0
        }
128
0
        return "";
129
0
    }
130
131
    static OUString emfObjectToName(sal_uInt16 type)
132
0
    {
133
0
        switch (type)
134
0
        {
135
0
            case EmfPlusObjectTypeBrush: return u"EmfPlusObjectTypeBrush"_ustr;
136
0
            case EmfPlusObjectTypePen: return u"EmfPlusObjectTypePen"_ustr;
137
0
            case EmfPlusObjectTypePath: return u"EmfPlusObjectTypePath"_ustr;
138
0
            case EmfPlusObjectTypeRegion: return u"EmfPlusObjectTypeRegion"_ustr;
139
0
            case EmfPlusObjectTypeImage: return u"EmfPlusObjectTypeImage"_ustr;
140
0
            case EmfPlusObjectTypeFont: return u"EmfPlusObjectTypeFont"_ustr;
141
0
            case EmfPlusObjectTypeStringFormat: return u"EmfPlusObjectTypeStringFormat"_ustr;
142
0
            case EmfPlusObjectTypeImageAttributes: return u"EmfPlusObjectTypeImageAttributes"_ustr;
143
0
            case EmfPlusObjectTypeCustomLineCap: return u"EmfPlusObjectTypeCustomLineCap"_ustr;
144
0
        }
145
0
        return u""_ustr;
146
0
    }
147
148
    static OUString PixelOffsetModeToString(sal_uInt16 nPixelOffset)
149
0
    {
150
0
        switch (nPixelOffset)
151
0
        {
152
0
            case PixelOffsetMode::PixelOffsetModeDefault: return u"PixelOffsetModeDefault"_ustr;
153
0
            case PixelOffsetMode::PixelOffsetModeHighSpeed: return u"PixelOffsetModeHighSpeed"_ustr;
154
0
            case PixelOffsetMode::PixelOffsetModeHighQuality: return u"PixelOffsetModeHighQuality"_ustr;
155
0
            case PixelOffsetMode::PixelOffsetModeNone: return u"PixelOffsetModeNone"_ustr;
156
0
            case PixelOffsetMode::PixelOffsetModeHalf: return u"PixelOffsetModeHalf"_ustr;
157
0
        }
158
0
        return u""_ustr;
159
0
    }
160
161
    static OUString SmoothingModeToString(sal_uInt16 nSmoothMode)
162
0
    {
163
0
        switch (nSmoothMode)
164
0
        {
165
0
            case SmoothingMode::SmoothingModeDefault: return u"SmoothingModeDefault"_ustr;
166
0
            case SmoothingMode::SmoothingModeHighSpeed: return u"SmoothModeHighSpeed"_ustr;
167
0
            case SmoothingMode::SmoothingModeHighQuality: return u"SmoothingModeHighQuality"_ustr;
168
0
            case SmoothingMode::SmoothingModeNone: return u"SmoothingModeNone"_ustr;
169
0
            case SmoothingMode::SmoothingModeAntiAlias8x4: return u"SmoothingModeAntiAlias8x4"_ustr;
170
0
            case SmoothingMode::SmoothingModeAntiAlias8x8: return u"SmoothingModeAntiAlias8x8"_ustr;
171
0
        }
172
0
        return u""_ustr;
173
0
    }
174
175
    static OUString TextRenderingHintToString(sal_uInt16 nHint)
176
0
    {
177
0
        switch (nHint)
178
0
        {
179
0
            case TextRenderingHint::TextRenderingHintSystemDefault: return u"TextRenderingHintSystemDefault"_ustr;
180
0
            case TextRenderingHint::TextRenderingHintSingleBitPerPixelGridFit: return u"TextRenderingHintSingleBitPerPixelGridFit"_ustr;
181
0
            case TextRenderingHint::TextRenderingHintSingleBitPerPixel: return u"TextRenderingHintSingleBitPerPixel"_ustr;
182
0
            case TextRenderingHint::TextRenderingHintAntialiasGridFit: return u"TextRenderingHintAntialiasGridFit"_ustr;
183
0
            case TextRenderingHint::TextRenderingHintAntialias: return u"TextRenderingHintAntialias"_ustr;
184
0
            case TextRenderingHint::TextRenderingHintClearTypeGridFit: return u"TextRenderingHintClearTypeGridFit"_ustr;
185
0
        }
186
0
        return u""_ustr;
187
0
    }
188
189
    static OUString InterpolationModeToString(sal_uInt16 nMode)
190
0
    {
191
0
        switch (nMode)
192
0
        {
193
0
            case InterpolationMode::InterpolationModeDefault: return u"InterpolationModeDefault"_ustr;
194
0
            case InterpolationMode::InterpolationModeLowQuality: return u"InterpolationModeLowQuality"_ustr;
195
0
            case InterpolationMode::InterpolationModeHighQuality: return u"InterpolationModeHighQuality"_ustr;
196
0
            case InterpolationMode::InterpolationModeBilinear: return u"InterpolationModeBilinear"_ustr;
197
0
            case InterpolationMode::InterpolationModeBicubic: return u"InterpolationModeBicubic"_ustr;
198
0
            case InterpolationMode::InterpolationModeNearestNeighbor: return u"InterpolationModeNearestNeighbor"_ustr;
199
0
            case InterpolationMode::InterpolationModeHighQualityBilinear: return u"InterpolationModeHighQualityBilinear"_ustr;
200
0
            case InterpolationMode::InterpolationModeHighQualityBicubic: return u"InterpolationModeHighQualityBicubic"_ustr;
201
0
        }
202
0
        return u""_ustr;
203
0
    }
204
205
    OUString UnitTypeToString(sal_uInt16 nType)
206
0
    {
207
0
        switch (nType)
208
0
        {
209
0
            case UnitTypeWorld: return u"UnitTypeWorld"_ustr;
210
0
            case UnitTypeDisplay: return u"UnitTypeDisplay"_ustr;
211
0
            case UnitTypePixel: return u"UnitTypePixel"_ustr;
212
0
            case UnitTypePoint: return u"UnitTypePoint"_ustr;
213
0
            case UnitTypeInch: return u"UnitTypeInch"_ustr;
214
0
            case UnitTypeDocument: return u"UnitTypeDocument"_ustr;
215
0
            case UnitTypeMillimeter: return u"UnitTypeMillimeter"_ustr;
216
0
        }
217
0
        return u""_ustr;
218
0
    }
219
220
    static bool IsBrush(sal_uInt16 flags)
221
0
    {
222
0
        return (!((flags >> 15) & 0x0001));
223
0
    }
224
225
    static OUString BrushIDToString(sal_uInt16 flags, sal_uInt32 brushid)
226
0
    {
227
0
        if (IsBrush(flags))
228
0
            return "EmfPlusBrush ID: " + OUString::number(brushid);
229
0
        else
230
0
            return "ARGB: 0x" + OUString::number(brushid, 16);
231
0
    }
232
233
    EMFPObject::~EMFPObject()
234
0
    {
235
0
    }
236
237
    double EmfPlusHelperData::unitToPixel(double n, sal_uInt32 aUnitType, Direction d)
238
0
    {
239
0
        switch (static_cast<UnitType>(aUnitType))
240
0
        {
241
0
            case UnitTypePixel:
242
0
                return n;
243
244
0
            case UnitTypePoint:
245
0
                return o3tl::convert(n, o3tl::Length::pt, o3tl::Length::in) * DPI(d);
246
247
0
            case UnitTypeInch:
248
0
                return n * DPI(d);
249
250
0
            case UnitTypeMillimeter:
251
0
                return o3tl::convert(n, o3tl::Length::mm, o3tl::Length::in) * DPI(d);
252
253
0
            case UnitTypeDocument:
254
0
                return n * DPI(d) / 300.0;
255
256
0
            case UnitTypeWorld:
257
0
            case UnitTypeDisplay:
258
0
                SAL_WARN("drawinglayer.emf", "EMF+\t Converting to World/Display.");
259
0
                return n;
260
261
0
            default:
262
0
                SAL_WARN("drawinglayer.emf", "EMF+\tTODO Unimplemented support of Unit Type: 0x" << std::hex << aUnitType);
263
0
                return n;
264
0
        }
265
0
    }
266
267
    void EmfPlusHelperData::processObjectRecord(SvMemoryStream& rObjectStream, sal_uInt16 flags, sal_uInt32 dataSize, bool bUseWholeStream)
268
0
    {
269
0
        sal_uInt16 objecttype = flags & 0x7f00;
270
0
        sal_uInt16 index = flags & 0xff;
271
0
        SAL_INFO("drawinglayer.emf", "EMF+ Object: " << emfObjectToName(objecttype) << " (0x" << objecttype << ")");
272
0
        SAL_INFO("drawinglayer.emf", "EMF+\tObject slot: " << index);
273
0
        SAL_INFO("drawinglayer.emf", "EMF+\tFlags: " << (flags & 0xff00));
274
275
0
        switch (objecttype)
276
0
        {
277
0
            case EmfPlusObjectTypeBrush:
278
0
            {
279
0
                EMFPBrush *brush = new EMFPBrush();
280
0
                maEMFPObjects[index].reset(brush);
281
0
                brush->Read(rObjectStream, *this, dataSize, bUseWholeStream);
282
0
                break;
283
0
            }
284
0
            case EmfPlusObjectTypePen:
285
0
            {
286
0
                EMFPPen *pen = new EMFPPen();
287
0
                maEMFPObjects[index].reset(pen);
288
0
                pen->Read(rObjectStream, *this, dataSize, bUseWholeStream);
289
0
                pen->penWidth = unitToPixel(pen->penWidth, pen->penUnit, Direction::horizontal);
290
0
                break;
291
0
            }
292
0
            case EmfPlusObjectTypePath:
293
0
            {
294
0
                sal_uInt32 aVersion(0), aPathPointCount(0), aPathPointFlags(0);
295
296
0
                rObjectStream.ReadUInt32(aVersion).ReadUInt32(aPathPointCount).ReadUInt32(aPathPointFlags);
297
0
                SAL_INFO("drawinglayer.emf", "EMF+\t\tVersion: 0x" << std::hex << aVersion);
298
0
                SAL_INFO("drawinglayer.emf", "EMF+\t\tNumber of points: " << std::dec << aPathPointCount);
299
0
                SAL_INFO("drawinglayer.emf", "EMF+\t\tPath point flags: 0x" << std::hex << aPathPointFlags << std::dec);
300
0
                EMFPPath *path = new EMFPPath(aPathPointCount);
301
0
                maEMFPObjects[index].reset(path);
302
0
                path->Read(rObjectStream, aPathPointFlags);
303
0
                break;
304
0
            }
305
0
            case EmfPlusObjectTypeRegion:
306
0
            {
307
0
                EMFPRegion *region = new EMFPRegion();
308
0
                maEMFPObjects[index].reset(region);
309
0
                region->ReadRegion(rObjectStream, *this);
310
0
                break;
311
0
            }
312
0
            case EmfPlusObjectTypeImage:
313
0
            {
314
0
                EMFPImage *image = new EMFPImage;
315
0
                maEMFPObjects[index].reset(image);
316
0
                image->type = 0;
317
0
                image->width = 0;
318
0
                image->height = 0;
319
0
                image->stride = 0;
320
0
                image->pixelFormat = 0;
321
0
                image->Read(rObjectStream, dataSize, bUseWholeStream);
322
0
                break;
323
0
            }
324
0
            case EmfPlusObjectTypeFont:
325
0
            {
326
0
                EMFPFont *font = new EMFPFont;
327
0
                maEMFPObjects[index].reset(font);
328
0
                font->emSize = 0;
329
0
                font->sizeUnit = 0;
330
0
                font->fontFlags = 0;
331
0
                font->Read(rObjectStream);
332
                // tdf#113624 Convert unit to Pixels
333
0
                font->emSize = unitToPixel(font->emSize, font->sizeUnit, Direction::horizontal);
334
335
0
                break;
336
0
            }
337
0
            case EmfPlusObjectTypeStringFormat:
338
0
            {
339
0
                EMFPStringFormat *stringFormat = new EMFPStringFormat();
340
0
                maEMFPObjects[index].reset(stringFormat);
341
0
                stringFormat->Read(rObjectStream);
342
0
                break;
343
0
            }
344
0
            case EmfPlusObjectTypeImageAttributes:
345
0
            {
346
0
                EMFPImageAttributes *imageAttributes = new EMFPImageAttributes();
347
0
                maEMFPObjects[index].reset(imageAttributes);
348
0
                imageAttributes->Read(rObjectStream);
349
0
                break;
350
0
            }
351
0
            case EmfPlusObjectTypeCustomLineCap:
352
0
            {
353
0
                SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object type 'custom line cap' not yet implemented");
354
0
                break;
355
0
            }
356
0
            default:
357
0
            {
358
0
                SAL_WARN("drawinglayer.emf", "EMF+\t TODO Object unhandled flags: 0x" << std::hex << (flags & 0xff00) << std::dec);
359
0
            }
360
0
        }
361
0
    }
362
363
    void EmfPlusHelperData::ReadPoint(SvStream& s, float& x, float& y, sal_uInt32 flags)
364
0
    {
365
0
        if (flags & 0x800)
366
0
        {
367
            // specifies a location in the coordinate space that is relative to
368
            // the location specified by the previous element in the array. In the case of the first element in
369
            // PointData, a previous location at coordinates (0,0) is assumed.
370
0
            SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Relative coordinates bit detected. Implement parse EMFPlusPointR");
371
0
        }
372
373
0
        if (flags & 0x4000)
374
0
        {
375
0
            sal_Int16 ix, iy;
376
377
0
            s.ReadInt16(ix).ReadInt16(iy);
378
379
0
            x = ix;
380
0
            y = iy;
381
0
        }
382
0
        else
383
0
        {
384
0
            s.ReadFloat(x).ReadFloat(y);
385
0
        }
386
0
    }
387
388
    void EmfPlusHelperData::ReadRectangle(SvStream& s, float& x, float& y, float &width, float& height, bool bCompressed)
389
0
    {
390
0
        if (bCompressed)
391
0
        {
392
0
            sal_Int16 ix, iy, iw, ih;
393
394
0
            s.ReadInt16(ix).ReadInt16(iy).ReadInt16(iw).ReadInt16(ih);
395
396
0
            x = ix;
397
0
            y = iy;
398
0
            width = iw;
399
0
            height = ih;
400
0
        }
401
0
        else
402
0
        {
403
0
            s.ReadFloat(x).ReadFloat(y).ReadFloat(width).ReadFloat(height);
404
0
        }
405
0
    }
406
407
    bool EmfPlusHelperData::readXForm(SvStream& rIn, basegfx::B2DHomMatrix& rTarget)
408
0
    {
409
0
        rTarget.identity();
410
411
0
        if (sizeof(float) != 4)
412
0
        {
413
0
            OSL_FAIL("EnhWMFReader::sizeof( float ) != 4");
414
0
            return false;
415
0
        }
416
0
        else
417
0
        {
418
0
            float eM11(0.0);
419
0
            float eM12(0.0);
420
0
            float eM21(0.0);
421
0
            float eM22(0.0);
422
0
            float eDx(0.0);
423
0
            float eDy(0.0);
424
0
            rIn.ReadFloat(eM11).ReadFloat(eM12).ReadFloat(eM21).ReadFloat(eM22).ReadFloat(eDx).ReadFloat(eDy);
425
0
            rTarget = basegfx::B2DHomMatrix(
426
0
                eM11, eM21, eDx,
427
0
                eM12, eM22, eDy);
428
0
        }
429
430
0
        return true;
431
0
    }
432
433
    void EmfPlusHelperData::mappingChanged()
434
0
    {
435
0
        if (mnPixX == 0 || mnPixY == 0)
436
0
        {
437
0
            SAL_WARN("drawinglayer.emf", "dimensions in pixels is 0");
438
0
            return;
439
0
        }
440
        // Call when mnMmX/mnMmY/mnPixX/mnPixY/mnFrameLeft/mnFrameTop/maWorldTransform/ changes.
441
        // Currently not used are mnHDPI/mnVDPI/mnFrameRight/mnFrameBottom. *If* these should
442
        // be used in the future, this method will need to be called.
443
        //
444
        // Re-calculate maMapTransform to contain the complete former transformation so that
445
        // it can be applied by a single matrix multiplication or be added to an encapsulated
446
        // primitive later
447
        //
448
        // To evtl. correct and see where this came from, please compare with the implementations
449
        // of EmfPlusHelperData::MapToDevice and EmfPlusHelperData::Map* in prev versions
450
0
        maMapTransform = maWorldTransform;
451
0
        maMapTransform.scale(mfPageScaleX, mfPageScaleY);
452
0
        maMapTransform *= basegfx::utils::createScaleTranslateB2DHomMatrix(100.0 * mnMmX / mnPixX, 100.0 * mnMmY / mnPixY,
453
0
                                                                           double(-mnFrameLeft), double(-mnFrameTop));
454
0
        maMapTransform *= maBaseTransform;
455
456
        // Used only for performance optimization, to do not calculate it every line draw
457
0
        mdExtractedXScale = std::hypot(maMapTransform.a(), maMapTransform.b());
458
0
        mdExtractedYScale = std::hypot(maMapTransform.c(), maMapTransform.d());
459
0
    }
460
461
    ::basegfx::B2DPoint EmfPlusHelperData::Map(double ix, double iy) const
462
0
    {
463
        // map in one step using complete MapTransform (see mappingChanged)
464
0
        return maMapTransform * ::basegfx::B2DPoint(ix, iy);
465
0
    }
466
467
0
    Color EmfPlusHelperData::EMFPGetBrushColorOrARGBColor(const sal_uInt16 flags, const sal_uInt32 brushIndexOrColor) const {
468
0
        Color color;
469
0
        if (flags & 0x8000) // we use a color
470
0
        {
471
0
            color = Color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff,
472
0
                          (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
473
0
        }
474
0
        else // we use a brush
475
0
        {
476
0
            const EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
477
0
            if (brush)
478
0
            {
479
0
                color = brush->GetColor();
480
0
                if (brush->type != BrushTypeSolidColor)
481
0
                    SAL_WARN("drawinglayer.emf", "EMF+\t\t TODO Brush other than solid color is not supported");
482
0
            }
483
0
        }
484
0
        return color;
485
0
    }
486
487
    void EmfPlusHelperData::GraphicStatePush(GraphicStateMap& map, sal_Int32 index)
488
0
    {
489
0
        GraphicStateMap::iterator iter = map.find( index );
490
491
0
        if ( iter != map.end() )
492
0
        {
493
0
            map.erase( iter );
494
0
            SAL_INFO("drawinglayer.emf", "EMF+\t\tStack index: " << index << " found and erased");
495
0
        }
496
497
0
        wmfemfhelper::PropertyHolder state = mrPropertyHolders.Current();
498
        // tdf#112500 We need to save world transform somehow, during graphic state push
499
0
        state.setTransformation(maWorldTransform);
500
0
        map[ index ] = std::move(state);
501
0
    }
502
503
    void EmfPlusHelperData::GraphicStatePop(GraphicStateMap& map, sal_Int32 index)
504
0
    {
505
0
        GraphicStateMap::iterator iter = map.find(index);
506
507
0
        if (iter != map.end())
508
0
        {
509
0
            wmfemfhelper::PropertyHolder state = iter->second;
510
511
0
            maWorldTransform = state.getTransformation();
512
0
            if (state.getClipPolyPolygonActive())
513
0
            {
514
0
                SAL_INFO("drawinglayer.emf",
515
0
                        "EMF+\t Restore clipping region to saved in index: " << index);
516
0
                wmfemfhelper::HandleNewClipRegion(state.getClipPolyPolygon(), mrTargetHolders,
517
0
                                                  mrPropertyHolders);
518
0
            }
519
0
            else
520
0
            {
521
0
                SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping");
522
0
                wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders,
523
0
                                                  mrPropertyHolders);
524
0
            }
525
0
            mappingChanged();
526
0
            SAL_INFO("drawinglayer.emf",
527
0
                    "EMF+\t\tStack index: " << index
528
0
                                            << " found, maWorldTransform: " << maWorldTransform);
529
0
        }
530
0
    }
531
532
    drawinglayer::attribute::LineStartEndAttribute
533
    EmfPlusHelperData::CreateLineEnd(const sal_Int32 aCap, const float aPenWidth) const
534
0
    {
535
0
        const double pw = mdExtractedYScale * aPenWidth;
536
0
        if (aCap == LineCapTypeSquare)
537
0
        {
538
0
            basegfx::B2DPolygon aCapPolygon(
539
0
                { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
540
0
            aCapPolygon.setClosed(true);
541
0
            return drawinglayer::attribute::LineStartEndAttribute(
542
0
                pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
543
0
        }
544
0
        else if (aCap == LineCapTypeRound)
545
0
        {
546
0
            basegfx::B2DPolygon aCapPolygon(
547
0
                { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.9236, -0.3827},
548
0
                  {0.7071, -0.7071}, {0.3827, -0.9236}, {0.0, -1.0}, {-0.3827, -0.9236},
549
0
                  {-0.7071, -0.7071}, {-0.9236, -0.3827}, {-1.0, 0.0} });
550
0
            aCapPolygon.setClosed(true);
551
0
            return drawinglayer::attribute::LineStartEndAttribute(
552
0
                pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
553
0
        }
554
0
        else if (aCap == LineCapTypeTriangle)
555
0
        {
556
0
            basegfx::B2DPolygon aCapPolygon(
557
0
                { {-1.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.0, -1.0}, {-1.0, 0.0} });
558
0
            aCapPolygon.setClosed(true);
559
0
            return drawinglayer::attribute::LineStartEndAttribute(
560
0
                pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
561
0
        }
562
0
        else if (aCap == LineCapTypeSquareAnchor)
563
0
        {
564
0
            basegfx::B2DPolygon aCapPolygon(
565
0
                { {-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
566
0
            aCapPolygon.setClosed(true);
567
0
            return drawinglayer::attribute::LineStartEndAttribute(
568
0
                1.5 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
569
0
        }
570
0
        else if (aCap == LineCapTypeRoundAnchor)
571
0
        {
572
0
            const basegfx::B2DPolygon aCapPolygon
573
0
                = ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(0.0, 0.0), 1.0, 1.0);
574
0
            return drawinglayer::attribute::LineStartEndAttribute(
575
0
                2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
576
0
        }
577
0
        else if (aCap == LineCapTypeDiamondAnchor)
578
0
        {
579
0
            basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 0.0}, {0.5, 0.5},
580
0
                                              {0.5, 1.0}, {-0.5, 1.0}, {-0.5, 0.5},
581
0
                                              {-1.0, 0.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
        else if (aCap == LineCapTypeArrowAnchor)
587
0
        {
588
0
            basegfx::B2DPolygon aCapPolygon({ {0.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0} });
589
0
            aCapPolygon.setClosed(true);
590
0
            return drawinglayer::attribute::LineStartEndAttribute(
591
0
                2.0 * pw, basegfx::B2DPolyPolygon(aCapPolygon), true);
592
0
        }
593
0
        return drawinglayer::attribute::LineStartEndAttribute();
594
0
    }
595
596
    void EmfPlusHelperData::EMFPPlusDrawPolygon(const ::basegfx::B2DPolyPolygon& polygon,
597
                                                sal_uInt32 penIndex)
598
0
    {
599
0
        const EMFPPen* pen = dynamic_cast<EMFPPen*>(maEMFPObjects[penIndex & 0xff].get());
600
0
        SAL_WARN_IF(!pen, "drawinglayer.emf", "emf+ missing pen");
601
602
0
        if (!(pen && polygon.count()))
603
0
            return;
604
605
0
        const double transformedPenWidth = mdExtractedYScale * pen->penWidth;
606
0
        drawinglayer::attribute::LineAttribute lineAttribute(
607
0
            pen->GetColor().getBColor(), transformedPenWidth, pen->maLineJoin,
608
0
            css::drawing::LineCap_BUTT, //TODO implement PenDataDashedLineCap support here
609
0
            pen->fMiterMinimumAngle);
610
611
0
        drawinglayer::attribute::LineStartEndAttribute aStart;
612
0
        if (pen->penDataFlags & EmfPlusPenDataStartCap)
613
0
        {
614
0
            if ((pen->penDataFlags & EmfPlusPenDataCustomStartCap)
615
0
                && (pen->customStartCap->polygon.begin()->count() > 1))
616
0
                aStart = drawinglayer::attribute::LineStartEndAttribute(
617
0
                    pen->customStartCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
618
0
                        * pen->customStartCap->widthScale * pen->penWidth,
619
0
                    pen->customStartCap->polygon, false);
620
0
            else
621
0
                aStart = EmfPlusHelperData::CreateLineEnd(pen->startCap, pen->penWidth);
622
0
        }
623
624
0
        drawinglayer::attribute::LineStartEndAttribute aEnd;
625
0
        if (pen->penDataFlags & EmfPlusPenDataEndCap)
626
0
        {
627
0
            if ((pen->penDataFlags & EmfPlusPenDataCustomEndCap)
628
0
                && (pen->customEndCap->polygon.begin()->count() > 1))
629
0
                aEnd = drawinglayer::attribute::LineStartEndAttribute(
630
0
                    pen->customEndCap->polygon.getB2DRange().getRange().getX() * mdExtractedXScale
631
0
                        * pen->customEndCap->widthScale * pen->penWidth,
632
0
                    pen->customEndCap->polygon, false);
633
0
            else
634
0
                aEnd = EmfPlusHelperData::CreateLineEnd(pen->endCap, pen->penWidth);
635
0
        }
636
637
0
        if (pen->GetColor().IsTransparent())
638
0
        {
639
0
            drawinglayer::primitive2d::Primitive2DContainer aContainer;
640
0
            if (aStart.isDefault() && aEnd.isDefault())
641
0
                aContainer.append(
642
0
                    new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
643
0
                        polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale)));
644
0
            else
645
0
            {
646
0
                aContainer.resize(polygon.count());
647
0
                for (sal_uInt32 i = 0; i < polygon.count(); i++)
648
0
                    aContainer[i] =
649
0
                        new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
650
0
                            polygon.getB2DPolygon(i), lineAttribute,
651
0
                            pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd);
652
0
            }
653
0
            mrTargetHolders.Current().append(
654
0
                new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
655
0
                    std::move(aContainer), (255 - pen->GetColor().GetAlpha()) / 255.0));
656
0
        }
657
0
        else
658
0
        {
659
0
            if (aStart.isDefault() && aEnd.isDefault())
660
0
                mrTargetHolders.Current().append(
661
0
                    new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
662
0
                        polygon, lineAttribute, pen->GetStrokeAttribute(mdExtractedXScale)));
663
0
            else
664
0
                for (sal_uInt32 i = 0; i < polygon.count(); i++)
665
0
                {
666
0
                    mrTargetHolders.Current().append(
667
0
                        new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
668
0
                            polygon.getB2DPolygon(i), lineAttribute,
669
0
                            pen->GetStrokeAttribute(mdExtractedXScale), aStart, aEnd));
670
0
                }
671
0
        }
672
0
        mrPropertyHolders.Current().setLineColor(pen->GetColor().getBColor());
673
0
        mrPropertyHolders.Current().setLineColorActive(true);
674
0
        mrPropertyHolders.Current().setFillColorActive(false);
675
0
    }
676
677
    // Convert a polypolygon so that nonzero/winding fill semantics produce
678
    // the same visual result when rendered with the even-odd fill rule used
679
    // by the primitive2d fill primitives. A self-intersecting single
680
    // polygon is resolved by solveCrossovers + stripNeutralPolygons and the
681
    // resulting sub-loops are merged. Multi-polygon inputs use
682
    // createNonzeroConform which removes inner duplicates of nested
683
    // same-orientation polygons.
684
    static ::basegfx::B2DPolyPolygon convertWindingToEvenOdd(
685
        const ::basegfx::B2DPolyPolygon& rIn)
686
0
    {
687
0
        if (rIn.count() == 1)
688
0
        {
689
0
            ::basegfx::B2DPolyPolygon aResolved
690
0
                = ::basegfx::utils::solveCrossovers(rIn.getB2DPolygon(0));
691
0
            aResolved = ::basegfx::utils::stripNeutralPolygons(aResolved);
692
0
            if (aResolved.count() > 1)
693
0
            {
694
0
                ::basegfx::B2DPolyPolygonVector aInput;
695
0
                for (sal_uInt32 i = 0; i < aResolved.count(); ++i)
696
0
                    aInput.push_back(::basegfx::B2DPolyPolygon(aResolved.getB2DPolygon(i)));
697
0
                return ::basegfx::utils::mergeToSinglePolyPolygon(aInput);
698
0
            }
699
0
            return aResolved;
700
0
        }
701
0
        return ::basegfx::utils::createNonzeroConform(rIn);
702
0
    }
703
704
    void EmfPlusHelperData::EMFPPlusFillPolygonSolidColor(const ::basegfx::B2DPolyPolygon& polygon, Color const& color)
705
0
    {
706
0
        if (color.GetAlpha() == 0)
707
0
            return;
708
709
0
        if (!color.IsTransparent())
710
0
        {
711
            // not transparent
712
0
            mrTargetHolders.Current().append(
713
0
                        new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
714
0
                            polygon,
715
0
                            color.getBColor()));
716
0
        }
717
0
        else
718
0
        {
719
            // transparent
720
0
            mrTargetHolders.Current().append(
721
0
                        new drawinglayer::primitive2d::PolyPolygonRGBAPrimitive2D(
722
0
                            polygon,
723
0
                            color.getBColor(),
724
0
                            (255 - color.GetAlpha()) / 255.0));
725
0
        }
726
0
    }
727
728
    void EmfPlusHelperData::EMFPPlusFillPolygon(const ::basegfx::B2DPolyPolygon& polygon, const bool isColor, const sal_uInt32 brushIndexOrColor)
729
0
    {
730
0
        if (!polygon.count())
731
0
          return;
732
733
0
        if (isColor) // use Color
734
0
        {
735
0
            SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, ARGB color: 0x" << std::hex << brushIndexOrColor << std::dec);
736
737
            // EMF Alpha (1 byte): An 8-bit unsigned integer that specifies the transparency of the background,
738
            // ranging from 0 for completely transparent to 0xFF for completely opaque.
739
0
            const Color color(ColorAlpha, (brushIndexOrColor >> 24), (brushIndexOrColor >> 16) & 0xff, (brushIndexOrColor >> 8) & 0xff, brushIndexOrColor & 0xff);
740
0
            EMFPPlusFillPolygonSolidColor(polygon, color);
741
742
0
            mrPropertyHolders.Current().setFillColor(color.getBColor());
743
0
            mrPropertyHolders.Current().setFillColorActive(true);
744
0
            mrPropertyHolders.Current().setLineColorActive(false);
745
0
        }
746
0
        else // use Brush
747
0
        {
748
0
            EMFPBrush* brush = dynamic_cast<EMFPBrush*>(maEMFPObjects[brushIndexOrColor & 0xff].get());
749
0
            SAL_INFO("drawinglayer.emf", "EMF+\t\t Fill polygon, brush slot: " << brushIndexOrColor << " (brush type: " << (brush ? brush->GetType() : -1) << ")");
750
751
            // give up in case something wrong happened
752
0
            if( !brush )
753
0
                return;
754
755
0
            mrPropertyHolders.Current().setFillColorActive(false);
756
0
            mrPropertyHolders.Current().setLineColorActive(false);
757
758
0
            if (brush->type == BrushTypeSolidColor)
759
0
            {
760
0
                Color fillColor = brush->solidColor;
761
0
                EMFPPlusFillPolygonSolidColor(polygon, fillColor);
762
0
            }
763
0
            else if (brush->type == BrushTypeHatchFill)
764
0
            {
765
                // EMF+ like hatching is currently not supported. These are just color blends which serve as an approximation for some of them
766
                // for the others the hatch "background" color (secondColor in brush) is used.
767
768
0
                bool isHatchBlend = true;
769
0
                double blendFactor = 0.0;
770
771
0
                switch (brush->hatchStyle)
772
0
                {
773
0
                    case HatchStyle05Percent: blendFactor = 0.05; break;
774
0
                    case HatchStyle10Percent: blendFactor = 0.10; break;
775
0
                    case HatchStyle20Percent: blendFactor = 0.20; break;
776
0
                    case HatchStyle25Percent: blendFactor = 0.25; break;
777
0
                    case HatchStyle30Percent: blendFactor = 0.30; break;
778
0
                    case HatchStyle40Percent: blendFactor = 0.40; break;
779
0
                    case HatchStyle50Percent: blendFactor = 0.50; break;
780
0
                    case HatchStyle60Percent: blendFactor = 0.60; break;
781
0
                    case HatchStyle70Percent: blendFactor = 0.70; break;
782
0
                    case HatchStyle75Percent: blendFactor = 0.75; break;
783
0
                    case HatchStyle80Percent: blendFactor = 0.80; break;
784
0
                    case HatchStyle90Percent: blendFactor = 0.90; break;
785
0
                    default:
786
0
                        isHatchBlend = false;
787
0
                        break;
788
0
                }
789
0
                Color fillColor;
790
0
                if (isHatchBlend)
791
0
                {
792
0
                    fillColor = brush->solidColor;
793
0
                    fillColor.Merge(brush->secondColor, static_cast<sal_uInt8>(255 * blendFactor));
794
0
                }
795
0
                else
796
0
                {
797
0
                    fillColor = brush->secondColor;
798
0
                }
799
                // temporal solution: create a solid colored polygon
800
                // TODO create a 'real' hatching primitive
801
0
                mrTargetHolders.Current().append(
802
0
                    new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
803
0
                        polygon,
804
0
                        fillColor.getBColor()));
805
0
            }
806
0
            else if (brush->type == BrushTypeTextureFill && brush->textureImage)
807
0
            {
808
0
                if (brush->textureImage->type != ImageDataTypeBitmap)
809
0
                {
810
0
                    SAL_WARN("drawinglayer.emf", "EMF+\tTextureFill: Metafiles type is not supported");
811
0
                    return;
812
0
                }
813
814
0
                const Size aSizePx = brush->textureImage->graphic.GetSizePixel();
815
0
                if (aSizePx.Width() <= 0 || aSizePx.Height() <= 0)
816
0
                    return;
817
818
0
                basegfx::B2DHomMatrix aBrushMatrix;
819
0
                if (brush->additionalFlags & 0x02) // 0x02 = BrushDataTransform flag
820
0
                    aBrushMatrix = brush->brush_transformation;
821
822
                // Create a scaling matrix to map the normalized 1x1 range
823
                // to the actual pixel dimensions of the texture.
824
825
0
                const basegfx::B2DHomMatrix aTextureScale(
826
0
                        /* Row 0, Column 0 */ aSizePx.Width(),
827
0
                        /* Row 0, Column 1 */ 0.0,
828
0
                        /* Row 0, Column 2 */ 0.0,
829
0
                        /* Row 1, Column 0 */ 0.0,
830
0
                        /* Row 1, Column 1 */ aSizePx.Height(),
831
0
                        /* Row 1, Column 2 */ 0.0);
832
833
0
                const basegfx::B2DHomMatrix aTotalMatrix = maMapTransform * aBrushMatrix * aTextureScale;
834
835
                // Invert the matrix to transform the fill polygon from document space
836
                // back into the local normalized texture space (0.0 to 1.0).
837
0
                basegfx::B2DHomMatrix aInvTotalMatrix = aTotalMatrix;
838
0
                if (!aInvTotalMatrix.invert())
839
0
                {
840
0
                    SAL_WARN("drawinglayer.emf", "EMF+\tTextureFill: Failed to invert transformation matrix");
841
0
                    return;
842
0
                }
843
0
                basegfx::B2DPolyPolygon aLocalPolygon = polygon;
844
0
                aLocalPolygon.transform(aInvTotalMatrix);
845
846
0
                const bool bIsTiled = (brush->wrapMode != WrapModeClamp);
847
848
0
                const drawinglayer::attribute::FillGraphicAttribute aFillAttribute(
849
0
                    brush->textureImage->graphic,
850
0
                    basegfx::B2DRange(0.0, 0.0, 1.0, 1.0),
851
0
                    bIsTiled);
852
853
0
                drawinglayer::primitive2d::Primitive2DReference xFillPrimitive =
854
0
                    new drawinglayer::primitive2d::PolyPolygonGraphicPrimitive2D(
855
0
                        aLocalPolygon,
856
0
                        basegfx::B2DRange(0.0, 0.0, 1.0, 1.0),
857
0
                        aFillAttribute);
858
859
0
                mrTargetHolders.Current().append(
860
0
                    new drawinglayer::primitive2d::TransformPrimitive2D(
861
0
                        aTotalMatrix,
862
0
                        drawinglayer::primitive2d::Primitive2DContainer({ xFillPrimitive })));
863
0
            }
864
0
            else if (brush->type == BrushTypePathGradient || brush->type == BrushTypeLinearGradient)
865
0
            {
866
0
                if (brush->type == BrushTypePathGradient && !(brush->additionalFlags & 0x1))
867
0
                {
868
0
                    SAL_WARN("drawinglayer.emf", "EMF+\t TODO Implement displaying BrushTypePathGradient with Boundary: ");
869
0
                }
870
0
                ::basegfx::B2DHomMatrix aTextureTransformation;
871
872
0
                if (brush->hasTransformation) {
873
0
                   aTextureTransformation = brush->brush_transformation;
874
875
                   // adjust aTextureTransformation for our world space:
876
                   // -> revert the mapping -> apply the transformation -> map back
877
0
                   basegfx::B2DHomMatrix aInvertedMapTrasform(maMapTransform);
878
0
                   aInvertedMapTrasform.invert();
879
0
                   aTextureTransformation =  maMapTransform * aTextureTransformation * aInvertedMapTrasform;
880
0
                }
881
882
                // select the stored colors
883
0
                const basegfx::BColor aStartColor = brush->solidColor.getBColor();
884
0
                const basegfx::BColor aEndColor = brush->secondColor.getBColor();
885
0
                drawinglayer::primitive2d::SvgGradientEntryVector aVector;
886
887
0
                if (brush->blendPositions)
888
0
                {
889
0
                    SAL_INFO("drawinglayer.emf", "EMF+\t\tUse blend");
890
891
                    // store the blendpoints in the vector
892
0
                    for (sal_uInt32 i = 0; i < brush->blendPoints; i++)
893
0
                    {
894
0
                        const double aBlendPoint = brush->blendPositions[i];
895
0
                        basegfx::BColor aColor;
896
0
                        aColor.setGreen(aStartColor.getGreen() + brush->blendFactors[i] * (aEndColor.getGreen() - aStartColor.getGreen()));
897
0
                        aColor.setBlue (aStartColor.getBlue()  + brush->blendFactors[i] * (aEndColor.getBlue() - aStartColor.getBlue()));
898
0
                        aColor.setRed  (aStartColor.getRed()   + brush->blendFactors[i] * (aEndColor.getRed() - aStartColor.getRed()));
899
0
                        const double aAlpha = brush->solidColor.GetAlpha() + brush->blendFactors[i] * (brush->secondColor.GetAlpha() - brush->solidColor.GetAlpha());
900
0
                        aVector.emplace_back(aBlendPoint, aColor, aAlpha / 255.0);
901
0
                    }
902
0
                }
903
0
                else if (brush->colorblendPositions)
904
0
                {
905
0
                    SAL_INFO("drawinglayer.emf", "EMF+\t\tUse color blend");
906
907
                    // store the colorBlends in the vector
908
0
                    for (sal_uInt32 i = 0; i < brush->colorblendPoints; i++)
909
0
                    {
910
0
                        const double aBlendPoint = brush->colorblendPositions[i];
911
0
                        const basegfx::BColor aColor = brush->colorblendColors[i].getBColor();
912
0
                        aVector.emplace_back(aBlendPoint, aColor, brush->colorblendColors[i].GetAlpha() / 255.0);
913
0
                    }
914
0
                }
915
0
                else // ok, no extra points: just start and end
916
0
                {
917
0
                    aVector.emplace_back(0.0, aStartColor, brush->solidColor.GetAlpha() / 255.0);
918
0
                    aVector.emplace_back(1.0, aEndColor, brush->secondColor.GetAlpha() / 255.0);
919
0
                }
920
921
                // get the polygon range to be able to map the start/end/center point correctly
922
                // therefore, create a mapping and invert it
923
0
                basegfx::B2DRange aPolygonRange= polygon.getB2DRange();
924
0
                basegfx::B2DHomMatrix aPolygonTransformation = basegfx::utils::createScaleTranslateB2DHomMatrix(
925
0
                    aPolygonRange.getWidth(),aPolygonRange.getHeight(),
926
0
                    aPolygonRange.getMinX(), aPolygonRange.getMinY());
927
0
                aPolygonTransformation.invert();
928
929
0
                if (brush->type == BrushTypeLinearGradient)
930
0
                {
931
                    // support for public enum EmfPlusWrapMode
932
0
                    basegfx::B2DPoint aStartPoint = Map(brush->firstPointX, 0.0);
933
0
                    aStartPoint = aPolygonTransformation * aStartPoint;
934
0
                    basegfx::B2DPoint aEndPoint = Map(brush->firstPointX + brush->aWidth, 0.0);
935
0
                    aEndPoint = aPolygonTransformation * aEndPoint;
936
937
                    // support for public enum EmfPlusWrapMode
938
0
                    drawinglayer::primitive2d::SpreadMethod aSpreadMethod(drawinglayer::primitive2d::SpreadMethod::Pad);
939
0
                    switch(brush->wrapMode)
940
0
                    {
941
0
                        case WrapModeTile:
942
0
                        case WrapModeTileFlipY:
943
0
                        {
944
0
                            aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Repeat;
945
0
                            break;
946
0
                        }
947
0
                        case WrapModeTileFlipX:
948
0
                        case WrapModeTileFlipXY:
949
0
                        {
950
0
                            aSpreadMethod = drawinglayer::primitive2d::SpreadMethod::Reflect;
951
0
                            break;
952
0
                        }
953
0
                        default:
954
0
                            break;
955
0
                    }
956
957
                    // create the same one used for SVG
958
0
                    mrTargetHolders.Current().append(
959
0
                        new drawinglayer::primitive2d::SvgLinearGradientPrimitive2D(
960
0
                            aTextureTransformation,
961
0
                            polygon,
962
0
                            std::move(aVector),
963
0
                            aStartPoint,
964
0
                            aEndPoint,
965
0
                            false,                  // do not use UnitCoordinates
966
0
                            aSpreadMethod));
967
0
                }
968
0
                else // BrushTypePathGradient
969
0
                {
970
                    // Render the gradient pattern into a small bitmap and use
971
                    // it as a (potentially tiled) texture fill. SvgRadialGradient
972
                    // alone cannot reproduce path-gradient tiling because the
973
                    // brush coordinate space and the fill-polygon space need
974
                    // not overlap.
975
0
                    if (!brush->path)
976
0
                        return;
977
978
0
                    basegfx::B2DRange aBrushBounds(
979
0
                        (brush->additionalFlags & 0x01)
980
0
                            ? brush->path->GetPolygon(*this, false).getB2DRange()
981
0
                            : brush->path->GetRawPointsPolygon().getB2DRange());
982
0
                    if (aBrushBounds.getWidth() <= 0 || aBrushBounds.getHeight() <= 0)
983
0
                        return;
984
985
0
                    constexpr sal_Int32 nMaxDim = 256;
986
0
                    sal_Int32 nBmpW, nBmpH;
987
0
                    if (aBrushBounds.getWidth() >= aBrushBounds.getHeight())
988
0
                    {
989
0
                        nBmpW = nMaxDim;
990
0
                        nBmpH = std::max<sal_Int32>(
991
0
                            1, std::lround(nMaxDim * aBrushBounds.getHeight()
992
0
                                           / aBrushBounds.getWidth()));
993
0
                    }
994
0
                    else
995
0
                    {
996
0
                        nBmpH = nMaxDim;
997
0
                        nBmpW = std::max<sal_Int32>(
998
0
                            1, std::lround(nMaxDim * aBrushBounds.getWidth()
999
0
                                           / aBrushBounds.getHeight()));
1000
0
                    }
1001
1002
                    // Path-gradient sweep: GDI+ partitions the brush
1003
                    // boundary into triangles formed by (centre, V_i,
1004
                    // V_{i+1}) and Gouraud-shades each one with vertex
1005
                    // colours (centerColor, surround[i],
1006
                    // surround[(i+1) % nSurround]). Inside a triangle the
1007
                    // pixel colour is the barycentric blend of the three
1008
                    // vertex colours; this reproduces the colourful per-
1009
                    // segment rendering that GDI+ produces (red/green/blue
1010
                    // points on a star with multiple surround colours, and
1011
                    // smooth radial gradient on a uniform-surround brush).
1012
                    // For boundary-only brushes (no BrushDataPath flag) the
1013
                    // brush data stores raw EmfPlusPointF arrays without
1014
                    // point types - GetPolygon() misinterprets the trailing
1015
                    // bytes as Bezier point types and returns a degenerate
1016
                    // polygon. Use the raw-points accessor instead.
1017
0
                    basegfx::B2DPolygon aSweepPolygon
1018
0
                        = (brush->additionalFlags & 0x01)
1019
0
                              ? brush->path->GetPolygon(*this, false).getB2DPolygon(0)
1020
0
                              : brush->path->GetRawPointsPolygon();
1021
                    // Flatten Bezier control points into straight-line
1022
                    // segments so the triangulation has a fine-enough
1023
                    // boundary. Without this, a 4-cardinal-vertex Bezier
1024
                    // ellipse becomes 4 triangles meeting at the centre
1025
                    // (a diamond), which is visibly wrong.
1026
0
                    if (aSweepPolygon.areControlPointsUsed())
1027
0
                        aSweepPolygon = basegfx::utils::adaptiveSubdivideByDistance(
1028
0
                            aSweepPolygon,
1029
0
                            std::max(aBrushBounds.getWidth(),
1030
0
                                     aBrushBounds.getHeight()) / 512.0);
1031
0
                    while (aSweepPolygon.count() >= 2
1032
0
                           && aSweepPolygon.getB2DPoint(0)
1033
0
                                  == aSweepPolygon.getB2DPoint(aSweepPolygon.count() - 1))
1034
0
                        aSweepPolygon.remove(aSweepPolygon.count() - 1);
1035
1036
0
                    const sal_uInt32 nVerts = aSweepPolygon.count();
1037
0
                    const basegfx::B2DPoint aCentre(
1038
0
                        brush->firstPointX, brush->firstPointY);
1039
1040
                    // Per-vertex surround colour (one per boundary vertex).
1041
                    // surroundColors[i] is a sal_uInt8-channel Color; convert
1042
                    // to BColor (0..1 doubles) once.
1043
0
                    std::vector<basegfx::BColor> aVertColor(nVerts);
1044
0
                    std::vector<double> aVertAlpha(nVerts);
1045
0
                    {
1046
0
                        const sal_uInt32 nSurr
1047
0
                            = brush->surroundColorsNumber > 0
1048
0
                                  ? brush->surroundColorsNumber
1049
0
                                  : 1;
1050
0
                        for (sal_uInt32 i = 0; i < nVerts; ++i)
1051
0
                        {
1052
0
                            const Color& c
1053
0
                                = brush->surroundColorsNumber > 0
1054
0
                                      ? brush->surroundColors[i % nSurr]
1055
0
                                      : brush->secondColor;
1056
0
                            aVertColor[i] = c.getBColor();
1057
0
                            aVertAlpha[i] = c.GetAlpha() / 255.0;
1058
0
                        }
1059
0
                    }
1060
0
                    const basegfx::BColor aCentreColor
1061
0
                        = brush->solidColor.getBColor();
1062
0
                    const double fCentreAlpha
1063
0
                        = brush->solidColor.GetAlpha() / 255.0;
1064
1065
                    // Optional centre->edge transition curves carried by the
1066
                    // brush. BlendFactors remap the gradient parameter t
1067
                    // (0 at centre, 1 at edge) before the centre/edge mix.
1068
                    // PresetColors (colorblend) replace the centre/edge mix
1069
                    // entirely with a sampled multi-stop colour curve. The
1070
                    // two are mutually exclusive in well-formed EMF+ data.
1071
0
                    struct FactorStop { double pos; double factor; };
1072
0
                    std::vector<FactorStop> aFactorCurve;
1073
0
                    if (brush->blendPositions && brush->blendPoints > 0)
1074
0
                    {
1075
0
                        aFactorCurve.reserve(brush->blendPoints);
1076
0
                        for (sal_uInt32 i = 0; i < brush->blendPoints; ++i)
1077
0
                            aFactorCurve.push_back({brush->blendPositions[i],
1078
0
                                                    brush->blendFactors[i]});
1079
0
                        std::sort(aFactorCurve.begin(), aFactorCurve.end(),
1080
0
                                  [](const FactorStop& a, const FactorStop& b)
1081
0
                                  { return a.pos < b.pos; });
1082
0
                    }
1083
1084
0
                    struct ColorStop { double pos; basegfx::BColor color; double alpha; };
1085
0
                    std::vector<ColorStop> aColorCurve;
1086
0
                    if (brush->colorblendPositions && brush->colorblendPoints > 0)
1087
0
                    {
1088
0
                        aColorCurve.reserve(brush->colorblendPoints);
1089
0
                        for (sal_uInt32 i = 0; i < brush->colorblendPoints; ++i)
1090
0
                            aColorCurve.push_back(
1091
0
                                {brush->colorblendPositions[i],
1092
0
                                 brush->colorblendColors[i].getBColor(),
1093
0
                                 brush->colorblendColors[i].GetAlpha() / 255.0});
1094
0
                        std::sort(aColorCurve.begin(), aColorCurve.end(),
1095
0
                                  [](const ColorStop& a, const ColorStop& b)
1096
0
                                  { return a.pos < b.pos; });
1097
0
                    }
1098
1099
0
                    auto sampleFactor = [&aFactorCurve](double t) -> double
1100
0
                    {
1101
0
                        if (aFactorCurve.empty()) return t;
1102
0
                        if (t <= aFactorCurve.front().pos)
1103
0
                            return aFactorCurve.front().factor;
1104
0
                        if (t >= aFactorCurve.back().pos)
1105
0
                            return aFactorCurve.back().factor;
1106
0
                        for (size_t i = 0; i + 1 < aFactorCurve.size(); ++i)
1107
0
                        {
1108
0
                            if (t <= aFactorCurve[i + 1].pos)
1109
0
                            {
1110
0
                                const double span
1111
0
                                    = aFactorCurve[i + 1].pos - aFactorCurve[i].pos;
1112
0
                                const double f
1113
0
                                    = span > 0.0 ? (t - aFactorCurve[i].pos) / span : 0.0;
1114
0
                                return aFactorCurve[i].factor
1115
0
                                       + f * (aFactorCurve[i + 1].factor
1116
0
                                              - aFactorCurve[i].factor);
1117
0
                            }
1118
0
                        }
1119
0
                        return aFactorCurve.back().factor;
1120
0
                    };
1121
1122
0
                    auto sampleColor = [&aColorCurve](double t)
1123
0
                        -> std::pair<basegfx::BColor, double>
1124
0
                    {
1125
0
                        if (aColorCurve.empty()) return {basegfx::BColor(), 1.0};
1126
0
                        if (t <= aColorCurve.front().pos)
1127
0
                            return {aColorCurve.front().color,
1128
0
                                    aColorCurve.front().alpha};
1129
0
                        if (t >= aColorCurve.back().pos)
1130
0
                            return {aColorCurve.back().color,
1131
0
                                    aColorCurve.back().alpha};
1132
0
                        for (size_t i = 0; i + 1 < aColorCurve.size(); ++i)
1133
0
                        {
1134
0
                            if (t <= aColorCurve[i + 1].pos)
1135
0
                            {
1136
0
                                const double span
1137
0
                                    = aColorCurve[i + 1].pos - aColorCurve[i].pos;
1138
0
                                const double f
1139
0
                                    = span > 0.0 ? (t - aColorCurve[i].pos) / span : 0.0;
1140
0
                                basegfx::BColor c;
1141
0
                                c.setRed(aColorCurve[i].color.getRed()
1142
0
                                         + f * (aColorCurve[i + 1].color.getRed()
1143
0
                                                - aColorCurve[i].color.getRed()));
1144
0
                                c.setGreen(aColorCurve[i].color.getGreen()
1145
0
                                           + f * (aColorCurve[i + 1].color.getGreen()
1146
0
                                                  - aColorCurve[i].color.getGreen()));
1147
0
                                c.setBlue(aColorCurve[i].color.getBlue()
1148
0
                                          + f * (aColorCurve[i + 1].color.getBlue()
1149
0
                                                 - aColorCurve[i].color.getBlue()));
1150
0
                                return {c, aColorCurve[i].alpha
1151
0
                                              + f * (aColorCurve[i + 1].alpha
1152
0
                                                     - aColorCurve[i].alpha)};
1153
0
                            }
1154
0
                        }
1155
0
                        return {aColorCurve.back().color,
1156
0
                                aColorCurve.back().alpha};
1157
0
                    };
1158
1159
0
                    auto signedCross = [](const basegfx::B2DPoint& a,
1160
0
                                          const basegfx::B2DPoint& b,
1161
0
                                          const basegfx::B2DPoint& c) -> double
1162
0
                    {
1163
0
                        return (b.getX() - a.getX()) * (c.getY() - a.getY())
1164
0
                               - (b.getY() - a.getY()) * (c.getX() - a.getX());
1165
0
                    };
1166
1167
                    // Compose the final pixel colour given the active
1168
                    // boundary segment index i, the segment-edge weights
1169
                    // w0/w1 and the centre-edge parameter tRaw. Mixes the
1170
                    // per-segment surround colours (Gouraud) and applies
1171
                    // the brush's blend curve when present.
1172
0
                    auto composeColor
1173
0
                        = [&aVertColor, &aVertAlpha, &aCentreColor, &fCentreAlpha,
1174
0
                           &aColorCurve, &sampleColor, &sampleFactor, nVerts](
1175
0
                              sal_uInt32 i, double w0, double w1, double tRaw,
1176
0
                              basegfx::BColor& outColor, double& outAlpha)
1177
0
                    {
1178
0
                        basegfx::BColor aEdgeColor;
1179
0
                        double fEdgeAlpha = 0.0;
1180
0
                        const double fEdgeSum = w0 + w1;
1181
0
                        if (fEdgeSum > 1e-12)
1182
0
                        {
1183
0
                            const double f0 = w0 / fEdgeSum;
1184
0
                            const double f1 = w1 / fEdgeSum;
1185
0
                            aEdgeColor.setRed(
1186
0
                                f0 * aVertColor[i].getRed()
1187
0
                                + f1 * aVertColor[(i + 1) % nVerts].getRed());
1188
0
                            aEdgeColor.setGreen(
1189
0
                                f0 * aVertColor[i].getGreen()
1190
0
                                + f1 * aVertColor[(i + 1) % nVerts].getGreen());
1191
0
                            aEdgeColor.setBlue(
1192
0
                                f0 * aVertColor[i].getBlue()
1193
0
                                + f1 * aVertColor[(i + 1) % nVerts].getBlue());
1194
0
                            fEdgeAlpha
1195
0
                                = f0 * aVertAlpha[i]
1196
0
                                  + f1 * aVertAlpha[(i + 1) % nVerts];
1197
0
                        }
1198
0
                        else
1199
0
                        {
1200
0
                            aEdgeColor = aVertColor[i];
1201
0
                            fEdgeAlpha = aVertAlpha[i];
1202
0
                        }
1203
0
                        if (!aColorCurve.empty())
1204
0
                        {
1205
                            // PresetColors override the centre/edge mix.
1206
0
                            auto [presetColor, presetAlpha] = sampleColor(tRaw);
1207
0
                            outColor = presetColor;
1208
0
                            outAlpha = presetAlpha;
1209
0
                        }
1210
0
                        else
1211
0
                        {
1212
                            // BlendFactors remap tRaw before mixing centre
1213
                            // and (per-segment Gouraud) edge colour. With
1214
                            // an empty factor curve sampleFactor returns t
1215
                            // unchanged so the mix is linear.
1216
0
                            const double tCurve = sampleFactor(tRaw);
1217
0
                            outColor.setRed(
1218
0
                                (1.0 - tCurve) * aCentreColor.getRed()
1219
0
                                + tCurve * aEdgeColor.getRed());
1220
0
                            outColor.setGreen(
1221
0
                                (1.0 - tCurve) * aCentreColor.getGreen()
1222
0
                                + tCurve * aEdgeColor.getGreen());
1223
0
                            outColor.setBlue(
1224
0
                                (1.0 - tCurve) * aCentreColor.getBlue()
1225
0
                                + tCurve * aEdgeColor.getBlue());
1226
0
                            outAlpha = (1.0 - tCurve) * fCentreAlpha
1227
0
                                       + tCurve * fEdgeAlpha;
1228
0
                        }
1229
0
                    };
1230
1231
0
                    Bitmap aBmp(Size(nBmpW, nBmpH), vcl::PixelFormat::N32_BPP);
1232
0
                    AlphaMask aAlpha(Size(nBmpW, nBmpH));
1233
0
                    aBmp.Erase(COL_BLACK);
1234
0
                    aAlpha.Erase(255);
1235
0
                    {
1236
0
                        BitmapScopedWriteAccess pBmpWrite(aBmp);
1237
0
                        BitmapScopedWriteAccess pAlphaWrite(aAlpha);
1238
0
                        if (!pBmpWrite || !pAlphaWrite)
1239
0
                            return;
1240
1241
                        // Track which pixels Pass 1 has painted so the
1242
                        // Clamp-mode Pass 2 can find the rest. Alpha == 0
1243
                        // alone is not a reliable "uncovered" marker
1244
                        // because a translucent surround colour can
1245
                        // legitimately produce alpha == 0 inside a fan
1246
                        // triangle.
1247
0
                        std::vector<bool> aCovered(
1248
0
                            static_cast<size_t>(nBmpW) * nBmpH, false);
1249
1250
0
                        auto writePixel
1251
0
                            = [&pBmpWrite, &pAlphaWrite, &aCovered, nBmpW](
1252
0
                                  sal_Int32 x, sal_Int32 y,
1253
0
                                  const basegfx::BColor& c, double a)
1254
0
                        {
1255
0
                            pBmpWrite->SetPixel(y, x, BitmapColor(
1256
0
                                static_cast<sal_uInt8>(std::lround(std::clamp(c.getRed(),   0.0, 1.0) * 255)),
1257
0
                                static_cast<sal_uInt8>(std::lround(std::clamp(c.getGreen(), 0.0, 1.0) * 255)),
1258
0
                                static_cast<sal_uInt8>(std::lround(std::clamp(c.getBlue(),  0.0, 1.0) * 255))));
1259
0
                            pAlphaWrite->SetPixelIndex(y, x, static_cast<sal_uInt8>(
1260
0
                                std::lround(std::clamp(a, 0.0, 1.0) * 255)));
1261
0
                            aCovered[static_cast<size_t>(y) * nBmpW + x] = true;
1262
0
                        };
1263
1264
                        // Pixel <-> brush-coord helpers. A pixel at integer
1265
                        // (x,y) has its centre at world coord
1266
                        //   bb.minX + (x + 0.5) / nBmpW * bb.width
1267
                        // (and similarly for y).
1268
0
                        auto pixelToWorld
1269
0
                            = [&aBrushBounds, nBmpW, nBmpH](
1270
0
                                  sal_Int32 x, sal_Int32 y) -> basegfx::B2DPoint
1271
0
                        {
1272
0
                            return basegfx::B2DPoint(
1273
0
                                aBrushBounds.getMinX()
1274
0
                                    + (x + 0.5) / nBmpW * aBrushBounds.getWidth(),
1275
0
                                aBrushBounds.getMinY()
1276
0
                                    + (y + 0.5) / nBmpH * aBrushBounds.getHeight());
1277
0
                        };
1278
0
                        auto worldToPixelX
1279
0
                            = [&aBrushBounds, nBmpW](double wx) -> double
1280
0
                        {
1281
0
                            return (wx - aBrushBounds.getMinX()) * nBmpW
1282
0
                                       / aBrushBounds.getWidth() - 0.5;
1283
0
                        };
1284
0
                        auto worldToPixelY
1285
0
                            = [&aBrushBounds, nBmpH](double wy) -> double
1286
0
                        {
1287
0
                            return (wy - aBrushBounds.getMinY()) * nBmpH
1288
0
                                       / aBrushBounds.getHeight() - 0.5;
1289
0
                        };
1290
1291
                        // Pass 1: rasterize each fan triangle (centre,
1292
                        // V_i, V_{i+1}) by iterating only the triangle's
1293
                        // pixel-space AABB. Cost is O(sum of triangle
1294
                        // areas), not O(bitmap area * triangle count).
1295
                        // Structurally this is what cairo's mesh-pattern
1296
                        // rasterizer does for PDF type-7 shadings: each
1297
                        // patch paints into its own pixel range, rather
1298
                        // than every pixel testing every patch.
1299
0
                        for (sal_uInt32 i = 0; i < nVerts; ++i)
1300
0
                        {
1301
0
                            const basegfx::B2DPoint aV0
1302
0
                                = aSweepPolygon.getB2DPoint(i);
1303
0
                            const basegfx::B2DPoint aV1
1304
0
                                = aSweepPolygon.getB2DPoint((i + 1) % nVerts);
1305
                            // Total triangle area (signed). Sign of
1306
                            // fTotal carries the polygon winding; a
1307
                            // point is inside the triangle when all
1308
                            // three sub-areas have that same sign.
1309
0
                            const double fTotal
1310
0
                                = signedCross(aCentre, aV0, aV1);
1311
0
                            if (std::abs(fTotal) < 1e-12)
1312
0
                                continue;
1313
0
                            const double s = fTotal > 0.0 ? 1.0 : -1.0;
1314
0
                            const double fInvAbsTotal = 1.0 / std::abs(fTotal);
1315
1316
0
                            const double fMinX = std::min({aCentre.getX(), aV0.getX(), aV1.getX()});
1317
0
                            const double fMaxX = std::max({aCentre.getX(), aV0.getX(), aV1.getX()});
1318
0
                            const double fMinY = std::min({aCentre.getY(), aV0.getY(), aV1.getY()});
1319
0
                            const double fMaxY = std::max({aCentre.getY(), aV0.getY(), aV1.getY()});
1320
0
                            const sal_Int32 nXMin = std::clamp<sal_Int32>(
1321
0
                                static_cast<sal_Int32>(std::floor(worldToPixelX(fMinX))),
1322
0
                                0, nBmpW - 1);
1323
0
                            const sal_Int32 nXMax = std::clamp<sal_Int32>(
1324
0
                                static_cast<sal_Int32>(std::ceil(worldToPixelX(fMaxX))),
1325
0
                                0, nBmpW - 1);
1326
0
                            const sal_Int32 nYMin = std::clamp<sal_Int32>(
1327
0
                                static_cast<sal_Int32>(std::floor(worldToPixelY(fMinY))),
1328
0
                                0, nBmpH - 1);
1329
0
                            const sal_Int32 nYMax = std::clamp<sal_Int32>(
1330
0
                                static_cast<sal_Int32>(std::ceil(worldToPixelY(fMaxY))),
1331
0
                                0, nBmpH - 1);
1332
1333
0
                            for (sal_Int32 y = nYMin; y <= nYMax; ++y)
1334
0
                            {
1335
0
                                for (sal_Int32 x = nXMin; x <= nXMax; ++x)
1336
0
                                {
1337
0
                                    const basegfx::B2DPoint aP = pixelToWorld(x, y);
1338
                                    // Barycentric weights via signed
1339
                                    // sub-triangle areas. wC = area(P,
1340
                                    // V0, V1) / area(C, V0, V1) etc.
1341
0
                                    const double wC
1342
0
                                        = s * signedCross(aP, aV0, aV1) * fInvAbsTotal;
1343
0
                                    const double w0
1344
0
                                        = s * signedCross(aCentre, aP, aV1) * fInvAbsTotal;
1345
0
                                    const double w1
1346
0
                                        = s * signedCross(aCentre, aV0, aP) * fInvAbsTotal;
1347
0
                                    constexpr double EPS = -1e-9;
1348
0
                                    if (wC < EPS || w0 < EPS || w1 < EPS)
1349
0
                                        continue;
1350
                                    // Centre/edge parameter (0 at centre,
1351
                                    // 1 at boundary segment).
1352
0
                                    const double tRaw
1353
0
                                        = std::clamp(1.0 - wC, 0.0, 1.0);
1354
0
                                    basegfx::BColor aColor;
1355
0
                                    double fAlpha = 0.0;
1356
0
                                    composeColor(i, w0, w1, tRaw, aColor, fAlpha);
1357
0
                                    writePixel(x, y, aColor, fAlpha);
1358
0
                                }
1359
0
                            }
1360
0
                        }
1361
1362
                        // Pass 2 (Clamp wrap mode): for pixels that fell
1363
                        // outside every fan triangle, fill with the
1364
                        // closest triangle's edge colour. This
1365
                        // eliminates sub-pixel transparent jaggies along
1366
                        // smooth Bezier fill polygons that extend
1367
                        // slightly past the flattened boundary.
1368
                        //
1369
                        // For other wrap modes we leave uncovered pixels
1370
                        // transparent so that adjacent tiles in the fill
1371
                        // polygon stay visually separate.
1372
0
                        if (brush->wrapMode == WrapModeClamp)
1373
0
                        {
1374
0
                            for (sal_Int32 y = 0; y < nBmpH; ++y)
1375
0
                            {
1376
0
                                for (sal_Int32 x = 0; x < nBmpW; ++x)
1377
0
                                {
1378
0
                                    if (aCovered[static_cast<size_t>(y) * nBmpW + x])
1379
0
                                        continue;
1380
0
                                    const basegfx::B2DPoint aP = pixelToWorld(x, y);
1381
0
                                    sal_uInt32 nClosestI = 0;
1382
0
                                    double fClosestW0 = 0.0;
1383
0
                                    double fClosestW1 = 0.0;
1384
0
                                    double fMaxNegMargin
1385
0
                                        = std::numeric_limits<double>::lowest();
1386
0
                                    for (sal_uInt32 i = 0; i < nVerts; ++i)
1387
0
                                    {
1388
0
                                        const basegfx::B2DPoint aV0
1389
0
                                            = aSweepPolygon.getB2DPoint(i);
1390
0
                                        const basegfx::B2DPoint aV1
1391
0
                                            = aSweepPolygon.getB2DPoint((i + 1) % nVerts);
1392
0
                                        const double fTotal
1393
0
                                            = signedCross(aCentre, aV0, aV1);
1394
0
                                        if (std::abs(fTotal) < 1e-12)
1395
0
                                            continue;
1396
0
                                        const double s
1397
0
                                            = fTotal > 0.0 ? 1.0 : -1.0;
1398
0
                                        const double fInvAbsTotal
1399
0
                                            = 1.0 / std::abs(fTotal);
1400
0
                                        const double wC
1401
0
                                            = s * signedCross(aP, aV0, aV1) * fInvAbsTotal;
1402
0
                                        const double w0
1403
0
                                            = s * signedCross(aCentre, aP, aV1) * fInvAbsTotal;
1404
0
                                        const double w1
1405
0
                                            = s * signedCross(aCentre, aV0, aP) * fInvAbsTotal;
1406
0
                                        const double fMin
1407
0
                                            = std::min({wC, w0, w1});
1408
0
                                        if (fMin > fMaxNegMargin)
1409
0
                                        {
1410
0
                                            fMaxNegMargin = fMin;
1411
0
                                            nClosestI = i;
1412
0
                                            fClosestW0 = w0;
1413
0
                                            fClosestW1 = w1;
1414
0
                                        }
1415
0
                                    }
1416
                                    // The pixel is outside its closest
1417
                                    // triangle (wC < 0), so the centre/
1418
                                    // edge parameter clamps to 1 and the
1419
                                    // colour is the pure edge mix.
1420
0
                                    basegfx::BColor aColor;
1421
0
                                    double fAlpha = 0.0;
1422
0
                                    composeColor(nClosestI, fClosestW0,
1423
0
                                                 fClosestW1, 1.0,
1424
0
                                                 aColor, fAlpha);
1425
0
                                    writePixel(x, y, aColor, fAlpha);
1426
0
                                }
1427
0
                            }
1428
0
                        }
1429
0
                    }
1430
0
                    Graphic aGraphic(Bitmap(aBmp, aAlpha));
1431
1432
                    // Map bitmap [0,1] to the brush's bounds in raw EMF
1433
                    // coords (with translation so the bitmap is anchored
1434
                    // where the brush actually lives, not at world origin).
1435
                    // maMapTransform then converts to world space, where
1436
                    // the fill polygon also lives - so the bitmap content
1437
                    // aligns with the fill polygon.
1438
0
                    const basegfx::B2DHomMatrix aBitmapToBrushSpace(
1439
0
                        aBrushBounds.getWidth(), 0.0, aBrushBounds.getMinX(),
1440
0
                        0.0, aBrushBounds.getHeight(), aBrushBounds.getMinY());
1441
1442
0
                    basegfx::B2DHomMatrix aBrushMatrix;
1443
0
                    if (brush->hasTransformation)
1444
0
                        aBrushMatrix = brush->brush_transformation;
1445
1446
0
                    const basegfx::B2DHomMatrix aTotalMatrix
1447
0
                        = maMapTransform * aBrushMatrix * aBitmapToBrushSpace;
1448
1449
0
                    basegfx::B2DHomMatrix aInvTotalMatrix = aTotalMatrix;
1450
0
                    if (!aInvTotalMatrix.invert())
1451
0
                        return;
1452
0
                    basegfx::B2DPolyPolygon aLocalPolygon = polygon;
1453
0
                    aLocalPolygon.transform(aInvTotalMatrix);
1454
1455
0
                    const bool bIsTiled = (brush->wrapMode != WrapModeClamp);
1456
1457
0
                    const drawinglayer::attribute::FillGraphicAttribute aFillAttribute(
1458
0
                        aGraphic, basegfx::B2DRange(0.0, 0.0, 1.0, 1.0), bIsTiled);
1459
1460
0
                    drawinglayer::primitive2d::Primitive2DReference xFillPrimitive
1461
0
                        = new drawinglayer::primitive2d::PolyPolygonGraphicPrimitive2D(
1462
0
                            aLocalPolygon,
1463
0
                            basegfx::B2DRange(0.0, 0.0, 1.0, 1.0),
1464
0
                            aFillAttribute);
1465
1466
0
                    mrTargetHolders.Current().append(
1467
0
                        new drawinglayer::primitive2d::TransformPrimitive2D(
1468
0
                            aTotalMatrix,
1469
0
                            drawinglayer::primitive2d::Primitive2DContainer({ xFillPrimitive })));
1470
0
                }
1471
0
            }
1472
0
        }
1473
0
    }
1474
1475
    const sal_uInt16 LOWERGAMMA = 1000;
1476
    const sal_uInt16 UPPERGAMMA = 2200;
1477
1478
    EmfPlusHelperData::EmfPlusHelperData(
1479
        SvMemoryStream& rMS,
1480
        wmfemfhelper::TargetHolders& rTargetHolders,
1481
        wmfemfhelper::PropertyHolders& rPropertyHolders)
1482
0
    :   mfPageScaleX(1.0),
1483
0
        mfPageScaleY(1.0),
1484
0
        mnOriginX(0),
1485
0
        mnOriginY(0),
1486
0
        mnHDPI(0),
1487
0
        mnVDPI(0),
1488
0
        mbSetTextContrast(false),
1489
0
        mnTextContrast(0),
1490
0
        mnFrameLeft(0),
1491
0
        mnFrameTop(0),
1492
0
        mnFrameRight(0),
1493
0
        mnFrameBottom(0),
1494
0
        mnPixX(0),
1495
0
        mnPixY(0),
1496
0
        mnMmX(0),
1497
0
        mnMmY(0),
1498
0
        mbMultipart(false),
1499
0
        mMFlags(0),
1500
0
        mdExtractedXScale(1.0),
1501
0
        mdExtractedYScale(1.0),
1502
0
        mrTargetHolders(rTargetHolders),
1503
0
        mrPropertyHolders(rPropertyHolders),
1504
0
        bIsGetDCProcessing(false)
1505
0
    {
1506
0
        rMS.ReadInt32(mnFrameLeft).ReadInt32(mnFrameTop).ReadInt32(mnFrameRight).ReadInt32(mnFrameBottom);
1507
0
        SAL_INFO("drawinglayer.emf", "EMF+ picture frame: " << mnFrameLeft << "," << mnFrameTop << " - " << mnFrameRight << "," << mnFrameBottom);
1508
0
        rMS.ReadInt32(mnPixX).ReadInt32(mnPixY).ReadInt32(mnMmX).ReadInt32(mnMmY);
1509
0
        SAL_INFO("drawinglayer.emf", "EMF+ ref device pixel size: " << mnPixX << "x" << mnPixY << " mm size: " << mnMmX << "x" << mnMmY);
1510
0
        readXForm(rMS, maBaseTransform);
1511
0
        SAL_INFO("drawinglayer.emf", "EMF+ base transform: " << maBaseTransform);
1512
0
        mappingChanged();
1513
0
    }
1514
1515
    EmfPlusHelperData::~EmfPlusHelperData()
1516
0
    {
1517
0
    }
1518
1519
    ::basegfx::B2DPolyPolygon EmfPlusHelperData::combineClip(::basegfx::B2DPolyPolygon const & leftPolygon, int combineMode, ::basegfx::B2DPolyPolygon const & rightPolygon)
1520
0
    {
1521
0
        basegfx::B2DPolyPolygon aClippedPolyPolygon;
1522
0
        switch (combineMode)
1523
0
        {
1524
0
        case EmfPlusCombineModeReplace:
1525
0
        {
1526
0
            aClippedPolyPolygon = rightPolygon;
1527
0
            break;
1528
0
        }
1529
0
        case EmfPlusCombineModeIntersect:
1530
0
        {
1531
0
            aClippedPolyPolygon = basegfx::utils::clipPolyPolygonOnPolyPolygon(
1532
0
                leftPolygon, rightPolygon, true, false);
1533
0
            break;
1534
0
        }
1535
0
        case EmfPlusCombineModeUnion:
1536
0
        {
1537
0
            aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationOr(leftPolygon, rightPolygon);
1538
0
            break;
1539
0
        }
1540
0
        case EmfPlusCombineModeXOR:
1541
0
        {
1542
0
            aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationXor(leftPolygon, rightPolygon);
1543
0
            break;
1544
0
        }
1545
0
        case EmfPlusCombineModeExclude:
1546
0
        {
1547
            // Replaces the existing region with the part of itself that is not in the new region.
1548
0
            aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(leftPolygon, rightPolygon);
1549
0
            break;
1550
0
        }
1551
0
        case EmfPlusCombineModeComplement:
1552
0
        {
1553
            // Replaces the existing region with the part of the new region that is not in the existing region.
1554
0
            aClippedPolyPolygon = ::basegfx::utils::solvePolygonOperationDiff(rightPolygon, leftPolygon);
1555
0
            break;
1556
0
        }
1557
0
        }
1558
0
        return aClippedPolyPolygon;
1559
0
    }
1560
1561
    void EmfPlusHelperData::processEmfPlusData(
1562
        SvMemoryStream& rMS,
1563
        const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/)
1564
0
    {
1565
0
        sal_uInt64 length = rMS.GetSize();
1566
1567
0
        if (length < 12)
1568
0
            SAL_WARN("drawinglayer.emf", "length is less than required header size");
1569
1570
        // 12 is minimal valid EMF+ record size; remaining bytes are padding
1571
0
        while (length >= 12)
1572
0
        {
1573
0
            sal_uInt16 type, flags;
1574
0
            sal_uInt32 size, dataSize;
1575
0
            sal_uInt64 next;
1576
1577
0
            rMS.ReadUInt16(type).ReadUInt16(flags).ReadUInt32(size).ReadUInt32(dataSize);
1578
1579
0
            next = rMS.Tell() + (size - 12);
1580
1581
0
            if (size < 12)
1582
0
            {
1583
0
                SAL_WARN("drawinglayer.emf", "Size field is less than 12 bytes");
1584
0
                break;
1585
0
            }
1586
0
            else if (size > length)
1587
0
            {
1588
0
                SAL_WARN("drawinglayer.emf", "Size field is greater than bytes left");
1589
0
                break;
1590
0
            }
1591
1592
0
            if (dataSize > (size - 12))
1593
0
            {
1594
0
                SAL_WARN("drawinglayer.emf", "DataSize field is greater than Size-12");
1595
0
                break;
1596
0
            }
1597
1598
0
            SAL_INFO("drawinglayer.emf", "EMF+ " << emfTypeToName(type) << " (0x" << std::hex << type << ")" << std::dec);
1599
0
            SAL_INFO("drawinglayer.emf", "EMF+\t record size: " << size);
1600
0
            SAL_INFO("drawinglayer.emf", "EMF+\t flags: 0x" << std::hex << flags << std::dec);
1601
0
            SAL_INFO("drawinglayer.emf", "EMF+\t data size: " << dataSize);
1602
1603
0
            if (bIsGetDCProcessing)
1604
0
            {
1605
0
                if (aGetDCState.getClipPolyPolygonActive())
1606
0
                {
1607
0
                    SAL_INFO("drawinglayer.emf", "EMF+\t Restore region to GetDC saved");
1608
0
                    wmfemfhelper::HandleNewClipRegion(aGetDCState.getClipPolyPolygon(), mrTargetHolders,
1609
0
                                                      mrPropertyHolders);
1610
0
                }
1611
0
                else
1612
0
                {
1613
0
                    SAL_INFO("drawinglayer.emf", "EMF+\t Disable clipping");
1614
0
                    wmfemfhelper::HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders,
1615
0
                                                      mrPropertyHolders);
1616
0
                }
1617
0
                bIsGetDCProcessing = false;
1618
0
            }
1619
0
            if (type == EmfPlusRecordTypeObject && ((mbMultipart && (flags & 0x7fff) == (mMFlags & 0x7fff)) || (flags & 0x8000)))
1620
0
            {
1621
0
                if (!mbMultipart)
1622
0
                {
1623
0
                    mbMultipart = true;
1624
0
                    mMFlags = flags;
1625
0
                    mMStream.Seek(0);
1626
0
                }
1627
1628
0
                OSL_ENSURE(dataSize >= 4, "No room for TotalObjectSize in EmfPlusContinuedObjectRecord");
1629
1630
                // 1st 4 bytes are TotalObjectSize
1631
0
                mMStream.WriteBytes(static_cast<const char *>(rMS.GetData()) + rMS.Tell() + 4, dataSize - 4);
1632
0
                SAL_INFO("drawinglayer.emf", "EMF+ read next object part size: " << size << " type: " << type << " flags: " << flags << " data size: " << dataSize);
1633
0
            }
1634
0
            else
1635
0
            {
1636
0
                if (mbMultipart)
1637
0
                {
1638
0
                    SAL_INFO("drawinglayer.emf", "EMF+ multipart record flags: " << mMFlags);
1639
0
                    mMStream.Seek(0);
1640
0
                    processObjectRecord(mMStream, mMFlags, 0, true);
1641
0
                }
1642
1643
0
                mbMultipart = false;
1644
0
            }
1645
1646
0
            if (type != EmfPlusRecordTypeObject || !(flags & 0x8000))
1647
0
            {
1648
0
                switch (type)
1649
0
                {
1650
0
                    case EmfPlusRecordTypeHeader:
1651
0
                    {
1652
0
                        sal_uInt32 version, emfPlusFlags;
1653
0
                        SAL_INFO("drawinglayer.emf", "EMF+\tDual: " << ((flags & 1) ? "true" : "false"));
1654
1655
0
                        rMS.ReadUInt32(version).ReadUInt32(emfPlusFlags).ReadUInt32(mnHDPI).ReadUInt32(mnVDPI);
1656
0
                        SAL_INFO("drawinglayer.emf", "EMF+\tVersion: 0x" << std::hex << version);
1657
0
                        SAL_INFO("drawinglayer.emf", "EMF+\tEmf+ Flags: 0x"  << emfPlusFlags << std::dec);
1658
0
                        SAL_INFO("drawinglayer.emf", "EMF+\tMetafile was recorded with a reference device context for " << ((emfPlusFlags & 1) ? "video display" : "printer"));
1659
0
                        SAL_INFO("drawinglayer.emf", "EMF+\tHorizontal DPI: " << mnHDPI);
1660
0
                        SAL_INFO("drawinglayer.emf", "EMF+\tVertical DPI: " << mnVDPI);
1661
0
                        break;
1662
0
                    }
1663
0
                    case EmfPlusRecordTypeEndOfFile:
1664
0
                    {
1665
0
                        break;
1666
0
                    }
1667
0
                    case EmfPlusRecordTypeComment:
1668
0
                    {
1669
#if OSL_DEBUG_LEVEL > 1
1670
                        unsigned char data;
1671
                        OUString hexdata;
1672
1673
                        SAL_INFO("drawinglayer.emf", "EMF+\tDatasize: 0x" << std::hex << dataSize << std::dec);
1674
1675
                        for (sal_uInt32 i=0; i<dataSize; i++)
1676
                        {
1677
                            rMS.ReadUChar(data);
1678
1679
                            if (i % 16 == 0)
1680
                                hexdata += "\n";
1681
1682
                            OUString padding;
1683
                            if ((data & 0xF0) == 0)
1684
                                padding = "0";
1685
1686
                            hexdata += "0x" + padding + OUString::number(data, 16) + " ";
1687
                        }
1688
1689
                        SAL_INFO("drawinglayer.emf", "EMF+\t" << hexdata);
1690
#endif
1691
0
                        break;
1692
0
                    }
1693
0
                    case EmfPlusRecordTypeGetDC:
1694
0
                    {
1695
0
                        bIsGetDCProcessing = true;
1696
0
                        aGetDCState = mrPropertyHolders.Current();
1697
0
                        SAL_INFO("drawinglayer.emf", "EMF+\tAlready used in svtools wmf/emf filter parser");
1698
0
                        break;
1699
0
                    }
1700
0
                    case EmfPlusRecordTypeObject:
1701
0
                    {
1702
0
                        processObjectRecord(rMS, flags, dataSize);
1703
0
                        break;
1704
0
                    }
1705
0
                    case EmfPlusRecordTypeFillPie:
1706
0
                    case EmfPlusRecordTypeDrawPie:
1707
0
                    case EmfPlusRecordTypeDrawArc:
1708
0
                    {
1709
0
                        float startAngle, sweepAngle;
1710
1711
                        // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
1712
0
                        sal_uInt32 brushIndexOrColor = 999;
1713
1714
0
                        if (type == EmfPlusRecordTypeFillPie)
1715
0
                        {
1716
0
                            rMS.ReadUInt32(brushIndexOrColor);
1717
0
                            SAL_INFO("drawinglayer.emf", "EMF+\t FillPie colorOrIndex: " << brushIndexOrColor);
1718
0
                        }
1719
0
                        else if (type == EmfPlusRecordTypeDrawPie)
1720
0
                        {
1721
0
                            SAL_INFO("drawinglayer.emf", "EMF+\t DrawPie");
1722
0
                        }
1723
0
                        else
1724
0
                        {
1725
0
                            SAL_INFO("drawinglayer.emf", "EMF+\t DrawArc");
1726
0
                        }
1727
1728
0
                        rMS.ReadFloat(startAngle).ReadFloat(sweepAngle);
1729
0
                        float dx, dy, dw, dh;
1730
0
                        ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
1731
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
1732
0
                        startAngle = basegfx::deg2rad(startAngle);
1733
0
                        sweepAngle = basegfx::deg2rad(sweepAngle);
1734
0
                        float endAngle = startAngle + sweepAngle;
1735
0
                        startAngle = fmodf(startAngle, static_cast<float>(M_PI * 2));
1736
1737
0
                        if (startAngle < 0.0)
1738
0
                        {
1739
0
                            startAngle += static_cast<float>(M_PI * 2.0);
1740
0
                        }
1741
0
                        endAngle = fmodf(endAngle, static_cast<float>(M_PI * 2.0));
1742
1743
0
                        if (endAngle < 0.0)
1744
0
                        {
1745
0
                            endAngle += static_cast<float>(M_PI * 2.0);
1746
0
                        }
1747
0
                        if (sweepAngle < 0)
1748
0
                        {
1749
0
                            std::swap(endAngle, startAngle);
1750
0
                        }
1751
1752
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Adjusted angles: start " <<
1753
0
                            basegfx::rad2deg(startAngle) << ", end: " << basegfx::rad2deg(endAngle) <<
1754
0
                            " startAngle: " << startAngle << " sweepAngle: " << sweepAngle);
1755
0
                        const ::basegfx::B2DPoint centerPoint(dx + 0.5 * dw, dy + 0.5 * dh);
1756
0
                        ::basegfx::B2DPolygon polygon(
1757
0
                            ::basegfx::utils::createPolygonFromEllipseSegment(centerPoint,
1758
0
                                                                              0.5 * dw, 0.5 * dh,
1759
0
                                                                              startAngle, endAngle));
1760
0
                        if (type != EmfPlusRecordTypeDrawArc)
1761
0
                        {
1762
0
                            polygon.append(centerPoint);
1763
0
                            polygon.setClosed(true);
1764
0
                        }
1765
0
                        ::basegfx::B2DPolyPolygon polyPolygon(polygon);
1766
0
                        polyPolygon.transform(maMapTransform);
1767
0
                        if (type == EmfPlusRecordTypeFillPie)
1768
0
                            EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor);
1769
0
                        else
1770
0
                            EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
1771
0
                    }
1772
0
                    break;
1773
0
                    case EmfPlusRecordTypeFillPath:
1774
0
                    {
1775
0
                        sal_uInt32 index = flags & 0xff;
1776
0
                        sal_uInt32 brushIndexOrColor;
1777
0
                        rMS.ReadUInt32(brushIndexOrColor);
1778
0
                        SAL_INFO("drawinglayer.emf", "EMF+ FillPath slot: " << index);
1779
1780
0
                        EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[index].get());
1781
0
                        if (path)
1782
0
                            EMFPPlusFillPolygon(path->GetPolygon(*this), flags & 0x8000, brushIndexOrColor);
1783
0
                        else
1784
0
                            SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillPath missing path");
1785
0
                    }
1786
0
                    break;
1787
0
                    case EmfPlusRecordTypeFillRegion:
1788
0
                    {
1789
0
                        sal_uInt32 index = flags & 0xff;
1790
0
                        sal_uInt32 brushIndexOrColor;
1791
0
                        rMS.ReadUInt32(brushIndexOrColor);
1792
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t FillRegion slot: " << index);
1793
1794
0
                        EMFPRegion* region = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
1795
0
                        if (region)
1796
0
                            EMFPPlusFillPolygon(region->regionPolyPolygon, flags & 0x8000, brushIndexOrColor);
1797
0
                        else
1798
0
                            SAL_WARN("drawinglayer.emf", "EMF+\tEmfPlusRecordTypeFillRegion missing region");
1799
0
                    }
1800
0
                    break;
1801
0
                    case EmfPlusRecordTypeDrawEllipse:
1802
0
                    case EmfPlusRecordTypeFillEllipse:
1803
0
                    {
1804
                        // Intentionally very bogus initial value to avoid MSVC complaining about potentially uninitialized local
1805
                        // variable. As long as the code stays as intended, this variable will be assigned a (real) value in the case
1806
                        // when it is later used.
1807
0
                        sal_uInt32 brushIndexOrColor = 1234567;
1808
1809
0
                        if (type == EmfPlusRecordTypeFillEllipse)
1810
0
                        {
1811
0
                            rMS.ReadUInt32(brushIndexOrColor);
1812
0
                        }
1813
1814
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t " << (type == EmfPlusRecordTypeFillEllipse ? "Fill" : "Draw") << "Ellipse slot: " << (flags & 0xff));
1815
0
                        float dx, dy, dw, dh;
1816
0
                        ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
1817
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
1818
0
                        ::basegfx::B2DPolyPolygon polyPolygon(
1819
0
                            ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(dx + 0.5 * dw, dy + 0.5 * dh),
1820
0
                                                                       0.5 * dw, 0.5 * dh));
1821
0
                        polyPolygon.transform(maMapTransform);
1822
0
                        if (type == EmfPlusRecordTypeFillEllipse)
1823
0
                            EMFPPlusFillPolygon(polyPolygon, flags & 0x8000, brushIndexOrColor);
1824
0
                        else
1825
0
                            EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
1826
0
                    }
1827
0
                    break;
1828
0
                    case EmfPlusRecordTypeFillRects:
1829
0
                    case EmfPlusRecordTypeDrawRects:
1830
0
                    {
1831
                        // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
1832
0
                        sal_uInt32 brushIndexOrColor = 999;
1833
0
                        ::basegfx::B2DPolyPolygon polyPolygon;
1834
0
                        sal_uInt32 rectangles;
1835
0
                        float x, y, width, height;
1836
0
                        const bool isColor = (flags & 0x8000);
1837
0
                        ::basegfx::B2DPolygon polygon;
1838
1839
0
                        if (EmfPlusRecordTypeFillRects == type)
1840
0
                        {
1841
0
                            SAL_INFO("drawinglayer.emf", "EMF+\t FillRects");
1842
0
                            rMS.ReadUInt32(brushIndexOrColor);
1843
0
                            SAL_INFO("drawinglayer.emf", "EMF+\t" << (isColor ? "color" : "brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
1844
0
                        }
1845
0
                        else
1846
0
                        {
1847
0
                            SAL_INFO("drawinglayer.emf", "EMF+\t DrawRects");
1848
0
                        }
1849
1850
0
                        rMS.ReadUInt32(rectangles);
1851
0
                        for (sal_uInt32 i = 0; i < rectangles; i++)
1852
0
                        {
1853
0
                            ReadRectangle(rMS, x, y, width, height, bool(flags & 0x4000));
1854
0
                            polygon.clear();
1855
0
                            polygon.append(Map(x, y));
1856
0
                            polygon.append(Map(x + width, y));
1857
0
                            polygon.append(Map(x + width, y + height));
1858
0
                            polygon.append(Map(x, y + height));
1859
0
                            polygon.setClosed(true);
1860
1861
0
                            SAL_INFO("drawinglayer.emf", "EMF+\t\t rectangle: " << x << ", "<< y << " " << width << "x" << height);
1862
1863
0
                            if (type == EmfPlusRecordTypeFillRects)
1864
0
                            {
1865
                                // Per MS-EMFPLUS each rect fills its
1866
                                // interior independently. Emitting them
1867
                                // as one polypolygon would cancel
1868
                                // pairwise overlaps under drawinglayer's
1869
                                // even-odd fill rule, leaving hollow
1870
                                // stripes where overlapping squares meet.
1871
0
                                EMFPPlusFillPolygon(::basegfx::B2DPolyPolygon(polygon),
1872
0
                                                    isColor, brushIndexOrColor);
1873
0
                            }
1874
0
                            else
1875
0
                            {
1876
0
                                polyPolygon.append(polygon);
1877
0
                            }
1878
0
                        }
1879
0
                        if (type == EmfPlusRecordTypeDrawRects)
1880
0
                            EMFPPlusDrawPolygon(polyPolygon, flags & 0xff);
1881
0
                        break;
1882
0
                    }
1883
0
                    case EmfPlusRecordTypeFillPolygon:
1884
0
                    {
1885
0
                        sal_uInt32 brushIndexOrColor, points;
1886
1887
0
                        rMS.ReadUInt32(brushIndexOrColor);
1888
0
                        rMS.ReadUInt32(points);
1889
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points);
1890
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << " : 0x" << std::hex << brushIndexOrColor << std::dec);
1891
1892
0
                        EMFPPath path(points, true);
1893
0
                        path.Read(rMS, flags);
1894
1895
0
                        EMFPPlusFillPolygon(path.GetPolygon(*this), flags & 0x8000, brushIndexOrColor);
1896
0
                        break;
1897
0
                    }
1898
0
                    case EmfPlusRecordTypeDrawLines:
1899
0
                    {
1900
0
                        sal_uInt32 points;
1901
0
                        rMS.ReadUInt32(points);
1902
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Points: " << points);
1903
0
                        EMFPPath path(points, true);
1904
0
                        path.Read(rMS, flags);
1905
1906
                        // 0x2000 bit indicates whether to draw an extra line between the last point
1907
                        // and the first point, to close the shape.
1908
0
                        EMFPPlusDrawPolygon(path.GetPolygon(*this, true, (flags & 0x2000)), flags);
1909
1910
0
                        break;
1911
0
                    }
1912
0
                    case EmfPlusRecordTypeDrawPath:
1913
0
                    {
1914
0
                        sal_uInt32 penIndex;
1915
0
                        rMS.ReadUInt32(penIndex);
1916
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Pen: " << penIndex);
1917
1918
0
                        EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get());
1919
0
                        if (path)
1920
0
                            EMFPPlusDrawPolygon(path->GetPolygon(*this), penIndex);
1921
0
                        else
1922
0
                            SAL_WARN("drawinglayer.emf", "\t\tEmfPlusRecordTypeDrawPath missing path");
1923
1924
0
                        break;
1925
0
                    }
1926
0
                    case EmfPlusRecordTypeDrawBeziers:
1927
0
                    {
1928
0
                        sal_uInt32 aCount;
1929
0
                        float x1, y1, x2, y2, x3, y3, x4, y4;
1930
0
                        ::basegfx::B2DPolygon aPolygon;
1931
0
                        rMS.ReadUInt32(aCount);
1932
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t DrawBeziers slot: " << (flags & 0xff));
1933
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Number of points: " << aCount);
1934
0
                        SAL_WARN_IF((aCount - 1) % 3 != 0, "drawinglayer.emf",
1935
0
                                    "EMF+\t Bezier Draw not support number of points other than 4, 7, "
1936
0
                                    "10, 13, 16...");
1937
1938
0
                        if (aCount < 4)
1939
0
                        {
1940
0
                            SAL_WARN("drawinglayer.emf", "EMF+\t Bezier Draw does not support less "
1941
0
                                                         "than 4 points. Number of points: "
1942
0
                                                             << aCount);
1943
0
                            break;
1944
0
                        }
1945
1946
0
                        ReadPoint(rMS, x1, y1, flags);
1947
                        // We need to add first starting point
1948
0
                        aPolygon.append(Map(x1, y1));
1949
0
                        SAL_INFO("drawinglayer.emf",
1950
0
                                 "EMF+\t Bezier starting point: " << x1 << "," << y1);
1951
0
                        for (sal_uInt32 i = 4; i <= aCount; i += 3)
1952
0
                        {
1953
0
                            ReadPoint(rMS, x2, y2, flags);
1954
0
                            ReadPoint(rMS, x3, y3, flags);
1955
0
                            ReadPoint(rMS, x4, y4, flags);
1956
1957
0
                            SAL_INFO("drawinglayer.emf",
1958
0
                                     "EMF+\t Bezier points: " << x2 << "," << y2 << " " << x3 << ","
1959
0
                                                              << y3 << " " << x4 << "," << y4);
1960
0
                            aPolygon.appendBezierSegment(Map(x2, y2), Map(x3, y3), Map(x4, y4));
1961
0
                        }
1962
0
                        EMFPPlusDrawPolygon(::basegfx::B2DPolyPolygon(aPolygon), flags & 0xff);
1963
0
                        break;
1964
0
                    }
1965
0
                    case EmfPlusRecordTypeDrawCurve:
1966
0
                    {
1967
0
                        sal_uInt32 aOffset, aNumSegments, points;
1968
0
                        float aTension;
1969
0
                        rMS.ReadFloat(aTension);
1970
0
                        rMS.ReadUInt32(aOffset);
1971
0
                        rMS.ReadUInt32(aNumSegments);
1972
0
                        rMS.ReadUInt32(points);
1973
0
                        SAL_WARN("drawinglayer.emf",
1974
0
                                "EMF+\t Tension: " << aTension << " Offset: " << aOffset
1975
0
                                                   << " NumSegments: " << aNumSegments
1976
0
                                                   << " Points: " << points);
1977
1978
0
                        EMFPPath path(points, true);
1979
0
                        path.Read(rMS, flags);
1980
1981
0
                        if (points >= 2)
1982
0
                            EMFPPlusDrawPolygon(
1983
0
                                path.GetCardinalSpline(*this, aTension, aOffset, aNumSegments),
1984
0
                                flags & 0xff);
1985
0
                        else
1986
0
                            SAL_WARN("drawinglayer.emf", "Not enough number of points");
1987
0
                        break;
1988
0
                    }
1989
0
                    case EmfPlusRecordTypeDrawClosedCurve:
1990
0
                    case EmfPlusRecordTypeFillClosedCurve:
1991
0
                    {
1992
                        // Silent MSVC warning C4701: potentially uninitialized local variable 'brushIndexOrColor' used
1993
0
                        sal_uInt32 brushIndexOrColor = 999, points;
1994
0
                        float aTension;
1995
0
                        if (type == EmfPlusRecordTypeFillClosedCurve)
1996
0
                        {
1997
0
                            rMS.ReadUInt32(brushIndexOrColor);
1998
0
                            SAL_INFO(
1999
0
                                "drawinglayer.emf",
2000
0
                                "EMF+\t Fill Mode: " << (flags & 0x2000 ? "Winding" : "Alternate"));
2001
0
                        }
2002
0
                        rMS.ReadFloat(aTension);
2003
0
                        rMS.ReadUInt32(points);
2004
0
                        SAL_WARN("drawinglayer.emf",
2005
0
                                "EMF+\t Tension: " << aTension << " Points: " << points);
2006
0
                        SAL_INFO("drawinglayer.emf",
2007
0
                                "EMF+\t " << (flags & 0x8000 ? "Color" : "Brush index") << " : 0x"
2008
0
                                        << std::hex << brushIndexOrColor << std::dec);
2009
0
                        if (points < 3)
2010
0
                        {
2011
0
                            SAL_WARN("drawinglayer.emf", "Not enough number of points");
2012
0
                            break;
2013
0
                        }
2014
0
                        EMFPPath path(points, true);
2015
0
                        path.Read(rMS, flags);
2016
0
                        if (type == EmfPlusRecordTypeFillClosedCurve)
2017
0
                        {
2018
0
                            ::basegfx::B2DPolyPolygon aPoly
2019
0
                                = path.GetClosedCardinalSpline(*this, aTension);
2020
0
                            if (flags & 0x2000)
2021
0
                                aPoly = convertWindingToEvenOdd(aPoly);
2022
0
                            EMFPPlusFillPolygon(aPoly, flags & 0x8000, brushIndexOrColor);
2023
0
                        }
2024
0
                        else
2025
0
                            EMFPPlusDrawPolygon(path.GetClosedCardinalSpline(*this, aTension),
2026
0
                                                flags & 0xff);
2027
0
                        break;
2028
0
                    }
2029
0
                    case EmfPlusRecordTypeDrawImage:
2030
0
                    case EmfPlusRecordTypeDrawImagePoints:
2031
0
                    {
2032
0
                        sal_uInt32 imageAttributesId;
2033
0
                        sal_Int32 sourceUnit;
2034
0
                        rMS.ReadUInt32(imageAttributesId).ReadInt32(sourceUnit);
2035
0
                        SAL_INFO("drawinglayer.emf",
2036
0
                                "EMF+\t " << (type == EmfPlusRecordTypeDrawImage ? "DrawImage"
2037
0
                                                                                : "DrawImagePoints")
2038
0
                                        << " image attributes Id: " << imageAttributesId
2039
0
                                        << " source unit: " << sourceUnit);
2040
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t TODO: use image attributes");
2041
2042
                        // Source unit of measurement type must be 1 pixel
2043
0
                        if (EMFPImage* image = sourceUnit == UnitTypePixel ?
2044
0
                                dynamic_cast<EMFPImage*>(maEMFPObjects[flags & 0xff].get()) :
2045
0
                                nullptr)
2046
0
                        {
2047
0
                            float sx, sy, sw, sh;
2048
0
                            ReadRectangle(rMS, sx, sy, sw, sh);
2049
2050
0
                            ::tools::Rectangle aSource(Point(sx, sy), Size(sw + 1, sh + 1));
2051
0
                            SAL_INFO("drawinglayer.emf",
2052
0
                                     "EMF+\t "
2053
0
                                        << (type == EmfPlusRecordTypeDrawImage ? "DrawImage"
2054
0
                                                                                : "DrawImagePoints")
2055
0
                                        << " source rectangle: " << sx << "," << sy << " " << sw << "x"
2056
0
                                        << sh);
2057
2058
0
                            float dx(0.), dy(0.), dw(0.), dh(0.);
2059
0
                            double fShearX = 0.0;
2060
0
                            double fShearY = 0.0;
2061
0
                            if (type == EmfPlusRecordTypeDrawImagePoints)
2062
0
                            {
2063
0
                                sal_uInt32 aCount;
2064
0
                                rMS.ReadUInt32(aCount);
2065
2066
                                // Number of points used by DrawImagePoints. Exactly 3 points must be specified.
2067
0
                                if (aCount != 3)
2068
0
                                {
2069
0
                                    SAL_WARN("drawinglayer.emf", "EMF+\t Wrong EMF+ file. Expected "
2070
0
                                                                "3 points, received: "
2071
0
                                                                    << aCount);
2072
0
                                    break;
2073
0
                                }
2074
0
                                float x1, y1, x2, y2, x3, y3;
2075
2076
0
                                ReadPoint(rMS, x1, y1, flags); // upper-left point
2077
0
                                ReadPoint(rMS, x2, y2, flags); // upper-right
2078
0
                                ReadPoint(rMS, x3, y3, flags); // lower-left
2079
2080
0
                                dx = x1;
2081
0
                                dy = y1;
2082
0
                                dw = x2 - x1;
2083
0
                                dh = y3 - y1;
2084
0
                                fShearX = x3 - x1;
2085
0
                                fShearY = y2 - y1;
2086
0
                            }
2087
0
                            else if (type == EmfPlusRecordTypeDrawImage)
2088
0
                                ReadRectangle(rMS, dx, dy, dw, dh, bool(flags & 0x4000));
2089
2090
0
                            SAL_INFO("drawinglayer.emf",
2091
0
                                    "EMF+\t Destination rectangle: " << dx << "," << dy << " " << dw << "x" << dh);
2092
0
                            Size aSize;
2093
0
                            if (image->type == ImageDataTypeBitmap)
2094
0
                            {
2095
0
                                aSize = image->graphic.GetBitmap().GetSizePixel();
2096
0
                                SAL_INFO("drawinglayer.emf", "EMF+\t Bitmap size: " << aSize.Width()
2097
0
                                                                                    << "x"
2098
0
                                                                                    << aSize.Height());
2099
0
                                if (sx < 0)
2100
0
                                {
2101
                                    // If src position is negative then we need shift image to right
2102
0
                                    dx = dx + ((-sx) / sw) * dw;
2103
0
                                    if (sx + sw <= aSize.Width())
2104
0
                                        dw = ((sw + sx) / sw) * dw;
2105
0
                                    else
2106
0
                                        dw = (aSize.Width() / sw) * dw;
2107
0
                                }
2108
0
                                else if (sx + sw > aSize.Width())
2109
                                    // If the src image is smaller that what we want to cut, then we need to scale down
2110
0
                                    dw = ((aSize.Width() - sx) / sw) * dw;
2111
2112
0
                                if (sy < 0)
2113
0
                                {
2114
0
                                    dy = dy + ((-sy) / sh) * dh;
2115
0
                                    if (sy + sh <= aSize.Height())
2116
0
                                        dh = ((sh + sy) / sh) * dh;
2117
0
                                    else
2118
0
                                        dh = (aSize.Height() / sh) * dh;
2119
0
                                }
2120
0
                                else if (sy + sh > aSize.Height())
2121
0
                                    dh = ((aSize.Height() - sy) / sh) * dh;
2122
0
                            }
2123
2124
0
                            const basegfx::B2DHomMatrix aDestMatrix =
2125
0
                                maMapTransform * basegfx::B2DHomMatrix(
2126
0
                                    /* Row 0, Column 0 */ dw,
2127
0
                                    /* Row 0, Column 1 */ fShearX,
2128
0
                                    /* Row 0, Column 2 */ dx,
2129
0
                                    /* Row 1, Column 0 */ fShearY,
2130
0
                                    /* Row 1, Column 1 */ dh,
2131
0
                                    /* Row 1, Column 2 */ dy);
2132
2133
0
                            if (image->type == ImageDataTypeBitmap)
2134
0
                            {
2135
0
                                Bitmap aBmp(image->graphic.GetBitmap());
2136
0
                                aBmp.Crop(aSource);
2137
0
                                aSize = aBmp.GetSizePixel();
2138
0
                                if (aSize.Width() > 0 && aSize.Height() > 0)
2139
0
                                {
2140
0
                                    mrTargetHolders.Current().append(
2141
0
                                        new drawinglayer::primitive2d::BitmapPrimitive2D(
2142
0
                                            aBmp, aDestMatrix));
2143
0
                                }
2144
0
                                else
2145
0
                                    SAL_WARN("drawinglayer.emf", "EMF+\t warning: empty bitmap");
2146
0
                            }
2147
0
                            else if (image->type == ImageDataTypeMetafile)
2148
0
                            {
2149
0
                                SAL_INFO("drawinglayer.emf",
2150
0
                                        "\t\t Metafile Dimensions: " << image->x << "," << image->y
2151
0
                                                                     << " [" << image->width << "x"
2152
0
                                                                     << image->height);
2153
2154
0
                                const GDIMetaFile aGDI(image->graphic.GetGDIMetaFile());
2155
0
                                basegfx::B2DHomMatrix aFinalMatrix = aDestMatrix;
2156
0
                                if (image->width > 0 && image->height > 0 && sw > 0. && sh > 0.)
2157
0
                                {
2158
0
                                    const double fRelSrcX = sx - static_cast<double>(image->x);
2159
0
                                    const double fRelSrcY = sy - static_cast<double>(image->y);
2160
0
                                    const basegfx::B2DHomMatrix aNormalizeMatrix(
2161
0
                                        /* Row 0, Column 0 */ static_cast<double>(image->width) / sw,
2162
0
                                        /* Row 0, Column 1 */ 0.0,
2163
0
                                        /* Row 0, Column 2 */ -fRelSrcX / sw,
2164
0
                                        /* Row 1, Column 0 */ 0.0,
2165
0
                                        /* Row 1, Column 1 */ static_cast<double>(image->height) / sh,
2166
0
                                        /* Row 1, Column 2 */ -fRelSrcY / sh);
2167
2168
0
                                    aFinalMatrix = aDestMatrix * aNormalizeMatrix;
2169
0
                                }
2170
2171
0
                                basegfx::B2DPolygon aClipPoly = basegfx::utils::createUnitPolygon();
2172
0
                                aClipPoly.transform(aDestMatrix);
2173
2174
0
                                mrTargetHolders.Current().append(
2175
0
                                    new drawinglayer::primitive2d::MaskPrimitive2D(
2176
0
                                        basegfx::B2DPolyPolygon(aClipPoly),
2177
0
                                        drawinglayer::primitive2d::Primitive2DContainer {
2178
0
                                            new drawinglayer::primitive2d::MetafilePrimitive2D(aFinalMatrix, aGDI)
2179
0
                                        }
2180
0
                                    )
2181
0
                                );
2182
0
                            }
2183
0
                        }
2184
0
                        else
2185
0
                        {
2186
0
                            SAL_WARN("drawinglayer.emf",
2187
0
                                    "EMF+\tDrawImage(Points) Wrong EMF+ file. Only Unit Type Pixel is "
2188
0
                                    "support by EMF+ specification for DrawImage(Points)");
2189
0
                        }
2190
0
                        break;
2191
0
                    }
2192
0
                    case EmfPlusRecordTypeDrawString:
2193
0
                    {
2194
0
                        sal_uInt32 brushId(0), formatId(0), stringLength(0);
2195
0
                        rMS.ReadUInt32(brushId).ReadUInt32(formatId).ReadUInt32(stringLength);
2196
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t FontId: " << OUString::number(flags & 0xFF));
2197
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t BrushId: " << BrushIDToString(flags, brushId));
2198
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t FormatId: " << formatId);
2199
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Length: " << stringLength);
2200
2201
                        // read the layout rectangle
2202
0
                        float lx, ly, lw, lh;
2203
0
                        rMS.ReadFloat(lx).ReadFloat(ly).ReadFloat(lw).ReadFloat(lh);
2204
2205
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t DrawString layoutRect: " << lx << "," << ly << " - " << lw << "x" << lh);
2206
                        // parse the string
2207
0
                        const OUString text = read_uInt16s_ToOUString(rMS, stringLength);
2208
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t DrawString string: " << text);
2209
                        // get the stringFormat from the Object table ( this is OPTIONAL and may be nullptr )
2210
0
                        const EMFPStringFormat *stringFormat = dynamic_cast<EMFPStringFormat*>(maEMFPObjects[formatId & 0xff].get());
2211
                        // get the font from the flags
2212
0
                        const EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get());
2213
0
                        if (!font)
2214
0
                        {
2215
0
                            break;
2216
0
                        }
2217
0
                        mrPropertyHolders.Current().setFont(vcl::Font(font->family, Size(font->emSize, font->emSize)));
2218
2219
0
                        drawinglayer::attribute::FontAttribute fontAttribute(
2220
0
                            font->family,                                          // font family
2221
0
                            u""_ustr,                                                    // (no) font style
2222
0
                            font->Bold() ? 8u : 1u,                                // weight: 8 = bold
2223
0
                            font->family == "SYMBOL",                              // symbol
2224
0
                            stringFormat && stringFormat->DirectionVertical(),     // vertical
2225
0
                            font->Italic(),                                        // italic
2226
0
                            false,                                                 // monospaced
2227
0
                            false,                                                 // outline = false, no such thing in MS-EMFPLUS
2228
0
                            stringFormat && stringFormat->DirectionRightToLeft(),  // right-to-left
2229
0
                            false);                                                // BiDiStrong
2230
2231
0
                        css::lang::Locale locale;
2232
0
                        double stringAlignmentHorizontalOffset = 0.0;
2233
0
                        double stringAlignmentVerticalOffset = font->emSize;
2234
0
                        std::vector<double> aDXArray;
2235
0
                        if (stringFormat)
2236
0
                        {
2237
0
                            LanguageTag aLanguageTag(static_cast<LanguageType>(stringFormat->language));
2238
0
                            locale = aLanguageTag.getLocale();
2239
0
                            drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
2240
2241
0
                            aTextLayouter.setFontAttribute(fontAttribute, font->emSize,
2242
0
                                font->emSize, locale);
2243
2244
0
                            double fTextWidth = aTextLayouter.getTextWidth(text, 0, stringLength);
2245
2246
                            // Apply character spacing (tracking).  Tracking is a multiplier on
2247
                            // each character's natural advance; default 1.0 means no change.
2248
0
                            if (stringFormat->tracking != 1.0f && stringLength > 0)
2249
0
                            {
2250
0
                                aDXArray = aTextLayouter.getTextArray(text, 0, stringLength);
2251
0
                                for (double& rPos : aDXArray)
2252
0
                                    rPos *= stringFormat->tracking;
2253
0
                                if (!aDXArray.empty())
2254
0
                                    fTextWidth = aDXArray.back();
2255
0
                            }
2256
2257
0
                            SAL_WARN_IF(stringFormat->DirectionRightToLeft(), "drawinglayer.emf",
2258
0
                                        "EMF+\t DrawString Alignment TODO For a right-to-left layout rectangle, the origin should be at the upper right.");
2259
0
                            if (stringFormat->stringAlignment == StringAlignmentNear)
2260
                                // Alignment is to the left side of the layout rectangle (lx, ly, lw, lh)
2261
0
                                stringAlignmentHorizontalOffset = stringFormat->leadingMargin * font->emSize;
2262
0
                            else if (stringFormat->stringAlignment == StringAlignmentCenter)
2263
                                // Alignment is centered between the origin and extent of the layout rectangle
2264
0
                                stringAlignmentHorizontalOffset = 0.5 * lw + (stringFormat->leadingMargin - stringFormat->trailingMargin) * font->emSize - 0.5 * fTextWidth;
2265
0
                            else if (stringFormat->stringAlignment == StringAlignmentFar)
2266
                                // Alignment is to the right side of the layout rectangle
2267
0
                                stringAlignmentHorizontalOffset = lw - stringFormat->trailingMargin * font->emSize - fTextWidth;
2268
2269
0
                            if (stringFormat->lineAlign == StringAlignmentNear)
2270
0
                                stringAlignmentVerticalOffset = font->emSize;
2271
0
                            else if (stringFormat->lineAlign == StringAlignmentCenter)
2272
0
                                stringAlignmentVerticalOffset = 0.5 * lh + 0.5 * font->emSize;
2273
0
                            else if (stringFormat->lineAlign == StringAlignmentFar)
2274
0
                                stringAlignmentVerticalOffset = lh;
2275
0
                        }
2276
0
                        else
2277
0
                        {
2278
                            // By default LeadingMargin is 1/6 inch
2279
                            // TODO for typographic fonts set value to 0.
2280
0
                            stringAlignmentHorizontalOffset = 16.0;
2281
2282
                            // use system default
2283
0
                            locale = Application::GetSettings().GetLanguageTag().getLocale();
2284
0
                        }
2285
2286
0
                        const basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
2287
0
                                    ::basegfx::B2DVector(font->emSize, font->emSize),
2288
0
                                    ::basegfx::B2DPoint(lx + stringAlignmentHorizontalOffset,
2289
0
                                                        ly + stringAlignmentVerticalOffset));
2290
2291
0
                        Color uncorrectedColor = EMFPGetBrushColorOrARGBColor(flags, brushId);
2292
0
                        Color color;
2293
2294
0
                        if (mbSetTextContrast)
2295
0
                        {
2296
0
                            sal_uInt16 nTextContrast = std::clamp(mnTextContrast, LOWERGAMMA, UPPERGAMMA);
2297
0
                            assert(nTextContrast >= LOWERGAMMA && nTextContrast <= UPPERGAMMA);
2298
0
                            const auto gammaVal = nTextContrast / 1000;
2299
0
                            assert(gammaVal != 0);
2300
0
                            SAL_INFO("drawinglayer.emf", "EMF+\t Text contrast: " << gammaVal << " gamma");
2301
0
                            const basegfx::BColorModifier_gamma gamma(gammaVal);
2302
2303
                            // gamma correct transparency color
2304
0
                            sal_uInt16 alpha = uncorrectedColor.GetAlpha();
2305
0
                            alpha = std::clamp(std::pow(alpha, 1.0 / gammaVal), 0.0, 1.0) * 255;
2306
2307
0
                            basegfx::BColor modifiedColor(gamma.getModifiedColor(uncorrectedColor.getBColor()));
2308
0
                            color.SetRed(modifiedColor.getRed() * 255);
2309
0
                            color.SetGreen(modifiedColor.getGreen() * 255);
2310
0
                            color.SetBlue(modifiedColor.getBlue() * 255);
2311
0
                            color.SetAlpha(alpha);
2312
0
                        }
2313
0
                        else
2314
0
                        {
2315
0
                            color = uncorrectedColor;
2316
0
                        }
2317
2318
0
                        mrPropertyHolders.Current().setTextColor(color.getBColor());
2319
0
                        mrPropertyHolders.Current().setTextColorActive(true);
2320
2321
0
                        if (color.GetAlpha() > 0)
2322
0
                        {
2323
0
                            rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText;
2324
0
                            if (font->Underline() || font->Strikeout())
2325
0
                            {
2326
0
                                pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
2327
0
                                            transformMatrix,
2328
0
                                            text,
2329
0
                                            0,             // text always starts at 0
2330
0
                                            stringLength,
2331
0
                                            std::move(aDXArray),
2332
0
                                            {},
2333
0
                                            std::move(fontAttribute),
2334
0
                                            locale,
2335
0
                                            color.getBColor(), // Font Color
2336
0
                                            COL_TRANSPARENT,   // Fill Color
2337
0
                                            0,
2338
0
                                            false,
2339
0
                                            {},
2340
0
                                            100, 0,
2341
0
                                            color.getBColor(), // OverlineColor
2342
0
                                            color.getBColor(), // TextlineColor
2343
0
                                            drawinglayer::primitive2d::TEXT_LINE_NONE,
2344
0
                                            font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
2345
0
                                            false,
2346
0
                                            font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE);
2347
0
                            }
2348
0
                            else
2349
0
                            {
2350
0
                                pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
2351
0
                                            transformMatrix,
2352
0
                                            text,
2353
0
                                            0,             // text always starts at 0
2354
0
                                            stringLength,
2355
0
                                            std::move(aDXArray),
2356
0
                                            {},
2357
0
                                            std::move(fontAttribute),
2358
0
                                            std::move(locale),
2359
0
                                            color.getBColor());
2360
0
                            }
2361
0
                            drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText);
2362
0
                            if (color.IsTransparent())
2363
0
                            {
2364
0
                                aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
2365
0
                                            drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText },
2366
0
                                            (255 - color.GetAlpha()) / 255.0);
2367
0
                            }
2368
2369
0
                            mrTargetHolders.Current().append(
2370
0
                                        new drawinglayer::primitive2d::TransformPrimitive2D(
2371
0
                                            maMapTransform,
2372
0
                                            drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } ));
2373
0
                        }
2374
0
                        break;
2375
0
                    }
2376
0
                    case EmfPlusRecordTypeSetPageTransform:
2377
0
                    {
2378
0
                        float pageScale;
2379
0
                        rMS.ReadFloat(pageScale);
2380
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Scale: " << pageScale << " unit: " << UnitTypeToString(flags));
2381
2382
0
                        mfPageScaleX = unitToPixel(pageScale, flags, Direction::horizontal);
2383
0
                        mfPageScaleY = unitToPixel(pageScale, flags, Direction::vertical);
2384
0
                        mappingChanged();
2385
2386
0
                        break;
2387
0
                    }
2388
0
                    case EmfPlusRecordTypeSetRenderingOrigin:
2389
0
                    {
2390
0
                        rMS.ReadInt32(mnOriginX).ReadInt32(mnOriginY);
2391
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t SetRenderingOrigin, [x,y]: " << mnOriginX << "," << mnOriginY);
2392
0
                        break;
2393
0
                    }
2394
0
                    case EmfPlusRecordTypeSetTextContrast:
2395
0
                    {
2396
0
                        mbSetTextContrast = true;
2397
0
                        mnTextContrast = flags & 0xFFF;
2398
0
                        SAL_WARN_IF(mnTextContrast > UPPERGAMMA || mnTextContrast < LOWERGAMMA,
2399
0
                            "drawinglayer.emf", "EMF+\t Gamma value is not with bounds 1000 to 2200, value is " << mnTextContrast);
2400
0
                        break;
2401
0
                    }
2402
0
                    case EmfPlusRecordTypeSetTextRenderingHint:
2403
0
                    {
2404
0
                        sal_uInt8 nTextRenderingHint = (flags & 0xFF) >> 1;
2405
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Text rendering hint: " << TextRenderingHintToString(nTextRenderingHint));
2406
0
                        SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetTextRenderingHint");
2407
0
                        break;
2408
0
                    }
2409
0
                    case EmfPlusRecordTypeSetAntiAliasMode:
2410
0
                    {
2411
0
                        bool bUseAntiAlias = (flags & 0x0001);
2412
0
                        sal_uInt8 nSmoothingMode = (flags & 0xFE00) >> 1;
2413
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Antialiasing: " << (bUseAntiAlias ? "enabled" : "disabled"));
2414
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Smoothing mode: " << SmoothingModeToString(nSmoothingMode));
2415
0
                        SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetAntiAliasMode");
2416
0
                        break;
2417
0
                    }
2418
0
                    case EmfPlusRecordTypeSetInterpolationMode:
2419
0
                    {
2420
0
                        sal_uInt16 nInterpolationMode = flags & 0xFF;
2421
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Interpolation mode: " << InterpolationModeToString(nInterpolationMode));
2422
0
                        SAL_WARN("drawinglayer.emf", "EMF+\t TODO InterpolationMode");
2423
0
                        break;
2424
0
                    }
2425
0
                    case EmfPlusRecordTypeSetPixelOffsetMode:
2426
0
                    {
2427
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Pixel offset mode: " << PixelOffsetModeToString(flags));
2428
0
                        SAL_WARN("drawinglayer.emf", "EMF+\t TODO SetPixelOffsetMode");
2429
0
                        break;
2430
0
                    }
2431
0
                    case EmfPlusRecordTypeSetCompositingQuality:
2432
0
                    {
2433
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t TODO SetCompositingQuality");
2434
0
                        break;
2435
0
                    }
2436
0
                    case EmfPlusRecordTypeSave:
2437
0
                    {
2438
0
                        sal_uInt32 stackIndex;
2439
0
                        rMS.ReadUInt32(stackIndex);
2440
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Save stack index: " << stackIndex);
2441
2442
0
                        GraphicStatePush(mGSStack, stackIndex);
2443
2444
0
                        break;
2445
0
                    }
2446
0
                    case EmfPlusRecordTypeRestore:
2447
0
                    {
2448
0
                        sal_uInt32 stackIndex;
2449
0
                        rMS.ReadUInt32(stackIndex);
2450
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Restore stack index: " << stackIndex);
2451
2452
0
                        GraphicStatePop(mGSStack, stackIndex);
2453
0
                        break;
2454
0
                    }
2455
0
                    case EmfPlusRecordTypeBeginContainer:
2456
0
                    {
2457
0
                        float dx, dy, dw, dh;
2458
0
                        ReadRectangle(rMS, dx, dy, dw, dh);
2459
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Dest RectData: " << dx << "," << dy << " " << dw << "x" << dh);
2460
2461
0
                        float sx, sy, sw, sh;
2462
0
                        ReadRectangle(rMS, sx, sy, sw, sh);
2463
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Source RectData: " << sx << "," << sy << " " << sw << "x" << sh);
2464
2465
0
                        sal_uInt32 stackIndex;
2466
0
                        rMS.ReadUInt32(stackIndex);
2467
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container stack index: " << stackIndex << ", PageUnit: " << flags);
2468
2469
0
                        GraphicStatePush(mGSContainerStack, stackIndex);
2470
0
                        const basegfx::B2DHomMatrix transform = basegfx::utils::createScaleTranslateB2DHomMatrix(
2471
0
                            unitToPixel(static_cast<double>(dw) / sw, flags, Direction::horizontal),
2472
0
                            unitToPixel(static_cast<double>(dh) / sh, flags, Direction::vertical),
2473
0
                            unitToPixel(static_cast<double>(dx) - sx, flags, Direction::horizontal),
2474
0
                            unitToPixel(static_cast<double>(dy) - sy, flags, Direction::vertical));
2475
0
                        maWorldTransform *= transform;
2476
0
                        mappingChanged();
2477
0
                        break;
2478
0
                    }
2479
0
                    case EmfPlusRecordTypeBeginContainerNoParams:
2480
0
                    {
2481
0
                        sal_uInt32 stackIndex;
2482
0
                        rMS.ReadUInt32(stackIndex);
2483
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Begin Container No Params stack index: " << stackIndex);
2484
2485
0
                        GraphicStatePush(mGSContainerStack, stackIndex);
2486
0
                        break;
2487
0
                    }
2488
0
                    case EmfPlusRecordTypeEndContainer:
2489
0
                    {
2490
0
                        sal_uInt32 stackIndex;
2491
0
                        rMS.ReadUInt32(stackIndex);
2492
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t End Container stack index: " << stackIndex);
2493
2494
0
                        GraphicStatePop(mGSContainerStack, stackIndex);
2495
0
                        break;
2496
0
                    }
2497
0
                    case EmfPlusRecordTypeSetWorldTransform:
2498
0
                    {
2499
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t SetWorldTransform, Post multiply: " << bool(flags & 0x2000));
2500
0
                        readXForm(rMS, maWorldTransform);
2501
0
                        mappingChanged();
2502
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t\t: " << maWorldTransform);
2503
0
                        break;
2504
0
                    }
2505
0
                    case EmfPlusRecordTypeResetWorldTransform:
2506
0
                    {
2507
0
                        maWorldTransform.identity();
2508
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t World transform: " << maWorldTransform);
2509
0
                        mappingChanged();
2510
0
                        break;
2511
0
                    }
2512
0
                    case EmfPlusRecordTypeMultiplyWorldTransform:
2513
0
                    {
2514
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t MultiplyWorldTransform, post multiply: " << bool(flags & 0x2000));
2515
0
                        basegfx::B2DHomMatrix transform;
2516
0
                        readXForm(rMS, transform);
2517
2518
0
                        SAL_INFO("drawinglayer.emf",
2519
0
                                 "EMF+\t Transform matrix: " << transform);
2520
2521
0
                        if (flags & 0x2000)
2522
0
                        {
2523
                            // post multiply
2524
0
                            maWorldTransform *= transform;
2525
0
                        }
2526
0
                        else
2527
0
                        {
2528
                            // pre multiply
2529
0
                            transform *= maWorldTransform;
2530
0
                            maWorldTransform = transform;
2531
0
                        }
2532
2533
0
                        mappingChanged();
2534
2535
0
                        SAL_INFO("drawinglayer.emf",
2536
0
                                 "EMF+\t World transform matrix: " << maWorldTransform);
2537
0
                        break;
2538
0
                    }
2539
0
                    case EmfPlusRecordTypeTranslateWorldTransform:
2540
0
                    {
2541
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t TranslateWorldTransform, Post multiply: " << bool(flags & 0x2000));
2542
2543
0
                        basegfx::B2DHomMatrix transform;
2544
0
                        float eDx, eDy;
2545
0
                        rMS.ReadFloat(eDx).ReadFloat(eDy);
2546
0
                        transform.set(0, 2, eDx);
2547
0
                        transform.set(1, 2, eDy);
2548
2549
0
                        SAL_INFO("drawinglayer.emf",
2550
0
                            "EMF+\t Translate matrix: " << transform);
2551
2552
0
                        if (flags & 0x2000)
2553
0
                        {
2554
                            // post multiply
2555
0
                            maWorldTransform *= transform;
2556
0
                        }
2557
0
                        else
2558
0
                        {
2559
                            // pre multiply
2560
0
                            transform *= maWorldTransform;
2561
0
                            maWorldTransform = transform;
2562
0
                        }
2563
2564
0
                        mappingChanged();
2565
2566
0
                        SAL_INFO("drawinglayer.emf",
2567
0
                                 "EMF+\t World transform matrix: " << maWorldTransform);
2568
0
                        break;
2569
0
                    }
2570
0
                    case EmfPlusRecordTypeScaleWorldTransform:
2571
0
                    {
2572
0
                        basegfx::B2DHomMatrix transform;
2573
0
                        float eSx, eSy;
2574
0
                        rMS.ReadFloat(eSx).ReadFloat(eSy);
2575
0
                        transform.set(0, 0, eSx);
2576
0
                        transform.set(1, 1, eSy);
2577
2578
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t ScaleWorldTransform Sx: " << eSx <<
2579
0
                                 " Sy: " << eSy << ", Post multiply:" << bool(flags & 0x2000));
2580
0
                        SAL_INFO("drawinglayer.emf",
2581
0
                                 "EMF+\t World transform matrix: " << maWorldTransform);
2582
2583
0
                        if (flags & 0x2000)
2584
0
                        {
2585
                            // post multiply
2586
0
                            maWorldTransform *= transform;
2587
0
                        }
2588
0
                        else
2589
0
                        {
2590
                            // pre multiply
2591
0
                            transform *= maWorldTransform;
2592
0
                            maWorldTransform = transform;
2593
0
                        }
2594
2595
0
                        mappingChanged();
2596
2597
0
                        SAL_INFO("drawinglayer.emf",
2598
0
                                 "EMF+\t World transform matrix: " << maWorldTransform);
2599
0
                        break;
2600
0
                    }
2601
0
                    case EmfPlusRecordTypeRotateWorldTransform:
2602
0
                    {
2603
                        // Angle of rotation in degrees
2604
0
                        float eAngle;
2605
0
                        rMS.ReadFloat(eAngle);
2606
2607
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t RotateWorldTransform Angle: " << eAngle <<
2608
0
                                 ", post multiply: " << bool(flags & 0x2000));
2609
                        // Skipping flags & 0x2000
2610
                        // For rotation transformation there is no difference between post and pre multiply
2611
0
                        maWorldTransform.rotate(basegfx::deg2rad(eAngle));
2612
0
                        mappingChanged();
2613
2614
0
                        SAL_INFO("drawinglayer.emf",
2615
0
                                "EMF+\t " << maWorldTransform);
2616
0
                        break;
2617
0
                    }
2618
0
                    case EmfPlusRecordTypeResetClip:
2619
0
                    {
2620
0
                        SAL_INFO("drawinglayer.emf", "EMF+ ResetClip");
2621
                        // We don't need to read anything more, as Size needs to be set 0x0000000C
2622
                        // and DataSize must be set to 0.
2623
2624
                        // Resets the current clipping region for the world space to infinity.
2625
0
                        HandleNewClipRegion(::basegfx::B2DPolyPolygon(), mrTargetHolders, mrPropertyHolders);
2626
0
                        break;
2627
0
                    }
2628
0
                    case EmfPlusRecordTypeSetClipRect:
2629
0
                    case EmfPlusRecordTypeSetClipPath:
2630
0
                    case EmfPlusRecordTypeSetClipRegion:
2631
0
                    {
2632
0
                        int combineMode = (flags >> 8) & 0xf;
2633
0
                        ::basegfx::B2DPolyPolygon polyPolygon;
2634
0
                        if (type == EmfPlusRecordTypeSetClipRect)
2635
0
                        {
2636
0
                            SAL_INFO("drawinglayer.emf", "EMF+\t SetClipRect");
2637
2638
0
                            float dx, dy, dw, dh;
2639
0
                            ReadRectangle(rMS, dx, dy, dw, dh);
2640
0
                            SAL_INFO("drawinglayer.emf",
2641
0
                                    "EMF+\t RectData: " << dx << "," << dy << " " << dw << "x" << dh);
2642
0
                            ::basegfx::B2DPoint mappedPoint1(Map(dx, dy));
2643
0
                            ::basegfx::B2DPoint mappedPoint2(Map(dx + dw, dy + dh));
2644
2645
0
                            polyPolygon
2646
0
                                = ::basegfx::B2DPolyPolygon(::basegfx::utils::createPolygonFromRect(
2647
0
                                    ::basegfx::B2DRectangle(mappedPoint1.getX(), mappedPoint1.getY(),
2648
0
                                                            mappedPoint2.getX(), mappedPoint2.getY())));
2649
0
                        }
2650
0
                        else if (type == EmfPlusRecordTypeSetClipPath)
2651
0
                        {
2652
0
                            SAL_INFO("drawinglayer.emf", "EMF+\tSetClipPath " << (flags & 0xff));
2653
2654
0
                            EMFPPath* path = dynamic_cast<EMFPPath*>(maEMFPObjects[flags & 0xff].get());
2655
0
                            if (!path)
2656
0
                            {
2657
0
                                SAL_WARN("drawinglayer.emf",
2658
0
                                        "EMF+\t TODO Unable to find path in slot: " << (flags & 0xff));
2659
0
                                break;
2660
0
                            }
2661
0
                            polyPolygon = path->GetPolygon(*this);
2662
0
                        }
2663
0
                        else if (type == EmfPlusRecordTypeSetClipRegion)
2664
0
                        {
2665
0
                            SAL_INFO("drawinglayer.emf", "EMF+\t Region in slot: " << (flags & 0xff));
2666
0
                            EMFPRegion* region
2667
0
                                = dynamic_cast<EMFPRegion*>(maEMFPObjects[flags & 0xff].get());
2668
0
                            if (!region)
2669
0
                            {
2670
0
                                SAL_WARN(
2671
0
                                    "drawinglayer.emf",
2672
0
                                    "EMF+\t TODO Unable to find region in slot: " << (flags & 0xff));
2673
0
                                break;
2674
0
                            }
2675
0
                            polyPolygon = region->regionPolyPolygon;
2676
0
                        }
2677
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Combine mode: " << combineMode);
2678
0
                        ::basegfx::B2DPolyPolygon aClippedPolyPolygon;
2679
0
                        if (mrPropertyHolders.Current().getClipPolyPolygonActive())
2680
0
                        {
2681
0
                            aClippedPolyPolygon
2682
0
                                = combineClip(mrPropertyHolders.Current().getClipPolyPolygon(),
2683
0
                                            combineMode, polyPolygon);
2684
0
                        }
2685
0
                        else
2686
0
                        {
2687
                            //Combine with infinity
2688
0
                            switch (combineMode)
2689
0
                            {
2690
0
                                case EmfPlusCombineModeReplace:
2691
0
                                case EmfPlusCombineModeIntersect:
2692
0
                                {
2693
0
                                    aClippedPolyPolygon = polyPolygon;
2694
0
                                    break;
2695
0
                                }
2696
0
                                case EmfPlusCombineModeUnion:
2697
0
                                {
2698
                                    // Disable clipping as the clipping is infinity
2699
0
                                    aClippedPolyPolygon = ::basegfx::B2DPolyPolygon();
2700
0
                                    break;
2701
0
                                }
2702
0
                                case EmfPlusCombineModeXOR:
2703
0
                                case EmfPlusCombineModeComplement:
2704
0
                                {
2705
                                    //TODO It is not correct and it should be fixed
2706
0
                                    aClippedPolyPolygon = std::move(polyPolygon);
2707
0
                                    break;
2708
0
                                }
2709
0
                                case EmfPlusCombineModeExclude:
2710
0
                                {
2711
                                    //TODO It is not correct and it should be fixed
2712
0
                                    aClippedPolyPolygon = ::basegfx::B2DPolyPolygon();
2713
0
                                    break;
2714
0
                                }
2715
0
                            }
2716
0
                        }
2717
0
                        HandleNewClipRegion(aClippedPolyPolygon, mrTargetHolders, mrPropertyHolders);
2718
0
                        break;
2719
0
                    }
2720
0
                    case EmfPlusRecordTypeOffsetClip:
2721
0
                    {
2722
0
                        float dx, dy;
2723
0
                        rMS.ReadFloat(dx).ReadFloat(dy);
2724
0
                        SAL_INFO("drawinglayer.emf", "EMF+\tOffset x:" << dx << ", y:" << dy);
2725
2726
0
                        basegfx::B2DPolyPolygon aPolyPolygon(
2727
0
                                    mrPropertyHolders.Current().getClipPolyPolygon());
2728
2729
0
                        SAL_INFO("drawinglayer.emf",
2730
0
                                 "EMF+\t PolyPolygon before translate: " << aPolyPolygon);
2731
2732
0
                        basegfx::B2DPoint aOffset = Map(dx, dy);
2733
0
                        basegfx::B2DHomMatrix transformMatrix;
2734
0
                        transformMatrix.set(0, 2, aOffset.getX());
2735
0
                        transformMatrix.set(1, 2, aOffset.getY());
2736
0
                        aPolyPolygon.transform(transformMatrix);
2737
2738
0
                        SAL_INFO("drawinglayer.emf",
2739
0
                                 "EMF+\t PolyPolygon after translate: " << aPolyPolygon <<
2740
0
                                 ", mapped offset x" << aOffset.getX() << ", mapped offset y" << aOffset.getY());
2741
0
                        HandleNewClipRegion(aPolyPolygon, mrTargetHolders, mrPropertyHolders);
2742
0
                        break;
2743
0
                    }
2744
0
                    case EmfPlusRecordTypeDrawDriverString:
2745
0
                    {
2746
0
                        sal_uInt32 brushIndexOrColor(0);
2747
0
                        sal_uInt32 optionFlags(0);
2748
0
                        sal_uInt32 hasMatrix(0);
2749
0
                        sal_uInt32 glyphsCount(0);
2750
0
                        rMS.ReadUInt32(brushIndexOrColor).ReadUInt32(optionFlags).ReadUInt32(hasMatrix).ReadUInt32(glyphsCount);
2751
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t " << ((flags & 0x8000) ? "Color" : "Brush index") << ": 0x" << std::hex << brushIndexOrColor << std::dec);
2752
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Option flags: 0x" << std::hex << optionFlags << std::dec);
2753
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Has matrix: " << hasMatrix);
2754
0
                        SAL_INFO("drawinglayer.emf", "EMF+\t Glyphs: " << glyphsCount);
2755
2756
                        // each glyph needs 2 bytes (UTF-16) + 2*4 bytes (X,Y floats) = 10 bytes
2757
0
                        if ((optionFlags & 1) && glyphsCount > 0
2758
0
                            && glyphsCount <= rMS.remainingSize() / 10)
2759
0
                        {
2760
0
                            std::unique_ptr<float[]> charsPosX(new float[glyphsCount]);
2761
0
                            std::unique_ptr<float[]> charsPosY(new float[glyphsCount]);
2762
0
                            OUString text = read_uInt16s_ToOUString(rMS, glyphsCount);
2763
0
                            SAL_INFO("drawinglayer.emf", "EMF+\t DrawDriverString string: " << text);
2764
2765
0
                            for (sal_uInt32 i = 0; i<glyphsCount; i++)
2766
0
                            {
2767
0
                                rMS.ReadFloat(charsPosX[i]).ReadFloat(charsPosY[i]);
2768
0
                                SAL_INFO("drawinglayer.emf", "EMF+\t\t glyphPosition[" << i << "]: " << charsPosX[i] << "," << charsPosY[i]);
2769
0
                            }
2770
2771
0
                            basegfx::B2DHomMatrix transform;
2772
2773
0
                            if (hasMatrix)
2774
0
                            {
2775
0
                                readXForm(rMS, transform);
2776
0
                                SAL_INFO("drawinglayer.emf", "EMF+\tmatrix: " << transform);
2777
0
                            }
2778
2779
                            // get the font from the flags
2780
0
                            EMFPFont *font = dynamic_cast<EMFPFont*>(maEMFPObjects[flags & 0xff].get());
2781
0
                            if (!font)
2782
0
                            {
2783
0
                                break;
2784
0
                            }
2785
                            // done reading
2786
2787
0
                            drawinglayer::attribute::FontAttribute fontAttribute(
2788
0
                                font->family,                                    // font family
2789
0
                                u""_ustr,                                              // (no) font style
2790
0
                                font->Bold() ? 8u : 1u,                          // weight: 8 = bold
2791
0
                                font->family == "SYMBOL",                        // symbol
2792
0
                                optionFlags & 0x2,                               // vertical
2793
0
                                font->Italic(),                                  // italic
2794
0
                                false,                                           // monospaced
2795
0
                                false,                                           // outline = false, no such thing in MS-EMFPLUS
2796
0
                                false,                                           // right-to-left
2797
0
                                false);                                          // BiDiStrong
2798
2799
0
                            const Color color = EMFPGetBrushColorOrARGBColor(flags, brushIndexOrColor);
2800
2801
                            // generate TextSimplePortionPrimitive2Ds or TextDecoratedPortionPrimitive2D
2802
                            // for all portions of text with the same charsPosY values
2803
0
                            sal_uInt32 pos = 0;
2804
0
                            while (pos < glyphsCount)
2805
0
                            {
2806
                                //determine the current length
2807
0
                                sal_uInt32 aLength = 1;
2808
0
                                while (pos + aLength < glyphsCount && std::abs( charsPosY[pos + aLength] - charsPosY[pos] ) < std::numeric_limits< float >::epsilon())
2809
0
                                    aLength++;
2810
2811
                                // generate the DX-Array
2812
0
                                std::vector<double> aDXArray;
2813
0
                                for (size_t i = 0; i < aLength - 1; i++)
2814
0
                                {
2815
0
                                    aDXArray.push_back(charsPosX[pos + i + 1] - charsPosX[pos]);
2816
0
                                }
2817
                                // last entry
2818
0
                                aDXArray.push_back(0);
2819
2820
0
                                basegfx::B2DHomMatrix transformMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(
2821
0
                                            ::basegfx::B2DVector(font->emSize, font->emSize),
2822
0
                                            ::basegfx::B2DPoint(charsPosX[pos], charsPosY[pos]));
2823
0
                                if (hasMatrix)
2824
0
                                    transformMatrix *= transform;
2825
0
                                if (color.GetAlpha() > 0)
2826
0
                                {
2827
0
                                    rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pBaseText;
2828
0
                                    if (font->Underline() || font->Strikeout())
2829
0
                                    {
2830
0
                                        pBaseText = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
2831
0
                                                    transformMatrix,
2832
0
                                                    text,
2833
0
                                                    pos,            // take character at current pos
2834
0
                                                    aLength,        // use determined length
2835
0
                                                    std::move(aDXArray),       // generated DXArray
2836
0
                                                    {},
2837
0
                                                    fontAttribute,
2838
0
                                                    Application::GetSettings().GetLanguageTag().getLocale(),
2839
0
                                                    color.getBColor(),
2840
0
                                                    COL_TRANSPARENT,
2841
0
                                                    0,
2842
0
                                                    false,
2843
0
                                                    {},
2844
0
                                                    100, 0,
2845
0
                                                    color.getBColor(),
2846
0
                                                    color.getBColor(),
2847
0
                                                    drawinglayer::primitive2d::TEXT_LINE_NONE,
2848
0
                                                    font->Underline() ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE,
2849
0
                                                    false,
2850
0
                                                    font->Strikeout() ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE);
2851
0
                                    }
2852
0
                                    else
2853
0
                                    {
2854
0
                                        pBaseText = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
2855
0
                                                    transformMatrix,
2856
0
                                                    text,
2857
0
                                                    pos,            // take character at current pos
2858
0
                                                    aLength,        // use determined length
2859
0
                                                    std::move(aDXArray),       // generated DXArray
2860
0
                                                    {},
2861
0
                                                    fontAttribute,
2862
0
                                                    Application::GetSettings().GetLanguageTag().getLocale(),
2863
0
                                                    color.getBColor());
2864
0
                                    }
2865
0
                                    drawinglayer::primitive2d::Primitive2DReference aPrimitiveText(pBaseText);
2866
0
                                    if (color.IsTransparent())
2867
0
                                    {
2868
0
                                        aPrimitiveText = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
2869
0
                                                    drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText },
2870
0
                                                    (255 - color.GetAlpha()) / 255.0);
2871
0
                                    }
2872
0
                                    mrTargetHolders.Current().append(
2873
0
                                                new drawinglayer::primitive2d::TransformPrimitive2D(
2874
0
                                                    maMapTransform,
2875
0
                                                    drawinglayer::primitive2d::Primitive2DContainer { aPrimitiveText } ));
2876
0
                                }
2877
2878
                                // update pos
2879
0
                                pos += aLength;
2880
0
                            }
2881
0
                        }
2882
0
                        else
2883
0
                        {
2884
0
                            SAL_WARN("drawinglayer.emf", "EMF+\tTODO: fonts (non-unicode glyphs chars)");
2885
0
                        }
2886
0
                        break;
2887
0
                    }
2888
0
                    default:
2889
0
                    {
2890
0
                        SAL_WARN("drawinglayer.emf", "EMF+ TODO unhandled record type: 0x" << std::hex << type << std::dec);
2891
0
                    }
2892
0
                }
2893
0
            }
2894
2895
0
            rMS.Seek(next);
2896
2897
0
            if (size <= length)
2898
0
            {
2899
0
                length -= size;
2900
0
            }
2901
0
            else
2902
0
            {
2903
0
                SAL_WARN("drawinglayer.emf", "ImplRenderer::processEMFPlus: "
2904
0
                    "size " << size << " > length " << length);
2905
0
                length = 0;
2906
0
            }
2907
0
        }
2908
0
    }
2909
}
2910
2911
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */