LibreOffice Module svx (master) 1
sdrdecompositiontools.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
40#include <svx/svdotext.hxx>
52#include <osl/diagnose.h>
53
54// for SlideBackgroundFillPrimitive2D
56#include <svx/unoapi.hxx>
57#include <svx/svdpage.hxx>
60
61using namespace com::sun::star;
62
63
65{
66namespace
67{
68// See also: SdrTextObj::AdjustRectToTextDistance
69basegfx::B2DRange getTextAnchorRange(const attribute::SdrTextAttribute& rText,
70 const basegfx::B2DRange& rSnapRange)
71{
72 // Take vertical text orientation into account when deciding
73 // which dimension is its width, and which is its height
74 const OutlinerParaObject& rOutlinerParaObj = rText.getOutlinerParaObject();
75 const bool bVerticalWriting(rOutlinerParaObj.IsEffectivelyVertical());
76 const double fWidthForText = bVerticalWriting ? rSnapRange.getHeight() : rSnapRange.getWidth();
77 // create a range describing the wanted text position and size (aTextAnchorRange). This
78 // means to use the text distance values here
79 // If the margin is larger than the entire width of the text area, then limit the
80 // margin.
81 const double fTextLeftDistance
82 = std::min(static_cast<double>(rText.getTextLeftDistance()), fWidthForText);
83 const double nTextRightDistance
84 = std::min(static_cast<double>(rText.getTextRightDistance()), fWidthForText);
85 double fDistanceForTextL, fDistanceForTextT, fDistanceForTextR, fDistanceForTextB;
86 if (!bVerticalWriting)
87 {
88 fDistanceForTextL = fTextLeftDistance;
89 fDistanceForTextT = rText.getTextUpperDistance();
90 fDistanceForTextR = nTextRightDistance;
91 fDistanceForTextB = rText.getTextLowerDistance();
92 }
93 else if (rOutlinerParaObj.IsTopToBottom())
94 {
95 fDistanceForTextL = rText.getTextLowerDistance();
96 fDistanceForTextT = fTextLeftDistance;
97 fDistanceForTextR = rText.getTextUpperDistance();
98 fDistanceForTextB = nTextRightDistance;
99 }
100 else
101 {
102 fDistanceForTextL = rText.getTextUpperDistance();
103 fDistanceForTextT = nTextRightDistance;
104 fDistanceForTextR = rText.getTextLowerDistance();
105 fDistanceForTextB = fTextLeftDistance;
106 }
107 const basegfx::B2DPoint aTopLeft(rSnapRange.getMinX() + fDistanceForTextL,
108 rSnapRange.getMinY() + fDistanceForTextT);
109 const basegfx::B2DPoint aBottomRight(rSnapRange.getMaxX() - fDistanceForTextR,
110 rSnapRange.getMaxY() - fDistanceForTextB);
111 basegfx::B2DRange aAnchorRange;
112 aAnchorRange.expand(aTopLeft);
113 aAnchorRange.expand(aBottomRight);
114
115 // If the shape has no width, then don't attempt to break the text into multiple
116 // lines, not a single character would satisfy a zero width requirement.
117 // SdrTextObj::impDecomposeBlockTextPrimitive() uses the same constant to
118 // effectively set no limits.
119 if (!bVerticalWriting && aAnchorRange.getWidth() == 0)
120 {
121 aAnchorRange.expand(basegfx::B2DPoint(aTopLeft.getX() - 1000000, aTopLeft.getY()));
122 aAnchorRange.expand(basegfx::B2DPoint(aBottomRight.getX() + 1000000, aBottomRight.getY()));
123 }
124 else if (bVerticalWriting && aAnchorRange.getHeight() == 0)
125 {
126 aAnchorRange.expand(basegfx::B2DPoint(aTopLeft.getX(), aTopLeft.getY() - 1000000));
127 aAnchorRange.expand(basegfx::B2DPoint(aBottomRight.getX(), aBottomRight.getY() + 1000000));
128 }
129 return aAnchorRange;
130}
131
132drawinglayer::attribute::SdrFillAttribute getMasterPageFillAttribute(
133 const geometry::ViewInformation2D& rViewInformation,
134 basegfx::B2DVector& rPageSize)
135{
137
138 // get SdrPage
139 const SdrPage* pVisualizedPage(GetSdrPageFromXDrawPage(rViewInformation.getVisualizedPage()));
140
141 if(nullptr != pVisualizedPage)
142 {
143 // copy needed values for further processing
144 rPageSize.setX(pVisualizedPage->GetWidth());
145 rPageSize.setY(pVisualizedPage->GetHeight());
146
147 if(pVisualizedPage->IsMasterPage())
148 {
149 // the page is a MasterPage, so we are in MasterPage view
150 // still need #i110846#, see ViewContactOfMasterPage
151 if(pVisualizedPage->getSdrPageProperties().GetStyleSheet())
152 {
153 // create page fill attributes with correct properties
155 pVisualizedPage->getSdrPageProperties().GetItemSet());
156 }
157 }
158 else
159 {
160 // the page is *no* MasterPage, we are in normal Page view, get the MasterPage
161 if(pVisualizedPage->TRG_HasMasterPage())
162 {
163 sdr::contact::ViewContact& rVC(pVisualizedPage->TRG_GetMasterPageDescriptorViewContact());
166
167 if(nullptr != pVCOMPD)
168 {
169 // in this case the still needed #i110846# is part of
170 // getCorrectSdrPageProperties, that's the main reason to re-use
171 // that call/functionality here
172 const SdrPageProperties* pCorrectProperties(
173 pVCOMPD->GetMasterPageDescriptor().getCorrectSdrPageProperties());
174
175 if(pCorrectProperties)
176 {
177 // create page fill attributes when correct properties were identified
179 pCorrectProperties->GetItemSet());
180 }
181 }
182 }
183 }
184 }
185
186 return aRetval;
187}
188
189// provide a Primitive2D for the SlideBackgroundFill-mode. It is capable
190// of expressing that state of fill and it's decomposition implements all
191// needed preparation of the geometry in an isolated and controllable
192// space and way.
193// It is currently simple buffered (due to being derived from
194// BufferedDecompositionPrimitive2D) and detects if FillStyle changes
195class SlideBackgroundFillPrimitive2D final : public BufferedDecompositionPrimitive2D
196{
197private:
200
203
204protected:
205 // create decomposition data
206 virtual void create2DDecomposition(
207 Primitive2DContainer& rContainer,
208 const geometry::ViewInformation2D& rViewInformation) const override;
209
210public:
212 SlideBackgroundFillPrimitive2D(
213 const basegfx::B2DPolyPolygon& rPolyPolygon);
214
216 virtual void get2DDecomposition(
217 Primitive2DDecompositionVisitor& rVisitor,
218 const geometry::ViewInformation2D& rViewInformation) const override;
219
221 const basegfx::B2DPolyPolygon& getB2DPolyPolygon() const { return maPolyPolygon; }
222
224 virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
225
227 virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override;
228
230 virtual sal_uInt32 getPrimitive2DID() const override;
231};
232
233SlideBackgroundFillPrimitive2D::SlideBackgroundFillPrimitive2D(
234 const basegfx::B2DPolyPolygon& rPolyPolygon)
235: BufferedDecompositionPrimitive2D()
236, maPolyPolygon(rPolyPolygon)
237, maLastFill()
238{
239}
240
241void SlideBackgroundFillPrimitive2D::create2DDecomposition(
242 Primitive2DContainer& rContainer,
243 const geometry::ViewInformation2D& rViewInformation) const
244{
245 basegfx::B2DVector aPageSize;
246
247 // get fill from target Page, this will check for all needed things
248 // like MasterPage/relationships, etc. (see getMasterPageFillAttribute impl above)
250 getMasterPageFillAttribute(rViewInformation, aPageSize));
251
252 // if fill is on default (empty), nothing will be shown, we are done
253 if(aFill.isDefault())
254 return;
255
256 // Get PolygonRange of own local geometry
257 const basegfx::B2DRange aPolygonRange(getB2DPolyPolygon().getB2DRange());
258
259 // if local geometry is empty, nothing will be shown, we are done
260 if(aPolygonRange.isEmpty())
261 return;
262
263 // Get PageRange
264 const basegfx::B2DRange aPageRange(0.0, 0.0, aPageSize.getX(), aPageSize.getY());
265
266 // if local geometry does not overlap with PageRange, nothing will be shown, we are done
267 if(!aPageRange.overlaps(aPolygonRange))
268 return;
269
270 // create FillPrimitive2D with the geometry (the PolyPolygon) and
271 // the page's definitonRange to:
272 // - on one hand limit to geometry
273 // - on the other hand allow continuation of fill outside of
274 // MasterPage's range
275 const attribute::FillGradientAttribute aEmptyFillTransparenceGradient;
276 const Primitive2DReference aCreatedFill(
278 getB2DPolyPolygon(), // geometry
279 aPageRange, // definition range
280 aFill,
281 aEmptyFillTransparenceGradient));
282
283 rContainer = Primitive2DContainer { aCreatedFill };
284}
285
286void SlideBackgroundFillPrimitive2D::get2DDecomposition(
287 Primitive2DDecompositionVisitor& rVisitor,
288 const geometry::ViewInformation2D& rViewInformation) const
289{
290 basegfx::B2DVector aPageSize;
292
293 if(!getBuffered2DDecomposition().empty())
294 {
295 aFill = getMasterPageFillAttribute(rViewInformation, aPageSize);
296
297 if(!(aFill == maLastFill))
298 {
299 // conditions of last local decomposition have changed, delete
300 const_cast< SlideBackgroundFillPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
301 }
302 }
303
304 if(getBuffered2DDecomposition().empty())
305 {
306 // remember last Fill
307 const_cast< SlideBackgroundFillPrimitive2D* >(this)->maLastFill = aFill;
308 }
309
310 // use parent implementation
311 BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
312}
313
314bool SlideBackgroundFillPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
315{
316 if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
317 {
318 const SlideBackgroundFillPrimitive2D& rCompare
319 = static_cast<const SlideBackgroundFillPrimitive2D&>(rPrimitive);
320
321 return getB2DPolyPolygon() == rCompare.getB2DPolyPolygon();
322 }
323
324 return false;
325}
326
327basegfx::B2DRange SlideBackgroundFillPrimitive2D::getB2DRange(
328 const geometry::ViewInformation2D& /*rViewInformation*/) const
329{
330 // return range
331 return basegfx::utils::getRange(getB2DPolyPolygon());
332}
333
334// provide unique ID
335sal_uInt32 SlideBackgroundFillPrimitive2D::getPrimitive2DID() const
336{
338}
339
340}; // end of anonymous namespace
341
342 class TransparencePrimitive2D;
343
345 const basegfx::B2DPolyPolygon& rPolyPolygon,
346 const attribute::SdrFillAttribute& rFill,
347 const attribute::FillGradientAttribute& rFillGradient)
348 {
349 // when we have no given definition range, use the range of the given geometry
350 // also for definition (simplest case)
351 const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon));
352
354 rPolyPolygon,
355 aRange,
356 rFill,
357 rFillGradient);
358 }
359
361 const basegfx::B2DPolyPolygon& rPolyPolygon,
362 const basegfx::B2DRange& rDefinitionRange,
363 const attribute::SdrFillAttribute& rFill,
364 const attribute::FillGradientAttribute& rFillGradient)
365 {
367 {
368 return Primitive2DReference();
369 }
370
371 // prepare fully scaled polygon
372 rtl::Reference<BasePrimitive2D> pNewFillPrimitive;
373
374 if(!rFill.getGradient().isDefault())
375 {
376 pNewFillPrimitive = new PolyPolygonGradientPrimitive2D(
377 rPolyPolygon,
378 rDefinitionRange,
379 rFill.getGradient());
380 }
381 else if(!rFill.getHatch().isDefault())
382 {
383 pNewFillPrimitive = new PolyPolygonHatchPrimitive2D(
384 rPolyPolygon,
385 rDefinitionRange,
386 rFill.getColor(),
387 rFill.getHatch());
388 }
389 else if(!rFill.getFillGraphic().isDefault())
390 {
391 pNewFillPrimitive = new PolyPolygonGraphicPrimitive2D(
392 rPolyPolygon,
393 rDefinitionRange,
394 rFill.getFillGraphic().createFillGraphicAttribute(rDefinitionRange));
395 }
396 else if(rFill.isSlideBackgroundFill())
397 {
398 // create needed Primitive2D representation for
399 // SlideBackgroundFill-mode
400 pNewFillPrimitive = new SlideBackgroundFillPrimitive2D(
401 rPolyPolygon);
402 }
403 else
404 {
405 pNewFillPrimitive = new PolyPolygonColorPrimitive2D(
406 rPolyPolygon,
407 rFill.getColor());
408 }
409
410 if(0.0 != rFill.getTransparence())
411 {
412 // create simpleTransparencePrimitive, add created fill primitive
413 Primitive2DContainer aContent { pNewFillPrimitive };
414 return Primitive2DReference(new UnifiedTransparencePrimitive2D(std::move(aContent), rFill.getTransparence()));
415 }
416 else if(!rFillGradient.isDefault())
417 {
418 // create sequence with created fill primitive
419 Primitive2DContainer aContent { pNewFillPrimitive };
420
421 // create FillGradientPrimitive2D for transparence and add to new sequence
422 // fillGradientPrimitive is enough here (compared to PolyPolygonGradientPrimitive2D) since float transparence will be masked anyways
423 const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon));
426 aRange,
427 rDefinitionRange,
428 rFillGradient));
429 Primitive2DContainer aAlpha { xRefB };
430
431 // create TransparencePrimitive2D using alpha and content
432 return Primitive2DReference(new TransparencePrimitive2D(std::move(aContent), std::move(aAlpha)));
433 }
434 else
435 {
436 // add to decomposition
437 return pNewFillPrimitive;
438 }
439 }
440
442 const basegfx::B2DPolygon& rPolygon,
443 const attribute::SdrLineAttribute& rLine,
445 {
446 // create line and stroke attribute
447 const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin(), rLine.getCap());
448 attribute::StrokeAttribute aStrokeAttribute(std::vector(rLine.getDotDashArray()), rLine.getFullDotDashLen());
449 rtl::Reference<BasePrimitive2D> pNewLinePrimitive;
450
451 if(!rPolygon.isClosed() && !rStroke.isDefault())
452 {
455
456 // create data
457 pNewLinePrimitive = new PolygonStrokeArrowPrimitive2D(rPolygon, aLineAttribute, aStrokeAttribute, aStart, aEnd);
458 }
459 else
460 {
461 // create data
462 pNewLinePrimitive = new PolygonStrokePrimitive2D(rPolygon, aLineAttribute, std::move(aStrokeAttribute));
463 }
464
465 if(0.0 != rLine.getTransparence())
466 {
467 // create simpleTransparencePrimitive, add created fill primitive
468 Primitive2DContainer aContent { pNewLinePrimitive };
469 return Primitive2DReference(new UnifiedTransparencePrimitive2D(std::move(aContent), rLine.getTransparence()));
470 }
471 else
472 {
473 // add to decomposition
474 return pNewLinePrimitive;
475 }
476 }
477
479 const basegfx::B2DPolyPolygon& rUnitPolyPolygon,
480 const basegfx::B2DHomMatrix& rObjectTransform,
481 const attribute::SdrTextAttribute& rText,
482 const attribute::SdrLineAttribute& rStroke,
483 bool bCellText,
484 bool bWordWrap)
485 {
486 basegfx::B2DHomMatrix aAnchorTransform(rObjectTransform);
488
489 if(rText.isContour())
490 {
491 // contour text
492 if(!rStroke.isDefault() && 0.0 != rStroke.getWidth())
493 {
494 // take line width into account and shrink contour polygon accordingly
495 // decompose to get scale
496 basegfx::B2DVector aScale, aTranslate;
497 double fRotate, fShearX;
498 rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX);
499
500 // scale outline to object's size to allow growing with value relative to that size
501 // and also to keep aspect ratio
502 basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon);
504 fabs(aScale.getX()), fabs(aScale.getY())));
505
506 // grow the polygon. To shrink, use negative value (half width)
507 aScaledUnitPolyPolygon = basegfx::utils::growInNormalDirection(aScaledUnitPolyPolygon, -(rStroke.getWidth() * 0.5));
508
509 // scale back to unit polygon
511 0.0 != aScale.getX() ? 1.0 / aScale.getX() : 1.0,
512 0.0 != aScale.getY() ? 1.0 / aScale.getY() : 1.0));
513
514 // create with unit polygon
515 pNew = new SdrContourTextPrimitive2D(
516 &rText.getSdrText(),
517 rText.getOutlinerParaObject(),
518 std::move(aScaledUnitPolyPolygon),
519 rObjectTransform);
520 }
521 else
522 {
523 // create with unit polygon
524 pNew = new SdrContourTextPrimitive2D(
525 &rText.getSdrText(),
526 rText.getOutlinerParaObject(),
527 rUnitPolyPolygon,
528 rObjectTransform);
529 }
530 }
531 else if(!rText.getSdrFormTextAttribute().isDefault())
532 {
533 // text on path, use scaled polygon
534 basegfx::B2DPolyPolygon aScaledPolyPolygon(rUnitPolyPolygon);
535 aScaledPolyPolygon.transform(rObjectTransform);
536 pNew = new SdrPathTextPrimitive2D(
537 &rText.getSdrText(),
538 rText.getOutlinerParaObject(),
539 std::move(aScaledPolyPolygon),
541 }
542 else
543 {
544 // rObjectTransform is the whole SdrObject transformation from unit rectangle
545 // to its size and position. Decompose to allow working with single values.
546 basegfx::B2DVector aScale, aTranslate;
547 double fRotate, fShearX;
548 rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX);
549
550 // extract mirroring
551 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
552 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
553 aScale = basegfx::absolute(aScale);
554
555 // Get the real size, since polygon outline and scale
556 // from the object transformation may vary (e.g. ellipse segments)
557 basegfx::B2DHomMatrix aJustScaleTransform;
558 aJustScaleTransform.set(0, 0, aScale.getX());
559 aJustScaleTransform.set(1, 1, aScale.getY());
560 basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon);
561 aScaledUnitPolyPolygon.transform(aJustScaleTransform);
562 const basegfx::B2DRange aTextAnchorRange
563 = getTextAnchorRange(rText, basegfx::utils::getRange(aScaledUnitPolyPolygon));
564
565 // now create a transformation from this basic range (aTextAnchorRange)
566 // #i121494# if we have no scale use at least 1.0 to have a carrier e.g. for
567 // mirror values, else these will get lost
569 basegfx::fTools::equalZero(aTextAnchorRange.getWidth()) ? 1.0 : aTextAnchorRange.getWidth(),
570 basegfx::fTools::equalZero(aTextAnchorRange.getHeight()) ? 1.0 : aTextAnchorRange.getHeight(),
571 aTextAnchorRange.getMinX(), aTextAnchorRange.getMinY());
572
573 // apply mirroring
574 aAnchorTransform.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
575
576 // apply object's other transforms
577 aAnchorTransform = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(fShearX, fRotate, aTranslate)
578 * aAnchorTransform;
579
580 if(rText.isFitToSize())
581 {
582 // stretched text in range
583 pNew = new SdrStretchTextPrimitive2D(
584 &rText.getSdrText(),
585 rText.getOutlinerParaObject(),
586 aAnchorTransform,
587 rText.isFixedCellHeight());
588 }
589 else if(rText.isAutoFit())
590 {
591 // isotropically scaled text in range
592 pNew = new SdrAutoFitTextPrimitive2D(
593 &rText.getSdrText(),
594 rText.getOutlinerParaObject(),
595 aAnchorTransform,
596 bWordWrap);
597 }
598 else if( rText.isChainable() && !rText.isInEditMode() )
599 {
600 pNew = new SdrChainedTextPrimitive2D(
601 &rText.getSdrText(),
602 rText.getOutlinerParaObject(),
603 aAnchorTransform );
604 }
605 else // text in range
606 {
607 // build new primitive
608 pNew = new SdrBlockTextPrimitive2D(
609 &rText.getSdrText(),
610 rText.getOutlinerParaObject(),
611 aAnchorTransform,
612 rText.getSdrTextHorzAdjust(),
613 rText.getSdrTextVertAdjust(),
614 rText.isFixedCellHeight(),
615 rText.isScroll(),
616 bCellText,
617 bWordWrap);
618 }
619 }
620
621 OSL_ENSURE(pNew != nullptr, "createTextPrimitive: no text primitive created (!)");
622
623 if(rText.isBlink())
624 {
625 // prepare animation and primitive list
627 rText.getBlinkTextTiming(aAnimationList);
628
629 if(0.0 != aAnimationList.getDuration())
630 {
631 // create content sequence
632 Primitive2DReference xRefA(pNew);
633 Primitive2DContainer aContent { xRefA };
634
635 // create and add animated switch primitive
636 return Primitive2DReference(new AnimatedBlinkPrimitive2D(aAnimationList, std::move(aContent)));
637 }
638 else
639 {
640 // add to decomposition
641 return Primitive2DReference(pNew);
642 }
643 }
644
645 if(rText.isScroll())
646 {
647 // suppress scroll when FontWork
649 {
650 // get scroll direction
651 const SdrTextAniDirection eDirection(rText.getSdrText().GetObject().GetTextAniDirection());
652 const bool bHorizontal(SdrTextAniDirection::Left == eDirection || SdrTextAniDirection::Right == eDirection);
653
654 // decompose to get separated values for the scroll box
655 basegfx::B2DVector aScale, aTranslate;
656 double fRotate, fShearX;
657 aAnchorTransform.decompose(aScale, aTranslate, fRotate, fShearX);
658
659 // build transform from scaled only to full AnchorTransform and inverse
661 fShearX, fRotate, aTranslate));
662 basegfx::B2DHomMatrix aISRT(aSRT);
663 aISRT.invert();
664
665 // bring the primitive back to scaled only and get scaled range, create new clone for this
666 rtl::Reference<SdrTextPrimitive2D> pNew2 = pNew->createTransformedClone(aISRT);
667 OSL_ENSURE(pNew2, "createTextPrimitive: Could not create transformed clone of text primitive (!)");
668 pNew = pNew2.get();
669
670 // create neutral geometry::ViewInformation2D for local range and decompose calls. This is okay
671 // since the decompose is view-independent
672 geometry::ViewInformation2D aViewInformation2D;
673
674 // get range
675 const basegfx::B2DRange aScaledRange(pNew->getB2DRange(aViewInformation2D));
676
677 // create left outside and right outside transformations. Also take care
678 // of the clip rectangle
679 basegfx::B2DHomMatrix aLeft, aRight;
680 basegfx::B2DPoint aClipTopLeft(0.0, 0.0);
681 basegfx::B2DPoint aClipBottomRight(aScale.getX(), aScale.getY());
682
683 if(bHorizontal)
684 {
685 aClipTopLeft.setY(aScaledRange.getMinY());
686 aClipBottomRight.setY(aScaledRange.getMaxY());
687 aLeft.translate(-aScaledRange.getMaxX(), 0.0);
688 aRight.translate(aScale.getX() - aScaledRange.getMinX(), 0.0);
689 }
690 else
691 {
692 aClipTopLeft.setX(aScaledRange.getMinX());
693 aClipBottomRight.setX(aScaledRange.getMaxX());
694 aLeft.translate(0.0, -aScaledRange.getMaxY());
695 aRight.translate(0.0, aScale.getY() - aScaledRange.getMinY());
696 }
697
698 aLeft *= aSRT;
699 aRight *= aSRT;
700
701 // prepare animation list
703
704 if(bHorizontal)
705 {
706 rText.getScrollTextTiming(aAnimationList, aScale.getX(), aScaledRange.getWidth());
707 }
708 else
709 {
710 rText.getScrollTextTiming(aAnimationList, aScale.getY(), aScaledRange.getHeight());
711 }
712
713 if(0.0 != aAnimationList.getDuration())
714 {
715 // create a new Primitive2DContainer containing the animated text in its scaled only state.
716 // use the decomposition to force to simple text primitives, those will no longer
717 // need the outliner for formatting (alternatively it is also possible to just add
718 // pNew to aNewPrimitiveSequence)
719 Primitive2DContainer aAnimSequence;
720 pNew->get2DDecomposition(aAnimSequence, aViewInformation2D);
721 pNew.clear();
722
723 // create a new animatedInterpolatePrimitive and add it
724 Primitive2DReference xRefA(new AnimatedInterpolatePrimitive2D({ aLeft, aRight }, aAnimationList, std::move(aAnimSequence)));
725 Primitive2DContainer aContent { xRefA };
726
727 // scrolling needs an encapsulating clipping primitive
728 const basegfx::B2DRange aClipRange(aClipTopLeft, aClipBottomRight);
730 aClipPolygon.transform(aSRT);
731 return Primitive2DReference(new MaskPrimitive2D(basegfx::B2DPolyPolygon(aClipPolygon), std::move(aContent)));
732 }
733 else
734 {
735 // add to decomposition
736 return Primitive2DReference(pNew);
737 }
738 }
739 }
740
741 if(rText.isInEditMode())
742 {
743 // #i97628#
744 // encapsulate with TextHierarchyEditPrimitive2D to allow renderers
745 // to suppress actively edited content if needed
746 Primitive2DReference xRefA(pNew);
747 Primitive2DContainer aContent { xRefA };
748
749 // create and add TextHierarchyEditPrimitive2D primitive
750 return Primitive2DReference(new TextHierarchyEditPrimitive2D(std::move(aContent)));
751 }
752 else
753 {
754 // add to decomposition
755 return pNew;
756 }
757 }
758
760 Primitive2DContainer&& rContent,
761 const attribute::SdrShadowAttribute& rShadow,
762 const basegfx::B2DHomMatrix& rObjectMatrix,
763 const Primitive2DContainer* pContentForShadow)
764 {
765 if(rContent.empty())
766 return std::move(rContent);
767
768 basegfx::B2DHomMatrix aShadowOffset;
769
770 if(rShadow.getSize().getX() != 100000)
771 {
772 basegfx::B2DTuple aScale;
773 basegfx::B2DTuple aTranslate;
774 double fRotate = 0;
775 double fShearX = 0;
776 rObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
777 // Scale the shadow
778 double nTranslateX = aTranslate.getX();
779 double nTranslateY = aTranslate.getY();
780
781 // The origin for scaling is the top left corner by default. A negative
782 // shadow offset changes the origin.
783 if (rShadow.getOffset().getX() < 0)
784 nTranslateX += aScale.getX();
785 if (rShadow.getOffset().getY() < 0)
786 nTranslateY += aScale.getY();
787
788 aShadowOffset.translate(-nTranslateX, -nTranslateY);
789 aShadowOffset.scale(rShadow.getSize().getX() * 0.00001, rShadow.getSize().getY() * 0.00001);
790 aShadowOffset.translate(nTranslateX, nTranslateY);
791 }
792
793 aShadowOffset.translate(rShadow.getOffset().getX(), rShadow.getOffset().getY());
794
795 // create shadow primitive and add content
796 const Primitive2DContainer& rContentForShadow
797 = pContentForShadow ? *pContentForShadow : rContent;
798 int nContentWithTransparence = std::count_if(
799 rContentForShadow.begin(), rContentForShadow.end(),
800 [](const Primitive2DReference& xChild) {
801 auto pChild = dynamic_cast<BufferedDecompositionPrimitive2D*>(xChild.get());
802 return pChild && pChild->getTransparenceForShadow() != 0;
803 });
804 if (nContentWithTransparence == 0)
805 {
806 Primitive2DContainer aRetval(2);
807 aRetval[0] = Primitive2DReference(
809 aShadowOffset,
810 rShadow.getColor(),
811 rShadow.getBlur(),
812 Primitive2DContainer(pContentForShadow ? *pContentForShadow : rContent)));
813
814 if (0.0 != rShadow.getTransparence())
815 {
816 // create SimpleTransparencePrimitive2D
817 Primitive2DContainer aTempContent{ aRetval[0] };
818
819 aRetval[0] = Primitive2DReference(
821 std::move(aTempContent),
822 rShadow.getTransparence()));
823 }
824
825 aRetval[1] = Primitive2DReference(new GroupPrimitive2D(std::move(rContent)));
826 return aRetval;
827 }
828
829 Primitive2DContainer aRetval;
830 for (const auto& xChild : rContentForShadow)
831 {
832 double fChildTransparence = 0.0;
833 auto pChild = dynamic_cast<BufferedDecompositionPrimitive2D*>(xChild.get());
834 if (pChild)
835 {
836 fChildTransparence = pChild->getTransparenceForShadow();
837 fChildTransparence /= 100;
838 }
839 aRetval.push_back(Primitive2DReference(
840 new ShadowPrimitive2D(aShadowOffset, rShadow.getColor(), rShadow.getBlur(),
841 Primitive2DContainer({ xChild }))));
842 if (rShadow.getTransparence() != 0.0 || fChildTransparence != 0.0)
843 {
844 Primitive2DContainer aTempContent{ aRetval.back() };
845
846 double fChildAlpha = 1.0 - fChildTransparence;
847 double fShadowAlpha = 1.0 - rShadow.getTransparence();
848 double fTransparence = 1.0 - fChildAlpha * fShadowAlpha;
850 std::move(aTempContent), fTransparence));
851 }
852 }
853
854 aRetval.push_back(
855 Primitive2DReference(new GroupPrimitive2D(std::move(rContent))));
856 return aRetval;
857 }
858
860 Primitive2DContainer&& rContent,
861 const attribute::SdrGlowAttribute& rGlow)
862 {
863 if(rContent.empty())
864 return std::move(rContent);
865 Primitive2DContainer aRetval(2);
866 aRetval[0] = Primitive2DReference(
867 new GlowPrimitive2D(rGlow.getColor(), rGlow.getRadius(), Primitive2DContainer(rContent)));
869 return aRetval;
870 }
871
873 sal_Int32 nRadius)
874 {
875 if (aContent.empty() || !nRadius)
876 return std::move(aContent);
877 Primitive2DContainer aRetval(1);
878 aRetval[0] = Primitive2DReference(new SoftEdgePrimitive2D(nRadius, std::move(aContent)));
879 return aRetval;
880 }
881
882} // end of namespace
883
884/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool IsTopToBottom() const
bool IsEffectivelyVertical() const
A SdrPage contains exactly one SdrObjList and a description of the physical page dimensions (size / m...
Definition: svdpage.hxx:377
SdrTextAniDirection GetTextAniDirection() const
Definition: svdotext.cxx:1776
SdrTextObj & GetObject() const
Definition: svdtext.hxx:63
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
void set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
void translate(double fX, double fY)
void scale(double fX, double fY)
void transform(const basegfx::B2DHomMatrix &rMatrix)
bool isClosed() const
void transform(const basegfx::B2DHomMatrix &rMatrix)
TYPE getMaxX() const
TYPE getWidth() const
TYPE getMinX() const
TYPE getMinY() const
void expand(const Tuple2D< TYPE > &rTuple)
TYPE getMaxY() const
TYPE getHeight() const
TYPE getX() const
void setY(TYPE fY)
TYPE getY() const
void setX(TYPE fX)
virtual double getDuration() const override
const basegfx::BColor & getColor() const
const FillHatchAttribute & getHatch() const
const FillGradientAttribute & getGradient() const
const SdrFillGraphicAttribute & getFillGraphic() const
FillGraphicAttribute createFillGraphicAttribute(const basegfx::B2DRange &rRange) const
css::drawing::LineCap getCap() const
const ::std::vector< double > & getDotDashArray() const
const basegfx::BColor & getColor() const
basegfx::B2DLineJoin getJoin() const
const basegfx::B2DPolyPolygon & getStartPolyPolygon() const
const basegfx::B2DPolyPolygon & getEndPolyPolygon() const
const basegfx::BColor & getColor() const
const basegfx::B2DVector & getSize() const
const basegfx::B2DVector & getOffset() const
const OutlinerParaObject & getOutlinerParaObject() const
void getScrollTextTiming(drawinglayer::animation::AnimationEntryList &rAnimList, double fFrameLength, double fTextLength) const
void getBlinkTextTiming(drawinglayer::animation::AnimationEntryList &rAnimList) const
SdrTextVertAdjust getSdrTextVertAdjust() const
const SdrFormTextAttribute & getSdrFormTextAttribute() const
SdrTextHorzAdjust getSdrTextHorzAdjust() const
bool equalZero(const T &rfVal)
bool less(const T &rfValA, const T &rfValB)
bool moreOrEqual(const T &rfValA, const T &rfValB)
B2DHomMatrix createScaleTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fTranslateX, double fTranslateY)
B2DPolygon createPolygonFromRect(const B2DRectangle &rRect, double fRadiusX, double fRadiusY)
B2DPolygon growInNormalDirection(const B2DPolygon &rCandidate, double fValue)
B2DHomMatrix createScaleB2DHomMatrix(double fScaleX, double fScaleY)
B2DHomMatrix createShearXRotateTranslateB2DHomMatrix(double fShearX, double fRadiant, double fTranslateX, double fTranslateY)
B2DRange getRange(const B2DPolygon &rCandidate)
B2DTuple absolute(const B2DTuple &rTup)
Primitive2DContainer createEmbeddedSoftEdgePrimitive(Primitive2DContainer &&aContent, sal_Int32 nRadius)
Primitive2DContainer createEmbeddedShadowPrimitive(Primitive2DContainer &&rContent, const attribute::SdrShadowAttribute &rShadow, const basegfx::B2DHomMatrix &rObjectMatrix, const Primitive2DContainer *pContentForShadow)
attribute::SdrFillAttribute createNewSdrFillAttribute(const SfxItemSet &rSet)
Primitive2DReference createPolygonLinePrimitive(const basegfx::B2DPolygon &rPolygon, const attribute::SdrLineAttribute &rLine, const attribute::SdrLineStartEndAttribute &rStroke)
Primitive2DReference createTextPrimitive(const basegfx::B2DPolyPolygon &rUnitPolyPolygon, const basegfx::B2DHomMatrix &rObjectTransform, const attribute::SdrTextAttribute &rText, const attribute::SdrLineAttribute &rStroke, bool bCellText, bool bWordWrap)
Primitive2DContainer createEmbeddedGlowPrimitive(Primitive2DContainer &&rContent, const attribute::SdrGlowAttribute &rGlow)
Primitive2DReference createPolyPolygonFillPrimitive(const basegfx::B2DPolyPolygon &rPolyPolygon, const basegfx::B2DRange &rDefinitionRange, const attribute::SdrFillAttribute &rFill, const attribute::FillGradientAttribute &rFillGradient)
basegfx::B2DPolyPolygon maPolyPolygon
the basegfx::B2DPolyPolygon geometry
drawinglayer::attribute::SdrFillAttribute maLastFill
the last SdrFillAttribute the geometry was created for
SdrTextAniDirection
Definition: sdtaditm.hxx:30
#define PRIMITIVE2D_ID_SLIDEBACKGROUNDFILLPRIMITIVE2D
SdrPage * GetSdrPageFromXDrawPage(const uno::Reference< drawing::XDrawPage > &xDrawPage) noexcept
returns the SdrObject from the given StarOffice API wrapper
Definition: unopage.cxx:886
bool operator==(const XclFontData &rLeft, const XclFontData &rRight)