xref: /trunk/main/drawinglayer/source/primitive2d/metafileprimitive2d.cxx (revision ffd38472365e95f6a578737bc9a5eb0fac624a86)
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 properties
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 Primitive target vector. It
403         encapsulates the new/delete functionality and allows 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 transparency 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         which change the Region of the current context. It takes care of creating the
956         current embedded 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 unnecessarily 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 changing 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::BColorModifierSharedPtr(
1067                                 new basegfx::BColorModifier_replace(
1068                                     basegfx::BColor(0.0, 0.0, 0.0)))));
1069                 }
1070                 else // if(rPropertyHolders.Current().isRasterOpInvert())
1071                 {
1072                     // invert content
1073                     rTargetHolders.Current().append(
1074                         new drawinglayer::primitive2d::InvertPrimitive2D(
1075                             aSubContent));
1076                 }
1077             }
1078         }
1079 
1080         // apply new settings
1081         rPropertyHolders.Current().setRasterOp(aRasterOp);
1082 
1083         // check if now active
1084         if(rPropertyHolders.Current().isRasterOpActive())
1085         {
1086             // prepare new content holder for new invert
1087             rTargetHolders.Push();
1088         }
1089     }
1090 
1091     /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
1092         It is a quite mighty action. This helper is for simple color filled background.
1093      */
1094     drawinglayer::primitive2d::BasePrimitive2D* CreateColorWallpaper(
1095         const basegfx::B2DRange& rRange,
1096         const basegfx::BColor& rColor,
1097         PropertyHolder& rPropertyHolder)
1098     {
1099         basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(rRange));
1100         aOutline.transform(rPropertyHolder.getTransformation());
1101 
1102         return new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
1103             basegfx::B2DPolyPolygon(aOutline),
1104             rColor);
1105     }
1106 
1107     /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
1108         It is a quite mighty action. This helper is for gradient filled background.
1109      */
1110     drawinglayer::primitive2d::BasePrimitive2D* CreateGradientWallpaper(
1111         const basegfx::B2DRange& rRange,
1112         const Gradient& rGradient,
1113         PropertyHolder& rPropertyHolder)
1114     {
1115         const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
1116 
1117         if(aAttribute.getStartColor() == aAttribute.getEndColor())
1118         {
1119             // not really a gradient. Create filled rectangle
1120             return CreateColorWallpaper(rRange, aAttribute.getStartColor(), rPropertyHolder);
1121         }
1122         else
1123         {
1124             // really a gradient
1125             drawinglayer::primitive2d::BasePrimitive2D* pRetval =
1126                 new drawinglayer::primitive2d::FillGradientPrimitive2D(
1127                     rRange,
1128                     aAttribute);
1129 
1130             if(!rPropertyHolder.getTransformation().isIdentity())
1131             {
1132                 const drawinglayer::primitive2d::Primitive2DReference xPrim(pRetval);
1133                 const drawinglayer::primitive2d::Primitive2DSequence xSeq(&xPrim, 1);
1134 
1135                 pRetval = new drawinglayer::primitive2d::TransformPrimitive2D(
1136                     rPropertyHolder.getTransformation(),
1137                     xSeq);
1138             }
1139 
1140             return pRetval;
1141         }
1142     }
1143 
1144     /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
1145         It is a quite mighty action. This helper decides if color and/or gradient
1146         background is needed for the wanted bitmap fill and then creates the needed
1147         WallpaperBitmapPrimitive2D. This primitive was created for this purpose and
1148         takes over all needed logic of orientations and tiling.
1149      */
1150     void CreateAndAppendBitmapWallpaper(
1151         basegfx::B2DRange aWallpaperRange,
1152         const Wallpaper& rWallpaper,
1153         TargetHolder& rTarget,
1154         PropertyHolder& rProperty)
1155     {
1156         const BitmapEx aBitmapEx(rWallpaper.GetBitmap());
1157         const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle());
1158 
1159         // if bitmap visualisation is transparent, maybe background
1160         // needs to be filled. Create background
1161         if(aBitmapEx.IsTransparent()
1162             || (WALLPAPER_TILE != eWallpaperStyle && WALLPAPER_SCALE != eWallpaperStyle))
1163         {
1164             if(rWallpaper.IsGradient())
1165             {
1166                 rTarget.append(
1167                     CreateGradientWallpaper(
1168                         aWallpaperRange,
1169                         rWallpaper.GetGradient(),
1170                         rProperty));
1171             }
1172             else if(!rWallpaper.GetColor().GetTransparency())
1173             {
1174                 rTarget.append(
1175                     CreateColorWallpaper(
1176                         aWallpaperRange,
1177                         rWallpaper.GetColor().getBColor(),
1178                         rProperty));
1179             }
1180         }
1181 
1182         // use wallpaper rect if set
1183         if(rWallpaper.IsRect() && !rWallpaper.GetRect().IsEmpty())
1184         {
1185             aWallpaperRange = basegfx::B2DRange(
1186                 rWallpaper.GetRect().Left(), rWallpaper.GetRect().Top(),
1187                 rWallpaper.GetRect().Right(), rWallpaper.GetRect().Bottom());
1188         }
1189 
1190         drawinglayer::primitive2d::BasePrimitive2D* pBitmapWallpaperFill =
1191             new drawinglayer::primitive2d::WallpaperBitmapPrimitive2D(
1192                 aWallpaperRange,
1193                 aBitmapEx,
1194                 eWallpaperStyle);
1195 
1196         if(rProperty.getTransformation().isIdentity())
1197         {
1198             // add directly
1199             rTarget.append(pBitmapWallpaperFill);
1200         }
1201         else
1202         {
1203             // when a transformation is set, embed to it
1204             const drawinglayer::primitive2d::Primitive2DReference xPrim(pBitmapWallpaperFill);
1205 
1206             rTarget.append(
1207                 new drawinglayer::primitive2d::TransformPrimitive2D(
1208                     rProperty.getTransformation(),
1209                     drawinglayer::primitive2d::Primitive2DSequence(&xPrim, 1)));
1210         }
1211     }
1212 
1213     /** helper to decide UnderlineAbove for text primitives */
1214     bool isUnderlineAbove(const Font& rFont)
1215     {
1216         if(!rFont.IsVertical())
1217         {
1218             return false;
1219         }
1220 
1221         if((LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()))
1222         {
1223             // the underline is right for Japanese only
1224             return true;
1225         }
1226 
1227         return false;
1228     }
1229 
1230     void createFontAttributeTransformAndAlignment(
1231         drawinglayer::attribute::FontAttribute& rFontAttribute,
1232         basegfx::B2DHomMatrix& rTextTransform,
1233         basegfx::B2DVector& rAlignmentOffset,
1234         PropertyHolder& rProperty)
1235     {
1236         const Font& rFont = rProperty.getFont();
1237         basegfx::B2DVector aFontScaling;
1238 
1239         rFontAttribute = drawinglayer::attribute::FontAttribute(
1240             drawinglayer::primitive2d::getFontAttributeFromVclFont(
1241                 aFontScaling,
1242                 rFont,
1243                 0 != (rProperty.getLayoutMode() & TEXT_LAYOUT_BIDI_RTL),
1244                 0 != (rProperty.getLayoutMode() & TEXT_LAYOUT_BIDI_STRONG)));
1245 
1246         // add FontScaling
1247         rTextTransform.scale(aFontScaling.getX(), aFontScaling.getY());
1248 
1249         // take text align into account
1250         if(ALIGN_BASELINE != rFont.GetAlign())
1251         {
1252             drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
1253             aTextLayouterDevice.setFont(rFont);
1254 
1255             if(ALIGN_TOP == rFont.GetAlign())
1256             {
1257                 rAlignmentOffset.setY(aTextLayouterDevice.getFontAscent());
1258             }
1259             else // ALIGN_BOTTOM
1260             {
1261                 rAlignmentOffset.setY(-aTextLayouterDevice.getFontDescent());
1262             }
1263 
1264             rTextTransform.translate(rAlignmentOffset.getX(), rAlignmentOffset.getY());
1265         }
1266 
1267         // add FontRotation (if used)
1268         if(rFont.GetOrientation())
1269         {
1270             rTextTransform.rotate(-rFont.GetOrientation() * F_PI1800);
1271         }
1272     }
1273 
1274     /** helper which takes complete care for creating the needed text primitives. It
1275         takes care of decorated stuff and all the geometry adaptions needed
1276      */
1277     void proccessMetaTextAction(
1278         const Point& rTextStartPosition,
1279         const XubString& rText,
1280         sal_uInt16 nTextStart,
1281         sal_uInt16 nTextLength,
1282         const ::std::vector< double >& rDXArray,
1283         TargetHolder& rTarget,
1284         PropertyHolder& rProperty)
1285     {
1286         drawinglayer::primitive2d::BasePrimitive2D* pResult = 0;
1287         const Font& rFont = rProperty.getFont();
1288         basegfx::B2DVector aAlignmentOffset(0.0, 0.0);
1289 
1290         if(nTextLength)
1291         {
1292             drawinglayer::attribute::FontAttribute aFontAttribute;
1293             basegfx::B2DHomMatrix aTextTransform;
1294 
1295             // fill parameters derived from current font
1296             createFontAttributeTransformAndAlignment(
1297                 aFontAttribute,
1298                 aTextTransform,
1299                 aAlignmentOffset,
1300                 rProperty);
1301 
1302             // add TextStartPosition
1303             aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y());
1304 
1305             // prepare FontColor and Locale
1306             const basegfx::BColor aFontColor(rProperty.getTextColor());
1307             const com::sun::star::lang::Locale aLocale(MsLangId::convertLanguageToLocale(rProperty.getLanguageType()));
1308             const bool bWordLineMode(rFont.IsWordLineMode());
1309 
1310             const bool bDecoratedIsNeeded(
1311                    UNDERLINE_NONE != rFont.GetOverline()
1312                 || UNDERLINE_NONE != rFont.GetUnderline()
1313                 || STRIKEOUT_NONE != rFont.GetStrikeout()
1314                 || EMPHASISMARK_NONE != (rFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
1315                 || RELIEF_NONE != rFont.GetRelief()
1316                 || rFont.IsShadow()
1317                 || bWordLineMode);
1318 
1319             if(bDecoratedIsNeeded)
1320             {
1321                 // prepare overline, underline and strikeout data
1322                 const drawinglayer::primitive2d::TextLine eFontOverline(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rFont.GetOverline()));
1323                 const drawinglayer::primitive2d::TextLine eFontUnderline(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rFont.GetUnderline()));
1324                 const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rFont.GetStrikeout()));
1325 
1326                 // check UnderlineAbove
1327                 const bool bUnderlineAbove(drawinglayer::primitive2d::TEXT_LINE_NONE != eFontUnderline && isUnderlineAbove(rFont));
1328 
1329                 // prepare emphasis mark data
1330                 drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE);
1331 
1332                 switch(rFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
1333                 {
1334                     case EMPHASISMARK_DOT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DOT; break;
1335                     case EMPHASISMARK_CIRCLE : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_CIRCLE; break;
1336                     case EMPHASISMARK_DISC : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DISC; break;
1337                     case EMPHASISMARK_ACCENT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_ACCENT; break;
1338                 }
1339 
1340                 const bool bEmphasisMarkAbove(rFont.GetEmphasisMark() & EMPHASISMARK_POS_ABOVE);
1341                 const bool bEmphasisMarkBelow(rFont.GetEmphasisMark() & EMPHASISMARK_POS_BELOW);
1342 
1343                 // prepare font relief data
1344                 drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
1345 
1346                 switch(rFont.GetRelief())
1347                 {
1348                     case RELIEF_EMBOSSED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
1349                     case RELIEF_ENGRAVED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
1350                     default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
1351                 }
1352 
1353                 // prepare shadow/outline data
1354                 const bool bShadow(rFont.IsShadow());
1355 
1356                 // TextDecoratedPortionPrimitive2D is needed, create one
1357                 pResult = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
1358 
1359                     // attributes for TextSimplePortionPrimitive2D
1360                     aTextTransform,
1361                     rText,
1362                     nTextStart,
1363                     nTextLength,
1364                     rDXArray,
1365                     aFontAttribute,
1366                     aLocale,
1367                     aFontColor,
1368 
1369                     // attributes for TextDecoratedPortionPrimitive2D
1370                     rProperty.getOverlineColorActive() ? rProperty.getOverlineColor() : aFontColor,
1371                     rProperty.getTextLineColorActive() ? rProperty.getTextLineColor() : aFontColor,
1372                     eFontOverline,
1373                     eFontUnderline,
1374                     bUnderlineAbove,
1375                     eTextStrikeout,
1376                     bWordLineMode,
1377                     eTextEmphasisMark,
1378                     bEmphasisMarkAbove,
1379                     bEmphasisMarkBelow,
1380                     eTextRelief,
1381                     bShadow);
1382             }
1383             else
1384             {
1385                 // TextSimplePortionPrimitive2D is enough
1386                 pResult = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
1387                     aTextTransform,
1388                     rText,
1389                     nTextStart,
1390                     nTextLength,
1391                     rDXArray,
1392                     aFontAttribute,
1393                     aLocale,
1394                     aFontColor);
1395             }
1396         }
1397 
1398         if(pResult && rProperty.getTextFillColorActive())
1399         {
1400             // text background is requested, add and encapsulate both to new primitive
1401             drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
1402             aTextLayouterDevice.setFont(rFont);
1403 
1404             // get text width
1405             double fTextWidth(0.0);
1406 
1407             if(rDXArray.empty())
1408             {
1409                 fTextWidth = aTextLayouterDevice.getTextWidth(rText, nTextStart, nTextLength);
1410             }
1411             else
1412             {
1413                 fTextWidth = rDXArray.back();
1414             }
1415 
1416             if(basegfx::fTools::more(fTextWidth, 0.0))
1417             {
1418                 // build text range
1419                 const basegfx::B2DRange aTextRange(
1420                     0.0, -aTextLayouterDevice.getFontAscent(),
1421                     fTextWidth, aTextLayouterDevice.getFontDescent());
1422 
1423                 // create Transform
1424                 basegfx::B2DHomMatrix aTextTransform;
1425 
1426                 aTextTransform.translate(aAlignmentOffset.getX(), aAlignmentOffset.getY());
1427 
1428                 if(rFont.GetOrientation())
1429                 {
1430                     aTextTransform.rotate(-rFont.GetOrientation() * F_PI1800);
1431                 }
1432 
1433                 aTextTransform.translate(rTextStartPosition.X(), rTextStartPosition.Y());
1434 
1435                 // prepare Primitive2DSequence, put text in foreground
1436                 drawinglayer::primitive2d::Primitive2DSequence aSequence(2);
1437                 aSequence[1] = drawinglayer::primitive2d::Primitive2DReference(pResult);
1438 
1439                 // prepare filled polygon
1440                 basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aTextRange));
1441                 aOutline.transform(aTextTransform);
1442 
1443                 aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(
1444                     new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
1445                         basegfx::B2DPolyPolygon(aOutline),
1446                         rProperty.getTextFillColor()));
1447 
1448                 // set as group at pResult
1449                 pResult = new drawinglayer::primitive2d::GroupPrimitive2D(aSequence);
1450             }
1451         }
1452 
1453         if(pResult)
1454         {
1455             // add created text primitive to target
1456             if(rProperty.getTransformation().isIdentity())
1457             {
1458                 rTarget.append(pResult);
1459             }
1460             else
1461             {
1462                 // when a transformation is set, embed to it
1463                 const drawinglayer::primitive2d::Primitive2DReference aReference(pResult);
1464 
1465                 rTarget.append(
1466                     new drawinglayer::primitive2d::TransformPrimitive2D(
1467                         rProperty.getTransformation(),
1468                         drawinglayer::primitive2d::Primitive2DSequence(&aReference, 1)));
1469             }
1470         }
1471     }
1472 
1473     /** helper which takes complete care for creating the needed textLine primitives */
1474     void proccessMetaTextLineAction(
1475         const MetaTextLineAction& rAction,
1476         TargetHolder& rTarget,
1477         PropertyHolder& rProperty)
1478     {
1479         const double fLineWidth(fabs((double)rAction.GetWidth()));
1480 
1481         if(fLineWidth > 0.0)
1482         {
1483             const drawinglayer::primitive2d::TextLine aOverlineMode(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rAction.GetOverline()));
1484             const drawinglayer::primitive2d::TextLine aUnderlineMode(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rAction.GetUnderline()));
1485             const drawinglayer::primitive2d::TextStrikeout aTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rAction.GetStrikeout()));
1486 
1487             const bool bOverlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE != aOverlineMode);
1488             const bool bUnderlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE != aUnderlineMode);
1489             const bool bStrikeoutUsed(drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE != aTextStrikeout);
1490 
1491             if(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed)
1492             {
1493                 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aTargetVector;
1494                 basegfx::B2DVector aAlignmentOffset(0.0, 0.0);
1495                 drawinglayer::attribute::FontAttribute aFontAttribute;
1496                 basegfx::B2DHomMatrix aTextTransform;
1497 
1498                 // fill parameters derived from current font
1499                 createFontAttributeTransformAndAlignment(
1500                     aFontAttribute,
1501                     aTextTransform,
1502                     aAlignmentOffset,
1503                     rProperty);
1504 
1505                 // add TextStartPosition
1506                 aTextTransform.translate(rAction.GetStartPoint().X(), rAction.GetStartPoint().Y());
1507 
1508                 // prepare TextLayouter (used in most cases)
1509                 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
1510                 aTextLayouter.setFont(rProperty.getFont());
1511 
1512                 if(bOverlineUsed)
1513                 {
1514                     // create primitive geometry for overline
1515                     aTargetVector.push_back(
1516                         new drawinglayer::primitive2d::TextLinePrimitive2D(
1517                             aTextTransform,
1518                             fLineWidth,
1519                             aTextLayouter.getOverlineOffset(),
1520                             aTextLayouter.getOverlineHeight(),
1521                             aOverlineMode,
1522                             rProperty.getOverlineColor()));
1523                 }
1524 
1525                 if(bUnderlineUsed)
1526                 {
1527                     // create primitive geometry for underline
1528                     aTargetVector.push_back(
1529                         new drawinglayer::primitive2d::TextLinePrimitive2D(
1530                             aTextTransform,
1531                             fLineWidth,
1532                             aTextLayouter.getUnderlineOffset(),
1533                             aTextLayouter.getUnderlineHeight(),
1534                             aUnderlineMode,
1535                             rProperty.getTextLineColor()));
1536                 }
1537 
1538                 if(bStrikeoutUsed)
1539                 {
1540                     // create primitive geometry for strikeout
1541                     if(drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout
1542                         || drawinglayer::primitive2d::TEXT_STRIKEOUT_X == aTextStrikeout)
1543                     {
1544                         // strikeout with character
1545                         const sal_Unicode aStrikeoutChar(
1546                             drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH == aTextStrikeout ? '/' : 'X');
1547                         const com::sun::star::lang::Locale aLocale(MsLangId::convertLanguageToLocale(
1548                             rProperty.getLanguageType()));
1549 
1550                         aTargetVector.push_back(
1551                             new drawinglayer::primitive2d::TextCharacterStrikeoutPrimitive2D(
1552                                 aTextTransform,
1553                                 fLineWidth,
1554                                 rProperty.getTextColor(),
1555                                 aStrikeoutChar,
1556                                 aFontAttribute,
1557                                 aLocale));
1558                     }
1559                     else
1560                     {
1561                         // strikeout with geometry
1562                         aTargetVector.push_back(
1563                             new drawinglayer::primitive2d::TextGeometryStrikeoutPrimitive2D(
1564                                 aTextTransform,
1565                                 fLineWidth,
1566                                 rProperty.getTextColor(),
1567                                 aTextLayouter.getUnderlineHeight(),
1568                                 aTextLayouter.getStrikeoutOffset(),
1569                                 aTextStrikeout));
1570                     }
1571                 }
1572 
1573                 if(aTargetVector.size())
1574                 {
1575                     // add created text primitive to target
1576                     if(rProperty.getTransformation().isIdentity())
1577                     {
1578                         for(sal_uInt32 a(0); a < aTargetVector.size(); a++)
1579                         {
1580                             rTarget.append(aTargetVector[a]);
1581                         }
1582                     }
1583                     else
1584                     {
1585                         // when a transformation is set, embed to it
1586                         drawinglayer::primitive2d::Primitive2DSequence xTargets(aTargetVector.size());
1587 
1588                         for(sal_uInt32 a(0); a < aTargetVector.size(); a++)
1589                         {
1590                             xTargets[a] = drawinglayer::primitive2d::Primitive2DReference(aTargetVector[a]);
1591                         }
1592 
1593                         rTarget.append(
1594                             new drawinglayer::primitive2d::TransformPrimitive2D(
1595                                 rProperty.getTransformation(),
1596                                 xTargets));
1597                     }
1598                 }
1599             }
1600         }
1601 
1602     }
1603 
1604     /** This is the main interpreter method. It is designed to handle the given Metafile
1605         completely inside the given context and target. It may use and modify the context and
1606         target. This design allows to call itself recursively which adapted contexts and
1607         targets as e.g. needed for the META_FLOATTRANSPARENT_ACTION where the content is expressed
1608         as a metafile as sub-content.
1609 
1610         This interpreter is as free of VCL functionality as possible. It uses VCL data classes
1611         (else reading the data would not be possible), but e.g. does NOT use a local OutputDevice
1612         as most other MetaFile interpreters/exporters do to hold and work with the current context.
1613         This is necessary to be able to get away from the strong internal VCL-binding.
1614 
1615         It tries to combine e.g. pixel and/or point actions and to stitch together single line primitives
1616         where possible (which is not trivial with the possible line geometry definitions).
1617 
1618         It tries to handle clipping no longer as Regions and spans of Rectangles, but as PolyPolygon
1619         ClipRegions with (where possible) high precision by using the best possible data quality
1620         from the Region. The Region is unavoidable as data container, but nowadays allows the transport
1621         of Polygon-based clip regions. Where this is not used, a Polygon is constructed from the
1622         Region ranges. All primitive clipping uses the MaskPrimitive2D with Polygon-based clipping.
1623 
1624         I have marked the single MetaActions with:
1625 
1626         SIMPLE, DONE:
1627         Simple, e.g. nothing to do or value setting in the context
1628 
1629         CHECKED, WORKS WELL:
1630         Thoroughly tested with extra written test code which created a replacement
1631         Metafile just to test this action in various combinations
1632 
1633         NEEDS IMPLEMENTATION:
1634         Not implemented and asserted, but also no usage found, neither in own Metafile
1635         creations, nor in EMF/WMF imports (checked with a whole bunch of critical EMF/WMF
1636         bugdocs)
1637 
1638         For more comments, see the single action implementations.
1639     */
1640     void interpretMetafile(
1641         const GDIMetaFile& rMetaFile,
1642         TargetHolders& rTargetHolders,
1643         PropertyHolders& rPropertyHolders,
1644         const drawinglayer::geometry::ViewInformation2D& rViewInformation)
1645     {
1646         const sal_uInt32 nCount(rMetaFile.GetActionCount());
1647 
1648         for(sal_uInt32 nAction(0); nAction < nCount; nAction++)
1649         {
1650             MetaAction* pAction = rMetaFile.GetAction(nAction);
1651 
1652             switch(pAction->GetType())
1653             {
1654                 case META_NULL_ACTION :
1655                 {
1656                     /** SIMPLE, DONE */
1657                     break;
1658                 }
1659                 case META_PIXEL_ACTION :
1660                 {
1661                     /** CHECKED, WORKS WELL */
1662                     std::vector< basegfx::B2DPoint > aPositions;
1663                     Color aLastColor(COL_BLACK);
1664 
1665                     while(META_PIXEL_ACTION == pAction->GetType() && nAction < nCount)
1666                     {
1667                         const MetaPixelAction* pA = (const MetaPixelAction*)pAction;
1668 
1669                         if(pA->GetColor() != aLastColor)
1670                         {
1671                             if(aPositions.size())
1672                             {
1673                                 createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
1674                                 aPositions.clear();
1675                             }
1676 
1677                             aLastColor = pA->GetColor();
1678                         }
1679 
1680                         const Point& rPoint = pA->GetPoint();
1681                         aPositions.push_back(basegfx::B2DPoint(rPoint.X(), rPoint.Y()));
1682                         nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
1683                     }
1684 
1685                     nAction--;
1686 
1687                     if(aPositions.size())
1688                     {
1689                         createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
1690                     }
1691 
1692                     break;
1693                 }
1694                 case META_POINT_ACTION :
1695                 {
1696                     /** CHECKED, WORKS WELL */
1697                     if(rPropertyHolders.Current().getLineColorActive())
1698                     {
1699                         std::vector< basegfx::B2DPoint > aPositions;
1700 
1701                         while(META_POINT_ACTION == pAction->GetType() && nAction < nCount)
1702                         {
1703                             const MetaPointAction* pA = (const MetaPointAction*)pAction;
1704                             const Point& rPoint = pA->GetPoint();
1705                             aPositions.push_back(basegfx::B2DPoint(rPoint.X(), rPoint.Y()));
1706                             nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
1707                         }
1708 
1709                         nAction--;
1710 
1711                         if(aPositions.size())
1712                         {
1713                             createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), rPropertyHolders.Current().getLineColor());
1714                         }
1715                     }
1716 
1717                     break;
1718                 }
1719                 case META_LINE_ACTION :
1720                 {
1721                     /** CHECKED, WORKS WELL */
1722                     if(rPropertyHolders.Current().getLineColorActive())
1723                     {
1724                         basegfx::B2DPolygon aLinePolygon;
1725                         LineInfo aLineInfo;
1726 
1727                         while(META_LINE_ACTION == pAction->GetType() && nAction < nCount)
1728                         {
1729                             const MetaLineAction* pA = (const MetaLineAction*)pAction;
1730                             const Point& rStartPoint = pA->GetStartPoint();
1731                             const Point& rEndPoint = pA->GetEndPoint();
1732                             const basegfx::B2DPoint aStart(rStartPoint.X(), rStartPoint.Y());
1733                             const basegfx::B2DPoint aEnd(rEndPoint.X(), rEndPoint.Y());
1734 
1735                             if(aLinePolygon.count())
1736                             {
1737                                 if(pA->GetLineInfo() == aLineInfo
1738                                     && aStart == aLinePolygon.getB2DPoint(aLinePolygon.count() - 1))
1739                                 {
1740                                     aLinePolygon.append(aEnd);
1741                                 }
1742                                 else
1743                                 {
1744                                     aLineInfo.SetLineJoin(basegfx::B2DLINEJOIN_NONE); // It were lines; force to NONE
1745                                     createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current());
1746                                     aLinePolygon.clear();
1747                                     aLineInfo = pA->GetLineInfo();
1748                                     aLinePolygon.append(aStart);
1749                                     aLinePolygon.append(aEnd);
1750                                 }
1751                             }
1752                             else
1753                             {
1754                                 aLineInfo = pA->GetLineInfo();
1755                                 aLinePolygon.append(aStart);
1756                                 aLinePolygon.append(aEnd);
1757                             }
1758 
1759                             nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
1760                         }
1761 
1762                         nAction--;
1763 
1764                         if(aLinePolygon.count())
1765                         {
1766                             aLineInfo.SetLineJoin(basegfx::B2DLINEJOIN_NONE); // It were lines; force to NONE
1767                             createLinePrimitive(aLinePolygon, aLineInfo, rTargetHolders.Current(), rPropertyHolders.Current());
1768                         }
1769                     }
1770 
1771                     break;
1772                 }
1773                 case META_RECT_ACTION :
1774                 {
1775                     /** CHECKED, WORKS WELL */
1776                     if(rPropertyHolders.Current().getLineOrFillActive())
1777                     {
1778                         const MetaRectAction* pA = (const MetaRectAction*)pAction;
1779                         const Rectangle& rRectangle = pA->GetRect();
1780 
1781                         if(!rRectangle.IsEmpty())
1782                         {
1783                             const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
1784 
1785                             if(!aRange.isEmpty())
1786                             {
1787                                 const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
1788                                 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1789                             }
1790                         }
1791                     }
1792 
1793                     break;
1794                 }
1795                 case META_ROUNDRECT_ACTION :
1796                 {
1797                     /** CHECKED, WORKS WELL */
1798                     /** The original OutputDevice::DrawRect paints nothing when nHor or nVer is zero; but just
1799                         because the tools::Polygon operator creating the rounding does produce nonsense. I assume
1800                         this an error and create an unrounded rectangle in that case (implicit in
1801                         createPolygonFromRect)
1802                      */
1803                     if(rPropertyHolders.Current().getLineOrFillActive())
1804                     {
1805                         const MetaRoundRectAction* pA = (const MetaRoundRectAction*)pAction;
1806                         const Rectangle& rRectangle = pA->GetRect();
1807 
1808                         if(!rRectangle.IsEmpty())
1809                         {
1810                             const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
1811 
1812                             if(!aRange.isEmpty())
1813                             {
1814                                 const sal_uInt32 nHor(pA->GetHorzRound());
1815                                 const sal_uInt32 nVer(pA->GetVertRound());
1816                                 basegfx::B2DPolygon aOutline;
1817 
1818                                 if(nHor || nVer)
1819                                 {
1820                                     double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0));
1821                                     double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0));
1822                                     fRadiusX = std::max(0.0, std::min(1.0, fRadiusX));
1823                                     fRadiusY = std::max(0.0, std::min(1.0, fRadiusY));
1824 
1825                                     aOutline = basegfx::tools::createPolygonFromRect(aRange, fRadiusX, fRadiusY);
1826                                 }
1827                                 else
1828                                 {
1829                                     aOutline = basegfx::tools::createPolygonFromRect(aRange);
1830                                 }
1831 
1832                                 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1833                             }
1834                         }
1835                     }
1836 
1837                     break;
1838                 }
1839                 case META_ELLIPSE_ACTION :
1840                 {
1841                     /** CHECKED, WORKS WELL */
1842                     if(rPropertyHolders.Current().getLineOrFillActive())
1843                     {
1844                         const MetaEllipseAction* pA = (const MetaEllipseAction*)pAction;
1845                         const Rectangle& rRectangle = pA->GetRect();
1846 
1847                         if(!rRectangle.IsEmpty())
1848                         {
1849                             const basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
1850 
1851                             if(!aRange.isEmpty())
1852                             {
1853                                 const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromEllipse(
1854                                     aRange.getCenter(), aRange.getWidth() * 0.5, aRange.getHeight() * 0.5));
1855 
1856                                 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1857                             }
1858                         }
1859                     }
1860 
1861                     break;
1862                 }
1863                 case META_ARC_ACTION :
1864                 {
1865                     /** CHECKED, WORKS WELL */
1866                     if(rPropertyHolders.Current().getLineColorActive())
1867                     {
1868                         const MetaArcAction* pA = (const MetaArcAction*)pAction;
1869                         const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_ARC);
1870                         const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
1871 
1872                         createHairlinePrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1873                     }
1874 
1875                     break;
1876                 }
1877                 case META_PIE_ACTION :
1878                 {
1879                     /** CHECKED, WORKS WELL */
1880                     if(rPropertyHolders.Current().getLineOrFillActive())
1881                     {
1882                         const MetaPieAction* pA = (const MetaPieAction*)pAction;
1883                         const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_PIE);
1884                         const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
1885 
1886                         createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1887                     }
1888 
1889                     break;
1890                 }
1891                 case META_CHORD_ACTION :
1892                 {
1893                     /** CHECKED, WORKS WELL */
1894                     if(rPropertyHolders.Current().getLineOrFillActive())
1895                     {
1896                         const MetaChordAction* pA = (const MetaChordAction*)pAction;
1897                         const Polygon aToolsPoly(pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), POLY_CHORD);
1898                         const basegfx::B2DPolygon aOutline(aToolsPoly.getB2DPolygon());
1899 
1900                         createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1901                     }
1902 
1903                     break;
1904                 }
1905                 case META_POLYLINE_ACTION :
1906                 {
1907                     /** CHECKED, WORKS WELL */
1908                     if(rPropertyHolders.Current().getLineColorActive())
1909                     {
1910                         const MetaPolyLineAction* pA = (const MetaPolyLineAction*)pAction;
1911                         createLinePrimitive(pA->GetPolygon().getB2DPolygon(), pA->GetLineInfo(), rTargetHolders.Current(), rPropertyHolders.Current());
1912                     }
1913 
1914                     break;
1915                 }
1916                 case META_POLYGON_ACTION :
1917                 {
1918                     /** CHECKED, WORKS WELL */
1919                     if(rPropertyHolders.Current().getLineOrFillActive())
1920                     {
1921                         const MetaPolygonAction* pA = (const MetaPolygonAction*)pAction;
1922                         basegfx::B2DPolygon aOutline(pA->GetPolygon().getB2DPolygon());
1923 
1924                         // the metafile play interprets the polygons from MetaPolygonAction
1925                         // always as closed and always paints an edge from last to first point,
1926                         // so force to closed here to emulate that
1927                         if(aOutline.count() > 1 && !aOutline.isClosed())
1928                         {
1929                             aOutline.setClosed(true);
1930                         }
1931 
1932                         createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1933                     }
1934 
1935                     break;
1936                 }
1937                 case META_POLYPOLYGON_ACTION :
1938                 {
1939                     /** CHECKED, WORKS WELL */
1940                     if(rPropertyHolders.Current().getLineOrFillActive())
1941                     {
1942                         const MetaPolyPolygonAction* pA = (const MetaPolyPolygonAction*)pAction;
1943                         basegfx::B2DPolyPolygon aPolyPolygonOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
1944 
1945                         // the metafile play interprets the single polygons from MetaPolyPolygonAction
1946                         // always as closed and always paints an edge from last to first point,
1947                         // so force to closed here to emulate that
1948                         for(sal_uInt32 b(0); b < aPolyPolygonOutline.count(); b++)
1949                         {
1950                             basegfx::B2DPolygon aPolygonOutline(aPolyPolygonOutline.getB2DPolygon(b));
1951 
1952                             if(aPolygonOutline.count() > 1 && !aPolygonOutline.isClosed())
1953                             {
1954                                 aPolygonOutline.setClosed(true);
1955                                 aPolyPolygonOutline.setB2DPolygon(b, aPolygonOutline);
1956                             }
1957                         }
1958 
1959                         createHairlineAndFillPrimitive(aPolyPolygonOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1960                     }
1961 
1962                     break;
1963                 }
1964                 case META_TEXT_ACTION :
1965                 {
1966                     /** CHECKED, WORKS WELL */
1967                     const MetaTextAction* pA = (const MetaTextAction*)pAction;
1968                     sal_uInt32 nTextLength(pA->GetLen());
1969                     const sal_uInt32 nTextIndex(pA->GetIndex());
1970                     const sal_uInt32 nStringLength(pA->GetText().Len());
1971 
1972                     if(nTextLength + nTextIndex > nStringLength)
1973                     {
1974                         nTextLength = nStringLength - nTextIndex;
1975                     }
1976 
1977                     if(nTextLength && rPropertyHolders.Current().getTextColorActive())
1978                     {
1979                         const std::vector< double > aDXArray;
1980                         proccessMetaTextAction(
1981                             pA->GetPoint(),
1982                             pA->GetText(),
1983                             nTextIndex,
1984                             nTextLength,
1985                             aDXArray,
1986                             rTargetHolders.Current(),
1987                             rPropertyHolders.Current());
1988                     }
1989 
1990                     break;
1991                 }
1992                 case META_TEXTARRAY_ACTION :
1993                 {
1994                     /** CHECKED, WORKS WELL */
1995                     const MetaTextArrayAction* pA = (const MetaTextArrayAction*)pAction;
1996                     sal_uInt32 nTextLength(pA->GetLen());
1997                     const sal_uInt32 nTextIndex(pA->GetIndex());
1998                     const sal_uInt32 nStringLength(pA->GetText().Len());
1999 
2000                     if(nTextLength + nTextIndex > nStringLength)
2001                     {
2002                         nTextLength = nTextIndex > nStringLength ? 0 : nStringLength - nTextIndex;
2003                     }
2004 
2005                     if(nTextLength && rPropertyHolders.Current().getTextColorActive())
2006                     {
2007                         // prepare DXArray (if used)
2008                         std::vector< double > aDXArray;
2009                         sal_Int32* pDXArray = pA->GetDXArray();
2010 
2011                         if(pDXArray)
2012                         {
2013                             aDXArray.reserve(nTextLength);
2014 
2015                             for(sal_uInt32 a(0); a < nTextLength; a++)
2016                             {
2017                                 aDXArray.push_back((double)(*(pDXArray + a)));
2018                             }
2019                         }
2020 
2021                         proccessMetaTextAction(
2022                             pA->GetPoint(),
2023                             pA->GetText(),
2024                             nTextIndex,
2025                             nTextLength,
2026                             aDXArray,
2027                             rTargetHolders.Current(),
2028                             rPropertyHolders.Current());
2029                     }
2030 
2031                     break;
2032                 }
2033                 case META_STRETCHTEXT_ACTION :
2034                 {
2035                     // #i108440# StarMath uses MetaStretchTextAction, thus support is needed.
2036                     // It looks as if it pretty never really uses a width different from
2037                     // the default text-layout width, but it's not possible to be sure.
2038                     // Implemented getting the DXArray and checking for scale at all. If
2039                     // scale is more than 3.5% different, scale the DXArray before usage.
2040                     // New status:
2041 
2042                     /** CHECKED, WORKS WELL */
2043                     const MetaStretchTextAction* pA = (const MetaStretchTextAction*)pAction;
2044                     sal_uInt32 nTextLength(pA->GetLen());
2045                     const sal_uInt32 nTextIndex(pA->GetIndex());
2046                     const sal_uInt32 nStringLength(pA->GetText().Len());
2047 
2048                     if(nTextLength + nTextIndex > nStringLength)
2049                     {
2050                         nTextLength = nStringLength - nTextIndex;
2051                     }
2052 
2053                     if(nTextLength && rPropertyHolders.Current().getTextColorActive())
2054                     {
2055                         drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
2056                         aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont());
2057 
2058                         ::std::vector< double > aTextArray(
2059                             aTextLayouterDevice.getTextArray(
2060                                 pA->GetText(),
2061                                 nTextIndex,
2062                                 nTextLength));
2063 
2064                         if(!aTextArray.empty())
2065                         {
2066                             const double fTextLength(aTextArray.back());
2067 
2068                             if(0.0 != fTextLength && pA->GetWidth())
2069                             {
2070                                 const double fRelative(pA->GetWidth() / fTextLength);
2071 
2072                                 if(fabs(fRelative - 1.0) >= 0.035)
2073                                 {
2074                                     // when derivation is more than 3.5% from default text size,
2075                                     // scale the DXArray
2076                                     for(sal_uInt32 a(0); a < aTextArray.size(); a++)
2077                                     {
2078                                         aTextArray[a] *= fRelative;
2079                                     }
2080                                 }
2081                             }
2082                         }
2083 
2084                         proccessMetaTextAction(
2085                             pA->GetPoint(),
2086                             pA->GetText(),
2087                             nTextIndex,
2088                             nTextLength,
2089                             aTextArray,
2090                             rTargetHolders.Current(),
2091                             rPropertyHolders.Current());
2092                     }
2093 
2094                     break;
2095                 }
2096                 case META_TEXTRECT_ACTION :
2097                 {
2098                     /** CHECKED, WORKS WELL */
2099                     // OSL_ENSURE(false, "META_TEXTRECT_ACTION requested (!)");
2100                     const MetaTextRectAction* pA = (const MetaTextRectAction*)pAction;
2101                     const Rectangle& rRectangle = pA->GetRect();
2102                     const sal_uInt32 nStringLength(pA->GetText().Len());
2103 
2104                     if(!rRectangle.IsEmpty() && 0 != nStringLength)
2105                     {
2106                         // The problem with this action is that it describes unlayouted text
2107                         // and the layout capabilities are in EditEngine/Outliner in SVX. The
2108                         // same problem is true for VCL which internally has implementations
2109                         // to layout text in this case. There exists even a call
2110                         // OutputDevice::AddTextRectActions(...) to create the needed actions
2111                         // as 'sub-content' of a Metafile. Unfortunately i do not have an
2112                         // OutputDevice here since this interpreter tries to work without
2113                         // VCL AFAP.
2114                         // Since AddTextRectActions is the only way as long as we do not have
2115                         // a simple text layouter available, i will try to add it to the
2116                         // TextLayouterDevice isolation.
2117                         drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
2118                         aTextLayouterDevice.setFont(rPropertyHolders.Current().getFont());
2119                         GDIMetaFile aGDIMetaFile;
2120 
2121                         aTextLayouterDevice.addTextRectActions(
2122                             rRectangle, pA->GetText(), pA->GetStyle(), aGDIMetaFile);
2123 
2124                         if(aGDIMetaFile.GetActionCount())
2125                         {
2126                             // create sub-content
2127                             drawinglayer::primitive2d::Primitive2DSequence xSubContent;
2128                             {
2129                                 rTargetHolders.Push();
2130 
2131                                 // for sub-Metafile contents, do start with new, default render state
2132                                 // #124686# ...but copy font, this is already set accordingly
2133                                 const Font& rTargetFont = rPropertyHolders.Current().getFont();
2134                                 rPropertyHolders.PushDefault();
2135                                 rPropertyHolders.Current().setFont(rTargetFont);
2136 
2137                                 interpretMetafile(aGDIMetaFile, rTargetHolders, rPropertyHolders, rViewInformation);
2138                                 xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
2139                                 rPropertyHolders.Pop();
2140                                 rTargetHolders.Pop();
2141                             }
2142 
2143                             if(xSubContent.hasElements())
2144                             {
2145                                 // add with transformation
2146                                 rTargetHolders.Current().append(
2147                                     new drawinglayer::primitive2d::TransformPrimitive2D(
2148                                         rPropertyHolders.Current().getTransformation(),
2149                                         xSubContent));
2150                             }
2151                         }
2152                     }
2153 
2154                     break;
2155                 }
2156                 case META_BMP_ACTION :
2157                 {
2158                     /** CHECKED, WORKS WELL */
2159                     const MetaBmpAction* pA = (const MetaBmpAction*)pAction;
2160                     const BitmapEx aBitmapEx(pA->GetBitmap());
2161 
2162                     createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
2163 
2164                     break;
2165                 }
2166                 case META_BMPSCALE_ACTION :
2167                 {
2168                     /** CHECKED, WORKS WELL */
2169                     const MetaBmpScaleAction* pA = (const MetaBmpScaleAction*)pAction;
2170                     const Bitmap aBitmapEx(pA->GetBitmap());
2171 
2172                     createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2173 
2174                     break;
2175                 }
2176                 case META_BMPSCALEPART_ACTION :
2177                 {
2178                     /** CHECKED, WORKS WELL */
2179                     const MetaBmpScalePartAction* pA = (const MetaBmpScalePartAction*)pAction;
2180                     const Bitmap& rBitmap = pA->GetBitmap();
2181 
2182                     if(!rBitmap.IsEmpty())
2183                     {
2184                         Bitmap aCroppedBitmap(rBitmap);
2185                         const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
2186 
2187                         if(!aCropRectangle.IsEmpty())
2188                         {
2189                             aCroppedBitmap.Crop(aCropRectangle);
2190                         }
2191 
2192                         const BitmapEx aCroppedBitmapEx(aCroppedBitmap);
2193                         createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2194                     }
2195 
2196                     break;
2197                 }
2198                 case META_BMPEX_ACTION :
2199                 {
2200                     /** CHECKED, WORKS WELL: Simply same as META_BMP_ACTION */
2201                     const MetaBmpExAction* pA = (const MetaBmpExAction*)pAction;
2202                     const BitmapEx& rBitmapEx = pA->GetBitmapEx();
2203 
2204                     createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
2205 
2206                     break;
2207                 }
2208                 case META_BMPEXSCALE_ACTION :
2209                 {
2210                     /** CHECKED, WORKS WELL: Simply same as META_BMPSCALE_ACTION */
2211                     const MetaBmpExScaleAction* pA = (const MetaBmpExScaleAction*)pAction;
2212                     const BitmapEx& rBitmapEx = pA->GetBitmapEx();
2213 
2214                     createBitmapExPrimitive(rBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2215 
2216                     break;
2217                 }
2218                 case META_BMPEXSCALEPART_ACTION :
2219                 {
2220                     /** CHECKED, WORKS WELL: Simply same as META_BMPSCALEPART_ACTION */
2221                     const MetaBmpExScalePartAction* pA = (const MetaBmpExScalePartAction*)pAction;
2222                     const BitmapEx& rBitmapEx = pA->GetBitmapEx();
2223 
2224                     if(!rBitmapEx.IsEmpty())
2225                     {
2226                         BitmapEx aCroppedBitmapEx(rBitmapEx);
2227                         const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
2228 
2229                         if(!aCropRectangle.IsEmpty())
2230                         {
2231                             aCroppedBitmapEx.Crop(aCropRectangle);
2232                         }
2233 
2234                         createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2235                     }
2236 
2237                     break;
2238                 }
2239                 case META_MASK_ACTION :
2240                 {
2241                     /** CHECKED, WORKS WELL: Simply same as META_BMP_ACTION */
2242                     const MetaMaskAction* pA = (const MetaMaskAction*)pAction;
2243                     const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
2244 
2245                     createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), rTargetHolders.Current(), rPropertyHolders.Current());
2246 
2247                     break;
2248                 }
2249                 case META_MASKSCALE_ACTION :
2250                 {
2251                     /** CHECKED, WORKS WELL: Simply same as META_BMPSCALE_ACTION */
2252                     const MetaMaskScaleAction* pA = (const MetaMaskScaleAction*)pAction;
2253                     const BitmapEx aBitmapEx(createMaskBmpEx(pA->GetBitmap(), pA->GetColor()));
2254 
2255                     createBitmapExPrimitive(aBitmapEx, pA->GetPoint(), pA->GetSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2256 
2257                     break;
2258                 }
2259                 case META_MASKSCALEPART_ACTION :
2260                 {
2261                     /** CHECKED, WORKS WELL: Simply same as META_BMPSCALEPART_ACTION */
2262                     const MetaMaskScalePartAction* pA = (const MetaMaskScalePartAction*)pAction;
2263                     const Bitmap& rBitmap = pA->GetBitmap();
2264 
2265                     if(!rBitmap.IsEmpty())
2266                     {
2267                         Bitmap aCroppedBitmap(rBitmap);
2268                         const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
2269 
2270                         if(!aCropRectangle.IsEmpty())
2271                         {
2272                             aCroppedBitmap.Crop(aCropRectangle);
2273                         }
2274 
2275                         const BitmapEx aCroppedBitmapEx(createMaskBmpEx(aCroppedBitmap, pA->GetColor()));
2276                         createBitmapExPrimitive(aCroppedBitmapEx, pA->GetDestPoint(), pA->GetDestSize(), rTargetHolders.Current(), rPropertyHolders.Current());
2277                     }
2278 
2279                     break;
2280                 }
2281                 case META_GRADIENT_ACTION :
2282                 {
2283                     /** CHECKED, WORKS WELL */
2284                     const MetaGradientAction* pA = (const MetaGradientAction*)pAction;
2285                     const Rectangle& rRectangle = pA->GetRect();
2286 
2287                     if(!rRectangle.IsEmpty())
2288                     {
2289                         basegfx::B2DRange aRange(rRectangle.Left(), rRectangle.Top(), rRectangle.Right(), rRectangle.Bottom());
2290 
2291                         if(!aRange.isEmpty())
2292                         {
2293                             const Gradient& rGradient = pA->GetGradient();
2294                             const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
2295                             basegfx::B2DPolyPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
2296 
2297                             if(aAttribute.getStartColor() == aAttribute.getEndColor())
2298                             {
2299                                 // not really a gradient. Create filled rectangle
2300                                 createFillPrimitive(
2301                                     aOutline,
2302                                     rTargetHolders.Current(),
2303                                     rPropertyHolders.Current());
2304                             }
2305                             else
2306                             {
2307                                 // really a gradient
2308                                 aRange.transform(rPropertyHolders.Current().getTransformation());
2309                                 drawinglayer::primitive2d::Primitive2DSequence xGradient(1);
2310 
2311                                 if(rPropertyHolders.Current().isRasterOpInvert())
2312                                 {
2313                                     // use a special version of FillGradientPrimitive2D which creates
2314                                     // non-overlapping geometry on decomposition to make the old XOR
2315                                     // paint 'trick' work.
2316                                     xGradient[0] = drawinglayer::primitive2d::Primitive2DReference(
2317                                         new drawinglayer::primitive2d::NonOverlappingFillGradientPrimitive2D(
2318                                             aRange,
2319                                             aAttribute));
2320                                 }
2321                                 else
2322                                 {
2323                                     xGradient[0] = drawinglayer::primitive2d::Primitive2DReference(
2324                                         new drawinglayer::primitive2d::FillGradientPrimitive2D(
2325                                             aRange,
2326                                             aAttribute));
2327                                 }
2328 
2329                                 // #i112300# clip against polygon representing the rectangle from
2330                                 // the action. This is implicitly done using a temp Clipping in VCL
2331                                 // when a MetaGradientAction is executed
2332                                 aOutline.transform(rPropertyHolders.Current().getTransformation());
2333                                 rTargetHolders.Current().append(
2334                                     new drawinglayer::primitive2d::MaskPrimitive2D(
2335                                         aOutline,
2336                                         xGradient));
2337                             }
2338                         }
2339                     }
2340 
2341                     break;
2342                 }
2343                 case META_HATCH_ACTION :
2344                 {
2345                     /** CHECKED, WORKS WELL */
2346                     const MetaHatchAction* pA = (const MetaHatchAction*)pAction;
2347                     basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
2348 
2349                     if(aOutline.count())
2350                     {
2351                         const Hatch& rHatch = pA->GetHatch();
2352                         const drawinglayer::attribute::FillHatchAttribute aAttribute(createFillHatchAttribute(rHatch));
2353 
2354                         aOutline.transform(rPropertyHolders.Current().getTransformation());
2355 
2356                         const basegfx::B2DRange aObjectRange(aOutline.getB2DRange());
2357                         const drawinglayer::primitive2d::Primitive2DReference aFillHatch(
2358                             new drawinglayer::primitive2d::FillHatchPrimitive2D(
2359                                 aObjectRange,
2360                                 basegfx::BColor(),
2361                                 aAttribute));
2362 
2363                         rTargetHolders.Current().append(
2364                             new drawinglayer::primitive2d::MaskPrimitive2D(
2365                                 aOutline,
2366                                 drawinglayer::primitive2d::Primitive2DSequence(&aFillHatch, 1)));
2367                     }
2368 
2369                     break;
2370                 }
2371                 case META_WALLPAPER_ACTION :
2372                 {
2373                     /** CHECKED, WORKS WELL */
2374                     const MetaWallpaperAction* pA = (const MetaWallpaperAction*)pAction;
2375                     Rectangle aWallpaperRectangle(pA->GetRect());
2376 
2377                     if(!aWallpaperRectangle.IsEmpty())
2378                     {
2379                         const Wallpaper& rWallpaper = pA->GetWallpaper();
2380                         const WallpaperStyle eWallpaperStyle(rWallpaper.GetStyle());
2381                         basegfx::B2DRange aWallpaperRange(
2382                             aWallpaperRectangle.Left(), aWallpaperRectangle.Top(),
2383                             aWallpaperRectangle.Right(), aWallpaperRectangle.Bottom());
2384 
2385                         if(WALLPAPER_NULL != eWallpaperStyle)
2386                         {
2387                             if(rWallpaper.IsBitmap())
2388                             {
2389                                 // create bitmap background. Caution: This
2390                                 // also will create gradient/color background(s)
2391                                 // when the bitmap is transparent or not tiled
2392                                 CreateAndAppendBitmapWallpaper(
2393                                     aWallpaperRange,
2394                                     rWallpaper,
2395                                     rTargetHolders.Current(),
2396                                     rPropertyHolders.Current());
2397                             }
2398                             else if(rWallpaper.IsGradient())
2399                             {
2400                                 // create gradient background
2401                                 rTargetHolders.Current().append(
2402                                     CreateGradientWallpaper(
2403                                         aWallpaperRange,
2404                                         rWallpaper.GetGradient(),
2405                                         rPropertyHolders.Current()));
2406                             }
2407                             else if(!rWallpaper.GetColor().GetTransparency())
2408                             {
2409                                 // create color background
2410                                 rTargetHolders.Current().append(
2411                                     CreateColorWallpaper(
2412                                         aWallpaperRange,
2413                                         rWallpaper.GetColor().getBColor(),
2414                                         rPropertyHolders.Current()));
2415                             }
2416                         }
2417                     }
2418 
2419                     break;
2420                 }
2421                 case META_CLIPREGION_ACTION :
2422                 {
2423                     /** CHECKED, WORKS WELL */
2424                     const MetaClipRegionAction* pA = (const MetaClipRegionAction*)pAction;
2425 
2426                     if(pA->IsClipping())
2427                     {
2428                         // new clipping. Get PolyPolygon and transform with current transformation
2429                         basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(pA->GetRegion()));
2430 
2431                         aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
2432                         HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
2433                     }
2434                     else
2435                     {
2436                         // end clipping
2437                         const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
2438 
2439                         HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
2440                     }
2441 
2442                     break;
2443                 }
2444                 case META_ISECTRECTCLIPREGION_ACTION :
2445                 {
2446                     /** CHECKED, WORKS WELL */
2447                     const MetaISectRectClipRegionAction* pA = (const MetaISectRectClipRegionAction*)pAction;
2448                     const Rectangle& rRectangle = pA->GetRect();
2449 
2450                     if(rRectangle.IsEmpty())
2451                     {
2452                         // intersect with empty rectangle will always give empty
2453                         // ClipPolyPolygon; start new clipping with empty PolyPolygon
2454                         const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
2455 
2456                         HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
2457                     }
2458                     else
2459                     {
2460                         // create transformed ClipRange
2461                         basegfx::B2DRange aClipRange(
2462                             rRectangle.Left(), rRectangle.Top(),
2463                             rRectangle.Right(), rRectangle.Bottom());
2464 
2465                         aClipRange.transform(rPropertyHolders.Current().getTransformation());
2466 
2467                         if(rPropertyHolders.Current().getClipPolyPolygonActive())
2468                         {
2469                             if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
2470                             {
2471                                 // nothing to do, empty active clipPolyPolygon will stay
2472                                 // empty when intersecting
2473                             }
2474                             else
2475                             {
2476                                 // AND existing region and new ClipRange
2477                                 const basegfx::B2DPolyPolygon aOriginalPolyPolygon(
2478                                     rPropertyHolders.Current().getClipPolyPolygon());
2479                                 basegfx::B2DPolyPolygon aClippedPolyPolygon;
2480 
2481                                 if(aOriginalPolyPolygon.count())
2482                                 {
2483                                     aClippedPolyPolygon = basegfx::tools::clipPolyPolygonOnRange(
2484                                         aOriginalPolyPolygon,
2485                                         aClipRange,
2486                                         true,
2487                                         false);
2488                                 }
2489 
2490                                 if(aClippedPolyPolygon != aOriginalPolyPolygon)
2491                                 {
2492                                     // start new clipping with intersected region
2493                                     HandleNewClipRegion(
2494                                         aClippedPolyPolygon,
2495                                         rTargetHolders,
2496                                         rPropertyHolders);
2497                                 }
2498                             }
2499                         }
2500                         else
2501                         {
2502                             // start new clipping with ClipRange
2503                             const basegfx::B2DPolyPolygon aNewClipPolyPolygon(
2504                                 basegfx::tools::createPolygonFromRect(aClipRange));
2505 
2506                             HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
2507                         }
2508                     }
2509 
2510                     break;
2511                 }
2512                 case META_ISECTREGIONCLIPREGION_ACTION :
2513                 {
2514                     /** CHECKED, WORKS WELL */
2515                     const MetaISectRegionClipRegionAction* pA = (const MetaISectRegionClipRegionAction*)pAction;
2516                     const Region& rNewRegion = pA->GetRegion();
2517 
2518                     if(rNewRegion.IsEmpty())
2519                     {
2520                         // intersect with empty region will always give empty
2521                         // region; start new clipping with empty PolyPolygon
2522                         const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
2523 
2524                         HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
2525                     }
2526                     else
2527                     {
2528                         // get new ClipPolyPolygon, transform it with current transformation
2529                         basegfx::B2DPolyPolygon aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(rNewRegion));
2530                         aNewClipPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
2531 
2532                         if(rPropertyHolders.Current().getClipPolyPolygonActive())
2533                         {
2534                             if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
2535                             {
2536                                 // nothing to do, empty active clipPolyPolygon will stay empty
2537                                 // when intersecting with any region
2538                             }
2539                             else
2540                             {
2541                                 // AND existing and new region
2542                                 const basegfx::B2DPolyPolygon aOriginalPolyPolygon(
2543                                     rPropertyHolders.Current().getClipPolyPolygon());
2544                                 basegfx::B2DPolyPolygon aClippedPolyPolygon;
2545 
2546                                 if(aOriginalPolyPolygon.count())
2547                                 {
2548                                     aClippedPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon(
2549                                         aOriginalPolyPolygon, aNewClipPolyPolygon, true, false);
2550                                 }
2551 
2552                                 if(aClippedPolyPolygon != aOriginalPolyPolygon)
2553                                 {
2554                                     // start new clipping with intersected ClipPolyPolygon
2555                                     HandleNewClipRegion(aClippedPolyPolygon, rTargetHolders, rPropertyHolders);
2556                                 }
2557                             }
2558                         }
2559                         else
2560                         {
2561                             // start new clipping with new ClipPolyPolygon
2562                             HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
2563                         }
2564                     }
2565 
2566                     break;
2567                 }
2568                 case META_MOVECLIPREGION_ACTION :
2569                 {
2570                     /** CHECKED, WORKS WELL */
2571                     const MetaMoveClipRegionAction* pA = (const MetaMoveClipRegionAction*)pAction;
2572 
2573                     if(rPropertyHolders.Current().getClipPolyPolygonActive())
2574                     {
2575                         if(0 == rPropertyHolders.Current().getClipPolyPolygon().count())
2576                         {
2577                             // nothing to do
2578                         }
2579                         else
2580                         {
2581                             const sal_Int32 nHor(pA->GetHorzMove());
2582                             const sal_Int32 nVer(pA->GetVertMove());
2583 
2584                             if(0 != nHor || 0 != nVer)
2585                             {
2586                                 // prepare translation, add current transformation
2587                                 basegfx::B2DVector aVector(pA->GetHorzMove(), pA->GetVertMove());
2588                                 aVector *= rPropertyHolders.Current().getTransformation();
2589                                 basegfx::B2DHomMatrix aTransform(
2590                                     basegfx::tools::createTranslateB2DHomMatrix(aVector));
2591 
2592                                 // transform existing region
2593                                 basegfx::B2DPolyPolygon aClipPolyPolygon(
2594                                     rPropertyHolders.Current().getClipPolyPolygon());
2595 
2596                                 aClipPolyPolygon.transform(aTransform);
2597                                 HandleNewClipRegion(aClipPolyPolygon, rTargetHolders, rPropertyHolders);
2598                             }
2599                         }
2600                     }
2601 
2602                     break;
2603                 }
2604                 case META_LINECOLOR_ACTION :
2605                 {
2606                     /** CHECKED, WORKS WELL */
2607                     const MetaLineColorAction* pA = (const MetaLineColorAction*)pAction;
2608                     const bool bActive(pA->IsSetting());
2609 
2610                     rPropertyHolders.Current().setLineColorActive(bActive);
2611                     if(bActive)
2612                         rPropertyHolders.Current().setLineColor(pA->GetColor().getBColor());
2613 
2614                     break;
2615                 }
2616                 case META_FILLCOLOR_ACTION :
2617                 {
2618                     /** CHECKED, WORKS WELL */
2619                     const MetaFillColorAction* pA = (const MetaFillColorAction*)pAction;
2620                     const bool bActive(pA->IsSetting());
2621 
2622                     rPropertyHolders.Current().setFillColorActive(bActive);
2623                     if(bActive)
2624                         rPropertyHolders.Current().setFillColor(pA->GetColor().getBColor());
2625 
2626                     break;
2627                 }
2628                 case META_TEXTCOLOR_ACTION :
2629                 {
2630                     /** SIMPLE, DONE */
2631                     const MetaTextColorAction* pA = (const MetaTextColorAction*)pAction;
2632                     const bool bActivate(COL_TRANSPARENT != pA->GetColor().GetColor());
2633 
2634                     rPropertyHolders.Current().setTextColorActive(bActivate);
2635                     rPropertyHolders.Current().setTextColor(pA->GetColor().getBColor());
2636 
2637                     break;
2638                 }
2639                 case META_TEXTFILLCOLOR_ACTION :
2640                 {
2641                     /** SIMPLE, DONE */
2642                     const MetaTextFillColorAction* pA = (const MetaTextFillColorAction*)pAction;
2643                     const bool bWithColorArgument(pA->IsSetting());
2644 
2645                     if(bWithColorArgument)
2646                     {
2647                         // emulate OutputDevice::SetTextFillColor(...) WITH argument
2648                         const Color& rFontFillColor = pA->GetColor();
2649                         rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor());
2650                         rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor.GetColor());
2651                     }
2652                     else
2653                     {
2654                         // emulate SetFillColor() <- NO argument (!)
2655                         rPropertyHolders.Current().setTextFillColorActive(false);
2656                     }
2657 
2658                     break;
2659                 }
2660                 case META_TEXTALIGN_ACTION :
2661                 {
2662                     /** SIMPLE, DONE */
2663                     const MetaTextAlignAction* pA = (const MetaTextAlignAction*)pAction;
2664                     const TextAlign aNewTextAlign = pA->GetTextAlign();
2665 
2666                     // TextAlign is applied to the current font (as in
2667                     // OutputDevice::SetTextAlign which would be used when
2668                     // playing the Metafile)
2669                     if(rPropertyHolders.Current().getFont().GetAlign() != aNewTextAlign)
2670                     {
2671                         Font aNewFont(rPropertyHolders.Current().getFont());
2672                         aNewFont.SetAlign(aNewTextAlign);
2673                         rPropertyHolders.Current().setFont(aNewFont);
2674                     }
2675 
2676                     break;
2677                 }
2678                 case META_MAPMODE_ACTION :
2679                 {
2680                     /** CHECKED, WORKS WELL */
2681                     // the most necessary MapMode to be interpreted is MAP_RELATIVE,
2682                     // but also the others may occur. Even not yet supported ones
2683                     // may need to be added here later
2684                     const MetaMapModeAction* pA = (const MetaMapModeAction*)pAction;
2685                     const MapMode& rMapMode = pA->GetMapMode();
2686                     basegfx::B2DHomMatrix aMapping;
2687 
2688                     if(MAP_RELATIVE == rMapMode.GetMapUnit())
2689                     {
2690                         aMapping = getTransformFromMapMode(rMapMode);
2691                     }
2692                     else
2693                     {
2694                         switch(rMapMode.GetMapUnit())
2695                         {
2696                             case MAP_100TH_MM :
2697                             {
2698                                 if(MAP_TWIP == rPropertyHolders.Current().getMapUnit())
2699                                 {
2700                                     // MAP_TWIP -> MAP_100TH_MM
2701                                     const double fTwipTo100thMm(127.0 / 72.0);
2702                                     aMapping.scale(fTwipTo100thMm, fTwipTo100thMm);
2703                                 }
2704                                 break;
2705                             }
2706                             case MAP_TWIP :
2707                             {
2708                                 if(MAP_100TH_MM == rPropertyHolders.Current().getMapUnit())
2709                                 {
2710                                     // MAP_100TH_MM -> MAP_TWIP
2711                                     const double f100thMmToTwip(72.0 / 127.0);
2712                                     aMapping.scale(f100thMmToTwip, f100thMmToTwip);
2713                                 }
2714                                 break;
2715                             }
2716                             default :
2717                             {
2718                                 OSL_ENSURE(false, "interpretMetafile: META_MAPMODE_ACTION with unsupported MapUnit (!)");
2719                                 break;
2720                             }
2721                         }
2722 
2723                         aMapping = getTransformFromMapMode(rMapMode) * aMapping;
2724                         rPropertyHolders.Current().setMapUnit(rMapMode.GetMapUnit());
2725                     }
2726 
2727                     if(!aMapping.isIdentity())
2728                     {
2729                         aMapping = aMapping * rPropertyHolders.Current().getTransformation();
2730                         rPropertyHolders.Current().setTransformation(aMapping);
2731                     }
2732 
2733                     break;
2734                 }
2735                 case META_FONT_ACTION :
2736                 {
2737                     /** SIMPLE, DONE */
2738                     const MetaFontAction* pA = (const MetaFontAction*)pAction;
2739                     rPropertyHolders.Current().setFont(pA->GetFont());
2740                     Size aFontSize(pA->GetFont().GetSize());
2741 
2742                     if(0 == aFontSize.Height())
2743                     {
2744                         // this should not happen but I got Metafiles where this was the
2745                         // case. A height needs to be guessed (similar to OutputDevice::ImplNewFont())
2746                         Font aCorrectedFont(pA->GetFont());
2747 
2748                         // guess 16 pixel (as in VCL)
2749                         aFontSize = Size(0, 16);
2750 
2751                         // convert to target MapUnit if not pixels
2752                         aFontSize = Application::GetDefaultDevice()->LogicToLogic(
2753                             aFontSize, MAP_PIXEL, rPropertyHolders.Current().getMapUnit());
2754 
2755                         aCorrectedFont.SetSize(aFontSize);
2756                         rPropertyHolders.Current().setFont(aCorrectedFont);
2757                     }
2758 
2759                     // older Metafiles have no META_TEXTCOLOR_ACTION which defines
2760                     // the FontColor now, so use the Font's color when not transparent
2761                     const Color& rFontColor = pA->GetFont().GetColor();
2762                     const bool bActivate(COL_TRANSPARENT != rFontColor.GetColor());
2763 
2764                     if(bActivate)
2765                     {
2766                         rPropertyHolders.Current().setTextColor(rFontColor.getBColor());
2767                     }
2768 
2769                     // caution: do NOT deactivate here on transparent, see
2770                     // OutputDevice::SetFont(..) for more info
2771                     // rPropertyHolders.Current().setTextColorActive(bActivate);
2772 
2773                     // for fill color emulate a MetaTextFillColorAction with !transparent as bool,
2774                     // see OutputDevice::SetFont(..) the if(mpMetaFile) case
2775                     if(bActivate)
2776                     {
2777                         const Color& rFontFillColor = pA->GetFont().GetFillColor();
2778                         rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor());
2779                         rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor.GetColor());
2780                     }
2781                     else
2782                     {
2783                         rPropertyHolders.Current().setTextFillColorActive(false);
2784                     }
2785 
2786                     break;
2787                 }
2788                 case META_PUSH_ACTION :
2789                 {
2790                     /** CHECKED, WORKS WELL */
2791                     const MetaPushAction* pA = (const MetaPushAction*)pAction;
2792                     rPropertyHolders.Push(pA->GetFlags());
2793 
2794                     break;
2795                 }
2796                 case META_POP_ACTION :
2797                 {
2798                     /** CHECKED, WORKS WELL */
2799                     const bool bRegionMayChange(rPropertyHolders.Current().getPushFlags() & PUSH_CLIPREGION);
2800                     const bool bRasterOpMayChange(rPropertyHolders.Current().getPushFlags() & PUSH_RASTEROP);
2801 
2802                     if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive())
2803                     {
2804                         // end evtl. clipping
2805                         const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
2806 
2807                         HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
2808                     }
2809 
2810                     if(bRasterOpMayChange && rPropertyHolders.Current().isRasterOpActive())
2811                     {
2812                         // end evtl. RasterOp
2813                         HandleNewRasterOp(ROP_OVERPAINT, rTargetHolders, rPropertyHolders);
2814                     }
2815 
2816                     rPropertyHolders.Pop();
2817 
2818                     if(bRasterOpMayChange && rPropertyHolders.Current().isRasterOpActive())
2819                     {
2820                         // start evtl. RasterOp
2821                         HandleNewRasterOp(rPropertyHolders.Current().getRasterOp(), rTargetHolders, rPropertyHolders);
2822                     }
2823 
2824                     if(bRegionMayChange && rPropertyHolders.Current().getClipPolyPolygonActive())
2825                     {
2826                         // start evtl. clipping
2827                         HandleNewClipRegion(
2828                             rPropertyHolders.Current().getClipPolyPolygon(), rTargetHolders, rPropertyHolders);
2829                     }
2830 
2831                     break;
2832                 }
2833                 case META_RASTEROP_ACTION :
2834                 {
2835                     /** CHECKED, WORKS WELL */
2836                     const MetaRasterOpAction* pA = (const MetaRasterOpAction*)pAction;
2837                     const RasterOp aRasterOp = pA->GetRasterOp();
2838 
2839                     HandleNewRasterOp(aRasterOp, rTargetHolders, rPropertyHolders);
2840 
2841                     break;
2842                 }
2843                 case META_TRANSPARENT_ACTION :
2844                 {
2845                     /** CHECKED, WORKS WELL */
2846                     const MetaTransparentAction* pA = (const MetaTransparentAction*)pAction;
2847                     const basegfx::B2DPolyPolygon aOutline(pA->GetPolyPolygon().getB2DPolyPolygon());
2848 
2849                     if(aOutline.count())
2850                     {
2851                         const sal_uInt16 nTransparence(pA->GetTransparence());
2852 
2853                         if(0 == nTransparence)
2854                         {
2855                             // not transparent
2856                             createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
2857                         }
2858                         else if(nTransparence >= 100)
2859                         {
2860                             // fully or more than transparent
2861                         }
2862                         else
2863                         {
2864                             // transparent. Create new target
2865                             rTargetHolders.Push();
2866 
2867                             // create primitives there and get them
2868                             createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
2869                             const drawinglayer::primitive2d::Primitive2DSequence aSubContent(
2870                                 rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current()));
2871 
2872                             // back to old target
2873                             rTargetHolders.Pop();
2874 
2875                             if(aSubContent.hasElements())
2876                             {
2877                                 rTargetHolders.Current().append(
2878                                     new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
2879                                         aSubContent,
2880                                         nTransparence * 0.01));
2881                             }
2882                         }
2883                     }
2884 
2885                     break;
2886                 }
2887                 case META_EPS_ACTION :
2888                 {
2889                     /** CHECKED, WORKS WELL */
2890                     // To support this action, i have added a EpsPrimitive2D which will
2891                     // by default decompose to the Metafile replacement data. To support
2892                     // this EPS on screen, the renderer visualizing this has to support
2893                     // that primitive and visualize the Eps file (e.g. printing)
2894                     const MetaEPSAction* pA = (const MetaEPSAction*)pAction;
2895                     const Rectangle aRectangle(pA->GetPoint(), pA->GetSize());
2896 
2897                     if(!aRectangle.IsEmpty())
2898                     {
2899                         // create object transform
2900                         basegfx::B2DHomMatrix aObjectTransform;
2901 
2902                         aObjectTransform.set(0, 0, aRectangle.GetWidth());
2903                         aObjectTransform.set(1, 1, aRectangle.GetHeight());
2904                         aObjectTransform.set(0, 2, aRectangle.Left());
2905                         aObjectTransform.set(1, 2, aRectangle.Top());
2906 
2907                         // add current transformation
2908                         aObjectTransform = rPropertyHolders.Current().getTransformation() * aObjectTransform;
2909 
2910                         // embed using EpsPrimitive
2911                         rTargetHolders.Current().append(
2912                             new drawinglayer::primitive2d::EpsPrimitive2D(
2913                                 aObjectTransform,
2914                                 pA->GetLink(),
2915                                 pA->GetSubstitute()));
2916                     }
2917 
2918                     break;
2919                 }
2920                 case META_REFPOINT_ACTION :
2921                 {
2922                     /** SIMPLE, DONE */
2923                     // only used for hatch and line pattern offsets, pretty much no longer
2924                     // supported today
2925                     // const MetaRefPointAction* pA = (const MetaRefPointAction*)pAction;
2926                     break;
2927                 }
2928                 case META_TEXTLINECOLOR_ACTION :
2929                 {
2930                     /** SIMPLE, DONE */
2931                     const MetaTextLineColorAction* pA = (const MetaTextLineColorAction*)pAction;
2932                     const bool bActive(pA->IsSetting());
2933 
2934                     rPropertyHolders.Current().setTextLineColorActive(bActive);
2935                     if(bActive)
2936                         rPropertyHolders.Current().setTextLineColor(pA->GetColor().getBColor());
2937 
2938                     break;
2939                 }
2940                 case META_TEXTLINE_ACTION :
2941                 {
2942                     /** CHECKED, WORKS WELL */
2943                     // actually creates overline, underline and strikeouts, so
2944                     // these should be isolated from TextDecoratedPortionPrimitive2D
2945                     // to own primitives. Done, available now.
2946                     //
2947                     // This Metaaction seems not to be used (was not used in any
2948                     // checked files). It's used in combination with the current
2949                     // Font.
2950                     const MetaTextLineAction* pA = (const MetaTextLineAction*)pAction;
2951 
2952                     proccessMetaTextLineAction(
2953                         *pA,
2954                         rTargetHolders.Current(),
2955                         rPropertyHolders.Current());
2956 
2957                     break;
2958                 }
2959                 case META_FLOATTRANSPARENT_ACTION :
2960                 {
2961                     /** CHECKED, WORKS WELL */
2962                     const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*)pAction;
2963                     const basegfx::B2DRange aTargetRange(
2964                         pA->GetPoint().X(),
2965                         pA->GetPoint().Y(),
2966                         pA->GetPoint().X() + pA->GetSize().Width(),
2967                         pA->GetPoint().Y() + pA->GetSize().Height());
2968 
2969                     if(!aTargetRange.isEmpty())
2970                     {
2971                         const GDIMetaFile& rContent = pA->GetGDIMetaFile();
2972 
2973                         if(rContent.GetActionCount())
2974                         {
2975                             // create the sub-content with no embedding specific to the
2976                             // sub-metafile, this seems not to be used.
2977                             drawinglayer::primitive2d::Primitive2DSequence xSubContent;
2978                             {
2979                                 rTargetHolders.Push();
2980                                 // #i# for sub-Metafile contents, do start with new, default render state
2981                                 rPropertyHolders.PushDefault();
2982                                 interpretMetafile(rContent, rTargetHolders, rPropertyHolders, rViewInformation);
2983                                 xSubContent = rTargetHolders.Current().getPrimitive2DSequence(rPropertyHolders.Current());
2984                                 rPropertyHolders.Pop();
2985                                 rTargetHolders.Pop();
2986                             }
2987 
2988                             if(xSubContent.hasElements())
2989                             {
2990                                 // prepare sub-content transform
2991                                 basegfx::B2DHomMatrix aSubTransform;
2992 
2993                                 // create SourceRange
2994                                 const basegfx::B2DRange aSourceRange(
2995                                     rContent.GetPrefMapMode().GetOrigin().X(),
2996                                     rContent.GetPrefMapMode().GetOrigin().Y(),
2997                                     rContent.GetPrefMapMode().GetOrigin().X() + rContent.GetPrefSize().Width(),
2998                                     rContent.GetPrefMapMode().GetOrigin().Y() + rContent.GetPrefSize().Height());
2999 
3000                                 // apply mapping if aTargetRange and aSourceRange are not equal
3001                                 if(!aSourceRange.equal(aTargetRange))
3002                                 {
3003                                     aSubTransform.translate(-aSourceRange.getMinX(), -aSourceRange.getMinY());
3004                                     aSubTransform.scale(
3005                                         aTargetRange.getWidth() / (basegfx::fTools::equalZero(aSourceRange.getWidth()) ? 1.0 : aSourceRange.getWidth()),
3006                                         aTargetRange.getHeight() / (basegfx::fTools::equalZero(aSourceRange.getHeight()) ? 1.0 : aSourceRange.getHeight()));
3007                                     aSubTransform.translate(aTargetRange.getMinX(), aTargetRange.getMinY());
3008                                 }
3009 
3010                                 // apply general current transformation
3011                                 aSubTransform = rPropertyHolders.Current().getTransformation() * aSubTransform;
3012 
3013                                 // evtl. embed sub-content to its transformation
3014                                 if(!aSubTransform.isIdentity())
3015                                 {
3016                                     const drawinglayer::primitive2d::Primitive2DReference aEmbeddedTransform(
3017                                         new drawinglayer::primitive2d::TransformPrimitive2D(
3018                                             aSubTransform,
3019                                             xSubContent));
3020 
3021                                     xSubContent = drawinglayer::primitive2d::Primitive2DSequence(&aEmbeddedTransform, 1);
3022                                 }
3023 
3024                                 // check if gradient is a real gradient
3025                                 const Gradient& rGradient = pA->GetGradient();
3026                                 const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
3027 
3028                                 if(aAttribute.getStartColor() == aAttribute.getEndColor())
3029                                 {
3030                                     // not really a gradient; create UnifiedTransparencePrimitive2D
3031                                     rTargetHolders.Current().append(
3032                                         new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
3033                                             xSubContent,
3034                                             aAttribute.getStartColor().luminance()));
3035                                 }
3036                                 else
3037                                 {
3038                                     // really a gradient. Create gradient sub-content (with correct scaling)
3039                                     basegfx::B2DRange aRange(aTargetRange);
3040                                     aRange.transform(rPropertyHolders.Current().getTransformation());
3041 
3042                                     // prepare gradient for transparent content
3043                                     const drawinglayer::primitive2d::Primitive2DReference xTransparence(
3044                                         new drawinglayer::primitive2d::FillGradientPrimitive2D(
3045                                             aRange,
3046                                             aAttribute));
3047 
3048                                     // create transparent primitive
3049                                     rTargetHolders.Current().append(
3050                                         new drawinglayer::primitive2d::TransparencePrimitive2D(
3051                                             xSubContent,
3052                                             drawinglayer::primitive2d::Primitive2DSequence(&xTransparence, 1)));
3053                                 }
3054                             }
3055                         }
3056                     }
3057 
3058                     break;
3059                 }
3060                 case META_GRADIENTEX_ACTION :
3061                 {
3062                     /** SIMPLE, DONE */
3063                     // This is only a data holder which is interpreted inside comment actions,
3064                     // see META_COMMENT_ACTION for more info
3065                     // const MetaGradientExAction* pA = (const MetaGradientExAction*)pAction;
3066                     break;
3067                 }
3068                 case META_LAYOUTMODE_ACTION :
3069                 {
3070                     /** SIMPLE, DONE */
3071                     const MetaLayoutModeAction* pA = (const MetaLayoutModeAction*)pAction;
3072                     rPropertyHolders.Current().setLayoutMode(pA->GetLayoutMode());
3073                     break;
3074                 }
3075                 case META_TEXTLANGUAGE_ACTION :
3076                 {
3077                     /** SIMPLE, DONE */
3078                     const MetaTextLanguageAction* pA = (const MetaTextLanguageAction*)pAction;
3079                     rPropertyHolders.Current().setLanguageType(pA->GetTextLanguage());
3080                     break;
3081                 }
3082                 case META_OVERLINECOLOR_ACTION :
3083                 {
3084                     /** SIMPLE, DONE */
3085                     const MetaOverlineColorAction* pA = (const MetaOverlineColorAction*)pAction;
3086                     const bool bActive(pA->IsSetting());
3087 
3088                     rPropertyHolders.Current().setOverlineColorActive(bActive);
3089                     if(bActive)
3090                         rPropertyHolders.Current().setOverlineColor(pA->GetColor().getBColor());
3091 
3092                     break;
3093                 }
3094                 case META_COMMENT_ACTION :
3095                 {
3096                     /** CHECKED, WORKS WELL */
3097                     // I already implemented
3098                     //     XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END
3099                     //     XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END,
3100                     // but opted to remove these again; it works well without them
3101                     // and makes the code less dependent from those Metafile Add-Ons
3102                     const MetaCommentAction* pA = (const MetaCommentAction*)pAction;
3103 
3104                     if(COMPARE_EQUAL == pA->GetComment().CompareIgnoreCaseToAscii("XGRAD_SEQ_BEGIN"))
3105                     {
3106                         // XGRAD_SEQ_BEGIN, XGRAD_SEQ_END should be supported since the
3107                         // pure recorded paint of the gradients uses the XOR paint functionality
3108                         // ('trick'). This is (and will be) problematic with AntAliasing, so it's
3109                         // better to use this info
3110                         const MetaGradientExAction* pMetaGradientExAction = 0;
3111                         bool bDone(false);
3112                         sal_uInt32 b(nAction + 1);
3113 
3114                         for(; !bDone && b < nCount; b++)
3115                         {
3116                             pAction = rMetaFile.GetAction(b);
3117 
3118                             if(META_GRADIENTEX_ACTION == pAction->GetType())
3119                             {
3120                                 pMetaGradientExAction = (const MetaGradientExAction*)pAction;
3121                             }
3122                             else if(META_COMMENT_ACTION == pAction->GetType())
3123                             {
3124                                 if(COMPARE_EQUAL == ((const MetaCommentAction*)pAction)->GetComment().CompareIgnoreCaseToAscii("XGRAD_SEQ_END"))
3125                                 {
3126                                     bDone = true;
3127                                 }
3128                             }
3129                         }
3130 
3131                         if(bDone && pMetaGradientExAction)
3132                         {
3133                             // consume actions and skip forward
3134                             nAction = b - 1;
3135 
3136                             // get geometry data
3137                             basegfx::B2DPolyPolygon aPolyPolygon(pMetaGradientExAction->GetPolyPolygon().getB2DPolyPolygon());
3138 
3139                             if(aPolyPolygon.count())
3140                             {
3141                                 // transform geometry
3142                                 aPolyPolygon.transform(rPropertyHolders.Current().getTransformation());
3143 
3144                                 // get and check if gradient is a real gradient
3145                                 const Gradient& rGradient = pMetaGradientExAction->GetGradient();
3146                                 const drawinglayer::attribute::FillGradientAttribute aAttribute(createFillGradientAttribute(rGradient));
3147 
3148                                 if(aAttribute.getStartColor() == aAttribute.getEndColor())
3149                                 {
3150                                     // not really a gradient
3151                                     rTargetHolders.Current().append(
3152                                         new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
3153                                             aPolyPolygon,
3154                                             aAttribute.getStartColor()));
3155                                 }
3156                                 else
3157                                 {
3158                                     // really a gradient
3159                                     rTargetHolders.Current().append(
3160                                         new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D(
3161                                             aPolyPolygon,
3162                                             aAttribute));
3163                                 }
3164                             }
3165                         }
3166                     }
3167 
3168                     break;
3169                 }
3170                 default:
3171                 {
3172                     OSL_ENSURE(false, "Unknown MetaFile Action (!)");
3173                     break;
3174                 }
3175             }
3176         }
3177     }
3178 } // end of anonymous namespace
3179 
3180 //////////////////////////////////////////////////////////////////////////////
3181 
3182 namespace drawinglayer
3183 {
3184     namespace primitive2d
3185     {
3186         Primitive2DSequence MetafilePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const
3187         {
3188             // prepare target and properties; each will have one default entry
3189             TargetHolders aTargetHolders;
3190             PropertyHolders aPropertyHolders;
3191 
3192             // set target MapUnit at Properties
3193             aPropertyHolders.Current().setMapUnit(getMetaFile().GetPrefMapMode().GetMapUnit());
3194 
3195             // interpret the Metafile
3196             interpretMetafile(getMetaFile(), aTargetHolders, aPropertyHolders, rViewInformation);
3197 
3198             // get the content. There should be only one target, as in the start condition,
3199             // but iterating will be the right thing to do when some push/pop is not closed
3200             Primitive2DSequence xRetval;
3201 
3202             while(aTargetHolders.size() > 1)
3203             {
3204                 appendPrimitive2DSequenceToPrimitive2DSequence(xRetval,
3205                     aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
3206                 aTargetHolders.Pop();
3207             }
3208 
3209             appendPrimitive2DSequenceToPrimitive2DSequence(xRetval,
3210                 aTargetHolders.Current().getPrimitive2DSequence(aPropertyHolders.Current()));
3211 
3212             if(xRetval.hasElements())
3213             {
3214                 // get target size
3215                 const Rectangle aMtfTarget(getMetaFile().GetPrefMapMode().GetOrigin(), getMetaFile().GetPrefSize());
3216 
3217                 // create transformation
3218                 basegfx::B2DHomMatrix aAdaptedTransform;
3219 
3220                 aAdaptedTransform.translate(-aMtfTarget.Left(), -aMtfTarget.Top());
3221                 aAdaptedTransform.scale(
3222                     aMtfTarget.getWidth() ? 1.0 / aMtfTarget.getWidth() : 1.0,
3223                     aMtfTarget.getHeight() ? 1.0 / aMtfTarget.getHeight() : 1.0);
3224                 aAdaptedTransform = getTransform() * aAdaptedTransform;
3225 
3226                 // embed to target transformation
3227                 const Primitive2DReference aEmbeddedTransform(
3228                     new TransformPrimitive2D(
3229                         aAdaptedTransform,
3230                         xRetval));
3231 
3232                 xRetval = Primitive2DSequence(&aEmbeddedTransform, 1);
3233             }
3234 
3235             return xRetval;
3236         }
3237 
3238         MetafilePrimitive2D::MetafilePrimitive2D(
3239             const basegfx::B2DHomMatrix& rMetaFileTransform,
3240             const GDIMetaFile& rMetaFile)
3241         :   BufferedDecompositionPrimitive2D(),
3242             maMetaFileTransform(rMetaFileTransform),
3243             maMetaFile(rMetaFile)
3244         {
3245         }
3246 
3247         bool MetafilePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
3248         {
3249             if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
3250             {
3251                 const MetafilePrimitive2D& rCompare = (MetafilePrimitive2D&)rPrimitive;
3252 
3253                 return (getTransform() == rCompare.getTransform()
3254                     && getMetaFile() == rCompare.getMetaFile());
3255             }
3256 
3257             return false;
3258         }
3259 
3260         basegfx::B2DRange MetafilePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
3261         {
3262             // use own implementation to quickly answer the getB2DRange question. The
3263             // MetafilePrimitive2D assumes that all geometry is inside of the shape. If
3264             // this is not the case (i have already seen some wrong Metafiles) it should
3265             // be embedded to a MaskPrimitive2D
3266             basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
3267             aRetval.transform(getTransform());
3268 
3269             return aRetval;
3270         }
3271 
3272         // provide unique ID
3273         ImplPrimitrive2DIDBlock(MetafilePrimitive2D, PRIMITIVE2D_ID_METAFILEPRIMITIVE2D)
3274 
3275     } // end of namespace primitive2d
3276 } // end of namespace drawinglayer
3277 
3278 //////////////////////////////////////////////////////////////////////////////
3279 // eof
3280