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