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