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