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_Int64 nParagraphsLeft = rTextObj.GetParagraphCount();
119
120 rFWData.nMaxParagraphsPerTextArea = ( ( nParagraphsLeft - 1 ) / nTextAreaCount ) + 1;
121 sal_uInt32 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