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