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/svgnode.hxx> 26 #include <basegfx/polygon/b2dpolypolygontools.hxx> 27 #include <svgio/svgreader/svgdocument.hxx> 28 #include <svgio/svgreader/svgnode.hxx> 29 #include <svgio/svgreader/svgstyleattributes.hxx> 30 #include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx> 31 #include <tools/urlobj.hxx> 32 33 ////////////////////////////////////////////////////////////////////////////// 34 35 namespace svgio 36 { 37 namespace svgreader 38 { 39 /// #125258# 40 bool SvgNode::supportsParentStyle() const 41 { 42 return true; 43 } 44 45 const SvgStyleAttributes* SvgNode::getSvgStyleAttributes() const 46 { 47 return 0; 48 } 49 50 void SvgNode::fillCssStyleVectorUsingHierarchyAndSelectors( 51 const rtl::OUString& rClassStr, 52 const SvgNode& rCurrent, 53 rtl::OUString aConcatenated) 54 { 55 const SvgDocument& rDocument = getDocument(); 56 57 if(rDocument.hasGlobalCssStyleAttributes()) 58 { 59 const SvgNode* pParent = rCurrent.getParent(); 60 61 // check for ID (highest priority) 62 if(rCurrent.getId()) 63 { 64 const rtl::OUString& rId = *rCurrent.getId(); 65 66 if(rId.getLength()) 67 { 68 const rtl::OUString aNewConcatenated( 69 rtl::OUString::createFromAscii("#") + 70 rId + 71 aConcatenated); 72 73 if(pParent) 74 { 75 // check for combined selectors at parent firstso that higher specificity will be in front 76 fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr, *pParent, aNewConcatenated); 77 } 78 79 const SvgStyleAttributes* pNew = rDocument.findGlobalCssStyleAttributes(aNewConcatenated); 80 81 if(pNew) 82 { 83 // add CssStyle if found 84 maCssStyleVector.push_back(pNew); 85 } 86 } 87 } 88 89 // check for 'class' references (a list of entries is allowed) 90 if(rCurrent.getClass()) 91 { 92 const rtl::OUString& rClassList = *rCurrent.getClass(); 93 const sal_Int32 nLen(rClassList.getLength()); 94 95 if(nLen) 96 { 97 std::vector< rtl::OUString > aParts; 98 sal_Int32 nPos(0); 99 rtl::OUStringBuffer aToken; 100 101 while(nPos < nLen) 102 { 103 const sal_Int32 nInitPos(nPos); 104 copyToLimiter(rClassList, sal_Unicode(' '), nPos, aToken, nLen); 105 skip_char(rClassList, sal_Unicode(' '), nPos, nLen); 106 const rtl::OUString aPart(aToken.makeStringAndClear().trim()); 107 108 if(aPart.getLength()) 109 { 110 aParts.push_back(aPart); 111 } 112 113 if(nInitPos == nPos) 114 { 115 OSL_ENSURE(false, "Could not interpret on current position (!)"); 116 nPos++; 117 } 118 } 119 120 for(sal_uInt32 a(0); a < aParts.size(); a++) 121 { 122 const rtl::OUString aNewConcatenated( 123 rtl::OUString::createFromAscii(".") + 124 aParts[a] + 125 aConcatenated); 126 127 if(pParent) 128 { 129 // check for combined selectors at parent firstso that higher specificity will be in front 130 fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr, *pParent, aNewConcatenated); 131 } 132 133 const SvgStyleAttributes* pNew = rDocument.findGlobalCssStyleAttributes(aNewConcatenated); 134 135 if(pNew) 136 { 137 // add CssStyle if found 138 maCssStyleVector.push_back(pNew); 139 } 140 } 141 } 142 } 143 144 // check for class-dependent references to CssStyles 145 if(rClassStr.getLength()) 146 { 147 rtl::OUString aNewConcatenated(aConcatenated); 148 149 if(!rCurrent.getId() && !rCurrent.getClass() && 0 == aConcatenated.indexOf(rClassStr)) 150 { 151 // no new CssStyle Selector and already starts with rClassStr, do not concatenate; 152 // we pass an 'empty' node (in the sense of CssStyle Selector) 153 } 154 else 155 { 156 aNewConcatenated = rClassStr + aConcatenated; 157 } 158 159 if(pParent) 160 { 161 // check for combined selectors at parent firstso that higher specificity will be in front 162 fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr, *pParent, aNewConcatenated); 163 } 164 165 const SvgStyleAttributes* pNew = rDocument.findGlobalCssStyleAttributes(aNewConcatenated); 166 167 if(pNew) 168 { 169 // add CssStyle if found 170 maCssStyleVector.push_back(pNew); 171 } 172 } 173 } 174 } 175 176 void SvgNode::fillCssStyleVector(const rtl::OUString& rClassStr) 177 { 178 OSL_ENSURE(!mbCssStyleVectorBuilt, "OOps, fillCssStyleVector called double ?!?"); 179 mbCssStyleVectorBuilt = true; 180 181 // #125293# If we have CssStyles we need to buuild a linked list of SvgStyleAttributes 182 // which represent this for the current object. There are various methods to 183 // specify CssStyles which need to be taken into account in a given order: 184 // - local CssStyle (independent from global CssStyles at SvgDocument) 185 // - 'id' CssStyle 186 // - 'class' CssStyle(s) 187 // - type-dependent elements (e..g. 'rect' for all rect elements) 188 // - local attributes (rOriginal) 189 // - inherited attributes (up the hierarchy) 190 // The first four will be collected in maCssStyleVector for the current element 191 // (once, this will not change) and be linked in the needed order using the 192 // get/setCssStyleParent at the SvgStyleAttributes which will be used preferred in 193 // member evaluation over the existing parent hierarchy 194 195 // check for local CssStyle with highest priority 196 if(mpLocalCssStyle) 197 { 198 // if we have one, use as first entry 199 maCssStyleVector.push_back(mpLocalCssStyle); 200 } 201 202 // check the hierarchy for concatenated patterns of Selectors 203 fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr, *this, rtl::OUString()); 204 205 // #125329# find Css selector '*', add as last element if found 206 const SvgStyleAttributes* pNew = getDocument().findGlobalCssStyleAttributes(rtl::OUString::createFromAscii("*")); 207 208 if(pNew) 209 { 210 // add CssStyle for selector '*' if found 211 maCssStyleVector.push_back(pNew); 212 } 213 } 214 215 const SvgStyleAttributes* SvgNode::checkForCssStyle(const rtl::OUString& rClassStr, const SvgStyleAttributes& rOriginal) const 216 { 217 if(!mbCssStyleVectorBuilt) 218 { 219 // build needed CssStyleVector for local node 220 const_cast< SvgNode* >(this)->fillCssStyleVector(rClassStr); 221 } 222 223 if(maCssStyleVector.empty()) 224 { 225 // return given original if no CssStlyes found 226 return &rOriginal; 227 } 228 else 229 { 230 // #125293# rOriginal will be the last element in the linked list; use no CssStyleParent 231 // there (reset it) to ensure that the parent hierarchy will be used when it's base 232 // is referenced. This new chaning inserts the CssStyles before the original style, 233 // this makes the whole process much safer since the original style when used will 234 // be not different to the situation without CssStyles; thus loops which may be caused 235 // by trying to use the parent hierarchy of the owner of the style will be avoided 236 // already in this mechanism. It's still good to keep the supportsParentStyle 237 // from #125258# in place, though. 238 // This chain building using pointers will be done every time when checkForCssStyle 239 // is used (not the search, only the chaining). This is needed since the CssStyles 240 // themselves will be potentially used multiple times. It is not expensive since it's 241 // only changing some pointers. 242 // The alternative would be to create the style hierarchy for every element (or even 243 // for the element containing the hierarchy) in a vector of pointers and to use that. 244 // Resetting the CssStyleParent on rOriginal is probably not needeed 245 // but simply safer to do. 246 const_cast< SvgStyleAttributes& >(rOriginal).setCssStyleParent(0); 247 248 // loop over the existing CssStyles and link them. There is a first one, take 249 // as current 250 SvgStyleAttributes* pCurrent = const_cast< SvgStyleAttributes* >(maCssStyleVector[0]); 251 252 for(sal_uInt32 a(1); a < maCssStyleVector.size(); a++) 253 { 254 SvgStyleAttributes* pNext = const_cast< SvgStyleAttributes* >(maCssStyleVector[a]); 255 256 pCurrent->setCssStyleParent(pNext); 257 pCurrent = pNext; 258 } 259 260 // pCurrent is the last used CssStyle, let it point to the original style 261 pCurrent->setCssStyleParent(&rOriginal); 262 263 // return 1st CssStyle as style chain start element (only for the 264 // local element, still no hierarchy used here) 265 return maCssStyleVector[0]; 266 } 267 } 268 269 SvgNode::SvgNode( 270 SVGToken aType, 271 SvgDocument& rDocument, 272 SvgNode* pParent) 273 : maType(aType), 274 mrDocument(rDocument), 275 mpParent(pParent), 276 mpAlternativeParent(0), 277 maChildren(), 278 mpId(0), 279 mpClass(0), 280 maXmlSpace(XmlSpace_notset), 281 maDisplay(Display_inline), 282 maCssStyleVector(), 283 mpLocalCssStyle(0), 284 mbCssStyleVectorBuilt(false) 285 { 286 OSL_ENSURE(SVGTokenUnknown != maType, "SvgNode with unknown type created (!)"); 287 288 if(pParent) 289 { 290 pParent->maChildren.push_back(this); 291 } 292 else 293 { 294 #ifdef DBG_UTIL 295 if(SVGTokenSvg != getType()) 296 { 297 OSL_ENSURE(false, "No parent for this node (!)"); 298 } 299 #endif 300 } 301 } 302 303 SvgNode::~SvgNode() 304 { 305 while(maChildren.size()) 306 { 307 delete maChildren[maChildren.size() - 1]; 308 maChildren.pop_back(); 309 } 310 311 if(mpId) 312 { 313 delete mpId; 314 } 315 316 if(mpClass) 317 { 318 delete mpClass; 319 } 320 321 if(mpLocalCssStyle) 322 { 323 delete mpLocalCssStyle; 324 } 325 } 326 327 void SvgNode::readLocalCssStyle(const rtl::OUString& aContent) 328 { 329 if(!mpLocalCssStyle) 330 { 331 // create LocalCssStyle if needed but not yet added 332 mpLocalCssStyle = new SvgStyleAttributes(*this); 333 } 334 else 335 { 336 // 2nd fill would be an error 337 OSL_ENSURE(false, "Svg node has two local CssStyles, this may lead to problems (!)"); 338 } 339 340 if(mpLocalCssStyle) 341 { 342 // parse and set values to it 343 mpLocalCssStyle->readCssStyle(aContent); 344 } 345 else 346 { 347 OSL_ENSURE(false, "Could not get/create a local CssStyle for a node (!)"); 348 } 349 } 350 351 void SvgNode::parseAttributes(const com::sun::star::uno::Reference< com::sun::star::xml::sax::XAttributeList >& xAttribs) 352 { 353 // no longer need to pre-sort moving 'style' entries to the back so that 354 // values get overwritten - that was the previous, not complete solution for 355 // handling the priorities between svg and Css properties 356 const sal_uInt32 nAttributes(xAttribs->getLength()); 357 358 for(sal_uInt32 a(0); a < nAttributes; a++) 359 { 360 const ::rtl::OUString aTokenName(xAttribs->getNameByIndex(a)); 361 const SVGToken aSVGToken(StrToSVGToken(aTokenName, false)); 362 363 parseAttribute(aTokenName, aSVGToken, xAttribs->getValueByIndex(a)); 364 } 365 } 366 367 Display getDisplayFromContent(const rtl::OUString& aContent) 368 { 369 if(aContent.getLength()) 370 { 371 static rtl::OUString aStrInline(rtl::OUString::createFromAscii("inline")); 372 static rtl::OUString aStrBlock(rtl::OUString::createFromAscii("block")); 373 static rtl::OUString aStrList_item(rtl::OUString::createFromAscii("list-item")); 374 static rtl::OUString aStrRun_in(rtl::OUString::createFromAscii("run-in")); 375 static rtl::OUString aStrCompact(rtl::OUString::createFromAscii("compact")); 376 static rtl::OUString aStrMarker(rtl::OUString::createFromAscii("marker")); 377 static rtl::OUString aStrTable(rtl::OUString::createFromAscii("table")); 378 static rtl::OUString aStrInline_table(rtl::OUString::createFromAscii("inline-table")); 379 static rtl::OUString aStrTable_row_group(rtl::OUString::createFromAscii("table-row-group")); 380 static rtl::OUString aStrTable_header_group(rtl::OUString::createFromAscii("table-header-group")); 381 static rtl::OUString aStrTable_footer_group(rtl::OUString::createFromAscii("table-footer-group")); 382 static rtl::OUString aStrTable_row(rtl::OUString::createFromAscii("table-row")); 383 static rtl::OUString aStrTable_column_group(rtl::OUString::createFromAscii("table-column-group")); 384 static rtl::OUString aStrTable_column(rtl::OUString::createFromAscii("table-column")); 385 static rtl::OUString aStrTable_cell(rtl::OUString::createFromAscii("table-cell")); 386 static rtl::OUString aStrTable_caption(rtl::OUString::createFromAscii("table-caption")); 387 static rtl::OUString aStrNone(rtl::OUString::createFromAscii("none")); 388 static rtl::OUString aStrInherit(rtl::OUString::createFromAscii("inherit")); 389 390 if(aContent.match(aStrInline)) 391 { 392 return Display_inline; 393 } 394 else if(aContent.match(aStrNone)) 395 { 396 return Display_none; 397 } 398 else if(aContent.match(aStrInherit)) 399 { 400 return Display_inherit; 401 } 402 else if(aContent.match(aStrBlock)) 403 { 404 return Display_block; 405 } 406 else if(aContent.match(aStrList_item)) 407 { 408 return Display_list_item; 409 } 410 else if(aContent.match(aStrRun_in)) 411 { 412 return Display_run_in; 413 } 414 else if(aContent.match(aStrCompact)) 415 { 416 return Display_compact; 417 } 418 else if(aContent.match(aStrMarker)) 419 { 420 return Display_marker; 421 } 422 else if(aContent.match(aStrTable)) 423 { 424 return Display_table; 425 } 426 else if(aContent.match(aStrInline_table)) 427 { 428 return Display_inline_table; 429 } 430 else if(aContent.match(aStrTable_row_group)) 431 { 432 return Display_table_row_group; 433 } 434 else if(aContent.match(aStrTable_header_group)) 435 { 436 return Display_table_header_group; 437 } 438 else if(aContent.match(aStrTable_footer_group)) 439 { 440 return Display_table_footer_group; 441 } 442 else if(aContent.match(aStrTable_row)) 443 { 444 return Display_table_row; 445 } 446 else if(aContent.match(aStrTable_column_group)) 447 { 448 return Display_table_column_group; 449 } 450 else if(aContent.match(aStrTable_column)) 451 { 452 return Display_table_column; 453 } 454 else if(aContent.match(aStrTable_cell)) 455 { 456 return Display_table_cell; 457 } 458 else if(aContent.match(aStrTable_caption)) 459 { 460 return Display_table_caption; 461 } 462 } 463 464 // return the default 465 return Display_inline; 466 } 467 468 void SvgNode::parseAttribute(const rtl::OUString& /*rTokenName*/, SVGToken aSVGToken, const rtl::OUString& aContent) 469 { 470 switch(aSVGToken) 471 { 472 case SVGTokenId: 473 { 474 if(aContent.getLength()) 475 { 476 setId(&aContent); 477 } 478 break; 479 } 480 case SVGTokenClass: 481 { 482 if(aContent.getLength()) 483 { 484 setClass(&aContent); 485 } 486 break; 487 } 488 case SVGTokenXmlSpace: 489 { 490 if(aContent.getLength()) 491 { 492 static rtl::OUString aStrDefault(rtl::OUString::createFromAscii("default")); 493 static rtl::OUString aStrPreserve(rtl::OUString::createFromAscii("preserve")); 494 495 if(aContent.match(aStrDefault)) 496 { 497 setXmlSpace(XmlSpace_default); 498 } 499 else if(aContent.match(aStrPreserve)) 500 { 501 setXmlSpace(XmlSpace_preserve); 502 } 503 } 504 break; 505 } 506 case SVGTokenDisplay: 507 { 508 if(aContent.getLength()) 509 { 510 setDisplay(getDisplayFromContent(aContent)); 511 } 512 break; 513 } 514 default: 515 { 516 break; 517 } 518 } 519 } 520 521 void SvgNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool bReferenced) const 522 { 523 if(Display_none == getDisplay()) 524 { 525 return; 526 } 527 528 if(!bReferenced) 529 { 530 if(SVGTokenDefs == getType() || 531 SVGTokenSymbol == getType() || 532 SVGTokenClipPathNode == getType() || 533 SVGTokenMask == getType() || 534 SVGTokenMarker == getType() || 535 SVGTokenPattern == getType()) 536 { 537 // do not decompose defs or symbol nodes (these hold only style-like 538 // objects which may be used by referencing them) except when doing 539 // so controlled referenced 540 541 // also do not decompose ClipPaths and Masks. These should be embedded 542 // in a defs node (which gets not decomposed by itself), but you never 543 // know 544 545 // also not directly used are Markers and Patterns, only indirecty used 546 // by reference 547 548 // #121656# also do not decompose nodes which have display="none" set 549 // as property 550 return; 551 } 552 } 553 554 const SvgNodeVector& rChildren = getChildren(); 555 556 if(!rChildren.empty()) 557 { 558 const sal_uInt32 nCount(rChildren.size()); 559 560 for(sal_uInt32 a(0); a < nCount; a++) 561 { 562 SvgNode* pCandidate = rChildren[a]; 563 564 if(pCandidate && Display_none != pCandidate->getDisplay()) 565 { 566 drawinglayer::primitive2d::Primitive2DSequence aNewTarget; 567 568 pCandidate->decomposeSvgNode(aNewTarget, bReferenced); 569 570 if(aNewTarget.hasElements()) 571 { 572 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewTarget); 573 } 574 } 575 else 576 { 577 OSL_ENSURE(false, "Null-Pointer in child node list (!)"); 578 } 579 } 580 581 if(rTarget.hasElements()) 582 { 583 const SvgStyleAttributes* pStyles = getSvgStyleAttributes(); 584 585 if(pStyles) 586 { 587 // check if we have Title or Desc 588 const rtl::OUString& rTitle = pStyles->getTitle(); 589 const rtl::OUString& rDesc = pStyles->getDesc(); 590 591 if(rTitle.getLength() || rDesc.getLength()) 592 { 593 // default object name is empty 594 rtl::OUString aObjectName; 595 596 // use path as object name when outmost element 597 if(SVGTokenSvg == getType()) 598 { 599 aObjectName = getDocument().getAbsolutePath(); 600 601 if(aObjectName.getLength()) 602 { 603 INetURLObject aURL(aObjectName); 604 605 aObjectName = aURL.getName( 606 INetURLObject::LAST_SEGMENT, 607 true, 608 INetURLObject::DECODE_WITH_CHARSET); 609 } 610 } 611 612 // pack in ObjectInfoPrimitive2D group 613 const drawinglayer::primitive2d::Primitive2DReference xRef( 614 new drawinglayer::primitive2d::ObjectInfoPrimitive2D( 615 rTarget, 616 aObjectName, 617 rTitle, 618 rDesc)); 619 620 rTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1); 621 } 622 } 623 } 624 } 625 } 626 627 const basegfx::B2DRange SvgNode::getCurrentViewPort() const 628 { 629 if(getParent()) 630 { 631 return getParent()->getCurrentViewPort(); 632 } 633 else 634 { 635 return basegfx::B2DRange(); // return empty B2DRange 636 } 637 } 638 639 double SvgNode::getCurrentFontSize() const 640 { 641 if(getSvgStyleAttributes()) 642 { 643 return getSvgStyleAttributes()->getFontSize().solve(*this, xcoordinate); 644 } 645 else if(getParent()) 646 { 647 return getParent()->getCurrentFontSize(); 648 } 649 else 650 { 651 return 0.0; 652 } 653 } 654 655 double SvgNode::getCurrentXHeight() const 656 { 657 if(getSvgStyleAttributes()) 658 { 659 // for XHeight, use FontSize currently 660 return getSvgStyleAttributes()->getFontSize().solve(*this, ycoordinate); 661 } 662 else if(getParent()) 663 { 664 return getParent()->getCurrentXHeight(); 665 } 666 else 667 { 668 return 0.0; 669 } 670 } 671 672 void SvgNode::setId(const rtl::OUString* pfId) 673 { 674 if(mpId) 675 { 676 mrDocument.removeSvgNodeFromMapper(*mpId); 677 delete mpId; 678 mpId = 0; 679 } 680 681 if(pfId) 682 { 683 mpId = new rtl::OUString(*pfId); 684 mrDocument.addSvgNodeToMapper(*mpId, *this); 685 } 686 } 687 688 void SvgNode::setClass(const rtl::OUString* pfClass) 689 { 690 if(mpClass) 691 { 692 mrDocument.removeSvgNodeFromMapper(*mpClass); 693 delete mpClass; 694 mpClass = 0; 695 } 696 697 if(pfClass) 698 { 699 mpClass = new rtl::OUString(*pfClass); 700 mrDocument.addSvgNodeToMapper(*mpClass, *this); 701 } 702 } 703 704 XmlSpace SvgNode::getXmlSpace() const 705 { 706 if(maXmlSpace != XmlSpace_notset) 707 { 708 return maXmlSpace; 709 } 710 711 if(getParent()) 712 { 713 return getParent()->getXmlSpace(); 714 } 715 716 // default is XmlSpace_default 717 return XmlSpace_default; 718 } 719 720 } // end of namespace svgreader 721 } // end of namespace svgio 722 723 ////////////////////////////////////////////////////////////////////////////// 724 // eof 725