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