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 31 #include <svx/svdotext.hxx> 32 #include <svx/svdoutl.hxx> 33 #include <basegfx/vector/b2dvector.hxx> 34 #include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx> 35 #include <basegfx/range/b2drange.hxx> 36 #include <vcl/salbtype.hxx> 37 #include <svl/itemset.hxx> 38 #include <basegfx/polygon/b2dpolygontools.hxx> 39 #include <basegfx/polygon/b2dpolygon.hxx> 40 #include <algorithm> 41 #include <svx/xtextit.hxx> 42 #include <svx/xftshtit.hxx> 43 #include <vcl/virdev.hxx> 44 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 45 #include <com/sun/star/i18n/ScriptType.hdl> 46 #include <com/sun/star/i18n/XBreakIterator.hpp> 47 #include <comphelper/processfactory.hxx> 48 #include <com/sun/star/i18n/CharacterIteratorMode.hdl> 49 #include <editeng/unolingu.hxx> 50 #include <drawinglayer/primitive2d/textlayoutdevice.hxx> 51 #include <drawinglayer/primitive2d/textprimitive2d.hxx> 52 #include <basegfx/color/bcolor.hxx> 53 54 ////////////////////////////////////////////////////////////////////////////// 55 // primitive decomposition helpers 56 57 #include <basegfx/polygon/b2dlinegeometry.hxx> 58 #include <drawinglayer/attribute/strokeattribute.hxx> 59 #include <svx/xlnclit.hxx> 60 #include <svx/xlntrit.hxx> 61 #include <svx/xlnwtit.hxx> 62 #include <svx/xlinjoit.hxx> 63 #include <svx/xlndsit.hxx> 64 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx> 65 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> 66 #include <editeng/editstat.hxx> 67 #include <svx/unoapi.hxx> 68 #include <drawinglayer/geometry/viewinformation2d.hxx> 69 #include <svx/sdr/attribute/sdrformtextoutlineattribute.hxx> 70 71 ////////////////////////////////////////////////////////////////////////////// 72 73 using namespace ::com::sun::star::uno; 74 using namespace ::com::sun::star::lang; 75 using namespace ::com::sun::star::i18n; 76 77 ////////////////////////////////////////////////////////////////////////////// 78 // PathTextPortion helper 79 80 namespace 81 { 82 class impPathTextPortion 83 { 84 basegfx::B2DVector maOffset; 85 String maText; 86 xub_StrLen mnTextStart; 87 xub_StrLen mnTextLength; 88 sal_uInt16 mnParagraph; 89 xub_StrLen mnIndex; 90 SvxFont maFont; 91 ::std::vector< double > maDblDXArray; // double DXArray, font size independent -> unit coordinate system 92 ::com::sun::star::lang::Locale maLocale; 93 94 // bitfield 95 unsigned mbRTL : 1; 96 97 public: 98 impPathTextPortion(DrawPortionInfo& rInfo) 99 : maOffset(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y()), 100 maText(rInfo.mrText), 101 mnTextStart(rInfo.mnTextStart), 102 mnTextLength(rInfo.mnTextLen), 103 mnParagraph(rInfo.mnPara), 104 mnIndex(rInfo.mnIndex), 105 maFont(rInfo.mrFont), 106 maDblDXArray(), 107 maLocale(rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale()), 108 mbRTL(rInfo.mrFont.IsVertical() ? false : rInfo.IsRTL()) 109 { 110 if(mnTextLength && rInfo.mpDXArray) 111 { 112 maDblDXArray.reserve(mnTextLength); 113 114 for(xub_StrLen a(0); a < mnTextLength; a++) 115 { 116 maDblDXArray.push_back((double)rInfo.mpDXArray[a]); 117 } 118 } 119 } 120 121 // for ::std::sort 122 bool operator<(const impPathTextPortion& rComp) const 123 { 124 if(mnParagraph < rComp.mnParagraph) 125 { 126 return true; 127 } 128 129 if(maOffset.getX() < rComp.maOffset.getX()) 130 { 131 return true; 132 } 133 134 return (maOffset.getY() < rComp.maOffset.getY()); 135 } 136 137 const basegfx::B2DVector& getOffset() const { return maOffset; } 138 const String& getText() const { return maText; } 139 xub_StrLen getTextStart() const { return mnTextStart; } 140 xub_StrLen getTextLength() const { return mnTextLength; } 141 sal_uInt16 getParagraph() const { return mnParagraph; } 142 xub_StrLen getIndex() const { return mnIndex; } 143 const SvxFont& getFont() const { return maFont; } 144 bool isRTL() const { return mbRTL; } 145 const ::std::vector< double >& getDoubleDXArray() const { return maDblDXArray; } 146 const ::com::sun::star::lang::Locale& getLocale() const { return maLocale; } 147 148 xub_StrLen getPortionIndex(xub_StrLen nIndex, xub_StrLen nLength) const 149 { 150 if(mbRTL) 151 { 152 return (mnTextStart + (mnTextLength - (nIndex + nLength))); 153 } 154 else 155 { 156 return (mnTextStart + nIndex); 157 } 158 } 159 160 double getDisplayLength(xub_StrLen nIndex, xub_StrLen nLength) const 161 { 162 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter; 163 double fRetval(0.0); 164 165 if(maFont.IsVertical()) 166 { 167 fRetval = aTextLayouter.getTextHeight() * (double)nLength; 168 } 169 else 170 { 171 fRetval = aTextLayouter.getTextWidth(maText, getPortionIndex(nIndex, nLength), nLength); 172 } 173 174 return fRetval; 175 } 176 }; 177 } // end of anonymous namespace 178 179 ////////////////////////////////////////////////////////////////////////////// 180 // TextBreakup helper 181 182 namespace 183 { 184 class impTextBreakupHandler 185 { 186 SdrOutliner& mrOutliner; 187 ::std::vector< impPathTextPortion > maPathTextPortions; 188 189 DECL_LINK(decompositionPathTextPrimitive, DrawPortionInfo* ); 190 191 public: 192 impTextBreakupHandler(SdrOutliner& rOutliner) 193 : mrOutliner(rOutliner) 194 { 195 } 196 197 const ::std::vector< impPathTextPortion >& decompositionPathTextPrimitive() 198 { 199 // strip portions to maPathTextPortions 200 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decompositionPathTextPrimitive)); 201 mrOutliner.StripPortions(); 202 203 if(!maPathTextPortions.empty()) 204 { 205 // sort portions by paragraph, x and y 206 ::std::sort(maPathTextPortions.begin(), maPathTextPortions.end()); 207 } 208 209 return maPathTextPortions; 210 } 211 }; 212 213 IMPL_LINK(impTextBreakupHandler, decompositionPathTextPrimitive, DrawPortionInfo*, pInfo) 214 { 215 maPathTextPortions.push_back(impPathTextPortion(*pInfo)); 216 return 0; 217 } 218 } // end of anonymous namespace 219 220 ////////////////////////////////////////////////////////////////////////////// 221 // TextBreakup one poly and one paragraph helper 222 223 namespace 224 { 225 class impPolygonParagraphHandler 226 { 227 const drawinglayer::attribute::SdrFormTextAttribute maSdrFormTextAttribute; // FormText parameters 228 std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& mrDecomposition; // destination primitive list 229 std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& mrShadowDecomposition; // destination primitive list for shadow 230 Reference < com::sun::star::i18n::XBreakIterator > mxBreak; // break iterator 231 232 double getParagraphTextLength(const ::std::vector< const impPathTextPortion* >& rTextPortions) 233 { 234 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter; 235 double fRetval(0.0); 236 237 for(sal_uInt32 a(0L); a < rTextPortions.size(); a++) 238 { 239 const impPathTextPortion* pCandidate = rTextPortions[a]; 240 241 if(pCandidate && pCandidate->getTextLength()) 242 { 243 aTextLayouter.setFont(pCandidate->getFont()); 244 fRetval += pCandidate->getDisplayLength(0L, pCandidate->getTextLength()); 245 } 246 } 247 248 return fRetval; 249 } 250 251 xub_StrLen getNextGlyphLen(const impPathTextPortion* pCandidate, xub_StrLen nPosition, const ::com::sun::star::lang::Locale& rFontLocale) 252 { 253 xub_StrLen nNextGlyphLen(1); 254 255 if(mxBreak.is()) 256 { 257 sal_Int32 nDone(0L); 258 nNextGlyphLen = (xub_StrLen)mxBreak->nextCharacters(pCandidate->getText(), nPosition, 259 rFontLocale, CharacterIteratorMode::SKIPCELL, 1, nDone) - nPosition; 260 } 261 262 return nNextGlyphLen; 263 } 264 265 public: 266 impPolygonParagraphHandler( 267 const drawinglayer::attribute::SdrFormTextAttribute& rSdrFormTextAttribute, 268 std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rDecomposition, 269 std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rShadowDecomposition) 270 : maSdrFormTextAttribute(rSdrFormTextAttribute), 271 mrDecomposition(rDecomposition), 272 mrShadowDecomposition(rShadowDecomposition) 273 { 274 // prepare BreakIterator 275 Reference < XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory(); 276 Reference < XInterface > xInterface = xMSF->createInstance(::rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")); 277 278 if(xInterface.is()) 279 { 280 Any x = xInterface->queryInterface(::getCppuType((const Reference< XBreakIterator >*)0)); 281 x >>= mxBreak; 282 } 283 } 284 285 void HandlePair(const basegfx::B2DPolygon rPolygonCandidate, const ::std::vector< const impPathTextPortion* >& rTextPortions) 286 { 287 // prepare polygon geometry, take into account as many parameters as possible 288 basegfx::B2DPolygon aPolygonCandidate(rPolygonCandidate); 289 const double fPolyLength(basegfx::tools::getLength(aPolygonCandidate)); 290 double fPolyEnd(fPolyLength); 291 double fPolyStart(0.0); 292 double fAutosizeScaleFactor(1.0); 293 bool bAutosizeScale(false); 294 295 if(maSdrFormTextAttribute.getFormTextMirror()) 296 { 297 aPolygonCandidate.flip(); 298 } 299 300 if(maSdrFormTextAttribute.getFormTextStart() 301 && (XFT_LEFT == maSdrFormTextAttribute.getFormTextAdjust() 302 || XFT_RIGHT == maSdrFormTextAttribute.getFormTextAdjust())) 303 { 304 if(XFT_LEFT == maSdrFormTextAttribute.getFormTextAdjust()) 305 { 306 fPolyStart += maSdrFormTextAttribute.getFormTextStart(); 307 308 if(fPolyStart > fPolyEnd) 309 { 310 fPolyStart = fPolyEnd; 311 } 312 } 313 else 314 { 315 fPolyEnd -= maSdrFormTextAttribute.getFormTextStart(); 316 317 if(fPolyEnd < fPolyStart) 318 { 319 fPolyEnd = fPolyStart; 320 } 321 } 322 } 323 324 if(XFT_LEFT != maSdrFormTextAttribute.getFormTextAdjust()) 325 { 326 // calculate total text length of this paragraph, some layout needs to be done 327 const double fParagraphTextLength(getParagraphTextLength(rTextPortions)); 328 329 // check if text is too long for paragraph. If yes, handle as if left aligned (default), 330 // but still take care of XFT_AUTOSIZE in that case 331 const bool bTextTooLong(fParagraphTextLength > (fPolyEnd - fPolyStart)); 332 333 if(XFT_RIGHT == maSdrFormTextAttribute.getFormTextAdjust()) 334 { 335 if(!bTextTooLong) 336 { 337 // if right aligned, add difference to polygon start 338 fPolyStart += ((fPolyEnd - fPolyStart) - fParagraphTextLength); 339 } 340 } 341 else if(XFT_CENTER == maSdrFormTextAttribute.getFormTextAdjust()) 342 { 343 if(!bTextTooLong) 344 { 345 // if centered, add half of difference to polygon start 346 fPolyStart += ((fPolyEnd - fPolyStart) - fParagraphTextLength) / 2.0; 347 } 348 } 349 else if(XFT_AUTOSIZE == maSdrFormTextAttribute.getFormTextAdjust()) 350 { 351 // if scale, prepare scale factor between curve length and text length 352 if(0.0 != fParagraphTextLength) 353 { 354 fAutosizeScaleFactor = (fPolyEnd - fPolyStart) / fParagraphTextLength; 355 bAutosizeScale = true; 356 } 357 } 358 } 359 360 // handle text portions for this paragraph 361 for(sal_uInt32 a(0L); a < rTextPortions.size() && fPolyStart < fPolyEnd; a++) 362 { 363 const impPathTextPortion* pCandidate = rTextPortions[a]; 364 basegfx::B2DVector aFontScaling; 365 const drawinglayer::attribute::FontAttribute aCandidateFontAttribute( 366 drawinglayer::primitive2d::getFontAttributeFromVclFont( 367 aFontScaling, 368 pCandidate->getFont(), 369 pCandidate->isRTL(), 370 false)); 371 372 if(pCandidate && pCandidate->getTextLength()) 373 { 374 drawinglayer::primitive2d::TextLayouterDevice aTextLayouter; 375 aTextLayouter.setFont(pCandidate->getFont()); 376 xub_StrLen nUsedTextLength(0); 377 378 while(nUsedTextLength < pCandidate->getTextLength() && fPolyStart < fPolyEnd) 379 { 380 xub_StrLen nNextGlyphLen(getNextGlyphLen(pCandidate, pCandidate->getTextStart() + nUsedTextLength, pCandidate->getLocale())); 381 382 // prepare portion length. Takes RTL sections into account. 383 double fPortionLength(pCandidate->getDisplayLength(nUsedTextLength, nNextGlyphLen)); 384 385 if(bAutosizeScale) 386 { 387 // when autosize scaling, expand portion length 388 fPortionLength *= fAutosizeScaleFactor; 389 } 390 391 // create transformation 392 basegfx::B2DHomMatrix aNewTransformA, aNewTransformB, aNewShadowTransform; 393 basegfx::B2DPoint aStartPos(basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart, fPolyLength)); 394 basegfx::B2DPoint aEndPos(aStartPos); 395 396 // add font scaling 397 aNewTransformA.scale(aFontScaling.getX(), aFontScaling.getY()); 398 399 // prepare scaling of text primitive 400 if(bAutosizeScale) 401 { 402 // when autosize scaling, expand text primitive scaling to it 403 aNewTransformA.scale(fAutosizeScaleFactor, fAutosizeScaleFactor); 404 } 405 406 // eventually create shadow primitives from aDecomposition and add to rDecomposition 407 const bool bShadow(XFTSHADOW_NONE != maSdrFormTextAttribute.getFormTextShadow()); 408 409 if(bShadow) 410 { 411 if(XFTSHADOW_NORMAL == maSdrFormTextAttribute.getFormTextShadow()) 412 { 413 aNewShadowTransform.translate( 414 maSdrFormTextAttribute.getFormTextShdwXVal(), 415 -maSdrFormTextAttribute.getFormTextShdwYVal()); 416 } 417 else // XFTSHADOW_SLANT 418 { 419 double fScaleValue(maSdrFormTextAttribute.getFormTextShdwYVal() / 100.0); 420 double fShearValue(-maSdrFormTextAttribute.getFormTextShdwXVal() * F_PI1800); 421 422 aNewShadowTransform.scale(1.0, fScaleValue); 423 aNewShadowTransform.shearX(sin(fShearValue)); 424 aNewShadowTransform.scale(1.0, cos(fShearValue)); 425 } 426 } 427 428 switch(maSdrFormTextAttribute.getFormTextStyle()) 429 { 430 case XFT_ROTATE : 431 { 432 aEndPos = basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength); 433 const basegfx::B2DVector aDirection(aEndPos - aStartPos); 434 aNewTransformB.rotate(atan2(aDirection.getY(), aDirection.getX())); 435 aNewTransformB.translate(aStartPos.getX(), aStartPos.getY()); 436 437 break; 438 } 439 case XFT_UPRIGHT : 440 { 441 aNewTransformB.translate(aStartPos.getX() - (fPortionLength / 2.0), aStartPos.getY()); 442 443 break; 444 } 445 case XFT_SLANTX : 446 { 447 aEndPos = basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength); 448 const basegfx::B2DVector aDirection(aEndPos - aStartPos); 449 const double fShearValue(atan2(aDirection.getY(), aDirection.getX())); 450 const double fSin(sin(fShearValue)); 451 const double fCos(cos(fShearValue)); 452 453 aNewTransformB.shearX(-fSin); 454 455 // Scale may lead to objects without height since fCos == 0.0 is possible. 456 // Renderers need to handle that, it's not a forbidden value and does not 457 // need to be avoided 458 aNewTransformB.scale(1.0, fCos); 459 aNewTransformB.translate(aStartPos.getX() - (fPortionLength / 2.0), aStartPos.getY()); 460 461 break; 462 } 463 case XFT_SLANTY : 464 { 465 aEndPos = basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength); 466 const basegfx::B2DVector aDirection(aEndPos - aStartPos); 467 const double fShearValue(atan2(aDirection.getY(), aDirection.getX())); 468 const double fCos(cos(fShearValue)); 469 const double fTan(tan(fShearValue)); 470 471 // shear to 'stand' on the curve 472 aNewTransformB.shearY(fTan); 473 474 // scale in X to make as tight as needed. As with XFT_SLANT_X, this may 475 // lead to primitives without width which the renderers will handle 476 aNewTransformA.scale(fCos, 1.0); 477 478 aNewTransformB.translate(aStartPos.getX(), aStartPos.getY()); 479 480 break; 481 } 482 default : break; // XFT_NONE 483 } 484 485 // distance from path? 486 if(maSdrFormTextAttribute.getFormTextDistance()) 487 { 488 if(aEndPos.equal(aStartPos)) 489 { 490 aEndPos = basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength); 491 } 492 493 // use back vector (aStartPos - aEndPos) here to get mirrored perpendicular as in old stuff 494 const basegfx::B2DVector aPerpendicular( 495 basegfx::getNormalizedPerpendicular(aStartPos - aEndPos) * 496 maSdrFormTextAttribute.getFormTextDistance()); 497 aNewTransformB.translate(aPerpendicular.getX(), aPerpendicular.getY()); 498 } 499 500 if(pCandidate->getText().Len() && nNextGlyphLen) 501 { 502 const xub_StrLen nPortionIndex(pCandidate->getPortionIndex(nUsedTextLength, nNextGlyphLen)); 503 ::std::vector< double > aNewDXArray; 504 505 if(nNextGlyphLen > 1 && pCandidate->getDoubleDXArray().size()) 506 { 507 // copy DXArray for portion 508 aNewDXArray.insert( 509 aNewDXArray.begin(), 510 pCandidate->getDoubleDXArray().begin() + nPortionIndex, 511 pCandidate->getDoubleDXArray().begin() + (nPortionIndex + nNextGlyphLen)); 512 513 if(nPortionIndex > 0) 514 { 515 // adapt to portion start 516 double fDXOffset= *(pCandidate->getDoubleDXArray().begin() + (nPortionIndex - 1)); 517 ::std::transform( 518 aNewDXArray.begin(), aNewDXArray.end(), 519 aNewDXArray.begin(), ::std::bind2nd(::std::minus<double>(), fDXOffset)); 520 } 521 522 if(bAutosizeScale) 523 { 524 // when autosize scaling, adapt to DXArray, too 525 ::std::transform( 526 aNewDXArray.begin(), aNewDXArray.end(), 527 aNewDXArray.begin(), ::std::bind2nd(::std::multiplies<double>(), fAutosizeScaleFactor)); 528 } 529 } 530 531 if(bShadow) 532 { 533 // shadow primitive creation 534 const Color aShadowColor(maSdrFormTextAttribute.getFormTextShdwColor()); 535 const basegfx::BColor aRGBShadowColor(aShadowColor.getBColor()); 536 537 drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pNew = 538 new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( 539 aNewTransformB * aNewShadowTransform * aNewTransformA, 540 pCandidate->getText(), 541 nPortionIndex, 542 nNextGlyphLen, 543 aNewDXArray, 544 aCandidateFontAttribute, 545 pCandidate->getLocale(), 546 aRGBShadowColor); 547 548 mrShadowDecomposition.push_back(pNew); 549 } 550 551 { 552 // primitive creation 553 const Color aColor(pCandidate->getFont().GetColor()); 554 const basegfx::BColor aRGBColor(aColor.getBColor()); 555 556 drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pNew = 557 new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( 558 aNewTransformB * aNewTransformA, 559 pCandidate->getText(), 560 nPortionIndex, 561 nNextGlyphLen, 562 aNewDXArray, 563 aCandidateFontAttribute, 564 pCandidate->getLocale(), 565 aRGBColor); 566 567 mrDecomposition.push_back(pNew); 568 } 569 } 570 571 // consume from portion // no += here, xub_StrLen is sal_uInt16 and the compiler will gererate a warning here 572 nUsedTextLength = nUsedTextLength + nNextGlyphLen; 573 574 // consume from polygon 575 fPolyStart += fPortionLength; 576 } 577 } 578 } 579 } 580 }; 581 } // end of anonymous namespace 582 583 ////////////////////////////////////////////////////////////////////////////// 584 // primitive decomposition helpers 585 586 namespace 587 { 588 void impAddPolygonStrokePrimitives( 589 const basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector, 590 const basegfx::B2DHomMatrix& rTransform, 591 const drawinglayer::attribute::LineAttribute& rLineAttribute, 592 const drawinglayer::attribute::StrokeAttribute& rStrokeAttribute, 593 std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rTarget) 594 { 595 for(basegfx::B2DPolyPolygonVector::const_iterator aPolygon(rB2DPolyPolyVector.begin()); aPolygon != rB2DPolyPolyVector.end(); aPolygon++) 596 { 597 // prepare PolyPolygons 598 basegfx::B2DPolyPolygon aB2DPolyPolygon = *aPolygon; 599 aB2DPolyPolygon.transform(rTransform); 600 601 for(sal_uInt32 a(0L); a < aB2DPolyPolygon.count(); a++) 602 { 603 // create one primitive per polygon 604 drawinglayer::primitive2d::PolygonStrokePrimitive2D* pNew = 605 new drawinglayer::primitive2d::PolygonStrokePrimitive2D( 606 aB2DPolyPolygon.getB2DPolygon(a), rLineAttribute, rStrokeAttribute); 607 rTarget.push_back(pNew); 608 } 609 } 610 } 611 612 drawinglayer::primitive2d::Primitive2DSequence impAddPathTextOutlines( 613 const std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rSource, 614 const drawinglayer::attribute::SdrFormTextOutlineAttribute& rOutlineAttribute) 615 { 616 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aNewPrimitives; 617 618 for(sal_uInt32 a(0L); a < rSource.size(); a++) 619 { 620 const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pTextCandidate = dynamic_cast< const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* >(rSource[a]); 621 622 if(pTextCandidate) 623 { 624 basegfx::B2DPolyPolygonVector aB2DPolyPolyVector; 625 basegfx::B2DHomMatrix aPolygonTransform; 626 627 // get text outlines and their object transformation 628 pTextCandidate->getTextOutlinesAndTransformation(aB2DPolyPolyVector, aPolygonTransform); 629 630 if(!aB2DPolyPolyVector.empty()) 631 { 632 // create stroke primitives 633 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aStrokePrimitives; 634 impAddPolygonStrokePrimitives( 635 aB2DPolyPolyVector, 636 aPolygonTransform, 637 rOutlineAttribute.getLineAttribute(), 638 rOutlineAttribute.getStrokeAttribute(), 639 aStrokePrimitives); 640 const sal_uInt32 nStrokeCount(aStrokePrimitives.size()); 641 642 if(nStrokeCount) 643 { 644 if(rOutlineAttribute.getTransparence()) 645 { 646 // create UnifiedTransparencePrimitive2D 647 drawinglayer::primitive2d::Primitive2DSequence aStrokePrimitiveSequence(nStrokeCount); 648 649 for(sal_uInt32 b(0L); b < nStrokeCount; b++) 650 { 651 aStrokePrimitiveSequence[b] = drawinglayer::primitive2d::Primitive2DReference(aStrokePrimitives[b]); 652 } 653 654 drawinglayer::primitive2d::UnifiedTransparencePrimitive2D* pNew2 = 655 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( 656 aStrokePrimitiveSequence, 657 (double)rOutlineAttribute.getTransparence() / 100.0); 658 aNewPrimitives.push_back(pNew2); 659 } 660 else 661 { 662 // add polygons to rDecomposition as polygonStrokePrimitives 663 aNewPrimitives.insert(aNewPrimitives.end(), aStrokePrimitives.begin(), aStrokePrimitives.end()); 664 } 665 } 666 } 667 } 668 } 669 670 const sal_uInt32 nNewCount(aNewPrimitives.size()); 671 672 if(nNewCount) 673 { 674 drawinglayer::primitive2d::Primitive2DSequence aRetval(nNewCount); 675 676 for(sal_uInt32 a(0L); a < nNewCount; a++) 677 { 678 aRetval[a] = drawinglayer::primitive2d::Primitive2DReference(aNewPrimitives[a]); 679 } 680 681 return aRetval; 682 } 683 else 684 { 685 return drawinglayer::primitive2d::Primitive2DSequence(); 686 } 687 } 688 } // end of anonymous namespace 689 690 ////////////////////////////////////////////////////////////////////////////// 691 // primitive decomposition 692 693 void SdrTextObj::impDecomposePathTextPrimitive( 694 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 695 const drawinglayer::primitive2d::SdrPathTextPrimitive2D& rSdrPathTextPrimitive, 696 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const 697 { 698 drawinglayer::primitive2d::Primitive2DSequence aRetvalA; 699 drawinglayer::primitive2d::Primitive2DSequence aRetvalB; 700 701 // prepare outliner 702 SdrOutliner& rOutliner = ImpGetDrawOutliner(); 703 rOutliner.SetUpdateMode(true); 704 rOutliner.Clear(); 705 rOutliner.SetPaperSize(Size(LONG_MAX,LONG_MAX)); 706 rOutliner.SetText(rSdrPathTextPrimitive.getOutlinerParaObject()); 707 708 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition 709 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage())); 710 711 // now break up to text portions 712 impTextBreakupHandler aConverter(rOutliner); 713 const ::std::vector< impPathTextPortion > rPathTextPortions = aConverter.decompositionPathTextPrimitive(); 714 715 if(!rPathTextPortions.empty()) 716 { 717 // get FormText and polygon values 718 const drawinglayer::attribute::SdrFormTextAttribute& rFormTextAttribute = rSdrPathTextPrimitive.getSdrFormTextAttribute(); 719 const basegfx::B2DPolyPolygon& rPathPolyPolygon(rSdrPathTextPrimitive.getPathPolyPolygon()); 720 721 // get loop count 722 sal_uInt32 nLoopCount(rPathPolyPolygon.count()); 723 724 if(rOutliner.GetParagraphCount() < nLoopCount) 725 { 726 nLoopCount = rOutliner.GetParagraphCount(); 727 } 728 729 if(nLoopCount) 730 { 731 // prepare common decomposition stuff 732 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aRegularDecomposition; 733 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aShadowDecomposition; 734 impPolygonParagraphHandler aPolygonParagraphHandler( 735 rFormTextAttribute, 736 aRegularDecomposition, 737 aShadowDecomposition); 738 sal_uInt32 a; 739 740 for(a = 0L; a < nLoopCount; a++) 741 { 742 // filter text portions for this paragraph 743 ::std::vector< const impPathTextPortion* > aParagraphTextPortions; 744 745 for(sal_uInt32 b(0L); b < rPathTextPortions.size(); b++) 746 { 747 const impPathTextPortion& rCandidate = rPathTextPortions[b]; 748 749 if(rCandidate.getParagraph() == a) 750 { 751 aParagraphTextPortions.push_back(&rCandidate); 752 } 753 } 754 755 // handle data pair polygon/ParagraphTextPortions 756 if(!aParagraphTextPortions.empty()) 757 { 758 aPolygonParagraphHandler.HandlePair(rPathPolyPolygon.getB2DPolygon(a), aParagraphTextPortions); 759 } 760 } 761 762 const sal_uInt32 nShadowCount(aShadowDecomposition.size()); 763 const sal_uInt32 nRegularCount(aRegularDecomposition.size()); 764 765 if(nShadowCount) 766 { 767 // add shadow primitives to decomposition 768 aRetvalA.realloc(nShadowCount); 769 770 for(a = 0L; a < nShadowCount; a++) 771 { 772 aRetvalA[a] = drawinglayer::primitive2d::Primitive2DReference(aShadowDecomposition[a]); 773 } 774 775 // evtl. add shadow outlines 776 if(rFormTextAttribute.getFormTextOutline() 777 && !rFormTextAttribute.getShadowOutline().isDefault()) 778 { 779 const drawinglayer::primitive2d::Primitive2DSequence aOutlines( 780 impAddPathTextOutlines( 781 aShadowDecomposition, 782 rFormTextAttribute.getShadowOutline())); 783 784 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(aRetvalA, aOutlines); 785 } 786 } 787 788 if(nRegularCount) 789 { 790 // add normal primitives to decomposition 791 aRetvalB.realloc(nRegularCount); 792 793 for(a = 0L; a < nRegularCount; a++) 794 { 795 aRetvalB[a] = drawinglayer::primitive2d::Primitive2DReference(aRegularDecomposition[a]); 796 } 797 798 // evtl. add outlines 799 if(rFormTextAttribute.getFormTextOutline() 800 && !rFormTextAttribute.getOutline().isDefault()) 801 { 802 const drawinglayer::primitive2d::Primitive2DSequence aOutlines( 803 impAddPathTextOutlines( 804 aRegularDecomposition, 805 rFormTextAttribute.getOutline())); 806 807 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(aRetvalB, aOutlines); 808 } 809 } 810 } 811 } 812 813 // cleanup outliner 814 rOutliner.SetDrawPortionHdl(Link()); 815 rOutliner.Clear(); 816 rOutliner.setVisualizedPage(0); 817 818 // concatenate all results 819 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aRetvalA); 820 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aRetvalB); 821 } 822 823 ////////////////////////////////////////////////////////////////////////////// 824 // eof 825