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