Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/emfio/source/reader/mtftools.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 <mtftools.hxx>
21
22
#include <cstdlib>
23
#include <memory>
24
#include <basegfx/matrix/b2dhommatrix.hxx>
25
#include <basegfx/polygon/b2dlinegeometry.hxx>
26
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
27
#include <basegfx/polygon/b2dpolypolygontools.hxx>
28
#include <tools/mapunit.hxx>
29
#include <vcl/metric.hxx>
30
#include <vcl/graphictools.hxx>
31
#include <vcl/BitmapTools.hxx>
32
#include <vcl/metaact.hxx>
33
#include <vcl/wall.hxx>
34
#include <vcl/region.hxx>
35
#include <vcl/canvastools.hxx>
36
#include <vcl/svapp.hxx>
37
#include <tools/stream.hxx>
38
#include <rtl/tencinfo.h>
39
#include <sal/log.hxx>
40
#include <osl/diagnose.h>
41
#include <vcl/virdev.hxx>
42
#include <o3tl/safeint.hxx>
43
#include <comphelper/configuration.hxx>
44
#include <unotools/defaultencoding.hxx>
45
#include <unotools/wincodepage.hxx>
46
47
#if OSL_DEBUG_LEVEL > 1
48
#define EMFP_DEBUG(x) x
49
#else
50
#define EMFP_DEBUG(x)
51
#endif
52
53
namespace emfio
54
{
55
    SvStream& operator >> (SvStream& rInStream, XForm& rXForm)
56
50.1k
    {
57
50.1k
        if (sizeof(float) != 4)
58
0
        {
59
0
            OSL_FAIL("EmfReader::sizeof( float ) != 4");
60
0
            rXForm = XForm();
61
0
        }
62
50.1k
        else
63
50.1k
        {
64
50.1k
            rInStream.ReadFloat(rXForm.eM11);
65
50.1k
            rInStream.ReadFloat(rXForm.eM12);
66
50.1k
            rInStream.ReadFloat(rXForm.eM21);
67
50.1k
            rInStream.ReadFloat(rXForm.eM22);
68
50.1k
            rInStream.ReadFloat(rXForm.eDx);
69
50.1k
            rInStream.ReadFloat(rXForm.eDy);
70
50.1k
            if (std::isnan(rXForm.eM11) ||
71
46.9k
                std::isnan(rXForm.eM12) ||
72
43.9k
                std::isnan(rXForm.eM21) ||
73
41.5k
                std::isnan(rXForm.eM22) ||
74
39.7k
                std::isnan(rXForm.eDx) ||
75
38.6k
                std::isnan(rXForm.eDy))
76
12.4k
            {
77
12.4k
                SAL_WARN("emfio", "XForm member isnan, ignoring");
78
12.4k
                rXForm = XForm();
79
12.4k
            }
80
50.1k
        }
81
50.1k
        return rInStream;
82
50.1k
    }
83
84
    Hatch mapWindowsHatch(sal_uInt32 nHatchIndex, const Color& rColor)
85
1.33k
    {
86
1.33k
        HatchStyle eStyle;
87
1.33k
        Degree10 nAngle;
88
1.33k
        switch (nHatchIndex)
89
1.33k
        {
90
938
            case 0: // HS_HORIZONTAL
91
938
                eStyle = HatchStyle::Single; nAngle = 0_deg10; break;
92
185
            case 1: // HS_VERTICAL
93
185
                eStyle = HatchStyle::Single; nAngle = Degree10(900); break;
94
105
            case 2: // HS_FDIAGONAL
95
105
                eStyle = HatchStyle::Single; nAngle = Degree10(1350); break;
96
10
            case 3: // HS_BDIAGONAL
97
10
                eStyle = HatchStyle::Single; nAngle = Degree10(450); break;
98
6
            case 4: // HS_CROSS
99
6
                eStyle = HatchStyle::Double; nAngle = 0_deg10; break;
100
95
            case 5: // HS_DIAGCROSS
101
95
                eStyle = HatchStyle::Double; nAngle = Degree10(450); break;
102
0
            default:
103
0
                SAL_WARN("emfio", "Unknown hatch index: " << nHatchIndex);
104
0
                eStyle = HatchStyle::Single; nAngle = 0_deg10; break;
105
1.33k
        }
106
1.33k
        return Hatch(eStyle, rColor, 50, nAngle);
107
1.33k
    }
108
109
    void WinMtfClipPath::intersectClip( const basegfx::B2DPolyPolygon& rPolyPolygon )
110
0
    {
111
0
        maClip.intersectPolyPolygon(rPolyPolygon);
112
0
    }
113
114
    void WinMtfClipPath::excludeClip( const basegfx::B2DPolyPolygon& rPolyPolygon )
115
0
    {
116
0
        maClip.subtractPolyPolygon(rPolyPolygon);
117
0
    }
118
119
    void WinMtfClipPath::setClipPath( const basegfx::B2DPolyPolygon& rB2DPoly, RegionMode nClippingMode )
120
0
    {
121
0
        switch ( nClippingMode )
122
0
        {
123
0
            case RegionMode::RGN_OR :
124
0
                maClip.unionPolyPolygon(rB2DPoly);
125
0
                break;
126
0
            case RegionMode::RGN_XOR :
127
0
                maClip.xorPolyPolygon(rB2DPoly);
128
0
                break;
129
0
            case RegionMode::RGN_DIFF :
130
0
                maClip.subtractPolyPolygon(rB2DPoly);
131
0
                break;
132
0
            case RegionMode::RGN_AND :
133
0
                maClip.intersectPolyPolygon(rB2DPoly);
134
0
                break;
135
0
            case RegionMode::RGN_COPY :
136
0
                maClip = basegfx::utils::B2DClipState(rB2DPoly);
137
0
                break;
138
0
        }
139
0
    }
140
141
    void WinMtfClipPath::moveClipRegion( const Size& rSize )
142
0
    {
143
0
        basegfx::B2DHomMatrix aTranslate;
144
0
        aTranslate.translate(rSize.Width(), rSize.Height());
145
0
        maClip.transform(aTranslate);
146
0
    }
147
148
    void WinMtfClipPath::setDefaultClipPath()
149
339
    {
150
        // Empty clip region - everything visible
151
339
        maClip = basegfx::utils::B2DClipState();
152
339
    }
153
154
    basegfx::B2DPolyPolygon const & WinMtfClipPath::getClipPath() const
155
0
    {
156
0
        return maClip.getClipPoly();
157
0
    }
158
159
    void WinMtfPathObj::AddPoint( const Point& rPoint )
160
354k
    {
161
354k
        if ( bClosed )
162
345k
            Insert( tools::Polygon() );
163
354k
        tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ];
164
354k
        rPoly.Insert( rPoly.GetSize(), rPoint );
165
354k
        bClosed = false;
166
354k
    }
167
168
    void WinMtfPathObj::AddPolyLine( const tools::Polygon& rPolyLine )
169
95.9k
    {
170
95.9k
        if ( bClosed )
171
76.5k
            Insert( tools::Polygon() );
172
95.9k
        tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ];
173
95.9k
        rPoly.Insert( rPoly.GetSize(), rPolyLine );
174
95.9k
        bClosed = false;
175
95.9k
    }
176
177
    void WinMtfPathObj::AddPolygon( const tools::Polygon& rPoly )
178
8.43k
    {
179
8.43k
        Insert( rPoly );
180
8.43k
        bClosed = true;
181
8.43k
    }
182
183
    void WinMtfPathObj::AddPolyPolygon( const tools::PolyPolygon& rPolyPoly )
184
218
    {
185
218
        sal_uInt16 i, nCount = rPolyPoly.Count();
186
1.26k
        for ( i = 0; i < nCount; i++ )
187
1.05k
            Insert( rPolyPoly[ i ] );
188
218
        bClosed = true;
189
218
    }
190
191
    void WinMtfPathObj::ClosePath()
192
416k
    {
193
416k
        if ( Count() )
194
415k
        {
195
415k
            tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ];
196
415k
            if ( rPoly.GetSize() > 2 )
197
77.9k
            {
198
77.9k
                Point aFirst( rPoly[ 0 ] );
199
77.9k
                if ( aFirst != rPoly[ rPoly.GetSize() - 1 ] )
200
39.0k
                    rPoly.Insert( rPoly.GetSize(), aFirst );
201
77.9k
            }
202
415k
        }
203
416k
        bClosed = true;
204
416k
    }
205
206
    WinMtfFontStyle::WinMtfFontStyle( LOGFONTW const & rFont )
207
30.3k
    {
208
30.3k
        rtl_TextEncoding eCharSet;
209
30.3k
        if ((rFont.alfFaceName == "Symbol")
210
27.8k
         || (rFont.alfFaceName == "MT Extra"))
211
2.51k
            eCharSet = RTL_TEXTENCODING_SYMBOL;
212
27.8k
        else if ((rFont.lfCharSet == DEFAULT_CHARSET) || (rFont.lfCharSet == OEM_CHARSET))
213
1.74k
            eCharSet = utl_getWinTextEncodingFromLangStr(utl_getLocaleForGlobalDefaultEncoding(),
214
1.74k
                                                         rFont.lfCharSet == OEM_CHARSET);
215
26.0k
        else
216
26.0k
            eCharSet = rtl_getTextEncodingFromWindowsCharset( rFont.lfCharSet );
217
30.3k
        if ( eCharSet == RTL_TEXTENCODING_DONTKNOW )
218
6.30k
            eCharSet = RTL_TEXTENCODING_MS_1252;
219
30.3k
        aFont.SetCharSet( eCharSet );
220
30.3k
        aFont.SetFamilyName( rFont.alfFaceName );
221
30.3k
        FontFamily eFamily;
222
30.3k
        switch ( rFont.lfPitchAndFamily >> 4 & 0x0f )
223
30.3k
        {
224
5.71k
            case FamilyFont::FF_ROMAN:
225
5.71k
                eFamily = FAMILY_ROMAN;
226
5.71k
            break;
227
228
5.81k
            case FamilyFont::FF_SWISS:
229
5.81k
                eFamily = FAMILY_SWISS;
230
5.81k
            break;
231
232
178
            case FamilyFont::FF_MODERN:
233
178
                eFamily = FAMILY_MODERN;
234
178
            break;
235
236
116
            case FamilyFont::FF_SCRIPT:
237
116
                eFamily = FAMILY_SCRIPT;
238
116
            break;
239
240
47
            case FamilyFont::FF_DECORATIVE:
241
47
                 eFamily = FAMILY_DECORATIVE;
242
47
            break;
243
244
18.4k
            default:
245
18.4k
                eFamily = FAMILY_DONTKNOW;
246
18.4k
            break;
247
30.3k
        }
248
30.3k
        aFont.SetFamily( eFamily );
249
250
30.3k
        FontPitch ePitch;
251
30.3k
        switch ( rFont.lfPitchAndFamily & 0x0f )
252
30.3k
        {
253
1.35k
            case FIXED_PITCH:
254
1.35k
                ePitch = PITCH_FIXED;
255
1.35k
            break;
256
257
13.3k
            case DEFAULT_PITCH:
258
18.6k
            case VARIABLE_PITCH:
259
28.9k
            default:
260
28.9k
                ePitch = PITCH_VARIABLE;
261
28.9k
            break;
262
30.3k
        }
263
30.3k
        aFont.SetPitch( ePitch );
264
265
30.3k
        FontWeight eWeight;
266
30.3k
        if (rFont.lfWeight == 0) // default weight SHOULD be used
267
3.82k
            eWeight = WEIGHT_DONTKNOW;
268
26.5k
        else if (rFont.lfWeight <= FW_THIN)
269
10.8k
            eWeight = WEIGHT_THIN;
270
15.6k
        else if( rFont.lfWeight <= FW_ULTRALIGHT )
271
414
            eWeight = WEIGHT_ULTRALIGHT;
272
15.2k
        else if( rFont.lfWeight <= FW_LIGHT )
273
578
            eWeight = WEIGHT_LIGHT;
274
14.6k
        else if( rFont.lfWeight <  FW_MEDIUM )
275
7.75k
            eWeight = WEIGHT_NORMAL;
276
6.91k
        else if( rFont.lfWeight == FW_MEDIUM )
277
19
            eWeight = WEIGHT_MEDIUM;
278
6.89k
        else if( rFont.lfWeight <= FW_SEMIBOLD )
279
306
            eWeight = WEIGHT_SEMIBOLD;
280
6.59k
        else if( rFont.lfWeight <= FW_BOLD )
281
2.11k
            eWeight = WEIGHT_BOLD;
282
4.47k
        else if( rFont.lfWeight <= FW_ULTRABOLD )
283
217
            eWeight = WEIGHT_ULTRABOLD;
284
4.25k
        else
285
4.25k
            eWeight = WEIGHT_BLACK;
286
30.3k
        aFont.SetWeight( eWeight );
287
288
30.3k
        if( rFont.lfItalic )
289
17.5k
            aFont.SetItalic( ITALIC_NORMAL );
290
291
30.3k
        if( rFont.lfUnderline )
292
11.1k
            aFont.SetUnderline( LINESTYLE_SINGLE );
293
294
30.3k
        if( rFont.lfStrikeOut )
295
15.4k
            aFont.SetStrikeout( STRIKEOUT_SINGLE );
296
297
30.3k
        aFont.SetOrientation( Degree10(static_cast<sal_Int16>(rFont.lfEscapement)) );
298
299
30.3k
        Size aFontSize( rFont.lfWidth, rFont.lfHeight );
300
30.3k
        if ( rFont.lfHeight > 0 )
301
4.01k
        {
302
            // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
303
4.01k
            SolarMutexGuard aGuard;
304
4.01k
            ScopedVclPtrInstance< VirtualDevice > pVDev;
305
            // converting the cell height into a font height
306
4.01k
            aFont.SetFontSize( aFontSize );
307
4.01k
            pVDev->SetFont( aFont );
308
4.01k
            FontMetric aMetric( pVDev->GetFontMetric() );
309
4.01k
            tools::Long nHeight = static_cast<tools::Long>(aMetric.GetAscent()) + aMetric.GetDescent();
310
4.01k
            if (nHeight)
311
4.01k
            {
312
4.01k
                double fHeight = (static_cast<double>(aFontSize.Height()) * rFont.lfHeight ) / nHeight;
313
4.01k
                aFontSize.setHeight( static_cast<sal_Int32>( fHeight + 0.5 ) );
314
4.01k
            }
315
4.01k
        }
316
317
        // Convert height to positive
318
30.3k
        aFontSize.setHeight( std::abs(aFontSize.Height()) );
319
30.3k
        aFont.SetFontSize(aFontSize);
320
321
        // tdf#127471 adapt nFontWidth from Windows-like notation to
322
        // NormedFontScaling if used for text scaling
323
30.3k
        const bool bFontScaledHorizontally(aFontSize.Width() != 0 && aFontSize.Width() != aFontSize.Height());
324
325
30.3k
        if(bFontScaledHorizontally)
326
6.54k
        {
327
            // tdf#127471 nFontWidth is the Windows FontScaling, need to convert
328
            // to notation relative to FontHeight.
329
6.54k
            const tools::Long nAverageFontWidth(aFont.GetOrCalculateAverageFontWidth());
330
331
6.54k
            if(nAverageFontWidth > 0)
332
6.54k
            {
333
6.54k
                const double fScaleFactor(static_cast<double>(aFontSize.Height()) / static_cast<double>(nAverageFontWidth));
334
6.54k
                aFont.SetFontWidth(static_cast<tools::Long>(static_cast<double>(aFontSize.Width()) * fScaleFactor));
335
6.54k
            }
336
6.54k
        }
337
30.3k
    };
338
339
30.3k
    WinMtfFontStyle::~WinMtfFontStyle() = default;
340
341
    // tdf#127471
342
    ScaledFontDetectCorrectHelper::ScaledFontDetectCorrectHelper()
343
22.6k
    {
344
22.6k
    }
345
346
    void ScaledFontDetectCorrectHelper::endCurrentMetaFontAction()
347
278k
    {
348
278k
        if(maCurrentMetaFontAction.is() && !maAlternativeFontScales.empty())
349
3.43k
        {
350
            // create average corrected FontScale value and count
351
            // positive/negative hits
352
3.43k
            sal_uInt32 nPositive(0);
353
3.43k
            sal_uInt32 nNegative(0);
354
3.43k
            double fAverage(0.0);
355
356
3.43k
            for(double fPart : maAlternativeFontScales)
357
3.51k
            {
358
3.51k
                if(fPart < 0.0)
359
2.70k
                {
360
2.70k
                    nNegative++;
361
2.70k
                    fAverage += -fPart;
362
2.70k
                }
363
814
                else
364
814
                {
365
814
                    nPositive++;
366
814
                    fAverage += fPart;
367
814
                }
368
3.51k
            }
369
370
3.43k
            fAverage /= static_cast<double>(maAlternativeFontScales.size());
371
372
3.43k
            if(nPositive >= nNegative)
373
737
            {
374
                // correction intended, it is probably an old imported file
375
737
                maPositiveIdentifiedCases.emplace_back(maCurrentMetaFontAction, fAverage);
376
737
            }
377
2.70k
            else
378
2.70k
            {
379
                // correction not favorable in the majority of cases for this Font, still
380
                // remember to have a weight in the last decision for correction
381
2.70k
                maNegativeIdentifiedCases.emplace_back(maCurrentMetaFontAction, fAverage);
382
2.70k
            }
383
3.43k
        }
384
385
278k
        maCurrentMetaFontAction.clear();
386
278k
        maAlternativeFontScales.clear();
387
278k
    }
388
389
    void ScaledFontDetectCorrectHelper::newCurrentMetaFontAction(const rtl::Reference<MetaFontAction>& rNewMetaFontAction)
390
9.10k
    {
391
9.10k
        maCurrentMetaFontAction.clear();
392
9.10k
        maAlternativeFontScales.clear();
393
394
9.10k
        if(!rNewMetaFontAction.is())
395
0
            return;
396
397
        // check 1st criteria for FontScale active. We usually write this,
398
        // so this will already sort out most situations
399
9.10k
        const vcl::Font& rCandidate(rNewMetaFontAction->GetFont());
400
401
9.10k
        if(0 != rCandidate.GetFontWidth())
402
5.31k
        {
403
5.31k
            const tools::Long nUnscaledAverageFontWidth(rCandidate.GetOrCalculateAverageFontWidth());
404
405
            // check 2nd (system-dependent) criteria for FontScale
406
5.31k
            if(nUnscaledAverageFontWidth != rCandidate.GetFontHeight())
407
5.24k
            {
408
                // FontScale is active, remember and use as current
409
5.24k
                maCurrentMetaFontAction = rNewMetaFontAction;
410
5.24k
            }
411
5.31k
        }
412
9.10k
    }
413
414
    void ScaledFontDetectCorrectHelper::evaluateAlternativeFontScale(OUString const & rText, tools::Long nImportedTextLength)
415
8.36k
    {
416
8.36k
        if(!maCurrentMetaFontAction.is())
417
4.42k
            return;
418
419
3.94k
        SolarMutexGuard aGuard; // VirtualDevice is not thread-safe
420
3.94k
        ScopedVclPtrInstance< VirtualDevice > pTempVirtualDevice;
421
422
        // calculate measured TextLength
423
3.94k
        const vcl::Font& rFontCandidate(maCurrentMetaFontAction->GetFont());
424
3.94k
        pTempVirtualDevice->SetFont(rFontCandidate);
425
3.94k
        tools::Long nMeasuredTextLength(pTempVirtualDevice->GetTextWidth(rText));
426
        // on failure, use original length
427
3.94k
        if (!nMeasuredTextLength)
428
423
            nMeasuredTextLength = nImportedTextLength;
429
430
        // compare expected and imported TextLengths
431
3.94k
        if (nImportedTextLength == nMeasuredTextLength)
432
424
            return;
433
434
3.51k
        const double fFactorText(static_cast<double>(nImportedTextLength) / static_cast<double>(nMeasuredTextLength));
435
3.51k
        const double fFactorTextPercent(fabs(1.0 - fFactorText) * 100.0);
436
437
        // if we assume that loaded file was written on old linux, we have to
438
        // back-convert the scale value: it was already adapted at font import (see
439
        // WinMtfFontStyle constructor). It was wrongly assumed to be Windows-style
440
        // FontScaling, so we need to revert that to get back to the needed FontScale
441
3.51k
        const tools::Long nUnscaledAverageFontWidth(rFontCandidate.GetOrCalculateAverageFontWidth());
442
3.51k
        const tools::Long nScaledAverageFontWidth(rFontCandidate.GetFontWidth());
443
3.51k
        const double fScaleFactor(static_cast<double>(nUnscaledAverageFontWidth) / static_cast<double>(rFontCandidate.GetFontHeight()));
444
3.51k
        const double fCorrectedAverageFontWidth(static_cast<double>(nScaledAverageFontWidth) * fScaleFactor);
445
3.51k
        tools::Long nCorrectedTextLength(0);
446
447
3.51k
        { // do in own scope, only need nUnscaledAverageFontWidth
448
3.51k
            vcl::Font rFontCandidate2(rFontCandidate);
449
3.51k
            rFontCandidate2.SetFontWidth(static_cast<tools::Long>(fCorrectedAverageFontWidth));
450
3.51k
            pTempVirtualDevice->SetFont(rFontCandidate2);
451
3.51k
            nCorrectedTextLength = pTempVirtualDevice->GetTextWidth(rText);
452
            // on failure, use original length
453
3.51k
            if (!nCorrectedTextLength)
454
25
                nCorrectedTextLength = nImportedTextLength;
455
3.51k
        }
456
457
3.51k
        const double fFactorCorrectedText(static_cast<double>(nImportedTextLength) / static_cast<double>(nCorrectedTextLength));
458
3.51k
        const double fFactorCorrectedTextPercent(fabs(1.0 - fFactorCorrectedText) * 100.0);
459
460
        // If FactorCorrectedText fits better than FactorText this is probably
461
        // an import of an old EMF/WMF written by LibreOffice on a non-Windows (unx) system
462
        // and should be corrected.
463
        // Usually in tested cases this lies inside 5% of range, so detecting this just using
464
        //  fFactorTextPercent inside 5% -> no old file
465
        //  fFactorCorrectedTextPercent inside 5% -> is old file
466
        // works not too bad, but there are some strange not so often used fonts where that
467
        // values do deviate, so better just compare if old corrected would fit better than
468
        // the uncorrected case, that is usually safe.
469
3.51k
        if(fFactorCorrectedTextPercent < fFactorTextPercent)
470
388
        {
471
388
            maAlternativeFontScales.push_back(fCorrectedAverageFontWidth);
472
388
        }
473
3.12k
        else
474
3.12k
        {
475
            // also push, but negative to remember non-fitting case
476
3.12k
            maAlternativeFontScales.push_back(-fCorrectedAverageFontWidth);
477
3.12k
        }
478
3.51k
    }
479
480
    void ScaledFontDetectCorrectHelper::applyAlternativeFontScale()
481
256k
    {
482
        // make sure last evtl. detected current FontAction gets added to identified cases
483
256k
        endCurrentMetaFontAction();
484
485
        // Take final decision to correct FontScaling for this imported Metafile or not.
486
        // It is possible to weight positive against negative cases, so to only finally
487
        // correct when more positive cases were detected.
488
        // But that would be inconsequent and wrong. *If* the detected case is an old import
489
        // the whole file was written with wrong FontScale values and all Font actions
490
        // need to be corrected. Thus, for now, correct all when there are/is positive
491
        // cases detected.
492
        // On the other hand it *may* be that for some strange fonts there is a false-positive
493
        // in the positive cases, so at least insist on positive cases being more than negative.
494
        // Still, do then correct *all* cases.
495
256k
        if(!maPositiveIdentifiedCases.empty()
496
565
            && maPositiveIdentifiedCases.size() >= maNegativeIdentifiedCases.size())
497
551
        {
498
551
            for(std::pair<rtl::Reference<MetaFontAction>, double>& rCandidate : maPositiveIdentifiedCases)
499
718
            {
500
718
                rCandidate.first->correctFontScale(static_cast<tools::Long>(rCandidate.second));
501
718
            }
502
551
            for(std::pair<rtl::Reference<MetaFontAction>, double>& rCandidate : maNegativeIdentifiedCases)
503
35
            {
504
35
                rCandidate.first->correctFontScale(static_cast<tools::Long>(rCandidate.second));
505
35
            }
506
551
        }
507
508
256k
        maPositiveIdentifiedCases.clear();
509
256k
        maNegativeIdentifiedCases.clear();
510
256k
    }
511
512
    Color MtfTools::ReadColor()
513
38.1k
    {
514
38.1k
        sal_uInt32 nColor(0);
515
38.1k
        mpInputStream->ReadUInt32( nColor );
516
38.1k
        Color aColor( COL_BLACK );
517
38.1k
        if ( ( nColor & 0xFFFF0000 ) == 0x01000000 )
518
738
        {
519
738
            size_t index = nColor & 0x0000FFFF;
520
738
            if ( index < maPalette.aPaletteColors.size() )
521
327
                aColor = maPalette.aPaletteColors[ index ];
522
411
            else
523
738
                SAL_INFO( "emfio", "\t\t Palette index out of range: " << index );
524
738
        }
525
37.3k
        else
526
37.3k
            aColor = Color( static_cast<sal_uInt8>( nColor ), static_cast<sal_uInt8>( nColor >> 8 ), static_cast<sal_uInt8>( nColor >> 16 ) );
527
528
38.1k
        SAL_INFO("emfio", "\t\tColor: " << aColor);
529
38.1k
        return aColor;
530
38.1k
    };
531
532
    Point MtfTools::ImplScale(const Point& rPoint) // Hack to set varying defaults for incompletely defined files.
533
0
    {
534
0
        if (!mbIsMapDevSet)
535
0
            return Point(rPoint.X() * UNDOCUMENTED_WIN_RCL_RELATION - mrclFrame.Left(),
536
0
                         rPoint.Y() * UNDOCUMENTED_WIN_RCL_RELATION - mrclFrame.Top());
537
0
        else
538
0
            return rPoint;
539
0
    }
540
541
    Point MtfTools::ImplMap( const Point& rPt )
542
4.80M
    {
543
4.80M
        if ( mnWinExtX && mnWinExtY )
544
4.78M
        {
545
4.78M
            double fX = rPt.X();
546
4.78M
            double fY = rPt.Y();
547
548
4.78M
            double fX2 = fX * maXForm.eM11 + fY * maXForm.eM21 + maXForm.eDx;
549
4.78M
            double fY2 = fX * maXForm.eM12 + fY * maXForm.eM22 + maXForm.eDy;
550
551
4.78M
            if ( meGfxMode == GraphicsMode::GM_COMPATIBLE )
552
4.78M
            {
553
4.78M
                fX2 -= mnWinOrgX;
554
4.78M
                fY2 -= mnWinOrgY;
555
556
4.78M
                switch( meMapMode )
557
4.78M
                {
558
1.37k
                    case MappingMode::MM_LOENGLISH :
559
1.37k
                    {
560
1.37k
                        fX2 = o3tl::convert(fX2, o3tl::Length::in100, o3tl::Length::mm100);
561
1.37k
                        fY2 = o3tl::convert(-fY2, o3tl::Length::in100, o3tl::Length::mm100);
562
1.37k
                    }
563
1.37k
                    break;
564
784
                    case MappingMode::MM_HIENGLISH :
565
784
                    {
566
784
                        fX2 = o3tl::convert(fX2, o3tl::Length::in1000, o3tl::Length::mm100);
567
784
                        fY2 = o3tl::convert(-fY2, o3tl::Length::in1000, o3tl::Length::mm100);
568
784
                    }
569
784
                    break;
570
891
                    case MappingMode::MM_TWIPS:
571
891
                    {
572
891
                        fX2 = o3tl::convert(fX2, o3tl::Length::twip, o3tl::Length::mm100);
573
891
                        fY2 = o3tl::convert(-fY2, o3tl::Length::twip, o3tl::Length::mm100);
574
891
                    }
575
891
                    break;
576
439
                    case MappingMode::MM_LOMETRIC :
577
439
                    {
578
439
                        fX2 = o3tl::convert(fX2, o3tl::Length::mm10, o3tl::Length::mm100);
579
439
                        fY2 = o3tl::convert(-fY2, o3tl::Length::mm10, o3tl::Length::mm100);
580
439
                    }
581
439
                    break;
582
2.35k
                    case MappingMode::MM_HIMETRIC : // in hundredth of a millimeter
583
2.35k
                    {
584
2.35k
                        fY2 *= -1;
585
2.35k
                    }
586
2.35k
                    break;
587
4.77M
                    default :
588
4.77M
                    {
589
4.77M
                        if (mnPixX == 0 || mnPixY == 0)
590
140k
                        {
591
140k
                            SAL_WARN("emfio", "invalid scaling factor");
592
140k
                            return Point();
593
140k
                        }
594
4.63M
                        else
595
4.63M
                        {
596
4.63M
                            if ( meMapMode != MappingMode::MM_TEXT )
597
905k
                            {
598
905k
                                fX2 /= mnWinExtX;
599
905k
                                fY2 /= mnWinExtY;
600
905k
                                fX2 *= mnDevWidth;
601
905k
                                fY2 *= mnDevHeight;
602
905k
                            }
603
4.63M
                            fX2 *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
604
4.63M
                            fY2 *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
605
4.63M
                        }
606
4.77M
                    }
607
4.63M
                    break;
608
4.78M
                }
609
610
4.64M
                double nDevOrgX = mnDevOrgX;
611
4.64M
                if (mnPixX)
612
4.63M
                    nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
613
4.64M
                fX2 += nDevOrgX;
614
4.64M
                double nDevOrgY = mnDevOrgY;
615
4.64M
                if (mnPixY)
616
4.63M
                    nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
617
4.64M
                fY2 += nDevOrgY;
618
619
4.64M
                fX2 -= mrclFrame.Left();
620
4.64M
                fY2 -= mrclFrame.Top();
621
4.64M
            }
622
4.64M
            return Point(basegfx::fround<tools::Long>(fX2), basegfx::fround<tools::Long>(fY2));
623
4.78M
        }
624
26.9k
        else
625
26.9k
            return Point();
626
4.80M
    };
627
628
    Size MtfTools::ImplMap(const Size& rSz, bool bDoWorldTransform)
629
219k
    {
630
219k
        if ( mnWinExtX && mnWinExtY )
631
212k
        {
632
            // #i121382# apply the whole WorldTransform, else a rotation will be misinterpreted
633
212k
            double fWidth, fHeight;
634
212k
            if (bDoWorldTransform)
635
184k
            {
636
184k
                fWidth = rSz.Width() * maXForm.eM11 + rSz.Height() * maXForm.eM21;
637
184k
                fHeight = rSz.Width() * maXForm.eM12 + rSz.Height() * maXForm.eM22;
638
184k
            }
639
28.4k
            else
640
28.4k
            {
641
                //take the scale, but not the rotation
642
28.4k
                basegfx::B2DHomMatrix aMatrix(maXForm.eM11, maXForm.eM12, 0,
643
28.4k
                                              maXForm.eM21, maXForm.eM22, 0);
644
28.4k
                basegfx::B2DTuple aScale, aTranslate;
645
28.4k
                double fRotate, fShearX;
646
28.4k
                if (!aMatrix.decompose(aScale, aTranslate, fRotate, fShearX))
647
388
                {
648
388
                    aScale.setX(1.0);
649
388
                    aScale.setY(1.0);
650
388
                }
651
28.4k
                fWidth = rSz.Width() * aScale.getX();
652
28.4k
                fHeight = rSz.Height() * aScale.getY();
653
28.4k
            }
654
655
212k
            if ( meGfxMode == GraphicsMode::GM_COMPATIBLE )
656
212k
            {
657
212k
                switch( meMapMode )
658
212k
                {
659
972
                    case MappingMode::MM_LOENGLISH :
660
972
                    {
661
972
                        fWidth = o3tl::convert(fWidth, o3tl::Length::in100, o3tl::Length::mm100);
662
972
                        fHeight = o3tl::convert(-fHeight, o3tl::Length::in100, o3tl::Length::mm100);
663
972
                    }
664
972
                    break;
665
558
                    case MappingMode::MM_HIENGLISH :
666
558
                    {
667
558
                        fWidth = o3tl::convert(fWidth, o3tl::Length::in1000, o3tl::Length::mm100);
668
558
                        fHeight = o3tl::convert(-fHeight, o3tl::Length::in1000, o3tl::Length::mm100);
669
558
                    }
670
558
                    break;
671
1.84k
                    case MappingMode::MM_LOMETRIC :
672
1.84k
                    {
673
1.84k
                        fWidth = o3tl::convert(fWidth, o3tl::Length::mm10, o3tl::Length::mm100);
674
1.84k
                        fHeight = o3tl::convert(-fHeight, o3tl::Length::mm10, o3tl::Length::mm100);
675
1.84k
                    }
676
1.84k
                    break;
677
2.81k
                    case MappingMode::MM_HIMETRIC : // in hundredth of millimeters
678
2.81k
                    {
679
2.81k
                        fHeight *= -1;
680
2.81k
                    }
681
2.81k
                    break;
682
77
                    case MappingMode::MM_TWIPS:
683
77
                    {
684
77
                        fWidth = o3tl::convert(fWidth, o3tl::Length::twip, o3tl::Length::mm100);
685
77
                        fHeight = o3tl::convert(-fHeight, o3tl::Length::twip, o3tl::Length::mm100);
686
77
                    }
687
77
                    break;
688
206k
                    default :
689
206k
                    {
690
206k
                        if (mnPixX == 0 || mnPixY == 0)
691
4.43k
                        {
692
4.43k
                            SAL_WARN("emfio", "invalid scaling factor");
693
4.43k
                            return Size();
694
4.43k
                        }
695
202k
                        else
696
202k
                        {
697
202k
                            if ( meMapMode != MappingMode::MM_TEXT )
698
111k
                            {
699
111k
                                fWidth /= mnWinExtX;
700
111k
                                fHeight /= mnWinExtY;
701
111k
                                fWidth *= mnDevWidth;
702
111k
                                fHeight *= mnDevHeight;
703
111k
                            }
704
202k
                            fWidth *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
705
202k
                            fHeight *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
706
202k
                        }
707
206k
                    }
708
202k
                    break;
709
212k
                }
710
212k
            }
711
208k
            return Size(basegfx::fround<tools::Long>(fWidth), basegfx::fround<tools::Long>(fHeight));
712
212k
        }
713
6.99k
        else
714
6.99k
            return Size();
715
219k
    }
716
717
    tools::Rectangle MtfTools::ImplMap( const tools::Rectangle& rRect )
718
61.3k
    {
719
61.3k
        tools::Rectangle aRect;
720
61.3k
        aRect.SetPos(ImplMap(rRect.TopLeft()));
721
61.3k
        aRect.SaturatingSetSize(ImplMap(rRect.GetSize()));
722
61.3k
        return aRect;
723
61.3k
    }
724
725
    void MtfTools::ImplMap( vcl::Font& rFont )
726
30.3k
    {
727
        // !!! HACK: we now always set the width to zero because the OS width is interpreted differently;
728
        // must later be made portable in SV (KA 1996-02-08)
729
30.3k
        Size  aFontSize = ImplMap (rFont.GetFontSize(), false);
730
731
30.3k
        const auto nHeight = aFontSize.Height();
732
30.3k
        if (nHeight < 0)
733
1.52k
            aFontSize.setHeight( o3tl::saturating_toggle_sign(nHeight) );
734
735
30.3k
        rFont.SetFontSize( aFontSize );
736
737
30.3k
        sal_Int32 nResult;
738
30.3k
        const bool bFail = o3tl::checked_multiply(mnWinExtX, mnWinExtY, nResult);
739
30.3k
        if (!bFail && nResult < 0)
740
454
            rFont.SetOrientation( 3600_deg10 - rFont.GetOrientation() );
741
30.3k
    }
742
743
    tools::Polygon& MtfTools::ImplMap( tools::Polygon& rPolygon )
744
151k
    {
745
151k
        sal_uInt16 nPoints = rPolygon.GetSize();
746
4.41M
        for ( sal_uInt16 i = 0; i < nPoints; i++ )
747
4.26M
        {
748
4.26M
            rPolygon[ i ] = ImplMap( rPolygon[ i ] );
749
4.26M
        }
750
151k
        return rPolygon;
751
151k
    }
752
753
    void MtfTools::ImplScale( tools::Polygon& rPolygon )
754
0
    {
755
0
        sal_uInt16 nPoints = rPolygon.GetSize();
756
0
        for ( sal_uInt16 i = 0; i < nPoints; i++ )
757
0
        {
758
0
            rPolygon[ i ] = ImplScale( rPolygon[ i ] );
759
0
        }
760
0
    }
761
762
    tools::PolyPolygon& MtfTools::ImplScale( tools::PolyPolygon& rPolyPolygon )
763
0
    {
764
0
        sal_uInt16 nPolys = rPolyPolygon.Count();
765
0
        for (sal_uInt16 i = 0; i < nPolys; ++i)
766
0
        {
767
0
            ImplScale(rPolyPolygon[i]);
768
0
        }
769
0
        return rPolyPolygon;
770
0
    }
771
772
    tools::PolyPolygon& MtfTools::ImplMap( tools::PolyPolygon& rPolyPolygon )
773
5.98k
    {
774
5.98k
        sal_uInt16 nPolys = rPolyPolygon.Count();
775
14.8k
        for ( sal_uInt16 i = 0; i < nPolys; ImplMap( rPolyPolygon[ i++ ] ) ) ;
776
5.98k
        return rPolyPolygon;
777
5.98k
    }
778
779
    void MtfTools::SelectObject( sal_uInt32 nIndex )
780
85.1k
    {
781
85.1k
        if ( nIndex & ENHMETA_STOCK_OBJECT )
782
13.7k
        {
783
13.7k
            SAL_INFO ( "emfio", "\t\t ENHMETA_STOCK_OBJECT, StockObject Enumeration: 0x" << std::hex  << nIndex );
784
13.7k
            StockObject nStockId = static_cast<StockObject>(nIndex & 0xFF);
785
13.7k
            switch( nStockId )
786
13.7k
            {
787
2.63k
                case StockObject::WHITE_BRUSH :
788
2.63k
                {
789
2.63k
                    maFillStyle = WinMtfFillStyle( COL_WHITE );
790
2.63k
                    mbFillStyleSelected = true;
791
2.63k
                }
792
2.63k
                break;
793
394
                case StockObject::LTGRAY_BRUSH :
794
394
                {
795
394
                    maFillStyle = WinMtfFillStyle( COL_LIGHTGRAY );
796
394
                    mbFillStyleSelected = true;
797
394
                }
798
394
                break;
799
325
                case StockObject::GRAY_BRUSH :
800
325
                {
801
325
                    maFillStyle = WinMtfFillStyle( COL_GRAY );
802
325
                    mbFillStyleSelected = true;
803
325
                }
804
325
                break;
805
75
                case StockObject::DKGRAY_BRUSH :
806
75
                {
807
75
                    maFillStyle = WinMtfFillStyle( COL_GRAY7 );
808
75
                    mbFillStyleSelected = true;
809
75
                }
810
75
                break;
811
406
                case StockObject::BLACK_BRUSH :
812
406
                {
813
406
                    maFillStyle = WinMtfFillStyle( COL_BLACK );
814
406
                    mbFillStyleSelected = true;
815
406
                }
816
406
                break;
817
1.05k
                case StockObject::NULL_BRUSH :
818
1.05k
                {
819
1.05k
                   maFillStyle = WinMtfFillStyle( COL_TRANSPARENT, true );
820
1.05k
                   mbFillStyleSelected = true;
821
1.05k
                }
822
1.05k
                break;
823
472
                case StockObject::WHITE_PEN :
824
472
                {
825
472
                    maLineStyle = WinMtfLineStyle(COL_WHITE, PS_COSMETIC, 1);
826
472
                }
827
472
                break;
828
2.94k
                case StockObject::BLACK_PEN :
829
2.94k
                {
830
2.94k
                    maLineStyle = WinMtfLineStyle(COL_BLACK, PS_COSMETIC, 1);
831
2.94k
                }
832
2.94k
                break;
833
1.94k
                case StockObject::NULL_PEN :
834
1.94k
                {
835
1.94k
                    maLineStyle = WinMtfLineStyle( COL_TRANSPARENT, true );
836
1.94k
                }
837
1.94k
                break;
838
3.49k
                default:
839
3.49k
                break;
840
13.7k
            }
841
13.7k
        }
842
71.4k
        else
843
71.4k
        {
844
71.4k
            nIndex &= 0xffff;       // safety check: don't allow index to be > 65535
845
846
71.4k
            GDIObj *pGDIObj = nullptr;
847
848
71.4k
            if ( nIndex < mvGDIObj.size() )
849
67.6k
                pGDIObj = mvGDIObj[ nIndex ].get();
850
851
71.4k
            if ( pGDIObj )
852
64.2k
            {
853
854
64.2k
                SAL_INFO ( "emfio", "\t\t Index: " << nIndex );
855
64.2k
                if (const auto pen = dynamic_cast<WinMtfLineStyle*>(pGDIObj))
856
19.6k
                {
857
19.6k
                    maLineStyle = *pen;
858
19.6k
                    SAL_INFO ( "emfio", "\t Line Style, Color: 0x" << std::hex << maLineStyle.aLineColor
859
19.6k
                                        << ", Weight: " << maLineStyle.aLineInfo.GetWidth() );
860
19.6k
                }
861
44.6k
                else if (const auto brush = dynamic_cast<WinMtfFillStyle*>(
862
44.6k
                             pGDIObj))
863
16.3k
                {
864
16.3k
                    maFillStyle = *brush;
865
16.3k
                    mbFillStyleSelected = true;
866
16.3k
                    SAL_INFO("emfio", "\t\tBrush Object, Index: " << nIndex << ", Color: " << maFillStyle.aFillColor);
867
16.3k
                }
868
28.3k
                else if (const auto font = dynamic_cast<WinMtfFontStyle*>(
869
28.3k
                             pGDIObj))
870
26.0k
                {
871
26.0k
                    maFont = font->aFont;
872
26.0k
                    SAL_INFO("emfio", "\t\tFont Object, Index: " << nIndex << ", Font: " << maFont.GetFamilyName() << " " << maFont.GetStyleName());
873
26.0k
                }
874
2.23k
                else if (const auto palette = dynamic_cast<WinMtfPalette*>(
875
2.23k
                             pGDIObj))
876
1.93k
                {
877
1.93k
                    maPalette = palette->aPaletteColors;
878
1.93k
                    SAL_INFO("emfio", "\t\tPalette Object, Index: " << nIndex << ", Number of colours: " << maPalette.aPaletteColors.size() );
879
1.93k
                }
880
64.2k
            }
881
7.18k
            else
882
7.18k
            {
883
7.18k
                SAL_WARN("emfio", "Warning: Unable to find Object with index:" << nIndex);
884
7.18k
            }
885
71.4k
        }
886
85.1k
    }
887
888
    void MtfTools::SetTextLayoutMode( vcl::text::ComplexTextLayoutFlags nTextLayoutMode )
889
58.2k
    {
890
58.2k
        mnTextLayoutMode = nTextLayoutMode;
891
58.2k
    }
892
893
    void MtfTools::SetArcDirection(bool bClockWise)
894
579
    {
895
579
        SAL_INFO("emfio", "\t\t Arc direction: " << (bClockWise ? "ClockWise" : "CounterClockWise"));
896
579
        mbClockWiseArcDirection = bClockWise;
897
579
    }
898
899
    void MtfTools::SetBkMode( BackgroundMode nMode )
900
4.53k
    {
901
4.53k
        if (mnBkMode != nMode)
902
2.74k
        {
903
2.74k
            mnBkMode = nMode;
904
            // The hatch fill background color emitted by UpdateFillStyle
905
            // depends on bk mode: opaque -> bk color, transparent -> none.
906
            // Invalidate maLatestFillStyle so UpdateFillStyle re-emits a
907
            // fresh MetaFillColorAction even when the brush is otherwise
908
            // unchanged.
909
2.74k
            maLatestFillStyle.aFillColor = Color(0x12, 0x34, 0x56);
910
2.74k
        }
911
4.53k
    }
912
913
    void MtfTools::SetPolyFillMode( sal_uInt32 nPolyFillMode )
914
3.26k
    {
915
3.26k
        mnPolyFillMode = nPolyFillMode;
916
3.26k
    }
917
918
    void MtfTools::SetBkColor( const Color& rColor )
919
5.33k
    {
920
5.33k
        maBkColor = rColor;
921
5.33k
    }
922
923
    void MtfTools::SetTextColor( const Color& rColor )
924
6.40k
    {
925
6.40k
        maTextColor = rColor;
926
6.40k
    }
927
928
    void MtfTools::SetTextAlign( sal_uInt32 nAlign )
929
6.19k
    {
930
6.19k
        mnTextAlign = nAlign;
931
6.19k
    }
932
933
    void MtfTools::ImplResizeObjectArry( sal_uInt32 nNewEntrys )
934
12.1k
    {
935
12.1k
        mvGDIObj.resize(nNewEntrys);
936
12.1k
    }
937
938
    // Convert a PolyPolygon so that nonzero/winding fill rule gives the same visual
939
    // result when rendered with even-odd fill rule (which is all VCL supports).
940
    static tools::PolyPolygon CreateWindingFillPolyPolygon(const tools::PolyPolygon& rPolyPoly)
941
7.20k
    {
942
7.20k
        basegfx::B2DPolyPolygon aB2D = rPolyPoly.getB2DPolyPolygon();
943
        // A filled polygon is closed whether or not its last point repeats its first
944
7.20k
        aB2D.setClosed(true);
945
7.20k
        return tools::PolyPolygon(basegfx::utils::createNonzeroConform(aB2D));
946
7.20k
    }
947
948
    void MtfTools::ImplDrawClippedPolyPolygon( const tools::PolyPolygon& rPolyPoly )
949
0
    {
950
0
        if ( !rPolyPoly.Count() )
951
0
            return;
952
953
0
        ImplSetNonPersistentLineColorTransparenz();
954
955
0
        if (mnPolyFillMode == 2) // WINDING
956
0
        {
957
0
            tools::PolyPolygon aFill = CreateWindingFillPolyPolygon(rPolyPoly);
958
0
            mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( aFill ) );
959
0
            return;
960
0
        }
961
962
0
        if ( rPolyPoly.Count() == 1 )
963
0
        {
964
0
            if ( rPolyPoly.IsRect() )
965
0
                mpGDIMetaFile->AddAction( new MetaRectAction( rPolyPoly.GetBoundRect() ) );
966
0
            else
967
0
            {
968
0
                tools::Polygon aPoly( rPolyPoly[ 0 ] );
969
0
                sal_uInt16 nCount = aPoly.GetSize();
970
0
                if ( nCount )
971
0
                {
972
0
                    if ( aPoly[ nCount - 1 ] != aPoly[ 0 ] )
973
0
                    {
974
0
                        Point aPoint( aPoly[ 0 ] );
975
0
                        aPoly.Insert( nCount, aPoint );
976
0
                    }
977
0
                    mpGDIMetaFile->AddAction( new MetaPolygonAction( std::move(aPoly) ) );
978
0
                }
979
0
            }
980
0
        }
981
0
        else
982
0
            mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPoly ) );
983
984
0
        if (maLatestFillStyle.aType == WinMtfFillStyleType::Hatch)
985
0
            mpGDIMetaFile->AddAction( new MetaHatchAction( rPolyPoly, maLatestFillStyle.aHatch ) );
986
0
    }
987
988
    void MtfTools::CreateObject( std::unique_ptr<GDIObj> pObject )
989
43.8k
    {
990
43.8k
        if ( pObject )
991
43.8k
        {
992
43.8k
            const auto pLineStyle = dynamic_cast<WinMtfLineStyle*>(pObject.get());
993
43.8k
            const auto pFontStyle = dynamic_cast<WinMtfFontStyle*>(pObject.get());
994
995
43.8k
            if ( pFontStyle )
996
24.5k
            {
997
24.5k
                if (pFontStyle->aFont.GetFontHeight() == 0)
998
1.80k
                    pFontStyle->aFont.SetFontHeight(423);
999
24.5k
                ImplMap(pFontStyle->aFont); // defaulting to 12pt
1000
24.5k
            }
1001
19.3k
            else if ( pLineStyle )
1002
7.96k
            {
1003
7.96k
                Size aSize(pLineStyle->aLineInfo.GetWidth(), 0);
1004
7.96k
                aSize = ImplMap(aSize);
1005
7.96k
                pLineStyle->aLineInfo.SetWidth(aSize.Width());
1006
7.96k
            }
1007
43.8k
        }
1008
43.8k
        std::vector<std::unique_ptr<GDIObj>>::size_type nIndex;
1009
55.1k
        for ( nIndex = mnLowestFreeGDIObj; nIndex < mvGDIObj.size(); nIndex++ )
1010
47.2k
        {
1011
47.2k
            if ( !mvGDIObj[ nIndex ] )
1012
35.9k
                break;
1013
47.2k
        }
1014
43.8k
        if ( nIndex > 0xffff )   // the same ceiling an indexed object has
1015
0
        {
1016
0
            SAL_WARN( "emfio", "too many objects" );
1017
0
            return;
1018
0
        }
1019
43.8k
        if ( nIndex == mvGDIObj.size() )
1020
7.89k
            ImplResizeObjectArry( mvGDIObj.size() + 16 );
1021
1022
43.8k
        mvGDIObj[ nIndex ] = std::move(pObject);
1023
43.8k
        mnLowestFreeGDIObj = nIndex + 1;
1024
43.8k
    }
1025
1026
    void MtfTools::CreateObjectIndexed( sal_uInt32 nIndex, std::unique_ptr<GDIObj> pObject )
1027
29.4k
    {
1028
29.4k
        if ( ( nIndex & ENHMETA_STOCK_OBJECT ) != 0 )
1029
183
            return;
1030
1031
29.2k
        nIndex &= 0xffff;       // safety check: do not allow index to be > 65535
1032
29.2k
        if ( pObject )
1033
29.2k
        {
1034
29.2k
            const auto pLineStyle = dynamic_cast<WinMtfLineStyle*>(pObject.get());
1035
29.2k
            const auto pFontStyle = dynamic_cast<WinMtfFontStyle*>(pObject.get());
1036
29.2k
            if ( pFontStyle )
1037
5.77k
            {
1038
5.77k
                if (pFontStyle->aFont.GetFontHeight() == 0)
1039
1.08k
                    pFontStyle->aFont.SetFontHeight(423);
1040
5.77k
                ImplMap(pFontStyle->aFont);
1041
5.77k
            }
1042
23.4k
            else if ( pLineStyle )
1043
5.47k
            {
1044
5.47k
                Size aSize(pLineStyle->aLineInfo.GetWidth(), 0);
1045
5.47k
                pLineStyle->aLineInfo.SetWidth( ImplMap(aSize).Width() );
1046
1047
5.47k
                if ( pLineStyle->aLineInfo.GetStyle() == LineStyle::Dash )
1048
1.53k
                {
1049
1.53k
                    aSize.AdjustWidth(1 );
1050
1.53k
                    tools::Long nDashLen, nDotLen = ImplMap( aSize ).Width();
1051
1.53k
                    const bool bFail = o3tl::checked_multiply<tools::Long>(nDotLen, 3, nDashLen);
1052
1.53k
                    if (!bFail)
1053
1.43k
                    {
1054
1.43k
                        pLineStyle->aLineInfo.SetDistance( nDotLen );
1055
1.43k
                        pLineStyle->aLineInfo.SetDotLen( nDotLen );
1056
1.43k
                        pLineStyle->aLineInfo.SetDashLen( nDotLen * 3 );
1057
1.43k
                    }
1058
93
                    else
1059
93
                    {
1060
93
                        SAL_WARN("emfio", "DotLen too long: " << nDotLen);
1061
93
                    }
1062
1.53k
                }
1063
5.47k
            }
1064
29.2k
        }
1065
29.2k
        if ( nIndex >= mvGDIObj.size() )
1066
4.22k
            ImplResizeObjectArry( nIndex + 16 );
1067
1068
29.2k
        if ( !pObject && nIndex < mnLowestFreeGDIObj )
1069
0
            mnLowestFreeGDIObj = nIndex;
1070
1071
29.2k
        mvGDIObj[ nIndex ] = std::move(pObject);
1072
29.2k
    }
1073
1074
    void MtfTools::CreateObject()
1075
3.90k
    {
1076
3.90k
        CreateObject(std::make_unique<GDIObj>());
1077
3.90k
    }
1078
1079
    void MtfTools::DeleteObject( sal_uInt32 nIndex )
1080
19.6k
    {
1081
19.6k
        if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
1082
19.6k
        {
1083
19.6k
            if ( nIndex < mvGDIObj.size() )
1084
18.9k
            {
1085
18.9k
                mvGDIObj[ nIndex ].reset();
1086
18.9k
                if ( nIndex < mnLowestFreeGDIObj )
1087
8.66k
                    mnLowestFreeGDIObj = nIndex;
1088
18.9k
            }
1089
19.6k
        }
1090
19.6k
    }
1091
1092
    void MtfTools::IntersectClipRect( const tools::Rectangle& rRect )
1093
11.9k
    {
1094
11.9k
        if (comphelper::IsFuzzing())
1095
11.9k
            return;
1096
0
        mbClipNeedsUpdate=true;
1097
0
        if ((rRect.Left()-rRect.Right()==0) && (rRect.Top()-rRect.Bottom()==0))
1098
0
        {
1099
0
            return; // empty rectangles cause trouble
1100
0
        }
1101
0
        tools::Polygon aPoly( rRect );
1102
0
        const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) );
1103
0
        maClipPath.intersectClip( aPolyPolyRect.getB2DPolyPolygon() );
1104
0
    }
1105
1106
    void MtfTools::ExcludeClipRect( const tools::Rectangle& rRect )
1107
549
    {
1108
549
        if (comphelper::IsFuzzing())
1109
549
            return;
1110
0
        mbClipNeedsUpdate=true;
1111
0
        tools::Polygon aPoly( rRect );
1112
0
        const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) );
1113
0
        maClipPath.excludeClip( aPolyPolyRect.getB2DPolyPolygon() );
1114
0
    }
1115
1116
    void MtfTools::MoveClipRegion( const Size& rSize )
1117
161
    {
1118
161
        if (comphelper::IsFuzzing())
1119
161
            return;
1120
0
        mbClipNeedsUpdate=true;
1121
0
        maClipPath.moveClipRegion( ImplMap( rSize ) );
1122
0
    }
1123
1124
    void MtfTools::SetClipPath( const tools::PolyPolygon& rPolyPolygon, RegionMode eClippingMode, bool bIsMapped )
1125
808
    {
1126
808
        if (comphelper::IsFuzzing())
1127
808
            return;
1128
0
        mbClipNeedsUpdate = true;
1129
0
        tools::PolyPolygon aPolyPolygon(rPolyPolygon);
1130
1131
0
        if (!bIsMapped)
1132
0
        {
1133
0
            if (!mbIsMapDevSet && (meMapMode == MappingMode::MM_ISOTROPIC || meMapMode == MappingMode::MM_ANISOTROPIC))
1134
0
                aPolyPolygon = ImplScale(aPolyPolygon);
1135
0
            else
1136
0
                aPolyPolygon = ImplMap(aPolyPolygon);
1137
0
        }
1138
0
        maClipPath.setClipPath(aPolyPolygon.getB2DPolyPolygon(), eClippingMode);
1139
0
    }
1140
1141
    void MtfTools::SetDefaultClipPath()
1142
339
    {
1143
339
        mbClipNeedsUpdate = true;
1144
339
        maClipPath.setDefaultClipPath();
1145
339
    }
1146
1147
    MtfTools::MtfTools( GDIMetaFile& rGDIMetaFile, SvStream& rStreamWMF)
1148
22.6k
    :   mnLatestTextAlign(90),
1149
22.6k
        mnTextAlign(TextAlignmentMode::TA_LEFT | TextAlignmentMode::TA_TOP | TextAlignmentMode::TA_NOUPDATECP),
1150
22.6k
        maLatestBkColor(ColorTransparency, 0x12345678),
1151
22.6k
        maBkColor(COL_WHITE),
1152
22.6k
        mnLatestTextLayoutMode(vcl::text::ComplexTextLayoutFlags::Default),
1153
22.6k
        mnTextLayoutMode(vcl::text::ComplexTextLayoutFlags::Default),
1154
22.6k
        mnLatestBkMode(BackgroundMode::NONE),
1155
22.6k
        mnBkMode(BackgroundMode::OPAQUE),
1156
22.6k
        meLatestRasterOp(RasterOp::Invert),
1157
22.6k
        meRasterOp(RasterOp::OverPaint),
1158
22.6k
        mnLowestFreeGDIObj(0),
1159
        mnRop(),
1160
22.6k
        meGfxMode(GraphicsMode::GM_COMPATIBLE),
1161
22.6k
        meMapMode(MappingMode::MM_TEXT),
1162
22.6k
        mnDevOrgX(0),
1163
22.6k
        mnDevOrgY(0),
1164
22.6k
        mnDevWidth(1),
1165
22.6k
        mnDevHeight(1),
1166
22.6k
        mnWinOrgX(0),
1167
22.6k
        mnWinOrgY(0),
1168
22.6k
        mnWinExtX(1),
1169
22.6k
        mnWinExtY(1),
1170
22.6k
        mnPixX(100),
1171
22.6k
        mnPixY(100),
1172
22.6k
        mnMillX(1),
1173
22.6k
        mnMillY(1),
1174
22.6k
        mpGDIMetaFile(&rGDIMetaFile),
1175
22.6k
        mpInputStream(&rStreamWMF),
1176
22.6k
        mnStartPos(0),
1177
22.6k
        mnEndPos(0),
1178
22.6k
        mbNopMode(false),
1179
22.6k
        mbClockWiseArcDirection(false),
1180
22.6k
        mbFillStyleSelected(false),
1181
22.6k
        mnPolyFillMode(1),          // ALTERNATE
1182
22.6k
        mbClipNeedsUpdate(true),
1183
22.6k
        mbComplexClip(false),
1184
22.6k
        mbIsMapWinSet(false),
1185
22.6k
        mbIsMapDevSet(false)
1186
22.6k
    {
1187
22.6k
        SvLockBytes *pLB = mpInputStream->GetLockBytes();
1188
1189
22.6k
        if (pLB)
1190
22.5k
        {
1191
22.5k
            pLB->SetSynchronMode();
1192
22.5k
        }
1193
1194
22.6k
        mnStartPos = mpInputStream->Tell();
1195
22.6k
        SetDevOrg(Point());
1196
1197
22.6k
        mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::CLIPREGION ) ); // The original clipregion has to be on top
1198
                                                                                 // of the stack so it can always be restored
1199
                                                                                 // this is necessary to be able to support
1200
                                                                                 // SetClipRgn( NULL ) and similar ClipRgn actions (SJ)
1201
1202
22.6k
        maFont.SetFamilyName( u"Arial"_ustr );                                         // sj: #i57205#, we do have some scaling problems if using
1203
22.6k
        maFont.SetCharSet( RTL_TEXTENCODING_MS_1252 );                           // the default font then most times a x11 font is used, we
1204
22.6k
        maFont.SetFontHeight( 423 );                                      // will prevent this defining a font
1205
1206
22.6k
        maLatestLineStyle.aLineColor = Color( 0x12, 0x34, 0x56 );
1207
22.6k
        maLatestFillStyle.aFillColor = Color( 0x12, 0x34, 0x56 );
1208
1209
22.6k
        mnRop = WMFRasterOp::Black;
1210
22.6k
        meRasterOp = RasterOp::OverPaint;
1211
22.6k
        mpGDIMetaFile->AddAction( new MetaRasterOpAction( RasterOp::OverPaint ) );
1212
22.6k
    }
1213
1214
    MtfTools::~MtfTools()
1215
22.6k
    {
1216
22.6k
        mpGDIMetaFile->AddAction( new MetaPopAction() );
1217
22.6k
        mpGDIMetaFile->SetPrefMapMode(MapMode(MapUnit::Map100thMM));
1218
22.6k
        if ( mrclFrame.IsEmpty() )
1219
11.6k
            mpGDIMetaFile->SetPrefSize( Size( mnDevWidth, mnDevHeight ) );
1220
11.0k
        else
1221
11.0k
            mpGDIMetaFile->SetPrefSize( mrclFrame.GetSize() );
1222
22.6k
    }
1223
1224
    void MtfTools::UpdateClipRegion()
1225
735k
    {
1226
735k
        if (!mbClipNeedsUpdate)
1227
722k
            return;
1228
1229
12.9k
        mbClipNeedsUpdate = false;
1230
12.9k
        mbComplexClip = false;
1231
1232
12.9k
        mpGDIMetaFile->AddAction( new MetaPopAction() );                    // taking the original clipregion
1233
12.9k
        mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::CLIPREGION ) );
1234
1235
        // skip for 'no clipping at all' case
1236
12.9k
        if( maClipPath.isEmpty() )
1237
12.9k
            return;
1238
1239
0
        const basegfx::B2DPolyPolygon& rClipPoly( maClipPath.getClipPath() );
1240
1241
0
        mbComplexClip = rClipPoly.count() > 1
1242
0
            || !basegfx::utils::isRectangle(rClipPoly);
1243
1244
        // This makes cases like tdf#45820 work in reasonable time.
1245
0
        if (mbComplexClip)
1246
0
        {
1247
0
            mpGDIMetaFile->AddAction(
1248
0
                new MetaISectRegionClipRegionAction(
1249
0
                    vcl::Region(rClipPoly)));
1250
0
            mbComplexClip = false;
1251
0
        }
1252
0
        else
1253
0
        {
1254
0
            mpGDIMetaFile->AddAction(
1255
0
                new MetaISectRectClipRegionAction(
1256
0
                    vcl::unotools::rectangleFromB2DRectangle(
1257
0
                        rClipPoly.getB2DRange())));
1258
0
        }
1259
0
    }
1260
1261
    bool MtfTools::ImplEmitLineHatch(const tools::Polygon& rPoly)
1262
457k
    {
1263
457k
        if (!maLineStyle.bHasHatch || maLineStyle.aLineInfo.GetWidth() <= 0)
1264
438k
            return false;
1265
1266
18.3k
        basegfx::B2DPolyPolygon aArea = basegfx::utils::createAreaGeometry(
1267
18.3k
            rPoly.getB2DPolygon(),
1268
18.3k
            maLineStyle.aLineInfo.GetWidth() / 2.0,
1269
18.3k
            maLineStyle.aLineInfo.GetLineJoin(),
1270
18.3k
            maLineStyle.aLineInfo.GetLineCap());
1271
18.3k
        tools::PolyPolygon aPP(aArea);
1272
1273
        // Fill the stroke area with background color (opaque) or transparent
1274
18.3k
        mpGDIMetaFile->AddAction(new MetaPushAction(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR));
1275
18.3k
        mpGDIMetaFile->AddAction(new MetaLineColorAction(Color(), false));
1276
18.3k
        if (mnBkMode == BackgroundMode::OPAQUE)
1277
18.1k
            mpGDIMetaFile->AddAction(new MetaFillColorAction(maBkColor, true));
1278
274
        else
1279
274
            mpGDIMetaFile->AddAction(new MetaFillColorAction(Color(), false));
1280
18.3k
        mpGDIMetaFile->AddAction(new MetaPolyPolygonAction(aPP));
1281
18.3k
        mpGDIMetaFile->AddAction(new MetaPopAction());
1282
1283
        // Draw the hatch pattern on top
1284
18.3k
        mpGDIMetaFile->AddAction(new MetaHatchAction(aPP, maLineStyle.aHatch));
1285
18.3k
        return true;
1286
457k
    }
1287
1288
    void MtfTools::ImplEmitPatternFill(const tools::PolyPolygon& rPolyPoly)
1289
6.03k
    {
1290
6.03k
        const tools::Rectangle aBound = rPolyPoly.GetBoundRect();
1291
6.03k
        if (aBound.IsEmpty() || maLatestFillStyle.aBmp.IsEmpty())
1292
3.76k
            return;
1293
1294
2.26k
        Wallpaper aWallpaper(maLatestFillStyle.aBmp);
1295
2.26k
        aWallpaper.SetStyle(WallpaperStyle::Tile);
1296
1297
        // Clip the tiled wallpaper to the polypoly so non-rectangular fills
1298
        // (and edge-tile bleed at the bounds) stay inside the shape.
1299
2.26k
        mpGDIMetaFile->AddAction(new MetaPushAction(vcl::PushFlags::CLIPREGION));
1300
2.26k
        mpGDIMetaFile->AddAction(new MetaISectRegionClipRegionAction(vcl::Region(rPolyPoly)));
1301
2.26k
        mpGDIMetaFile->AddAction(new MetaWallpaperAction(aBound, aWallpaper));
1302
2.26k
        mpGDIMetaFile->AddAction(new MetaPopAction());
1303
2.26k
    }
1304
1305
    void MtfTools::ImplSetNonPersistentLineColorTransparenz()
1306
62.2k
    {
1307
62.2k
        WinMtfLineStyle aTransparentLine( COL_TRANSPARENT, true );
1308
62.2k
        if ( ! ( maLatestLineStyle == aTransparentLine ) )
1309
20.0k
        {
1310
20.0k
            maLatestLineStyle = aTransparentLine;
1311
20.0k
            mpGDIMetaFile->AddAction( new MetaLineColorAction( aTransparentLine.aLineColor, !aTransparentLine.bTransparent ) );
1312
20.0k
        }
1313
62.2k
    }
1314
1315
    void MtfTools::ResetInternalState()
1316
515
    {
1317
515
        maLatestBkColor = Color(0x12, 0x34, 0x56);
1318
515
        mnLatestBkMode = BackgroundMode::NONE;
1319
1320
515
        maLatestFillStyle.aFillColor = Color(0x12, 0x34, 0x56);
1321
515
        maLatestLineStyle.aLineColor = Color(0x12, 0x34, 0x56);
1322
1323
515
        meLatestRasterOp = RasterOp::Invert;
1324
1325
515
        mnLatestTextAlign = 90;
1326
515
        mnLatestTextLayoutMode = vcl::text::ComplexTextLayoutFlags::Default;
1327
515
    }
1328
1329
    void MtfTools::UpdateLineStyle()
1330
74.3k
    {
1331
74.3k
        if (!( maLatestLineStyle == maLineStyle ) )
1332
29.9k
        {
1333
29.9k
            maLatestLineStyle = maLineStyle;
1334
29.9k
            mpGDIMetaFile->AddAction( new MetaLineColorAction( maLineStyle.aLineColor, !maLineStyle.bTransparent ) );
1335
29.9k
        }
1336
74.3k
    }
1337
1338
    void MtfTools::UpdateFillStyle()
1339
128k
    {
1340
128k
        if ( !mbFillStyleSelected )     // SJ: #i57205# taking care of bkcolor if no brush is selected
1341
101k
            maFillStyle = WinMtfFillStyle( maBkColor, mnBkMode == BackgroundMode::Transparent );
1342
128k
        if (!( maLatestFillStyle == maFillStyle ) )
1343
17.2k
        {
1344
17.2k
            maLatestFillStyle = maFillStyle;
1345
17.2k
            if (maFillStyle.aType == WinMtfFillStyleType::Solid)
1346
14.9k
                mpGDIMetaFile->AddAction( new MetaFillColorAction( maFillStyle.aFillColor, !maFillStyle.bTransparent ) );
1347
2.28k
            else if (maFillStyle.aType == WinMtfFillStyleType::Hatch)
1348
239
            {
1349
239
                if (mnBkMode == BackgroundMode::OPAQUE)
1350
199
                    mpGDIMetaFile->AddAction( new MetaFillColorAction( maBkColor, true ) );
1351
40
                else
1352
40
                    mpGDIMetaFile->AddAction( new MetaFillColorAction( Color(), false ) );
1353
239
            }
1354
17.2k
        }
1355
128k
    }
1356
1357
    WMFRasterOp MtfTools::SetRasterOp( WMFRasterOp nRasterOp )
1358
152k
    {
1359
152k
        WMFRasterOp nRetROP = mnRop;
1360
152k
        if ( nRasterOp != mnRop )
1361
147k
        {
1362
147k
            mnRop = nRasterOp;
1363
1364
147k
            if ( mbNopMode && ( nRasterOp != WMFRasterOp::Nop ) )
1365
1.50k
            {   // changing modes from WMFRasterOp::Nop so set pen and brush
1366
1.50k
                maFillStyle = maNopFillStyle;
1367
1.50k
                maLineStyle = maNopLineStyle;
1368
1.50k
                mbNopMode = false;
1369
1.50k
            }
1370
147k
            switch( nRasterOp )
1371
147k
            {
1372
13.0k
                case WMFRasterOp::Not:
1373
13.0k
                    meRasterOp = RasterOp::Invert;
1374
13.0k
                break;
1375
1376
14.1k
                case WMFRasterOp::XorPen:
1377
14.1k
                    meRasterOp = RasterOp::Xor;
1378
14.1k
                break;
1379
1380
1.52k
                case WMFRasterOp::Nop:
1381
1.52k
                {
1382
1.52k
                    meRasterOp = RasterOp::OverPaint;
1383
1.52k
                    if( !mbNopMode )
1384
1.52k
                    {
1385
1.52k
                        maNopFillStyle = maFillStyle;
1386
1.52k
                        maNopLineStyle = maLineStyle;
1387
1.52k
                        maFillStyle = WinMtfFillStyle( COL_TRANSPARENT, true );
1388
1.52k
                        maLineStyle = WinMtfLineStyle( COL_TRANSPARENT, true );
1389
1.52k
                        mbNopMode = true;
1390
1.52k
                    }
1391
1.52k
                }
1392
1.52k
                break;
1393
1394
119k
                default:
1395
119k
                    meRasterOp = RasterOp::OverPaint;
1396
119k
                break;
1397
147k
            }
1398
147k
        }
1399
152k
        if ( nRetROP != nRasterOp )
1400
147k
            mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) );
1401
152k
        return nRetROP;
1402
152k
    };
1403
1404
    void MtfTools::StrokeAndFillPath( bool bStroke, bool bFill )
1405
14.1k
    {
1406
14.1k
        if ( !maPathObj.Count() )
1407
6.93k
            return;
1408
1409
7.19k
        UpdateClipRegion();
1410
7.19k
        UpdateLineStyle();
1411
7.19k
        UpdateFillStyle();
1412
7.19k
        bool bWindingFill = (mnPolyFillMode == 2);
1413
7.19k
        if ( bFill )
1414
4.70k
        {
1415
            // When using WINDING fill mode, we convert the geometry so it renders
1416
            // correctly with even-odd fill. We must also disable line color to avoid
1417
            // stroking internal edges of the converted fill geometry.
1418
4.70k
            if ( !bStroke || bWindingFill )
1419
4.12k
            {
1420
4.12k
                mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::LINECOLOR ) );
1421
4.12k
                mpGDIMetaFile->AddAction( new MetaLineColorAction( Color(), false ) );
1422
4.12k
            }
1423
4.70k
            if (bWindingFill)
1424
2.51k
            {
1425
2.51k
                tools::PolyPolygon aFill = CreateWindingFillPolyPolygon(maPathObj);
1426
2.51k
                mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( aFill ) );
1427
2.51k
            }
1428
2.19k
            else
1429
2.19k
            {
1430
2.19k
                if ( maPathObj.Count() == 1 )
1431
1.68k
                    mpGDIMetaFile->AddAction( new MetaPolygonAction( maPathObj.GetObject( 0 ) ) );
1432
504
                else
1433
504
                    mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( maPathObj ) );
1434
2.19k
            }
1435
1436
4.70k
            if (maLatestFillStyle.aType == WinMtfFillStyleType::Hatch)
1437
342
                mpGDIMetaFile->AddAction( new MetaHatchAction( maPathObj, maLatestFillStyle.aHatch ) );
1438
1439
4.70k
            if ( !bStroke || bWindingFill )
1440
4.12k
                mpGDIMetaFile->AddAction( new MetaPopAction() );
1441
4.70k
        }
1442
        // tdf#142014 By default the stroke is made with hairline. If width is bigger, we need to use PolyLineAction
1443
7.19k
        if ( bStroke )
1444
4.17k
        {
1445
            // bFill is drawing hairstyle line. So we need to draw it only when the width is different than 0
1446
            // When using WINDING fill mode, line was disabled for the fill, so always draw stroke
1447
4.17k
            if ( !bFill || bWindingFill || maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1448
3.76k
            {
1449
3.76k
                sal_uInt16 i, nCount = maPathObj.Count();
1450
425k
                for ( i = 0; i < nCount; i++ )
1451
421k
                {
1452
421k
                    if (!ImplEmitLineHatch( maPathObj[ i ] ))
1453
404k
                        mpGDIMetaFile->AddAction( new MetaPolyLineAction( maPathObj[ i ], maLineStyle.aLineInfo ) );
1454
421k
                }
1455
3.76k
            }
1456
4.17k
        }
1457
7.19k
        ClearPath();
1458
7.19k
    }
1459
1460
    void MtfTools::DrawPixel( const Point& rSource, const Color& rColor )
1461
1.48k
    {
1462
1.48k
        mpGDIMetaFile->AddAction( new MetaPixelAction( ImplMap( rSource), rColor ) );
1463
1.48k
    }
1464
1465
    void MtfTools::MoveTo( const Point& rPoint, bool bRecordPath )
1466
14.6k
    {
1467
14.6k
        Point aDest( ImplMap( rPoint ) );
1468
14.6k
        if ( bRecordPath )
1469
10.0k
        {
1470
            // fdo#57353 create new subpath for subsequent moves
1471
10.0k
            if ( maPathObj.Count() )
1472
7.92k
                if ( maPathObj[ maPathObj.Count() - 1 ].GetSize() )
1473
4.75k
                    maPathObj.Insert( tools::Polygon() );
1474
10.0k
            maPathObj.AddPoint( aDest );
1475
10.0k
        }
1476
14.6k
        maActPos = aDest;
1477
14.6k
    }
1478
1479
    void MtfTools::LineTo( const Point& rPoint, bool bRecordPath )
1480
354k
    {
1481
354k
        UpdateClipRegion();
1482
354k
        Point aDest( ImplMap( rPoint ) );
1483
354k
        if ( bRecordPath )
1484
344k
            maPathObj.AddPoint( aDest );
1485
10.1k
        else
1486
10.1k
        {
1487
10.1k
            UpdateLineStyle();
1488
10.1k
            tools::Polygon aLinePoly(2);
1489
10.1k
            aLinePoly.SetPoint(maActPos, 0);
1490
10.1k
            aLinePoly.SetPoint(aDest, 1);
1491
10.1k
            if (!ImplEmitLineHatch(aLinePoly))
1492
9.77k
                mpGDIMetaFile->AddAction( new MetaLineAction( maActPos, aDest, maLineStyle.aLineInfo ) );
1493
10.1k
        }
1494
354k
        maActPos = aDest;
1495
354k
    }
1496
1497
    void MtfTools::DrawRectWithBGColor(const tools::Rectangle& rRect)
1498
6.97k
    {
1499
6.97k
        WinMtfFillStyle aFillStyleBackup = maFillStyle;
1500
6.97k
        bool            bTransparentBackup = maLineStyle.bTransparent;
1501
6.97k
        BackgroundMode  mnBkModeBackup = mnBkMode;
1502
1503
6.97k
        const tools::Polygon aPoly( rRect );
1504
6.97k
        maLineStyle.bTransparent = true;
1505
6.97k
        maFillStyle = maBkColor;
1506
6.97k
        mnBkMode = BackgroundMode::OPAQUE;
1507
6.97k
        ImplSetNonPersistentLineColorTransparenz();
1508
6.97k
        DrawPolygon(aPoly, false);
1509
6.97k
        mnBkMode = mnBkModeBackup; // The rectangle needs to be always drawned even if mode is transparent
1510
6.97k
        maFillStyle = std::move(aFillStyleBackup);
1511
6.97k
        maLineStyle.bTransparent = bTransparentBackup;
1512
6.97k
    }
1513
1514
    void MtfTools::DrawRect( const tools::Rectangle& rRect, bool bEdge )
1515
45.6k
    {
1516
45.6k
        UpdateClipRegion();
1517
45.6k
        UpdateFillStyle();
1518
1519
45.6k
        if ( mbComplexClip )
1520
0
        {
1521
0
            tools::Polygon aPoly( ImplMap( rRect ) );
1522
0
            tools::PolyPolygon aPolyPolyRect( aPoly );
1523
0
            tools::PolyPolygon aDest;
1524
0
            tools::PolyPolygon(maClipPath.getClipPath()).GetIntersection( aPolyPolyRect, aDest );
1525
0
            ImplDrawClippedPolyPolygon( aDest );
1526
0
        }
1527
45.6k
        else
1528
45.6k
        {
1529
45.6k
            const bool bPattern = (maLatestFillStyle.aType == WinMtfFillStyleType::Pattern);
1530
45.6k
            if ( bEdge )
1531
1.51k
            {
1532
1.51k
                if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1533
957
                {
1534
957
                    ImplSetNonPersistentLineColorTransparenz();
1535
957
                    if ( bPattern )
1536
18
                        ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( ImplMap( rRect ) ) ) );
1537
939
                    else
1538
939
                        mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) );
1539
957
                    UpdateLineStyle();
1540
957
                    tools::Polygon aEdgePoly( ImplMap( rRect ) );
1541
957
                    if (!ImplEmitLineHatch( aEdgePoly ))
1542
957
                        mpGDIMetaFile->AddAction( new MetaPolyLineAction( aEdgePoly, maLineStyle.aLineInfo ) );
1543
957
                }
1544
560
                else
1545
560
                {
1546
560
                    UpdateLineStyle();
1547
560
                    if ( bPattern )
1548
13
                        ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( ImplMap( rRect ) ) ) );
1549
547
                    else
1550
547
                        mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) );
1551
560
                }
1552
1.51k
            }
1553
44.1k
            else
1554
44.1k
            {
1555
44.1k
                ImplSetNonPersistentLineColorTransparenz();
1556
44.1k
                if ( bPattern )
1557
872
                    ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( ImplMap( rRect ) ) ) );
1558
43.2k
                else
1559
43.2k
                    mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) );
1560
44.1k
            }
1561
1562
45.6k
            if (maLatestFillStyle.aType == WinMtfFillStyleType::Hatch)
1563
2.17k
            {
1564
2.17k
                tools::PolyPolygon aPolyPoly( tools::Polygon( ImplMap( rRect ) ) );
1565
2.17k
                mpGDIMetaFile->AddAction( new MetaHatchAction( aPolyPoly, maLatestFillStyle.aHatch ) );
1566
2.17k
            }
1567
45.6k
        }
1568
45.6k
    }
1569
1570
    void MtfTools::DrawRoundRect( const tools::Rectangle& rRect, const Size& rSize )
1571
2.31k
    {
1572
2.31k
        UpdateClipRegion();
1573
2.31k
        UpdateLineStyle();
1574
2.31k
        UpdateFillStyle();
1575
2.31k
        if (maLatestFillStyle.aType == WinMtfFillStyleType::Pattern)
1576
373
        {
1577
373
            tools::Polygon aRoundRectPoly( rRect, rSize.Width(), rSize.Height() );
1578
373
            ImplMap( aRoundRectPoly );
1579
373
            ImplEmitPatternFill( tools::PolyPolygon( aRoundRectPoly ) );
1580
373
        }
1581
1.94k
        else
1582
1.94k
            mpGDIMetaFile->AddAction( new MetaRoundRectAction( ImplMap( rRect ), std::abs( ImplMap( rSize ).Width() ), std::abs( ImplMap( rSize ).Height() ) ) );
1583
        // tdf#142139 Wrong line width during WMF import
1584
2.31k
        if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1585
1.84k
        {
1586
1.84k
            tools::Polygon aRoundRectPoly( rRect, rSize.Width(), rSize.Height() );
1587
1.84k
            ImplMap( aRoundRectPoly );
1588
1.84k
            if (!ImplEmitLineHatch( aRoundRectPoly ))
1589
1.84k
                mpGDIMetaFile->AddAction( new MetaPolyLineAction( aRoundRectPoly, maLineStyle.aLineInfo ) );
1590
1.84k
        }
1591
2.31k
    }
1592
1593
    void MtfTools::DrawEllipse( const tools::Rectangle& rRect )
1594
3.64k
    {
1595
3.64k
        UpdateClipRegion();
1596
3.64k
        UpdateFillStyle();
1597
1598
3.64k
        const bool bPattern = (maLatestFillStyle.aType == WinMtfFillStyleType::Pattern);
1599
3.64k
        const Point aCenter( ImplMap( rRect.Center() ) );
1600
3.64k
        const Size  aRad( ImplMap( Size( rRect.GetWidth() / 2, rRect.GetHeight() / 2 ) ) );
1601
1602
3.64k
        if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1603
1.33k
        {
1604
1.33k
            ImplSetNonPersistentLineColorTransparenz();
1605
1.33k
            if ( bPattern )
1606
98
                ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( aCenter, aRad.Width(), aRad.Height() ) ) );
1607
1.23k
            else
1608
1.23k
                mpGDIMetaFile->AddAction( new MetaEllipseAction( ImplMap( rRect ) ) );
1609
1.33k
            UpdateLineStyle();
1610
1.33k
            tools::Polygon aEllipsePoly( aCenter, aRad.Width(), aRad.Height() );
1611
1.33k
            if (!ImplEmitLineHatch( aEllipsePoly ))
1612
1.33k
                mpGDIMetaFile->AddAction( new MetaPolyLineAction( aEllipsePoly, maLineStyle.aLineInfo ) );
1613
1.33k
        }
1614
2.31k
        else
1615
2.31k
        {
1616
2.31k
            UpdateLineStyle();
1617
2.31k
            if ( bPattern )
1618
44
                ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( aCenter, aRad.Width(), aRad.Height() ) ) );
1619
2.26k
            else
1620
2.26k
                mpGDIMetaFile->AddAction( new MetaEllipseAction( ImplMap( rRect ) ) );
1621
2.31k
        }
1622
3.64k
    }
1623
1624
    void MtfTools::DrawArc( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd, bool bTo )
1625
3.18k
    {
1626
3.18k
        UpdateClipRegion();
1627
3.18k
        UpdateLineStyle();
1628
3.18k
        UpdateFillStyle();
1629
1630
3.18k
        tools::Rectangle   aRect( ImplMap( rRect ) );
1631
3.18k
        Point       aStart( ImplMap( rStart ) );
1632
3.18k
        Point       aEnd( ImplMap( rEnd ) );
1633
1634
3.18k
        if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1635
2.39k
        {
1636
2.39k
            if ( aStart == aEnd )
1637
1.19k
            {   // SJ: #i53768# if start & end is identical, then we have to draw a full ellipse
1638
1.19k
                Point aCenter( aRect.Center() );
1639
1.19k
                Size  aRad( aRect.GetWidth() / 2, aRect.GetHeight() / 2 );
1640
1.19k
                tools::Polygon aArcPoly( aCenter, aRad.Width(), aRad.Height() );
1641
1.19k
                if (!ImplEmitLineHatch( aArcPoly ))
1642
1.19k
                    mpGDIMetaFile->AddAction( new MetaPolyLineAction( aArcPoly, maLineStyle.aLineInfo ) );
1643
1.19k
            }
1644
1.20k
            else
1645
1.20k
            {
1646
1.20k
                tools::Polygon aArcPoly( aRect, aStart, aEnd, PolyStyle::Arc );
1647
1.20k
                if (!ImplEmitLineHatch( aArcPoly ))
1648
1.20k
                    mpGDIMetaFile->AddAction( new MetaPolyLineAction( aArcPoly, maLineStyle.aLineInfo ) );
1649
1.20k
            }
1650
2.39k
        }
1651
788
        else
1652
788
            mpGDIMetaFile->AddAction( new MetaArcAction( aRect, aStart, aEnd ) );
1653
1654
3.18k
        if ( bTo )
1655
0
            maActPos = aEnd;
1656
3.18k
    }
1657
1658
    void MtfTools::DrawPie( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd )
1659
1.36k
    {
1660
1.36k
        UpdateClipRegion();
1661
1.36k
        UpdateFillStyle();
1662
1663
1.36k
        tools::Rectangle   aRect( ImplMap( rRect ) );
1664
1.36k
        Point       aStart( ImplMap( rStart ) );
1665
1.36k
        Point       aEnd( ImplMap( rEnd ) );
1666
1.36k
        const bool bPattern = (maLatestFillStyle.aType == WinMtfFillStyleType::Pattern);
1667
1668
1.36k
        if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1669
942
        {
1670
942
            ImplSetNonPersistentLineColorTransparenz();
1671
942
            if ( bPattern )
1672
36
                ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Pie ) ) );
1673
906
            else
1674
906
                mpGDIMetaFile->AddAction( new MetaPieAction( aRect, aStart, aEnd ) );
1675
942
            UpdateLineStyle();
1676
942
            {
1677
942
                tools::Polygon aPiePoly( aRect, aStart, aEnd, PolyStyle::Pie );
1678
942
                if (!ImplEmitLineHatch( aPiePoly ))
1679
942
                    mpGDIMetaFile->AddAction( new MetaPolyLineAction( aPiePoly, maLineStyle.aLineInfo ) );
1680
942
            }
1681
942
        }
1682
426
        else
1683
426
        {
1684
426
            UpdateLineStyle();
1685
426
            if ( bPattern )
1686
33
                ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Pie ) ) );
1687
393
            else
1688
393
                mpGDIMetaFile->AddAction( new MetaPieAction( aRect, aStart, aEnd ) );
1689
426
        }
1690
1.36k
    }
1691
1692
    void MtfTools::DrawChord( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd )
1693
839
    {
1694
839
        UpdateClipRegion();
1695
839
        UpdateFillStyle();
1696
1697
839
        tools::Rectangle   aRect( ImplMap( rRect ) );
1698
839
        Point       aStart( ImplMap( rStart ) );
1699
839
        Point       aEnd( ImplMap( rEnd ) );
1700
839
        const bool bPattern = (maLatestFillStyle.aType == WinMtfFillStyleType::Pattern);
1701
1702
839
        if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1703
384
        {
1704
384
            ImplSetNonPersistentLineColorTransparenz();
1705
384
            if ( bPattern )
1706
8
                ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Chord ) ) );
1707
376
            else
1708
376
                mpGDIMetaFile->AddAction( new MetaChordAction( aRect, aStart, aEnd ) );
1709
384
            UpdateLineStyle();
1710
384
            {
1711
384
                tools::Polygon aChordPoly( aRect, aStart, aEnd, PolyStyle::Chord );
1712
384
                if (!ImplEmitLineHatch( aChordPoly ))
1713
384
                    mpGDIMetaFile->AddAction( new MetaPolyLineAction( aChordPoly, maLineStyle.aLineInfo ) );
1714
384
            }
1715
384
        }
1716
455
        else
1717
455
        {
1718
455
            UpdateLineStyle();
1719
455
            if ( bPattern )
1720
9
                ImplEmitPatternFill( tools::PolyPolygon( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Chord ) ) );
1721
446
            else
1722
446
                mpGDIMetaFile->AddAction( new MetaChordAction( aRect, aStart, aEnd ) );
1723
455
        }
1724
839
    }
1725
1726
    void MtfTools::DrawPolygon( tools::Polygon rPolygon, bool bRecordPath )
1727
36.7k
    {
1728
36.7k
        UpdateClipRegion();
1729
36.7k
        ImplMap( rPolygon );
1730
36.7k
        if ( bRecordPath )
1731
8.43k
            maPathObj.AddPolygon( rPolygon );
1732
28.3k
        else
1733
28.3k
        {
1734
28.3k
            UpdateFillStyle();
1735
1736
28.3k
            if ( mbComplexClip )
1737
0
            {
1738
0
                tools::PolyPolygon aPolyPoly( rPolygon );
1739
0
                auto tmp = maClipPath.getClip();
1740
0
                tmp.intersectPolyPolygon(aPolyPoly.getB2DPolyPolygon());
1741
0
                tools::PolyPolygon aDest(tmp.getClipPoly());
1742
0
                ImplDrawClippedPolyPolygon( aDest );
1743
0
            }
1744
28.3k
            else
1745
28.3k
            {
1746
28.3k
                if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1747
4.68k
                {
1748
4.68k
                    sal_uInt16 nCount = rPolygon.GetSize();
1749
4.68k
                    if ( nCount )
1750
3.06k
                    {
1751
3.06k
                        if ( rPolygon[ nCount - 1 ] != rPolygon[ 0 ] )
1752
309
                        {
1753
309
                            Point aPoint( rPolygon[ 0 ] );
1754
309
                            rPolygon.Insert( nCount, aPoint );
1755
309
                        }
1756
3.06k
                    }
1757
4.68k
                    ImplSetNonPersistentLineColorTransparenz();
1758
4.68k
                    if (mnPolyFillMode == 2) // WINDING
1759
686
                    {
1760
686
                        tools::PolyPolygon aFill = CreateWindingFillPolyPolygon(tools::PolyPolygon(rPolygon));
1761
686
                        mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( aFill ) );
1762
686
                    }
1763
3.99k
                    else
1764
3.99k
                    {
1765
3.99k
                        mpGDIMetaFile->AddAction( new MetaPolygonAction( rPolygon ) );
1766
3.99k
                    }
1767
4.68k
                    if (maLatestFillStyle.aType == WinMtfFillStyleType::Hatch)
1768
510
                        mpGDIMetaFile->AddAction( new MetaHatchAction( tools::PolyPolygon( rPolygon ), maLatestFillStyle.aHatch ) );
1769
4.68k
                    UpdateLineStyle();
1770
4.68k
                    if (!ImplEmitLineHatch( rPolygon ))
1771
4.60k
                        mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) );
1772
4.68k
                }
1773
23.6k
                else
1774
23.6k
                {
1775
23.6k
                    UpdateLineStyle();
1776
1777
23.6k
                    if (maLatestFillStyle.aType == WinMtfFillStyleType::Hatch)
1778
238
                    {
1779
238
                        mpGDIMetaFile->AddAction( new MetaPolygonAction( rPolygon ) );
1780
238
                        mpGDIMetaFile->AddAction( new MetaHatchAction( tools::PolyPolygon( std::move(rPolygon) ), maLatestFillStyle.aHatch ) );
1781
238
                    }
1782
23.4k
                    else if (maLatestFillStyle.aType != WinMtfFillStyleType::Pattern)
1783
20.0k
                    {
1784
20.0k
                        if (mnPolyFillMode == 2) // WINDING
1785
2.84k
                        {
1786
2.84k
                            tools::PolyPolygon aFill = CreateWindingFillPolyPolygon(tools::PolyPolygon(rPolygon));
1787
2.84k
                            ImplSetNonPersistentLineColorTransparenz();
1788
2.84k
                            mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( aFill ) );
1789
2.84k
                            UpdateLineStyle();
1790
                            // Stroke with original polygon, closing it for the polyline
1791
2.84k
                            sal_uInt16 nCount = rPolygon.GetSize();
1792
2.84k
                            if (nCount && rPolygon[nCount - 1] != rPolygon[0])
1793
471
                            {
1794
471
                                Point aPoint(rPolygon[0]);
1795
471
                                rPolygon.Insert(nCount, aPoint);
1796
471
                            }
1797
2.84k
                            if (!ImplEmitLineHatch( rPolygon ))
1798
2.84k
                                mpGDIMetaFile->AddAction(new MetaPolyLineAction(rPolygon, maLineStyle.aLineInfo));
1799
2.84k
                        }
1800
17.2k
                        else
1801
17.2k
                            mpGDIMetaFile->AddAction( new MetaPolygonAction( std::move(rPolygon) ) );
1802
20.0k
                    }
1803
3.32k
                    else
1804
3.32k
                    {
1805
3.32k
                        ImplEmitPatternFill( tools::PolyPolygon( std::move(rPolygon) ) );
1806
3.32k
                    }
1807
1808
23.6k
                }
1809
28.3k
            }
1810
28.3k
        }
1811
36.7k
    }
1812
1813
    void MtfTools::DrawPolyPolygon( tools::PolyPolygon& rPolyPolygon, bool bRecordPath )
1814
5.98k
    {
1815
5.98k
        UpdateClipRegion();
1816
1817
5.98k
        ImplMap( rPolyPolygon );
1818
1819
5.98k
        if ( bRecordPath )
1820
218
            maPathObj.AddPolyPolygon( rPolyPolygon );
1821
5.76k
        else
1822
5.76k
        {
1823
5.76k
            UpdateFillStyle();
1824
1825
5.76k
            if ( mbComplexClip )
1826
0
            {
1827
0
                tools::PolyPolygon aDest;
1828
0
                tools::PolyPolygon(maClipPath.getClipPath()).GetIntersection( rPolyPolygon, aDest );
1829
0
                ImplDrawClippedPolyPolygon( aDest );
1830
0
            }
1831
5.76k
            else
1832
5.76k
            {
1833
5.76k
                UpdateLineStyle();
1834
5.76k
                if (mnPolyFillMode == 2) // WINDING
1835
1.16k
                {
1836
                    // Convert geometry so even-odd rendering matches winding fill.
1837
                    // Disable line to avoid stroking internal resolved edges.
1838
1.16k
                    tools::PolyPolygon aFill = CreateWindingFillPolyPolygon(rPolyPolygon);
1839
1.16k
                    mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::LINECOLOR ) );
1840
1.16k
                    mpGDIMetaFile->AddAction( new MetaLineColorAction( Color(), false ) );
1841
1.16k
                    mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( aFill ) );
1842
1.16k
                    mpGDIMetaFile->AddAction( new MetaPopAction() );
1843
1.16k
                    if (maLatestFillStyle.aType == WinMtfFillStyleType::Hatch)
1844
20
                        mpGDIMetaFile->AddAction( new MetaHatchAction( rPolyPolygon, maLatestFillStyle.aHatch ) );
1845
                    // Stroke with original geometry
1846
3.26k
                    for (sal_uInt16 nPoly = 0; nPoly < rPolyPolygon.Count(); ++nPoly)
1847
2.10k
                    {
1848
2.10k
                        if (!ImplEmitLineHatch( rPolyPolygon[nPoly] ))
1849
2.10k
                            mpGDIMetaFile->AddAction(new MetaPolyLineAction(rPolyPolygon[nPoly], maLineStyle.aLineInfo));
1850
2.10k
                    }
1851
1.16k
                }
1852
4.60k
                else
1853
4.60k
                {
1854
4.60k
                    if (maLatestFillStyle.aType == WinMtfFillStyleType::Pattern)
1855
1.20k
                        ImplEmitPatternFill( rPolyPolygon );
1856
3.39k
                    else
1857
3.39k
                        mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPolygon ) );
1858
4.60k
                    if (maLatestFillStyle.aType == WinMtfFillStyleType::Hatch)
1859
36
                        mpGDIMetaFile->AddAction( new MetaHatchAction( rPolyPolygon, maLatestFillStyle.aHatch ) );
1860
4.60k
                    if (maLineStyle.aLineInfo.GetWidth() > 0 || maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash)
1861
1.05k
                    {
1862
2.18k
                        for (sal_uInt16 nPoly = 0; nPoly < rPolyPolygon.Count(); ++nPoly)
1863
1.13k
                        {
1864
1.13k
                            if (!ImplEmitLineHatch( rPolyPolygon[nPoly] ))
1865
862
                                mpGDIMetaFile->AddAction(new MetaPolyLineAction(rPolyPolygon[nPoly], maLineStyle.aLineInfo));
1866
1.13k
                        }
1867
1.05k
                    }
1868
4.60k
                }
1869
5.76k
            }
1870
5.76k
        }
1871
5.98k
    }
1872
1873
    void MtfTools::DrawPolyLine( tools::Polygon rPolygon, bool bTo, bool bRecordPath )
1874
18.5k
    {
1875
18.5k
        UpdateClipRegion();
1876
1877
18.5k
        sal_uInt16 nPoints = rPolygon.GetSize();
1878
18.5k
        if (nPoints < 1)
1879
9.99k
            return;
1880
1881
8.56k
        ImplMap( rPolygon );
1882
8.56k
        if ( bTo )
1883
4.36k
        {
1884
4.36k
            rPolygon[ 0 ] = maActPos;
1885
4.36k
            maActPos = rPolygon[ rPolygon.GetSize() - 1 ];
1886
4.36k
        }
1887
8.56k
        if ( bRecordPath )
1888
1.69k
            maPathObj.AddPolyLine( rPolygon );
1889
6.87k
        else
1890
6.87k
        {
1891
6.87k
            UpdateLineStyle();
1892
6.87k
            if (!ImplEmitLineHatch( rPolygon ))
1893
6.25k
                mpGDIMetaFile->AddAction( new MetaPolyLineAction( std::move(rPolygon), maLineStyle.aLineInfo ) );
1894
6.87k
        }
1895
8.56k
    }
1896
1897
    static bool AllowDim(tools::Long nDim)
1898
18.5k
    {
1899
18.5k
        static bool bFuzzing = comphelper::IsFuzzing();
1900
18.5k
        if (bFuzzing)
1901
18.5k
        {
1902
18.5k
            if (nDim > 0x20000000 || nDim < -0x20000000)
1903
3.19k
            {
1904
3.19k
                SAL_WARN("vcl", "skipping huge dimension: " << nDim);
1905
3.19k
                return false;
1906
3.19k
            }
1907
18.5k
        }
1908
15.3k
        return true;
1909
18.5k
    }
1910
1911
    static bool AllowPoint(const Point& rPoint)
1912
10.5k
    {
1913
10.5k
        return AllowDim(rPoint.X()) && AllowDim(rPoint.Y());
1914
10.5k
    }
1915
1916
    void MtfTools::DrawPolyBezier( tools::Polygon rPolygon, bool bTo, bool bRecordPath )
1917
99.8k
    {
1918
99.8k
        sal_uInt16 nPoints = rPolygon.GetSize();
1919
99.8k
        if ( ( nPoints < 4 ) || ( ( ( nPoints - 4 ) % 3 ) != 0 ) )
1920
5.31k
        {
1921
5.31k
            SAL_WARN("emfio",
1922
5.31k
                "EMF file error: Number of Bezier points is not set of three");
1923
5.31k
            return;
1924
5.31k
        }
1925
94.5k
        UpdateClipRegion();
1926
94.5k
        ImplMap( rPolygon );
1927
94.5k
        if ( bTo )
1928
94.4k
        {
1929
94.4k
            rPolygon[ 0 ] = maActPos;
1930
94.4k
            maActPos = rPolygon[ nPoints - 1 ];
1931
94.4k
        }
1932
94.5k
        sal_uInt16 i;
1933
191k
        for ( i = 0; ( i + 2 ) < nPoints; )
1934
97.3k
        {
1935
97.3k
            rPolygon.SetFlags( i++, PolyFlags::Normal );
1936
97.3k
            rPolygon.SetFlags( i++, PolyFlags::Control );
1937
97.3k
            rPolygon.SetFlags( i++, PolyFlags::Control );
1938
97.3k
        }
1939
94.5k
        if ( bRecordPath )
1940
94.2k
            maPathObj.AddPolyLine( rPolygon );
1941
280
        else
1942
280
        {
1943
280
            UpdateLineStyle();
1944
280
            if (!ImplEmitLineHatch( rPolygon ))
1945
237
                mpGDIMetaFile->AddAction( new MetaPolyLineAction( std::move(rPolygon), maLineStyle.aLineInfo ) );
1946
280
        }
1947
94.5k
    }
1948
1949
    void MtfTools::DrawText(Point& rPosition, OUString const & rText, KernArray* pDXArry, tools::Long* pDYArry, const float fXScale, const float fYScale, bool bRecordPath, GraphicsMode nGfxMode)
1950
46.7k
    {
1951
46.7k
        UpdateClipRegion();
1952
46.7k
        rPosition = ImplMap( rPosition );
1953
46.7k
        GraphicsMode nOldGfxMode = GetGfxMode();
1954
46.7k
        SetGfxMode( GraphicsMode::GM_COMPATIBLE );
1955
1956
46.7k
        if (pDXArry)
1957
12.4k
        {
1958
12.4k
            sal_Int64 nSumX = 0, nSumY = 0;
1959
54.9k
            for (sal_Int32 i = 0; i < rText.getLength(); i++ )
1960
42.4k
            {
1961
42.4k
                nSumX += (*pDXArry)[i];
1962
1963
                // #i121382# Map DXArray using WorldTransform
1964
42.4k
                const Size aSizeX(ImplMap(Size(nSumX, 0)));
1965
42.4k
                const basegfx::B2DVector aVectorX(aSizeX.Width(), aSizeX.Height());
1966
42.4k
                (*pDXArry)[i] = aVectorX.getLength() * (nSumX >= 0 ? 1 : -1);
1967
1968
42.4k
                if (pDYArry)
1969
10.5k
                {
1970
10.5k
                    nSumY += pDYArry[i];
1971
1972
10.5k
                    const Size aSizeY(ImplMap(Size(0, nSumY)));
1973
10.5k
                    const basegfx::B2DVector aVectorY(aSizeY.Width(), aSizeY.Height());
1974
                    // Reverse Y
1975
10.5k
                    pDYArry[i] = basegfx::fround<tools::Long>(aVectorY.getLength());
1976
10.5k
                    pDYArry[i] *= (nSumY >= 0 ? -1 : 1);
1977
10.5k
                }
1978
42.4k
            }
1979
12.4k
        }
1980
46.7k
        if ( mnLatestTextLayoutMode != mnTextLayoutMode )
1981
5.68k
        {
1982
5.68k
            mnLatestTextLayoutMode = mnTextLayoutMode;
1983
5.68k
            mpGDIMetaFile->AddAction( new MetaLayoutModeAction( mnTextLayoutMode ) );
1984
5.68k
        }
1985
46.7k
        SetGfxMode(nGfxMode);
1986
46.7k
        TextAlign eTextAlign;
1987
46.7k
        if (mnTextAlign & TA_BASELINE)
1988
21.9k
            eTextAlign = ALIGN_BASELINE;
1989
24.7k
        else if (mnTextAlign & TA_BOTTOM)
1990
695
            eTextAlign = ALIGN_BOTTOM;
1991
24.0k
        else
1992
24.0k
            eTextAlign = ALIGN_TOP;
1993
46.7k
        bool bChangeFont = false;
1994
46.7k
        if ( mnLatestTextAlign != mnTextAlign )
1995
5.36k
        {
1996
5.36k
            bChangeFont = true;
1997
1998
5.36k
            if ((mnLatestTextAlign & TA_RTLREADING) != (mnTextAlign & TA_RTLREADING))
1999
535
            {
2000
535
                auto nFlags = vcl::text::ComplexTextLayoutFlags::Default;
2001
535
                if (mnTextAlign & TA_RTLREADING)
2002
302
                {
2003
302
                    nFlags = vcl::text::ComplexTextLayoutFlags::BiDiRtl
2004
302
                             | vcl::text::ComplexTextLayoutFlags::TextOriginLeft;
2005
302
                }
2006
2007
535
                mpGDIMetaFile->AddAction(new MetaLayoutModeAction(nFlags));
2008
535
            }
2009
2010
5.36k
            mnLatestTextAlign = mnTextAlign;
2011
5.36k
            mpGDIMetaFile->AddAction( new MetaTextAlignAction( eTextAlign ) );
2012
5.36k
        }
2013
46.7k
        if ( maLatestTextColor != maTextColor )
2014
590
        {
2015
590
            bChangeFont = true;
2016
590
            maLatestTextColor = maTextColor;
2017
590
            mpGDIMetaFile->AddAction( new MetaTextColorAction( maTextColor ) );
2018
590
        }
2019
46.7k
        bool bChangeFillColor = false;
2020
46.7k
        if ( maLatestBkColor != maBkColor )
2021
5.29k
        {
2022
5.29k
            bChangeFillColor = true;
2023
5.29k
            maLatestBkColor = maBkColor;
2024
5.29k
        }
2025
46.7k
        if ( mnLatestBkMode != mnBkMode )
2026
7.54k
        {
2027
7.54k
            bChangeFillColor = true;
2028
7.54k
            mnLatestBkMode = mnBkMode;
2029
7.54k
        }
2030
46.7k
        if ( bChangeFillColor )
2031
8.20k
        {
2032
8.20k
            bChangeFont = true;
2033
8.20k
            mpGDIMetaFile->AddAction( new MetaTextFillColorAction( maFont.GetFillColor(), !maFont.IsTransparent() ) );
2034
8.20k
        }
2035
        // Create a local copy of the current font to apply transient modifications
2036
        // (such as color, alignment, and custom scaling) before recording it to the metafile.
2037
46.7k
        vcl::Font aTmp( maFont );
2038
46.7k
        aTmp.SetColor( maTextColor );
2039
2040
46.7k
        if( mnBkMode == BackgroundMode::Transparent )
2041
22.2k
        {
2042
22.2k
            aTmp.SetFillColor( COL_TRANSPARENT );
2043
22.2k
            aTmp.SetTransparent( true );
2044
22.2k
        }
2045
24.5k
        else
2046
24.5k
        {
2047
24.5k
            aTmp.SetFillColor( maBkColor );
2048
24.5k
            aTmp.SetTransparent( false );
2049
24.5k
        }
2050
2051
46.7k
        aTmp.SetAlignment( eTextAlign );
2052
2053
46.7k
        if ( nGfxMode == GraphicsMode::GM_ADVANCED )
2054
637
        {
2055
            // check whether there is a font rotation applied via transformation
2056
637
            Point aP1( ImplMap( Point() ) );
2057
637
            Point aP2( ImplMap( Point( 0, 100 ) ) );
2058
637
            aP2.setX(o3tl::saturating_sub(aP2.X(), aP1.X()));
2059
637
            aP2.setY(o3tl::saturating_sub(aP2.Y(), aP1.Y()));
2060
637
            double fX = aP2.X();
2061
637
            double fY = aP2.Y();
2062
637
            if ( fX )
2063
448
            {
2064
448
                double fOrientation = basegfx::rad2deg(acos(fX / std::hypot(fX, fY)));
2065
448
                if ( fY > 0 )
2066
28
                    fOrientation = 360 - fOrientation;
2067
448
                fOrientation += 90;
2068
448
                fOrientation *= 10;
2069
448
                aTmp.SetOrientation( aTmp.GetOrientation() + Degree10( static_cast<sal_Int16>(fOrientation) ) );
2070
448
            }
2071
637
        }
2072
46.1k
        else if (nGfxMode == GraphicsMode::GM_COMPATIBLE)
2073
35.9k
        {
2074
35.9k
            if (fXScale != 0.0)
2075
35.4k
            {
2076
                // As we changing only font width, we are skipping if scales have the same values
2077
35.4k
                const bool bNeedsWidthScale = (std::fabs(fYScale) != std::fabs(fXScale));
2078
35.4k
                if (bNeedsWidthScale)
2079
1.37k
                {
2080
1.37k
                    Size aFontSize = aTmp.GetFontSize();
2081
1.37k
                    const float fTestWidthScale = std::fabs(fYScale / fXScale);
2082
2083
                    // If Width is 0, the font is scaled proportionally based on Height.
2084
1.37k
                    if (aFontSize.Width() == 0)
2085
1.26k
                        aFontSize.setWidth(basegfx::fround<tools::Long>(aFontSize.Height() * fTestWidthScale));
2086
112
                    else
2087
112
                        aFontSize.setWidth(basegfx::fround<tools::Long>(aFontSize.Width() * fTestWidthScale));
2088
1.37k
                    aTmp.SetFontSize(aFontSize);
2089
1.37k
                    bChangeFont = true;
2090
1.37k
                }
2091
35.4k
            }
2092
2093
35.9k
            if ((fYScale < 0.0) && (fXScale < 0.0))
2094
78
            {
2095
                // Both scales negative = 180 degree rotation. vcl::Font handles this perfectly.
2096
78
                aTmp.SetOrientation(aTmp.GetOrientation() + Degree10(1800));
2097
78
            }
2098
35.8k
            else if ((fYScale < 0.0) || (fXScale < 0.0))
2099
48
            {
2100
                // Single-axis mirroring.
2101
                // In GM_COMPATIBLE, text glyphs are NOT physically mirrored.
2102
                // However, the flipped coordinate system reverses the rotation direction.
2103
                // Inverting the angle (360 degrees - current angle) fixes the text direction.
2104
48
                aTmp.SetOrientation(Degree10(3600) - aTmp.GetOrientation());
2105
2106
                // TODO: True single-axis glyph mirroring would require a MapMode transform here
2107
48
            }
2108
35.9k
        }
2109
2110
46.7k
        if( mnTextAlign & ( TA_UPDATECP | TA_RIGHT_CENTER ) )
2111
2.18k
        {
2112
            // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
2113
2.18k
            SolarMutexGuard aGuard;
2114
2.18k
            ScopedVclPtrInstance< VirtualDevice > pVDev;
2115
2.18k
            sal_Int32 nTextWidth;
2116
2.18k
            Point aActPosDelta;
2117
2.18k
            pVDev->SetMapMode( MapMode( MapUnit::Map100thMM ) );
2118
2.18k
            pVDev->SetFont( maFont );
2119
2.18k
            const sal_uInt32 nLen = pDXArry ? rText.getLength() : 0;
2120
2.18k
            if (nLen)
2121
643
            {
2122
643
                nTextWidth = pVDev->GetTextWidth( OUString(rText[ nLen - 1 ]) );
2123
643
                if( nLen > 1 )
2124
150
                    nTextWidth += (*pDXArry)[ nLen - 2 ];
2125
                // tdf#39894: We should consider the distance to next character cell origin
2126
643
                aActPosDelta.setX( (*pDXArry)[ nLen - 1 ] );
2127
643
                if ( pDYArry )
2128
138
                {
2129
138
                    aActPosDelta.setY( pDYArry[ nLen - 1 ] );
2130
138
                }
2131
643
            }
2132
1.54k
            else
2133
1.54k
            {
2134
1.54k
                nTextWidth = pVDev->GetTextWidth( rText );
2135
1.54k
                aActPosDelta.setX( nTextWidth );
2136
1.54k
            }
2137
2138
2.18k
            if( mnTextAlign & TA_UPDATECP )
2139
1.17k
                rPosition = maActPos;
2140
2141
2.18k
            if (mnTextAlign & TA_RIGHT_CENTER)
2142
1.08k
            {
2143
1.08k
                Point aDisplacement(((mnTextAlign & TA_RIGHT_CENTER) == TA_CENTER) ? nTextWidth >> 1: nTextWidth, 0);
2144
1.08k
                Point().RotateAround(aDisplacement, maFont.GetOrientation());
2145
1.08k
                rPosition -= aDisplacement;
2146
1.08k
            }
2147
2148
2.18k
            if( mnTextAlign & TA_UPDATECP )
2149
1.17k
            {
2150
1.17k
                Point().RotateAround(aActPosDelta, maFont.GetOrientation());
2151
1.17k
                maActPos = rPosition + aActPosDelta;
2152
1.17k
            }
2153
2.18k
        }
2154
2155
46.7k
        if(bChangeFont || (maLatestFont != aTmp))
2156
21.4k
        {
2157
21.4k
            maLatestFont = aTmp;
2158
21.4k
            rtl::Reference<MetaFontAction> aNewMetaFontAction(new MetaFontAction(aTmp));
2159
2160
            // tdf#127471 end evtl active MetaFontAction scale corrector detector/collector
2161
21.4k
            maScaledFontHelper.endCurrentMetaFontAction();
2162
2163
            // !bRecordPath: else no MetaTextArrayAction will be created
2164
            // nullptr != pDXArry: detection only possible when text size is given
2165
            // rText.getLength(): no useful check without text
2166
21.4k
            if(!bRecordPath && nullptr != pDXArry && 0 != rText.getLength())
2167
9.10k
            {
2168
9.10k
                maScaledFontHelper.newCurrentMetaFontAction(aNewMetaFontAction);
2169
9.10k
            }
2170
2171
21.4k
            mpGDIMetaFile->AddAction( aNewMetaFontAction );
2172
21.4k
            mpGDIMetaFile->AddAction( new MetaTextAlignAction( aTmp.GetAlignment() ) );
2173
21.4k
            mpGDIMetaFile->AddAction( new MetaTextColorAction( aTmp.GetColor() ) );
2174
21.4k
            mpGDIMetaFile->AddAction( new MetaTextFillColorAction( aTmp.GetFillColor(), !aTmp.IsTransparent() ) );
2175
21.4k
        }
2176
2177
46.7k
        if ( bRecordPath )
2178
8.53k
        {
2179
            // TODO
2180
8.53k
        }
2181
38.2k
        else
2182
38.2k
        {
2183
38.2k
            if ( pDXArry && pDYArry )
2184
4.11k
            {
2185
14.6k
                for (sal_Int32 i = 0; i < rText.getLength(); ++i)
2186
10.5k
                {
2187
10.5k
                    Point aCharDisplacement( i ? (*pDXArry)[i-1] : 0, i ? pDYArry[i-1] : 0 );
2188
10.5k
                    if (AllowPoint(aCharDisplacement))
2189
7.32k
                    {
2190
7.32k
                        Point().RotateAround(aCharDisplacement, maFont.GetOrientation());
2191
7.32k
                        mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition + aCharDisplacement, OUString( rText[i] ), KernArraySpan(), {}, 0, 1 ) );
2192
7.32k
                    }
2193
10.5k
                }
2194
4.11k
            }
2195
34.0k
            else
2196
34.0k
            {
2197
                /* because text without dx array is badly scaled, we
2198
                   will create such an array if necessary */
2199
34.0k
                KernArraySpan pDX;
2200
34.0k
                KernArray aMyDXArray;
2201
34.0k
                if (pDXArry)
2202
8.36k
                {
2203
8.36k
                    pDX = *pDXArry;
2204
                    // only useful when we have an imported DXArray
2205
8.36k
                    if(!rText.isEmpty())
2206
8.36k
                    {
2207
8.36k
                        maScaledFontHelper.evaluateAlternativeFontScale(
2208
8.36k
                            rText,
2209
8.36k
                            (*pDXArry)[rText.getLength() - 1] // extract imported TextLength
2210
8.36k
                        );
2211
8.36k
                    }
2212
8.36k
                }
2213
25.7k
                else
2214
25.7k
                {
2215
                    // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
2216
25.7k
                    SolarMutexGuard aGuard;
2217
25.7k
                    ScopedVclPtrInstance< VirtualDevice > pVDev;
2218
25.7k
                    pVDev->SetMapMode(MapMode(MapUnit::Map100thMM));
2219
25.7k
                    pVDev->SetFont( maLatestFont );
2220
25.7k
                    pVDev->GetTextArray( rText, &aMyDXArray, 0, rText.getLength());
2221
25.7k
                    pDX = aMyDXArray;
2222
25.7k
                }
2223
34.0k
                mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition, rText, pDX, {}, 0, rText.getLength() ) );
2224
34.0k
            }
2225
38.2k
        }
2226
46.7k
        SetGfxMode( nOldGfxMode );
2227
46.7k
    }
2228
2229
    bool MtfTools::DrawGlyphs( Point& rPosition, const std::vector<sal_uInt16>& rGlyphIds,
2230
                               KernArray* pDXArry, tools::Long* pDYArry,
2231
                               const float fXScale, const float fYScale,
2232
                               bool bRecordPath, GraphicsMode nGfxMode )
2233
3.17k
    {
2234
3.17k
        if (rGlyphIds.empty())
2235
906
            return true;
2236
2237
        // Check that the required font is actually available — glyph indices
2238
        // are font-specific and meaningless if a different font is substituted.
2239
2.26k
        {
2240
2.26k
            SolarMutexGuard aGuard;
2241
2.26k
            ScopedVclPtrInstance< VirtualDevice > pVDev;
2242
2.26k
            if ( !pVDev->IsFontAvailable( maFont.GetFamilyName() ) )
2243
1.17k
            {
2244
1.17k
                SAL_WARN("emfio", "ETO_GLYPH_INDEX: font '"
2245
1.17k
                                  << maFont.GetFamilyName()
2246
1.17k
                                  << "' not available, falling back to text path, which will be a "
2247
1.17k
                                     "garbled nonsense");
2248
1.17k
                return false;
2249
1.17k
            }
2250
2.26k
        }
2251
2252
1.09k
        UpdateClipRegion();
2253
1.09k
        rPosition = ImplMap( rPosition );
2254
1.09k
        GraphicsMode nOldGfxMode = GetGfxMode();
2255
1.09k
        SetGfxMode( GraphicsMode::GM_COMPATIBLE );
2256
2257
        // Map DX/DY arrays (same as DrawText)
2258
1.09k
        sal_Int32 nLen = static_cast<sal_Int32>(rGlyphIds.size());
2259
1.09k
        if (pDXArry)
2260
104
        {
2261
104
            sal_Int64 nSumX = 0, nSumY = 0;
2262
1.94k
            for (sal_Int32 i = 0; i < nLen; i++)
2263
1.84k
            {
2264
1.84k
                nSumX += (*pDXArry)[i];
2265
1.84k
                const Size aSizeX(ImplMap(Size(nSumX, 0)));
2266
1.84k
                const basegfx::B2DVector aVectorX(aSizeX.Width(), aSizeX.Height());
2267
1.84k
                (*pDXArry)[i] = aVectorX.getLength() * (nSumX >= 0 ? 1 : -1);
2268
2269
1.84k
                if (pDYArry)
2270
177
                {
2271
177
                    nSumY += pDYArry[i];
2272
177
                    const Size aSizeY(ImplMap(Size(0, nSumY)));
2273
177
                    const basegfx::B2DVector aVectorY(aSizeY.Width(), aSizeY.Height());
2274
177
                    pDYArry[i] = basegfx::fround<tools::Long>(aVectorY.getLength());
2275
177
                    pDYArry[i] *= (nSumY >= 0 ? -1 : 1);
2276
177
                }
2277
1.84k
            }
2278
104
        }
2279
2280
        // Text layout mode (same as DrawText)
2281
1.09k
        if ( mnLatestTextLayoutMode != mnTextLayoutMode )
2282
120
        {
2283
120
            mnLatestTextLayoutMode = mnTextLayoutMode;
2284
120
            mpGDIMetaFile->AddAction( new MetaLayoutModeAction( mnTextLayoutMode ) );
2285
120
        }
2286
1.09k
        SetGfxMode(nGfxMode);
2287
2288
        // Text alignment (same as DrawText)
2289
1.09k
        TextAlign eTextAlign;
2290
1.09k
        if (mnTextAlign & TA_BASELINE)
2291
995
            eTextAlign = ALIGN_BASELINE;
2292
97
        else if (mnTextAlign & TA_BOTTOM)
2293
7
            eTextAlign = ALIGN_BOTTOM;
2294
90
        else
2295
90
            eTextAlign = ALIGN_TOP;
2296
2297
1.09k
        bool bChangeFont = false;
2298
1.09k
        if ( mnLatestTextAlign != mnTextAlign )
2299
390
        {
2300
390
            bChangeFont = true;
2301
390
            mnLatestTextAlign = mnTextAlign;
2302
390
            mpGDIMetaFile->AddAction( new MetaTextAlignAction( eTextAlign ) );
2303
390
        }
2304
1.09k
        if ( maLatestTextColor != maTextColor )
2305
404
        {
2306
404
            bChangeFont = true;
2307
404
            maLatestTextColor = maTextColor;
2308
404
            mpGDIMetaFile->AddAction( new MetaTextColorAction( maTextColor ) );
2309
404
        }
2310
1.09k
        bool bChangeFillColor = false;
2311
1.09k
        if ( maLatestBkColor != maBkColor )
2312
356
        {
2313
356
            bChangeFillColor = true;
2314
356
            maLatestBkColor = maBkColor;
2315
356
        }
2316
1.09k
        if ( mnLatestBkMode != mnBkMode )
2317
398
        {
2318
398
            bChangeFillColor = true;
2319
398
            mnLatestBkMode = mnBkMode;
2320
398
        }
2321
1.09k
        if ( bChangeFillColor )
2322
398
        {
2323
398
            bChangeFont = true;
2324
398
            mpGDIMetaFile->AddAction( new MetaTextFillColorAction( maFont.GetFillColor(), !maFont.IsTransparent() ) );
2325
398
        }
2326
1.09k
        vcl::Font aTmp( maFont );
2327
1.09k
        aTmp.SetColor( maTextColor );
2328
2329
1.09k
        if( mnBkMode == BackgroundMode::Transparent )
2330
140
        {
2331
140
            aTmp.SetFillColor( COL_TRANSPARENT );
2332
140
            aTmp.SetTransparent( true );
2333
140
        }
2334
952
        else
2335
952
        {
2336
952
            aTmp.SetFillColor( maBkColor );
2337
952
            aTmp.SetTransparent( false );
2338
952
        }
2339
2340
1.09k
        aTmp.SetAlignment( eTextAlign );
2341
2342
1.09k
        if ( nGfxMode == GraphicsMode::GM_ADVANCED )
2343
1
        {
2344
1
            Point aP1( ImplMap( Point() ) );
2345
1
            Point aP2( ImplMap( Point( 0, 100 ) ) );
2346
1
            aP2.setX(o3tl::saturating_sub(aP2.X(), aP1.X()));
2347
1
            aP2.setY(o3tl::saturating_sub(aP2.Y(), aP1.Y()));
2348
1
            double fX = aP2.X();
2349
1
            double fY = aP2.Y();
2350
1
            if ( fX )
2351
0
            {
2352
0
                double fOrientation = basegfx::rad2deg(acos(fX / std::hypot(fX, fY)));
2353
0
                if ( fY > 0 )
2354
0
                    fOrientation = 360 - fOrientation;
2355
0
                fOrientation += 90;
2356
0
                fOrientation *= 10;
2357
0
                aTmp.SetOrientation( aTmp.GetOrientation() + Degree10( static_cast<sal_Int16>(fOrientation) ) );
2358
0
            }
2359
1
        }
2360
1.09k
        else if (nGfxMode == GraphicsMode::GM_COMPATIBLE)
2361
3
        {
2362
3
            if (fXScale != 0.0)
2363
2
            {
2364
2
                const bool bNeedsWidthScale = (std::fabs(fYScale) != std::fabs(fXScale));
2365
2
                if (bNeedsWidthScale)
2366
2
                {
2367
2
                    Size aFontSize = aTmp.GetFontSize();
2368
2
                    const float fTestWidthScale = std::fabs(fYScale / fXScale);
2369
2370
2
                    if (aFontSize.Width() == 0)
2371
2
                        aFontSize.setWidth(basegfx::fround<tools::Long>(aFontSize.Height() * fTestWidthScale));
2372
0
                    else
2373
0
                        aFontSize.setWidth(basegfx::fround<tools::Long>(aFontSize.Width() * fTestWidthScale));
2374
2
                    aTmp.SetFontSize(aFontSize);
2375
2
                    bChangeFont = true;
2376
2
                }
2377
2
            }
2378
2379
3
            if ((fYScale < 0.0) && (fXScale < 0.0))
2380
0
            {
2381
0
                aTmp.SetOrientation(aTmp.GetOrientation() + Degree10(1800));
2382
0
            }
2383
3
            else if ((fYScale < 0.0) || (fXScale < 0.0))
2384
0
            {
2385
0
                aTmp.SetOrientation(Degree10(3600) - aTmp.GetOrientation());
2386
0
            }
2387
3
        }
2388
2389
1.09k
        if( mnTextAlign & ( TA_UPDATECP | TA_RIGHT_CENTER ) )
2390
1.01k
        {
2391
1.01k
            SolarMutexGuard aGuard;
2392
1.01k
            ScopedVclPtrInstance< VirtualDevice > pVDev;
2393
1.01k
            sal_Int32 nTextWidth;
2394
1.01k
            Point aActPosDelta;
2395
1.01k
            pVDev->SetMapMode( MapMode( MapUnit::Map100thMM ) );
2396
1.01k
            pVDev->SetFont( maFont );
2397
1.01k
            if (nLen && pDXArry)
2398
53
            {
2399
                // Approximate width of last glyph
2400
53
                nTextWidth = pVDev->GetTextWidth( u"M"_ustr );
2401
53
                if( nLen > 1 )
2402
51
                    nTextWidth += (*pDXArry)[ nLen - 2 ];
2403
53
                aActPosDelta.setX( (*pDXArry)[ nLen - 1 ] );
2404
53
                if ( pDYArry )
2405
20
                    aActPosDelta.setY( pDYArry[ nLen - 1 ] );
2406
53
            }
2407
963
            else
2408
963
            {
2409
963
                nTextWidth = 0;
2410
963
                aActPosDelta.setX( nTextWidth );
2411
963
            }
2412
2413
1.01k
            if( mnTextAlign & TA_UPDATECP )
2414
1.00k
                rPosition = maActPos;
2415
2416
1.01k
            if (mnTextAlign & TA_RIGHT_CENTER)
2417
20
            {
2418
20
                Point aDisplacement(((mnTextAlign & TA_RIGHT_CENTER) == TA_CENTER) ? nTextWidth >> 1: nTextWidth, 0);
2419
20
                Point().RotateAround(aDisplacement, maFont.GetOrientation());
2420
20
                rPosition -= aDisplacement;
2421
20
            }
2422
2423
1.01k
            if( mnTextAlign & TA_UPDATECP )
2424
1.00k
            {
2425
1.00k
                Point().RotateAround(aActPosDelta, maFont.GetOrientation());
2426
1.00k
                maActPos = rPosition + aActPosDelta;
2427
1.00k
            }
2428
1.01k
        }
2429
2430
1.09k
        if(bChangeFont || (maLatestFont != aTmp))
2431
769
        {
2432
769
            maLatestFont = aTmp;
2433
769
            rtl::Reference<MetaFontAction> aNewMetaFontAction(new MetaFontAction(aTmp));
2434
769
            maScaledFontHelper.endCurrentMetaFontAction();
2435
769
            mpGDIMetaFile->AddAction( aNewMetaFontAction );
2436
769
            mpGDIMetaFile->AddAction( new MetaTextAlignAction( aTmp.GetAlignment() ) );
2437
769
            mpGDIMetaFile->AddAction( new MetaTextColorAction( aTmp.GetColor() ) );
2438
769
            mpGDIMetaFile->AddAction( new MetaTextFillColorAction( aTmp.GetFillColor(), !aTmp.IsTransparent() ) );
2439
769
        }
2440
2441
        // Convert glyph indices to polypolygon outlines
2442
1.09k
        if ( !bRecordPath )
2443
1.07k
        {
2444
1.07k
            SolarMutexGuard aGuard;
2445
1.07k
            ScopedVclPtrInstance< VirtualDevice > pVDev;
2446
1.07k
            pVDev->SetMapMode( MapMode( MapUnit::Map100thMM ) );
2447
1.07k
            pVDev->SetFont( maLatestFont );
2448
2449
            // GetGlyphOutlines() returns outlines in the OutputDevice's
2450
            // logical coordinates; since the VirtualDevice is set to
2451
            // Map100thMM above, outlines come back in 100th-mm — no
2452
            // additional scaling is needed.
2453
1.07k
            std::vector<sal_uInt32> aGlyphIds32(rGlyphIds.begin(), rGlyphIds.end());
2454
1.07k
            basegfx::B2DPolyPolygonVector aOutlines;
2455
1.07k
            pVDev->GetGlyphOutlines(aGlyphIds32.data(), nLen, aOutlines);
2456
2457
            // Emit each glyph as a separate filled PolyPolygon. We must NOT
2458
            // combine all glyphs into a single PolyPolygon because the even-odd
2459
            // fill rule would cancel out overlapping regions where Arabic
2460
            // connected glyphs overlap, leaving visible white gaps.
2461
1.07k
            bool bHasGlyphs = false;
2462
239k
            for (sal_Int32 i = 0; i < nLen && i < static_cast<sal_Int32>(aOutlines.size()); ++i)
2463
238k
            {
2464
238k
                basegfx::B2DPolyPolygon aGlyph = aOutlines[i];
2465
238k
                if (aGlyph.count() == 0)
2466
173k
                    continue;
2467
2468
                // Position glyph: glyph 0 at origin, glyph i at DX[i-1]
2469
64.6k
                double fDX = 0, fDY = 0;
2470
64.6k
                if (pDXArry && i > 0)
2471
1.60k
                    fDX = (*pDXArry)[i - 1];
2472
64.6k
                if (pDYArry && i > 0)
2473
118
                    fDY = pDYArry[i - 1];
2474
2475
64.6k
                basegfx::B2DHomMatrix aMat;
2476
64.6k
                if (fDX != 0 || fDY != 0)
2477
810
                    aMat.translate(fDX, fDY);
2478
2479
                // Translate to text position
2480
64.6k
                aMat.translate(rPosition.X(), rPosition.Y());
2481
64.6k
                aGlyph.transform(aMat);
2482
2483
64.6k
                tools::PolyPolygon aGlyphPoly(aGlyph);
2484
                // Use aTmp's orientation so scale-induced rotation (GM_COMPATIBLE
2485
                // with negative fXScale/fYScale, or GM_ADVANCED world transform)
2486
                // is applied to the emitted polygons.
2487
64.6k
                if (aTmp.GetOrientation() != 0_deg10)
2488
63.4k
                {
2489
192k
                    for (sal_uInt16 p = 0; p < aGlyphPoly.Count(); ++p)
2490
129k
                        aGlyphPoly[p].Rotate(rPosition, aTmp.GetOrientation());
2491
63.4k
                }
2492
2493
64.6k
                if (!bHasGlyphs)
2494
1.07k
                {
2495
                    // Emit Push/FillColor/LineColor once before the first glyph
2496
1.07k
                    mpGDIMetaFile->AddAction( new MetaPushAction( vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR ) );
2497
1.07k
                    mpGDIMetaFile->AddAction( new MetaFillColorAction( maTextColor, true ) );
2498
1.07k
                    mpGDIMetaFile->AddAction( new MetaLineColorAction( Color(), false ) );
2499
1.07k
                    bHasGlyphs = true;
2500
1.07k
                }
2501
2502
64.6k
                mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( aGlyphPoly ) );
2503
64.6k
            }
2504
2505
1.07k
            if (bHasGlyphs)
2506
1.07k
                mpGDIMetaFile->AddAction( new MetaPopAction() );
2507
1.07k
        }
2508
2509
1.09k
        SetGfxMode( nOldGfxMode );
2510
1.09k
        return true;
2511
2.26k
    }
2512
2513
    void MtfTools::ImplDrawBitmap( const Point& rPos, const Size& rSize, const Bitmap& rBitmap )
2514
36.6k
    {
2515
36.6k
        Bitmap aBmp( rBitmap );
2516
36.6k
        if ( mbComplexClip )
2517
0
        {
2518
0
            vcl::bitmap::DrawAndClipBitmap(rPos, rSize, rBitmap, aBmp, maClipPath.getClipPath());
2519
0
        }
2520
2521
36.6k
        if ( aBmp.HasAlpha() )
2522
13.6k
            mpGDIMetaFile->AddAction( new MetaBmpExScaleAction( rPos, rSize, aBmp ) );
2523
23.0k
        else
2524
23.0k
            mpGDIMetaFile->AddAction( new MetaBmpScaleAction( rPos, rSize, aBmp ) );
2525
36.6k
    }
2526
2527
    void MtfTools::ResolveBitmapActions( std::vector<BSaveStruct>& rSaveList )
2528
37.4k
    {
2529
37.4k
        UpdateClipRegion();
2530
2531
37.4k
        size_t nObjects     = rSaveList.size();
2532
37.4k
        size_t nObjectsLeft = nObjects;
2533
2534
88.2k
        while ( nObjectsLeft )
2535
50.7k
        {
2536
50.7k
            size_t          i;
2537
50.7k
            size_t          nObjectsOfSameSize = 0;
2538
50.7k
            size_t          nObjectStartIndex = nObjects - nObjectsLeft;
2539
2540
50.7k
            BSaveStruct*    pSave = &rSaveList[nObjectStartIndex];
2541
50.7k
            tools::Rectangle       aRect( pSave->aOutRect );
2542
2543
91.7k
            for ( i = nObjectStartIndex; i < nObjects; )
2544
54.2k
            {
2545
54.2k
                nObjectsOfSameSize++;
2546
54.2k
                if ( ++i < nObjects )
2547
16.8k
                {
2548
16.8k
                    pSave = &rSaveList[i];
2549
16.8k
                    if ( pSave->aOutRect != aRect )
2550
13.2k
                        break;
2551
16.8k
                }
2552
54.2k
            }
2553
50.7k
            Point   aPos( ImplMap( aRect.TopLeft() ) );
2554
50.7k
            Size    aSize( ImplMap( aRect.GetSize() ) );
2555
2556
104k
            for ( i = nObjectStartIndex; i < ( nObjectStartIndex + nObjectsOfSameSize ); i++ )
2557
54.0k
            {
2558
54.0k
                pSave = &rSaveList[i];
2559
2560
54.0k
                sal_uInt32  nWinRop = pSave->nWinRop;
2561
54.0k
                sal_uInt8   nRasterOperation = static_cast<sal_uInt8>( nWinRop >> 16 );
2562
2563
54.0k
                sal_uInt32  nUsed =  0;
2564
54.0k
                if ( ( nRasterOperation & 0xf )  != ( nRasterOperation >> 4 ) )
2565
14.4k
                    nUsed |= 1;     // pattern is used
2566
54.0k
                if ( ( nRasterOperation & 0x33 ) != ( ( nRasterOperation & 0xcc ) >> 2 ) )
2567
24.2k
                    nUsed |= 2;     // source is used
2568
54.0k
                if ( ( nRasterOperation & 0xaa ) != ( ( nRasterOperation & 0x55 ) << 1 ) )
2569
23.3k
                    nUsed |= 4;     // destination is used
2570
2571
54.0k
                if ( (nUsed & 1) && (( nUsed & 2 ) == 0) && nWinRop != PATINVERT )
2572
670
                {   // patterns aren't well supported yet
2573
670
                    WMFRasterOp nOldRop = SetRasterOp( WMFRasterOp::NONE );  // in this case nRasterOperation is either 0 or 0xff
2574
670
                    UpdateFillStyle();
2575
670
                    DrawRect( aRect, false );
2576
670
                    SetRasterOp( nOldRop );
2577
670
                }
2578
53.4k
                else
2579
53.4k
                {
2580
53.4k
                    bool bDrawn = false;
2581
2582
53.4k
                    if ( i == nObjectStartIndex )   // optimizing, sometimes it is possible to create just one transparent bitmap
2583
50.0k
                    {
2584
50.0k
                        if ( nObjectsOfSameSize == 2 )
2585
1.62k
                        {
2586
1.62k
                            BSaveStruct* pSave2 = &rSaveList[i + 1];
2587
1.62k
                            if ( ( pSave->aBmp.GetPrefSize() == pSave2->aBmp.GetPrefSize() ) &&
2588
1.54k
                                 ( pSave->aBmp.GetPrefMapMode() == pSave2->aBmp.GetPrefMapMode() ) )
2589
1.28k
                            {
2590
                                // TODO: Strictly speaking, we should
2591
                                // check whether mask is monochrome, and
2592
                                // whether image is black (upper branch)
2593
                                // or white (lower branch). Otherwise, the
2594
                                // effect is not the same as a masked
2595
                                // bitmap.
2596
1.28k
                                if ( ( nWinRop == SRCPAINT ) && ( pSave2->nWinRop == SRCAND ) )
2597
160
                                {
2598
160
                                    Bitmap aMask( pSave->aBmp.CreateColorBitmap() );
2599
160
                                    aMask.Invert();
2600
160
                                    Bitmap aBmpEx( pSave2->aBmp.CreateColorBitmap(), aMask );
2601
160
                                    ImplDrawBitmap( aPos, aSize, aBmpEx );
2602
160
                                    bDrawn = true;
2603
160
                                    i++;
2604
160
                                }
2605
                                // #i20085# This is just the other way
2606
                                // around as above. Only difference: mask
2607
                                // is inverted
2608
1.12k
                                else if ( ( nWinRop == SRCAND ) && ( pSave2->nWinRop == SRCPAINT ) )
2609
39
                                {
2610
39
                                    const Bitmap aMask( pSave->aBmp.CreateColorBitmap() );
2611
39
                                    Bitmap aBmpEx( pSave2->aBmp.CreateColorBitmap(), aMask );
2612
39
                                    ImplDrawBitmap( aPos, aSize, aBmpEx );
2613
39
                                    bDrawn = true;
2614
39
                                    i++;
2615
39
                                }
2616
                                // tdf#90539
2617
1.08k
                                else if ( ( nWinRop == SRCAND ) && ( pSave2->nWinRop == SRCINVERT ) )
2618
0
                                {
2619
0
                                    const Bitmap aMask( pSave->aBmp.CreateColorBitmap() );
2620
0
                                    Bitmap aBmpEx( pSave2->aBmp.CreateColorBitmap(), aMask );
2621
0
                                    ImplDrawBitmap( aPos, aSize, aBmpEx );
2622
0
                                    bDrawn = true;
2623
0
                                    i++;
2624
0
                                }
2625
1.28k
                            }
2626
1.62k
                        }
2627
50.0k
                    }
2628
2629
53.4k
                    if ( !bDrawn )
2630
53.2k
                    {
2631
53.2k
                        Push();
2632
53.2k
                        WMFRasterOp nOldRop = SetRasterOp( WMFRasterOp::CopyPen );
2633
53.2k
                        Bitmap      aBitmap( pSave->aBmp.CreateColorBitmap() );
2634
53.2k
                        sal_uInt32  nOperation = ( nRasterOperation & 0xf );
2635
53.2k
                        switch( nOperation )
2636
53.2k
                        {
2637
2.97k
                            case 0x1 :
2638
10.2k
                            case 0xe :
2639
10.2k
                            {
2640
10.2k
                                if(pSave->aBmp.HasAlpha())
2641
1.92k
                                {
2642
1.92k
                                    ImplDrawBitmap( aPos, aSize, pSave->aBmp );
2643
1.92k
                                }
2644
8.29k
                                else
2645
8.29k
                                {
2646
8.29k
                                    SetRasterOp( WMFRasterOp::XorPen );
2647
8.29k
                                    ImplDrawBitmap( aPos, aSize, aBitmap );
2648
8.29k
                                    SetRasterOp( WMFRasterOp::CopyPen );
2649
8.29k
                                    Bitmap  aMask( aBitmap );
2650
8.29k
                                    aMask.Invert();
2651
8.29k
                                    Bitmap aBmpEx( aBitmap, aMask );
2652
8.29k
                                    ImplDrawBitmap( aPos, aSize, aBmpEx );
2653
8.29k
                                    if ( nOperation == 0x1 )
2654
2.97k
                                    {
2655
2.97k
                                        SetRasterOp( WMFRasterOp::Not );
2656
2.97k
                                        DrawRect( aRect, false );
2657
2.97k
                                    }
2658
8.29k
                                }
2659
10.2k
                            }
2660
10.2k
                            break;
2661
3.69k
                            case 0x7 :
2662
5.49k
                            case 0x8 :
2663
5.49k
                            {
2664
5.49k
                                Bitmap  aMask( aBitmap );
2665
5.49k
                                if ( ( nUsed & 1 ) && ( nRasterOperation & 0xb0 ) == 0xb0 )     // pattern used
2666
3.81k
                                {
2667
3.81k
                                    aBitmap.Convert( BmpConversion::N24Bit );
2668
3.81k
                                    aBitmap.Erase( maFillStyle.aFillColor );
2669
3.81k
                                }
2670
5.49k
                                Bitmap aBmpEx( aBitmap, aMask );
2671
5.49k
                                ImplDrawBitmap( aPos, aSize, aBmpEx );
2672
5.49k
                                if ( nOperation == 0x7 )
2673
3.69k
                                {
2674
3.69k
                                    SetRasterOp( WMFRasterOp::Not );
2675
3.69k
                                    DrawRect( aRect, false );
2676
3.69k
                                }
2677
5.49k
                            }
2678
5.49k
                            break;
2679
2680
1.35k
                            case 0x4 :
2681
2.21k
                            case 0xb :
2682
2.21k
                            {
2683
2.21k
                                SetRasterOp( WMFRasterOp::Not );
2684
2.21k
                                DrawRect( aRect, false );
2685
2.21k
                                SetRasterOp( WMFRasterOp::CopyPen );
2686
2.21k
                                Bitmap  aMask( aBitmap );
2687
2.21k
                                aBitmap.Invert();
2688
2.21k
                                Bitmap aBmpEx( aBitmap, aMask );
2689
2.21k
                                ImplDrawBitmap( aPos, aSize, aBmpEx );
2690
2.21k
                                SetRasterOp( WMFRasterOp::XorPen );
2691
2.21k
                                ImplDrawBitmap( aPos, aSize, aBitmap );
2692
2.21k
                                if ( nOperation == 0xb )
2693
865
                                {
2694
865
                                    SetRasterOp( WMFRasterOp::Not );
2695
865
                                    DrawRect( aRect, false );
2696
865
                                }
2697
2.21k
                            }
2698
2.21k
                            break;
2699
2700
1.25k
                            case 0x2 :
2701
2.77k
                            case 0xd :
2702
2.77k
                            {
2703
2.77k
                                Bitmap  aMask( aBitmap );
2704
2.77k
                                aMask.Invert();
2705
2.77k
                                Bitmap aBmpEx( aBitmap, aMask );
2706
2.77k
                                ImplDrawBitmap( aPos, aSize, aBmpEx );
2707
2.77k
                                SetRasterOp( WMFRasterOp::XorPen );
2708
2.77k
                                ImplDrawBitmap( aPos, aSize, aBitmap );
2709
2.77k
                                if ( nOperation == 0xd )
2710
1.51k
                                {
2711
1.51k
                                    SetRasterOp( WMFRasterOp::Not );
2712
1.51k
                                    DrawRect( aRect, false );
2713
1.51k
                                }
2714
2.77k
                            }
2715
2.77k
                            break;
2716
597
                            case 0x6 :
2717
855
                            case 0x9 :
2718
855
                            {
2719
855
                                SetRasterOp( WMFRasterOp::XorPen );
2720
855
                                ImplDrawBitmap( aPos, aSize, aBitmap );
2721
855
                                if ( nOperation == 0x9 )
2722
258
                                {
2723
258
                                    SetRasterOp( WMFRasterOp::Not );
2724
258
                                    DrawRect( aRect, false );
2725
258
                                }
2726
855
                            }
2727
855
                            break;
2728
2729
27.1k
                            case 0x0 :  // WHITENESS
2730
29.2k
                            case 0xf :  // BLACKNESS
2731
29.2k
                            {                                                   // in this case nRasterOperation is either 0 or 0xff
2732
29.2k
                                maFillStyle = WinMtfFillStyle( Color( nRasterOperation, nRasterOperation, nRasterOperation ) );
2733
29.2k
                                UpdateFillStyle();
2734
29.2k
                                DrawRect( aRect, false );
2735
29.2k
                            }
2736
29.2k
                            break;
2737
2738
247
                            case 0x3 :  // only source is used
2739
1.62k
                            case 0xc :
2740
1.62k
                            {
2741
1.62k
                                if ( nRasterOperation == 0x33 )
2742
97
                                    aBitmap.Invert();
2743
1.62k
                                if (pSave->m_bForceAlpha)
2744
1.02k
                                {
2745
1.02k
                                    ImplDrawBitmap(aPos, aSize, pSave->aBmp);
2746
1.02k
                                }
2747
595
                                else
2748
595
                                {
2749
595
                                    ImplDrawBitmap(aPos, aSize, aBitmap);
2750
595
                                }
2751
1.62k
                            }
2752
1.62k
                            break;
2753
2754
671
                            case 0x5 :  // only destination is used
2755
671
                            {
2756
671
                                SetRasterOp( WMFRasterOp::Not );
2757
671
                                DrawRect( aRect, false );
2758
671
                            }
2759
671
                            break;
2760
2761
107
                            case 0xa :  // no operation
2762
107
                            break;
2763
53.2k
                        }
2764
53.2k
                        SetRasterOp( nOldRop );
2765
53.2k
                        Pop();
2766
53.2k
                    }
2767
53.4k
                }
2768
54.0k
            }
2769
50.7k
            nObjectsLeft -= nObjectsOfSameSize;
2770
50.7k
        }
2771
2772
37.4k
        rSaveList.clear();
2773
37.4k
    }
2774
2775
    void MtfTools::SetDevOrg( const Point& rPoint )
2776
25.4k
    {
2777
25.4k
        mnDevOrgX = rPoint.X();
2778
25.4k
        mnDevOrgY = rPoint.Y();
2779
25.4k
    }
2780
2781
    void MtfTools::SetDevOrgOffset( sal_Int32 nXAdd, sal_Int32 nYAdd )
2782
479
    {
2783
479
        mnDevOrgX += nXAdd;
2784
479
        mnDevOrgY += nYAdd;
2785
479
    }
2786
2787
    void MtfTools::SetDevExt( const Size& rSize ,bool regular)
2788
32.1k
    {
2789
32.1k
        if ( !(rSize.Width() && rSize.Height()) )
2790
1.34k
            return;
2791
2792
30.8k
        switch( meMapMode )
2793
30.8k
        {
2794
4.76k
            case MappingMode::MM_ISOTROPIC :
2795
28.5k
            case MappingMode::MM_ANISOTROPIC :
2796
28.5k
            {
2797
28.5k
                mnDevWidth = rSize.Width();
2798
28.5k
                mnDevHeight = rSize.Height();
2799
28.5k
                break;
2800
4.76k
            }
2801
2802
            //do nothing
2803
2.22k
            default:
2804
2.22k
                break;
2805
30.8k
        }
2806
30.8k
        if (regular)
2807
26.4k
        {
2808
26.4k
            mbIsMapDevSet=true;
2809
26.4k
        }
2810
30.8k
    }
2811
2812
    void MtfTools::ScaleDevExt(double fX, double fY)
2813
5.01k
    {
2814
5.01k
        mnDevWidth = basegfx::fround(mnDevWidth * fX);
2815
5.01k
        mnDevHeight = basegfx::fround(mnDevHeight * fY);
2816
5.01k
    }
2817
2818
    void MtfTools::SetWinOrg( const Point& rPoint , bool bIsEMF)
2819
34.4k
    {
2820
34.4k
        mnWinOrgX = rPoint.X();
2821
34.4k
        mnWinOrgY = rPoint.Y();
2822
34.4k
        if (bIsEMF)
2823
8.68k
        {
2824
8.68k
            SetDevByWin();
2825
8.68k
        }
2826
34.4k
        mbIsMapWinSet=true;
2827
34.4k
    }
2828
2829
    void MtfTools::SetWinOrgOffset( sal_Int32 nXAdd, sal_Int32 nYAdd )
2830
215
    {
2831
215
        mnWinOrgX += nXAdd;
2832
215
        mnWinOrgY += nYAdd;
2833
215
    }
2834
2835
    void MtfTools::SetDevByWin() //mnWinExt...-stuff has to be assigned before.
2836
11.2k
    {
2837
11.2k
        if (!mbIsMapDevSet)
2838
9.45k
        {
2839
9.45k
            if ( meMapMode == MappingMode::MM_ISOTROPIC ) //TODO: WHAT ABOUT ANISOTROPIC???
2840
6.45k
            {
2841
6.45k
                sal_Int32 nX, nY;
2842
6.45k
                if (o3tl::checked_add(mnWinExtX, mnWinOrgX, nX) || o3tl::checked_sub(mnWinExtY, mnWinOrgY, nY))
2843
1.27k
                    return;
2844
5.17k
                Size aSize(nX >> MS_FIXPOINT_BITCOUNT_28_4, -(nY >> MS_FIXPOINT_BITCOUNT_28_4));
2845
5.17k
                SetDevExt(aSize, false);
2846
5.17k
            }
2847
9.45k
        }
2848
11.2k
    }
2849
2850
    void MtfTools::SetWinExt(const Size& rSize, bool bIsEMF)
2851
30.3k
    {
2852
30.3k
        if (!(rSize.Width() && rSize.Height()))
2853
2.63k
            return;
2854
2855
27.7k
        switch( meMapMode )
2856
27.7k
        {
2857
2.09k
            case MappingMode::MM_ISOTROPIC :
2858
25.5k
            case MappingMode::MM_ANISOTROPIC :
2859
25.5k
            {
2860
25.5k
                mnWinExtX = rSize.Width();
2861
25.5k
                mnWinExtY = rSize.Height();
2862
25.5k
                if (bIsEMF)
2863
2.60k
                {
2864
2.60k
                    SetDevByWin();
2865
2.60k
                }
2866
25.5k
                mbIsMapWinSet = true;
2867
25.5k
                break;
2868
2.09k
            }
2869
2870
2.13k
            default:
2871
                //do nothing
2872
2.13k
                break;
2873
27.7k
        }
2874
27.7k
    }
2875
2876
    void MtfTools::ScaleWinExt(double fX, double fY)
2877
4.08k
    {
2878
4.08k
        mnWinExtX = basegfx::fround(mnWinExtX * fX);
2879
4.08k
        mnWinExtY = basegfx::fround(mnWinExtY * fY);
2880
4.08k
    }
2881
2882
    void MtfTools::SetrclBounds( const tools::Rectangle& rRect )
2883
10.9k
    {
2884
10.9k
        mrclBounds = rRect;
2885
10.9k
    }
2886
2887
    void MtfTools::SetrclFrame( const tools::Rectangle& rRect )
2888
11.0k
    {
2889
11.0k
        mrclFrame = rRect;
2890
11.0k
    }
2891
2892
    void MtfTools::SetRefPix( const Size& rSize )
2893
10.9k
    {
2894
10.9k
        mnPixX = rSize.Width();
2895
10.9k
        mnPixY = rSize.Height();
2896
10.9k
    }
2897
2898
    void MtfTools::SetRefMill( const Size& rSize )
2899
10.9k
    {
2900
10.9k
        mnMillX = rSize.Width();
2901
10.9k
        mnMillY = rSize.Height();
2902
10.9k
    }
2903
2904
    void MtfTools::SetMapMode( MappingMode nMapMode )
2905
16.1k
    {
2906
16.1k
        meMapMode = nMapMode;
2907
16.1k
        if ( nMapMode == MappingMode::MM_TEXT && !mbIsMapWinSet )
2908
463
        {
2909
463
            mnWinExtX = mnDevWidth;
2910
463
            mnWinExtY = mnDevHeight;
2911
463
        }
2912
15.6k
        else if ( meMapMode == MappingMode::MM_HIMETRIC )
2913
1.18k
        {
2914
1.18k
            sal_Int32 nWinExtX, nWinExtY;
2915
1.18k
            if (o3tl::checked_multiply<sal_Int32>(mnMillX, 100, nWinExtX) ||
2916
995
                o3tl::checked_multiply<sal_Int32>(mnMillY, 100, nWinExtY))
2917
547
            {
2918
547
                return;
2919
547
            }
2920
635
            mnWinExtX = nWinExtX;
2921
635
            mnWinExtY = nWinExtY;
2922
635
        }
2923
16.1k
    }
2924
2925
    void MtfTools::SetWorldTransform( const XForm& rXForm )
2926
1.06k
    {
2927
1.06k
        maXForm.eM11 = rXForm.eM11;
2928
1.06k
        maXForm.eM12 = rXForm.eM12;
2929
1.06k
        maXForm.eM21 = rXForm.eM21;
2930
1.06k
        maXForm.eM22 = rXForm.eM22;
2931
1.06k
        maXForm.eDx = rXForm.eDx;
2932
1.06k
        maXForm.eDy = rXForm.eDy;
2933
1.06k
    }
2934
2935
    void MtfTools::ModifyWorldTransform( const XForm& rXForm, ModifyWorldTransformMode nMode )
2936
4.50k
    {
2937
4.50k
        switch( nMode )
2938
4.50k
        {
2939
1.21k
            case ModifyWorldTransformMode::MWT_IDENTITY :
2940
1.21k
            {
2941
1.21k
                maXForm.eM11 = maXForm.eM22 = 1.0f;
2942
1.21k
                maXForm.eM12 = maXForm.eM21 = maXForm.eDx = maXForm.eDy = 0.0f;
2943
1.21k
                break;
2944
0
            }
2945
2946
397
            case ModifyWorldTransformMode::MWT_RIGHTMULTIPLY :
2947
1.40k
            case ModifyWorldTransformMode::MWT_LEFTMULTIPLY :
2948
1.40k
            {
2949
1.40k
                const XForm* pLeft;
2950
1.40k
                const XForm* pRight;
2951
2952
1.40k
                if ( nMode == ModifyWorldTransformMode::MWT_LEFTMULTIPLY )
2953
1.01k
                {
2954
1.01k
                    pLeft = &rXForm;
2955
1.01k
                    pRight = &maXForm;
2956
1.01k
                }
2957
397
                else
2958
397
                {
2959
397
                    pLeft = &maXForm;
2960
397
                    pRight = &rXForm;
2961
397
                }
2962
2963
1.40k
                float aF[3][3];
2964
1.40k
                float bF[3][3];
2965
1.40k
                float cF[3][3];
2966
2967
1.40k
                aF[0][0] = pLeft->eM11;
2968
1.40k
                aF[0][1] = pLeft->eM12;
2969
1.40k
                aF[0][2] = 0;
2970
1.40k
                aF[1][0] = pLeft->eM21;
2971
1.40k
                aF[1][1] = pLeft->eM22;
2972
1.40k
                aF[1][2] = 0;
2973
1.40k
                aF[2][0] = pLeft->eDx;
2974
1.40k
                aF[2][1] = pLeft->eDy;
2975
1.40k
                aF[2][2] = 1;
2976
2977
1.40k
                bF[0][0] = pRight->eM11;
2978
1.40k
                bF[0][1] = pRight->eM12;
2979
1.40k
                bF[0][2] = 0;
2980
1.40k
                bF[1][0] = pRight->eM21;
2981
1.40k
                bF[1][1] = pRight->eM22;
2982
1.40k
                bF[1][2] = 0;
2983
1.40k
                bF[2][0] = pRight->eDx;
2984
1.40k
                bF[2][1] = pRight->eDy;
2985
1.40k
                bF[2][2] = 1;
2986
2987
1.40k
                int i, j, k;
2988
5.63k
                for ( i = 0; i < 3; i++ )
2989
4.22k
                {
2990
16.8k
                  for ( j = 0; j < 3; j++ )
2991
12.6k
                  {
2992
12.6k
                     cF[i][j] = 0;
2993
50.6k
                     for ( k = 0; k < 3; k++ )
2994
38.0k
                        cF[i][j] += aF[i][k] * bF[k][j];
2995
12.6k
                  }
2996
4.22k
                }
2997
1.40k
                maXForm.eM11 = cF[0][0];
2998
1.40k
                maXForm.eM12 = cF[0][1];
2999
1.40k
                maXForm.eM21 = cF[1][0];
3000
1.40k
                maXForm.eM22 = cF[1][1];
3001
1.40k
                maXForm.eDx = cF[2][0];
3002
1.40k
                maXForm.eDy = cF[2][1];
3003
1.40k
                break;
3004
397
            }
3005
237
            case ModifyWorldTransformMode::MWT_SET:
3006
237
            {
3007
237
                SetWorldTransform(rXForm);
3008
237
                break;
3009
397
            }
3010
4.50k
        }
3011
4.50k
    }
3012
3013
    void MtfTools::Push()                       // !! to be able to access the original ClipRegion it
3014
75.5k
    {                                               // is not allowed to use the MetaPushAction()
3015
75.5k
        UpdateClipRegion();                         // (the original clip region is on top of the stack) (SJ)
3016
75.5k
        auto pSave = std::make_shared<SaveStruct>();
3017
3018
75.5k
        pSave->aLineStyle = maLineStyle;
3019
75.5k
        pSave->aFillStyle = maFillStyle;
3020
3021
75.5k
        pSave->aFont = maFont;
3022
75.5k
        pSave->aTextColor = maTextColor;
3023
75.5k
        pSave->nTextAlign = mnTextAlign;
3024
75.5k
        pSave->nTextLayoutMode = mnTextLayoutMode;
3025
75.5k
        pSave->eMapMode = meMapMode;
3026
75.5k
        pSave->eGfxMode = meGfxMode;
3027
75.5k
        pSave->nBkMode = mnBkMode;
3028
75.5k
        pSave->aBkColor = maBkColor;
3029
75.5k
        pSave->bClockWiseArcDirection = mbClockWiseArcDirection;
3030
75.5k
        pSave->bFillStyleSelected = mbFillStyleSelected;
3031
75.5k
        pSave->nPolyFillMode = mnPolyFillMode;
3032
3033
75.5k
        pSave->aActPos = maActPos;
3034
75.5k
        pSave->aXForm = maXForm;
3035
75.5k
        pSave->eRasterOp = meRasterOp;
3036
3037
75.5k
        pSave->nWinOrgX = mnWinOrgX;
3038
75.5k
        pSave->nWinOrgY = mnWinOrgY;
3039
75.5k
        pSave->nWinExtX = mnWinExtX;
3040
75.5k
        pSave->nWinExtY = mnWinExtY;
3041
75.5k
        pSave->nDevOrgX = mnDevOrgX;
3042
75.5k
        pSave->nDevOrgY = mnDevOrgY;
3043
75.5k
        pSave->nDevWidth = mnDevWidth;
3044
75.5k
        pSave->nDevHeight = mnDevHeight;
3045
3046
75.5k
        pSave->maPathObj = maPathObj;
3047
75.5k
        pSave->maClipPath = maClipPath;
3048
3049
75.5k
        SAL_INFO("emfio", "\t\t GfxMode: " << static_cast<sal_uInt32>(meGfxMode));
3050
75.5k
        SAL_INFO("emfio", "\t\t MapMode: " << static_cast<sal_uInt32>(meMapMode));
3051
75.5k
        SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX << ", " << mnWinOrgY);
3052
75.5k
        SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX << " x " << mnWinExtY);
3053
75.5k
        SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX << ", " << mnDevOrgY);
3054
75.5k
        SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth << " x " << mnDevHeight);
3055
75.5k
        SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle.aLineColor << " FillStyle: " << maFillStyle.aFillColor );
3056
75.5k
        mvSaveStack.push_back(std::move(pSave));
3057
75.5k
    }
3058
3059
    void MtfTools::Pop( const sal_Int32 nSavedDC )
3060
68.9k
    {
3061
68.9k
        if ( nSavedDC == 0 )
3062
29
            return;
3063
3064
68.9k
        sal_Int32 aIndex;
3065
68.9k
        if ( nSavedDC < 0 ) // WMF/EMF, if negative, nSavedDC represents an instance relative to the current state.
3066
68.4k
            aIndex = static_cast< sal_Int32 >( mvSaveStack.size() ) + nSavedDC;
3067
548
        else
3068
548
            aIndex = nSavedDC; // WMF, if positive, nSavedDC represents a specific instance of the state to be restored.
3069
68.9k
        if( aIndex < 0 )
3070
1.81k
        {
3071
1.81k
            mvSaveStack.clear();
3072
1.81k
            return;
3073
1.81k
        }
3074
67.1k
        if( mvSaveStack.empty() || ( aIndex >= static_cast< sal_Int32 >( mvSaveStack.size() ) ) )
3075
425
            return;
3076
3077
66.7k
        mvSaveStack.resize( aIndex + 1 );
3078
        // Backup the current data on the stack
3079
66.7k
        std::shared_ptr<SaveStruct>& pSave( mvSaveStack.back() );
3080
3081
66.7k
        maLineStyle = pSave->aLineStyle;
3082
66.7k
        maFillStyle = pSave->aFillStyle;
3083
3084
66.7k
        maFont = pSave->aFont;
3085
66.7k
        maTextColor = pSave->aTextColor;
3086
66.7k
        mnTextAlign = pSave->nTextAlign;
3087
66.7k
        mnTextLayoutMode = pSave->nTextLayoutMode;
3088
66.7k
        mnBkMode = pSave->nBkMode;
3089
66.7k
        meGfxMode = pSave->eGfxMode;
3090
66.7k
        meMapMode = pSave->eMapMode;
3091
66.7k
        maBkColor = pSave->aBkColor;
3092
66.7k
        mbClockWiseArcDirection = pSave->bClockWiseArcDirection;
3093
66.7k
        mbFillStyleSelected = pSave->bFillStyleSelected;
3094
66.7k
        mnPolyFillMode = pSave->nPolyFillMode;
3095
3096
66.7k
        maActPos = pSave->aActPos;
3097
66.7k
        maXForm = pSave->aXForm;
3098
66.7k
        meRasterOp = pSave->eRasterOp;
3099
3100
66.7k
        mnWinOrgX = pSave->nWinOrgX;
3101
66.7k
        mnWinOrgY = pSave->nWinOrgY;
3102
66.7k
        mnWinExtX = pSave->nWinExtX;
3103
66.7k
        mnWinExtY = pSave->nWinExtY;
3104
66.7k
        mnDevOrgX = pSave->nDevOrgX;
3105
66.7k
        mnDevOrgY = pSave->nDevOrgY;
3106
66.7k
        mnDevWidth = pSave->nDevWidth;
3107
66.7k
        mnDevHeight = pSave->nDevHeight;
3108
3109
66.7k
        maPathObj = pSave->maPathObj;
3110
66.7k
        if ( ! ( maClipPath == pSave->maClipPath ) )
3111
0
        {
3112
0
            maClipPath = pSave->maClipPath;
3113
0
            mbClipNeedsUpdate = true;
3114
0
        }
3115
66.7k
        if ( meLatestRasterOp != meRasterOp )
3116
5.77k
        {
3117
5.77k
            mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) );
3118
5.77k
            meLatestRasterOp = meRasterOp;
3119
5.77k
        }
3120
3121
66.7k
        SAL_INFO("emfio", "\t\t GfxMode: " << static_cast<sal_uInt32>(meGfxMode));
3122
66.7k
        SAL_INFO("emfio", "\t\t MapMode: " << static_cast<sal_uInt32>(meMapMode));
3123
66.7k
        SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX << ", " << mnWinOrgY);
3124
66.7k
        SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX << " x " << mnWinExtY);
3125
66.7k
        SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX << ", " << mnDevOrgY);
3126
66.7k
        SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth << " x " << mnDevHeight);
3127
66.7k
        SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle.aLineColor << " FillStyle: " << maFillStyle.aFillColor );
3128
66.7k
        mvSaveStack.pop_back();
3129
66.7k
    }
3130
3131
    void MtfTools::AddFromGDIMetaFile( GDIMetaFile& rGDIMetaFile )
3132
41
    {
3133
41
       rGDIMetaFile.Play( *mpGDIMetaFile );
3134
41
    }
3135
3136
    void MtfTools::PassEMFPlusHeaderInfo()
3137
1.84k
    {
3138
1.84k
        EMFP_DEBUG(printf ("\t\t\tadd EMF_PLUS header info\n"));
3139
3140
1.84k
        SvMemoryStream mem;
3141
1.84k
        sal_Int32 nLeft, nRight, nTop, nBottom;
3142
3143
1.84k
        nLeft = mrclFrame.Left();
3144
1.84k
        nTop = mrclFrame.Top();
3145
1.84k
        nRight = mrclFrame.Right();
3146
1.84k
        nBottom = mrclFrame.Bottom();
3147
3148
        // emf header info
3149
1.84k
        mem.WriteInt32( nLeft ).WriteInt32( nTop ).WriteInt32( nRight ).WriteInt32( nBottom );
3150
1.84k
        mem.WriteInt32( mnPixX ).WriteInt32( mnPixY ).WriteInt32( mnMillX ).WriteInt32( mnMillY );
3151
3152
1.84k
        float one, zero;
3153
3154
1.84k
        one = 1;
3155
1.84k
        zero = 0;
3156
3157
        // add transformation matrix to be used in vcl's metaact.cxx for
3158
        // rotate and scale operations
3159
1.84k
        mem.WriteFloat( one ).WriteFloat( zero ).WriteFloat( zero ).WriteFloat( one ).WriteFloat( zero ).WriteFloat( zero );
3160
3161
        // need to flush the stream, otherwise GetEndOfData will return 0
3162
        // on windows where the function parameters are probably resolved in reverse order
3163
1.84k
        mem.Flush();
3164
3165
1.84k
        mpGDIMetaFile->AddAction( new MetaCommentAction( "EMF_PLUS_HEADER_INFO"_ostr, 0, static_cast<const sal_uInt8*>(mem.GetData()), mem.GetEndOfData() ) );
3166
1.84k
        mpGDIMetaFile->UseCanvas( true );
3167
1.84k
    }
3168
3169
    void MtfTools::PassEMFPlus( void const * pBuffer, sal_uInt32 nLength )
3170
7.18k
    {
3171
7.18k
        EMFP_DEBUG(printf ("\t\t\tadd EMF_PLUS comment length %04x\n",(unsigned int) nLength));
3172
7.18k
        mpGDIMetaFile->AddAction( new MetaCommentAction( "EMF_PLUS"_ostr, 0, static_cast<const sal_uInt8*>(pBuffer), nLength ) );
3173
7.18k
    }
3174
}
3175
3176
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */