fdo#74697 Add Bluez 5 support for impress remote.
[LibreOffice.git] / drawinglayer / source / primitive2d / metafileprimitive2d.cxx
bloba7abeb66bc0baf1b3e29c287c758459a651a0ac5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
55 #include <numeric>
57 //////////////////////////////////////////////////////////////////////////////
59 using namespace com::sun::star;
61 //////////////////////////////////////////////////////////////////////////////
63 namespace
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.
71 class PropertyHolder
73 private:
74 /// current transformation (aka MapMode)
75 basegfx::B2DHomMatrix maTransformation;
76 MapUnit maMapUnit;
78 /// current colors
79 basegfx::BColor maLineColor;
80 basegfx::BColor maFillColor;
81 basegfx::BColor maTextColor;
82 basegfx::BColor maTextFillColor;
83 basegfx::BColor maTextLineColor;
84 basegfx::BColor maOverlineColor;
86 /// clipping
87 basegfx::B2DPolyPolygon maClipPolyPoygon;
89 /// font, etc.
90 Font maFont;
91 RasterOp maRasterOp;
92 sal_uInt32 mnLayoutMode;
93 LanguageType maLanguageType;
94 sal_uInt16 mnPushFlags;
96 /// bitfield
97 /// contains all active markers
98 bool mbLineColor : 1;
99 bool mbFillColor : 1;
100 bool mbTextColor : 1;
101 bool mbTextFillColor : 1;
102 bool mbTextLineColor : 1;
103 bool mbOverlineColor : 1;
104 bool mbClipPolyPolygonActive : 1;
106 public:
107 PropertyHolder()
108 : maTransformation(),
109 maMapUnit(MAP_100TH_MM),
110 maLineColor(),
111 maFillColor(),
112 maTextColor(COL_BLACK),
113 maTextFillColor(),
114 maTextLineColor(),
115 maOverlineColor(),
116 maClipPolyPoygon(),
117 maFont(),
118 maRasterOp(ROP_OVERPAINT),
119 mnLayoutMode(0),
120 maLanguageType(0),
121 mnPushFlags(0),
122 mbLineColor(false),
123 mbFillColor(false),
124 mbTextColor(true),
125 mbTextFillColor(false),
126 mbTextLineColor(false),
127 mbOverlineColor(false),
128 mbClipPolyPolygonActive(false)
132 ~PropertyHolder()
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 //////////////////////////////////////////////////////////////////////////////
202 namespace
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
215 private:
216 std::vector< PropertyHolder* > maPropertyHolders;
218 public:
219 PropertyHolders()
221 maPropertyHolders.push_back(new PropertyHolder());
224 sal_uInt32 size() const
226 return maPropertyHolders.size();
229 void PushDefault()
231 PropertyHolder* pNew = new PropertyHolder();
232 maPropertyHolders.push_back(pNew);
235 void Push(sal_uInt16 nPushFlags)
237 if(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);
249 void Pop()
251 OSL_ENSURE(maPropertyHolders.size(), "PropertyHolders: POP with no property holders (!)");
252 const sal_uInt32 nSize(maPropertyHolders.size());
254 if(nSize)
256 const PropertyHolder* pTip = maPropertyHolders.back();
257 const sal_uInt16 nPushFlags(pTip->getPushFlags());
259 if(nPushFlags)
261 if(nSize > 1)
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 ))
317 // not supported
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());
341 // execute the pop
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();
354 ~PropertyHolders()
356 while(!maPropertyHolders.empty())
358 delete maPropertyHolders.back();
359 maPropertyHolders.pop_back();
363 } // end of anonymous namespace
365 //////////////////////////////////////////////////////////////////////////////
367 namespace
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
374 PolyPolygon.
376 basegfx::B2DPolyPolygon getB2DPolyPolygonFromRegion(const Region& rRegion)
378 basegfx::B2DPolyPolygon aRetval;
380 if(!rRegion.IsEmpty())
382 Region aRegion(rRegion);
383 aRetval = aRegion.GetB2DPolyPolygon();
385 if(!aRetval.count())
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);
405 return aRetval;
407 } // end of anonymous namespace
409 //////////////////////////////////////////////////////////////////////////////
411 namespace
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
417 data.
419 class TargetHolder
421 private:
422 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aTargets;
424 public:
425 TargetHolder()
426 : aTargets()
430 ~TargetHolder()
432 const sal_uInt32 nCount(aTargets.size());
434 for(sal_uInt32 a(0); a < nCount; a++)
436 delete aTargets[a];
440 sal_uInt32 size() const
442 return aTargets.size();
445 void append(drawinglayer::primitive2d::BasePrimitive2D* pCandidate)
447 if(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.
467 aTargets.clear();
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(
477 rClipPolyPolygon,
478 xRetval));
480 xRetval = drawinglayer::primitive2d::Primitive2DSequence(&xMask, 1);
484 return xRetval;
487 } // end of anonymous namespace
489 //////////////////////////////////////////////////////////////////////////////
491 namespace
493 /** Helper class which builds a stack on the TargetHolder class */
494 class TargetHolders
496 private:
497 std::vector< TargetHolder* > maTargetHolders;
499 public:
500 TargetHolders()
502 maTargetHolders.push_back(new TargetHolder());
505 sal_uInt32 size() const
507 return maTargetHolders.size();
510 void Push()
512 maTargetHolders.push_back(new TargetHolder());
515 void Pop()
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();
532 ~TargetHolders()
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
561 protected:
562 /// local decomposition.
563 virtual Primitive2DSequence create2DDecomposition(
564 const geometry::ViewInformation2D& rViewInformation) const;
566 public:
567 /// constructor
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);
583 else
585 return Primitive2DSequence();
588 } // end of namespace primitive2d
589 } // end of namespace drawinglayer
591 //////////////////////////////////////////////////////////////////////////////
593 namespace
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)
609 aMapping.scale(
610 double(rMapMode.GetScaleX()),
611 double(rMapMode.GetScaleY()));
614 return aMapping;
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())
628 rTarget.append(
629 new drawinglayer::primitive2d::PointArrayPrimitive2D(
630 rPositions,
631 aBColor));
633 else
635 std::vector< basegfx::B2DPoint > aPositions(rPositions);
637 for(sal_uInt32 a(0); a < aPositions.size(); a++)
639 aPositions[a] = rProperties.getTransformation() * aPositions[a];
642 rTarget.append(
643 new drawinglayer::primitive2d::PointArrayPrimitive2D(
644 aPositions,
645 aBColor));
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());
660 rTarget.append(
661 new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
662 aLinePolygon,
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());
677 rTarget.append(
678 new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
679 aFillPolyPolygon,
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());
706 if(bDashDotUsed)
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(
727 fDotDashArray,
728 fAccumulated);
730 rTarget.append(
731 new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
732 aLinePolygon,
733 aLineAttribute,
734 aStrokeAttribute));
736 else
738 rTarget.append(
739 new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
740 aLinePolygon,
741 aLineAttribute));
744 else
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,
796 const Point& rPoint,
797 TargetHolder& rTarget,
798 PropertyHolder& rProperties)
800 if(!rBitmapEx.IsEmpty())
802 basegfx::B2DPoint aPoint(rPoint.X(), rPoint.Y());
803 aPoint = rProperties.getTransformation() * aPoint;
805 rTarget.append(
806 new drawinglayer::primitive2d::DiscreteBitmapPrimitive2D(
807 rBitmapEx,
808 aPoint));
812 /** helper to create BitmapPrimitive2D based on current context */
813 void createBitmapExPrimitive(
814 const BitmapEx& rBitmapEx,
815 const Point& rPoint,
816 const Size& rSize,
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;
831 rTarget.append(
832 new drawinglayer::primitive2d::BitmapPrimitive2D(
833 rBitmapEx,
834 aObjectTransform));
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
840 transparent)
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;
890 break;
892 case GradientStyle_AXIAL :
894 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_AXIAL;
895 break;
897 case GradientStyle_RADIAL :
899 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_RADIAL;
900 break;
902 case GradientStyle_ELLIPTICAL :
904 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_ELLIPTICAL;
905 break;
907 case GradientStyle_SQUARE :
909 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_SQUARE;
910 break;
912 default : // GradientStyle_RECT
914 aGradientStyle = drawinglayer::attribute::GRADIENTSTYLE_RECT;
915 break;
919 return drawinglayer::attribute::FillGradientAttribute(
920 aGradientStyle,
921 (double)rGradient.GetBorder() * 0.01,
922 (double)rGradient.GetOfsX() * 0.01,
923 (double)rGradient.GetOfsY() * 0.01,
924 (double)rGradient.GetAngle() * F_PI1800,
925 aStart,
926 aEnd,
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;
942 break;
944 case HATCH_DOUBLE :
946 aHatchStyle = drawinglayer::attribute::HATCHSTYLE_DOUBLE;
947 break;
949 case HATCH_TRIPLE :
951 aHatchStyle = drawinglayer::attribute::HATCHSTYLE_TRIPLE;
952 break;
956 return drawinglayer::attribute::FillHatchAttribute(
957 aHatchStyle,
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
962 false);
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
994 return;
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
1003 return;
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(
1030 aSubContent));
1034 // apply new settings to current properties by setting
1035 // the new region now
1036 rPropertyHolders.Current().setClipPolyPolygonActive(bNewActive);
1038 if(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(
1054 RasterOp aRasterOp,
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(
1077 aSubContent,
1078 basegfx::BColorModifier(basegfx::BColor(0.0, 0.0, 0.0))));
1080 else // if(rPropertyHolders.Current().isRasterOpInvert())
1082 // invert content
1083 rTargetHolders.Current().append(
1084 new drawinglayer::primitive2d::InvertPrimitive2D(
1085 aSubContent));
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),
1114 rColor);
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);
1132 else
1134 // really a gradient
1135 drawinglayer::primitive2d::BasePrimitive2D* pRetval =
1136 new drawinglayer::primitive2d::FillGradientPrimitive2D(
1137 rRange,
1138 aAttribute);
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(),
1147 xSeq);
1150 return pRetval;
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())
1176 rTarget.append(
1177 CreateGradientWallpaper(
1178 aWallpaperRange,
1179 rWallpaper.GetGradient(),
1180 rProperty));
1182 else if(!rWallpaper.GetColor().GetTransparency())
1184 rTarget.append(
1185 CreateColorWallpaper(
1186 aWallpaperRange,
1187 rWallpaper.GetColor().getBColor(),
1188 rProperty));
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(
1202 aWallpaperRange,
1203 aBitmapEx,
1204 eWallpaperStyle);
1206 if(rProperty.getTransformation().isIdentity())
1208 // add directly
1209 rTarget.append(pBitmapWallpaperFill);
1211 else
1213 // when a transformation is set, embed to it
1214 const drawinglayer::primitive2d::Primitive2DReference xPrim(pBitmapWallpaperFill);
1216 rTarget.append(
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())
1228 return false;
1231 if((LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()))
1233 // the underline is right for Japanese only
1234 return true;
1237 return false;
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(
1251 aFontScaling,
1252 rFont,
1253 0 != (rProperty.getLayoutMode() & TEXT_LAYOUT_BIDI_RTL),
1254 0 != (rProperty.getLayoutMode() & TEXT_LAYOUT_BIDI_STRONG)));
1256 // add FontScaling
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);
1300 if(nTextLength)
1302 drawinglayer::attribute::FontAttribute aFontAttribute;
1303 basegfx::B2DHomMatrix aTextTransform;
1305 // fill parameters derived from current font
1306 createFontAttributeTransformAndAlignment(
1307 aFontAttribute,
1308 aTextTransform,
1309 aAlignmentOffset,
1310 rProperty);
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()
1326 || rFont.IsShadow()
1327 || bWordLineMode);
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
1370 aTextTransform,
1371 rText,
1372 nTextStart,
1373 nTextLength,
1374 rDXArray,
1375 aFontAttribute,
1376 aLocale,
1377 aFontColor,
1379 // attributes for TextDecoratedPortionPrimitive2D
1380 rProperty.getOverlineColorActive() ? rProperty.getOverlineColor() : aFontColor,
1381 rProperty.getTextLineColorActive() ? rProperty.getTextLineColor() : aFontColor,
1382 eFontOverline,
1383 eFontUnderline,
1384 bUnderlineAbove,
1385 eTextStrikeout,
1386 bWordLineMode,
1387 eTextEmphasisMark,
1388 bEmphasisMarkAbove,
1389 bEmphasisMarkBelow,
1390 eTextRelief,
1391 bShadow);
1393 else
1395 // TextSimplePortionPrimitive2D is enough
1396 pResult = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
1397 aTextTransform,
1398 rText,
1399 nTextStart,
1400 nTextLength,
1401 rDXArray,
1402 aFontAttribute,
1403 aLocale,
1404 aFontColor);
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);
1414 // get text width
1415 double fTextWidth(0.0);
1417 if(rDXArray.empty())
1419 fTextWidth = aTextLayouterDevice.getTextWidth(rText, nTextStart, nTextLength);
1421 else
1423 fTextWidth = rDXArray.back();
1426 if(basegfx::fTools::more(fTextWidth, 0.0))
1428 // build text range
1429 const basegfx::B2DRange aTextRange(
1430 0.0, -aTextLayouterDevice.getFontAscent(),
1431 fTextWidth, aTextLayouterDevice.getFontDescent());
1433 // create Transform
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);
1463 if(pResult)
1465 // add created text primitive to target
1466 if(rProperty.getTransformation().isIdentity())
1468 rTarget.append(pResult);
1470 else
1472 // when a transformation is set, embed to it
1473 const drawinglayer::primitive2d::Primitive2DReference aReference(pResult);
1475 rTarget.append(
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(
1510 aFontAttribute,
1511 aTextTransform,
1512 aAlignmentOffset,
1513 rProperty);
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());
1522 if(bOverlineUsed)
1524 // create primitive geometry for overline
1525 aTargetVector.push_back(
1526 new drawinglayer::primitive2d::TextLinePrimitive2D(
1527 aTextTransform,
1528 fLineWidth,
1529 aTextLayouter.getOverlineOffset(),
1530 aTextLayouter.getOverlineHeight(),
1531 aOverlineMode,
1532 rProperty.getOverlineColor()));
1535 if(bUnderlineUsed)
1537 // create primitive geometry for underline
1538 aTargetVector.push_back(
1539 new drawinglayer::primitive2d::TextLinePrimitive2D(
1540 aTextTransform,
1541 fLineWidth,
1542 aTextLayouter.getUnderlineOffset(),
1543 aTextLayouter.getUnderlineHeight(),
1544 aUnderlineMode,
1545 rProperty.getTextLineColor()));
1548 if(bStrikeoutUsed)
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(
1562 aTextTransform,
1563 fLineWidth,
1564 rProperty.getTextColor(),
1565 aStrikeoutChar,
1566 aFontAttribute,
1567 aLocale));
1569 else
1571 // strikeout with geometry
1572 aTargetVector.push_back(
1573 new drawinglayer::primitive2d::TextGeometryStrikeoutPrimitive2D(
1574 aTextTransform,
1575 fLineWidth,
1576 rProperty.getTextColor(),
1577 aTextLayouter.getUnderlineHeight(),
1578 aTextLayouter.getStrikeoutOffset(),
1579 aTextStrikeout));
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]);
1593 else
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]);
1603 rTarget.append(
1604 new drawinglayer::primitive2d::TransformPrimitive2D(
1605 rProperty.getTransformation(),
1606 xTargets));
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:
1636 SIMPLE, DONE:
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
1646 bugdocs)
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 :
1666 /** SIMPLE, DONE */
1667 break;
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());
1684 aPositions.clear();
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);
1695 nAction--;
1697 if(!aPositions.empty())
1699 createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), aLastColor.getBColor());
1702 break;
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);
1719 nAction--;
1721 if(!aPositions.empty())
1723 createPointArrayPrimitive(aPositions, rTargetHolders.Current(), rPropertyHolders.Current(), rPropertyHolders.Current().getLineColor());
1727 break;
1729 case META_LINE_ACTION :
1731 /** CHECKED, WORKS WELL */
1732 if(rPropertyHolders.Current().getLineColorActive())
1734 basegfx::B2DPolygon aLinePolygon;
1735 LineInfo aLineInfo;
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);
1752 else
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);
1762 else
1764 aLineInfo = pA->GetLineInfo();
1765 aLinePolygon.append(aStart);
1766 aLinePolygon.append(aEnd);
1769 nAction++; if(nAction < nCount) pAction = rMetaFile.GetAction(nAction);
1772 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());
1781 break;
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());
1803 break;
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;
1828 if(nHor || nVer)
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);
1837 else
1839 aOutline = basegfx::tools::createPolygonFromRect(aRange);
1842 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
1847 break;
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());
1871 break;
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());
1885 break;
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());
1899 break;
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());
1913 break;
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());
1924 break;
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());
1945 break;
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());
1972 break;
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(
1991 pA->GetPoint(),
1992 pA->GetText(),
1993 nTextIndex,
1994 nTextLength,
1995 aDXArray,
1996 rTargetHolders.Current(),
1997 rPropertyHolders.Current());
2000 break;
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();
2021 if(pDXArray)
2023 aDXArray.reserve(nTextLength);
2025 for(sal_uInt32 a(0); a < nTextLength; a++)
2027 aDXArray.push_back((double)(*(pDXArray + a)));
2031 processMetaTextAction(
2032 pA->GetPoint(),
2033 pA->GetText(),
2034 nTextIndex,
2035 nTextLength,
2036 aDXArray,
2037 rTargetHolders.Current(),
2038 rPropertyHolders.Current());
2041 break;
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.
2050 // New status:
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(
2070 pA->GetText(),
2071 nTextIndex,
2072 nTextLength));
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(
2095 pA->GetPoint(),
2096 pA->GetText(),
2097 nTextIndex,
2098 nTextLength,
2099 aTextArray,
2100 rTargetHolders.Current(),
2101 rPropertyHolders.Current());
2104 break;
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
2123 // VCL AFAP.
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(),
2154 xSubContent));
2159 break;
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());
2169 break;
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());
2179 break;
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());
2201 break;
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());
2211 break;
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());
2221 break;
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());
2242 break;
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());
2252 break;
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());
2262 break;
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());
2284 break;
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(
2306 aOutline,
2307 rTargetHolders.Current(),
2308 rPropertyHolders.Current());
2310 else
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(
2323 aRange,
2324 aAttribute));
2326 else
2328 xGradient[0] = drawinglayer::primitive2d::Primitive2DReference(
2329 new drawinglayer::primitive2d::FillGradientPrimitive2D(
2330 aRange,
2331 aAttribute));
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(
2340 aOutline,
2341 xGradient));
2346 break;
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(
2364 aObjectRange,
2365 basegfx::BColor(),
2366 aAttribute));
2368 rTargetHolders.Current().append(
2369 new drawinglayer::primitive2d::MaskPrimitive2D(
2370 aOutline,
2371 drawinglayer::primitive2d::Primitive2DSequence(&aFillHatch, 1)));
2374 break;
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(
2398 aWallpaperRange,
2399 rWallpaper,
2400 rTargetHolders.Current(),
2401 rPropertyHolders.Current());
2403 else if(rWallpaper.IsGradient())
2405 // create gradient background
2406 rTargetHolders.Current().append(
2407 CreateGradientWallpaper(
2408 aWallpaperRange,
2409 rWallpaper.GetGradient(),
2410 rPropertyHolders.Current()));
2412 else if(!rWallpaper.GetColor().GetTransparency())
2414 // create color background
2415 rTargetHolders.Current().append(
2416 CreateColorWallpaper(
2417 aWallpaperRange,
2418 rWallpaper.GetColor().getBColor(),
2419 rPropertyHolders.Current()));
2424 break;
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);
2439 else
2441 // end clipping
2442 const basegfx::B2DPolyPolygon aEmptyPolyPolygon;
2444 HandleNewClipRegion(aEmptyPolyPolygon, rTargetHolders, rPropertyHolders);
2447 break;
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);
2463 else
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
2479 else
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,
2490 aClipRange,
2491 true,
2492 false);
2495 if(aClippedPolyPolygon != aOriginalPolyPolygon)
2497 // start new clipping with intersected region
2498 HandleNewClipRegion(
2499 aClippedPolyPolygon,
2500 rTargetHolders,
2501 rPropertyHolders);
2505 else
2507 // start new clipping with ClipRange
2508 const basegfx::B2DPolyPolygon aNewClipPolyPolygon(
2509 basegfx::tools::createPolygonFromRect(aClipRange));
2511 HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
2515 break;
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);
2531 else
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
2544 else
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);
2564 else
2566 // start new clipping with new ClipPolyPolygon
2567 HandleNewClipRegion(aNewClipPolyPolygon, rTargetHolders, rPropertyHolders);
2571 break;
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())
2582 // nothing to do
2584 else
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);
2607 break;
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);
2616 if(bActive)
2617 rPropertyHolders.Current().setLineColor(pA->GetColor().getBColor());
2619 break;
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);
2628 if(bActive)
2629 rPropertyHolders.Current().setFillColor(pA->GetColor().getBColor());
2631 break;
2633 case META_TEXTCOLOR_ACTION :
2635 /** SIMPLE, DONE */
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());
2642 break;
2644 case META_TEXTFILLCOLOR_ACTION :
2646 /** SIMPLE, DONE */
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());
2657 else
2659 // emulate SetFillColor() <- NO argument (!)
2660 rPropertyHolders.Current().setTextFillColorActive(false);
2663 break;
2665 case META_TEXTALIGN_ACTION :
2667 /** SIMPLE, DONE */
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);
2681 break;
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);
2697 else
2699 switch(rMapMode.GetMapUnit())
2701 case MAP_100TH_MM :
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);
2709 break;
2711 case MAP_TWIP :
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);
2719 break;
2721 default :
2723 OSL_FAIL("interpretMetafile: META_MAPMODE_ACTION with unsupported MapUnit (!)");
2724 break;
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);
2738 break;
2740 case META_FONT_ACTION :
2742 /** SIMPLE, DONE */
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());
2769 if(bActivate)
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
2780 if(bActivate)
2782 const Color& rFontFillColor = pA->GetFont().GetFillColor();
2783 rPropertyHolders.Current().setTextFillColor(rFontFillColor.getBColor());
2784 rPropertyHolders.Current().setTextFillColorActive(COL_TRANSPARENT != rFontFillColor.GetColor());
2786 else
2788 rPropertyHolders.Current().setTextFillColorActive(false);
2791 break;
2793 case META_PUSH_ACTION :
2795 /** CHECKED, WORKS WELL */
2796 const MetaPushAction* pA = (const MetaPushAction*)pAction;
2797 rPropertyHolders.Push(pA->GetFlags());
2799 break;
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);
2836 break;
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);
2846 break;
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)
2860 // not transparent
2861 createHairlineAndFillPrimitive(aOutline, rTargetHolders.Current(), rPropertyHolders.Current());
2863 else if(nTransparence >= 100)
2865 // fully or more than transparent
2867 else
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(
2884 aSubContent,
2885 nTransparence * 0.01));
2890 break;
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(
2918 aObjectTransform,
2919 pA->GetLink(),
2920 pA->GetSubstitute()));
2923 break;
2925 case META_REFPOINT_ACTION :
2927 /** SIMPLE, DONE */
2928 // only used for hatch and line pattern offsets, pretty much no longer
2929 // supported today
2930 // const MetaRefPointAction* pA = (const MetaRefPointAction*)pAction;
2931 break;
2933 case META_TEXTLINECOLOR_ACTION :
2935 /** SIMPLE, DONE */
2936 const MetaTextLineColorAction* pA = (const MetaTextLineColorAction*)pAction;
2937 const bool bActive(pA->IsSetting());
2939 rPropertyHolders.Current().setTextLineColorActive(bActive);
2940 if(bActive)
2941 rPropertyHolders.Current().setTextLineColor(pA->GetColor().getBColor());
2943 break;
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
2954 // Font.
2955 const MetaTextLineAction* pA = (const MetaTextLineAction*)pAction;
2957 proccessMetaTextLineAction(
2958 *pA,
2959 rTargetHolders.Current(),
2960 rPropertyHolders.Current());
2962 break;
2964 case META_FLOATTRANSPARENT_ACTION :
2966 /** CHECKED, WORKS WELL */
2967 const MetaFloatTransparentAction* pA = (const MetaFloatTransparentAction*)pAction;
2968 const basegfx::B2DRange aTargetRange(
2969 pA->GetPoint().X(),
2970 pA->GetPoint().Y(),
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());
3008 aTransform.scale(
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(
3015 aTransform,
3016 xSubContent));
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(
3030 xSubContent,
3031 aAttribute.getStartColor().luminance()));
3033 else
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(
3042 aRange,
3043 aAttribute));
3045 // create transparence primitive
3046 rTargetHolders.Current().append(
3047 new drawinglayer::primitive2d::TransparencePrimitive2D(
3048 xSubContent,
3049 drawinglayer::primitive2d::Primitive2DSequence(&xTransparence, 1)));
3055 break;
3057 case META_GRADIENTEX_ACTION :
3059 /** SIMPLE, DONE */
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;
3063 break;
3065 case META_LAYOUTMODE_ACTION :
3067 /** SIMPLE, DONE */
3068 const MetaLayoutModeAction* pA = (const MetaLayoutModeAction*)pAction;
3069 rPropertyHolders.Current().setLayoutMode(pA->GetLayoutMode());
3070 break;
3072 case META_TEXTLANGUAGE_ACTION :
3074 /** SIMPLE, DONE */
3075 const MetaTextLanguageAction* pA = (const MetaTextLanguageAction*)pAction;
3076 rPropertyHolders.Current().setLanguageType(pA->GetTextLanguage());
3077 break;
3079 case META_OVERLINECOLOR_ACTION :
3081 /** SIMPLE, DONE */
3082 const MetaOverlineColorAction* pA = (const MetaOverlineColorAction*)pAction;
3083 const bool bActive(pA->IsSetting());
3085 rPropertyHolders.Current().setOverlineColorActive(bActive);
3086 if(bActive)
3087 rPropertyHolders.Current().setOverlineColor(pA->GetColor().getBColor());
3089 break;
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;
3108 bool bDone(false);
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")))
3123 bDone = true;
3128 if(bDone && pMetaGradientExAction)
3130 // consume actions and skip forward
3131 nAction = b - 1;
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(
3150 aPolyPolygon,
3151 aAttribute.getStartColor()));
3153 else
3155 // really a gradient
3156 rTargetHolders.Current().append(
3157 new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D(
3158 aPolyPolygon,
3159 aAttribute));
3165 break;
3167 default:
3169 OSL_FAIL("Unknown MetaFile Action (!)");
3170 break;
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())
3211 // get target size
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(
3226 aAdaptedTransform,
3227 xRetval));
3229 xRetval = Primitive2DSequence(&aEmbeddedTransform, 1);
3232 return xRetval;
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());
3254 return false;
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());
3266 return aRetval;
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: */