1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
21 #include <basegfx/tools/canvastools.hxx>
22 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
23 #include <basegfx/color/bcolor.hxx>
24 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
25 #include <vcl/lineinfo.hxx>
26 #include <drawinglayer/attribute/lineattribute.hxx>
27 #include <drawinglayer/attribute/strokeattribute.hxx>
28 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
29 #include <vcl/metaact.hxx>
30 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
31 #include <basegfx/matrix/b2dhommatrixtools.hxx>
32 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
33 #include <basegfx/polygon/b2dpolygontools.hxx>
34 #include <drawinglayer/primitive2d/discretebitmapprimitive2d.hxx>
35 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
36 #include <vcl/salbtype.hxx>
37 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
38 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
39 #include <vcl/svapp.hxx>
40 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
41 #include <drawinglayer/primitive2d/fillhatchprimitive2d.hxx>
42 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
43 #include <basegfx/polygon/b2dpolygonclipper.hxx>
44 #include <drawinglayer/primitive2d/invertprimitive2d.hxx>
45 #include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
46 #include <drawinglayer/primitive2d/fillbitmapprimitive2d.hxx>
47 #include <drawinglayer/primitive2d/wallpaperprimitive2d.hxx>
48 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
49 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
50 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
51 #include <i18nlangtag/languagetag.hxx>
52 #include <drawinglayer/primitive2d/textlineprimitive2d.hxx>
53 #include <drawinglayer/primitive2d/textstrikeoutprimitive2d.hxx>
54 #include <drawinglayer/primitive2d/epsprimitive2d.hxx>
57 //////////////////////////////////////////////////////////////////////////////
59 using namespace com::sun::star
;
61 //////////////////////////////////////////////////////////////////////////////
65 /** helper class for graphic context
67 This class allows to hold a complete status of classic
68 VCL OutputDevice stati. This data is needed for correct
69 interpretation of the MetaFile action flow.
74 /// current transformation (aka MapMode)
75 basegfx::B2DHomMatrix maTransformation
;
79 basegfx::BColor maLineColor
;
80 basegfx::BColor maFillColor
;
81 basegfx::BColor maTextColor
;
82 basegfx::BColor maTextFillColor
;
83 basegfx::BColor maTextLineColor
;
84 basegfx::BColor maOverlineColor
;
87 basegfx::B2DPolyPolygon maClipPolyPoygon
;
92 sal_uInt32 mnLayoutMode
;
93 LanguageType maLanguageType
;
94 sal_uInt16 mnPushFlags
;
97 /// contains all active markers
100 bool mbTextColor
: 1;
101 bool mbTextFillColor
: 1;
102 bool mbTextLineColor
: 1;
103 bool mbOverlineColor
: 1;
104 bool mbClipPolyPolygonActive
: 1;
108 : maTransformation(),
109 maMapUnit(MAP_100TH_MM
),
112 maTextColor(COL_BLACK
),
118 maRasterOp(ROP_OVERPAINT
),
125 mbTextFillColor(false),
126 mbTextLineColor(false),
127 mbOverlineColor(false),
128 mbClipPolyPolygonActive(false)
136 /// read/write accesses
137 const basegfx::B2DHomMatrix
& getTransformation() const { return maTransformation
; }
138 void setTransformation(const basegfx::B2DHomMatrix
& rNew
) { if(rNew
!= maTransformation
) maTransformation
= rNew
; }
140 MapUnit
getMapUnit() const { return maMapUnit
; }
141 void setMapUnit(MapUnit eNew
) { if(eNew
!= maMapUnit
) maMapUnit
= eNew
; }
143 const basegfx::BColor
& getLineColor() const { return maLineColor
; }
144 void setLineColor(const basegfx::BColor
& rNew
) { if(rNew
!= maLineColor
) maLineColor
= rNew
; }
145 bool getLineColorActive() const { return mbLineColor
; }
146 void setLineColorActive(bool bNew
) { if(bNew
!= mbLineColor
) mbLineColor
= bNew
; }
148 const basegfx::BColor
& getFillColor() const { return maFillColor
; }
149 void setFillColor(const basegfx::BColor
& rNew
) { if(rNew
!= maFillColor
) maFillColor
= rNew
; }
150 bool getFillColorActive() const { return mbFillColor
; }
151 void setFillColorActive(bool bNew
) { if(bNew
!= mbFillColor
) mbFillColor
= bNew
; }
153 const basegfx::BColor
& getTextColor() const { return maTextColor
; }
154 void setTextColor(const basegfx::BColor
& rNew
) { if(rNew
!= maTextColor
) maTextColor
= rNew
; }
155 bool getTextColorActive() const { return mbTextColor
; }
156 void setTextColorActive(bool bNew
) { if(bNew
!= mbTextColor
) mbTextColor
= bNew
; }
158 const basegfx::BColor
& getTextFillColor() const { return maTextFillColor
; }
159 void setTextFillColor(const basegfx::BColor
& rNew
) { if(rNew
!= maTextFillColor
) maTextFillColor
= rNew
; }
160 bool getTextFillColorActive() const { return mbTextFillColor
; }
161 void setTextFillColorActive(bool bNew
) { if(bNew
!= mbTextFillColor
) mbTextFillColor
= bNew
; }
163 const basegfx::BColor
& getTextLineColor() const { return maTextLineColor
; }
164 void setTextLineColor(const basegfx::BColor
& rNew
) { if(rNew
!= maTextLineColor
) maTextLineColor
= rNew
; }
165 bool getTextLineColorActive() const { return mbTextLineColor
; }
166 void setTextLineColorActive(bool bNew
) { if(bNew
!= mbTextLineColor
) mbTextLineColor
= bNew
; }
168 const basegfx::BColor
& getOverlineColor() const { return maOverlineColor
; }
169 void setOverlineColor(const basegfx::BColor
& rNew
) { if(rNew
!= maOverlineColor
) maOverlineColor
= rNew
; }
170 bool getOverlineColorActive() const { return mbOverlineColor
; }
171 void setOverlineColorActive(bool bNew
) { if(bNew
!= mbOverlineColor
) mbOverlineColor
= bNew
; }
173 const basegfx::B2DPolyPolygon
& getClipPolyPolygon() const { return maClipPolyPoygon
; }
174 void setClipPolyPolygon(const basegfx::B2DPolyPolygon
& rNew
) { if(rNew
!= maClipPolyPoygon
) maClipPolyPoygon
= rNew
; }
175 bool getClipPolyPolygonActive() const { return mbClipPolyPolygonActive
; }
176 void setClipPolyPolygonActive(bool bNew
) { if(bNew
!= mbClipPolyPolygonActive
) mbClipPolyPolygonActive
= bNew
; }
178 const Font
& getFont() const { return maFont
; }
179 void setFont(const Font
& rFont
) { if(rFont
!= maFont
) maFont
= rFont
; }
181 const RasterOp
& getRasterOp() const { return maRasterOp
; }
182 void setRasterOp(const RasterOp
& rRasterOp
) { if(rRasterOp
!= maRasterOp
) maRasterOp
= rRasterOp
; }
183 bool isRasterOpInvert() const { return (ROP_XOR
== maRasterOp
|| ROP_INVERT
== maRasterOp
); }
184 bool isRasterOpForceBlack() const { return ROP_0
== maRasterOp
; }
185 bool isRasterOpActive() const { return isRasterOpInvert() || isRasterOpForceBlack(); }
187 sal_uInt32
getLayoutMode() const { return mnLayoutMode
; }
188 void setLayoutMode(sal_uInt32 nNew
) { if(nNew
!= mnLayoutMode
) mnLayoutMode
= nNew
; }
190 LanguageType
getLanguageType() const { return maLanguageType
; }
191 void setLanguageType(LanguageType aNew
) { if(aNew
!= maLanguageType
) maLanguageType
= aNew
; }
193 sal_uInt16
getPushFlags() const { return mnPushFlags
; }
194 void setPushFlags(sal_uInt16 nNew
) { if(nNew
!= mnPushFlags
) mnPushFlags
= nNew
; }
196 bool getLineOrFillActive() const { return (mbLineColor
|| mbFillColor
); }
198 } // end of anonymous namespace
200 //////////////////////////////////////////////////////////////////////////////
204 /** stack for properites
206 This class builds a stack based on the PropertyHolder
207 class. It encapsulates the pointer/new/delete usage to
208 make it safe and implements the push/pop as needed by a
209 VCL Metafile interpreter. The critical part here are the
210 flag values VCL OutputDevice uses here; not all stuff is
211 pushed and thus needs to be copied at pop.
213 class PropertyHolders
216 std::vector
< PropertyHolder
* > maPropertyHolders
;
221 maPropertyHolders
.push_back(new PropertyHolder());
224 sal_uInt32
size() const
226 return maPropertyHolders
.size();
231 PropertyHolder
* pNew
= new PropertyHolder();
232 maPropertyHolders
.push_back(pNew
);
235 void Push(sal_uInt16 nPushFlags
)
239 OSL_ENSURE(maPropertyHolders
.size(), "PropertyHolders: PUSH with no property holders (!)");
240 if ( !maPropertyHolders
.empty() )
242 PropertyHolder
* pNew
= new PropertyHolder(*maPropertyHolders
.back());
243 pNew
->setPushFlags(nPushFlags
);
244 maPropertyHolders
.push_back(pNew
);
251 OSL_ENSURE(maPropertyHolders
.size(), "PropertyHolders: POP with no property holders (!)");
252 const sal_uInt32
nSize(maPropertyHolders
.size());
256 const PropertyHolder
* pTip
= maPropertyHolders
.back();
257 const sal_uInt16
nPushFlags(pTip
->getPushFlags());
263 // copy back content for all non-set flags
264 PropertyHolder
* pLast
= maPropertyHolders
[nSize
- 2];
266 if(PUSH_ALL
!= nPushFlags
)
268 if(!(nPushFlags
& PUSH_LINECOLOR
))
270 pLast
->setLineColor(pTip
->getLineColor());
271 pLast
->setLineColorActive(pTip
->getLineColorActive());
273 if(!(nPushFlags
& PUSH_FILLCOLOR
))
275 pLast
->setFillColor(pTip
->getFillColor());
276 pLast
->setFillColorActive(pTip
->getFillColorActive());
278 if(!(nPushFlags
& PUSH_FONT
))
280 pLast
->setFont(pTip
->getFont());
282 if(!(nPushFlags
& PUSH_TEXTCOLOR
))
284 pLast
->setTextColor(pTip
->getTextColor());
285 pLast
->setTextColorActive(pTip
->getTextColorActive());
287 if(!(nPushFlags
& PUSH_MAPMODE
))
289 pLast
->setTransformation(pTip
->getTransformation());
290 pLast
->setMapUnit(pTip
->getMapUnit());
292 if(!(nPushFlags
& PUSH_CLIPREGION
))
294 pLast
->setClipPolyPolygon(pTip
->getClipPolyPolygon());
295 pLast
->setClipPolyPolygonActive(pTip
->getClipPolyPolygonActive());
297 if(!(nPushFlags
& PUSH_RASTEROP
))
299 pLast
->setRasterOp(pTip
->getRasterOp());
301 if(!(nPushFlags
& PUSH_TEXTFILLCOLOR
))
303 pLast
->setTextFillColor(pTip
->getTextFillColor());
304 pLast
->setTextFillColorActive(pTip
->getTextFillColorActive());
306 if(!(nPushFlags
& PUSH_TEXTALIGN
))
308 if(pLast
->getFont().GetAlign() != pTip
->getFont().GetAlign())
310 Font
aFont(pLast
->getFont());
311 aFont
.SetAlign(pTip
->getFont().GetAlign());
312 pLast
->setFont(aFont
);
315 if(!(nPushFlags
& PUSH_REFPOINT
))
319 if(!(nPushFlags
& PUSH_TEXTLINECOLOR
))
321 pLast
->setTextLineColor(pTip
->getTextLineColor());
322 pLast
->setTextLineColorActive(pTip
->getTextLineColorActive());
324 if(!(nPushFlags
& PUSH_TEXTLAYOUTMODE
))
326 pLast
->setLayoutMode(pTip
->getLayoutMode());
328 if(!(nPushFlags
& PUSH_TEXTLANGUAGE
))
330 pLast
->setLanguageType(pTip
->getLanguageType());
332 if(!(nPushFlags
& PUSH_OVERLINECOLOR
))
334 pLast
->setOverlineColor(pTip
->getOverlineColor());
335 pLast
->setOverlineColorActive(pTip
->getOverlineColorActive());
342 delete maPropertyHolders
.back();
343 maPropertyHolders
.pop_back();
347 PropertyHolder
& Current()
349 static PropertyHolder aDummy
;
350 OSL_ENSURE(maPropertyHolders
.size(), "PropertyHolders: CURRENT with no property holders (!)");
351 return maPropertyHolders
.empty() ? aDummy
: *maPropertyHolders
.back();
356 while(!maPropertyHolders
.empty())
358 delete maPropertyHolders
.back();
359 maPropertyHolders
.pop_back();
363 } // end of anonymous namespace
365 //////////////////////////////////////////////////////////////////////////////
369 /** helper to convert a Region to a B2DPolyPolygon
370 when it does not yet contain one. In the future
371 this may be expanded to merge the polygons created
372 from rectangles or use a special algo to directly turn
373 the spans of regions to a single, already merged
376 basegfx::B2DPolyPolygon
getB2DPolyPolygonFromRegion(const Region
& rRegion
)
378 basegfx::B2DPolyPolygon aRetval
;
380 if(!rRegion
.IsEmpty())
382 Region
aRegion(rRegion
);
383 aRetval
= aRegion
.GetB2DPolyPolygon();
387 RegionHandle
aRegionHandle(aRegion
.BeginEnumRects());
388 Rectangle aRegionRectangle
;
390 while(aRegion
.GetEnumRects(aRegionHandle
, aRegionRectangle
))
392 if(!aRegionRectangle
.IsEmpty())
394 const basegfx::B2DRange
aRegionRange(
395 aRegionRectangle
.Left(), aRegionRectangle
.Top(),
396 aRegionRectangle
.Right(), aRegionRectangle
.Bottom());
397 aRetval
.append(basegfx::tools::createPolygonFromRect(aRegionRange
));
401 aRegion
.EndEnumRects(aRegionHandle
);
407 } // end of anonymous namespace
409 //////////////////////////////////////////////////////////////////////////////
413 /** Helper class to buffer and hold a Primive target vector. It
414 encapsulates the new/delete functionality and aloows to work
415 on pointers of the implementation classes. All data will
416 be converted to uno sequences of uno references when accessing the
422 std::vector
< drawinglayer::primitive2d::BasePrimitive2D
* > aTargets
;
432 const sal_uInt32
nCount(aTargets
.size());
434 for(sal_uInt32
a(0); a
< nCount
; a
++)
440 sal_uInt32
size() const
442 return aTargets
.size();
445 void append(drawinglayer::primitive2d::BasePrimitive2D
* pCandidate
)
449 aTargets
.push_back(pCandidate
);
453 drawinglayer::primitive2d::Primitive2DSequence
getPrimitive2DSequence(const PropertyHolder
& rPropertyHolder
)
455 const sal_uInt32
nCount(aTargets
.size());
456 drawinglayer::primitive2d::Primitive2DSequence
xRetval(nCount
);
458 for(sal_uInt32
a(0); a
< nCount
; a
++)
460 xRetval
[a
] = aTargets
[a
];
463 // All Targets were pointers, but do not need to be deleted since they
464 // were converted to UNO API references now, so they stay as long as
465 // referenced. Do NOT delete the C++ implementation classes here, but clear
466 // the buffer to not delete them in the destructor.
469 if(xRetval
.hasElements() && rPropertyHolder
.getClipPolyPolygonActive())
471 const basegfx::B2DPolyPolygon
& rClipPolyPolygon
= rPropertyHolder
.getClipPolyPolygon();
473 if(rClipPolyPolygon
.count())
475 const drawinglayer::primitive2d::Primitive2DReference
xMask(
476 new drawinglayer::primitive2d::MaskPrimitive2D(
480 xRetval
= drawinglayer::primitive2d::Primitive2DSequence(&xMask
, 1);
487 } // end of anonymous namespace
489 //////////////////////////////////////////////////////////////////////////////
493 /** Helper class which builds a stack on the TargetHolder class */
497 std::vector
< TargetHolder
* > maTargetHolders
;
502 maTargetHolders
.push_back(new TargetHolder());
505 sal_uInt32
size() const
507 return maTargetHolders
.size();
512 maTargetHolders
.push_back(new TargetHolder());
517 OSL_ENSURE(maTargetHolders
.size(), "TargetHolders: POP with no property holders (!)");
518 if(!maTargetHolders
.empty())
520 delete maTargetHolders
.back();
521 maTargetHolders
.pop_back();
525 TargetHolder
& Current()
527 static TargetHolder aDummy
;
528 OSL_ENSURE(maTargetHolders
.size(), "TargetHolders: CURRENT with no property holders (!)");
529 return maTargetHolders
.empty() ? aDummy
: *maTargetHolders
.back();
534 while(!maTargetHolders
.empty())
536 delete maTargetHolders
.back();
537 maTargetHolders
.pop_back();
541 } // end of anonymous namespace
543 //////////////////////////////////////////////////////////////////////////////
545 namespace drawinglayer
547 namespace primitive2d
549 /** NonOverlappingFillGradientPrimitive2D class
551 This is a special version of the FillGradientPrimitive2D which decomposes
552 to a non-overlapping geometry version of the gradient. This needs to be
553 used to support the old XOR paint-'trick'.
555 It does not need an own identifier since a renderer who wants to interpret
556 it itself may do so. It just overloads the decomposition of the C++
557 implementation class to do an alternative decomposition.
559 class NonOverlappingFillGradientPrimitive2D
: public FillGradientPrimitive2D
562 /// local decomposition.
563 virtual Primitive2DSequence
create2DDecomposition(
564 const geometry::ViewInformation2D
& rViewInformation
) const;
568 NonOverlappingFillGradientPrimitive2D(
569 const basegfx::B2DRange
& rObjectRange
,
570 const attribute::FillGradientAttribute
& rFillGradient
)
571 : FillGradientPrimitive2D(rObjectRange
, rFillGradient
)
576 Primitive2DSequence
NonOverlappingFillGradientPrimitive2D::create2DDecomposition(
577 const geometry::ViewInformation2D
& /*rViewInformation*/) const
579 if(!getFillGradient().isDefault())
581 return createFill(false);
585 return Primitive2DSequence();
588 } // end of namespace primitive2d
589 } // end of namespace drawinglayer
591 //////////////////////////////////////////////////////////////////////////////
595 /** helper to convert a MapMode to a transformation */
596 basegfx::B2DHomMatrix
getTransformFromMapMode(const MapMode
& rMapMode
)
598 basegfx::B2DHomMatrix aMapping
;
599 const Fraction
aNoScale(1, 1);
600 const Point
& rOrigin(rMapMode
.GetOrigin());
602 if(0 != rOrigin
.X() || 0 != rOrigin
.Y())
604 aMapping
.translate(rOrigin
.X(), rOrigin
.Y());
607 if(rMapMode
.GetScaleX() != aNoScale
|| rMapMode
.GetScaleY() != aNoScale
)
610 double(rMapMode
.GetScaleX()),
611 double(rMapMode
.GetScaleY()));
617 /** helper to create a PointArrayPrimitive2D based on current context */
618 void createPointArrayPrimitive(
619 const std::vector
< basegfx::B2DPoint
>& rPositions
,
620 TargetHolder
& rTarget
,
621 PropertyHolder
& rProperties
,
622 basegfx::BColor aBColor
)
624 if(!rPositions
.empty())
626 if(rProperties
.getTransformation().isIdentity())
629 new drawinglayer::primitive2d::PointArrayPrimitive2D(
635 std::vector
< basegfx::B2DPoint
> aPositions(rPositions
);
637 for(sal_uInt32
a(0); a
< aPositions
.size(); a
++)
639 aPositions
[a
] = rProperties
.getTransformation() * aPositions
[a
];
643 new drawinglayer::primitive2d::PointArrayPrimitive2D(
650 /** helper to create a PolygonHairlinePrimitive2D based on current context */
651 void createHairlinePrimitive(
652 const basegfx::B2DPolygon
& rLinePolygon
,
653 TargetHolder
& rTarget
,
654 PropertyHolder
& rProperties
)
656 if(rLinePolygon
.count())
658 basegfx::B2DPolygon
aLinePolygon(rLinePolygon
);
659 aLinePolygon
.transform(rProperties
.getTransformation());
661 new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
663 rProperties
.getLineColor()));
667 /** helper to create a PolyPolygonColorPrimitive2D based on current context */
668 void createFillPrimitive(
669 const basegfx::B2DPolyPolygon
& rFillPolyPolygon
,
670 TargetHolder
& rTarget
,
671 PropertyHolder
& rProperties
)
673 if(rFillPolyPolygon
.count())
675 basegfx::B2DPolyPolygon
aFillPolyPolygon(rFillPolyPolygon
);
676 aFillPolyPolygon
.transform(rProperties
.getTransformation());
678 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
680 rProperties
.getFillColor()));
684 /** helper to create a PolygonStrokePrimitive2D based on current context */
685 void createLinePrimitive(
686 const basegfx::B2DPolygon
& rLinePolygon
,
687 const LineInfo
& rLineInfo
,
688 TargetHolder
& rTarget
,
689 PropertyHolder
& rProperties
)
691 if(rLinePolygon
.count())
693 const bool bDashDotUsed(LINE_DASH
== rLineInfo
.GetStyle());
694 const bool bWidthUsed(rLineInfo
.GetWidth() > 1);
696 if(bDashDotUsed
|| bWidthUsed
)
698 basegfx::B2DPolygon
aLinePolygon(rLinePolygon
);
699 aLinePolygon
.transform(rProperties
.getTransformation());
700 const drawinglayer::attribute::LineAttribute
aLineAttribute(
701 rProperties
.getLineColor(),
702 bWidthUsed
? rLineInfo
.GetWidth() : 0.0,
703 rLineInfo
.GetLineJoin(),
704 rLineInfo
.GetLineCap());
708 ::std::vector
< double > fDotDashArray
;
709 const double fDashLen(rLineInfo
.GetDashLen());
710 const double fDotLen(rLineInfo
.GetDotLen());
711 const double fDistance(rLineInfo
.GetDistance());
713 for(sal_uInt16
a(0); a
< rLineInfo
.GetDashCount(); a
++)
715 fDotDashArray
.push_back(fDashLen
);
716 fDotDashArray
.push_back(fDistance
);
719 for(sal_uInt16
b(0); b
< rLineInfo
.GetDotCount(); b
++)
721 fDotDashArray
.push_back(fDotLen
);
722 fDotDashArray
.push_back(fDistance
);
725 const double fAccumulated(::std::accumulate(fDotDashArray
.begin(), fDotDashArray
.end(), 0.0));
726 const drawinglayer::attribute::StrokeAttribute
aStrokeAttribute(
731 new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
739 new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
746 createHairlinePrimitive(rLinePolygon
, rTarget
, rProperties
);
751 /** helper to create needed line and fill primitives based on current context */
752 void createHairlineAndFillPrimitive(
753 const basegfx::B2DPolygon
& rPolygon
,
754 TargetHolder
& rTarget
,
755 PropertyHolder
& rProperties
)
757 if(rProperties
.getFillColorActive())
759 createFillPrimitive(basegfx::B2DPolyPolygon(rPolygon
), rTarget
, rProperties
);
762 if(rProperties
.getLineColorActive())
764 createHairlinePrimitive(rPolygon
, rTarget
, rProperties
);
768 /** helper to create needed line and fill primitives based on current context */
769 void createHairlineAndFillPrimitive(
770 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
771 TargetHolder
& rTarget
,
772 PropertyHolder
& rProperties
)
774 if(rProperties
.getFillColorActive())
776 createFillPrimitive(rPolyPolygon
, rTarget
, rProperties
);
779 if(rProperties
.getLineColorActive())
781 for(sal_uInt32
a(0); a
< rPolyPolygon
.count(); a
++)
783 createHairlinePrimitive(rPolyPolygon
.getB2DPolygon(a
), rTarget
, rProperties
);
788 /** helper to create DiscreteBitmapPrimitive2D based on current context.
789 The DiscreteBitmapPrimitive2D is especially created for this usage
790 since no other usage defines a bitmap visualisation based on top-left
791 position and size in pixels. At the end it will create a view-dependent
792 transformed embedding of a BitmapPrimitive2D.
794 void createBitmapExPrimitive(
795 const BitmapEx
& rBitmapEx
,
797 TargetHolder
& rTarget
,
798 PropertyHolder
& rProperties
)
800 if(!rBitmapEx
.IsEmpty())
802 basegfx::B2DPoint
aPoint(rPoint
.X(), rPoint
.Y());
803 aPoint
= rProperties
.getTransformation() * aPoint
;
806 new drawinglayer::primitive2d::DiscreteBitmapPrimitive2D(
812 /** helper to create BitmapPrimitive2D based on current context */
813 void createBitmapExPrimitive(
814 const BitmapEx
& rBitmapEx
,
817 TargetHolder
& rTarget
,
818 PropertyHolder
& rProperties
)
820 if(!rBitmapEx
.IsEmpty())
822 basegfx::B2DHomMatrix aObjectTransform
;
824 aObjectTransform
.set(0, 0, rSize
.Width());
825 aObjectTransform
.set(1, 1, rSize
.Height());
826 aObjectTransform
.set(0, 2, rPoint
.X());
827 aObjectTransform
.set(1, 2, rPoint
.Y());
829 aObjectTransform
= rProperties
.getTransformation() * aObjectTransform
;
832 new drawinglayer::primitive2d::BitmapPrimitive2D(
838 /** helper to create a regular BotmapEx from a MaskAction (definitions
839 which use a bitmap without transparence but define one of the colors as
842 BitmapEx
createMaskBmpEx(const Bitmap
& rBitmap
, const Color
& rMaskColor
)
844 const Color
aWhite(COL_WHITE
);
845 BitmapPalette
aBiLevelPalette(2);
847 aBiLevelPalette
[0] = aWhite
;
848 aBiLevelPalette
[1] = rMaskColor
;
850 Bitmap
aMask(rBitmap
.CreateMask(aWhite
));
851 Bitmap
aSolid(rBitmap
.GetSizePixel(), 1, &aBiLevelPalette
);
853 aSolid
.Erase(rMaskColor
);
855 return BitmapEx(aSolid
, aMask
);
858 /** helper to convert from a VCL Gradient definition to the corresponding
859 data for primitive representation
861 drawinglayer::attribute::FillGradientAttribute
createFillGradientAttribute(const Gradient
& rGradient
)
863 const Color
aStartColor(rGradient
.GetStartColor());
864 const sal_uInt16
nStartIntens(rGradient
.GetStartIntensity());
865 basegfx::BColor
aStart(aStartColor
.getBColor());
867 if(nStartIntens
!= 100)
869 const basegfx::BColor aBlack
;
870 aStart
= interpolate(aBlack
, aStart
, (double)nStartIntens
* 0.01);
873 const Color
aEndColor(rGradient
.GetEndColor());
874 const sal_uInt16
nEndIntens(rGradient
.GetEndIntensity());
875 basegfx::BColor
aEnd(aEndColor
.getBColor());
877 if(nEndIntens
!= 100)
879 const basegfx::BColor aBlack
;
880 aEnd
= interpolate(aBlack
, aEnd
, (double)nEndIntens
* 0.01);
883 drawinglayer::attribute::GradientStyle
aGradientStyle(drawinglayer::attribute::GRADIENTSTYLE_RECT
);
885 switch(rGradient
.GetStyle())
887 case GradientStyle_LINEAR
:
889 aGradientStyle
= drawinglayer::attribute::GRADIENTSTYLE_LINEAR
;
892 case GradientStyle_AXIAL
:
894 aGradientStyle
= drawinglayer::attribute::GRADIENTSTYLE_AXIAL
;
897 case GradientStyle_RADIAL
:
899 aGradientStyle
= drawinglayer::attribute::GRADIENTSTYLE_RADIAL
;
902 case GradientStyle_ELLIPTICAL
:
904 aGradientStyle
= drawinglayer::attribute::GRADIENTSTYLE_ELLIPTICAL
;
907 case GradientStyle_SQUARE
:
909 aGradientStyle
= drawinglayer::attribute::GRADIENTSTYLE_SQUARE
;
912 default : // GradientStyle_RECT
914 aGradientStyle
= drawinglayer::attribute::GRADIENTSTYLE_RECT
;
919 return drawinglayer::attribute::FillGradientAttribute(
921 (double)rGradient
.GetBorder() * 0.01,
922 (double)rGradient
.GetOfsX() * 0.01,
923 (double)rGradient
.GetOfsY() * 0.01,
924 (double)rGradient
.GetAngle() * F_PI1800
,
927 rGradient
.GetSteps());
930 /** helper to convert from a VCL Hatch definition to the corresponding
931 data for primitive representation
933 drawinglayer::attribute::FillHatchAttribute
createFillHatchAttribute(const Hatch
& rHatch
)
935 drawinglayer::attribute::HatchStyle
aHatchStyle(drawinglayer::attribute::HATCHSTYLE_SINGLE
);
937 switch(rHatch
.GetStyle())
939 default : // case HATCH_SINGLE :
941 aHatchStyle
= drawinglayer::attribute::HATCHSTYLE_SINGLE
;
946 aHatchStyle
= drawinglayer::attribute::HATCHSTYLE_DOUBLE
;
951 aHatchStyle
= drawinglayer::attribute::HATCHSTYLE_TRIPLE
;
956 return drawinglayer::attribute::FillHatchAttribute(
958 (double)rHatch
.GetDistance(),
959 (double)rHatch
.GetAngle() * F_PI1800
,
960 rHatch
.GetColor().getBColor(),
961 3, // same default as VCL, a minimum of three discrete units (pixels) offset
965 /** helper to take needed action on ClipRegion change. This method needs to be called
966 on any Region change, e.g. at the obvious actions doing this, but also at pop-calls
967 which change the Region of the current context. It takes care of creating the
968 current embeddec context, set the new Region at the context and eventually prepare
969 a new target for embracing new geometry to the current region
971 void HandleNewClipRegion(
972 const basegfx::B2DPolyPolygon
& rClipPolyPolygon
,
973 TargetHolders
& rTargetHolders
,
974 PropertyHolders
& rPropertyHolders
)
976 const bool bNewActive(rClipPolyPolygon
.count());
978 // #i108636# The handlig of new ClipPolyPolygons was not done as good as possible
979 // in the first version of this interpreter; e.g. when a ClipPolyPolygon was set
980 // initially and then using a lot of push/pop actions, the pop always leads
981 // to setting a 'new' ClipPolyPolygon which indeed is the return to the ClipPolyPolygon
982 // of the properties next on the stack.
984 // This ClipPolyPolygon is identical to the current one, so there is no need to
985 // create a MaskPrimitive2D containing the up-to-now created primitives, but
986 // this was done before. While this does not lead to wrong primitive
987 // representations of the metafile data, it creates unneccesarily expensive
988 // representations. Just detecting when no really 'new' ClipPolyPolygon gets set
989 // solves the problem.
991 if(!rPropertyHolders
.Current().getClipPolyPolygonActive() && !bNewActive
)
993 // no active ClipPolyPolygon exchanged by no new one, done
997 if(rPropertyHolders
.Current().getClipPolyPolygonActive() && bNewActive
)
999 // active ClipPolyPolygon and new active ClipPolyPolygon
1000 if(rPropertyHolders
.Current().getClipPolyPolygon() == rClipPolyPolygon
)
1002 // new is the same as old, done
1007 // Here the old and the new are definitively different, maybe
1008 // old one and/or new one is not active.
1010 // Handle deletion of old ClipPolyPolygon. The process evtl. created primitives which
1011 // belong to this active ClipPolyPolygon. These need to be embedded to a
1012 // MaskPrimitive2D accordingly.
1013 if(rPropertyHolders
.Current().getClipPolyPolygonActive() && rTargetHolders
.size() > 1)
1015 drawinglayer::primitive2d::Primitive2DSequence aSubContent
;
1017 if(rPropertyHolders
.Current().getClipPolyPolygon().count()
1018 && rTargetHolders
.Current().size())
1020 aSubContent
= rTargetHolders
.Current().getPrimitive2DSequence(
1021 rPropertyHolders
.Current());
1024 rTargetHolders
.Pop();
1026 if(aSubContent
.hasElements())
1028 rTargetHolders
.Current().append(
1029 new drawinglayer::primitive2d::GroupPrimitive2D(
1034 // apply new settings to current properties by setting
1035 // the new region now
1036 rPropertyHolders
.Current().setClipPolyPolygonActive(bNewActive
);
1040 rPropertyHolders
.Current().setClipPolyPolygon(rClipPolyPolygon
);
1042 // prepare new content holder for new active region
1043 rTargetHolders
.Push();
1047 /** helper to handle the change of RasterOp. It takes care of encapsulating all current
1048 geometry to the current RasterOp (if changed) and needs to be called on any RasterOp
1049 change. It will also start a new geometry target to embrace to the new RasterOp if
1050 a changuing RasterOp is used. Currently, ROP_XOR and ROP_INVERT are supported using
1051 InvertPrimitive2D, and ROP_0 by using a ModifiedColorPrimitive2D to force to black paint
1053 void HandleNewRasterOp(
1055 TargetHolders
& rTargetHolders
,
1056 PropertyHolders
& rPropertyHolders
)
1058 // check if currently active
1059 if(rPropertyHolders
.Current().isRasterOpActive() && rTargetHolders
.size() > 1)
1061 drawinglayer::primitive2d::Primitive2DSequence aSubContent
;
1063 if(rTargetHolders
.Current().size())
1065 aSubContent
= rTargetHolders
.Current().getPrimitive2DSequence(rPropertyHolders
.Current());
1068 rTargetHolders
.Pop();
1070 if(aSubContent
.hasElements())
1072 if(rPropertyHolders
.Current().isRasterOpForceBlack())
1074 // force content to black
1075 rTargetHolders
.Current().append(
1076 new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
1078 basegfx::BColorModifier(basegfx::BColor(0.0, 0.0, 0.0))));
1080 else // if(rPropertyHolders.Current().isRasterOpInvert())
1083 rTargetHolders
.Current().append(
1084 new drawinglayer::primitive2d::InvertPrimitive2D(
1090 // apply new settings
1091 rPropertyHolders
.Current().setRasterOp(aRasterOp
);
1093 // check if now active
1094 if(rPropertyHolders
.Current().isRasterOpActive())
1096 // prepare new content holder for new invert
1097 rTargetHolders
.Push();
1101 /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
1102 It is a quite mighty action. This helper is for simple color filled background.
1104 drawinglayer::primitive2d::BasePrimitive2D
* CreateColorWallpaper(
1105 const basegfx::B2DRange
& rRange
,
1106 const basegfx::BColor
& rColor
,
1107 PropertyHolder
& rPropertyHolder
)
1109 basegfx::B2DPolygon
aOutline(basegfx::tools::createPolygonFromRect(rRange
));
1110 aOutline
.transform(rPropertyHolder
.getTransformation());
1112 return new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
1113 basegfx::B2DPolyPolygon(aOutline
),
1117 /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
1118 It is a quite mighty action. This helper is for gradient filled background.
1120 drawinglayer::primitive2d::BasePrimitive2D
* CreateGradientWallpaper(
1121 const basegfx::B2DRange
& rRange
,
1122 const Gradient
& rGradient
,
1123 PropertyHolder
& rPropertyHolder
)
1125 const drawinglayer::attribute::FillGradientAttribute
aAttribute(createFillGradientAttribute(rGradient
));
1127 if(aAttribute
.getStartColor() == aAttribute
.getEndColor())
1129 // not really a gradient. Create filled rectangle
1130 return CreateColorWallpaper(rRange
, aAttribute
.getStartColor(), rPropertyHolder
);
1134 // really a gradient
1135 drawinglayer::primitive2d::BasePrimitive2D
* pRetval
=
1136 new drawinglayer::primitive2d::FillGradientPrimitive2D(
1140 if(!rPropertyHolder
.getTransformation().isIdentity())
1142 const drawinglayer::primitive2d::Primitive2DReference
xPrim(pRetval
);
1143 const drawinglayer::primitive2d::Primitive2DSequence
xSeq(&xPrim
, 1);
1145 pRetval
= new drawinglayer::primitive2d::TransformPrimitive2D(
1146 rPropertyHolder
.getTransformation(),
1154 /** helper to create needed data to emulate the VCL Wallpaper Metafile action.
1155 It is a quite mighty action. This helper decides if color and/or gradient
1156 background is needed for the wanted bitmap fill and then creates the needed
1157 WallpaperBitmapPrimitive2D. This primitive was created for this purpose and
1158 takes over all needed logic of orientations and tiling.
1160 void CreateAndAppendBitmapWallpaper(
1161 basegfx::B2DRange aWallpaperRange
,
1162 const Wallpaper
& rWallpaper
,
1163 TargetHolder
& rTarget
,
1164 PropertyHolder
& rProperty
)
1166 const BitmapEx
aBitmapEx(rWallpaper
.GetBitmap());
1167 const WallpaperStyle
eWallpaperStyle(rWallpaper
.GetStyle());
1169 // if bitmap visualisation is transparent, maybe background
1170 // needs to be filled. Create background
1171 if(aBitmapEx
.IsTransparent()
1172 || (WALLPAPER_TILE
!= eWallpaperStyle
&& WALLPAPER_SCALE
!= eWallpaperStyle
))
1174 if(rWallpaper
.IsGradient())
1177 CreateGradientWallpaper(
1179 rWallpaper
.GetGradient(),
1182 else if(!rWallpaper
.GetColor().GetTransparency())
1185 CreateColorWallpaper(
1187 rWallpaper
.GetColor().getBColor(),
1192 // use wallpaper rect if set
1193 if(rWallpaper
.IsRect() && !rWallpaper
.GetRect().IsEmpty())
1195 aWallpaperRange
= basegfx::B2DRange(
1196 rWallpaper
.GetRect().Left(), rWallpaper
.GetRect().Top(),
1197 rWallpaper
.GetRect().Right(), rWallpaper
.GetRect().Bottom());
1200 drawinglayer::primitive2d::BasePrimitive2D
* pBitmapWallpaperFill
=
1201 new drawinglayer::primitive2d::WallpaperBitmapPrimitive2D(
1206 if(rProperty
.getTransformation().isIdentity())
1209 rTarget
.append(pBitmapWallpaperFill
);
1213 // when a transformation is set, embed to it
1214 const drawinglayer::primitive2d::Primitive2DReference
xPrim(pBitmapWallpaperFill
);
1217 new drawinglayer::primitive2d::TransformPrimitive2D(
1218 rProperty
.getTransformation(),
1219 drawinglayer::primitive2d::Primitive2DSequence(&xPrim
, 1)));
1223 /** helper to decide UnderlineAbove for text primitives */
1224 bool isUnderlineAbove(const Font
& rFont
)
1226 if(!rFont
.IsVertical())
1231 if((LANGUAGE_JAPANESE
== rFont
.GetLanguage()) || (LANGUAGE_JAPANESE
== rFont
.GetCJKContextLanguage()))
1233 // the underline is right for Japanese only
1240 void createFontAttributeTransformAndAlignment(
1241 drawinglayer::attribute::FontAttribute
& rFontAttribute
,
1242 basegfx::B2DHomMatrix
& rTextTransform
,
1243 basegfx::B2DVector
& rAlignmentOffset
,
1244 PropertyHolder
& rProperty
)
1246 const Font
& rFont
= rProperty
.getFont();
1247 basegfx::B2DVector aFontScaling
;
1249 rFontAttribute
= drawinglayer::attribute::FontAttribute(
1250 drawinglayer::primitive2d::getFontAttributeFromVclFont(
1253 0 != (rProperty
.getLayoutMode() & TEXT_LAYOUT_BIDI_RTL
),
1254 0 != (rProperty
.getLayoutMode() & TEXT_LAYOUT_BIDI_STRONG
)));
1257 rTextTransform
.scale(aFontScaling
.getX(), aFontScaling
.getY());
1259 // take text align into account
1260 if(ALIGN_BASELINE
!= rFont
.GetAlign())
1262 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice
;
1263 aTextLayouterDevice
.setFont(rFont
);
1265 if(ALIGN_TOP
== rFont
.GetAlign())
1267 rAlignmentOffset
.setY(aTextLayouterDevice
.getFontAscent());
1269 else // ALIGN_BOTTOM
1271 rAlignmentOffset
.setY(-aTextLayouterDevice
.getFontDescent());
1274 rTextTransform
.translate(rAlignmentOffset
.getX(), rAlignmentOffset
.getY());
1277 // add FontRotation (if used)
1278 if(rFont
.GetOrientation())
1280 rTextTransform
.rotate(-rFont
.GetOrientation() * F_PI1800
);
1284 /** helper which takes complete care for creating the needed text primitives. It
1285 takes care of decorated stuff and all the geometry adaptions needed
1287 void processMetaTextAction(
1288 const Point
& rTextStartPosition
,
1289 const OUString
& rText
,
1290 sal_uInt16 nTextStart
,
1291 sal_uInt16 nTextLength
,
1292 const ::std::vector
< double >& rDXArray
,
1293 TargetHolder
& rTarget
,
1294 PropertyHolder
& rProperty
)
1296 drawinglayer::primitive2d::BasePrimitive2D
* pResult
= 0;
1297 const Font
& rFont
= rProperty
.getFont();
1298 basegfx::B2DVector
aAlignmentOffset(0.0, 0.0);
1302 drawinglayer::attribute::FontAttribute aFontAttribute
;
1303 basegfx::B2DHomMatrix aTextTransform
;
1305 // fill parameters derived from current font
1306 createFontAttributeTransformAndAlignment(
1312 // add TextStartPosition
1313 aTextTransform
.translate(rTextStartPosition
.X(), rTextStartPosition
.Y());
1315 // prepare FontColor and Locale
1316 const basegfx::BColor
aFontColor(rProperty
.getTextColor());
1317 const com::sun::star::lang::Locale
aLocale(LanguageTag(rProperty
.getLanguageType()).getLocale());
1318 const bool bWordLineMode(rFont
.IsWordLineMode());
1320 const bool bDecoratedIsNeeded(
1321 UNDERLINE_NONE
!= rFont
.GetOverline()
1322 || UNDERLINE_NONE
!= rFont
.GetUnderline()
1323 || STRIKEOUT_NONE
!= rFont
.GetStrikeout()
1324 || EMPHASISMARK_NONE
!= (rFont
.GetEmphasisMark() & EMPHASISMARK_STYLE
)
1325 || RELIEF_NONE
!= rFont
.GetRelief()
1329 if(bDecoratedIsNeeded
)
1331 // prepare overline, underline and srikeout data
1332 const drawinglayer::primitive2d::TextLine
eFontOverline(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rFont
.GetOverline()));
1333 const drawinglayer::primitive2d::TextLine
eFontUnderline(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rFont
.GetUnderline()));
1334 const drawinglayer::primitive2d::TextStrikeout
eTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rFont
.GetStrikeout()));
1336 // check UndelineAbove
1337 const bool bUnderlineAbove(drawinglayer::primitive2d::TEXT_LINE_NONE
!= eFontUnderline
&& isUnderlineAbove(rFont
));
1339 // prepare emphasis mark data
1340 drawinglayer::primitive2d::TextEmphasisMark
eTextEmphasisMark(drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE
);
1342 switch(rFont
.GetEmphasisMark() & EMPHASISMARK_STYLE
)
1344 case EMPHASISMARK_DOT
: eTextEmphasisMark
= drawinglayer::primitive2d::TEXT_EMPHASISMARK_DOT
; break;
1345 case EMPHASISMARK_CIRCLE
: eTextEmphasisMark
= drawinglayer::primitive2d::TEXT_EMPHASISMARK_CIRCLE
; break;
1346 case EMPHASISMARK_DISC
: eTextEmphasisMark
= drawinglayer::primitive2d::TEXT_EMPHASISMARK_DISC
; break;
1347 case EMPHASISMARK_ACCENT
: eTextEmphasisMark
= drawinglayer::primitive2d::TEXT_EMPHASISMARK_ACCENT
; break;
1350 const bool bEmphasisMarkAbove(rFont
.GetEmphasisMark() & EMPHASISMARK_POS_ABOVE
);
1351 const bool bEmphasisMarkBelow(rFont
.GetEmphasisMark() & EMPHASISMARK_POS_BELOW
);
1353 // prepare font relief data
1354 drawinglayer::primitive2d::TextRelief
eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE
);
1356 switch(rFont
.GetRelief())
1358 case RELIEF_EMBOSSED
: eTextRelief
= drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED
; break;
1359 case RELIEF_ENGRAVED
: eTextRelief
= drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED
; break;
1360 default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
1363 // prepare shadow/outline data
1364 const bool bShadow(rFont
.IsShadow());
1366 // TextDecoratedPortionPrimitive2D is needed, create one
1367 pResult
= new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
1369 // attributes for TextSimplePortionPrimitive2D
1379 // attributes for TextDecoratedPortionPrimitive2D
1380 rProperty
.getOverlineColorActive() ? rProperty
.getOverlineColor() : aFontColor
,
1381 rProperty
.getTextLineColorActive() ? rProperty
.getTextLineColor() : aFontColor
,
1395 // TextSimplePortionPrimitive2D is enough
1396 pResult
= new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
1408 if(pResult
&& rProperty
.getTextFillColorActive())
1410 // text background is requested, add and encapsulate both to new primitive
1411 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice
;
1412 aTextLayouterDevice
.setFont(rFont
);
1415 double fTextWidth(0.0);
1417 if(rDXArray
.empty())
1419 fTextWidth
= aTextLayouterDevice
.getTextWidth(rText
, nTextStart
, nTextLength
);
1423 fTextWidth
= rDXArray
.back();
1426 if(basegfx::fTools::more(fTextWidth
, 0.0))
1429 const basegfx::B2DRange
aTextRange(
1430 0.0, -aTextLayouterDevice
.getFontAscent(),
1431 fTextWidth
, aTextLayouterDevice
.getFontDescent());
1434 basegfx::B2DHomMatrix aTextTransform
;
1436 aTextTransform
.translate(aAlignmentOffset
.getX(), aAlignmentOffset
.getY());
1438 if(rFont
.GetOrientation())
1440 aTextTransform
.rotate(-rFont
.GetOrientation() * F_PI1800
);
1443 aTextTransform
.translate(rTextStartPosition
.X(), rTextStartPosition
.Y());
1445 // prepare Primitive2DSequence, put text in foreground
1446 drawinglayer::primitive2d::Primitive2DSequence
aSequence(2);
1447 aSequence
[1] = drawinglayer::primitive2d::Primitive2DReference(pResult
);
1449 // prepare filled polygon
1450 basegfx::B2DPolygon
aOutline(basegfx::tools::createPolygonFromRect(aTextRange
));
1451 aOutline
.transform(aTextTransform
);
1453 aSequence
[0] = drawinglayer::primitive2d::Primitive2DReference(
1454 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
1455 basegfx::B2DPolyPolygon(aOutline
),
1456 rProperty
.getTextFillColor()));
1458 // set as group at pResult
1459 pResult
= new drawinglayer::primitive2d::GroupPrimitive2D(aSequence
);
1465 // add created text primitive to target
1466 if(rProperty
.getTransformation().isIdentity())
1468 rTarget
.append(pResult
);
1472 // when a transformation is set, embed to it
1473 const drawinglayer::primitive2d::Primitive2DReference
aReference(pResult
);
1476 new drawinglayer::primitive2d::TransformPrimitive2D(
1477 rProperty
.getTransformation(),
1478 drawinglayer::primitive2d::Primitive2DSequence(&aReference
, 1)));
1483 /** helper which takes complete care for creating the needed textLine primitives */
1484 void proccessMetaTextLineAction(
1485 const MetaTextLineAction
& rAction
,
1486 TargetHolder
& rTarget
,
1487 PropertyHolder
& rProperty
)
1489 const double fLineWidth(fabs((double)rAction
.GetWidth()));
1491 if(fLineWidth
> 0.0)
1493 const drawinglayer::primitive2d::TextLine
aOverlineMode(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rAction
.GetOverline()));
1494 const drawinglayer::primitive2d::TextLine
aUnderlineMode(drawinglayer::primitive2d::mapFontUnderlineToTextLine(rAction
.GetUnderline()));
1495 const drawinglayer::primitive2d::TextStrikeout
aTextStrikeout(drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rAction
.GetStrikeout()));
1497 const bool bOverlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE
!= aOverlineMode
);
1498 const bool bUnderlineUsed(drawinglayer::primitive2d::TEXT_LINE_NONE
!= aUnderlineMode
);
1499 const bool bStrikeoutUsed(drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE
!= aTextStrikeout
);
1501 if(bUnderlineUsed
|| bStrikeoutUsed
|| bOverlineUsed
)
1503 std::vector
< drawinglayer::primitive2d::BasePrimitive2D
* > aTargetVector
;
1504 basegfx::B2DVector
aAlignmentOffset(0.0, 0.0);
1505 drawinglayer::attribute::FontAttribute aFontAttribute
;
1506 basegfx::B2DHomMatrix aTextTransform
;
1508 // fill parameters derived from current font
1509 createFontAttributeTransformAndAlignment(
1515 // add TextStartPosition
1516 aTextTransform
.translate(rAction
.GetStartPoint().X(), rAction
.GetStartPoint().Y());
1518 // prepare TextLayouter (used in most cases)
1519 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter
;
1520 aTextLayouter
.setFont(rProperty
.getFont());
1524 // create primitive geometry for overline
1525 aTargetVector
.push_back(
1526 new drawinglayer::primitive2d::TextLinePrimitive2D(
1529 aTextLayouter
.getOverlineOffset(),
1530 aTextLayouter
.getOverlineHeight(),
1532 rProperty
.getOverlineColor()));
1537 // create primitive geometry for underline
1538 aTargetVector
.push_back(
1539 new drawinglayer::primitive2d::TextLinePrimitive2D(
1542 aTextLayouter
.getUnderlineOffset(),
1543 aTextLayouter
.getUnderlineHeight(),
1545 rProperty
.getTextLineColor()));
1550 // create primitive geometry for strikeout
1551 if(drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH
== aTextStrikeout
1552 || drawinglayer::primitive2d::TEXT_STRIKEOUT_X
== aTextStrikeout
)
1554 // strikeout with character
1555 const sal_Unicode
aStrikeoutChar(
1556 drawinglayer::primitive2d::TEXT_STRIKEOUT_SLASH
== aTextStrikeout
? '/' : 'X');
1557 const com::sun::star::lang::Locale
aLocale(LanguageTag(
1558 rProperty
.getLanguageType()).getLocale());
1560 aTargetVector
.push_back(
1561 new drawinglayer::primitive2d::TextCharacterStrikeoutPrimitive2D(
1564 rProperty
.getTextColor(),
1571 // strikeout with geometry
1572 aTargetVector
.push_back(
1573 new drawinglayer::primitive2d::TextGeometryStrikeoutPrimitive2D(
1576 rProperty
.getTextColor(),
1577 aTextLayouter
.getUnderlineHeight(),
1578 aTextLayouter
.getStrikeoutOffset(),
1583 if(!aTargetVector
.empty())
1585 // add created text primitive to target
1586 if(rProperty
.getTransformation().isIdentity())
1588 for(sal_uInt32
a(0); a
< aTargetVector
.size(); a
++)
1590 rTarget
.append(aTargetVector
[a
]);
1595 // when a transformation is set, embed to it
1596 drawinglayer::primitive2d::Primitive2DSequence
xTargets(aTargetVector
.size());
1598 for(sal_uInt32
a(0); a
< aTargetVector
.size(); a
++)
1600 xTargets
[a
] = drawinglayer::primitive2d::Primitive2DReference(aTargetVector
[a
]);
1604 new drawinglayer::primitive2d::TransformPrimitive2D(
1605 rProperty
.getTransformation(),
1614 /** This is the main interpreter method. It is designed to handle the given Metafile
1615 completely inside the given context and target. It may use and modify the context and
1616 target. This design allows to call itself recursively which adapted contexts and
1617 targets as e.g. needed for the META_FLOATTRANSPARENT_ACTION where the content is expressed
1618 as a metafile as sub-content.
1620 This interpreter is as free of VCL functionality as possible. It uses VCL data classes
1621 (else reading the data would not be possible), but e.g. does NOT use a local OutputDevice
1622 as most other MetaFile interpreters/exporters do to hold and work with the current context.
1623 This is necessary to be able to get away from the strong internal VCL-binding.
1625 It tries to combine e.g. pixel and/or point actions and to stitch together single line primitives
1626 where possible (which is not trivial with the possible line geometry definitions).
1628 It tries to handle clipping no longer as Regions and spans of Rectangles, but as PolyPolygon
1629 ClipRegions with (where possible) high precision by using the best possible data quality
1630 from the Region. The Region is unavoidable as data container, but nowadays allows the transport
1631 of Polygon-based clip regions. Where this is not used, a Polygon is constructed from the
1632 Region ranges. All primitive clipping uses the MaskPrimitive2D with Polygon-based clipping.
1634 I have marked the single MetaActions with:
1637 Simple, e.g nothing to do or value setting in the context
1639 CHECKED, WORKS WELL:
1640 Thoroughly tested with extra written test code which created a replacement
1641 Metafile just to test this action in various combinations
1643 NEEDS IMPLEMENTATION:
1644 Not implemented and asserted, but also no usage found, neither in own Metafile
1645 creations, nor in EMF/WMF imports (checked with a whole bunch of critical EMF/WMF
1648 For more commens, see the single action implementations.
1650 void interpretMetafile(
1651 const GDIMetaFile
& rMetaFile
,
1652 TargetHolders
& rTargetHolders
,
1653 PropertyHolders
& rPropertyHolders
,
1654 const drawinglayer::geometry::ViewInformation2D
& rViewInformation
)
1656 const size_t nCount(rMetaFile
.GetActionSize());
1658 for(size_t nAction(0); nAction
< nCount
; nAction
++)
1660 MetaAction
* pAction
= rMetaFile
.GetAction(nAction
);
1662 switch(pAction
->GetType())
1664 case META_NULL_ACTION
:
1669 case META_PIXEL_ACTION
:
1671 /** CHECKED, WORKS WELL */
1672 std::vector
< basegfx::B2DPoint
> aPositions
;
1673 Color
aLastColor(COL_BLACK
);
1675 while(META_PIXEL_ACTION
== pAction
->GetType() && nAction
< nCount
)
1677 const MetaPixelAction
* pA
= (const MetaPixelAction
*)pAction
;
1679 if(pA
->GetColor() != aLastColor
)
1681 if(!aPositions
.empty())
1683 createPointArrayPrimitive(aPositions
, rTargetHolders
.Current(), rPropertyHolders
.Current(), aLastColor
.getBColor());
1687 aLastColor
= pA
->GetColor();
1690 const Point
& rPoint
= pA
->GetPoint();
1691 aPositions
.push_back(basegfx::B2DPoint(rPoint
.X(), rPoint
.Y()));
1692 nAction
++; if(nAction
< nCount
) pAction
= rMetaFile
.GetAction(nAction
);
1697 if(!aPositions
.empty())
1699 createPointArrayPrimitive(aPositions
, rTargetHolders
.Current(), rPropertyHolders
.Current(), aLastColor
.getBColor());
1704 case META_POINT_ACTION
:
1706 /** CHECKED, WORKS WELL */
1707 if(rPropertyHolders
.Current().getLineColorActive())
1709 std::vector
< basegfx::B2DPoint
> aPositions
;
1711 while(META_POINT_ACTION
== pAction
->GetType() && nAction
< nCount
)
1713 const MetaPointAction
* pA
= (const MetaPointAction
*)pAction
;
1714 const Point
& rPoint
= pA
->GetPoint();
1715 aPositions
.push_back(basegfx::B2DPoint(rPoint
.X(), rPoint
.Y()));
1716 nAction
++; if(nAction
< nCount
) pAction
= rMetaFile
.GetAction(nAction
);
1721 if(!aPositions
.empty())
1723 createPointArrayPrimitive(aPositions
, rTargetHolders
.Current(), rPropertyHolders
.Current(), rPropertyHolders
.Current().getLineColor());
1729 case META_LINE_ACTION
:
1731 /** CHECKED, WORKS WELL */
1732 if(rPropertyHolders
.Current().getLineColorActive())
1734 basegfx::B2DPolygon aLinePolygon
;
1737 while(META_LINE_ACTION
== pAction
->GetType() && nAction
< nCount
)
1739 const MetaLineAction
* pA
= (const MetaLineAction
*)pAction
;
1740 const Point
& rStartPoint
= pA
->GetStartPoint();
1741 const Point
& rEndPoint
= pA
->GetEndPoint();
1742 const basegfx::B2DPoint
aStart(rStartPoint
.X(), rStartPoint
.Y());
1743 const basegfx::B2DPoint
aEnd(rEndPoint
.X(), rEndPoint
.Y());
1745 if(aLinePolygon
.count())
1747 if(pA
->GetLineInfo() == aLineInfo
1748 && aStart
== aLinePolygon
.getB2DPoint(aLinePolygon
.count() - 1))
1750 aLinePolygon
.append(aEnd
);
1754 aLineInfo
.SetLineJoin(basegfx::B2DLINEJOIN_NONE
); // It were lines; force to NONE
1755 createLinePrimitive(aLinePolygon
, aLineInfo
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1756 aLinePolygon
.clear();
1757 aLineInfo
= pA
->GetLineInfo();
1758 aLinePolygon
.append(aStart
);
1759 aLinePolygon
.append(aEnd
);
1764 aLineInfo
= pA
->GetLineInfo();
1765 aLinePolygon
.append(aStart
);
1766 aLinePolygon
.append(aEnd
);
1769 nAction
++; if(nAction
< nCount
) pAction
= rMetaFile
.GetAction(nAction
);
1774 if(aLinePolygon
.count())
1776 aLineInfo
.SetLineJoin(basegfx::B2DLINEJOIN_NONE
); // It were lines; force to NONE
1777 createLinePrimitive(aLinePolygon
, aLineInfo
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1783 case META_RECT_ACTION
:
1785 /** CHECKED, WORKS WELL */
1786 if(rPropertyHolders
.Current().getLineOrFillActive())
1788 const MetaRectAction
* pA
= (const MetaRectAction
*)pAction
;
1789 const Rectangle
& rRectangle
= pA
->GetRect();
1791 if(!rRectangle
.IsEmpty())
1793 const basegfx::B2DRange
aRange(rRectangle
.Left(), rRectangle
.Top(), rRectangle
.Right(), rRectangle
.Bottom());
1795 if(!aRange
.isEmpty())
1797 const basegfx::B2DPolygon
aOutline(basegfx::tools::createPolygonFromRect(aRange
));
1798 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1805 case META_ROUNDRECT_ACTION
:
1807 /** CHECKED, WORKS WELL */
1808 /** The original OutputDevice::DrawRect paints nothing when nHor or nVer is zero; but just
1809 because the tools::Polygon operator creating the rounding does produce nonsense. I assume
1810 this an error and create an unrounded rectangle in that case (implicit in
1811 createPolygonFromRect)
1813 if(rPropertyHolders
.Current().getLineOrFillActive())
1815 const MetaRoundRectAction
* pA
= (const MetaRoundRectAction
*)pAction
;
1816 const Rectangle
& rRectangle
= pA
->GetRect();
1818 if(!rRectangle
.IsEmpty())
1820 const basegfx::B2DRange
aRange(rRectangle
.Left(), rRectangle
.Top(), rRectangle
.Right(), rRectangle
.Bottom());
1822 if(!aRange
.isEmpty())
1824 const sal_uInt32
nHor(pA
->GetHorzRound());
1825 const sal_uInt32
nVer(pA
->GetVertRound());
1826 basegfx::B2DPolygon aOutline
;
1830 double fRadiusX((nHor
* 2.0) / (aRange
.getWidth() > 0.0 ? aRange
.getWidth() : 1.0));
1831 double fRadiusY((nVer
* 2.0) / (aRange
.getHeight() > 0.0 ? aRange
.getHeight() : 1.0));
1832 fRadiusX
= std::max(0.0, std::min(1.0, fRadiusX
));
1833 fRadiusY
= std::max(0.0, std::min(1.0, fRadiusY
));
1835 aOutline
= basegfx::tools::createPolygonFromRect(aRange
, fRadiusX
, fRadiusY
);
1839 aOutline
= basegfx::tools::createPolygonFromRect(aRange
);
1842 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1849 case META_ELLIPSE_ACTION
:
1851 /** CHECKED, WORKS WELL */
1852 if(rPropertyHolders
.Current().getLineOrFillActive())
1854 const MetaEllipseAction
* pA
= (const MetaEllipseAction
*)pAction
;
1855 const Rectangle
& rRectangle
= pA
->GetRect();
1857 if(!rRectangle
.IsEmpty())
1859 const basegfx::B2DRange
aRange(rRectangle
.Left(), rRectangle
.Top(), rRectangle
.Right(), rRectangle
.Bottom());
1861 if(!aRange
.isEmpty())
1863 const basegfx::B2DPolygon
aOutline(basegfx::tools::createPolygonFromEllipse(
1864 aRange
.getCenter(), aRange
.getWidth() * 0.5, aRange
.getHeight() * 0.5));
1866 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1873 case META_ARC_ACTION
:
1875 /** CHECKED, WORKS WELL */
1876 if(rPropertyHolders
.Current().getLineColorActive())
1878 const MetaArcAction
* pA
= (const MetaArcAction
*)pAction
;
1879 const Polygon
aToolsPoly(pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), POLY_ARC
);
1880 const basegfx::B2DPolygon
aOutline(aToolsPoly
.getB2DPolygon());
1882 createHairlinePrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1887 case META_PIE_ACTION
:
1889 /** CHECKED, WORKS WELL */
1890 if(rPropertyHolders
.Current().getLineOrFillActive())
1892 const MetaPieAction
* pA
= (const MetaPieAction
*)pAction
;
1893 const Polygon
aToolsPoly(pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), POLY_PIE
);
1894 const basegfx::B2DPolygon
aOutline(aToolsPoly
.getB2DPolygon());
1896 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1901 case META_CHORD_ACTION
:
1903 /** CHECKED, WORKS WELL */
1904 if(rPropertyHolders
.Current().getLineOrFillActive())
1906 const MetaChordAction
* pA
= (const MetaChordAction
*)pAction
;
1907 const Polygon
aToolsPoly(pA
->GetRect(), pA
->GetStartPoint(), pA
->GetEndPoint(), POLY_CHORD
);
1908 const basegfx::B2DPolygon
aOutline(aToolsPoly
.getB2DPolygon());
1910 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1915 case META_POLYLINE_ACTION
:
1917 /** CHECKED, WORKS WELL */
1918 if(rPropertyHolders
.Current().getLineColorActive())
1920 const MetaPolyLineAction
* pA
= (const MetaPolyLineAction
*)pAction
;
1921 createLinePrimitive(pA
->GetPolygon().getB2DPolygon(), pA
->GetLineInfo(), rTargetHolders
.Current(), rPropertyHolders
.Current());
1926 case META_POLYGON_ACTION
:
1928 /** CHECKED, WORKS WELL */
1929 if(rPropertyHolders
.Current().getLineOrFillActive())
1931 const MetaPolygonAction
* pA
= (const MetaPolygonAction
*)pAction
;
1932 basegfx::B2DPolygon
aOutline(pA
->GetPolygon().getB2DPolygon());
1934 // the metafile play interprets the polygons from MetaPolygonAction
1935 // always as closed and always paints an edge from last to first point,
1936 // so force to closed here to emulate that
1937 if(aOutline
.count() > 1 && !aOutline
.isClosed())
1939 aOutline
.setClosed(true);
1942 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1947 case META_POLYPOLYGON_ACTION
:
1949 /** CHECKED, WORKS WELL */
1950 if(rPropertyHolders
.Current().getLineOrFillActive())
1952 const MetaPolyPolygonAction
* pA
= (const MetaPolyPolygonAction
*)pAction
;
1953 basegfx::B2DPolyPolygon
aPolyPolygonOutline(pA
->GetPolyPolygon().getB2DPolyPolygon());
1955 // the metafile play interprets the single polygons from MetaPolyPolygonAction
1956 // always as closed and always paints an edge from last to first point,
1957 // so force to closed here to emulate that
1958 for(sal_uInt32
b(0); b
< aPolyPolygonOutline
.count(); b
++)
1960 basegfx::B2DPolygon
aPolygonOutline(aPolyPolygonOutline
.getB2DPolygon(b
));
1962 if(aPolygonOutline
.count() > 1 && !aPolygonOutline
.isClosed())
1964 aPolygonOutline
.setClosed(true);
1965 aPolyPolygonOutline
.setB2DPolygon(b
, aPolygonOutline
);
1969 createHairlineAndFillPrimitive(aPolyPolygonOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
1974 case META_TEXT_ACTION
:
1976 /** CHECKED, WORKS WELL */
1977 const MetaTextAction
* pA
= (const MetaTextAction
*)pAction
;
1978 sal_uInt32
nTextLength(pA
->GetLen());
1979 const sal_uInt32
nTextIndex(pA
->GetIndex());
1980 const sal_uInt32
nStringLength(pA
->GetText().getLength());
1982 if(nTextLength
+ nTextIndex
> nStringLength
)
1984 nTextLength
= nStringLength
- nTextIndex
;
1987 if(nTextLength
&& rPropertyHolders
.Current().getTextColorActive())
1989 const std::vector
< double > aDXArray
;
1990 processMetaTextAction(
1996 rTargetHolders
.Current(),
1997 rPropertyHolders
.Current());
2002 case META_TEXTARRAY_ACTION
:
2004 /** CHECKED, WORKS WELL */
2005 const MetaTextArrayAction
* pA
= (const MetaTextArrayAction
*)pAction
;
2006 sal_uInt32
nTextLength(pA
->GetLen());
2007 const sal_uInt32
nTextIndex(pA
->GetIndex());
2008 const sal_uInt32
nStringLength(pA
->GetText().getLength());
2010 if(nTextLength
+ nTextIndex
> nStringLength
)
2012 nTextLength
= nTextIndex
> nStringLength
? 0 : nStringLength
- nTextIndex
;
2015 if(nTextLength
&& rPropertyHolders
.Current().getTextColorActive())
2017 // preapare DXArray (if used)
2018 std::vector
< double > aDXArray
;
2019 sal_Int32
* pDXArray
= pA
->GetDXArray();
2023 aDXArray
.reserve(nTextLength
);
2025 for(sal_uInt32
a(0); a
< nTextLength
; a
++)
2027 aDXArray
.push_back((double)(*(pDXArray
+ a
)));
2031 processMetaTextAction(
2037 rTargetHolders
.Current(),
2038 rPropertyHolders
.Current());
2043 case META_STRETCHTEXT_ACTION
:
2045 // #i108440# StarMath uses MetaStretchTextAction, thus support is needed.
2046 // It looks as if it pretty never really uses a width different from
2047 // the default text-layout width, but it's not possible to be sure.
2048 // Implemented getting the DXArray and checking for scale at all. If
2049 // scale is more than 3.5% different, scale the DXArray before usage.
2052 /** CHECKED, WORKS WELL */
2053 const MetaStretchTextAction
* pA
= (const MetaStretchTextAction
*)pAction
;
2054 sal_uInt32
nTextLength(pA
->GetLen());
2055 const sal_uInt32
nTextIndex(pA
->GetIndex());
2056 const sal_uInt32
nStringLength(pA
->GetText().getLength());
2058 if(nTextLength
+ nTextIndex
> nStringLength
)
2060 nTextLength
= nStringLength
- nTextIndex
;
2063 if(nTextLength
&& rPropertyHolders
.Current().getTextColorActive())
2065 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice
;
2066 aTextLayouterDevice
.setFont(rPropertyHolders
.Current().getFont());
2068 ::std::vector
< double > aTextArray(
2069 aTextLayouterDevice
.getTextArray(
2074 if(!aTextArray
.empty())
2076 const double fTextLength(aTextArray
.back());
2078 if(0.0 != fTextLength
&& pA
->GetWidth())
2080 const double fRelative(pA
->GetWidth() / fTextLength
);
2082 if(fabs(fRelative
- 1.0) >= 0.035)
2084 // when derivation is more than 3,5% from default text size,
2085 // scale the DXArray
2086 for(sal_uInt32
a(0); a
< aTextArray
.size(); a
++)
2088 aTextArray
[a
] *= fRelative
;
2094 processMetaTextAction(
2100 rTargetHolders
.Current(),
2101 rPropertyHolders
.Current());
2106 case META_TEXTRECT_ACTION
:
2108 /** CHECKED, WORKS WELL */
2109 // OSL_FAIL("META_TEXTRECT_ACTION requested (!)");
2110 const MetaTextRectAction
* pA
= (const MetaTextRectAction
*)pAction
;
2111 const Rectangle
& rRectangle
= pA
->GetRect();
2112 const sal_uInt32
nStringLength(pA
->GetText().getLength());
2114 if(!rRectangle
.IsEmpty() && 0 != nStringLength
)
2116 // The problem with this action is that it describes unlayouted text
2117 // and the layout capabilities are in EditEngine/Outliner in SVX. The
2118 // same problem is true for VCL which internally has implementations
2119 // to layout text in this case. There exists even a call
2120 // OutputDevice::AddTextRectActions(...) to create the needed actions
2121 // as 'sub-content' of a Metafile. Unfortunately i do not have an
2122 // OutputDevice here since this interpreter tries to work without
2124 // Since AddTextRectActions is the only way as long as we do not have
2125 // a simple text layouter available, i will try to add it to the
2126 // TextLayouterDevice isloation.
2127 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice
;
2128 aTextLayouterDevice
.setFont(rPropertyHolders
.Current().getFont());
2129 GDIMetaFile aGDIMetaFile
;
2131 aTextLayouterDevice
.addTextRectActions(
2132 rRectangle
, pA
->GetText(), pA
->GetStyle(), aGDIMetaFile
);
2134 if(aGDIMetaFile
.GetActionSize())
2136 // create sub-content
2137 drawinglayer::primitive2d::Primitive2DSequence xSubContent
;
2139 rTargetHolders
.Push();
2140 // #i# for sub-Mteafile contents, do start with new, default render state
2141 rPropertyHolders
.PushDefault();
2142 interpretMetafile(aGDIMetaFile
, rTargetHolders
, rPropertyHolders
, rViewInformation
);
2143 xSubContent
= rTargetHolders
.Current().getPrimitive2DSequence(rPropertyHolders
.Current());
2144 rPropertyHolders
.Pop();
2145 rTargetHolders
.Pop();
2148 if(xSubContent
.hasElements())
2150 // add with transformation
2151 rTargetHolders
.Current().append(
2152 new drawinglayer::primitive2d::TransformPrimitive2D(
2153 rPropertyHolders
.Current().getTransformation(),
2161 case META_BMP_ACTION
:
2163 /** CHECKED, WORKS WELL */
2164 const MetaBmpAction
* pA
= (const MetaBmpAction
*)pAction
;
2165 const BitmapEx
aBitmapEx(pA
->GetBitmap());
2167 createBitmapExPrimitive(aBitmapEx
, pA
->GetPoint(), rTargetHolders
.Current(), rPropertyHolders
.Current());
2171 case META_BMPSCALE_ACTION
:
2173 /** CHECKED, WORKS WELL */
2174 const MetaBmpScaleAction
* pA
= (const MetaBmpScaleAction
*)pAction
;
2175 const Bitmap
aBitmapEx(pA
->GetBitmap());
2177 createBitmapExPrimitive(aBitmapEx
, pA
->GetPoint(), pA
->GetSize(), rTargetHolders
.Current(), rPropertyHolders
.Current());
2181 case META_BMPSCALEPART_ACTION
:
2183 /** CHECKED, WORKS WELL */
2184 const MetaBmpScalePartAction
* pA
= (const MetaBmpScalePartAction
*)pAction
;
2185 const Bitmap
& rBitmap
= pA
->GetBitmap();
2187 if(!rBitmap
.IsEmpty())
2189 Bitmap
aCroppedBitmap(rBitmap
);
2190 const Rectangle
aCropRectangle(pA
->GetSrcPoint(), pA
->GetSrcSize());
2192 if(!aCropRectangle
.IsEmpty())
2194 aCroppedBitmap
.Crop(aCropRectangle
);
2197 const BitmapEx
aCroppedBitmapEx(aCroppedBitmap
);
2198 createBitmapExPrimitive(aCroppedBitmapEx
, pA
->GetDestPoint(), pA
->GetDestSize(), rTargetHolders
.Current(), rPropertyHolders
.Current());
2203 case META_BMPEX_ACTION
:
2205 /** CHECKED, WORKS WELL: Simply same as META_BMP_ACTION */
2206 const MetaBmpExAction
* pA
= (const MetaBmpExAction
*)pAction
;
2207 const BitmapEx
& rBitmapEx
= pA
->GetBitmapEx();
2209 createBitmapExPrimitive(rBitmapEx
, pA
->GetPoint(), rTargetHolders
.Current(), rPropertyHolders
.Current());
2213 case META_BMPEXSCALE_ACTION
:
2215 /** CHECKED, WORKS WELL: Simply same as META_BMPSCALE_ACTION */
2216 const MetaBmpExScaleAction
* pA
= (const MetaBmpExScaleAction
*)pAction
;
2217 const BitmapEx
& rBitmapEx
= pA
->GetBitmapEx();
2219 createBitmapExPrimitive(rBitmapEx
, pA
->GetPoint(), pA
->GetSize(), rTargetHolders
.Current(), rPropertyHolders
.Current());
2223 case META_BMPEXSCALEPART_ACTION
:
2225 /** CHECKED, WORKS WELL: Simply same as META_BMPSCALEPART_ACTION */
2226 const MetaBmpExScalePartAction
* pA
= (const MetaBmpExScalePartAction
*)pAction
;
2227 const BitmapEx
& rBitmapEx
= pA
->GetBitmapEx();
2229 if(!rBitmapEx
.IsEmpty())
2231 BitmapEx
aCroppedBitmapEx(rBitmapEx
);
2232 const Rectangle
aCropRectangle(pA
->GetSrcPoint(), pA
->GetSrcSize());
2234 if(!aCropRectangle
.IsEmpty())
2236 aCroppedBitmapEx
.Crop(aCropRectangle
);
2239 createBitmapExPrimitive(aCroppedBitmapEx
, pA
->GetDestPoint(), pA
->GetDestSize(), rTargetHolders
.Current(), rPropertyHolders
.Current());
2244 case META_MASK_ACTION
:
2246 /** CHECKED, WORKS WELL: Simply same as META_BMP_ACTION */
2247 const MetaMaskAction
* pA
= (const MetaMaskAction
*)pAction
;
2248 const BitmapEx
aBitmapEx(createMaskBmpEx(pA
->GetBitmap(), pA
->GetColor()));
2250 createBitmapExPrimitive(aBitmapEx
, pA
->GetPoint(), rTargetHolders
.Current(), rPropertyHolders
.Current());
2254 case META_MASKSCALE_ACTION
:
2256 /** CHECKED, WORKS WELL: Simply same as META_BMPSCALE_ACTION */
2257 const MetaMaskScaleAction
* pA
= (const MetaMaskScaleAction
*)pAction
;
2258 const BitmapEx
aBitmapEx(createMaskBmpEx(pA
->GetBitmap(), pA
->GetColor()));
2260 createBitmapExPrimitive(aBitmapEx
, pA
->GetPoint(), pA
->GetSize(), rTargetHolders
.Current(), rPropertyHolders
.Current());
2264 case META_MASKSCALEPART_ACTION
:
2266 /** CHECKED, WORKS WELL: Simply same as META_BMPSCALEPART_ACTION */
2267 const MetaMaskScalePartAction
* pA
= (const MetaMaskScalePartAction
*)pAction
;
2268 const Bitmap
& rBitmap
= pA
->GetBitmap();
2270 if(!rBitmap
.IsEmpty())
2272 Bitmap
aCroppedBitmap(rBitmap
);
2273 const Rectangle
aCropRectangle(pA
->GetSrcPoint(), pA
->GetSrcSize());
2275 if(!aCropRectangle
.IsEmpty())
2277 aCroppedBitmap
.Crop(aCropRectangle
);
2280 const BitmapEx
aCroppedBitmapEx(createMaskBmpEx(aCroppedBitmap
, pA
->GetColor()));
2281 createBitmapExPrimitive(aCroppedBitmapEx
, pA
->GetDestPoint(), pA
->GetDestSize(), rTargetHolders
.Current(), rPropertyHolders
.Current());
2286 case META_GRADIENT_ACTION
:
2288 /** CHECKED, WORKS WELL */
2289 const MetaGradientAction
* pA
= (const MetaGradientAction
*)pAction
;
2290 const Rectangle
& rRectangle
= pA
->GetRect();
2292 if(!rRectangle
.IsEmpty())
2294 basegfx::B2DRange
aRange(rRectangle
.Left(), rRectangle
.Top(), rRectangle
.Right(), rRectangle
.Bottom());
2296 if(!aRange
.isEmpty())
2298 const Gradient
& rGradient
= pA
->GetGradient();
2299 const drawinglayer::attribute::FillGradientAttribute
aAttribute(createFillGradientAttribute(rGradient
));
2300 basegfx::B2DPolyPolygon
aOutline(basegfx::tools::createPolygonFromRect(aRange
));
2302 if(aAttribute
.getStartColor() == aAttribute
.getEndColor())
2304 // not really a gradient. Create filled rectangle
2305 createFillPrimitive(
2307 rTargetHolders
.Current(),
2308 rPropertyHolders
.Current());
2312 // really a gradient
2313 aRange
.transform(rPropertyHolders
.Current().getTransformation());
2314 drawinglayer::primitive2d::Primitive2DSequence
xGradient(1);
2316 if(rPropertyHolders
.Current().isRasterOpInvert())
2318 // use a special version of FillGradientPrimitive2D which creates
2319 // non-overlapping geometry on decomposition to makethe old XOR
2320 // paint 'trick' work.
2321 xGradient
[0] = drawinglayer::primitive2d::Primitive2DReference(
2322 new drawinglayer::primitive2d::NonOverlappingFillGradientPrimitive2D(
2328 xGradient
[0] = drawinglayer::primitive2d::Primitive2DReference(
2329 new drawinglayer::primitive2d::FillGradientPrimitive2D(
2334 // #i112300# clip against polygon representing the rectangle from
2335 // the action. This is implicitely done using a temp Clipping in VCL
2336 // when a MetaGradientAction is executed
2337 aOutline
.transform(rPropertyHolders
.Current().getTransformation());
2338 rTargetHolders
.Current().append(
2339 new drawinglayer::primitive2d::MaskPrimitive2D(
2348 case META_HATCH_ACTION
:
2350 /** CHECKED, WORKS WELL */
2351 const MetaHatchAction
* pA
= (const MetaHatchAction
*)pAction
;
2352 basegfx::B2DPolyPolygon
aOutline(pA
->GetPolyPolygon().getB2DPolyPolygon());
2354 if(aOutline
.count())
2356 const Hatch
& rHatch
= pA
->GetHatch();
2357 const drawinglayer::attribute::FillHatchAttribute
aAttribute(createFillHatchAttribute(rHatch
));
2359 aOutline
.transform(rPropertyHolders
.Current().getTransformation());
2361 const basegfx::B2DRange
aObjectRange(aOutline
.getB2DRange());
2362 const drawinglayer::primitive2d::Primitive2DReference
aFillHatch(
2363 new drawinglayer::primitive2d::FillHatchPrimitive2D(
2368 rTargetHolders
.Current().append(
2369 new drawinglayer::primitive2d::MaskPrimitive2D(
2371 drawinglayer::primitive2d::Primitive2DSequence(&aFillHatch
, 1)));
2376 case META_WALLPAPER_ACTION
:
2378 /** CHECKED, WORKS WELL */
2379 const MetaWallpaperAction
* pA
= (const MetaWallpaperAction
*)pAction
;
2380 Rectangle
aWallpaperRectangle(pA
->GetRect());
2382 if(!aWallpaperRectangle
.IsEmpty())
2384 const Wallpaper
& rWallpaper
= pA
->GetWallpaper();
2385 const WallpaperStyle
eWallpaperStyle(rWallpaper
.GetStyle());
2386 basegfx::B2DRange
aWallpaperRange(
2387 aWallpaperRectangle
.Left(), aWallpaperRectangle
.Top(),
2388 aWallpaperRectangle
.Right(), aWallpaperRectangle
.Bottom());
2390 if(WALLPAPER_NULL
!= eWallpaperStyle
)
2392 if(rWallpaper
.IsBitmap())
2394 // create bitmap background. Caution: This
2395 // also will create gradient/color background(s)
2396 // when the bitmap is transparent or not tiled
2397 CreateAndAppendBitmapWallpaper(
2400 rTargetHolders
.Current(),
2401 rPropertyHolders
.Current());
2403 else if(rWallpaper
.IsGradient())
2405 // create gradient background
2406 rTargetHolders
.Current().append(
2407 CreateGradientWallpaper(
2409 rWallpaper
.GetGradient(),
2410 rPropertyHolders
.Current()));
2412 else if(!rWallpaper
.GetColor().GetTransparency())
2414 // create color background
2415 rTargetHolders
.Current().append(
2416 CreateColorWallpaper(
2418 rWallpaper
.GetColor().getBColor(),
2419 rPropertyHolders
.Current()));
2426 case META_CLIPREGION_ACTION
:
2428 /** CHECKED, WORKS WELL */
2429 const MetaClipRegionAction
* pA
= (const MetaClipRegionAction
*)pAction
;
2431 if(pA
->IsClipping())
2433 // new clipping. Get PolyPolygon and transform with current transformation
2434 basegfx::B2DPolyPolygon
aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(pA
->GetRegion()));
2436 aNewClipPolyPolygon
.transform(rPropertyHolders
.Current().getTransformation());
2437 HandleNewClipRegion(aNewClipPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2442 const basegfx::B2DPolyPolygon aEmptyPolyPolygon
;
2444 HandleNewClipRegion(aEmptyPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2449 case META_ISECTRECTCLIPREGION_ACTION
:
2451 /** CHECKED, WORKS WELL */
2452 const MetaISectRectClipRegionAction
* pA
= (const MetaISectRectClipRegionAction
*)pAction
;
2453 const Rectangle
& rRectangle
= pA
->GetRect();
2455 if(rRectangle
.IsEmpty())
2457 // intersect with empty rectangle will always give empty
2458 // ClipPolyPolygon; start new clipping with empty PolyPolygon
2459 const basegfx::B2DPolyPolygon aEmptyPolyPolygon
;
2461 HandleNewClipRegion(aEmptyPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2465 // create transformed ClipRange
2466 basegfx::B2DRange
aClipRange(
2467 rRectangle
.Left(), rRectangle
.Top(),
2468 rRectangle
.Right(), rRectangle
.Bottom());
2470 aClipRange
.transform(rPropertyHolders
.Current().getTransformation());
2472 if(rPropertyHolders
.Current().getClipPolyPolygonActive())
2474 if(0 == rPropertyHolders
.Current().getClipPolyPolygon().count())
2476 // nothing to do, empty active clipPolyPolygon will stay
2477 // empty when intersecting
2481 // AND existing region and new ClipRange
2482 const basegfx::B2DPolyPolygon
aOriginalPolyPolygon(
2483 rPropertyHolders
.Current().getClipPolyPolygon());
2484 basegfx::B2DPolyPolygon aClippedPolyPolygon
;
2486 if(aOriginalPolyPolygon
.count())
2488 aClippedPolyPolygon
= basegfx::tools::clipPolyPolygonOnRange(
2489 aOriginalPolyPolygon
,
2495 if(aClippedPolyPolygon
!= aOriginalPolyPolygon
)
2497 // start new clipping with intersected region
2498 HandleNewClipRegion(
2499 aClippedPolyPolygon
,
2507 // start new clipping with ClipRange
2508 const basegfx::B2DPolyPolygon
aNewClipPolyPolygon(
2509 basegfx::tools::createPolygonFromRect(aClipRange
));
2511 HandleNewClipRegion(aNewClipPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2517 case META_ISECTREGIONCLIPREGION_ACTION
:
2519 /** CHECKED, WORKS WELL */
2520 const MetaISectRegionClipRegionAction
* pA
= (const MetaISectRegionClipRegionAction
*)pAction
;
2521 const Region
& rNewRegion
= pA
->GetRegion();
2523 if(rNewRegion
.IsEmpty())
2525 // intersect with empty region will always give empty
2526 // region; start new clipping with empty PolyPolygon
2527 const basegfx::B2DPolyPolygon aEmptyPolyPolygon
;
2529 HandleNewClipRegion(aEmptyPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2533 // get new ClipPolyPolygon, transform it with current transformation
2534 basegfx::B2DPolyPolygon
aNewClipPolyPolygon(getB2DPolyPolygonFromRegion(rNewRegion
));
2535 aNewClipPolyPolygon
.transform(rPropertyHolders
.Current().getTransformation());
2537 if(rPropertyHolders
.Current().getClipPolyPolygonActive())
2539 if(0 == rPropertyHolders
.Current().getClipPolyPolygon().count())
2541 // nothing to do, empty active clipPolyPolygon will stay empty
2542 // when intersecting with any region
2546 // AND existing and new region
2547 const basegfx::B2DPolyPolygon
aOriginalPolyPolygon(
2548 rPropertyHolders
.Current().getClipPolyPolygon());
2549 basegfx::B2DPolyPolygon aClippedPolyPolygon
;
2551 if(aOriginalPolyPolygon
.count())
2553 aClippedPolyPolygon
= basegfx::tools::clipPolyPolygonOnPolyPolygon(
2554 aOriginalPolyPolygon
, aNewClipPolyPolygon
, true, false);
2557 if(aClippedPolyPolygon
!= aOriginalPolyPolygon
)
2559 // start new clipping with intersected ClipPolyPolygon
2560 HandleNewClipRegion(aClippedPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2566 // start new clipping with new ClipPolyPolygon
2567 HandleNewClipRegion(aNewClipPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2573 case META_MOVECLIPREGION_ACTION
:
2575 /** CHECKED, WORKS WELL */
2576 const MetaMoveClipRegionAction
* pA
= (const MetaMoveClipRegionAction
*)pAction
;
2578 if(rPropertyHolders
.Current().getClipPolyPolygonActive())
2580 if(0 == rPropertyHolders
.Current().getClipPolyPolygon().count())
2586 const sal_Int32
nHor(pA
->GetHorzMove());
2587 const sal_Int32
nVer(pA
->GetVertMove());
2589 if(0 != nHor
|| 0 != nVer
)
2591 // prepare translation, add current transformation
2592 basegfx::B2DVector
aVector(pA
->GetHorzMove(), pA
->GetVertMove());
2593 aVector
*= rPropertyHolders
.Current().getTransformation();
2594 basegfx::B2DHomMatrix
aTransform(
2595 basegfx::tools::createTranslateB2DHomMatrix(aVector
));
2597 // transform existing region
2598 basegfx::B2DPolyPolygon
aClipPolyPolygon(
2599 rPropertyHolders
.Current().getClipPolyPolygon());
2601 aClipPolyPolygon
.transform(aTransform
);
2602 HandleNewClipRegion(aClipPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2609 case META_LINECOLOR_ACTION
:
2611 /** CHECKED, WORKS WELL */
2612 const MetaLineColorAction
* pA
= (const MetaLineColorAction
*)pAction
;
2613 const bool bActive(pA
->IsSetting());
2615 rPropertyHolders
.Current().setLineColorActive(bActive
);
2617 rPropertyHolders
.Current().setLineColor(pA
->GetColor().getBColor());
2621 case META_FILLCOLOR_ACTION
:
2623 /** CHECKED, WORKS WELL */
2624 const MetaFillColorAction
* pA
= (const MetaFillColorAction
*)pAction
;
2625 const bool bActive(pA
->IsSetting());
2627 rPropertyHolders
.Current().setFillColorActive(bActive
);
2629 rPropertyHolders
.Current().setFillColor(pA
->GetColor().getBColor());
2633 case META_TEXTCOLOR_ACTION
:
2636 const MetaTextColorAction
* pA
= (const MetaTextColorAction
*)pAction
;
2637 const bool bActivate(COL_TRANSPARENT
!= pA
->GetColor().GetColor());
2639 rPropertyHolders
.Current().setTextColorActive(bActivate
);
2640 rPropertyHolders
.Current().setTextColor(pA
->GetColor().getBColor());
2644 case META_TEXTFILLCOLOR_ACTION
:
2647 const MetaTextFillColorAction
* pA
= (const MetaTextFillColorAction
*)pAction
;
2648 const bool bWithColorArgument(pA
->IsSetting());
2650 if(bWithColorArgument
)
2652 // emulate OutputDevice::SetTextFillColor(...) WITH argument
2653 const Color
& rFontFillColor
= pA
->GetColor();
2654 rPropertyHolders
.Current().setTextFillColor(rFontFillColor
.getBColor());
2655 rPropertyHolders
.Current().setTextFillColorActive(COL_TRANSPARENT
!= rFontFillColor
.GetColor());
2659 // emulate SetFillColor() <- NO argument (!)
2660 rPropertyHolders
.Current().setTextFillColorActive(false);
2665 case META_TEXTALIGN_ACTION
:
2668 const MetaTextAlignAction
* pA
= (const MetaTextAlignAction
*)pAction
;
2669 const TextAlign aNewTextAlign
= pA
->GetTextAlign();
2671 // TextAlign is applied to the current font (as in
2672 // OutputDevice::SetTextAlign which would be used when
2673 // playing the Metafile)
2674 if(rPropertyHolders
.Current().getFont().GetAlign() != aNewTextAlign
)
2676 Font
aNewFont(rPropertyHolders
.Current().getFont());
2677 aNewFont
.SetAlign(aNewTextAlign
);
2678 rPropertyHolders
.Current().setFont(aNewFont
);
2683 case META_MAPMODE_ACTION
:
2685 /** CHECKED, WORKS WELL */
2686 // the most necessary MapMode to be interpreted is MAP_RELATIVE,
2687 // but also the others may occur. Even not yet supported ones
2688 // may need to be added here later
2689 const MetaMapModeAction
* pA
= (const MetaMapModeAction
*)pAction
;
2690 const MapMode
& rMapMode
= pA
->GetMapMode();
2691 basegfx::B2DHomMatrix aMapping
;
2693 if(MAP_RELATIVE
== rMapMode
.GetMapUnit())
2695 aMapping
= getTransformFromMapMode(rMapMode
);
2699 switch(rMapMode
.GetMapUnit())
2703 if(MAP_TWIP
== rPropertyHolders
.Current().getMapUnit())
2705 // MAP_TWIP -> MAP_100TH_MM
2706 const double fTwipTo100thMm(127.0 / 72.0);
2707 aMapping
.scale(fTwipTo100thMm
, fTwipTo100thMm
);
2713 if(MAP_100TH_MM
== rPropertyHolders
.Current().getMapUnit())
2715 // MAP_100TH_MM -> MAP_TWIP
2716 const double f100thMmToTwip(72.0 / 127.0);
2717 aMapping
.scale(f100thMmToTwip
, f100thMmToTwip
);
2723 OSL_FAIL("interpretMetafile: META_MAPMODE_ACTION with unsupported MapUnit (!)");
2728 aMapping
= getTransformFromMapMode(rMapMode
) * aMapping
;
2729 rPropertyHolders
.Current().setMapUnit(rMapMode
.GetMapUnit());
2732 if(!aMapping
.isIdentity())
2734 aMapping
= aMapping
* rPropertyHolders
.Current().getTransformation();
2735 rPropertyHolders
.Current().setTransformation(aMapping
);
2740 case META_FONT_ACTION
:
2743 const MetaFontAction
* pA
= (const MetaFontAction
*)pAction
;
2744 rPropertyHolders
.Current().setFont(pA
->GetFont());
2745 Size
aFontSize(pA
->GetFont().GetSize());
2747 if(0 == aFontSize
.Height())
2749 // this should not happen but i got Metafiles where this was the
2750 // case. A height needs to be guessed (similar to OutputDevice::ImplNewFont())
2751 Font
aCorrectedFont(pA
->GetFont());
2753 // guess 16 pixel (as in VCL)
2754 aFontSize
= Size(0, 16);
2756 // convert to target MapUnit if not pixels
2757 aFontSize
= Application::GetDefaultDevice()->LogicToLogic(
2758 aFontSize
, MAP_PIXEL
, rPropertyHolders
.Current().getMapUnit());
2760 aCorrectedFont
.SetSize(aFontSize
);
2761 rPropertyHolders
.Current().setFont(aCorrectedFont
);
2764 // older Metafiles have no META_TEXTCOLOR_ACTION which defines
2765 // the FontColor now, so use the Font's color when not transparent
2766 const Color
& rFontColor
= pA
->GetFont().GetColor();
2767 const bool bActivate(COL_TRANSPARENT
!= rFontColor
.GetColor());
2771 rPropertyHolders
.Current().setTextColor(rFontColor
.getBColor());
2774 // caution: do NOT decativate here on transparet, see
2775 // OutputDevice::SetFont(..) for more info
2776 // rPropertyHolders.Current().setTextColorActive(bActivate);
2778 // for fill color emulate a MetaTextFillColorAction with !transparent as bool,
2779 // see OutputDevice::SetFont(..) the if(mpMetaFile) case
2782 const Color
& rFontFillColor
= pA
->GetFont().GetFillColor();
2783 rPropertyHolders
.Current().setTextFillColor(rFontFillColor
.getBColor());
2784 rPropertyHolders
.Current().setTextFillColorActive(COL_TRANSPARENT
!= rFontFillColor
.GetColor());
2788 rPropertyHolders
.Current().setTextFillColorActive(false);
2793 case META_PUSH_ACTION
:
2795 /** CHECKED, WORKS WELL */
2796 const MetaPushAction
* pA
= (const MetaPushAction
*)pAction
;
2797 rPropertyHolders
.Push(pA
->GetFlags());
2801 case META_POP_ACTION
:
2803 /** CHECKED, WORKS WELL */
2804 const bool bRegionMayChange(rPropertyHolders
.Current().getPushFlags() & PUSH_CLIPREGION
);
2805 const bool bRasterOpMayChange(rPropertyHolders
.Current().getPushFlags() & PUSH_RASTEROP
);
2807 if(bRegionMayChange
&& rPropertyHolders
.Current().getClipPolyPolygonActive())
2809 // end evtl. clipping
2810 const basegfx::B2DPolyPolygon aEmptyPolyPolygon
;
2812 HandleNewClipRegion(aEmptyPolyPolygon
, rTargetHolders
, rPropertyHolders
);
2815 if(bRasterOpMayChange
&& rPropertyHolders
.Current().isRasterOpActive())
2817 // end evtl. RasterOp
2818 HandleNewRasterOp(ROP_OVERPAINT
, rTargetHolders
, rPropertyHolders
);
2821 rPropertyHolders
.Pop();
2823 if(bRasterOpMayChange
&& rPropertyHolders
.Current().isRasterOpActive())
2825 // start evtl. RasterOp
2826 HandleNewRasterOp(rPropertyHolders
.Current().getRasterOp(), rTargetHolders
, rPropertyHolders
);
2829 if(bRegionMayChange
&& rPropertyHolders
.Current().getClipPolyPolygonActive())
2831 // start evtl. clipping
2832 HandleNewClipRegion(
2833 rPropertyHolders
.Current().getClipPolyPolygon(), rTargetHolders
, rPropertyHolders
);
2838 case META_RASTEROP_ACTION
:
2840 /** CHECKED, WORKS WELL */
2841 const MetaRasterOpAction
* pA
= (const MetaRasterOpAction
*)pAction
;
2842 const RasterOp aRasterOp
= pA
->GetRasterOp();
2844 HandleNewRasterOp(aRasterOp
, rTargetHolders
, rPropertyHolders
);
2848 case META_TRANSPARENT_ACTION
:
2850 /** CHECKED, WORKS WELL */
2851 const MetaTransparentAction
* pA
= (const MetaTransparentAction
*)pAction
;
2852 const basegfx::B2DPolyPolygon
aOutline(pA
->GetPolyPolygon().getB2DPolyPolygon());
2854 if(aOutline
.count())
2856 const sal_uInt16
nTransparence(pA
->GetTransparence());
2858 if(0 == nTransparence
)
2861 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
2863 else if(nTransparence
>= 100)
2865 // fully or more than transparent
2869 // transparent. Create new target
2870 rTargetHolders
.Push();
2872 // create primitives there and get them
2873 createHairlineAndFillPrimitive(aOutline
, rTargetHolders
.Current(), rPropertyHolders
.Current());
2874 const drawinglayer::primitive2d::Primitive2DSequence
aSubContent(
2875 rTargetHolders
.Current().getPrimitive2DSequence(rPropertyHolders
.Current()));
2877 // back to old target
2878 rTargetHolders
.Pop();
2880 if(aSubContent
.hasElements())
2882 rTargetHolders
.Current().append(
2883 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
2885 nTransparence
* 0.01));
2892 case META_EPS_ACTION
:
2894 /** CHECKED, WORKS WELL */
2895 // To support this action, i have added a EpsPrimitive2D which will
2896 // by default decompose to the Metafile replacement data. To support
2897 // this EPS on screen, the renderer visualizing this has to support
2898 // that primitive and visualize the Eps file (e.g. printing)
2899 const MetaEPSAction
* pA
= (const MetaEPSAction
*)pAction
;
2900 const Rectangle
aRectangle(pA
->GetPoint(), pA
->GetSize());
2902 if(!aRectangle
.IsEmpty())
2904 // create object transform
2905 basegfx::B2DHomMatrix aObjectTransform
;
2907 aObjectTransform
.set(0, 0, aRectangle
.GetWidth());
2908 aObjectTransform
.set(1, 1, aRectangle
.GetHeight());
2909 aObjectTransform
.set(0, 2, aRectangle
.Left());
2910 aObjectTransform
.set(1, 2, aRectangle
.Top());
2912 // add current transformation
2913 aObjectTransform
= rPropertyHolders
.Current().getTransformation() * aObjectTransform
;
2915 // embed using EpsPrimitive
2916 rTargetHolders
.Current().append(
2917 new drawinglayer::primitive2d::EpsPrimitive2D(
2920 pA
->GetSubstitute()));
2925 case META_REFPOINT_ACTION
:
2928 // only used for hatch and line pattern offsets, pretty much no longer
2930 // const MetaRefPointAction* pA = (const MetaRefPointAction*)pAction;
2933 case META_TEXTLINECOLOR_ACTION
:
2936 const MetaTextLineColorAction
* pA
= (const MetaTextLineColorAction
*)pAction
;
2937 const bool bActive(pA
->IsSetting());
2939 rPropertyHolders
.Current().setTextLineColorActive(bActive
);
2941 rPropertyHolders
.Current().setTextLineColor(pA
->GetColor().getBColor());
2945 case META_TEXTLINE_ACTION
:
2947 /** CHECKED, WORKS WELL */
2948 // actually creates overline, underline and strikeouts, so
2949 // these should be isolated from TextDecoratedPortionPrimitive2D
2950 // to own primitives. Done, available now.
2952 // This Metaaction seems not to be used (was not used in any
2953 // checked files). It's used in combination with the current
2955 const MetaTextLineAction
* pA
= (const MetaTextLineAction
*)pAction
;
2957 proccessMetaTextLineAction(
2959 rTargetHolders
.Current(),
2960 rPropertyHolders
.Current());
2964 case META_FLOATTRANSPARENT_ACTION
:
2966 /** CHECKED, WORKS WELL */
2967 const MetaFloatTransparentAction
* pA
= (const MetaFloatTransparentAction
*)pAction
;
2968 const basegfx::B2DRange
aTargetRange(
2971 pA
->GetPoint().X() + pA
->GetSize().Width(),
2972 pA
->GetPoint().Y() + pA
->GetSize().Height());
2974 if(!aTargetRange
.isEmpty())
2976 const GDIMetaFile
& rContent
= pA
->GetGDIMetaFile();
2978 if(rContent
.GetActionSize())
2980 // create the sub-content with no embedding specific to the
2981 // sub-metafile, this seems not to be used.
2982 drawinglayer::primitive2d::Primitive2DSequence xSubContent
;
2984 rTargetHolders
.Push();
2985 // #i# for sub-Mteafile contents, do start with new, default render state
2986 rPropertyHolders
.PushDefault();
2987 interpretMetafile(rContent
, rTargetHolders
, rPropertyHolders
, rViewInformation
);
2988 xSubContent
= rTargetHolders
.Current().getPrimitive2DSequence(rPropertyHolders
.Current());
2989 rPropertyHolders
.Pop();
2990 rTargetHolders
.Pop();
2993 if(xSubContent
.hasElements())
2995 // create SourceRange
2996 const basegfx::B2DRange
aSourceRange(
2997 rContent
.GetPrefMapMode().GetOrigin().X(),
2998 rContent
.GetPrefMapMode().GetOrigin().Y(),
2999 rContent
.GetPrefMapMode().GetOrigin().X() + rContent
.GetPrefSize().Width(),
3000 rContent
.GetPrefMapMode().GetOrigin().Y() + rContent
.GetPrefSize().Height());
3002 // apply mapping if aTargetRange and aSourceRange are not equal
3003 if(!aSourceRange
.equal(aTargetRange
))
3005 basegfx::B2DHomMatrix aTransform
;
3007 aTransform
.translate(-aSourceRange
.getMinX(), -aSourceRange
.getMinY());
3009 aTargetRange
.getWidth() / (basegfx::fTools::equalZero(aSourceRange
.getWidth()) ? 1.0 : aSourceRange
.getWidth()),
3010 aTargetRange
.getHeight() / (basegfx::fTools::equalZero(aSourceRange
.getHeight()) ? 1.0 : aSourceRange
.getHeight()));
3011 aTransform
.translate(aTargetRange
.getMinX(), aTargetRange
.getMinY());
3013 const drawinglayer::primitive2d::Primitive2DReference
aEmbeddedTransform(
3014 new drawinglayer::primitive2d::TransformPrimitive2D(
3018 xSubContent
= drawinglayer::primitive2d::Primitive2DSequence(&aEmbeddedTransform
, 1);
3021 // check if gradient is a real gradient
3022 const Gradient
& rGradient
= pA
->GetGradient();
3023 const drawinglayer::attribute::FillGradientAttribute
aAttribute(createFillGradientAttribute(rGradient
));
3025 if(aAttribute
.getStartColor() == aAttribute
.getEndColor())
3027 // not really a gradient; create UnifiedTransparencePrimitive2D
3028 rTargetHolders
.Current().append(
3029 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
3031 aAttribute
.getStartColor().luminance()));
3035 // really a gradient. Create gradient sub-content (with correct scaling)
3036 basegfx::B2DRange
aRange(aTargetRange
);
3037 aRange
.transform(rPropertyHolders
.Current().getTransformation());
3039 // prepare gradient for transparent content
3040 const drawinglayer::primitive2d::Primitive2DReference
xTransparence(
3041 new drawinglayer::primitive2d::FillGradientPrimitive2D(
3045 // create transparence primitive
3046 rTargetHolders
.Current().append(
3047 new drawinglayer::primitive2d::TransparencePrimitive2D(
3049 drawinglayer::primitive2d::Primitive2DSequence(&xTransparence
, 1)));
3057 case META_GRADIENTEX_ACTION
:
3060 // This is only a data holder which is interpreted inside comment actions,
3061 // see META_COMMENT_ACTION for more info
3062 // const MetaGradientExAction* pA = (const MetaGradientExAction*)pAction;
3065 case META_LAYOUTMODE_ACTION
:
3068 const MetaLayoutModeAction
* pA
= (const MetaLayoutModeAction
*)pAction
;
3069 rPropertyHolders
.Current().setLayoutMode(pA
->GetLayoutMode());
3072 case META_TEXTLANGUAGE_ACTION
:
3075 const MetaTextLanguageAction
* pA
= (const MetaTextLanguageAction
*)pAction
;
3076 rPropertyHolders
.Current().setLanguageType(pA
->GetTextLanguage());
3079 case META_OVERLINECOLOR_ACTION
:
3082 const MetaOverlineColorAction
* pA
= (const MetaOverlineColorAction
*)pAction
;
3083 const bool bActive(pA
->IsSetting());
3085 rPropertyHolders
.Current().setOverlineColorActive(bActive
);
3087 rPropertyHolders
.Current().setOverlineColor(pA
->GetColor().getBColor());
3091 case META_COMMENT_ACTION
:
3093 /** CHECKED, WORKS WELL */
3094 // I already implemented
3095 // XPATHFILL_SEQ_BEGIN, XPATHFILL_SEQ_END
3096 // XPATHSTROKE_SEQ_BEGIN, XPATHSTROKE_SEQ_END,
3097 // but opted to remove these again; it works well without them
3098 // and makes the code less dependent from those Metafile Add-Ons
3099 const MetaCommentAction
* pA
= (const MetaCommentAction
*)pAction
;
3101 if (pA
->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_BEGIN")))
3103 // XGRAD_SEQ_BEGIN, XGRAD_SEQ_END should be supported since the
3104 // pure recorded paint of the gradients uses the XOR paint functionality
3105 // ('trick'). This is (and will be) broblematic with AntAliasing, so it's
3106 // better to use this info
3107 const MetaGradientExAction
* pMetaGradientExAction
= 0;
3109 sal_uInt32
b(nAction
+ 1);
3111 for(; !bDone
&& b
< nCount
; b
++)
3113 pAction
= rMetaFile
.GetAction(b
);
3115 if(META_GRADIENTEX_ACTION
== pAction
->GetType())
3117 pMetaGradientExAction
= (const MetaGradientExAction
*)pAction
;
3119 else if(META_COMMENT_ACTION
== pAction
->GetType())
3121 if (((const MetaCommentAction
*)pAction
)->GetComment().equalsIgnoreAsciiCaseL(RTL_CONSTASCII_STRINGPARAM("XGRAD_SEQ_END")))
3128 if(bDone
&& pMetaGradientExAction
)
3130 // consume actions and skip forward
3133 // get geometry data
3134 basegfx::B2DPolyPolygon
aPolyPolygon(pMetaGradientExAction
->GetPolyPolygon().getB2DPolyPolygon());
3136 if(aPolyPolygon
.count())
3138 // transform geometry
3139 aPolyPolygon
.transform(rPropertyHolders
.Current().getTransformation());
3141 // get and check if gradient is a real gradient
3142 const Gradient
& rGradient
= pMetaGradientExAction
->GetGradient();
3143 const drawinglayer::attribute::FillGradientAttribute
aAttribute(createFillGradientAttribute(rGradient
));
3145 if(aAttribute
.getStartColor() == aAttribute
.getEndColor())
3147 // not really a gradient
3148 rTargetHolders
.Current().append(
3149 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
3151 aAttribute
.getStartColor()));
3155 // really a gradient
3156 rTargetHolders
.Current().append(
3157 new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D(
3169 OSL_FAIL("Unknown MetaFile Action (!)");
3175 } // end of anonymous namespace
3177 //////////////////////////////////////////////////////////////////////////////
3179 namespace drawinglayer
3181 namespace primitive2d
3183 Primitive2DSequence
MetafilePrimitive2D::create2DDecomposition(const geometry::ViewInformation2D
& rViewInformation
) const
3185 // prepare target and porperties; each will have one default entry
3186 TargetHolders aTargetHolders
;
3187 PropertyHolders aPropertyHolders
;
3189 // set target MapUnit at Properties
3190 aPropertyHolders
.Current().setMapUnit(getMetaFile().GetPrefMapMode().GetMapUnit());
3192 // interpret the Metafile
3193 interpretMetafile(getMetaFile(), aTargetHolders
, aPropertyHolders
, rViewInformation
);
3195 // get the content. There should be only one target, as in the start condition,
3196 // but iterating will be the right thing to do when some push/pop is not closed
3197 Primitive2DSequence xRetval
;
3199 while(aTargetHolders
.size() > 1)
3201 appendPrimitive2DSequenceToPrimitive2DSequence(xRetval
,
3202 aTargetHolders
.Current().getPrimitive2DSequence(aPropertyHolders
.Current()));
3203 aTargetHolders
.Pop();
3206 appendPrimitive2DSequenceToPrimitive2DSequence(xRetval
,
3207 aTargetHolders
.Current().getPrimitive2DSequence(aPropertyHolders
.Current()));
3209 if(xRetval
.hasElements())
3212 const Rectangle
aMtfTarget(getMetaFile().GetPrefMapMode().GetOrigin(), getMetaFile().GetPrefSize());
3214 // create transformation
3215 basegfx::B2DHomMatrix aAdaptedTransform
;
3217 aAdaptedTransform
.translate(-aMtfTarget
.Left(), -aMtfTarget
.Top());
3218 aAdaptedTransform
.scale(
3219 aMtfTarget
.getWidth() ? 1.0 / aMtfTarget
.getWidth() : 1.0,
3220 aMtfTarget
.getHeight() ? 1.0 / aMtfTarget
.getHeight() : 1.0);
3221 aAdaptedTransform
= getTransform() * aAdaptedTransform
;
3223 // embed to target transformation
3224 const Primitive2DReference
aEmbeddedTransform(
3225 new TransformPrimitive2D(
3229 xRetval
= Primitive2DSequence(&aEmbeddedTransform
, 1);
3235 MetafilePrimitive2D::MetafilePrimitive2D(
3236 const basegfx::B2DHomMatrix
& rMetaFileTransform
,
3237 const GDIMetaFile
& rMetaFile
)
3238 : BufferedDecompositionPrimitive2D(),
3239 maMetaFileTransform(rMetaFileTransform
),
3240 maMetaFile(rMetaFile
)
3244 bool MetafilePrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
3246 if(BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
3248 const MetafilePrimitive2D
& rCompare
= (MetafilePrimitive2D
&)rPrimitive
;
3250 return (getTransform() == rCompare
.getTransform()
3251 && getMetaFile() == rCompare
.getMetaFile());
3257 basegfx::B2DRange
MetafilePrimitive2D::getB2DRange(const geometry::ViewInformation2D
& /*rViewInformation*/) const
3259 // use own implementation to quickly answer the getB2DRange question. The
3260 // MetafilePrimitive2D assumes that all geometry is inside of the shape. If
3261 // this is not the case (i have already seen some wrong Metafiles) it should
3262 // be embedded to a MaskPrimitive2D
3263 basegfx::B2DRange
aRetval(0.0, 0.0, 1.0, 1.0);
3264 aRetval
.transform(getTransform());
3269 // provide unique ID
3270 ImplPrimitive2DIDBlock(MetafilePrimitive2D
, PRIMITIVE2D_ID_METAFILEPRIMITIVE2D
)
3272 } // end of namespace primitive2d
3273 } // end of namespace drawinglayer
3275 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */