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
20 #include <sdr/primitive2d/sdrdecompositiontools.hxx>
21 #include <drawinglayer/primitive2d/baseprimitive2d.hxx>
22 #include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
23 #include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx>
24 #include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
25 #include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
26 #include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
27 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
28 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
29 #include <basegfx/polygon/b2dpolypolygontools.hxx>
30 #include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
31 #include <drawinglayer/attribute/strokeattribute.hxx>
32 #include <drawinglayer/attribute/linestartendattribute.hxx>
33 #include <drawinglayer/primitive2d/polygonprimitive2d.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
53
54 using namespace com::sun::star;
55
56
57 namespace drawinglayer::primitive2d
58 {
59 namespace
60 {
61 // See also: SdrTextObj::AdjustRectToTextDistance
getTextAnchorRange(const attribute::SdrTextAttribute & rText,const basegfx::B2DRange & rSnapRange)62 basegfx::B2DRange getTextAnchorRange(const attribute::SdrTextAttribute& rText,
63 const basegfx::B2DRange& rSnapRange)
64 {
65 // Take vertical text orientation into account when deciding
66 // which dimension is its width, and which is its height
67 const OutlinerParaObject& rOutlinerParaObj = rText.getOutlinerParaObject();
68 const bool bVerticalWriting(rOutlinerParaObj.IsVertical());
69 const double fWidthForText = bVerticalWriting ? rSnapRange.getHeight() : rSnapRange.getWidth();
70 // create a range describing the wanted text position and size (aTextAnchorRange). This
71 // means to use the text distance values here
72 // If the margin is larger than the entire width of the text area, then limit the
73 // margin.
74 const double fTextLeftDistance
75 = std::min(static_cast<double>(rText.getTextLeftDistance()), fWidthForText);
76 const double nTextRightDistance
77 = std::min(static_cast<double>(rText.getTextRightDistance()), fWidthForText);
78 double fDistanceForTextL, fDistanceForTextT, fDistanceForTextR, fDistanceForTextB;
79 if (!bVerticalWriting)
80 {
81 fDistanceForTextL = fTextLeftDistance;
82 fDistanceForTextT = rText.getTextUpperDistance();
83 fDistanceForTextR = nTextRightDistance;
84 fDistanceForTextB = rText.getTextLowerDistance();
85 }
86 else if (rOutlinerParaObj.IsTopToBottom())
87 {
88 fDistanceForTextL = rText.getTextLowerDistance();
89 fDistanceForTextT = fTextLeftDistance;
90 fDistanceForTextR = rText.getTextUpperDistance();
91 fDistanceForTextB = nTextRightDistance;
92 }
93 else
94 {
95 fDistanceForTextL = rText.getTextUpperDistance();
96 fDistanceForTextT = nTextRightDistance;
97 fDistanceForTextR = rText.getTextLowerDistance();
98 fDistanceForTextB = fTextLeftDistance;
99 }
100 const basegfx::B2DPoint aTopLeft(rSnapRange.getMinX() + fDistanceForTextL,
101 rSnapRange.getMinY() + fDistanceForTextT);
102 const basegfx::B2DPoint aBottomRight(rSnapRange.getMaxX() - fDistanceForTextR,
103 rSnapRange.getMaxY() - fDistanceForTextB);
104 basegfx::B2DRange aAnchorRange;
105 aAnchorRange.expand(aTopLeft);
106 aAnchorRange.expand(aBottomRight);
107
108 // If the shape has no width, then don't attempt to break the text into multiple
109 // lines, not a single character would satisfy a zero width requirement.
110 // SdrTextObj::impDecomposeBlockTextPrimitive() uses the same constant to
111 // effectively set no limits.
112 if (!bVerticalWriting && aAnchorRange.getWidth() == 0)
113 {
114 aAnchorRange.expand(basegfx::B2DPoint(aTopLeft.getX() - 1000000, aTopLeft.getY()));
115 aAnchorRange.expand(basegfx::B2DPoint(aBottomRight.getX() + 1000000, aBottomRight.getY()));
116 }
117 else if (bVerticalWriting && aAnchorRange.getHeight() == 0)
118 {
119 aAnchorRange.expand(basegfx::B2DPoint(aTopLeft.getX(), aTopLeft.getY() - 1000000));
120 aAnchorRange.expand(basegfx::B2DPoint(aBottomRight.getX(), aBottomRight.getY() + 1000000));
121 }
122 return aAnchorRange;
123 }
124 };
125
126 class TransparencePrimitive2D;
127
createPolyPolygonFillPrimitive(const basegfx::B2DPolyPolygon & rPolyPolygon,const attribute::SdrFillAttribute & rFill,const attribute::FillGradientAttribute & rFillGradient)128 Primitive2DReference createPolyPolygonFillPrimitive(
129 const basegfx::B2DPolyPolygon& rPolyPolygon,
130 const attribute::SdrFillAttribute& rFill,
131 const attribute::FillGradientAttribute& rFillGradient)
132 {
133 // when we have no given definition range, use the range of the given geometry
134 // also for definition (simplest case)
135 const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon));
136
137 return createPolyPolygonFillPrimitive(
138 rPolyPolygon,
139 aRange,
140 rFill,
141 rFillGradient);
142 }
143
createPolyPolygonFillPrimitive(const basegfx::B2DPolyPolygon & rPolyPolygon,const basegfx::B2DRange & rDefinitionRange,const attribute::SdrFillAttribute & rFill,const attribute::FillGradientAttribute & rFillGradient)144 Primitive2DReference createPolyPolygonFillPrimitive(
145 const basegfx::B2DPolyPolygon& rPolyPolygon,
146 const basegfx::B2DRange& rDefinitionRange,
147 const attribute::SdrFillAttribute& rFill,
148 const attribute::FillGradientAttribute& rFillGradient)
149 {
150 if(basegfx::fTools::moreOrEqual(rFill.getTransparence(), 1.0))
151 {
152 return Primitive2DReference();
153 }
154
155 // prepare fully scaled polygon
156 rtl::Reference<BasePrimitive2D> pNewFillPrimitive;
157
158 if(!rFill.getGradient().isDefault())
159 {
160 pNewFillPrimitive = new PolyPolygonGradientPrimitive2D(
161 rPolyPolygon,
162 rDefinitionRange,
163 rFill.getGradient());
164 }
165 else if(!rFill.getHatch().isDefault())
166 {
167 pNewFillPrimitive = new PolyPolygonHatchPrimitive2D(
168 rPolyPolygon,
169 rDefinitionRange,
170 rFill.getColor(),
171 rFill.getHatch());
172 }
173 else if(!rFill.getFillGraphic().isDefault())
174 {
175 pNewFillPrimitive = new PolyPolygonGraphicPrimitive2D(
176 rPolyPolygon,
177 rDefinitionRange,
178 rFill.getFillGraphic().createFillGraphicAttribute(rDefinitionRange));
179 }
180 else
181 {
182 pNewFillPrimitive = new PolyPolygonColorPrimitive2D(
183 rPolyPolygon,
184 rFill.getColor());
185 }
186
187 if(0.0 != rFill.getTransparence())
188 {
189 // create simpleTransparencePrimitive, add created fill primitive
190 const Primitive2DContainer aContent { pNewFillPrimitive };
191 return Primitive2DReference(new UnifiedTransparencePrimitive2D(aContent, rFill.getTransparence()));
192 }
193 else if(!rFillGradient.isDefault())
194 {
195 // create sequence with created fill primitive
196 const Primitive2DContainer aContent { pNewFillPrimitive };
197
198 // create FillGradientPrimitive2D for transparence and add to new sequence
199 // fillGradientPrimitive is enough here (compared to PolyPolygonGradientPrimitive2D) since float transparence will be masked anyways
200 const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon));
201 const Primitive2DReference xRefB(
202 new FillGradientPrimitive2D(
203 aRange,
204 rDefinitionRange,
205 rFillGradient));
206 const Primitive2DContainer aAlpha { xRefB };
207
208 // create TransparencePrimitive2D using alpha and content
209 return Primitive2DReference(new TransparencePrimitive2D(aContent, aAlpha));
210 }
211 else
212 {
213 // add to decomposition
214 return Primitive2DReference(pNewFillPrimitive);
215 }
216 }
217
createPolygonLinePrimitive(const basegfx::B2DPolygon & rPolygon,const attribute::SdrLineAttribute & rLine,const attribute::SdrLineStartEndAttribute & rStroke)218 Primitive2DReference createPolygonLinePrimitive(
219 const basegfx::B2DPolygon& rPolygon,
220 const attribute::SdrLineAttribute& rLine,
221 const attribute::SdrLineStartEndAttribute& rStroke)
222 {
223 // create line and stroke attribute
224 const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin(), rLine.getCap());
225 const attribute::StrokeAttribute aStrokeAttribute(rLine.getDotDashArray(), rLine.getFullDotDashLen());
226 rtl::Reference<BasePrimitive2D> pNewLinePrimitive;
227
228 if(!rPolygon.isClosed() && !rStroke.isDefault())
229 {
230 attribute::LineStartEndAttribute aStart(rStroke.getStartWidth(), rStroke.getStartPolyPolygon(), rStroke.isStartCentered());
231 attribute::LineStartEndAttribute aEnd(rStroke.getEndWidth(), rStroke.getEndPolyPolygon(), rStroke.isEndCentered());
232
233 // create data
234 pNewLinePrimitive = new PolygonStrokeArrowPrimitive2D(rPolygon, aLineAttribute, aStrokeAttribute, aStart, aEnd);
235 }
236 else
237 {
238 // create data
239 pNewLinePrimitive = new PolygonStrokePrimitive2D(rPolygon, aLineAttribute, aStrokeAttribute);
240 }
241
242 if(0.0 != rLine.getTransparence())
243 {
244 // create simpleTransparencePrimitive, add created fill primitive
245 const Primitive2DContainer aContent { pNewLinePrimitive };
246 return Primitive2DReference(new UnifiedTransparencePrimitive2D(aContent, rLine.getTransparence()));
247 }
248 else
249 {
250 // add to decomposition
251 return Primitive2DReference(pNewLinePrimitive);
252 }
253 }
254
createTextPrimitive(const basegfx::B2DPolyPolygon & rUnitPolyPolygon,const basegfx::B2DHomMatrix & rObjectTransform,const attribute::SdrTextAttribute & rText,const attribute::SdrLineAttribute & rStroke,bool bCellText,bool bWordWrap)255 Primitive2DReference createTextPrimitive(
256 const basegfx::B2DPolyPolygon& rUnitPolyPolygon,
257 const basegfx::B2DHomMatrix& rObjectTransform,
258 const attribute::SdrTextAttribute& rText,
259 const attribute::SdrLineAttribute& rStroke,
260 bool bCellText,
261 bool bWordWrap)
262 {
263 basegfx::B2DHomMatrix aAnchorTransform(rObjectTransform);
264 rtl::Reference<SdrTextPrimitive2D> pNew;
265
266 if(rText.isContour())
267 {
268 // contour text
269 if(!rStroke.isDefault() && 0.0 != rStroke.getWidth())
270 {
271 // take line width into account and shrink contour polygon accordingly
272 // decompose to get scale
273 basegfx::B2DVector aScale, aTranslate;
274 double fRotate, fShearX;
275 rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX);
276
277 // scale outline to object's size to allow growing with value relative to that size
278 // and also to keep aspect ratio
279 basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon);
280 aScaledUnitPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix(
281 fabs(aScale.getX()), fabs(aScale.getY())));
282
283 // grow the polygon. To shrink, use negative value (half width)
284 aScaledUnitPolyPolygon = basegfx::utils::growInNormalDirection(aScaledUnitPolyPolygon, -(rStroke.getWidth() * 0.5));
285
286 // scale back to unit polygon
287 aScaledUnitPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix(
288 0.0 != aScale.getX() ? 1.0 / aScale.getX() : 1.0,
289 0.0 != aScale.getY() ? 1.0 / aScale.getY() : 1.0));
290
291 // create with unit polygon
292 pNew = new SdrContourTextPrimitive2D(
293 &rText.getSdrText(),
294 rText.getOutlinerParaObject(),
295 aScaledUnitPolyPolygon,
296 rObjectTransform);
297 }
298 else
299 {
300 // create with unit polygon
301 pNew = new SdrContourTextPrimitive2D(
302 &rText.getSdrText(),
303 rText.getOutlinerParaObject(),
304 rUnitPolyPolygon,
305 rObjectTransform);
306 }
307 }
308 else if(!rText.getSdrFormTextAttribute().isDefault())
309 {
310 // text on path, use scaled polygon
311 basegfx::B2DPolyPolygon aScaledPolyPolygon(rUnitPolyPolygon);
312 aScaledPolyPolygon.transform(rObjectTransform);
313 pNew = new SdrPathTextPrimitive2D(
314 &rText.getSdrText(),
315 rText.getOutlinerParaObject(),
316 aScaledPolyPolygon,
317 rText.getSdrFormTextAttribute());
318 }
319 else
320 {
321 // rObjectTransform is the whole SdrObject transformation from unit rectangle
322 // to its size and position. Decompose to allow working with single values.
323 basegfx::B2DVector aScale, aTranslate;
324 double fRotate, fShearX;
325 rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX);
326
327 // extract mirroring
328 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
329 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
330 aScale = basegfx::absolute(aScale);
331
332 // Get the real size, since polygon outline and scale
333 // from the object transformation may vary (e.g. ellipse segments)
334 basegfx::B2DHomMatrix aJustScaleTransform;
335 aJustScaleTransform.set(0, 0, aScale.getX());
336 aJustScaleTransform.set(1, 1, aScale.getY());
337 basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon);
338 aScaledUnitPolyPolygon.transform(aJustScaleTransform);
339 const basegfx::B2DRange aTextAnchorRange
340 = getTextAnchorRange(rText, basegfx::utils::getRange(aScaledUnitPolyPolygon));
341
342 // now create a transformation from this basic range (aTextAnchorRange)
343 // #i121494# if we have no scale use at least 1.0 to have a carrier e.g. for
344 // mirror values, else these will get lost
345 aAnchorTransform = basegfx::utils::createScaleTranslateB2DHomMatrix(
346 basegfx::fTools::equalZero(aTextAnchorRange.getWidth()) ? 1.0 : aTextAnchorRange.getWidth(),
347 basegfx::fTools::equalZero(aTextAnchorRange.getHeight()) ? 1.0 : aTextAnchorRange.getHeight(),
348 aTextAnchorRange.getMinX(), aTextAnchorRange.getMinY());
349
350 // apply mirroring
351 aAnchorTransform.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
352
353 // apply object's other transforms
354 aAnchorTransform = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(fShearX, fRotate, aTranslate)
355 * aAnchorTransform;
356
357 if(rText.isFitToSize())
358 {
359 // stretched text in range
360 pNew = new SdrStretchTextPrimitive2D(
361 &rText.getSdrText(),
362 rText.getOutlinerParaObject(),
363 aAnchorTransform,
364 rText.isFixedCellHeight());
365 }
366 else if(rText.isAutoFit())
367 {
368 // isotropically scaled text in range
369 pNew = new SdrAutoFitTextPrimitive2D(
370 &rText.getSdrText(),
371 rText.getOutlinerParaObject(),
372 aAnchorTransform,
373 bWordWrap);
374 }
375 else if( rText.isChainable() && !rText.isInEditMode() )
376 {
377 pNew = new SdrChainedTextPrimitive2D(
378 &rText.getSdrText(),
379 rText.getOutlinerParaObject(),
380 aAnchorTransform );
381 }
382 else // text in range
383 {
384 // build new primitive
385 pNew = new SdrBlockTextPrimitive2D(
386 &rText.getSdrText(),
387 rText.getOutlinerParaObject(),
388 aAnchorTransform,
389 rText.getSdrTextHorzAdjust(),
390 rText.getSdrTextVertAdjust(),
391 rText.isFixedCellHeight(),
392 rText.isScroll(),
393 bCellText,
394 bWordWrap);
395 }
396 }
397
398 OSL_ENSURE(pNew != nullptr, "createTextPrimitive: no text primitive created (!)");
399
400 if(rText.isBlink())
401 {
402 // prepare animation and primitive list
403 drawinglayer::animation::AnimationEntryList aAnimationList;
404 rText.getBlinkTextTiming(aAnimationList);
405
406 if(0.0 != aAnimationList.getDuration())
407 {
408 // create content sequence
409 const Primitive2DReference xRefA(pNew);
410 const Primitive2DContainer aContent { xRefA };
411
412 // create and add animated switch primitive
413 return Primitive2DReference(new AnimatedBlinkPrimitive2D(aAnimationList, aContent));
414 }
415 else
416 {
417 // add to decomposition
418 return Primitive2DReference(pNew);
419 }
420 }
421
422 if(rText.isScroll())
423 {
424 // suppress scroll when FontWork
425 if(rText.getSdrFormTextAttribute().isDefault())
426 {
427 // get scroll direction
428 const SdrTextAniDirection eDirection(rText.getSdrText().GetObject().GetTextAniDirection());
429 const bool bHorizontal(SdrTextAniDirection::Left == eDirection || SdrTextAniDirection::Right == eDirection);
430
431 // decompose to get separated values for the scroll box
432 basegfx::B2DVector aScale, aTranslate;
433 double fRotate, fShearX;
434 aAnchorTransform.decompose(aScale, aTranslate, fRotate, fShearX);
435
436 // build transform from scaled only to full AnchorTransform and inverse
437 const basegfx::B2DHomMatrix aSRT(basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
438 fShearX, fRotate, aTranslate));
439 basegfx::B2DHomMatrix aISRT(aSRT);
440 aISRT.invert();
441
442 // bring the primitive back to scaled only and get scaled range, create new clone for this
443 rtl::Reference<SdrTextPrimitive2D> pNew2 = pNew->createTransformedClone(aISRT);
444 OSL_ENSURE(pNew2, "createTextPrimitive: Could not create transformed clone of text primitive (!)");
445 pNew = pNew2.get();
446
447 // create neutral geometry::ViewInformation2D for local range and decompose calls. This is okay
448 // since the decompose is view-independent
449 const uno::Sequence< beans::PropertyValue > xViewParameters;
450 geometry::ViewInformation2D aViewInformation2D(xViewParameters);
451
452 // get range
453 const basegfx::B2DRange aScaledRange(pNew->getB2DRange(aViewInformation2D));
454
455 // create left outside and right outside transformations. Also take care
456 // of the clip rectangle
457 basegfx::B2DHomMatrix aLeft, aRight;
458 basegfx::B2DPoint aClipTopLeft(0.0, 0.0);
459 basegfx::B2DPoint aClipBottomRight(aScale.getX(), aScale.getY());
460
461 if(bHorizontal)
462 {
463 aClipTopLeft.setY(aScaledRange.getMinY());
464 aClipBottomRight.setY(aScaledRange.getMaxY());
465 aLeft.translate(-aScaledRange.getMaxX(), 0.0);
466 aRight.translate(aScale.getX() - aScaledRange.getMinX(), 0.0);
467 }
468 else
469 {
470 aClipTopLeft.setX(aScaledRange.getMinX());
471 aClipBottomRight.setX(aScaledRange.getMaxX());
472 aLeft.translate(0.0, -aScaledRange.getMaxY());
473 aRight.translate(0.0, aScale.getY() - aScaledRange.getMinY());
474 }
475
476 aLeft *= aSRT;
477 aRight *= aSRT;
478
479 // prepare animation list
480 drawinglayer::animation::AnimationEntryList aAnimationList;
481
482 if(bHorizontal)
483 {
484 rText.getScrollTextTiming(aAnimationList, aScale.getX(), aScaledRange.getWidth());
485 }
486 else
487 {
488 rText.getScrollTextTiming(aAnimationList, aScale.getY(), aScaledRange.getHeight());
489 }
490
491 if(0.0 != aAnimationList.getDuration())
492 {
493 // create a new Primitive2DContainer containing the animated text in its scaled only state.
494 // use the decomposition to force to simple text primitives, those will no longer
495 // need the outliner for formatting (alternatively it is also possible to just add
496 // pNew to aNewPrimitiveSequence)
497 Primitive2DContainer aAnimSequence;
498 pNew->get2DDecomposition(aAnimSequence, aViewInformation2D);
499 pNew.clear();
500
501 // create a new animatedInterpolatePrimitive and add it
502 std::vector< basegfx::B2DHomMatrix > aMatrixStack;
503 aMatrixStack.push_back(aLeft);
504 aMatrixStack.push_back(aRight);
505 const Primitive2DReference xRefA(new AnimatedInterpolatePrimitive2D(aMatrixStack, aAnimationList, aAnimSequence));
506 const Primitive2DContainer aContent { xRefA };
507
508 // scrolling needs an encapsulating clipping primitive
509 const basegfx::B2DRange aClipRange(aClipTopLeft, aClipBottomRight);
510 basegfx::B2DPolygon aClipPolygon(basegfx::utils::createPolygonFromRect(aClipRange));
511 aClipPolygon.transform(aSRT);
512 return Primitive2DReference(new MaskPrimitive2D(basegfx::B2DPolyPolygon(aClipPolygon), aContent));
513 }
514 else
515 {
516 // add to decomposition
517 return Primitive2DReference(pNew);
518 }
519 }
520 }
521
522 if(rText.isInEditMode())
523 {
524 // #i97628#
525 // encapsulate with TextHierarchyEditPrimitive2D to allow renderers
526 // to suppress actively edited content if needed
527 const Primitive2DReference xRefA(pNew);
528 const Primitive2DContainer aContent { xRefA };
529
530 // create and add TextHierarchyEditPrimitive2D primitive
531 return Primitive2DReference(new TextHierarchyEditPrimitive2D(aContent));
532 }
533 else
534 {
535 // add to decomposition
536 return pNew;
537 }
538 }
539
createEmbeddedShadowPrimitive(const Primitive2DContainer & rContent,const attribute::SdrShadowAttribute & rShadow,const basegfx::B2DHomMatrix & rObjectMatrix,const Primitive2DContainer * pContentForShadow)540 Primitive2DContainer createEmbeddedShadowPrimitive(
541 const Primitive2DContainer& rContent,
542 const attribute::SdrShadowAttribute& rShadow,
543 const basegfx::B2DHomMatrix& rObjectMatrix,
544 const Primitive2DContainer* pContentForShadow)
545 {
546 if(!rContent.empty())
547 {
548 basegfx::B2DHomMatrix aShadowOffset;
549
550 {
551 if(rShadow.getSize().getX() != 100000)
552 {
553 basegfx::B2DTuple aScale;
554 basegfx::B2DTuple aTranslate;
555 double fRotate = 0;
556 double fShearX = 0;
557 rObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
558 // Scale the shadow
559 double nTranslateX = aTranslate.getX();
560 double nTranslateY = aTranslate.getY();
561
562 // The origin for scaling is the top left corner by default. A negative
563 // shadow offset changes the origin.
564 if (rShadow.getOffset().getX() < 0)
565 nTranslateX += aScale.getX();
566 if (rShadow.getOffset().getY() < 0)
567 nTranslateY += aScale.getY();
568
569 aShadowOffset.translate(-nTranslateX, -nTranslateY);
570 aShadowOffset.scale(rShadow.getSize().getX() * 0.00001, rShadow.getSize().getY() * 0.00001);
571 aShadowOffset.translate(nTranslateX, nTranslateY);
572 }
573
574 aShadowOffset.translate(rShadow.getOffset().getX(), rShadow.getOffset().getY());
575 }
576
577 // create shadow primitive and add content
578 const Primitive2DContainer& rContentForShadow
579 = pContentForShadow ? *pContentForShadow : rContent;
580 int nContentWithTransparence = std::count_if(
581 rContentForShadow.begin(), rContentForShadow.end(),
582 [](const Primitive2DReference& xChild) {
583 auto pChild = dynamic_cast<BufferedDecompositionPrimitive2D*>(xChild.get());
584 return pChild && pChild->getTransparenceForShadow() != 0;
585 });
586 if (nContentWithTransparence == 0)
587 {
588 Primitive2DContainer aRetval(2);
589 aRetval[0] = Primitive2DReference(
590 new ShadowPrimitive2D(
591 aShadowOffset,
592 rShadow.getColor(),
593 rShadow.getBlur(),
594 (pContentForShadow ? *pContentForShadow : rContent)));
595
596 if(0.0 != rShadow.getTransparence())
597 {
598 // create SimpleTransparencePrimitive2D
599 const Primitive2DContainer aTempContent { aRetval[0] };
600
601 aRetval[0] = Primitive2DReference(
602 new UnifiedTransparencePrimitive2D(
603 aTempContent,
604 rShadow.getTransparence()));
605 }
606
607 aRetval[1] = Primitive2DReference(new GroupPrimitive2D(rContent));
608 return aRetval;
609 }
610
611 Primitive2DContainer aRetval;
612 for (const auto& xChild : rContentForShadow)
613 {
614 double fChildTransparence = 0.0;
615 auto pChild = dynamic_cast<BufferedDecompositionPrimitive2D*>(xChild.get());
616 if (pChild)
617 {
618 fChildTransparence = pChild->getTransparenceForShadow();
619 fChildTransparence /= 100;
620 }
621 aRetval.push_back(Primitive2DReference(
622 new ShadowPrimitive2D(aShadowOffset, rShadow.getColor(), rShadow.getBlur(),
623 { xChild })));
624 if (rShadow.getTransparence() != 0.0 || fChildTransparence != 0.0)
625 {
626 Primitive2DContainer aTempContent{ aRetval.back() };
627
628 double fChildAlpha = 1.0 - fChildTransparence;
629 double fShadowAlpha = 1.0 - rShadow.getTransparence();
630 double fTransparence = 1.0 - fChildAlpha * fShadowAlpha;
631 aRetval.back() = Primitive2DReference(new UnifiedTransparencePrimitive2D(
632 std::move(aTempContent), fTransparence));
633 }
634 }
635
636 aRetval.push_back(
637 Primitive2DReference(new GroupPrimitive2D(rContent)));
638 return aRetval;
639 }
640 else
641 {
642 return rContent;
643 }
644 }
645
createEmbeddedGlowPrimitive(const Primitive2DContainer & rContent,const attribute::SdrGlowAttribute & rGlow)646 Primitive2DContainer createEmbeddedGlowPrimitive(
647 const Primitive2DContainer& rContent,
648 const attribute::SdrGlowAttribute& rGlow)
649 {
650 if(rContent.empty())
651 return rContent;
652 Primitive2DContainer aRetval(2);
653 aRetval[0] = Primitive2DReference(
654 new GlowPrimitive2D(rGlow.getColor(), rGlow.getRadius(), rContent));
655 aRetval[1] = Primitive2DReference(new GroupPrimitive2D(rContent));
656 return aRetval;
657 }
658
createEmbeddedSoftEdgePrimitive(const Primitive2DContainer & rContent,sal_Int32 nRadius)659 Primitive2DContainer createEmbeddedSoftEdgePrimitive(const Primitive2DContainer& rContent,
660 sal_Int32 nRadius)
661 {
662 if (rContent.empty() || !nRadius)
663 return rContent;
664 Primitive2DContainer aRetval(1);
665 aRetval[0] = Primitive2DReference(new SoftEdgePrimitive2D(nRadius, rContent));
666 return aRetval;
667 }
668
669 } // end of namespace
670
671 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
672