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 <osl/mutex.hxx>
22 #include <vcl/svapp.hxx>
23 #include <comphelper/sequence.hxx>
24 #include <comphelper/anytostring.hxx>
25 #include <cppuhelper/exc_hlp.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/geometry/RealPoint2D.hpp>
31 #include <com/sun/star/rendering/PanoseProportion.hpp>
32 #include <com/sun/star/rendering/ViewState.hpp>
33 #include <com/sun/star/rendering/RenderState.hpp>
34 #include <com/sun/star/rendering/XCanvasFont.hpp>
35 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
36 #include <com/sun/star/rendering/XCanvas.hpp>
37 #include <com/sun/star/rendering/PathCapType.hpp>
38 #include <com/sun/star/rendering/PathJoinType.hpp>
39 #include <basegfx/tools/canvastools.hxx>
40 #include <basegfx/tools/gradienttools.hxx>
41 #include <basegfx/numeric/ftools.hxx>
42 #include <basegfx/polygon/b2dpolypolygontools.hxx>
43 #include <basegfx/polygon/b2dpolygontools.hxx>
44 #include <basegfx/polygon/b2dpolygon.hxx>
45 #include <basegfx/polygon/b2dpolypolygon.hxx>
46 #include <basegfx/matrix/b2dhommatrix.hxx>
47 #include <basegfx/vector/b2dsize.hxx>
48 #include <basegfx/range/b2drectangle.hxx>
49 #include <basegfx/point/b2dpoint.hxx>
50 #include <basegfx/tuple/b2dtuple.hxx>
51 #include <basegfx/polygon/b2dpolygonclipper.hxx>
52 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
53 #include <canvas/canvastools.hxx>
54 #include <vcl/canvastools.hxx>
55 #include <vcl/salbtype.hxx>
56 #include <vcl/gdimtf.hxx>
57 #include <vcl/metaact.hxx>
58 #include <vcl/virdev.hxx>
59 #include <vcl/metric.hxx>
60 #include <vcl/graphictools.hxx>
61 #include <tools/poly.hxx>
62 #include <i18nlangtag/languagetag.hxx>
63 #include <implrenderer.hxx>
65 #include <outdevstate.hxx>
67 #include <bitmapaction.hxx>
68 #include <lineaction.hxx>
69 #include <pointaction.hxx>
70 #include <polypolyaction.hxx>
71 #include <textaction.hxx>
72 #include <transparencygroupaction.hxx>
78 #include "mtftools.hxx"
79 #include <basegfx/matrix/b2dhommatrixtools.hxx>
81 using namespace ::com::sun::star
;
84 // free support functions
85 // ======================
88 template < class MetaActionType
> void setStateColor( MetaActionType
* pAct
,
90 uno::Sequence
< double >& rColorSequence
,
91 const cppcanvas::CanvasSharedPtr
& rCanvas
)
93 rIsColorSet
= pAct
->IsSetting();
96 ::Color
aColor( pAct
->GetColor() );
98 // force alpha part of color to
99 // opaque. transparent painting is done
100 // explicitly via MetaActionType::Transparent
101 aColor
.SetTransparency(0);
102 //aColor.SetTransparency(128);
104 rColorSequence
= vcl::unotools::colorToDoubleSequence(
106 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
110 void setupStrokeAttributes( rendering::StrokeAttributes
& o_rStrokeAttributes
,
111 const ::cppcanvas::internal::ActionFactoryParameters
& rParms
,
112 const LineInfo
& rLineInfo
)
114 const ::basegfx::B2DSize
aWidth( rLineInfo
.GetWidth(), 0 );
115 o_rStrokeAttributes
.StrokeWidth
=
116 (rParms
.mrStates
.getState().mapModeTransform
* aWidth
).getX();
118 // setup reasonable defaults
119 o_rStrokeAttributes
.MiterLimit
= 15.0; // 1.0 was no good default; GDI+'s limit is 10.0, our's is 15.0
120 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::BUTT
;
121 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::BUTT
;
123 switch (rLineInfo
.GetLineJoin())
125 case basegfx::B2DLineJoin::NONE
:
126 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::NONE
;
128 case basegfx::B2DLineJoin::Bevel
:
129 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::BEVEL
;
131 case basegfx::B2DLineJoin::Miter
:
132 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::MITER
;
134 case basegfx::B2DLineJoin::Round
:
135 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::ROUND
;
139 switch(rLineInfo
.GetLineCap())
141 default: /* css::drawing::LineCap_BUTT */
143 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::BUTT
;
144 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::BUTT
;
147 case css::drawing::LineCap_ROUND
:
149 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::ROUND
;
150 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::ROUND
;
153 case css::drawing::LineCap_SQUARE
:
155 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::SQUARE
;
156 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::SQUARE
;
161 if( LINE_DASH
== rLineInfo
.GetStyle() )
163 const ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
165 // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing.
167 // interpret dash info only if explicitly enabled as
169 const ::basegfx::B2DSize
aDistance( rLineInfo
.GetDistance(), 0 );
170 const double nDistance( (rState
.mapModeTransform
* aDistance
).getX() );
172 const ::basegfx::B2DSize
aDashLen( rLineInfo
.GetDashLen(), 0 );
173 const double nDashLen( (rState
.mapModeTransform
* aDashLen
).getX() );
175 const ::basegfx::B2DSize
aDotLen( rLineInfo
.GetDotLen(), 0 );
176 const double nDotLen( (rState
.mapModeTransform
* aDotLen
).getX() );
178 const sal_Int32
nNumArryEntries( 2*rLineInfo
.GetDashCount() +
179 2*rLineInfo
.GetDotCount() );
181 o_rStrokeAttributes
.DashArray
.realloc( nNumArryEntries
);
182 double* pDashArray
= o_rStrokeAttributes
.DashArray
.getArray();
185 // iteratively fill dash array, first with dashes, then
189 sal_Int32 nCurrEntry
=0;
191 for( sal_Int32 i
=0; i
<rLineInfo
.GetDashCount(); ++i
)
193 pDashArray
[nCurrEntry
++] = nDashLen
;
194 pDashArray
[nCurrEntry
++] = nDistance
;
196 for( sal_Int32 i
=0; i
<rLineInfo
.GetDotCount(); ++i
)
198 pDashArray
[nCurrEntry
++] = nDotLen
;
199 pDashArray
[nCurrEntry
++] = nDistance
;
205 /** Create masked BitmapEx, where the white areas of rBitmap are
206 transparent, and the other appear in rMaskColor.
208 BitmapEx
createMaskBmpEx( const Bitmap
& rBitmap
,
209 const ::Color
& rMaskColor
)
211 const ::Color
aWhite( COL_WHITE
);
212 BitmapPalette
aBiLevelPalette(2);
213 aBiLevelPalette
[0] = aWhite
;
214 aBiLevelPalette
[1] = rMaskColor
;
216 Bitmap
aMask( rBitmap
.CreateMask( aWhite
));
217 Bitmap
aSolid( rBitmap
.GetSizePixel(),
220 aSolid
.Erase( rMaskColor
);
222 return BitmapEx( aSolid
, aMask
);
225 OUString
convertToLocalizedNumerals(const OUString
& rStr
,
226 LanguageType eTextLanguage
)
228 OUStringBuffer
aBuf(rStr
);
229 for (sal_Int32 i
= 0; i
< aBuf
.getLength(); ++i
)
231 sal_Unicode nChar
= aBuf
[i
];
232 if (nChar
>= '0' && nChar
<= '9')
233 aBuf
[i
] = GetLocalizedChar(nChar
, eTextLanguage
);
235 return aBuf
.makeStringAndClear();
243 // state stack manipulators
245 void VectorOfOutDevStates::clearStateStack()
248 const OutDevState aDefaultState
;
249 m_aStates
.push_back(aDefaultState
);
252 OutDevState
& VectorOfOutDevStates::getState()
254 return m_aStates
.back();
257 const OutDevState
& VectorOfOutDevStates::getState() const
259 return m_aStates
.back();
262 void VectorOfOutDevStates::pushState(PushFlags nFlags
)
264 m_aStates
.push_back( getState() );
265 getState().pushFlags
= nFlags
;
268 void VectorOfOutDevStates::popState()
270 if( getState().pushFlags
!= PushFlags::ALL
)
272 // a state is pushed which is incomplete, i.e. does not
273 // restore everything to the previous stack level when
275 // That means, we take the old state, and restore every
276 // OutDevState member whose flag is set, from the new to the
277 // old state. Then the new state gets overwritten by the
280 // preset to-be-calculated new state with old state
281 OutDevState
aCalculatedNewState( getState() );
283 // selectively copy to-be-restored content over saved old
285 m_aStates
.pop_back();
287 const OutDevState
& rNewState( getState() );
289 if( (aCalculatedNewState
.pushFlags
& PushFlags::LINECOLOR
) )
291 aCalculatedNewState
.lineColor
= rNewState
.lineColor
;
292 aCalculatedNewState
.isLineColorSet
= rNewState
.isLineColorSet
;
295 if( (aCalculatedNewState
.pushFlags
& PushFlags::FILLCOLOR
) )
297 aCalculatedNewState
.fillColor
= rNewState
.fillColor
;
298 aCalculatedNewState
.isFillColorSet
= rNewState
.isFillColorSet
;
301 if( (aCalculatedNewState
.pushFlags
& PushFlags::FONT
) )
303 aCalculatedNewState
.xFont
= rNewState
.xFont
;
304 aCalculatedNewState
.fontRotation
= rNewState
.fontRotation
;
305 aCalculatedNewState
.textReliefStyle
= rNewState
.textReliefStyle
;
306 aCalculatedNewState
.textOverlineStyle
= rNewState
.textOverlineStyle
;
307 aCalculatedNewState
.textUnderlineStyle
= rNewState
.textUnderlineStyle
;
308 aCalculatedNewState
.textStrikeoutStyle
= rNewState
.textStrikeoutStyle
;
309 aCalculatedNewState
.textEmphasisMarkStyle
= rNewState
.textEmphasisMarkStyle
;
310 aCalculatedNewState
.isTextEffectShadowSet
= rNewState
.isTextEffectShadowSet
;
311 aCalculatedNewState
.isTextWordUnderlineSet
= rNewState
.isTextWordUnderlineSet
;
312 aCalculatedNewState
.isTextOutlineModeSet
= rNewState
.isTextOutlineModeSet
;
315 if( (aCalculatedNewState
.pushFlags
& PushFlags::TEXTCOLOR
) )
317 aCalculatedNewState
.textColor
= rNewState
.textColor
;
320 if( (aCalculatedNewState
.pushFlags
& PushFlags::MAPMODE
) )
322 aCalculatedNewState
.mapModeTransform
= rNewState
.mapModeTransform
;
325 if( (aCalculatedNewState
.pushFlags
& PushFlags::CLIPREGION
) )
327 aCalculatedNewState
.clip
= rNewState
.clip
;
328 aCalculatedNewState
.clipRect
= rNewState
.clipRect
;
329 aCalculatedNewState
.xClipPoly
= rNewState
.xClipPoly
;
332 // TODO(F2): Raster ops NYI
333 // if( (aCalculatedNewState.pushFlags & PushFlags::RASTEROP) )
337 if( (aCalculatedNewState
.pushFlags
& PushFlags::TEXTFILLCOLOR
) )
339 aCalculatedNewState
.textFillColor
= rNewState
.textFillColor
;
340 aCalculatedNewState
.isTextFillColorSet
= rNewState
.isTextFillColorSet
;
343 if( (aCalculatedNewState
.pushFlags
& PushFlags::TEXTALIGN
) )
345 aCalculatedNewState
.textReferencePoint
= rNewState
.textReferencePoint
;
348 // TODO(F1): Refpoint handling NYI
349 // if( (aCalculatedNewState.pushFlags & PushFlags::REFPOINT) )
353 if( (aCalculatedNewState
.pushFlags
& PushFlags::TEXTLINECOLOR
) )
355 aCalculatedNewState
.textLineColor
= rNewState
.textLineColor
;
356 aCalculatedNewState
.isTextLineColorSet
= rNewState
.isTextLineColorSet
;
359 if( (aCalculatedNewState
.pushFlags
& PushFlags::TEXTLAYOUTMODE
) )
361 aCalculatedNewState
.textAlignment
= rNewState
.textAlignment
;
362 aCalculatedNewState
.textDirection
= rNewState
.textDirection
;
365 // TODO(F2): Text language handling NYI
366 // if( (aCalculatedNewState.pushFlags & PushFlags::TEXTLANGUAGE) )
370 // always copy push mode
371 aCalculatedNewState
.pushFlags
= rNewState
.pushFlags
;
374 getState() = aCalculatedNewState
;
378 m_aStates
.pop_back();
382 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon
& rPolyPoly
,
383 const ActionFactoryParameters
& rParms
)
385 const OutDevState
& rState( rParms
.mrStates
.getState() );
386 if( (!rState
.isLineColorSet
&&
387 !rState
.isFillColorSet
) ||
388 (rState
.lineColor
.getLength() == 0 &&
389 rState
.fillColor
.getLength() == 0) )
394 ActionSharedPtr
pPolyAction(
395 internal::PolyPolyActionFactory::createPolyPolyAction(
396 rPolyPoly
, rParms
.mrCanvas
, rState
) );
403 rParms
.mrCurrActionIndex
) );
405 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
411 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon
& rPoly
,
412 const ActionFactoryParameters
& rParms
)
414 return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly
),
418 void ImplRenderer::skipContent( GDIMetaFile
& rMtf
,
419 const char* pCommentString
,
420 sal_Int32
& io_rCurrActionIndex
)
422 ENSURE_OR_THROW( pCommentString
,
423 "ImplRenderer::skipContent(): NULL string given" );
425 MetaAction
* pCurrAct
;
426 while( (pCurrAct
=rMtf
.NextAction()) != nullptr )
428 // increment action index, we've skipped an action.
429 ++io_rCurrActionIndex
;
431 if( pCurrAct
->GetType() == MetaActionType::COMMENT
&&
432 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase(
435 // requested comment found, done
444 bool ImplRenderer::isActionContained( GDIMetaFile
& rMtf
,
445 const char* pCommentString
,
446 MetaActionType nType
)
448 ENSURE_OR_THROW( pCommentString
,
449 "ImplRenderer::isActionContained(): NULL string given" );
453 // at least _one_ call to GDIMetaFile::NextAction() is
457 MetaAction
* pCurrAct
;
458 while( (pCurrAct
=rMtf
.NextAction()) != nullptr )
460 if( pCurrAct
->GetType() == nType
)
462 bRet
= true; // action type found
466 if( pCurrAct
->GetType() == MetaActionType::COMMENT
&&
467 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase(
470 // delimiting end comment found, done
471 bRet
= false; // not yet found
478 // rewind metafile to previous position (this method must
479 // 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( (sal_uInt8
)(aVCLStartColor
.GetRed() * nStartIntensity
/ 100) );
534 aVCLStartColor
.SetGreen( (sal_uInt8
)(aVCLStartColor
.GetGreen() * nStartIntensity
/ 100) );
535 aVCLStartColor
.SetBlue( (sal_uInt8
)(aVCLStartColor
.GetBlue() * nStartIntensity
/ 100) );
537 const sal_uInt16
nEndIntensity( rGradient
.GetEndIntensity() );
538 ::Color
aVCLEndColor( rGradient
.GetEndColor() );
539 aVCLEndColor
.SetRed( (sal_uInt8
)(aVCLEndColor
.GetRed() * nEndIntensity
/ 100) );
540 aVCLEndColor
.SetGreen( (sal_uInt8
)(aVCLEndColor
.GetGreen() * nEndIntensity
/ 100) );
541 aVCLEndColor
.SetBlue( (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(2);
553 uno::Sequence
< double > aStops(2);
555 if( rGradient
.GetStyle() == GradientStyle_AXIAL
)
564 aColors
[0] = aEndColor
;
565 aColors
[1] = aStartColor
;
566 aColors
[2] = aEndColor
;
573 aColors
[0] = aStartColor
;
574 aColors
[1] = aEndColor
;
577 const ::basegfx::B2DRectangle
aBounds(
578 ::basegfx::tools::getRange(aDevicePoly
) );
579 const ::basegfx::B2DVector
aOffset(
580 rGradient
.GetOfsX() / 100.0,
581 rGradient
.GetOfsY() / 100.0);
582 double fRotation( rGradient
.GetAngle() * M_PI
/ 1800.0 );
583 const double fBorder( rGradient
.GetBorder() / 100.0 );
585 basegfx::B2DHomMatrix aRot90
;
586 aRot90
.rotate(M_PI_2
);
588 basegfx::ODFGradientInfo aGradInfo
;
589 OUString aGradientService
;
590 switch( rGradient
.GetStyle() )
592 case GradientStyle_LINEAR
:
593 aGradInfo
= basegfx::tools::createLinearODFGradientInfo(
598 // map ODF to svg gradient orientation - x
599 // instead of y direction
600 aGradInfo
.setTextureTransform(aGradInfo
.getTextureTransform() * aRot90
);
601 aGradientService
= "LinearGradient";
604 case GradientStyle_AXIAL
:
606 // Adapt the border so that it is suitable
607 // for the axial gradient. An axial
608 // gradient consists of two linear
609 // gradients. Each of those covers half
610 // of the total size. In order to
611 // compensate for the condensed display of
612 // the linear gradients, we have to
613 // enlarge the area taken up by the actual
614 // gradient (1-fBorder). After that we
615 // have to turn the result back into a
616 // border value, hence the second (left
618 const double fAxialBorder (1-2*(1-fBorder
));
619 aGradInfo
= basegfx::tools::createAxialODFGradientInfo(
624 // map ODF to svg gradient orientation - x
625 // instead of y direction
626 aGradInfo
.setTextureTransform(aGradInfo
.getTextureTransform() * aRot90
);
628 // map ODF axial gradient to 3-stop linear
629 // gradient - shift left by 0.5
630 basegfx::B2DHomMatrix aShift
;
632 aShift
.translate(-0.5,0);
633 aGradInfo
.setTextureTransform(aGradInfo
.getTextureTransform() * aShift
);
634 aGradientService
= "LinearGradient";
638 case GradientStyle_RADIAL
:
639 aGradInfo
= basegfx::tools::createRadialODFGradientInfo(
644 aGradientService
= "EllipticalGradient";
647 case GradientStyle_ELLIPTICAL
:
648 aGradInfo
= basegfx::tools::createEllipticalODFGradientInfo(
654 aGradientService
= "EllipticalGradient";
657 case GradientStyle_SQUARE
:
658 aGradInfo
= basegfx::tools::createSquareODFGradientInfo(
664 aGradientService
= "RectangularGradient";
667 case GradientStyle_RECT
:
668 aGradInfo
= basegfx::tools::createRectangularODFGradientInfo(
674 aGradientService
= "RectangularGradient";
678 ENSURE_OR_THROW( false,
679 "ImplRenderer::createGradientAction(): Unexpected gradient type" );
683 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture
.AffineTransform
,
684 aGradInfo
.getTextureTransform() );
686 uno::Sequence
<uno::Any
> args(3);
687 beans::PropertyValue aProp
;
688 aProp
.Name
= "Colors";
689 aProp
.Value
<<= aColors
;
691 aProp
.Name
= "Stops";
692 aProp
.Value
<<= aStops
;
694 aProp
.Name
= "AspectRatio";
695 aProp
.Value
<<= aGradInfo
.getAspectRatio();
698 aTexture
.Gradient
.set(
699 xFactory
->createInstanceWithArguments(aGradientService
,
702 if( aTexture
.Gradient
.is() )
704 ActionSharedPtr
pPolyAction(
705 internal::PolyPolyActionFactory::createPolyPolyAction(
708 rParms
.mrStates
.getState(),
716 rParms
.mrCurrActionIndex
) );
718 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
721 // done, using native gradients
727 // cannot currently use native canvas gradients, as a
728 // finite step size is given (this funny feature is not
729 // supported by the XCanvas API)
730 rParms
.mrStates
.pushState(PushFlags::ALL
);
732 if( !bIsPolygonRectangle
)
734 // only clip, if given polygon is not a rectangle in
735 // the first place (the gradient is always limited to
736 // the given bound rect)
744 rParms
.mrVDev
.AddGradientActions( rPoly
.GetBoundRect(),
748 createActions( aTmpMtf
, rParms
, bSubsettableActions
);
750 rParms
.mrStates
.popState();
753 uno::Reference
< rendering::XCanvasFont
> ImplRenderer::createFont( double& o_rFontRotation
,
754 const vcl::Font
& rFont
,
755 const ActionFactoryParameters
& rParms
)
757 rendering::FontRequest aFontRequest
;
759 if( rParms
.mrParms
.maFontName
.is_initialized() )
760 aFontRequest
.FontDescription
.FamilyName
= *rParms
.mrParms
.maFontName
;
762 aFontRequest
.FontDescription
.FamilyName
= rFont
.GetFamilyName();
764 aFontRequest
.FontDescription
.StyleName
= rFont
.GetStyleName();
766 aFontRequest
.FontDescription
.IsSymbolFont
= (rFont
.GetCharSet() == RTL_TEXTENCODING_SYMBOL
) ? util::TriState_YES
: util::TriState_NO
;
767 aFontRequest
.FontDescription
.IsVertical
= rFont
.IsVertical() ? util::TriState_YES
: util::TriState_NO
;
769 // TODO(F2): improve vclenum->panose conversion
770 aFontRequest
.FontDescription
.FontDescription
.Weight
=
771 rParms
.mrParms
.maFontWeight
.is_initialized() ?
772 *rParms
.mrParms
.maFontWeight
:
773 ::canvas::tools::numeric_cast
<sal_Int8
>( ::basegfx::fround( rFont
.GetWeight() ) );
774 aFontRequest
.FontDescription
.FontDescription
.Letterform
=
775 rParms
.mrParms
.maFontLetterForm
.is_initialized() ?
776 *rParms
.mrParms
.maFontLetterForm
:
777 (rFont
.GetItalic() == ITALIC_NONE
) ? 0 : 9;
778 aFontRequest
.FontDescription
.FontDescription
.Proportion
=
779 rParms
.mrParms
.maFontProportion
.is_initialized() ?
780 *rParms
.mrParms
.maFontProportion
:
781 (rFont
.GetPitch() == PITCH_FIXED
)
782 ? rendering::PanoseProportion::MONO_SPACED
783 : rendering::PanoseProportion::ANYTHING
;
785 LanguageType aLang
= rFont
.GetLanguage();
786 aFontRequest
.Locale
= LanguageTag::convertToLocale( aLang
, false);
788 // setup state-local text transformation,
789 // if the font be rotated
790 const short nFontAngle( rFont
.GetOrientation() );
791 if( nFontAngle
!= 0 )
793 // set to unity transform rotated by font angle
794 const double nAngle( nFontAngle
* (F_PI
/ 1800.0) );
795 o_rFontRotation
= -nAngle
;
799 o_rFontRotation
= 0.0;
802 geometry::Matrix2D aFontMatrix
;
803 ::canvas::tools::setIdentityMatrix2D( aFontMatrix
);
805 // TODO(F2): use correct scale direction, font
806 // height might be width or anything else
808 // TODO(Q3): This code smells of programming by
809 // coincidence (the next two if statements)
811 ::Size
rFontSizeLog( rFont
.GetFontSize() );
813 if (rFontSizeLog
.Height() == 0)
815 // guess 16 pixel (as in VCL)
816 rFontSizeLog
= ::Size(0, 16);
818 // convert to target MapUnit if not pixels
819 rFontSizeLog
= OutputDevice::LogicToLogic(rFontSizeLog
, MAP_PIXEL
, rParms
.mrVDev
.GetMapMode());
822 const sal_Int32 nFontWidthLog
= rFontSizeLog
.Width();
823 if( nFontWidthLog
!= 0 )
825 vcl::Font aTestFont
= rFont
;
826 aTestFont
.SetAverageFontWidth( 0 );
827 sal_Int32 nNormalWidth
= rParms
.mrVDev
.GetFontMetric( aTestFont
).GetAverageFontWidth();
828 if( nNormalWidth
!= nFontWidthLog
)
830 aFontMatrix
.m00
= (double)nFontWidthLog
/ nNormalWidth
;
833 // #i52608# apply map mode scale also to font matrix - an
834 // anisotrophic mapmode must be reflected in an
835 // anisotrophic font matrix scale.
836 const OutDevState
& rState( rParms
.mrStates
.getState() );
837 if( !::basegfx::fTools::equal(
838 rState
.mapModeTransform
.get(0,0),
839 rState
.mapModeTransform
.get(1,1)) )
841 const double nScaleX( rState
.mapModeTransform
.get(0,0) );
842 const double nScaleY( rState
.mapModeTransform
.get(1,1) );
844 // note: no reason to check for division by zero, we
845 // always have the value closer (or equal) to zero as
847 if( fabs(nScaleX
) < fabs(nScaleY
) )
848 aFontMatrix
.m00
*= nScaleX
/ nScaleY
;
850 aFontMatrix
.m11
*= nScaleY
/ nScaleX
;
852 aFontRequest
.CellSize
= (rState
.mapModeTransform
* vcl::unotools::b2DSizeFromSize(rFontSizeLog
)).getY();
854 return rParms
.mrCanvas
->getUNOCanvas()->createFont( aFontRequest
,
855 uno::Sequence
< beans::PropertyValue
>(),
859 // create text effects such as shadow/relief/embossed
860 void ImplRenderer::createTextAction( const ::Point
& rStartPoint
,
861 const OUString
& rString
,
864 const long* pCharWidths
,
865 const ActionFactoryParameters
& rParms
,
866 bool bSubsettableActions
)
868 ENSURE_OR_THROW( nIndex
>= 0 && nLength
<= rString
.getLength() + nIndex
,
869 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
872 return; // zero-length text, no visible output
874 const OutDevState
& rState( rParms
.mrStates
.getState() );
876 // TODO(F2): implement all text effects
877 // if( rState.textAlignment ); // TODO(F2): NYI
879 ::Color
aShadowColor( COL_AUTO
);
880 ::Color
aReliefColor( COL_AUTO
);
881 ::Size aShadowOffset
;
882 ::Size aReliefOffset
;
884 uno::Reference
<rendering::XColorSpace
> xColorSpace(
885 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
887 if( rState
.isTextEffectShadowSet
)
889 // calculate shadow offset (similar to outdev3.cxx)
890 // TODO(F3): better match with outdev3.cxx
891 sal_Int32 nShadowOffset
= static_cast<sal_Int32
>(1.5 + ((rParms
.mrVDev
.GetFont().GetFontHeight()-24.0)/24.0));
892 if( nShadowOffset
< 1 )
895 aShadowOffset
.setWidth( nShadowOffset
);
896 aShadowOffset
.setHeight( nShadowOffset
);
898 // determine shadow color (from outdev3.cxx)
899 ::Color aTextColor
= vcl::unotools::doubleSequenceToColor(
900 rState
.textColor
, xColorSpace
);
901 bool bIsDark
= (aTextColor
.GetColor() == COL_BLACK
)
902 || (aTextColor
.GetLuminance() < 8);
904 aShadowColor
= bIsDark
? COL_LIGHTGRAY
: COL_BLACK
;
905 aShadowColor
.SetTransparency( aTextColor
.GetTransparency() );
908 if( rState
.textReliefStyle
)
910 // calculate relief offset (similar to outdev3.cxx)
911 sal_Int32 nReliefOffset
= rParms
.mrVDev
.PixelToLogic( Size( 1, 1 ) ).Height();
912 nReliefOffset
+= nReliefOffset
/2;
913 if( nReliefOffset
< 1 )
916 if( rState
.textReliefStyle
== RELIEF_ENGRAVED
)
917 nReliefOffset
= -nReliefOffset
;
919 aReliefOffset
.setWidth( nReliefOffset
);
920 aReliefOffset
.setHeight( nReliefOffset
);
922 // determine relief color (from outdev3.cxx)
923 ::Color aTextColor
= vcl::unotools::doubleSequenceToColor(
924 rState
.textColor
, xColorSpace
);
926 aReliefColor
= ::Color( COL_LIGHTGRAY
);
928 // we don't have a automatic color, so black is always
929 // drawn on white (literally copied from
930 // vcl/source/gdi/outdev3.cxx)
931 if( aTextColor
.GetColor() == COL_BLACK
)
933 aTextColor
= ::Color( COL_WHITE
);
934 rParms
.mrStates
.getState().textColor
=
935 vcl::unotools::colorToDoubleSequence(
936 aTextColor
, xColorSpace
);
939 if( aTextColor
.GetColor() == COL_WHITE
)
940 aReliefColor
= ::Color( COL_BLACK
);
941 aReliefColor
.SetTransparency( aTextColor
.GetTransparency() );
944 // create the actual text action
945 ActionSharedPtr
pTextAction(
946 TextActionFactory::createTextAction(
960 bSubsettableActions
) );
962 ActionSharedPtr pStrikeoutTextAction
;
964 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
|| rState
.textStrikeoutStyle
== STRIKEOUT_SLASH
)
966 long nWidth
= rParms
.mrVDev
.GetTextWidth( rString
,nIndex
,nLength
);
968 sal_Unicode pChars
[4];
969 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
)
973 pChars
[3]=pChars
[2]=pChars
[1]=pChars
[0];
975 long nStrikeoutWidth
= (rParms
.mrVDev
.GetTextWidth(
976 OUString(pChars
, SAL_N_ELEMENTS(pChars
))) + 2) / 4;
978 if( nStrikeoutWidth
<= 0 )
981 long nMaxWidth
= nStrikeoutWidth
/2;
984 nMaxWidth
+= nWidth
+ 1;
986 long nFullStrikeoutWidth
= 0;
987 OUString aStrikeoutText
;
988 while( (nFullStrikeoutWidth
+=nStrikeoutWidth
) < nMaxWidth
+1 )
989 aStrikeoutText
+= OUString(pChars
[0]);
991 sal_Int32 nLen
= aStrikeoutText
.getLength();
995 long nInterval
= ( nWidth
- nStrikeoutWidth
* nLen
) / nLen
;
996 nStrikeoutWidth
+= nInterval
;
997 long* pStrikeoutCharWidths
= new long[nLen
];
999 for ( int i
= 0;i
<nLen
; i
++)
1001 pStrikeoutCharWidths
[i
] = nStrikeoutWidth
;
1004 for ( int i
= 1;i
< nLen
; i
++ )
1006 pStrikeoutCharWidths
[ i
] += pStrikeoutCharWidths
[ i
-1 ];
1009 sal_Int32 nStartPos
= 0;
1011 pStrikeoutTextAction
=
1012 TextActionFactory::createTextAction(
1020 aStrikeoutText
.getLength(),
1021 pStrikeoutCharWidths
,
1026 bSubsettableActions
) ;
1032 maActions
.push_back(
1035 rParms
.mrCurrActionIndex
) );
1037 if ( pStrikeoutTextAction
)
1039 maActions
.push_back(
1041 pStrikeoutTextAction
,
1042 rParms
.mrCurrActionIndex
) );
1045 rParms
.mrCurrActionIndex
+= pTextAction
->getActionCount()-1;
1049 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon
& rClipPoly
,
1050 const ActionFactoryParameters
& rParms
,
1053 ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
1055 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1056 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1058 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1059 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1062 (bEmptyClipRect
&& bEmptyClipPoly
) )
1064 rState
.clip
= rClipPoly
;
1068 if( !bEmptyClipRect
)
1070 // TODO(P3): Use Liang-Barsky polygon clip here,
1071 // after all, one object is just a rectangle!
1073 // convert rect to polygon beforehand, must revert
1074 // to general polygon clipping here.
1075 rState
.clip
= ::basegfx::B2DPolyPolygon(
1076 ::basegfx::tools::createPolygonFromRect(
1077 // #121100# VCL rectangular clips always
1078 // include one more pixel to the right
1080 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1081 rState
.clipRect
.Top(),
1082 rState
.clipRect
.Right()+1,
1083 rState
.clipRect
.Bottom()+1 ) ) );
1087 rState
.clip
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
1088 rClipPoly
, rState
.clip
, true, false);
1091 // by now, our clip resides in the OutDevState::clip
1093 rState
.clipRect
.SetEmpty();
1095 if( rState
.clip
.count() == 0 )
1097 if( rState
.clipRect
.IsEmpty() )
1099 rState
.xClipPoly
.clear();
1103 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1104 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1105 ::basegfx::B2DPolyPolygon(
1106 ::basegfx::tools::createPolygonFromRect(
1107 // #121100# VCL rectangular clips
1108 // always include one more pixel to
1109 // the right and the bottom
1110 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1111 rState
.clipRect
.Top(),
1112 rState
.clipRect
.Right()+1,
1113 rState
.clipRect
.Bottom()+1 ) ) ) );
1118 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1119 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1124 void ImplRenderer::updateClipping( const ::Rectangle
& rClipRect
,
1125 const ActionFactoryParameters
& rParms
,
1128 ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
1130 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1131 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1133 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1134 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1137 (bEmptyClipRect
&& bEmptyClipPoly
) )
1139 rState
.clipRect
= rClipRect
;
1140 rState
.clip
.clear();
1142 else if( bEmptyClipPoly
)
1144 rState
.clipRect
.Intersection( rClipRect
);
1145 rState
.clip
.clear();
1149 // TODO(P3): Handle a fourth case here, when all clip
1150 // polygons are rectangular, once B2DMultiRange's
1151 // sweep line implementation is done.
1153 // general case: convert to polygon and clip
1156 // convert rect to polygon beforehand, must revert
1157 // to general polygon clipping here.
1158 ::basegfx::B2DPolyPolygon
aClipPoly(
1159 ::basegfx::tools::createPolygonFromRect(
1160 ::basegfx::B2DRectangle( rClipRect
.Left(),
1163 rClipRect
.Bottom() ) ) );
1165 rState
.clipRect
.SetEmpty();
1168 rState
.clip
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
1169 aClipPoly
, rState
.clip
, true, false);
1172 if( rState
.clip
.count() == 0 )
1174 if( rState
.clipRect
.IsEmpty() )
1176 rState
.xClipPoly
.clear();
1180 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1181 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1182 ::basegfx::B2DPolyPolygon(
1183 ::basegfx::tools::createPolygonFromRect(
1184 // #121100# VCL rectangular clips
1185 // always include one more pixel to
1186 // the right and the bottom
1187 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1188 rState
.clipRect
.Top(),
1189 rState
.clipRect
.Right()+1,
1190 rState
.clipRect
.Bottom()+1 ) ) ) );
1195 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1196 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1201 bool ImplRenderer::createActions( GDIMetaFile
& rMtf
,
1202 const ActionFactoryParameters
& rFactoryParms
,
1203 bool bSubsettableActions
)
1205 /* TODO(P2): interpret mtf-comments
1206 ================================
1208 - gradient fillings (do that via comments)
1210 - think about mapping. _If_ we do everything in logical
1211 coordinates (which would solve the probs for stroke
1212 widths and text offsets), then we would have to
1213 recalc scaling for every drawing operation. This is
1214 because the outdev map mode might change at any time.
1215 Also keep in mind, that, although we've double precision
1216 float arithmetic now, different offsets might still
1217 generate different roundings (aka
1218 'OutputDevice::SetPixelOffset())
1222 // alias common parameters
1223 VectorOfOutDevStates
& rStates(rFactoryParms
.mrStates
);
1224 const CanvasSharedPtr
& rCanvas(rFactoryParms
.mrCanvas
);
1225 ::VirtualDevice
& rVDev(rFactoryParms
.mrVDev
);
1226 const Parameters
& rParms(rFactoryParms
.mrParms
);
1227 sal_Int32
& io_rCurrActionIndex(rFactoryParms
.mrCurrActionIndex
);
1230 // Loop over every metaaction
1231 // ==========================
1232 MetaAction
* pCurrAct
;
1234 // TODO(P1): think about caching
1235 for( pCurrAct
=rMtf
.FirstAction();
1237 pCurrAct
= rMtf
.NextAction() )
1239 // execute every action, to keep VDev state up-to-date
1240 // currently used only for
1242 // - the line/fill color when processing a MetaActionType::Transparent
1243 // - SetFont to process font metric specific actions
1244 pCurrAct
->Execute( &rVDev
);
1246 SAL_INFO("cppcanvas.emf", "MTF\trecord type: 0x" << static_cast<sal_uInt16
>(pCurrAct
->GetType()) << " (" << static_cast<sal_uInt16
>(pCurrAct
->GetType()) << ")");
1248 switch( pCurrAct
->GetType() )
1252 // In the first part of this monster-switch, we
1253 // handle all state-changing meta actions. These
1254 // are all handled locally.
1257 case MetaActionType::PUSH
:
1259 MetaPushAction
* pPushAction
= static_cast<MetaPushAction
*>(pCurrAct
);
1260 rStates
.pushState(pPushAction
->GetFlags());
1264 case MetaActionType::POP
:
1268 case MetaActionType::TEXTLANGUAGE
:
1269 // FALLTHROUGH intended
1270 case MetaActionType::REFPOINT
:
1271 // handled via pCurrAct->Execute( &rVDev )
1274 case MetaActionType::MAPMODE
:
1275 // modify current mapModeTransformation
1276 // transformation, such that subsequent
1277 // coordinates map correctly
1278 tools::calcLogic2PixelAffineTransform( rStates
.getState().mapModeTransform
,
1282 // monitor clip regions, to assemble clip polygon on our own
1283 case MetaActionType::CLIPREGION
:
1285 MetaClipRegionAction
* pClipAction
= static_cast<MetaClipRegionAction
*>(pCurrAct
);
1287 if( !pClipAction
->IsClipping() )
1290 rStates
.getState().clip
.clear();
1294 if( !pClipAction
->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1296 SAL_INFO( "cppcanvas.emf", "ImplRenderer::createActions(): non-polygonal clip "
1297 "region encountered, falling back to bounding box!" );
1299 // #121806# explicitly kept integer
1300 Rectangle
aClipRect(
1302 pClipAction
->GetRegion().GetBoundRect() ) );
1304 // intersect current clip with given rect
1312 // set new clip polygon (don't intersect
1313 // with old one, just set it)
1315 // #121806# explicitly kept integer
1316 basegfx::B2DPolyPolygon
aPolyPolygon(pClipAction
->GetRegion().GetAsB2DPolyPolygon());
1318 aPolyPolygon
.transform(rVDev
.GetViewTransformation());
1329 case MetaActionType::ISECTRECTCLIPREGION
:
1331 MetaISectRectClipRegionAction
* pClipAction
= static_cast<MetaISectRectClipRegionAction
*>(pCurrAct
);
1333 // #121806# explicitly kept integer
1334 Rectangle
aClipRect(
1335 rVDev
.LogicToPixel( pClipAction
->GetRect() ) );
1337 // intersect current clip with given rect
1346 case MetaActionType::ISECTREGIONCLIPREGION
:
1348 MetaISectRegionClipRegionAction
* pClipAction
= static_cast<MetaISectRegionClipRegionAction
*>(pCurrAct
);
1350 if( !pClipAction
->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1352 SAL_INFO( "cppcanvas.emf", "ImplRenderer::createActions(): non-polygonal clip "
1353 "region encountered, falling back to bounding box!" );
1355 // #121806# explicitly kept integer
1356 Rectangle
aClipRect(
1357 rVDev
.LogicToPixel( pClipAction
->GetRegion().GetBoundRect() ) );
1359 // intersect current clip with given rect
1367 // intersect current clip with given clip polygon
1369 // #121806# explicitly kept integer
1370 basegfx::B2DPolyPolygon
aPolyPolygon(pClipAction
->GetRegion().GetAsB2DPolyPolygon());
1372 aPolyPolygon
.transform(rVDev
.GetViewTransformation());
1382 case MetaActionType::MOVECLIPREGION
:
1386 case MetaActionType::LINECOLOR
:
1387 if( !rParms
.maLineColor
.is_initialized() )
1389 setStateColor( static_cast<MetaLineColorAction
*>(pCurrAct
),
1390 rStates
.getState().isLineColorSet
,
1391 rStates
.getState().lineColor
,
1396 // #120994# Do switch on/off LineColor, even when a overriding one is set
1397 bool bSetting(static_cast<MetaLineColorAction
*>(pCurrAct
)->IsSetting());
1399 rStates
.getState().isLineColorSet
= bSetting
;
1403 case MetaActionType::FILLCOLOR
:
1404 if( !rParms
.maFillColor
.is_initialized() )
1406 setStateColor( static_cast<MetaFillColorAction
*>(pCurrAct
),
1407 rStates
.getState().isFillColorSet
,
1408 rStates
.getState().fillColor
,
1413 // #120994# Do switch on/off FillColor, even when a overriding one is set
1414 bool bSetting(static_cast<MetaFillColorAction
*>(pCurrAct
)->IsSetting());
1416 rStates
.getState().isFillColorSet
= bSetting
;
1420 case MetaActionType::TEXTCOLOR
:
1422 if( !rParms
.maTextColor
.is_initialized() )
1424 // Text color is set unconditionally, thus, no
1425 // use of setStateColor here
1426 ::Color
aColor( static_cast<MetaTextColorAction
*>(pCurrAct
)->GetColor() );
1428 // force alpha part of color to
1429 // opaque. transparent painting is done
1430 // explicitly via MetaActionType::Transparent
1431 aColor
.SetTransparency(0);
1433 rStates
.getState().textColor
=
1434 vcl::unotools::colorToDoubleSequence(
1436 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1441 case MetaActionType::TEXTFILLCOLOR
:
1442 if( !rParms
.maTextColor
.is_initialized() )
1444 setStateColor( static_cast<MetaTextFillColorAction
*>(pCurrAct
),
1445 rStates
.getState().isTextFillColorSet
,
1446 rStates
.getState().textFillColor
,
1451 // #120994# Do switch on/off TextFillColor, even when a overriding one is set
1452 bool bSetting(static_cast<MetaTextFillColorAction
*>(pCurrAct
)->IsSetting());
1454 rStates
.getState().isTextFillColorSet
= bSetting
;
1458 case MetaActionType::TEXTLINECOLOR
:
1459 if( !rParms
.maTextColor
.is_initialized() )
1461 setStateColor( static_cast<MetaTextLineColorAction
*>(pCurrAct
),
1462 rStates
.getState().isTextLineColorSet
,
1463 rStates
.getState().textLineColor
,
1468 // #120994# Do switch on/off TextLineColor, even when a overriding one is set
1469 bool bSetting(static_cast<MetaTextLineColorAction
*>(pCurrAct
)->IsSetting());
1471 rStates
.getState().isTextLineColorSet
= bSetting
;
1475 case MetaActionType::TEXTALIGN
:
1477 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1478 const TextAlign
eTextAlign( static_cast<MetaTextAlignAction
*>(pCurrAct
)->GetTextAlign() );
1480 rState
.textReferencePoint
= eTextAlign
;
1484 case MetaActionType::FONT
:
1486 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1487 const vcl::Font
& rFont( static_cast<MetaFontAction
*>(pCurrAct
)->GetFont() );
1489 rState
.xFont
= createFont( rState
.fontRotation
,
1493 // TODO(Q2): define and use appropriate enumeration types
1494 rState
.textReliefStyle
= (sal_Int8
)rFont
.GetRelief();
1495 rState
.textOverlineStyle
= (sal_Int8
)rFont
.GetOverline();
1496 rState
.textUnderlineStyle
= rParms
.maFontUnderline
.is_initialized() ?
1497 (*rParms
.maFontUnderline
? (sal_Int8
)LINESTYLE_SINGLE
: (sal_Int8
)LINESTYLE_NONE
) :
1498 (sal_Int8
)rFont
.GetUnderline();
1499 rState
.textStrikeoutStyle
= (sal_Int8
)rFont
.GetStrikeout();
1500 rState
.textEmphasisMarkStyle
= rFont
.GetEmphasisMark() & FontEmphasisMark::Style
;
1501 rState
.isTextEffectShadowSet
= rFont
.IsShadow();
1502 rState
.isTextWordUnderlineSet
= rFont
.IsWordLineMode();
1503 rState
.isTextOutlineModeSet
= rFont
.IsOutline();
1507 case MetaActionType::RASTEROP
:
1511 case MetaActionType::LAYOUTMODE
:
1513 // TODO(F2): A lot is missing here
1514 ComplexTextLayoutMode nLayoutMode
= static_cast<MetaLayoutModeAction
*>(pCurrAct
)->GetLayoutMode();
1515 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1517 ComplexTextLayoutMode nBidiLayoutMode
= nLayoutMode
& (TEXT_LAYOUT_BIDI_RTL
|TEXT_LAYOUT_BIDI_STRONG
);
1518 if( nBidiLayoutMode
== TEXT_LAYOUT_DEFAULT
)
1519 rState
.textDirection
= rendering::TextDirection::WEAK_LEFT_TO_RIGHT
;
1520 else if( nBidiLayoutMode
== TEXT_LAYOUT_BIDI_STRONG
)
1521 rState
.textDirection
= rendering::TextDirection::STRONG_LEFT_TO_RIGHT
;
1522 else if( nBidiLayoutMode
== TEXT_LAYOUT_BIDI_RTL
)
1523 rState
.textDirection
= rendering::TextDirection::WEAK_RIGHT_TO_LEFT
;
1524 else if( nBidiLayoutMode
== (TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_BIDI_STRONG
))
1525 rState
.textDirection
= rendering::TextDirection::STRONG_RIGHT_TO_LEFT
;
1527 rState
.textAlignment
= 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1528 if( (nLayoutMode
& (TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_TEXTORIGIN_RIGHT
) )
1529 && !(nLayoutMode
& TEXT_LAYOUT_TEXTORIGIN_LEFT
) )
1531 rState
.textAlignment
= 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1537 // In the second part of this monster-switch, we
1538 // handle all recursing meta actions. These are the
1539 // ones generating a metafile by themselves, which is
1540 // then processed by recursively calling this method.
1543 case MetaActionType::GRADIENT
:
1545 MetaGradientAction
* pGradAct
= static_cast<MetaGradientAction
*>(pCurrAct
);
1546 createGradientAction( ::tools::Polygon( pGradAct
->GetRect() ),
1547 pGradAct
->GetGradient(),
1550 bSubsettableActions
);
1554 case MetaActionType::HATCH
:
1556 // TODO(F2): use native Canvas hatches here
1557 GDIMetaFile aTmpMtf
;
1559 rVDev
.AddHatchActions( static_cast<MetaHatchAction
*>(pCurrAct
)->GetPolyPolygon(),
1560 static_cast<MetaHatchAction
*>(pCurrAct
)->GetHatch(),
1562 createActions( aTmpMtf
, rFactoryParms
,
1563 bSubsettableActions
);
1567 case MetaActionType::EPS
:
1569 MetaEPSAction
* pAct
= static_cast<MetaEPSAction
*>(pCurrAct
);
1570 const GDIMetaFile
& rSubstitute
= pAct
->GetSubstitute();
1572 // #121806# explicitly kept integer
1573 const Size
aMtfSize( rSubstitute
.GetPrefSize() );
1574 const Size
aMtfSizePixPre( rVDev
.LogicToPixel( aMtfSize
,
1575 rSubstitute
.GetPrefMapMode() ) );
1577 // #i44110# correct null-sized output - there
1578 // are metafiles which have zero size in at
1579 // least one dimension
1580 const Size
aMtfSizePix( ::std::max( aMtfSizePixPre
.Width(), 1L ),
1581 ::std::max( aMtfSizePixPre
.Height(), 1L ) );
1583 // Setup local transform, such that the
1584 // metafile renders itself into the given
1586 rStates
.pushState(PushFlags::ALL
);
1589 rVDev
.SetMapMode( rSubstitute
.GetPrefMapMode() );
1591 const ::Point
& rPos( rVDev
.LogicToPixel( pAct
->GetPoint() ) );
1592 const ::Size
& rSize( rVDev
.LogicToPixel( pAct
->GetSize() ) );
1594 rStates
.getState().transform
.translate( rPos
.X(),
1596 rStates
.getState().transform
.scale( (double)rSize
.Width() / aMtfSizePix
.Width(),
1597 (double)rSize
.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 );
1621 (pCurrAct
=rMtf
.NextAction()) != nullptr )
1623 switch( pCurrAct
->GetType() )
1625 // extract gradient info
1626 case MetaActionType::GRADIENTEX
:
1627 pGradAction
= static_cast<MetaGradientExAction
*>(pCurrAct
);
1630 // skip broken-down rendering, output gradient when sequence is ended
1631 case MetaActionType::COMMENT
:
1632 if( static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END") )
1638 createGradientAction( pGradAction
->GetPolyPolygon(),
1639 pGradAction
->GetGradient(),
1642 bSubsettableActions
);
1650 // TODO(P2): Handle drawing layer strokes, via
1651 // XPATHSTROKE_SEQ_BEGIN comment
1653 // Handle drawing layer fills
1654 else if( pAct
->GetComment() == "XPATHFILL_SEQ_BEGIN" )
1656 const sal_uInt8
* pData
= pAct
->GetData();
1659 SvMemoryStream
aMemStm( const_cast<sal_uInt8
*>(pData
), pAct
->GetDataSize(), StreamMode::READ
);
1661 SvtGraphicFill aFill
;
1662 ReadSvtGraphicFill( aMemStm
, aFill
);
1664 // TODO(P2): Also handle gradients and
1665 // hatches like this
1667 // only evaluate comment for pure
1668 // bitmap fills. If a transparency
1669 // gradient is involved (denoted by
1670 // the FloatTransparent action), take
1671 // the normal meta actions.
1672 if( aFill
.getFillType() == SvtGraphicFill::fillTexture
&&
1673 !isActionContained( rMtf
,
1674 "XPATHFILL_SEQ_END",
1675 MetaActionType::FLOATTRANSPARENT
) )
1677 rendering::Texture aTexture
;
1679 // TODO(F1): the SvtGraphicFill
1680 // can also transport metafiles
1681 // here, handle that case, too
1683 aFill
.getGraphic( aGraphic
);
1685 BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
1686 const ::Size
aBmpSize( aBmpEx
.GetSizePixel() );
1688 ::SvtGraphicFill::Transform aTransform
;
1689 aFill
.getTransform( aTransform
);
1691 ::basegfx::B2DHomMatrix aMatrix
;
1693 // convert to basegfx matrix
1694 aMatrix
.set(0,0, aTransform
.matrix
[ 0 ] );
1695 aMatrix
.set(0,1, aTransform
.matrix
[ 1 ] );
1696 aMatrix
.set(0,2, aTransform
.matrix
[ 2 ] );
1697 aMatrix
.set(1,0, aTransform
.matrix
[ 3 ] );
1698 aMatrix
.set(1,1, aTransform
.matrix
[ 4 ] );
1699 aMatrix
.set(1,2, aTransform
.matrix
[ 5 ] );
1701 ::basegfx::B2DHomMatrix aScale
;
1702 aScale
.scale( aBmpSize
.Width(),
1703 aBmpSize
.Height() );
1705 // post-multiply with the bitmap
1706 // size (XCanvas' texture assumes
1707 // the given bitmap to be
1708 // normalized to [0,1]x[0,1]
1710 aMatrix
= aMatrix
* aScale
;
1712 // pre-multiply with the
1713 // logic-to-pixel scale factor
1714 // (the metafile comment works in
1715 // logical coordinates).
1716 ::basegfx::B2DHomMatrix aLogic2PixelTransform
;
1717 aMatrix
*= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform
,
1720 ::basegfx::unotools::affineMatrixFromHomMatrix(
1721 aTexture
.AffineTransform
,
1724 aTexture
.Alpha
= 1.0 - aFill
.getTransparency();
1726 vcl::unotools::xBitmapFromBitmapEx(
1727 rCanvas
->getUNOCanvas()->getDevice(),
1729 if( aFill
.isTiling() )
1731 aTexture
.RepeatModeX
= rendering::TexturingMode::REPEAT
;
1732 aTexture
.RepeatModeY
= rendering::TexturingMode::REPEAT
;
1736 aTexture
.RepeatModeX
= rendering::TexturingMode::NONE
;
1737 aTexture
.RepeatModeY
= rendering::TexturingMode::NONE
;
1740 ::tools::PolyPolygon aPath
;
1741 aFill
.getPath( aPath
);
1743 ::basegfx::B2DPolyPolygon
aPoly( aPath
.getB2DPolyPolygon() );
1744 aPoly
.transform( rStates
.getState().mapModeTransform
);
1745 ActionSharedPtr
pPolyAction(
1746 internal::PolyPolyActionFactory::createPolyPolyAction(
1754 maActions
.push_back(
1757 io_rCurrActionIndex
) );
1759 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
1762 // skip broken-down render output
1764 "XPATHFILL_SEQ_END",
1765 io_rCurrActionIndex
);
1769 // Handle drawing layer fills
1770 else if( pAct
->GetComment() == "EMF_PLUS" ) {
1771 static int count
= -1, limit
= 0x7fffffff;
1774 if (char *env
= getenv ("EMF_PLUS_LIMIT")) {
1776 SAL_INFO ("cppcanvas.emf", "EMF+ records limit: " << limit
);
1779 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer, size: " << pAct
->GetDataSize ());
1781 processEMFPlus( pAct
, rFactoryParms
, rStates
.getState(), rCanvas
);
1783 } else if( pAct
->GetComment() == "EMF_PLUS_HEADER_INFO" ) {
1784 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pAct
->GetDataSize ());
1786 SvMemoryStream
rMF (const_cast<sal_uInt8
*>(pAct
->GetData ()), pAct
->GetDataSize (), StreamMode::READ
);
1788 rMF
.ReadInt32( nFrameLeft
).ReadInt32( nFrameTop
).ReadInt32( nFrameRight
).ReadInt32( nFrameBottom
);
1789 SAL_INFO ("cppcanvas.emf", "EMF+ picture frame: " << nFrameLeft
<< "," << nFrameTop
<< " - " << nFrameRight
<< "," << nFrameBottom
);
1790 rMF
.ReadInt32( nPixX
).ReadInt32( nPixY
).ReadInt32( nMmX
).ReadInt32( nMmY
);
1791 SAL_INFO ("cppcanvas.emf", "EMF+ ref device pixel size: " << nPixX
<< "x" << nPixY
<< " mm size: " << nMmX
<< "x" << nMmY
);
1793 ReadXForm( rMF
, aBaseTransform
);
1794 //aWorldTransform.Set (aBaseTransform);
1800 // In the third part of this monster-switch, we
1801 // handle all 'acting' meta actions. These are all
1802 // processed by constructing function objects for
1803 // them, which will later ease caching.
1806 case MetaActionType::POINT
:
1808 const OutDevState
& rState( rStates
.getState() );
1809 if( rState
.lineColor
.getLength() )
1811 ActionSharedPtr
pPointAction(
1812 internal::PointActionFactory::createPointAction(
1813 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint(
1814 static_cast<MetaPointAction
*>(pCurrAct
)->GetPoint() ),
1820 maActions
.push_back(
1823 io_rCurrActionIndex
) );
1825 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1831 case MetaActionType::PIXEL
:
1833 const OutDevState
& rState( rStates
.getState() );
1834 if( rState
.lineColor
.getLength() )
1836 ActionSharedPtr
pPointAction(
1837 internal::PointActionFactory::createPointAction(
1838 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint(
1839 static_cast<MetaPixelAction
*>(pCurrAct
)->GetPoint() ),
1842 static_cast<MetaPixelAction
*>(pCurrAct
)->GetColor() ) );
1846 maActions
.push_back(
1849 io_rCurrActionIndex
) );
1851 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1857 case MetaActionType::LINE
:
1859 const OutDevState
& rState( rStates
.getState() );
1860 if( rState
.lineColor
.getLength() )
1862 MetaLineAction
* pLineAct
= static_cast<MetaLineAction
*>(pCurrAct
);
1864 const LineInfo
& rLineInfo( pLineAct
->GetLineInfo() );
1866 const ::basegfx::B2DPoint
aStartPoint(
1867 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( pLineAct
->GetStartPoint() ));
1868 const ::basegfx::B2DPoint
aEndPoint(
1869 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( pLineAct
->GetEndPoint() ));
1871 ActionSharedPtr pLineAction
;
1873 if( rLineInfo
.IsDefault() )
1877 internal::LineActionFactory::createLineAction(
1885 maActions
.push_back(
1888 io_rCurrActionIndex
) );
1890 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
1893 else if( LINE_NONE
!= rLineInfo
.GetStyle() )
1896 rendering::StrokeAttributes aStrokeAttributes
;
1898 setupStrokeAttributes( aStrokeAttributes
,
1902 // XCanvas can only stroke polygons,
1903 // not simple lines - thus, handle
1904 // this case via the polypolygon
1906 ::basegfx::B2DPolygon aPoly
;
1907 aPoly
.append( aStartPoint
);
1908 aPoly
.append( aEndPoint
);
1910 internal::PolyPolyActionFactory::createPolyPolyAction(
1911 ::basegfx::B2DPolyPolygon( aPoly
),
1912 rCanvas
, rState
, aStrokeAttributes
);
1916 maActions
.push_back(
1919 io_rCurrActionIndex
) );
1921 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
1924 // else: line style is default
1925 // (i.e. invisible), don't generate action
1930 case MetaActionType::RECT
:
1932 const Rectangle
& rRect(
1933 static_cast<MetaRectAction
*>(pCurrAct
)->GetRect() );
1935 if( rRect
.IsEmpty() )
1938 const OutDevState
& rState( rStates
.getState() );
1939 const ::basegfx::B2DPoint
aTopLeftPixel(
1940 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ) );
1941 const ::basegfx::B2DPoint
aBottomRightPixel(
1942 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1943 // #121100# OutputDevice::DrawRect() fills
1944 // rectangles Apple-like, i.e. with one
1945 // additional pixel to the right and bottom.
1946 ::basegfx::B2DPoint(1,1) );
1948 createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
1949 ::basegfx::B2DRange( aTopLeftPixel
,
1950 aBottomRightPixel
)),
1955 case MetaActionType::ROUNDRECT
:
1957 const Rectangle
& rRect(
1958 static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetRect());
1960 if( rRect
.IsEmpty() )
1963 ::basegfx::B2DPolygon
aPoly(
1964 ::basegfx::tools::createPolygonFromRect(
1965 ::basegfx::B2DRange(
1966 vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
1967 vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1968 ::basegfx::B2DPoint(1,1) ),
1969 ( (double) static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetHorzRound() ) / rRect
.GetWidth(),
1970 ( (double) static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetVertRound() ) / rRect
.GetHeight() ) );
1971 aPoly
.transform( rStates
.getState().mapModeTransform
);
1973 createFillAndStroke( aPoly
,
1978 case MetaActionType::ELLIPSE
:
1980 const Rectangle
& rRect(
1981 static_cast<MetaEllipseAction
*>(pCurrAct
)->GetRect() );
1983 if( rRect
.IsEmpty() )
1986 const ::basegfx::B2DRange
aRange(
1987 vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
1988 vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1989 ::basegfx::B2DPoint(1,1) );
1991 ::basegfx::B2DPolygon
aPoly(
1992 ::basegfx::tools::createPolygonFromEllipse(
1995 aRange
.getHeight() ));
1996 aPoly
.transform( rStates
.getState().mapModeTransform
);
1998 createFillAndStroke( aPoly
,
2003 case MetaActionType::ARC
:
2005 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2006 const ::tools::Polygon
aToolsPoly( static_cast<MetaArcAction
*>(pCurrAct
)->GetRect(),
2007 static_cast<MetaArcAction
*>(pCurrAct
)->GetStartPoint(),
2008 static_cast<MetaArcAction
*>(pCurrAct
)->GetEndPoint(), POLY_ARC
);
2009 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2010 aPoly
.transform( rStates
.getState().mapModeTransform
);
2012 createFillAndStroke( aPoly
,
2017 case MetaActionType::PIE
:
2019 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2020 const ::tools::Polygon
aToolsPoly( static_cast<MetaPieAction
*>(pCurrAct
)->GetRect(),
2021 static_cast<MetaPieAction
*>(pCurrAct
)->GetStartPoint(),
2022 static_cast<MetaPieAction
*>(pCurrAct
)->GetEndPoint(), POLY_PIE
);
2023 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2024 aPoly
.transform( rStates
.getState().mapModeTransform
);
2026 createFillAndStroke( aPoly
,
2031 case MetaActionType::CHORD
:
2033 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2034 const ::tools::Polygon
aToolsPoly( static_cast<MetaChordAction
*>(pCurrAct
)->GetRect(),
2035 static_cast<MetaChordAction
*>(pCurrAct
)->GetStartPoint(),
2036 static_cast<MetaChordAction
*>(pCurrAct
)->GetEndPoint(), POLY_CHORD
);
2037 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2038 aPoly
.transform( rStates
.getState().mapModeTransform
);
2040 createFillAndStroke( aPoly
,
2045 case MetaActionType::POLYLINE
:
2047 const OutDevState
& rState( rStates
.getState() );
2048 if( rState
.lineColor
.getLength() ||
2049 rState
.fillColor
.getLength() )
2051 MetaPolyLineAction
* pPolyLineAct
= static_cast<MetaPolyLineAction
*>(pCurrAct
);
2053 const LineInfo
& rLineInfo( pPolyLineAct
->GetLineInfo() );
2054 ::basegfx::B2DPolygon
aPoly( pPolyLineAct
->GetPolygon().getB2DPolygon() );
2055 aPoly
.transform( rState
.mapModeTransform
);
2057 ActionSharedPtr pLineAction
;
2059 if( rLineInfo
.IsDefault() )
2061 // plain hair line polygon
2063 internal::PolyPolyActionFactory::createLinePolyPolyAction(
2064 ::basegfx::B2DPolyPolygon(aPoly
),
2070 maActions
.push_back(
2073 io_rCurrActionIndex
) );
2075 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2078 else if( LINE_NONE
!= rLineInfo
.GetStyle() )
2080 // 'thick' line polygon
2081 rendering::StrokeAttributes aStrokeAttributes
;
2083 setupStrokeAttributes( aStrokeAttributes
,
2088 internal::PolyPolyActionFactory::createPolyPolyAction(
2089 ::basegfx::B2DPolyPolygon(aPoly
),
2092 aStrokeAttributes
) ;
2096 maActions
.push_back(
2099 io_rCurrActionIndex
) );
2101 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2104 // else: line style is default
2105 // (i.e. invisible), don't generate action
2110 case MetaActionType::POLYGON
:
2112 ::basegfx::B2DPolygon
aPoly( static_cast<MetaPolygonAction
*>(pCurrAct
)->GetPolygon().getB2DPolygon() );
2113 aPoly
.transform( rStates
.getState().mapModeTransform
);
2114 createFillAndStroke( aPoly
,
2119 case MetaActionType::POLYPOLYGON
:
2121 ::basegfx::B2DPolyPolygon
aPoly( static_cast<MetaPolyPolygonAction
*>(pCurrAct
)->GetPolyPolygon().getB2DPolyPolygon() );
2122 aPoly
.transform( rStates
.getState().mapModeTransform
);
2123 createFillAndStroke( aPoly
,
2128 case MetaActionType::BMP
:
2130 MetaBmpAction
* pAct
= static_cast<MetaBmpAction
*>(pCurrAct
);
2132 ActionSharedPtr
pBmpAction(
2133 internal::BitmapActionFactory::createBitmapAction(
2135 rStates
.getState().mapModeTransform
*
2136 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2138 rStates
.getState() ) );
2142 maActions
.push_back(
2145 io_rCurrActionIndex
) );
2147 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2152 case MetaActionType::BMPSCALE
:
2154 MetaBmpScaleAction
* pAct
= static_cast<MetaBmpScaleAction
*>(pCurrAct
);
2156 ActionSharedPtr
pBmpAction(
2157 internal::BitmapActionFactory::createBitmapAction(
2159 rStates
.getState().mapModeTransform
*
2160 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2161 rStates
.getState().mapModeTransform
*
2162 vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2164 rStates
.getState() ) );
2168 maActions
.push_back(
2171 io_rCurrActionIndex
) );
2173 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2178 case MetaActionType::BMPSCALEPART
:
2180 MetaBmpScalePartAction
* pAct
= static_cast<MetaBmpScalePartAction
*>(pCurrAct
);
2182 // crop bitmap to given source rectangle (no
2183 // need to copy and convert the whole bitmap)
2184 ::Bitmap
aBmp( pAct
->GetBitmap() );
2185 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2186 pAct
->GetSrcSize() );
2187 aBmp
.Crop( aCropRect
);
2189 ActionSharedPtr
pBmpAction(
2190 internal::BitmapActionFactory::createBitmapAction(
2192 rStates
.getState().mapModeTransform
*
2193 vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2194 rStates
.getState().mapModeTransform
*
2195 vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2197 rStates
.getState() ) );
2201 maActions
.push_back(
2204 io_rCurrActionIndex
) );
2206 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2211 case MetaActionType::BMPEX
:
2213 MetaBmpExAction
* pAct
= static_cast<MetaBmpExAction
*>(pCurrAct
);
2215 ActionSharedPtr
pBmpAction(
2216 internal::BitmapActionFactory::createBitmapAction(
2217 pAct
->GetBitmapEx(),
2218 rStates
.getState().mapModeTransform
*
2219 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2221 rStates
.getState() ) );
2225 maActions
.push_back(
2228 io_rCurrActionIndex
) );
2230 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2235 case MetaActionType::BMPEXSCALE
:
2237 MetaBmpExScaleAction
* pAct
= static_cast<MetaBmpExScaleAction
*>(pCurrAct
);
2239 ActionSharedPtr
pBmpAction(
2240 internal::BitmapActionFactory::createBitmapAction(
2241 pAct
->GetBitmapEx(),
2242 rStates
.getState().mapModeTransform
*
2243 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2244 rStates
.getState().mapModeTransform
*
2245 vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2247 rStates
.getState() ) );
2251 maActions
.push_back(
2254 io_rCurrActionIndex
) );
2256 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2261 case MetaActionType::BMPEXSCALEPART
:
2263 MetaBmpExScalePartAction
* pAct
= static_cast<MetaBmpExScalePartAction
*>(pCurrAct
);
2265 // crop bitmap to given source rectangle (no
2266 // need to copy and convert the whole bitmap)
2267 BitmapEx
aBmp( pAct
->GetBitmapEx() );
2268 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2269 pAct
->GetSrcSize() );
2270 aBmp
.Crop( aCropRect
);
2272 ActionSharedPtr
pBmpAction(
2273 internal::BitmapActionFactory::createBitmapAction(
2275 rStates
.getState().mapModeTransform
*
2276 vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2277 rStates
.getState().mapModeTransform
*
2278 vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2280 rStates
.getState() ) );
2284 maActions
.push_back(
2287 io_rCurrActionIndex
) );
2289 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2294 case MetaActionType::MASK
:
2296 MetaMaskAction
* pAct
= static_cast<MetaMaskAction
*>(pCurrAct
);
2298 // create masked BitmapEx right here, as the
2299 // canvas does not provide equivalent
2301 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2302 pAct
->GetColor() ));
2304 ActionSharedPtr
pBmpAction(
2305 internal::BitmapActionFactory::createBitmapAction(
2307 rStates
.getState().mapModeTransform
*
2308 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2310 rStates
.getState() ) );
2314 maActions
.push_back(
2317 io_rCurrActionIndex
) );
2319 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2324 case MetaActionType::MASKSCALE
:
2326 MetaMaskScaleAction
* pAct
= static_cast<MetaMaskScaleAction
*>(pCurrAct
);
2328 // create masked BitmapEx right here, as the
2329 // canvas does not provide equivalent
2331 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2332 pAct
->GetColor() ));
2334 ActionSharedPtr
pBmpAction(
2335 internal::BitmapActionFactory::createBitmapAction(
2337 rStates
.getState().mapModeTransform
*
2338 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2339 rStates
.getState().mapModeTransform
*
2340 vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2342 rStates
.getState() ) );
2346 maActions
.push_back(
2349 io_rCurrActionIndex
) );
2351 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2356 case MetaActionType::MASKSCALEPART
:
2358 MetaMaskScalePartAction
* pAct
= static_cast<MetaMaskScalePartAction
*>(pCurrAct
);
2360 // create masked BitmapEx right here, as the
2361 // canvas does not provide equivalent
2363 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2364 pAct
->GetColor() ));
2366 // crop bitmap to given source rectangle (no
2367 // need to copy and convert the whole bitmap)
2368 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2369 pAct
->GetSrcSize() );
2370 aBmp
.Crop( aCropRect
);
2372 ActionSharedPtr
pBmpAction(
2373 internal::BitmapActionFactory::createBitmapAction(
2375 rStates
.getState().mapModeTransform
*
2376 vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2377 rStates
.getState().mapModeTransform
*
2378 vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2380 rStates
.getState() ) );
2384 maActions
.push_back(
2387 io_rCurrActionIndex
) );
2389 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2394 case MetaActionType::GRADIENTEX
:
2395 // TODO(F1): use native Canvas gradients here
2396 // action is ignored here, because redundant to MetaActionType::GRADIENT
2399 case MetaActionType::WALLPAPER
:
2403 case MetaActionType::Transparent
:
2405 const OutDevState
& rState( rStates
.getState() );
2406 if( rState
.lineColor
.getLength() ||
2407 rState
.fillColor
.getLength() )
2409 MetaTransparentAction
* pAct
= static_cast<MetaTransparentAction
*>(pCurrAct
);
2410 ::basegfx::B2DPolyPolygon
aPoly( pAct
->GetPolyPolygon().getB2DPolyPolygon() );
2411 aPoly
.transform( rState
.mapModeTransform
);
2413 ActionSharedPtr
pPolyAction(
2414 internal::PolyPolyActionFactory::createPolyPolyAction(
2418 pAct
->GetTransparence() ) );
2422 maActions
.push_back(
2425 io_rCurrActionIndex
) );
2427 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2433 case MetaActionType::FLOATTRANSPARENT
:
2435 MetaFloatTransparentAction
* pAct
= static_cast<MetaFloatTransparentAction
*>(pCurrAct
);
2437 internal::MtfAutoPtr
pMtf(
2438 new ::GDIMetaFile( pAct
->GetGDIMetaFile() ) );
2440 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2441 internal::GradientAutoPtr
pGradient(
2442 new Gradient( pAct
->GetGradient() ) );
2444 DBG_TESTSOLARMUTEX();
2446 ActionSharedPtr
pFloatTransAction(
2447 internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2449 std::move(pGradient
),
2450 rStates
.getState().mapModeTransform
*
2451 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2452 rStates
.getState().mapModeTransform
*
2453 vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2455 rStates
.getState() ) );
2457 if( pFloatTransAction
)
2459 maActions
.push_back(
2462 io_rCurrActionIndex
) );
2464 io_rCurrActionIndex
+= pFloatTransAction
->getActionCount()-1;
2469 case MetaActionType::TEXT
:
2471 MetaTextAction
* pAct
= static_cast<MetaTextAction
*>(pCurrAct
);
2472 OUString sText
= pAct
->GetText();
2474 if (rVDev
.GetDigitLanguage())
2475 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2477 const sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
2486 bSubsettableActions
);
2490 case MetaActionType::TEXTARRAY
:
2492 MetaTextArrayAction
* pAct
= static_cast<MetaTextArrayAction
*>(pCurrAct
);
2493 OUString sText
= pAct
->GetText();
2495 if (rVDev
.GetDigitLanguage())
2496 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2498 const sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
2507 bSubsettableActions
);
2511 case MetaActionType::TEXTLINE
:
2513 MetaTextLineAction
* pAct
= static_cast<MetaTextLineAction
*>(pCurrAct
);
2515 const OutDevState
& rState( rStates
.getState() );
2516 const ::Size
aBaselineOffset( tools::getBaselineOffset( rState
,
2518 const ::basegfx::B2DSize
aSize( rState
.mapModeTransform
*
2519 ::basegfx::B2DSize(pAct
->GetWidth(),
2522 ActionSharedPtr
pPolyAction(
2523 PolyPolyActionFactory::createPolyPolyAction(
2524 tools::createTextLinesPolyPolygon(
2525 rState
.mapModeTransform
*
2526 ::basegfx::B2DPoint(
2527 vcl::unotools::b2DPointFromPoint(pAct
->GetStartPoint()) +
2528 vcl::unotools::b2DSizeFromSize(aBaselineOffset
)),
2530 tools::createTextLineInfo( rVDev
,
2535 if( pPolyAction
.get() )
2537 maActions
.push_back(
2540 io_rCurrActionIndex
) );
2542 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2547 case MetaActionType::TEXTRECT
:
2549 MetaTextRectAction
* pAct
= static_cast<MetaTextRectAction
*>(pCurrAct
);
2551 rStates
.pushState(PushFlags::ALL
);
2553 // use the VDev to break up the text rect
2554 // action into readily formatted lines
2555 GDIMetaFile aTmpMtf
;
2556 rVDev
.AddTextRectActions( pAct
->GetRect(),
2561 createActions( aTmpMtf
,
2563 bSubsettableActions
);
2570 case MetaActionType::STRETCHTEXT
:
2572 MetaStretchTextAction
* pAct
= static_cast<MetaStretchTextAction
*>(pCurrAct
);
2573 OUString sText
= pAct
->GetText();
2575 if (rVDev
.GetDigitLanguage())
2576 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2578 const sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
2580 // #i70897# Nothing to do, actually...
2584 // have to fit the text into the given
2585 // width. This is achieved by internally
2586 // generating a DX array, and uniformly
2587 // distributing the excess/insufficient width
2588 // to every logical character.
2589 ::std::unique_ptr
< long []> pDXArray( new long[nLen
] );
2591 rVDev
.GetTextArray( pAct
->GetText(), pDXArray
.get(),
2592 pAct
->GetIndex(), pAct
->GetLen() );
2594 const sal_Int32
nWidthDifference( pAct
->GetWidth() - pDXArray
[ nLen
-1 ] );
2596 // Last entry of pDXArray contains total width of the text
2597 long* p
= pDXArray
.get();
2598 for (sal_Int32 i
= 1; i
<= nLen
; ++i
)
2600 // calc ratio for every array entry, to
2601 // distribute rounding errors 'evenly'
2602 // across the characters. Note that each
2603 // entry represents the 'end' position of
2604 // the corresponding character, thus, we
2605 // let i run from 1 to nLen.
2606 *p
++ += (long)i
*nWidthDifference
/nLen
;
2616 bSubsettableActions
);
2621 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
2625 // increment action index (each mtf action counts _at
2626 // least_ one. Some count for more, therefore,
2627 // io_rCurrActionIndex is sometimes incremented by
2628 // pAct->getActionCount()-1 above, the -1 being the
2629 // correction for the unconditional increment here).
2630 ++io_rCurrActionIndex
;
2639 class ActionRenderer
2642 explicit ActionRenderer( const ::basegfx::B2DHomMatrix
& rTransformation
) :
2643 maTransformation( rTransformation
),
2653 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2655 // ANDing the result. We want to fail if at least
2656 // one action failed.
2657 mbRet
&= rAction
.mpAction
->render( maTransformation
);
2660 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2661 const Action::Subset
& rSubset
)
2663 // ANDing the result. We want to fail if at least
2664 // one action failed.
2665 mbRet
&= rAction
.mpAction
->renderSubset( maTransformation
,
2670 ::basegfx::B2DHomMatrix maTransformation
;
2677 explicit AreaQuery( const ::basegfx::B2DHomMatrix
& rTransformation
) :
2678 maTransformation( rTransformation
),
2683 static bool result()
2685 return true; // nothing can fail here
2688 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2690 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
) );
2693 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2694 const Action::Subset
& rSubset
)
2696 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
,
2700 const ::basegfx::B2DRange
& getBounds() const
2706 ::basegfx::B2DHomMatrix maTransformation
;
2707 ::basegfx::B2DRange maBounds
;
2710 // Doing that via inline class. Compilers tend to not inline free
2712 struct UpperBoundActionIndexComparator
2714 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rLHS
,
2715 const ::cppcanvas::internal::ImplRenderer::MtfAction
& rRHS
)
2717 const sal_Int32
nLHSCount( rLHS
.mpAction
?
2718 rLHS
.mpAction
->getActionCount() : 0 );
2719 const sal_Int32
nRHSCount( rRHS
.mpAction
?
2720 rRHS
.mpAction
->getActionCount() : 0 );
2722 // compare end of action range, to have an action selected
2723 // by lower_bound even if the requested index points in
2724 // the middle of the action's range
2725 return rLHS
.mnOrigIndex
+ nLHSCount
< rRHS
.mnOrigIndex
+ nRHSCount
;
2729 /** Algorithm to apply given functor to a subset range
2733 Functor to call for each element of the subset
2734 range. Must provide the following method signatures:
2735 bool result() (returning false if operation failed)
2738 template< typename Functor
> bool
2739 forSubsetRange( Functor
& rFunctor
,
2740 ImplRenderer::ActionVector::const_iterator aRangeBegin
,
2741 const ImplRenderer::ActionVector::const_iterator
& aRangeEnd
,
2742 sal_Int32 nStartIndex
,
2743 sal_Int32 nEndIndex
,
2744 const ImplRenderer::ActionVector::const_iterator
& rEnd
)
2746 if( aRangeBegin
== aRangeEnd
)
2748 // only a single action. Setup subset, and call functor
2749 Action::Subset aSubset
;
2750 aSubset
.mnSubsetBegin
= ::std::max( sal_Int32( 0 ),
2751 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2752 aSubset
.mnSubsetEnd
= ::std::min( aRangeBegin
->mpAction
->getActionCount(),
2753 nEndIndex
- aRangeBegin
->mnOrigIndex
);
2755 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2756 "ImplRenderer::forSubsetRange(): Invalid indices" );
2758 rFunctor( *aRangeBegin
, aSubset
);
2762 // more than one action.
2764 // render partial first, full intermediate, and
2765 // partial last action
2766 Action::Subset aSubset
;
2767 aSubset
.mnSubsetBegin
= ::std::max( sal_Int32( 0 ),
2768 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2769 aSubset
.mnSubsetEnd
= aRangeBegin
->mpAction
->getActionCount();
2771 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2772 "ImplRenderer::forSubsetRange(): Invalid indices" );
2774 rFunctor( *aRangeBegin
, aSubset
);
2776 // first action rendered, skip to next
2779 // render full middle actions
2780 while( aRangeBegin
!= aRangeEnd
)
2781 rFunctor( *aRangeBegin
++ );
2783 if( aRangeEnd
== rEnd
||
2784 aRangeEnd
->mnOrigIndex
> nEndIndex
)
2786 // aRangeEnd denotes end of action vector,
2790 // nEndIndex references something _after_
2791 // aRangeBegin, but _before_ aRangeEnd
2793 // either way: no partial action left
2794 return rFunctor
.result();
2797 aSubset
.mnSubsetBegin
= 0;
2798 aSubset
.mnSubsetEnd
= nEndIndex
- aRangeEnd
->mnOrigIndex
;
2800 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2801 "ImplRenderer::forSubsetRange(): Invalid indices" );
2803 rFunctor( *aRangeEnd
, aSubset
);
2806 return rFunctor
.result();
2810 bool ImplRenderer::getSubsetIndices( sal_Int32
& io_rStartIndex
,
2811 sal_Int32
& io_rEndIndex
,
2812 ActionVector::const_iterator
& o_rRangeBegin
,
2813 ActionVector::const_iterator
& o_rRangeEnd
) const
2815 ENSURE_OR_RETURN_FALSE( io_rStartIndex
<=io_rEndIndex
,
2816 "ImplRenderer::getSubsetIndices(): invalid action range" );
2818 ENSURE_OR_RETURN_FALSE( !maActions
.empty(),
2819 "ImplRenderer::getSubsetIndices(): no actions to render" );
2821 const sal_Int32
nMinActionIndex( maActions
.front().mnOrigIndex
);
2822 const sal_Int32
nMaxActionIndex( maActions
.back().mnOrigIndex
+
2823 maActions
.back().mpAction
->getActionCount() );
2825 // clip given range to permissible values (there might be
2826 // ranges before and behind the valid indices)
2827 io_rStartIndex
= ::std::max( nMinActionIndex
,
2829 io_rEndIndex
= ::std::min( nMaxActionIndex
,
2832 if( io_rStartIndex
== io_rEndIndex
||
2833 io_rStartIndex
> io_rEndIndex
)
2835 // empty range, don't render anything. The second
2836 // condition e.g. happens if the requested range lies
2837 // fully before or behind the valid action indices.
2842 const ActionVector::const_iterator
aBegin( maActions
.begin() );
2843 const ActionVector::const_iterator
aEnd( maActions
.end() );
2846 // find start and end action
2847 // =========================
2848 o_rRangeBegin
= ::std::lower_bound( aBegin
, aEnd
,
2849 MtfAction( ActionSharedPtr(), io_rStartIndex
),
2850 UpperBoundActionIndexComparator() );
2851 o_rRangeEnd
= ::std::lower_bound( aBegin
, aEnd
,
2852 MtfAction( ActionSharedPtr(), io_rEndIndex
),
2853 UpperBoundActionIndexComparator() );
2861 ImplRenderer::ImplRenderer( const CanvasSharedPtr
& rCanvas
,
2862 const GDIMetaFile
& rMtf
,
2863 const Parameters
& rParams
)
2864 : CanvasGraphicHelper(rCanvas
)
2879 , mbMultipart(false)
2882 memset (aObjects
, 0, sizeof (aObjects
));
2884 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2886 OSL_ENSURE( rCanvas
.get() != nullptr && rCanvas
->getUNOCanvas().is(),
2887 "ImplRenderer::ImplRenderer(): Invalid canvas" );
2888 OSL_ENSURE( rCanvas
->getUNOCanvas()->getDevice().is(),
2889 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2891 // make sure canvas and graphic device are valid; action
2892 // creation don't check that every time
2893 if( rCanvas
.get() == nullptr ||
2894 !rCanvas
->getUNOCanvas().is() ||
2895 !rCanvas
->getUNOCanvas()->getDevice().is() )
2897 // leave actions empty
2901 VectorOfOutDevStates aStateStack
;
2903 ScopedVclPtrInstance
< VirtualDevice
> aVDev
;
2904 aVDev
->EnableOutput( false );
2906 // Setup VDev for state tracking and mapping
2907 // =========================================
2909 aVDev
->SetMapMode( rMtf
.GetPrefMapMode() );
2911 const Size
aMtfSize( rMtf
.GetPrefSize() );
2912 const Size
aMtfSizePixPre( aVDev
->LogicToPixel( aMtfSize
,
2913 rMtf
.GetPrefMapMode() ) );
2915 // #i44110# correct null-sized output - there are shapes
2916 // which have zero size in at least one dimension
2917 const Size
aMtfSizePix( ::std::max( aMtfSizePixPre
.Width(), 1L ),
2918 ::std::max( aMtfSizePixPre
.Height(), 1L ) );
2920 sal_Int32
nCurrActions(0);
2921 ActionFactoryParameters
aParms(aStateStack
,
2928 aStateStack
.clearStateStack();
2930 // Setup local state, such that the metafile renders
2931 // itself into a one-by-one square at the origin for
2932 // identity view and render transformations
2933 aStateStack
.getState().transform
.scale( 1.0 / aMtfSizePix
.Width(),
2934 1.0 / aMtfSizePix
.Height() );
2936 tools::calcLogic2PixelAffineTransform( aStateStack
.getState().mapModeTransform
,
2939 ColorSharedPtr
pColor( getCanvas()->createColor() );
2942 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2943 // setup default text color to black
2945 rState
.textFillColor
=
2946 rState
.textLineColor
= pColor
->getDeviceColor( 0x000000FF );
2949 // apply overrides from the Parameters struct
2950 if( rParams
.maFillColor
.is_initialized() )
2952 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2953 rState
.isFillColorSet
= true;
2954 rState
.fillColor
= pColor
->getDeviceColor( *rParams
.maFillColor
);
2956 if( rParams
.maLineColor
.is_initialized() )
2958 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2959 rState
.isLineColorSet
= true;
2960 rState
.lineColor
= pColor
->getDeviceColor( *rParams
.maLineColor
);
2962 if( rParams
.maTextColor
.is_initialized() )
2964 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2965 rState
.isTextFillColorSet
= true;
2966 rState
.isTextLineColorSet
= true;
2968 rState
.textFillColor
=
2969 rState
.textLineColor
= pColor
->getDeviceColor( *rParams
.maTextColor
);
2971 if( rParams
.maFontName
.is_initialized() ||
2972 rParams
.maFontWeight
.is_initialized() ||
2973 rParams
.maFontLetterForm
.is_initialized() ||
2974 rParams
.maFontUnderline
.is_initialized() ||
2975 rParams
.maFontProportion
.is_initialized() )
2977 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2979 rState
.xFont
= createFont( rState
.fontRotation
,
2980 vcl::Font(), // default font
2985 createActions( const_cast<GDIMetaFile
&>(rMtf
), // HACK(Q2):
2994 true // TODO(P1): make subsettability configurable
2998 ImplRenderer::~ImplRenderer()
3000 // don't leak EMFPObjects
3001 for(EMFPObject
* aObject
: aObjects
)
3005 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex
,
3006 sal_Int32 nEndIndex
) const
3008 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::drawSubset()" );
3010 ActionVector::const_iterator aRangeBegin
;
3011 ActionVector::const_iterator aRangeEnd
;
3015 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
3016 aRangeBegin
, aRangeEnd
) )
3017 return true; // nothing to render (but _that_ was successful)
3019 // now, aRangeBegin references the action in which the
3020 // subset rendering must start, and aRangeEnd references
3021 // the action in which the subset rendering must end (it
3022 // might also end right at the start of the referenced
3023 // action, such that zero of that action needs to be
3027 // render subset of actions
3028 // ========================
3030 ::basegfx::B2DHomMatrix aMatrix
;
3031 ::canvas::tools::getRenderStateTransform( aMatrix
,
3034 ActionRenderer
aRenderer( aMatrix
);
3036 return forSubsetRange( aRenderer
,
3043 catch( uno::Exception
& )
3045 SAL_WARN("cppcanvas.emf", "" << OUStringToOString(
3046 comphelper::anyToString( cppu::getCaughtException() ),
3047 RTL_TEXTENCODING_UTF8
).getStr() );
3049 // convert error to return value
3054 ::basegfx::B2DRange
ImplRenderer::getSubsetArea( sal_Int32 nStartIndex
,
3055 sal_Int32 nEndIndex
) const
3057 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3059 ActionVector::const_iterator aRangeBegin
;
3060 ActionVector::const_iterator aRangeEnd
;
3062 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
3063 aRangeBegin
, aRangeEnd
) )
3064 return ::basegfx::B2DRange(); // nothing to render -> empty range
3066 // now, aRangeBegin references the action in which the
3067 // subset querying must start, and aRangeEnd references
3068 // the action in which the subset querying must end (it
3069 // might also end right at the start of the referenced
3070 // action, such that zero of that action needs to be
3074 // query bounds for subset of actions
3075 // ==================================
3077 ::basegfx::B2DHomMatrix aMatrix
;
3078 ::canvas::tools::getRenderStateTransform( aMatrix
,
3081 AreaQuery
aQuery( aMatrix
);
3082 forSubsetRange( aQuery
,
3089 return aQuery
.getBounds();
3092 bool ImplRenderer::draw() const
3094 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::draw()" );
3096 ::basegfx::B2DHomMatrix aMatrix
;
3097 ::canvas::tools::getRenderStateTransform( aMatrix
,
3102 return ::std::for_each( maActions
.begin(), maActions
.end(), ActionRenderer( aMatrix
) ).result();
3104 catch( uno::Exception
& )
3106 SAL_WARN( "cppcanvas.emf", "" << OUStringToOString(
3107 comphelper::anyToString( cppu::getCaughtException() ),
3108 RTL_TEXTENCODING_UTF8
).getStr() );
3116 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */