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