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 <tools/diagnose_ex.h>
21 #include <tools/debug.hxx>
22 #include <vcl/svapp.hxx>
23 #include <comphelper/propertysequence.hxx>
24 #include <cppcanvas/canvas.hxx>
25 #include <com/sun/star/rendering/XGraphicDevice.hpp>
26 #include <com/sun/star/rendering/TexturingMode.hpp>
27 #include <com/sun/star/uno/Sequence.hxx>
28 #include <com/sun/star/rendering/PanoseProportion.hpp>
29 #include <com/sun/star/rendering/XCanvasFont.hpp>
30 #include <com/sun/star/rendering/XCanvas.hpp>
31 #include <com/sun/star/rendering/PathCapType.hpp>
32 #include <com/sun/star/rendering/PathJoinType.hpp>
33 #include <basegfx/utils/canvastools.hxx>
34 #include <basegfx/utils/gradienttools.hxx>
35 #include <basegfx/numeric/ftools.hxx>
36 #include <basegfx/polygon/b2dpolypolygontools.hxx>
37 #include <basegfx/polygon/b2dpolygontools.hxx>
38 #include <basegfx/polygon/b2dpolygon.hxx>
39 #include <basegfx/polygon/b2dpolypolygon.hxx>
40 #include <basegfx/matrix/b2dhommatrix.hxx>
41 #include <basegfx/vector/b2dsize.hxx>
42 #include <basegfx/range/b2drectangle.hxx>
43 #include <basegfx/point/b2dpoint.hxx>
44 #include <basegfx/tuple/b2dtuple.hxx>
45 #include <basegfx/polygon/b2dpolygonclipper.hxx>
46 #include <canvas/canvastools.hxx>
47 #include <rtl/ustrbuf.hxx>
48 #include <vcl/canvastools.hxx>
49 #include <vcl/gdimtf.hxx>
50 #include <vcl/metaact.hxx>
51 #include <vcl/virdev.hxx>
52 #include <vcl/metric.hxx>
53 #include <vcl/graphictools.hxx>
54 #include <vcl/BitmapPalette.hxx>
55 #include <tools/poly.hxx>
56 #include <i18nlangtag/languagetag.hxx>
57 #include <implrenderer.hxx>
59 #include <outdevstate.hxx>
61 #include <sal/log.hxx>
62 #include "bitmapaction.hxx"
63 #include "lineaction.hxx"
64 #include "pointaction.hxx"
65 #include "polypolyaction.hxx"
66 #include "textaction.hxx"
67 #include "transparencygroupaction.hxx"
72 #include "mtftools.hxx"
74 using namespace ::com::sun::star
;
77 // free support functions
78 // ======================
81 template < class MetaActionType
> void setStateColor( MetaActionType
* pAct
,
83 uno::Sequence
< double >& rColorSequence
,
84 const cppcanvas::CanvasSharedPtr
& rCanvas
)
86 rIsColorSet
= pAct
->IsSetting();
90 ::Color
aColor( pAct
->GetColor() );
92 // force alpha part of color to
93 // opaque. transparent painting is done
94 // explicitly via MetaActionType::Transparent
95 aColor
.SetTransparency(0);
96 //aColor.SetTransparency(128);
98 rColorSequence
= vcl::unotools::colorToDoubleSequence(
100 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
103 void setupStrokeAttributes( rendering::StrokeAttributes
& o_rStrokeAttributes
,
104 const ::cppcanvas::internal::ActionFactoryParameters
& rParms
,
105 const LineInfo
& rLineInfo
)
107 const ::basegfx::B2DSize
aWidth( rLineInfo
.GetWidth(), 0 );
108 o_rStrokeAttributes
.StrokeWidth
=
109 (rParms
.mrStates
.getState().mapModeTransform
* aWidth
).getLength();
111 // setup reasonable defaults
112 o_rStrokeAttributes
.MiterLimit
= 15.0; // 1.0 was no good default; GDI+'s limit is 10.0, our's is 15.0
113 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::BUTT
;
114 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::BUTT
;
116 switch (rLineInfo
.GetLineJoin())
118 case basegfx::B2DLineJoin::NONE
:
119 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::NONE
;
121 case basegfx::B2DLineJoin::Bevel
:
122 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::BEVEL
;
124 case basegfx::B2DLineJoin::Miter
:
125 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::MITER
;
127 case basegfx::B2DLineJoin::Round
:
128 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::ROUND
;
132 switch(rLineInfo
.GetLineCap())
134 default: /* css::drawing::LineCap_BUTT */
136 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::BUTT
;
137 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::BUTT
;
140 case css::drawing::LineCap_ROUND
:
142 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::ROUND
;
143 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::ROUND
;
146 case css::drawing::LineCap_SQUARE
:
148 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::SQUARE
;
149 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::SQUARE
;
154 if( LineStyle::Dash
!= rLineInfo
.GetStyle() )
157 const ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
159 // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing.
161 // interpret dash info only if explicitly enabled as
163 const ::basegfx::B2DSize
aDistance( rLineInfo
.GetDistance(), 0 );
164 const double nDistance( (rState
.mapModeTransform
* aDistance
).getLength() );
166 const ::basegfx::B2DSize
aDashLen( rLineInfo
.GetDashLen(), 0 );
167 const double nDashLen( (rState
.mapModeTransform
* aDashLen
).getLength() );
169 const ::basegfx::B2DSize
aDotLen( rLineInfo
.GetDotLen(), 0 );
170 const double nDotLen( (rState
.mapModeTransform
* aDotLen
).getLength() );
172 const sal_Int32
nNumArryEntries( 2*rLineInfo
.GetDashCount() +
173 2*rLineInfo
.GetDotCount() );
175 o_rStrokeAttributes
.DashArray
.realloc( nNumArryEntries
);
176 double* pDashArray
= o_rStrokeAttributes
.DashArray
.getArray();
179 // iteratively fill dash array, first with dashes, then
183 sal_Int32 nCurrEntry
=0;
185 for( sal_Int32 i
=0; i
<rLineInfo
.GetDashCount(); ++i
)
187 pDashArray
[nCurrEntry
++] = nDashLen
;
188 pDashArray
[nCurrEntry
++] = nDistance
;
190 for( sal_Int32 i
=0; i
<rLineInfo
.GetDotCount(); ++i
)
192 pDashArray
[nCurrEntry
++] = nDotLen
;
193 pDashArray
[nCurrEntry
++] = nDistance
;
198 /** Create masked BitmapEx, where the white areas of rBitmap are
199 transparent, and the other appear in rMaskColor.
201 BitmapEx
createMaskBmpEx( const Bitmap
& rBitmap
,
202 const ::Color
& rMaskColor
)
204 const ::Color
aWhite( COL_WHITE
);
205 BitmapPalette
aBiLevelPalette(2);
206 aBiLevelPalette
[0] = aWhite
;
207 aBiLevelPalette
[1] = rMaskColor
;
209 Bitmap
aMask( rBitmap
.CreateMask( aWhite
));
210 Bitmap
aSolid( rBitmap
.GetSizePixel(),
213 aSolid
.Erase( rMaskColor
);
215 return BitmapEx( aSolid
, aMask
);
218 OUString
convertToLocalizedNumerals(const OUString
& rStr
,
219 LanguageType eTextLanguage
)
221 OUStringBuffer
aBuf(rStr
);
222 for (sal_Int32 i
= 0; i
< aBuf
.getLength(); ++i
)
224 sal_Unicode nChar
= aBuf
[i
];
225 if (nChar
>= '0' && nChar
<= '9')
226 aBuf
[i
] = GetLocalizedChar(nChar
, eTextLanguage
);
228 return aBuf
.makeStringAndClear();
232 namespace cppcanvas::internal
234 // state stack manipulators
236 void VectorOfOutDevStates::clearStateStack()
239 const OutDevState aDefaultState
;
240 m_aStates
.push_back(aDefaultState
);
243 OutDevState
& VectorOfOutDevStates::getState()
245 return m_aStates
.back();
248 const OutDevState
& VectorOfOutDevStates::getState() const
250 return m_aStates
.back();
253 void VectorOfOutDevStates::pushState(PushFlags nFlags
)
255 m_aStates
.push_back( getState() );
256 getState().pushFlags
= nFlags
;
259 void VectorOfOutDevStates::popState()
261 if( getState().pushFlags
!= PushFlags::ALL
)
263 // a state is pushed which is incomplete, i.e. does not
264 // restore everything to the previous stack level when
266 // That means, we take the old state, and restore every
267 // OutDevState member whose flag is set, from the new to the
268 // old state. Then the new state gets overwritten by the
271 // preset to-be-calculated new state with old state
272 OutDevState
aCalculatedNewState( getState() );
274 // selectively copy to-be-restored content over saved old
276 m_aStates
.pop_back();
278 const OutDevState
& rNewState( getState() );
280 if( aCalculatedNewState
.pushFlags
& PushFlags::LINECOLOR
)
282 aCalculatedNewState
.lineColor
= rNewState
.lineColor
;
283 aCalculatedNewState
.isLineColorSet
= rNewState
.isLineColorSet
;
286 if( aCalculatedNewState
.pushFlags
& PushFlags::FILLCOLOR
)
288 aCalculatedNewState
.fillColor
= rNewState
.fillColor
;
289 aCalculatedNewState
.isFillColorSet
= rNewState
.isFillColorSet
;
292 if( aCalculatedNewState
.pushFlags
& PushFlags::FONT
)
294 aCalculatedNewState
.xFont
= rNewState
.xFont
;
295 aCalculatedNewState
.fontRotation
= rNewState
.fontRotation
;
296 aCalculatedNewState
.textReliefStyle
= rNewState
.textReliefStyle
;
297 aCalculatedNewState
.textOverlineStyle
= rNewState
.textOverlineStyle
;
298 aCalculatedNewState
.textUnderlineStyle
= rNewState
.textUnderlineStyle
;
299 aCalculatedNewState
.textStrikeoutStyle
= rNewState
.textStrikeoutStyle
;
300 aCalculatedNewState
.textEmphasisMark
= rNewState
.textEmphasisMark
;
301 aCalculatedNewState
.isTextEffectShadowSet
= rNewState
.isTextEffectShadowSet
;
302 aCalculatedNewState
.isTextWordUnderlineSet
= rNewState
.isTextWordUnderlineSet
;
303 aCalculatedNewState
.isTextOutlineModeSet
= rNewState
.isTextOutlineModeSet
;
306 if( aCalculatedNewState
.pushFlags
& PushFlags::TEXTCOLOR
)
308 aCalculatedNewState
.textColor
= rNewState
.textColor
;
311 if( aCalculatedNewState
.pushFlags
& PushFlags::MAPMODE
)
313 aCalculatedNewState
.mapModeTransform
= rNewState
.mapModeTransform
;
316 if( aCalculatedNewState
.pushFlags
& PushFlags::CLIPREGION
)
318 aCalculatedNewState
.clip
= rNewState
.clip
;
319 aCalculatedNewState
.clipRect
= rNewState
.clipRect
;
320 aCalculatedNewState
.xClipPoly
= rNewState
.xClipPoly
;
323 // TODO(F2): Raster ops NYI
324 // if( (aCalculatedNewState.pushFlags & PushFlags::RASTEROP) )
328 if( aCalculatedNewState
.pushFlags
& PushFlags::TEXTFILLCOLOR
)
330 aCalculatedNewState
.textFillColor
= rNewState
.textFillColor
;
331 aCalculatedNewState
.isTextFillColorSet
= rNewState
.isTextFillColorSet
;
334 if( aCalculatedNewState
.pushFlags
& PushFlags::TEXTALIGN
)
336 aCalculatedNewState
.textReferencePoint
= rNewState
.textReferencePoint
;
339 // TODO(F1): Refpoint handling NYI
340 // if( (aCalculatedNewState.pushFlags & PushFlags::REFPOINT) )
344 if( aCalculatedNewState
.pushFlags
& PushFlags::TEXTLINECOLOR
)
346 aCalculatedNewState
.textLineColor
= rNewState
.textLineColor
;
347 aCalculatedNewState
.isTextLineColorSet
= rNewState
.isTextLineColorSet
;
350 if( aCalculatedNewState
.pushFlags
& PushFlags::OVERLINECOLOR
)
352 aCalculatedNewState
.textOverlineColor
= rNewState
.textOverlineColor
;
353 aCalculatedNewState
.isTextOverlineColorSet
= rNewState
.isTextOverlineColorSet
;
356 if( aCalculatedNewState
.pushFlags
& PushFlags::TEXTLAYOUTMODE
)
358 aCalculatedNewState
.textAlignment
= rNewState
.textAlignment
;
359 aCalculatedNewState
.textDirection
= rNewState
.textDirection
;
362 // TODO(F2): Text language handling NYI
363 // if( (aCalculatedNewState.pushFlags & PushFlags::TEXTLANGUAGE) )
367 // always copy push mode
368 aCalculatedNewState
.pushFlags
= rNewState
.pushFlags
;
371 getState() = aCalculatedNewState
;
375 m_aStates
.pop_back();
379 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon
& rPolyPoly
,
380 const ActionFactoryParameters
& rParms
)
382 const OutDevState
& rState( rParms
.mrStates
.getState() );
383 if( (!rState
.isLineColorSet
&&
384 !rState
.isFillColorSet
) ||
385 (!rState
.lineColor
.hasElements() &&
386 !rState
.fillColor
.hasElements()) )
391 std::shared_ptr
<Action
> pPolyAction(
392 internal::PolyPolyActionFactory::createPolyPolyAction(
393 rPolyPoly
, rParms
.mrCanvas
, rState
) );
397 maActions
.emplace_back(
399 rParms
.mrCurrActionIndex
);
401 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
407 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon
& rPoly
,
408 const ActionFactoryParameters
& rParms
)
410 return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly
),
414 void ImplRenderer::skipContent( GDIMetaFile
& rMtf
,
415 const char* pCommentString
,
416 sal_Int32
& io_rCurrActionIndex
)
418 ENSURE_OR_THROW( pCommentString
,
419 "ImplRenderer::skipContent(): NULL string given" );
421 MetaAction
* pCurrAct
;
422 while( (pCurrAct
=rMtf
.NextAction()) != nullptr )
424 // increment action index, we've skipped an action.
425 ++io_rCurrActionIndex
;
427 if( pCurrAct
->GetType() == MetaActionType::COMMENT
&&
428 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase(
431 // requested comment found, done
439 bool ImplRenderer::isActionContained( GDIMetaFile
& rMtf
,
440 const char* pCommentString
,
441 MetaActionType nType
)
443 ENSURE_OR_THROW( pCommentString
,
444 "ImplRenderer::isActionContained(): NULL string given" );
448 // at least _one_ call to GDIMetaFile::NextAction() is
452 MetaAction
* pCurrAct
;
453 while( (pCurrAct
=rMtf
.NextAction()) != nullptr )
455 if( pCurrAct
->GetType() == nType
)
457 bRet
= true; // action type found
461 if( pCurrAct
->GetType() == MetaActionType::COMMENT
&&
462 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase(
465 // delimiting end comment found, done
466 bRet
= false; // not yet found
473 // rewind metafile to previous position (this method must
474 // not change the current metaaction)
480 // EOF, and not yet found
487 void ImplRenderer::createGradientAction( const ::tools::PolyPolygon
& rPoly
,
488 const ::Gradient
& rGradient
,
489 const ActionFactoryParameters
& rParms
,
490 bool bIsPolygonRectangle
,
491 bool bSubsettableActions
)
493 DBG_TESTSOLARMUTEX();
495 ::basegfx::B2DPolyPolygon
aDevicePoly( rPoly
.getB2DPolyPolygon() );
496 aDevicePoly
.transform( rParms
.mrStates
.getState().mapModeTransform
);
498 // decide, whether this gradient can be rendered natively
499 // by the canvas, or must be emulated via VCL gradient
500 // action extraction.
501 const sal_uInt16
nSteps( rGradient
.GetSteps() );
503 if( // step count is infinite, can use native canvas
506 // step count is sufficiently high, such that no
507 // discernible difference should be visible.
510 uno::Reference
< lang::XMultiServiceFactory
> xFactory(
511 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
515 rendering::Texture aTexture
;
517 aTexture
.RepeatModeX
= rendering::TexturingMode::CLAMP
;
518 aTexture
.RepeatModeY
= rendering::TexturingMode::CLAMP
;
519 aTexture
.Alpha
= 1.0;
522 // setup start/end color values
525 // scale color coefficients with gradient intensities
526 const sal_uInt16
nStartIntensity( rGradient
.GetStartIntensity() );
527 ::Color
aVCLStartColor( rGradient
.GetStartColor() );
528 aVCLStartColor
.SetRed( static_cast<sal_uInt8
>(aVCLStartColor
.GetRed() * nStartIntensity
/ 100) );
529 aVCLStartColor
.SetGreen( static_cast<sal_uInt8
>(aVCLStartColor
.GetGreen() * nStartIntensity
/ 100) );
530 aVCLStartColor
.SetBlue( static_cast<sal_uInt8
>(aVCLStartColor
.GetBlue() * nStartIntensity
/ 100) );
532 const sal_uInt16
nEndIntensity( rGradient
.GetEndIntensity() );
533 ::Color
aVCLEndColor( rGradient
.GetEndColor() );
534 aVCLEndColor
.SetRed( static_cast<sal_uInt8
>(aVCLEndColor
.GetRed() * nEndIntensity
/ 100) );
535 aVCLEndColor
.SetGreen( static_cast<sal_uInt8
>(aVCLEndColor
.GetGreen() * nEndIntensity
/ 100) );
536 aVCLEndColor
.SetBlue( static_cast<sal_uInt8
>(aVCLEndColor
.GetBlue() * nEndIntensity
/ 100) );
538 uno::Reference
<rendering::XColorSpace
> xColorSpace(
539 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace());
540 const uno::Sequence
< double > aStartColor(
541 vcl::unotools::colorToDoubleSequence( aVCLStartColor
,
543 const uno::Sequence
< double > aEndColor(
544 vcl::unotools::colorToDoubleSequence( aVCLEndColor
,
547 uno::Sequence
< uno::Sequence
< double > > aColors(2);
548 uno::Sequence
< double > aStops(2);
550 if( rGradient
.GetStyle() == GradientStyle::Axial
)
559 aColors
[0] = aEndColor
;
560 aColors
[1] = aStartColor
;
561 aColors
[2] = aEndColor
;
568 aColors
[0] = aStartColor
;
569 aColors
[1] = aEndColor
;
572 const ::basegfx::B2DRectangle
aBounds(
573 ::basegfx::utils::getRange(aDevicePoly
) );
574 const ::basegfx::B2DVector
aOffset(
575 rGradient
.GetOfsX() / 100.0,
576 rGradient
.GetOfsY() / 100.0);
577 double fRotation( rGradient
.GetAngle().get() * M_PI
/ 1800.0 );
578 const double fBorder( rGradient
.GetBorder() / 100.0 );
580 basegfx::B2DHomMatrix aRot90
;
581 aRot90
.rotate(M_PI_2
);
583 basegfx::ODFGradientInfo aGradInfo
;
584 OUString aGradientService
;
585 switch( rGradient
.GetStyle() )
587 case GradientStyle::Linear
:
588 aGradInfo
= basegfx::utils::createLinearODFGradientInfo(
593 // map ODF to svg gradient orientation - x
594 // instead of y direction
595 aGradInfo
.setTextureTransform(aGradInfo
.getTextureTransform() * aRot90
);
596 aGradientService
= "LinearGradient";
599 case GradientStyle::Axial
:
601 // Adapt the border so that it is suitable
602 // for the axial gradient. An axial
603 // gradient consists of two linear
604 // gradients. Each of those covers half
605 // of the total size. In order to
606 // compensate for the condensed display of
607 // the linear gradients, we have to
608 // enlarge the area taken up by the actual
609 // gradient (1-fBorder). After that we
610 // have to turn the result back into a
611 // border value, hence the second (left
613 const double fAxialBorder (1-2*(1-fBorder
));
614 aGradInfo
= basegfx::utils::createAxialODFGradientInfo(
619 // map ODF to svg gradient orientation - x
620 // instead of y direction
621 aGradInfo
.setTextureTransform(aGradInfo
.getTextureTransform() * aRot90
);
623 // map ODF axial gradient to 3-stop linear
624 // gradient - shift left by 0.5
625 basegfx::B2DHomMatrix aShift
;
627 aShift
.translate(-0.5,0);
628 aGradInfo
.setTextureTransform(aGradInfo
.getTextureTransform() * aShift
);
629 aGradientService
= "LinearGradient";
633 case GradientStyle::Radial
:
634 aGradInfo
= basegfx::utils::createRadialODFGradientInfo(
639 aGradientService
= "EllipticalGradient";
642 case GradientStyle::Elliptical
:
643 aGradInfo
= basegfx::utils::createEllipticalODFGradientInfo(
649 aGradientService
= "EllipticalGradient";
652 case GradientStyle::Square
:
653 aGradInfo
= basegfx::utils::createSquareODFGradientInfo(
659 aGradientService
= "RectangularGradient";
662 case GradientStyle::Rect
:
663 aGradInfo
= basegfx::utils::createRectangularODFGradientInfo(
669 aGradientService
= "RectangularGradient";
673 ENSURE_OR_THROW( false,
674 "ImplRenderer::createGradientAction(): Unexpected gradient type" );
678 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture
.AffineTransform
,
679 aGradInfo
.getTextureTransform() );
681 uno::Sequence
<uno::Any
> args(comphelper::InitAnyPropertySequence(
683 {"Colors", uno::Any(aColors
)},
684 {"Stops", uno::Any(aStops
)},
685 {"AspectRatio", uno::Any(aGradInfo
.getAspectRatio())},
687 aTexture
.Gradient
.set(
688 xFactory
->createInstanceWithArguments(aGradientService
,
691 if( aTexture
.Gradient
.is() )
693 std::shared_ptr
<Action
> pPolyAction(
694 internal::PolyPolyActionFactory::createPolyPolyAction(
697 rParms
.mrStates
.getState(),
702 maActions
.emplace_back(
704 rParms
.mrCurrActionIndex
);
706 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
709 // done, using native gradients
715 // cannot currently use native canvas gradients, as a
716 // finite step size is given (this funny feature is not
717 // supported by the XCanvas API)
718 rParms
.mrStates
.pushState(PushFlags::ALL
);
720 if( !bIsPolygonRectangle
)
722 // only clip, if given polygon is not a rectangle in
723 // the first place (the gradient is always limited to
724 // the given bound rect)
732 rParms
.mrVDev
.AddGradientActions( rPoly
.GetBoundRect(),
736 createActions( aTmpMtf
, rParms
, bSubsettableActions
);
738 rParms
.mrStates
.popState();
741 uno::Reference
< rendering::XCanvasFont
> ImplRenderer::createFont( double& o_rFontRotation
,
742 const vcl::Font
& rFont
,
743 const ActionFactoryParameters
& rParms
)
745 rendering::FontRequest aFontRequest
;
747 if( rParms
.mrParms
.maFontName
)
748 aFontRequest
.FontDescription
.FamilyName
= *rParms
.mrParms
.maFontName
;
750 aFontRequest
.FontDescription
.FamilyName
= rFont
.GetFamilyName();
752 aFontRequest
.FontDescription
.StyleName
= rFont
.GetStyleName();
754 aFontRequest
.FontDescription
.IsSymbolFont
= (rFont
.GetCharSet() == RTL_TEXTENCODING_SYMBOL
) ? util::TriState_YES
: util::TriState_NO
;
755 aFontRequest
.FontDescription
.IsVertical
= rFont
.IsVertical() ? util::TriState_YES
: util::TriState_NO
;
757 // TODO(F2): improve vclenum->panose conversion
758 aFontRequest
.FontDescription
.FontDescription
.Weight
=
759 rParms
.mrParms
.maFontWeight
?
760 *rParms
.mrParms
.maFontWeight
:
761 ::canvas::tools::numeric_cast
<sal_Int8
>( ::basegfx::fround( rFont
.GetWeight() ) );
762 aFontRequest
.FontDescription
.FontDescription
.Letterform
=
763 rParms
.mrParms
.maFontLetterForm
?
764 *rParms
.mrParms
.maFontLetterForm
:
765 (rFont
.GetItalic() == ITALIC_NONE
) ? 0 : 9;
766 aFontRequest
.FontDescription
.FontDescription
.Proportion
=
767 (rFont
.GetPitch() == PITCH_FIXED
)
768 ? rendering::PanoseProportion::MONO_SPACED
769 : rendering::PanoseProportion::ANYTHING
;
771 LanguageType aLang
= rFont
.GetLanguage();
772 aFontRequest
.Locale
= LanguageTag::convertToLocale( aLang
, false);
774 // setup state-local text transformation,
775 // if the font be rotated
776 const short nFontAngle( rFont
.GetOrientation() );
777 if( nFontAngle
!= 0 )
779 // set to unity transform rotated by font angle
780 const double nAngle( nFontAngle
* (F_PI
/ 1800.0) );
781 o_rFontRotation
= -nAngle
;
785 o_rFontRotation
= 0.0;
788 geometry::Matrix2D aFontMatrix
;
789 ::canvas::tools::setIdentityMatrix2D( aFontMatrix
);
791 // TODO(F2): use correct scale direction, font
792 // height might be width or anything else
794 // TODO(Q3): This code smells of programming by
795 // coincidence (the next two if statements)
797 ::Size
rFontSizeLog( rFont
.GetFontSize() );
799 if (rFontSizeLog
.Height() == 0)
801 // guess 16 pixel (as in VCL)
802 rFontSizeLog
= ::Size(0, 16);
804 // convert to target MapUnit if not pixels
805 rFontSizeLog
= OutputDevice::LogicToLogic(rFontSizeLog
, MapMode(MapUnit::MapPixel
), rParms
.mrVDev
.GetMapMode());
808 const sal_Int32 nFontWidthLog
= rFontSizeLog
.Width();
809 if( nFontWidthLog
!= 0 )
811 vcl::Font aTestFont
= rFont
;
812 aTestFont
.SetAverageFontWidth( 0 );
813 sal_Int32 nNormalWidth
= rParms
.mrVDev
.GetFontMetric( aTestFont
).GetAverageFontWidth();
814 if( nNormalWidth
!= nFontWidthLog
)
816 aFontMatrix
.m00
= static_cast<double>(nFontWidthLog
) / nNormalWidth
;
819 // #i52608# apply map mode scale also to font matrix - an
820 // anisotrophic mapmode must be reflected in an
821 // anisotrophic font matrix scale.
822 const OutDevState
& rState( rParms
.mrStates
.getState() );
823 if( !::basegfx::fTools::equal(
824 rState
.mapModeTransform
.get(0,0),
825 rState
.mapModeTransform
.get(1,1)) )
827 const double nScaleX( rState
.mapModeTransform
.get(0,0) );
828 const double nScaleY( rState
.mapModeTransform
.get(1,1) );
830 // note: no reason to check for division by zero, we
831 // always have the value closer (or equal) to zero as
833 if( fabs(nScaleX
) < fabs(nScaleY
) )
834 aFontMatrix
.m00
*= nScaleX
/ nScaleY
;
836 aFontMatrix
.m11
*= nScaleY
/ nScaleX
;
838 aFontRequest
.CellSize
= (rState
.mapModeTransform
* vcl::unotools::b2DSizeFromSize(rFontSizeLog
)).getY();
840 if (rFont
.GetEmphasisMark() != FontEmphasisMark::NONE
)
842 uno::Sequence
< beans::PropertyValue
> aProperties(1);
843 aProperties
[0].Name
= "EmphasisMark";
844 aProperties
[0].Value
<<= sal_uInt32(rFont
.GetEmphasisMark());
845 return rParms
.mrCanvas
->getUNOCanvas()->createFont(aFontRequest
,
850 return rParms
.mrCanvas
->getUNOCanvas()->createFont( aFontRequest
,
851 uno::Sequence
< beans::PropertyValue
>(),
855 // create text effects such as shadow/relief/embossed
856 void ImplRenderer::createTextAction( const ::Point
& rStartPoint
,
857 const OUString
& rString
,
860 const ::tools::Long
* pCharWidths
,
861 const ActionFactoryParameters
& rParms
,
862 bool bSubsettableActions
)
864 ENSURE_OR_THROW( nIndex
>= 0 && nLength
<= rString
.getLength() + nIndex
,
865 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
868 return; // zero-length text, no visible output
870 const OutDevState
& rState( rParms
.mrStates
.getState() );
872 // TODO(F2): implement all text effects
873 // if( rState.textAlignment ); // TODO(F2): NYI
875 ::Color
aTextFillColor( COL_AUTO
);
876 ::Color
aShadowColor( COL_AUTO
);
877 ::Color
aReliefColor( COL_AUTO
);
878 ::Size aShadowOffset
;
879 ::Size aReliefOffset
;
881 uno::Reference
<rendering::XColorSpace
> xColorSpace(
882 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
884 if( rState
.isTextEffectShadowSet
)
886 // calculate shadow offset (similar to outdev3.cxx)
887 // TODO(F3): better match with outdev3.cxx
888 sal_Int32 nShadowOffset
= static_cast<sal_Int32
>(1.5 + ((rParms
.mrVDev
.GetFont().GetFontHeight()-24.0)/24.0));
889 if( nShadowOffset
< 1 )
892 aShadowOffset
.setWidth( nShadowOffset
);
893 aShadowOffset
.setHeight( nShadowOffset
);
895 // determine shadow color (from outdev3.cxx)
896 ::Color aTextColor
= vcl::unotools::doubleSequenceToColor(
897 rState
.textColor
, xColorSpace
);
898 bool bIsDark
= (aTextColor
== COL_BLACK
)
899 || (aTextColor
.GetLuminance() < 8);
901 aShadowColor
= bIsDark
? COL_LIGHTGRAY
: COL_BLACK
;
902 aShadowColor
.SetTransparency( aTextColor
.GetTransparency() );
905 if( rState
.textReliefStyle
!= FontRelief::NONE
)
907 // calculate relief offset (similar to outdev3.cxx)
908 sal_Int32 nReliefOffset
= rParms
.mrVDev
.PixelToLogic( Size( 1, 1 ) ).Height();
909 nReliefOffset
+= nReliefOffset
/2;
910 if( nReliefOffset
< 1 )
913 if( rState
.textReliefStyle
== FontRelief::Engraved
)
914 nReliefOffset
= -nReliefOffset
;
916 aReliefOffset
.setWidth( nReliefOffset
);
917 aReliefOffset
.setHeight( nReliefOffset
);
919 // determine relief color (from outdev3.cxx)
920 ::Color aTextColor
= vcl::unotools::doubleSequenceToColor(
921 rState
.textColor
, xColorSpace
);
923 aReliefColor
= COL_LIGHTGRAY
;
925 // we don't have an automatic color, so black is always
926 // drawn on white (literally copied from
927 // vcl/source/gdi/outdev3.cxx)
928 if( aTextColor
== COL_BLACK
)
930 aTextColor
= COL_WHITE
;
931 rParms
.mrStates
.getState().textColor
=
932 vcl::unotools::colorToDoubleSequence(
933 aTextColor
, xColorSpace
);
936 if( aTextColor
== COL_WHITE
)
937 aReliefColor
= COL_BLACK
;
938 aReliefColor
.SetTransparency( aTextColor
.GetTransparency() );
941 if (rState
.isTextFillColorSet
)
942 aTextFillColor
= vcl::unotools::doubleSequenceToColor(rState
.textFillColor
, xColorSpace
);
944 // create the actual text action
945 std::shared_ptr
<Action
> pTextAction(
946 TextActionFactory::createTextAction(
961 bSubsettableActions
) );
963 std::shared_ptr
<Action
> pStrikeoutTextAction
;
965 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
|| rState
.textStrikeoutStyle
== STRIKEOUT_SLASH
)
967 ::tools::Long nWidth
= rParms
.mrVDev
.GetTextWidth( rString
,nIndex
,nLength
);
969 sal_Unicode pChars
[4];
970 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
)
974 pChars
[3]=pChars
[2]=pChars
[1]=pChars
[0];
976 ::tools::Long nStrikeoutWidth
= (rParms
.mrVDev
.GetTextWidth(
977 OUString(pChars
, SAL_N_ELEMENTS(pChars
))) + 2) / 4;
979 if( nStrikeoutWidth
<= 0 )
982 ::tools::Long nMaxWidth
= nStrikeoutWidth
/2;
985 nMaxWidth
+= nWidth
+ 1;
987 ::tools::Long nFullStrikeoutWidth
= 0;
988 OUStringBuffer aStrikeoutText
;
989 while( (nFullStrikeoutWidth
+=nStrikeoutWidth
) < nMaxWidth
+1 )
990 aStrikeoutText
.append(pChars
[0]);
992 sal_Int32 nLen
= aStrikeoutText
.getLength();
996 ::tools::Long nInterval
= ( nWidth
- nStrikeoutWidth
* nLen
) / nLen
;
997 nStrikeoutWidth
+= nInterval
;
998 ::tools::Long
* pStrikeoutCharWidths
= new ::tools::Long
[nLen
];
1000 for ( int i
= 0;i
<nLen
; i
++)
1002 pStrikeoutCharWidths
[i
] = nStrikeoutWidth
;
1005 for ( int i
= 1;i
< nLen
; i
++ )
1007 pStrikeoutCharWidths
[ i
] += pStrikeoutCharWidths
[ i
-1 ];
1010 pStrikeoutTextAction
=
1011 TextActionFactory::createTextAction(
1018 aStrikeoutText
.makeStringAndClear(),
1021 pStrikeoutCharWidths
,
1026 bSubsettableActions
) ;
1033 maActions
.emplace_back(
1035 rParms
.mrCurrActionIndex
);
1037 if ( pStrikeoutTextAction
)
1039 maActions
.emplace_back(
1040 pStrikeoutTextAction
,
1041 rParms
.mrCurrActionIndex
);
1044 rParms
.mrCurrActionIndex
+= pTextAction
->getActionCount()-1;
1047 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon
& rClipPoly
,
1048 const ActionFactoryParameters
& rParms
,
1051 ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
1053 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1054 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1056 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1057 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1060 (bEmptyClipRect
&& bEmptyClipPoly
) )
1062 rState
.clip
= rClipPoly
;
1066 if( !bEmptyClipRect
)
1068 // TODO(P3): Use Liang-Barsky polygon clip here,
1069 // after all, one object is just a rectangle!
1071 // convert rect to polygon beforehand, must revert
1072 // to general polygon clipping here.
1073 ::tools::Rectangle aRect
= rState
.clipRect
;
1074 // #121100# VCL rectangular clips always
1075 // include one more pixel to the right
1077 aRect
.AdjustRight(1);
1078 aRect
.AdjustBottom(1);
1079 rState
.clip
= ::basegfx::B2DPolyPolygon(
1080 ::basegfx::utils::createPolygonFromRect(
1081 vcl::unotools::b2DRectangleFromRectangle(aRect
) ) );
1085 rState
.clip
= basegfx::utils::clipPolyPolygonOnPolyPolygon(
1086 rClipPoly
, rState
.clip
, true, false);
1089 // by now, our clip resides in the OutDevState::clip
1091 rState
.clipRect
.SetEmpty();
1093 if( rState
.clip
.count() == 0 )
1095 if( rState
.clipRect
.IsEmpty() )
1097 rState
.xClipPoly
.clear();
1101 ::tools::Rectangle aRect
= rState
.clipRect
;
1102 // #121100# VCL rectangular clips
1103 // always include one more pixel to
1104 // the right and the bottom
1105 aRect
.AdjustRight(1);
1106 aRect
.AdjustBottom(1);
1107 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1108 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1109 ::basegfx::B2DPolyPolygon(
1110 ::basegfx::utils::createPolygonFromRect(
1111 vcl::unotools::b2DRectangleFromRectangle(aRect
) ) ) );
1116 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1117 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1122 void ImplRenderer::updateClipping( const ::tools::Rectangle
& rClipRect
,
1123 const ActionFactoryParameters
& rParms
,
1126 ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
1128 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1129 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1131 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1132 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1135 (bEmptyClipRect
&& bEmptyClipPoly
) )
1137 rState
.clipRect
= rClipRect
;
1138 rState
.clip
.clear();
1140 else if( bEmptyClipPoly
)
1142 rState
.clipRect
.Intersection( rClipRect
);
1143 rState
.clip
.clear();
1147 // TODO(P3): Handle a fourth case here, when all clip
1148 // polygons are rectangular, once B2DMultiRange's
1149 // sweep line implementation is done.
1151 // general case: convert to polygon and clip
1154 // convert rect to polygon beforehand, must revert
1155 // to general polygon clipping here.
1156 ::basegfx::B2DPolyPolygon
aClipPoly(
1157 ::basegfx::utils::createPolygonFromRect(
1158 vcl::unotools::b2DRectangleFromRectangle(rClipRect
) ) );
1160 rState
.clipRect
.SetEmpty();
1163 rState
.clip
= basegfx::utils::clipPolyPolygonOnPolyPolygon(
1164 aClipPoly
, rState
.clip
, true, false);
1167 if( rState
.clip
.count() == 0 )
1169 if( rState
.clipRect
.IsEmpty() )
1171 rState
.xClipPoly
.clear();
1175 // #121100# VCL rectangular clips
1176 // always include one more pixel to
1177 // the right and the bottom
1178 ::tools::Rectangle aRect
= rState
.clipRect
;
1179 aRect
.AdjustRight(1);
1180 aRect
.AdjustBottom(1);
1181 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1182 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1183 ::basegfx::B2DPolyPolygon(
1184 ::basegfx::utils::createPolygonFromRect(
1185 vcl::unotools::b2DRectangleFromRectangle(aRect
) ) ) );
1190 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1191 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1196 void ImplRenderer::createActions( GDIMetaFile
& rMtf
,
1197 const ActionFactoryParameters
& rFactoryParms
,
1198 bool bSubsettableActions
)
1200 /* TODO(P2): interpret mtf-comments
1201 ================================
1203 - gradient fillings (do that via comments)
1205 - think about mapping. _If_ we do everything in logical
1206 coordinates (which would solve the probs for stroke
1207 widths and text offsets), then we would have to
1208 recalc scaling for every drawing operation. This is
1209 because the outdev map mode might change at any time.
1210 Also keep in mind, that, although we've double precision
1211 float arithmetic now, different offsets might still
1212 generate different roundings (aka
1213 'OutputDevice::SetPixelOffset())
1217 // alias common parameters
1218 VectorOfOutDevStates
& rStates(rFactoryParms
.mrStates
);
1219 const CanvasSharedPtr
& rCanvas(rFactoryParms
.mrCanvas
);
1220 ::VirtualDevice
& rVDev(rFactoryParms
.mrVDev
);
1221 const Parameters
& rParms(rFactoryParms
.mrParms
);
1222 sal_Int32
& io_rCurrActionIndex(rFactoryParms
.mrCurrActionIndex
);
1225 // Loop over every metaaction
1226 // ==========================
1227 MetaAction
* pCurrAct
;
1229 // TODO(P1): think about caching
1230 for( pCurrAct
=rMtf
.FirstAction();
1232 pCurrAct
= rMtf
.NextAction() )
1234 // execute every action, to keep VDev state up-to-date
1235 // currently used only for
1237 // - the line/fill color when processing a MetaActionType::Transparent
1238 // - SetFont to process font metric specific actions
1239 pCurrAct
->Execute( &rVDev
);
1241 SAL_INFO("cppcanvas.emf", "MTF\trecord type: 0x" << static_cast<sal_uInt16
>(pCurrAct
->GetType()) << " (" << static_cast<sal_uInt16
>(pCurrAct
->GetType()) << ")");
1243 switch( pCurrAct
->GetType() )
1247 // In the first part of this monster-switch, we
1248 // handle all state-changing meta actions. These
1249 // are all handled locally.
1252 case MetaActionType::PUSH
:
1254 MetaPushAction
* pPushAction
= static_cast<MetaPushAction
*>(pCurrAct
);
1255 rStates
.pushState(pPushAction
->GetFlags());
1259 case MetaActionType::POP
:
1263 case MetaActionType::TEXTLANGUAGE
:
1264 case MetaActionType::REFPOINT
:
1265 // handled via pCurrAct->Execute( &rVDev )
1268 case MetaActionType::MAPMODE
:
1269 // modify current mapModeTransformation
1270 // transformation, such that subsequent
1271 // coordinates map correctly
1272 tools::calcLogic2PixelAffineTransform( rStates
.getState().mapModeTransform
,
1276 // monitor clip regions, to assemble clip polygon on our own
1277 case MetaActionType::CLIPREGION
:
1279 MetaClipRegionAction
* pClipAction
= static_cast<MetaClipRegionAction
*>(pCurrAct
);
1281 if( !pClipAction
->IsClipping() )
1284 rStates
.getState().clip
.clear();
1288 if( !pClipAction
->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1290 SAL_INFO( "cppcanvas.emf", "ImplRenderer::createActions(): non-polygonal clip "
1291 "region encountered, falling back to bounding box!" );
1293 // #121806# explicitly kept integer
1294 ::tools::Rectangle
aClipRect(
1296 pClipAction
->GetRegion().GetBoundRect() ) );
1298 // intersect current clip with given rect
1306 // set new clip polygon (don't intersect
1307 // with old one, just set it)
1309 // #121806# explicitly kept integer
1310 basegfx::B2DPolyPolygon
aPolyPolygon(pClipAction
->GetRegion().GetAsB2DPolyPolygon());
1312 aPolyPolygon
.transform(rVDev
.GetViewTransformation());
1323 case MetaActionType::ISECTRECTCLIPREGION
:
1325 MetaISectRectClipRegionAction
* pClipAction
= static_cast<MetaISectRectClipRegionAction
*>(pCurrAct
);
1327 // #121806# explicitly kept integer
1328 ::tools::Rectangle
aClipRect(
1329 rVDev
.LogicToPixel( pClipAction
->GetRect() ) );
1331 // intersect current clip with given rect
1340 case MetaActionType::ISECTREGIONCLIPREGION
:
1342 MetaISectRegionClipRegionAction
* pClipAction
= static_cast<MetaISectRegionClipRegionAction
*>(pCurrAct
);
1344 if( !pClipAction
->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1346 SAL_INFO( "cppcanvas.emf", "ImplRenderer::createActions(): non-polygonal clip "
1347 "region encountered, falling back to bounding box!" );
1349 // #121806# explicitly kept integer
1350 ::tools::Rectangle
aClipRect(
1351 rVDev
.LogicToPixel( pClipAction
->GetRegion().GetBoundRect() ) );
1353 // intersect current clip with given rect
1361 // intersect current clip with given clip polygon
1363 // #121806# explicitly kept integer
1364 basegfx::B2DPolyPolygon
aPolyPolygon(pClipAction
->GetRegion().GetAsB2DPolyPolygon());
1366 aPolyPolygon
.transform(rVDev
.GetViewTransformation());
1376 case MetaActionType::MOVECLIPREGION
:
1380 case MetaActionType::LINECOLOR
:
1381 if( !rParms
.maLineColor
)
1383 setStateColor( static_cast<MetaLineColorAction
*>(pCurrAct
),
1384 rStates
.getState().isLineColorSet
,
1385 rStates
.getState().lineColor
,
1390 // #120994# Do switch on/off LineColor, even when an overriding one is set
1391 bool bSetting(static_cast<MetaLineColorAction
*>(pCurrAct
)->IsSetting());
1393 rStates
.getState().isLineColorSet
= bSetting
;
1397 case MetaActionType::FILLCOLOR
:
1398 if( !rParms
.maFillColor
)
1400 setStateColor( static_cast<MetaFillColorAction
*>(pCurrAct
),
1401 rStates
.getState().isFillColorSet
,
1402 rStates
.getState().fillColor
,
1407 // #120994# Do switch on/off FillColor, even when an overriding one is set
1408 bool bSetting(static_cast<MetaFillColorAction
*>(pCurrAct
)->IsSetting());
1410 rStates
.getState().isFillColorSet
= bSetting
;
1414 case MetaActionType::TEXTCOLOR
:
1416 if( !rParms
.maTextColor
)
1418 // Text color is set unconditionally, thus, no
1419 // use of setStateColor here
1420 ::Color
aColor( static_cast<MetaTextColorAction
*>(pCurrAct
)->GetColor() );
1422 // force alpha part of color to
1423 // opaque. transparent painting is done
1424 // explicitly via MetaActionType::Transparent
1425 aColor
.SetTransparency(0);
1427 rStates
.getState().textColor
=
1428 vcl::unotools::colorToDoubleSequence(
1430 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1435 case MetaActionType::TEXTFILLCOLOR
:
1436 if( !rParms
.maTextColor
)
1438 setStateColor( static_cast<MetaTextFillColorAction
*>(pCurrAct
),
1439 rStates
.getState().isTextFillColorSet
,
1440 rStates
.getState().textFillColor
,
1445 // #120994# Do switch on/off TextFillColor, even when an overriding one is set
1446 bool bSetting(static_cast<MetaTextFillColorAction
*>(pCurrAct
)->IsSetting());
1448 rStates
.getState().isTextFillColorSet
= bSetting
;
1452 case MetaActionType::TEXTLINECOLOR
:
1453 if( !rParms
.maTextColor
)
1455 setStateColor( static_cast<MetaTextLineColorAction
*>(pCurrAct
),
1456 rStates
.getState().isTextLineColorSet
,
1457 rStates
.getState().textLineColor
,
1462 // #120994# Do switch on/off TextLineColor, even when an overriding one is set
1463 bool bSetting(static_cast<MetaTextLineColorAction
*>(pCurrAct
)->IsSetting());
1465 rStates
.getState().isTextLineColorSet
= bSetting
;
1469 case MetaActionType::OVERLINECOLOR
:
1470 if( !rParms
.maTextColor
)
1472 setStateColor( static_cast<MetaOverlineColorAction
*>(pCurrAct
),
1473 rStates
.getState().isTextOverlineColorSet
,
1474 rStates
.getState().textOverlineColor
,
1479 bool bSetting(static_cast<MetaOverlineColorAction
*>(pCurrAct
)->IsSetting());
1481 rStates
.getState().isTextOverlineColorSet
= bSetting
;
1485 case MetaActionType::TEXTALIGN
:
1487 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1488 const TextAlign
eTextAlign( static_cast<MetaTextAlignAction
*>(pCurrAct
)->GetTextAlign() );
1490 rState
.textReferencePoint
= eTextAlign
;
1494 case MetaActionType::FONT
:
1496 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1497 const vcl::Font
& rFont( static_cast<MetaFontAction
*>(pCurrAct
)->GetFont() );
1499 rState
.xFont
= createFont( rState
.fontRotation
,
1503 // TODO(Q2): define and use appropriate enumeration types
1504 rState
.textReliefStyle
= rFont
.GetRelief();
1505 rState
.textOverlineStyle
= static_cast<sal_Int8
>(rFont
.GetOverline());
1506 rState
.textUnderlineStyle
= rParms
.maFontUnderline
?
1507 (*rParms
.maFontUnderline
? sal_Int8(LINESTYLE_SINGLE
) : sal_Int8(LINESTYLE_NONE
)) :
1508 static_cast<sal_Int8
>(rFont
.GetUnderline());
1509 rState
.textStrikeoutStyle
= static_cast<sal_Int8
>(rFont
.GetStrikeout());
1510 rState
.textEmphasisMark
= rFont
.GetEmphasisMark();
1511 rState
.isTextEffectShadowSet
= rFont
.IsShadow();
1512 rState
.isTextWordUnderlineSet
= rFont
.IsWordLineMode();
1513 rState
.isTextOutlineModeSet
= rFont
.IsOutline();
1517 case MetaActionType::RASTEROP
:
1521 case MetaActionType::LAYOUTMODE
:
1523 // TODO(F2): A lot is missing here
1524 ComplexTextLayoutFlags nLayoutMode
= static_cast<MetaLayoutModeAction
*>(pCurrAct
)->GetLayoutMode();
1525 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1527 ComplexTextLayoutFlags nBidiLayoutMode
= nLayoutMode
& (ComplexTextLayoutFlags::BiDiRtl
|ComplexTextLayoutFlags::BiDiStrong
);
1528 if( nBidiLayoutMode
== ComplexTextLayoutFlags::Default
)
1529 rState
.textDirection
= rendering::TextDirection::WEAK_LEFT_TO_RIGHT
;
1530 else if( nBidiLayoutMode
== ComplexTextLayoutFlags::BiDiStrong
)
1531 rState
.textDirection
= rendering::TextDirection::STRONG_LEFT_TO_RIGHT
;
1532 else if( nBidiLayoutMode
== ComplexTextLayoutFlags::BiDiRtl
)
1533 rState
.textDirection
= rendering::TextDirection::WEAK_RIGHT_TO_LEFT
;
1534 else if( nBidiLayoutMode
== (ComplexTextLayoutFlags::BiDiRtl
| ComplexTextLayoutFlags::BiDiStrong
))
1535 rState
.textDirection
= rendering::TextDirection::STRONG_RIGHT_TO_LEFT
;
1537 rState
.textAlignment
= 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1538 if( (nLayoutMode
& (ComplexTextLayoutFlags::BiDiRtl
| ComplexTextLayoutFlags::TextOriginRight
) )
1539 && !(nLayoutMode
& ComplexTextLayoutFlags::TextOriginLeft
) )
1541 rState
.textAlignment
= 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1547 // In the second part of this monster-switch, we
1548 // handle all recursing meta actions. These are the
1549 // ones generating a metafile by themselves, which is
1550 // then processed by recursively calling this method.
1553 case MetaActionType::GRADIENT
:
1555 MetaGradientAction
* pGradAct
= static_cast<MetaGradientAction
*>(pCurrAct
);
1556 createGradientAction( ::tools::Polygon( pGradAct
->GetRect() ),
1557 pGradAct
->GetGradient(),
1560 bSubsettableActions
);
1564 case MetaActionType::HATCH
:
1566 // TODO(F2): use native Canvas hatches here
1567 GDIMetaFile aTmpMtf
;
1569 rVDev
.AddHatchActions( static_cast<MetaHatchAction
*>(pCurrAct
)->GetPolyPolygon(),
1570 static_cast<MetaHatchAction
*>(pCurrAct
)->GetHatch(),
1572 createActions( aTmpMtf
, rFactoryParms
,
1573 bSubsettableActions
);
1577 case MetaActionType::EPS
:
1579 MetaEPSAction
* pAct
= static_cast<MetaEPSAction
*>(pCurrAct
);
1580 const GDIMetaFile
& rSubstitute
= pAct
->GetSubstitute();
1582 // #121806# explicitly kept integer
1583 const Size
aMtfSize( rSubstitute
.GetPrefSize() );
1584 const Size
aMtfSizePixPre( rVDev
.LogicToPixel( aMtfSize
,
1585 rSubstitute
.GetPrefMapMode() ) );
1587 // #i44110# correct null-sized output - there
1588 // are metafiles which have zero size in at
1589 // least one dimension
1591 // Remark the 1L cannot be replaced, that would cause max to compare long/int
1592 const Size
aMtfSizePix( std::max( aMtfSizePixPre
.Width(), ::tools::Long(1) ),
1593 std::max( aMtfSizePixPre
.Height(), ::tools::Long(1) ) );
1595 // Setup local transform, such that the
1596 // metafile renders itself into the given
1598 rStates
.pushState(PushFlags::ALL
);
1601 rVDev
.SetMapMode( rSubstitute
.GetPrefMapMode() );
1603 const ::Point
& rPos( rVDev
.LogicToPixel( pAct
->GetPoint() ) );
1604 const ::Size
& rSize( rVDev
.LogicToPixel( pAct
->GetSize() ) );
1606 rStates
.getState().transform
.translate( rPos
.X(),
1608 rStates
.getState().transform
.scale( static_cast<double>(rSize
.Width()) / aMtfSizePix
.Width(),
1609 static_cast<double>(rSize
.Height()) / aMtfSizePix
.Height() );
1611 createActions( const_cast<GDIMetaFile
&>(pAct
->GetSubstitute()),
1613 bSubsettableActions
);
1620 // handle metafile comments, to retrieve
1621 // meta-information for gradients, fills and
1622 // strokes. May skip actions, and may recurse.
1623 case MetaActionType::COMMENT
:
1625 MetaCommentAction
* pAct
= static_cast<MetaCommentAction
*>(pCurrAct
);
1628 if (pAct
->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
1630 MetaGradientExAction
* pGradAction
= nullptr;
1631 bool bDone( false );
1634 pCurrAct
=rMtf
.NextAction();
1637 switch( pCurrAct
->GetType() )
1639 // extract gradient info
1640 case MetaActionType::GRADIENTEX
:
1641 pGradAction
= static_cast<MetaGradientExAction
*>(pCurrAct
);
1644 // skip broken-down rendering, output gradient when sequence is ended
1645 case MetaActionType::COMMENT
:
1646 if( static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END") )
1652 createGradientAction( pGradAction
->GetPolyPolygon(),
1653 pGradAction
->GetGradient(),
1656 bSubsettableActions
);
1664 // TODO(P2): Handle drawing layer strokes, via
1665 // XPATHSTROKE_SEQ_BEGIN comment
1667 // Handle drawing layer fills
1668 else if( pAct
->GetComment() == "XPATHFILL_SEQ_BEGIN" )
1670 const sal_uInt8
* pData
= pAct
->GetData();
1673 SvMemoryStream
aMemStm( const_cast<sal_uInt8
*>(pData
), pAct
->GetDataSize(), StreamMode::READ
);
1675 SvtGraphicFill aFill
;
1676 ReadSvtGraphicFill( aMemStm
, aFill
);
1678 // TODO(P2): Also handle gradients and
1679 // hatches like this
1681 // only evaluate comment for pure
1682 // bitmap fills. If a transparency
1683 // gradient is involved (denoted by
1684 // the FloatTransparent action), take
1685 // the normal meta actions.
1686 if( aFill
.getFillType() == SvtGraphicFill::fillTexture
&&
1687 !isActionContained( rMtf
,
1688 "XPATHFILL_SEQ_END",
1689 MetaActionType::FLOATTRANSPARENT
) )
1691 rendering::Texture aTexture
;
1693 // TODO(F1): the SvtGraphicFill
1694 // can also transport metafiles
1695 // here, handle that case, too
1697 aFill
.getGraphic( aGraphic
);
1699 BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
1700 const ::Size
aBmpSize( aBmpEx
.GetSizePixel() );
1702 ::SvtGraphicFill::Transform aTransform
;
1703 aFill
.getTransform( aTransform
);
1705 ::basegfx::B2DHomMatrix aMatrix
;
1707 // convert to basegfx matrix
1708 aMatrix
.set(0,0, aTransform
.matrix
[ 0 ] );
1709 aMatrix
.set(0,1, aTransform
.matrix
[ 1 ] );
1710 aMatrix
.set(0,2, aTransform
.matrix
[ 2 ] );
1711 aMatrix
.set(1,0, aTransform
.matrix
[ 3 ] );
1712 aMatrix
.set(1,1, aTransform
.matrix
[ 4 ] );
1713 aMatrix
.set(1,2, aTransform
.matrix
[ 5 ] );
1715 ::basegfx::B2DHomMatrix aScale
;
1716 aScale
.scale( aBmpSize
.Width(),
1717 aBmpSize
.Height() );
1719 // post-multiply with the bitmap
1720 // size (XCanvas' texture assumes
1721 // the given bitmap to be
1722 // normalized to [0,1]x[0,1]
1724 aMatrix
= aMatrix
* aScale
;
1726 // pre-multiply with the
1727 // logic-to-pixel scale factor
1728 // (the metafile comment works in
1729 // logical coordinates).
1730 ::basegfx::B2DHomMatrix aLogic2PixelTransform
;
1731 aMatrix
*= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform
,
1734 ::basegfx::unotools::affineMatrixFromHomMatrix(
1735 aTexture
.AffineTransform
,
1738 aTexture
.Alpha
= 1.0 - aFill
.getTransparency();
1739 aTexture
.Bitmap
= vcl::unotools::xBitmapFromBitmapEx( aBmpEx
);
1740 if( aFill
.isTiling() )
1742 aTexture
.RepeatModeX
= rendering::TexturingMode::REPEAT
;
1743 aTexture
.RepeatModeY
= rendering::TexturingMode::REPEAT
;
1747 aTexture
.RepeatModeX
= rendering::TexturingMode::NONE
;
1748 aTexture
.RepeatModeY
= rendering::TexturingMode::NONE
;
1751 ::tools::PolyPolygon aPath
;
1752 aFill
.getPath( aPath
);
1754 ::basegfx::B2DPolyPolygon
aPoly( aPath
.getB2DPolyPolygon() );
1755 aPoly
.transform( rStates
.getState().mapModeTransform
);
1756 std::shared_ptr
<Action
> pPolyAction(
1757 internal::PolyPolyActionFactory::createPolyPolyAction(
1765 maActions
.emplace_back(
1767 io_rCurrActionIndex
);
1769 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
1772 // skip broken-down render output
1774 "XPATHFILL_SEQ_END",
1775 io_rCurrActionIndex
);
1779 // Handle drawing layer fills
1780 else if( pAct
->GetComment() == "EMF_PLUS" ) {
1781 SAL_WARN ("cppcanvas.emf", "EMF+ code was refactored and removed");
1782 } else if( pAct
->GetComment() == "EMF_PLUS_HEADER_INFO" ) {
1783 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pAct
->GetDataSize ());
1785 SvMemoryStream
rMF (const_cast<sal_uInt8
*>(pAct
->GetData ()), pAct
->GetDataSize (), StreamMode::READ
);
1787 rMF
.ReadInt32( nFrameLeft
).ReadInt32( nFrameTop
).ReadInt32( nFrameRight
).ReadInt32( nFrameBottom
);
1788 SAL_INFO ("cppcanvas.emf", "EMF+ picture frame: " << nFrameLeft
<< "," << nFrameTop
<< " - " << nFrameRight
<< "," << nFrameBottom
);
1789 rMF
.ReadInt32( nPixX
).ReadInt32( nPixY
).ReadInt32( nMmX
).ReadInt32( nMmY
);
1790 SAL_INFO ("cppcanvas.emf", "EMF+ ref device pixel size: " << nPixX
<< "x" << nPixY
<< " mm size: " << nMmX
<< "x" << nMmY
);
1792 ReadXForm( rMF
, aBaseTransform
);
1793 //aWorldTransform.Set (aBaseTransform);
1799 // In the third part of this monster-switch, we
1800 // handle all 'acting' meta actions. These are all
1801 // processed by constructing function objects for
1802 // them, which will later ease caching.
1805 case MetaActionType::POINT
:
1807 const OutDevState
& rState( rStates
.getState() );
1808 if( rState
.lineColor
.hasElements() )
1810 std::shared_ptr
<Action
> pPointAction(
1811 internal::PointActionFactory::createPointAction(
1812 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint(
1813 static_cast<MetaPointAction
*>(pCurrAct
)->GetPoint() ),
1819 maActions
.emplace_back(
1821 io_rCurrActionIndex
);
1823 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1829 case MetaActionType::PIXEL
:
1831 const OutDevState
& rState( rStates
.getState() );
1832 if( rState
.lineColor
.hasElements() )
1834 std::shared_ptr
<Action
> pPointAction(
1835 internal::PointActionFactory::createPointAction(
1836 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint(
1837 static_cast<MetaPixelAction
*>(pCurrAct
)->GetPoint() ),
1840 static_cast<MetaPixelAction
*>(pCurrAct
)->GetColor() ) );
1844 maActions
.emplace_back(
1846 io_rCurrActionIndex
);
1848 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1854 case MetaActionType::LINE
:
1856 const OutDevState
& rState( rStates
.getState() );
1857 if( rState
.lineColor
.hasElements() )
1859 MetaLineAction
* pLineAct
= static_cast<MetaLineAction
*>(pCurrAct
);
1861 const LineInfo
& rLineInfo( pLineAct
->GetLineInfo() );
1863 const ::basegfx::B2DPoint
aStartPoint(
1864 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( pLineAct
->GetStartPoint() ));
1865 const ::basegfx::B2DPoint
aEndPoint(
1866 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( pLineAct
->GetEndPoint() ));
1868 std::shared_ptr
<Action
> pLineAction
;
1870 if( rLineInfo
.IsDefault() )
1874 internal::LineActionFactory::createLineAction(
1882 maActions
.emplace_back(
1884 io_rCurrActionIndex
);
1886 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
1889 else if( LineStyle::NONE
!= rLineInfo
.GetStyle() )
1892 rendering::StrokeAttributes aStrokeAttributes
;
1894 setupStrokeAttributes( aStrokeAttributes
,
1898 // XCanvas can only stroke polygons,
1899 // not simple lines - thus, handle
1900 // this case via the polypolygon
1902 ::basegfx::B2DPolygon aPoly
;
1903 aPoly
.append( aStartPoint
);
1904 aPoly
.append( aEndPoint
);
1906 internal::PolyPolyActionFactory::createPolyPolyAction(
1907 ::basegfx::B2DPolyPolygon( aPoly
),
1908 rCanvas
, rState
, aStrokeAttributes
);
1912 maActions
.emplace_back(
1914 io_rCurrActionIndex
);
1916 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
1919 // else: line style is default
1920 // (i.e. invisible), don't generate action
1925 case MetaActionType::RECT
:
1927 const ::tools::Rectangle
& rRect(
1928 static_cast<MetaRectAction
*>(pCurrAct
)->GetRect() );
1930 if( rRect
.IsEmpty() )
1933 const OutDevState
& rState( rStates
.getState() );
1934 const ::basegfx::B2DPoint
aTopLeftPixel(
1935 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ) );
1936 const ::basegfx::B2DPoint
aBottomRightPixel(
1937 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1938 // #121100# OutputDevice::DrawRect() fills
1939 // rectangles Apple-like, i.e. with one
1940 // additional pixel to the right and bottom.
1941 ::basegfx::B2DPoint(1,1) );
1943 createFillAndStroke( ::basegfx::utils::createPolygonFromRect(
1944 ::basegfx::B2DRange( aTopLeftPixel
,
1945 aBottomRightPixel
)),
1950 case MetaActionType::ROUNDRECT
:
1952 const ::tools::Rectangle
& rRect(
1953 static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetRect());
1955 if( rRect
.IsEmpty() )
1958 ::basegfx::B2DPolygon
aPoly(
1959 ::basegfx::utils::createPolygonFromRect(
1960 ::basegfx::B2DRange(
1961 vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
1962 vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1963 ::basegfx::B2DPoint(1,1) ),
1964 static_cast<double>(static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetHorzRound()) / rRect
.GetWidth(),
1965 static_cast<double>(static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetVertRound()) / rRect
.GetHeight() ) );
1966 aPoly
.transform( rStates
.getState().mapModeTransform
);
1968 createFillAndStroke( aPoly
,
1973 case MetaActionType::ELLIPSE
:
1975 const ::tools::Rectangle
& rRect(
1976 static_cast<MetaEllipseAction
*>(pCurrAct
)->GetRect() );
1978 if( rRect
.IsEmpty() )
1981 const ::basegfx::B2DRange
aRange(
1982 vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
1983 vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1984 ::basegfx::B2DPoint(1,1) );
1986 ::basegfx::B2DPolygon
aPoly(
1987 ::basegfx::utils::createPolygonFromEllipse(
1989 aRange
.getWidth() / 2, // divide by 2 since createPolygonFromEllipse
1990 aRange
.getHeight() / 2 )); // expects the radius and NOT the diameter!
1991 aPoly
.transform( rStates
.getState().mapModeTransform
);
1993 createFillAndStroke( aPoly
,
1998 case MetaActionType::ARC
:
2000 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2001 const ::tools::Polygon
aToolsPoly( static_cast<MetaArcAction
*>(pCurrAct
)->GetRect(),
2002 static_cast<MetaArcAction
*>(pCurrAct
)->GetStartPoint(),
2003 static_cast<MetaArcAction
*>(pCurrAct
)->GetEndPoint(), PolyStyle::Arc
);
2004 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2005 aPoly
.transform( rStates
.getState().mapModeTransform
);
2007 createFillAndStroke( aPoly
,
2012 case MetaActionType::PIE
:
2014 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2015 const ::tools::Polygon
aToolsPoly( static_cast<MetaPieAction
*>(pCurrAct
)->GetRect(),
2016 static_cast<MetaPieAction
*>(pCurrAct
)->GetStartPoint(),
2017 static_cast<MetaPieAction
*>(pCurrAct
)->GetEndPoint(), PolyStyle::Pie
);
2018 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2019 aPoly
.transform( rStates
.getState().mapModeTransform
);
2021 createFillAndStroke( aPoly
,
2026 case MetaActionType::CHORD
:
2028 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2029 const ::tools::Polygon
aToolsPoly( static_cast<MetaChordAction
*>(pCurrAct
)->GetRect(),
2030 static_cast<MetaChordAction
*>(pCurrAct
)->GetStartPoint(),
2031 static_cast<MetaChordAction
*>(pCurrAct
)->GetEndPoint(), PolyStyle::Chord
);
2032 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2033 aPoly
.transform( rStates
.getState().mapModeTransform
);
2035 createFillAndStroke( aPoly
,
2040 case MetaActionType::POLYLINE
:
2042 const OutDevState
& rState( rStates
.getState() );
2043 if( rState
.lineColor
.hasElements() ||
2044 rState
.fillColor
.hasElements() )
2046 MetaPolyLineAction
* pPolyLineAct
= static_cast<MetaPolyLineAction
*>(pCurrAct
);
2048 const LineInfo
& rLineInfo( pPolyLineAct
->GetLineInfo() );
2049 ::basegfx::B2DPolygon
aPoly( pPolyLineAct
->GetPolygon().getB2DPolygon() );
2050 aPoly
.transform( rState
.mapModeTransform
);
2052 std::shared_ptr
<Action
> pLineAction
;
2054 if( rLineInfo
.IsDefault() )
2056 // plain hair line polygon
2058 internal::PolyPolyActionFactory::createLinePolyPolyAction(
2059 ::basegfx::B2DPolyPolygon(aPoly
),
2065 maActions
.emplace_back(
2067 io_rCurrActionIndex
);
2069 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2072 else if( LineStyle::NONE
!= rLineInfo
.GetStyle() )
2074 // 'thick' line polygon
2075 rendering::StrokeAttributes aStrokeAttributes
;
2077 setupStrokeAttributes( aStrokeAttributes
,
2082 internal::PolyPolyActionFactory::createPolyPolyAction(
2083 ::basegfx::B2DPolyPolygon(aPoly
),
2086 aStrokeAttributes
) ;
2090 maActions
.emplace_back(
2092 io_rCurrActionIndex
);
2094 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2097 // else: line style is default
2098 // (i.e. invisible), don't generate action
2103 case MetaActionType::POLYGON
:
2105 ::basegfx::B2DPolygon
aPoly( static_cast<MetaPolygonAction
*>(pCurrAct
)->GetPolygon().getB2DPolygon() );
2106 aPoly
.transform( rStates
.getState().mapModeTransform
);
2107 createFillAndStroke( aPoly
,
2112 case MetaActionType::POLYPOLYGON
:
2114 ::basegfx::B2DPolyPolygon
aPoly( static_cast<MetaPolyPolygonAction
*>(pCurrAct
)->GetPolyPolygon().getB2DPolyPolygon() );
2115 aPoly
.transform( rStates
.getState().mapModeTransform
);
2116 createFillAndStroke( aPoly
,
2121 case MetaActionType::BMP
:
2123 MetaBmpAction
* pAct
= static_cast<MetaBmpAction
*>(pCurrAct
);
2125 std::shared_ptr
<Action
> pBmpAction(
2126 internal::BitmapActionFactory::createBitmapAction(
2127 BitmapEx(pAct
->GetBitmap()),
2128 rStates
.getState().mapModeTransform
*
2129 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2131 rStates
.getState() ) );
2135 maActions
.emplace_back(
2137 io_rCurrActionIndex
);
2139 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2144 case MetaActionType::BMPSCALE
:
2146 MetaBmpScaleAction
* pAct
= static_cast<MetaBmpScaleAction
*>(pCurrAct
);
2148 std::shared_ptr
<Action
> pBmpAction(
2149 internal::BitmapActionFactory::createBitmapAction(
2150 BitmapEx(pAct
->GetBitmap()),
2151 rStates
.getState().mapModeTransform
*
2152 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2153 rStates
.getState().mapModeTransform
*
2154 vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2156 rStates
.getState() ) );
2160 maActions
.emplace_back(
2162 io_rCurrActionIndex
);
2164 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2169 case MetaActionType::BMPSCALEPART
:
2171 MetaBmpScalePartAction
* pAct
= static_cast<MetaBmpScalePartAction
*>(pCurrAct
);
2173 // crop bitmap to given source rectangle (no
2174 // need to copy and convert the whole bitmap)
2175 ::Bitmap
aBmp( pAct
->GetBitmap() );
2176 const ::tools::Rectangle
aCropRect( pAct
->GetSrcPoint(),
2177 pAct
->GetSrcSize() );
2178 aBmp
.Crop( aCropRect
);
2180 std::shared_ptr
<Action
> pBmpAction(
2181 internal::BitmapActionFactory::createBitmapAction(
2183 rStates
.getState().mapModeTransform
*
2184 vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2185 rStates
.getState().mapModeTransform
*
2186 vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2188 rStates
.getState() ) );
2192 maActions
.emplace_back(
2194 io_rCurrActionIndex
);
2196 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2201 case MetaActionType::BMPEX
:
2203 MetaBmpExAction
* pAct
= static_cast<MetaBmpExAction
*>(pCurrAct
);
2205 std::shared_ptr
<Action
> pBmpAction(
2206 internal::BitmapActionFactory::createBitmapAction(
2207 pAct
->GetBitmapEx(),
2208 rStates
.getState().mapModeTransform
*
2209 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2211 rStates
.getState() ) );
2215 maActions
.emplace_back(
2217 io_rCurrActionIndex
);
2219 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2224 case MetaActionType::BMPEXSCALE
:
2226 MetaBmpExScaleAction
* pAct
= static_cast<MetaBmpExScaleAction
*>(pCurrAct
);
2228 std::shared_ptr
<Action
> pBmpAction(
2229 internal::BitmapActionFactory::createBitmapAction(
2230 pAct
->GetBitmapEx(),
2231 rStates
.getState().mapModeTransform
*
2232 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2233 rStates
.getState().mapModeTransform
*
2234 vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2236 rStates
.getState() ) );
2240 maActions
.emplace_back(
2242 io_rCurrActionIndex
);
2244 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2249 case MetaActionType::BMPEXSCALEPART
:
2251 MetaBmpExScalePartAction
* pAct
= static_cast<MetaBmpExScalePartAction
*>(pCurrAct
);
2253 // crop bitmap to given source rectangle (no
2254 // need to copy and convert the whole bitmap)
2255 BitmapEx
aBmp( pAct
->GetBitmapEx() );
2256 const ::tools::Rectangle
aCropRect( pAct
->GetSrcPoint(),
2257 pAct
->GetSrcSize() );
2258 aBmp
.Crop( aCropRect
);
2260 std::shared_ptr
<Action
> pBmpAction(
2261 internal::BitmapActionFactory::createBitmapAction(
2263 rStates
.getState().mapModeTransform
*
2264 vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2265 rStates
.getState().mapModeTransform
*
2266 vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2268 rStates
.getState() ) );
2272 maActions
.emplace_back(
2274 io_rCurrActionIndex
);
2276 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2281 case MetaActionType::MASK
:
2283 MetaMaskAction
* pAct
= static_cast<MetaMaskAction
*>(pCurrAct
);
2285 // create masked BitmapEx right here, as the
2286 // canvas does not provide equivalent
2288 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2289 pAct
->GetColor() ));
2291 std::shared_ptr
<Action
> pBmpAction(
2292 internal::BitmapActionFactory::createBitmapAction(
2294 rStates
.getState().mapModeTransform
*
2295 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2297 rStates
.getState() ) );
2301 maActions
.emplace_back(
2303 io_rCurrActionIndex
);
2305 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2310 case MetaActionType::MASKSCALE
:
2312 MetaMaskScaleAction
* pAct
= static_cast<MetaMaskScaleAction
*>(pCurrAct
);
2314 // create masked BitmapEx right here, as the
2315 // canvas does not provide equivalent
2317 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2318 pAct
->GetColor() ));
2320 std::shared_ptr
<Action
> pBmpAction(
2321 internal::BitmapActionFactory::createBitmapAction(
2323 rStates
.getState().mapModeTransform
*
2324 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2325 rStates
.getState().mapModeTransform
*
2326 vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2328 rStates
.getState() ) );
2332 maActions
.emplace_back(
2334 io_rCurrActionIndex
);
2336 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2341 case MetaActionType::MASKSCALEPART
:
2343 MetaMaskScalePartAction
* pAct
= static_cast<MetaMaskScalePartAction
*>(pCurrAct
);
2345 // create masked BitmapEx right here, as the
2346 // canvas does not provide equivalent
2348 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2349 pAct
->GetColor() ));
2351 // crop bitmap to given source rectangle (no
2352 // need to copy and convert the whole bitmap)
2353 const ::tools::Rectangle
aCropRect( pAct
->GetSrcPoint(),
2354 pAct
->GetSrcSize() );
2355 aBmp
.Crop( aCropRect
);
2357 std::shared_ptr
<Action
> pBmpAction(
2358 internal::BitmapActionFactory::createBitmapAction(
2360 rStates
.getState().mapModeTransform
*
2361 vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2362 rStates
.getState().mapModeTransform
*
2363 vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2365 rStates
.getState() ) );
2369 maActions
.emplace_back(
2371 io_rCurrActionIndex
);
2373 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2378 case MetaActionType::GRADIENTEX
:
2379 // TODO(F1): use native Canvas gradients here
2380 // action is ignored here, because redundant to MetaActionType::GRADIENT
2383 case MetaActionType::WALLPAPER
:
2387 case MetaActionType::Transparent
:
2389 const OutDevState
& rState( rStates
.getState() );
2390 if( rState
.lineColor
.hasElements() ||
2391 rState
.fillColor
.hasElements() )
2393 MetaTransparentAction
* pAct
= static_cast<MetaTransparentAction
*>(pCurrAct
);
2394 ::basegfx::B2DPolyPolygon
aPoly( pAct
->GetPolyPolygon().getB2DPolyPolygon() );
2395 aPoly
.transform( rState
.mapModeTransform
);
2397 std::shared_ptr
<Action
> pPolyAction(
2398 internal::PolyPolyActionFactory::createPolyPolyAction(
2402 pAct
->GetTransparence() ) );
2406 maActions
.emplace_back(
2408 io_rCurrActionIndex
);
2410 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2416 case MetaActionType::FLOATTRANSPARENT
:
2418 MetaFloatTransparentAction
* pAct
= static_cast<MetaFloatTransparentAction
*>(pCurrAct
);
2420 internal::MtfAutoPtr
pMtf(
2421 new ::GDIMetaFile( pAct
->GetGDIMetaFile() ) );
2423 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2424 internal::GradientAutoPtr
pGradient(
2425 new Gradient( pAct
->GetGradient() ) );
2427 DBG_TESTSOLARMUTEX();
2429 std::shared_ptr
<Action
> pFloatTransAction(
2430 internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2432 std::move(pGradient
),
2433 rStates
.getState().mapModeTransform
*
2434 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2435 rStates
.getState().mapModeTransform
*
2436 vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2438 rStates
.getState() ) );
2440 if( pFloatTransAction
)
2442 maActions
.emplace_back(
2444 io_rCurrActionIndex
);
2446 io_rCurrActionIndex
+= pFloatTransAction
->getActionCount()-1;
2451 case MetaActionType::TEXT
:
2453 MetaTextAction
* pAct
= static_cast<MetaTextAction
*>(pCurrAct
);
2454 OUString sText
= pAct
->GetText();
2456 if (rVDev
.GetDigitLanguage())
2457 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2459 const sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
2468 bSubsettableActions
);
2472 case MetaActionType::TEXTARRAY
:
2474 MetaTextArrayAction
* pAct
= static_cast<MetaTextArrayAction
*>(pCurrAct
);
2475 OUString sText
= pAct
->GetText();
2477 if (rVDev
.GetDigitLanguage())
2478 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2480 const sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
2489 bSubsettableActions
);
2493 case MetaActionType::TEXTLINE
:
2495 MetaTextLineAction
* pAct
= static_cast<MetaTextLineAction
*>(pCurrAct
);
2497 const OutDevState
& rState( rStates
.getState() );
2498 const ::Size
aBaselineOffset( tools::getBaselineOffset( rState
,
2500 const ::basegfx::B2DSize
aSize( rState
.mapModeTransform
*
2501 ::basegfx::B2DSize(pAct
->GetWidth(),
2504 std::shared_ptr
<Action
> pPolyAction(
2505 PolyPolyActionFactory::createPolyPolyAction(
2506 tools::createTextLinesPolyPolygon(
2507 rState
.mapModeTransform
*
2508 ::basegfx::B2DPoint(
2509 vcl::unotools::b2DPointFromPoint(pAct
->GetStartPoint()) +
2510 vcl::unotools::b2DSizeFromSize(aBaselineOffset
)),
2512 tools::createTextLineInfo( rVDev
,
2519 maActions
.emplace_back(
2521 io_rCurrActionIndex
);
2523 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2528 case MetaActionType::TEXTRECT
:
2530 MetaTextRectAction
* pAct
= static_cast<MetaTextRectAction
*>(pCurrAct
);
2532 rStates
.pushState(PushFlags::ALL
);
2534 // use the VDev to break up the text rect
2535 // action into readily formatted lines
2536 GDIMetaFile aTmpMtf
;
2537 rVDev
.AddTextRectActions( pAct
->GetRect(),
2542 createActions( aTmpMtf
,
2544 bSubsettableActions
);
2551 case MetaActionType::STRETCHTEXT
:
2553 MetaStretchTextAction
* pAct
= static_cast<MetaStretchTextAction
*>(pCurrAct
);
2554 OUString sText
= pAct
->GetText();
2556 if (rVDev
.GetDigitLanguage())
2557 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2559 const sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
2561 // #i70897# Nothing to do, actually...
2565 // have to fit the text into the given
2566 // width. This is achieved by internally
2567 // generating a DX array, and uniformly
2568 // distributing the excess/insufficient width
2569 // to every logical character.
2570 std::unique_ptr
< ::tools::Long
[]> pDXArray( new ::tools::Long
[nLen
] );
2572 rVDev
.GetTextArray( pAct
->GetText(), pDXArray
.get(),
2573 pAct
->GetIndex(), pAct
->GetLen() );
2575 const sal_Int32
nWidthDifference( pAct
->GetWidth() - pDXArray
[ nLen
-1 ] );
2577 // Last entry of pDXArray contains total width of the text
2578 ::tools::Long
* p
= pDXArray
.get();
2579 for (sal_Int32 i
= 1; i
<= nLen
; ++i
)
2581 // calc ratio for every array entry, to
2582 // distribute rounding errors 'evenly'
2583 // across the characters. Note that each
2584 // entry represents the 'end' position of
2585 // the corresponding character, thus, we
2586 // let i run from 1 to nLen.
2587 *p
++ += static_cast<::tools::Long
>(i
)*nWidthDifference
/nLen
;
2597 bSubsettableActions
);
2602 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
2606 // increment action index (each mtf action counts _at
2607 // least_ one. Some count for more, therefore,
2608 // io_rCurrActionIndex is sometimes incremented by
2609 // pAct->getActionCount()-1 above, the -1 being the
2610 // correction for the unconditional increment here).
2611 ++io_rCurrActionIndex
;
2618 class ActionRenderer
2621 explicit ActionRenderer( const ::basegfx::B2DHomMatrix
& rTransformation
) :
2622 maTransformation( rTransformation
),
2632 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2634 // ANDing the result. We want to fail if at least
2635 // one action failed.
2636 mbRet
&= rAction
.mpAction
->render( maTransformation
);
2639 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2640 const Action::Subset
& rSubset
)
2642 // ANDing the result. We want to fail if at least
2643 // one action failed.
2644 mbRet
&= rAction
.mpAction
->renderSubset( maTransformation
,
2649 ::basegfx::B2DHomMatrix maTransformation
;
2656 explicit AreaQuery( const ::basegfx::B2DHomMatrix
& rTransformation
) :
2657 maTransformation( rTransformation
),
2662 static bool result()
2664 return true; // nothing can fail here
2667 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2669 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
) );
2672 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2673 const Action::Subset
& rSubset
)
2675 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
,
2679 const ::basegfx::B2DRange
& getBounds() const
2685 ::basegfx::B2DHomMatrix maTransformation
;
2686 ::basegfx::B2DRange maBounds
;
2689 // Doing that via inline class. Compilers tend to not inline free
2691 struct UpperBoundActionIndexComparator
2693 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rLHS
,
2694 const ::cppcanvas::internal::ImplRenderer::MtfAction
& rRHS
)
2696 const sal_Int32
nLHSCount( rLHS
.mpAction
?
2697 rLHS
.mpAction
->getActionCount() : 0 );
2698 const sal_Int32
nRHSCount( rRHS
.mpAction
?
2699 rRHS
.mpAction
->getActionCount() : 0 );
2701 // compare end of action range, to have an action selected
2702 // by lower_bound even if the requested index points in
2703 // the middle of the action's range
2704 return rLHS
.mnOrigIndex
+ nLHSCount
< rRHS
.mnOrigIndex
+ nRHSCount
;
2708 /** Algorithm to apply given functor to a subset range
2712 Functor to call for each element of the subset
2713 range. Must provide the following method signatures:
2714 bool result() (returning false if operation failed)
2717 template< typename Functor
> bool
2718 forSubsetRange( Functor
& rFunctor
,
2719 ImplRenderer::ActionVector::const_iterator aRangeBegin
,
2720 const ImplRenderer::ActionVector::const_iterator
& aRangeEnd
,
2721 sal_Int32 nStartIndex
,
2722 sal_Int32 nEndIndex
,
2723 const ImplRenderer::ActionVector::const_iterator
& rEnd
)
2725 if( aRangeBegin
== aRangeEnd
)
2727 // only a single action. Setup subset, and call functor
2728 Action::Subset aSubset
;
2729 aSubset
.mnSubsetBegin
= std::max( sal_Int32( 0 ),
2730 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2731 aSubset
.mnSubsetEnd
= std::min( aRangeBegin
->mpAction
->getActionCount(),
2732 nEndIndex
- aRangeBegin
->mnOrigIndex
);
2734 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2735 "ImplRenderer::forSubsetRange(): Invalid indices" );
2737 rFunctor( *aRangeBegin
, aSubset
);
2741 // more than one action.
2743 // render partial first, full intermediate, and
2744 // partial last action
2745 Action::Subset aSubset
;
2746 aSubset
.mnSubsetBegin
= std::max( sal_Int32( 0 ),
2747 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2748 aSubset
.mnSubsetEnd
= aRangeBegin
->mpAction
->getActionCount();
2750 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2751 "ImplRenderer::forSubsetRange(): Invalid indices" );
2753 rFunctor( *aRangeBegin
, aSubset
);
2755 // first action rendered, skip to next
2758 // render full middle actions
2759 while( aRangeBegin
!= aRangeEnd
)
2760 rFunctor( *aRangeBegin
++ );
2762 if( aRangeEnd
== rEnd
||
2763 aRangeEnd
->mnOrigIndex
> nEndIndex
)
2765 // aRangeEnd denotes end of action vector,
2769 // nEndIndex references something _after_
2770 // aRangeBegin, but _before_ aRangeEnd
2772 // either way: no partial action left
2773 return rFunctor
.result();
2776 aSubset
.mnSubsetBegin
= 0;
2777 aSubset
.mnSubsetEnd
= nEndIndex
- aRangeEnd
->mnOrigIndex
;
2779 ENSURE_OR_RETURN_FALSE(aSubset
.mnSubsetEnd
>= 0,
2780 "ImplRenderer::forSubsetRange(): Invalid indices" );
2782 rFunctor( *aRangeEnd
, aSubset
);
2785 return rFunctor
.result();
2789 bool ImplRenderer::getSubsetIndices( sal_Int32
& io_rStartIndex
,
2790 sal_Int32
& io_rEndIndex
,
2791 ActionVector::const_iterator
& o_rRangeBegin
,
2792 ActionVector::const_iterator
& o_rRangeEnd
) const
2794 ENSURE_OR_RETURN_FALSE( io_rStartIndex
<=io_rEndIndex
,
2795 "ImplRenderer::getSubsetIndices(): invalid action range" );
2797 ENSURE_OR_RETURN_FALSE( !maActions
.empty(),
2798 "ImplRenderer::getSubsetIndices(): no actions to render" );
2800 const sal_Int32
nMinActionIndex( maActions
.front().mnOrigIndex
);
2801 const sal_Int32
nMaxActionIndex( maActions
.back().mnOrigIndex
+
2802 maActions
.back().mpAction
->getActionCount() );
2804 // clip given range to permissible values (there might be
2805 // ranges before and behind the valid indices)
2806 io_rStartIndex
= std::max( nMinActionIndex
,
2808 io_rEndIndex
= std::min( nMaxActionIndex
,
2811 if( io_rStartIndex
== io_rEndIndex
||
2812 io_rStartIndex
> io_rEndIndex
)
2814 // empty range, don't render anything. The second
2815 // condition e.g. happens if the requested range lies
2816 // fully before or behind the valid action indices.
2821 const ActionVector::const_iterator
aBegin( maActions
.begin() );
2822 const ActionVector::const_iterator
aEnd( maActions
.end() );
2825 // find start and end action
2826 // =========================
2827 o_rRangeBegin
= std::lower_bound( aBegin
, aEnd
,
2828 MtfAction( std::shared_ptr
<Action
>(), io_rStartIndex
),
2829 UpperBoundActionIndexComparator() );
2830 o_rRangeEnd
= std::lower_bound( aBegin
, aEnd
,
2831 MtfAction( std::shared_ptr
<Action
>(), io_rEndIndex
),
2832 UpperBoundActionIndexComparator() );
2840 ImplRenderer::ImplRenderer( const CanvasSharedPtr
& rCanvas
,
2841 const GDIMetaFile
& rMtf
,
2842 const Parameters
& rParams
)
2843 : CanvasGraphicHelper(rCanvas
)
2854 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2856 OSL_ENSURE( rCanvas
&& rCanvas
->getUNOCanvas().is(),
2857 "ImplRenderer::ImplRenderer(): Invalid canvas" );
2858 OSL_ENSURE( rCanvas
->getUNOCanvas()->getDevice().is(),
2859 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2861 // make sure canvas and graphic device are valid; action
2862 // creation don't check that every time
2864 !rCanvas
->getUNOCanvas().is() ||
2865 !rCanvas
->getUNOCanvas()->getDevice().is() )
2867 // leave actions empty
2871 VectorOfOutDevStates aStateStack
;
2873 ScopedVclPtrInstance
< VirtualDevice
> aVDev
;
2874 aVDev
->EnableOutput( false );
2876 // Setup VDev for state tracking and mapping
2877 // =========================================
2879 aVDev
->SetMapMode( rMtf
.GetPrefMapMode() );
2881 const Size
aMtfSize( rMtf
.GetPrefSize() );
2882 const Size
aMtfSizePixPre( aVDev
->LogicToPixel( aMtfSize
,
2883 rMtf
.GetPrefMapMode() ) );
2885 // #i44110# correct null-sized output - there are shapes
2886 // which have zero size in at least one dimension
2887 // Remark the 1L cannot be replaced, that would cause max to compare long/int
2888 const Size
aMtfSizePix( std::max( aMtfSizePixPre
.Width(), ::tools::Long(1) ),
2889 std::max( aMtfSizePixPre
.Height(), ::tools::Long(1) ) );
2891 sal_Int32
nCurrActions(0);
2892 ActionFactoryParameters
aParms(aStateStack
,
2899 aStateStack
.clearStateStack();
2901 // Setup local state, such that the metafile renders
2902 // itself into a one-by-one square at the origin for
2903 // identity view and render transformations
2904 aStateStack
.getState().transform
.scale( 1.0 / aMtfSizePix
.Width(),
2905 1.0 / aMtfSizePix
.Height() );
2907 tools::calcLogic2PixelAffineTransform( aStateStack
.getState().mapModeTransform
,
2911 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2912 // setup default text color to black
2914 rState
.textFillColor
=
2915 rState
.textOverlineColor
=
2916 rState
.textLineColor
= tools::intSRGBAToDoubleSequence( 0x000000FF );
2919 // apply overrides from the Parameters struct
2920 if( rParams
.maFillColor
)
2922 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2923 rState
.isFillColorSet
= true;
2924 rState
.fillColor
= tools::intSRGBAToDoubleSequence( *rParams
.maFillColor
);
2926 if( rParams
.maLineColor
)
2928 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2929 rState
.isLineColorSet
= true;
2930 rState
.lineColor
= tools::intSRGBAToDoubleSequence( *rParams
.maLineColor
);
2932 if( rParams
.maTextColor
)
2934 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2935 rState
.isTextFillColorSet
= true;
2936 rState
.isTextOverlineColorSet
= true;
2937 rState
.isTextLineColorSet
= true;
2939 rState
.textFillColor
=
2940 rState
.textOverlineColor
=
2941 rState
.textLineColor
= tools::intSRGBAToDoubleSequence( *rParams
.maTextColor
);
2943 if( rParams
.maFontName
||
2944 rParams
.maFontWeight
||
2945 rParams
.maFontLetterForm
||
2946 rParams
.maFontUnderline
)
2948 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2950 rState
.xFont
= createFont( rState
.fontRotation
,
2951 vcl::Font(), // default font
2956 createActions( const_cast<GDIMetaFile
&>(rMtf
), // HACK(Q2):
2965 true // TODO(P1): make subsettability configurable
2969 ImplRenderer::~ImplRenderer()
2973 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex
,
2974 sal_Int32 nEndIndex
) const
2976 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::drawSubset()" );
2978 ActionVector::const_iterator aRangeBegin
;
2979 ActionVector::const_iterator aRangeEnd
;
2983 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
2984 aRangeBegin
, aRangeEnd
) )
2985 return true; // nothing to render (but _that_ was successful)
2987 // now, aRangeBegin references the action in which the
2988 // subset rendering must start, and aRangeEnd references
2989 // the action in which the subset rendering must end (it
2990 // might also end right at the start of the referenced
2991 // action, such that zero of that action needs to be
2995 // render subset of actions
2996 // ========================
2998 ::basegfx::B2DHomMatrix aMatrix
;
2999 ::canvas::tools::getRenderStateTransform( aMatrix
,
3002 ActionRenderer
aRenderer( aMatrix
);
3004 return forSubsetRange( aRenderer
,
3011 catch( uno::Exception
& )
3013 DBG_UNHANDLED_EXCEPTION("cppcanvas.emf");
3014 // convert error to return value
3019 ::basegfx::B2DRange
ImplRenderer::getSubsetArea( sal_Int32 nStartIndex
,
3020 sal_Int32 nEndIndex
) const
3022 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3024 ActionVector::const_iterator aRangeBegin
;
3025 ActionVector::const_iterator aRangeEnd
;
3027 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
3028 aRangeBegin
, aRangeEnd
) )
3029 return ::basegfx::B2DRange(); // nothing to render -> empty range
3031 // now, aRangeBegin references the action in which the
3032 // subset querying must start, and aRangeEnd references
3033 // the action in which the subset querying must end (it
3034 // might also end right at the start of the referenced
3035 // action, such that zero of that action needs to be
3039 // query bounds for subset of actions
3040 // ==================================
3042 ::basegfx::B2DHomMatrix aMatrix
;
3043 ::canvas::tools::getRenderStateTransform( aMatrix
,
3046 AreaQuery
aQuery( aMatrix
);
3047 forSubsetRange( aQuery
,
3054 return aQuery
.getBounds();
3057 bool ImplRenderer::draw() const
3059 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::draw()" );
3061 ::basegfx::B2DHomMatrix aMatrix
;
3062 ::canvas::tools::getRenderStateTransform( aMatrix
,
3067 return std::for_each( maActions
.begin(), maActions
.end(), ActionRenderer( aMatrix
) ).result();
3069 catch( uno::Exception
& )
3071 DBG_UNHANDLED_EXCEPTION( "cppcanvas.emf");
3077 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */