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 <svx/svddef.hxx>
22 #include <svx/svdogrp.hxx>
23 #include <svx/svdopath.hxx>
24 #include <vcl/metric.hxx>
25 #include <svx/svdpage.hxx>
26 #include <svx/sdasitm.hxx>
27 #include <svx/sdasaitm.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/charscaleitem.hxx>
37 #include "svx/EnhancedCustomShapeTypeNames.hxx"
38 #include <svx/svdorect.hxx>
39 #include <svx/svdoashp.hxx>
40 #include <editeng/outliner.hxx>
41 #include <editeng/outlobj.hxx>
42 #include <editeng/editobj.hxx>
43 #include <editeng/editeng.hxx>
44 #include <o3tl/numeric.hxx>
45 #include <svx/svdmodel.hxx>
49 #include <comphelper/processfactory.hxx>
50 #include <com/sun/star/i18n/BreakIterator.hpp>
51 #include <com/sun/star/i18n/ScriptType.hpp>
52 #include <basegfx/polygon/b2dpolypolygontools.hxx>
53 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
54 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
55 #include <basegfx/polygon/b2dpolygontools.hxx>
57 using namespace com::sun::star
;
58 using namespace com::sun::star::uno
;
60 struct FWCharacterData
// representing a single character
62 std::vector
< tools::PolyPolygon
> vOutlines
;
65 struct FWParagraphData
// representing a single paragraph
68 std::vector
< FWCharacterData
> vCharacters
;
70 sal_Int16 nFrameDirection
;
72 struct FWTextArea
// representing multiple concluding paragraphs
74 std::vector
< FWParagraphData
> vParagraphs
;
77 struct FWData
// representing the whole text
79 std::vector
< FWTextArea
> vTextAreas
;
80 double fHorizontalTextScaling
;
81 sal_uInt32 nMaxParagraphsPerTextArea
;
82 sal_Int32 nSingleLineHeight
;
87 static bool InitializeFontWorkData( const SdrObject
* pCustomShape
, const sal_uInt16 nOutlinesCount2d
, FWData
& rFWData
)
90 bool bSingleLineMode
= false;
91 sal_uInt16 nTextAreaCount
= nOutlinesCount2d
;
92 if ( nOutlinesCount2d
& 1 )
93 bSingleLineMode
= true;
99 rFWData
.bSingleLineMode
= bSingleLineMode
;
101 // setting the strings
102 OutlinerParaObject
* pParaObj
= static_cast<const SdrObjCustomShape
*>(pCustomShape
)->GetOutlinerParaObject();
105 const EditTextObject
& rTextObj
= pParaObj
->GetTextObject();
106 sal_Int32 nParagraphsLeft
= rTextObj
.GetParagraphCount();
108 rFWData
.nMaxParagraphsPerTextArea
= ( ( nParagraphsLeft
- 1 ) / nTextAreaCount
) + 1;
110 while( nParagraphsLeft
&& nTextAreaCount
)
112 FWTextArea aTextArea
;
113 sal_Int32 i
, nParagraphs
= ( ( nParagraphsLeft
- 1 ) / nTextAreaCount
) + 1;
114 for ( i
= 0; i
< nParagraphs
; ++i
, ++j
)
116 FWParagraphData aParagraphData
;
117 aParagraphData
.aString
= rTextObj
.GetText( j
);
119 const SfxItemSet
& rParaSet
= rTextObj
.GetParaAttribs( j
); // retrieving some paragraph attributes
120 aParagraphData
.nFrameDirection
= static_cast<const SvxFrameDirectionItem
&>(rParaSet
.Get( EE_PARA_WRITINGDIR
)).GetValue();
121 aTextArea
.vParagraphs
.push_back( aParagraphData
);
123 rFWData
.vTextAreas
.push_back( aTextArea
);
124 nParagraphsLeft
-= nParagraphs
;
133 double GetLength( const Polygon
& rPolygon
)
136 if ( rPolygon
.GetSize() > 1 )
138 sal_uInt16 nCount
= rPolygon
.GetSize();
140 fLength
+= ((Polygon
&)rPolygon
).CalcDistance( nCount
, nCount
- 1 );
146 /* CalculateHorizontalScalingFactor returns the horizontal scaling factor for
147 the whole text object, so that each text will match its corresponding 2d Outline */
148 void CalculateHorizontalScalingFactor( const SdrObject
* pCustomShape
,
149 FWData
& rFWData
, const tools::PolyPolygon
& rOutline2d
)
151 double fScalingFactor
= 1.0;
152 bool bScalingFactorDefined
= false;
155 bool bSingleLineMode
= false;
156 sal_uInt16 nOutlinesCount2d
= rOutline2d
.Count();
159 const SvxFontItem
& rFontItem
= static_cast<const SvxFontItem
&>(pCustomShape
->GetMergedItem( EE_CHAR_FONTINFO
));
160 aFont
.SetHeight( pCustomShape
->GetLogicRect().GetHeight() / rFWData
.nMaxParagraphsPerTextArea
);
161 aFont
.SetAlign( ALIGN_TOP
);
162 aFont
.SetName( rFontItem
.GetFamilyName() );
163 aFont
.SetFamily( rFontItem
.GetFamily() );
164 aFont
.SetStyleName( rFontItem
.GetStyleName() );
165 aFont
.SetOrientation( 0 );
166 // initializing virtual device
168 ScopedVclPtrInstance
< VirtualDevice
> pVirDev( 1 );
169 pVirDev
->SetMapMode( MAP_100TH_MM
);
170 pVirDev
->SetFont( aFont
);
172 if ( nOutlinesCount2d
& 1 )
173 bSingleLineMode
= true;
175 std::vector
< FWTextArea
>::iterator aTextAreaIter
= rFWData
.vTextAreas
.begin();
176 std::vector
< FWTextArea
>::iterator aTextAreaIEnd
= rFWData
.vTextAreas
.end();
177 while( aTextAreaIter
!= aTextAreaIEnd
)
179 // calculating the width of the corresponding 2d text area
180 double fWidth
= GetLength( rOutline2d
.GetObject( i
++ ) );
181 if ( !bSingleLineMode
)
183 fWidth
+= GetLength( rOutline2d
.GetObject( i
++ ) );
186 std::vector
< FWParagraphData
>::const_iterator
aParagraphIter( aTextAreaIter
->vParagraphs
.begin() );
187 std::vector
< FWParagraphData
>::const_iterator
aParagraphIEnd( aTextAreaIter
->vParagraphs
.end() );
188 while( aParagraphIter
!= aParagraphIEnd
)
190 double fTextWidth
= pVirDev
->GetTextWidth( aParagraphIter
->aString
);
191 if ( fTextWidth
> 0.0 )
193 double fScale
= fWidth
/ fTextWidth
;
194 if ( !bScalingFactorDefined
)
196 fScalingFactor
= fScale
;
197 bScalingFactorDefined
= true;
201 if ( fScale
< fScalingFactor
)
202 fScalingFactor
= fScale
;
209 rFWData
.fHorizontalTextScaling
= fScalingFactor
;
212 void GetTextAreaOutline( const FWData
& rFWData
, const SdrObject
* pCustomShape
, FWTextArea
& rTextArea
, bool bSameLetterHeights
)
214 bool bIsVertical
= static_cast<const SdrObjCustomShape
*>(pCustomShape
)->IsVerticalWriting();
215 sal_Int32 nVerticalOffset
= rFWData
.nMaxParagraphsPerTextArea
> rTextArea
.vParagraphs
.size()
216 ? rFWData
.nSingleLineHeight
/ 2 : 0;
218 std::vector
< FWParagraphData
>::iterator
aParagraphIter( rTextArea
.vParagraphs
.begin() );
219 std::vector
< FWParagraphData
>::iterator
aParagraphIEnd( rTextArea
.vParagraphs
.end() );
220 while( aParagraphIter
!= aParagraphIEnd
)
222 const OUString
& rText
= aParagraphIter
->aString
;
223 if ( !rText
.isEmpty() )
225 // generating vcl/font
226 sal_uInt16 nScriptType
= i18n::ScriptType::LATIN
;
227 Reference
< i18n::XBreakIterator
> xBI( EnhancedCustomShapeFontWork::GetBreakIterator() );
230 nScriptType
= xBI
->getScriptType( rText
, 0 );
231 if( i18n::ScriptType::WEAK
== nScriptType
)
233 sal_Int32 nChg
= xBI
->endOfScript( rText
, 0, nScriptType
);
234 if (nChg
< rText
.getLength() && nChg
>= 0)
235 nScriptType
= xBI
->getScriptType( rText
, nChg
);
237 nScriptType
= i18n::ScriptType::LATIN
;
240 sal_uInt16 nFntItm
= EE_CHAR_FONTINFO
;
241 if ( nScriptType
== i18n::ScriptType::COMPLEX
)
242 nFntItm
= EE_CHAR_FONTINFO_CTL
;
243 else if ( nScriptType
== i18n::ScriptType::ASIAN
)
244 nFntItm
= EE_CHAR_FONTINFO_CJK
;
245 const SvxFontItem
& rFontItem
= static_cast<const SvxFontItem
&>(pCustomShape
->GetMergedItem( nFntItm
));
247 aFont
.SetHeight( rFWData
.nSingleLineHeight
);
248 aFont
.SetAlign( ALIGN_TOP
);
250 aFont
.SetName( rFontItem
.GetFamilyName() );
251 aFont
.SetFamily( rFontItem
.GetFamily() );
252 aFont
.SetStyleName( rFontItem
.GetStyleName() );
253 aFont
.SetOrientation( 0 );
255 const SvxPostureItem
& rPostureItem
= static_cast<const SvxPostureItem
&>(pCustomShape
->GetMergedItem( EE_CHAR_ITALIC
));
256 aFont
.SetItalic( rPostureItem
.GetPosture() );
258 const SvxWeightItem
& rWeightItem
= static_cast<const SvxWeightItem
&>(pCustomShape
->GetMergedItem( EE_CHAR_WEIGHT
));
259 aFont
.SetWeight( rWeightItem
.GetWeight() );
261 // initializing virtual device
262 ScopedVclPtrInstance
< VirtualDevice
> pVirDev( 1 );
263 pVirDev
->SetMapMode( MAP_100TH_MM
);
264 pVirDev
->SetFont( aFont
);
265 pVirDev
->EnableRTL( true );
266 if ( aParagraphIter
->nFrameDirection
== FRMDIR_HORI_RIGHT_TOP
)
267 pVirDev
->SetLayoutMode( TEXT_LAYOUT_BIDI_RTL
);
269 const SvxCharScaleWidthItem
& rCharScaleWidthItem
= static_cast<const SvxCharScaleWidthItem
&>(pCustomShape
->GetMergedItem( EE_CHAR_FONTWIDTH
));
270 sal_uInt16 nCharScaleWidth
= rCharScaleWidthItem
.GetValue();
271 long* pDXArry
= NULL
;
272 sal_Int32 nWidth
= 0;
277 // vertical _> each single character needs to be rotated by 90
279 sal_Int32 nHeight
= 0;
280 Rectangle aSingleCharacterUnion
;
281 for ( i
= 0; i
< rText
.getLength(); i
++ )
283 FWCharacterData aCharacterData
;
284 OUString
aCharText( (sal_Unicode
)rText
[ i
] );
285 if ( pVirDev
->GetTextOutlines( aCharacterData
.vOutlines
, aCharText
, 0, 0, -1, true, nWidth
, pDXArry
) )
287 sal_Int32 nTextWidth
= pVirDev
->GetTextWidth( aCharText
);
288 std::vector
< tools::PolyPolygon
>::iterator aOutlineIter
= aCharacterData
.vOutlines
.begin();
289 std::vector
< tools::PolyPolygon
>::iterator aOutlineIEnd
= aCharacterData
.vOutlines
.end();
290 if ( aOutlineIter
== aOutlineIEnd
)
292 nHeight
+= rFWData
.nSingleLineHeight
;
296 while ( aOutlineIter
!= aOutlineIEnd
)
299 aOutlineIter
->Rotate( Point( nTextWidth
/ 2, rFWData
.nSingleLineHeight
/ 2 ), 900 );
300 aCharacterData
.aBoundRect
.Union( aOutlineIter
->GetBoundRect() );
303 aOutlineIter
= aCharacterData
.vOutlines
.begin();
304 aOutlineIEnd
= aCharacterData
.vOutlines
.end();
305 while ( aOutlineIter
!= aOutlineIEnd
)
307 sal_Int32 nM
= - aCharacterData
.aBoundRect
.Left() + nHeight
;
308 aOutlineIter
->Move( nM
, 0 );
309 aCharacterData
.aBoundRect
.Move( nM
, 0 );
312 nHeight
+= aCharacterData
.aBoundRect
.GetWidth() + ( rFWData
.nSingleLineHeight
/ 5 );
313 aSingleCharacterUnion
.Union( aCharacterData
.aBoundRect
);
316 aParagraphIter
->vCharacters
.push_back( aCharacterData
);
318 std::vector
< FWCharacterData
>::iterator
aCharacterIter( aParagraphIter
->vCharacters
.begin() );
319 std::vector
< FWCharacterData
>::iterator
aCharacterIEnd( aParagraphIter
->vCharacters
.end() );
320 while ( aCharacterIter
!= aCharacterIEnd
)
322 std::vector
< tools::PolyPolygon
>::iterator
aOutlineIter( aCharacterIter
->vOutlines
.begin() );
323 std::vector
< tools::PolyPolygon
>::iterator
aOutlineIEnd( aCharacterIter
->vOutlines
.end() );
324 while ( aOutlineIter
!= aOutlineIEnd
)
326 aOutlineIter
->Move( ( aSingleCharacterUnion
.GetWidth() - aCharacterIter
->aBoundRect
.GetWidth() ) / 2, 0 );
334 if ( ( nCharScaleWidth
!= 100 ) && nCharScaleWidth
)
335 { // applying character spacing
336 pDXArry
= new long[ rText
.getLength() ];
337 pVirDev
->GetTextArray( rText
, pDXArry
);
338 FontMetric
aFontMetric( pVirDev
->GetFontMetric() );
339 aFont
.SetWidth( (sal_Int32
)( (double)aFontMetric
.GetWidth() * ( (double)100 / (double)nCharScaleWidth
) ) );
340 pVirDev
->SetFont( aFont
);
342 FWCharacterData aCharacterData
;
343 if ( pVirDev
->GetTextOutlines( aCharacterData
.vOutlines
, rText
, 0, 0, -1, true, nWidth
, pDXArry
) )
345 aParagraphIter
->vCharacters
.push_back( aCharacterData
);
350 // veritcal alignment
351 std::vector
< FWCharacterData
>::iterator
aCharacterIter( aParagraphIter
->vCharacters
.begin() );
352 std::vector
< FWCharacterData
>::iterator
aCharacterIEnd ( aParagraphIter
->vCharacters
.end() );
353 while ( aCharacterIter
!= aCharacterIEnd
)
355 std::vector
< tools::PolyPolygon
>::iterator
aOutlineIter( aCharacterIter
->vOutlines
.begin() );
356 std::vector
< tools::PolyPolygon
>::iterator
aOutlineIEnd( aCharacterIter
->vOutlines
.end() );
357 while( aOutlineIter
!= aOutlineIEnd
)
360 tools::PolyPolygon
& rPolyPoly
= *aOutlineIter
++;
362 if ( nVerticalOffset
)
363 rPolyPoly
.Move( 0, nVerticalOffset
);
365 // retrieving the boundrect for the paragraph
366 Rectangle
aBoundRect( rPolyPoly
.GetBoundRect() );
367 aParagraphIter
->aBoundRect
.Union( aBoundRect
);
372 // updating the boundrect for the text area by merging the current paragraph boundrect
373 if ( aParagraphIter
->aBoundRect
.IsEmpty() )
375 if ( rTextArea
.aBoundRect
.IsEmpty() )
376 rTextArea
.aBoundRect
= Rectangle( Point( 0, 0 ), Size( 1, rFWData
.nSingleLineHeight
) );
378 rTextArea
.aBoundRect
.Bottom() += rFWData
.nSingleLineHeight
;
382 Rectangle
& rParagraphBoundRect
= aParagraphIter
->aBoundRect
;
383 rTextArea
.aBoundRect
.Union( rParagraphBoundRect
);
385 if ( bSameLetterHeights
)
387 std::vector
< FWCharacterData
>::iterator
aCharacterIter( aParagraphIter
->vCharacters
.begin() );
388 std::vector
< FWCharacterData
>::iterator
aCharacterIEnd( aParagraphIter
->vCharacters
.end() );
389 while ( aCharacterIter
!= aCharacterIEnd
)
391 std::vector
< tools::PolyPolygon
>::iterator
aOutlineIter( aCharacterIter
->vOutlines
.begin() );
392 std::vector
< tools::PolyPolygon
>::iterator
aOutlineIEnd( aCharacterIter
->vOutlines
.end() );
393 while( aOutlineIter
!= aOutlineIEnd
)
395 Rectangle
aPolyPolyBoundRect( aOutlineIter
->GetBoundRect() );
396 if (aPolyPolyBoundRect
.GetHeight() != rParagraphBoundRect
.GetHeight() && aPolyPolyBoundRect
.GetHeight())
397 aOutlineIter
->Scale( 1.0, (double)rParagraphBoundRect
.GetHeight() / aPolyPolyBoundRect
.GetHeight() );
398 aPolyPolyBoundRect
= aOutlineIter
->GetBoundRect();
399 sal_Int32 nMove
= aPolyPolyBoundRect
.Top() - rParagraphBoundRect
.Top();
401 aOutlineIter
->Move( 0, -nMove
);
409 nVerticalOffset
-= rFWData
.nSingleLineHeight
;
411 nVerticalOffset
+= rFWData
.nSingleLineHeight
;
416 void GetFontWorkOutline( FWData
& rFWData
, const SdrObject
* pCustomShape
)
418 SdrTextHorzAdjust
eHorzAdjust( static_cast<const SdrTextHorzAdjustItem
&>(pCustomShape
->GetMergedItem( SDRATTR_TEXT_HORZADJUST
)).GetValue() );
419 SdrFitToSizeType
eFTS( static_cast<const SdrTextFitToSizeTypeItem
&>(pCustomShape
->GetMergedItem( SDRATTR_TEXT_FITTOSIZE
)).GetValue() );
421 std::vector
< FWTextArea
>::iterator aTextAreaIter
= rFWData
.vTextAreas
.begin();
422 std::vector
< FWTextArea
>::iterator aTextAreaIEnd
= rFWData
.vTextAreas
.end();
424 rFWData
.nSingleLineHeight
= (sal_Int32
)( ( (double)pCustomShape
->GetLogicRect().GetHeight()
425 / rFWData
.nMaxParagraphsPerTextArea
) * rFWData
.fHorizontalTextScaling
);
427 bool bSameLetterHeights
= false;
428 const SdrCustomShapeGeometryItem
& rGeometryItem
= static_cast<const SdrCustomShapeGeometryItem
&>(pCustomShape
->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY
));
429 const com::sun::star::uno::Any
* pAny
= rGeometryItem
.GetPropertyValueByName( "TextPath", "SameLetterHeights" );
431 *pAny
>>= bSameLetterHeights
;
433 while ( aTextAreaIter
!= aTextAreaIEnd
)
435 GetTextAreaOutline( rFWData
, pCustomShape
, *aTextAreaIter
, bSameLetterHeights
);
436 if ( eFTS
== SDRTEXTFIT_ALLLINES
)
438 std::vector
< FWParagraphData
>::iterator
aParagraphIter( aTextAreaIter
->vParagraphs
.begin() );
439 std::vector
< FWParagraphData
>::iterator
aParagraphIEnd( aTextAreaIter
->vParagraphs
.end() );
440 while ( aParagraphIter
!= aParagraphIEnd
)
442 sal_Int32 nParaWidth
= aParagraphIter
->aBoundRect
.GetWidth();
445 double fScale
= (double)aTextAreaIter
->aBoundRect
.GetWidth() / nParaWidth
;
447 std::vector
< FWCharacterData
>::iterator
aCharacterIter( aParagraphIter
->vCharacters
.begin() );
448 std::vector
< FWCharacterData
>::iterator
aCharacterIEnd( aParagraphIter
->vCharacters
.end() );
449 while ( aCharacterIter
!= aCharacterIEnd
)
451 std::vector
< tools::PolyPolygon
>::iterator aOutlineIter
= aCharacterIter
->vOutlines
.begin();
452 std::vector
< tools::PolyPolygon
>::iterator aOutlineIEnd
= aCharacterIter
->vOutlines
.end();
453 while( aOutlineIter
!= aOutlineIEnd
)
455 aOutlineIter
->Scale( fScale
, 1.0 );
466 switch( eHorzAdjust
)
468 case SDRTEXTHORZADJUST_RIGHT
:
469 case SDRTEXTHORZADJUST_CENTER
:
471 std::vector
< FWParagraphData
>::iterator
aParagraphIter( aTextAreaIter
->vParagraphs
.begin() );
472 std::vector
< FWParagraphData
>::iterator
aParagraphIEnd( aTextAreaIter
->vParagraphs
.end() );
473 while ( aParagraphIter
!= aParagraphIEnd
)
475 sal_Int32 nHorzDiff
= 0;
476 if ( eHorzAdjust
== SDRTEXTHORZADJUST_CENTER
)
477 nHorzDiff
= ( aTextAreaIter
->aBoundRect
.GetWidth() - aParagraphIter
->aBoundRect
.GetWidth() ) / 2;
478 else if ( eHorzAdjust
== SDRTEXTHORZADJUST_RIGHT
)
479 nHorzDiff
= ( aTextAreaIter
->aBoundRect
.GetWidth() - aParagraphIter
->aBoundRect
.GetWidth() );
482 std::vector
< FWCharacterData
>::iterator
aCharacterIter( aParagraphIter
->vCharacters
.begin() );
483 std::vector
< FWCharacterData
>::iterator
aCharacterIEnd( aParagraphIter
->vCharacters
.end() );
484 while ( aCharacterIter
!= aCharacterIEnd
)
486 std::vector
< tools::PolyPolygon
>::iterator aOutlineIter
= aCharacterIter
->vOutlines
.begin();
487 std::vector
< tools::PolyPolygon
>::iterator aOutlineIEnd
= aCharacterIter
->vOutlines
.end();
488 while( aOutlineIter
!= aOutlineIEnd
)
490 aOutlineIter
->Move( nHorzDiff
, 0 );
501 case SDRTEXTHORZADJUST_BLOCK
: break; // don't know
502 case SDRTEXTHORZADJUST_LEFT
: break; // already left aligned -> nothing to do
509 basegfx::B2DPolyPolygon
GetOutlinesFromShape2d( const SdrObject
* pShape2d
)
511 basegfx::B2DPolyPolygon aOutlines2d
;
513 SdrObjListIter
aObjListIter( *pShape2d
, IM_DEEPWITHGROUPS
);
514 while( aObjListIter
.IsMore() )
516 SdrObject
* pPartObj
= aObjListIter
.Next();
517 if ( pPartObj
->ISA( SdrPathObj
) )
519 basegfx::B2DPolyPolygon
aCandidate(static_cast<SdrPathObj
*>(pPartObj
)->GetPathPoly());
520 if(aCandidate
.areControlPointsUsed())
522 aCandidate
= basegfx::tools::adaptiveSubdivideByAngle(aCandidate
);
524 aOutlines2d
.append(aCandidate
);
531 void CalcDistances( const Polygon
& rPoly
, std::vector
< double >& rDistances
)
533 sal_uInt16 i
, nCount
= rPoly
.GetSize();
536 for ( i
= 0; i
< nCount
; i
++ )
538 double fDistance
= i
? ((Polygon
&)rPoly
).CalcDistance( i
, i
- 1 ) : 0.0;
539 rDistances
.push_back( fDistance
);
541 std::partial_sum( rDistances
.begin(), rDistances
.end(), rDistances
.begin() );
542 double fLength
= rDistances
[ rDistances
.size() - 1 ];
545 std::vector
< double >::iterator aIter
= rDistances
.begin();
546 std::vector
< double >::iterator aEnd
= rDistances
.end();
547 while ( aIter
!= aEnd
)
553 void InsertMissingOutlinePoints( const Polygon
& /*rOutlinePoly*/, const std::vector
< double >& rDistances
, const Rectangle
& rTextAreaBoundRect
, Polygon
& rPoly
)
555 sal_uInt16 nSize
= rPoly
.GetSize();
559 long nTextWidth
= rTextAreaBoundRect
.GetWidth();
562 throw o3tl::divide_by_zero();
564 double fLastDistance
= 0.0;
565 for (sal_uInt16 i
= 0; i
< nSize
; ++i
)
567 Point
& rPoint
= rPoly
[ i
];
568 double fDistance
= (double)( rPoint
.X() - rTextAreaBoundRect
.Left() ) / (double)nTextWidth
;
571 if ( fDistance
> fLastDistance
)
573 std::vector
< double >::const_iterator aIter
= std::upper_bound( rDistances
.begin(), rDistances
.end(), fLastDistance
);
574 if ( aIter
!= rDistances
.end() && ( *aIter
> fLastDistance
) && ( *aIter
< fDistance
) )
576 Point
& rPt0
= rPoly
[ i
- 1 ];
577 sal_Int32 fX
= rPoint
.X() - rPt0
.X();
578 sal_Int32 fY
= rPoint
.Y() - rPt0
.Y();
579 double fd
= ( 1.0 / ( fDistance
- fLastDistance
) ) * ( *aIter
- fLastDistance
);
580 rPoly
.Insert( i
, Point( (sal_Int32
)( rPt0
.X() + fX
* fd
), (sal_Int32
)( rPt0
.Y() + fY
* fd
) ) );
584 else if ( fDistance
< fLastDistance
)
586 std::vector
< double >::const_iterator aIter
= std::lower_bound( rDistances
.begin(), rDistances
.end(), fLastDistance
);
587 if ( aIter
!= rDistances
.begin() )
590 if ( ( *aIter
> fDistance
) && ( *aIter
< fLastDistance
) )
592 Point
& rPt0
= rPoly
[ i
- 1 ];
593 sal_Int32 fX
= rPoint
.X() - rPt0
.X();
594 sal_Int32 fY
= rPoint
.Y() - rPt0
.Y();
595 double fd
= ( 1.0 / ( fDistance
- fLastDistance
) ) * ( *aIter
- fLastDistance
);
596 rPoly
.Insert( i
, Point( (sal_Int32
)( rPt0
.X() + fX
* fd
), (sal_Int32
)( rPt0
.Y() + fY
* fd
) ) );
602 fLastDistance
= fDistance
;
606 void GetPoint( const Polygon
& rPoly
, const std::vector
< double >& rDistances
, const double& fX
, double& fx1
, double& fy1
)
609 if ( rPoly
.GetSize() > 1 )
611 std::vector
< double >::const_iterator aIter
= std::lower_bound( rDistances
.begin(), rDistances
.end(), fX
);
612 sal_uInt16 nIdx
= sal::static_int_cast
<sal_uInt16
>( std::distance( rDistances
.begin(), aIter
) );
613 if ( aIter
== rDistances
.end() )
615 const Point
& rPt
= rPoly
[ nIdx
];
618 if ( nIdx
&& ( aIter
!= rDistances
.end() ) && ( *aIter
!= fX
) )
620 nIdx
= sal::static_int_cast
<sal_uInt16
>( std::distance( rDistances
.begin(), aIter
) );
621 double fDist0
= *( aIter
- 1 );
622 double fd
= ( 1.0 / ( *aIter
- fDist0
) ) * ( fX
- fDist0
);
623 const Point
& rPt2
= rPoly
[ nIdx
- 1 ];
624 double fWidth
= rPt
.X() - rPt2
.X();
625 double fHeight
= rPt
.Y() - rPt2
.Y();
628 fx1
= rPt2
.X() + fWidth
;
629 fy1
= rPt2
.Y() + fHeight
;
634 void FitTextOutlinesToShapeOutlines( const tools::PolyPolygon
& aOutlines2d
, FWData
& rFWData
)
636 std::vector
< FWTextArea
>::iterator aTextAreaIter
= rFWData
.vTextAreas
.begin();
637 std::vector
< FWTextArea
>::iterator aTextAreaIEnd
= rFWData
.vTextAreas
.end();
639 sal_uInt16 nOutline2dIdx
= 0;
640 while( aTextAreaIter
!= aTextAreaIEnd
)
642 Rectangle rTextAreaBoundRect
= aTextAreaIter
->aBoundRect
;
643 sal_Int32 nLeft
= rTextAreaBoundRect
.Left();
644 sal_Int32 nTop
= rTextAreaBoundRect
.Top();
645 sal_Int32 nWidth
= rTextAreaBoundRect
.GetWidth();
646 sal_Int32 nHeight
= rTextAreaBoundRect
.GetHeight();
647 if ( rFWData
.bSingleLineMode
&& nHeight
&& nWidth
)
649 if ( nOutline2dIdx
>= aOutlines2d
.Count() )
651 const Polygon
& rOutlinePoly( aOutlines2d
[ nOutline2dIdx
++ ] );
652 const sal_uInt16 nPointCount
= rOutlinePoly
.GetSize();
653 if ( nPointCount
> 1 )
655 std::vector
< double > vDistances
;
656 vDistances
.reserve( nPointCount
);
657 CalcDistances( rOutlinePoly
, vDistances
);
658 if ( !vDistances
.empty() )
660 std::vector
< FWParagraphData
>::iterator
aParagraphIter( aTextAreaIter
->vParagraphs
.begin() );
661 std::vector
< FWParagraphData
>::iterator
aParagraphIEnd( aTextAreaIter
->vParagraphs
.end() );
662 while( aParagraphIter
!= aParagraphIEnd
)
664 std::vector
< FWCharacterData
>::iterator
aCharacterIter( aParagraphIter
->vCharacters
.begin() );
665 std::vector
< FWCharacterData
>::iterator
aCharacterIEnd( aParagraphIter
->vCharacters
.end() );
666 while ( aCharacterIter
!= aCharacterIEnd
)
668 std::vector
< tools::PolyPolygon
>::iterator aOutlineIter
= aCharacterIter
->vOutlines
.begin();
669 std::vector
< tools::PolyPolygon
>::iterator aOutlineIEnd
= aCharacterIter
->vOutlines
.end();
670 while( aOutlineIter
!= aOutlineIEnd
)
672 tools::PolyPolygon
& rPolyPoly
= *aOutlineIter
;
673 Rectangle
aBoundRect( rPolyPoly
.GetBoundRect() );
674 double fx1
= aBoundRect
.Left() - nLeft
;
675 double fx2
= aBoundRect
.Right() - nLeft
;
677 double fM1
= fx1
/ (double)nWidth
;
678 double fM2
= fx2
/ (double)nWidth
;
680 GetPoint( rOutlinePoly
, vDistances
, fM1
, fx1
, fy1
);
681 GetPoint( rOutlinePoly
, vDistances
, fM2
, fx2
, fy2
);
683 double fvx
= ( fy2
- fy1
);
684 double fvy
= - ( fx2
- fx1
);
685 fx1
= fx1
+ ( ( fx2
- fx1
) * 0.5 );
686 fy1
= fy1
+ ( ( fy2
- fy1
) * 0.5 );
688 double fAngle
= atan2( -fvx
, -fvy
);
689 double fL
= hypot( fvx
, fvy
);
692 fL
= (double)( aTextAreaIter
->aBoundRect
.GetHeight() / 2.0 + aTextAreaIter
->aBoundRect
.Top() ) - aParagraphIter
->aBoundRect
.Center().Y();
695 rPolyPoly
.Rotate( Point( aBoundRect
.Center().X(), aParagraphIter
->aBoundRect
.Center().Y() ), sin( fAngle
), cos( fAngle
) );
696 rPolyPoly
.Move( (sal_Int32
)( ( fx1
+ fvx
)- aBoundRect
.Center().X() ), (sal_Int32
)( ( fy1
+ fvy
) - aParagraphIter
->aBoundRect
.Center().Y() ) );
709 if ( ( nOutline2dIdx
+ 1 ) >= aOutlines2d
.Count() )
711 const Polygon
& rOutlinePoly( aOutlines2d
[ nOutline2dIdx
++ ] );
712 const Polygon
& rOutlinePoly2( aOutlines2d
[ nOutline2dIdx
++ ] );
713 const sal_uInt16 nPointCount
= rOutlinePoly
.GetSize();
714 const sal_uInt16 nPointCount2
= rOutlinePoly2
.GetSize();
715 if ( ( nPointCount
> 1 ) && ( nPointCount2
> 1 ) )
717 std::vector
< double > vDistances
;
718 vDistances
.reserve( nPointCount
);
719 std::vector
< double > vDistances2
;
720 vDistances2
.reserve( nPointCount2
);
721 CalcDistances( rOutlinePoly
, vDistances
);
722 CalcDistances( rOutlinePoly2
, vDistances2
);
723 std::vector
< FWParagraphData
>::iterator aParagraphIter
= aTextAreaIter
->vParagraphs
.begin();
724 std::vector
< FWParagraphData
>::iterator aParagraphIEnd
= aTextAreaIter
->vParagraphs
.end();
725 while( aParagraphIter
!= aParagraphIEnd
)
727 std::vector
< FWCharacterData
>::iterator
aCharacterIter( aParagraphIter
->vCharacters
.begin() );
728 std::vector
< FWCharacterData
>::iterator
aCharacterIEnd( aParagraphIter
->vCharacters
.end() );
729 while ( aCharacterIter
!= aCharacterIEnd
)
731 std::vector
< tools::PolyPolygon
>::iterator aOutlineIter
= aCharacterIter
->vOutlines
.begin();
732 std::vector
< tools::PolyPolygon
>::iterator aOutlineIEnd
= aCharacterIter
->vOutlines
.end();
733 while( aOutlineIter
!= aOutlineIEnd
)
735 tools::PolyPolygon
& rPolyPoly
= *aOutlineIter
;
736 sal_uInt16 i
, nPolyCount
= rPolyPoly
.Count();
737 for ( i
= 0; i
< nPolyCount
; i
++ )
740 basegfx::B2DPolygon
aCandidate(rPolyPoly
[ i
].getB2DPolygon());
742 if(aCandidate
.areControlPointsUsed())
744 aCandidate
= basegfx::tools::adaptiveSubdivideByAngle(aCandidate
);
747 // create local polygon copy to work on
748 Polygon
aLocalPoly(aCandidate
);
750 InsertMissingOutlinePoints( rOutlinePoly
, vDistances
, rTextAreaBoundRect
, aLocalPoly
);
751 InsertMissingOutlinePoints( rOutlinePoly2
, vDistances2
, rTextAreaBoundRect
, aLocalPoly
);
753 sal_uInt16 _nPointCount
= aLocalPoly
.GetSize();
756 if (!nWidth
|| !nHeight
)
757 throw o3tl::divide_by_zero();
758 for (sal_uInt16 j
= 0; j
< _nPointCount
; ++j
)
760 Point
& rPoint
= aLocalPoly
[ j
];
763 double fX
= (double)rPoint
.X() / (double)nWidth
;
764 double fY
= (double)rPoint
.Y() / (double)nHeight
;
766 double fx1
, fy1
, fx2
, fy2
;
767 GetPoint( rOutlinePoly
, vDistances
, fX
, fx1
, fy1
);
768 GetPoint( rOutlinePoly2
, vDistances2
, fX
, fx2
, fy2
);
769 double fWidth
= fx2
- fx1
;
770 double fHeight
= fy2
- fy1
;
771 rPoint
.X() = (sal_Int32
)( fx1
+ fWidth
* fY
);
772 rPoint
.Y() = (sal_Int32
)( fy1
+ fHeight
* fY
);
776 // write back polygon
777 rPolyPoly
[ i
] = aLocalPoly
;
791 SdrObject
* CreateSdrObjectFromParagraphOutlines( const FWData
& rFWData
, const SdrObject
* pCustomShape
)
793 SdrObject
* pRet
= NULL
;
794 basegfx::B2DPolyPolygon aPolyPoly
;
795 if ( !rFWData
.vTextAreas
.empty() )
797 std::vector
< FWTextArea
>::const_iterator aTextAreaIter
= rFWData
.vTextAreas
.begin();
798 std::vector
< FWTextArea
>::const_iterator aTextAreaIEnd
= rFWData
.vTextAreas
.end();
799 while ( aTextAreaIter
!= aTextAreaIEnd
)
801 std::vector
< FWParagraphData
>::const_iterator aParagraphIter
= aTextAreaIter
->vParagraphs
.begin();
802 std::vector
< FWParagraphData
>::const_iterator aParagraphIEnd
= aTextAreaIter
->vParagraphs
.end();
803 while ( aParagraphIter
!= aParagraphIEnd
)
805 std::vector
< FWCharacterData
>::const_iterator
aCharacterIter( aParagraphIter
->vCharacters
.begin() );
806 std::vector
< FWCharacterData
>::const_iterator
aCharacterIEnd( aParagraphIter
->vCharacters
.end() );
807 while ( aCharacterIter
!= aCharacterIEnd
)
809 std::vector
< tools::PolyPolygon
>::const_iterator aOutlineIter
= aCharacterIter
->vOutlines
.begin();
810 std::vector
< tools::PolyPolygon
>::const_iterator aOutlineIEnd
= aCharacterIter
->vOutlines
.end();
811 while( aOutlineIter
!= aOutlineIEnd
)
813 aPolyPoly
.append( aOutlineIter
->getB2DPolyPolygon() );
823 pRet
= new SdrPathObj( OBJ_POLY
, aPolyPoly
);
825 SfxItemSet
aSet( pCustomShape
->GetMergedItemSet() );
826 aSet
.ClearItem( SDRATTR_TEXTDIRECTION
); //SJ: vertical writing is not required, by removing this item no outliner is created
827 aSet
.Put(makeSdrShadowItem(false)); // #i37011# NO shadow for FontWork geometry
828 pRet
->SetMergedItemSet( aSet
); // * otherwise we would crash, because the outliner tries to create a Paraobject, but there is no model
833 Reference
< i18n::XBreakIterator
> EnhancedCustomShapeFontWork::mxBreakIterator
= 0;
835 Reference
< i18n::XBreakIterator
> EnhancedCustomShapeFontWork::GetBreakIterator()
837 if ( !mxBreakIterator
.is() )
839 Reference
< uno::XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
840 mxBreakIterator
= i18n::BreakIterator::create(xContext
);
842 return mxBreakIterator
;
845 SdrObject
* EnhancedCustomShapeFontWork::CreateFontWork( const SdrObject
* pShape2d
, const SdrObject
* pCustomShape
)
847 SdrObject
* pRet
= NULL
;
849 tools::PolyPolygon
aOutlines2d( GetOutlinesFromShape2d( pShape2d
) );
850 sal_uInt16 nOutlinesCount2d
= aOutlines2d
.Count();
851 if ( nOutlinesCount2d
)
854 if ( InitializeFontWorkData( pCustomShape
, nOutlinesCount2d
, aFWData
) )
856 /* retrieves the horizontal scaling factor that has to be used
857 to fit each paragraph text into its corresponding 2d outline */
858 CalculateHorizontalScalingFactor( pCustomShape
, aFWData
, aOutlines2d
);
860 /* retrieving the Outlines for the each Paragraph. */
862 GetFontWorkOutline( aFWData
, pCustomShape
);
864 FitTextOutlinesToShapeOutlines( aOutlines2d
, aFWData
);
866 pRet
= CreateSdrObjectFromParagraphOutlines( aFWData
, pCustomShape
);
872 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */