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/b2dpolypolygontools.hxx>
26 #include <vcl/metric.hxx>
27 #include <vcl/graphictools.hxx>
28 #include <vcl/BitmapTools.hxx>
29 #include <vcl/metaact.hxx>
30 #include <vcl/canvastools.hxx>
31 #include <vcl/svapp.hxx>
32 #include <tools/stream.hxx>
33 #include <rtl/tencinfo.h>
34 #include <sal/log.hxx>
35 #include <osl/diagnose.h>
36 #include <vcl/virdev.hxx>
37 #include <o3tl/safeint.hxx>
38 #include <unotools/configmgr.hxx>
39 #include <unotools/defaultencoding.hxx>
40 #include <unotools/wincodepage.hxx>
41 
42 #if OSL_DEBUG_LEVEL > 1
43 #define EMFP_DEBUG(x) x
44 #else
45 #define EMFP_DEBUG(x)
46 #endif
47 
48 namespace emfio
49 {
operator >>(SvStream & rInStream,XForm & rXForm)50     SvStream& operator >> (SvStream& rInStream, XForm& rXForm)
51     {
52         if (sizeof(float) != 4)
53         {
54             OSL_FAIL("EmfReader::sizeof( float ) != 4");
55             rXForm = XForm();
56         }
57         else
58         {
59             rInStream.ReadFloat(rXForm.eM11);
60             rInStream.ReadFloat(rXForm.eM12);
61             rInStream.ReadFloat(rXForm.eM21);
62             rInStream.ReadFloat(rXForm.eM22);
63             rInStream.ReadFloat(rXForm.eDx);
64             rInStream.ReadFloat(rXForm.eDy);
65         }
66         return rInStream;
67     }
68 
intersectClip(const basegfx::B2DPolyPolygon & rPolyPolygon)69     void WinMtfClipPath::intersectClip( const basegfx::B2DPolyPolygon& rPolyPolygon )
70     {
71         maClip.intersectPolyPolygon(rPolyPolygon);
72     }
73 
excludeClip(const basegfx::B2DPolyPolygon & rPolyPolygon)74     void WinMtfClipPath::excludeClip( const basegfx::B2DPolyPolygon& rPolyPolygon )
75     {
76         maClip.subtractPolyPolygon(rPolyPolygon);
77     }
78 
setClipPath(const basegfx::B2DPolyPolygon & rB2DPoly,sal_Int32 nClippingMode)79     void WinMtfClipPath::setClipPath( const basegfx::B2DPolyPolygon& rB2DPoly, sal_Int32 nClippingMode )
80     {
81         switch ( nClippingMode )
82         {
83             case RGN_OR :
84                 maClip.unionPolyPolygon(rB2DPoly);
85                 break;
86             case RGN_XOR :
87                 maClip.xorPolyPolygon(rB2DPoly);
88                 break;
89             case RGN_DIFF :
90                 maClip.subtractPolyPolygon(rB2DPoly);
91                 break;
92             case RGN_AND :
93                 maClip.intersectPolyPolygon(rB2DPoly);
94                 break;
95             case RGN_COPY :
96                 maClip = basegfx::utils::B2DClipState(rB2DPoly);
97                 break;
98         }
99     }
100 
moveClipRegion(const Size & rSize)101     void WinMtfClipPath::moveClipRegion( const Size& rSize )
102     {
103         basegfx::B2DHomMatrix aTranslate;
104         aTranslate.translate(rSize.Width(), rSize.Height());
105         maClip.transform(aTranslate);
106     }
107 
setDefaultClipPath()108     void WinMtfClipPath::setDefaultClipPath()
109     {
110         // Empty clip region - everything visible
111         maClip = basegfx::utils::B2DClipState();
112     }
113 
getClipPath() const114     basegfx::B2DPolyPolygon const & WinMtfClipPath::getClipPath() const
115     {
116         return maClip.getClipPoly();
117     }
118 
AddPoint(const Point & rPoint)119     void WinMtfPathObj::AddPoint( const Point& rPoint )
120     {
121         if ( bClosed )
122             Insert( tools::Polygon() );
123         tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ];
124         rPoly.Insert( rPoly.GetSize(), rPoint );
125         bClosed = false;
126     }
127 
AddPolyLine(const tools::Polygon & rPolyLine)128     void WinMtfPathObj::AddPolyLine( const tools::Polygon& rPolyLine )
129     {
130         if ( bClosed )
131             Insert( tools::Polygon() );
132         tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ];
133         rPoly.Insert( rPoly.GetSize(), rPolyLine );
134         bClosed = false;
135     }
136 
AddPolygon(const tools::Polygon & rPoly)137     void WinMtfPathObj::AddPolygon( const tools::Polygon& rPoly )
138     {
139         Insert( rPoly );
140         bClosed = true;
141     }
142 
AddPolyPolygon(const tools::PolyPolygon & rPolyPoly)143     void WinMtfPathObj::AddPolyPolygon( const tools::PolyPolygon& rPolyPoly )
144     {
145         sal_uInt16 i, nCount = rPolyPoly.Count();
146         for ( i = 0; i < nCount; i++ )
147             Insert( rPolyPoly[ i ] );
148         bClosed = true;
149     }
150 
ClosePath()151     void WinMtfPathObj::ClosePath()
152     {
153         if ( Count() )
154         {
155             tools::Polygon& rPoly = static_cast<tools::PolyPolygon&>(*this)[ Count() - 1 ];
156             if ( rPoly.GetSize() > 2 )
157             {
158                 Point aFirst( rPoly[ 0 ] );
159                 if ( aFirst != rPoly[ rPoly.GetSize() - 1 ] )
160                     rPoly.Insert( rPoly.GetSize(), aFirst );
161             }
162         }
163         bClosed = true;
164     }
165 
WinMtfFontStyle(LOGFONTW const & rFont)166     WinMtfFontStyle::WinMtfFontStyle( LOGFONTW const & rFont )
167     {
168         rtl_TextEncoding eCharSet;
169         if ((rFont.alfFaceName == "Symbol")
170          || (rFont.alfFaceName == "MT Extra"))
171             eCharSet = RTL_TEXTENCODING_SYMBOL;
172         else if ((rFont.lfCharSet == DEFAULT_CHARSET) || (rFont.lfCharSet == OEM_CHARSET))
173             eCharSet = utl_getWinTextEncodingFromLangStr(utl_getLocaleForGlobalDefaultEncoding(),
174                                                          rFont.lfCharSet == OEM_CHARSET);
175         else
176             eCharSet = rtl_getTextEncodingFromWindowsCharset( rFont.lfCharSet );
177         if ( eCharSet == RTL_TEXTENCODING_DONTKNOW )
178             eCharSet = RTL_TEXTENCODING_MS_1252;
179         aFont.SetCharSet( eCharSet );
180         aFont.SetFamilyName( rFont.alfFaceName );
181         FontFamily eFamily;
182         switch ( rFont.lfPitchAndFamily & 0xf0 )
183         {
184             case FF_ROMAN:
185                 eFamily = FAMILY_ROMAN;
186             break;
187 
188             case FF_SWISS:
189                 eFamily = FAMILY_SWISS;
190             break;
191 
192             case FF_MODERN:
193                 eFamily = FAMILY_MODERN;
194             break;
195 
196             case FF_SCRIPT:
197                 eFamily = FAMILY_SCRIPT;
198             break;
199 
200             case FF_DECORATIVE:
201                  eFamily = FAMILY_DECORATIVE;
202             break;
203 
204             default:
205                 eFamily = FAMILY_DONTKNOW;
206             break;
207         }
208         aFont.SetFamily( eFamily );
209 
210         FontPitch ePitch;
211         switch ( rFont.lfPitchAndFamily & 0x0f )
212         {
213             case FIXED_PITCH:
214                 ePitch = PITCH_FIXED;
215             break;
216 
217             case DEFAULT_PITCH:
218             case VARIABLE_PITCH:
219             default:
220                 ePitch = PITCH_VARIABLE;
221             break;
222         }
223         aFont.SetPitch( ePitch );
224 
225         FontWeight eWeight;
226         if (rFont.lfWeight == 0) // default weight SHOULD be used
227             eWeight = WEIGHT_DONTKNOW;
228         else if (rFont.lfWeight <= FW_THIN)
229             eWeight = WEIGHT_THIN;
230         else if( rFont.lfWeight <= FW_ULTRALIGHT )
231             eWeight = WEIGHT_ULTRALIGHT;
232         else if( rFont.lfWeight <= FW_LIGHT )
233             eWeight = WEIGHT_LIGHT;
234         else if( rFont.lfWeight <  FW_MEDIUM )
235             eWeight = WEIGHT_NORMAL;
236         else if( rFont.lfWeight == FW_MEDIUM )
237             eWeight = WEIGHT_MEDIUM;
238         else if( rFont.lfWeight <= FW_SEMIBOLD )
239             eWeight = WEIGHT_SEMIBOLD;
240         else if( rFont.lfWeight <= FW_BOLD )
241             eWeight = WEIGHT_BOLD;
242         else if( rFont.lfWeight <= FW_ULTRABOLD )
243             eWeight = WEIGHT_ULTRABOLD;
244         else
245             eWeight = WEIGHT_BLACK;
246         aFont.SetWeight( eWeight );
247 
248         if( rFont.lfItalic )
249             aFont.SetItalic( ITALIC_NORMAL );
250 
251         if( rFont.lfUnderline )
252             aFont.SetUnderline( LINESTYLE_SINGLE );
253 
254         if( rFont.lfStrikeOut )
255             aFont.SetStrikeout( STRIKEOUT_SINGLE );
256 
257         aFont.SetOrientation( Degree10(static_cast<sal_Int16>(rFont.lfEscapement)) );
258 
259         Size aFontSize( Size( rFont.lfWidth, rFont.lfHeight ) );
260         if ( rFont.lfHeight > 0 )
261         {
262             // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
263             SolarMutexGuard aGuard;
264             ScopedVclPtrInstance< VirtualDevice > pVDev;
265             // converting the cell height into a font height
266             aFont.SetFontSize( aFontSize );
267             pVDev->SetFont( aFont );
268             FontMetric aMetric( pVDev->GetFontMetric() );
269             tools::Long nHeight = aMetric.GetAscent() + aMetric.GetDescent();
270             if (nHeight)
271             {
272                 double fHeight = (static_cast<double>(aFontSize.Height()) * rFont.lfHeight ) / nHeight;
273                 aFontSize.setHeight( static_cast<sal_Int32>( fHeight + 0.5 ) );
274             }
275         }
276 
277         // Convert height to positive
278         aFontSize.setHeight( std::abs(aFontSize.Height()) );
279         aFont.SetFontSize(aFontSize);
280 
281         // tdf#127471 adapt nFontWidth from Windows-like notation to
282         // NormedFontScaling if used for text scaling
283 #ifndef _WIN32
284         const bool bFontScaledHorizontally(aFontSize.Width() != 0 && aFontSize.Width() != aFontSize.Height());
285 
286         if(bFontScaledHorizontally)
287         {
288             // tdf#127471 nFontWidth is the Windows FontScaling, need to convert to
289             // Non-Windowslike notation relative to FontHeight.
290             const tools::Long nAverageFontWidth(aFont.GetOrCalculateAverageFontWidth());
291 
292             if(nAverageFontWidth > 0)
293             {
294                 const double fScaleFactor(static_cast<double>(aFontSize.Height()) / static_cast<double>(nAverageFontWidth));
295                 aFont.SetAverageFontWidth(static_cast<tools::Long>(static_cast<double>(aFontSize.Width()) * fScaleFactor));
296             }
297         }
298 #endif
299     };
300 
301     // tdf#127471
ScaledFontDetectCorrectHelper()302     ScaledFontDetectCorrectHelper::ScaledFontDetectCorrectHelper()
303     :   maCurrentMetaFontAction(),
304         maAlternativeFontScales(),
305         maPositiveIdentifiedCases(),
306         maNegativeIdentifiedCases()
307     {
308     }
309 
endCurrentMetaFontAction()310     void ScaledFontDetectCorrectHelper::endCurrentMetaFontAction()
311     {
312         if(maCurrentMetaFontAction.is() && !maAlternativeFontScales.empty())
313         {
314             // create average corrected FontScale value and count
315             // positive/negative hits
316             sal_uInt32 nPositive(0);
317             sal_uInt32 nNegative(0);
318             double fAverage(0.0);
319 
320             for(double fPart : maAlternativeFontScales)
321             {
322                 if(fPart < 0.0)
323                 {
324                     nNegative++;
325                     fAverage += -fPart;
326                 }
327                 else
328                 {
329                     nPositive++;
330                     fAverage += fPart;
331                 }
332             }
333 
334             fAverage /= static_cast<double>(maAlternativeFontScales.size());
335 
336             if(nPositive >= nNegative)
337             {
338                 // correction intended, it is probably an old imported file
339                 maPositiveIdentifiedCases.push_back(std::pair<rtl::Reference<MetaFontAction>, double>(maCurrentMetaFontAction, fAverage));
340             }
341             else
342             {
343                 // correction not favorable in the majority of cases for this Font, still
344                 // remember to have a weight in the last decision for correction
345                 maNegativeIdentifiedCases.push_back(std::pair<rtl::Reference<MetaFontAction>, double>(maCurrentMetaFontAction, fAverage));
346             }
347         }
348 
349         maCurrentMetaFontAction.clear();
350         maAlternativeFontScales.clear();
351     }
352 
newCurrentMetaFontAction(rtl::Reference<MetaFontAction> & rNewMetaFontAction)353     void ScaledFontDetectCorrectHelper::newCurrentMetaFontAction(rtl::Reference<MetaFontAction>& rNewMetaFontAction)
354     {
355         maCurrentMetaFontAction.clear();
356         maAlternativeFontScales.clear();
357 
358         if(!rNewMetaFontAction.is())
359             return;
360 
361         // check 1st criteria for FontScale active. We usually write this,
362         // so this will already sort out most situations
363         const vcl::Font& rCandidate(rNewMetaFontAction->GetFont());
364 
365         if(0 != rCandidate.GetAverageFontWidth())
366         {
367             const tools::Long nUnscaledAverageFontWidth(rCandidate.GetOrCalculateAverageFontWidth());
368 
369             // check 2nd (system-dependent) criteria for FontScale
370             if(nUnscaledAverageFontWidth != rCandidate.GetFontHeight())
371             {
372                 // FontScale is active, remember and use as current
373                 maCurrentMetaFontAction = rNewMetaFontAction;
374             }
375         }
376     }
377 
evaluateAlternativeFontScale(OUString const & rText,tools::Long nImportedTextLength)378     void ScaledFontDetectCorrectHelper::evaluateAlternativeFontScale(OUString const & rText, tools::Long nImportedTextLength)
379     {
380         if(!maCurrentMetaFontAction.is())
381             return;
382 
383         SolarMutexGuard aGuard; // VirtualDevice is not thread-safe
384         ScopedVclPtrInstance< VirtualDevice > pTempVirtualDevice;
385 
386         // calculate measured TextLength
387         const vcl::Font& rFontCandidate(maCurrentMetaFontAction->GetFont());
388         pTempVirtualDevice->SetFont(rFontCandidate);
389         tools::Long nMeasuredTextLength(pTempVirtualDevice->GetTextWidth(rText));
390         // on failure, use original length
391         if (!nMeasuredTextLength)
392             nMeasuredTextLength = nImportedTextLength;
393 
394         // compare expected and imported TextLengths
395         if (nImportedTextLength != nMeasuredTextLength)
396         {
397             const double fFactorText(static_cast<double>(nImportedTextLength) / static_cast<double>(nMeasuredTextLength));
398             const double fFactorTextPercent(fabs(1.0 - fFactorText) * 100.0);
399 
400             // if we assume that loaded file was written on old linux, we have to
401             // back-convert the scale value depending on which system we run
402 #ifdef _WIN32
403             // When running on Windows the value was not adapted at font import (see WinMtfFontStyle
404             // constructor), so it is still NormedFontScaling and we need to convert to Windows-style
405             // scaling
406 #else
407             // When running on unx (non-Windows) the value was already adapted at font import (see WinMtfFontStyle
408             // constructor). It was wrongly assumed to be Windows-style FontScaling, so we need to revert that
409             // to get back to the needed unx-style FontScale
410 #endif
411             // Interestingly this leads to the *same* correction, so no need to make this
412             // system-dependent (!)
413             const tools::Long nUnscaledAverageFontWidth(rFontCandidate.GetOrCalculateAverageFontWidth());
414             const tools::Long nScaledAverageFontWidth(rFontCandidate.GetAverageFontWidth());
415             const double fScaleFactor(static_cast<double>(nUnscaledAverageFontWidth) / static_cast<double>(rFontCandidate.GetFontHeight()));
416             const double fCorrectedAverageFontWidth(static_cast<double>(nScaledAverageFontWidth) * fScaleFactor);
417             tools::Long nCorrectedTextLength(0);
418 
419             { // do in own scope, only need nUnscaledAverageFontWidth
420                 vcl::Font rFontCandidate2(rFontCandidate);
421                 rFontCandidate2.SetAverageFontWidth(static_cast<tools::Long>(fCorrectedAverageFontWidth));
422                 pTempVirtualDevice->SetFont(rFontCandidate2);
423                 nCorrectedTextLength = pTempVirtualDevice->GetTextWidth(rText);
424                 // on failure, use original length
425                 if (!nCorrectedTextLength)
426                     nCorrectedTextLength = nImportedTextLength;
427             }
428 
429             const double fFactorCorrectedText(static_cast<double>(nImportedTextLength) / static_cast<double>(nCorrectedTextLength));
430             const double fFactorCorrectedTextPercent(fabs(1.0 - fFactorCorrectedText) * 100.0);
431 
432             // If FactorCorrectedText fits better than FactorText this is probably
433             // an import of an old EMF/WMF written by LibreOffice on a non-Windows (unx) system
434             // and should be corrected.
435             // Usually in tested cases this lies inside 5% of range, so detecting this just using
436             //  fFactorTextPercent inside 5% -> no old file
437             //  fFactorCorrectedTextPercent inside 5% -> is old file
438             // works not too bad, but there are some strange not so often used fonts where that
439             // values do deviate, so better just compare if old corrected would fit better than
440             // the uncorrected case, that is usually safe.
441             if(fFactorCorrectedTextPercent < fFactorTextPercent)
442             {
443                 maAlternativeFontScales.push_back(fCorrectedAverageFontWidth);
444             }
445             else
446             {
447                 // also push, but negative to remember non-fitting case
448                 maAlternativeFontScales.push_back(-fCorrectedAverageFontWidth);
449             }
450         }
451     }
452 
applyAlternativeFontScale()453     void ScaledFontDetectCorrectHelper::applyAlternativeFontScale()
454     {
455         // make sure last evtl. detected current FontAction gets added to identified cases
456         endCurrentMetaFontAction();
457 
458         // Take final decision to correct FontScaling for this imported Metafile or not.
459         // It is possible to weight positive against negative cases, so to only finally
460         // correct when more positive cases were detected.
461         // But that would be inconsequent and wrong. *If* the detected case is an old import
462         // the whole file was written with wrong FontScale values and all Font actions
463         // need to be corrected. Thus, for now, correct all when there are/is positive
464         // cases detected.
465         // On the other hand it *may* be that for some strange fonts there is a false-positive
466         // in the positive cases, so at least insist on positive cases being more than negative.
467         // Still, do then correct *all* cases.
468         if(!maPositiveIdentifiedCases.empty()
469             && maPositiveIdentifiedCases.size() >= maNegativeIdentifiedCases.size())
470         {
471             for(std::pair<rtl::Reference<MetaFontAction>, double>& rCandidate : maPositiveIdentifiedCases)
472             {
473                 rCandidate.first->correctFontScale(static_cast<tools::Long>(rCandidate.second));
474             }
475             for(std::pair<rtl::Reference<MetaFontAction>, double>& rCandidate : maNegativeIdentifiedCases)
476             {
477                 rCandidate.first->correctFontScale(static_cast<tools::Long>(rCandidate.second));
478             }
479         }
480 
481         maPositiveIdentifiedCases.clear();
482         maNegativeIdentifiedCases.clear();
483     }
484 
ReadColor()485     Color MtfTools::ReadColor()
486     {
487         sal_uInt32 nColor;
488         mpInputStream->ReadUInt32( nColor );
489         Color aColor( COL_BLACK );
490         if ( ( nColor & 0xFFFF0000 ) == 0x01000000 )
491         {
492             size_t index = nColor & 0x0000FFFF;
493             if ( index < maPalette.aPaletteColors.size() )
494                 aColor = maPalette.aPaletteColors[ index ];
495             else
496                 SAL_INFO( "emfio", "\t\t Palette index out of range: " << index );
497         }
498         else
499             aColor = Color( static_cast<sal_uInt8>( nColor ), static_cast<sal_uInt8>( nColor >> 8 ), static_cast<sal_uInt8>( nColor >> 16 ) );
500 
501         SAL_INFO("emfio", "\t\tColor: " << aColor);
502         return aColor;
503     };
504 
ImplScale(const Point & rPoint)505     Point MtfTools::ImplScale(const Point& rPoint) // Hack to set varying defaults for incompletely defined files.
506     {
507         if (!mbIsMapDevSet)
508             return Point(rPoint.X() * UNDOCUMENTED_WIN_RCL_RELATION - mrclFrame.Left(),
509                          rPoint.Y() * UNDOCUMENTED_WIN_RCL_RELATION - mrclFrame.Top());
510         else
511             return rPoint;
512     }
513 
ImplMap(const Point & rPt)514     Point MtfTools::ImplMap( const Point& rPt )
515     {
516         if ( mnWinExtX && mnWinExtY )
517         {
518             double fX = rPt.X();
519             double fY = rPt.Y();
520 
521             double fX2 = fX * maXForm.eM11 + fY * maXForm.eM21 + maXForm.eDx;
522             double fY2 = fX * maXForm.eM12 + fY * maXForm.eM22 + maXForm.eDy;
523 
524             if ( mnGfxMode == GM_COMPATIBLE )
525             {
526                 switch( mnMapMode )
527                 {
528                     case MM_LOENGLISH :
529                     {
530                         fX2 -= mnWinOrgX;
531                         fY2  = mnWinOrgY-fY2;
532                         fX2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH * 10;
533                         fY2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH * 10;
534                         double nDevOrgX = mnDevOrgX;
535                         if (mnPixX)
536                             nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
537                         fX2 += nDevOrgX;
538                         double nDevOrgY = mnDevOrgY;
539                         if (mnPixY)
540                             nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
541                         fY2 += nDevOrgY;
542                     }
543                     break;
544                     case MM_HIENGLISH :
545                     {
546                         fX2 -= mnWinOrgX;
547                         fY2  = mnWinOrgY-fY2;
548                         fX2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH;
549                         fY2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH;
550                         double nDevOrgX = mnDevOrgX;
551                         if (mnPixX)
552                             nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
553                         fX2 += nDevOrgX;
554                         double nDevOrgY = mnDevOrgY;
555                         if (mnPixY)
556                             nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
557                         fY2 += nDevOrgY;
558                     }
559                     break;
560                     case MM_TWIPS:
561                     {
562                         fX2 -= mnWinOrgX;
563                         fY2  = mnWinOrgY-fY2;
564                         fX2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH / MILLIINCH_PER_TWIPS;
565                         fY2 *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH / MILLIINCH_PER_TWIPS;
566                         double nDevOrgX = mnDevOrgX;
567                         if (mnPixX)
568                             nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
569                         fX2 += nDevOrgX;
570                         double nDevOrgY = mnDevOrgY;
571                         if (mnPixY)
572                             nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
573                         fY2 += nDevOrgY;
574                     }
575                     break;
576                     case MM_LOMETRIC :
577                     {
578                         fX2 -= mnWinOrgX;
579                         fY2  = mnWinOrgY-fY2;
580                         fX2 *= 10;
581                         fY2 *= 10;
582                         double nDevOrgX = mnDevOrgX;
583                         if (mnPixX)
584                             nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
585                         fX2 += nDevOrgX;
586                         double nDevOrgY = mnDevOrgY;
587                         if (mnPixY)
588                             nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
589                         fY2 += nDevOrgY;
590                     }
591                     break;
592                     case MM_HIMETRIC : // in hundredth of a millimeter
593                     {
594                         fX2 -= mnWinOrgX;
595                         fY2  = mnWinOrgY-fY2;
596                         double nDevOrgX = mnDevOrgX;
597                         if (mnPixX)
598                             nDevOrgX *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
599                         fX2 += nDevOrgX;
600                         double nDevOrgY = mnDevOrgY;
601                         if (mnPixY)
602                             nDevOrgY *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
603                         fY2 += nDevOrgY;
604                     }
605                     break;
606                     default :
607                     {
608                         if (mnPixX == 0 || mnPixY == 0)
609                         {
610                             SAL_WARN("emfio", "invalid scaling factor");
611                             return Point();
612                         }
613                         else
614                         {
615                             fX2 -= mnWinOrgX;
616                             fY2 -= mnWinOrgY;
617                             if ( mnMapMode != MM_TEXT )
618                             {
619                                 fX2 /= mnWinExtX;
620                                 fY2 /= mnWinExtY;
621                                 fX2 *= mnDevWidth;
622                                 fY2 *= mnDevHeight;
623                             }
624                             fX2 += mnDevOrgX;
625                             fY2 += mnDevOrgY;   // fX2, fY2 now in device units
626                             fX2 *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
627                             fY2 *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
628                         }
629                     }
630                     break;
631                 }
632                 fX2 -= mrclFrame.Left();
633                 fY2 -= mrclFrame.Top();
634             }
635             return Point(basegfx::fround(fX2), basegfx::fround(fY2));
636         }
637         else
638             return Point();
639     };
640 
ImplMap(const Size & rSz,bool bDoWorldTransform)641     Size MtfTools::ImplMap(const Size& rSz, bool bDoWorldTransform)
642     {
643         if ( mnWinExtX && mnWinExtY )
644         {
645             // #i121382# apply the whole WorldTransform, else a rotation will be misinterpreted
646             double fWidth, fHeight;
647             if (bDoWorldTransform)
648             {
649                 fWidth = rSz.Width() * maXForm.eM11 + rSz.Height() * maXForm.eM21;
650                 fHeight = rSz.Width() * maXForm.eM12 + rSz.Height() * maXForm.eM22;
651             }
652             else
653             {
654                 //take the scale, but not the rotation
655                 basegfx::B2DHomMatrix aMatrix(maXForm.eM11, maXForm.eM12, 0,
656                                               maXForm.eM21, maXForm.eM22, 0);
657                 basegfx::B2DTuple aScale, aTranslate;
658                 double fRotate, fShearX;
659                 if (!aMatrix.decompose(aScale, aTranslate, fRotate, fShearX))
660                 {
661                     aScale.setX(1.0);
662                     aScale.setY(1.0);
663                 }
664                 fWidth = rSz.Width() * aScale.getX();
665                 fHeight = rSz.Height() * aScale.getY();
666             }
667 
668             if ( mnGfxMode == GM_COMPATIBLE )
669             {
670                 switch( mnMapMode )
671                 {
672                     case MM_LOENGLISH :
673                     {
674                         fWidth *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH*10;
675                         fHeight*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH*10;
676                     }
677                     break;
678                     case MM_HIENGLISH :
679                     {
680                         fWidth *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH;
681                         fHeight*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH;
682                     }
683                     break;
684                     case MM_LOMETRIC :
685                     {
686                         fWidth *= 10;
687                         fHeight*=-10;
688                     }
689                     break;
690                     case MM_HIMETRIC : // in hundredth of millimeters
691                     {
692                         fHeight *= -1;
693                     }
694                     break;
695                     case MM_TWIPS:
696                     {
697                         fWidth *= HUNDREDTH_MILLIMETERS_PER_MILLIINCH/MILLIINCH_PER_TWIPS;
698                         fHeight*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH/MILLIINCH_PER_TWIPS;
699                     }
700                     break;
701                     default :
702                     {
703                         if (mnPixX == 0 || mnPixY == 0)
704                         {
705                             SAL_WARN("emfio", "invalid scaling factor");
706                             return Size();
707                         }
708                         else
709                         {
710                             if ( mnMapMode != MM_TEXT )
711                             {
712                                 fWidth /= mnWinExtX;
713                                 fHeight /= mnWinExtY;
714                                 fWidth *= mnDevWidth;
715                                 fHeight *= mnDevHeight;
716                             }
717                             fWidth *= static_cast<double>(mnMillX) * 100.0 / static_cast<double>(mnPixX);
718                             fHeight *= static_cast<double>(mnMillY) * 100.0 / static_cast<double>(mnPixY);
719                         }
720                     }
721                     break;
722                 }
723             }
724             return Size(basegfx::fround(fWidth), basegfx::fround(fHeight));
725         }
726         else
727             return Size();
728     }
729 
ImplMap(const tools::Rectangle & rRect)730     tools::Rectangle MtfTools::ImplMap( const tools::Rectangle& rRect )
731     {
732         tools::Rectangle aRect;
733         aRect.SetPos(ImplMap(rRect.TopLeft()));
734         aRect.SaturatingSetSize(ImplMap(rRect.GetSize()));
735         return aRect;
736     }
737 
ImplMap(vcl::Font & rFont)738     void MtfTools::ImplMap( vcl::Font& rFont )
739     {
740         // !!! HACK: we now always set the width to zero because the OS width is interpreted differently;
741         // must later be made portable in SV (KA 1996-02-08)
742         Size  aFontSize = ImplMap (rFont.GetFontSize(), false);
743 
744         const auto nHeight = aFontSize.Height();
745         if (nHeight < 0)
746             aFontSize.setHeight( o3tl::saturating_toggle_sign(nHeight) );
747 
748         rFont.SetFontSize( aFontSize );
749 
750         sal_Int32 nResult;
751         const bool bFail = o3tl::checked_multiply(mnWinExtX, mnWinExtY, nResult);
752         if (!bFail && nResult < 0)
753             rFont.SetOrientation( 3600_deg10 - rFont.GetOrientation() );
754     }
755 
ImplMap(tools::Polygon & rPolygon)756     tools::Polygon& MtfTools::ImplMap( tools::Polygon& rPolygon )
757     {
758         sal_uInt16 nPoints = rPolygon.GetSize();
759         for ( sal_uInt16 i = 0; i < nPoints; i++ )
760         {
761             rPolygon[ i ] = ImplMap( rPolygon[ i ] );
762         }
763         return rPolygon;
764     }
765 
ImplScale(tools::Polygon & rPolygon)766     void MtfTools::ImplScale( tools::Polygon& rPolygon )
767     {
768         sal_uInt16 nPoints = rPolygon.GetSize();
769         for ( sal_uInt16 i = 0; i < nPoints; i++ )
770         {
771             rPolygon[ i ] = ImplScale( rPolygon[ i ] );
772         }
773     }
774 
ImplScale(tools::PolyPolygon & rPolyPolygon)775     tools::PolyPolygon& MtfTools::ImplScale( tools::PolyPolygon& rPolyPolygon )
776     {
777         sal_uInt16 nPolys = rPolyPolygon.Count();
778         for (sal_uInt16 i = 0; i < nPolys; ++i)
779         {
780             ImplScale(rPolyPolygon[i]);
781         }
782         return rPolyPolygon;
783     }
784 
ImplMap(tools::PolyPolygon & rPolyPolygon)785     tools::PolyPolygon& MtfTools::ImplMap( tools::PolyPolygon& rPolyPolygon )
786     {
787         sal_uInt16 nPolys = rPolyPolygon.Count();
788         for ( sal_uInt16 i = 0; i < nPolys; ImplMap( rPolyPolygon[ i++ ] ) ) ;
789         return rPolyPolygon;
790     }
791 
SelectObject(sal_uInt32 nIndex)792     void MtfTools::SelectObject( sal_uInt32 nIndex )
793     {
794         if ( nIndex & ENHMETA_STOCK_OBJECT )
795         {
796             SAL_INFO ( "emfio", "\t\t ENHMETA_STOCK_OBJECT, StockObject Enumeration: 0x" << std::hex  << nIndex );
797             sal_uInt16 nStockId = static_cast<sal_uInt8>(nIndex);
798             switch( nStockId )
799             {
800                 case WHITE_BRUSH :
801                 {
802                     maFillStyle = WinMtfFillStyle( COL_WHITE );
803                     mbFillStyleSelected = true;
804                 }
805                 break;
806                 case LTGRAY_BRUSH :
807                 {
808                     maFillStyle = WinMtfFillStyle( COL_LIGHTGRAY );
809                     mbFillStyleSelected = true;
810                 }
811                 break;
812                 case GRAY_BRUSH :
813                 {
814                     maFillStyle = WinMtfFillStyle( COL_GRAY );
815                     mbFillStyleSelected = true;
816                 }
817                 break;
818                 case DKGRAY_BRUSH :
819                 {
820                     maFillStyle = WinMtfFillStyle( COL_GRAY7 );
821                     mbFillStyleSelected = true;
822                 }
823                 break;
824                 case BLACK_BRUSH :
825                 {
826                     maFillStyle = WinMtfFillStyle( COL_BLACK );
827                     mbFillStyleSelected = true;
828                 }
829                 break;
830                 case NULL_BRUSH :
831                 {
832                    maFillStyle = WinMtfFillStyle( COL_TRANSPARENT, true );
833                    mbFillStyleSelected = true;
834                 }
835                 break;
836                 case WHITE_PEN :
837                 {
838                     maLineStyle = WinMtfLineStyle( COL_WHITE );
839                 }
840                 break;
841                 case BLACK_PEN :
842                 {
843                     maLineStyle = WinMtfLineStyle( COL_BLACK );
844                 }
845                 break;
846                 case NULL_PEN :
847                 {
848                     maLineStyle = WinMtfLineStyle( COL_TRANSPARENT, true );
849                 }
850                 break;
851                 default:
852                 break;
853             }
854         }
855         else
856         {
857             nIndex &= 0xffff;       // safety check: don't allow index to be > 65535
858 
859             GDIObj *pGDIObj = nullptr;
860 
861             if ( nIndex < mvGDIObj.size() )
862                 pGDIObj = mvGDIObj[ nIndex ].get();
863 
864             if ( pGDIObj )
865             {
866 
867                 SAL_INFO ( "emfio", "\t\t Index: " << nIndex );
868                 if (const auto pen = dynamic_cast<WinMtfLineStyle*>(pGDIObj))
869                 {
870                     maLineStyle = *pen;
871                     SAL_INFO ( "emfio", "\t Line Style, Color: 0x" << std::hex << maLineStyle.aLineColor
872                                         << ", Weight: " << maLineStyle.aLineInfo.GetWidth() );
873                 }
874                 else if (const auto brush = dynamic_cast<WinMtfFillStyle*>(
875                              pGDIObj))
876                 {
877                     maFillStyle = *brush;
878                     mbFillStyleSelected = true;
879                     SAL_INFO("emfio", "\t\tBrush Object, Index: " << nIndex << ", Color: " << maFillStyle.aFillColor);
880                 }
881                 else if (const auto font = dynamic_cast<WinMtfFontStyle*>(
882                              pGDIObj))
883                 {
884                     maFont = font->aFont;
885                     SAL_INFO("emfio", "\t\tFont Object, Index: " << nIndex << ", Font: " << maFont.GetFamilyName() << " " << maFont.GetStyleName());
886                 }
887                 else if (const auto palette = dynamic_cast<WinMtfPalette*>(
888                              pGDIObj))
889                 {
890                     maPalette = palette->aPaletteColors;
891                     SAL_INFO("emfio", "\t\tPalette Object, Index: " << nIndex << ", Number of colours: " << maPalette.aPaletteColors.size() );
892                 }
893             }
894             else
895             {
896                 SAL_WARN("emfio", "Warning: Unable to find Object with index:" << nIndex);
897             }
898         }
899     }
900 
SetTextLayoutMode(ComplexTextLayoutFlags nTextLayoutMode)901     void MtfTools::SetTextLayoutMode( ComplexTextLayoutFlags nTextLayoutMode )
902     {
903         mnTextLayoutMode = nTextLayoutMode;
904     }
905 
SetBkMode(BkMode nMode)906     void MtfTools::SetBkMode( BkMode nMode )
907     {
908         mnBkMode = nMode;
909     }
910 
SetBkColor(const Color & rColor)911     void MtfTools::SetBkColor( const Color& rColor )
912     {
913         maBkColor = rColor;
914     }
915 
SetTextColor(const Color & rColor)916     void MtfTools::SetTextColor( const Color& rColor )
917     {
918         maTextColor = rColor;
919     }
920 
SetTextAlign(sal_uInt32 nAlign)921     void MtfTools::SetTextAlign( sal_uInt32 nAlign )
922     {
923         mnTextAlign = nAlign;
924     }
925 
ImplResizeObjectArry(sal_uInt32 nNewEntrys)926     void MtfTools::ImplResizeObjectArry( sal_uInt32 nNewEntrys )
927     {
928         mvGDIObj.resize(nNewEntrys);
929     }
930 
ImplDrawClippedPolyPolygon(const tools::PolyPolygon & rPolyPoly)931     void MtfTools::ImplDrawClippedPolyPolygon( const tools::PolyPolygon& rPolyPoly )
932     {
933         if ( !rPolyPoly.Count() )
934             return;
935 
936         ImplSetNonPersistentLineColorTransparenz();
937         if ( rPolyPoly.Count() == 1 )
938         {
939             if ( rPolyPoly.IsRect() )
940                 mpGDIMetaFile->AddAction( new MetaRectAction( rPolyPoly.GetBoundRect() ) );
941             else
942             {
943                 tools::Polygon aPoly( rPolyPoly[ 0 ] );
944                 sal_uInt16 nCount = aPoly.GetSize();
945                 if ( nCount )
946                 {
947                     if ( aPoly[ nCount - 1 ] != aPoly[ 0 ] )
948                     {
949                         Point aPoint( aPoly[ 0 ] );
950                         aPoly.Insert( nCount, aPoint );
951                     }
952                     mpGDIMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
953                 }
954             }
955         }
956         else
957             mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPoly ) );
958     }
959 
CreateObject(std::unique_ptr<GDIObj> pObject)960     void MtfTools::CreateObject( std::unique_ptr<GDIObj> pObject )
961     {
962         if ( pObject )
963         {
964             const auto pLineStyle = dynamic_cast<WinMtfLineStyle*>(pObject.get());
965             const auto pFontStyle = dynamic_cast<WinMtfFontStyle*>(pObject.get());
966 
967             if ( pFontStyle )
968             {
969                 if (pFontStyle->aFont.GetFontHeight() == 0)
970                     pFontStyle->aFont.SetFontHeight(423);
971                 ImplMap(pFontStyle->aFont); // defaulting to 12pt
972             }
973             else if ( pLineStyle )
974             {
975                 Size aSize(pLineStyle->aLineInfo.GetWidth(), 0);
976                 aSize = ImplMap(aSize);
977                 pLineStyle->aLineInfo.SetWidth(aSize.Width());
978             }
979         }
980         std::vector<std::unique_ptr<GDIObj>>::size_type nIndex;
981         for ( nIndex = 0; nIndex < mvGDIObj.size(); nIndex++ )
982         {
983             if ( !mvGDIObj[ nIndex ] )
984                 break;
985         }
986         if ( nIndex == mvGDIObj.size() )
987             ImplResizeObjectArry( mvGDIObj.size() + 16 );
988 
989         mvGDIObj[ nIndex ] = std::move(pObject);
990     }
991 
CreateObjectIndexed(sal_uInt32 nIndex,std::unique_ptr<GDIObj> pObject)992     void MtfTools::CreateObjectIndexed( sal_uInt32 nIndex, std::unique_ptr<GDIObj> pObject )
993     {
994         if ( ( nIndex & ENHMETA_STOCK_OBJECT ) != 0 )
995             return;
996 
997         nIndex &= 0xffff;       // safety check: do not allow index to be > 65535
998         if ( pObject )
999         {
1000             const auto pLineStyle = dynamic_cast<WinMtfLineStyle*>(pObject.get());
1001             const auto pFontStyle = dynamic_cast<WinMtfFontStyle*>(pObject.get());
1002             if ( pFontStyle )
1003             {
1004                 if (pFontStyle->aFont.GetFontHeight() == 0)
1005                     pFontStyle->aFont.SetFontHeight(423);
1006                 ImplMap(pFontStyle->aFont);
1007             }
1008             else if ( pLineStyle )
1009             {
1010                 Size aSize(pLineStyle->aLineInfo.GetWidth(), 0);
1011                 pLineStyle->aLineInfo.SetWidth( ImplMap(aSize).Width() );
1012 
1013                 if ( pLineStyle->aLineInfo.GetStyle() == LineStyle::Dash )
1014                 {
1015                     aSize.AdjustWidth(1 );
1016                     tools::Long nDotLen = ImplMap( aSize ).Width();
1017                     pLineStyle->aLineInfo.SetDistance( nDotLen );
1018                     pLineStyle->aLineInfo.SetDotLen( nDotLen );
1019                     pLineStyle->aLineInfo.SetDashLen( nDotLen * 3 );
1020                 }
1021             }
1022         }
1023         if ( nIndex >= mvGDIObj.size() )
1024             ImplResizeObjectArry( nIndex + 16 );
1025 
1026         mvGDIObj[ nIndex ] = std::move(pObject);
1027     }
1028 
CreateObject()1029     void MtfTools::CreateObject()
1030     {
1031         CreateObject(std::make_unique<GDIObj>());
1032     }
1033 
DeleteObject(sal_uInt32 nIndex)1034     void MtfTools::DeleteObject( sal_uInt32 nIndex )
1035     {
1036         if ( ( nIndex & ENHMETA_STOCK_OBJECT ) == 0 )
1037         {
1038             if ( nIndex < mvGDIObj.size() )
1039             {
1040                 mvGDIObj[ nIndex ].reset();
1041             }
1042         }
1043     }
1044 
IntersectClipRect(const tools::Rectangle & rRect)1045     void MtfTools::IntersectClipRect( const tools::Rectangle& rRect )
1046     {
1047         if (utl::ConfigManager::IsFuzzing())
1048             return;
1049         mbClipNeedsUpdate=true;
1050         if ((rRect.Left()-rRect.Right()==0) && (rRect.Top()-rRect.Bottom()==0))
1051         {
1052             return; // empty rectangles cause trouble
1053         }
1054         tools::Polygon aPoly( rRect );
1055         const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) );
1056         maClipPath.intersectClip( aPolyPolyRect.getB2DPolyPolygon() );
1057     }
1058 
ExcludeClipRect(const tools::Rectangle & rRect)1059     void MtfTools::ExcludeClipRect( const tools::Rectangle& rRect )
1060     {
1061         if (utl::ConfigManager::IsFuzzing())
1062             return;
1063         mbClipNeedsUpdate=true;
1064         tools::Polygon aPoly( rRect );
1065         const tools::PolyPolygon aPolyPolyRect( ImplMap( aPoly ) );
1066         maClipPath.excludeClip( aPolyPolyRect.getB2DPolyPolygon() );
1067     }
1068 
MoveClipRegion(const Size & rSize)1069     void MtfTools::MoveClipRegion( const Size& rSize )
1070     {
1071         if (utl::ConfigManager::IsFuzzing())
1072             return;
1073         mbClipNeedsUpdate=true;
1074         maClipPath.moveClipRegion( ImplMap( rSize ) );
1075     }
1076 
SetClipPath(const tools::PolyPolygon & rPolyPolygon,sal_Int32 nClippingMode,bool bIsMapped)1077     void MtfTools::SetClipPath( const tools::PolyPolygon& rPolyPolygon, sal_Int32 nClippingMode, bool bIsMapped )
1078     {
1079         if (utl::ConfigManager::IsFuzzing())
1080             return;
1081         mbClipNeedsUpdate = true;
1082         tools::PolyPolygon aPolyPolygon(rPolyPolygon);
1083 
1084         if (!bIsMapped)
1085         {
1086             if (!mbIsMapDevSet && (mnMapMode == MM_ISOTROPIC || mnMapMode == MM_ANISOTROPIC))
1087                 aPolyPolygon = ImplScale(aPolyPolygon);
1088             else
1089                 aPolyPolygon = ImplMap(aPolyPolygon);
1090         }
1091         maClipPath.setClipPath(aPolyPolygon.getB2DPolyPolygon(), nClippingMode);
1092     }
1093 
SetDefaultClipPath()1094     void MtfTools::SetDefaultClipPath()
1095     {
1096         mbClipNeedsUpdate = true;
1097         maClipPath.setDefaultClipPath();
1098     }
1099 
MtfTools(GDIMetaFile & rGDIMetaFile,SvStream & rStreamWMF)1100     MtfTools::MtfTools( GDIMetaFile& rGDIMetaFile, SvStream& rStreamWMF)
1101     :   maPathObj(),
1102         maClipPath(),
1103         maLatestLineStyle(),
1104         maLineStyle(),
1105         maNopLineStyle(),
1106         maLatestFillStyle(),
1107         maFillStyle(),
1108         maNopFillStyle(),
1109         maPalette(),
1110         maLatestFont(),
1111         maFont(),
1112         mnLatestTextAlign(90),
1113         mnTextAlign(TA_LEFT | TA_TOP | TA_NOUPDATECP),
1114         maLatestTextColor(),
1115         maTextColor(),
1116         maLatestBkColor(ColorTransparency, 0x12345678),
1117         maBkColor(COL_WHITE),
1118         mnLatestTextLayoutMode(ComplexTextLayoutFlags::Default),
1119         mnTextLayoutMode(ComplexTextLayoutFlags::Default),
1120         mnLatestBkMode(BkMode::NONE),
1121         mnBkMode(BkMode::OPAQUE),
1122         meLatestRasterOp(RasterOp::Invert),
1123         meRasterOp(RasterOp::OverPaint),
1124         mvGDIObj(),
1125         maActPos(),
1126         mnRop(),
1127         mvSaveStack(),
1128         mnGfxMode(GM_COMPATIBLE),
1129         mnMapMode(MM_TEXT),
1130         maXForm(),
1131         mnDevOrgX(0),
1132         mnDevOrgY(0),
1133         mnDevWidth(1),
1134         mnDevHeight(1),
1135         mnWinOrgX(0),
1136         mnWinOrgY(0),
1137         mnWinExtX(1),
1138         mnWinExtY(1),
1139         mnPixX(100),
1140         mnPixY(100),
1141         mnMillX(1),
1142         mnMillY(1),
1143         mrclFrame(),
1144         mrclBounds(),
1145         mpGDIMetaFile(&rGDIMetaFile),
1146         mpInputStream(&rStreamWMF),
1147         mnStartPos(0),
1148         mnEndPos(0),
1149         maBmpSaveList(),
1150         maScaledFontHelper(),
1151         mbNopMode(false),
1152         mbFillStyleSelected(false),
1153         mbClipNeedsUpdate(true),
1154         mbComplexClip(false),
1155         mbIsMapWinSet(false),
1156         mbIsMapDevSet(false)
1157     {
1158         SvLockBytes *pLB = mpInputStream->GetLockBytes();
1159 
1160         if (pLB)
1161         {
1162             pLB->SetSynchronMode();
1163         }
1164 
1165         mnStartPos = mpInputStream->Tell();
1166         SetDevOrg(Point());
1167 
1168         mpGDIMetaFile->AddAction( new MetaPushAction( PushFlags::CLIPREGION ) ); // The original clipregion has to be on top
1169                                                                                  // of the stack so it can always be restored
1170                                                                                  // this is necessary to be able to support
1171                                                                                  // SetClipRgn( NULL ) and similar ClipRgn actions (SJ)
1172 
1173         maFont.SetFamilyName( "Arial" );                                         // sj: #i57205#, we do have some scaling problems if using
1174         maFont.SetCharSet( RTL_TEXTENCODING_MS_1252 );                           // the default font then most times a x11 font is used, we
1175         maFont.SetFontHeight( 423 );                                      // will prevent this defining a font
1176 
1177         maLatestLineStyle.aLineColor = Color( 0x12, 0x34, 0x56 );
1178         maLatestFillStyle.aFillColor = Color( 0x12, 0x34, 0x56 );
1179 
1180         mnRop = WMFRasterOp::Black;
1181         meRasterOp = RasterOp::OverPaint;
1182         mpGDIMetaFile->AddAction( new MetaRasterOpAction( RasterOp::OverPaint ) );
1183     }
1184 
~MtfTools()1185     MtfTools::~MtfTools() COVERITY_NOEXCEPT_FALSE
1186     {
1187         mpGDIMetaFile->AddAction( new MetaPopAction() );
1188         mpGDIMetaFile->SetPrefMapMode(MapMode(MapUnit::Map100thMM));
1189         if ( mrclFrame.IsEmpty() )
1190             mpGDIMetaFile->SetPrefSize( Size( mnDevWidth, mnDevHeight ) );
1191         else
1192             mpGDIMetaFile->SetPrefSize( mrclFrame.GetSize() );
1193     }
1194 
UpdateClipRegion()1195     void MtfTools::UpdateClipRegion()
1196     {
1197         if (!mbClipNeedsUpdate)
1198             return;
1199 
1200         mbClipNeedsUpdate = false;
1201         mbComplexClip = false;
1202 
1203         mpGDIMetaFile->AddAction( new MetaPopAction() );                    // taking the original clipregion
1204         mpGDIMetaFile->AddAction( new MetaPushAction( PushFlags::CLIPREGION ) );
1205 
1206         // skip for 'no clipping at all' case
1207         if( maClipPath.isEmpty() )
1208             return;
1209 
1210         const basegfx::B2DPolyPolygon& rClipPoly( maClipPath.getClipPath() );
1211 
1212         mbComplexClip = rClipPoly.count() > 1
1213             || !basegfx::utils::isRectangle(rClipPoly);
1214 
1215         // This makes cases like tdf#45820 work in reasonable time.
1216         if (mbComplexClip)
1217         {
1218             mpGDIMetaFile->AddAction(
1219                 new MetaISectRegionClipRegionAction(
1220                     vcl::Region(rClipPoly)));
1221             mbComplexClip = false;
1222         }
1223         else
1224         {
1225             mpGDIMetaFile->AddAction(
1226                 new MetaISectRectClipRegionAction(
1227                     vcl::unotools::rectangleFromB2DRectangle(
1228                         rClipPoly.getB2DRange())));
1229         }
1230     }
1231 
ImplSetNonPersistentLineColorTransparenz()1232     void MtfTools::ImplSetNonPersistentLineColorTransparenz()
1233     {
1234         WinMtfLineStyle aTransparentLine( COL_TRANSPARENT, true );
1235         if ( ! ( maLatestLineStyle == aTransparentLine ) )
1236         {
1237             maLatestLineStyle = aTransparentLine;
1238             mpGDIMetaFile->AddAction( new MetaLineColorAction( aTransparentLine.aLineColor, !aTransparentLine.bTransparent ) );
1239         }
1240     }
1241 
UpdateLineStyle()1242     void MtfTools::UpdateLineStyle()
1243     {
1244         if (!( maLatestLineStyle == maLineStyle ) )
1245         {
1246             maLatestLineStyle = maLineStyle;
1247             mpGDIMetaFile->AddAction( new MetaLineColorAction( maLineStyle.aLineColor, !maLineStyle.bTransparent ) );
1248         }
1249     }
1250 
UpdateFillStyle()1251     void MtfTools::UpdateFillStyle()
1252     {
1253         if ( !mbFillStyleSelected )     // SJ: #i57205# taking care of bkcolor if no brush is selected
1254             maFillStyle = WinMtfFillStyle( maBkColor, mnBkMode == BkMode::Transparent );
1255         if (!( maLatestFillStyle == maFillStyle ) )
1256         {
1257             maLatestFillStyle = maFillStyle;
1258             if (maFillStyle.aType == WinMtfFillStyleType::Solid)
1259                 mpGDIMetaFile->AddAction( new MetaFillColorAction( maFillStyle.aFillColor, !maFillStyle.bTransparent ) );
1260         }
1261     }
1262 
SetRasterOp(WMFRasterOp nRasterOp)1263     WMFRasterOp MtfTools::SetRasterOp( WMFRasterOp nRasterOp )
1264     {
1265         WMFRasterOp nRetROP = mnRop;
1266         if ( nRasterOp != mnRop )
1267         {
1268             mnRop = nRasterOp;
1269 
1270             if ( mbNopMode && ( nRasterOp != WMFRasterOp::Nop ) )
1271             {   // changing modes from WMFRasterOp::Nop so set pen and brush
1272                 maFillStyle = maNopFillStyle;
1273                 maLineStyle = maNopLineStyle;
1274                 mbNopMode = false;
1275             }
1276             switch( nRasterOp )
1277             {
1278                 case WMFRasterOp::Not:
1279                     meRasterOp = RasterOp::Invert;
1280                 break;
1281 
1282                 case WMFRasterOp::XorPen:
1283                     meRasterOp = RasterOp::Xor;
1284                 break;
1285 
1286                 case WMFRasterOp::Nop:
1287                 {
1288                     meRasterOp = RasterOp::OverPaint;
1289                     if( !mbNopMode )
1290                     {
1291                         maNopFillStyle = maFillStyle;
1292                         maNopLineStyle = maLineStyle;
1293                         maFillStyle = WinMtfFillStyle( COL_TRANSPARENT, true );
1294                         maLineStyle = WinMtfLineStyle( COL_TRANSPARENT, true );
1295                         mbNopMode = true;
1296                     }
1297                 }
1298                 break;
1299 
1300                 default:
1301                     meRasterOp = RasterOp::OverPaint;
1302                 break;
1303             }
1304         }
1305         if ( nRetROP != nRasterOp )
1306             mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) );
1307         return nRetROP;
1308     };
1309 
StrokeAndFillPath(bool bStroke,bool bFill)1310     void MtfTools::StrokeAndFillPath( bool bStroke, bool bFill )
1311     {
1312         if ( !maPathObj.Count() )
1313             return;
1314 
1315         UpdateClipRegion();
1316         UpdateLineStyle();
1317         UpdateFillStyle();
1318         if ( bFill )
1319         {
1320             if ( !bStroke )
1321             {
1322                 mpGDIMetaFile->AddAction( new MetaPushAction( PushFlags::LINECOLOR ) );
1323                 mpGDIMetaFile->AddAction( new MetaLineColorAction( Color(), false ) );
1324             }
1325             if ( maPathObj.Count() == 1 )
1326                 mpGDIMetaFile->AddAction( new MetaPolygonAction( maPathObj.GetObject( 0 ) ) );
1327             else
1328                 mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( maPathObj ) );
1329 
1330             if ( !bStroke )
1331                 mpGDIMetaFile->AddAction( new MetaPopAction() );
1332         }
1333         // tdf#142014 By default the stroke is made with hairline. If width is bigger, we need to use PolyLineAction
1334         if ( bStroke )
1335         {
1336             // bFill is drawing hairstyle line. So we need to draw it only when the width is different than 0
1337             if ( !bFill || maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1338             {
1339                 sal_uInt16 i, nCount = maPathObj.Count();
1340                 for ( i = 0; i < nCount; i++ )
1341                     mpGDIMetaFile->AddAction( new MetaPolyLineAction( maPathObj[ i ], maLineStyle.aLineInfo ) );
1342             }
1343         }
1344         ClearPath();
1345     }
1346 
DrawPixel(const Point & rSource,const Color & rColor)1347     void MtfTools::DrawPixel( const Point& rSource, const Color& rColor )
1348     {
1349         mpGDIMetaFile->AddAction( new MetaPixelAction( ImplMap( rSource), rColor ) );
1350     }
1351 
MoveTo(const Point & rPoint,bool bRecordPath)1352     void MtfTools::MoveTo( const Point& rPoint, bool bRecordPath )
1353     {
1354         Point aDest( ImplMap( rPoint ) );
1355         if ( bRecordPath )
1356         {
1357             // fdo#57353 create new subpath for subsequent moves
1358             if ( maPathObj.Count() )
1359                 if ( maPathObj[ maPathObj.Count() - 1 ].GetSize() )
1360                     maPathObj.Insert( tools::Polygon() );
1361             maPathObj.AddPoint( aDest );
1362         }
1363         maActPos = aDest;
1364     }
1365 
LineTo(const Point & rPoint,bool bRecordPath)1366     void MtfTools::LineTo( const Point& rPoint, bool bRecordPath )
1367     {
1368         UpdateClipRegion();
1369         Point aDest( ImplMap( rPoint ) );
1370         if ( bRecordPath )
1371             maPathObj.AddPoint( aDest );
1372         else
1373         {
1374             UpdateLineStyle();
1375             mpGDIMetaFile->AddAction( new MetaLineAction( maActPos, aDest, maLineStyle.aLineInfo ) );
1376         }
1377         maActPos = aDest;
1378     }
1379 
DrawRectWithBGColor(const tools::Rectangle & rRect)1380     void MtfTools::DrawRectWithBGColor(const tools::Rectangle& rRect)
1381     {
1382         WinMtfFillStyle aFillStyleBackup = maFillStyle;
1383         bool            aTransparentBackup = maLineStyle.bTransparent;
1384         BkMode          mnBkModeBackup = mnBkMode;
1385 
1386         const tools::Polygon aPoly( rRect );
1387         maLineStyle.bTransparent = true;
1388         maFillStyle = maBkColor;
1389         mnBkMode = BkMode::OPAQUE;
1390         ImplSetNonPersistentLineColorTransparenz();
1391         DrawPolygon(aPoly, false);
1392         mnBkMode = mnBkModeBackup; // The rectangle needs to be always drawned even if mode is transparent
1393         maFillStyle = aFillStyleBackup;
1394         maLineStyle.bTransparent = aTransparentBackup;
1395     }
1396 
DrawRect(const tools::Rectangle & rRect,bool bEdge)1397     void MtfTools::DrawRect( const tools::Rectangle& rRect, bool bEdge )
1398     {
1399         UpdateClipRegion();
1400         UpdateFillStyle();
1401 
1402         if ( mbComplexClip )
1403         {
1404             tools::Polygon aPoly( ImplMap( rRect ) );
1405             tools::PolyPolygon aPolyPolyRect( aPoly );
1406             tools::PolyPolygon aDest;
1407             tools::PolyPolygon(maClipPath.getClipPath()).GetIntersection( aPolyPolyRect, aDest );
1408             ImplDrawClippedPolyPolygon( aDest );
1409         }
1410         else
1411         {
1412             if ( bEdge )
1413             {
1414                 if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1415                 {
1416                     ImplSetNonPersistentLineColorTransparenz();
1417                     mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) );
1418                     UpdateLineStyle();
1419                     mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( ImplMap( rRect ) ),maLineStyle.aLineInfo ) );
1420                 }
1421                 else
1422                 {
1423                     UpdateLineStyle();
1424                     mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) );
1425                 }
1426             }
1427             else
1428             {
1429                 ImplSetNonPersistentLineColorTransparenz();
1430                 mpGDIMetaFile->AddAction( new MetaRectAction( ImplMap( rRect ) ) );
1431             }
1432         }
1433     }
1434 
DrawRoundRect(const tools::Rectangle & rRect,const Size & rSize)1435     void MtfTools::DrawRoundRect( const tools::Rectangle& rRect, const Size& rSize )
1436     {
1437         UpdateClipRegion();
1438         UpdateLineStyle();
1439         UpdateFillStyle();
1440         mpGDIMetaFile->AddAction( new MetaRoundRectAction( ImplMap( rRect ), std::abs( ImplMap( rSize ).Width() ), std::abs( ImplMap( rSize ).Height() ) ) );
1441         // tdf#142139 Wrong line width during WMF import
1442         if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1443         {
1444             tools::Polygon aRoundRectPoly( rRect, rSize.Width(), rSize.Height() );
1445             mpGDIMetaFile->AddAction( new MetaPolyLineAction( ImplMap( aRoundRectPoly ), maLineStyle.aLineInfo ) );
1446         }
1447     }
1448 
DrawEllipse(const tools::Rectangle & rRect)1449     void MtfTools::DrawEllipse( const tools::Rectangle& rRect )
1450     {
1451         UpdateClipRegion();
1452         UpdateFillStyle();
1453 
1454         if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1455         {
1456             Point aCenter( ImplMap( rRect.Center() ) );
1457             Size  aRad( ImplMap( Size( rRect.GetWidth() / 2, rRect.GetHeight() / 2 ) ) );
1458 
1459             ImplSetNonPersistentLineColorTransparenz();
1460             mpGDIMetaFile->AddAction( new MetaEllipseAction( ImplMap( rRect ) ) );
1461             UpdateLineStyle();
1462             mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aCenter, aRad.Width(), aRad.Height() ), maLineStyle.aLineInfo ) );
1463         }
1464         else
1465         {
1466             UpdateLineStyle();
1467             mpGDIMetaFile->AddAction( new MetaEllipseAction( ImplMap( rRect ) ) );
1468         }
1469     }
1470 
DrawArc(const tools::Rectangle & rRect,const Point & rStart,const Point & rEnd,bool bTo)1471     void MtfTools::DrawArc( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd, bool bTo )
1472     {
1473         UpdateClipRegion();
1474         UpdateLineStyle();
1475         UpdateFillStyle();
1476 
1477         tools::Rectangle   aRect( ImplMap( rRect ) );
1478         Point       aStart( ImplMap( rStart ) );
1479         Point       aEnd( ImplMap( rEnd ) );
1480 
1481         if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1482         {
1483             if ( aStart == aEnd )
1484             {   // SJ: #i53768# if start & end is identical, then we have to draw a full ellipse
1485                 Point aCenter( aRect.Center() );
1486                 Size  aRad( aRect.GetWidth() / 2, aRect.GetHeight() / 2 );
1487 
1488                 mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aCenter, aRad.Width(), aRad.Height() ), maLineStyle.aLineInfo ) );
1489             }
1490             else
1491                 mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Arc ), maLineStyle.aLineInfo ) );
1492         }
1493         else
1494             mpGDIMetaFile->AddAction( new MetaArcAction( aRect, aStart, aEnd ) );
1495 
1496         if ( bTo )
1497             maActPos = aEnd;
1498     }
1499 
DrawPie(const tools::Rectangle & rRect,const Point & rStart,const Point & rEnd)1500     void MtfTools::DrawPie( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd )
1501     {
1502         UpdateClipRegion();
1503         UpdateFillStyle();
1504 
1505         tools::Rectangle   aRect( ImplMap( rRect ) );
1506         Point       aStart( ImplMap( rStart ) );
1507         Point       aEnd( ImplMap( rEnd ) );
1508 
1509         if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1510         {
1511             ImplSetNonPersistentLineColorTransparenz();
1512             mpGDIMetaFile->AddAction( new MetaPieAction( aRect, aStart, aEnd ) );
1513             UpdateLineStyle();
1514             mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Pie ), maLineStyle.aLineInfo ) );
1515         }
1516         else
1517         {
1518             UpdateLineStyle();
1519             mpGDIMetaFile->AddAction( new MetaPieAction( aRect, aStart, aEnd ) );
1520         }
1521     }
1522 
DrawChord(const tools::Rectangle & rRect,const Point & rStart,const Point & rEnd)1523     void MtfTools::DrawChord( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd )
1524     {
1525         UpdateClipRegion();
1526         UpdateFillStyle();
1527 
1528         tools::Rectangle   aRect( ImplMap( rRect ) );
1529         Point       aStart( ImplMap( rStart ) );
1530         Point       aEnd( ImplMap( rEnd ) );
1531 
1532         if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1533         {
1534             ImplSetNonPersistentLineColorTransparenz();
1535             mpGDIMetaFile->AddAction( new MetaChordAction( aRect, aStart, aEnd ) );
1536             UpdateLineStyle();
1537             mpGDIMetaFile->AddAction( new MetaPolyLineAction( tools::Polygon( aRect, aStart, aEnd, PolyStyle::Chord ), maLineStyle.aLineInfo ) );
1538         }
1539         else
1540         {
1541             UpdateLineStyle();
1542             mpGDIMetaFile->AddAction( new MetaChordAction( aRect, aStart, aEnd ) );
1543         }
1544     }
1545 
DrawPolygon(tools::Polygon rPolygon,bool bRecordPath)1546     void MtfTools::DrawPolygon( tools::Polygon rPolygon, bool bRecordPath )
1547     {
1548         UpdateClipRegion();
1549         ImplMap( rPolygon );
1550         if ( bRecordPath )
1551             maPathObj.AddPolygon( rPolygon );
1552         else
1553         {
1554             UpdateFillStyle();
1555 
1556             if ( mbComplexClip )
1557             {
1558                 tools::PolyPolygon aPolyPoly( rPolygon );
1559                 auto tmp = maClipPath.getClip();
1560                 tmp.intersectPolyPolygon(aPolyPoly.getB2DPolyPolygon());
1561                 tools::PolyPolygon aDest(tmp.getClipPoly());
1562                 ImplDrawClippedPolyPolygon( aDest );
1563             }
1564             else
1565             {
1566                 if ( maLineStyle.aLineInfo.GetWidth() || ( maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash ) )
1567                 {
1568                     sal_uInt16 nCount = rPolygon.GetSize();
1569                     if ( nCount )
1570                     {
1571                         if ( rPolygon[ nCount - 1 ] != rPolygon[ 0 ] )
1572                         {
1573                             Point aPoint( rPolygon[ 0 ] );
1574                             rPolygon.Insert( nCount, aPoint );
1575                         }
1576                     }
1577                     ImplSetNonPersistentLineColorTransparenz();
1578                     mpGDIMetaFile->AddAction( new MetaPolygonAction( rPolygon ) );
1579                     UpdateLineStyle();
1580                     mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) );
1581                 }
1582                 else
1583                 {
1584                     UpdateLineStyle();
1585 
1586                     if (maLatestFillStyle.aType != WinMtfFillStyleType::Pattern)
1587                         mpGDIMetaFile->AddAction( new MetaPolygonAction( rPolygon ) );
1588                     else {
1589                         SvtGraphicFill aFill( tools::PolyPolygon( rPolygon ),
1590                                               Color(),
1591                                               0.0,
1592                                               SvtGraphicFill::fillNonZero,
1593                                               SvtGraphicFill::fillTexture,
1594                                               SvtGraphicFill::Transform(),
1595                                               true,
1596                                               SvtGraphicFill::hatchSingle,
1597                                               Color(),
1598                                               SvtGraphicFill::GradientType::Linear,
1599                                               Color(),
1600                                               Color(),
1601                                               0,
1602                                               Graphic (BitmapEx(maLatestFillStyle.aBmp)));
1603 
1604                         SvMemoryStream  aMemStm;
1605 
1606                         WriteSvtGraphicFill( aMemStm, aFill );
1607 
1608                         mpGDIMetaFile->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN", 0,
1609                                                                 static_cast<const sal_uInt8*>(aMemStm.GetData()),
1610                                                                 aMemStm.TellEnd() ) );
1611                         mpGDIMetaFile->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_END" ) );
1612                     }
1613 
1614                 }
1615             }
1616         }
1617     }
1618 
DrawPolyPolygon(tools::PolyPolygon & rPolyPolygon,bool bRecordPath)1619     void MtfTools::DrawPolyPolygon( tools::PolyPolygon& rPolyPolygon, bool bRecordPath )
1620     {
1621         UpdateClipRegion();
1622 
1623         ImplMap( rPolyPolygon );
1624 
1625         if ( bRecordPath )
1626             maPathObj.AddPolyPolygon( rPolyPolygon );
1627         else
1628         {
1629             UpdateFillStyle();
1630 
1631             if ( mbComplexClip )
1632             {
1633                 tools::PolyPolygon aDest;
1634                 tools::PolyPolygon(maClipPath.getClipPath()).GetIntersection( rPolyPolygon, aDest );
1635                 ImplDrawClippedPolyPolygon( aDest );
1636             }
1637             else
1638             {
1639                 UpdateLineStyle();
1640                 mpGDIMetaFile->AddAction( new MetaPolyPolygonAction( rPolyPolygon ) );
1641                 if (maLineStyle.aLineInfo.GetWidth() > 0 || maLineStyle.aLineInfo.GetStyle() == LineStyle::Dash)
1642                 {
1643                     for (sal_uInt16 nPoly = 0; nPoly < rPolyPolygon.Count(); ++nPoly)
1644                     {
1645                         mpGDIMetaFile->AddAction(new MetaPolyLineAction(rPolyPolygon[nPoly], maLineStyle.aLineInfo));
1646                     }
1647                 }
1648             }
1649         }
1650     }
1651 
DrawPolyLine(tools::Polygon rPolygon,bool bTo,bool bRecordPath)1652     void MtfTools::DrawPolyLine( tools::Polygon rPolygon, bool bTo, bool bRecordPath )
1653     {
1654         UpdateClipRegion();
1655 
1656         sal_uInt16 nPoints = rPolygon.GetSize();
1657         if (nPoints < 1)
1658             return;
1659 
1660         ImplMap( rPolygon );
1661         if ( bTo )
1662         {
1663             rPolygon[ 0 ] = maActPos;
1664             maActPos = rPolygon[ rPolygon.GetSize() - 1 ];
1665         }
1666         if ( bRecordPath )
1667             maPathObj.AddPolyLine( rPolygon );
1668         else
1669         {
1670             UpdateLineStyle();
1671             mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) );
1672         }
1673     }
1674 
DrawPolyBezier(tools::Polygon rPolygon,bool bTo,bool bRecordPath)1675     void MtfTools::DrawPolyBezier( tools::Polygon rPolygon, bool bTo, bool bRecordPath )
1676     {
1677         sal_uInt16 nPoints = rPolygon.GetSize();
1678         if ( ( nPoints < 4 ) || ( ( ( nPoints - 4 ) % 3 ) != 0 ) )
1679             return;
1680 
1681         UpdateClipRegion();
1682 
1683         ImplMap( rPolygon );
1684         if ( bTo )
1685         {
1686             rPolygon[ 0 ] = maActPos;
1687             maActPos = rPolygon[ nPoints - 1 ];
1688         }
1689         sal_uInt16 i;
1690         for ( i = 0; ( i + 2 ) < nPoints; )
1691         {
1692             rPolygon.SetFlags( i++, PolyFlags::Normal );
1693             rPolygon.SetFlags( i++, PolyFlags::Control );
1694             rPolygon.SetFlags( i++, PolyFlags::Control );
1695         }
1696         if ( bRecordPath )
1697             maPathObj.AddPolyLine( rPolygon );
1698         else
1699         {
1700             UpdateLineStyle();
1701             mpGDIMetaFile->AddAction( new MetaPolyLineAction( rPolygon, maLineStyle.aLineInfo ) );
1702         }
1703     }
1704 
DrawText(Point & rPosition,OUString const & rText,tools::Long * pDXArry,tools::Long * pDYArry,bool bRecordPath,sal_Int32 nGfxMode)1705     void MtfTools::DrawText( Point& rPosition, OUString const & rText, tools::Long* pDXArry, tools::Long* pDYArry, bool bRecordPath, sal_Int32 nGfxMode )
1706     {
1707         UpdateClipRegion();
1708         rPosition = ImplMap( rPosition );
1709         sal_Int32 nOldGfxMode = GetGfxMode();
1710         SetGfxMode( GM_COMPATIBLE );
1711 
1712         if (pDXArry)
1713         {
1714             sal_Int32 nSumX = 0, nSumY = 0;
1715             for (sal_Int32 i = 0; i < rText.getLength(); i++ )
1716             {
1717                 nSumX += pDXArry[i];
1718 
1719                 // #i121382# Map DXArray using WorldTransform
1720                 const Size aSizeX(ImplMap(Size(nSumX, 0)));
1721                 const basegfx::B2DVector aVectorX(aSizeX.Width(), aSizeX.Height());
1722                 pDXArry[i] = basegfx::fround(aVectorX.getLength());
1723                 pDXArry[i] *= (nSumX >= 0 ? 1 : -1);
1724 
1725                 if (pDYArry)
1726                 {
1727                     nSumY += pDYArry[i];
1728 
1729                     const Size aSizeY(ImplMap(Size(0, nSumY)));
1730                     const basegfx::B2DVector aVectorY(aSizeY.Width(), aSizeY.Height());
1731                     // Reverse Y
1732                     pDYArry[i] = basegfx::fround(aVectorY.getLength());
1733                     pDYArry[i] *= (nSumY >= 0 ? -1 : 1);
1734                 }
1735             }
1736         }
1737         if ( mnLatestTextLayoutMode != mnTextLayoutMode )
1738         {
1739             mnLatestTextLayoutMode = mnTextLayoutMode;
1740             mpGDIMetaFile->AddAction( new MetaLayoutModeAction( mnTextLayoutMode ) );
1741         }
1742         SetGfxMode( nGfxMode );
1743         TextAlign eTextAlign;
1744         if ( ( mnTextAlign & TA_BASELINE) == TA_BASELINE )
1745             eTextAlign = ALIGN_BASELINE;
1746         else if( ( mnTextAlign & TA_BOTTOM) == TA_BOTTOM )
1747             eTextAlign = ALIGN_BOTTOM;
1748         else
1749             eTextAlign = ALIGN_TOP;
1750         bool bChangeFont = false;
1751         if ( mnLatestTextAlign != mnTextAlign )
1752         {
1753             bChangeFont = true;
1754             mnLatestTextAlign = mnTextAlign;
1755             mpGDIMetaFile->AddAction( new MetaTextAlignAction( eTextAlign ) );
1756         }
1757         if ( maLatestTextColor != maTextColor )
1758         {
1759             bChangeFont = true;
1760             maLatestTextColor = maTextColor;
1761             mpGDIMetaFile->AddAction( new MetaTextColorAction( maTextColor ) );
1762         }
1763         bool bChangeFillColor = false;
1764         if ( maLatestBkColor != maBkColor )
1765         {
1766             bChangeFillColor = true;
1767             maLatestBkColor = maBkColor;
1768         }
1769         if ( mnLatestBkMode != mnBkMode )
1770         {
1771             bChangeFillColor = true;
1772             mnLatestBkMode = mnBkMode;
1773         }
1774         if ( bChangeFillColor )
1775         {
1776             bChangeFont = true;
1777             mpGDIMetaFile->AddAction( new MetaTextFillColorAction( maFont.GetFillColor(), !maFont.IsTransparent() ) );
1778         }
1779         vcl::Font aTmp( maFont );
1780         aTmp.SetColor( maTextColor );
1781         aTmp.SetFillColor( maBkColor );
1782 
1783         if( mnBkMode == BkMode::Transparent )
1784             aTmp.SetTransparent( true );
1785         else
1786             aTmp.SetTransparent( false );
1787 
1788         aTmp.SetAlignment( eTextAlign );
1789 
1790         if ( nGfxMode == GM_ADVANCED )
1791         {
1792             // check whether there is a font rotation applied via transformation
1793             Point aP1( ImplMap( Point() ) );
1794             Point aP2( ImplMap( Point( 0, 100 ) ) );
1795             aP2.AdjustX( -(aP1.X()) );
1796             aP2.AdjustY( -(aP1.Y()) );
1797             double fX = aP2.X();
1798             double fY = aP2.Y();
1799             if ( fX )
1800             {
1801                 double fOrientation = acos( fX / sqrt( fX * fX + fY * fY ) ) * 57.29577951308;
1802                 if ( fY > 0 )
1803                     fOrientation = 360 - fOrientation;
1804                 fOrientation += 90;
1805                 fOrientation *= 10;
1806                 aTmp.SetOrientation( aTmp.GetOrientation() + Degree10( static_cast<sal_Int16>(fOrientation) ) );
1807             }
1808         }
1809 
1810         if( mnTextAlign & ( TA_UPDATECP | TA_RIGHT_CENTER ) )
1811         {
1812             // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
1813             SolarMutexGuard aGuard;
1814             ScopedVclPtrInstance< VirtualDevice > pVDev;
1815             sal_Int32 nTextWidth;
1816             Point aActPosDelta;
1817             pVDev->SetMapMode( MapMode( MapUnit::Map100thMM ) );
1818             pVDev->SetFont( maFont );
1819             const sal_uInt32 nLen = pDXArry ? rText.getLength() : 0;
1820             if (nLen)
1821             {
1822                 nTextWidth = pVDev->GetTextWidth( OUString(rText[ nLen - 1 ]) );
1823                 if( nLen > 1 )
1824                     nTextWidth += pDXArry[ nLen - 2 ];
1825                 // tdf#39894: We should consider the distance to next character cell origin
1826                 aActPosDelta.setX( pDXArry[ nLen - 1 ] );
1827                 if ( pDYArry )
1828                 {
1829                     aActPosDelta.setY( pDYArry[ nLen - 1 ] );
1830                 }
1831             }
1832             else
1833             {
1834                 nTextWidth = pVDev->GetTextWidth( rText );
1835                 aActPosDelta.setX( nTextWidth );
1836             }
1837 
1838             if( mnTextAlign & TA_UPDATECP )
1839                 rPosition = maActPos;
1840 
1841             if ( mnTextAlign & TA_RIGHT_CENTER )
1842             {
1843                 Point aDisplacement( ( ( mnTextAlign & TA_RIGHT_CENTER ) == TA_RIGHT ) ? nTextWidth : nTextWidth >> 1, 0 );
1844                 Point().RotateAround(aDisplacement, maFont.GetOrientation());
1845                 rPosition -= aDisplacement;
1846             }
1847 
1848             if( mnTextAlign & TA_UPDATECP )
1849             {
1850                 Point().RotateAround(aActPosDelta, maFont.GetOrientation());
1851                 maActPos = rPosition + aActPosDelta;
1852             }
1853         }
1854 
1855         if(bChangeFont || (maLatestFont != aTmp))
1856         {
1857             maLatestFont = aTmp;
1858             rtl::Reference<MetaFontAction> aNewMetaFontAction(new MetaFontAction(aTmp));
1859 
1860             // tdf#127471 end evtl active MetaFontAction scale corrector detector/collector
1861             maScaledFontHelper.endCurrentMetaFontAction();
1862 
1863             // !bRecordPath: else no MetaTextArrayAction will be created
1864             // nullptr != pDXArry: detection only possible when text size is given
1865             // rText.getLength(): no useful check without text
1866             if(!bRecordPath && nullptr != pDXArry && 0 != rText.getLength())
1867             {
1868                 maScaledFontHelper.newCurrentMetaFontAction(aNewMetaFontAction);
1869             }
1870 
1871             mpGDIMetaFile->AddAction( aNewMetaFontAction );
1872             mpGDIMetaFile->AddAction( new MetaTextAlignAction( aTmp.GetAlignment() ) );
1873             mpGDIMetaFile->AddAction( new MetaTextColorAction( aTmp.GetColor() ) );
1874             mpGDIMetaFile->AddAction( new MetaTextFillColorAction( aTmp.GetFillColor(), !aTmp.IsTransparent() ) );
1875         }
1876 
1877         if ( bRecordPath )
1878         {
1879             // TODO
1880         }
1881         else
1882         {
1883             if ( pDXArry && pDYArry )
1884             {
1885                 for (sal_Int32 i = 0; i < rText.getLength(); ++i)
1886                 {
1887                     Point aCharDisplacement( i ? pDXArry[i-1] : 0, i ? pDYArry[i-1] : 0 );
1888                     Point().RotateAround(aCharDisplacement, maFont.GetOrientation());
1889                     mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition + aCharDisplacement, OUString( rText[i] ), nullptr, 0, 1 ) );
1890                 }
1891             }
1892             else
1893             {
1894                 /* because text without dx array is badly scaled, we
1895                    will create such an array if necessary */
1896                 tools::Long* pDX = pDXArry;
1897                 if (pDXArry)
1898                 {
1899                     // only useful when we have an imported DXArray
1900                     if(!rText.isEmpty())
1901                     {
1902                         maScaledFontHelper.evaluateAlternativeFontScale(
1903                             rText,
1904                             pDXArry[rText.getLength() - 1] // extract imported TextLength
1905                         );
1906                     }
1907                 }
1908                 else
1909                 {
1910                     // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
1911                     SolarMutexGuard aGuard;
1912                     ScopedVclPtrInstance< VirtualDevice > pVDev;
1913                     pDX = new tools::Long[ rText.getLength() ];
1914                     pVDev->SetMapMode(MapMode(MapUnit::Map100thMM));
1915                     pVDev->SetFont( maLatestFont );
1916                     pVDev->GetTextArray( rText, pDX, 0, rText.getLength());
1917                 }
1918                 mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition, rText, pDX, 0, rText.getLength() ) );
1919                 if ( !pDXArry )     // this means we have created our own array
1920                     delete[] pDX;   // which must be deleted
1921             }
1922         }
1923         SetGfxMode( nOldGfxMode );
1924     }
1925 
ImplDrawBitmap(const Point & rPos,const Size & rSize,const BitmapEx & rBitmap)1926     void MtfTools::ImplDrawBitmap( const Point& rPos, const Size& rSize, const BitmapEx& rBitmap )
1927     {
1928         BitmapEx aBmpEx( rBitmap );
1929         if ( mbComplexClip )
1930         {
1931             vcl::bitmap::DrawAndClipBitmap(rPos, rSize, rBitmap, aBmpEx, maClipPath.getClipPath());
1932         }
1933 
1934         if ( aBmpEx.IsAlpha() )
1935             mpGDIMetaFile->AddAction( new MetaBmpExScaleAction( rPos, rSize, aBmpEx ) );
1936         else
1937             mpGDIMetaFile->AddAction( new MetaBmpScaleAction( rPos, rSize, aBmpEx.GetBitmap() ) );
1938     }
1939 
ResolveBitmapActions(std::vector<std::unique_ptr<BSaveStruct>> & rSaveList)1940     void MtfTools::ResolveBitmapActions( std::vector<std::unique_ptr<BSaveStruct>>& rSaveList )
1941     {
1942         UpdateClipRegion();
1943 
1944         size_t nObjects     = rSaveList.size();
1945         size_t nObjectsLeft = nObjects;
1946 
1947         while ( nObjectsLeft )
1948         {
1949             size_t          i;
1950             size_t          nObjectsOfSameSize = 0;
1951             size_t          nObjectStartIndex = nObjects - nObjectsLeft;
1952 
1953             BSaveStruct*    pSave = rSaveList[nObjectStartIndex].get();
1954             tools::Rectangle       aRect( pSave->aOutRect );
1955 
1956             for ( i = nObjectStartIndex; i < nObjects; )
1957             {
1958                 nObjectsOfSameSize++;
1959                 if ( ++i < nObjects )
1960                 {
1961                     pSave = rSaveList[i].get();
1962                     if ( pSave->aOutRect != aRect )
1963                         break;
1964                 }
1965             }
1966             Point   aPos( ImplMap( aRect.TopLeft() ) );
1967             Size    aSize( ImplMap( aRect.GetSize() ) );
1968 
1969             for ( i = nObjectStartIndex; i < ( nObjectStartIndex + nObjectsOfSameSize ); i++ )
1970             {
1971                 pSave = rSaveList[i].get();
1972 
1973                 sal_uInt32  nWinRop = pSave->nWinRop;
1974                 sal_uInt8   nRasterOperation = static_cast<sal_uInt8>( nWinRop >> 16 );
1975 
1976                 sal_uInt32  nUsed =  0;
1977                 if ( ( nRasterOperation & 0xf )  != ( nRasterOperation >> 4 ) )
1978                     nUsed |= 1;     // pattern is used
1979                 if ( ( nRasterOperation & 0x33 ) != ( ( nRasterOperation & 0xcc ) >> 2 ) )
1980                     nUsed |= 2;     // source is used
1981                 if ( ( nRasterOperation & 0xaa ) != ( ( nRasterOperation & 0x55 ) << 1 ) )
1982                     nUsed |= 4;     // destination is used
1983 
1984                 if ( (nUsed & 1) && (( nUsed & 2 ) == 0) && nWinRop != PATINVERT )
1985                 {   // patterns aren't well supported yet
1986                     WMFRasterOp nOldRop = SetRasterOp( WMFRasterOp::NONE );  // in this case nRasterOperation is either 0 or 0xff
1987                     UpdateFillStyle();
1988                     DrawRect( aRect, false );
1989                     SetRasterOp( nOldRop );
1990                 }
1991                 else
1992                 {
1993                     bool bDrawn = false;
1994 
1995                     if ( i == nObjectStartIndex )   // optimizing, sometimes it is possible to create just one transparent bitmap
1996                     {
1997                         if ( nObjectsOfSameSize == 2 )
1998                         {
1999                             BSaveStruct* pSave2 = rSaveList[i + 1].get();
2000                             if ( ( pSave->aBmpEx.GetPrefSize() == pSave2->aBmpEx.GetPrefSize() ) &&
2001                                  ( pSave->aBmpEx.GetPrefMapMode() == pSave2->aBmpEx.GetPrefMapMode() ) )
2002                             {
2003                                 // TODO: Strictly speaking, we should
2004                                 // check whether mask is monochrome, and
2005                                 // whether image is black (upper branch)
2006                                 // or white (lower branch). Otherwise, the
2007                                 // effect is not the same as a masked
2008                                 // bitmap.
2009                                 if ( ( nWinRop == SRCPAINT ) && ( pSave2->nWinRop == SRCAND ) )
2010                                 {
2011                                     Bitmap aMask( pSave->aBmpEx.GetBitmap() ); aMask.Invert();
2012                                     BitmapEx aBmpEx( pSave2->aBmpEx.GetBitmap(), aMask );
2013                                     ImplDrawBitmap( aPos, aSize, aBmpEx );
2014                                     bDrawn = true;
2015                                     i++;
2016                                 }
2017                                 // #i20085# This is just the other way
2018                                 // around as above. Only difference: mask
2019                                 // is inverted
2020                                 else if ( ( nWinRop == SRCAND ) && ( pSave2->nWinRop == SRCPAINT ) )
2021                                 {
2022                                     const Bitmap & rMask( pSave->aBmpEx.GetBitmap() );
2023                                     BitmapEx aBmpEx( pSave2->aBmpEx.GetBitmap(), rMask );
2024                                     ImplDrawBitmap( aPos, aSize, aBmpEx );
2025                                     bDrawn = true;
2026                                     i++;
2027                                 }
2028                                 // tdf#90539
2029                                 else if ( ( nWinRop == SRCAND ) && ( pSave2->nWinRop == SRCINVERT ) )
2030                                 {
2031                                     const Bitmap & rMask( pSave->aBmpEx.GetBitmap() );
2032                                     BitmapEx aBmpEx( pSave2->aBmpEx.GetBitmap(), rMask );
2033                                     ImplDrawBitmap( aPos, aSize, aBmpEx );
2034                                     bDrawn = true;
2035                                     i++;
2036                                 }
2037                             }
2038                         }
2039                     }
2040 
2041                     if ( !bDrawn )
2042                     {
2043                         Push();
2044                         WMFRasterOp nOldRop = SetRasterOp( WMFRasterOp::CopyPen );
2045                         Bitmap      aBitmap( pSave->aBmpEx.GetBitmap() );
2046                         sal_uInt32  nOperation = ( nRasterOperation & 0xf );
2047                         switch( nOperation )
2048                         {
2049                             case 0x1 :
2050                             case 0xe :
2051                             {
2052                                 if(pSave->aBmpEx.IsAlpha())
2053                                 {
2054                                     ImplDrawBitmap( aPos, aSize, pSave->aBmpEx );
2055                                 }
2056                                 else
2057                                 {
2058                                     SetRasterOp( WMFRasterOp::XorPen );
2059                                     ImplDrawBitmap( aPos, aSize, BitmapEx(aBitmap) );
2060                                     SetRasterOp( WMFRasterOp::CopyPen );
2061                                     Bitmap  aMask( aBitmap );
2062                                     aMask.Invert();
2063                                     BitmapEx aBmpEx( aBitmap, aMask );
2064                                     ImplDrawBitmap( aPos, aSize, aBmpEx );
2065                                     if ( nOperation == 0x1 )
2066                                     {
2067                                         SetRasterOp( WMFRasterOp::Not );
2068                                         DrawRect( aRect, false );
2069                                     }
2070                                 }
2071                             }
2072                             break;
2073                             case 0x7 :
2074                             case 0x8 :
2075                             {
2076                                 Bitmap  aMask( aBitmap );
2077                                 if ( ( nUsed & 1 ) && ( nRasterOperation & 0xb0 ) == 0xb0 )     // pattern used
2078                                 {
2079                                     aBitmap.Convert( BmpConversion::N24Bit );
2080                                     aBitmap.Erase( maFillStyle.aFillColor );
2081                                 }
2082                                 BitmapEx aBmpEx( aBitmap, aMask );
2083                                 ImplDrawBitmap( aPos, aSize, aBmpEx );
2084                                 if ( nOperation == 0x7 )
2085                                 {
2086                                     SetRasterOp( WMFRasterOp::Not );
2087                                     DrawRect( aRect, false );
2088                                 }
2089                             }
2090                             break;
2091 
2092                             case 0x4 :
2093                             case 0xb :
2094                             {
2095                                 SetRasterOp( WMFRasterOp::Not );
2096                                 DrawRect( aRect, false );
2097                                 SetRasterOp( WMFRasterOp::CopyPen );
2098                                 Bitmap  aMask( aBitmap );
2099                                 aBitmap.Invert();
2100                                 BitmapEx aBmpEx( aBitmap, aMask );
2101                                 ImplDrawBitmap( aPos, aSize, aBmpEx );
2102                                 SetRasterOp( WMFRasterOp::XorPen );
2103                                 ImplDrawBitmap( aPos, aSize, BitmapEx(aBitmap) );
2104                                 if ( nOperation == 0xb )
2105                                 {
2106                                     SetRasterOp( WMFRasterOp::Not );
2107                                     DrawRect( aRect, false );
2108                                 }
2109                             }
2110                             break;
2111 
2112                             case 0x2 :
2113                             case 0xd :
2114                             {
2115                                 Bitmap  aMask( aBitmap );
2116                                 aMask.Invert();
2117                                 BitmapEx aBmpEx( aBitmap, aMask );
2118                                 ImplDrawBitmap( aPos, aSize, aBmpEx );
2119                                 SetRasterOp( WMFRasterOp::XorPen );
2120                                 ImplDrawBitmap( aPos, aSize, BitmapEx(aBitmap) );
2121                                 if ( nOperation == 0xd )
2122                                 {
2123                                     SetRasterOp( WMFRasterOp::Not );
2124                                     DrawRect( aRect, false );
2125                                 }
2126                             }
2127                             break;
2128                             case 0x6 :
2129                             case 0x9 :
2130                             {
2131                                 SetRasterOp( WMFRasterOp::XorPen );
2132                                 ImplDrawBitmap( aPos, aSize, BitmapEx(aBitmap) );
2133                                 if ( nOperation == 0x9 )
2134                                 {
2135                                     SetRasterOp( WMFRasterOp::Not );
2136                                     DrawRect( aRect, false );
2137                                 }
2138                             }
2139                             break;
2140 
2141                             case 0x0 :  // WHITENESS
2142                             case 0xf :  // BLACKNESS
2143                             {                                                   // in this case nRasterOperation is either 0 or 0xff
2144                                 maFillStyle = WinMtfFillStyle( Color( nRasterOperation, nRasterOperation, nRasterOperation ) );
2145                                 UpdateFillStyle();
2146                                 DrawRect( aRect, false );
2147                             }
2148                             break;
2149 
2150                             case 0x3 :  // only source is used
2151                             case 0xc :
2152                             {
2153                                 if ( nRasterOperation == 0x33 )
2154                                     aBitmap.Invert();
2155                                 if (pSave->m_bForceAlpha)
2156                                 {
2157                                     ImplDrawBitmap(aPos, aSize, pSave->aBmpEx);
2158                                 }
2159                                 else
2160                                 {
2161                                     ImplDrawBitmap(aPos, aSize, BitmapEx(aBitmap));
2162                                 }
2163                             }
2164                             break;
2165 
2166                             case 0x5 :  // only destination is used
2167                             {
2168                                 SetRasterOp( WMFRasterOp::Not );
2169                                 DrawRect( aRect, false );
2170                             }
2171                             break;
2172 
2173                             case 0xa :  // no operation
2174                             break;
2175                         }
2176                         SetRasterOp( nOldRop );
2177                         Pop();
2178                     }
2179                 }
2180             }
2181             nObjectsLeft -= nObjectsOfSameSize;
2182         }
2183 
2184         rSaveList.clear();
2185     }
2186 
SetDevOrg(const Point & rPoint)2187     void MtfTools::SetDevOrg( const Point& rPoint )
2188     {
2189         mnDevOrgX = rPoint.X();
2190         mnDevOrgY = rPoint.Y();
2191     }
2192 
SetDevOrgOffset(sal_Int32 nXAdd,sal_Int32 nYAdd)2193     void MtfTools::SetDevOrgOffset( sal_Int32 nXAdd, sal_Int32 nYAdd )
2194     {
2195         mnDevOrgX += nXAdd;
2196         mnDevOrgY += nYAdd;
2197     }
2198 
SetDevExt(const Size & rSize,bool regular)2199     void MtfTools::SetDevExt( const Size& rSize ,bool regular)
2200     {
2201         if ( !(rSize.Width() && rSize.Height()) )
2202             return;
2203 
2204         switch( mnMapMode )
2205         {
2206             case MM_ISOTROPIC :
2207             case MM_ANISOTROPIC :
2208             {
2209                 mnDevWidth = rSize.Width();
2210                 mnDevHeight = rSize.Height();
2211             }
2212         }
2213         if (regular)
2214         {
2215             mbIsMapDevSet=true;
2216         }
2217     }
2218 
ScaleDevExt(double fX,double fY)2219     void MtfTools::ScaleDevExt(double fX, double fY)
2220     {
2221         mnDevWidth = basegfx::fround(mnDevWidth * fX);
2222         mnDevHeight = basegfx::fround(mnDevHeight * fY);
2223     }
2224 
SetWinOrg(const Point & rPoint,bool bIsEMF)2225     void MtfTools::SetWinOrg( const Point& rPoint , bool bIsEMF)
2226     {
2227         mnWinOrgX = rPoint.X();
2228         mnWinOrgY = rPoint.Y();
2229         if (bIsEMF)
2230         {
2231             SetDevByWin();
2232         }
2233         mbIsMapWinSet=true;
2234     }
2235 
SetWinOrgOffset(sal_Int32 nXAdd,sal_Int32 nYAdd)2236     void MtfTools::SetWinOrgOffset( sal_Int32 nXAdd, sal_Int32 nYAdd )
2237     {
2238         mnWinOrgX += nXAdd;
2239         mnWinOrgY += nYAdd;
2240     }
2241 
SetDevByWin()2242     void MtfTools::SetDevByWin() //mnWinExt...-stuff has to be assigned before.
2243     {
2244         if (!mbIsMapDevSet)
2245         {
2246             if ( mnMapMode == MM_ISOTROPIC ) //TODO: WHAT ABOUT ANISOTROPIC???
2247             {
2248                 sal_Int32 nX, nY;
2249                 if (o3tl::checked_add(mnWinExtX, mnWinOrgX, nX) || o3tl::checked_sub(mnWinExtY, mnWinOrgY, nY))
2250                     return;
2251                 Size aSize(nX >> MS_FIXPOINT_BITCOUNT_28_4, -(nY >> MS_FIXPOINT_BITCOUNT_28_4));
2252                 SetDevExt(aSize, false);
2253             }
2254         }
2255     }
2256 
SetWinExt(const Size & rSize,bool bIsEMF)2257     void MtfTools::SetWinExt(const Size& rSize, bool bIsEMF)
2258     {
2259         if (!(rSize.Width() && rSize.Height()))
2260             return;
2261 
2262         switch( mnMapMode )
2263         {
2264             case MM_ISOTROPIC :
2265             case MM_ANISOTROPIC :
2266             {
2267                 mnWinExtX = rSize.Width();
2268                 mnWinExtY = rSize.Height();
2269                 if (bIsEMF)
2270                 {
2271                     SetDevByWin();
2272                 }
2273                 mbIsMapWinSet = true;
2274             }
2275         }
2276     }
2277 
ScaleWinExt(double fX,double fY)2278     void MtfTools::ScaleWinExt(double fX, double fY)
2279     {
2280         mnWinExtX = basegfx::fround(mnWinExtX * fX);
2281         mnWinExtY = basegfx::fround(mnWinExtY * fY);
2282     }
2283 
SetrclBounds(const tools::Rectangle & rRect)2284     void MtfTools::SetrclBounds( const tools::Rectangle& rRect )
2285     {
2286         mrclBounds = rRect;
2287     }
2288 
SetrclFrame(const tools::Rectangle & rRect)2289     void MtfTools::SetrclFrame( const tools::Rectangle& rRect )
2290     {
2291         mrclFrame = rRect;
2292     }
2293 
SetRefPix(const Size & rSize)2294     void MtfTools::SetRefPix( const Size& rSize )
2295     {
2296         mnPixX = rSize.Width();
2297         mnPixY = rSize.Height();
2298     }
2299 
SetRefMill(const Size & rSize)2300     void MtfTools::SetRefMill( const Size& rSize )
2301     {
2302         mnMillX = rSize.Width();
2303         mnMillY = rSize.Height();
2304     }
2305 
SetMapMode(sal_uInt32 nMapMode)2306     void MtfTools::SetMapMode( sal_uInt32 nMapMode )
2307     {
2308         mnMapMode = nMapMode;
2309         if ( nMapMode == MM_TEXT && !mbIsMapWinSet )
2310         {
2311             mnWinExtX = mnDevWidth;
2312             mnWinExtY = mnDevHeight;
2313         }
2314         else if ( mnMapMode == MM_HIMETRIC )
2315         {
2316             sal_Int32 nWinExtX, nWinExtY;
2317             if (o3tl::checked_multiply<sal_Int32>(mnMillX, 100, nWinExtX) ||
2318                 o3tl::checked_multiply<sal_Int32>(mnMillY, 100, nWinExtY))
2319             {
2320                 return;
2321             }
2322             mnWinExtX = nWinExtX;
2323             mnWinExtY = nWinExtY;
2324         }
2325     }
2326 
SetWorldTransform(const XForm & rXForm)2327     void MtfTools::SetWorldTransform( const XForm& rXForm )
2328     {
2329         maXForm.eM11 = rXForm.eM11;
2330         maXForm.eM12 = rXForm.eM12;
2331         maXForm.eM21 = rXForm.eM21;
2332         maXForm.eM22 = rXForm.eM22;
2333         maXForm.eDx = rXForm.eDx;
2334         maXForm.eDy = rXForm.eDy;
2335     }
2336 
ModifyWorldTransform(const XForm & rXForm,sal_uInt32 nMode)2337     void MtfTools::ModifyWorldTransform( const XForm& rXForm, sal_uInt32 nMode )
2338     {
2339         switch( nMode )
2340         {
2341             case MWT_IDENTITY :
2342             {
2343                 maXForm.eM11 = maXForm.eM22 = 1.0f;
2344                 maXForm.eM12 = maXForm.eM21 = maXForm.eDx = maXForm.eDy = 0.0f;
2345                 break;
2346             }
2347 
2348             case MWT_RIGHTMULTIPLY :
2349             case MWT_LEFTMULTIPLY :
2350             {
2351                 const XForm* pLeft;
2352                 const XForm* pRight;
2353 
2354                 if ( nMode == MWT_LEFTMULTIPLY )
2355                 {
2356                     pLeft = &rXForm;
2357                     pRight = &maXForm;
2358                 }
2359                 else
2360                 {
2361                     pLeft = &maXForm;
2362                     pRight = &rXForm;
2363                 }
2364 
2365                 float aF[3][3];
2366                 float bF[3][3];
2367                 float cF[3][3];
2368 
2369                 aF[0][0] = pLeft->eM11;
2370                 aF[0][1] = pLeft->eM12;
2371                 aF[0][2] = 0;
2372                 aF[1][0] = pLeft->eM21;
2373                 aF[1][1] = pLeft->eM22;
2374                 aF[1][2] = 0;
2375                 aF[2][0] = pLeft->eDx;
2376                 aF[2][1] = pLeft->eDy;
2377                 aF[2][2] = 1;
2378 
2379                 bF[0][0] = pRight->eM11;
2380                 bF[0][1] = pRight->eM12;
2381                 bF[0][2] = 0;
2382                 bF[1][0] = pRight->eM21;
2383                 bF[1][1] = pRight->eM22;
2384                 bF[1][2] = 0;
2385                 bF[2][0] = pRight->eDx;
2386                 bF[2][1] = pRight->eDy;
2387                 bF[2][2] = 1;
2388 
2389                 int i, j, k;
2390                 for ( i = 0; i < 3; i++ )
2391                 {
2392                   for ( j = 0; j < 3; j++ )
2393                   {
2394                      cF[i][j] = 0;
2395                      for ( k = 0; k < 3; k++ )
2396                         cF[i][j] += aF[i][k] * bF[k][j];
2397                   }
2398                 }
2399                 maXForm.eM11 = cF[0][0];
2400                 maXForm.eM12 = cF[0][1];
2401                 maXForm.eM21 = cF[1][0];
2402                 maXForm.eM22 = cF[1][1];
2403                 maXForm.eDx = cF[2][0];
2404                 maXForm.eDy = cF[2][1];
2405                 break;
2406             }
2407             case MWT_SET:
2408             {
2409                 SetWorldTransform(rXForm);
2410                 break;
2411             }
2412         }
2413     }
2414 
Push()2415     void MtfTools::Push()                       // !! to be able to access the original ClipRegion it
2416     {                                               // is not allowed to use the MetaPushAction()
2417         UpdateClipRegion();                         // (the original clip region is on top of the stack) (SJ)
2418         auto pSave = std::make_shared<SaveStruct>();
2419 
2420         pSave->aLineStyle = maLineStyle;
2421         pSave->aFillStyle = maFillStyle;
2422 
2423         pSave->aFont = maFont;
2424         pSave->aTextColor = maTextColor;
2425         pSave->nTextAlign = mnTextAlign;
2426         pSave->nTextLayoutMode = mnTextLayoutMode;
2427         pSave->nMapMode = mnMapMode;
2428         pSave->nGfxMode = mnGfxMode;
2429         pSave->nBkMode = mnBkMode;
2430         pSave->aBkColor = maBkColor;
2431         pSave->bFillStyleSelected = mbFillStyleSelected;
2432 
2433         pSave->aActPos = maActPos;
2434         pSave->aXForm = maXForm;
2435         pSave->eRasterOp = meRasterOp;
2436 
2437         pSave->nWinOrgX = mnWinOrgX;
2438         pSave->nWinOrgY = mnWinOrgY;
2439         pSave->nWinExtX = mnWinExtX;
2440         pSave->nWinExtY = mnWinExtY;
2441         pSave->nDevOrgX = mnDevOrgX;
2442         pSave->nDevOrgY = mnDevOrgY;
2443         pSave->nDevWidth = mnDevWidth;
2444         pSave->nDevHeight = mnDevHeight;
2445 
2446         pSave->maPathObj = maPathObj;
2447         pSave->maClipPath = maClipPath;
2448 
2449         SAL_INFO("emfio", "\t\t GfxMode: " << mnGfxMode);
2450         SAL_INFO("emfio", "\t\t MapMode: " << mnMapMode);
2451         SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX << ", " << mnWinOrgY);
2452         SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX << " x " << mnWinExtY);
2453         SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX << ", " << mnDevOrgY);
2454         SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth << " x " << mnDevHeight);
2455         SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle.aLineColor << " FillStyle: " << maFillStyle.aFillColor );
2456         mvSaveStack.push_back( pSave );
2457     }
2458 
Pop(const sal_Int32 nSavedDC)2459     void MtfTools::Pop( const sal_Int32 nSavedDC )
2460     {
2461         if ( nSavedDC == 0 )
2462             return;
2463 
2464         sal_Int32 aIndex;
2465         if ( nSavedDC < 0 ) // WMF/EMF, if negative, nSavedDC represents an instance relative to the current state.
2466             aIndex = static_cast< sal_Int32 >( mvSaveStack.size() ) + nSavedDC;
2467         else
2468             aIndex = nSavedDC; // WMF, if positive, nSavedDC represents a specific instance of the state to be restored.
2469         if( aIndex < 0 )
2470         {
2471             mvSaveStack.clear();
2472             return;
2473         }
2474         if( mvSaveStack.empty() || ( aIndex >= static_cast< sal_Int32 >( mvSaveStack.size() ) ) )
2475             return;
2476 
2477         mvSaveStack.resize( aIndex + 1 );
2478         // Backup the current data on the stack
2479         std::shared_ptr<SaveStruct>& pSave( mvSaveStack.back() );
2480 
2481         maLineStyle = pSave->aLineStyle;
2482         maFillStyle = pSave->aFillStyle;
2483 
2484         maFont = pSave->aFont;
2485         maTextColor = pSave->aTextColor;
2486         mnTextAlign = pSave->nTextAlign;
2487         mnTextLayoutMode = pSave->nTextLayoutMode;
2488         mnBkMode = pSave->nBkMode;
2489         mnGfxMode = pSave->nGfxMode;
2490         mnMapMode = pSave->nMapMode;
2491         maBkColor = pSave->aBkColor;
2492         mbFillStyleSelected = pSave->bFillStyleSelected;
2493 
2494         maActPos = pSave->aActPos;
2495         maXForm = pSave->aXForm;
2496         meRasterOp = pSave->eRasterOp;
2497 
2498         mnWinOrgX = pSave->nWinOrgX;
2499         mnWinOrgY = pSave->nWinOrgY;
2500         mnWinExtX = pSave->nWinExtX;
2501         mnWinExtY = pSave->nWinExtY;
2502         mnDevOrgX = pSave->nDevOrgX;
2503         mnDevOrgY = pSave->nDevOrgY;
2504         mnDevWidth = pSave->nDevWidth;
2505         mnDevHeight = pSave->nDevHeight;
2506 
2507         maPathObj = pSave->maPathObj;
2508         if ( ! ( maClipPath == pSave->maClipPath ) )
2509         {
2510             maClipPath = pSave->maClipPath;
2511             mbClipNeedsUpdate = true;
2512         }
2513         if ( meLatestRasterOp != meRasterOp )
2514         {
2515             mpGDIMetaFile->AddAction( new MetaRasterOpAction( meRasterOp ) );
2516             meLatestRasterOp = meRasterOp;
2517         }
2518 
2519         SAL_INFO("emfio", "\t\t GfxMode: " << mnGfxMode);
2520         SAL_INFO("emfio", "\t\t MapMode: " << mnMapMode);
2521         SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX << ", " << mnWinOrgY);
2522         SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX << " x " << mnWinExtY);
2523         SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX << ", " << mnDevOrgY);
2524         SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth << " x " << mnDevHeight);
2525         SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle.aLineColor << " FillStyle: " << maFillStyle.aFillColor );
2526         mvSaveStack.pop_back();
2527     }
2528 
AddFromGDIMetaFile(GDIMetaFile & rGDIMetaFile)2529     void MtfTools::AddFromGDIMetaFile( GDIMetaFile& rGDIMetaFile )
2530     {
2531        rGDIMetaFile.Play( *mpGDIMetaFile );
2532     }
2533 
PassEMFPlusHeaderInfo()2534     void MtfTools::PassEMFPlusHeaderInfo()
2535     {
2536         EMFP_DEBUG(printf ("\t\t\tadd EMF_PLUS header info\n"));
2537 
2538         SvMemoryStream mem;
2539         sal_Int32 nLeft, nRight, nTop, nBottom;
2540 
2541         nLeft = mrclFrame.Left();
2542         nTop = mrclFrame.Top();
2543         nRight = mrclFrame.Right();
2544         nBottom = mrclFrame.Bottom();
2545 
2546         // emf header info
2547         mem.WriteInt32( nLeft ).WriteInt32( nTop ).WriteInt32( nRight ).WriteInt32( nBottom );
2548         mem.WriteInt32( mnPixX ).WriteInt32( mnPixY ).WriteInt32( mnMillX ).WriteInt32( mnMillY );
2549 
2550         float one, zero;
2551 
2552         one = 1;
2553         zero = 0;
2554 
2555         // add transformation matrix to be used in vcl's metaact.cxx for
2556         // rotate and scale operations
2557         mem.WriteFloat( one ).WriteFloat( zero ).WriteFloat( zero ).WriteFloat( one ).WriteFloat( zero ).WriteFloat( zero );
2558 
2559         // need to flush the stream, otherwise GetEndOfData will return 0
2560         // on windows where the function parameters are probably resolved in reverse order
2561         mem.Flush();
2562 
2563         mpGDIMetaFile->AddAction( new MetaCommentAction( "EMF_PLUS_HEADER_INFO", 0, static_cast<const sal_uInt8*>(mem.GetData()), mem.GetEndOfData() ) );
2564         mpGDIMetaFile->UseCanvas( true );
2565     }
2566 
PassEMFPlus(void const * pBuffer,sal_uInt32 nLength)2567     void MtfTools::PassEMFPlus( void const * pBuffer, sal_uInt32 nLength )
2568     {
2569         EMFP_DEBUG(printf ("\t\t\tadd EMF_PLUS comment length %04x\n",(unsigned int) nLength));
2570         mpGDIMetaFile->AddAction( new MetaCommentAction( "EMF_PLUS", 0, static_cast<const sal_uInt8*>(pBuffer), nLength ) );
2571     }
2572 }
2573 
2574 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2575