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 88 : struct FWCharacterData // representing a single character
60 : {
61 : std::vector< PolyPolygon > vOutlines;
62 : Rectangle aBoundRect;
63 : };
64 168 : struct FWParagraphData // representing a single paragraph
65 : {
66 : OUString aString;
67 : std::vector< FWCharacterData > vCharacters;
68 : Rectangle aBoundRect;
69 : sal_Int16 nFrameDirection;
70 : };
71 56 : struct FWTextArea // representing multiple concluding paragraphs
72 : {
73 : std::vector< FWParagraphData > vParagraphs;
74 : Rectangle aBoundRect;
75 : };
76 28 : 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 14 : static bool InitializeFontWorkData( const SdrObject* pCustomShape, const sal_uInt16 nOutlinesCount2d, FWData& rFWData )
87 : {
88 14 : bool bNoErr = false;
89 14 : bool bSingleLineMode = false;
90 14 : sal_uInt16 nTextAreaCount = nOutlinesCount2d;
91 14 : if ( nOutlinesCount2d & 1 )
92 0 : bSingleLineMode = true;
93 : else
94 14 : nTextAreaCount >>= 1;
95 :
96 14 : if ( nTextAreaCount )
97 : {
98 14 : rFWData.bSingleLineMode = bSingleLineMode;
99 :
100 : // setting the strings
101 14 : OutlinerParaObject* pParaObj = ((SdrObjCustomShape*)pCustomShape)->GetOutlinerParaObject();
102 14 : if ( pParaObj )
103 : {
104 14 : const EditTextObject& rTextObj = pParaObj->GetTextObject();
105 14 : sal_Int32 nParagraphsLeft = rTextObj.GetParagraphCount();
106 :
107 14 : rFWData.nMaxParagraphsPerTextArea = ( ( nParagraphsLeft - 1 ) / nTextAreaCount ) + 1;
108 14 : sal_Int32 j = 0;
109 42 : while( nParagraphsLeft && nTextAreaCount )
110 : {
111 14 : FWTextArea aTextArea;
112 14 : sal_Int32 i, nParagraphs = ( ( nParagraphsLeft - 1 ) / nTextAreaCount ) + 1;
113 38 : for ( i = 0; i < nParagraphs; ++i, ++j )
114 : {
115 24 : FWParagraphData aParagraphData;
116 24 : aParagraphData.aString = rTextObj.GetText( j );
117 :
118 24 : const SfxItemSet& rParaSet = rTextObj.GetParaAttribs( j ); // retrieving some paragraph attributes
119 24 : aParagraphData.nFrameDirection = ((SvxFrameDirectionItem&)rParaSet.Get( EE_PARA_WRITINGDIR )).GetValue();
120 24 : aTextArea.vParagraphs.push_back( aParagraphData );
121 24 : }
122 14 : rFWData.vTextAreas.push_back( aTextArea );
123 14 : nParagraphsLeft -= nParagraphs;
124 14 : nTextAreaCount--;
125 14 : }
126 14 : bNoErr = true;
127 : }
128 : }
129 14 : return bNoErr;
130 : }
131 :
132 28 : double GetLength( const Polygon& rPolygon )
133 : {
134 28 : double fLength = 0;
135 28 : if ( rPolygon.GetSize() > 1 )
136 : {
137 28 : sal_uInt16 nCount = rPolygon.GetSize();
138 156 : while( --nCount )
139 100 : fLength += ((Polygon&)rPolygon).CalcDistance( nCount, nCount - 1 );
140 : }
141 28 : 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 14 : void CalculateHorizontalScalingFactor( const SdrObject* pCustomShape,
148 : FWData& rFWData, const PolyPolygon& rOutline2d )
149 : {
150 14 : double fScalingFactor = 1.0;
151 14 : bool bScalingFactorDefined = false;
152 :
153 14 : sal_uInt16 i = 0;
154 14 : bool bSingleLineMode = false;
155 14 : sal_uInt16 nOutlinesCount2d = rOutline2d.Count();
156 :
157 14 : Font aFont;
158 14 : SvxFontItem& rFontItem = (SvxFontItem&)pCustomShape->GetMergedItem( EE_CHAR_FONTINFO );
159 14 : aFont.SetHeight( pCustomShape->GetLogicRect().GetHeight() / rFWData.nMaxParagraphsPerTextArea );
160 14 : aFont.SetAlign( ALIGN_TOP );
161 14 : aFont.SetName( rFontItem.GetFamilyName() );
162 14 : aFont.SetFamily( rFontItem.GetFamily() );
163 14 : aFont.SetStyleName( rFontItem.GetStyleName() );
164 14 : aFont.SetOrientation( 0 );
165 : // initializing virtual device
166 :
167 28 : VirtualDevice aVirDev( 1 );
168 14 : aVirDev.SetMapMode( MAP_100TH_MM );
169 14 : aVirDev.SetFont( aFont );
170 :
171 14 : if ( nOutlinesCount2d & 1 )
172 0 : bSingleLineMode = true;
173 :
174 14 : std::vector< FWTextArea >::iterator aTextAreaIter = rFWData.vTextAreas.begin();
175 14 : std::vector< FWTextArea >::iterator aTextAreaIEnd = rFWData.vTextAreas.end();
176 42 : while( aTextAreaIter != aTextAreaIEnd )
177 : {
178 : // calculating the width of the corresponding 2d text area
179 14 : double fWidth = GetLength( rOutline2d.GetObject( i++ ) );
180 14 : if ( !bSingleLineMode )
181 : {
182 14 : fWidth += GetLength( rOutline2d.GetObject( i++ ) );
183 14 : fWidth /= 2.0;
184 : }
185 14 : std::vector< FWParagraphData >::const_iterator aParagraphIter( aTextAreaIter->vParagraphs.begin() );
186 14 : std::vector< FWParagraphData >::const_iterator aParagraphIEnd( aTextAreaIter->vParagraphs.end() );
187 52 : while( aParagraphIter != aParagraphIEnd )
188 : {
189 24 : double fTextWidth = aVirDev.GetTextWidth( aParagraphIter->aString );
190 24 : if ( fTextWidth > 0.0 )
191 : {
192 22 : double fScale = fWidth / fTextWidth;
193 22 : if ( !bScalingFactorDefined )
194 : {
195 14 : fScalingFactor = fScale;
196 14 : bScalingFactorDefined = true;
197 : }
198 : else
199 : {
200 8 : if ( fScale < fScalingFactor )
201 4 : fScalingFactor = fScale;
202 : }
203 : }
204 24 : ++aParagraphIter;
205 : }
206 14 : ++aTextAreaIter;
207 : }
208 28 : rFWData.fHorizontalTextScaling = fScalingFactor;
209 14 : }
210 :
211 14 : void GetTextAreaOutline( const FWData& rFWData, const SdrObject* pCustomShape, FWTextArea& rTextArea, sal_Bool bSameLetterHeights )
212 : {
213 14 : sal_Bool bIsVertical = ((SdrObjCustomShape*)pCustomShape)->IsVerticalWriting();
214 14 : sal_Int32 nVerticalOffset = rFWData.nMaxParagraphsPerTextArea > rTextArea.vParagraphs.size()
215 14 : ? rFWData.nSingleLineHeight / 2 : 0;
216 :
217 14 : std::vector< FWParagraphData >::iterator aParagraphIter( rTextArea.vParagraphs.begin() );
218 14 : std::vector< FWParagraphData >::iterator aParagraphIEnd( rTextArea.vParagraphs.end() );
219 52 : while( aParagraphIter != aParagraphIEnd )
220 : {
221 24 : const OUString& rText = aParagraphIter->aString;
222 24 : if ( !rText.isEmpty() )
223 : {
224 : // generating vcl/font
225 22 : sal_uInt16 nScriptType = i18n::ScriptType::LATIN;
226 22 : Reference< i18n::XBreakIterator > xBI( EnhancedCustomShapeFontWork::GetBreakIterator() );
227 22 : if ( xBI.is() )
228 : {
229 22 : nScriptType = xBI->getScriptType( rText, 0 );
230 22 : 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 22 : sal_uInt16 nFntItm = EE_CHAR_FONTINFO;
240 22 : if ( nScriptType == i18n::ScriptType::COMPLEX )
241 0 : nFntItm = EE_CHAR_FONTINFO_CTL;
242 22 : else if ( nScriptType == i18n::ScriptType::ASIAN )
243 0 : nFntItm = EE_CHAR_FONTINFO_CJK;
244 22 : SvxFontItem& rFontItem = (SvxFontItem&)pCustomShape->GetMergedItem( nFntItm );
245 44 : Font aFont;
246 22 : aFont.SetHeight( rFWData.nSingleLineHeight );
247 22 : aFont.SetAlign( ALIGN_TOP );
248 :
249 22 : aFont.SetName( rFontItem.GetFamilyName() );
250 22 : aFont.SetFamily( rFontItem.GetFamily() );
251 22 : aFont.SetStyleName( rFontItem.GetStyleName() );
252 22 : aFont.SetOrientation( 0 );
253 :
254 22 : SvxPostureItem& rPostureItem = (SvxPostureItem&)pCustomShape->GetMergedItem( EE_CHAR_ITALIC );
255 22 : aFont.SetItalic( rPostureItem.GetPosture() );
256 :
257 22 : SvxWeightItem& rWeightItem = (SvxWeightItem&)pCustomShape->GetMergedItem( EE_CHAR_WEIGHT );
258 22 : aFont.SetWeight( rWeightItem.GetWeight() );
259 :
260 : // initializing virtual device
261 44 : VirtualDevice aVirDev( 1 );
262 22 : aVirDev.SetMapMode( MAP_100TH_MM );
263 22 : aVirDev.SetFont( aFont );
264 22 : aVirDev.EnableRTL( true );
265 22 : if ( aParagraphIter->nFrameDirection == FRMDIR_HORI_RIGHT_TOP )
266 0 : aVirDev.SetLayoutMode( TEXT_LAYOUT_BIDI_RTL );
267 :
268 22 : SvxCharScaleWidthItem& rCharScaleWidthItem = (SvxCharScaleWidthItem&)pCustomShape->GetMergedItem( EE_CHAR_FONTWIDTH );
269 22 : sal_uInt16 nCharScaleWidth = rCharScaleWidthItem.GetValue();
270 22 : sal_Int32* pDXArry = NULL;
271 22 : sal_Int32 nWidth = 0;
272 :
273 : // VERTICAL
274 22 : 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 22 : 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 22 : FWCharacterData aCharacterData;
342 22 : if ( aVirDev.GetTextOutlines( aCharacterData.vOutlines, rText, 0, 0, -1, true, nWidth, pDXArry ) )
343 : {
344 22 : aParagraphIter->vCharacters.push_back( aCharacterData );
345 22 : }
346 : }
347 22 : delete[] pDXArry;
348 :
349 : // veritcal alignment
350 22 : std::vector< FWCharacterData >::iterator aCharacterIter( aParagraphIter->vCharacters.begin() );
351 22 : std::vector< FWCharacterData >::iterator aCharacterIEnd ( aParagraphIter->vCharacters.end() );
352 66 : while ( aCharacterIter != aCharacterIEnd )
353 : {
354 22 : std::vector< PolyPolygon >::iterator aOutlineIter( aCharacterIter->vOutlines.begin() );
355 22 : std::vector< PolyPolygon >::iterator aOutlineIEnd( aCharacterIter->vOutlines.end() );
356 278 : while( aOutlineIter != aOutlineIEnd )
357 : {
358 :
359 234 : PolyPolygon& rPolyPoly = *aOutlineIter++;
360 :
361 234 : if ( nVerticalOffset )
362 108 : rPolyPoly.Move( 0, nVerticalOffset );
363 :
364 : // retrieving the boundrect for the paragraph
365 234 : Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
366 234 : aParagraphIter->aBoundRect.Union( aBoundRect );
367 : }
368 22 : ++aCharacterIter;
369 22 : }
370 : }
371 : // updating the boundrect for the text area by merging the current paragraph boundrect
372 24 : if ( aParagraphIter->aBoundRect.IsEmpty() )
373 : {
374 2 : if ( rTextArea.aBoundRect.IsEmpty() )
375 0 : rTextArea.aBoundRect = Rectangle( Point( 0, 0 ), Size( 1, rFWData.nSingleLineHeight ) );
376 : else
377 2 : rTextArea.aBoundRect.Bottom() += rFWData.nSingleLineHeight;
378 : }
379 : else
380 : {
381 22 : Rectangle& rParagraphBoundRect = aParagraphIter->aBoundRect;
382 22 : rTextArea.aBoundRect.Union( rParagraphBoundRect );
383 :
384 22 : 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 24 : if ( bIsVertical )
408 0 : nVerticalOffset -= rFWData.nSingleLineHeight;
409 : else
410 24 : nVerticalOffset += rFWData.nSingleLineHeight;
411 24 : ++aParagraphIter;
412 : }
413 14 : }
414 :
415 14 : void GetFontWorkOutline( FWData& rFWData, const SdrObject* pCustomShape )
416 : {
417 14 : SdrTextHorzAdjust eHorzAdjust( ((SdrTextHorzAdjustItem&)pCustomShape->GetMergedItem( SDRATTR_TEXT_HORZADJUST )).GetValue() );
418 14 : SdrFitToSizeType eFTS( ((SdrTextFitToSizeTypeItem&)pCustomShape->GetMergedItem( SDRATTR_TEXT_FITTOSIZE )).GetValue() );
419 :
420 14 : std::vector< FWTextArea >::iterator aTextAreaIter = rFWData.vTextAreas.begin();
421 14 : std::vector< FWTextArea >::iterator aTextAreaIEnd = rFWData.vTextAreas.end();
422 :
423 14 : rFWData.nSingleLineHeight = (sal_Int32)( ( (double)pCustomShape->GetLogicRect().GetHeight()
424 14 : / rFWData.nMaxParagraphsPerTextArea ) * rFWData.fHorizontalTextScaling );
425 :
426 14 : sal_Bool bSameLetterHeights = sal_False;
427 14 : SdrCustomShapeGeometryItem& rGeometryItem = (SdrCustomShapeGeometryItem&)pCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
428 14 : const OUString sTextPath( "TextPath" );
429 28 : const OUString sSameLetterHeights( "SameLetterHeights" );
430 14 : com::sun::star::uno::Any* pAny = rGeometryItem.GetPropertyValueByName( sTextPath, sSameLetterHeights );
431 14 : if ( pAny )
432 14 : *pAny >>= bSameLetterHeights;
433 :
434 42 : while ( aTextAreaIter != aTextAreaIEnd )
435 : {
436 14 : GetTextAreaOutline( rFWData, pCustomShape, *aTextAreaIter, bSameLetterHeights );
437 14 : 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 14 : 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 14 : case SDRTEXTHORZADJUST_BLOCK : break; // don't know
503 0 : case SDRTEXTHORZADJUST_LEFT : break; // already left aligned -> nothing to do
504 : }
505 : }
506 14 : ++aTextAreaIter;
507 14 : }
508 14 : }
509 :
510 14 : basegfx::B2DPolyPolygon GetOutlinesFromShape2d( const SdrObject* pShape2d )
511 : {
512 14 : basegfx::B2DPolyPolygon aOutlines2d;
513 :
514 28 : SdrObjListIter aObjListIter( *pShape2d, IM_DEEPWITHGROUPS );
515 56 : while( aObjListIter.IsMore() )
516 : {
517 28 : SdrObject* pPartObj = aObjListIter.Next();
518 28 : if ( pPartObj->ISA( SdrPathObj ) )
519 : {
520 28 : basegfx::B2DPolyPolygon aCandidate(((SdrPathObj*)pPartObj)->GetPathPoly());
521 28 : if(aCandidate.areControlPointsUsed())
522 : {
523 4 : aCandidate = basegfx::tools::adaptiveSubdivideByAngle(aCandidate);
524 : }
525 28 : aOutlines2d.append(aCandidate);
526 : }
527 : }
528 :
529 28 : return aOutlines2d;
530 : }
531 :
532 28 : void CalcDistances( const Polygon& rPoly, std::vector< double >& rDistances )
533 : {
534 28 : sal_uInt16 i, nCount = rPoly.GetSize();
535 28 : if ( nCount > 1 )
536 : {
537 156 : for ( i = 0; i < nCount; i++ )
538 : {
539 128 : double fDistance = i ? ((Polygon&)rPoly).CalcDistance( i, i - 1 ) : 0.0;
540 128 : rDistances.push_back( fDistance );
541 : }
542 28 : std::partial_sum( rDistances.begin(), rDistances.end(), rDistances.begin() );
543 28 : double fLength = rDistances[ rDistances.size() - 1 ];
544 28 : if ( fLength > 0.0 )
545 : {
546 28 : std::vector< double >::iterator aIter = rDistances.begin();
547 28 : std::vector< double >::iterator aEnd = rDistances.end();
548 184 : while ( aIter != aEnd )
549 128 : *aIter++ /= fLength;
550 : }
551 : }
552 28 : }
553 :
554 724 : void InsertMissingOutlinePoints( const Polygon& /*rOutlinePoly*/, const std::vector< double >& rDistances, const Rectangle& rTextAreaBoundRect, Polygon& rPoly )
555 : {
556 724 : sal_uInt16 i = 0;
557 724 : double fLastDistance = 0.0;
558 80852 : for ( i = 0; i < rPoly.GetSize(); i++ )
559 : {
560 80128 : Point& rPoint = rPoly[ i ];
561 80128 : double fDistance = (double)( rPoint.X() - rTextAreaBoundRect.Left() ) / (double)rTextAreaBoundRect.GetWidth();
562 80128 : if ( i )
563 : {
564 79404 : if ( fDistance > fLastDistance )
565 : {
566 28334 : std::vector< double >::const_iterator aIter = std::upper_bound( rDistances.begin(), rDistances.end(), fLastDistance );
567 28334 : if ( aIter != rDistances.end() && ( *aIter > fLastDistance ) && ( *aIter < fDistance ) )
568 : {
569 290 : Point& rPt0 = rPoly[ i - 1 ];
570 290 : sal_Int32 fX = rPoint.X() - rPt0.X();
571 290 : sal_Int32 fY = rPoint.Y() - rPt0.Y();
572 290 : double fd = ( 1.0 / ( fDistance - fLastDistance ) ) * ( *aIter - fLastDistance );
573 290 : rPoly.Insert( i, Point( (sal_Int32)( rPt0.X() + fX * fd ), (sal_Int32)( rPt0.Y() + fY * fd ) ) );
574 290 : fDistance = *aIter;
575 : }
576 : }
577 51070 : else if ( fDistance < fLastDistance )
578 : {
579 28190 : std::vector< double >::const_iterator aIter = std::lower_bound( rDistances.begin(), rDistances.end(), fLastDistance );
580 28190 : if ( aIter != rDistances.begin() )
581 : {
582 28190 : --aIter;
583 28190 : if ( ( *aIter > fDistance ) && ( *aIter < fLastDistance ) )
584 : {
585 290 : Point& rPt0 = rPoly[ i - 1 ];
586 290 : sal_Int32 fX = rPoint.X() - rPt0.X();
587 290 : sal_Int32 fY = rPoint.Y() - rPt0.Y();
588 290 : double fd = ( 1.0 / ( fDistance - fLastDistance ) ) * ( *aIter - fLastDistance );
589 290 : rPoly.Insert( i, Point( (sal_Int32)( rPt0.X() + fX * fd ), (sal_Int32)( rPt0.Y() + fY * fd ) ) );
590 290 : fDistance = *aIter;
591 : }
592 : }
593 : }
594 : }
595 80128 : fLastDistance = fDistance;
596 : }
597 724 : }
598 :
599 80128 : void GetPoint( const Polygon& rPoly, const std::vector< double >& rDistances, const double& fX, double& fx1, double& fy1 )
600 : {
601 80128 : fy1 = fx1 = 0.0;
602 80128 : if ( rPoly.GetSize() > 1 )
603 : {
604 80128 : std::vector< double >::const_iterator aIter = std::lower_bound( rDistances.begin(), rDistances.end(), fX );
605 80128 : sal_uInt16 nIdx = sal::static_int_cast<sal_uInt16>( std::distance( rDistances.begin(), aIter ) );
606 80128 : if ( aIter == rDistances.end() )
607 0 : nIdx--;
608 80128 : const Point& rPt = rPoly[ nIdx ];
609 80128 : fx1 = rPt.X();
610 80128 : fy1 = rPt.Y();
611 80128 : if ( nIdx && ( aIter != rDistances.end() ) && ( *aIter != fX ) )
612 : {
613 80068 : nIdx = sal::static_int_cast<sal_uInt16>( std::distance( rDistances.begin(), aIter ) );
614 80068 : double fDist0 = *( aIter - 1 );
615 80068 : double fd = ( 1.0 / ( *aIter - fDist0 ) ) * ( fX - fDist0 );
616 80068 : const Point& rPt2 = rPoly[ nIdx - 1 ];
617 80068 : double fWidth = rPt.X() - rPt2.X();
618 80068 : double fHeight= rPt.Y() - rPt2.Y();
619 80068 : fWidth *= fd;
620 80068 : fHeight*= fd;
621 80068 : fx1 = rPt2.X() + fWidth;
622 80068 : fy1 = rPt2.Y() + fHeight;
623 : }
624 : }
625 80128 : }
626 :
627 14 : void FitTextOutlinesToShapeOutlines( const PolyPolygon& aOutlines2d, FWData& rFWData )
628 : {
629 14 : std::vector< FWTextArea >::iterator aTextAreaIter = rFWData.vTextAreas.begin();
630 14 : std::vector< FWTextArea >::iterator aTextAreaIEnd = rFWData.vTextAreas.end();
631 :
632 14 : sal_uInt16 nOutline2dIdx = 0;
633 42 : while( aTextAreaIter != aTextAreaIEnd )
634 : {
635 14 : Rectangle rTextAreaBoundRect = aTextAreaIter->aBoundRect;
636 14 : sal_Int32 nLeft = rTextAreaBoundRect.Left();
637 14 : sal_Int32 nTop = rTextAreaBoundRect.Top();
638 14 : sal_Int32 nWidth = rTextAreaBoundRect.GetWidth();
639 14 : sal_Int32 nHeight= rTextAreaBoundRect.GetHeight();
640 14 : 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 14 : if ( ( nOutline2dIdx + 1 ) >= aOutlines2d.Count() )
703 0 : break;
704 14 : const Polygon& rOutlinePoly( aOutlines2d[ nOutline2dIdx++ ] );
705 14 : const Polygon& rOutlinePoly2( aOutlines2d[ nOutline2dIdx++ ] );
706 14 : const sal_uInt16 nPointCount = rOutlinePoly.GetSize();
707 14 : const sal_uInt16 nPointCount2 = rOutlinePoly2.GetSize();
708 14 : if ( ( nPointCount > 1 ) && ( nPointCount2 > 1 ) )
709 : {
710 14 : std::vector< double > vDistances;
711 14 : vDistances.reserve( nPointCount );
712 28 : std::vector< double > vDistances2;
713 14 : vDistances2.reserve( nPointCount2 );
714 14 : CalcDistances( rOutlinePoly, vDistances );
715 14 : CalcDistances( rOutlinePoly2, vDistances2 );
716 14 : std::vector< FWParagraphData >::iterator aParagraphIter = aTextAreaIter->vParagraphs.begin();
717 14 : std::vector< FWParagraphData >::iterator aParagraphIEnd = aTextAreaIter->vParagraphs.end();
718 52 : while( aParagraphIter != aParagraphIEnd )
719 : {
720 24 : std::vector< FWCharacterData >::iterator aCharacterIter( aParagraphIter->vCharacters.begin() );
721 24 : std::vector< FWCharacterData >::iterator aCharacterIEnd( aParagraphIter->vCharacters.end() );
722 70 : while ( aCharacterIter != aCharacterIEnd )
723 : {
724 22 : std::vector< PolyPolygon >::iterator aOutlineIter = aCharacterIter->vOutlines.begin();
725 22 : std::vector< PolyPolygon >::iterator aOutlineIEnd = aCharacterIter->vOutlines.end();
726 278 : while( aOutlineIter != aOutlineIEnd )
727 : {
728 234 : PolyPolygon& rPolyPoly = *aOutlineIter;
729 234 : sal_uInt16 i, nPolyCount = rPolyPoly.Count();
730 596 : for ( i = 0; i < nPolyCount; i++ )
731 : {
732 : // #i35928#
733 362 : basegfx::B2DPolygon aCandidate(rPolyPoly[ i ].getB2DPolygon());
734 :
735 362 : if(aCandidate.areControlPointsUsed())
736 : {
737 307 : aCandidate = basegfx::tools::adaptiveSubdivideByAngle(aCandidate);
738 : }
739 :
740 : // create local polygon copy to work on
741 724 : Polygon aLocalPoly(aCandidate);
742 :
743 362 : InsertMissingOutlinePoints( rOutlinePoly, vDistances, rTextAreaBoundRect, aLocalPoly );
744 362 : InsertMissingOutlinePoints( rOutlinePoly2, vDistances2, rTextAreaBoundRect, aLocalPoly );
745 :
746 362 : sal_uInt16 j, _nPointCount = aLocalPoly.GetSize();
747 40426 : for ( j = 0; j < _nPointCount; j++ )
748 : {
749 40064 : Point& rPoint = aLocalPoly[ j ];
750 40064 : rPoint.X() -= nLeft;
751 40064 : rPoint.Y() -= nTop;
752 40064 : double fX = (double)rPoint.X() / (double)nWidth;
753 40064 : double fY = (double)rPoint.Y() / (double)nHeight;
754 :
755 : double fx1, fy1, fx2, fy2;
756 40064 : GetPoint( rOutlinePoly, vDistances, fX, fx1, fy1 );
757 40064 : GetPoint( rOutlinePoly2, vDistances2, fX, fx2, fy2 );
758 40064 : double fWidth = fx2 - fx1;
759 40064 : double fHeight= fy2 - fy1;
760 40064 : rPoint.X() = (sal_Int32)( fx1 + fWidth * fY );
761 40064 : rPoint.Y() = (sal_Int32)( fy1 + fHeight* fY );
762 : }
763 :
764 : // write back polygon
765 362 : rPolyPoly[ i ] = aLocalPoly;
766 362 : }
767 234 : ++aOutlineIter;
768 : }
769 22 : ++aCharacterIter;
770 : }
771 24 : ++aParagraphIter;
772 14 : }
773 : }
774 : }
775 14 : ++aTextAreaIter;
776 : }
777 14 : }
778 :
779 14 : SdrObject* CreateSdrObjectFromParagraphOutlines( const FWData& rFWData, const SdrObject* pCustomShape )
780 : {
781 14 : SdrObject* pRet = NULL;
782 14 : basegfx::B2DPolyPolygon aPolyPoly;
783 14 : if ( !rFWData.vTextAreas.empty() )
784 : {
785 14 : std::vector< FWTextArea >::const_iterator aTextAreaIter = rFWData.vTextAreas.begin();
786 14 : std::vector< FWTextArea >::const_iterator aTextAreaIEnd = rFWData.vTextAreas.end();
787 42 : while ( aTextAreaIter != aTextAreaIEnd )
788 : {
789 14 : std::vector< FWParagraphData >::const_iterator aParagraphIter = aTextAreaIter->vParagraphs.begin();
790 14 : std::vector< FWParagraphData >::const_iterator aParagraphIEnd = aTextAreaIter->vParagraphs.end();
791 52 : while ( aParagraphIter != aParagraphIEnd )
792 : {
793 24 : std::vector< FWCharacterData >::const_iterator aCharacterIter( aParagraphIter->vCharacters.begin() );
794 24 : std::vector< FWCharacterData >::const_iterator aCharacterIEnd( aParagraphIter->vCharacters.end() );
795 70 : while ( aCharacterIter != aCharacterIEnd )
796 : {
797 22 : std::vector< PolyPolygon >::const_iterator aOutlineIter = aCharacterIter->vOutlines.begin();
798 22 : std::vector< PolyPolygon >::const_iterator aOutlineIEnd = aCharacterIter->vOutlines.end();
799 278 : while( aOutlineIter != aOutlineIEnd )
800 : {
801 234 : aPolyPoly.append( aOutlineIter->getB2DPolyPolygon() );
802 234 : ++aOutlineIter;
803 : }
804 22 : ++aCharacterIter;
805 : }
806 24 : ++aParagraphIter;
807 : }
808 14 : ++aTextAreaIter;
809 : }
810 :
811 14 : pRet = new SdrPathObj( OBJ_POLY, aPolyPoly );
812 :
813 14 : SfxItemSet aSet( pCustomShape->GetMergedItemSet() );
814 14 : aSet.ClearItem( SDRATTR_TEXTDIRECTION ); //SJ: vertical writing is not required, by removing this item no outliner is created
815 14 : aSet.Put(SdrShadowItem(false)); // #i37011# NO shadow for FontWork geometry
816 14 : pRet->SetMergedItemSet( aSet ); // * otherwise we would crash, because the outliner tries to create a Paraobject, but there is no model
817 : }
818 14 : return pRet;
819 : }
820 :
821 85 : Reference < i18n::XBreakIterator > EnhancedCustomShapeFontWork::mxBreakIterator = 0;
822 :
823 22 : Reference < i18n::XBreakIterator > EnhancedCustomShapeFontWork::GetBreakIterator()
824 : {
825 22 : if ( !mxBreakIterator.is() )
826 : {
827 2 : Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
828 2 : mxBreakIterator = i18n::BreakIterator::create(xContext);
829 : }
830 22 : return mxBreakIterator;
831 : }
832 :
833 14 : SdrObject* EnhancedCustomShapeFontWork::CreateFontWork( const SdrObject* pShape2d, const SdrObject* pCustomShape )
834 : {
835 14 : SdrObject* pRet = NULL;
836 :
837 14 : PolyPolygon aOutlines2d( GetOutlinesFromShape2d( pShape2d ) );
838 14 : sal_uInt16 nOutlinesCount2d = aOutlines2d.Count();
839 14 : if ( nOutlinesCount2d )
840 : {
841 14 : FWData aFWData;
842 14 : 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 14 : CalculateHorizontalScalingFactor( pCustomShape, aFWData, aOutlines2d );
847 :
848 : /* retrieving the Outlines for the each Paragraph. */
849 :
850 14 : GetFontWorkOutline( aFWData, pCustomShape );
851 :
852 14 : FitTextOutlinesToShapeOutlines( aOutlines2d, aFWData );
853 :
854 14 : pRet = CreateSdrObjectFromParagraphOutlines( aFWData, pCustomShape );
855 14 : }
856 : }
857 14 : return pRet;
858 255 : }
859 :
860 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|