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