xref: /trunk/main/svx/source/customshapes/EnhancedCustomShapeFontWork.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svx.hxx"
30 #include "EnhancedCustomShapeFontWork.hxx"
31 #include <tools/solar.h>               // UINTXX
32 #include <svx/svddef.hxx>
33 #include <svx/svdogrp.hxx>
34 #include <svx/svdopath.hxx>
35 #include <vcl/metric.hxx>
36 #include <svx/svdpage.hxx>
37 #include <svx/sdasitm.hxx>
38 #include <svx/sdasaitm.hxx>
39 #include <svx/sdtfsitm.hxx>
40 #include <vcl/virdev.hxx>
41 #include <svx/svditer.hxx>
42 #include <vcl/metric.hxx>
43 #include <editeng/eeitem.hxx>
44 #include <editeng/frmdiritem.hxx>
45 #include <editeng/fontitem.hxx>
46 #include <editeng/postitem.hxx>
47 #include <editeng/wghtitem.hxx>
48 #include <editeng/charscaleitem.hxx>
49 #include "svx/EnhancedCustomShapeTypeNames.hxx"
50 #include <svx/svdorect.hxx>
51 #include <svx/svdoashp.hxx>
52 #include <editeng/outliner.hxx>
53 #include <editeng/outlobj.hxx>
54 #include <editeng/editobj.hxx>
55 #include <editeng/editeng.hxx>
56 #include <svx/svdmodel.hxx>
57 #include <vector>
58 #include <numeric>
59 #include <algorithm>
60 #include <comphelper/processfactory.hxx>
61 #ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_
62 #include <com/sun/star/i18n/ScriptType.hdl>
63 #endif
64 #include <basegfx/polygon/b2dpolypolygontools.hxx>
65 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
66 #ifndef _COM_SUN_STAR_I18N_CHARACTERITERATORMODE_HDL_
67 #include <com/sun/star/i18n/CharacterIteratorMode.hdl>
68 #endif
69 #include <basegfx/polygon/b2dpolygontools.hxx>
70 
71 using namespace com::sun::star;
72 using namespace com::sun::star::uno;
73 
74 typedef std::vector< std::vector< double > > PolyPolygonDistances;
75 
76 struct FWCharacterData                  // representing a single character
77 {
78     std::vector< PolyPolygon >          vOutlines;
79     Rectangle                           aBoundRect;
80 };
81 struct FWParagraphData                  // representing a single paragraph
82 {
83     rtl::OUString                       aString;
84     std::vector< FWCharacterData >      vCharacters;
85     Rectangle                           aBoundRect;
86     sal_Int16                           nFrameDirection;
87 };
88 struct FWTextArea                       // representing multiple concluding paragraphs
89 {
90     std::vector< FWParagraphData >      vParagraphs;
91     Rectangle                           aBoundRect;
92 };
93 struct FWData                           // representing the whole text
94 {
95     std::vector< FWTextArea >           vTextAreas;
96     double                              fHorizontalTextScaling;
97     sal_uInt32                          nMaxParagraphsPerTextArea;
98     sal_Int32                           nSingleLineHeight;
99     sal_Bool                            bSingleLineMode;
100 };
101 
102 
103 sal_Bool InitializeFontWorkData( const SdrObject* pCustomShape, const sal_uInt16 nOutlinesCount2d, FWData& rFWData )
104 {
105     sal_Bool bNoErr = sal_False;
106     sal_Bool bSingleLineMode = sal_False;
107     sal_uInt16 nTextAreaCount = nOutlinesCount2d;
108     if ( nOutlinesCount2d & 1 )
109         bSingleLineMode = sal_True;
110     else
111         nTextAreaCount >>= 1;
112 
113     if ( nTextAreaCount )
114     {
115         rFWData.bSingleLineMode = bSingleLineMode;
116 
117         // setting the strings
118         OutlinerParaObject* pParaObj = ((SdrObjCustomShape*)pCustomShape)->GetOutlinerParaObject();
119         if ( pParaObj )
120         {
121             const EditTextObject& rTextObj = pParaObj->GetTextObject();
122             sal_Int32 nParagraphsLeft = rTextObj.GetParagraphCount();
123 
124             rFWData.nMaxParagraphsPerTextArea = ( ( nParagraphsLeft - 1 ) / nTextAreaCount ) + 1;
125             sal_Int16 j = 0;
126             while( nParagraphsLeft && nTextAreaCount )
127             {
128                 FWTextArea aTextArea;
129                 sal_Int32 i, nParagraphs = ( ( nParagraphsLeft - 1 ) / nTextAreaCount ) + 1;
130                 for ( i = 0; i < nParagraphs; i++, j++ )
131                 {
132                     FWParagraphData aParagraphData;
133                     aParagraphData.aString = rTextObj.GetText( j );
134 
135                     const SfxItemSet& rParaSet = rTextObj.GetParaAttribs( j );  // retrieving some paragraph attributes
136                     aParagraphData.nFrameDirection = ((SvxFrameDirectionItem&)rParaSet.Get( EE_PARA_WRITINGDIR )).GetValue();
137                     aTextArea.vParagraphs.push_back( aParagraphData );
138                 }
139                 rFWData.vTextAreas.push_back( aTextArea );
140                 nParagraphsLeft -= nParagraphs;
141                 nTextAreaCount--;
142             }
143             bNoErr = sal_True;
144         }
145     }
146     return bNoErr;
147 }
148 
149 double GetLength( const Polygon& rPolygon )
150 {
151     double fLength = 0;
152     if ( rPolygon.GetSize() > 1 )
153     {
154         sal_uInt16 nCount = rPolygon.GetSize();
155         while( --nCount )
156             fLength += ((Polygon&)rPolygon).CalcDistance( nCount, nCount - 1 );
157     }
158     return fLength;
159 }
160 
161 
162 /* CalculateHorizontalScalingFactor returns the horizontal scaling factor for
163 the whole text object, so that each text will match its corresponding 2d Outline */
164 void CalculateHorizontalScalingFactor( const SdrObject* pCustomShape,
165                                         FWData& rFWData, const PolyPolygon& rOutline2d )
166 {
167     double fScalingFactor = 1.0;
168     sal_Bool bScalingFactorDefined = sal_False;
169 
170     sal_uInt16 i = 0;
171     sal_Bool bSingleLineMode = sal_False;
172     sal_uInt16 nOutlinesCount2d = rOutline2d.Count();
173 
174     Font aFont;
175     SvxFontItem& rFontItem = (SvxFontItem&)pCustomShape->GetMergedItem( EE_CHAR_FONTINFO );
176     aFont.SetHeight( pCustomShape->GetLogicRect().GetHeight() / rFWData.nMaxParagraphsPerTextArea );
177     aFont.SetAlign( ALIGN_TOP );
178     aFont.SetName( rFontItem.GetFamilyName() );
179     aFont.SetFamily( rFontItem.GetFamily() );
180     aFont.SetStyleName( rFontItem.GetStyleName() );
181     aFont.SetOrientation( 0 );
182     // initializing virtual device
183 
184     VirtualDevice aVirDev( 1 );
185     aVirDev.SetMapMode( MAP_100TH_MM );
186     aVirDev.SetFont( aFont );
187 
188     if ( nOutlinesCount2d & 1 )
189         bSingleLineMode = sal_True;
190 
191     std::vector< FWTextArea >::iterator aTextAreaIter = rFWData.vTextAreas.begin();
192     std::vector< FWTextArea >::iterator aTextAreaIEnd = rFWData.vTextAreas.end();
193     while( aTextAreaIter != aTextAreaIEnd )
194     {
195         // calculating the width of the corresponding 2d text area
196         double fWidth = GetLength( rOutline2d.GetObject( i++ ) );
197         if ( !bSingleLineMode )
198         {
199             fWidth += GetLength( rOutline2d.GetObject( i++ ) );
200             fWidth /= 2.0;
201         }
202         std::vector< FWParagraphData >::const_iterator aParagraphIter( aTextAreaIter->vParagraphs.begin() );
203         std::vector< FWParagraphData >::const_iterator aParagraphIEnd( aTextAreaIter->vParagraphs.end() );
204         while( aParagraphIter != aParagraphIEnd )
205         {
206             double fTextWidth = aVirDev.GetTextWidth( aParagraphIter->aString );
207             if ( fTextWidth > 0.0 )
208             {
209                 double fScale = fWidth / fTextWidth;
210                 if ( !bScalingFactorDefined )
211                 {
212                     fScalingFactor = fScale;
213                     bScalingFactorDefined = sal_True;
214                 }
215                 else
216                 {
217                     if ( fScale < fScalingFactor )
218                         fScalingFactor = fScale;
219                 }
220             }
221             aParagraphIter++;
222         }
223         aTextAreaIter++;
224     }
225     rFWData.fHorizontalTextScaling = fScalingFactor;
226 }
227 
228 void GetTextAreaOutline( const FWData& rFWData, const SdrObject* pCustomShape, FWTextArea& rTextArea, sal_Bool bSameLetterHeights )
229 {
230     sal_Bool bIsVertical = ((SdrObjCustomShape*)pCustomShape)->IsVerticalWriting();
231     sal_Int32 nVerticalOffset = rFWData.nMaxParagraphsPerTextArea > rTextArea.vParagraphs.size()
232                                     ? rFWData.nSingleLineHeight / 2 : 0;
233 
234     std::vector< FWParagraphData >::iterator aParagraphIter( rTextArea.vParagraphs.begin() );
235     std::vector< FWParagraphData >::iterator aParagraphIEnd( rTextArea.vParagraphs.end() );
236     while( aParagraphIter != aParagraphIEnd )
237     {
238         const rtl::OUString& rText = aParagraphIter->aString;
239         if ( rText.getLength() )
240         {
241             // generating vcl/font
242             sal_uInt16 nScriptType = i18n::ScriptType::LATIN;
243             Reference< i18n::XBreakIterator > xBI( EnhancedCustomShapeFontWork::GetBreakIterator() );
244             if ( xBI.is() )
245             {
246                 nScriptType = xBI->getScriptType( rText, 0 );
247                 sal_uInt16 nChg = 0;
248                 if( i18n::ScriptType::WEAK == nScriptType )
249                 {
250                     nChg = (xub_StrLen)xBI->endOfScript( rText, nChg, nScriptType );
251                     if( nChg < rText.getLength() )
252                         nScriptType = xBI->getScriptType( rText, nChg );
253                     else
254                         nScriptType = i18n::ScriptType::LATIN;
255                 }
256             }
257             sal_uInt16 nFntItm = EE_CHAR_FONTINFO;
258             if ( nScriptType == i18n::ScriptType::COMPLEX )
259                 nFntItm = EE_CHAR_FONTINFO_CTL;
260             else if ( nScriptType == i18n::ScriptType::ASIAN )
261                 nFntItm = EE_CHAR_FONTINFO_CJK;
262             SvxFontItem& rFontItem = (SvxFontItem&)pCustomShape->GetMergedItem( nFntItm );
263             Font aFont;
264             aFont.SetHeight( rFWData.nSingleLineHeight );
265             aFont.SetAlign( ALIGN_TOP );
266     //      aFont.SetAlign( )
267 
268             aFont.SetName( rFontItem.GetFamilyName() );
269             aFont.SetFamily( rFontItem.GetFamily() );
270             aFont.SetStyleName( rFontItem.GetStyleName() );
271             aFont.SetOrientation( 0 );
272 
273             SvxPostureItem& rPostureItem = (SvxPostureItem&)pCustomShape->GetMergedItem( EE_CHAR_ITALIC );
274             aFont.SetItalic( rPostureItem.GetPosture() );
275 
276             SvxWeightItem& rWeightItem = (SvxWeightItem&)pCustomShape->GetMergedItem( EE_CHAR_WEIGHT );
277             aFont.SetWeight( rWeightItem.GetWeight() );
278 
279             // initializing virtual device
280             VirtualDevice aVirDev( 1 );
281             aVirDev.SetMapMode( MAP_100TH_MM );
282             aVirDev.SetFont( aFont );
283             aVirDev.EnableRTL( sal_True );
284             if ( aParagraphIter->nFrameDirection == FRMDIR_HORI_RIGHT_TOP )
285                 aVirDev.SetLayoutMode( TEXT_LAYOUT_BIDI_RTL );
286 
287             SvxCharScaleWidthItem& rCharScaleWidthItem = (SvxCharScaleWidthItem&)pCustomShape->GetMergedItem( EE_CHAR_FONTWIDTH );
288             sal_uInt16 nCharScaleWidth = rCharScaleWidthItem.GetValue();
289             sal_Int32* pDXArry = NULL;
290             sal_Int32 nWidth = 0;
291 
292             // VERTICAL
293             if ( bIsVertical )
294             {
295                 // vertical _> each single character needs to be rotated by 90
296                 sal_Int32 i;
297                 sal_Int32 nHeight = 0;
298                 Rectangle aSingleCharacterUnion;
299                 for ( i = 0; i < rText.getLength(); i++ )
300                 {
301                     FWCharacterData aCharacterData;
302                     rtl::OUString aCharText( (sal_Unicode)rText[ i ] );
303                     if ( aVirDev.GetTextOutlines( aCharacterData.vOutlines, aCharText, 0, 0, STRING_LEN, sal_True, nWidth, pDXArry ) )
304                     {
305                         sal_Int32 nTextWidth = aVirDev.GetTextWidth( aCharText, 0, STRING_LEN );
306                         std::vector< PolyPolygon >::iterator aOutlineIter = aCharacterData.vOutlines.begin();
307                         std::vector< PolyPolygon >::iterator aOutlineIEnd = aCharacterData.vOutlines.end();
308                         if ( aOutlineIter == aOutlineIEnd )
309                         {
310                             nHeight += rFWData.nSingleLineHeight;
311                         }
312                         else
313                         {
314                             while ( aOutlineIter != aOutlineIEnd )
315                             {
316                                 // rotating
317                                 aOutlineIter->Rotate( Point( nTextWidth / 2, rFWData.nSingleLineHeight / 2 ), 900 );
318                                 aCharacterData.aBoundRect.Union( aOutlineIter->GetBoundRect() );
319                                 aOutlineIter++;
320                             }
321                             aOutlineIter = aCharacterData.vOutlines.begin();
322                             aOutlineIEnd = aCharacterData.vOutlines.end();
323                             while ( aOutlineIter != aOutlineIEnd )
324                             {
325                                 sal_Int32 nM = - aCharacterData.aBoundRect.Left() + nHeight;
326                                 aOutlineIter->Move( nM, 0 );
327                                 aCharacterData.aBoundRect.Move( nM, 0 );
328                                 aOutlineIter++;
329                             }
330                             nHeight += aCharacterData.aBoundRect.GetWidth() + ( rFWData.nSingleLineHeight / 5 );
331                             aSingleCharacterUnion.Union( aCharacterData.aBoundRect );
332                         }
333                     }
334                     aParagraphIter->vCharacters.push_back( aCharacterData );
335                 }
336                 std::vector< FWCharacterData >::iterator aCharacterIter( aParagraphIter->vCharacters.begin() );
337                 std::vector< FWCharacterData >::iterator aCharacterIEnd( aParagraphIter->vCharacters.end() );
338                 while ( aCharacterIter != aCharacterIEnd )
339                 {
340                     std::vector< PolyPolygon >::iterator aOutlineIter( aCharacterIter->vOutlines.begin() );
341                     std::vector< PolyPolygon >::iterator aOutlineIEnd( aCharacterIter->vOutlines.end() );
342                     while ( aOutlineIter != aOutlineIEnd )
343                     {
344                         aOutlineIter->Move( ( aSingleCharacterUnion.GetWidth() - aCharacterIter->aBoundRect.GetWidth() ) / 2, 0 );
345                         aOutlineIter++;
346                     }
347                     aCharacterIter++;
348                 }
349             }
350             else
351             {
352                 if ( ( nCharScaleWidth != 100 ) && nCharScaleWidth )
353                 {   // applying character spacing
354                     pDXArry = new sal_Int32[ rText.getLength() ];
355                     aVirDev.GetTextArray( rText, pDXArry, 0, STRING_LEN );
356                     FontMetric aFontMetric( aVirDev.GetFontMetric() );
357                     aFont.SetWidth( (sal_Int32)( (double)aFontMetric.GetWidth() * ( (double)100 / (double)nCharScaleWidth ) ) );
358                     aVirDev.SetFont( aFont );
359                 }
360                 FWCharacterData aCharacterData;
361                 if ( aVirDev.GetTextOutlines( aCharacterData.vOutlines, rText, 0, 0, STRING_LEN, sal_True, nWidth, pDXArry ) )
362                 {
363                     aParagraphIter->vCharacters.push_back( aCharacterData );
364                 }
365 
366 /* trying to retrieve each single character _> is not working well
367                 sal_Int32 i;
368                 for ( i = 0; i < rText.getLength(); i++ )
369                 {
370                     FWCharacterData aCharacterData;
371                     if ( aVirDev.GetTextOutlines( aCharacterData.vOutlines, rText, 0, i, 1, sal_True, nWidth, pDXArry ) )
372                     {
373                         std::vector< PolyPolygon >::iterator aOutlineIter = aCharacterData.vOutlines.begin();
374                         std::vector< PolyPolygon >::iterator aOutlineIEnd  = aCharacterData.vOutlines.end();
375                         while ( aOutlineIter != aOutlineIEnd )
376                         {
377                             aCharacterData.aBoundRect.Union( aOutlineIter->GetBoundRect() );
378                             aOutlineIter++;
379                         }
380                     }
381                     aParagraphIter->vCharacters.push_back( aCharacterData );
382                 }
383 */
384             }
385             delete[] pDXArry;
386 
387             // veritcal alignment
388             std::vector< FWCharacterData >::iterator aCharacterIter( aParagraphIter->vCharacters.begin() );
389             std::vector< FWCharacterData >::iterator aCharacterIEnd ( aParagraphIter->vCharacters.end() );
390             while ( aCharacterIter != aCharacterIEnd )
391             {
392                 std::vector< PolyPolygon >::iterator aOutlineIter( aCharacterIter->vOutlines.begin() );
393                 std::vector< PolyPolygon >::iterator aOutlineIEnd( aCharacterIter->vOutlines.end() );
394                 while( aOutlineIter != aOutlineIEnd )
395                 {
396 
397                     PolyPolygon& rPolyPoly = *aOutlineIter++;
398 
399                     if ( nVerticalOffset )
400                         rPolyPoly.Move( 0, nVerticalOffset );
401 
402                     // retrieving the boundrect for the paragraph
403                     Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
404                     aParagraphIter->aBoundRect.Union( aBoundRect );
405                 }
406                 aCharacterIter++;
407             }
408         }
409         // updating the boundrect for the text area by merging the current paragraph boundrect
410         if ( aParagraphIter->aBoundRect.IsEmpty() )
411         {
412             if ( rTextArea.aBoundRect.IsEmpty() )
413                 rTextArea.aBoundRect = Rectangle( Point( 0, 0 ), Size( 1, rFWData.nSingleLineHeight ) );
414             else
415                 rTextArea.aBoundRect.Bottom() += rFWData.nSingleLineHeight;
416         }
417         else
418         {
419             Rectangle& rParagraphBoundRect = aParagraphIter->aBoundRect;
420             rTextArea.aBoundRect.Union( rParagraphBoundRect );
421 
422             if ( bSameLetterHeights )
423             {
424                 std::vector< FWCharacterData >::iterator aCharacterIter( aParagraphIter->vCharacters.begin() );
425                 std::vector< FWCharacterData >::iterator aCharacterIEnd( aParagraphIter->vCharacters.end() );
426                 while ( aCharacterIter != aCharacterIEnd )
427                 {
428                     std::vector< PolyPolygon >::iterator aOutlineIter( aCharacterIter->vOutlines.begin() );
429                     std::vector< PolyPolygon >::iterator aOutlineIEnd( aCharacterIter->vOutlines.end() );
430                     while( aOutlineIter != aOutlineIEnd )
431                     {
432                         Rectangle aPolyPolyBoundRect( aOutlineIter->GetBoundRect() );
433                         if ( aPolyPolyBoundRect.GetHeight() != rParagraphBoundRect.GetHeight() )
434                             aOutlineIter->Scale( 1.0, (double)rParagraphBoundRect.GetHeight() / aPolyPolyBoundRect.GetHeight() );
435                         aPolyPolyBoundRect = aOutlineIter->GetBoundRect();
436                         sal_Int32 nMove = aPolyPolyBoundRect.Top() - rParagraphBoundRect.Top();
437                         if ( nMove )
438                             aOutlineIter->Move( 0, -nMove );
439                         aOutlineIter++;
440                     }
441                     aCharacterIter++;
442                 }
443             }
444         }
445         if ( bIsVertical )
446             nVerticalOffset -= rFWData.nSingleLineHeight;
447         else
448             nVerticalOffset += rFWData.nSingleLineHeight;
449         aParagraphIter++;
450     }
451 }
452 
453 void GetFontWorkOutline( FWData& rFWData, const SdrObject* pCustomShape )
454 {
455     SdrTextHorzAdjust eHorzAdjust( ((SdrTextHorzAdjustItem&)pCustomShape->GetMergedItem( SDRATTR_TEXT_HORZADJUST )).GetValue() );
456     SdrFitToSizeType  eFTS( ((SdrTextFitToSizeTypeItem&)pCustomShape->GetMergedItem( SDRATTR_TEXT_FITTOSIZE )).GetValue() );
457 
458     std::vector< FWTextArea >::iterator aTextAreaIter = rFWData.vTextAreas.begin();
459     std::vector< FWTextArea >::iterator aTextAreaIEnd = rFWData.vTextAreas.end();
460 
461     rFWData.nSingleLineHeight = (sal_Int32)( ( (double)pCustomShape->GetLogicRect().GetHeight()
462                                                 / rFWData.nMaxParagraphsPerTextArea ) * rFWData.fHorizontalTextScaling );
463 
464     sal_Bool bSameLetterHeights = sal_False;
465     SdrCustomShapeGeometryItem& rGeometryItem = (SdrCustomShapeGeometryItem&)pCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
466     const rtl::OUString sTextPath( RTL_CONSTASCII_USTRINGPARAM ( "TextPath" ) );
467     const rtl::OUString sSameLetterHeights( RTL_CONSTASCII_USTRINGPARAM ( "SameLetterHeights" ) );
468     com::sun::star::uno::Any* pAny = rGeometryItem.GetPropertyValueByName( sTextPath, sSameLetterHeights );
469     if ( pAny )
470         *pAny >>= bSameLetterHeights;
471 
472     while ( aTextAreaIter != aTextAreaIEnd )
473     {
474         GetTextAreaOutline( rFWData, pCustomShape, *aTextAreaIter, bSameLetterHeights );
475         if ( eFTS == SDRTEXTFIT_ALLLINES )
476         {
477             std::vector< FWParagraphData >::iterator aParagraphIter( aTextAreaIter->vParagraphs.begin() );
478             std::vector< FWParagraphData >::iterator aParagraphIEnd( aTextAreaIter->vParagraphs.end() );
479             while ( aParagraphIter != aParagraphIEnd )
480             {
481                 sal_Int32 nParaWidth = aParagraphIter->aBoundRect.GetWidth();
482                 if ( nParaWidth )
483                 {
484                     double fScale = (double)aTextAreaIter->aBoundRect.GetWidth() / nParaWidth;
485 
486                     std::vector< FWCharacterData >::iterator aCharacterIter( aParagraphIter->vCharacters.begin() );
487                     std::vector< FWCharacterData >::iterator aCharacterIEnd( aParagraphIter->vCharacters.end() );
488                     while ( aCharacterIter != aCharacterIEnd )
489                     {
490                         std::vector< PolyPolygon >::iterator aOutlineIter = aCharacterIter->vOutlines.begin();
491                         std::vector< PolyPolygon >::iterator aOutlineIEnd = aCharacterIter->vOutlines.end();
492                         while( aOutlineIter != aOutlineIEnd )
493                         {
494                             aOutlineIter->Scale( fScale, 1.0 );
495                             aOutlineIter++;
496                         }
497                         aCharacterIter++;
498                     }
499                 }
500                 aParagraphIter++;
501             }
502         }
503         else
504         {
505             switch( eHorzAdjust )
506             {
507                 case SDRTEXTHORZADJUST_RIGHT :
508                 case SDRTEXTHORZADJUST_CENTER:
509                 {
510                     std::vector< FWParagraphData >::iterator aParagraphIter( aTextAreaIter->vParagraphs.begin() );
511                     std::vector< FWParagraphData >::iterator aParagraphIEnd( aTextAreaIter->vParagraphs.end() );
512                     while ( aParagraphIter != aParagraphIEnd )
513                     {
514                         sal_Int32 nHorzDiff = 0;
515                         if ( eHorzAdjust == SDRTEXTHORZADJUST_CENTER )
516                             nHorzDiff = ( aTextAreaIter->aBoundRect.GetWidth() - aParagraphIter->aBoundRect.GetWidth() ) / 2;
517                         else if ( eHorzAdjust == SDRTEXTHORZADJUST_RIGHT )
518                             nHorzDiff = ( aTextAreaIter->aBoundRect.GetWidth() - aParagraphIter->aBoundRect.GetWidth() );
519                         if ( nHorzDiff )
520                         {
521                             std::vector< FWCharacterData >::iterator aCharacterIter( aParagraphIter->vCharacters.begin() );
522                             std::vector< FWCharacterData >::iterator aCharacterIEnd( aParagraphIter->vCharacters.end() );
523                             while ( aCharacterIter != aCharacterIEnd )
524                             {
525                                 std::vector< PolyPolygon >::iterator aOutlineIter = aCharacterIter->vOutlines.begin();
526                                 std::vector< PolyPolygon >::iterator aOutlineIEnd = aCharacterIter->vOutlines.end();
527                                 while( aOutlineIter != aOutlineIEnd )
528                                 {
529                                     aOutlineIter->Move( nHorzDiff, 0 );
530                                     aOutlineIter++;
531                                 }
532                                 aCharacterIter++;
533                             }
534                         }
535                         aParagraphIter++;
536                     }
537                 }
538                 break;
539                 default:
540                 case SDRTEXTHORZADJUST_BLOCK : break;   // don't know
541                 case SDRTEXTHORZADJUST_LEFT : break;    // already left aligned -> nothing to do
542             }
543         }
544         aTextAreaIter++;
545     }
546 }
547 
548 basegfx::B2DPolyPolygon GetOutlinesFromShape2d( const SdrObject* pShape2d )
549 {
550     basegfx::B2DPolyPolygon aOutlines2d;
551 
552     SdrObjListIter aObjListIter( *pShape2d, IM_DEEPWITHGROUPS );
553     while( aObjListIter.IsMore() )
554     {
555         SdrObject* pPartObj = aObjListIter.Next();
556         if ( pPartObj->ISA( SdrPathObj ) )
557         {
558             basegfx::B2DPolyPolygon aCandidate(((SdrPathObj*)pPartObj)->GetPathPoly());
559             if(aCandidate.areControlPointsUsed())
560             {
561                 aCandidate = basegfx::tools::adaptiveSubdivideByAngle(aCandidate);
562             }
563             aOutlines2d.append(aCandidate);
564         }
565     }
566 
567     return aOutlines2d;
568 }
569 
570 void CalcDistances( const Polygon& rPoly, std::vector< double >& rDistances )
571 {
572     sal_uInt16 i, nCount = rPoly.GetSize();
573     if ( nCount > 1 )
574     {
575         for ( i = 0; i < nCount; i++ )
576         {
577             double fDistance = i ? ((Polygon&)rPoly).CalcDistance( i, i - 1 ) : 0.0;
578             rDistances.push_back( fDistance );
579         }
580         std::partial_sum( rDistances.begin(), rDistances.end(), rDistances.begin() );
581         double fLength = rDistances[ rDistances.size() - 1 ];
582         if ( fLength > 0.0 )
583         {
584             std::vector< double >::iterator aIter = rDistances.begin();
585             std::vector< double >::iterator aEnd = rDistances.end();
586             while ( aIter != aEnd )
587                 *aIter++ /= fLength;
588         }
589     }
590 }
591 
592 void InsertMissingOutlinePoints( const Polygon& /*rOutlinePoly*/, const std::vector< double >& rDistances, const Rectangle& rTextAreaBoundRect, Polygon& rPoly )
593 {
594     sal_uInt16 i = 0;
595     double fLastDistance = 0.0;
596     for ( i = 0; i < rPoly.GetSize(); i++ )
597     {
598         Point& rPoint = rPoly[ i ];
599         double fDistance = (double)( rPoint.X() - rTextAreaBoundRect.Left() ) / (double)rTextAreaBoundRect.GetWidth();
600         if ( i )
601         {
602             if ( fDistance > fLastDistance )
603             {
604                 std::vector< double >::const_iterator aIter = std::upper_bound( rDistances.begin(), rDistances.end(), fLastDistance );
605                 if  ( aIter != rDistances.end() && ( *aIter > fLastDistance ) && ( *aIter < fDistance ) )
606                 {
607                     Point& rPt0 = rPoly[ i - 1 ];
608                     sal_Int32 fX = rPoint.X() - rPt0.X();
609                     sal_Int32 fY = rPoint.Y() - rPt0.Y();
610                     double fd = ( 1.0 / ( fDistance - fLastDistance ) ) * ( *aIter - fLastDistance );
611                     rPoly.Insert( i, Point( (sal_Int32)( rPt0.X() + fX * fd ), (sal_Int32)( rPt0.Y() + fY * fd ) ) );
612                     fDistance = *aIter;
613                 }
614             }
615             else if ( fDistance < fLastDistance )
616             {
617                 std::vector< double >::const_iterator aIter = std::lower_bound( rDistances.begin(), rDistances.end(), fLastDistance );
618                 if  ( aIter-- != rDistances.begin() )
619                 {
620                     if ( ( *aIter > fDistance ) && ( *aIter < fLastDistance ) )
621                     {
622                         Point& rPt0 = rPoly[ i - 1 ];
623                         sal_Int32 fX = rPoint.X() - rPt0.X();
624                         sal_Int32 fY = rPoint.Y() - rPt0.Y();
625                         double fd = ( 1.0 / ( fDistance - fLastDistance ) ) * ( *aIter - fLastDistance );
626                         rPoly.Insert( i, Point( (sal_Int32)( rPt0.X() + fX * fd ), (sal_Int32)( rPt0.Y() + fY * fd ) ) );
627                         fDistance = *aIter;
628                     }
629                 }
630             }
631         }
632         fLastDistance = fDistance;
633     }
634 }
635 
636 void GetPoint( const Polygon& rPoly, const std::vector< double >& rDistances, const double& fX, double& fx1, double& fy1 )
637 {
638     fy1 = fx1 = 0.0;
639     if ( rPoly.GetSize() )
640     {
641         std::vector< double >::const_iterator aIter = std::lower_bound( rDistances.begin(), rDistances.end(), fX );
642         sal_uInt16 nIdx = sal::static_int_cast<sal_uInt16>( std::distance( rDistances.begin(), aIter ) );
643         if ( aIter == rDistances.end() )
644             nIdx--;
645         const Point& rPt = rPoly[ nIdx ];
646         fx1 = rPt.X();
647         fy1 = rPt.Y();
648         if ( nIdx && ( aIter != rDistances.end() ) && ( *aIter != fX ) )
649         {
650             nIdx = sal::static_int_cast<sal_uInt16>( std::distance( rDistances.begin(), aIter ) );
651             double fDist0 = *( aIter - 1 );
652             double fd = ( 1.0 / ( *aIter - fDist0 ) ) * ( fX - fDist0 );
653             const Point& rPt2 = rPoly[ nIdx - 1 ];
654             double fWidth = rPt.X() - rPt2.X();
655             double fHeight= rPt.Y() - rPt2.Y();
656             fWidth *= fd;
657             fHeight*= fd;
658             fx1 = rPt2.X() + fWidth;
659             fy1 = rPt2.Y() + fHeight;
660         }
661     }
662 }
663 
664 void FitTextOutlinesToShapeOutlines( const PolyPolygon& aOutlines2d, FWData& rFWData )
665 {
666     std::vector< FWTextArea >::iterator aTextAreaIter = rFWData.vTextAreas.begin();
667     std::vector< FWTextArea >::iterator aTextAreaIEnd = rFWData.vTextAreas.end();
668 
669     sal_uInt16 nOutline2dIdx = 0;
670     while( aTextAreaIter != aTextAreaIEnd )
671     {
672         Rectangle rTextAreaBoundRect = aTextAreaIter->aBoundRect;
673         sal_Int32 nLeft = rTextAreaBoundRect.Left();
674         sal_Int32 nTop = rTextAreaBoundRect.Top();
675         sal_Int32 nWidth = rTextAreaBoundRect.GetWidth();
676         sal_Int32 nHeight= rTextAreaBoundRect.GetHeight();
677         if ( rFWData.bSingleLineMode && nHeight && nWidth )
678         {
679             if ( nOutline2dIdx >= aOutlines2d.Count() )
680                 break;
681             const Polygon& rOutlinePoly( aOutlines2d[ nOutline2dIdx++ ] );
682             const sal_uInt16 nPointCount = rOutlinePoly.GetSize();
683             if ( nPointCount > 1 )
684             {
685                 std::vector< double > vDistances;
686                 vDistances.reserve( nPointCount );
687                 CalcDistances( rOutlinePoly, vDistances );
688                 if ( !vDistances.empty() )
689                 {
690                     std::vector< FWParagraphData >::iterator aParagraphIter( aTextAreaIter->vParagraphs.begin() );
691                     std::vector< FWParagraphData >::iterator aParagraphIEnd( aTextAreaIter->vParagraphs.end() );
692                     while( aParagraphIter != aParagraphIEnd )
693                     {
694                         std::vector< FWCharacterData >::iterator aCharacterIter( aParagraphIter->vCharacters.begin() );
695                         std::vector< FWCharacterData >::iterator aCharacterIEnd( aParagraphIter->vCharacters.end() );
696                         while ( aCharacterIter != aCharacterIEnd )
697                         {
698                             std::vector< PolyPolygon >::iterator aOutlineIter = aCharacterIter->vOutlines.begin();
699                             std::vector< PolyPolygon >::iterator aOutlineIEnd = aCharacterIter->vOutlines.end();
700                             while( aOutlineIter != aOutlineIEnd )
701                             {
702                                 PolyPolygon& rPolyPoly = *aOutlineIter;
703                                 Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
704                                 double fx1 = aBoundRect.Left() - nLeft;
705                                 double fx2 = aBoundRect.Right() - nLeft;
706                                 double fy1, fy2;
707                                 double fM1 = fx1 / (double)nWidth;
708                                 double fM2 = fx2 / (double)nWidth;
709 
710                                 GetPoint( rOutlinePoly, vDistances, fM1, fx1, fy1 );
711                                 GetPoint( rOutlinePoly, vDistances, fM2, fx2, fy2 );
712 
713                                 double fvx = ( fy2 - fy1 );
714                                 double fvy = - ( fx2 - fx1 );
715                                 fx1 = fx1 + ( ( fx2 - fx1 ) * 0.5 );
716                                 fy1 = fy1 + ( ( fy2 - fy1 ) * 0.5 );
717 
718                                 double fAngle = atan2( -fvx, -fvy );
719                                 double fL = hypot( fvx, fvy );
720                                 fvx = fvx / fL;
721                                 fvy = fvy / fL;
722                                 fL = (double)( aTextAreaIter->aBoundRect.GetHeight() / 2.0 + aTextAreaIter->aBoundRect.Top() ) - aParagraphIter->aBoundRect.Center().Y();
723                                 fvx *= fL;
724                                 fvy *= fL;
725                                 rPolyPoly.Rotate( Point( aBoundRect.Center().X(), aParagraphIter->aBoundRect.Center().Y() ), sin( fAngle ), cos( fAngle ) );
726                                 rPolyPoly.Move( (sal_Int32)( ( fx1 + fvx )- aBoundRect.Center().X() ), (sal_Int32)( ( fy1 + fvy ) - aParagraphIter->aBoundRect.Center().Y() ) );
727 
728                                 aOutlineIter++;
729                             }
730                             aCharacterIter++;
731                         }
732                         aParagraphIter++;
733                     }
734                 }
735             }
736         }
737         else
738         {
739             if ( ( nOutline2dIdx + 1 ) >= aOutlines2d.Count() )
740                 break;
741             const Polygon& rOutlinePoly( aOutlines2d[ nOutline2dIdx++ ] );
742             const Polygon& rOutlinePoly2( aOutlines2d[ nOutline2dIdx++ ] );
743             const sal_uInt16 nPointCount = rOutlinePoly.GetSize();
744             const sal_uInt16 nPointCount2 = rOutlinePoly2.GetSize();
745             if ( ( nPointCount > 1 ) && ( nPointCount2 > 1 ) )
746             {
747                 std::vector< double > vDistances;
748                 vDistances.reserve( nPointCount );
749                 std::vector< double > vDistances2;
750                 vDistances2.reserve( nPointCount2 );
751                 CalcDistances( rOutlinePoly, vDistances );
752                 CalcDistances( rOutlinePoly2, vDistances2 );
753                 std::vector< FWParagraphData >::iterator aParagraphIter = aTextAreaIter->vParagraphs.begin();
754                 std::vector< FWParagraphData >::iterator aParagraphIEnd = aTextAreaIter->vParagraphs.end();
755                 while( aParagraphIter != aParagraphIEnd )
756                 {
757                     std::vector< FWCharacterData >::iterator aCharacterIter( aParagraphIter->vCharacters.begin() );
758                     std::vector< FWCharacterData >::iterator aCharacterIEnd( aParagraphIter->vCharacters.end() );
759                     while ( aCharacterIter != aCharacterIEnd )
760                     {
761                         std::vector< PolyPolygon >::iterator aOutlineIter = aCharacterIter->vOutlines.begin();
762                         std::vector< PolyPolygon >::iterator aOutlineIEnd = aCharacterIter->vOutlines.end();
763                         while( aOutlineIter != aOutlineIEnd )
764                         {
765                             PolyPolygon& rPolyPoly = *aOutlineIter;
766                             sal_uInt16 i, nPolyCount = rPolyPoly.Count();
767                             for ( i = 0; i < nPolyCount; i++ )
768                             {
769                                 // #i35928#
770                                 basegfx::B2DPolygon aCandidate(rPolyPoly[ i ].getB2DPolygon());
771 
772                                 if(aCandidate.areControlPointsUsed())
773                                 {
774                                     aCandidate = basegfx::tools::adaptiveSubdivideByAngle(aCandidate);
775                                 }
776 
777                                 // create local polygon copy to work on
778                                 Polygon aLocalPoly(aCandidate);
779 
780                                 InsertMissingOutlinePoints( rOutlinePoly, vDistances, rTextAreaBoundRect, aLocalPoly );
781                                 InsertMissingOutlinePoints( rOutlinePoly2, vDistances2, rTextAreaBoundRect, aLocalPoly );
782 
783                                 sal_uInt16 j, _nPointCount = aLocalPoly.GetSize();
784                                 for ( j = 0; j < _nPointCount; j++ )
785                                 {
786                                     Point& rPoint = aLocalPoly[ j ];
787                                     rPoint.X() -= nLeft;
788                                     rPoint.Y() -= nTop;
789                                     double fX = (double)rPoint.X() / (double)nWidth;
790                                     double fY = (double)rPoint.Y() / (double)nHeight;
791 
792                                     double fx1, fy1, fx2, fy2;
793                                     GetPoint( rOutlinePoly, vDistances, fX, fx1, fy1 );
794                                     GetPoint( rOutlinePoly2, vDistances2, fX, fx2, fy2 );
795                                     double fWidth = fx2 - fx1;
796                                     double fHeight= fy2 - fy1;
797                                     rPoint.X() = (sal_Int32)( fx1 + fWidth * fY );
798                                     rPoint.Y() = (sal_Int32)( fy1 + fHeight* fY );
799                                 }
800 
801                                 // write back polygon
802                                 rPolyPoly[ i ] = aLocalPoly;
803                             }
804                             aOutlineIter++;
805                         }
806                         aCharacterIter++;
807                     }
808                     aParagraphIter++;
809                 }
810             }
811         }
812         aTextAreaIter++;
813     }
814 }
815 
816 SdrObject* CreateSdrObjectFromParagraphOutlines( const FWData& rFWData, const SdrObject* pCustomShape )
817 {
818     SdrObject* pRet = NULL;
819     if ( !rFWData.vTextAreas.empty() )
820     {
821         pRet = new SdrObjGroup();
822 // SJ: not setting model, so we save a lot of broadcasting and the model is not modified any longer
823 //      pRet->SetModel( pCustomShape->GetModel() );
824         std::vector< FWTextArea >::const_iterator aTextAreaIter = rFWData.vTextAreas.begin();
825         std::vector< FWTextArea >::const_iterator aTextAreaIEnd = rFWData.vTextAreas.end();
826         while ( aTextAreaIter != aTextAreaIEnd )
827         {
828             std::vector< FWParagraphData >::const_iterator aParagraphIter = aTextAreaIter->vParagraphs.begin();
829             std::vector< FWParagraphData >::const_iterator aParagraphIEnd = aTextAreaIter->vParagraphs.end();
830             while ( aParagraphIter != aParagraphIEnd )
831             {
832                 std::vector< FWCharacterData >::const_iterator aCharacterIter( aParagraphIter->vCharacters.begin() );
833                 std::vector< FWCharacterData >::const_iterator aCharacterIEnd( aParagraphIter->vCharacters.end() );
834                 while ( aCharacterIter != aCharacterIEnd )
835                 {
836                     std::vector< PolyPolygon >::const_iterator aOutlineIter = aCharacterIter->vOutlines.begin();
837                     std::vector< PolyPolygon >::const_iterator aOutlineIEnd = aCharacterIter->vOutlines.end();
838                     while( aOutlineIter != aOutlineIEnd )
839                     {
840                         SdrObject* pPathObj = new SdrPathObj( OBJ_POLY, aOutlineIter->getB2DPolyPolygon() );
841     // SJ: not setting model, so we save a lot of broadcasting and the model is not modified any longer
842     //                  pPathObj->SetModel( pCustomShape->GetModel() );
843                         ((SdrObjGroup*)pRet)->GetSubList()->NbcInsertObject( pPathObj );
844                         aOutlineIter++;
845                     }
846                     aCharacterIter++;
847                 }
848                 aParagraphIter++;
849             }
850             aTextAreaIter++;
851         }
852 
853         Point aP( pCustomShape->GetSnapRect().Center() );
854         Size aS( pCustomShape->GetLogicRect().GetSize() );
855         aP.X() -= aS.Width() / 2;
856         aP.Y() -= aS.Height() / 2;
857         Rectangle aLogicRect( aP, aS );
858 
859         SfxItemSet aSet( pCustomShape->GetMergedItemSet() );
860         aSet.ClearItem( SDRATTR_TEXTDIRECTION );    //SJ: vertical writing is not required, by removing this item no outliner is created
861         aSet.Put(SdrShadowItem(sal_False)); // #i37011# NO shadow for FontWork geometry
862         pRet->SetMergedItemSet( aSet );             // * otherwise we would crash, because the outliner tries to create a Paraobject, but there is no model
863     }
864     return pRet;
865 }
866 
867 ::com::sun::star::uno::Reference < ::com::sun::star::i18n::XBreakIterator > EnhancedCustomShapeFontWork::mxBreakIterator = 0;
868 
869 Reference < i18n::XBreakIterator > EnhancedCustomShapeFontWork::GetBreakIterator()
870 {
871     if ( !mxBreakIterator.is() )
872     {
873         Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
874         Reference < XInterface > xI = xMSF->createInstance( rtl::OUString::createFromAscii( "com.sun.star.i18n.BreakIterator" ) );
875         if ( xI.is() )
876         {
877             Any x = xI->queryInterface( ::getCppuType((const Reference< i18n::XBreakIterator >*)0) );
878             x >>= mxBreakIterator;
879         }
880     }
881     return mxBreakIterator;
882 }
883 
884 SdrObject* EnhancedCustomShapeFontWork::CreateFontWork( const SdrObject* pShape2d, const SdrObject* pCustomShape )
885 {
886     SdrObject* pRet = NULL;
887 
888     Rectangle aLogicRect( pCustomShape->GetLogicRect() );
889     PolyPolygon aOutlines2d( GetOutlinesFromShape2d( pShape2d ) );
890     sal_uInt16 nOutlinesCount2d = aOutlines2d.Count();
891     if ( nOutlinesCount2d )
892     {
893         FWData aFWData;
894         if ( InitializeFontWorkData( pCustomShape, nOutlinesCount2d, aFWData ) )
895         {
896             /* retrieves the horizontal scaling factor that has to be used
897             to fit each paragraph text into its corresponding 2d outline */
898             CalculateHorizontalScalingFactor( pCustomShape, aFWData, aOutlines2d );
899 
900             /* retrieving the Outlines for the each Paragraph. */
901 
902             GetFontWorkOutline( aFWData, pCustomShape );
903 
904             FitTextOutlinesToShapeOutlines( aOutlines2d, aFWData );
905 
906             pRet = CreateSdrObjectFromParagraphOutlines( aFWData, pCustomShape );
907         }
908     }
909     return pRet;
910 }
911