xref: /trunk/main/drawinglayer/source/primitive2d/metafileprimitive2d.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_drawinglayer.hxx"
30 
31 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
32 #include <basegfx/tools/canvastools.hxx>
33 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
34 #include <basegfx/color/bcolor.hxx>
35 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
36 #include <vcl/lineinfo.hxx>
37 #include <drawinglayer/attribute/lineattribute.hxx>
38 #include <drawinglayer/attribute/strokeattribute.hxx>
39 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
40 #include <vcl/metaact.hxx>
41 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
42 #include <basegfx/matrix/b2dhommatrixtools.hxx>
43 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
44 #include <basegfx/polygon/b2dpolygontools.hxx>
45 #include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx>
46 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
47 #include <vcl/salbtype.hxx>
48 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
49 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
50 #include <vcl/svapp.hxx>
51 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
52 #include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
53 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
54 #include <basegfx/polygon/b2dpolygonclipper.hxx>
55 #include <drawinglayer/primitive2d/invertprimitive2d.hxx>
56 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
57 #include <drawinglayer/primitive2d/fillbitmapprimitive2d.hxx>
58 #include <drawinglayer/primitive2d/wallpaperprimitive2d.hxx>
59 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
60 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
61 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
62 #include <i18npool/mslangid.hxx>
63 #include <drawinglayer/primitive2d/textlineprimitive2d.hxx>
64 #include <drawinglayer/primitive2d/textstrikeoutprimitive2d.hxx>
65 #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
66 #include <drawinglayer/primitive2d/rendergraphicprimitive2d.hxx>
67 #include <numeric>
68 
69 //////////////////////////////////////////////////////////////////////////////
70 
71 using namespace com::sun::star;
72 
73 //////////////////////////////////////////////////////////////////////////////
74 
75 namespace
76 {
77     /** helper class for graphic context
78 
79         This class allows to hold a complete status of classic
80         VCL OutputDevice stati. This data is needed for correct
81         interpretation of the MetaFile action flow.
82     */
83     class PropertyHolder
84     {
85     private:
86         /// current transformation (aka MapMode)
87         basegfx::B2DHomMatrix   maTransformation;
88         MapUnit                 maMapUnit;
89 
90         /// current colors
91         basegfx::BColor         maLineColor;
92         basegfx::BColor         maFillColor;
93         basegfx::BColor         maTextColor;
94         basegfx::BColor         maTextFillColor;
95         basegfx::BColor         maTextLineColor;
96         basegfx::BColor         maOverlineColor;
97 
98         /// clipping
99         basegfx::B2DPolyPolygon maClipPolyPoygon;
100 
101         /// font, etc.
102         Font                    maFont;
103         RasterOp                maRasterOp;
104         sal_uInt32              mnLayoutMode;
105         LanguageType            maLanguageType;
106         sal_uInt16              mnPushFlags;
107 
108         /// bitfield
109         /// contains all active markers
110         bool                    mbLineColor : 1;
111         bool                    mbFillColor : 1;
112         bool                    mbTextColor : 1;
113         bool                    mbTextFillColor : 1;
114         bool                    mbTextLineColor : 1;
115         bool                    mbOverlineColor : 1;
116         bool                    mbClipPolyPolygonActive : 1;
117 
118     public:
119         PropertyHolder()
120         :   maTransformation(),
121             maMapUnit(MAP_100TH_MM),
122             maLineColor(),
123             maFillColor(),
124             maTextColor(COL_BLACK),
125             maTextFillColor(),
126             maTextLineColor(),
127             maOverlineColor(),
128             maClipPolyPoygon(),
129             maFont(),
130             maRasterOp(ROP_OVERPAINT),
131             mnLayoutMode(0),
132             maLanguageType(0),
133             mnPushFlags(0),
134             mbLineColor(false),
135             mbFillColor(false),
136             mbTextColor(true),
137             mbTextFillColor(false),
138             mbTextLineColor(false),
139             mbOverlineColor(false),
140             mbClipPolyPolygonActive(false)
141         {
142         }
143 
144         ~PropertyHolder()
145         {
146         }
147 
148         /// read/write accesses
149         const basegfx::B2DHomMatrix& getTransformation() const { return maTransformation; }
150         void setTransformation(const basegfx::B2DHomMatrix& rNew) { if(rNew != maTransformation) maTransformation = rNew; }
151 
152         MapUnit getMapUnit() const { return maMapUnit; }
153         void setMapUnit(MapUnit eNew) { if(eNew != maMapUnit) maMapUnit = eNew; }
154 
155         const basegfx::BColor& getLineColor() const { return maLineColor; }
156         void setLineColor(const basegfx::BColor& rNew) { if(rNew != maLineColor) maLineColor = rNew; }
157         bool getLineColorActive() const { return mbLineColor; }
158         void setLineColorActive(bool bNew) { if(bNew != mbLineColor) mbLineColor = bNew; }
159 
160         const basegfx::BColor& getFillColor() const { return maFillColor; }
161         void setFillColor(const basegfx::BColor& rNew) { if(rNew != maFillColor) maFillColor = rNew; }
162         bool getFillColorActive() const { return mbFillColor; }
163         void setFillColorActive(bool bNew) { if(bNew != mbFillColor) mbFillColor = bNew; }
164 
165         const basegfx::BColor& getTextColor() const { return maTextColor; }
166         void setTextColor(const basegfx::BColor& rNew) { if(rNew != maTextColor) maTextColor = rNew; }
167         bool getTextColorActive() const { return mbTextColor; }
168         void setTextColorActive(bool bNew) { if(bNew != mbTextColor) mbTextColor = bNew; }
169 
170         const basegfx::BColor& getTextFillColor() const { return maTextFillColor; }
171         void setTextFillColor(const basegfx::BColor& rNew) { if(rNew != maTextFillColor) maTextFillColor = rNew; }
172         bool getTextFillColorActive() const { return mbTextFillColor; }
173         void setTextFillColorActive(bool bNew) { if(bNew != mbTextFillColor) mbTextFillColor = bNew; }
174 
175         const basegfx::BColor& getTextLineColor() const { return maTextLineColor; }
176         void setTextLineColor(const basegfx::BColor& rNew) { if(rNew != maTextLineColor) maTextLineColor = rNew; }
177         bool getTextLineColorActive() const { return mbTextLineColor; }
178         void setTextLineColorActive(bool bNew) { if(bNew != mbTextLineColor) mbTextLineColor = bNew; }
179 
180         const basegfx::BColor& getOverlineColor() const { return maOverlineColor; }
181         void setOverlineColor(const basegfx::BColor& rNew) { if(rNew != maOverlineColor) maOverlineColor = rNew; }
182         bool getOverlineColorActive() const { return mbOverlineColor; }
183         void setOverlineColorActive(bool bNew) { if(bNew != mbOverlineColor) mbOverlineColor = bNew; }
184 
185         const basegfx::B2DPolyPolygon& getClipPolyPolygon() const { return maClipPolyPoygon; }
186         void setClipPolyPolygon(const basegfx::B2DPolyPolygon& rNew) { if(rNew != maClipPolyPoygon) maClipPolyPoygon = rNew; }
187         bool getClipPolyPolygonActive() const { return mbClipPolyPolygonActive; }
188         void setClipPolyPolygonActive(bool bNew) { if(bNew != mbClipPolyPolygonActive) mbClipPolyPolygonActive = bNew; }
189 
190         const Font& getFont() const { return maFont; }
191         void setFont(const Font& rFont) { if(rFont != maFont) maFont = rFont; }
192 
193         const RasterOp& getRasterOp() const { return maRasterOp; }
194         void setRasterOp(const RasterOp& rRasterOp) { if(rRasterOp != maRasterOp) maRasterOp = rRasterOp; }
195         bool isRasterOpInvert() const { return (ROP_XOR == maRasterOp || ROP_INVERT == maRasterOp); }
196         bool isRasterOpForceBlack() const { return ROP_0 == maRasterOp; }
197         bool isRasterOpActive() const { return isRasterOpInvert() || isRasterOpForceBlack(); }
198 
199         sal_uInt32 getLayoutMode() const { return mnLayoutMode; }
200         void setLayoutMode(sal_uInt32 nNew) { if(nNew != mnLayoutMode) mnLayoutMode = nNew; }
201 
202         LanguageType getLanguageType() const { return maLanguageType; }
203         void setLanguageType(LanguageType aNew) { if(aNew != maLanguageType) maLanguageType = aNew; }
204 
205         sal_uInt16 getPushFlags() const { return mnPushFlags; }
206         void setPushFlags(sal_uInt16 nNew) { if(nNew != mnPushFlags) mnPushFlags = nNew; }
207 
208         bool getLineOrFillActive() const { return (mbLineColor || mbFillColor); }
209     };
210 } // end of anonymous namespace
211 
212 //////////////////////////////////////////////////////////////////////////////
213 
214 namespace
215 {
216     /** stack for properites
217 
218         This class builds a stack based on the PropertyHolder
219         class. It encapsulates the pointer/new/delete usage to
220         make it safe and implements the push/pop as needed by a
221         VCL Metafile interpreter. The critical part here are the
222         flag values VCL OutputDevice uses here; not all stuff is
223         pushed and thus needs to be copied at pop.
224     */
225     class PropertyHolders
226     {
227     private:
228         std::vector< PropertyHolder* >          maPropertyHolders;
229 
230     public:
231         PropertyHolders()
232         {
233             maPropertyHolders.push_back(new PropertyHolder());
234         }
235 
236         sal_uInt32 size()
237         {
238             return maPropertyHolders.size();
239         }
240 
241         void PushDefault()
242         {
243             PropertyHolder* pNew = new PropertyHolder();
244             maPropertyHolders.push_back(pNew);
245         }
246 
247         void Push(sal_uInt16 nPushFlags)
248         {
249             if(nPushFlags)
250             {
251                 OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: PUSH with no property holders (!)");
252                 if ( !maPropertyHolders.empty() )
253                 {
254                     PropertyHolder* pNew = new PropertyHolder(*maPropertyHolders.back());
255                     pNew->setPushFlags(nPushFlags);
256                     maPropertyHolders.push_back(pNew);
257                 }
258             }
259         }
260 
261         void Pop()
262         {
263             OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: POP with no property holders (!)");
264             const sal_uInt32 nSize(maPropertyHolders.size());
265 
266             if(nSize)
267             {
268                 const PropertyHolder* pTip = maPropertyHolders.back();
269                 const sal_uInt16 nPushFlags(pTip->getPushFlags());
270 
271                 if(nPushFlags)
272                 {
273                     if(nSize > 1)
274                     {
275                         // copy back content for all non-set flags
276                         PropertyHolder* pLast = maPropertyHolders[nSize - 2];
277 
278                         if(PUSH_ALL != nPushFlags)
279                         {
280                             if(!(nPushFlags & PUSH_LINECOLOR      ))
281                             {
282                                 pLast->setLineColor(pTip->getLineColor());
283                                 pLast->setLineColorActive(pTip->getLineColorActive());
284                             }
285                             if(!(nPushFlags & PUSH_FILLCOLOR      ))
286                             {
287                                 pLast->setFillColor(pTip->getFillColor());
288                                 pLast->setFillColorActive(pTip->getFillColorActive());
289                             }
290                             if(!(nPushFlags & PUSH_FONT           ))
291                             {
292                                 pLast->setFont(pTip->getFont());
293                             }
294                             if(!(nPushFlags & PUSH_TEXTCOLOR      ))
295                             {
296                                 pLast->setTextColor(pTip->getTextColor());
297                                 pLast->setTextColorActive(pTip->getTextColorActive());
298                             }
299                             if(!(nPushFlags & PUSH_MAPMODE        ))
300                             {
301                                 pLast->setTransformation(pTip->getTransformation());
302                                 pLast->setMapUnit(pTip->getMapUnit());
303                             }
304                             if(!(nPushFlags & PUSH_CLIPREGION     ))
305                             {
306                                 pLast->setClipPolyPolygon(pTip->getClipPolyPolygon());
307                                 pLast->setClipPolyPolygonActive(pTip->getClipPolyPolygonActive());
308                             }
309                             if(!(nPushFlags & PUSH_RASTEROP       ))
310                             {
311                                 pLast->setRasterOp(pTip->getRasterOp());
312                             }
313                             if(!(nPushFlags & PUSH_TEXTFILLCOLOR  ))
314                             {
315                                 pLast->setTextFillColor(pTip->getTextFillColor());
316                                 pLast->setTextFillColorActive(pTip->getTextFillColorActive());
317                             }
318                             if(!(nPushFlags & PUSH_TEXTALIGN      ))
319                             {
320                                 if(pLast->getFont().GetAlign() != pTip->getFont().GetAlign())
321                                 {
322                                     Font aFont(pLast->getFont());
323                                     aFont.SetAlign(pTip->getFont().GetAlign());
324                                     pLast->setFont(aFont);
325                                 }
326                             }
327                             if(!(nPushFlags & PUSH_REFPOINT       ))
328                             {
329                                 // not supported
330                             }
331                             if(!(nPushFlags & PUSH_TEXTLINECOLOR  ))
332                             {
333                                 pLast->setTextLineColor(pTip->getTextLineColor());
334                                 pLast->setTextLineColorActive(pTip->getTextLineColorActive());
335                             }
336                             if(!(nPushFlags & PUSH_TEXTLAYOUTMODE ))
337                             {
338                                 pLast->setLayoutMode(pTip->getLayoutMode());
339                             }
340                             if(!(nPushFlags & PUSH_TEXTLANGUAGE   ))
341                             {
342                                 pLast->setLanguageType(pTip->getLanguageType());
343                             }
344                             if(!(nPushFlags & PUSH_OVERLINECOLOR  ))
345                             {
346                                 pLast->setOverlineColor(pTip->getOverlineColor());
347                                 pLast->setOverlineColorActive(pTip->getOverlineColorActive());
348                             }
349                         }
350                     }
351                 }
352 
353                 // execute the pop
354                 delete maPropertyHolders.back();
355                 maPropertyHolders.pop_back();
356             }
357         }
358 
359         PropertyHolder& Current()
360         {
361             static PropertyHolder aDummy;
362             OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: CURRENT with no property holders (!)");
363             return maPropertyHolders.empty() ? aDummy : *maPropertyHolders.back();
364         }
365 
366         ~PropertyHolders()
367         {
368             while(maPropertyHolders.size())
369             {
370                 delete maPropertyHolders.back();
371                 maPropertyHolders.pop_back();
372             }
373         }
374     };
375 } // end of anonymous namespace
376 
377 //////////////////////////////////////////////////////////////////////////////
378 
379 namespace
380 {
381     /** helper to convert a Region to a B2DPolyPolygon
382         when it does not yet contain one. In the future
383         this may be expanded to merge the polygons created
384         from rectangles or use a special algo to directly turn
385         the spans of regions to a single, already merged
386         PolyPolygon.
387      */
388     basegfx::B2DPolyPolygon getB2DPolyPolygonFromRegion(const Region& rRegion)
389     {
390         basegfx::B2DPolyPolygon aRetval;
391 
392         if(!rRegion.IsEmpty())
393         {
394             Region aRegion(rRegion);
395             aRetval = aRegion.GetB2DPolyPolygon();
396 
397             if(!aRetval.count())
398             {
399                 RegionHandle aRegionHandle(aRegion.BeginEnumRects());
400                 Rectangle aRegionRectangle;
401 
402                 while(aRegion.GetEnumRects(aRegionHandle, aRegionRectangle))
403                 {
404                     if(!aRegionRectangle.IsEmpty())
405                     {
406                         const basegfx::B2DRange aRegionRange(
407                             aRegionRectangle.Left(), aRegionRectangle.Top(),
408                             aRegionRectangle.Right(), aRegionRectangle.Bottom());
409                         aRetval.append(basegfx::tools::createPolygonFromRect(aRegionRange));
410                     }
411                 }
412 
413                 aRegion.EndEnumRects(aRegionHandle);
414             }
415         }
416 
417         return aRetval;
418     }
419 } // end of anonymous namespace
420 
421 //////////////////////////////////////////////////////////////////////////////
422 
423 namespace
424 {
425     /** Helper class to buffer and hold a Primive target vector. It
426         encapsulates the new/delete functionality and aloows to work
427         on pointers of the implementation classes. All data will
428         be converted to uno sequences of uno references when accessing the
429         data.
430     */
431     class TargetHolder
432     {
433     private:
434         std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aTargets;
435 
436     public:
437         TargetHolder()
438         :   aTargets()
439         {
440         }
441 
442         ~TargetHolder()
443         {
444             const sal_uInt32 nCount(aTargets.size());
445 
446             for(sal_uInt32 a(0); a < nCount; a++)
447             {
448                 delete aTargets[a];
449             }
450         }
451 
452         sal_uInt32 size()
453         {
454             return aTargets.size();
455         }
456 
457         void append(drawinglayer::primitive2d::BasePrimitive2D* pCandidate)
458         {
459             if(pCandidate)
460             {
461                 aTargets.push_back(pCandidate);
462             }
463         }
464 
465         drawinglayer::primitive2d::Primitive2DSequence getPrimitive2DSequence(const PropertyHolder& rPropertyHolder)
466         {
467             const sal_uInt32 nCount(aTargets.size());
468             drawinglayer::primitive2d::Primitive2DSequence xRetval(nCount);
469 
470             for(sal_uInt32 a(0); a < nCount; a++)
471             {
472                 xRetval[a] = aTargets[a];
473             }
474 
475             // All Targets were pointers, but do not need to be deleted since they
476             // were converted to UNO API references now, so they stay as long as
477             // referenced. Do NOT delete the C++ implementation classes here, but clear
478             // the buffer to not delete them in the destructor.
479             aTargets.clear();
480 
481             if(xRetval.hasElements() && rPropertyHolder.getClipPolyPolygonActive())
482             {
483                 const basegfx::B2DPolyPolygon& rClipPolyPolygon = rPropertyHolder.getClipPolyPolygon();
484 
485                 if(rClipPolyPolygon.count())
486                 {
487                     const drawinglayer::primitive2d::Primitive2DReference xMask(
488                         new drawinglayer::primitive2d::MaskPrimitive2D(
489                             rClipPolyPolygon,
490                             xRetval));
491 
492                     xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1);
493                 }
494             }
495 
496             return xRetval;
497         }
498     };
499 } // end of anonymous namespace
500 
501 //////////////////////////////////////////////////////////////////////////////
502 
503 namespace
504 {
505     /** Helper class which builds a stack on the TargetHolder class */
506     class TargetHolders
507     {
508     private:
509         std::vector< TargetHolder* >          maTargetHolders;
510 
511     public:
512         TargetHolders()
513         {
514             maTargetHolders.push_back(new TargetHolder());
515         }
516 
517         sal_uInt32 size()
518         {
519             return maTargetHolders.size();
520         }
521 
522         void Push()
523         {
524             maTargetHolders.push_back(new TargetHolder());
525         }
526 
527         void Pop()
528         {
529             OSL_ENSURE(maTargetHolders.size(), "TargetHolders: POP with no property holders (!)");
530             if(maTargetHolders.size())
531             {
532                 delete maTargetHolders.back();
533                 maTargetHolders.pop_back();
534             }
535         }
536 
537         TargetHolder& Current()
538         {
539             OSL_ENSURE(maTargetHolders.size(), "TargetHolders: CURRENT with no property holders (!)");
540             return *maTargetHolders.back();
541         }
542 
543         ~TargetHolders()
544         {
545             while(maTargetHolders.size())
546             {
547                 delete maTargetHolders.back();
548                 maTargetHolders.pop_back();
549             }
550         }
551     };
552 } // end of anonymous namespace
553 
554 //////////////////////////////////////////////////////////////////////////////
555 
556 namespace drawinglayer
557 {
558     namespace primitive2d
559     {
560         /** NonOverlappingFillGradientPrimitive2D class
561 
562             This is a special version of the FillGradientPrimitive2D which decomposes
563             to a non-overlapping geometry version of the gradient. This needs to be
564             used to support the old XOR paint-'trick'.
565 
566             It does not need an own identifier since a renderer who wants to interpret
567             it itself may do so. It just overloads the decomposition of the C++
568             implementation class to do an alternative decomposition.
569          */
570         class NonOverlappingFillGradientPrimitive2D : public FillGradientPrimitive2D
571         {
572         protected:
573             /// local decomposition.
574             virtual Primitive2DSequence create2DDecomposition(
575                 const geometry::ViewInformation2D& rViewInformation) const;
576 
577         public:
578             /// constructor
579             NonOverlappingFillGradientPrimitive2D(
580                 const basegfx::B2DRange& rObjectRange,
581                 const attribute::FillGradientAttribute& rFillGradient)
582             :   FillGradientPrimitive2D(rObjectRange, rFillGradient)
583             {
584             }
585         };
586 
587         Primitive2DSequence NonOverlappingFillGradientPrimitive2D::create2DDecomposition(
588             const geometry::ViewInformation2D& /*rViewInformation*/) const
589         {
590             if(!getFillGradient().isDefault())
591             {
592                 return createFill(false);
593             }
594             else
595             {
596                 return Primitive2DSequence();
597             }
598         }
599     } // end of namespace primitive2d
600 } // end of namespace drawinglayer
601 
602 //////////////////////////////////////////////////////////////////////////////
603 
604 namespace
605 {
606     /** helper to convert a MapMode to a transformation */
607     basegfx::B2DHomMatrix getTransformFromMapMode(const MapMode& rMapMode)
608     {
609         basegfx::B2DHomMatrix aMapping;
610         const Fraction aNoScale(1, 1);
611         const Point& rOrigin(rMapMode.GetOrigin());
612 
613         if(0 != rOrigin.X() || 0 != rOrigin.Y())
614         {
615             aMapping.translate(rOrigin.X(), rOrigin.Y());
616         }
617 
618         if(rMapMode.GetScaleX() != aNoScale || rMapMode.GetScaleY() != aNoScale)
619         {
620             aMapping.scale(
621                 double(rMapMode.GetScaleX()),
622                 double(rMapMode.GetScaleY()));
623         }
624 
625         return aMapping;
626     }
627 
628     /** helper to create a PointArrayPrimitive2D based on current context */
629     void createPointArrayPrimitive(
630         const std::vector< basegfx::B2DPoint >& rPositions,
631         TargetHolder& rTarget,
632         PropertyHolder& rProperties,
633         basegfx::BColor aBColor)
634     {
635         if(rPositions.size())
636         {
637             if(rProperties.getTransformation().isIdentity())
638             {
639                 rTarget.append(
640                     new drawinglayer::primitive2d::PointArrayPrimitive2D(
641                         rPositions,
642                         aBColor));
643             }
644             else
645             {
646                 std::vector< basegfx::B2DPoint > aPositions(rPositions);
647 
648                 for(sal_uInt32 a(0); a < aPositions.size(); a++)
649                 {
650                     aPositions[a] = rProperties.getTransformation() * aPositions[a];
651                 }
652 
653                 rTarget.append(
654                     new drawinglayer::primitive2d::PointArrayPrimitive2D(
655                         aPositions,
656                         aBColor));
657             }
658         }
659     }
660 
661     /** helper to create a PolygonHairlinePrimitive2D based on current context */
662     void createHairlinePrimitive(
663         const basegfx::B2DPolygon& rLinePolygon,
664         TargetHolder& rTarget,
665         PropertyHolder& rProperties)
666     {
667         if(rLinePolygon.count())
668         {
669             basegfx::B2DPolygon aLinePolygon(rLinePolygon);
670             aLinePolygon.transform(rProperties.getTransformation());
671             rTarget.append(
672                 new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
673                     aLinePolygon,
674                     rProperties.getLineColor()));
675         }
676     }
677 
678     /** helper to create a PolyPolygonColorPrimitive2D based on current context */
679     void createFillPrimitive(
680         const basegfx::B2DPolyPolygon& rFillPolyPolygon,
681         TargetHolder& rTarget,
682         PropertyHolder& rProperties)
683     {
684         if(rFillPolyPolygon.count())
685         {
686             basegfx::B2DPolyPolygon aFillPolyPolygon(rFillPolyPolygon);
687             aFillPolyPolygon.transform(rProperties.getTransformation());
688             rTarget.append(
689                 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
690                     aFillPolyPolygon,
691                     rProperties.getFillColor()));
692         }
693     }
694 
695     /** helper to create a PolygonStrokePrimitive2D based on current context */
696     void createLinePrimitive(
697         const basegfx::B2DPolygon& rLinePolygon,
698         const LineInfo& rLineInfo,
699         TargetHolder& rTarget,
700         PropertyHolder& rProperties)
701     {
702         if(rLinePolygon.count())
703         {
704             const bool bDashDotUsed(LINE_DASH == rLineInfo.GetStyle());
705             const bool bWidthUsed(rLineInfo.GetWidth() > 1);
706 
707             if(bDashDotUsed || bWidthUsed)
708             {
709                 basegfx::B2DPolygon aLinePolygon(rLinePolygon);
710                 aLinePolygon.transform(rProperties.getTransformation());
711                 const drawinglayer::attribute::LineAttribute aLineAttribute(
712                     rProperties.getLineColor(),
713                     bWidthUsed ? rLineInfo.GetWidth() : 0.0,
714                     rLineInfo.GetLineJoin());
715 
716                 if(bDashDotUsed)
717                 {
718                     ::std::vector< double > fDotDashArray;
719                     const double fDashLen(rLineInfo.GetDashLen());
720                     const double fDotLen(rLineInfo.GetDotLen());
721                     const double fDistance(rLineInfo.GetDistance());
722 
723                     for(sal_uInt16 a(0); a < rLineInfo.GetDashCount(); a++)
724                     {
725                         fDotDashArray.push_back(fDashLen);
726                         fDotDashArray.push_back(fDistance);
727                     }
728 
729                     for(sal_uInt16 b(0); b < rLineInfo.GetDotCount(); b++)
730                     {
731                         fDotDashArray.push_back(fDotLen);
732                         fDotDashArray.push_back(fDistance);
733                     }
734 
735                     const double fAccumulated(::std::accumulate(fDotDashArray.begin(), fDotDashArray.end(), 0.0));
736                     const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(
737                         fDotDashArray,
738                         fAccumulated);
739 
740                     rTarget.append(
741                         new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
742                             aLinePolygon,
743                             aLineAttribute,
744                             aStrokeAttribute));
745                 }
746                 else
747                 {
748                     rTarget.append(
749                         new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
750                             aLinePolygon,
751                             aLineAttribute));
752                 }
753             }
754             else
755             {
756                 createHairlinePrimitive(rLinePolygon, rTarget, rProperties);
757             }
758         }
759     }
760 
761     /** helper to create needed line and fill primitives based on current context */
762     void createHairlineAndFillPrimitive(
763         const basegfx::B2DPolygon& rPolygon,
764         TargetHolder& rTarget,
765         PropertyHolder& rProperties)
766     {
767         if(rProperties.getFillColorActive())
768         {
769             createFillPrimitive(basegfx::B2DPolyPolygon(rPolygon), rTarget, rProperties);
770         }
771 
772         if(rProperties.getLineColorActive())
773         {
774             createHairlinePrimitive(rPolygon, rTarget, rProperties);
775         }
776     }
777 
778     /** helper to create needed line and fill primitives based on current context */
779     void createHairlineAndFillPrimitive(
780         const basegfx::B2DPolyPolygon& rPolyPolygon,
781         TargetHolder& rTarget,
782         PropertyHolder& rProperties)
783     {
784         if(rProperties.getFillColorActive())
785         {
786             createFillPrimitive(rPolyPolygon, rTarget, rProperties);
787         }
788 
789         if(rProperties.getLineColorActive())
790         {
791             for(sal_uInt32 a(0); a < rPolyPolygon.count(); a++)
792             {
793                 createHairlinePrimitive(rPolyPolygon.getB2DPolygon(a), rTarget, rProperties);
794             }
795         }
796     }
797 
798     /** helper to create DiscreteBitmapPrimitive2D based on current context.
799         The DiscreteBitmapPrimitive2D is especially created for this usage
800         since no other usage defines a bitmap visualisation based on top-left
801         position and size in pixels. At the end it will create a view-dependent
802         transformed embedding of a BitmapPrimitive2D.
803     */
804     void createBitmapExPrimitive(
805         const BitmapEx& rBitmapEx,
806         const Point& rPoint,
807         TargetHolder& rTarget,
808         PropertyHolder& rProperties)
809     {
810         if(!rBitmapEx.IsEmpty())
811         {
812             basegfx::B2DPoint aPoint(rPoint.X(), rPoint.Y());
813             aPoint = rProperties.getTransformation() * aPoint;
814 
815             rTarget.append(
816                 new drawinglayer::primitive2d::DiscreteBitmapPrimitive2D(
817                     rBitmapEx,
818                     aPoint));
819         }
820     }
821 
822     /** helper to create BitmapPrimitive2D based on current context */
823     void createBitmapExPrimitive(
824         const BitmapEx& rBitmapEx,
825         const Point& rPoint,
826         const Size& rSize,
827         TargetHolder& rTarget,
828         PropertyHolder& rProperties)
829     {
830         if(!rBitmapEx.IsEmpty())
831         {
832             basegfx::B2DHomMatrix aObjectTransform;
833 
834             aObjectTransform.set(0, 0, rSize.Width());
835             aObjectTransform.set(1, 1, rSize.Height());
836             aObjectTransform.set(0, 2, rPoint.X());
837             aObjectTransform.set(1, 2, rPoint.Y());
838 
839             aObjectTransform = rProperties.getTransformation() * aObjectTransform;
840 
841             rTarget.append(
842                 new drawinglayer::primitive2d::BitmapPrimitive2D(
843                     rBitmapEx,
844                     aObjectTransform));
845         }
846     }
847 
848     /** helper to create a regular BotmapEx from a MaskAction (definitions
849         which use a bitmap without transparence but define one of the colors as
850         transparent)
851      */
852     BitmapEx createMaskBmpEx(const Bitmap& rBitmap, const Color& rMaskColor)
853     {
854         const Color aWhite(COL_WHITE);
855         BitmapPalette aBiLevelPalette(2);
856 
857         aBiLevelPalette[0] = aWhite;
858         aBiLevelPalette[1] = rMaskColor;
859 
860         Bitmap aMask(rBitmap.CreateMask(aWhite));
861         Bitmap aSolid(rBitmap.GetSizePixel(), 1, &aBiLevelPalette);
862 
863         aSolid.Erase(rMaskColor);
864 
865         return BitmapEx(aSolid, aMask);
866     }
867 
868     /** helper to convert from a VCL Gradient definition to the corresponding
869         data for primitive representation
870      */
871     drawinglayer::attribute::FillGradientAttribute createFillGradientAttribute(const Gradient& rGradient)
872     {
873         const Color aStartColor(rGradient.GetStartColor());
874         const sal_uInt16 nStartIntens(rGradient.GetStartIntensity());
875         basegfx::BColor aStart(aStartColor.getBColor());
876 
877         if(nStartIntens != 100)
878         {
879             const basegfx::BColor aBlack;
880             aStart = interpolate(aBlack, aStart, (double)nStartIntens * 0.01);
881         }
882 
883         const Color aEndColor(rGradient.GetEndColor());
884         const sal_uInt16 nEndIntens(rGradient.GetEndIntensity());
885         basegfx::BColor aEnd(aEndColor.getBColor());
886 
887         if(nEndIntens != 100)
888         {
889             const basegfx::BColor aBlack;
890             aEnd = interpolate(aBlack, aEnd, (double)nEndIntens * 0.01);
891         }
892 
893         drawinglayer::attribute::GradientStyle aGradientStyle(drawinglayer::attribute::GRADIENTSTYLE_RECT);
894 
895         switch(rGradient.GetStyle())
896         {
897             case GRADIENT_LINEAR :
898             {
899                 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_LINEAR;
900                 break;
901             }
902             case GRADIENT_AXIAL :
903             {
904                 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_AXIAL;
905                 break;
906             }
907             case GRADIENT_RADIAL :
908             {
909                 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_RADIAL;
910                 break;
911             }
912             case GRADIENT_ELLIPTICAL :
913             {
914                 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_ELLIPTICAL;
915                 break;
916             }
917             case GRADIENT_SQUARE :
918             {
919                 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_SQUARE;
920                 break;
921             }
922             default : // GRADIENT_RECT
923             {
924                 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_RECT;
925                 break;
926             }
927         }
928 
929         return drawinglayer::attribute::FillGradientAttribute(
930             aGradientStyle,
931             (double)rGradient.GetBorder() * 0.01,
932             (double)rGradient.GetOfsX() * 0.01,
933             (double)rGradient.GetOfsY() * 0.01,
934             (double)rGradient.GetAngle() * F_PI1800,
935             aStart,
936             aEnd,
937             rGradient.GetSteps());
938     }
939 
940     /** helper to convert from a VCL Hatch definition to the corresponding
941         data for primitive representation
942      */
943     drawinglayer::attribute::FillHatchAttribute createFillHatchAttribute(const Hatch& rHatch)
944     {
945         drawinglayer::attribute::HatchStyle aHatchStyle(drawinglayer::attribute::HATCHSTYLE_SINGLE);
946 
947         switch(rHatch.GetStyle())
948         {
949             default : // case HATCH_SINGLE :
950             {
951                 aHatchStyle = drawinglayer::attribute::HATCHSTYLE_SINGLE;
952                 break;
953             }
954             case HATCH_DOUBLE :
955             {
956                 aHatchStyle = drawinglayer::attribute::HATCHSTYLE_DOUBLE;
957                 break;
958             }
959             case HATCH_TRIPLE :
960             {
961                 aHatchStyle = drawinglayer::attribute::HATCHSTYLE_TRIPLE;
962                 break;
963             }
964         }
965 
966         return drawinglayer::attribute::FillHatchAttribute(
967             aHatchStyle,
968             (double)rHatch.GetDistance(),
969             (double)rHatch.GetAngle() * F_PI1800,
970             rHatch.GetColor().getBColor(),
971             false);
972     }
973 
974     /** helper to take needed action on ClipRegion change. This method needs to be called
975         on any Region change, e.g. at the obvious actions doing this, but also at pop-calls
976         whcih change the Region of the current context. It takes care of creating the
977         current embeddec context, set the new Region at the context and eventually prepare
978         a new target for embracing new geometry to the current region
979      */
980     void HandleNewClipRegion(
981         const basegfx::B2DPolyPolygon& rClipPolyPolygon,
982         TargetHolders& rTargetHolders,
983         PropertyHolders& rPropertyHolders)
984     {
985         const bool bNewActive(rClipPolyPolygon.count());
986 
987         // #i108636# The handlig of new ClipPolyPolygons was not done as good as possible
988         // in the first version of this interpreter; e.g. when a ClipPolyPolygon was set
989         // initially and then using a lot of push/pop actions, the pop always leads
990         // to setting a 'new' ClipPolyPolygon which indeed is the return to the ClipPolyPolygon
991         // of the properties next on the stack.
992         //
993         // This ClipPolyPolygon is identical to the current one, so there is no need to
994         // create a MaskPrimitive2D containing the up-to-now created primitives, but
995         // this was done before. While this does not lead to wrong primitive
996         // representations of the metafile data, it creates unneccesarily expensive
997         // representations. Just detecting when no really 'new' ClipPolyPolygon gets set
998         // solves the problem.
999 
1000         if(!rPropertyHolders.Current().getClipPolyPolygonActive() && !bNewActive)
1001         {
1002             // no active ClipPolyPolygon exchanged by no new one, done
1003             return;
1004         }
1005 
1006         if(rPropertyHolders.Current().getClipPolyPolygonActive() && bNewActive)
1007         {
1008             // active ClipPolyPolygon and new active ClipPolyPolygon
1009             if(rPropertyHolders.Current().getClipPolyPolygon() == rClipPolyPolygon)
1010             {
1011                 // new is the same as old, done
1012                 return;
1013             }
1014         }
1015 
1016         // Here the old and the new are definitively different, maybe
1017         // old one and/or new one is not active.
1018 
1019         // Handle deletion of old ClipPolyPolygon. The process evtl. created primitives which
1020         // belong to this active ClipPolyPolygon. These need to be embedded to a
1021         // MaskPrimitive2D accordingly.
1022         if(rPropertyHolders.Current().getClipPolyPolygonActive() && rTargetHolders.size() > 1)
1023         {
1024             drawinglayer::primitive2d::Primitive2DSequence aSubContent;
1025 
1026             if(rPropertyHolders.Current().getClipPolyPolygon().count()
1027                 && rTargetHolders.Current().size())
1028             {
1029                 aSubContent = rTargetHolders.Current().getPrimitive2DSequence(
1030                     rPropertyHolders.Current());
1031             }
1032 
1033             rTargetHolders.Pop();
1034 
1035             if(aSubContent.hasElements())
1036             {
1037                 rTargetHolders.Current().append(
1038                     new drawinglayer::primitive2d::GroupPrimitive2D(
1039                         aSubContent));
1040             }
1041         }
1042 
1043         // apply new settings to current properties by setting
1044         // the new region now
1045         rPropertyHolders.Current().setClipPolyPolygonActive(bNewActive);
1046 
1047         if(bNewActive)
1048         {
1049             rPropertyHolders.Current().setClipPolyPolygon(rClipPolyPolygon);
1050 
1051             // prepare new content holder for new active region
1052             rTargetHolders.Push();
1053         }
1054     }
1055 
1056     /** helper to handle the change of RasterOp. It takes care of encapsulating all current
1057         geometry to the current RasterOp (if changed) and needs to be called on any RasterOp
1058         change. It will also start a new geometry target to embrace to the new RasterOp if
1059         a changuing RasterOp is used. Currently, ROP_XOR and ROP_INVERT are supported using
1060         InvertPrimitive2D, and ROP_0 by using a ModifiedColorPrimitive2D to force to black paint
1061      */
1062     void HandleNewRasterOp(
1063         RasterOp aRasterOp,
1064         TargetHolders& rTargetHolders,
1065         PropertyHolders& rPropertyHolders)
1066     {
1067         // check if currently active
1068         if(rPropertyHolders.Current().isRasterOpActive() && rTargetHolders.size() > 1)
1069         {
1070             drawinglayer::primitive2d::Primitive2DSequence aSubContent;
1071 
1072             if(rTargetHolders.Current().size())
1073             {
1074                 aSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
1075             }
1076 
1077             rTargetHolders.Pop();
1078 
1079             if(aSubContent.hasElements())
1080             {
1081                 if(rPropertyHolders.Current().isRasterOpForceBlack())
1082                 {
1083                     // force content to black
1084                     rTargetHolders.Current().append(
1085                         new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
1086                             aSubContent,
1087                             basegfx::BColorModifier(basegfx::BColor(0.0, 0.0, 0.0))));
1088                 }
1089                 else // if(rPropertyHolders.Current().isRasterOpInvert())
1090                 {
1091                     // invert content
1092                     rTargetHolders.Current().append(
1093                         new drawinglayer::primitive2d::InvertPrimitive2D(
1094                             aSubContent));
1095                 }
1096             }
1097         }
1098 
1099         // apply new settings
1100         rPropertyHolders.Current().setRasterOp(aRasterOp);
1101 
1102         // check if now active
1103         if(rPropertyHolders.Current().isRasterOpActive())
1104         {
1105             // prepare new content holder for new invert
1106             rTargetHolders.Push();
1107         }
1108     }
1109 
1110     /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
1111         It is a quite mighty action. This helper is for simple color filled background.
1112      */
1113     drawinglayer::primitive2d::BasePrimitive2D* CreateColorWallpaper(
1114         const basegfx::B2DRange& rRange,
1115         const basegfx::BColor& rColor,
1116         PropertyHolder& rPropertyHolder)
1117     {
1118         basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(rRange));
1119         aOutline.transform(rPropertyHolder.getTransformation());
1120 
1121         return new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
1122             basegfx::B2DPolyPolygon(aOutline),
1123             rColor);
1124     }
1125 
1126     /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
1127         It is a quite mighty action. This helper is for gradient filled background.
1128      */
1129     drawinglayer::primitive2d::BasePrimitive2D* CreateGradientWallpaper(
1130         const basegfx::B2DRange& rRange,
1131         const Gradient& rGradient,
1132         PropertyHolder& rPropertyHolder)
1133     {
1134         const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
1135 
1136         if(aAttribute.getStartColor() == aAttribute.getEndColor())
1137         {
1138             // not really a gradient. Create filled rectangle
1139             return CreateColorWallpaper(rRange, aAttribute.getStartColor(), rPropertyHolder);
1140         }
1141         else
1142         {
1143             // really a gradient
1144             drawinglayer::primitive2d::BasePrimitive2D* pRetval =
1145                 new drawinglayer::primitive2d::FillGradientPrimitive2D(
1146                     rRange,
1147                     aAttribute);
1148 
1149             if(!rPropertyHolder.getTransformation().isIdentity())
1150             {
1151                 const drawinglayer::primitive2d::Primitive2DReference xPrim(pRetval);
1152                 const drawinglayer::primitive2d::Primitive2DSequence xSeq(&xPrim, 1);
1153 
1154                 pRetval = new drawinglayer::primitive2d::TransformPrimitive2D(
1155                     rPropertyHolder.getTransformation(),
1156                     xSeq);
1157             }
1158 
1159             return pRetval;
1160         }
1161     }
1162 
1163     /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
1164         It is a quite mighty action. This helper decides if color and/or gradient
1165         background is needed for the wnated bitmap fill and then creates the needed
1166         WallpaperBitmapPrimitive2D. This primitive was created for this purpose and
1167         takes over all needed logic of orientations and tiling.
1168      */
1169     void CreateAndAppendBitmapWallpaper(
1170         basegfx::B2DRange aWallpaperRange,
1171         const Wallpaper& rWallpaper,
1172         TargetHolder& rTarget,
1173         PropertyHolder& rProperty)
1174     {
1175         const BitmapEx aBitmapEx(rWallpaper.GetBitmap());
1176         const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle());
1177 
1178         // if bitmap visualisation is transparent, maybe background
1179         // needs to be filled. Create background
1180         if(aBitmapEx.IsTransparent()
1181             || (WALLPAPER_TILE != eWallpaperStyle && WALLPAPER_SCALE != eWallpaperStyle))
1182         {
1183             if(rWallpaper.IsGradient())
1184             {
1185                 rTarget.append(
1186                     CreateGradientWallpaper(
1187                         aWallpaperRange,
1188                         rWallpaper.GetGradient(),
1189                         rProperty));
1190             }
1191             else if(!rWallpaper.GetColor().GetTransparency())
1192             {
1193                 rTarget.append(
1194                     CreateColorWallpaper(
1195                         aWallpaperRange,
1196                         rWallpaper.GetColor().getBColor(),
1197                         rProperty));
1198             }
1199         }
1200 
1201         // use wallpaper rect if set
1202         if(rWallpaper.IsRect() && !rWallpaper.GetRect().IsEmpty())
1203         {
1204             aWallpaperRange = basegfx::B2DRange(
1205                 rWallpaper.GetRect().Left(), rWallpaper.GetRect().Top(),
1206                 rWallpaper.GetRect().Right(), rWallpaper.GetRect().Bottom());
1207         }
1208 
1209         drawinglayer::primitive2d::BasePrimitive2D* pBitmapWallpaperFill =
1210             new drawinglayer::primitive2d::WallpaperBitmapPrimitive2D(
1211                 aWallpaperRange,
1212                 aBitmapEx,
1213                 eWallpaperStyle);
1214 
1215         if(rProperty.getTransformation().isIdentity())
1216         {
1217             // add directly
1218             rTarget.append(pBitmapWallpaperFill);
1219         }
1220         else
1221         {
1222             // when a transformation is set, embed to it
1223             const drawinglayer::primitive2d::Primitive2DReference xPrim(pBitmapWallpaperFill);
1224 
1225             rTarget.append(
1226                 new drawinglayer::primitive2d::TransformPrimitive2D(
1227                     rProperty.getTransformation(),
1228                     drawinglayer::primitive2d::Primitive2DSequence(&xPrim, 1)));
1229         }
1230     }
1231 
1232     /** helper to decide UnderlineAbove for text primitives */
1233     bool isUnderlineAbove(const Font& rFont)
1234     {
1235         if(!rFont.IsVertical())
1236         {
1237             return false;
1238         }
1239 
1240         if((LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()))
1241         {
1242             // the underline is right for Japanese only
1243             return true;
1244         }
1245 
1246         return false;
1247     }
1248 
1249     void createFontAttributeTransformAndAlignment(
1250         drawinglayer::attribute::FontAttribute& rFontAttribute,
1251         basegfx::B2DHomMatrix& rTextTransform,
1252         basegfx::B2DVector& rAlignmentOffset,
1253         PropertyHolder& rProperty)
1254     {
1255         const Font& rFont = rProperty.getFont();
1256         basegfx::B2DVector aFontScaling;
1257 
1258         rFontAttribute = drawinglayer::attribute::FontAttribute(
1259             drawinglayer::primitive2d::getFontAttributeFromVclFont(
1260                 aFontScaling,
1261                 rFont,
1262                 0 != (rProperty.getLayoutMode() & TEXT_LAYOUT_BIDI_RTL),
1263                 0 != (rProperty.getLayoutMode() & TEXT_LAYOUT_BIDI_STRONG)));
1264 
1265         // add FontScaling
1266         rTextTransform.scale(aFontScaling.getX(), aFontScaling.getY());
1267 
1268         // take text align into account
1269         if(ALIGN_BASELINE != rFont.GetAlign())
1270         {
1271             drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
1272             aTextLayouterDevice.setFont(rFont);
1273 
1274             if(ALIGN_TOP == rFont.GetAlign())
1275             {
1276                 rAlignmentOffset.setY(aTextLayouterDevice.getFontAscent());
1277             }
1278             else // ALIGN_BOTTOM
1279             {
1280                 rAlignmentOffset.setY(-aTextLayouterDevice.getFontDescent());
1281             }
1282 
1283             rTextTransform.translate(rAlignmentOffset.getX(), rAlignmentOffset.getY());
1284         }
1285 
1286         // add FontRotation (if used)
1287         if(rFont.GetOrientation())
1288         {
1289             rTextTransform.rotate(-rFont.GetOrientation() * F_PI1800);
1290         }
1291     }
1292 
1293     /** helper which takes complete care for creating the needed text primitives. It
1294         takes care of decorated stuff and all the geometry adaptions needed
1295      */
1296     void proccessMetaTextAction(
1297         const Point& rTextStartPosition,
1298         const XubString& rText,
1299         sal_uInt16 nTextStart,
1300         sal_uInt16 nTextLength,
1301         const ::std::vector< double >& rDXArray,
1302         TargetHolder& rTarget,
1303         PropertyHolder& rProperty)
1304     {
1305         drawinglayer::primitive2d::BasePrimitive2D* pResult = 0;
1306         const Font& rFont = rProperty.getFont();
1307         basegfx::B2DVector aAlignmentOffset(0.0, 0.0);
1308 
1309         if(nTextLength)
1310         {
1311             drawinglayer::attribute::FontAttribute aFontAttribute;
1312             basegfx::B2DHomMatrix aTextTransform;
1313 
1314             // fill parameters derived from current font
1315             createFontAttributeTransformAndAlignment(
1316                 aFontAttribute,
1317                 aTextTransform,
1318                 aAlignmentOffset,
1319                 rProperty);
1320 
1321             // add TextStartPosition
1322             aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y());
1323 
1324             // prepare FontColor and Locale
1325             const basegfx::BColor aFontColor(rProperty.getTextColor());
1326             const com::sun::star::lang::Locale aLocale(MsLangId::convertLanguageToLocale(rProperty.getLanguageType()));
1327             const bool bWordLineMode(rFont.IsWordLineMode());
1328 
1329             const bool bDecoratedIsNeeded(
1330                    UNDERLINE_NONE != rFont.GetOverline()
1331                 || UNDERLINE_NONE != rFont.GetUnderline()
1332                 || STRIKEOUT_NONE != rFont.GetStrikeout()
1333                 || EMPHASISMARK_NONE != (rFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
1334                 || RELIEF_NONE != rFont.GetRelief()
1335                 || rFont.IsShadow()
1336                 || bWordLineMode);
1337 
1338             if(bDecoratedIsNeeded)
1339             {
1340                 // prepare overline, underline and srikeout data
1341                 const drawinglayer::primitive2d::TextLine eFontOverline(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rFont.GetOverline()));
1342                 const drawinglayer::primitive2d::TextLine eFontUnderline(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rFont.GetUnderline()));
1343                 const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rFont.GetStrikeout()));
1344 
1345                 // check UndelineAbove
1346                 const bool bUnderlineAbove(drawinglayer::primitive2d::TEXT_LINE_NONE != eFontUnderline && isUnderlineAbove(rFont));
1347 
1348                 // prepare emphasis mark data
1349                 drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE);
1350 
1351                 switch(rFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
1352                 {
1353                     case EMPHASISMARK_DOT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DOT; break;
1354                     case EMPHASISMARK_CIRCLE : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_CIRCLE; break;
1355                     case EMPHASISMARK_DISC : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DISC; break;
1356                     case EMPHASISMARK_ACCENT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_ACCENT; break;
1357                 }
1358 
1359                 const bool bEmphasisMarkAbove(rFont.GetEmphasisMark() & EMPHASISMARK_POS_ABOVE);
1360                 const bool bEmphasisMarkBelow(rFont.GetEmphasisMark() & EMPHASISMARK_POS_BELOW);
1361 
1362                 // prepare font relief data
1363                 drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
1364 
1365                 switch(rFont.GetRelief())
1366                 {
1367                     case RELIEF_EMBOSSED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
1368                     case RELIEF_ENGRAVED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
1369                     default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
1370                 }
1371 
1372                 // prepare shadow/outline data
1373                 const bool bShadow(rFont.IsShadow());
1374 
1375                 // TextDecoratedPortionPrimitive2D is needed, create one
1376                 pResult = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
1377 
1378                     // attributes for TextSimplePortionPrimitive2D
1379                     aTextTransform,
1380                     rText,
1381                     nTextStart,
1382                     nTextLength,
1383                     rDXArray,
1384                     aFontAttribute,
1385                     aLocale,
1386                     aFontColor,
1387 
1388                     // attributes for TextDecoratedPortionPrimitive2D
1389                     rProperty.getOverlineColorActive() ? rProperty.getOverlineColor() : aFontColor,
1390                     rProperty.getTextLineColorActive() ? rProperty.getTextLineColor() : aFontColor,
1391                     eFontOverline,
1392                     eFontUnderline,
1393                     bUnderlineAbove,
1394                     eTextStrikeout,
1395                     bWordLineMode,
1396                     eTextEmphasisMark,
1397                     bEmphasisMarkAbove,
1398                     bEmphasisMarkBelow,
1399                     eTextRelief,
1400                     bShadow);
1401             }
1402             else
1403             {
1404                 // TextSimplePortionPrimitive2D is enough
1405                 pResult = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
1406                     aTextTransform,
1407                     rText,
1408                     nTextStart,
1409                     nTextLength,
1410                     rDXArray,
1411                     aFontAttribute,
1412                     aLocale,
1413                     aFontColor);
1414             }
1415         }
1416 
1417         if(pResult && rProperty.getTextFillColorActive())
1418         {
1419             // text background is requested, add and encapsulate both to new primitive
1420             drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
1421             aTextLayouterDevice.setFont(rFont);
1422 
1423             // get text width
1424             double fTextWidth(0.0);
1425 
1426             if(rDXArray.empty())
1427             {
1428                 fTextWidth = aTextLayouterDevice.getTextWidth(rText, nTextStart, nTextLength);
1429             }
1430             else
1431             {
1432                 fTextWidth = rDXArray.back();
1433             }
1434 
1435             if(basegfx::fTools::more(fTextWidth, 0.0))
1436             {
1437                 // build text range
1438                 const basegfx::B2DRange aTextRange(
1439                     0.0, -aTextLayouterDevice.getFontAscent(),
1440                     fTextWidth, aTextLayouterDevice.getFontDescent());
1441 
1442                 // create Transform
1443                 basegfx::B2DHomMatrix aTextTransform;
1444 
1445                 aTextTransform.translate(aAlignmentOffset.getX(), aAlignmentOffset.getY());
1446 
1447                 if(rFont.GetOrientation())
1448                 {
1449                     aTextTransform.rotate(-rFont.GetOrientation() * F_PI1800);
1450                 }
1451 
1452                 aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y());
1453 
1454                 // prepare Primitive2DSequence, put text in foreground
1455                 drawinglayer::primitive2d::Primitive2DSequence aSequence(2);
1456                 aSequence[1] = drawinglayer::primitive2d::Primitive2DReference(pResult);
1457 
1458                 // prepare filled polygon
1459                 basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aTextRange));
1460                 aOutline.transform(aTextTransform);
1461 
1462                 aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(
1463                     new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
1464                         basegfx::B2DPolyPolygon(aOutline),
1465                         rProperty.getTextFillColor()));
1466 
1467                 // set as group at pResult
1468                 pResult = new drawinglayer::primitive2d::GroupPrimitive2D(aSequence);
1469             }
1470         }
1471 
1472         if(pResult)
1473         {
1474             // add created text primitive to target
1475             if(rProperty.getTransformation().isIdentity())
1476             {
1477                 rTarget.append(pResult);
1478             }
1479             else
1480             {
1481                 // when a transformation is set, embed to it
1482                 const drawinglayer::primitive2d::Primitive2DReference aReference(pResult);
1483 
1484                 rTarget.append(
1485                     new drawinglayer::primitive2d::TransformPrimitive2D(
1486                         rProperty.getTransformation(),
1487                         drawinglayer::primitive2d::Primitive2DSequence(&aReference, 1)));
1488             }
1489         }
1490     }
1491 
1492     /** helper which takes complete care for creating the needed textLine primitives */
1493     void proccessMetaTextLineAction(
1494         const MetaTextLineAction& rAction,
1495         TargetHolder& rTarget,
1496         PropertyHolder& rProperty)
1497     {
1498         const double fLineWidth(fabs((double)rAction.GetWidth()));
1499 
1500         if(fLineWidth > 0.0)
1501         {
1502             const drawinglayer::primitive2d::TextLine aOverlineMode(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rAction.GetOverline()));
1503             const drawinglayer::primitive2d::TextLine aUnderlineMode(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rAction.GetUnderline()));
1504             const drawinglayer::primitive2d::TextStrikeout aTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rAction.GetStrikeout()));
1505 
1506             const bool bOverlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE != aOverlineMode);
1507             const bool bUnderlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE != aUnderlineMode);
1508             const bool bStrikeoutUsed(drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE != aTextStrikeout);
1509 
1510             if(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed)
1511             {
1512                 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aTargetVector;
1513                 basegfx::B2DVector aAlignmentOffset(0.0, 0.0);
1514                 drawinglayer::attribute::FontAttribute aFontAttribute;
1515                 basegfx::B2DHomMatrix aTextTransform;
1516 
1517                 // fill parameters derived from current font
1518                 createFontAttributeTransformAndAlignment(
1519                     aFontAttribute,
1520                     aTextTransform,
1521                     aAlignmentOffset,
1522                     rProperty);
1523 
1524                 // add TextStartPosition
1525                 aTextTransform.translate(rAction.GetStartPoint().X(), rAction.GetStartPoint().Y());
1526 
1527                 // prepare TextLayouter (used in most cases)
1528                 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
1529                 aTextLayouter.setFont(rProperty.getFont());
1530 
1531                 if(bOverlineUsed)
1532                 {
1533                     // create primitive geometry for overline
1534                     aTargetVector.push_back(
1535                         new drawinglayer::primitive2d::TextLinePrimitive2D(
1536                             aTextTransform,
1537                             fLineWidth,
1538                             aTextLayouter.getOverlineOffset(),
1539                             aTextLayouter.getOverlineHeight(),
1540                             aOverlineMode,
1541                             rProperty.getOverlineColor()));
1542                 }
1543 
1544                 if(bUnderlineUsed)
1545                 {
1546                     // create primitive geometry for underline
1547                     aTargetVector.push_back(
1548                         new drawinglayer::primitive2d::TextLinePrimitive2D(
1549                             aTextTransform,
1550                             fLineWidth,
1551                             aTextLayouter.getUnderlineOffset(),
1552                             aTextLayouter.getUnderlineHeight(),
1553                             aUnderlineMode,
1554                             rProperty.getTextLineColor()));
1555                 }
1556 
1557                 if(bStrikeoutUsed)
1558                 {
1559                     // create primitive geometry for strikeout
1560                     if(drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout
1561                         || drawinglayer::primitive2d::TEXT_STRIKEOUT_X == aTextStrikeout)
1562                     {
1563                         // strikeout with character
1564                         const sal_Unicode aStrikeoutChar(
1565                             drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout ? '/' : 'X');
1566                         const com::sun::star::lang::Locale aLocale(MsLangId::convertLanguageToLocale(
1567                             rProperty.getLanguageType()));
1568 
1569                         aTargetVector.push_back(
1570                             new drawinglayer::primitive2d::TextCharacterStrikeoutPrimitive2D(
1571                                 aTextTransform,
1572                                 fLineWidth,
1573                                 rProperty.getTextColor(),
1574                                 aStrikeoutChar,
1575                                 aFontAttribute,
1576                                 aLocale));
1577                     }
1578                     else
1579                     {
1580                         // strikeout with geometry
1581                         aTargetVector.push_back(
1582                             new drawinglayer::primitive2d::TextGeometryStrikeoutPrimitive2D(
1583                                 aTextTransform,
1584                                 fLineWidth,
1585                                 rProperty.getTextColor(),
1586                                 aTextLayouter.getUnderlineHeight(),
1587                                 aTextLayouter.getStrikeoutOffset(),
1588                                 aTextStrikeout));
1589                     }
1590                 }
1591 
1592                 if(aTargetVector.size())
1593                 {
1594                     // add created text primitive to target
1595                     if(rProperty.getTransformation().isIdentity())
1596                     {
1597                         for(sal_uInt32 a(0); a < aTargetVector.size(); a++)
1598                         {
1599                             rTarget.append(aTargetVector[a]);
1600                         }
1601                     }
1602                     else
1603                     {
1604                         // when a transformation is set, embed to it
1605                         drawinglayer::primitive2d::Primitive2DSequence xTargets(aTargetVector.size());
1606 
1607                         for(sal_uInt32 a(0); a < aTargetVector.size(); a++)
1608                         {
1609                             xTargets[a] = drawinglayer::primitive2d::Primitive2DReference(aTargetVector[a]);
1610                         }
1611 
1612                         rTarget.append(
1613                             new drawinglayer::primitive2d::TransformPrimitive2D(
1614                                 rProperty.getTransformation(),
1615                                 xTargets));
1616                     }
1617                 }
1618             }
1619         }
1620 
1621     }
1622 
1623     /** This is the main interpreter method. It is designed to handle the given Metafile
1624         completely inside the given context and target. It may use and modify the context and
1625         target. This design allows to call itself recursively wich adapted contexts and
1626         targets as e.g. needed for the META_FLOATTRANSPARENT_ACTION where the content is expressed
1627         as a metafile as sub-content.
1628 
1629         This interpreter is as free of VCL functionality as possible. It uses VCL data classes
1630         (else reading the data would not be possible), but e.g. does NOT use a local OutputDevice
1631         as most other MetaFile interpreters/exporters do to hold and work with the current context.
1632         This is necessary to be able to get away from the strong internal VCL-binding.
1633 
1634         It tries to combine e.g. pixel and/or point actions and to stitch together single line primitives
1635         where possible (which is not trivial with the possible line geometry definitions).
1636 
1637         It tries to handle clipping no longer as Regions and spans of Rectangles, but as PolyPolygon
1638         ClipRegions with (where possible) high precision by using the best possible data quality
1639         from the Region. The Region is unavoidable as data container, but nowadays allows the transport
1640         of Polygon-based clip regions. Where this is not used, a Polygon is constructed from the
1641         Region ranges. All primitive clipping uses the MaskPrimitive2D with Polygon-based clipping.
1642 
1643         I have marked the single MetaActions with:
1644 
1645         SIMPLE, DONE:
1646         Simple, e.g nothing to do or value setting in the context
1647 
1648         CHECKED, WORKS WELL:
1649         Thoroughly tested with extra written test code which created a replacement
1650         Metafile just to test this action in various combinations
1651 
1652         NEEDS IMPLEMENTATION:
1653         Not implemented and asserted, but also no usage found, neither in own Metafile
1654         creations, nor in EMF/WMF imports (checked with a whole bunch of critical EMF/WMF
1655         bugdocs)
1656 
1657         For more commens, see the single action implementations.
1658     */
1659     void interpretMetafile(
1660         const GDIMetaFile& rMetaFile,
1661         TargetHolders& rTargetHolders,
1662         PropertyHolders& rPropertyHolders,
1663         const drawinglayer::geometry::ViewInformation2D& rViewInformation)
1664     {
1665         const sal_uInt32 nCount(rMetaFile.GetActionCount());
1666 
1667         for(sal_uInt32 nAction(0); nAction < nCount; nAction++)
1668         {
1669             MetaAction* pAction = rMetaFile.GetAction(nAction);
1670 
1671             switch(pAction->GetType())
1672             {
1673                 case META_NULL_ACTION :
1674                 {
1675                     /** SIMPLE, DONE */
1676                     break;
1677                 }
1678                 case META_PIXEL_ACTION :
1679                 {
1680                     /** CHECKED, WORKS WELL */
1681                     std::vector< basegfx::B2DPoint > aPositions;
1682                     Color aLastColor(COL_BLACK);
1683 
1684                     while(META_PIXEL_ACTION == pAction->GetType() && nAction < nCount)
1685                     {
1686                         const MetaPixelAction* pA = (const MetaPixelAction*)pAction;
1687 
1688                         if(pA->GetColor() != aLastColor)
1689                         {
1690                             if(aPositions.size())
1691                             {
1692                                 createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
1693                                 aPositions.clear();
1694                             }
1695 
1696                             aLastColor = pA->GetColor();
1697                         }
1698 
1699                         const Point& rPoint = pA->GetPoint();
1700                         aPositions.push_back(basegfx::B2DPoint(rPoint.X(), rPoint.Y()));
1701                         nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
1702                     }
1703 
1704                     nAction--;
1705 
1706                     if(aPositions.size())
1707                     {
1708                         createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
1709                     }
1710 
1711                     break;
1712                 }
1713                 case META_POINT_ACTION :
1714                 {
1715                     /** CHECKED, WORKS WELL */
1716                     if(rPropertyHolders.Current().getLineColorActive())
1717                     {
1718                         std::vector< basegfx::B2DPoint > aPositions;
1719 
1720                         while(META_POINT_ACTION == pAction->GetType() && nAction < nCount)
1721                         {
1722                             const MetaPointAction* pA = (const MetaPointAction*)pAction;
1723                             const Point& rPoint = pA->GetPoint();
1724                             aPositions.push_back(basegfx::B2DPoint(rPoint.X(), rPoint.Y()));
1725                             nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
1726                         }
1727 
1728                         nAction--;
1729 
1730                         if(aPositions.size())
1731                         {
1732                             createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), rPropertyHolders.Current().getLineColor());
1733                         }
1734                     }
1735 
1736                     break;
1737                 }
1738                 case META_LINE_ACTION :
1739                 {
1740                     /** CHECKED, WORKS WELL */
1741                     if(rPropertyHolders.Current().getLineColorActive())
1742                     {
1743                         basegfx::B2DPolygon aLinePolygon;
1744                         LineInfo aLineInfo;
1745 
1746                         while(META_LINE_ACTION == pAction->GetType() && nAction < nCount)
1747                         {
1748                             const MetaLineAction* pA = (const MetaLineAction*)pAction;
1749                             const Point& rStartPoint = pA->GetStartPoint();
1750                             const Point& rEndPoint = pA->GetEndPoint();
1751                             const basegfx::B2DPoint aStart(rStartPoint.X(), rStartPoint.Y());
1752                             const basegfx::B2DPoint aEnd(rEndPoint.X(), rEndPoint.Y());
1753 
1754                             if(aLinePolygon.count())
1755                             {
1756                                 if(pA->GetLineInfo() == aLineInfo
1757                                     && aStart == aLinePolygon.getB2DPoint(aLinePolygon.count() - 1))
1758                                 {
1759                                     aLinePolygon.append(aEnd);
1760                                 }
1761                                 else
1762                                 {
1763                                     aLineInfo.SetLineJoin(basegfx::B2DLINEJOIN_NONE); // It were lines; force to NONE
1764                                     createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current());
1765                                     aLinePolygon.clear();
1766                                     aLineInfo = pA->GetLineInfo();
1767                                     aLinePolygon.append(aStart);
1768                                     aLinePolygon.append(aEnd);
1769                                 }
1770                             }
1771                             else
1772                             {
1773                                 aLineInfo = pA->GetLineInfo();
1774                                 aLinePolygon.append(aStart);
1775                                 aLinePolygon.append(aEnd);
1776                             }
1777 
1778                             nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
1779                         }
1780 
1781                         nAction--;
1782 
1783                         if(aLinePolygon.count())
1784                         {
1785                             aLineInfo.SetLineJoin(basegfx::B2DLINEJOIN_NONE); // It were lines; force to NONE
1786                             createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current());
1787                         }
1788                     }
1789 
1790                     break;
1791                 }
1792                 case META_RECT_ACTION :
1793                 {
1794                     /** CHECKED, WORKS WELL */
1795                     if(rPropertyHolders.Current().getLineOrFillActive())
1796                     {
1797                         const MetaRectAction* pA = (const MetaRectAction*)pAction;
1798                         const Rectangle& rRectangle = pA->GetRect();
1799 
1800                         if(!rRectangle.IsEmpty())
1801                         {
1802                             const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
1803 
1804                             if(!aRange.isEmpty())
1805                             {
1806                                 const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
1807                                 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1808                             }
1809                         }
1810                     }
1811 
1812                     break;
1813                 }
1814                 case META_ROUNDRECT_ACTION :
1815                 {
1816                     /** CHECKED, WORKS WELL */
1817                     /** The original OutputDevice::DrawRect paints nothing when nHor or nVer is zero; but just
1818                         because the tools::Polygon operator creating the rounding does produce nonsense. I assume
1819                         this an error and create an unrounded rectangle in that case (implicit in
1820                         createPolygonFromRect)
1821                      */
1822                     if(rPropertyHolders.Current().getLineOrFillActive())
1823                     {
1824                         const MetaRoundRectAction* pA = (const MetaRoundRectAction*)pAction;
1825                         const Rectangle& rRectangle = pA->GetRect();
1826 
1827                         if(!rRectangle.IsEmpty())
1828                         {
1829                             const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
1830 
1831                             if(!aRange.isEmpty())
1832                             {
1833                                 const sal_uInt32 nHor(pA->GetHorzRound());
1834                                 const sal_uInt32 nVer(pA->GetVertRound());
1835                                 basegfx::B2DPolygon aOutline;
1836 
1837                                 if(nHor || nVer)
1838                                 {
1839                                     double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0));
1840                                     double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0));
1841                                     fRadiusX = std::max(0.0, std::min(1.0, fRadiusX));
1842                                     fRadiusY = std::max(0.0, std::min(1.0, fRadiusY));
1843 
1844                                     aOutline = basegfx::tools::createPolygonFromRect(aRange, fRadiusX, fRadiusY);
1845                                 }
1846                                 else
1847                                 {
1848                                     aOutline = basegfx::tools::createPolygonFromRect(aRange);
1849                                 }
1850 
1851                                 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1852                             }
1853                         }
1854                     }
1855 
1856                     break;
1857                 }
1858                 case META_ELLIPSE_ACTION :
1859                 {
1860                     /** CHECKED, WORKS WELL */
1861                     if(rPropertyHolders.Current().getLineOrFillActive())
1862                     {
1863                         const MetaEllipseAction* pA = (const MetaEllipseAction*)pAction;
1864                         const Rectangle& rRectangle = pA->GetRect();
1865 
1866                         if(!rRectangle.IsEmpty())
1867                         {
1868                             const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
1869 
1870                             if(!aRange.isEmpty())
1871                             {
1872                                 const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromEllipse(
1873                                     aRange.getCenter(), aRange.getWidth() * 0.5, aRange.getHeight() * 0.5));
1874 
1875                                 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1876                             }
1877                         }
1878                     }
1879 
1880                     break;
1881                 }
1882                 case META_ARC_ACTION :
1883                 {
1884                     /** CHECKED, WORKS WELL */
1885                     if(rPropertyHolders.Current().getLineColorActive())
1886                     {
1887                         const MetaArcAction* pA = (const MetaArcAction*)pAction;
1888                         const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_ARC);
1889                         const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
1890 
1891                         createHairlinePrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1892                     }
1893 
1894                     break;
1895                 }
1896                 case META_PIE_ACTION :
1897                 {
1898                     /** CHECKED, WORKS WELL */
1899                     if(rPropertyHolders.Current().getLineOrFillActive())
1900                     {
1901                         const MetaPieAction* pA = (const MetaPieAction*)pAction;
1902                         const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_PIE);
1903                         const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
1904 
1905                         createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1906                     }
1907 
1908                     break;
1909                 }
1910                 case META_CHORD_ACTION :
1911                 {
1912                     /** CHECKED, WORKS WELL */
1913                     if(rPropertyHolders.Current().getLineOrFillActive())
1914                     {
1915                         const MetaChordAction* pA = (const MetaChordAction*)pAction;
1916                         const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_CHORD);
1917                         const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
1918 
1919                         createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1920                     }
1921 
1922                     break;
1923                 }
1924                 case META_POLYLINE_ACTION :
1925                 {
1926                     /** CHECKED, WORKS WELL */
1927                     if(rPropertyHolders.Current().getLineColorActive())
1928                     {
1929                         const MetaPolyLineAction* pA = (const MetaPolyLineAction*)pAction;
1930                         createLinePrimitive(pA->GetPolygon().getB2DPolygon(), pA->GetLineInfo(), rTargetHolders.Current(), rPropertyHolders.Current());
1931                     }
1932 
1933                     break;
1934                 }
1935                 case META_POLYGON_ACTION :
1936                 {
1937                     /** CHECKED, WORKS WELL */
1938                     if(rPropertyHolders.Current().getLineOrFillActive())
1939                     {
1940                         const MetaPolygonAction* pA = (const MetaPolygonAction*)pAction;
1941                         basegfx::B2DPolygon aOutline(pA->GetPolygon().getB2DPolygon());
1942 
1943                         // the metafile play interprets the polygons from MetaPolygonAction
1944                         // always as closed and always paints an edge from last to first point,
1945                         // so force to closed here to emulate that
1946                         if(aOutline.count() > 1 && !aOutline.isClosed())
1947                         {
1948                             aOutline.setClosed(true);
1949                         }
1950 
1951                         createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1952                     }
1953 
1954                     break;
1955                 }
1956                 case META_POLYPOLYGON_ACTION :
1957                 {
1958                     /** CHECKED, WORKS WELL */
1959                     if(rPropertyHolders.Current().getLineOrFillActive())
1960                     {
1961                         const MetaPolyPolygonAction* pA = (const MetaPolyPolygonAction*)pAction;
1962                         basegfx::B2DPolyPolygon aPolyPolygonOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
1963 
1964                         // the metafile play interprets the single polygons from MetaPolyPolygonAction
1965                         // always as closed and always paints an edge from last to first point,
1966                         // so force to closed here to emulate that
1967                         for(sal_uInt32 b(0); b < aPolyPolygonOutline.count(); b++)
1968                         {
1969                             basegfx::B2DPolygon aPolygonOutline(aPolyPolygonOutline.getB2DPolygon(b));
1970 
1971                             if(aPolygonOutline.count() > 1 && !aPolygonOutline.isClosed())
1972                             {
1973                                 aPolygonOutline.setClosed(true);
1974                                 aPolyPolygonOutline.setB2DPolygon(b, aPolygonOutline);
1975                             }
1976                         }
1977 
1978                         createHairlineAndFillPrimitive(aPolyPolygonOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1979                     }
1980 
1981                     break;
1982                 }
1983                 case META_TEXT_ACTION :
1984                 {
1985                     /** CHECKED, WORKS WELL */
1986                     const MetaTextAction* pA = (const MetaTextAction*)pAction;
1987                     sal_uInt32 nTextLength(pA->GetLen());
1988                     const sal_uInt32 nTextIndex(pA->GetIndex());
1989                     const sal_uInt32 nStringLength(pA->GetText().Len());
1990 
1991                     if(nTextLength + nTextIndex > nStringLength)
1992                     {
1993                         nTextLength = nStringLength - nTextIndex;
1994                     }
1995 
1996                     if(nTextLength && rPropertyHolders.Current().getTextColorActive())
1997                     {
1998                         const std::vector< double > aDXArray;
1999                         proccessMetaTextAction(
2000                             pA->GetPoint(),
2001                             pA->GetText(),
2002                             nTextIndex,
2003                             nTextLength,
2004                             aDXArray,
2005                             rTargetHolders.Current(),
2006                             rPropertyHolders.Current());
2007                     }
2008 
2009                     break;
2010                 }
2011                 case META_TEXTARRAY_ACTION :
2012                 {
2013                     /** CHECKED, WORKS WELL */
2014                     const MetaTextArrayAction* pA = (const MetaTextArrayAction*)pAction;
2015                     sal_uInt32 nTextLength(pA->GetLen());
2016                     const sal_uInt32 nTextIndex(pA->GetIndex());
2017                     const sal_uInt32 nStringLength(pA->GetText().Len());
2018 
2019                     if(nTextLength + nTextIndex > nStringLength)
2020                     {
2021                         nTextLength = nTextIndex > nStringLength ? 0 : nStringLength - nTextIndex;
2022                     }
2023 
2024                     if(nTextLength && rPropertyHolders.Current().getTextColorActive())
2025                     {
2026                         // preapare DXArray (if used)
2027                         std::vector< double > aDXArray;
2028                         sal_Int32* pDXArray = pA->GetDXArray();
2029 
2030                         if(pDXArray)
2031                         {
2032                             aDXArray.reserve(nTextLength);
2033 
2034                             for(sal_uInt32 a(0); a < nTextLength; a++)
2035                             {
2036                                 aDXArray.push_back((double)(*(pDXArray + a)));
2037                             }
2038                         }
2039 
2040                         proccessMetaTextAction(
2041                             pA->GetPoint(),
2042                             pA->GetText(),
2043                             nTextIndex,
2044                             nTextLength,
2045                             aDXArray,
2046                             rTargetHolders.Current(),
2047                             rPropertyHolders.Current());
2048                     }
2049 
2050                     break;
2051                 }
2052                 case META_STRETCHTEXT_ACTION :
2053                 {
2054                     // #i108440# StarMath uses MetaStretchTextAction, thus support is needed.
2055                     // It looks as if it pretty never really uses a width different from
2056                     // the default text-layout width, but it's not possible to be sure.
2057                     // Implemented getting the DXArray and checking for scale at all. If
2058                     // scale is more than 3.5% different, scale the DXArray before usage.
2059                     // New status:
2060 
2061                     /** CHECKED, WORKS WELL */
2062                     const MetaStretchTextAction* pA = (const MetaStretchTextAction*)pAction;
2063                     sal_uInt32 nTextLength(pA->GetLen());
2064                     const sal_uInt32 nTextIndex(pA->GetIndex());
2065                     const sal_uInt32 nStringLength(pA->GetText().Len());
2066 
2067                     if(nTextLength + nTextIndex > nStringLength)
2068                     {
2069                         nTextLength = nStringLength - nTextIndex;
2070                     }
2071 
2072                     if(nTextLength && rPropertyHolders.Current().getTextColorActive())
2073                     {
2074                         drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
2075                         aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont());
2076 
2077                         ::std::vector< double > aTextArray(
2078                             aTextLayouterDevice.getTextArray(
2079                                 pA->GetText(),
2080                                 nTextIndex,
2081                                 nTextLength));
2082 
2083                         if(!aTextArray.empty())
2084                         {
2085                             const double fTextLength(aTextArray.back());
2086 
2087                             if(0.0 != fTextLength && pA->GetWidth())
2088                             {
2089                                 const double fRelative(pA->GetWidth() / fTextLength);
2090 
2091                                 if(fabs(fRelative - 1.0) >= 0.035)
2092                                 {
2093                                     // when derivation is more than 3,5% from default text size,
2094                                     // scale the DXArray
2095                                     for(sal_uInt32 a(0); a < aTextArray.size(); a++)
2096                                     {
2097                                         aTextArray[a] *= fRelative;
2098                                     }
2099                                 }
2100                             }
2101                         }
2102 
2103                         proccessMetaTextAction(
2104                             pA->GetPoint(),
2105                             pA->GetText(),
2106                             nTextIndex,
2107                             nTextLength,
2108                             aTextArray,
2109                             rTargetHolders.Current(),
2110                             rPropertyHolders.Current());
2111                     }
2112 
2113                     break;
2114                 }
2115                 case META_TEXTRECT_ACTION :
2116                 {
2117                     /** CHECKED, WORKS WELL */
2118                     // OSL_ENSURE(false, "META_TEXTRECT_ACTION requested (!)");
2119                     const MetaTextRectAction* pA = (const MetaTextRectAction*)pAction;
2120                     const Rectangle& rRectangle = pA->GetRect();
2121                     const sal_uInt32 nStringLength(pA->GetText().Len());
2122 
2123                     if(!rRectangle.IsEmpty() && 0 != nStringLength)
2124                     {
2125                         // The problem with this action is that it describes unlayouted text
2126                         // and the layout capabilities are in EditEngine/Outliner in SVX. The
2127                         // same problem is true for VCL which internally has implementations
2128                         // to layout text in this case. There exists even a call
2129                         // OutputDevice::AddTextRectActions(...) to create the needed actions
2130                         // as 'sub-content' of a Metafile. Unfortunately i do not have an
2131                         // OutputDevice here since this interpreter tries to work without
2132                         // VCL AFAP.
2133                         // Since AddTextRectActions is the only way as long as we do not have
2134                         // a simple text layouter available, i will try to add it to the
2135                         // TextLayouterDevice isloation.
2136                         drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
2137                         aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont());
2138                         GDIMetaFile aGDIMetaFile;
2139 
2140                         aTextLayouterDevice.addTextRectActions(
2141                             rRectangle, pA->GetText(), pA->GetStyle(), aGDIMetaFile);
2142 
2143                         if(aGDIMetaFile.GetActionCount())
2144                         {
2145                             // cerate sub-content
2146                             drawinglayer::primitive2d::Primitive2DSequence xSubContent;
2147                             {
2148                                 rTargetHolders.Push();
2149                                 // #i# for sub-Mteafile contents, do start with new, default render state
2150                                 rPropertyHolders.PushDefault();
2151                                 interpretMetafile(aGDIMetaFile, rTargetHolders, rPropertyHolders, rViewInformation);
2152                                 xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
2153                                 rPropertyHolders.Pop();
2154                                 rTargetHolders.Pop();
2155                             }
2156 
2157                             if(xSubContent.hasElements())
2158                             {
2159                                 // add with transformation
2160                                 rTargetHolders.Current().append(
2161                                     new drawinglayer::primitive2d::TransformPrimitive2D(
2162                                         rPropertyHolders.Current().getTransformation(),
2163                                         xSubContent));
2164                             }
2165                         }
2166                     }
2167 
2168                     break;
2169                 }
2170                 case META_BMP_ACTION :
2171                 {
2172                     /** CHECKED, WORKS WELL */
2173                     const MetaBmpAction* pA = (const MetaBmpAction*)pAction;
2174                     const BitmapEx aBitmapEx(pA->GetBitmap());
2175 
2176                     createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
2177 
2178                     break;
2179                 }
2180                 case META_BMPSCALE_ACTION :
2181                 {
2182                     /** CHECKED, WORKS WELL */
2183                     const MetaBmpScaleAction* pA = (const MetaBmpScaleAction*)pAction;
2184                     const Bitmap aBitmapEx(pA->GetBitmap());
2185 
2186                     createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2187 
2188                     break;
2189                 }
2190                 case META_BMPSCALEPART_ACTION :
2191                 {
2192                     /** CHECKED, WORKS WELL */
2193                     const MetaBmpScalePartAction* pA = (const MetaBmpScalePartAction*)pAction;
2194                     const Bitmap& rBitmap = pA->GetBitmap();
2195 
2196                     if(!rBitmap.IsEmpty())
2197                     {
2198                         Bitmap aCroppedBitmap(rBitmap);
2199                         const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
2200 
2201                         if(!aCropRectangle.IsEmpty())
2202                         {
2203                             aCroppedBitmap.Crop(aCropRectangle);
2204                         }
2205 
2206                         const BitmapEx aCroppedBitmapEx(aCroppedBitmap);
2207                         createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2208                     }
2209 
2210                     break;
2211                 }
2212                 case META_BMPEX_ACTION :
2213                 {
2214                     /** CHECKED, WORKS WELL: Simply same as META_BMP_ACTION */
2215                     const MetaBmpExAction* pA = (const MetaBmpExAction*)pAction;
2216                     const BitmapEx& rBitmapEx = pA->GetBitmapEx();
2217 
2218                     createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
2219 
2220                     break;
2221                 }
2222                 case META_BMPEXSCALE_ACTION :
2223                 {
2224                     /** CHECKED, WORKS WELL: Simply same as META_BMPSCALE_ACTION */
2225                     const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*)pAction;
2226                     const BitmapEx& rBitmapEx = pA->GetBitmapEx();
2227 
2228                     createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2229 
2230                     break;
2231                 }
2232                 case META_BMPEXSCALEPART_ACTION :
2233                 {
2234                     /** CHECKED, WORKS WELL: Simply same as META_BMPSCALEPART_ACTION */
2235                     const MetaBmpExScalePartAction* pA = (const MetaBmpExScalePartAction*)pAction;
2236                     const BitmapEx& rBitmapEx = pA->GetBitmapEx();
2237 
2238                     if(!rBitmapEx.IsEmpty())
2239                     {
2240                         BitmapEx aCroppedBitmapEx(rBitmapEx);
2241                         const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
2242 
2243                         if(!aCropRectangle.IsEmpty())
2244                         {
2245                             aCroppedBitmapEx.Crop(aCropRectangle);
2246                         }
2247 
2248                         createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2249                     }
2250 
2251                     break;
2252                 }
2253                 case META_MASK_ACTION :
2254                 {
2255                     /** CHECKED, WORKS WELL: Simply same as META_BMP_ACTION */
2256                     const MetaMaskAction* pA = (const MetaMaskAction*)pAction;
2257                     const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
2258 
2259                     createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
2260 
2261                     break;
2262                 }
2263                 case META_MASKSCALE_ACTION :
2264                 {
2265                     /** CHECKED, WORKS WELL: Simply same as META_BMPSCALE_ACTION */
2266                     const MetaMaskScaleAction* pA = (const MetaMaskScaleAction*)pAction;
2267                     const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
2268 
2269                     createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2270 
2271                     break;
2272                 }
2273                 case META_MASKSCALEPART_ACTION :
2274                 {
2275                     /** CHECKED, WORKS WELL: Simply same as META_BMPSCALEPART_ACTION */
2276                     const MetaMaskScalePartAction* pA = (const MetaMaskScalePartAction*)pAction;
2277                     const Bitmap& rBitmap = pA->GetBitmap();
2278 
2279                     if(!rBitmap.IsEmpty())
2280                     {
2281                         Bitmap aCroppedBitmap(rBitmap);
2282                         const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
2283 
2284                         if(!aCropRectangle.IsEmpty())
2285                         {
2286                             aCroppedBitmap.Crop(aCropRectangle);
2287                         }
2288 
2289                         const BitmapEx aCroppedBitmapEx(createMaskBmpEx(aCroppedBitmap, pA->GetColor()));
2290                         createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2291                     }
2292 
2293                     break;
2294                 }
2295                 case META_GRADIENT_ACTION :
2296                 {
2297                     /** CHECKED, WORKS WELL */
2298                     const MetaGradientAction* pA = (const MetaGradientAction*)pAction;
2299                     const Rectangle& rRectangle = pA->GetRect();
2300 
2301                     if(!rRectangle.IsEmpty())
2302                     {
2303                         basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
2304 
2305                         if(!aRange.isEmpty())
2306                         {
2307                             const Gradient& rGradient = pA->GetGradient();
2308                             const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
2309                             basegfx::B2DPolyPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
2310 
2311                             if(aAttribute.getStartColor() == aAttribute.getEndColor())
2312                             {
2313                                 // not really a gradient. Create filled rectangle
2314                                 createFillPrimitive(
2315                                     aOutline,
2316                                     rTargetHolders.Current(),
2317                                     rPropertyHolders.Current());
2318                             }
2319                             else
2320                             {
2321                                 // really a gradient
2322                                 aRange.transform(rPropertyHolders.Current().getTransformation());
2323                                 drawinglayer::primitive2d::Primitive2DSequence xGradient(1);
2324 
2325                                 if(rPropertyHolders.Current().isRasterOpInvert())
2326                                 {
2327                                     // use a special version of FillGradientPrimitive2D which creates
2328                                     // non-overlapping geometry on decomposition to makethe old XOR
2329                                     // paint 'trick' work.
2330                                     xGradient[0] = drawinglayer::primitive2d::Primitive2DReference(
2331                                         new drawinglayer::primitive2d::NonOverlappingFillGradientPrimitive2D(
2332                                             aRange,
2333                                             aAttribute));
2334                                 }
2335                                 else
2336                                 {
2337                                     xGradient[0] = drawinglayer::primitive2d::Primitive2DReference(
2338                                         new drawinglayer::primitive2d::FillGradientPrimitive2D(
2339                                             aRange,
2340                                             aAttribute));
2341                                 }
2342 
2343                                 // #i112300# clip against polygon representing the rectangle from
2344                                 // the action. This is implicitely done using a temp Clipping in VCL
2345                                 // when a MetaGradientAction is executed
2346                                 aOutline.transform(rPropertyHolders.Current().getTransformation());
2347                                 rTargetHolders.Current().append(
2348                                     new drawinglayer::primitive2d::MaskPrimitive2D(
2349                                         aOutline,
2350                                         xGradient));
2351                             }
2352                         }
2353                     }
2354 
2355                     break;
2356                 }
2357                 case META_HATCH_ACTION :
2358                 {
2359                     /** CHECKED, WORKS WELL */
2360                     const MetaHatchAction* pA = (const MetaHatchAction*)pAction;
2361                     basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
2362 
2363                     if(aOutline.count())
2364                     {
2365                         const Hatch& rHatch = pA->GetHatch();
2366                         const drawinglayer::attribute::FillHatchAttribute aAttribute(createFillHatchAttribute(rHatch));
2367 
2368                         aOutline.transform(rPropertyHolders.Current().getTransformation());
2369 
2370                         const basegfx::B2DRange aObjectRange(aOutline.getB2DRange());
2371                         const drawinglayer::primitive2d::Primitive2DReference aFillHatch(
2372                             new drawinglayer::primitive2d::FillHatchPrimitive2D(
2373                                 aObjectRange,
2374                                 basegfx::BColor(),
2375                                 aAttribute));
2376 
2377                         rTargetHolders.Current().append(
2378                             new drawinglayer::primitive2d::MaskPrimitive2D(
2379                                 aOutline,
2380                                 drawinglayer::primitive2d::Primitive2DSequence(&aFillHatch, 1)));
2381                     }
2382 
2383                     break;
2384                 }
2385                 case META_WALLPAPER_ACTION :
2386                 {
2387                     /** CHECKED, WORKS WELL */
2388                     const MetaWallpaperAction* pA = (const MetaWallpaperAction*)pAction;
2389                     Rectangle aWallpaperRectangle(pA->GetRect());
2390 
2391                     if(!aWallpaperRectangle.IsEmpty())
2392                     {
2393                         const Wallpaper& rWallpaper = pA->GetWallpaper();
2394                         const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle());
2395                         basegfx::B2DRange aWallpaperRange(
2396                             aWallpaperRectangle.Left(), aWallpaperRectangle.Top(),
2397                             aWallpaperRectangle.Right(), aWallpaperRectangle.Bottom());
2398 
2399                         if(WALLPAPER_NULL != eWallpaperStyle)
2400                         {
2401                             if(rWallpaper.IsBitmap())
2402                             {
2403                                 // create bitmap background. Caution: This
2404                                 // also will create gradient/color background(s)
2405                                 // when the bitmap is transparent or not tiled
2406                                 CreateAndAppendBitmapWallpaper(
2407                                     aWallpaperRange,
2408                                     rWallpaper,
2409                                     rTargetHolders.Current(),
2410                                     rPropertyHolders.Current());
2411                             }
2412                             else if(rWallpaper.IsGradient())
2413                             {
2414                                 // create gradient background
2415                                 rTargetHolders.Current().append(
2416                                     CreateGradientWallpaper(
2417                                         aWallpaperRange,
2418                                         rWallpaper.GetGradient(),
2419                                         rPropertyHolders.Current()));
2420                             }
2421                             else if(!rWallpaper.GetColor().GetTransparency())
2422                             {
2423                                 // create color background
2424                                 rTargetHolders.Current().append(
2425                                     CreateColorWallpaper(
2426                                         aWallpaperRange,
2427                                         rWallpaper.GetColor().getBColor(),
2428                                         rPropertyHolders.Current()));
2429                             }
2430                         }
2431                     }
2432 
2433                     break;
2434                 }
2435                 case META_CLIPREGION_ACTION :
2436                 {
2437                     /** CHECKED, WORKS WELL */
2438                     const MetaClipRegionAction* pA = (const MetaClipRegionAction*)pAction;
2439 
2440                     if(pA->IsClipping())
2441                     {
2442                         // new clipping. Get PolyPolygon and transform with current transformation
2443                         basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(pA->GetRegion()));
2444 
2445                         aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
2446                         HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
2447                     }
2448                     else
2449                     {
2450                         // end clipping
2451                         const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
2452 
2453                         HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
2454                     }
2455 
2456                     break;
2457                 }
2458                 case META_ISECTRECTCLIPREGION_ACTION :
2459                 {
2460                     /** CHECKED, WORKS WELL */
2461                     const MetaISectRectClipRegionAction* pA = (const MetaISectRectClipRegionAction*)pAction;
2462                     const Rectangle& rRectangle = pA->GetRect();
2463 
2464                     if(rRectangle.IsEmpty())
2465                     {
2466                         // intersect with empty rectangle will always give empty
2467                         // ClipPolyPolygon; start new clipping with empty PolyPolygon
2468                         const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
2469 
2470                         HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
2471                     }
2472                     else
2473                     {
2474                         // create transformed ClipRange
2475                         basegfx::B2DRange aClipRange(
2476                             rRectangle.Left(), rRectangle.Top(),
2477                             rRectangle.Right(), rRectangle.Bottom());
2478 
2479                         aClipRange.transform(rPropertyHolders.Current().getTransformation());
2480 
2481                         if(rPropertyHolders.Current().getClipPolyPolygonActive())
2482                         {
2483                             if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
2484                             {
2485                                 // nothing to do, empty active clipPolyPolygon will stay
2486                                 // empty when intersecting
2487                             }
2488                             else
2489                             {
2490                                 // AND existing region and new ClipRange
2491                                 const basegfx::B2DPolyPolygon aOriginalPolyPolygon(
2492                                     rPropertyHolders.Current().getClipPolyPolygon());
2493                                 basegfx::B2DPolyPolygon aClippedPolyPolygon;
2494 
2495                                 if(aOriginalPolyPolygon.count())
2496                                 {
2497                                     aClippedPolyPolygon = basegfx::tools::clipPolyPolygonOnRange(
2498                                         aOriginalPolyPolygon,
2499                                         aClipRange,
2500                                         true,
2501                                         false);
2502                                 }
2503 
2504                                 if(aClippedPolyPolygon != aOriginalPolyPolygon)
2505                                 {
2506                                     // start new clipping with intersected region
2507                                     HandleNewClipRegion(
2508                                         aClippedPolyPolygon,
2509                                         rTargetHolders,
2510                                         rPropertyHolders);
2511                                 }
2512                             }
2513                         }
2514                         else
2515                         {
2516                             // start new clipping with ClipRange
2517                             const basegfx::B2DPolyPolygon aNewClipPolyPolygon(
2518                                 basegfx::tools::createPolygonFromRect(aClipRange));
2519 
2520                             HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
2521                         }
2522                     }
2523 
2524                     break;
2525                 }
2526                 case META_ISECTREGIONCLIPREGION_ACTION :
2527                 {
2528                     /** CHECKED, WORKS WELL */
2529                     const MetaISectRegionClipRegionAction* pA = (const MetaISectRegionClipRegionAction*)pAction;
2530                     const Region& rNewRegion = pA->GetRegion();
2531 
2532                     if(rNewRegion.IsEmpty())
2533                     {
2534                         // intersect with empty region will always give empty
2535                         // region; start new clipping with empty PolyPolygon
2536                         const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
2537 
2538                         HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
2539                     }
2540                     else
2541                     {
2542                         // get new ClipPolyPolygon, transform it with current transformation
2543                         basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(rNewRegion));
2544                         aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
2545 
2546                         if(rPropertyHolders.Current().getClipPolyPolygonActive())
2547                         {
2548                             if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
2549                             {
2550                                 // nothing to do, empty active clipPolyPolygon will stay empty
2551                                 // when intersecting with any region
2552                             }
2553                             else
2554                             {
2555                                 // AND existing and new region
2556                                 const basegfx::B2DPolyPolygon aOriginalPolyPolygon(
2557                                     rPropertyHolders.Current().getClipPolyPolygon());
2558                                 basegfx::B2DPolyPolygon aClippedPolyPolygon;
2559 
2560                                 if(aOriginalPolyPolygon.count())
2561                                 {
2562                                     aClippedPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon(
2563                                         aOriginalPolyPolygon, aNewClipPolyPolygon, true, false);
2564                                 }
2565 
2566                                 if(aClippedPolyPolygon != aOriginalPolyPolygon)
2567                                 {
2568                                     // start new clipping with intersected ClipPolyPolygon
2569                                     HandleNewClipRegion(aClippedPolyPolygon, rTargetHolders, rPropertyHolders);
2570                                 }
2571                             }
2572                         }
2573                         else
2574                         {
2575                             // start new clipping with new ClipPolyPolygon
2576                             HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
2577                         }
2578                     }
2579 
2580                     break;
2581                 }
2582                 case META_MOVECLIPREGION_ACTION :
2583                 {
2584                     /** CHECKED, WORKS WELL */
2585                     const MetaMoveClipRegionAction* pA = (const MetaMoveClipRegionAction*)pAction;
2586 
2587                     if(rPropertyHolders.Current().getClipPolyPolygonActive())
2588                     {
2589                         if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
2590                         {
2591                             // nothing to do
2592                         }
2593                         else
2594                         {
2595                             const sal_Int32 nHor(pA->GetHorzMove());
2596                             const sal_Int32 nVer(pA->GetVertMove());
2597 
2598                             if(0 != nHor || 0 != nVer)
2599                             {
2600                                 // prepare translation, add current transformation
2601                                 basegfx::B2DVector aVector(pA->GetHorzMove(), pA->GetVertMove());
2602                                 aVector *= rPropertyHolders.Current().getTransformation();
2603                                 basegfx::B2DHomMatrix aTransform(
2604                                     basegfx::tools::createTranslateB2DHomMatrix(aVector));
2605 
2606                                 // transform existing region
2607                                 basegfx::B2DPolyPolygon aClipPolyPolygon(
2608                                     rPropertyHolders.Current().getClipPolyPolygon());
2609 
2610                                 aClipPolyPolygon.transform(aTransform);
2611                                 HandleNewClipRegion(aClipPolyPolygon, rTargetHolders, rPropertyHolders);
2612                             }
2613                         }
2614                     }
2615 
2616                     break;
2617                 }
2618                 case META_LINECOLOR_ACTION :
2619                 {
2620                     /** CHECKED, WORKS WELL */
2621                     const MetaLineColorAction* pA = (const MetaLineColorAction*)pAction;
2622                     const bool bActive(pA->IsSetting());
2623 
2624                     rPropertyHolders.Current().setLineColorActive(bActive);
2625                     if(bActive)
2626                         rPropertyHolders.Current().setLineColor(pA->GetColor().getBColor());
2627 
2628                     break;
2629                 }
2630                 case META_FILLCOLOR_ACTION :
2631                 {
2632                     /** CHECKED, WORKS WELL */
2633                     const MetaFillColorAction* pA = (const MetaFillColorAction*)pAction;
2634                     const bool bActive(pA->IsSetting());
2635 
2636                     rPropertyHolders.Current().setFillColorActive(bActive);
2637                     if(bActive)
2638                         rPropertyHolders.Current().setFillColor(pA->GetColor().getBColor());
2639 
2640                     break;
2641                 }
2642                 case META_TEXTCOLOR_ACTION :
2643                 {
2644                     /** SIMPLE, DONE */
2645                     const MetaTextColorAction* pA = (const MetaTextColorAction*)pAction;
2646                     const bool bActivate(COL_TRANSPARENT != pA->GetColor().GetColor());
2647 
2648                     rPropertyHolders.Current().setTextColorActive(bActivate);
2649                     rPropertyHolders.Current().setTextColor(pA->GetColor().getBColor());
2650 
2651                     break;
2652                 }
2653                 case META_TEXTFILLCOLOR_ACTION :
2654                 {
2655                     /** SIMPLE, DONE */
2656                     const MetaTextFillColorAction* pA = (const MetaTextFillColorAction*)pAction;
2657                     const bool bWithColorArgument(pA->IsSetting());
2658 
2659                     if(bWithColorArgument)
2660                     {
2661                         // emulate OutputDevice::SetTextFillColor(...) WITH argument
2662                         const Color& rFontFillColor = pA->GetColor();
2663                         rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor());
2664                         rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor.GetColor());
2665                     }
2666                     else
2667                     {
2668                         // emulate SetFillColor() <- NO argument (!)
2669                         rPropertyHolders.Current().setTextFillColorActive(false);
2670                     }
2671 
2672                     break;
2673                 }
2674                 case META_TEXTALIGN_ACTION :
2675                 {
2676                     /** SIMPLE, DONE */
2677                     const MetaTextAlignAction* pA = (const MetaTextAlignAction*)pAction;
2678                     const TextAlign aNewTextAlign = pA->GetTextAlign();
2679 
2680                     // TextAlign is applied to the current font (as in
2681                     // OutputDevice::SetTextAlign which would be used when
2682                     // playing the Metafile)
2683                     if(rPropertyHolders.Current().getFont().GetAlign() != aNewTextAlign)
2684                     {
2685                         Font aNewFont(rPropertyHolders.Current().getFont());
2686                         aNewFont.SetAlign(aNewTextAlign);
2687                         rPropertyHolders.Current().setFont(aNewFont);
2688                     }
2689 
2690                     break;
2691                 }
2692                 case META_MAPMODE_ACTION :
2693                 {
2694                     /** CHECKED, WORKS WELL */
2695                     // the most necessary MapMode to be interpreted is MAP_RELATIVE,
2696                     // but also the others may occur. Even not yet supported ones
2697                     // may need to be added here later
2698                     const MetaMapModeAction* pA = (const MetaMapModeAction*)pAction;
2699                     const MapMode& rMapMode = pA->GetMapMode();
2700                     basegfx::B2DHomMatrix aMapping;
2701 
2702                     if(MAP_RELATIVE == rMapMode.GetMapUnit())
2703                     {
2704                         aMapping = getTransformFromMapMode(rMapMode);
2705                     }
2706                     else
2707                     {
2708                         switch(rMapMode.GetMapUnit())
2709                         {
2710                             case MAP_100TH_MM :
2711                             {
2712                                 if(MAP_TWIP == rPropertyHolders.Current().getMapUnit())
2713                                 {
2714                                     // MAP_TWIP -> MAP_100TH_MM
2715                                     const double fTwipTo100thMm(127.0 / 72.0);
2716                                     aMapping.scale(fTwipTo100thMm, fTwipTo100thMm);
2717                                 }
2718                                 break;
2719                             }
2720                             case MAP_TWIP :
2721                             {
2722                                 if(MAP_100TH_MM == rPropertyHolders.Current().getMapUnit())
2723                                 {
2724                                     // MAP_100TH_MM -> MAP_TWIP
2725                                     const double f100thMmToTwip(72.0 / 127.0);
2726                                     aMapping.scale(f100thMmToTwip, f100thMmToTwip);
2727                                 }
2728                                 break;
2729                             }
2730                             default :
2731                             {
2732                                 OSL_ENSURE(false, "interpretMetafile: META_MAPMODE_ACTION with unsupported MapUnit (!)");
2733                                 break;
2734                             }
2735                         }
2736 
2737                         aMapping = getTransformFromMapMode(rMapMode) * aMapping;
2738                         rPropertyHolders.Current().setMapUnit(rMapMode.GetMapUnit());
2739                     }
2740 
2741                     if(!aMapping.isIdentity())
2742                     {
2743                         aMapping = aMapping * rPropertyHolders.Current().getTransformation();
2744                         rPropertyHolders.Current().setTransformation(aMapping);
2745                     }
2746 
2747                     break;
2748                 }
2749                 case META_FONT_ACTION :
2750                 {
2751                     /** SIMPLE, DONE */
2752                     const MetaFontAction* pA = (const MetaFontAction*)pAction;
2753                     rPropertyHolders.Current().setFont(pA->GetFont());
2754                     Size aFontSize(pA->GetFont().GetSize());
2755 
2756                     if(0 == aFontSize.Height())
2757                     {
2758                         // this should not happen but i got Metafiles where this was the
2759                         // case. A height needs to be guessed (similar to OutputDevice::ImplNewFont())
2760                         Font aCorrectedFont(pA->GetFont());
2761 
2762                         // guess 16 pixel (as in VCL)
2763                         aFontSize = Size(0, 16);
2764 
2765                         // convert to target MapUnit if not pixels
2766                         aFontSize = Application::GetDefaultDevice()->LogicToLogic(
2767                             aFontSize, MAP_PIXEL, rPropertyHolders.Current().getMapUnit());
2768 
2769                         aCorrectedFont.SetSize(aFontSize);
2770                         rPropertyHolders.Current().setFont(aCorrectedFont);
2771                     }
2772 
2773                     // older Metafiles have no META_TEXTCOLOR_ACTION which defines
2774                     // the FontColor now, so use the Font's color when not transparent
2775                     const Color& rFontColor = pA->GetFont().GetColor();
2776                     const bool bActivate(COL_TRANSPARENT != rFontColor.GetColor());
2777 
2778                     if(bActivate)
2779                     {
2780                         rPropertyHolders.Current().setTextColor(rFontColor.getBColor());
2781                     }
2782 
2783                     // caution: do NOT decativate here on transparet, see
2784                     // OutputDevice::SetFont(..) for more info
2785                     // rPropertyHolders.Current().setTextColorActive(bActivate);
2786 
2787                     // for fill color emulate a MetaTextFillColorAction with !transparent as bool,
2788                     // see OutputDevice::SetFont(..) the if(mpMetaFile) case
2789                     if(bActivate)
2790                     {
2791                         const Color& rFontFillColor = pA->GetFont().GetFillColor();
2792                         rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor());
2793                         rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor.GetColor());
2794                     }
2795                     else
2796                     {
2797                         rPropertyHolders.Current().setTextFillColorActive(false);
2798                     }
2799 
2800                     break;
2801                 }
2802                 case META_PUSH_ACTION :
2803                 {
2804                     /** CHECKED, WORKS WELL */
2805                     const MetaPushAction* pA = (const MetaPushAction*)pAction;
2806                     rPropertyHolders.Push(pA->GetFlags());
2807 
2808                     break;
2809                 }
2810                 case META_POP_ACTION :
2811                 {
2812                     /** CHECKED, WORKS WELL */
2813                     const bool bRegionMayChange(rPropertyHolders.Current().getPushFlags() & PUSH_CLIPREGION);
2814                     const bool bRasterOpMayChange(rPropertyHolders.Current().getPushFlags() & PUSH_RASTEROP);
2815 
2816                     if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive())
2817                     {
2818                         // end evtl. clipping
2819                         const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
2820 
2821                         HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
2822                     }
2823 
2824                     if(bRasterOpMayChange && rPropertyHolders.Current().isRasterOpActive())
2825                     {
2826                         // end evtl. RasterOp
2827                         HandleNewRasterOp(ROP_OVERPAINT, rTargetHolders, rPropertyHolders);
2828                     }
2829 
2830                     rPropertyHolders.Pop();
2831 
2832                     if(bRasterOpMayChange && rPropertyHolders.Current().isRasterOpActive())
2833                     {
2834                         // start evtl. RasterOp
2835                         HandleNewRasterOp(rPropertyHolders.Current().getRasterOp(), rTargetHolders, rPropertyHolders);
2836                     }
2837 
2838                     if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive())
2839                     {
2840                         // start evtl. clipping
2841                         HandleNewClipRegion(
2842                             rPropertyHolders.Current().getClipPolyPolygon(), rTargetHolders, rPropertyHolders);
2843                     }
2844 
2845                     break;
2846                 }
2847                 case META_RASTEROP_ACTION :
2848                 {
2849                     /** CHECKED, WORKS WELL */
2850                     const MetaRasterOpAction* pA = (const MetaRasterOpAction*)pAction;
2851                     const RasterOp aRasterOp = pA->GetRasterOp();
2852 
2853                     HandleNewRasterOp(aRasterOp, rTargetHolders, rPropertyHolders);
2854 
2855                     break;
2856                 }
2857                 case META_TRANSPARENT_ACTION :
2858                 {
2859                     /** CHECKED, WORKS WELL */
2860                     const MetaTransparentAction* pA = (const MetaTransparentAction*)pAction;
2861                     const basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
2862 
2863                     if(aOutline.count())
2864                     {
2865                         const sal_uInt16 nTransparence(pA->GetTransparence());
2866 
2867                         if(0 == nTransparence)
2868                         {
2869                             // not transparent
2870                             createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
2871                         }
2872                         else if(nTransparence >= 100)
2873                         {
2874                             // fully or more than transparent
2875                         }
2876                         else
2877                         {
2878                             // transparent. Create new target
2879                             rTargetHolders.Push();
2880 
2881                             // create primitives there and get them
2882                             createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
2883                             const drawinglayer::primitive2d::Primitive2DSequence aSubContent(
2884                                 rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current()));
2885 
2886                             // back to old target
2887                             rTargetHolders.Pop();
2888 
2889                             if(aSubContent.hasElements())
2890                             {
2891                                 rTargetHolders.Current().append(
2892                                     new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
2893                                         aSubContent,
2894                                         nTransparence * 0.01));
2895                             }
2896                         }
2897                     }
2898 
2899                     break;
2900                 }
2901                 case META_EPS_ACTION :
2902                 {
2903                     /** CHECKED, WORKS WELL */
2904                     // To support this action, i have added a EpsPrimitive2D which will
2905                     // by default decompose to the Metafile replacement data. To support
2906                     // this EPS on screen, the renderer visualizing this has to support
2907                     // that primitive and visualize the Eps file (e.g. printing)
2908                     const MetaEPSAction* pA = (const MetaEPSAction*)pAction;
2909                     const Rectangle aRectangle(pA->GetPoint(), pA->GetSize());
2910 
2911                     if(!aRectangle.IsEmpty())
2912                     {
2913                         // create object transform
2914                         basegfx::B2DHomMatrix aObjectTransform;
2915 
2916                         aObjectTransform.set(0, 0, aRectangle.GetWidth());
2917                         aObjectTransform.set(1, 1, aRectangle.GetHeight());
2918                         aObjectTransform.set(0, 2, aRectangle.Left());
2919                         aObjectTransform.set(1, 2, aRectangle.Top());
2920 
2921                         // add current transformation
2922                         aObjectTransform = rPropertyHolders.Current().getTransformation() * aObjectTransform;
2923 
2924                         // embed using EpsPrimitive
2925                         rTargetHolders.Current().append(
2926                             new drawinglayer::primitive2d::EpsPrimitive2D(
2927                                 aObjectTransform,
2928                                 pA->GetLink(),
2929                                 pA->GetSubstitute()));
2930                     }
2931 
2932                     break;
2933                 }
2934                 case META_REFPOINT_ACTION :
2935                 {
2936                     /** SIMPLE, DONE */
2937                     // only used for hatch and line pattern offsets, pretty much no longer
2938                     // supported today
2939                     // const MetaRefPointAction* pA = (const MetaRefPointAction*)pAction;
2940                     break;
2941                 }
2942                 case META_TEXTLINECOLOR_ACTION :
2943                 {
2944                     /** SIMPLE, DONE */
2945                     const MetaTextLineColorAction* pA = (const MetaTextLineColorAction*)pAction;
2946                     const bool bActive(pA->IsSetting());
2947 
2948                     rPropertyHolders.Current().setTextLineColorActive(bActive);
2949                     if(bActive)
2950                         rPropertyHolders.Current().setTextLineColor(pA->GetColor().getBColor());
2951 
2952                     break;
2953                 }
2954                 case META_TEXTLINE_ACTION :
2955                 {
2956                     /** CHECKED, WORKS WELL */
2957                     // actually creates overline, underline and strikeouts, so
2958                     // these should be isolated from TextDecoratedPortionPrimitive2D
2959                     // to own primitives. Done, available now.
2960                     //
2961                     // This Metaaction seems not to be used (was not used in any
2962                     // checked files). It's used in combination with the current
2963                     // Font.
2964                     const MetaTextLineAction* pA = (const MetaTextLineAction*)pAction;
2965 
2966                     proccessMetaTextLineAction(
2967                         *pA,
2968                         rTargetHolders.Current(),
2969                         rPropertyHolders.Current());
2970 
2971                     break;
2972                 }
2973                 case META_FLOATTRANSPARENT_ACTION :
2974                 {
2975                     /** CHECKED, WORKS WELL */
2976                     const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*)pAction;
2977                     const Rectangle aTargetRectangle(pA->GetPoint(), pA->GetSize());
2978 
2979                     if(!aTargetRectangle.IsEmpty())
2980                     {
2981                         const GDIMetaFile& rContent = pA->GetGDIMetaFile();
2982 
2983                         if(rContent.GetActionCount())
2984                         {
2985                             // create the sub-content with no embedding specific to the
2986                             // sub-metafile, this seems not to be used.
2987                             drawinglayer::primitive2d::Primitive2DSequence xSubContent;
2988                             {
2989                                 rTargetHolders.Push();
2990                                 // #i# for sub-Mteafile contents, do start with new, default render state
2991                                 rPropertyHolders.PushDefault();
2992                                 interpretMetafile(rContent, rTargetHolders, rPropertyHolders, rViewInformation);
2993                                 xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
2994                                 rPropertyHolders.Pop();
2995                                 rTargetHolders.Pop();
2996                             }
2997 
2998                             if(xSubContent.hasElements())
2999                             {
3000                                 // check if gradient is a real gradient
3001                                 const Gradient& rGradient = pA->GetGradient();
3002                                 const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
3003 
3004                                 if(aAttribute.getStartColor() == aAttribute.getEndColor())
3005                                 {
3006                                     // not really a gradient; create UnifiedTransparencePrimitive2D
3007                                     rTargetHolders.Current().append(
3008                                         new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
3009                                             xSubContent,
3010                                             aAttribute.getStartColor().luminance()));
3011                                 }
3012                                 else
3013                                 {
3014                                     // really a gradient. Create gradient sub-content (with correct scaling)
3015                                     basegfx::B2DRange aRange(
3016                                         aTargetRectangle.Left(), aTargetRectangle.Top(),
3017                                         aTargetRectangle.Right(), aTargetRectangle.Bottom());
3018                                     aRange.transform(rPropertyHolders.Current().getTransformation());
3019 
3020                                     // prepare gradient for transparent content
3021                                     const drawinglayer::primitive2d::Primitive2DReference xTransparence(
3022                                         new drawinglayer::primitive2d::FillGradientPrimitive2D(
3023                                             aRange,
3024                                             aAttribute));
3025 
3026                                     // create transparence primitive
3027                                     rTargetHolders.Current().append(
3028                                         new drawinglayer::primitive2d::TransparencePrimitive2D(
3029                                             xSubContent,
3030                                             drawinglayer::primitive2d::Primitive2DSequence(&xTransparence, 1)));
3031                                 }
3032                             }
3033                         }
3034                     }
3035 
3036                     break;
3037                 }
3038                 case META_GRADIENTEX_ACTION :
3039                 {
3040                     /** SIMPLE, DONE */
3041                     // This is only a data holder which is interpreted inside comment actions,
3042                     // see META_COMMENT_ACTION for more info
3043                     // const MetaGradientExAction* pA = (const MetaGradientExAction*)pAction;
3044                     break;
3045                 }
3046                 case META_LAYOUTMODE_ACTION :
3047                 {
3048                     /** SIMPLE, DONE */
3049                     const MetaLayoutModeAction* pA = (const MetaLayoutModeAction*)pAction;
3050                     rPropertyHolders.Current().setLayoutMode(pA->GetLayoutMode());
3051                     break;
3052                 }
3053                 case META_TEXTLANGUAGE_ACTION :
3054                 {
3055                     /** SIMPLE, DONE */
3056                     const MetaTextLanguageAction* pA = (const MetaTextLanguageAction*)pAction;
3057                     rPropertyHolders.Current().setLanguageType(pA->GetTextLanguage());
3058                     break;
3059                 }
3060                 case META_OVERLINECOLOR_ACTION :
3061                 {
3062                     /** SIMPLE, DONE */
3063                     const MetaOverlineColorAction* pA = (const MetaOverlineColorAction*)pAction;
3064                     const bool bActive(pA->IsSetting());
3065 
3066                     rPropertyHolders.Current().setOverlineColorActive(bActive);
3067                     if(bActive)
3068                         rPropertyHolders.Current().setOverlineColor(pA->GetColor().getBColor());
3069 
3070                     break;
3071                 }
3072                 case META_RENDERGRAPHIC_ACTION :
3073                 {
3074                     const MetaRenderGraphicAction* pA = (const MetaRenderGraphicAction*)pAction;
3075                     const Rectangle aRectangle(pA->GetPoint(), pA->GetSize());
3076 
3077                     if(!aRectangle.IsEmpty())
3078                     {
3079                         // create object transform
3080                         basegfx::B2DHomMatrix aObjectTransform;
3081 
3082                         aObjectTransform.set(0, 0, aRectangle.GetWidth());
3083                         aObjectTransform.set(1, 1, aRectangle.GetHeight());
3084                         aObjectTransform.set(0, 2, aRectangle.Left());
3085                         aObjectTransform.set(1, 2, aRectangle.Top());
3086 
3087                         // add current transformation
3088                         aObjectTransform = rPropertyHolders.Current().getTransformation() * aObjectTransform;
3089 
3090                         // embed using EpsPrimitive
3091                         rTargetHolders.Current().append(
3092                             new drawinglayer::primitive2d::RenderGraphicPrimitive2D(
3093                                 pA->GetRenderGraphic(),
3094                                 aObjectTransform ) );
3095                     }
3096 
3097                     break;
3098                 }
3099                 case META_COMMENT_ACTION :
3100                 {
3101                     /** CHECKED, WORKS WELL */
3102                     // I already implemented
3103                     //     XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END
3104                     //     XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END,
3105                     // but opted to remove these again; it works well without them
3106                     // and makes the code less dependent from those Metafile Add-Ons
3107                     const MetaCommentAction* pA = (const MetaCommentAction*)pAction;
3108 
3109                     if(COMPARE_EQUAL == pA->GetComment().CompareIgnoreCaseToAscii("XGRAD_SEQ_BEGIN"))
3110                     {
3111                         // XGRAD_SEQ_BEGIN, XGRAD_SEQ_END should be supported since the
3112                         // pure recorded paint of the gradients uses the XOR paint functionality
3113                         // ('trick'). This is (and will be) broblematic with AntAliasing, so it's
3114                         // better to use this info
3115                         const MetaGradientExAction* pMetaGradientExAction = 0;
3116                         bool bDone(false);
3117                         sal_uInt32 b(nAction + 1);
3118 
3119                         for(; !bDone && b < nCount; b++)
3120                         {
3121                             pAction = rMetaFile.GetAction(b);
3122 
3123                             if(META_GRADIENTEX_ACTION == pAction->GetType())
3124                             {
3125                                 pMetaGradientExAction = (const MetaGradientExAction*)pAction;
3126                             }
3127                             else if(META_COMMENT_ACTION == pAction->GetType())
3128                             {
3129                                 if(COMPARE_EQUAL == ((const MetaCommentAction*)pAction)->GetComment().CompareIgnoreCaseToAscii("XGRAD_SEQ_END"))
3130                                 {
3131                                     bDone = true;
3132                                 }
3133                             }
3134                         }
3135 
3136                         if(bDone && pMetaGradientExAction)
3137                         {
3138                             // consume actions and skip forward
3139                             nAction = b - 1;
3140 
3141                             // get geometry data
3142                             basegfx::B2DPolyPolygon aPolyPolygon(pMetaGradientExAction->GetPolyPolygon().getB2DPolyPolygon());
3143 
3144                             if(aPolyPolygon.count())
3145                             {
3146                                 // transform geometry
3147                                 aPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
3148 
3149                                 // get and check if gradient is a real gradient
3150                                 const Gradient& rGradient = pMetaGradientExAction->GetGradient();
3151                                 const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
3152 
3153                                 if(aAttribute.getStartColor() == aAttribute.getEndColor())
3154                                 {
3155                                     // not really a gradient
3156                                     rTargetHolders.Current().append(
3157                                         new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
3158                                             aPolyPolygon,
3159                                             aAttribute.getStartColor()));
3160                                 }
3161                                 else
3162                                 {
3163                                     // really a gradient
3164                                     rTargetHolders.Current().append(
3165                                         new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D(
3166                                             aPolyPolygon,
3167                                             aAttribute));
3168                                 }
3169                             }
3170                         }
3171                     }
3172 
3173                     break;
3174                 }
3175                 default:
3176                 {
3177                     OSL_ENSURE(false, "Unknown MetaFile Action (!)");
3178                     break;
3179                 }
3180             }
3181         }
3182     }
3183 } // end of anonymous namespace
3184 
3185 //////////////////////////////////////////////////////////////////////////////
3186 
3187 namespace drawinglayer
3188 {
3189     namespace primitive2d
3190     {
3191         Primitive2DSequence MetafilePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
3192         {
3193             // prepare target and porperties; each will have one default entry
3194             TargetHolders aTargetHolders;
3195             PropertyHolders aPropertyHolders;
3196 
3197             // set target MapUnit at Properties
3198             aPropertyHolders.Current().setMapUnit(getMetaFile().GetPrefMapMode().GetMapUnit());
3199 
3200             // interpret the Metafile
3201             interpretMetafile(getMetaFile(), aTargetHolders, aPropertyHolders, rViewInformation);
3202 
3203             // get the content. There should be ony one target, as in the start condition,
3204             // but iterating will be the right thing to do when some push/pop is not closed
3205             Primitive2DSequence xRetval;
3206 
3207             while(aTargetHolders.size() > 1)
3208             {
3209                 appendPrimitive2DSequenceToPrimitive2DSequence(xRetval,
3210                     aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
3211                 aTargetHolders.Pop();
3212             }
3213 
3214             appendPrimitive2DSequenceToPrimitive2DSequence(xRetval,
3215                 aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
3216 
3217             if(xRetval.hasElements())
3218             {
3219                 // get target size
3220                 const Rectangle aMtfTarget(getMetaFile().GetPrefMapMode().GetOrigin(), getMetaFile().GetPrefSize());
3221 
3222                 // create transformation
3223                 basegfx::B2DHomMatrix aAdaptedTransform;
3224 
3225                 aAdaptedTransform.translate(-aMtfTarget.Left(), -aMtfTarget.Top());
3226                 aAdaptedTransform.scale(
3227                     aMtfTarget.getWidth() ? 1.0 / aMtfTarget.getWidth() : 1.0,
3228                     aMtfTarget.getHeight() ? 1.0 / aMtfTarget.getHeight() : 1.0);
3229                 aAdaptedTransform = getTransform() * aAdaptedTransform;
3230 
3231                 // embed to target transformation
3232                 const Primitive2DReference aEmbeddedTransform(
3233                     new TransformPrimitive2D(
3234                         aAdaptedTransform,
3235                         xRetval));
3236 
3237                 xRetval = Primitive2DSequence(&aEmbeddedTransform, 1);
3238             }
3239 
3240             return xRetval;
3241         }
3242 
3243         MetafilePrimitive2D::MetafilePrimitive2D(
3244             const basegfx::B2DHomMatrix& rMetaFileTransform,
3245             const GDIMetaFile& rMetaFile)
3246         :   BufferedDecompositionPrimitive2D(),
3247             maMetaFileTransform(rMetaFileTransform),
3248             maMetaFile(rMetaFile)
3249         {
3250         }
3251 
3252         bool MetafilePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
3253         {
3254             if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
3255             {
3256                 const MetafilePrimitive2D& rCompare = (MetafilePrimitive2D&)rPrimitive;
3257 
3258                 return (getTransform() == rCompare.getTransform()
3259                     && getMetaFile() == rCompare.getMetaFile());
3260             }
3261 
3262             return false;
3263         }
3264 
3265         basegfx::B2DRange MetafilePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
3266         {
3267             // use own implementation to quickly answer the getB2DRange question. The
3268             // MetafilePrimitive2D assumes that all geometry is inside of the shape. If
3269             // this is not the case (i have already seen some wrong Metafiles) it should
3270             // be embedded to a MaskPrimitive2D
3271             basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
3272             aRetval.transform(getTransform());
3273 
3274             return aRetval;
3275         }
3276 
3277         // provide unique ID
3278         ImplPrimitrive2DIDBlock(MetafilePrimitive2D, PRIMITIVE2D_ID_METAFILEPRIMITIVE2D)
3279 
3280     } // end of namespace primitive2d
3281 } // end of namespace drawinglayer
3282 
3283 //////////////////////////////////////////////////////////////////////////////
3284 // eof
3285