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