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