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