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