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