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