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