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