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 <comphelper/diagnose_ex.hxx>
21 #include <tools/debug.hxx>
23 #include <vcl/svapp.hxx>
24 #include <comphelper/propertysequence.hxx>
25 #include <comphelper/propertyvalue.hxx>
26 #include <cppcanvas/canvas.hxx>
27 #include <com/sun/star/rendering/XGraphicDevice.hpp>
28 #include <com/sun/star/rendering/TexturingMode.hpp>
29 #include <com/sun/star/uno/Sequence.hxx>
30 #include <com/sun/star/rendering/PanoseProportion.hpp>
31 #include <com/sun/star/rendering/XCanvasFont.hpp>
32 #include <com/sun/star/rendering/XCanvas.hpp>
33 #include <com/sun/star/rendering/PathCapType.hpp>
34 #include <com/sun/star/rendering/PathJoinType.hpp>
35 #include <basegfx/utils/canvastools.hxx>
36 #include <basegfx/utils/gradienttools.hxx>
37 #include <basegfx/numeric/ftools.hxx>
38 #include <basegfx/polygon/b2dpolypolygontools.hxx>
39 #include <basegfx/polygon/b2dpolygontools.hxx>
40 #include <basegfx/polygon/b2dpolygon.hxx>
41 #include <basegfx/polygon/b2dpolypolygon.hxx>
42 #include <basegfx/matrix/b2dhommatrix.hxx>
43 #include <basegfx/vector/b2dsize.hxx>
44 #include <basegfx/range/b2drectangle.hxx>
45 #include <basegfx/point/b2dpoint.hxx>
46 #include <basegfx/tuple/b2dtuple.hxx>
47 #include <basegfx/polygon/b2dpolygonclipper.hxx>
48 #include <canvas/canvastools.hxx>
49 #include <rtl/ustrbuf.hxx>
50 #include <vcl/canvastools.hxx>
51 #include <vcl/gdimtf.hxx>
52 #include <vcl/metaact.hxx>
53 #include <vcl/virdev.hxx>
54 #include <vcl/metric.hxx>
55 #include <vcl/graphictools.hxx>
56 #include <vcl/BitmapPalette.hxx>
57 #include <tools/poly.hxx>
58 #include <i18nlangtag/languagetag.hxx>
59 #include <implrenderer.hxx>
61 #include <outdevstate.hxx>
63 #include <sal/log.hxx>
64 #include "bitmapaction.hxx"
65 #include "lineaction.hxx"
66 #include "pointaction.hxx"
67 #include "polypolyaction.hxx"
68 #include "textaction.hxx"
69 #include "transparencygroupaction.hxx"
73 #include <string_view>
74 #include "mtftools.hxx"
76 using namespace ::com::sun::star
;
79 // free support functions
80 // ======================
83 template < class MetaActionType
> void setStateColor( MetaActionType
* pAct
,
85 uno::Sequence
< double >& rColorSequence
,
86 const cppcanvas::CanvasSharedPtr
& rCanvas
)
88 rIsColorSet
= pAct
->IsSetting();
92 ::Color
aColor( pAct
->GetColor() );
94 // force alpha part of color to
95 // opaque. transparent painting is done
96 // explicitly via MetaActionType::Transparent
98 //aColor.SetTransparency(128);
100 rColorSequence
= vcl::unotools::colorToDoubleSequence(
102 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
105 void setupStrokeAttributes( rendering::StrokeAttributes
& o_rStrokeAttributes
,
106 const ::cppcanvas::internal::ActionFactoryParameters
& rParms
,
107 const LineInfo
& rLineInfo
)
109 const ::basegfx::B2DSize
aWidth( rLineInfo
.GetWidth(), 0 );
110 o_rStrokeAttributes
.StrokeWidth
=
111 (rParms
.mrStates
.getState().mapModeTransform
* aWidth
).getLength();
113 // setup reasonable defaults
114 o_rStrokeAttributes
.MiterLimit
= 15.0; // 1.0 was no good default; GDI+'s limit is 10.0, our's is 15.0
115 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::BUTT
;
116 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::BUTT
;
118 switch (rLineInfo
.GetLineJoin())
120 case basegfx::B2DLineJoin::NONE
:
121 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::NONE
;
123 case basegfx::B2DLineJoin::Bevel
:
124 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::BEVEL
;
126 case basegfx::B2DLineJoin::Miter
:
127 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::MITER
;
129 case basegfx::B2DLineJoin::Round
:
130 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::ROUND
;
134 switch(rLineInfo
.GetLineCap())
136 default: /* css::drawing::LineCap_BUTT */
138 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::BUTT
;
139 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::BUTT
;
142 case css::drawing::LineCap_ROUND
:
144 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::ROUND
;
145 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::ROUND
;
148 case css::drawing::LineCap_SQUARE
:
150 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::SQUARE
;
151 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::SQUARE
;
156 if( LineStyle::Dash
!= rLineInfo
.GetStyle() )
159 const ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
161 // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing.
163 // interpret dash info only if explicitly enabled as
165 const ::basegfx::B2DSize
aDistance( rLineInfo
.GetDistance(), 0 );
166 const double nDistance( (rState
.mapModeTransform
* aDistance
).getLength() );
168 const ::basegfx::B2DSize
aDashLen( rLineInfo
.GetDashLen(), 0 );
169 const double nDashLen( (rState
.mapModeTransform
* aDashLen
).getLength() );
171 const ::basegfx::B2DSize
aDotLen( rLineInfo
.GetDotLen(), 0 );
172 const double nDotLen( (rState
.mapModeTransform
* aDotLen
).getLength() );
174 const sal_Int32
nNumArryEntries( 2*rLineInfo
.GetDashCount() +
175 2*rLineInfo
.GetDotCount() );
177 o_rStrokeAttributes
.DashArray
.realloc( nNumArryEntries
);
178 double* pDashArray
= o_rStrokeAttributes
.DashArray
.getArray();
181 // iteratively fill dash array, first with dashes, then
185 sal_Int32 nCurrEntry
=0;
187 for( sal_Int32 i
=0; i
<rLineInfo
.GetDashCount(); ++i
)
189 pDashArray
[nCurrEntry
++] = nDashLen
;
190 pDashArray
[nCurrEntry
++] = nDistance
;
192 for( sal_Int32 i
=0; i
<rLineInfo
.GetDotCount(); ++i
)
194 pDashArray
[nCurrEntry
++] = nDotLen
;
195 pDashArray
[nCurrEntry
++] = nDistance
;
200 /** Create masked BitmapEx, where the white areas of rBitmap are
201 transparent, and the other appear in rMaskColor.
203 BitmapEx
createMaskBmpEx( const Bitmap
& rBitmap
,
204 const ::Color
& rMaskColor
)
206 const ::Color
aWhite( COL_WHITE
);
207 BitmapPalette aBiLevelPalette
{
211 AlphaMask
aMask( rBitmap
.CreateAlphaMask( aWhite
));
212 Bitmap
aSolid( rBitmap
.GetSizePixel(),
213 vcl::PixelFormat::N8_BPP
,
215 aSolid
.Erase( rMaskColor
);
217 return BitmapEx( aSolid
, aMask
);
220 OUString
convertToLocalizedNumerals(std::u16string_view rStr
,
221 LanguageType eTextLanguage
)
223 OUStringBuffer
aBuf(rStr
);
224 for (sal_Int32 i
= 0; i
< aBuf
.getLength(); ++i
)
226 sal_Unicode nChar
= aBuf
[i
];
227 if (nChar
>= '0' && nChar
<= '9')
228 aBuf
[i
] = GetLocalizedChar(nChar
, eTextLanguage
);
230 return aBuf
.makeStringAndClear();
234 namespace cppcanvas::internal
236 // state stack manipulators
238 void VectorOfOutDevStates::clearStateStack()
241 const OutDevState aDefaultState
;
242 m_aStates
.push_back(aDefaultState
);
245 OutDevState
& VectorOfOutDevStates::getState()
247 return m_aStates
.back();
250 const OutDevState
& VectorOfOutDevStates::getState() const
252 return m_aStates
.back();
255 void VectorOfOutDevStates::pushState(vcl::PushFlags nFlags
)
257 m_aStates
.push_back( getState() );
258 getState().pushFlags
= nFlags
;
261 void VectorOfOutDevStates::popState()
263 if( getState().pushFlags
!= vcl::PushFlags::ALL
)
265 // a state is pushed which is incomplete, i.e. does not
266 // restore everything to the previous stack level when
268 // That means, we take the old state, and restore every
269 // OutDevState member whose flag is set, from the new to the
270 // old state. Then the new state gets overwritten by the
273 // preset to-be-calculated new state with old state
274 OutDevState
aCalculatedNewState( getState() );
276 // selectively copy to-be-restored content over saved old
278 m_aStates
.pop_back();
280 const OutDevState
& rNewState( getState() );
282 if( aCalculatedNewState
.pushFlags
& vcl::PushFlags::LINECOLOR
)
284 aCalculatedNewState
.lineColor
= rNewState
.lineColor
;
285 aCalculatedNewState
.isLineColorSet
= rNewState
.isLineColorSet
;
288 if( aCalculatedNewState
.pushFlags
& vcl::PushFlags::FILLCOLOR
)
290 aCalculatedNewState
.fillColor
= rNewState
.fillColor
;
291 aCalculatedNewState
.isFillColorSet
= rNewState
.isFillColorSet
;
294 if( aCalculatedNewState
.pushFlags
& vcl::PushFlags::FONT
)
296 aCalculatedNewState
.xFont
= rNewState
.xFont
;
297 aCalculatedNewState
.fontRotation
= rNewState
.fontRotation
;
298 aCalculatedNewState
.textReliefStyle
= rNewState
.textReliefStyle
;
299 aCalculatedNewState
.textOverlineStyle
= rNewState
.textOverlineStyle
;
300 aCalculatedNewState
.textUnderlineStyle
= rNewState
.textUnderlineStyle
;
301 aCalculatedNewState
.textStrikeoutStyle
= rNewState
.textStrikeoutStyle
;
302 aCalculatedNewState
.textEmphasisMark
= rNewState
.textEmphasisMark
;
303 aCalculatedNewState
.isTextEffectShadowSet
= rNewState
.isTextEffectShadowSet
;
304 aCalculatedNewState
.isTextWordUnderlineSet
= rNewState
.isTextWordUnderlineSet
;
305 aCalculatedNewState
.isTextOutlineModeSet
= rNewState
.isTextOutlineModeSet
;
308 if( aCalculatedNewState
.pushFlags
& vcl::PushFlags::TEXTCOLOR
)
310 aCalculatedNewState
.textColor
= rNewState
.textColor
;
313 if( aCalculatedNewState
.pushFlags
& vcl::PushFlags::MAPMODE
)
315 aCalculatedNewState
.mapModeTransform
= rNewState
.mapModeTransform
;
318 if( aCalculatedNewState
.pushFlags
& vcl::PushFlags::CLIPREGION
)
320 aCalculatedNewState
.clip
= rNewState
.clip
;
321 aCalculatedNewState
.clipRect
= rNewState
.clipRect
;
322 aCalculatedNewState
.xClipPoly
= rNewState
.xClipPoly
;
325 // TODO(F2): Raster ops NYI
326 // if( (aCalculatedNewState.pushFlags & vcl::PushFlags::RASTEROP) )
330 if( aCalculatedNewState
.pushFlags
& vcl::PushFlags::TEXTFILLCOLOR
)
332 aCalculatedNewState
.textFillColor
= rNewState
.textFillColor
;
333 aCalculatedNewState
.isTextFillColorSet
= rNewState
.isTextFillColorSet
;
336 if( aCalculatedNewState
.pushFlags
& vcl::PushFlags::TEXTALIGN
)
338 aCalculatedNewState
.textReferencePoint
= rNewState
.textReferencePoint
;
341 // TODO(F1): Refpoint handling NYI
342 // if( (aCalculatedNewState.pushFlags & vcl::PushFlags::REFPOINT) )
346 if( aCalculatedNewState
.pushFlags
& vcl::PushFlags::TEXTLINECOLOR
)
348 aCalculatedNewState
.textLineColor
= rNewState
.textLineColor
;
349 aCalculatedNewState
.isTextLineColorSet
= rNewState
.isTextLineColorSet
;
352 if( aCalculatedNewState
.pushFlags
& vcl::PushFlags::OVERLINECOLOR
)
354 aCalculatedNewState
.textOverlineColor
= rNewState
.textOverlineColor
;
355 aCalculatedNewState
.isTextOverlineColorSet
= rNewState
.isTextOverlineColorSet
;
358 if( aCalculatedNewState
.pushFlags
& vcl::PushFlags::TEXTLAYOUTMODE
)
360 aCalculatedNewState
.textAlignment
= rNewState
.textAlignment
;
361 aCalculatedNewState
.textDirection
= rNewState
.textDirection
;
364 // TODO(F2): Text language handling NYI
365 // if( (aCalculatedNewState.pushFlags & vcl::PushFlags::TEXTLANGUAGE) )
369 // always copy push mode
370 aCalculatedNewState
.pushFlags
= rNewState
.pushFlags
;
373 getState() = std::move(aCalculatedNewState
);
377 m_aStates
.pop_back();
381 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon
& rPolyPoly
,
382 const ActionFactoryParameters
& rParms
)
384 const OutDevState
& rState( rParms
.mrStates
.getState() );
385 if( (!rState
.isLineColorSet
&&
386 !rState
.isFillColorSet
) ||
387 (!rState
.lineColor
.hasElements() &&
388 !rState
.fillColor
.hasElements()) )
393 std::shared_ptr
<Action
> pPolyAction(
394 internal::PolyPolyActionFactory::createPolyPolyAction(
395 rPolyPoly
, rParms
.mrCanvas
, rState
) );
399 maActions
.emplace_back(
401 rParms
.mrCurrActionIndex
);
403 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
409 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon
& rPoly
,
410 const ActionFactoryParameters
& rParms
)
412 return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly
),
416 void ImplRenderer::skipContent( GDIMetaFile
& rMtf
,
417 const char* pCommentString
,
418 sal_Int32
& io_rCurrActionIndex
)
420 ENSURE_OR_THROW( pCommentString
,
421 "ImplRenderer::skipContent(): NULL string given" );
423 MetaAction
* pCurrAct
;
424 while( (pCurrAct
=rMtf
.NextAction()) != nullptr )
426 // increment action index, we've skipped an action.
427 ++io_rCurrActionIndex
;
429 if( pCurrAct
->GetType() == MetaActionType::COMMENT
&&
430 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase(
433 // requested comment found, done
441 bool ImplRenderer::isActionContained( GDIMetaFile
& rMtf
,
442 const char* pCommentString
,
443 MetaActionType nType
)
445 ENSURE_OR_THROW( pCommentString
,
446 "ImplRenderer::isActionContained(): NULL string given" );
450 // at least _one_ call to GDIMetaFile::NextAction() is
454 MetaAction
* pCurrAct
;
455 while( (pCurrAct
=rMtf
.NextAction()) != nullptr )
457 if( pCurrAct
->GetType() == nType
)
459 bRet
= true; // action type found
463 if( pCurrAct
->GetType() == MetaActionType::COMMENT
&&
464 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase(
467 // delimiting end comment found, done
468 bRet
= false; // not yet found
475 // rewind metafile to previous position (this method must
476 // not change the current metaaction)
485 // EOF, and not yet found
492 void ImplRenderer::createGradientAction( const ::tools::PolyPolygon
& rPoly
,
493 const ::Gradient
& rGradient
,
494 const ActionFactoryParameters
& rParms
,
495 bool bIsPolygonRectangle
,
496 bool bSubsettableActions
)
498 DBG_TESTSOLARMUTEX();
500 ::basegfx::B2DPolyPolygon
aDevicePoly( rPoly
.getB2DPolyPolygon() );
501 aDevicePoly
.transform( rParms
.mrStates
.getState().mapModeTransform
);
503 // decide, whether this gradient can be rendered natively
504 // by the canvas, or must be emulated via VCL gradient
505 // action extraction.
506 const sal_uInt16
nSteps( rGradient
.GetSteps() );
508 if( // step count is infinite, can use native canvas
511 // step count is sufficiently high, such that no
512 // discernible difference should be visible.
515 uno::Reference
< lang::XMultiServiceFactory
> xFactory(
516 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
520 rendering::Texture aTexture
;
522 aTexture
.RepeatModeX
= rendering::TexturingMode::CLAMP
;
523 aTexture
.RepeatModeY
= rendering::TexturingMode::CLAMP
;
524 aTexture
.Alpha
= 1.0;
527 // setup start/end color values
530 // scale color coefficients with gradient intensities
531 const sal_uInt16
nStartIntensity( rGradient
.GetStartIntensity() );
532 ::Color
aVCLStartColor( rGradient
.GetStartColor() );
533 aVCLStartColor
.SetRed( static_cast<sal_uInt8
>(aVCLStartColor
.GetRed() * nStartIntensity
/ 100) );
534 aVCLStartColor
.SetGreen( static_cast<sal_uInt8
>(aVCLStartColor
.GetGreen() * nStartIntensity
/ 100) );
535 aVCLStartColor
.SetBlue( static_cast<sal_uInt8
>(aVCLStartColor
.GetBlue() * nStartIntensity
/ 100) );
537 const sal_uInt16
nEndIntensity( rGradient
.GetEndIntensity() );
538 ::Color
aVCLEndColor( rGradient
.GetEndColor() );
539 aVCLEndColor
.SetRed( static_cast<sal_uInt8
>(aVCLEndColor
.GetRed() * nEndIntensity
/ 100) );
540 aVCLEndColor
.SetGreen( static_cast<sal_uInt8
>(aVCLEndColor
.GetGreen() * nEndIntensity
/ 100) );
541 aVCLEndColor
.SetBlue( static_cast<sal_uInt8
>(aVCLEndColor
.GetBlue() * nEndIntensity
/ 100) );
543 uno::Reference
<rendering::XColorSpace
> xColorSpace(
544 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace());
545 const uno::Sequence
< double > aStartColor(
546 vcl::unotools::colorToDoubleSequence( aVCLStartColor
,
548 const uno::Sequence
< double > aEndColor(
549 vcl::unotools::colorToDoubleSequence( aVCLEndColor
,
552 uno::Sequence
< uno::Sequence
< double > > aColors
;
553 uno::Sequence
< double > aStops
;
555 if( rGradient
.GetStyle() == css::awt::GradientStyle_AXIAL
)
557 aStops
= { 0.0, 0.5, 1.0 };
558 aColors
= { aEndColor
, aStartColor
, aEndColor
};
562 aStops
= { 0.0, 1.0 };
563 aColors
= { aStartColor
, aEndColor
};
566 const ::basegfx::B2DRectangle
aBounds(
567 ::basegfx::utils::getRange(aDevicePoly
) );
568 const ::basegfx::B2DVector
aOffset(
569 rGradient
.GetOfsX() / 100.0,
570 rGradient
.GetOfsY() / 100.0);
571 double fRotation
= toRadians( rGradient
.GetAngle() );
572 const double fBorder( rGradient
.GetBorder() / 100.0 );
574 basegfx::B2DHomMatrix aRot90
;
575 aRot90
.rotate(M_PI_2
);
577 basegfx::ODFGradientInfo aGradInfo
;
578 OUString aGradientService
;
579 switch( rGradient
.GetStyle() )
581 case css::awt::GradientStyle_LINEAR
:
582 aGradInfo
= basegfx::utils::createLinearODFGradientInfo(
587 // map ODF to svg gradient orientation - x
588 // instead of y direction
589 aGradInfo
.setTextureTransform(aGradInfo
.getTextureTransform() * aRot90
);
590 aGradientService
= "LinearGradient";
593 case css::awt::GradientStyle_AXIAL
:
595 // Adapt the border so that it is suitable
596 // for the axial gradient. An axial
597 // gradient consists of two linear
598 // gradients. Each of those covers half
599 // of the total size. In order to
600 // compensate for the condensed display of
601 // the linear gradients, we have to
602 // enlarge the area taken up by the actual
603 // gradient (1-fBorder). After that we
604 // have to turn the result back into a
605 // border value, hence the second (left
607 const double fAxialBorder (1-2*(1-fBorder
));
608 aGradInfo
= basegfx::utils::createAxialODFGradientInfo(
613 // map ODF to svg gradient orientation - x
614 // instead of y direction
615 aGradInfo
.setTextureTransform(aGradInfo
.getTextureTransform() * aRot90
);
617 // map ODF axial gradient to 3-stop linear
618 // gradient - shift left by 0.5
619 basegfx::B2DHomMatrix aShift
;
621 aShift
.translate(-0.5,0);
622 aGradInfo
.setTextureTransform(aGradInfo
.getTextureTransform() * aShift
);
623 aGradientService
= "LinearGradient";
627 case css::awt::GradientStyle_RADIAL
:
628 aGradInfo
= basegfx::utils::createRadialODFGradientInfo(
633 aGradientService
= "EllipticalGradient";
636 case css::awt::GradientStyle_ELLIPTICAL
:
637 aGradInfo
= basegfx::utils::createEllipticalODFGradientInfo(
643 aGradientService
= "EllipticalGradient";
646 case css::awt::GradientStyle_SQUARE
:
647 aGradInfo
= basegfx::utils::createSquareODFGradientInfo(
653 aGradientService
= "RectangularGradient";
656 case css::awt::GradientStyle_RECT
:
657 aGradInfo
= basegfx::utils::createRectangularODFGradientInfo(
663 aGradientService
= "RectangularGradient";
667 ENSURE_OR_THROW( false,
668 "ImplRenderer::createGradientAction(): Unexpected gradient type" );
672 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture
.AffineTransform
,
673 aGradInfo
.getTextureTransform() );
675 uno::Sequence
<uno::Any
> args(comphelper::InitAnyPropertySequence(
677 {"Colors", uno::Any(aColors
)},
678 {"Stops", uno::Any(aStops
)},
679 {"AspectRatio", uno::Any(aGradInfo
.getAspectRatio())},
681 aTexture
.Gradient
.set(
682 xFactory
->createInstanceWithArguments(aGradientService
,
685 if( aTexture
.Gradient
.is() )
687 std::shared_ptr
<Action
> pPolyAction(
688 internal::PolyPolyActionFactory::createPolyPolyAction(
691 rParms
.mrStates
.getState(),
696 maActions
.emplace_back(
698 rParms
.mrCurrActionIndex
);
700 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
703 // done, using native gradients
709 // cannot currently use native canvas gradients, as a
710 // finite step size is given (this funny feature is not
711 // supported by the XCanvas API)
712 rParms
.mrStates
.pushState(vcl::PushFlags::ALL
);
714 if( !bIsPolygonRectangle
)
716 // only clip, if given polygon is not a rectangle in
717 // the first place (the gradient is always limited to
718 // the given bound rect)
726 Gradient
aGradient(rGradient
);
727 aGradient
.AddGradientActions( rPoly
.GetBoundRect(), aTmpMtf
);
729 createActions( aTmpMtf
, rParms
, bSubsettableActions
);
731 rParms
.mrStates
.popState();
734 uno::Reference
< rendering::XCanvasFont
> ImplRenderer::createFont( double& o_rFontRotation
,
735 const vcl::Font
& rFont
,
736 const ActionFactoryParameters
& rParms
)
738 rendering::FontRequest aFontRequest
;
740 if( rParms
.mrParms
.maFontName
)
741 aFontRequest
.FontDescription
.FamilyName
= *rParms
.mrParms
.maFontName
;
743 aFontRequest
.FontDescription
.FamilyName
= rFont
.GetFamilyName();
745 aFontRequest
.FontDescription
.StyleName
= rFont
.GetStyleName();
747 aFontRequest
.FontDescription
.IsSymbolFont
= (rFont
.GetCharSet() == RTL_TEXTENCODING_SYMBOL
) ? util::TriState_YES
: util::TriState_NO
;
748 aFontRequest
.FontDescription
.IsVertical
= rFont
.IsVertical() ? util::TriState_YES
: util::TriState_NO
;
750 // TODO(F2): improve vclenum->panose conversion
751 aFontRequest
.FontDescription
.FontDescription
.Weight
=
752 rParms
.mrParms
.maFontWeight
?
753 *rParms
.mrParms
.maFontWeight
:
754 ::canvas::tools::numeric_cast
<sal_Int8
>( ::basegfx::fround( rFont
.GetWeight() ) );
755 aFontRequest
.FontDescription
.FontDescription
.Letterform
=
756 rParms
.mrParms
.maFontLetterForm
?
757 *rParms
.mrParms
.maFontLetterForm
:
758 (rFont
.GetItalic() == ITALIC_NONE
) ? 0 : 9;
759 aFontRequest
.FontDescription
.FontDescription
.Proportion
=
760 (rFont
.GetPitch() == PITCH_FIXED
)
761 ? rendering::PanoseProportion::MONO_SPACED
762 : rendering::PanoseProportion::ANYTHING
;
764 LanguageType aLang
= rFont
.GetLanguage();
765 aFontRequest
.Locale
= LanguageTag::convertToLocale( aLang
, false);
767 // setup state-local text transformation,
768 // if the font be rotated
769 const auto nFontAngle( rFont
.GetOrientation() );
772 // set to unity transform rotated by font angle
773 const double nAngle( toRadians(nFontAngle
) );
774 o_rFontRotation
= -nAngle
;
778 o_rFontRotation
= 0.0;
781 geometry::Matrix2D aFontMatrix
;
782 ::canvas::tools::setIdentityMatrix2D( aFontMatrix
);
784 // TODO(F2): use correct scale direction, font
785 // height might be width or anything else
787 // TODO(Q3): This code smells of programming by
788 // coincidence (the next two if statements)
790 ::Size
rFontSizeLog( rFont
.GetFontSize() );
792 if (rFontSizeLog
.Height() == 0)
794 // guess 16 pixel (as in VCL)
795 rFontSizeLog
= ::Size(0, 16);
797 // convert to target MapUnit if not pixels
798 rFontSizeLog
= OutputDevice::LogicToLogic(rFontSizeLog
, MapMode(MapUnit::MapPixel
), rParms
.mrVDev
.GetMapMode());
801 const sal_Int32 nFontWidthLog
= rFontSizeLog
.Width();
802 if( nFontWidthLog
!= 0 )
804 vcl::Font aTestFont
= rFont
;
805 aTestFont
.SetAverageFontWidth( 0 );
806 sal_Int32 nNormalWidth
= rParms
.mrVDev
.GetFontMetric( aTestFont
).GetAverageFontWidth();
807 if( nNormalWidth
!= nFontWidthLog
)
809 aFontMatrix
.m00
= static_cast<double>(nFontWidthLog
) / nNormalWidth
;
812 // #i52608# apply map mode scale also to font matrix - an
813 // anisotrophic mapmode must be reflected in an
814 // anisotrophic font matrix scale.
815 const OutDevState
& rState( rParms
.mrStates
.getState() );
816 if( !::basegfx::fTools::equal(
817 rState
.mapModeTransform
.get(0,0),
818 rState
.mapModeTransform
.get(1,1)) )
820 const double nScaleX( rState
.mapModeTransform
.get(0,0) );
821 const double nScaleY( rState
.mapModeTransform
.get(1,1) );
823 // note: no reason to check for division by zero, we
824 // always have the value closer (or equal) to zero as
826 if( fabs(nScaleX
) < fabs(nScaleY
) )
827 aFontMatrix
.m00
*= nScaleX
/ nScaleY
;
829 aFontMatrix
.m11
*= nScaleY
/ nScaleX
;
831 aFontRequest
.CellSize
= (rState
.mapModeTransform
* vcl::unotools::b2DSizeFromSize(rFontSizeLog
)).getHeight();
833 if (rFont
.GetEmphasisMark() != FontEmphasisMark::NONE
)
835 uno::Sequence
< beans::PropertyValue
> aProperties
{ comphelper::makePropertyValue(
836 u
"EmphasisMark"_ustr
, sal_uInt32(rFont
.GetEmphasisMark())) };
837 return rParms
.mrCanvas
->getUNOCanvas()->createFont(aFontRequest
,
842 return rParms
.mrCanvas
->getUNOCanvas()->createFont( aFontRequest
,
843 uno::Sequence
< beans::PropertyValue
>(),
847 // create text effects such as shadow/relief/embossed
848 void ImplRenderer::createTextAction( const ::Point
& rStartPoint
,
849 const OUString
& rString
,
852 KernArraySpan pCharWidths
,
853 std::span
<const sal_Bool
> pKashidaArray
,
854 const ActionFactoryParameters
& rParms
,
855 bool bSubsettableActions
)
857 ENSURE_OR_THROW( nIndex
>= 0 && nLength
<= rString
.getLength() + nIndex
,
858 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
861 return; // zero-length text, no visible output
863 const OutDevState
& rState( rParms
.mrStates
.getState() );
865 // TODO(F2): implement all text effects
866 // if( rState.textAlignment ); // TODO(F2): NYI
868 ::Color
aTextFillColor( COL_AUTO
);
869 ::Color
aShadowColor( COL_AUTO
);
870 ::Color
aReliefColor( COL_AUTO
);
871 ::Size aShadowOffset
;
872 ::Size aReliefOffset
;
874 uno::Reference
<rendering::XColorSpace
> xColorSpace(
875 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
877 if( rState
.isTextEffectShadowSet
)
879 // calculate shadow offset (similar to outdev3.cxx)
880 // TODO(F3): better match with outdev3.cxx
881 sal_Int32 nShadowOffset
= static_cast<sal_Int32
>(1.5 + ((rParms
.mrVDev
.GetFont().GetFontHeight()-24.0)/24.0));
882 if( nShadowOffset
< 1 )
885 aShadowOffset
.setWidth( nShadowOffset
);
886 aShadowOffset
.setHeight( nShadowOffset
);
888 // determine shadow color (from outdev3.cxx)
889 ::Color aTextColor
= vcl::unotools::doubleSequenceToColor(
890 rState
.textColor
, xColorSpace
);
891 bool bIsDark
= (aTextColor
== COL_BLACK
)
892 || (aTextColor
.GetLuminance() < 8);
894 aShadowColor
= bIsDark
? COL_LIGHTGRAY
: COL_BLACK
;
895 aShadowColor
.SetAlpha( aTextColor
.GetAlpha() );
898 if( rState
.textReliefStyle
!= FontRelief::NONE
)
900 // calculate relief offset (similar to outdev3.cxx)
901 sal_Int32 nReliefOffset
= rParms
.mrVDev
.PixelToLogic( Size( 1, 1 ) ).Height();
902 nReliefOffset
+= nReliefOffset
/2;
903 if( nReliefOffset
< 1 )
906 if( rState
.textReliefStyle
== FontRelief::Engraved
)
907 nReliefOffset
= -nReliefOffset
;
909 aReliefOffset
.setWidth( nReliefOffset
);
910 aReliefOffset
.setHeight( nReliefOffset
);
912 // determine relief color (from outdev3.cxx)
913 ::Color aTextColor
= vcl::unotools::doubleSequenceToColor(
914 rState
.textColor
, xColorSpace
);
916 aReliefColor
= COL_LIGHTGRAY
;
918 // we don't have an automatic color, so black is always
919 // drawn on white (literally copied from
920 // vcl/source/gdi/outdev3.cxx)
921 if( aTextColor
== COL_BLACK
)
923 aTextColor
= COL_WHITE
;
924 rParms
.mrStates
.getState().textColor
=
925 vcl::unotools::colorToDoubleSequence(
926 aTextColor
, xColorSpace
);
929 if( aTextColor
== COL_WHITE
)
930 aReliefColor
= COL_BLACK
;
931 aReliefColor
.SetAlpha( aTextColor
.GetAlpha() );
934 if (rState
.isTextFillColorSet
)
935 aTextFillColor
= vcl::unotools::doubleSequenceToColor(rState
.textFillColor
, xColorSpace
);
937 // create the actual text action
938 std::shared_ptr
<Action
> pTextAction(
939 TextActionFactory::createTextAction(
955 bSubsettableActions
) );
957 std::shared_ptr
<Action
> pStrikeoutTextAction
;
959 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
|| rState
.textStrikeoutStyle
== STRIKEOUT_SLASH
)
961 ::tools::Long nWidth
= rParms
.mrVDev
.GetTextWidth( rString
,nIndex
,nLength
);
963 sal_Unicode pChars
[4];
964 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
)
968 pChars
[3]=pChars
[2]=pChars
[1]=pChars
[0];
970 ::tools::Long nStrikeoutWidth
= (rParms
.mrVDev
.GetTextWidth(
971 OUString(pChars
, std::size(pChars
))) + 2) / 4;
973 if( nStrikeoutWidth
<= 0 )
976 ::tools::Long nMaxWidth
= nStrikeoutWidth
/2;
979 nMaxWidth
+= nWidth
+ 1;
981 ::tools::Long nFullStrikeoutWidth
= 0;
982 OUStringBuffer aStrikeoutText
;
983 while( (nFullStrikeoutWidth
+=nStrikeoutWidth
) < nMaxWidth
+1 )
984 aStrikeoutText
.append(pChars
[0]);
986 sal_Int32 nLen
= aStrikeoutText
.getLength();
990 ::tools::Long nInterval
= ( nWidth
- nStrikeoutWidth
* nLen
) / nLen
;
991 nStrikeoutWidth
+= nInterval
;
992 KernArray aStrikeoutCharWidths
;
994 for ( int i
= 0;i
< nLen
; i
++ )
995 aStrikeoutCharWidths
.push_back(nStrikeoutWidth
* (i
+ 1));
997 pStrikeoutTextAction
=
998 TextActionFactory::createTextAction(
1005 aStrikeoutText
.makeStringAndClear(),
1008 aStrikeoutCharWidths
,
1014 bSubsettableActions
) ;
1021 maActions
.emplace_back(
1023 rParms
.mrCurrActionIndex
);
1025 if ( pStrikeoutTextAction
)
1027 maActions
.emplace_back(
1028 pStrikeoutTextAction
,
1029 rParms
.mrCurrActionIndex
);
1032 rParms
.mrCurrActionIndex
+= pTextAction
->getActionCount()-1;
1035 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon
& rClipPoly
,
1036 const ActionFactoryParameters
& rParms
,
1039 ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
1041 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1042 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1044 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1045 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1048 (bEmptyClipRect
&& bEmptyClipPoly
) )
1050 rState
.clip
= rClipPoly
;
1054 if( !bEmptyClipRect
)
1056 // TODO(P3): Use Liang-Barsky polygon clip here,
1057 // after all, one object is just a rectangle!
1059 // convert rect to polygon beforehand, must revert
1060 // to general polygon clipping here.
1061 ::tools::Rectangle aRect
= rState
.clipRect
;
1062 // #121100# VCL rectangular clips always
1063 // include one more pixel to the right
1065 aRect
.AdjustRight(1);
1066 aRect
.AdjustBottom(1);
1067 rState
.clip
= ::basegfx::B2DPolyPolygon(
1068 ::basegfx::utils::createPolygonFromRect(
1069 vcl::unotools::b2DRectangleFromRectangle(aRect
) ) );
1073 rState
.clip
= basegfx::utils::clipPolyPolygonOnPolyPolygon(
1074 rClipPoly
, rState
.clip
, true, false);
1077 // by now, our clip resides in the OutDevState::clip
1079 rState
.clipRect
.SetEmpty();
1081 if( rState
.clip
.count() == 0 )
1083 if( rState
.clipRect
.IsEmpty() )
1085 rState
.xClipPoly
.clear();
1089 ::tools::Rectangle aRect
= rState
.clipRect
;
1090 // #121100# VCL rectangular clips
1091 // always include one more pixel to
1092 // the right and the bottom
1093 aRect
.AdjustRight(1);
1094 aRect
.AdjustBottom(1);
1095 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1096 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1097 ::basegfx::B2DPolyPolygon(
1098 ::basegfx::utils::createPolygonFromRect(
1099 vcl::unotools::b2DRectangleFromRectangle(aRect
) ) ) );
1104 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1105 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1110 void ImplRenderer::updateClipping( const ::tools::Rectangle
& rClipRect
,
1111 const ActionFactoryParameters
& rParms
,
1114 ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
1116 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1117 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1119 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1120 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1123 (bEmptyClipRect
&& bEmptyClipPoly
) )
1125 rState
.clipRect
= rClipRect
;
1126 rState
.clip
.clear();
1128 else if( bEmptyClipPoly
)
1130 rState
.clipRect
.Intersection( rClipRect
);
1131 rState
.clip
.clear();
1135 // TODO(P3): Handle a fourth case here, when all clip
1136 // polygons are rectangular, once B2DMultiRange's
1137 // sweep line implementation is done.
1139 // general case: convert to polygon and clip
1142 // convert rect to polygon beforehand, must revert
1143 // to general polygon clipping here.
1144 ::basegfx::B2DPolyPolygon
aClipPoly(
1145 ::basegfx::utils::createPolygonFromRect(
1146 vcl::unotools::b2DRectangleFromRectangle(rClipRect
) ) );
1148 rState
.clipRect
.SetEmpty();
1151 rState
.clip
= basegfx::utils::clipPolyPolygonOnPolyPolygon(
1152 aClipPoly
, rState
.clip
, true, false);
1155 if( rState
.clip
.count() == 0 )
1157 if( rState
.clipRect
.IsEmpty() )
1159 rState
.xClipPoly
.clear();
1163 // #121100# VCL rectangular clips
1164 // always include one more pixel to
1165 // the right and the bottom
1166 ::tools::Rectangle aRect
= rState
.clipRect
;
1167 aRect
.AdjustRight(1);
1168 aRect
.AdjustBottom(1);
1169 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1170 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1171 ::basegfx::B2DPolyPolygon(
1172 ::basegfx::utils::createPolygonFromRect(
1173 vcl::unotools::b2DRectangleFromRectangle(aRect
) ) ) );
1178 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1179 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1184 void ImplRenderer::createActions( GDIMetaFile
& rMtf
,
1185 const ActionFactoryParameters
& rFactoryParms
,
1186 bool bSubsettableActions
)
1188 /* TODO(P2): interpret mtf-comments
1189 ================================
1191 - gradient fillings (do that via comments)
1193 - think about mapping. _If_ we do everything in logical
1194 coordinates (which would solve the probs for stroke
1195 widths and text offsets), then we would have to
1196 recalc scaling for every drawing operation. This is
1197 because the outdev map mode might change at any time.
1198 Also keep in mind, that, although we've double precision
1199 float arithmetic now, different offsets might still
1200 generate different roundings (aka
1201 'OutputDevice::SetPixelOffset())
1205 // alias common parameters
1206 VectorOfOutDevStates
& rStates(rFactoryParms
.mrStates
);
1207 const CanvasSharedPtr
& rCanvas(rFactoryParms
.mrCanvas
);
1208 ::VirtualDevice
& rVDev(rFactoryParms
.mrVDev
);
1209 const Parameters
& rParms(rFactoryParms
.mrParms
);
1210 sal_Int32
& io_rCurrActionIndex(rFactoryParms
.mrCurrActionIndex
);
1213 // Loop over every metaaction
1214 // ==========================
1215 MetaAction
* pCurrAct
;
1217 // TODO(P1): think about caching
1218 for( pCurrAct
=rMtf
.FirstAction();
1220 pCurrAct
= rMtf
.NextAction() )
1222 // execute every action, to keep VDev state up-to-date
1223 // currently used only for
1225 // - the line/fill color when processing a MetaActionType::Transparent
1226 // - SetFont to process font metric specific actions
1227 pCurrAct
->Execute( &rVDev
);
1229 SAL_INFO("cppcanvas.emf", "MTF\trecord type: 0x" << static_cast<sal_uInt16
>(pCurrAct
->GetType()) << " (" << static_cast<sal_uInt16
>(pCurrAct
->GetType()) << ")");
1231 switch( pCurrAct
->GetType() )
1235 // In the first part of this monster-switch, we
1236 // handle all state-changing meta actions. These
1237 // are all handled locally.
1240 case MetaActionType::PUSH
:
1242 MetaPushAction
* pPushAction
= static_cast<MetaPushAction
*>(pCurrAct
);
1243 rStates
.pushState(pPushAction
->GetFlags());
1247 case MetaActionType::POP
:
1251 case MetaActionType::TEXTLANGUAGE
:
1252 case MetaActionType::REFPOINT
:
1253 // handled via pCurrAct->Execute( &rVDev )
1256 case MetaActionType::MAPMODE
:
1257 // modify current mapModeTransformation
1258 // transformation, such that subsequent
1259 // coordinates map correctly
1260 tools::calcLogic2PixelAffineTransform( rStates
.getState().mapModeTransform
,
1264 // monitor clip regions, to assemble clip polygon on our own
1265 case MetaActionType::CLIPREGION
:
1267 MetaClipRegionAction
* pClipAction
= static_cast<MetaClipRegionAction
*>(pCurrAct
);
1269 if( !pClipAction
->IsClipping() )
1272 rStates
.getState().clip
.clear();
1276 if( !pClipAction
->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1278 SAL_INFO( "cppcanvas.emf", "ImplRenderer::createActions(): non-polygonal clip "
1279 "region encountered, falling back to bounding box!" );
1281 // #121806# explicitly kept integer
1282 ::tools::Rectangle
aClipRect(
1284 pClipAction
->GetRegion().GetBoundRect() ) );
1286 // intersect current clip with given rect
1294 // set new clip polygon (don't intersect
1295 // with old one, just set it)
1297 // #121806# explicitly kept integer
1298 basegfx::B2DPolyPolygon
aPolyPolygon(pClipAction
->GetRegion().GetAsB2DPolyPolygon());
1300 aPolyPolygon
.transform(rVDev
.GetViewTransformation());
1311 case MetaActionType::ISECTRECTCLIPREGION
:
1313 MetaISectRectClipRegionAction
* pClipAction
= static_cast<MetaISectRectClipRegionAction
*>(pCurrAct
);
1315 // #121806# explicitly kept integer
1316 ::tools::Rectangle
aClipRect(
1317 rVDev
.LogicToPixel( pClipAction
->GetRect() ) );
1319 // intersect current clip with given rect
1328 case MetaActionType::ISECTREGIONCLIPREGION
:
1330 MetaISectRegionClipRegionAction
* pClipAction
= static_cast<MetaISectRegionClipRegionAction
*>(pCurrAct
);
1332 if( !pClipAction
->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1334 SAL_INFO( "cppcanvas.emf", "ImplRenderer::createActions(): non-polygonal clip "
1335 "region encountered, falling back to bounding box!" );
1337 // #121806# explicitly kept integer
1338 ::tools::Rectangle
aClipRect(
1339 rVDev
.LogicToPixel( pClipAction
->GetRegion().GetBoundRect() ) );
1341 // intersect current clip with given rect
1349 // intersect current clip with given clip polygon
1351 // #121806# explicitly kept integer
1352 basegfx::B2DPolyPolygon
aPolyPolygon(pClipAction
->GetRegion().GetAsB2DPolyPolygon());
1354 aPolyPolygon
.transform(rVDev
.GetViewTransformation());
1364 case MetaActionType::MOVECLIPREGION
:
1368 case MetaActionType::LINECOLOR
:
1369 if( !rParms
.maLineColor
)
1371 setStateColor( static_cast<MetaLineColorAction
*>(pCurrAct
),
1372 rStates
.getState().isLineColorSet
,
1373 rStates
.getState().lineColor
,
1378 // #120994# Do switch on/off LineColor, even when an overriding one is set
1379 bool bSetting(static_cast<MetaLineColorAction
*>(pCurrAct
)->IsSetting());
1381 rStates
.getState().isLineColorSet
= bSetting
;
1385 case MetaActionType::FILLCOLOR
:
1386 if( !rParms
.maFillColor
)
1388 setStateColor( static_cast<MetaFillColorAction
*>(pCurrAct
),
1389 rStates
.getState().isFillColorSet
,
1390 rStates
.getState().fillColor
,
1395 // #120994# Do switch on/off FillColor, even when an overriding one is set
1396 bool bSetting(static_cast<MetaFillColorAction
*>(pCurrAct
)->IsSetting());
1398 rStates
.getState().isFillColorSet
= bSetting
;
1402 case MetaActionType::TEXTCOLOR
:
1404 if( !rParms
.maTextColor
)
1406 // Text color is set unconditionally, thus, no
1407 // use of setStateColor here
1408 ::Color
aColor( static_cast<MetaTextColorAction
*>(pCurrAct
)->GetColor() );
1410 // force alpha part of color to
1411 // opaque. transparent painting is done
1412 // explicitly via MetaActionType::Transparent
1413 aColor
.SetAlpha(255);
1415 rStates
.getState().textColor
=
1416 vcl::unotools::colorToDoubleSequence(
1418 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1423 case MetaActionType::TEXTFILLCOLOR
:
1424 if( !rParms
.maTextColor
)
1426 setStateColor( static_cast<MetaTextFillColorAction
*>(pCurrAct
),
1427 rStates
.getState().isTextFillColorSet
,
1428 rStates
.getState().textFillColor
,
1433 // #120994# Do switch on/off TextFillColor, even when an overriding one is set
1434 bool bSetting(static_cast<MetaTextFillColorAction
*>(pCurrAct
)->IsSetting());
1436 rStates
.getState().isTextFillColorSet
= bSetting
;
1440 case MetaActionType::TEXTLINECOLOR
:
1441 if( !rParms
.maTextColor
)
1443 setStateColor( static_cast<MetaTextLineColorAction
*>(pCurrAct
),
1444 rStates
.getState().isTextLineColorSet
,
1445 rStates
.getState().textLineColor
,
1450 // #120994# Do switch on/off TextLineColor, even when an overriding one is set
1451 bool bSetting(static_cast<MetaTextLineColorAction
*>(pCurrAct
)->IsSetting());
1453 rStates
.getState().isTextLineColorSet
= bSetting
;
1457 case MetaActionType::OVERLINECOLOR
:
1458 if( !rParms
.maTextColor
)
1460 setStateColor( static_cast<MetaOverlineColorAction
*>(pCurrAct
),
1461 rStates
.getState().isTextOverlineColorSet
,
1462 rStates
.getState().textOverlineColor
,
1467 bool bSetting(static_cast<MetaOverlineColorAction
*>(pCurrAct
)->IsSetting());
1469 rStates
.getState().isTextOverlineColorSet
= bSetting
;
1473 case MetaActionType::TEXTALIGN
:
1475 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1476 const TextAlign
eTextAlign( static_cast<MetaTextAlignAction
*>(pCurrAct
)->GetTextAlign() );
1478 rState
.textReferencePoint
= eTextAlign
;
1482 case MetaActionType::FONT
:
1484 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1485 const vcl::Font
& rFont( static_cast<MetaFontAction
*>(pCurrAct
)->GetFont() );
1487 rState
.xFont
= createFont( rState
.fontRotation
,
1491 // TODO(Q2): define and use appropriate enumeration types
1492 rState
.textReliefStyle
= rFont
.GetRelief();
1493 rState
.textOverlineStyle
= static_cast<sal_Int8
>(rFont
.GetOverline());
1494 rState
.textUnderlineStyle
= rParms
.maFontUnderline
.has_value() ?
1495 (*rParms
.maFontUnderline
? sal_Int8(LINESTYLE_SINGLE
) : sal_Int8(LINESTYLE_NONE
)) :
1496 static_cast<sal_Int8
>(rFont
.GetUnderline());
1497 rState
.textStrikeoutStyle
= static_cast<sal_Int8
>(rFont
.GetStrikeout());
1498 rState
.textEmphasisMark
= rFont
.GetEmphasisMark();
1499 rState
.isTextEffectShadowSet
= rFont
.IsShadow();
1500 rState
.isTextWordUnderlineSet
= rFont
.IsWordLineMode();
1501 rState
.isTextOutlineModeSet
= rFont
.IsOutline();
1505 case MetaActionType::RASTEROP
:
1509 case MetaActionType::LAYOUTMODE
:
1511 // TODO(F2): A lot is missing here
1512 vcl::text::ComplexTextLayoutFlags nLayoutMode
= static_cast<MetaLayoutModeAction
*>(pCurrAct
)->GetLayoutMode();
1513 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1515 vcl::text::ComplexTextLayoutFlags nBidiLayoutMode
= nLayoutMode
& (vcl::text::ComplexTextLayoutFlags::BiDiRtl
|vcl::text::ComplexTextLayoutFlags::BiDiStrong
);
1516 if( nBidiLayoutMode
== vcl::text::ComplexTextLayoutFlags::Default
)
1517 rState
.textDirection
= rendering::TextDirection::WEAK_LEFT_TO_RIGHT
;
1518 else if( nBidiLayoutMode
== vcl::text::ComplexTextLayoutFlags::BiDiStrong
)
1519 rState
.textDirection
= rendering::TextDirection::STRONG_LEFT_TO_RIGHT
;
1520 else if( nBidiLayoutMode
== vcl::text::ComplexTextLayoutFlags::BiDiRtl
)
1521 rState
.textDirection
= rendering::TextDirection::WEAK_RIGHT_TO_LEFT
;
1522 else if( nBidiLayoutMode
== (vcl::text::ComplexTextLayoutFlags::BiDiRtl
| vcl::text::ComplexTextLayoutFlags::BiDiStrong
))
1523 rState
.textDirection
= rendering::TextDirection::STRONG_RIGHT_TO_LEFT
;
1525 rState
.textAlignment
= 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1526 if( (nLayoutMode
& (vcl::text::ComplexTextLayoutFlags::BiDiRtl
| vcl::text::ComplexTextLayoutFlags::TextOriginRight
) )
1527 && !(nLayoutMode
& vcl::text::ComplexTextLayoutFlags::TextOriginLeft
) )
1529 rState
.textAlignment
= 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1535 // In the second part of this monster-switch, we
1536 // handle all recursing meta actions. These are the
1537 // ones generating a metafile by themselves, which is
1538 // then processed by recursively calling this method.
1541 case MetaActionType::GRADIENT
:
1543 MetaGradientAction
* pGradAct
= static_cast<MetaGradientAction
*>(pCurrAct
);
1544 createGradientAction( ::tools::PolyPolygon( pGradAct
->GetRect() ),
1545 pGradAct
->GetGradient(),
1548 bSubsettableActions
);
1552 case MetaActionType::HATCH
:
1554 // TODO(F2): use native Canvas hatches here
1555 GDIMetaFile aTmpMtf
;
1557 rVDev
.AddHatchActions( static_cast<MetaHatchAction
*>(pCurrAct
)->GetPolyPolygon(),
1558 static_cast<MetaHatchAction
*>(pCurrAct
)->GetHatch(),
1560 createActions( aTmpMtf
, rFactoryParms
,
1561 bSubsettableActions
);
1565 case MetaActionType::EPS
:
1567 MetaEPSAction
* pAct
= static_cast<MetaEPSAction
*>(pCurrAct
);
1568 const GDIMetaFile
& rSubstitute
= pAct
->GetSubstitute();
1570 // #121806# explicitly kept integer
1571 const Size
aMtfSize( rSubstitute
.GetPrefSize() );
1572 const Size
aMtfSizePixPre( rVDev
.LogicToPixel( aMtfSize
,
1573 rSubstitute
.GetPrefMapMode() ) );
1575 // #i44110# correct null-sized output - there
1576 // are metafiles which have zero size in at
1577 // least one dimension
1579 // Remark the 1L cannot be replaced, that would cause max to compare long/int
1580 const Size
aMtfSizePix( std::max( aMtfSizePixPre
.Width(), ::tools::Long(1) ),
1581 std::max( aMtfSizePixPre
.Height(), ::tools::Long(1) ) );
1583 // Setup local transform, such that the
1584 // metafile renders itself into the given
1586 rStates
.pushState(vcl::PushFlags::ALL
);
1589 rVDev
.SetMapMode( rSubstitute
.GetPrefMapMode() );
1591 const ::Point
aPos( rVDev
.LogicToPixel( pAct
->GetPoint() ) );
1592 const ::Size
aSize( rVDev
.LogicToPixel( pAct
->GetSize() ) );
1594 rStates
.getState().transform
.translate( aPos
.X(),
1596 rStates
.getState().transform
.scale( static_cast<double>(aSize
.Width()) / aMtfSizePix
.Width(),
1597 static_cast<double>(aSize
.Height()) / aMtfSizePix
.Height() );
1599 createActions( const_cast<GDIMetaFile
&>(pAct
->GetSubstitute()),
1601 bSubsettableActions
);
1608 // handle metafile comments, to retrieve
1609 // meta-information for gradients, fills and
1610 // strokes. May skip actions, and may recurse.
1611 case MetaActionType::COMMENT
:
1613 MetaCommentAction
* pAct
= static_cast<MetaCommentAction
*>(pCurrAct
);
1616 if (pAct
->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
1618 MetaGradientExAction
* pGradAction
= nullptr;
1619 bool bDone( false );
1622 pCurrAct
=rMtf
.NextAction();
1625 switch( pCurrAct
->GetType() )
1627 // extract gradient info
1628 case MetaActionType::GRADIENTEX
:
1629 pGradAction
= static_cast<MetaGradientExAction
*>(pCurrAct
);
1632 // skip broken-down rendering, output gradient when sequence is ended
1633 case MetaActionType::COMMENT
:
1634 if( static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END") )
1640 createGradientAction( pGradAction
->GetPolyPolygon(),
1641 pGradAction
->GetGradient(),
1644 bSubsettableActions
);
1652 // TODO(P2): Handle drawing layer strokes, via
1653 // XPATHSTROKE_SEQ_BEGIN comment
1655 // Handle drawing layer fills
1656 else if( pAct
->GetComment() == "XPATHFILL_SEQ_BEGIN" )
1658 const sal_uInt8
* pData
= pAct
->GetData();
1661 SvMemoryStream
aMemStm( const_cast<sal_uInt8
*>(pData
), pAct
->GetDataSize(), StreamMode::READ
);
1663 SvtGraphicFill aFill
;
1664 ReadSvtGraphicFill( aMemStm
, aFill
);
1666 // TODO(P2): Also handle gradients and
1667 // hatches like this
1669 // only evaluate comment for pure
1670 // bitmap fills. If a transparency
1671 // gradient is involved (denoted by
1672 // the FloatTransparent action), take
1673 // the normal meta actions.
1674 if( aFill
.getFillType() == SvtGraphicFill::fillTexture
&&
1675 !isActionContained( rMtf
,
1676 "XPATHFILL_SEQ_END",
1677 MetaActionType::FLOATTRANSPARENT
) )
1679 rendering::Texture aTexture
;
1681 // TODO(F1): the SvtGraphicFill
1682 // can also transport metafiles
1683 // here, handle that case, too
1685 aFill
.getGraphic( aGraphic
);
1687 BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
1688 const ::Size
aBmpSize( aBmpEx
.GetSizePixel() );
1690 ::SvtGraphicFill::Transform aTransform
;
1691 aFill
.getTransform( aTransform
);
1693 ::basegfx::B2DHomMatrix aMatrix
;
1695 // convert to basegfx matrix
1696 aMatrix
.set(0,0, aTransform
.matrix
[ 0 ] );
1697 aMatrix
.set(0,1, aTransform
.matrix
[ 1 ] );
1698 aMatrix
.set(0,2, aTransform
.matrix
[ 2 ] );
1699 aMatrix
.set(1,0, aTransform
.matrix
[ 3 ] );
1700 aMatrix
.set(1,1, aTransform
.matrix
[ 4 ] );
1701 aMatrix
.set(1,2, aTransform
.matrix
[ 5 ] );
1703 ::basegfx::B2DHomMatrix aScale
;
1704 aScale
.scale( aBmpSize
.Width(),
1705 aBmpSize
.Height() );
1707 // post-multiply with the bitmap
1708 // size (XCanvas' texture assumes
1709 // the given bitmap to be
1710 // normalized to [0,1]x[0,1]
1712 aMatrix
= aMatrix
* aScale
;
1714 // pre-multiply with the
1715 // logic-to-pixel scale factor
1716 // (the metafile comment works in
1717 // logical coordinates).
1718 ::basegfx::B2DHomMatrix aLogic2PixelTransform
;
1719 aMatrix
*= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform
,
1722 ::basegfx::unotools::affineMatrixFromHomMatrix(
1723 aTexture
.AffineTransform
,
1726 aTexture
.Alpha
= 1.0 - aFill
.getTransparency();
1727 aTexture
.Bitmap
= vcl::unotools::xBitmapFromBitmapEx( aBmpEx
);
1728 if( aFill
.isTiling() )
1730 aTexture
.RepeatModeX
= rendering::TexturingMode::REPEAT
;
1731 aTexture
.RepeatModeY
= rendering::TexturingMode::REPEAT
;
1735 aTexture
.RepeatModeX
= rendering::TexturingMode::NONE
;
1736 aTexture
.RepeatModeY
= rendering::TexturingMode::NONE
;
1739 ::tools::PolyPolygon aPath
;
1740 aFill
.getPath( aPath
);
1742 ::basegfx::B2DPolyPolygon
aPoly( aPath
.getB2DPolyPolygon() );
1743 aPoly
.transform( rStates
.getState().mapModeTransform
);
1744 std::shared_ptr
<Action
> pPolyAction(
1745 internal::PolyPolyActionFactory::createPolyPolyAction(
1753 maActions
.emplace_back(
1755 io_rCurrActionIndex
);
1757 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
1760 // skip broken-down render output
1762 "XPATHFILL_SEQ_END",
1763 io_rCurrActionIndex
);
1767 // Handle drawing layer fills
1768 else if( pAct
->GetComment() == "EMF_PLUS" ) {
1769 SAL_WARN ("cppcanvas.emf", "EMF+ code was refactored and removed");
1770 } else if( pAct
->GetComment() == "EMF_PLUS_HEADER_INFO" ) {
1771 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pAct
->GetDataSize ());
1773 SvMemoryStream
rMF (const_cast<sal_uInt8
*>(pAct
->GetData ()), pAct
->GetDataSize (), StreamMode::READ
);
1775 rMF
.ReadInt32( nFrameLeft
).ReadInt32( nFrameTop
).ReadInt32( nFrameRight
).ReadInt32( nFrameBottom
);
1776 SAL_INFO ("cppcanvas.emf", "EMF+ picture frame: " << nFrameLeft
<< "," << nFrameTop
<< " - " << nFrameRight
<< "," << nFrameBottom
);
1777 rMF
.ReadInt32( nPixX
).ReadInt32( nPixY
).ReadInt32( nMmX
).ReadInt32( nMmY
);
1778 SAL_INFO ("cppcanvas.emf", "EMF+ ref device pixel size: " << nPixX
<< "x" << nPixY
<< " mm size: " << nMmX
<< "x" << nMmY
);
1780 ReadXForm( rMF
, aBaseTransform
);
1781 //aWorldTransform.Set (aBaseTransform);
1787 // In the third part of this monster-switch, we
1788 // handle all 'acting' meta actions. These are all
1789 // processed by constructing function objects for
1790 // them, which will later ease caching.
1793 case MetaActionType::POINT
:
1795 const OutDevState
& rState( rStates
.getState() );
1796 if( rState
.lineColor
.hasElements() )
1798 std::shared_ptr
<Action
> pPointAction(
1799 internal::PointActionFactory::createPointAction(
1800 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint(
1801 static_cast<MetaPointAction
*>(pCurrAct
)->GetPoint() ),
1807 maActions
.emplace_back(
1809 io_rCurrActionIndex
);
1811 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1817 case MetaActionType::PIXEL
:
1819 const OutDevState
& rState( rStates
.getState() );
1820 if( rState
.lineColor
.hasElements() )
1822 std::shared_ptr
<Action
> pPointAction(
1823 internal::PointActionFactory::createPointAction(
1824 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint(
1825 static_cast<MetaPixelAction
*>(pCurrAct
)->GetPoint() ),
1828 static_cast<MetaPixelAction
*>(pCurrAct
)->GetColor() ) );
1832 maActions
.emplace_back(
1834 io_rCurrActionIndex
);
1836 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1842 case MetaActionType::LINE
:
1844 const OutDevState
& rState( rStates
.getState() );
1845 if( rState
.lineColor
.hasElements() )
1847 MetaLineAction
* pLineAct
= static_cast<MetaLineAction
*>(pCurrAct
);
1849 const LineInfo
& rLineInfo( pLineAct
->GetLineInfo() );
1851 const ::basegfx::B2DPoint
aStartPoint(
1852 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( pLineAct
->GetStartPoint() ));
1853 const ::basegfx::B2DPoint
aEndPoint(
1854 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( pLineAct
->GetEndPoint() ));
1856 std::shared_ptr
<Action
> pLineAction
;
1858 if( rLineInfo
.IsDefault() )
1862 internal::LineActionFactory::createLineAction(
1870 maActions
.emplace_back(
1872 io_rCurrActionIndex
);
1874 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
1877 else if( LineStyle::NONE
!= rLineInfo
.GetStyle() )
1880 rendering::StrokeAttributes aStrokeAttributes
;
1882 setupStrokeAttributes( aStrokeAttributes
,
1886 // XCanvas can only stroke polygons,
1887 // not simple lines - thus, handle
1888 // this case via the polypolygon
1890 ::basegfx::B2DPolygon aPoly
;
1891 aPoly
.append( aStartPoint
);
1892 aPoly
.append( aEndPoint
);
1894 internal::PolyPolyActionFactory::createPolyPolyAction(
1895 ::basegfx::B2DPolyPolygon( aPoly
),
1896 rCanvas
, rState
, aStrokeAttributes
);
1900 maActions
.emplace_back(
1902 io_rCurrActionIndex
);
1904 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
1907 // else: line style is default
1908 // (i.e. invisible), don't generate action
1913 case MetaActionType::RECT
:
1915 const ::tools::Rectangle
& rRect(
1916 static_cast<MetaRectAction
*>(pCurrAct
)->GetRect() );
1918 if( rRect
.IsEmpty() )
1921 const OutDevState
& rState( rStates
.getState() );
1922 const ::basegfx::B2DPoint
aTopLeftPixel(
1923 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ) );
1924 const ::basegfx::B2DPoint
aBottomRightPixel(
1925 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1926 // #121100# OutputDevice::DrawRect() fills
1927 // rectangles Apple-like, i.e. with one
1928 // additional pixel to the right and bottom.
1929 ::basegfx::B2DPoint(1,1) );
1931 createFillAndStroke( ::basegfx::utils::createPolygonFromRect(
1932 ::basegfx::B2DRange( aTopLeftPixel
,
1933 aBottomRightPixel
)),
1938 case MetaActionType::ROUNDRECT
:
1940 const ::tools::Rectangle
& rRect(
1941 static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetRect());
1943 if( rRect
.IsEmpty() )
1946 ::basegfx::B2DPolygon
aPoly(
1947 ::basegfx::utils::createPolygonFromRect(
1948 ::basegfx::B2DRange(
1949 vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
1950 vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1951 ::basegfx::B2DPoint(1,1) ),
1952 static_cast<double>(static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetHorzRound()) / rRect
.GetWidth(),
1953 static_cast<double>(static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetVertRound()) / rRect
.GetHeight() ) );
1954 aPoly
.transform( rStates
.getState().mapModeTransform
);
1956 createFillAndStroke( aPoly
,
1961 case MetaActionType::ELLIPSE
:
1963 const ::tools::Rectangle
& rRect(
1964 static_cast<MetaEllipseAction
*>(pCurrAct
)->GetRect() );
1966 if( rRect
.IsEmpty() )
1969 const ::basegfx::B2DRange
aRange(
1970 vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
1971 vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1972 ::basegfx::B2DPoint(1,1) );
1974 ::basegfx::B2DPolygon
aPoly(
1975 ::basegfx::utils::createPolygonFromEllipse(
1977 aRange
.getWidth() / 2, // divide by 2 since createPolygonFromEllipse
1978 aRange
.getHeight() / 2 )); // expects the radius and NOT the diameter!
1979 aPoly
.transform( rStates
.getState().mapModeTransform
);
1981 createFillAndStroke( aPoly
,
1986 case MetaActionType::ARC
:
1988 // TODO(F1): Missing basegfx functionality. Mind empty rects!
1989 const ::tools::Polygon
aToolsPoly( static_cast<MetaArcAction
*>(pCurrAct
)->GetRect(),
1990 static_cast<MetaArcAction
*>(pCurrAct
)->GetStartPoint(),
1991 static_cast<MetaArcAction
*>(pCurrAct
)->GetEndPoint(), PolyStyle::Arc
);
1992 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
1993 aPoly
.transform( rStates
.getState().mapModeTransform
);
1995 createFillAndStroke( aPoly
,
2000 case MetaActionType::PIE
:
2002 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2003 const ::tools::Polygon
aToolsPoly( static_cast<MetaPieAction
*>(pCurrAct
)->GetRect(),
2004 static_cast<MetaPieAction
*>(pCurrAct
)->GetStartPoint(),
2005 static_cast<MetaPieAction
*>(pCurrAct
)->GetEndPoint(), PolyStyle::Pie
);
2006 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2007 aPoly
.transform( rStates
.getState().mapModeTransform
);
2009 createFillAndStroke( aPoly
,
2014 case MetaActionType::CHORD
:
2016 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2017 const ::tools::Polygon
aToolsPoly( static_cast<MetaChordAction
*>(pCurrAct
)->GetRect(),
2018 static_cast<MetaChordAction
*>(pCurrAct
)->GetStartPoint(),
2019 static_cast<MetaChordAction
*>(pCurrAct
)->GetEndPoint(), PolyStyle::Chord
);
2020 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2021 aPoly
.transform( rStates
.getState().mapModeTransform
);
2023 createFillAndStroke( aPoly
,
2028 case MetaActionType::POLYLINE
:
2030 const OutDevState
& rState( rStates
.getState() );
2031 if( rState
.lineColor
.hasElements() ||
2032 rState
.fillColor
.hasElements() )
2034 MetaPolyLineAction
* pPolyLineAct
= static_cast<MetaPolyLineAction
*>(pCurrAct
);
2036 const LineInfo
& rLineInfo( pPolyLineAct
->GetLineInfo() );
2037 ::basegfx::B2DPolygon
aPoly( pPolyLineAct
->GetPolygon().getB2DPolygon() );
2038 aPoly
.transform( rState
.mapModeTransform
);
2040 std::shared_ptr
<Action
> pLineAction
;
2042 if( rLineInfo
.IsDefault() )
2044 // plain hair line polygon
2046 internal::PolyPolyActionFactory::createLinePolyPolyAction(
2047 ::basegfx::B2DPolyPolygon(aPoly
),
2053 maActions
.emplace_back(
2055 io_rCurrActionIndex
);
2057 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2060 else if( LineStyle::NONE
!= rLineInfo
.GetStyle() )
2062 // 'thick' line polygon
2063 rendering::StrokeAttributes aStrokeAttributes
;
2065 setupStrokeAttributes( aStrokeAttributes
,
2070 internal::PolyPolyActionFactory::createPolyPolyAction(
2071 ::basegfx::B2DPolyPolygon(aPoly
),
2074 aStrokeAttributes
) ;
2078 maActions
.emplace_back(
2080 io_rCurrActionIndex
);
2082 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2085 // else: line style is default
2086 // (i.e. invisible), don't generate action
2091 case MetaActionType::POLYGON
:
2093 ::basegfx::B2DPolygon
aPoly( static_cast<MetaPolygonAction
*>(pCurrAct
)->GetPolygon().getB2DPolygon() );
2094 aPoly
.transform( rStates
.getState().mapModeTransform
);
2095 createFillAndStroke( aPoly
,
2100 case MetaActionType::POLYPOLYGON
:
2102 ::basegfx::B2DPolyPolygon
aPoly( static_cast<MetaPolyPolygonAction
*>(pCurrAct
)->GetPolyPolygon().getB2DPolyPolygon() );
2103 aPoly
.transform( rStates
.getState().mapModeTransform
);
2104 createFillAndStroke( aPoly
,
2109 case MetaActionType::BMP
:
2111 MetaBmpAction
* pAct
= static_cast<MetaBmpAction
*>(pCurrAct
);
2113 std::shared_ptr
<Action
> pBmpAction(
2114 internal::BitmapActionFactory::createBitmapAction(
2115 BitmapEx(pAct
->GetBitmap()),
2116 rStates
.getState().mapModeTransform
*
2117 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2119 rStates
.getState() ) );
2123 maActions
.emplace_back(
2125 io_rCurrActionIndex
);
2127 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2132 case MetaActionType::BMPSCALE
:
2134 MetaBmpScaleAction
* pAct
= static_cast<MetaBmpScaleAction
*>(pCurrAct
);
2136 std::shared_ptr
<Action
> pBmpAction(
2137 internal::BitmapActionFactory::createBitmapAction(
2138 BitmapEx(pAct
->GetBitmap()),
2139 rStates
.getState().mapModeTransform
* vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2140 rStates
.getState().mapModeTransform
* vcl::unotools::b2DVectorFromSize( pAct
->GetSize() ),
2142 rStates
.getState() ) );
2146 maActions
.emplace_back(
2148 io_rCurrActionIndex
);
2150 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2155 case MetaActionType::BMPSCALEPART
:
2157 MetaBmpScalePartAction
* pAct
= static_cast<MetaBmpScalePartAction
*>(pCurrAct
);
2159 // crop bitmap to given source rectangle (no
2160 // need to copy and convert the whole bitmap)
2161 ::Bitmap
aBmp( pAct
->GetBitmap() );
2162 const ::tools::Rectangle
aCropRect( pAct
->GetSrcPoint(),
2163 pAct
->GetSrcSize() );
2164 aBmp
.Crop( aCropRect
);
2166 std::shared_ptr
<Action
> pBmpAction(
2167 internal::BitmapActionFactory::createBitmapAction(
2169 rStates
.getState().mapModeTransform
*
2170 vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2171 rStates
.getState().mapModeTransform
*
2172 vcl::unotools::b2DVectorFromSize( pAct
->GetDestSize() ),
2174 rStates
.getState() ) );
2178 maActions
.emplace_back(
2180 io_rCurrActionIndex
);
2182 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2187 case MetaActionType::BMPEX
:
2189 MetaBmpExAction
* pAct
= static_cast<MetaBmpExAction
*>(pCurrAct
);
2191 std::shared_ptr
<Action
> pBmpAction(
2192 internal::BitmapActionFactory::createBitmapAction(
2193 pAct
->GetBitmapEx(),
2194 rStates
.getState().mapModeTransform
*
2195 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2197 rStates
.getState() ) );
2201 maActions
.emplace_back(
2203 io_rCurrActionIndex
);
2205 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2210 case MetaActionType::BMPEXSCALE
:
2212 MetaBmpExScaleAction
* pAct
= static_cast<MetaBmpExScaleAction
*>(pCurrAct
);
2214 std::shared_ptr
<Action
> pBmpAction(
2215 internal::BitmapActionFactory::createBitmapAction(
2216 pAct
->GetBitmapEx(),
2217 rStates
.getState().mapModeTransform
*
2218 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2219 rStates
.getState().mapModeTransform
*
2220 vcl::unotools::b2DVectorFromSize( pAct
->GetSize() ),
2222 rStates
.getState() ) );
2226 maActions
.emplace_back(
2228 io_rCurrActionIndex
);
2230 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2235 case MetaActionType::BMPEXSCALEPART
:
2237 MetaBmpExScalePartAction
* pAct
= static_cast<MetaBmpExScalePartAction
*>(pCurrAct
);
2239 // crop bitmap to given source rectangle (no
2240 // need to copy and convert the whole bitmap)
2241 BitmapEx
aBmp( pAct
->GetBitmapEx() );
2242 const ::tools::Rectangle
aCropRect( pAct
->GetSrcPoint(),
2243 pAct
->GetSrcSize() );
2244 aBmp
.Crop( aCropRect
);
2246 std::shared_ptr
<Action
> pBmpAction(
2247 internal::BitmapActionFactory::createBitmapAction(
2249 rStates
.getState().mapModeTransform
*
2250 vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2251 rStates
.getState().mapModeTransform
*
2252 vcl::unotools::b2DVectorFromSize( pAct
->GetDestSize() ),
2254 rStates
.getState() ) );
2258 maActions
.emplace_back(
2260 io_rCurrActionIndex
);
2262 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2267 case MetaActionType::MASK
:
2269 MetaMaskAction
* pAct
= static_cast<MetaMaskAction
*>(pCurrAct
);
2271 // create masked BitmapEx right here, as the
2272 // canvas does not provide equivalent
2274 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2275 pAct
->GetColor() ));
2277 std::shared_ptr
<Action
> pBmpAction(
2278 internal::BitmapActionFactory::createBitmapAction(
2280 rStates
.getState().mapModeTransform
*
2281 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2283 rStates
.getState() ) );
2287 maActions
.emplace_back(
2289 io_rCurrActionIndex
);
2291 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2296 case MetaActionType::MASKSCALE
:
2298 MetaMaskScaleAction
* pAct
= static_cast<MetaMaskScaleAction
*>(pCurrAct
);
2300 // create masked BitmapEx right here, as the
2301 // canvas does not provide equivalent
2303 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2304 pAct
->GetColor() ));
2306 std::shared_ptr
<Action
> pBmpAction(
2307 internal::BitmapActionFactory::createBitmapAction(
2309 rStates
.getState().mapModeTransform
*
2310 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2311 rStates
.getState().mapModeTransform
*
2312 vcl::unotools::b2DVectorFromSize( pAct
->GetSize() ),
2314 rStates
.getState() ) );
2318 maActions
.emplace_back(
2320 io_rCurrActionIndex
);
2322 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2327 case MetaActionType::MASKSCALEPART
:
2329 MetaMaskScalePartAction
* pAct
= static_cast<MetaMaskScalePartAction
*>(pCurrAct
);
2331 // create masked BitmapEx right here, as the
2332 // canvas does not provide equivalent
2334 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2335 pAct
->GetColor() ));
2337 // crop bitmap to given source rectangle (no
2338 // need to copy and convert the whole bitmap)
2339 const ::tools::Rectangle
aCropRect( pAct
->GetSrcPoint(),
2340 pAct
->GetSrcSize() );
2341 aBmp
.Crop( aCropRect
);
2343 std::shared_ptr
<Action
> pBmpAction(
2344 internal::BitmapActionFactory::createBitmapAction(
2346 rStates
.getState().mapModeTransform
*
2347 vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2348 rStates
.getState().mapModeTransform
*
2349 vcl::unotools::b2DVectorFromSize( pAct
->GetDestSize() ),
2351 rStates
.getState() ) );
2355 maActions
.emplace_back(
2357 io_rCurrActionIndex
);
2359 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2364 case MetaActionType::GRADIENTEX
:
2365 // TODO(F1): use native Canvas gradients here
2366 // action is ignored here, because redundant to MetaActionType::GRADIENT
2369 case MetaActionType::WALLPAPER
:
2373 case MetaActionType::Transparent
:
2375 const OutDevState
& rState( rStates
.getState() );
2376 if( rState
.lineColor
.hasElements() ||
2377 rState
.fillColor
.hasElements() )
2379 MetaTransparentAction
* pAct
= static_cast<MetaTransparentAction
*>(pCurrAct
);
2380 ::basegfx::B2DPolyPolygon
aPoly( pAct
->GetPolyPolygon().getB2DPolyPolygon() );
2381 aPoly
.transform( rState
.mapModeTransform
);
2383 std::shared_ptr
<Action
> pPolyAction(
2384 internal::PolyPolyActionFactory::createPolyPolyAction(
2388 pAct
->GetTransparence() ) );
2392 maActions
.emplace_back(
2394 io_rCurrActionIndex
);
2396 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2402 case MetaActionType::FLOATTRANSPARENT
:
2404 MetaFloatTransparentAction
* pAct
= static_cast<MetaFloatTransparentAction
*>(pCurrAct
);
2406 std::unique_ptr
< GDIMetaFile
> pMtf(
2407 new ::GDIMetaFile( pAct
->GetGDIMetaFile() ) );
2409 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2410 std::optional
< Gradient
> pGradient( pAct
->GetGradient() );
2412 DBG_TESTSOLARMUTEX();
2414 std::shared_ptr
<Action
> pFloatTransAction(
2415 internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2417 std::move(pGradient
),
2418 rStates
.getState().mapModeTransform
*
2419 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2420 rStates
.getState().mapModeTransform
*
2421 vcl::unotools::b2DVectorFromSize( pAct
->GetSize() ),
2423 rStates
.getState() ) );
2425 if( pFloatTransAction
)
2427 maActions
.emplace_back(
2429 io_rCurrActionIndex
);
2431 io_rCurrActionIndex
+= pFloatTransAction
->getActionCount()-1;
2436 case MetaActionType::TEXT
:
2438 MetaTextAction
* pAct
= static_cast<MetaTextAction
*>(pCurrAct
);
2439 OUString sText
= pAct
->GetText();
2441 if (rVDev
.GetDigitLanguage())
2442 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2444 const sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
2454 bSubsettableActions
);
2458 case MetaActionType::TEXTARRAY
:
2460 MetaTextArrayAction
* pAct
= static_cast<MetaTextArrayAction
*>(pCurrAct
);
2461 OUString sText
= pAct
->GetText();
2463 if (rVDev
.GetDigitLanguage())
2464 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2466 const sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
2474 pAct
->GetKashidaArray(),
2476 bSubsettableActions
);
2480 case MetaActionType::TEXTLINE
:
2482 MetaTextLineAction
* pAct
= static_cast<MetaTextLineAction
*>(pCurrAct
);
2484 const OutDevState
& rState( rStates
.getState() );
2485 const ::Size
aBaselineOffset( tools::getBaselineOffset( rState
,
2487 const ::basegfx::B2DSize
aSize( rState
.mapModeTransform
*
2488 ::basegfx::B2DSize(pAct
->GetWidth(),
2491 std::shared_ptr
<Action
> pPolyAction(
2492 PolyPolyActionFactory::createPolyPolyAction(
2493 tools::createTextLinesPolyPolygon(
2494 rState
.mapModeTransform
*
2495 ::basegfx::B2DPoint(
2496 vcl::unotools::b2DPointFromPoint(pAct
->GetStartPoint()) +
2497 vcl::unotools::b2DVectorFromSize(aBaselineOffset
)),
2499 tools::createTextLineInfo( rVDev
,
2506 maActions
.emplace_back(
2508 io_rCurrActionIndex
);
2510 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2515 case MetaActionType::TEXTRECT
:
2517 MetaTextRectAction
* pAct
= static_cast<MetaTextRectAction
*>(pCurrAct
);
2519 rStates
.pushState(vcl::PushFlags::ALL
);
2521 // use the VDev to break up the text rect
2522 // action into readily formatted lines
2523 GDIMetaFile aTmpMtf
;
2524 rVDev
.AddTextRectActions( pAct
->GetRect(),
2529 createActions( aTmpMtf
,
2531 bSubsettableActions
);
2538 case MetaActionType::STRETCHTEXT
:
2540 MetaStretchTextAction
* pAct
= static_cast<MetaStretchTextAction
*>(pCurrAct
);
2541 OUString sText
= pAct
->GetText();
2543 if (rVDev
.GetDigitLanguage())
2544 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2546 const sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
2548 // #i70897# Nothing to do, actually...
2552 // have to fit the text into the given
2553 // width. This is achieved by internally
2554 // generating a DX array, and uniformly
2555 // distributing the excess/insufficient width
2556 // to every logical character.
2559 rVDev
.GetTextArray( pAct
->GetText(), &aDXArray
,
2560 pAct
->GetIndex(), pAct
->GetLen() );
2562 const double nWidthDifferencePerDx
= ( pAct
->GetWidth() - aDXArray
[ nLen
-1 ] ) / nLen
;
2564 // Last entry of pDXArray contains total width of the text
2565 for (sal_Int32 i
= 1; i
<= nLen
; ++i
)
2567 // calc ratio for every array entry, to
2568 // distribute rounding errors 'evenly'
2569 // across the characters. Note that each
2570 // entry represents the 'end' position of
2571 // the corresponding character, thus, we
2572 // let i run from 1 to nLen.
2573 aDXArray
[i
- 1] += i
* nWidthDifferencePerDx
;
2584 bSubsettableActions
);
2589 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
2593 // increment action index (each mtf action counts _at
2594 // least_ one. Some count for more, therefore,
2595 // io_rCurrActionIndex is sometimes incremented by
2596 // pAct->getActionCount()-1 above, the -1 being the
2597 // correction for the unconditional increment here).
2598 ++io_rCurrActionIndex
;
2605 class ActionRenderer
2608 explicit ActionRenderer( ::basegfx::B2DHomMatrix aTransformation
) :
2609 maTransformation(std::move( aTransformation
)),
2619 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2621 // ANDing the result. We want to fail if at least
2622 // one action failed.
2623 mbRet
&= rAction
.mpAction
->render( maTransformation
);
2626 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2627 const Action::Subset
& rSubset
)
2629 // ANDing the result. We want to fail if at least
2630 // one action failed.
2631 mbRet
&= rAction
.mpAction
->renderSubset( maTransformation
,
2636 ::basegfx::B2DHomMatrix maTransformation
;
2643 explicit AreaQuery( ::basegfx::B2DHomMatrix aTransformation
) :
2644 maTransformation(std::move( aTransformation
))
2648 static bool result()
2650 return true; // nothing can fail here
2653 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2655 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
) );
2658 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2659 const Action::Subset
& rSubset
)
2661 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
,
2665 const ::basegfx::B2DRange
& getBounds() const
2671 ::basegfx::B2DHomMatrix maTransformation
;
2672 ::basegfx::B2DRange maBounds
;
2675 // Doing that via inline class. Compilers tend to not inline free
2677 struct UpperBoundActionIndexComparator
2679 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rLHS
,
2680 const ::cppcanvas::internal::ImplRenderer::MtfAction
& rRHS
)
2682 const sal_Int32
nLHSCount( rLHS
.mpAction
?
2683 rLHS
.mpAction
->getActionCount() : 0 );
2684 const sal_Int32
nRHSCount( rRHS
.mpAction
?
2685 rRHS
.mpAction
->getActionCount() : 0 );
2687 // compare end of action range, to have an action selected
2688 // by lower_bound even if the requested index points in
2689 // the middle of the action's range
2690 return rLHS
.mnOrigIndex
+ nLHSCount
< rRHS
.mnOrigIndex
+ nRHSCount
;
2694 /** Algorithm to apply given functor to a subset range
2698 Functor to call for each element of the subset
2699 range. Must provide the following method signatures:
2700 bool result() (returning false if operation failed)
2703 template< typename Functor
> bool
2704 forSubsetRange( Functor
& rFunctor
,
2705 ImplRenderer::ActionVector::const_iterator aRangeBegin
,
2706 const ImplRenderer::ActionVector::const_iterator
& aRangeEnd
,
2707 sal_Int32 nStartIndex
,
2708 sal_Int32 nEndIndex
,
2709 const ImplRenderer::ActionVector::const_iterator
& rEnd
)
2711 if( aRangeBegin
== aRangeEnd
)
2713 // only a single action. Setup subset, and call functor
2714 Action::Subset aSubset
;
2715 aSubset
.mnSubsetBegin
= std::max( sal_Int32( 0 ),
2716 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2717 aSubset
.mnSubsetEnd
= std::min( aRangeBegin
->mpAction
->getActionCount(),
2718 nEndIndex
- aRangeBegin
->mnOrigIndex
);
2720 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2721 "ImplRenderer::forSubsetRange(): Invalid indices" );
2723 rFunctor( *aRangeBegin
, aSubset
);
2727 // more than one action.
2729 // render partial first, full intermediate, and
2730 // partial last action
2731 Action::Subset aSubset
;
2732 aSubset
.mnSubsetBegin
= std::max( sal_Int32( 0 ),
2733 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2734 aSubset
.mnSubsetEnd
= aRangeBegin
->mpAction
->getActionCount();
2736 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2737 "ImplRenderer::forSubsetRange(): Invalid indices" );
2739 rFunctor( *aRangeBegin
, aSubset
);
2741 // first action rendered, skip to next
2744 // render full middle actions
2745 while( aRangeBegin
!= aRangeEnd
)
2746 rFunctor( *aRangeBegin
++ );
2748 if( aRangeEnd
== rEnd
||
2749 aRangeEnd
->mnOrigIndex
> nEndIndex
)
2751 // aRangeEnd denotes end of action vector,
2755 // nEndIndex references something _after_
2756 // aRangeBegin, but _before_ aRangeEnd
2758 // either way: no partial action left
2759 return rFunctor
.result();
2762 aSubset
.mnSubsetBegin
= 0;
2763 aSubset
.mnSubsetEnd
= nEndIndex
- aRangeEnd
->mnOrigIndex
;
2765 ENSURE_OR_RETURN_FALSE(aSubset
.mnSubsetEnd
>= 0,
2766 "ImplRenderer::forSubsetRange(): Invalid indices" );
2768 rFunctor( *aRangeEnd
, aSubset
);
2771 return rFunctor
.result();
2775 bool ImplRenderer::getSubsetIndices( sal_Int32
& io_rStartIndex
,
2776 sal_Int32
& io_rEndIndex
,
2777 ActionVector::const_iterator
& o_rRangeBegin
,
2778 ActionVector::const_iterator
& o_rRangeEnd
) const
2780 ENSURE_OR_RETURN_FALSE( io_rStartIndex
<=io_rEndIndex
,
2781 "ImplRenderer::getSubsetIndices(): invalid action range" );
2783 ENSURE_OR_RETURN_FALSE( !maActions
.empty(),
2784 "ImplRenderer::getSubsetIndices(): no actions to render" );
2786 const sal_Int32
nMinActionIndex( maActions
.front().mnOrigIndex
);
2787 const sal_Int32
nMaxActionIndex( maActions
.back().mnOrigIndex
+
2788 maActions
.back().mpAction
->getActionCount() );
2790 // clip given range to permissible values (there might be
2791 // ranges before and behind the valid indices)
2792 io_rStartIndex
= std::max( nMinActionIndex
,
2794 io_rEndIndex
= std::min( nMaxActionIndex
,
2797 if( io_rStartIndex
== io_rEndIndex
||
2798 io_rStartIndex
> io_rEndIndex
)
2800 // empty range, don't render anything. The second
2801 // condition e.g. happens if the requested range lies
2802 // fully before or behind the valid action indices.
2807 const ActionVector::const_iterator
aBegin( maActions
.begin() );
2808 const ActionVector::const_iterator
aEnd( maActions
.end() );
2811 // find start and end action
2812 // =========================
2813 o_rRangeBegin
= std::lower_bound( aBegin
, aEnd
,
2814 MtfAction( std::shared_ptr
<Action
>(), io_rStartIndex
),
2815 UpperBoundActionIndexComparator() );
2816 o_rRangeEnd
= std::lower_bound( aBegin
, aEnd
,
2817 MtfAction( std::shared_ptr
<Action
>(), io_rEndIndex
),
2818 UpperBoundActionIndexComparator() );
2826 ImplRenderer::ImplRenderer( const CanvasSharedPtr
& rCanvas
,
2827 const GDIMetaFile
& rMtf
,
2828 const Parameters
& rParams
)
2829 : CanvasGraphicHelper(rCanvas
)
2839 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2841 OSL_ENSURE( rCanvas
&& rCanvas
->getUNOCanvas().is(),
2842 "ImplRenderer::ImplRenderer(): Invalid canvas" );
2843 OSL_ENSURE( rCanvas
->getUNOCanvas()->getDevice().is(),
2844 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2846 // make sure canvas and graphic device are valid; action
2847 // creation don't check that every time
2849 !rCanvas
->getUNOCanvas().is() ||
2850 !rCanvas
->getUNOCanvas()->getDevice().is() )
2852 // leave actions empty
2856 VectorOfOutDevStates aStateStack
;
2858 ScopedVclPtrInstance
< VirtualDevice
> aVDev
;
2859 aVDev
->EnableOutput( false );
2861 // Setup VDev for state tracking and mapping
2862 // =========================================
2864 aVDev
->SetMapMode( rMtf
.GetPrefMapMode() );
2866 const Size
aMtfSize( rMtf
.GetPrefSize() );
2867 const Size
aMtfSizePixPre( aVDev
->LogicToPixel( aMtfSize
,
2868 rMtf
.GetPrefMapMode() ) );
2870 // #i44110# correct null-sized output - there are shapes
2871 // which have zero size in at least one dimension
2872 // Remark the 1L cannot be replaced, that would cause max to compare long/int
2873 const Size
aMtfSizePix( std::max( aMtfSizePixPre
.Width(), ::tools::Long(1) ),
2874 std::max( aMtfSizePixPre
.Height(), ::tools::Long(1) ) );
2876 sal_Int32
nCurrActions(0);
2877 ActionFactoryParameters
aParms(aStateStack
,
2884 aStateStack
.clearStateStack();
2886 // Setup local state, such that the metafile renders
2887 // itself into a one-by-one square at the origin for
2888 // identity view and render transformations
2889 aStateStack
.getState().transform
.scale( 1.0 / aMtfSizePix
.Width(),
2890 1.0 / aMtfSizePix
.Height() );
2892 tools::calcLogic2PixelAffineTransform( aStateStack
.getState().mapModeTransform
,
2896 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2897 // setup default text color to black
2899 rState
.textFillColor
=
2900 rState
.textOverlineColor
=
2901 rState
.textLineColor
= tools::intSRGBAToDoubleSequence( 0x000000FF );
2904 // apply overrides from the Parameters struct
2905 if( rParams
.maFillColor
)
2907 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2908 rState
.isFillColorSet
= true;
2909 rState
.fillColor
= tools::intSRGBAToDoubleSequence( *rParams
.maFillColor
);
2911 if( rParams
.maLineColor
)
2913 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2914 rState
.isLineColorSet
= true;
2915 rState
.lineColor
= tools::intSRGBAToDoubleSequence( *rParams
.maLineColor
);
2917 if( rParams
.maTextColor
)
2919 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2920 rState
.isTextFillColorSet
= true;
2921 rState
.isTextOverlineColorSet
= true;
2922 rState
.isTextLineColorSet
= true;
2924 rState
.textFillColor
=
2925 rState
.textOverlineColor
=
2926 rState
.textLineColor
= tools::intSRGBAToDoubleSequence( *rParams
.maTextColor
);
2928 if( rParams
.maFontName
||
2929 rParams
.maFontWeight
||
2930 rParams
.maFontLetterForm
||
2931 rParams
.maFontUnderline
.has_value() )
2933 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2935 rState
.xFont
= createFont( rState
.fontRotation
,
2936 vcl::Font(), // default font
2941 createActions( const_cast<GDIMetaFile
&>(rMtf
), // HACK(Q2):
2950 true // TODO(P1): make subsettability configurable
2954 ImplRenderer::~ImplRenderer()
2958 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex
,
2959 sal_Int32 nEndIndex
) const
2961 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::drawSubset()" );
2963 ActionVector::const_iterator aRangeBegin
;
2964 ActionVector::const_iterator aRangeEnd
;
2968 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
2969 aRangeBegin
, aRangeEnd
) )
2970 return true; // nothing to render (but _that_ was successful)
2972 // now, aRangeBegin references the action in which the
2973 // subset rendering must start, and aRangeEnd references
2974 // the action in which the subset rendering must end (it
2975 // might also end right at the start of the referenced
2976 // action, such that zero of that action needs to be
2980 // render subset of actions
2981 // ========================
2983 ::basegfx::B2DHomMatrix aMatrix
= ::canvas::tools::getRenderStateTransform( getRenderState() );
2985 ActionRenderer
aRenderer( aMatrix
);
2987 return forSubsetRange( aRenderer
,
2994 catch( uno::Exception
& )
2996 DBG_UNHANDLED_EXCEPTION("cppcanvas.emf");
2997 // convert error to return value
3002 ::basegfx::B2DRange
ImplRenderer::getSubsetArea( sal_Int32 nStartIndex
,
3003 sal_Int32 nEndIndex
) const
3005 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3007 ActionVector::const_iterator aRangeBegin
;
3008 ActionVector::const_iterator aRangeEnd
;
3010 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
3011 aRangeBegin
, aRangeEnd
) )
3012 return ::basegfx::B2DRange(); // nothing to render -> empty range
3014 // now, aRangeBegin references the action in which the
3015 // subset querying must start, and aRangeEnd references
3016 // the action in which the subset querying must end (it
3017 // might also end right at the start of the referenced
3018 // action, such that zero of that action needs to be
3022 // query bounds for subset of actions
3023 // ==================================
3025 ::basegfx::B2DHomMatrix aMatrix
= ::canvas::tools::getRenderStateTransform(
3028 AreaQuery
aQuery( aMatrix
);
3029 forSubsetRange( aQuery
,
3036 return aQuery
.getBounds();
3039 bool ImplRenderer::draw() const
3041 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::draw()" );
3043 ::basegfx::B2DHomMatrix aMatrix
= ::canvas::tools::getRenderStateTransform(
3048 return std::for_each( maActions
.begin(), maActions
.end(), ActionRenderer( aMatrix
) ).result();
3050 catch( uno::Exception
& )
3052 DBG_UNHANDLED_EXCEPTION( "cppcanvas.emf");
3058 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */