LibreOffice Module svx (master)  1
EnhancedCustomShapeFontWork.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 
21 #include <svl/itemset.hxx>
22 #include <svx/svddef.hxx>
23 #include <svx/svdopath.hxx>
24 #include <vcl/metric.hxx>
25 #include <svx/sdasitm.hxx>
26 #include <svx/sdtfsitm.hxx>
27 #include <vcl/virdev.hxx>
28 #include <svx/svditer.hxx>
29 #include <editeng/eeitem.hxx>
30 #include <editeng/frmdiritem.hxx>
31 #include <editeng/fontitem.hxx>
32 #include <editeng/postitem.hxx>
33 #include <editeng/wghtitem.hxx>
34 #include <editeng/fhgtitem.hxx>
36 #include <svx/svdoashp.hxx>
37 #include <svx/sdshitm.hxx>
38 #include <editeng/outlobj.hxx>
39 #include <editeng/editobj.hxx>
40 #include <o3tl/numeric.hxx>
41 #include <vector>
42 #include <numeric>
43 #include <algorithm>
45 #include <com/sun/star/i18n/BreakIterator.hpp>
46 #include <com/sun/star/i18n/ScriptType.hpp>
49 #include <sal/log.hxx>
50 #include <rtl/math.hxx>
51 
52 using namespace com::sun::star;
53 using namespace com::sun::star::uno;
54 
55 namespace {
56 
57 struct FWCharacterData // representing a single character
58 {
59  std::vector< tools::PolyPolygon > vOutlines;
60  tools::Rectangle aBoundRect;
61 };
62 struct FWParagraphData // representing a single paragraph
63 {
64  OUString aString;
65  std::vector< FWCharacterData > vCharacters;
66  tools::Rectangle aBoundRect;
67  SvxFrameDirection nFrameDirection;
68 };
69 struct FWTextArea // representing multiple concluding paragraphs
70 {
71  std::vector< FWParagraphData > vParagraphs;
72  tools::Rectangle aBoundRect;
73 };
74 struct FWData // representing the whole text
75 {
76  std::vector< FWTextArea > vTextAreas;
77  double fHorizontalTextScaling;
78  double fVerticalTextScaling;
79  sal_uInt32 nMaxParagraphsPerTextArea;
80  sal_Int32 nSingleLineHeight;
81  bool bSingleLineMode;
82  bool bScaleX;
83 };
84 
85 }
86 
88  const SdrObjCustomShape& rSdrObjCustomShape,
89  const sal_uInt16 nOutlinesCount2d,
90  FWData& rFWData)
91 {
92  bool bNoErr = false;
93  bool bSingleLineMode = false;
94  sal_uInt16 nTextAreaCount = nOutlinesCount2d;
95  if ( nOutlinesCount2d & 1 )
96  bSingleLineMode = true;
97  else
98  nTextAreaCount >>= 1;
99 
100  const SdrCustomShapeGeometryItem& rGeometryItem( rSdrObjCustomShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
101  const css::uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "TextPath", "ScaleX" );
102  if (pAny)
103  *pAny >>= rFWData.bScaleX;
104  else
105  rFWData.bScaleX = false;
106 
107  if ( nTextAreaCount )
108  {
109  rFWData.bSingleLineMode = bSingleLineMode;
110 
111  // setting the strings
112  OutlinerParaObject* pParaObj(rSdrObjCustomShape.GetOutlinerParaObject());
113 
114  if ( pParaObj )
115  {
116  const EditTextObject& rTextObj = pParaObj->GetTextObject();
117  sal_Int32 nParagraphsLeft = rTextObj.GetParagraphCount();
118 
119  rFWData.nMaxParagraphsPerTextArea = ( ( nParagraphsLeft - 1 ) / nTextAreaCount ) + 1;
120  sal_Int32 j = 0;
121  while( nParagraphsLeft && nTextAreaCount )
122  {
123  FWTextArea aTextArea;
124  sal_Int32 i, nParagraphs = ( ( nParagraphsLeft - 1 ) / nTextAreaCount ) + 1;
125  for ( i = 0; i < nParagraphs; ++i, ++j )
126  {
127  FWParagraphData aParagraphData;
128  aParagraphData.aString = rTextObj.GetText( j );
129 
130  const SfxItemSet& rParaSet = rTextObj.GetParaAttribs( j ); // retrieving some paragraph attributes
131  aParagraphData.nFrameDirection = rParaSet.Get( EE_PARA_WRITINGDIR ).GetValue();
132  aTextArea.vParagraphs.push_back( aParagraphData );
133  }
134  rFWData.vTextAreas.push_back( aTextArea );
135  nParagraphsLeft -= nParagraphs;
136  nTextAreaCount--;
137  }
138  bNoErr = true;
139  }
140  }
141  return bNoErr;
142 }
143 
144 static double GetLength( const tools::Polygon& rPolygon )
145 {
146  double fLength = 0;
147  if ( rPolygon.GetSize() > 1 )
148  {
149  sal_uInt16 nCount = rPolygon.GetSize();
150  while( --nCount )
151  fLength += rPolygon.CalcDistance( nCount, nCount - 1 );
152  }
153  return fLength;
154 }
155 
156 static void UpdateScalingMode(FWData& rFWData, const tools::PolyPolygon& rOutline2d,
157  bool bSingleLineMode,
158  VirtualDevice* pVirDev, double& rScalingFactor)
159 {
160  sal_uInt16 i = 0;
161  bool bScalingFactorDefined = false; // New calculation for each font size
162  for( const auto& rTextArea : rFWData.vTextAreas )
163  {
164  // calculating the width of the corresponding 2d text area
165  double fWidth = GetLength( rOutline2d.GetObject( i++ ) );
166  if ( !bSingleLineMode )
167  {
168  fWidth += GetLength( rOutline2d.GetObject( i++ ) );
169  fWidth /= 2.0;
170  }
171 
172  for( const auto& rParagraph : rTextArea.vParagraphs )
173  {
174  double fTextWidth = pVirDev->GetTextWidth( rParagraph.aString );
175  if ( fTextWidth > 0.0 )
176  {
177  double fScale = fWidth / fTextWidth;
178  if ( !bScalingFactorDefined )
179  {
180  rScalingFactor = fScale;
181  bScalingFactorDefined = true;
182  }
183  else if (fScale < rScalingFactor)
184  {
185  rScalingFactor = fScale;
186  }
187  }
188  }
189  }
190 }
191 
192 /* CalculateHorizontalScalingFactor returns the horizontal scaling factor for
193 the whole text object, so that each text will match its corresponding 2d Outline */
195  const SdrObjCustomShape& rSdrObjCustomShape,
196  FWData& rFWData,
197  const tools::PolyPolygon& rOutline2d)
198 {
199  double fScalingFactor = 1.0;
200  rFWData.fVerticalTextScaling = 1.0;
201 
202  bool bSingleLineMode = false;
203  sal_uInt16 nOutlinesCount2d = rOutline2d.Count();
204 
205  vcl::Font aFont;
206  const SvxFontItem& rFontItem( rSdrObjCustomShape.GetMergedItem( EE_CHAR_FONTINFO ) );
207  const SvxFontHeightItem& rFontHeight( rSdrObjCustomShape.GetMergedItem( EE_CHAR_FONTHEIGHT ) );
208  sal_Int32 nFontSize = rFontHeight.GetHeight();
209 
210  SAL_WARN_IF(nFontSize > SAL_MAX_INT16, "svx", "CalculateHorizontalScalingFactor suspiciously large font height: " << nFontSize);
211 
212  if (rFWData.bScaleX)
213  aFont.SetFontHeight( nFontSize );
214  else
215  aFont.SetFontHeight( rSdrObjCustomShape.GetLogicRect().GetHeight() / rFWData.nMaxParagraphsPerTextArea );
216 
217  aFont.SetAlignment( ALIGN_TOP );
218  aFont.SetFamilyName( rFontItem.GetFamilyName() );
219  aFont.SetFamily( rFontItem.GetFamily() );
220  aFont.SetStyleName( rFontItem.GetStyleName() );
221  const SvxPostureItem& rPostureItem = rSdrObjCustomShape.GetMergedItem( EE_CHAR_ITALIC );
222  aFont.SetItalic( rPostureItem.GetPosture() );
223 
224  const SvxWeightItem& rWeightItem = rSdrObjCustomShape.GetMergedItem( EE_CHAR_WEIGHT );
225  aFont.SetWeight( rWeightItem.GetWeight() );
226  aFont.SetOrientation( 0_deg10 );
227  // initializing virtual device
228 
229  ScopedVclPtrInstance< VirtualDevice > pVirDev(DeviceFormat::DEFAULT);
230  pVirDev->SetMapMode(MapMode(MapUnit::Map100thMM));
231  pVirDev->SetFont( aFont );
232 
233  if ( nOutlinesCount2d & 1 )
234  bSingleLineMode = true;
235 
236  // In case of rFWData.bScaleX == true it loops with reduced font size until the current run
237  // results in a fScalingFactor >=1.0. The fact, that case rFWData.bScaleX == true keeps font
238  // size if possible, is not done here with scaling factor 1 but is done in method
239  // FitTextOutlinesToShapeOutlines()
240  do
241  {
242  UpdateScalingMode(rFWData, rOutline2d, bSingleLineMode, pVirDev, fScalingFactor);
243 
244  if (fScalingFactor < 1.0)
245  {
246  // we have a very large font that will require scaling down to a very small value.
247  if (nFontSize > 128)
248  {
249  // see if it will even be possible at the min size
250  sal_Int32 nOrigFontSize = nFontSize;
251  double fOrigScalingFactor = fScalingFactor;
252 
253  nFontSize = 2;
254  pVirDev->Push(vcl::PushFlags::FONT);
255  aFont.SetFontHeight(nFontSize);
256  pVirDev->SetFont(aFont);
257  UpdateScalingMode(rFWData, rOutline2d, bSingleLineMode, pVirDev, fScalingFactor);
258  pVirDev->Pop();
259 
260  const bool bHopeLess = fScalingFactor < 1.0;
261  // if its hopeless then just continue on with this FontSize of 2, otherwise
262  // continue to try smaller sizes
263  if (!bHopeLess)
264  {
265  nFontSize = nOrigFontSize;
266  fScalingFactor = fOrigScalingFactor;
267 
268  // skip directly to a small font size
269  double nEstimatedFinalFontSize = nFontSize * fScalingFactor;
270  double nOnePercentFontSize = nFontSize / 100.0;
271  if (nEstimatedFinalFontSize < nOnePercentFontSize)
272  {
273  nFontSize = std::max<int>(16, std::ceil(5 * nEstimatedFinalFontSize));
274  SAL_WARN("svx", "CalculateHorizontalScalingFactor skipping direct to: " << nFontSize << " from " << rFontHeight.GetHeight());
275  }
276  }
277  }
278  nFontSize--;
279  aFont.SetFontHeight( nFontSize );
280  pVirDev->SetFont( aFont );
281  }
282  }
283  while (rFWData.bScaleX && fScalingFactor < 1.0 && nFontSize > 1 );
284 
285  if (nFontSize > 1)
286  rFWData.fVerticalTextScaling = static_cast<double>(nFontSize) / rFontHeight.GetHeight();
287 
288  rFWData.fHorizontalTextScaling = fScalingFactor;
289 }
290 
291 static void GetTextAreaOutline(
292  const FWData& rFWData,
293  const SdrObjCustomShape& rSdrObjCustomShape,
294  FWTextArea& rTextArea,
295  bool bSameLetterHeights)
296 {
297  bool bIsVertical(rSdrObjCustomShape.IsVerticalWriting());
298  sal_Int32 nVerticalOffset = rFWData.nMaxParagraphsPerTextArea > rTextArea.vParagraphs.size()
299  ? rFWData.nSingleLineHeight / 2 : 0;
300 
301  for( auto& rParagraph : rTextArea.vParagraphs )
302  {
303  const OUString& rText = rParagraph.aString;
304  if ( !rText.isEmpty() )
305  {
306  // generating vcl/font
307  sal_uInt16 nScriptType = i18n::ScriptType::LATIN;
309  if ( xBI.is() )
310  {
311  nScriptType = xBI->getScriptType( rText, 0 );
312  if( i18n::ScriptType::WEAK == nScriptType )
313  {
314  sal_Int32 nChg = xBI->endOfScript( rText, 0, nScriptType );
315  if (nChg < rText.getLength() && nChg >= 0)
316  nScriptType = xBI->getScriptType( rText, nChg );
317  else
318  nScriptType = i18n::ScriptType::LATIN;
319  }
320  }
321  sal_uInt16 nFntItm = EE_CHAR_FONTINFO;
322  if ( nScriptType == i18n::ScriptType::COMPLEX )
323  nFntItm = EE_CHAR_FONTINFO_CTL;
324  else if ( nScriptType == i18n::ScriptType::ASIAN )
325  nFntItm = EE_CHAR_FONTINFO_CJK;
326  const SvxFontItem& rFontItem = static_cast<const SvxFontItem&>(rSdrObjCustomShape.GetMergedItem( nFntItm ));
327  vcl::Font aFont;
328 
329  aFont.SetFontHeight( rFWData.nSingleLineHeight );
330 
331  aFont.SetAlignment( ALIGN_TOP );
332 
333  aFont.SetFamilyName( rFontItem.GetFamilyName() );
334  aFont.SetFamily( rFontItem.GetFamily() );
335  aFont.SetStyleName( rFontItem.GetStyleName() );
336  aFont.SetOrientation( 0_deg10 );
337 
338  const SvxPostureItem& rPostureItem = rSdrObjCustomShape.GetMergedItem( EE_CHAR_ITALIC );
339  aFont.SetItalic( rPostureItem.GetPosture() );
340 
341  const SvxWeightItem& rWeightItem = rSdrObjCustomShape.GetMergedItem( EE_CHAR_WEIGHT );
342  aFont.SetWeight( rWeightItem.GetWeight() );
343 
344  // initializing virtual device
345  ScopedVclPtrInstance< VirtualDevice > pVirDev(DeviceFormat::DEFAULT);
346  pVirDev->SetMapMode(MapMode(MapUnit::Map100thMM));
347  pVirDev->SetFont( aFont );
348  pVirDev->EnableRTL();
349  if ( rParagraph.nFrameDirection == SvxFrameDirection::Horizontal_RL_TB )
350  pVirDev->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::BiDiRtl );
351 
352  const SvxCharScaleWidthItem& rCharScaleWidthItem = rSdrObjCustomShape.GetMergedItem( EE_CHAR_FONTWIDTH );
353  sal_uInt16 nCharScaleWidth = rCharScaleWidthItem.GetValue();
354  sal_Int32 nWidth = 0;
355 
356  // VERTICAL
357  if ( bIsVertical )
358  {
359  // vertical _> each single character needs to be rotated by 90
360  sal_Int32 i;
361  sal_Int32 nHeight = 0;
362  tools::Rectangle aSingleCharacterUnion;
363  for ( i = 0; i < rText.getLength(); i++ )
364  {
365  FWCharacterData aCharacterData;
366  OUString aCharText( rText[ i ] );
367  if ( pVirDev->GetTextOutlines( aCharacterData.vOutlines, aCharText, 0, 0, -1, nWidth, {} ) )
368  {
369  sal_Int32 nTextWidth = pVirDev->GetTextWidth( aCharText);
370  if ( aCharacterData.vOutlines.empty() )
371  {
372  nHeight += rFWData.nSingleLineHeight;
373  }
374  else
375  {
376  for ( auto& rOutline : aCharacterData.vOutlines )
377  {
378  // rotating
379  rOutline.Rotate( Point( nTextWidth / 2, rFWData.nSingleLineHeight / 2 ), 900_deg10 );
380  aCharacterData.aBoundRect.Union( rOutline.GetBoundRect() );
381  }
382  for ( auto& rOutline : aCharacterData.vOutlines )
383  {
384  sal_Int32 nM = - aCharacterData.aBoundRect.Left() + nHeight;
385  rOutline.Move( nM, 0 );
386  aCharacterData.aBoundRect.Move( nM, 0 );
387  }
388  nHeight += aCharacterData.aBoundRect.GetWidth() + ( rFWData.nSingleLineHeight / 5 );
389  aSingleCharacterUnion.Union( aCharacterData.aBoundRect );
390  }
391  }
392  rParagraph.vCharacters.push_back( aCharacterData );
393  }
394  for ( auto& rCharacter : rParagraph.vCharacters )
395  {
396  for ( auto& rOutline : rCharacter.vOutlines )
397  {
398  rOutline.Move( ( aSingleCharacterUnion.GetWidth() - rCharacter.aBoundRect.GetWidth() ) / 2, 0 );
399  }
400  }
401  }
402  else
403  {
404  std::vector<sal_Int32> aDXArry;
405  if ( ( nCharScaleWidth != 100 ) && nCharScaleWidth )
406  { // applying character spacing
407  pVirDev->GetTextArray( rText, &aDXArry);
408  FontMetric aFontMetric( pVirDev->GetFontMetric() );
409  aFont.SetAverageFontWidth( static_cast<sal_Int32>( static_cast<double>(aFontMetric.GetAverageFontWidth()) * ( double(100) / static_cast<double>(nCharScaleWidth) ) ) );
410  pVirDev->SetFont( aFont );
411  }
412  FWCharacterData aCharacterData;
413  if ( pVirDev->GetTextOutlines( aCharacterData.vOutlines, rText, 0, 0, -1, nWidth, aDXArry ) )
414  {
415  rParagraph.vCharacters.push_back( aCharacterData );
416  }
417  else
418  {
419  // GetTextOutlines failed what usually means that it is
420  // not implemented. To make FontWork not fail (it is
421  // dependent of graphic content to get a Range) create
422  // a rectangle substitution for now
423  pVirDev->GetTextArray( rText, &aDXArry);
424  aCharacterData.vOutlines.clear();
425 
426  if(aDXArry.size())
427  {
428  for(size_t a(0); a < aDXArry.size(); a++)
429  {
430  const basegfx::B2DPolygon aPolygon(
433  0 == a ? 0 : aDXArry[a - 1],
434  0,
435  aDXArry[a],
436  aFont.GetFontHeight()
437  )));
438  aCharacterData.vOutlines.push_back(tools::PolyPolygon(tools::Polygon(aPolygon)));
439  }
440  }
441  else
442  {
443  const basegfx::B2DPolygon aPolygon(
446  0,
447  0,
448  aDXArry.empty() ? 10 : aDXArry.back(),
449  aFont.GetFontHeight()
450  )));
451  aCharacterData.vOutlines.push_back(tools::PolyPolygon(tools::Polygon(aPolygon)));
452  }
453 
454 
455  rParagraph.vCharacters.push_back( aCharacterData );
456  }
457  }
458 
459  // vertical alignment
460  for ( auto& rCharacter : rParagraph.vCharacters )
461  {
462  for( tools::PolyPolygon& rPolyPoly : rCharacter.vOutlines )
463  {
464  if ( nVerticalOffset )
465  rPolyPoly.Move( 0, nVerticalOffset );
466 
467  // retrieving the boundrect for the paragraph
468  tools::Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
469  rParagraph.aBoundRect.Union( aBoundRect );
470  }
471  }
472  }
473  // updating the boundrect for the text area by merging the current paragraph boundrect
474  if ( rParagraph.aBoundRect.IsEmpty() )
475  {
476  if ( rTextArea.aBoundRect.IsEmpty() )
477  rTextArea.aBoundRect = tools::Rectangle( Point( 0, 0 ), Size( 1, rFWData.nSingleLineHeight ) );
478  else
479  rTextArea.aBoundRect.AdjustBottom(rFWData.nSingleLineHeight );
480  }
481  else
482  {
483  tools::Rectangle& rParagraphBoundRect = rParagraph.aBoundRect;
484  rTextArea.aBoundRect.Union( rParagraphBoundRect );
485 
486  if ( bSameLetterHeights )
487  {
488  for ( auto& rCharacter : rParagraph.vCharacters )
489  {
490  for( auto& rOutline : rCharacter.vOutlines )
491  {
492  tools::Rectangle aPolyPolyBoundRect( rOutline.GetBoundRect() );
493  if (aPolyPolyBoundRect.GetHeight() != rParagraphBoundRect.GetHeight() && aPolyPolyBoundRect.GetHeight())
494  rOutline.Scale( 1.0, static_cast<double>(rParagraphBoundRect.GetHeight()) / aPolyPolyBoundRect.GetHeight() );
495  aPolyPolyBoundRect = rOutline.GetBoundRect();
496  sal_Int32 nMove = aPolyPolyBoundRect.Top() - rParagraphBoundRect.Top();
497  if ( nMove )
498  rOutline.Move( 0, -nMove );
499  }
500  }
501  }
502  }
503  if ( bIsVertical )
504  nVerticalOffset -= rFWData.nSingleLineHeight;
505  else
506  nVerticalOffset += rFWData.nSingleLineHeight;
507  }
508 }
509 
510 static bool GetFontWorkOutline(
511  FWData& rFWData,
512  const SdrObjCustomShape& rSdrObjCustomShape)
513 {
514  SdrTextHorzAdjust eHorzAdjust(rSdrObjCustomShape.GetMergedItem( SDRATTR_TEXT_HORZADJUST ).GetValue());
515  drawing::TextFitToSizeType const eFTS(rSdrObjCustomShape.GetMergedItem( SDRATTR_TEXT_FITTOSIZE ).GetValue());
516 
517  bool bSameLetterHeights = false;
518  const SdrCustomShapeGeometryItem& rGeometryItem(rSdrObjCustomShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
519  const css::uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "TextPath", "SameLetterHeights" );
520  if ( pAny )
521  *pAny >>= bSameLetterHeights;
522 
523  const SvxFontHeightItem& rFontHeight( rSdrObjCustomShape.GetMergedItem( EE_CHAR_FONTHEIGHT ) );
524  if (rFWData.bScaleX)
525  rFWData.nSingleLineHeight = rFWData.fVerticalTextScaling * rFontHeight.GetHeight();
526  else
527  rFWData.nSingleLineHeight = static_cast<sal_Int32>( ( static_cast<double>( rSdrObjCustomShape.GetLogicRect().GetHeight() )
528  / rFWData.nMaxParagraphsPerTextArea ) * rFWData.fHorizontalTextScaling );
529 
530  if (rFWData.nSingleLineHeight == SAL_MIN_INT32)
531  return false;
532 
533  for ( auto& rTextArea : rFWData.vTextAreas )
534  {
536  rFWData,
537  rSdrObjCustomShape,
538  rTextArea,
539  bSameLetterHeights);
540 
541  if (eFTS == drawing::TextFitToSizeType_ALLLINES ||
542  // tdf#97630 interpret PROPORTIONAL same as ALLLINES so we don't
543  // need another ODF attribute!
544  eFTS == drawing::TextFitToSizeType_PROPORTIONAL)
545  {
546  for ( auto& rParagraph : rTextArea.vParagraphs )
547  {
548  sal_Int32 nParaWidth = rParagraph.aBoundRect.GetWidth();
549  if ( nParaWidth )
550  {
551  double fScale = static_cast<double>(rTextArea.aBoundRect.GetWidth()) / nParaWidth;
552 
553  for ( auto& rCharacter : rParagraph.vCharacters )
554  {
555  for( auto& rOutline : rCharacter.vOutlines )
556  {
557  rOutline.Scale( fScale, 1.0 );
558  }
559  }
560  }
561  }
562  }
563  else if (rFWData.bScaleX)
564  {
565  const SdrTextVertAdjust nVertJustify = rSdrObjCustomShape.GetMergedItem( SDRATTR_TEXT_VERTADJUST ).GetValue();
566  double fFactor = nVertJustify == SdrTextVertAdjust::SDRTEXTVERTADJUST_BOTTOM ? -0.5 : ( nVertJustify == SdrTextVertAdjust::SDRTEXTVERTADJUST_TOP ? 0.5 : 0 );
567 
568  for ( auto& rParagraph : rTextArea.vParagraphs )
569  {
570  sal_Int32 nHorzDiff = 0;
571  sal_Int32 nVertDiff = static_cast<double>( rFWData.nSingleLineHeight ) * fFactor * ( rTextArea.vParagraphs.size() - 1 );
572 
573  if ( eHorzAdjust == SDRTEXTHORZADJUST_CENTER )
574  nHorzDiff = ( rFWData.fHorizontalTextScaling * rTextArea.aBoundRect.GetWidth() - rParagraph.aBoundRect.GetWidth() ) / 2;
575  else if ( eHorzAdjust == SDRTEXTHORZADJUST_RIGHT )
576  nHorzDiff = ( rFWData.fHorizontalTextScaling * rTextArea.aBoundRect.GetWidth() - rParagraph.aBoundRect.GetWidth() );
577 
578  if (nHorzDiff || nVertDiff)
579  {
580  for ( auto& rCharacter : rParagraph.vCharacters )
581  {
582  for( auto& rOutline : rCharacter.vOutlines )
583  {
584  rOutline.Move( nHorzDiff, nVertDiff );
585  }
586  }
587  }
588  }
589  }
590  else
591  {
592  switch( eHorzAdjust )
593  {
596  {
597  for ( auto& rParagraph : rTextArea.vParagraphs )
598  {
599  sal_Int32 nHorzDiff = 0;
600  if ( eHorzAdjust == SDRTEXTHORZADJUST_CENTER )
601  nHorzDiff = ( rTextArea.aBoundRect.GetWidth() - rParagraph.aBoundRect.GetWidth() ) / 2;
602  else if ( eHorzAdjust == SDRTEXTHORZADJUST_RIGHT )
603  nHorzDiff = ( rTextArea.aBoundRect.GetWidth() - rParagraph.aBoundRect.GetWidth() );
604  if ( nHorzDiff )
605  {
606  for ( auto& rCharacter : rParagraph.vCharacters )
607  {
608  for( auto& rOutline : rCharacter.vOutlines )
609  {
610  rOutline.Move( nHorzDiff, 0 );
611  }
612  }
613  }
614  }
615  }
616  break;
617  default:
618  case SDRTEXTHORZADJUST_BLOCK : break; // don't know
619  case SDRTEXTHORZADJUST_LEFT : break; // already left aligned -> nothing to do
620  }
621  }
622  }
623 
624  return true;
625 }
626 
628 {
629  basegfx::B2DPolyPolygon aOutlines2d;
630 
631  SdrObjListIter aObjListIter( *pShape2d, SdrIterMode::DeepWithGroups );
632  while( aObjListIter.IsMore() )
633  {
634  SdrObject* pPartObj = aObjListIter.Next();
635  if ( auto pPathObj = dynamic_cast<const SdrPathObj*>( pPartObj))
636  {
637  basegfx::B2DPolyPolygon aCandidate(pPathObj->GetPathPoly());
638  if(aCandidate.areControlPointsUsed())
639  {
640  aCandidate = basegfx::utils::adaptiveSubdivideByAngle(aCandidate);
641  }
642  aOutlines2d.append(aCandidate);
643  }
644  }
645 
646  return aOutlines2d;
647 }
648 
649 static void CalcDistances( const tools::Polygon& rPoly, std::vector< double >& rDistances )
650 {
651  sal_uInt16 i, nCount = rPoly.GetSize();
652  if ( nCount <= 1 )
653  return;
654 
655  for ( i = 0; i < nCount; i++ )
656  {
657  double fDistance = i ? rPoly.CalcDistance( i, i - 1 ) : 0.0;
658  rDistances.push_back( fDistance );
659  }
660  std::partial_sum( rDistances.begin(), rDistances.end(), rDistances.begin() );
661  double fLength = rDistances[ rDistances.size() - 1 ];
662  if ( fLength > 0.0 )
663  {
664  for ( auto& rDistance : rDistances )
665  rDistance /= fLength;
666  }
667 }
668 
669 static void InsertMissingOutlinePoints( const std::vector< double >& rDistances,
670  const tools::Rectangle& rTextAreaBoundRect, tools::Polygon& rPoly )
671 {
672  sal_uInt16 nSize = rPoly.GetSize();
673  if (nSize == 0)
674  return;
675 
676  tools::Long nTextWidth = rTextAreaBoundRect.GetWidth();
677 
678  if (nTextWidth == 0)
679  throw o3tl::divide_by_zero();
680 
681  double fLastDistance = 0.0;
682  for (sal_uInt16 i = 0; i < nSize; ++i)
683  {
684  Point& rPoint = rPoly[ i ];
685  double fDistance = static_cast<double>( rPoint.X() - rTextAreaBoundRect.Left() ) / static_cast<double>(nTextWidth);
686  if ( i )
687  {
688  if ( fDistance > fLastDistance )
689  {
690  std::vector< double >::const_iterator aIter = std::upper_bound( rDistances.begin(), rDistances.end(), fLastDistance );
691  if ( aIter != rDistances.end() && ( *aIter > fLastDistance ) && ( *aIter < fDistance ) )
692  {
693  Point& rPt0 = rPoly[ i - 1 ];
694  sal_Int32 fX = rPoint.X() - rPt0.X();
695  sal_Int32 fY = rPoint.Y() - rPt0.Y();
696  double fd = ( 1.0 / ( fDistance - fLastDistance ) ) * ( *aIter - fLastDistance );
697  rPoly.Insert( i, Point( static_cast<sal_Int32>( rPt0.X() + fX * fd ), static_cast<sal_Int32>( rPt0.Y() + fY * fd ) ) );
698  fDistance = *aIter;
699  }
700  }
701  else if ( fDistance < fLastDistance )
702  {
703  std::vector< double >::const_iterator aIter = std::lower_bound( rDistances.begin(), rDistances.end(), fLastDistance );
704  if ( aIter != rDistances.begin() )
705  {
706  --aIter;
707  if ( ( *aIter > fDistance ) && ( *aIter < fLastDistance ) )
708  {
709  Point& rPt0 = rPoly[ i - 1 ];
710  sal_Int32 fX = rPoint.X() - rPt0.X();
711  sal_Int32 fY = rPoint.Y() - rPt0.Y();
712  double fd = ( 1.0 / ( fDistance - fLastDistance ) ) * ( *aIter - fLastDistance );
713  rPoly.Insert( i, Point( static_cast<sal_Int32>( rPt0.X() + fX * fd ), static_cast<sal_Int32>( rPt0.Y() + fY * fd ) ) );
714  fDistance = *aIter;
715  }
716  }
717  }
718  }
719  fLastDistance = fDistance;
720  }
721 }
722 
723 static void GetPoint( const tools::Polygon& rPoly, const std::vector< double >& rDistances, const double& fX, double& fx1, double& fy1 )
724 {
725  fy1 = fx1 = 0.0;
726  if ( rPoly.GetSize() <= 1 )
727  return;
728 
729  std::vector< double >::const_iterator aIter = std::lower_bound( rDistances.begin(), rDistances.end(), fX );
730  sal_uInt16 nIdx = sal::static_int_cast<sal_uInt16>( std::distance( rDistances.begin(), aIter ) );
731  if ( aIter == rDistances.end() )
732  nIdx--;
733  const Point& rPt = rPoly[ nIdx ];
734  fx1 = rPt.X();
735  fy1 = rPt.Y();
736  if ( !nIdx || ( aIter == rDistances.end() ) || rtl::math::approxEqual( *aIter, fX ) )
737  return;
738 
739  nIdx = sal::static_int_cast<sal_uInt16>( std::distance( rDistances.begin(), aIter ) );
740  double fDist0 = *( aIter - 1 );
741  double fd = ( 1.0 / ( *aIter - fDist0 ) ) * ( fX - fDist0 );
742  const Point& rPt2 = rPoly[ nIdx - 1 ];
743  double fWidth = rPt.X() - rPt2.X();
744  double fHeight= rPt.Y() - rPt2.Y();
745  fWidth *= fd;
746  fHeight*= fd;
747  fx1 = rPt2.X() + fWidth;
748  fy1 = rPt2.Y() + fHeight;
749 }
750 
751 static void FitTextOutlinesToShapeOutlines( const tools::PolyPolygon& aOutlines2d, FWData& rFWData )
752 {
753  sal_uInt16 nOutline2dIdx = 0;
754  for( auto& rTextArea : rFWData.vTextAreas )
755  {
756  tools::Rectangle rTextAreaBoundRect = rTextArea.aBoundRect;
757  sal_Int32 nLeft = rTextAreaBoundRect.Left();
758  sal_Int32 nTop = rTextAreaBoundRect.Top();
759  sal_Int32 nWidth = rTextAreaBoundRect.GetWidth();
760  sal_Int32 nHeight= rTextAreaBoundRect.GetHeight();
761 
762  if (rFWData.bScaleX)
763  {
764  nWidth *= rFWData.fHorizontalTextScaling;
765  }
766 
767  if ( rFWData.bSingleLineMode && nHeight && nWidth )
768  {
769  if ( nOutline2dIdx >= aOutlines2d.Count() )
770  break;
771  const tools::Polygon& rOutlinePoly( aOutlines2d[ nOutline2dIdx++ ] );
772  const sal_uInt16 nPointCount = rOutlinePoly.GetSize();
773  if ( nPointCount > 1 )
774  {
775  std::vector< double > vDistances;
776  vDistances.reserve( nPointCount );
777  CalcDistances( rOutlinePoly, vDistances );
778  if ( !vDistances.empty() )
779  {
780  for( auto& rParagraph : rTextArea.vParagraphs )
781  {
782  for ( auto& rCharacter : rParagraph.vCharacters )
783  {
784  for( tools::PolyPolygon& rPolyPoly : rCharacter.vOutlines )
785  {
786  tools::Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
787  double fx1 = aBoundRect.Left() - nLeft;
788  double fx2 = aBoundRect.Right() - nLeft;
789  double fy1, fy2;
790  double fM1 = fx1 / static_cast<double>(nWidth);
791  double fM2 = fx2 / static_cast<double>(nWidth);
792 
793  GetPoint( rOutlinePoly, vDistances, fM1, fx1, fy1 );
794  GetPoint( rOutlinePoly, vDistances, fM2, fx2, fy2 );
795 
796  double fvx = fy2 - fy1;
797  double fvy = - ( fx2 - fx1 );
798  fx1 = fx1 + ( ( fx2 - fx1 ) * 0.5 );
799  fy1 = fy1 + ( ( fy2 - fy1 ) * 0.5 );
800 
801  double fAngle = atan2( -fvx, -fvy );
802  double fL = hypot( fvx, fvy );
803  if (fL == 0.0)
804  {
805  SAL_WARN("svx", "FitTextOutlinesToShapeOutlines div-by-zero, abandon fit");
806  break;
807  }
808  fvx = fvx / fL;
809  fvy = fvy / fL;
810  fL = rTextArea.aBoundRect.GetHeight() / 2.0 + rTextArea.aBoundRect.Top() - rParagraph.aBoundRect.Center().Y();
811  fvx *= fL;
812  fvy *= fL;
813  rPolyPoly.Rotate( Point( aBoundRect.Center().X(), rParagraph.aBoundRect.Center().Y() ), sin( fAngle ), cos( fAngle ) );
814  rPolyPoly.Move( static_cast<sal_Int32>( ( fx1 + fvx )- aBoundRect.Center().X() ), static_cast<sal_Int32>( ( fy1 + fvy ) - rParagraph.aBoundRect.Center().Y() ) );
815  }
816  }
817  }
818  }
819  }
820  }
821  else
822  {
823  if ( ( nOutline2dIdx + 1 ) >= aOutlines2d.Count() )
824  break;
825  const tools::Polygon& rOutlinePoly( aOutlines2d[ nOutline2dIdx++ ] );
826  const tools::Polygon& rOutlinePoly2( aOutlines2d[ nOutline2dIdx++ ] );
827  const sal_uInt16 nPointCount = rOutlinePoly.GetSize();
828  const sal_uInt16 nPointCount2 = rOutlinePoly2.GetSize();
829  if ( ( nPointCount > 1 ) && ( nPointCount2 > 1 ) )
830  {
831  std::vector< double > vDistances;
832  vDistances.reserve( nPointCount );
833  std::vector< double > vDistances2;
834  vDistances2.reserve( nPointCount2 );
835  CalcDistances( rOutlinePoly, vDistances );
836  CalcDistances( rOutlinePoly2, vDistances2 );
837  for( auto& rParagraph : rTextArea.vParagraphs )
838  {
839  for ( auto& rCharacter : rParagraph.vCharacters )
840  {
841  for( tools::PolyPolygon& rPolyPoly : rCharacter.vOutlines )
842  {
843  sal_uInt16 i, nPolyCount = rPolyPoly.Count();
844  for ( i = 0; i < nPolyCount; i++ )
845  {
846  // #i35928#
847  basegfx::B2DPolygon aCandidate(rPolyPoly[ i ].getB2DPolygon());
848 
849  if(aCandidate.areControlPointsUsed())
850  {
851  aCandidate = basegfx::utils::adaptiveSubdivideByAngle(aCandidate);
852  }
853 
854  // create local polygon copy to work on
855  tools::Polygon aLocalPoly(aCandidate);
856 
857  InsertMissingOutlinePoints( vDistances, rTextAreaBoundRect, aLocalPoly );
858  InsertMissingOutlinePoints( vDistances2, rTextAreaBoundRect, aLocalPoly );
859 
860  sal_uInt16 _nPointCount = aLocalPoly.GetSize();
861  if (_nPointCount)
862  {
863  if (!nWidth || !nHeight)
864  throw o3tl::divide_by_zero();
865  for (sal_uInt16 j = 0; j < _nPointCount; ++j)
866  {
867  Point& rPoint = aLocalPoly[ j ];
868  rPoint.AdjustX( -nLeft );
869  rPoint.AdjustY( -nTop );
870  double fX = static_cast<double>(rPoint.X()) / static_cast<double>(nWidth);
871  double fY = static_cast<double>(rPoint.Y()) / static_cast<double>(nHeight);
872 
873  double fx1, fy1, fx2, fy2;
874  GetPoint( rOutlinePoly, vDistances, fX, fx1, fy1 );
875  GetPoint( rOutlinePoly2, vDistances2, fX, fx2, fy2 );
876  double fWidth = fx2 - fx1;
877  double fHeight= fy2 - fy1;
878  rPoint.setX( static_cast<sal_Int32>( fx1 + fWidth * fY ) );
879  rPoint.setY( static_cast<sal_Int32>( fy1 + fHeight* fY ) );
880  }
881  }
882 
883  // write back polygon
884  rPolyPoly[ i ] = aLocalPoly;
885  }
886  }
887  }
888  }
889  }
890  }
891  }
892 }
893 
895  const FWData& rFWData,
896  const SdrObjCustomShape& rSdrObjCustomShape)
897 {
898  SdrObject* pRet = nullptr;
899  basegfx::B2DPolyPolygon aPolyPoly;
900  if ( !rFWData.vTextAreas.empty() )
901  {
902  for ( const auto& rTextArea : rFWData.vTextAreas )
903  {
904  for ( const auto& rParagraph : rTextArea.vParagraphs )
905  {
906  for ( const auto& rCharacter : rParagraph.vCharacters )
907  {
908  for( const auto& rOutline : rCharacter.vOutlines )
909  {
910  aPolyPoly.append( rOutline.getB2DPolyPolygon() );
911  }
912  }
913  }
914  }
915 
916  pRet = new SdrPathObj(
917  rSdrObjCustomShape.getSdrModelFromSdrObject(),
919  aPolyPoly);
920 
921  SfxItemSet aSet(rSdrObjCustomShape.GetMergedItemSet());
922  aSet.ClearItem( SDRATTR_TEXTDIRECTION ); //SJ: vertical writing is not required, by removing this item no outliner is created
923  aSet.Put(makeSdrShadowItem(false)); // #i37011# NO shadow for FontWork geometry
924  pRet->SetMergedItemSet( aSet ); // * otherwise we would crash, because the outliner tries to create a Paraobject, but there is no model
925  }
926 
927  return pRet;
928 }
929 
931 
933 {
934  if ( !mxBreakIterator.is() )
935  {
936  Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
937  mxBreakIterator = i18n::BreakIterator::create(xContext);
938  }
939  return mxBreakIterator;
940 }
941 
943  const SdrObject* pShape2d,
944  const SdrObjCustomShape& rSdrObjCustomShape)
945 {
946  SdrObject* pRet = nullptr;
947 
948  tools::PolyPolygon aOutlines2d( GetOutlinesFromShape2d( pShape2d ) );
949  sal_uInt16 nOutlinesCount2d = aOutlines2d.Count();
950  if ( nOutlinesCount2d )
951  {
952  FWData aFWData;
953 
954  if(InitializeFontWorkData(rSdrObjCustomShape, nOutlinesCount2d, aFWData))
955  {
956  /* retrieves the horizontal scaling factor that has to be used
957  to fit each paragraph text into its corresponding 2d outline */
959  rSdrObjCustomShape,
960  aFWData,
961  aOutlines2d);
962 
963  /* retrieving the Outlines for the each Paragraph. */
964  if(!GetFontWorkOutline(
965  aFWData,
966  rSdrObjCustomShape))
967  {
968  return nullptr;
969  }
970 
971  FitTextOutlinesToShapeOutlines( aOutlines2d, aFWData );
972 
974  aFWData,
975  rSdrObjCustomShape);
976  }
977  }
978  return pRet;
979 }
980 
981 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void SetFamily(FontFamily)
sal_uInt16 Count() const
constexpr TypedWhichId< SvxFontItem > EE_CHAR_FONTINFO_CJK(EE_CHAR_START+17)
constexpr TypedWhichId< SdrTextHorzAdjustItem > SDRATTR_TEXT_HORZADJUST(SDRATTR_MISC_FIRST+13)
const tools::Polygon & GetObject(sal_uInt16 nPos) const
static void CalculateHorizontalScalingFactor(const SdrObjCustomShape &rSdrObjCustomShape, FWData &rFWData, const tools::PolyPolygon &rOutline2d)
void SetAverageFontWidth(tools::Long nWidth)
static void GetTextAreaOutline(const FWData &rFWData, const SdrObjCustomShape &rSdrObjCustomShape, FWTextArea &rTextArea, bool bSameLetterHeights)
static bool InitializeFontWorkData(const SdrObjCustomShape &rSdrObjCustomShape, const sal_uInt16 nOutlinesCount2d, FWData &rFWData)
constexpr tools::Long Left() const
SdrTextHorzAdjust
Definition: sdtaitm.hxx:53
static css::uno::Reference< css::i18n::XBreakIterator > mxBreakIterator
long Long
constexpr TypedWhichId< SvxPostureItem > EE_CHAR_ITALIC(EE_CHAR_START+7)
void SetWeight(FontWeight)
bool areControlPointsUsed() const
SvxFrameDirection
constexpr TypedWhichId< SvxFontHeightItem > EE_CHAR_FONTHEIGHT(EE_CHAR_START+2)
FontFamily GetFamily() const
constexpr TypedWhichId< SvxFrameDirectionItem > EE_PARA_WRITINGDIR(EE_PARA_START+0)
FontItalic GetPosture() const
tools::Long GetFontHeight() const
bool IsMore() const
Definition: svditer.hxx:62
constexpr TypedWhichId< SvxFontItem > EE_CHAR_FONTINFO_CTL(EE_CHAR_START+18)
void Rotate(const Point &rCenter, double fSin, double fCos)
int nCount
constexpr tools::Long GetWidth() const
void SetMergedItemSet(const SfxItemSet &rSet, bool bClearAllItems=false)
Definition: svdobj.cxx:2002
virtual const SfxItemSet & GetParaAttribs(sal_Int32 nPara) const =0
constexpr TypedWhichId< SvxWeightItem > EE_CHAR_WEIGHT(EE_CHAR_START+4)
static void GetPoint(const tools::Polygon &rPoly, const std::vector< double > &rDistances, const double &fX, double &fx1, double &fy1)
const OUString & GetStyleName() const
static double GetLength(const tools::Polygon &rPolygon)
constexpr TypedWhichId< SvxWritingModeItem > SDRATTR_TEXTDIRECTION(SDRATTR_NOTPERSIST_FIRST+34)
constexpr TypedWhichId< SdrTextFitToSizeTypeItem > SDRATTR_TEXT_FITTOSIZE(SDRATTR_MISC_FIRST+3)
sal_uInt16 ClearItem(sal_uInt16 nWhich=0)
void SetFamilyName(const OUString &rFamilyName)
constexpr TypedWhichId< SdrCustomShapeGeometryItem > SDRATTR_CUSTOMSHAPE_GEOMETRY(SDRATTR_CUSTOMSHAPE_FIRST+2)
int i
tools::Long GetTextWidth(const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, vcl::text::TextLayoutCache const *=nullptr, SalLayoutGlyphs const *const pLayoutCache=nullptr) const
uno_Any a
FontWeight GetWeight() const
static void CalcDistances(const tools::Polygon &rPoly, std::vector< double > &rDistances)
static bool GetFontWorkOutline(FWData &rFWData, const SdrObjCustomShape &rSdrObjCustomShape)
constexpr TypedWhichId< SvxCharScaleWidthItem > EE_CHAR_FONTWIDTH(EE_CHAR_START+3)
void SetOrientation(Degree10 nLineOrientation)
constexpr TypedWhichId< SdrTextVertAdjustItem > SDRATTR_TEXT_VERTADJUST(SDRATTR_MISC_FIRST+8)
B2DPolygon createPolygonFromRect(const B2DRectangle &rRect, double fRadiusX, double fRadiusY)
virtual OUString GetText(sal_Int32 nPara) const =0
#define SAL_MIN_INT32
static void FitTextOutlinesToShapeOutlines(const tools::PolyPolygon &aOutlines2d, FWData &rFWData)
void Move(tools::Long nHorzMove, tools::Long nVertMove)
SdrModel & getSdrModelFromSdrObject() const
Definition: svdobj.cxx:284
constexpr tools::Long Top() const
sal_uInt16 GetSize() const
static void UpdateScalingMode(FWData &rFWData, const tools::PolyPolygon &rOutline2d, bool bSingleLineMode, VirtualDevice *pVirDev, double &rScalingFactor)
static SdrObject * CreateSdrObjectFromParagraphOutlines(const FWData &rFWData, const SdrObjCustomShape &rSdrObjCustomShape)
const SfxPoolItem & GetMergedItem(const sal_uInt16 nWhich) const
Definition: svdobj.cxx:2012
double CalcDistance(sal_uInt16 nPt1, sal_uInt16 nPt2) const
Abstract DrawObject.
Definition: svdobj.hxx:260
void SetAlignment(TextAlign)
void append(const B2DPolygon &rPolygon, sal_uInt32 nCount=1)
const OUString & GetFamilyName() const
SdrObject * Next()
Definition: svditer.hxx:63
ALIGN_TOP
void SetStyleName(const OUString &rStyleName)
virtual bool IsVerticalWriting() const
Definition: svdotext.cxx:1470
sal_uInt32 GetHeight() const
B2DPolygon adaptiveSubdivideByAngle(const B2DPolygon &rCandidate, double fAngleBound)
const SfxPoolItem & Get(sal_uInt16 nWhich, bool bSrchInParent=true) const
tools::Rectangle & Union(const tools::Rectangle &rRect)
#define SAL_WARN_IF(condition, area, stream)
virtual OutlinerParaObject * GetOutlinerParaObject() const override
Definition: svdotext.cxx:1330
virtual sal_Int32 GetParagraphCount() const =0
static SdrObject * CreateFontWork(const SdrObject *pShape2d, const SdrObjCustomShape &rSdrObjCustomShape)
const SfxItemSet & GetMergedItemSet() const
Definition: svdobj.cxx:1977
#define SAL_MAX_INT16
SdrOnOffItem makeSdrShadowItem(bool bShadow)
Definition: sdshitm.hxx:25
constexpr TypedWhichId< SvxFontItem > EE_CHAR_FONTINFO(EE_CHAR_START+1)
static basegfx::B2DPolyPolygon GetOutlinesFromShape2d(const SdrObject *pShape2d)
css::uno::Any * GetPropertyValueByName(const OUString &rPropName)
static css::uno::Reference< css::i18n::XBreakIterator > const & GetBreakIterator()
void SetFontHeight(tools::Long nHeight)
void SetItalic(FontItalic)
void Move(tools::Long nHorzMoveDelta, tools::Long nVertMoveDelta)
tools::Rectangle GetBoundRect() const
#define SAL_WARN(area, stream)
static void InsertMissingOutlinePoints(const std::vector< double > &rDistances, const tools::Rectangle &rTextAreaBoundRect, tools::Polygon &rPoly)
SdrTextVertAdjust
Definition: sdtaitm.hxx:29
constexpr tools::Long GetHeight() const
virtual const tools::Rectangle & GetLogicRect() const override
Definition: svdotxtr.cxx:69
void Insert(sal_uInt16 nPos, const Point &rPt)