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