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>
53#include <osl/diagnose.h>
54
55// for SlideBackgroundFillPrimitive2D
57#include <svx/unoapi.hxx>
58#include <svx/svdpage.hxx>
61
62using namespace com::sun::star;
63
64
66{
67namespace
68{
70basegfx::B2DTuple getShadowScaleOriginOffset(const basegfx::B2DTuple& aScale,
72{
73 switch (eAlignment)
74 {
76 return { 0, 0 };
78 return { aScale.getX() / 2, 0 };
80 return { aScale.getX(), 0 };
82 return { 0, aScale.getY() / 2 };
84 return { aScale.getX() / 2, aScale.getY() / 2 };
86 return { aScale.getX(), aScale.getY() / 2 };
88 return { 0, aScale.getY() };
90 return { aScale.getX() / 2, aScale.getY() };
92 return { aScale.getX(), aScale.getY() };
93 default:
94 return { 0, 0 };
95 }
96};
97
98// See also: SdrTextObj::AdjustRectToTextDistance
99basegfx::B2DRange getTextAnchorRange(const attribute::SdrTextAttribute& rText,
100 const basegfx::B2DRange& rSnapRange)
101{
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)
117 {
118 fDistanceForTextL = fTextLeftDistance;
119 fDistanceForTextT = rText.getTextUpperDistance();
120 fDistanceForTextR = nTextRightDistance;
121 fDistanceForTextB = rText.getTextLowerDistance();
122 }
123 else if (rOutlinerParaObj.IsTopToBottom())
124 {
125 fDistanceForTextL = rText.getTextLowerDistance();
126 fDistanceForTextT = fTextLeftDistance;
127 fDistanceForTextR = rText.getTextUpperDistance();
128 fDistanceForTextB = nTextRightDistance;
129 }
130 else
131 {
132 fDistanceForTextL = rText.getTextUpperDistance();
133 fDistanceForTextT = nTextRightDistance;
134 fDistanceForTextR = rText.getTextLowerDistance();
135 fDistanceForTextB = fTextLeftDistance;
136 }
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);
144
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)
150 {
151 aAnchorRange.expand(basegfx::B2DPoint(aTopLeft.getX() - 1000000, aTopLeft.getY()));
152 aAnchorRange.expand(basegfx::B2DPoint(aBottomRight.getX() + 1000000, aBottomRight.getY()));
153 }
154 else if (bVerticalWriting && aAnchorRange.getHeight() == 0)
155 {
156 aAnchorRange.expand(basegfx::B2DPoint(aTopLeft.getX(), aTopLeft.getY() - 1000000));
157 aAnchorRange.expand(basegfx::B2DPoint(aBottomRight.getX(), aBottomRight.getY() + 1000000));
158 }
159 return aAnchorRange;
160}
161
162drawinglayer::attribute::SdrFillAttribute getMasterPageFillAttribute(
163 const geometry::ViewInformation2D& rViewInformation,
164 basegfx::B2DVector& rPageSize)
165{
167
168 // get SdrPage
169 const SdrPage* pVisualizedPage(GetSdrPageFromXDrawPage(rViewInformation.getVisualizedPage()));
170
171 if(nullptr != pVisualizedPage)
172 {
173 // copy needed values for further processing
174 rPageSize.setX(pVisualizedPage->GetWidth());
175 rPageSize.setY(pVisualizedPage->GetHeight());
176
177 if(pVisualizedPage->IsMasterPage())
178 {
179 // the page is a MasterPage, so we are in MasterPage view
180 // still need #i110846#, see ViewContactOfMasterPage
181 if(pVisualizedPage->getSdrPageProperties().GetStyleSheet())
182 {
183 // create page fill attributes with correct properties
185 pVisualizedPage->getSdrPageProperties().GetItemSet());
186 }
187 }
188 else
189 {
190 // the page is *no* MasterPage, we are in normal Page view, get the MasterPage
191 if(pVisualizedPage->TRG_HasMasterPage())
192 {
193 sdr::contact::ViewContact& rVC(pVisualizedPage->TRG_GetMasterPageDescriptorViewContact());
196
197 if(nullptr != pVCOMPD)
198 {
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());
204
205 if(pCorrectProperties)
206 {
207 // create page fill attributes when correct properties were identified
209 pCorrectProperties->GetItemSet());
210 }
211 }
212 }
213 }
214 }
215
216 return aRetval;
217}
218
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
225class SlideBackgroundFillPrimitive2D final : public BufferedDecompositionPrimitive2D
226{
227private:
230
233
234protected:
235 // create decomposition data
236 virtual void create2DDecomposition(
237 Primitive2DContainer& rContainer,
238 const geometry::ViewInformation2D& rViewInformation) const override;
239
240public:
242 SlideBackgroundFillPrimitive2D(
243 const basegfx::B2DPolyPolygon& rPolyPolygon);
244
246 virtual void get2DDecomposition(
247 Primitive2DDecompositionVisitor& rVisitor,
248 const geometry::ViewInformation2D& rViewInformation) const override;
249
251 const basegfx::B2DPolyPolygon& getB2DPolyPolygon() const { return maPolyPolygon; }
252
254 virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
255
257 virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override;
258
260 virtual sal_uInt32 getPrimitive2DID() const override;
261};
262
263SlideBackgroundFillPrimitive2D::SlideBackgroundFillPrimitive2D(
264 const basegfx::B2DPolyPolygon& rPolyPolygon)
265: BufferedDecompositionPrimitive2D()
266, maPolyPolygon(rPolyPolygon)
267, maLastFill()
268{
269}
270
271void SlideBackgroundFillPrimitive2D::create2DDecomposition(
272 Primitive2DContainer& rContainer,
273 const geometry::ViewInformation2D& rViewInformation) const
274{
275 basegfx::B2DVector aPageSize;
276
277 // get fill from target Page, this will check for all needed things
278 // like MasterPage/relationships, etc. (see getMasterPageFillAttribute impl above)
280 getMasterPageFillAttribute(rViewInformation, aPageSize));
281
282 // if fill is on default (empty), nothing will be shown, we are done
283 if(aFill.isDefault())
284 return;
285
286 // Get PolygonRange of own local geometry
287 const basegfx::B2DRange aPolygonRange(getB2DPolyPolygon().getB2DRange());
288
289 // if local geometry is empty, nothing will be shown, we are done
290 if(aPolygonRange.isEmpty())
291 return;
292
293 // Get PageRange
294 const basegfx::B2DRange aPageRange(0.0, 0.0, aPageSize.getX(), aPageSize.getY());
295
296 // if local geometry does not overlap with PageRange, nothing will be shown, we are done
297 if(!aPageRange.overlaps(aPolygonRange))
298 return;
299
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(
308 getB2DPolyPolygon(), // geometry
309 aPageRange, // definition range
310 aFill,
311 aEmptyFillTransparenceGradient));
312
313 rContainer = Primitive2DContainer { aCreatedFill };
314}
315
316void SlideBackgroundFillPrimitive2D::get2DDecomposition(
317 Primitive2DDecompositionVisitor& rVisitor,
318 const geometry::ViewInformation2D& rViewInformation) const
319{
320 basegfx::B2DVector aPageSize;
322
323 if(!getBuffered2DDecomposition().empty())
324 {
325 aFill = getMasterPageFillAttribute(rViewInformation, aPageSize);
326
327 if(!(aFill == maLastFill))
328 {
329 // conditions of last local decomposition have changed, delete
330 const_cast< SlideBackgroundFillPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
331 }
332 }
333
334 if(getBuffered2DDecomposition().empty())
335 {
336 // remember last Fill
337 const_cast< SlideBackgroundFillPrimitive2D* >(this)->maLastFill = aFill;
338 }
339
340 // use parent implementation
341 BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
342}
343
344bool SlideBackgroundFillPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
345{
346 if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
347 {
348 const SlideBackgroundFillPrimitive2D& rCompare
349 = static_cast<const SlideBackgroundFillPrimitive2D&>(rPrimitive);
350
351 return getB2DPolyPolygon() == rCompare.getB2DPolyPolygon();
352 }
353
354 return false;
355}
356
357basegfx::B2DRange SlideBackgroundFillPrimitive2D::getB2DRange(
358 const geometry::ViewInformation2D& /*rViewInformation*/) const
359{
360 // return range
361 return basegfx::utils::getRange(getB2DPolyPolygon());
362}
363
364// provide unique ID
365sal_uInt32 SlideBackgroundFillPrimitive2D::getPrimitive2DID() const
366{
368}
369
370}; // end of anonymous namespace
371
372 class TransparencePrimitive2D;
373
375 const basegfx::B2DPolyPolygon& rPolyPolygon,
376 const attribute::SdrFillAttribute& rFill,
377 const attribute::FillGradientAttribute& rFillGradient)
378 {
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));
382
384 rPolyPolygon,
385 aRange,
386 rFill,
387 rFillGradient);
388 }
389
391 const basegfx::B2DPolyPolygon& rPolyPolygon,
392 const basegfx::B2DRange& rDefinitionRange,
393 const attribute::SdrFillAttribute& rFill,
394 const attribute::FillGradientAttribute& rFillGradient)
395 {
397 {
398 return Primitive2DReference();
399 }
400
401 // prepare fully scaled polygon
402 rtl::Reference<BasePrimitive2D> pNewFillPrimitive;
403
404 if(!rFill.getGradient().isDefault())
405 {
406 pNewFillPrimitive = new PolyPolygonGradientPrimitive2D(
407 rPolyPolygon,
408 rDefinitionRange,
409 rFill.getGradient());
410 }
411 else if(!rFill.getHatch().isDefault())
412 {
413 pNewFillPrimitive = new PolyPolygonHatchPrimitive2D(
414 rPolyPolygon,
415 rDefinitionRange,
416 rFill.getColor(),
417 rFill.getHatch());
418 }
419 else if(!rFill.getFillGraphic().isDefault())
420 {
421 pNewFillPrimitive = new PolyPolygonGraphicPrimitive2D(
422 rPolyPolygon,
423 rDefinitionRange,
424 rFill.getFillGraphic().createFillGraphicAttribute(rDefinitionRange));
425 }
426 else if(rFill.isSlideBackgroundFill())
427 {
428 // create needed Primitive2D representation for
429 // SlideBackgroundFill-mode
430 pNewFillPrimitive = new SlideBackgroundFillPrimitive2D(
431 rPolyPolygon);
432 }
433 else
434 {
435 pNewFillPrimitive = new PolyPolygonColorPrimitive2D(
436 rPolyPolygon,
437 rFill.getColor());
438 }
439
440 if(0.0 != rFill.getTransparence())
441 {
442 // create simpleTransparencePrimitive, add created fill primitive
443 Primitive2DContainer aContent { pNewFillPrimitive };
444 return Primitive2DReference(new UnifiedTransparencePrimitive2D(std::move(aContent), rFill.getTransparence()));
445 }
446 else if(!rFillGradient.isDefault())
447 {
448 // create sequence with created fill primitive
449 Primitive2DContainer aContent { pNewFillPrimitive };
450
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));
456 aRange,
457 rDefinitionRange,
458 rFillGradient));
459 Primitive2DContainer aAlpha { xRefB };
460
461 // create TransparencePrimitive2D using alpha and content
462 return Primitive2DReference(new TransparencePrimitive2D(std::move(aContent), std::move(aAlpha)));
463 }
464 else
465 {
466 // add to decomposition
467 return pNewFillPrimitive;
468 }
469 }
470
472 const basegfx::B2DPolygon& rPolygon,
473 const attribute::SdrLineAttribute& rLine,
475 {
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;
480
481 if(!rPolygon.isClosed() && !rStroke.isDefault())
482 {
485
486 // create data
487 pNewLinePrimitive = new PolygonStrokeArrowPrimitive2D(rPolygon, aLineAttribute, aStrokeAttribute, aStart, aEnd);
488 }
489 else
490 {
491 // create data
492 pNewLinePrimitive = new PolygonStrokePrimitive2D(rPolygon, aLineAttribute, std::move(aStrokeAttribute));
493 }
494
495 if(0.0 != rLine.getTransparence())
496 {
497 // create simpleTransparencePrimitive, add created fill primitive
498 Primitive2DContainer aContent { pNewLinePrimitive };
499 return Primitive2DReference(new UnifiedTransparencePrimitive2D(std::move(aContent), rLine.getTransparence()));
500 }
501 else
502 {
503 // add to decomposition
504 return pNewLinePrimitive;
505 }
506 }
507
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)
515 {
516 basegfx::B2DHomMatrix aAnchorTransform(rObjectTransform);
518
519 if(rText.isContour())
520 {
521 // contour text
522 if(!rStroke.isDefault() && 0.0 != rStroke.getWidth())
523 {
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);
529
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);
534 fabs(aScale.getX()), fabs(aScale.getY())));
535
536 // grow the polygon. To shrink, use negative value (half width)
537 aScaledUnitPolyPolygon = basegfx::utils::growInNormalDirection(aScaledUnitPolyPolygon, -(rStroke.getWidth() * 0.5));
538
539 // scale back to unit polygon
541 0.0 != aScale.getX() ? 1.0 / aScale.getX() : 1.0,
542 0.0 != aScale.getY() ? 1.0 / aScale.getY() : 1.0));
543
544 // create with unit polygon
545 pNew = new SdrContourTextPrimitive2D(
546 &rText.getSdrText(),
547 rText.getOutlinerParaObject(),
548 std::move(aScaledUnitPolyPolygon),
549 rObjectTransform);
550 }
551 else
552 {
553 // create with unit polygon
554 pNew = new SdrContourTextPrimitive2D(
555 &rText.getSdrText(),
556 rText.getOutlinerParaObject(),
557 rUnitPolyPolygon,
558 rObjectTransform);
559 }
560 }
561 else if(!rText.getSdrFormTextAttribute().isDefault())
562 {
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),
571 }
572 else
573 {
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);
579
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);
584
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));
594
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
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());
602
603 // apply mirroring
604 aAnchorTransform.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
605
606 // apply object's other transforms
607 aAnchorTransform = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(fShearX, fRotate, aTranslate)
608 * aAnchorTransform;
609
610 if(rText.isFitToSize())
611 {
612 // stretched text in range
613 pNew = new SdrStretchTextPrimitive2D(
614 &rText.getSdrText(),
615 rText.getOutlinerParaObject(),
616 aAnchorTransform,
617 rText.isFixedCellHeight());
618 }
619 else if(rText.isAutoFit())
620 {
621 // isotropically scaled text in range
622 pNew = new SdrAutoFitTextPrimitive2D(
623 &rText.getSdrText(),
624 rText.getOutlinerParaObject(),
625 aAnchorTransform,
626 bWordWrap);
627 }
628 else if( rText.isChainable() && !rText.isInEditMode() )
629 {
630 pNew = new SdrChainedTextPrimitive2D(
631 &rText.getSdrText(),
632 rText.getOutlinerParaObject(),
633 aAnchorTransform );
634 }
635 else // text in range
636 {
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);
648 }
649 }
650
651 OSL_ENSURE(pNew != nullptr, "createTextPrimitive: no text primitive created (!)");
652
653 if(rText.isBlink())
654 {
655 // prepare animation and primitive list
657 rText.getBlinkTextTiming(aAnimationList);
658
659 if(0.0 != aAnimationList.getDuration())
660 {
661 // create content sequence
662 Primitive2DReference xRefA(pNew);
663 Primitive2DContainer aContent { xRefA };
664
665 // create and add animated switch primitive
666 return Primitive2DReference(new AnimatedBlinkPrimitive2D(aAnimationList, std::move(aContent)));
667 }
668 else
669 {
670 // add to decomposition
671 return Primitive2DReference(pNew);
672 }
673 }
674
675 if(rText.isScroll())
676 {
677 // suppress scroll when FontWork
679 {
680 // get scroll direction
681 const SdrTextAniDirection eDirection(rText.getSdrText().GetObject().GetTextAniDirection());
682 const bool bHorizontal(SdrTextAniDirection::Left == eDirection || SdrTextAniDirection::Right == eDirection);
683
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);
688
689 // build transform from scaled only to full AnchorTransform and inverse
691 fShearX, fRotate, aTranslate));
692 basegfx::B2DHomMatrix aISRT(aSRT);
693 aISRT.invert();
694
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();
699
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;
703
704 // get range
705 const basegfx::B2DRange aScaledRange(pNew->getB2DRange(aViewInformation2D));
706
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());
712
713 if(bHorizontal)
714 {
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);
719 }
720 else
721 {
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());
726 }
727
728 aLeft *= aSRT;
729 aRight *= aSRT;
730
731 // prepare animation list
733
734 if(bHorizontal)
735 {
736 rText.getScrollTextTiming(aAnimationList, aScale.getX(), aScaledRange.getWidth());
737 }
738 else
739 {
740 rText.getScrollTextTiming(aAnimationList, aScale.getY(), aScaledRange.getHeight());
741 }
742
743 if(0.0 != aAnimationList.getDuration())
744 {
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();
752
753 // create a new animatedInterpolatePrimitive and add it
754 Primitive2DReference xRefA(new AnimatedInterpolatePrimitive2D({ aLeft, aRight }, aAnimationList, std::move(aAnimSequence)));
755 Primitive2DContainer aContent { xRefA };
756
757 // scrolling needs an encapsulating clipping primitive
758 const basegfx::B2DRange aClipRange(aClipTopLeft, aClipBottomRight);
760 aClipPolygon.transform(aSRT);
761 return Primitive2DReference(new MaskPrimitive2D(basegfx::B2DPolyPolygon(aClipPolygon), std::move(aContent)));
762 }
763 else
764 {
765 // add to decomposition
766 return Primitive2DReference(pNew);
767 }
768 }
769 }
770
771 if(rText.isInEditMode())
772 {
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 };
778
779 // create and add TextHierarchyEditPrimitive2D primitive
780 return Primitive2DReference(new TextHierarchyEditPrimitive2D(std::move(aContent)));
781 }
782 else
783 {
784 // add to decomposition
785 return pNew;
786 }
787 }
788
790 Primitive2DContainer&& rContent,
791 const attribute::SdrShadowAttribute& rShadow,
792 const basegfx::B2DHomMatrix& rObjectMatrix,
793 const Primitive2DContainer* pContentForShadow)
794 {
795 if(rContent.empty())
796 return std::move(rContent);
797
798 basegfx::B2DHomMatrix aShadowOffset;
799
800 if(rShadow.getSize().getX() != 100000)
801 {
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);
812 }
813
814 aShadowOffset.translate(rShadow.getOffset().getX(), rShadow.getOffset().getY());
815
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;
824 });
825 if (nContentWithTransparence == 0)
826 {
827 Primitive2DContainer aRetval(2);
828 aRetval[0] = Primitive2DReference(
830 aShadowOffset,
831 rShadow.getColor(),
832 rShadow.getBlur(),
833 Primitive2DContainer(pContentForShadow ? *pContentForShadow : rContent)));
834
835 if (0.0 != rShadow.getTransparence())
836 {
837 // create SimpleTransparencePrimitive2D
838 Primitive2DContainer aTempContent{ aRetval[0] };
839
840 aRetval[0] = Primitive2DReference(
842 std::move(aTempContent),
843 rShadow.getTransparence()));
844 }
845
846 aRetval[1] = Primitive2DReference(new GroupPrimitive2D(std::move(rContent)));
847 return aRetval;
848 }
849
850 Primitive2DContainer aRetval;
851 for (const auto& xChild : rContentForShadow)
852 {
853 double fChildTransparence = 0.0;
854 auto pChild = dynamic_cast<BufferedDecompositionPrimitive2D*>(xChild.get());
855 if (pChild)
856 {
857 fChildTransparence = pChild->getTransparenceForShadow();
858 fChildTransparence /= 100;
859 }
860 aRetval.push_back(Primitive2DReference(
861 new ShadowPrimitive2D(aShadowOffset, rShadow.getColor(), rShadow.getBlur(),
862 Primitive2DContainer({ xChild }))));
863 if (rShadow.getTransparence() != 0.0 || fChildTransparence != 0.0)
864 {
865 Primitive2DContainer aTempContent{ aRetval.back() };
866
867 double fChildAlpha = 1.0 - fChildTransparence;
868 double fShadowAlpha = 1.0 - rShadow.getTransparence();
869 double fTransparence = 1.0 - fChildAlpha * fShadowAlpha;
871 std::move(aTempContent), fTransparence));
872 }
873 }
874
875 aRetval.push_back(
876 Primitive2DReference(new GroupPrimitive2D(std::move(rContent))));
877 return aRetval;
878 }
879
881 Primitive2DContainer&& rContent,
882 const attribute::SdrGlowAttribute& rGlow)
883 {
884 if(rContent.empty())
885 return std::move(rContent);
886 Primitive2DContainer aRetval(2);
887 aRetval[0] = Primitive2DReference(
888 new GlowPrimitive2D(rGlow.getColor(), rGlow.getRadius(), Primitive2DContainer(rContent)));
890 return aRetval;
891 }
892
894 sal_Int32 nRadius)
895 {
896 if (aContent.empty() || !nRadius)
897 return std::move(aContent);
898 Primitive2DContainer aRetval(1);
899 aRetval[0] = Primitive2DReference(new SoftEdgePrimitive2D(nRadius, std::move(aContent)));
900 return aRetval;
901 }
902
903} // end of namespace
904
905/* 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:379
SdrTextAniDirection GetTextAniDirection() const
Definition: svdotext.cxx:1844
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
model::RectangleAlignment getAlignment() 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)
RectangleAlignment
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)