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