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)); 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 mpTarget->parseAttributes(xAttribs); 334 335 if(pNew->isTextCss()) 336 { 337 maCssContents.push_back(rtl::OUString()); 338 } 339 break; 340 } 341 342 /// structural elements clip-path and mask. Content gets scanned, but 343 /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced) 344 case SVGTokenClipPathNode: 345 { 346 /// new node for ClipPath 347 mpTarget = new SvgClipPathNode(maDocument, mpTarget); 348 mpTarget->parseAttributes(xAttribs); 349 break; 350 } 351 case SVGTokenMask: 352 { 353 /// new node for Mask 354 mpTarget = new SvgMaskNode(maDocument, mpTarget); 355 mpTarget->parseAttributes(xAttribs); 356 break; 357 } 358 359 /// structural element marker 360 case SVGTokenMarker: 361 { 362 /// new node for marker 363 mpTarget = new SvgMarkerNode(maDocument, mpTarget); 364 mpTarget->parseAttributes(xAttribs); 365 break; 366 } 367 368 /// structural element pattern 369 case SVGTokenPattern: 370 { 371 /// new node for pattern 372 mpTarget = new SvgPatternNode(maDocument, mpTarget); 373 mpTarget->parseAttributes(xAttribs); 374 break; 375 } 376 377 default: 378 { 379 /// invalid token, ignore 380 #ifdef DBG_UTIL 381 myAssert( 382 rtl::OUString::createFromAscii("Unknown Base SvgToken <") + 383 aName + 384 rtl::OUString::createFromAscii("> (!)")); 385 #endif 386 break; 387 } 388 } 389 } 390 } 391 392 void SvgDocHdl::endElement( const ::rtl::OUString& aName ) throw (xml::sax::SAXException, uno::RuntimeException) 393 { 394 if(aName.getLength()) 395 { 396 const SVGToken aSVGToken(StrToSVGToken(aName)); 397 SvgNode* pWhitespaceCheck(SVGTokenText == aSVGToken ? mpTarget : 0); 398 SvgStyleNode* pCssStyle(SVGTokenStyle == aSVGToken ? static_cast< SvgStyleNode* >(mpTarget) : 0); 399 SvgTitleDescNode* pSvgTitleDescNode(SVGTokenTitle == aSVGToken || SVGTokenDesc == aSVGToken ? static_cast< SvgTitleDescNode* >(mpTarget) : 0); 400 401 switch(aSVGToken) 402 { 403 /// valid tokens for which a new one was created 404 405 /// structural elements 406 case SVGTokenDefs: 407 case SVGTokenG: 408 case SVGTokenSvg: 409 case SVGTokenSymbol: 410 case SVGTokenUse: 411 412 /// shape elements 413 case SVGTokenCircle: 414 case SVGTokenEllipse: 415 case SVGTokenLine: 416 case SVGTokenPath: 417 case SVGTokenPolygon: 418 case SVGTokenPolyline: 419 case SVGTokenRect: 420 case SVGTokenImage: 421 422 /// title and description 423 case SVGTokenTitle: 424 case SVGTokenDesc: 425 426 /// gradients 427 case SVGTokenLinearGradient: 428 case SVGTokenRadialGradient: 429 430 /// gradient stops 431 case SVGTokenStop: 432 433 /// text 434 case SVGTokenText: 435 case SVGTokenTspan: 436 case SVGTokenTextPath: 437 case SVGTokenTref: 438 439 /// styles (as stylesheets) 440 case SVGTokenStyle: 441 442 /// structural elements clip-path and mask 443 case SVGTokenClipPathNode: 444 case SVGTokenMask: 445 446 /// structural element marker 447 case SVGTokenMarker: 448 449 /// structural element pattern 450 case SVGTokenPattern: 451 452 /// content handling after parsing 453 { 454 if(mpTarget) 455 { 456 if(!mpTarget->getParent()) 457 { 458 // last element closing, save this tree 459 maDocument.appendNode(mpTarget); 460 } 461 462 mpTarget = const_cast< SvgNode* >(mpTarget->getParent()); 463 } 464 else 465 { 466 OSL_ENSURE(false, "Closing token, but no context (!)"); 467 } 468 break; 469 } 470 default: 471 { 472 /// invalid token, ignore 473 } 474 } 475 476 if(pSvgTitleDescNode && mpTarget) 477 { 478 const rtl::OUString aText(pSvgTitleDescNode->getText()); 479 480 if(aText.getLength()) 481 { 482 if(SVGTokenTitle == aSVGToken) 483 { 484 mpTarget->parseAttribute(getStrTitle(), aSVGToken, aText); 485 } 486 else // if(SVGTokenDesc == aSVGToken) 487 { 488 mpTarget->parseAttribute(getStrDesc(), aSVGToken, aText); 489 } 490 } 491 } 492 493 if(pCssStyle && pCssStyle->isTextCss()) 494 { 495 // css style parsing 496 if(maCssContents.size()) 497 { 498 // need to interpret css styles and remember them as StyleSheets 499 pCssStyle->addCssStyleSheet(*(maCssContents.end() - 1)); 500 maCssContents.pop_back(); 501 } 502 else 503 { 504 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)"); 505 } 506 } 507 508 if(pWhitespaceCheck) 509 { 510 // cleanup read strings 511 whiteSpaceHandling(pWhitespaceCheck, 0); 512 } 513 } 514 } 515 516 void SvgDocHdl::characters( const ::rtl::OUString& aChars ) throw (xml::sax::SAXException, uno::RuntimeException) 517 { 518 const sal_uInt32 nLength(aChars.getLength()); 519 520 if(mpTarget && nLength) 521 { 522 switch(mpTarget->getType()) 523 { 524 case SVGTokenText: 525 case SVGTokenTspan: 526 case SVGTokenTextPath: 527 { 528 const SvgNodeVector& rChilds = mpTarget->getChildren(); 529 SvgCharacterNode* pTarget = 0; 530 531 if(rChilds.size()) 532 { 533 pTarget = dynamic_cast< SvgCharacterNode* >(rChilds[rChilds.size() - 1]); 534 } 535 536 if(pTarget) 537 { 538 // concatenate to current character span 539 pTarget->concatenate(aChars); 540 } 541 else 542 { 543 // add character span as simplified tspan (no arguments) 544 // as direct child of SvgTextNode/SvgTspanNode/SvgTextPathNode 545 new SvgCharacterNode(maDocument, mpTarget, aChars); 546 } 547 break; 548 } 549 case SVGTokenStyle: 550 { 551 SvgStyleNode& rSvgStyleNode = static_cast< SvgStyleNode& >(*mpTarget); 552 553 if(rSvgStyleNode.isTextCss()) 554 { 555 // collect characters for css style 556 if(maCssContents.size()) 557 { 558 const ::rtl::OUString aTrimmedChars(aChars.trim()); 559 560 if(aTrimmedChars.getLength()) 561 { 562 std::vector< rtl::OUString >::iterator aString(maCssContents.end() - 1); 563 (*aString) += aTrimmedChars; 564 } 565 } 566 else 567 { 568 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)"); 569 } 570 } 571 break; 572 } 573 case SVGTokenTitle: 574 case SVGTokenDesc: 575 { 576 SvgTitleDescNode& rSvgTitleDescNode = static_cast< SvgTitleDescNode& >(*mpTarget); 577 578 // add text directly to SvgTitleDescNode 579 rSvgTitleDescNode.concatenate(aChars); 580 break; 581 } 582 default: 583 { 584 // characters not used by a known node 585 break; 586 } 587 } 588 } 589 } 590 591 void SvgDocHdl::ignorableWhitespace(const ::rtl::OUString& /*aWhitespaces*/) throw (xml::sax::SAXException, uno::RuntimeException) 592 { 593 } 594 595 void SvgDocHdl::processingInstruction(const ::rtl::OUString& /*aTarget*/, const ::rtl::OUString& /*aData*/) throw (xml::sax::SAXException, uno::RuntimeException) 596 { 597 } 598 599 void SvgDocHdl::setDocumentLocator(const uno::Reference< xml::sax::XLocator >& /*xLocator*/) throw (xml::sax::SAXException, uno::RuntimeException) 600 { 601 } 602 } // end of namespace svgreader 603 } // end of namespace svgio 604 605 ////////////////////////////////////////////////////////////////////////////// 606 // eof 607