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 <unotools/configmgr.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
);
69 void WinMtfClipPath::intersectClip( const basegfx::B2DPolyPolygon
& rPolyPolygon
)
71 maClip
.intersectPolyPolygon(rPolyPolygon
);
74 void WinMtfClipPath::excludeClip( const basegfx::B2DPolyPolygon
& rPolyPolygon
)
76 maClip
.subtractPolyPolygon(rPolyPolygon
);
79 void WinMtfClipPath::setClipPath( const basegfx::B2DPolyPolygon
& rB2DPoly
, sal_Int32 nClippingMode
)
81 switch ( nClippingMode
)
84 maClip
.unionPolyPolygon(rB2DPoly
);
87 maClip
.xorPolyPolygon(rB2DPoly
);
90 maClip
.subtractPolyPolygon(rB2DPoly
);
93 maClip
.intersectPolyPolygon(rB2DPoly
);
96 maClip
= basegfx::utils::B2DClipState(rB2DPoly
);
101 void WinMtfClipPath::moveClipRegion( const Size
& rSize
)
103 basegfx::B2DHomMatrix aTranslate
;
104 aTranslate
.translate(rSize
.Width(), rSize
.Height());
105 maClip
.transform(aTranslate
);
108 void WinMtfClipPath::setDefaultClipPath()
110 // Empty clip region - everything visible
111 maClip
= basegfx::utils::B2DClipState();
114 basegfx::B2DPolyPolygon
const & WinMtfClipPath::getClipPath() const
116 return maClip
.getClipPoly();
119 void WinMtfPathObj::AddPoint( const Point
& rPoint
)
122 Insert( tools::Polygon() );
123 tools::Polygon
& rPoly
= static_cast<tools::PolyPolygon
&>(*this)[ Count() - 1 ];
124 rPoly
.Insert( rPoly
.GetSize(), rPoint
);
128 void WinMtfPathObj::AddPolyLine( const tools::Polygon
& rPolyLine
)
131 Insert( tools::Polygon() );
132 tools::Polygon
& rPoly
= static_cast<tools::PolyPolygon
&>(*this)[ Count() - 1 ];
133 rPoly
.Insert( rPoly
.GetSize(), rPolyLine
);
137 void WinMtfPathObj::AddPolygon( const tools::Polygon
& rPoly
)
143 void WinMtfPathObj::AddPolyPolygon( const tools::PolyPolygon
& rPolyPoly
)
145 sal_uInt16 i
, nCount
= rPolyPoly
.Count();
146 for ( i
= 0; i
< nCount
; i
++ )
147 Insert( rPolyPoly
[ i
] );
151 void WinMtfPathObj::ClosePath()
155 tools::Polygon
& rPoly
= static_cast<tools::PolyPolygon
&>(*this)[ Count() - 1 ];
156 if ( rPoly
.GetSize() > 2 )
158 Point
aFirst( rPoly
[ 0 ] );
159 if ( aFirst
!= rPoly
[ rPoly
.GetSize() - 1 ] )
160 rPoly
.Insert( rPoly
.GetSize(), aFirst
);
166 WinMtfFontStyle::WinMtfFontStyle( LOGFONTW
const & rFont
)
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
);
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
);
182 switch ( rFont
.lfPitchAndFamily
& 0xf0 )
185 eFamily
= FAMILY_ROMAN
;
189 eFamily
= FAMILY_SWISS
;
193 eFamily
= FAMILY_MODERN
;
197 eFamily
= FAMILY_SCRIPT
;
201 eFamily
= FAMILY_DECORATIVE
;
205 eFamily
= FAMILY_DONTKNOW
;
208 aFont
.SetFamily( eFamily
);
211 switch ( rFont
.lfPitchAndFamily
& 0x0f )
214 ePitch
= PITCH_FIXED
;
220 ePitch
= PITCH_VARIABLE
;
223 aFont
.SetPitch( ePitch
);
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
;
245 eWeight
= WEIGHT_BLACK
;
246 aFont
.SetWeight( eWeight
);
249 aFont
.SetItalic( ITALIC_NORMAL
);
251 if( rFont
.lfUnderline
)
252 aFont
.SetUnderline( LINESTYLE_SINGLE
);
254 if( rFont
.lfStrikeOut
)
255 aFont
.SetStrikeout( STRIKEOUT_SINGLE
);
257 aFont
.SetOrientation( Degree10(static_cast<sal_Int16
>(rFont
.lfEscapement
)) );
259 Size
aFontSize( Size( rFont
.lfWidth
, rFont
.lfHeight
) );
260 if ( rFont
.lfHeight
> 0 )
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();
272 double fHeight
= (static_cast<double>(aFontSize
.Height()) * rFont
.lfHeight
) / nHeight
;
273 aFontSize
.setHeight( static_cast<sal_Int32
>( fHeight
+ 0.5 ) );
277 // Convert height to positive
278 aFontSize
.setHeight( std::abs(aFontSize
.Height()) );
279 aFont
.SetFontSize(aFontSize
);
281 // tdf#127471 adapt nFontWidth from Windows-like notation to
282 // NormedFontScaling if used for text scaling
284 const bool bFontScaledHorizontally(aFontSize
.Width() != 0 && aFontSize
.Width() != aFontSize
.Height());
286 if(bFontScaledHorizontally
)
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());
292 if(nAverageFontWidth
> 0)
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
));
302 ScaledFontDetectCorrectHelper::ScaledFontDetectCorrectHelper()
303 : maCurrentMetaFontAction(),
304 maAlternativeFontScales(),
305 maPositiveIdentifiedCases(),
306 maNegativeIdentifiedCases()
310 void ScaledFontDetectCorrectHelper::endCurrentMetaFontAction()
312 if(maCurrentMetaFontAction
.is() && !maAlternativeFontScales
.empty())
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);
320 for(double fPart
: maAlternativeFontScales
)
334 fAverage
/= static_cast<double>(maAlternativeFontScales
.size());
336 if(nPositive
>= nNegative
)
338 // correction intended, it is probably an old imported file
339 maPositiveIdentifiedCases
.push_back(std::pair
<rtl::Reference
<MetaFontAction
>, double>(maCurrentMetaFontAction
, fAverage
));
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
));
349 maCurrentMetaFontAction
.clear();
350 maAlternativeFontScales
.clear();
353 void ScaledFontDetectCorrectHelper::newCurrentMetaFontAction(rtl::Reference
<MetaFontAction
>& rNewMetaFontAction
)
355 maCurrentMetaFontAction
.clear();
356 maAlternativeFontScales
.clear();
358 if(!rNewMetaFontAction
.is())
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());
365 if(0 != rCandidate
.GetAverageFontWidth())
367 const tools::Long
nUnscaledAverageFontWidth(rCandidate
.GetOrCalculateAverageFontWidth());
369 // check 2nd (system-dependent) criteria for FontScale
370 if(nUnscaledAverageFontWidth
!= rCandidate
.GetFontHeight())
372 // FontScale is active, remember and use as current
373 maCurrentMetaFontAction
= rNewMetaFontAction
;
378 void ScaledFontDetectCorrectHelper::evaluateAlternativeFontScale(OUString
const & rText
, tools::Long nImportedTextLength
)
380 if(!maCurrentMetaFontAction
.is())
383 SolarMutexGuard aGuard
; // VirtualDevice is not thread-safe
384 ScopedVclPtrInstance
< VirtualDevice
> pTempVirtualDevice
;
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
;
394 // compare expected and imported TextLengths
395 if (nImportedTextLength
!= nMeasuredTextLength
)
397 const double fFactorText(static_cast<double>(nImportedTextLength
) / static_cast<double>(nMeasuredTextLength
));
398 const double fFactorTextPercent(fabs(1.0 - fFactorText
) * 100.0);
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
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
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
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);
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
;
429 const double fFactorCorrectedText(static_cast<double>(nImportedTextLength
) / static_cast<double>(nCorrectedTextLength
));
430 const double fFactorCorrectedTextPercent(fabs(1.0 - fFactorCorrectedText
) * 100.0);
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
)
443 maAlternativeFontScales
.push_back(fCorrectedAverageFontWidth
);
447 // also push, but negative to remember non-fitting case
448 maAlternativeFontScales
.push_back(-fCorrectedAverageFontWidth
);
453 void ScaledFontDetectCorrectHelper::applyAlternativeFontScale()
455 // make sure last evtl. detected current FontAction gets added to identified cases
456 endCurrentMetaFontAction();
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
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())
471 for(std::pair
<rtl::Reference
<MetaFontAction
>, double>& rCandidate
: maPositiveIdentifiedCases
)
473 rCandidate
.first
->correctFontScale(static_cast<tools::Long
>(rCandidate
.second
));
475 for(std::pair
<rtl::Reference
<MetaFontAction
>, double>& rCandidate
: maNegativeIdentifiedCases
)
477 rCandidate
.first
->correctFontScale(static_cast<tools::Long
>(rCandidate
.second
));
481 maPositiveIdentifiedCases
.clear();
482 maNegativeIdentifiedCases
.clear();
485 Color
MtfTools::ReadColor()
488 mpInputStream
->ReadUInt32( nColor
);
489 Color
aColor( COL_BLACK
);
490 if ( ( nColor
& 0xFFFF0000 ) == 0x01000000 )
492 size_t index
= nColor
& 0x0000FFFF;
493 if ( index
< maPalette
.aPaletteColors
.size() )
494 aColor
= maPalette
.aPaletteColors
[ index
];
496 SAL_INFO( "emfio", "\t\t Palette index out of range: " << index
);
499 aColor
= Color( static_cast<sal_uInt8
>( nColor
), static_cast<sal_uInt8
>( nColor
>> 8 ), static_cast<sal_uInt8
>( nColor
>> 16 ) );
501 SAL_INFO("emfio", "\t\tColor: " << aColor
);
505 Point
MtfTools::ImplScale(const Point
& rPoint
) // Hack to set varying defaults for incompletely defined files.
508 return Point(rPoint
.X() * UNDOCUMENTED_WIN_RCL_RELATION
- mrclFrame
.Left(),
509 rPoint
.Y() * UNDOCUMENTED_WIN_RCL_RELATION
- mrclFrame
.Top());
514 Point
MtfTools::ImplMap( const Point
& rPt
)
516 if ( mnWinExtX
&& mnWinExtY
)
521 double fX2
= fX
* maXForm
.eM11
+ fY
* maXForm
.eM21
+ maXForm
.eDx
;
522 double fY2
= fX
* maXForm
.eM12
+ fY
* maXForm
.eM22
+ maXForm
.eDy
;
524 if ( mnGfxMode
== GM_COMPATIBLE
)
532 fX2
*= HUNDREDTH_MILLIMETERS_PER_MILLIINCH
* 10;
533 fY2
*= HUNDREDTH_MILLIMETERS_PER_MILLIINCH
* 10;
534 double nDevOrgX
= mnDevOrgX
;
536 nDevOrgX
*= static_cast<double>(mnMillX
) * 100.0 / static_cast<double>(mnPixX
);
538 double nDevOrgY
= mnDevOrgY
;
540 nDevOrgY
*= static_cast<double>(mnMillY
) * 100.0 / static_cast<double>(mnPixY
);
548 fX2
*= HUNDREDTH_MILLIMETERS_PER_MILLIINCH
;
549 fY2
*= HUNDREDTH_MILLIMETERS_PER_MILLIINCH
;
550 double nDevOrgX
= mnDevOrgX
;
552 nDevOrgX
*= static_cast<double>(mnMillX
) * 100.0 / static_cast<double>(mnPixX
);
554 double nDevOrgY
= mnDevOrgY
;
556 nDevOrgY
*= static_cast<double>(mnMillY
) * 100.0 / static_cast<double>(mnPixY
);
564 fX2
*= HUNDREDTH_MILLIMETERS_PER_MILLIINCH
/ MILLIINCH_PER_TWIPS
;
565 fY2
*= HUNDREDTH_MILLIMETERS_PER_MILLIINCH
/ MILLIINCH_PER_TWIPS
;
566 double nDevOrgX
= mnDevOrgX
;
568 nDevOrgX
*= static_cast<double>(mnMillX
) * 100.0 / static_cast<double>(mnPixX
);
570 double nDevOrgY
= mnDevOrgY
;
572 nDevOrgY
*= static_cast<double>(mnMillY
) * 100.0 / static_cast<double>(mnPixY
);
582 double nDevOrgX
= mnDevOrgX
;
584 nDevOrgX
*= static_cast<double>(mnMillX
) * 100.0 / static_cast<double>(mnPixX
);
586 double nDevOrgY
= mnDevOrgY
;
588 nDevOrgY
*= static_cast<double>(mnMillY
) * 100.0 / static_cast<double>(mnPixY
);
592 case MM_HIMETRIC
: // in hundredth of a millimeter
596 double nDevOrgX
= mnDevOrgX
;
598 nDevOrgX
*= static_cast<double>(mnMillX
) * 100.0 / static_cast<double>(mnPixX
);
600 double nDevOrgY
= mnDevOrgY
;
602 nDevOrgY
*= static_cast<double>(mnMillY
) * 100.0 / static_cast<double>(mnPixY
);
608 if (mnPixX
== 0 || mnPixY
== 0)
610 SAL_WARN("emfio", "invalid scaling factor");
617 if ( mnMapMode
!= MM_TEXT
)
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
);
632 fX2
-= mrclFrame
.Left();
633 fY2
-= mrclFrame
.Top();
635 return Point(basegfx::fround(fX2
), basegfx::fround(fY2
));
641 Size
MtfTools::ImplMap(const Size
& rSz
, bool bDoWorldTransform
)
643 if ( mnWinExtX
&& mnWinExtY
)
645 // #i121382# apply the whole WorldTransform, else a rotation will be misinterpreted
646 double fWidth
, fHeight
;
647 if (bDoWorldTransform
)
649 fWidth
= rSz
.Width() * maXForm
.eM11
+ rSz
.Height() * maXForm
.eM21
;
650 fHeight
= rSz
.Width() * maXForm
.eM12
+ rSz
.Height() * maXForm
.eM22
;
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
))
664 fWidth
= rSz
.Width() * aScale
.getX();
665 fHeight
= rSz
.Height() * aScale
.getY();
668 if ( mnGfxMode
== GM_COMPATIBLE
)
674 fWidth
*= HUNDREDTH_MILLIMETERS_PER_MILLIINCH
*10;
675 fHeight
*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH
*10;
680 fWidth
*= HUNDREDTH_MILLIMETERS_PER_MILLIINCH
;
681 fHeight
*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH
;
690 case MM_HIMETRIC
: // in hundredth of millimeters
697 fWidth
*= HUNDREDTH_MILLIMETERS_PER_MILLIINCH
/MILLIINCH_PER_TWIPS
;
698 fHeight
*=-HUNDREDTH_MILLIMETERS_PER_MILLIINCH
/MILLIINCH_PER_TWIPS
;
703 if (mnPixX
== 0 || mnPixY
== 0)
705 SAL_WARN("emfio", "invalid scaling factor");
710 if ( mnMapMode
!= MM_TEXT
)
713 fHeight
/= mnWinExtY
;
714 fWidth
*= mnDevWidth
;
715 fHeight
*= mnDevHeight
;
717 fWidth
*= static_cast<double>(mnMillX
) * 100.0 / static_cast<double>(mnPixX
);
718 fHeight
*= static_cast<double>(mnMillY
) * 100.0 / static_cast<double>(mnPixY
);
724 return Size(basegfx::fround(fWidth
), basegfx::fround(fHeight
));
730 tools::Rectangle
MtfTools::ImplMap( const tools::Rectangle
& rRect
)
732 tools::Rectangle aRect
;
733 aRect
.SetPos(ImplMap(rRect
.TopLeft()));
734 aRect
.SaturatingSetSize(ImplMap(rRect
.GetSize()));
738 void MtfTools::ImplMap( vcl::Font
& rFont
)
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);
744 const auto nHeight
= aFontSize
.Height();
746 aFontSize
.setHeight( o3tl::saturating_toggle_sign(nHeight
) );
748 rFont
.SetFontSize( aFontSize
);
751 const bool bFail
= o3tl::checked_multiply(mnWinExtX
, mnWinExtY
, nResult
);
752 if (!bFail
&& nResult
< 0)
753 rFont
.SetOrientation( 3600_deg10
- rFont
.GetOrientation() );
756 tools::Polygon
& MtfTools::ImplMap( tools::Polygon
& rPolygon
)
758 sal_uInt16 nPoints
= rPolygon
.GetSize();
759 for ( sal_uInt16 i
= 0; i
< nPoints
; i
++ )
761 rPolygon
[ i
] = ImplMap( rPolygon
[ i
] );
766 void MtfTools::ImplScale( tools::Polygon
& rPolygon
)
768 sal_uInt16 nPoints
= rPolygon
.GetSize();
769 for ( sal_uInt16 i
= 0; i
< nPoints
; i
++ )
771 rPolygon
[ i
] = ImplScale( rPolygon
[ i
] );
775 tools::PolyPolygon
& MtfTools::ImplScale( tools::PolyPolygon
& rPolyPolygon
)
777 sal_uInt16 nPolys
= rPolyPolygon
.Count();
778 for (sal_uInt16 i
= 0; i
< nPolys
; ++i
)
780 ImplScale(rPolyPolygon
[i
]);
785 tools::PolyPolygon
& MtfTools::ImplMap( tools::PolyPolygon
& rPolyPolygon
)
787 sal_uInt16 nPolys
= rPolyPolygon
.Count();
788 for ( sal_uInt16 i
= 0; i
< nPolys
; ImplMap( rPolyPolygon
[ i
++ ] ) ) ;
792 void MtfTools::SelectObject( sal_uInt32 nIndex
)
794 if ( nIndex
& ENHMETA_STOCK_OBJECT
)
796 SAL_INFO ( "emfio", "\t\t ENHMETA_STOCK_OBJECT, StockObject Enumeration: 0x" << std::hex
<< nIndex
);
797 sal_uInt16 nStockId
= static_cast<sal_uInt8
>(nIndex
);
802 maFillStyle
= WinMtfFillStyle( COL_WHITE
);
803 mbFillStyleSelected
= true;
808 maFillStyle
= WinMtfFillStyle( COL_LIGHTGRAY
);
809 mbFillStyleSelected
= true;
814 maFillStyle
= WinMtfFillStyle( COL_GRAY
);
815 mbFillStyleSelected
= true;
820 maFillStyle
= WinMtfFillStyle( COL_GRAY7
);
821 mbFillStyleSelected
= true;
826 maFillStyle
= WinMtfFillStyle( COL_BLACK
);
827 mbFillStyleSelected
= true;
832 maFillStyle
= WinMtfFillStyle( COL_TRANSPARENT
, true );
833 mbFillStyleSelected
= true;
838 maLineStyle
= WinMtfLineStyle( COL_WHITE
);
843 maLineStyle
= WinMtfLineStyle( COL_BLACK
);
848 maLineStyle
= WinMtfLineStyle( COL_TRANSPARENT
, true );
857 nIndex
&= 0xffff; // safety check: don't allow index to be > 65535
859 GDIObj
*pGDIObj
= nullptr;
861 if ( nIndex
< mvGDIObj
.size() )
862 pGDIObj
= mvGDIObj
[ nIndex
].get();
867 SAL_INFO ( "emfio", "\t\t Index: " << nIndex
);
868 if (const auto pen
= dynamic_cast<WinMtfLineStyle
*>(pGDIObj
))
871 SAL_INFO ( "emfio", "\t Line Style, Color: 0x" << std::hex
<< maLineStyle
.aLineColor
872 << ", Weight: " << maLineStyle
.aLineInfo
.GetWidth() );
874 else if (const auto brush
= dynamic_cast<WinMtfFillStyle
*>(
877 maFillStyle
= *brush
;
878 mbFillStyleSelected
= true;
879 SAL_INFO("emfio", "\t\tBrush Object, Index: " << nIndex
<< ", Color: " << maFillStyle
.aFillColor
);
881 else if (const auto font
= dynamic_cast<WinMtfFontStyle
*>(
884 maFont
= font
->aFont
;
885 SAL_INFO("emfio", "\t\tFont Object, Index: " << nIndex
<< ", Font: " << maFont
.GetFamilyName() << " " << maFont
.GetStyleName());
887 else if (const auto palette
= dynamic_cast<WinMtfPalette
*>(
890 maPalette
= palette
->aPaletteColors
;
891 SAL_INFO("emfio", "\t\tPalette Object, Index: " << nIndex
<< ", Number of colours: " << maPalette
.aPaletteColors
.size() );
896 SAL_WARN("emfio", "Warning: Unable to find Object with index:" << nIndex
);
901 void MtfTools::SetTextLayoutMode( ComplexTextLayoutFlags nTextLayoutMode
)
903 mnTextLayoutMode
= nTextLayoutMode
;
906 void MtfTools::SetBkMode( BkMode nMode
)
911 void MtfTools::SetBkColor( const Color
& rColor
)
916 void MtfTools::SetTextColor( const Color
& rColor
)
918 maTextColor
= rColor
;
921 void MtfTools::SetTextAlign( sal_uInt32 nAlign
)
923 mnTextAlign
= nAlign
;
926 void MtfTools::ImplResizeObjectArry( sal_uInt32 nNewEntrys
)
928 mvGDIObj
.resize(nNewEntrys
);
931 void MtfTools::ImplDrawClippedPolyPolygon( const tools::PolyPolygon
& rPolyPoly
)
933 if ( !rPolyPoly
.Count() )
936 ImplSetNonPersistentLineColorTransparenz();
937 if ( rPolyPoly
.Count() == 1 )
939 if ( rPolyPoly
.IsRect() )
940 mpGDIMetaFile
->AddAction( new MetaRectAction( rPolyPoly
.GetBoundRect() ) );
943 tools::Polygon
aPoly( rPolyPoly
[ 0 ] );
944 sal_uInt16 nCount
= aPoly
.GetSize();
947 if ( aPoly
[ nCount
- 1 ] != aPoly
[ 0 ] )
949 Point
aPoint( aPoly
[ 0 ] );
950 aPoly
.Insert( nCount
, aPoint
);
952 mpGDIMetaFile
->AddAction( new MetaPolygonAction( aPoly
) );
957 mpGDIMetaFile
->AddAction( new MetaPolyPolygonAction( rPolyPoly
) );
960 void MtfTools::CreateObject( std::unique_ptr
<GDIObj
> pObject
)
964 const auto pLineStyle
= dynamic_cast<WinMtfLineStyle
*>(pObject
.get());
965 const auto pFontStyle
= dynamic_cast<WinMtfFontStyle
*>(pObject
.get());
969 if (pFontStyle
->aFont
.GetFontHeight() == 0)
970 pFontStyle
->aFont
.SetFontHeight(423);
971 ImplMap(pFontStyle
->aFont
); // defaulting to 12pt
973 else if ( pLineStyle
)
975 Size
aSize(pLineStyle
->aLineInfo
.GetWidth(), 0);
976 aSize
= ImplMap(aSize
);
977 pLineStyle
->aLineInfo
.SetWidth(aSize
.Width());
980 std::vector
<std::unique_ptr
<GDIObj
>>::size_type nIndex
;
981 for ( nIndex
= 0; nIndex
< mvGDIObj
.size(); nIndex
++ )
983 if ( !mvGDIObj
[ nIndex
] )
986 if ( nIndex
== mvGDIObj
.size() )
987 ImplResizeObjectArry( mvGDIObj
.size() + 16 );
989 mvGDIObj
[ nIndex
] = std::move(pObject
);
992 void MtfTools::CreateObjectIndexed( sal_uInt32 nIndex
, std::unique_ptr
<GDIObj
> pObject
)
994 if ( ( nIndex
& ENHMETA_STOCK_OBJECT
) != 0 )
997 nIndex
&= 0xffff; // safety check: do not allow index to be > 65535
1000 const auto pLineStyle
= dynamic_cast<WinMtfLineStyle
*>(pObject
.get());
1001 const auto pFontStyle
= dynamic_cast<WinMtfFontStyle
*>(pObject
.get());
1004 if (pFontStyle
->aFont
.GetFontHeight() == 0)
1005 pFontStyle
->aFont
.SetFontHeight(423);
1006 ImplMap(pFontStyle
->aFont
);
1008 else if ( pLineStyle
)
1010 Size
aSize(pLineStyle
->aLineInfo
.GetWidth(), 0);
1011 pLineStyle
->aLineInfo
.SetWidth( ImplMap(aSize
).Width() );
1013 if ( pLineStyle
->aLineInfo
.GetStyle() == LineStyle::Dash
)
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 );
1023 if ( nIndex
>= mvGDIObj
.size() )
1024 ImplResizeObjectArry( nIndex
+ 16 );
1026 mvGDIObj
[ nIndex
] = std::move(pObject
);
1029 void MtfTools::CreateObject()
1031 CreateObject(std::make_unique
<GDIObj
>());
1034 void MtfTools::DeleteObject( sal_uInt32 nIndex
)
1036 if ( ( nIndex
& ENHMETA_STOCK_OBJECT
) == 0 )
1038 if ( nIndex
< mvGDIObj
.size() )
1040 mvGDIObj
[ nIndex
].reset();
1045 void MtfTools::IntersectClipRect( const tools::Rectangle
& rRect
)
1047 if (utl::ConfigManager::IsFuzzing())
1049 mbClipNeedsUpdate
=true;
1050 if ((rRect
.Left()-rRect
.Right()==0) && (rRect
.Top()-rRect
.Bottom()==0))
1052 return; // empty rectangles cause trouble
1054 tools::Polygon
aPoly( rRect
);
1055 const tools::PolyPolygon
aPolyPolyRect( ImplMap( aPoly
) );
1056 maClipPath
.intersectClip( aPolyPolyRect
.getB2DPolyPolygon() );
1059 void MtfTools::ExcludeClipRect( const tools::Rectangle
& rRect
)
1061 if (utl::ConfigManager::IsFuzzing())
1063 mbClipNeedsUpdate
=true;
1064 tools::Polygon
aPoly( rRect
);
1065 const tools::PolyPolygon
aPolyPolyRect( ImplMap( aPoly
) );
1066 maClipPath
.excludeClip( aPolyPolyRect
.getB2DPolyPolygon() );
1069 void MtfTools::MoveClipRegion( const Size
& rSize
)
1071 if (utl::ConfigManager::IsFuzzing())
1073 mbClipNeedsUpdate
=true;
1074 maClipPath
.moveClipRegion( ImplMap( rSize
) );
1077 void MtfTools::SetClipPath( const tools::PolyPolygon
& rPolyPolygon
, sal_Int32 nClippingMode
, bool bIsMapped
)
1079 if (utl::ConfigManager::IsFuzzing())
1081 mbClipNeedsUpdate
= true;
1082 tools::PolyPolygon
aPolyPolygon(rPolyPolygon
);
1086 if (!mbIsMapDevSet
&& (mnMapMode
== MM_ISOTROPIC
|| mnMapMode
== MM_ANISOTROPIC
))
1087 aPolyPolygon
= ImplScale(aPolyPolygon
);
1089 aPolyPolygon
= ImplMap(aPolyPolygon
);
1091 maClipPath
.setClipPath(aPolyPolygon
.getB2DPolyPolygon(), nClippingMode
);
1094 void MtfTools::SetDefaultClipPath()
1096 mbClipNeedsUpdate
= true;
1097 maClipPath
.setDefaultClipPath();
1100 MtfTools::MtfTools( GDIMetaFile
& rGDIMetaFile
, SvStream
& rStreamWMF
)
1103 maLatestLineStyle(),
1106 maLatestFillStyle(),
1112 mnLatestTextAlign(90),
1113 mnTextAlign(TA_LEFT
| TA_TOP
| TA_NOUPDATECP
),
1114 maLatestTextColor(),
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
),
1128 mnGfxMode(GM_COMPATIBLE
),
1145 mpGDIMetaFile(&rGDIMetaFile
),
1146 mpInputStream(&rStreamWMF
),
1150 maScaledFontHelper(),
1152 mbFillStyleSelected(false),
1153 mbClipNeedsUpdate(true),
1154 mbComplexClip(false),
1155 mbIsMapWinSet(false),
1156 mbIsMapDevSet(false)
1158 SvLockBytes
*pLB
= mpInputStream
->GetLockBytes();
1162 pLB
->SetSynchronMode();
1165 mnStartPos
= mpInputStream
->Tell();
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)
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
1177 maLatestLineStyle
.aLineColor
= Color( 0x12, 0x34, 0x56 );
1178 maLatestFillStyle
.aFillColor
= Color( 0x12, 0x34, 0x56 );
1180 mnRop
= WMFRasterOp::Black
;
1181 meRasterOp
= RasterOp::OverPaint
;
1182 mpGDIMetaFile
->AddAction( new MetaRasterOpAction( RasterOp::OverPaint
) );
1185 MtfTools::~MtfTools() COVERITY_NOEXCEPT_FALSE
1187 mpGDIMetaFile
->AddAction( new MetaPopAction() );
1188 mpGDIMetaFile
->SetPrefMapMode(MapMode(MapUnit::Map100thMM
));
1189 if ( mrclFrame
.IsEmpty() )
1190 mpGDIMetaFile
->SetPrefSize( Size( mnDevWidth
, mnDevHeight
) );
1192 mpGDIMetaFile
->SetPrefSize( mrclFrame
.GetSize() );
1195 void MtfTools::UpdateClipRegion()
1197 if (!mbClipNeedsUpdate
)
1200 mbClipNeedsUpdate
= false;
1201 mbComplexClip
= false;
1203 mpGDIMetaFile
->AddAction( new MetaPopAction() ); // taking the original clipregion
1204 mpGDIMetaFile
->AddAction( new MetaPushAction( PushFlags::CLIPREGION
) );
1206 // skip for 'no clipping at all' case
1207 if( maClipPath
.isEmpty() )
1210 const basegfx::B2DPolyPolygon
& rClipPoly( maClipPath
.getClipPath() );
1212 mbComplexClip
= rClipPoly
.count() > 1
1213 || !basegfx::utils::isRectangle(rClipPoly
);
1215 // This makes cases like tdf#45820 work in reasonable time.
1218 mpGDIMetaFile
->AddAction(
1219 new MetaISectRegionClipRegionAction(
1220 vcl::Region(rClipPoly
)));
1221 mbComplexClip
= false;
1225 mpGDIMetaFile
->AddAction(
1226 new MetaISectRectClipRegionAction(
1227 vcl::unotools::rectangleFromB2DRectangle(
1228 rClipPoly
.getB2DRange())));
1232 void MtfTools::ImplSetNonPersistentLineColorTransparenz()
1234 WinMtfLineStyle
aTransparentLine( COL_TRANSPARENT
, true );
1235 if ( ! ( maLatestLineStyle
== aTransparentLine
) )
1237 maLatestLineStyle
= aTransparentLine
;
1238 mpGDIMetaFile
->AddAction( new MetaLineColorAction( aTransparentLine
.aLineColor
, !aTransparentLine
.bTransparent
) );
1242 void MtfTools::UpdateLineStyle()
1244 if (!( maLatestLineStyle
== maLineStyle
) )
1246 maLatestLineStyle
= maLineStyle
;
1247 mpGDIMetaFile
->AddAction( new MetaLineColorAction( maLineStyle
.aLineColor
, !maLineStyle
.bTransparent
) );
1251 void MtfTools::UpdateFillStyle()
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
) )
1257 maLatestFillStyle
= maFillStyle
;
1258 if (maFillStyle
.aType
== WinMtfFillStyleType::Solid
)
1259 mpGDIMetaFile
->AddAction( new MetaFillColorAction( maFillStyle
.aFillColor
, !maFillStyle
.bTransparent
) );
1263 WMFRasterOp
MtfTools::SetRasterOp( WMFRasterOp nRasterOp
)
1265 WMFRasterOp nRetROP
= mnRop
;
1266 if ( nRasterOp
!= mnRop
)
1270 if ( mbNopMode
&& ( nRasterOp
!= WMFRasterOp::Nop
) )
1271 { // changing modes from WMFRasterOp::Nop so set pen and brush
1272 maFillStyle
= maNopFillStyle
;
1273 maLineStyle
= maNopLineStyle
;
1278 case WMFRasterOp::Not
:
1279 meRasterOp
= RasterOp::Invert
;
1282 case WMFRasterOp::XorPen
:
1283 meRasterOp
= RasterOp::Xor
;
1286 case WMFRasterOp::Nop
:
1288 meRasterOp
= RasterOp::OverPaint
;
1291 maNopFillStyle
= maFillStyle
;
1292 maNopLineStyle
= maLineStyle
;
1293 maFillStyle
= WinMtfFillStyle( COL_TRANSPARENT
, true );
1294 maLineStyle
= WinMtfLineStyle( COL_TRANSPARENT
, true );
1301 meRasterOp
= RasterOp::OverPaint
;
1305 if ( nRetROP
!= nRasterOp
)
1306 mpGDIMetaFile
->AddAction( new MetaRasterOpAction( meRasterOp
) );
1310 void MtfTools::StrokeAndFillPath( bool bStroke
, bool bFill
)
1312 if ( !maPathObj
.Count() )
1322 mpGDIMetaFile
->AddAction( new MetaPushAction( PushFlags::LINECOLOR
) );
1323 mpGDIMetaFile
->AddAction( new MetaLineColorAction( Color(), false ) );
1325 if ( maPathObj
.Count() == 1 )
1326 mpGDIMetaFile
->AddAction( new MetaPolygonAction( maPathObj
.GetObject( 0 ) ) );
1328 mpGDIMetaFile
->AddAction( new MetaPolyPolygonAction( maPathObj
) );
1331 mpGDIMetaFile
->AddAction( new MetaPopAction() );
1333 // tdf#142014 By default the stroke is made with hairline. If width is bigger, we need to use PolyLineAction
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
) )
1339 sal_uInt16 i
, nCount
= maPathObj
.Count();
1340 for ( i
= 0; i
< nCount
; i
++ )
1341 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( maPathObj
[ i
], maLineStyle
.aLineInfo
) );
1347 void MtfTools::DrawPixel( const Point
& rSource
, const Color
& rColor
)
1349 mpGDIMetaFile
->AddAction( new MetaPixelAction( ImplMap( rSource
), rColor
) );
1352 void MtfTools::MoveTo( const Point
& rPoint
, bool bRecordPath
)
1354 Point
aDest( ImplMap( rPoint
) );
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
);
1366 void MtfTools::LineTo( const Point
& rPoint
, bool bRecordPath
)
1369 Point
aDest( ImplMap( rPoint
) );
1371 maPathObj
.AddPoint( aDest
);
1375 mpGDIMetaFile
->AddAction( new MetaLineAction( maActPos
, aDest
, maLineStyle
.aLineInfo
) );
1380 void MtfTools::DrawRectWithBGColor(const tools::Rectangle
& rRect
)
1382 WinMtfFillStyle aFillStyleBackup
= maFillStyle
;
1383 bool aTransparentBackup
= maLineStyle
.bTransparent
;
1384 BkMode mnBkModeBackup
= mnBkMode
;
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
;
1397 void MtfTools::DrawRect( const tools::Rectangle
& rRect
, bool bEdge
)
1402 if ( mbComplexClip
)
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
);
1414 if ( maLineStyle
.aLineInfo
.GetWidth() || ( maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
) )
1416 ImplSetNonPersistentLineColorTransparenz();
1417 mpGDIMetaFile
->AddAction( new MetaRectAction( ImplMap( rRect
) ) );
1419 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( tools::Polygon( ImplMap( rRect
) ),maLineStyle
.aLineInfo
) );
1424 mpGDIMetaFile
->AddAction( new MetaRectAction( ImplMap( rRect
) ) );
1429 ImplSetNonPersistentLineColorTransparenz();
1430 mpGDIMetaFile
->AddAction( new MetaRectAction( ImplMap( rRect
) ) );
1435 void MtfTools::DrawRoundRect( const tools::Rectangle
& rRect
, const Size
& rSize
)
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
) )
1444 tools::Polygon
aRoundRectPoly( rRect
, rSize
.Width(), rSize
.Height() );
1445 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( ImplMap( aRoundRectPoly
), maLineStyle
.aLineInfo
) );
1449 void MtfTools::DrawEllipse( const tools::Rectangle
& rRect
)
1454 if ( maLineStyle
.aLineInfo
.GetWidth() || ( maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
) )
1456 Point
aCenter( ImplMap( rRect
.Center() ) );
1457 Size
aRad( ImplMap( Size( rRect
.GetWidth() / 2, rRect
.GetHeight() / 2 ) ) );
1459 ImplSetNonPersistentLineColorTransparenz();
1460 mpGDIMetaFile
->AddAction( new MetaEllipseAction( ImplMap( rRect
) ) );
1462 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( tools::Polygon( aCenter
, aRad
.Width(), aRad
.Height() ), maLineStyle
.aLineInfo
) );
1467 mpGDIMetaFile
->AddAction( new MetaEllipseAction( ImplMap( rRect
) ) );
1471 void MtfTools::DrawArc( const tools::Rectangle
& rRect
, const Point
& rStart
, const Point
& rEnd
, bool bTo
)
1477 tools::Rectangle
aRect( ImplMap( rRect
) );
1478 Point
aStart( ImplMap( rStart
) );
1479 Point
aEnd( ImplMap( rEnd
) );
1481 if ( maLineStyle
.aLineInfo
.GetWidth() || ( maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
) )
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 );
1488 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( tools::Polygon( aCenter
, aRad
.Width(), aRad
.Height() ), maLineStyle
.aLineInfo
) );
1491 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( tools::Polygon( aRect
, aStart
, aEnd
, PolyStyle::Arc
), maLineStyle
.aLineInfo
) );
1494 mpGDIMetaFile
->AddAction( new MetaArcAction( aRect
, aStart
, aEnd
) );
1500 void MtfTools::DrawPie( const tools::Rectangle
& rRect
, const Point
& rStart
, const Point
& rEnd
)
1505 tools::Rectangle
aRect( ImplMap( rRect
) );
1506 Point
aStart( ImplMap( rStart
) );
1507 Point
aEnd( ImplMap( rEnd
) );
1509 if ( maLineStyle
.aLineInfo
.GetWidth() || ( maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
) )
1511 ImplSetNonPersistentLineColorTransparenz();
1512 mpGDIMetaFile
->AddAction( new MetaPieAction( aRect
, aStart
, aEnd
) );
1514 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( tools::Polygon( aRect
, aStart
, aEnd
, PolyStyle::Pie
), maLineStyle
.aLineInfo
) );
1519 mpGDIMetaFile
->AddAction( new MetaPieAction( aRect
, aStart
, aEnd
) );
1523 void MtfTools::DrawChord( const tools::Rectangle
& rRect
, const Point
& rStart
, const Point
& rEnd
)
1528 tools::Rectangle
aRect( ImplMap( rRect
) );
1529 Point
aStart( ImplMap( rStart
) );
1530 Point
aEnd( ImplMap( rEnd
) );
1532 if ( maLineStyle
.aLineInfo
.GetWidth() || ( maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
) )
1534 ImplSetNonPersistentLineColorTransparenz();
1535 mpGDIMetaFile
->AddAction( new MetaChordAction( aRect
, aStart
, aEnd
) );
1537 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( tools::Polygon( aRect
, aStart
, aEnd
, PolyStyle::Chord
), maLineStyle
.aLineInfo
) );
1542 mpGDIMetaFile
->AddAction( new MetaChordAction( aRect
, aStart
, aEnd
) );
1546 void MtfTools::DrawPolygon( tools::Polygon rPolygon
, bool bRecordPath
)
1549 ImplMap( rPolygon
);
1551 maPathObj
.AddPolygon( rPolygon
);
1556 if ( mbComplexClip
)
1558 tools::PolyPolygon
aPolyPoly( rPolygon
);
1559 auto tmp
= maClipPath
.getClip();
1560 tmp
.intersectPolyPolygon(aPolyPoly
.getB2DPolyPolygon());
1561 tools::PolyPolygon
aDest(tmp
.getClipPoly());
1562 ImplDrawClippedPolyPolygon( aDest
);
1566 if ( maLineStyle
.aLineInfo
.GetWidth() || ( maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
) )
1568 sal_uInt16 nCount
= rPolygon
.GetSize();
1571 if ( rPolygon
[ nCount
- 1 ] != rPolygon
[ 0 ] )
1573 Point
aPoint( rPolygon
[ 0 ] );
1574 rPolygon
.Insert( nCount
, aPoint
);
1577 ImplSetNonPersistentLineColorTransparenz();
1578 mpGDIMetaFile
->AddAction( new MetaPolygonAction( rPolygon
) );
1580 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( rPolygon
, maLineStyle
.aLineInfo
) );
1586 if (maLatestFillStyle
.aType
!= WinMtfFillStyleType::Pattern
)
1587 mpGDIMetaFile
->AddAction( new MetaPolygonAction( rPolygon
) );
1589 SvtGraphicFill
aFill( tools::PolyPolygon( rPolygon
),
1592 SvtGraphicFill::fillNonZero
,
1593 SvtGraphicFill::fillTexture
,
1594 SvtGraphicFill::Transform(),
1596 SvtGraphicFill::hatchSingle
,
1598 SvtGraphicFill::GradientType::Linear
,
1602 Graphic (BitmapEx(maLatestFillStyle
.aBmp
)));
1604 SvMemoryStream aMemStm
;
1606 WriteSvtGraphicFill( aMemStm
, aFill
);
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" ) );
1619 void MtfTools::DrawPolyPolygon( tools::PolyPolygon
& rPolyPolygon
, bool bRecordPath
)
1623 ImplMap( rPolyPolygon
);
1626 maPathObj
.AddPolyPolygon( rPolyPolygon
);
1631 if ( mbComplexClip
)
1633 tools::PolyPolygon aDest
;
1634 tools::PolyPolygon(maClipPath
.getClipPath()).GetIntersection( rPolyPolygon
, aDest
);
1635 ImplDrawClippedPolyPolygon( aDest
);
1640 mpGDIMetaFile
->AddAction( new MetaPolyPolygonAction( rPolyPolygon
) );
1641 if (maLineStyle
.aLineInfo
.GetWidth() > 0 || maLineStyle
.aLineInfo
.GetStyle() == LineStyle::Dash
)
1643 for (sal_uInt16 nPoly
= 0; nPoly
< rPolyPolygon
.Count(); ++nPoly
)
1645 mpGDIMetaFile
->AddAction(new MetaPolyLineAction(rPolyPolygon
[nPoly
], maLineStyle
.aLineInfo
));
1652 void MtfTools::DrawPolyLine( tools::Polygon rPolygon
, bool bTo
, bool bRecordPath
)
1656 sal_uInt16 nPoints
= rPolygon
.GetSize();
1660 ImplMap( rPolygon
);
1663 rPolygon
[ 0 ] = maActPos
;
1664 maActPos
= rPolygon
[ rPolygon
.GetSize() - 1 ];
1667 maPathObj
.AddPolyLine( rPolygon
);
1671 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( rPolygon
, maLineStyle
.aLineInfo
) );
1675 void MtfTools::DrawPolyBezier( tools::Polygon rPolygon
, bool bTo
, bool bRecordPath
)
1677 sal_uInt16 nPoints
= rPolygon
.GetSize();
1678 if ( ( nPoints
< 4 ) || ( ( ( nPoints
- 4 ) % 3 ) != 0 ) )
1683 ImplMap( rPolygon
);
1686 rPolygon
[ 0 ] = maActPos
;
1687 maActPos
= rPolygon
[ nPoints
- 1 ];
1690 for ( i
= 0; ( i
+ 2 ) < nPoints
; )
1692 rPolygon
.SetFlags( i
++, PolyFlags::Normal
);
1693 rPolygon
.SetFlags( i
++, PolyFlags::Control
);
1694 rPolygon
.SetFlags( i
++, PolyFlags::Control
);
1697 maPathObj
.AddPolyLine( rPolygon
);
1701 mpGDIMetaFile
->AddAction( new MetaPolyLineAction( rPolygon
, maLineStyle
.aLineInfo
) );
1705 void MtfTools::DrawText( Point
& rPosition
, OUString
const & rText
, tools::Long
* pDXArry
, tools::Long
* pDYArry
, bool bRecordPath
, sal_Int32 nGfxMode
)
1708 rPosition
= ImplMap( rPosition
);
1709 sal_Int32 nOldGfxMode
= GetGfxMode();
1710 SetGfxMode( GM_COMPATIBLE
);
1714 sal_Int32 nSumX
= 0, nSumY
= 0;
1715 for (sal_Int32 i
= 0; i
< rText
.getLength(); i
++ )
1717 nSumX
+= pDXArry
[i
];
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);
1727 nSumY
+= pDYArry
[i
];
1729 const Size
aSizeY(ImplMap(Size(0, nSumY
)));
1730 const basegfx::B2DVector
aVectorY(aSizeY
.Width(), aSizeY
.Height());
1732 pDYArry
[i
] = basegfx::fround(aVectorY
.getLength());
1733 pDYArry
[i
] *= (nSumY
>= 0 ? -1 : 1);
1737 if ( mnLatestTextLayoutMode
!= mnTextLayoutMode
)
1739 mnLatestTextLayoutMode
= mnTextLayoutMode
;
1740 mpGDIMetaFile
->AddAction( new MetaLayoutModeAction( mnTextLayoutMode
) );
1742 SetGfxMode( nGfxMode
);
1743 TextAlign eTextAlign
;
1744 if ( ( mnTextAlign
& TA_BASELINE
) == TA_BASELINE
)
1745 eTextAlign
= TextAlign::Baseline
;
1746 else if( ( mnTextAlign
& TA_BOTTOM
) == TA_BOTTOM
)
1747 eTextAlign
= TextAlign::Bottom
;
1749 eTextAlign
= TextAlign::Top
;
1750 bool bChangeFont
= false;
1751 if ( mnLatestTextAlign
!= mnTextAlign
)
1754 mnLatestTextAlign
= mnTextAlign
;
1755 mpGDIMetaFile
->AddAction( new MetaTextAlignAction( eTextAlign
) );
1757 if ( maLatestTextColor
!= maTextColor
)
1760 maLatestTextColor
= maTextColor
;
1761 mpGDIMetaFile
->AddAction( new MetaTextColorAction( maTextColor
) );
1763 bool bChangeFillColor
= false;
1764 if ( maLatestBkColor
!= maBkColor
)
1766 bChangeFillColor
= true;
1767 maLatestBkColor
= maBkColor
;
1769 if ( mnLatestBkMode
!= mnBkMode
)
1771 bChangeFillColor
= true;
1772 mnLatestBkMode
= mnBkMode
;
1774 if ( bChangeFillColor
)
1777 mpGDIMetaFile
->AddAction( new MetaTextFillColorAction( maFont
.GetFillColor(), !maFont
.IsTransparent() ) );
1779 vcl::Font
aTmp( maFont
);
1780 aTmp
.SetColor( maTextColor
);
1781 aTmp
.SetFillColor( maBkColor
);
1783 if( mnBkMode
== BkMode::Transparent
)
1784 aTmp
.SetTransparent( true );
1786 aTmp
.SetTransparent( false );
1788 aTmp
.SetAlignment( eTextAlign
);
1790 if ( nGfxMode
== GM_ADVANCED
)
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();
1801 double fOrientation
= acos( fX
/ sqrt( fX
* fX
+ fY
* fY
) ) * 57.29577951308;
1803 fOrientation
= 360 - fOrientation
;
1806 aTmp
.SetOrientation( aTmp
.GetOrientation() + Degree10( static_cast<sal_Int16
>(fOrientation
) ) );
1810 if( mnTextAlign
& ( TA_UPDATECP
| TA_RIGHT_CENTER
) )
1812 // #i117968# VirtualDevice is not thread safe, but filter is used in multithreading
1813 SolarMutexGuard aGuard
;
1814 ScopedVclPtrInstance
< VirtualDevice
> pVDev
;
1815 sal_Int32 nTextWidth
;
1817 pVDev
->SetMapMode( MapMode( MapUnit::Map100thMM
) );
1818 pVDev
->SetFont( maFont
);
1819 const sal_uInt32 nLen
= pDXArry
? rText
.getLength() : 0;
1822 nTextWidth
= pVDev
->GetTextWidth( OUString(rText
[ 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 ] );
1829 aActPosDelta
.setY( pDYArry
[ nLen
- 1 ] );
1834 nTextWidth
= pVDev
->GetTextWidth( rText
);
1835 aActPosDelta
.setX( nTextWidth
);
1838 if( mnTextAlign
& TA_UPDATECP
)
1839 rPosition
= maActPos
;
1841 if ( mnTextAlign
& TA_RIGHT_CENTER
)
1843 Point
aDisplacement( ( ( mnTextAlign
& TA_RIGHT_CENTER
) == TA_RIGHT
) ? nTextWidth
: nTextWidth
>> 1, 0 );
1844 Point().RotateAround(aDisplacement
, maFont
.GetOrientation());
1845 rPosition
-= aDisplacement
;
1848 if( mnTextAlign
& TA_UPDATECP
)
1850 Point().RotateAround(aActPosDelta
, maFont
.GetOrientation());
1851 maActPos
= rPosition
+ aActPosDelta
;
1855 if(bChangeFont
|| (maLatestFont
!= aTmp
))
1857 maLatestFont
= aTmp
;
1858 rtl::Reference
<MetaFontAction
> aNewMetaFontAction(new MetaFontAction(aTmp
));
1860 // tdf#127471 end evtl active MetaFontAction scale corrector detector/collector
1861 maScaledFontHelper
.endCurrentMetaFontAction();
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())
1868 maScaledFontHelper
.newCurrentMetaFontAction(aNewMetaFontAction
);
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() ) );
1883 if ( pDXArry
&& pDYArry
)
1885 for (sal_Int32 i
= 0; i
< rText
.getLength(); ++i
)
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 ) );
1894 /* because text without dx array is badly scaled, we
1895 will create such an array if necessary */
1896 tools::Long
* pDX
= pDXArry
;
1899 // only useful when we have an imported DXArray
1900 if(!rText
.isEmpty())
1902 maScaledFontHelper
.evaluateAlternativeFontScale(
1904 pDXArry
[rText
.getLength() - 1] // extract imported TextLength
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());
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
1923 SetGfxMode( nOldGfxMode
);
1926 void MtfTools::ImplDrawBitmap( const Point
& rPos
, const Size
& rSize
, const BitmapEx
& rBitmap
)
1928 BitmapEx
aBmpEx( rBitmap
);
1929 if ( mbComplexClip
)
1931 vcl::bitmap::DrawAndClipBitmap(rPos
, rSize
, rBitmap
, aBmpEx
, maClipPath
.getClipPath());
1934 if ( aBmpEx
.IsAlpha() )
1935 mpGDIMetaFile
->AddAction( new MetaBmpExScaleAction( rPos
, rSize
, aBmpEx
) );
1937 mpGDIMetaFile
->AddAction( new MetaBmpScaleAction( rPos
, rSize
, aBmpEx
.GetBitmap() ) );
1940 void MtfTools::ResolveBitmapActions( std::vector
<std::unique_ptr
<BSaveStruct
>>& rSaveList
)
1944 size_t nObjects
= rSaveList
.size();
1945 size_t nObjectsLeft
= nObjects
;
1947 while ( nObjectsLeft
)
1950 size_t nObjectsOfSameSize
= 0;
1951 size_t nObjectStartIndex
= nObjects
- nObjectsLeft
;
1953 BSaveStruct
* pSave
= rSaveList
[nObjectStartIndex
].get();
1954 tools::Rectangle
aRect( pSave
->aOutRect
);
1956 for ( i
= nObjectStartIndex
; i
< nObjects
; )
1958 nObjectsOfSameSize
++;
1959 if ( ++i
< nObjects
)
1961 pSave
= rSaveList
[i
].get();
1962 if ( pSave
->aOutRect
!= aRect
)
1966 Point
aPos( ImplMap( aRect
.TopLeft() ) );
1967 Size
aSize( ImplMap( aRect
.GetSize() ) );
1969 for ( i
= nObjectStartIndex
; i
< ( nObjectStartIndex
+ nObjectsOfSameSize
); i
++ )
1971 pSave
= rSaveList
[i
].get();
1973 sal_uInt32 nWinRop
= pSave
->nWinRop
;
1974 sal_uInt8 nRasterOperation
= static_cast<sal_uInt8
>( nWinRop
>> 16 );
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
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
1988 DrawRect( aRect
, false );
1989 SetRasterOp( nOldRop
);
1993 bool bDrawn
= false;
1995 if ( i
== nObjectStartIndex
) // optimizing, sometimes it is possible to create just one transparent bitmap
1997 if ( nObjectsOfSameSize
== 2 )
1999 BSaveStruct
* pSave2
= rSaveList
[i
+ 1].get();
2000 if ( ( pSave
->aBmpEx
.GetPrefSize() == pSave2
->aBmpEx
.GetPrefSize() ) &&
2001 ( pSave
->aBmpEx
.GetPrefMapMode() == pSave2
->aBmpEx
.GetPrefMapMode() ) )
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
2009 if ( ( nWinRop
== SRCPAINT
) && ( pSave2
->nWinRop
== SRCAND
) )
2011 Bitmap
aMask( pSave
->aBmpEx
.GetBitmap() ); aMask
.Invert();
2012 BitmapEx
aBmpEx( pSave2
->aBmpEx
.GetBitmap(), aMask
);
2013 ImplDrawBitmap( aPos
, aSize
, aBmpEx
);
2017 // #i20085# This is just the other way
2018 // around as above. Only difference: mask
2020 else if ( ( nWinRop
== SRCAND
) && ( pSave2
->nWinRop
== SRCPAINT
) )
2022 const Bitmap
& rMask( pSave
->aBmpEx
.GetBitmap() );
2023 BitmapEx
aBmpEx( pSave2
->aBmpEx
.GetBitmap(), rMask
);
2024 ImplDrawBitmap( aPos
, aSize
, aBmpEx
);
2029 else if ( ( nWinRop
== SRCAND
) && ( pSave2
->nWinRop
== SRCINVERT
) )
2031 const Bitmap
& rMask( pSave
->aBmpEx
.GetBitmap() );
2032 BitmapEx
aBmpEx( pSave2
->aBmpEx
.GetBitmap(), rMask
);
2033 ImplDrawBitmap( aPos
, aSize
, aBmpEx
);
2044 WMFRasterOp nOldRop
= SetRasterOp( WMFRasterOp::CopyPen
);
2045 Bitmap
aBitmap( pSave
->aBmpEx
.GetBitmap() );
2046 sal_uInt32 nOperation
= ( nRasterOperation
& 0xf );
2047 switch( nOperation
)
2052 if(pSave
->aBmpEx
.IsAlpha())
2054 ImplDrawBitmap( aPos
, aSize
, pSave
->aBmpEx
);
2058 SetRasterOp( WMFRasterOp::XorPen
);
2059 ImplDrawBitmap( aPos
, aSize
, BitmapEx(aBitmap
) );
2060 SetRasterOp( WMFRasterOp::CopyPen
);
2061 Bitmap
aMask( aBitmap
);
2063 BitmapEx
aBmpEx( aBitmap
, aMask
);
2064 ImplDrawBitmap( aPos
, aSize
, aBmpEx
);
2065 if ( nOperation
== 0x1 )
2067 SetRasterOp( WMFRasterOp::Not
);
2068 DrawRect( aRect
, false );
2076 Bitmap
aMask( aBitmap
);
2077 if ( ( nUsed
& 1 ) && ( nRasterOperation
& 0xb0 ) == 0xb0 ) // pattern used
2079 aBitmap
.Convert( BmpConversion::N24Bit
);
2080 aBitmap
.Erase( maFillStyle
.aFillColor
);
2082 BitmapEx
aBmpEx( aBitmap
, aMask
);
2083 ImplDrawBitmap( aPos
, aSize
, aBmpEx
);
2084 if ( nOperation
== 0x7 )
2086 SetRasterOp( WMFRasterOp::Not
);
2087 DrawRect( aRect
, false );
2095 SetRasterOp( WMFRasterOp::Not
);
2096 DrawRect( aRect
, false );
2097 SetRasterOp( WMFRasterOp::CopyPen
);
2098 Bitmap
aMask( aBitmap
);
2100 BitmapEx
aBmpEx( aBitmap
, aMask
);
2101 ImplDrawBitmap( aPos
, aSize
, aBmpEx
);
2102 SetRasterOp( WMFRasterOp::XorPen
);
2103 ImplDrawBitmap( aPos
, aSize
, BitmapEx(aBitmap
) );
2104 if ( nOperation
== 0xb )
2106 SetRasterOp( WMFRasterOp::Not
);
2107 DrawRect( aRect
, false );
2115 Bitmap
aMask( aBitmap
);
2117 BitmapEx
aBmpEx( aBitmap
, aMask
);
2118 ImplDrawBitmap( aPos
, aSize
, aBmpEx
);
2119 SetRasterOp( WMFRasterOp::XorPen
);
2120 ImplDrawBitmap( aPos
, aSize
, BitmapEx(aBitmap
) );
2121 if ( nOperation
== 0xd )
2123 SetRasterOp( WMFRasterOp::Not
);
2124 DrawRect( aRect
, false );
2131 SetRasterOp( WMFRasterOp::XorPen
);
2132 ImplDrawBitmap( aPos
, aSize
, BitmapEx(aBitmap
) );
2133 if ( nOperation
== 0x9 )
2135 SetRasterOp( WMFRasterOp::Not
);
2136 DrawRect( aRect
, false );
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
) );
2146 DrawRect( aRect
, false );
2150 case 0x3 : // only source is used
2153 if ( nRasterOperation
== 0x33 )
2155 if (pSave
->m_bForceAlpha
)
2157 ImplDrawBitmap(aPos
, aSize
, pSave
->aBmpEx
);
2161 ImplDrawBitmap(aPos
, aSize
, BitmapEx(aBitmap
));
2166 case 0x5 : // only destination is used
2168 SetRasterOp( WMFRasterOp::Not
);
2169 DrawRect( aRect
, false );
2173 case 0xa : // no operation
2176 SetRasterOp( nOldRop
);
2181 nObjectsLeft
-= nObjectsOfSameSize
;
2187 void MtfTools::SetDevOrg( const Point
& rPoint
)
2189 mnDevOrgX
= rPoint
.X();
2190 mnDevOrgY
= rPoint
.Y();
2193 void MtfTools::SetDevOrgOffset( sal_Int32 nXAdd
, sal_Int32 nYAdd
)
2199 void MtfTools::SetDevExt( const Size
& rSize
,bool regular
)
2201 if ( !(rSize
.Width() && rSize
.Height()) )
2207 case MM_ANISOTROPIC
:
2209 mnDevWidth
= rSize
.Width();
2210 mnDevHeight
= rSize
.Height();
2219 void MtfTools::ScaleDevExt(double fX
, double fY
)
2221 mnDevWidth
= basegfx::fround(mnDevWidth
* fX
);
2222 mnDevHeight
= basegfx::fround(mnDevHeight
* fY
);
2225 void MtfTools::SetWinOrg( const Point
& rPoint
, bool bIsEMF
)
2227 mnWinOrgX
= rPoint
.X();
2228 mnWinOrgY
= rPoint
.Y();
2236 void MtfTools::SetWinOrgOffset( sal_Int32 nXAdd
, sal_Int32 nYAdd
)
2242 void MtfTools::SetDevByWin() //mnWinExt...-stuff has to be assigned before.
2246 if ( mnMapMode
== MM_ISOTROPIC
) //TODO: WHAT ABOUT ANISOTROPIC???
2249 if (o3tl::checked_add(mnWinExtX
, mnWinOrgX
, nX
) || o3tl::checked_sub(mnWinExtY
, mnWinOrgY
, nY
))
2251 Size
aSize(nX
>> MS_FIXPOINT_BITCOUNT_28_4
, -(nY
>> MS_FIXPOINT_BITCOUNT_28_4
));
2252 SetDevExt(aSize
, false);
2257 void MtfTools::SetWinExt(const Size
& rSize
, bool bIsEMF
)
2259 if (!(rSize
.Width() && rSize
.Height()))
2265 case MM_ANISOTROPIC
:
2267 mnWinExtX
= rSize
.Width();
2268 mnWinExtY
= rSize
.Height();
2273 mbIsMapWinSet
= true;
2278 void MtfTools::ScaleWinExt(double fX
, double fY
)
2280 mnWinExtX
= basegfx::fround(mnWinExtX
* fX
);
2281 mnWinExtY
= basegfx::fround(mnWinExtY
* fY
);
2284 void MtfTools::SetrclBounds( const tools::Rectangle
& rRect
)
2289 void MtfTools::SetrclFrame( const tools::Rectangle
& rRect
)
2294 void MtfTools::SetRefPix( const Size
& rSize
)
2296 mnPixX
= rSize
.Width();
2297 mnPixY
= rSize
.Height();
2300 void MtfTools::SetRefMill( const Size
& rSize
)
2302 mnMillX
= rSize
.Width();
2303 mnMillY
= rSize
.Height();
2306 void MtfTools::SetMapMode( sal_uInt32 nMapMode
)
2308 mnMapMode
= nMapMode
;
2309 if ( nMapMode
== MM_TEXT
&& !mbIsMapWinSet
)
2311 mnWinExtX
= mnDevWidth
;
2312 mnWinExtY
= mnDevHeight
;
2314 else if ( mnMapMode
== MM_HIMETRIC
)
2316 sal_Int32 nWinExtX
, nWinExtY
;
2317 if (o3tl::checked_multiply
<sal_Int32
>(mnMillX
, 100, nWinExtX
) ||
2318 o3tl::checked_multiply
<sal_Int32
>(mnMillY
, 100, nWinExtY
))
2322 mnWinExtX
= nWinExtX
;
2323 mnWinExtY
= nWinExtY
;
2327 void MtfTools::SetWorldTransform( const XForm
& rXForm
)
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
;
2337 void MtfTools::ModifyWorldTransform( const XForm
& rXForm
, sal_uInt32 nMode
)
2343 maXForm
.eM11
= maXForm
.eM22
= 1.0f
;
2344 maXForm
.eM12
= maXForm
.eM21
= maXForm
.eDx
= maXForm
.eDy
= 0.0f
;
2348 case MWT_RIGHTMULTIPLY
:
2349 case MWT_LEFTMULTIPLY
:
2352 const XForm
* pRight
;
2354 if ( nMode
== MWT_LEFTMULTIPLY
)
2369 aF
[0][0] = pLeft
->eM11
;
2370 aF
[0][1] = pLeft
->eM12
;
2372 aF
[1][0] = pLeft
->eM21
;
2373 aF
[1][1] = pLeft
->eM22
;
2375 aF
[2][0] = pLeft
->eDx
;
2376 aF
[2][1] = pLeft
->eDy
;
2379 bF
[0][0] = pRight
->eM11
;
2380 bF
[0][1] = pRight
->eM12
;
2382 bF
[1][0] = pRight
->eM21
;
2383 bF
[1][1] = pRight
->eM22
;
2385 bF
[2][0] = pRight
->eDx
;
2386 bF
[2][1] = pRight
->eDy
;
2390 for ( i
= 0; i
< 3; i
++ )
2392 for ( j
= 0; j
< 3; j
++ )
2395 for ( k
= 0; k
< 3; k
++ )
2396 cF
[i
][j
] += aF
[i
][k
] * bF
[k
][j
];
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];
2409 SetWorldTransform(rXForm
);
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
>();
2420 pSave
->aLineStyle
= maLineStyle
;
2421 pSave
->aFillStyle
= maFillStyle
;
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
;
2433 pSave
->aActPos
= maActPos
;
2434 pSave
->aXForm
= maXForm
;
2435 pSave
->eRasterOp
= meRasterOp
;
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
;
2446 pSave
->maPathObj
= maPathObj
;
2447 pSave
->maClipPath
= maClipPath
;
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
);
2459 void MtfTools::Pop( const sal_Int32 nSavedDC
)
2461 if ( nSavedDC
== 0 )
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
;
2468 aIndex
= nSavedDC
; // WMF, if positive, nSavedDC represents a specific instance of the state to be restored.
2471 mvSaveStack
.clear();
2474 if( mvSaveStack
.empty() || ( aIndex
>= static_cast< sal_Int32
>( mvSaveStack
.size() ) ) )
2477 mvSaveStack
.resize( aIndex
+ 1 );
2478 // Backup the current data on the stack
2479 std::shared_ptr
<SaveStruct
>& pSave( mvSaveStack
.back() );
2481 maLineStyle
= pSave
->aLineStyle
;
2482 maFillStyle
= pSave
->aFillStyle
;
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
;
2494 maActPos
= pSave
->aActPos
;
2495 maXForm
= pSave
->aXForm
;
2496 meRasterOp
= pSave
->eRasterOp
;
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
;
2507 maPathObj
= pSave
->maPathObj
;
2508 if ( ! ( maClipPath
== pSave
->maClipPath
) )
2510 maClipPath
= pSave
->maClipPath
;
2511 mbClipNeedsUpdate
= true;
2513 if ( meLatestRasterOp
!= meRasterOp
)
2515 mpGDIMetaFile
->AddAction( new MetaRasterOpAction( meRasterOp
) );
2516 meLatestRasterOp
= meRasterOp
;
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();
2529 void MtfTools::AddFromGDIMetaFile( GDIMetaFile
& rGDIMetaFile
)
2531 rGDIMetaFile
.Play( *mpGDIMetaFile
);
2534 void MtfTools::PassEMFPlusHeaderInfo()
2536 EMFP_DEBUG(printf ("\t\t\tadd EMF_PLUS header info\n"));
2539 sal_Int32 nLeft
, nRight
, nTop
, nBottom
;
2541 nLeft
= mrclFrame
.Left();
2542 nTop
= mrclFrame
.Top();
2543 nRight
= mrclFrame
.Right();
2544 nBottom
= mrclFrame
.Bottom();
2547 mem
.WriteInt32( nLeft
).WriteInt32( nTop
).WriteInt32( nRight
).WriteInt32( nBottom
);
2548 mem
.WriteInt32( mnPixX
).WriteInt32( mnPixY
).WriteInt32( mnMillX
).WriteInt32( mnMillY
);
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
);
2559 // need to flush the stream, otherwise GetEndOfData will return 0
2560 // on windows where the function parameters are probably resolved in reverse order
2563 mpGDIMetaFile
->AddAction( new MetaCommentAction( "EMF_PLUS_HEADER_INFO", 0, static_cast<const sal_uInt8
*>(mem
.GetData()), mem
.GetEndOfData() ) );
2564 mpGDIMetaFile
->UseCanvas( true );
2567 void MtfTools::PassEMFPlus( void const * pBuffer
, sal_uInt32 nLength
)
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
) );
2574 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */