1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "EnhancedCustomShapeFontWork.hxx"
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>
37 #include <editeng/charscaleitem.hxx>
38 #include <svx/EnhancedCustomShapeTypeNames.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>
52 #include <comphelper/processfactory.hxx>
53 #include <com/sun/star/i18n/BreakIterator.hpp>
54 #include <com/sun/star/i18n/ScriptType.hpp>
55 #include <basegfx/polygon/b2dpolypolygontools.hxx>
56 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
57 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
58 #include <basegfx/polygon/b2dpolygontools.hxx>
59 #include <sal/log.hxx>
61 using namespace com::sun::star
;
62 using namespace com::sun::star::uno
;
64 struct FWCharacterData
// representing a single character
66 std::vector
< tools::PolyPolygon
> vOutlines
;
67 tools::Rectangle aBoundRect
;
69 struct FWParagraphData
// representing a single paragraph
72 std::vector
< FWCharacterData
> vCharacters
;
73 tools::Rectangle aBoundRect
;
74 SvxFrameDirection nFrameDirection
;
76 struct FWTextArea
// representing multiple concluding paragraphs
78 std::vector
< FWParagraphData
> vParagraphs
;
79 tools::Rectangle aBoundRect
;
81 struct FWData
// representing the whole text
83 std::vector
< FWTextArea
> vTextAreas
;
84 double fHorizontalTextScaling
;
85 double fVerticalTextScaling
;
86 sal_uInt32 nMaxParagraphsPerTextArea
;
87 sal_Int32 nSingleLineHeight
;
93 static bool InitializeFontWorkData(
94 const SdrObjCustomShape
& rSdrObjCustomShape
,
95 const sal_uInt16 nOutlinesCount2d
,
99 bool bSingleLineMode
= false;
100 sal_uInt16 nTextAreaCount
= nOutlinesCount2d
;
101 if ( nOutlinesCount2d
& 1 )
102 bSingleLineMode
= true;
104 nTextAreaCount
>>= 1;
106 const SdrCustomShapeGeometryItem
& rGeometryItem( rSdrObjCustomShape
.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY
) );
107 const css::uno::Any
* pAny
= rGeometryItem
.GetPropertyValueByName( "TextPath", "ScaleX" );
109 *pAny
>>= rFWData
.bScaleX
;
111 rFWData
.bScaleX
= false;
113 if ( nTextAreaCount
)
115 rFWData
.bSingleLineMode
= bSingleLineMode
;
117 // setting the strings
118 OutlinerParaObject
* pParaObj(rSdrObjCustomShape
.GetOutlinerParaObject());
122 const EditTextObject
& rTextObj
= pParaObj
->GetTextObject();
123 sal_Int32 nParagraphsLeft
= rTextObj
.GetParagraphCount();
125 rFWData
.nMaxParagraphsPerTextArea
= ( ( nParagraphsLeft
- 1 ) / nTextAreaCount
) + 1;
127 while( nParagraphsLeft
&& nTextAreaCount
)
129 FWTextArea aTextArea
;
130 sal_Int32 i
, nParagraphs
= ( ( nParagraphsLeft
- 1 ) / nTextAreaCount
) + 1;
131 for ( i
= 0; i
< nParagraphs
; ++i
, ++j
)
133 FWParagraphData aParagraphData
;
134 aParagraphData
.aString
= rTextObj
.GetText( j
);
136 const SfxItemSet
& rParaSet
= rTextObj
.GetParaAttribs( j
); // retrieving some paragraph attributes
137 aParagraphData
.nFrameDirection
= rParaSet
.Get( EE_PARA_WRITINGDIR
).GetValue();
138 aTextArea
.vParagraphs
.push_back( aParagraphData
);
140 rFWData
.vTextAreas
.push_back( aTextArea
);
141 nParagraphsLeft
-= nParagraphs
;
150 static double GetLength( const tools::Polygon
& rPolygon
)
153 if ( rPolygon
.GetSize() > 1 )
155 sal_uInt16 nCount
= rPolygon
.GetSize();
157 fLength
+= rPolygon
.CalcDistance( nCount
, nCount
- 1 );
163 /* CalculateHorizontalScalingFactor returns the horizontal scaling factor for
164 the whole text object, so that each text will match its corresponding 2d Outline */
165 static void CalculateHorizontalScalingFactor(
166 const SdrObjCustomShape
& rSdrObjCustomShape
,
168 const tools::PolyPolygon
& rOutline2d
)
170 double fScalingFactor
= 1.0;
171 bool bScalingFactorDefined
= false;
172 rFWData
.fVerticalTextScaling
= 1.0;
175 bool bSingleLineMode
= false;
176 sal_uInt16 nOutlinesCount2d
= rOutline2d
.Count();
179 const SvxFontItem
& rFontItem( rSdrObjCustomShape
.GetMergedItem( EE_CHAR_FONTINFO
) );
180 const SvxFontHeightItem
& rFontHeight( rSdrObjCustomShape
.GetMergedItem( EE_CHAR_FONTHEIGHT
) );
181 sal_Int32 nFontSize
= rFontHeight
.GetHeight();
184 aFont
.SetFontHeight( nFontSize
);
186 aFont
.SetFontHeight( rSdrObjCustomShape
.GetLogicRect().GetHeight() / rFWData
.nMaxParagraphsPerTextArea
);
188 aFont
.SetAlignment( ALIGN_TOP
);
189 aFont
.SetFamilyName( rFontItem
.GetFamilyName() );
190 aFont
.SetFamily( rFontItem
.GetFamily() );
191 aFont
.SetStyleName( rFontItem
.GetStyleName() );
192 const SvxPostureItem
& rPostureItem
= rSdrObjCustomShape
.GetMergedItem( EE_CHAR_ITALIC
);
193 aFont
.SetItalic( rPostureItem
.GetPosture() );
195 const SvxWeightItem
& rWeightItem
= rSdrObjCustomShape
.GetMergedItem( EE_CHAR_WEIGHT
);
196 aFont
.SetWeight( rWeightItem
.GetWeight() );
197 aFont
.SetOrientation( 0 );
198 // initializing virtual device
200 ScopedVclPtrInstance
< VirtualDevice
> pVirDev(DeviceFormat::BITMASK
);
201 pVirDev
->SetMapMode(MapMode(MapUnit::Map100thMM
));
202 pVirDev
->SetFont( aFont
);
204 if ( nOutlinesCount2d
& 1 )
205 bSingleLineMode
= true;
210 for( const auto& rTextArea
: rFWData
.vTextAreas
)
212 // calculating the width of the corresponding 2d text area
213 double fWidth
= GetLength( rOutline2d
.GetObject( i
++ ) );
214 if ( !bSingleLineMode
)
216 fWidth
+= GetLength( rOutline2d
.GetObject( i
++ ) );
220 for( const auto& rParagraph
: rTextArea
.vParagraphs
)
222 double fTextWidth
= pVirDev
->GetTextWidth( rParagraph
.aString
);
223 if ( fTextWidth
> 0.0 )
225 double fScale
= fWidth
/ fTextWidth
;
226 if ( !bScalingFactorDefined
)
228 fScalingFactor
= fScale
;
229 bScalingFactorDefined
= true;
231 else if ( fScale
< fScalingFactor
|| ( rFWData
.bScaleX
&& fScalingFactor
< 1.0 ) )
233 fScalingFactor
= fScale
;
239 if (fScalingFactor
< 1.0)
242 aFont
.SetFontHeight( nFontSize
);
243 pVirDev
->SetFont( aFont
);
246 while (rFWData
.bScaleX
&& fScalingFactor
< 1.0 && nFontSize
> 1 );
249 rFWData
.fVerticalTextScaling
= static_cast<double>(nFontSize
) / rFontHeight
.GetHeight();
252 fScalingFactor
*= 1.1;
254 rFWData
.fHorizontalTextScaling
= fScalingFactor
;
257 static void GetTextAreaOutline(
258 const FWData
& rFWData
,
259 const SdrObjCustomShape
& rSdrObjCustomShape
,
260 FWTextArea
& rTextArea
,
261 bool bSameLetterHeights
)
263 bool bIsVertical(rSdrObjCustomShape
.IsVerticalWriting());
264 sal_Int32 nVerticalOffset
= rFWData
.nMaxParagraphsPerTextArea
> rTextArea
.vParagraphs
.size()
265 ? rFWData
.nSingleLineHeight
/ 2 : 0;
267 for( auto& rParagraph
: rTextArea
.vParagraphs
)
269 const OUString
& rText
= rParagraph
.aString
;
270 if ( !rText
.isEmpty() )
272 // generating vcl/font
273 sal_uInt16 nScriptType
= i18n::ScriptType::LATIN
;
274 Reference
< i18n::XBreakIterator
> xBI( EnhancedCustomShapeFontWork::GetBreakIterator() );
277 nScriptType
= xBI
->getScriptType( rText
, 0 );
278 if( i18n::ScriptType::WEAK
== nScriptType
)
280 sal_Int32 nChg
= xBI
->endOfScript( rText
, 0, nScriptType
);
281 if (nChg
< rText
.getLength() && nChg
>= 0)
282 nScriptType
= xBI
->getScriptType( rText
, nChg
);
284 nScriptType
= i18n::ScriptType::LATIN
;
287 sal_uInt16 nFntItm
= EE_CHAR_FONTINFO
;
288 if ( nScriptType
== i18n::ScriptType::COMPLEX
)
289 nFntItm
= EE_CHAR_FONTINFO_CTL
;
290 else if ( nScriptType
== i18n::ScriptType::ASIAN
)
291 nFntItm
= EE_CHAR_FONTINFO_CJK
;
292 const SvxFontItem
& rFontItem
= static_cast<const SvxFontItem
&>(rSdrObjCustomShape
.GetMergedItem( nFntItm
));
295 aFont
.SetFontHeight( rFWData
.nSingleLineHeight
);
297 aFont
.SetAlignment( ALIGN_TOP
);
299 aFont
.SetFamilyName( rFontItem
.GetFamilyName() );
300 aFont
.SetFamily( rFontItem
.GetFamily() );
301 aFont
.SetStyleName( rFontItem
.GetStyleName() );
302 aFont
.SetOrientation( 0 );
304 const SvxPostureItem
& rPostureItem
= rSdrObjCustomShape
.GetMergedItem( EE_CHAR_ITALIC
);
305 aFont
.SetItalic( rPostureItem
.GetPosture() );
307 const SvxWeightItem
& rWeightItem
= rSdrObjCustomShape
.GetMergedItem( EE_CHAR_WEIGHT
);
308 aFont
.SetWeight( rWeightItem
.GetWeight() );
310 // initializing virtual device
311 ScopedVclPtrInstance
< VirtualDevice
> pVirDev(DeviceFormat::BITMASK
);
312 pVirDev
->SetMapMode(MapMode(MapUnit::Map100thMM
));
313 pVirDev
->SetFont( aFont
);
314 pVirDev
->EnableRTL();
315 if ( rParagraph
.nFrameDirection
== SvxFrameDirection::Horizontal_RL_TB
)
316 pVirDev
->SetLayoutMode( ComplexTextLayoutFlags::BiDiRtl
);
318 const SvxCharScaleWidthItem
& rCharScaleWidthItem
= rSdrObjCustomShape
.GetMergedItem( EE_CHAR_FONTWIDTH
);
319 sal_uInt16 nCharScaleWidth
= rCharScaleWidthItem
.GetValue();
320 std::unique_ptr
<long[]> pDXArry
;
321 sal_Int32 nWidth
= 0;
326 // vertical _> each single character needs to be rotated by 90
328 sal_Int32 nHeight
= 0;
329 tools::Rectangle aSingleCharacterUnion
;
330 for ( i
= 0; i
< rText
.getLength(); i
++ )
332 FWCharacterData aCharacterData
;
333 OUString
aCharText( rText
[ i
] );
334 if ( pVirDev
->GetTextOutlines( aCharacterData
.vOutlines
, aCharText
, 0, 0, -1, nWidth
, pDXArry
.get() ) )
336 sal_Int32 nTextWidth
= pVirDev
->GetTextWidth( aCharText
);
337 if ( aCharacterData
.vOutlines
.empty() )
339 nHeight
+= rFWData
.nSingleLineHeight
;
343 for ( auto& rOutline
: aCharacterData
.vOutlines
)
346 rOutline
.Rotate( Point( nTextWidth
/ 2, rFWData
.nSingleLineHeight
/ 2 ), 900 );
347 aCharacterData
.aBoundRect
.Union( rOutline
.GetBoundRect() );
349 for ( auto& rOutline
: aCharacterData
.vOutlines
)
351 sal_Int32 nM
= - aCharacterData
.aBoundRect
.Left() + nHeight
;
352 rOutline
.Move( nM
, 0 );
353 aCharacterData
.aBoundRect
.Move( nM
, 0 );
355 nHeight
+= aCharacterData
.aBoundRect
.GetWidth() + ( rFWData
.nSingleLineHeight
/ 5 );
356 aSingleCharacterUnion
.Union( aCharacterData
.aBoundRect
);
359 rParagraph
.vCharacters
.push_back( aCharacterData
);
361 for ( auto& rCharacter
: rParagraph
.vCharacters
)
363 for ( auto& rOutline
: rCharacter
.vOutlines
)
365 rOutline
.Move( ( aSingleCharacterUnion
.GetWidth() - rCharacter
.aBoundRect
.GetWidth() ) / 2, 0 );
371 if ( ( nCharScaleWidth
!= 100 ) && nCharScaleWidth
)
372 { // applying character spacing
373 pDXArry
.reset(new long[ rText
.getLength() ]);
374 pVirDev
->GetTextArray( rText
, pDXArry
.get());
375 FontMetric
aFontMetric( pVirDev
->GetFontMetric() );
376 aFont
.SetAverageFontWidth( static_cast<sal_Int32
>( static_cast<double>(aFontMetric
.GetAverageFontWidth()) * ( double(100) / static_cast<double>(nCharScaleWidth
) ) ) );
377 pVirDev
->SetFont( aFont
);
379 FWCharacterData aCharacterData
;
380 if ( pVirDev
->GetTextOutlines( aCharacterData
.vOutlines
, rText
, 0, 0, -1, nWidth
, pDXArry
.get() ) )
382 rParagraph
.vCharacters
.push_back( aCharacterData
);
386 // vertical alignment
387 for ( auto& rCharacter
: rParagraph
.vCharacters
)
389 for( tools::PolyPolygon
& rPolyPoly
: rCharacter
.vOutlines
)
391 if ( nVerticalOffset
)
392 rPolyPoly
.Move( 0, nVerticalOffset
);
394 // retrieving the boundrect for the paragraph
395 tools::Rectangle
aBoundRect( rPolyPoly
.GetBoundRect() );
396 rParagraph
.aBoundRect
.Union( aBoundRect
);
400 // updating the boundrect for the text area by merging the current paragraph boundrect
401 if ( rParagraph
.aBoundRect
.IsEmpty() )
403 if ( rTextArea
.aBoundRect
.IsEmpty() )
404 rTextArea
.aBoundRect
= tools::Rectangle( Point( 0, 0 ), Size( 1, rFWData
.nSingleLineHeight
) );
406 rTextArea
.aBoundRect
.AdjustBottom(rFWData
.nSingleLineHeight
);
410 tools::Rectangle
& rParagraphBoundRect
= rParagraph
.aBoundRect
;
411 rTextArea
.aBoundRect
.Union( rParagraphBoundRect
);
413 if ( bSameLetterHeights
)
415 for ( auto& rCharacter
: rParagraph
.vCharacters
)
417 for( auto& rOutline
: rCharacter
.vOutlines
)
419 tools::Rectangle
aPolyPolyBoundRect( rOutline
.GetBoundRect() );
420 if (aPolyPolyBoundRect
.GetHeight() != rParagraphBoundRect
.GetHeight() && aPolyPolyBoundRect
.GetHeight())
421 rOutline
.Scale( 1.0, static_cast<double>(rParagraphBoundRect
.GetHeight()) / aPolyPolyBoundRect
.GetHeight() );
422 aPolyPolyBoundRect
= rOutline
.GetBoundRect();
423 sal_Int32 nMove
= aPolyPolyBoundRect
.Top() - rParagraphBoundRect
.Top();
425 rOutline
.Move( 0, -nMove
);
431 nVerticalOffset
-= rFWData
.nSingleLineHeight
;
433 nVerticalOffset
+= rFWData
.nSingleLineHeight
;
437 static bool GetFontWorkOutline(
439 const SdrObjCustomShape
& rSdrObjCustomShape
)
441 SdrTextHorzAdjust
eHorzAdjust(rSdrObjCustomShape
.GetMergedItem( SDRATTR_TEXT_HORZADJUST
).GetValue());
442 drawing::TextFitToSizeType
const eFTS(rSdrObjCustomShape
.GetMergedItem( SDRATTR_TEXT_FITTOSIZE
).GetValue());
444 bool bSameLetterHeights
= false;
445 const SdrCustomShapeGeometryItem
& rGeometryItem(rSdrObjCustomShape
.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY
));
446 const css::uno::Any
* pAny
= rGeometryItem
.GetPropertyValueByName( "TextPath", "SameLetterHeights" );
448 *pAny
>>= bSameLetterHeights
;
450 const SvxFontHeightItem
& rFontHeight( rSdrObjCustomShape
.GetMergedItem( EE_CHAR_FONTHEIGHT
) );
452 rFWData
.nSingleLineHeight
= rFWData
.fVerticalTextScaling
* rFontHeight
.GetHeight();
454 rFWData
.nSingleLineHeight
= static_cast<sal_Int32
>( ( static_cast<double>( rSdrObjCustomShape
.GetLogicRect().GetHeight() )
455 / rFWData
.nMaxParagraphsPerTextArea
) * rFWData
.fHorizontalTextScaling
);
457 if (rFWData
.nSingleLineHeight
== SAL_MIN_INT32
)
460 for ( auto& rTextArea
: rFWData
.vTextAreas
)
468 if (eFTS
== drawing::TextFitToSizeType_ALLLINES
||
469 // tdf#97630 interpret PROPORTIONAL same as ALLLINES so we don't
470 // need another ODF attribute!
471 eFTS
== drawing::TextFitToSizeType_PROPORTIONAL
)
473 for ( auto& rParagraph
: rTextArea
.vParagraphs
)
475 sal_Int32 nParaWidth
= rParagraph
.aBoundRect
.GetWidth();
478 double fScale
= static_cast<double>(rTextArea
.aBoundRect
.GetWidth()) / nParaWidth
;
480 for ( auto& rCharacter
: rParagraph
.vCharacters
)
482 for( auto& rOutline
: rCharacter
.vOutlines
)
484 rOutline
.Scale( fScale
, 1.0 );
490 else if (rFWData
.bScaleX
)
492 const SdrTextVertAdjust nVertJustify
= rSdrObjCustomShape
.GetMergedItem( SDRATTR_TEXT_VERTADJUST
).GetValue();
493 double fFactor
= nVertJustify
== SdrTextVertAdjust::SDRTEXTVERTADJUST_BOTTOM
? -0.5 : ( nVertJustify
== SdrTextVertAdjust::SDRTEXTVERTADJUST_TOP
? 0.5 : 0 );
495 for ( auto& rParagraph
: rTextArea
.vParagraphs
)
497 sal_Int32 nHorzDiff
= 0;
498 sal_Int32 nVertDiff
= static_cast<double>( rFWData
.nSingleLineHeight
) * fFactor
* ( rTextArea
.vParagraphs
.size() - 1 );
500 if ( eHorzAdjust
== SDRTEXTHORZADJUST_CENTER
)
501 nHorzDiff
= ( rFWData
.fHorizontalTextScaling
* rTextArea
.aBoundRect
.GetWidth() - rParagraph
.aBoundRect
.GetWidth() ) / 2;
502 else if ( eHorzAdjust
== SDRTEXTHORZADJUST_RIGHT
)
503 nHorzDiff
= ( rFWData
.fHorizontalTextScaling
* rTextArea
.aBoundRect
.GetWidth() - rParagraph
.aBoundRect
.GetWidth() );
507 for ( auto& rCharacter
: rParagraph
.vCharacters
)
509 for( auto& rOutline
: rCharacter
.vOutlines
)
511 rOutline
.Move( nHorzDiff
, nVertDiff
);
519 switch( eHorzAdjust
)
521 case SDRTEXTHORZADJUST_RIGHT
:
522 case SDRTEXTHORZADJUST_CENTER
:
524 for ( auto& rParagraph
: rTextArea
.vParagraphs
)
526 sal_Int32 nHorzDiff
= 0;
527 if ( eHorzAdjust
== SDRTEXTHORZADJUST_CENTER
)
528 nHorzDiff
= ( rTextArea
.aBoundRect
.GetWidth() - rParagraph
.aBoundRect
.GetWidth() ) / 2;
529 else if ( eHorzAdjust
== SDRTEXTHORZADJUST_RIGHT
)
530 nHorzDiff
= ( rTextArea
.aBoundRect
.GetWidth() - rParagraph
.aBoundRect
.GetWidth() );
533 for ( auto& rCharacter
: rParagraph
.vCharacters
)
535 for( auto& rOutline
: rCharacter
.vOutlines
)
537 rOutline
.Move( nHorzDiff
, 0 );
545 case SDRTEXTHORZADJUST_BLOCK
: break; // don't know
546 case SDRTEXTHORZADJUST_LEFT
: break; // already left aligned -> nothing to do
554 static basegfx::B2DPolyPolygon
GetOutlinesFromShape2d( const SdrObject
* pShape2d
)
556 basegfx::B2DPolyPolygon aOutlines2d
;
558 SdrObjListIter
aObjListIter( *pShape2d
, SdrIterMode::DeepWithGroups
);
559 while( aObjListIter
.IsMore() )
561 SdrObject
* pPartObj
= aObjListIter
.Next();
562 if ( dynamic_cast<const SdrPathObj
*>( pPartObj
) != nullptr )
564 basegfx::B2DPolyPolygon
aCandidate(static_cast<SdrPathObj
*>(pPartObj
)->GetPathPoly());
565 if(aCandidate
.areControlPointsUsed())
567 aCandidate
= basegfx::utils::adaptiveSubdivideByAngle(aCandidate
);
569 aOutlines2d
.append(aCandidate
);
576 static void CalcDistances( const tools::Polygon
& rPoly
, std::vector
< double >& rDistances
)
578 sal_uInt16 i
, nCount
= rPoly
.GetSize();
581 for ( i
= 0; i
< nCount
; i
++ )
583 double fDistance
= i
? rPoly
.CalcDistance( i
, i
- 1 ) : 0.0;
584 rDistances
.push_back( fDistance
);
586 std::partial_sum( rDistances
.begin(), rDistances
.end(), rDistances
.begin() );
587 double fLength
= rDistances
[ rDistances
.size() - 1 ];
590 for ( auto& rDistance
: rDistances
)
591 rDistance
/= fLength
;
596 static void InsertMissingOutlinePoints( const std::vector
< double >& rDistances
,
597 const tools::Rectangle
& rTextAreaBoundRect
, tools::Polygon
& rPoly
)
599 sal_uInt16 nSize
= rPoly
.GetSize();
603 long nTextWidth
= rTextAreaBoundRect
.GetWidth();
606 throw o3tl::divide_by_zero();
608 double fLastDistance
= 0.0;
609 for (sal_uInt16 i
= 0; i
< nSize
; ++i
)
611 Point
& rPoint
= rPoly
[ i
];
612 double fDistance
= static_cast<double>( rPoint
.X() - rTextAreaBoundRect
.Left() ) / static_cast<double>(nTextWidth
);
615 if ( fDistance
> fLastDistance
)
617 std::vector
< double >::const_iterator aIter
= std::upper_bound( rDistances
.begin(), rDistances
.end(), fLastDistance
);
618 if ( aIter
!= rDistances
.end() && ( *aIter
> fLastDistance
) && ( *aIter
< fDistance
) )
620 Point
& rPt0
= rPoly
[ i
- 1 ];
621 sal_Int32 fX
= rPoint
.X() - rPt0
.X();
622 sal_Int32 fY
= rPoint
.Y() - rPt0
.Y();
623 double fd
= ( 1.0 / ( fDistance
- fLastDistance
) ) * ( *aIter
- fLastDistance
);
624 rPoly
.Insert( i
, Point( static_cast<sal_Int32
>( rPt0
.X() + fX
* fd
), static_cast<sal_Int32
>( rPt0
.Y() + fY
* fd
) ) );
628 else if ( fDistance
< fLastDistance
)
630 std::vector
< double >::const_iterator aIter
= std::lower_bound( rDistances
.begin(), rDistances
.end(), fLastDistance
);
631 if ( aIter
!= rDistances
.begin() )
634 if ( ( *aIter
> fDistance
) && ( *aIter
< fLastDistance
) )
636 Point
& rPt0
= rPoly
[ i
- 1 ];
637 sal_Int32 fX
= rPoint
.X() - rPt0
.X();
638 sal_Int32 fY
= rPoint
.Y() - rPt0
.Y();
639 double fd
= ( 1.0 / ( fDistance
- fLastDistance
) ) * ( *aIter
- fLastDistance
);
640 rPoly
.Insert( i
, Point( static_cast<sal_Int32
>( rPt0
.X() + fX
* fd
), static_cast<sal_Int32
>( rPt0
.Y() + fY
* fd
) ) );
646 fLastDistance
= fDistance
;
650 static void GetPoint( const tools::Polygon
& rPoly
, const std::vector
< double >& rDistances
, const double& fX
, double& fx1
, double& fy1
)
653 if ( rPoly
.GetSize() > 1 )
655 std::vector
< double >::const_iterator aIter
= std::lower_bound( rDistances
.begin(), rDistances
.end(), fX
);
656 sal_uInt16 nIdx
= sal::static_int_cast
<sal_uInt16
>( std::distance( rDistances
.begin(), aIter
) );
657 if ( aIter
== rDistances
.end() )
659 const Point
& rPt
= rPoly
[ nIdx
];
662 if ( nIdx
&& ( aIter
!= rDistances
.end() ) && !rtl::math::approxEqual( *aIter
, fX
) )
664 nIdx
= sal::static_int_cast
<sal_uInt16
>( std::distance( rDistances
.begin(), aIter
) );
665 double fDist0
= *( aIter
- 1 );
666 double fd
= ( 1.0 / ( *aIter
- fDist0
) ) * ( fX
- fDist0
);
667 const Point
& rPt2
= rPoly
[ nIdx
- 1 ];
668 double fWidth
= rPt
.X() - rPt2
.X();
669 double fHeight
= rPt
.Y() - rPt2
.Y();
672 fx1
= rPt2
.X() + fWidth
;
673 fy1
= rPt2
.Y() + fHeight
;
678 static void FitTextOutlinesToShapeOutlines( const tools::PolyPolygon
& aOutlines2d
, FWData
& rFWData
)
680 sal_uInt16 nOutline2dIdx
= 0;
681 for( auto& rTextArea
: rFWData
.vTextAreas
)
683 tools::Rectangle rTextAreaBoundRect
= rTextArea
.aBoundRect
;
684 sal_Int32 nLeft
= rTextAreaBoundRect
.Left();
685 sal_Int32 nTop
= rTextAreaBoundRect
.Top();
686 sal_Int32 nWidth
= rTextAreaBoundRect
.GetWidth();
687 sal_Int32 nHeight
= rTextAreaBoundRect
.GetHeight();
691 nWidth
*= rFWData
.fHorizontalTextScaling
;
694 if ( rFWData
.bSingleLineMode
&& nHeight
&& nWidth
)
696 if ( nOutline2dIdx
>= aOutlines2d
.Count() )
698 const tools::Polygon
& rOutlinePoly( aOutlines2d
[ nOutline2dIdx
++ ] );
699 const sal_uInt16 nPointCount
= rOutlinePoly
.GetSize();
700 if ( nPointCount
> 1 )
702 std::vector
< double > vDistances
;
703 vDistances
.reserve( nPointCount
);
704 CalcDistances( rOutlinePoly
, vDistances
);
705 if ( !vDistances
.empty() )
707 for( auto& rParagraph
: rTextArea
.vParagraphs
)
709 for ( auto& rCharacter
: rParagraph
.vCharacters
)
711 for( tools::PolyPolygon
& rPolyPoly
: rCharacter
.vOutlines
)
713 tools::Rectangle
aBoundRect( rPolyPoly
.GetBoundRect() );
714 double fx1
= aBoundRect
.Left() - nLeft
;
715 double fx2
= aBoundRect
.Right() - nLeft
;
717 double fM1
= fx1
/ static_cast<double>(nWidth
);
718 double fM2
= fx2
/ static_cast<double>(nWidth
);
720 GetPoint( rOutlinePoly
, vDistances
, fM1
, fx1
, fy1
);
721 GetPoint( rOutlinePoly
, vDistances
, fM2
, fx2
, fy2
);
723 double fvx
= fy2
- fy1
;
724 double fvy
= - ( fx2
- fx1
);
725 fx1
= fx1
+ ( ( fx2
- fx1
) * 0.5 );
726 fy1
= fy1
+ ( ( fy2
- fy1
) * 0.5 );
728 double fAngle
= atan2( -fvx
, -fvy
);
729 double fL
= hypot( fvx
, fvy
);
732 SAL_WARN("svx", "FitTextOutlinesToShapeOutlines div-by-zero, abandon fit");
737 fL
= rTextArea
.aBoundRect
.GetHeight() / 2.0 + rTextArea
.aBoundRect
.Top() - rParagraph
.aBoundRect
.Center().Y();
740 rPolyPoly
.Rotate( Point( aBoundRect
.Center().X(), rParagraph
.aBoundRect
.Center().Y() ), sin( fAngle
), cos( fAngle
) );
741 rPolyPoly
.Move( static_cast<sal_Int32
>( ( fx1
+ fvx
)- aBoundRect
.Center().X() ), static_cast<sal_Int32
>( ( fy1
+ fvy
) - rParagraph
.aBoundRect
.Center().Y() ) );
750 if ( ( nOutline2dIdx
+ 1 ) >= aOutlines2d
.Count() )
752 const tools::Polygon
& rOutlinePoly( aOutlines2d
[ nOutline2dIdx
++ ] );
753 const tools::Polygon
& rOutlinePoly2( aOutlines2d
[ nOutline2dIdx
++ ] );
754 const sal_uInt16 nPointCount
= rOutlinePoly
.GetSize();
755 const sal_uInt16 nPointCount2
= rOutlinePoly2
.GetSize();
756 if ( ( nPointCount
> 1 ) && ( nPointCount2
> 1 ) )
758 std::vector
< double > vDistances
;
759 vDistances
.reserve( nPointCount
);
760 std::vector
< double > vDistances2
;
761 vDistances2
.reserve( nPointCount2
);
762 CalcDistances( rOutlinePoly
, vDistances
);
763 CalcDistances( rOutlinePoly2
, vDistances2
);
764 for( auto& rParagraph
: rTextArea
.vParagraphs
)
766 for ( auto& rCharacter
: rParagraph
.vCharacters
)
768 for( tools::PolyPolygon
& rPolyPoly
: rCharacter
.vOutlines
)
770 sal_uInt16 i
, nPolyCount
= rPolyPoly
.Count();
771 for ( i
= 0; i
< nPolyCount
; i
++ )
774 basegfx::B2DPolygon
aCandidate(rPolyPoly
[ i
].getB2DPolygon());
776 if(aCandidate
.areControlPointsUsed())
778 aCandidate
= basegfx::utils::adaptiveSubdivideByAngle(aCandidate
);
781 // create local polygon copy to work on
782 tools::Polygon
aLocalPoly(aCandidate
);
784 InsertMissingOutlinePoints( vDistances
, rTextAreaBoundRect
, aLocalPoly
);
785 InsertMissingOutlinePoints( vDistances2
, rTextAreaBoundRect
, aLocalPoly
);
787 sal_uInt16 _nPointCount
= aLocalPoly
.GetSize();
790 if (!nWidth
|| !nHeight
)
791 throw o3tl::divide_by_zero();
792 for (sal_uInt16 j
= 0; j
< _nPointCount
; ++j
)
794 Point
& rPoint
= aLocalPoly
[ j
];
795 rPoint
.AdjustX( -nLeft
);
796 rPoint
.AdjustY( -nTop
);
797 double fX
= static_cast<double>(rPoint
.X()) / static_cast<double>(nWidth
);
798 double fY
= static_cast<double>(rPoint
.Y()) / static_cast<double>(nHeight
);
800 double fx1
, fy1
, fx2
, fy2
;
801 GetPoint( rOutlinePoly
, vDistances
, fX
, fx1
, fy1
);
802 GetPoint( rOutlinePoly2
, vDistances2
, fX
, fx2
, fy2
);
803 double fWidth
= fx2
- fx1
;
804 double fHeight
= fy2
- fy1
;
805 rPoint
.setX( static_cast<sal_Int32
>( fx1
+ fWidth
* fY
) );
806 rPoint
.setY( static_cast<sal_Int32
>( fy1
+ fHeight
* fY
) );
810 // write back polygon
811 rPolyPoly
[ i
] = aLocalPoly
;
821 static SdrObject
* CreateSdrObjectFromParagraphOutlines(
822 const FWData
& rFWData
,
823 const SdrObjCustomShape
& rSdrObjCustomShape
)
825 SdrObject
* pRet
= nullptr;
826 basegfx::B2DPolyPolygon aPolyPoly
;
827 if ( !rFWData
.vTextAreas
.empty() )
829 for ( const auto& rTextArea
: rFWData
.vTextAreas
)
831 for ( const auto& rParagraph
: rTextArea
.vParagraphs
)
833 for ( const auto& rCharacter
: rParagraph
.vCharacters
)
835 for( const auto& rOutline
: rCharacter
.vOutlines
)
837 aPolyPoly
.append( rOutline
.getB2DPolyPolygon() );
843 pRet
= new SdrPathObj(
844 rSdrObjCustomShape
.getSdrModelFromSdrObject(),
848 SfxItemSet
aSet(rSdrObjCustomShape
.GetMergedItemSet());
849 aSet
.ClearItem( SDRATTR_TEXTDIRECTION
); //SJ: vertical writing is not required, by removing this item no outliner is created
850 aSet
.Put(makeSdrShadowItem(false)); // #i37011# NO shadow for FontWork geometry
851 pRet
->SetMergedItemSet( aSet
); // * otherwise we would crash, because the outliner tries to create a Paraobject, but there is no model
857 Reference
< i18n::XBreakIterator
> EnhancedCustomShapeFontWork::mxBreakIterator
;
859 Reference
< i18n::XBreakIterator
> const & EnhancedCustomShapeFontWork::GetBreakIterator()
861 if ( !mxBreakIterator
.is() )
863 Reference
< uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
864 mxBreakIterator
= i18n::BreakIterator::create(xContext
);
866 return mxBreakIterator
;
869 SdrObject
* EnhancedCustomShapeFontWork::CreateFontWork(
870 const SdrObject
* pShape2d
,
871 const SdrObjCustomShape
& rSdrObjCustomShape
)
873 SdrObject
* pRet
= nullptr;
875 tools::PolyPolygon
aOutlines2d( GetOutlinesFromShape2d( pShape2d
) );
876 sal_uInt16 nOutlinesCount2d
= aOutlines2d
.Count();
877 if ( nOutlinesCount2d
)
881 if(InitializeFontWorkData(rSdrObjCustomShape
, nOutlinesCount2d
, aFWData
))
883 /* retrieves the horizontal scaling factor that has to be used
884 to fit each paragraph text into its corresponding 2d outline */
885 CalculateHorizontalScalingFactor(
890 /* retrieving the Outlines for the each Paragraph. */
891 if(!GetFontWorkOutline(
898 FitTextOutlinesToShapeOutlines( aOutlines2d
, aFWData
);
900 pRet
= CreateSdrObjectFromParagraphOutlines(
908 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */