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 const SvgStyleAttributes* SvgNode::getSvgStyleAttributes() const 40 { 41 return 0; 42 } 43 44 const SvgStyleAttributes* SvgNode::checkForCssStyle(const rtl::OUString& rClassStr, const SvgStyleAttributes& rOriginal) const 45 { 46 if(maCssStyleVector.empty()) // #120435# Evaluate for CSS styles only once, this cannot change 47 { 48 const SvgDocument& rDocument = getDocument(); 49 50 if(rDocument.hasSvgStyleAttributesById()) 51 { 52 if(getClass()) 53 { 54 // find all referenced CSS styles, a list of entries is allowed 55 const rtl::OUString* pClassList = getClass(); 56 const sal_Int32 nLen(pClassList->getLength()); 57 sal_Int32 nPos(0); 58 const SvgStyleAttributes* pNew = 0; 59 60 skip_char(*pClassList, sal_Unicode(' '), nPos, nLen); 61 62 while(nPos < nLen) 63 { 64 rtl::OUStringBuffer aTokenValue; 65 66 copyToLimiter(*pClassList, sal_Unicode(' '), nPos, aTokenValue, nLen); 67 skip_char(*pClassList, sal_Unicode(' '), nPos, nLen); 68 69 rtl::OUString aId(rtl::OUString::createFromAscii(".")); 70 const rtl::OUString aOUTokenValue(aTokenValue.makeStringAndClear()); 71 72 // look for CSS style common to token 73 aId = aId + aOUTokenValue; 74 pNew = rDocument.findSvgStyleAttributesById(aId); 75 76 if(!pNew && rClassStr.getLength()) 77 { 78 // look for CSS style common to class.token 79 aId = rClassStr + aId; 80 81 pNew = rDocument.findSvgStyleAttributesById(aId); 82 } 83 84 if(pNew) 85 { 86 const_cast< SvgNode* >(this)->maCssStyleVector.push_back(pNew); 87 } 88 } 89 } 90 91 if(maCssStyleVector.empty() && getId()) 92 { 93 // if none found, search for CSS style equal to Id 94 const SvgStyleAttributes* pNew = rDocument.findSvgStyleAttributesById(*getId()); 95 96 if(pNew) 97 { 98 const_cast< SvgNode* >(this)->maCssStyleVector.push_back(pNew); 99 } 100 } 101 102 if(maCssStyleVector.empty() && rClassStr.getLength()) 103 { 104 // if none found, search for CSS style equal to class type 105 const SvgStyleAttributes* pNew = rDocument.findSvgStyleAttributesById(rClassStr); 106 107 if(pNew) 108 { 109 const_cast< SvgNode* >(this)->maCssStyleVector.push_back(pNew); 110 } 111 } 112 } 113 } 114 115 if(maCssStyleVector.empty()) 116 { 117 return &rOriginal; 118 } 119 else 120 { 121 // set CssStyleParent at maCssStyleVector members to hang them in front of 122 // the existing style. Build a style chain, reset parent of original for security. 123 // Repeated style requests should only be issued from sub-Text nodes and I'm not 124 // sure if in-between text nodes may build other chains (should not happen). But 125 // it's only a re-chaining with pointers (cheap), so allow to do it every time. 126 SvgStyleAttributes* pCurrent = const_cast< SvgStyleAttributes* >(&rOriginal); 127 pCurrent->setCssStyleParent(0); 128 129 for(sal_uInt32 a(0); a < maCssStyleVector.size(); a++) 130 { 131 SvgStyleAttributes* pCandidate = const_cast< SvgStyleAttributes* >(maCssStyleVector[maCssStyleVector.size() - a - 1]); 132 133 pCandidate->setCssStyleParent(pCurrent); 134 pCurrent = pCandidate; 135 } 136 137 return pCurrent; 138 } 139 } 140 141 SvgNode::SvgNode( 142 SVGToken aType, 143 SvgDocument& rDocument, 144 SvgNode* pParent) 145 : maType(aType), 146 mrDocument(rDocument), 147 mpParent(pParent), 148 mpAlternativeParent(0), 149 maChildren(), 150 mpId(0), 151 mpClass(0), 152 maXmlSpace(XmlSpace_notset), 153 maCssStyleVector() 154 { 155 OSL_ENSURE(SVGTokenUnknown != maType, "SvgNode with unknown type created (!)"); 156 157 if(pParent) 158 { 159 pParent->maChildren.push_back(this); 160 } 161 else 162 { 163 #ifdef DBG_UTIL 164 if(SVGTokenSvg != getType()) 165 { 166 OSL_ENSURE(false, "No parent for this node (!)"); 167 } 168 #endif 169 } 170 } 171 172 SvgNode::~SvgNode() 173 { 174 while(maChildren.size()) 175 { 176 delete maChildren[maChildren.size() - 1]; 177 maChildren.pop_back(); 178 } 179 180 if(mpId) delete mpId; 181 if(mpClass) delete mpClass; 182 } 183 184 void SvgNode::parseAttributes(const com::sun::star::uno::Reference< com::sun::star::xml::sax::XAttributeList >& xAttribs) 185 { 186 const sal_uInt32 nAttributes(xAttribs->getLength()); 187 188 for(sal_uInt32 a(0); a < nAttributes; a++) 189 { 190 const ::rtl::OUString aTokenName(xAttribs->getNameByIndex(a)); 191 192 parseAttribute(aTokenName, StrToSVGToken(aTokenName), xAttribs->getValueByIndex(a)); 193 } 194 } 195 196 void SvgNode::parseAttribute(const rtl::OUString& /*rTokenName*/, SVGToken aSVGToken, const rtl::OUString& aContent) 197 { 198 switch(aSVGToken) 199 { 200 case SVGTokenId: 201 { 202 if(aContent.getLength()) 203 { 204 setId(&aContent); 205 } 206 break; 207 } 208 case SVGTokenClass: 209 { 210 if(aContent.getLength()) 211 { 212 setClass(&aContent); 213 } 214 break; 215 } 216 case SVGTokenXmlSpace: 217 { 218 if(aContent.getLength()) 219 { 220 static rtl::OUString aStrDefault(rtl::OUString::createFromAscii("default")); 221 static rtl::OUString aStrPreserve(rtl::OUString::createFromAscii("preserve")); 222 223 if(aContent.match(aStrDefault)) 224 { 225 setXmlSpace(XmlSpace_default); 226 } 227 else if(aContent.match(aStrPreserve)) 228 { 229 setXmlSpace(XmlSpace_preserve); 230 } 231 } 232 break; 233 } 234 default: 235 { 236 break; 237 } 238 } 239 } 240 241 void SvgNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool bReferenced) const 242 { 243 if(!bReferenced) 244 { 245 if(SVGTokenDefs == getType() || 246 SVGTokenSymbol == getType() || 247 SVGTokenClipPathNode == getType() || 248 SVGTokenMask == getType() || 249 SVGTokenMarker == getType() || 250 SVGTokenPattern == getType()) 251 { 252 // do not decompose defs or symbol nodes (these hold only style-like 253 // objects which may be used by referencing them) except when doing 254 // so controlled referenced 255 256 // also do not decompose ClipPaths and Masks. These should be embedded 257 // in a defs node (which gets not decomposed by itself), but you never 258 // know 259 260 // also not directly used are Markers and Patterns, only indirecty used 261 // by reference 262 return; 263 } 264 } 265 266 const SvgNodeVector& rChildren = getChildren(); 267 268 if(!rChildren.empty()) 269 { 270 const sal_uInt32 nCount(rChildren.size()); 271 272 for(sal_uInt32 a(0); a < nCount; a++) 273 { 274 SvgNode* pCandidate = rChildren[a]; 275 276 if(pCandidate) 277 { 278 drawinglayer::primitive2d::Primitive2DSequence aNewTarget; 279 280 pCandidate->decomposeSvgNode(aNewTarget, bReferenced); 281 282 if(aNewTarget.hasElements()) 283 { 284 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewTarget); 285 } 286 } 287 else 288 { 289 OSL_ENSURE(false, "Null-Pointer in child node list (!)"); 290 } 291 } 292 293 if(rTarget.hasElements()) 294 { 295 const SvgStyleAttributes* pStyles = getSvgStyleAttributes(); 296 297 if(pStyles) 298 { 299 // check if we have Title or Desc 300 const rtl::OUString& rTitle = pStyles->getTitle(); 301 const rtl::OUString& rDesc = pStyles->getDesc(); 302 303 if(rTitle.getLength() || rDesc.getLength()) 304 { 305 // default object name is empty 306 rtl::OUString aObjectName; 307 308 // use path as object name when outmost element 309 if(SVGTokenSvg == getType()) 310 { 311 aObjectName = getDocument().getAbsolutePath(); 312 313 if(aObjectName.getLength()) 314 { 315 INetURLObject aURL(aObjectName); 316 317 aObjectName = aURL.getName( 318 INetURLObject::LAST_SEGMENT, 319 true, 320 INetURLObject::DECODE_WITH_CHARSET); 321 } 322 } 323 324 // pack in ObjectInfoPrimitive2D group 325 const drawinglayer::primitive2d::Primitive2DReference xRef( 326 new drawinglayer::primitive2d::ObjectInfoPrimitive2D( 327 rTarget, 328 aObjectName, 329 rTitle, 330 rDesc)); 331 332 rTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1); 333 } 334 } 335 } 336 } 337 } 338 339 const basegfx::B2DRange* SvgNode::getCurrentViewPort() const 340 { 341 if(getParent()) 342 { 343 return getParent()->getCurrentViewPort(); 344 } 345 else 346 { 347 return 0; 348 } 349 } 350 351 double SvgNode::getCurrentFontSize() const 352 { 353 if(getSvgStyleAttributes()) 354 { 355 return getSvgStyleAttributes()->getFontSize().solve(*this, xcoordinate); 356 } 357 else if(getParent()) 358 { 359 return getParent()->getCurrentFontSize(); 360 } 361 else 362 { 363 return 0.0; 364 } 365 } 366 367 double SvgNode::getCurrentXHeight() const 368 { 369 if(getSvgStyleAttributes()) 370 { 371 // for XHeight, use FontSize currently 372 return getSvgStyleAttributes()->getFontSize().solve(*this, ycoordinate); 373 } 374 else if(getParent()) 375 { 376 return getParent()->getCurrentXHeight(); 377 } 378 else 379 { 380 return 0.0; 381 } 382 } 383 384 void SvgNode::setId(const rtl::OUString* pfId) 385 { 386 if(mpId) 387 { 388 mrDocument.removeSvgNodeFromMapper(*mpId); 389 delete mpId; 390 mpId = 0; 391 } 392 393 if(pfId) 394 { 395 mpId = new rtl::OUString(*pfId); 396 mrDocument.addSvgNodeToMapper(*mpId, *this); 397 } 398 } 399 400 void SvgNode::setClass(const rtl::OUString* pfClass) 401 { 402 if(mpClass) 403 { 404 mrDocument.removeSvgNodeFromMapper(*mpClass); 405 delete mpClass; 406 mpClass = 0; 407 } 408 409 if(pfClass) 410 { 411 mpClass = new rtl::OUString(*pfClass); 412 mrDocument.addSvgNodeToMapper(*mpClass, *this); 413 } 414 } 415 416 XmlSpace SvgNode::getXmlSpace() const 417 { 418 if(maXmlSpace != XmlSpace_notset) 419 { 420 return maXmlSpace; 421 } 422 423 if(getParent()) 424 { 425 return getParent()->getXmlSpace(); 426 } 427 428 // default is XmlSpace_default 429 return XmlSpace_default; 430 } 431 432 } // end of namespace svgreader 433 } // end of namespace svgio 434 435 ////////////////////////////////////////////////////////////////////////////// 436 // eof 437