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