1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <drawinglayer/processor2d/vclmetafileprocessor2d.hxx>
21 #include <tools/gen.hxx>
22 #include <vcl/virdev.hxx>
23 #include <vcl/gdimtf.hxx>
24 #include <vcl/gradient.hxx>
25 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
26 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
27 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
28 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
29 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
30 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
31 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
32 #include <basegfx/polygon/b2dpolygonclipper.hxx>
33 #include <basegfx/polygon/b2dpolypolygontools.hxx>
34 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
35 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
36 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
37 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
38 #include <drawinglayer/processor2d/vclpixelprocessor2d.hxx>
39 #include <tools/stream.hxx>
40 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
41 #include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
42 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
43 #include <vcl/graphictools.hxx>
44 #include <vcl/metaact.hxx>
45 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
46 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
47 #include <comphelper/processfactory.hxx>
48 #include <rtl/ustring.hxx>
49 #include <com/sun/star/i18n/BreakIterator.hpp>
50 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
51 #include <com/sun/star/i18n/WordType.hpp>
52 #include <drawinglayer/primitive2d/controlprimitive2d.hxx>
53 #include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
54 #include <basegfx/polygon/b2dpolygontools.hxx>
55 #include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
56 #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
57 #include <basegfx/polygon/b2dlinegeometry.hxx>
59 //////////////////////////////////////////////////////////////////////////////
60 // for PDFExtOutDevData Graphic support
62 #include <vcl/graph.hxx>
63 #include <vcl/svapp.hxx>
64 #include <toolkit/helper/formpdfexport.hxx>
66 //////////////////////////////////////////////////////////////////////////////
67 // for Control printing
69 #include <com/sun/star/beans/XPropertySet.hpp>
71 //////////////////////////////////////////////////////////////////////////////
72 // for StructureTagPrimitive support in sd's unomodel.cxx
74 #include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
76 //////////////////////////////////////////////////////////////////////////////
78 using namespace com::sun::star
;
80 //////////////////////////////////////////////////////////////////////////////
81 // #112245# definition for maximum allowed point count due to Metafile target.
82 // To be on the safe side with the old tools polygon, use slightly less then
83 // the theoretical maximum (bad experiences with tools polygon)
85 #define MAX_POLYGON_POINT_COUNT_METAFILE (0x0000fff0)
87 //////////////////////////////////////////////////////////////////////////////
91 // #112245# helper to split line polygon in half
92 void splitLinePolygon(
93 const basegfx::B2DPolygon
& rBasePolygon
,
94 basegfx::B2DPolygon
& o_aLeft
,
95 basegfx::B2DPolygon
& o_aRight
)
97 const sal_uInt32
nCount(rBasePolygon
.count());
101 const sal_uInt32
nHalfCount((nCount
- 1) >> 1);
103 o_aLeft
= basegfx::B2DPolygon(rBasePolygon
, 0, nHalfCount
+ 1);
104 o_aLeft
.setClosed(false);
106 o_aRight
= basegfx::B2DPolygon(rBasePolygon
, nHalfCount
, nCount
- nHalfCount
);
107 o_aRight
.setClosed(false);
109 if(rBasePolygon
.isClosed())
111 o_aRight
.append(rBasePolygon
.getB2DPoint(0));
113 if(rBasePolygon
.areControlPointsUsed())
115 o_aRight
.setControlPoints(
116 o_aRight
.count() - 1,
117 rBasePolygon
.getPrevControlPoint(0),
118 rBasePolygon
.getNextControlPoint(0));
129 // #112245# helper to evtl. split filled polygons to maximum metafile point count
130 bool fillPolyPolygonNeededToBeSplit(basegfx::B2DPolyPolygon
& rPolyPolygon
)
133 const sal_uInt32
nPolyCount(rPolyPolygon
.count());
137 basegfx::B2DPolyPolygon aSplitted
;
139 for(sal_uInt32
a(0); a
< nPolyCount
; a
++)
141 const basegfx::B2DPolygon
aCandidate(rPolyPolygon
.getB2DPolygon(a
));
142 const sal_uInt32
nPointCount(aCandidate
.count());
143 bool bNeedToSplit(false);
145 if(aCandidate
.areControlPointsUsed())
147 // compare with the maximum for bezier curved polygons
148 bNeedToSplit
= nPointCount
> ((MAX_POLYGON_POINT_COUNT_METAFILE
/ 3L) - 1L);
152 // compare with the maximum for simple point polygons
153 bNeedToSplit
= nPointCount
> (MAX_POLYGON_POINT_COUNT_METAFILE
- 1);
158 // need to split the partial polygon
159 const basegfx::B2DRange
aRange(aCandidate
.getB2DRange());
160 const basegfx::B2DPoint
aCenter(aRange
.getCenter());
162 if(aRange
.getWidth() > aRange
.getHeight())
164 // clip in left and right
165 const basegfx::B2DPolyPolygon
aLeft(
166 basegfx::tools::clipPolygonOnParallelAxis(
172 const basegfx::B2DPolyPolygon
aRight(
173 basegfx::tools::clipPolygonOnParallelAxis(
180 aSplitted
.append(aLeft
);
181 aSplitted
.append(aRight
);
185 // clip in top and bottom
186 const basegfx::B2DPolyPolygon
aTop(
187 basegfx::tools::clipPolygonOnParallelAxis(
193 const basegfx::B2DPolyPolygon
aBottom(
194 basegfx::tools::clipPolygonOnParallelAxis(
201 aSplitted
.append(aTop
);
202 aSplitted
.append(aBottom
);
207 aSplitted
.append(aCandidate
);
211 if(aSplitted
.count() != nPolyCount
)
213 rPolyPolygon
= aSplitted
;
220 /** Filter input polypolygon for effectively empty sub-fills
222 Needed to fix fdo#37559
225 PolyPolygon to filter
227 @return converted tools PolyPolygon, w/o one-point fills
229 PolyPolygon
getFillPolyPolygon( const ::basegfx::B2DPolyPolygon
& rPoly
)
231 // filter input rPoly
232 basegfx::B2DPolyPolygon aPoly
;
233 sal_uInt32
nCount(rPoly
.count());
234 for( sal_uInt32 i
=0; i
<nCount
; ++i
)
236 basegfx::B2DPolygon
aCandidate(rPoly
.getB2DPolygon(i
));
237 if( !aCandidate
.isClosed() || aCandidate
.count() > 1 )
238 aPoly
.append(aCandidate
);
240 return PolyPolygon(aPoly
);
243 } // end of anonymous namespace
245 //////////////////////////////////////////////////////////////////////////////
247 namespace drawinglayer
249 namespace processor2d
251 Rectangle
VclMetafileProcessor2D::impDumpToMetaFile(
252 const primitive2d::Primitive2DSequence
& rContent
,
253 GDIMetaFile
& o_rContentMetafile
)
255 // Prepare VDev, MetaFile and connections
256 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
257 GDIMetaFile
* pLastMetafile
= mpMetaFile
;
258 basegfx::B2DRange
aPrimitiveRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent
, getViewInformation2D()));
260 // transform primitive range with current transformation (e.g shadow offset)
261 aPrimitiveRange
.transform(maCurrentTransformation
);
263 const Rectangle
aPrimitiveRectangle(
264 basegfx::fround(aPrimitiveRange
.getMinX()), basegfx::fround(aPrimitiveRange
.getMinY()),
265 basegfx::fround(aPrimitiveRange
.getMaxX()), basegfx::fround(aPrimitiveRange
.getMaxY()));
266 VirtualDevice aContentVDev
;
267 MapMode
aNewMapMode(pLastOutputDevice
->GetMapMode());
269 mpOutputDevice
= &aContentVDev
;
270 mpMetaFile
= &o_rContentMetafile
;
271 aContentVDev
.EnableOutput(false);
272 aContentVDev
.SetMapMode(pLastOutputDevice
->GetMapMode());
273 o_rContentMetafile
.Record(&aContentVDev
);
274 aContentVDev
.SetLineColor(pLastOutputDevice
->GetLineColor());
275 aContentVDev
.SetFillColor(pLastOutputDevice
->GetFillColor());
276 aContentVDev
.SetFont(pLastOutputDevice
->GetFont());
277 aContentVDev
.SetDrawMode(pLastOutputDevice
->GetDrawMode());
278 aContentVDev
.SetSettings(pLastOutputDevice
->GetSettings());
279 aContentVDev
.SetRefPoint(pLastOutputDevice
->GetRefPoint());
285 o_rContentMetafile
.Stop();
286 o_rContentMetafile
.WindStart();
287 aNewMapMode
.SetOrigin(aPrimitiveRectangle
.TopLeft());
288 o_rContentMetafile
.SetPrefMapMode(aNewMapMode
);
289 o_rContentMetafile
.SetPrefSize(aPrimitiveRectangle
.GetSize());
290 mpOutputDevice
= pLastOutputDevice
;
291 mpMetaFile
= pLastMetafile
;
293 return aPrimitiveRectangle
;
296 void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient(
297 Gradient
& o_rVCLGradient
,
298 const attribute::FillGradientAttribute
& rFiGrAtt
,
299 bool bIsTransparenceGradient
)
301 if(bIsTransparenceGradient
)
303 // it's about transparence channel intensities (black/white), do not use color modifier
304 o_rVCLGradient
.SetStartColor(Color(rFiGrAtt
.getStartColor()));
305 o_rVCLGradient
.SetEndColor(Color(rFiGrAtt
.getEndColor()));
309 // use color modifier to influence start/end color of gradient
310 o_rVCLGradient
.SetStartColor(Color(maBColorModifierStack
.getModifiedColor(rFiGrAtt
.getStartColor())));
311 o_rVCLGradient
.SetEndColor(Color(maBColorModifierStack
.getModifiedColor(rFiGrAtt
.getEndColor())));
314 o_rVCLGradient
.SetAngle(static_cast< sal_uInt16
>(rFiGrAtt
.getAngle() * (1.0 / F_PI1800
)));
315 o_rVCLGradient
.SetBorder(static_cast< sal_uInt16
>(rFiGrAtt
.getBorder() * 100.0));
316 o_rVCLGradient
.SetOfsX(static_cast< sal_uInt16
>(rFiGrAtt
.getOffsetX() * 100.0));
317 o_rVCLGradient
.SetOfsY(static_cast< sal_uInt16
>(rFiGrAtt
.getOffsetY() * 100.0));
318 o_rVCLGradient
.SetSteps(rFiGrAtt
.getSteps());
320 // defaults for intensity; those were computed into the start/end colors already
321 o_rVCLGradient
.SetStartIntensity(100);
322 o_rVCLGradient
.SetEndIntensity(100);
324 switch(rFiGrAtt
.getStyle())
326 default : // attribute::GRADIENTSTYLE_LINEAR :
328 o_rVCLGradient
.SetStyle(GradientStyle_LINEAR
);
331 case attribute::GRADIENTSTYLE_AXIAL
:
333 o_rVCLGradient
.SetStyle(GradientStyle_AXIAL
);
336 case attribute::GRADIENTSTYLE_RADIAL
:
338 o_rVCLGradient
.SetStyle(GradientStyle_RADIAL
);
341 case attribute::GRADIENTSTYLE_ELLIPTICAL
:
343 o_rVCLGradient
.SetStyle(GradientStyle_ELLIPTICAL
);
346 case attribute::GRADIENTSTYLE_SQUARE
:
348 o_rVCLGradient
.SetStyle(GradientStyle_SQUARE
);
351 case attribute::GRADIENTSTYLE_RECT
:
353 o_rVCLGradient
.SetStyle(GradientStyle_RECT
);
359 void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill
* pSvtGraphicFill
)
361 if(pSvtGraphicFill
&& !mnSvtGraphicFillCount
)
363 SvMemoryStream aMemStm
;
365 aMemStm
<< *pSvtGraphicFill
;
366 mpMetaFile
->AddAction(new MetaCommentAction("XPATHFILL_SEQ_BEGIN", 0, static_cast< const sal_uInt8
* >(aMemStm
.GetData()), aMemStm
.Seek(STREAM_SEEK_TO_END
)));
367 mnSvtGraphicFillCount
++;
371 void VclMetafileProcessor2D::impEndSvtGraphicFill(SvtGraphicFill
* pSvtGraphicFill
)
373 if(pSvtGraphicFill
&& mnSvtGraphicFillCount
)
375 mnSvtGraphicFillCount
--;
376 mpMetaFile
->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END"));
377 delete pSvtGraphicFill
;
381 double VclMetafileProcessor2D::getTransformedLineWidth( double fWidth
) const
383 // #i113922# the LineWidth is duplicated in the MetaPolylineAction,
384 // and also inside the SvtGraphicStroke and needs transforming into
385 // the same space as its co-ordinates here cf. fdo#61789
386 // This is a partial fix. When a object transformation is used which
387 // e.g. contains a scaleX != scaleY, an unproportional scaling will happen.
388 const basegfx::B2DVector
aDiscreteUnit( maCurrentTransformation
* basegfx::B2DVector( fWidth
, 0.0 ) );
390 return aDiscreteUnit
.getLength();
393 SvtGraphicStroke
* VclMetafileProcessor2D::impTryToCreateSvtGraphicStroke(
394 const basegfx::B2DPolygon
& rB2DPolygon
,
395 const basegfx::BColor
* pColor
,
396 const attribute::LineAttribute
* pLineAttribute
,
397 const attribute::StrokeAttribute
* pStrokeAttribute
,
398 const attribute::LineStartEndAttribute
* pStart
,
399 const attribute::LineStartEndAttribute
* pEnd
)
401 SvtGraphicStroke
* pRetval
= 0;
403 if(rB2DPolygon
.count() && !mnSvtGraphicStrokeCount
)
405 basegfx::BColor aStrokeColor
;
406 basegfx::B2DPolyPolygon aStartArrow
;
407 basegfx::B2DPolyPolygon aEndArrow
;
411 aStrokeColor
= *pColor
;
413 else if(pLineAttribute
)
415 aStrokeColor
= maBColorModifierStack
.getModifiedColor(pLineAttribute
->getColor());
418 // It IS needed to record the stroke color at all in the metafile,
419 // SvtGraphicStroke has NO entry for stroke color(!)
420 mpOutputDevice
->SetLineColor(Color(aStrokeColor
));
422 if(!rB2DPolygon
.isClosed())
424 double fPolyLength(0.0);
426 if(pStart
&& pStart
->isActive())
428 fPolyLength
= basegfx::tools::getLength(rB2DPolygon
);
430 aStartArrow
= basegfx::tools::createAreaGeometryForLineStartEnd(
431 rB2DPolygon
, pStart
->getB2DPolyPolygon(), true, pStart
->getWidth(),
432 fPolyLength
, pStart
->isCentered() ? 0.5 : 0.0, 0);
435 if(pEnd
&& pEnd
->isActive())
437 if(basegfx::fTools::equalZero(fPolyLength
))
439 fPolyLength
= basegfx::tools::getLength(rB2DPolygon
);
442 aEndArrow
= basegfx::tools::createAreaGeometryForLineStartEnd(
443 rB2DPolygon
, pEnd
->getB2DPolyPolygon(), false, pEnd
->getWidth(),
444 fPolyLength
, pEnd
->isCentered() ? 0.5 : 0.0, 0);
448 SvtGraphicStroke::JoinType
eJoin(SvtGraphicStroke::joinNone
);
449 SvtGraphicStroke::CapType
eCap(SvtGraphicStroke::capButt
);
450 double fLineWidth(0.0);
451 double fMiterLength(0.0);
452 SvtGraphicStroke::DashArray aDashArray
;
456 fLineWidth
= fMiterLength
= getTransformedLineWidth( pLineAttribute
->getWidth() );
459 switch(pLineAttribute
->getLineJoin())
461 default : // basegfx::B2DLINEJOIN_NONE :
463 eJoin
= SvtGraphicStroke::joinNone
;
466 case basegfx::B2DLINEJOIN_BEVEL
:
468 eJoin
= SvtGraphicStroke::joinBevel
;
471 case basegfx::B2DLINEJOIN_MIDDLE
:
472 case basegfx::B2DLINEJOIN_MITER
:
474 eJoin
= SvtGraphicStroke::joinMiter
;
475 // ATM 15 degrees is assumed
476 fMiterLength
/= rtl::math::sin(M_PI
* (15.0 / 360.0));
479 case basegfx::B2DLINEJOIN_ROUND
:
481 eJoin
= SvtGraphicStroke::joinRound
;
487 switch(pLineAttribute
->getLineCap())
489 default: /* com::sun::star::drawing::LineCap_BUTT */
491 eCap
= SvtGraphicStroke::capButt
;
494 case com::sun::star::drawing::LineCap_ROUND
:
496 eCap
= SvtGraphicStroke::capRound
;
499 case com::sun::star::drawing::LineCap_SQUARE
:
501 eCap
= SvtGraphicStroke::capSquare
;
510 aDashArray
= pStrokeAttribute
->getDotDashArray();
513 // #i101734# apply current object transformation to created geometry.
514 // This is a partial fix. When a object transformation is used which
515 // e.g. contains a scaleX != scaleY, an unproportional scaling would
516 // have to be applied to the evtl. existing fat line. The current
517 // concept of PDF export and SvtGraphicStroke usage does simply not
518 // allow handling such definitions. The only clean way would be to
519 // add the transformation to SvtGraphicStroke and to handle it there
520 basegfx::B2DPolygon
aB2DPolygon(rB2DPolygon
);
522 aB2DPolygon
.transform(maCurrentTransformation
);
523 aStartArrow
.transform(maCurrentTransformation
);
524 aEndArrow
.transform(maCurrentTransformation
);
526 pRetval
= new SvtGraphicStroke(
527 Polygon(aB2DPolygon
),
528 PolyPolygon(aStartArrow
),
529 PolyPolygon(aEndArrow
),
530 mfCurrentUnifiedTransparence
,
541 void VclMetafileProcessor2D::impStartSvtGraphicStroke(SvtGraphicStroke
* pSvtGraphicStroke
)
543 if(pSvtGraphicStroke
&& !mnSvtGraphicStrokeCount
)
545 SvMemoryStream aMemStm
;
547 aMemStm
<< *pSvtGraphicStroke
;
548 mpMetaFile
->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_BEGIN", 0, static_cast< const sal_uInt8
* >(aMemStm
.GetData()), aMemStm
.Seek(STREAM_SEEK_TO_END
)));
549 mnSvtGraphicStrokeCount
++;
553 void VclMetafileProcessor2D::impEndSvtGraphicStroke(SvtGraphicStroke
* pSvtGraphicStroke
)
555 if(pSvtGraphicStroke
&& mnSvtGraphicStrokeCount
)
557 mnSvtGraphicStrokeCount
--;
558 mpMetaFile
->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END"));
559 delete pSvtGraphicStroke
;
563 // init static break iterator
564 uno::Reference
< ::com::sun::star::i18n::XBreakIterator
> VclMetafileProcessor2D::mxBreakIterator
;
566 VclMetafileProcessor2D::VclMetafileProcessor2D(const geometry::ViewInformation2D
& rViewInformation
, OutputDevice
& rOutDev
)
567 : VclProcessor2D(rViewInformation
, rOutDev
),
568 mpMetaFile(rOutDev
.GetConnectMetaFile()),
569 mnSvtGraphicFillCount(0),
570 mnSvtGraphicStrokeCount(0),
571 mfCurrentUnifiedTransparence(0.0),
572 mpPDFExtOutDevData(dynamic_cast< vcl::PDFExtOutDevData
* >(rOutDev
.GetExtOutDevData()))
574 OSL_ENSURE(rOutDev
.GetConnectMetaFile(), "VclMetafileProcessor2D: Used on OutDev which has no MetaFile Target (!)");
575 // draw to logic coordinates, do not initialize maCurrentTransformation to viewTransformation
576 // but only to ObjectTransformation. Do not change MapMode of destination.
577 maCurrentTransformation
= rViewInformation
.getObjectTransformation();
580 VclMetafileProcessor2D::~VclMetafileProcessor2D()
582 // MapMode was not changed, no restore necessary
585 /***********************************************************************************************
587 Support of MetaCommentActions in the VclMetafileProcessor2D
588 Found MetaCommentActions and how they are supported:
590 XGRAD_SEQ_BEGIN, XGRAD_SEQ_END:
592 Used inside OutputDevice::DrawGradient to mark the start and end of a MetaGradientEx action.
593 It is used in various exporters/importers to have direct access to the gradient before it
594 is rendered by VCL (and thus fragmented to polygon color actions and others). On that base, e.g.
595 the Metafile to SdrObject import creates it's gradient objects.
596 Best (and safest) way to support it here is to use PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D,
597 map it back to the corresponding tools PolyPolygon and the Gradient and just call
598 OutputDevice::DrawGradient which creates the necessary compatible actions.
600 XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END:
602 Two producers, one is vcl/source/gdi/gdimtf.cxx, line 1273. There, it is transformed
603 inside GDIMetaFile::Rotate, nothing to take care of here.
604 The second producer is in graphics/svx/source/svdraw/impgrfll.cxx, line 374. This is used
605 with each incarnation of Imp_GraphicFill when a metafile is recorded, fillstyle is not
606 XFILL_NONE and not completely transparent. It creates a SvtGraphicFill and streams it
607 to the comment action. A closing end token is created in the destructor.
608 Usages of Imp_GraphicFill are in Do_Paint_Object-methods of SdrCircObj, SdrPathObj and
610 The token users pick various actions from SvtGraphicFill, so it may need to be added for all kind
611 of filled objects, even simple colored polygons. It is added as extra information; the
612 Metafile actions between the two tokens are interpreted as output generated from those
613 fills. Thus, users have the choice to use the SvtGraphicFill info or the created output
615 Even for XFillTransparenceItem it is used, thus it may need to be supported in
616 UnifiedTransparencePrimitive2D, too, when interpreted as normally filled PolyPolygon.
618 PRIMITIVE2D_ID_POLYPOLYGONBITMAPPRIMITIVE2D,
619 PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D,
620 PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D,
621 PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D,
622 and for PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D when detected unified transparence
624 XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END:
626 Similar to pathfill, but using SvtGraphicStroke instead. It also has two producers where one
627 is also the GDIMetaFile::Rotate. Another user is MetaCommentAction::Move which modifies the
628 contained path accordingly.
629 The other one is SdrObject::Imp_DrawLineGeometry. It's done when MetaFile is set at OutDev and
630 only when geometry is a single polygon (!). I see no reason for that; in the PS exporter this
631 would hinder to make use of PolyPolygon strokes. I will need to add support at:
632 PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
633 PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
634 PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D
635 This can be done hierarchical, too.
636 Okay, base implementation done based on those three primitives.
638 FIELD_SEQ_BEGIN, FIELD_SEQ_END
640 Used from slideshow for URLs, created from diverse SvxField implementations inside
641 createBeginComment()/createEndComment(). createBeginComment() is used from editeng\impedit3.cxx
642 inside ImpEditEngine::Paint.
643 Created TextHierarchyFieldPrimitive2D and added needed infos there; it is an group primitive and wraps
644 text primitives (but is not limited to that). It contains the field type if special actions for the
645 support of FIELD_SEQ_BEGIN/END are needed; this is the case for Page and URL fields. If more is
646 needed, it may be supported there.
647 FIELD_SEQ_BEGIN;PageField
649 Okay, these are now completely supported by TextHierarchyFieldPrimitive2D. URL works, too.
653 XTEXT_EOC(i) end of character
654 XTEXT_EOW(i) end of word
655 XTEXT_EOS(i) end of sentence
657 this three are with index and are created with the help of a i18n::XBreakIterator in
658 ImplDrawWithComments. Simplifying, moving out text painting, reworking to create some
659 data structure for holding those TEXT infos.
660 Supported directly by TextSimplePortionPrimitive2D with adding a Locale to the basic text
661 primitive. In the MetaFileRenderer, the creation is now done (see below). This has the advantage
662 that this creations do not need to be done for all paints all the time. This would be
663 expensive since the BreakIterator and it's usage is expensive and for each paint also the
664 whole character stops would need to be created.
665 Created only for TextDecoratedPortionPrimitive2D due to XTEXT_EOL and XTEXT_EOP (see below)
667 XTEXT_EOL() end of line
668 XTEXT_EOP() end of paragraph
670 First try with boolean marks at TextDecoratedPortionPrimitive2D did not work too well,
671 i decided to solve it with structure. I added the TextHierarchyPrimitives for this,
673 - TextHierarchyLinePrimitive2D: Encapsulates single line
674 - TextHierarchyParagraphPrimitive2D: Encapsulates single paragraph
675 - TextHierarchyBlockPrimitive2D: encapsulates object texts (only one ATM)
676 Those are now supported in hierarchy. This means the MetaFile renderer will support them
677 by using them, reculrively using their content and adding MetaFile comments as needed.
678 This also means that when another text layouter will be used it will be necessary to
679 create/support the same HierarchyPrimitives to support users.
680 To transport the information using this hierarchy is best suited to all future needs;
681 the slideshow will be able to profit from it directly when using primitives; all other
682 renderers not interested in the text structure will just ignore the encapsulations.
684 XTEXT_PAINTSHAPE_BEGIN, XTEXT_PAINTSHAPE_END
685 Supported now by the TextHierarchyBlockPrimitive2D.
687 EPSReplacementGraphic:
688 Only used in goodies\source\filter.vcl\ieps\ieps.cxx and svx\source\xml\xmlgrhlp.cxx to
689 hold the original EPS which was imported in the same MetaFile as first 2 entries. Only
690 used to export the original again (if exists).
691 Not necessary to support with MetaFuleRenderer.
693 XTEXT_SCROLLRECT, XTEXT_PAINTRECT
694 Currently used to get extra MetaFile infos using GraphicExporter which again uses
695 SdrTextObj::GetTextScrollMetaFileAndRectangle(). ATM works with primitives since
696 the rectangle data is added directly by the GraphicsExporter as comment. Does not need
697 to be adapted at once.
698 When adapting later, the only user - the diashow - should directly use the provided
699 Anination infos in the appropriate primitives (e.g. AnimatedSwitchPrimitive2D)
701 PRNSPOOL_TRANSPARENTBITMAP_BEGIN, PRNSPOOL_TRANSPARENTBITMAP_END
702 VCL usage when printing PL -> THB. Okay, THB confirms that it is only used as
703 a fix (hack) while VCL printing. It is needed to not downscale a bitmap which
704 was explicitly created for the printer already again to some default maximum
706 Nothing to do here for the primitive renderer.
708 Support for vcl::PDFExtOutDevData:
709 PL knows that SJ did that stuff, it's used to hold a pointer to PDFExtOutDevData at
710 the OutDev. When set, some extra data is written there. Trying simple PDF export and
711 watching if i get those infos.
712 Well, a PDF export does not use e.g. ImpEditEngine::Paint since the PdfFilter uses
713 the SdXImpressDocument::render and thus uses the VclMetafileProcessor2D. I will check
714 if i get a PDFExtOutDevData at the target output device.
715 Indeed, i get one. Checking what all may be done when that extra-device-info is there.
717 All in all i have to talk to SJ. I will need to emulate some of those actions, but
718 i need to discuss which ones.
719 In the future, all those infos would be taken from the primitive sequence anyways,
720 thus these extensions would potentially be temporary, too.
721 Discussed with SJ, added the necessary support and tested it. Details follow.
723 - In ImpEditEngine::Paint, paragraph infos and URL stuff is added.
724 Added in primitive MetaFile renderer.
725 Checking URL: Indeed, current version exports it, but it is missing in primitive
726 CWS version. Adding support.
727 Okay, URLs work. Checked, Done.
729 - UnoControlPDFExportContact is only created when PDFExtOutDevData is used at the
730 target and uno control data is created in UnoControlPDFExportContact::do_PaintObject.
731 This may be added in primitive MetaFile renderer.
733 OOps, the necessary helper stuff is in svx/source/form/formpdxexport.cxx in namespace
734 svxform. Have to talk to FS if this has to be like that. Especially since
735 ::vcl::PDFWriter::AnyWidget is filled out, which is already part of vcl.
736 Wrote an eMail to FS, he is on vacation currently. I see no reason why not to move
737 that stuff to somewhere else, maybe tools or svtools ?!? We will see...
738 Moved to toolkit, so i have to link against it. I tried VCL first, but it did
739 not work since VCLUnoHelper::CreateFont is unresolved in VCL (!). Other then the name
740 may imply, it is defined in toolkit (!). Since toolkit is linked against VCL itself,
741 the lowest move,ment plave is toolkit.
742 Checked form control export, it works well. Done.
744 - In goodies, in GraphicObject::Draw, when the used Graphic is linked, infos are
745 generated. I will need to check what happens here with primitives.
746 To support, use of GraphicPrimitive2D (PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D) may be needed.
747 Added support, but feature is broken in main version, so i cannot test at all.
748 Writing a bug to CL (or SJ) and seeing what happens (#i80380#).
749 SJ took a look and we got it working. Tested VCL MetaFile Renderer based export,
750 as intended, the original file is exported. Works, Done.
757 - Maybe there are more places to take care of for vcl::PDFExtOutDevData!
761 ****************************************************************************************************/
763 void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D
& rCandidate
)
765 switch(rCandidate
.getPrimitive2DID())
767 case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D
:
769 // directdraw of wrong spell primitive
770 // Ignore for VclMetafileProcessor2D, this is for printing and MetaFile recording only
773 case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D
:
775 const primitive2d::GraphicPrimitive2D
& rGraphicPrimitive
= static_cast< const primitive2d::GraphicPrimitive2D
& >(rCandidate
);
776 bool bUsingPDFExtOutDevData(false);
777 basegfx::B2DVector aTranslate
, aScale
;
778 static bool bSuppressPDFExtOutDevDataSupport(false);
780 if(mpPDFExtOutDevData
&& !bSuppressPDFExtOutDevDataSupport
)
782 // emulate data handling from UnoControlPDFExportContact, original see
783 // svtools/source/graphic/grfmgr.cxx
784 const Graphic
& rGraphic
= rGraphicPrimitive
.getGraphicObject().GetGraphic();
786 if(rGraphic
.IsLink())
788 const GraphicAttr
& rAttr
= rGraphicPrimitive
.getGraphicAttr();
790 if(!rAttr
.IsSpecialDrawMode() && !rAttr
.IsAdjusted())
792 const basegfx::B2DHomMatrix
& rTransform
= rGraphicPrimitive
.getTransform();
793 double fRotate
, fShearX
;
794 rTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
796 if( basegfx::fTools::equalZero( fRotate
) && ( aScale
.getX() > 0.0 ) && ( aScale
.getY() > 0.0 ) )
798 bUsingPDFExtOutDevData
= true;
799 mpPDFExtOutDevData
->BeginGroup();
805 // process recursively and add MetaFile comment
806 process(rGraphicPrimitive
.get2DDecomposition(getViewInformation2D()));
808 if(bUsingPDFExtOutDevData
)
810 // emulate data handling from UnoControlPDFExportContact, original see
811 // svtools/source/graphic/grfmgr.cxx
812 const basegfx::B2DRange
aCurrentRange(
813 aTranslate
.getX(), aTranslate
.getY(),
814 aTranslate
.getX() + aScale
.getX(), aTranslate
.getY() + aScale
.getY());
815 const Rectangle
aCurrentRect(
816 sal_Int32(floor(aCurrentRange
.getMinX())), sal_Int32(floor(aCurrentRange
.getMinY())),
817 sal_Int32(ceil(aCurrentRange
.getMaxX())), sal_Int32(ceil(aCurrentRange
.getMaxY())));
818 const GraphicAttr
& rAttr
= rGraphicPrimitive
.getGraphicAttr();
821 if(rAttr
.IsCropped())
823 // calculate scalings between real image size and logic object size. This
824 // is necessary since the crop values are relative to original bitmap size
825 double fFactorX(1.0);
826 double fFactorY(1.0);
829 const MapMode
aMapMode100thmm(MAP_100TH_MM
);
830 const Size
aBitmapSize(Application::GetDefaultDevice()->LogicToLogic(
831 rGraphicPrimitive
.getGraphicObject().GetPrefSize(),
832 rGraphicPrimitive
.getGraphicObject().GetPrefMapMode(), aMapMode100thmm
));
833 const double fDivX(aBitmapSize
.Width() - rAttr
.GetLeftCrop() - rAttr
.GetRightCrop());
834 const double fDivY(aBitmapSize
.Height() - rAttr
.GetTopCrop() - rAttr
.GetBottomCrop());
836 if(!basegfx::fTools::equalZero(fDivX
))
838 fFactorX
= aScale
.getX() / fDivX
;
841 if(!basegfx::fTools::equalZero(fDivY
))
843 fFactorY
= aScale
.getY() / fDivY
;
847 // calculate crop range and rect
848 basegfx::B2DRange aCropRange
;
849 aCropRange
.expand(aCurrentRange
.getMinimum() - basegfx::B2DPoint(rAttr
.GetLeftCrop() * fFactorX
, rAttr
.GetTopCrop() * fFactorY
));
850 aCropRange
.expand(aCurrentRange
.getMaximum() + basegfx::B2DPoint(rAttr
.GetRightCrop() * fFactorX
, rAttr
.GetBottomCrop() * fFactorY
));
852 aCropRect
= Rectangle(
853 sal_Int32(floor(aCropRange
.getMinX())), sal_Int32(floor(aCropRange
.getMinY())),
854 sal_Int32(ceil(aCropRange
.getMaxX())), sal_Int32(ceil(aCropRange
.getMaxY())));
857 mpPDFExtOutDevData
->EndGroup(rGraphicPrimitive
.getGraphicObject().GetGraphic(),
858 rAttr
.GetTransparency(),
865 case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D
:
867 const primitive2d::ControlPrimitive2D
& rControlPrimitive
= static_cast< const primitive2d::ControlPrimitive2D
& >(rCandidate
);
868 const uno::Reference
< awt::XControl
>& rXControl(rControlPrimitive
.getXControl());
869 bool bIsPrintableControl(false);
871 // find out if control is printable
876 uno::Reference
< beans::XPropertySet
> xModelProperties(rXControl
->getModel(), uno::UNO_QUERY
);
877 uno::Reference
< beans::XPropertySetInfo
> xPropertyInfo(xModelProperties
.is()
878 ? xModelProperties
->getPropertySetInfo()
879 : uno::Reference
< beans::XPropertySetInfo
>());
880 const OUString
sPrintablePropertyName("Printable");
882 if(xPropertyInfo
.is() && xPropertyInfo
->hasPropertyByName(sPrintablePropertyName
))
884 OSL_VERIFY(xModelProperties
->getPropertyValue(sPrintablePropertyName
) >>= bIsPrintableControl
);
887 catch(const uno::Exception
&)
889 OSL_FAIL("VclMetafileProcessor2D: No access to printable flag of Control, caught an exception!");
893 // PDF export and printing only for printable controls
894 if(bIsPrintableControl
)
896 const bool bPDFExport(mpPDFExtOutDevData
&& mpPDFExtOutDevData
->GetIsExportFormFields());
897 bool bDoProcessRecursively(true);
901 // PDF export. Emulate data handling from UnoControlPDFExportContact
902 // I have now moved describePDFControl to toolkit, thus i can implement the PDF
903 // form control support now as follows
904 ::std::auto_ptr
< ::vcl::PDFWriter::AnyWidget
> pPDFControl
;
905 ::toolkitform::describePDFControl( rXControl
, pPDFControl
, *mpPDFExtOutDevData
);
907 if(pPDFControl
.get())
909 // still need to fill in the location (is a class Rectangle)
910 const basegfx::B2DRange
aRangeLogic(rControlPrimitive
.getB2DRange(getViewInformation2D()));
911 const Rectangle
aRectLogic(
912 (sal_Int32
)floor(aRangeLogic
.getMinX()), (sal_Int32
)floor(aRangeLogic
.getMinY()),
913 (sal_Int32
)ceil(aRangeLogic
.getMaxX()), (sal_Int32
)ceil(aRangeLogic
.getMaxY()));
914 pPDFControl
->Location
= aRectLogic
;
916 Size
aFontSize(pPDFControl
->TextFont
.GetSize());
917 aFontSize
= mpOutputDevice
->LogicToLogic(aFontSize
, MapMode(MAP_POINT
), mpOutputDevice
->GetMapMode());
918 pPDFControl
->TextFont
.SetSize(aFontSize
);
920 mpPDFExtOutDevData
->BeginStructureElement(vcl::PDFWriter::Form
);
921 mpPDFExtOutDevData
->CreateControl(*pPDFControl
.get());
922 mpPDFExtOutDevData
->EndStructureElement();
924 // no normal paint needed (see original UnoControlPDFExportContact::do_PaintObject);
925 // do not process recursively
926 bDoProcessRecursively
= false;
930 // PDF export did not work, try simple output.
931 // Fallback to printer output by not setting bDoProcessRecursively
936 // #i93169# used flag the wrong way; true means that nothing was done yet
937 if(bDoProcessRecursively
)
942 // remember old graphics and create new
943 uno::Reference
< awt::XView
> xControlView(rXControl
, uno::UNO_QUERY_THROW
);
944 const uno::Reference
< awt::XGraphics
> xOriginalGraphics(xControlView
->getGraphics());
945 const uno::Reference
< awt::XGraphics
> xNewGraphics(mpOutputDevice
->CreateUnoGraphics());
947 if(xNewGraphics
.is())
949 // link graphics and view
950 xControlView
->setGraphics(xNewGraphics
);
953 const basegfx::B2DHomMatrix
aObjectToDiscrete(getViewInformation2D().getObjectToViewTransformation() * rControlPrimitive
.getTransform());
954 const basegfx::B2DPoint
aTopLeftDiscrete(aObjectToDiscrete
* basegfx::B2DPoint(0.0, 0.0));
957 xControlView
->draw(basegfx::fround(aTopLeftDiscrete
.getX()), basegfx::fround(aTopLeftDiscrete
.getY()));
958 bDoProcessRecursively
= false;
960 // restore original graphics
961 xControlView
->setGraphics(xOriginalGraphics
);
964 catch( const uno::Exception
& )
966 OSL_FAIL("VclMetafileProcessor2D: Printing of Control failed, caught an exception!");
970 // process recursively if not done yet to export as decomposition (bitmap)
971 if(bDoProcessRecursively
)
973 process(rControlPrimitive
.get2DDecomposition(getViewInformation2D()));
979 case PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D
:
981 // support for FIELD_SEQ_BEGIN, FIELD_SEQ_END and URL. It wraps text primitives (but is not limited to)
982 // thus do the MetafileAction embedding stuff but just handle recursively.
983 const primitive2d::TextHierarchyFieldPrimitive2D
& rFieldPrimitive
= static_cast< const primitive2d::TextHierarchyFieldPrimitive2D
& >(rCandidate
);
984 const OString
aCommentStringCommon(RTL_CONSTASCII_STRINGPARAM("FIELD_SEQ_BEGIN"));
985 const OString
aCommentStringPage(RTL_CONSTASCII_STRINGPARAM("FIELD_SEQ_BEGIN;PageField"));
986 const OString
aCommentStringEnd(RTL_CONSTASCII_STRINGPARAM("FIELD_SEQ_END"));
988 switch(rFieldPrimitive
.getType())
990 default : // case drawinglayer::primitive2d::FIELD_TYPE_COMMON :
992 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringCommon
));
995 case drawinglayer::primitive2d::FIELD_TYPE_PAGE
:
997 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringPage
));
1000 case drawinglayer::primitive2d::FIELD_TYPE_URL
:
1002 const OUString
& rURL
= rFieldPrimitive
.getString();
1003 const String
aOldString(rURL
);
1004 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringCommon
, 0, reinterpret_cast< const sal_uInt8
* >(aOldString
.GetBuffer()), 2 * aOldString
.Len()));
1009 // process recursively
1010 const primitive2d::Primitive2DSequence rContent
= rFieldPrimitive
.get2DDecomposition(getViewInformation2D());
1013 // for the end comment the type is not relevant yet, they are all the same. Just add.
1014 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringEnd
));
1016 if(mpPDFExtOutDevData
&& drawinglayer::primitive2d::FIELD_TYPE_URL
== rFieldPrimitive
.getType())
1018 // emulate data handling from ImpEditEngine::Paint
1019 const basegfx::B2DRange
aViewRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent
, getViewInformation2D()));
1020 const Rectangle
aRectLogic(
1021 (sal_Int32
)floor(aViewRange
.getMinX()), (sal_Int32
)floor(aViewRange
.getMinY()),
1022 (sal_Int32
)ceil(aViewRange
.getMaxX()), (sal_Int32
)ceil(aViewRange
.getMaxY()));
1023 vcl::PDFExtOutDevBookmarkEntry aBookmark
;
1024 aBookmark
.nLinkId
= mpPDFExtOutDevData
->CreateLink(aRectLogic
);
1025 aBookmark
.aBookmark
= rFieldPrimitive
.getString();
1026 std::vector
< vcl::PDFExtOutDevBookmarkEntry
>& rBookmarks
= mpPDFExtOutDevData
->GetBookmarks();
1027 rBookmarks
.push_back( aBookmark
);
1032 case PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D
:
1034 const primitive2d::TextHierarchyLinePrimitive2D
& rLinePrimitive
= static_cast< const primitive2d::TextHierarchyLinePrimitive2D
& >(rCandidate
);
1035 const OString
aCommentString(RTL_CONSTASCII_STRINGPARAM("XTEXT_EOL"));
1037 // process recursively and add MetaFile comment
1038 process(rLinePrimitive
.get2DDecomposition(getViewInformation2D()));
1039 mpMetaFile
->AddAction(new MetaCommentAction(aCommentString
));
1043 case PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D
:
1045 // in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The
1046 // "XTEXT_EOC" is used, use here, too.
1047 const primitive2d::TextHierarchyBulletPrimitive2D
& rBulletPrimitive
= static_cast< const primitive2d::TextHierarchyBulletPrimitive2D
& >(rCandidate
);
1048 const OString
aCommentString(RTL_CONSTASCII_STRINGPARAM("XTEXT_EOC"));
1050 // process recursively and add MetaFile comment
1051 process(rBulletPrimitive
.get2DDecomposition(getViewInformation2D()));
1052 mpMetaFile
->AddAction(new MetaCommentAction(aCommentString
));
1056 case PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D
:
1058 const primitive2d::TextHierarchyParagraphPrimitive2D
& rParagraphPrimitive
= static_cast< const primitive2d::TextHierarchyParagraphPrimitive2D
& >(rCandidate
);
1059 const OString
aCommentString(RTL_CONSTASCII_STRINGPARAM("XTEXT_EOP"));
1061 if(mpPDFExtOutDevData
)
1063 // emulate data handling from ImpEditEngine::Paint
1064 mpPDFExtOutDevData
->BeginStructureElement( vcl::PDFWriter::Paragraph
);
1067 // process recursively and add MetaFile comment
1068 process(rParagraphPrimitive
.get2DDecomposition(getViewInformation2D()));
1069 mpMetaFile
->AddAction(new MetaCommentAction(aCommentString
));
1071 if(mpPDFExtOutDevData
)
1073 // emulate data handling from ImpEditEngine::Paint
1074 mpPDFExtOutDevData
->EndStructureElement();
1079 case PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D
:
1081 const primitive2d::TextHierarchyBlockPrimitive2D
& rBlockPrimitive
= static_cast< const primitive2d::TextHierarchyBlockPrimitive2D
& >(rCandidate
);
1082 const OString
aCommentStringA(RTL_CONSTASCII_STRINGPARAM("XTEXT_PAINTSHAPE_BEGIN"));
1083 const OString
aCommentStringB(RTL_CONSTASCII_STRINGPARAM("XTEXT_PAINTSHAPE_END"));
1085 // add MetaFile comment, process recursively and add MetaFile comment
1086 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringA
));
1087 process(rBlockPrimitive
.get2DDecomposition(getViewInformation2D()));
1088 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringB
));
1092 case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D
:
1093 case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D
:
1095 // for supporting TEXT_ MetaFile actions there is more to do here; get the candidate
1096 const primitive2d::TextSimplePortionPrimitive2D
& rTextCandidate
= static_cast< const primitive2d::TextSimplePortionPrimitive2D
& >(rCandidate
);
1097 // const primitive2d::TextDecoratedPortionPrimitive2D* pTextDecoratedCandidate = dynamic_cast< const primitive2d::TextDecoratedPortionPrimitive2D* >(&rCandidate);
1099 // Adapt evtl. used special DrawMode
1100 const sal_uInt32
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
1101 adaptTextToFillDrawMode();
1103 // directdraw of text simple portion; use default processing
1104 RenderTextSimpleOrDecoratedPortionPrimitive2D(rTextCandidate
);
1107 mpOutputDevice
->SetDrawMode(nOriginalDrawMode
);
1109 // #i101169# if(pTextDecoratedCandidate)
1111 // support for TEXT_ MetaFile actions only for decorated texts
1112 if(!mxBreakIterator
.is())
1114 uno::Reference
< uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
1115 mxBreakIterator
= i18n::BreakIterator::create(xContext
);
1118 const OUString
& rTxt
= rTextCandidate
.getText();
1119 const sal_Int32
nTextLength(rTextCandidate
.getTextLength()); // rTxt.getLength());
1123 const ::com::sun::star::lang::Locale
& rLocale
= rTextCandidate
.getLocale();
1124 const sal_Int32
nTextPosition(rTextCandidate
.getTextPosition());
1127 sal_Int32
nNextCellBreak(mxBreakIterator
->nextCharacters(rTxt
, nTextPosition
, rLocale
, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL
, 0, nDone
));
1128 ::com::sun::star::i18n::Boundary
nNextWordBoundary(mxBreakIterator
->getWordBoundary(rTxt
, nTextPosition
, rLocale
, ::com::sun::star::i18n::WordType::ANY_WORD
, sal_True
));
1129 sal_Int32
nNextSentenceBreak(mxBreakIterator
->endOfSentence(rTxt
, nTextPosition
, rLocale
));
1130 const OString
aCommentStringA(RTL_CONSTASCII_STRINGPARAM("XTEXT_EOC"));
1131 const OString
aCommentStringB(RTL_CONSTASCII_STRINGPARAM("XTEXT_EOW"));
1132 const OString
aCommentStringC(RTL_CONSTASCII_STRINGPARAM("XTEXT_EOS"));
1134 for(sal_Int32
i(nTextPosition
); i
< nTextPosition
+ nTextLength
; i
++)
1136 // create the entries for the respective break positions
1137 if(i
== nNextCellBreak
)
1139 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringA
, i
- nTextPosition
));
1140 nNextCellBreak
= mxBreakIterator
->nextCharacters(rTxt
, i
, rLocale
, ::com::sun::star::i18n::CharacterIteratorMode::SKIPCELL
, 1, nDone
);
1142 if(i
== nNextWordBoundary
.endPos
)
1144 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringB
, i
- nTextPosition
));
1145 nNextWordBoundary
= mxBreakIterator
->getWordBoundary(rTxt
, i
+ 1, rLocale
, ::com::sun::star::i18n::WordType::ANY_WORD
, sal_True
);
1147 if(i
== nNextSentenceBreak
)
1149 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringC
, i
- nTextPosition
));
1150 nNextSentenceBreak
= mxBreakIterator
->endOfSentence(rTxt
, i
+ 1, rLocale
);
1158 case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
:
1160 const primitive2d::PolygonHairlinePrimitive2D
& rHairlinePrimitive
= static_cast< const primitive2d::PolygonHairlinePrimitive2D
& >(rCandidate
);
1161 const basegfx::B2DPolygon
& rBasePolygon
= rHairlinePrimitive
.getB2DPolygon();
1163 if(rBasePolygon
.count() > (MAX_POLYGON_POINT_COUNT_METAFILE
- 1))
1165 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1166 // per polygon. If there are more, split the polygon in half and call recursively
1167 basegfx::B2DPolygon aLeft
, aRight
;
1168 splitLinePolygon(rBasePolygon
, aLeft
, aRight
);
1169 const primitive2d::PolygonHairlinePrimitive2D
aPLeft(aLeft
, rHairlinePrimitive
.getBColor());
1170 const primitive2d::PolygonHairlinePrimitive2D
aPRight(aRight
, rHairlinePrimitive
.getBColor());
1172 processBasePrimitive2D(aPLeft
);
1173 processBasePrimitive2D(aPRight
);
1177 // direct draw of hairline; use default processing
1178 // support SvtGraphicStroke MetaCommentAction
1179 const basegfx::BColor
aLineColor(maBColorModifierStack
.getModifiedColor(rHairlinePrimitive
.getBColor()));
1180 SvtGraphicStroke
* pSvtGraphicStroke
= impTryToCreateSvtGraphicStroke(
1181 rHairlinePrimitive
.getB2DPolygon(),
1185 impStartSvtGraphicStroke(pSvtGraphicStroke
);
1186 RenderPolygonHairlinePrimitive2D(static_cast< const primitive2d::PolygonHairlinePrimitive2D
& >(rCandidate
), false);
1187 impEndSvtGraphicStroke(pSvtGraphicStroke
);
1191 case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
:
1193 const primitive2d::PolygonStrokePrimitive2D
& rStrokePrimitive
= static_cast< const primitive2d::PolygonStrokePrimitive2D
& >(rCandidate
);
1194 const basegfx::B2DPolygon
& rBasePolygon
= rStrokePrimitive
.getB2DPolygon();
1196 if(rBasePolygon
.count() > (MAX_POLYGON_POINT_COUNT_METAFILE
- 1))
1198 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1199 // per polygon. If there are more, split the polygon in half and call recursively
1200 basegfx::B2DPolygon aLeft
, aRight
;
1201 splitLinePolygon(rBasePolygon
, aLeft
, aRight
);
1202 const primitive2d::PolygonStrokePrimitive2D
aPLeft(
1203 aLeft
, rStrokePrimitive
.getLineAttribute(), rStrokePrimitive
.getStrokeAttribute());
1204 const primitive2d::PolygonStrokePrimitive2D
aPRight(
1205 aRight
, rStrokePrimitive
.getLineAttribute(), rStrokePrimitive
.getStrokeAttribute());
1207 processBasePrimitive2D(aPLeft
);
1208 processBasePrimitive2D(aPRight
);
1212 // support SvtGraphicStroke MetaCommentAction
1213 SvtGraphicStroke
* pSvtGraphicStroke
= impTryToCreateSvtGraphicStroke(
1215 &rStrokePrimitive
.getLineAttribute(),
1216 &rStrokePrimitive
.getStrokeAttribute(),
1219 impStartSvtGraphicStroke(pSvtGraphicStroke
);
1220 const attribute::LineAttribute
& rLine
= rStrokePrimitive
.getLineAttribute();
1222 // create MetaPolyLineActions, but without LINE_DASH
1223 if(basegfx::fTools::more(rLine
.getWidth(), 0.0))
1225 const attribute::StrokeAttribute
& rStroke
= rStrokePrimitive
.getStrokeAttribute();
1226 basegfx::B2DPolyPolygon aHairLinePolyPolygon
;
1228 if(0.0 == rStroke
.getFullDotDashLen())
1230 aHairLinePolyPolygon
.append(rBasePolygon
);
1234 basegfx::tools::applyLineDashing(
1235 rBasePolygon
, rStroke
.getDotDashArray(),
1236 &aHairLinePolyPolygon
, 0, rStroke
.getFullDotDashLen());
1239 const basegfx::BColor
aHairlineColor(maBColorModifierStack
.getModifiedColor(rLine
.getColor()));
1240 mpOutputDevice
->SetLineColor(Color(aHairlineColor
));
1241 mpOutputDevice
->SetFillColor();
1242 aHairLinePolyPolygon
.transform(maCurrentTransformation
);
1244 // use the transformed line width
1245 LineInfo
aLineInfo(LINE_SOLID
, basegfx::fround(getTransformedLineWidth(rLine
.getWidth())));
1246 aLineInfo
.SetLineJoin(rLine
.getLineJoin());
1247 aLineInfo
.SetLineCap(rLine
.getLineCap());
1249 for(sal_uInt32
a(0); a
< aHairLinePolyPolygon
.count(); a
++)
1251 const basegfx::B2DPolygon
aCandidate(aHairLinePolyPolygon
.getB2DPolygon(a
));
1253 if(aCandidate
.count() > 1)
1255 const Polygon
aToolsPolygon(aCandidate
);
1257 mpMetaFile
->AddAction(new MetaPolyLineAction(aToolsPolygon
, aLineInfo
));
1263 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
1266 impEndSvtGraphicStroke(pSvtGraphicStroke
);
1271 case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D
:
1273 const primitive2d::PolygonStrokeArrowPrimitive2D
& rStrokeArrowPrimitive
= static_cast< const primitive2d::PolygonStrokeArrowPrimitive2D
& >(rCandidate
);
1274 const basegfx::B2DPolygon
& rBasePolygon
= rStrokeArrowPrimitive
.getB2DPolygon();
1276 if(rBasePolygon
.count() > (MAX_POLYGON_POINT_COUNT_METAFILE
- 1))
1278 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1279 // per polygon. If there are more, split the polygon in half and call recursively
1280 basegfx::B2DPolygon aLeft
, aRight
;
1281 splitLinePolygon(rBasePolygon
, aLeft
, aRight
);
1282 const attribute::LineStartEndAttribute aEmpty
;
1283 const primitive2d::PolygonStrokeArrowPrimitive2D
aPLeft(
1285 rStrokeArrowPrimitive
.getLineAttribute(),
1286 rStrokeArrowPrimitive
.getStrokeAttribute(),
1287 rStrokeArrowPrimitive
.getStart(),
1289 const primitive2d::PolygonStrokeArrowPrimitive2D
aPRight(
1291 rStrokeArrowPrimitive
.getLineAttribute(),
1292 rStrokeArrowPrimitive
.getStrokeAttribute(),
1294 rStrokeArrowPrimitive
.getEnd());
1296 processBasePrimitive2D(aPLeft
);
1297 processBasePrimitive2D(aPRight
);
1301 // support SvtGraphicStroke MetaCommentAction
1302 SvtGraphicStroke
* pSvtGraphicStroke
= impTryToCreateSvtGraphicStroke(
1304 &rStrokeArrowPrimitive
.getLineAttribute(),
1305 &rStrokeArrowPrimitive
.getStrokeAttribute(),
1306 &rStrokeArrowPrimitive
.getStart(),
1307 &rStrokeArrowPrimitive
.getEnd());
1309 // write LineGeometry start marker
1310 impStartSvtGraphicStroke(pSvtGraphicStroke
);
1312 // #i116162# When B&W is set as DrawMode, DRAWMODE_WHITEFILL is used
1313 // to let all fills be just white; for lines DRAWMODE_BLACKLINE is used
1314 // so all line geometry is supposed to get black. Since in the in-between
1315 // stages of line geometry drawing filled polygons are used (e.g. line
1316 // start/ends) it is necessary to change these drawmodes to preserve
1317 // that lines shall be black; thus change DRAWMODE_WHITEFILL to
1318 // DRAWMODE_BLACKFILL during line geometry processing to have line geometry
1319 // parts filled black.
1320 const sal_uLong
nOldDrawMode(mpOutputDevice
->GetDrawMode());
1321 const bool bDrawmodeChange(nOldDrawMode
& DRAWMODE_WHITEFILL
&& mnSvtGraphicStrokeCount
);
1325 mpOutputDevice
->SetDrawMode((nOldDrawMode
& ~DRAWMODE_WHITEFILL
) | DRAWMODE_BLACKFILL
);
1328 // process sub-line geometry (evtl. filled PolyPolygons)
1329 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
1333 mpOutputDevice
->SetDrawMode(nOldDrawMode
);
1336 // write LineGeometry end marker
1337 impEndSvtGraphicStroke(pSvtGraphicStroke
);
1342 case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D
:
1344 // direct draw of transformed BitmapEx primitive; use default processing
1345 RenderBitmapPrimitive2D(static_cast< const primitive2d::BitmapPrimitive2D
& >(rCandidate
));
1348 case PRIMITIVE2D_ID_POLYPOLYGONBITMAPPRIMITIVE2D
:
1350 // need to handle PolyPolygonBitmapPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
1351 const primitive2d::PolyPolygonBitmapPrimitive2D
& rBitmapCandidate
= static_cast< const primitive2d::PolyPolygonBitmapPrimitive2D
& >(rCandidate
);
1352 basegfx::B2DPolyPolygon
aLocalPolyPolygon(rBitmapCandidate
.getB2DPolyPolygon());
1354 if(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon
))
1356 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1357 // per polygon. If there are more use the splitted polygon and call recursively
1358 const primitive2d::PolyPolygonBitmapPrimitive2D
aSplitted(
1360 rBitmapCandidate
.getFillBitmap());
1362 processBasePrimitive2D(aSplitted
);
1366 SvtGraphicFill
* pSvtGraphicFill
= 0;
1368 if(!mnSvtGraphicFillCount
&& aLocalPolyPolygon
.count())
1370 aLocalPolyPolygon
.transform(maCurrentTransformation
);
1371 // calculate transformation. Get real object size, all values in FillBitmapAttribute
1372 // are relative to the unified object
1373 const attribute::FillBitmapAttribute
& rFillBitmapAttribute
= rBitmapCandidate
.getFillBitmap();
1374 const basegfx::B2DRange
aOutlineRange(basegfx::tools::getRange(aLocalPolyPolygon
));
1375 const basegfx::B2DVector
aOutlineSize(aOutlineRange
.getRange());
1377 // get absolute values
1378 const basegfx::B2DVector
aFillBitmapSize(rFillBitmapAttribute
.getSize() * aOutlineSize
);
1379 const basegfx::B2DPoint
aFillBitmapTopLeft(rFillBitmapAttribute
.getTopLeft() * aOutlineSize
);
1381 // the scaling needs scale from pixel to logic coordinate system
1382 const BitmapEx
& rBitmapEx
= rFillBitmapAttribute
.getBitmapEx();
1383 Size
aBmpSizePixel(rBitmapEx
.GetSizePixel());
1385 if(!aBmpSizePixel
.Width())
1387 aBmpSizePixel
.Width() = 1;
1390 if(!aBmpSizePixel
.Height())
1392 aBmpSizePixel
.Height() = 1;
1395 // setup transformation like in impgrfll
1396 SvtGraphicFill::Transform aTransform
;
1398 // scale values are divided by bitmap pixel sizes
1399 aTransform
.matrix
[0] = aFillBitmapSize
.getX() / aBmpSizePixel
.Width();
1400 aTransform
.matrix
[4] = aFillBitmapSize
.getY() / aBmpSizePixel
.Height();
1402 // translates are absolute
1403 aTransform
.matrix
[2] = aFillBitmapTopLeft
.getX();
1404 aTransform
.matrix
[5] = aFillBitmapTopLeft
.getY();
1406 // setup fill graphic like in impgrfll
1407 Graphic aFillGraphic
= Graphic(rBitmapEx
);
1408 aFillGraphic
.SetPrefMapMode(MapMode(MAP_PIXEL
));
1409 aFillGraphic
.SetPrefSize(aBmpSizePixel
);
1411 pSvtGraphicFill
= new SvtGraphicFill(
1412 getFillPolyPolygon(aLocalPolyPolygon
),
1415 SvtGraphicFill::fillEvenOdd
,
1416 SvtGraphicFill::fillTexture
,
1418 rFillBitmapAttribute
.getTiling(),
1419 SvtGraphicFill::hatchSingle
,
1421 SvtGraphicFill::gradientLinear
,
1428 // Do use decomposition; encapsulate with SvtGraphicFill
1429 impStartSvtGraphicFill(pSvtGraphicFill
);
1430 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
1431 impEndSvtGraphicFill(pSvtGraphicFill
);
1436 case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D
:
1438 // need to handle PolyPolygonHatchPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
1439 const primitive2d::PolyPolygonHatchPrimitive2D
& rHatchCandidate
= static_cast< const primitive2d::PolyPolygonHatchPrimitive2D
& >(rCandidate
);
1440 const attribute::FillHatchAttribute
& rFillHatchAttribute
= rHatchCandidate
.getFillHatch();
1441 basegfx::B2DPolyPolygon
aLocalPolyPolygon(rHatchCandidate
.getB2DPolyPolygon());
1443 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1444 // per polygon. Split polygon until there are less than that
1445 while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon
))
1448 if(rFillHatchAttribute
.isFillBackground())
1450 // with fixing #i111954# (see below) the possible background
1451 // fill of a hatched object was lost.Generate a background fill
1452 // primitive and render it
1453 const primitive2d::Primitive2DReference
xBackground(
1454 new primitive2d::PolyPolygonColorPrimitive2D(
1456 rHatchCandidate
.getBackgroundColor()));
1458 process(primitive2d::Primitive2DSequence(&xBackground
, 1));
1461 SvtGraphicFill
* pSvtGraphicFill
= 0;
1462 aLocalPolyPolygon
.transform(maCurrentTransformation
);
1464 if(!mnSvtGraphicFillCount
&& aLocalPolyPolygon
.count())
1466 // re-create a VCL hatch as base data
1467 SvtGraphicFill::HatchType
eHatch(SvtGraphicFill::hatchSingle
);
1469 switch(rFillHatchAttribute
.getStyle())
1471 default: // attribute::HATCHSTYLE_SINGLE :
1473 eHatch
= SvtGraphicFill::hatchSingle
;
1476 case attribute::HATCHSTYLE_DOUBLE
:
1478 eHatch
= SvtGraphicFill::hatchDouble
;
1481 case attribute::HATCHSTYLE_TRIPLE
:
1483 eHatch
= SvtGraphicFill::hatchTriple
;
1488 SvtGraphicFill::Transform aTransform
;
1491 aTransform
.matrix
[0] *= rFillHatchAttribute
.getDistance();
1492 aTransform
.matrix
[4] *= rFillHatchAttribute
.getDistance();
1494 // rotate (was never correct in impgrfll anyways, use correct angle now)
1495 aTransform
.matrix
[0] *= cos(rFillHatchAttribute
.getAngle());
1496 aTransform
.matrix
[1] *= -sin(rFillHatchAttribute
.getAngle());
1497 aTransform
.matrix
[3] *= sin(rFillHatchAttribute
.getAngle());
1498 aTransform
.matrix
[4] *= cos(rFillHatchAttribute
.getAngle());
1500 pSvtGraphicFill
= new SvtGraphicFill(
1501 getFillPolyPolygon(aLocalPolyPolygon
),
1504 SvtGraphicFill::fillEvenOdd
,
1505 SvtGraphicFill::fillHatch
,
1509 Color(rFillHatchAttribute
.getColor()),
1510 SvtGraphicFill::gradientLinear
,
1517 // Do use decomposition; encapsulate with SvtGraphicFill
1518 impStartSvtGraphicFill(pSvtGraphicFill
);
1520 // #i111954# do NOT use decomposition, but use direct VCL-command
1521 // process(rCandidate.get2DDecomposition(getViewInformation2D()));
1522 const PolyPolygon
aToolsPolyPolygon(basegfx::tools::adaptiveSubdivideByAngle(aLocalPolyPolygon
));
1523 const HatchStyle
aHatchStyle(
1524 attribute::HATCHSTYLE_SINGLE
== rFillHatchAttribute
.getStyle() ? HATCH_SINGLE
:
1525 attribute::HATCHSTYLE_DOUBLE
== rFillHatchAttribute
.getStyle() ? HATCH_DOUBLE
:
1528 mpOutputDevice
->DrawHatch(aToolsPolyPolygon
,
1530 Color(rFillHatchAttribute
.getColor()),
1531 basegfx::fround(rFillHatchAttribute
.getDistance()),
1532 basegfx::fround(rFillHatchAttribute
.getAngle() / F_PI1800
)));
1534 impEndSvtGraphicFill(pSvtGraphicFill
);
1538 case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D
:
1540 basegfx::B2DVector aScale
, aTranslate
;
1541 double fRotate
, fShearX
;
1543 maCurrentTransformation
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
1545 if(!basegfx::fTools::equalZero(fRotate
) || !basegfx::fTools::equalZero(fShearX
))
1547 // #i121185# When rotation or shear is used, a VCL Gradient cannot be used directly.
1548 // This is because VCL Gradient mechanism does *not* support to rotate the gradient
1549 // with objects and this case is not expressable in a Metafile (and cannot be added
1550 // since the FileFormats used, e.g. *.wmf, do not support it either).
1551 // Such cases happen when a graphic object uses a Metafile as graphic information or
1552 // a fill style definition uses a Metafile. In this cases the graphic content is
1553 // rotated with the graphic or filled object; this is not supported by the target
1554 // format of this conversion renderer - Metafiles.
1555 // To solve this, not a Gradient is written, but the decomposition of this object
1556 // is written to the Metafile. This is the PolyPolygons building the gradient fill.
1557 // These will need more space and time, but the result will be as if the Gradient
1558 // was rotated with the object.
1559 // This mechanism is used by all exporters still not using Primtives (e.g. Print,
1560 // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile
1561 // transfers. One more reason to *change* these to primitives.
1562 // BTW: One more example how useful the principles of primitives are; the decomposition
1563 // is by definition a simpler, maybe more expensive representation of the same content.
1564 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
1568 const primitive2d::PolyPolygonGradientPrimitive2D
& rGradientCandidate
= static_cast< const primitive2d::PolyPolygonGradientPrimitive2D
& >(rCandidate
);
1569 basegfx::B2DPolyPolygon
aLocalPolyPolygon(rGradientCandidate
.getB2DPolyPolygon());
1571 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1572 // per polygon. Split polygon until there are less than that
1573 while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon
))
1576 // for support of MetaCommentActions of the form XGRAD_SEQ_BEGIN, XGRAD_SEQ_END
1577 // it is safest to use the VCL OutputDevice::DrawGradient method which creates those.
1578 // re-create a VCL-gradient from FillGradientPrimitive2D and the needed tools PolyPolygon
1579 Gradient aVCLGradient
;
1580 impConvertFillGradientAttributeToVCLGradient(aVCLGradient
, rGradientCandidate
.getFillGradient(), false);
1581 aLocalPolyPolygon
.transform(maCurrentTransformation
);
1583 // #i82145# ATM VCL printing of gradients using curved shapes does not work,
1584 // i submitted the bug with the given ID to THB. When that task is fixed it is
1585 // necessary to again remove this subdivision since it decreases possible
1586 // printing quality (not even resolution-dependent for now). THB will tell
1587 // me when that task is fixed in the master
1588 const PolyPolygon
aToolsPolyPolygon(
1590 basegfx::tools::adaptiveSubdivideByAngle(aLocalPolyPolygon
)));
1593 // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1594 SvtGraphicFill
* pSvtGraphicFill
= 0;
1596 if(!mnSvtGraphicFillCount
&& aLocalPolyPolygon
.count())
1598 // setup gradient stuff like in like in impgrfll
1599 SvtGraphicFill::GradientType
eGrad(SvtGraphicFill::gradientLinear
);
1601 switch(aVCLGradient
.GetStyle())
1603 default : // GradientStyle_LINEAR:
1604 case GradientStyle_AXIAL
:
1605 eGrad
= SvtGraphicFill::gradientLinear
;
1607 case GradientStyle_RADIAL
:
1608 case GradientStyle_ELLIPTICAL
:
1609 eGrad
= SvtGraphicFill::gradientRadial
;
1611 case GradientStyle_SQUARE
:
1612 case GradientStyle_RECT
:
1613 eGrad
= SvtGraphicFill::gradientRectangular
;
1617 pSvtGraphicFill
= new SvtGraphicFill(
1621 SvtGraphicFill::fillEvenOdd
,
1622 SvtGraphicFill::fillGradient
,
1623 SvtGraphicFill::Transform(),
1625 SvtGraphicFill::hatchSingle
,
1628 aVCLGradient
.GetStartColor(),
1629 aVCLGradient
.GetEndColor(),
1630 aVCLGradient
.GetSteps(),
1634 // call VCL directly; encapsulate with SvtGraphicFill
1635 impStartSvtGraphicFill(pSvtGraphicFill
);
1636 mpOutputDevice
->DrawGradient(aToolsPolyPolygon
, aVCLGradient
);
1637 impEndSvtGraphicFill(pSvtGraphicFill
);
1639 // NO usage of common own gradient randerer, not used ATM for VCL MetaFile, see text above
1640 // RenderPolyPolygonGradientPrimitive2D(static_cast< const primitive2d::PolyPolygonGradientPrimitive2D& >(rCandidate));
1645 case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D
:
1647 const primitive2d::PolyPolygonColorPrimitive2D
& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D
& >(rCandidate
));
1648 basegfx::B2DPolyPolygon
aLocalPolyPolygon(rPolygonCandidate
.getB2DPolyPolygon());
1650 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1651 // per polygon. Split polygon until there are less than that
1652 while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon
))
1655 const basegfx::BColor
aPolygonColor(maBColorModifierStack
.getModifiedColor(rPolygonCandidate
.getBColor()));
1656 aLocalPolyPolygon
.transform(maCurrentTransformation
);
1658 // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1659 SvtGraphicFill
* pSvtGraphicFill
= 0;
1661 if(!mnSvtGraphicFillCount
&& aLocalPolyPolygon
.count())
1663 // setup simple color fill stuff like in impgrfll
1664 pSvtGraphicFill
= new SvtGraphicFill(
1665 getFillPolyPolygon(aLocalPolyPolygon
),
1666 Color(aPolygonColor
),
1668 SvtGraphicFill::fillEvenOdd
,
1669 SvtGraphicFill::fillSolid
,
1670 SvtGraphicFill::Transform(),
1672 SvtGraphicFill::hatchSingle
,
1674 SvtGraphicFill::gradientLinear
,
1681 // set line and fill color
1682 mpOutputDevice
->SetFillColor(Color(aPolygonColor
));
1683 mpOutputDevice
->SetLineColor();
1685 // call VCL directly; encapsulate with SvtGraphicFill
1686 impStartSvtGraphicFill(pSvtGraphicFill
);
1687 mpOutputDevice
->DrawPolyPolygon(aLocalPolyPolygon
);
1688 impEndSvtGraphicFill(pSvtGraphicFill
);
1692 case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D
:
1694 static bool bUseMetaFilePrimitiveDecomposition(true);
1695 const primitive2d::MetafilePrimitive2D
& aMetafile
= static_cast< const primitive2d::MetafilePrimitive2D
& >(rCandidate
);
1697 if(bUseMetaFilePrimitiveDecomposition
&& !aMetafile
.getMetaFile().GetUseCanvas())
1699 // use new Metafile decomposition
1700 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
1704 // direct draw of MetaFile, use default pocessing
1705 RenderMetafilePrimitive2D(aMetafile
);
1710 case PRIMITIVE2D_ID_MASKPRIMITIVE2D
:
1712 // mask group. Special handling for MetaFiles.
1713 const primitive2d::MaskPrimitive2D
& rMaskCandidate
= static_cast< const primitive2d::MaskPrimitive2D
& >(rCandidate
);
1715 if(rMaskCandidate
.getChildren().hasElements())
1717 basegfx::B2DPolyPolygon
aMask(rMaskCandidate
.getMask());
1721 // prepare new mask polygon and rescue current one
1722 aMask
.transform(maCurrentTransformation
);
1723 const basegfx::B2DPolyPolygon
aLastClipPolyPolygon(maClipPolyPolygon
);
1725 if(maClipPolyPolygon
.count())
1727 // there is already a clip polygon set; build clipped union of
1728 // current mask polygon and new one
1729 maClipPolyPolygon
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
1732 true, // #i106516# we want the inside of aMask, not the outside
1737 // use mask directly
1738 maClipPolyPolygon
= aMask
;
1741 if(maClipPolyPolygon
.count())
1743 // set VCL clip region; subdivide before conversion to tools polygon. Subdivision necessary (!)
1744 // Removed subdivision and fixed in Region::ImplPolyPolyRegionToBandRegionFunc() in VCL where
1745 // the ClipRegion is built from the Polygon. A AdaptiveSubdivide on the source polygon was missing there
1746 mpOutputDevice
->Push(PUSH_CLIPREGION
);
1747 //mpOutputDevice->SetClipRegion(Region(PolyPolygon(basegfx::tools::adaptiveSubdivideByAngle(maClipPolyPolygon))));
1748 //mpOutputDevice->SetClipRegion(Region(PolyPolygon(maClipPolyPolygon)));
1749 mpOutputDevice
->SetClipRegion(Region(maClipPolyPolygon
));
1752 // recursively paint content
1753 process(rMaskCandidate
.getChildren());
1755 if(maClipPolyPolygon
.count())
1757 // restore VCL clip region
1758 mpOutputDevice
->Pop();
1761 // restore to rescued clip polygon
1762 maClipPolyPolygon
= aLastClipPolyPolygon
;
1766 // no mask, no clipping. recursively paint content
1767 process(rMaskCandidate
.getChildren());
1773 case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D
:
1775 // modified color group. Force output to unified color. Use default pocessing.
1776 RenderModifiedColorPrimitive2D(static_cast< const primitive2d::ModifiedColorPrimitive2D
& >(rCandidate
));
1779 case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D
:
1781 // for metafile: Need to examine what the pure vcl version is doing here actually
1782 // - uses DrawTransparent with metafile for content and a gradient
1783 // - uses DrawTransparent for single PolyPoylgons directly. Can be detected by
1784 // checking the content for single PolyPolygonColorPrimitive2D
1785 const primitive2d::UnifiedTransparencePrimitive2D
& rUniTransparenceCandidate
= static_cast< const primitive2d::UnifiedTransparencePrimitive2D
& >(rCandidate
);
1786 const primitive2d::Primitive2DSequence rContent
= rUniTransparenceCandidate
.getChildren();
1788 if(rContent
.hasElements())
1790 if(0.0 == rUniTransparenceCandidate
.getTransparence())
1792 // not transparent at all, use content
1793 process(rUniTransparenceCandidate
.getChildren());
1795 else if(rUniTransparenceCandidate
.getTransparence() > 0.0 && rUniTransparenceCandidate
.getTransparence() < 1.0)
1797 // try to identify a single PolyPolygonColorPrimitive2D in the
1798 // content part of the transparence primitive
1799 const primitive2d::PolyPolygonColorPrimitive2D
* pPoPoColor
= 0;
1800 static bool bForceToMetafile(false);
1802 if(!bForceToMetafile
&& 1 == rContent
.getLength())
1804 const primitive2d::Primitive2DReference
xReference(rContent
[0]);
1805 pPoPoColor
= dynamic_cast< const primitive2d::PolyPolygonColorPrimitive2D
* >(xReference
.get());
1808 // PolyPolygonGradientPrimitive2D, PolyPolygonHatchPrimitive2D and
1809 // PolyPolygonBitmapPrimitive2D are derived from PolyPolygonColorPrimitive2D.
1810 // Check also for correct ID to exclude derived implementations
1811 if(pPoPoColor
&& PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D
== pPoPoColor
->getPrimitive2DID())
1813 // single transparent PolyPolygon identified, use directly
1814 const basegfx::BColor
aPolygonColor(maBColorModifierStack
.getModifiedColor(pPoPoColor
->getBColor()));
1815 basegfx::B2DPolyPolygon
aLocalPolyPolygon(pPoPoColor
->getB2DPolyPolygon());
1817 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1818 // per polygon. Split polygon until there are less than that
1819 while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon
))
1823 aLocalPolyPolygon
.transform(maCurrentTransformation
);
1825 // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1826 SvtGraphicFill
* pSvtGraphicFill
= 0;
1828 if(!mnSvtGraphicFillCount
&& aLocalPolyPolygon
.count())
1830 // setup simple color with transparence fill stuff like in impgrfll
1831 pSvtGraphicFill
= new SvtGraphicFill(
1832 getFillPolyPolygon(aLocalPolyPolygon
),
1833 Color(aPolygonColor
),
1834 rUniTransparenceCandidate
.getTransparence(),
1835 SvtGraphicFill::fillEvenOdd
,
1836 SvtGraphicFill::fillSolid
,
1837 SvtGraphicFill::Transform(),
1839 SvtGraphicFill::hatchSingle
,
1841 SvtGraphicFill::gradientLinear
,
1848 // set line and fill color
1849 const sal_uInt16
nTransPercentVcl((sal_uInt16
)basegfx::fround(rUniTransparenceCandidate
.getTransparence() * 100.0));
1850 mpOutputDevice
->SetFillColor(Color(aPolygonColor
));
1851 mpOutputDevice
->SetLineColor();
1853 // call VCL directly; encapsulate with SvtGraphicFill
1854 impStartSvtGraphicFill(pSvtGraphicFill
);
1855 mpOutputDevice
->DrawTransparent(
1856 PolyPolygon(aLocalPolyPolygon
),
1858 impEndSvtGraphicFill(pSvtGraphicFill
);
1862 // svae old mfCurrentUnifiedTransparence and set new one
1863 // so that contained SvtGraphicStroke may use the current one
1864 const double fLastCurrentUnifiedTransparence(mfCurrentUnifiedTransparence
);
1865 // #i105377# paint the content metafile opaque as the transparency gets
1866 // split of into the gradient below
1867 // mfCurrentUnifiedTransparence = rUniTransparenceCandidate.getTransparence();
1868 mfCurrentUnifiedTransparence
= 0;
1870 // various content, create content-metafile
1871 GDIMetaFile aContentMetafile
;
1872 const Rectangle
aPrimitiveRectangle(impDumpToMetaFile(rContent
, aContentMetafile
));
1874 // restore mfCurrentUnifiedTransparence; it may have been used
1875 // while processing the sub-content in impDumpToMetaFile
1876 mfCurrentUnifiedTransparence
= fLastCurrentUnifiedTransparence
;
1878 // create uniform VCL gradient for uniform transparency
1879 Gradient aVCLGradient
;
1880 const sal_uInt8
nTransPercentVcl((sal_uInt8
)basegfx::fround(rUniTransparenceCandidate
.getTransparence() * 255.0));
1881 const Color
aTransColor(nTransPercentVcl
, nTransPercentVcl
, nTransPercentVcl
);
1883 aVCLGradient
.SetStyle(GradientStyle_LINEAR
);
1884 aVCLGradient
.SetStartColor(aTransColor
);
1885 aVCLGradient
.SetEndColor(aTransColor
);
1886 aVCLGradient
.SetAngle(0);
1887 aVCLGradient
.SetBorder(0);
1888 aVCLGradient
.SetOfsX(0);
1889 aVCLGradient
.SetOfsY(0);
1890 aVCLGradient
.SetStartIntensity(100);
1891 aVCLGradient
.SetEndIntensity(100);
1892 aVCLGradient
.SetSteps(2);
1895 mpOutputDevice
->DrawTransparent(
1896 aContentMetafile
, aPrimitiveRectangle
.TopLeft(),
1897 aPrimitiveRectangle
.GetSize(), aVCLGradient
);
1904 case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D
:
1906 // for metafile: Need to examine what the pure vcl version is doing here actually
1907 // - uses DrawTransparent with metafile for content and a gradient
1908 // i can detect this here with checking the gradient part for a single
1909 // FillGradientPrimitive2D and reconstruct the gradient.
1910 // If that detection goes wrong, i have to create an transparence-blended bitmap. Eventually
1911 // do that in stripes, else RenderTransparencePrimitive2D may just be used
1912 const primitive2d::TransparencePrimitive2D
& rTransparenceCandidate
= static_cast< const primitive2d::TransparencePrimitive2D
& >(rCandidate
);
1913 const primitive2d::Primitive2DSequence rContent
= rTransparenceCandidate
.getChildren();
1914 const primitive2d::Primitive2DSequence rTransparence
= rTransparenceCandidate
.getTransparence();
1916 if(rContent
.hasElements() && rTransparence
.hasElements())
1918 // try to identify a single FillGradientPrimitive2D in the
1919 // transparence part of the primitive
1920 const primitive2d::FillGradientPrimitive2D
* pFiGradient
= 0;
1921 static bool bForceToBigTransparentVDev(false);
1923 if(!bForceToBigTransparentVDev
&& 1 == rTransparence
.getLength())
1925 const primitive2d::Primitive2DReference
xReference(rTransparence
[0]);
1926 pFiGradient
= dynamic_cast< const primitive2d::FillGradientPrimitive2D
* >(xReference
.get());
1929 // Check also for correct ID to exclude derived implementations
1930 if(pFiGradient
&& PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D
== pFiGradient
->getPrimitive2DID())
1932 // various content, create content-metafile
1933 GDIMetaFile aContentMetafile
;
1934 const Rectangle
aPrimitiveRectangle(impDumpToMetaFile(rContent
, aContentMetafile
));
1936 // re-create a VCL-gradient from FillGradientPrimitive2D
1937 Gradient aVCLGradient
;
1938 impConvertFillGradientAttributeToVCLGradient(aVCLGradient
, pFiGradient
->getFillGradient(), true);
1941 mpOutputDevice
->DrawTransparent(
1942 aContentMetafile
, aPrimitiveRectangle
.TopLeft(),
1943 aPrimitiveRectangle
.GetSize(), aVCLGradient
);
1947 // sub-transparence group. Draw to VDev first.
1948 // this may get refined to tiling when resolution is too big here
1950 // need to avoid switching off MapMode stuff here; maybe need another
1951 // tooling class, cannot just do the same as with the pixel renderer.
1952 // Need to experiment...
1954 // Okay, basic implementation finished and tested. The DPI stuff was hard
1955 // and not easy to find out that it's needed.
1956 // Since this will not yet happen normally (as long as noone constructs
1957 // transparence primitives with non-trivial transparence content) i will for now not
1958 // refine to tiling here.
1960 basegfx::B2DRange
aViewRange(primitive2d::getB2DRangeFromPrimitive2DSequence(rContent
, getViewInformation2D()));
1961 aViewRange
.transform(maCurrentTransformation
);
1962 const Rectangle
aRectLogic(
1963 (sal_Int32
)floor(aViewRange
.getMinX()), (sal_Int32
)floor(aViewRange
.getMinY()),
1964 (sal_Int32
)ceil(aViewRange
.getMaxX()), (sal_Int32
)ceil(aViewRange
.getMaxY()));
1965 const Rectangle
aRectPixel(mpOutputDevice
->LogicToPixel(aRectLogic
));
1966 Size
aSizePixel(aRectPixel
.GetSize());
1967 const Point aEmptyPoint
;
1968 VirtualDevice aBufferDevice
;
1969 const sal_uInt32
nMaxQuadratPixels(500000);
1970 const sal_uInt32
nViewVisibleArea(aSizePixel
.getWidth() * aSizePixel
.getHeight());
1971 double fReduceFactor(1.0);
1973 if(nViewVisibleArea
> nMaxQuadratPixels
)
1975 // reduce render size
1976 fReduceFactor
= sqrt((double)nMaxQuadratPixels
/ (double)nViewVisibleArea
);
1977 aSizePixel
= Size(basegfx::fround((double)aSizePixel
.getWidth() * fReduceFactor
),
1978 basegfx::fround((double)aSizePixel
.getHeight() * fReduceFactor
));
1981 if(aBufferDevice
.SetOutputSizePixel(aSizePixel
))
1983 // create and set MapModes for target devices
1984 MapMode
aNewMapMode(mpOutputDevice
->GetMapMode());
1985 aNewMapMode
.SetOrigin(Point(-aRectLogic
.Left(), -aRectLogic
.Top()));
1986 aBufferDevice
.SetMapMode(aNewMapMode
);
1988 // prepare view transformation for target renderers
1989 // ATTENTION! Need to apply another scaling because of the potential DPI differences
1990 // between Printer and VDev (mpOutputDevice and aBufferDevice here).
1991 // To get the DPI, LogicToPixel from (1,1) from MAP_INCH needs to be used.
1992 basegfx::B2DHomMatrix
aViewTransform(aBufferDevice
.GetViewTransformation());
1993 const Size
aDPIOld(mpOutputDevice
->LogicToPixel(Size(1, 1), MAP_INCH
));
1994 const Size
aDPINew(aBufferDevice
.LogicToPixel(Size(1, 1), MAP_INCH
));
1995 const double fDPIXChange((double)aDPIOld
.getWidth() / (double)aDPINew
.getWidth());
1996 const double fDPIYChange((double)aDPIOld
.getHeight() / (double)aDPINew
.getHeight());
1998 if(!basegfx::fTools::equal(fDPIXChange
, 1.0) || !basegfx::fTools::equal(fDPIYChange
, 1.0))
2000 aViewTransform
.scale(fDPIXChange
, fDPIYChange
);
2003 // also take scaling from Size reduction into acount
2004 if(!basegfx::fTools::equal(fReduceFactor
, 1.0))
2006 aViewTransform
.scale(fReduceFactor
, fReduceFactor
);
2009 // create view information and pixel renderer. Reuse known ViewInformation
2010 // except new transformation and range
2011 const geometry::ViewInformation2D
aViewInfo(
2012 getViewInformation2D().getObjectTransformation(),
2015 getViewInformation2D().getVisualizedPage(),
2016 getViewInformation2D().getViewTime(),
2017 getViewInformation2D().getExtendedInformationSequence());
2019 VclPixelProcessor2D
aBufferProcessor(aViewInfo
, aBufferDevice
);
2021 // draw content using pixel renderer
2022 aBufferProcessor
.process(rContent
);
2023 const Bitmap
aBmContent(aBufferDevice
.GetBitmap(aEmptyPoint
, aSizePixel
));
2025 // draw transparence using pixel renderer
2026 aBufferDevice
.Erase();
2027 aBufferProcessor
.process(rTransparence
);
2028 const AlphaMask
aBmAlpha(aBufferDevice
.GetBitmap(aEmptyPoint
, aSizePixel
));
2031 mpOutputDevice
->DrawBitmapEx(
2032 aRectLogic
.TopLeft(),
2033 aRectLogic
.GetSize(),
2034 BitmapEx(aBmContent
, aBmAlpha
));
2041 case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D
:
2043 // use default transform group pocessing
2044 RenderTransformPrimitive2D(static_cast< const primitive2d::TransformPrimitive2D
& >(rCandidate
));
2047 case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D
:
2049 // new XDrawPage for ViewInformation2D
2050 RenderPagePreviewPrimitive2D(static_cast< const primitive2d::PagePreviewPrimitive2D
& >(rCandidate
));
2053 case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D
:
2055 // use default marker array pocessing
2056 RenderMarkerArrayPrimitive2D(static_cast< const primitive2d::MarkerArrayPrimitive2D
& >(rCandidate
));
2059 case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D
:
2061 // use default point array pocessing
2062 RenderPointArrayPrimitive2D(static_cast< const primitive2d::PointArrayPrimitive2D
& >(rCandidate
));
2065 case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D
:
2067 // structured tag primitive
2068 const primitive2d::StructureTagPrimitive2D
& rStructureTagCandidate
= static_cast< const primitive2d::StructureTagPrimitive2D
& >(rCandidate
);
2069 const vcl::PDFWriter::StructElement
& rTagElement(rStructureTagCandidate
.getStructureElement());
2070 const bool bTagUsed(vcl::PDFWriter::NonStructElement
!= rTagElement
);
2072 if(mpPDFExtOutDevData
&& bTagUsed
)
2075 mpPDFExtOutDevData
->BeginStructureElement(rTagElement
);
2078 // proccess children normally
2079 process(rStructureTagCandidate
.getChildren());
2081 if(mpPDFExtOutDevData
&& bTagUsed
)
2084 mpPDFExtOutDevData
->EndStructureElement();
2089 case PRIMITIVE2D_ID_EPSPRIMITIVE2D
:
2091 RenderEpsPrimitive2D(static_cast< const primitive2d::EpsPrimitive2D
& >(rCandidate
));
2096 // process recursively
2097 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
2102 } // end of namespace processor2d
2103 } // end of namespace drawinglayer
2105 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */