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 { 40 SvgTextPositions::SvgTextPositions() 41 : maX(), 42 maY(), 43 maDx(), 44 maDy(), 45 maRotate(), 46 maTextLength(), 47 mbLengthAdjust(true) 48 { 49 } 50 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 } 153 } 154 155 } // end of namespace svgreader 156 } // end of namespace svgio 157 158 ////////////////////////////////////////////////////////////////////////////// 159 160 namespace svgio 161 { 162 namespace svgreader 163 { 164 class localTextBreakupHelper : public drawinglayer::primitive2d::TextBreakupHelper 165 { 166 private: 167 SvgTextPosition& mrSvgTextPosition; 168 169 protected: 170 /// allow user callback to allow changes to the new TextTransformation. Default 171 /// does nothing. 172 virtual bool allowChange(sal_uInt32 nCount, basegfx::B2DHomMatrix& rNewTransform, sal_uInt32 nIndex, sal_uInt32 nLength); 173 174 public: 175 localTextBreakupHelper( 176 const drawinglayer::primitive2d::Primitive2DReference& rxSource, 177 SvgTextPosition& rSvgTextPosition) 178 : drawinglayer::primitive2d::TextBreakupHelper(rxSource), 179 mrSvgTextPosition(rSvgTextPosition) 180 { 181 } 182 }; 183 184 bool localTextBreakupHelper::allowChange(sal_uInt32 nCount, basegfx::B2DHomMatrix& rNewTransform, sal_uInt32 nIndex, sal_uInt32 nLength) 185 { 186 const double fRotation(mrSvgTextPosition.consumeRotation()); 187 188 if(0.0 != fRotation) 189 { 190 const basegfx::B2DPoint aBasePoint(rNewTransform * basegfx::B2DPoint(0.0, 0.0)); 191 192 rNewTransform.translate(-aBasePoint.getX(), -aBasePoint.getY()); 193 rNewTransform.rotate(fRotation); 194 rNewTransform.translate(aBasePoint.getX(), aBasePoint.getY()); 195 } 196 197 return true; 198 } 199 200 } // end of namespace svgreader 201 } // end of namespace svgio 202 203 ////////////////////////////////////////////////////////////////////////////// 204 205 namespace svgio 206 { 207 namespace svgreader 208 { 209 SvgCharacterNode::SvgCharacterNode( 210 SvgDocument& rDocument, 211 SvgNode* pParent, 212 const rtl::OUString& rText) 213 : SvgNode(SVGTokenCharacter, rDocument, pParent), 214 maText(rText) 215 { 216 } 217 218 SvgCharacterNode::~SvgCharacterNode() 219 { 220 } 221 222 const SvgStyleAttributes* SvgCharacterNode::getSvgStyleAttributes() const 223 { 224 // no own style, use parent's 225 if(getParent()) 226 { 227 return getParent()->getSvgStyleAttributes(); 228 } 229 else 230 { 231 return 0; 232 } 233 } 234 235 drawinglayer::primitive2d::TextSimplePortionPrimitive2D* SvgCharacterNode::createSimpleTextPrimitive( 236 SvgTextPosition& rSvgTextPosition, 237 const SvgStyleAttributes& rSvgStyleAttributes) const 238 { 239 // prepare retval, index and length 240 drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pRetval = 0; 241 sal_uInt32 nIndex(0); 242 sal_uInt32 nLength(getText().getLength()); 243 244 if(nLength) 245 { 246 // prepare FontAttribute 247 const rtl::OUString aFontFamily = rSvgStyleAttributes.getFontFamily().empty() ? 248 rtl::OUString(rtl::OUString::createFromAscii("Times New Roman")) : 249 rSvgStyleAttributes.getFontFamily()[0]; 250 const ::FontWeight nFontWeight(getVclFontWeight(rSvgStyleAttributes.getFontWeight())); 251 bool bSymbol(false); 252 bool bVertical(false); 253 bool bItalic(FontStyle_italic == rSvgStyleAttributes.getFontStyle() || FontStyle_oblique == rSvgStyleAttributes.getFontStyle()); 254 bool bMonospaced(false); 255 bool bOutline(false); 256 bool bRTL(false); 257 bool bBiDiStrong(false); 258 259 const drawinglayer::attribute::FontAttribute aFontAttribute( 260 aFontFamily, 261 rtl::OUString(), 262 nFontWeight, 263 bSymbol, 264 bVertical, 265 bItalic, 266 bMonospaced, 267 bOutline, 268 bRTL, 269 bBiDiStrong); 270 271 // prepare FontSize 272 double fFontWidth(rSvgStyleAttributes.getFontSize().solve(*this, length)); 273 double fFontHeight(fFontWidth); 274 275 // prepare locale 276 ::com::sun::star::lang::Locale aLocale; 277 278 // prepare TextLayouterDevice 279 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice; 280 aTextLayouterDevice.setFontAttribute(aFontAttribute, fFontWidth, fFontHeight, aLocale); 281 282 // prepare TextArray 283 ::std::vector< double > aTextArray(rSvgTextPosition.getX()); 284 285 if(!aTextArray.empty() && aTextArray.size() < nLength) 286 { 287 const sal_uInt32 nArray(aTextArray.size()); 288 289 if(nArray < nLength) 290 { 291 double fStartX(0.0); 292 293 if(rSvgTextPosition.getParent() && rSvgTextPosition.getParent()->getAbsoluteX()) 294 { 295 fStartX = rSvgTextPosition.getParent()->getPosition().getX(); 296 } 297 else 298 { 299 fStartX = aTextArray[nArray - 1]; 300 } 301 302 ::std::vector< double > aExtendArray(aTextLayouterDevice.getTextArray(getText(), nArray, nLength - nArray)); 303 aTextArray.reserve(nLength); 304 305 for(sal_uInt32 a(0); a < aExtendArray.size(); a++) 306 { 307 aTextArray.push_back(aExtendArray[a] + fStartX); 308 } 309 } 310 } 311 312 // get current TextPosition and TextWidth in units 313 basegfx::B2DPoint aPosition(rSvgTextPosition.getPosition()); 314 double fTextWidth(aTextLayouterDevice.getTextWidth(getText(), nIndex, nLength)); 315 316 // check for user-given TextLength 317 if(0.0 != rSvgTextPosition.getTextLength() 318 && !basegfx::fTools::equal(fTextWidth, rSvgTextPosition.getTextLength())) 319 { 320 const double fFactor(rSvgTextPosition.getTextLength() / fTextWidth); 321 322 if(rSvgTextPosition.getLengthAdjust()) 323 { 324 // spacing, need to create and expand TextArray 325 if(aTextArray.empty()) 326 { 327 aTextArray = aTextLayouterDevice.getTextArray(getText(), nIndex, nLength); 328 } 329 330 for(sal_uInt32 a(0); a < aTextArray.size(); a++) 331 { 332 aTextArray[a] *= fFactor; 333 } 334 } 335 else 336 { 337 // spacing and glyphs, just apply to FontWidth 338 fFontWidth *= fFactor; 339 } 340 341 fTextWidth = rSvgTextPosition.getTextLength(); 342 } 343 344 // get TextAlign 345 TextAlign aTextAlign(rSvgStyleAttributes.getTextAlign()); 346 347 // map TextAnchor to TextAlign, there seems not to be a difference 348 if(TextAnchor_notset != rSvgStyleAttributes.getTextAnchor()) 349 { 350 switch(rSvgStyleAttributes.getTextAnchor()) 351 { 352 case TextAnchor_start: 353 { 354 aTextAlign = TextAlign_left; 355 break; 356 } 357 case TextAnchor_middle: 358 { 359 aTextAlign = TextAlign_center; 360 break; 361 } 362 case TextAnchor_end: 363 { 364 aTextAlign = TextAlign_right; 365 break; 366 } 367 } 368 } 369 370 // apply TextAlign 371 switch(aTextAlign) 372 { 373 case TextAlign_right: 374 { 375 aPosition.setX(aPosition.getX() - fTextWidth); 376 break; 377 } 378 case TextAlign_center: 379 { 380 aPosition.setX(aPosition.getX() - (fTextWidth * 0.5)); 381 break; 382 } 383 case TextAlign_notset: 384 case TextAlign_left: 385 case TextAlign_justify: 386 { 387 // TextAlign_notset, TextAlign_left: nothing to do 388 // TextAlign_justify is not clear currently; handle as TextAlign_left 389 break; 390 } 391 } 392 393 // get fill color 394 const basegfx::BColor aFill(rSvgStyleAttributes.getFill() 395 ? *rSvgStyleAttributes.getFill() 396 : basegfx::BColor(0.0, 0.0, 0.0)); 397 398 // prepare TextTransformation 399 basegfx::B2DHomMatrix aTextTransform; 400 401 aTextTransform.scale(fFontWidth, fFontHeight); 402 aTextTransform.translate(aPosition.getX(), aPosition.getY()); 403 404 // check TextDecoration and if TextDecoratedPortionPrimitive2D is needed 405 const TextDecoration aDeco(rSvgStyleAttributes.getTextDecoration()); 406 407 if(TextDecoration_underline == aDeco 408 || TextDecoration_overline == aDeco 409 || TextDecoration_line_through == aDeco) 410 { 411 // get the fill for decroation as described by SVG. We cannot 412 // have different stroke colors/definitions for those, though 413 const SvgStyleAttributes* pDecoDef = rSvgStyleAttributes.getTextDecorationDefiningSvgStyleAttributes(); 414 const basegfx::BColor aDecoColor(pDecoDef && pDecoDef->getFill() ? *pDecoDef->getFill() : aFill); 415 416 // create decorated text primitive 417 pRetval = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( 418 aTextTransform, 419 getText(), 420 nIndex, 421 nLength, 422 aTextArray, 423 aFontAttribute, 424 aLocale, 425 aFill, 426 427 // extra props for decorated 428 aDecoColor, 429 aDecoColor, 430 TextDecoration_overline == aDeco ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE, 431 TextDecoration_underline == aDeco ? drawinglayer::primitive2d::TEXT_LINE_SINGLE : drawinglayer::primitive2d::TEXT_LINE_NONE, 432 false, 433 TextDecoration_line_through == aDeco ? drawinglayer::primitive2d::TEXT_STRIKEOUT_SINGLE : drawinglayer::primitive2d::TEXT_STRIKEOUT_NONE, 434 false, 435 drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE, 436 true, 437 false, 438 drawinglayer::primitive2d::TEXT_RELIEF_NONE, 439 false); 440 } 441 else 442 { 443 // create text primitive 444 pRetval = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( 445 aTextTransform, 446 getText(), 447 nIndex, 448 nLength, 449 aTextArray, 450 aFontAttribute, 451 aLocale, 452 aFill); 453 } 454 455 // advance current TextPosition 456 rSvgTextPosition.setPosition(rSvgTextPosition.getPosition() + basegfx::B2DVector(fTextWidth, 0.0)); 457 } 458 459 return pRetval; 460 } 461 462 void SvgCharacterNode::decomposeTextWithStyle( 463 drawinglayer::primitive2d::Primitive2DSequence& rTarget, 464 SvgTextPosition& rSvgTextPosition, 465 const SvgStyleAttributes& rSvgStyleAttributes) const 466 { 467 const drawinglayer::primitive2d::Primitive2DReference xRef( 468 createSimpleTextPrimitive( 469 rSvgTextPosition, 470 rSvgStyleAttributes)); 471 472 if(xRef.is()) 473 { 474 if(!rSvgTextPosition.isRotated()) 475 { 476 drawinglayer::primitive2d::appendPrimitive2DReferenceToPrimitive2DSequence(rTarget, xRef); 477 } 478 else 479 { 480 // need to apply rotations to each character as given 481 localTextBreakupHelper alocalTextBreakupHelper(xRef, rSvgTextPosition); 482 const drawinglayer::primitive2d::Primitive2DSequence aResult( 483 alocalTextBreakupHelper.getResult(drawinglayer::primitive2d::BreakupUnit_character)); 484 485 if(aResult.hasElements()) 486 { 487 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aResult); 488 } 489 490 // also consume for the implied single space 491 rSvgTextPosition.consumeRotation(); 492 } 493 } 494 } 495 496 void SvgCharacterNode::whiteSpaceHandling() 497 { 498 if(XmlSpace_default == getXmlSpace()) 499 { 500 maText = whiteSpaceHandlingDefault(maText); 501 } 502 else 503 { 504 maText = whiteSpaceHandlingPreserve(maText); 505 } 506 } 507 508 void SvgCharacterNode::addGap() 509 { 510 maText += rtl::OUString(sal_Unicode(' ')); 511 } 512 513 void SvgCharacterNode::concatenate(const rtl::OUString& rText) 514 { 515 maText += rText; 516 } 517 518 void SvgCharacterNode::decomposeText(drawinglayer::primitive2d::Primitive2DSequence& rTarget, SvgTextPosition& rSvgTextPosition) const 519 { 520 if(getText().getLength()) 521 { 522 const SvgStyleAttributes* pSvgStyleAttributes = getSvgStyleAttributes(); 523 524 if(pSvgStyleAttributes) 525 { 526 decomposeTextWithStyle(rTarget, rSvgTextPosition, *pSvgStyleAttributes); 527 } 528 } 529 } 530 531 } // end of namespace svgreader 532 } // end of namespace svgio 533 534 ////////////////////////////////////////////////////////////////////////////// 535 536 namespace svgio 537 { 538 namespace svgreader 539 { 540 SvgTextPosition::SvgTextPosition( 541 SvgTextPosition* pParent, 542 const InfoProvider& rInfoProvider, 543 const SvgTextPositions& rSvgTextPositions) 544 : mpParent(pParent), 545 maX(), // computed below 546 maY(), // computed below 547 maRotate(solveSvgNumberVector(rSvgTextPositions.getRotate(), rInfoProvider, length)), 548 mfTextLength(0.0), 549 maPosition(), // computed below 550 mnRotationIndex(0), 551 mbLengthAdjust(rSvgTextPositions.getLengthAdjust()), 552 mbAbsoluteX(false), 553 mbAbsoluteY(false) 554 { 555 // get TextLength if provided 556 if(rSvgTextPositions.getTextLength().isSet()) 557 { 558 mfTextLength = rSvgTextPositions.getTextLength().solve(rInfoProvider, length); 559 } 560 561 // SVG does not really define in which units a �rotate� for Text/TSpan is given, 562 // but it seems to be degrees. Convert here to radians 563 if(!maRotate.empty()) 564 { 565 const double fFactor(F_PI / 180.0); 566 567 for(sal_uInt32 a(0); a < maRotate.size(); a++) 568 { 569 maRotate[a] *= fFactor; 570 } 571 } 572 573 // get text positions X 574 const sal_uInt32 nSizeX(rSvgTextPositions.getX().size()); 575 576 if(nSizeX) 577 { 578 // we have absolute positions, get first one as current text position X 579 maPosition.setX(rSvgTextPositions.getX()[0].solve(rInfoProvider, xcoordinate)); 580 mbAbsoluteX = true; 581 582 if(nSizeX > 1) 583 { 584 // fill deltas to maX 585 maX.reserve(nSizeX); 586 587 for(sal_uInt32 a(1); a < nSizeX; a++) 588 { 589 maX.push_back(rSvgTextPositions.getX()[a].solve(rInfoProvider, xcoordinate) - maPosition.getX()); 590 } 591 } 592 } 593 else 594 { 595 // no absolute position, get from parent 596 if(pParent) 597 { 598 maPosition.setX(pParent->getPosition().getX()); 599 } 600 601 const sal_uInt32 nSizeDx(rSvgTextPositions.getDx().size()); 602 603 if(nSizeDx) 604 { 605 // relative positions given, translate position derived from parent 606 maPosition.setX(maPosition.getX() + rSvgTextPositions.getDx()[0].solve(rInfoProvider, xcoordinate)); 607 608 if(nSizeDx > 1) 609 { 610 // fill deltas to maX 611 maX.reserve(nSizeDx); 612 613 for(sal_uInt32 a(1); a < nSizeDx; a++) 614 { 615 maX.push_back(rSvgTextPositions.getDx()[a].solve(rInfoProvider, xcoordinate)); 616 } 617 } 618 } 619 } 620 621 // get text positions Y 622 const sal_uInt32 nSizeY(rSvgTextPositions.getY().size()); 623 624 if(nSizeY) 625 { 626 // we have absolute positions, get first one as current text position Y 627 maPosition.setY(rSvgTextPositions.getY()[0].solve(rInfoProvider, ycoordinate)); 628 mbAbsoluteX = true; 629 630 if(nSizeY > 1) 631 { 632 // fill deltas to maY 633 maY.reserve(nSizeY); 634 635 for(sal_uInt32 a(1); a < nSizeY; a++) 636 { 637 maY.push_back(rSvgTextPositions.getY()[a].solve(rInfoProvider, ycoordinate) - maPosition.getY()); 638 } 639 } 640 } 641 else 642 { 643 // no absolute position, get from parent 644 if(pParent) 645 { 646 maPosition.setY(pParent->getPosition().getY()); 647 } 648 649 const sal_uInt32 nSizeDy(rSvgTextPositions.getDy().size()); 650 651 if(nSizeDy) 652 { 653 // relative positions given, translate position derived from parent 654 maPosition.setY(maPosition.getY() + rSvgTextPositions.getDy()[0].solve(rInfoProvider, ycoordinate)); 655 656 if(nSizeDy > 1) 657 { 658 // fill deltas to maY 659 maY.reserve(nSizeDy); 660 661 for(sal_uInt32 a(1); a < nSizeDy; a++) 662 { 663 maY.push_back(rSvgTextPositions.getDy()[a].solve(rInfoProvider, ycoordinate)); 664 } 665 } 666 } 667 } 668 } 669 670 bool SvgTextPosition::isRotated() const 671 { 672 if(maRotate.empty()) 673 { 674 if(getParent()) 675 { 676 return getParent()->isRotated(); 677 } 678 else 679 { 680 return false; 681 } 682 } 683 else 684 { 685 return true; 686 } 687 } 688 689 double SvgTextPosition::consumeRotation() 690 { 691 double fRetval(0.0); 692 693 if(maRotate.empty()) 694 { 695 if(getParent()) 696 { 697 fRetval = mpParent->consumeRotation(); 698 } 699 else 700 { 701 fRetval = 0.0; 702 } 703 } 704 else 705 { 706 const sal_uInt32 nSize(maRotate.size()); 707 708 if(mnRotationIndex < nSize) 709 { 710 fRetval = maRotate[mnRotationIndex++]; 711 } 712 else 713 { 714 fRetval = maRotate[nSize - 1]; 715 } 716 } 717 718 return fRetval; 719 } 720 721 } // end of namespace svgreader 722 } // end of namespace svgio 723 724 ////////////////////////////////////////////////////////////////////////////// 725 // eof 726