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>
33 bool SvgNode::supportsParentStyle() const
38 const SvgStyleAttributes
* SvgNode::getSvgStyleAttributes() const
43 void SvgNode::fillCssStyleVectorUsingHierarchyAndSelectors(
44 const OUString
& rClassStr
,
45 const SvgNode
& rCurrent
,
46 const OUString
& aConcatenated
)
48 const SvgDocument
& rDocument
= getDocument();
50 if(rDocument
.hasGlobalCssStyleAttributes())
52 const SvgNode
* pParent
= rCurrent
.getParent();
54 // check for ID (highest priority)
57 const OUString
& rId
= *rCurrent
.getId();
61 const OUString
aNewConcatenated(
62 "#" + rId
+ aConcatenated
);
66 // check for combined selectors at parent firstso that higher specificity will be in front
67 fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr
, *pParent
, aNewConcatenated
);
70 const SvgStyleAttributes
* pNew
= rDocument
.findGlobalCssStyleAttributes(aNewConcatenated
);
74 // add CssStyle if found
75 maCssStyleVector
.push_back(pNew
);
80 // check for 'class' references (a list of entries is allowed)
81 if(rCurrent
.getClass())
83 const OUString
& rClassList
= *rCurrent
.getClass();
84 const sal_Int32
nLen(rClassList
.getLength());
88 std::vector
< OUString
> aParts
;
90 OUStringBuffer aToken
;
94 const sal_Int32
nInitPos(nPos
);
95 copyToLimiter(rClassList
, sal_Unicode(' '), nPos
, aToken
, nLen
);
96 skip_char(rClassList
, sal_Unicode(' '), nPos
, nLen
);
97 const OUString
aPart(aToken
.makeStringAndClear().trim());
101 aParts
.push_back(aPart
);
106 OSL_ENSURE(false, "Could not interpret on current position (!)");
111 for(sal_uInt32
a(0); a
< aParts
.size(); a
++)
113 const OUString
aNewConcatenated(
114 "." + aParts
[a
] + aConcatenated
);
118 // check for combined selectors at parent firstso that higher specificity will be in front
119 fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr
, *pParent
, aNewConcatenated
);
122 const SvgStyleAttributes
* pNew
= rDocument
.findGlobalCssStyleAttributes(aNewConcatenated
);
126 // add CssStyle if found
127 maCssStyleVector
.push_back(pNew
);
133 // check for class-dependent references to CssStyles
134 if(!rClassStr
.isEmpty())
136 OUString
aNewConcatenated(aConcatenated
);
138 if(!rCurrent
.getId() && !rCurrent
.getClass() && 0 == aConcatenated
.indexOf(rClassStr
))
140 // no new CssStyle Selector and already starts with rClassStr, do not concatenate;
141 // we pass an 'empty' node (in the sense of CssStyle Selector)
145 aNewConcatenated
= rClassStr
+ aConcatenated
;
150 // check for combined selectors at parent firstso that higher specificity will be in front
151 fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr
, *pParent
, aNewConcatenated
);
154 const SvgStyleAttributes
* pNew
= rDocument
.findGlobalCssStyleAttributes(aNewConcatenated
);
158 // add CssStyle if found
159 maCssStyleVector
.push_back(pNew
);
165 void SvgNode::fillCssStyleVector(const OUString
& rClassStr
)
167 OSL_ENSURE(!mbCssStyleVectorBuilt
, "OOps, fillCssStyleVector called double ?!?");
168 mbCssStyleVectorBuilt
= true;
170 // #i125293# If we have CssStyles we need to buuild a linked list of SvgStyleAttributes
171 // which represent this for the current object. There are various methods to
172 // specify CssStyles which need to be taken into account in a given order:
173 // - local CssStyle (independent from global CssStyles at SvgDocument)
175 // - 'class' CssStyle(s)
176 // - type-dependent elements (e..g. 'rect' for all rect elements)
177 // - local attributes (rOriginal)
178 // - inherited attributes (up the hierarchy)
179 // The first four will be collected in maCssStyleVector for the current element
180 // (once, this will not change) and be linked in the needed order using the
181 // get/setCssStyleParent at the SvgStyleAttributes which will be used preferred in
182 // member evaluation over the existing parent hierarchy
184 // check for local CssStyle with highest priority
187 // if we have one, use as first entry
188 maCssStyleVector
.push_back(mpLocalCssStyle
);
191 // check the hierarchy for concatenated patterns of Selectors
192 fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr
, *this, OUString());
194 // #i125329# find Css selector '*', add as last element if found
195 const SvgStyleAttributes
* pNew
= getDocument().findGlobalCssStyleAttributes("*");
199 // add CssStyle for selector '*' if found
200 maCssStyleVector
.push_back(pNew
);
204 const SvgStyleAttributes
* SvgNode::checkForCssStyle(const OUString
& rClassStr
, const SvgStyleAttributes
& rOriginal
) const
206 if(!mbCssStyleVectorBuilt
)
208 // build needed CssStyleVector for local node
209 const_cast< SvgNode
* >(this)->fillCssStyleVector(rClassStr
);
212 if(maCssStyleVector
.empty())
214 // return given original if no CssStlyes found
219 // #i125293# rOriginal will be the last element in the linked list; use no CssStyleParent
220 // there (reset it) to ensure that the parent hierarchy will be used when it's base
221 // is referenced. This new chaining inserts the CssStyles before the original style,
222 // this makes the whole process much safer since the original style when used will
223 // be not different to the situation without CssStyles; thus loops which may be caused
224 // by trying to use the parent hierarchy of the owner of the style will be avoided
225 // already in this mechanism. It's still good to keep the supportsParentStyle
226 // from #i125258# in place, though.
227 // This chain building using pointers will be done every time when checkForCssStyle
228 // is used (not the search, only the chaining). This is needed since the CssStyles
229 // themselves will be potentially used multiple times. It is not expensive since it's
230 // only changing some pointers.
231 // The alternative would be to create the style hierarchy for every element (or even
232 // for the element containing the hierarchy) in a vector of pointers and to use that.
233 // Resetting the CssStyleParent on rOriginal is probably not needeed
234 // but simply safer to do.
235 const_cast< SvgStyleAttributes
& >(rOriginal
).setCssStyleParent(0);
237 // loop over the existing CssStyles and link them. There is a first one, take
239 SvgStyleAttributes
* pCurrent
= const_cast< SvgStyleAttributes
* >(maCssStyleVector
[0]);
241 for(sal_uInt32
a(1); a
< maCssStyleVector
.size(); a
++)
243 SvgStyleAttributes
* pNext
= const_cast< SvgStyleAttributes
* >(maCssStyleVector
[a
]);
245 pCurrent
->setCssStyleParent(pNext
);
249 // pCurrent is the last used CssStyle, let it point to the original style
250 pCurrent
->setCssStyleParent(&rOriginal
);
252 // return 1st CssStyle as style chain start element (only for the
253 // local element, still no hierarchy used here)
254 return maCssStyleVector
[0];
260 SvgDocument
& rDocument
,
263 mrDocument(rDocument
),
265 mpAlternativeParent(0),
269 maXmlSpace(XmlSpace_notset
),
270 maDisplay(Display_inline
),
273 mbCssStyleVectorBuilt(false)
275 OSL_ENSURE(SVGTokenUnknown
!= maType
, "SvgNode with unknown type created (!)");
279 pParent
->maChildren
.push_back(this);
284 if(SVGTokenSvg
!= getType())
286 OSL_ENSURE(false, "No parent for this node (!)");
294 while(maChildren
.size())
296 delete maChildren
[maChildren
.size() - 1];
297 maChildren
.pop_back();
312 delete mpLocalCssStyle
;
316 void SvgNode::readLocalCssStyle(const OUString
& aContent
)
320 // create LocalCssStyle if needed but not yet added
321 mpLocalCssStyle
= new SvgStyleAttributes(*this);
325 // 2nd fill would be an error
326 OSL_ENSURE(false, "Svg node has two local CssStyles, this may lead to problems (!)");
331 // parse and set values to it
332 mpLocalCssStyle
->readCssStyle(aContent
);
336 OSL_ENSURE(false, "Could not get/create a local CssStyle for a node (!)");
340 void SvgNode::parseAttributes(const com::sun::star::uno::Reference
< com::sun::star::xml::sax::XAttributeList
>& xAttribs
)
342 // no longer need to pre-sort moving 'style' entries to the back so that
343 // values get overwritten - that was the previous, not complete solution for
344 // handling the priorities between svg and Css properties
345 const sal_uInt32
nAttributes(xAttribs
->getLength());
347 for(sal_uInt32
a(0); a
< nAttributes
; a
++)
349 const OUString
aTokenName(xAttribs
->getNameByIndex(a
));
350 const SVGToken
aSVGToken(StrToSVGToken(aTokenName
, false));
352 parseAttribute(aTokenName
, aSVGToken
, xAttribs
->getValueByIndex(a
));
356 Display
getDisplayFromContent(const OUString
& aContent
)
358 if(!aContent
.isEmpty())
360 if(aContent
.startsWith("inline"))
362 return Display_inline
;
364 else if(aContent
.startsWith("none"))
368 else if(aContent
.startsWith("inherit"))
370 return Display_inherit
;
372 else if(aContent
.startsWith("block"))
374 return Display_block
;
376 else if(aContent
.startsWith("list-item"))
378 return Display_list_item
;
380 else if(aContent
.startsWith("run-in"))
382 return Display_run_in
;
384 else if(aContent
.startsWith("compact"))
386 return Display_compact
;
388 else if(aContent
.startsWith("marker"))
390 return Display_marker
;
392 else if(aContent
.startsWith("table"))
394 return Display_table
;
396 else if(aContent
.startsWith("inline-table"))
398 return Display_inline_table
;
400 else if(aContent
.startsWith("table-row-group"))
402 return Display_table_row_group
;
404 else if(aContent
.startsWith("table-header-group"))
406 return Display_table_header_group
;
408 else if(aContent
.startsWith("table-footer-group"))
410 return Display_table_footer_group
;
412 else if(aContent
.startsWith("table-row"))
414 return Display_table_row
;
416 else if(aContent
.startsWith("table-column-group"))
418 return Display_table_column_group
;
420 else if(aContent
.startsWith("table-column"))
422 return Display_table_column
;
424 else if(aContent
.startsWith("table-cell"))
426 return Display_table_cell
;
428 else if(aContent
.startsWith("table-caption"))
430 return Display_table_caption
;
434 // return the default
435 return Display_inline
;
438 void SvgNode::parseAttribute(const OUString
& /*rTokenName*/, SVGToken aSVGToken
, const OUString
& aContent
)
444 if(!aContent
.isEmpty())
452 if(!aContent
.isEmpty())
458 case SVGTokenXmlSpace
:
460 if(!aContent
.isEmpty())
462 if(aContent
.startsWith("default"))
464 setXmlSpace(XmlSpace_default
);
466 else if(aContent
.startsWith("preserve"))
468 setXmlSpace(XmlSpace_preserve
);
473 case SVGTokenDisplay
:
475 if(!aContent
.isEmpty())
477 setDisplay(getDisplayFromContent(aContent
));
488 void SvgNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence
& rTarget
, bool bReferenced
) const
490 if(Display_none
== getDisplay())
497 if(SVGTokenDefs
== getType() ||
498 SVGTokenSymbol
== getType() ||
499 SVGTokenClipPathNode
== getType() ||
500 SVGTokenMask
== getType() ||
501 SVGTokenMarker
== getType() ||
502 SVGTokenPattern
== getType())
504 // do not decompose defs or symbol nodes (these hold only style-like
505 // objects which may be used by referencing them) except when doing
506 // so controlled referenced
508 // also do not decompose ClipPaths and Masks. These should be embedded
509 // in a defs node (which gets not decomposed by itself), but you never
512 // also not directly used are Markers and Patterns, only indirecty used
515 // #i121656# also do not decompose nodes which have display="none" set
521 const SvgNodeVector
& rChildren
= getChildren();
523 if(!rChildren
.empty())
525 const sal_uInt32
nCount(rChildren
.size());
527 for(sal_uInt32
a(0); a
< nCount
; a
++)
529 SvgNode
* pCandidate
= rChildren
[a
];
531 if(pCandidate
&& Display_none
!= pCandidate
->getDisplay())
533 const SvgNodeVector
& rGrandChildren
= pCandidate
->getChildren();
534 const SvgStyleAttributes
* pChildStyles
= pCandidate
->getSvgStyleAttributes();
536 // - visible terminal nodes
537 // - all non-terminal nodes (might contain visible nodes down the hierarchy)
538 if( !rGrandChildren
.empty() || ( pChildStyles
&& (Visibility_visible
== pChildStyles
->getVisibility())) )
540 drawinglayer::primitive2d::Primitive2DSequence aNewTarget
;
541 pCandidate
->decomposeSvgNode(aNewTarget
, bReferenced
);
543 if(aNewTarget
.hasElements())
545 drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget
, aNewTarget
);
551 OSL_ENSURE(false, "Null-Pointer in child node list (!)");
555 if(rTarget
.hasElements())
557 const SvgStyleAttributes
* pStyles
= getSvgStyleAttributes();
560 // check if we have Title or Desc
561 const OUString
& rTitle
= pStyles
->getTitle();
562 const OUString
& rDesc
= pStyles
->getDesc();
564 if(!rTitle
.isEmpty() || !rDesc
.isEmpty())
566 // default object name is empty
567 OUString aObjectName
;
569 // use path as object name when outmost element
570 if(SVGTokenSvg
== getType())
572 aObjectName
= getDocument().getAbsolutePath();
574 if(!aObjectName
.isEmpty())
576 INetURLObject
aURL(aObjectName
);
578 aObjectName
= aURL
.getName(
579 INetURLObject::LAST_SEGMENT
,
581 INetURLObject::DECODE_WITH_CHARSET
);
585 // pack in ObjectInfoPrimitive2D group
586 const drawinglayer::primitive2d::Primitive2DReference
xRef(
587 new drawinglayer::primitive2d::ObjectInfoPrimitive2D(
593 rTarget
= drawinglayer::primitive2d::Primitive2DSequence(&xRef
, 1);
600 const basegfx::B2DRange
SvgNode::getCurrentViewPort() const
604 return getParent()->getCurrentViewPort();
608 return basegfx::B2DRange(); // return empty B2DRange
612 double SvgNode::getCurrentFontSizeInherited() const
616 return getParent()->getCurrentFontSize();
624 double SvgNode::getCurrentFontSize() const
626 if(getSvgStyleAttributes())
627 return getSvgStyleAttributes()->getFontSize().solve(*this, xcoordinate
);
629 return getCurrentFontSizeInherited();
632 double SvgNode::getCurrentXHeightInherited() const
636 return getParent()->getCurrentXHeight();
644 double SvgNode::getCurrentXHeight() const
646 if(getSvgStyleAttributes())
647 // for XHeight, use FontSize currently
648 return getSvgStyleAttributes()->getFontSize().solve(*this, ycoordinate
);
650 return getCurrentXHeightInherited();
653 void SvgNode::setId(const OUString
* pfId
)
657 mrDocument
.removeSvgNodeFromMapper(*mpId
);
664 mpId
= new OUString(*pfId
);
665 mrDocument
.addSvgNodeToMapper(*mpId
, *this);
669 void SvgNode::setClass(const OUString
* pfClass
)
673 mrDocument
.removeSvgNodeFromMapper(*mpClass
);
680 mpClass
= new OUString(*pfClass
);
681 mrDocument
.addSvgNodeToMapper(*mpClass
, *this);
685 XmlSpace
SvgNode::getXmlSpace() const
687 if(maXmlSpace
!= XmlSpace_notset
)
694 return getParent()->getXmlSpace();
697 // default is XmlSpace_default
698 return XmlSpace_default
;
701 } // end of namespace svgreader
702 } // end of namespace svgio
705 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */