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