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