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