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