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/svgdocumenthandler.hxx> 26 #include <svgio/svgreader/svgtoken.hxx> 27 #include <svgio/svgreader/svgsvgnode.hxx> 28 #include <svgio/svgreader/svggnode.hxx> 29 #include <svgio/svgreader/svgnode.hxx> 30 #include <svgio/svgreader/svgpathnode.hxx> 31 #include <svgio/svgreader/svgrectnode.hxx> 32 #include <svgio/svgreader/svggradientnode.hxx> 33 #include <svgio/svgreader/svggradientstopnode.hxx> 34 #include <svgio/svgreader/svgsymbolnode.hxx> 35 #include <svgio/svgreader/svgusenode.hxx> 36 #include <svgio/svgreader/svgcirclenode.hxx> 37 #include <svgio/svgreader/svgellipsenode.hxx> 38 #include <svgio/svgreader/svglinenode.hxx> 39 #include <svgio/svgreader/svgpolynode.hxx> 40 #include <svgio/svgreader/svgsymbolnode.hxx> 41 #include <svgio/svgreader/svgtextnode.hxx> 42 #include <svgio/svgreader/svgcharacternode.hxx> 43 #include <svgio/svgreader/svgtspannode.hxx> 44 #include <svgio/svgreader/svgtrefnode.hxx> 45 #include <svgio/svgreader/svgtextpathnode.hxx> 46 #include <svgio/svgreader/svgstylenode.hxx> 47 #include <svgio/svgreader/svgimagenode.hxx> 48 #include <svgio/svgreader/svgclippathnode.hxx> 49 #include <svgio/svgreader/svgmasknode.hxx> 50 #include <svgio/svgreader/svgmarkernode.hxx> 51 #include <svgio/svgreader/svgpatternnode.hxx> 52 #include <svgio/svgreader/svgtitledescnode.hxx> 53 54 ////////////////////////////////////////////////////////////////////////////// 55 56 using namespace com::sun::star; 57 58 ////////////////////////////////////////////////////////////////////////////// 59 60 namespace 61 { 62 svgio::svgreader::SvgCharacterNode* whiteSpaceHandling(svgio::svgreader::SvgNode* pNode, svgio::svgreader::SvgCharacterNode* pLast) 63 { 64 if(pNode) 65 { 66 const svgio::svgreader::SvgNodeVector& rChilds = pNode->getChildren(); 67 const sal_uInt32 nCount(rChilds.size()); 68 69 for(sal_uInt32 a(0); a < nCount; a++) 70 { 71 svgio::svgreader::SvgNode* pCandidate = rChilds[a]; 72 73 if(pCandidate) 74 { 75 switch(pCandidate->getType()) 76 { 77 case svgio::svgreader::SVGTokenCharacter: 78 { 79 // clean whitespace in text span 80 svgio::svgreader::SvgCharacterNode* pCharNode = static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate); 81 pCharNode->whiteSpaceHandling(); 82 83 // pCharNode may have lost all text. If that's the case, ignore 84 // as invalid character node 85 if(pCharNode->getText().getLength()) 86 { 87 if(pLast) 88 { 89 bool bAddGap(true); 90 static bool bNoGapsForBaselineShift(true); 91 92 if(bNoGapsForBaselineShift) 93 { 94 // With this option a baseline shift between two char parts ('words') 95 // will not add a space 'gap' to the end of the (non-last) word. This 96 // seems to be the standard behaviour, see last bugdoc attached #122524# 97 const svgio::svgreader::SvgStyleAttributes* pStyleLast = pLast->getSvgStyleAttributes(); 98 const svgio::svgreader::SvgStyleAttributes* pStyleCurrent = pCandidate->getSvgStyleAttributes(); 99 100 if(pStyleLast && pStyleCurrent && pStyleLast->getBaselineShift() != pStyleCurrent->getBaselineShift()) 101 { 102 bAddGap = false; 103 } 104 } 105 106 // add in-between whitespace (single space) to last 107 // known character node 108 if(bAddGap) 109 { 110 pLast->addGap(); 111 } 112 } 113 114 // remember new last corected character node 115 pLast = pCharNode; 116 } 117 break; 118 } 119 case svgio::svgreader::SVGTokenTspan: 120 case svgio::svgreader::SVGTokenTextPath: 121 case svgio::svgreader::SVGTokenTref: 122 { 123 // recursively clean whitespaces in subhierarchy 124 pLast = whiteSpaceHandling(pCandidate, pLast); 125 break; 126 } 127 default: 128 { 129 OSL_ENSURE(false, "Unexpected token inside SVGTokenText (!)"); 130 break; 131 } 132 } 133 } 134 } 135 } 136 137 return pLast; 138 } 139 } 140 141 ////////////////////////////////////////////////////////////////////////////// 142 143 namespace svgio 144 { 145 namespace svgreader 146 { 147 SvgDocHdl::SvgDocHdl(const rtl::OUString& aAbsolutePath) 148 : maDocument(aAbsolutePath), 149 mpTarget(0), 150 maCssContents() 151 { 152 } 153 154 SvgDocHdl::~SvgDocHdl() 155 { 156 #ifdef DBG_UTIL 157 if(mpTarget) 158 { 159 OSL_ENSURE(false, "SvgDocHdl destructed with active target (!)"); 160 delete mpTarget; 161 } 162 OSL_ENSURE(!maCssContents.size(), "SvgDocHdl destructed with active css style stack entry (!)"); 163 #endif 164 } 165 166 void SvgDocHdl::startDocument( ) throw (xml::sax::SAXException, uno::RuntimeException) 167 { 168 OSL_ENSURE(!mpTarget, "Already a target at document start (!)"); 169 OSL_ENSURE(!maCssContents.size(), "SvgDocHdl startDocument with active css style stack entry (!)"); 170 } 171 172 void SvgDocHdl::endDocument( ) throw (xml::sax::SAXException, uno::RuntimeException) 173 { 174 OSL_ENSURE(!mpTarget, "Still a target at document end (!)"); 175 OSL_ENSURE(!maCssContents.size(), "SvgDocHdl endDocument with active css style stack entry (!)"); 176 } 177 178 void SvgDocHdl::startElement( const ::rtl::OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs ) throw (xml::sax::SAXException, uno::RuntimeException) 179 { 180 if(aName.getLength()) 181 { 182 const SVGToken aSVGToken(StrToSVGToken(aName, false)); 183 184 switch(aSVGToken) 185 { 186 /// structural elements 187 case SVGTokenSymbol: 188 { 189 /// new basic node for Symbol. Content gets scanned, but 190 /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced) 191 mpTarget = new SvgSymbolNode(maDocument, mpTarget); 192 mpTarget->parseAttributes(xAttribs); 193 break; 194 } 195 case SVGTokenDefs: 196 case SVGTokenG: 197 { 198 /// new node for Defs/G 199 mpTarget = new SvgGNode(aSVGToken, maDocument, mpTarget); 200 mpTarget->parseAttributes(xAttribs); 201 break; 202 } 203 case SVGTokenSvg: 204 { 205 /// new node for Svg 206 mpTarget = new SvgSvgNode(maDocument, mpTarget); 207 mpTarget->parseAttributes(xAttribs); 208 break; 209 } 210 case SVGTokenUse: 211 { 212 /// new node for Use 213 mpTarget = new SvgUseNode(maDocument, mpTarget); 214 mpTarget->parseAttributes(xAttribs); 215 break; 216 } 217 218 /// shape elements 219 case SVGTokenCircle: 220 { 221 /// new node for Circle 222 mpTarget = new SvgCircleNode(maDocument, mpTarget); 223 mpTarget->parseAttributes(xAttribs); 224 break; 225 } 226 case SVGTokenEllipse: 227 { 228 /// new node for Ellipse 229 mpTarget = new SvgEllipseNode(maDocument, mpTarget); 230 mpTarget->parseAttributes(xAttribs); 231 break; 232 } 233 case SVGTokenLine: 234 { 235 /// new node for Line 236 mpTarget = new SvgLineNode(maDocument, mpTarget); 237 mpTarget->parseAttributes(xAttribs); 238 break; 239 } 240 case SVGTokenPath: 241 { 242 /// new node for Path 243 mpTarget = new SvgPathNode(maDocument, mpTarget); 244 mpTarget->parseAttributes(xAttribs); 245 break; 246 } 247 case SVGTokenPolygon: 248 { 249 /// new node for Polygon 250 mpTarget = new SvgPolyNode(maDocument, mpTarget, false); 251 mpTarget->parseAttributes(xAttribs); 252 break; 253 } 254 case SVGTokenPolyline: 255 { 256 /// new node for Polyline 257 mpTarget = new SvgPolyNode(maDocument, mpTarget, true); 258 mpTarget->parseAttributes(xAttribs); 259 break; 260 } 261 case SVGTokenRect: 262 { 263 /// new node for Rect 264 mpTarget = new SvgRectNode(maDocument, mpTarget); 265 mpTarget->parseAttributes(xAttribs); 266 break; 267 } 268 case SVGTokenImage: 269 { 270 /// new node for Image 271 mpTarget = new SvgImageNode(maDocument, mpTarget); 272 mpTarget->parseAttributes(xAttribs); 273 break; 274 } 275 276 /// title and description 277 case SVGTokenTitle: 278 case SVGTokenDesc: 279 { 280 /// new node for Title and/or Desc 281 mpTarget = new SvgTitleDescNode(aSVGToken, maDocument, mpTarget); 282 break; 283 } 284 285 /// gradients 286 case SVGTokenLinearGradient: 287 case SVGTokenRadialGradient: 288 { 289 mpTarget = new SvgGradientNode(aSVGToken, maDocument, mpTarget); 290 mpTarget->parseAttributes(xAttribs); 291 break; 292 } 293 294 /// gradient stops 295 case SVGTokenStop: 296 { 297 mpTarget = new SvgGradientStopNode(maDocument, mpTarget); 298 mpTarget->parseAttributes(xAttribs); 299 break; 300 } 301 302 /// text 303 case SVGTokenText: 304 { 305 mpTarget = new SvgTextNode(maDocument, mpTarget); 306 mpTarget->parseAttributes(xAttribs); 307 break; 308 } 309 case SVGTokenTspan: 310 { 311 mpTarget = new SvgTspanNode(maDocument, mpTarget); 312 mpTarget->parseAttributes(xAttribs); 313 break; 314 } 315 case SVGTokenTref: 316 { 317 mpTarget = new SvgTrefNode(maDocument, mpTarget); 318 mpTarget->parseAttributes(xAttribs); 319 break; 320 } 321 case SVGTokenTextPath: 322 { 323 mpTarget = new SvgTextPathNode(maDocument, mpTarget); 324 mpTarget->parseAttributes(xAttribs); 325 break; 326 } 327 328 /// styles (as stylesheets) 329 case SVGTokenStyle: 330 { 331 SvgStyleNode* pNew = new SvgStyleNode(maDocument, mpTarget); 332 mpTarget = pNew; 333 const sal_uInt32 nAttributes(xAttribs->getLength()); 334 335 if(0 == nAttributes) 336 { 337 // #125326# no attributes, thus also no type="text/css". This is allowed to be missing, 338 // thus do mark this style as CssStyle. This is required to read the contained 339 // text (which defines the css style) 340 pNew->setTextCss(true); 341 } 342 else 343 { 344 // #125326# there are attributes, read them. This will set isTextCss to true if 345 // a type="text/css" is contained as exact match, else not 346 mpTarget->parseAttributes(xAttribs); 347 } 348 349 if(pNew->isTextCss()) 350 { 351 // if it is a Css style, allow reading text between the start and end tag (see 352 // SvgDocHdl::characters for details) 353 maCssContents.push_back(rtl::OUString()); 354 } 355 break; 356 } 357 358 /// structural elements clip-path and mask. Content gets scanned, but 359 /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced) 360 case SVGTokenClipPathNode: 361 { 362 /// new node for ClipPath 363 mpTarget = new SvgClipPathNode(maDocument, mpTarget); 364 mpTarget->parseAttributes(xAttribs); 365 break; 366 } 367 case SVGTokenMask: 368 { 369 /// new node for Mask 370 mpTarget = new SvgMaskNode(maDocument, mpTarget); 371 mpTarget->parseAttributes(xAttribs); 372 break; 373 } 374 375 /// structural element marker 376 case SVGTokenMarker: 377 { 378 /// new node for marker 379 mpTarget = new SvgMarkerNode(maDocument, mpTarget); 380 mpTarget->parseAttributes(xAttribs); 381 break; 382 } 383 384 /// structural element pattern 385 case SVGTokenPattern: 386 { 387 /// new node for pattern 388 mpTarget = new SvgPatternNode(maDocument, mpTarget); 389 mpTarget->parseAttributes(xAttribs); 390 break; 391 } 392 393 default: 394 { 395 /// invalid token, ignore 396 #ifdef DBG_UTIL 397 myAssert( 398 rtl::OUString::createFromAscii("Unknown Base SvgToken <") + 399 aName + 400 rtl::OUString::createFromAscii("> (!)")); 401 #endif 402 break; 403 } 404 } 405 } 406 } 407 408 void SvgDocHdl::endElement( const ::rtl::OUString& aName ) throw (xml::sax::SAXException, uno::RuntimeException) 409 { 410 if(aName.getLength()) 411 { 412 const SVGToken aSVGToken(StrToSVGToken(aName, false)); 413 SvgNode* pWhitespaceCheck(SVGTokenText == aSVGToken ? mpTarget : 0); 414 SvgStyleNode* pCssStyle(SVGTokenStyle == aSVGToken ? static_cast< SvgStyleNode* >(mpTarget) : 0); 415 SvgTitleDescNode* pSvgTitleDescNode(SVGTokenTitle == aSVGToken || SVGTokenDesc == aSVGToken ? static_cast< SvgTitleDescNode* >(mpTarget) : 0); 416 417 switch(aSVGToken) 418 { 419 /// valid tokens for which a new one was created 420 421 /// structural elements 422 case SVGTokenDefs: 423 case SVGTokenG: 424 case SVGTokenSvg: 425 case SVGTokenSymbol: 426 case SVGTokenUse: 427 428 /// shape elements 429 case SVGTokenCircle: 430 case SVGTokenEllipse: 431 case SVGTokenLine: 432 case SVGTokenPath: 433 case SVGTokenPolygon: 434 case SVGTokenPolyline: 435 case SVGTokenRect: 436 case SVGTokenImage: 437 438 /// title and description 439 case SVGTokenTitle: 440 case SVGTokenDesc: 441 442 /// gradients 443 case SVGTokenLinearGradient: 444 case SVGTokenRadialGradient: 445 446 /// gradient stops 447 case SVGTokenStop: 448 449 /// text 450 case SVGTokenText: 451 case SVGTokenTspan: 452 case SVGTokenTextPath: 453 case SVGTokenTref: 454 455 /// styles (as stylesheets) 456 case SVGTokenStyle: 457 458 /// structural elements clip-path and mask 459 case SVGTokenClipPathNode: 460 case SVGTokenMask: 461 462 /// structural element marker 463 case SVGTokenMarker: 464 465 /// structural element pattern 466 case SVGTokenPattern: 467 468 /// content handling after parsing 469 { 470 if(mpTarget) 471 { 472 if(!mpTarget->getParent()) 473 { 474 // last element closing, save this tree 475 maDocument.appendNode(mpTarget); 476 } 477 478 mpTarget = const_cast< SvgNode* >(mpTarget->getParent()); 479 } 480 else 481 { 482 OSL_ENSURE(false, "Closing token, but no context (!)"); 483 } 484 break; 485 } 486 default: 487 { 488 /// invalid token, ignore 489 } 490 } 491 492 if(pSvgTitleDescNode && mpTarget) 493 { 494 const rtl::OUString aText(pSvgTitleDescNode->getText()); 495 496 if(aText.getLength()) 497 { 498 if(SVGTokenTitle == aSVGToken) 499 { 500 mpTarget->parseAttribute(getStrTitle(), aSVGToken, aText); 501 } 502 else // if(SVGTokenDesc == aSVGToken) 503 { 504 mpTarget->parseAttribute(getStrDesc(), aSVGToken, aText); 505 } 506 } 507 } 508 509 if(pCssStyle && pCssStyle->isTextCss()) 510 { 511 // css style parsing 512 if(maCssContents.size()) 513 { 514 // need to interpret css styles and remember them as StyleSheets 515 pCssStyle->addCssStyleSheet(*(maCssContents.end() - 1)); 516 maCssContents.pop_back(); 517 } 518 else 519 { 520 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)"); 521 } 522 } 523 524 if(pWhitespaceCheck) 525 { 526 // cleanup read strings 527 whiteSpaceHandling(pWhitespaceCheck, 0); 528 } 529 } 530 } 531 532 void SvgDocHdl::characters( const ::rtl::OUString& aChars ) throw (xml::sax::SAXException, uno::RuntimeException) 533 { 534 const sal_uInt32 nLength(aChars.getLength()); 535 536 if(mpTarget && nLength) 537 { 538 switch(mpTarget->getType()) 539 { 540 case SVGTokenText: 541 case SVGTokenTspan: 542 case SVGTokenTextPath: 543 { 544 const SvgNodeVector& rChilds = mpTarget->getChildren(); 545 SvgCharacterNode* pTarget = 0; 546 547 if(rChilds.size()) 548 { 549 pTarget = dynamic_cast< SvgCharacterNode* >(rChilds[rChilds.size() - 1]); 550 } 551 552 if(pTarget) 553 { 554 // concatenate to current character span 555 pTarget->concatenate(aChars); 556 } 557 else 558 { 559 // add character span as simplified tspan (no arguments) 560 // as direct child of SvgTextNode/SvgTspanNode/SvgTextPathNode 561 new SvgCharacterNode(maDocument, mpTarget, aChars); 562 } 563 break; 564 } 565 case SVGTokenStyle: 566 { 567 SvgStyleNode& rSvgStyleNode = static_cast< SvgStyleNode& >(*mpTarget); 568 569 if(rSvgStyleNode.isTextCss()) 570 { 571 // collect characters for css style 572 if(maCssContents.size()) 573 { 574 const ::rtl::OUString aTrimmedChars(aChars.trim()); 575 576 if(aTrimmedChars.getLength()) 577 { 578 std::vector< rtl::OUString >::iterator aString(maCssContents.end() - 1); 579 (*aString) += aTrimmedChars; 580 } 581 } 582 else 583 { 584 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)"); 585 } 586 } 587 break; 588 } 589 case SVGTokenTitle: 590 case SVGTokenDesc: 591 { 592 SvgTitleDescNode& rSvgTitleDescNode = static_cast< SvgTitleDescNode& >(*mpTarget); 593 594 // add text directly to SvgTitleDescNode 595 rSvgTitleDescNode.concatenate(aChars); 596 break; 597 } 598 default: 599 { 600 // characters not used by a known node 601 break; 602 } 603 } 604 } 605 } 606 607 void SvgDocHdl::ignorableWhitespace(const ::rtl::OUString& /*aWhitespaces*/) throw (xml::sax::SAXException, uno::RuntimeException) 608 { 609 } 610 611 void SvgDocHdl::processingInstruction(const ::rtl::OUString& /*aTarget*/, const ::rtl::OUString& /*aData*/) throw (xml::sax::SAXException, uno::RuntimeException) 612 { 613 } 614 615 void SvgDocHdl::setDocumentLocator(const uno::Reference< xml::sax::XLocator >& /*xLocator*/) throw (xml::sax::SAXException, uno::RuntimeException) 616 { 617 } 618 } // end of namespace svgreader 619 } // end of namespace svgio 620 621 ////////////////////////////////////////////////////////////////////////////// 622 // eof 623