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().HasPolyPolygonOrB2DPolyPolygon() )
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                                 basegfx::B2DPolyPolygon aPolyPolygon(pClipAction->GetRegion().GetAsB2DPolyPolygon());
1422 
1423                                 aPolyPolygon.transform(rVDev.GetViewTransformation());
1424                                 updateClipping(
1425                                     aPolyPolygon,
1426                                     rFactoryParms,
1427                                     false );
1428                             }
1429                         }
1430 
1431                         break;
1432                     }
1433 
1434                     case META_ISECTRECTCLIPREGION_ACTION:
1435                     {
1436                         MetaISectRectClipRegionAction* pClipAction = static_cast<MetaISectRectClipRegionAction*>(pCurrAct);
1437 
1438                         // #121806# explicitely kept integer
1439                         Rectangle aClipRect(
1440                             rVDev.LogicToPixel( pClipAction->GetRect() ) );
1441 
1442                         // intersect current clip with given rect
1443                         updateClipping(
1444                             aClipRect,
1445                             rFactoryParms,
1446                             true );
1447 
1448                         break;
1449                     }
1450 
1451                     case META_ISECTREGIONCLIPREGION_ACTION:
1452                     {
1453                         MetaISectRegionClipRegionAction* pClipAction = static_cast<MetaISectRegionClipRegionAction*>(pCurrAct);
1454 
1455                         if( !pClipAction->GetRegion().HasPolyPolygonOrB2DPolyPolygon() )
1456                         {
1457                             VERBOSE_TRACE( "ImplRenderer::createActions(): non-polygonal clip "
1458                                            "region encountered, falling back to bounding box!" );
1459 
1460                             // #121806# explicitely kept integer
1461                             Rectangle aClipRect(
1462                                 rVDev.LogicToPixel( pClipAction->GetRegion().GetBoundRect() ) );
1463 
1464                             // intersect current clip with given rect
1465                             updateClipping(
1466                                 aClipRect,
1467                                 rFactoryParms,
1468                                 true );
1469                         }
1470                         else
1471                         {
1472                             // intersect current clip with given clip polygon
1473 
1474                             // #121806# explicitely kept integer
1475                             basegfx::B2DPolyPolygon aPolyPolygon(pClipAction->GetRegion().GetAsB2DPolyPolygon());
1476 
1477                             aPolyPolygon.transform(rVDev.GetViewTransformation());
1478                             updateClipping(
1479                                 aPolyPolygon,
1480                                 rFactoryParms,
1481                                 true );
1482                         }
1483 
1484                         break;
1485                     }
1486 
1487                     case META_MOVECLIPREGION_ACTION:
1488                         // TODO(F2): NYI
1489                         break;
1490 
1491                     case META_LINECOLOR_ACTION:
1492                         if( !rParms.maLineColor.is_initialized() )
1493                         {
1494                             setStateColor( static_cast<MetaLineColorAction*>(pCurrAct),
1495                                            getState( rStates ).isLineColorSet,
1496                                            getState( rStates ).lineColor,
1497                                            rCanvas );
1498                         }
1499                         else
1500                         {
1501                             // #120994# Do switch on/off LineColor, even when a overriding one is set
1502                             bool bSetting(static_cast<MetaLineColorAction*>(pCurrAct)->IsSetting());
1503 
1504                             getState( rStates ).isLineColorSet = bSetting;
1505                         }
1506                         break;
1507 
1508                     case META_FILLCOLOR_ACTION:
1509                         if( !rParms.maFillColor.is_initialized() )
1510                         {
1511                             setStateColor( static_cast<MetaFillColorAction*>(pCurrAct),
1512                                            getState( rStates ).isFillColorSet,
1513                                            getState( rStates ).fillColor,
1514                                            rCanvas );
1515                         }
1516                         else
1517                         {
1518                             // #120994# Do switch on/off FillColor, even when a overriding one is set
1519                             bool bSetting(static_cast<MetaFillColorAction*>(pCurrAct)->IsSetting());
1520 
1521                             getState( rStates ).isFillColorSet = bSetting;
1522                         }
1523                         break;
1524 
1525                     case META_TEXTCOLOR_ACTION:
1526                     {
1527                         if( !rParms.maTextColor.is_initialized() )
1528                         {
1529                             // Text color is set unconditionally, thus, no
1530                             // use of setStateColor here
1531                             ::Color aColor( static_cast<MetaTextColorAction*>(pCurrAct)->GetColor() );
1532 
1533                             // force alpha part of color to
1534                             // opaque. transparent painting is done
1535                             // explicitely via META_TRANSPARENT_ACTION
1536                             aColor.SetTransparency(0);
1537 
1538                             getState( rStates ).textColor =
1539                                 ::vcl::unotools::colorToDoubleSequence(
1540                                     aColor,
1541                                     rCanvas->getUNOCanvas()->getDevice()->getDeviceColorSpace() );
1542                         }
1543                     }
1544                     break;
1545 
1546                     case META_TEXTFILLCOLOR_ACTION:
1547                         if( !rParms.maTextColor.is_initialized() )
1548                         {
1549                             setStateColor( static_cast<MetaTextFillColorAction*>(pCurrAct),
1550                                            getState( rStates ).isTextFillColorSet,
1551                                            getState( rStates ).textFillColor,
1552                                            rCanvas );
1553                         }
1554                         break;
1555 
1556                     case META_TEXTLINECOLOR_ACTION:
1557                         if( !rParms.maTextColor.is_initialized() )
1558                         {
1559                             setStateColor( static_cast<MetaTextLineColorAction*>(pCurrAct),
1560                                            getState( rStates ).isTextLineColorSet,
1561                                            getState( rStates ).textLineColor,
1562                                            rCanvas );
1563                         }
1564                         break;
1565 
1566                     case META_TEXTALIGN_ACTION:
1567                     {
1568                         ::cppcanvas::internal::OutDevState& rState = getState( rStates );
1569                         const TextAlign eTextAlign( static_cast<MetaTextAlignAction*>(pCurrAct)->GetTextAlign() );
1570 
1571                         rState.textReferencePoint = eTextAlign;
1572                     }
1573                     break;
1574 
1575                     case META_FONT_ACTION:
1576                     {
1577                         ::cppcanvas::internal::OutDevState& rState = getState( rStates );
1578                         const ::Font& rFont( static_cast<MetaFontAction*>(pCurrAct)->GetFont() );
1579 
1580                         rState.xFont = createFont( rState.fontRotation,
1581                                                    rFont,
1582                                                    rFactoryParms );
1583 
1584                         // TODO(Q2): define and use appropriate enumeration types
1585                         rState.textReliefStyle          = (sal_Int8)rFont.GetRelief();
1586                         rState.textOverlineStyle        = (sal_Int8)rFont.GetOverline();
1587                         rState.textUnderlineStyle       = rParms.maFontUnderline.is_initialized() ?
1588                             (*rParms.maFontUnderline ? (sal_Int8)UNDERLINE_SINGLE : (sal_Int8)UNDERLINE_NONE) :
1589                             (sal_Int8)rFont.GetUnderline();
1590                         rState.textStrikeoutStyle       = (sal_Int8)rFont.GetStrikeout();
1591                         rState.textEmphasisMarkStyle    = (sal_Int8)rFont.GetEmphasisMark();
1592                         rState.isTextEffectShadowSet    = (rFont.IsShadow() != sal_False);
1593                         rState.isTextWordUnderlineSet   = (rFont.IsWordLineMode() != sal_False);
1594                         rState.isTextOutlineModeSet     = (rFont.IsOutline() != sal_False);
1595                     }
1596                     break;
1597 
1598                     case META_RASTEROP_ACTION:
1599                         // TODO(F2): NYI
1600                         break;
1601 
1602                     case META_LAYOUTMODE_ACTION:
1603                     {
1604                         // TODO(F2): A lot is missing here
1605                         int nLayoutMode = static_cast<MetaLayoutModeAction*>(pCurrAct)->GetLayoutMode();
1606                         ::cppcanvas::internal::OutDevState& rState = getState( rStates );
1607                         switch( nLayoutMode & (TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_BIDI_STRONG) )
1608                         {
1609                             case TEXT_LAYOUT_BIDI_LTR:
1610                                 rState.textDirection = rendering::TextDirection::WEAK_LEFT_TO_RIGHT;
1611                                 break;
1612 
1613                             case (TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_BIDI_STRONG):
1614                                 rState.textDirection = rendering::TextDirection::STRONG_LEFT_TO_RIGHT;
1615                                 break;
1616 
1617                             case TEXT_LAYOUT_BIDI_RTL:
1618                                 rState.textDirection = rendering::TextDirection::WEAK_RIGHT_TO_LEFT;
1619                                 break;
1620 
1621                             case (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_BIDI_STRONG):
1622                                 rState.textDirection = rendering::TextDirection::STRONG_RIGHT_TO_LEFT;
1623                                 break;
1624                         }
1625 
1626                         rState.textAlignment = 0; // TODO(F2): rendering::TextAlignment::LEFT_ALIGNED;
1627                         if( (nLayoutMode & (TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_RIGHT) )
1628                             && !(nLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT ) )
1629                         {
1630                             rState.textAlignment = 1; // TODO(F2): rendering::TextAlignment::RIGHT_ALIGNED;
1631                         }
1632                     }
1633                     break;
1634 
1635                     // ------------------------------------------------------------
1636 
1637                     // In the second part of this monster-switch, we
1638                     // handle all recursing meta actions. These are the
1639                     // ones generating a metafile by themselves, which is
1640                     // then processed by recursively calling this method.
1641 
1642                     // ------------------------------------------------------------
1643 
1644                     case META_GRADIENT_ACTION:
1645                     {
1646                         MetaGradientAction* pGradAct = static_cast<MetaGradientAction*>(pCurrAct);
1647                         createGradientAction( ::Polygon( pGradAct->GetRect() ),
1648                                               pGradAct->GetGradient(),
1649                                               rFactoryParms,
1650                                               true,
1651                                               bSubsettableActions );
1652                     }
1653                     break;
1654 
1655                     case META_HATCH_ACTION:
1656                     {
1657                         // TODO(F2): use native Canvas hatches here
1658                         GDIMetaFile aTmpMtf;
1659 
1660                         rVDev.AddHatchActions( static_cast<MetaHatchAction*>(pCurrAct)->GetPolyPolygon(),
1661                                                static_cast<MetaHatchAction*>(pCurrAct)->GetHatch(),
1662                                                aTmpMtf );
1663                         createActions( aTmpMtf, rFactoryParms,
1664                                        bSubsettableActions );
1665                     }
1666                     break;
1667 
1668                     case META_EPS_ACTION:
1669                     {
1670                         MetaEPSAction* 		pAct = static_cast<MetaEPSAction*>(pCurrAct);
1671                         const GDIMetaFile&  rSubstitute = pAct->GetSubstitute();
1672 
1673                         // #121806# explicitely kept integer
1674                         const Size aMtfSize( rSubstitute.GetPrefSize() );
1675                         const Size aMtfSizePixPre( rVDev.LogicToPixel( aMtfSize,
1676                                                                        rSubstitute.GetPrefMapMode() ) );
1677 
1678                         // #i44110# correct null-sized output - there
1679                         // are metafiles which have zero size in at
1680                         // least one dimension
1681                         const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
1682                                                 ::std::max( aMtfSizePixPre.Height(), 1L ) );
1683 
1684                         // Setup local transform, such that the
1685                         // metafile renders itself into the given
1686                         // output rectangle
1687                         pushState( rStates, PUSH_ALL );
1688 
1689                         rVDev.Push();
1690                         rVDev.SetMapMode( rSubstitute.GetPrefMapMode() );
1691 
1692                         const ::Point& rPos( rVDev.LogicToPixel( pAct->GetPoint() ) );
1693                         const ::Size&  rSize( rVDev.LogicToPixel( pAct->GetSize() ) );
1694 
1695                         getState( rStates ).transform.translate( rPos.X(),
1696                                                                  rPos.Y() );
1697                         getState( rStates ).transform.scale( (double)rSize.Width() / aMtfSizePix.Width(),
1698                                                              (double)rSize.Height() / aMtfSizePix.Height() );
1699 
1700                         createActions( const_cast<GDIMetaFile&>(pAct->GetSubstitute()),
1701                                        rFactoryParms,
1702                                        bSubsettableActions );
1703 
1704                         rVDev.Pop();
1705                         popState( rStates );
1706                     }
1707                     break;
1708 
1709                     // handle metafile comments, to retrieve
1710                     // meta-information for gradients, fills and
1711                     // strokes. May skip actions, and may recurse.
1712                     case META_COMMENT_ACTION:
1713                     {
1714                         MetaCommentAction* pAct = static_cast<MetaCommentAction*>(pCurrAct);
1715 
1716                         // Handle gradients
1717                         if ( pAct->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_BEGIN" ) == COMPARE_EQUAL )
1718                         {
1719                             MetaGradientExAction* pGradAction = NULL;
1720                             bool bDone( false );
1721                             while( !bDone &&
1722                                    (pCurrAct=rMtf.NextAction()) != NULL )
1723                             {
1724                                 switch( pCurrAct->GetType() )
1725                                 {
1726                                     // extract gradient info
1727                                     case META_GRADIENTEX_ACTION:
1728                                         pGradAction = static_cast<MetaGradientExAction*>(pCurrAct);
1729                                         break;
1730 
1731                                     // skip broken-down rendering, output gradient when sequence is ended
1732                                     case META_COMMENT_ACTION:
1733                                         if( static_cast<MetaCommentAction*>(pCurrAct)->GetComment().CompareIgnoreCaseToAscii( "XGRAD_SEQ_END" ) == COMPARE_EQUAL )
1734                                         {
1735                                             bDone = true;
1736 
1737                                             if( pGradAction )
1738                                             {
1739                                                 createGradientAction( pGradAction->GetPolyPolygon(),
1740                                                                       pGradAction->GetGradient(),
1741                                                                       rFactoryParms,
1742                                                                       false,
1743                                                                       bSubsettableActions );
1744                                             }
1745                                         }
1746                                         break;
1747                                 }
1748                             }
1749                         }
1750                         // TODO(P2): Handle drawing layer strokes, via
1751                         // XPATHSTROKE_SEQ_BEGIN comment
1752 
1753                         // Handle drawing layer fills
1754                         else if( pAct->GetComment().Equals( "XPATHFILL_SEQ_BEGIN" ) )
1755                         {
1756                             const sal_uInt8* pData = pAct->GetData();
1757                             if ( pData )
1758                             {
1759                                 SvMemoryStream	aMemStm( (void*)pData, pAct->GetDataSize(), STREAM_READ );
1760 
1761                                 SvtGraphicFill aFill;
1762                                 aMemStm >> aFill;
1763 
1764                                 // TODO(P2): Also handle gradients and
1765                                 // hatches like this
1766 
1767                                 // only evaluate comment for pure
1768                                 // bitmap fills. If a transparency
1769                                 // gradient is involved (denoted by
1770                                 // the FloatTransparent action), take
1771                                 // the normal meta actions.
1772                                 if( aFill.getFillType() == SvtGraphicFill::fillTexture &&
1773                                     !isActionContained( rMtf,
1774                                                        "XPATHFILL_SEQ_END",
1775                                                         META_FLOATTRANSPARENT_ACTION ) )
1776                                 {
1777                                     rendering::Texture aTexture;
1778 
1779                                     // TODO(F1): the SvtGraphicFill
1780                                     // can also transport metafiles
1781                                     // here, handle that case, too
1782                                     Graphic	aGraphic;
1783                                     aFill.getGraphic( aGraphic );
1784 
1785                                     BitmapEx 	 aBmpEx( aGraphic.GetBitmapEx() );
1786                                     const ::Size aBmpSize( aBmpEx.GetSizePixel() );
1787 
1788                                     ::SvtGraphicFill::Transform aTransform;
1789                                     aFill.getTransform( aTransform );
1790 
1791                                     ::basegfx::B2DHomMatrix aMatrix;
1792 
1793                                     // convert to basegfx matrix
1794                                     aMatrix.set(0,0, aTransform.matrix[ 0 ] );
1795                                     aMatrix.set(0,1, aTransform.matrix[ 1 ] );
1796                                     aMatrix.set(0,2, aTransform.matrix[ 2 ] );
1797                                     aMatrix.set(1,0, aTransform.matrix[ 3 ] );
1798                                     aMatrix.set(1,1, aTransform.matrix[ 4 ] );
1799                                     aMatrix.set(1,2, aTransform.matrix[ 5 ] );
1800 
1801                                     ::basegfx::B2DHomMatrix aScale;
1802                                     aScale.scale( aBmpSize.Width(),
1803                                                   aBmpSize.Height() );
1804 
1805                                     // post-multiply with the bitmap
1806                                     // size (XCanvas' texture assumes
1807                                     // the given bitmap to be
1808                                     // normalized to [0,1]x[0,1]
1809                                     // rectangle)
1810                                     aMatrix = aMatrix * aScale;
1811 
1812                                     // pre-multiply with the
1813                                     // logic-to-pixel scale factor
1814                                     // (the metafile comment works in
1815                                     // logical coordinates).
1816                                     ::basegfx::B2DHomMatrix aLogic2PixelTransform;
1817                                     aMatrix *= tools::calcLogic2PixelLinearTransform( aLogic2PixelTransform,
1818                                                                                       rVDev );
1819 
1820                                     ::basegfx::unotools::affineMatrixFromHomMatrix(
1821                                         aTexture.AffineTransform,
1822                                         aMatrix );
1823 
1824                                     aTexture.Alpha = 1.0 - aFill.getTransparency();
1825                                     aTexture.Bitmap =
1826                                         ::vcl::unotools::xBitmapFromBitmapEx(
1827                                             rCanvas->getUNOCanvas()->getDevice(),
1828                                             aBmpEx );
1829                                     if( aFill.isTiling() )
1830                                     {
1831                                         aTexture.RepeatModeX = rendering::TexturingMode::REPEAT;
1832                                         aTexture.RepeatModeY = rendering::TexturingMode::REPEAT;
1833                                     }
1834                                     else
1835                                     {
1836                                         aTexture.RepeatModeX = rendering::TexturingMode::NONE;
1837                                         aTexture.RepeatModeY = rendering::TexturingMode::NONE;
1838                                     }
1839 
1840                                     ::PolyPolygon aPath;
1841                                     aFill.getPath( aPath );
1842 
1843                                     ::basegfx::B2DPolyPolygon aPoly( aPath.getB2DPolyPolygon() );
1844                                     aPoly.transform( getState( rStates ).mapModeTransform );
1845                                     ActionSharedPtr pPolyAction(
1846                                         internal::PolyPolyActionFactory::createPolyPolyAction(
1847                                             aPoly,
1848                                             rCanvas,
1849                                             getState( rStates ),
1850                                             aTexture ) );
1851 
1852                                     if( pPolyAction )
1853                                     {
1854                                         maActions.push_back(
1855                                             MtfAction(
1856                                                 pPolyAction,
1857                                                 io_rCurrActionIndex ) );
1858 
1859                                         io_rCurrActionIndex += pPolyAction->getActionCount()-1;
1860                                     }
1861 
1862                                     // skip broken-down render output
1863                                     skipContent( rMtf,
1864                                                  "XPATHFILL_SEQ_END",
1865                                                  io_rCurrActionIndex );
1866                                 }
1867                             }
1868                         }
1869                     }
1870                     break;
1871 
1872                     // ------------------------------------------------------------
1873 
1874                     // In the third part of this monster-switch, we
1875                     // handle all 'acting' meta actions. These are all
1876                     // processed by constructing function objects for
1877                     // them, which will later ease caching.
1878 
1879                     // ------------------------------------------------------------
1880 
1881                     case META_POINT_ACTION:
1882                     {
1883                         const OutDevState& rState( getState( rStates ) );
1884                         if( rState.lineColor.getLength() )
1885                         {
1886                             ActionSharedPtr pPointAction(
1887                                 internal::PointActionFactory::createPointAction(
1888                                     rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1889                                         static_cast<MetaPointAction*>(pCurrAct)->GetPoint() ),
1890                                     rCanvas,
1891                                     rState ) );
1892 
1893                             if( pPointAction )
1894                             {
1895                                 maActions.push_back(
1896                                     MtfAction(
1897                                         pPointAction,
1898                                         io_rCurrActionIndex ) );
1899 
1900                                 io_rCurrActionIndex += pPointAction->getActionCount()-1;
1901                             }
1902                         }
1903                     }
1904                     break;
1905 
1906                     case META_PIXEL_ACTION:
1907                     {
1908                         const OutDevState& rState( getState( rStates ) );
1909                         if( rState.lineColor.getLength() )
1910                         {
1911                             ActionSharedPtr pPointAction(
1912                                 internal::PointActionFactory::createPointAction(
1913                                     rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint(
1914                                         static_cast<MetaPixelAction*>(pCurrAct)->GetPoint() ),
1915                                     rCanvas,
1916                                     rState,
1917                                     static_cast<MetaPixelAction*>(pCurrAct)->GetColor() ) );
1918 
1919                             if( pPointAction )
1920                             {
1921                                 maActions.push_back(
1922                                     MtfAction(
1923                                         pPointAction,
1924                                         io_rCurrActionIndex ) );
1925 
1926                                 io_rCurrActionIndex += pPointAction->getActionCount()-1;
1927                             }
1928                         }
1929                     }
1930                     break;
1931 
1932                     case META_LINE_ACTION:
1933                     {
1934                         const OutDevState& rState( getState( rStates ) );
1935                         if( rState.lineColor.getLength() )
1936                         {
1937                             MetaLineAction* pLineAct = static_cast<MetaLineAction*>(pCurrAct);
1938 
1939                             const LineInfo& rLineInfo( pLineAct->GetLineInfo() );
1940 
1941                             const ::basegfx::B2DPoint aStartPoint(
1942                                 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetStartPoint() ));
1943                             const ::basegfx::B2DPoint aEndPoint(
1944                                 rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( pLineAct->GetEndPoint() ));
1945 
1946                             ActionSharedPtr pLineAction;
1947 
1948                             if( rLineInfo.IsDefault() )
1949                             {
1950                                 // plain hair line
1951                                 pLineAction =
1952                                     internal::LineActionFactory::createLineAction(
1953                                         aStartPoint,
1954                                         aEndPoint,
1955                                         rCanvas,
1956                                         rState );
1957 
1958                                 if( pLineAction )
1959                                 {
1960                                     maActions.push_back(
1961                                         MtfAction(
1962                                             pLineAction,
1963                                             io_rCurrActionIndex ) );
1964 
1965                                     io_rCurrActionIndex += pLineAction->getActionCount()-1;
1966                                 }
1967                             }
1968                             else if( LINE_NONE != rLineInfo.GetStyle() )
1969                             {
1970                                 // 'thick' line
1971                                 rendering::StrokeAttributes aStrokeAttributes;
1972 
1973                                 setupStrokeAttributes( aStrokeAttributes,
1974                                                        rFactoryParms,
1975                                                        rLineInfo );
1976 
1977                                 // XCanvas can only stroke polygons,
1978                                 // not simple lines - thus, handle
1979                                 // this case via the polypolygon
1980                                 // action
1981                                 ::basegfx::B2DPolygon aPoly;
1982                                 aPoly.append( aStartPoint );
1983                                 aPoly.append( aEndPoint );
1984                                 pLineAction =
1985                                     internal::PolyPolyActionFactory::createPolyPolyAction(
1986                                         ::basegfx::B2DPolyPolygon( aPoly ),
1987                                         rCanvas, rState, aStrokeAttributes );
1988 
1989                                 if( pLineAction )
1990                                 {
1991                                     maActions.push_back(
1992                                         MtfAction(
1993                                             pLineAction,
1994                                             io_rCurrActionIndex ) );
1995 
1996                                     io_rCurrActionIndex += pLineAction->getActionCount()-1;
1997                                 }
1998                             }
1999                             // else: line style is default
2000                             // (i.e. invisible), don't generate action
2001                         }
2002                     }
2003                     break;
2004 
2005                     case META_RECT_ACTION:
2006                     {
2007                         const Rectangle& rRect(
2008                             static_cast<MetaRectAction*>(pCurrAct)->GetRect() );
2009 
2010                         if( rRect.IsEmpty() )
2011                             break;
2012 
2013                         const OutDevState& rState( getState( rStates ) );
2014                         const ::basegfx::B2DPoint aTopLeftPixel(
2015                             rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ) );
2016                         const ::basegfx::B2DPoint aBottomRightPixel(
2017                             rState.mapModeTransform * ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
2018                             // #121100# OutputDevice::DrawRect() fills
2019                             // rectangles Apple-like, i.e. with one
2020                             // additional pixel to the right and bottom.
2021                             ::basegfx::B2DPoint(1,1) );
2022 
2023                         createFillAndStroke( ::basegfx::tools::createPolygonFromRect(
2024                                                  ::basegfx::B2DRange( aTopLeftPixel,
2025                                                                       aBottomRightPixel )),
2026                                              rFactoryParms );
2027                         break;
2028                     }
2029 
2030                     case META_ROUNDRECT_ACTION:
2031                     {
2032                         const Rectangle& rRect(
2033                             static_cast<MetaRoundRectAction*>(pCurrAct)->GetRect());
2034 
2035                         if( rRect.IsEmpty() )
2036                             break;
2037 
2038                         ::basegfx::B2DPolygon aPoly(
2039                             ::basegfx::tools::createPolygonFromRect(
2040                                 ::basegfx::B2DRange(
2041                                     ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
2042                                     ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
2043                                     ::basegfx::B2DPoint(1,1) ),
2044                                 static_cast<MetaRoundRectAction*>(pCurrAct)->GetHorzRound(),
2045                                 static_cast<MetaRoundRectAction*>(pCurrAct)->GetVertRound() ));
2046                         aPoly.transform( getState( rStates ).mapModeTransform );
2047 
2048                         createFillAndStroke( aPoly,
2049                                              rFactoryParms );
2050                     }
2051                     break;
2052 
2053                     case META_ELLIPSE_ACTION:
2054                     {
2055                         const Rectangle& rRect(
2056                             static_cast<MetaEllipseAction*>(pCurrAct)->GetRect() );
2057 
2058                         if( rRect.IsEmpty() )
2059                             break;
2060 
2061                         const ::basegfx::B2DRange aRange(
2062                             ::vcl::unotools::b2DPointFromPoint( rRect.TopLeft() ),
2063                             ::vcl::unotools::b2DPointFromPoint( rRect.BottomRight() ) +
2064                             ::basegfx::B2DPoint(1,1) );
2065 
2066                         ::basegfx::B2DPolygon aPoly(
2067                             ::basegfx::tools::createPolygonFromEllipse(
2068                                 aRange.getCenter(),
2069                                 aRange.getWidth(),
2070                                 aRange.getHeight() ));
2071                         aPoly.transform( getState( rStates ).mapModeTransform );
2072 
2073                         createFillAndStroke( aPoly,
2074                                              rFactoryParms );
2075                     }
2076                     break;
2077 
2078                     case META_ARC_ACTION:
2079                     {
2080                         // TODO(F1): Missing basegfx functionality. Mind empty rects!
2081                         const Polygon aToolsPoly( static_cast<MetaArcAction*>(pCurrAct)->GetRect(),
2082                                                   static_cast<MetaArcAction*>(pCurrAct)->GetStartPoint(),
2083                                                   static_cast<MetaArcAction*>(pCurrAct)->GetEndPoint(), POLY_ARC );
2084                         ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2085                         aPoly.transform( getState( rStates ).mapModeTransform );
2086 
2087                         createFillAndStroke( aPoly,
2088                                              rFactoryParms );
2089                     }
2090                     break;
2091 
2092                     case META_PIE_ACTION:
2093                     {
2094                         // TODO(F1): Missing basegfx functionality. Mind empty rects!
2095                         const Polygon aToolsPoly( static_cast<MetaPieAction*>(pCurrAct)->GetRect(),
2096                                                   static_cast<MetaPieAction*>(pCurrAct)->GetStartPoint(),
2097                                                   static_cast<MetaPieAction*>(pCurrAct)->GetEndPoint(), POLY_PIE );
2098                         ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2099                         aPoly.transform( getState( rStates ).mapModeTransform );
2100 
2101                         createFillAndStroke( aPoly,
2102                                              rFactoryParms );
2103                     }
2104                     break;
2105 
2106                     case META_CHORD_ACTION:
2107                     {
2108                         // TODO(F1): Missing basegfx functionality. Mind empty rects!
2109                         const Polygon aToolsPoly( static_cast<MetaChordAction*>(pCurrAct)->GetRect(),
2110                                                   static_cast<MetaChordAction*>(pCurrAct)->GetStartPoint(),
2111                                                   static_cast<MetaChordAction*>(pCurrAct)->GetEndPoint(), POLY_CHORD );
2112                         ::basegfx::B2DPolygon aPoly( aToolsPoly.getB2DPolygon() );
2113                         aPoly.transform( getState( rStates ).mapModeTransform );
2114 
2115                         createFillAndStroke( aPoly,
2116                                              rFactoryParms );
2117                     }
2118                     break;
2119 
2120                     case META_POLYLINE_ACTION:
2121                     {
2122                         const OutDevState& rState( getState( rStates ) );
2123                         if( rState.lineColor.getLength() ||
2124                             rState.fillColor.getLength() )
2125                         {
2126                             MetaPolyLineAction* pPolyLineAct = static_cast<MetaPolyLineAction*>(pCurrAct);
2127 
2128                             const LineInfo& rLineInfo( pPolyLineAct->GetLineInfo() );
2129                             ::basegfx::B2DPolygon aPoly( pPolyLineAct->GetPolygon().getB2DPolygon() );
2130                             aPoly.transform( rState.mapModeTransform );
2131 
2132                             ActionSharedPtr pLineAction;
2133 
2134                             if( rLineInfo.IsDefault() )
2135                             {
2136                                 // plain hair line polygon
2137                                 pLineAction =
2138                                     internal::PolyPolyActionFactory::createLinePolyPolyAction(
2139                                         ::basegfx::B2DPolyPolygon(aPoly),
2140                                         rCanvas,
2141                                         rState );
2142 
2143                                 if( pLineAction )
2144                                 {
2145                                     maActions.push_back(
2146                                         MtfAction(
2147                                             pLineAction,
2148                                             io_rCurrActionIndex ) );
2149 
2150                                     io_rCurrActionIndex += pLineAction->getActionCount()-1;
2151                                 }
2152                             }
2153                             else if( LINE_NONE != rLineInfo.GetStyle() )
2154                             {
2155                                 // 'thick' line polygon
2156                                 rendering::StrokeAttributes aStrokeAttributes;
2157 
2158                                 setupStrokeAttributes( aStrokeAttributes,
2159                                                        rFactoryParms,
2160                                                        rLineInfo );
2161 
2162                                 pLineAction =
2163                                     internal::PolyPolyActionFactory::createPolyPolyAction(
2164                                         ::basegfx::B2DPolyPolygon(aPoly),
2165                                         rCanvas,
2166                                         rState,
2167                                         aStrokeAttributes ) ;
2168 
2169                                 if( pLineAction )
2170                                 {
2171                                     maActions.push_back(
2172                                         MtfAction(
2173                                             pLineAction,
2174                                             io_rCurrActionIndex ) );
2175 
2176                                     io_rCurrActionIndex += pLineAction->getActionCount()-1;
2177                                 }
2178                             }
2179                             // else: line style is default
2180                             // (i.e. invisible), don't generate action
2181                         }
2182                     }
2183                     break;
2184 
2185                     case META_POLYGON_ACTION:
2186                     {
2187                         ::basegfx::B2DPolygon aPoly( static_cast<MetaPolygonAction*>(pCurrAct)->GetPolygon().getB2DPolygon() );
2188                         aPoly.transform( getState( rStates ).mapModeTransform );
2189                         createFillAndStroke( aPoly,
2190                                              rFactoryParms );
2191                     }
2192                     break;
2193 
2194                     case META_POLYPOLYGON_ACTION:
2195                     {
2196                         ::basegfx::B2DPolyPolygon aPoly( static_cast<MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon().getB2DPolyPolygon() );
2197                         aPoly.transform( getState( rStates ).mapModeTransform );
2198                         createFillAndStroke( aPoly,
2199                                              rFactoryParms );
2200                     }
2201                     break;
2202 
2203                     case META_BMP_ACTION:
2204                     {
2205                         MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pCurrAct);
2206 
2207                         ActionSharedPtr pBmpAction(
2208                                 internal::BitmapActionFactory::createBitmapAction(
2209                                     pAct->GetBitmap(),
2210                                     getState( rStates ).mapModeTransform *
2211                                     ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2212                                     rCanvas,
2213                                     getState( rStates ) ) );
2214 
2215                         if( pBmpAction )
2216                         {
2217                             maActions.push_back(
2218                                 MtfAction(
2219                                     pBmpAction,
2220                                     io_rCurrActionIndex ) );
2221 
2222                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2223                         }
2224                     }
2225                     break;
2226 
2227                     case META_BMPSCALE_ACTION:
2228                     {
2229                         MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pCurrAct);
2230 
2231                         ActionSharedPtr pBmpAction(
2232                                 internal::BitmapActionFactory::createBitmapAction(
2233                                     pAct->GetBitmap(),
2234                                     getState( rStates ).mapModeTransform *
2235                                     ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2236                                     getState( rStates ).mapModeTransform *
2237                                     ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2238                                     rCanvas,
2239                                     getState( rStates ) ) );
2240 
2241                         if( pBmpAction )
2242                         {
2243                             maActions.push_back(
2244                                 MtfAction(
2245                                     pBmpAction,
2246                                     io_rCurrActionIndex ) );
2247 
2248                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2249                         }
2250                     }
2251                     break;
2252 
2253                     case META_BMPSCALEPART_ACTION:
2254                     {
2255                         MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pCurrAct);
2256 
2257                         // crop bitmap to given source rectangle (no
2258                         // need to copy and convert the whole bitmap)
2259                         Bitmap aBmp( pAct->GetBitmap() );
2260                         const Rectangle aCropRect( pAct->GetSrcPoint(),
2261                                                     pAct->GetSrcSize() );
2262                         aBmp.Crop( aCropRect );
2263 
2264                         ActionSharedPtr pBmpAction(
2265                                 internal::BitmapActionFactory::createBitmapAction(
2266                                     aBmp,
2267                                     getState( rStates ).mapModeTransform *
2268                                     ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2269                                     getState( rStates ).mapModeTransform *
2270                                     ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2271                                     rCanvas,
2272                                     getState( rStates ) ) );
2273 
2274                         if( pBmpAction )
2275                         {
2276                             maActions.push_back(
2277                                 MtfAction(
2278                                     pBmpAction,
2279                                     io_rCurrActionIndex ) );
2280 
2281                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2282                         }
2283                     }
2284                     break;
2285 
2286                     case META_BMPEX_ACTION:
2287                     {
2288                         MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pCurrAct);
2289 
2290                         ActionSharedPtr pBmpAction(
2291                                 internal::BitmapActionFactory::createBitmapAction(
2292                                     pAct->GetBitmapEx(),
2293                                     getState( rStates ).mapModeTransform *
2294                                     ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2295                                     rCanvas,
2296                                     getState( rStates ) ) );
2297 
2298                         if( pBmpAction )
2299                         {
2300                             maActions.push_back(
2301                                 MtfAction(
2302                                     pBmpAction,
2303                                     io_rCurrActionIndex ) );
2304 
2305                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2306                         }
2307                     }
2308                     break;
2309 
2310                     case META_BMPEXSCALE_ACTION:
2311                     {
2312                         MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pCurrAct);
2313 
2314                         ActionSharedPtr pBmpAction(
2315                                 internal::BitmapActionFactory::createBitmapAction(
2316                                     pAct->GetBitmapEx(),
2317                                     getState( rStates ).mapModeTransform *
2318                                     ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2319                                     getState( rStates ).mapModeTransform *
2320                                     ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2321                                     rCanvas,
2322                                     getState( rStates ) ) );
2323 
2324                         if( pBmpAction )
2325                         {
2326                             maActions.push_back(
2327                                 MtfAction(
2328                                     pBmpAction,
2329                                     io_rCurrActionIndex ) );
2330 
2331                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2332                         }
2333                     }
2334                     break;
2335 
2336                     case META_BMPEXSCALEPART_ACTION:
2337                     {
2338                         MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pCurrAct);
2339 
2340                         // crop bitmap to given source rectangle (no
2341                         // need to copy and convert the whole bitmap)
2342                         BitmapEx aBmp( pAct->GetBitmapEx() );
2343                         const Rectangle aCropRect( pAct->GetSrcPoint(),
2344                                                    pAct->GetSrcSize() );
2345                         aBmp.Crop( aCropRect );
2346 
2347                         ActionSharedPtr pBmpAction(
2348                             internal::BitmapActionFactory::createBitmapAction(
2349                                 aBmp,
2350                                 getState( rStates ).mapModeTransform *
2351                                 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2352                                 getState( rStates ).mapModeTransform *
2353                                 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2354                                 rCanvas,
2355                                 getState( rStates ) ) );
2356 
2357                         if( pBmpAction )
2358                         {
2359                             maActions.push_back(
2360                                 MtfAction(
2361                                     pBmpAction,
2362                                     io_rCurrActionIndex ) );
2363 
2364                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2365                         }
2366                     }
2367                     break;
2368 
2369                     case META_MASK_ACTION:
2370                     {
2371                         MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pCurrAct);
2372 
2373                         // create masked BitmapEx right here, as the
2374                         // canvas does not provide equivalent
2375                         // functionality
2376                         BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2377                                                         pAct->GetColor() ));
2378 
2379                         ActionSharedPtr pBmpAction(
2380                             internal::BitmapActionFactory::createBitmapAction(
2381                                 aBmp,
2382                                 getState( rStates ).mapModeTransform *
2383                                 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2384                                 rCanvas,
2385                                 getState( rStates ) ) );
2386 
2387                         if( pBmpAction )
2388                         {
2389                             maActions.push_back(
2390                                 MtfAction(
2391                                     pBmpAction,
2392                                     io_rCurrActionIndex ) );
2393 
2394                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2395                         }
2396                     }
2397                     break;
2398 
2399                     case META_MASKSCALE_ACTION:
2400                     {
2401                         MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pCurrAct);
2402 
2403                         // create masked BitmapEx right here, as the
2404                         // canvas does not provide equivalent
2405                         // functionality
2406                         BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2407                                                         pAct->GetColor() ));
2408 
2409                         ActionSharedPtr pBmpAction(
2410                             internal::BitmapActionFactory::createBitmapAction(
2411                                 aBmp,
2412                                 getState( rStates ).mapModeTransform *
2413                                 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2414                                 getState( rStates ).mapModeTransform *
2415                                 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2416                                 rCanvas,
2417                                 getState( rStates ) ) );
2418 
2419                         if( pBmpAction )
2420                         {
2421                             maActions.push_back(
2422                                 MtfAction(
2423                                     pBmpAction,
2424                                     io_rCurrActionIndex ) );
2425 
2426                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2427                         }
2428                     }
2429                     break;
2430 
2431                     case META_MASKSCALEPART_ACTION:
2432                     {
2433                         MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pCurrAct);
2434 
2435                         // create masked BitmapEx right here, as the
2436                         // canvas does not provide equivalent
2437                         // functionality
2438                         BitmapEx aBmp( createMaskBmpEx( pAct->GetBitmap(),
2439                                                         pAct->GetColor() ));
2440 
2441                         // crop bitmap to given source rectangle (no
2442                         // need to copy and convert the whole bitmap)
2443                         const Rectangle aCropRect( pAct->GetSrcPoint(),
2444                                                    pAct->GetSrcSize() );
2445                         aBmp.Crop( aCropRect );
2446 
2447                         ActionSharedPtr pBmpAction(
2448                             internal::BitmapActionFactory::createBitmapAction(
2449                                 aBmp,
2450                                 getState( rStates ).mapModeTransform *
2451                                 ::vcl::unotools::b2DPointFromPoint( pAct->GetDestPoint() ),
2452                                 getState( rStates ).mapModeTransform *
2453                                 ::vcl::unotools::b2DSizeFromSize( pAct->GetDestSize() ),
2454                                 rCanvas,
2455                                 getState( rStates ) ) );
2456 
2457                         if( pBmpAction )
2458                         {
2459                             maActions.push_back(
2460                                 MtfAction(
2461                                     pBmpAction,
2462                                     io_rCurrActionIndex ) );
2463 
2464                             io_rCurrActionIndex += pBmpAction->getActionCount()-1;
2465                         }
2466                     }
2467                     break;
2468 
2469                     case META_GRADIENTEX_ACTION:
2470                         // TODO(F1): use native Canvas gradients here
2471                         // action is ignored here, because redundant to META_GRADIENT_ACTION
2472                         break;
2473 
2474                     case META_WALLPAPER_ACTION:
2475                         // TODO(F2): NYI
2476                         break;
2477 
2478                     case META_TRANSPARENT_ACTION:
2479                     {
2480                         const OutDevState& rState( getState( rStates ) );
2481                         if( rState.lineColor.getLength() ||
2482                             rState.fillColor.getLength() )
2483                         {
2484                             MetaTransparentAction* pAct = static_cast<MetaTransparentAction*>(pCurrAct);
2485                             ::basegfx::B2DPolyPolygon aPoly( pAct->GetPolyPolygon().getB2DPolyPolygon() );
2486                             aPoly.transform( rState.mapModeTransform );
2487 
2488                             ActionSharedPtr pPolyAction(
2489                                 internal::PolyPolyActionFactory::createPolyPolyAction(
2490                                     aPoly,
2491                                     rCanvas,
2492                                     rState,
2493                                     pAct->GetTransparence() ) );
2494 
2495                             if( pPolyAction )
2496                             {
2497                                 maActions.push_back(
2498                                     MtfAction(
2499                                         pPolyAction,
2500                                         io_rCurrActionIndex ) );
2501 
2502                                 io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2503                             }
2504                         }
2505                     }
2506                     break;
2507 
2508                     case META_FLOATTRANSPARENT_ACTION:
2509                     {
2510                         MetaFloatTransparentAction* pAct = static_cast<MetaFloatTransparentAction*>(pCurrAct);
2511 
2512                         internal::MtfAutoPtr pMtf(
2513                             new ::GDIMetaFile( pAct->GetGDIMetaFile() ) );
2514 
2515                         // TODO(P2): Use native canvas gradients here (saves a lot of UNO calls)
2516                         internal::GradientAutoPtr pGradient(
2517                             new Gradient( pAct->GetGradient() ) );
2518 
2519                         DBG_TESTSOLARMUTEX();
2520 
2521                         ActionSharedPtr pFloatTransAction(
2522                             internal::TransparencyGroupActionFactory::createTransparencyGroupAction(
2523                                 pMtf,
2524                                 pGradient,
2525                                 rParms,
2526                                 getState( rStates ).mapModeTransform *
2527                                 ::vcl::unotools::b2DPointFromPoint( pAct->GetPoint() ),
2528                                 getState( rStates ).mapModeTransform *
2529                                 ::vcl::unotools::b2DSizeFromSize( pAct->GetSize() ),
2530                                 rCanvas,
2531                                 getState( rStates ) ) );
2532 
2533                         if( pFloatTransAction )
2534                         {
2535                             maActions.push_back(
2536                                 MtfAction(
2537                                     pFloatTransAction,
2538                                     io_rCurrActionIndex ) );
2539 
2540                             io_rCurrActionIndex += pFloatTransAction->getActionCount()-1;
2541                         }
2542                     }
2543                     break;
2544 
2545                     case META_TEXT_ACTION:
2546                     {
2547                         MetaTextAction* pAct = static_cast<MetaTextAction*>(pCurrAct);
2548                         XubString sText = XubString( pAct->GetText() );
2549 
2550                         if( rVDev.GetDigitLanguage())
2551                             convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2552 
2553                         createTextAction(
2554                             pAct->GetPoint(),
2555                             sText,
2556                             pAct->GetIndex(),
2557                             pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(),
2558                             NULL,
2559                             rFactoryParms,
2560                             bSubsettableActions );
2561                     }
2562                     break;
2563 
2564                     case META_TEXTARRAY_ACTION:
2565                     {
2566                         MetaTextArrayAction* pAct = static_cast<MetaTextArrayAction*>(pCurrAct);
2567                         XubString sText = XubString( pAct->GetText() );
2568 
2569                         if( rVDev.GetDigitLanguage())
2570                             convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2571 
2572                         createTextAction(
2573                             pAct->GetPoint(),
2574                             sText,
2575                             pAct->GetIndex(),
2576                             pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(),
2577                             pAct->GetDXArray(),
2578                             rFactoryParms,
2579                             bSubsettableActions );
2580                     }
2581                     break;
2582 
2583                     case META_TEXTLINE_ACTION:
2584                     {
2585                         MetaTextLineAction*      pAct = static_cast<MetaTextLineAction*>(pCurrAct);
2586 
2587                         const OutDevState&       rState( getState( rStates ) );
2588                         const ::Size             aBaselineOffset( tools::getBaselineOffset( rState,
2589                                                                                             rVDev ) );
2590                         const ::Point 		     aStartPoint( pAct->GetStartPoint() );
2591                         const ::basegfx::B2DSize aSize( rState.mapModeTransform *
2592                                                         ::basegfx::B2DSize(pAct->GetWidth(),
2593                                                                            0 ));
2594 
2595                         ActionSharedPtr pPolyAction(
2596                             PolyPolyActionFactory::createPolyPolyAction(
2597                                 tools::createTextLinesPolyPolygon(
2598                                     rState.mapModeTransform *
2599                                     ::basegfx::B2DPoint(
2600                                         ::vcl::unotools::b2DPointFromPoint(pAct->GetStartPoint()) +
2601                                         ::vcl::unotools::b2DSizeFromSize(aBaselineOffset)),
2602                                     aSize.getX(),
2603                                     tools::createTextLineInfo( rVDev,
2604                                                                rState )),
2605                                 rCanvas,
2606                                 rState ) );
2607 
2608                         if( pPolyAction.get() )
2609                         {
2610                             maActions.push_back(
2611                                 MtfAction(
2612                                     pPolyAction,
2613                                     io_rCurrActionIndex ) );
2614 
2615                             io_rCurrActionIndex += pPolyAction->getActionCount()-1;
2616                         }
2617                     }
2618                     break;
2619 
2620                     case META_TEXTRECT_ACTION:
2621                     {
2622                         MetaTextRectAction* pAct = static_cast<MetaTextRectAction*>(pCurrAct);
2623 
2624                         pushState( rStates, PUSH_ALL );
2625 
2626                         // use the VDev to break up the text rect
2627                         // action into readily formatted lines
2628                         GDIMetaFile aTmpMtf;
2629                         rVDev.AddTextRectActions( pAct->GetRect(),
2630                                                   pAct->GetText(),
2631                                                   pAct->GetStyle(),
2632                                                   aTmpMtf );
2633 
2634                         createActions( aTmpMtf,
2635                                        rFactoryParms,
2636                                        bSubsettableActions );
2637 
2638                         popState( rStates );
2639 
2640                         break;
2641                     }
2642 
2643                     case META_STRETCHTEXT_ACTION:
2644                     {
2645                         MetaStretchTextAction* pAct = static_cast<MetaStretchTextAction*>(pCurrAct);
2646                         XubString sText = XubString( pAct->GetText() );
2647 
2648                         if( rVDev.GetDigitLanguage())
2649                             convertToLocalizedNumerals ( sText,rVDev.GetDigitLanguage() );
2650 
2651                         const sal_uInt16 nLen( pAct->GetLen() == (sal_uInt16)STRING_LEN ?
2652                                            pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen() );
2653 
2654                         // #i70897# Nothing to do, actually...
2655                         if( nLen == 0 )
2656                             break;
2657 
2658                         // have to fit the text into the given
2659                         // width. This is achieved by internally
2660                         // generating a DX array, and uniformly
2661                         // distributing the excess/insufficient width
2662                         // to every logical character.
2663                         ::boost::scoped_array< sal_Int32 > pDXArray( new sal_Int32[nLen] );
2664 
2665                         rVDev.GetTextArray( pAct->GetText(), pDXArray.get(),
2666                                             pAct->GetIndex(), pAct->GetLen() );
2667 
2668                         const sal_Int32 nWidthDifference( pAct->GetWidth() - pDXArray[ nLen-1 ] );
2669 
2670                         // Last entry of pDXArray contains total width of the text
2671                         sal_Int32* p=pDXArray.get();
2672                         for( sal_uInt16 i=1; i<=nLen; ++i )
2673                         {
2674                             // calc ratio for every array entry, to
2675                             // distribute rounding errors 'evenly'
2676                             // across the characters. Note that each
2677                             // entry represents the 'end' position of
2678                             // the corresponding character, thus, we
2679                             // let i run from 1 to nLen.
2680                             *p++ += (sal_Int32)i*nWidthDifference/nLen;
2681                         }
2682 
2683                         createTextAction(
2684                             pAct->GetPoint(),
2685                             sText,
2686                             pAct->GetIndex(),
2687                             pAct->GetLen() == (sal_uInt16)STRING_LEN ? pAct->GetText().Len() - pAct->GetIndex() : pAct->GetLen(),
2688                             pDXArray.get(),
2689                             rFactoryParms,
2690                             bSubsettableActions );
2691                     }
2692                     break;
2693 
2694                     default:
2695                         OSL_ENSURE( false,
2696                                     "Unknown meta action type encountered" );
2697                         break;
2698                 }
2699 
2700                 // increment action index (each mtf action counts _at
2701                 // least_ one. Some count for more, therefore,
2702                 // io_rCurrActionIndex is sometimes incremented by
2703                 // pAct->getActionCount()-1 above, the -1 being the
2704                 // correction for the unconditional increment here).
2705                 ++io_rCurrActionIndex;
2706             }
2707 
2708             return true;
2709         }
2710 
2711 
2712         namespace
2713         {
2714             class ActionRenderer
2715             {
2716             public:
2717                 ActionRenderer( const ::basegfx::B2DHomMatrix& rTransformation ) :
2718                     maTransformation( rTransformation ),
2719                     mbRet( true )
2720                 {
2721                 }
2722 
2723                 bool result()
2724                 {
2725                     return mbRet;
2726                 }
2727 
2728                 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2729                 {
2730                     // ANDing the result. We want to fail if at least
2731                     // one action failed.
2732                     mbRet &= rAction.mpAction->render( maTransformation );
2733                 }
2734 
2735                 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction&	rAction,
2736                                  const Action::Subset&									rSubset )
2737                 {
2738                     // ANDing the result. We want to fail if at least
2739                     // one action failed.
2740                     mbRet &= rAction.mpAction->render( maTransformation,
2741                                                        rSubset );
2742                 }
2743 
2744             private:
2745                 ::basegfx::B2DHomMatrix	maTransformation;
2746                 bool					mbRet;
2747             };
2748 
2749             class AreaQuery
2750             {
2751             public:
2752                 AreaQuery( const ::basegfx::B2DHomMatrix& rTransformation ) :
2753                     maTransformation( rTransformation ),
2754                     maBounds()
2755                 {
2756                 }
2757 
2758                 bool result()
2759                 {
2760                     return true; // nothing can fail here
2761                 }
2762 
2763                 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rAction )
2764                 {
2765                     maBounds.expand( rAction.mpAction->getBounds( maTransformation ) );
2766                 }
2767 
2768                 void operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction&	rAction,
2769                                  const Action::Subset&									rSubset )
2770                 {
2771                     maBounds.expand( rAction.mpAction->getBounds( maTransformation,
2772                                                                   rSubset ) );
2773                 }
2774 
2775                 ::basegfx::B2DRange getBounds() const
2776                 {
2777                     return maBounds;
2778                 }
2779 
2780             private:
2781                 ::basegfx::B2DHomMatrix	maTransformation;
2782                 ::basegfx::B2DRange		maBounds;
2783             };
2784 
2785             // Doing that via inline class. Compilers tend to not inline free
2786             // functions.
2787             struct UpperBoundActionIndexComparator
2788             {
2789                 bool operator()( const ::cppcanvas::internal::ImplRenderer::MtfAction& rLHS,
2790                                  const ::cppcanvas::internal::ImplRenderer::MtfAction& rRHS )
2791                 {
2792                     const sal_Int32 nLHSCount( rLHS.mpAction ?
2793                                                rLHS.mpAction->getActionCount() : 0 );
2794                     const sal_Int32 nRHSCount( rRHS.mpAction ?
2795                                                rRHS.mpAction->getActionCount() : 0 );
2796 
2797                     // compare end of action range, to have an action selected
2798                     // by lower_bound even if the requested index points in
2799                     // the middle of the action's range
2800                     return rLHS.mnOrigIndex + nLHSCount < rRHS.mnOrigIndex + nRHSCount;
2801                 }
2802             };
2803 
2804             /** Algorithm to apply given functor to a subset range
2805 
2806             	@tpl Functor
2807 
2808                 Functor to call for each element of the subset
2809                 range. Must provide the following method signatures:
2810                 bool result() (returning false if operation failed)
2811 
2812              */
2813             template< typename Functor > bool
2814             	forSubsetRange( Functor& 											rFunctor,
2815                                 ImplRenderer::ActionVector::const_iterator			aRangeBegin,
2816                                 ImplRenderer::ActionVector::const_iterator			aRangeEnd,
2817                                 sal_Int32											nStartIndex,
2818                                 sal_Int32											nEndIndex,
2819                                 const ImplRenderer::ActionVector::const_iterator&	rEnd )
2820             {
2821                 if( aRangeBegin == aRangeEnd )
2822                 {
2823                     // only a single action. Setup subset, and call functor
2824                     Action::Subset aSubset;
2825                     aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2826                                                         nStartIndex - aRangeBegin->mnOrigIndex );
2827                     aSubset.mnSubsetEnd   = ::std::min( aRangeBegin->mpAction->getActionCount(),
2828                                                         nEndIndex - aRangeBegin->mnOrigIndex );
2829 
2830                     ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2831                                       "ImplRenderer::forSubsetRange(): Invalid indices" );
2832 
2833                     rFunctor( *aRangeBegin, aSubset );
2834                 }
2835                 else
2836                 {
2837                     // more than one action.
2838 
2839                     // render partial first, full intermediate, and
2840                     // partial last action
2841                     Action::Subset aSubset;
2842                     aSubset.mnSubsetBegin = ::std::max( sal_Int32( 0 ),
2843                                                         nStartIndex - aRangeBegin->mnOrigIndex );
2844                     aSubset.mnSubsetEnd   = aRangeBegin->mpAction->getActionCount();
2845 
2846                     ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2847                                       "ImplRenderer::forSubsetRange(): Invalid indices" );
2848 
2849                     rFunctor( *aRangeBegin, aSubset );
2850 
2851                     // first action rendered, skip to next
2852                     ++aRangeBegin;
2853 
2854                     // render full middle actions
2855                     while( aRangeBegin != aRangeEnd )
2856                         rFunctor( *aRangeBegin++ );
2857 
2858                     if( aRangeEnd == rEnd ||
2859                         aRangeEnd->mnOrigIndex > nEndIndex )
2860                     {
2861                         // aRangeEnd denotes end of action vector,
2862                         //
2863                         // or
2864                         //
2865                         // nEndIndex references something _after_
2866                         // aRangeBegin, but _before_ aRangeEnd
2867                         //
2868                         // either way: no partial action left
2869                         return rFunctor.result();
2870                     }
2871 
2872                     aSubset.mnSubsetBegin = 0;
2873                     aSubset.mnSubsetEnd   = nEndIndex - aRangeEnd->mnOrigIndex;
2874 
2875                     ENSURE_OR_RETURN_FALSE( aSubset.mnSubsetBegin >= 0 && aSubset.mnSubsetEnd >= 0,
2876                                       "ImplRenderer::forSubsetRange(): Invalid indices" );
2877 
2878                     rFunctor( *aRangeEnd, aSubset );
2879                 }
2880 
2881                 return rFunctor.result();
2882             }
2883         }
2884 
2885         bool ImplRenderer::getSubsetIndices( sal_Int32&						io_rStartIndex,
2886                                              sal_Int32&						io_rEndIndex,
2887                                              ActionVector::const_iterator& 	o_rRangeBegin,
2888                                              ActionVector::const_iterator& 	o_rRangeEnd ) const
2889         {
2890             ENSURE_OR_RETURN_FALSE( io_rStartIndex<=io_rEndIndex,
2891                               "ImplRenderer::getSubsetIndices(): invalid action range" );
2892 
2893             ENSURE_OR_RETURN_FALSE( !maActions.empty(),
2894                               "ImplRenderer::getSubsetIndices(): no actions to render" );
2895 
2896             const sal_Int32 nMinActionIndex( maActions.front().mnOrigIndex );
2897             const sal_Int32 nMaxActionIndex( maActions.back().mnOrigIndex +
2898                                              maActions.back().mpAction->getActionCount() );
2899 
2900             // clip given range to permissible values (there might be
2901             // ranges before and behind the valid indices)
2902             io_rStartIndex = ::std::max( nMinActionIndex,
2903                                          io_rStartIndex );
2904             io_rEndIndex = ::std::min( nMaxActionIndex,
2905                                        io_rEndIndex );
2906 
2907             if( io_rStartIndex == io_rEndIndex ||
2908                 io_rStartIndex > io_rEndIndex )
2909             {
2910 				// empty range, don't render anything. The second
2911 				// condition e.g. happens if the requested range lies
2912 				// fully before or behind the valid action indices.
2913                 return false;
2914             }
2915 
2916 
2917             const ActionVector::const_iterator aBegin( maActions.begin() );
2918             const ActionVector::const_iterator aEnd( maActions.end() );
2919 
2920 
2921             // find start and end action
2922             // =========================
2923             o_rRangeBegin = ::std::lower_bound( aBegin, aEnd,
2924                                                 MtfAction( ActionSharedPtr(), io_rStartIndex ),
2925                                                 UpperBoundActionIndexComparator() );
2926             o_rRangeEnd   = ::std::lower_bound( aBegin, aEnd,
2927                                                 MtfAction( ActionSharedPtr(), io_rEndIndex ),
2928                                                 UpperBoundActionIndexComparator() );
2929             return true;
2930         }
2931 
2932 
2933         // Public methods
2934         // ====================================================================
2935 
2936         ImplRenderer::ImplRenderer( const CanvasSharedPtr&	rCanvas,
2937                                     const GDIMetaFile&		rMtf,
2938                                     const Parameters&		rParams ) :
2939             CanvasGraphicHelper( rCanvas ),
2940             maActions()
2941         {
2942             RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(mtf)" );
2943 
2944             OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
2945                         "ImplRenderer::ImplRenderer(): Invalid canvas" );
2946             OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
2947                         "ImplRenderer::ImplRenderer(): Invalid graphic device" );
2948 
2949             // make sure canvas and graphic device are valid; action
2950             // creation don't check that every time
2951             if( rCanvas.get() == NULL ||
2952                 !rCanvas->getUNOCanvas().is() ||
2953                 !rCanvas->getUNOCanvas()->getDevice().is() )
2954             {
2955                 // leave actions empty
2956                 return;
2957             }
2958 
2959             VectorOfOutDevStates	aStateStack;
2960 
2961             VirtualDevice aVDev;
2962             aVDev.EnableOutput( sal_False );
2963 
2964             // Setup VDev for state tracking and mapping
2965             // =========================================
2966 
2967             aVDev.SetMapMode( rMtf.GetPrefMapMode() );
2968 
2969             const Size aMtfSize( rMtf.GetPrefSize() );
2970             const Size aMtfSizePixPre( aVDev.LogicToPixel( aMtfSize,
2971                                                            rMtf.GetPrefMapMode() ) );
2972             const Point aEmptyPt;
2973             const Point aMtfOriginPix( aVDev.LogicToPixel( aEmptyPt ) );
2974 
2975             // #i44110# correct null-sized output - there are shapes
2976             // which have zero size in at least one dimension
2977             const Size aMtfSizePix( ::std::max( aMtfSizePixPre.Width(), 1L ),
2978                                     ::std::max( aMtfSizePixPre.Height(), 1L ) );
2979 
2980             sal_Int32 nCurrActions(0);
2981             ActionFactoryParameters aParms(aStateStack,
2982                                            rCanvas,
2983                                            aVDev,
2984                                            rParams,
2985                                            nCurrActions );
2986 
2987             // init state stack
2988             clearStateStack( aStateStack );
2989 
2990             // Setup local state, such that the metafile renders
2991             // itself into a one-by-one square at the origin for
2992             // identity view and render transformations
2993             getState( aStateStack ).transform.scale( 1.0 / aMtfSizePix.Width(),
2994                                                      1.0 / aMtfSizePix.Height() );
2995 
2996             tools::calcLogic2PixelAffineTransform( getState( aStateStack ).mapModeTransform,
2997                                                    aVDev );
2998 
2999             ColorSharedPtr pColor( getCanvas()->createColor() );
3000 
3001             // setup default text color to black
3002             getState( aStateStack ).textColor =
3003                 getState( aStateStack ).textFillColor =
3004                 getState( aStateStack ).textLineColor = pColor->getDeviceColor( 0x000000FF );
3005 
3006             // apply overrides from the Parameters struct
3007             if( rParams.maFillColor.is_initialized() )
3008             {
3009                 getState( aStateStack ).isFillColorSet = true;
3010                 getState( aStateStack ).fillColor = pColor->getDeviceColor( *rParams.maFillColor );
3011             }
3012             if( rParams.maLineColor.is_initialized() )
3013             {
3014                 getState( aStateStack ).isLineColorSet = true;
3015                 getState( aStateStack ).lineColor = pColor->getDeviceColor( *rParams.maLineColor );
3016             }
3017             if( rParams.maTextColor.is_initialized() )
3018             {
3019                 getState( aStateStack ).isTextFillColorSet = true;
3020                 getState( aStateStack ).isTextLineColorSet = true;
3021                 getState( aStateStack ).textColor =
3022                     getState( aStateStack ).textFillColor =
3023                     getState( aStateStack ).textLineColor = pColor->getDeviceColor( *rParams.maTextColor );
3024             }
3025             if( rParams.maFontName.is_initialized() ||
3026                 rParams.maFontWeight.is_initialized() ||
3027                 rParams.maFontLetterForm.is_initialized() ||
3028                 rParams.maFontUnderline.is_initialized()  ||
3029                 rParams.maFontProportion.is_initialized() )
3030             {
3031                 ::cppcanvas::internal::OutDevState& rState = getState( aStateStack );
3032 
3033                 rState.xFont = createFont( rState.fontRotation,
3034                                            ::Font(), // default font
3035                                            aParms );
3036             }
3037 
3038             createActions( const_cast<GDIMetaFile&>(rMtf), // HACK(Q2):
3039 								                           // we're
3040                         		                           // changing
3041                            		                           // the
3042                            		                           // current
3043                            		                           // action
3044                            		                           // in
3045                            		                           // createActions!
3046                            aParms,
3047                            true // TODO(P1): make subsettability configurable
3048                             );
3049         }
3050 
3051         ImplRenderer::ImplRenderer( const CanvasSharedPtr&	rCanvas,
3052                                     const BitmapEx&			rBmpEx,
3053                                     const Parameters&		rParams ) :
3054             CanvasGraphicHelper( rCanvas ),
3055             maActions()
3056         {
3057             // TODO(F3): property modification parameters are
3058             // currently ignored for Bitmaps
3059             (void)rParams;
3060 
3061             RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::ImplRenderer(bitmap)" );
3062 
3063             OSL_ENSURE( rCanvas.get() != NULL && rCanvas->getUNOCanvas().is(),
3064                         "ImplRenderer::ImplRenderer(): Invalid canvas" );
3065             OSL_ENSURE( rCanvas->getUNOCanvas()->getDevice().is(),
3066                         "ImplRenderer::ImplRenderer(): Invalid graphic device" );
3067 
3068             // make sure canvas and graphic device are valid; action
3069             // creation don't check that every time
3070             if( rCanvas.get() == NULL ||
3071                 !rCanvas->getUNOCanvas().is() ||
3072                 !rCanvas->getUNOCanvas()->getDevice().is() )
3073             {
3074                 // leave actions empty
3075                 return;
3076             }
3077 
3078             OutDevState aState;
3079 
3080             const Size aBmpSize( rBmpEx.GetSizePixel() );
3081 
3082             // Setup local state, such that the bitmap renders itself
3083             // into a one-by-one square for identity view and render
3084             // transformations
3085             aState.transform.scale( 1.0 / aBmpSize.Width(),
3086                                     1.0 / aBmpSize.Height() );
3087 
3088             // create a single action for the provided BitmapEx
3089             maActions.push_back(
3090                 MtfAction(
3091                     BitmapActionFactory::createBitmapAction(
3092                         rBmpEx,
3093                         ::basegfx::B2DPoint(),
3094                         rCanvas,
3095                         aState),
3096                     0 ) );
3097         }
3098 
3099         ImplRenderer::~ImplRenderer()
3100         {
3101         }
3102 
3103         bool ImplRenderer::drawSubset( sal_Int32	nStartIndex,
3104                                        sal_Int32	nEndIndex ) const
3105         {
3106             RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::drawSubset()" );
3107 
3108             ActionVector::const_iterator aRangeBegin;
3109             ActionVector::const_iterator aRangeEnd;
3110 
3111             try
3112             {
3113                 if( !getSubsetIndices( nStartIndex, nEndIndex,
3114                                        aRangeBegin, aRangeEnd ) )
3115                     return true; // nothing to render (but _that_ was successful)
3116 
3117                 // now, aRangeBegin references the action in which the
3118                 // subset rendering must start, and aRangeEnd references
3119                 // the action in which the subset rendering must end (it
3120                 // might also end right at the start of the referenced
3121                 // action, such that zero of that action needs to be
3122                 // rendered).
3123 
3124 
3125                 // render subset of actions
3126                 // ========================
3127 
3128                 ::basegfx::B2DHomMatrix aMatrix;
3129                 ::canvas::tools::getRenderStateTransform( aMatrix,
3130                                                           getRenderState() );
3131 
3132                 ActionRenderer aRenderer( aMatrix );
3133 
3134                 return forSubsetRange( aRenderer,
3135                                        aRangeBegin,
3136                                        aRangeEnd,
3137                                        nStartIndex,
3138                                        nEndIndex,
3139                                        maActions.end() );
3140             }
3141             catch( uno::Exception& )
3142             {
3143                 OSL_ENSURE( false,
3144                             rtl::OUStringToOString(
3145                                 comphelper::anyToString( cppu::getCaughtException() ),
3146                                 RTL_TEXTENCODING_UTF8 ).getStr() );
3147 
3148                 // convert error to return value
3149                 return false;
3150             }
3151         }
3152 
3153         ::basegfx::B2DRange ImplRenderer::getSubsetArea( sal_Int32	nStartIndex,
3154                                                          sal_Int32	nEndIndex ) const
3155         {
3156             RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::getSubsetArea()" );
3157 
3158             ActionVector::const_iterator aRangeBegin;
3159             ActionVector::const_iterator aRangeEnd;
3160 
3161             if( !getSubsetIndices( nStartIndex, nEndIndex,
3162                                    aRangeBegin, aRangeEnd ) )
3163                 return ::basegfx::B2DRange(); // nothing to render -> empty range
3164 
3165             // now, aRangeBegin references the action in which the
3166             // subset querying must start, and aRangeEnd references
3167             // the action in which the subset querying must end (it
3168             // might also end right at the start of the referenced
3169             // action, such that zero of that action needs to be
3170             // queried).
3171 
3172 
3173             // query bounds for subset of actions
3174             // ==================================
3175 
3176             ::basegfx::B2DHomMatrix aMatrix;
3177             ::canvas::tools::getRenderStateTransform( aMatrix,
3178                                                       getRenderState() );
3179 
3180             AreaQuery aQuery( aMatrix );
3181             forSubsetRange( aQuery,
3182                             aRangeBegin,
3183                             aRangeEnd,
3184                             nStartIndex,
3185                             nEndIndex,
3186                             maActions.end() );
3187 
3188             return aQuery.getBounds();
3189         }
3190 
3191         bool ImplRenderer::draw() const
3192         {
3193             RTL_LOGFILE_CONTEXT( aLog, "::cppcanvas::internal::ImplRenderer::draw()" );
3194 
3195             ::basegfx::B2DHomMatrix aMatrix;
3196             ::canvas::tools::getRenderStateTransform( aMatrix,
3197                                                       getRenderState() );
3198 
3199             try
3200             {
3201                 return ::std::for_each( maActions.begin(), maActions.end(), ActionRenderer( aMatrix ) ).result();
3202             }
3203             catch( uno::Exception& )
3204             {
3205                 OSL_ENSURE( false,
3206                             rtl::OUStringToOString(
3207                                 comphelper::anyToString( cppu::getCaughtException() ),
3208                                 RTL_TEXTENCODING_UTF8 ).getStr() );
3209 
3210                 return false;
3211             }
3212         }
3213     }
3214 }
3215