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