xref: /trunk/main/svgio/source/svgreader/svgnode.cxx (revision 0906e779)
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