1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <mtftools.hxx>
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 <comphelper/configuration.hxx>
39 #include <unotools/defaultencoding.hxx>
40 #include <unotools/wincodepage.hxx>
42 #if OSL_DEBUG_LEVEL > 1
43 #define EMFP_DEBUG(x) x
50 SvStream
& operator >> (SvStream
& rInStream
, XForm
& rXForm
)
52 if (sizeof(float) != 4)
54 OSL_FAIL("EmfReader::sizeof( float ) != 4");
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 if (std::isnan(rXForm
.eM11
) ||
66 std::isnan(rXForm
.eM12
) ||
67 std::isnan(rXForm
.eM21
) ||
68 std::isnan(rXForm
.eM22
) ||
69 std::isnan(rXForm
.eDx
) ||
70 std::isnan(rXForm
.eDy
))
72 SAL_WARN("emfio", "XForm member isnan, ignoring");
79 void WinMtfClipPath::intersectClip( const basegfx::B2DPolyPolygon
& rPolyPolygon
)
81 maClip
.intersectPolyPolygon(rPolyPolygon
);
84 void WinMtfClipPath::excludeClip( const basegfx::B2DPolyPolygon
& rPolyPolygon
)
86 maClip
.subtractPolyPolygon(rPolyPolygon
);
89 void WinMtfClipPath::setClipPath( const basegfx::B2DPolyPolygon
& rB2DPoly
, RegionMode nClippingMode
)
91 switch ( nClippingMode
)
93 case RegionMode::RGN_OR
:
94 maClip
.unionPolyPolygon(rB2DPoly
);
96 case RegionMode::RGN_XOR
:
97 maClip
.xorPolyPolygon(rB2DPoly
);
99 case RegionMode::RGN_DIFF
:
100 maClip
.subtractPolyPolygon(rB2DPoly
);
102 case RegionMode::RGN_AND
:
103 maClip
.intersectPolyPolygon(rB2DPoly
);
105 case RegionMode::RGN_COPY
:
106 maClip
= basegfx::utils::B2DClipState(rB2DPoly
);
111 void WinMtfClipPath::moveClipRegion( const Size
& rSize
)
113 basegfx::B2DHomMatrix aTranslate
;
114 aTranslate
.translate(rSize
.Width(), rSize
.Height());
115 maClip
.transform(aTranslate
);
118 void WinMtfClipPath::setDefaultClipPath()
120 // Empty clip region - everything visible
121 maClip
= basegfx::utils::B2DClipState();
124 basegfx::B2DPolyPolygon
const & WinMtfClipPath::getClipPath() const
126 return maClip
.getClipPoly();
129 void WinMtfPathObj::AddPoint( const Point
& rPoint
)
132 Insert( tools::Polygon() );
133 tools::Polygon
& rPoly
= static_cast<tools::PolyPolygon
&>(*this)[ Count() - 1 ];
134 rPoly
.Insert( rPoly
.GetSize(), rPoint
);
138 void WinMtfPathObj::AddPolyLine( const tools::Polygon
& rPolyLine
)
141 Insert( tools::Polygon() );
142 tools::Polygon
& rPoly
= static_cast<tools::PolyPolygon
&>(*this)[ Count() - 1 ];
143 rPoly
.Insert( rPoly
.GetSize(), rPolyLine
);
147 void WinMtfPathObj::AddPolygon( const tools::Polygon
& rPoly
)
153 void WinMtfPathObj::AddPolyPolygon( const tools::PolyPolygon
& rPolyPoly
)
155 sal_uInt16 i
, nCount
= rPolyPoly
.Count();
156 for ( i
= 0; i
< nCount
; i
++ )
157 Insert( rPolyPoly
[ i
] );
161 void WinMtfPathObj::ClosePath()
165 tools::Polygon
& rPoly
= static_cast<tools::PolyPolygon
&>(*this)[ Count() - 1 ];
166 if ( rPoly
.GetSize() > 2 )
168 Point
aFirst( rPoly
[ 0 ] );
169 if ( aFirst
!= rPoly
[ rPoly
.GetSize() - 1 ] )
170 rPoly
.Insert( rPoly
.GetSize(), aFirst
);
176 WinMtfFontStyle::WinMtfFontStyle( LOGFONTW
const & rFont
)
178 rtl_TextEncoding eCharSet
;
179 if ((rFont
.alfFaceName
== "Symbol")
180 || (rFont
.alfFaceName
== "MT Extra"))
181 eCharSet
= RTL_TEXTENCODING_SYMBOL
;
182 else if ((rFont
.lfCharSet
== DEFAULT_CHARSET
) || (rFont
.lfCharSet
== OEM_CHARSET
))
183 eCharSet
= utl_getWinTextEncodingFromLangStr(utl_getLocaleForGlobalDefaultEncoding(),
184 rFont
.lfCharSet
== OEM_CHARSET
);
186 eCharSet
= rtl_getTextEncodingFromWindowsCharset( rFont
.lfCharSet
);
187 if ( eCharSet
== RTL_TEXTENCODING_DONTKNOW
)
188 eCharSet
= RTL_TEXTENCODING_MS_1252
;
189 aFont
.SetCharSet( eCharSet
);
190 aFont
.SetFamilyName( rFont
.alfFaceName
);
192 switch ( rFont
.lfPitchAndFamily
>> 4 & 0x0f )
194 case FamilyFont::FF_ROMAN
:
195 eFamily
= FAMILY_ROMAN
;
198 case FamilyFont::FF_SWISS
:
199 eFamily
= FAMILY_SWISS
;
202 case FamilyFont::FF_MODERN
:
203 eFamily
= FAMILY_MODERN
;
206 case FamilyFont::FF_SCRIPT
:
207 eFamily
= FAMILY_SCRIPT
;
210 case FamilyFont::FF_DECORATIVE
:
211 eFamily
= FAMILY_DECORATIVE
;
215 eFamily
= FAMILY_DONTKNOW
;
218 aFont
.SetFamily( eFamily
);
221 switch ( rFont
.lfPitchAndFamily
& 0x0f )
224 ePitch
= PITCH_FIXED
;
230 ePitch
= PITCH_VARIABLE
;
233 aFont
.SetPitch( ePitch
);
236 if (rFont
.lfWeight
== 0) // default weight SHOULD be used
237 eWeight
= WEIGHT_DONTKNOW
;
238 else if (rFont
.lfWeight
<= FW_THIN
)
239 eWeight
= WEIGHT_THIN
;
240 else if( rFont
.lfWeight
<= FW_ULTRALIGHT
)
241 eWeight
= WEIGHT_ULTRALIGHT
;
242 else if( rFont
.lfWeight
<= FW_LIGHT
)
243 eWeight
= WEIGHT_LIGHT
;
244 else if( rFont
.lfWeight
< FW_MEDIUM
)
245 eWeight
= WEIGHT_NORMAL
;
246 else if( rFont
.lfWeight
== FW_MEDIUM
)
247 eWeight
= WEIGHT_MEDIUM
;
248 else if( rFont
.lfWeight
<= FW_SEMIBOLD
)
249 eWeight
= WEIGHT_SEMIBOLD
;
250 else if( rFont
.lfWeight
<= FW_BOLD
)
251 eWeight
= WEIGHT_BOLD
;
252 else if( rFont
.lfWeight
<= FW_ULTRABOLD
)
253 eWeight
= WEIGHT_ULTRABOLD
;
255 eWeight
= WEIGHT_BLACK
;
256 aFont
.SetWeight( eWeight
);
259 aFont
.SetItalic( ITALIC_NORMAL
);
261 if( rFont
.lfUnderline
)
262 aFont
.SetUnderline( LINESTYLE_SINGLE
);
264 if( rFont
.lfStrikeOut
)
265 aFont
.SetStrikeout( STRIKEOUT_SINGLE
);
267 aFont
.SetOrientation( Degree10(static_cast<sal_Int16
>(rFont
.lfEscapement
)) );
269 Size
aFontSize( rFont
.lfWidth
, rFont
.lfHeight
);
270 if ( rFont
.lfHeight
> 0 )
272 // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
273 SolarMutexGuard aGuard
;
274 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
275 // converting the cell height into a font height
276 aFont
.SetFontSize( aFontSize
);
277 pVDev
->SetFont( aFont
);
278 FontMetric
aMetric( pVDev
->GetFontMetric() );
279 tools::Long nHeight
= aMetric
.GetAscent() + aMetric
.GetDescent();
282 double fHeight
= (static_cast<double>(aFontSize
.Height()) * rFont
.lfHeight
) / nHeight
;
283 aFontSize
.setHeight( static_cast<sal_Int32
>( fHeight
+ 0.5 ) );
287 // Convert height to positive
288 aFontSize
.setHeight( std::abs(aFontSize
.Height()) );
289 aFont
.SetFontSize(aFontSize
);
291 // tdf#127471 adapt nFontWidth from Windows-like notation to
292 // NormedFontScaling if used for text scaling
294 const bool bFontScaledHorizontally(aFontSize
.Width() != 0 && aFontSize
.Width() != aFontSize
.Height());
296 if(bFontScaledHorizontally
)
298 // tdf#127471 nFontWidth is the Windows FontScaling, need to convert to
299 // Non-Windowslike notation relative to FontHeight.
300 const tools::Long
nAverageFontWidth(aFont
.GetOrCalculateAverageFontWidth());
302 if(nAverageFontWidth
> 0)
304 const double fScaleFactor(static_cast<double>(aFontSize
.Height()) / static_cast<double>(nAverageFontWidth
));
305 aFont
.SetAverageFontWidth(static_cast<tools::Long
>(static_cast<double>(aFontSize
.Width()) * fScaleFactor
));
311 WinMtfFontStyle::~WinMtfFontStyle() = default;
314 ScaledFontDetectCorrectHelper::ScaledFontDetectCorrectHelper()
318 void ScaledFontDetectCorrectHelper::endCurrentMetaFontAction()
320 if(maCurrentMetaFontAction
.is() && !maAlternativeFontScales
.empty())
322 // create average corrected FontScale value and count
323 // positive/negative hits
324 sal_uInt32
nPositive(0);
325 sal_uInt32
nNegative(0);
326 double fAverage(0.0);
328 for(double fPart
: maAlternativeFontScales
)
342 fAverage
/= static_cast<double>(maAlternativeFontScales
.size());
344 if(nPositive
>= nNegative
)
346 // correction intended, it is probably an old imported file
347 maPositiveIdentifiedCases
.emplace_back(maCurrentMetaFontAction
, fAverage
);
351 // correction not favorable in the majority of cases for this Font, still
352 // remember to have a weight in the last decision for correction
353 maNegativeIdentifiedCases
.emplace_back(maCurrentMetaFontAction
, fAverage
);
357 maCurrentMetaFontAction
.clear();
358 maAlternativeFontScales
.clear();
361 void ScaledFontDetectCorrectHelper::newCurrentMetaFontAction(const rtl::Reference
<MetaFontAction
>& rNewMetaFontAction
)
363 maCurrentMetaFontAction
.clear();
364 maAlternativeFontScales
.clear();
366 if(!rNewMetaFontAction
.is())
369 // check 1st criteria for FontScale active. We usually write this,
370 // so this will already sort out most situations
371 const vcl::Font
& rCandidate(rNewMetaFontAction
->GetFont());
373 if(0 != rCandidate
.GetAverageFontWidth())
375 const tools::Long
nUnscaledAverageFontWidth(rCandidate
.GetOrCalculateAverageFontWidth());
377 // check 2nd (system-dependent) criteria for FontScale
378 if(nUnscaledAverageFontWidth
!= rCandidate
.GetFontHeight())
380 // FontScale is active, remember and use as current
381 maCurrentMetaFontAction
= rNewMetaFontAction
;
386 void ScaledFontDetectCorrectHelper::evaluateAlternativeFontScale(OUString
const & rText
, tools::Long nImportedTextLength
)
388 if(!maCurrentMetaFontAction
.is())
391 SolarMutexGuard aGuard
; // VirtualDevice is not thread-safe
392 ScopedVclPtrInstance
< VirtualDevice
> pTempVirtualDevice
;
394 // calculate measured TextLength
395 const vcl::Font
& rFontCandidate(maCurrentMetaFontAction
->GetFont());
396 pTempVirtualDevice
->SetFont(rFontCandidate
);
397 tools::Long
nMeasuredTextLength(pTempVirtualDevice
->GetTextWidth(rText
));
398 // on failure, use original length
399 if (!nMeasuredTextLength
)
400 nMeasuredTextLength
= nImportedTextLength
;
402 // compare expected and imported TextLengths
403 if (nImportedTextLength
== nMeasuredTextLength
)
406 const double fFactorText(static_cast<double>(nImportedTextLength
) / static_cast<double>(nMeasuredTextLength
));
407 const double fFactorTextPercent(fabs(1.0 - fFactorText
) * 100.0);
409 // if we assume that loaded file was written on old linux, we have to
410 // back-convert the scale value depending on which system we run
412 // When running on Windows the value was not adapted at font import (see WinMtfFontStyle
413 // constructor), so it is still NormedFontScaling and we need to convert to Windows-style
416 // When running on unx (non-Windows) the value was already adapted at font import (see WinMtfFontStyle
417 // constructor). It was wrongly assumed to be Windows-style FontScaling, so we need to revert that
418 // to get back to the needed unx-style FontScale
420 // Interestingly this leads to the *same* correction, so no need to make this
421 // system-dependent (!)
422 const tools::Long
nUnscaledAverageFontWidth(rFontCandidate
.GetOrCalculateAverageFontWidth());
423 const tools::Long
nScaledAverageFontWidth(rFontCandidate
.GetAverageFontWidth());
424 const double fScaleFactor(static_cast<double>(nUnscaledAverageFontWidth
) / static_cast<double>(rFontCandidate
.GetFontHeight()));
425 const double fCorrectedAverageFontWidth(static_cast<double>(nScaledAverageFontWidth
) * fScaleFactor
);
426 tools::Long
nCorrectedTextLength(0);
428 { // do in own scope, only need nUnscaledAverageFontWidth
429 vcl::Font
rFontCandidate2(rFontCandidate
);
430 rFontCandidate2
.SetAverageFontWidth(static_cast<tools::Long
>(fCorrectedAverageFontWidth
));
431 pTempVirtualDevice
->SetFont(rFontCandidate2
);
432 nCorrectedTextLength
= pTempVirtualDevice
->GetTextWidth(rText
);
433 // on failure, use original length
434 if (!nCorrectedTextLength
)
435 nCorrectedTextLength
= nImportedTextLength
;
438 const double fFactorCorrectedText(static_cast<double>(nImportedTextLength
) / static_cast<double>(nCorrectedTextLength
));
439 const double fFactorCorrectedTextPercent(fabs(1.0 - fFactorCorrectedText
) * 100.0);
441 // If FactorCorrectedText fits better than FactorText this is probably
442 // an import of an old EMF/WMF written by LibreOffice on a non-Windows (unx) system
443 // and should be corrected.
444 // Usually in tested cases this lies inside 5% of range, so detecting this just using
445 // fFactorTextPercent inside 5% -> no old file
446 // fFactorCorrectedTextPercent inside 5% -> is old file
447 // works not too bad, but there are some strange not so often used fonts where that
448 // values do deviate, so better just compare if old corrected would fit better than
449 // the uncorrected case, that is usually safe.
450 if(fFactorCorrectedTextPercent
< fFactorTextPercent
)
452 maAlternativeFontScales
.push_back(fCorrectedAverageFontWidth
);
456 // also push, but negative to remember non-fitting case
457 maAlternativeFontScales
.push_back(-fCorrectedAverageFontWidth
);
461 void ScaledFontDetectCorrectHelper::applyAlternativeFontScale()
463 // make sure last evtl. detected current FontAction gets added to identified cases
464 endCurrentMetaFontAction();
466 // Take final decision to correct FontScaling for this imported Metafile or not.
467 // It is possible to weight positive against negative cases, so to only finally
468 // correct when more positive cases were detected.
469 // But that would be inconsequent and wrong. *If* the detected case is an old import
470 // the whole file was written with wrong FontScale values and all Font actions
471 // need to be corrected. Thus, for now, correct all when there are/is positive
473 // On the other hand it *may* be that for some strange fonts there is a false-positive
474 // in the positive cases, so at least insist on positive cases being more than negative.
475 // Still, do then correct *all* cases.
476 if(!maPositiveIdentifiedCases
.empty()
477 && maPositiveIdentifiedCases
.size() >= maNegativeIdentifiedCases
.size())
479 for(std::pair
<rtl::Reference
<MetaFontAction
>, double>& rCandidate
: maPositiveIdentifiedCases
)
481 rCandidate
.first
->correctFontScale(static_cast<tools::Long
>(rCandidate
.second
));
483 for(std::pair
<rtl::Reference
<MetaFontAction
>, double>& rCandidate
: maNegativeIdentifiedCases
)
485 rCandidate
.first
->correctFontScale(static_cast<tools::Long
>(rCandidate
.second
));
489 maPositiveIdentifiedCases
.clear();
490 maNegativeIdentifiedCases
.clear();
493 Color
MtfTools::ReadColor()
495 sal_uInt32
nColor(0);
496 mpInputStream
->ReadUInt32( nColor
);
497 Color
aColor( COL_BLACK
);
498 if ( ( nColor
& 0xFFFF0000 ) == 0x01000000 )
500 size_t index
= nColor
& 0x0000FFFF;
501 if ( index
< maPalette
.aPaletteColors
.size() )
502 aColor
= maPalette
.aPaletteColors
[ index
];
504 SAL_INFO( "emfio", "\t\t Palette index out of range: " << index
);
507 aColor
= Color( static_cast<sal_uInt8
>( nColor
), static_cast<sal_uInt8
>( nColor
>> 8 ), static_cast<sal_uInt8
>( nColor
>> 16 ) );
509 SAL_INFO("emfio", "\t\tColor: " << aColor
);
513 Point
MtfTools::ImplScale(const Point
& rPoint
) // Hack to set varying defaults for incompletely defined files.
516 return Point(rPoint
.X() * UNDOCUMENTED_WIN_RCL_RELATION
- mrclFrame
.Left(),
517 rPoint
.Y() * UNDOCUMENTED_WIN_RCL_RELATION
- mrclFrame
.Top());
522 Point
MtfTools::ImplMap( const Point
& rPt
)
524 if ( mnWinExtX
&& mnWinExtY
)
529 double fX2
= fX
* maXForm
.eM11
+ fY
* maXForm
.eM21
+ maXForm
.eDx
;
530 double fY2
= fX
* maXForm
.eM12
+ fY
* maXForm
.eM22
+ maXForm
.eDy
;
532 if ( meGfxMode
== GraphicsMode::GM_COMPATIBLE
)
539 case MappingMode::MM_LOENGLISH
:
541 fX2
= o3tl::convert(fX2
, o3tl::Length::in100
, o3tl::Length::mm100
);
542 fY2
= o3tl::convert(-fY2
, o3tl::Length::in100
, o3tl::Length::mm100
);
545 case MappingMode::MM_HIENGLISH
:
547 fX2
= o3tl::convert(fX2
, o3tl::Length::in1000
, o3tl::Length::mm100
);
548 fY2
= o3tl::convert(-fY2
, o3tl::Length::in1000
, o3tl::Length::mm100
);
551 case MappingMode::MM_TWIPS
:
553 fX2
= o3tl::convert(fX2
, o3tl::Length::twip
, o3tl::Length::mm100
);
554 fY2
= o3tl::convert(-fY2
, o3tl::Length::twip
, o3tl::Length::mm100
);
557 case MappingMode::MM_LOMETRIC
:
559 fX2
= o3tl::convert(fX2
, o3tl::Length::mm10
, o3tl::Length::mm100
);
560 fY2
= o3tl::convert(-fY2
, o3tl::Length::mm10
, o3tl::Length::mm100
);
563 case MappingMode::MM_HIMETRIC
: // in hundredth of a millimeter
570 if (mnPixX
== 0 || mnPixY
== 0)
572 SAL_WARN("emfio", "invalid scaling factor");
577 if ( meMapMode
!= MappingMode::MM_TEXT
)
584 fX2
*= static_cast<double>(mnMillX
) * 100.0 / static_cast<double>(mnPixX
);
585 fY2
*= static_cast<double>(mnMillY
) * 100.0 / static_cast<double>(mnPixY
);
591 double nDevOrgX
= mnDevOrgX
;
593 nDevOrgX
*= static_cast<double>(mnMillX
) * 100.0 / static_cast<double>(mnPixX
);
595 double nDevOrgY
= mnDevOrgY
;
597 nDevOrgY
*= static_cast<double>(mnMillY
) * 100.0 / static_cast<double>(mnPixY
);
600 fX2
-= mrclFrame
.Left();
601 fY2
-= mrclFrame
.Top();
603 return Point(basegfx::fround
<tools::Long
>(fX2
), basegfx::fround
<tools::Long
>(fY2
));
609 Size
MtfTools::ImplMap(const Size
& rSz
, bool bDoWorldTransform
)
611 if ( mnWinExtX
&& mnWinExtY
)
613 // #i121382# apply the whole WorldTransform, else a rotation will be misinterpreted
614 double fWidth
, fHeight
;
615 if (bDoWorldTransform
)
617 fWidth
= rSz
.Width() * maXForm
.eM11
+ rSz
.Height() * maXForm
.eM21
;
618 fHeight
= rSz
.Width() * maXForm
.eM12
+ rSz
.Height() * maXForm
.eM22
;
622 //take the scale, but not the rotation
623 basegfx::B2DHomMatrix
aMatrix(maXForm
.eM11
, maXForm
.eM12
, 0,
624 maXForm
.eM21
, maXForm
.eM22
, 0);
625 basegfx::B2DTuple aScale
, aTranslate
;
626 double fRotate
, fShearX
;
627 if (!aMatrix
.decompose(aScale
, aTranslate
, fRotate
, fShearX
))
632 fWidth
= rSz
.Width() * aScale
.getX();
633 fHeight
= rSz
.Height() * aScale
.getY();
636 if ( meGfxMode
== GraphicsMode::GM_COMPATIBLE
)
640 case MappingMode::MM_LOENGLISH
:
642 fWidth
= o3tl::convert(fWidth
, o3tl::Length::in100
, o3tl::Length::mm100
);
643 fHeight
= o3tl::convert(-fHeight
, o3tl::Length::in100
, o3tl::Length::mm100
);
646 case MappingMode::MM_HIENGLISH
:
648 fWidth
= o3tl::convert(fWidth
, o3tl::Length::in1000
, o3tl::Length::mm100
);
649 fHeight
= o3tl::convert(-fHeight
, o3tl::Length::in1000
, o3tl::Length::mm100
);
652 case MappingMode::MM_LOMETRIC
:
654 fWidth
= o3tl::convert(fWidth
, o3tl::Length::mm10
, o3tl::Length::mm100
);
655 fHeight
= o3tl::convert(-fHeight
, o3tl::Length::mm10
, o3tl::Length::mm100
);
658 case MappingMode::MM_HIMETRIC
: // in hundredth of millimeters
663 case MappingMode::MM_TWIPS
:
665 fWidth
= o3tl::convert(fWidth
, o3tl::Length::twip
, o3tl::Length::mm100
);
666 fHeight
= o3tl::convert(-fHeight
, o3tl::Length::twip
, o3tl::Length::mm100
);
671 if (mnPixX
== 0 || mnPixY
== 0)
673 SAL_WARN("emfio", "invalid scaling factor");
678 if ( meMapMode
!= MappingMode::MM_TEXT
)
681 fHeight
/= mnWinExtY
;
682 fWidth
*= mnDevWidth
;
683 fHeight
*= mnDevHeight
;
685 fWidth
*= static_cast<double>(mnMillX
) * 100.0 / static_cast<double>(mnPixX
);
686 fHeight
*= static_cast<double>(mnMillY
) * 100.0 / static_cast<double>(mnPixY
);
692 return Size(basegfx::fround
<tools::Long
>(fWidth
), basegfx::fround
<tools::Long
>(fHeight
));
698 tools::Rectangle
MtfTools::ImplMap( const tools::Rectangle
& rRect
)
700 tools::Rectangle aRect
;
701 aRect
.SetPos(ImplMap(rRect
.TopLeft()));
702 aRect
.SaturatingSetSize(ImplMap(rRect
.GetSize()));
706 void MtfTools::ImplMap( vcl::Font
& rFont
)
708 // !!! HACK: we now always set the width to zero because the OS width is interpreted differently;
709 // must later be made portable in SV (KA 1996-02-08)
710 Size aFontSize
= ImplMap (rFont
.GetFontSize(), false);
712 const auto nHeight
= aFontSize
.Height();
714 aFontSize
.setHeight( o3tl::saturating_toggle_sign(nHeight
) );
716 rFont
.SetFontSize( aFontSize
);
719 const bool bFail
= o3tl::checked_multiply(mnWinExtX
, mnWinExtY
, nResult
);
720 if (!bFail
&& nResult
< 0)
721 rFont
.SetOrientation( 3600_deg10
- rFont
.GetOrientation() );
724 tools::Polygon
& MtfTools::ImplMap( tools::Polygon
& rPolygon
)
726 sal_uInt16 nPoints
= rPolygon
.GetSize();
727 for ( sal_uInt16 i
= 0; i
< nPoints
; i
++ )
729 rPolygon
[ i
] = ImplMap( rPolygon
[ i
] );
734 void MtfTools::ImplScale( tools::Polygon
& rPolygon
)
736 sal_uInt16 nPoints
= rPolygon
.GetSize();
737 for ( sal_uInt16 i
= 0; i
< nPoints
; i
++ )
739 rPolygon
[ i
] = ImplScale( rPolygon
[ i
] );
743 tools::PolyPolygon
& MtfTools::ImplScale( tools::PolyPolygon
& rPolyPolygon
)
745 sal_uInt16 nPolys
= rPolyPolygon
.Count();
746 for (sal_uInt16 i
= 0; i
< nPolys
; ++i
)
748 ImplScale(rPolyPolygon
[i
]);
753 tools::PolyPolygon
& MtfTools::ImplMap( tools::PolyPolygon
& rPolyPolygon
)
755 sal_uInt16 nPolys
= rPolyPolygon
.Count();
756 for ( sal_uInt16 i
= 0; i
< nPolys
; ImplMap( rPolyPolygon
[ i
++ ] ) ) ;
760 void MtfTools::SelectObject( sal_uInt32 nIndex
)
762 if ( nIndex
& ENHMETA_STOCK_OBJECT
)
764 SAL_INFO ( "emfio", "\t\t ENHMETA_STOCK_OBJECT, StockObject Enumeration: 0x" << std::hex
<< nIndex
);
765 StockObject nStockId
= static_cast<StockObject
>(nIndex
& 0xFF);
768 case StockObject::WHITE_BRUSH
:
770 maFillStyle
= WinMtfFillStyle( COL_WHITE
);
771 mbFillStyleSelected
= true;
774 case StockObject::LTGRAY_BRUSH
:
776 maFillStyle
= WinMtfFillStyle( COL_LIGHTGRAY
);
777 mbFillStyleSelected
= true;
780 case StockObject::GRAY_BRUSH
:
782 maFillStyle
= WinMtfFillStyle( COL_GRAY
);
783 mbFillStyleSelected
= true;
786 case StockObject::DKGRAY_BRUSH
:
788 maFillStyle
= WinMtfFillStyle( COL_GRAY7
);
789 mbFillStyleSelected
= true;
792 case StockObject::BLACK_BRUSH
:
794 maFillStyle
= WinMtfFillStyle( COL_BLACK
);
795 mbFillStyleSelected
= true;
798 case StockObject::NULL_BRUSH
:
800 maFillStyle
= WinMtfFillStyle( COL_TRANSPARENT
, true );
801 mbFillStyleSelected
= true;
804 case StockObject::WHITE_PEN
:
806 maLineStyle
= WinMtfLineStyle(COL_WHITE
, PS_COSMETIC
, 0);
809 case StockObject::BLACK_PEN
:
811 maLineStyle
= WinMtfLineStyle(COL_BLACK
, PS_COSMETIC
, 0);
814 case StockObject::NULL_PEN
:
816 maLineStyle
= WinMtfLineStyle( COL_TRANSPARENT
, true );
825 nIndex
&= 0xffff; // safety check: don't allow index to be > 65535
827 GDIObj
*pGDIObj
= nullptr;
829 if ( nIndex
< mvGDIObj
.size() )
830 pGDIObj
= mvGDIObj
[ nIndex
].get();
835 SAL_INFO ( "emfio", "\t\t Index: " << nIndex
);
836 if (const auto pen
= dynamic_cast<WinMtfLineStyle
*>(pGDIObj
))
839 SAL_INFO ( "emfio", "\t Line Style, Color: 0x" << std::hex
<< maLineStyle
.aLineColor
840 << ", Weight: " << maLineStyle
.aLineInfo
.GetWidth() );
842 else if (const auto brush
= dynamic_cast<WinMtfFillStyle
*>(
845 maFillStyle
= *brush
;
846 mbFillStyleSelected
= true;
847 SAL_INFO("emfio", "\t\tBrush Object, Index: " << nIndex
<< ", Color: " << maFillStyle
.aFillColor
);
849 else if (const auto font
= dynamic_cast<WinMtfFontStyle
*>(
852 maFont
= font
->aFont
;
853 SAL_INFO("emfio", "\t\tFont Object, Index: " << nIndex
<< ", Font: " << maFont
.GetFamilyName() << " " << maFont
.GetStyleName());
855 else if (const auto palette
= dynamic_cast<WinMtfPalette
*>(
858 maPalette
= palette
->aPaletteColors
;
859 SAL_INFO("emfio", "\t\tPalette Object, Index: " << nIndex
<< ", Number of colours: " << maPalette
.aPaletteColors
.size() );
864 SAL_WARN("emfio", "Warning: Unable to find Object with index:" << nIndex
);
869 void MtfTools::SetTextLayoutMode( vcl::text::ComplexTextLayoutFlags nTextLayoutMode
)
871 mnTextLayoutMode
= nTextLayoutMode
;
874 void MtfTools::SetArcDirection(bool bClockWise
)
876 SAL_INFO("emfio", "\t\t Arc direction: " << (bClockWise
? "ClockWise" : "CounterClockWise"));
877 mbClockWiseArcDirection
= bClockWise
;
880 void MtfTools::SetBkMode( BackgroundMode nMode
)
885 void MtfTools::SetBkColor( const Color
& rColor
)
890 void MtfTools::SetTextColor( const Color
& rColor
)
892 maTextColor
= rColor
;
895 void MtfTools::SetTextAlign( sal_uInt32 nAlign
)
897 mnTextAlign
= nAlign
;
900 void MtfTools::ImplResizeObjectArry( sal_uInt32 nNewEntrys
)
902 mvGDIObj
.resize(nNewEntrys
);
905 void MtfTools::ImplDrawClippedPolyPolygon( const tools::PolyPolygon
& rPolyPoly
)
907 if ( !rPolyPoly
.Count() )
910 ImplSetNonPersistentLineColorTransparenz();
911 if ( rPolyPoly
.Count() == 1 )
913 if ( rPolyPoly
.IsRect() )
914 mpGDIMetaFile
->AddAction( new MetaRectAction( rPolyPoly
.GetBoundRect() ) );
917 tools::Polygon
aPoly( rPolyPoly
[ 0 ] );
918 sal_uInt16 nCount
= aPoly
.GetSize();
921 if ( aPoly
[ nCount
- 1 ] != aPoly
[ 0 ] )
923 Point
aPoint( aPoly
[ 0 ] );
924 aPoly
.Insert( nCount
, aPoint
);
926 mpGDIMetaFile
->AddAction( new MetaPolygonAction( std::move(aPoly
) ) );
931 mpGDIMetaFile
->AddAction( new MetaPolyPolygonAction( rPolyPoly
) );
934 void MtfTools::CreateObject( std::unique_ptr
<GDIObj
> pObject
)
938 const auto pLineStyle
= dynamic_cast<WinMtfLineStyle
*>(pObject
.get());
939 const auto pFontStyle
= dynamic_cast<WinMtfFontStyle
*>(pObject
.get());
943 if (pFontStyle
->aFont
.GetFontHeight() == 0)
944 pFontStyle
->aFont
.SetFontHeight(423);
945 ImplMap(pFontStyle
->aFont
); // defaulting to 12pt
947 else if ( pLineStyle
)
949 Size
aSize(pLineStyle
->aLineInfo
.GetWidth(), 0);
950 aSize
= ImplMap(aSize
);
951 pLineStyle
->aLineInfo
.SetWidth(aSize
.Width());
954 std::vector
<std::unique_ptr
<GDIObj
>>::size_type nIndex
;
955 for ( nIndex
= 0; nIndex
< mvGDIObj
.size(); nIndex
++ )
957 if ( !mvGDIObj
[ nIndex
] )
960 if ( nIndex
== mvGDIObj
.size() )
961 ImplResizeObjectArry( mvGDIObj
.size() + 16 );
963 mvGDIObj
[ nIndex
] = std::move(pObject
);
966 void MtfTools::CreateObjectIndexed( sal_uInt32 nIndex
, std::unique_ptr
<GDIObj
> pObject
)
968 if ( ( nIndex
& ENHMETA_STOCK_OBJECT
) != 0 )
971 nIndex
&= 0xffff; // safety check: do not allow index to be > 65535
974 const auto pLineStyle
= dynamic_cast<WinMtfLineStyle
*>(pObject
.get());
975 const auto pFontStyle
= dynamic_cast<WinMtfFontStyle
*>(pObject
.get());
978 if (pFontStyle
->aFont
.GetFontHeight() == 0)
979 pFontStyle
->aFont
.SetFontHeight(423);
980 ImplMap(pFontStyle
->aFont
);
982 else if ( pLineStyle
)
984 Size
aSize(pLineStyle
->aLineInfo
.GetWidth(), 0);
985 pLineStyle
->aLineInfo
.SetWidth( ImplMap(aSize
).Width() );
987 if ( pLineStyle
->aLineInfo
.GetStyle() == LineStyle::Dash
)
989 aSize
.AdjustWidth(1 );
990 tools::Long nDashLen
, nDotLen
= ImplMap( aSize
).Width();
991 const bool bFail
= o3tl::checked_multiply
<tools::Long
>(nDotLen
, 3, nDashLen
);
994 pLineStyle
->aLineInfo
.SetDistance( nDotLen
);
995 pLineStyle
->aLineInfo
.SetDotLen( nDotLen
);
996 pLineStyle
->aLineInfo
.SetDashLen( nDotLen
* 3 );
1000 SAL_WARN("emfio", "DotLen too long: " << nDotLen
);
1005 if ( nIndex
>= mvGDIObj
.size() )
1006 ImplResizeObjectArry( nIndex
+ 16 );
1008 mvGDIObj
[ nIndex
] = std::move(pObject
);
1011 void MtfTools::CreateObject()
1013 CreateObject(std::make_unique
<GDIObj
>());
1016 void MtfTools::DeleteObject( sal_uInt32 nIndex
)
1018 if ( ( nIndex
& ENHMETA_STOCK_OBJECT
) == 0 )
1020 if ( nIndex
< mvGDIObj
.size() )
1022 mvGDIObj
[ nIndex
].reset();
1027 void MtfTools::IntersectClipRect( const tools::Rectangle
& rRect
)
1029 if (comphelper::IsFuzzing())
1031 mbClipNeedsUpdate
=true;
1032 if ((rRect
.Left()-rRect
.Right()==0) && (rRect
.Top()-rRect
.Bottom()==0))
1034 return; // empty rectangles cause trouble
1036 tools::Polygon
aPoly( rRect
);
1037 const tools::PolyPolygon
aPolyPolyRect( ImplMap( aPoly
) );
1038 maClipPath
.intersectClip( aPolyPolyRect
.getB2DPolyPolygon() );
1041 void MtfTools::ExcludeClipRect( const tools::Rectangle
& rRect
)
1043 if (comphelper::IsFuzzing())
1045 mbClipNeedsUpdate
=true;
1046 tools::Polygon
aPoly( rRect
);
1047 const tools::PolyPolygon
aPolyPolyRect( ImplMap( aPoly
) );
1048 maClipPath
.excludeClip( aPolyPolyRect
.getB2DPolyPolygon() );
1051 void MtfTools::MoveClipRegion( const Size
& rSize
)
1053 if (comphelper::IsFuzzing())
1055 mbClipNeedsUpdate
=true;
1056 maClipPath
.moveClipRegion( ImplMap( rSize
) );
1059 void MtfTools::SetClipPath( const tools::PolyPolygon
& rPolyPolygon
, RegionMode eClippingMode
, bool bIsMapped
)
1061 if (comphelper::IsFuzzing())
1063 mbClipNeedsUpdate
= true;
1064 tools::PolyPolygon
aPolyPolygon(rPolyPolygon
);
1068 if (!mbIsMapDevSet
&& (meMapMode
== MappingMode::MM_ISOTROPIC
|| meMapMode
== MappingMode::MM_ANISOTROPIC
))
1069 aPolyPolygon
= ImplScale(aPolyPolygon
);
1071 aPolyPolygon
= ImplMap(aPolyPolygon
);
1073 maClipPath
.setClipPath(aPolyPolygon
.getB2DPolyPolygon(), eClippingMode
);
1076 void MtfTools::SetDefaultClipPath()
1078 mbClipNeedsUpdate
= true;
1079 maClipPath
.setDefaultClipPath();
1082 MtfTools::MtfTools( GDIMetaFile
& rGDIMetaFile
, SvStream
& rStreamWMF
)
1083 : mnLatestTextAlign(90),
1084 mnTextAlign(TextAlignmentMode::TA_LEFT
| TextAlignmentMode::TA_TOP
| TextAlignmentMode::TA_NOUPDATECP
),
1085 maLatestBkColor(ColorTransparency
, 0x12345678),
1086 maBkColor(COL_WHITE
),
1087 mnLatestTextLayoutMode(vcl::text::ComplexTextLayoutFlags::Default
),
1088 mnTextLayoutMode(vcl::text::ComplexTextLayoutFlags::Default
),
1089 mnLatestBkMode(BackgroundMode::NONE
),
1090 mnBkMode(BackgroundMode::OPAQUE
),
1091 meLatestRasterOp(RasterOp::Invert
),
1092 meRasterOp(RasterOp::OverPaint
),
1094 meGfxMode(GraphicsMode::GM_COMPATIBLE
),
1095 meMapMode(MappingMode::MM_TEXT
),
1108 mpGDIMetaFile(&rGDIMetaFile
),
1109 mpInputStream(&rStreamWMF
),
1113 mbClockWiseArcDirection(false),
1114 mbFillStyleSelected(false),
1115 mbClipNeedsUpdate(true),
1116 mbComplexClip(false),
1117 mbIsMapWinSet(false),
1118 mbIsMapDevSet(false)
1120 SvLockBytes
*pLB
= mpInputStream
->GetLockBytes();
1124 pLB
->SetSynchronMode();
1127 mnStartPos
= mpInputStream
->Tell();
1130 mpGDIMetaFile
->AddAction( new MetaPushAction( vcl::PushFlags::CLIPREGION
) ); // The original clipregion has to be on top
1131 // of the stack so it can always be restored
1132 // this is necessary to be able to support
1133 // SetClipRgn( NULL ) and similar ClipRgn actions (SJ)
1135 maFont
.SetFamilyName( u
"Arial"_ustr
); // sj: #i57205#, we do have some scaling problems if using
1136 maFont
.SetCharSet( RTL_TEXTENCODING_MS_1252
); // the default font then most times a x11 font is used, we
1137 maFont
.SetFontHeight( 423 ); // will prevent this defining a font
1139 maLatestLineStyle
.aLineColor
= Color( 0x12, 0x34, 0x56 );
1140 maLatestFillStyle
.aFillColor
= Color( 0x12, 0x34, 0x56 );
1142 mnRop
= WMFRasterOp::Black
;
1143 meRasterOp
= RasterOp::OverPaint
;
1144 mpGDIMetaFile
->AddAction( new MetaRasterOpAction( RasterOp::OverPaint
) );
1147 MtfTools::~MtfTools() COVERITY_NOEXCEPT_FALSE
1149 mpGDIMetaFile
->AddAction( new MetaPopAction() );
1150 mpGDIMetaFile
->SetPrefMapMode(MapMode(MapUnit::Map100thMM
));
1151 if ( mrclFrame
.IsEmpty() )
1152 mpGDIMetaFile
->SetPrefSize( Size( mnDevWidth
, mnDevHeight
) );
1154 mpGDIMetaFile
->SetPrefSize( mrclFrame
.GetSize() );
1157 void MtfTools::UpdateClipRegion()
1159 if (!mbClipNeedsUpdate
)
1162 mbClipNeedsUpdate
= false;
1163 mbComplexClip
= false;
1165 mpGDIMetaFile
->AddAction( new MetaPopAction() ); // taking the original clipregion
1166 mpGDIMetaFile
->AddAction( new MetaPushAction( vcl::PushFlags::CLIPREGION
) );
1168 // skip for 'no clipping at all' case
1169 if( maClipPath
.isEmpty() )
1172 const basegfx::B2DPolyPolygon
& rClipPoly( maClipPath
.getClipPath() );
1174 mbComplexClip
= rClipPoly
.count() > 1
1175 || !basegfx::utils::isRectangle(rClipPoly
);
1177 // This makes cases like tdf#45820 work in reasonable time.
1180 mpGDIMetaFile
->AddAction(
1181 new MetaISectRegionClipRegionAction(
1182 vcl::Region(rClipPoly
)));
1183 mbComplexClip
= false;
1187 mpGDIMetaFile
->AddAction(
1188 new MetaISectRectClipRegionAction(
1189 vcl::unotools::rectangleFromB2DRectangle(
1190 rClipPoly
.getB2DRange())));
1194 void MtfTools::ImplSetNonPersistentLineColorTransparenz()
1196 WinMtfLineStyle
aTransparentLine( COL_TRANSPARENT
, true );
1197 if ( ! ( maLatestLineStyle
== aTransparentLine
) )
1199 maLatestLineStyle
= aTransparentLine
;
1200 mpGDIMetaFile
->AddAction( new MetaLineColorAction( aTransparentLine
.aLineColor
, !aTransparentLine
.bTransparent
) );
1204 void MtfTools::UpdateLineStyle()
1206 if (!( maLatestLineStyle
== maLineStyle
) )
1208 maLatestLineStyle
= maLineStyle
;
1209 mpGDIMetaFile
->AddAction( new MetaLineColorAction( maLineStyle
.aLineColor
, !maLineStyle
.bTransparent
) );
1213 void MtfTools::UpdateFillStyle()
1215 if ( !mbFillStyleSelected
) // SJ: #i57205# taking care of bkcolor if no brush is selected
1216 maFillStyle
= WinMtfFillStyle( maBkColor
, mnBkMode
== BackgroundMode::Transparent
);
1217 if (!( maLatestFillStyle
== maFillStyle
) )
1219 maLatestFillStyle
= maFillStyle
;
1220 if (maFillStyle
.aType
== WinMtfFillStyleType::Solid
)
1221 mpGDIMetaFile
->AddAction( new MetaFillColorAction( maFillStyle
.aFillColor
, !maFillStyle
.bTransparent
) );
1225 WMFRasterOp
MtfTools::SetRasterOp( WMFRasterOp nRasterOp
)
1227 WMFRasterOp nRetROP
= mnRop
;
1228 if ( nRasterOp
!= mnRop
)
1232 if ( mbNopMode
&& ( nRasterOp
!= WMFRasterOp::Nop
) )
1233 { // changing modes from WMFRasterOp::Nop so set pen and brush
1234 maFillStyle
= maNopFillStyle
;
1235 maLineStyle
= maNopLineStyle
;
1240 case WMFRasterOp::Not
:
1241 meRasterOp
= RasterOp::Invert
;
1244 case WMFRasterOp::XorPen
:
1245 meRasterOp
= RasterOp::Xor
;
1248 case WMFRasterOp::Nop
:
1250 meRasterOp
= RasterOp::OverPaint
;
1253 maNopFillStyle
= maFillStyle
;
1254 maNopLineStyle
= maLineStyle
;
1255 maFillStyle
= WinMtfFillStyle( COL_TRANSPARENT
, true );
1256 maLineStyle
= WinMtfLineStyle( COL_TRANSPARENT
, true );
1263 meRasterOp
= RasterOp::OverPaint
;
1267 if ( nRetROP
!= nRasterOp
)
1268 mpGDIMetaFile
->AddAction( new MetaRasterOpAction( meRasterOp
) );
1272 void MtfTools::StrokeAndFillPath( bool bStroke
, bool bFill
)
1274 if ( !maPathObj
.Count() )
1284 mpGDIMetaFile
->AddAction( new MetaPushAction( vcl::PushFlags::LINECOLOR
) );
1285 mpGDIMetaFile
->AddAction( new MetaLineColorAction( Color(), false ) );
1287 if ( maPathObj
.Count() == 1 )
1288 mpGDIMetaFile
->AddAction( new MetaPolygonAction( maPathObj
.GetObject( 0 ) ) );
1290 mpGDIMetaFile
->AddAction( new MetaPolyPolygonAction( maPathObj
) );
1293 mpGDIMetaFile
->AddAction( new MetaPopAction() );
1295 // tdf#142014 By default the stroke is made with hairline. If width is bigger, we need to use PolyLineAction
1298 // bFill is drawing hairstyle line. So we need to draw it only when the width is different than 0
1299 if ( !bFill
|| maLineStyle
.aLineInfo
.GetWidth() || ( maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
) )
1301 sal_uInt16 i
, nCount
= maPathObj
.Count();
1302 for ( i
= 0; i
< nCount
; i
++ )
1303 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( maPathObj
[ i
], maLineStyle
.aLineInfo
) );
1309 void MtfTools::DrawPixel( const Point
& rSource
, const Color
& rColor
)
1311 mpGDIMetaFile
->AddAction( new MetaPixelAction( ImplMap( rSource
), rColor
) );
1314 void MtfTools::MoveTo( const Point
& rPoint
, bool bRecordPath
)
1316 Point
aDest( ImplMap( rPoint
) );
1319 // fdo#57353 create new subpath for subsequent moves
1320 if ( maPathObj
.Count() )
1321 if ( maPathObj
[ maPathObj
.Count() - 1 ].GetSize() )
1322 maPathObj
.Insert( tools::Polygon() );
1323 maPathObj
.AddPoint( aDest
);
1328 void MtfTools::LineTo( const Point
& rPoint
, bool bRecordPath
)
1331 Point
aDest( ImplMap( rPoint
) );
1333 maPathObj
.AddPoint( aDest
);
1337 mpGDIMetaFile
->AddAction( new MetaLineAction( maActPos
, aDest
, maLineStyle
.aLineInfo
) );
1342 void MtfTools::DrawRectWithBGColor(const tools::Rectangle
& rRect
)
1344 WinMtfFillStyle aFillStyleBackup
= maFillStyle
;
1345 bool bTransparentBackup
= maLineStyle
.bTransparent
;
1346 BackgroundMode mnBkModeBackup
= mnBkMode
;
1348 const tools::Polygon
aPoly( rRect
);
1349 maLineStyle
.bTransparent
= true;
1350 maFillStyle
= maBkColor
;
1351 mnBkMode
= BackgroundMode::OPAQUE
;
1352 ImplSetNonPersistentLineColorTransparenz();
1353 DrawPolygon(aPoly
, false);
1354 mnBkMode
= mnBkModeBackup
; // The rectangle needs to be always drawned even if mode is transparent
1355 maFillStyle
= std::move(aFillStyleBackup
);
1356 maLineStyle
.bTransparent
= bTransparentBackup
;
1359 void MtfTools::DrawRect( const tools::Rectangle
& rRect
, bool bEdge
)
1364 if ( mbComplexClip
)
1366 tools::Polygon
aPoly( ImplMap( rRect
) );
1367 tools::PolyPolygon
aPolyPolyRect( aPoly
);
1368 tools::PolyPolygon aDest
;
1369 tools::PolyPolygon(maClipPath
.getClipPath()).GetIntersection( aPolyPolyRect
, aDest
);
1370 ImplDrawClippedPolyPolygon( aDest
);
1376 if ( maLineStyle
.aLineInfo
.GetWidth() || ( maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
) )
1378 ImplSetNonPersistentLineColorTransparenz();
1379 mpGDIMetaFile
->AddAction( new MetaRectAction( ImplMap( rRect
) ) );
1381 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( tools::Polygon( ImplMap( rRect
) ),maLineStyle
.aLineInfo
) );
1386 mpGDIMetaFile
->AddAction( new MetaRectAction( ImplMap( rRect
) ) );
1391 ImplSetNonPersistentLineColorTransparenz();
1392 mpGDIMetaFile
->AddAction( new MetaRectAction( ImplMap( rRect
) ) );
1397 void MtfTools::DrawRoundRect( const tools::Rectangle
& rRect
, const Size
& rSize
)
1402 mpGDIMetaFile
->AddAction( new MetaRoundRectAction( ImplMap( rRect
), std::abs( ImplMap( rSize
).Width() ), std::abs( ImplMap( rSize
).Height() ) ) );
1403 // tdf#142139 Wrong line width during WMF import
1404 if ( maLineStyle
.aLineInfo
.GetWidth() || ( maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
) )
1406 tools::Polygon
aRoundRectPoly( rRect
, rSize
.Width(), rSize
.Height() );
1407 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( ImplMap( aRoundRectPoly
), maLineStyle
.aLineInfo
) );
1411 void MtfTools::DrawEllipse( const tools::Rectangle
& rRect
)
1416 if ( maLineStyle
.aLineInfo
.GetWidth() || ( maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
) )
1418 Point
aCenter( ImplMap( rRect
.Center() ) );
1419 Size
aRad( ImplMap( Size( rRect
.GetWidth() / 2, rRect
.GetHeight() / 2 ) ) );
1421 ImplSetNonPersistentLineColorTransparenz();
1422 mpGDIMetaFile
->AddAction( new MetaEllipseAction( ImplMap( rRect
) ) );
1424 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( tools::Polygon( std::move(aCenter
), aRad
.Width(), aRad
.Height() ), maLineStyle
.aLineInfo
) );
1429 mpGDIMetaFile
->AddAction( new MetaEllipseAction( ImplMap( rRect
) ) );
1433 void MtfTools::DrawArc( const tools::Rectangle
& rRect
, const Point
& rStart
, const Point
& rEnd
, bool bTo
)
1439 tools::Rectangle
aRect( ImplMap( rRect
) );
1440 Point
aStart( ImplMap( rStart
) );
1441 Point
aEnd( ImplMap( rEnd
) );
1443 if ( maLineStyle
.aLineInfo
.GetWidth() || ( maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
) )
1445 if ( aStart
== aEnd
)
1446 { // SJ: #i53768# if start & end is identical, then we have to draw a full ellipse
1447 Point
aCenter( aRect
.Center() );
1448 Size
aRad( aRect
.GetWidth() / 2, aRect
.GetHeight() / 2 );
1450 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( tools::Polygon( std::move(aCenter
), aRad
.Width(), aRad
.Height() ), maLineStyle
.aLineInfo
) );
1453 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( tools::Polygon( aRect
, aStart
, aEnd
, PolyStyle::Arc
), maLineStyle
.aLineInfo
) );
1456 mpGDIMetaFile
->AddAction( new MetaArcAction( aRect
, aStart
, aEnd
) );
1462 void MtfTools::DrawPie( const tools::Rectangle
& rRect
, const Point
& rStart
, const Point
& rEnd
)
1467 tools::Rectangle
aRect( ImplMap( rRect
) );
1468 Point
aStart( ImplMap( rStart
) );
1469 Point
aEnd( ImplMap( rEnd
) );
1471 if ( maLineStyle
.aLineInfo
.GetWidth() || ( maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
) )
1473 ImplSetNonPersistentLineColorTransparenz();
1474 mpGDIMetaFile
->AddAction( new MetaPieAction( aRect
, aStart
, aEnd
) );
1476 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( tools::Polygon( aRect
, aStart
, aEnd
, PolyStyle::Pie
), maLineStyle
.aLineInfo
) );
1481 mpGDIMetaFile
->AddAction( new MetaPieAction( aRect
, aStart
, aEnd
) );
1485 void MtfTools::DrawChord( const tools::Rectangle
& rRect
, const Point
& rStart
, const Point
& rEnd
)
1490 tools::Rectangle
aRect( ImplMap( rRect
) );
1491 Point
aStart( ImplMap( rStart
) );
1492 Point
aEnd( ImplMap( rEnd
) );
1494 if ( maLineStyle
.aLineInfo
.GetWidth() || ( maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
) )
1496 ImplSetNonPersistentLineColorTransparenz();
1497 mpGDIMetaFile
->AddAction( new MetaChordAction( aRect
, aStart
, aEnd
) );
1499 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( tools::Polygon( aRect
, aStart
, aEnd
, PolyStyle::Chord
), maLineStyle
.aLineInfo
) );
1504 mpGDIMetaFile
->AddAction( new MetaChordAction( aRect
, aStart
, aEnd
) );
1508 void MtfTools::DrawPolygon( tools::Polygon rPolygon
, bool bRecordPath
)
1511 ImplMap( rPolygon
);
1513 maPathObj
.AddPolygon( rPolygon
);
1518 if ( mbComplexClip
)
1520 tools::PolyPolygon
aPolyPoly( rPolygon
);
1521 auto tmp
= maClipPath
.getClip();
1522 tmp
.intersectPolyPolygon(aPolyPoly
.getB2DPolyPolygon());
1523 tools::PolyPolygon
aDest(tmp
.getClipPoly());
1524 ImplDrawClippedPolyPolygon( aDest
);
1528 if ( maLineStyle
.aLineInfo
.GetWidth() || ( maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
) )
1530 sal_uInt16 nCount
= rPolygon
.GetSize();
1533 if ( rPolygon
[ nCount
- 1 ] != rPolygon
[ 0 ] )
1535 Point
aPoint( rPolygon
[ 0 ] );
1536 rPolygon
.Insert( nCount
, aPoint
);
1539 ImplSetNonPersistentLineColorTransparenz();
1540 mpGDIMetaFile
->AddAction( new MetaPolygonAction( rPolygon
) );
1542 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( std::move(rPolygon
), maLineStyle
.aLineInfo
) );
1548 if (maLatestFillStyle
.aType
!= WinMtfFillStyleType::Pattern
)
1549 mpGDIMetaFile
->AddAction( new MetaPolygonAction( std::move(rPolygon
) ) );
1551 SvtGraphicFill
aFill( tools::PolyPolygon( rPolygon
),
1554 SvtGraphicFill::fillNonZero
,
1555 SvtGraphicFill::fillTexture
,
1556 SvtGraphicFill::Transform(),
1558 SvtGraphicFill::hatchSingle
,
1560 SvtGraphicFill::GradientType::Linear
,
1564 Graphic (BitmapEx(maLatestFillStyle
.aBmp
)));
1566 SvMemoryStream aMemStm
;
1568 WriteSvtGraphicFill( aMemStm
, aFill
);
1570 mpGDIMetaFile
->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_BEGIN"_ostr
, 0,
1571 static_cast<const sal_uInt8
*>(aMemStm
.GetData()),
1572 aMemStm
.TellEnd() ) );
1573 mpGDIMetaFile
->AddAction( new MetaCommentAction( "XPATHFILL_SEQ_END"_ostr
) );
1581 void MtfTools::DrawPolyPolygon( tools::PolyPolygon
& rPolyPolygon
, bool bRecordPath
)
1585 ImplMap( rPolyPolygon
);
1588 maPathObj
.AddPolyPolygon( rPolyPolygon
);
1593 if ( mbComplexClip
)
1595 tools::PolyPolygon aDest
;
1596 tools::PolyPolygon(maClipPath
.getClipPath()).GetIntersection( rPolyPolygon
, aDest
);
1597 ImplDrawClippedPolyPolygon( aDest
);
1602 mpGDIMetaFile
->AddAction( new MetaPolyPolygonAction( rPolyPolygon
) );
1603 if (maLineStyle
.aLineInfo
.GetWidth() > 0 || maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
)
1605 for (sal_uInt16 nPoly
= 0; nPoly
< rPolyPolygon
.Count(); ++nPoly
)
1607 mpGDIMetaFile
->AddAction(new MetaPolyLineAction(rPolyPolygon
[nPoly
], maLineStyle
.aLineInfo
));
1614 void MtfTools::DrawPolyLine( tools::Polygon rPolygon
, bool bTo
, bool bRecordPath
)
1618 sal_uInt16 nPoints
= rPolygon
.GetSize();
1622 ImplMap( rPolygon
);
1625 rPolygon
[ 0 ] = maActPos
;
1626 maActPos
= rPolygon
[ rPolygon
.GetSize() - 1 ];
1629 maPathObj
.AddPolyLine( rPolygon
);
1633 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( std::move(rPolygon
), maLineStyle
.aLineInfo
) );
1637 void MtfTools::DrawPolyBezier( tools::Polygon rPolygon
, bool bTo
, bool bRecordPath
)
1639 sal_uInt16 nPoints
= rPolygon
.GetSize();
1640 if ( ( nPoints
< 4 ) || ( ( ( nPoints
- 4 ) % 3 ) != 0 ) )
1643 "EMF file error: Number of Bezier points is not set of three");
1647 ImplMap( rPolygon
);
1650 rPolygon
[ 0 ] = maActPos
;
1651 maActPos
= rPolygon
[ nPoints
- 1 ];
1654 for ( i
= 0; ( i
+ 2 ) < nPoints
; )
1656 rPolygon
.SetFlags( i
++, PolyFlags::Normal
);
1657 rPolygon
.SetFlags( i
++, PolyFlags::Control
);
1658 rPolygon
.SetFlags( i
++, PolyFlags::Control
);
1661 maPathObj
.AddPolyLine( rPolygon
);
1665 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( std::move(rPolygon
), maLineStyle
.aLineInfo
) );
1669 void MtfTools::DrawText( Point
& rPosition
, OUString
const & rText
, KernArray
* pDXArry
, tools::Long
* pDYArry
, bool bRecordPath
, GraphicsMode nGfxMode
)
1672 rPosition
= ImplMap( rPosition
);
1673 GraphicsMode nOldGfxMode
= GetGfxMode();
1674 SetGfxMode( GraphicsMode::GM_COMPATIBLE
);
1678 sal_Int64 nSumX
= 0, nSumY
= 0;
1679 for (sal_Int32 i
= 0; i
< rText
.getLength(); i
++ )
1681 nSumX
+= (*pDXArry
)[i
];
1683 // #i121382# Map DXArray using WorldTransform
1684 const Size
aSizeX(ImplMap(Size(nSumX
, 0)));
1685 const basegfx::B2DVector
aVectorX(aSizeX
.Width(), aSizeX
.Height());
1686 (*pDXArry
)[i
] = aVectorX
.getLength() * (nSumX
>= 0 ? 1 : -1);
1690 nSumY
+= pDYArry
[i
];
1692 const Size
aSizeY(ImplMap(Size(0, nSumY
)));
1693 const basegfx::B2DVector
aVectorY(aSizeY
.Width(), aSizeY
.Height());
1695 pDYArry
[i
] = basegfx::fround
<tools::Long
>(aVectorY
.getLength());
1696 pDYArry
[i
] *= (nSumY
>= 0 ? -1 : 1);
1700 if ( mnLatestTextLayoutMode
!= mnTextLayoutMode
)
1702 mnLatestTextLayoutMode
= mnTextLayoutMode
;
1703 mpGDIMetaFile
->AddAction( new MetaLayoutModeAction( mnTextLayoutMode
) );
1705 SetGfxMode(nGfxMode
);
1706 TextAlign eTextAlign
;
1707 if (mnTextAlign
& TA_BASELINE
)
1708 eTextAlign
= ALIGN_BASELINE
;
1709 else if (mnTextAlign
& TA_BOTTOM
)
1710 eTextAlign
= ALIGN_BOTTOM
;
1712 eTextAlign
= ALIGN_TOP
;
1713 bool bChangeFont
= false;
1714 if ( mnLatestTextAlign
!= mnTextAlign
)
1718 if ((mnLatestTextAlign
& TA_RTLREADING
) != (mnTextAlign
& TA_RTLREADING
))
1720 auto nFlags
= vcl::text::ComplexTextLayoutFlags::Default
;
1721 if (mnTextAlign
& TA_RTLREADING
)
1723 nFlags
= vcl::text::ComplexTextLayoutFlags::BiDiRtl
1724 | vcl::text::ComplexTextLayoutFlags::TextOriginLeft
;
1727 mpGDIMetaFile
->AddAction(new MetaLayoutModeAction(nFlags
));
1730 mnLatestTextAlign
= mnTextAlign
;
1731 mpGDIMetaFile
->AddAction( new MetaTextAlignAction( eTextAlign
) );
1733 if ( maLatestTextColor
!= maTextColor
)
1736 maLatestTextColor
= maTextColor
;
1737 mpGDIMetaFile
->AddAction( new MetaTextColorAction( maTextColor
) );
1739 bool bChangeFillColor
= false;
1740 if ( maLatestBkColor
!= maBkColor
)
1742 bChangeFillColor
= true;
1743 maLatestBkColor
= maBkColor
;
1745 if ( mnLatestBkMode
!= mnBkMode
)
1747 bChangeFillColor
= true;
1748 mnLatestBkMode
= mnBkMode
;
1750 if ( bChangeFillColor
)
1753 mpGDIMetaFile
->AddAction( new MetaTextFillColorAction( maFont
.GetFillColor(), !maFont
.IsTransparent() ) );
1755 vcl::Font
aTmp( maFont
);
1756 aTmp
.SetColor( maTextColor
);
1757 aTmp
.SetFillColor( maBkColor
);
1759 if( mnBkMode
== BackgroundMode::Transparent
)
1760 aTmp
.SetTransparent( true );
1762 aTmp
.SetTransparent( false );
1764 aTmp
.SetAlignment( eTextAlign
);
1766 if ( nGfxMode
== GraphicsMode::GM_ADVANCED
)
1768 // check whether there is a font rotation applied via transformation
1769 Point
aP1( ImplMap( Point() ) );
1770 Point
aP2( ImplMap( Point( 0, 100 ) ) );
1771 aP2
.setX(o3tl::saturating_sub(aP2
.X(), aP1
.X()));
1772 aP2
.setY(o3tl::saturating_sub(aP2
.Y(), aP1
.Y()));
1773 double fX
= aP2
.X();
1774 double fY
= aP2
.Y();
1777 double fOrientation
= basegfx::rad2deg(acos(fX
/ std::hypot(fX
, fY
)));
1779 fOrientation
= 360 - fOrientation
;
1782 aTmp
.SetOrientation( aTmp
.GetOrientation() + Degree10( static_cast<sal_Int16
>(fOrientation
) ) );
1786 if( mnTextAlign
& ( TA_UPDATECP
| TA_RIGHT_CENTER
) )
1788 // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
1789 SolarMutexGuard aGuard
;
1790 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
1791 sal_Int32 nTextWidth
;
1793 pVDev
->SetMapMode( MapMode( MapUnit::Map100thMM
) );
1794 pVDev
->SetFont( maFont
);
1795 const sal_uInt32 nLen
= pDXArry
? rText
.getLength() : 0;
1798 nTextWidth
= pVDev
->GetTextWidth( OUString(rText
[ nLen
- 1 ]) );
1800 nTextWidth
+= (*pDXArry
)[ nLen
- 2 ];
1801 // tdf#39894: We should consider the distance to next character cell origin
1802 aActPosDelta
.setX( (*pDXArry
)[ nLen
- 1 ] );
1805 aActPosDelta
.setY( pDYArry
[ nLen
- 1 ] );
1810 nTextWidth
= pVDev
->GetTextWidth( rText
);
1811 aActPosDelta
.setX( nTextWidth
);
1814 if( mnTextAlign
& TA_UPDATECP
)
1815 rPosition
= maActPos
;
1817 if (mnTextAlign
& TA_RIGHT_CENTER
)
1819 Point
aDisplacement(((mnTextAlign
& TA_RIGHT_CENTER
) == TA_CENTER
) ? nTextWidth
>> 1: nTextWidth
, 0);
1820 Point().RotateAround(aDisplacement
, maFont
.GetOrientation());
1821 rPosition
-= aDisplacement
;
1824 if( mnTextAlign
& TA_UPDATECP
)
1826 Point().RotateAround(aActPosDelta
, maFont
.GetOrientation());
1827 maActPos
= rPosition
+ aActPosDelta
;
1831 if(bChangeFont
|| (maLatestFont
!= aTmp
))
1833 maLatestFont
= aTmp
;
1834 rtl::Reference
<MetaFontAction
> aNewMetaFontAction(new MetaFontAction(aTmp
));
1836 // tdf#127471 end evtl active MetaFontAction scale corrector detector/collector
1837 maScaledFontHelper
.endCurrentMetaFontAction();
1839 // !bRecordPath: else no MetaTextArrayAction will be created
1840 // nullptr != pDXArry: detection only possible when text size is given
1841 // rText.getLength(): no useful check without text
1842 if(!bRecordPath
&& nullptr != pDXArry
&& 0 != rText
.getLength())
1844 maScaledFontHelper
.newCurrentMetaFontAction(aNewMetaFontAction
);
1847 mpGDIMetaFile
->AddAction( aNewMetaFontAction
);
1848 mpGDIMetaFile
->AddAction( new MetaTextAlignAction( aTmp
.GetAlignment() ) );
1849 mpGDIMetaFile
->AddAction( new MetaTextColorAction( aTmp
.GetColor() ) );
1850 mpGDIMetaFile
->AddAction( new MetaTextFillColorAction( aTmp
.GetFillColor(), !aTmp
.IsTransparent() ) );
1859 if ( pDXArry
&& pDYArry
)
1861 for (sal_Int32 i
= 0; i
< rText
.getLength(); ++i
)
1863 Point
aCharDisplacement( i
? (*pDXArry
)[i
-1] : 0, i
? pDYArry
[i
-1] : 0 );
1864 Point().RotateAround(aCharDisplacement
, maFont
.GetOrientation());
1865 mpGDIMetaFile
->AddAction( new MetaTextArrayAction( rPosition
+ aCharDisplacement
, OUString( rText
[i
] ), KernArraySpan(), {}, 0, 1 ) );
1870 /* because text without dx array is badly scaled, we
1871 will create such an array if necessary */
1873 KernArray aMyDXArray
;
1877 // only useful when we have an imported DXArray
1878 if(!rText
.isEmpty())
1880 maScaledFontHelper
.evaluateAlternativeFontScale(
1882 (*pDXArry
)[rText
.getLength() - 1] // extract imported TextLength
1888 // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
1889 SolarMutexGuard aGuard
;
1890 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
1891 pVDev
->SetMapMode(MapMode(MapUnit::Map100thMM
));
1892 pVDev
->SetFont( maLatestFont
);
1893 pVDev
->GetTextArray( rText
, &aMyDXArray
, 0, rText
.getLength());
1896 mpGDIMetaFile
->AddAction( new MetaTextArrayAction( rPosition
, rText
, pDX
, {}, 0, rText
.getLength() ) );
1899 SetGfxMode( nOldGfxMode
);
1902 void MtfTools::ImplDrawBitmap( const Point
& rPos
, const Size
& rSize
, const BitmapEx
& rBitmap
)
1904 BitmapEx
aBmpEx( rBitmap
);
1905 if ( mbComplexClip
)
1907 vcl::bitmap::DrawAndClipBitmap(rPos
, rSize
, rBitmap
, aBmpEx
, maClipPath
.getClipPath());
1910 if ( aBmpEx
.IsAlpha() )
1911 mpGDIMetaFile
->AddAction( new MetaBmpExScaleAction( rPos
, rSize
, aBmpEx
) );
1913 mpGDIMetaFile
->AddAction( new MetaBmpScaleAction( rPos
, rSize
, aBmpEx
.GetBitmap() ) );
1916 void MtfTools::ResolveBitmapActions( std::vector
<BSaveStruct
>& rSaveList
)
1920 size_t nObjects
= rSaveList
.size();
1921 size_t nObjectsLeft
= nObjects
;
1923 while ( nObjectsLeft
)
1926 size_t nObjectsOfSameSize
= 0;
1927 size_t nObjectStartIndex
= nObjects
- nObjectsLeft
;
1929 BSaveStruct
* pSave
= &rSaveList
[nObjectStartIndex
];
1930 tools::Rectangle
aRect( pSave
->aOutRect
);
1932 for ( i
= nObjectStartIndex
; i
< nObjects
; )
1934 nObjectsOfSameSize
++;
1935 if ( ++i
< nObjects
)
1937 pSave
= &rSaveList
[i
];
1938 if ( pSave
->aOutRect
!= aRect
)
1942 Point
aPos( ImplMap( aRect
.TopLeft() ) );
1943 Size
aSize( ImplMap( aRect
.GetSize() ) );
1945 for ( i
= nObjectStartIndex
; i
< ( nObjectStartIndex
+ nObjectsOfSameSize
); i
++ )
1947 pSave
= &rSaveList
[i
];
1949 sal_uInt32 nWinRop
= pSave
->nWinRop
;
1950 sal_uInt8 nRasterOperation
= static_cast<sal_uInt8
>( nWinRop
>> 16 );
1952 sal_uInt32 nUsed
= 0;
1953 if ( ( nRasterOperation
& 0xf ) != ( nRasterOperation
>> 4 ) )
1954 nUsed
|= 1; // pattern is used
1955 if ( ( nRasterOperation
& 0x33 ) != ( ( nRasterOperation
& 0xcc ) >> 2 ) )
1956 nUsed
|= 2; // source is used
1957 if ( ( nRasterOperation
& 0xaa ) != ( ( nRasterOperation
& 0x55 ) << 1 ) )
1958 nUsed
|= 4; // destination is used
1960 if ( (nUsed
& 1) && (( nUsed
& 2 ) == 0) && nWinRop
!= PATINVERT
)
1961 { // patterns aren't well supported yet
1962 WMFRasterOp nOldRop
= SetRasterOp( WMFRasterOp::NONE
); // in this case nRasterOperation is either 0 or 0xff
1964 DrawRect( aRect
, false );
1965 SetRasterOp( nOldRop
);
1969 bool bDrawn
= false;
1971 if ( i
== nObjectStartIndex
) // optimizing, sometimes it is possible to create just one transparent bitmap
1973 if ( nObjectsOfSameSize
== 2 )
1975 BSaveStruct
* pSave2
= &rSaveList
[i
+ 1];
1976 if ( ( pSave
->aBmpEx
.GetPrefSize() == pSave2
->aBmpEx
.GetPrefSize() ) &&
1977 ( pSave
->aBmpEx
.GetPrefMapMode() == pSave2
->aBmpEx
.GetPrefMapMode() ) )
1979 // TODO: Strictly speaking, we should
1980 // check whether mask is monochrome, and
1981 // whether image is black (upper branch)
1982 // or white (lower branch). Otherwise, the
1983 // effect is not the same as a masked
1985 if ( ( nWinRop
== SRCPAINT
) && ( pSave2
->nWinRop
== SRCAND
) )
1987 Bitmap
aMask( pSave
->aBmpEx
.GetBitmap() ); aMask
.Invert();
1988 BitmapEx
aBmpEx( pSave2
->aBmpEx
.GetBitmap(), aMask
);
1989 ImplDrawBitmap( aPos
, aSize
, aBmpEx
);
1993 // #i20085# This is just the other way
1994 // around as above. Only difference: mask
1996 else if ( ( nWinRop
== SRCAND
) && ( pSave2
->nWinRop
== SRCPAINT
) )
1998 const Bitmap
& rMask( pSave
->aBmpEx
.GetBitmap() );
1999 BitmapEx
aBmpEx( pSave2
->aBmpEx
.GetBitmap(), rMask
);
2000 ImplDrawBitmap( aPos
, aSize
, aBmpEx
);
2005 else if ( ( nWinRop
== SRCAND
) && ( pSave2
->nWinRop
== SRCINVERT
) )
2007 const Bitmap
& rMask( pSave
->aBmpEx
.GetBitmap() );
2008 BitmapEx
aBmpEx( pSave2
->aBmpEx
.GetBitmap(), rMask
);
2009 ImplDrawBitmap( aPos
, aSize
, aBmpEx
);
2020 WMFRasterOp nOldRop
= SetRasterOp( WMFRasterOp::CopyPen
);
2021 Bitmap
aBitmap( pSave
->aBmpEx
.GetBitmap() );
2022 sal_uInt32 nOperation
= ( nRasterOperation
& 0xf );
2023 switch( nOperation
)
2028 if(pSave
->aBmpEx
.IsAlpha())
2030 ImplDrawBitmap( aPos
, aSize
, pSave
->aBmpEx
);
2034 SetRasterOp( WMFRasterOp::XorPen
);
2035 ImplDrawBitmap( aPos
, aSize
, BitmapEx(aBitmap
) );
2036 SetRasterOp( WMFRasterOp::CopyPen
);
2037 Bitmap
aMask( aBitmap
);
2039 BitmapEx
aBmpEx( aBitmap
, aMask
);
2040 ImplDrawBitmap( aPos
, aSize
, aBmpEx
);
2041 if ( nOperation
== 0x1 )
2043 SetRasterOp( WMFRasterOp::Not
);
2044 DrawRect( aRect
, false );
2052 Bitmap
aMask( aBitmap
);
2053 if ( ( nUsed
& 1 ) && ( nRasterOperation
& 0xb0 ) == 0xb0 ) // pattern used
2055 aBitmap
.Convert( BmpConversion::N24Bit
);
2056 aBitmap
.Erase( maFillStyle
.aFillColor
);
2058 BitmapEx
aBmpEx( aBitmap
, aMask
);
2059 ImplDrawBitmap( aPos
, aSize
, aBmpEx
);
2060 if ( nOperation
== 0x7 )
2062 SetRasterOp( WMFRasterOp::Not
);
2063 DrawRect( aRect
, false );
2071 SetRasterOp( WMFRasterOp::Not
);
2072 DrawRect( aRect
, false );
2073 SetRasterOp( WMFRasterOp::CopyPen
);
2074 Bitmap
aMask( aBitmap
);
2076 BitmapEx
aBmpEx( aBitmap
, aMask
);
2077 ImplDrawBitmap( aPos
, aSize
, aBmpEx
);
2078 SetRasterOp( WMFRasterOp::XorPen
);
2079 ImplDrawBitmap( aPos
, aSize
, BitmapEx(aBitmap
) );
2080 if ( nOperation
== 0xb )
2082 SetRasterOp( WMFRasterOp::Not
);
2083 DrawRect( aRect
, false );
2091 Bitmap
aMask( aBitmap
);
2093 BitmapEx
aBmpEx( aBitmap
, aMask
);
2094 ImplDrawBitmap( aPos
, aSize
, aBmpEx
);
2095 SetRasterOp( WMFRasterOp::XorPen
);
2096 ImplDrawBitmap( aPos
, aSize
, BitmapEx(aBitmap
) );
2097 if ( nOperation
== 0xd )
2099 SetRasterOp( WMFRasterOp::Not
);
2100 DrawRect( aRect
, false );
2107 SetRasterOp( WMFRasterOp::XorPen
);
2108 ImplDrawBitmap( aPos
, aSize
, BitmapEx(aBitmap
) );
2109 if ( nOperation
== 0x9 )
2111 SetRasterOp( WMFRasterOp::Not
);
2112 DrawRect( aRect
, false );
2117 case 0x0 : // WHITENESS
2118 case 0xf : // BLACKNESS
2119 { // in this case nRasterOperation is either 0 or 0xff
2120 maFillStyle
= WinMtfFillStyle( Color( nRasterOperation
, nRasterOperation
, nRasterOperation
) );
2122 DrawRect( aRect
, false );
2126 case 0x3 : // only source is used
2129 if ( nRasterOperation
== 0x33 )
2131 if (pSave
->m_bForceAlpha
)
2133 ImplDrawBitmap(aPos
, aSize
, pSave
->aBmpEx
);
2137 ImplDrawBitmap(aPos
, aSize
, BitmapEx(aBitmap
));
2142 case 0x5 : // only destination is used
2144 SetRasterOp( WMFRasterOp::Not
);
2145 DrawRect( aRect
, false );
2149 case 0xa : // no operation
2152 SetRasterOp( nOldRop
);
2157 nObjectsLeft
-= nObjectsOfSameSize
;
2163 void MtfTools::SetDevOrg( const Point
& rPoint
)
2165 mnDevOrgX
= rPoint
.X();
2166 mnDevOrgY
= rPoint
.Y();
2169 void MtfTools::SetDevOrgOffset( sal_Int32 nXAdd
, sal_Int32 nYAdd
)
2175 void MtfTools::SetDevExt( const Size
& rSize
,bool regular
)
2177 if ( !(rSize
.Width() && rSize
.Height()) )
2182 case MappingMode::MM_ISOTROPIC
:
2183 case MappingMode::MM_ANISOTROPIC
:
2185 mnDevWidth
= rSize
.Width();
2186 mnDevHeight
= rSize
.Height();
2200 void MtfTools::ScaleDevExt(double fX
, double fY
)
2202 mnDevWidth
= basegfx::fround(mnDevWidth
* fX
);
2203 mnDevHeight
= basegfx::fround(mnDevHeight
* fY
);
2206 void MtfTools::SetWinOrg( const Point
& rPoint
, bool bIsEMF
)
2208 mnWinOrgX
= rPoint
.X();
2209 mnWinOrgY
= rPoint
.Y();
2217 void MtfTools::SetWinOrgOffset( sal_Int32 nXAdd
, sal_Int32 nYAdd
)
2223 void MtfTools::SetDevByWin() //mnWinExt...-stuff has to be assigned before.
2227 if ( meMapMode
== MappingMode::MM_ISOTROPIC
) //TODO: WHAT ABOUT ANISOTROPIC???
2230 if (o3tl::checked_add(mnWinExtX
, mnWinOrgX
, nX
) || o3tl::checked_sub(mnWinExtY
, mnWinOrgY
, nY
))
2232 Size
aSize(nX
>> MS_FIXPOINT_BITCOUNT_28_4
, -(nY
>> MS_FIXPOINT_BITCOUNT_28_4
));
2233 SetDevExt(aSize
, false);
2238 void MtfTools::SetWinExt(const Size
& rSize
, bool bIsEMF
)
2240 if (!(rSize
.Width() && rSize
.Height()))
2245 case MappingMode::MM_ISOTROPIC
:
2246 case MappingMode::MM_ANISOTROPIC
:
2248 mnWinExtX
= rSize
.Width();
2249 mnWinExtY
= rSize
.Height();
2254 mbIsMapWinSet
= true;
2264 void MtfTools::ScaleWinExt(double fX
, double fY
)
2266 mnWinExtX
= basegfx::fround(mnWinExtX
* fX
);
2267 mnWinExtY
= basegfx::fround(mnWinExtY
* fY
);
2270 void MtfTools::SetrclBounds( const tools::Rectangle
& rRect
)
2275 void MtfTools::SetrclFrame( const tools::Rectangle
& rRect
)
2280 void MtfTools::SetRefPix( const Size
& rSize
)
2282 mnPixX
= rSize
.Width();
2283 mnPixY
= rSize
.Height();
2286 void MtfTools::SetRefMill( const Size
& rSize
)
2288 mnMillX
= rSize
.Width();
2289 mnMillY
= rSize
.Height();
2292 void MtfTools::SetMapMode( MappingMode nMapMode
)
2294 meMapMode
= nMapMode
;
2295 if ( nMapMode
== MappingMode::MM_TEXT
&& !mbIsMapWinSet
)
2297 mnWinExtX
= mnDevWidth
;
2298 mnWinExtY
= mnDevHeight
;
2300 else if ( meMapMode
== MappingMode::MM_HIMETRIC
)
2302 sal_Int32 nWinExtX
, nWinExtY
;
2303 if (o3tl::checked_multiply
<sal_Int32
>(mnMillX
, 100, nWinExtX
) ||
2304 o3tl::checked_multiply
<sal_Int32
>(mnMillY
, 100, nWinExtY
))
2308 mnWinExtX
= nWinExtX
;
2309 mnWinExtY
= nWinExtY
;
2313 void MtfTools::SetWorldTransform( const XForm
& rXForm
)
2315 maXForm
.eM11
= rXForm
.eM11
;
2316 maXForm
.eM12
= rXForm
.eM12
;
2317 maXForm
.eM21
= rXForm
.eM21
;
2318 maXForm
.eM22
= rXForm
.eM22
;
2319 maXForm
.eDx
= rXForm
.eDx
;
2320 maXForm
.eDy
= rXForm
.eDy
;
2323 void MtfTools::ModifyWorldTransform( const XForm
& rXForm
, ModifyWorldTransformMode nMode
)
2327 case ModifyWorldTransformMode::MWT_IDENTITY
:
2329 maXForm
.eM11
= maXForm
.eM22
= 1.0f
;
2330 maXForm
.eM12
= maXForm
.eM21
= maXForm
.eDx
= maXForm
.eDy
= 0.0f
;
2334 case ModifyWorldTransformMode::MWT_RIGHTMULTIPLY
:
2335 case ModifyWorldTransformMode::MWT_LEFTMULTIPLY
:
2338 const XForm
* pRight
;
2340 if ( nMode
== ModifyWorldTransformMode::MWT_LEFTMULTIPLY
)
2355 aF
[0][0] = pLeft
->eM11
;
2356 aF
[0][1] = pLeft
->eM12
;
2358 aF
[1][0] = pLeft
->eM21
;
2359 aF
[1][1] = pLeft
->eM22
;
2361 aF
[2][0] = pLeft
->eDx
;
2362 aF
[2][1] = pLeft
->eDy
;
2365 bF
[0][0] = pRight
->eM11
;
2366 bF
[0][1] = pRight
->eM12
;
2368 bF
[1][0] = pRight
->eM21
;
2369 bF
[1][1] = pRight
->eM22
;
2371 bF
[2][0] = pRight
->eDx
;
2372 bF
[2][1] = pRight
->eDy
;
2376 for ( i
= 0; i
< 3; i
++ )
2378 for ( j
= 0; j
< 3; j
++ )
2381 for ( k
= 0; k
< 3; k
++ )
2382 cF
[i
][j
] += aF
[i
][k
] * bF
[k
][j
];
2385 maXForm
.eM11
= cF
[0][0];
2386 maXForm
.eM12
= cF
[0][1];
2387 maXForm
.eM21
= cF
[1][0];
2388 maXForm
.eM22
= cF
[1][1];
2389 maXForm
.eDx
= cF
[2][0];
2390 maXForm
.eDy
= cF
[2][1];
2393 case ModifyWorldTransformMode::MWT_SET
:
2395 SetWorldTransform(rXForm
);
2401 void MtfTools::Push() // !! to be able to access the original ClipRegion it
2402 { // is not allowed to use the MetaPushAction()
2403 UpdateClipRegion(); // (the original clip region is on top of the stack) (SJ)
2404 auto pSave
= std::make_shared
<SaveStruct
>();
2406 pSave
->aLineStyle
= maLineStyle
;
2407 pSave
->aFillStyle
= maFillStyle
;
2409 pSave
->aFont
= maFont
;
2410 pSave
->aTextColor
= maTextColor
;
2411 pSave
->nTextAlign
= mnTextAlign
;
2412 pSave
->nTextLayoutMode
= mnTextLayoutMode
;
2413 pSave
->eMapMode
= meMapMode
;
2414 pSave
->eGfxMode
= meGfxMode
;
2415 pSave
->nBkMode
= mnBkMode
;
2416 pSave
->aBkColor
= maBkColor
;
2417 pSave
->bClockWiseArcDirection
= mbClockWiseArcDirection
;
2418 pSave
->bFillStyleSelected
= mbFillStyleSelected
;
2420 pSave
->aActPos
= maActPos
;
2421 pSave
->aXForm
= maXForm
;
2422 pSave
->eRasterOp
= meRasterOp
;
2424 pSave
->nWinOrgX
= mnWinOrgX
;
2425 pSave
->nWinOrgY
= mnWinOrgY
;
2426 pSave
->nWinExtX
= mnWinExtX
;
2427 pSave
->nWinExtY
= mnWinExtY
;
2428 pSave
->nDevOrgX
= mnDevOrgX
;
2429 pSave
->nDevOrgY
= mnDevOrgY
;
2430 pSave
->nDevWidth
= mnDevWidth
;
2431 pSave
->nDevHeight
= mnDevHeight
;
2433 pSave
->maPathObj
= maPathObj
;
2434 pSave
->maClipPath
= maClipPath
;
2436 SAL_INFO("emfio", "\t\t GfxMode: " << static_cast<sal_uInt32
>(meGfxMode
));
2437 SAL_INFO("emfio", "\t\t MapMode: " << static_cast<sal_uInt32
>(meMapMode
));
2438 SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX
<< ", " << mnWinOrgY
);
2439 SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX
<< " x " << mnWinExtY
);
2440 SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX
<< ", " << mnDevOrgY
);
2441 SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth
<< " x " << mnDevHeight
);
2442 SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle
.aLineColor
<< " FillStyle: " << maFillStyle
.aFillColor
);
2443 mvSaveStack
.push_back( pSave
);
2446 void MtfTools::Pop( const sal_Int32 nSavedDC
)
2448 if ( nSavedDC
== 0 )
2452 if ( nSavedDC
< 0 ) // WMF/EMF, if negative, nSavedDC represents an instance relative to the current state.
2453 aIndex
= static_cast< sal_Int32
>( mvSaveStack
.size() ) + nSavedDC
;
2455 aIndex
= nSavedDC
; // WMF, if positive, nSavedDC represents a specific instance of the state to be restored.
2458 mvSaveStack
.clear();
2461 if( mvSaveStack
.empty() || ( aIndex
>= static_cast< sal_Int32
>( mvSaveStack
.size() ) ) )
2464 mvSaveStack
.resize( aIndex
+ 1 );
2465 // Backup the current data on the stack
2466 std::shared_ptr
<SaveStruct
>& pSave( mvSaveStack
.back() );
2468 maLineStyle
= pSave
->aLineStyle
;
2469 maFillStyle
= pSave
->aFillStyle
;
2471 maFont
= pSave
->aFont
;
2472 maTextColor
= pSave
->aTextColor
;
2473 mnTextAlign
= pSave
->nTextAlign
;
2474 mnTextLayoutMode
= pSave
->nTextLayoutMode
;
2475 mnBkMode
= pSave
->nBkMode
;
2476 meGfxMode
= pSave
->eGfxMode
;
2477 meMapMode
= pSave
->eMapMode
;
2478 maBkColor
= pSave
->aBkColor
;
2479 mbClockWiseArcDirection
= pSave
->bClockWiseArcDirection
;
2480 mbFillStyleSelected
= pSave
->bFillStyleSelected
;
2482 maActPos
= pSave
->aActPos
;
2483 maXForm
= pSave
->aXForm
;
2484 meRasterOp
= pSave
->eRasterOp
;
2486 mnWinOrgX
= pSave
->nWinOrgX
;
2487 mnWinOrgY
= pSave
->nWinOrgY
;
2488 mnWinExtX
= pSave
->nWinExtX
;
2489 mnWinExtY
= pSave
->nWinExtY
;
2490 mnDevOrgX
= pSave
->nDevOrgX
;
2491 mnDevOrgY
= pSave
->nDevOrgY
;
2492 mnDevWidth
= pSave
->nDevWidth
;
2493 mnDevHeight
= pSave
->nDevHeight
;
2495 maPathObj
= pSave
->maPathObj
;
2496 if ( ! ( maClipPath
== pSave
->maClipPath
) )
2498 maClipPath
= pSave
->maClipPath
;
2499 mbClipNeedsUpdate
= true;
2501 if ( meLatestRasterOp
!= meRasterOp
)
2503 mpGDIMetaFile
->AddAction( new MetaRasterOpAction( meRasterOp
) );
2504 meLatestRasterOp
= meRasterOp
;
2507 SAL_INFO("emfio", "\t\t GfxMode: " << static_cast<sal_uInt32
>(meGfxMode
));
2508 SAL_INFO("emfio", "\t\t MapMode: " << static_cast<sal_uInt32
>(meMapMode
));
2509 SAL_INFO("emfio", "\t\t WinOrg: " << mnWinOrgX
<< ", " << mnWinOrgY
);
2510 SAL_INFO("emfio", "\t\t WinExt: " << mnWinExtX
<< " x " << mnWinExtY
);
2511 SAL_INFO("emfio", "\t\t DevOrg: " << mnDevOrgX
<< ", " << mnDevOrgY
);
2512 SAL_INFO("emfio", "\t\t DevWidth/Height: " << mnDevWidth
<< " x " << mnDevHeight
);
2513 SAL_INFO("emfio", "\t\t LineStyle: " << maLineStyle
.aLineColor
<< " FillStyle: " << maFillStyle
.aFillColor
);
2514 mvSaveStack
.pop_back();
2517 void MtfTools::AddFromGDIMetaFile( GDIMetaFile
& rGDIMetaFile
)
2519 rGDIMetaFile
.Play( *mpGDIMetaFile
);
2522 void MtfTools::PassEMFPlusHeaderInfo()
2524 EMFP_DEBUG(printf ("\t\t\tadd EMF_PLUS header info\n"));
2527 sal_Int32 nLeft
, nRight
, nTop
, nBottom
;
2529 nLeft
= mrclFrame
.Left();
2530 nTop
= mrclFrame
.Top();
2531 nRight
= mrclFrame
.Right();
2532 nBottom
= mrclFrame
.Bottom();
2535 mem
.WriteInt32( nLeft
).WriteInt32( nTop
).WriteInt32( nRight
).WriteInt32( nBottom
);
2536 mem
.WriteInt32( mnPixX
).WriteInt32( mnPixY
).WriteInt32( mnMillX
).WriteInt32( mnMillY
);
2543 // add transformation matrix to be used in vcl's metaact.cxx for
2544 // rotate and scale operations
2545 mem
.WriteFloat( one
).WriteFloat( zero
).WriteFloat( zero
).WriteFloat( one
).WriteFloat( zero
).WriteFloat( zero
);
2547 // need to flush the stream, otherwise GetEndOfData will return 0
2548 // on windows where the function parameters are probably resolved in reverse order
2551 mpGDIMetaFile
->AddAction( new MetaCommentAction( "EMF_PLUS_HEADER_INFO"_ostr
, 0, static_cast<const sal_uInt8
*>(mem
.GetData()), mem
.GetEndOfData() ) );
2552 mpGDIMetaFile
->UseCanvas( true );
2555 void MtfTools::PassEMFPlus( void const * pBuffer
, sal_uInt32 nLength
)
2557 EMFP_DEBUG(printf ("\t\t\tadd EMF_PLUS comment length %04x\n",(unsigned int) nLength
));
2558 mpGDIMetaFile
->AddAction( new MetaCommentAction( "EMF_PLUS"_ostr
, 0, static_cast<const sal_uInt8
*>(pBuffer
), nLength
) );
2562 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */