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 "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/maskprimitive2d.hxx>
31 #include <drawinglayer/primitive2d/metafileprimitive2d.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 "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>
58 #include <vcl/dibtools.hxx>
60 // for PDFExtOutDevData Graphic support
61 #include <vcl/graph.hxx>
62 #include <vcl/svapp.hxx>
63 #include <toolkit/helper/formpdfexport.hxx>
65 // for Control printing
66 #include <com/sun/star/beans/XPropertySet.hpp>
68 // for StructureTagPrimitive support in sd's unomodel.cxx
69 #include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
71 using namespace com::sun::star
;
73 // #112245# definition for maximum allowed point count due to Metafile target.
74 // To be on the safe side with the old tools polygon, use slightly less then
75 // the theoretical maximum (bad experiences with tools polygon)
77 #define MAX_POLYGON_POINT_COUNT_METAFILE (0x0000fff0)
81 // #112245# helper to split line polygon in half
82 void splitLinePolygon(
83 const basegfx::B2DPolygon
& rBasePolygon
,
84 basegfx::B2DPolygon
& o_aLeft
,
85 basegfx::B2DPolygon
& o_aRight
)
87 const sal_uInt32
nCount(rBasePolygon
.count());
91 const sal_uInt32
nHalfCount((nCount
- 1) >> 1);
93 o_aLeft
= basegfx::B2DPolygon(rBasePolygon
, 0, nHalfCount
+ 1);
94 o_aLeft
.setClosed(false);
96 o_aRight
= basegfx::B2DPolygon(rBasePolygon
, nHalfCount
, nCount
- nHalfCount
);
97 o_aRight
.setClosed(false);
99 if(rBasePolygon
.isClosed())
101 o_aRight
.append(rBasePolygon
.getB2DPoint(0));
103 if(rBasePolygon
.areControlPointsUsed())
105 o_aRight
.setControlPoints(
106 o_aRight
.count() - 1,
107 rBasePolygon
.getPrevControlPoint(0),
108 rBasePolygon
.getNextControlPoint(0));
119 // #112245# helper to evtl. split filled polygons to maximum metafile point count
120 bool fillPolyPolygonNeededToBeSplit(basegfx::B2DPolyPolygon
& rPolyPolygon
)
123 const sal_uInt32
nPolyCount(rPolyPolygon
.count());
127 basegfx::B2DPolyPolygon aSplitted
;
129 for(sal_uInt32
a(0); a
< nPolyCount
; a
++)
131 const basegfx::B2DPolygon
aCandidate(rPolyPolygon
.getB2DPolygon(a
));
132 const sal_uInt32
nPointCount(aCandidate
.count());
133 bool bNeedToSplit(false);
135 if(aCandidate
.areControlPointsUsed())
137 // compare with the maximum for bezier curved polygons
138 bNeedToSplit
= nPointCount
> ((MAX_POLYGON_POINT_COUNT_METAFILE
/ 3L) - 1L);
142 // compare with the maximum for simple point polygons
143 bNeedToSplit
= nPointCount
> (MAX_POLYGON_POINT_COUNT_METAFILE
- 1);
148 // need to split the partial polygon
149 const basegfx::B2DRange
aRange(aCandidate
.getB2DRange());
150 const basegfx::B2DPoint
aCenter(aRange
.getCenter());
152 if(aRange
.getWidth() > aRange
.getHeight())
154 // clip in left and right
155 const basegfx::B2DPolyPolygon
aLeft(
156 basegfx::tools::clipPolygonOnParallelAxis(
162 const basegfx::B2DPolyPolygon
aRight(
163 basegfx::tools::clipPolygonOnParallelAxis(
170 aSplitted
.append(aLeft
);
171 aSplitted
.append(aRight
);
175 // clip in top and bottom
176 const basegfx::B2DPolyPolygon
aTop(
177 basegfx::tools::clipPolygonOnParallelAxis(
183 const basegfx::B2DPolyPolygon
aBottom(
184 basegfx::tools::clipPolygonOnParallelAxis(
191 aSplitted
.append(aTop
);
192 aSplitted
.append(aBottom
);
197 aSplitted
.append(aCandidate
);
201 if(aSplitted
.count() != nPolyCount
)
203 rPolyPolygon
= aSplitted
;
210 /** Filter input polypolygon for effectively empty sub-fills
212 Needed to fix fdo#37559
215 tools::PolyPolygon to filter
217 @return converted tools PolyPolygon, w/o one-point fills
219 ::tools::PolyPolygon
getFillPolyPolygon( const ::basegfx::B2DPolyPolygon
& rPoly
)
221 // filter input rPoly
222 basegfx::B2DPolyPolygon aPoly
;
223 sal_uInt32
nCount(rPoly
.count());
224 for( sal_uInt32 i
=0; i
<nCount
; ++i
)
226 basegfx::B2DPolygon
aCandidate(rPoly
.getB2DPolygon(i
));
227 if( !aCandidate
.isClosed() || aCandidate
.count() > 1 )
228 aPoly
.append(aCandidate
);
230 return ::tools::PolyPolygon(aPoly
);
233 } // end of anonymous namespace
235 namespace drawinglayer
237 namespace processor2d
239 Rectangle
VclMetafileProcessor2D::impDumpToMetaFile(
240 const primitive2d::Primitive2DContainer
& rContent
,
241 GDIMetaFile
& o_rContentMetafile
)
243 // Prepare VDev, MetaFile and connections
244 OutputDevice
* pLastOutputDevice
= mpOutputDevice
;
245 GDIMetaFile
* pLastMetafile
= mpMetaFile
;
246 basegfx::B2DRange
aPrimitiveRange(rContent
.getB2DRange(getViewInformation2D()));
248 // transform primitive range with current transformation (e.g shadow offset)
249 aPrimitiveRange
.transform(maCurrentTransformation
);
251 const Rectangle
aPrimitiveRectangle(
252 basegfx::fround(aPrimitiveRange
.getMinX()), basegfx::fround(aPrimitiveRange
.getMinY()),
253 basegfx::fround(aPrimitiveRange
.getMaxX()), basegfx::fround(aPrimitiveRange
.getMaxY()));
254 ScopedVclPtrInstance
< VirtualDevice
> aContentVDev
;
255 MapMode
aNewMapMode(pLastOutputDevice
->GetMapMode());
257 mpOutputDevice
= aContentVDev
.get();
258 mpMetaFile
= &o_rContentMetafile
;
259 aContentVDev
->EnableOutput(false);
260 aContentVDev
->SetMapMode(pLastOutputDevice
->GetMapMode());
261 o_rContentMetafile
.Record(aContentVDev
.get());
262 aContentVDev
->SetLineColor(pLastOutputDevice
->GetLineColor());
263 aContentVDev
->SetFillColor(pLastOutputDevice
->GetFillColor());
264 aContentVDev
->SetFont(pLastOutputDevice
->GetFont());
265 aContentVDev
->SetDrawMode(pLastOutputDevice
->GetDrawMode());
266 aContentVDev
->SetSettings(pLastOutputDevice
->GetSettings());
267 aContentVDev
->SetRefPoint(pLastOutputDevice
->GetRefPoint());
273 o_rContentMetafile
.Stop();
274 o_rContentMetafile
.WindStart();
275 aNewMapMode
.SetOrigin(aPrimitiveRectangle
.TopLeft());
276 o_rContentMetafile
.SetPrefMapMode(aNewMapMode
);
277 o_rContentMetafile
.SetPrefSize(aPrimitiveRectangle
.GetSize());
278 mpOutputDevice
= pLastOutputDevice
;
279 mpMetaFile
= pLastMetafile
;
281 return aPrimitiveRectangle
;
284 void VclMetafileProcessor2D::impConvertFillGradientAttributeToVCLGradient(
285 Gradient
& o_rVCLGradient
,
286 const attribute::FillGradientAttribute
& rFiGrAtt
,
287 bool bIsTransparenceGradient
)
289 if(bIsTransparenceGradient
)
291 // it's about transparence channel intensities (black/white), do not use color modifier
292 o_rVCLGradient
.SetStartColor(Color(rFiGrAtt
.getStartColor()));
293 o_rVCLGradient
.SetEndColor(Color(rFiGrAtt
.getEndColor()));
297 // use color modifier to influence start/end color of gradient
298 o_rVCLGradient
.SetStartColor(Color(maBColorModifierStack
.getModifiedColor(rFiGrAtt
.getStartColor())));
299 o_rVCLGradient
.SetEndColor(Color(maBColorModifierStack
.getModifiedColor(rFiGrAtt
.getEndColor())));
302 o_rVCLGradient
.SetAngle(static_cast< sal_uInt16
>(rFiGrAtt
.getAngle() * (1.0 / F_PI1800
)));
303 o_rVCLGradient
.SetBorder(static_cast< sal_uInt16
>(rFiGrAtt
.getBorder() * 100.0));
304 o_rVCLGradient
.SetOfsX(static_cast< sal_uInt16
>(rFiGrAtt
.getOffsetX() * 100.0));
305 o_rVCLGradient
.SetOfsY(static_cast< sal_uInt16
>(rFiGrAtt
.getOffsetY() * 100.0));
306 o_rVCLGradient
.SetSteps(rFiGrAtt
.getSteps());
308 // defaults for intensity; those were computed into the start/end colors already
309 o_rVCLGradient
.SetStartIntensity(100);
310 o_rVCLGradient
.SetEndIntensity(100);
312 switch(rFiGrAtt
.getStyle())
314 default : // attribute::GradientStyle::Linear :
316 o_rVCLGradient
.SetStyle(GradientStyle_LINEAR
);
319 case attribute::GradientStyle::Axial
:
321 o_rVCLGradient
.SetStyle(GradientStyle_AXIAL
);
324 case attribute::GradientStyle::Radial
:
326 o_rVCLGradient
.SetStyle(GradientStyle_RADIAL
);
329 case attribute::GradientStyle::Elliptical
:
331 o_rVCLGradient
.SetStyle(GradientStyle_ELLIPTICAL
);
334 case attribute::GradientStyle::Square
:
336 o_rVCLGradient
.SetStyle(GradientStyle_SQUARE
);
339 case attribute::GradientStyle::Rect
:
341 o_rVCLGradient
.SetStyle(GradientStyle_RECT
);
347 void VclMetafileProcessor2D::impStartSvtGraphicFill(SvtGraphicFill
* pSvtGraphicFill
)
349 if(pSvtGraphicFill
&& !mnSvtGraphicFillCount
)
351 SvMemoryStream aMemStm
;
353 WriteSvtGraphicFill( aMemStm
, *pSvtGraphicFill
);
354 mpMetaFile
->AddAction(new MetaCommentAction("XPATHFILL_SEQ_BEGIN", 0, static_cast< const sal_uInt8
* >(aMemStm
.GetData()), aMemStm
.Seek(STREAM_SEEK_TO_END
)));
355 mnSvtGraphicFillCount
++;
359 void VclMetafileProcessor2D::impEndSvtGraphicFill(SvtGraphicFill
* pSvtGraphicFill
)
361 if(pSvtGraphicFill
&& mnSvtGraphicFillCount
)
363 mnSvtGraphicFillCount
--;
364 mpMetaFile
->AddAction(new MetaCommentAction("XPATHFILL_SEQ_END"));
365 delete pSvtGraphicFill
;
369 double VclMetafileProcessor2D::getTransformedLineWidth( double fWidth
) const
371 // #i113922# the LineWidth is duplicated in the MetaPolylineAction,
372 // and also inside the SvtGraphicStroke and needs transforming into
373 // the same space as its co-ordinates here cf. fdo#61789
374 // This is a partial fix. When a object transformation is used which
375 // e.g. contains a scaleX != scaleY, an unproportional scaling will happen.
376 const basegfx::B2DVector
aDiscreteUnit( maCurrentTransformation
* basegfx::B2DVector( fWidth
, 0.0 ) );
378 return aDiscreteUnit
.getLength();
381 SvtGraphicStroke
* VclMetafileProcessor2D::impTryToCreateSvtGraphicStroke(
382 const basegfx::B2DPolygon
& rB2DPolygon
,
383 const basegfx::BColor
* pColor
,
384 const attribute::LineAttribute
* pLineAttribute
,
385 const attribute::StrokeAttribute
* pStrokeAttribute
,
386 const attribute::LineStartEndAttribute
* pStart
,
387 const attribute::LineStartEndAttribute
* pEnd
)
389 SvtGraphicStroke
* pRetval
= nullptr;
391 if(rB2DPolygon
.count() && !mnSvtGraphicStrokeCount
)
393 basegfx::B2DPolygon
aLocalPolygon(rB2DPolygon
);
394 basegfx::BColor aStrokeColor
;
395 basegfx::B2DPolyPolygon aStartArrow
;
396 basegfx::B2DPolyPolygon aEndArrow
;
400 aStrokeColor
= *pColor
;
402 else if(pLineAttribute
)
404 aStrokeColor
= maBColorModifierStack
.getModifiedColor(pLineAttribute
->getColor());
407 // It IS needed to record the stroke color at all in the metafile,
408 // SvtGraphicStroke has NO entry for stroke color(!)
409 mpOutputDevice
->SetLineColor(Color(aStrokeColor
));
411 if(!aLocalPolygon
.isClosed())
413 double fPolyLength(0.0);
417 if(pStart
&& pStart
->isActive())
419 fPolyLength
= basegfx::tools::getLength(aLocalPolygon
);
421 aStartArrow
= basegfx::tools::createAreaGeometryForLineStartEnd(
422 aLocalPolygon
, pStart
->getB2DPolyPolygon(), true, pStart
->getWidth(),
423 fPolyLength
, pStart
->isCentered() ? 0.5 : 0.0, &fStart
);
426 if(pEnd
&& pEnd
->isActive())
428 if(basegfx::fTools::equalZero(fPolyLength
))
430 fPolyLength
= basegfx::tools::getLength(aLocalPolygon
);
433 aEndArrow
= basegfx::tools::createAreaGeometryForLineStartEnd(
434 aLocalPolygon
, pEnd
->getB2DPolyPolygon(), false, pEnd
->getWidth(),
435 fPolyLength
, pEnd
->isCentered() ? 0.5 : 0.0, &fEnd
);
438 if(0.0 != fStart
|| 0.0 != fEnd
)
440 // build new poly, consume something from old poly
441 aLocalPolygon
= basegfx::tools::getSnippetAbsolute(aLocalPolygon
, fStart
, fPolyLength
- fEnd
, fPolyLength
);
445 SvtGraphicStroke::JoinType
eJoin(SvtGraphicStroke::joinNone
);
446 SvtGraphicStroke::CapType
eCap(SvtGraphicStroke::capButt
);
447 double fLineWidth(0.0);
448 double fMiterLength(0.0);
449 SvtGraphicStroke::DashArray aDashArray
;
453 fLineWidth
= fMiterLength
= getTransformedLineWidth( pLineAttribute
->getWidth() );
456 switch(pLineAttribute
->getLineJoin())
458 case basegfx::B2DLineJoin::NONE
:
460 eJoin
= SvtGraphicStroke::joinNone
;
463 case basegfx::B2DLineJoin::Bevel
:
465 eJoin
= SvtGraphicStroke::joinBevel
;
468 case basegfx::B2DLineJoin::Miter
:
470 eJoin
= SvtGraphicStroke::joinMiter
;
471 // ATM 15 degrees is assumed
472 fMiterLength
/= rtl::math::sin(M_PI
* (15.0 / 360.0));
475 case basegfx::B2DLineJoin::Round
:
477 eJoin
= SvtGraphicStroke::joinRound
;
483 switch(pLineAttribute
->getLineCap())
485 default: /* css::drawing::LineCap_BUTT */
487 eCap
= SvtGraphicStroke::capButt
;
490 case css::drawing::LineCap_ROUND
:
492 eCap
= SvtGraphicStroke::capRound
;
495 case css::drawing::LineCap_SQUARE
:
497 eCap
= SvtGraphicStroke::capSquare
;
506 aDashArray
= pStrokeAttribute
->getDotDashArray();
509 // #i101734# apply current object transformation to created geometry.
510 // This is a partial fix. When a object transformation is used which
511 // e.g. contains a scaleX != scaleY, an unproportional scaling would
512 // have to be applied to the evtl. existing fat line. The current
513 // concept of PDF export and SvtGraphicStroke usage does simply not
514 // allow handling such definitions. The only clean way would be to
515 // add the transformation to SvtGraphicStroke and to handle it there
516 aLocalPolygon
.transform(maCurrentTransformation
);
517 aStartArrow
.transform(maCurrentTransformation
);
518 aEndArrow
.transform(maCurrentTransformation
);
520 pRetval
= new SvtGraphicStroke(
521 ::tools::Polygon(aLocalPolygon
),
522 ::tools::PolyPolygon(aStartArrow
),
523 ::tools::PolyPolygon(aEndArrow
),
524 mfCurrentUnifiedTransparence
,
535 void VclMetafileProcessor2D::impStartSvtGraphicStroke(SvtGraphicStroke
* pSvtGraphicStroke
)
537 if(pSvtGraphicStroke
&& !mnSvtGraphicStrokeCount
)
539 SvMemoryStream aMemStm
;
541 WriteSvtGraphicStroke( aMemStm
, *pSvtGraphicStroke
);
542 mpMetaFile
->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_BEGIN", 0, static_cast< const sal_uInt8
* >(aMemStm
.GetData()), aMemStm
.Seek(STREAM_SEEK_TO_END
)));
543 mnSvtGraphicStrokeCount
++;
547 void VclMetafileProcessor2D::impEndSvtGraphicStroke(SvtGraphicStroke
* pSvtGraphicStroke
)
549 if(pSvtGraphicStroke
&& mnSvtGraphicStrokeCount
)
551 mnSvtGraphicStrokeCount
--;
552 mpMetaFile
->AddAction(new MetaCommentAction("XPATHSTROKE_SEQ_END"));
553 delete pSvtGraphicStroke
;
557 // init static break iterator
558 uno::Reference
< css::i18n::XBreakIterator
> VclMetafileProcessor2D::mxBreakIterator
;
560 VclMetafileProcessor2D::VclMetafileProcessor2D(const geometry::ViewInformation2D
& rViewInformation
, OutputDevice
& rOutDev
)
561 : VclProcessor2D(rViewInformation
, rOutDev
),
562 mpMetaFile(rOutDev
.GetConnectMetaFile()),
563 mnSvtGraphicFillCount(0),
564 mnSvtGraphicStrokeCount(0),
565 mfCurrentUnifiedTransparence(0.0),
566 mpPDFExtOutDevData(dynamic_cast< vcl::PDFExtOutDevData
* >(rOutDev
.GetExtOutDevData()))
568 OSL_ENSURE(rOutDev
.GetConnectMetaFile(), "VclMetafileProcessor2D: Used on OutDev which has no MetaFile Target (!)");
569 // draw to logic coordinates, do not initialize maCurrentTransformation to viewTransformation
570 // but only to ObjectTransformation. Do not change MapMode of destination.
571 maCurrentTransformation
= rViewInformation
.getObjectTransformation();
574 VclMetafileProcessor2D::~VclMetafileProcessor2D()
576 // MapMode was not changed, no restore necessary
579 /***********************************************************************************************
581 Support of MetaCommentActions in the VclMetafileProcessor2D
582 Found MetaCommentActions and how they are supported:
584 XGRAD_SEQ_BEGIN, XGRAD_SEQ_END:
586 Used inside OutputDevice::DrawGradient to mark the start and end of a MetaGradientEx action.
587 It is used in various exporters/importers to have direct access to the gradient before it
588 is rendered by VCL (and thus fragmented to polygon color actions and others). On that base, e.g.
589 the Metafile to SdrObject import creates its gradient objects.
590 Best (and safest) way to support it here is to use PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D,
591 map it back to the corresponding tools tools::PolyPolygon and the Gradient and just call
592 OutputDevice::DrawGradient which creates the necessary compatible actions.
594 XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END:
596 Two producers, one is vcl/source/gdi/gdimtf.cxx, line 1273. There, it is transformed
597 inside GDIMetaFile::Rotate, nothing to take care of here.
598 The second producer is in graphics/svx/source/svdraw/impgrfll.cxx, line 374. This is used
599 with each incarnation of Imp_GraphicFill when a metafile is recorded, fillstyle is not
600 XFILL_NONE and not completely transparent. It creates a SvtGraphicFill and streams it
601 to the comment action. A closing end token is created in the destructor.
602 Usages of Imp_GraphicFill are in Do_Paint_Object-methods of SdrCircObj, SdrPathObj and
604 The token users pick various actions from SvtGraphicFill, so it may need to be added for all kind
605 of filled objects, even simple colored polygons. It is added as extra information; the
606 Metafile actions between the two tokens are interpreted as output generated from those
607 fills. Thus, users have the choice to use the SvtGraphicFill info or the created output
609 Even for XFillTransparenceItem it is used, thus it may need to be supported in
610 UnifiedTransparencePrimitive2D, too, when interpreted as normally filled PolyPolygon.
612 PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D,
613 PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D,
614 PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D,
615 PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D,
616 and for PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D when detected unified transparence
618 XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END:
620 Similar to pathfill, but using SvtGraphicStroke instead. It also has two producers where one
621 is also the GDIMetaFile::Rotate. Another user is MetaCommentAction::Move which modifies the
622 contained path accordingly.
623 The other one is SdrObject::Imp_DrawLineGeometry. It's done when MetaFile is set at OutDev and
624 only when geometry is a single polygon (!). I see no reason for that; in the PS exporter this
625 would hinder to make use of tools::PolyPolygon strokes. I will need to add support at:
626 PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
627 PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
628 PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D
629 This can be done hierarchical, too.
630 Okay, base implementation done based on those three primitives.
632 FIELD_SEQ_BEGIN, FIELD_SEQ_END
634 Used from slideshow for URLs, created from diverse SvxField implementations inside
635 createBeginComment()/createEndComment(). createBeginComment() is used from editeng\impedit3.cxx
636 inside ImpEditEngine::Paint.
637 Created TextHierarchyFieldPrimitive2D and added needed infos there; it is an group primitive and wraps
638 text primitives (but is not limited to that). It contains the field type if special actions for the
639 support of FIELD_SEQ_BEGIN/END are needed; this is the case for Page and URL fields. If more is
640 needed, it may be supported there.
641 FIELD_SEQ_BEGIN;PageField
643 Okay, these are now completely supported by TextHierarchyFieldPrimitive2D. URL works, too.
647 XTEXT_EOC(i) end of character
648 XTEXT_EOW(i) end of word
649 XTEXT_EOS(i) end of sentence
651 this three are with index and are created with the help of a i18n::XBreakIterator in
652 ImplDrawWithComments. Simplifying, moving out text painting, reworking to create some
653 data structure for holding those TEXT infos.
654 Supported directly by TextSimplePortionPrimitive2D with adding a Locale to the basic text
655 primitive. In the MetaFileRenderer, the creation is now done (see below). This has the advantage
656 that this creations do not need to be done for all paints all the time. This would be
657 expensive since the BreakIterator and it's usage is expensive and for each paint also the
658 whole character stops would need to be created.
659 Created only for TextDecoratedPortionPrimitive2D due to XTEXT_EOL and XTEXT_EOP (see below)
661 XTEXT_EOL() end of line
662 XTEXT_EOP() end of paragraph
664 First try with boolean marks at TextDecoratedPortionPrimitive2D did not work too well,
665 i decided to solve it with structure. I added the TextHierarchyPrimitives for this,
667 - TextHierarchyLinePrimitive2D: Encapsulates single line
668 - TextHierarchyParagraphPrimitive2D: Encapsulates single paragraph
669 - TextHierarchyBlockPrimitive2D: encapsulates object texts (only one ATM)
670 Those are now supported in hierarchy. This means the MetaFile renderer will support them
671 by using them, recursively using their content and adding MetaFile comments as needed.
672 This also means that when another text layouter will be used it will be necessary to
673 create/support the same HierarchyPrimitives to support users.
674 To transport the information using this hierarchy is best suited to all future needs;
675 the slideshow will be able to profit from it directly when using primitives; all other
676 renderers not interested in the text structure will just ignore the encapsulations.
678 XTEXT_PAINTSHAPE_BEGIN, XTEXT_PAINTSHAPE_END
679 Supported now by the TextHierarchyBlockPrimitive2D.
681 EPSReplacementGraphic:
682 Only used in goodies\source\filter.vcl\ieps\ieps.cxx and svx\source\xml\xmlgrhlp.cxx to
683 hold the original EPS which was imported in the same MetaFile as first 2 entries. Only
684 used to export the original again (if exists).
685 Not necessary to support with MetaFuleRenderer.
687 XTEXT_SCROLLRECT, XTEXT_PAINTRECT
688 Currently used to get extra MetaFile infos using GraphicExporter which again uses
689 SdrTextObj::GetTextScrollMetaFileAndRectangle(). ATM works with primitives since
690 the rectangle data is added directly by the GraphicsExporter as comment. Does not need
691 to be adapted at once.
692 When adapting later, the only user - the diashow - should directly use the provided
693 Animation infos in the appropriate primitives (e.g. AnimatedSwitchPrimitive2D)
695 PRNSPOOL_TRANSPARENTBITMAP_BEGIN, PRNSPOOL_TRANSPARENTBITMAP_END
696 VCL usage when printing PL -> THB. Okay, THB confirms that it is only used as
697 a fix (hack) while VCL printing. It is needed to not downscale a bitmap which
698 was explicitly created for the printer already again to some default maximum
700 Nothing to do here for the primitive renderer.
702 Support for vcl::PDFExtOutDevData:
703 PL knows that SJ did that stuff, it's used to hold a pointer to PDFExtOutDevData at
704 the OutDev. When set, some extra data is written there. Trying simple PDF export and
705 watching if I get those infos.
706 Well, a PDF export does not use e.g. ImpEditEngine::Paint since the PdfFilter uses
707 the SdXImpressDocument::render and thus uses the VclMetafileProcessor2D. I will check
708 if I get a PDFExtOutDevData at the target output device.
709 Indeed, I get one. Checking what all may be done when that extra-device-info is there.
711 All in all I have to talk to SJ. I will need to emulate some of those actions, but
712 i need to discuss which ones.
713 In the future, all those infos would be taken from the primitive sequence anyways,
714 thus these extensions would potentially be temporary, too.
715 Discussed with SJ, added the necessary support and tested it. Details follow.
717 - In ImpEditEngine::Paint, paragraph infos and URL stuff is added.
718 Added in primitive MetaFile renderer.
719 Checking URL: Indeed, current version exports it, but it is missing in primitive
720 CWS version. Adding support.
721 Okay, URLs work. Checked, Done.
723 - UnoControlPDFExportContact is only created when PDFExtOutDevData is used at the
724 target and uno control data is created in UnoControlPDFExportContact::do_PaintObject.
725 This may be added in primitive MetaFile renderer.
727 OOps, the necessary helper stuff is in svx/source/form/formpdxexport.cxx in namespace
728 svxform. Have to talk to FS if this has to be like that. Especially since
729 vcl::PDFWriter::AnyWidget is filled out, which is already part of vcl.
730 Wrote an eMail to FS, he is on vacation currently. I see no reason why not to move
731 that stuff to somewhere else, maybe tools or svtools ?!? We will see...
732 Moved to toolkit, so I have to link against it. I tried VCL first, but it did
733 not work since VCLUnoHelper::CreateFont is unresolved in VCL (!). Other then the name
734 may imply, it is defined in toolkit (!). Since toolkit is linked against VCL itself,
735 the lowest movement plane is toolkit.
736 Checked form control export, it works well. Done.
738 - In goodies, in GraphicObject::Draw, when the used Graphic is linked, infos are
739 generated. I will need to check what happens here with primitives.
740 To support, use of GraphicPrimitive2D (PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D) may be needed.
741 Added support, but feature is broken in main version, so i cannot test at all.
742 Writing a bug to CL (or SJ) and seeing what happens (#i80380#).
743 SJ took a look and we got it working. Tested VCL MetaFile Renderer based export,
744 as intended, the original file is exported. Works, Done.
749 - Maybe there are more places to take care of for vcl::PDFExtOutDevData!
752 ****************************************************************************************************/
754 void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D
& rCandidate
)
756 switch(rCandidate
.getPrimitive2DID())
758 case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D
:
760 // directdraw of wrong spell primitive
761 // Ignore for VclMetafileProcessor2D, this is for printing and MetaFile recording only
764 case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D
:
766 const primitive2d::GraphicPrimitive2D
& rGraphicPrimitive
= static_cast< const primitive2d::GraphicPrimitive2D
& >(rCandidate
);
767 bool bUsingPDFExtOutDevData(false);
768 basegfx::B2DVector aTranslate
, aScale
;
769 static bool bSuppressPDFExtOutDevDataSupport(false);
771 if(mpPDFExtOutDevData
&& !bSuppressPDFExtOutDevDataSupport
)
773 // emulate data handling from UnoControlPDFExportContact, original see
774 // svtools/source/graphic/grfmgr.cxx
775 const Graphic
& rGraphic
= rGraphicPrimitive
.getGraphicObject().GetGraphic();
777 if(rGraphic
.IsLink())
779 const GraphicAttr
& rAttr
= rGraphicPrimitive
.getGraphicAttr();
781 if(!rAttr
.IsSpecialDrawMode() && !rAttr
.IsAdjusted())
783 const basegfx::B2DHomMatrix
& rTransform
= rGraphicPrimitive
.getTransform();
784 double fRotate
, fShearX
;
785 rTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
787 if( basegfx::fTools::equalZero( fRotate
) && ( aScale
.getX() > 0.0 ) && ( aScale
.getY() > 0.0 ) )
789 bUsingPDFExtOutDevData
= true;
790 mpPDFExtOutDevData
->BeginGroup();
796 // process recursively and add MetaFile comment
797 process(rGraphicPrimitive
.get2DDecomposition(getViewInformation2D()));
799 if(bUsingPDFExtOutDevData
)
801 // emulate data handling from UnoControlPDFExportContact, original see
802 // svtools/source/graphic/grfmgr.cxx
803 const basegfx::B2DRange
aCurrentRange(
804 aTranslate
.getX(), aTranslate
.getY(),
805 aTranslate
.getX() + aScale
.getX(), aTranslate
.getY() + aScale
.getY());
806 const Rectangle
aCurrentRect(
807 sal_Int32(floor(aCurrentRange
.getMinX())), sal_Int32(floor(aCurrentRange
.getMinY())),
808 sal_Int32(ceil(aCurrentRange
.getMaxX())), sal_Int32(ceil(aCurrentRange
.getMaxY())));
809 const GraphicAttr
& rAttr
= rGraphicPrimitive
.getGraphicAttr();
810 // fdo#72530 don't pass empty Rectangle to EndGroup
811 Rectangle
aCropRect(aCurrentRect
);
813 if(rAttr
.IsCropped())
815 // calculate scalings between real image size and logic object size. This
816 // is necessary since the crop values are relative to original bitmap size
817 double fFactorX(1.0);
818 double fFactorY(1.0);
821 const MapMode
aMapMode100thmm(MAP_100TH_MM
);
822 const Size
aBitmapSize(OutputDevice::LogicToLogic(
823 rGraphicPrimitive
.getGraphicObject().GetPrefSize(),
824 rGraphicPrimitive
.getGraphicObject().GetPrefMapMode(), aMapMode100thmm
));
825 const double fDivX(aBitmapSize
.Width() - rAttr
.GetLeftCrop() - rAttr
.GetRightCrop());
826 const double fDivY(aBitmapSize
.Height() - rAttr
.GetTopCrop() - rAttr
.GetBottomCrop());
828 if(!basegfx::fTools::equalZero(fDivX
))
830 fFactorX
= aScale
.getX() / fDivX
;
833 if(!basegfx::fTools::equalZero(fDivY
))
835 fFactorY
= aScale
.getY() / fDivY
;
839 // calculate crop range and rect
840 basegfx::B2DRange aCropRange
;
841 aCropRange
.expand(aCurrentRange
.getMinimum() - basegfx::B2DPoint(rAttr
.GetLeftCrop() * fFactorX
, rAttr
.GetTopCrop() * fFactorY
));
842 aCropRange
.expand(aCurrentRange
.getMaximum() + basegfx::B2DPoint(rAttr
.GetRightCrop() * fFactorX
, rAttr
.GetBottomCrop() * fFactorY
));
844 aCropRect
= Rectangle(
845 sal_Int32(floor(aCropRange
.getMinX())), sal_Int32(floor(aCropRange
.getMinY())),
846 sal_Int32(ceil(aCropRange
.getMaxX())), sal_Int32(ceil(aCropRange
.getMaxY())));
849 // #i123295# 3rd param is uncropped rect, 4th is cropped. The primitive has the cropped
850 // object transformation, thus aCurrentRect *is* the clip region while aCropRect is the expanded,
851 // uncropped region. Thus, correct order is aCropRect, aCurrentRect
852 mpPDFExtOutDevData
->EndGroup(rGraphicPrimitive
.getGraphicObject().GetGraphic(),
853 rAttr
.GetTransparency(),
860 case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D
:
862 const primitive2d::ControlPrimitive2D
& rControlPrimitive
= static_cast< const primitive2d::ControlPrimitive2D
& >(rCandidate
);
863 const uno::Reference
< awt::XControl
>& rXControl(rControlPrimitive
.getXControl());
864 bool bIsPrintableControl(false);
866 // find out if control is printable
871 uno::Reference
< beans::XPropertySet
> xModelProperties(rXControl
->getModel(), uno::UNO_QUERY
);
872 uno::Reference
< beans::XPropertySetInfo
> xPropertyInfo(xModelProperties
.is()
873 ? xModelProperties
->getPropertySetInfo()
874 : uno::Reference
< beans::XPropertySetInfo
>());
875 const OUString
sPrintablePropertyName("Printable");
877 if(xPropertyInfo
.is() && xPropertyInfo
->hasPropertyByName(sPrintablePropertyName
))
879 OSL_VERIFY(xModelProperties
->getPropertyValue(sPrintablePropertyName
) >>= bIsPrintableControl
);
882 catch(const uno::Exception
&)
884 OSL_FAIL("VclMetafileProcessor2D: No access to printable flag of Control, caught an exception!");
888 // PDF export and printing only for printable controls
889 if(bIsPrintableControl
)
891 const bool bPDFExport(mpPDFExtOutDevData
&& mpPDFExtOutDevData
->GetIsExportFormFields());
892 bool bDoProcessRecursively(true);
896 // PDF export. Emulate data handling from UnoControlPDFExportContact
897 // I have now moved describePDFControl to toolkit, thus i can implement the PDF
898 // form control support now as follows
899 ::std::unique_ptr
< vcl::PDFWriter::AnyWidget
> pPDFControl(
900 ::toolkitform::describePDFControl( rXControl
, *mpPDFExtOutDevData
) );
902 if(pPDFControl
.get())
904 // still need to fill in the location (is a class Rectangle)
905 const basegfx::B2DRange
aRangeLogic(rControlPrimitive
.getB2DRange(getViewInformation2D()));
906 const Rectangle
aRectLogic(
907 (sal_Int32
)floor(aRangeLogic
.getMinX()), (sal_Int32
)floor(aRangeLogic
.getMinY()),
908 (sal_Int32
)ceil(aRangeLogic
.getMaxX()), (sal_Int32
)ceil(aRangeLogic
.getMaxY()));
909 pPDFControl
->Location
= aRectLogic
;
911 Size
aFontSize(pPDFControl
->TextFont
.GetFontSize());
912 aFontSize
= OutputDevice::LogicToLogic(aFontSize
, MapMode(MAP_POINT
), mpOutputDevice
->GetMapMode());
913 pPDFControl
->TextFont
.SetFontSize(aFontSize
);
915 mpPDFExtOutDevData
->BeginStructureElement(vcl::PDFWriter::Form
);
916 mpPDFExtOutDevData
->CreateControl(*pPDFControl
.get());
917 mpPDFExtOutDevData
->EndStructureElement();
919 // no normal paint needed (see original UnoControlPDFExportContact::do_PaintObject);
920 // do not process recursively
921 bDoProcessRecursively
= false;
925 // PDF export did not work, try simple output.
926 // Fallback to printer output by not setting bDoProcessRecursively
931 // #i93169# used flag the wrong way; true means that nothing was done yet
932 if(bDoProcessRecursively
)
937 // remember old graphics and create new
938 uno::Reference
< awt::XView
> xControlView(rXControl
, uno::UNO_QUERY_THROW
);
939 const uno::Reference
< awt::XGraphics
> xOriginalGraphics(xControlView
->getGraphics());
940 const uno::Reference
< awt::XGraphics
> xNewGraphics(mpOutputDevice
->CreateUnoGraphics());
942 if(xNewGraphics
.is())
944 // link graphics and view
945 xControlView
->setGraphics(xNewGraphics
);
948 const basegfx::B2DHomMatrix
aObjectToDiscrete(getViewInformation2D().getObjectToViewTransformation() * rControlPrimitive
.getTransform());
949 const basegfx::B2DPoint
aTopLeftDiscrete(aObjectToDiscrete
* basegfx::B2DPoint(0.0, 0.0));
952 xControlView
->draw(basegfx::fround(aTopLeftDiscrete
.getX()), basegfx::fround(aTopLeftDiscrete
.getY()));
953 bDoProcessRecursively
= false;
955 // restore original graphics
956 xControlView
->setGraphics(xOriginalGraphics
);
959 catch( const uno::Exception
& )
961 OSL_FAIL("VclMetafileProcessor2D: Printing of Control failed, caught an exception!");
965 // process recursively if not done yet to export as decomposition (bitmap)
966 if(bDoProcessRecursively
)
968 process(rControlPrimitive
.get2DDecomposition(getViewInformation2D()));
974 case PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D
:
976 // support for FIELD_SEQ_BEGIN, FIELD_SEQ_END and URL. It wraps text primitives (but is not limited to)
977 // thus do the MetafileAction embedding stuff but just handle recursively.
978 const primitive2d::TextHierarchyFieldPrimitive2D
& rFieldPrimitive
= static_cast< const primitive2d::TextHierarchyFieldPrimitive2D
& >(rCandidate
);
979 const OString
aCommentStringCommon("FIELD_SEQ_BEGIN");
980 const OString
aCommentStringPage("FIELD_SEQ_BEGIN;PageField");
981 const OString
aCommentStringEnd("FIELD_SEQ_END");
983 switch(rFieldPrimitive
.getType())
985 default : // case drawinglayer::primitive2d::FIELD_TYPE_COMMON :
987 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringCommon
));
990 case drawinglayer::primitive2d::FIELD_TYPE_PAGE
:
992 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringPage
));
995 case drawinglayer::primitive2d::FIELD_TYPE_URL
:
997 const OUString
& rURL
= rFieldPrimitive
.getString();
998 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringCommon
, 0, reinterpret_cast< const sal_uInt8
* >(rURL
.getStr()), 2 * rURL
.getLength()));
1003 // process recursively
1004 const primitive2d::Primitive2DContainer rContent
= rFieldPrimitive
.get2DDecomposition(getViewInformation2D());
1007 // for the end comment the type is not relevant yet, they are all the same. Just add.
1008 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringEnd
));
1010 if(mpPDFExtOutDevData
&& drawinglayer::primitive2d::FIELD_TYPE_URL
== rFieldPrimitive
.getType())
1012 // emulate data handling from ImpEditEngine::Paint
1013 const basegfx::B2DRange
aViewRange(rContent
.getB2DRange(getViewInformation2D()));
1014 const Rectangle
aRectLogic(
1015 (sal_Int32
)floor(aViewRange
.getMinX()), (sal_Int32
)floor(aViewRange
.getMinY()),
1016 (sal_Int32
)ceil(aViewRange
.getMaxX()), (sal_Int32
)ceil(aViewRange
.getMaxY()));
1017 vcl::PDFExtOutDevBookmarkEntry aBookmark
;
1018 aBookmark
.nLinkId
= mpPDFExtOutDevData
->CreateLink(aRectLogic
);
1019 aBookmark
.aBookmark
= rFieldPrimitive
.getString();
1020 std::vector
< vcl::PDFExtOutDevBookmarkEntry
>& rBookmarks
= mpPDFExtOutDevData
->GetBookmarks();
1021 rBookmarks
.push_back( aBookmark
);
1026 case PRIMITIVE2D_ID_TEXTHIERARCHYLINEPRIMITIVE2D
:
1028 const primitive2d::TextHierarchyLinePrimitive2D
& rLinePrimitive
= static_cast< const primitive2d::TextHierarchyLinePrimitive2D
& >(rCandidate
);
1029 const OString
aCommentString("XTEXT_EOL");
1031 // process recursively and add MetaFile comment
1032 process(rLinePrimitive
.get2DDecomposition(getViewInformation2D()));
1033 mpMetaFile
->AddAction(new MetaCommentAction(aCommentString
));
1037 case PRIMITIVE2D_ID_TEXTHIERARCHYBULLETPRIMITIVE2D
:
1039 // in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The
1040 // "XTEXT_EOC" is used, use here, too.
1041 const primitive2d::TextHierarchyBulletPrimitive2D
& rBulletPrimitive
= static_cast< const primitive2d::TextHierarchyBulletPrimitive2D
& >(rCandidate
);
1042 const OString
aCommentString("XTEXT_EOC");
1044 // process recursively and add MetaFile comment
1045 process(rBulletPrimitive
.get2DDecomposition(getViewInformation2D()));
1046 mpMetaFile
->AddAction(new MetaCommentAction(aCommentString
));
1050 case PRIMITIVE2D_ID_TEXTHIERARCHYPARAGRAPHPRIMITIVE2D
:
1052 const primitive2d::TextHierarchyParagraphPrimitive2D
& rParagraphPrimitive
= static_cast< const primitive2d::TextHierarchyParagraphPrimitive2D
& >(rCandidate
);
1053 const OString
aCommentString("XTEXT_EOP");
1055 if(mpPDFExtOutDevData
)
1057 // emulate data handling from ImpEditEngine::Paint
1058 mpPDFExtOutDevData
->BeginStructureElement( vcl::PDFWriter::Paragraph
);
1061 // process recursively and add MetaFile comment
1062 process(rParagraphPrimitive
.get2DDecomposition(getViewInformation2D()));
1063 mpMetaFile
->AddAction(new MetaCommentAction(aCommentString
));
1065 if(mpPDFExtOutDevData
)
1067 // emulate data handling from ImpEditEngine::Paint
1068 mpPDFExtOutDevData
->EndStructureElement();
1073 case PRIMITIVE2D_ID_TEXTHIERARCHYBLOCKPRIMITIVE2D
:
1075 const primitive2d::TextHierarchyBlockPrimitive2D
& rBlockPrimitive
= static_cast< const primitive2d::TextHierarchyBlockPrimitive2D
& >(rCandidate
);
1076 const OString
aCommentStringA("XTEXT_PAINTSHAPE_BEGIN");
1077 const OString
aCommentStringB("XTEXT_PAINTSHAPE_END");
1079 // add MetaFile comment, process recursively and add MetaFile comment
1080 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringA
));
1081 process(rBlockPrimitive
.get2DDecomposition(getViewInformation2D()));
1082 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringB
));
1086 case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D
:
1087 case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D
:
1089 // for supporting TEXT_ MetaFile actions there is more to do here; get the candidate
1090 const primitive2d::TextSimplePortionPrimitive2D
& rTextCandidate
= static_cast< const primitive2d::TextSimplePortionPrimitive2D
& >(rCandidate
);
1091 // const primitive2d::TextDecoratedPortionPrimitive2D* pTextDecoratedCandidate = dynamic_cast< const primitive2d::TextDecoratedPortionPrimitive2D* >(&rCandidate);
1093 // Adapt evtl. used special DrawMode
1094 const DrawModeFlags
nOriginalDrawMode(mpOutputDevice
->GetDrawMode());
1095 adaptTextToFillDrawMode();
1097 // directdraw of text simple portion; use default processing
1098 RenderTextSimpleOrDecoratedPortionPrimitive2D(rTextCandidate
);
1101 mpOutputDevice
->SetDrawMode(nOriginalDrawMode
);
1103 // #i101169# if(pTextDecoratedCandidate)
1105 // support for TEXT_ MetaFile actions only for decorated texts
1106 if(!mxBreakIterator
.is())
1108 uno::Reference
< uno::XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
1109 mxBreakIterator
= i18n::BreakIterator::create(xContext
);
1112 const OUString
& rTxt
= rTextCandidate
.getText();
1113 const sal_Int32
nTextLength(rTextCandidate
.getTextLength()); // rTxt.getLength());
1117 const css::lang::Locale
& rLocale
= rTextCandidate
.getLocale();
1118 const sal_Int32
nTextPosition(rTextCandidate
.getTextPosition());
1121 sal_Int32
nNextCellBreak(mxBreakIterator
->nextCharacters(rTxt
, nTextPosition
, rLocale
, css::i18n::CharacterIteratorMode::SKIPCELL
, 0, nDone
));
1122 css::i18n::Boundary
nNextWordBoundary(mxBreakIterator
->getWordBoundary(rTxt
, nTextPosition
, rLocale
, css::i18n::WordType::ANY_WORD
, true));
1123 sal_Int32
nNextSentenceBreak(mxBreakIterator
->endOfSentence(rTxt
, nTextPosition
, rLocale
));
1124 const OString
aCommentStringA("XTEXT_EOC");
1125 const OString
aCommentStringB("XTEXT_EOW");
1126 const OString
aCommentStringC("XTEXT_EOS");
1128 for(sal_Int32
i(nTextPosition
); i
< nTextPosition
+ nTextLength
; i
++)
1130 // create the entries for the respective break positions
1131 if(i
== nNextCellBreak
)
1133 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringA
, i
- nTextPosition
));
1134 nNextCellBreak
= mxBreakIterator
->nextCharacters(rTxt
, i
, rLocale
, css::i18n::CharacterIteratorMode::SKIPCELL
, 1, nDone
);
1136 if(i
== nNextWordBoundary
.endPos
)
1138 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringB
, i
- nTextPosition
));
1139 nNextWordBoundary
= mxBreakIterator
->getWordBoundary(rTxt
, i
+ 1, rLocale
, css::i18n::WordType::ANY_WORD
, true);
1141 if(i
== nNextSentenceBreak
)
1143 mpMetaFile
->AddAction(new MetaCommentAction(aCommentStringC
, i
- nTextPosition
));
1144 nNextSentenceBreak
= mxBreakIterator
->endOfSentence(rTxt
, i
+ 1, rLocale
);
1152 case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
:
1154 const primitive2d::PolygonHairlinePrimitive2D
& rHairlinePrimitive
= static_cast< const primitive2d::PolygonHairlinePrimitive2D
& >(rCandidate
);
1155 const basegfx::B2DPolygon
& rBasePolygon
= rHairlinePrimitive
.getB2DPolygon();
1157 if(rBasePolygon
.count() > (MAX_POLYGON_POINT_COUNT_METAFILE
- 1))
1159 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1160 // per polygon. If there are more, split the polygon in half and call recursively
1161 basegfx::B2DPolygon aLeft
, aRight
;
1162 splitLinePolygon(rBasePolygon
, aLeft
, aRight
);
1163 uno::Reference
< primitive2d::PolygonHairlinePrimitive2D
> xPLeft(new primitive2d::PolygonHairlinePrimitive2D(aLeft
, rHairlinePrimitive
.getBColor()));
1164 uno::Reference
< primitive2d::PolygonHairlinePrimitive2D
> xPRight(new primitive2d::PolygonHairlinePrimitive2D(aRight
, rHairlinePrimitive
.getBColor()));
1166 processBasePrimitive2D(*xPLeft
.get());
1167 processBasePrimitive2D(*xPRight
.get());
1171 // direct draw of hairline; use default processing
1172 // support SvtGraphicStroke MetaCommentAction
1173 const basegfx::BColor
aLineColor(maBColorModifierStack
.getModifiedColor(rHairlinePrimitive
.getBColor()));
1174 SvtGraphicStroke
* pSvtGraphicStroke
= nullptr;
1176 // #i121267# Not needed, does not give better quality compared with
1177 // the MetaActionType::POLYPOLYGON written by RenderPolygonHairlinePrimitive2D
1179 bool bSupportSvtGraphicStroke(false);
1181 if(bSupportSvtGraphicStroke
)
1183 pSvtGraphicStroke
= impTryToCreateSvtGraphicStroke(
1184 rHairlinePrimitive
.getB2DPolygon(),
1186 nullptr, nullptr, nullptr, nullptr);
1188 impStartSvtGraphicStroke(pSvtGraphicStroke
);
1191 RenderPolygonHairlinePrimitive2D(static_cast< const primitive2d::PolygonHairlinePrimitive2D
& >(rCandidate
), false);
1193 if(bSupportSvtGraphicStroke
)
1195 impEndSvtGraphicStroke(pSvtGraphicStroke
);
1200 case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
:
1202 const primitive2d::PolygonStrokePrimitive2D
& rStrokePrimitive
= static_cast< const primitive2d::PolygonStrokePrimitive2D
& >(rCandidate
);
1203 const basegfx::B2DPolygon
& rBasePolygon
= rStrokePrimitive
.getB2DPolygon();
1205 if(rBasePolygon
.count() > (MAX_POLYGON_POINT_COUNT_METAFILE
- 1))
1207 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1208 // per polygon. If there are more, split the polygon in half and call recursively
1209 basegfx::B2DPolygon aLeft
, aRight
;
1210 splitLinePolygon(rBasePolygon
, aLeft
, aRight
);
1211 uno::Reference
< primitive2d::PolygonStrokePrimitive2D
> xPLeft(new primitive2d::PolygonStrokePrimitive2D(
1212 aLeft
, rStrokePrimitive
.getLineAttribute(), rStrokePrimitive
.getStrokeAttribute()));
1213 uno::Reference
< primitive2d::PolygonStrokePrimitive2D
> xPRight(new primitive2d::PolygonStrokePrimitive2D(
1214 aRight
, rStrokePrimitive
.getLineAttribute(), rStrokePrimitive
.getStrokeAttribute()));
1216 processBasePrimitive2D(*xPLeft
.get());
1217 processBasePrimitive2D(*xPRight
.get());
1221 // support SvtGraphicStroke MetaCommentAction
1222 SvtGraphicStroke
* pSvtGraphicStroke
= impTryToCreateSvtGraphicStroke(
1223 rBasePolygon
, nullptr,
1224 &rStrokePrimitive
.getLineAttribute(),
1225 &rStrokePrimitive
.getStrokeAttribute(),
1228 impStartSvtGraphicStroke(pSvtGraphicStroke
);
1229 const attribute::LineAttribute
& rLine
= rStrokePrimitive
.getLineAttribute();
1231 // create MetaPolyLineActions, but without LINE_DASH
1232 if(basegfx::fTools::more(rLine
.getWidth(), 0.0))
1234 const attribute::StrokeAttribute
& rStroke
= rStrokePrimitive
.getStrokeAttribute();
1235 basegfx::B2DPolyPolygon aHairLinePolyPolygon
;
1237 if(0.0 == rStroke
.getFullDotDashLen())
1239 aHairLinePolyPolygon
.append(rBasePolygon
);
1243 basegfx::tools::applyLineDashing(
1244 rBasePolygon
, rStroke
.getDotDashArray(),
1245 &aHairLinePolyPolygon
, nullptr, rStroke
.getFullDotDashLen());
1248 const basegfx::BColor
aHairlineColor(maBColorModifierStack
.getModifiedColor(rLine
.getColor()));
1249 mpOutputDevice
->SetLineColor(Color(aHairlineColor
));
1250 mpOutputDevice
->SetFillColor();
1251 aHairLinePolyPolygon
.transform(maCurrentTransformation
);
1253 // use the transformed line width
1254 LineInfo
aLineInfo(LINE_SOLID
, basegfx::fround(getTransformedLineWidth(rLine
.getWidth())));
1255 aLineInfo
.SetLineJoin(rLine
.getLineJoin());
1256 aLineInfo
.SetLineCap(rLine
.getLineCap());
1258 for(sal_uInt32
a(0); a
< aHairLinePolyPolygon
.count(); a
++)
1260 const basegfx::B2DPolygon
aCandidate(aHairLinePolyPolygon
.getB2DPolygon(a
));
1262 if(aCandidate
.count() > 1)
1264 const ::tools::Polygon
aToolsPolygon(aCandidate
);
1266 mpMetaFile
->AddAction(new MetaPolyLineAction(aToolsPolygon
, aLineInfo
));
1272 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
1275 impEndSvtGraphicStroke(pSvtGraphicStroke
);
1280 case PRIMITIVE2D_ID_POLYGONSTROKEARROWPRIMITIVE2D
:
1282 const primitive2d::PolygonStrokeArrowPrimitive2D
& rStrokeArrowPrimitive
= static_cast< const primitive2d::PolygonStrokeArrowPrimitive2D
& >(rCandidate
);
1283 const basegfx::B2DPolygon
& rBasePolygon
= rStrokeArrowPrimitive
.getB2DPolygon();
1285 if(rBasePolygon
.count() > (MAX_POLYGON_POINT_COUNT_METAFILE
- 1))
1287 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1288 // per polygon. If there are more, split the polygon in half and call recursively
1289 basegfx::B2DPolygon aLeft
, aRight
;
1290 splitLinePolygon(rBasePolygon
, aLeft
, aRight
);
1291 const attribute::LineStartEndAttribute aEmpty
;
1292 uno::Reference
< primitive2d::PolygonStrokeArrowPrimitive2D
> xPLeft(new primitive2d::PolygonStrokeArrowPrimitive2D(
1294 rStrokeArrowPrimitive
.getLineAttribute(),
1295 rStrokeArrowPrimitive
.getStrokeAttribute(),
1296 rStrokeArrowPrimitive
.getStart(),
1298 uno::Reference
< primitive2d::PolygonStrokeArrowPrimitive2D
> xPRight(new primitive2d::PolygonStrokeArrowPrimitive2D(
1300 rStrokeArrowPrimitive
.getLineAttribute(),
1301 rStrokeArrowPrimitive
.getStrokeAttribute(),
1303 rStrokeArrowPrimitive
.getEnd()));
1305 processBasePrimitive2D(*xPLeft
.get());
1306 processBasePrimitive2D(*xPRight
.get());
1310 // support SvtGraphicStroke MetaCommentAction
1311 SvtGraphicStroke
* pSvtGraphicStroke
= impTryToCreateSvtGraphicStroke(
1312 rBasePolygon
, nullptr,
1313 &rStrokeArrowPrimitive
.getLineAttribute(),
1314 &rStrokeArrowPrimitive
.getStrokeAttribute(),
1315 &rStrokeArrowPrimitive
.getStart(),
1316 &rStrokeArrowPrimitive
.getEnd());
1318 // write LineGeometry start marker
1319 impStartSvtGraphicStroke(pSvtGraphicStroke
);
1321 // #i116162# When B&W is set as DrawMode, DrawModeFlags::WhiteFill is used
1322 // to let all fills be just white; for lines DrawModeFlags::BlackLine is used
1323 // so all line geometry is supposed to get black. Since in the in-between
1324 // stages of line geometry drawing filled polygons are used (e.g. line
1325 // start/ends) it is necessary to change these drawmodes to preserve
1326 // that lines shall be black; thus change DrawModeFlags::WhiteFill to
1327 // DrawModeFlags::BlackFill during line geometry processing to have line geometry
1328 // parts filled black.
1329 const DrawModeFlags
nOldDrawMode(mpOutputDevice
->GetDrawMode());
1330 const bool bDrawmodeChange(nOldDrawMode
& DrawModeFlags::WhiteFill
&& mnSvtGraphicStrokeCount
);
1334 mpOutputDevice
->SetDrawMode((nOldDrawMode
& ~DrawModeFlags::WhiteFill
) | DrawModeFlags::BlackFill
);
1337 // process sub-line geometry (evtl. filled PolyPolygons)
1338 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
1342 mpOutputDevice
->SetDrawMode(nOldDrawMode
);
1345 // write LineGeometry end marker
1346 impEndSvtGraphicStroke(pSvtGraphicStroke
);
1351 case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D
:
1353 // direct draw of transformed BitmapEx primitive; use default processing, but without
1354 // former testing if graphic content is inside discrete local viewport; this is not
1355 // setup for metafile targets (metafile renderer tries to render in logic coordinates,
1356 // the mapping is kept to the OutputDevice for better Metafile recording)
1357 RenderBitmapPrimitive2D(static_cast< const primitive2d::BitmapPrimitive2D
& >(rCandidate
));
1360 case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D
:
1362 // need to handle PolyPolygonGraphicPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
1363 const primitive2d::PolyPolygonGraphicPrimitive2D
& rBitmapCandidate
= static_cast< const primitive2d::PolyPolygonGraphicPrimitive2D
& >(rCandidate
);
1364 basegfx::B2DPolyPolygon
aLocalPolyPolygon(rBitmapCandidate
.getB2DPolyPolygon());
1366 if(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon
))
1368 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1369 // per polygon. If there are more use the splitted polygon and call recursively
1370 uno::Reference
< primitive2d::PolyPolygonGraphicPrimitive2D
> xSplitted(new primitive2d::PolyPolygonGraphicPrimitive2D(
1372 rBitmapCandidate
.getFillGraphic()));
1374 processBasePrimitive2D(*xSplitted
.get());
1378 SvtGraphicFill
* pSvtGraphicFill
= nullptr;
1380 if(!mnSvtGraphicFillCount
&& aLocalPolyPolygon
.count())
1382 // #121194# Changed implementation and checked usages fo convert to metafile,
1383 // presentation start (uses SvtGraphicFill) and printing.
1385 // calculate transformation. Get real object size, all values in FillGraphicAttribute
1386 // are relative to the unified object
1387 aLocalPolyPolygon
.transform(maCurrentTransformation
);
1388 const basegfx::B2DVector
aOutlineSize(aLocalPolyPolygon
.getB2DRange().getRange());
1390 // the scaling needs scale from pixel to logic coordinate system
1391 const attribute::FillGraphicAttribute
& rFillGraphicAttribute
= rBitmapCandidate
.getFillGraphic();
1392 const Size
aBmpSizePixel(rFillGraphicAttribute
.getGraphic().GetSizePixel());
1394 // setup transformation like in impgrfll. Multiply with aOutlineSize
1395 // to get from unit coordinates in rFillGraphicAttribute.getGraphicRange()
1396 // to object coordinates with object's top left being at (0,0). Divide
1397 // by pixel size so that scale from pixel to logic will work in SvtGraphicFill.
1398 const basegfx::B2DVector
aTransformScale(
1399 rFillGraphicAttribute
.getGraphicRange().getRange() /
1401 std::max(1.0, double(aBmpSizePixel
.Width())),
1402 std::max(1.0, double(aBmpSizePixel
.Height()))) *
1404 const basegfx::B2DPoint
aTransformPosition(
1405 rFillGraphicAttribute
.getGraphicRange().getMinimum() * aOutlineSize
);
1407 // setup transformation like in impgrfll
1408 SvtGraphicFill::Transform aTransform
;
1410 // scale values are divided by bitmap pixel sizes
1411 aTransform
.matrix
[0] = aTransformScale
.getX();
1412 aTransform
.matrix
[4] = aTransformScale
.getY();
1414 // translates are absolute
1415 aTransform
.matrix
[2] = aTransformPosition
.getX();
1416 aTransform
.matrix
[5] = aTransformPosition
.getY();
1418 pSvtGraphicFill
= new SvtGraphicFill(
1419 getFillPolyPolygon(aLocalPolyPolygon
),
1422 SvtGraphicFill::fillEvenOdd
,
1423 SvtGraphicFill::fillTexture
,
1425 rFillGraphicAttribute
.getTiling(),
1426 SvtGraphicFill::hatchSingle
,
1428 SvtGraphicFill::gradientLinear
,
1432 rFillGraphicAttribute
.getGraphic());
1435 // Do use decomposition; encapsulate with SvtGraphicFill
1436 impStartSvtGraphicFill(pSvtGraphicFill
);
1437 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
1438 impEndSvtGraphicFill(pSvtGraphicFill
);
1443 case PRIMITIVE2D_ID_POLYPOLYGONHATCHPRIMITIVE2D
:
1445 // need to handle PolyPolygonHatchPrimitive2D here to support XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END
1446 const primitive2d::PolyPolygonHatchPrimitive2D
& rHatchCandidate
= static_cast< const primitive2d::PolyPolygonHatchPrimitive2D
& >(rCandidate
);
1447 const attribute::FillHatchAttribute
& rFillHatchAttribute
= rHatchCandidate
.getFillHatch();
1448 basegfx::B2DPolyPolygon
aLocalPolyPolygon(rHatchCandidate
.getB2DPolyPolygon());
1450 if(aLocalPolyPolygon
.getB2DRange() != rHatchCandidate
.getDefinitionRange())
1452 // the range which defines the hatch is different from the range of the
1453 // geometry (used for writer frames). This cannot be done calling vcl, thus use
1454 // decomposition here
1455 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
1459 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1460 // per polygon. Split polygon until there are less than that
1461 while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon
))
1464 if(rFillHatchAttribute
.isFillBackground())
1466 // with fixing #i111954# (see below) the possible background
1467 // fill of a hatched object was lost.Generate a background fill
1468 // primitive and render it
1469 const primitive2d::Primitive2DReference
xBackground(
1470 new primitive2d::PolyPolygonColorPrimitive2D(
1472 rHatchCandidate
.getBackgroundColor()));
1474 process(primitive2d::Primitive2DContainer
{ xBackground
});
1477 SvtGraphicFill
* pSvtGraphicFill
= nullptr;
1478 aLocalPolyPolygon
.transform(maCurrentTransformation
);
1480 if(!mnSvtGraphicFillCount
&& aLocalPolyPolygon
.count())
1482 // re-create a VCL hatch as base data
1483 SvtGraphicFill::HatchType
eHatch(SvtGraphicFill::hatchSingle
);
1485 switch(rFillHatchAttribute
.getStyle())
1487 default: // attribute::HatchStyle::Single :
1489 eHatch
= SvtGraphicFill::hatchSingle
;
1492 case attribute::HatchStyle::Double
:
1494 eHatch
= SvtGraphicFill::hatchDouble
;
1497 case attribute::HatchStyle::Triple
:
1499 eHatch
= SvtGraphicFill::hatchTriple
;
1504 SvtGraphicFill::Transform aTransform
;
1507 aTransform
.matrix
[0] *= rFillHatchAttribute
.getDistance();
1508 aTransform
.matrix
[4] *= rFillHatchAttribute
.getDistance();
1510 // rotate (was never correct in impgrfll anyways, use correct angle now)
1511 aTransform
.matrix
[0] *= cos(rFillHatchAttribute
.getAngle());
1512 aTransform
.matrix
[1] *= -sin(rFillHatchAttribute
.getAngle());
1513 aTransform
.matrix
[3] *= sin(rFillHatchAttribute
.getAngle());
1514 aTransform
.matrix
[4] *= cos(rFillHatchAttribute
.getAngle());
1516 pSvtGraphicFill
= new SvtGraphicFill(
1517 getFillPolyPolygon(aLocalPolyPolygon
),
1520 SvtGraphicFill::fillEvenOdd
,
1521 SvtGraphicFill::fillHatch
,
1525 Color(rFillHatchAttribute
.getColor()),
1526 SvtGraphicFill::gradientLinear
,
1533 // Do use decomposition; encapsulate with SvtGraphicFill
1534 impStartSvtGraphicFill(pSvtGraphicFill
);
1536 // #i111954# do NOT use decomposition, but use direct VCL-command
1537 // process(rCandidate.get2DDecomposition(getViewInformation2D()));
1538 const ::tools::PolyPolygon
aToolsPolyPolygon(basegfx::tools::adaptiveSubdivideByAngle(aLocalPolyPolygon
));
1539 const HatchStyle
aHatchStyle(
1540 attribute::HatchStyle::Single
== rFillHatchAttribute
.getStyle() ? HATCH_SINGLE
:
1541 attribute::HatchStyle::Double
== rFillHatchAttribute
.getStyle() ? HATCH_DOUBLE
:
1544 mpOutputDevice
->DrawHatch(aToolsPolyPolygon
,
1546 Color(rFillHatchAttribute
.getColor()),
1547 basegfx::fround(rFillHatchAttribute
.getDistance()),
1548 basegfx::fround(rFillHatchAttribute
.getAngle() / F_PI1800
)));
1550 impEndSvtGraphicFill(pSvtGraphicFill
);
1554 case PRIMITIVE2D_ID_POLYPOLYGONGRADIENTPRIMITIVE2D
:
1556 basegfx::B2DVector aScale
, aTranslate
;
1557 double fRotate
, fShearX
;
1559 maCurrentTransformation
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
1561 if(!basegfx::fTools::equalZero(fRotate
) || !basegfx::fTools::equalZero(fShearX
))
1563 // #i121185# When rotation or shear is used, a VCL Gradient cannot be used directly.
1564 // This is because VCL Gradient mechanism does *not* support to rotate the gradient
1565 // with objects and this case is not expressable in a Metafile (and cannot be added
1566 // since the FileFormats used, e.g. *.wmf, do not support it either).
1567 // Such cases happen when a graphic object uses a Metafile as graphic information or
1568 // a fill style definition uses a Metafile. In this cases the graphic content is
1569 // rotated with the graphic or filled object; this is not supported by the target
1570 // format of this conversion renderer - Metafiles.
1571 // To solve this, not a Gradient is written, but the decomposition of this object
1572 // is written to the Metafile. This is the PolyPolygons building the gradient fill.
1573 // These will need more space and time, but the result will be as if the Gradient
1574 // was rotated with the object.
1575 // This mechanism is used by all exporters still not using Primtives (e.g. Print,
1576 // Slideshow, Export rto PDF, export to Picture, ...) but relying on Metafile
1577 // transfers. One more reason to *change* these to primitives.
1578 // BTW: One more example how useful the principles of primitives are; the decomposition
1579 // is by definition a simpler, maybe more expensive representation of the same content.
1580 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
1584 const primitive2d::PolyPolygonGradientPrimitive2D
& rGradientCandidate
= static_cast< const primitive2d::PolyPolygonGradientPrimitive2D
& >(rCandidate
);
1585 basegfx::B2DPolyPolygon
aLocalPolyPolygon(rGradientCandidate
.getB2DPolyPolygon());
1587 if(aLocalPolyPolygon
.getB2DRange() != rGradientCandidate
.getDefinitionRange())
1589 // the range which defines the gradient is different from the range of the
1590 // geometry (used for writer frames). This cannot be done calling vcl, thus use
1591 // decomposition here
1592 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
1596 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1597 // per polygon. Split polygon until there are less than that
1598 while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon
))
1601 // for support of MetaCommentActions of the form XGRAD_SEQ_BEGIN, XGRAD_SEQ_END
1602 // it is safest to use the VCL OutputDevice::DrawGradient method which creates those.
1603 // re-create a VCL-gradient from FillGradientPrimitive2D and the needed tools PolyPolygon
1604 Gradient aVCLGradient
;
1605 impConvertFillGradientAttributeToVCLGradient(aVCLGradient
, rGradientCandidate
.getFillGradient(), false);
1606 aLocalPolyPolygon
.transform(maCurrentTransformation
);
1608 // #i82145# ATM VCL printing of gradients using curved shapes does not work,
1609 // i submitted the bug with the given ID to THB. When that task is fixed it is
1610 // necessary to again remove this subdivision since it decreases possible
1611 // printing quality (not even resolution-dependent for now). THB will tell
1612 // me when that task is fixed in the master
1613 const ::tools::PolyPolygon
aToolsPolyPolygon(
1615 basegfx::tools::adaptiveSubdivideByAngle(aLocalPolyPolygon
)));
1618 // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1619 SvtGraphicFill
* pSvtGraphicFill
= nullptr;
1621 if(!mnSvtGraphicFillCount
&& aLocalPolyPolygon
.count())
1623 // setup gradient stuff like in like in impgrfll
1624 SvtGraphicFill::GradientType
eGrad(SvtGraphicFill::gradientLinear
);
1626 switch(aVCLGradient
.GetStyle())
1628 default : // GradientStyle_LINEAR:
1629 case GradientStyle_AXIAL
:
1630 eGrad
= SvtGraphicFill::gradientLinear
;
1632 case GradientStyle_RADIAL
:
1633 case GradientStyle_ELLIPTICAL
:
1634 eGrad
= SvtGraphicFill::gradientRadial
;
1636 case GradientStyle_SQUARE
:
1637 case GradientStyle_RECT
:
1638 eGrad
= SvtGraphicFill::gradientRectangular
;
1642 pSvtGraphicFill
= new SvtGraphicFill(
1646 SvtGraphicFill::fillEvenOdd
,
1647 SvtGraphicFill::fillGradient
,
1648 SvtGraphicFill::Transform(),
1650 SvtGraphicFill::hatchSingle
,
1653 aVCLGradient
.GetStartColor(),
1654 aVCLGradient
.GetEndColor(),
1655 aVCLGradient
.GetSteps(),
1659 // call VCL directly; encapsulate with SvtGraphicFill
1660 impStartSvtGraphicFill(pSvtGraphicFill
);
1661 mpOutputDevice
->DrawGradient(aToolsPolyPolygon
, aVCLGradient
);
1662 impEndSvtGraphicFill(pSvtGraphicFill
);
1666 case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D
:
1668 const primitive2d::PolyPolygonColorPrimitive2D
& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D
& >(rCandidate
));
1669 basegfx::B2DPolyPolygon
aLocalPolyPolygon(rPolygonCandidate
.getB2DPolyPolygon());
1671 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1672 // per polygon. Split polygon until there are less than that
1673 while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon
))
1676 const basegfx::BColor
aPolygonColor(maBColorModifierStack
.getModifiedColor(rPolygonCandidate
.getBColor()));
1677 aLocalPolyPolygon
.transform(maCurrentTransformation
);
1679 // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1680 SvtGraphicFill
* pSvtGraphicFill
= nullptr;
1682 // #i121267# Not needed, does not give better quality compared with
1683 // the MetaActionType::POLYPOLYGON written by the DrawPolyPolygon command
1685 bool bSupportSvtGraphicFill(false);
1687 if(bSupportSvtGraphicFill
&& !mnSvtGraphicFillCount
&& aLocalPolyPolygon
.count())
1689 // setup simple color fill stuff like in impgrfll
1690 pSvtGraphicFill
= new SvtGraphicFill(
1691 getFillPolyPolygon(aLocalPolyPolygon
),
1692 Color(aPolygonColor
),
1694 SvtGraphicFill::fillEvenOdd
,
1695 SvtGraphicFill::fillSolid
,
1696 SvtGraphicFill::Transform(),
1698 SvtGraphicFill::hatchSingle
,
1700 SvtGraphicFill::gradientLinear
,
1707 // set line and fill color
1708 mpOutputDevice
->SetFillColor(Color(aPolygonColor
));
1709 mpOutputDevice
->SetLineColor();
1711 // call VCL directly; encapsulate with SvtGraphicFill
1712 if(bSupportSvtGraphicFill
)
1714 impStartSvtGraphicFill(pSvtGraphicFill
);
1717 mpOutputDevice
->DrawPolyPolygon(aLocalPolyPolygon
);
1719 if(bSupportSvtGraphicFill
)
1721 impEndSvtGraphicFill(pSvtGraphicFill
);
1726 case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D
:
1728 static bool bUseMetaFilePrimitiveDecomposition(true);
1729 const primitive2d::MetafilePrimitive2D
& aMetafile
= static_cast< const primitive2d::MetafilePrimitive2D
& >(rCandidate
);
1731 if(bUseMetaFilePrimitiveDecomposition
&& !aMetafile
.getMetaFile().GetUseCanvas())
1733 // Use new Metafile decomposition.
1734 // TODO EMF+ stuffed into METACOMMENT support required
1735 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
1739 // direct draw of MetaFile, use default processing
1740 RenderMetafilePrimitive2D(aMetafile
);
1745 case PRIMITIVE2D_ID_MASKPRIMITIVE2D
:
1747 // mask group. Special handling for MetaFiles.
1748 const primitive2d::MaskPrimitive2D
& rMaskCandidate
= static_cast< const primitive2d::MaskPrimitive2D
& >(rCandidate
);
1750 if(!rMaskCandidate
.getChildren().empty())
1752 basegfx::B2DPolyPolygon
aMask(rMaskCandidate
.getMask());
1756 // prepare new mask polygon and rescue current one
1757 aMask
.transform(maCurrentTransformation
);
1758 const basegfx::B2DPolyPolygon
aLastClipPolyPolygon(maClipPolyPolygon
);
1760 if(maClipPolyPolygon
.count())
1762 // there is already a clip polygon set; build clipped union of
1763 // current mask polygon and new one
1764 maClipPolyPolygon
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
1767 true, // #i106516# we want the inside of aMask, not the outside
1772 // use mask directly
1773 maClipPolyPolygon
= aMask
;
1776 if(maClipPolyPolygon
.count())
1778 // set VCL clip region; subdivide before conversion to tools polygon. Subdivision necessary (!)
1779 // Removed subdivision and fixed in vcl::Region::ImplPolyPolyRegionToBandRegionFunc() in VCL where
1780 // the ClipRegion is built from the Polygon. A AdaptiveSubdivide on the source polygon was missing there
1781 mpOutputDevice
->Push(PushFlags::CLIPREGION
);
1782 mpOutputDevice
->SetClipRegion(vcl::Region(maClipPolyPolygon
));
1784 // recursively paint content
1785 // #i121267# Only need to process sub-content when clip polygon is *not* empty.
1786 // If it is empty, the clip is empty and there can be nothing inside.
1787 process(rMaskCandidate
.getChildren());
1789 // restore VCL clip region
1790 mpOutputDevice
->Pop();
1793 // restore to rescued clip polygon
1794 maClipPolyPolygon
= aLastClipPolyPolygon
;
1798 // no mask, no clipping. recursively paint content
1799 process(rMaskCandidate
.getChildren());
1805 case PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D
:
1807 // modified color group. Force output to unified color. Use default pocessing.
1808 RenderModifiedColorPrimitive2D(static_cast< const primitive2d::ModifiedColorPrimitive2D
& >(rCandidate
));
1811 case PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D
:
1813 // for metafile: Need to examine what the pure vcl version is doing here actually
1814 // - uses DrawTransparent with metafile for content and a gradient
1815 // - uses DrawTransparent for single PolyPoylgons directly. Can be detected by
1816 // checking the content for single PolyPolygonColorPrimitive2D
1817 const primitive2d::UnifiedTransparencePrimitive2D
& rUniTransparenceCandidate
= static_cast< const primitive2d::UnifiedTransparencePrimitive2D
& >(rCandidate
);
1818 const primitive2d::Primitive2DContainer
& rContent
= rUniTransparenceCandidate
.getChildren();
1820 if(!rContent
.empty())
1822 if(0.0 == rUniTransparenceCandidate
.getTransparence())
1824 // not transparent at all, use content
1825 process(rUniTransparenceCandidate
.getChildren());
1827 else if(rUniTransparenceCandidate
.getTransparence() > 0.0 && rUniTransparenceCandidate
.getTransparence() < 1.0)
1829 // try to identify a single PolyPolygonColorPrimitive2D in the
1830 // content part of the transparence primitive
1831 const primitive2d::PolyPolygonColorPrimitive2D
* pPoPoColor
= nullptr;
1832 static bool bForceToMetafile(false);
1834 if(!bForceToMetafile
&& 1 == rContent
.size())
1836 const primitive2d::Primitive2DReference
xReference(rContent
[0]);
1837 pPoPoColor
= dynamic_cast< const primitive2d::PolyPolygonColorPrimitive2D
* >(xReference
.get());
1840 // PolyPolygonGradientPrimitive2D, PolyPolygonHatchPrimitive2D and
1841 // PolyPolygonGraphicPrimitive2D are derived from PolyPolygonColorPrimitive2D.
1842 // Check also for correct ID to exclude derived implementations
1843 if(pPoPoColor
&& PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D
== pPoPoColor
->getPrimitive2DID())
1845 // single transparent tools::PolyPolygon identified, use directly
1846 const basegfx::BColor
aPolygonColor(maBColorModifierStack
.getModifiedColor(pPoPoColor
->getBColor()));
1847 basegfx::B2DPolyPolygon
aLocalPolyPolygon(pPoPoColor
->getB2DPolyPolygon());
1849 // #i112245# Metafiles use tools Polygon and are not able to have more than 65535 points
1850 // per polygon. Split polygon until there are less than that
1851 while(fillPolyPolygonNeededToBeSplit(aLocalPolyPolygon
))
1855 aLocalPolyPolygon
.transform(maCurrentTransformation
);
1857 // XPATHFILL_SEQ_BEGIN/XPATHFILL_SEQ_END support
1858 SvtGraphicFill
* pSvtGraphicFill
= nullptr;
1860 // #i121267# Not needed, does not give better quality compared with
1861 // the MetaActionType::POLYPOLYGON written by the DrawPolyPolygon command
1863 bool bSupportSvtGraphicFill(false);
1865 if(bSupportSvtGraphicFill
&& !mnSvtGraphicFillCount
&& aLocalPolyPolygon
.count())
1867 // setup simple color with transparence fill stuff like in impgrfll
1868 pSvtGraphicFill
= new SvtGraphicFill(
1869 getFillPolyPolygon(aLocalPolyPolygon
),
1870 Color(aPolygonColor
),
1871 rUniTransparenceCandidate
.getTransparence(),
1872 SvtGraphicFill::fillEvenOdd
,
1873 SvtGraphicFill::fillSolid
,
1874 SvtGraphicFill::Transform(),
1876 SvtGraphicFill::hatchSingle
,
1878 SvtGraphicFill::gradientLinear
,
1885 // set line and fill color
1886 const sal_uInt16
nTransPercentVcl((sal_uInt16
)basegfx::fround(rUniTransparenceCandidate
.getTransparence() * 100.0));
1887 mpOutputDevice
->SetFillColor(Color(aPolygonColor
));
1888 mpOutputDevice
->SetLineColor();
1890 // call VCL directly; encapsulate with SvtGraphicFill
1891 if(bSupportSvtGraphicFill
)
1893 impStartSvtGraphicFill(pSvtGraphicFill
);
1896 mpOutputDevice
->DrawTransparent(
1897 ::tools::PolyPolygon(aLocalPolyPolygon
),
1900 if(bSupportSvtGraphicFill
)
1902 impEndSvtGraphicFill(pSvtGraphicFill
);
1907 // save old mfCurrentUnifiedTransparence and set new one
1908 // so that contained SvtGraphicStroke may use the current one
1909 const double fLastCurrentUnifiedTransparence(mfCurrentUnifiedTransparence
);
1910 // #i105377# paint the content metafile opaque as the transparency gets
1911 // split of into the gradient below
1912 // mfCurrentUnifiedTransparence = rUniTransparenceCandidate.getTransparence();
1913 mfCurrentUnifiedTransparence
= 0;
1915 // various content, create content-metafile
1916 GDIMetaFile aContentMetafile
;
1917 const Rectangle
aPrimitiveRectangle(impDumpToMetaFile(rContent
, aContentMetafile
));
1919 // restore mfCurrentUnifiedTransparence; it may have been used
1920 // while processing the sub-content in impDumpToMetaFile
1921 mfCurrentUnifiedTransparence
= fLastCurrentUnifiedTransparence
;
1923 // create uniform VCL gradient for uniform transparency
1924 Gradient aVCLGradient
;
1925 const sal_uInt8
nTransPercentVcl((sal_uInt8
)basegfx::fround(rUniTransparenceCandidate
.getTransparence() * 255.0));
1926 const Color
aTransColor(nTransPercentVcl
, nTransPercentVcl
, nTransPercentVcl
);
1928 aVCLGradient
.SetStyle(GradientStyle_LINEAR
);
1929 aVCLGradient
.SetStartColor(aTransColor
);
1930 aVCLGradient
.SetEndColor(aTransColor
);
1931 aVCLGradient
.SetAngle(0);
1932 aVCLGradient
.SetBorder(0);
1933 aVCLGradient
.SetOfsX(0);
1934 aVCLGradient
.SetOfsY(0);
1935 aVCLGradient
.SetStartIntensity(100);
1936 aVCLGradient
.SetEndIntensity(100);
1937 aVCLGradient
.SetSteps(2);
1940 mpOutputDevice
->DrawTransparent(
1941 aContentMetafile
, aPrimitiveRectangle
.TopLeft(),
1942 aPrimitiveRectangle
.GetSize(), aVCLGradient
);
1949 case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D
:
1951 // for metafile: Need to examine what the pure vcl version is doing here actually
1952 // - uses DrawTransparent with metafile for content and a gradient
1953 // i can detect this here with checking the gradient part for a single
1954 // FillGradientPrimitive2D and reconstruct the gradient.
1955 // If that detection goes wrong, I have to create an transparence-blended bitmap. Eventually
1956 // do that in stripes, else RenderTransparencePrimitive2D may just be used
1957 const primitive2d::TransparencePrimitive2D
& rTransparenceCandidate
= static_cast< const primitive2d::TransparencePrimitive2D
& >(rCandidate
);
1958 const primitive2d::Primitive2DContainer
& rContent
= rTransparenceCandidate
.getChildren();
1959 const primitive2d::Primitive2DContainer
& rTransparence
= rTransparenceCandidate
.getTransparence();
1961 if(!rContent
.empty() && !rTransparence
.empty())
1963 // try to identify a single FillGradientPrimitive2D in the
1964 // transparence part of the primitive
1965 const primitive2d::FillGradientPrimitive2D
* pFiGradient
= nullptr;
1966 static bool bForceToBigTransparentVDev(false);
1968 if(!bForceToBigTransparentVDev
&& 1 == rTransparence
.size())
1970 const primitive2d::Primitive2DReference
xReference(rTransparence
[0]);
1971 pFiGradient
= dynamic_cast< const primitive2d::FillGradientPrimitive2D
* >(xReference
.get());
1974 // Check also for correct ID to exclude derived implementations
1975 if(pFiGradient
&& PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D
== pFiGradient
->getPrimitive2DID())
1977 // various content, create content-metafile
1978 GDIMetaFile aContentMetafile
;
1979 const Rectangle
aPrimitiveRectangle(impDumpToMetaFile(rContent
, aContentMetafile
));
1981 // re-create a VCL-gradient from FillGradientPrimitive2D
1982 Gradient aVCLGradient
;
1983 impConvertFillGradientAttributeToVCLGradient(aVCLGradient
, pFiGradient
->getFillGradient(), true);
1986 mpOutputDevice
->DrawTransparent(
1987 aContentMetafile
, aPrimitiveRectangle
.TopLeft(),
1988 aPrimitiveRectangle
.GetSize(), aVCLGradient
);
1992 // sub-transparence group. Draw to VDev first.
1993 // this may get refined to tiling when resolution is too big here
1995 // need to avoid switching off MapMode stuff here; maybe need another
1996 // tooling class, cannot just do the same as with the pixel renderer.
1997 // Need to experiment...
1999 // Okay, basic implementation finished and tested. The DPI stuff was hard
2000 // and not easy to find out that it's needed.
2001 // Since this will not yet happen normally (as long as no one constructs
2002 // transparence primitives with non-trivial transparence content) i will for now not
2003 // refine to tiling here.
2005 basegfx::B2DRange
aViewRange(rContent
.getB2DRange(getViewInformation2D()));
2006 aViewRange
.transform(maCurrentTransformation
);
2007 const Rectangle
aRectLogic(
2008 (sal_Int32
)floor(aViewRange
.getMinX()), (sal_Int32
)floor(aViewRange
.getMinY()),
2009 (sal_Int32
)ceil(aViewRange
.getMaxX()), (sal_Int32
)ceil(aViewRange
.getMaxY()));
2010 const Rectangle
aRectPixel(mpOutputDevice
->LogicToPixel(aRectLogic
));
2011 Size
aSizePixel(aRectPixel
.GetSize());
2012 const Point aEmptyPoint
;
2013 ScopedVclPtrInstance
< VirtualDevice
> aBufferDevice
;
2014 const sal_uInt32
nMaxQuadratPixels(500000);
2015 const sal_uInt32
nViewVisibleArea(aSizePixel
.getWidth() * aSizePixel
.getHeight());
2016 double fReduceFactor(1.0);
2018 if(nViewVisibleArea
> nMaxQuadratPixels
)
2020 // reduce render size
2021 fReduceFactor
= sqrt((double)nMaxQuadratPixels
/ (double)nViewVisibleArea
);
2022 aSizePixel
= Size(basegfx::fround((double)aSizePixel
.getWidth() * fReduceFactor
),
2023 basegfx::fround((double)aSizePixel
.getHeight() * fReduceFactor
));
2026 if(aBufferDevice
->SetOutputSizePixel(aSizePixel
))
2028 // create and set MapModes for target devices
2029 MapMode
aNewMapMode(mpOutputDevice
->GetMapMode());
2030 aNewMapMode
.SetOrigin(Point(-aRectLogic
.Left(), -aRectLogic
.Top()));
2031 aBufferDevice
->SetMapMode(aNewMapMode
);
2033 // prepare view transformation for target renderers
2034 // ATTENTION! Need to apply another scaling because of the potential DPI differences
2035 // between Printer and VDev (mpOutputDevice and aBufferDevice here).
2036 // To get the DPI, LogicToPixel from (1,1) from MAP_INCH needs to be used.
2037 basegfx::B2DHomMatrix
aViewTransform(aBufferDevice
->GetViewTransformation());
2038 const Size
aDPIOld(mpOutputDevice
->LogicToPixel(Size(1, 1), MAP_INCH
));
2039 const Size
aDPINew(aBufferDevice
->LogicToPixel(Size(1, 1), MAP_INCH
));
2040 const double fDPIXChange((double)aDPIOld
.getWidth() / (double)aDPINew
.getWidth());
2041 const double fDPIYChange((double)aDPIOld
.getHeight() / (double)aDPINew
.getHeight());
2043 if(!basegfx::fTools::equal(fDPIXChange
, 1.0) || !basegfx::fTools::equal(fDPIYChange
, 1.0))
2045 aViewTransform
.scale(fDPIXChange
, fDPIYChange
);
2048 // also take scaling from Size reduction into acount
2049 if(!basegfx::fTools::equal(fReduceFactor
, 1.0))
2051 aViewTransform
.scale(fReduceFactor
, fReduceFactor
);
2054 // create view information and pixel renderer. Reuse known ViewInformation
2055 // except new transformation and range
2056 const geometry::ViewInformation2D
aViewInfo(
2057 getViewInformation2D().getObjectTransformation(),
2060 getViewInformation2D().getVisualizedPage(),
2061 getViewInformation2D().getViewTime(),
2062 getViewInformation2D().getExtendedInformationSequence());
2064 VclPixelProcessor2D
aBufferProcessor(aViewInfo
, *aBufferDevice
.get());
2066 // draw content using pixel renderer
2067 aBufferProcessor
.process(rContent
);
2068 const Bitmap
aBmContent(aBufferDevice
->GetBitmap(aEmptyPoint
, aSizePixel
));
2070 // draw transparence using pixel renderer
2071 aBufferDevice
->Erase();
2072 aBufferProcessor
.process(rTransparence
);
2073 const AlphaMask
aBmAlpha(aBufferDevice
->GetBitmap(aEmptyPoint
, aSizePixel
));
2076 mpOutputDevice
->DrawBitmapEx(
2077 aRectLogic
.TopLeft(),
2078 aRectLogic
.GetSize(),
2079 BitmapEx(aBmContent
, aBmAlpha
));
2086 case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D
:
2088 // use default transform group pocessing
2089 RenderTransformPrimitive2D(static_cast< const primitive2d::TransformPrimitive2D
& >(rCandidate
));
2092 case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D
:
2094 // new XDrawPage for ViewInformation2D
2095 RenderPagePreviewPrimitive2D(static_cast< const primitive2d::PagePreviewPrimitive2D
& >(rCandidate
));
2098 case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D
:
2100 // use default marker array pocessing
2101 RenderMarkerArrayPrimitive2D(static_cast< const primitive2d::MarkerArrayPrimitive2D
& >(rCandidate
));
2104 case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D
:
2106 // use default point array pocessing
2107 RenderPointArrayPrimitive2D(static_cast< const primitive2d::PointArrayPrimitive2D
& >(rCandidate
));
2110 case PRIMITIVE2D_ID_STRUCTURETAGPRIMITIVE2D
:
2112 // structured tag primitive
2113 const primitive2d::StructureTagPrimitive2D
& rStructureTagCandidate
= static_cast< const primitive2d::StructureTagPrimitive2D
& >(rCandidate
);
2114 const vcl::PDFWriter::StructElement
& rTagElement(rStructureTagCandidate
.getStructureElement());
2115 const bool bTagUsed(vcl::PDFWriter::NonStructElement
!= rTagElement
);
2117 if(mpPDFExtOutDevData
&& bTagUsed
)
2120 mpPDFExtOutDevData
->BeginStructureElement(rTagElement
);
2123 // process children normally
2124 process(rStructureTagCandidate
.getChildren());
2126 if(mpPDFExtOutDevData
&& bTagUsed
)
2129 mpPDFExtOutDevData
->EndStructureElement();
2134 case PRIMITIVE2D_ID_EPSPRIMITIVE2D
:
2136 RenderEpsPrimitive2D(static_cast< const primitive2d::EpsPrimitive2D
& >(rCandidate
));
2141 // process recursively
2142 process(rCandidate
.get2DDecomposition(getViewInformation2D()));
2147 } // end of namespace processor2d
2148 } // end of namespace drawinglayer
2150 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */