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