LibreOffice Module svx (master) 1
svdotextdecomposition.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
20#include <svx/compatflags.hxx>
21#include <svx/svdetc.hxx>
22#include <svx/svdoutl.hxx>
23#include <svx/svdpage.hxx>
24#include <svx/svdotext.hxx>
25#include <svx/svdmodel.hxx>
26#include <svx/sdasitm.hxx>
27#include <textchain.hxx>
28#include <textchainflow.hxx>
29#include <svx/sdtacitm.hxx>
30#include <svx/sdtayitm.hxx>
31#include <svx/sdtaiitm.hxx>
32#include <svx/sdtaaitm.hxx>
33#include <svx/xfillit0.hxx>
34#include <svx/xbtmpit.hxx>
40#include <editeng/eeitem.hxx>
41#include <editeng/editstat.hxx>
42#include <tools/helpers.hxx>
43#include <svl/itemset.hxx>
46#include <vcl/svapp.hxx>
48#include <editeng/svxenum.hxx>
49#include <editeng/flditem.hxx>
56#include <svx/unoapi.hxx>
58#include <editeng/outlobj.hxx>
60#include <sal/log.hxx>
61#include <osl/diagnose.h>
62
63using namespace com::sun::star;
64
65// helpers
66
67namespace
68{
69 class impTextBreakupHandler
70 {
71 private:
75
76 SdrOutliner& mrOutliner;
77 basegfx::B2DHomMatrix maNewTransformA;
78 basegfx::B2DHomMatrix maNewTransformB;
79
80 // the visible area for contour text decomposition
81 basegfx::B2DVector maScale;
82
83 // ClipRange for BlockText decomposition; only text portions completely
84 // inside are to be accepted, so this is different from geometric clipping
85 // (which would allow e.g. upper parts of portions to remain). Only used for
86 // BlockText (see there)
87 basegfx::B2DRange maClipRange;
88
89 DECL_LINK(decomposeContourTextPrimitive, DrawPortionInfo*, void);
90 DECL_LINK(decomposeBlockTextPrimitive, DrawPortionInfo*, void);
91 DECL_LINK(decomposeStretchTextPrimitive, DrawPortionInfo*, void);
92
93 DECL_LINK(decomposeContourBulletPrimitive, DrawBulletInfo*, void);
94 DECL_LINK(decomposeBlockBulletPrimitive, DrawBulletInfo*, void);
95 DECL_LINK(decomposeStretchBulletPrimitive, DrawBulletInfo*, void);
96
97 void impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo);
99 void impFlushTextPortionPrimitivesToLinePrimitives();
100 void impFlushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara);
101 void impHandleDrawPortionInfo(const DrawPortionInfo& rInfo);
102 void impHandleDrawBulletInfo(const DrawBulletInfo& rInfo);
103
104 public:
105 explicit impTextBreakupHandler(SdrOutliner& rOutliner)
106 : mrOutliner(rOutliner)
107 {
108 }
109
110 void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, const basegfx::B2DVector& rScale)
111 {
112 maScale = rScale;
113 maNewTransformA = rNewTransformA;
114 maNewTransformB = rNewTransformB;
115 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeContourTextPrimitive));
116 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeContourBulletPrimitive));
117 mrOutliner.StripPortions();
120 }
121
122 void decomposeBlockTextPrimitive(
123 const basegfx::B2DHomMatrix& rNewTransformA,
124 const basegfx::B2DHomMatrix& rNewTransformB,
125 const basegfx::B2DRange& rClipRange)
126 {
127 maNewTransformA = rNewTransformA;
128 maNewTransformB = rNewTransformB;
129 maClipRange = rClipRange;
130 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeBlockTextPrimitive));
131 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeBlockBulletPrimitive));
132 mrOutliner.StripPortions();
135 }
136
137 void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB)
138 {
139 maNewTransformA = rNewTransformA;
140 maNewTransformB = rNewTransformB;
141 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeStretchTextPrimitive));
142 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeStretchBulletPrimitive));
143 mrOutliner.StripPortions();
146 }
147
148 drawinglayer::primitive2d::Primitive2DContainer extractPrimitive2DSequence();
149 };
150
151 void impTextBreakupHandler::impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo)
152 {
153 if(rInfo.maText.isEmpty() || !rInfo.mnTextLen)
154 return;
155
156 OUString caseMappedText = rInfo.mrFont.CalcCaseMap( rInfo.maText );
157 basegfx::B2DVector aFontScaling;
160 aFontScaling,
161 rInfo.mrFont,
162 rInfo.IsRTL(),
163 false));
164 basegfx::B2DHomMatrix aNewTransform;
165
166 // add font scale to new transform
167 aNewTransform.scale(aFontScaling.getX(), aFontScaling.getY());
168
169 // look for proportional font scaling, if necessary, scale accordingly
170 sal_Int8 nPropr(rInfo.mrFont.GetPropr());
171 if(100 != nPropr)
172 {
173 const double fFactor(rInfo.mrFont.GetPropr() / 100.0);
174 aNewTransform.scale(fFactor, fFactor);
175 }
176
177 // apply font rotate
178 if(rInfo.mrFont.GetOrientation())
179 {
180 aNewTransform.rotate(-toRadians(rInfo.mrFont.GetOrientation()));
181 }
182
183 // look for escapement, if necessary, translate accordingly
184 if(rInfo.mrFont.GetEscapement())
185 {
186 sal_Int16 nEsc(rInfo.mrFont.GetEscapement());
187
188 if(DFLT_ESC_AUTO_SUPER == nEsc)
189 {
190 nEsc = .8 * (100 - nPropr);
191 assert (nEsc == DFLT_ESC_SUPER && "I'm sure this formula needs to be changed, but how to confirm that???");
192 nEsc = DFLT_ESC_SUPER;
193 }
194 else if(DFLT_ESC_AUTO_SUB == nEsc)
195 {
196 nEsc = .2 * -(100 - nPropr);
197 assert (nEsc == -20 && "I'm sure this formula needs to be changed, but how to confirm that???");
198 nEsc = -20;
199 }
200
201 if(nEsc > MAX_ESC_POS)
202 {
203 nEsc = MAX_ESC_POS;
204 }
205 else if(nEsc < -MAX_ESC_POS)
206 {
207 nEsc = -MAX_ESC_POS;
208 }
209
210 const double fEscapement(nEsc / -100.0);
211 aNewTransform.translate(0.0, fEscapement * aFontScaling.getY());
212 }
213
214 // apply transformA
215 aNewTransform *= maNewTransformA;
216
217 // apply local offset
218 aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y());
219
220 // also apply embedding object's transform
221 aNewTransform *= maNewTransformB;
222
223 // prepare DXArray content. To make it independent from font size (and such from
224 // the text transformation), scale it to unit coordinates
225 ::std::vector< double > aDXArray;
226
227 if(!rInfo.mpDXArray.empty() && rInfo.mnTextLen)
228 {
229 aDXArray.reserve(rInfo.mnTextLen);
230
231 for(sal_Int32 a=0; a < rInfo.mnTextLen; a++)
232 {
233 aDXArray.push_back(static_cast<double>(rInfo.mpDXArray[a]));
234 }
235 }
236
237 ::std::vector< sal_Bool > aKashidaArray;
238
239 if(!rInfo.mpKashidaArray.empty() && rInfo.mnTextLen)
240 {
241 aKashidaArray.reserve(rInfo.mnTextLen);
242
243 for(sal_Int32 a=0; a < rInfo.mnTextLen; a++)
244 {
245 aKashidaArray.push_back(rInfo.mpKashidaArray[a]);
246 }
247 }
248
249 // create complex text primitive and append
250 const Color aFontColor(rInfo.mrFont.GetColor());
251 const basegfx::BColor aBFontColor(aFontColor.getBColor());
252
253 const Color aTextFillColor(rInfo.mrFont.GetFillColor());
254
255 // prepare wordLineMode (for underline and strikeout)
256 // NOT for bullet texts. It is set (this may be an error by itself), but needs to be suppressed to hinder e.g. '1)'
257 // to be split which would not look like the original
258 const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && !rInfo.mbEndOfBullet);
259
260 // prepare new primitive
262 const bool bDecoratedIsNeeded(
263 LINESTYLE_NONE != rInfo.mrFont.GetOverline()
264 || LINESTYLE_NONE != rInfo.mrFont.GetUnderline()
265 || STRIKEOUT_NONE != rInfo.mrFont.GetStrikeout()
266 || FontEmphasisMark::NONE != (rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::Style)
267 || FontRelief::NONE != rInfo.mrFont.GetRelief()
268 || rInfo.mrFont.IsShadow()
269 || bWordLineMode);
270
271 if(bDecoratedIsNeeded)
272 {
273 // TextDecoratedPortionPrimitive2D needed, prepare some more data
274 // get overline and underline color. If it's on automatic (0xffffffff) use FontColor instead
275 const Color aUnderlineColor(rInfo.maTextLineColor);
276 const basegfx::BColor aBUnderlineColor((aUnderlineColor == COL_AUTO) ? aBFontColor : aUnderlineColor.getBColor());
277 const Color aOverlineColor(rInfo.maOverlineColor);
278 const basegfx::BColor aBOverlineColor((aOverlineColor == COL_AUTO) ? aBFontColor : aOverlineColor.getBColor());
279
280 // prepare overline and underline data
281 const drawinglayer::primitive2d::TextLine eFontOverline(
283 const drawinglayer::primitive2d::TextLine eFontLineStyle(
285
286 // check UnderlineAbove
287 const bool bUnderlineAbove(
288 drawinglayer::primitive2d::TEXT_LINE_NONE != eFontLineStyle && rInfo.mrFont.IsUnderlineAbove());
289
290 // prepare strikeout data
291 const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(
293
294 // prepare emphasis mark data
296
297 switch(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::Style)
298 {
299 case FontEmphasisMark::Dot : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT; break;
300 case FontEmphasisMark::Circle : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE; break;
301 case FontEmphasisMark::Disc : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC; break;
302 case FontEmphasisMark::Accent : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT; break;
303 default: break;
304 }
305
306 const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::PosAbove);
307 const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::PosBelow);
308
309 // prepare font relief data
311
312 switch(rInfo.mrFont.GetRelief())
313 {
314 case FontRelief::Embossed : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
315 case FontRelief::Engraved : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
316 default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
317 }
318
319 // prepare shadow/outline data
320 const bool bShadow(rInfo.mrFont.IsShadow());
321
322 // TextDecoratedPortionPrimitive2D is needed, create one
324
325 // attributes for TextSimplePortionPrimitive2D
326 aNewTransform,
327 caseMappedText,
328 rInfo.mnTextStart,
329 rInfo.mnTextLen,
330 std::vector(aDXArray),
331 std::vector(aKashidaArray),
332 aFontAttribute,
333 rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(),
334 aBFontColor,
335 aTextFillColor,
336
337 // attributes for TextDecoratedPortionPrimitive2D
338 aBOverlineColor,
339 aBUnderlineColor,
340 eFontOverline,
341 eFontLineStyle,
342 bUnderlineAbove,
343 eTextStrikeout,
344 bWordLineMode,
345 eTextEmphasisMark,
346 bEmphasisMarkAbove,
347 bEmphasisMarkBelow,
348 eTextRelief,
349 bShadow);
350 }
351 else
352 {
353 // TextSimplePortionPrimitive2D is enough
355 aNewTransform,
356 caseMappedText,
357 rInfo.mnTextStart,
358 rInfo.mnTextLen,
359 std::vector(aDXArray),
360 std::vector(aKashidaArray),
361 std::move(aFontAttribute),
362 rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(),
363 aBFontColor,
364 rInfo.mbFilled,
365 rInfo.mnWidthToFill,
366 aTextFillColor);
367 }
368
369 if (aFontColor.IsTransparent())
370 {
371 // Handle semi-transparent text for both the decorated and simple case here.
374 (255 - aFontColor.GetAlpha()) / 255.0);
375 }
376
377 if(rInfo.mbEndOfBullet)
378 {
379 // embed in TextHierarchyBulletPrimitive2D
380 drawinglayer::primitive2d::Primitive2DReference aNewReference(pNewPrimitive);
381 drawinglayer::primitive2d::Primitive2DContainer aNewSequence { aNewReference } ;
382 pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(std::move(aNewSequence));
383 }
384
385 if(rInfo.mpFieldData)
386 {
387 pNewPrimitive = impCheckFieldPrimitive(pNewPrimitive.get(), rInfo);
388 }
389
390 maTextPortionPrimitives.push_back(pNewPrimitive);
391
392 // support for WrongSpellVector. Create WrongSpellPrimitives as needed
393 if(!rInfo.mpWrongSpellVector || aDXArray.empty())
394 return;
395
396 const sal_Int32 nSize(rInfo.mpWrongSpellVector->size());
397 const sal_Int32 nDXCount(aDXArray.size());
398 const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded
399
400 for(sal_Int32 a(0); a < nSize; a++)
401 {
402 const EEngineData::WrongSpellClass& rCandidate = (*rInfo.mpWrongSpellVector)[a];
403
404 if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart)
405 {
406 const sal_Int32 nStart(rCandidate.nStart - rInfo.mnTextStart);
407 const sal_Int32 nEnd(rCandidate.nEnd - rInfo.mnTextStart);
408 double fStart(0.0);
409 double fEnd(0.0);
410
411 if(nStart > 0 && nStart - 1 < nDXCount)
412 {
413 fStart = aDXArray[nStart - 1];
414 }
415
416 if(nEnd > 0 && nEnd - 1 < nDXCount)
417 {
418 fEnd = aDXArray[nEnd - 1];
419 }
420
421 if(!basegfx::fTools::equal(fStart, fEnd))
422 {
423 if(rInfo.IsRTL())
424 {
425 // #i98523#
426 // When the portion is RTL, mirror the redlining using the
427 // full portion width
428 const double fTextWidth(aDXArray[aDXArray.size() - 1]);
429
430 fStart = fTextWidth - fStart;
431 fEnd = fTextWidth - fEnd;
432
433 // tdf#151968
434 // if start < end, OutputDevice::DrawWaveLine() will
435 // think it is a rotated line, so we swap fStart and
436 // fEnd to avoid this.
437 std::swap(fStart, fEnd);
438 }
439
440 // need to take FontScaling out of values; it's already part of
441 // aNewTransform and would be double applied
442 const double fFontScaleX(aFontScaling.getX());
443
444 if(!basegfx::fTools::equal(fFontScaleX, 1.0)
445 && !basegfx::fTools::equalZero(fFontScaleX))
446 {
447 fStart /= fFontScaleX;
448 fEnd /= fFontScaleX;
449 }
450
451 maTextPortionPrimitives.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D(
452 aNewTransform,
453 fStart,
454 fEnd,
455 aSpellColor));
456 }
457 }
458 }
459 }
460
461 rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> impTextBreakupHandler::impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo)
462 {
464 if(rInfo.mpFieldData)
465 {
466 // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a TextHierarchyFieldPrimitive2D
467 // which holds the field type and, if applicable, the URL
468 const SvxURLField* pURLField = dynamic_cast< const SvxURLField* >(rInfo.mpFieldData);
469 const SvxPageField* pPageField = dynamic_cast< const SvxPageField* >(rInfo.mpFieldData);
470
471 // embed current primitive to a sequence
473
474 if(pPrimitive)
475 {
476 aSequence.resize(1);
477 aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive);
478 }
479
480 if(pURLField)
481 {
482 // extended this to hold more of the contents of the original
483 // SvxURLField since that stuff is still used in HitTest and e.g. Calc
484 std::vector< std::pair< OUString, OUString>> meValues;
485 meValues.emplace_back("URL", pURLField->GetURL());
486 meValues.emplace_back("Representation", pURLField->GetRepresentation());
487 meValues.emplace_back("TargetFrame", pURLField->GetTargetFrame());
488 meValues.emplace_back("SvxURLFormat", OUString::number(static_cast<sal_uInt16>(pURLField->GetFormat())));
490 }
491 else if(pPageField)
492 {
494 }
495 else
496 {
498 }
499 }
500
501 return xRet;
502 }
503
504 void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives()
505 {
506 // only create a line primitive when we had content; there is no need for
507 // empty line primitives (contrary to paragraphs, see below).
508 if(!maTextPortionPrimitives.empty())
509 {
510 maLinePrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyLinePrimitive2D(std::move(maTextPortionPrimitives)));
511 }
512 }
513
514 void impTextBreakupHandler::impFlushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara)
515 {
516 sal_Int16 nDepth = mrOutliner.GetDepth(nPara);
517 EBulletInfo eInfo = mrOutliner.GetBulletInfo(nPara);
518 // Pass -1 to signal VclMetafileProcessor2D that there is no active
519 // bullets/numbering in this paragraph (i.e. this is normal text)
520 const sal_Int16 nOutlineLevel( eInfo.bVisible ? nDepth : -1);
521
522 // ALWAYS create a paragraph primitive, even when no content was added. This is done to
523 // have the correct paragraph count even with empty paragraphs. Those paragraphs will
524 // have an empty sub-PrimitiveSequence.
525 maParagraphPrimitives.push_back(
527 std::move(maLinePrimitives),
528 nOutlineLevel));
529 }
530
531 void impTextBreakupHandler::impHandleDrawPortionInfo(const DrawPortionInfo& rInfo)
532 {
533 impCreateTextPortionPrimitive(rInfo);
534
535 if(rInfo.mbEndOfLine || rInfo.mbEndOfParagraph)
536 {
537 impFlushTextPortionPrimitivesToLinePrimitives();
538 }
539
540 if(rInfo.mbEndOfParagraph)
541 {
542 impFlushLinePrimitivesToParagraphPrimitives(rInfo.mnPara);
543 }
544 }
545
546 void impTextBreakupHandler::impHandleDrawBulletInfo(const DrawBulletInfo& rInfo)
547 {
548 basegfx::B2DHomMatrix aNewTransform;
549
550 // add size to new transform
551 aNewTransform.scale(rInfo.maBulletSize.getWidth(), rInfo.maBulletSize.getHeight());
552
553 // apply transformA
554 aNewTransform *= maNewTransformA;
555
556 // apply local offset
557 aNewTransform.translate(rInfo.maBulletPosition.X(), rInfo.maBulletPosition.Y());
558
559 // also apply embedding object's transform
560 aNewTransform *= maNewTransformB;
561
562 // prepare empty GraphicAttr
563 const GraphicAttr aGraphicAttr;
564
565 // create GraphicPrimitive2D
567 aNewTransform,
569 aGraphicAttr));
570
571 // embed in TextHierarchyBulletPrimitive2D
572 drawinglayer::primitive2d::Primitive2DContainer aNewSequence { aNewReference };
574
575 // add to output
576 maTextPortionPrimitives.push_back(pNewPrimitive);
577 }
578
579 IMPL_LINK(impTextBreakupHandler, decomposeContourTextPrimitive, DrawPortionInfo*, pInfo, void)
580 {
581 // for contour text, ignore (clip away) all portions which are below
582 // the visible area given by maScale
583 if(pInfo && static_cast<double>(pInfo->mrStartPos.Y()) < maScale.getY())
584 {
585 impHandleDrawPortionInfo(*pInfo);
586 }
587 }
588
589 IMPL_LINK(impTextBreakupHandler, decomposeBlockTextPrimitive, DrawPortionInfo*, pInfo, void)
590 {
591 if(!pInfo)
592 return;
593
594 // Is clipping wanted? This is text clipping; only accept a portion
595 // if it's completely in the range
596 if(!maClipRange.isEmpty())
597 {
598 // Test start position first; this allows to not get the text range at
599 // all if text is far outside
600 const basegfx::B2DPoint aStartPosition(pInfo->mrStartPos.X(), pInfo->mrStartPos.Y());
601
602 if(!maClipRange.isInside(aStartPosition))
603 {
604 return;
605 }
606
607 // Start position is inside. Get TextBoundRect and TopLeft next
609 aTextLayouterDevice.setFont(pInfo->mrFont);
610
611 const basegfx::B2DRange aTextBoundRect(
612 aTextLayouterDevice.getTextBoundRect(
613 pInfo->maText, pInfo->mnTextStart, pInfo->mnTextLen));
614 const basegfx::B2DPoint aTopLeft(aTextBoundRect.getMinimum() + aStartPosition);
615
616 if(!maClipRange.isInside(aTopLeft))
617 {
618 return;
619 }
620
621 // TopLeft is inside. Get BottomRight and check
622 const basegfx::B2DPoint aBottomRight(aTextBoundRect.getMaximum() + aStartPosition);
623
624 if(!maClipRange.isInside(aBottomRight))
625 {
626 return;
627 }
628
629 // all inside, clip was successful
630 }
631 impHandleDrawPortionInfo(*pInfo);
632 }
633
634 IMPL_LINK(impTextBreakupHandler, decomposeStretchTextPrimitive, DrawPortionInfo*, pInfo, void)
635 {
636 if(pInfo)
637 {
638 impHandleDrawPortionInfo(*pInfo);
639 }
640 }
641
642 IMPL_LINK(impTextBreakupHandler, decomposeContourBulletPrimitive, DrawBulletInfo*, pInfo, void)
643 {
644 if(pInfo)
645 {
646 impHandleDrawBulletInfo(*pInfo);
647 }
648 }
649
650 IMPL_LINK(impTextBreakupHandler, decomposeBlockBulletPrimitive, DrawBulletInfo*, pInfo, void)
651 {
652 if(pInfo)
653 {
654 impHandleDrawBulletInfo(*pInfo);
655 }
656 }
657
658 IMPL_LINK(impTextBreakupHandler, decomposeStretchBulletPrimitive, DrawBulletInfo*, pInfo, void)
659 {
660 if(pInfo)
661 {
662 impHandleDrawBulletInfo(*pInfo);
663 }
664 }
665
666 drawinglayer::primitive2d::Primitive2DContainer impTextBreakupHandler::extractPrimitive2DSequence()
667 {
668 if(!maTextPortionPrimitives.empty())
669 {
670 // collect non-closed lines
671 impFlushTextPortionPrimitivesToLinePrimitives();
672 }
673
674 if(!maLinePrimitives.empty())
675 {
676 // collect non-closed paragraphs
677 impFlushLinePrimitivesToParagraphPrimitives(mrOutliner.GetParagraphCount() - 1);
678 }
679
680 return std::move(maParagraphPrimitives);
681 }
682} // end of anonymous namespace
683
684
685// primitive decompositions
686
689 const drawinglayer::primitive2d::SdrContourTextPrimitive2D& rSdrContourTextPrimitive,
690 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
691{
692 // decompose matrix to have position and size of text
693 basegfx::B2DVector aScale, aTranslate;
694 double fRotate, fShearX;
695 rSdrContourTextPrimitive.getObjectTransform().decompose(aScale, aTranslate, fRotate, fShearX);
696
697 // prepare contour polygon, force to non-mirrored for laying out
698 basegfx::B2DPolyPolygon aPolyPolygon(rSdrContourTextPrimitive.getUnitPolyPolygon());
699 aPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix(fabs(aScale.getX()), fabs(aScale.getY())));
700
701 // prepare outliner
702 SolarMutexGuard aSolarGuard;
703 SdrOutliner& rOutliner = ImpGetDrawOutliner();
704 const Size aNullSize;
705 rOutliner.SetPaperSize(aNullSize);
706 rOutliner.SetPolygon(aPolyPolygon);
707 rOutliner.SetUpdateLayout(true);
708 rOutliner.SetText(rSdrContourTextPrimitive.getOutlinerParaObject());
709
710 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
711 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
712
713 // prepare matrices to apply to newly created primitives
714 basegfx::B2DHomMatrix aNewTransformA;
715
716 // mirroring. We are now in the polygon sizes. When mirroring in X and Y,
717 // move the null point which was top left to bottom right.
718 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
719 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
720
721 // in-between the translations of the single primitives will take place. Afterwards,
722 // the object's transformations need to be applied
724 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
725 fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
726
727 // now break up text primitives.
728 impTextBreakupHandler aConverter(rOutliner);
729 aConverter.decomposeContourTextPrimitive(aNewTransformA, aNewTransformB, aScale);
730
731 // cleanup outliner
732 rOutliner.Clear();
733 rOutliner.setVisualizedPage(nullptr);
734
735 rTarget = aConverter.extractPrimitive2DSequence();
736}
737
740 const drawinglayer::primitive2d::SdrAutoFitTextPrimitive2D& rSdrAutofitTextPrimitive,
741 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
742{
743 // decompose matrix to have position and size of text
744 basegfx::B2DVector aScale, aTranslate;
745 double fRotate, fShearX;
746 rSdrAutofitTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
747
748 // use B2DRange aAnchorTextRange for calculations
749 basegfx::B2DRange aAnchorTextRange(aTranslate);
750 aAnchorTextRange.expand(aTranslate + aScale);
751
752 // prepare outliner
753 const SfxItemSet& rTextItemSet = rSdrAutofitTextPrimitive.getSdrText()->GetItemSet();
754 SolarMutexGuard aSolarGuard;
755 SdrOutliner& rOutliner = ImpGetDrawOutliner();
756 SdrTextVertAdjust eVAdj = GetTextVerticalAdjust(rTextItemSet);
757 SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust(rTextItemSet);
758 const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
759 const Size aNullSize;
760
761 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
762 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
763
764 rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::AUTOPAGESIZE|EEControlBits::STRETCHING);
765 rOutliner.SetMinAutoPaperSize(aNullSize);
766 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
767
768 // That color needs to be restored on leaving this method
769 Color aOriginalBackColor(rOutliner.GetBackgroundColor());
770 setSuitableOutlinerBg(rOutliner);
771
772 // add one to range sizes to get back to the old Rectangle and outliner measurements
773 const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1));
774 const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1));
775 const OutlinerParaObject* pOutlinerParaObject = rSdrAutofitTextPrimitive.getSdrText()->GetOutlinerParaObject();
776 OSL_ENSURE(pOutlinerParaObject, "impDecomposeBlockTextPrimitive used with no OutlinerParaObject (!)");
777 const bool bVerticalWriting(pOutlinerParaObject->IsEffectivelyVertical());
778 const bool bTopToBottom(pOutlinerParaObject->IsTopToBottom());
779 const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
780
781 if(rSdrAutofitTextPrimitive.getWordWrap() || IsTextFrame())
782 {
783 rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
784 }
785
786 if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWriting)
787 {
788 rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
789 rOutliner.SetMinColumnWrapHeight(nAnchorTextHeight);
790 }
791
792 if(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWriting)
793 {
794 rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
795 rOutliner.SetMinColumnWrapHeight(nAnchorTextWidth);
796 }
797
798 rOutliner.SetPaperSize(aNullSize);
799 rOutliner.SetUpdateLayout(true);
800 rOutliner.SetText(*pOutlinerParaObject);
801 ImpAutoFitText(rOutliner,aAnchorTextSize,bVerticalWriting);
802
803 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
804 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
805
806 // now get back the layouted text size from outliner
807 const Size aOutlinerTextSize(rOutliner.GetPaperSize());
808 const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), aOutlinerTextSize.Height());
809 basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
810
811 // correct horizontal translation using the now known text size
813 {
814 const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
815
816 if(SDRTEXTHORZADJUST_CENTER == eHAdj)
817 {
818 aAdjustTranslate.setX(fFree / 2.0);
819 }
820
821 if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
822 {
823 aAdjustTranslate.setX(fFree);
824 }
825 }
826
827 // correct vertical translation using the now known text size
829 {
830 const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
831
832 if(SDRTEXTVERTADJUST_CENTER == eVAdj)
833 {
834 aAdjustTranslate.setY(fFree / 2.0);
835 }
836
837 if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
838 {
839 aAdjustTranslate.setY(fFree);
840 }
841 }
842
843 // prepare matrices to apply to newly created primitives. aNewTransformA
844 // will get coordinates in aOutlinerScale size and positive in X, Y.
845 basegfx::B2DHomMatrix aNewTransformA;
846 basegfx::B2DHomMatrix aNewTransformB;
847
848 // translate relative to given primitive to get same rotation and shear
849 // as the master shape we are working on. For vertical, use the top-right
850 // corner
851 const double fStartInX(bVerticalWriting && bTopToBottom ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
852 const double fStartInY(bVerticalWriting && !bTopToBottom ? aAdjustTranslate.getY() + aOutlinerScale.getY() : aAdjustTranslate.getY());
853 aNewTransformA.translate(fStartInX, fStartInY);
854
855 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
856 // move the null point which was top left to bottom right.
857 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
858 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
859 aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
860
861 // in-between the translations of the single primitives will take place. Afterwards,
862 // the object's transformations need to be applied
863 aNewTransformB.shearX(fShearX);
864 aNewTransformB.rotate(fRotate);
865 aNewTransformB.translate(aTranslate.getX(), aTranslate.getY());
866
867 basegfx::B2DRange aClipRange;
868
869 // now break up text primitives.
870 impTextBreakupHandler aConverter(rOutliner);
871 aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
872
873 // cleanup outliner
874 rOutliner.SetBackgroundColor(aOriginalBackColor);
875 rOutliner.Clear();
876 rOutliner.setVisualizedPage(nullptr);
877 rOutliner.SetControlWord(nOriginalControlWord);
878
879 rTarget = aConverter.extractPrimitive2DSequence();
880}
881
882// Resolves: fdo#35779 set background color of this shape as the editeng background if there
883// is one. Check the shape itself, then the host page, then that page's master page.
885{
886 const SfxItemSet* pBackgroundFillSet = getBackgroundFillSet();
887 if (drawing::FillStyle_NONE != pBackgroundFillSet->Get(XATTR_FILLSTYLE).GetValue())
888 {
889 Color aColor(rOutliner.GetBackgroundColor());
890 GetDraftFillColor(*pBackgroundFillSet, aColor);
891 rOutliner.SetBackgroundColor(aColor);
892 return true;
893 }
894 return false;
895}
896
898{
899 const SfxItemSet* pBackgroundFillSet = &GetObjectItemSet();
900
901 if (drawing::FillStyle_NONE == pBackgroundFillSet->Get(XATTR_FILLSTYLE).GetValue())
902 {
903 SdrPage* pOwnerPage(getSdrPageFromSdrObject());
904 if (pOwnerPage)
905 {
906 pBackgroundFillSet = &pOwnerPage->getSdrPageProperties().GetItemSet();
907
908 if (drawing::FillStyle_NONE == pBackgroundFillSet->Get(XATTR_FILLSTYLE).GetValue())
909 {
910 if (!pOwnerPage->IsMasterPage() && pOwnerPage->TRG_HasMasterPage())
911 {
912 pBackgroundFillSet = &pOwnerPage->TRG_GetMasterPage().getSdrPageProperties().GetItemSet();
913 }
914 }
915 }
916 }
917 return pBackgroundFillSet;
918}
919
921{
922 if(IsGroupObject()) // Doesn't make sense, and GetObjectItemSet() asserts.
923 return nullptr;
924 const SfxItemSet* pBackgroundFillSet = getBackgroundFillSet();
925 if (drawing::FillStyle_BITMAP != pBackgroundFillSet->Get(XATTR_FILLSTYLE).GetValue())
926 return nullptr;
927 return &pBackgroundFillSet->Get(XATTR_FILLBITMAP).GetGraphicObject().GetGraphic();
928}
929
932 const drawinglayer::primitive2d::SdrBlockTextPrimitive2D& rSdrBlockTextPrimitive,
933 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
934{
935 // decompose matrix to have position and size of text
936 basegfx::B2DVector aScale, aTranslate;
937 double fRotate, fShearX;
938 rSdrBlockTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
939
940 // use B2DRange aAnchorTextRange for calculations
941 basegfx::B2DRange aAnchorTextRange(aTranslate);
942 aAnchorTextRange.expand(aTranslate + aScale);
943
944 // prepare outliner
945 const bool bIsCell(rSdrBlockTextPrimitive.getCellText());
946 SolarMutexGuard aSolarGuard;
947 SdrOutliner& rOutliner = ImpGetDrawOutliner();
948 SdrTextHorzAdjust eHAdj = rSdrBlockTextPrimitive.getSdrTextHorzAdjust();
949 SdrTextVertAdjust eVAdj = rSdrBlockTextPrimitive.getSdrTextVertAdjust();
950 const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
951 const Size aNullSize;
952
953 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
954 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
955 rOutliner.SetFixedCellHeight(rSdrBlockTextPrimitive.isFixedCellHeight());
956 rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::AUTOPAGESIZE);
957 rOutliner.SetMinAutoPaperSize(aNullSize);
958 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
959
960 // That color needs to be restored on leaving this method
961 Color aOriginalBackColor(rOutliner.GetBackgroundColor());
962 setSuitableOutlinerBg(rOutliner);
963
964 // add one to range sizes to get back to the old Rectangle and outliner measurements
965 const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1));
966 const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1));
967 const bool bVerticalWriting(rSdrBlockTextPrimitive.getOutlinerParaObject().IsEffectivelyVertical());
968 const bool bTopToBottom(rSdrBlockTextPrimitive.getOutlinerParaObject().IsTopToBottom());
969 const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
970
971 if(bIsCell)
972 {
973 // cell text is formatted neither like a text object nor like an object
974 // text, so use a special setup here
975 rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
976
977 // #i106214# To work with an unchangeable PaperSize (CellSize in
978 // this case) Set(Min|Max)AutoPaperSize and SetPaperSize have to be used.
979 // #i106214# This was not completely correct; to still measure the real
980 // text height to allow vertical adjust (and vice versa for VerticalWritintg)
981 // only one aspect has to be set, but the other one to zero
982 if(bVerticalWriting)
983 {
984 // measure the horizontal text size
985 rOutliner.SetMinAutoPaperSize(Size(0, aAnchorTextSize.Height()));
986 }
987 else
988 {
989 // measure the vertical text size
990 rOutliner.SetMinAutoPaperSize(Size(aAnchorTextSize.Width(), 0));
991 }
992
993 rOutliner.SetPaperSize(aAnchorTextSize);
994 rOutliner.SetUpdateLayout(true);
995 rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
996 }
997 else
998 {
999 // check if block text is used (only one of them can be true)
1000 const bool bHorizontalIsBlock(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWriting);
1001 const bool bVerticalIsBlock(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWriting);
1002
1003 // set minimal paper size horizontally/vertically if needed
1004 if(bHorizontalIsBlock)
1005 {
1006 rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
1007 rOutliner.SetMinColumnWrapHeight(nAnchorTextHeight);
1008 }
1009 else if(bVerticalIsBlock)
1010 {
1011 rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
1012 rOutliner.SetMinColumnWrapHeight(nAnchorTextWidth);
1013 }
1014
1015 if((rSdrBlockTextPrimitive.getWordWrap() || IsTextFrame()) && !rSdrBlockTextPrimitive.getUnlimitedPage())
1016 {
1017 // #i103454# maximal paper size hor/ver needs to be limited to text
1018 // frame size. If it's block text, still allow the 'other' direction
1019 // to grow to get a correct real text size when using GetPaperSize().
1020 // When just using aAnchorTextSize as maximum, GetPaperSize()
1021 // would just return aAnchorTextSize again: this means, the wanted
1022 // 'measurement' of the real size of block text would not work
1023 Size aMaxAutoPaperSize(aAnchorTextSize);
1024
1025 // Usual processing - always grow in one of directions
1026 bool bAllowGrowVertical = !bVerticalWriting;
1027 bool bAllowGrowHorizontal = bVerticalWriting;
1028
1029 // Compatibility mode for tdf#99729
1030 if (getSdrModelFromSdrObject().GetCompatibilityFlag(
1032 {
1033 bAllowGrowVertical = bHorizontalIsBlock;
1034 bAllowGrowHorizontal = bVerticalIsBlock;
1035 }
1036
1037 if (bAllowGrowVertical)
1038 {
1039 // allow to grow vertical for horizontal texts
1040 aMaxAutoPaperSize.setHeight(1000000);
1041 }
1042 else if (bAllowGrowHorizontal)
1043 {
1044 // allow to grow horizontal for vertical texts
1045 aMaxAutoPaperSize.setWidth(1000000);
1046 }
1047
1048 rOutliner.SetMaxAutoPaperSize(aMaxAutoPaperSize);
1049 }
1050
1051 rOutliner.SetPaperSize(aNullSize);
1052 rOutliner.SetUpdateLayout(true);
1053 rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
1054 }
1055
1056 rOutliner.SetControlWord(nOriginalControlWord);
1057
1058 // now get back the layouted text size from outliner
1059 const Size aOutlinerTextSize(rOutliner.GetPaperSize());
1060 const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), aOutlinerTextSize.Height());
1061 basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
1062
1063 // For draw objects containing text correct hor/ver alignment if text is bigger
1064 // than the object itself. Without that correction, the text would always be
1065 // formatted to the left edge (or top edge when vertical) of the draw object.
1066 if(!IsTextFrame() && !bIsCell)
1067 {
1068 if(aAnchorTextRange.getWidth() < aOutlinerScale.getX() && !bVerticalWriting)
1069 {
1070 // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
1071 // else the alignment is wanted.
1072 if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
1073 {
1074 SvxAdjust eAdjust = GetObjectItemSet().Get(EE_PARA_JUST).GetAdjust();
1075 switch(eAdjust)
1076 {
1077 case SvxAdjust::Left: eHAdj = SDRTEXTHORZADJUST_LEFT; break;
1078 case SvxAdjust::Right: eHAdj = SDRTEXTHORZADJUST_RIGHT; break;
1079 case SvxAdjust::Center: eHAdj = SDRTEXTHORZADJUST_CENTER; break;
1080 default: break;
1081 }
1082 }
1083 }
1084
1085 if(aAnchorTextRange.getHeight() < aOutlinerScale.getY() && bVerticalWriting)
1086 {
1087 // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
1088 // else the alignment is wanted.
1089 if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
1090 {
1092 }
1093 }
1094 }
1095
1096 // correct horizontal translation using the now known text size
1097 if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
1098 {
1099 const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
1100
1101 if(SDRTEXTHORZADJUST_CENTER == eHAdj)
1102 {
1103 aAdjustTranslate.setX(fFree / 2.0);
1104 }
1105
1106 if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
1107 {
1108 aAdjustTranslate.setX(fFree);
1109 }
1110 }
1111
1112 const double fFreeVerticalSpace(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
1113 bool bClipVerticalTextOverflow = fFreeVerticalSpace < 0
1115 // correct vertical translation using the now known text size
1116 if((SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
1117 && !bClipVerticalTextOverflow)
1118 {
1119 if(SDRTEXTVERTADJUST_CENTER == eVAdj)
1120 {
1121 aAdjustTranslate.setY(fFreeVerticalSpace / 2.0);
1122 }
1123
1124 if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
1125 {
1126 aAdjustTranslate.setY(fFreeVerticalSpace);
1127 }
1128 }
1129
1130 // prepare matrices to apply to newly created primitives. aNewTransformA
1131 // will get coordinates in aOutlinerScale size and positive in X, Y.
1132 // Translate relative to given primitive to get same rotation and shear
1133 // as the master shape we are working on. For vertical, use the top-right
1134 // corner
1135 const double fStartInX(bVerticalWriting && bTopToBottom ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
1136 const double fStartInY(bVerticalWriting && !bTopToBottom ? aAdjustTranslate.getY() + aOutlinerScale.getY() : aAdjustTranslate.getY());
1137 basegfx::B2DHomMatrix aNewTransformA(basegfx::utils::createTranslateB2DHomMatrix(fStartInX, fStartInY));
1138
1139 // Apply the camera rotation. It have to be applied after adjustment of
1140 // the text (top, bottom, center, left, right).
1141 if(GetCameraZRotation() != 0)
1142 {
1143 // First find the text rect.
1144 basegfx::B2DRange aTextRectangle(/*x1=*/aTranslate.getX() + aAdjustTranslate.getX(),
1145 /*y1=*/aTranslate.getY() + aAdjustTranslate.getY(),
1146 /*x2=*/aTranslate.getX() + aOutlinerScale.getX() - aAdjustTranslate.getX(),
1147 /*y2=*/aTranslate.getY() + aOutlinerScale.getY() - aAdjustTranslate.getY());
1148
1149 // Rotate the text from the center point.
1150 basegfx::B2DVector aTranslateToCenter(aTextRectangle.getWidth() / 2, aTextRectangle.getHeight() / 2);
1151 aNewTransformA.translate(-aTranslateToCenter.getX(), -aTranslateToCenter.getY());
1152 aNewTransformA.rotate(basegfx::deg2rad(360.0 - GetCameraZRotation() ));
1153 aNewTransformA.translate(aTranslateToCenter.getX(), aTranslateToCenter.getY());
1154 }
1155
1156 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
1157 // move the null point which was top left to bottom right.
1158 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
1159 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
1160
1161 // in-between the translations of the single primitives will take place. Afterwards,
1162 // the object's transformations need to be applied
1164 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
1165 fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
1166
1167
1168 // create ClipRange (if needed)
1169 basegfx::B2DRange aClipRange;
1170 if(bClipVerticalTextOverflow)
1171 aClipRange = {0, 0, std::numeric_limits<double>::max(), aAnchorTextRange.getHeight()};
1172
1173 // now break up text primitives.
1174 impTextBreakupHandler aConverter(rOutliner);
1175 aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
1176
1177 // cleanup outliner
1178 rOutliner.SetBackgroundColor(aOriginalBackColor);
1179 rOutliner.Clear();
1180 rOutliner.setVisualizedPage(nullptr);
1181
1182 rTarget = aConverter.extractPrimitive2DSequence();
1183}
1184
1187 const drawinglayer::primitive2d::SdrStretchTextPrimitive2D& rSdrStretchTextPrimitive,
1188 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
1189{
1190 // decompose matrix to have position and size of text
1191 basegfx::B2DVector aScale, aTranslate;
1192 double fRotate, fShearX;
1193 rSdrStretchTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
1194
1195 // prepare outliner
1196 SolarMutexGuard aSolarGuard;
1197 SdrOutliner& rOutliner = ImpGetDrawOutliner();
1198 const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
1199 const Size aNullSize;
1200
1201 rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE);
1202 rOutliner.SetFixedCellHeight(rSdrStretchTextPrimitive.isFixedCellHeight());
1203 rOutliner.SetMinAutoPaperSize(aNullSize);
1204 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
1205 rOutliner.SetPaperSize(aNullSize);
1206 rOutliner.SetUpdateLayout(true);
1207 rOutliner.SetText(rSdrStretchTextPrimitive.getOutlinerParaObject());
1208
1209 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
1210 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
1211
1212 // now get back the laid out text size from outliner
1213 const Size aOutlinerTextSize(rOutliner.CalcTextSize());
1214 const basegfx::B2DVector aOutlinerScale(
1215 aOutlinerTextSize.Width() == tools::Long(0) ? 1.0 : aOutlinerTextSize.Width(),
1216 aOutlinerTextSize.Height() == tools::Long(0) ? 1.0 : aOutlinerTextSize.Height());
1217
1218 // prepare matrices to apply to newly created primitives
1219 basegfx::B2DHomMatrix aNewTransformA;
1220
1221 // #i101957# Check for vertical text. If used, aNewTransformA
1222 // needs to translate the text initially around object width to orient
1223 // it relative to the topper right instead of the topper left
1224 const bool bVertical(rSdrStretchTextPrimitive.getOutlinerParaObject().IsEffectivelyVertical());
1225 const bool bTopToBottom(rSdrStretchTextPrimitive.getOutlinerParaObject().IsTopToBottom());
1226
1227 if(bVertical)
1228 {
1229 if(bTopToBottom)
1230 aNewTransformA.translate(aScale.getX(), 0.0);
1231 else
1232 aNewTransformA.translate(0.0, aScale.getY());
1233 }
1234
1235 // calculate global char stretching scale parameters. Use non-mirrored sizes
1236 // to layout without mirroring
1237 const double fScaleX(fabs(aScale.getX()) / aOutlinerScale.getX());
1238 const double fScaleY(fabs(aScale.getY()) / aOutlinerScale.getY());
1239 rOutliner.setGlobalScale(fScaleX * 100.0, fScaleY * 100.0, 100.0, 100.0);
1240
1241 // When mirroring in X and Y,
1242 // move the null point which was top left to bottom right.
1243 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
1244 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
1245
1246 // in-between the translations of the single primitives will take place. Afterwards,
1247 // the object's transformations need to be applied
1249 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
1250 fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
1251
1252 // now break up text primitives.
1253 impTextBreakupHandler aConverter(rOutliner);
1254 aConverter.decomposeStretchTextPrimitive(aNewTransformA, aNewTransformB);
1255
1256 // cleanup outliner
1257 rOutliner.SetControlWord(nOriginalControlWord);
1258 rOutliner.Clear();
1259 rOutliner.setVisualizedPage(nullptr);
1260
1261 rTarget = aConverter.extractPrimitive2DSequence();
1262}
1263
1264
1265// timing generators
1266#define ENDLESS_LOOP (0xffffffff)
1267#define ENDLESS_TIME (double(0xffffffff))
1268#define PIXEL_DPI (96.0)
1269
1271{
1273 return;
1274
1275 // get values
1276 const SfxItemSet& rSet = GetObjectItemSet();
1277 const sal_uInt32 nRepeat(static_cast<sal_uInt32>(rSet.Get(SDRATTR_TEXT_ANICOUNT).GetValue()));
1278 double fDelay(static_cast<double>(rSet.Get(SDRATTR_TEXT_ANIDELAY).GetValue()));
1279
1280 if(0.0 == fDelay)
1281 {
1282 // use default
1283 fDelay = 250.0;
1284 }
1285
1286 // prepare loop and add
1289 aLoop.append(aStart);
1291 aLoop.append(aEnd);
1292 rAnimList.append(aLoop);
1293
1294 // add stopped state if loop is not endless
1295 if(0 != nRepeat)
1296 {
1297 bool bVisibleWhenStopped(rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE).GetValue());
1298 drawinglayer::animation::AnimationEntryFixed aStop(ENDLESS_TIME, bVisibleWhenStopped ? 0.0 : 1.0);
1299 rAnimList.append(aStop);
1300 }
1301}
1302
1303static void impCreateScrollTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
1304{
1305 bool bVisibleWhenStopped(rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE).GetValue());
1306 bool bVisibleWhenStarted(rSet.Get(SDRATTR_TEXT_ANISTARTINSIDE).GetValue());
1307 const sal_uInt32 nRepeat(rSet.Get(SDRATTR_TEXT_ANICOUNT).GetValue());
1308
1309 if(bVisibleWhenStarted)
1310 {
1311 // move from center to outside
1312 drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
1313 rAnimList.append(aInOut);
1314 }
1315
1316 // loop. In loop, move through
1318 drawinglayer::animation::AnimationEntryLinear aThrough(fTimeFullPath, fFrequency, bForward ? 0.0 : 1.0, bForward ? 1.0 : 0.0);
1319 aLoop.append(aThrough);
1320 rAnimList.append(aLoop);
1321
1322 if(0 != nRepeat && bVisibleWhenStopped)
1323 {
1324 // move from outside to center
1325 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
1326 rAnimList.append(aOutIn);
1327
1328 // add timing for staying at the end
1330 rAnimList.append(aEnd);
1331 }
1332}
1333
1334static void impCreateAlternateTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, double fRelativeTextLength, bool bForward, double fTimeFullPath, double fFrequency)
1335{
1336 if(basegfx::fTools::more(fRelativeTextLength, 0.5))
1337 {
1338 // this is the case when fTextLength > fFrameLength, text is bigger than animation frame.
1339 // In that case, correct direction
1340 bForward = !bForward;
1341 }
1342
1343 const double fStartPosition(bForward ? fRelativeTextLength : 1.0 - fRelativeTextLength);
1344 const double fEndPosition(bForward ? 1.0 - fRelativeTextLength : fRelativeTextLength);
1345 bool bVisibleWhenStarted(rSet.Get(SDRATTR_TEXT_ANISTARTINSIDE).GetValue());
1346 const sal_uInt32 nRepeat(rSet.Get(SDRATTR_TEXT_ANICOUNT).GetValue());
1347
1348 if(!bVisibleWhenStarted)
1349 {
1350 // move from outside to center
1351 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
1352 rAnimList.append(aOutIn);
1353 }
1354
1355 // loop. In loop, move out and in again. fInnerMovePath may be negative when text is bigger then frame,
1356 // so use absolute value
1357 const double fInnerMovePath(fabs(1.0 - (fRelativeTextLength * 2.0)));
1358 const double fTimeForInnerPath(fTimeFullPath * fInnerMovePath);
1359 const double fHalfInnerPath(fTimeForInnerPath * 0.5);
1360 const sal_uInt32 nDoubleRepeat(nRepeat / 2L);
1361
1362 if(nDoubleRepeat || 0 == nRepeat)
1363 {
1364 // double forth and back loop
1365 drawinglayer::animation::AnimationEntryLoop aLoop(nDoubleRepeat ? nDoubleRepeat : ENDLESS_LOOP);
1366 drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
1367 aLoop.append(aTime0);
1368 drawinglayer::animation::AnimationEntryLinear aTime1(fTimeForInnerPath, fFrequency, fEndPosition, fStartPosition);
1369 aLoop.append(aTime1);
1370 drawinglayer::animation::AnimationEntryLinear aTime2(fHalfInnerPath, fFrequency, fStartPosition, 0.5);
1371 aLoop.append(aTime2);
1372 rAnimList.append(aLoop);
1373 }
1374
1375 if(nRepeat % 2L)
1376 {
1377 // repeat is uneven, so we need one more forth and back to center
1378 drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
1379 rAnimList.append(aTime0);
1380 drawinglayer::animation::AnimationEntryLinear aTime1(fHalfInnerPath, fFrequency, fEndPosition, 0.5);
1381 rAnimList.append(aTime1);
1382 }
1383
1384 if(0 == nRepeat)
1385 return;
1386
1387 bool bVisibleWhenStopped(rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE).GetValue());
1388 if(bVisibleWhenStopped)
1389 {
1390 // add timing for staying at the end
1392 rAnimList.append(aEnd);
1393 }
1394 else
1395 {
1396 // move from center to outside
1397 drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
1398 rAnimList.append(aInOut);
1399 }
1400}
1401
1402static void impCreateSlideTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
1403{
1404 // move in from outside, start outside
1405 const double fStartPosition(bForward ? 0.0 : 1.0);
1406 const sal_uInt32 nRepeat(rSet.Get(SDRATTR_TEXT_ANICOUNT).GetValue());
1407
1408 // move from outside to center
1409 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
1410 rAnimList.append(aOutIn);
1411
1412 // loop. In loop, move out and in again
1413 if(nRepeat > 1 || 0 == nRepeat)
1414 {
1415 drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat - 1 : ENDLESS_LOOP);
1416 drawinglayer::animation::AnimationEntryLinear aTime0(fTimeFullPath * 0.5, fFrequency, 0.5, fStartPosition);
1417 aLoop.append(aTime0);
1418 drawinglayer::animation::AnimationEntryLinear aTime1(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
1419 aLoop.append(aTime1);
1420 rAnimList.append(aLoop);
1421 }
1422
1423 // always visible when stopped, so add timing for staying at the end when not endless
1424 if(0 != nRepeat)
1425 {
1427 rAnimList.append(aEnd);
1428 }
1429}
1430
1431void SdrTextObj::impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const
1432{
1433 const SdrTextAniKind eAniKind(GetTextAniKind());
1434
1435 if(SdrTextAniKind::Scroll != eAniKind && SdrTextAniKind::Alternate != eAniKind && SdrTextAniKind::Slide != eAniKind)
1436 return;
1437
1438 // get data. Goal is to calculate fTimeFullPath which is the time needed to
1439 // move animation from (0.0) to (1.0) state
1440 const SfxItemSet& rSet = GetObjectItemSet();
1441 double fAnimationDelay(static_cast<double>(rSet.Get(SDRATTR_TEXT_ANIDELAY).GetValue()));
1442 double fSingleStepWidth(static_cast<double>(rSet.Get(SDRATTR_TEXT_ANIAMOUNT).GetValue()));
1443 const SdrTextAniDirection eDirection(GetTextAniDirection());
1444 const bool bForward(SdrTextAniDirection::Right == eDirection || SdrTextAniDirection::Down == eDirection);
1445
1446 if(basegfx::fTools::equalZero(fAnimationDelay))
1447 {
1448 // default to 1/20 second
1449 fAnimationDelay = 50.0;
1450 }
1451
1452 if(basegfx::fTools::less(fSingleStepWidth, 0.0))
1453 {
1454 // data is in pixels, convert to logic. Imply PIXEL_DPI dpi.
1455 // It makes no sense to keep the view-transformation centered
1456 // definitions, so get rid of them here.
1457 fSingleStepWidth = (-fSingleStepWidth * (2540.0 / PIXEL_DPI));
1458 }
1459
1460 if(basegfx::fTools::equalZero(fSingleStepWidth))
1461 {
1462 // default to 1 millimeter
1463 fSingleStepWidth = 100.0;
1464 }
1465
1466 // use the length of the full animation path and the number of steps
1467 // to get the full path time
1468 const double fFullPathLength(fFrameLength + fTextLength);
1469 const double fNumberOfSteps(fFullPathLength / fSingleStepWidth);
1470 double fTimeFullPath(fNumberOfSteps * fAnimationDelay);
1471
1472 if(fTimeFullPath < fAnimationDelay)
1473 {
1474 fTimeFullPath = fAnimationDelay;
1475 }
1476
1477 switch(eAniKind)
1478 {
1480 {
1481 impCreateScrollTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
1482 break;
1483 }
1485 {
1486 double fRelativeTextLength(fTextLength / (fFrameLength + fTextLength));
1487 impCreateAlternateTiming(rSet, rAnimList, fRelativeTextLength, bForward, fTimeFullPath, fAnimationDelay);
1488 break;
1489 }
1491 {
1492 impCreateSlideTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
1493 break;
1494 }
1495 default : break; // SdrTextAniKind::NONE, SdrTextAniKind::Blink
1496 }
1497}
1498
1500{
1501 if (GetTextChain()->GetNilChainingEvent(this))
1502 return;
1503
1504 GetTextChain()->SetNilChainingEvent(this, true);
1505
1506 TextChainFlow aTxtChainFlow(const_cast<SdrTextObj*>(this));
1507 bool bIsOverflow;
1508
1509#ifdef DBG_UTIL
1510 // Some debug output
1511 size_t nObjCount(getSdrPageFromSdrObject()->GetObjCount());
1512 for (size_t i = 0; i < nObjCount; i++)
1513 {
1515 if(pCurObj == this)
1516 {
1517 SAL_INFO("svx.chaining", "Working on TextBox " << i);
1518 break;
1519 }
1520 }
1521#endif
1522
1523 aTxtChainFlow.CheckForFlowEvents(&rOutliner);
1524
1525 if (aTxtChainFlow.IsUnderflow() && !IsInEditMode())
1526 {
1527 // underflow-induced overflow
1528 aTxtChainFlow.ExecuteUnderflow(&rOutliner);
1529 bIsOverflow = aTxtChainFlow.IsOverflow();
1530 } else {
1531 // standard overflow (no underflow before)
1532 bIsOverflow = aTxtChainFlow.IsOverflow();
1533 }
1534
1535 if (bIsOverflow && !IsInEditMode()) {
1536 // Initialize Chaining Outliner
1537 SdrOutliner &rChainingOutl(getSdrModelFromSdrObject().GetChainingOutliner(this));
1538 ImpInitDrawOutliner( rChainingOutl );
1539 rChainingOutl.SetUpdateLayout(true);
1540 // We must pass the chaining outliner otherwise we would mess up decomposition
1541 aTxtChainFlow.ExecuteOverflow(&rOutliner, &rChainingOutl);
1542 }
1543
1544 GetTextChain()->SetNilChainingEvent(this, false);
1545}
1546
1549 const drawinglayer::primitive2d::SdrChainedTextPrimitive2D& rSdrChainedTextPrimitive,
1550 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
1551{
1552 // decompose matrix to have position and size of text
1553 basegfx::B2DVector aScale, aTranslate;
1554 double fRotate, fShearX;
1555 rSdrChainedTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
1556
1557 // use B2DRange aAnchorTextRange for calculations
1558 basegfx::B2DRange aAnchorTextRange(aTranslate);
1559 aAnchorTextRange.expand(aTranslate + aScale);
1560
1561 // prepare outliner
1562 const SfxItemSet& rTextItemSet = rSdrChainedTextPrimitive.getSdrText()->GetItemSet();
1563 SolarMutexGuard aSolarGuard;
1564 SdrOutliner& rOutliner = ImpGetDrawOutliner();
1565
1566 SdrTextVertAdjust eVAdj = GetTextVerticalAdjust(rTextItemSet);
1567 SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust(rTextItemSet);
1568 const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
1569 const Size aNullSize;
1570
1571 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
1572 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
1573
1574 rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::AUTOPAGESIZE|EEControlBits::STRETCHING);
1575 rOutliner.SetMinAutoPaperSize(aNullSize);
1576 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
1577
1578 // add one to range sizes to get back to the old Rectangle and outliner measurements
1579 const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1));
1580 const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1));
1581
1582 // Text
1583 const OutlinerParaObject* pOutlinerParaObject = rSdrChainedTextPrimitive.getSdrText()->GetOutlinerParaObject();
1584 OSL_ENSURE(pOutlinerParaObject, "impDecomposeBlockTextPrimitive used with no OutlinerParaObject (!)");
1585
1586 const bool bVerticalWriting(pOutlinerParaObject->IsEffectivelyVertical());
1587 const bool bTopToBottom(pOutlinerParaObject->IsTopToBottom());
1588 const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
1589
1590 if(IsTextFrame())
1591 {
1592 rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
1593 }
1594
1595 if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWriting)
1596 {
1597 rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
1598 }
1599
1600 if(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWriting)
1601 {
1602 rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
1603 }
1604
1605 rOutliner.SetPaperSize(aNullSize);
1606 rOutliner.SetUpdateLayout(true);
1607 // Sets original text
1608 rOutliner.SetText(*pOutlinerParaObject);
1609
1610 /* Begin overflow/underflow handling */
1611
1613
1614 /* End overflow/underflow handling */
1615
1616 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
1617 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
1618
1619 // now get back the layouted text size from outliner
1620 const Size aOutlinerTextSize(rOutliner.GetPaperSize());
1621 const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), aOutlinerTextSize.Height());
1622 basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
1623
1624 // correct horizontal translation using the now known text size
1625 if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
1626 {
1627 const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
1628
1629 if(SDRTEXTHORZADJUST_CENTER == eHAdj)
1630 {
1631 aAdjustTranslate.setX(fFree / 2.0);
1632 }
1633
1634 if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
1635 {
1636 aAdjustTranslate.setX(fFree);
1637 }
1638 }
1639
1640 // correct vertical translation using the now known text size
1641 if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
1642 {
1643 const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
1644
1645 if(SDRTEXTVERTADJUST_CENTER == eVAdj)
1646 {
1647 aAdjustTranslate.setY(fFree / 2.0);
1648 }
1649
1650 if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
1651 {
1652 aAdjustTranslate.setY(fFree);
1653 }
1654 }
1655
1656 // prepare matrices to apply to newly created primitives. aNewTransformA
1657 // will get coordinates in aOutlinerScale size and positive in X, Y.
1658 basegfx::B2DHomMatrix aNewTransformA;
1659 basegfx::B2DHomMatrix aNewTransformB;
1660
1661 // translate relative to given primitive to get same rotation and shear
1662 // as the master shape we are working on. For vertical, use the top-right
1663 // corner
1664 const double fStartInX(bVerticalWriting && bTopToBottom ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
1665 const double fStartInY(bVerticalWriting && !bTopToBottom ? aAdjustTranslate.getY() + aOutlinerScale.getY() : aAdjustTranslate.getY());
1666 aNewTransformA.translate(fStartInX, fStartInY);
1667
1668 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
1669 // move the null point which was top left to bottom right.
1670 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
1671 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
1672 aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
1673
1674 // in-between the translations of the single primitives will take place. Afterwards,
1675 // the object's transformations need to be applied
1676 aNewTransformB.shearX(fShearX);
1677 aNewTransformB.rotate(fRotate);
1678 aNewTransformB.translate(aTranslate.getX(), aTranslate.getY());
1679
1680 basegfx::B2DRange aClipRange;
1681
1682 // now break up text primitives.
1683 impTextBreakupHandler aConverter(rOutliner);
1684 aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
1685
1686 // cleanup outliner
1687 rOutliner.Clear();
1688 rOutliner.setVisualizedPage(nullptr);
1689 rOutliner.SetControlWord(nOriginalControlWord);
1690
1691 rTarget = aConverter.extractPrimitive2DSequence();
1692}
1693
1694// Direct decomposer for text visualization when you already have a prepared
1695// Outliner containing all the needed information
1698 SdrOutliner& rOutliner,
1699 const basegfx::B2DHomMatrix& rNewTransformA,
1700 const basegfx::B2DHomMatrix& rNewTransformB,
1701 const basegfx::B2DRange& rClipRange)
1702{
1703 impTextBreakupHandler aConverter(rOutliner);
1704 aConverter.decomposeBlockTextPrimitive(rNewTransformA, rNewTransformB, rClipRange);
1705 rTarget.append(aConverter.extractPrimitive2DSequence());
1706}
1707
1709{
1710 const css::uno::Any* pAny;
1711 double fTextCameraZRotateAngle = 0.0;
1712 const SfxItemSet& rSet = GetObjectItemSet();
1714
1715 pAny = rGeometryItem.GetPropertyValueByName("TextCameraZRotateAngle");
1716
1717 if ( pAny )
1718 *pAny >>= fTextCameraZRotateAngle;
1719
1720 return fTextCameraZRotateAngle;
1721}
1722
1723/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
IMPL_LINK(MaskData, PipetteHdl, const OUString &, rId, void)
Definition: _bmpmask.cxx:192
const GraphicObject maBulletGraphicObject
Point maBulletPosition
o3tl::span< const sal_Bool > mpKashidaArray
const Point & mrStartPos
const SvxFieldData * mpFieldData
sal_Int32 mnTextLen
const Color maOverlineColor
const EEngineData::WrongSpellVector * mpWrongSpellVector
sal_Int32 mnPara
const SvxFont & mrFont
const Color maTextLineColor
bool IsRTL() const
tools::Long mnWidthToFill
const css::lang::Locale * mpLocale
const OUString maText
o3tl::span< const sal_Int32 > mpDXArray
sal_Int32 mnTextStart
bool IsTopToBottom() const
bool IsEffectivelyVertical() const
void SetMaxAutoPaperSize(const Size &rSz)
void SetText(const OutlinerParaObject &)
const Size & GetPaperSize() const
void SetDrawPortionHdl(const Link< DrawPortionInfo *, void > &rLink)
void SetMinColumnWrapHeight(tools::Long nVal)
void SetMinAutoPaperSize(const Size &rSz)
void SetPaperSize(const Size &rSize)
void Clear()
sal_Int16 GetDepth(sal_Int32 nPara) const
bool SetUpdateLayout(bool bUpdate)
void SetDrawBulletHdl(const Link< DrawBulletInfo *, void > &rLink)
void SetFixedCellHeight(bool bUseFixedCellHeight)
void StripPortions()
EEControlBits GetControlWord() const
EBulletInfo GetBulletInfo(sal_Int32 nPara)
Color const & GetBackgroundColor() const
void SetControlWord(EEControlBits nWord)
void SetPolygon(const basegfx::B2DPolyPolygon &rPolyPolygon)
Size CalcTextSize()
void setGlobalScale(double rFontX=100.0, double rFontY=100.0, double rSpacingX=100.0, double rSpacingY=100.0)
void SetBackgroundColor(const Color &rColor)
sal_Int32 GetParagraphCount() const
constexpr tools::Long Y() const
constexpr tools::Long X() const
css::uno::Any * GetPropertyValueByName(const OUString &rPropName)
bool setSuitableOutlinerBg(Outliner &rOutliner) const
SdrModel & getSdrModelFromSdrObject() const
Definition: svdobj.cxx:289
const Graphic * getFillGraphic() const
bool IsGroupObject() const
Definition: svdobj.cxx:712
SdrPage * getSdrPageFromSdrObject() const
Definition: svdobj.cxx:279
const SfxItemSet * getBackgroundFillSet() const
const SfxItemSet & GetObjectItemSet() const
Definition: svdobj.cxx:1969
void setVisualizedPage(const SdrPage *pPage)
Definition: svdoutl.hxx:44
const SfxItemSet & GetItemSet() const
Definition: svdpage.hxx:342
A SdrPage contains exactly one SdrObjList and a description of the physical page dimensions (size / m...
Definition: svdpage.hxx:379
SdrPage & TRG_GetMasterPage() const
Definition: svdpage.cxx:1658
bool IsMasterPage() const
Definition: svdpage.hxx:464
bool TRG_HasMasterPage() const
Definition: svdpage.hxx:500
SdrPageProperties & getSdrPageProperties()
Definition: svdpage.cxx:1877
SdrTextHorzAdjust GetTextHorizontalAdjust() const
Definition: svdotext.cxx:346
double GetCameraZRotation() const
SdrOutliner & ImpGetDrawOutliner() const
Definition: svdotext.cxx:1194
bool IsInEditMode() const
Definition: svdotext.hxx:339
void impDecomposeContourTextPrimitive(drawinglayer::primitive2d::Primitive2DContainer &rTarget, const drawinglayer::primitive2d::SdrContourTextPrimitive2D &rSdrContourTextPrimitive, const drawinglayer::geometry::ViewInformation2D &aViewInformation) const
void impDecomposeBlockTextPrimitive(drawinglayer::primitive2d::Primitive2DContainer &rTarget, const drawinglayer::primitive2d::SdrBlockTextPrimitive2D &rSdrBlockTextPrimitive, const drawinglayer::geometry::ViewInformation2D &aViewInformation) const
void impDecomposeStretchTextPrimitive(drawinglayer::primitive2d::Primitive2DContainer &rTarget, const drawinglayer::primitive2d::SdrStretchTextPrimitive2D &rSdrStretchTextPrimitive, const drawinglayer::geometry::ViewInformation2D &aViewInformation) const
void impDecomposeChainedTextPrimitive(drawinglayer::primitive2d::Primitive2DContainer &rTarget, const drawinglayer::primitive2d::SdrChainedTextPrimitive2D &rSdrChainedTextPrimitive, const drawinglayer::geometry::ViewInformation2D &aViewInformation) const
void ImpAutoFitText(SdrOutliner &rOutliner) const
Definition: svdotext.cxx:1263
SVX_DLLPRIVATE void ImpInitDrawOutliner(SdrOutliner &rOutl) const
Definition: svdotext.cxx:1174
SdrTextAniDirection GetTextAniDirection() const
Definition: svdotext.cxx:1844
void impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList &rAnimList, double fFrameLength, double fTextLength) const
void impDecomposeAutoFitTextPrimitive(drawinglayer::primitive2d::Primitive2DContainer &rTarget, const drawinglayer::primitive2d::SdrAutoFitTextPrimitive2D &rSdrAutofitTextPrimitive, const drawinglayer::geometry::ViewInformation2D &aViewInformation) const
static void impDecomposeBlockTextPrimitiveDirect(drawinglayer::primitive2d::Primitive2DContainer &rTarget, SdrOutliner &rOutliner, const basegfx::B2DHomMatrix &rNewTransformA, const basegfx::B2DHomMatrix &rNewTransformB, const basegfx::B2DRange &rClipRange)
bool IsTextFrame() const
Definition: svdotext.hxx:359
SdrTextVertAdjust GetTextVerticalAdjust() const
Definition: svdotext.cxx:378
TextChain * GetTextChain() const
Definition: svdotext.cxx:1544
SdrTextAniKind GetTextAniKind() const
Definition: svdotext.cxx:1839
void impGetBlinkTextTiming(drawinglayer::animation::AnimationEntryList &rAnimList) const
void impHandleChainingEventsDuringDecomposition(SdrOutliner &rOutliner) const
OutlinerParaObject * GetOutlinerParaObject()
Definition: svdtext.cxx:78
const SfxItemSet & GetItemSet() const
Definition: svdtext.cxx:67
const SfxPoolItem & Get(sal_uInt16 nWhich, bool bSrchInParent=true) const
constexpr tools::Long getHeight() const
constexpr tools::Long Height() const
constexpr tools::Long getWidth() const
void setWidth(tools::Long nWidth)
void setHeight(tools::Long nHeight)
constexpr tools::Long Width() const
sal_uInt8 GetPropr() const
short GetEscapement() const
OUString CalcCaseMap(const OUString &rTxt) const
const OUString & GetRepresentation() const
SvxURLFormat GetFormat() const
const OUString & GetTargetFrame() const
const OUString & GetURL() const
bool IsOverflow() const
bool IsUnderflow() const
void ExecuteUnderflow(SdrOutliner *)
void ExecuteOverflow(SdrOutliner *, SdrOutliner *)
virtual void CheckForFlowEvents(SdrOutliner *)
void SetNilChainingEvent(const SdrTextObj *, bool)
Definition: textchain.cxx:43
bool decompose(B2DTuple &rScale, B2DTuple &rTranslate, double &rRotate, double &rShearX) const
void shearX(double fSx)
void rotate(double fRadiant)
void translate(double fX, double fY)
void scale(double fX, double fY)
void transform(const basegfx::B2DHomMatrix &rMatrix)
TYPE getWidth() const
void expand(const Tuple2D< TYPE > &rTuple)
bool isInside(const Tuple2D< TYPE > &rTuple) const
bool isEmpty() const
TYPE getHeight() const
TYPE getX() const
void setY(TYPE fY)
TYPE getY() const
void setX(TYPE fX)
void append(const AnimationEntry &rCandidate)
const css::uno::Reference< css::drawing::XDrawPage > & getVisualizedPage() const
const basegfx::B2DHomMatrix & getTextRangeTransform() const
const basegfx::B2DHomMatrix & getTextRangeTransform() const
const basegfx::B2DHomMatrix & getTextRangeTransform() const
const basegfx::B2DPolyPolygon & getUnitPolyPolygon() const
const basegfx::B2DHomMatrix & getObjectTransform() const
const basegfx::B2DHomMatrix & getTextRangeTransform() const
const OutlinerParaObject & getOutlinerParaObject() const
basegfx::B2DRange getTextBoundRect(const OUString &rText, sal_uInt32 nIndex, sal_uInt32 nLength) const
void setFont(const vcl::Font &rFont)
constexpr bool empty() const noexcept
@ AnchoredTextOverflowLegacy
for tdf#99729
double toRadians(D x)
DECL_LINK(CheckNameHdl, SvxNameDialog &, bool)
EEControlBits
constexpr TypedWhichId< SvxAdjustItem > EE_PARA_JUST(EE_PARA_START+16)
#define DFLT_ESC_SUPER
#define MAX_ESC_POS
FilterGroup & rTarget
tools::Long FRound(double fVal)
uno_Any a
#define SAL_INFO(area, stream)
bool more(const T &rfValA, const T &rfValB)
bool equalZero(const T &rfVal)
bool equal(T const &rfValA, T const &rfValB)
bool less(const T &rfValA, const T &rfValB)
B2DHomMatrix createScaleB2DHomMatrix(double fScaleX, double fScaleY)
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
B2DHomMatrix createScaleShearXRotateTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fShearX, double fRadiant, double fTranslateX, double fTranslateY)
constexpr double deg2rad(double v)
rtl::Reference< BasePrimitive2D > Primitive2DReference
TextLine mapFontLineStyleToTextLine(FontLineStyle eLineStyle)
TextStrikeout mapFontStrikeoutToTextStrikeout(FontStrikeout eFontStrikeout)
attribute::FontAttribute getFontAttributeFromVclFont(basegfx::B2DVector &o_rSize, const vcl::Font &rFont, bool bRTL, bool bBiDiStrong)
int i
long Long
SdrTextAniDirection
Definition: sdtaditm.hxx:30
SdrTextVertAdjust
Definition: sdtaitm.hxx:29
@ SDRTEXTVERTADJUST_BOTTOM
Definition: sdtaitm.hxx:31
@ SDRTEXTVERTADJUST_BLOCK
Definition: sdtaitm.hxx:32
@ SDRTEXTVERTADJUST_CENTER
Definition: sdtaitm.hxx:30
SdrTextHorzAdjust
Definition: sdtaitm.hxx:53
@ SDRTEXTHORZADJUST_LEFT
Definition: sdtaitm.hxx:53
@ SDRTEXTHORZADJUST_BLOCK
Definition: sdtaitm.hxx:56
@ SDRTEXTHORZADJUST_CENTER
Definition: sdtaitm.hxx:54
@ SDRTEXTHORZADJUST_RIGHT
Definition: sdtaitm.hxx:55
SdrTextAniKind
Animation type for text frame.
Definition: sdtakitm.hxx:29
@ Scroll
blinking
@ Slide
scroll back and forth
@ Alternate
scroll through
@ Blink
no animation
static SfxItemSet & rSet
constexpr TypedWhichId< SdrTextAniDelayItem > SDRATTR_TEXT_ANIDELAY(SDRATTR_MISC_FIRST+19)
constexpr TypedWhichId< SdrOnOffItem > SDRATTR_TEXT_CLIPVERTOVERFLOW(SDRATTR_MISC_FIRST+26)
constexpr TypedWhichId< SdrTextAniStartInsideItem > SDRATTR_TEXT_ANISTARTINSIDE(SDRATTR_MISC_FIRST+16)
constexpr TypedWhichId< SdrTextAniStopInsideItem > SDRATTR_TEXT_ANISTOPINSIDE(SDRATTR_MISC_FIRST+17)
constexpr TypedWhichId< SdrTextAniCountItem > SDRATTR_TEXT_ANICOUNT(SDRATTR_MISC_FIRST+18)
constexpr TypedWhichId< SdrTextAniAmountItem > SDRATTR_TEXT_ANIAMOUNT(SDRATTR_MISC_FIRST+20)
constexpr TypedWhichId< SdrCustomShapeGeometryItem > SDRATTR_CUSTOMSHAPE_GEOMETRY(SDRATTR_CUSTOMSHAPE_FIRST+2)
bool GetDraftFillColor(const SfxItemSet &rSet, Color &rCol)
Returns a replacement for an XFillStyle.
Definition: svdetc.cxx:240
SdrTextObj * DynCastSdrTextObj(SdrObject *pObj)
Definition: svdobj.cxx:3212
static void impCreateSlideTiming(const SfxItemSet &rSet, drawinglayer::animation::AnimationEntryList &rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
static void impCreateAlternateTiming(const SfxItemSet &rSet, drawinglayer::animation::AnimationEntryList &rAnimList, double fRelativeTextLength, bool bForward, double fTimeFullPath, double fFrequency)
#define ENDLESS_LOOP
#define ENDLESS_TIME
#define PIXEL_DPI
static void impCreateScrollTiming(const SfxItemSet &rSet, drawinglayer::animation::AnimationEntryList &rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
SvxAdjust
signed char sal_Int8
SdrPage * GetSdrPageFromXDrawPage(const uno::Reference< drawing::XDrawPage > &xDrawPage) noexcept
returns the SdrObject from the given StarOffice API wrapper
Definition: unopage.cxx:886
constexpr TypedWhichId< XFillBitmapItem > XATTR_FILLBITMAP(XATTR_FILL_FIRST+4)
constexpr TypedWhichId< XFillStyleItem > XATTR_FILLSTYLE(XATTR_FILL_FIRST)