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