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