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