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 <drawinglayer/primitive2d/textprimitive2d.hxx> 36 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> 37 #include <basegfx/range/b2drange.hxx> 38 #include <editeng/editstat.hxx> 39 #include <vcl/salbtype.hxx> 40 #include <svx/sdtfchim.hxx> 41 #include <svl/itemset.hxx> 42 #include <basegfx/polygon/b2dpolygontools.hxx> 43 #include <basegfx/polygon/b2dpolygon.hxx> 44 #include <drawinglayer/animation/animationtiming.hxx> 45 #include <basegfx/color/bcolor.hxx> 46 #include <vcl/svapp.hxx> 47 #include <editeng/eeitemid.hxx> 48 #include <editeng/escpitem.hxx> 49 #include <editeng/svxenum.hxx> 50 #include <editeng/flditem.hxx> 51 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx> 52 #include <vcl/metaact.hxx> 53 #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx> 54 #include <drawinglayer/primitive2d/graphicprimitive2d.hxx> 55 #include <drawinglayer/primitive2d/textlayoutdevice.hxx> 56 #include <svx/unoapi.hxx> 57 #include <drawinglayer/geometry/viewinformation2d.hxx> 58 #include <editeng/outlobj.hxx> 59 #include <basegfx/matrix/b2dhommatrixtools.hxx> 60 61 ////////////////////////////////////////////////////////////////////////////// 62 // helpers 63 64 namespace 65 { 66 drawinglayer::primitive2d::Primitive2DSequence impConvertVectorToPrimitive2DSequence(const std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rPrimitiveVector) 67 { 68 const sal_Int32 nCount(rPrimitiveVector.size()); 69 drawinglayer::primitive2d::Primitive2DSequence aRetval(nCount); 70 71 for(sal_Int32 a(0L); a < nCount; a++) 72 { 73 aRetval[a] = drawinglayer::primitive2d::Primitive2DReference(rPrimitiveVector[a]); 74 } 75 76 return aRetval; 77 } 78 79 class impTextBreakupHandler 80 { 81 private: 82 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maTextPortionPrimitives; 83 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maLinePrimitives; 84 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maParagraphPrimitives; 85 86 SdrOutliner& mrOutliner; 87 basegfx::B2DHomMatrix maNewTransformA; 88 basegfx::B2DHomMatrix maNewTransformB; 89 90 // the visible area for contour text decomposition 91 basegfx::B2DVector maScale; 92 93 // #SJ# ClipRange for BlockText decomposition; only text portions completely 94 // inside are to be accepted, so this is different from geometric clipping 95 // (which would allow e.g. upper parts of portions to remain). Only used for 96 // BlockText (see there) 97 basegfx::B2DRange maClipRange; 98 99 DECL_LINK(decomposeContourTextPrimitive, DrawPortionInfo* ); 100 DECL_LINK(decomposeBlockTextPrimitive, DrawPortionInfo* ); 101 DECL_LINK(decomposeStretchTextPrimitive, DrawPortionInfo* ); 102 103 DECL_LINK(decomposeContourBulletPrimitive, DrawBulletInfo* ); 104 DECL_LINK(decomposeBlockBulletPrimitive, DrawBulletInfo* ); 105 DECL_LINK(decomposeStretchBulletPrimitive, DrawBulletInfo* ); 106 107 bool impIsUnderlineAbove(const Font& rFont) const; 108 void impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo); 109 drawinglayer::primitive2d::BasePrimitive2D* impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) const; 110 void impFlushTextPortionPrimitivesToLinePrimitives(); 111 void impFlushLinePrimitivesToParagraphPrimitives(); 112 void impHandleDrawPortionInfo(const DrawPortionInfo& rInfo); 113 void impHandleDrawBulletInfo(const DrawBulletInfo& rInfo); 114 115 public: 116 impTextBreakupHandler(SdrOutliner& rOutliner) 117 : maTextPortionPrimitives(), 118 maLinePrimitives(), 119 maParagraphPrimitives(), 120 mrOutliner(rOutliner), 121 maNewTransformA(), 122 maNewTransformB(), 123 maScale(), 124 maClipRange() 125 { 126 } 127 128 void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, const basegfx::B2DVector& rScale) 129 { 130 maScale = rScale; 131 maNewTransformA = rNewTransformA; 132 maNewTransformB = rNewTransformB; 133 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeContourTextPrimitive)); 134 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeContourBulletPrimitive)); 135 mrOutliner.StripPortions(); 136 mrOutliner.SetDrawPortionHdl(Link()); 137 mrOutliner.SetDrawBulletHdl(Link()); 138 } 139 140 void decomposeBlockTextPrimitive( 141 const basegfx::B2DHomMatrix& rNewTransformA, 142 const basegfx::B2DHomMatrix& rNewTransformB, 143 const basegfx::B2DRange& rClipRange) 144 { 145 maNewTransformA = rNewTransformA; 146 maNewTransformB = rNewTransformB; 147 maClipRange = rClipRange; 148 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeBlockTextPrimitive)); 149 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeBlockBulletPrimitive)); 150 mrOutliner.StripPortions(); 151 mrOutliner.SetDrawPortionHdl(Link()); 152 mrOutliner.SetDrawBulletHdl(Link()); 153 } 154 155 void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB) 156 { 157 maNewTransformA = rNewTransformA; 158 maNewTransformB = rNewTransformB; 159 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeStretchTextPrimitive)); 160 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeStretchBulletPrimitive)); 161 mrOutliner.StripPortions(); 162 mrOutliner.SetDrawPortionHdl(Link()); 163 mrOutliner.SetDrawBulletHdl(Link()); 164 } 165 166 drawinglayer::primitive2d::Primitive2DSequence getPrimitive2DSequence(); 167 }; 168 169 bool impTextBreakupHandler::impIsUnderlineAbove(const Font& rFont) const 170 { 171 if(!rFont.IsVertical()) 172 { 173 return false; 174 } 175 176 if((LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage())) 177 { 178 // the underline is right for Japanese only 179 return true; 180 } 181 182 return false; 183 } 184 185 void impTextBreakupHandler::impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo) 186 { 187 if(rInfo.mrText.Len() && rInfo.mnTextLen) 188 { 189 basegfx::B2DVector aFontScaling; 190 drawinglayer::attribute::FontAttribute aFontAttribute( 191 drawinglayer::primitive2d::getFontAttributeFromVclFont( 192 aFontScaling, 193 rInfo.mrFont, 194 rInfo.IsRTL(), 195 false)); 196 basegfx::B2DHomMatrix aNewTransform; 197 198 // add font scale to new transform 199 aNewTransform.scale(aFontScaling.getX(), aFontScaling.getY()); 200 201 // look for proportional font scaling, evtl scale accordingly 202 if(100 != rInfo.mrFont.GetPropr()) 203 { 204 const double fFactor(rInfo.mrFont.GetPropr() / 100.0); 205 aNewTransform.scale(fFactor, fFactor); 206 } 207 208 // apply font rotate 209 if(rInfo.mrFont.GetOrientation()) 210 { 211 aNewTransform.rotate(-rInfo.mrFont.GetOrientation() * F_PI1800); 212 } 213 214 // look for escapement, evtl translate accordingly 215 if(rInfo.mrFont.GetEscapement()) 216 { 217 sal_Int16 nEsc(rInfo.mrFont.GetEscapement()); 218 219 if(DFLT_ESC_AUTO_SUPER == nEsc) 220 { 221 nEsc = 33; 222 } 223 else if(DFLT_ESC_AUTO_SUB == nEsc) 224 { 225 nEsc = -20; 226 } 227 228 if(nEsc > 100) 229 { 230 nEsc = 100; 231 } 232 else if(nEsc < -100) 233 { 234 nEsc = -100; 235 } 236 237 const double fEscapement(nEsc / -100.0); 238 aNewTransform.translate(0.0, fEscapement * aFontScaling.getY()); 239 } 240 241 // apply transformA 242 aNewTransform *= maNewTransformA; 243 244 // apply local offset 245 aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y()); 246 247 // also apply embedding object's transform 248 aNewTransform *= maNewTransformB; 249 250 // prepare DXArray content. To make it independent from font size (and such from 251 // the text transformation), scale it to unit coordinates 252 ::std::vector< double > aDXArray; 253 static bool bDisableTextArray(false); 254 255 if(!bDisableTextArray && rInfo.mpDXArray && rInfo.mnTextLen) 256 { 257 aDXArray.reserve(rInfo.mnTextLen); 258 259 for(xub_StrLen a(0); a < rInfo.mnTextLen; a++) 260 { 261 aDXArray.push_back((double)rInfo.mpDXArray[a]); 262 } 263 } 264 265 // create complex text primitive and append 266 const Color aFontColor(rInfo.mrFont.GetColor()); 267 const basegfx::BColor aBFontColor(aFontColor.getBColor()); 268 269 // prepare wordLineMode (for underline and strikeout) 270 // NOT for bullet texts. It is set (this may be an error by itself), but needs to be suppressed to hinder e.g. '1)' 271 // to be splitted which would not look like the original 272 const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && !rInfo.mbEndOfBullet); 273 274 // prepare new primitive 275 drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = 0; 276 const bool bDecoratedIsNeeded( 277 UNDERLINE_NONE != rInfo.mrFont.GetOverline() 278 || UNDERLINE_NONE != rInfo.mrFont.GetUnderline() 279 || STRIKEOUT_NONE != rInfo.mrFont.GetStrikeout() 280 || EMPHASISMARK_NONE != (rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE) 281 || RELIEF_NONE != rInfo.mrFont.GetRelief() 282 || rInfo.mrFont.IsShadow() 283 || bWordLineMode); 284 285 if(bDecoratedIsNeeded) 286 { 287 // TextDecoratedPortionPrimitive2D needed, prepare some more data 288 // get overline and underline color. If it's on automatic (0xffffffff) use FontColor instead 289 const Color aUnderlineColor(rInfo.maTextLineColor); 290 const basegfx::BColor aBUnderlineColor((0xffffffff == aUnderlineColor.GetColor()) ? aBFontColor : aUnderlineColor.getBColor()); 291 const Color aOverlineColor(rInfo.maOverlineColor); 292 const basegfx::BColor aBOverlineColor((0xffffffff == aOverlineColor.GetColor()) ? aBFontColor : aOverlineColor.getBColor()); 293 294 // prepare overline and underline data 295 const drawinglayer::primitive2d::TextLine eFontOverline( 296 drawinglayer::primitive2d::mapFontUnderlineToTextLine(rInfo.mrFont.GetOverline())); 297 const drawinglayer::primitive2d::TextLine eFontUnderline( 298 drawinglayer::primitive2d::mapFontUnderlineToTextLine(rInfo.mrFont.GetUnderline())); 299 300 // check UndelineAbove 301 const bool bUnderlineAbove( 302 drawinglayer::primitive2d::TEXT_LINE_NONE != eFontUnderline && impIsUnderlineAbove(rInfo.mrFont)); 303 304 // prepare strikeout data 305 const drawinglayer::primitive2d::TextStrikeout eTextStrikeout( 306 drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rInfo.mrFont.GetStrikeout())); 307 308 // prepare emphasis mark data 309 drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE); 310 311 switch(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE) 312 { 313 case EMPHASISMARK_DOT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DOT; break; 314 case EMPHASISMARK_CIRCLE : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_CIRCLE; break; 315 case EMPHASISMARK_DISC : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DISC; break; 316 case EMPHASISMARK_ACCENT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_ACCENT; break; 317 } 318 319 const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_ABOVE); 320 const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_BELOW); 321 322 // prepare font relief data 323 drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE); 324 325 switch(rInfo.mrFont.GetRelief()) 326 { 327 case RELIEF_EMBOSSED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break; 328 case RELIEF_ENGRAVED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break; 329 default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE 330 } 331 332 // prepare shadow/outline data 333 const bool bShadow(rInfo.mrFont.IsShadow()); 334 335 // TextDecoratedPortionPrimitive2D is needed, create one 336 pNewPrimitive = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( 337 338 // attributes for TextSimplePortionPrimitive2D 339 aNewTransform, 340 rInfo.mrText, 341 rInfo.mnTextStart, 342 rInfo.mnTextLen, 343 aDXArray, 344 aFontAttribute, 345 rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(), 346 aBFontColor, 347 348 // attributes for TextDecoratedPortionPrimitive2D 349 aBOverlineColor, 350 aBUnderlineColor, 351 eFontOverline, 352 eFontUnderline, 353 bUnderlineAbove, 354 eTextStrikeout, 355 bWordLineMode, 356 eTextEmphasisMark, 357 bEmphasisMarkAbove, 358 bEmphasisMarkBelow, 359 eTextRelief, 360 bShadow); 361 } 362 else 363 { 364 // TextSimplePortionPrimitive2D is enough 365 pNewPrimitive = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( 366 aNewTransform, 367 rInfo.mrText, 368 rInfo.mnTextStart, 369 rInfo.mnTextLen, 370 aDXArray, 371 aFontAttribute, 372 rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(), 373 aBFontColor); 374 } 375 376 if(rInfo.mbEndOfBullet) 377 { 378 // embed in TextHierarchyBulletPrimitive2D 379 const drawinglayer::primitive2d::Primitive2DReference aNewReference(pNewPrimitive); 380 const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1); 381 pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence); 382 } 383 384 if(rInfo.mpFieldData) 385 { 386 pNewPrimitive = impCheckFieldPrimitive(pNewPrimitive, rInfo); 387 } 388 389 maTextPortionPrimitives.push_back(pNewPrimitive); 390 391 // support for WrongSpellVector. Create WrongSpellPrimitives as needed 392 if(rInfo.mpWrongSpellVector && !aDXArray.empty()) 393 { 394 const sal_uInt32 nSize(rInfo.mpWrongSpellVector->size()); 395 const sal_uInt32 nDXCount(aDXArray.size()); 396 const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded 397 398 for(sal_uInt32 a(0); a < nSize; a++) 399 { 400 const EEngineData::WrongSpellClass& rCandidate = (*rInfo.mpWrongSpellVector)[a]; 401 402 if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart) 403 { 404 const sal_uInt32 nStart(rCandidate.nStart - rInfo.mnTextStart); 405 const sal_uInt32 nEnd(rCandidate.nEnd - rInfo.mnTextStart); 406 double fStart(0.0); 407 double fEnd(0.0); 408 409 if(nStart > 0 && nStart - 1 < nDXCount) 410 { 411 fStart = aDXArray[nStart - 1]; 412 } 413 414 if(nEnd > 0 && nEnd - 1 < nDXCount) 415 { 416 fEnd = aDXArray[nEnd - 1]; 417 } 418 419 if(!basegfx::fTools::equal(fStart, fEnd)) 420 { 421 if(rInfo.IsRTL()) 422 { 423 // #i98523# 424 // When the portion is RTL, mirror the redlining using the 425 // full portion width 426 const double fTextWidth(aDXArray[aDXArray.size() - 1]); 427 428 fStart = fTextWidth - fStart; 429 fEnd = fTextWidth - fEnd; 430 } 431 432 // need to take FontScaling out of values; it's already part of 433 // aNewTransform and would be double applied 434 const double fFontScaleX(aFontScaling.getX()); 435 436 if(!basegfx::fTools::equal(fFontScaleX, 1.0) 437 && !basegfx::fTools::equalZero(fFontScaleX)) 438 { 439 fStart /= fFontScaleX; 440 fEnd /= fFontScaleX; 441 } 442 443 maTextPortionPrimitives.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D( 444 aNewTransform, 445 fStart, 446 fEnd, 447 aSpellColor)); 448 } 449 } 450 } 451 } 452 } 453 } 454 455 drawinglayer::primitive2d::BasePrimitive2D* impTextBreakupHandler::impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) const 456 { 457 if(rInfo.mpFieldData) 458 { 459 // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a TextHierarchyFieldPrimitive2D 460 // which holds the field type and evtl. the URL 461 const SvxURLField* pURLField = dynamic_cast< const SvxURLField* >(rInfo.mpFieldData); 462 const SvxPageField* pPageField = dynamic_cast< const SvxPageField* >(rInfo.mpFieldData); 463 464 // embed current primitive to a sequence 465 drawinglayer::primitive2d::Primitive2DSequence aSequence; 466 467 if(pPrimitive) 468 { 469 aSequence.realloc(1); 470 aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive); 471 } 472 473 if(pURLField) 474 { 475 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_URL, pURLField->GetURL()); 476 } 477 else if(pPageField) 478 { 479 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_PAGE, String()); 480 } 481 else 482 { 483 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_COMMON, String()); 484 } 485 } 486 487 return pPrimitive; 488 } 489 490 void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives() 491 { 492 // only create a line primitive when we had content; there is no need for 493 // empty line primitives (contrary to paragraphs, see below). 494 if(!maTextPortionPrimitives.empty()) 495 { 496 drawinglayer::primitive2d::Primitive2DSequence aLineSequence(impConvertVectorToPrimitive2DSequence(maTextPortionPrimitives)); 497 maTextPortionPrimitives.clear(); 498 maLinePrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyLinePrimitive2D(aLineSequence)); 499 } 500 } 501 502 void impTextBreakupHandler::impFlushLinePrimitivesToParagraphPrimitives() 503 { 504 // ALWAYS create a paragraph primitive, even when no content was added. This is done to 505 // have the correct paragraph count even with empty paragraphs. Those paragraphs will 506 // have an empty sub-PrimitiveSequence. 507 drawinglayer::primitive2d::Primitive2DSequence aParagraphSequence(impConvertVectorToPrimitive2DSequence(maLinePrimitives)); 508 maLinePrimitives.clear(); 509 maParagraphPrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyParagraphPrimitive2D(aParagraphSequence)); 510 } 511 512 void impTextBreakupHandler::impHandleDrawPortionInfo(const DrawPortionInfo& rInfo) 513 { 514 impCreateTextPortionPrimitive(rInfo); 515 516 if(rInfo.mbEndOfLine || rInfo.mbEndOfParagraph) 517 { 518 impFlushTextPortionPrimitivesToLinePrimitives(); 519 } 520 521 if(rInfo.mbEndOfParagraph) 522 { 523 impFlushLinePrimitivesToParagraphPrimitives(); 524 } 525 } 526 527 void impTextBreakupHandler::impHandleDrawBulletInfo(const DrawBulletInfo& rInfo) 528 { 529 basegfx::B2DHomMatrix aNewTransform; 530 531 // add size to new transform 532 aNewTransform.scale(rInfo.maBulletSize.getWidth(), rInfo.maBulletSize.getHeight()); 533 534 // apply transformA 535 aNewTransform *= maNewTransformA; 536 537 // apply local offset 538 aNewTransform.translate(rInfo.maBulletPosition.X(), rInfo.maBulletPosition.Y()); 539 540 // also apply embedding object's transform 541 aNewTransform *= maNewTransformB; 542 543 // prepare empty GraphicAttr 544 const GraphicAttr aGraphicAttr; 545 546 // create GraphicPrimitive2D 547 const drawinglayer::primitive2d::Primitive2DReference aNewReference(new drawinglayer::primitive2d::GraphicPrimitive2D( 548 aNewTransform, 549 rInfo.maBulletGraphicObject, 550 aGraphicAttr)); 551 552 // embed in TextHierarchyBulletPrimitive2D 553 const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1); 554 drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence); 555 556 // add to output 557 maTextPortionPrimitives.push_back(pNewPrimitive); 558 } 559 560 IMPL_LINK(impTextBreakupHandler, decomposeContourTextPrimitive, DrawPortionInfo*, pInfo) 561 { 562 // for contour text, ignore (clip away) all portions which are below 563 // the visible area given by maScale 564 if(pInfo && (double)pInfo->mrStartPos.Y() < maScale.getY()) 565 { 566 impHandleDrawPortionInfo(*pInfo); 567 } 568 569 return 0; 570 } 571 572 IMPL_LINK(impTextBreakupHandler, decomposeBlockTextPrimitive, DrawPortionInfo*, pInfo) 573 { 574 if(pInfo) 575 { 576 // #SJ# Is clipping wanted? This is text clipping; only accept a portion 577 // if it's completely in the range 578 if(!maClipRange.isEmpty()) 579 { 580 // Test start position first; this allows to not get the text range at 581 // all if text is far outside 582 const basegfx::B2DPoint aStartPosition(pInfo->mrStartPos.X(), pInfo->mrStartPos.Y()); 583 584 if(!maClipRange.isInside(aStartPosition)) 585 { 586 return 0; 587 } 588 589 // Start position is inside. Get TextBoundRect and TopLeft next 590 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice; 591 aTextLayouterDevice.setFont(pInfo->mrFont); 592 593 const basegfx::B2DRange aTextBoundRect( 594 aTextLayouterDevice.getTextBoundRect( 595 pInfo->mrText, pInfo->mnTextStart, pInfo->mnTextLen)); 596 const basegfx::B2DPoint aTopLeft(aTextBoundRect.getMinimum() + aStartPosition); 597 598 if(!maClipRange.isInside(aTopLeft)) 599 { 600 return 0; 601 } 602 603 // TopLeft is inside. Get BottomRight and check 604 const basegfx::B2DPoint aBottomRight(aTextBoundRect.getMaximum() + aStartPosition); 605 606 if(!maClipRange.isInside(aBottomRight)) 607 { 608 return 0; 609 } 610 611 // all inside, clip was successful 612 } 613 impHandleDrawPortionInfo(*pInfo); 614 } 615 616 return 0; 617 } 618 619 IMPL_LINK(impTextBreakupHandler, decomposeStretchTextPrimitive, DrawPortionInfo*, pInfo) 620 { 621 if(pInfo) 622 { 623 impHandleDrawPortionInfo(*pInfo); 624 } 625 626 return 0; 627 } 628 629 IMPL_LINK(impTextBreakupHandler, decomposeContourBulletPrimitive, DrawBulletInfo*, pInfo) 630 { 631 if(pInfo) 632 { 633 impHandleDrawBulletInfo(*pInfo); 634 } 635 636 return 0; 637 } 638 639 IMPL_LINK(impTextBreakupHandler, decomposeBlockBulletPrimitive, DrawBulletInfo*, pInfo) 640 { 641 if(pInfo) 642 { 643 impHandleDrawBulletInfo(*pInfo); 644 } 645 646 return 0; 647 } 648 649 IMPL_LINK(impTextBreakupHandler, decomposeStretchBulletPrimitive, DrawBulletInfo*, pInfo) 650 { 651 if(pInfo) 652 { 653 impHandleDrawBulletInfo(*pInfo); 654 } 655 656 return 0; 657 } 658 659 drawinglayer::primitive2d::Primitive2DSequence impTextBreakupHandler::getPrimitive2DSequence() 660 { 661 if(!maTextPortionPrimitives.empty()) 662 { 663 // collect non-closed lines 664 impFlushTextPortionPrimitivesToLinePrimitives(); 665 } 666 667 if(!maLinePrimitives.empty()) 668 { 669 // collect non-closed paragraphs 670 impFlushLinePrimitivesToParagraphPrimitives(); 671 } 672 673 return impConvertVectorToPrimitive2DSequence(maParagraphPrimitives); 674 } 675 } // end of anonymous namespace 676 677 ////////////////////////////////////////////////////////////////////////////// 678 // primitive decompositions 679 680 void SdrTextObj::impDecomposeContourTextPrimitive( 681 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 682 const drawinglayer::primitive2d::SdrContourTextPrimitive2D& rSdrContourTextPrimitive, 683 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const 684 { 685 // decompose matrix to have position and size of text 686 basegfx::B2DVector aScale, aTranslate; 687 double fRotate, fShearX; 688 rSdrContourTextPrimitive.getObjectTransform().decompose(aScale, aTranslate, fRotate, fShearX); 689 690 // prepare contour polygon, force to non-mirrored for layouting 691 basegfx::B2DPolyPolygon aPolyPolygon(rSdrContourTextPrimitive.getUnitPolyPolygon()); 692 aPolyPolygon.transform(basegfx::tools::createScaleB2DHomMatrix(fabs(aScale.getX()), fabs(aScale.getY()))); 693 694 // prepare outliner 695 SdrOutliner& rOutliner = ImpGetDrawOutliner(); 696 const Size aNullSize; 697 rOutliner.SetPaperSize(aNullSize); 698 rOutliner.SetPolygon(aPolyPolygon); 699 rOutliner.SetUpdateMode(true); 700 rOutliner.SetText(rSdrContourTextPrimitive.getOutlinerParaObject()); 701 702 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition 703 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage())); 704 705 // prepare matrices to apply to newly created primitives 706 basegfx::B2DHomMatrix aNewTransformA; 707 708 // mirroring. We are now in the polygon sizes. When mirroring in X and Y, 709 // move the null point which was top left to bottom right. 710 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0)); 711 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0)); 712 713 // in-between the translations of the single primitives will take place. Afterwards, 714 // the object's transformations need to be applied 715 const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix( 716 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0, 717 fShearX, fRotate, aTranslate.getX(), aTranslate.getY())); 718 719 // now break up text primitives. 720 impTextBreakupHandler aConverter(rOutliner); 721 aConverter.decomposeContourTextPrimitive(aNewTransformA, aNewTransformB, aScale); 722 723 // cleanup outliner 724 rOutliner.Clear(); 725 rOutliner.setVisualizedPage(0); 726 727 rTarget = aConverter.getPrimitive2DSequence(); 728 } 729 730 void SdrTextObj::impDecomposeBlockTextPrimitive( 731 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 732 const drawinglayer::primitive2d::SdrBlockTextPrimitive2D& rSdrBlockTextPrimitive, 733 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const 734 { 735 // decompose matrix to have position and size of text 736 basegfx::B2DVector aScale, aTranslate; 737 double fRotate, fShearX; 738 rSdrBlockTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX); 739 740 // use B2DRange aAnchorTextRange for calculations 741 basegfx::B2DRange aAnchorTextRange(aTranslate); 742 aAnchorTextRange.expand(aTranslate + aScale); 743 744 // prepare outliner 745 const bool bIsCell(rSdrBlockTextPrimitive.getCellText()); 746 SdrOutliner& rOutliner = ImpGetDrawOutliner(); 747 SdrTextHorzAdjust eHAdj = rSdrBlockTextPrimitive.getSdrTextHorzAdjust(); 748 SdrTextVertAdjust eVAdj = rSdrBlockTextPrimitive.getSdrTextVertAdjust(); 749 const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord()); 750 const Size aNullSize; 751 752 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition 753 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage())); 754 rOutliner.SetFixedCellHeight(rSdrBlockTextPrimitive.isFixedCellHeight()); 755 rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_AUTOPAGESIZE); 756 rOutliner.SetMinAutoPaperSize(aNullSize); 757 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000)); 758 759 // add one to rage sizes to get back to the old Rectangle and outliner measurements 760 const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1L)); 761 const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1L)); 762 const bool bVerticalWritintg(rSdrBlockTextPrimitive.getOutlinerParaObject().IsVertical()); 763 const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight)); 764 765 if(bIsCell) 766 { 767 // cell text is formated neither like a text object nor like a object 768 // text, so use a special setup here 769 rOutliner.SetMaxAutoPaperSize(aAnchorTextSize); 770 771 // #i106214# To work with an unchangeable PaperSize (CellSize in 772 // this case) Set(Min|Max)AutoPaperSize and SetPaperSize have to be used. 773 // #i106214# This was not completely correct; to still measure the real 774 // text height to allow vertical adjust (and vice versa for VerticalWritintg) 775 // only one aspect has to be set, but the other one to zero 776 if(bVerticalWritintg) 777 { 778 // measure the horizontal text size 779 rOutliner.SetMinAutoPaperSize(Size(0, aAnchorTextSize.Height())); 780 } 781 else 782 { 783 // measure the vertical text size 784 rOutliner.SetMinAutoPaperSize(Size(aAnchorTextSize.Width(), 0)); 785 } 786 787 rOutliner.SetPaperSize(aAnchorTextSize); 788 rOutliner.SetUpdateMode(true); 789 rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject()); 790 } 791 else 792 { 793 // check if block text is used (only one of them can be true) 794 const bool bHorizontalIsBlock(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWritintg); 795 const bool bVerticalIsBlock(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWritintg); 796 797 // set minimal paper size hor/ver if needed 798 if(bHorizontalIsBlock) 799 { 800 rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0)); 801 } 802 else if(bVerticalIsBlock) 803 { 804 rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight)); 805 } 806 807 if((rSdrBlockTextPrimitive.getWordWrap() || IsTextFrame()) && !rSdrBlockTextPrimitive.getUnlimitedPage()) 808 { 809 // #i103454# maximal paper size hor/ver needs to be limited to text 810 // frame size. If it's block text, still allow the 'other' direction 811 // to grow to get a correct real text size when using GetPaperSize(). 812 // When just using aAnchorTextSize as maximum, GetPaperSize() 813 // would just return aAnchorTextSize again: this means, the wanted 814 // 'measurement' of the real size of block text would not work 815 Size aMaxAutoPaperSize(aAnchorTextSize); 816 817 if(bHorizontalIsBlock) 818 { 819 // allow to grow vertical for horizontal blocks 820 aMaxAutoPaperSize.setHeight(1000000); 821 } 822 else if(bVerticalIsBlock) 823 { 824 // allow to grow horizontal for vertical blocks 825 aMaxAutoPaperSize.setWidth(1000000); 826 } 827 828 rOutliner.SetMaxAutoPaperSize(aMaxAutoPaperSize); 829 } 830 831 rOutliner.SetPaperSize(aNullSize); 832 rOutliner.SetUpdateMode(true); 833 rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject()); 834 } 835 836 rOutliner.SetControlWord(nOriginalControlWord); 837 838 // now get back the layouted text size from outliner 839 const Size aOutlinerTextSiz(rOutliner.GetPaperSize()); 840 const basegfx::B2DVector aOutlinerScale(aOutlinerTextSiz.Width(), aOutlinerTextSiz.Height()); 841 basegfx::B2DVector aAdjustTranslate(0.0, 0.0); 842 843 // For draw objects containing text correct hor/ver alignment if text is bigger 844 // than the object itself. Without that correction, the text would always be 845 // formatted to the left edge (or top edge when vertical) of the draw object. 846 if(!IsTextFrame() && !bIsCell) 847 { 848 if(aAnchorTextRange.getWidth() < aOutlinerScale.getX() && !bVerticalWritintg) 849 { 850 // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK, 851 // else the alignment is wanted. 852 if(SDRTEXTHORZADJUST_BLOCK == eHAdj) 853 { 854 eHAdj = SDRTEXTHORZADJUST_CENTER; 855 } 856 } 857 858 if(aAnchorTextRange.getHeight() < aOutlinerScale.getY() && bVerticalWritintg) 859 { 860 // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK, 861 // else the alignment is wanted. 862 if(SDRTEXTVERTADJUST_BLOCK == eVAdj) 863 { 864 eVAdj = SDRTEXTVERTADJUST_CENTER; 865 } 866 } 867 } 868 869 // correct horizontal translation using the now known text size 870 if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj) 871 { 872 const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX()); 873 874 if(SDRTEXTHORZADJUST_CENTER == eHAdj) 875 { 876 aAdjustTranslate.setX(fFree / 2.0); 877 } 878 879 if(SDRTEXTHORZADJUST_RIGHT == eHAdj) 880 { 881 aAdjustTranslate.setX(fFree); 882 } 883 } 884 885 // correct vertical translation using the now known text size 886 if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj) 887 { 888 const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY()); 889 890 if(SDRTEXTVERTADJUST_CENTER == eVAdj) 891 { 892 aAdjustTranslate.setY(fFree / 2.0); 893 } 894 895 if(SDRTEXTVERTADJUST_BOTTOM == eVAdj) 896 { 897 aAdjustTranslate.setY(fFree); 898 } 899 } 900 901 // prepare matrices to apply to newly created primitives. aNewTransformA 902 // will get coordinates in aOutlinerScale size and positive in X, Y. 903 // Translate relative to given primitive to get same rotation and shear 904 // as the master shape we are working on. For vertical, use the top-right 905 // corner 906 const double fStartInX(bVerticalWritintg ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX()); 907 const basegfx::B2DTuple aAdjOffset(fStartInX, aAdjustTranslate.getY()); 908 basegfx::B2DHomMatrix aNewTransformA(basegfx::tools::createTranslateB2DHomMatrix(aAdjOffset.getX(), aAdjOffset.getY())); 909 910 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y, 911 // move the null point which was top left to bottom right. 912 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0)); 913 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0)); 914 915 // in-between the translations of the single primitives will take place. Afterwards, 916 // the object's transformations need to be applied 917 const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix( 918 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0, 919 fShearX, fRotate, aTranslate.getX(), aTranslate.getY())); 920 921 // #SJ# create ClipRange (if needed) 922 basegfx::B2DRange aClipRange; 923 924 if(rSdrBlockTextPrimitive.getClipOnBounds()) 925 { 926 aClipRange.expand(-aAdjOffset); 927 aClipRange.expand(basegfx::B2DTuple(aAnchorTextSize.Width(), aAnchorTextSize.Height()) - aAdjOffset); 928 } 929 930 // now break up text primitives. 931 impTextBreakupHandler aConverter(rOutliner); 932 aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange); 933 934 // cleanup outliner 935 rOutliner.Clear(); 936 rOutliner.setVisualizedPage(0); 937 938 rTarget = aConverter.getPrimitive2DSequence(); 939 } 940 941 void SdrTextObj::impDecomposeStretchTextPrimitive( 942 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 943 const drawinglayer::primitive2d::SdrStretchTextPrimitive2D& rSdrStretchTextPrimitive, 944 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const 945 { 946 // decompose matrix to have position and size of text 947 basegfx::B2DVector aScale, aTranslate; 948 double fRotate, fShearX; 949 rSdrStretchTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX); 950 951 // use non-mirrored B2DRange aAnchorTextRange for calculations 952 basegfx::B2DRange aAnchorTextRange(aTranslate); 953 aAnchorTextRange.expand(aTranslate + aScale); 954 955 // prepare outliner 956 SdrOutliner& rOutliner = ImpGetDrawOutliner(); 957 const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord()); 958 const Size aNullSize; 959 960 rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_STRETCHING|EE_CNTRL_AUTOPAGESIZE); 961 rOutliner.SetFixedCellHeight(rSdrStretchTextPrimitive.isFixedCellHeight()); 962 rOutliner.SetMinAutoPaperSize(aNullSize); 963 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000)); 964 rOutliner.SetPaperSize(aNullSize); 965 rOutliner.SetUpdateMode(true); 966 rOutliner.SetText(rSdrStretchTextPrimitive.getOutlinerParaObject()); 967 968 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition 969 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage())); 970 971 // now get back the layouted text size from outliner 972 const Size aOutlinerTextSiz(rOutliner.CalcTextSize()); 973 const basegfx::B2DVector aOutlinerScale( 974 basegfx::fTools::equalZero(aOutlinerTextSiz.Width()) ? 1.0 : aOutlinerTextSiz.Width(), 975 basegfx::fTools::equalZero(aOutlinerTextSiz.Height()) ? 1.0 : aOutlinerTextSiz.Height()); 976 977 // prepare matrices to apply to newly created primitives 978 basegfx::B2DHomMatrix aNewTransformA; 979 980 // #i101957# Check for vertical text. If used, aNewTransformA 981 // needs to translate the text initially around object width to orient 982 // it relative to the topper right instead of the topper left 983 const bool bVertical(rSdrStretchTextPrimitive.getOutlinerParaObject().IsVertical()); 984 985 if(bVertical) 986 { 987 aNewTransformA.translate(aScale.getX(), 0.0); 988 } 989 990 // calculate global char stretching scale parameters. Use non-mirrored sizes 991 // to layout without mirroring 992 const double fScaleX(fabs(aScale.getX()) / aOutlinerScale.getX()); 993 const double fScaleY(fabs(aScale.getY()) / aOutlinerScale.getY()); 994 rOutliner.SetGlobalCharStretching((sal_Int16)FRound(fScaleX * 100.0), (sal_Int16)FRound(fScaleY * 100.0)); 995 996 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y, 997 // move the null point which was top left to bottom right. 998 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0)); 999 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0)); 1000 1001 // in-between the translations of the single primitives will take place. Afterwards, 1002 // the object's transformations need to be applied 1003 const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix( 1004 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0, 1005 fShearX, fRotate, aTranslate.getX(), aTranslate.getY())); 1006 1007 // now break up text primitives. 1008 impTextBreakupHandler aConverter(rOutliner); 1009 aConverter.decomposeStretchTextPrimitive(aNewTransformA, aNewTransformB); 1010 1011 // cleanup outliner 1012 rOutliner.SetControlWord(nOriginalControlWord); 1013 rOutliner.Clear(); 1014 rOutliner.setVisualizedPage(0); 1015 1016 rTarget = aConverter.getPrimitive2DSequence(); 1017 } 1018 1019 ////////////////////////////////////////////////////////////////////////////// 1020 // timing generators 1021 #define ENDLESS_LOOP (0xffffffff) 1022 #define ENDLESS_TIME ((double)0xffffffff) 1023 #define PIXEL_DPI (96.0) 1024 1025 void SdrTextObj::impGetBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const 1026 { 1027 if(SDRTEXTANI_BLINK == GetTextAniKind()) 1028 { 1029 // get values 1030 const SfxItemSet& rSet = GetObjectItemSet(); 1031 const sal_uInt32 nRepeat((sal_uInt32)((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue()); 1032 bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue()); 1033 double fDelay((double)((SdrTextAniDelayItem&)rSet.Get(SDRATTR_TEXT_ANIDELAY)).GetValue()); 1034 1035 if(0.0 == fDelay) 1036 { 1037 // use default 1038 fDelay = 250.0; 1039 } 1040 1041 // prepare loop and add 1042 drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP); 1043 drawinglayer::animation::AnimationEntryFixed aStart(fDelay, 0.0); 1044 aLoop.append(aStart); 1045 drawinglayer::animation::AnimationEntryFixed aEnd(fDelay, 1.0); 1046 aLoop.append(aEnd); 1047 rAnimList.append(aLoop); 1048 1049 // add stopped state if loop is not endless 1050 if(0L != nRepeat) 1051 { 1052 drawinglayer::animation::AnimationEntryFixed aStop(ENDLESS_TIME, bVisisbleWhenStopped ? 0.0 : 1.0); 1053 rAnimList.append(aStop); 1054 } 1055 } 1056 } 1057 1058 void impCreateScrollTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency) 1059 { 1060 bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue()); 1061 bool bVisisbleWhenStarted(((SdrTextAniStartInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE )).GetValue()); 1062 const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue()); 1063 1064 if(bVisisbleWhenStarted) 1065 { 1066 // move from center to outside 1067 drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0); 1068 rAnimList.append(aInOut); 1069 } 1070 1071 // loop. In loop, move through 1072 if(nRepeat || 0L == nRepeat) 1073 { 1074 drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP); 1075 drawinglayer::animation::AnimationEntryLinear aThrough(fTimeFullPath, fFrequency, bForward ? 0.0 : 1.0, bForward ? 1.0 : 0.0); 1076 aLoop.append(aThrough); 1077 rAnimList.append(aLoop); 1078 } 1079 1080 if(0L != nRepeat && bVisisbleWhenStopped) 1081 { 1082 // move from outside to center 1083 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5); 1084 rAnimList.append(aOutIn); 1085 1086 // add timing for staying at the end 1087 drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5); 1088 rAnimList.append(aEnd); 1089 } 1090 } 1091 1092 void impCreateAlternateTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, double fRelativeTextLength, bool bForward, double fTimeFullPath, double fFrequency) 1093 { 1094 if(basegfx::fTools::more(fRelativeTextLength, 0.5)) 1095 { 1096 // this is the case when fTextLength > fFrameLength, text is bigger than animation frame. 1097 // In that case, correct direction 1098 bForward = !bForward; 1099 } 1100 1101 const double fStartPosition(bForward ? fRelativeTextLength : 1.0 - fRelativeTextLength); 1102 const double fEndPosition(bForward ? 1.0 - fRelativeTextLength : fRelativeTextLength); 1103 bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue()); 1104 bool bVisisbleWhenStarted(((SdrTextAniStartInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE )).GetValue()); 1105 const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue()); 1106 1107 if(!bVisisbleWhenStarted) 1108 { 1109 // move from outside to center 1110 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5); 1111 rAnimList.append(aOutIn); 1112 } 1113 1114 // loop. In loop, move out and in again. fInnerMovePath may be negative when text is bigger then frame, 1115 // so use absolute value 1116 const double fInnerMovePath(fabs(1.0 - (fRelativeTextLength * 2.0))); 1117 const double fTimeForInnerPath(fTimeFullPath * fInnerMovePath); 1118 const double fHalfInnerPath(fTimeForInnerPath * 0.5); 1119 const sal_uInt32 nDoubleRepeat(nRepeat / 2L); 1120 1121 if(nDoubleRepeat || 0L == nRepeat) 1122 { 1123 // double forth and back loop 1124 drawinglayer::animation::AnimationEntryLoop aLoop(nDoubleRepeat ? nDoubleRepeat : ENDLESS_LOOP); 1125 drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition); 1126 aLoop.append(aTime0); 1127 drawinglayer::animation::AnimationEntryLinear aTime1(fTimeForInnerPath, fFrequency, fEndPosition, fStartPosition); 1128 aLoop.append(aTime1); 1129 drawinglayer::animation::AnimationEntryLinear aTime2(fHalfInnerPath, fFrequency, fStartPosition, 0.5); 1130 aLoop.append(aTime2); 1131 rAnimList.append(aLoop); 1132 } 1133 1134 if(nRepeat % 2L) 1135 { 1136 // repeat is uneven, so we need one more forth and back to center 1137 drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition); 1138 rAnimList.append(aTime0); 1139 drawinglayer::animation::AnimationEntryLinear aTime1(fHalfInnerPath, fFrequency, fEndPosition, 0.5); 1140 rAnimList.append(aTime1); 1141 } 1142 1143 if(0L != nRepeat) 1144 { 1145 if(bVisisbleWhenStopped) 1146 { 1147 // add timing for staying at the end 1148 drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5); 1149 rAnimList.append(aEnd); 1150 } 1151 else 1152 { 1153 // move from center to outside 1154 drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0); 1155 rAnimList.append(aInOut); 1156 } 1157 } 1158 } 1159 1160 void impCreateSlideTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency) 1161 { 1162 // move in from outside, start outside 1163 const double fStartPosition(bForward ? 0.0 : 1.0); 1164 const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue()); 1165 1166 // move from outside to center 1167 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5); 1168 rAnimList.append(aOutIn); 1169 1170 // loop. In loop, move out and in again 1171 if(nRepeat > 1L || 0L == nRepeat) 1172 { 1173 drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat - 1L : ENDLESS_LOOP); 1174 drawinglayer::animation::AnimationEntryLinear aTime0(fTimeFullPath * 0.5, fFrequency, 0.5, fStartPosition); 1175 aLoop.append(aTime0); 1176 drawinglayer::animation::AnimationEntryLinear aTime1(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5); 1177 aLoop.append(aTime1); 1178 rAnimList.append(aLoop); 1179 } 1180 1181 // always visible when stopped, so add timing for staying at the end when not endless 1182 if(0L != nRepeat) 1183 { 1184 drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5); 1185 rAnimList.append(aEnd); 1186 } 1187 } 1188 1189 void SdrTextObj::impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const 1190 { 1191 const SdrTextAniKind eAniKind(GetTextAniKind()); 1192 1193 if(SDRTEXTANI_SCROLL == eAniKind || SDRTEXTANI_ALTERNATE == eAniKind || SDRTEXTANI_SLIDE == eAniKind) 1194 { 1195 // get data. Goal is to calculate fTimeFullPath which is the time needed to 1196 // move animation from (0.0) to (1.0) state 1197 const SfxItemSet& rSet = GetObjectItemSet(); 1198 double fAnimationDelay((double)((SdrTextAniDelayItem&)rSet.Get(SDRATTR_TEXT_ANIDELAY)).GetValue()); 1199 double fSingleStepWidth((double)((SdrTextAniAmountItem&)rSet.Get(SDRATTR_TEXT_ANIAMOUNT)).GetValue()); 1200 const SdrTextAniDirection eDirection(GetTextAniDirection()); 1201 const bool bForward(SDRTEXTANI_RIGHT == eDirection || SDRTEXTANI_DOWN == eDirection); 1202 1203 if(basegfx::fTools::equalZero(fAnimationDelay)) 1204 { 1205 // default to 1/20 second 1206 fAnimationDelay = 50.0; 1207 } 1208 1209 if(basegfx::fTools::less(fSingleStepWidth, 0.0)) 1210 { 1211 // data is in pixels, convert to logic. Imply PIXEL_DPI dpi. 1212 // It makes no sense to keep the view-transformation centered 1213 // definitions, so get rid of them here. 1214 fSingleStepWidth = (-fSingleStepWidth * (2540.0 / PIXEL_DPI)); 1215 } 1216 1217 if(basegfx::fTools::equalZero(fSingleStepWidth)) 1218 { 1219 // default to 1 milimeter 1220 fSingleStepWidth = 100.0; 1221 } 1222 1223 // use the length of the full animation path and the number of steps 1224 // to get the full path time 1225 const double fFullPathLength(fFrameLength + fTextLength); 1226 const double fNumberOfSteps(fFullPathLength / fSingleStepWidth); 1227 double fTimeFullPath(fNumberOfSteps * fAnimationDelay); 1228 1229 if(fTimeFullPath < fAnimationDelay) 1230 { 1231 fTimeFullPath = fAnimationDelay; 1232 } 1233 1234 switch(eAniKind) 1235 { 1236 case SDRTEXTANI_SCROLL : 1237 { 1238 impCreateScrollTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay); 1239 break; 1240 } 1241 case SDRTEXTANI_ALTERNATE : 1242 { 1243 double fRelativeTextLength(fTextLength / (fFrameLength + fTextLength)); 1244 impCreateAlternateTiming(rSet, rAnimList, fRelativeTextLength, bForward, fTimeFullPath, fAnimationDelay); 1245 break; 1246 } 1247 case SDRTEXTANI_SLIDE : 1248 { 1249 impCreateSlideTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay); 1250 break; 1251 } 1252 default : break; // SDRTEXTANI_NONE, SDRTEXTANI_BLINK 1253 } 1254 } 1255 } 1256 1257 ////////////////////////////////////////////////////////////////////////////// 1258 // eof 1259