1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_cppcanvas.hxx"
30 
31 #include <canvas/debug.hxx>
32 #include <tools/diagnose_ex.h>
33 #include <canvas/verbosetrace.hxx>
34 #include <osl/mutex.hxx>
35 #include <vos/mutex.hxx>
36 #include <vcl/svapp.hxx>
37 #include <rtl/logfile.hxx>
38 #include <comphelper/sequence.hxx>
39 #include <comphelper/anytostring.hxx>
40 #include <cppuhelper/exc_hlp.hxx>
41 #include <cppcanvas/canvas.hxx>
42 #include <com/sun/star/rendering/XGraphicDevice.hpp>
43 #include <com/sun/star/rendering/TexturingMode.hpp>
44 #include <com/sun/star/uno/Sequence.hxx>
45 #include <com/sun/star/geometry/RealPoint2D.hpp>
46 #include <com/sun/star/rendering/PanoseProportion.hpp>
47 #include <com/sun/star/rendering/ViewState.hpp>
48 #include <com/sun/star/rendering/RenderState.hpp>
49 #include <com/sun/star/rendering/XCanvasFont.hpp>
50 #include <com/sun/star/rendering/XPolyPolygon2D.hpp>
51 #include <com/sun/star/rendering/XCanvas.hpp>
52 #include <com/sun/star/rendering/PathCapType.hpp>
53 #include <com/sun/star/rendering/PathJoinType.hpp>
54 #include <basegfx/tools/canvastools.hxx>
55 #include <basegfx/tools/gradienttools.hxx>
56 #include <basegfx/numeric/ftools.hxx>
57 #include <basegfx/polygon/b2dpolypolygontools.hxx>
58 #include <basegfx/polygon/b2dpolygontools.hxx>
59 #include <basegfx/polygon/b2dpolygon.hxx>
60 #include <basegfx/polygon/b2dpolypolygon.hxx>
61 #include <basegfx/matrix/b2dhommatrix.hxx>
62 #include <basegfx/vector/b2dsize.hxx>
63 #include <basegfx/range/b2drectangle.hxx>
64 #include <basegfx/point/b2dpoint.hxx>
65 #include <basegfx/tuple/b2dtuple.hxx>
66 #include <basegfx/polygon/b2dpolygonclipper.hxx>
67 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
68 #include <canvas/canvastools.hxx>
69 #include <vcl/canvastools.hxx>
70 #include <vcl/salbtype.hxx>
71 #include <vcl/gdimtf.hxx>
72 #include <vcl/metaact.hxx>
73 #include <vcl/virdev.hxx>
74 #include <vcl/metric.hxx>
75 #include <vcl/graphictools.hxx>
76 #include <tools/poly.hxx>
77 #include <i18npool/mslangid.hxx>
78 #include <implrenderer.hxx>
79 #include <tools.hxx>
80 #include <outdevstate.hxx>
81 #include <action.hxx>
82 #include <bitmapaction.hxx>
83 #include <lineaction.hxx>
84 #include <pointaction.hxx>
85 #include <polypolyaction.hxx>
86 #include <rendergraphicaction.hxx>
87 #include <textaction.hxx>
88 #include <transparencygroupaction.hxx>
89 #include <vector>
90 #include <algorithm>
91 #include <iterator>
92 #include <boost/scoped_array.hpp>
93 #include "mtftools.hxx"
94 #include "outdevstate.hxx"
95 #include <basegfx/matrix/b2dhommatrixtools.hxx>
96 
97 
98 using namespace ::com::sun::star;
99 
100 
101 // free support functions
102 // ======================
103 namespace
104 {
105     template < class MetaActionType > void setStateColor( MetaActionType* 					pAct,
106                                                           bool&								rIsColorSet,
107                                                           uno::Sequence< double >& 			rColorSequence,
108                                                           const cppcanvas::CanvasSharedPtr&	rCanvas )
109     {
110         // set rIsColorSet and check for true at the same time
111         if( (rIsColorSet=pAct->IsSetting()) != false )
112         {
113             ::Color aColor( pAct->GetColor() );
114 
115             // force alpha part of color to
116             // opaque. transparent painting is done
117             // explicitely via META_TRANSPARENT_ACTION
118             aColor.SetTransparency(0);
119             //aColor.SetTransparency(128);
120 
121             rColorSequence = ::vcl::unotools::colorToDoubleSequence(
122                 aColor,
123                 rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
124         }
125     }
126 
127 
128     // state stack manipulators
129     // ------------------------
130     void clearStateStack( ::cppcanvas::internal::VectorOfOutDevStates& rStates )
131     {
132         rStates.clear();
133         const ::cppcanvas::internal::OutDevState aDefaultState;
134         rStates.push_back( aDefaultState );
135     }
136 
137     ::cppcanvas::internal::OutDevState& getState( ::cppcanvas::internal::VectorOfOutDevStates& rStates )
138     {
139         return rStates.back();
140     }
141 
142     const ::cppcanvas::internal::OutDevState& getState( const ::cppcanvas::internal::VectorOfOutDevStates& rStates )
143     {
144         return rStates.back();
145     }
146 
147     void pushState( ::cppcanvas::internal::VectorOfOutDevStates& rStates,
148                     sal_uInt16 nFlags											)
149     {
150         rStates.push_back( getState( rStates ) );
151         getState( rStates ).pushFlags = nFlags;
152     }
153 
154     void popState( ::cppcanvas::internal::VectorOfOutDevStates& rStates )
155     {
156         if( getState( rStates ).pushFlags != PUSH_ALL )
157         {
158             // a state is pushed which is incomplete, i.e. does not
159             // restore everything to the previous stack level when
160             // popped.
161             // That means, we take the old state, and restore every
162             // OutDevState member whose flag is set, from the new to the
163             // old state. Then the new state gets overwritten by the
164             // calculated state
165 
166             // preset to-be-calculated new state with old state
167             ::cppcanvas::internal::OutDevState aCalculatedNewState( getState( rStates ) );
168 
169             // selectively copy to-be-restored content over saved old
170             // state
171             rStates.pop_back();
172 
173             const ::cppcanvas::internal::OutDevState& rNewState( getState( rStates ) );
174 
175             if( (aCalculatedNewState.pushFlags & PUSH_LINECOLOR) )
176             {
177                 aCalculatedNewState.lineColor 	   = rNewState.lineColor;
178                 aCalculatedNewState.isLineColorSet = rNewState.isLineColorSet;
179             }
180 
181             if( (aCalculatedNewState.pushFlags & PUSH_FILLCOLOR) )
182             {
183                 aCalculatedNewState.fillColor 	   = rNewState.fillColor;
184                 aCalculatedNewState.isFillColorSet = rNewState.isFillColorSet;
185             }
186 
187             if( (aCalculatedNewState.pushFlags & PUSH_FONT) )
188             {
189                 aCalculatedNewState.xFont 					= rNewState.xFont;
190                 aCalculatedNewState.fontRotation 			= rNewState.fontRotation;
191                 aCalculatedNewState.textReliefStyle 		= rNewState.textReliefStyle;
192                 aCalculatedNewState.textOverlineStyle 		= rNewState.textOverlineStyle;
193                 aCalculatedNewState.textUnderlineStyle 		= rNewState.textUnderlineStyle;
194                 aCalculatedNewState.textStrikeoutStyle 		= rNewState.textStrikeoutStyle;
195                 aCalculatedNewState.textEmphasisMarkStyle 	= rNewState.textEmphasisMarkStyle;
196                 aCalculatedNewState.isTextEffectShadowSet 	= rNewState.isTextEffectShadowSet;
197                 aCalculatedNewState.isTextWordUnderlineSet 	= rNewState.isTextWordUnderlineSet;
198                 aCalculatedNewState.isTextOutlineModeSet 	= rNewState.isTextOutlineModeSet;
199             }
200 
201             if( (aCalculatedNewState.pushFlags & PUSH_TEXTCOLOR) )
202             {
203                 aCalculatedNewState.textColor = rNewState.textColor;
204             }
205 
206             if( (aCalculatedNewState.pushFlags & PUSH_MAPMODE) )
207             {
208                 aCalculatedNewState.mapModeTransform = rNewState.mapModeTransform;
209             }
210 
211             if( (aCalculatedNewState.pushFlags & PUSH_CLIPREGION) )
212             {
213                 aCalculatedNewState.clip 		= rNewState.clip;
214                 aCalculatedNewState.clipRect	= rNewState.clipRect;
215                 aCalculatedNewState.xClipPoly	= rNewState.xClipPoly;
216             }
217 
218             // TODO(F2): Raster ops NYI
219             // if( (aCalculatedNewState.pushFlags & PUSH_RASTEROP) )
220             // {
221             // }
222 
223             if( (aCalculatedNewState.pushFlags & PUSH_TEXTFILLCOLOR) )
224             {
225                 aCalculatedNewState.textFillColor 	   = rNewState.textFillColor;
226                 aCalculatedNewState.isTextFillColorSet = rNewState.isTextFillColorSet;
227             }
228 
229             if( (aCalculatedNewState.pushFlags & PUSH_TEXTALIGN) )
230             {
231                 aCalculatedNewState.textReferencePoint = rNewState.textReferencePoint;
232             }
233 
234             // TODO(F1): Refpoint handling NYI
235             // if( (aCalculatedNewState.pushFlags & PUSH_REFPOINT) )
236             // {
237             // }
238 
239             if( (aCalculatedNewState.pushFlags & PUSH_TEXTLINECOLOR) )
240             {
241                 aCalculatedNewState.textLineColor 	   = rNewState.textLineColor;
242                 aCalculatedNewState.isTextLineColorSet = rNewState.isTextLineColorSet;
243             }
244 
245             if( (aCalculatedNewState.pushFlags & PUSH_TEXTLAYOUTMODE) )
246             {
247                 aCalculatedNewState.textAlignment = rNewState.textAlignment;
248                 aCalculatedNewState.textDirection = rNewState.textDirection;
249             }
250 
251             // TODO(F2): Text language handling NYI
252             // if( (aCalculatedNewState.pushFlags & PUSH_TEXTLANGUAGE) )
253             // {
254             // }
255 
256             // always copy push mode
257             aCalculatedNewState.pushFlags = rNewState.pushFlags;
258 
259             // flush to stack
260             getState( rStates ) = aCalculatedNewState;
261         }
262         else
263         {
264             rStates.pop_back();
265         }
266     }
267 
268     void setupStrokeAttributes( rendering::StrokeAttributes&                          o_rStrokeAttributes,
269                                 const ::cppcanvas::internal::ActionFactoryParameters& rParms,
270                                 const LineInfo&                                       rLineInfo 				)
271     {
272         const ::basegfx::B2DSize aWidth( rLineInfo.GetWidth(), 0 );
273         o_rStrokeAttributes.StrokeWidth =
274             (getState( rParms.mrStates ).mapModeTransform * aWidth).getX();
275 
276         // setup reasonable defaults
277         o_rStrokeAttributes.MiterLimit 	 = 15.0; // 1.0 was no good default; GDI+'s limit is 10.0, our's is 15.0
278         o_rStrokeAttributes.StartCapType = rendering::PathCapType::BUTT;
279         o_rStrokeAttributes.EndCapType   = rendering::PathCapType::BUTT;
280 
281         switch(rLineInfo.GetLineJoin())
282         {
283             default: // B2DLINEJOIN_NONE, B2DLINEJOIN_MIDDLE
284                 o_rStrokeAttributes.JoinType = rendering::PathJoinType::NONE;
285                 break;
286             case basegfx::B2DLINEJOIN_BEVEL:
287                 o_rStrokeAttributes.JoinType = rendering::PathJoinType::BEVEL;
288                 break;
289             case basegfx::B2DLINEJOIN_MITER:
290                 o_rStrokeAttributes.JoinType = rendering::PathJoinType::MITER;
291                 break;
292             case basegfx::B2DLINEJOIN_ROUND:
293                 o_rStrokeAttributes.JoinType = rendering::PathJoinType::ROUND;
294                 break;
295         }
296 
297         if( LINE_DASH == rLineInfo.GetStyle() )
298         {
299             const ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) );
300 
301             // TODO(F1): Interpret OutDev::GetRefPoint() for the start of the dashing.
302 
303             // interpret dash info only if explicitely enabled as
304             // style
305             const ::basegfx::B2DSize aDistance( rLineInfo.GetDistance(), 0 );
306             const double nDistance( (rState.mapModeTransform * aDistance).getX() );
307 
308             const ::basegfx::B2DSize aDashLen( rLineInfo.GetDashLen(), 0 );
309             const double nDashLen( (rState.mapModeTransform * aDashLen).getX() );
310 
311             const ::basegfx::B2DSize aDotLen( rLineInfo.GetDotLen(), 0 );
312             const double nDotLen( (rState.mapModeTransform * aDotLen).getX() );
313 
314             const sal_Int32 nNumArryEntries( 2*rLineInfo.GetDashCount() +
315                                              2*rLineInfo.GetDotCount() );
316 
317             o_rStrokeAttributes.DashArray.realloc( nNumArryEntries );
318             double* pDashArray = o_rStrokeAttributes.DashArray.getArray();
319 
320 
321             // iteratively fill dash array, first with dashs, then
322             // with dots.
323             // ===================================================
324 
325             sal_Int32 nCurrEntry=0;
326 
327             for( sal_Int32 i=0; i<rLineInfo.GetDashCount(); ++i )
328             {
329                 pDashArray[nCurrEntry++] = nDashLen;
330                 pDashArray[nCurrEntry++] = nDistance;
331             }
332             for( sal_Int32 i=0; i<rLineInfo.GetDotCount(); ++i )
333             {
334                 pDashArray[nCurrEntry++] = nDotLen;
335                 pDashArray[nCurrEntry++] = nDistance;
336             }
337         }
338     }
339 
340 
341     /** Create masked BitmapEx, where the white areas of rBitmap are
342         transparent, and the other appear in rMaskColor.
343      */
344     BitmapEx createMaskBmpEx( const Bitmap&  rBitmap,
345                               const ::Color& rMaskColor )
346     {
347         const ::Color aWhite( COL_WHITE );
348         BitmapPalette aBiLevelPalette(2);
349         aBiLevelPalette[0] = aWhite;
350         aBiLevelPalette[1] = rMaskColor;
351 
352         Bitmap aMask( rBitmap.CreateMask( aWhite ));
353         Bitmap aSolid( rBitmap.GetSizePixel(),
354                        1,
355                        &aBiLevelPalette );
356         aSolid.Erase( rMaskColor );
357 
358         return BitmapEx( aSolid, aMask );
359     }
360 
361     /** Shameless rip from vcl/source/gdi/outdev3.cxx
362 
363         Should consolidate, into something like basetxt...
364      */
365     sal_Unicode getLocalizedChar( sal_Unicode nChar, LanguageType eLang )
366     {
367         // currently only conversion from ASCII digits is interesting
368         if( (nChar < '0') || ('9' < nChar) )
369             return nChar;
370 
371         sal_Unicode nOffset(0);
372         // eLang & LANGUAGE_MASK_PRIMARY catches language independent of region.
373         // CAVEAT! To some like Mongolian MS assigned the same primary language
374         // although the script type is different!
375         switch( eLang & LANGUAGE_MASK_PRIMARY )
376         {
377             default:
378                 break;
379 
380             case LANGUAGE_ARABIC_SAUDI_ARABIA  & LANGUAGE_MASK_PRIMARY:
381             case LANGUAGE_URDU          & LANGUAGE_MASK_PRIMARY:
382             case LANGUAGE_PUNJABI       & LANGUAGE_MASK_PRIMARY: //???
383                 nOffset = 0x0660 - '0';  // arabic/persian/urdu
384                 break;
385             case LANGUAGE_BENGALI       & LANGUAGE_MASK_PRIMARY:
386                 nOffset = 0x09E6 - '0';  // bengali
387                 break;
388             case LANGUAGE_BURMESE       & LANGUAGE_MASK_PRIMARY:
389                 nOffset = 0x1040 - '0';  // burmese
390                 break;
391             case LANGUAGE_HINDI         & LANGUAGE_MASK_PRIMARY:
392                 nOffset = 0x0966 - '0';  // devanagari
393                 break;
394             case LANGUAGE_GUJARATI      & LANGUAGE_MASK_PRIMARY:
395                 nOffset = 0x0AE6 - '0';  // gujarati
396                 break;
397             case LANGUAGE_KANNADA       & LANGUAGE_MASK_PRIMARY:
398                 nOffset = 0x0CE6 - '0';  // kannada
399                 break;
400             case LANGUAGE_KHMER         & LANGUAGE_MASK_PRIMARY:
401                 nOffset = 0x17E0 - '0';  // khmer
402                 break;
403             case LANGUAGE_LAO           & LANGUAGE_MASK_PRIMARY:
404                 nOffset = 0x0ED0 - '0';  // lao
405                 break;
406             case LANGUAGE_MALAYALAM     & LANGUAGE_MASK_PRIMARY:
407                 nOffset = 0x0D66 - '0';   // malayalam
408                 break;
409             case LANGUAGE_MONGOLIAN     & LANGUAGE_MASK_PRIMARY:
410                 if (eLang == LANGUAGE_MONGOLIAN_MONGOLIAN)
411                     nOffset = 0x1810 - '0';   // mongolian
412                 else
413                     nOffset = 0;              // mongolian cyrillic
414                 break;
415             case LANGUAGE_ORIYA         & LANGUAGE_MASK_PRIMARY:
416                 nOffset = 0x0B66 - '0';   // oriya
417                 break;
418             case LANGUAGE_TAMIL         & LANGUAGE_MASK_PRIMARY:
419                 nOffset = 0x0BE7 - '0';   // tamil
420                 break;
421             case LANGUAGE_TELUGU        & LANGUAGE_MASK_PRIMARY:
422                 nOffset = 0x0C66 - '0';   // telugu
423                 break;
424             case LANGUAGE_THAI          & LANGUAGE_MASK_PRIMARY:
425                 nOffset = 0x0E50 - '0';   // thai
426                 break;
427             case LANGUAGE_TIBETAN       & LANGUAGE_MASK_PRIMARY:
428                 nOffset = 0x0F20 - '0';   // tibetan
429                 break;
430         }
431 
432         nChar = sal::static_int_cast<sal_Unicode>(nChar + nOffset);
433         return nChar;
434     }
435 
436     void convertToLocalizedNumerals( XubString&   rStr,
437                                      LanguageType eTextLanguage )
438     {
439         const sal_Unicode* pBase = rStr.GetBuffer();
440         const sal_Unicode* pBegin = pBase + 0;
441         const xub_StrLen nEndIndex = rStr.Len();
442         const sal_Unicode* pEnd = pBase + nEndIndex;
443 
444         for( ; pBegin < pEnd; ++pBegin )
445         {
446             // TODO: are there non-digit localizations?
447             if( (*pBegin >= '0') && (*pBegin <= '9') )
448             {
449                 // translate characters to local preference
450                 sal_Unicode cChar = getLocalizedChar( *pBegin, eTextLanguage );
451                 if( cChar != *pBegin )
452                     rStr.SetChar( sal::static_int_cast<sal_uInt16>(pBegin - pBase), cChar );
453             }
454         }
455     }
456 }
457 
458 
459 namespace cppcanvas
460 {
461     namespace internal
462     {
463         bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolyPolygon& rPolyPoly,
464                                                 const ActionFactoryParameters&   rParms )
465         {
466             const OutDevState& rState( getState( rParms.mrStates ) );
467             if( (!rState.isLineColorSet &&
468                  !rState.isFillColorSet) ||
469                 (rState.lineColor.getLength() == 0 &&
470                  rState.fillColor.getLength() == 0) )
471             {
472                 return false;
473             }
474 
475             ActionSharedPtr pPolyAction(
476                 internal::PolyPolyActionFactory::createPolyPolyAction(
477                     rPolyPoly, rParms.mrCanvas, rState ) );
478 
479             if( pPolyAction )
480             {
481                 maActions.push_back(
482                     MtfAction(
483                         pPolyAction,
484                         rParms.mrCurrActionIndex ) );
485 
486                 rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
487             }
488 
489             return true;
490         }
491 
492         bool ImplRenderer::createFillAndStroke( const ::basegfx::B2DPolygon&   rPoly,
493                                                 const ActionFactoryParameters& rParms )
494         {
495             return createFillAndStroke( ::basegfx::B2DPolyPolygon( rPoly ),
496                                         rParms );
497         }
498 
499         void ImplRenderer::skipContent( GDIMetaFile& rMtf,
500                                         const char*  pCommentString,
501                                         sal_Int32& 	 io_rCurrActionIndex ) const
502         {
503             ENSURE_OR_THROW( pCommentString,
504                               "ImplRenderer::skipContent(): NULL string given" );
505 
506             MetaAction* pCurrAct;
507             while( (pCurrAct=rMtf.NextAction()) != NULL )
508             {
509                 // increment action index, we've skipped an action.
510                 ++io_rCurrActionIndex;
511 
512                 if( pCurrAct->GetType() == META_COMMENT_ACTION &&
513                     static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii(
514                         pCommentString ) == COMPARE_EQUAL )
515                 {
516                     // requested comment found, done
517                     return;
518                 }
519             }
520 
521             // EOF
522             return;
523         }
524 
525         bool ImplRenderer::isActionContained( GDIMetaFile& rMtf,
526                                               const char*  pCommentString,
527                                               sal_uInt16	   nType ) const
528         {
529             ENSURE_OR_THROW( pCommentString,
530                               "ImplRenderer::isActionContained(): NULL string given" );
531 
532             bool bRet( false );
533 
534             // at least _one_ call to GDIMetaFile::NextAction() is
535             // executed
536             sal_uIntPtr nPos( 1 );
537 
538             MetaAction* pCurrAct;
539             while( (pCurrAct=rMtf.NextAction()) != NULL )
540             {
541                 if( pCurrAct->GetType() == nType )
542                 {
543                     bRet = true; // action type found
544                     break;
545                 }
546 
547                 if( pCurrAct->GetType() == META_COMMENT_ACTION &&
548                     static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii(
549                         pCommentString ) == COMPARE_EQUAL )
550                 {
551                     // delimiting end comment found, done
552                     bRet = false; // not yet found
553                     break;
554                 }
555 
556                 ++nPos;
557             }
558 
559             // rewind metafile to previous position (this method must
560             // not change the current metaaction)
561             while( nPos-- )
562                 rMtf.WindPrev();
563 
564             if( !pCurrAct )
565             {
566                 // EOF, and not yet found
567                 bRet = false;
568             }
569 
570             return bRet;
571         }
572 
573         void ImplRenderer::createGradientAction( const ::PolyPolygon& 			rPoly,
574                                                  const ::Gradient&				rGradient,
575                                                  const ActionFactoryParameters& rParms,
576                                                  bool							bIsPolygonRectangle,
577                                                  bool 							bSubsettableActions )
578         {
579             DBG_TESTSOLARMUTEX();
580 
581             ::basegfx::B2DPolyPolygon aDevicePoly( rPoly.getB2DPolyPolygon() );
582             aDevicePoly.transform( getState( rParms.mrStates ).mapModeTransform );
583 
584             // decide, whether this gradient can be rendered natively
585             // by the canvas, or must be emulated via VCL gradient
586             // action extraction.
587             const sal_uInt16 nSteps( rGradient.GetSteps() );
588 
589             if( // step count is infinite, can use native canvas
590                 // gradients here
591                 nSteps == 0 ||
592                 // step count is sufficiently high, such that no
593                 // discernible difference should be visible.
594                 nSteps > 64 )
595             {
596                 uno::Reference< lang::XMultiServiceFactory> xFactory(
597                     rParms.mrCanvas->getUNOCanvas()->getDevice()->getParametricPolyPolygonFactory() );
598 
599                 if( xFactory.is() )
600                 {
601                     rendering::Texture aTexture;
602 
603                     aTexture.RepeatModeX = rendering::TexturingMode::CLAMP;
604                     aTexture.RepeatModeY = rendering::TexturingMode::CLAMP;
605                     aTexture.Alpha = 1.0;
606 
607 
608                     // setup start/end color values
609                     // ----------------------------
610 
611                     // scale color coefficients with gradient intensities
612                     const sal_uInt16 nStartIntensity( rGradient.GetStartIntensity() );
613                     ::Color aVCLStartColor( rGradient.GetStartColor() );
614                     aVCLStartColor.SetRed( (sal_uInt8)(aVCLStartColor.GetRed() * nStartIntensity / 100) );
615                     aVCLStartColor.SetGreen( (sal_uInt8)(aVCLStartColor.GetGreen() * nStartIntensity / 100) );
616                     aVCLStartColor.SetBlue( (sal_uInt8)(aVCLStartColor.GetBlue() * nStartIntensity / 100) );
617 
618                     const sal_uInt16 nEndIntensity( rGradient.GetEndIntensity() );
619                     ::Color aVCLEndColor( rGradient.GetEndColor() );
620                     aVCLEndColor.SetRed( (sal_uInt8)(aVCLEndColor.GetRed() * nEndIntensity / 100) );
621                     aVCLEndColor.SetGreen( (sal_uInt8)(aVCLEndColor.GetGreen() * nEndIntensity / 100) );
622                     aVCLEndColor.SetBlue( (sal_uInt8)(aVCLEndColor.GetBlue() * nEndIntensity / 100) );
623 
624                     uno::Reference<rendering::XColorSpace> xColorSpace(
625                         rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace());
626                     const uno::Sequence< double > aStartColor(
627                         ::vcl::unotools::colorToDoubleSequence( aVCLStartColor,
628                                                                 xColorSpace ));
629                     const uno::Sequence< double > aEndColor(
630                         ::vcl::unotools::colorToDoubleSequence( aVCLEndColor,
631                                                                 xColorSpace ));
632 
633                     uno::Sequence< uno::Sequence < double > > aColors(2);
634                     uno::Sequence< double > aStops(2);
635 
636                     if( rGradient.GetStyle() == GRADIENT_AXIAL )
637                     {
638                         aStops.realloc(3);
639                         aColors.realloc(3);
640 
641                         aStops[0] = 0.0;
642                         aStops[1] = 0.5;
643                         aStops[2] = 1.0;
644 
645                         aColors[0] = aEndColor;
646                         aColors[1] = aStartColor;
647                         aColors[2] = aEndColor;
648                     }
649                     else
650                     {
651                         aStops[0] = 0.0;
652                         aStops[1] = 1.0;
653 
654                         aColors[0] = aStartColor;
655                         aColors[1] = aEndColor;
656                     }
657 
658                     const ::basegfx::B2DRectangle aBounds(
659                         ::basegfx::tools::getRange(aDevicePoly) );
660                     const ::basegfx::B2DVector aOffset(
661                         rGradient.GetOfsX() / 100.0,
662                         rGradient.GetOfsY() / 100.0);
663                     double fRotation( rGradient.GetAngle() * M_PI / 1800.0 );
664                     const double fBorder( rGradient.GetBorder() / 100.0 );
665 
666                     basegfx::B2DHomMatrix aRot90;
667                     aRot90.rotate(M_PI_2);
668 
669                     basegfx::ODFGradientInfo aGradInfo;
670                     rtl::OUString aGradientService;
671                     switch( rGradient.GetStyle() )
672                     {
673                         case GRADIENT_LINEAR:
674                             basegfx::tools::createLinearODFGradientInfo(aGradInfo,
675                                                                         aBounds,
676                                                                         nSteps,
677                                                                         fBorder,
678                                                                         fRotation);
679                             // map odf to svg gradient orientation - x
680                             // instead of y direction
681                             aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aRot90;
682                             aGradientService = rtl::OUString::createFromAscii("LinearGradient");
683                             break;
684 
685                         case GRADIENT_AXIAL:
686                         {
687                             // Adapt the border so that it is suitable
688                             // for the axial gradient.  An axial
689                             // gradient consists of two linear
690                             // gradients.  Each of those covers half
691                             // of the total size.  In order to
692                             // compensate for the condensed display of
693                             // the linear gradients, we have to
694                             // enlarge the area taken up by the actual
695                             // gradient (1-fBorder).  After that we
696                             // have to turn the result back into a
697                             // border value, hence the second (left
698                             // most 1-...
699                             const double fAxialBorder (1-2*(1-fBorder));
700                             basegfx::tools::createAxialODFGradientInfo(aGradInfo,
701                                                                         aBounds,
702                                                                         nSteps,
703                                                                         fAxialBorder,
704                                                                         fRotation);
705                             // map odf to svg gradient orientation - x
706                             // instead of y direction
707                             aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aRot90;
708 
709                             // map odf axial gradient to 3-stop linear
710                             // gradient - shift left by 0.5
711                             basegfx::B2DHomMatrix aShift;
712                             aShift.translate(-0.5,0);
713                             aGradInfo.maTextureTransform = aGradInfo.maTextureTransform * aShift;
714 
715                             aGradientService = rtl::OUString::createFromAscii("LinearGradient");
716                             break;
717                         }
718 
719                         case GRADIENT_RADIAL:
720                             basegfx::tools::createRadialODFGradientInfo(aGradInfo,
721                                                                         aBounds,
722                                                                         aOffset,
723                                                                         nSteps,
724                                                                         fBorder);
725                             aGradientService = rtl::OUString::createFromAscii("EllipticalGradient");
726                             break;
727 
728                         case GRADIENT_ELLIPTICAL:
729                             basegfx::tools::createEllipticalODFGradientInfo(aGradInfo,
730                                                                             aBounds,
731                                                                             aOffset,
732                                                                             nSteps,
733                                                                             fBorder,
734                                                                             fRotation);
735                             aGradientService = rtl::OUString::createFromAscii("EllipticalGradient");
736                             break;
737 
738                         case GRADIENT_SQUARE:
739                             basegfx::tools::createSquareODFGradientInfo(aGradInfo,
740                                                                         aBounds,
741                                                                         aOffset,
742                                                                         nSteps,
743                                                                         fBorder,
744                                                                         fRotation);
745                             aGradientService = rtl::OUString::createFromAscii("RectangularGradient");
746                             break;
747 
748                         case GRADIENT_RECT:
749                             basegfx::tools::createRectangularODFGradientInfo(aGradInfo,
750                                                                              aBounds,
751                                                                              aOffset,
752                                                                              nSteps,
753                                                                              fBorder,
754                                                                              fRotation);
755                             aGradientService = rtl::OUString::createFromAscii("RectangularGradient");
756                             break;
757 
758                         default:
759                             ENSURE_OR_THROW( false,
760                                              "ImplRenderer::createGradientAction(): Unexpected gradient type" );
761                             break;
762                     }
763 
764                     // As the texture coordinate space is relative to
765                     // the polygon coordinate space (NOT to the
766                     // polygon itself), move gradient to the start of
767                     // the actual polygon. If we skip this, the
768                     // gradient will always display at the origin, and
769                     // not within the polygon bound (which might be
770                     // miles away from the origin).
771                     aGradInfo.maTextureTransform.translate( aBounds.getMinX(),
772                                                             aBounds.getMinY() );
773                     ::basegfx::unotools::affineMatrixFromHomMatrix( aTexture.AffineTransform,
774                                                                     aGradInfo.maTextureTransform );
775 
776                     uno::Sequence<uno::Any> args(3);
777                     beans::PropertyValue aProp;
778                     aProp.Name = rtl::OUString::createFromAscii("Colors");
779                     aProp.Value <<= aColors;
780                     args[0] <<= aProp;
781                     aProp.Name = rtl::OUString::createFromAscii("Stops");
782                     aProp.Value <<= aStops;
783                     args[1] <<= aProp;
784                     aProp.Name = rtl::OUString::createFromAscii("AspectRatio");
785                     aProp.Value <<= aGradInfo.mfAspectRatio;
786                     args[2] <<= aProp;
787 
788                     aTexture.Gradient.set(
789                         xFactory->createInstanceWithArguments(aGradientService,
790                                                               args),
791                         uno::UNO_QUERY);
792                     if( aTexture.Gradient.is() )
793                     {
794                         ActionSharedPtr pPolyAction(
795                             internal::PolyPolyActionFactory::createPolyPolyAction(
796                                 aDevicePoly,
797                                 rParms.mrCanvas,
798                                 getState( rParms.mrStates ),
799                                 aTexture ) );
800 
801                         if( pPolyAction )
802                         {
803                             maActions.push_back(
804                                 MtfAction(
805                                     pPolyAction,
806                                     rParms.mrCurrActionIndex ) );
807 
808                             rParms.mrCurrActionIndex += pPolyAction->getActionCount()-1;
809                         }
810 
811                         // done, using native gradients
812                         return;
813                     }
814                 }
815             }
816 
817             // cannot currently use native canvas gradients, as a
818             // finite step size is given (this funny feature is not
819             // supported by the XCanvas API)
820             pushState( rParms.mrStates, PUSH_ALL );
821 
822             if( !bIsPolygonRectangle )
823             {
824                 // only clip, if given polygon is not a rectangle in
825                 // the first place (the gradient is always limited to
826                 // the given bound rect)
827                 updateClipping(
828                     aDevicePoly,
829                     rParms,
830                     true );
831             }
832 
833             GDIMetaFile aTmpMtf;
834             rParms.mrVDev.AddGradientActions( rPoly.GetBoundRect(),
835                                               rGradient,
836                                                aTmpMtf );
837 
838             createActions( aTmpMtf, rParms, bSubsettableActions );
839 
840             popState( rParms.mrStates );
841         }
842 
843         uno::Reference< rendering::XCanvasFont > ImplRenderer::createFont( double&                        o_rFontRotation,
844                                                                            const ::Font&                  rFont,
845                                                                            const ActionFactoryParameters& rParms ) const
846         {
847             rendering::FontRequest aFontRequest;
848 
849             if( rParms.mrParms.maFontName.is_initialized() )
850                 aFontRequest.FontDescription.FamilyName = *rParms.mrParms.maFontName;
851             else
852                 aFontRequest.FontDescription.FamilyName = rFont.GetName();
853 
854             aFontRequest.FontDescription.StyleName = rFont.GetStyleName();
855 
856             aFontRequest.FontDescription.IsSymbolFont = (rFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL) ? util::TriState_YES : util::TriState_NO;
857             aFontRequest.FontDescription.IsVertical = rFont.IsVertical() ? util::TriState_YES : util::TriState_NO;
858 
859             // TODO(F2): improve vclenum->panose conversion
860             aFontRequest.FontDescription.FontDescription.Weight =
861                 rParms.mrParms.maFontWeight.is_initialized() ?
862                 *rParms.mrParms.maFontWeight :
863                 ::canvas::tools::numeric_cast<sal_Int8>( ::basegfx::fround( rFont.GetWeight() ) );
864             aFontRequest.FontDescription.FontDescription.Letterform =
865                 rParms.mrParms.maFontLetterForm.is_initialized() ?
866                 *rParms.mrParms.maFontLetterForm :
867                 (rFont.GetItalic() == ITALIC_NONE) ? 0 : 9;
868             aFontRequest.FontDescription.FontDescription.Proportion =
869                 rParms.mrParms.maFontProportion.is_initialized() ?
870                 *rParms.mrParms.maFontProportion :
871                 (rFont.GetPitch() == PITCH_FIXED)
872                     ? rendering::PanoseProportion::MONO_SPACED
873                     : rendering::PanoseProportion::ANYTHING;
874 
875             LanguageType aLang = rFont.GetLanguage();
876             aFontRequest.Locale = MsLangId::convertLanguageToLocale(aLang, false);
877 
878             // setup state-local text transformation,
879             // if the font be rotated
880             const short nFontAngle( rFont.GetOrientation() );
881             if( nFontAngle != 0 )
882             {
883                 // set to unity transform rotated by font angle
884                 const double nAngle( nFontAngle * (F_PI / 1800.0) );
885                 o_rFontRotation = -nAngle;
886             }
887             else
888             {
889                 o_rFontRotation = 0.0;
890             }
891 
892             geometry::Matrix2D aFontMatrix;
893             ::canvas::tools::setIdentityMatrix2D( aFontMatrix );
894 
895             // TODO(F2): use correct scale direction, font
896             // height might be width or anything else
897 
898             // TODO(Q3): This code smells of programming by
899             // coincidence (the next two if statements)
900             const ::Size rFontSizeLog( rFont.GetSize() );
901             const sal_Int32 nFontWidthLog = rFontSizeLog.Width();
902             if( nFontWidthLog != 0 )
903             {
904                 ::Font aTestFont = rFont;
905                 aTestFont.SetWidth( 0 );
906                 sal_Int32 nNormalWidth = rParms.mrVDev.GetFontMetric( aTestFont ).GetWidth();
907                 if( nNormalWidth != nFontWidthLog )
908                     if( nNormalWidth )
909                         aFontMatrix.m00 = (double)nFontWidthLog / nNormalWidth;
910             }
911 
912             // #i52608# apply map mode scale also to font matrix - an
913             // anisotrophic mapmode must be reflected in an
914             // anisotrophic font matrix scale.
915             const OutDevState& rState( getState( rParms.mrStates ) );
916             if( !::basegfx::fTools::equal(
917                     rState.mapModeTransform.get(0,0),
918                     rState.mapModeTransform.get(1,1)) )
919             {
920                 const double nScaleX( rState.mapModeTransform.get(0,0) );
921                 const double nScaleY( rState.mapModeTransform.get(1,1) );
922 
923                 // note: no reason to check for division by zero, we
924                 // always have the value closer (or equal) to zero as
925                 // the nominator.
926                 if( fabs(nScaleX) < fabs(nScaleY) )
927                     aFontMatrix.m00 *= nScaleX / nScaleY;
928                 else
929                     aFontMatrix.m11 *= nScaleY / nScaleX;
930             }
931             aFontRequest.CellSize = (rState.mapModeTransform * ::vcl::unotools::b2DSizeFromSize(rFontSizeLog)).getY();
932 
933             return rParms.mrCanvas->getUNOCanvas()->createFont( aFontRequest,
934                                                                 uno::Sequence< beans::PropertyValue >(),
935                                                                 aFontMatrix );
936         }
937 
938         // create text effects such as shadow/relief/embossed
939         void ImplRenderer::createTextAction( const ::Point& 		        rStartPoint,
940                                              const String                   rString,
941                                              int                            nIndex,
942                                              int                            nLength,
943                                              const sal_Int32*               pCharWidths,
944                                              const ActionFactoryParameters& rParms,
945                                              bool                           bSubsettableActions )
946         {
947             ENSURE_OR_THROW( nIndex >= 0 && nLength <= rString.Len() + nIndex,
948                               "ImplRenderer::createTextWithEffectsAction(): Invalid text index" );
949 
950             if( !nLength )
951                 return; // zero-length text, no visible output
952 
953             const OutDevState& rState( getState( rParms.mrStates ) );
954 
955             // TODO(F2): implement all text effects
956             // if( rState.textAlignment );             // TODO(F2): NYI
957 
958             ::Color aShadowColor( COL_AUTO );
959             ::Color aReliefColor( COL_AUTO );
960             ::Size  aShadowOffset;
961             ::Size  aReliefOffset;
962 
963             uno::Reference<rendering::XColorSpace> xColorSpace(
964                 rParms.mrCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
965 
966             if( rState.isTextEffectShadowSet )
967             {
968                 // calculate shadow offset (similar to outdev3.cxx)
969                 // TODO(F3): better match with outdev3.cxx
970                 sal_Int32 nShadowOffset = static_cast<sal_Int32>(1.5 + ((rParms.mrVDev.GetFont().GetHeight()-24.0)/24.0));
971                 if( nShadowOffset < 1 )
972                     nShadowOffset = 1;
973 
974                 aShadowOffset.setWidth( nShadowOffset );
975                 aShadowOffset.setHeight( nShadowOffset );
976 
977                 // determine shadow color (from outdev3.cxx)
978                 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
979                     rState.textColor, xColorSpace );
980                 bool bIsDark = (aTextColor.GetColor() == COL_BLACK)
981                     || (aTextColor.GetLuminance() < 8);
982 
983                 aShadowColor = bIsDark ? COL_LIGHTGRAY : COL_BLACK;
984                 aShadowColor.SetTransparency( aTextColor.GetTransparency() );
985             }
986 
987             if( rState.textReliefStyle )
988             {
989                 // calculate relief offset (similar to outdev3.cxx)
990                 sal_Int32 nReliefOffset = rParms.mrVDev.PixelToLogic( Size( 1, 1 ) ).Height();
991                 nReliefOffset += nReliefOffset/2;
992                 if( nReliefOffset < 1 )
993                     nReliefOffset = 1;
994 
995                 if( rState.textReliefStyle == RELIEF_ENGRAVED )
996                     nReliefOffset = -nReliefOffset;
997 
998                 aReliefOffset.setWidth( nReliefOffset );
999                 aReliefOffset.setHeight( nReliefOffset );
1000 
1001                 // determine relief color (from outdev3.cxx)
1002                 ::Color aTextColor = ::vcl::unotools::doubleSequenceToColor(
1003                     rState.textColor, xColorSpace );
1004 
1005                 aReliefColor = ::Color( COL_LIGHTGRAY );
1006 
1007                 // we don't have a automatic color, so black is always
1008                 // drawn on white (literally copied from
1009                 // vcl/source/gdi/outdev3.cxx)
1010                 if( aTextColor.GetColor() == COL_BLACK )
1011                 {
1012                     aTextColor = ::Color( COL_WHITE );
1013                     getState( rParms.mrStates ).textColor =
1014                         ::vcl::unotools::colorToDoubleSequence(
1015                             aTextColor, xColorSpace );
1016                 }
1017 
1018                 if( aTextColor.GetColor() == COL_WHITE )
1019                     aReliefColor = ::Color( COL_BLACK );
1020                 aReliefColor.SetTransparency( aTextColor.GetTransparency() );
1021             }
1022 
1023             // create the actual text action
1024             ActionSharedPtr pTextAction(
1025                 TextActionFactory::createTextAction(
1026                     rStartPoint,
1027                     aReliefOffset,
1028                     aReliefColor,
1029                     aShadowOffset,
1030                     aShadowColor,
1031                     rString,
1032                     nIndex,
1033                     nLength,
1034                     pCharWidths,
1035                     rParms.mrVDev,
1036                     rParms.mrCanvas,
1037                     rState,
1038                     rParms.mrParms,
1039                     bSubsettableActions ) );
1040 
1041             ActionSharedPtr pStrikeoutTextAction;
1042 
1043             if ( rState.textStrikeoutStyle == STRIKEOUT_X || rState.textStrikeoutStyle == STRIKEOUT_SLASH )
1044             {
1045                 long nWidth = rParms.mrVDev.GetTextWidth( rString,nIndex,nLength );
1046 
1047                 xub_Unicode pChars[5];
1048                 if ( rState.textStrikeoutStyle == STRIKEOUT_X )
1049                     pChars[0] = 'X';
1050                 else
1051                     pChars[0] = '/';
1052                 pChars[3]=pChars[2]=pChars[1]=pChars[0];
1053 
1054                 long nStrikeoutWidth = nWidth;
1055                 String aStrikeoutTest( pChars, 4 );
1056 
1057                 if( aStrikeoutTest.Len() )
1058                 {
1059                     nStrikeoutWidth = ( rParms.mrVDev.GetTextWidth( aStrikeoutTest ) + 2 ) / 4;
1060                     aStrikeoutTest.Erase();
1061 
1062                     if( nStrikeoutWidth <= 0 )
1063                         nStrikeoutWidth = 1;
1064                 }
1065 
1066                 long nMaxWidth = nStrikeoutWidth/2;
1067                 if ( nMaxWidth < 2 )
1068                     nMaxWidth = 2;
1069                 nMaxWidth += nWidth + 1;
1070 
1071                 long nFullStrikeoutWidth = 0;
1072                 String aStrikeoutText( pChars, 0 );
1073                 while( (nFullStrikeoutWidth+=nStrikeoutWidth ) < nMaxWidth+1 )
1074                     aStrikeoutText += pChars[0];
1075 
1076 
1077                 sal_Int32 nStartPos = 0;
1078                 xub_StrLen nLen = aStrikeoutText.Len();
1079 
1080                 if( nLen )
1081                 {
1082                     long nInterval = ( nWidth - nStrikeoutWidth * nLen ) / nLen;
1083                     nStrikeoutWidth += nInterval;
1084                     sal_Int32* pStrikeoutCharWidths = new sal_Int32[nLen];
1085 
1086                     for ( int i = 0;i<nLen; i++)
1087                     {
1088                         pStrikeoutCharWidths[i] = nStrikeoutWidth;
1089                     }
1090 
1091                     for ( int i = 1;i< nLen; i++ )
1092                     {
1093                         pStrikeoutCharWidths[ i ] += pStrikeoutCharWidths[ i-1 ];
1094                     }
1095 
1096                     pStrikeoutTextAction =
1097                         TextActionFactory::createTextAction(
1098                             rStartPoint,
1099                             aReliefOffset,
1100                             aReliefColor,
1101                             aShadowOffset,
1102                             aShadowColor,
1103                             aStrikeoutText,
1104                             nStartPos,
1105                             aStrikeoutText.Len(),
1106                             pStrikeoutCharWidths,
1107                             rParms.mrVDev,
1108                             rParms.mrCanvas,
1109                             rState,
1110                             rParms.mrParms,
1111                             bSubsettableActions ) ;
1112                 }
1113             }
1114 
1115             if( pTextAction )
1116             {
1117                 maActions.push_back(
1118                     MtfAction(
1119                         pTextAction,
1120                         rParms.mrCurrActionIndex ) );
1121 
1122                 if ( pStrikeoutTextAction )
1123                 {
1124                     maActions.push_back(
1125                         MtfAction(
1126                         pStrikeoutTextAction,
1127                         rParms.mrCurrActionIndex ) );
1128                 }
1129 
1130                 rParms.mrCurrActionIndex += pTextAction->getActionCount()-1;
1131             }
1132 		}
1133 
1134         void ImplRenderer::updateClipping( const ::basegfx::B2DPolyPolygon&	rClipPoly,
1135                                            const ActionFactoryParameters&   rParms,
1136                                            bool								bIntersect )
1137         {
1138             ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) );
1139             ::basegfx::B2DPolyPolygon aClipPoly( rClipPoly );
1140 
1141             const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1142             const bool bEmptyClipPoly( rState.clip.count() == 0 );
1143 
1144             ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1145                               "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1146 
1147             if( !bIntersect ||
1148                 (bEmptyClipRect && bEmptyClipPoly) )
1149             {
1150                 rState.clip = rClipPoly;
1151             }
1152             else
1153             {
1154                 if( !bEmptyClipRect )
1155                 {
1156                     // TODO(P3): Use Liang-Barsky polygon clip here,
1157                     // after all, one object is just a rectangle!
1158 
1159                     // convert rect to polygon beforehand, must revert
1160                     // to general polygon clipping here.
1161                     rState.clip = ::basegfx::B2DPolyPolygon(
1162                         ::basegfx::tools::createPolygonFromRect(
1163                             // #121100# VCL rectangular clips always
1164                             // include one more pixel to the right
1165                             // and the bottom
1166                             ::basegfx::B2DRectangle( rState.clipRect.Left(),
1167                                                      rState.clipRect.Top(),
1168                                                      rState.clipRect.Right()+1,
1169                                                      rState.clipRect.Bottom()+1 ) ) );
1170                 }
1171 
1172                 // AW: Simplified
1173 				rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1174 					aClipPoly, rState.clip, true, false);
1175             }
1176 
1177             // by now, our clip resides in the OutDevState::clip
1178             // poly-polygon.
1179             rState.clipRect.SetEmpty();
1180 
1181             if( rState.clip.count() == 0 )
1182             {
1183                 if( rState.clipRect.IsEmpty() )
1184                 {
1185                     rState.xClipPoly.clear();
1186                 }
1187                 else
1188                 {
1189                     rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1190                         rParms.mrCanvas->getUNOCanvas()->getDevice(),
1191                         ::basegfx::B2DPolyPolygon(
1192                             ::basegfx::tools::createPolygonFromRect(
1193                                 // #121100# VCL rectangular clips
1194                                 // always include one more pixel to
1195                                 // the right and the bottom
1196                                 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1197                                                          rState.clipRect.Top(),
1198                                                          rState.clipRect.Right()+1,
1199                                                          rState.clipRect.Bottom()+1 ) ) ) );
1200                 }
1201             }
1202             else
1203             {
1204                 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1205                     rParms.mrCanvas->getUNOCanvas()->getDevice(),
1206                     rState.clip );
1207             }
1208         }
1209 
1210         void ImplRenderer::updateClipping( const ::Rectangle&		      rClipRect,
1211                                            const ActionFactoryParameters& rParms,
1212                                            bool                           bIntersect )
1213         {
1214             ::cppcanvas::internal::OutDevState& rState( getState( rParms.mrStates ) );
1215 
1216             const bool bEmptyClipRect( rState.clipRect.IsEmpty() );
1217             const bool bEmptyClipPoly( rState.clip.count() == 0 );
1218 
1219             ENSURE_OR_THROW( bEmptyClipPoly || bEmptyClipRect,
1220                               "ImplRenderer::updateClipping(): Clip rect and polygon are both set!" );
1221 
1222             if( !bIntersect ||
1223                 (bEmptyClipRect && bEmptyClipPoly) )
1224             {
1225                 rState.clipRect = rClipRect;
1226                 rState.clip.clear();
1227             }
1228             else if( bEmptyClipPoly )
1229             {
1230                 rState.clipRect.Intersection( rClipRect );
1231                 rState.clip.clear();
1232             }
1233             else
1234             {
1235                 // TODO(P3): Handle a fourth case here, when all clip
1236                 // polygons are rectangular, once B2DMultiRange's
1237                 // sweep line implementation is done.
1238 
1239                 // general case: convert to polygon and clip
1240                 // -----------------------------------------
1241 
1242                 // convert rect to polygon beforehand, must revert
1243                 // to general polygon clipping here.
1244                 ::basegfx::B2DPolyPolygon aClipPoly(
1245                     ::basegfx::tools::createPolygonFromRect(
1246                         ::basegfx::B2DRectangle( rClipRect.Left(),
1247                                                  rClipRect.Top(),
1248                                                  rClipRect.Right(),
1249                                                  rClipRect.Bottom() ) ) );
1250 
1251                 rState.clipRect.SetEmpty();
1252 
1253                 // AW: Simplified
1254 				rState.clip = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1255 					aClipPoly, rState.clip, true, false);
1256             }
1257 
1258             if( rState.clip.count() == 0 )
1259             {
1260                 if( rState.clipRect.IsEmpty() )
1261                 {
1262                     rState.xClipPoly.clear();
1263                 }
1264                 else
1265                 {
1266                     rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1267                         rParms.mrCanvas->getUNOCanvas()->getDevice(),
1268                         ::basegfx::B2DPolyPolygon(
1269                             ::basegfx::tools::createPolygonFromRect(
1270                                 // #121100# VCL rectangular clips
1271                                 // always include one more pixel to
1272                                 // the right and the bottom
1273                                 ::basegfx::B2DRectangle( rState.clipRect.Left(),
1274                                                          rState.clipRect.Top(),
1275                                                          rState.clipRect.Right()+1,
1276                                                          rState.clipRect.Bottom()+1 ) ) ) );
1277                 }
1278             }
1279             else
1280             {
1281                 rState.xClipPoly = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon(
1282                     rParms.mrCanvas->getUNOCanvas()->getDevice(),
1283                     rState.clip );
1284             }
1285         }
1286 
1287         bool ImplRenderer::createActions( GDIMetaFile&				     rMtf,
1288                                           const ActionFactoryParameters& rFactoryParms,
1289                                           bool                           bSubsettableActions )
1290         {
1291             /* TODO(P2): interpret mtf-comments
1292                ================================
1293 
1294                - gradient fillings (do that via comments)
1295 
1296                - think about mapping. _If_ we do everything in logical
1297                	 coordinates (which would solve the probs for stroke
1298                  widths and text offsets), then we would have to
1299                  recalc scaling for every drawing operation. This is
1300                  because the outdev map mode might change at any time.
1301                  Also keep in mind, that, although we've double precision
1302                  float arithmetic now, different offsets might still
1303                  generate different roundings (aka
1304                  'OutputDevice::SetPixelOffset())
1305 
1306              */
1307 
1308             // alias common parameters
1309             VectorOfOutDevStates&  rStates(rFactoryParms.mrStates);
1310             const CanvasSharedPtr& rCanvas(rFactoryParms.mrCanvas);
1311             ::VirtualDevice&       rVDev(rFactoryParms.mrVDev);
1312             const Parameters&      rParms(rFactoryParms.mrParms);
1313             sal_Int32&             io_rCurrActionIndex(rFactoryParms.mrCurrActionIndex);
1314 
1315 
1316             // Loop over every metaaction
1317             // ==========================
1318             MetaAction* pCurrAct;
1319 
1320             // TODO(P1): think about caching
1321             for( pCurrAct=rMtf.FirstAction();
1322                  pCurrAct;
1323                  pCurrAct = rMtf.NextAction() )
1324             {
1325                 // execute every action, to keep VDev state up-to-date
1326                 // currently used only for
1327                 // - the map mode
1328                 // - the line/fill color when processing a META_TRANSPARENT_ACTION
1329                 // - SetFont to process font metric specific actions
1330                 pCurrAct->Execute( &rVDev );
1331 
1332                 switch( pCurrAct->GetType() )
1333                 {
1334                     // ------------------------------------------------------------
1335 
1336                     // In the first part of this monster-switch, we
1337                     // handle all state-changing meta actions. These
1338                     // are all handled locally.
1339 
1340                     // ------------------------------------------------------------
1341 
1342                     case META_PUSH_ACTION:
1343                     {
1344                         MetaPushAction* pPushAction = static_cast<MetaPushAction*>(pCurrAct);
1345                         pushState( rStates,
1346                                    pPushAction->GetFlags() );
1347                     }
1348                     break;
1349 
1350                     case META_POP_ACTION:
1351                         popState( rStates );
1352                         break;
1353 
1354                     case META_TEXTLANGUAGE_ACTION:
1355                         // FALLTHROUGH intended
1356                     case META_REFPOINT_ACTION:
1357                         // handled via pCurrAct->Execute( &rVDev )
1358                         break;
1359 
1360                     case META_MAPMODE_ACTION:
1361                         // modify current mapModeTransformation
1362                         // transformation, such that subsequent
1363                         // coordinates map correctly
1364                         tools::calcLogic2PixelAffineTransform( getState( rStates ).mapModeTransform,
1365                                                                rVDev );
1366                         break;
1367 
1368                     // monitor clip regions, to assemble clip polygon on our own
1369                     case META_CLIPREGION_ACTION:
1370                     {
1371                         MetaClipRegionAction* pClipAction = static_cast<MetaClipRegionAction*>(pCurrAct);
1372 
1373                         if( !pClipAction->IsClipping() )
1374                         {
1375                             // clear clipping
1376                             getState( rStates ).clip.clear();
1377                         }
1378                         else
1379                         {
1380                             if( !pClipAction->GetRegion().HasPolyPolygon() )
1381                             {
1382                                 VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1383                                                "region encountered, falling back to bounding box!" );
1384 
1385                                 // #121806# explicitely kept integer
1386                                 Rectangle aClipRect(
1387                                     rVDev.LogicToPixel(
1388                                         pClipAction->GetRegion().GetBoundRect() ) );
1389 
1390                                 // intersect current clip with given rect
1391                                 updateClipping(
1392                                     aClipRect,
1393                                     rFactoryParms,
1394                                     false );
1395                             }
1396                             else
1397                             {
1398                                 // set new clip polygon (don't intersect
1399                                 // with old one, just set it)
1400 
1401                                 // #121806# explicitely kept integer
1402                                 updateClipping(
1403                                     rVDev.LogicToPixel(
1404                                         pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
1405                                     rFactoryParms,
1406                                     false );
1407                             }
1408                         }
1409 
1410                         break;
1411                     }
1412 
1413                     case META_ISECTRECTCLIPREGION_ACTION:
1414                     {
1415                         MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct);
1416 
1417                         // #121806# explicitely kept integer
1418                         Rectangle aClipRect(
1419                             rVDev.LogicToPixel( pClipAction->GetRect() ) );
1420 
1421                         // intersect current clip with given rect
1422                         updateClipping(
1423                             aClipRect,
1424                             rFactoryParms,
1425                             true );
1426 
1427                         break;
1428                     }
1429 
1430                     case META_ISECTREGIONCLIPREGION_ACTION:
1431                     {
1432                         MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct);
1433 
1434                         if( !pClipAction->GetRegion().HasPolyPolygon() )
1435                         {
1436                             VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1437                                            "region encountered, falling back to bounding box!" );
1438 
1439                             // #121806# explicitely kept integer
1440                             Rectangle aClipRect(
1441                                 rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) );
1442 
1443                             // intersect current clip with given rect
1444                             updateClipping(
1445                                 aClipRect,
1446                                 rFactoryParms,
1447                                 true );
1448                         }
1449                         else
1450                         {
1451                             // intersect current clip with given clip polygon
1452 
1453                             // #121806# explicitely kept integer
1454                             updateClipping(
1455                                 rVDev.LogicToPixel(
1456                                     pClipAction->GetRegion().GetPolyPolygon() ).getB2DPolyPolygon(),
1457                                 rFactoryParms,
1458                                 true );
1459                         }
1460 
1461                         break;
1462                     }
1463 
1464                     case META_MOVECLIPREGION_ACTION:
1465                         // TODO(F2): NYI
1466                         break;
1467 
1468                     case META_LINECOLOR_ACTION:
1469                         if( !rParms.maLineColor.is_initialized() )
1470                         {
1471                             setStateColor( static_cast<MetaLineColorAction*>(pCurrAct),
1472                                            getState( rStates ).isLineColorSet,
1473                                            getState( rStates ).lineColor,
1474                                            rCanvas );
1475                         }
1476                         break;
1477 
1478                     case META_FILLCOLOR_ACTION:
1479                         if( !rParms.maFillColor.is_initialized() )
1480                         {
1481                             setStateColor( static_cast<MetaFillColorAction*>(pCurrAct),
1482                                            getState( rStates ).isFillColorSet,
1483                                            getState( rStates ).fillColor,
1484                                            rCanvas );
1485                         }
1486                         break;
1487 
1488                     case META_TEXTCOLOR_ACTION:
1489                     {
1490                         if( !rParms.maTextColor.is_initialized() )
1491                         {
1492                             // Text color is set unconditionally, thus, no
1493                             // use of setStateColor here
1494                             ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() );
1495 
1496                             // force alpha part of color to
1497                             // opaque. transparent painting is done
1498                             // explicitely via META_TRANSPARENT_ACTION
1499                             aColor.SetTransparency(0);
1500 
1501                             getState( rStates ).textColor =
1502                                 ::vcl::unotools::colorToDoubleSequence(
1503                                     aColor,
1504                                     rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1505                         }
1506                     }
1507                     break;
1508 
1509                     case META_TEXTFILLCOLOR_ACTION:
1510                         if( !rParms.maTextColor.is_initialized() )
1511                         {
1512                             setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct),
1513                                            getState( rStates ).isTextFillColorSet,
1514                                            getState( rStates ).textFillColor,
1515                                            rCanvas );
1516                         }
1517                         break;
1518 
1519                     case META_TEXTLINECOLOR_ACTION:
1520                         if( !rParms.maTextColor.is_initialized() )
1521                         {
1522                             setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct),
1523                                            getState( rStates ).isTextLineColorSet,
1524                                            getState( rStates ).textLineColor,
1525                                            rCanvas );
1526                         }
1527                         break;
1528 
1529                     case META_TEXTALIGN_ACTION:
1530                     {
1531                         ::cppcanvas::internal::OutDevState& rState = getState( rStates );
1532                         const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() );
1533 
1534                         rState.textReferencePoint = eTextAlign;
1535                     }
1536                     break;
1537 
1538                     case META_FONT_ACTION:
1539                     {
1540                         ::cppcanvas::internal::OutDevState& rState = getState( rStates );
1541                         const ::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() );
1542 
1543                         rState.xFont = createFont( rState.fontRotation,
1544                                                    rFont,
1545                                                    rFactoryParms );
1546 
1547                         // TODO(Q2): define and use appropriate enumeration types
1548                         rState.textReliefStyle          = (sal_Int8)rFont.GetRelief();
1549                         rState.textOverlineStyle        = (sal_Int8)rFont.GetOverline();
1550                         rState.textUnderlineStyle       = rParms.maFontUnderline.is_initialized() ?
1551                             (*rParms.maFontUnderline ? (sal_Int8)UNDERLINE_SINGLE : (sal_Int8)UNDERLINE_NONE) :
1552                             (sal_Int8)rFont.GetUnderline();
1553                         rState.textStrikeoutStyle       = (sal_Int8)rFont.GetStrikeout();
1554                         rState.textEmphasisMarkStyle    = (sal_Int8)rFont.GetEmphasisMark();
1555                         rState.isTextEffectShadowSet    = (rFont.IsShadow() != sal_False);
1556                         rState.isTextWordUnderlineSet   = (rFont.IsWordLineMode() != sal_False);
1557                         rState.isTextOutlineModeSet     = (rFont.IsOutline() != sal_False);
1558                     }
1559                     break;
1560 
1561                     case META_RASTEROP_ACTION:
1562                         // TODO(F2): NYI
1563                         break;
1564 
1565                     case META_LAYOUTMODE_ACTION:
1566                     {
1567                         // TODO(F2): A lot is missing here
1568                         int nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode();
1569                         ::cppcanvas::internal::OutDevState& rState = getState( rStates );
1570                         switch( nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG) )
1571                         {
1572                             case TEXT_LAYOUT_BIDI_LTR:
1573                                 rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT;
1574                                 break;
1575 
1576                             case (TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG):
1577                                 rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT;
1578                                 break;
1579 
1580                             case TEXT_LAYOUT_BIDI_RTL:
1581                                 rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT;
1582                                 break;
1583 
1584                             case (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG):
1585                                 rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT;
1586                                 break;
1587                         }
1588 
1589                         rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1590                         if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) )
1591                             && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) )
1592                         {
1593                             rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1594                         }
1595                     }
1596                     break;
1597 
1598                     // ------------------------------------------------------------
1599 
1600                     // In the second part of this monster-switch, we
1601                     // handle all recursing meta actions. These are the
1602                     // ones generating a metafile by themselves, which is
1603                     // then processed by recursively calling this method.
1604 
1605                     // ------------------------------------------------------------
1606 
1607                     case META_GRADIENT_ACTION:
1608                     {
1609                         MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct);
1610                         createGradientAction( ::Polygon( pGradAct->GetRect() ),
1611                                               pGradAct->GetGradient(),
1612                                               rFactoryParms,
1613                                               true,
1614                                               bSubsettableActions );
1615                     }
1616                     break;
1617 
1618                     case META_HATCH_ACTION:
1619                     {
1620                         // TODO(F2): use native Canvas hatches here
1621                         GDIMetaFile aTmpMtf;
1622 
1623                         rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(),
1624                                                static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(),
1625                                                aTmpMtf );
1626                         createActions( aTmpMtf, rFactoryParms,
1627                                        bSubsettableActions );
1628                     }
1629                     break;
1630 
1631                     case META_EPS_ACTION:
1632                     {
1633                         MetaEPSAction* 		pAct = static_cast<MetaEPSAction*>(pCurrAct);
1634                         const GDIMetaFile&  rSubstitute = pAct->GetSubstitute();
1635 
1636                         // #121806# explicitely kept integer
1637                         const Size aMtfSize( rSubstitute.GetPrefSize() );
1638                         const Size aMtfSizePixPre( rVDev.LogicToPixel( aMtfSize,
1639                                                                        rSubstitute.GetPrefMapMode() ) );
1640 
1641                         // #i44110# correct null-sized output - there
1642                         // are metafiles which have zero size in at
1643                         // least one dimension
1644                         const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
1645                                                 ::std::max( aMtfSizePixPre.Height(), 1L ) );
1646 
1647                         // Setup local transform, such that the
1648                         // metafile renders itself into the given
1649                         // output rectangle
1650                         pushState( rStates, PUSH_ALL );
1651 
1652                         rVDev.Push();
1653                         rVDev.SetMapMode( rSubstitute.GetPrefMapMode() );
1654 
1655                         const ::Point& rPos( rVDev.LogicToPixel( pAct->GetPoint() ) );
1656                         const ::Size&  rSize( rVDev.LogicToPixel( pAct->GetSize() ) );
1657 
1658                         getState( rStates ).transform.translate( rPos.X(),
1659                                                                  rPos.Y() );
1660                         getState( rStates ).transform.scale( (double)rSize.Width() / aMtfSizePix.Width(),
1661                                                              (double)rSize.Height() / aMtfSizePix.Height() );
1662 
1663                         createActions( const_cast<GDIMetaFile&>(pAct->GetSubstitute()),
1664                                        rFactoryParms,
1665                                        bSubsettableActions );
1666 
1667                         rVDev.Pop();
1668                         popState( rStates );
1669                     }
1670                     break;
1671 
1672                     // handle metafile comments, to retrieve
1673                     // meta-information for gradients, fills and
1674                     // strokes. May skip actions, and may recurse.
1675                     case META_COMMENT_ACTION:
1676                     {
1677                         MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct);
1678 
1679                         // Handle gradients
1680                         if ( pAct->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL )
1681                         {
1682                             MetaGradientExAction* pGradAction = NULL;
1683                             bool bDone( false );
1684                             while( !bDone &&
1685                                    (pCurrAct=rMtf.NextAction()) != NULL )
1686                             {
1687                                 switch( pCurrAct->GetType() )
1688                                 {
1689                                     // extract gradient info
1690                                     case META_GRADIENTEX_ACTION:
1691                                         pGradAction = static_cast<MetaGradientExAction*>(pCurrAct);
1692                                         break;
1693 
1694                                     // skip broken-down rendering, output gradient when sequence is ended
1695                                     case META_COMMENT_ACTION:
1696                                         if( static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL )
1697                                         {
1698                                             bDone = true;
1699 
1700                                             if( pGradAction )
1701                                             {
1702                                                 createGradientAction( pGradAction->GetPolyPolygon(),
1703                                                                       pGradAction->GetGradient(),
1704                                                                       rFactoryParms,
1705                                                                       false,
1706                                                                       bSubsettableActions );
1707                                             }
1708                                         }
1709                                         break;
1710                                 }
1711                             }
1712                         }
1713                         // TODO(P2): Handle drawing layer strokes, via
1714                         // XPATHSTROKE_SEQ_BEGIN comment
1715 
1716                         // Handle drawing layer fills
1717                         else if( pAct->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) )
1718                         {
1719                             const sal_uInt8* pData = pAct->GetData();
1720                             if ( pData )
1721                             {
1722                                 SvMemoryStream	aMemStm( (void*)pData, pAct->GetDataSize(), STREAM_READ );
1723 
1724                                 SvtGraphicFill aFill;
1725                                 aMemStm >> aFill;
1726 
1727                                 // TODO(P2): Also handle gradients and
1728                                 // hatches like this
1729 
1730                                 // only evaluate comment for pure
1731                                 // bitmap fills. If a transparency
1732                                 // gradient is involved (denoted by
1733                                 // the FloatTransparent action), take
1734                                 // the normal meta actions.
1735                                 if( aFill.getFillType() == SvtGraphicFill::fillTexture &&
1736                                     !isActionContained( rMtf,
1737                                                        "XPATHFILL_SEQ_END",
1738                                                         META_FLOATTRANSPARENT_ACTION ) )
1739                                 {
1740                                     rendering::Texture aTexture;
1741 
1742                                     // TODO(F1): the SvtGraphicFill
1743                                     // can also transport metafiles
1744                                     // here, handle that case, too
1745                                     Graphic	aGraphic;
1746                                     aFill.getGraphic( aGraphic );
1747 
1748                                     BitmapEx 	 aBmpEx( aGraphic.GetBitmapEx() );
1749                                     const ::Size aBmpSize( aBmpEx.GetSizePixel() );
1750 
1751                                     ::SvtGraphicFill::Transform aTransform;
1752                                     aFill.getTransform( aTransform );
1753 
1754                                     ::basegfx::B2DHomMatrix aMatrix;
1755 
1756                                     // convert to basegfx matrix
1757                                     aMatrix.set(0,0, aTransform.matrix[ 0 ] );
1758                                     aMatrix.set(0,1, aTransform.matrix[ 1 ] );
1759                                     aMatrix.set(0,2, aTransform.matrix[ 2 ] );
1760                                     aMatrix.set(1,0, aTransform.matrix[ 3 ] );
1761                                     aMatrix.set(1,1, aTransform.matrix[ 4 ] );
1762                                     aMatrix.set(1,2, aTransform.matrix[ 5 ] );
1763 
1764                                     ::basegfx::B2DHomMatrix aScale;
1765                                     aScale.scale( aBmpSize.Width(),
1766                                                   aBmpSize.Height() );
1767 
1768                                     // post-multiply with the bitmap
1769                                     // size (XCanvas' texture assumes
1770                                     // the given bitmap to be
1771                                     // normalized to [0,1]x[0,1]
1772                                     // rectangle)
1773                                     aMatrix = aMatrix * aScale;
1774 
1775                                     // pre-multiply with the
1776                                     // logic-to-pixel scale factor
1777                                     // (the metafile comment works in
1778                                     // logical coordinates).
1779                                     ::basegfx::B2DHomMatrix aLogic2PixelTransform;
1780                                     aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform,
1781                                                                                       rVDev );
1782 
1783                                     ::basegfx::unotools::affineMatrixFromHomMatrix(
1784                                         aTexture.AffineTransform,
1785                                         aMatrix );
1786 
1787                                     aTexture.Alpha = 1.0 - aFill.getTransparency();
1788                                     aTexture.Bitmap =
1789                                         ::vcl::unotools::xBitmapFromBitmapEx(
1790                                             rCanvas->getUNOCanvas()->getDevice(),
1791                                             aBmpEx );
1792                                     if( aFill.isTiling() )
1793                                     {
1794                                         aTexture.RepeatModeX = rendering::TexturingMode::REPEAT;
1795                                         aTexture.RepeatModeY = rendering::TexturingMode::REPEAT;
1796                                     }
1797                                     else
1798                                     {
1799                                         aTexture.RepeatModeX = rendering::TexturingMode::NONE;
1800                                         aTexture.RepeatModeY = rendering::TexturingMode::NONE;
1801                                     }
1802 
1803                                     ::PolyPolygon aPath;
1804                                     aFill.getPath( aPath );
1805 
1806                                     ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() );
1807                                     aPoly.transform( getState( rStates ).mapModeTransform );
1808                                     ActionSharedPtr pPolyAction(
1809                                         internal::PolyPolyActionFactory::createPolyPolyAction(
1810                                             aPoly,
1811                                             rCanvas,
1812                                             getState( rStates ),
1813                                             aTexture ) );
1814 
1815                                     if( pPolyAction )
1816                                     {
1817                                         maActions.push_back(
1818                                             MtfAction(
1819                                                 pPolyAction,
1820                                                 io_rCurrActionIndex ) );
1821 
1822                                         io_rCurrActionIndex += pPolyAction->getActionCount()-1;
1823                                     }
1824 
1825                                     // skip broken-down render output
1826                                     skipContent( rMtf,
1827                                                  "XPATHFILL_SEQ_END",
1828                                                  io_rCurrActionIndex );
1829                                 }
1830                             }
1831                         }
1832                     }
1833                     break;
1834 
1835                     // ------------------------------------------------------------
1836 
1837                     // In the third part of this monster-switch, we
1838                     // handle all 'acting' meta actions. These are all
1839                     // processed by constructing function objects for
1840                     // them, which will later ease caching.
1841 
1842                     // ------------------------------------------------------------
1843 
1844                     case META_POINT_ACTION:
1845                     {
1846                         const OutDevState& rState( getState( rStates ) );
1847                         if( rState.lineColor.getLength() )
1848                         {
1849                             ActionSharedPtr pPointAction(
1850                                 internal::PointActionFactory::createPointAction(
1851                                     rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1852                                         static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ),
1853                                     rCanvas,
1854                                     rState ) );
1855 
1856                             if( pPointAction )
1857                             {
1858                                 maActions.push_back(
1859                                     MtfAction(
1860                                         pPointAction,
1861                                         io_rCurrActionIndex ) );
1862 
1863                                 io_rCurrActionIndex += pPointAction->getActionCount()-1;
1864                             }
1865                         }
1866                     }
1867                     break;
1868 
1869                     case META_PIXEL_ACTION:
1870                     {
1871                         const OutDevState& rState( getState( rStates ) );
1872                         if( rState.lineColor.getLength() )
1873                         {
1874                             ActionSharedPtr pPointAction(
1875                                 internal::PointActionFactory::createPointAction(
1876                                     rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1877                                         static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ),
1878                                     rCanvas,
1879                                     rState,
1880                                     static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) );
1881 
1882                             if( pPointAction )
1883                             {
1884                                 maActions.push_back(
1885                                     MtfAction(
1886                                         pPointAction,
1887                                         io_rCurrActionIndex ) );
1888 
1889                                 io_rCurrActionIndex += pPointAction->getActionCount()-1;
1890                             }
1891                         }
1892                     }
1893                     break;
1894 
1895                     case META_LINE_ACTION:
1896                     {
1897                         const OutDevState& rState( getState( rStates ) );
1898                         if( rState.lineColor.getLength() )
1899                         {
1900                             MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct);
1901 
1902                             const LineInfo& rLineInfo( pLineAct->GetLineInfo() );
1903 
1904                             const ::basegfx::B2DPoint aStartPoint(
1905                                 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() ));
1906                             const ::basegfx::B2DPoint aEndPoint(
1907                                 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() ));
1908 
1909                             ActionSharedPtr pLineAction;
1910 
1911                             if( rLineInfo.IsDefault() )
1912                             {
1913                                 // plain hair line
1914                                 pLineAction =
1915                                     internal::LineActionFactory::createLineAction(
1916                                         aStartPoint,
1917                                         aEndPoint,
1918                                         rCanvas,
1919                                         rState );
1920 
1921                                 if( pLineAction )
1922                                 {
1923                                     maActions.push_back(
1924                                         MtfAction(
1925                                             pLineAction,
1926                                             io_rCurrActionIndex ) );
1927 
1928                                     io_rCurrActionIndex += pLineAction->getActionCount()-1;
1929                                 }
1930                             }
1931                             else if( LINE_NONE != rLineInfo.GetStyle() )
1932                             {
1933                                 // 'thick' line
1934                                 rendering::StrokeAttributes aStrokeAttributes;
1935 
1936                                 setupStrokeAttributes( aStrokeAttributes,
1937                                                        rFactoryParms,
1938                                                        rLineInfo );
1939 
1940                                 // XCanvas can only stroke polygons,
1941                                 // not simple lines - thus, handle
1942                                 // this case via the polypolygon
1943                                 // action
1944                                 ::basegfx::B2DPolygon aPoly;
1945                                 aPoly.append( aStartPoint );
1946                                 aPoly.append( aEndPoint );
1947                                 pLineAction =
1948                                     internal::PolyPolyActionFactory::createPolyPolyAction(
1949                                         ::basegfx::B2DPolyPolygon( aPoly ),
1950                                         rCanvas, rState, aStrokeAttributes );
1951 
1952                                 if( pLineAction )
1953                                 {
1954                                     maActions.push_back(
1955                                         MtfAction(
1956                                             pLineAction,
1957                                             io_rCurrActionIndex ) );
1958 
1959                                     io_rCurrActionIndex += pLineAction->getActionCount()-1;
1960                                 }
1961                             }
1962                             // else: line style is default
1963                             // (i.e. invisible), don't generate action
1964                         }
1965                     }
1966                     break;
1967 
1968                     case META_RECT_ACTION:
1969                     {
1970                         const Rectangle& rRect(
1971                             static_cast<MetaRectAction*>(pCurrAct)->GetRect() );
1972 
1973                         if( rRect.IsEmpty() )
1974                             break;
1975 
1976                         const OutDevState& rState( getState( rStates ) );
1977                         const ::basegfx::B2DPoint aTopLeftPixel(
1978                             rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) );
1979                         const ::basegfx::B2DPoint aBottomRightPixel(
1980                             rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
1981                             // #121100# OutputDevice::DrawRect() fills
1982                             // rectangles Apple-like, i.e. with one
1983                             // additional pixel to the right and bottom.
1984                             ::basegfx::B2DPoint(1,1) );
1985 
1986                         createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
1987                                                  ::basegfx::B2DRange( aTopLeftPixel,
1988                                                                       aBottomRightPixel )),
1989                                              rFactoryParms );
1990                         break;
1991                     }
1992 
1993                     case META_ROUNDRECT_ACTION:
1994                     {
1995                         const Rectangle& rRect(
1996                             static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect());
1997 
1998                         if( rRect.IsEmpty() )
1999                             break;
2000 
2001                         ::basegfx::B2DPolygon aPoly(
2002                             ::basegfx::tools::createPolygonFromRect(
2003                                 ::basegfx::B2DRange(
2004                                     ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
2005                                     ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
2006                                     ::basegfx::B2DPoint(1,1) ),
2007                                 static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound(),
2008                                 static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() ));
2009                         aPoly.transform( getState( rStates ).mapModeTransform );
2010 
2011                         createFillAndStroke( aPoly,
2012                                              rFactoryParms );
2013                     }
2014                     break;
2015 
2016                     case META_ELLIPSE_ACTION:
2017                     {
2018                         const Rectangle& rRect(
2019                             static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() );
2020 
2021                         if( rRect.IsEmpty() )
2022                             break;
2023 
2024                         const ::basegfx::B2DRange aRange(
2025                             ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
2026                             ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
2027                             ::basegfx::B2DPoint(1,1) );
2028 
2029                         ::basegfx::B2DPolygon aPoly(
2030                             ::basegfx::tools::createPolygonFromEllipse(
2031                                 aRange.getCenter(),
2032                                 aRange.getWidth(),
2033                                 aRange.getHeight() ));
2034                         aPoly.transform( getState( rStates ).mapModeTransform );
2035 
2036                         createFillAndStroke( aPoly,
2037                                              rFactoryParms );
2038                     }
2039                     break;
2040 
2041                     case META_ARC_ACTION:
2042                     {
2043                         // TODO(F1): Missing basegfx functionality. Mind empty rects!
2044                         const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(),
2045                                                   static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(),
2046                                                   static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC );
2047                         ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2048                         aPoly.transform( getState( rStates ).mapModeTransform );
2049 
2050                         createFillAndStroke( aPoly,
2051                                              rFactoryParms );
2052                     }
2053                     break;
2054 
2055                     case META_PIE_ACTION:
2056                     {
2057                         // TODO(F1): Missing basegfx functionality. Mind empty rects!
2058                         const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(),
2059                                                   static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(),
2060                                                   static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE );
2061                         ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2062                         aPoly.transform( getState( rStates ).mapModeTransform );
2063 
2064                         createFillAndStroke( aPoly,
2065                                              rFactoryParms );
2066                     }
2067                     break;
2068 
2069                     case META_CHORD_ACTION:
2070                     {
2071                         // TODO(F1): Missing basegfx functionality. Mind empty rects!
2072                         const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(),
2073                                                   static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(),
2074                                                   static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD );
2075                         ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2076                         aPoly.transform( getState( rStates ).mapModeTransform );
2077 
2078                         createFillAndStroke( aPoly,
2079                                              rFactoryParms );
2080                     }
2081                     break;
2082 
2083                     case META_POLYLINE_ACTION:
2084                     {
2085                         const OutDevState& rState( getState( rStates ) );
2086                         if( rState.lineColor.getLength() ||
2087                             rState.fillColor.getLength() )
2088                         {
2089                             MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct);
2090 
2091                             const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() );
2092                             ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() );
2093                             aPoly.transform( rState.mapModeTransform );
2094 
2095                             ActionSharedPtr pLineAction;
2096 
2097                             if( rLineInfo.IsDefault() )
2098                             {
2099                                 // plain hair line polygon
2100                                 pLineAction =
2101                                     internal::PolyPolyActionFactory::createLinePolyPolyAction(
2102                                         ::basegfx::B2DPolyPolygon(aPoly),
2103                                         rCanvas,
2104                                         rState );
2105 
2106                                 if( pLineAction )
2107                                 {
2108                                     maActions.push_back(
2109                                         MtfAction(
2110                                             pLineAction,
2111                                             io_rCurrActionIndex ) );
2112 
2113                                     io_rCurrActionIndex += pLineAction->getActionCount()-1;
2114                                 }
2115                             }
2116                             else if( LINE_NONE != rLineInfo.GetStyle() )
2117                             {
2118                                 // 'thick' line polygon
2119                                 rendering::StrokeAttributes aStrokeAttributes;
2120 
2121                                 setupStrokeAttributes( aStrokeAttributes,
2122                                                        rFactoryParms,
2123                                                        rLineInfo );
2124 
2125                                 pLineAction =
2126                                     internal::PolyPolyActionFactory::createPolyPolyAction(
2127                                         ::basegfx::B2DPolyPolygon(aPoly),
2128                                         rCanvas,
2129                                         rState,
2130                                         aStrokeAttributes ) ;
2131 
2132                                 if( pLineAction )
2133                                 {
2134                                     maActions.push_back(
2135                                         MtfAction(
2136                                             pLineAction,
2137                                             io_rCurrActionIndex ) );
2138 
2139                                     io_rCurrActionIndex += pLineAction->getActionCount()-1;
2140                                 }
2141                             }
2142                             // else: line style is default
2143                             // (i.e. invisible), don't generate action
2144                         }
2145                     }
2146                     break;
2147 
2148                     case META_POLYGON_ACTION:
2149                     {
2150                         ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() );
2151                         aPoly.transform( getState( rStates ).mapModeTransform );
2152                         createFillAndStroke( aPoly,
2153                                              rFactoryParms );
2154                     }
2155                     break;
2156 
2157                     case META_POLYPOLYGON_ACTION:
2158                     {
2159                         ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() );
2160                         aPoly.transform( getState( rStates ).mapModeTransform );
2161                         createFillAndStroke( aPoly,
2162                                              rFactoryParms );
2163                     }
2164                     break;
2165 
2166                     case META_BMP_ACTION:
2167                     {
2168                         MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct);
2169 
2170                         ActionSharedPtr pBmpAction(
2171                                 internal::BitmapActionFactory::createBitmapAction(
2172                                     pAct->GetBitmap(),
2173                                     getState( rStates ).mapModeTransform *
2174                                     ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2175                                     rCanvas,
2176                                     getState( rStates ) ) );
2177 
2178                         if( pBmpAction )
2179                         {
2180                             maActions.push_back(
2181                                 MtfAction(
2182                                     pBmpAction,
2183                                     io_rCurrActionIndex ) );
2184 
2185                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2186                         }
2187                     }
2188                     break;
2189 
2190                     case META_BMPSCALE_ACTION:
2191                     {
2192                         MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct);
2193 
2194                         ActionSharedPtr pBmpAction(
2195                                 internal::BitmapActionFactory::createBitmapAction(
2196                                     pAct->GetBitmap(),
2197                                     getState( rStates ).mapModeTransform *
2198                                     ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2199                                     getState( rStates ).mapModeTransform *
2200                                     ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2201                                     rCanvas,
2202                                     getState( rStates ) ) );
2203 
2204                         if( pBmpAction )
2205                         {
2206                             maActions.push_back(
2207                                 MtfAction(
2208                                     pBmpAction,
2209                                     io_rCurrActionIndex ) );
2210 
2211                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2212                         }
2213                     }
2214                     break;
2215 
2216                     case META_BMPSCALEPART_ACTION:
2217                     {
2218                         MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct);
2219 
2220                         // crop bitmap to given source rectangle (no
2221                         // need to copy and convert the whole bitmap)
2222                         Bitmap aBmp( pAct->GetBitmap() );
2223                         const Rectangle aCropRect( pAct->GetSrcPoint(),
2224                                                     pAct->GetSrcSize() );
2225                         aBmp.Crop( aCropRect );
2226 
2227                         ActionSharedPtr pBmpAction(
2228                                 internal::BitmapActionFactory::createBitmapAction(
2229                                     aBmp,
2230                                     getState( rStates ).mapModeTransform *
2231                                     ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2232                                     getState( rStates ).mapModeTransform *
2233                                     ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2234                                     rCanvas,
2235                                     getState( rStates ) ) );
2236 
2237                         if( pBmpAction )
2238                         {
2239                             maActions.push_back(
2240                                 MtfAction(
2241                                     pBmpAction,
2242                                     io_rCurrActionIndex ) );
2243 
2244                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2245                         }
2246                     }
2247                     break;
2248 
2249                     case META_BMPEX_ACTION:
2250                     {
2251                         MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct);
2252 
2253                         ActionSharedPtr pBmpAction(
2254                                 internal::BitmapActionFactory::createBitmapAction(
2255                                     pAct->GetBitmapEx(),
2256                                     getState( rStates ).mapModeTransform *
2257                                     ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2258                                     rCanvas,
2259                                     getState( rStates ) ) );
2260 
2261                         if( pBmpAction )
2262                         {
2263                             maActions.push_back(
2264                                 MtfAction(
2265                                     pBmpAction,
2266                                     io_rCurrActionIndex ) );
2267 
2268                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2269                         }
2270                     }
2271                     break;
2272 
2273                     case META_BMPEXSCALE_ACTION:
2274                     {
2275                         MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct);
2276 
2277                         ActionSharedPtr pBmpAction(
2278                                 internal::BitmapActionFactory::createBitmapAction(
2279                                     pAct->GetBitmapEx(),
2280                                     getState( rStates ).mapModeTransform *
2281                                     ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2282                                     getState( rStates ).mapModeTransform *
2283                                     ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2284                                     rCanvas,
2285                                     getState( rStates ) ) );
2286 
2287                         if( pBmpAction )
2288                         {
2289                             maActions.push_back(
2290                                 MtfAction(
2291                                     pBmpAction,
2292                                     io_rCurrActionIndex ) );
2293 
2294                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2295                         }
2296                     }
2297                     break;
2298 
2299                     case META_BMPEXSCALEPART_ACTION:
2300                     {
2301                         MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct);
2302 
2303                         // crop bitmap to given source rectangle (no
2304                         // need to copy and convert the whole bitmap)
2305                         BitmapEx aBmp( pAct->GetBitmapEx() );
2306                         const Rectangle aCropRect( pAct->GetSrcPoint(),
2307                                                    pAct->GetSrcSize() );
2308                         aBmp.Crop( aCropRect );
2309 
2310                         ActionSharedPtr pBmpAction(
2311                             internal::BitmapActionFactory::createBitmapAction(
2312                                 aBmp,
2313                                 getState( rStates ).mapModeTransform *
2314                                 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2315                                 getState( rStates ).mapModeTransform *
2316                                 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2317                                 rCanvas,
2318                                 getState( rStates ) ) );
2319 
2320                         if( pBmpAction )
2321                         {
2322                             maActions.push_back(
2323                                 MtfAction(
2324                                     pBmpAction,
2325                                     io_rCurrActionIndex ) );
2326 
2327                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2328                         }
2329                     }
2330                     break;
2331 
2332                     case META_MASK_ACTION:
2333                     {
2334                         MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct);
2335 
2336                         // create masked BitmapEx right here, as the
2337                         // canvas does not provide equivalent
2338                         // functionality
2339                         BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2340                                                         pAct->GetColor() ));
2341 
2342                         ActionSharedPtr pBmpAction(
2343                             internal::BitmapActionFactory::createBitmapAction(
2344                                 aBmp,
2345                                 getState( rStates ).mapModeTransform *
2346                                 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2347                                 rCanvas,
2348                                 getState( rStates ) ) );
2349 
2350                         if( pBmpAction )
2351                         {
2352                             maActions.push_back(
2353                                 MtfAction(
2354                                     pBmpAction,
2355                                     io_rCurrActionIndex ) );
2356 
2357                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2358                         }
2359                     }
2360                     break;
2361 
2362                     case META_MASKSCALE_ACTION:
2363                     {
2364                         MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct);
2365 
2366                         // create masked BitmapEx right here, as the
2367                         // canvas does not provide equivalent
2368                         // functionality
2369                         BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2370                                                         pAct->GetColor() ));
2371 
2372                         ActionSharedPtr pBmpAction(
2373                             internal::BitmapActionFactory::createBitmapAction(
2374                                 aBmp,
2375                                 getState( rStates ).mapModeTransform *
2376                                 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2377                                 getState( rStates ).mapModeTransform *
2378                                 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2379                                 rCanvas,
2380                                 getState( rStates ) ) );
2381 
2382                         if( pBmpAction )
2383                         {
2384                             maActions.push_back(
2385                                 MtfAction(
2386                                     pBmpAction,
2387                                     io_rCurrActionIndex ) );
2388 
2389                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2390                         }
2391                     }
2392                     break;
2393 
2394                     case META_MASKSCALEPART_ACTION:
2395                     {
2396                         MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct);
2397 
2398                         // create masked BitmapEx right here, as the
2399                         // canvas does not provide equivalent
2400                         // functionality
2401                         BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2402                                                         pAct->GetColor() ));
2403 
2404                         // crop bitmap to given source rectangle (no
2405                         // need to copy and convert the whole bitmap)
2406                         const Rectangle aCropRect( pAct->GetSrcPoint(),
2407                                                    pAct->GetSrcSize() );
2408                         aBmp.Crop( aCropRect );
2409 
2410                         ActionSharedPtr pBmpAction(
2411                             internal::BitmapActionFactory::createBitmapAction(
2412                                 aBmp,
2413                                 getState( rStates ).mapModeTransform *
2414                                 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2415                                 getState( rStates ).mapModeTransform *
2416                                 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2417                                 rCanvas,
2418                                 getState( rStates ) ) );
2419 
2420                         if( pBmpAction )
2421                         {
2422                             maActions.push_back(
2423                                 MtfAction(
2424                                     pBmpAction,
2425                                     io_rCurrActionIndex ) );
2426 
2427                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2428                         }
2429                     }
2430                     break;
2431 
2432                     case META_GRADIENTEX_ACTION:
2433                         // TODO(F1): use native Canvas gradients here
2434                         // action is ignored here, because redundant to META_GRADIENT_ACTION
2435                         break;
2436 
2437                     case META_WALLPAPER_ACTION:
2438                         // TODO(F2): NYI
2439                         break;
2440 
2441                     case META_TRANSPARENT_ACTION:
2442                     {
2443                         const OutDevState& rState( getState( rStates ) );
2444                         if( rState.lineColor.getLength() ||
2445                             rState.fillColor.getLength() )
2446                         {
2447                             MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct);
2448                             ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() );
2449                             aPoly.transform( rState.mapModeTransform );
2450 
2451                             ActionSharedPtr pPolyAction(
2452                                 internal::PolyPolyActionFactory::createPolyPolyAction(
2453                                     aPoly,
2454                                     rCanvas,
2455                                     rState,
2456                                     pAct->GetTransparence() ) );
2457 
2458                             if( pPolyAction )
2459                             {
2460                                 maActions.push_back(
2461                                     MtfAction(
2462                                         pPolyAction,
2463                                         io_rCurrActionIndex ) );
2464 
2465                                 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2466                             }
2467                         }
2468                     }
2469                     break;
2470 
2471                     case META_FLOATTRANSPARENT_ACTION:
2472                     {
2473                         MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct);
2474 
2475                         internal::MtfAutoPtr pMtf(
2476                             new ::GDIMetaFile( pAct->GetGDIMetaFile() ) );
2477 
2478                         // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2479                         internal::GradientAutoPtr pGradient(
2480                             new Gradient( pAct->GetGradient() ) );
2481 
2482                         DBG_TESTSOLARMUTEX();
2483 
2484                         ActionSharedPtr pFloatTransAction(
2485                             internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2486                                 pMtf,
2487                                 pGradient,
2488                                 rParms,
2489                                 getState( rStates ).mapModeTransform *
2490                                 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2491                                 getState( rStates ).mapModeTransform *
2492                                 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2493                                 rCanvas,
2494                                 getState( rStates ) ) );
2495 
2496                         if( pFloatTransAction )
2497                         {
2498                             maActions.push_back(
2499                                 MtfAction(
2500                                     pFloatTransAction,
2501                                     io_rCurrActionIndex ) );
2502 
2503                             io_rCurrActionIndex += pFloatTransAction->getActionCount()-1;
2504                         }
2505                     }
2506                     break;
2507 
2508                     case META_TEXT_ACTION:
2509                     {
2510                         MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct);
2511                         XubString sText = XubString( pAct->GetText() );
2512 
2513                         if( rVDev.GetDigitLanguage())
2514                             convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2515 
2516                         createTextAction(
2517                             pAct->GetPoint(),
2518                             sText,
2519                             pAct->GetIndex(),
2520                             pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(),
2521                             NULL,
2522                             rFactoryParms,
2523                             bSubsettableActions );
2524                     }
2525                     break;
2526 
2527                     case META_TEXTARRAY_ACTION:
2528                     {
2529                         MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct);
2530                         XubString sText = XubString( pAct->GetText() );
2531 
2532                         if( rVDev.GetDigitLanguage())
2533                             convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2534 
2535                         createTextAction(
2536                             pAct->GetPoint(),
2537                             sText,
2538                             pAct->GetIndex(),
2539                             pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(),
2540                             pAct->GetDXArray(),
2541                             rFactoryParms,
2542                             bSubsettableActions );
2543                     }
2544                     break;
2545 
2546                     case META_TEXTLINE_ACTION:
2547                     {
2548                         MetaTextLineAction*      pAct = static_cast<MetaTextLineAction*>(pCurrAct);
2549 
2550                         const OutDevState&       rState( getState( rStates ) );
2551                         const ::Size             aBaselineOffset( tools::getBaselineOffset( rState,
2552                                                                                             rVDev ) );
2553                         const ::Point 		     aStartPoint( pAct->GetStartPoint() );
2554                         const ::basegfx::B2DSize aSize( rState.mapModeTransform *
2555                                                         ::basegfx::B2DSize(pAct->GetWidth(),
2556                                                                            0 ));
2557 
2558                         ActionSharedPtr pPolyAction(
2559                             PolyPolyActionFactory::createPolyPolyAction(
2560                                 tools::createTextLinesPolyPolygon(
2561                                     rState.mapModeTransform *
2562                                     ::basegfx::B2DPoint(
2563                                         ::vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) +
2564                                         ::vcl::unotools::b2DSizeFromSize(aBaselineOffset)),
2565                                     aSize.getX(),
2566                                     tools::createTextLineInfo( rVDev,
2567                                                                rState )),
2568                                 rCanvas,
2569                                 rState ) );
2570 
2571                         if( pPolyAction.get() )
2572                         {
2573                             maActions.push_back(
2574                                 MtfAction(
2575                                     pPolyAction,
2576                                     io_rCurrActionIndex ) );
2577 
2578                             io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2579                         }
2580                     }
2581                     break;
2582 
2583                     case META_TEXTRECT_ACTION:
2584                     {
2585                         MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct);
2586 
2587                         pushState( rStates, PUSH_ALL );
2588 
2589                         // use the VDev to break up the text rect
2590                         // action into readily formatted lines
2591                         GDIMetaFile aTmpMtf;
2592                         rVDev.AddTextRectActions( pAct->GetRect(),
2593                                                   pAct->GetText(),
2594                                                   pAct->GetStyle(),
2595                                                   aTmpMtf );
2596 
2597                         createActions( aTmpMtf,
2598                                        rFactoryParms,
2599                                        bSubsettableActions );
2600 
2601                         popState( rStates );
2602 
2603                         break;
2604                     }
2605 
2606                     case META_STRETCHTEXT_ACTION:
2607                     {
2608                         MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct);
2609                         XubString sText = XubString( pAct->GetText() );
2610 
2611                         if( rVDev.GetDigitLanguage())
2612                             convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2613 
2614                         const sal_uInt16 nLen( pAct->GetLen() == (sal_uInt16)STRING_LEN ?
2615                                            pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen() );
2616 
2617                         // #i70897# Nothing to do, actually...
2618                         if( nLen == 0 )
2619                             break;
2620 
2621                         // have to fit the text into the given
2622                         // width. This is achieved by internally
2623                         // generating a DX array, and uniformly
2624                         // distributing the excess/insufficient width
2625                         // to every logical character.
2626                         ::boost::scoped_array< sal_Int32 > pDXArray( new sal_Int32[nLen] );
2627 
2628                         rVDev.GetTextArray( pAct->GetText(), pDXArray.get(),
2629                                             pAct->GetIndex(), pAct->GetLen() );
2630 
2631                         const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] );
2632 
2633                         // Last entry of pDXArray contains total width of the text
2634                         sal_Int32* p=pDXArray.get();
2635                         for( sal_uInt16 i=1; i<=nLen; ++i )
2636                         {
2637                             // calc ratio for every array entry, to
2638                             // distribute rounding errors 'evenly'
2639                             // across the characters. Note that each
2640                             // entry represents the 'end' position of
2641                             // the corresponding character, thus, we
2642                             // let i run from 1 to nLen.
2643                             *p++ += (sal_Int32)i*nWidthDifference/nLen;
2644                         }
2645 
2646                         createTextAction(
2647                             pAct->GetPoint(),
2648                             sText,
2649                             pAct->GetIndex(),
2650                             pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(),
2651                             pDXArray.get(),
2652                             rFactoryParms,
2653                             bSubsettableActions );
2654                     }
2655                     break;
2656 
2657                     case META_RENDERGRAPHIC_ACTION:
2658                     {
2659                         MetaRenderGraphicAction* pAct = static_cast<MetaRenderGraphicAction*>(pCurrAct);
2660 
2661                         ActionSharedPtr pRenderGraphicAction(
2662                                 internal::RenderGraphicActionFactory::createRenderGraphicAction(
2663                                     pAct->GetRenderGraphic(),
2664                                     getState( rStates ).mapModeTransform *
2665                                     ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2666                                     getState( rStates ).mapModeTransform *
2667                                     ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2668                                     rCanvas,
2669                                     getState( rStates ) ) );
2670 
2671                         if( pRenderGraphicAction )
2672                         {
2673                             maActions.push_back(
2674                                 MtfAction(
2675                                     pRenderGraphicAction,
2676                                     io_rCurrActionIndex ) );
2677 
2678                             io_rCurrActionIndex += pRenderGraphicAction->getActionCount()-1;
2679                         }
2680                     }
2681                     break;
2682 
2683                     default:
2684                         OSL_ENSURE( false,
2685                                     "Unknown meta action type encountered" );
2686                         break;
2687                 }
2688 
2689                 // increment action index (each mtf action counts _at
2690                 // least_ one. Some count for more, therefore,
2691                 // io_rCurrActionIndex is sometimes incremented by
2692                 // pAct->getActionCount()-1 above, the -1 being the
2693                 // correction for the unconditional increment here).
2694                 ++io_rCurrActionIndex;
2695             }
2696 
2697             return true;
2698         }
2699 
2700 
2701         namespace
2702         {
2703             class ActionRenderer
2704             {
2705             public:
2706                 ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) :
2707                     maTransformation( rTransformation ),
2708                     mbRet( true )
2709                 {
2710                 }
2711 
2712                 bool result()
2713                 {
2714                     return mbRet;
2715                 }
2716 
2717                 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2718                 {
2719                     // ANDing the result. We want to fail if at least
2720                     // one action failed.
2721                     mbRet &= rAction.mpAction->render( maTransformation );
2722                 }
2723 
2724                 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction&	rAction,
2725                                  const Action::Subset&									rSubset )
2726                 {
2727                     // ANDing the result. We want to fail if at least
2728                     // one action failed.
2729                     mbRet &= rAction.mpAction->render( maTransformation,
2730                                                        rSubset );
2731                 }
2732 
2733             private:
2734                 ::basegfx::B2DHomMatrix	maTransformation;
2735                 bool					mbRet;
2736             };
2737 
2738             class AreaQuery
2739             {
2740             public:
2741                 AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) :
2742                     maTransformation( rTransformation ),
2743                     maBounds()
2744                 {
2745                 }
2746 
2747                 bool result()
2748                 {
2749                     return true; // nothing can fail here
2750                 }
2751 
2752                 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2753                 {
2754                     maBounds.expand( rAction.mpAction->getBounds( maTransformation ) );
2755                 }
2756 
2757                 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction&	rAction,
2758                                  const Action::Subset&									rSubset )
2759                 {
2760                     maBounds.expand( rAction.mpAction->getBounds( maTransformation,
2761                                                                   rSubset ) );
2762                 }
2763 
2764                 ::basegfx::B2DRange getBounds() const
2765                 {
2766                     return maBounds;
2767                 }
2768 
2769             private:
2770                 ::basegfx::B2DHomMatrix	maTransformation;
2771                 ::basegfx::B2DRange		maBounds;
2772             };
2773 
2774             // Doing that via inline class. Compilers tend to not inline free
2775             // functions.
2776             struct UpperBoundActionIndexComparator
2777             {
2778                 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS,
2779                                  const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS )
2780                 {
2781                     const sal_Int32 nLHSCount( rLHS.mpAction ?
2782                                                rLHS.mpAction->getActionCount() : 0 );
2783                     const sal_Int32 nRHSCount( rRHS.mpAction ?
2784                                                rRHS.mpAction->getActionCount() : 0 );
2785 
2786                     // compare end of action range, to have an action selected
2787                     // by lower_bound even if the requested index points in
2788                     // the middle of the action's range
2789                     return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount;
2790                 }
2791             };
2792 
2793             /** Algorithm to apply given functor to a subset range
2794 
2795             	@tpl Functor
2796 
2797                 Functor to call for each element of the subset
2798                 range. Must provide the following method signatures:
2799                 bool result() (returning false if operation failed)
2800 
2801              */
2802             template< typename Functor > bool
2803             	forSubsetRange( Functor& 											rFunctor,
2804                                 ImplRenderer::ActionVector::const_iterator			aRangeBegin,
2805                                 ImplRenderer::ActionVector::const_iterator			aRangeEnd,
2806                                 sal_Int32											nStartIndex,
2807                                 sal_Int32											nEndIndex,
2808                                 const ImplRenderer::ActionVector::const_iterator&	rEnd )
2809             {
2810                 if( aRangeBegin == aRangeEnd )
2811                 {
2812                     // only a single action. Setup subset, and call functor
2813                     Action::Subset aSubset;
2814                     aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2815                                                         nStartIndex - aRangeBegin->mnOrigIndex );
2816                     aSubset.mnSubsetEnd   = ::std::min( aRangeBegin->mpAction->getActionCount(),
2817                                                         nEndIndex - aRangeBegin->mnOrigIndex );
2818 
2819                     ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2820                                       "ImplRenderer::forSubsetRange(): Invalid indices" );
2821 
2822                     rFunctor( *aRangeBegin, aSubset );
2823                 }
2824                 else
2825                 {
2826                     // more than one action.
2827 
2828                     // render partial first, full intermediate, and
2829                     // partial last action
2830                     Action::Subset aSubset;
2831                     aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2832                                                         nStartIndex - aRangeBegin->mnOrigIndex );
2833                     aSubset.mnSubsetEnd   = aRangeBegin->mpAction->getActionCount();
2834 
2835                     ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2836                                       "ImplRenderer::forSubsetRange(): Invalid indices" );
2837 
2838                     rFunctor( *aRangeBegin, aSubset );
2839 
2840                     // first action rendered, skip to next
2841                     ++aRangeBegin;
2842 
2843                     // render full middle actions
2844                     while( aRangeBegin != aRangeEnd )
2845                         rFunctor( *aRangeBegin++ );
2846 
2847                     if( aRangeEnd == rEnd ||
2848                         aRangeEnd->mnOrigIndex > nEndIndex )
2849                     {
2850                         // aRangeEnd denotes end of action vector,
2851                         //
2852                         // or
2853                         //
2854                         // nEndIndex references something _after_
2855                         // aRangeBegin, but _before_ aRangeEnd
2856                         //
2857                         // either way: no partial action left
2858                         return rFunctor.result();
2859                     }
2860 
2861                     aSubset.mnSubsetBegin = 0;
2862                     aSubset.mnSubsetEnd   = nEndIndex - aRangeEnd->mnOrigIndex;
2863 
2864                     ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2865                                       "ImplRenderer::forSubsetRange(): Invalid indices" );
2866 
2867                     rFunctor( *aRangeEnd, aSubset );
2868                 }
2869 
2870                 return rFunctor.result();
2871             }
2872         }
2873 
2874         bool ImplRenderer::getSubsetIndices( sal_Int32&						io_rStartIndex,
2875                                              sal_Int32&						io_rEndIndex,
2876                                              ActionVector::const_iterator& 	o_rRangeBegin,
2877                                              ActionVector::const_iterator& 	o_rRangeEnd ) const
2878         {
2879             ENSURE_OR_RETURN_FALSE( io_rStartIndex<=io_rEndIndex,
2880                               "ImplRenderer::getSubsetIndices(): invalid action range" );
2881 
2882             ENSURE_OR_RETURN_FALSE( !maActions.empty(),
2883                               "ImplRenderer::getSubsetIndices(): no actions to render" );
2884 
2885             const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex );
2886             const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex +
2887                                              maActions.back().mpAction->getActionCount() );
2888 
2889             // clip given range to permissible values (there might be
2890             // ranges before and behind the valid indices)
2891             io_rStartIndex = ::std::max( nMinActionIndex,
2892                                          io_rStartIndex );
2893             io_rEndIndex = ::std::min( nMaxActionIndex,
2894                                        io_rEndIndex );
2895 
2896             if( io_rStartIndex == io_rEndIndex ||
2897                 io_rStartIndex > io_rEndIndex )
2898             {
2899 				// empty range, don't render anything. The second
2900 				// condition e.g. happens if the requested range lies
2901 				// fully before or behind the valid action indices.
2902                 return false;
2903             }
2904 
2905 
2906             const ActionVector::const_iterator aBegin( maActions.begin() );
2907             const ActionVector::const_iterator aEnd( maActions.end() );
2908 
2909 
2910             // find start and end action
2911             // =========================
2912             o_rRangeBegin = ::std::lower_bound( aBegin, aEnd,
2913                                                 MtfAction( ActionSharedPtr(), io_rStartIndex ),
2914                                                 UpperBoundActionIndexComparator() );
2915             o_rRangeEnd   = ::std::lower_bound( aBegin, aEnd,
2916                                                 MtfAction( ActionSharedPtr(), io_rEndIndex ),
2917                                                 UpperBoundActionIndexComparator() );
2918             return true;
2919         }
2920 
2921 
2922         // Public methods
2923         // ====================================================================
2924 
2925         ImplRenderer::ImplRenderer( const CanvasSharedPtr&	rCanvas,
2926                                     const GDIMetaFile&		rMtf,
2927                                     const Parameters&		rParams ) :
2928             CanvasGraphicHelper( rCanvas ),
2929             maActions()
2930         {
2931             RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2932 
2933             OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
2934                         "ImplRenderer::ImplRenderer(): Invalid canvas" );
2935             OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
2936                         "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2937 
2938             // make sure canvas and graphic device are valid; action
2939             // creation don't check that every time
2940             if( rCanvas.get() == NULL ||
2941                 !rCanvas->getUNOCanvas().is() ||
2942                 !rCanvas->getUNOCanvas()->getDevice().is() )
2943             {
2944                 // leave actions empty
2945                 return;
2946             }
2947 
2948             VectorOfOutDevStates	aStateStack;
2949 
2950             VirtualDevice aVDev;
2951             aVDev.EnableOutput( sal_False );
2952 
2953             // Setup VDev for state tracking and mapping
2954             // =========================================
2955 
2956             aVDev.SetMapMode( rMtf.GetPrefMapMode() );
2957 
2958             const Size aMtfSize( rMtf.GetPrefSize() );
2959             const Size aMtfSizePixPre( aVDev.LogicToPixel( aMtfSize,
2960                                                            rMtf.GetPrefMapMode() ) );
2961             const Point aEmptyPt;
2962             const Point aMtfOriginPix( aVDev.LogicToPixel( aEmptyPt ) );
2963 
2964             // #i44110# correct null-sized output - there are shapes
2965             // which have zero size in at least one dimension
2966             const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
2967                                     ::std::max( aMtfSizePixPre.Height(), 1L ) );
2968 
2969             sal_Int32 nCurrActions(0);
2970             ActionFactoryParameters aParms(aStateStack,
2971                                            rCanvas,
2972                                            aVDev,
2973                                            rParams,
2974                                            nCurrActions );
2975 
2976             // init state stack
2977             clearStateStack( aStateStack );
2978 
2979             // Setup local state, such that the metafile renders
2980             // itself into a one-by-one square at the origin for
2981             // identity view and render transformations
2982             getState( aStateStack ).transform.scale( 1.0 / aMtfSizePix.Width(),
2983                                                      1.0 / aMtfSizePix.Height() );
2984 
2985             tools::calcLogic2PixelAffineTransform( getState( aStateStack ).mapModeTransform,
2986                                                    aVDev );
2987 
2988             ColorSharedPtr pColor( getCanvas()->createColor() );
2989 
2990             // setup default text color to black
2991             getState( aStateStack ).textColor =
2992                 getState( aStateStack ).textFillColor =
2993                 getState( aStateStack ).textLineColor = pColor->getDeviceColor( 0x000000FF );
2994 
2995             // apply overrides from the Parameters struct
2996             if( rParams.maFillColor.is_initialized() )
2997             {
2998                 getState( aStateStack ).isFillColorSet = true;
2999                 getState( aStateStack ).fillColor = pColor->getDeviceColor( *rParams.maFillColor );
3000             }
3001             if( rParams.maLineColor.is_initialized() )
3002             {
3003                 getState( aStateStack ).isLineColorSet = true;
3004                 getState( aStateStack ).lineColor = pColor->getDeviceColor( *rParams.maLineColor );
3005             }
3006             if( rParams.maTextColor.is_initialized() )
3007             {
3008                 getState( aStateStack ).isTextFillColorSet = true;
3009                 getState( aStateStack ).isTextLineColorSet = true;
3010                 getState( aStateStack ).textColor =
3011                     getState( aStateStack ).textFillColor =
3012                     getState( aStateStack ).textLineColor = pColor->getDeviceColor( *rParams.maTextColor );
3013             }
3014             if( rParams.maFontName.is_initialized() ||
3015                 rParams.maFontWeight.is_initialized() ||
3016                 rParams.maFontLetterForm.is_initialized() ||
3017                 rParams.maFontUnderline.is_initialized()  ||
3018                 rParams.maFontProportion.is_initialized() )
3019             {
3020                 ::cppcanvas::internal::OutDevState& rState = getState( aStateStack );
3021 
3022                 rState.xFont = createFont( rState.fontRotation,
3023                                            ::Font(), // default font
3024                                            aParms );
3025             }
3026 
3027             createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2):
3028 								                           // we're
3029                         		                           // changing
3030                            		                           // the
3031                            		                           // current
3032                            		                           // action
3033                            		                           // in
3034                            		                           // createActions!
3035                            aParms,
3036                            true // TODO(P1): make subsettability configurable
3037                             );
3038         }
3039 
3040         ImplRenderer::ImplRenderer( const CanvasSharedPtr&	rCanvas,
3041                                     const BitmapEx&			rBmpEx,
3042                                     const Parameters&		rParams ) :
3043             CanvasGraphicHelper( rCanvas ),
3044             maActions()
3045         {
3046             // TODO(F3): property modification parameters are
3047             // currently ignored for Bitmaps
3048             (void)rParams;
3049 
3050             RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(bitmap)" );
3051 
3052             OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
3053                         "ImplRenderer::ImplRenderer(): Invalid canvas" );
3054             OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
3055                         "ImplRenderer::ImplRenderer(): Invalid graphic device" );
3056 
3057             // make sure canvas and graphic device are valid; action
3058             // creation don't check that every time
3059             if( rCanvas.get() == NULL ||
3060                 !rCanvas->getUNOCanvas().is() ||
3061                 !rCanvas->getUNOCanvas()->getDevice().is() )
3062             {
3063                 // leave actions empty
3064                 return;
3065             }
3066 
3067             OutDevState aState;
3068 
3069             const Size aBmpSize( rBmpEx.GetSizePixel() );
3070 
3071             // Setup local state, such that the bitmap renders itself
3072             // into a one-by-one square for identity view and render
3073             // transformations
3074             aState.transform.scale( 1.0 / aBmpSize.Width(),
3075                                     1.0 / aBmpSize.Height() );
3076 
3077             // create a single action for the provided BitmapEx
3078             maActions.push_back(
3079                 MtfAction(
3080                     BitmapActionFactory::createBitmapAction(
3081                         rBmpEx,
3082                         ::basegfx::B2DPoint(),
3083                         rCanvas,
3084                         aState),
3085                     0 ) );
3086         }
3087 
3088         ImplRenderer::~ImplRenderer()
3089         {
3090         }
3091 
3092         bool ImplRenderer::drawSubset( sal_Int32	nStartIndex,
3093                                        sal_Int32	nEndIndex ) const
3094         {
3095             RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::drawSubset()" );
3096 
3097             ActionVector::const_iterator aRangeBegin;
3098             ActionVector::const_iterator aRangeEnd;
3099 
3100             try
3101             {
3102                 if( !getSubsetIndices( nStartIndex, nEndIndex,
3103                                        aRangeBegin, aRangeEnd ) )
3104                     return true; // nothing to render (but _that_ was successful)
3105 
3106                 // now, aRangeBegin references the action in which the
3107                 // subset rendering must start, and aRangeEnd references
3108                 // the action in which the subset rendering 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
3111                 // rendered).
3112 
3113 
3114                 // render subset of actions
3115                 // ========================
3116 
3117                 ::basegfx::B2DHomMatrix aMatrix;
3118                 ::canvas::tools::getRenderStateTransform( aMatrix,
3119                                                           getRenderState() );
3120 
3121                 ActionRenderer aRenderer( aMatrix );
3122 
3123                 return forSubsetRange( aRenderer,
3124                                        aRangeBegin,
3125                                        aRangeEnd,
3126                                        nStartIndex,
3127                                        nEndIndex,
3128                                        maActions.end() );
3129             }
3130             catch( uno::Exception& )
3131             {
3132                 OSL_ENSURE( false,
3133                             rtl::OUStringToOString(
3134                                 comphelper::anyToString( cppu::getCaughtException() ),
3135                                 RTL_TEXTENCODING_UTF8 ).getStr() );
3136 
3137                 // convert error to return value
3138                 return false;
3139             }
3140         }
3141 
3142         ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32	nStartIndex,
3143                                                          sal_Int32	nEndIndex ) const
3144         {
3145             RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3146 
3147             ActionVector::const_iterator aRangeBegin;
3148             ActionVector::const_iterator aRangeEnd;
3149 
3150             if( !getSubsetIndices( nStartIndex, nEndIndex,
3151                                    aRangeBegin, aRangeEnd ) )
3152                 return ::basegfx::B2DRange(); // nothing to render -> empty range
3153 
3154             // now, aRangeBegin references the action in which the
3155             // subset querying must start, and aRangeEnd references
3156             // the action in which the subset querying must end (it
3157             // might also end right at the start of the referenced
3158             // action, such that zero of that action needs to be
3159             // queried).
3160 
3161 
3162             // query bounds for subset of actions
3163             // ==================================
3164 
3165             ::basegfx::B2DHomMatrix aMatrix;
3166             ::canvas::tools::getRenderStateTransform( aMatrix,
3167                                                       getRenderState() );
3168 
3169             AreaQuery aQuery( aMatrix );
3170             forSubsetRange( aQuery,
3171                             aRangeBegin,
3172                             aRangeEnd,
3173                             nStartIndex,
3174                             nEndIndex,
3175                             maActions.end() );
3176 
3177             return aQuery.getBounds();
3178         }
3179 
3180         bool ImplRenderer::draw() const
3181         {
3182             RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::draw()" );
3183 
3184             ::basegfx::B2DHomMatrix aMatrix;
3185             ::canvas::tools::getRenderStateTransform( aMatrix,
3186                                                       getRenderState() );
3187 
3188             try
3189             {
3190                 return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result();
3191             }
3192             catch( uno::Exception& )
3193             {
3194                 OSL_ENSURE( false,
3195                             rtl::OUStringToOString(
3196                                 comphelper::anyToString( cppu::getCaughtException() ),
3197                                 RTL_TEXTENCODING_UTF8 ).getStr() );
3198 
3199                 return false;
3200             }
3201         }
3202     }
3203 }
3204