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