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