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