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_svgio.hxx" 24 25 #include <svgio/svgreader/svgcharacternode.hxx> 26 #include <svgio/svgreader/svgstyleattributes.hxx> 27 #include <drawinglayer/attribute/fontattribute.hxx> 28 #include <drawinglayer/primitive2d/textprimitive2d.hxx> 29 #include <drawinglayer/primitive2d/textlayoutdevice.hxx> 30 #include <drawinglayer/primitive2d/textbreakuphelper.hxx> 31 #include <drawinglayer/primitive2d/groupprimitive2d.hxx> 32 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> 33 34 ////////////////////////////////////////////////////////////////////////////// 35 36 namespace svgio 37 { 38 namespace svgreader 39 { SvgTextPositions()40 SvgTextPositions::SvgTextPositions() 41 : maX(), 42 maY(), 43 maDx(), 44 maDy(), 45 maRotate(), 46 maTextLength(), 47 mbLengthAdjust(true) 48 { 49 } 50 parseTextPositionAttributes(const rtl::OUString &,SVGToken aSVGToken,const rtl::OUString & aContent)51 void SvgTextPositions::parseTextPositionAttributes(const rtl::OUString& /*rTokenName*/, SVGToken aSVGToken, const rtl::OUString& aContent) 52 { 53 // parse own 54 switch(aSVGToken) 55 { 56 case SVGTokenX: 57 { 58 if(aContent.getLength()) 59 { 60 SvgNumberVector aVector; 61 62 if(readSvgNumberVector(aContent, aVector)) 63 { 64 setX(aVector); 65 } 66 } 67 break; 68 } 69 case SVGTokenY: 70 { 71 if(aContent.getLength()) 72 { 73 SvgNumberVector aVector; 74 75 if(readSvgNumberVector(aContent, aVector)) 76 { 77 setY(aVector); 78 } 79 } 80 break; 81 } 82 case SVGTokenDx: 83 { 84 if(aContent.getLength()) 85 { 86 SvgNumberVector aVector; 87 88 if(readSvgNumberVector(aContent, aVector)) 89 { 90 setDx(aVector); 91 } 92 } 93 break; 94 } 95 case SVGTokenDy: 96 { 97 if(aContent.getLength()) 98 { 99 SvgNumberVector aVector; 100 101 if(readSvgNumberVector(aContent, aVector)) 102 { 103 setDy(aVector); 104 } 105 } 106 break; 107 } 108 case SVGTokenRotate: 109 { 110 if(aContent.getLength()) 111 { 112 SvgNumberVector aVector; 113 114 if(readSvgNumberVector(aContent, aVector)) 115 { 116 setRotate(aVector); 117 } 118 } 119 break; 120 } 121 case SVGTokenTextLength: 122 { 123 SvgNumber aNum; 124 125 if(readSingleNumber(aContent, aNum)) 126 { 127 if(aNum.isPositive()) 128 { 129 setTextLength(aNum); 130 } 131 } 132 break; 133 } 134 case SVGTokenLengthAdjust: 135 { 136 if(aContent.getLength()) 137 { 138 static rtl::OUString aStrSpacing(rtl::OUString::createFromAscii("spacing")); 139 static rtl::OUString aStrSpacingAndGlyphs(rtl::OUString::createFromAscii("spacingAndGlyphs")); 140 141 if(aContent.match(aStrSpacing)) 142 { 143 setLengthAdjust(true); 144 } 145 else if(aContent.match(aStrSpacingAndGlyphs)) 146 { 147 setLengthAdjust(false); 148 } 149 } 150 break; 151 } 152 default: 153 { 154 break; 155 } 156 } 157 } 158 159 } // end of namespace svgreader 160 } // end of namespace svgio 161 162 ////////////////////////////////////////////////////////////////////////////// 163 164 namespace svgio 165 { 166 namespace svgreader 167 { 168 class localTextBreakupHelper : public drawinglayer::primitive2d::TextBreakupHelper 169 { 170 private: 171 SvgTextPosition& mrSvgTextPosition; 172 173 protected: 174 /// allow user callback to allow changes to the new TextTransformation. Default 175 /// does nothing. 176 virtual bool allowChange(sal_uInt32 nCount, basegfx::B2DHomMatrix& rNewTransform, sal_uInt32 nIndex, sal_uInt32 nLength); 177 178 public: localTextBreakupHelper(const drawinglayer::primitive2d::TextSimplePortionPrimitive2D & rSource,SvgTextPosition & rSvgTextPosition)179 localTextBreakupHelper( 180 const drawinglayer::primitive2d::TextSimplePortionPrimitive2D& rSource, 181 SvgTextPosition& rSvgTextPosition) 182 : drawinglayer::primitive2d::TextBreakupHelper(rSource), 183 mrSvgTextPosition(rSvgTextPosition) 184 { 185 } 186 }; 187 allowChange(sal_uInt32,basegfx::B2DHomMatrix & rNewTransform,sal_uInt32,sal_uInt32)188 bool localTextBreakupHelper::allowChange(sal_uInt32 /*nCount*/, basegfx::B2DHomMatrix& rNewTransform, sal_uInt32 /*nIndex*/, sal_uInt32 /*nLength*/) 189 { 190 const double fRotation(mrSvgTextPosition.consumeRotation()); 191 192 if(0.0 != fRotation) 193 { 194 const basegfx::B2DPoint aBasePoint(rNewTransform * basegfx::B2DPoint(0.0, 0.0)); 195 196 rNewTransform.translate(-aBasePoint.getX(), -aBasePoint.getY()); 197 rNewTransform.rotate(fRotation); 198 rNewTransform.translate(aBasePoint.getX(), aBasePoint.getY()); 199 } 200 201 return true; 202 } 203 204 } // end of namespace svgreader 205 } // end of namespace svgio 206 207 ////////////////////////////////////////////////////////////////////////////// 208 209 namespace svgio 210 { 211 namespace svgreader 212 { SvgCharacterNode(SvgDocument & rDocument,SvgNode * pParent,const rtl::OUString & rText)213 SvgCharacterNode::SvgCharacterNode( 214 SvgDocument& rDocument, 215 SvgNode* pParent, 216 const rtl::OUString& rText) 217 : SvgNode(SVGTokenCharacter, rDocument, pParent), 218 maText(rText) 219 { 220 } 221 ~SvgCharacterNode()222 SvgCharacterNode::~SvgCharacterNode() 223 { 224 } 225 getSvgStyleAttributes() const226 const SvgStyleAttributes* SvgCharacterNode::getSvgStyleAttributes() const 227 { 228 // no own style, use parent's 229 if(getParent()) 230 { 231 return getParent()->getSvgStyleAttributes(); 232 } 233 else 234 { 235 return 0; 236 } 237 } 238 createSimpleTextPrimitive(SvgTextPosition & rSvgTextPosition,const SvgStyleAttributes & rSvgStyleAttributes) const239 drawinglayer::primitive2d::TextSimplePortionPrimitive2D* SvgCharacterNode::createSimpleTextPrimitive( 240 SvgTextPosition& rSvgTextPosition, 241 const SvgStyleAttributes& rSvgStyleAttributes) const 242 { 243 // prepare retval, index and length 244 drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pRetval = 0; 245 sal_uInt32 nIndex(0); 246 sal_uInt32 nLength(getText().getLength()); 247 248 if(nLength) 249 { 250 // prepare FontAttribute 251 rtl::OUString aFontFamily = rSvgStyleAttributes.getFontFamily().empty() ? 252 rtl::OUString(rtl::OUString::createFromAscii("Times New Roman")) : 253 rSvgStyleAttributes.getFontFamily()[0]; 254 255 // #122324# if the FontFamily name ends on ' embedded' it is probably a re-import 256 // of a SVG export with fiont embedding. Remove this to make font matching work. This 257 // is pretty safe since there should be no font family names ending on ' embedded'. 258 // Remove again when FontEmbedding is implemented in SVG import 259 if(aFontFamily.endsWithAsciiL(" embedded", 9)) 260 { 261 aFontFamily = aFontFamily.copy(0, aFontFamily.getLength() - 9); 262 } 263 264 const ::FontWeight nFontWeight(getVclFontWeight(rSvgStyleAttributes.getFontWeight())); 265 bool bSymbol(false); 266 bool bVertical(false); 267 bool bItalic(FontStyle_italic == rSvgStyleAttributes.getFontStyle() || FontStyle_oblique == rSvgStyleAttributes.getFontStyle()); 268 bool bMonospaced(false); 269 bool bOutline(false); 270 bool bRTL(false); 271 bool bBiDiStrong(false); 272 273 const drawinglayer::attribute::FontAttribute aFontAttribute( 274 aFontFamily, 275 rtl::OUString(), 276 nFontWeight, 277 bSymbol, 278 bVertical, 279 bItalic, 280 bMonospaced, 281 bOutline, 282 bRTL, 283 bBiDiStrong); 284 285 // prepare FontSize 286 double fFontWidth(rSvgStyleAttributes.getFontSize().solve(*this, length)); 287 double fFontHeight(fFontWidth); 288 289 // prepare locale 290 ::com::sun::star::lang::Locale aLocale; 291 292 // prepare TextLayouterDevice 293 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice; 294 aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth, fFontHeight, aLocale); 295 296 // prepare TextArray 297 ::std::vector< double > aTextArray(rSvgTextPosition.getX()); 298 299 if(!aTextArray.empty() && aTextArray.size() < nLength) 300 { 301 const sal_uInt32 nArray(aTextArray.size()); 302 303 if(nArray < nLength) 304 { 305 double fStartX(0.0); 306 307 if(rSvgTextPosition.getParent() && rSvgTextPosition.getParent()->getAbsoluteX()) 308 { 309 fStartX = rSvgTextPosition.getParent()->getPosition().getX(); 310 } 311 else 312 { 313 fStartX = aTextArray[nArray - 1]; 314 } 315 316 ::std::vector< double > aExtendArray(aTextLayouterDevice.getTextArray(getText(), nArray, nLength - nArray)); 317 aTextArray.reserve(nLength); 318 319 for(sal_uInt32 a(0); a < aExtendArray.size(); a++) 320 { 321 aTextArray.push_back(aExtendArray[a] + fStartX); 322 } 323 } 324 } 325 326 // get current TextPosition and TextWidth in units 327 basegfx::B2DPoint aPosition(rSvgTextPosition.getPosition()); 328 double fTextWidth(aTextLayouterDevice.getTextWidth(getText(), nIndex, nLength)); 329 330 // check for user-given TextLength 331 if(0.0 != rSvgTextPosition.getTextLength() 332 && !basegfx::fTools::equal(fTextWidth, rSvgTextPosition.getTextLength())) 333 { 334 const double fFactor(rSvgTextPosition.getTextLength() / fTextWidth); 335 336 if(rSvgTextPosition.getLengthAdjust()) 337 { 338 // spacing, need to create and expand TextArray 339 if(aTextArray.empty()) 340 { 341 aTextArray = aTextLayouterDevice.getTextArray(getText(), nIndex, nLength); 342 } 343 344 for(sal_uInt32 a(0); a < aTextArray.size(); a++) 345 { 346 aTextArray[a] *= fFactor; 347 } 348 } 349 else 350 { 351 // spacing and glyphs, just apply to FontWidth 352 fFontWidth *= fFactor; 353 } 354 355 fTextWidth = rSvgTextPosition.getTextLength(); 356 } 357 358 // get TextAlign 359 TextAlign aTextAlign(rSvgStyleAttributes.getTextAlign()); 360 361 // map TextAnchor to TextAlign, there seems not to be a difference 362 if(TextAnchor_notset != rSvgStyleAttributes.getTextAnchor()) 363 { 364 switch(rSvgStyleAttributes.getTextAnchor()) 365 { 366 case TextAnchor_start: 367 { 368 aTextAlign = TextAlign_left; 369 break; 370 } 371 case TextAnchor_middle: 372 { 373 aTextAlign = TextAlign_center; 374 break; 375 } 376 case TextAnchor_end: 377 { 378 aTextAlign = TextAlign_right; 379 break; 380 } 381 default: 382 { 383 break; 384 } 385 } 386 } 387 388 // apply TextAlign 389 switch(aTextAlign) 390 { 391 case TextAlign_right: 392 { 393 aPosition.setX(aPosition.getX() - fTextWidth); 394 break; 395 } 396 case TextAlign_center: 397 { 398 aPosition.setX(aPosition.getX() - (fTextWidth * 0.5)); 399 break; 400 } 401 case TextAlign_notset: 402 case TextAlign_left: 403 case TextAlign_justify: 404 { 405 // TextAlign_notset, TextAlign_left: nothing to do 406 // TextAlign_justify is not clear currently; handle as TextAlign_left 407 break; 408 } 409 } 410 411 // get BaselineShift 412 const BaselineShift aBaselineShift(rSvgStyleAttributes.getBaselineShift()); 413 414 // apply BaselineShift 415 switch(aBaselineShift) 416 { 417 case BaselineShift_Sub: 418 { 419 aPosition.setY(aPosition.getY() + aTextLayouterDevice.getUnderlineOffset()); 420 break; 421 } 422 case BaselineShift_Super: 423 { 424 aPosition.setY(aPosition.getY() + aTextLayouterDevice.getOverlineOffset()); 425 break; 426 } 427 case BaselineShift_Percentage: 428 case BaselineShift_Length: 429 { 430 const SvgNumber aNumber(rSvgStyleAttributes.getBaselineShiftNumber()); 431 const double mfBaselineShift(aNumber.solve(*this, length)); 432 433 aPosition.setY(aPosition.getY() + mfBaselineShift); 434 break; 435 } 436 default: // BaselineShift_Baseline 437 { 438 // nothing to do 439 break; 440 } 441 } 442 443 // get fill color 444 const basegfx::BColor aFill(rSvgStyleAttributes.getFill() 445 ? *rSvgStyleAttributes.getFill() 446 : basegfx::BColor(0.0, 0.0, 0.0)); 447 448 // prepare TextTransformation 449 basegfx::B2DHomMatrix aTextTransform; 450 451 aTextTransform.scale(fFontWidth, fFontHeight); 452 aTextTransform.translate(aPosition.getX(), aPosition.getY()); 453 454 // check TextDecoration and if TextDecoratedPortionPrimitive2D is needed 455 const TextDecoration aDeco(rSvgStyleAttributes.getTextDecoration()); 456 457 if(TextDecoration_underline == aDeco 458 || TextDecoration_overline == aDeco 459 || TextDecoration_line_through == aDeco) 460 { 461 // get the fill for decroation as described by SVG. We cannot 462 // have different stroke colors/definitions for those, though 463 const SvgStyleAttributes* pDecoDef = rSvgStyleAttributes.getTextDecorationDefiningSvgStyleAttributes(); 464 const basegfx::BColor aDecoColor(pDecoDef && pDecoDef->getFill() ? *pDecoDef->getFill() : aFill); 465 466 // create decorated text primitive 467 pRetval = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( 468 aTextTransform, 469 getText(), 470 nIndex, 471 nLength, 472 aTextArray, 473 aFontAttribute, 474 aLocale, 475 aFill, 476 477 // extra props for decorated 478 aDecoColor, 479 aDecoColor, 480 TextDecoration_overline == aDeco ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE, 481 TextDecoration_underline == aDeco ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE, 482 false, 483 TextDecoration_line_through == aDeco ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE, 484 false, 485 drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE, 486 true, 487 false, 488 drawinglayer::primitive2d::TEXT_RELIEF_NONE, 489 false); 490 } 491 else 492 { 493 // create text primitive 494 pRetval = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( 495 aTextTransform, 496 getText(), 497 nIndex, 498 nLength, 499 aTextArray, 500 aFontAttribute, 501 aLocale, 502 aFill); 503 } 504 505 // advance current TextPosition 506 rSvgTextPosition.setPosition(rSvgTextPosition.getPosition() + basegfx::B2DVector(fTextWidth, 0.0)); 507 } 508 509 return pRetval; 510 } 511 decomposeTextWithStyle(drawinglayer::primitive2d::Primitive2DSequence & rTarget,SvgTextPosition & rSvgTextPosition,const SvgStyleAttributes & rSvgStyleAttributes) const512 void SvgCharacterNode::decomposeTextWithStyle( 513 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 514 SvgTextPosition& rSvgTextPosition, 515 const SvgStyleAttributes& rSvgStyleAttributes) const 516 { 517 const drawinglayer::primitive2d::Primitive2DReference xRef( 518 createSimpleTextPrimitive( 519 rSvgTextPosition, 520 rSvgStyleAttributes)); 521 522 if(xRef.is()) 523 { 524 if(!rSvgTextPosition.isRotated()) 525 { 526 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xRef); 527 } 528 else 529 { 530 // need to apply rotations to each character as given 531 const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pCandidate = 532 dynamic_cast< const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* >(xRef.get()); 533 534 if(pCandidate) 535 { 536 const localTextBreakupHelper alocalTextBreakupHelper(*pCandidate, rSvgTextPosition); 537 const drawinglayer::primitive2d::Primitive2DSequence aResult( 538 alocalTextBreakupHelper.getResult(drawinglayer::primitive2d::BreakupUnit_character)); 539 540 if(aResult.hasElements()) 541 { 542 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aResult); 543 } 544 545 // also consume for the implied single space 546 rSvgTextPosition.consumeRotation(); 547 } 548 else 549 { 550 OSL_ENSURE(false, "Used primitive is not a text primitive (!)"); 551 } 552 } 553 } 554 } 555 whiteSpaceHandling()556 void SvgCharacterNode::whiteSpaceHandling() 557 { 558 if(XmlSpace_default == getXmlSpace()) 559 { 560 maText = whiteSpaceHandlingDefault(maText); 561 } 562 else 563 { 564 maText = whiteSpaceHandlingPreserve(maText); 565 } 566 } 567 addGap()568 void SvgCharacterNode::addGap() 569 { 570 maText += rtl::OUString(sal_Unicode(' ')); 571 } 572 concatenate(const rtl::OUString & rText)573 void SvgCharacterNode::concatenate(const rtl::OUString& rText) 574 { 575 maText += rText; 576 } 577 decomposeText(drawinglayer::primitive2d::Primitive2DSequence & rTarget,SvgTextPosition & rSvgTextPosition) const578 void SvgCharacterNode::decomposeText(drawinglayer::primitive2d::Primitive2DSequence& rTarget, SvgTextPosition& rSvgTextPosition) const 579 { 580 if(getText().getLength()) 581 { 582 const SvgStyleAttributes* pSvgStyleAttributes = getSvgStyleAttributes(); 583 584 if(pSvgStyleAttributes) 585 { 586 decomposeTextWithStyle(rTarget, rSvgTextPosition, *pSvgStyleAttributes); 587 } 588 } 589 } 590 591 } // end of namespace svgreader 592 } // end of namespace svgio 593 594 ////////////////////////////////////////////////////////////////////////////// 595 596 namespace svgio 597 { 598 namespace svgreader 599 { SvgTextPosition(SvgTextPosition * pParent,const InfoProvider & rInfoProvider,const SvgTextPositions & rSvgTextPositions)600 SvgTextPosition::SvgTextPosition( 601 SvgTextPosition* pParent, 602 const InfoProvider& rInfoProvider, 603 const SvgTextPositions& rSvgTextPositions) 604 : mpParent(pParent), 605 maX(), // computed below 606 maY(), // computed below 607 maRotate(solveSvgNumberVector(rSvgTextPositions.getRotate(), rInfoProvider, length)), 608 mfTextLength(0.0), 609 maPosition(), // computed below 610 mnRotationIndex(0), 611 mbLengthAdjust(rSvgTextPositions.getLengthAdjust()), 612 mbAbsoluteX(false), 613 mbAbsoluteY(false) 614 { 615 // get TextLength if provided 616 if(rSvgTextPositions.getTextLength().isSet()) 617 { 618 mfTextLength = rSvgTextPositions.getTextLength().solve(rInfoProvider, length); 619 } 620 621 // SVG does not really define in which units a �rotate� for Text/TSpan is given, 622 // but it seems to be degrees. Convert here to radians 623 if(!maRotate.empty()) 624 { 625 const double fFactor(F_PI / 180.0); 626 627 for(sal_uInt32 a(0); a < maRotate.size(); a++) 628 { 629 maRotate[a] *= fFactor; 630 } 631 } 632 633 // get text positions X 634 const sal_uInt32 nSizeX(rSvgTextPositions.getX().size()); 635 636 if(nSizeX) 637 { 638 // we have absolute positions, get first one as current text position X 639 maPosition.setX(rSvgTextPositions.getX()[0].solve(rInfoProvider, xcoordinate)); 640 mbAbsoluteX = true; 641 642 if(nSizeX > 1) 643 { 644 // fill deltas to maX 645 maX.reserve(nSizeX); 646 647 for(sal_uInt32 a(1); a < nSizeX; a++) 648 { 649 maX.push_back(rSvgTextPositions.getX()[a].solve(rInfoProvider, xcoordinate) - maPosition.getX()); 650 } 651 } 652 } 653 else 654 { 655 // no absolute position, get from parent 656 if(pParent) 657 { 658 maPosition.setX(pParent->getPosition().getX()); 659 } 660 661 const sal_uInt32 nSizeDx(rSvgTextPositions.getDx().size()); 662 663 if(nSizeDx) 664 { 665 // relative positions given, translate position derived from parent 666 maPosition.setX(maPosition.getX() + rSvgTextPositions.getDx()[0].solve(rInfoProvider, xcoordinate)); 667 668 if(nSizeDx > 1) 669 { 670 // fill deltas to maX 671 maX.reserve(nSizeDx); 672 673 for(sal_uInt32 a(1); a < nSizeDx; a++) 674 { 675 maX.push_back(rSvgTextPositions.getDx()[a].solve(rInfoProvider, xcoordinate)); 676 } 677 } 678 } 679 } 680 681 // get text positions Y 682 const sal_uInt32 nSizeY(rSvgTextPositions.getY().size()); 683 684 if(nSizeY) 685 { 686 // we have absolute positions, get first one as current text position Y 687 maPosition.setY(rSvgTextPositions.getY()[0].solve(rInfoProvider, ycoordinate)); 688 mbAbsoluteX = true; 689 690 if(nSizeY > 1) 691 { 692 // fill deltas to maY 693 maY.reserve(nSizeY); 694 695 for(sal_uInt32 a(1); a < nSizeY; a++) 696 { 697 maY.push_back(rSvgTextPositions.getY()[a].solve(rInfoProvider, ycoordinate) - maPosition.getY()); 698 } 699 } 700 } 701 else 702 { 703 // no absolute position, get from parent 704 if(pParent) 705 { 706 maPosition.setY(pParent->getPosition().getY()); 707 } 708 709 const sal_uInt32 nSizeDy(rSvgTextPositions.getDy().size()); 710 711 if(nSizeDy) 712 { 713 // relative positions given, translate position derived from parent 714 maPosition.setY(maPosition.getY() + rSvgTextPositions.getDy()[0].solve(rInfoProvider, ycoordinate)); 715 716 if(nSizeDy > 1) 717 { 718 // fill deltas to maY 719 maY.reserve(nSizeDy); 720 721 for(sal_uInt32 a(1); a < nSizeDy; a++) 722 { 723 maY.push_back(rSvgTextPositions.getDy()[a].solve(rInfoProvider, ycoordinate)); 724 } 725 } 726 } 727 } 728 } 729 isRotated() const730 bool SvgTextPosition::isRotated() const 731 { 732 if(maRotate.empty()) 733 { 734 if(getParent()) 735 { 736 return getParent()->isRotated(); 737 } 738 else 739 { 740 return false; 741 } 742 } 743 else 744 { 745 return true; 746 } 747 } 748 consumeRotation()749 double SvgTextPosition::consumeRotation() 750 { 751 double fRetval(0.0); 752 753 if(maRotate.empty()) 754 { 755 if(getParent()) 756 { 757 fRetval = mpParent->consumeRotation(); 758 } 759 else 760 { 761 fRetval = 0.0; 762 } 763 } 764 else 765 { 766 const sal_uInt32 nSize(maRotate.size()); 767 768 if(mnRotationIndex < nSize) 769 { 770 fRetval = maRotate[mnRotationIndex++]; 771 } 772 else 773 { 774 fRetval = maRotate[nSize - 1]; 775 } 776 } 777 778 return fRetval; 779 } 780 781 } // end of namespace svgreader 782 } // end of namespace svgio 783 784 ////////////////////////////////////////////////////////////////////////////// 785 // eof 786