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 <wmfemfhelper.hxx>
21 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
22 #include <vcl/lineinfo.hxx>
23 #include <vcl/metaact.hxx>
24 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
25 #include <basegfx/matrix/b2dhommatrixtools.hxx>
26 #include <basegfx/utils/gradienttools.hxx>
27 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
28 #include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
29 #include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
30 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
31 #include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
32 #include <basegfx/polygon/b2dpolygontools.hxx>
33 #include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx>
34 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
35 #include <vcl/BitmapPalette.hxx>
36 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
37 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
38 #include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
39 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
40 #include <basegfx/polygon/b2dpolygonclipper.hxx>
41 #include <drawinglayer/primitive2d/invertprimitive2d.hxx>
42 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
43 #include <primitive2d/wallpaperprimitive2d.hxx>
44 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
45 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
46 #include <primitive2d/textlineprimitive2d.hxx>
47 #include <primitive2d/textstrikeoutprimitive2d.hxx>
48 #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
49 #include <sal/log.hxx>
50 #include <tools/fract.hxx>
51 #include <tools/stream.hxx>
52 #include <tools/UnitConversion.hxx>
53 #include <vcl/canvastools.hxx>
54 #include <vcl/gradient.hxx>
55 #include <vcl/hatch.hxx>
56 #include <vcl/outdev.hxx>
57 #include <i18nlangtag/languagetag.hxx>
58 #include <emfplushelper.hxx>
60 #include <toolkit/helper/vclunohelper.hxx>
62 namespace drawinglayer::primitive2d
66 /** NonOverlappingFillGradientPrimitive2D class
68 This is a special version of the FillGradientPrimitive2D which decomposes
69 to a non-overlapping geometry version of the gradient. This needs to be
70 used to support the old XOR paint-'trick'.
72 It does not need an own identifier since a renderer who wants to interpret
73 it itself may do so. It just overrides the decomposition of the C++
74 implementation class to do an alternative decomposition.
76 class NonOverlappingFillGradientPrimitive2D
: public FillGradientPrimitive2D
79 /// local decomposition.
80 virtual void create2DDecomposition(Primitive2DContainer
& rContainer
,
81 const geometry::ViewInformation2D
& rViewInformation
) const override
;
85 NonOverlappingFillGradientPrimitive2D(
86 const basegfx::B2DRange
& rObjectRange
,
87 const attribute::FillGradientAttribute
& rFillGradient
)
88 : FillGradientPrimitive2D(rObjectRange
, rFillGradient
)
95 void NonOverlappingFillGradientPrimitive2D::create2DDecomposition(
96 Primitive2DContainer
& rContainer
,
97 const geometry::ViewInformation2D
& /*rViewInformation*/) const
99 if (!getFillGradient().isDefault())
101 createFill(rContainer
, false);
105 } // end of namespace
107 namespace wmfemfhelper
109 /** helper class for graphic context
111 This class allows to hold a complete representation of classic
112 VCL OutputDevice state. This data is needed for correct
113 interpretation of the MetaFile action flow.
115 PropertyHolder::PropertyHolder()
116 : maMapUnit(MapUnit::Map100thMM
),
117 maTextColor(sal_uInt32(COL_BLACK
)),
118 maRasterOp(RasterOp::OverPaint
),
119 mnLayoutMode(vcl::text::ComplexTextLayoutFlags::Default
),
121 mnPushFlags(vcl::PushFlags::NONE
),
125 mbTextFillColor(false),
126 mbTextLineColor(false),
127 mbOverlineColor(false),
128 mbClipPolyPolygonActive(false)
133 namespace wmfemfhelper
135 /** stack for properties
137 This class builds a stack based on the PropertyHolder
138 class. It encapsulates the pointer/new/delete usage to
139 make it safe and implements the push/pop as needed by a
140 VCL Metafile interpreter. The critical part here are the
141 flag values VCL OutputDevice uses here; not all stuff is
142 pushed and thus needs to be copied at pop.
144 PropertyHolders::PropertyHolders()
146 maPropertyHolders
.push_back(new PropertyHolder());
149 void PropertyHolders::PushDefault()
151 PropertyHolder
* pNew
= new PropertyHolder();
152 maPropertyHolders
.push_back(pNew
);
155 void PropertyHolders::Push(vcl::PushFlags nPushFlags
)
157 if (bool(nPushFlags
))
159 OSL_ENSURE(maPropertyHolders
.size(), "PropertyHolders: PUSH with no property holders (!)");
160 if (!maPropertyHolders
.empty())
162 PropertyHolder
* pNew
= new PropertyHolder(*maPropertyHolders
.back());
163 pNew
->setPushFlags(nPushFlags
);
164 maPropertyHolders
.push_back(pNew
);
169 void PropertyHolders::Pop()
171 OSL_ENSURE(maPropertyHolders
.size(), "PropertyHolders: POP with no property holders (!)");
172 const sal_uInt32
nSize(maPropertyHolders
.size());
177 const PropertyHolder
* pTip
= maPropertyHolders
.back();
178 const vcl::PushFlags
nPushFlags(pTip
->getPushFlags());
180 if (nPushFlags
!= vcl::PushFlags::NONE
)
184 // copy back content for all non-set flags
185 PropertyHolder
* pLast
= maPropertyHolders
[nSize
- 2];
187 if (vcl::PushFlags::ALL
!= nPushFlags
)
189 if (!(nPushFlags
& vcl::PushFlags::LINECOLOR
))
191 pLast
->setLineColor(pTip
->getLineColor());
192 pLast
->setLineColorActive(pTip
->getLineColorActive());
194 if (!(nPushFlags
& vcl::PushFlags::FILLCOLOR
))
196 pLast
->setFillColor(pTip
->getFillColor());
197 pLast
->setFillColorActive(pTip
->getFillColorActive());
199 if (!(nPushFlags
& vcl::PushFlags::FONT
))
201 pLast
->setFont(pTip
->getFont());
203 if (!(nPushFlags
& vcl::PushFlags::TEXTCOLOR
))
205 pLast
->setTextColor(pTip
->getTextColor());
206 pLast
->setTextColorActive(pTip
->getTextColorActive());
208 if (!(nPushFlags
& vcl::PushFlags::MAPMODE
))
210 pLast
->setTransformation(pTip
->getTransformation());
211 pLast
->setMapUnit(pTip
->getMapUnit());
213 if (!(nPushFlags
& vcl::PushFlags::CLIPREGION
))
215 pLast
->setClipPolyPolygon(pTip
->getClipPolyPolygon());
216 pLast
->setClipPolyPolygonActive(pTip
->getClipPolyPolygonActive());
218 if (!(nPushFlags
& vcl::PushFlags::RASTEROP
))
220 pLast
->setRasterOp(pTip
->getRasterOp());
222 if (!(nPushFlags
& vcl::PushFlags::TEXTFILLCOLOR
))
224 pLast
->setTextFillColor(pTip
->getTextFillColor());
225 pLast
->setTextFillColorActive(pTip
->getTextFillColorActive());
227 if (!(nPushFlags
& vcl::PushFlags::TEXTALIGN
))
229 if (pLast
->getFont().GetAlignment() != pTip
->getFont().GetAlignment())
231 vcl::Font
aFont(pLast
->getFont());
232 aFont
.SetAlignment(pTip
->getFont().GetAlignment());
233 pLast
->setFont(aFont
);
236 if (!(nPushFlags
& vcl::PushFlags::REFPOINT
))
240 if (!(nPushFlags
& vcl::PushFlags::TEXTLINECOLOR
))
242 pLast
->setTextLineColor(pTip
->getTextLineColor());
243 pLast
->setTextLineColorActive(pTip
->getTextLineColorActive());
245 if (!(nPushFlags
& vcl::PushFlags::TEXTLAYOUTMODE
))
247 pLast
->setLayoutMode(pTip
->getLayoutMode());
249 if (!(nPushFlags
& vcl::PushFlags::TEXTLANGUAGE
))
251 pLast
->setLanguageType(pTip
->getLanguageType());
253 if (!(nPushFlags
& vcl::PushFlags::OVERLINECOLOR
))
255 pLast
->setOverlineColor(pTip
->getOverlineColor());
256 pLast
->setOverlineColorActive(pTip
->getOverlineColorActive());
263 delete maPropertyHolders
.back();
264 maPropertyHolders
.pop_back();
267 PropertyHolder
& PropertyHolders::Current()
269 static PropertyHolder aDummy
;
270 OSL_ENSURE(maPropertyHolders
.size(), "PropertyHolders: CURRENT with no property holders (!)");
271 return maPropertyHolders
.empty() ? aDummy
: *maPropertyHolders
.back();
274 PropertyHolders::~PropertyHolders()
276 while (!maPropertyHolders
.empty())
278 delete maPropertyHolders
.back();
279 maPropertyHolders
.pop_back();
286 /** helper to convert a vcl::Region to a B2DPolyPolygon
287 when it does not yet contain one. In the future
288 this may be expanded to merge the polygons created
289 from rectangles or use a special algo to directly turn
290 the spans of regions to a single, already merged
293 basegfx::B2DPolyPolygon
getB2DPolyPolygonFromRegion(const vcl::Region
& rRegion
)
295 basegfx::B2DPolyPolygon aRetval
;
297 if (!rRegion
.IsEmpty())
299 aRetval
= rRegion
.GetAsB2DPolyPolygon();
306 namespace wmfemfhelper
308 /** Helper class to buffer and hold a Primitive target vector. It
309 encapsulates the new/delete functionality and allows to work
310 on pointers of the implementation classes. All data will
311 be converted to uno sequences of uno references when accessing the
314 TargetHolder::TargetHolder()
318 TargetHolder::~TargetHolder()
322 sal_uInt32
TargetHolder::size() const
324 return aTargets
.size();
327 void TargetHolder::append(drawinglayer::primitive2d::BasePrimitive2D
* pCandidate
)
331 aTargets
.push_back(pCandidate
);
335 drawinglayer::primitive2d::Primitive2DContainer
TargetHolder::getPrimitive2DSequence(const PropertyHolder
& rPropertyHolder
)
337 drawinglayer::primitive2d::Primitive2DContainer xRetval
= std::move(aTargets
);
340 if (!xRetval
.empty() && rPropertyHolder
.getClipPolyPolygonActive())
342 const basegfx::B2DPolyPolygon
& rClipPolyPolygon
= rPropertyHolder
.getClipPolyPolygon();
344 if (rClipPolyPolygon
.count())
346 drawinglayer::primitive2d::Primitive2DReference
xMask(
347 new drawinglayer::primitive2d::MaskPrimitive2D(
349 std::move(xRetval
)));
351 xRetval
= drawinglayer::primitive2d::Primitive2DContainer
{ xMask
};
359 namespace wmfemfhelper
361 /** Helper class which builds a stack on the TargetHolder class */
362 TargetHolders::TargetHolders()
364 maTargetHolders
.push_back(new TargetHolder());
367 sal_uInt32
TargetHolders::size() const
369 return maTargetHolders
.size();
372 void TargetHolders::Push()
374 maTargetHolders
.push_back(new TargetHolder());
377 void TargetHolders::Pop()
379 OSL_ENSURE(maTargetHolders
.size(), "TargetHolders: POP with no property holders (!)");
380 if (!maTargetHolders
.empty())
382 delete maTargetHolders
.back();
383 maTargetHolders
.pop_back();
387 TargetHolder
& TargetHolders::Current()
389 static TargetHolder aDummy
;
390 OSL_ENSURE(maTargetHolders
.size(), "TargetHolders: CURRENT with no property holders (!)");
391 return maTargetHolders
.empty() ? aDummy
: *maTargetHolders
.back();
394 TargetHolders::~TargetHolders()
396 while (!maTargetHolders
.empty())
398 delete maTargetHolders
.back();
399 maTargetHolders
.pop_back();
406 /** helper to convert a MapMode to a transformation */
407 basegfx::B2DHomMatrix
getTransformFromMapMode(const MapMode
& rMapMode
)
409 basegfx::B2DHomMatrix aMapping
;
410 const Fraction
aNoScale(1, 1);
411 const Point
& rOrigin(rMapMode
.GetOrigin());
413 if(0 != rOrigin
.X() || 0 != rOrigin
.Y())
415 aMapping
.translate(rOrigin
.X(), rOrigin
.Y());
418 if(rMapMode
.GetScaleX() != aNoScale
|| rMapMode
.GetScaleY() != aNoScale
)
421 double(rMapMode
.GetScaleX()),
422 double(rMapMode
.GetScaleY()));
429 namespace wmfemfhelper
431 /** helper to create a PointArrayPrimitive2D based on current context */
432 static void createPointArrayPrimitive(
433 std::vector
< basegfx::B2DPoint
>&& rPositions
,
434 TargetHolder
& rTarget
,
435 PropertyHolder
const & rProperties
,
436 const basegfx::BColor
& rBColor
)
438 if(rPositions
.empty())
441 if(rProperties
.getTransformation().isIdentity())
444 new drawinglayer::primitive2d::PointArrayPrimitive2D(
445 std::move(rPositions
),
450 for(basegfx::B2DPoint
& aPosition
: rPositions
)
452 aPosition
= rProperties
.getTransformation() * aPosition
;
456 new drawinglayer::primitive2d::PointArrayPrimitive2D(
457 std::move(rPositions
),
462 /** helper to create a PolygonHairlinePrimitive2D based on current context */
463 static void createHairlinePrimitive(
464 const basegfx::B2DPolygon
& rLinePolygon
,
465 TargetHolder
& rTarget
,
466 PropertyHolder
const & rProperties
)
468 if(rLinePolygon
.count())
470 basegfx::B2DPolygon
aLinePolygon(rLinePolygon
);
471 aLinePolygon
.transform(rProperties
.getTransformation());
473 new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
474 std::move(aLinePolygon
),
475 rProperties
.getLineColor()));
479 /** helper to create a PolyPolygonColorPrimitive2D based on current context */
480 static void createFillPrimitive(
481 const basegfx::B2DPolyPolygon
& rFillPolyPolygon
,
482 TargetHolder
& rTarget
,
483 PropertyHolder
const & rProperties
)
485 if(rFillPolyPolygon
.count())
487 basegfx::B2DPolyPolygon
aFillPolyPolygon(rFillPolyPolygon
);
488 aFillPolyPolygon
.transform(rProperties
.getTransformation());
490 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
491 std::move(aFillPolyPolygon
),
492 rProperties
.getFillColor()));
496 /** helper to create a PolygonStrokePrimitive2D based on current context */
497 static void createLinePrimitive(
498 const basegfx::B2DPolygon
& rLinePolygon
,
499 const LineInfo
& rLineInfo
,
500 TargetHolder
& rTarget
,
501 PropertyHolder
const & rProperties
)
503 if(!rLinePolygon
.count())
506 const bool bDashDotUsed(LineStyle::Dash
== rLineInfo
.GetStyle());
507 const bool bWidthUsed(rLineInfo
.GetWidth() > 1);
509 if(bDashDotUsed
|| bWidthUsed
)
511 basegfx::B2DPolygon
aLinePolygon(rLinePolygon
);
512 aLinePolygon
.transform(rProperties
.getTransformation());
513 drawinglayer::attribute::LineAttribute
aLineAttribute(
514 rProperties
.getLineColor(),
515 bWidthUsed
? rLineInfo
.GetWidth() : 0.0,
516 rLineInfo
.GetLineJoin(),
517 rLineInfo
.GetLineCap());
521 std::vector
< double > fDotDashArray
= rLineInfo
.GetDotDashArray();
522 const double fAccumulated(std::accumulate(fDotDashArray
.begin(), fDotDashArray
.end(), 0.0));
523 drawinglayer::attribute::StrokeAttribute
aStrokeAttribute(
524 std::move(fDotDashArray
),
528 new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
529 std::move(aLinePolygon
),
530 std::move(aLineAttribute
),
531 std::move(aStrokeAttribute
)));
536 new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
537 std::move(aLinePolygon
),
543 createHairlinePrimitive(rLinePolygon
, rTarget
, rProperties
);
547 /** helper to create needed line and fill primitives based on current context */
548 static void createHairlineAndFillPrimitive(
549 const basegfx::B2DPolygon
& rPolygon
,
550 TargetHolder
& rTarget
,
551 PropertyHolder
const & rProperties
)
553 if(rProperties
.getFillColorActive())
555 createFillPrimitive(basegfx::B2DPolyPolygon(rPolygon
), rTarget
, rProperties
);
558 if(rProperties
.getLineColorActive())
560 createHairlinePrimitive(rPolygon
, rTarget
, rProperties
);
564 /** helper to create needed line and fill primitives based on current context */
565 static void createHairlineAndFillPrimitive(
566 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
567 TargetHolder
& rTarget
,
568 PropertyHolder
const & rProperties
)
570 if(rProperties
.getFillColorActive())
572 createFillPrimitive(rPolyPolygon
, rTarget
, rProperties
);
575 if(rProperties
.getLineColorActive())
577 for(sal_uInt32
a(0); a
< rPolyPolygon
.count(); a
++)
579 createHairlinePrimitive(rPolyPolygon
.getB2DPolygon(a
), rTarget
, rProperties
);
584 /** helper to create DiscreteBitmapPrimitive2D based on current context.
585 The DiscreteBitmapPrimitive2D is especially created for this usage
586 since no other usage defines a bitmap visualisation based on top-left
587 position and size in pixels. At the end it will create a view-dependent
588 transformed embedding of a BitmapPrimitive2D.
590 static void createBitmapExPrimitive(
591 const BitmapEx
& rBitmapEx
,
593 TargetHolder
& rTarget
,
594 PropertyHolder
const & rProperties
)
596 if(!rBitmapEx
.IsEmpty())
598 basegfx::B2DPoint
aPoint(rPoint
.X(), rPoint
.Y());
599 aPoint
= rProperties
.getTransformation() * aPoint
;
602 new drawinglayer::primitive2d::DiscreteBitmapPrimitive2D(
608 /** helper to create BitmapPrimitive2D based on current context */
609 static void createBitmapExPrimitive(
610 const BitmapEx
& rBitmapEx
,
613 TargetHolder
& rTarget
,
614 PropertyHolder
const & rProperties
)
616 if(rBitmapEx
.IsEmpty())
619 basegfx::B2DHomMatrix aObjectTransform
;
621 aObjectTransform
.set(0, 0, rSize
.Width());
622 aObjectTransform
.set(1, 1, rSize
.Height());
623 aObjectTransform
.set(0, 2, rPoint
.X());
624 aObjectTransform
.set(1, 2, rPoint
.Y());
626 aObjectTransform
= rProperties
.getTransformation() * aObjectTransform
;
629 new drawinglayer::primitive2d::BitmapPrimitive2D(
634 /** helper to create a regular BotmapEx from a MaskAction (definitions
635 which use a bitmap without transparence but define one of the colors as
638 static BitmapEx
createMaskBmpEx(const Bitmap
& rBitmap
, const Color
& rMaskColor
)
640 const Color
aWhite(COL_WHITE
);
641 BitmapPalette aBiLevelPalette
{
645 Bitmap
aMask(rBitmap
.CreateMask(aWhite
));
646 Bitmap
aSolid(rBitmap
.GetSizePixel(), vcl::PixelFormat::N1_BPP
, &aBiLevelPalette
);
648 aSolid
.Erase(rMaskColor
);
650 return BitmapEx(aSolid
, aMask
);
653 /** helper to convert from a VCL Gradient definition to the corresponding
654 data for primitive representation
656 static drawinglayer::attribute::FillGradientAttribute
createFillGradientAttribute(const Gradient
& rGradient
)
658 const Color
aStartColor(rGradient
.GetStartColor());
659 const sal_uInt16
nStartIntens(rGradient
.GetStartIntensity());
660 basegfx::BColor
aStart(aStartColor
.getBColor());
662 if(nStartIntens
!= 100)
664 const basegfx::BColor aBlack
;
665 aStart
= interpolate(aBlack
, aStart
, static_cast<double>(nStartIntens
) * 0.01);
668 const Color
aEndColor(rGradient
.GetEndColor());
669 const sal_uInt16
nEndIntens(rGradient
.GetEndIntensity());
670 basegfx::BColor
aEnd(aEndColor
.getBColor());
672 if(nEndIntens
!= 100)
674 const basegfx::BColor aBlack
;
675 aEnd
= interpolate(aBlack
, aEnd
, static_cast<double>(nEndIntens
) * 0.01);
678 return drawinglayer::attribute::FillGradientAttribute(
679 rGradient
.GetStyle(),
680 static_cast<double>(rGradient
.GetBorder()) * 0.01,
681 static_cast<double>(rGradient
.GetOfsX()) * 0.01,
682 static_cast<double>(rGradient
.GetOfsY()) * 0.01,
683 toRadians(rGradient
.GetAngle()),
684 basegfx::BColorStops(aStart
, aEnd
),
685 rGradient
.GetSteps());
688 /** helper to convert from a VCL Hatch definition to the corresponding
689 data for primitive representation
691 static drawinglayer::attribute::FillHatchAttribute
createFillHatchAttribute(const Hatch
& rHatch
)
693 drawinglayer::attribute::HatchStyle
aHatchStyle(drawinglayer::attribute::HatchStyle::Single
);
695 switch(rHatch
.GetStyle())
697 default : // case HatchStyle::Single :
699 aHatchStyle
= drawinglayer::attribute::HatchStyle::Single
;
702 case HatchStyle::Double
:
704 aHatchStyle
= drawinglayer::attribute::HatchStyle::Double
;
707 case HatchStyle::Triple
:
709 aHatchStyle
= drawinglayer::attribute::HatchStyle::Triple
;
714 return drawinglayer::attribute::FillHatchAttribute(
716 static_cast<double>(rHatch
.GetDistance()),
717 toRadians(rHatch
.GetAngle()),
718 rHatch
.GetColor().getBColor(),
719 3, // same default as VCL, a minimum of three discrete units (pixels) offset
723 /** helper to take needed action on ClipRegion change. This method needs to be called
724 on any vcl::Region change, e.g. at the obvious actions doing this, but also at pop-calls
725 which change the vcl::Region of the current context. It takes care of creating the
726 current embedded context, set the new vcl::Region at the context and possibly prepare
727 a new target for including new geometry into the current region
729 void HandleNewClipRegion(
730 const basegfx::B2DPolyPolygon
& rClipPolyPolygon
,
731 TargetHolders
& rTargetHolders
,
732 PropertyHolders
& rPropertyHolders
)
734 const bool bNewActive(rClipPolyPolygon
.count());
736 // #i108636# The handling of new ClipPolyPolygons was not done as good as possible
737 // in the first version of this interpreter; e.g. when a ClipPolyPolygon was set
738 // initially and then using a lot of push/pop actions, the pop always leads
739 // to setting a 'new' ClipPolyPolygon which indeed is the return to the ClipPolyPolygon
740 // of the properties next on the stack.
742 // This ClipPolyPolygon is identical to the current one, so there is no need to
743 // create a MaskPrimitive2D containing the up-to-now created primitives, but
744 // this was done before. While this does not lead to wrong primitive
745 // representations of the metafile data, it creates unnecessarily expensive
746 // representations. Just detecting when no really 'new' ClipPolyPolygon gets set
747 // solves the problem.
749 if(!rPropertyHolders
.Current().getClipPolyPolygonActive() && !bNewActive
)
751 // no active ClipPolyPolygon exchanged by no new one, done
755 if(rPropertyHolders
.Current().getClipPolyPolygonActive() && bNewActive
)
757 // active ClipPolyPolygon and new active ClipPolyPolygon
758 if(rPropertyHolders
.Current().getClipPolyPolygon() == rClipPolyPolygon
)
760 // new is the same as old, done
765 // Here the old and the new are definitively different, maybe
766 // old one and/or new one is not active.
768 // Handle deletion of old ClipPolyPolygon. The process evtl. created primitives which
769 // belong to this active ClipPolyPolygon. These need to be embedded to a
770 // MaskPrimitive2D accordingly.
771 if(rPropertyHolders
.Current().getClipPolyPolygonActive() && rTargetHolders
.size() > 1)
773 drawinglayer::primitive2d::Primitive2DContainer aSubContent
;
775 if(rPropertyHolders
.Current().getClipPolyPolygon().count()
776 && rTargetHolders
.Current().size())
778 aSubContent
= rTargetHolders
.Current().getPrimitive2DSequence(
779 rPropertyHolders
.Current());
782 rTargetHolders
.Pop();
784 if(!aSubContent
.empty())
786 rTargetHolders
.Current().append(
787 new drawinglayer::primitive2d::GroupPrimitive2D(
788 std::move(aSubContent
)));
792 // apply new settings to current properties by setting
793 // the new region now
794 rPropertyHolders
.Current().setClipPolyPolygonActive(bNewActive
);
798 rPropertyHolders
.Current().setClipPolyPolygon(rClipPolyPolygon
);
800 // prepare new content holder for new active region
801 rTargetHolders
.Push();
805 /** helper to handle the change of RasterOp. It takes care of encapsulating all current
806 geometry to the current RasterOp (if changed) and needs to be called on any RasterOp
807 change. It will also start a new geometry target to embrace to the new RasterOp if
808 a changing RasterOp is used. Currently, RasterOp::Xor and RasterOp::Invert are supported using
809 InvertPrimitive2D, and RasterOp::N0 by using a ModifiedColorPrimitive2D to force to black paint
811 static void HandleNewRasterOp(
813 TargetHolders
& rTargetHolders
,
814 PropertyHolders
& rPropertyHolders
)
816 // check if currently active
817 if(rPropertyHolders
.Current().isRasterOpActive() && rTargetHolders
.size() > 1)
819 drawinglayer::primitive2d::Primitive2DContainer aSubContent
;
821 if(rTargetHolders
.Current().size())
823 aSubContent
= rTargetHolders
.Current().getPrimitive2DSequence(rPropertyHolders
.Current());
826 rTargetHolders
.Pop();
828 if(!aSubContent
.empty())
830 if(rPropertyHolders
.Current().isRasterOpForceBlack())
832 // force content to black
833 rTargetHolders
.Current().append(
834 new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
835 std::move(aSubContent
),
836 std::make_shared
<basegfx::BColorModifier_replace
>(
837 basegfx::BColor(0.0, 0.0, 0.0))));
839 else // if(rPropertyHolders.Current().isRasterOpInvert())
842 rTargetHolders
.Current().append(
843 new drawinglayer::primitive2d::InvertPrimitive2D(
844 std::move(aSubContent
)));
849 // apply new settings
850 rPropertyHolders
.Current().setRasterOp(aRasterOp
);
852 // check if now active
853 if(rPropertyHolders
.Current().isRasterOpActive())
855 // prepare new content holder for new invert
856 rTargetHolders
.Push();
860 /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
861 It is a quite mighty action. This helper is for simple color filled background.
863 static rtl::Reference
<drawinglayer::primitive2d::BasePrimitive2D
> CreateColorWallpaper(
864 const basegfx::B2DRange
& rRange
,
865 const basegfx::BColor
& rColor
,
866 PropertyHolder
const & rPropertyHolder
)
868 basegfx::B2DPolygon
aOutline(basegfx::utils::createPolygonFromRect(rRange
));
869 aOutline
.transform(rPropertyHolder
.getTransformation());
871 return new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
872 basegfx::B2DPolyPolygon(aOutline
),
876 /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
877 It is a quite mighty action. This helper is for gradient filled background.
879 static rtl::Reference
<drawinglayer::primitive2d::BasePrimitive2D
> CreateGradientWallpaper(
880 const basegfx::B2DRange
& rRange
,
881 const Gradient
& rGradient
,
882 PropertyHolder
const & rPropertyHolder
)
884 drawinglayer::attribute::FillGradientAttribute
aAttribute(createFillGradientAttribute(rGradient
));
885 basegfx::BColor aSingleColor
;
887 if (aAttribute
.getColorStops().isSingleColor(aSingleColor
))
889 // not really a gradient. Create filled rectangle
890 return CreateColorWallpaper(rRange
, aSingleColor
, rPropertyHolder
);
895 rtl::Reference
<drawinglayer::primitive2d::BasePrimitive2D
> pRetval(
896 new drawinglayer::primitive2d::FillGradientPrimitive2D(
898 std::move(aAttribute
)));
900 if(!rPropertyHolder
.getTransformation().isIdentity())
902 const drawinglayer::primitive2d::Primitive2DReference
xPrim(pRetval
);
903 drawinglayer::primitive2d::Primitive2DContainer xSeq
{ xPrim
};
905 pRetval
= new drawinglayer::primitive2d::TransformPrimitive2D(
906 rPropertyHolder
.getTransformation(),
914 /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
915 It is a quite mighty action. This helper decides if color and/or gradient
916 background is needed for the wanted bitmap fill and then creates the needed
917 WallpaperBitmapPrimitive2D. This primitive was created for this purpose and
918 takes over all needed logic of orientations and tiling.
920 static void CreateAndAppendBitmapWallpaper(
921 basegfx::B2DRange aWallpaperRange
,
922 const Wallpaper
& rWallpaper
,
923 TargetHolder
& rTarget
,
924 PropertyHolder
const & rProperty
)
926 const BitmapEx
aBitmapEx(rWallpaper
.GetBitmap());
927 const WallpaperStyle
eWallpaperStyle(rWallpaper
.GetStyle());
929 // if bitmap visualisation is transparent, maybe background
930 // needs to be filled. Create background
931 if(aBitmapEx
.IsAlpha()
932 || (WallpaperStyle::Tile
!= eWallpaperStyle
&& WallpaperStyle::Scale
!= eWallpaperStyle
))
934 if(rWallpaper
.IsGradient())
937 CreateGradientWallpaper(
939 rWallpaper
.GetGradient(),
942 else if(!rWallpaper
.GetColor().IsTransparent())
945 CreateColorWallpaper(
947 rWallpaper
.GetColor().getBColor(),
952 // use wallpaper rect if set
953 if(rWallpaper
.IsRect() && !rWallpaper
.GetRect().IsEmpty())
955 aWallpaperRange
= vcl::unotools::b2DRectangleFromRectangle(rWallpaper
.GetRect());
958 rtl::Reference
<drawinglayer::primitive2d::BasePrimitive2D
> pBitmapWallpaperFill
=
959 new drawinglayer::primitive2d::WallpaperBitmapPrimitive2D(
964 if(rProperty
.getTransformation().isIdentity())
967 rTarget
.append(pBitmapWallpaperFill
);
971 // when a transformation is set, embed to it
972 const drawinglayer::primitive2d::Primitive2DReference
xPrim(pBitmapWallpaperFill
);
975 new drawinglayer::primitive2d::TransformPrimitive2D(
976 rProperty
.getTransformation(),
977 drawinglayer::primitive2d::Primitive2DContainer
{ xPrim
}));
981 /** helper to decide UnderlineAbove for text primitives */
982 static bool isUnderlineAbove(const vcl::Font
& rFont
)
984 if(!rFont
.IsVertical())
989 // the underline is right for Japanese only
990 return (LANGUAGE_JAPANESE
== rFont
.GetLanguage()) || (LANGUAGE_JAPANESE
== rFont
.GetCJKContextLanguage());
993 static void createFontAttributeTransformAndAlignment(
994 drawinglayer::attribute::FontAttribute
& rFontAttribute
,
995 basegfx::B2DHomMatrix
& rTextTransform
,
996 basegfx::B2DVector
& rAlignmentOffset
,
997 PropertyHolder
const & rProperty
)
999 const vcl::Font
& rFont
= rProperty
.getFont();
1000 basegfx::B2DVector aFontScaling
;
1002 rFontAttribute
= drawinglayer::primitive2d::getFontAttributeFromVclFont(
1005 bool(rProperty
.getLayoutMode() & vcl::text::ComplexTextLayoutFlags::BiDiRtl
),
1006 bool(rProperty
.getLayoutMode() & vcl::text::ComplexTextLayoutFlags::BiDiStrong
));
1009 rTextTransform
.scale(aFontScaling
.getX(), aFontScaling
.getY());
1011 // take text align into account
1012 if(ALIGN_BASELINE
!= rFont
.GetAlignment())
1014 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice
;
1015 aTextLayouterDevice
.setFont(rFont
);
1017 if(ALIGN_TOP
== rFont
.GetAlignment())
1019 rAlignmentOffset
.setY(aTextLayouterDevice
.getFontAscent());
1021 else // ALIGN_BOTTOM
1023 rAlignmentOffset
.setY(-aTextLayouterDevice
.getFontDescent());
1026 rTextTransform
.translate(rAlignmentOffset
.getX(), rAlignmentOffset
.getY());
1029 // add FontRotation (if used)
1030 if(rFont
.GetOrientation())
1032 rTextTransform
.rotate(-toRadians(rFont
.GetOrientation()));
1036 /** helper which takes complete care for creating the needed text primitives. It
1037 takes care of decorated stuff and all the geometry adaptations needed
1039 static void processMetaTextAction(
1040 const Point
& rTextStartPosition
,
1041 const OUString
& rText
,
1042 sal_uInt16 nTextStart
,
1043 sal_uInt16 nTextLength
,
1044 std::vector
< double >&& rDXArray
,
1045 std::vector
< sal_Bool
>&& rKashidaArray
,
1046 TargetHolder
& rTarget
,
1047 PropertyHolder
const & rProperty
)
1049 rtl::Reference
<drawinglayer::primitive2d::BasePrimitive2D
> pResult
;
1050 const vcl::Font
& rFont
= rProperty
.getFont();
1051 basegfx::B2DVector
aAlignmentOffset(0.0, 0.0);
1055 drawinglayer::attribute::FontAttribute aFontAttribute
;
1056 basegfx::B2DHomMatrix aTextTransform
;
1058 // fill parameters derived from current font
1059 createFontAttributeTransformAndAlignment(
1065 // add TextStartPosition
1066 aTextTransform
.translate(rTextStartPosition
.X(), rTextStartPosition
.Y());
1068 // prepare FontColor and Locale
1069 const basegfx::BColor
aFontColor(rProperty
.getTextColor());
1070 const Color
aFillColor(rFont
.GetFillColor());
1071 css::lang::Locale
aLocale(LanguageTag(rProperty
.getLanguageType()).getLocale());
1072 const bool bWordLineMode(rFont
.IsWordLineMode());
1074 const bool bDecoratedIsNeeded(
1075 LINESTYLE_NONE
!= rFont
.GetOverline()
1076 || LINESTYLE_NONE
!= rFont
.GetUnderline()
1077 || STRIKEOUT_NONE
!= rFont
.GetStrikeout()
1078 || FontEmphasisMark::NONE
!= (rFont
.GetEmphasisMark() & FontEmphasisMark::Style
)
1079 || FontRelief::NONE
!= rFont
.GetRelief()
1083 if(bDecoratedIsNeeded
)
1085 // prepare overline, underline and strikeout data
1086 const drawinglayer::primitive2d::TextLine
eFontOverline(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rFont
.GetOverline()));
1087 const drawinglayer::primitive2d::TextLine
eFontLineStyle(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rFont
.GetUnderline()));
1088 const drawinglayer::primitive2d::TextStrikeout
eTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rFont
.GetStrikeout()));
1090 // check UndelineAbove
1091 const bool bUnderlineAbove(drawinglayer::primitive2d::TEXT_LINE_NONE
!= eFontLineStyle
&& isUnderlineAbove(rFont
));
1093 // prepare emphasis mark data
1094 drawinglayer::primitive2d::TextEmphasisMark
eTextEmphasisMark(drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE
);
1096 switch(rFont
.GetEmphasisMark() & FontEmphasisMark::Style
)
1098 case FontEmphasisMark::Dot
: eTextEmphasisMark
= drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT
; break;
1099 case FontEmphasisMark::Circle
: eTextEmphasisMark
= drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE
; break;
1100 case FontEmphasisMark::Disc
: eTextEmphasisMark
= drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC
; break;
1101 case FontEmphasisMark::Accent
: eTextEmphasisMark
= drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT
; break;
1105 const bool bEmphasisMarkAbove(rFont
.GetEmphasisMark() & FontEmphasisMark::PosAbove
);
1106 const bool bEmphasisMarkBelow(rFont
.GetEmphasisMark() & FontEmphasisMark::PosBelow
);
1108 // prepare font relief data
1109 drawinglayer::primitive2d::TextRelief
eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE
);
1111 switch(rFont
.GetRelief())
1113 case FontRelief::Embossed
: eTextRelief
= drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED
; break;
1114 case FontRelief::Engraved
: eTextRelief
= drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED
; break;
1115 default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
1118 // prepare shadow/outline data
1119 const bool bShadow(rFont
.IsShadow());
1121 // TextDecoratedPortionPrimitive2D is needed, create one
1122 pResult
= new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
1124 // attributes for TextSimplePortionPrimitive2D
1129 std::move(rDXArray
),
1130 std::move(rKashidaArray
),
1136 // attributes for TextDecoratedPortionPrimitive2D
1137 rProperty
.getOverlineColorActive() ? rProperty
.getOverlineColor() : aFontColor
,
1138 rProperty
.getTextLineColorActive() ? rProperty
.getTextLineColor() : aFontColor
,
1152 // TextSimplePortionPrimitive2D is enough
1153 pResult
= new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
1158 std::vector(rDXArray
),
1159 std::vector(rKashidaArray
),
1160 std::move(aFontAttribute
),
1166 if(pResult
&& rProperty
.getTextFillColorActive())
1168 // text background is requested, add and encapsulate both to new primitive
1169 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice
;
1170 aTextLayouterDevice
.setFont(rFont
);
1173 double fTextWidth(0.0);
1175 if(rDXArray
.empty())
1177 fTextWidth
= aTextLayouterDevice
.getTextWidth(rText
, nTextStart
, nTextLength
);
1181 fTextWidth
= rDXArray
.back();
1184 if(basegfx::fTools::more(fTextWidth
, 0.0))
1187 const basegfx::B2DRange
aTextRange(
1188 0.0, -aTextLayouterDevice
.getFontAscent(),
1189 fTextWidth
, aTextLayouterDevice
.getFontDescent());
1192 basegfx::B2DHomMatrix aTextTransform
;
1194 aTextTransform
.translate(aAlignmentOffset
.getX(), aAlignmentOffset
.getY());
1196 if(rFont
.GetOrientation())
1198 aTextTransform
.rotate(-toRadians(rFont
.GetOrientation()));
1201 aTextTransform
.translate(rTextStartPosition
.X(), rTextStartPosition
.Y());
1203 // prepare Primitive2DSequence, put text in foreground
1204 drawinglayer::primitive2d::Primitive2DContainer
aSequence(2);
1205 aSequence
[1] = pResult
;
1207 // prepare filled polygon
1208 basegfx::B2DPolygon
aOutline(basegfx::utils::createPolygonFromRect(aTextRange
));
1209 aOutline
.transform(aTextTransform
);
1211 aSequence
[0] = drawinglayer::primitive2d::Primitive2DReference(
1212 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
1213 basegfx::B2DPolyPolygon(aOutline
),
1214 rProperty
.getTextFillColor()));
1216 // set as group at pResult
1217 pResult
= new drawinglayer::primitive2d::GroupPrimitive2D(std::move(aSequence
));
1224 // add created text primitive to target
1225 if(rProperty
.getTransformation().isIdentity())
1227 rTarget
.append(pResult
);
1231 // when a transformation is set, embed to it
1232 const drawinglayer::primitive2d::Primitive2DReference
aReference(pResult
);
1235 new drawinglayer::primitive2d::TransformPrimitive2D(
1236 rProperty
.getTransformation(),
1237 drawinglayer::primitive2d::Primitive2DContainer
{ aReference
}));
1241 /** helper which takes complete care for creating the needed textLine primitives */
1242 static void processMetaTextLineAction(
1243 const MetaTextLineAction
& rAction
,
1244 TargetHolder
& rTarget
,
1245 PropertyHolder
const & rProperty
)
1247 const double fLineWidth(fabs(static_cast<double>(rAction
.GetWidth())));
1249 if(fLineWidth
<= 0.0)
1252 const drawinglayer::primitive2d::TextLine
aOverlineMode(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rAction
.GetOverline()));
1253 const drawinglayer::primitive2d::TextLine
aUnderlineMode(drawinglayer::primitive2d::mapFontLineStyleToTextLine(rAction
.GetUnderline()));
1254 const drawinglayer::primitive2d::TextStrikeout
aTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rAction
.GetStrikeout()));
1256 const bool bOverlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE
!= aOverlineMode
);
1257 const bool bUnderlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE
!= aUnderlineMode
);
1258 const bool bStrikeoutUsed(drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE
!= aTextStrikeout
);
1260 if(!(bUnderlineUsed
|| bStrikeoutUsed
|| bOverlineUsed
))
1263 drawinglayer::primitive2d::Primitive2DContainer aTargets
;
1264 basegfx::B2DVector
aAlignmentOffset(0.0, 0.0);
1265 drawinglayer::attribute::FontAttribute aFontAttribute
;
1266 basegfx::B2DHomMatrix aTextTransform
;
1268 // fill parameters derived from current font
1269 createFontAttributeTransformAndAlignment(
1275 // add TextStartPosition
1276 aTextTransform
.translate(rAction
.GetStartPoint().X(), rAction
.GetStartPoint().Y());
1278 // prepare TextLayouter (used in most cases)
1279 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter
;
1280 aTextLayouter
.setFont(rProperty
.getFont());
1284 // create primitive geometry for overline
1286 new drawinglayer::primitive2d::TextLinePrimitive2D(
1289 aTextLayouter
.getOverlineOffset(),
1290 aTextLayouter
.getOverlineHeight(),
1292 rProperty
.getOverlineColor()));
1297 // create primitive geometry for underline
1299 new drawinglayer::primitive2d::TextLinePrimitive2D(
1302 aTextLayouter
.getUnderlineOffset(),
1303 aTextLayouter
.getUnderlineHeight(),
1305 rProperty
.getTextLineColor()));
1310 // create primitive geometry for strikeout
1311 if(drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH
== aTextStrikeout
1312 || drawinglayer::primitive2d::TEXT_STRIKEOUT_X
== aTextStrikeout
)
1314 // strikeout with character
1315 const sal_Unicode
aStrikeoutChar(
1316 drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH
== aTextStrikeout
? '/' : 'X');
1317 css::lang::Locale
aLocale(LanguageTag(
1318 rProperty
.getLanguageType()).getLocale());
1321 new drawinglayer::primitive2d::TextCharacterStrikeoutPrimitive2D(
1324 rProperty
.getTextColor(),
1326 std::move(aFontAttribute
),
1327 std::move(aLocale
)));
1331 // strikeout with geometry
1333 new drawinglayer::primitive2d::TextGeometryStrikeoutPrimitive2D(
1336 rProperty
.getTextColor(),
1337 aTextLayouter
.getUnderlineHeight(),
1338 aTextLayouter
.getStrikeoutOffset(),
1343 if(aTargets
.empty())
1346 // add created text primitive to target
1347 if(rProperty
.getTransformation().isIdentity())
1349 rTarget
.append(std::move(aTargets
));
1353 // when a transformation is set, embed to it
1355 new drawinglayer::primitive2d::TransformPrimitive2D(
1356 rProperty
.getTransformation(),
1357 std::move(aTargets
)));
1361 /** This is the main interpreter method. It is designed to handle the given Metafile
1362 completely inside the given context and target. It may use and modify the context and
1363 target. This design allows to call itself recursively which adapted contexts and
1364 targets as e.g. needed for the MetaActionType::FLOATTRANSPARENT where the content is expressed
1365 as a metafile as sub-content.
1367 This interpreter is as free of VCL functionality as possible. It uses VCL data classes
1368 (else reading the data would not be possible), but e.g. does NOT use a local OutputDevice
1369 as most other MetaFile interpreters/exporters do to hold and work with the current context.
1370 This is necessary to be able to get away from the strong internal VCL-binding.
1372 It tries to combine e.g. pixel and/or point actions and to stitch together single line primitives
1373 where possible (which is not trivial with the possible line geometry definitions).
1375 It tries to handle clipping no longer as Regions and spans of Rectangles, but as PolyPolygon
1376 ClipRegions with (where possible) high precision by using the best possible data quality
1377 from the Region. The vcl::Region is unavoidable as data container, but nowadays allows the transport
1378 of Polygon-based clip regions. Where this is not used, a Polygon is constructed from the
1379 vcl::Region ranges. All primitive clipping uses the MaskPrimitive2D with Polygon-based clipping.
1381 I have marked the single MetaActions with:
1384 Simple, e.g nothing to do or value setting in the context
1386 CHECKED, WORKS WELL:
1387 Thoroughly tested with extra written test code which created a replacement
1388 Metafile just to test this action in various combinations
1390 NEEDS IMPLEMENTATION:
1391 Not implemented and asserted, but also no usage found, neither in own Metafile
1392 creations, nor in EMF/WMF imports (checked with a whole bunch of critical EMF/WMF
1395 For more comments, see the single action implementations.
1397 static void implInterpretMetafile(
1398 const GDIMetaFile
& rMetaFile
,
1399 TargetHolders
& rTargetHolders
,
1400 PropertyHolders
& rPropertyHolders
,
1401 const drawinglayer::geometry::ViewInformation2D
& rViewInformation
)
1403 const size_t nCount(rMetaFile
.GetActionSize());
1404 std::unique_ptr
<emfplushelper::EmfPlusHelper
> aEMFPlus
;
1406 for(size_t nAction(0); nAction
< nCount
; nAction
++)
1408 MetaAction
* pAction
= rMetaFile
.GetAction(nAction
);
1410 switch(pAction
->GetType())
1412 case MetaActionType::NONE
:
1417 case MetaActionType::PIXEL
:
1419 /** CHECKED, WORKS WELL */
1420 std::vector
< basegfx::B2DPoint
> aPositions
;
1421 Color
aLastColor(COL_BLACK
);
1423 while(MetaActionType::PIXEL
== pAction
->GetType() && nAction
< nCount
)
1425 const MetaPixelAction
* pA
= static_cast<const MetaPixelAction
*>(pAction
);
1427 if(pA
->GetColor() != aLastColor
)
1429 if(!aPositions
.empty())
1431 createPointArrayPrimitive(std::move(aPositions
), rTargetHolders
.Current(), rPropertyHolders
.Current(), aLastColor
.getBColor());
1435 aLastColor
= pA
->GetColor();
1438 const Point
& rPoint
= pA
->GetPoint();
1439 aPositions
.emplace_back(rPoint
.X(), rPoint
.Y());
1440 nAction
++; if(nAction
< nCount
) pAction
= rMetaFile
.GetAction(nAction
);
1445 if(!aPositions
.empty())
1447 createPointArrayPrimitive(std::move(aPositions
), rTargetHolders
.Current(), rPropertyHolders
.Current(), aLastColor
.getBColor());
1452 case MetaActionType::POINT
:
1454 /** CHECKED, WORKS WELL */
1455 if(rPropertyHolders
.Current().getLineColorActive())
1457 std::vector
< basegfx::B2DPoint
> aPositions
;
1459 while(MetaActionType::POINT
== pAction
->GetType() && nAction
< nCount
)
1461 const MetaPointAction
* pA
= static_cast<const MetaPointAction
*>(pAction
);
1462 const Point
& rPoint
= pA
->GetPoint();
1463 aPositions
.emplace_back(rPoint
.X(), rPoint
.Y());
1464 nAction
++; if(nAction
< nCount
) pAction
= rMetaFile
.GetAction(nAction
);
1469 if(!aPositions
.empty())
1471 createPointArrayPrimitive(std::move(aPositions
), rTargetHolders
.Current(), rPropertyHolders
.Current(), rPropertyHolders
.Current().getLineColor());
1477 case MetaActionType::LINE
:
1479 /** CHECKED, WORKS WELL */
1480 if(rPropertyHolders
.Current().getLineColorActive())
1482 basegfx::B2DPolygon aLinePolygon
;
1485 while(MetaActionType::LINE
== pAction
->GetType() && nAction
< nCount
)
1487 const MetaLineAction
* pA
= static_cast<const MetaLineAction
*>(pAction
);
1488 const Point
& rStartPoint
= pA
->GetStartPoint();
1489 const Point
& rEndPoint
= pA
->GetEndPoint();
1490 const basegfx::B2DPoint
aStart(rStartPoint
.X(), rStartPoint
.Y());
1491 const basegfx::B2DPoint
aEnd(rEndPoint
.X(), rEndPoint
.Y());
1493 if(aLinePolygon
.count())
1495 if(pA
->GetLineInfo() == aLineInfo
1496 && aStart
== aLinePolygon
.getB2DPoint(aLinePolygon
.count() - 1))
1498 aLinePolygon
.append(aEnd
);
1502 createLinePrimitive(aLinePolygon
, aLineInfo
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1503 aLinePolygon
.clear();
1504 aLineInfo
= pA
->GetLineInfo();
1505 aLinePolygon
.append(aStart
);
1506 aLinePolygon
.append(aEnd
);
1511 aLineInfo
= pA
->GetLineInfo();
1512 aLinePolygon
.append(aStart
);
1513 aLinePolygon
.append(aEnd
);
1517 if (nAction
< nCount
)
1518 pAction
= rMetaFile
.GetAction(nAction
);
1522 if (aLinePolygon
.count())
1523 createLinePrimitive(aLinePolygon
, aLineInfo
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1528 case MetaActionType::RECT
:
1530 /** CHECKED, WORKS WELL */
1531 if(rPropertyHolders
.Current().getLineOrFillActive())
1533 const MetaRectAction
* pA
= static_cast<const MetaRectAction
*>(pAction
);
1534 const tools::Rectangle
& rRectangle
= pA
->GetRect();
1536 if(!rRectangle
.IsEmpty())
1538 const basegfx::B2DRange aRange
= vcl::unotools::b2DRectangleFromRectangle(rRectangle
);
1540 if(!aRange
.isEmpty())
1542 const basegfx::B2DPolygon
aOutline(basegfx::utils::createPolygonFromRect(aRange
));
1543 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1550 case MetaActionType::ROUNDRECT
:
1552 /** CHECKED, WORKS WELL */
1553 /** The original OutputDevice::DrawRect paints nothing when nHor or nVer is zero; but just
1554 because the tools::Polygon operator creating the rounding does produce nonsense. I assume
1555 this an error and create an unrounded rectangle in that case (implicit in
1556 createPolygonFromRect)
1558 if(rPropertyHolders
.Current().getLineOrFillActive())
1560 const MetaRoundRectAction
* pA
= static_cast<const MetaRoundRectAction
*>(pAction
);
1561 const tools::Rectangle
& rRectangle
= pA
->GetRect();
1563 if(!rRectangle
.IsEmpty())
1565 const basegfx::B2DRange aRange
= vcl::unotools::b2DRectangleFromRectangle(rRectangle
);
1567 if(!aRange
.isEmpty())
1569 const sal_uInt32
nHor(pA
->GetHorzRound());
1570 const sal_uInt32
nVer(pA
->GetVertRound());
1571 basegfx::B2DPolygon aOutline
;
1575 double fRadiusX((nHor
* 2.0) / (aRange
.getWidth() > 0.0 ? aRange
.getWidth() : 1.0));
1576 double fRadiusY((nVer
* 2.0) / (aRange
.getHeight() > 0.0 ? aRange
.getHeight() : 1.0));
1577 fRadiusX
= std::clamp(fRadiusX
, 0.0, 1.0);
1578 fRadiusY
= std::clamp(fRadiusY
, 0.0, 1.0);
1580 aOutline
= basegfx::utils::createPolygonFromRect(aRange
, fRadiusX
, fRadiusY
);
1584 aOutline
= basegfx::utils::createPolygonFromRect(aRange
);
1587 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1594 case MetaActionType::ELLIPSE
:
1596 /** CHECKED, WORKS WELL */
1597 if(rPropertyHolders
.Current().getLineOrFillActive())
1599 const MetaEllipseAction
* pA
= static_cast<const MetaEllipseAction
*>(pAction
);
1600 const tools::Rectangle
& rRectangle
= pA
->GetRect();
1602 if(!rRectangle
.IsEmpty())
1604 const basegfx::B2DRange aRange
= vcl::unotools::b2DRectangleFromRectangle(rRectangle
);
1606 if(!aRange
.isEmpty())
1608 const basegfx::B2DPolygon
aOutline(basegfx::utils::createPolygonFromEllipse(
1609 aRange
.getCenter(), aRange
.getWidth() * 0.5, aRange
.getHeight() * 0.5));
1611 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1618 case MetaActionType::ARC
:
1620 /** CHECKED, WORKS WELL */
1621 if(rPropertyHolders
.Current().getLineColorActive())
1623 const MetaArcAction
* pA
= static_cast<const MetaArcAction
*>(pAction
);
1624 const tools::Polygon
aToolsPoly(pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), PolyStyle::Arc
);
1625 const basegfx::B2DPolygon
aOutline(aToolsPoly
.getB2DPolygon());
1627 createHairlinePrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1632 case MetaActionType::PIE
:
1634 /** CHECKED, WORKS WELL */
1635 if(rPropertyHolders
.Current().getLineOrFillActive())
1637 const MetaPieAction
* pA
= static_cast<const MetaPieAction
*>(pAction
);
1638 const tools::Polygon
aToolsPoly(pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), PolyStyle::Pie
);
1639 const basegfx::B2DPolygon
aOutline(aToolsPoly
.getB2DPolygon());
1641 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1646 case MetaActionType::CHORD
:
1648 /** CHECKED, WORKS WELL */
1649 if(rPropertyHolders
.Current().getLineOrFillActive())
1651 const MetaChordAction
* pA
= static_cast<const MetaChordAction
*>(pAction
);
1652 const tools::Polygon
aToolsPoly(pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), PolyStyle::Chord
);
1653 const basegfx::B2DPolygon
aOutline(aToolsPoly
.getB2DPolygon());
1655 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1660 case MetaActionType::POLYLINE
:
1662 /** CHECKED, WORKS WELL */
1663 if(rPropertyHolders
.Current().getLineColorActive())
1665 const MetaPolyLineAction
* pA
= static_cast<const MetaPolyLineAction
*>(pAction
);
1666 createLinePrimitive(pA
->GetPolygon().getB2DPolygon(), pA
->GetLineInfo(), rTargetHolders
.Current(), rPropertyHolders
.Current());
1671 case MetaActionType::POLYGON
:
1673 /** CHECKED, WORKS WELL */
1674 if(rPropertyHolders
.Current().getLineOrFillActive())
1676 const MetaPolygonAction
* pA
= static_cast<const MetaPolygonAction
*>(pAction
);
1677 basegfx::B2DPolygon
aOutline(pA
->GetPolygon().getB2DPolygon());
1679 // the metafile play interprets the polygons from MetaPolygonAction
1680 // always as closed and always paints an edge from last to first point,
1681 // so force to closed here to emulate that
1682 if(aOutline
.count() > 1 && !aOutline
.isClosed())
1684 aOutline
.setClosed(true);
1687 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1692 case MetaActionType::POLYPOLYGON
:
1694 /** CHECKED, WORKS WELL */
1695 if(rPropertyHolders
.Current().getLineOrFillActive())
1697 const MetaPolyPolygonAction
* pA
= static_cast<const MetaPolyPolygonAction
*>(pAction
);
1698 basegfx::B2DPolyPolygon
aPolyPolygonOutline(pA
->GetPolyPolygon().getB2DPolyPolygon());
1700 // the metafile play interprets the single polygons from MetaPolyPolygonAction
1701 // always as closed and always paints an edge from last to first point,
1702 // so force to closed here to emulate that
1703 for(sal_uInt32
b(0); b
< aPolyPolygonOutline
.count(); b
++)
1705 basegfx::B2DPolygon
aPolygonOutline(aPolyPolygonOutline
.getB2DPolygon(b
));
1707 if(aPolygonOutline
.count() > 1 && !aPolygonOutline
.isClosed())
1709 aPolygonOutline
.setClosed(true);
1710 aPolyPolygonOutline
.setB2DPolygon(b
, aPolygonOutline
);
1714 createHairlineAndFillPrimitive(aPolyPolygonOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1719 case MetaActionType::TEXT
:
1721 /** CHECKED, WORKS WELL */
1722 const MetaTextAction
* pA
= static_cast<const MetaTextAction
*>(pAction
);
1723 sal_uInt32
nTextLength(pA
->GetLen());
1724 const sal_uInt32
nTextIndex(pA
->GetIndex());
1725 const sal_uInt32
nStringLength(pA
->GetText().getLength());
1727 if(nTextLength
+ nTextIndex
> nStringLength
)
1729 nTextLength
= nStringLength
- nTextIndex
;
1732 if(nTextLength
&& rPropertyHolders
.Current().getTextColorActive())
1734 std::vector
< double > aDXArray
{};
1735 processMetaTextAction(
1740 std::move(aDXArray
),
1742 rTargetHolders
.Current(),
1743 rPropertyHolders
.Current());
1748 case MetaActionType::TEXTARRAY
:
1750 /** CHECKED, WORKS WELL */
1751 const MetaTextArrayAction
* pA
= static_cast<const MetaTextArrayAction
*>(pAction
);
1752 sal_uInt32
nTextLength(pA
->GetLen());
1753 const sal_uInt32
nTextIndex(pA
->GetIndex());
1754 const sal_uInt32
nStringLength(pA
->GetText().getLength());
1756 if(nTextLength
+ nTextIndex
> nStringLength
)
1758 nTextLength
= nTextIndex
> nStringLength
? 0 : nStringLength
- nTextIndex
;
1761 if(nTextLength
&& rPropertyHolders
.Current().getTextColorActive())
1763 // prepare DXArray (if used)
1764 std::vector
< double > aDXArray
;
1765 const KernArray
& rDXArray
= pA
->GetDXArray();
1766 std::vector
< sal_Bool
> aKashidaArray
= pA
->GetKashidaArray();
1768 if(!rDXArray
.empty())
1770 aDXArray
.reserve(nTextLength
);
1772 for(sal_uInt32
a(0); a
< nTextLength
; a
++)
1774 aDXArray
.push_back(static_cast<double>(rDXArray
[a
]));
1778 processMetaTextAction(
1783 std::move(aDXArray
),
1784 std::move(aKashidaArray
),
1785 rTargetHolders
.Current(),
1786 rPropertyHolders
.Current());
1791 case MetaActionType::STRETCHTEXT
:
1793 // #i108440# StarMath uses MetaStretchTextAction, thus support is needed.
1794 // It looks as if it pretty never really uses a width different from
1795 // the default text-layout width, but it's not possible to be sure.
1796 // Implemented getting the DXArray and checking for scale at all. If
1797 // scale is more than 3.5% different, scale the DXArray before usage.
1800 /** CHECKED, WORKS WELL */
1801 const MetaStretchTextAction
* pA
= static_cast<const MetaStretchTextAction
*>(pAction
);
1802 sal_uInt32
nTextLength(pA
->GetLen());
1803 const sal_uInt32
nTextIndex(pA
->GetIndex());
1804 const sal_uInt32
nStringLength(pA
->GetText().getLength());
1806 if(nTextLength
+ nTextIndex
> nStringLength
)
1808 nTextLength
= nStringLength
- nTextIndex
;
1811 if(nTextLength
&& rPropertyHolders
.Current().getTextColorActive())
1813 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice
;
1814 aTextLayouterDevice
.setFont(rPropertyHolders
.Current().getFont());
1816 std::vector
< double > aTextArray(
1817 aTextLayouterDevice
.getTextArray(
1822 if(!aTextArray
.empty())
1824 const double fTextLength(aTextArray
.back());
1826 if(0.0 != fTextLength
&& pA
->GetWidth())
1828 const double fRelative(pA
->GetWidth() / fTextLength
);
1830 if(fabs(fRelative
- 1.0) >= 0.035)
1832 // when derivation is more than 3,5% from default text size,
1833 // scale the DXArray
1834 for(double & a
: aTextArray
)
1842 processMetaTextAction(
1847 std::move(aTextArray
),
1849 rTargetHolders
.Current(),
1850 rPropertyHolders
.Current());
1855 case MetaActionType::TEXTRECT
:
1857 /** CHECKED, WORKS WELL */
1858 // OSL_FAIL("MetaActionType::TEXTRECT requested (!)");
1859 const MetaTextRectAction
* pA
= static_cast<const MetaTextRectAction
*>(pAction
);
1860 const tools::Rectangle
& rRectangle
= pA
->GetRect();
1861 const sal_uInt32
nStringLength(pA
->GetText().getLength());
1863 if(!rRectangle
.IsEmpty() && 0 != nStringLength
)
1865 // The problem with this action is that it describes unlayouted text
1866 // and the layout capabilities are in EditEngine/Outliner in SVX. The
1867 // same problem is true for VCL which internally has implementations
1868 // to layout text in this case. There exists even a call
1869 // OutputDevice::AddTextRectActions(...) to create the needed actions
1870 // as 'sub-content' of a Metafile. Unfortunately i do not have an
1871 // OutputDevice here since this interpreter tries to work without
1873 // Since AddTextRectActions is the only way as long as we do not have
1874 // a simple text layouter available, i will try to add it to the
1875 // TextLayouterDevice isolation.
1876 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice
;
1877 aTextLayouterDevice
.setFont(rPropertyHolders
.Current().getFont());
1878 GDIMetaFile aGDIMetaFile
;
1880 aTextLayouterDevice
.addTextRectActions(
1881 rRectangle
, pA
->GetText(), pA
->GetStyle(), aGDIMetaFile
);
1883 if(aGDIMetaFile
.GetActionSize())
1885 // create sub-content
1886 drawinglayer::primitive2d::Primitive2DContainer xSubContent
;
1888 rTargetHolders
.Push();
1890 // for sub-Mteafile contents, do start with new, default render state
1891 // #i124686# ...but copy font, this is already set accordingly
1892 vcl::Font aTargetFont
= rPropertyHolders
.Current().getFont();
1893 rPropertyHolders
.PushDefault();
1894 rPropertyHolders
.Current().setFont(aTargetFont
);
1896 implInterpretMetafile(aGDIMetaFile
, rTargetHolders
, rPropertyHolders
, rViewInformation
);
1897 xSubContent
= rTargetHolders
.Current().getPrimitive2DSequence(rPropertyHolders
.Current());
1898 rPropertyHolders
.Pop();
1899 rTargetHolders
.Pop();
1902 if(!xSubContent
.empty())
1904 // add with transformation
1905 rTargetHolders
.Current().append(
1906 new drawinglayer::primitive2d::TransformPrimitive2D(
1907 rPropertyHolders
.Current().getTransformation(),
1908 std::move(xSubContent
)));
1915 case MetaActionType::BMP
:
1917 /** CHECKED, WORKS WELL */
1918 const MetaBmpAction
* pA
= static_cast<const MetaBmpAction
*>(pAction
);
1919 const BitmapEx
aBitmapEx(pA
->GetBitmap());
1921 createBitmapExPrimitive(aBitmapEx
, pA
->GetPoint(), rTargetHolders
.Current(), rPropertyHolders
.Current());
1925 case MetaActionType::BMPSCALE
:
1927 /** CHECKED, WORKS WELL */
1928 const MetaBmpScaleAction
* pA
= static_cast<const MetaBmpScaleAction
*>(pAction
);
1929 const BitmapEx
aBitmapEx(pA
->GetBitmap());
1931 createBitmapExPrimitive(aBitmapEx
, pA
->GetPoint(), pA
->GetSize(), rTargetHolders
.Current(), rPropertyHolders
.Current());
1935 case MetaActionType::BMPSCALEPART
:
1937 /** CHECKED, WORKS WELL */
1938 const MetaBmpScalePartAction
* pA
= static_cast<const MetaBmpScalePartAction
*>(pAction
);
1939 const Bitmap
& rBitmap
= pA
->GetBitmap();
1941 if(!rBitmap
.IsEmpty())
1943 Bitmap
aCroppedBitmap(rBitmap
);
1944 const tools::Rectangle
aCropRectangle(pA
->GetSrcPoint(), pA
->GetSrcSize());
1946 if(!aCropRectangle
.IsEmpty())
1948 aCroppedBitmap
.Crop(aCropRectangle
);
1951 const BitmapEx
aCroppedBitmapEx(aCroppedBitmap
);
1952 createBitmapExPrimitive(aCroppedBitmapEx
, pA
->GetDestPoint(), pA
->GetDestSize(), rTargetHolders
.Current(), rPropertyHolders
.Current());
1957 case MetaActionType::BMPEX
:
1959 /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMP */
1960 const MetaBmpExAction
* pA
= static_cast<const MetaBmpExAction
*>(pAction
);
1961 const BitmapEx
& rBitmapEx
= pA
->GetBitmapEx();
1963 createBitmapExPrimitive(rBitmapEx
, pA
->GetPoint(), rTargetHolders
.Current(), rPropertyHolders
.Current());
1967 case MetaActionType::BMPEXSCALE
:
1969 /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALE */
1970 const MetaBmpExScaleAction
* pA
= static_cast<const MetaBmpExScaleAction
*>(pAction
);
1971 const BitmapEx
& rBitmapEx
= pA
->GetBitmapEx();
1973 createBitmapExPrimitive(rBitmapEx
, pA
->GetPoint(), pA
->GetSize(), rTargetHolders
.Current(), rPropertyHolders
.Current());
1977 case MetaActionType::BMPEXSCALEPART
:
1979 /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALEPART */
1980 const MetaBmpExScalePartAction
* pA
= static_cast<const MetaBmpExScalePartAction
*>(pAction
);
1981 const BitmapEx
& rBitmapEx
= pA
->GetBitmapEx();
1983 if(!rBitmapEx
.IsEmpty())
1985 BitmapEx
aCroppedBitmapEx(rBitmapEx
);
1986 const tools::Rectangle
aCropRectangle(pA
->GetSrcPoint(), pA
->GetSrcSize());
1988 if(!aCropRectangle
.IsEmpty())
1990 aCroppedBitmapEx
.Crop(aCropRectangle
);
1993 createBitmapExPrimitive(aCroppedBitmapEx
, pA
->GetDestPoint(), pA
->GetDestSize(), rTargetHolders
.Current(), rPropertyHolders
.Current());
1998 case MetaActionType::MASK
:
2000 /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMP */
2001 /** Huh, no it isn't!? */
2002 const MetaMaskAction
* pA
= static_cast<const MetaMaskAction
*>(pAction
);
2003 const BitmapEx
aBitmapEx(createMaskBmpEx(pA
->GetBitmap(), pA
->GetColor()));
2005 createBitmapExPrimitive(aBitmapEx
, pA
->GetPoint(), rTargetHolders
.Current(), rPropertyHolders
.Current());
2009 case MetaActionType::MASKSCALE
:
2011 /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALE */
2012 const MetaMaskScaleAction
* pA
= static_cast<const MetaMaskScaleAction
*>(pAction
);
2013 const BitmapEx
aBitmapEx(createMaskBmpEx(pA
->GetBitmap(), pA
->GetColor()));
2015 createBitmapExPrimitive(aBitmapEx
, pA
->GetPoint(), pA
->GetSize(), rTargetHolders
.Current(), rPropertyHolders
.Current());
2019 case MetaActionType::MASKSCALEPART
:
2021 /** CHECKED, WORKS WELL: Simply same as MetaActionType::BMPSCALEPART */
2022 const MetaMaskScalePartAction
* pA
= static_cast<const MetaMaskScalePartAction
*>(pAction
);
2023 const Bitmap
& rBitmap
= pA
->GetBitmap();
2025 if(!rBitmap
.IsEmpty())
2027 Bitmap
aCroppedBitmap(rBitmap
);
2028 const tools::Rectangle
aCropRectangle(pA
->GetSrcPoint(), pA
->GetSrcSize());
2030 if(!aCropRectangle
.IsEmpty())
2032 aCroppedBitmap
.Crop(aCropRectangle
);
2035 const BitmapEx
aCroppedBitmapEx(createMaskBmpEx(aCroppedBitmap
, pA
->GetColor()));
2036 createBitmapExPrimitive(aCroppedBitmapEx
, pA
->GetDestPoint(), pA
->GetDestSize(), rTargetHolders
.Current(), rPropertyHolders
.Current());
2041 case MetaActionType::GRADIENT
:
2043 /** CHECKED, WORKS WELL */
2044 const MetaGradientAction
* pA
= static_cast<const MetaGradientAction
*>(pAction
);
2045 const tools::Rectangle
& rRectangle
= pA
->GetRect();
2047 if(!rRectangle
.IsEmpty())
2049 basegfx::B2DRange aRange
= vcl::unotools::b2DRectangleFromRectangle(rRectangle
);
2051 if(!aRange
.isEmpty())
2053 const Gradient
& rGradient
= pA
->GetGradient();
2054 drawinglayer::attribute::FillGradientAttribute
aAttribute(createFillGradientAttribute(rGradient
));
2055 basegfx::B2DPolyPolygon
aOutline(basegfx::utils::createPolygonFromRect(aRange
));
2056 basegfx::BColor aSingleColor
;
2058 if (aAttribute
.getColorStops().isSingleColor(aSingleColor
))
2060 // not really a gradient. Create filled rectangle
2061 createFillPrimitive(
2063 rTargetHolders
.Current(),
2064 rPropertyHolders
.Current());
2068 // really a gradient
2069 aRange
.transform(rPropertyHolders
.Current().getTransformation());
2070 drawinglayer::primitive2d::Primitive2DContainer
xGradient(1);
2072 if(rPropertyHolders
.Current().isRasterOpInvert())
2074 // use a special version of FillGradientPrimitive2D which creates
2075 // non-overlapping geometry on decomposition to make the old XOR
2076 // paint 'trick' work.
2077 xGradient
[0] = drawinglayer::primitive2d::Primitive2DReference(
2078 new drawinglayer::primitive2d::NonOverlappingFillGradientPrimitive2D(
2084 xGradient
[0] = drawinglayer::primitive2d::Primitive2DReference(
2085 new drawinglayer::primitive2d::FillGradientPrimitive2D(
2087 std::move(aAttribute
)));
2090 // #i112300# clip against polygon representing the rectangle from
2091 // the action. This is implicitly done using a temp Clipping in VCL
2092 // when a MetaGradientAction is executed
2093 aOutline
.transform(rPropertyHolders
.Current().getTransformation());
2094 rTargetHolders
.Current().append(
2095 new drawinglayer::primitive2d::MaskPrimitive2D(
2096 std::move(aOutline
),
2097 std::move(xGradient
)));
2104 case MetaActionType::HATCH
:
2106 /** CHECKED, WORKS WELL */
2107 const MetaHatchAction
* pA
= static_cast<const MetaHatchAction
*>(pAction
);
2108 basegfx::B2DPolyPolygon
aOutline(pA
->GetPolyPolygon().getB2DPolyPolygon());
2110 if(aOutline
.count())
2112 const Hatch
& rHatch
= pA
->GetHatch();
2113 drawinglayer::attribute::FillHatchAttribute
aAttribute(createFillHatchAttribute(rHatch
));
2115 aOutline
.transform(rPropertyHolders
.Current().getTransformation());
2117 const basegfx::B2DRange
aObjectRange(aOutline
.getB2DRange());
2118 const drawinglayer::primitive2d::Primitive2DReference
aFillHatch(
2119 new drawinglayer::primitive2d::FillHatchPrimitive2D(
2122 std::move(aAttribute
)));
2124 rTargetHolders
.Current().append(
2125 new drawinglayer::primitive2d::MaskPrimitive2D(
2126 std::move(aOutline
),
2127 drawinglayer::primitive2d::Primitive2DContainer
{ aFillHatch
}));
2132 case MetaActionType::WALLPAPER
:
2134 /** CHECKED, WORKS WELL */
2135 const MetaWallpaperAction
* pA
= static_cast<const MetaWallpaperAction
*>(pAction
);
2136 tools::Rectangle
aWallpaperRectangle(pA
->GetRect());
2138 if(!aWallpaperRectangle
.IsEmpty())
2140 const Wallpaper
& rWallpaper
= pA
->GetWallpaper();
2141 const WallpaperStyle
eWallpaperStyle(rWallpaper
.GetStyle());
2142 basegfx::B2DRange aWallpaperRange
= vcl::unotools::b2DRectangleFromRectangle(aWallpaperRectangle
);
2144 if(WallpaperStyle::NONE
!= eWallpaperStyle
)
2146 if(rWallpaper
.IsBitmap())
2148 // create bitmap background. Caution: This
2149 // also will create gradient/color background(s)
2150 // when the bitmap is transparent or not tiled
2151 CreateAndAppendBitmapWallpaper(
2154 rTargetHolders
.Current(),
2155 rPropertyHolders
.Current());
2157 else if(rWallpaper
.IsGradient())
2159 // create gradient background
2160 rTargetHolders
.Current().append(
2161 CreateGradientWallpaper(
2163 rWallpaper
.GetGradient(),
2164 rPropertyHolders
.Current()));
2166 else if(!rWallpaper
.GetColor().IsTransparent())
2168 // create color background
2169 rTargetHolders
.Current().append(
2170 CreateColorWallpaper(
2172 rWallpaper
.GetColor().getBColor(),
2173 rPropertyHolders
.Current()));
2180 case MetaActionType::CLIPREGION
:
2182 /** CHECKED, WORKS WELL */
2183 const MetaClipRegionAction
* pA
= static_cast<const MetaClipRegionAction
*>(pAction
);
2185 if(pA
->IsClipping())
2187 // new clipping. Get tools::PolyPolygon and transform with current transformation
2188 basegfx::B2DPolyPolygon
aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(pA
->GetRegion()));
2190 aNewClipPolyPolygon
.transform(rPropertyHolders
.Current().getTransformation());
2191 HandleNewClipRegion(aNewClipPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2196 const basegfx::B2DPolyPolygon aEmptyPolyPolygon
;
2198 HandleNewClipRegion(aEmptyPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2203 case MetaActionType::ISECTRECTCLIPREGION
:
2205 /** CHECKED, WORKS WELL */
2206 const MetaISectRectClipRegionAction
* pA
= static_cast<const MetaISectRectClipRegionAction
*>(pAction
);
2207 const tools::Rectangle
& rRectangle
= pA
->GetRect();
2209 if(rRectangle
.IsEmpty())
2211 // intersect with empty rectangle will always give empty
2212 // ClipPolyPolygon; start new clipping with empty PolyPolygon
2213 const basegfx::B2DPolyPolygon aEmptyPolyPolygon
;
2215 HandleNewClipRegion(aEmptyPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2219 // create transformed ClipRange
2220 basegfx::B2DRange aClipRange
= vcl::unotools::b2DRectangleFromRectangle(rRectangle
);
2222 aClipRange
.transform(rPropertyHolders
.Current().getTransformation());
2224 if(rPropertyHolders
.Current().getClipPolyPolygonActive())
2226 if(0 == rPropertyHolders
.Current().getClipPolyPolygon().count())
2228 // nothing to do, empty active clipPolyPolygon will stay
2229 // empty when intersecting
2233 // AND existing region and new ClipRange
2234 const basegfx::B2DPolyPolygon
aOriginalPolyPolygon(
2235 rPropertyHolders
.Current().getClipPolyPolygon());
2236 basegfx::B2DPolyPolygon aClippedPolyPolygon
;
2238 if(aOriginalPolyPolygon
.count())
2240 aClippedPolyPolygon
= basegfx::utils::clipPolyPolygonOnRange(
2241 aOriginalPolyPolygon
,
2247 if(aClippedPolyPolygon
!= aOriginalPolyPolygon
)
2249 // start new clipping with intersected region
2250 HandleNewClipRegion(
2251 aClippedPolyPolygon
,
2259 // start new clipping with ClipRange
2260 const basegfx::B2DPolyPolygon
aNewClipPolyPolygon(
2261 basegfx::utils::createPolygonFromRect(aClipRange
));
2263 HandleNewClipRegion(aNewClipPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2269 case MetaActionType::ISECTREGIONCLIPREGION
:
2271 /** CHECKED, WORKS WELL */
2272 const MetaISectRegionClipRegionAction
* pA
= static_cast<const MetaISectRegionClipRegionAction
*>(pAction
);
2273 const vcl::Region
& rNewRegion
= pA
->GetRegion();
2275 if(rNewRegion
.IsEmpty())
2277 // intersect with empty region will always give empty
2278 // region; start new clipping with empty PolyPolygon
2279 const basegfx::B2DPolyPolygon aEmptyPolyPolygon
;
2281 HandleNewClipRegion(aEmptyPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2285 // get new ClipPolyPolygon, transform it with current transformation
2286 basegfx::B2DPolyPolygon
aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(rNewRegion
));
2287 aNewClipPolyPolygon
.transform(rPropertyHolders
.Current().getTransformation());
2289 if(rPropertyHolders
.Current().getClipPolyPolygonActive())
2291 if(0 == rPropertyHolders
.Current().getClipPolyPolygon().count())
2293 // nothing to do, empty active clipPolyPolygon will stay empty
2294 // when intersecting with any region
2298 // AND existing and new region
2299 const basegfx::B2DPolyPolygon
aOriginalPolyPolygon(
2300 rPropertyHolders
.Current().getClipPolyPolygon());
2301 basegfx::B2DPolyPolygon aClippedPolyPolygon
;
2303 if(aOriginalPolyPolygon
.count())
2305 aClippedPolyPolygon
= basegfx::utils::clipPolyPolygonOnPolyPolygon(
2306 aOriginalPolyPolygon
, aNewClipPolyPolygon
, true, false);
2309 if(aClippedPolyPolygon
!= aOriginalPolyPolygon
)
2311 // start new clipping with intersected ClipPolyPolygon
2312 HandleNewClipRegion(aClippedPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2318 // start new clipping with new ClipPolyPolygon
2319 HandleNewClipRegion(aNewClipPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2325 case MetaActionType::MOVECLIPREGION
:
2327 /** CHECKED, WORKS WELL */
2328 const MetaMoveClipRegionAction
* pA
= static_cast<const MetaMoveClipRegionAction
*>(pAction
);
2330 if(rPropertyHolders
.Current().getClipPolyPolygonActive())
2332 if(0 == rPropertyHolders
.Current().getClipPolyPolygon().count())
2338 const sal_Int32
nHor(pA
->GetHorzMove());
2339 const sal_Int32
nVer(pA
->GetVertMove());
2341 if(0 != nHor
|| 0 != nVer
)
2343 // prepare translation, add current transformation
2344 basegfx::B2DVector
aVector(pA
->GetHorzMove(), pA
->GetVertMove());
2345 aVector
*= rPropertyHolders
.Current().getTransformation();
2346 basegfx::B2DHomMatrix
aTransform(
2347 basegfx::utils::createTranslateB2DHomMatrix(aVector
));
2349 // transform existing region
2350 basegfx::B2DPolyPolygon
aClipPolyPolygon(
2351 rPropertyHolders
.Current().getClipPolyPolygon());
2353 aClipPolyPolygon
.transform(aTransform
);
2354 HandleNewClipRegion(aClipPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2361 case MetaActionType::LINECOLOR
:
2363 /** CHECKED, WORKS WELL */
2364 const MetaLineColorAction
* pA
= static_cast<const MetaLineColorAction
*>(pAction
);
2365 // tdf#89901 do as OutDev does: COL_TRANSPARENT deactivates line draw
2366 const bool bActive(pA
->IsSetting() && COL_TRANSPARENT
!= pA
->GetColor());
2368 rPropertyHolders
.Current().setLineColorActive(bActive
);
2370 rPropertyHolders
.Current().setLineColor(pA
->GetColor().getBColor());
2374 case MetaActionType::FILLCOLOR
:
2376 /** CHECKED, WORKS WELL */
2377 const MetaFillColorAction
* pA
= static_cast<const MetaFillColorAction
*>(pAction
);
2378 // tdf#89901 do as OutDev does: COL_TRANSPARENT deactivates polygon fill
2379 const bool bActive(pA
->IsSetting() && COL_TRANSPARENT
!= pA
->GetColor());
2381 rPropertyHolders
.Current().setFillColorActive(bActive
);
2383 rPropertyHolders
.Current().setFillColor(pA
->GetColor().getBColor());
2387 case MetaActionType::TEXTCOLOR
:
2390 const MetaTextColorAction
* pA
= static_cast<const MetaTextColorAction
*>(pAction
);
2391 const bool bActivate(COL_TRANSPARENT
!= pA
->GetColor());
2393 rPropertyHolders
.Current().setTextColorActive(bActivate
);
2394 rPropertyHolders
.Current().setTextColor(pA
->GetColor().getBColor());
2398 case MetaActionType::TEXTFILLCOLOR
:
2401 const MetaTextFillColorAction
* pA
= static_cast<const MetaTextFillColorAction
*>(pAction
);
2402 const bool bWithColorArgument(pA
->IsSetting());
2404 if(bWithColorArgument
)
2406 // emulate OutputDevice::SetTextFillColor(...) WITH argument
2407 const Color
& rFontFillColor
= pA
->GetColor();
2408 rPropertyHolders
.Current().setTextFillColor(rFontFillColor
.getBColor());
2409 rPropertyHolders
.Current().setTextFillColorActive(COL_TRANSPARENT
!= rFontFillColor
);
2413 // emulate SetFillColor() <- NO argument (!)
2414 rPropertyHolders
.Current().setTextFillColorActive(false);
2419 case MetaActionType::TEXTALIGN
:
2422 const MetaTextAlignAction
* pA
= static_cast<const MetaTextAlignAction
*>(pAction
);
2423 const TextAlign aNewTextAlign
= pA
->GetTextAlign();
2425 // TextAlign is applied to the current font (as in
2426 // OutputDevice::SetTextAlign which would be used when
2427 // playing the Metafile)
2428 if(rPropertyHolders
.Current().getFont().GetAlignment() != aNewTextAlign
)
2430 vcl::Font
aNewFont(rPropertyHolders
.Current().getFont());
2431 aNewFont
.SetAlignment(aNewTextAlign
);
2432 rPropertyHolders
.Current().setFont(aNewFont
);
2437 case MetaActionType::MAPMODE
:
2439 /** CHECKED, WORKS WELL */
2440 // the most necessary MapMode to be interpreted is MapUnit::MapRelative,
2441 // but also the others may occur. Even not yet supported ones
2442 // may need to be added here later
2443 const MetaMapModeAction
* pA
= static_cast<const MetaMapModeAction
*>(pAction
);
2444 const MapMode
& rMapMode
= pA
->GetMapMode();
2445 basegfx::B2DHomMatrix aMapping
;
2447 if(MapUnit::MapRelative
== rMapMode
.GetMapUnit())
2449 aMapping
= getTransformFromMapMode(rMapMode
);
2453 const auto eFrom
= MapToO3tlLength(rPropertyHolders
.Current().getMapUnit()),
2454 eTo
= MapToO3tlLength(rMapMode
.GetMapUnit());
2455 if (eFrom
!= o3tl::Length::invalid
&& eTo
!= o3tl::Length::invalid
)
2457 const double fConvert(o3tl::convert(1.0, eFrom
, eTo
));
2458 aMapping
.scale(fConvert
, fConvert
);
2461 OSL_FAIL("implInterpretMetafile: MetaActionType::MAPMODE with unsupported MapUnit (!)");
2463 aMapping
= getTransformFromMapMode(rMapMode
) * aMapping
;
2464 rPropertyHolders
.Current().setMapUnit(rMapMode
.GetMapUnit());
2467 if(!aMapping
.isIdentity())
2469 aMapping
= aMapping
* rPropertyHolders
.Current().getTransformation();
2470 rPropertyHolders
.Current().setTransformation(aMapping
);
2475 case MetaActionType::FONT
:
2478 const MetaFontAction
* pA
= static_cast<const MetaFontAction
*>(pAction
);
2479 rPropertyHolders
.Current().setFont(pA
->GetFont());
2480 Size
aFontSize(pA
->GetFont().GetFontSize());
2482 if(0 == aFontSize
.Height())
2484 // this should not happen but i got Metafiles where this was the
2485 // case. A height needs to be guessed (similar to OutputDevice::ImplNewFont())
2486 vcl::Font
aCorrectedFont(pA
->GetFont());
2488 // guess 16 pixel (as in VCL)
2489 aFontSize
= Size(0, 16);
2491 // convert to target MapUnit if not pixels
2492 aFontSize
= OutputDevice::LogicToLogic(
2493 aFontSize
, MapMode(MapUnit::MapPixel
), MapMode(rPropertyHolders
.Current().getMapUnit()));
2495 aCorrectedFont
.SetFontSize(aFontSize
);
2496 rPropertyHolders
.Current().setFont(aCorrectedFont
);
2499 // older Metafiles have no MetaActionType::TEXTCOLOR which defines
2500 // the FontColor now, so use the Font's color when not transparent
2501 const Color
& rFontColor
= pA
->GetFont().GetColor();
2502 const bool bActivate(COL_TRANSPARENT
!= rFontColor
);
2506 rPropertyHolders
.Current().setTextColor(rFontColor
.getBColor());
2509 // caution: do NOT deactivate here on transparent, see
2510 // OutputDevice::SetFont(..) for more info
2511 // rPropertyHolders.Current().setTextColorActive(bActivate);
2513 // for fill color emulate a MetaTextFillColorAction with !transparent as bool,
2514 // see OutputDevice::SetFont(..) the if(mpMetaFile) case
2517 const Color
& rFontFillColor
= pA
->GetFont().GetFillColor();
2518 rPropertyHolders
.Current().setTextFillColor(rFontFillColor
.getBColor());
2519 rPropertyHolders
.Current().setTextFillColorActive(COL_TRANSPARENT
!= rFontFillColor
);
2523 rPropertyHolders
.Current().setTextFillColorActive(false);
2528 case MetaActionType::PUSH
:
2530 /** CHECKED, WORKS WELL */
2531 const MetaPushAction
* pA
= static_cast<const MetaPushAction
*>(pAction
);
2532 rPropertyHolders
.Push(pA
->GetFlags());
2536 case MetaActionType::POP
:
2538 /** CHECKED, WORKS WELL */
2539 const bool bRegionMayChange(rPropertyHolders
.Current().getPushFlags() & vcl::PushFlags::CLIPREGION
);
2540 const bool bRasterOpMayChange(rPropertyHolders
.Current().getPushFlags() & vcl::PushFlags::RASTEROP
);
2542 if(bRegionMayChange
&& rPropertyHolders
.Current().getClipPolyPolygonActive())
2544 // end evtl. clipping
2545 const basegfx::B2DPolyPolygon aEmptyPolyPolygon
;
2547 HandleNewClipRegion(aEmptyPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2550 if(bRasterOpMayChange
&& rPropertyHolders
.Current().isRasterOpActive())
2552 // end evtl. RasterOp
2553 HandleNewRasterOp(RasterOp::OverPaint
, rTargetHolders
, rPropertyHolders
);
2556 rPropertyHolders
.Pop();
2558 if(bRasterOpMayChange
&& rPropertyHolders
.Current().isRasterOpActive())
2560 // start evtl. RasterOp
2561 HandleNewRasterOp(rPropertyHolders
.Current().getRasterOp(), rTargetHolders
, rPropertyHolders
);
2564 if(bRegionMayChange
&& rPropertyHolders
.Current().getClipPolyPolygonActive())
2566 // start evtl. clipping
2567 HandleNewClipRegion(
2568 rPropertyHolders
.Current().getClipPolyPolygon(), rTargetHolders
, rPropertyHolders
);
2573 case MetaActionType::RASTEROP
:
2575 /** CHECKED, WORKS WELL */
2576 const MetaRasterOpAction
* pA
= static_cast<const MetaRasterOpAction
*>(pAction
);
2577 const RasterOp aRasterOp
= pA
->GetRasterOp();
2579 HandleNewRasterOp(aRasterOp
, rTargetHolders
, rPropertyHolders
);
2583 case MetaActionType::Transparent
:
2585 /** CHECKED, WORKS WELL */
2586 const MetaTransparentAction
* pA
= static_cast<const MetaTransparentAction
*>(pAction
);
2587 const basegfx::B2DPolyPolygon
aOutline(pA
->GetPolyPolygon().getB2DPolyPolygon());
2589 if(aOutline
.count())
2591 const sal_uInt16
nTransparence(pA
->GetTransparence());
2593 if(0 == nTransparence
)
2596 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
2598 else if(nTransparence
>= 100)
2600 // fully or more than transparent
2604 // transparent. Create new target
2605 rTargetHolders
.Push();
2607 // create primitives there and get them
2608 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
2609 drawinglayer::primitive2d::Primitive2DContainer
aSubContent(
2610 rTargetHolders
.Current().getPrimitive2DSequence(rPropertyHolders
.Current()));
2612 // back to old target
2613 rTargetHolders
.Pop();
2615 if(!aSubContent
.empty())
2617 rTargetHolders
.Current().append(
2618 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
2619 std::move(aSubContent
),
2620 nTransparence
* 0.01));
2627 case MetaActionType::EPS
:
2629 /** CHECKED, WORKS WELL */
2630 // To support this action, I have added a EpsPrimitive2D which will
2631 // by default decompose to the Metafile replacement data. To support
2632 // this EPS on screen, the renderer visualizing this has to support
2633 // that primitive and visualize the Eps file (e.g. printing)
2634 const MetaEPSAction
* pA
= static_cast<const MetaEPSAction
*>(pAction
);
2635 const tools::Rectangle
aRectangle(pA
->GetPoint(), pA
->GetSize());
2637 if(!aRectangle
.IsEmpty())
2639 // create object transform
2640 basegfx::B2DHomMatrix aObjectTransform
;
2642 aObjectTransform
.set(0, 0, aRectangle
.GetWidth());
2643 aObjectTransform
.set(1, 1, aRectangle
.GetHeight());
2644 aObjectTransform
.set(0, 2, aRectangle
.Left());
2645 aObjectTransform
.set(1, 2, aRectangle
.Top());
2647 // add current transformation
2648 aObjectTransform
= rPropertyHolders
.Current().getTransformation() * aObjectTransform
;
2650 // embed using EpsPrimitive
2651 rTargetHolders
.Current().append(
2652 new drawinglayer::primitive2d::EpsPrimitive2D(
2655 pA
->GetSubstitute()));
2660 case MetaActionType::REFPOINT
:
2663 // only used for hatch and line pattern offsets, pretty much no longer
2665 // const MetaRefPointAction* pA = (const MetaRefPointAction*)pAction;
2668 case MetaActionType::TEXTLINECOLOR
:
2671 const MetaTextLineColorAction
* pA
= static_cast<const MetaTextLineColorAction
*>(pAction
);
2672 const bool bActive(pA
->IsSetting());
2674 rPropertyHolders
.Current().setTextLineColorActive(bActive
);
2676 rPropertyHolders
.Current().setTextLineColor(pA
->GetColor().getBColor());
2680 case MetaActionType::TEXTLINE
:
2682 /** CHECKED, WORKS WELL */
2683 // actually creates overline, underline and strikeouts, so
2684 // these should be isolated from TextDecoratedPortionPrimitive2D
2685 // to own primitives. Done, available now.
2687 // This Metaaction seems not to be used (was not used in any
2688 // checked files). It's used in combination with the current
2690 const MetaTextLineAction
* pA
= static_cast<const MetaTextLineAction
*>(pAction
);
2692 processMetaTextLineAction(
2694 rTargetHolders
.Current(),
2695 rPropertyHolders
.Current());
2699 case MetaActionType::FLOATTRANSPARENT
:
2701 /** CHECKED, WORKS WELL */
2702 const MetaFloatTransparentAction
* pA
= static_cast<const MetaFloatTransparentAction
*>(pAction
);
2703 const basegfx::B2DRange
aTargetRange(
2706 pA
->GetPoint().X() + pA
->GetSize().Width(),
2707 pA
->GetPoint().Y() + pA
->GetSize().Height());
2709 if(!aTargetRange
.isEmpty())
2711 const GDIMetaFile
& rContent
= pA
->GetGDIMetaFile();
2713 if(rContent
.GetActionSize())
2715 // create the sub-content with no embedding specific to the
2716 // sub-metafile, this seems not to be used.
2717 drawinglayer::primitive2d::Primitive2DContainer xSubContent
;
2719 rTargetHolders
.Push();
2720 // #i# for sub-Mteafile contents, do start with new, default render state
2721 rPropertyHolders
.PushDefault();
2722 implInterpretMetafile(rContent
, rTargetHolders
, rPropertyHolders
, rViewInformation
);
2723 xSubContent
= rTargetHolders
.Current().getPrimitive2DSequence(rPropertyHolders
.Current());
2724 rPropertyHolders
.Pop();
2725 rTargetHolders
.Pop();
2728 if(!xSubContent
.empty())
2730 // prepare sub-content transform
2731 basegfx::B2DHomMatrix aSubTransform
;
2733 // create SourceRange
2734 const basegfx::B2DRange
aSourceRange(
2735 rContent
.GetPrefMapMode().GetOrigin().X(),
2736 rContent
.GetPrefMapMode().GetOrigin().Y(),
2737 rContent
.GetPrefMapMode().GetOrigin().X() + rContent
.GetPrefSize().Width(),
2738 rContent
.GetPrefMapMode().GetOrigin().Y() + rContent
.GetPrefSize().Height());
2740 // apply mapping if aTargetRange and aSourceRange are not equal
2741 if(!aSourceRange
.equal(aTargetRange
))
2743 aSubTransform
.translate(-aSourceRange
.getMinX(), -aSourceRange
.getMinY());
2744 aSubTransform
.scale(
2745 aTargetRange
.getWidth() / (basegfx::fTools::equalZero(aSourceRange
.getWidth()) ? 1.0 : aSourceRange
.getWidth()),
2746 aTargetRange
.getHeight() / (basegfx::fTools::equalZero(aSourceRange
.getHeight()) ? 1.0 : aSourceRange
.getHeight()));
2747 aSubTransform
.translate(aTargetRange
.getMinX(), aTargetRange
.getMinY());
2750 // apply general current transformation
2751 aSubTransform
= rPropertyHolders
.Current().getTransformation() * aSubTransform
;
2753 // evtl. embed sub-content to its transformation
2754 if(!aSubTransform
.isIdentity())
2756 const drawinglayer::primitive2d::Primitive2DReference
aEmbeddedTransform(
2757 new drawinglayer::primitive2d::TransformPrimitive2D(
2759 std::move(xSubContent
)));
2761 xSubContent
= drawinglayer::primitive2d::Primitive2DContainer
{ aEmbeddedTransform
};
2764 // check if gradient is a real gradient
2765 const Gradient
& rGradient
= pA
->GetGradient();
2766 drawinglayer::attribute::FillGradientAttribute
aAttribute(createFillGradientAttribute(rGradient
));
2767 basegfx::BColor aSingleColor
;
2769 if (aAttribute
.getColorStops().isSingleColor(aSingleColor
))
2771 // not really a gradient; create UnifiedTransparencePrimitive2D
2772 rTargetHolders
.Current().append(
2773 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
2774 std::move(xSubContent
),
2775 aSingleColor
.luminance()));
2779 // really a gradient. Create gradient sub-content (with correct scaling)
2780 basegfx::B2DRange
aRange(aTargetRange
);
2781 aRange
.transform(rPropertyHolders
.Current().getTransformation());
2783 // prepare gradient for transparent content
2784 const drawinglayer::primitive2d::Primitive2DReference
xTransparence(
2785 new drawinglayer::primitive2d::FillGradientPrimitive2D(
2787 std::move(aAttribute
)));
2789 // create transparence primitive
2790 rTargetHolders
.Current().append(
2791 new drawinglayer::primitive2d::TransparencePrimitive2D(
2792 std::move(xSubContent
),
2793 drawinglayer::primitive2d::Primitive2DContainer
{ xTransparence
}));
2801 case MetaActionType::GRADIENTEX
:
2804 // This is only a data holder which is interpreted inside comment actions,
2805 // see MetaActionType::COMMENT for more info
2806 // const MetaGradientExAction* pA = (const MetaGradientExAction*)pAction;
2809 case MetaActionType::LAYOUTMODE
:
2812 const MetaLayoutModeAction
* pA
= static_cast<const MetaLayoutModeAction
*>(pAction
);
2813 rPropertyHolders
.Current().setLayoutMode(pA
->GetLayoutMode());
2816 case MetaActionType::TEXTLANGUAGE
:
2819 const MetaTextLanguageAction
* pA
= static_cast<const MetaTextLanguageAction
*>(pAction
);
2820 rPropertyHolders
.Current().setLanguageType(pA
->GetTextLanguage());
2823 case MetaActionType::OVERLINECOLOR
:
2826 const MetaOverlineColorAction
* pA
= static_cast<const MetaOverlineColorAction
*>(pAction
);
2827 const bool bActive(pA
->IsSetting());
2829 rPropertyHolders
.Current().setOverlineColorActive(bActive
);
2831 rPropertyHolders
.Current().setOverlineColor(pA
->GetColor().getBColor());
2835 case MetaActionType::COMMENT
:
2837 /** CHECKED, WORKS WELL */
2838 // I already implemented
2839 // XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END
2840 // XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END,
2841 // but opted to remove these again; it works well without them
2842 // and makes the code less dependent from those Metafile Add-Ons
2843 const MetaCommentAction
* pA
= static_cast<const MetaCommentAction
*>(pAction
);
2845 if (pA
->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
2847 // XGRAD_SEQ_BEGIN, XGRAD_SEQ_END should be supported since the
2848 // pure recorded paint of the gradients uses the XOR paint functionality
2849 // ('trick'). This is (and will be) problematic with AntiAliasing, so it's
2850 // better to use this info
2851 const MetaGradientExAction
* pMetaGradientExAction
= nullptr;
2853 size_t b(nAction
+ 1);
2855 for(; !bDone
&& b
< nCount
; b
++)
2857 pAction
= rMetaFile
.GetAction(b
);
2859 if(MetaActionType::GRADIENTEX
== pAction
->GetType())
2861 pMetaGradientExAction
= static_cast<const MetaGradientExAction
*>(pAction
);
2863 else if(MetaActionType::COMMENT
== pAction
->GetType())
2865 if (static_cast<const MetaCommentAction
*>(pAction
)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END"))
2872 if(bDone
&& pMetaGradientExAction
)
2874 // consume actions and skip forward
2877 // get geometry data
2878 basegfx::B2DPolyPolygon
aPolyPolygon(pMetaGradientExAction
->GetPolyPolygon().getB2DPolyPolygon());
2880 if(aPolyPolygon
.count())
2882 // transform geometry
2883 aPolyPolygon
.transform(rPropertyHolders
.Current().getTransformation());
2885 // get and check if gradient is a real gradient
2886 const Gradient
& rGradient
= pMetaGradientExAction
->GetGradient();
2887 drawinglayer::attribute::FillGradientAttribute
aAttribute(createFillGradientAttribute(rGradient
));
2888 basegfx::BColor aSingleColor
;
2890 if (aAttribute
.getColorStops().isSingleColor(aSingleColor
))
2892 // not really a gradient
2893 rTargetHolders
.Current().append(
2894 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
2895 std::move(aPolyPolygon
),
2900 // really a gradient
2901 rTargetHolders
.Current().append(
2902 new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D(
2904 std::move(aAttribute
)));
2909 else if (pA
->GetComment().equalsIgnoreAsciiCase("EMF_PLUS_HEADER_INFO"))
2913 // error: should not yet exist
2914 SAL_INFO("drawinglayer.emf", "Error: multiple EMF_PLUS_HEADER_INFO");
2918 SAL_INFO("drawinglayer.emf", "EMF+ passed to canvas mtf renderer - header info, size: " << pA
->GetDataSize());
2919 SvMemoryStream
aMemoryStream(const_cast<sal_uInt8
*>(pA
->GetData()), pA
->GetDataSize(), StreamMode::READ
);
2922 new emfplushelper::EmfPlusHelper(
2928 else if (pA
->GetComment().equalsIgnoreAsciiCase("EMF_PLUS"))
2932 // error: should exist
2933 SAL_INFO("drawinglayer.emf", "Error: EMF_PLUS before EMF_PLUS_HEADER_INFO");
2937 static int count
= -1, limit
= 0x7fffffff;
2943 if (char *env
= getenv("EMF_PLUS_LIMIT"))
2946 SAL_INFO("drawinglayer.emf", "EMF+ records limit: " << limit
);
2950 SAL_INFO("drawinglayer.emf", "EMF+ passed to canvas mtf renderer, size: " << pA
->GetDataSize());
2954 SvMemoryStream
aMemoryStream(const_cast<sal_uInt8
*>(pA
->GetData()), pA
->GetDataSize(), StreamMode::READ
);
2956 aEMFPlus
->processEmfPlusData(
2969 OSL_FAIL("Unknown MetaFile Action (!)");
2977 namespace wmfemfhelper
2979 drawinglayer::primitive2d::Primitive2DContainer
interpretMetafile(
2980 const GDIMetaFile
& rMetaFile
,
2981 const drawinglayer::geometry::ViewInformation2D
& rViewInformation
)
2983 // prepare target and properties; each will have one default entry
2984 drawinglayer::primitive2d::Primitive2DContainer xRetval
;
2985 TargetHolders aTargetHolders
;
2986 PropertyHolders aPropertyHolders
;
2988 // set target MapUnit at Properties
2989 aPropertyHolders
.Current().setMapUnit(rMetaFile
.GetPrefMapMode().GetMapUnit());
2991 // interpret the Metafile
2992 implInterpretMetafile(rMetaFile
, aTargetHolders
, aPropertyHolders
, rViewInformation
);
2994 // get the content. There should be only one target, as in the start condition,
2995 // but iterating will be the right thing to do when some push/pop is not closed
2996 while (aTargetHolders
.size() > 1)
2999 aTargetHolders
.Current().getPrimitive2DSequence(aPropertyHolders
.Current()));
3000 aTargetHolders
.Pop();
3004 aTargetHolders
.Current().getPrimitive2DSequence(aPropertyHolders
.Current()));
3009 } // end of namespace wmfemfhelper
3011 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */