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