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