1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
32 const SvgStyleAttributes
* SvgNode::getSvgStyleAttributes() const
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())
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());
51 const SvgStyleAttributes
* pNew
= 0;
53 skip_char(*pClassList
, ' ', 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
);
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());
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
);
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
);
126 pCurrent
->setCssStyleParent(0);
135 SvgDocument
& rDocument
,
138 mrDocument(rDocument
),
140 mpAlternativeParent(0),
144 maXmlSpace(XmlSpace_notset
),
145 maDisplay(Display_inline
),
148 OSL_ENSURE(SVGTokenUnknown
!= maType
, "SvgNode with unknown type created (!)");
152 pParent
->maChildren
.push_back(this);
157 if(SVGTokenSvg
!= getType())
159 OSL_ENSURE(false, "No parent for this node (!)");
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
);
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
))
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
)
327 if(!aContent
.isEmpty())
335 if(!aContent
.isEmpty())
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
);
359 case SVGTokenDisplay
:
361 if(aContent
.getLength())
363 setDisplay(getDisplayFromContent(aContent
));
374 void SvgNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence
& rTarget
, bool bReferenced
) const
376 if(Display_none
== getDisplay())
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
398 // also not directly used are Markers and Patterns, only indirecty used
401 // #i121656# also do not decompose nodes which have display="none" set
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
);
430 OSL_ENSURE(false, "Null-Pointer in child node list (!)");
434 if(rTarget
.hasElements())
436 const SvgStyleAttributes
* pStyles
= getSvgStyleAttributes();
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
,
461 INetURLObject::DECODE_WITH_CHARSET
);
465 // pack in ObjectInfoPrimitive2D group
466 const drawinglayer::primitive2d::Primitive2DReference
xRef(
467 new drawinglayer::primitive2d::ObjectInfoPrimitive2D(
473 rTarget
= drawinglayer::primitive2d::Primitive2DSequence(&xRef
, 1);
480 const basegfx::B2DRange
SvgNode::getCurrentViewPort() const
484 return getParent()->getCurrentViewPort();
488 return basegfx::B2DRange(); // return empty B2DRange
492 double SvgNode::getCurrentFontSizeInherited() const
496 return getParent()->getCurrentFontSize();
504 double SvgNode::getCurrentFontSize() const
506 if(getSvgStyleAttributes())
507 return getSvgStyleAttributes()->getFontSize().solve(*this, xcoordinate
);
509 return getCurrentFontSizeInherited();
512 double SvgNode::getCurrentXHeightInherited() const
516 return getParent()->getCurrentXHeight();
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
)
537 mrDocument
.removeSvgNodeFromMapper(*mpId
);
544 mpId
= new OUString(*pfId
);
545 mrDocument
.addSvgNodeToMapper(*mpId
, *this);
549 void SvgNode::setClass(const OUString
* pfClass
)
553 mrDocument
.removeSvgNodeFromMapper(*mpClass
);
560 mpClass
= new OUString(*pfClass
);
561 mrDocument
.addSvgNodeToMapper(*mpClass
, *this);
565 XmlSpace
SvgNode::getXmlSpace() const
567 if(maXmlSpace
!= XmlSpace_notset
)
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: */