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 <canvas/debug.hxx>
21 #include <tools/diagnose_ex.h>
22 #include <canvas/verbosetrace.hxx>
23 #include <osl/mutex.hxx>
24 #include <vcl/svapp.hxx>
25 #include <comphelper/sequence.hxx>
26 #include <comphelper/anytostring.hxx>
27 #include <cppuhelper/exc_hlp.hxx>
28 #include <cppcanvas/canvas.hxx>
29 #include <com/sun/star/rendering/XGraphicDevice.hpp>
30 #include <com/sun/star/rendering/TexturingMode.hpp>
31 #include <com/sun/star/uno/Sequence.hxx>
32 #include <com/sun/star/geometry/RealPoint2D.hpp>
33 #include <com/sun/star/rendering/PanoseProportion.hpp>
34 #include <com/sun/star/rendering/ViewState.hpp>
35 #include <com/sun/star/rendering/RenderState.hpp>
36 #include <com/sun/star/rendering/XCanvasFont.hpp>
37 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
38 #include <com/sun/star/rendering/XCanvas.hpp>
39 #include <com/sun/star/rendering/PathCapType.hpp>
40 #include <com/sun/star/rendering/PathJoinType.hpp>
41 #include <basegfx/tools/canvastools.hxx>
42 #include <basegfx/tools/gradienttools.hxx>
43 #include <basegfx/numeric/ftools.hxx>
44 #include <basegfx/polygon/b2dpolypolygontools.hxx>
45 #include <basegfx/polygon/b2dpolygontools.hxx>
46 #include <basegfx/polygon/b2dpolygon.hxx>
47 #include <basegfx/polygon/b2dpolypolygon.hxx>
48 #include <basegfx/matrix/b2dhommatrix.hxx>
49 #include <basegfx/vector/b2dsize.hxx>
50 #include <basegfx/range/b2drectangle.hxx>
51 #include <basegfx/point/b2dpoint.hxx>
52 #include <basegfx/tuple/b2dtuple.hxx>
53 #include <basegfx/polygon/b2dpolygonclipper.hxx>
54 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
55 #include <canvas/canvastools.hxx>
56 #include <vcl/canvastools.hxx>
57 #include <vcl/salbtype.hxx>
58 #include <vcl/gdimtf.hxx>
59 #include <vcl/metaact.hxx>
60 #include <vcl/virdev.hxx>
61 #include <vcl/metric.hxx>
62 #include <vcl/graphictools.hxx>
63 #include <tools/poly.hxx>
64 #include <i18nlangtag/languagetag.hxx>
65 #include <implrenderer.hxx>
67 #include <outdevstate.hxx>
69 #include <bitmapaction.hxx>
70 #include <lineaction.hxx>
71 #include <pointaction.hxx>
72 #include <polypolyaction.hxx>
73 #include <textaction.hxx>
74 #include <transparencygroupaction.hxx>
79 #include <boost/scoped_array.hpp>
80 #include "mtftools.hxx"
81 #include <basegfx/matrix/b2dhommatrixtools.hxx>
83 using namespace ::com::sun::star
;
86 // free support functions
87 // ======================
90 template < class MetaActionType
> void setStateColor( MetaActionType
* pAct
,
92 uno::Sequence
< double >& rColorSequence
,
93 const cppcanvas::CanvasSharedPtr
& rCanvas
)
95 rIsColorSet
= pAct
->IsSetting();
98 ::Color
aColor( pAct
->GetColor() );
100 // force alpha part of color to
101 // opaque. transparent painting is done
102 // explicitly via MetaActionType::Transparent
103 aColor
.SetTransparency(0);
104 //aColor.SetTransparency(128);
106 rColorSequence
= vcl::unotools::colorToDoubleSequence(
108 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
112 void setupStrokeAttributes( rendering::StrokeAttributes
& o_rStrokeAttributes
,
113 const ::cppcanvas::internal::ActionFactoryParameters
& rParms
,
114 const LineInfo
& rLineInfo
)
116 const ::basegfx::B2DSize
aWidth( rLineInfo
.GetWidth(), 0 );
117 o_rStrokeAttributes
.StrokeWidth
=
118 (rParms
.mrStates
.getState().mapModeTransform
* aWidth
).getX();
120 // setup reasonable defaults
121 o_rStrokeAttributes
.MiterLimit
= 15.0; // 1.0 was no good default; GDI+'s limit is 10.0, our's is 15.0
122 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::BUTT
;
123 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::BUTT
;
125 switch(rLineInfo
.GetLineJoin())
127 default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE
128 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::NONE
;
130 case basegfx::B2DLINEJOIN_BEVEL
:
131 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::BEVEL
;
133 case basegfx::B2DLINEJOIN_MITER
:
134 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::MITER
;
136 case basegfx::B2DLINEJOIN_ROUND
:
137 o_rStrokeAttributes
.JoinType
= rendering::PathJoinType::ROUND
;
141 switch(rLineInfo
.GetLineCap())
143 default: /* com::sun::star::drawing::LineCap_BUTT */
145 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::BUTT
;
146 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::BUTT
;
149 case com::sun::star::drawing::LineCap_ROUND
:
151 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::ROUND
;
152 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::ROUND
;
155 case com::sun::star::drawing::LineCap_SQUARE
:
157 o_rStrokeAttributes
.StartCapType
= rendering::PathCapType::SQUARE
;
158 o_rStrokeAttributes
.EndCapType
= rendering::PathCapType::SQUARE
;
163 if( LINE_DASH
== rLineInfo
.GetStyle() )
165 const ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
167 // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing.
169 // interpret dash info only if explicitly enabled as
171 const ::basegfx::B2DSize
aDistance( rLineInfo
.GetDistance(), 0 );
172 const double nDistance( (rState
.mapModeTransform
* aDistance
).getX() );
174 const ::basegfx::B2DSize
aDashLen( rLineInfo
.GetDashLen(), 0 );
175 const double nDashLen( (rState
.mapModeTransform
* aDashLen
).getX() );
177 const ::basegfx::B2DSize
aDotLen( rLineInfo
.GetDotLen(), 0 );
178 const double nDotLen( (rState
.mapModeTransform
* aDotLen
).getX() );
180 const sal_Int32
nNumArryEntries( 2*rLineInfo
.GetDashCount() +
181 2*rLineInfo
.GetDotCount() );
183 o_rStrokeAttributes
.DashArray
.realloc( nNumArryEntries
);
184 double* pDashArray
= o_rStrokeAttributes
.DashArray
.getArray();
187 // iteratively fill dash array, first with dashs, then
191 sal_Int32 nCurrEntry
=0;
193 for( sal_Int32 i
=0; i
<rLineInfo
.GetDashCount(); ++i
)
195 pDashArray
[nCurrEntry
++] = nDashLen
;
196 pDashArray
[nCurrEntry
++] = nDistance
;
198 for( sal_Int32 i
=0; i
<rLineInfo
.GetDotCount(); ++i
)
200 pDashArray
[nCurrEntry
++] = nDotLen
;
201 pDashArray
[nCurrEntry
++] = nDistance
;
207 /** Create masked BitmapEx, where the white areas of rBitmap are
208 transparent, and the other appear in rMaskColor.
210 BitmapEx
createMaskBmpEx( const Bitmap
& rBitmap
,
211 const ::Color
& rMaskColor
)
213 const ::Color
aWhite( COL_WHITE
);
214 BitmapPalette
aBiLevelPalette(2);
215 aBiLevelPalette
[0] = aWhite
;
216 aBiLevelPalette
[1] = rMaskColor
;
218 Bitmap
aMask( rBitmap
.CreateMask( aWhite
));
219 Bitmap
aSolid( rBitmap
.GetSizePixel(),
222 aSolid
.Erase( rMaskColor
);
224 return BitmapEx( aSolid
, aMask
);
227 OUString
convertToLocalizedNumerals(const OUString
& rStr
,
228 LanguageType eTextLanguage
)
230 OUStringBuffer
aBuf(rStr
);
231 for (sal_Int32 i
= 0; i
< aBuf
.getLength(); ++i
)
233 sal_Unicode nChar
= aBuf
[i
];
234 if (nChar
>= '0' && nChar
<= '9')
235 aBuf
[i
] = GetLocalizedChar(nChar
, eTextLanguage
);
237 return aBuf
.makeStringAndClear();
245 // state stack manipulators
247 void VectorOfOutDevStates::clearStateStack()
250 const OutDevState aDefaultState
;
251 m_aStates
.push_back(aDefaultState
);
254 OutDevState
& VectorOfOutDevStates::getState()
256 return m_aStates
.back();
259 const OutDevState
& VectorOfOutDevStates::getState() const
261 return m_aStates
.back();
264 void VectorOfOutDevStates::pushState(PushFlags nFlags
)
266 m_aStates
.push_back( getState() );
267 getState().pushFlags
= nFlags
;
270 void VectorOfOutDevStates::popState()
272 if( getState().pushFlags
!= PushFlags::ALL
)
274 // a state is pushed which is incomplete, i.e. does not
275 // restore everything to the previous stack level when
277 // That means, we take the old state, and restore every
278 // OutDevState member whose flag is set, from the new to the
279 // old state. Then the new state gets overwritten by the
282 // preset to-be-calculated new state with old state
283 OutDevState
aCalculatedNewState( getState() );
285 // selectively copy to-be-restored content over saved old
287 m_aStates
.pop_back();
289 const OutDevState
& rNewState( getState() );
291 if( (aCalculatedNewState
.pushFlags
& PushFlags::LINECOLOR
) )
293 aCalculatedNewState
.lineColor
= rNewState
.lineColor
;
294 aCalculatedNewState
.isLineColorSet
= rNewState
.isLineColorSet
;
297 if( (aCalculatedNewState
.pushFlags
& PushFlags::FILLCOLOR
) )
299 aCalculatedNewState
.fillColor
= rNewState
.fillColor
;
300 aCalculatedNewState
.isFillColorSet
= rNewState
.isFillColorSet
;
303 if( (aCalculatedNewState
.pushFlags
& PushFlags::FONT
) )
305 aCalculatedNewState
.xFont
= rNewState
.xFont
;
306 aCalculatedNewState
.fontRotation
= rNewState
.fontRotation
;
307 aCalculatedNewState
.textReliefStyle
= rNewState
.textReliefStyle
;
308 aCalculatedNewState
.textOverlineStyle
= rNewState
.textOverlineStyle
;
309 aCalculatedNewState
.textUnderlineStyle
= rNewState
.textUnderlineStyle
;
310 aCalculatedNewState
.textStrikeoutStyle
= rNewState
.textStrikeoutStyle
;
311 aCalculatedNewState
.textEmphasisMarkStyle
= rNewState
.textEmphasisMarkStyle
;
312 aCalculatedNewState
.isTextEffectShadowSet
= rNewState
.isTextEffectShadowSet
;
313 aCalculatedNewState
.isTextWordUnderlineSet
= rNewState
.isTextWordUnderlineSet
;
314 aCalculatedNewState
.isTextOutlineModeSet
= rNewState
.isTextOutlineModeSet
;
317 if( (aCalculatedNewState
.pushFlags
& PushFlags::TEXTCOLOR
) )
319 aCalculatedNewState
.textColor
= rNewState
.textColor
;
322 if( (aCalculatedNewState
.pushFlags
& PushFlags::MAPMODE
) )
324 aCalculatedNewState
.mapModeTransform
= rNewState
.mapModeTransform
;
327 if( (aCalculatedNewState
.pushFlags
& PushFlags::CLIPREGION
) )
329 aCalculatedNewState
.clip
= rNewState
.clip
;
330 aCalculatedNewState
.clipRect
= rNewState
.clipRect
;
331 aCalculatedNewState
.xClipPoly
= rNewState
.xClipPoly
;
334 // TODO(F2): Raster ops NYI
335 // if( (aCalculatedNewState.pushFlags & PushFlags::RASTEROP) )
339 if( (aCalculatedNewState
.pushFlags
& PushFlags::TEXTFILLCOLOR
) )
341 aCalculatedNewState
.textFillColor
= rNewState
.textFillColor
;
342 aCalculatedNewState
.isTextFillColorSet
= rNewState
.isTextFillColorSet
;
345 if( (aCalculatedNewState
.pushFlags
& PushFlags::TEXTALIGN
) )
347 aCalculatedNewState
.textReferencePoint
= rNewState
.textReferencePoint
;
350 // TODO(F1): Refpoint handling NYI
351 // if( (aCalculatedNewState.pushFlags & PushFlags::REFPOINT) )
355 if( (aCalculatedNewState
.pushFlags
& PushFlags::TEXTLINECOLOR
) )
357 aCalculatedNewState
.textLineColor
= rNewState
.textLineColor
;
358 aCalculatedNewState
.isTextLineColorSet
= rNewState
.isTextLineColorSet
;
361 if( (aCalculatedNewState
.pushFlags
& PushFlags::TEXTLAYOUTMODE
) )
363 aCalculatedNewState
.textAlignment
= rNewState
.textAlignment
;
364 aCalculatedNewState
.textDirection
= rNewState
.textDirection
;
367 // TODO(F2): Text language handling NYI
368 // if( (aCalculatedNewState.pushFlags & PushFlags::TEXTLANGUAGE) )
372 // always copy push mode
373 aCalculatedNewState
.pushFlags
= rNewState
.pushFlags
;
376 getState() = aCalculatedNewState
;
380 m_aStates
.pop_back();
384 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon
& rPolyPoly
,
385 const ActionFactoryParameters
& rParms
)
387 const OutDevState
& rState( rParms
.mrStates
.getState() );
388 if( (!rState
.isLineColorSet
&&
389 !rState
.isFillColorSet
) ||
390 (rState
.lineColor
.getLength() == 0 &&
391 rState
.fillColor
.getLength() == 0) )
396 ActionSharedPtr
pPolyAction(
397 internal::PolyPolyActionFactory::createPolyPolyAction(
398 rPolyPoly
, rParms
.mrCanvas
, rState
) );
405 rParms
.mrCurrActionIndex
) );
407 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
413 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon
& rPoly
,
414 const ActionFactoryParameters
& rParms
)
416 return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly
),
420 void ImplRenderer::skipContent( GDIMetaFile
& rMtf
,
421 const char* pCommentString
,
422 sal_Int32
& io_rCurrActionIndex
)
424 ENSURE_OR_THROW( pCommentString
,
425 "ImplRenderer::skipContent(): NULL string given" );
427 MetaAction
* pCurrAct
;
428 while( (pCurrAct
=rMtf
.NextAction()) != NULL
)
430 // increment action index, we've skipped an action.
431 ++io_rCurrActionIndex
;
433 if( pCurrAct
->GetType() == MetaActionType::COMMENT
&&
434 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase(
437 // requested comment found, done
446 bool ImplRenderer::isActionContained( GDIMetaFile
& rMtf
,
447 const char* pCommentString
,
448 MetaActionType nType
)
450 ENSURE_OR_THROW( pCommentString
,
451 "ImplRenderer::isActionContained(): NULL string given" );
455 // at least _one_ call to GDIMetaFile::NextAction() is
457 sal_uIntPtr
nPos( 1 );
459 MetaAction
* pCurrAct
;
460 while( (pCurrAct
=rMtf
.NextAction()) != NULL
)
462 if( pCurrAct
->GetType() == nType
)
464 bRet
= true; // action type found
468 if( pCurrAct
->GetType() == MetaActionType::COMMENT
&&
469 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase(
472 // delimiting end comment found, done
473 bRet
= false; // not yet found
480 // rewind metafile to previous position (this method must
481 // not change the current metaaction)
487 // EOF, and not yet found
494 void ImplRenderer::createGradientAction( const ::tools::PolyPolygon
& rPoly
,
495 const ::Gradient
& rGradient
,
496 const ActionFactoryParameters
& rParms
,
497 bool bIsPolygonRectangle
,
498 bool bSubsettableActions
)
500 DBG_TESTSOLARMUTEX();
502 ::basegfx::B2DPolyPolygon
aDevicePoly( rPoly
.getB2DPolyPolygon() );
503 aDevicePoly
.transform( rParms
.mrStates
.getState().mapModeTransform
);
505 // decide, whether this gradient can be rendered natively
506 // by the canvas, or must be emulated via VCL gradient
507 // action extraction.
508 const sal_uInt16
nSteps( rGradient
.GetSteps() );
510 if( // step count is infinite, can use native canvas
513 // step count is sufficiently high, such that no
514 // discernible difference should be visible.
517 uno::Reference
< lang::XMultiServiceFactory
> xFactory(
518 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
522 rendering::Texture aTexture
;
524 aTexture
.RepeatModeX
= rendering::TexturingMode::CLAMP
;
525 aTexture
.RepeatModeY
= rendering::TexturingMode::CLAMP
;
526 aTexture
.Alpha
= 1.0;
529 // setup start/end color values
532 // scale color coefficients with gradient intensities
533 const sal_uInt16
nStartIntensity( rGradient
.GetStartIntensity() );
534 ::Color
aVCLStartColor( rGradient
.GetStartColor() );
535 aVCLStartColor
.SetRed( (sal_uInt8
)(aVCLStartColor
.GetRed() * nStartIntensity
/ 100) );
536 aVCLStartColor
.SetGreen( (sal_uInt8
)(aVCLStartColor
.GetGreen() * nStartIntensity
/ 100) );
537 aVCLStartColor
.SetBlue( (sal_uInt8
)(aVCLStartColor
.GetBlue() * nStartIntensity
/ 100) );
539 const sal_uInt16
nEndIntensity( rGradient
.GetEndIntensity() );
540 ::Color
aVCLEndColor( rGradient
.GetEndColor() );
541 aVCLEndColor
.SetRed( (sal_uInt8
)(aVCLEndColor
.GetRed() * nEndIntensity
/ 100) );
542 aVCLEndColor
.SetGreen( (sal_uInt8
)(aVCLEndColor
.GetGreen() * nEndIntensity
/ 100) );
543 aVCLEndColor
.SetBlue( (sal_uInt8
)(aVCLEndColor
.GetBlue() * nEndIntensity
/ 100) );
545 uno::Reference
<rendering::XColorSpace
> xColorSpace(
546 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace());
547 const uno::Sequence
< double > aStartColor(
548 vcl::unotools::colorToDoubleSequence( aVCLStartColor
,
550 const uno::Sequence
< double > aEndColor(
551 vcl::unotools::colorToDoubleSequence( aVCLEndColor
,
554 uno::Sequence
< uno::Sequence
< double > > aColors(2);
555 uno::Sequence
< double > aStops(2);
557 if( rGradient
.GetStyle() == GradientStyle_AXIAL
)
566 aColors
[0] = aEndColor
;
567 aColors
[1] = aStartColor
;
568 aColors
[2] = aEndColor
;
575 aColors
[0] = aStartColor
;
576 aColors
[1] = aEndColor
;
579 const ::basegfx::B2DRectangle
aBounds(
580 ::basegfx::tools::getRange(aDevicePoly
) );
581 const ::basegfx::B2DVector
aOffset(
582 rGradient
.GetOfsX() / 100.0,
583 rGradient
.GetOfsY() / 100.0);
584 double fRotation( rGradient
.GetAngle() * M_PI
/ 1800.0 );
585 const double fBorder( rGradient
.GetBorder() / 100.0 );
587 basegfx::B2DHomMatrix aRot90
;
588 aRot90
.rotate(M_PI_2
);
590 basegfx::ODFGradientInfo aGradInfo
;
591 OUString aGradientService
;
592 switch( rGradient
.GetStyle() )
594 case GradientStyle_LINEAR
:
595 aGradInfo
= basegfx::tools::createLinearODFGradientInfo(
600 // map ODF to svg gradient orientation - x
601 // instead of y direction
602 aGradInfo
.setTextureTransform(aGradInfo
.getTextureTransform() * aRot90
);
603 aGradientService
= "LinearGradient";
606 case GradientStyle_AXIAL
:
608 // Adapt the border so that it is suitable
609 // for the axial gradient. An axial
610 // gradient consists of two linear
611 // gradients. Each of those covers half
612 // of the total size. In order to
613 // compensate for the condensed display of
614 // the linear gradients, we have to
615 // enlarge the area taken up by the actual
616 // gradient (1-fBorder). After that we
617 // have to turn the result back into a
618 // border value, hence the second (left
620 const double fAxialBorder (1-2*(1-fBorder
));
621 aGradInfo
= basegfx::tools::createAxialODFGradientInfo(
626 // map ODF to svg gradient orientation - x
627 // instead of y direction
628 aGradInfo
.setTextureTransform(aGradInfo
.getTextureTransform() * aRot90
);
630 // map ODF axial gradient to 3-stop linear
631 // gradient - shift left by 0.5
632 basegfx::B2DHomMatrix aShift
;
634 aShift
.translate(-0.5,0);
635 aGradInfo
.setTextureTransform(aGradInfo
.getTextureTransform() * aShift
);
636 aGradientService
= "LinearGradient";
640 case GradientStyle_RADIAL
:
641 aGradInfo
= basegfx::tools::createRadialODFGradientInfo(
646 aGradientService
= "EllipticalGradient";
649 case GradientStyle_ELLIPTICAL
:
650 aGradInfo
= basegfx::tools::createEllipticalODFGradientInfo(
656 aGradientService
= "EllipticalGradient";
659 case GradientStyle_SQUARE
:
660 aGradInfo
= basegfx::tools::createSquareODFGradientInfo(
666 aGradientService
= "RectangularGradient";
669 case GradientStyle_RECT
:
670 aGradInfo
= basegfx::tools::createRectangularODFGradientInfo(
676 aGradientService
= "RectangularGradient";
680 ENSURE_OR_THROW( false,
681 "ImplRenderer::createGradientAction(): Unexpected gradient type" );
685 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture
.AffineTransform
,
686 aGradInfo
.getTextureTransform() );
688 uno::Sequence
<uno::Any
> args(3);
689 beans::PropertyValue aProp
;
690 aProp
.Name
= "Colors";
691 aProp
.Value
<<= aColors
;
693 aProp
.Name
= "Stops";
694 aProp
.Value
<<= aStops
;
696 aProp
.Name
= "AspectRatio";
697 aProp
.Value
<<= aGradInfo
.getAspectRatio();
700 aTexture
.Gradient
.set(
701 xFactory
->createInstanceWithArguments(aGradientService
,
704 if( aTexture
.Gradient
.is() )
706 ActionSharedPtr
pPolyAction(
707 internal::PolyPolyActionFactory::createPolyPolyAction(
710 rParms
.mrStates
.getState(),
718 rParms
.mrCurrActionIndex
) );
720 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
723 // done, using native gradients
729 // cannot currently use native canvas gradients, as a
730 // finite step size is given (this funny feature is not
731 // supported by the XCanvas API)
732 rParms
.mrStates
.pushState(PushFlags::ALL
);
734 if( !bIsPolygonRectangle
)
736 // only clip, if given polygon is not a rectangle in
737 // the first place (the gradient is always limited to
738 // the given bound rect)
746 rParms
.mrVDev
.AddGradientActions( rPoly
.GetBoundRect(),
750 createActions( aTmpMtf
, rParms
, bSubsettableActions
);
752 rParms
.mrStates
.popState();
755 uno::Reference
< rendering::XCanvasFont
> ImplRenderer::createFont( double& o_rFontRotation
,
756 const vcl::Font
& rFont
,
757 const ActionFactoryParameters
& rParms
)
759 rendering::FontRequest aFontRequest
;
761 if( rParms
.mrParms
.maFontName
.is_initialized() )
762 aFontRequest
.FontDescription
.FamilyName
= *rParms
.mrParms
.maFontName
;
764 aFontRequest
.FontDescription
.FamilyName
= rFont
.GetName();
766 aFontRequest
.FontDescription
.StyleName
= rFont
.GetStyleName();
768 aFontRequest
.FontDescription
.IsSymbolFont
= (rFont
.GetCharSet() == RTL_TEXTENCODING_SYMBOL
) ? util::TriState_YES
: util::TriState_NO
;
769 aFontRequest
.FontDescription
.IsVertical
= rFont
.IsVertical() ? util::TriState_YES
: util::TriState_NO
;
771 // TODO(F2): improve vclenum->panose conversion
772 aFontRequest
.FontDescription
.FontDescription
.Weight
=
773 rParms
.mrParms
.maFontWeight
.is_initialized() ?
774 *rParms
.mrParms
.maFontWeight
:
775 ::canvas::tools::numeric_cast
<sal_Int8
>( ::basegfx::fround( rFont
.GetWeight() ) );
776 aFontRequest
.FontDescription
.FontDescription
.Letterform
=
777 rParms
.mrParms
.maFontLetterForm
.is_initialized() ?
778 *rParms
.mrParms
.maFontLetterForm
:
779 (rFont
.GetItalic() == ITALIC_NONE
) ? 0 : 9;
780 aFontRequest
.FontDescription
.FontDescription
.Proportion
=
781 rParms
.mrParms
.maFontProportion
.is_initialized() ?
782 *rParms
.mrParms
.maFontProportion
:
783 (rFont
.GetPitch() == PITCH_FIXED
)
784 ? rendering::PanoseProportion::MONO_SPACED
785 : rendering::PanoseProportion::ANYTHING
;
787 LanguageType aLang
= rFont
.GetLanguage();
788 aFontRequest
.Locale
= LanguageTag::convertToLocale( aLang
, false);
790 // setup state-local text transformation,
791 // if the font be rotated
792 const short nFontAngle( rFont
.GetOrientation() );
793 if( nFontAngle
!= 0 )
795 // set to unity transform rotated by font angle
796 const double nAngle( nFontAngle
* (F_PI
/ 1800.0) );
797 o_rFontRotation
= -nAngle
;
801 o_rFontRotation
= 0.0;
804 geometry::Matrix2D aFontMatrix
;
805 ::canvas::tools::setIdentityMatrix2D( aFontMatrix
);
807 // TODO(F2): use correct scale direction, font
808 // height might be width or anything else
810 // TODO(Q3): This code smells of programming by
811 // coincidence (the next two if statements)
813 ::Size
rFontSizeLog( rFont
.GetSize() );
815 if (rFontSizeLog
.Height() == 0)
817 // guess 16 pixel (as in VCL)
818 rFontSizeLog
= ::Size(0, 16);
820 // convert to target MapUnit if not pixels
821 rFontSizeLog
= OutputDevice::LogicToLogic(rFontSizeLog
, MAP_PIXEL
, rParms
.mrVDev
.GetMapMode());
824 const sal_Int32 nFontWidthLog
= rFontSizeLog
.Width();
825 if( nFontWidthLog
!= 0 )
827 vcl::Font aTestFont
= rFont
;
828 aTestFont
.SetWidth( 0 );
829 sal_Int32 nNormalWidth
= rParms
.mrVDev
.GetFontMetric( aTestFont
).GetWidth();
830 if( nNormalWidth
!= nFontWidthLog
)
832 aFontMatrix
.m00
= (double)nFontWidthLog
/ nNormalWidth
;
835 // #i52608# apply map mode scale also to font matrix - an
836 // anisotrophic mapmode must be reflected in an
837 // anisotrophic font matrix scale.
838 const OutDevState
& rState( rParms
.mrStates
.getState() );
839 if( !::basegfx::fTools::equal(
840 rState
.mapModeTransform
.get(0,0),
841 rState
.mapModeTransform
.get(1,1)) )
843 const double nScaleX( rState
.mapModeTransform
.get(0,0) );
844 const double nScaleY( rState
.mapModeTransform
.get(1,1) );
846 // note: no reason to check for division by zero, we
847 // always have the value closer (or equal) to zero as
849 if( fabs(nScaleX
) < fabs(nScaleY
) )
850 aFontMatrix
.m00
*= nScaleX
/ nScaleY
;
852 aFontMatrix
.m11
*= nScaleY
/ nScaleX
;
854 aFontRequest
.CellSize
= (rState
.mapModeTransform
* vcl::unotools::b2DSizeFromSize(rFontSizeLog
)).getY();
856 return rParms
.mrCanvas
->getUNOCanvas()->createFont( aFontRequest
,
857 uno::Sequence
< beans::PropertyValue
>(),
861 // create text effects such as shadow/relief/embossed
862 void ImplRenderer::createTextAction( const ::Point
& rStartPoint
,
863 const OUString
& rString
,
866 const long* pCharWidths
,
867 const ActionFactoryParameters
& rParms
,
868 bool bSubsettableActions
)
870 ENSURE_OR_THROW( nIndex
>= 0 && nLength
<= rString
.getLength() + nIndex
,
871 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
874 return; // zero-length text, no visible output
876 const OutDevState
& rState( rParms
.mrStates
.getState() );
878 // TODO(F2): implement all text effects
879 // if( rState.textAlignment ); // TODO(F2): NYI
881 ::Color
aShadowColor( COL_AUTO
);
882 ::Color
aReliefColor( COL_AUTO
);
883 ::Size aShadowOffset
;
884 ::Size aReliefOffset
;
886 uno::Reference
<rendering::XColorSpace
> xColorSpace(
887 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
889 if( rState
.isTextEffectShadowSet
)
891 // calculate shadow offset (similar to outdev3.cxx)
892 // TODO(F3): better match with outdev3.cxx
893 sal_Int32 nShadowOffset
= static_cast<sal_Int32
>(1.5 + ((rParms
.mrVDev
.GetFont().GetHeight()-24.0)/24.0));
894 if( nShadowOffset
< 1 )
897 aShadowOffset
.setWidth( nShadowOffset
);
898 aShadowOffset
.setHeight( nShadowOffset
);
900 // determine shadow color (from outdev3.cxx)
901 ::Color aTextColor
= vcl::unotools::doubleSequenceToColor(
902 rState
.textColor
, xColorSpace
);
903 bool bIsDark
= (aTextColor
.GetColor() == COL_BLACK
)
904 || (aTextColor
.GetLuminance() < 8);
906 aShadowColor
= bIsDark
? COL_LIGHTGRAY
: COL_BLACK
;
907 aShadowColor
.SetTransparency( aTextColor
.GetTransparency() );
910 if( rState
.textReliefStyle
)
912 // calculate relief offset (similar to outdev3.cxx)
913 sal_Int32 nReliefOffset
= rParms
.mrVDev
.PixelToLogic( Size( 1, 1 ) ).Height();
914 nReliefOffset
+= nReliefOffset
/2;
915 if( nReliefOffset
< 1 )
918 if( rState
.textReliefStyle
== RELIEF_ENGRAVED
)
919 nReliefOffset
= -nReliefOffset
;
921 aReliefOffset
.setWidth( nReliefOffset
);
922 aReliefOffset
.setHeight( nReliefOffset
);
924 // determine relief color (from outdev3.cxx)
925 ::Color aTextColor
= vcl::unotools::doubleSequenceToColor(
926 rState
.textColor
, xColorSpace
);
928 aReliefColor
= ::Color( COL_LIGHTGRAY
);
930 // we don't have a automatic color, so black is always
931 // drawn on white (literally copied from
932 // vcl/source/gdi/outdev3.cxx)
933 if( aTextColor
.GetColor() == COL_BLACK
)
935 aTextColor
= ::Color( COL_WHITE
);
936 rParms
.mrStates
.getState().textColor
=
937 vcl::unotools::colorToDoubleSequence(
938 aTextColor
, xColorSpace
);
941 if( aTextColor
.GetColor() == COL_WHITE
)
942 aReliefColor
= ::Color( COL_BLACK
);
943 aReliefColor
.SetTransparency( aTextColor
.GetTransparency() );
946 // create the actual text action
947 ActionSharedPtr
pTextAction(
948 TextActionFactory::createTextAction(
962 bSubsettableActions
) );
964 ActionSharedPtr pStrikeoutTextAction
;
966 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
|| rState
.textStrikeoutStyle
== STRIKEOUT_SLASH
)
968 long nWidth
= rParms
.mrVDev
.GetTextWidth( rString
,nIndex
,nLength
);
970 sal_Unicode pChars
[4];
971 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
)
975 pChars
[3]=pChars
[2]=pChars
[1]=pChars
[0];
977 long nStrikeoutWidth
= (rParms
.mrVDev
.GetTextWidth(
978 OUString(pChars
, SAL_N_ELEMENTS(pChars
))) + 2) / 4;
980 if( nStrikeoutWidth
<= 0 )
983 long nMaxWidth
= nStrikeoutWidth
/2;
986 nMaxWidth
+= nWidth
+ 1;
988 long nFullStrikeoutWidth
= 0;
989 OUString aStrikeoutText
;
990 while( (nFullStrikeoutWidth
+=nStrikeoutWidth
) < nMaxWidth
+1 )
991 aStrikeoutText
+= OUString(pChars
[0]);
993 sal_Int32 nLen
= aStrikeoutText
.getLength();
997 long nInterval
= ( nWidth
- nStrikeoutWidth
* nLen
) / nLen
;
998 nStrikeoutWidth
+= nInterval
;
999 long* pStrikeoutCharWidths
= new long[nLen
];
1001 for ( int i
= 0;i
<nLen
; i
++)
1003 pStrikeoutCharWidths
[i
] = nStrikeoutWidth
;
1006 for ( int i
= 1;i
< nLen
; i
++ )
1008 pStrikeoutCharWidths
[ i
] += pStrikeoutCharWidths
[ i
-1 ];
1011 sal_Int32 nStartPos
= 0;
1013 pStrikeoutTextAction
=
1014 TextActionFactory::createTextAction(
1022 aStrikeoutText
.getLength(),
1023 pStrikeoutCharWidths
,
1028 bSubsettableActions
) ;
1034 maActions
.push_back(
1037 rParms
.mrCurrActionIndex
) );
1039 if ( pStrikeoutTextAction
)
1041 maActions
.push_back(
1043 pStrikeoutTextAction
,
1044 rParms
.mrCurrActionIndex
) );
1047 rParms
.mrCurrActionIndex
+= pTextAction
->getActionCount()-1;
1051 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon
& rClipPoly
,
1052 const ActionFactoryParameters
& rParms
,
1055 ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
1056 ::basegfx::B2DPolyPolygon
aClipPoly( rClipPoly
);
1058 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1059 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1061 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1062 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1065 (bEmptyClipRect
&& bEmptyClipPoly
) )
1067 rState
.clip
= rClipPoly
;
1071 if( !bEmptyClipRect
)
1073 // TODO(P3): Use Liang-Barsky polygon clip here,
1074 // after all, one object is just a rectangle!
1076 // convert rect to polygon beforehand, must revert
1077 // to general polygon clipping here.
1078 rState
.clip
= ::basegfx::B2DPolyPolygon(
1079 ::basegfx::tools::createPolygonFromRect(
1080 // #121100# VCL rectangular clips always
1081 // include one more pixel to the right
1083 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1084 rState
.clipRect
.Top(),
1085 rState
.clipRect
.Right()+1,
1086 rState
.clipRect
.Bottom()+1 ) ) );
1090 rState
.clip
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
1091 aClipPoly
, rState
.clip
, true, false);
1094 // by now, our clip resides in the OutDevState::clip
1096 rState
.clipRect
.SetEmpty();
1098 if( rState
.clip
.count() == 0 )
1100 if( rState
.clipRect
.IsEmpty() )
1102 rState
.xClipPoly
.clear();
1106 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1107 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1108 ::basegfx::B2DPolyPolygon(
1109 ::basegfx::tools::createPolygonFromRect(
1110 // #121100# VCL rectangular clips
1111 // always include one more pixel to
1112 // the right and the bottom
1113 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1114 rState
.clipRect
.Top(),
1115 rState
.clipRect
.Right()+1,
1116 rState
.clipRect
.Bottom()+1 ) ) ) );
1121 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1122 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1127 void ImplRenderer::updateClipping( const ::Rectangle
& rClipRect
,
1128 const ActionFactoryParameters
& rParms
,
1131 ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
1133 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1134 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1136 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1137 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1140 (bEmptyClipRect
&& bEmptyClipPoly
) )
1142 rState
.clipRect
= rClipRect
;
1143 rState
.clip
.clear();
1145 else if( bEmptyClipPoly
)
1147 rState
.clipRect
.Intersection( rClipRect
);
1148 rState
.clip
.clear();
1152 // TODO(P3): Handle a fourth case here, when all clip
1153 // polygons are rectangular, once B2DMultiRange's
1154 // sweep line implementation is done.
1156 // general case: convert to polygon and clip
1159 // convert rect to polygon beforehand, must revert
1160 // to general polygon clipping here.
1161 ::basegfx::B2DPolyPolygon
aClipPoly(
1162 ::basegfx::tools::createPolygonFromRect(
1163 ::basegfx::B2DRectangle( rClipRect
.Left(),
1166 rClipRect
.Bottom() ) ) );
1168 rState
.clipRect
.SetEmpty();
1171 rState
.clip
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
1172 aClipPoly
, rState
.clip
, true, false);
1175 if( rState
.clip
.count() == 0 )
1177 if( rState
.clipRect
.IsEmpty() )
1179 rState
.xClipPoly
.clear();
1183 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1184 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1185 ::basegfx::B2DPolyPolygon(
1186 ::basegfx::tools::createPolygonFromRect(
1187 // #121100# VCL rectangular clips
1188 // always include one more pixel to
1189 // the right and the bottom
1190 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1191 rState
.clipRect
.Top(),
1192 rState
.clipRect
.Right()+1,
1193 rState
.clipRect
.Bottom()+1 ) ) ) );
1198 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1199 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1204 bool ImplRenderer::createActions( GDIMetaFile
& rMtf
,
1205 const ActionFactoryParameters
& rFactoryParms
,
1206 bool bSubsettableActions
)
1208 /* TODO(P2): interpret mtf-comments
1209 ================================
1211 - gradient fillings (do that via comments)
1213 - think about mapping. _If_ we do everything in logical
1214 coordinates (which would solve the probs for stroke
1215 widths and text offsets), then we would have to
1216 recalc scaling for every drawing operation. This is
1217 because the outdev map mode might change at any time.
1218 Also keep in mind, that, although we've double precision
1219 float arithmetic now, different offsets might still
1220 generate different roundings (aka
1221 'OutputDevice::SetPixelOffset())
1225 // alias common parameters
1226 VectorOfOutDevStates
& rStates(rFactoryParms
.mrStates
);
1227 const CanvasSharedPtr
& rCanvas(rFactoryParms
.mrCanvas
);
1228 ::VirtualDevice
& rVDev(rFactoryParms
.mrVDev
);
1229 const Parameters
& rParms(rFactoryParms
.mrParms
);
1230 sal_Int32
& io_rCurrActionIndex(rFactoryParms
.mrCurrActionIndex
);
1233 // Loop over every metaaction
1234 // ==========================
1235 MetaAction
* pCurrAct
;
1237 // TODO(P1): think about caching
1238 for( pCurrAct
=rMtf
.FirstAction();
1240 pCurrAct
= rMtf
.NextAction() )
1242 // execute every action, to keep VDev state up-to-date
1243 // currently used only for
1245 // - the line/fill color when processing a MetaActionType::Transparent
1246 // - SetFont to process font metric specific actions
1247 pCurrAct
->Execute( &rVDev
);
1249 SAL_INFO("cppcanvas.emf", "MTF\trecord type: 0x" << static_cast<sal_uInt16
>(pCurrAct
->GetType()) << " (" << static_cast<sal_uInt16
>(pCurrAct
->GetType()) << ")");
1251 switch( pCurrAct
->GetType() )
1255 // In the first part of this monster-switch, we
1256 // handle all state-changing meta actions. These
1257 // are all handled locally.
1261 case MetaActionType::PUSH
:
1263 MetaPushAction
* pPushAction
= static_cast<MetaPushAction
*>(pCurrAct
);
1264 rStates
.pushState(pPushAction
->GetFlags());
1268 case MetaActionType::POP
:
1272 case MetaActionType::TEXTLANGUAGE
:
1273 // FALLTHROUGH intended
1274 case MetaActionType::REFPOINT
:
1275 // handled via pCurrAct->Execute( &rVDev )
1278 case MetaActionType::MAPMODE
:
1279 // modify current mapModeTransformation
1280 // transformation, such that subsequent
1281 // coordinates map correctly
1282 tools::calcLogic2PixelAffineTransform( rStates
.getState().mapModeTransform
,
1286 // monitor clip regions, to assemble clip polygon on our own
1287 case MetaActionType::CLIPREGION
:
1289 MetaClipRegionAction
* pClipAction
= static_cast<MetaClipRegionAction
*>(pCurrAct
);
1291 if( !pClipAction
->IsClipping() )
1294 rStates
.getState().clip
.clear();
1298 if( !pClipAction
->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1300 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1301 "region encountered, falling back to bounding box!" );
1303 // #121806# explicitly kept integer
1304 Rectangle
aClipRect(
1306 pClipAction
->GetRegion().GetBoundRect() ) );
1308 // intersect current clip with given rect
1316 // set new clip polygon (don't intersect
1317 // with old one, just set it)
1319 // #121806# explicitly kept integer
1320 basegfx::B2DPolyPolygon
aPolyPolygon(pClipAction
->GetRegion().GetAsB2DPolyPolygon());
1322 aPolyPolygon
.transform(rVDev
.GetViewTransformation());
1333 case MetaActionType::ISECTRECTCLIPREGION
:
1335 MetaISectRectClipRegionAction
* pClipAction
= static_cast<MetaISectRectClipRegionAction
*>(pCurrAct
);
1337 // #121806# explicitly kept integer
1338 Rectangle
aClipRect(
1339 rVDev
.LogicToPixel( pClipAction
->GetRect() ) );
1341 // intersect current clip with given rect
1350 case MetaActionType::ISECTREGIONCLIPREGION
:
1352 MetaISectRegionClipRegionAction
* pClipAction
= static_cast<MetaISectRegionClipRegionAction
*>(pCurrAct
);
1354 if( !pClipAction
->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1356 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1357 "region encountered, falling back to bounding box!" );
1359 // #121806# explicitly kept integer
1360 Rectangle
aClipRect(
1361 rVDev
.LogicToPixel( pClipAction
->GetRegion().GetBoundRect() ) );
1363 // intersect current clip with given rect
1371 // intersect current clip with given clip polygon
1373 // #121806# explicitly kept integer
1374 basegfx::B2DPolyPolygon
aPolyPolygon(pClipAction
->GetRegion().GetAsB2DPolyPolygon());
1376 aPolyPolygon
.transform(rVDev
.GetViewTransformation());
1386 case MetaActionType::MOVECLIPREGION
:
1390 case MetaActionType::LINECOLOR
:
1391 if( !rParms
.maLineColor
.is_initialized() )
1393 setStateColor( static_cast<MetaLineColorAction
*>(pCurrAct
),
1394 rStates
.getState().isLineColorSet
,
1395 rStates
.getState().lineColor
,
1400 // #120994# Do switch on/off LineColor, even when a overriding one is set
1401 bool bSetting(static_cast<MetaLineColorAction
*>(pCurrAct
)->IsSetting());
1403 rStates
.getState().isLineColorSet
= bSetting
;
1407 case MetaActionType::FILLCOLOR
:
1408 if( !rParms
.maFillColor
.is_initialized() )
1410 setStateColor( static_cast<MetaFillColorAction
*>(pCurrAct
),
1411 rStates
.getState().isFillColorSet
,
1412 rStates
.getState().fillColor
,
1417 // #120994# Do switch on/off FillColor, even when a overriding one is set
1418 bool bSetting(static_cast<MetaFillColorAction
*>(pCurrAct
)->IsSetting());
1420 rStates
.getState().isFillColorSet
= bSetting
;
1424 case MetaActionType::TEXTCOLOR
:
1426 if( !rParms
.maTextColor
.is_initialized() )
1428 // Text color is set unconditionally, thus, no
1429 // use of setStateColor here
1430 ::Color
aColor( static_cast<MetaTextColorAction
*>(pCurrAct
)->GetColor() );
1432 // force alpha part of color to
1433 // opaque. transparent painting is done
1434 // explicitly via MetaActionType::Transparent
1435 aColor
.SetTransparency(0);
1437 rStates
.getState().textColor
=
1438 vcl::unotools::colorToDoubleSequence(
1440 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1445 case MetaActionType::TEXTFILLCOLOR
:
1446 if( !rParms
.maTextColor
.is_initialized() )
1448 setStateColor( static_cast<MetaTextFillColorAction
*>(pCurrAct
),
1449 rStates
.getState().isTextFillColorSet
,
1450 rStates
.getState().textFillColor
,
1455 // #120994# Do switch on/off TextFillColor, even when a overriding one is set
1456 bool bSetting(static_cast<MetaTextFillColorAction
*>(pCurrAct
)->IsSetting());
1458 rStates
.getState().isTextFillColorSet
= bSetting
;
1462 case MetaActionType::TEXTLINECOLOR
:
1463 if( !rParms
.maTextColor
.is_initialized() )
1465 setStateColor( static_cast<MetaTextLineColorAction
*>(pCurrAct
),
1466 rStates
.getState().isTextLineColorSet
,
1467 rStates
.getState().textLineColor
,
1472 // #120994# Do switch on/off TextLineColor, even when a overriding one is set
1473 bool bSetting(static_cast<MetaTextLineColorAction
*>(pCurrAct
)->IsSetting());
1475 rStates
.getState().isTextLineColorSet
= bSetting
;
1479 case MetaActionType::TEXTALIGN
:
1481 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1482 const TextAlign
eTextAlign( static_cast<MetaTextAlignAction
*>(pCurrAct
)->GetTextAlign() );
1484 rState
.textReferencePoint
= eTextAlign
;
1488 case MetaActionType::FONT
:
1490 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1491 const vcl::Font
& rFont( static_cast<MetaFontAction
*>(pCurrAct
)->GetFont() );
1493 rState
.xFont
= createFont( rState
.fontRotation
,
1497 // TODO(Q2): define and use appropriate enumeration types
1498 rState
.textReliefStyle
= (sal_Int8
)rFont
.GetRelief();
1499 rState
.textOverlineStyle
= (sal_Int8
)rFont
.GetOverline();
1500 rState
.textUnderlineStyle
= rParms
.maFontUnderline
.is_initialized() ?
1501 (*rParms
.maFontUnderline
? (sal_Int8
)UNDERLINE_SINGLE
: (sal_Int8
)UNDERLINE_NONE
) :
1502 (sal_Int8
)rFont
.GetUnderline();
1503 rState
.textStrikeoutStyle
= (sal_Int8
)rFont
.GetStrikeout();
1504 rState
.textEmphasisMarkStyle
= (sal_Int8
)rFont
.GetEmphasisMark();
1505 rState
.isTextEffectShadowSet
= rFont
.IsShadow();
1506 rState
.isTextWordUnderlineSet
= rFont
.IsWordLineMode();
1507 rState
.isTextOutlineModeSet
= rFont
.IsOutline();
1511 case MetaActionType::RASTEROP
:
1515 case MetaActionType::LAYOUTMODE
:
1517 // TODO(F2): A lot is missing here
1518 ComplexTextLayoutMode nLayoutMode
= static_cast<MetaLayoutModeAction
*>(pCurrAct
)->GetLayoutMode();
1519 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1521 ComplexTextLayoutMode nBidiLayoutMode
= nLayoutMode
& (TEXT_LAYOUT_BIDI_RTL
|TEXT_LAYOUT_BIDI_STRONG
);
1522 if( nBidiLayoutMode
== TEXT_LAYOUT_DEFAULT
)
1523 rState
.textDirection
= rendering::TextDirection::WEAK_LEFT_TO_RIGHT
;
1524 else if( nBidiLayoutMode
== TEXT_LAYOUT_BIDI_STRONG
)
1525 rState
.textDirection
= rendering::TextDirection::STRONG_LEFT_TO_RIGHT
;
1526 else if( nBidiLayoutMode
== TEXT_LAYOUT_BIDI_RTL
)
1527 rState
.textDirection
= rendering::TextDirection::WEAK_RIGHT_TO_LEFT
;
1528 else if( nBidiLayoutMode
== (TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_BIDI_STRONG
))
1529 rState
.textDirection
= rendering::TextDirection::STRONG_RIGHT_TO_LEFT
;
1531 rState
.textAlignment
= 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1532 if( (nLayoutMode
& (TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_TEXTORIGIN_RIGHT
) )
1533 && !(nLayoutMode
& TEXT_LAYOUT_TEXTORIGIN_LEFT
) )
1535 rState
.textAlignment
= 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1542 // In the second part of this monster-switch, we
1543 // handle all recursing meta actions. These are the
1544 // ones generating a metafile by themselves, which is
1545 // then processed by recursively calling this method.
1549 case MetaActionType::GRADIENT
:
1551 MetaGradientAction
* pGradAct
= static_cast<MetaGradientAction
*>(pCurrAct
);
1552 createGradientAction( ::Polygon( pGradAct
->GetRect() ),
1553 pGradAct
->GetGradient(),
1556 bSubsettableActions
);
1560 case MetaActionType::HATCH
:
1562 // TODO(F2): use native Canvas hatches here
1563 GDIMetaFile aTmpMtf
;
1565 rVDev
.AddHatchActions( static_cast<MetaHatchAction
*>(pCurrAct
)->GetPolyPolygon(),
1566 static_cast<MetaHatchAction
*>(pCurrAct
)->GetHatch(),
1568 createActions( aTmpMtf
, rFactoryParms
,
1569 bSubsettableActions
);
1573 case MetaActionType::EPS
:
1575 MetaEPSAction
* pAct
= static_cast<MetaEPSAction
*>(pCurrAct
);
1576 const GDIMetaFile
& rSubstitute
= pAct
->GetSubstitute();
1578 // #121806# explicitly kept integer
1579 const Size
aMtfSize( rSubstitute
.GetPrefSize() );
1580 const Size
aMtfSizePixPre( rVDev
.LogicToPixel( aMtfSize
,
1581 rSubstitute
.GetPrefMapMode() ) );
1583 // #i44110# correct null-sized output - there
1584 // are metafiles which have zero size in at
1585 // least one dimension
1586 const Size
aMtfSizePix( ::std::max( aMtfSizePixPre
.Width(), 1L ),
1587 ::std::max( aMtfSizePixPre
.Height(), 1L ) );
1589 // Setup local transform, such that the
1590 // metafile renders itself into the given
1592 rStates
.pushState(PushFlags::ALL
);
1595 rVDev
.SetMapMode( rSubstitute
.GetPrefMapMode() );
1597 const ::Point
& rPos( rVDev
.LogicToPixel( pAct
->GetPoint() ) );
1598 const ::Size
& rSize( rVDev
.LogicToPixel( pAct
->GetSize() ) );
1600 rStates
.getState().transform
.translate( rPos
.X(),
1602 rStates
.getState().transform
.scale( (double)rSize
.Width() / aMtfSizePix
.Width(),
1603 (double)rSize
.Height() / aMtfSizePix
.Height() );
1605 createActions( const_cast<GDIMetaFile
&>(pAct
->GetSubstitute()),
1607 bSubsettableActions
);
1614 // handle metafile comments, to retrieve
1615 // meta-information for gradients, fills and
1616 // strokes. May skip actions, and may recurse.
1617 case MetaActionType::COMMENT
:
1619 MetaCommentAction
* pAct
= static_cast<MetaCommentAction
*>(pCurrAct
);
1622 if (pAct
->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
1624 MetaGradientExAction
* pGradAction
= NULL
;
1625 bool bDone( false );
1627 (pCurrAct
=rMtf
.NextAction()) != NULL
)
1629 switch( pCurrAct
->GetType() )
1631 // extract gradient info
1632 case MetaActionType::GRADIENTEX
:
1633 pGradAction
= static_cast<MetaGradientExAction
*>(pCurrAct
);
1636 // skip broken-down rendering, output gradient when sequence is ended
1637 case MetaActionType::COMMENT
:
1638 if( static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END") )
1644 createGradientAction( pGradAction
->GetPolyPolygon(),
1645 pGradAction
->GetGradient(),
1648 bSubsettableActions
);
1656 // TODO(P2): Handle drawing layer strokes, via
1657 // XPATHSTROKE_SEQ_BEGIN comment
1659 // Handle drawing layer fills
1660 else if( pAct
->GetComment() == "XPATHFILL_SEQ_BEGIN" )
1662 const sal_uInt8
* pData
= pAct
->GetData();
1665 SvMemoryStream
aMemStm( (void*)pData
, pAct
->GetDataSize(), StreamMode::READ
);
1667 SvtGraphicFill aFill
;
1668 ReadSvtGraphicFill( aMemStm
, aFill
);
1670 // TODO(P2): Also handle gradients and
1671 // hatches like this
1673 // only evaluate comment for pure
1674 // bitmap fills. If a transparency
1675 // gradient is involved (denoted by
1676 // the FloatTransparent action), take
1677 // the normal meta actions.
1678 if( aFill
.getFillType() == SvtGraphicFill::fillTexture
&&
1679 !isActionContained( rMtf
,
1680 "XPATHFILL_SEQ_END",
1681 MetaActionType::FLOATTRANSPARENT
) )
1683 rendering::Texture aTexture
;
1685 // TODO(F1): the SvtGraphicFill
1686 // can also transport metafiles
1687 // here, handle that case, too
1689 aFill
.getGraphic( aGraphic
);
1691 BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
1692 const ::Size
aBmpSize( aBmpEx
.GetSizePixel() );
1694 ::SvtGraphicFill::Transform aTransform
;
1695 aFill
.getTransform( aTransform
);
1697 ::basegfx::B2DHomMatrix aMatrix
;
1699 // convert to basegfx matrix
1700 aMatrix
.set(0,0, aTransform
.matrix
[ 0 ] );
1701 aMatrix
.set(0,1, aTransform
.matrix
[ 1 ] );
1702 aMatrix
.set(0,2, aTransform
.matrix
[ 2 ] );
1703 aMatrix
.set(1,0, aTransform
.matrix
[ 3 ] );
1704 aMatrix
.set(1,1, aTransform
.matrix
[ 4 ] );
1705 aMatrix
.set(1,2, aTransform
.matrix
[ 5 ] );
1707 ::basegfx::B2DHomMatrix aScale
;
1708 aScale
.scale( aBmpSize
.Width(),
1709 aBmpSize
.Height() );
1711 // post-multiply with the bitmap
1712 // size (XCanvas' texture assumes
1713 // the given bitmap to be
1714 // normalized to [0,1]x[0,1]
1716 aMatrix
= aMatrix
* aScale
;
1718 // pre-multiply with the
1719 // logic-to-pixel scale factor
1720 // (the metafile comment works in
1721 // logical coordinates).
1722 ::basegfx::B2DHomMatrix aLogic2PixelTransform
;
1723 aMatrix
*= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform
,
1726 ::basegfx::unotools::affineMatrixFromHomMatrix(
1727 aTexture
.AffineTransform
,
1730 aTexture
.Alpha
= 1.0 - aFill
.getTransparency();
1732 vcl::unotools::xBitmapFromBitmapEx(
1733 rCanvas
->getUNOCanvas()->getDevice(),
1735 if( aFill
.isTiling() )
1737 aTexture
.RepeatModeX
= rendering::TexturingMode::REPEAT
;
1738 aTexture
.RepeatModeY
= rendering::TexturingMode::REPEAT
;
1742 aTexture
.RepeatModeX
= rendering::TexturingMode::NONE
;
1743 aTexture
.RepeatModeY
= rendering::TexturingMode::NONE
;
1746 ::tools::PolyPolygon aPath
;
1747 aFill
.getPath( aPath
);
1749 ::basegfx::B2DPolyPolygon
aPoly( aPath
.getB2DPolyPolygon() );
1750 aPoly
.transform( rStates
.getState().mapModeTransform
);
1751 ActionSharedPtr
pPolyAction(
1752 internal::PolyPolyActionFactory::createPolyPolyAction(
1760 maActions
.push_back(
1763 io_rCurrActionIndex
) );
1765 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
1768 // skip broken-down render output
1770 "XPATHFILL_SEQ_END",
1771 io_rCurrActionIndex
);
1775 // Handle drawing layer fills
1776 else if( pAct
->GetComment() == "EMF_PLUS" ) {
1777 static int count
= -1, limit
= 0x7fffffff;
1780 if (char *env
= getenv ("EMF_PLUS_LIMIT")) {
1782 SAL_INFO ("cppcanvas.emf", "EMF+ records limit: " << limit
);
1785 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer, size: " << pAct
->GetDataSize ());
1787 processEMFPlus( pAct
, rFactoryParms
, rStates
.getState(), rCanvas
);
1789 } else if( pAct
->GetComment() == "EMF_PLUS_HEADER_INFO" ) {
1790 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pAct
->GetDataSize ());
1792 SvMemoryStream
rMF ((void*) pAct
->GetData (), pAct
->GetDataSize (), StreamMode::READ
);
1794 rMF
.ReadInt32( nFrameLeft
).ReadInt32( nFrameTop
).ReadInt32( nFrameRight
).ReadInt32( nFrameBottom
);
1795 SAL_INFO ("cppcanvas.emf", "EMF+ picture frame: " << nFrameLeft
<< "," << nFrameTop
<< " - " << nFrameRight
<< "," << nFrameBottom
);
1796 rMF
.ReadInt32( nPixX
).ReadInt32( nPixY
).ReadInt32( nMmX
).ReadInt32( nMmY
);
1797 SAL_INFO ("cppcanvas.emf", "EMF+ ref device pixel size: " << nPixX
<< "x" << nPixY
<< " mm size: " << nMmX
<< "x" << nMmY
);
1799 ReadXForm( rMF
, aBaseTransform
);
1800 //aWorldTransform.Set (aBaseTransform);
1807 // In the third part of this monster-switch, we
1808 // handle all 'acting' meta actions. These are all
1809 // processed by constructing function objects for
1810 // them, which will later ease caching.
1814 case MetaActionType::POINT
:
1816 const OutDevState
& rState( rStates
.getState() );
1817 if( rState
.lineColor
.getLength() )
1819 ActionSharedPtr
pPointAction(
1820 internal::PointActionFactory::createPointAction(
1821 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint(
1822 static_cast<MetaPointAction
*>(pCurrAct
)->GetPoint() ),
1828 maActions
.push_back(
1831 io_rCurrActionIndex
) );
1833 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1839 case MetaActionType::PIXEL
:
1841 const OutDevState
& rState( rStates
.getState() );
1842 if( rState
.lineColor
.getLength() )
1844 ActionSharedPtr
pPointAction(
1845 internal::PointActionFactory::createPointAction(
1846 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint(
1847 static_cast<MetaPixelAction
*>(pCurrAct
)->GetPoint() ),
1850 static_cast<MetaPixelAction
*>(pCurrAct
)->GetColor() ) );
1854 maActions
.push_back(
1857 io_rCurrActionIndex
) );
1859 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1865 case MetaActionType::LINE
:
1867 const OutDevState
& rState( rStates
.getState() );
1868 if( rState
.lineColor
.getLength() )
1870 MetaLineAction
* pLineAct
= static_cast<MetaLineAction
*>(pCurrAct
);
1872 const LineInfo
& rLineInfo( pLineAct
->GetLineInfo() );
1874 const ::basegfx::B2DPoint
aStartPoint(
1875 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( pLineAct
->GetStartPoint() ));
1876 const ::basegfx::B2DPoint
aEndPoint(
1877 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( pLineAct
->GetEndPoint() ));
1879 ActionSharedPtr pLineAction
;
1881 if( rLineInfo
.IsDefault() )
1885 internal::LineActionFactory::createLineAction(
1893 maActions
.push_back(
1896 io_rCurrActionIndex
) );
1898 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
1901 else if( LINE_NONE
!= rLineInfo
.GetStyle() )
1904 rendering::StrokeAttributes aStrokeAttributes
;
1906 setupStrokeAttributes( aStrokeAttributes
,
1910 // XCanvas can only stroke polygons,
1911 // not simple lines - thus, handle
1912 // this case via the polypolygon
1914 ::basegfx::B2DPolygon aPoly
;
1915 aPoly
.append( aStartPoint
);
1916 aPoly
.append( aEndPoint
);
1918 internal::PolyPolyActionFactory::createPolyPolyAction(
1919 ::basegfx::B2DPolyPolygon( aPoly
),
1920 rCanvas
, rState
, aStrokeAttributes
);
1924 maActions
.push_back(
1927 io_rCurrActionIndex
) );
1929 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
1932 // else: line style is default
1933 // (i.e. invisible), don't generate action
1938 case MetaActionType::RECT
:
1940 const Rectangle
& rRect(
1941 static_cast<MetaRectAction
*>(pCurrAct
)->GetRect() );
1943 if( rRect
.IsEmpty() )
1946 const OutDevState
& rState( rStates
.getState() );
1947 const ::basegfx::B2DPoint
aTopLeftPixel(
1948 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ) );
1949 const ::basegfx::B2DPoint
aBottomRightPixel(
1950 rState
.mapModeTransform
* vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1951 // #121100# OutputDevice::DrawRect() fills
1952 // rectangles Apple-like, i.e. with one
1953 // additional pixel to the right and bottom.
1954 ::basegfx::B2DPoint(1,1) );
1956 createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
1957 ::basegfx::B2DRange( aTopLeftPixel
,
1958 aBottomRightPixel
)),
1963 case MetaActionType::ROUNDRECT
:
1965 const Rectangle
& rRect(
1966 static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetRect());
1968 if( rRect
.IsEmpty() )
1971 ::basegfx::B2DPolygon
aPoly(
1972 ::basegfx::tools::createPolygonFromRect(
1973 ::basegfx::B2DRange(
1974 vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
1975 vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1976 ::basegfx::B2DPoint(1,1) ),
1977 ( (double) static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetHorzRound() ) / rRect
.GetWidth(),
1978 ( (double) static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetVertRound() ) / rRect
.GetHeight() ) );
1979 aPoly
.transform( rStates
.getState().mapModeTransform
);
1981 createFillAndStroke( aPoly
,
1986 case MetaActionType::ELLIPSE
:
1988 const Rectangle
& rRect(
1989 static_cast<MetaEllipseAction
*>(pCurrAct
)->GetRect() );
1991 if( rRect
.IsEmpty() )
1994 const ::basegfx::B2DRange
aRange(
1995 vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
1996 vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1997 ::basegfx::B2DPoint(1,1) );
1999 ::basegfx::B2DPolygon
aPoly(
2000 ::basegfx::tools::createPolygonFromEllipse(
2003 aRange
.getHeight() ));
2004 aPoly
.transform( rStates
.getState().mapModeTransform
);
2006 createFillAndStroke( aPoly
,
2011 case MetaActionType::ARC
:
2013 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2014 const Polygon
aToolsPoly( static_cast<MetaArcAction
*>(pCurrAct
)->GetRect(),
2015 static_cast<MetaArcAction
*>(pCurrAct
)->GetStartPoint(),
2016 static_cast<MetaArcAction
*>(pCurrAct
)->GetEndPoint(), POLY_ARC
);
2017 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2018 aPoly
.transform( rStates
.getState().mapModeTransform
);
2020 createFillAndStroke( aPoly
,
2025 case MetaActionType::PIE
:
2027 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2028 const Polygon
aToolsPoly( static_cast<MetaPieAction
*>(pCurrAct
)->GetRect(),
2029 static_cast<MetaPieAction
*>(pCurrAct
)->GetStartPoint(),
2030 static_cast<MetaPieAction
*>(pCurrAct
)->GetEndPoint(), POLY_PIE
);
2031 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2032 aPoly
.transform( rStates
.getState().mapModeTransform
);
2034 createFillAndStroke( aPoly
,
2039 case MetaActionType::CHORD
:
2041 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2042 const Polygon
aToolsPoly( static_cast<MetaChordAction
*>(pCurrAct
)->GetRect(),
2043 static_cast<MetaChordAction
*>(pCurrAct
)->GetStartPoint(),
2044 static_cast<MetaChordAction
*>(pCurrAct
)->GetEndPoint(), POLY_CHORD
);
2045 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2046 aPoly
.transform( rStates
.getState().mapModeTransform
);
2048 createFillAndStroke( aPoly
,
2053 case MetaActionType::POLYLINE
:
2055 const OutDevState
& rState( rStates
.getState() );
2056 if( rState
.lineColor
.getLength() ||
2057 rState
.fillColor
.getLength() )
2059 MetaPolyLineAction
* pPolyLineAct
= static_cast<MetaPolyLineAction
*>(pCurrAct
);
2061 const LineInfo
& rLineInfo( pPolyLineAct
->GetLineInfo() );
2062 ::basegfx::B2DPolygon
aPoly( pPolyLineAct
->GetPolygon().getB2DPolygon() );
2063 aPoly
.transform( rState
.mapModeTransform
);
2065 ActionSharedPtr pLineAction
;
2067 if( rLineInfo
.IsDefault() )
2069 // plain hair line polygon
2071 internal::PolyPolyActionFactory::createLinePolyPolyAction(
2072 ::basegfx::B2DPolyPolygon(aPoly
),
2078 maActions
.push_back(
2081 io_rCurrActionIndex
) );
2083 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2086 else if( LINE_NONE
!= rLineInfo
.GetStyle() )
2088 // 'thick' line polygon
2089 rendering::StrokeAttributes aStrokeAttributes
;
2091 setupStrokeAttributes( aStrokeAttributes
,
2096 internal::PolyPolyActionFactory::createPolyPolyAction(
2097 ::basegfx::B2DPolyPolygon(aPoly
),
2100 aStrokeAttributes
) ;
2104 maActions
.push_back(
2107 io_rCurrActionIndex
) );
2109 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2112 // else: line style is default
2113 // (i.e. invisible), don't generate action
2118 case MetaActionType::POLYGON
:
2120 ::basegfx::B2DPolygon
aPoly( static_cast<MetaPolygonAction
*>(pCurrAct
)->GetPolygon().getB2DPolygon() );
2121 aPoly
.transform( rStates
.getState().mapModeTransform
);
2122 createFillAndStroke( aPoly
,
2127 case MetaActionType::POLYPOLYGON
:
2129 ::basegfx::B2DPolyPolygon
aPoly( static_cast<MetaPolyPolygonAction
*>(pCurrAct
)->GetPolyPolygon().getB2DPolyPolygon() );
2130 aPoly
.transform( rStates
.getState().mapModeTransform
);
2131 createFillAndStroke( aPoly
,
2136 case MetaActionType::BMP
:
2138 MetaBmpAction
* pAct
= static_cast<MetaBmpAction
*>(pCurrAct
);
2140 ActionSharedPtr
pBmpAction(
2141 internal::BitmapActionFactory::createBitmapAction(
2143 rStates
.getState().mapModeTransform
*
2144 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2146 rStates
.getState() ) );
2150 maActions
.push_back(
2153 io_rCurrActionIndex
) );
2155 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2160 case MetaActionType::BMPSCALE
:
2162 MetaBmpScaleAction
* pAct
= static_cast<MetaBmpScaleAction
*>(pCurrAct
);
2164 ActionSharedPtr
pBmpAction(
2165 internal::BitmapActionFactory::createBitmapAction(
2167 rStates
.getState().mapModeTransform
*
2168 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2169 rStates
.getState().mapModeTransform
*
2170 vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2172 rStates
.getState() ) );
2176 maActions
.push_back(
2179 io_rCurrActionIndex
) );
2181 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2186 case MetaActionType::BMPSCALEPART
:
2188 MetaBmpScalePartAction
* pAct
= static_cast<MetaBmpScalePartAction
*>(pCurrAct
);
2190 // crop bitmap to given source rectangle (no
2191 // need to copy and convert the whole bitmap)
2192 Bitmap
aBmp( pAct
->GetBitmap() );
2193 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2194 pAct
->GetSrcSize() );
2195 aBmp
.Crop( aCropRect
);
2197 ActionSharedPtr
pBmpAction(
2198 internal::BitmapActionFactory::createBitmapAction(
2200 rStates
.getState().mapModeTransform
*
2201 vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2202 rStates
.getState().mapModeTransform
*
2203 vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2205 rStates
.getState() ) );
2209 maActions
.push_back(
2212 io_rCurrActionIndex
) );
2214 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2219 case MetaActionType::BMPEX
:
2221 MetaBmpExAction
* pAct
= static_cast<MetaBmpExAction
*>(pCurrAct
);
2223 ActionSharedPtr
pBmpAction(
2224 internal::BitmapActionFactory::createBitmapAction(
2225 pAct
->GetBitmapEx(),
2226 rStates
.getState().mapModeTransform
*
2227 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2229 rStates
.getState() ) );
2233 maActions
.push_back(
2236 io_rCurrActionIndex
) );
2238 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2243 case MetaActionType::BMPEXSCALE
:
2245 MetaBmpExScaleAction
* pAct
= static_cast<MetaBmpExScaleAction
*>(pCurrAct
);
2247 ActionSharedPtr
pBmpAction(
2248 internal::BitmapActionFactory::createBitmapAction(
2249 pAct
->GetBitmapEx(),
2250 rStates
.getState().mapModeTransform
*
2251 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2252 rStates
.getState().mapModeTransform
*
2253 vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2255 rStates
.getState() ) );
2259 maActions
.push_back(
2262 io_rCurrActionIndex
) );
2264 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2269 case MetaActionType::BMPEXSCALEPART
:
2271 MetaBmpExScalePartAction
* pAct
= static_cast<MetaBmpExScalePartAction
*>(pCurrAct
);
2273 // crop bitmap to given source rectangle (no
2274 // need to copy and convert the whole bitmap)
2275 BitmapEx
aBmp( pAct
->GetBitmapEx() );
2276 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2277 pAct
->GetSrcSize() );
2278 aBmp
.Crop( aCropRect
);
2280 ActionSharedPtr
pBmpAction(
2281 internal::BitmapActionFactory::createBitmapAction(
2283 rStates
.getState().mapModeTransform
*
2284 vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2285 rStates
.getState().mapModeTransform
*
2286 vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2288 rStates
.getState() ) );
2292 maActions
.push_back(
2295 io_rCurrActionIndex
) );
2297 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2302 case MetaActionType::MASK
:
2304 MetaMaskAction
* pAct
= static_cast<MetaMaskAction
*>(pCurrAct
);
2306 // create masked BitmapEx right here, as the
2307 // canvas does not provide equivalent
2309 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2310 pAct
->GetColor() ));
2312 ActionSharedPtr
pBmpAction(
2313 internal::BitmapActionFactory::createBitmapAction(
2315 rStates
.getState().mapModeTransform
*
2316 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2318 rStates
.getState() ) );
2322 maActions
.push_back(
2325 io_rCurrActionIndex
) );
2327 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2332 case MetaActionType::MASKSCALE
:
2334 MetaMaskScaleAction
* pAct
= static_cast<MetaMaskScaleAction
*>(pCurrAct
);
2336 // create masked BitmapEx right here, as the
2337 // canvas does not provide equivalent
2339 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2340 pAct
->GetColor() ));
2342 ActionSharedPtr
pBmpAction(
2343 internal::BitmapActionFactory::createBitmapAction(
2345 rStates
.getState().mapModeTransform
*
2346 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2347 rStates
.getState().mapModeTransform
*
2348 vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2350 rStates
.getState() ) );
2354 maActions
.push_back(
2357 io_rCurrActionIndex
) );
2359 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2364 case MetaActionType::MASKSCALEPART
:
2366 MetaMaskScalePartAction
* pAct
= static_cast<MetaMaskScalePartAction
*>(pCurrAct
);
2368 // create masked BitmapEx right here, as the
2369 // canvas does not provide equivalent
2371 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2372 pAct
->GetColor() ));
2374 // crop bitmap to given source rectangle (no
2375 // need to copy and convert the whole bitmap)
2376 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2377 pAct
->GetSrcSize() );
2378 aBmp
.Crop( aCropRect
);
2380 ActionSharedPtr
pBmpAction(
2381 internal::BitmapActionFactory::createBitmapAction(
2383 rStates
.getState().mapModeTransform
*
2384 vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2385 rStates
.getState().mapModeTransform
*
2386 vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2388 rStates
.getState() ) );
2392 maActions
.push_back(
2395 io_rCurrActionIndex
) );
2397 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2402 case MetaActionType::GRADIENTEX
:
2403 // TODO(F1): use native Canvas gradients here
2404 // action is ignored here, because redundant to MetaActionType::GRADIENT
2407 case MetaActionType::WALLPAPER
:
2411 case MetaActionType::Transparent
:
2413 const OutDevState
& rState( rStates
.getState() );
2414 if( rState
.lineColor
.getLength() ||
2415 rState
.fillColor
.getLength() )
2417 MetaTransparentAction
* pAct
= static_cast<MetaTransparentAction
*>(pCurrAct
);
2418 ::basegfx::B2DPolyPolygon
aPoly( pAct
->GetPolyPolygon().getB2DPolyPolygon() );
2419 aPoly
.transform( rState
.mapModeTransform
);
2421 ActionSharedPtr
pPolyAction(
2422 internal::PolyPolyActionFactory::createPolyPolyAction(
2426 pAct
->GetTransparence() ) );
2430 maActions
.push_back(
2433 io_rCurrActionIndex
) );
2435 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2441 case MetaActionType::FLOATTRANSPARENT
:
2443 MetaFloatTransparentAction
* pAct
= static_cast<MetaFloatTransparentAction
*>(pCurrAct
);
2445 internal::MtfAutoPtr
pMtf(
2446 new ::GDIMetaFile( pAct
->GetGDIMetaFile() ) );
2448 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2449 internal::GradientAutoPtr
pGradient(
2450 new Gradient( pAct
->GetGradient() ) );
2452 DBG_TESTSOLARMUTEX();
2454 ActionSharedPtr
pFloatTransAction(
2455 internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2457 std::move(pGradient
),
2459 rStates
.getState().mapModeTransform
*
2460 vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2461 rStates
.getState().mapModeTransform
*
2462 vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2464 rStates
.getState() ) );
2466 if( pFloatTransAction
)
2468 maActions
.push_back(
2471 io_rCurrActionIndex
) );
2473 io_rCurrActionIndex
+= pFloatTransAction
->getActionCount()-1;
2478 case MetaActionType::TEXT
:
2480 MetaTextAction
* pAct
= static_cast<MetaTextAction
*>(pCurrAct
);
2481 OUString sText
= pAct
->GetText();
2483 if (rVDev
.GetDigitLanguage())
2484 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2486 const sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
2495 bSubsettableActions
);
2499 case MetaActionType::TEXTARRAY
:
2501 MetaTextArrayAction
* pAct
= static_cast<MetaTextArrayAction
*>(pCurrAct
);
2502 OUString sText
= pAct
->GetText();
2504 if (rVDev
.GetDigitLanguage())
2505 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2507 const sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
2516 bSubsettableActions
);
2520 case MetaActionType::TEXTLINE
:
2522 MetaTextLineAction
* pAct
= static_cast<MetaTextLineAction
*>(pCurrAct
);
2524 const OutDevState
& rState( rStates
.getState() );
2525 const ::Size
aBaselineOffset( tools::getBaselineOffset( rState
,
2527 const ::basegfx::B2DSize
aSize( rState
.mapModeTransform
*
2528 ::basegfx::B2DSize(pAct
->GetWidth(),
2531 ActionSharedPtr
pPolyAction(
2532 PolyPolyActionFactory::createPolyPolyAction(
2533 tools::createTextLinesPolyPolygon(
2534 rState
.mapModeTransform
*
2535 ::basegfx::B2DPoint(
2536 vcl::unotools::b2DPointFromPoint(pAct
->GetStartPoint()) +
2537 vcl::unotools::b2DSizeFromSize(aBaselineOffset
)),
2539 tools::createTextLineInfo( rVDev
,
2544 if( pPolyAction
.get() )
2546 maActions
.push_back(
2549 io_rCurrActionIndex
) );
2551 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2556 case MetaActionType::TEXTRECT
:
2558 MetaTextRectAction
* pAct
= static_cast<MetaTextRectAction
*>(pCurrAct
);
2560 rStates
.pushState(PushFlags::ALL
);
2562 // use the VDev to break up the text rect
2563 // action into readily formatted lines
2564 GDIMetaFile aTmpMtf
;
2565 rVDev
.AddTextRectActions( pAct
->GetRect(),
2570 createActions( aTmpMtf
,
2572 bSubsettableActions
);
2579 case MetaActionType::STRETCHTEXT
:
2581 MetaStretchTextAction
* pAct
= static_cast<MetaStretchTextAction
*>(pCurrAct
);
2582 OUString sText
= pAct
->GetText();
2584 if (rVDev
.GetDigitLanguage())
2585 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2587 const sal_Int32 nLen
= std::min(pAct
->GetLen(), pAct
->GetText().getLength() - pAct
->GetIndex());
2589 // #i70897# Nothing to do, actually...
2593 // have to fit the text into the given
2594 // width. This is achieved by internally
2595 // generating a DX array, and uniformly
2596 // distributing the excess/insufficient width
2597 // to every logical character.
2598 ::boost::scoped_array
< long > pDXArray( new long[nLen
] );
2600 rVDev
.GetTextArray( pAct
->GetText(), pDXArray
.get(),
2601 pAct
->GetIndex(), pAct
->GetLen() );
2603 const sal_Int32
nWidthDifference( pAct
->GetWidth() - pDXArray
[ nLen
-1 ] );
2605 // Last entry of pDXArray contains total width of the text
2606 long* p
= pDXArray
.get();
2607 for (sal_Int32 i
= 1; i
<= nLen
; ++i
)
2609 // calc ratio for every array entry, to
2610 // distribute rounding errors 'evenly'
2611 // across the characters. Note that each
2612 // entry represents the 'end' position of
2613 // the corresponding character, thus, we
2614 // let i run from 1 to nLen.
2615 *p
++ += (long)i
*nWidthDifference
/nLen
;
2625 bSubsettableActions
);
2630 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
2634 // increment action index (each mtf action counts _at
2635 // least_ one. Some count for more, therefore,
2636 // io_rCurrActionIndex is sometimes incremented by
2637 // pAct->getActionCount()-1 above, the -1 being the
2638 // correction for the unconditional increment here).
2639 ++io_rCurrActionIndex
;
2648 class ActionRenderer
2651 ActionRenderer( const ::basegfx::B2DHomMatrix
& rTransformation
) :
2652 maTransformation( rTransformation
),
2662 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2664 // ANDing the result. We want to fail if at least
2665 // one action failed.
2666 mbRet
&= rAction
.mpAction
->render( maTransformation
);
2669 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2670 const Action::Subset
& rSubset
)
2672 // ANDing the result. We want to fail if at least
2673 // one action failed.
2674 mbRet
&= rAction
.mpAction
->renderSubset( maTransformation
,
2679 ::basegfx::B2DHomMatrix maTransformation
;
2686 AreaQuery( const ::basegfx::B2DHomMatrix
& rTransformation
) :
2687 maTransformation( rTransformation
),
2692 static bool result()
2694 return true; // nothing can fail here
2697 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2699 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
) );
2702 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2703 const Action::Subset
& rSubset
)
2705 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
,
2709 ::basegfx::B2DRange
getBounds() const
2715 ::basegfx::B2DHomMatrix maTransformation
;
2716 ::basegfx::B2DRange maBounds
;
2719 // Doing that via inline class. Compilers tend to not inline free
2721 struct UpperBoundActionIndexComparator
2723 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rLHS
,
2724 const ::cppcanvas::internal::ImplRenderer::MtfAction
& rRHS
)
2726 const sal_Int32
nLHSCount( rLHS
.mpAction
?
2727 rLHS
.mpAction
->getActionCount() : 0 );
2728 const sal_Int32
nRHSCount( rRHS
.mpAction
?
2729 rRHS
.mpAction
->getActionCount() : 0 );
2731 // compare end of action range, to have an action selected
2732 // by lower_bound even if the requested index points in
2733 // the middle of the action's range
2734 return rLHS
.mnOrigIndex
+ nLHSCount
< rRHS
.mnOrigIndex
+ nRHSCount
;
2738 /** Algorithm to apply given functor to a subset range
2742 Functor to call for each element of the subset
2743 range. Must provide the following method signatures:
2744 bool result() (returning false if operation failed)
2747 template< typename Functor
> bool
2748 forSubsetRange( Functor
& rFunctor
,
2749 ImplRenderer::ActionVector::const_iterator aRangeBegin
,
2750 ImplRenderer::ActionVector::const_iterator aRangeEnd
,
2751 sal_Int32 nStartIndex
,
2752 sal_Int32 nEndIndex
,
2753 const ImplRenderer::ActionVector::const_iterator
& rEnd
)
2755 if( aRangeBegin
== aRangeEnd
)
2757 // only a single action. Setup subset, and call functor
2758 Action::Subset aSubset
;
2759 aSubset
.mnSubsetBegin
= ::std::max( sal_Int32( 0 ),
2760 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2761 aSubset
.mnSubsetEnd
= ::std::min( aRangeBegin
->mpAction
->getActionCount(),
2762 nEndIndex
- aRangeBegin
->mnOrigIndex
);
2764 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2765 "ImplRenderer::forSubsetRange(): Invalid indices" );
2767 rFunctor( *aRangeBegin
, aSubset
);
2771 // more than one action.
2773 // render partial first, full intermediate, and
2774 // partial last action
2775 Action::Subset aSubset
;
2776 aSubset
.mnSubsetBegin
= ::std::max( sal_Int32( 0 ),
2777 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2778 aSubset
.mnSubsetEnd
= aRangeBegin
->mpAction
->getActionCount();
2780 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2781 "ImplRenderer::forSubsetRange(): Invalid indices" );
2783 rFunctor( *aRangeBegin
, aSubset
);
2785 // first action rendered, skip to next
2788 // render full middle actions
2789 while( aRangeBegin
!= aRangeEnd
)
2790 rFunctor( *aRangeBegin
++ );
2792 if( aRangeEnd
== rEnd
||
2793 aRangeEnd
->mnOrigIndex
> nEndIndex
)
2795 // aRangeEnd denotes end of action vector,
2799 // nEndIndex references something _after_
2800 // aRangeBegin, but _before_ aRangeEnd
2802 // either way: no partial action left
2803 return rFunctor
.result();
2806 aSubset
.mnSubsetBegin
= 0;
2807 aSubset
.mnSubsetEnd
= nEndIndex
- aRangeEnd
->mnOrigIndex
;
2809 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2810 "ImplRenderer::forSubsetRange(): Invalid indices" );
2812 rFunctor( *aRangeEnd
, aSubset
);
2815 return rFunctor
.result();
2819 bool ImplRenderer::getSubsetIndices( sal_Int32
& io_rStartIndex
,
2820 sal_Int32
& io_rEndIndex
,
2821 ActionVector::const_iterator
& o_rRangeBegin
,
2822 ActionVector::const_iterator
& o_rRangeEnd
) const
2824 ENSURE_OR_RETURN_FALSE( io_rStartIndex
<=io_rEndIndex
,
2825 "ImplRenderer::getSubsetIndices(): invalid action range" );
2827 ENSURE_OR_RETURN_FALSE( !maActions
.empty(),
2828 "ImplRenderer::getSubsetIndices(): no actions to render" );
2830 const sal_Int32
nMinActionIndex( maActions
.front().mnOrigIndex
);
2831 const sal_Int32
nMaxActionIndex( maActions
.back().mnOrigIndex
+
2832 maActions
.back().mpAction
->getActionCount() );
2834 // clip given range to permissible values (there might be
2835 // ranges before and behind the valid indices)
2836 io_rStartIndex
= ::std::max( nMinActionIndex
,
2838 io_rEndIndex
= ::std::min( nMaxActionIndex
,
2841 if( io_rStartIndex
== io_rEndIndex
||
2842 io_rStartIndex
> io_rEndIndex
)
2844 // empty range, don't render anything. The second
2845 // condition e.g. happens if the requested range lies
2846 // fully before or behind the valid action indices.
2851 const ActionVector::const_iterator
aBegin( maActions
.begin() );
2852 const ActionVector::const_iterator
aEnd( maActions
.end() );
2855 // find start and end action
2856 // =========================
2857 o_rRangeBegin
= ::std::lower_bound( aBegin
, aEnd
,
2858 MtfAction( ActionSharedPtr(), io_rStartIndex
),
2859 UpperBoundActionIndexComparator() );
2860 o_rRangeEnd
= ::std::lower_bound( aBegin
, aEnd
,
2861 MtfAction( ActionSharedPtr(), io_rEndIndex
),
2862 UpperBoundActionIndexComparator() );
2870 ImplRenderer::ImplRenderer( const CanvasSharedPtr
& rCanvas
,
2871 const GDIMetaFile
& rMtf
,
2872 const Parameters
& rParams
)
2873 : CanvasGraphicHelper(rCanvas
)
2888 , mbMultipart(false)
2891 memset (aObjects
, 0, sizeof (aObjects
));
2893 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2895 OSL_ENSURE( rCanvas
.get() != NULL
&& rCanvas
->getUNOCanvas().is(),
2896 "ImplRenderer::ImplRenderer(): Invalid canvas" );
2897 OSL_ENSURE( rCanvas
->getUNOCanvas()->getDevice().is(),
2898 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2900 // make sure canvas and graphic device are valid; action
2901 // creation don't check that every time
2902 if( rCanvas
.get() == NULL
||
2903 !rCanvas
->getUNOCanvas().is() ||
2904 !rCanvas
->getUNOCanvas()->getDevice().is() )
2906 // leave actions empty
2910 VectorOfOutDevStates aStateStack
;
2912 ScopedVclPtrInstance
< VirtualDevice
> aVDev
;
2913 aVDev
->EnableOutput( false );
2915 // Setup VDev for state tracking and mapping
2916 // =========================================
2918 aVDev
->SetMapMode( rMtf
.GetPrefMapMode() );
2920 const Size
aMtfSize( rMtf
.GetPrefSize() );
2921 const Size
aMtfSizePixPre( aVDev
->LogicToPixel( aMtfSize
,
2922 rMtf
.GetPrefMapMode() ) );
2924 // #i44110# correct null-sized output - there are shapes
2925 // which have zero size in at least one dimension
2926 const Size
aMtfSizePix( ::std::max( aMtfSizePixPre
.Width(), 1L ),
2927 ::std::max( aMtfSizePixPre
.Height(), 1L ) );
2929 sal_Int32
nCurrActions(0);
2930 ActionFactoryParameters
aParms(aStateStack
,
2937 aStateStack
.clearStateStack();
2939 // Setup local state, such that the metafile renders
2940 // itself into a one-by-one square at the origin for
2941 // identity view and render transformations
2942 aStateStack
.getState().transform
.scale( 1.0 / aMtfSizePix
.Width(),
2943 1.0 / aMtfSizePix
.Height() );
2945 tools::calcLogic2PixelAffineTransform( aStateStack
.getState().mapModeTransform
,
2948 ColorSharedPtr
pColor( getCanvas()->createColor() );
2951 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2952 // setup default text color to black
2954 rState
.textFillColor
=
2955 rState
.textLineColor
= pColor
->getDeviceColor( 0x000000FF );
2958 // apply overrides from the Parameters struct
2959 if( rParams
.maFillColor
.is_initialized() )
2961 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2962 rState
.isFillColorSet
= true;
2963 rState
.fillColor
= pColor
->getDeviceColor( *rParams
.maFillColor
);
2965 if( rParams
.maLineColor
.is_initialized() )
2967 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2968 rState
.isLineColorSet
= true;
2969 rState
.lineColor
= pColor
->getDeviceColor( *rParams
.maLineColor
);
2971 if( rParams
.maTextColor
.is_initialized() )
2973 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2974 rState
.isTextFillColorSet
= true;
2975 rState
.isTextLineColorSet
= true;
2977 rState
.textFillColor
=
2978 rState
.textLineColor
= pColor
->getDeviceColor( *rParams
.maTextColor
);
2980 if( rParams
.maFontName
.is_initialized() ||
2981 rParams
.maFontWeight
.is_initialized() ||
2982 rParams
.maFontLetterForm
.is_initialized() ||
2983 rParams
.maFontUnderline
.is_initialized() ||
2984 rParams
.maFontProportion
.is_initialized() )
2986 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2988 rState
.xFont
= createFont( rState
.fontRotation
,
2989 vcl::Font(), // default font
2994 createActions( const_cast<GDIMetaFile
&>(rMtf
), // HACK(Q2):
3003 true // TODO(P1): make subsettability configurable
3007 ImplRenderer::~ImplRenderer()
3009 // don't leak EMFPObjects
3010 for(unsigned int i
=0; i
<SAL_N_ELEMENTS(aObjects
); ++i
)
3014 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex
,
3015 sal_Int32 nEndIndex
) const
3017 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::drawSubset()" );
3019 ActionVector::const_iterator aRangeBegin
;
3020 ActionVector::const_iterator aRangeEnd
;
3024 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
3025 aRangeBegin
, aRangeEnd
) )
3026 return true; // nothing to render (but _that_ was successful)
3028 // now, aRangeBegin references the action in which the
3029 // subset rendering must start, and aRangeEnd references
3030 // the action in which the subset rendering must end (it
3031 // might also end right at the start of the referenced
3032 // action, such that zero of that action needs to be
3036 // render subset of actions
3037 // ========================
3039 ::basegfx::B2DHomMatrix aMatrix
;
3040 ::canvas::tools::getRenderStateTransform( aMatrix
,
3043 ActionRenderer
aRenderer( aMatrix
);
3045 return forSubsetRange( aRenderer
,
3052 catch( uno::Exception
& )
3054 SAL_WARN("cppcanvas.emf", "" << OUStringToOString(
3055 comphelper::anyToString( cppu::getCaughtException() ),
3056 RTL_TEXTENCODING_UTF8
).getStr() );
3058 // convert error to return value
3063 ::basegfx::B2DRange
ImplRenderer::getSubsetArea( sal_Int32 nStartIndex
,
3064 sal_Int32 nEndIndex
) const
3066 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3068 ActionVector::const_iterator aRangeBegin
;
3069 ActionVector::const_iterator aRangeEnd
;
3071 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
3072 aRangeBegin
, aRangeEnd
) )
3073 return ::basegfx::B2DRange(); // nothing to render -> empty range
3075 // now, aRangeBegin references the action in which the
3076 // subset querying must start, and aRangeEnd references
3077 // the action in which the subset querying must end (it
3078 // might also end right at the start of the referenced
3079 // action, such that zero of that action needs to be
3083 // query bounds for subset of actions
3084 // ==================================
3086 ::basegfx::B2DHomMatrix aMatrix
;
3087 ::canvas::tools::getRenderStateTransform( aMatrix
,
3090 AreaQuery
aQuery( aMatrix
);
3091 forSubsetRange( aQuery
,
3098 return aQuery
.getBounds();
3101 bool ImplRenderer::draw() const
3103 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::draw()" );
3105 ::basegfx::B2DHomMatrix aMatrix
;
3106 ::canvas::tools::getRenderStateTransform( aMatrix
,
3111 return ::std::for_each( maActions
.begin(), maActions
.end(), ActionRenderer( aMatrix
) ).result();
3113 catch( uno::Exception
& )
3115 SAL_WARN( "cppcanvas.emf", "" << OUStringToOString(
3116 comphelper::anyToString( cppu::getCaughtException() ),
3117 RTL_TEXTENCODING_UTF8
).getStr() );
3125 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */