1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sdr/primitive2d/sdrdecompositiontools.hxx>
21 #include <drawinglayer/primitive2d/baseprimitive2d.hxx>
22 #include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
23 #include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
24 #include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx>
25 #include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
26 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
27 #include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
28 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
29 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
30 #include <basegfx/polygon/b2dpolypolygontools.hxx>
31 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
32 #include <drawinglayer/attribute/strokeattribute.hxx>
33 #include <drawinglayer/attribute/linestartendattribute.hxx>
34 #include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
35 #include <basegfx/matrix/b2dhommatrix.hxx>
36 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
37 #include <sdr/attribute/sdrtextattribute.hxx>
38 #include <drawinglayer/primitive2d/glowprimitive2d.hxx>
39 #include <sdr/primitive2d/sdrtextprimitive2d.hxx>
40 #include <svx/svdotext.hxx>
41 #include <basegfx/polygon/b2dpolygontools.hxx>
42 #include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
43 #include <drawinglayer/animation/animationtiming.hxx>
44 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
45 #include <drawinglayer/geometry/viewinformation2d.hxx>
46 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
47 #include <drawinglayer/attribute/sdrfillattribute.hxx>
48 #include <drawinglayer/attribute/sdrlineattribute.hxx>
49 #include <drawinglayer/attribute/sdrlinestartendattribute.hxx>
50 #include <drawinglayer/attribute/sdrshadowattribute.hxx>
51 #include <drawinglayer/attribute/sdrglowattribute.hxx>
52 #include <docmodel/theme/FormatScheme.hxx>
53 #include <osl/diagnose.h>
55 // for SlideBackgroundFillPrimitive2D
56 #include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
57 #include <svx/unoapi.hxx>
58 #include <svx/svdpage.hxx>
59 #include <sdr/primitive2d/sdrattributecreator.hxx>
60 #include <sdr/contact/viewcontactofmasterpagedescriptor.hxx>
62 using namespace com::sun::star
;
65 namespace drawinglayer::primitive2d
69 /// @returns the offset to apply/unapply to scale according to correct origin for a given alignment.
70 basegfx::B2DTuple
getShadowScaleOriginOffset(const basegfx::B2DTuple
& aScale
,
71 model::RectangleAlignment eAlignment
)
75 case model::RectangleAlignment::TopLeft
:
77 case model::RectangleAlignment::Top
:
78 return { aScale
.getX() / 2, 0 };
79 case model::RectangleAlignment::TopRight
:
80 return { aScale
.getX(), 0 };
81 case model::RectangleAlignment::Left
:
82 return { 0, aScale
.getY() / 2 };
83 case model::RectangleAlignment::Center
:
84 return { aScale
.getX() / 2, aScale
.getY() / 2 };
85 case model::RectangleAlignment::Right
:
86 return { aScale
.getX(), aScale
.getY() / 2 };
87 case model::RectangleAlignment::BottomLeft
:
88 return { 0, aScale
.getY() };
89 case model::RectangleAlignment::Bottom
:
90 return { aScale
.getX() / 2, aScale
.getY() };
91 case model::RectangleAlignment::BottomRight
:
92 return { aScale
.getX(), aScale
.getY() };
98 // See also: SdrTextObj::AdjustRectToTextDistance
99 basegfx::B2DRange
getTextAnchorRange(const attribute::SdrTextAttribute
& rText
,
100 const basegfx::B2DRange
& rSnapRange
)
102 // Take vertical text orientation into account when deciding
103 // which dimension is its width, and which is its height
104 const OutlinerParaObject
& rOutlinerParaObj
= rText
.getOutlinerParaObject();
105 const bool bVerticalWriting(rOutlinerParaObj
.IsEffectivelyVertical());
106 const double fWidthForText
= bVerticalWriting
? rSnapRange
.getHeight() : rSnapRange
.getWidth();
107 // create a range describing the wanted text position and size (aTextAnchorRange). This
108 // means to use the text distance values here
109 // If the margin is larger than the entire width of the text area, then limit the
111 const double fTextLeftDistance
112 = std::min(static_cast<double>(rText
.getTextLeftDistance()), fWidthForText
);
113 const double nTextRightDistance
114 = std::min(static_cast<double>(rText
.getTextRightDistance()), fWidthForText
);
115 double fDistanceForTextL
, fDistanceForTextT
, fDistanceForTextR
, fDistanceForTextB
;
116 if (!bVerticalWriting
)
118 fDistanceForTextL
= fTextLeftDistance
;
119 fDistanceForTextT
= rText
.getTextUpperDistance();
120 fDistanceForTextR
= nTextRightDistance
;
121 fDistanceForTextB
= rText
.getTextLowerDistance();
123 else if (rOutlinerParaObj
.IsTopToBottom())
125 fDistanceForTextL
= rText
.getTextLowerDistance();
126 fDistanceForTextT
= fTextLeftDistance
;
127 fDistanceForTextR
= rText
.getTextUpperDistance();
128 fDistanceForTextB
= nTextRightDistance
;
132 fDistanceForTextL
= rText
.getTextUpperDistance();
133 fDistanceForTextT
= nTextRightDistance
;
134 fDistanceForTextR
= rText
.getTextLowerDistance();
135 fDistanceForTextB
= fTextLeftDistance
;
137 const basegfx::B2DPoint
aTopLeft(rSnapRange
.getMinX() + fDistanceForTextL
,
138 rSnapRange
.getMinY() + fDistanceForTextT
);
139 const basegfx::B2DPoint
aBottomRight(rSnapRange
.getMaxX() - fDistanceForTextR
,
140 rSnapRange
.getMaxY() - fDistanceForTextB
);
141 basegfx::B2DRange aAnchorRange
;
142 aAnchorRange
.expand(aTopLeft
);
143 aAnchorRange
.expand(aBottomRight
);
145 // If the shape has no width, then don't attempt to break the text into multiple
146 // lines, not a single character would satisfy a zero width requirement.
147 // SdrTextObj::impDecomposeBlockTextPrimitive() uses the same constant to
148 // effectively set no limits.
149 if (!bVerticalWriting
&& aAnchorRange
.getWidth() == 0)
151 aAnchorRange
.expand(basegfx::B2DPoint(aTopLeft
.getX() - 1000000, aTopLeft
.getY()));
152 aAnchorRange
.expand(basegfx::B2DPoint(aBottomRight
.getX() + 1000000, aBottomRight
.getY()));
154 else if (bVerticalWriting
&& aAnchorRange
.getHeight() == 0)
156 aAnchorRange
.expand(basegfx::B2DPoint(aTopLeft
.getX(), aTopLeft
.getY() - 1000000));
157 aAnchorRange
.expand(basegfx::B2DPoint(aBottomRight
.getX(), aBottomRight
.getY() + 1000000));
162 drawinglayer::attribute::SdrFillAttribute
getMasterPageFillAttribute(
163 const geometry::ViewInformation2D
& rViewInformation
,
164 basegfx::B2DVector
& rPageSize
)
166 drawinglayer::attribute::SdrFillAttribute aRetval
;
169 const SdrPage
* pVisualizedPage(GetSdrPageFromXDrawPage(rViewInformation
.getVisualizedPage()));
171 if(nullptr != pVisualizedPage
)
173 // copy needed values for further processing
174 rPageSize
.setX(pVisualizedPage
->GetWidth());
175 rPageSize
.setY(pVisualizedPage
->GetHeight());
177 if(pVisualizedPage
->IsMasterPage())
179 // the page is a MasterPage, so we are in MasterPage view
180 // still need #i110846#, see ViewContactOfMasterPage
181 if(pVisualizedPage
->getSdrPageProperties().GetStyleSheet())
183 // create page fill attributes with correct properties
184 aRetval
= drawinglayer::primitive2d::createNewSdrFillAttribute(
185 pVisualizedPage
->getSdrPageProperties().GetItemSet());
190 // the page is *no* MasterPage, we are in normal Page view, get the MasterPage
191 if(pVisualizedPage
->TRG_HasMasterPage())
193 sdr::contact::ViewContact
& rVC(pVisualizedPage
->TRG_GetMasterPageDescriptorViewContact());
194 sdr::contact::ViewContactOfMasterPageDescriptor
* pVCOMPD(
195 dynamic_cast<sdr::contact::ViewContactOfMasterPageDescriptor
*>(&rVC
));
197 if(nullptr != pVCOMPD
)
199 // in this case the still needed #i110846# is part of
200 // getCorrectSdrPageProperties, that's the main reason to re-use
201 // that call/functionality here
202 const SdrPageProperties
* pCorrectProperties(
203 pVCOMPD
->GetMasterPageDescriptor().getCorrectSdrPageProperties());
205 if(pCorrectProperties
)
207 // create page fill attributes when correct properties were identified
208 aRetval
= drawinglayer::primitive2d::createNewSdrFillAttribute(
209 pCorrectProperties
->GetItemSet());
219 // provide a Primitive2D for the SlideBackgroundFill-mode. It is capable
220 // of expressing that state of fill and it's decomposition implements all
221 // needed preparation of the geometry in an isolated and controllable
223 // It is currently simple buffered (due to being derived from
224 // BufferedDecompositionPrimitive2D) and detects if FillStyle changes
225 class SlideBackgroundFillPrimitive2D final
: public BufferedDecompositionPrimitive2D
228 /// the basegfx::B2DPolyPolygon geometry
229 basegfx::B2DPolyPolygon maPolyPolygon
;
231 /// the last SdrFillAttribute the geometry was created for
232 drawinglayer::attribute::SdrFillAttribute maLastFill
;
235 // create decomposition data
236 virtual void create2DDecomposition(
237 Primitive2DContainer
& rContainer
,
238 const geometry::ViewInformation2D
& rViewInformation
) const override
;
242 SlideBackgroundFillPrimitive2D(
243 const basegfx::B2DPolyPolygon
& rPolyPolygon
);
245 /// check existing decomposition data, call parent
246 virtual void get2DDecomposition(
247 Primitive2DDecompositionVisitor
& rVisitor
,
248 const geometry::ViewInformation2D
& rViewInformation
) const override
;
251 const basegfx::B2DPolyPolygon
& getB2DPolyPolygon() const { return maPolyPolygon
; }
254 virtual bool operator==(const BasePrimitive2D
& rPrimitive
) const override
;
257 virtual basegfx::B2DRange
getB2DRange(const geometry::ViewInformation2D
& rViewInformation
) const override
;
259 /// provide unique ID
260 virtual sal_uInt32
getPrimitive2DID() const override
;
263 SlideBackgroundFillPrimitive2D::SlideBackgroundFillPrimitive2D(
264 const basegfx::B2DPolyPolygon
& rPolyPolygon
)
265 : BufferedDecompositionPrimitive2D()
266 , maPolyPolygon(rPolyPolygon
)
271 void SlideBackgroundFillPrimitive2D::create2DDecomposition(
272 Primitive2DContainer
& rContainer
,
273 const geometry::ViewInformation2D
& rViewInformation
) const
275 basegfx::B2DVector aPageSize
;
277 // get fill from target Page, this will check for all needed things
278 // like MasterPage/relationships, etc. (see getMasterPageFillAttribute impl above)
279 drawinglayer::attribute::SdrFillAttribute
aFill(
280 getMasterPageFillAttribute(rViewInformation
, aPageSize
));
282 // if fill is on default (empty), nothing will be shown, we are done
283 if(aFill
.isDefault())
286 // Get PolygonRange of own local geometry
287 const basegfx::B2DRange
aPolygonRange(getB2DPolyPolygon().getB2DRange());
289 // if local geometry is empty, nothing will be shown, we are done
290 if(aPolygonRange
.isEmpty())
294 const basegfx::B2DRange
aPageRange(0.0, 0.0, aPageSize
.getX(), aPageSize
.getY());
296 // if local geometry does not overlap with PageRange, nothing will be shown, we are done
297 if(!aPageRange
.overlaps(aPolygonRange
))
300 // create FillPrimitive2D with the geometry (the PolyPolygon) and
301 // the page's definitonRange to:
302 // - on one hand limit to geometry
303 // - on the other hand allow continuation of fill outside of
304 // MasterPage's range
305 const attribute::FillGradientAttribute aEmptyFillTransparenceGradient
;
306 const Primitive2DReference
aCreatedFill(
307 createPolyPolygonFillPrimitive(
308 getB2DPolyPolygon(), // geometry
309 aPageRange
, // definition range
311 aEmptyFillTransparenceGradient
));
313 rContainer
= Primitive2DContainer
{ aCreatedFill
};
316 void SlideBackgroundFillPrimitive2D::get2DDecomposition(
317 Primitive2DDecompositionVisitor
& rVisitor
,
318 const geometry::ViewInformation2D
& rViewInformation
) const
320 basegfx::B2DVector aPageSize
;
321 drawinglayer::attribute::SdrFillAttribute aFill
;
323 if(!getBuffered2DDecomposition().empty())
325 aFill
= getMasterPageFillAttribute(rViewInformation
, aPageSize
);
327 if(!(aFill
== maLastFill
))
329 // conditions of last local decomposition have changed, delete
330 const_cast< SlideBackgroundFillPrimitive2D
* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
334 if(getBuffered2DDecomposition().empty())
336 // remember last Fill
337 const_cast< SlideBackgroundFillPrimitive2D
* >(this)->maLastFill
= aFill
;
340 // use parent implementation
341 BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor
, rViewInformation
);
344 bool SlideBackgroundFillPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
346 if (BufferedDecompositionPrimitive2D::operator==(rPrimitive
))
348 const SlideBackgroundFillPrimitive2D
& rCompare
349 = static_cast<const SlideBackgroundFillPrimitive2D
&>(rPrimitive
);
351 return getB2DPolyPolygon() == rCompare
.getB2DPolyPolygon();
357 basegfx::B2DRange
SlideBackgroundFillPrimitive2D::getB2DRange(
358 const geometry::ViewInformation2D
& /*rViewInformation*/) const
361 return basegfx::utils::getRange(getB2DPolyPolygon());
365 sal_uInt32
SlideBackgroundFillPrimitive2D::getPrimitive2DID() const
367 return PRIMITIVE2D_ID_SLIDEBACKGROUNDFILLPRIMITIVE2D
;
370 }; // end of anonymous namespace
372 class TransparencePrimitive2D
;
374 Primitive2DReference
createPolyPolygonFillPrimitive(
375 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
376 const attribute::SdrFillAttribute
& rFill
,
377 const attribute::FillGradientAttribute
& rFillGradient
)
379 // when we have no given definition range, use the range of the given geometry
380 // also for definition (simplest case)
381 const basegfx::B2DRange
aRange(basegfx::utils::getRange(rPolyPolygon
));
383 return createPolyPolygonFillPrimitive(
390 Primitive2DReference
createPolyPolygonFillPrimitive(
391 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
392 const basegfx::B2DRange
& rDefinitionRange
,
393 const attribute::SdrFillAttribute
& rFill
,
394 const attribute::FillGradientAttribute
& rFillGradient
)
396 if(basegfx::fTools::moreOrEqual(rFill
.getTransparence(), 1.0))
398 return Primitive2DReference();
401 // prepare fully scaled polygon
402 rtl::Reference
<BasePrimitive2D
> pNewFillPrimitive
;
404 if(!rFill
.getGradient().isDefault())
406 pNewFillPrimitive
= new PolyPolygonGradientPrimitive2D(
409 rFill
.getGradient());
411 else if(!rFill
.getHatch().isDefault())
413 pNewFillPrimitive
= new PolyPolygonHatchPrimitive2D(
419 else if(!rFill
.getFillGraphic().isDefault())
421 pNewFillPrimitive
= new PolyPolygonGraphicPrimitive2D(
424 rFill
.getFillGraphic().createFillGraphicAttribute(rDefinitionRange
));
426 else if(rFill
.isSlideBackgroundFill())
428 // create needed Primitive2D representation for
429 // SlideBackgroundFill-mode
430 pNewFillPrimitive
= new SlideBackgroundFillPrimitive2D(
435 pNewFillPrimitive
= new PolyPolygonColorPrimitive2D(
440 if(0.0 != rFill
.getTransparence())
442 // create simpleTransparencePrimitive, add created fill primitive
443 Primitive2DContainer aContent
{ pNewFillPrimitive
};
444 return Primitive2DReference(new UnifiedTransparencePrimitive2D(std::move(aContent
), rFill
.getTransparence()));
446 else if(!rFillGradient
.isDefault())
448 // create sequence with created fill primitive
449 Primitive2DContainer aContent
{ pNewFillPrimitive
};
451 // create FillGradientPrimitive2D for transparence and add to new sequence
452 // fillGradientPrimitive is enough here (compared to PolyPolygonGradientPrimitive2D) since float transparence will be masked anyways
453 const basegfx::B2DRange
aRange(basegfx::utils::getRange(rPolyPolygon
));
454 Primitive2DReference
xRefB(
455 new FillGradientPrimitive2D(
459 Primitive2DContainer aAlpha
{ xRefB
};
461 // create TransparencePrimitive2D using alpha and content
462 return Primitive2DReference(new TransparencePrimitive2D(std::move(aContent
), std::move(aAlpha
)));
466 // add to decomposition
467 return pNewFillPrimitive
;
471 Primitive2DReference
createPolygonLinePrimitive(
472 const basegfx::B2DPolygon
& rPolygon
,
473 const attribute::SdrLineAttribute
& rLine
,
474 const attribute::SdrLineStartEndAttribute
& rStroke
)
476 // create line and stroke attribute
477 const attribute::LineAttribute
aLineAttribute(rLine
.getColor(), rLine
.getWidth(), rLine
.getJoin(), rLine
.getCap());
478 attribute::StrokeAttribute
aStrokeAttribute(std::vector(rLine
.getDotDashArray()), rLine
.getFullDotDashLen());
479 rtl::Reference
<BasePrimitive2D
> pNewLinePrimitive
;
481 if(!rPolygon
.isClosed() && !rStroke
.isDefault())
483 attribute::LineStartEndAttribute
aStart(rStroke
.getStartWidth(), rStroke
.getStartPolyPolygon(), rStroke
.isStartCentered());
484 attribute::LineStartEndAttribute
aEnd(rStroke
.getEndWidth(), rStroke
.getEndPolyPolygon(), rStroke
.isEndCentered());
487 pNewLinePrimitive
= new PolygonStrokeArrowPrimitive2D(rPolygon
, aLineAttribute
, aStrokeAttribute
, aStart
, aEnd
);
492 pNewLinePrimitive
= new PolygonStrokePrimitive2D(rPolygon
, aLineAttribute
, std::move(aStrokeAttribute
));
495 if(0.0 != rLine
.getTransparence())
497 // create simpleTransparencePrimitive, add created fill primitive
498 Primitive2DContainer aContent
{ pNewLinePrimitive
};
499 return Primitive2DReference(new UnifiedTransparencePrimitive2D(std::move(aContent
), rLine
.getTransparence()));
503 // add to decomposition
504 return pNewLinePrimitive
;
508 Primitive2DReference
createTextPrimitive(
509 const basegfx::B2DPolyPolygon
& rUnitPolyPolygon
,
510 const basegfx::B2DHomMatrix
& rObjectTransform
,
511 const attribute::SdrTextAttribute
& rText
,
512 const attribute::SdrLineAttribute
& rStroke
,
516 basegfx::B2DHomMatrix
aAnchorTransform(rObjectTransform
);
517 rtl::Reference
<SdrTextPrimitive2D
> pNew
;
519 if(rText
.isContour())
522 if(!rStroke
.isDefault() && 0.0 != rStroke
.getWidth())
524 // take line width into account and shrink contour polygon accordingly
525 // decompose to get scale
526 basegfx::B2DVector aScale
, aTranslate
;
527 double fRotate
, fShearX
;
528 rObjectTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
530 // scale outline to object's size to allow growing with value relative to that size
531 // and also to keep aspect ratio
532 basegfx::B2DPolyPolygon
aScaledUnitPolyPolygon(rUnitPolyPolygon
);
533 aScaledUnitPolyPolygon
.transform(basegfx::utils::createScaleB2DHomMatrix(
534 fabs(aScale
.getX()), fabs(aScale
.getY())));
536 // grow the polygon. To shrink, use negative value (half width)
537 aScaledUnitPolyPolygon
= basegfx::utils::growInNormalDirection(aScaledUnitPolyPolygon
, -(rStroke
.getWidth() * 0.5));
539 // scale back to unit polygon
540 aScaledUnitPolyPolygon
.transform(basegfx::utils::createScaleB2DHomMatrix(
541 0.0 != aScale
.getX() ? 1.0 / aScale
.getX() : 1.0,
542 0.0 != aScale
.getY() ? 1.0 / aScale
.getY() : 1.0));
544 // create with unit polygon
545 pNew
= new SdrContourTextPrimitive2D(
547 rText
.getOutlinerParaObject(),
548 std::move(aScaledUnitPolyPolygon
),
553 // create with unit polygon
554 pNew
= new SdrContourTextPrimitive2D(
556 rText
.getOutlinerParaObject(),
561 else if(!rText
.getSdrFormTextAttribute().isDefault())
563 // text on path, use scaled polygon
564 basegfx::B2DPolyPolygon
aScaledPolyPolygon(rUnitPolyPolygon
);
565 aScaledPolyPolygon
.transform(rObjectTransform
);
566 pNew
= new SdrPathTextPrimitive2D(
568 rText
.getOutlinerParaObject(),
569 std::move(aScaledPolyPolygon
),
570 rText
.getSdrFormTextAttribute());
574 // rObjectTransform is the whole SdrObject transformation from unit rectangle
575 // to its size and position. Decompose to allow working with single values.
576 basegfx::B2DVector aScale
, aTranslate
;
577 double fRotate
, fShearX
;
578 rObjectTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
581 const bool bMirrorX(basegfx::fTools::less(aScale
.getX(), 0.0));
582 const bool bMirrorY(basegfx::fTools::less(aScale
.getY(), 0.0));
583 aScale
= basegfx::absolute(aScale
);
585 // Get the real size, since polygon outline and scale
586 // from the object transformation may vary (e.g. ellipse segments)
587 basegfx::B2DHomMatrix aJustScaleTransform
;
588 aJustScaleTransform
.set(0, 0, aScale
.getX());
589 aJustScaleTransform
.set(1, 1, aScale
.getY());
590 basegfx::B2DPolyPolygon
aScaledUnitPolyPolygon(rUnitPolyPolygon
);
591 aScaledUnitPolyPolygon
.transform(aJustScaleTransform
);
592 const basegfx::B2DRange aTextAnchorRange
593 = getTextAnchorRange(rText
, basegfx::utils::getRange(aScaledUnitPolyPolygon
));
595 // now create a transformation from this basic range (aTextAnchorRange)
596 // #i121494# if we have no scale use at least 1.0 to have a carrier e.g. for
597 // mirror values, else these will get lost
598 aAnchorTransform
= basegfx::utils::createScaleTranslateB2DHomMatrix(
599 basegfx::fTools::equalZero(aTextAnchorRange
.getWidth()) ? 1.0 : aTextAnchorRange
.getWidth(),
600 basegfx::fTools::equalZero(aTextAnchorRange
.getHeight()) ? 1.0 : aTextAnchorRange
.getHeight(),
601 aTextAnchorRange
.getMinX(), aTextAnchorRange
.getMinY());
604 aAnchorTransform
.scale(bMirrorX
? -1.0 : 1.0, bMirrorY
? -1.0 : 1.0);
606 // apply object's other transforms
607 aAnchorTransform
= basegfx::utils::createShearXRotateTranslateB2DHomMatrix(fShearX
, fRotate
, aTranslate
)
610 if(rText
.isFitToSize())
612 // stretched text in range
613 pNew
= new SdrStretchTextPrimitive2D(
615 rText
.getOutlinerParaObject(),
617 rText
.isFixedCellHeight());
619 else if(rText
.isAutoFit())
621 // isotropically scaled text in range
622 pNew
= new SdrAutoFitTextPrimitive2D(
624 rText
.getOutlinerParaObject(),
628 else if( rText
.isChainable() && !rText
.isInEditMode() )
630 pNew
= new SdrChainedTextPrimitive2D(
632 rText
.getOutlinerParaObject(),
635 else // text in range
637 // build new primitive
638 pNew
= new SdrBlockTextPrimitive2D(
640 rText
.getOutlinerParaObject(),
642 rText
.getSdrTextHorzAdjust(),
643 rText
.getSdrTextVertAdjust(),
644 rText
.isFixedCellHeight(),
651 OSL_ENSURE(pNew
!= nullptr, "createTextPrimitive: no text primitive created (!)");
655 // prepare animation and primitive list
656 drawinglayer::animation::AnimationEntryList aAnimationList
;
657 rText
.getBlinkTextTiming(aAnimationList
);
659 if(0.0 != aAnimationList
.getDuration())
661 // create content sequence
662 Primitive2DReference
xRefA(pNew
);
663 Primitive2DContainer aContent
{ xRefA
};
665 // create and add animated switch primitive
666 return Primitive2DReference(new AnimatedBlinkPrimitive2D(aAnimationList
, std::move(aContent
)));
670 // add to decomposition
671 return Primitive2DReference(pNew
);
677 // suppress scroll when FontWork
678 if(rText
.getSdrFormTextAttribute().isDefault())
680 // get scroll direction
681 const SdrTextAniDirection
eDirection(rText
.getSdrText().GetObject().GetTextAniDirection());
682 const bool bHorizontal(SdrTextAniDirection::Left
== eDirection
|| SdrTextAniDirection::Right
== eDirection
);
684 // decompose to get separated values for the scroll box
685 basegfx::B2DVector aScale
, aTranslate
;
686 double fRotate
, fShearX
;
687 aAnchorTransform
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
689 // build transform from scaled only to full AnchorTransform and inverse
690 const basegfx::B2DHomMatrix
aSRT(basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
691 fShearX
, fRotate
, aTranslate
));
692 basegfx::B2DHomMatrix
aISRT(aSRT
);
695 // bring the primitive back to scaled only and get scaled range, create new clone for this
696 rtl::Reference
<SdrTextPrimitive2D
> pNew2
= pNew
->createTransformedClone(aISRT
);
697 OSL_ENSURE(pNew2
, "createTextPrimitive: Could not create transformed clone of text primitive (!)");
700 // create neutral geometry::ViewInformation2D for local range and decompose calls. This is okay
701 // since the decompose is view-independent
702 geometry::ViewInformation2D aViewInformation2D
;
705 const basegfx::B2DRange
aScaledRange(pNew
->getB2DRange(aViewInformation2D
));
707 // create left outside and right outside transformations. Also take care
708 // of the clip rectangle
709 basegfx::B2DHomMatrix aLeft
, aRight
;
710 basegfx::B2DPoint
aClipTopLeft(0.0, 0.0);
711 basegfx::B2DPoint
aClipBottomRight(aScale
.getX(), aScale
.getY());
715 aClipTopLeft
.setY(aScaledRange
.getMinY());
716 aClipBottomRight
.setY(aScaledRange
.getMaxY());
717 aLeft
.translate(-aScaledRange
.getMaxX(), 0.0);
718 aRight
.translate(aScale
.getX() - aScaledRange
.getMinX(), 0.0);
722 aClipTopLeft
.setX(aScaledRange
.getMinX());
723 aClipBottomRight
.setX(aScaledRange
.getMaxX());
724 aLeft
.translate(0.0, -aScaledRange
.getMaxY());
725 aRight
.translate(0.0, aScale
.getY() - aScaledRange
.getMinY());
731 // prepare animation list
732 drawinglayer::animation::AnimationEntryList aAnimationList
;
736 rText
.getScrollTextTiming(aAnimationList
, aScale
.getX(), aScaledRange
.getWidth());
740 rText
.getScrollTextTiming(aAnimationList
, aScale
.getY(), aScaledRange
.getHeight());
743 if(0.0 != aAnimationList
.getDuration())
745 // create a new Primitive2DContainer containing the animated text in its scaled only state.
746 // use the decomposition to force to simple text primitives, those will no longer
747 // need the outliner for formatting (alternatively it is also possible to just add
748 // pNew to aNewPrimitiveSequence)
749 Primitive2DContainer aAnimSequence
;
750 pNew
->get2DDecomposition(aAnimSequence
, aViewInformation2D
);
753 // create a new animatedInterpolatePrimitive and add it
754 Primitive2DReference
xRefA(new AnimatedInterpolatePrimitive2D({ aLeft
, aRight
}, aAnimationList
, std::move(aAnimSequence
)));
755 Primitive2DContainer aContent
{ xRefA
};
757 // scrolling needs an encapsulating clipping primitive
758 const basegfx::B2DRange
aClipRange(aClipTopLeft
, aClipBottomRight
);
759 basegfx::B2DPolygon
aClipPolygon(basegfx::utils::createPolygonFromRect(aClipRange
));
760 aClipPolygon
.transform(aSRT
);
761 return Primitive2DReference(new MaskPrimitive2D(basegfx::B2DPolyPolygon(aClipPolygon
), std::move(aContent
)));
765 // add to decomposition
766 return Primitive2DReference(pNew
);
771 if(rText
.isInEditMode())
774 // encapsulate with TextHierarchyEditPrimitive2D to allow renderers
775 // to suppress actively edited content if needed
776 Primitive2DReference
xRefA(pNew
);
777 Primitive2DContainer aContent
{ xRefA
};
779 // create and add TextHierarchyEditPrimitive2D primitive
780 return Primitive2DReference(new TextHierarchyEditPrimitive2D(std::move(aContent
)));
784 // add to decomposition
789 Primitive2DContainer
createEmbeddedShadowPrimitive(
790 Primitive2DContainer
&& rContent
,
791 const attribute::SdrShadowAttribute
& rShadow
,
792 const basegfx::B2DHomMatrix
& rObjectMatrix
,
793 const Primitive2DContainer
* pContentForShadow
)
796 return std::move(rContent
);
798 basegfx::B2DHomMatrix aShadowOffset
;
800 if(rShadow
.getSize().getX() != 100000)
802 basegfx::B2DTuple aScale
;
803 basegfx::B2DTuple aTranslate
;
806 rObjectMatrix
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
808 aTranslate
+= getShadowScaleOriginOffset(aScale
, rShadow
.getAlignment());
809 aShadowOffset
.translate(-aTranslate
);
810 aShadowOffset
.scale(rShadow
.getSize().getX() * 0.00001, rShadow
.getSize().getY() * 0.00001);
811 aShadowOffset
.translate(aTranslate
);
814 aShadowOffset
.translate(rShadow
.getOffset().getX(), rShadow
.getOffset().getY());
816 // create shadow primitive and add content
817 const Primitive2DContainer
& rContentForShadow
818 = pContentForShadow
? *pContentForShadow
: rContent
;
819 int nContentWithTransparence
= std::count_if(
820 rContentForShadow
.begin(), rContentForShadow
.end(),
821 [](const Primitive2DReference
& xChild
) {
822 auto pChild
= dynamic_cast<BufferedDecompositionPrimitive2D
*>(xChild
.get());
823 return pChild
&& pChild
->getTransparenceForShadow() != 0;
825 if (nContentWithTransparence
== 0)
827 Primitive2DContainer
aRetval(2);
828 aRetval
[0] = Primitive2DReference(
829 new ShadowPrimitive2D(
833 Primitive2DContainer(pContentForShadow
? *pContentForShadow
: rContent
)));
835 if (0.0 != rShadow
.getTransparence())
837 // create SimpleTransparencePrimitive2D
838 Primitive2DContainer aTempContent
{ aRetval
[0] };
840 aRetval
[0] = Primitive2DReference(
841 new UnifiedTransparencePrimitive2D(
842 std::move(aTempContent
),
843 rShadow
.getTransparence()));
846 aRetval
[1] = Primitive2DReference(new GroupPrimitive2D(std::move(rContent
)));
850 Primitive2DContainer aRetval
;
851 for (const auto& xChild
: rContentForShadow
)
853 aRetval
.push_back(Primitive2DReference(
854 new ShadowPrimitive2D(aShadowOffset
, rShadow
.getColor(), rShadow
.getBlur(),
855 Primitive2DContainer({ xChild
}))));
856 if (rShadow
.getTransparence() != 0.0)
858 Primitive2DContainer aTempContent
{ aRetval
.back() };
859 aRetval
.back() = Primitive2DReference(new UnifiedTransparencePrimitive2D(
860 std::move(aTempContent
), rShadow
.getTransparence()));
865 Primitive2DReference(new GroupPrimitive2D(std::move(rContent
))));
869 Primitive2DContainer
createEmbeddedGlowPrimitive(
870 Primitive2DContainer
&& rContent
,
871 const attribute::SdrGlowAttribute
& rGlow
)
874 return std::move(rContent
);
875 Primitive2DContainer
aRetval(2);
876 aRetval
[0] = Primitive2DReference(
877 new GlowPrimitive2D(rGlow
.getColor(), rGlow
.getRadius(), Primitive2DContainer(rContent
)));
878 aRetval
[1] = Primitive2DReference(new GroupPrimitive2D(Primitive2DContainer(rContent
)));
882 Primitive2DContainer
createEmbeddedSoftEdgePrimitive(Primitive2DContainer
&& aContent
,
885 if (aContent
.empty() || !nRadius
)
886 return std::move(aContent
);
887 Primitive2DContainer
aRetval(1);
888 aRetval
[0] = Primitive2DReference(new SoftEdgePrimitive2D(nRadius
, std::move(aContent
)));
892 } // end of namespace
894 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */