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