Stop leaking all ScPostIt instances.
[LibreOffice.git] / svgio / source / svgreader / svgnode.cxx
blob728905b5710f7912175b07a65c9f7ad63ed0a256
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <basegfx/polygon/b2dpolypolygontools.hxx>
21 #include <svgio/svgreader/svgdocument.hxx>
22 #include <svgio/svgreader/svgnode.hxx>
23 #include <svgio/svgreader/svgstyleattributes.hxx>
24 #include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
25 #include <tools/urlobj.hxx>
28 namespace svgio
30 namespace svgreader
32 const SvgStyleAttributes* SvgNode::getSvgStyleAttributes() const
34 return 0;
37 const SvgStyleAttributes* SvgNode::checkForCssStyle(const rtl::OUString& rClassStr, const SvgStyleAttributes& rOriginal) const
39 if(maCssStyleVector.empty()) // #120435# Evaluate for CSS styles only once, this cannot change
41 const SvgDocument& rDocument = getDocument();
43 if(rDocument.hasSvgStyleAttributesById())
45 if(getClass())
47 // find all referenced CSS styles, a list of entries is allowed
48 const rtl::OUString* pClassList = getClass();
49 const sal_Int32 nLen(pClassList->getLength());
50 sal_Int32 nPos(0);
51 const SvgStyleAttributes* pNew = 0;
53 skip_char(*pClassList, ' ', nPos, nLen);
55 while(nPos < nLen)
57 rtl::OUStringBuffer aTokenValue;
59 copyToLimiter(*pClassList, ' ', nPos, aTokenValue, nLen);
60 skip_char(*pClassList, ' ', nPos, nLen);
62 rtl::OUString aId(rtl::OUString::createFromAscii("."));
63 const rtl::OUString aOUTokenValue(aTokenValue.makeStringAndClear());
65 // look for CSS style common to token
66 aId = aId + aOUTokenValue;
67 pNew = rDocument.findSvgStyleAttributesById(aId);
69 if(!pNew && !rClassStr.isEmpty())
71 // look for CSS style common to class.token
72 aId = rClassStr + aId;
74 pNew = rDocument.findSvgStyleAttributesById(aId);
77 if(pNew)
79 const_cast< SvgNode* >(this)->maCssStyleVector.push_back(pNew);
84 if(maCssStyleVector.empty() && getId())
86 // if none found, search for CSS style equal to Id
87 const SvgStyleAttributes* pNew = rDocument.findSvgStyleAttributesById(*getId());
89 if(pNew)
91 const_cast< SvgNode* >(this)->maCssStyleVector.push_back(pNew);
95 if(maCssStyleVector.empty() && !rClassStr.isEmpty())
97 // if none found, search for CSS style equal to class type
98 const SvgStyleAttributes* pNew = rDocument.findSvgStyleAttributesById(rClassStr);
100 if(pNew)
102 const_cast< SvgNode* >(this)->maCssStyleVector.push_back(pNew);
108 if(!maCssStyleVector.empty())
110 // #i123510# if CSS styles were found, create a linked list with rOriginal as parent
111 // and all CSS styles as linked children, so that the style attribute has
112 // priority over the CSS style. If there is no style attribute this means that
113 // no values are set at rOriginal, thus it is still correct to have that order.
114 // Repeated style requests should only be issued from sub-Text nodes and I'm not
115 // sure if in-between text nodes may build other chains (should not happen). But
116 // it's only a re-chaining with pointers (cheap), so allow to do it every time.
117 SvgStyleAttributes* pCurrent = const_cast< SvgStyleAttributes* >(&rOriginal);
118 pCurrent->setCssStyleParent(0);
120 for(sal_uInt32 a(0); a < maCssStyleVector.size(); a++)
122 SvgStyleAttributes* pNext = const_cast< SvgStyleAttributes* >(maCssStyleVector[a]);
124 pCurrent->setCssStyleParent(pNext);
125 pCurrent = pNext;
126 pCurrent->setCssStyleParent(0);
130 return &rOriginal;
133 SvgNode::SvgNode(
134 SVGToken aType,
135 SvgDocument& rDocument,
136 SvgNode* pParent)
137 : maType(aType),
138 mrDocument(rDocument),
139 mpParent(pParent),
140 mpAlternativeParent(0),
141 maChildren(),
142 mpId(0),
143 mpClass(0),
144 maXmlSpace(XmlSpace_notset),
145 maDisplay(Display_inline),
146 maCssStyleVector()
148 OSL_ENSURE(SVGTokenUnknown != maType, "SvgNode with unknown type created (!)");
150 if(pParent)
152 pParent->maChildren.push_back(this);
154 else
156 #ifdef DBG_UTIL
157 if(SVGTokenSvg != getType())
159 OSL_ENSURE(false, "No parent for this node (!)");
161 #endif
165 SvgNode::~SvgNode()
167 while(maChildren.size())
169 delete maChildren[maChildren.size() - 1];
170 maChildren.pop_back();
173 if(mpId) delete mpId;
174 if(mpClass) delete mpClass;
177 void SvgNode::parseAttributes(const com::sun::star::uno::Reference< com::sun::star::xml::sax::XAttributeList >& xAttribs)
179 const sal_uInt32 nAttributes(xAttribs->getLength());
180 // #i122522# SVG defines that 'In general, this means that the presentation attributes have
181 // lower priority than other CSS style rules specified in author style sheets or style
182 // attributes.' in http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes
183 // (6.4 Specifying properties using the presentation attributes SVG 1.1). That means that
184 // e.g. font-size will appear as presentation attribute and CSS style attribute. In these
185 // cases, CSS style attributes need to have precedence. To do so it is possible to create
186 // a proirity system for all properties of a shape, but it will also work to parse the
187 // presentation attributes of type 'style' last, so they will overwrite the less-prioritized
188 // already interpreted ones. Thus, remember SVGTokenStyle entries and parse them last.
189 // To make this work it is required that parseAttribute is only called by parseAttributes
190 // which is the case.
191 std::vector< sal_uInt32 > aSVGTokenStyleIndexes;
193 for(sal_uInt32 a(0); a < nAttributes; a++)
195 const OUString aTokenName(xAttribs->getNameByIndex(a));
196 const SVGToken aSVGToken(StrToSVGToken(aTokenName));
198 if(SVGTokenStyle == aSVGToken)
200 // #i122522# remember SVGTokenStyle entry
201 aSVGTokenStyleIndexes.push_back(a);
203 else
205 parseAttribute(aTokenName, aSVGToken, xAttribs->getValueByIndex(a));
209 // #i122522# parse SVGTokenStyle entries last to override already interpreted
210 // 'presentation attributes' of potenially the same type
211 for(sal_uInt32 b(0); b < aSVGTokenStyleIndexes.size(); b++)
213 const sal_uInt32 nSVGTokenStyleIndex(aSVGTokenStyleIndexes[b]);
214 const ::rtl::OUString aTokenName(xAttribs->getNameByIndex(nSVGTokenStyleIndex));
216 parseAttribute(aTokenName, SVGTokenStyle, xAttribs->getValueByIndex(nSVGTokenStyleIndex));
220 Display getDisplayFromContent(const rtl::OUString& aContent)
222 if(aContent.getLength())
224 static rtl::OUString aStrInline(rtl::OUString::createFromAscii("inline"));
225 static rtl::OUString aStrBlock(rtl::OUString::createFromAscii("block"));
226 static rtl::OUString aStrList_item(rtl::OUString::createFromAscii("list-item"));
227 static rtl::OUString aStrRun_in(rtl::OUString::createFromAscii("run-in"));
228 static rtl::OUString aStrCompact(rtl::OUString::createFromAscii("compact"));
229 static rtl::OUString aStrMarker(rtl::OUString::createFromAscii("marker"));
230 static rtl::OUString aStrTable(rtl::OUString::createFromAscii("table"));
231 static rtl::OUString aStrInline_table(rtl::OUString::createFromAscii("inline-table"));
232 static rtl::OUString aStrTable_row_group(rtl::OUString::createFromAscii("table-row-group"));
233 static rtl::OUString aStrTable_header_group(rtl::OUString::createFromAscii("table-header-group"));
234 static rtl::OUString aStrTable_footer_group(rtl::OUString::createFromAscii("table-footer-group"));
235 static rtl::OUString aStrTable_row(rtl::OUString::createFromAscii("table-row"));
236 static rtl::OUString aStrTable_column_group(rtl::OUString::createFromAscii("table-column-group"));
237 static rtl::OUString aStrTable_column(rtl::OUString::createFromAscii("table-column"));
238 static rtl::OUString aStrTable_cell(rtl::OUString::createFromAscii("table-cell"));
239 static rtl::OUString aStrTable_caption(rtl::OUString::createFromAscii("table-caption"));
240 static rtl::OUString aStrNone(rtl::OUString::createFromAscii("none"));
241 static rtl::OUString aStrInherit(rtl::OUString::createFromAscii("inherit"));
243 if(aContent.match(aStrInline))
245 return Display_inline;
247 else if(aContent.match(aStrNone))
249 return Display_none;
251 else if(aContent.match(aStrInherit))
253 return Display_inherit;
255 else if(aContent.match(aStrBlock))
257 return Display_block;
259 else if(aContent.match(aStrList_item))
261 return Display_list_item;
263 else if(aContent.match(aStrRun_in))
265 return Display_run_in;
267 else if(aContent.match(aStrCompact))
269 return Display_compact;
271 else if(aContent.match(aStrMarker))
273 return Display_marker;
275 else if(aContent.match(aStrTable))
277 return Display_table;
279 else if(aContent.match(aStrInline_table))
281 return Display_inline_table;
283 else if(aContent.match(aStrTable_row_group))
285 return Display_table_row_group;
287 else if(aContent.match(aStrTable_header_group))
289 return Display_table_header_group;
291 else if(aContent.match(aStrTable_footer_group))
293 return Display_table_footer_group;
295 else if(aContent.match(aStrTable_row))
297 return Display_table_row;
299 else if(aContent.match(aStrTable_column_group))
301 return Display_table_column_group;
303 else if(aContent.match(aStrTable_column))
305 return Display_table_column;
307 else if(aContent.match(aStrTable_cell))
309 return Display_table_cell;
311 else if(aContent.match(aStrTable_caption))
313 return Display_table_caption;
317 // return the default
318 return Display_inline;
321 void SvgNode::parseAttribute(const OUString& /*rTokenName*/, SVGToken aSVGToken, const OUString& aContent)
323 switch(aSVGToken)
325 case SVGTokenId:
327 if(!aContent.isEmpty())
329 setId(&aContent);
331 break;
333 case SVGTokenClass:
335 if(!aContent.isEmpty())
337 setClass(&aContent);
339 break;
341 case SVGTokenXmlSpace:
343 if(!aContent.isEmpty())
345 static OUString aStrDefault(OUString::createFromAscii("default"));
346 static OUString aStrPreserve(OUString::createFromAscii("preserve"));
348 if(aContent.match(aStrDefault))
350 setXmlSpace(XmlSpace_default);
352 else if(aContent.match(aStrPreserve))
354 setXmlSpace(XmlSpace_preserve);
357 break;
359 case SVGTokenDisplay:
361 if(aContent.getLength())
363 setDisplay(getDisplayFromContent(aContent));
365 break;
367 default:
369 break;
374 void SvgNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool bReferenced) const
376 if(Display_none == getDisplay())
378 return;
381 if(!bReferenced)
383 if(SVGTokenDefs == getType() ||
384 SVGTokenSymbol == getType() ||
385 SVGTokenClipPathNode == getType() ||
386 SVGTokenMask == getType() ||
387 SVGTokenMarker == getType() ||
388 SVGTokenPattern == getType())
390 // do not decompose defs or symbol nodes (these hold only style-like
391 // objects which may be used by referencing them) except when doing
392 // so controlled referenced
394 // also do not decompose ClipPaths and Masks. These should be embedded
395 // in a defs node (which gets not decomposed by itself), but you never
396 // know
398 // also not directly used are Markers and Patterns, only indirecty used
399 // by reference
401 // #i121656# also do not decompose nodes which have display="none" set
402 // as property
403 return;
407 const SvgNodeVector& rChildren = getChildren();
409 if(!rChildren.empty())
411 const sal_uInt32 nCount(rChildren.size());
413 for(sal_uInt32 a(0); a < nCount; a++)
415 SvgNode* pCandidate = rChildren[a];
417 if(pCandidate && Display_none != pCandidate->getDisplay())
419 drawinglayer::primitive2d::Primitive2DSequence aNewTarget;
421 pCandidate->decomposeSvgNode(aNewTarget, bReferenced);
423 if(aNewTarget.hasElements())
425 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewTarget);
428 else
430 OSL_ENSURE(false, "Null-Pointer in child node list (!)");
434 if(rTarget.hasElements())
436 const SvgStyleAttributes* pStyles = getSvgStyleAttributes();
438 if(pStyles)
440 // check if we have Title or Desc
441 const OUString& rTitle = pStyles->getTitle();
442 const OUString& rDesc = pStyles->getDesc();
444 if(!rTitle.isEmpty() || !rDesc.isEmpty())
446 // default object name is empty
447 OUString aObjectName;
449 // use path as object name when outmost element
450 if(SVGTokenSvg == getType())
452 aObjectName = getDocument().getAbsolutePath();
454 if(!aObjectName.isEmpty())
456 INetURLObject aURL(aObjectName);
458 aObjectName = aURL.getName(
459 INetURLObject::LAST_SEGMENT,
460 true,
461 INetURLObject::DECODE_WITH_CHARSET);
465 // pack in ObjectInfoPrimitive2D group
466 const drawinglayer::primitive2d::Primitive2DReference xRef(
467 new drawinglayer::primitive2d::ObjectInfoPrimitive2D(
468 rTarget,
469 aObjectName,
470 rTitle,
471 rDesc));
473 rTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
480 const basegfx::B2DRange SvgNode::getCurrentViewPort() const
482 if(getParent())
484 return getParent()->getCurrentViewPort();
486 else
488 return basegfx::B2DRange(); // return empty B2DRange
492 double SvgNode::getCurrentFontSizeInherited() const
494 if(getParent())
496 return getParent()->getCurrentFontSize();
498 else
500 return 0.0;
504 double SvgNode::getCurrentFontSize() const
506 if(getSvgStyleAttributes())
507 return getSvgStyleAttributes()->getFontSize().solve(*this, xcoordinate);
509 return getCurrentFontSizeInherited();
512 double SvgNode::getCurrentXHeightInherited() const
514 if(getParent())
516 return getParent()->getCurrentXHeight();
518 else
520 return 0.0;
524 double SvgNode::getCurrentXHeight() const
526 if(getSvgStyleAttributes())
527 // for XHeight, use FontSize currently
528 return getSvgStyleAttributes()->getFontSize().solve(*this, ycoordinate);
530 return getCurrentXHeightInherited();
533 void SvgNode::setId(const OUString* pfId)
535 if(mpId)
537 mrDocument.removeSvgNodeFromMapper(*mpId);
538 delete mpId;
539 mpId = 0;
542 if(pfId)
544 mpId = new OUString(*pfId);
545 mrDocument.addSvgNodeToMapper(*mpId, *this);
549 void SvgNode::setClass(const OUString* pfClass)
551 if(mpClass)
553 mrDocument.removeSvgNodeFromMapper(*mpClass);
554 delete mpClass;
555 mpClass = 0;
558 if(pfClass)
560 mpClass = new OUString(*pfClass);
561 mrDocument.addSvgNodeToMapper(*mpClass, *this);
565 XmlSpace SvgNode::getXmlSpace() const
567 if(maXmlSpace != XmlSpace_notset)
569 return maXmlSpace;
572 if(getParent())
574 return getParent()->getXmlSpace();
577 // default is XmlSpace_default
578 return XmlSpace_default;
581 } // end of namespace svgreader
582 } // end of namespace svgio
585 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */