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>
78 #include <boost/scoped_array.hpp>
79 #include "mtftools.hxx"
80 #include "outdevstate.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 // set rIsColorSet and check for true at the same time
96 if( (rIsColorSet
=pAct
->IsSetting()) != false )
98 ::Color
aColor( pAct
->GetColor() );
100 // force alpha part of color to
101 // opaque. transparent painting is done
102 // explicitly via META_TRANSPARENT_ACTION
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
189 // ===================================================
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
246 // ------------------------
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(sal_uInt16 nFlags
)
266 m_aStates
.push_back( getState() );
267 getState().pushFlags
= nFlags
;
270 void VectorOfOutDevStates::popState()
272 if( getState().pushFlags
!= PUSH_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
& PUSH_LINECOLOR
) )
293 aCalculatedNewState
.lineColor
= rNewState
.lineColor
;
294 aCalculatedNewState
.isLineColorSet
= rNewState
.isLineColorSet
;
297 if( (aCalculatedNewState
.pushFlags
& PUSH_FILLCOLOR
) )
299 aCalculatedNewState
.fillColor
= rNewState
.fillColor
;
300 aCalculatedNewState
.isFillColorSet
= rNewState
.isFillColorSet
;
303 if( (aCalculatedNewState
.pushFlags
& PUSH_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
& PUSH_TEXTCOLOR
) )
319 aCalculatedNewState
.textColor
= rNewState
.textColor
;
322 if( (aCalculatedNewState
.pushFlags
& PUSH_MAPMODE
) )
324 aCalculatedNewState
.mapModeTransform
= rNewState
.mapModeTransform
;
327 if( (aCalculatedNewState
.pushFlags
& PUSH_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 & PUSH_RASTEROP) )
339 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTFILLCOLOR
) )
341 aCalculatedNewState
.textFillColor
= rNewState
.textFillColor
;
342 aCalculatedNewState
.isTextFillColorSet
= rNewState
.isTextFillColorSet
;
345 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTALIGN
) )
347 aCalculatedNewState
.textReferencePoint
= rNewState
.textReferencePoint
;
350 // TODO(F1): Refpoint handling NYI
351 // if( (aCalculatedNewState.pushFlags & PUSH_REFPOINT) )
355 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTLINECOLOR
) )
357 aCalculatedNewState
.textLineColor
= rNewState
.textLineColor
;
358 aCalculatedNewState
.isTextLineColorSet
= rNewState
.isTextLineColorSet
;
361 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTLAYOUTMODE
) )
363 aCalculatedNewState
.textAlignment
= rNewState
.textAlignment
;
364 aCalculatedNewState
.textDirection
= rNewState
.textDirection
;
367 // TODO(F2): Text language handling NYI
368 // if( (aCalculatedNewState.pushFlags & PUSH_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
) const
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() == META_COMMENT_ACTION
&&
434 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase(
437 // requested comment found, done
446 bool ImplRenderer::isActionContained( GDIMetaFile
& rMtf
,
447 const char* pCommentString
,
448 sal_uInt16 nType
) const
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() == META_COMMENT_ACTION
&&
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 ::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
530 // ----------------------------
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(PUSH_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
,
757 const ActionFactoryParameters
& rParms
) const
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)
812 const ::Size
rFontSizeLog( rFont
.GetSize() );
813 const sal_Int32 nFontWidthLog
= rFontSizeLog
.Width();
814 if( nFontWidthLog
!= 0 )
816 ::Font aTestFont
= rFont
;
817 aTestFont
.SetWidth( 0 );
818 sal_Int32 nNormalWidth
= rParms
.mrVDev
.GetFontMetric( aTestFont
).GetWidth();
819 if( nNormalWidth
!= nFontWidthLog
)
821 aFontMatrix
.m00
= (double)nFontWidthLog
/ nNormalWidth
;
824 // #i52608# apply map mode scale also to font matrix - an
825 // anisotrophic mapmode must be reflected in an
826 // anisotrophic font matrix scale.
827 const OutDevState
& rState( rParms
.mrStates
.getState() );
828 if( !::basegfx::fTools::equal(
829 rState
.mapModeTransform
.get(0,0),
830 rState
.mapModeTransform
.get(1,1)) )
832 const double nScaleX( rState
.mapModeTransform
.get(0,0) );
833 const double nScaleY( rState
.mapModeTransform
.get(1,1) );
835 // note: no reason to check for division by zero, we
836 // always have the value closer (or equal) to zero as
838 if( fabs(nScaleX
) < fabs(nScaleY
) )
839 aFontMatrix
.m00
*= nScaleX
/ nScaleY
;
841 aFontMatrix
.m11
*= nScaleY
/ nScaleX
;
843 aFontRequest
.CellSize
= (rState
.mapModeTransform
* ::vcl::unotools::b2DSizeFromSize(rFontSizeLog
)).getY();
845 return rParms
.mrCanvas
->getUNOCanvas()->createFont( aFontRequest
,
846 uno::Sequence
< beans::PropertyValue
>(),
850 // create text effects such as shadow/relief/embossed
851 void ImplRenderer::createTextAction( const ::Point
& rStartPoint
,
852 const OUString rString
,
855 const sal_Int32
* pCharWidths
,
856 const ActionFactoryParameters
& rParms
,
857 bool bSubsettableActions
)
859 ENSURE_OR_THROW( nIndex
>= 0 && nLength
<= rString
.getLength() + nIndex
,
860 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
863 return; // zero-length text, no visible output
865 const OutDevState
& rState( rParms
.mrStates
.getState() );
867 // TODO(F2): implement all text effects
868 // if( rState.textAlignment ); // TODO(F2): NYI
870 ::Color
aShadowColor( COL_AUTO
);
871 ::Color
aReliefColor( COL_AUTO
);
872 ::Size aShadowOffset
;
873 ::Size aReliefOffset
;
875 uno::Reference
<rendering::XColorSpace
> xColorSpace(
876 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
878 if( rState
.isTextEffectShadowSet
)
880 // calculate shadow offset (similar to outdev3.cxx)
881 // TODO(F3): better match with outdev3.cxx
882 sal_Int32 nShadowOffset
= static_cast<sal_Int32
>(1.5 + ((rParms
.mrVDev
.GetFont().GetHeight()-24.0)/24.0));
883 if( nShadowOffset
< 1 )
886 aShadowOffset
.setWidth( nShadowOffset
);
887 aShadowOffset
.setHeight( nShadowOffset
);
889 // determine shadow color (from outdev3.cxx)
890 ::Color aTextColor
= ::vcl::unotools::doubleSequenceToColor(
891 rState
.textColor
, xColorSpace
);
892 bool bIsDark
= (aTextColor
.GetColor() == COL_BLACK
)
893 || (aTextColor
.GetLuminance() < 8);
895 aShadowColor
= bIsDark
? COL_LIGHTGRAY
: COL_BLACK
;
896 aShadowColor
.SetTransparency( aTextColor
.GetTransparency() );
899 if( rState
.textReliefStyle
)
901 // calculate relief offset (similar to outdev3.cxx)
902 sal_Int32 nReliefOffset
= rParms
.mrVDev
.PixelToLogic( Size( 1, 1 ) ).Height();
903 nReliefOffset
+= nReliefOffset
/2;
904 if( nReliefOffset
< 1 )
907 if( rState
.textReliefStyle
== RELIEF_ENGRAVED
)
908 nReliefOffset
= -nReliefOffset
;
910 aReliefOffset
.setWidth( nReliefOffset
);
911 aReliefOffset
.setHeight( nReliefOffset
);
913 // determine relief color (from outdev3.cxx)
914 ::Color aTextColor
= ::vcl::unotools::doubleSequenceToColor(
915 rState
.textColor
, xColorSpace
);
917 aReliefColor
= ::Color( COL_LIGHTGRAY
);
919 // we don't have a automatic color, so black is always
920 // drawn on white (literally copied from
921 // vcl/source/gdi/outdev3.cxx)
922 if( aTextColor
.GetColor() == COL_BLACK
)
924 aTextColor
= ::Color( COL_WHITE
);
925 rParms
.mrStates
.getState().textColor
=
926 ::vcl::unotools::colorToDoubleSequence(
927 aTextColor
, xColorSpace
);
930 if( aTextColor
.GetColor() == COL_WHITE
)
931 aReliefColor
= ::Color( COL_BLACK
);
932 aReliefColor
.SetTransparency( aTextColor
.GetTransparency() );
935 // create the actual text action
936 ActionSharedPtr
pTextAction(
937 TextActionFactory::createTextAction(
951 bSubsettableActions
) );
953 ActionSharedPtr pStrikeoutTextAction
;
955 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
|| rState
.textStrikeoutStyle
== STRIKEOUT_SLASH
)
957 long nWidth
= rParms
.mrVDev
.GetTextWidth( rString
,nIndex
,nLength
);
959 sal_Unicode pChars
[4];
960 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
)
964 pChars
[3]=pChars
[2]=pChars
[1]=pChars
[0];
966 long nStrikeoutWidth
= (rParms
.mrVDev
.GetTextWidth(
967 OUString(pChars
, SAL_N_ELEMENTS(pChars
))) + 2) / 4;
969 if( nStrikeoutWidth
<= 0 )
972 long nMaxWidth
= nStrikeoutWidth
/2;
975 nMaxWidth
+= nWidth
+ 1;
977 long nFullStrikeoutWidth
= 0;
978 OUString aStrikeoutText
;
979 while( (nFullStrikeoutWidth
+=nStrikeoutWidth
) < nMaxWidth
+1 )
980 aStrikeoutText
+= OUString(pChars
[0]);
982 sal_Int32 nLen
= aStrikeoutText
.getLength();
986 long nInterval
= ( nWidth
- nStrikeoutWidth
* nLen
) / nLen
;
987 nStrikeoutWidth
+= nInterval
;
988 sal_Int32
* pStrikeoutCharWidths
= new sal_Int32
[nLen
];
990 for ( int i
= 0;i
<nLen
; i
++)
992 pStrikeoutCharWidths
[i
] = nStrikeoutWidth
;
995 for ( int i
= 1;i
< nLen
; i
++ )
997 pStrikeoutCharWidths
[ i
] += pStrikeoutCharWidths
[ i
-1 ];
1000 sal_Int32 nStartPos
= 0;
1002 pStrikeoutTextAction
=
1003 TextActionFactory::createTextAction(
1011 aStrikeoutText
.getLength(),
1012 pStrikeoutCharWidths
,
1017 bSubsettableActions
) ;
1023 maActions
.push_back(
1026 rParms
.mrCurrActionIndex
) );
1028 if ( pStrikeoutTextAction
)
1030 maActions
.push_back(
1032 pStrikeoutTextAction
,
1033 rParms
.mrCurrActionIndex
) );
1036 rParms
.mrCurrActionIndex
+= pTextAction
->getActionCount()-1;
1040 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon
& rClipPoly
,
1041 const ActionFactoryParameters
& rParms
,
1044 ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
1045 ::basegfx::B2DPolyPolygon
aClipPoly( rClipPoly
);
1047 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1048 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1050 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1051 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1054 (bEmptyClipRect
&& bEmptyClipPoly
) )
1056 rState
.clip
= rClipPoly
;
1060 if( !bEmptyClipRect
)
1062 // TODO(P3): Use Liang-Barsky polygon clip here,
1063 // after all, one object is just a rectangle!
1065 // convert rect to polygon beforehand, must revert
1066 // to general polygon clipping here.
1067 rState
.clip
= ::basegfx::B2DPolyPolygon(
1068 ::basegfx::tools::createPolygonFromRect(
1069 // #121100# VCL rectangular clips always
1070 // include one more pixel to the right
1072 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1073 rState
.clipRect
.Top(),
1074 rState
.clipRect
.Right()+1,
1075 rState
.clipRect
.Bottom()+1 ) ) );
1079 rState
.clip
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
1080 aClipPoly
, rState
.clip
, true, false);
1083 // by now, our clip resides in the OutDevState::clip
1085 rState
.clipRect
.SetEmpty();
1087 if( rState
.clip
.count() == 0 )
1089 if( rState
.clipRect
.IsEmpty() )
1091 rState
.xClipPoly
.clear();
1095 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1096 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1097 ::basegfx::B2DPolyPolygon(
1098 ::basegfx::tools::createPolygonFromRect(
1099 // #121100# VCL rectangular clips
1100 // always include one more pixel to
1101 // the right and the bottom
1102 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1103 rState
.clipRect
.Top(),
1104 rState
.clipRect
.Right()+1,
1105 rState
.clipRect
.Bottom()+1 ) ) ) );
1110 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1111 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1116 void ImplRenderer::updateClipping( const ::Rectangle
& rClipRect
,
1117 const ActionFactoryParameters
& rParms
,
1120 ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
1122 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1123 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1125 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1126 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1129 (bEmptyClipRect
&& bEmptyClipPoly
) )
1131 rState
.clipRect
= rClipRect
;
1132 rState
.clip
.clear();
1134 else if( bEmptyClipPoly
)
1136 rState
.clipRect
.Intersection( rClipRect
);
1137 rState
.clip
.clear();
1141 // TODO(P3): Handle a fourth case here, when all clip
1142 // polygons are rectangular, once B2DMultiRange's
1143 // sweep line implementation is done.
1145 // general case: convert to polygon and clip
1146 // -----------------------------------------
1148 // convert rect to polygon beforehand, must revert
1149 // to general polygon clipping here.
1150 ::basegfx::B2DPolyPolygon
aClipPoly(
1151 ::basegfx::tools::createPolygonFromRect(
1152 ::basegfx::B2DRectangle( rClipRect
.Left(),
1155 rClipRect
.Bottom() ) ) );
1157 rState
.clipRect
.SetEmpty();
1160 rState
.clip
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
1161 aClipPoly
, rState
.clip
, true, false);
1164 if( rState
.clip
.count() == 0 )
1166 if( rState
.clipRect
.IsEmpty() )
1168 rState
.xClipPoly
.clear();
1172 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1173 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1174 ::basegfx::B2DPolyPolygon(
1175 ::basegfx::tools::createPolygonFromRect(
1176 // #121100# VCL rectangular clips
1177 // always include one more pixel to
1178 // the right and the bottom
1179 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1180 rState
.clipRect
.Top(),
1181 rState
.clipRect
.Right()+1,
1182 rState
.clipRect
.Bottom()+1 ) ) ) );
1187 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1188 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1193 bool ImplRenderer::createActions( GDIMetaFile
& rMtf
,
1194 const ActionFactoryParameters
& rFactoryParms
,
1195 bool bSubsettableActions
)
1197 /* TODO(P2): interpret mtf-comments
1198 ================================
1200 - gradient fillings (do that via comments)
1202 - think about mapping. _If_ we do everything in logical
1203 coordinates (which would solve the probs for stroke
1204 widths and text offsets), then we would have to
1205 recalc scaling for every drawing operation. This is
1206 because the outdev map mode might change at any time.
1207 Also keep in mind, that, although we've double precision
1208 float arithmetic now, different offsets might still
1209 generate different roundings (aka
1210 'OutputDevice::SetPixelOffset())
1214 // alias common parameters
1215 VectorOfOutDevStates
& rStates(rFactoryParms
.mrStates
);
1216 const CanvasSharedPtr
& rCanvas(rFactoryParms
.mrCanvas
);
1217 ::VirtualDevice
& rVDev(rFactoryParms
.mrVDev
);
1218 const Parameters
& rParms(rFactoryParms
.mrParms
);
1219 sal_Int32
& io_rCurrActionIndex(rFactoryParms
.mrCurrActionIndex
);
1222 // Loop over every metaaction
1223 // ==========================
1224 MetaAction
* pCurrAct
;
1226 // TODO(P1): think about caching
1227 for( pCurrAct
=rMtf
.FirstAction();
1229 pCurrAct
= rMtf
.NextAction() )
1231 // execute every action, to keep VDev state up-to-date
1232 // currently used only for
1234 // - the line/fill color when processing a META_TRANSPARENT_ACTION
1235 // - SetFont to process font metric specific actions
1236 pCurrAct
->Execute( &rVDev
);
1238 SAL_INFO("cppcanvas.emf", "MTF\trecord type: 0x" << pCurrAct
->GetType() << " (" << pCurrAct
->GetType() << ")");
1240 switch( pCurrAct
->GetType() )
1242 // ------------------------------------------------------------
1244 // In the first part of this monster-switch, we
1245 // handle all state-changing meta actions. These
1246 // are all handled locally.
1248 // ------------------------------------------------------------
1250 case META_PUSH_ACTION
:
1252 MetaPushAction
* pPushAction
= static_cast<MetaPushAction
*>(pCurrAct
);
1253 rStates
.pushState(pPushAction
->GetFlags());
1257 case META_POP_ACTION
:
1261 case META_TEXTLANGUAGE_ACTION
:
1262 // FALLTHROUGH intended
1263 case META_REFPOINT_ACTION
:
1264 // handled via pCurrAct->Execute( &rVDev )
1267 case META_MAPMODE_ACTION
:
1268 // modify current mapModeTransformation
1269 // transformation, such that subsequent
1270 // coordinates map correctly
1271 tools::calcLogic2PixelAffineTransform( rStates
.getState().mapModeTransform
,
1275 // monitor clip regions, to assemble clip polygon on our own
1276 case META_CLIPREGION_ACTION
:
1278 MetaClipRegionAction
* pClipAction
= static_cast<MetaClipRegionAction
*>(pCurrAct
);
1280 if( !pClipAction
->IsClipping() )
1283 rStates
.getState().clip
.clear();
1287 if( !pClipAction
->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1289 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1290 "region encountered, falling back to bounding box!" );
1292 // #121806# explicitly kept integer
1293 Rectangle
aClipRect(
1295 pClipAction
->GetRegion().GetBoundRect() ) );
1297 // intersect current clip with given rect
1305 // set new clip polygon (don't intersect
1306 // with old one, just set it)
1308 // #121806# explicitly kept integer
1309 basegfx::B2DPolyPolygon
aPolyPolygon(pClipAction
->GetRegion().GetAsB2DPolyPolygon());
1311 aPolyPolygon
.transform(rVDev
.GetViewTransformation());
1322 case META_ISECTRECTCLIPREGION_ACTION
:
1324 MetaISectRectClipRegionAction
* pClipAction
= static_cast<MetaISectRectClipRegionAction
*>(pCurrAct
);
1326 // #121806# explicitly kept integer
1327 Rectangle
aClipRect(
1328 rVDev
.LogicToPixel( pClipAction
->GetRect() ) );
1330 // intersect current clip with given rect
1339 case META_ISECTREGIONCLIPREGION_ACTION
:
1341 MetaISectRegionClipRegionAction
* pClipAction
= static_cast<MetaISectRegionClipRegionAction
*>(pCurrAct
);
1343 if( !pClipAction
->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1345 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1346 "region encountered, falling back to bounding box!" );
1348 // #121806# explicitly kept integer
1349 Rectangle
aClipRect(
1350 rVDev
.LogicToPixel( pClipAction
->GetRegion().GetBoundRect() ) );
1352 // intersect current clip with given rect
1360 // intersect current clip with given clip polygon
1362 // #121806# explicitly kept integer
1363 basegfx::B2DPolyPolygon
aPolyPolygon(pClipAction
->GetRegion().GetAsB2DPolyPolygon());
1365 aPolyPolygon
.transform(rVDev
.GetViewTransformation());
1375 case META_MOVECLIPREGION_ACTION
:
1379 case META_LINECOLOR_ACTION
:
1380 if( !rParms
.maLineColor
.is_initialized() )
1382 setStateColor( static_cast<MetaLineColorAction
*>(pCurrAct
),
1383 rStates
.getState().isLineColorSet
,
1384 rStates
.getState().lineColor
,
1389 // #120994# Do switch on/off LineColor, even when a overriding one is set
1390 bool bSetting(static_cast<MetaLineColorAction
*>(pCurrAct
)->IsSetting());
1392 rStates
.getState().isLineColorSet
= bSetting
;
1396 case META_FILLCOLOR_ACTION
:
1397 if( !rParms
.maFillColor
.is_initialized() )
1399 setStateColor( static_cast<MetaFillColorAction
*>(pCurrAct
),
1400 rStates
.getState().isFillColorSet
,
1401 rStates
.getState().fillColor
,
1406 // #120994# Do switch on/off FillColor, even when a overriding one is set
1407 bool bSetting(static_cast<MetaFillColorAction
*>(pCurrAct
)->IsSetting());
1409 rStates
.getState().isFillColorSet
= bSetting
;
1413 case META_TEXTCOLOR_ACTION
:
1415 if( !rParms
.maTextColor
.is_initialized() )
1417 // Text color is set unconditionally, thus, no
1418 // use of setStateColor here
1419 ::Color
aColor( static_cast<MetaTextColorAction
*>(pCurrAct
)->GetColor() );
1421 // force alpha part of color to
1422 // opaque. transparent painting is done
1423 // explicitly via META_TRANSPARENT_ACTION
1424 aColor
.SetTransparency(0);
1426 rStates
.getState().textColor
=
1427 ::vcl::unotools::colorToDoubleSequence(
1429 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1434 case META_TEXTFILLCOLOR_ACTION
:
1435 if( !rParms
.maTextColor
.is_initialized() )
1437 setStateColor( static_cast<MetaTextFillColorAction
*>(pCurrAct
),
1438 rStates
.getState().isTextFillColorSet
,
1439 rStates
.getState().textFillColor
,
1444 // #120994# Do switch on/off TextFillColor, even when a overriding one is set
1445 bool bSetting(static_cast<MetaTextFillColorAction
*>(pCurrAct
)->IsSetting());
1447 rStates
.getState().isTextFillColorSet
= bSetting
;
1451 case META_TEXTLINECOLOR_ACTION
:
1452 if( !rParms
.maTextColor
.is_initialized() )
1454 setStateColor( static_cast<MetaTextLineColorAction
*>(pCurrAct
),
1455 rStates
.getState().isTextLineColorSet
,
1456 rStates
.getState().textLineColor
,
1461 // #120994# Do switch on/off TextLineColor, even when a overriding one is set
1462 bool bSetting(static_cast<MetaTextLineColorAction
*>(pCurrAct
)->IsSetting());
1464 rStates
.getState().isTextLineColorSet
= bSetting
;
1468 case META_TEXTALIGN_ACTION
:
1470 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1471 const TextAlign
eTextAlign( static_cast<MetaTextAlignAction
*>(pCurrAct
)->GetTextAlign() );
1473 rState
.textReferencePoint
= eTextAlign
;
1477 case META_FONT_ACTION
:
1479 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1480 const ::Font
& rFont( static_cast<MetaFontAction
*>(pCurrAct
)->GetFont() );
1482 rState
.xFont
= createFont( rState
.fontRotation
,
1486 // TODO(Q2): define and use appropriate enumeration types
1487 rState
.textReliefStyle
= (sal_Int8
)rFont
.GetRelief();
1488 rState
.textOverlineStyle
= (sal_Int8
)rFont
.GetOverline();
1489 rState
.textUnderlineStyle
= rParms
.maFontUnderline
.is_initialized() ?
1490 (*rParms
.maFontUnderline
? (sal_Int8
)UNDERLINE_SINGLE
: (sal_Int8
)UNDERLINE_NONE
) :
1491 (sal_Int8
)rFont
.GetUnderline();
1492 rState
.textStrikeoutStyle
= (sal_Int8
)rFont
.GetStrikeout();
1493 rState
.textEmphasisMarkStyle
= (sal_Int8
)rFont
.GetEmphasisMark();
1494 rState
.isTextEffectShadowSet
= (rFont
.IsShadow() != sal_False
);
1495 rState
.isTextWordUnderlineSet
= (rFont
.IsWordLineMode() != sal_False
);
1496 rState
.isTextOutlineModeSet
= (rFont
.IsOutline() != sal_False
);
1500 case META_RASTEROP_ACTION
:
1504 case META_LAYOUTMODE_ACTION
:
1506 // TODO(F2): A lot is missing here
1507 int nLayoutMode
= static_cast<MetaLayoutModeAction
*>(pCurrAct
)->GetLayoutMode();
1508 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1509 switch( nLayoutMode
& (TEXT_LAYOUT_BIDI_RTL
|TEXT_LAYOUT_BIDI_STRONG
) )
1511 case TEXT_LAYOUT_BIDI_LTR
:
1512 rState
.textDirection
= rendering::TextDirection::WEAK_LEFT_TO_RIGHT
;
1515 case (TEXT_LAYOUT_BIDI_LTR
| TEXT_LAYOUT_BIDI_STRONG
):
1516 rState
.textDirection
= rendering::TextDirection::STRONG_LEFT_TO_RIGHT
;
1519 case TEXT_LAYOUT_BIDI_RTL
:
1520 rState
.textDirection
= rendering::TextDirection::WEAK_RIGHT_TO_LEFT
;
1523 case (TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_BIDI_STRONG
):
1524 rState
.textDirection
= rendering::TextDirection::STRONG_RIGHT_TO_LEFT
;
1528 rState
.textAlignment
= 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1529 if( (nLayoutMode
& (TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_TEXTORIGIN_RIGHT
) )
1530 && !(nLayoutMode
& TEXT_LAYOUT_TEXTORIGIN_LEFT
) )
1532 rState
.textAlignment
= 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1537 // ------------------------------------------------------------
1539 // In the second part of this monster-switch, we
1540 // handle all recursing meta actions. These are the
1541 // ones generating a metafile by themselves, which is
1542 // then processed by recursively calling this method.
1544 // ------------------------------------------------------------
1546 case META_GRADIENT_ACTION
:
1548 MetaGradientAction
* pGradAct
= static_cast<MetaGradientAction
*>(pCurrAct
);
1549 createGradientAction( ::Polygon( pGradAct
->GetRect() ),
1550 pGradAct
->GetGradient(),
1553 bSubsettableActions
);
1557 case META_HATCH_ACTION
:
1559 // TODO(F2): use native Canvas hatches here
1560 GDIMetaFile aTmpMtf
;
1562 rVDev
.AddHatchActions( static_cast<MetaHatchAction
*>(pCurrAct
)->GetPolyPolygon(),
1563 static_cast<MetaHatchAction
*>(pCurrAct
)->GetHatch(),
1565 createActions( aTmpMtf
, rFactoryParms
,
1566 bSubsettableActions
);
1570 case META_EPS_ACTION
:
1572 MetaEPSAction
* pAct
= static_cast<MetaEPSAction
*>(pCurrAct
);
1573 const GDIMetaFile
& rSubstitute
= pAct
->GetSubstitute();
1575 // #121806# explicitly kept integer
1576 const Size
aMtfSize( rSubstitute
.GetPrefSize() );
1577 const Size
aMtfSizePixPre( rVDev
.LogicToPixel( aMtfSize
,
1578 rSubstitute
.GetPrefMapMode() ) );
1580 // #i44110# correct null-sized output - there
1581 // are metafiles which have zero size in at
1582 // least one dimension
1583 const Size
aMtfSizePix( ::std::max( aMtfSizePixPre
.Width(), 1L ),
1584 ::std::max( aMtfSizePixPre
.Height(), 1L ) );
1586 // Setup local transform, such that the
1587 // metafile renders itself into the given
1589 rStates
.pushState(PUSH_ALL
);
1592 rVDev
.SetMapMode( rSubstitute
.GetPrefMapMode() );
1594 const ::Point
& rPos( rVDev
.LogicToPixel( pAct
->GetPoint() ) );
1595 const ::Size
& rSize( rVDev
.LogicToPixel( pAct
->GetSize() ) );
1597 rStates
.getState().transform
.translate( rPos
.X(),
1599 rStates
.getState().transform
.scale( (double)rSize
.Width() / aMtfSizePix
.Width(),
1600 (double)rSize
.Height() / aMtfSizePix
.Height() );
1602 createActions( const_cast<GDIMetaFile
&>(pAct
->GetSubstitute()),
1604 bSubsettableActions
);
1611 // handle metafile comments, to retrieve
1612 // meta-information for gradients, fills and
1613 // strokes. May skip actions, and may recurse.
1614 case META_COMMENT_ACTION
:
1616 MetaCommentAction
* pAct
= static_cast<MetaCommentAction
*>(pCurrAct
);
1619 if (pAct
->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_BEGIN")))
1621 MetaGradientExAction
* pGradAction
= NULL
;
1622 bool bDone( false );
1624 (pCurrAct
=rMtf
.NextAction()) != NULL
)
1626 switch( pCurrAct
->GetType() )
1628 // extract gradient info
1629 case META_GRADIENTEX_ACTION
:
1630 pGradAction
= static_cast<MetaGradientExAction
*>(pCurrAct
);
1633 // skip broken-down rendering, output gradient when sequence is ended
1634 case META_COMMENT_ACTION
:
1635 if( static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_END")) )
1641 createGradientAction( pGradAction
->GetPolyPolygon(),
1642 pGradAction
->GetGradient(),
1645 bSubsettableActions
);
1652 // TODO(P2): Handle drawing layer strokes, via
1653 // XPATHSTROKE_SEQ_BEGIN comment
1655 // Handle drawing layer fills
1656 else if( pAct
->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("XPATHFILL_SEQ_BEGIN")) )
1658 const sal_uInt8
* pData
= pAct
->GetData();
1661 SvMemoryStream
aMemStm( (void*)pData
, pAct
->GetDataSize(), STREAM_READ
);
1663 SvtGraphicFill aFill
;
1666 // TODO(P2): Also handle gradients and
1667 // hatches like this
1669 // only evaluate comment for pure
1670 // bitmap fills. If a transparency
1671 // gradient is involved (denoted by
1672 // the FloatTransparent action), take
1673 // the normal meta actions.
1674 if( aFill
.getFillType() == SvtGraphicFill::fillTexture
&&
1675 !isActionContained( rMtf
,
1676 "XPATHFILL_SEQ_END",
1677 META_FLOATTRANSPARENT_ACTION
) )
1679 rendering::Texture aTexture
;
1681 // TODO(F1): the SvtGraphicFill
1682 // can also transport metafiles
1683 // here, handle that case, too
1685 aFill
.getGraphic( aGraphic
);
1687 BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
1688 const ::Size
aBmpSize( aBmpEx
.GetSizePixel() );
1690 ::SvtGraphicFill::Transform aTransform
;
1691 aFill
.getTransform( aTransform
);
1693 ::basegfx::B2DHomMatrix aMatrix
;
1695 // convert to basegfx matrix
1696 aMatrix
.set(0,0, aTransform
.matrix
[ 0 ] );
1697 aMatrix
.set(0,1, aTransform
.matrix
[ 1 ] );
1698 aMatrix
.set(0,2, aTransform
.matrix
[ 2 ] );
1699 aMatrix
.set(1,0, aTransform
.matrix
[ 3 ] );
1700 aMatrix
.set(1,1, aTransform
.matrix
[ 4 ] );
1701 aMatrix
.set(1,2, aTransform
.matrix
[ 5 ] );
1703 ::basegfx::B2DHomMatrix aScale
;
1704 aScale
.scale( aBmpSize
.Width(),
1705 aBmpSize
.Height() );
1707 // post-multiply with the bitmap
1708 // size (XCanvas' texture assumes
1709 // the given bitmap to be
1710 // normalized to [0,1]x[0,1]
1712 aMatrix
= aMatrix
* aScale
;
1714 // pre-multiply with the
1715 // logic-to-pixel scale factor
1716 // (the metafile comment works in
1717 // logical coordinates).
1718 ::basegfx::B2DHomMatrix aLogic2PixelTransform
;
1719 aMatrix
*= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform
,
1722 ::basegfx::unotools::affineMatrixFromHomMatrix(
1723 aTexture
.AffineTransform
,
1726 aTexture
.Alpha
= 1.0 - aFill
.getTransparency();
1728 ::vcl::unotools::xBitmapFromBitmapEx(
1729 rCanvas
->getUNOCanvas()->getDevice(),
1731 if( aFill
.isTiling() )
1733 aTexture
.RepeatModeX
= rendering::TexturingMode::REPEAT
;
1734 aTexture
.RepeatModeY
= rendering::TexturingMode::REPEAT
;
1738 aTexture
.RepeatModeX
= rendering::TexturingMode::NONE
;
1739 aTexture
.RepeatModeY
= rendering::TexturingMode::NONE
;
1742 ::PolyPolygon aPath
;
1743 aFill
.getPath( aPath
);
1745 ::basegfx::B2DPolyPolygon
aPoly( aPath
.getB2DPolyPolygon() );
1746 aPoly
.transform( rStates
.getState().mapModeTransform
);
1747 ActionSharedPtr
pPolyAction(
1748 internal::PolyPolyActionFactory::createPolyPolyAction(
1756 maActions
.push_back(
1759 io_rCurrActionIndex
) );
1761 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
1764 // skip broken-down render output
1766 "XPATHFILL_SEQ_END",
1767 io_rCurrActionIndex
);
1771 // Handle drawing layer fills
1772 else if( pAct
->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("EMF_PLUS")) ) {
1773 static int count
= -1, limit
= 0x7fffffff;
1776 if (char *env
= getenv ("EMF_PLUS_LIMIT")) {
1778 SAL_INFO ("cppcanvas.emf", "EMF+ records limit: " << limit
);
1781 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer, size: " << pAct
->GetDataSize ());
1783 processEMFPlus( pAct
, rFactoryParms
, rStates
.getState(), rCanvas
);
1785 } else if( pAct
->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("EMF_PLUS_HEADER_INFO")) ) {
1786 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pAct
->GetDataSize ());
1788 SvMemoryStream
rMF ((void*) pAct
->GetData (), pAct
->GetDataSize (), STREAM_READ
);
1790 rMF
>> nFrameLeft
>> nFrameTop
>> nFrameRight
>> nFrameBottom
;
1791 SAL_INFO ("cppcanvas.emf", "EMF+ picture frame: " << nFrameLeft
<< "," << nFrameTop
<< " - " << nFrameRight
<< "," << nFrameBottom
);
1792 rMF
>> nPixX
>> nPixY
>> nMmX
>> nMmY
;
1793 SAL_INFO ("cppcanvas.emf", "EMF+ ref device pixel size: " << nPixX
<< "x" << nPixY
<< " mm size: " << nMmX
<< "x" << nMmY
);
1795 rMF
>> aBaseTransform
;
1796 //aWorldTransform.Set (aBaseTransform);
1801 // ------------------------------------------------------------
1803 // In the third part of this monster-switch, we
1804 // handle all 'acting' meta actions. These are all
1805 // processed by constructing function objects for
1806 // them, which will later ease caching.
1808 // ------------------------------------------------------------
1810 case META_POINT_ACTION
:
1812 const OutDevState
& rState( rStates
.getState() );
1813 if( rState
.lineColor
.getLength() )
1815 ActionSharedPtr
pPointAction(
1816 internal::PointActionFactory::createPointAction(
1817 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint(
1818 static_cast<MetaPointAction
*>(pCurrAct
)->GetPoint() ),
1824 maActions
.push_back(
1827 io_rCurrActionIndex
) );
1829 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1835 case META_PIXEL_ACTION
:
1837 const OutDevState
& rState( rStates
.getState() );
1838 if( rState
.lineColor
.getLength() )
1840 ActionSharedPtr
pPointAction(
1841 internal::PointActionFactory::createPointAction(
1842 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint(
1843 static_cast<MetaPixelAction
*>(pCurrAct
)->GetPoint() ),
1846 static_cast<MetaPixelAction
*>(pCurrAct
)->GetColor() ) );
1850 maActions
.push_back(
1853 io_rCurrActionIndex
) );
1855 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1861 case META_LINE_ACTION
:
1863 const OutDevState
& rState( rStates
.getState() );
1864 if( rState
.lineColor
.getLength() )
1866 MetaLineAction
* pLineAct
= static_cast<MetaLineAction
*>(pCurrAct
);
1868 const LineInfo
& rLineInfo( pLineAct
->GetLineInfo() );
1870 const ::basegfx::B2DPoint
aStartPoint(
1871 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( pLineAct
->GetStartPoint() ));
1872 const ::basegfx::B2DPoint
aEndPoint(
1873 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( pLineAct
->GetEndPoint() ));
1875 ActionSharedPtr pLineAction
;
1877 if( rLineInfo
.IsDefault() )
1881 internal::LineActionFactory::createLineAction(
1889 maActions
.push_back(
1892 io_rCurrActionIndex
) );
1894 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
1897 else if( LINE_NONE
!= rLineInfo
.GetStyle() )
1900 rendering::StrokeAttributes aStrokeAttributes
;
1902 setupStrokeAttributes( aStrokeAttributes
,
1906 // XCanvas can only stroke polygons,
1907 // not simple lines - thus, handle
1908 // this case via the polypolygon
1910 ::basegfx::B2DPolygon aPoly
;
1911 aPoly
.append( aStartPoint
);
1912 aPoly
.append( aEndPoint
);
1914 internal::PolyPolyActionFactory::createPolyPolyAction(
1915 ::basegfx::B2DPolyPolygon( aPoly
),
1916 rCanvas
, rState
, aStrokeAttributes
);
1920 maActions
.push_back(
1923 io_rCurrActionIndex
) );
1925 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
1928 // else: line style is default
1929 // (i.e. invisible), don't generate action
1934 case META_RECT_ACTION
:
1936 const Rectangle
& rRect(
1937 static_cast<MetaRectAction
*>(pCurrAct
)->GetRect() );
1939 if( rRect
.IsEmpty() )
1942 const OutDevState
& rState( rStates
.getState() );
1943 const ::basegfx::B2DPoint
aTopLeftPixel(
1944 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ) );
1945 const ::basegfx::B2DPoint
aBottomRightPixel(
1946 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1947 // #121100# OutputDevice::DrawRect() fills
1948 // rectangles Apple-like, i.e. with one
1949 // additional pixel to the right and bottom.
1950 ::basegfx::B2DPoint(1,1) );
1952 createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
1953 ::basegfx::B2DRange( aTopLeftPixel
,
1954 aBottomRightPixel
)),
1959 case META_ROUNDRECT_ACTION
:
1961 const Rectangle
& rRect(
1962 static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetRect());
1964 if( rRect
.IsEmpty() )
1967 ::basegfx::B2DPolygon
aPoly(
1968 ::basegfx::tools::createPolygonFromRect(
1969 ::basegfx::B2DRange(
1970 ::vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
1971 ::vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1972 ::basegfx::B2DPoint(1,1) ),
1973 ( (double) static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetHorzRound() ) / rRect
.GetWidth(),
1974 ( (double) static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetVertRound() ) / rRect
.GetHeight() ) );
1975 aPoly
.transform( rStates
.getState().mapModeTransform
);
1977 createFillAndStroke( aPoly
,
1982 case META_ELLIPSE_ACTION
:
1984 const Rectangle
& rRect(
1985 static_cast<MetaEllipseAction
*>(pCurrAct
)->GetRect() );
1987 if( rRect
.IsEmpty() )
1990 const ::basegfx::B2DRange
aRange(
1991 ::vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
1992 ::vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1993 ::basegfx::B2DPoint(1,1) );
1995 ::basegfx::B2DPolygon
aPoly(
1996 ::basegfx::tools::createPolygonFromEllipse(
1999 aRange
.getHeight() ));
2000 aPoly
.transform( rStates
.getState().mapModeTransform
);
2002 createFillAndStroke( aPoly
,
2007 case META_ARC_ACTION
:
2009 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2010 const Polygon
aToolsPoly( static_cast<MetaArcAction
*>(pCurrAct
)->GetRect(),
2011 static_cast<MetaArcAction
*>(pCurrAct
)->GetStartPoint(),
2012 static_cast<MetaArcAction
*>(pCurrAct
)->GetEndPoint(), POLY_ARC
);
2013 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2014 aPoly
.transform( rStates
.getState().mapModeTransform
);
2016 createFillAndStroke( aPoly
,
2021 case META_PIE_ACTION
:
2023 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2024 const Polygon
aToolsPoly( static_cast<MetaPieAction
*>(pCurrAct
)->GetRect(),
2025 static_cast<MetaPieAction
*>(pCurrAct
)->GetStartPoint(),
2026 static_cast<MetaPieAction
*>(pCurrAct
)->GetEndPoint(), POLY_PIE
);
2027 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2028 aPoly
.transform( rStates
.getState().mapModeTransform
);
2030 createFillAndStroke( aPoly
,
2035 case META_CHORD_ACTION
:
2037 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2038 const Polygon
aToolsPoly( static_cast<MetaChordAction
*>(pCurrAct
)->GetRect(),
2039 static_cast<MetaChordAction
*>(pCurrAct
)->GetStartPoint(),
2040 static_cast<MetaChordAction
*>(pCurrAct
)->GetEndPoint(), POLY_CHORD
);
2041 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2042 aPoly
.transform( rStates
.getState().mapModeTransform
);
2044 createFillAndStroke( aPoly
,
2049 case META_POLYLINE_ACTION
:
2051 const OutDevState
& rState( rStates
.getState() );
2052 if( rState
.lineColor
.getLength() ||
2053 rState
.fillColor
.getLength() )
2055 MetaPolyLineAction
* pPolyLineAct
= static_cast<MetaPolyLineAction
*>(pCurrAct
);
2057 const LineInfo
& rLineInfo( pPolyLineAct
->GetLineInfo() );
2058 ::basegfx::B2DPolygon
aPoly( pPolyLineAct
->GetPolygon().getB2DPolygon() );
2059 aPoly
.transform( rState
.mapModeTransform
);
2061 ActionSharedPtr pLineAction
;
2063 if( rLineInfo
.IsDefault() )
2065 // plain hair line polygon
2067 internal::PolyPolyActionFactory::createLinePolyPolyAction(
2068 ::basegfx::B2DPolyPolygon(aPoly
),
2074 maActions
.push_back(
2077 io_rCurrActionIndex
) );
2079 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2082 else if( LINE_NONE
!= rLineInfo
.GetStyle() )
2084 // 'thick' line polygon
2085 rendering::StrokeAttributes aStrokeAttributes
;
2087 setupStrokeAttributes( aStrokeAttributes
,
2092 internal::PolyPolyActionFactory::createPolyPolyAction(
2093 ::basegfx::B2DPolyPolygon(aPoly
),
2096 aStrokeAttributes
) ;
2100 maActions
.push_back(
2103 io_rCurrActionIndex
) );
2105 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2108 // else: line style is default
2109 // (i.e. invisible), don't generate action
2114 case META_POLYGON_ACTION
:
2116 ::basegfx::B2DPolygon
aPoly( static_cast<MetaPolygonAction
*>(pCurrAct
)->GetPolygon().getB2DPolygon() );
2117 aPoly
.transform( rStates
.getState().mapModeTransform
);
2118 createFillAndStroke( aPoly
,
2123 case META_POLYPOLYGON_ACTION
:
2125 ::basegfx::B2DPolyPolygon
aPoly( static_cast<MetaPolyPolygonAction
*>(pCurrAct
)->GetPolyPolygon().getB2DPolyPolygon() );
2126 aPoly
.transform( rStates
.getState().mapModeTransform
);
2127 createFillAndStroke( aPoly
,
2132 case META_BMP_ACTION
:
2134 MetaBmpAction
* pAct
= static_cast<MetaBmpAction
*>(pCurrAct
);
2136 ActionSharedPtr
pBmpAction(
2137 internal::BitmapActionFactory::createBitmapAction(
2139 rStates
.getState().mapModeTransform
*
2140 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2142 rStates
.getState() ) );
2146 maActions
.push_back(
2149 io_rCurrActionIndex
) );
2151 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2156 case META_BMPSCALE_ACTION
:
2158 MetaBmpScaleAction
* pAct
= static_cast<MetaBmpScaleAction
*>(pCurrAct
);
2160 ActionSharedPtr
pBmpAction(
2161 internal::BitmapActionFactory::createBitmapAction(
2163 rStates
.getState().mapModeTransform
*
2164 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2165 rStates
.getState().mapModeTransform
*
2166 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2168 rStates
.getState() ) );
2172 maActions
.push_back(
2175 io_rCurrActionIndex
) );
2177 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2182 case META_BMPSCALEPART_ACTION
:
2184 MetaBmpScalePartAction
* pAct
= static_cast<MetaBmpScalePartAction
*>(pCurrAct
);
2186 // crop bitmap to given source rectangle (no
2187 // need to copy and convert the whole bitmap)
2188 Bitmap
aBmp( pAct
->GetBitmap() );
2189 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2190 pAct
->GetSrcSize() );
2191 aBmp
.Crop( aCropRect
);
2193 ActionSharedPtr
pBmpAction(
2194 internal::BitmapActionFactory::createBitmapAction(
2196 rStates
.getState().mapModeTransform
*
2197 ::vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2198 rStates
.getState().mapModeTransform
*
2199 ::vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2201 rStates
.getState() ) );
2205 maActions
.push_back(
2208 io_rCurrActionIndex
) );
2210 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2215 case META_BMPEX_ACTION
:
2217 MetaBmpExAction
* pAct
= static_cast<MetaBmpExAction
*>(pCurrAct
);
2219 ActionSharedPtr
pBmpAction(
2220 internal::BitmapActionFactory::createBitmapAction(
2221 pAct
->GetBitmapEx(),
2222 rStates
.getState().mapModeTransform
*
2223 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2225 rStates
.getState() ) );
2229 maActions
.push_back(
2232 io_rCurrActionIndex
) );
2234 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2239 case META_BMPEXSCALE_ACTION
:
2241 MetaBmpExScaleAction
* pAct
= static_cast<MetaBmpExScaleAction
*>(pCurrAct
);
2243 ActionSharedPtr
pBmpAction(
2244 internal::BitmapActionFactory::createBitmapAction(
2245 pAct
->GetBitmapEx(),
2246 rStates
.getState().mapModeTransform
*
2247 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2248 rStates
.getState().mapModeTransform
*
2249 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2251 rStates
.getState() ) );
2255 maActions
.push_back(
2258 io_rCurrActionIndex
) );
2260 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2265 case META_BMPEXSCALEPART_ACTION
:
2267 MetaBmpExScalePartAction
* pAct
= static_cast<MetaBmpExScalePartAction
*>(pCurrAct
);
2269 // crop bitmap to given source rectangle (no
2270 // need to copy and convert the whole bitmap)
2271 BitmapEx
aBmp( pAct
->GetBitmapEx() );
2272 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2273 pAct
->GetSrcSize() );
2274 aBmp
.Crop( aCropRect
);
2276 ActionSharedPtr
pBmpAction(
2277 internal::BitmapActionFactory::createBitmapAction(
2279 rStates
.getState().mapModeTransform
*
2280 ::vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2281 rStates
.getState().mapModeTransform
*
2282 ::vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2284 rStates
.getState() ) );
2288 maActions
.push_back(
2291 io_rCurrActionIndex
) );
2293 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2298 case META_MASK_ACTION
:
2300 MetaMaskAction
* pAct
= static_cast<MetaMaskAction
*>(pCurrAct
);
2302 // create masked BitmapEx right here, as the
2303 // canvas does not provide equivalent
2305 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2306 pAct
->GetColor() ));
2308 ActionSharedPtr
pBmpAction(
2309 internal::BitmapActionFactory::createBitmapAction(
2311 rStates
.getState().mapModeTransform
*
2312 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2314 rStates
.getState() ) );
2318 maActions
.push_back(
2321 io_rCurrActionIndex
) );
2323 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2328 case META_MASKSCALE_ACTION
:
2330 MetaMaskScaleAction
* pAct
= static_cast<MetaMaskScaleAction
*>(pCurrAct
);
2332 // create masked BitmapEx right here, as the
2333 // canvas does not provide equivalent
2335 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2336 pAct
->GetColor() ));
2338 ActionSharedPtr
pBmpAction(
2339 internal::BitmapActionFactory::createBitmapAction(
2341 rStates
.getState().mapModeTransform
*
2342 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2343 rStates
.getState().mapModeTransform
*
2344 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2346 rStates
.getState() ) );
2350 maActions
.push_back(
2353 io_rCurrActionIndex
) );
2355 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2360 case META_MASKSCALEPART_ACTION
:
2362 MetaMaskScalePartAction
* pAct
= static_cast<MetaMaskScalePartAction
*>(pCurrAct
);
2364 // create masked BitmapEx right here, as the
2365 // canvas does not provide equivalent
2367 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2368 pAct
->GetColor() ));
2370 // crop bitmap to given source rectangle (no
2371 // need to copy and convert the whole bitmap)
2372 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2373 pAct
->GetSrcSize() );
2374 aBmp
.Crop( aCropRect
);
2376 ActionSharedPtr
pBmpAction(
2377 internal::BitmapActionFactory::createBitmapAction(
2379 rStates
.getState().mapModeTransform
*
2380 ::vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2381 rStates
.getState().mapModeTransform
*
2382 ::vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2384 rStates
.getState() ) );
2388 maActions
.push_back(
2391 io_rCurrActionIndex
) );
2393 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2398 case META_GRADIENTEX_ACTION
:
2399 // TODO(F1): use native Canvas gradients here
2400 // action is ignored here, because redundant to META_GRADIENT_ACTION
2403 case META_WALLPAPER_ACTION
:
2407 case META_TRANSPARENT_ACTION
:
2409 const OutDevState
& rState( rStates
.getState() );
2410 if( rState
.lineColor
.getLength() ||
2411 rState
.fillColor
.getLength() )
2413 MetaTransparentAction
* pAct
= static_cast<MetaTransparentAction
*>(pCurrAct
);
2414 ::basegfx::B2DPolyPolygon
aPoly( pAct
->GetPolyPolygon().getB2DPolyPolygon() );
2415 aPoly
.transform( rState
.mapModeTransform
);
2417 ActionSharedPtr
pPolyAction(
2418 internal::PolyPolyActionFactory::createPolyPolyAction(
2422 pAct
->GetTransparence() ) );
2426 maActions
.push_back(
2429 io_rCurrActionIndex
) );
2431 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2437 case META_FLOATTRANSPARENT_ACTION
:
2439 MetaFloatTransparentAction
* pAct
= static_cast<MetaFloatTransparentAction
*>(pCurrAct
);
2441 internal::MtfAutoPtr
pMtf(
2442 new ::GDIMetaFile( pAct
->GetGDIMetaFile() ) );
2444 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2445 internal::GradientAutoPtr
pGradient(
2446 new Gradient( pAct
->GetGradient() ) );
2448 DBG_TESTSOLARMUTEX();
2450 ActionSharedPtr
pFloatTransAction(
2451 internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2455 rStates
.getState().mapModeTransform
*
2456 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2457 rStates
.getState().mapModeTransform
*
2458 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2460 rStates
.getState() ) );
2462 if( pFloatTransAction
)
2464 maActions
.push_back(
2467 io_rCurrActionIndex
) );
2469 io_rCurrActionIndex
+= pFloatTransAction
->getActionCount()-1;
2474 case META_TEXT_ACTION
:
2476 MetaTextAction
* pAct
= static_cast<MetaTextAction
*>(pCurrAct
);
2477 OUString sText
= pAct
->GetText();
2479 if (rVDev
.GetDigitLanguage())
2480 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2486 pAct
->GetLen() == (sal_uInt16
)STRING_LEN
? pAct
->GetText().getLength() - pAct
->GetIndex() : pAct
->GetLen(),
2489 bSubsettableActions
);
2493 case META_TEXTARRAY_ACTION
:
2495 MetaTextArrayAction
* pAct
= static_cast<MetaTextArrayAction
*>(pCurrAct
);
2496 OUString sText
= pAct
->GetText();
2498 if (rVDev
.GetDigitLanguage())
2499 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2505 pAct
->GetLen() == (sal_uInt16
)STRING_LEN
? pAct
->GetText().getLength() - pAct
->GetIndex() : pAct
->GetLen(),
2508 bSubsettableActions
);
2512 case META_TEXTLINE_ACTION
:
2514 MetaTextLineAction
* pAct
= static_cast<MetaTextLineAction
*>(pCurrAct
);
2516 const OutDevState
& rState( rStates
.getState() );
2517 const ::Size
aBaselineOffset( tools::getBaselineOffset( rState
,
2519 const ::basegfx::B2DSize
aSize( rState
.mapModeTransform
*
2520 ::basegfx::B2DSize(pAct
->GetWidth(),
2523 ActionSharedPtr
pPolyAction(
2524 PolyPolyActionFactory::createPolyPolyAction(
2525 tools::createTextLinesPolyPolygon(
2526 rState
.mapModeTransform
*
2527 ::basegfx::B2DPoint(
2528 ::vcl::unotools::b2DPointFromPoint(pAct
->GetStartPoint()) +
2529 ::vcl::unotools::b2DSizeFromSize(aBaselineOffset
)),
2531 tools::createTextLineInfo( rVDev
,
2536 if( pPolyAction
.get() )
2538 maActions
.push_back(
2541 io_rCurrActionIndex
) );
2543 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2548 case META_TEXTRECT_ACTION
:
2550 MetaTextRectAction
* pAct
= static_cast<MetaTextRectAction
*>(pCurrAct
);
2552 rStates
.pushState(PUSH_ALL
);
2554 // use the VDev to break up the text rect
2555 // action into readily formatted lines
2556 GDIMetaFile aTmpMtf
;
2557 rVDev
.AddTextRectActions( pAct
->GetRect(),
2562 createActions( aTmpMtf
,
2564 bSubsettableActions
);
2571 case META_STRETCHTEXT_ACTION
:
2573 MetaStretchTextAction
* pAct
= static_cast<MetaStretchTextAction
*>(pCurrAct
);
2574 OUString sText
= pAct
->GetText();
2576 if (rVDev
.GetDigitLanguage())
2577 sText
= convertToLocalizedNumerals(sText
, rVDev
.GetDigitLanguage());
2579 const sal_uInt16
nLen( pAct
->GetLen() == (sal_uInt16
)STRING_LEN
?
2580 pAct
->GetText().getLength() - pAct
->GetIndex() : pAct
->GetLen() );
2582 // #i70897# Nothing to do, actually...
2586 // have to fit the text into the given
2587 // width. This is achieved by internally
2588 // generating a DX array, and uniformly
2589 // distributing the excess/insufficient width
2590 // to every logical character.
2591 ::boost::scoped_array
< sal_Int32
> pDXArray( new sal_Int32
[nLen
] );
2593 rVDev
.GetTextArray( pAct
->GetText(), pDXArray
.get(),
2594 pAct
->GetIndex(), pAct
->GetLen() );
2596 const sal_Int32
nWidthDifference( pAct
->GetWidth() - pDXArray
[ nLen
-1 ] );
2598 // Last entry of pDXArray contains total width of the text
2599 sal_Int32
* p
=pDXArray
.get();
2600 for( sal_uInt16 i
=1; i
<=nLen
; ++i
)
2602 // calc ratio for every array entry, to
2603 // distribute rounding errors 'evenly'
2604 // across the characters. Note that each
2605 // entry represents the 'end' position of
2606 // the corresponding character, thus, we
2607 // let i run from 1 to nLen.
2608 *p
++ += (sal_Int32
)i
*nWidthDifference
/nLen
;
2615 pAct
->GetLen() == (sal_uInt16
)STRING_LEN
? pAct
->GetText().getLength() - pAct
->GetIndex() : pAct
->GetLen(),
2618 bSubsettableActions
);
2623 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
2627 // increment action index (each mtf action counts _at
2628 // least_ one. Some count for more, therefore,
2629 // io_rCurrActionIndex is sometimes incremented by
2630 // pAct->getActionCount()-1 above, the -1 being the
2631 // correction for the unconditional increment here).
2632 ++io_rCurrActionIndex
;
2641 class ActionRenderer
2644 ActionRenderer( const ::basegfx::B2DHomMatrix
& rTransformation
) :
2645 maTransformation( rTransformation
),
2655 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2657 // ANDing the result. We want to fail if at least
2658 // one action failed.
2659 mbRet
&= rAction
.mpAction
->render( maTransformation
);
2662 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2663 const Action::Subset
& rSubset
)
2665 // ANDing the result. We want to fail if at least
2666 // one action failed.
2667 mbRet
&= rAction
.mpAction
->renderSubset( maTransformation
,
2672 ::basegfx::B2DHomMatrix maTransformation
;
2679 AreaQuery( const ::basegfx::B2DHomMatrix
& rTransformation
) :
2680 maTransformation( rTransformation
),
2687 return true; // nothing can fail here
2690 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2692 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
) );
2695 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2696 const Action::Subset
& rSubset
)
2698 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
,
2702 ::basegfx::B2DRange
getBounds() const
2708 ::basegfx::B2DHomMatrix maTransformation
;
2709 ::basegfx::B2DRange maBounds
;
2712 // Doing that via inline class. Compilers tend to not inline free
2714 struct UpperBoundActionIndexComparator
2716 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rLHS
,
2717 const ::cppcanvas::internal::ImplRenderer::MtfAction
& rRHS
)
2719 const sal_Int32
nLHSCount( rLHS
.mpAction
?
2720 rLHS
.mpAction
->getActionCount() : 0 );
2721 const sal_Int32
nRHSCount( rRHS
.mpAction
?
2722 rRHS
.mpAction
->getActionCount() : 0 );
2724 // compare end of action range, to have an action selected
2725 // by lower_bound even if the requested index points in
2726 // the middle of the action's range
2727 return rLHS
.mnOrigIndex
+ nLHSCount
< rRHS
.mnOrigIndex
+ nRHSCount
;
2731 /** Algorithm to apply given functor to a subset range
2735 Functor to call for each element of the subset
2736 range. Must provide the following method signatures:
2737 bool result() (returning false if operation failed)
2740 template< typename Functor
> bool
2741 forSubsetRange( Functor
& rFunctor
,
2742 ImplRenderer::ActionVector::const_iterator aRangeBegin
,
2743 ImplRenderer::ActionVector::const_iterator aRangeEnd
,
2744 sal_Int32 nStartIndex
,
2745 sal_Int32 nEndIndex
,
2746 const ImplRenderer::ActionVector::const_iterator
& rEnd
)
2748 if( aRangeBegin
== aRangeEnd
)
2750 // only a single action. Setup subset, and call functor
2751 Action::Subset aSubset
;
2752 aSubset
.mnSubsetBegin
= ::std::max( sal_Int32( 0 ),
2753 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2754 aSubset
.mnSubsetEnd
= ::std::min( aRangeBegin
->mpAction
->getActionCount(),
2755 nEndIndex
- aRangeBegin
->mnOrigIndex
);
2757 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2758 "ImplRenderer::forSubsetRange(): Invalid indices" );
2760 rFunctor( *aRangeBegin
, aSubset
);
2764 // more than one action.
2766 // render partial first, full intermediate, and
2767 // partial last action
2768 Action::Subset aSubset
;
2769 aSubset
.mnSubsetBegin
= ::std::max( sal_Int32( 0 ),
2770 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2771 aSubset
.mnSubsetEnd
= aRangeBegin
->mpAction
->getActionCount();
2773 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2774 "ImplRenderer::forSubsetRange(): Invalid indices" );
2776 rFunctor( *aRangeBegin
, aSubset
);
2778 // first action rendered, skip to next
2781 // render full middle actions
2782 while( aRangeBegin
!= aRangeEnd
)
2783 rFunctor( *aRangeBegin
++ );
2785 if( aRangeEnd
== rEnd
||
2786 aRangeEnd
->mnOrigIndex
> nEndIndex
)
2788 // aRangeEnd denotes end of action vector,
2792 // nEndIndex references something _after_
2793 // aRangeBegin, but _before_ aRangeEnd
2795 // either way: no partial action left
2796 return rFunctor
.result();
2799 aSubset
.mnSubsetBegin
= 0;
2800 aSubset
.mnSubsetEnd
= nEndIndex
- aRangeEnd
->mnOrigIndex
;
2802 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2803 "ImplRenderer::forSubsetRange(): Invalid indices" );
2805 rFunctor( *aRangeEnd
, aSubset
);
2808 return rFunctor
.result();
2812 bool ImplRenderer::getSubsetIndices( sal_Int32
& io_rStartIndex
,
2813 sal_Int32
& io_rEndIndex
,
2814 ActionVector::const_iterator
& o_rRangeBegin
,
2815 ActionVector::const_iterator
& o_rRangeEnd
) const
2817 ENSURE_OR_RETURN_FALSE( io_rStartIndex
<=io_rEndIndex
,
2818 "ImplRenderer::getSubsetIndices(): invalid action range" );
2820 ENSURE_OR_RETURN_FALSE( !maActions
.empty(),
2821 "ImplRenderer::getSubsetIndices(): no actions to render" );
2823 const sal_Int32
nMinActionIndex( maActions
.front().mnOrigIndex
);
2824 const sal_Int32
nMaxActionIndex( maActions
.back().mnOrigIndex
+
2825 maActions
.back().mpAction
->getActionCount() );
2827 // clip given range to permissible values (there might be
2828 // ranges before and behind the valid indices)
2829 io_rStartIndex
= ::std::max( nMinActionIndex
,
2831 io_rEndIndex
= ::std::min( nMaxActionIndex
,
2834 if( io_rStartIndex
== io_rEndIndex
||
2835 io_rStartIndex
> io_rEndIndex
)
2837 // empty range, don't render anything. The second
2838 // condition e.g. happens if the requested range lies
2839 // fully before or behind the valid action indices.
2844 const ActionVector::const_iterator
aBegin( maActions
.begin() );
2845 const ActionVector::const_iterator
aEnd( maActions
.end() );
2848 // find start and end action
2849 // =========================
2850 o_rRangeBegin
= ::std::lower_bound( aBegin
, aEnd
,
2851 MtfAction( ActionSharedPtr(), io_rStartIndex
),
2852 UpperBoundActionIndexComparator() );
2853 o_rRangeEnd
= ::std::lower_bound( aBegin
, aEnd
,
2854 MtfAction( ActionSharedPtr(), io_rEndIndex
),
2855 UpperBoundActionIndexComparator() );
2861 // ====================================================================
2863 ImplRenderer::ImplRenderer( const CanvasSharedPtr
& rCanvas
,
2864 const GDIMetaFile
& rMtf
,
2865 const Parameters
& rParams
) :
2866 CanvasGraphicHelper( rCanvas
),
2869 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2871 OSL_ENSURE( rCanvas
.get() != NULL
&& rCanvas
->getUNOCanvas().is(),
2872 "ImplRenderer::ImplRenderer(): Invalid canvas" );
2873 OSL_ENSURE( rCanvas
->getUNOCanvas()->getDevice().is(),
2874 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2876 // make sure canvas and graphic device are valid; action
2877 // creation don't check that every time
2878 if( rCanvas
.get() == NULL
||
2879 !rCanvas
->getUNOCanvas().is() ||
2880 !rCanvas
->getUNOCanvas()->getDevice().is() )
2882 // leave actions empty
2886 VectorOfOutDevStates aStateStack
;
2888 VirtualDevice aVDev
;
2889 aVDev
.EnableOutput( sal_False
);
2891 // Setup VDev for state tracking and mapping
2892 // =========================================
2894 aVDev
.SetMapMode( rMtf
.GetPrefMapMode() );
2896 const Size
aMtfSize( rMtf
.GetPrefSize() );
2897 const Size
aMtfSizePixPre( aVDev
.LogicToPixel( aMtfSize
,
2898 rMtf
.GetPrefMapMode() ) );
2900 // #i44110# correct null-sized output - there are shapes
2901 // which have zero size in at least one dimension
2902 const Size
aMtfSizePix( ::std::max( aMtfSizePixPre
.Width(), 1L ),
2903 ::std::max( aMtfSizePixPre
.Height(), 1L ) );
2905 sal_Int32
nCurrActions(0);
2906 ActionFactoryParameters
aParms(aStateStack
,
2913 aStateStack
.clearStateStack();
2915 // Setup local state, such that the metafile renders
2916 // itself into a one-by-one square at the origin for
2917 // identity view and render transformations
2918 aStateStack
.getState().transform
.scale( 1.0 / aMtfSizePix
.Width(),
2919 1.0 / aMtfSizePix
.Height() );
2921 tools::calcLogic2PixelAffineTransform( aStateStack
.getState().mapModeTransform
,
2924 ColorSharedPtr
pColor( getCanvas()->createColor() );
2927 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2928 // setup default text color to black
2930 rState
.textFillColor
=
2931 rState
.textLineColor
= pColor
->getDeviceColor( 0x000000FF );
2934 // apply overrides from the Parameters struct
2935 if( rParams
.maFillColor
.is_initialized() )
2937 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2938 rState
.isFillColorSet
= true;
2939 rState
.fillColor
= pColor
->getDeviceColor( *rParams
.maFillColor
);
2941 if( rParams
.maLineColor
.is_initialized() )
2943 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2944 rState
.isLineColorSet
= true;
2945 rState
.lineColor
= pColor
->getDeviceColor( *rParams
.maLineColor
);
2947 if( rParams
.maTextColor
.is_initialized() )
2949 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2950 rState
.isTextFillColorSet
= true;
2951 rState
.isTextLineColorSet
= true;
2953 rState
.textFillColor
=
2954 rState
.textLineColor
= pColor
->getDeviceColor( *rParams
.maTextColor
);
2956 if( rParams
.maFontName
.is_initialized() ||
2957 rParams
.maFontWeight
.is_initialized() ||
2958 rParams
.maFontLetterForm
.is_initialized() ||
2959 rParams
.maFontUnderline
.is_initialized() ||
2960 rParams
.maFontProportion
.is_initialized() )
2962 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2964 rState
.xFont
= createFont( rState
.fontRotation
,
2965 ::Font(), // default font
2970 memset (aObjects
, 0, sizeof (aObjects
));
2971 mbMultipart
= false;
2973 createActions( const_cast<GDIMetaFile
&>(rMtf
), // HACK(Q2):
2982 true // TODO(P1): make subsettability configurable
2986 ImplRenderer::~ImplRenderer()
2988 // don't leak EMFPObjects
2989 for(unsigned int i
=0; i
<SAL_N_ELEMENTS(aObjects
); ++i
)
2993 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex
,
2994 sal_Int32 nEndIndex
) const
2996 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::drawSubset()" );
2998 ActionVector::const_iterator aRangeBegin
;
2999 ActionVector::const_iterator aRangeEnd
;
3003 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
3004 aRangeBegin
, aRangeEnd
) )
3005 return true; // nothing to render (but _that_ was successful)
3007 // now, aRangeBegin references the action in which the
3008 // subset rendering must start, and aRangeEnd references
3009 // the action in which the subset rendering must end (it
3010 // might also end right at the start of the referenced
3011 // action, such that zero of that action needs to be
3015 // render subset of actions
3016 // ========================
3018 ::basegfx::B2DHomMatrix aMatrix
;
3019 ::canvas::tools::getRenderStateTransform( aMatrix
,
3022 ActionRenderer
aRenderer( aMatrix
);
3024 return forSubsetRange( aRenderer
,
3031 catch( uno::Exception
& )
3033 SAL_WARN("cppcanvas.emf", "" << OUStringToOString(
3034 comphelper::anyToString( cppu::getCaughtException() ),
3035 RTL_TEXTENCODING_UTF8
).getStr() );
3037 // convert error to return value
3042 ::basegfx::B2DRange
ImplRenderer::getSubsetArea( sal_Int32 nStartIndex
,
3043 sal_Int32 nEndIndex
) const
3045 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3047 ActionVector::const_iterator aRangeBegin
;
3048 ActionVector::const_iterator aRangeEnd
;
3050 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
3051 aRangeBegin
, aRangeEnd
) )
3052 return ::basegfx::B2DRange(); // nothing to render -> empty range
3054 // now, aRangeBegin references the action in which the
3055 // subset querying must start, and aRangeEnd references
3056 // the action in which the subset querying must end (it
3057 // might also end right at the start of the referenced
3058 // action, such that zero of that action needs to be
3062 // query bounds for subset of actions
3063 // ==================================
3065 ::basegfx::B2DHomMatrix aMatrix
;
3066 ::canvas::tools::getRenderStateTransform( aMatrix
,
3069 AreaQuery
aQuery( aMatrix
);
3070 forSubsetRange( aQuery
,
3077 return aQuery
.getBounds();
3080 bool ImplRenderer::draw() const
3082 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::draw()" );
3084 ::basegfx::B2DHomMatrix aMatrix
;
3085 ::canvas::tools::getRenderStateTransform( aMatrix
,
3090 return ::std::for_each( maActions
.begin(), maActions
.end(), ActionRenderer( aMatrix
) ).result();
3092 catch( uno::Exception
& )
3094 SAL_WARN( "cppcanvas.emf", "" << OUStringToOString(
3095 comphelper::anyToString( cppu::getCaughtException() ),
3096 RTL_TEXTENCODING_UTF8
).getStr() );
3104 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */