xref: /aoo42x/main/svgio/source/svgreader/svgnode.cxx (revision 7b027e49)
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                 // #123510# if CSS styles were found, create a linked list with rOriginal as parent
118                 // and all CSS styles as linked children, so that the style attribute has
119                 // priority over the CSS style. If there is no style attribute this means that
120                 // no values are set at rOriginal, thus it is still correct to have that order.
121                 // Repeated style requests should only be issued from sub-Text nodes and I'm not
122                 // sure if in-between text nodes may build other chains (should not happen). But
123                 // it's only a re-chaining with pointers (cheap), so allow to do it every time.
124                 SvgStyleAttributes* pCurrent = const_cast< SvgStyleAttributes* >(&rOriginal);
125                 pCurrent->setCssStyleParent(0);
126 
127                 for(sal_uInt32 a(0); a < maCssStyleVector.size(); a++)
128                 {
129                     SvgStyleAttributes* pNext = const_cast< SvgStyleAttributes* >(maCssStyleVector[a]);
130 
131                     pCurrent->setCssStyleParent(pNext);
132                     pCurrent = pNext;
133                     pCurrent->setCssStyleParent(0);
134                 }
135             }
136 
137             return &rOriginal;
138         }
139 
140         SvgNode::SvgNode(
141             SVGToken aType,
142             SvgDocument& rDocument,
143             SvgNode* pParent)
144         :   maType(aType),
145             mrDocument(rDocument),
146             mpParent(pParent),
147             mpAlternativeParent(0),
148             maChildren(),
149             mpId(0),
150             mpClass(0),
151             maXmlSpace(XmlSpace_notset),
152             maDisplay(Display_inline),
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             // #122522# SVG defines that 'In general, this means that the presentation attributes have
188             // lower priority than other CSS style rules specified in author style sheets or �style�
189             // attributes.' in http://www.w3.org/TR/SVG/styling.html#UsingPresentationAttributes
190             // (6.4 Specifying properties using the presentation attributes SVG 1.1). That means that
191             // e.g. font-size will appear as presentation attribute and CSS style attribute. In these
192             // cases, CSS style attributes need to have precedence. To do so it is possible to create
193             // a proirity system for all properties of a shape, but it will also work to parse the
194             // presentation attributes of type 'style' last, so they will overwrite the less-prioritized
195             // already interpreted ones. Thus, remember SVGTokenStyle entries and parse them last.
196             // To make this work it is required that parseAttribute is only called by parseAttributes
197             // which is the case.
198             std::vector< sal_uInt32 > aSVGTokenStyleIndexes;
199 
200             for(sal_uInt32 a(0); a < nAttributes; a++)
201             {
202                 const ::rtl::OUString aTokenName(xAttribs->getNameByIndex(a));
203                 const SVGToken aSVGToken(StrToSVGToken(aTokenName));
204 
205                 if(SVGTokenStyle == aSVGToken)
206                 {
207                     // #122522# remember SVGTokenStyle entry
208                     aSVGTokenStyleIndexes.push_back(a);
209                 }
210                 else
211                 {
212                     parseAttribute(aTokenName, aSVGToken, xAttribs->getValueByIndex(a));
213                 }
214             }
215 
216             // #122522# parse SVGTokenStyle entries last to override already interpreted
217             // 'presentation attributes' of potenially the same type
218             for(sal_uInt32 b(0); b < aSVGTokenStyleIndexes.size(); b++)
219             {
220                 const sal_uInt32 nSVGTokenStyleIndex(aSVGTokenStyleIndexes[b]);
221                 const ::rtl::OUString aTokenName(xAttribs->getNameByIndex(nSVGTokenStyleIndex));
222 
223                 parseAttribute(aTokenName, SVGTokenStyle, xAttribs->getValueByIndex(nSVGTokenStyleIndex));
224             }
225         }
226 
227         Display getDisplayFromContent(const rtl::OUString& aContent)
228         {
229             if(aContent.getLength())
230             {
231                 static rtl::OUString aStrInline(rtl::OUString::createFromAscii("inline"));
232                 static rtl::OUString aStrBlock(rtl::OUString::createFromAscii("block"));
233                 static rtl::OUString aStrList_item(rtl::OUString::createFromAscii("list-item"));
234                 static rtl::OUString aStrRun_in(rtl::OUString::createFromAscii("run-in"));
235                 static rtl::OUString aStrCompact(rtl::OUString::createFromAscii("compact"));
236                 static rtl::OUString aStrMarker(rtl::OUString::createFromAscii("marker"));
237                 static rtl::OUString aStrTable(rtl::OUString::createFromAscii("table"));
238                 static rtl::OUString aStrInline_table(rtl::OUString::createFromAscii("inline-table"));
239                 static rtl::OUString aStrTable_row_group(rtl::OUString::createFromAscii("table-row-group"));
240                 static rtl::OUString aStrTable_header_group(rtl::OUString::createFromAscii("table-header-group"));
241                 static rtl::OUString aStrTable_footer_group(rtl::OUString::createFromAscii("table-footer-group"));
242                 static rtl::OUString aStrTable_row(rtl::OUString::createFromAscii("table-row"));
243                 static rtl::OUString aStrTable_column_group(rtl::OUString::createFromAscii("table-column-group"));
244                 static rtl::OUString aStrTable_column(rtl::OUString::createFromAscii("table-column"));
245                 static rtl::OUString aStrTable_cell(rtl::OUString::createFromAscii("table-cell"));
246                 static rtl::OUString aStrTable_caption(rtl::OUString::createFromAscii("table-caption"));
247                 static rtl::OUString aStrNone(rtl::OUString::createFromAscii("none"));
248                 static rtl::OUString aStrInherit(rtl::OUString::createFromAscii("inherit"));
249 
250                 if(aContent.match(aStrInline))
251                 {
252                     return Display_inline;
253                 }
254                 else if(aContent.match(aStrNone))
255                 {
256                     return Display_none;
257                 }
258                 else if(aContent.match(aStrInherit))
259                 {
260                     return Display_inherit;
261                 }
262                 else if(aContent.match(aStrBlock))
263                 {
264                     return Display_block;
265                 }
266                 else if(aContent.match(aStrList_item))
267                 {
268                     return Display_list_item;
269                 }
270                 else if(aContent.match(aStrRun_in))
271                 {
272                     return Display_run_in;
273                 }
274                 else if(aContent.match(aStrCompact))
275                 {
276                     return Display_compact;
277                 }
278                 else if(aContent.match(aStrMarker))
279                 {
280                     return Display_marker;
281                 }
282                 else if(aContent.match(aStrTable))
283                 {
284                     return Display_table;
285                 }
286                 else if(aContent.match(aStrInline_table))
287                 {
288                     return Display_inline_table;
289                 }
290                 else if(aContent.match(aStrTable_row_group))
291                 {
292                     return Display_table_row_group;
293                 }
294                 else if(aContent.match(aStrTable_header_group))
295                 {
296                     return Display_table_header_group;
297                 }
298                 else if(aContent.match(aStrTable_footer_group))
299                 {
300                     return Display_table_footer_group;
301                 }
302                 else if(aContent.match(aStrTable_row))
303                 {
304                     return Display_table_row;
305                 }
306                 else if(aContent.match(aStrTable_column_group))
307                 {
308                     return Display_table_column_group;
309                 }
310                 else if(aContent.match(aStrTable_column))
311                 {
312                     return Display_table_column;
313                 }
314                 else if(aContent.match(aStrTable_cell))
315                 {
316                     return Display_table_cell;
317                 }
318                 else if(aContent.match(aStrTable_caption))
319                 {
320                     return Display_table_caption;
321                 }
322             }
323 
324             // return the default
325             return Display_inline;
326         }
327 
328         void SvgNode::parseAttribute(const rtl::OUString& /*rTokenName*/, SVGToken aSVGToken, const rtl::OUString& aContent)
329         {
330             switch(aSVGToken)
331             {
332                 case SVGTokenId:
333                 {
334                     if(aContent.getLength())
335                     {
336                         setId(&aContent);
337                     }
338                     break;
339                 }
340                 case SVGTokenClass:
341                 {
342                     if(aContent.getLength())
343                     {
344                         setClass(&aContent);
345                     }
346                     break;
347                 }
348                 case SVGTokenXmlSpace:
349                 {
350                     if(aContent.getLength())
351                     {
352                         static rtl::OUString aStrDefault(rtl::OUString::createFromAscii("default"));
353                         static rtl::OUString aStrPreserve(rtl::OUString::createFromAscii("preserve"));
354 
355                         if(aContent.match(aStrDefault))
356                         {
357                             setXmlSpace(XmlSpace_default);
358                         }
359                         else if(aContent.match(aStrPreserve))
360                         {
361                             setXmlSpace(XmlSpace_preserve);
362                         }
363                     }
364                     break;
365                 }
366                 case SVGTokenDisplay:
367                 {
368                     if(aContent.getLength())
369                     {
370                         setDisplay(getDisplayFromContent(aContent));
371                     }
372                     break;
373                 }
374                 default:
375                 {
376                     break;
377                 }
378             }
379         }
380 
381         void SvgNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool bReferenced) const
382         {
383             if(Display_none == getDisplay())
384             {
385                 return;
386             }
387 
388             if(!bReferenced)
389             {
390                 if(SVGTokenDefs == getType() ||
391                     SVGTokenSymbol == getType() ||
392                     SVGTokenClipPathNode == getType() ||
393                     SVGTokenMask == getType() ||
394                     SVGTokenMarker == getType() ||
395                     SVGTokenPattern == getType())
396                 {
397                     // do not decompose defs or symbol nodes (these hold only style-like
398                     // objects which may be used by referencing them) except when doing
399                     // so controlled referenced
400 
401                     // also do not decompose ClipPaths and Masks. These should be embedded
402                     // in a defs node (which gets not decomposed by itself), but you never
403                     // know
404 
405                     // also not directly used are Markers and Patterns, only indirecty used
406                     // by reference
407 
408                     // #121656# also do not decompose nodes which have display="none" set
409                     // as property
410                     return;
411                 }
412             }
413 
414             const SvgNodeVector& rChildren = getChildren();
415 
416             if(!rChildren.empty())
417             {
418                 const sal_uInt32 nCount(rChildren.size());
419 
420                 for(sal_uInt32 a(0); a < nCount; a++)
421                 {
422                     SvgNode* pCandidate = rChildren[a];
423 
424                     if(pCandidate && Display_none != pCandidate->getDisplay())
425                     {
426                         drawinglayer::primitive2d::Primitive2DSequence aNewTarget;
427 
428                         pCandidate->decomposeSvgNode(aNewTarget, bReferenced);
429 
430                         if(aNewTarget.hasElements())
431                         {
432                             drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewTarget);
433                         }
434                     }
435                     else
436                     {
437                         OSL_ENSURE(false, "Null-Pointer in child node list (!)");
438                     }
439                 }
440 
441                 if(rTarget.hasElements())
442                 {
443                     const SvgStyleAttributes* pStyles = getSvgStyleAttributes();
444 
445                     if(pStyles)
446                     {
447                         // check if we have Title or Desc
448                         const rtl::OUString& rTitle = pStyles->getTitle();
449                         const rtl::OUString& rDesc = pStyles->getDesc();
450 
451                         if(rTitle.getLength() || rDesc.getLength())
452                         {
453                             // default object name is empty
454                             rtl::OUString aObjectName;
455 
456                             // use path as object name when outmost element
457                             if(SVGTokenSvg == getType())
458                             {
459                                 aObjectName = getDocument().getAbsolutePath();
460 
461                                 if(aObjectName.getLength())
462                                 {
463                             		INetURLObject aURL(aObjectName);
464 
465                                     aObjectName = aURL.getName(
466                                         INetURLObject::LAST_SEGMENT,
467                                         true,
468                                         INetURLObject::DECODE_WITH_CHARSET);
469                                 }
470                             }
471 
472                             // pack in ObjectInfoPrimitive2D group
473                             const drawinglayer::primitive2d::Primitive2DReference xRef(
474                                 new drawinglayer::primitive2d::ObjectInfoPrimitive2D(
475                                     rTarget,
476                                     aObjectName,
477                                     rTitle,
478                                     rDesc));
479 
480                             rTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
481                         }
482                     }
483                 }
484             }
485         }
486 
487         const basegfx::B2DRange SvgNode::getCurrentViewPort() const
488         {
489             if(getParent())
490             {
491                 return getParent()->getCurrentViewPort();
492             }
493             else
494             {
495                 return basegfx::B2DRange(); // return empty B2DRange
496             }
497         }
498 
499         double SvgNode::getCurrentFontSize() const
500         {
501             if(getSvgStyleAttributes())
502             {
503                 return getSvgStyleAttributes()->getFontSize().solve(*this, xcoordinate);
504             }
505             else if(getParent())
506             {
507                 return getParent()->getCurrentFontSize();
508             }
509             else
510             {
511                 return 0.0;
512             }
513         }
514 
515         double SvgNode::getCurrentXHeight() const
516         {
517             if(getSvgStyleAttributes())
518             {
519                 // for XHeight, use FontSize currently
520                 return getSvgStyleAttributes()->getFontSize().solve(*this, ycoordinate);
521             }
522             else if(getParent())
523             {
524                 return getParent()->getCurrentXHeight();
525             }
526             else
527             {
528                 return 0.0;
529             }
530         }
531 
532         void SvgNode::setId(const rtl::OUString* pfId)
533         {
534             if(mpId)
535             {
536                 mrDocument.removeSvgNodeFromMapper(*mpId);
537                 delete mpId;
538                 mpId = 0;
539             }
540 
541             if(pfId)
542             {
543                 mpId = new rtl::OUString(*pfId);
544                 mrDocument.addSvgNodeToMapper(*mpId, *this);
545             }
546         }
547 
548         void SvgNode::setClass(const rtl::OUString* pfClass)
549         {
550             if(mpClass)
551             {
552                 mrDocument.removeSvgNodeFromMapper(*mpClass);
553                 delete mpClass;
554                 mpClass = 0;
555             }
556 
557             if(pfClass)
558             {
559                 mpClass = new rtl::OUString(*pfClass);
560                 mrDocument.addSvgNodeToMapper(*mpClass, *this);
561             }
562         }
563 
564         XmlSpace SvgNode::getXmlSpace() const
565         {
566             if(maXmlSpace != XmlSpace_notset)
567             {
568                 return maXmlSpace;
569             }
570 
571             if(getParent())
572             {
573                 return getParent()->getXmlSpace();
574             }
575 
576             // default is XmlSpace_default
577             return XmlSpace_default;
578         }
579 
580     } // end of namespace svgreader
581 } // end of namespace svgio
582 
583 //////////////////////////////////////////////////////////////////////////////
584 // eof
585