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