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