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