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