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