Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / sdr / primitive2d / sdrdecompositiontools.cxx
blob50f66391d95e46660aa4714d1d91b6d4fc48fadd
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 <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
67 namespace
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)
73 switch (eAlignment)
75 case model::RectangleAlignment::TopLeft:
76 return { 0, 0 };
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() };
93 default:
94 return { 0, 0 };
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
110 // margin.
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;
130 else
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));
159 return aAnchorRange;
162 drawinglayer::attribute::SdrFillAttribute getMasterPageFillAttribute(
163 const geometry::ViewInformation2D& rViewInformation,
164 basegfx::B2DVector& rPageSize)
166 drawinglayer::attribute::SdrFillAttribute aRetval;
168 // get SdrPage
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());
188 else
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());
216 return aRetval;
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
222 // space and way.
223 // It is currently simple buffered (due to being derived from
224 // BufferedDecompositionPrimitive2D) and detects if FillStyle changes
225 class SlideBackgroundFillPrimitive2D final : public BufferedDecompositionPrimitive2D
227 private:
228 /// the basegfx::B2DPolyPolygon geometry
229 basegfx::B2DPolyPolygon maPolyPolygon;
231 /// the last SdrFillAttribute the geometry was created for
232 drawinglayer::attribute::SdrFillAttribute maLastFill;
234 protected:
235 // create decomposition data
236 virtual void create2DDecomposition(
237 Primitive2DContainer& rContainer,
238 const geometry::ViewInformation2D& rViewInformation) const override;
240 public:
241 /// constructor
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;
250 /// data read access
251 const basegfx::B2DPolyPolygon& getB2DPolyPolygon() const { return maPolyPolygon; }
253 /// compare operator
254 virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
256 /// get range
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)
267 , maLastFill()
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())
284 return;
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())
291 return;
293 // Get PageRange
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))
298 return;
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
310 aFill,
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();
354 return false;
357 basegfx::B2DRange SlideBackgroundFillPrimitive2D::getB2DRange(
358 const geometry::ViewInformation2D& /*rViewInformation*/) const
360 // return range
361 return basegfx::utils::getRange(getB2DPolyPolygon());
364 // provide unique ID
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(
384 rPolyPolygon,
385 aRange,
386 rFill,
387 rFillGradient);
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(
407 rPolyPolygon,
408 rDefinitionRange,
409 rFill.getGradient());
411 else if(!rFill.getHatch().isDefault())
413 pNewFillPrimitive = new PolyPolygonHatchPrimitive2D(
414 rPolyPolygon,
415 rDefinitionRange,
416 rFill.getColor(),
417 rFill.getHatch());
419 else if(!rFill.getFillGraphic().isDefault())
421 pNewFillPrimitive = new PolyPolygonGraphicPrimitive2D(
422 rPolyPolygon,
423 rDefinitionRange,
424 rFill.getFillGraphic().createFillGraphicAttribute(rDefinitionRange));
426 else if(rFill.isSlideBackgroundFill())
428 // create needed Primitive2D representation for
429 // SlideBackgroundFill-mode
430 pNewFillPrimitive = new SlideBackgroundFillPrimitive2D(
431 rPolyPolygon);
433 else
435 pNewFillPrimitive = new PolyPolygonColorPrimitive2D(
436 rPolyPolygon,
437 rFill.getColor());
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(
456 aRange,
457 rDefinitionRange,
458 rFillGradient));
459 Primitive2DContainer aAlpha { xRefB };
461 // create TransparencePrimitive2D using alpha and content
462 return Primitive2DReference(new TransparencePrimitive2D(std::move(aContent), std::move(aAlpha)));
464 else
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());
486 // create data
487 pNewLinePrimitive = new PolygonStrokeArrowPrimitive2D(rPolygon, aLineAttribute, aStrokeAttribute, aStart, aEnd);
489 else
491 // create data
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()));
501 else
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,
513 bool bCellText,
514 bool bWordWrap)
516 basegfx::B2DHomMatrix aAnchorTransform(rObjectTransform);
517 rtl::Reference<SdrTextPrimitive2D> pNew;
519 if(rText.isContour())
521 // contour text
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(
546 &rText.getSdrText(),
547 rText.getOutlinerParaObject(),
548 std::move(aScaledUnitPolyPolygon),
549 rObjectTransform);
551 else
553 // create with unit polygon
554 pNew = new SdrContourTextPrimitive2D(
555 &rText.getSdrText(),
556 rText.getOutlinerParaObject(),
557 rUnitPolyPolygon,
558 rObjectTransform);
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(
567 &rText.getSdrText(),
568 rText.getOutlinerParaObject(),
569 std::move(aScaledPolyPolygon),
570 rText.getSdrFormTextAttribute());
572 else
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);
580 // extract mirroring
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());
603 // apply mirroring
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)
608 * aAnchorTransform;
610 if(rText.isFitToSize())
612 // stretched text in range
613 pNew = new SdrStretchTextPrimitive2D(
614 &rText.getSdrText(),
615 rText.getOutlinerParaObject(),
616 aAnchorTransform,
617 rText.isFixedCellHeight());
619 else if(rText.isAutoFit())
621 // isotropically scaled text in range
622 pNew = new SdrAutoFitTextPrimitive2D(
623 &rText.getSdrText(),
624 rText.getOutlinerParaObject(),
625 aAnchorTransform,
626 bWordWrap);
628 else if( rText.isChainable() && !rText.isInEditMode() )
630 pNew = new SdrChainedTextPrimitive2D(
631 &rText.getSdrText(),
632 rText.getOutlinerParaObject(),
633 aAnchorTransform );
635 else // text in range
637 // build new primitive
638 pNew = new SdrBlockTextPrimitive2D(
639 &rText.getSdrText(),
640 rText.getOutlinerParaObject(),
641 aAnchorTransform,
642 rText.getSdrTextHorzAdjust(),
643 rText.getSdrTextVertAdjust(),
644 rText.isFixedCellHeight(),
645 rText.isScroll(),
646 bCellText,
647 bWordWrap);
651 OSL_ENSURE(pNew != nullptr, "createTextPrimitive: no text primitive created (!)");
653 if(rText.isBlink())
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)));
668 else
670 // add to decomposition
671 return Primitive2DReference(pNew);
675 if(rText.isScroll())
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);
693 aISRT.invert();
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 (!)");
698 pNew = pNew2.get();
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;
704 // get range
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());
713 if(bHorizontal)
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);
720 else
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());
728 aLeft *= aSRT;
729 aRight *= aSRT;
731 // prepare animation list
732 drawinglayer::animation::AnimationEntryList aAnimationList;
734 if(bHorizontal)
736 rText.getScrollTextTiming(aAnimationList, aScale.getX(), aScaledRange.getWidth());
738 else
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);
751 pNew.clear();
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)));
763 else
765 // add to decomposition
766 return Primitive2DReference(pNew);
771 if(rText.isInEditMode())
773 // #i97628#
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)));
782 else
784 // add to decomposition
785 return pNew;
789 Primitive2DContainer createEmbeddedShadowPrimitive(
790 Primitive2DContainer&& rContent,
791 const attribute::SdrShadowAttribute& rShadow,
792 const basegfx::B2DHomMatrix& rObjectMatrix,
793 const Primitive2DContainer* pContentForShadow)
795 if(rContent.empty())
796 return std::move(rContent);
798 basegfx::B2DHomMatrix aShadowOffset;
800 if(rShadow.getSize().getX() != 100000)
802 basegfx::B2DTuple aScale;
803 basegfx::B2DTuple aTranslate;
804 double fRotate = 0;
805 double fShearX = 0;
806 rObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
807 // Scale the shadow
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(
830 aShadowOffset,
831 rShadow.getColor(),
832 rShadow.getBlur(),
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)));
847 return aRetval;
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()));
864 aRetval.push_back(
865 Primitive2DReference(new GroupPrimitive2D(std::move(rContent))));
866 return aRetval;
869 Primitive2DContainer createEmbeddedGlowPrimitive(
870 Primitive2DContainer&& rContent,
871 const attribute::SdrGlowAttribute& rGlow)
873 if(rContent.empty())
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)));
879 return aRetval;
882 Primitive2DContainer createEmbeddedSoftEdgePrimitive(Primitive2DContainer&& aContent,
883 sal_Int32 nRadius)
885 if (aContent.empty() || !nRadius)
886 return std::move(aContent);
887 Primitive2DContainer aRetval(1);
888 aRetval[0] = Primitive2DReference(new SoftEdgePrimitive2D(nRadius, std::move(aContent)));
889 return aRetval;
892 } // end of namespace
894 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */