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