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 /** Shameless rip from vcl/source/gdi/outdev3.cxx
229 Should consolidate, into something like basetxt...
231 sal_Unicode
getLocalizedChar( sal_Unicode nChar
, LanguageType eLang
)
233 // currently only conversion from ASCII digits is interesting
234 if( (nChar
< '0') || ('9' < nChar
) )
237 sal_Unicode
nOffset(0);
238 // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
239 // CAVEAT! To some like Mongolian MS assigned the same primary language
240 // although the script type is different!
241 switch( eLang
& LANGUAGE_MASK_PRIMARY
)
246 case LANGUAGE_ARABIC_SAUDI_ARABIA
& LANGUAGE_MASK_PRIMARY
:
247 case LANGUAGE_URDU
& LANGUAGE_MASK_PRIMARY
:
248 case LANGUAGE_PUNJABI
& LANGUAGE_MASK_PRIMARY
: //???
249 nOffset
= 0x0660 - '0'; // arabic/persian/urdu
251 case LANGUAGE_BENGALI
& LANGUAGE_MASK_PRIMARY
:
252 nOffset
= 0x09E6 - '0'; // bengali
254 case LANGUAGE_BURMESE
& LANGUAGE_MASK_PRIMARY
:
255 nOffset
= 0x1040 - '0'; // burmese
257 case LANGUAGE_HINDI
& LANGUAGE_MASK_PRIMARY
:
258 nOffset
= 0x0966 - '0'; // devanagari
260 case LANGUAGE_GUJARATI
& LANGUAGE_MASK_PRIMARY
:
261 nOffset
= 0x0AE6 - '0'; // gujarati
263 case LANGUAGE_KANNADA
& LANGUAGE_MASK_PRIMARY
:
264 nOffset
= 0x0CE6 - '0'; // kannada
266 case LANGUAGE_KHMER
& LANGUAGE_MASK_PRIMARY
:
267 nOffset
= 0x17E0 - '0'; // khmer
269 case LANGUAGE_LAO
& LANGUAGE_MASK_PRIMARY
:
270 nOffset
= 0x0ED0 - '0'; // lao
272 case LANGUAGE_MALAYALAM
& LANGUAGE_MASK_PRIMARY
:
273 nOffset
= 0x0D66 - '0'; // malayalam
275 case LANGUAGE_MONGOLIAN
& LANGUAGE_MASK_PRIMARY
:
276 if (eLang
== LANGUAGE_MONGOLIAN_MONGOLIAN
)
277 nOffset
= 0x1810 - '0'; // mongolian
279 nOffset
= 0; // mongolian cyrillic
281 case LANGUAGE_ORIYA
& LANGUAGE_MASK_PRIMARY
:
282 nOffset
= 0x0B66 - '0'; // oriya
284 case LANGUAGE_TAMIL
& LANGUAGE_MASK_PRIMARY
:
285 nOffset
= 0x0BE7 - '0'; // tamil
287 case LANGUAGE_TELUGU
& LANGUAGE_MASK_PRIMARY
:
288 nOffset
= 0x0C66 - '0'; // telugu
290 case LANGUAGE_THAI
& LANGUAGE_MASK_PRIMARY
:
291 nOffset
= 0x0E50 - '0'; // thai
293 case LANGUAGE_TIBETAN
& LANGUAGE_MASK_PRIMARY
:
294 nOffset
= 0x0F20 - '0'; // tibetan
298 nChar
= sal::static_int_cast
<sal_Unicode
>(nChar
+ nOffset
);
302 void convertToLocalizedNumerals( XubString
& rStr
,
303 LanguageType eTextLanguage
)
305 const sal_Unicode
* pBase
= rStr
.GetBuffer();
306 const sal_Unicode
* pBegin
= pBase
+ 0;
307 const xub_StrLen nEndIndex
= rStr
.Len();
308 const sal_Unicode
* pEnd
= pBase
+ nEndIndex
;
310 for( ; pBegin
< pEnd
; ++pBegin
)
312 // TODO: are there non-digit localizations?
313 if( (*pBegin
>= '0') && (*pBegin
<= '9') )
315 // translate characters to local preference
316 sal_Unicode cChar
= getLocalizedChar( *pBegin
, eTextLanguage
);
317 if( cChar
!= *pBegin
)
318 rStr
.SetChar( sal::static_int_cast
<sal_uInt16
>(pBegin
- pBase
), cChar
);
329 // state stack manipulators
330 // ------------------------
331 void VectorOfOutDevStates::clearStateStack()
334 const OutDevState aDefaultState
;
335 m_aStates
.push_back(aDefaultState
);
338 OutDevState
& VectorOfOutDevStates::getState()
340 return m_aStates
.back();
343 const OutDevState
& VectorOfOutDevStates::getState() const
345 return m_aStates
.back();
348 void VectorOfOutDevStates::pushState(sal_uInt16 nFlags
)
350 m_aStates
.push_back( getState() );
351 getState().pushFlags
= nFlags
;
354 void VectorOfOutDevStates::popState()
356 if( getState().pushFlags
!= PUSH_ALL
)
358 // a state is pushed which is incomplete, i.e. does not
359 // restore everything to the previous stack level when
361 // That means, we take the old state, and restore every
362 // OutDevState member whose flag is set, from the new to the
363 // old state. Then the new state gets overwritten by the
366 // preset to-be-calculated new state with old state
367 OutDevState
aCalculatedNewState( getState() );
369 // selectively copy to-be-restored content over saved old
371 m_aStates
.pop_back();
373 const OutDevState
& rNewState( getState() );
375 if( (aCalculatedNewState
.pushFlags
& PUSH_LINECOLOR
) )
377 aCalculatedNewState
.lineColor
= rNewState
.lineColor
;
378 aCalculatedNewState
.isLineColorSet
= rNewState
.isLineColorSet
;
381 if( (aCalculatedNewState
.pushFlags
& PUSH_FILLCOLOR
) )
383 aCalculatedNewState
.fillColor
= rNewState
.fillColor
;
384 aCalculatedNewState
.isFillColorSet
= rNewState
.isFillColorSet
;
387 if( (aCalculatedNewState
.pushFlags
& PUSH_FONT
) )
389 aCalculatedNewState
.xFont
= rNewState
.xFont
;
390 aCalculatedNewState
.fontRotation
= rNewState
.fontRotation
;
391 aCalculatedNewState
.textReliefStyle
= rNewState
.textReliefStyle
;
392 aCalculatedNewState
.textOverlineStyle
= rNewState
.textOverlineStyle
;
393 aCalculatedNewState
.textUnderlineStyle
= rNewState
.textUnderlineStyle
;
394 aCalculatedNewState
.textStrikeoutStyle
= rNewState
.textStrikeoutStyle
;
395 aCalculatedNewState
.textEmphasisMarkStyle
= rNewState
.textEmphasisMarkStyle
;
396 aCalculatedNewState
.isTextEffectShadowSet
= rNewState
.isTextEffectShadowSet
;
397 aCalculatedNewState
.isTextWordUnderlineSet
= rNewState
.isTextWordUnderlineSet
;
398 aCalculatedNewState
.isTextOutlineModeSet
= rNewState
.isTextOutlineModeSet
;
401 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTCOLOR
) )
403 aCalculatedNewState
.textColor
= rNewState
.textColor
;
406 if( (aCalculatedNewState
.pushFlags
& PUSH_MAPMODE
) )
408 aCalculatedNewState
.mapModeTransform
= rNewState
.mapModeTransform
;
411 if( (aCalculatedNewState
.pushFlags
& PUSH_CLIPREGION
) )
413 aCalculatedNewState
.clip
= rNewState
.clip
;
414 aCalculatedNewState
.clipRect
= rNewState
.clipRect
;
415 aCalculatedNewState
.xClipPoly
= rNewState
.xClipPoly
;
418 // TODO(F2): Raster ops NYI
419 // if( (aCalculatedNewState.pushFlags & PUSH_RASTEROP) )
423 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTFILLCOLOR
) )
425 aCalculatedNewState
.textFillColor
= rNewState
.textFillColor
;
426 aCalculatedNewState
.isTextFillColorSet
= rNewState
.isTextFillColorSet
;
429 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTALIGN
) )
431 aCalculatedNewState
.textReferencePoint
= rNewState
.textReferencePoint
;
434 // TODO(F1): Refpoint handling NYI
435 // if( (aCalculatedNewState.pushFlags & PUSH_REFPOINT) )
439 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTLINECOLOR
) )
441 aCalculatedNewState
.textLineColor
= rNewState
.textLineColor
;
442 aCalculatedNewState
.isTextLineColorSet
= rNewState
.isTextLineColorSet
;
445 if( (aCalculatedNewState
.pushFlags
& PUSH_TEXTLAYOUTMODE
) )
447 aCalculatedNewState
.textAlignment
= rNewState
.textAlignment
;
448 aCalculatedNewState
.textDirection
= rNewState
.textDirection
;
451 // TODO(F2): Text language handling NYI
452 // if( (aCalculatedNewState.pushFlags & PUSH_TEXTLANGUAGE) )
456 // always copy push mode
457 aCalculatedNewState
.pushFlags
= rNewState
.pushFlags
;
460 getState() = aCalculatedNewState
;
464 m_aStates
.pop_back();
468 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon
& rPolyPoly
,
469 const ActionFactoryParameters
& rParms
)
471 const OutDevState
& rState( rParms
.mrStates
.getState() );
472 if( (!rState
.isLineColorSet
&&
473 !rState
.isFillColorSet
) ||
474 (rState
.lineColor
.getLength() == 0 &&
475 rState
.fillColor
.getLength() == 0) )
480 ActionSharedPtr
pPolyAction(
481 internal::PolyPolyActionFactory::createPolyPolyAction(
482 rPolyPoly
, rParms
.mrCanvas
, rState
) );
489 rParms
.mrCurrActionIndex
) );
491 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
497 bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon
& rPoly
,
498 const ActionFactoryParameters
& rParms
)
500 return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly
),
504 void ImplRenderer::skipContent( GDIMetaFile
& rMtf
,
505 const char* pCommentString
,
506 sal_Int32
& io_rCurrActionIndex
) const
508 ENSURE_OR_THROW( pCommentString
,
509 "ImplRenderer::skipContent(): NULL string given" );
511 MetaAction
* pCurrAct
;
512 while( (pCurrAct
=rMtf
.NextAction()) != NULL
)
514 // increment action index, we've skipped an action.
515 ++io_rCurrActionIndex
;
517 if( pCurrAct
->GetType() == META_COMMENT_ACTION
&&
518 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase(
521 // requested comment found, done
530 bool ImplRenderer::isActionContained( GDIMetaFile
& rMtf
,
531 const char* pCommentString
,
532 sal_uInt16 nType
) const
534 ENSURE_OR_THROW( pCommentString
,
535 "ImplRenderer::isActionContained(): NULL string given" );
539 // at least _one_ call to GDIMetaFile::NextAction() is
541 sal_uIntPtr
nPos( 1 );
543 MetaAction
* pCurrAct
;
544 while( (pCurrAct
=rMtf
.NextAction()) != NULL
)
546 if( pCurrAct
->GetType() == nType
)
548 bRet
= true; // action type found
552 if( pCurrAct
->GetType() == META_COMMENT_ACTION
&&
553 static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCase(
556 // delimiting end comment found, done
557 bRet
= false; // not yet found
564 // rewind metafile to previous position (this method must
565 // not change the current metaaction)
571 // EOF, and not yet found
578 void ImplRenderer::createGradientAction( const ::PolyPolygon
& rPoly
,
579 const ::Gradient
& rGradient
,
580 const ActionFactoryParameters
& rParms
,
581 bool bIsPolygonRectangle
,
582 bool bSubsettableActions
)
584 DBG_TESTSOLARMUTEX();
586 ::basegfx::B2DPolyPolygon
aDevicePoly( rPoly
.getB2DPolyPolygon() );
587 aDevicePoly
.transform( rParms
.mrStates
.getState().mapModeTransform
);
589 // decide, whether this gradient can be rendered natively
590 // by the canvas, or must be emulated via VCL gradient
591 // action extraction.
592 const sal_uInt16
nSteps( rGradient
.GetSteps() );
594 if( // step count is infinite, can use native canvas
597 // step count is sufficiently high, such that no
598 // discernible difference should be visible.
601 uno::Reference
< lang::XMultiServiceFactory
> xFactory(
602 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
606 rendering::Texture aTexture
;
608 aTexture
.RepeatModeX
= rendering::TexturingMode::CLAMP
;
609 aTexture
.RepeatModeY
= rendering::TexturingMode::CLAMP
;
610 aTexture
.Alpha
= 1.0;
613 // setup start/end color values
614 // ----------------------------
616 // scale color coefficients with gradient intensities
617 const sal_uInt16
nStartIntensity( rGradient
.GetStartIntensity() );
618 ::Color
aVCLStartColor( rGradient
.GetStartColor() );
619 aVCLStartColor
.SetRed( (sal_uInt8
)(aVCLStartColor
.GetRed() * nStartIntensity
/ 100) );
620 aVCLStartColor
.SetGreen( (sal_uInt8
)(aVCLStartColor
.GetGreen() * nStartIntensity
/ 100) );
621 aVCLStartColor
.SetBlue( (sal_uInt8
)(aVCLStartColor
.GetBlue() * nStartIntensity
/ 100) );
623 const sal_uInt16
nEndIntensity( rGradient
.GetEndIntensity() );
624 ::Color
aVCLEndColor( rGradient
.GetEndColor() );
625 aVCLEndColor
.SetRed( (sal_uInt8
)(aVCLEndColor
.GetRed() * nEndIntensity
/ 100) );
626 aVCLEndColor
.SetGreen( (sal_uInt8
)(aVCLEndColor
.GetGreen() * nEndIntensity
/ 100) );
627 aVCLEndColor
.SetBlue( (sal_uInt8
)(aVCLEndColor
.GetBlue() * nEndIntensity
/ 100) );
629 uno::Reference
<rendering::XColorSpace
> xColorSpace(
630 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace());
631 const uno::Sequence
< double > aStartColor(
632 ::vcl::unotools::colorToDoubleSequence( aVCLStartColor
,
634 const uno::Sequence
< double > aEndColor(
635 ::vcl::unotools::colorToDoubleSequence( aVCLEndColor
,
638 uno::Sequence
< uno::Sequence
< double > > aColors(2);
639 uno::Sequence
< double > aStops(2);
641 if( rGradient
.GetStyle() == GradientStyle_AXIAL
)
650 aColors
[0] = aEndColor
;
651 aColors
[1] = aStartColor
;
652 aColors
[2] = aEndColor
;
659 aColors
[0] = aStartColor
;
660 aColors
[1] = aEndColor
;
663 const ::basegfx::B2DRectangle
aBounds(
664 ::basegfx::tools::getRange(aDevicePoly
) );
665 const ::basegfx::B2DVector
aOffset(
666 rGradient
.GetOfsX() / 100.0,
667 rGradient
.GetOfsY() / 100.0);
668 double fRotation( rGradient
.GetAngle() * M_PI
/ 1800.0 );
669 const double fBorder( rGradient
.GetBorder() / 100.0 );
671 basegfx::B2DHomMatrix aRot90
;
672 aRot90
.rotate(M_PI_2
);
674 basegfx::ODFGradientInfo aGradInfo
;
675 OUString aGradientService
;
676 switch( rGradient
.GetStyle() )
678 case GradientStyle_LINEAR
:
679 basegfx::tools::createLinearODFGradientInfo(aGradInfo
,
684 // map ODF to svg gradient orientation - x
685 // instead of y direction
686 aGradInfo
.maTextureTransform
= aGradInfo
.maTextureTransform
* aRot90
;
687 aGradientService
= "LinearGradient";
690 case GradientStyle_AXIAL
:
692 // Adapt the border so that it is suitable
693 // for the axial gradient. An axial
694 // gradient consists of two linear
695 // gradients. Each of those covers half
696 // of the total size. In order to
697 // compensate for the condensed display of
698 // the linear gradients, we have to
699 // enlarge the area taken up by the actual
700 // gradient (1-fBorder). After that we
701 // have to turn the result back into a
702 // border value, hence the second (left
704 const double fAxialBorder (1-2*(1-fBorder
));
705 basegfx::tools::createAxialODFGradientInfo(aGradInfo
,
710 // map ODF to svg gradient orientation - x
711 // instead of y direction
712 aGradInfo
.maTextureTransform
= aGradInfo
.maTextureTransform
* aRot90
;
714 // map ODF axial gradient to 3-stop linear
715 // gradient - shift left by 0.5
716 basegfx::B2DHomMatrix aShift
;
717 aShift
.translate(-0.5,0);
718 aGradInfo
.maTextureTransform
= aGradInfo
.maTextureTransform
* aShift
;
720 aGradientService
= "LinearGradient";
724 case GradientStyle_RADIAL
:
725 basegfx::tools::createRadialODFGradientInfo(aGradInfo
,
730 aGradientService
= "EllipticalGradient";
733 case GradientStyle_ELLIPTICAL
:
734 basegfx::tools::createEllipticalODFGradientInfo(aGradInfo
,
740 aGradientService
= "EllipticalGradient";
743 case GradientStyle_SQUARE
:
744 basegfx::tools::createSquareODFGradientInfo(aGradInfo
,
750 aGradientService
= "RectangularGradient";
753 case GradientStyle_RECT
:
754 basegfx::tools::createRectangularODFGradientInfo(aGradInfo
,
760 aGradientService
= "RectangularGradient";
764 ENSURE_OR_THROW( false,
765 "ImplRenderer::createGradientAction(): Unexpected gradient type" );
769 ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture
.AffineTransform
,
770 aGradInfo
.maTextureTransform
);
772 uno::Sequence
<uno::Any
> args(3);
773 beans::PropertyValue aProp
;
774 aProp
.Name
= "Colors";
775 aProp
.Value
<<= aColors
;
777 aProp
.Name
= "Stops";
778 aProp
.Value
<<= aStops
;
780 aProp
.Name
= "AspectRatio";
781 aProp
.Value
<<= aGradInfo
.mfAspectRatio
;
784 aTexture
.Gradient
.set(
785 xFactory
->createInstanceWithArguments(aGradientService
,
788 if( aTexture
.Gradient
.is() )
790 ActionSharedPtr
pPolyAction(
791 internal::PolyPolyActionFactory::createPolyPolyAction(
794 rParms
.mrStates
.getState(),
802 rParms
.mrCurrActionIndex
) );
804 rParms
.mrCurrActionIndex
+= pPolyAction
->getActionCount()-1;
807 // done, using native gradients
813 // cannot currently use native canvas gradients, as a
814 // finite step size is given (this funny feature is not
815 // supported by the XCanvas API)
816 rParms
.mrStates
.pushState(PUSH_ALL
);
818 if( !bIsPolygonRectangle
)
820 // only clip, if given polygon is not a rectangle in
821 // the first place (the gradient is always limited to
822 // the given bound rect)
830 rParms
.mrVDev
.AddGradientActions( rPoly
.GetBoundRect(),
834 createActions( aTmpMtf
, rParms
, bSubsettableActions
);
836 rParms
.mrStates
.popState();
839 uno::Reference
< rendering::XCanvasFont
> ImplRenderer::createFont( double& o_rFontRotation
,
841 const ActionFactoryParameters
& rParms
) const
843 rendering::FontRequest aFontRequest
;
845 if( rParms
.mrParms
.maFontName
.is_initialized() )
846 aFontRequest
.FontDescription
.FamilyName
= *rParms
.mrParms
.maFontName
;
848 aFontRequest
.FontDescription
.FamilyName
= rFont
.GetName();
850 aFontRequest
.FontDescription
.StyleName
= rFont
.GetStyleName();
852 aFontRequest
.FontDescription
.IsSymbolFont
= (rFont
.GetCharSet() == RTL_TEXTENCODING_SYMBOL
) ? util::TriState_YES
: util::TriState_NO
;
853 aFontRequest
.FontDescription
.IsVertical
= rFont
.IsVertical() ? util::TriState_YES
: util::TriState_NO
;
855 // TODO(F2): improve vclenum->panose conversion
856 aFontRequest
.FontDescription
.FontDescription
.Weight
=
857 rParms
.mrParms
.maFontWeight
.is_initialized() ?
858 *rParms
.mrParms
.maFontWeight
:
859 ::canvas::tools::numeric_cast
<sal_Int8
>( ::basegfx::fround( rFont
.GetWeight() ) );
860 aFontRequest
.FontDescription
.FontDescription
.Letterform
=
861 rParms
.mrParms
.maFontLetterForm
.is_initialized() ?
862 *rParms
.mrParms
.maFontLetterForm
:
863 (rFont
.GetItalic() == ITALIC_NONE
) ? 0 : 9;
864 aFontRequest
.FontDescription
.FontDescription
.Proportion
=
865 rParms
.mrParms
.maFontProportion
.is_initialized() ?
866 *rParms
.mrParms
.maFontProportion
:
867 (rFont
.GetPitch() == PITCH_FIXED
)
868 ? rendering::PanoseProportion::MONO_SPACED
869 : rendering::PanoseProportion::ANYTHING
;
871 LanguageType aLang
= rFont
.GetLanguage();
872 aFontRequest
.Locale
= LanguageTag( aLang
).getLocale( false);
874 // setup state-local text transformation,
875 // if the font be rotated
876 const short nFontAngle( rFont
.GetOrientation() );
877 if( nFontAngle
!= 0 )
879 // set to unity transform rotated by font angle
880 const double nAngle( nFontAngle
* (F_PI
/ 1800.0) );
881 o_rFontRotation
= -nAngle
;
885 o_rFontRotation
= 0.0;
888 geometry::Matrix2D aFontMatrix
;
889 ::canvas::tools::setIdentityMatrix2D( aFontMatrix
);
891 // TODO(F2): use correct scale direction, font
892 // height might be width or anything else
894 // TODO(Q3): This code smells of programming by
895 // coincidence (the next two if statements)
896 const ::Size
rFontSizeLog( rFont
.GetSize() );
897 const sal_Int32 nFontWidthLog
= rFontSizeLog
.Width();
898 if( nFontWidthLog
!= 0 )
900 ::Font aTestFont
= rFont
;
901 aTestFont
.SetWidth( 0 );
902 sal_Int32 nNormalWidth
= rParms
.mrVDev
.GetFontMetric( aTestFont
).GetWidth();
903 if( nNormalWidth
!= nFontWidthLog
)
905 aFontMatrix
.m00
= (double)nFontWidthLog
/ nNormalWidth
;
908 // #i52608# apply map mode scale also to font matrix - an
909 // anisotrophic mapmode must be reflected in an
910 // anisotrophic font matrix scale.
911 const OutDevState
& rState( rParms
.mrStates
.getState() );
912 if( !::basegfx::fTools::equal(
913 rState
.mapModeTransform
.get(0,0),
914 rState
.mapModeTransform
.get(1,1)) )
916 const double nScaleX( rState
.mapModeTransform
.get(0,0) );
917 const double nScaleY( rState
.mapModeTransform
.get(1,1) );
919 // note: no reason to check for division by zero, we
920 // always have the value closer (or equal) to zero as
922 if( fabs(nScaleX
) < fabs(nScaleY
) )
923 aFontMatrix
.m00
*= nScaleX
/ nScaleY
;
925 aFontMatrix
.m11
*= nScaleY
/ nScaleX
;
927 aFontRequest
.CellSize
= (rState
.mapModeTransform
* ::vcl::unotools::b2DSizeFromSize(rFontSizeLog
)).getY();
929 return rParms
.mrCanvas
->getUNOCanvas()->createFont( aFontRequest
,
930 uno::Sequence
< beans::PropertyValue
>(),
934 // create text effects such as shadow/relief/embossed
935 void ImplRenderer::createTextAction( const ::Point
& rStartPoint
,
936 const String rString
,
939 const sal_Int32
* pCharWidths
,
940 const ActionFactoryParameters
& rParms
,
941 bool bSubsettableActions
)
943 ENSURE_OR_THROW( nIndex
>= 0 && nLength
<= rString
.Len() + nIndex
,
944 "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
947 return; // zero-length text, no visible output
949 const OutDevState
& rState( rParms
.mrStates
.getState() );
951 // TODO(F2): implement all text effects
952 // if( rState.textAlignment ); // TODO(F2): NYI
954 ::Color
aShadowColor( COL_AUTO
);
955 ::Color
aReliefColor( COL_AUTO
);
956 ::Size aShadowOffset
;
957 ::Size aReliefOffset
;
959 uno::Reference
<rendering::XColorSpace
> xColorSpace(
960 rParms
.mrCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
962 if( rState
.isTextEffectShadowSet
)
964 // calculate shadow offset (similar to outdev3.cxx)
965 // TODO(F3): better match with outdev3.cxx
966 sal_Int32 nShadowOffset
= static_cast<sal_Int32
>(1.5 + ((rParms
.mrVDev
.GetFont().GetHeight()-24.0)/24.0));
967 if( nShadowOffset
< 1 )
970 aShadowOffset
.setWidth( nShadowOffset
);
971 aShadowOffset
.setHeight( nShadowOffset
);
973 // determine shadow color (from outdev3.cxx)
974 ::Color aTextColor
= ::vcl::unotools::doubleSequenceToColor(
975 rState
.textColor
, xColorSpace
);
976 bool bIsDark
= (aTextColor
.GetColor() == COL_BLACK
)
977 || (aTextColor
.GetLuminance() < 8);
979 aShadowColor
= bIsDark
? COL_LIGHTGRAY
: COL_BLACK
;
980 aShadowColor
.SetTransparency( aTextColor
.GetTransparency() );
983 if( rState
.textReliefStyle
)
985 // calculate relief offset (similar to outdev3.cxx)
986 sal_Int32 nReliefOffset
= rParms
.mrVDev
.PixelToLogic( Size( 1, 1 ) ).Height();
987 nReliefOffset
+= nReliefOffset
/2;
988 if( nReliefOffset
< 1 )
991 if( rState
.textReliefStyle
== RELIEF_ENGRAVED
)
992 nReliefOffset
= -nReliefOffset
;
994 aReliefOffset
.setWidth( nReliefOffset
);
995 aReliefOffset
.setHeight( nReliefOffset
);
997 // determine relief color (from outdev3.cxx)
998 ::Color aTextColor
= ::vcl::unotools::doubleSequenceToColor(
999 rState
.textColor
, xColorSpace
);
1001 aReliefColor
= ::Color( COL_LIGHTGRAY
);
1003 // we don't have a automatic color, so black is always
1004 // drawn on white (literally copied from
1005 // vcl/source/gdi/outdev3.cxx)
1006 if( aTextColor
.GetColor() == COL_BLACK
)
1008 aTextColor
= ::Color( COL_WHITE
);
1009 rParms
.mrStates
.getState().textColor
=
1010 ::vcl::unotools::colorToDoubleSequence(
1011 aTextColor
, xColorSpace
);
1014 if( aTextColor
.GetColor() == COL_WHITE
)
1015 aReliefColor
= ::Color( COL_BLACK
);
1016 aReliefColor
.SetTransparency( aTextColor
.GetTransparency() );
1019 // create the actual text action
1020 ActionSharedPtr
pTextAction(
1021 TextActionFactory::createTextAction(
1035 bSubsettableActions
) );
1037 ActionSharedPtr pStrikeoutTextAction
;
1039 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
|| rState
.textStrikeoutStyle
== STRIKEOUT_SLASH
)
1041 long nWidth
= rParms
.mrVDev
.GetTextWidth( rString
,nIndex
,nLength
);
1043 sal_Unicode pChars
[4];
1044 if ( rState
.textStrikeoutStyle
== STRIKEOUT_X
)
1048 pChars
[3]=pChars
[2]=pChars
[1]=pChars
[0];
1050 long nStrikeoutWidth
= (rParms
.mrVDev
.GetTextWidth(
1051 OUString(pChars
, SAL_N_ELEMENTS(pChars
))) + 2) / 4;
1053 if( nStrikeoutWidth
<= 0 )
1054 nStrikeoutWidth
= 1;
1056 long nMaxWidth
= nStrikeoutWidth
/2;
1057 if ( nMaxWidth
< 2 )
1059 nMaxWidth
+= nWidth
+ 1;
1061 long nFullStrikeoutWidth
= 0;
1062 String aStrikeoutText
;
1063 while( (nFullStrikeoutWidth
+=nStrikeoutWidth
) < nMaxWidth
+1 )
1064 aStrikeoutText
+= pChars
[0];
1066 xub_StrLen nLen
= aStrikeoutText
.Len();
1070 long nInterval
= ( nWidth
- nStrikeoutWidth
* nLen
) / nLen
;
1071 nStrikeoutWidth
+= nInterval
;
1072 sal_Int32
* pStrikeoutCharWidths
= new sal_Int32
[nLen
];
1074 for ( int i
= 0;i
<nLen
; i
++)
1076 pStrikeoutCharWidths
[i
] = nStrikeoutWidth
;
1079 for ( int i
= 1;i
< nLen
; i
++ )
1081 pStrikeoutCharWidths
[ i
] += pStrikeoutCharWidths
[ i
-1 ];
1084 sal_Int32 nStartPos
= 0;
1086 pStrikeoutTextAction
=
1087 TextActionFactory::createTextAction(
1095 aStrikeoutText
.Len(),
1096 pStrikeoutCharWidths
,
1101 bSubsettableActions
) ;
1107 maActions
.push_back(
1110 rParms
.mrCurrActionIndex
) );
1112 if ( pStrikeoutTextAction
)
1114 maActions
.push_back(
1116 pStrikeoutTextAction
,
1117 rParms
.mrCurrActionIndex
) );
1120 rParms
.mrCurrActionIndex
+= pTextAction
->getActionCount()-1;
1124 void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon
& rClipPoly
,
1125 const ActionFactoryParameters
& rParms
,
1128 ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
1129 ::basegfx::B2DPolyPolygon
aClipPoly( rClipPoly
);
1131 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1132 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1134 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1135 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1138 (bEmptyClipRect
&& bEmptyClipPoly
) )
1140 rState
.clip
= rClipPoly
;
1144 if( !bEmptyClipRect
)
1146 // TODO(P3): Use Liang-Barsky polygon clip here,
1147 // after all, one object is just a rectangle!
1149 // convert rect to polygon beforehand, must revert
1150 // to general polygon clipping here.
1151 rState
.clip
= ::basegfx::B2DPolyPolygon(
1152 ::basegfx::tools::createPolygonFromRect(
1153 // #121100# VCL rectangular clips always
1154 // include one more pixel to the right
1156 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1157 rState
.clipRect
.Top(),
1158 rState
.clipRect
.Right()+1,
1159 rState
.clipRect
.Bottom()+1 ) ) );
1163 rState
.clip
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
1164 aClipPoly
, rState
.clip
, true, false);
1167 // by now, our clip resides in the OutDevState::clip
1169 rState
.clipRect
.SetEmpty();
1171 if( rState
.clip
.count() == 0 )
1173 if( rState
.clipRect
.IsEmpty() )
1175 rState
.xClipPoly
.clear();
1179 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1180 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1181 ::basegfx::B2DPolyPolygon(
1182 ::basegfx::tools::createPolygonFromRect(
1183 // #121100# VCL rectangular clips
1184 // always include one more pixel to
1185 // the right and the bottom
1186 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1187 rState
.clipRect
.Top(),
1188 rState
.clipRect
.Right()+1,
1189 rState
.clipRect
.Bottom()+1 ) ) ) );
1194 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1195 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1200 void ImplRenderer::updateClipping( const ::Rectangle
& rClipRect
,
1201 const ActionFactoryParameters
& rParms
,
1204 ::cppcanvas::internal::OutDevState
& rState( rParms
.mrStates
.getState() );
1206 const bool bEmptyClipRect( rState
.clipRect
.IsEmpty() );
1207 const bool bEmptyClipPoly( rState
.clip
.count() == 0 );
1209 ENSURE_OR_THROW( bEmptyClipPoly
|| bEmptyClipRect
,
1210 "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1213 (bEmptyClipRect
&& bEmptyClipPoly
) )
1215 rState
.clipRect
= rClipRect
;
1216 rState
.clip
.clear();
1218 else if( bEmptyClipPoly
)
1220 rState
.clipRect
.Intersection( rClipRect
);
1221 rState
.clip
.clear();
1225 // TODO(P3): Handle a fourth case here, when all clip
1226 // polygons are rectangular, once B2DMultiRange's
1227 // sweep line implementation is done.
1229 // general case: convert to polygon and clip
1230 // -----------------------------------------
1232 // convert rect to polygon beforehand, must revert
1233 // to general polygon clipping here.
1234 ::basegfx::B2DPolyPolygon
aClipPoly(
1235 ::basegfx::tools::createPolygonFromRect(
1236 ::basegfx::B2DRectangle( rClipRect
.Left(),
1239 rClipRect
.Bottom() ) ) );
1241 rState
.clipRect
.SetEmpty();
1244 rState
.clip
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
1245 aClipPoly
, rState
.clip
, true, false);
1248 if( rState
.clip
.count() == 0 )
1250 if( rState
.clipRect
.IsEmpty() )
1252 rState
.xClipPoly
.clear();
1256 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1257 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1258 ::basegfx::B2DPolyPolygon(
1259 ::basegfx::tools::createPolygonFromRect(
1260 // #121100# VCL rectangular clips
1261 // always include one more pixel to
1262 // the right and the bottom
1263 ::basegfx::B2DRectangle( rState
.clipRect
.Left(),
1264 rState
.clipRect
.Top(),
1265 rState
.clipRect
.Right()+1,
1266 rState
.clipRect
.Bottom()+1 ) ) ) );
1271 rState
.xClipPoly
= ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1272 rParms
.mrCanvas
->getUNOCanvas()->getDevice(),
1277 bool ImplRenderer::createActions( GDIMetaFile
& rMtf
,
1278 const ActionFactoryParameters
& rFactoryParms
,
1279 bool bSubsettableActions
)
1281 /* TODO(P2): interpret mtf-comments
1282 ================================
1284 - gradient fillings (do that via comments)
1286 - think about mapping. _If_ we do everything in logical
1287 coordinates (which would solve the probs for stroke
1288 widths and text offsets), then we would have to
1289 recalc scaling for every drawing operation. This is
1290 because the outdev map mode might change at any time.
1291 Also keep in mind, that, although we've double precision
1292 float arithmetic now, different offsets might still
1293 generate different roundings (aka
1294 'OutputDevice::SetPixelOffset())
1298 // alias common parameters
1299 VectorOfOutDevStates
& rStates(rFactoryParms
.mrStates
);
1300 const CanvasSharedPtr
& rCanvas(rFactoryParms
.mrCanvas
);
1301 ::VirtualDevice
& rVDev(rFactoryParms
.mrVDev
);
1302 const Parameters
& rParms(rFactoryParms
.mrParms
);
1303 sal_Int32
& io_rCurrActionIndex(rFactoryParms
.mrCurrActionIndex
);
1306 // Loop over every metaaction
1307 // ==========================
1308 MetaAction
* pCurrAct
;
1310 // TODO(P1): think about caching
1311 for( pCurrAct
=rMtf
.FirstAction();
1313 pCurrAct
= rMtf
.NextAction() )
1315 // execute every action, to keep VDev state up-to-date
1316 // currently used only for
1318 // - the line/fill color when processing a META_TRANSPARENT_ACTION
1319 // - SetFont to process font metric specific actions
1320 pCurrAct
->Execute( &rVDev
);
1322 SAL_INFO("cppcanvas.emf", "MTF\trecord type: 0x" << pCurrAct
->GetType() << " (" << pCurrAct
->GetType() << ")");
1324 switch( pCurrAct
->GetType() )
1326 // ------------------------------------------------------------
1328 // In the first part of this monster-switch, we
1329 // handle all state-changing meta actions. These
1330 // are all handled locally.
1332 // ------------------------------------------------------------
1334 case META_PUSH_ACTION
:
1336 MetaPushAction
* pPushAction
= static_cast<MetaPushAction
*>(pCurrAct
);
1337 rStates
.pushState(pPushAction
->GetFlags());
1341 case META_POP_ACTION
:
1345 case META_TEXTLANGUAGE_ACTION
:
1346 // FALLTHROUGH intended
1347 case META_REFPOINT_ACTION
:
1348 // handled via pCurrAct->Execute( &rVDev )
1351 case META_MAPMODE_ACTION
:
1352 // modify current mapModeTransformation
1353 // transformation, such that subsequent
1354 // coordinates map correctly
1355 tools::calcLogic2PixelAffineTransform( rStates
.getState().mapModeTransform
,
1359 // monitor clip regions, to assemble clip polygon on our own
1360 case META_CLIPREGION_ACTION
:
1362 MetaClipRegionAction
* pClipAction
= static_cast<MetaClipRegionAction
*>(pCurrAct
);
1364 if( !pClipAction
->IsClipping() )
1367 rStates
.getState().clip
.clear();
1371 if( !pClipAction
->GetRegion().HasPolyPolygon() )
1373 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1374 "region encountered, falling back to bounding box!" );
1376 // #121806# explicitly kept integer
1377 Rectangle
aClipRect(
1379 pClipAction
->GetRegion().GetBoundRect() ) );
1381 // intersect current clip with given rect
1389 // set new clip polygon (don't intersect
1390 // with old one, just set it)
1392 // #121806# explicitly kept integer
1395 pClipAction
->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
1404 case META_ISECTRECTCLIPREGION_ACTION
:
1406 MetaISectRectClipRegionAction
* pClipAction
= static_cast<MetaISectRectClipRegionAction
*>(pCurrAct
);
1408 // #121806# explicitly kept integer
1409 Rectangle
aClipRect(
1410 rVDev
.LogicToPixel( pClipAction
->GetRect() ) );
1412 // intersect current clip with given rect
1421 case META_ISECTREGIONCLIPREGION_ACTION
:
1423 MetaISectRegionClipRegionAction
* pClipAction
= static_cast<MetaISectRegionClipRegionAction
*>(pCurrAct
);
1425 if( !pClipAction
->GetRegion().HasPolyPolygon() )
1427 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1428 "region encountered, falling back to bounding box!" );
1430 // #121806# explicitly kept integer
1431 Rectangle
aClipRect(
1432 rVDev
.LogicToPixel( pClipAction
->GetRegion().GetBoundRect() ) );
1434 // intersect current clip with given rect
1442 // intersect current clip with given clip polygon
1444 // #121806# explicitly kept integer
1447 pClipAction
->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
1455 case META_MOVECLIPREGION_ACTION
:
1459 case META_LINECOLOR_ACTION
:
1460 if( !rParms
.maLineColor
.is_initialized() )
1462 setStateColor( static_cast<MetaLineColorAction
*>(pCurrAct
),
1463 rStates
.getState().isLineColorSet
,
1464 rStates
.getState().lineColor
,
1469 case META_FILLCOLOR_ACTION
:
1470 if( !rParms
.maFillColor
.is_initialized() )
1472 setStateColor( static_cast<MetaFillColorAction
*>(pCurrAct
),
1473 rStates
.getState().isFillColorSet
,
1474 rStates
.getState().fillColor
,
1479 case META_TEXTCOLOR_ACTION
:
1481 if( !rParms
.maTextColor
.is_initialized() )
1483 // Text color is set unconditionally, thus, no
1484 // use of setStateColor here
1485 ::Color
aColor( static_cast<MetaTextColorAction
*>(pCurrAct
)->GetColor() );
1487 // force alpha part of color to
1488 // opaque. transparent painting is done
1489 // explicitly via META_TRANSPARENT_ACTION
1490 aColor
.SetTransparency(0);
1492 rStates
.getState().textColor
=
1493 ::vcl::unotools::colorToDoubleSequence(
1495 rCanvas
->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1500 case META_TEXTFILLCOLOR_ACTION
:
1501 if( !rParms
.maTextColor
.is_initialized() )
1503 setStateColor( static_cast<MetaTextFillColorAction
*>(pCurrAct
),
1504 rStates
.getState().isTextFillColorSet
,
1505 rStates
.getState().textFillColor
,
1510 case META_TEXTLINECOLOR_ACTION
:
1511 if( !rParms
.maTextColor
.is_initialized() )
1513 setStateColor( static_cast<MetaTextLineColorAction
*>(pCurrAct
),
1514 rStates
.getState().isTextLineColorSet
,
1515 rStates
.getState().textLineColor
,
1520 case META_TEXTALIGN_ACTION
:
1522 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1523 const TextAlign
eTextAlign( static_cast<MetaTextAlignAction
*>(pCurrAct
)->GetTextAlign() );
1525 rState
.textReferencePoint
= eTextAlign
;
1529 case META_FONT_ACTION
:
1531 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1532 const ::Font
& rFont( static_cast<MetaFontAction
*>(pCurrAct
)->GetFont() );
1534 rState
.xFont
= createFont( rState
.fontRotation
,
1538 // TODO(Q2): define and use appropriate enumeration types
1539 rState
.textReliefStyle
= (sal_Int8
)rFont
.GetRelief();
1540 rState
.textOverlineStyle
= (sal_Int8
)rFont
.GetOverline();
1541 rState
.textUnderlineStyle
= rParms
.maFontUnderline
.is_initialized() ?
1542 (*rParms
.maFontUnderline
? (sal_Int8
)UNDERLINE_SINGLE
: (sal_Int8
)UNDERLINE_NONE
) :
1543 (sal_Int8
)rFont
.GetUnderline();
1544 rState
.textStrikeoutStyle
= (sal_Int8
)rFont
.GetStrikeout();
1545 rState
.textEmphasisMarkStyle
= (sal_Int8
)rFont
.GetEmphasisMark();
1546 rState
.isTextEffectShadowSet
= (rFont
.IsShadow() != sal_False
);
1547 rState
.isTextWordUnderlineSet
= (rFont
.IsWordLineMode() != sal_False
);
1548 rState
.isTextOutlineModeSet
= (rFont
.IsOutline() != sal_False
);
1552 case META_RASTEROP_ACTION
:
1556 case META_LAYOUTMODE_ACTION
:
1558 // TODO(F2): A lot is missing here
1559 int nLayoutMode
= static_cast<MetaLayoutModeAction
*>(pCurrAct
)->GetLayoutMode();
1560 ::cppcanvas::internal::OutDevState
& rState
= rStates
.getState();
1561 switch( nLayoutMode
& (TEXT_LAYOUT_BIDI_RTL
|TEXT_LAYOUT_BIDI_STRONG
) )
1563 case TEXT_LAYOUT_BIDI_LTR
:
1564 rState
.textDirection
= rendering::TextDirection::WEAK_LEFT_TO_RIGHT
;
1567 case (TEXT_LAYOUT_BIDI_LTR
| TEXT_LAYOUT_BIDI_STRONG
):
1568 rState
.textDirection
= rendering::TextDirection::STRONG_LEFT_TO_RIGHT
;
1571 case TEXT_LAYOUT_BIDI_RTL
:
1572 rState
.textDirection
= rendering::TextDirection::WEAK_RIGHT_TO_LEFT
;
1575 case (TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_BIDI_STRONG
):
1576 rState
.textDirection
= rendering::TextDirection::STRONG_RIGHT_TO_LEFT
;
1580 rState
.textAlignment
= 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1581 if( (nLayoutMode
& (TEXT_LAYOUT_BIDI_RTL
| TEXT_LAYOUT_TEXTORIGIN_RIGHT
) )
1582 && !(nLayoutMode
& TEXT_LAYOUT_TEXTORIGIN_LEFT
) )
1584 rState
.textAlignment
= 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1589 // ------------------------------------------------------------
1591 // In the second part of this monster-switch, we
1592 // handle all recursing meta actions. These are the
1593 // ones generating a metafile by themselves, which is
1594 // then processed by recursively calling this method.
1596 // ------------------------------------------------------------
1598 case META_GRADIENT_ACTION
:
1600 MetaGradientAction
* pGradAct
= static_cast<MetaGradientAction
*>(pCurrAct
);
1601 createGradientAction( ::Polygon( pGradAct
->GetRect() ),
1602 pGradAct
->GetGradient(),
1605 bSubsettableActions
);
1609 case META_HATCH_ACTION
:
1611 // TODO(F2): use native Canvas hatches here
1612 GDIMetaFile aTmpMtf
;
1614 rVDev
.AddHatchActions( static_cast<MetaHatchAction
*>(pCurrAct
)->GetPolyPolygon(),
1615 static_cast<MetaHatchAction
*>(pCurrAct
)->GetHatch(),
1617 createActions( aTmpMtf
, rFactoryParms
,
1618 bSubsettableActions
);
1622 case META_EPS_ACTION
:
1624 MetaEPSAction
* pAct
= static_cast<MetaEPSAction
*>(pCurrAct
);
1625 const GDIMetaFile
& rSubstitute
= pAct
->GetSubstitute();
1627 // #121806# explicitly kept integer
1628 const Size
aMtfSize( rSubstitute
.GetPrefSize() );
1629 const Size
aMtfSizePixPre( rVDev
.LogicToPixel( aMtfSize
,
1630 rSubstitute
.GetPrefMapMode() ) );
1632 // #i44110# correct null-sized output - there
1633 // are metafiles which have zero size in at
1634 // least one dimension
1635 const Size
aMtfSizePix( ::std::max( aMtfSizePixPre
.Width(), 1L ),
1636 ::std::max( aMtfSizePixPre
.Height(), 1L ) );
1638 // Setup local transform, such that the
1639 // metafile renders itself into the given
1641 rStates
.pushState(PUSH_ALL
);
1644 rVDev
.SetMapMode( rSubstitute
.GetPrefMapMode() );
1646 const ::Point
& rPos( rVDev
.LogicToPixel( pAct
->GetPoint() ) );
1647 const ::Size
& rSize( rVDev
.LogicToPixel( pAct
->GetSize() ) );
1649 rStates
.getState().transform
.translate( rPos
.X(),
1651 rStates
.getState().transform
.scale( (double)rSize
.Width() / aMtfSizePix
.Width(),
1652 (double)rSize
.Height() / aMtfSizePix
.Height() );
1654 createActions( const_cast<GDIMetaFile
&>(pAct
->GetSubstitute()),
1656 bSubsettableActions
);
1663 // handle metafile comments, to retrieve
1664 // meta-information for gradients, fills and
1665 // strokes. May skip actions, and may recurse.
1666 case META_COMMENT_ACTION
:
1668 MetaCommentAction
* pAct
= static_cast<MetaCommentAction
*>(pCurrAct
);
1671 if (pAct
->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_BEGIN")))
1673 MetaGradientExAction
* pGradAction
= NULL
;
1674 bool bDone( false );
1676 (pCurrAct
=rMtf
.NextAction()) != NULL
)
1678 switch( pCurrAct
->GetType() )
1680 // extract gradient info
1681 case META_GRADIENTEX_ACTION
:
1682 pGradAction
= static_cast<MetaGradientExAction
*>(pCurrAct
);
1685 // skip broken-down rendering, output gradient when sequence is ended
1686 case META_COMMENT_ACTION
:
1687 if( static_cast<MetaCommentAction
*>(pCurrAct
)->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_END")) )
1693 createGradientAction( pGradAction
->GetPolyPolygon(),
1694 pGradAction
->GetGradient(),
1697 bSubsettableActions
);
1704 // TODO(P2): Handle drawing layer strokes, via
1705 // XPATHSTROKE_SEQ_BEGIN comment
1707 // Handle drawing layer fills
1708 else if( pAct
->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("XPATHFILL_SEQ_BEGIN")) )
1710 const sal_uInt8
* pData
= pAct
->GetData();
1713 SvMemoryStream
aMemStm( (void*)pData
, pAct
->GetDataSize(), STREAM_READ
);
1715 SvtGraphicFill aFill
;
1718 // TODO(P2): Also handle gradients and
1719 // hatches like this
1721 // only evaluate comment for pure
1722 // bitmap fills. If a transparency
1723 // gradient is involved (denoted by
1724 // the FloatTransparent action), take
1725 // the normal meta actions.
1726 if( aFill
.getFillType() == SvtGraphicFill::fillTexture
&&
1727 !isActionContained( rMtf
,
1728 "XPATHFILL_SEQ_END",
1729 META_FLOATTRANSPARENT_ACTION
) )
1731 rendering::Texture aTexture
;
1733 // TODO(F1): the SvtGraphicFill
1734 // can also transport metafiles
1735 // here, handle that case, too
1737 aFill
.getGraphic( aGraphic
);
1739 BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
1740 const ::Size
aBmpSize( aBmpEx
.GetSizePixel() );
1742 ::SvtGraphicFill::Transform aTransform
;
1743 aFill
.getTransform( aTransform
);
1745 ::basegfx::B2DHomMatrix aMatrix
;
1747 // convert to basegfx matrix
1748 aMatrix
.set(0,0, aTransform
.matrix
[ 0 ] );
1749 aMatrix
.set(0,1, aTransform
.matrix
[ 1 ] );
1750 aMatrix
.set(0,2, aTransform
.matrix
[ 2 ] );
1751 aMatrix
.set(1,0, aTransform
.matrix
[ 3 ] );
1752 aMatrix
.set(1,1, aTransform
.matrix
[ 4 ] );
1753 aMatrix
.set(1,2, aTransform
.matrix
[ 5 ] );
1755 ::basegfx::B2DHomMatrix aScale
;
1756 aScale
.scale( aBmpSize
.Width(),
1757 aBmpSize
.Height() );
1759 // post-multiply with the bitmap
1760 // size (XCanvas' texture assumes
1761 // the given bitmap to be
1762 // normalized to [0,1]x[0,1]
1764 aMatrix
= aMatrix
* aScale
;
1766 // pre-multiply with the
1767 // logic-to-pixel scale factor
1768 // (the metafile comment works in
1769 // logical coordinates).
1770 ::basegfx::B2DHomMatrix aLogic2PixelTransform
;
1771 aMatrix
*= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform
,
1774 ::basegfx::unotools::affineMatrixFromHomMatrix(
1775 aTexture
.AffineTransform
,
1778 aTexture
.Alpha
= 1.0 - aFill
.getTransparency();
1780 ::vcl::unotools::xBitmapFromBitmapEx(
1781 rCanvas
->getUNOCanvas()->getDevice(),
1783 if( aFill
.isTiling() )
1785 aTexture
.RepeatModeX
= rendering::TexturingMode::REPEAT
;
1786 aTexture
.RepeatModeY
= rendering::TexturingMode::REPEAT
;
1790 aTexture
.RepeatModeX
= rendering::TexturingMode::NONE
;
1791 aTexture
.RepeatModeY
= rendering::TexturingMode::NONE
;
1794 ::PolyPolygon aPath
;
1795 aFill
.getPath( aPath
);
1797 ::basegfx::B2DPolyPolygon
aPoly( aPath
.getB2DPolyPolygon() );
1798 aPoly
.transform( rStates
.getState().mapModeTransform
);
1799 ActionSharedPtr
pPolyAction(
1800 internal::PolyPolyActionFactory::createPolyPolyAction(
1808 maActions
.push_back(
1811 io_rCurrActionIndex
) );
1813 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
1816 // skip broken-down render output
1818 "XPATHFILL_SEQ_END",
1819 io_rCurrActionIndex
);
1823 // Handle drawing layer fills
1824 else if( pAct
->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("EMF_PLUS")) ) {
1825 static int count
= -1, limit
= 0x7fffffff;
1828 if (char *env
= getenv ("EMF_PLUS_LIMIT")) {
1830 SAL_INFO ("cppcanvas.emf", "EMF+ records limit: " << limit
);
1833 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer, size: " << pAct
->GetDataSize ());
1835 processEMFPlus( pAct
, rFactoryParms
, rStates
.getState(), rCanvas
);
1837 } else if( pAct
->GetComment().equalsL(RTL_CONSTASCII_STRINGPARAM("EMF_PLUS_HEADER_INFO")) ) {
1838 SAL_INFO ("cppcanvas.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pAct
->GetDataSize ());
1840 SvMemoryStream
rMF ((void*) pAct
->GetData (), pAct
->GetDataSize (), STREAM_READ
);
1842 rMF
>> nFrameLeft
>> nFrameTop
>> nFrameRight
>> nFrameBottom
;
1843 SAL_INFO ("cppcanvas.emf", "EMF+ picture frame: " << nFrameLeft
<< "," << nFrameTop
<< " - " << nFrameRight
<< "," << nFrameBottom
);
1844 rMF
>> nPixX
>> nPixY
>> nMmX
>> nMmY
;
1845 SAL_INFO ("cppcanvas.emf", "EMF+ ref device pixel size: " << nPixX
<< "x" << nPixY
<< " mm size: " << nMmX
<< "x" << nMmY
);
1847 rMF
>> aBaseTransform
;
1848 //aWorldTransform.Set (aBaseTransform);
1853 // ------------------------------------------------------------
1855 // In the third part of this monster-switch, we
1856 // handle all 'acting' meta actions. These are all
1857 // processed by constructing function objects for
1858 // them, which will later ease caching.
1860 // ------------------------------------------------------------
1862 case META_POINT_ACTION
:
1864 const OutDevState
& rState( rStates
.getState() );
1865 if( rState
.lineColor
.getLength() )
1867 ActionSharedPtr
pPointAction(
1868 internal::PointActionFactory::createPointAction(
1869 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint(
1870 static_cast<MetaPointAction
*>(pCurrAct
)->GetPoint() ),
1876 maActions
.push_back(
1879 io_rCurrActionIndex
) );
1881 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1887 case META_PIXEL_ACTION
:
1889 const OutDevState
& rState( rStates
.getState() );
1890 if( rState
.lineColor
.getLength() )
1892 ActionSharedPtr
pPointAction(
1893 internal::PointActionFactory::createPointAction(
1894 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint(
1895 static_cast<MetaPixelAction
*>(pCurrAct
)->GetPoint() ),
1898 static_cast<MetaPixelAction
*>(pCurrAct
)->GetColor() ) );
1902 maActions
.push_back(
1905 io_rCurrActionIndex
) );
1907 io_rCurrActionIndex
+= pPointAction
->getActionCount()-1;
1913 case META_LINE_ACTION
:
1915 const OutDevState
& rState( rStates
.getState() );
1916 if( rState
.lineColor
.getLength() )
1918 MetaLineAction
* pLineAct
= static_cast<MetaLineAction
*>(pCurrAct
);
1920 const LineInfo
& rLineInfo( pLineAct
->GetLineInfo() );
1922 const ::basegfx::B2DPoint
aStartPoint(
1923 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( pLineAct
->GetStartPoint() ));
1924 const ::basegfx::B2DPoint
aEndPoint(
1925 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( pLineAct
->GetEndPoint() ));
1927 ActionSharedPtr pLineAction
;
1929 if( rLineInfo
.IsDefault() )
1933 internal::LineActionFactory::createLineAction(
1941 maActions
.push_back(
1944 io_rCurrActionIndex
) );
1946 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
1949 else if( LINE_NONE
!= rLineInfo
.GetStyle() )
1952 rendering::StrokeAttributes aStrokeAttributes
;
1954 setupStrokeAttributes( aStrokeAttributes
,
1958 // XCanvas can only stroke polygons,
1959 // not simple lines - thus, handle
1960 // this case via the polypolygon
1962 ::basegfx::B2DPolygon aPoly
;
1963 aPoly
.append( aStartPoint
);
1964 aPoly
.append( aEndPoint
);
1966 internal::PolyPolyActionFactory::createPolyPolyAction(
1967 ::basegfx::B2DPolyPolygon( aPoly
),
1968 rCanvas
, rState
, aStrokeAttributes
);
1972 maActions
.push_back(
1975 io_rCurrActionIndex
) );
1977 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
1980 // else: line style is default
1981 // (i.e. invisible), don't generate action
1986 case META_RECT_ACTION
:
1988 const Rectangle
& rRect(
1989 static_cast<MetaRectAction
*>(pCurrAct
)->GetRect() );
1991 if( rRect
.IsEmpty() )
1994 const OutDevState
& rState( rStates
.getState() );
1995 const ::basegfx::B2DPoint
aTopLeftPixel(
1996 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ) );
1997 const ::basegfx::B2DPoint
aBottomRightPixel(
1998 rState
.mapModeTransform
* ::vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
1999 // #121100# OutputDevice::DrawRect() fills
2000 // rectangles Apple-like, i.e. with one
2001 // additional pixel to the right and bottom.
2002 ::basegfx::B2DPoint(1,1) );
2004 createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
2005 ::basegfx::B2DRange( aTopLeftPixel
,
2006 aBottomRightPixel
)),
2011 case META_ROUNDRECT_ACTION
:
2013 const Rectangle
& rRect(
2014 static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetRect());
2016 if( rRect
.IsEmpty() )
2019 ::basegfx::B2DPolygon
aPoly(
2020 ::basegfx::tools::createPolygonFromRect(
2021 ::basegfx::B2DRange(
2022 ::vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
2023 ::vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
2024 ::basegfx::B2DPoint(1,1) ),
2025 ( (double) static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetHorzRound() ) / rRect
.GetWidth(),
2026 ( (double) static_cast<MetaRoundRectAction
*>(pCurrAct
)->GetVertRound() ) / rRect
.GetHeight() ) );
2027 aPoly
.transform( rStates
.getState().mapModeTransform
);
2029 createFillAndStroke( aPoly
,
2034 case META_ELLIPSE_ACTION
:
2036 const Rectangle
& rRect(
2037 static_cast<MetaEllipseAction
*>(pCurrAct
)->GetRect() );
2039 if( rRect
.IsEmpty() )
2042 const ::basegfx::B2DRange
aRange(
2043 ::vcl::unotools::b2DPointFromPoint( rRect
.TopLeft() ),
2044 ::vcl::unotools::b2DPointFromPoint( rRect
.BottomRight() ) +
2045 ::basegfx::B2DPoint(1,1) );
2047 ::basegfx::B2DPolygon
aPoly(
2048 ::basegfx::tools::createPolygonFromEllipse(
2051 aRange
.getHeight() ));
2052 aPoly
.transform( rStates
.getState().mapModeTransform
);
2054 createFillAndStroke( aPoly
,
2059 case META_ARC_ACTION
:
2061 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2062 const Polygon
aToolsPoly( static_cast<MetaArcAction
*>(pCurrAct
)->GetRect(),
2063 static_cast<MetaArcAction
*>(pCurrAct
)->GetStartPoint(),
2064 static_cast<MetaArcAction
*>(pCurrAct
)->GetEndPoint(), POLY_ARC
);
2065 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2066 aPoly
.transform( rStates
.getState().mapModeTransform
);
2068 createFillAndStroke( aPoly
,
2073 case META_PIE_ACTION
:
2075 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2076 const Polygon
aToolsPoly( static_cast<MetaPieAction
*>(pCurrAct
)->GetRect(),
2077 static_cast<MetaPieAction
*>(pCurrAct
)->GetStartPoint(),
2078 static_cast<MetaPieAction
*>(pCurrAct
)->GetEndPoint(), POLY_PIE
);
2079 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2080 aPoly
.transform( rStates
.getState().mapModeTransform
);
2082 createFillAndStroke( aPoly
,
2087 case META_CHORD_ACTION
:
2089 // TODO(F1): Missing basegfx functionality. Mind empty rects!
2090 const Polygon
aToolsPoly( static_cast<MetaChordAction
*>(pCurrAct
)->GetRect(),
2091 static_cast<MetaChordAction
*>(pCurrAct
)->GetStartPoint(),
2092 static_cast<MetaChordAction
*>(pCurrAct
)->GetEndPoint(), POLY_CHORD
);
2093 ::basegfx::B2DPolygon
aPoly( aToolsPoly
.getB2DPolygon() );
2094 aPoly
.transform( rStates
.getState().mapModeTransform
);
2096 createFillAndStroke( aPoly
,
2101 case META_POLYLINE_ACTION
:
2103 const OutDevState
& rState( rStates
.getState() );
2104 if( rState
.lineColor
.getLength() ||
2105 rState
.fillColor
.getLength() )
2107 MetaPolyLineAction
* pPolyLineAct
= static_cast<MetaPolyLineAction
*>(pCurrAct
);
2109 const LineInfo
& rLineInfo( pPolyLineAct
->GetLineInfo() );
2110 ::basegfx::B2DPolygon
aPoly( pPolyLineAct
->GetPolygon().getB2DPolygon() );
2111 aPoly
.transform( rState
.mapModeTransform
);
2113 ActionSharedPtr pLineAction
;
2115 if( rLineInfo
.IsDefault() )
2117 // plain hair line polygon
2119 internal::PolyPolyActionFactory::createLinePolyPolyAction(
2120 ::basegfx::B2DPolyPolygon(aPoly
),
2126 maActions
.push_back(
2129 io_rCurrActionIndex
) );
2131 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2134 else if( LINE_NONE
!= rLineInfo
.GetStyle() )
2136 // 'thick' line polygon
2137 rendering::StrokeAttributes aStrokeAttributes
;
2139 setupStrokeAttributes( aStrokeAttributes
,
2144 internal::PolyPolyActionFactory::createPolyPolyAction(
2145 ::basegfx::B2DPolyPolygon(aPoly
),
2148 aStrokeAttributes
) ;
2152 maActions
.push_back(
2155 io_rCurrActionIndex
) );
2157 io_rCurrActionIndex
+= pLineAction
->getActionCount()-1;
2160 // else: line style is default
2161 // (i.e. invisible), don't generate action
2166 case META_POLYGON_ACTION
:
2168 ::basegfx::B2DPolygon
aPoly( static_cast<MetaPolygonAction
*>(pCurrAct
)->GetPolygon().getB2DPolygon() );
2169 aPoly
.transform( rStates
.getState().mapModeTransform
);
2170 createFillAndStroke( aPoly
,
2175 case META_POLYPOLYGON_ACTION
:
2177 ::basegfx::B2DPolyPolygon
aPoly( static_cast<MetaPolyPolygonAction
*>(pCurrAct
)->GetPolyPolygon().getB2DPolyPolygon() );
2178 aPoly
.transform( rStates
.getState().mapModeTransform
);
2179 createFillAndStroke( aPoly
,
2184 case META_BMP_ACTION
:
2186 MetaBmpAction
* pAct
= static_cast<MetaBmpAction
*>(pCurrAct
);
2188 ActionSharedPtr
pBmpAction(
2189 internal::BitmapActionFactory::createBitmapAction(
2191 rStates
.getState().mapModeTransform
*
2192 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2194 rStates
.getState() ) );
2198 maActions
.push_back(
2201 io_rCurrActionIndex
) );
2203 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2208 case META_BMPSCALE_ACTION
:
2210 MetaBmpScaleAction
* pAct
= static_cast<MetaBmpScaleAction
*>(pCurrAct
);
2212 ActionSharedPtr
pBmpAction(
2213 internal::BitmapActionFactory::createBitmapAction(
2215 rStates
.getState().mapModeTransform
*
2216 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2217 rStates
.getState().mapModeTransform
*
2218 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2220 rStates
.getState() ) );
2224 maActions
.push_back(
2227 io_rCurrActionIndex
) );
2229 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2234 case META_BMPSCALEPART_ACTION
:
2236 MetaBmpScalePartAction
* pAct
= static_cast<MetaBmpScalePartAction
*>(pCurrAct
);
2238 // crop bitmap to given source rectangle (no
2239 // need to copy and convert the whole bitmap)
2240 Bitmap
aBmp( pAct
->GetBitmap() );
2241 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2242 pAct
->GetSrcSize() );
2243 aBmp
.Crop( aCropRect
);
2245 ActionSharedPtr
pBmpAction(
2246 internal::BitmapActionFactory::createBitmapAction(
2248 rStates
.getState().mapModeTransform
*
2249 ::vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2250 rStates
.getState().mapModeTransform
*
2251 ::vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2253 rStates
.getState() ) );
2257 maActions
.push_back(
2260 io_rCurrActionIndex
) );
2262 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2267 case META_BMPEX_ACTION
:
2269 MetaBmpExAction
* pAct
= static_cast<MetaBmpExAction
*>(pCurrAct
);
2271 ActionSharedPtr
pBmpAction(
2272 internal::BitmapActionFactory::createBitmapAction(
2273 pAct
->GetBitmapEx(),
2274 rStates
.getState().mapModeTransform
*
2275 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2277 rStates
.getState() ) );
2281 maActions
.push_back(
2284 io_rCurrActionIndex
) );
2286 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2291 case META_BMPEXSCALE_ACTION
:
2293 MetaBmpExScaleAction
* pAct
= static_cast<MetaBmpExScaleAction
*>(pCurrAct
);
2295 ActionSharedPtr
pBmpAction(
2296 internal::BitmapActionFactory::createBitmapAction(
2297 pAct
->GetBitmapEx(),
2298 rStates
.getState().mapModeTransform
*
2299 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2300 rStates
.getState().mapModeTransform
*
2301 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2303 rStates
.getState() ) );
2307 maActions
.push_back(
2310 io_rCurrActionIndex
) );
2312 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2317 case META_BMPEXSCALEPART_ACTION
:
2319 MetaBmpExScalePartAction
* pAct
= static_cast<MetaBmpExScalePartAction
*>(pCurrAct
);
2321 // crop bitmap to given source rectangle (no
2322 // need to copy and convert the whole bitmap)
2323 BitmapEx
aBmp( pAct
->GetBitmapEx() );
2324 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2325 pAct
->GetSrcSize() );
2326 aBmp
.Crop( aCropRect
);
2328 ActionSharedPtr
pBmpAction(
2329 internal::BitmapActionFactory::createBitmapAction(
2331 rStates
.getState().mapModeTransform
*
2332 ::vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2333 rStates
.getState().mapModeTransform
*
2334 ::vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2336 rStates
.getState() ) );
2340 maActions
.push_back(
2343 io_rCurrActionIndex
) );
2345 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2350 case META_MASK_ACTION
:
2352 MetaMaskAction
* pAct
= static_cast<MetaMaskAction
*>(pCurrAct
);
2354 // create masked BitmapEx right here, as the
2355 // canvas does not provide equivalent
2357 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2358 pAct
->GetColor() ));
2360 ActionSharedPtr
pBmpAction(
2361 internal::BitmapActionFactory::createBitmapAction(
2363 rStates
.getState().mapModeTransform
*
2364 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2366 rStates
.getState() ) );
2370 maActions
.push_back(
2373 io_rCurrActionIndex
) );
2375 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2380 case META_MASKSCALE_ACTION
:
2382 MetaMaskScaleAction
* pAct
= static_cast<MetaMaskScaleAction
*>(pCurrAct
);
2384 // create masked BitmapEx right here, as the
2385 // canvas does not provide equivalent
2387 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2388 pAct
->GetColor() ));
2390 ActionSharedPtr
pBmpAction(
2391 internal::BitmapActionFactory::createBitmapAction(
2393 rStates
.getState().mapModeTransform
*
2394 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2395 rStates
.getState().mapModeTransform
*
2396 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2398 rStates
.getState() ) );
2402 maActions
.push_back(
2405 io_rCurrActionIndex
) );
2407 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2412 case META_MASKSCALEPART_ACTION
:
2414 MetaMaskScalePartAction
* pAct
= static_cast<MetaMaskScalePartAction
*>(pCurrAct
);
2416 // create masked BitmapEx right here, as the
2417 // canvas does not provide equivalent
2419 BitmapEx
aBmp( createMaskBmpEx( pAct
->GetBitmap(),
2420 pAct
->GetColor() ));
2422 // crop bitmap to given source rectangle (no
2423 // need to copy and convert the whole bitmap)
2424 const Rectangle
aCropRect( pAct
->GetSrcPoint(),
2425 pAct
->GetSrcSize() );
2426 aBmp
.Crop( aCropRect
);
2428 ActionSharedPtr
pBmpAction(
2429 internal::BitmapActionFactory::createBitmapAction(
2431 rStates
.getState().mapModeTransform
*
2432 ::vcl::unotools::b2DPointFromPoint( pAct
->GetDestPoint() ),
2433 rStates
.getState().mapModeTransform
*
2434 ::vcl::unotools::b2DSizeFromSize( pAct
->GetDestSize() ),
2436 rStates
.getState() ) );
2440 maActions
.push_back(
2443 io_rCurrActionIndex
) );
2445 io_rCurrActionIndex
+= pBmpAction
->getActionCount()-1;
2450 case META_GRADIENTEX_ACTION
:
2451 // TODO(F1): use native Canvas gradients here
2452 // action is ignored here, because redundant to META_GRADIENT_ACTION
2455 case META_WALLPAPER_ACTION
:
2459 case META_TRANSPARENT_ACTION
:
2461 const OutDevState
& rState( rStates
.getState() );
2462 if( rState
.lineColor
.getLength() ||
2463 rState
.fillColor
.getLength() )
2465 MetaTransparentAction
* pAct
= static_cast<MetaTransparentAction
*>(pCurrAct
);
2466 ::basegfx::B2DPolyPolygon
aPoly( pAct
->GetPolyPolygon().getB2DPolyPolygon() );
2467 aPoly
.transform( rState
.mapModeTransform
);
2469 ActionSharedPtr
pPolyAction(
2470 internal::PolyPolyActionFactory::createPolyPolyAction(
2474 pAct
->GetTransparence() ) );
2478 maActions
.push_back(
2481 io_rCurrActionIndex
) );
2483 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2489 case META_FLOATTRANSPARENT_ACTION
:
2491 MetaFloatTransparentAction
* pAct
= static_cast<MetaFloatTransparentAction
*>(pCurrAct
);
2493 internal::MtfAutoPtr
pMtf(
2494 new ::GDIMetaFile( pAct
->GetGDIMetaFile() ) );
2496 // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2497 internal::GradientAutoPtr
pGradient(
2498 new Gradient( pAct
->GetGradient() ) );
2500 DBG_TESTSOLARMUTEX();
2502 ActionSharedPtr
pFloatTransAction(
2503 internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2507 rStates
.getState().mapModeTransform
*
2508 ::vcl::unotools::b2DPointFromPoint( pAct
->GetPoint() ),
2509 rStates
.getState().mapModeTransform
*
2510 ::vcl::unotools::b2DSizeFromSize( pAct
->GetSize() ),
2512 rStates
.getState() ) );
2514 if( pFloatTransAction
)
2516 maActions
.push_back(
2519 io_rCurrActionIndex
) );
2521 io_rCurrActionIndex
+= pFloatTransAction
->getActionCount()-1;
2526 case META_TEXT_ACTION
:
2528 MetaTextAction
* pAct
= static_cast<MetaTextAction
*>(pCurrAct
);
2529 XubString sText
= XubString( pAct
->GetText() );
2531 if( rVDev
.GetDigitLanguage())
2532 convertToLocalizedNumerals ( sText
,rVDev
.GetDigitLanguage() );
2538 pAct
->GetLen() == (sal_uInt16
)STRING_LEN
? pAct
->GetText().getLength() - pAct
->GetIndex() : pAct
->GetLen(),
2541 bSubsettableActions
);
2545 case META_TEXTARRAY_ACTION
:
2547 MetaTextArrayAction
* pAct
= static_cast<MetaTextArrayAction
*>(pCurrAct
);
2548 XubString sText
= XubString( pAct
->GetText() );
2550 if( rVDev
.GetDigitLanguage())
2551 convertToLocalizedNumerals ( sText
,rVDev
.GetDigitLanguage() );
2557 pAct
->GetLen() == (sal_uInt16
)STRING_LEN
? pAct
->GetText().getLength() - pAct
->GetIndex() : pAct
->GetLen(),
2560 bSubsettableActions
);
2564 case META_TEXTLINE_ACTION
:
2566 MetaTextLineAction
* pAct
= static_cast<MetaTextLineAction
*>(pCurrAct
);
2568 const OutDevState
& rState( rStates
.getState() );
2569 const ::Size
aBaselineOffset( tools::getBaselineOffset( rState
,
2571 const ::basegfx::B2DSize
aSize( rState
.mapModeTransform
*
2572 ::basegfx::B2DSize(pAct
->GetWidth(),
2575 ActionSharedPtr
pPolyAction(
2576 PolyPolyActionFactory::createPolyPolyAction(
2577 tools::createTextLinesPolyPolygon(
2578 rState
.mapModeTransform
*
2579 ::basegfx::B2DPoint(
2580 ::vcl::unotools::b2DPointFromPoint(pAct
->GetStartPoint()) +
2581 ::vcl::unotools::b2DSizeFromSize(aBaselineOffset
)),
2583 tools::createTextLineInfo( rVDev
,
2588 if( pPolyAction
.get() )
2590 maActions
.push_back(
2593 io_rCurrActionIndex
) );
2595 io_rCurrActionIndex
+= pPolyAction
->getActionCount()-1;
2600 case META_TEXTRECT_ACTION
:
2602 MetaTextRectAction
* pAct
= static_cast<MetaTextRectAction
*>(pCurrAct
);
2604 rStates
.pushState(PUSH_ALL
);
2606 // use the VDev to break up the text rect
2607 // action into readily formatted lines
2608 GDIMetaFile aTmpMtf
;
2609 rVDev
.AddTextRectActions( pAct
->GetRect(),
2614 createActions( aTmpMtf
,
2616 bSubsettableActions
);
2623 case META_STRETCHTEXT_ACTION
:
2625 MetaStretchTextAction
* pAct
= static_cast<MetaStretchTextAction
*>(pCurrAct
);
2626 XubString sText
= XubString( pAct
->GetText() );
2628 if( rVDev
.GetDigitLanguage())
2629 convertToLocalizedNumerals ( sText
,rVDev
.GetDigitLanguage() );
2631 const sal_uInt16
nLen( pAct
->GetLen() == (sal_uInt16
)STRING_LEN
?
2632 pAct
->GetText().getLength() - pAct
->GetIndex() : pAct
->GetLen() );
2634 // #i70897# Nothing to do, actually...
2638 // have to fit the text into the given
2639 // width. This is achieved by internally
2640 // generating a DX array, and uniformly
2641 // distributing the excess/insufficient width
2642 // to every logical character.
2643 ::boost::scoped_array
< sal_Int32
> pDXArray( new sal_Int32
[nLen
] );
2645 rVDev
.GetTextArray( pAct
->GetText(), pDXArray
.get(),
2646 pAct
->GetIndex(), pAct
->GetLen() );
2648 const sal_Int32
nWidthDifference( pAct
->GetWidth() - pDXArray
[ nLen
-1 ] );
2650 // Last entry of pDXArray contains total width of the text
2651 sal_Int32
* p
=pDXArray
.get();
2652 for( sal_uInt16 i
=1; i
<=nLen
; ++i
)
2654 // calc ratio for every array entry, to
2655 // distribute rounding errors 'evenly'
2656 // across the characters. Note that each
2657 // entry represents the 'end' position of
2658 // the corresponding character, thus, we
2659 // let i run from 1 to nLen.
2660 *p
++ += (sal_Int32
)i
*nWidthDifference
/nLen
;
2667 pAct
->GetLen() == (sal_uInt16
)STRING_LEN
? pAct
->GetText().getLength() - pAct
->GetIndex() : pAct
->GetLen(),
2670 bSubsettableActions
);
2675 SAL_WARN( "cppcanvas.emf", "Unknown meta action type encountered" );
2679 // increment action index (each mtf action counts _at
2680 // least_ one. Some count for more, therefore,
2681 // io_rCurrActionIndex is sometimes incremented by
2682 // pAct->getActionCount()-1 above, the -1 being the
2683 // correction for the unconditional increment here).
2684 ++io_rCurrActionIndex
;
2693 class ActionRenderer
2696 ActionRenderer( const ::basegfx::B2DHomMatrix
& rTransformation
) :
2697 maTransformation( rTransformation
),
2707 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2709 // ANDing the result. We want to fail if at least
2710 // one action failed.
2711 mbRet
&= rAction
.mpAction
->render( maTransformation
);
2714 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2715 const Action::Subset
& rSubset
)
2717 // ANDing the result. We want to fail if at least
2718 // one action failed.
2719 mbRet
&= rAction
.mpAction
->renderSubset( maTransformation
,
2724 ::basegfx::B2DHomMatrix maTransformation
;
2731 AreaQuery( const ::basegfx::B2DHomMatrix
& rTransformation
) :
2732 maTransformation( rTransformation
),
2739 return true; // nothing can fail here
2742 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
)
2744 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
) );
2747 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rAction
,
2748 const Action::Subset
& rSubset
)
2750 maBounds
.expand( rAction
.mpAction
->getBounds( maTransformation
,
2754 ::basegfx::B2DRange
getBounds() const
2760 ::basegfx::B2DHomMatrix maTransformation
;
2761 ::basegfx::B2DRange maBounds
;
2764 // Doing that via inline class. Compilers tend to not inline free
2766 struct UpperBoundActionIndexComparator
2768 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction
& rLHS
,
2769 const ::cppcanvas::internal::ImplRenderer::MtfAction
& rRHS
)
2771 const sal_Int32
nLHSCount( rLHS
.mpAction
?
2772 rLHS
.mpAction
->getActionCount() : 0 );
2773 const sal_Int32
nRHSCount( rRHS
.mpAction
?
2774 rRHS
.mpAction
->getActionCount() : 0 );
2776 // compare end of action range, to have an action selected
2777 // by lower_bound even if the requested index points in
2778 // the middle of the action's range
2779 return rLHS
.mnOrigIndex
+ nLHSCount
< rRHS
.mnOrigIndex
+ nRHSCount
;
2783 /** Algorithm to apply given functor to a subset range
2787 Functor to call for each element of the subset
2788 range. Must provide the following method signatures:
2789 bool result() (returning false if operation failed)
2792 template< typename Functor
> bool
2793 forSubsetRange( Functor
& rFunctor
,
2794 ImplRenderer::ActionVector::const_iterator aRangeBegin
,
2795 ImplRenderer::ActionVector::const_iterator aRangeEnd
,
2796 sal_Int32 nStartIndex
,
2797 sal_Int32 nEndIndex
,
2798 const ImplRenderer::ActionVector::const_iterator
& rEnd
)
2800 if( aRangeBegin
== aRangeEnd
)
2802 // only a single action. Setup subset, and call functor
2803 Action::Subset aSubset
;
2804 aSubset
.mnSubsetBegin
= ::std::max( sal_Int32( 0 ),
2805 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2806 aSubset
.mnSubsetEnd
= ::std::min( aRangeBegin
->mpAction
->getActionCount(),
2807 nEndIndex
- aRangeBegin
->mnOrigIndex
);
2809 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2810 "ImplRenderer::forSubsetRange(): Invalid indices" );
2812 rFunctor( *aRangeBegin
, aSubset
);
2816 // more than one action.
2818 // render partial first, full intermediate, and
2819 // partial last action
2820 Action::Subset aSubset
;
2821 aSubset
.mnSubsetBegin
= ::std::max( sal_Int32( 0 ),
2822 nStartIndex
- aRangeBegin
->mnOrigIndex
);
2823 aSubset
.mnSubsetEnd
= aRangeBegin
->mpAction
->getActionCount();
2825 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2826 "ImplRenderer::forSubsetRange(): Invalid indices" );
2828 rFunctor( *aRangeBegin
, aSubset
);
2830 // first action rendered, skip to next
2833 // render full middle actions
2834 while( aRangeBegin
!= aRangeEnd
)
2835 rFunctor( *aRangeBegin
++ );
2837 if( aRangeEnd
== rEnd
||
2838 aRangeEnd
->mnOrigIndex
> nEndIndex
)
2840 // aRangeEnd denotes end of action vector,
2844 // nEndIndex references something _after_
2845 // aRangeBegin, but _before_ aRangeEnd
2847 // either way: no partial action left
2848 return rFunctor
.result();
2851 aSubset
.mnSubsetBegin
= 0;
2852 aSubset
.mnSubsetEnd
= nEndIndex
- aRangeEnd
->mnOrigIndex
;
2854 ENSURE_OR_RETURN_FALSE( aSubset
.mnSubsetBegin
>= 0 && aSubset
.mnSubsetEnd
>= 0,
2855 "ImplRenderer::forSubsetRange(): Invalid indices" );
2857 rFunctor( *aRangeEnd
, aSubset
);
2860 return rFunctor
.result();
2864 bool ImplRenderer::getSubsetIndices( sal_Int32
& io_rStartIndex
,
2865 sal_Int32
& io_rEndIndex
,
2866 ActionVector::const_iterator
& o_rRangeBegin
,
2867 ActionVector::const_iterator
& o_rRangeEnd
) const
2869 ENSURE_OR_RETURN_FALSE( io_rStartIndex
<=io_rEndIndex
,
2870 "ImplRenderer::getSubsetIndices(): invalid action range" );
2872 ENSURE_OR_RETURN_FALSE( !maActions
.empty(),
2873 "ImplRenderer::getSubsetIndices(): no actions to render" );
2875 const sal_Int32
nMinActionIndex( maActions
.front().mnOrigIndex
);
2876 const sal_Int32
nMaxActionIndex( maActions
.back().mnOrigIndex
+
2877 maActions
.back().mpAction
->getActionCount() );
2879 // clip given range to permissible values (there might be
2880 // ranges before and behind the valid indices)
2881 io_rStartIndex
= ::std::max( nMinActionIndex
,
2883 io_rEndIndex
= ::std::min( nMaxActionIndex
,
2886 if( io_rStartIndex
== io_rEndIndex
||
2887 io_rStartIndex
> io_rEndIndex
)
2889 // empty range, don't render anything. The second
2890 // condition e.g. happens if the requested range lies
2891 // fully before or behind the valid action indices.
2896 const ActionVector::const_iterator
aBegin( maActions
.begin() );
2897 const ActionVector::const_iterator
aEnd( maActions
.end() );
2900 // find start and end action
2901 // =========================
2902 o_rRangeBegin
= ::std::lower_bound( aBegin
, aEnd
,
2903 MtfAction( ActionSharedPtr(), io_rStartIndex
),
2904 UpperBoundActionIndexComparator() );
2905 o_rRangeEnd
= ::std::lower_bound( aBegin
, aEnd
,
2906 MtfAction( ActionSharedPtr(), io_rEndIndex
),
2907 UpperBoundActionIndexComparator() );
2913 // ====================================================================
2915 ImplRenderer::ImplRenderer( const CanvasSharedPtr
& rCanvas
,
2916 const GDIMetaFile
& rMtf
,
2917 const Parameters
& rParams
) :
2918 CanvasGraphicHelper( rCanvas
),
2921 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2923 OSL_ENSURE( rCanvas
.get() != NULL
&& rCanvas
->getUNOCanvas().is(),
2924 "ImplRenderer::ImplRenderer(): Invalid canvas" );
2925 OSL_ENSURE( rCanvas
->getUNOCanvas()->getDevice().is(),
2926 "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2928 // make sure canvas and graphic device are valid; action
2929 // creation don't check that every time
2930 if( rCanvas
.get() == NULL
||
2931 !rCanvas
->getUNOCanvas().is() ||
2932 !rCanvas
->getUNOCanvas()->getDevice().is() )
2934 // leave actions empty
2938 VectorOfOutDevStates aStateStack
;
2940 VirtualDevice aVDev
;
2941 aVDev
.EnableOutput( sal_False
);
2943 // Setup VDev for state tracking and mapping
2944 // =========================================
2946 aVDev
.SetMapMode( rMtf
.GetPrefMapMode() );
2948 const Size
aMtfSize( rMtf
.GetPrefSize() );
2949 const Size
aMtfSizePixPre( aVDev
.LogicToPixel( aMtfSize
,
2950 rMtf
.GetPrefMapMode() ) );
2952 // #i44110# correct null-sized output - there are shapes
2953 // which have zero size in at least one dimension
2954 const Size
aMtfSizePix( ::std::max( aMtfSizePixPre
.Width(), 1L ),
2955 ::std::max( aMtfSizePixPre
.Height(), 1L ) );
2957 sal_Int32
nCurrActions(0);
2958 ActionFactoryParameters
aParms(aStateStack
,
2965 aStateStack
.clearStateStack();
2967 // Setup local state, such that the metafile renders
2968 // itself into a one-by-one square at the origin for
2969 // identity view and render transformations
2970 aStateStack
.getState().transform
.scale( 1.0 / aMtfSizePix
.Width(),
2971 1.0 / aMtfSizePix
.Height() );
2973 tools::calcLogic2PixelAffineTransform( aStateStack
.getState().mapModeTransform
,
2976 ColorSharedPtr
pColor( getCanvas()->createColor() );
2979 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2980 // setup default text color to black
2982 rState
.textFillColor
=
2983 rState
.textLineColor
= pColor
->getDeviceColor( 0x000000FF );
2986 // apply overrides from the Parameters struct
2987 if( rParams
.maFillColor
.is_initialized() )
2989 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2990 rState
.isFillColorSet
= true;
2991 rState
.fillColor
= pColor
->getDeviceColor( *rParams
.maFillColor
);
2993 if( rParams
.maLineColor
.is_initialized() )
2995 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
2996 rState
.isLineColorSet
= true;
2997 rState
.lineColor
= pColor
->getDeviceColor( *rParams
.maLineColor
);
2999 if( rParams
.maTextColor
.is_initialized() )
3001 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
3002 rState
.isTextFillColorSet
= true;
3003 rState
.isTextLineColorSet
= true;
3005 rState
.textFillColor
=
3006 rState
.textLineColor
= pColor
->getDeviceColor( *rParams
.maTextColor
);
3008 if( rParams
.maFontName
.is_initialized() ||
3009 rParams
.maFontWeight
.is_initialized() ||
3010 rParams
.maFontLetterForm
.is_initialized() ||
3011 rParams
.maFontUnderline
.is_initialized() ||
3012 rParams
.maFontProportion
.is_initialized() )
3014 ::cppcanvas::internal::OutDevState
& rState
= aStateStack
.getState();
3016 rState
.xFont
= createFont( rState
.fontRotation
,
3017 ::Font(), // default font
3022 memset (aObjects
, 0, sizeof (aObjects
));
3023 mbMultipart
= false;
3025 createActions( const_cast<GDIMetaFile
&>(rMtf
), // HACK(Q2):
3034 true // TODO(P1): make subsettability configurable
3038 ImplRenderer::~ImplRenderer()
3040 // don't leak EMFPObjects
3041 for(unsigned int i
=0; i
<SAL_N_ELEMENTS(aObjects
); ++i
)
3045 bool ImplRenderer::drawSubset( sal_Int32 nStartIndex
,
3046 sal_Int32 nEndIndex
) const
3048 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::drawSubset()" );
3050 ActionVector::const_iterator aRangeBegin
;
3051 ActionVector::const_iterator aRangeEnd
;
3055 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
3056 aRangeBegin
, aRangeEnd
) )
3057 return true; // nothing to render (but _that_ was successful)
3059 // now, aRangeBegin references the action in which the
3060 // subset rendering must start, and aRangeEnd references
3061 // the action in which the subset rendering must end (it
3062 // might also end right at the start of the referenced
3063 // action, such that zero of that action needs to be
3067 // render subset of actions
3068 // ========================
3070 ::basegfx::B2DHomMatrix aMatrix
;
3071 ::canvas::tools::getRenderStateTransform( aMatrix
,
3074 ActionRenderer
aRenderer( aMatrix
);
3076 return forSubsetRange( aRenderer
,
3083 catch( uno::Exception
& )
3085 SAL_WARN("cppcanvas.emf", "" << OUStringToOString(
3086 comphelper::anyToString( cppu::getCaughtException() ),
3087 RTL_TEXTENCODING_UTF8
).getStr() );
3089 // convert error to return value
3094 ::basegfx::B2DRange
ImplRenderer::getSubsetArea( sal_Int32 nStartIndex
,
3095 sal_Int32 nEndIndex
) const
3097 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3099 ActionVector::const_iterator aRangeBegin
;
3100 ActionVector::const_iterator aRangeEnd
;
3102 if( !getSubsetIndices( nStartIndex
, nEndIndex
,
3103 aRangeBegin
, aRangeEnd
) )
3104 return ::basegfx::B2DRange(); // nothing to render -> empty range
3106 // now, aRangeBegin references the action in which the
3107 // subset querying must start, and aRangeEnd references
3108 // the action in which the subset querying must end (it
3109 // might also end right at the start of the referenced
3110 // action, such that zero of that action needs to be
3114 // query bounds for subset of actions
3115 // ==================================
3117 ::basegfx::B2DHomMatrix aMatrix
;
3118 ::canvas::tools::getRenderStateTransform( aMatrix
,
3121 AreaQuery
aQuery( aMatrix
);
3122 forSubsetRange( aQuery
,
3129 return aQuery
.getBounds();
3132 bool ImplRenderer::draw() const
3134 SAL_INFO( "cppcanvas.emf", "::cppcanvas::internal::ImplRenderer::draw()" );
3136 ::basegfx::B2DHomMatrix aMatrix
;
3137 ::canvas::tools::getRenderStateTransform( aMatrix
,
3142 return ::std::for_each( maActions
.begin(), maActions
.end(), ActionRenderer( aMatrix
) ).result();
3144 catch( uno::Exception
& )
3146 SAL_WARN( "cppcanvas.emf", "" << OUStringToOString(
3147 comphelper::anyToString( cppu::getCaughtException() ),
3148 RTL_TEXTENCODING_UTF8
).getStr() );
3156 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */