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