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