bump product version to 5.0.4.1
[LibreOffice.git] / svx / source / customshapes / EnhancedCustomShapeFontWork.cxx
blob7e033863237f8460ab268fca6ca69d68c380befe
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
46 #include <vector>
47 #include <numeric>
48 #include <algorithm>
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;
63 Rectangle aBoundRect;
65 struct FWParagraphData // representing a single paragraph
67 OUString aString;
68 std::vector< FWCharacterData > vCharacters;
69 Rectangle aBoundRect;
70 sal_Int16 nFrameDirection;
72 struct FWTextArea // representing multiple concluding paragraphs
74 std::vector< FWParagraphData > vParagraphs;
75 Rectangle aBoundRect;
77 struct FWData // representing the whole text
79 std::vector< FWTextArea > vTextAreas;
80 double fHorizontalTextScaling;
81 sal_uInt32 nMaxParagraphsPerTextArea;
82 sal_Int32 nSingleLineHeight;
83 bool bSingleLineMode;
87 static bool InitializeFontWorkData( const SdrObject* pCustomShape, const sal_uInt16 nOutlinesCount2d, FWData& rFWData )
89 bool bNoErr = false;
90 bool bSingleLineMode = false;
91 sal_uInt16 nTextAreaCount = nOutlinesCount2d;
92 if ( nOutlinesCount2d & 1 )
93 bSingleLineMode = true;
94 else
95 nTextAreaCount >>= 1;
97 if ( nTextAreaCount )
99 rFWData.bSingleLineMode = bSingleLineMode;
101 // setting the strings
102 OutlinerParaObject* pParaObj = static_cast<const SdrObjCustomShape*>(pCustomShape)->GetOutlinerParaObject();
103 if ( pParaObj )
105 const EditTextObject& rTextObj = pParaObj->GetTextObject();
106 sal_Int32 nParagraphsLeft = rTextObj.GetParagraphCount();
108 rFWData.nMaxParagraphsPerTextArea = ( ( nParagraphsLeft - 1 ) / nTextAreaCount ) + 1;
109 sal_Int32 j = 0;
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;
125 nTextAreaCount--;
127 bNoErr = true;
130 return bNoErr;
133 double GetLength( const Polygon& rPolygon )
135 double fLength = 0;
136 if ( rPolygon.GetSize() > 1 )
138 sal_uInt16 nCount = rPolygon.GetSize();
139 while( --nCount )
140 fLength += ((Polygon&)rPolygon).CalcDistance( nCount, nCount - 1 );
142 return fLength;
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;
154 sal_uInt16 i = 0;
155 bool bSingleLineMode = false;
156 sal_uInt16 nOutlinesCount2d = rOutline2d.Count();
158 vcl::Font aFont;
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++ ) );
184 fWidth /= 2.0;
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;
199 else
201 if ( fScale < fScalingFactor )
202 fScalingFactor = fScale;
205 ++aParagraphIter;
207 ++aTextAreaIter;
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() );
228 if ( xBI.is() )
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 );
236 else
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 ));
246 vcl::Font aFont;
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;
274 // VERTICAL
275 if ( bIsVertical )
277 // vertical _> each single character needs to be rotated by 90
278 sal_Int32 i;
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;
294 else
296 while ( aOutlineIter != aOutlineIEnd )
298 // rotating
299 aOutlineIter->Rotate( Point( nTextWidth / 2, rFWData.nSingleLineHeight / 2 ), 900 );
300 aCharacterData.aBoundRect.Union( aOutlineIter->GetBoundRect() );
301 ++aOutlineIter;
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 );
310 ++aOutlineIter;
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 );
327 ++aOutlineIter;
329 ++aCharacterIter;
332 else
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 );
348 delete[] pDXArry;
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 );
369 ++aCharacterIter;
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 ) );
377 else
378 rTextArea.aBoundRect.Bottom() += rFWData.nSingleLineHeight;
380 else
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();
400 if ( nMove )
401 aOutlineIter->Move( 0, -nMove );
402 ++aOutlineIter;
404 ++aCharacterIter;
408 if ( bIsVertical )
409 nVerticalOffset -= rFWData.nSingleLineHeight;
410 else
411 nVerticalOffset += rFWData.nSingleLineHeight;
412 ++aParagraphIter;
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" );
430 if ( pAny )
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();
443 if ( nParaWidth )
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 );
456 ++aOutlineIter;
458 ++aCharacterIter;
461 ++aParagraphIter;
464 else
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() );
480 if ( nHorzDiff )
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 );
491 ++aOutlineIter;
493 ++aCharacterIter;
496 ++aParagraphIter;
499 break;
500 default:
501 case SDRTEXTHORZADJUST_BLOCK : break; // don't know
502 case SDRTEXTHORZADJUST_LEFT : break; // already left aligned -> nothing to do
505 ++aTextAreaIter;
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);
528 return aOutlines2d;
531 void CalcDistances( const Polygon& rPoly, std::vector< double >& rDistances )
533 sal_uInt16 i, nCount = rPoly.GetSize();
534 if ( nCount > 1 )
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 ];
543 if ( fLength > 0.0 )
545 std::vector< double >::iterator aIter = rDistances.begin();
546 std::vector< double >::iterator aEnd = rDistances.end();
547 while ( aIter != aEnd )
548 *aIter++ /= fLength;
553 void InsertMissingOutlinePoints( const Polygon& /*rOutlinePoly*/, const std::vector< double >& rDistances, const Rectangle& rTextAreaBoundRect, Polygon& rPoly )
555 sal_uInt16 nSize = rPoly.GetSize();
556 if (nSize == 0)
557 return;
559 long nTextWidth = rTextAreaBoundRect.GetWidth();
561 if (nTextWidth == 0)
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;
569 if ( i )
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 ) ) );
581 fDistance = *aIter;
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() )
589 --aIter;
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 ) ) );
597 fDistance = *aIter;
602 fLastDistance = fDistance;
606 void GetPoint( const Polygon& rPoly, const std::vector< double >& rDistances, const double& fX, double& fx1, double& fy1 )
608 fy1 = fx1 = 0.0;
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() )
614 nIdx--;
615 const Point& rPt = rPoly[ nIdx ];
616 fx1 = rPt.X();
617 fy1 = rPt.Y();
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();
626 fWidth *= fd;
627 fHeight*= fd;
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() )
650 break;
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;
676 double fy1, fy2;
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 );
690 fvx = fvx / fL;
691 fvy = fvy / fL;
692 fL = (double)( aTextAreaIter->aBoundRect.GetHeight() / 2.0 + aTextAreaIter->aBoundRect.Top() ) - aParagraphIter->aBoundRect.Center().Y();
693 fvx *= fL;
694 fvy *= fL;
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() ) );
698 ++aOutlineIter;
700 ++aCharacterIter;
702 ++aParagraphIter;
707 else
709 if ( ( nOutline2dIdx + 1 ) >= aOutlines2d.Count() )
710 break;
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++ )
739 // #i35928#
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();
754 if (_nPointCount)
756 if (!nWidth || !nHeight)
757 throw o3tl::divide_by_zero();
758 for (sal_uInt16 j = 0; j < _nPointCount; ++j)
760 Point& rPoint = aLocalPoly[ j ];
761 rPoint.X() -= nLeft;
762 rPoint.Y() -= nTop;
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;
779 ++aOutlineIter;
781 ++aCharacterIter;
783 ++aParagraphIter;
787 ++aTextAreaIter;
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() );
814 ++aOutlineIter;
816 ++aCharacterIter;
818 ++aParagraphIter;
820 ++aTextAreaIter;
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
830 return pRet;
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 )
853 FWData aFWData;
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 );
869 return pRet;
872 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */