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