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