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 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_drawinglayer.hxx"
24 
25 #include <drawinglayer/processor2d/vclmetafileprocessor2d.hxx>
26 #include <tools/gen.hxx>
27 #include <vcl/virdev.hxx>
28 #include <vcl/gdimtf.hxx>
29 #include <vcl/gradient.hxx>
30 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
31 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
32 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
33 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
34 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
35 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
36 #include <basegfx/polygon/b2dpolygonclipper.hxx>
37 #include <basegfx/polygon/b2dpolypolygontools.hxx>
38 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
39 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
40 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
41 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
42 #include <drawinglayer/processor2d/vclpixelprocessor2d.hxx>
43 #include <tools/stream.hxx>
44 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
45 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
46 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
47 #include <vcl/graphictools.hxx>
48 #include <vcl/metaact.hxx>
49 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
50 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
51 #include <comphelper/processfactory.hxx>
52 #include <rtl/ustring.hxx>
53 #include <com/sun/star/i18n/CharacterIteratorMode.hdl>
54 #include <com/sun/star/i18n/WordType.hpp>
55 #include <drawinglayer/primitive2d/controlprimitive2d.hxx>
56 #include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
57 #include <basegfx/polygon/b2dpolygontools.hxx>
58 #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
59 #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
60 #include <basegfx/polygon/b2dlinegeometry.hxx>
61 #include <vcl/dibtools.hxx>
62 
63 //////////////////////////////////////////////////////////////////////////////
64 // for PDFExtOutDevData Graphic support
65 
66 #include <vcl/graph.hxx>
67 #include <vcl/svapp.hxx>
68 #include <toolkit/helper/formpdfexport.hxx>
69 
70 //////////////////////////////////////////////////////////////////////////////
71 // for Control printing
72 
73 #include <com/sun/star/beans/XPropertySet.hpp>
74 
75 //////////////////////////////////////////////////////////////////////////////
76 // for StructureTagPrimitive support in sd's unomodel.cxx
77 
78 #include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
79 
80 //////////////////////////////////////////////////////////////////////////////
81 
82 using namespace com::sun::star;
83 
84 //////////////////////////////////////////////////////////////////////////////
85 // #112245# definition for maximum allowed point count due to Metafile target.
86 // To be on the safe side with the old tools polygon, use slightly less then
87 // the theoretical maximum (bad experiences with tools polygon)
88 
89 #define MAX_POLYGON_POINT_COUNT_METAFILE    (0x0000fff0)
90 
91 //////////////////////////////////////////////////////////////////////////////
92 
93 namespace
94 {
95     // #112245# helper to split line polygon in half
96     void splitLinePolygon(
97         const basegfx::B2DPolygon& rBasePolygon,
98         basegfx::B2DPolygon& o_aLeft,
99         basegfx::B2DPolygon& o_aRight)
100     {
101         const sal_uInt32 nCount(rBasePolygon.count());
102 
103         if(nCount)
104         {
105             const sal_uInt32 nHalfCount((nCount - 1) >> 1);
106 
107             o_aLeft = basegfx::B2DPolygon(rBasePolygon, 0, nHalfCount + 1);
108             o_aLeft.setClosed(false);
109 
110             o_aRight = basegfx::B2DPolygon(rBasePolygon, nHalfCount, nCount - nHalfCount);
111             o_aRight.setClosed(false);
112 
113             if(rBasePolygon.isClosed())
114             {
115                 o_aRight.append(rBasePolygon.getB2DPoint(0));
116 
117                 if(rBasePolygon.areControlPointsUsed())
118                 {
119                     o_aRight.setControlPoints(
120                         o_aRight.count() - 1,
121                         rBasePolygon.getPrevControlPoint(0),
122                         rBasePolygon.getNextControlPoint(0));
123                 }
124             }
125         }
126         else
127         {
128             o_aLeft.clear();
129             o_aRight.clear();
130         }
131     }
132 
133     // #112245# helper to evtl. split filled polygons to maximum metafile point count
134     bool fillPolyPolygonNeededToBeSplit(basegfx::B2DPolyPolygon& rPolyPolygon)
135     {
136         bool bRetval(false);
137         const sal_uInt32 nPolyCount(rPolyPolygon.count());
138 
139         if(nPolyCount)
140         {
141             basegfx::B2DPolyPolygon aSplitted;
142 
143             for(sal_uInt32 a(0); a < nPolyCount; a++)
144             {
145                 const basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(a));
146                 const sal_uInt32 nPointCount(aCandidate.count());
147                 bool bNeedToSplit(false);
148 
149                 if(aCandidate.areControlPointsUsed())
150                 {
151                     // compare with the maximum for bezier curved polygons
152             		bNeedToSplit = nPointCount > ((MAX_POLYGON_POINT_COUNT_METAFILE / 3L) - 1L);
153                 }
154                 else
155                 {
156                     // compare with the maximum for simple point polygons
157                     bNeedToSplit = nPointCount > (MAX_POLYGON_POINT_COUNT_METAFILE - 1);
158                 }
159 
160                 if(bNeedToSplit)
161                 {
162                     // need to split the partial polygon
163                     const basegfx::B2DRange aRange(aCandidate.getB2DRange());
164             		const basegfx::B2DPoint aCenter(aRange.getCenter());
165 
166                     if(aRange.getWidth() > aRange.getHeight())
167                     {
168                         // clip in left and right
169                         const basegfx::B2DPolyPolygon aLeft(
170                             basegfx::tools::clipPolygonOnParallelAxis(
171                                 aCandidate,
172                                 false,
173                                 true,
174                                 aCenter.getX(),
175                                 false));
176                         const basegfx::B2DPolyPolygon aRight(
177                             basegfx::tools::clipPolygonOnParallelAxis(
178                                 aCandidate,
179                                 false,
180                                 false,
181                                 aCenter.getX(),
182                                 false));
183 
184                         aSplitted.append(aLeft);
185                         aSplitted.append(aRight);
186                     }
187                     else
188                     {
189                         // clip in top and bottom
190                         const basegfx::B2DPolyPolygon aTop(
191                             basegfx::tools::clipPolygonOnParallelAxis(
192                                 aCandidate,
193                                 true,
194                                 true,
195                                 aCenter.getY(),
196                                 false));
197                         const basegfx::B2DPolyPolygon aBottom(
198                             basegfx::tools::clipPolygonOnParallelAxis(
199                                 aCandidate,
200                                 true,
201                                 false,
202                                 aCenter.getY(),
203                                 false));
204 
205                         aSplitted.append(aTop);
206                         aSplitted.append(aBottom);
207                     }
208                 }
209                 else
210                 {
211                     aSplitted.append(aCandidate);
212                 }
213             }
214 
215             if(aSplitted.count() != nPolyCount)
216             {
217                 rPolyPolygon = aSplitted;
218             }
219         }
220 
221         return bRetval;
222     }
223 } // end of anonymous namespace
224 
225 //////////////////////////////////////////////////////////////////////////////
226 
227 namespace drawinglayer
228 {
229 	namespace processor2d
230 	{
231         Rectangle VclMetafileProcessor2D::impDumpToMetaFile(
232 			const primitive2d::Primitive2DSequence& rContent,
233 			GDIMetaFile& o_rContentMetafile)
234         {
235             // Prepare VDev, MetaFile and connections
236 			OutputDevice* pLastOutputDevice = mpOutputDevice;
237             GDIMetaFile* pLastMetafile = mpMetaFile;
238 			basegfx::B2DRange aPrimitiveRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent, getViewInformation2D()));
239 
240 			// transform primitive range with current transformation (e.g shadow offset)
241 			aPrimitiveRange.transform(maCurrentTransformation);
242 
243 			const Rectangle aPrimitiveRectangle(
244 				basegfx::fround(aPrimitiveRange.getMinX()), basegfx::fround(aPrimitiveRange.getMinY()),
245 				basegfx::fround(aPrimitiveRange.getMaxX()), basegfx::fround(aPrimitiveRange.getMaxY()));
246 			VirtualDevice aContentVDev;
247 			MapMode aNewMapMode(pLastOutputDevice->GetMapMode());
248 
249 			mpOutputDevice = &aContentVDev;
250             mpMetaFile = &o_rContentMetafile;
251 			aContentVDev.EnableOutput(false);
252 			aContentVDev.SetMapMode(pLastOutputDevice->GetMapMode());
253 			o_rContentMetafile.Record(&aContentVDev);
254 			aContentVDev.SetLineColor(pLastOutputDevice->GetLineColor());
255 			aContentVDev.SetFillColor(pLastOutputDevice->GetFillColor());
256 			aContentVDev.SetFont(pLastOutputDevice->GetFont());
257 			aContentVDev.SetDrawMode(pLastOutputDevice->GetDrawMode());
258 			aContentVDev.SetSettings(pLastOutputDevice->GetSettings());
259 			aContentVDev.SetRefPoint(pLastOutputDevice->GetRefPoint());
260 
261             // dump to MetaFile
262 			process(rContent);
263 
264             // cleanups
265 			o_rContentMetafile.Stop();
266 			o_rContentMetafile.WindStart();
267 			aNewMapMode.SetOrigin(aPrimitiveRectangle.TopLeft());
268 			o_rContentMetafile.SetPrefMapMode(aNewMapMode);
269 			o_rContentMetafile.SetPrefSize(aPrimitiveRectangle.GetSize());
270 			mpOutputDevice = pLastOutputDevice;
271             mpMetaFile = pLastMetafile;
272 
273             return aPrimitiveRectangle;
274         }
275 
276 		void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient(
277 			Gradient& o_rVCLGradient,
278 			const attribute::FillGradientAttribute& rFiGrAtt,
279 			bool bIsTransparenceGradient)
280         {
281 			if(bIsTransparenceGradient)
282 			{
283 				// it's about transparence channel intensities (black/white), do not use color modifier
284 			    o_rVCLGradient.SetStartColor(Color(rFiGrAtt.getStartColor()));
285 			    o_rVCLGradient.SetEndColor(Color(rFiGrAtt.getEndColor()));
286 			}
287 			else
288 			{
289 				// use color modifier to influence start/end color of gradient
290 			    o_rVCLGradient.SetStartColor(Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getStartColor())));
291 			    o_rVCLGradient.SetEndColor(Color(maBColorModifierStack.getModifiedColor(rFiGrAtt.getEndColor())));
292 			}
293 
294             o_rVCLGradient.SetAngle(static_cast< sal_uInt16 >(rFiGrAtt.getAngle() * (1.0 / F_PI1800)));
295             o_rVCLGradient.SetBorder(static_cast< sal_uInt16 >(rFiGrAtt.getBorder() * 100.0));
296 		    o_rVCLGradient.SetOfsX(static_cast< sal_uInt16 >(rFiGrAtt.getOffsetX() * 100.0));
297 		    o_rVCLGradient.SetOfsY(static_cast< sal_uInt16 >(rFiGrAtt.getOffsetY() * 100.0));
298 		    o_rVCLGradient.SetSteps(rFiGrAtt.getSteps());
299 
300             // defaults for intensity; those were computed into the start/end colors already
301 		    o_rVCLGradient.SetStartIntensity(100);
302 		    o_rVCLGradient.SetEndIntensity(100);
303 
304             switch(rFiGrAtt.getStyle())
305             {
306                 default : // attribute::GRADIENTSTYLE_LINEAR :
307                 {
308                     o_rVCLGradient.SetStyle(GRADIENT_LINEAR);
309                     break;
310                 }
311                 case attribute::GRADIENTSTYLE_AXIAL :
312                 {
313                     o_rVCLGradient.SetStyle(GRADIENT_AXIAL);
314                     break;
315                 }
316                 case attribute::GRADIENTSTYLE_RADIAL :
317                 {
318                     o_rVCLGradient.SetStyle(GRADIENT_RADIAL);
319                     break;
320                 }
321                 case attribute::GRADIENTSTYLE_ELLIPTICAL :
322                 {
323                     o_rVCLGradient.SetStyle(GRADIENT_ELLIPTICAL);
324                     break;
325                 }
326                 case attribute::GRADIENTSTYLE_SQUARE :
327                 {
328                     o_rVCLGradient.SetStyle(GRADIENT_SQUARE);
329                     break;
330                 }
331                 case attribute::GRADIENTSTYLE_RECT :
332                 {
333                     o_rVCLGradient.SetStyle(GRADIENT_RECT);
334                     break;
335                 }
336             }
337         }
338 
339 		void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill* pSvtGraphicFill)
340 		{
341 			if(pSvtGraphicFill && !mnSvtGraphicFillCount)
342 			{
343 				SvMemoryStream aMemStm;
344 
345 				aMemStm << *pSvtGraphicFill;
346 				mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_BEGIN", 0, static_cast< const sal_uInt8* >(aMemStm.GetData()), aMemStm.Seek(STREAM_SEEK_TO_END)));
347 				mnSvtGraphicFillCount++;
348 			}
349 		}
350 
351 		void VclMetafileProcessor2D::impEndSvtGraphicFill(SvtGraphicFill* pSvtGraphicFill)
352 		{
353 			if(pSvtGraphicFill && mnSvtGraphicFillCount)
354 			{
355 				mnSvtGraphicFillCount--;
356 				mpMetaFile->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END"));
357 				delete pSvtGraphicFill;
358 			}
359 		}
360 
361 		SvtGraphicStroke* VclMetafileProcessor2D::impTryToCreateSvtGraphicStroke(
362 			const basegfx::B2DPolygon& rB2DPolygon,
363 			const basegfx::BColor* pColor,
364 			const attribute::LineAttribute* pLineAttribute,
365 			const attribute::StrokeAttribute* pStrokeAttribute,
366 			const attribute::LineStartEndAttribute* pStart,
367 			const attribute::LineStartEndAttribute* pEnd)
368 		{
369 			SvtGraphicStroke* pRetval = 0;
370 
371 			if(rB2DPolygon.count() && !mnSvtGraphicStrokeCount)
372 			{
373 				basegfx::BColor aStrokeColor;
374 				basegfx::B2DPolyPolygon aStartArrow;
375 				basegfx::B2DPolyPolygon aEndArrow;
376 
377 				if(pColor)
378 				{
379 					aStrokeColor = *pColor;
380 				}
381 				else if(pLineAttribute)
382 				{
383 					aStrokeColor = maBColorModifierStack.getModifiedColor(pLineAttribute->getColor());
384 				}
385 
386 				// It IS needed to record the stroke color at all in the metafile,
387 				// SvtGraphicStroke has NO entry for stroke color(!)
388 				mpOutputDevice->SetLineColor(Color(aStrokeColor));
389 
390 				if(!rB2DPolygon.isClosed())
391 				{
392 					double fPolyLength(0.0);
393 
394 					if(pStart && pStart->isActive())
395 					{
396 						fPolyLength = basegfx::tools::getLength(rB2DPolygon);
397 
398 						aStartArrow = basegfx::tools::createAreaGeometryForLineStartEnd(
399 							rB2DPolygon, pStart->getB2DPolyPolygon(), true, pStart->getWidth(),
400 							fPolyLength, pStart->isCentered() ? 0.5 : 0.0, 0);
401 					}
402 
403 					if(pEnd && pEnd->isActive())
404 					{
405 						if(basegfx::fTools::equalZero(fPolyLength))
406 						{
407 							fPolyLength = basegfx::tools::getLength(rB2DPolygon);
408 						}
409 
410 						aEndArrow = basegfx::tools::createAreaGeometryForLineStartEnd(
411 							rB2DPolygon, pEnd->getB2DPolyPolygon(), false, pEnd->getWidth(),
412 							fPolyLength, pEnd->isCentered() ? 0.5 : 0.0, 0);
413 					}
414 				}
415 
416                 SvtGraphicStroke::JoinType eJoin(SvtGraphicStroke::joinNone);
417                 SvtGraphicStroke::CapType eCap(SvtGraphicStroke::capButt);
418 				double fLineWidth(0.0);
419 				double fMiterLength(0.0);
420 				SvtGraphicStroke::DashArray aDashArray;
421 
422 				if(pLineAttribute)
423 				{
424 					// pre-fill fLineWidth #119198# Need to apply maCurrentTransformation, too (!)
425 					const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation * basegfx::B2DVector(pLineAttribute->getWidth(), 0.0));
426 					fLineWidth = aDiscreteUnit.getLength();
427 
428 					// pre-fill fMiterLength
429 					fMiterLength = fLineWidth;
430 
431 					// get Join
432 					switch(pLineAttribute->getLineJoin())
433 					{
434 						default : // basegfx::B2DLINEJOIN_NONE :
435 						{
436 							eJoin = SvtGraphicStroke::joinNone;
437 							break;
438 						}
439 						case basegfx::B2DLINEJOIN_BEVEL :
440 						{
441 							eJoin = SvtGraphicStroke::joinBevel;
442 							break;
443 						}
444 						case basegfx::B2DLINEJOIN_MIDDLE :
445 						case basegfx::B2DLINEJOIN_MITER :
446 						{
447 							eJoin = SvtGraphicStroke::joinMiter;
448 							// ATM 15 degrees is assumed
449 							fMiterLength /= rtl::math::sin(M_PI * (15.0 / 360.0));
450 							break;
451 						}
452 						case basegfx::B2DLINEJOIN_ROUND :
453 						{
454 							eJoin = SvtGraphicStroke::joinRound;
455 							break;
456 						}
457 					}
458 
459                     // get stroke
460                     switch(pLineAttribute->getLineCap())
461                     {
462                         default: /* com::sun::star::drawing::LineCap_BUTT */
463                         {
464                             eCap = SvtGraphicStroke::capButt;
465                             break;
466                         }
467                         case com::sun::star::drawing::LineCap_ROUND:
468                         {
469                             eCap = SvtGraphicStroke::capRound;
470                             break;
471                         }
472                         case com::sun::star::drawing::LineCap_SQUARE:
473                         {
474                             eCap = SvtGraphicStroke::capSquare;
475                             break;
476                         }
477                     }
478                 }
479 
480 				if(pStrokeAttribute)
481 				{
482 					// copy dash array
483 					aDashArray = pStrokeAttribute->getDotDashArray();
484 				}
485 
486 				// #i101734# apply current object transformation to created geometry.
487 				// This is a partial fix. When a object transformation is used which
488 				// e.g. contains a scaleX != scaleY, an unproportional scaling would
489 				// have to be applied to the evtl. existing fat line. The current
490 				// concept of PDF export and SvtGraphicStroke usage does simply not
491 				// allow handling such definitions. The only clean way would be to
492 				// add the transformation to SvtGraphicStroke and to handle it there
493 				basegfx::B2DPolygon aB2DPolygon(rB2DPolygon);
494 
495 				aB2DPolygon.transform(maCurrentTransformation);
496 				aStartArrow.transform(maCurrentTransformation);
497 				aEndArrow.transform(maCurrentTransformation);
498 
499 				pRetval = new SvtGraphicStroke(
500 					Polygon(aB2DPolygon),
501 					PolyPolygon(aStartArrow),
502 					PolyPolygon(aEndArrow),
503 					mfCurrentUnifiedTransparence,
504 					fLineWidth,
505 					eCap,
506 					eJoin,
507 					fMiterLength,
508 					aDashArray);
509 			}
510 
511 			return pRetval;
512 		}
513 
514 		void VclMetafileProcessor2D::impStartSvtGraphicStroke(SvtGraphicStroke* pSvtGraphicStroke)
515 		{
516 			if(pSvtGraphicStroke && !mnSvtGraphicStrokeCount)
517 			{
518 				SvMemoryStream aMemStm;
519 
520 				aMemStm << *pSvtGraphicStroke;
521 				mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_BEGIN", 0, static_cast< const sal_uInt8* >(aMemStm.GetData()), aMemStm.Seek(STREAM_SEEK_TO_END)));
522 				mnSvtGraphicStrokeCount++;
523 			}
524 		}
525 
526 		void VclMetafileProcessor2D::impEndSvtGraphicStroke(SvtGraphicStroke* pSvtGraphicStroke)
527 		{
528 			if(pSvtGraphicStroke && mnSvtGraphicStrokeCount)
529 			{
530 				mnSvtGraphicStrokeCount--;
531 				mpMetaFile->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END"));
532 				delete pSvtGraphicStroke;
533 			}
534 		}
535 
536         // init static break iterator
537         uno::Reference< ::com::sun::star::i18n::XBreakIterator > VclMetafileProcessor2D::mxBreakIterator;
538 
539 		VclMetafileProcessor2D::VclMetafileProcessor2D(const geometry::ViewInformation2D& rViewInformation, OutputDevice& rOutDev)
540 		:	VclProcessor2D(rViewInformation, rOutDev),
541 			mpMetaFile(rOutDev.GetConnectMetaFile()),
542 			mnSvtGraphicFillCount(0),
543 			mnSvtGraphicStrokeCount(0),
544 			mfCurrentUnifiedTransparence(0.0),
545 			mpPDFExtOutDevData(dynamic_cast< vcl::PDFExtOutDevData* >(rOutDev.GetExtOutDevData()))
546 		{
547 			OSL_ENSURE(rOutDev.GetConnectMetaFile(), "VclMetafileProcessor2D: Used on OutDev which has no MetaFile Target (!)");
548 			// draw to logic coordinates, do not initialize maCurrentTransformation to viewTransformation
549             // but only to ObjectTransformation. Do not change MapMode of destination.
550 			maCurrentTransformation = rViewInformation.getObjectTransformation();
551 		}
552 
553 		VclMetafileProcessor2D::~VclMetafileProcessor2D()
554 		{
555 			// MapMode was not changed, no restore necessary
556 		}
557 
558         /***********************************************************************************************
559 
560             Support of MetaCommentActions in the VclMetafileProcessor2D
561             Found MetaCommentActions and how they are supported:
562 
563             XGRAD_SEQ_BEGIN, XGRAD_SEQ_END:
564 
565 			Used inside OutputDevice::DrawGradient to mark the start and end of a MetaGradientEx action.
566             It is used in various exporters/importers to have direct access to the gradient before it
567             is rendered by VCL (and thus fragmented to polygon color actions and others). On that base, e.g.
568             the Metafile to SdrObject import creates it's gradient objects.
569             Best (and safest) way to support it here is to use PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D,
570             map it back to the corresponding tools PolyPolygon and the Gradient and just call
571             OutputDevice::DrawGradient which creates the necessary compatible actions.
572 
573             XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END:
574 
575 			Two producers, one is vcl/source/gdi/gdimtf.cxx, line 1273. There, it is transformed
576             inside GDIMetaFile::Rotate, nothing to take care of here.
577             The second producer is in graphics/svx/source/svdraw/impgrfll.cxx, line 374. This is used
578             with each incarnation of Imp_GraphicFill when a metafile is recorded, fillstyle is not
579             XFILL_NONE and not completely transparent. It creates a SvtGraphicFill and streams it
580             to the comment action. A closing end token is created in the destructor.
581 			Usages of Imp_GraphicFill are in Do_Paint_Object-methods of SdrCircObj, SdrPathObj and
582 			SdrRectObj.
583             The token users pick various actions from SvtGraphicFill, so it may need to be added for all kind
584             of filled objects, even simple colored polygons. It is added as extra information; the
585             Metafile actions between the two tokens are interpreted as output generated from those
586             fills. Thus, users have the choice to use the SvtGraphicFill info or the created output
587             actions.
588             Even for XFillTransparenceItem it is used, thus it may need to be supported in
589             UnifiedTransparencePrimitive2D, too, when interpreted as normally filled PolyPolygon.
590 			Implemented for:
591 				PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D,
592 				PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D,
593 				PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D,
594 				PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D,
595 				and for PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D when detected unified transparence
596 
597             XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END:
598 
599 			Similar to pathfill, but using SvtGraphicStroke instead. It also has two producers where one
600 			is also the GDIMetaFile::Rotate. Another user is MetaCommentAction::Move which modifies the
601 			contained path accordingly.
602 			The other one is SdrObject::Imp_DrawLineGeometry. It's done when MetaFile is set at OutDev and
603 			only when geometry is a single polygon (!). I see no reason for that; in the PS exporter this
604 			would hinder to make use of PolyPolygon strokes. I will need to add support at:
605 				PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
606 				PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
607 				PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D
608 			This can be done hierarchical, too.
609 			Okay, base implementation done based on those three primitives.
610 
611             FIELD_SEQ_BEGIN, FIELD_SEQ_END
612 
613             Used from slideshow for URLs, created from diverse SvxField implementations inside
614             createBeginComment()/createEndComment(). createBeginComment() is used from editeng\impedit3.cxx
615             inside ImpEditEngine::Paint.
616             Created TextHierarchyFieldPrimitive2D and added needed infos there; it is an group primitive and wraps
617             text primitives (but is not limited to that). It contains the field type if special actions for the
618 			support of FIELD_SEQ_BEGIN/END are needed; this is the case for Page and URL fields. If more is
619 			needed, it may be supported there.
620             FIELD_SEQ_BEGIN;PageField
621             FIELD_SEQ_END
622             Okay, these are now completely supported by TextHierarchyFieldPrimitive2D. URL works, too.
623 
624             XTEXT
625 
626             XTEXT_EOC(i) end of character
627             XTEXT_EOW(i) end of word
628             XTEXT_EOS(i) end of sentence
629 
630             this three are with index and are created with the help of a i18n::XBreakIterator in
631             ImplDrawWithComments. Simplifying, moving out text painting, reworking to create some
632             data structure for holding those TEXT infos.
633             Supported directly by TextSimplePortionPrimitive2D with adding a Locale to the basic text
634             primitive. In the MetaFileRenderer, the creation is now done (see below). This has the advantage
635             that this creations do not need to be done for all paints all the time. This would be
636             expensive since the BreakIterator and it's usage is expensive and for each paint also the
637             whole character stops would need to be created.
638             Created only for TextDecoratedPortionPrimitive2D due to XTEXT_EOL and XTEXT_EOP (see below)
639 
640             XTEXT_EOL() end of line
641             XTEXT_EOP() end of paragraph
642 
643             First try with boolean marks at TextDecoratedPortionPrimitive2D did not work too well,
644 			i decided to solve it with structure. I added the TextHierarchyPrimitives for this,
645 			namely:
646 			- TextHierarchyLinePrimitive2D: Encapsulates single line
647 			- TextHierarchyParagraphPrimitive2D: Encapsulates single paragraph
648 			- TextHierarchyBlockPrimitive2D: encapsulates object texts (only one ATM)
649 			Those are now supported in hierarchy. This means the MetaFile renderer will support them
650 			by using them, reculrively using their content and adding MetaFile comments as needed.
651 			This also means that when another text layouter will be used it will be necessary to
652 			create/support the same HierarchyPrimitives to support users.
653 			To transport the information using this hierarchy is best suited to all future needs;
654 			the slideshow will be able to profit from it directly when using primitives; all other
655 			renderers not interested in the text structure will just ignore the encapsulations.
656 
657             XTEXT_PAINTSHAPE_BEGIN, XTEXT_PAINTSHAPE_END
658 			Supported now by the TextHierarchyBlockPrimitive2D.
659 
660             EPSReplacementGraphic:
661 			Only used in goodies\source\filter.vcl\ieps\ieps.cxx and svx\source\xml\xmlgrhlp.cxx to
662 			hold the original EPS which was imported in the same MetaFile as first 2 entries. Only
663 			used to export the original again (if exists).
664 			Not necessary to support with MetaFuleRenderer.
665 
666             XTEXT_SCROLLRECT, XTEXT_PAINTRECT
667 			Currently used to get extra MetaFile infos using GraphicExporter which again uses
668 			SdrTextObj::GetTextScrollMetaFileAndRectangle(). ATM works with primitives since
669 			the rectangle data is added directly by the GraphicsExporter as comment. Does not need
670 			to be adapted at once.
671 			When adapting later, the only user - the diashow - should directly use the provided
672 			Anination infos in the appropriate primitives (e.g. AnimatedSwitchPrimitive2D)
673 
674             PRNSPOOL_TRANSPARENTBITMAP_BEGIN, PRNSPOOL_TRANSPARENTBITMAP_END
675 			VCL usage when printing PL -> THB. Okay, THB confirms that it is only used as
676 			a fix (hack) while VCL printing. It is needed to not downscale a bitmap which
677 			was explicitely created for the printer already again to some default maximum
678 			bitmap sizes.
679 			Nothing to do here for the primitive renderer.
680 
681 			Support for vcl::PDFExtOutDevData:
682 			PL knows that SJ did that stuff, it's used to hold a pointer to PDFExtOutDevData at
683 			the OutDev. When set, some extra data is written there. Trying simple PDF export and
684 			watching if i get those infos.
685 			Well, a PDF export does not use e.g. ImpEditEngine::Paint since the PdfFilter uses
686 			the SdXImpressDocument::render and thus uses the VclMetafileProcessor2D. I will check
687 			if i get a PDFExtOutDevData at the target output device.
688 			Indeed, i get one. Checking what all may be done when that extra-device-info is there.
689 
690 		    All in all i have to talk to SJ. I will need to emulate some of those actions, but
691 			i need to discuss which ones.
692 			In the future, all those infos would be taken from the primitive sequence anyways,
693 			thus these extensions would potentially be temporary, too.
694             Discussed with SJ, added the necessary support and tested it. Details follow.
695 
696 			- In ImpEditEngine::Paint, paragraph infos and URL stuff is added.
697 			  Added in primitive MetaFile renderer.
698 			  Checking URL: Indeed, current version exports it, but it is missing in primitive
699 			  CWS version. Adding support.
700 			  Okay, URLs work. Checked, Done.
701 
702             - UnoControlPDFExportContact is only created when PDFExtOutDevData is used at the
703 			  target and uno control data is created in UnoControlPDFExportContact::do_PaintObject.
704 			  This may be added in primitive MetaFile renderer.
705 			  Adding support...
706 			  OOps, the necessary helper stuff is in svx/source/form/formpdxexport.cxx in namespace
707 			  svxform. Have to talk to FS if this has to be like that. Especially since
708 			  ::vcl::PDFWriter::AnyWidget is filled out, which is already part of vcl.
709 			  Wrote an eMail to FS, he is on vacation currently. I see no reason why not to move
710 			  that stuff to somewhere else, maybe tools or svtools ?!? We will see...
711               Moved to toolkit, so i have to link against it. I tried VCL first, but it did
712               not work since VCLUnoHelper::CreateFont is unresolved in VCL (!). Other then the name
713               may imply, it is defined in toolkit (!). Since toolkit is linked against VCL itself,
714               the lowest move,ment plave is toolkit.
715               Checked form control export, it works well. Done.
716 
717 		    - In goodies, in GraphicObject::Draw, when the used Graphic is linked, infos are
718 			  generated. I will need to check what happens here with primitives.
719 			  To support, use of GraphicPrimitive2D (PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D) may be needed.
720 			  Added support, but feature is broken in main version, so i cannot test at all.
721 			  Writing a bug to CL (or SJ) and seeing what happens (#i80380#).
722               SJ took a look and we got it working. Tested VCL MetaFile Renderer based export,
723               as intended, the original file is exported. Works, Done.
724 
725 
726 
727 
728             To be done:
729 
730             - Maybe there are more places to take care of for vcl::PDFExtOutDevData!
731 
732 
733 
734         ****************************************************************************************************/
735 
736 		void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
737 		{
738             switch(rCandidate.getPrimitive2DID())
739 			{
740                 case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D :
741                 {
742 					// directdraw of wrong spell primitive
743                     // Ignore for VclMetafileProcessor2D, this is for printing and MetaFile recording only
744                     break;
745                 }
746 				case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D :
747 				{
748                     const primitive2d::GraphicPrimitive2D& rGraphicPrimitive = static_cast< const primitive2d::GraphicPrimitive2D& >(rCandidate);
749 					bool bUsingPDFExtOutDevData(false);
750 					basegfx::B2DVector aTranslate, aScale;
751 					static bool bSuppressPDFExtOutDevDataSupport(false);
752 
753 					if(mpPDFExtOutDevData && !bSuppressPDFExtOutDevDataSupport)
754 					{
755 						// emulate data handling from UnoControlPDFExportContact, original see
756 						// svtools/source/graphic/grfmgr.cxx
757 						const Graphic& rGraphic = rGraphicPrimitive.getGraphicObject().GetGraphic();
758 
759 						if(rGraphic.IsLink())
760 						{
761 							const GraphicAttr& rAttr = rGraphicPrimitive.getGraphicAttr();
762 
763 							if(!rAttr.IsSpecialDrawMode() && !rAttr.IsAdjusted())
764 							{
765 								const basegfx::B2DHomMatrix& rTransform = rGraphicPrimitive.getTransform();
766 								double fRotate, fShearX;
767 								rTransform.decompose(aScale, aTranslate, fRotate, fShearX);
768 
769 								if( basegfx::fTools::equalZero( fRotate ) && ( aScale.getX() > 0.0 ) && ( aScale.getY() > 0.0 ) )
770 								{
771 									bUsingPDFExtOutDevData = true;
772 									mpPDFExtOutDevData->BeginGroup();
773 								}
774 							}
775 						}
776 					}
777 
778 					// process recursively and add MetaFile comment
779 					process(rGraphicPrimitive.get2DDecomposition(getViewInformation2D()));
780 
781 					if(bUsingPDFExtOutDevData)
782 					{
783 						// emulate data handling from UnoControlPDFExportContact, original see
784 						// svtools/source/graphic/grfmgr.cxx
785 						const basegfx::B2DRange aCurrentRange(
786 							aTranslate.getX(), aTranslate.getY(),
787 							aTranslate.getX() + aScale.getX(), aTranslate.getY() + aScale.getY());
788 						const Rectangle aCurrentRect(
789 							sal_Int32(floor(aCurrentRange.getMinX())), sal_Int32(floor(aCurrentRange.getMinY())),
790 							sal_Int32(ceil(aCurrentRange.getMaxX())), sal_Int32(ceil(aCurrentRange.getMaxY())));
791 						const GraphicAttr& rAttr = rGraphicPrimitive.getGraphicAttr();
792 						Rectangle aCropRect;
793 
794 						if(rAttr.IsCropped())
795 						{
796 							// calculate scalings between real image size and logic object size. This
797 							// is necessary since the crop values are relative to original bitmap size
798 							double fFactorX(1.0);
799 							double fFactorY(1.0);
800 
801 							{
802 								const MapMode aMapMode100thmm(MAP_100TH_MM);
803 								const Size aBitmapSize(Application::GetDefaultDevice()->LogicToLogic(
804 									rGraphicPrimitive.getGraphicObject().GetPrefSize(),
805 									rGraphicPrimitive.getGraphicObject().GetPrefMapMode(), aMapMode100thmm));
806 								const double fDivX(aBitmapSize.Width() - rAttr.GetLeftCrop() - rAttr.GetRightCrop());
807 								const double fDivY(aBitmapSize.Height() - rAttr.GetTopCrop() - rAttr.GetBottomCrop());
808 
809 								if(!basegfx::fTools::equalZero(fDivX))
810 								{
811 									fFactorX = aScale.getX() / fDivX;
812 								}
813 
814 								if(!basegfx::fTools::equalZero(fDivY))
815 								{
816 									fFactorY = aScale.getY() / fDivY;
817 								}
818 							}
819 
820 							// calculate crop range and rect
821 							basegfx::B2DRange aCropRange;
822 							aCropRange.expand(aCurrentRange.getMinimum() - basegfx::B2DPoint(rAttr.GetLeftCrop() * fFactorX, rAttr.GetTopCrop() * fFactorY));
823 							aCropRange.expand(aCurrentRange.getMaximum() + basegfx::B2DPoint(rAttr.GetRightCrop() * fFactorX, rAttr.GetBottomCrop() * fFactorY));
824 
825 							aCropRect = Rectangle(
826 								sal_Int32(floor(aCropRange.getMinX())), sal_Int32(floor(aCropRange.getMinY())),
827 								sal_Int32(ceil(aCropRange.getMaxX())), sal_Int32(ceil(aCropRange.getMaxY())));
828 						}
829 
830 						mpPDFExtOutDevData->EndGroup(rGraphicPrimitive.getGraphicObject().GetGraphic(),
831 							rAttr.GetTransparency(),
832 							aCurrentRect,
833 							aCropRect);
834 					}
835 
836 					break;
837 				}
838 				case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D :
839 				{
840                     const primitive2d::ControlPrimitive2D& rControlPrimitive = static_cast< const primitive2d::ControlPrimitive2D& >(rCandidate);
841         			const uno::Reference< awt::XControl >& rXControl(rControlPrimitive.getXControl());
842 					bool bIsPrintableControl(false);
843 
844                     // find out if control is printable
845 					if(rXControl.is())
846 					{
847 						try
848 						{
849 							uno::Reference< beans::XPropertySet > xModelProperties(rXControl->getModel(), uno::UNO_QUERY);
850 							uno::Reference< beans::XPropertySetInfo > xPropertyInfo(xModelProperties.is()
851 								? xModelProperties->getPropertySetInfo()
852 								: uno::Reference< beans::XPropertySetInfo >());
853 							const ::rtl::OUString sPrintablePropertyName(RTL_CONSTASCII_USTRINGPARAM("Printable"));
854 
855 							if(xPropertyInfo.is() && xPropertyInfo->hasPropertyByName(sPrintablePropertyName))
856 							{
857 								OSL_VERIFY(xModelProperties->getPropertyValue(sPrintablePropertyName) >>= bIsPrintableControl);
858 							}
859 						}
860 						catch(const uno::Exception&)
861 						{
862 				            OSL_ENSURE(false, "VclMetafileProcessor2D: No access to printable flag of Control, caught an exception!");
863 						}
864 					}
865 
866                     // PDF export and printing only for printable controls
867                     if(bIsPrintableControl)
868                     {
869                         const bool bPDFExport(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportFormFields());
870                         bool bDoProcessRecursively(true);
871 
872                         if(bPDFExport)
873 					    {
874 						    // PDF export. Emulate data handling from UnoControlPDFExportContact
875                             // I have now moved describePDFControl to toolkit, thus i can implement the PDF
876                             // form control support now as follows
877                             ::std::auto_ptr< ::vcl::PDFWriter::AnyWidget > pPDFControl;
878                             ::toolkitform::describePDFControl( rXControl, pPDFControl, *mpPDFExtOutDevData );
879 
880                             if(pPDFControl.get())
881                             {
882                                 // still need to fill in the location (is a class Rectangle)
883 			                    const basegfx::B2DRange aRangeLogic(rControlPrimitive.getB2DRange(getViewInformation2D()));
884 	                            const Rectangle aRectLogic(
885 		                            (sal_Int32)floor(aRangeLogic.getMinX()), (sal_Int32)floor(aRangeLogic.getMinY()),
886 		                            (sal_Int32)ceil(aRangeLogic.getMaxX()), (sal_Int32)ceil(aRangeLogic.getMaxY()));
887                                 pPDFControl->Location = aRectLogic;
888 
889                                 Size aFontSize(pPDFControl->TextFont.GetSize());
890                                 aFontSize = mpOutputDevice->LogicToLogic(aFontSize, MapMode(MAP_POINT), mpOutputDevice->GetMapMode());
891                                 pPDFControl->TextFont.SetSize(aFontSize);
892 
893                                 mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form);
894                                 mpPDFExtOutDevData->CreateControl(*pPDFControl.get());
895                                 mpPDFExtOutDevData->EndStructureElement();
896 
897                                 // no normal paint needed (see original UnoControlPDFExportContact::do_PaintObject);
898                                 // do not process recursively
899                                 bDoProcessRecursively = false;
900                             }
901                             else
902                             {
903                                 // PDF export did not work, try simple output.
904                                 // Fallback to printer output by not setting bDoProcessRecursively
905                                 // to false.
906                             }
907 					    }
908 
909                         // #i93169# used flag the wrong way; true means that nothing was done yet
910                         if(bDoProcessRecursively)
911 					    {
912     					    // printer output
913 					        try
914 					        {
915                                 // remember old graphics and create new
916 					            uno::Reference< awt::XView > xControlView(rXControl, uno::UNO_QUERY_THROW);
917                                 const uno::Reference< awt::XGraphics > xOriginalGraphics(xControlView->getGraphics());
918 					            const uno::Reference< awt::XGraphics > xNewGraphics(mpOutputDevice->CreateUnoGraphics());
919 
920                                 if(xNewGraphics.is())
921                                 {
922 				                    // link graphics and view
923 				                    xControlView->setGraphics(xNewGraphics);
924 
925                                     // get position
926                                     const basegfx::B2DHomMatrix aObjectToDiscrete(getViewInformation2D().getObjectToViewTransformation() * rControlPrimitive.getTransform());
927                                     const basegfx::B2DPoint aTopLeftDiscrete(aObjectToDiscrete * basegfx::B2DPoint(0.0, 0.0));
928 
929                                     // draw it
930                                     xControlView->draw(basegfx::fround(aTopLeftDiscrete.getX()), basegfx::fround(aTopLeftDiscrete.getY()));
931                                     bDoProcessRecursively = false;
932 
933                                     // restore original graphics
934 				                    xControlView->setGraphics(xOriginalGraphics);
935                                 }
936 					        }
937 					        catch( const uno::Exception& )
938 					        {
939 			                    OSL_ENSURE(false, "VclMetafileProcessor2D: Printing of Control failed, caught an exception!");
940 					        }
941 					    }
942 
943 					    // process recursively if not done yet to export as decomposition (bitmap)
944                         if(bDoProcessRecursively)
945                         {
946     					    process(rControlPrimitive.get2DDecomposition(getViewInformation2D()));
947                         }
948                     }
949 
950 					break;
951 				}
952                 case PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D :
953                 {
954                     // support for FIELD_SEQ_BEGIN, FIELD_SEQ_END and URL. It wraps text primitives (but is not limited to)
955                     // thus do the MetafileAction embedding stuff but just handle recursively.
956 					const primitive2d::TextHierarchyFieldPrimitive2D& rFieldPrimitive = static_cast< const primitive2d::TextHierarchyFieldPrimitive2D& >(rCandidate);
957                     static const ByteString aCommentStringCommon("FIELD_SEQ_BEGIN");
958 					static const ByteString aCommentStringPage("FIELD_SEQ_BEGIN;PageField");
959 					static const ByteString aCommentStringEnd("FIELD_SEQ_END");
960 
961 					switch(rFieldPrimitive.getType())
962 					{
963 						default : // case drawinglayer::primitive2d::FIELD_TYPE_COMMON :
964 						{
965 		                    mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon));
966 							break;
967 						}
968 						case drawinglayer::primitive2d::FIELD_TYPE_PAGE :
969 						{
970 		                    mpMetaFile->AddAction(new MetaCommentAction(aCommentStringPage));
971 							break;
972 						}
973 						case drawinglayer::primitive2d::FIELD_TYPE_URL :
974 						{
975 							const rtl::OUString& rURL = rFieldPrimitive.getString();
976                             const String aOldString(rURL);
977 		                    mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon, 0, reinterpret_cast< const sal_uInt8* >(aOldString.GetBuffer()), 2 * aOldString.Len()));
978 							break;
979 						}
980 					}
981 
982 					// process recursively
983 					const primitive2d::Primitive2DSequence rContent = rFieldPrimitive.get2DDecomposition(getViewInformation2D());
984 					process(rContent);
985 
986 					// for the end comment the type is not relevant yet, they are all the same. Just add.
987                     mpMetaFile->AddAction(new MetaCommentAction(aCommentStringEnd));
988 
989 					if(mpPDFExtOutDevData && drawinglayer::primitive2d::FIELD_TYPE_URL == rFieldPrimitive.getType())
990 					{
991 						// emulate data handling from ImpEditEngine::Paint
992 			            const basegfx::B2DRange aViewRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent, getViewInformation2D()));
993 	                    const Rectangle aRectLogic(
994 		                    (sal_Int32)floor(aViewRange.getMinX()), (sal_Int32)floor(aViewRange.getMinY()),
995 		                    (sal_Int32)ceil(aViewRange.getMaxX()), (sal_Int32)ceil(aViewRange.getMaxY()));
996 						vcl::PDFExtOutDevBookmarkEntry aBookmark;
997 						aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic);
998 						aBookmark.aBookmark = rFieldPrimitive.getString();
999 						std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = mpPDFExtOutDevData->GetBookmarks();
1000 						rBookmarks.push_back( aBookmark );
1001 					}
1002 
1003                     break;
1004                 }
1005                 case PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D :
1006                 {
1007                     const primitive2d::TextHierarchyLinePrimitive2D& rLinePrimitive = static_cast< const primitive2d::TextHierarchyLinePrimitive2D& >(rCandidate);
1008                     static const ByteString aCommentString("XTEXT_EOL");
1009 
1010                     // process recursively and add MetaFile comment
1011 					process(rLinePrimitive.get2DDecomposition(getViewInformation2D()));
1012                     mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
1013 
1014                     break;
1015                 }
1016                 case PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D :
1017                 {
1018                     // in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The
1019                     // "XTEXT_EOC" is used, use here, too.
1020 					const primitive2d::TextHierarchyBulletPrimitive2D& rBulletPrimitive = static_cast< const primitive2d::TextHierarchyBulletPrimitive2D& >(rCandidate);
1021                     static const ByteString aCommentString("XTEXT_EOC");
1022 
1023                     // process recursively and add MetaFile comment
1024 					process(rBulletPrimitive.get2DDecomposition(getViewInformation2D()));
1025                     mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
1026 
1027                     break;
1028                 }
1029                 case PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D :
1030                 {
1031                     const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive = static_cast< const primitive2d::TextHierarchyParagraphPrimitive2D& >(rCandidate);
1032                     static const ByteString aCommentString("XTEXT_EOP");
1033 
1034 					if(mpPDFExtOutDevData)
1035 					{
1036 						// emulate data handling from ImpEditEngine::Paint
1037 						mpPDFExtOutDevData->BeginStructureElement( vcl::PDFWriter::Paragraph );
1038 					}
1039 
1040                     // process recursively and add MetaFile comment
1041 					process(rParagraphPrimitive.get2DDecomposition(getViewInformation2D()));
1042                     mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
1043 
1044 					if(mpPDFExtOutDevData)
1045 					{
1046 						// emulate data handling from ImpEditEngine::Paint
1047 						mpPDFExtOutDevData->EndStructureElement();
1048 					}
1049 
1050 					break;
1051                 }
1052                 case PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D :
1053                 {
1054                     const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive = static_cast< const primitive2d::TextHierarchyBlockPrimitive2D& >(rCandidate);
1055                     static const ByteString aCommentStringA("XTEXT_PAINTSHAPE_BEGIN");
1056                     static const ByteString aCommentStringB("XTEXT_PAINTSHAPE_END");
1057 
1058                     // add MetaFile comment, process recursively and add MetaFile comment
1059                     mpMetaFile->AddAction(new MetaCommentAction(aCommentStringA));
1060 					process(rBlockPrimitive.get2DDecomposition(getViewInformation2D()));
1061                     mpMetaFile->AddAction(new MetaCommentAction(aCommentStringB));
1062 
1063                     break;
1064                 }
1065 				case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D :
1066 				case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D :
1067 				{
1068                     // for supporting TEXT_ MetaFile actions there is more to do here; get the candidate
1069 					const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate = static_cast< const primitive2d::TextSimplePortionPrimitive2D& >(rCandidate);
1070 					// const primitive2d::TextDecoratedPortionPrimitive2D* pTextDecoratedCandidate = dynamic_cast< const primitive2d::TextDecoratedPortionPrimitive2D* >(&rCandidate);
1071 
1072 					// Adapt evtl. used special DrawMode
1073 					const sal_uInt32 nOriginalDrawMode(mpOutputDevice->GetDrawMode());
1074 					adaptTextToFillDrawMode();
1075 
1076 					// directdraw of text simple portion; use default processing
1077 					RenderTextSimpleOrDecoratedPortionPrimitive2D(rTextCandidate);
1078 
1079 					// restore DrawMode
1080 					mpOutputDevice->SetDrawMode(nOriginalDrawMode);
1081 
1082 					// #i101169# if(pTextDecoratedCandidate)
1083                     {
1084                         // support for TEXT_ MetaFile actions only for decorated texts
1085                         if(!mxBreakIterator.is())
1086                         {
1087                             uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xMSF(::comphelper::getProcessServiceFactory());
1088                             mxBreakIterator.set(xMSF->createInstance(rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")), uno::UNO_QUERY);
1089                         }
1090 
1091                         if(mxBreakIterator.is())
1092                         {
1093                             const rtl::OUString& rTxt = rTextCandidate.getText();
1094                             const sal_Int32 nTextLength(rTextCandidate.getTextLength()); // rTxt.getLength());
1095 
1096                             if(nTextLength)
1097                             {
1098                                 const ::com::sun::star::lang::Locale& rLocale = rTextCandidate.getLocale();
1099                                 const sal_Int32 nTextPosition(rTextCandidate.getTextPosition());
1100 
1101                                 sal_Int32 nDone;
1102                                 sal_Int32 nNextCellBreak(mxBreakIterator->nextCharacters(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 0, nDone));
1103                                 ::com::sun::star::i18n::Boundary nNextWordBoundary(mxBreakIterator->getWordBoundary(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True));
1104                                 sal_Int32 nNextSentenceBreak(mxBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale));
1105                                 static const ByteString aCommentStringA("XTEXT_EOC");
1106                                 static const ByteString aCommentStringB("XTEXT_EOW");
1107                                 static const ByteString aCommentStringC("XTEXT_EOS");
1108 
1109                                 for(sal_Int32 i(nTextPosition); i < nTextPosition + nTextLength; i++)
1110                                 {
1111                                     // create the entries for the respective break positions
1112                                     if(i == nNextCellBreak)
1113                                     {
1114                                         mpMetaFile->AddAction(new MetaCommentAction(aCommentStringA, i - nTextPosition));
1115                                         nNextCellBreak = mxBreakIterator->nextCharacters(rTxt, i, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
1116                                     }
1117                                     if(i == nNextWordBoundary.endPos)
1118                                     {
1119                                         mpMetaFile->AddAction(new MetaCommentAction(aCommentStringB, i - nTextPosition));
1120                                         nNextWordBoundary = mxBreakIterator->getWordBoundary(rTxt, i + 1, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True);
1121                                     }
1122                                     if(i == nNextSentenceBreak)
1123                                     {
1124                                         mpMetaFile->AddAction(new MetaCommentAction(aCommentStringC, i - nTextPosition));
1125                                         nNextSentenceBreak = mxBreakIterator->endOfSentence(rTxt, i + 1, rLocale);
1126                                     }
1127                                 }
1128                             }
1129                         }
1130                     }
1131 
1132                     break;
1133 				}
1134 				case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D :
1135 				{
1136 					const primitive2d::PolygonHairlinePrimitive2D& rHairlinePrimitive = static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate);
1137         			const basegfx::B2DPolygon& rBasePolygon = rHairlinePrimitive.getB2DPolygon();
1138 
1139                     if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
1140                     {
1141                         // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1142                         // per polygon. If there are more, split the polygon in half and call recursively
1143                         basegfx::B2DPolygon aLeft, aRight;
1144                         splitLinePolygon(rBasePolygon, aLeft, aRight);
1145                         const primitive2d::PolygonHairlinePrimitive2D aPLeft(aLeft, rHairlinePrimitive.getBColor());
1146                         const primitive2d::PolygonHairlinePrimitive2D aPRight(aRight, rHairlinePrimitive.getBColor());
1147 
1148                         processBasePrimitive2D(aPLeft);
1149                         processBasePrimitive2D(aPRight);
1150                     }
1151                     else
1152                     {
1153     					// direct draw of hairline; use default processing
1154     					// support SvtGraphicStroke MetaCommentAction
1155 					    const basegfx::BColor aLineColor(maBColorModifierStack.getModifiedColor(rHairlinePrimitive.getBColor()));
1156                         SvtGraphicStroke* pSvtGraphicStroke = 0;
1157 
1158                         // #121267# Not needed, does not give better quality compared with
1159                         // the META_POLYPOLYGON_ACTION written by RenderPolygonHairlinePrimitive2D
1160                         // below
1161                         bool bSupportSvtGraphicStroke(false);
1162 
1163                         if(bSupportSvtGraphicStroke)
1164                         {
1165                             pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
1166                                 rHairlinePrimitive.getB2DPolygon(),
1167                                 &aLineColor,
1168                                 0, 0, 0, 0);
1169 
1170     					    impStartSvtGraphicStroke(pSvtGraphicStroke);
1171                         }
1172 
1173 					    RenderPolygonHairlinePrimitive2D(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate), false);
1174 
1175                         if(bSupportSvtGraphicStroke)
1176                         {
1177                             impEndSvtGraphicStroke(pSvtGraphicStroke);
1178                         }
1179                     }
1180 					break;
1181 				}
1182 				case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D :
1183 				{
1184 					const primitive2d::PolygonStrokePrimitive2D& rStrokePrimitive = static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate);
1185         			const basegfx::B2DPolygon& rBasePolygon = rStrokePrimitive.getB2DPolygon();
1186 
1187                     if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
1188                     {
1189                         // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1190                         // per polygon. If there are more, split the polygon in half and call recursively
1191                         basegfx::B2DPolygon aLeft, aRight;
1192                         splitLinePolygon(rBasePolygon, aLeft, aRight);
1193                         const primitive2d::PolygonStrokePrimitive2D aPLeft(
1194                             aLeft, rStrokePrimitive.getLineAttribute(), rStrokePrimitive.getStrokeAttribute());
1195                         const primitive2d::PolygonStrokePrimitive2D aPRight(
1196                             aRight, rStrokePrimitive.getLineAttribute(), rStrokePrimitive.getStrokeAttribute());
1197 
1198                         processBasePrimitive2D(aPLeft);
1199                         processBasePrimitive2D(aPRight);
1200                     }
1201                     else
1202                     {
1203     					// support SvtGraphicStroke MetaCommentAction
1204 					    SvtGraphicStroke* pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
1205                             rBasePolygon, 0,
1206                             &rStrokePrimitive.getLineAttribute(),
1207                             &rStrokePrimitive.getStrokeAttribute(),
1208                             0, 0);
1209 
1210 					    impStartSvtGraphicStroke(pSvtGraphicStroke);
1211 					    const attribute::LineAttribute& rLine = rStrokePrimitive.getLineAttribute();
1212 
1213 					    // create MetaPolyLineActions, but without LINE_DASH
1214 					    if(basegfx::fTools::more(rLine.getWidth(), 0.0))
1215 					    {
1216 						    const attribute::StrokeAttribute& rStroke = rStrokePrimitive.getStrokeAttribute();
1217 						    basegfx::B2DPolyPolygon aHairLinePolyPolygon;
1218 
1219 						    if(0.0 == rStroke.getFullDotDashLen())
1220 						    {
1221 							    aHairLinePolyPolygon.append(rBasePolygon);
1222 						    }
1223 						    else
1224 						    {
1225 							    basegfx::tools::applyLineDashing(
1226 								    rBasePolygon, rStroke.getDotDashArray(),
1227 								    &aHairLinePolyPolygon, 0, rStroke.getFullDotDashLen());
1228 						    }
1229 
1230 						    const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLine.getColor()));
1231 						    mpOutputDevice->SetLineColor(Color(aHairlineColor));
1232 						    mpOutputDevice->SetFillColor();
1233 						    aHairLinePolyPolygon.transform(maCurrentTransformation);
1234 
1235 							// #i113922# LineWidth needs to be transformed, too
1236 							const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation * basegfx::B2DVector(rLine.getWidth(), 0.0));
1237 							const double fDiscreteLineWidth(aDiscreteUnit.getLength());
1238 
1239 							LineInfo aLineInfo(LINE_SOLID, basegfx::fround(fDiscreteLineWidth));
1240 						    aLineInfo.SetLineJoin(rLine.getLineJoin());
1241                             aLineInfo.SetLineCap(rLine.getLineCap());
1242 
1243 						    for(sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++)
1244 						    {
1245 							    const basegfx::B2DPolygon aCandidate(aHairLinePolyPolygon.getB2DPolygon(a));
1246 
1247 							    if(aCandidate.count() > 1)
1248 							    {
1249 								    const Polygon aToolsPolygon(aCandidate);
1250 
1251                 				    mpMetaFile->AddAction(new MetaPolyLineAction(aToolsPolygon, aLineInfo));
1252 							    }
1253 						    }
1254 					    }
1255 					    else
1256 					    {
1257 						    process(rCandidate.get2DDecomposition(getViewInformation2D()));
1258 					    }
1259 
1260 					    impEndSvtGraphicStroke(pSvtGraphicStroke);
1261                     }
1262 
1263 					break;
1264 				}
1265 				case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D :
1266 				{
1267 					const primitive2d::PolygonStrokeArrowPrimitive2D& rStrokeArrowPrimitive = static_cast< const primitive2d::PolygonStrokeArrowPrimitive2D& >(rCandidate);
1268         			const basegfx::B2DPolygon& rBasePolygon = rStrokeArrowPrimitive.getB2DPolygon();
1269 
1270                     if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
1271                     {
1272                         // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1273                         // per polygon. If there are more, split the polygon in half and call recursively
1274                         basegfx::B2DPolygon aLeft, aRight;
1275                         splitLinePolygon(rBasePolygon, aLeft, aRight);
1276                         const attribute::LineStartEndAttribute aEmpty;
1277                         const primitive2d::PolygonStrokeArrowPrimitive2D aPLeft(
1278                             aLeft,
1279                             rStrokeArrowPrimitive.getLineAttribute(),
1280                             rStrokeArrowPrimitive.getStrokeAttribute(),
1281                             rStrokeArrowPrimitive.getStart(),
1282                             aEmpty);
1283                         const primitive2d::PolygonStrokeArrowPrimitive2D aPRight(
1284                             aRight,
1285                             rStrokeArrowPrimitive.getLineAttribute(),
1286                             rStrokeArrowPrimitive.getStrokeAttribute(),
1287                             aEmpty,
1288                             rStrokeArrowPrimitive.getEnd());
1289 
1290                         processBasePrimitive2D(aPLeft);
1291                         processBasePrimitive2D(aPRight);
1292                     }
1293                     else
1294                     {
1295     					// support SvtGraphicStroke MetaCommentAction
1296 	    				SvtGraphicStroke* pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
1297                             rBasePolygon, 0,
1298                             &rStrokeArrowPrimitive.getLineAttribute(),
1299                             &rStrokeArrowPrimitive.getStrokeAttribute(),
1300                             &rStrokeArrowPrimitive.getStart(),
1301                             &rStrokeArrowPrimitive.getEnd());
1302 
1303                         // write LineGeometry start marker
1304 			    		impStartSvtGraphicStroke(pSvtGraphicStroke);
1305 
1306                         // #116162# When B&W is set as DrawMode, DRAWMODE_WHITEFILL is used
1307                         // to let all fills be just white; for lines DRAWMODE_BLACKLINE is used
1308                         // so all line geometry is supposed to get black. Since in the in-between
1309                         // stages of line geometry drawing filled polygons are used (e.g. line
1310                         // start/ends) it is necessary to change these drawmodes to preserve
1311                         // that lines shall be black; thus change DRAWMODE_WHITEFILL to
1312                         // DRAWMODE_BLACKFILL during line geometry processing to have line geometry
1313                         // parts filled black.
1314                         const sal_uLong nOldDrawMode(mpOutputDevice->GetDrawMode());
1315                         const bool bDrawmodeChange(nOldDrawMode & DRAWMODE_WHITEFILL && mnSvtGraphicStrokeCount);
1316 
1317                         if(bDrawmodeChange)
1318                         {
1319                             mpOutputDevice->SetDrawMode((nOldDrawMode & ~DRAWMODE_WHITEFILL) | DRAWMODE_BLACKFILL);
1320                         }
1321 
1322                         // process sub-line geometry (evtl. filled PolyPolygons)
1323                         process(rCandidate.get2DDecomposition(getViewInformation2D()));
1324 
1325                         if(bDrawmodeChange)
1326                         {
1327                             mpOutputDevice->SetDrawMode(nOldDrawMode);
1328                         }
1329 
1330                         // write LineGeometry end marker
1331                         impEndSvtGraphicStroke(pSvtGraphicStroke);
1332                     }
1333 
1334                     break;
1335 				}
1336 				case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D :
1337 				{
1338                     // direct draw of transformed BitmapEx primitive; use default processing
1339 					RenderBitmapPrimitive2D(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate));
1340 					break;
1341 				}
1342 				case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D :
1343 				{
1344 					// need to handle PolyPolygonGraphicPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
1345 					const primitive2d::PolyPolygonGraphicPrimitive2D& rBitmapCandidate = static_cast< const primitive2d::PolyPolygonGraphicPrimitive2D& >(rCandidate);
1346 					basegfx::B2DPolyPolygon aLocalPolyPolygon(rBitmapCandidate.getB2DPolyPolygon());
1347 
1348                     if(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1349                     {
1350                         // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1351                         // per polygon. If there are more use the splitted polygon and call recursively
1352                         const primitive2d::PolyPolygonGraphicPrimitive2D aSplitted(
1353                             aLocalPolyPolygon,
1354                             rBitmapCandidate.getFillGraphic());
1355 
1356                         processBasePrimitive2D(aSplitted);
1357                     }
1358                     else
1359                     {
1360                         SvtGraphicFill* pSvtGraphicFill = 0;
1361 
1362 					    if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1363 					    {
1364                             // #121194# Changed implementation and checked usages fo convert to metafile,
1365                             // presentation start (uses SvtGraphicFill) and printing.
1366 
1367                             // calculate transformation. Get real object size, all values in FillGraphicAttribute
1368 						    // are relative to the unified object
1369 						    aLocalPolyPolygon.transform(maCurrentTransformation);
1370                             const basegfx::B2DVector aOutlineSize(aLocalPolyPolygon.getB2DRange().getRange());
1371 
1372 						    // the scaling needs scale from pixel to logic coordinate system
1373 						    const attribute::FillGraphicAttribute& rFillGraphicAttribute = rBitmapCandidate.getFillGraphic();
1374 						    const Size aBmpSizePixel(rFillGraphicAttribute.getGraphic().GetSizePixel());
1375 
1376 						    // setup transformation like in impgrfll. Multiply with aOutlineSize
1377                             // to get from unit coordinates in rFillGraphicAttribute.getGraphicRange()
1378                             // to object coordinates with object's top left being at (0,0). Divide
1379                             // by pixel size so that scale from pixel to logic will work in SvtGraphicFill.
1380                             const basegfx::B2DVector aTransformScale(
1381                                 rFillGraphicAttribute.getGraphicRange().getRange() /
1382                                 basegfx::B2DVector(
1383                                     std::max(1.0, double(aBmpSizePixel.Width())),
1384                                     std::max(1.0, double(aBmpSizePixel.Height()))) *
1385                                 aOutlineSize);
1386                             const basegfx::B2DPoint aTransformPosition(
1387                                 rFillGraphicAttribute.getGraphicRange().getMinimum() * aOutlineSize);
1388 
1389 						    // setup transformation like in impgrfll
1390 						    SvtGraphicFill::Transform aTransform;
1391 
1392 						    // scale values are divided by bitmap pixel sizes
1393 						    aTransform.matrix[0] = aTransformScale.getX();
1394 						    aTransform.matrix[4] = aTransformScale.getY();
1395 
1396                             // translates are absolute
1397                             aTransform.matrix[2] = aTransformPosition.getX();
1398 						    aTransform.matrix[5] = aTransformPosition.getY();
1399 
1400                             pSvtGraphicFill = new SvtGraphicFill(
1401 							    PolyPolygon(aLocalPolyPolygon),
1402 							    Color(),
1403 							    0.0,
1404 							    SvtGraphicFill::fillEvenOdd,
1405 							    SvtGraphicFill::fillTexture,
1406 							    aTransform,
1407 							    rFillGraphicAttribute.getTiling(),
1408 							    SvtGraphicFill::hatchSingle,
1409 							    Color(),
1410 							    SvtGraphicFill::gradientLinear,
1411 							    Color(),
1412 							    Color(),
1413 							    0,
1414 							    rFillGraphicAttribute.getGraphic());
1415 					    }
1416 
1417 					    // Do use decomposition; encapsulate with SvtGraphicFill
1418 					    impStartSvtGraphicFill(pSvtGraphicFill);
1419 					    process(rCandidate.get2DDecomposition(getViewInformation2D()));
1420 					    impEndSvtGraphicFill(pSvtGraphicFill);
1421                     }
1422 
1423 					break;
1424 				}
1425 				case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D :
1426 				{
1427 					// need to handle PolyPolygonHatchPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
1428 					const primitive2d::PolyPolygonHatchPrimitive2D& rHatchCandidate = static_cast< const primitive2d::PolyPolygonHatchPrimitive2D& >(rCandidate);
1429 				    const attribute::FillHatchAttribute& rFillHatchAttribute = rHatchCandidate.getFillHatch();
1430 					basegfx::B2DPolyPolygon aLocalPolyPolygon(rHatchCandidate.getB2DPolyPolygon());
1431 
1432                     // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1433                     // per polygon. Split polygon until there are less than that
1434                     while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1435                         ;
1436 
1437 					if(rFillHatchAttribute.isFillBackground())
1438 					{
1439 						// with fixing #i111954# (see below) the possible background
1440 						// fill of a hatched object was lost.Generate a background fill
1441 						// primitive and render it
1442 					    const primitive2d::Primitive2DReference xBackground(
1443 							new primitive2d::PolyPolygonColorPrimitive2D(
1444 								aLocalPolyPolygon,
1445 								rHatchCandidate.getBackgroundColor()));
1446 
1447 						process(primitive2d::Primitive2DSequence(&xBackground, 1));
1448 					}
1449 
1450                     SvtGraphicFill* pSvtGraphicFill = 0;
1451 				    aLocalPolyPolygon.transform(maCurrentTransformation);
1452 
1453 				    if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1454 				    {
1455 					    // re-create a VCL hatch as base data
1456 					    SvtGraphicFill::HatchType eHatch(SvtGraphicFill::hatchSingle);
1457 
1458 					    switch(rFillHatchAttribute.getStyle())
1459 					    {
1460 						    default: // attribute::HATCHSTYLE_SINGLE :
1461 						    {
1462 							    eHatch = SvtGraphicFill::hatchSingle;
1463 							    break;
1464 						    }
1465 						    case attribute::HATCHSTYLE_DOUBLE :
1466 						    {
1467 							    eHatch = SvtGraphicFill::hatchDouble;
1468 							    break;
1469 						    }
1470 						    case attribute::HATCHSTYLE_TRIPLE :
1471 						    {
1472 							    eHatch = SvtGraphicFill::hatchTriple;
1473 							    break;
1474 						    }
1475 					    }
1476 
1477 					    SvtGraphicFill::Transform aTransform;
1478 
1479 					    // scale
1480 					    aTransform.matrix[0] *= rFillHatchAttribute.getDistance();
1481 					    aTransform.matrix[4] *= rFillHatchAttribute.getDistance();
1482 
1483 					    // rotate (was never correct in impgrfll anyways, use correct angle now)
1484 					    aTransform.matrix[0] *= cos(rFillHatchAttribute.getAngle());
1485 					    aTransform.matrix[1] *= -sin(rFillHatchAttribute.getAngle());
1486 					    aTransform.matrix[3] *= sin(rFillHatchAttribute.getAngle());
1487 					    aTransform.matrix[4] *= cos(rFillHatchAttribute.getAngle());
1488 
1489 					    pSvtGraphicFill = new SvtGraphicFill(
1490 						    PolyPolygon(aLocalPolyPolygon),
1491 						    Color(),
1492 						    0.0,
1493 						    SvtGraphicFill::fillEvenOdd,
1494 						    SvtGraphicFill::fillHatch,
1495 						    aTransform,
1496 						    false,
1497 						    eHatch,
1498 						    Color(rFillHatchAttribute.getColor()),
1499 						    SvtGraphicFill::gradientLinear,
1500 						    Color(),
1501 						    Color(),
1502 						    0,
1503 						    Graphic());
1504 				    }
1505 
1506 				    // Do use decomposition; encapsulate with SvtGraphicFill
1507 				    impStartSvtGraphicFill(pSvtGraphicFill);
1508 
1509                     // #i111954# do NOT use decomposition, but use direct VCL-command
1510 			        // process(rCandidate.get2DDecomposition(getViewInformation2D()));
1511 			        const PolyPolygon aToolsPolyPolygon(aLocalPolyPolygon);
1512                     const HatchStyle aHatchStyle(
1513                         attribute::HATCHSTYLE_SINGLE == rFillHatchAttribute.getStyle() ? HATCH_SINGLE :
1514                         attribute::HATCHSTYLE_DOUBLE == rFillHatchAttribute.getStyle() ? HATCH_DOUBLE :
1515                         HATCH_TRIPLE);
1516 
1517                     mpOutputDevice->DrawHatch(aToolsPolyPolygon,
1518                         Hatch(aHatchStyle,
1519                             Color(rFillHatchAttribute.getColor()),
1520                             basegfx::fround(rFillHatchAttribute.getDistance()),
1521                             basegfx::fround(rFillHatchAttribute.getAngle() / F_PI1800)));
1522 
1523                     impEndSvtGraphicFill(pSvtGraphicFill);
1524 
1525 					break;
1526 				}
1527 				case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D :
1528                 {
1529                     basegfx::B2DVector aScale, aTranslate;
1530                     double fRotate, fShearX;
1531 
1532                     maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
1533 
1534                     if(!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX))
1535                     {
1536                         // #121185# When rotation or shear is used, a VCL Gradient cannot be used directly.
1537                         // This is because VCL Gradient mechanism does *not* support to rotate the gradient
1538                         // with objects and this case is not expressable in a Metafile (and cannot be added
1539                         // since the FileFormats used, e.g. *.wmf, do not support it either).
1540                         // Such cases happen when a graphic object uses a Metafile as graphic information or
1541                         // a fill style definition uses a Metafile. In this cases the graphic content is
1542                         // rotated with the graphic or filled object; this is not supported by the target
1543                         // format of this conversion renderer - Metafiles.
1544                         // To solve this, not a Gradient is written, but the decomposition of this object
1545                         // is written to the Metafile. This is the PolyPolygons building the gradient fill.
1546                         // These will need more space and time, but the result will be as if the Gradient
1547                         // was rotated with the object.
1548                         // This mechanism is used by all exporters still not using Primtives (e.g. Print,
1549                         // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile
1550                         // transfers. One more reason to *change* these to primitives.
1551                         // BTW: One more example how useful the principles of primitives are; the decomposition
1552                         // is by definition a simpler, maybe more expensive representation of the same content.
1553                         process(rCandidate.get2DDecomposition(getViewInformation2D()));
1554                     }
1555                     else
1556                     {
1557 					    const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate = static_cast< const primitive2d::PolyPolygonGradientPrimitive2D& >(rCandidate);
1558 			            basegfx::B2DPolyPolygon aLocalPolyPolygon(rGradientCandidate.getB2DPolyPolygon());
1559 
1560                         // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1561                         // per polygon. Split polygon until there are less than that
1562                         while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1563                             ;
1564 
1565                         // for support of MetaCommentActions of the form XGRAD_SEQ_BEGIN, XGRAD_SEQ_END
1566                         // it is safest to use the VCL OutputDevice::DrawGradient method which creates those.
1567                         // re-create a VCL-gradient from FillGradientPrimitive2D and the needed tools PolyPolygon
1568 				        Gradient aVCLGradient;
1569                         impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rGradientCandidate.getFillGradient(), false);
1570 		                aLocalPolyPolygon.transform(maCurrentTransformation);
1571 
1572 				        // #i82145# ATM VCL printing of gradients using curved shapes does not work,
1573 				        // i submitted the bug with the given ID to THB. When that task is fixed it is
1574 				        // necessary to again remove this subdivision since it decreases possible
1575 				        // printing quality (not even resolution-dependent for now). THB will tell
1576 				        // me when that task is fixed in the master
1577 				        const PolyPolygon aToolsPolyPolygon(basegfx::tools::adaptiveSubdivideByAngle(aLocalPolyPolygon));
1578 
1579 				        // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1580 				        SvtGraphicFill* pSvtGraphicFill = 0;
1581 
1582 				        if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1583 				        {
1584 					        // setup gradient stuff like in like in impgrfll
1585 					        SvtGraphicFill::GradientType eGrad(SvtGraphicFill::gradientLinear);
1586 
1587 					        switch(aVCLGradient.GetStyle())
1588 					        {
1589 						        default : // GRADIENT_LINEAR:
1590 						        case GRADIENT_AXIAL:
1591 							        eGrad = SvtGraphicFill::gradientLinear;
1592 							        break;
1593 						        case GRADIENT_RADIAL:
1594 						        case GRADIENT_ELLIPTICAL:
1595 							        eGrad = SvtGraphicFill::gradientRadial;
1596 							        break;
1597 						        case GRADIENT_SQUARE:
1598 						        case GRADIENT_RECT:
1599 							        eGrad = SvtGraphicFill::gradientRectangular;
1600 							        break;
1601 					        }
1602 
1603 					        pSvtGraphicFill = new SvtGraphicFill(
1604 						        aToolsPolyPolygon,
1605 						        Color(),
1606 						        0.0,
1607 						        SvtGraphicFill::fillEvenOdd,
1608 						        SvtGraphicFill::fillGradient,
1609 						        SvtGraphicFill::Transform(),
1610 						        false,
1611 						        SvtGraphicFill::hatchSingle,
1612 						        Color(),
1613 						        eGrad,
1614 						        aVCLGradient.GetStartColor(),
1615 						        aVCLGradient.GetEndColor(),
1616 						        aVCLGradient.GetSteps(),
1617 						        Graphic());
1618 				        }
1619 
1620 				        // call VCL directly; encapsulate with SvtGraphicFill
1621 				        impStartSvtGraphicFill(pSvtGraphicFill);
1622 		                mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient);
1623 				        impEndSvtGraphicFill(pSvtGraphicFill);
1624                     }
1625 
1626                     break;
1627                 }
1628 				case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D :
1629 				{
1630 					const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate));
1631 					basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon());
1632 
1633                     // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1634                     // per polygon. Split polygon until there are less than that
1635                     while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1636                         ;
1637 
1638 				    const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor()));
1639 				    aLocalPolyPolygon.transform(maCurrentTransformation);
1640 
1641 				    // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1642 				    SvtGraphicFill* pSvtGraphicFill = 0;
1643 
1644                     // #121267# Not needed, does not give better quality compared with
1645                     // the META_POLYPOLYGON_ACTION written by the DrawPolyPolygon command
1646                     // below
1647                     bool bSupportSvtGraphicFill(false);
1648 
1649 				    if(bSupportSvtGraphicFill && !mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1650 				    {
1651 					    // setup simple color fill stuff like in impgrfll
1652 					    pSvtGraphicFill = new SvtGraphicFill(
1653 						    PolyPolygon(aLocalPolyPolygon),
1654 						    Color(aPolygonColor),
1655 						    0.0,
1656 						    SvtGraphicFill::fillEvenOdd,
1657 						    SvtGraphicFill::fillSolid,
1658 						    SvtGraphicFill::Transform(),
1659 						    false,
1660 						    SvtGraphicFill::hatchSingle,
1661 						    Color(),
1662 						    SvtGraphicFill::gradientLinear,
1663 						    Color(),
1664 						    Color(),
1665 						    0,
1666 						    Graphic());
1667 				    }
1668 
1669                     // set line and fill color
1670 				    mpOutputDevice->SetFillColor(Color(aPolygonColor));
1671 				    mpOutputDevice->SetLineColor();
1672 
1673 				    // call VCL directly; encapsulate with SvtGraphicFill
1674                     if(bSupportSvtGraphicFill)
1675                     {
1676                             impStartSvtGraphicFill(pSvtGraphicFill);
1677                     }
1678 
1679                     mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon);
1680 
1681                     if(bSupportSvtGraphicFill)
1682                     {
1683                         impEndSvtGraphicFill(pSvtGraphicFill);
1684                     }
1685 
1686 					break;
1687 				}
1688 				case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
1689 				{
1690                     // mask group. Special handling for MetaFiles.
1691 					const primitive2d::MaskPrimitive2D& rMaskCandidate = static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate);
1692 
1693                     if(rMaskCandidate.getChildren().hasElements())
1694 			        {
1695 				        basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
1696 
1697 				        if(aMask.count())
1698 				        {
1699 							// prepare new mask polygon and rescue current one
1700 					        aMask.transform(maCurrentTransformation);
1701                             const basegfx::B2DPolyPolygon aLastClipPolyPolygon(maClipPolyPolygon);
1702 
1703                             if(maClipPolyPolygon.count())
1704                             {
1705 								// there is already a clip polygon set; build clipped union of
1706 								// current mask polygon and new one
1707 								maClipPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1708                                     aMask,
1709                                     maClipPolyPolygon,
1710                                     true, // #i106516# we want the inside of aMask, not the outside
1711                                     false);
1712                             }
1713                             else
1714                             {
1715                                 // use mask directly
1716                                 maClipPolyPolygon = aMask;
1717                             }
1718 
1719                             if(maClipPolyPolygon.count())
1720                             {
1721                                 // set VCL clip region; subdivide before conversion to tools polygon. Subdivision necessary (!)
1722                                 // Removed subdivision and fixed in Region::ImplPolyPolyRegionToBandRegionFunc() in VCL where
1723                                 // the ClipRegion is built from the Polygon. A AdaptiveSubdivide on the source polygon was missing there
1724                                 mpOutputDevice->Push(PUSH_CLIPREGION);
1725                                 mpOutputDevice->SetClipRegion(Region(maClipPolyPolygon));
1726 
1727                                 // recursively paint content
1728                                 // #121267# Only need to process sub-content when clip polygon is *not* empty.
1729                                 // If it is empty, the clip is empty and there can be nothing inside.
1730                                 process(rMaskCandidate.getChildren());
1731 
1732                                 // restore VCL clip region
1733                                 mpOutputDevice->Pop();
1734                             }
1735 
1736                             // restore to rescued clip polygon
1737                             maClipPolyPolygon = aLastClipPolyPolygon;
1738 				        }
1739                         else
1740                         {
1741                             // no mask, no clipping. recursively paint content
1742 					        process(rMaskCandidate.getChildren());
1743                         }
1744 			        }
1745 
1746                     break;
1747 				}
1748 				case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D :
1749 				{
1750 					// modified color group. Force output to unified color. Use default pocessing.
1751 					RenderModifiedColorPrimitive2D(static_cast< const primitive2d::ModifiedColorPrimitive2D& >(rCandidate));
1752 					break;
1753 				}
1754                 case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D :
1755 				{
1756                     // HiddenGeometryPrimitive2D; to rebuilt the old MetaFile creation, it is necessary to
1757                     // not ignore them (as it was thought), but to add a MetaFile entry for them.
1758         		    basegfx::B2DRange aInvisibleRange(rCandidate.getB2DRange(getViewInformation2D()));
1759 
1760                     if(!aInvisibleRange.isEmpty())
1761                     {
1762 		                aInvisibleRange.transform(maCurrentTransformation);
1763                         const Rectangle aRectLogic(
1764 	                        (sal_Int32)floor(aInvisibleRange.getMinX()), (sal_Int32)floor(aInvisibleRange.getMinY()),
1765 	                        (sal_Int32)ceil(aInvisibleRange.getMaxX()), (sal_Int32)ceil(aInvisibleRange.getMaxY()));
1766 
1767                         mpOutputDevice->SetFillColor();
1768 		                mpOutputDevice->SetLineColor();
1769 		                mpOutputDevice->DrawRect(aRectLogic);
1770                     }
1771 
1772 					break;
1773 				}
1774 				case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D :
1775 				{
1776 					// for metafile: Need to examine what the pure vcl version is doing here actually
1777 					// - uses DrawTransparent with metafile for content and a gradient
1778 					// - uses DrawTransparent for single PolyPoylgons directly. Can be detected by
1779 					//   checking the content for single PolyPolygonColorPrimitive2D
1780 					const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate = static_cast< const primitive2d::UnifiedTransparencePrimitive2D& >(rCandidate);
1781 					const primitive2d::Primitive2DSequence rContent = rUniTransparenceCandidate.getChildren();
1782 
1783 					if(rContent.hasElements())
1784 					{
1785                         if(0.0 == rUniTransparenceCandidate.getTransparence())
1786                         {
1787                             // not transparent at all, use content
1788 	                        process(rUniTransparenceCandidate.getChildren());
1789                         }
1790 			            else if(rUniTransparenceCandidate.getTransparence() > 0.0 && rUniTransparenceCandidate.getTransparence() < 1.0)
1791 			            {
1792 						    // try to identify a single PolyPolygonColorPrimitive2D in the
1793 						    // content part of the transparence primitive
1794 						    const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor = 0;
1795 						    static bool bForceToMetafile(false);
1796 
1797 						    if(!bForceToMetafile && 1 == rContent.getLength())
1798 						    {
1799 							    const primitive2d::Primitive2DReference xReference(rContent[0]);
1800 							    pPoPoColor = dynamic_cast< const primitive2d::PolyPolygonColorPrimitive2D* >(xReference.get());
1801 						    }
1802 
1803 						    // PolyPolygonGradientPrimitive2D, PolyPolygonHatchPrimitive2D and
1804 						    // PolyPolygonGraphicPrimitive2D are derived from PolyPolygonColorPrimitive2D.
1805 						    // Check also for correct ID to exclude derived implementations
1806 						    if(pPoPoColor && PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D == pPoPoColor->getPrimitive2DID())
1807 						    {
1808 							    // single transparent PolyPolygon identified, use directly
1809 							    const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(pPoPoColor->getBColor()));
1810 							    basegfx::B2DPolyPolygon aLocalPolyPolygon(pPoPoColor->getB2DPolyPolygon());
1811 
1812                                 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1813                                 // per polygon. Split polygon until there are less than that
1814                                 while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1815                                     ;
1816 
1817                                 // now transform
1818                                 aLocalPolyPolygon.transform(maCurrentTransformation);
1819 
1820 							    // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1821 							    SvtGraphicFill* pSvtGraphicFill = 0;
1822 
1823                                 // #121267# Not needed, does not give better quality compared with
1824                                 // the META_POLYPOLYGON_ACTION written by the DrawPolyPolygon command
1825                                 // below
1826                                 bool bSupportSvtGraphicFill(false);
1827 
1828 							    if(bSupportSvtGraphicFill && !mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1829 							    {
1830 								    // setup simple color with transparence fill stuff like in impgrfll
1831 								    pSvtGraphicFill = new SvtGraphicFill(
1832 									    PolyPolygon(aLocalPolyPolygon),
1833 									    Color(aPolygonColor),
1834 									    rUniTransparenceCandidate.getTransparence(),
1835 									    SvtGraphicFill::fillEvenOdd,
1836 									    SvtGraphicFill::fillSolid,
1837 									    SvtGraphicFill::Transform(),
1838 									    false,
1839 									    SvtGraphicFill::hatchSingle,
1840 									    Color(),
1841 									    SvtGraphicFill::gradientLinear,
1842 									    Color(),
1843 									    Color(),
1844 									    0,
1845 									    Graphic());
1846 							    }
1847 
1848                                 // set line and fill color
1849 							    const sal_uInt16 nTransPercentVcl((sal_uInt16)basegfx::fround(rUniTransparenceCandidate.getTransparence() * 100.0));
1850 							    mpOutputDevice->SetFillColor(Color(aPolygonColor));
1851 							    mpOutputDevice->SetLineColor();
1852 
1853 							    // call VCL directly; encapsulate with SvtGraphicFill
1854                                 if(bSupportSvtGraphicFill)
1855                                 {
1856                                     impStartSvtGraphicFill(pSvtGraphicFill);
1857                                 }
1858 
1859 							    mpOutputDevice->DrawTransparent(
1860 								    PolyPolygon(aLocalPolyPolygon),
1861 								    nTransPercentVcl);
1862 
1863                                 if(bSupportSvtGraphicFill)
1864                                 {
1865                                     impEndSvtGraphicFill(pSvtGraphicFill);
1866                                 }
1867 						    }
1868 						    else
1869 						    {
1870 							    // svae old mfCurrentUnifiedTransparence and set new one
1871 							    // so that contained SvtGraphicStroke may use the current one
1872 							    const double fLastCurrentUnifiedTransparence(mfCurrentUnifiedTransparence);
1873                                 // #i105377# paint the content metafile opaque as the transparency gets
1874                                 // split of into the gradient below
1875 							    // mfCurrentUnifiedTransparence = rUniTransparenceCandidate.getTransparence();
1876 							    mfCurrentUnifiedTransparence = 0;
1877 
1878 							    // various content, create content-metafile
1879 							    GDIMetaFile aContentMetafile;
1880                                 const Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile));
1881 
1882 							    // restore mfCurrentUnifiedTransparence; it may have been used
1883 							    // while processing the sub-content in impDumpToMetaFile
1884 							    mfCurrentUnifiedTransparence = fLastCurrentUnifiedTransparence;
1885 
1886 							    // create uniform VCL gradient for uniform transparency
1887 							    Gradient aVCLGradient;
1888 							    const sal_uInt8 nTransPercentVcl((sal_uInt8)basegfx::fround(rUniTransparenceCandidate.getTransparence() * 255.0));
1889 							    const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl);
1890 
1891 							    aVCLGradient.SetStyle(GRADIENT_LINEAR);
1892 							    aVCLGradient.SetStartColor(aTransColor);
1893 							    aVCLGradient.SetEndColor(aTransColor);
1894 							    aVCLGradient.SetAngle(0);
1895 							    aVCLGradient.SetBorder(0);
1896 							    aVCLGradient.SetOfsX(0);
1897 							    aVCLGradient.SetOfsY(0);
1898 							    aVCLGradient.SetStartIntensity(100);
1899 							    aVCLGradient.SetEndIntensity(100);
1900 							    aVCLGradient.SetSteps(2);
1901 
1902 							    // render it to VCL
1903 							    mpOutputDevice->DrawTransparent(
1904 								    aContentMetafile, aPrimitiveRectangle.TopLeft(),
1905 								    aPrimitiveRectangle.GetSize(), aVCLGradient);
1906 						    }
1907 					    }
1908                     }
1909 
1910 					break;
1911 				}
1912 				case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D :
1913 				{
1914 					// for metafile: Need to examine what the pure vcl version is doing here actually
1915 					// - uses DrawTransparent with metafile for content and a gradient
1916 					// i can detect this here with checking the gradient part for a single
1917 					// FillGradientPrimitive2D and reconstruct the gradient.
1918 					// If that detection goes wrong, i have to create an transparence-blended bitmap. Eventually
1919 					// do that in stripes, else RenderTransparencePrimitive2D may just be used
1920 					const primitive2d::TransparencePrimitive2D& rTransparenceCandidate = static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate);
1921 					const primitive2d::Primitive2DSequence rContent = rTransparenceCandidate.getChildren();
1922 					const primitive2d::Primitive2DSequence rTransparence = rTransparenceCandidate.getTransparence();
1923 
1924 					if(rContent.hasElements() && rTransparence.hasElements())
1925 					{
1926 						// try to identify a single FillGradientPrimitive2D in the
1927 						// transparence part of the primitive
1928 						const primitive2d::FillGradientPrimitive2D* pFiGradient = 0;
1929 						static bool bForceToBigTransparentVDev(false);
1930 
1931 						if(!bForceToBigTransparentVDev && 1 == rTransparence.getLength())
1932 						{
1933 							const primitive2d::Primitive2DReference xReference(rTransparence[0]);
1934 							pFiGradient = dynamic_cast< const primitive2d::FillGradientPrimitive2D* >(xReference.get());
1935 						}
1936 
1937 						// Check also for correct ID to exclude derived implementations
1938 						if(pFiGradient && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D == pFiGradient->getPrimitive2DID())
1939 						{
1940 							// various content, create content-metafile
1941 							GDIMetaFile aContentMetafile;
1942                             const Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile));
1943 
1944 							// re-create a VCL-gradient from FillGradientPrimitive2D
1945 							Gradient aVCLGradient;
1946                             impConvertFillGradientAttributeToVCLGradient(aVCLGradient, pFiGradient->getFillGradient(), true);
1947 
1948 							// render it to VCL
1949 							mpOutputDevice->DrawTransparent(
1950 								aContentMetafile, aPrimitiveRectangle.TopLeft(),
1951 								aPrimitiveRectangle.GetSize(), aVCLGradient);
1952                         }
1953                         else
1954                         {
1955 	    				    // sub-transparence group. Draw to VDev first.
1956                             // this may get refined to tiling when resolution is too big here
1957 
1958                             // need to avoid switching off MapMode stuff here; maybe need another
1959                             // tooling class, cannot just do the same as with the pixel renderer.
1960                             // Need to experiment...
1961 
1962                             // Okay, basic implementation finished and tested. The DPI stuff was hard
1963                             // and not easy to find out that it's needed.
1964                             // Since this will not yet happen normally (as long as noone constructs
1965                             // transparence primitives with non-trivial transparence content) i will for now not
1966                             // refine to tiling here.
1967 
1968 				            basegfx::B2DRange aViewRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent, getViewInformation2D()));
1969 				            aViewRange.transform(maCurrentTransformation);
1970 		                    const Rectangle aRectLogic(
1971 			                    (sal_Int32)floor(aViewRange.getMinX()), (sal_Int32)floor(aViewRange.getMinY()),
1972 			                    (sal_Int32)ceil(aViewRange.getMaxX()), (sal_Int32)ceil(aViewRange.getMaxY()));
1973 		                    const Rectangle aRectPixel(mpOutputDevice->LogicToPixel(aRectLogic));
1974                             Size aSizePixel(aRectPixel.GetSize());
1975                     		const Point aEmptyPoint;
1976                             VirtualDevice aBufferDevice;
1977                             const sal_uInt32 nMaxQuadratPixels(500000);
1978                             const sal_uInt32 nViewVisibleArea(aSizePixel.getWidth() * aSizePixel.getHeight());
1979                             double fReduceFactor(1.0);
1980 
1981                             if(nViewVisibleArea > nMaxQuadratPixels)
1982                             {
1983                                 // reduce render size
1984                                 fReduceFactor = sqrt((double)nMaxQuadratPixels / (double)nViewVisibleArea);
1985                                 aSizePixel = Size(basegfx::fround((double)aSizePixel.getWidth() * fReduceFactor),
1986                                     basegfx::fround((double)aSizePixel.getHeight() * fReduceFactor));
1987                             }
1988 
1989                             if(aBufferDevice.SetOutputSizePixel(aSizePixel))
1990                             {
1991                                 // create and set MapModes for target devices
1992 		                        MapMode aNewMapMode(mpOutputDevice->GetMapMode());
1993 		                        aNewMapMode.SetOrigin(Point(-aRectLogic.Left(), -aRectLogic.Top()));
1994 		                        aBufferDevice.SetMapMode(aNewMapMode);
1995 
1996                                 // prepare view transformation for target renderers
1997                                 // ATTENTION! Need to apply another scaling because of the potential DPI differences
1998                                 // between Printer and VDev (mpOutputDevice and aBufferDevice here).
1999                                 // To get the DPI, LogicToPixel from (1,1) from MAP_INCH needs to be used.
2000                                 basegfx::B2DHomMatrix aViewTransform(aBufferDevice.GetViewTransformation());
2001                                 const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MAP_INCH));
2002                                 const Size aDPINew(aBufferDevice.LogicToPixel(Size(1, 1), MAP_INCH));
2003                                 const double fDPIXChange((double)aDPIOld.getWidth() / (double)aDPINew.getWidth());
2004                                 const double fDPIYChange((double)aDPIOld.getHeight() / (double)aDPINew.getHeight());
2005 
2006                                 if(!basegfx::fTools::equal(fDPIXChange, 1.0) || !basegfx::fTools::equal(fDPIYChange, 1.0))
2007                                 {
2008                                     aViewTransform.scale(fDPIXChange, fDPIYChange);
2009                                 }
2010 
2011                                 // also take scaling from Size reduction into acount
2012                                 if(!basegfx::fTools::equal(fReduceFactor, 1.0))
2013                                 {
2014                                     aViewTransform.scale(fReduceFactor, fReduceFactor);
2015                                 }
2016 
2017                                 // create view information and pixel renderer. Reuse known ViewInformation
2018 								// except new transformation and range
2019                                 const geometry::ViewInformation2D aViewInfo(
2020 									getViewInformation2D().getObjectTransformation(),
2021 									aViewTransform,
2022 									aViewRange,
2023 									getViewInformation2D().getVisualizedPage(),
2024 									getViewInformation2D().getViewTime(),
2025 									getViewInformation2D().getExtendedInformationSequence());
2026 
2027 								VclPixelProcessor2D aBufferProcessor(aViewInfo, aBufferDevice);
2028 
2029                                 // draw content using pixel renderer
2030 				                aBufferProcessor.process(rContent);
2031 	                            const Bitmap aBmContent(aBufferDevice.GetBitmap(aEmptyPoint, aSizePixel));
2032 
2033                                 // draw transparence using pixel renderer
2034                                 aBufferDevice.Erase();
2035 				                aBufferProcessor.process(rTransparence);
2036                         		const AlphaMask aBmAlpha(aBufferDevice.GetBitmap(aEmptyPoint, aSizePixel));
2037 
2038 #ifdef DBG_UTIL
2039                                 static bool bDoSaveForVisualControl(false);
2040 			                    if(bDoSaveForVisualControl)
2041 			                    {
2042 				                    SvFileStream aNew(String(ByteString( "c:\\test.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC);
2043 
2044                                     WriteDIB(aBmContent, aNew, false, true);
2045 			                    }
2046 #endif
2047 
2048                                 // paint
2049                                 mpOutputDevice->DrawBitmapEx(
2050                                     aRectLogic.TopLeft(),
2051                                     aRectLogic.GetSize(),
2052                                     BitmapEx(aBmContent, aBmAlpha));
2053                             }
2054                         }
2055                     }
2056 
2057 					break;
2058 				}
2059 				case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D :
2060 				{
2061 					// use default transform group pocessing
2062 					RenderTransformPrimitive2D(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate));
2063 					break;
2064 				}
2065                 case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D :
2066 				{
2067 					// new XDrawPage for ViewInformation2D
2068 					RenderPagePreviewPrimitive2D(static_cast< const primitive2d::PagePreviewPrimitive2D& >(rCandidate));
2069 					break;
2070 				}
2071 				case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D :
2072 				{
2073 					// use default marker array pocessing
2074 					RenderMarkerArrayPrimitive2D(static_cast< const primitive2d::MarkerArrayPrimitive2D& >(rCandidate));
2075 					break;
2076 				}
2077 				case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D :
2078 				{
2079 					// use default point array pocessing
2080 					RenderPointArrayPrimitive2D(static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate));
2081 					break;
2082 				}
2083 				case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D :
2084 				{
2085 					// structured tag primitive
2086 					const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate = static_cast< const primitive2d::StructureTagPrimitive2D& >(rCandidate);
2087 					const vcl::PDFWriter::StructElement& rTagElement(rStructureTagCandidate.getStructureElement());
2088 					const bool bTagUsed(vcl::PDFWriter::NonStructElement != rTagElement);
2089 
2090 					if(mpPDFExtOutDevData &&  bTagUsed)
2091 					{
2092 						// write start tag
2093 						mpPDFExtOutDevData->BeginStructureElement(rTagElement);
2094 					}
2095 
2096 					// proccess childs normally
2097 					process(rStructureTagCandidate.getChildren());
2098 
2099 					if(mpPDFExtOutDevData &&  bTagUsed)
2100 					{
2101 						// write end tag
2102 						mpPDFExtOutDevData->EndStructureElement();
2103 					}
2104 
2105 					break;
2106 				}
2107                 case PRIMITIVE2D_ID_EPSPRIMITIVE2D :
2108                 {
2109 					RenderEpsPrimitive2D(static_cast< const primitive2d::EpsPrimitive2D& >(rCandidate));
2110                     break;
2111                 }
2112 				default :
2113 				{
2114                     // process recursively
2115 					process(rCandidate.get2DDecomposition(getViewInformation2D()));
2116 					break;
2117 				}
2118 			}
2119 		}
2120 	} // end of namespace processor2d
2121 } // end of namespace drawinglayer
2122 
2123 //////////////////////////////////////////////////////////////////////////////
2124 // eof
2125