xref: /trunk/main/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx (revision 6633521bc073098a770862620d30a6cde2bbc30c)
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 
800                         // #123295# As described below this is the expanded, uncropped region
801                         // and needs to be given in any case, especially when no cropping it is
802                         // equal to the current rect. To make clear: normally the uncropped region
803                         // (aka the aCropRect) is bigger than the CurrentRect. Or in other words:
804                         // The current rect is the object area. This internal crop definition is
805                         // somewhat crude, but used (and defined in graphic-dependent units what
806                         // leads to even more problems, percentages would have been better). All
807                         // in all this is a place that makes clear that a pure PDF export which does
808                         // not use Metafile and the associated hacks (like this one) but is based on
809                         // Primitves and uses a Primitive Renderer would be the better choice for
810                         // the future.
811                         Rectangle aCropRect(aCurrentRect);
812 
813                         if(rAttr.IsCropped())
814                         {
815                             // calculate scalings between real image size and logic object size. This
816                             // is necessary since the crop values are relative to original bitmap size
817                             double fFactorX(1.0);
818                             double fFactorY(1.0);
819 
820                             {
821                                 const MapMode aMapMode100thmm(MAP_100TH_MM);
822                                 const Size aBitmapSize(Application::GetDefaultDevice()->LogicToLogic(
823                                     rGraphicPrimitive.getGraphicObject().GetPrefSize(),
824                                     rGraphicPrimitive.getGraphicObject().GetPrefMapMode(), aMapMode100thmm));
825                                 const double fDivX(aBitmapSize.Width() - rAttr.GetLeftCrop() - rAttr.GetRightCrop());
826                                 const double fDivY(aBitmapSize.Height() - rAttr.GetTopCrop() - rAttr.GetBottomCrop());
827 
828                                 if(!basegfx::fTools::equalZero(fDivX))
829                                 {
830                                     fFactorX = aScale.getX() / fDivX;
831                                 }
832 
833                                 if(!basegfx::fTools::equalZero(fDivY))
834                                 {
835                                     fFactorY = aScale.getY() / fDivY;
836                                 }
837                             }
838 
839                             // calculate crop range and rect
840                             basegfx::B2DRange aCropRange;
841                             aCropRange.expand(aCurrentRange.getMinimum() - basegfx::B2DPoint(rAttr.GetLeftCrop() * fFactorX, rAttr.GetTopCrop() * fFactorY));
842                             aCropRange.expand(aCurrentRange.getMaximum() + basegfx::B2DPoint(rAttr.GetRightCrop() * fFactorX, rAttr.GetBottomCrop() * fFactorY));
843 
844                             aCropRect = Rectangle(
845                                 sal_Int32(floor(aCropRange.getMinX())), sal_Int32(floor(aCropRange.getMinY())),
846                                 sal_Int32(ceil(aCropRange.getMaxX())), sal_Int32(ceil(aCropRange.getMaxY())));
847                         }
848 
849                         // #123295# 3rd param is uncropped rect, 4th is cropped. The primitive has the cropped
850                         // object transformation, thus aCurrentRect *is* the clip region while aCropRect is the expanded,
851                         // uncropped region. Thus, correct order is aCropRect, aCurrentRect
852                         mpPDFExtOutDevData->EndGroup(rGraphicPrimitive.getGraphicObject().GetGraphic(),
853                             rAttr.GetTransparency(),
854                             aCropRect,
855                             aCurrentRect);
856                     }
857 
858                     break;
859                 }
860                 case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D :
861                 {
862                     const primitive2d::ControlPrimitive2D& rControlPrimitive = static_cast< const primitive2d::ControlPrimitive2D& >(rCandidate);
863                     const uno::Reference< awt::XControl >& rXControl(rControlPrimitive.getXControl());
864                     bool bIsPrintableControl(false);
865 
866                     // find out if control is printable
867                     if(rXControl.is())
868                     {
869                         try
870                         {
871                             uno::Reference< beans::XPropertySet > xModelProperties(rXControl->getModel(), uno::UNO_QUERY);
872                             uno::Reference< beans::XPropertySetInfo > xPropertyInfo(xModelProperties.is()
873                                 ? xModelProperties->getPropertySetInfo()
874                                 : uno::Reference< beans::XPropertySetInfo >());
875                             const ::rtl::OUString sPrintablePropertyName(RTL_CONSTASCII_USTRINGPARAM("Printable"));
876 
877                             if(xPropertyInfo.is() && xPropertyInfo->hasPropertyByName(sPrintablePropertyName))
878                             {
879                                 OSL_VERIFY(xModelProperties->getPropertyValue(sPrintablePropertyName) >>= bIsPrintableControl);
880                             }
881                         }
882                         catch(const uno::Exception&)
883                         {
884                             OSL_ENSURE(false, "VclMetafileProcessor2D: No access to printable flag of Control, caught an exception!");
885                         }
886                     }
887 
888                     // PDF export and printing only for printable controls
889                     if(bIsPrintableControl)
890                     {
891                         const bool bPDFExport(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportFormFields());
892                         bool bDoProcessRecursively(true);
893 
894                         if(bPDFExport)
895                         {
896                             // PDF export. Emulate data handling from UnoControlPDFExportContact
897                             // I have now moved describePDFControl to toolkit, thus i can implement the PDF
898                             // form control support now as follows
899                             ::std::auto_ptr< ::vcl::PDFWriter::AnyWidget > pPDFControl;
900                             ::toolkitform::describePDFControl( rXControl, pPDFControl, *mpPDFExtOutDevData );
901 
902                             if(pPDFControl.get())
903                             {
904                                 // still need to fill in the location (is a class Rectangle)
905                                 const basegfx::B2DRange aRangeLogic(rControlPrimitive.getB2DRange(getViewInformation2D()));
906                                 const Rectangle aRectLogic(
907                                     (sal_Int32)floor(aRangeLogic.getMinX()), (sal_Int32)floor(aRangeLogic.getMinY()),
908                                     (sal_Int32)ceil(aRangeLogic.getMaxX()), (sal_Int32)ceil(aRangeLogic.getMaxY()));
909                                 pPDFControl->Location = aRectLogic;
910 
911                                 Size aFontSize(pPDFControl->TextFont.GetSize());
912                                 aFontSize = mpOutputDevice->LogicToLogic(aFontSize, MapMode(MAP_POINT), mpOutputDevice->GetMapMode());
913                                 pPDFControl->TextFont.SetSize(aFontSize);
914 
915                                 mpPDFExtOutDevData->BeginStructureElement(vcl::PDFWriter::Form);
916                                 mpPDFExtOutDevData->CreateControl(*pPDFControl.get());
917                                 mpPDFExtOutDevData->EndStructureElement();
918 
919                                 // no normal paint needed (see original UnoControlPDFExportContact::do_PaintObject);
920                                 // do not process recursively
921                                 bDoProcessRecursively = false;
922                             }
923                             else
924                             {
925                                 // PDF export did not work, try simple output.
926                                 // Fallback to printer output by not setting bDoProcessRecursively
927                                 // to false.
928                             }
929                         }
930 
931                         // #i93169# used flag the wrong way; true means that nothing was done yet
932                         if(bDoProcessRecursively)
933                         {
934                             // printer output
935                             try
936                             {
937                                 // remember old graphics and create new
938                                 uno::Reference< awt::XView > xControlView(rXControl, uno::UNO_QUERY_THROW);
939                                 const uno::Reference< awt::XGraphics > xOriginalGraphics(xControlView->getGraphics());
940                                 const uno::Reference< awt::XGraphics > xNewGraphics(mpOutputDevice->CreateUnoGraphics());
941 
942                                 if(xNewGraphics.is())
943                                 {
944                                     // link graphics and view
945                                     xControlView->setGraphics(xNewGraphics);
946 
947                                     // get position
948                                     const basegfx::B2DHomMatrix aObjectToDiscrete(getViewInformation2D().getObjectToViewTransformation() * rControlPrimitive.getTransform());
949                                     const basegfx::B2DPoint aTopLeftDiscrete(aObjectToDiscrete * basegfx::B2DPoint(0.0, 0.0));
950 
951                                     // draw it
952                                     xControlView->draw(basegfx::fround(aTopLeftDiscrete.getX()), basegfx::fround(aTopLeftDiscrete.getY()));
953                                     bDoProcessRecursively = false;
954 
955                                     // restore original graphics
956                                     xControlView->setGraphics(xOriginalGraphics);
957                                 }
958                             }
959                             catch( const uno::Exception& )
960                             {
961                                 OSL_ENSURE(false, "VclMetafileProcessor2D: Printing of Control failed, caught an exception!");
962                             }
963                         }
964 
965                         // process recursively if not done yet to export as decomposition (bitmap)
966                         if(bDoProcessRecursively)
967                         {
968                             process(rControlPrimitive.get2DDecomposition(getViewInformation2D()));
969                         }
970                     }
971 
972                     break;
973                 }
974                 case PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D :
975                 {
976                     // support for FIELD_SEQ_BEGIN, FIELD_SEQ_END and URL. It wraps text primitives (but is not limited to)
977                     // thus do the MetafileAction embedding stuff but just handle recursively.
978                     const primitive2d::TextHierarchyFieldPrimitive2D& rFieldPrimitive = static_cast< const primitive2d::TextHierarchyFieldPrimitive2D& >(rCandidate);
979                     static const ByteString aCommentStringCommon("FIELD_SEQ_BEGIN");
980                     static const ByteString aCommentStringPage("FIELD_SEQ_BEGIN;PageField");
981                     static const ByteString aCommentStringEnd("FIELD_SEQ_END");
982 
983                     switch(rFieldPrimitive.getType())
984                     {
985                         default : // case drawinglayer::primitive2d::FIELD_TYPE_COMMON :
986                         {
987                             mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon));
988                             break;
989                         }
990                         case drawinglayer::primitive2d::FIELD_TYPE_PAGE :
991                         {
992                             mpMetaFile->AddAction(new MetaCommentAction(aCommentStringPage));
993                             break;
994                         }
995                         case drawinglayer::primitive2d::FIELD_TYPE_URL :
996                         {
997                             const rtl::OUString& rURL = rFieldPrimitive.getString();
998                             const String aOldString(rURL);
999                             mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon, 0, reinterpret_cast< const sal_uInt8* >(aOldString.GetBuffer()), 2 * aOldString.Len()));
1000                             break;
1001                         }
1002                     }
1003 
1004                     // process recursively
1005                     const primitive2d::Primitive2DSequence rContent = rFieldPrimitive.get2DDecomposition(getViewInformation2D());
1006                     process(rContent);
1007 
1008                     // for the end comment the type is not relevant yet, they are all the same. Just add.
1009                     mpMetaFile->AddAction(new MetaCommentAction(aCommentStringEnd));
1010 
1011                     if(mpPDFExtOutDevData && drawinglayer::primitive2d::FIELD_TYPE_URL == rFieldPrimitive.getType())
1012                     {
1013                         // emulate data handling from ImpEditEngine::Paint
1014                         const basegfx::B2DRange aViewRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent, getViewInformation2D()));
1015                         const Rectangle aRectLogic(
1016                             (sal_Int32)floor(aViewRange.getMinX()), (sal_Int32)floor(aViewRange.getMinY()),
1017                             (sal_Int32)ceil(aViewRange.getMaxX()), (sal_Int32)ceil(aViewRange.getMaxY()));
1018                         vcl::PDFExtOutDevBookmarkEntry aBookmark;
1019                         aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic);
1020                         aBookmark.aBookmark = rFieldPrimitive.getString();
1021                         std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = mpPDFExtOutDevData->GetBookmarks();
1022                         rBookmarks.push_back( aBookmark );
1023                     }
1024 
1025                     break;
1026                 }
1027                 case PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D :
1028                 {
1029                     const primitive2d::TextHierarchyLinePrimitive2D& rLinePrimitive = static_cast< const primitive2d::TextHierarchyLinePrimitive2D& >(rCandidate);
1030                     static const ByteString aCommentString("XTEXT_EOL");
1031 
1032                     // process recursively and add MetaFile comment
1033                     process(rLinePrimitive.get2DDecomposition(getViewInformation2D()));
1034                     mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
1035 
1036                     break;
1037                 }
1038                 case PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D :
1039                 {
1040                     // in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The
1041                     // "XTEXT_EOC" is used, use here, too.
1042                     const primitive2d::TextHierarchyBulletPrimitive2D& rBulletPrimitive = static_cast< const primitive2d::TextHierarchyBulletPrimitive2D& >(rCandidate);
1043                     static const ByteString aCommentString("XTEXT_EOC");
1044 
1045                     // process recursively and add MetaFile comment
1046                     process(rBulletPrimitive.get2DDecomposition(getViewInformation2D()));
1047                     mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
1048 
1049                     break;
1050                 }
1051                 case PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D :
1052                 {
1053                     const primitive2d::TextHierarchyParagraphPrimitive2D& rParagraphPrimitive = static_cast< const primitive2d::TextHierarchyParagraphPrimitive2D& >(rCandidate);
1054                     static const ByteString aCommentString("XTEXT_EOP");
1055 
1056                     if(mpPDFExtOutDevData)
1057                     {
1058                         // emulate data handling from ImpEditEngine::Paint
1059                         mpPDFExtOutDevData->BeginStructureElement( vcl::PDFWriter::Paragraph );
1060                     }
1061 
1062                     // process recursively and add MetaFile comment
1063                     process(rParagraphPrimitive.get2DDecomposition(getViewInformation2D()));
1064                     mpMetaFile->AddAction(new MetaCommentAction(aCommentString));
1065 
1066                     if(mpPDFExtOutDevData)
1067                     {
1068                         // emulate data handling from ImpEditEngine::Paint
1069                         mpPDFExtOutDevData->EndStructureElement();
1070                     }
1071 
1072                     break;
1073                 }
1074                 case PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D :
1075                 {
1076                     const primitive2d::TextHierarchyBlockPrimitive2D& rBlockPrimitive = static_cast< const primitive2d::TextHierarchyBlockPrimitive2D& >(rCandidate);
1077                     static const ByteString aCommentStringA("XTEXT_PAINTSHAPE_BEGIN");
1078                     static const ByteString aCommentStringB("XTEXT_PAINTSHAPE_END");
1079 
1080                     // add MetaFile comment, process recursively and add MetaFile comment
1081                     mpMetaFile->AddAction(new MetaCommentAction(aCommentStringA));
1082                     process(rBlockPrimitive.get2DDecomposition(getViewInformation2D()));
1083                     mpMetaFile->AddAction(new MetaCommentAction(aCommentStringB));
1084 
1085                     break;
1086                 }
1087                 case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D :
1088                 case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D :
1089                 {
1090                     // for supporting TEXT_ MetaFile actions there is more to do here; get the candidate
1091                     const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate = static_cast< const primitive2d::TextSimplePortionPrimitive2D& >(rCandidate);
1092                     // const primitive2d::TextDecoratedPortionPrimitive2D* pTextDecoratedCandidate = dynamic_cast< const primitive2d::TextDecoratedPortionPrimitive2D* >(&rCandidate);
1093 
1094                     // Adapt evtl. used special DrawMode
1095                     const sal_uInt32 nOriginalDrawMode(mpOutputDevice->GetDrawMode());
1096                     adaptTextToFillDrawMode();
1097 
1098                     // directdraw of text simple portion; use default processing
1099                     RenderTextSimpleOrDecoratedPortionPrimitive2D(rTextCandidate);
1100 
1101                     // restore DrawMode
1102                     mpOutputDevice->SetDrawMode(nOriginalDrawMode);
1103 
1104                     // #i101169# if(pTextDecoratedCandidate)
1105                     {
1106                         // support for TEXT_ MetaFile actions only for decorated texts
1107                         if(!mxBreakIterator.is())
1108                         {
1109                             uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xMSF(::comphelper::getProcessServiceFactory());
1110                             mxBreakIterator.set(xMSF->createInstance(rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")), uno::UNO_QUERY);
1111                         }
1112 
1113                         if(mxBreakIterator.is())
1114                         {
1115                             const rtl::OUString& rTxt = rTextCandidate.getText();
1116                             const sal_Int32 nTextLength(rTextCandidate.getTextLength()); // rTxt.getLength());
1117 
1118                             if(nTextLength)
1119                             {
1120                                 const ::com::sun::star::lang::Locale& rLocale = rTextCandidate.getLocale();
1121                                 const sal_Int32 nTextPosition(rTextCandidate.getTextPosition());
1122 
1123                                 sal_Int32 nDone;
1124                                 sal_Int32 nNextCellBreak(mxBreakIterator->nextCharacters(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 0, nDone));
1125                                 ::com::sun::star::i18n::Boundary nNextWordBoundary(mxBreakIterator->getWordBoundary(rTxt, nTextPosition, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True));
1126                                 sal_Int32 nNextSentenceBreak(mxBreakIterator->endOfSentence(rTxt, nTextPosition, rLocale));
1127                                 static const ByteString aCommentStringA("XTEXT_EOC");
1128                                 static const ByteString aCommentStringB("XTEXT_EOW");
1129                                 static const ByteString aCommentStringC("XTEXT_EOS");
1130 
1131                                 for(sal_Int32 i(nTextPosition); i < nTextPosition + nTextLength; i++)
1132                                 {
1133                                     // create the entries for the respective break positions
1134                                     if(i == nNextCellBreak)
1135                                     {
1136                                         mpMetaFile->AddAction(new MetaCommentAction(aCommentStringA, i - nTextPosition));
1137                                         nNextCellBreak = mxBreakIterator->nextCharacters(rTxt, i, rLocale, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
1138                                     }
1139                                     if(i == nNextWordBoundary.endPos)
1140                                     {
1141                                         mpMetaFile->AddAction(new MetaCommentAction(aCommentStringB, i - nTextPosition));
1142                                         nNextWordBoundary = mxBreakIterator->getWordBoundary(rTxt, i + 1, rLocale, ::com::sun::star::i18n::WordType::ANY_WORD, sal_True);
1143                                     }
1144                                     if(i == nNextSentenceBreak)
1145                                     {
1146                                         mpMetaFile->AddAction(new MetaCommentAction(aCommentStringC, i - nTextPosition));
1147                                         nNextSentenceBreak = mxBreakIterator->endOfSentence(rTxt, i + 1, rLocale);
1148                                     }
1149                                 }
1150                             }
1151                         }
1152                     }
1153 
1154                     break;
1155                 }
1156                 case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D :
1157                 {
1158                     const primitive2d::PolygonHairlinePrimitive2D& rHairlinePrimitive = static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate);
1159                     const basegfx::B2DPolygon& rBasePolygon = rHairlinePrimitive.getB2DPolygon();
1160 
1161                     if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
1162                     {
1163                         // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1164                         // per polygon. If there are more, split the polygon in half and call recursively
1165                         basegfx::B2DPolygon aLeft, aRight;
1166                         splitLinePolygon(rBasePolygon, aLeft, aRight);
1167                         const primitive2d::PolygonHairlinePrimitive2D aPLeft(aLeft, rHairlinePrimitive.getBColor());
1168                         const primitive2d::PolygonHairlinePrimitive2D aPRight(aRight, rHairlinePrimitive.getBColor());
1169 
1170                         processBasePrimitive2D(aPLeft);
1171                         processBasePrimitive2D(aPRight);
1172                     }
1173                     else
1174                     {
1175                         // direct draw of hairline; use default processing
1176                         // support SvtGraphicStroke MetaCommentAction
1177                         const basegfx::BColor aLineColor(maBColorModifierStack.getModifiedColor(rHairlinePrimitive.getBColor()));
1178                         SvtGraphicStroke* pSvtGraphicStroke = 0;
1179 
1180                         // #121267# Not needed, does not give better quality compared with
1181                         // the META_POLYPOLYGON_ACTION written by RenderPolygonHairlinePrimitive2D
1182                         // below
1183                         bool bSupportSvtGraphicStroke(false);
1184 
1185                         if(bSupportSvtGraphicStroke)
1186                         {
1187                             pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
1188                                 rHairlinePrimitive.getB2DPolygon(),
1189                                 &aLineColor,
1190                                 0, 0, 0, 0);
1191 
1192                             impStartSvtGraphicStroke(pSvtGraphicStroke);
1193                         }
1194 
1195                         RenderPolygonHairlinePrimitive2D(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate), false);
1196 
1197                         if(bSupportSvtGraphicStroke)
1198                         {
1199                             impEndSvtGraphicStroke(pSvtGraphicStroke);
1200                         }
1201                     }
1202                     break;
1203                 }
1204                 case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D :
1205                 {
1206                     const primitive2d::PolygonStrokePrimitive2D& rStrokePrimitive = static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate);
1207                     const basegfx::B2DPolygon& rBasePolygon = rStrokePrimitive.getB2DPolygon();
1208 
1209                     if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
1210                     {
1211                         // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1212                         // per polygon. If there are more, split the polygon in half and call recursively
1213                         basegfx::B2DPolygon aLeft, aRight;
1214                         splitLinePolygon(rBasePolygon, aLeft, aRight);
1215                         const primitive2d::PolygonStrokePrimitive2D aPLeft(
1216                             aLeft, rStrokePrimitive.getLineAttribute(), rStrokePrimitive.getStrokeAttribute());
1217                         const primitive2d::PolygonStrokePrimitive2D aPRight(
1218                             aRight, rStrokePrimitive.getLineAttribute(), rStrokePrimitive.getStrokeAttribute());
1219 
1220                         processBasePrimitive2D(aPLeft);
1221                         processBasePrimitive2D(aPRight);
1222                     }
1223                     else
1224                     {
1225                         // support SvtGraphicStroke MetaCommentAction
1226                         SvtGraphicStroke* pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
1227                             rBasePolygon, 0,
1228                             &rStrokePrimitive.getLineAttribute(),
1229                             &rStrokePrimitive.getStrokeAttribute(),
1230                             0, 0);
1231 
1232                         impStartSvtGraphicStroke(pSvtGraphicStroke);
1233                         const attribute::LineAttribute& rLine = rStrokePrimitive.getLineAttribute();
1234 
1235                         // create MetaPolyLineActions, but without LINE_DASH
1236                         if(basegfx::fTools::more(rLine.getWidth(), 0.0))
1237                         {
1238                             const attribute::StrokeAttribute& rStroke = rStrokePrimitive.getStrokeAttribute();
1239                             basegfx::B2DPolyPolygon aHairLinePolyPolygon;
1240 
1241                             if(0.0 == rStroke.getFullDotDashLen())
1242                             {
1243                                 aHairLinePolyPolygon.append(rBasePolygon);
1244                             }
1245                             else
1246                             {
1247                                 basegfx::tools::applyLineDashing(
1248                                     rBasePolygon, rStroke.getDotDashArray(),
1249                                     &aHairLinePolyPolygon, 0, rStroke.getFullDotDashLen());
1250                             }
1251 
1252                             const basegfx::BColor aHairlineColor(maBColorModifierStack.getModifiedColor(rLine.getColor()));
1253                             mpOutputDevice->SetLineColor(Color(aHairlineColor));
1254                             mpOutputDevice->SetFillColor();
1255                             aHairLinePolyPolygon.transform(maCurrentTransformation);
1256 
1257                             // #i113922# LineWidth needs to be transformed, too
1258                             const basegfx::B2DVector aDiscreteUnit(maCurrentTransformation * basegfx::B2DVector(rLine.getWidth(), 0.0));
1259                             const double fDiscreteLineWidth(aDiscreteUnit.getLength());
1260 
1261                             LineInfo aLineInfo(LINE_SOLID, basegfx::fround(fDiscreteLineWidth));
1262                             aLineInfo.SetLineJoin(rLine.getLineJoin());
1263                             aLineInfo.SetLineCap(rLine.getLineCap());
1264 
1265                             for(sal_uInt32 a(0); a < aHairLinePolyPolygon.count(); a++)
1266                             {
1267                                 const basegfx::B2DPolygon aCandidate(aHairLinePolyPolygon.getB2DPolygon(a));
1268 
1269                                 if(aCandidate.count() > 1)
1270                                 {
1271                                     const Polygon aToolsPolygon(aCandidate);
1272 
1273                                     mpMetaFile->AddAction(new MetaPolyLineAction(aToolsPolygon, aLineInfo));
1274                                 }
1275                             }
1276                         }
1277                         else
1278                         {
1279                             process(rCandidate.get2DDecomposition(getViewInformation2D()));
1280                         }
1281 
1282                         impEndSvtGraphicStroke(pSvtGraphicStroke);
1283                     }
1284 
1285                     break;
1286                 }
1287                 case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D :
1288                 {
1289                     const primitive2d::PolygonStrokeArrowPrimitive2D& rStrokeArrowPrimitive = static_cast< const primitive2d::PolygonStrokeArrowPrimitive2D& >(rCandidate);
1290                     const basegfx::B2DPolygon& rBasePolygon = rStrokeArrowPrimitive.getB2DPolygon();
1291 
1292                     if(rBasePolygon.count() > (MAX_POLYGON_POINT_COUNT_METAFILE - 1))
1293                     {
1294                         // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1295                         // per polygon. If there are more, split the polygon in half and call recursively
1296                         basegfx::B2DPolygon aLeft, aRight;
1297                         splitLinePolygon(rBasePolygon, aLeft, aRight);
1298                         const attribute::LineStartEndAttribute aEmpty;
1299                         const primitive2d::PolygonStrokeArrowPrimitive2D aPLeft(
1300                             aLeft,
1301                             rStrokeArrowPrimitive.getLineAttribute(),
1302                             rStrokeArrowPrimitive.getStrokeAttribute(),
1303                             rStrokeArrowPrimitive.getStart(),
1304                             aEmpty);
1305                         const primitive2d::PolygonStrokeArrowPrimitive2D aPRight(
1306                             aRight,
1307                             rStrokeArrowPrimitive.getLineAttribute(),
1308                             rStrokeArrowPrimitive.getStrokeAttribute(),
1309                             aEmpty,
1310                             rStrokeArrowPrimitive.getEnd());
1311 
1312                         processBasePrimitive2D(aPLeft);
1313                         processBasePrimitive2D(aPRight);
1314                     }
1315                     else
1316                     {
1317                         // support SvtGraphicStroke MetaCommentAction
1318                         SvtGraphicStroke* pSvtGraphicStroke = impTryToCreateSvtGraphicStroke(
1319                             rBasePolygon, 0,
1320                             &rStrokeArrowPrimitive.getLineAttribute(),
1321                             &rStrokeArrowPrimitive.getStrokeAttribute(),
1322                             &rStrokeArrowPrimitive.getStart(),
1323                             &rStrokeArrowPrimitive.getEnd());
1324 
1325                         // write LineGeometry start marker
1326                         impStartSvtGraphicStroke(pSvtGraphicStroke);
1327 
1328                         // #116162# When B&W is set as DrawMode, DRAWMODE_WHITEFILL is used
1329                         // to let all fills be just white; for lines DRAWMODE_BLACKLINE is used
1330                         // so all line geometry is supposed to get black. Since in the in-between
1331                         // stages of line geometry drawing filled polygons are used (e.g. line
1332                         // start/ends) it is necessary to change these drawmodes to preserve
1333                         // that lines shall be black; thus change DRAWMODE_WHITEFILL to
1334                         // DRAWMODE_BLACKFILL during line geometry processing to have line geometry
1335                         // parts filled black.
1336                         const sal_uLong nOldDrawMode(mpOutputDevice->GetDrawMode());
1337                         const bool bDrawmodeChange(nOldDrawMode & DRAWMODE_WHITEFILL && mnSvtGraphicStrokeCount);
1338 
1339                         if(bDrawmodeChange)
1340                         {
1341                             mpOutputDevice->SetDrawMode((nOldDrawMode & ~DRAWMODE_WHITEFILL) | DRAWMODE_BLACKFILL);
1342                         }
1343 
1344                         // process sub-line geometry (evtl. filled PolyPolygons)
1345                         process(rCandidate.get2DDecomposition(getViewInformation2D()));
1346 
1347                         if(bDrawmodeChange)
1348                         {
1349                             mpOutputDevice->SetDrawMode(nOldDrawMode);
1350                         }
1351 
1352                         // write LineGeometry end marker
1353                         impEndSvtGraphicStroke(pSvtGraphicStroke);
1354                     }
1355 
1356                     break;
1357                 }
1358                 case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D :
1359                 {
1360                     // direct draw of transformed BitmapEx primitive; use default processing, but without
1361                     // former testing if graphic content is inside discrete local viewport; this is not
1362                     // setup for metafile targets (metafile renderer tries to render in logic coordinates,
1363                     // the mapping is kept to the OutputDevice for better Metafile recording)
1364                     RenderBitmapPrimitive2D(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate));
1365                     break;
1366                 }
1367                 case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D :
1368                 {
1369                     // need to handle PolyPolygonGraphicPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
1370                     const primitive2d::PolyPolygonGraphicPrimitive2D& rBitmapCandidate = static_cast< const primitive2d::PolyPolygonGraphicPrimitive2D& >(rCandidate);
1371                     basegfx::B2DPolyPolygon aLocalPolyPolygon(rBitmapCandidate.getB2DPolyPolygon());
1372 
1373                     if(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1374                     {
1375                         // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1376                         // per polygon. If there are more use the splitted polygon and call recursively
1377                         const primitive2d::PolyPolygonGraphicPrimitive2D aSplitted(
1378                             aLocalPolyPolygon,
1379                             rBitmapCandidate.getFillGraphic());
1380 
1381                         processBasePrimitive2D(aSplitted);
1382                     }
1383                     else
1384                     {
1385                         SvtGraphicFill* pSvtGraphicFill = 0;
1386 
1387                         if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1388                         {
1389                             // #121194# Changed implementation and checked usages fo convert to metafile,
1390                             // presentation start (uses SvtGraphicFill) and printing.
1391 
1392                             // calculate transformation. Get real object size, all values in FillGraphicAttribute
1393                             // are relative to the unified object
1394                             aLocalPolyPolygon.transform(maCurrentTransformation);
1395                             const basegfx::B2DVector aOutlineSize(aLocalPolyPolygon.getB2DRange().getRange());
1396 
1397                             // the scaling needs scale from pixel to logic coordinate system
1398                             const attribute::FillGraphicAttribute& rFillGraphicAttribute = rBitmapCandidate.getFillGraphic();
1399                             const Size aBmpSizePixel(rFillGraphicAttribute.getGraphic().GetSizePixel());
1400 
1401                             // setup transformation like in impgrfll. Multiply with aOutlineSize
1402                             // to get from unit coordinates in rFillGraphicAttribute.getGraphicRange()
1403                             // to object coordinates with object's top left being at (0,0). Divide
1404                             // by pixel size so that scale from pixel to logic will work in SvtGraphicFill.
1405                             const basegfx::B2DVector aTransformScale(
1406                                 rFillGraphicAttribute.getGraphicRange().getRange() /
1407                                 basegfx::B2DVector(
1408                                     std::max(1.0, double(aBmpSizePixel.Width())),
1409                                     std::max(1.0, double(aBmpSizePixel.Height()))) *
1410                                 aOutlineSize);
1411                             const basegfx::B2DPoint aTransformPosition(
1412                                 rFillGraphicAttribute.getGraphicRange().getMinimum() * aOutlineSize);
1413 
1414                             // setup transformation like in impgrfll
1415                             SvtGraphicFill::Transform aTransform;
1416 
1417                             // scale values are divided by bitmap pixel sizes
1418                             aTransform.matrix[0] = aTransformScale.getX();
1419                             aTransform.matrix[4] = aTransformScale.getY();
1420 
1421                             // translates are absolute
1422                             aTransform.matrix[2] = aTransformPosition.getX();
1423                             aTransform.matrix[5] = aTransformPosition.getY();
1424 
1425                             pSvtGraphicFill = new SvtGraphicFill(
1426                                 PolyPolygon(aLocalPolyPolygon),
1427                                 Color(),
1428                                 0.0,
1429                                 SvtGraphicFill::fillEvenOdd,
1430                                 SvtGraphicFill::fillTexture,
1431                                 aTransform,
1432                                 rFillGraphicAttribute.getTiling(),
1433                                 SvtGraphicFill::hatchSingle,
1434                                 Color(),
1435                                 SvtGraphicFill::gradientLinear,
1436                                 Color(),
1437                                 Color(),
1438                                 0,
1439                                 rFillGraphicAttribute.getGraphic());
1440                         }
1441 
1442                         // Do use decomposition; encapsulate with SvtGraphicFill
1443                         impStartSvtGraphicFill(pSvtGraphicFill);
1444                         process(rCandidate.get2DDecomposition(getViewInformation2D()));
1445                         impEndSvtGraphicFill(pSvtGraphicFill);
1446                     }
1447 
1448                     break;
1449                 }
1450                 case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D :
1451                 {
1452                     // need to handle PolyPolygonHatchPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
1453                     const primitive2d::PolyPolygonHatchPrimitive2D& rHatchCandidate = static_cast< const primitive2d::PolyPolygonHatchPrimitive2D& >(rCandidate);
1454                     const attribute::FillHatchAttribute& rFillHatchAttribute = rHatchCandidate.getFillHatch();
1455                     basegfx::B2DPolyPolygon aLocalPolyPolygon(rHatchCandidate.getB2DPolyPolygon());
1456 
1457                     if(aLocalPolyPolygon.getB2DRange() != rHatchCandidate.getDefinitionRange())
1458                     {
1459                         // the range which defines the hatch is different from the range of the
1460                         // geometry (used for writer frames). This cannot be done calling vcl, thus use
1461                         // decomposition here
1462                         process(rCandidate.get2DDecomposition(getViewInformation2D()));
1463                         break;
1464                     }
1465 
1466                     // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1467                     // per polygon. Split polygon until there are less than that
1468                     while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1469                         ;
1470 
1471                     if(rFillHatchAttribute.isFillBackground())
1472                     {
1473                         // with fixing #i111954# (see below) the possible background
1474                         // fill of a hatched object was lost.Generate a background fill
1475                         // primitive and render it
1476                         const primitive2d::Primitive2DReference xBackground(
1477                             new primitive2d::PolyPolygonColorPrimitive2D(
1478                                 aLocalPolyPolygon,
1479                                 rHatchCandidate.getBackgroundColor()));
1480 
1481                         process(primitive2d::Primitive2DSequence(&xBackground, 1));
1482                     }
1483 
1484                     SvtGraphicFill* pSvtGraphicFill = 0;
1485                     aLocalPolyPolygon.transform(maCurrentTransformation);
1486 
1487                     if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1488                     {
1489                         // re-create a VCL hatch as base data
1490                         SvtGraphicFill::HatchType eHatch(SvtGraphicFill::hatchSingle);
1491 
1492                         switch(rFillHatchAttribute.getStyle())
1493                         {
1494                             default: // attribute::HATCHSTYLE_SINGLE :
1495                             {
1496                                 eHatch = SvtGraphicFill::hatchSingle;
1497                                 break;
1498                             }
1499                             case attribute::HATCHSTYLE_DOUBLE :
1500                             {
1501                                 eHatch = SvtGraphicFill::hatchDouble;
1502                                 break;
1503                             }
1504                             case attribute::HATCHSTYLE_TRIPLE :
1505                             {
1506                                 eHatch = SvtGraphicFill::hatchTriple;
1507                                 break;
1508                             }
1509                         }
1510 
1511                         SvtGraphicFill::Transform aTransform;
1512 
1513                         // scale
1514                         aTransform.matrix[0] *= rFillHatchAttribute.getDistance();
1515                         aTransform.matrix[4] *= rFillHatchAttribute.getDistance();
1516 
1517                         // rotate (was never correct in impgrfll anyways, use correct angle now)
1518                         aTransform.matrix[0] *= cos(rFillHatchAttribute.getAngle());
1519                         aTransform.matrix[1] *= -sin(rFillHatchAttribute.getAngle());
1520                         aTransform.matrix[3] *= sin(rFillHatchAttribute.getAngle());
1521                         aTransform.matrix[4] *= cos(rFillHatchAttribute.getAngle());
1522 
1523                         pSvtGraphicFill = new SvtGraphicFill(
1524                             PolyPolygon(aLocalPolyPolygon),
1525                             Color(),
1526                             0.0,
1527                             SvtGraphicFill::fillEvenOdd,
1528                             SvtGraphicFill::fillHatch,
1529                             aTransform,
1530                             false,
1531                             eHatch,
1532                             Color(rFillHatchAttribute.getColor()),
1533                             SvtGraphicFill::gradientLinear,
1534                             Color(),
1535                             Color(),
1536                             0,
1537                             Graphic());
1538                     }
1539 
1540                     // Do use decomposition; encapsulate with SvtGraphicFill
1541                     impStartSvtGraphicFill(pSvtGraphicFill);
1542 
1543                     // #i111954# do NOT use decomposition, but use direct VCL-command
1544                     // process(rCandidate.get2DDecomposition(getViewInformation2D()));
1545                     const PolyPolygon aToolsPolyPolygon(aLocalPolyPolygon);
1546                     const HatchStyle aHatchStyle(
1547                         attribute::HATCHSTYLE_SINGLE == rFillHatchAttribute.getStyle() ? HATCH_SINGLE :
1548                         attribute::HATCHSTYLE_DOUBLE == rFillHatchAttribute.getStyle() ? HATCH_DOUBLE :
1549                         HATCH_TRIPLE);
1550 
1551                     mpOutputDevice->DrawHatch(aToolsPolyPolygon,
1552                         Hatch(aHatchStyle,
1553                             Color(rFillHatchAttribute.getColor()),
1554                             basegfx::fround(rFillHatchAttribute.getDistance()),
1555                             basegfx::fround(rFillHatchAttribute.getAngle() / F_PI1800)));
1556 
1557                     impEndSvtGraphicFill(pSvtGraphicFill);
1558 
1559                     break;
1560                 }
1561                 case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D :
1562                 {
1563                     basegfx::B2DVector aScale, aTranslate;
1564                     double fRotate, fShearX;
1565 
1566                     maCurrentTransformation.decompose(aScale, aTranslate, fRotate, fShearX);
1567 
1568                     if(!basegfx::fTools::equalZero(fRotate) || !basegfx::fTools::equalZero(fShearX))
1569                     {
1570                         // #121185# When rotation or shear is used, a VCL Gradient cannot be used directly.
1571                         // This is because VCL Gradient mechanism does *not* support to rotate the gradient
1572                         // with objects and this case is not expressable in a Metafile (and cannot be added
1573                         // since the FileFormats used, e.g. *.wmf, do not support it either).
1574                         // Such cases happen when a graphic object uses a Metafile as graphic information or
1575                         // a fill style definition uses a Metafile. In this cases the graphic content is
1576                         // rotated with the graphic or filled object; this is not supported by the target
1577                         // format of this conversion renderer - Metafiles.
1578                         // To solve this, not a Gradient is written, but the decomposition of this object
1579                         // is written to the Metafile. This is the PolyPolygons building the gradient fill.
1580                         // These will need more space and time, but the result will be as if the Gradient
1581                         // was rotated with the object.
1582                         // This mechanism is used by all exporters still not using Primtives (e.g. Print,
1583                         // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile
1584                         // transfers. One more reason to *change* these to primitives.
1585                         // BTW: One more example how useful the principles of primitives are; the decomposition
1586                         // is by definition a simpler, maybe more expensive representation of the same content.
1587                         process(rCandidate.get2DDecomposition(getViewInformation2D()));
1588                         break;
1589                     }
1590 
1591                     const primitive2d::PolyPolygonGradientPrimitive2D& rGradientCandidate = static_cast< const primitive2d::PolyPolygonGradientPrimitive2D& >(rCandidate);
1592                     basegfx::B2DPolyPolygon aLocalPolyPolygon(rGradientCandidate.getB2DPolyPolygon());
1593 
1594                     if(aLocalPolyPolygon.getB2DRange() != rGradientCandidate.getDefinitionRange())
1595                     {
1596                         // the range which defines the gradient is different from the range of the
1597                         // geometry (used for writer frames). This cannot be done calling vcl, thus use
1598                         // decomposition here
1599                         process(rCandidate.get2DDecomposition(getViewInformation2D()));
1600                         break;
1601                     }
1602 
1603                     // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1604                     // per polygon. Split polygon until there are less than that
1605                     while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1606                         ;
1607 
1608                     // for support of MetaCommentActions of the form XGRAD_SEQ_BEGIN, XGRAD_SEQ_END
1609                     // it is safest to use the VCL OutputDevice::DrawGradient method which creates those.
1610                     // re-create a VCL-gradient from FillGradientPrimitive2D and the needed tools PolyPolygon
1611                     Gradient aVCLGradient;
1612                     impConvertFillGradientAttributeToVCLGradient(aVCLGradient, rGradientCandidate.getFillGradient(), false);
1613                     aLocalPolyPolygon.transform(maCurrentTransformation);
1614 
1615                     // #i82145# ATM VCL printing of gradients using curved shapes does not work,
1616                     // i submitted the bug with the given ID to THB. When that task is fixed it is
1617                     // necessary to again remove this subdivision since it decreases possible
1618                     // printing quality (not even resolution-dependent for now). THB will tell
1619                     // me when that task is fixed in the master
1620                     const PolyPolygon aToolsPolyPolygon(basegfx::tools::adaptiveSubdivideByAngle(aLocalPolyPolygon));
1621 
1622                     // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1623                     SvtGraphicFill* pSvtGraphicFill = 0;
1624 
1625                     if(!mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1626                     {
1627                         // setup gradient stuff like in like in impgrfll
1628                         SvtGraphicFill::GradientType eGrad(SvtGraphicFill::gradientLinear);
1629 
1630                         switch(aVCLGradient.GetStyle())
1631                         {
1632                             default : // GRADIENT_LINEAR:
1633                             case GRADIENT_AXIAL:
1634                                 eGrad = SvtGraphicFill::gradientLinear;
1635                                 break;
1636                             case GRADIENT_RADIAL:
1637                             case GRADIENT_ELLIPTICAL:
1638                                 eGrad = SvtGraphicFill::gradientRadial;
1639                                 break;
1640                             case GRADIENT_SQUARE:
1641                             case GRADIENT_RECT:
1642                                 eGrad = SvtGraphicFill::gradientRectangular;
1643                                 break;
1644                         }
1645 
1646                         pSvtGraphicFill = new SvtGraphicFill(
1647                             aToolsPolyPolygon,
1648                             Color(),
1649                             0.0,
1650                             SvtGraphicFill::fillEvenOdd,
1651                             SvtGraphicFill::fillGradient,
1652                             SvtGraphicFill::Transform(),
1653                             false,
1654                             SvtGraphicFill::hatchSingle,
1655                             Color(),
1656                             eGrad,
1657                             aVCLGradient.GetStartColor(),
1658                             aVCLGradient.GetEndColor(),
1659                             aVCLGradient.GetSteps(),
1660                             Graphic());
1661                     }
1662 
1663                     // call VCL directly; encapsulate with SvtGraphicFill
1664                     impStartSvtGraphicFill(pSvtGraphicFill);
1665                     mpOutputDevice->DrawGradient(aToolsPolyPolygon, aVCLGradient);
1666                     impEndSvtGraphicFill(pSvtGraphicFill);
1667 
1668                     break;
1669                 }
1670                 case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D :
1671                 {
1672                     const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate));
1673                     basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolygonCandidate.getB2DPolyPolygon());
1674 
1675                     // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1676                     // per polygon. Split polygon until there are less than that
1677                     while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1678                         ;
1679 
1680                     const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(rPolygonCandidate.getBColor()));
1681                     aLocalPolyPolygon.transform(maCurrentTransformation);
1682 
1683                     // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1684                     SvtGraphicFill* pSvtGraphicFill = 0;
1685 
1686                     // #121267# Not needed, does not give better quality compared with
1687                     // the META_POLYPOLYGON_ACTION written by the DrawPolyPolygon command
1688                     // below
1689                     bool bSupportSvtGraphicFill(false);
1690 
1691                     if(bSupportSvtGraphicFill && !mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1692                     {
1693                         // setup simple color fill stuff like in impgrfll
1694                         pSvtGraphicFill = new SvtGraphicFill(
1695                             PolyPolygon(aLocalPolyPolygon),
1696                             Color(aPolygonColor),
1697                             0.0,
1698                             SvtGraphicFill::fillEvenOdd,
1699                             SvtGraphicFill::fillSolid,
1700                             SvtGraphicFill::Transform(),
1701                             false,
1702                             SvtGraphicFill::hatchSingle,
1703                             Color(),
1704                             SvtGraphicFill::gradientLinear,
1705                             Color(),
1706                             Color(),
1707                             0,
1708                             Graphic());
1709                     }
1710 
1711                     // set line and fill color
1712                     mpOutputDevice->SetFillColor(Color(aPolygonColor));
1713                     mpOutputDevice->SetLineColor();
1714 
1715                     // call VCL directly; encapsulate with SvtGraphicFill
1716                     if(bSupportSvtGraphicFill)
1717                     {
1718                             impStartSvtGraphicFill(pSvtGraphicFill);
1719                     }
1720 
1721                     mpOutputDevice->DrawPolyPolygon(aLocalPolyPolygon);
1722 
1723                     if(bSupportSvtGraphicFill)
1724                     {
1725                         impEndSvtGraphicFill(pSvtGraphicFill);
1726                     }
1727 
1728                     break;
1729                 }
1730                 case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
1731                 {
1732                     // mask group. Special handling for MetaFiles.
1733                     const primitive2d::MaskPrimitive2D& rMaskCandidate = static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate);
1734 
1735                     if(rMaskCandidate.getChildren().hasElements())
1736                     {
1737                         basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
1738 
1739                         if(aMask.count())
1740                         {
1741                             // prepare new mask polygon and rescue current one
1742                             aMask.transform(maCurrentTransformation);
1743                             const basegfx::B2DPolyPolygon aLastClipPolyPolygon(maClipPolyPolygon);
1744 
1745                             if(maClipPolyPolygon.count())
1746                             {
1747                                 // due to the cost of PolyPolygon clipping and numerical reasons try first if the current
1748                                 // and the new ClipRegion are ranges. If yes, processing can be simplified
1749                                 if(basegfx::tools::isRectangle(aMask)
1750                                     && basegfx::tools::isRectangle(maClipPolyPolygon))
1751                                 {
1752                                     // both ClipPolygons are rectangles
1753                                     if(aMask.getB2DRange().equal(maClipPolyPolygon.getB2DRange()))
1754                                     {
1755                                         // equal -> no change in ClipRegion needed, leave
1756                                         // maClipPolyPolygon unchanged
1757                                     }
1758                                     else
1759                                     {
1760                                         // not equal -> create new ClipRegion from the two ranges
1761                                         basegfx::B2DRange aClipRange(aMask.getB2DRange());
1762 
1763                                         aClipRange.intersect(maClipPolyPolygon.getB2DRange());
1764 
1765                                         if(aClipRange.isEmpty())
1766                                         {
1767                                             // no common ClipRegion -> set empty ClipRegion, no content to show
1768                                             maClipPolyPolygon.clear();
1769                                         }
1770                                         else
1771                                         {
1772                                             // use common ClipRegion as new ClipRegion
1773                                             maClipPolyPolygon = basegfx::B2DPolyPolygon(
1774                                                 basegfx::tools::createPolygonFromRect(aClipRange));
1775                                         }
1776                                     }
1777                                 }
1778                                 else
1779                                 {
1780                                     // The current ClipRegion or the new one is not a rectangle;
1781                                     // there is already a clip polygon set; build clipped union of
1782                                     // current mask polygon and new one
1783                                     maClipPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon(
1784                                         aMask,
1785                                         maClipPolyPolygon,
1786                                         true, // #i106516# we want the inside of aMask, not the outside
1787                                         false);
1788                                 }
1789                             }
1790                             else
1791                             {
1792                                 // use new mask directly as ClipRegion
1793                                 maClipPolyPolygon = aMask;
1794                             }
1795 
1796                             if(maClipPolyPolygon.count())
1797                             {
1798                                 // set VCL clip region; subdivide before conversion to tools polygon. Subdivision necessary (!)
1799                                 // Removed subdivision and fixed in Region::ImplPolyPolyRegionToBandRegionFunc() in VCL where
1800                                 // the ClipRegion is built from the Polygon. A AdaptiveSubdivide on the source polygon was missing there
1801                                 const bool bNewClipRegion(maClipPolyPolygon != aLastClipPolyPolygon);
1802 
1803                                 if(bNewClipRegion)
1804                                 {
1805                                     mpOutputDevice->Push(PUSH_CLIPREGION);
1806                                     mpOutputDevice->SetClipRegion(Region(maClipPolyPolygon));
1807                                 }
1808 
1809                                 // recursively paint content
1810                                 // #121267# Only need to process sub-content when clip polygon is *not* empty.
1811                                 // If it is empty, the clip is empty and there can be nothing inside.
1812                                 process(rMaskCandidate.getChildren());
1813 
1814                                 // restore VCL clip region
1815                                 if(bNewClipRegion)
1816                                 {
1817                                     mpOutputDevice->Pop();
1818                                 }
1819                             }
1820 
1821                             // restore to rescued clip polygon
1822                             maClipPolyPolygon = aLastClipPolyPolygon;
1823                         }
1824                         else
1825                         {
1826                             // no mask, no clipping. recursively paint content
1827                             process(rMaskCandidate.getChildren());
1828                         }
1829                     }
1830 
1831                     break;
1832                 }
1833                 case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D :
1834                 {
1835                     // modified color group. Force output to unified color. Use default pocessing.
1836                     RenderModifiedColorPrimitive2D(static_cast< const primitive2d::ModifiedColorPrimitive2D& >(rCandidate));
1837                     break;
1838                 }
1839                 case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D :
1840                 {
1841                     // HiddenGeometryPrimitive2D; to rebuilt the old MetaFile creation, it is necessary to
1842                     // not ignore them (as it was thought), but to add a MetaFile entry for them.
1843                     basegfx::B2DRange aInvisibleRange(rCandidate.getB2DRange(getViewInformation2D()));
1844 
1845                     if(!aInvisibleRange.isEmpty())
1846                     {
1847                         aInvisibleRange.transform(maCurrentTransformation);
1848                         const Rectangle aRectLogic(
1849                             (sal_Int32)floor(aInvisibleRange.getMinX()), (sal_Int32)floor(aInvisibleRange.getMinY()),
1850                             (sal_Int32)ceil(aInvisibleRange.getMaxX()), (sal_Int32)ceil(aInvisibleRange.getMaxY()));
1851 
1852                         mpOutputDevice->SetFillColor();
1853                         mpOutputDevice->SetLineColor();
1854                         mpOutputDevice->DrawRect(aRectLogic);
1855                     }
1856 
1857                     break;
1858                 }
1859                 case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D :
1860                 {
1861                     // for metafile: Need to examine what the pure vcl version is doing here actually
1862                     // - uses DrawTransparent with metafile for content and a gradient
1863                     // - uses DrawTransparent for single PolyPoylgons directly. Can be detected by
1864                     //   checking the content for single PolyPolygonColorPrimitive2D
1865                     const primitive2d::UnifiedTransparencePrimitive2D& rUniTransparenceCandidate = static_cast< const primitive2d::UnifiedTransparencePrimitive2D& >(rCandidate);
1866                     const primitive2d::Primitive2DSequence rContent = rUniTransparenceCandidate.getChildren();
1867 
1868                     if(rContent.hasElements())
1869                     {
1870                         if(0.0 == rUniTransparenceCandidate.getTransparence())
1871                         {
1872                             // not transparent at all, use content
1873                             process(rUniTransparenceCandidate.getChildren());
1874                         }
1875                         else if(rUniTransparenceCandidate.getTransparence() > 0.0 && rUniTransparenceCandidate.getTransparence() < 1.0)
1876                         {
1877                             // try to identify a single PolyPolygonColorPrimitive2D in the
1878                             // content part of the transparence primitive
1879                             const primitive2d::PolyPolygonColorPrimitive2D* pPoPoColor = 0;
1880                             static bool bForceToMetafile(false);
1881 
1882                             if(!bForceToMetafile && 1 == rContent.getLength())
1883                             {
1884                                 const primitive2d::Primitive2DReference xReference(rContent[0]);
1885                                 pPoPoColor = dynamic_cast< const primitive2d::PolyPolygonColorPrimitive2D* >(xReference.get());
1886                             }
1887 
1888                             // PolyPolygonGradientPrimitive2D, PolyPolygonHatchPrimitive2D and
1889                             // PolyPolygonGraphicPrimitive2D are derived from PolyPolygonColorPrimitive2D.
1890                             // Check also for correct ID to exclude derived implementations
1891                             if(pPoPoColor && PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D == pPoPoColor->getPrimitive2DID())
1892                             {
1893                                 // single transparent PolyPolygon identified, use directly
1894                                 const basegfx::BColor aPolygonColor(maBColorModifierStack.getModifiedColor(pPoPoColor->getBColor()));
1895                                 basegfx::B2DPolyPolygon aLocalPolyPolygon(pPoPoColor->getB2DPolyPolygon());
1896 
1897                                 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1898                                 // per polygon. Split polygon until there are less than that
1899                                 while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon))
1900                                     ;
1901 
1902                                 // now transform
1903                                 aLocalPolyPolygon.transform(maCurrentTransformation);
1904 
1905                                 // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1906                                 SvtGraphicFill* pSvtGraphicFill = 0;
1907 
1908                                 // #121267# Not needed, does not give better quality compared with
1909                                 // the META_POLYPOLYGON_ACTION written by the DrawPolyPolygon command
1910                                 // below
1911                                 bool bSupportSvtGraphicFill(false);
1912 
1913                                 if(bSupportSvtGraphicFill && !mnSvtGraphicFillCount && aLocalPolyPolygon.count())
1914                                 {
1915                                     // setup simple color with transparence fill stuff like in impgrfll
1916                                     pSvtGraphicFill = new SvtGraphicFill(
1917                                         PolyPolygon(aLocalPolyPolygon),
1918                                         Color(aPolygonColor),
1919                                         rUniTransparenceCandidate.getTransparence(),
1920                                         SvtGraphicFill::fillEvenOdd,
1921                                         SvtGraphicFill::fillSolid,
1922                                         SvtGraphicFill::Transform(),
1923                                         false,
1924                                         SvtGraphicFill::hatchSingle,
1925                                         Color(),
1926                                         SvtGraphicFill::gradientLinear,
1927                                         Color(),
1928                                         Color(),
1929                                         0,
1930                                         Graphic());
1931                                 }
1932 
1933                                 // set line and fill color
1934                                 const sal_uInt16 nTransPercentVcl((sal_uInt16)basegfx::fround(rUniTransparenceCandidate.getTransparence() * 100.0));
1935                                 mpOutputDevice->SetFillColor(Color(aPolygonColor));
1936                                 mpOutputDevice->SetLineColor();
1937 
1938                                 // call VCL directly; encapsulate with SvtGraphicFill
1939                                 if(bSupportSvtGraphicFill)
1940                                 {
1941                                     impStartSvtGraphicFill(pSvtGraphicFill);
1942                                 }
1943 
1944                                 mpOutputDevice->DrawTransparent(
1945                                     PolyPolygon(aLocalPolyPolygon),
1946                                     nTransPercentVcl);
1947 
1948                                 if(bSupportSvtGraphicFill)
1949                                 {
1950                                     impEndSvtGraphicFill(pSvtGraphicFill);
1951                                 }
1952                             }
1953                             else
1954                             {
1955                                 // svae old mfCurrentUnifiedTransparence and set new one
1956                                 // so that contained SvtGraphicStroke may use the current one
1957                                 const double fLastCurrentUnifiedTransparence(mfCurrentUnifiedTransparence);
1958                                 // #i105377# paint the content metafile opaque as the transparency gets
1959                                 // split of into the gradient below
1960                                 // mfCurrentUnifiedTransparence = rUniTransparenceCandidate.getTransparence();
1961                                 mfCurrentUnifiedTransparence = 0;
1962 
1963                                 // various content, create content-metafile
1964                                 GDIMetaFile aContentMetafile;
1965                                 const Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile));
1966 
1967                                 // restore mfCurrentUnifiedTransparence; it may have been used
1968                                 // while processing the sub-content in impDumpToMetaFile
1969                                 mfCurrentUnifiedTransparence = fLastCurrentUnifiedTransparence;
1970 
1971                                 // create uniform VCL gradient for uniform transparency
1972                                 Gradient aVCLGradient;
1973                                 const sal_uInt8 nTransPercentVcl((sal_uInt8)basegfx::fround(rUniTransparenceCandidate.getTransparence() * 255.0));
1974                                 const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl);
1975 
1976                                 aVCLGradient.SetStyle(GRADIENT_LINEAR);
1977                                 aVCLGradient.SetStartColor(aTransColor);
1978                                 aVCLGradient.SetEndColor(aTransColor);
1979                                 aVCLGradient.SetAngle(0);
1980                                 aVCLGradient.SetBorder(0);
1981                                 aVCLGradient.SetOfsX(0);
1982                                 aVCLGradient.SetOfsY(0);
1983                                 aVCLGradient.SetStartIntensity(100);
1984                                 aVCLGradient.SetEndIntensity(100);
1985                                 aVCLGradient.SetSteps(2);
1986 
1987                                 // render it to VCL
1988                                 mpOutputDevice->DrawTransparent(
1989                                     aContentMetafile, aPrimitiveRectangle.TopLeft(),
1990                                     aPrimitiveRectangle.GetSize(), aVCLGradient);
1991                             }
1992                         }
1993                     }
1994 
1995                     break;
1996                 }
1997                 case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D :
1998                 {
1999                     // for metafile: Need to examine what the pure vcl version is doing here actually
2000                     // - uses DrawTransparent with metafile for content and a gradient
2001                     // i can detect this here with checking the gradient part for a single
2002                     // FillGradientPrimitive2D and reconstruct the gradient.
2003                     // If that detection goes wrong, i have to create an transparence-blended bitmap. Eventually
2004                     // do that in stripes, else RenderTransparencePrimitive2D may just be used
2005                     const primitive2d::TransparencePrimitive2D& rTransparenceCandidate = static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate);
2006                     const primitive2d::Primitive2DSequence rContent = rTransparenceCandidate.getChildren();
2007                     const primitive2d::Primitive2DSequence rTransparence = rTransparenceCandidate.getTransparence();
2008 
2009                     if(rContent.hasElements() && rTransparence.hasElements())
2010                     {
2011                         // try to identify a single FillGradientPrimitive2D in the
2012                         // transparence part of the primitive
2013                         const primitive2d::FillGradientPrimitive2D* pFiGradient = 0;
2014                         static bool bForceToBigTransparentVDev(false);
2015 
2016                         if(!bForceToBigTransparentVDev && 1 == rTransparence.getLength())
2017                         {
2018                             const primitive2d::Primitive2DReference xReference(rTransparence[0]);
2019                             pFiGradient = dynamic_cast< const primitive2d::FillGradientPrimitive2D* >(xReference.get());
2020                         }
2021 
2022                         // Check also for correct ID to exclude derived implementations
2023                         if(pFiGradient && PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D == pFiGradient->getPrimitive2DID())
2024                         {
2025                             // various content, create content-metafile
2026                             GDIMetaFile aContentMetafile;
2027                             const Rectangle aPrimitiveRectangle(impDumpToMetaFile(rContent, aContentMetafile));
2028 
2029                             // re-create a VCL-gradient from FillGradientPrimitive2D
2030                             Gradient aVCLGradient;
2031                             impConvertFillGradientAttributeToVCLGradient(aVCLGradient, pFiGradient->getFillGradient(), true);
2032 
2033                             // render it to VCL
2034                             mpOutputDevice->DrawTransparent(
2035                                 aContentMetafile, aPrimitiveRectangle.TopLeft(),
2036                                 aPrimitiveRectangle.GetSize(), aVCLGradient);
2037                         }
2038                         else
2039                         {
2040                             // sub-transparence group. Draw to VDev first.
2041                             // this may get refined to tiling when resolution is too big here
2042 
2043                             // need to avoid switching off MapMode stuff here; maybe need another
2044                             // tooling class, cannot just do the same as with the pixel renderer.
2045                             // Need to experiment...
2046 
2047                             // Okay, basic implementation finished and tested. The DPI stuff was hard
2048                             // and not easy to find out that it's needed.
2049                             // Since this will not yet happen normally (as long as no one constructs
2050                             // transparence primitives with non-trivial transparence content) i will for now not
2051                             // refine to tiling here.
2052 
2053                             basegfx::B2DRange aViewRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent, getViewInformation2D()));
2054                             aViewRange.transform(maCurrentTransformation);
2055                             const Rectangle aRectLogic(
2056                                 (sal_Int32)floor(aViewRange.getMinX()), (sal_Int32)floor(aViewRange.getMinY()),
2057                                 (sal_Int32)ceil(aViewRange.getMaxX()), (sal_Int32)ceil(aViewRange.getMaxY()));
2058                             const Rectangle aRectPixel(mpOutputDevice->LogicToPixel(aRectLogic));
2059                             Size aSizePixel(aRectPixel.GetSize());
2060                             const Point aEmptyPoint;
2061                             VirtualDevice aBufferDevice;
2062                             const sal_uInt32 nMaxQuadratPixels(500000);
2063                             const sal_uInt32 nViewVisibleArea(aSizePixel.getWidth() * aSizePixel.getHeight());
2064                             double fReduceFactor(1.0);
2065 
2066                             if(nViewVisibleArea > nMaxQuadratPixels)
2067                             {
2068                                 // reduce render size
2069                                 fReduceFactor = sqrt((double)nMaxQuadratPixels / (double)nViewVisibleArea);
2070                                 aSizePixel = Size(basegfx::fround((double)aSizePixel.getWidth() * fReduceFactor),
2071                                     basegfx::fround((double)aSizePixel.getHeight() * fReduceFactor));
2072                             }
2073 
2074                             if(aBufferDevice.SetOutputSizePixel(aSizePixel))
2075                             {
2076                                 // create and set MapModes for target devices
2077                                 MapMode aNewMapMode(mpOutputDevice->GetMapMode());
2078                                 aNewMapMode.SetOrigin(Point(-aRectLogic.Left(), -aRectLogic.Top()));
2079                                 aBufferDevice.SetMapMode(aNewMapMode);
2080 
2081                                 // prepare view transformation for target renderers
2082                                 // ATTENTION! Need to apply another scaling because of the potential DPI differences
2083                                 // between Printer and VDev (mpOutputDevice and aBufferDevice here).
2084                                 // To get the DPI, LogicToPixel from (1,1) from MAP_INCH needs to be used.
2085                                 basegfx::B2DHomMatrix aViewTransform(aBufferDevice.GetViewTransformation());
2086                                 const Size aDPIOld(mpOutputDevice->LogicToPixel(Size(1, 1), MAP_INCH));
2087                                 const Size aDPINew(aBufferDevice.LogicToPixel(Size(1, 1), MAP_INCH));
2088                                 const double fDPIXChange((double)aDPIOld.getWidth() / (double)aDPINew.getWidth());
2089                                 const double fDPIYChange((double)aDPIOld.getHeight() / (double)aDPINew.getHeight());
2090 
2091                                 if(!basegfx::fTools::equal(fDPIXChange, 1.0) || !basegfx::fTools::equal(fDPIYChange, 1.0))
2092                                 {
2093                                     aViewTransform.scale(fDPIXChange, fDPIYChange);
2094                                 }
2095 
2096                                 // also take scaling from Size reduction into acount
2097                                 if(!basegfx::fTools::equal(fReduceFactor, 1.0))
2098                                 {
2099                                     aViewTransform.scale(fReduceFactor, fReduceFactor);
2100                                 }
2101 
2102                                 // create view information and pixel renderer. Reuse known ViewInformation
2103                                 // except new transformation and range
2104                                 const geometry::ViewInformation2D aViewInfo(
2105                                     getViewInformation2D().getObjectTransformation(),
2106                                     aViewTransform,
2107                                     aViewRange,
2108                                     getViewInformation2D().getVisualizedPage(),
2109                                     getViewInformation2D().getViewTime(),
2110                                     getViewInformation2D().getExtendedInformationSequence());
2111 
2112                                 VclPixelProcessor2D aBufferProcessor(aViewInfo, aBufferDevice);
2113 
2114                                 // draw content using pixel renderer
2115                                 aBufferProcessor.process(rContent);
2116                                 const Bitmap aBmContent(aBufferDevice.GetBitmap(aEmptyPoint, aSizePixel));
2117 
2118                                 // draw transparence using pixel renderer
2119                                 aBufferDevice.Erase();
2120                                 aBufferProcessor.process(rTransparence);
2121                                 const AlphaMask aBmAlpha(aBufferDevice.GetBitmap(aEmptyPoint, aSizePixel));
2122 
2123 #ifdef DBG_UTIL
2124                                 static bool bDoSaveForVisualControl(false);
2125                                 if(bDoSaveForVisualControl)
2126                                 {
2127                                     SvFileStream aNew(String(ByteString( "c:\\test.bmp" ), RTL_TEXTENCODING_UTF8), STREAM_WRITE|STREAM_TRUNC);
2128 
2129                                     WriteDIB(aBmContent, aNew, false, true);
2130                                 }
2131 #endif
2132 
2133                                 // paint
2134                                 mpOutputDevice->DrawBitmapEx(
2135                                     aRectLogic.TopLeft(),
2136                                     aRectLogic.GetSize(),
2137                                     BitmapEx(aBmContent, aBmAlpha));
2138                             }
2139                         }
2140                     }
2141 
2142                     break;
2143                 }
2144                 case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D :
2145                 {
2146                     // use default transform group pocessing
2147                     RenderTransformPrimitive2D(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate));
2148                     break;
2149                 }
2150                 case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D :
2151                 {
2152                     // new XDrawPage for ViewInformation2D
2153                     RenderPagePreviewPrimitive2D(static_cast< const primitive2d::PagePreviewPrimitive2D& >(rCandidate));
2154                     break;
2155                 }
2156                 case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D :
2157                 {
2158                     // use default marker array pocessing
2159                     RenderMarkerArrayPrimitive2D(static_cast< const primitive2d::MarkerArrayPrimitive2D& >(rCandidate));
2160                     break;
2161                 }
2162                 case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D :
2163                 {
2164                     // use default point array pocessing
2165                     RenderPointArrayPrimitive2D(static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate));
2166                     break;
2167                 }
2168                 case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D :
2169                 {
2170                     // structured tag primitive
2171                     const primitive2d::StructureTagPrimitive2D& rStructureTagCandidate = static_cast< const primitive2d::StructureTagPrimitive2D& >(rCandidate);
2172                     const vcl::PDFWriter::StructElement& rTagElement(rStructureTagCandidate.getStructureElement());
2173                     const bool bTagUsed(vcl::PDFWriter::NonStructElement != rTagElement);
2174 
2175                     if(mpPDFExtOutDevData &&  bTagUsed)
2176                     {
2177                         // write start tag
2178                         mpPDFExtOutDevData->BeginStructureElement(rTagElement);
2179                     }
2180 
2181                     // process childs normally
2182                     process(rStructureTagCandidate.getChildren());
2183 
2184                     if(mpPDFExtOutDevData &&  bTagUsed)
2185                     {
2186                         // write end tag
2187                         mpPDFExtOutDevData->EndStructureElement();
2188                     }
2189 
2190                     break;
2191                 }
2192                 case PRIMITIVE2D_ID_EPSPRIMITIVE2D :
2193                 {
2194                     RenderEpsPrimitive2D(static_cast< const primitive2d::EpsPrimitive2D& >(rCandidate));
2195                     break;
2196                 }
2197                 default :
2198                 {
2199                     // process recursively
2200                     process(rCandidate.get2DDecomposition(getViewInformation2D()));
2201                     break;
2202                 }
2203             }
2204         }
2205     } // end of namespace processor2d
2206 } // end of namespace drawinglayer
2207 
2208 //////////////////////////////////////////////////////////////////////////////
2209 // eof
2210