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