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