xref: /aoo42x/main/svgio/source/svgreader/svgnode.cxx (revision e9ec990d)
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         void SvgNode::fillCssStyleVectorUsingHierarchyAndSelectors(
51             const rtl::OUString& rClassStr,
52             const SvgNode& rCurrent,
53             rtl::OUString aConcatenated)
54         {
55             const SvgDocument& rDocument = getDocument();
56 
57             if(rDocument.hasGlobalCssStyleAttributes())
58             {
59                 const SvgNode* pParent = rCurrent.getParent();
60 
61                 // check for ID (highest priority)
62                 if(rCurrent.getId())
63                 {
64                     const rtl::OUString& rId = *rCurrent.getId();
65 
66                     if(rId.getLength())
67                     {
68                         const rtl::OUString aNewConcatenated(
69                             rtl::OUString::createFromAscii("#") +
70                             rId +
71                             aConcatenated);
72 
73                         if(pParent)
74                         {
75                             // check for combined selectors at parent firstso that higher specificity will be in front
76                             fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr, *pParent, aNewConcatenated);
77                         }
78 
79                         const SvgStyleAttributes* pNew = rDocument.findGlobalCssStyleAttributes(aNewConcatenated);
80 
81                         if(pNew)
82                         {
83                             // add CssStyle if found
84                             maCssStyleVector.push_back(pNew);
85                         }
86                     }
87                 }
88 
89                 // check for 'class' references (a list of entries is allowed)
90                 if(rCurrent.getClass())
91                 {
92                     const rtl::OUString& rClassList = *rCurrent.getClass();
93                     const sal_Int32 nLen(rClassList.getLength());
94 
95                     if(nLen)
96                     {
97                         std::vector< rtl::OUString > aParts;
98                         sal_Int32 nPos(0);
99                         rtl::OUStringBuffer aToken;
100 
101                         while(nPos < nLen)
102                         {
103                             const sal_Int32 nInitPos(nPos);
104                             copyToLimiter(rClassList, sal_Unicode(' '), nPos, aToken, nLen);
105                             skip_char(rClassList, sal_Unicode(' '), nPos, nLen);
106                             const rtl::OUString aPart(aToken.makeStringAndClear().trim());
107 
108                             if(aPart.getLength())
109                             {
110                                 aParts.push_back(aPart);
111                             }
112 
113                             if(nInitPos == nPos)
114                             {
115                                 OSL_ENSURE(false, "Could not interpret on current position (!)");
116                                 nPos++;
117                             }
118                         }
119 
120                         for(sal_uInt32 a(0); a < aParts.size(); a++)
121                         {
122                             const rtl::OUString aNewConcatenated(
123                                 rtl::OUString::createFromAscii(".") +
124                                 aParts[a] +
125                                 aConcatenated);
126 
127                             if(pParent)
128                             {
129                                 // check for combined selectors at parent firstso that higher specificity will be in front
130                                 fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr, *pParent, aNewConcatenated);
131                             }
132 
133                             const SvgStyleAttributes* pNew = rDocument.findGlobalCssStyleAttributes(aNewConcatenated);
134 
135                             if(pNew)
136                             {
137                                 // add CssStyle if found
138                                 maCssStyleVector.push_back(pNew);
139                             }
140                         }
141                     }
142                 }
143 
144                 // check for class-dependent references to CssStyles
145                 if(rClassStr.getLength())
146                 {
147                     rtl::OUString aNewConcatenated(aConcatenated);
148 
149                     if(!rCurrent.getId() && !rCurrent.getClass() && 0 == aConcatenated.indexOf(rClassStr))
150                     {
151                         // no new CssStyle Selector and already starts with rClassStr, do not concatenate;
152                         // we pass an 'empty' node (in the sense of CssStyle Selector)
153                     }
154                     else
155                     {
156                         aNewConcatenated = rClassStr + aConcatenated;
157                     }
158 
159                     if(pParent)
160                     {
161                         // check for combined selectors at parent firstso that higher specificity will be in front
162                         fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr, *pParent, aNewConcatenated);
163                     }
164 
165                     const SvgStyleAttributes* pNew = rDocument.findGlobalCssStyleAttributes(aNewConcatenated);
166 
167                     if(pNew)
168                     {
169                         // add CssStyle if found
170                         maCssStyleVector.push_back(pNew);
171                     }
172                 }
173             }
174         }
175 
176         void SvgNode::fillCssStyleVector(const rtl::OUString& rClassStr)
177         {
178             OSL_ENSURE(!mbCssStyleVectorBuilt, "OOps, fillCssStyleVector called double ?!?");
179             mbCssStyleVectorBuilt = true;
180 
181             // #125293# If we have CssStyles we need to buuild a linked list of SvgStyleAttributes
182             // which represent this for the current object. There are various methods to
183             // specify CssStyles which need to be taken into account in a given order:
184             // - local CssStyle (independent from global CssStyles at SvgDocument)
185             // - 'id' CssStyle
186             // - 'class' CssStyle(s)
187             // - type-dependent elements (e..g. 'rect' for all rect elements)
188             // - local attributes (rOriginal)
189             // - inherited attributes (up the hierarchy)
190             // The first four will be collected in maCssStyleVector for the current element
191             // (once, this will not change) and be linked in the needed order using the
192             // get/setCssStyleParent at the SvgStyleAttributes which will be used preferred in
193             // member evaluation over the existing parent hierarchy
194 
195             // check for local CssStyle with highest priority
196             if(mpLocalCssStyle)
197             {
198                 // if we have one, use as first entry
199                 maCssStyleVector.push_back(mpLocalCssStyle);
200             }
201 
202             // check the hierarchy for concatenated patterns of Selectors
203             fillCssStyleVectorUsingHierarchyAndSelectors(rClassStr, *this, rtl::OUString());
204 
205             // #125329# find Css selector '*', add as last element if found
206             const SvgStyleAttributes* pNew = getDocument().findGlobalCssStyleAttributes(rtl::OUString::createFromAscii("*"));
207 
208             if(pNew)
209             {
210                 // add CssStyle for selector '*' if found
211                 maCssStyleVector.push_back(pNew);
212             }
213         }
214 
215         const SvgStyleAttributes* SvgNode::checkForCssStyle(const rtl::OUString& rClassStr, const SvgStyleAttributes& rOriginal) const
216         {
217             if(!mbCssStyleVectorBuilt)
218             {
219                 // build needed CssStyleVector for local node
220                 const_cast< SvgNode* >(this)->fillCssStyleVector(rClassStr);
221             }
222 
223             if(maCssStyleVector.empty())
224             {
225                 // return given original if no CssStlyes found
226                 return &rOriginal;
227             }
228             else
229             {
230                 // #125293# rOriginal will be the last element in the linked list; use no CssStyleParent
231                 // there (reset it) to ensure that the parent hierarchy will be used when it's base
232                 // is referenced. This new chaning inserts the CssStyles before the original style,
233                 // this makes the whole process much safer since the original style when used will
234                 // be not different to the situation without CssStyles; thus loops which may be caused
235                 // by trying to use the parent hierarchy of the owner of the style will be avoided
236                 // already in this mechanism. It's still good to keep the supportsParentStyle
237                 // from #125258# in place, though.
238                 // This chain building using pointers will be done every time when checkForCssStyle
239                 // is used (not the search, only the chaining). This is needed since the CssStyles
240                 // themselves will be potentially used multiple times. It is not expensive since it's
241                 // only changing some pointers.
242                 // The alternative would be to create the style hierarchy for every element (or even
243                 // for the element containing the hierarchy) in a vector of pointers and to use that.
244                 // Resetting the CssStyleParent on rOriginal is probably not needeed
245                 // but simply safer to do.
246                 const_cast< SvgStyleAttributes& >(rOriginal).setCssStyleParent(0);
247 
248                 // loop over the existing CssStyles and link them. There is a first one, take
249                 // as current
250                 SvgStyleAttributes* pCurrent = const_cast< SvgStyleAttributes* >(maCssStyleVector[0]);
251 
252                 for(sal_uInt32 a(1); a < maCssStyleVector.size(); a++)
253                 {
254                     SvgStyleAttributes* pNext = const_cast< SvgStyleAttributes* >(maCssStyleVector[a]);
255 
256                     pCurrent->setCssStyleParent(pNext);
257                     pCurrent = pNext;
258                 }
259 
260                 // pCurrent is the last used CssStyle, let it point to the original style
261                 pCurrent->setCssStyleParent(&rOriginal);
262 
263                 // return 1st CssStyle as style chain start element (only for the
264                 // local element, still no hierarchy used here)
265                 return maCssStyleVector[0];
266             }
267         }
268 
269         SvgNode::SvgNode(
270             SVGToken aType,
271             SvgDocument& rDocument,
272             SvgNode* pParent)
273         :   maType(aType),
274             mrDocument(rDocument),
275             mpParent(pParent),
276             mpAlternativeParent(0),
277             maChildren(),
278             mpId(0),
279             mpClass(0),
280             maXmlSpace(XmlSpace_notset),
281             maDisplay(Display_inline),
282             maCssStyleVector(),
283             mpLocalCssStyle(0),
284             mbCssStyleVectorBuilt(false)
285         {
286             OSL_ENSURE(SVGTokenUnknown != maType, "SvgNode with unknown type created (!)");
287 
288             if(pParent)
289             {
290                 pParent->maChildren.push_back(this);
291             }
292             else
293             {
294 #ifdef DBG_UTIL
295                 if(SVGTokenSvg != getType())
296                 {
297                     OSL_ENSURE(false, "No parent for this node (!)");
298                 }
299 #endif
300             }
301         }
302 
303         SvgNode::~SvgNode()
304         {
305             while(maChildren.size())
306             {
307                 delete maChildren[maChildren.size() - 1];
308                 maChildren.pop_back();
309             }
310 
311             if(mpId)
312             {
313                 delete mpId;
314             }
315 
316             if(mpClass)
317             {
318                 delete mpClass;
319             }
320 
321             if(mpLocalCssStyle)
322             {
323                 delete mpLocalCssStyle;
324             }
325         }
326 
327         void SvgNode::readLocalCssStyle(const rtl::OUString& aContent)
328         {
329             if(!mpLocalCssStyle)
330             {
331                 // create LocalCssStyle if needed but not yet added
332                 mpLocalCssStyle = new SvgStyleAttributes(*this);
333             }
334             else
335             {
336                 // 2nd fill would be an error
337                 OSL_ENSURE(false, "Svg node has two local CssStyles, this may lead to problems (!)");
338             }
339 
340             if(mpLocalCssStyle)
341             {
342                 // parse and set values to it
343                 mpLocalCssStyle->readCssStyle(aContent);
344             }
345             else
346             {
347                 OSL_ENSURE(false, "Could not get/create a local CssStyle for a node (!)");
348             }
349         }
350 
351         void SvgNode::parseAttributes(const com::sun::star::uno::Reference< com::sun::star::xml::sax::XAttributeList >& xAttribs)
352         {
353             // no longer need to pre-sort moving 'style' entries to the back so that
354             // values get overwritten - that was the previous, not complete solution for
355             // handling the priorities between svg and Css properties
356             const sal_uInt32 nAttributes(xAttribs->getLength());
357 
358             for(sal_uInt32 a(0); a < nAttributes; a++)
359             {
360                 const ::rtl::OUString aTokenName(xAttribs->getNameByIndex(a));
361                 const SVGToken aSVGToken(StrToSVGToken(aTokenName, false));
362 
363                 parseAttribute(aTokenName, aSVGToken, xAttribs->getValueByIndex(a));
364             }
365         }
366 
367         Display getDisplayFromContent(const rtl::OUString& aContent)
368         {
369             if(aContent.getLength())
370             {
371                 static rtl::OUString aStrInline(rtl::OUString::createFromAscii("inline"));
372                 static rtl::OUString aStrBlock(rtl::OUString::createFromAscii("block"));
373                 static rtl::OUString aStrList_item(rtl::OUString::createFromAscii("list-item"));
374                 static rtl::OUString aStrRun_in(rtl::OUString::createFromAscii("run-in"));
375                 static rtl::OUString aStrCompact(rtl::OUString::createFromAscii("compact"));
376                 static rtl::OUString aStrMarker(rtl::OUString::createFromAscii("marker"));
377                 static rtl::OUString aStrTable(rtl::OUString::createFromAscii("table"));
378                 static rtl::OUString aStrInline_table(rtl::OUString::createFromAscii("inline-table"));
379                 static rtl::OUString aStrTable_row_group(rtl::OUString::createFromAscii("table-row-group"));
380                 static rtl::OUString aStrTable_header_group(rtl::OUString::createFromAscii("table-header-group"));
381                 static rtl::OUString aStrTable_footer_group(rtl::OUString::createFromAscii("table-footer-group"));
382                 static rtl::OUString aStrTable_row(rtl::OUString::createFromAscii("table-row"));
383                 static rtl::OUString aStrTable_column_group(rtl::OUString::createFromAscii("table-column-group"));
384                 static rtl::OUString aStrTable_column(rtl::OUString::createFromAscii("table-column"));
385                 static rtl::OUString aStrTable_cell(rtl::OUString::createFromAscii("table-cell"));
386                 static rtl::OUString aStrTable_caption(rtl::OUString::createFromAscii("table-caption"));
387                 static rtl::OUString aStrNone(rtl::OUString::createFromAscii("none"));
388                 static rtl::OUString aStrInherit(rtl::OUString::createFromAscii("inherit"));
389 
390                 if(aContent.match(aStrInline))
391                 {
392                     return Display_inline;
393                 }
394                 else if(aContent.match(aStrNone))
395                 {
396                     return Display_none;
397                 }
398                 else if(aContent.match(aStrInherit))
399                 {
400                     return Display_inherit;
401                 }
402                 else if(aContent.match(aStrBlock))
403                 {
404                     return Display_block;
405                 }
406                 else if(aContent.match(aStrList_item))
407                 {
408                     return Display_list_item;
409                 }
410                 else if(aContent.match(aStrRun_in))
411                 {
412                     return Display_run_in;
413                 }
414                 else if(aContent.match(aStrCompact))
415                 {
416                     return Display_compact;
417                 }
418                 else if(aContent.match(aStrMarker))
419                 {
420                     return Display_marker;
421                 }
422                 else if(aContent.match(aStrTable))
423                 {
424                     return Display_table;
425                 }
426                 else if(aContent.match(aStrInline_table))
427                 {
428                     return Display_inline_table;
429                 }
430                 else if(aContent.match(aStrTable_row_group))
431                 {
432                     return Display_table_row_group;
433                 }
434                 else if(aContent.match(aStrTable_header_group))
435                 {
436                     return Display_table_header_group;
437                 }
438                 else if(aContent.match(aStrTable_footer_group))
439                 {
440                     return Display_table_footer_group;
441                 }
442                 else if(aContent.match(aStrTable_row))
443                 {
444                     return Display_table_row;
445                 }
446                 else if(aContent.match(aStrTable_column_group))
447                 {
448                     return Display_table_column_group;
449                 }
450                 else if(aContent.match(aStrTable_column))
451                 {
452                     return Display_table_column;
453                 }
454                 else if(aContent.match(aStrTable_cell))
455                 {
456                     return Display_table_cell;
457                 }
458                 else if(aContent.match(aStrTable_caption))
459                 {
460                     return Display_table_caption;
461                 }
462             }
463 
464             // return the default
465             return Display_inline;
466         }
467 
468         void SvgNode::parseAttribute(const rtl::OUString& /*rTokenName*/, SVGToken aSVGToken, const rtl::OUString& aContent)
469         {
470             switch(aSVGToken)
471             {
472                 case SVGTokenId:
473                 {
474                     if(aContent.getLength())
475                     {
476                         setId(&aContent);
477                     }
478                     break;
479                 }
480                 case SVGTokenClass:
481                 {
482                     if(aContent.getLength())
483                     {
484                         setClass(&aContent);
485                     }
486                     break;
487                 }
488                 case SVGTokenXmlSpace:
489                 {
490                     if(aContent.getLength())
491                     {
492                         static rtl::OUString aStrDefault(rtl::OUString::createFromAscii("default"));
493                         static rtl::OUString aStrPreserve(rtl::OUString::createFromAscii("preserve"));
494 
495                         if(aContent.match(aStrDefault))
496                         {
497                             setXmlSpace(XmlSpace_default);
498                         }
499                         else if(aContent.match(aStrPreserve))
500                         {
501                             setXmlSpace(XmlSpace_preserve);
502                         }
503                     }
504                     break;
505                 }
506                 case SVGTokenDisplay:
507                 {
508                     if(aContent.getLength())
509                     {
510                         setDisplay(getDisplayFromContent(aContent));
511                     }
512                     break;
513                 }
514                 default:
515                 {
516                     break;
517                 }
518             }
519         }
520 
521         void SvgNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool bReferenced) const
522         {
523             if(Display_none == getDisplay())
524             {
525                 return;
526             }
527 
528             if(!bReferenced)
529             {
530                 if(SVGTokenDefs == getType() ||
531                     SVGTokenSymbol == getType() ||
532                     SVGTokenClipPathNode == getType() ||
533                     SVGTokenMask == getType() ||
534                     SVGTokenMarker == getType() ||
535                     SVGTokenPattern == getType())
536                 {
537                     // do not decompose defs or symbol nodes (these hold only style-like
538                     // objects which may be used by referencing them) except when doing
539                     // so controlled referenced
540 
541                     // also do not decompose ClipPaths and Masks. These should be embedded
542                     // in a defs node (which gets not decomposed by itself), but you never
543                     // know
544 
545                     // also not directly used are Markers and Patterns, only indirecty used
546                     // by reference
547 
548                     // #121656# also do not decompose nodes which have display="none" set
549                     // as property
550                     return;
551                 }
552             }
553 
554             const SvgNodeVector& rChildren = getChildren();
555 
556             if(!rChildren.empty())
557             {
558                 const sal_uInt32 nCount(rChildren.size());
559 
560                 for(sal_uInt32 a(0); a < nCount; a++)
561                 {
562                     SvgNode* pCandidate = rChildren[a];
563 
564                     if(pCandidate && Display_none != pCandidate->getDisplay())
565                     {
566                         drawinglayer::primitive2d::Primitive2DSequence aNewTarget;
567 
568                         pCandidate->decomposeSvgNode(aNewTarget, bReferenced);
569 
570                         if(aNewTarget.hasElements())
571                         {
572                             drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewTarget);
573                         }
574                     }
575                     else
576                     {
577                         OSL_ENSURE(false, "Null-Pointer in child node list (!)");
578                     }
579                 }
580 
581                 if(rTarget.hasElements())
582                 {
583                     const SvgStyleAttributes* pStyles = getSvgStyleAttributes();
584 
585                     if(pStyles)
586                     {
587                         // check if we have Title or Desc
588                         const rtl::OUString& rTitle = pStyles->getTitle();
589                         const rtl::OUString& rDesc = pStyles->getDesc();
590 
591                         if(rTitle.getLength() || rDesc.getLength())
592                         {
593                             // default object name is empty
594                             rtl::OUString aObjectName;
595 
596                             // use path as object name when outmost element
597                             if(SVGTokenSvg == getType())
598                             {
599                                 aObjectName = getDocument().getAbsolutePath();
600 
601                                 if(aObjectName.getLength())
602                                 {
603                             		INetURLObject aURL(aObjectName);
604 
605                                     aObjectName = aURL.getName(
606                                         INetURLObject::LAST_SEGMENT,
607                                         true,
608                                         INetURLObject::DECODE_WITH_CHARSET);
609                                 }
610                             }
611 
612                             // pack in ObjectInfoPrimitive2D group
613                             const drawinglayer::primitive2d::Primitive2DReference xRef(
614                                 new drawinglayer::primitive2d::ObjectInfoPrimitive2D(
615                                     rTarget,
616                                     aObjectName,
617                                     rTitle,
618                                     rDesc));
619 
620                             rTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
621                         }
622                     }
623                 }
624             }
625         }
626 
627         const basegfx::B2DRange SvgNode::getCurrentViewPort() const
628         {
629             if(getParent())
630             {
631                 return getParent()->getCurrentViewPort();
632             }
633             else
634             {
635                 return basegfx::B2DRange(); // return empty B2DRange
636             }
637         }
638 
639         double SvgNode::getCurrentFontSize() const
640         {
641             if(getSvgStyleAttributes())
642             {
643                 return getSvgStyleAttributes()->getFontSize().solve(*this, xcoordinate);
644             }
645             else if(getParent())
646             {
647                 return getParent()->getCurrentFontSize();
648             }
649             else
650             {
651                 return 0.0;
652             }
653         }
654 
655         double SvgNode::getCurrentXHeight() const
656         {
657             if(getSvgStyleAttributes())
658             {
659                 // for XHeight, use FontSize currently
660                 return getSvgStyleAttributes()->getFontSize().solve(*this, ycoordinate);
661             }
662             else if(getParent())
663             {
664                 return getParent()->getCurrentXHeight();
665             }
666             else
667             {
668                 return 0.0;
669             }
670         }
671 
672         void SvgNode::setId(const rtl::OUString* pfId)
673         {
674             if(mpId)
675             {
676                 mrDocument.removeSvgNodeFromMapper(*mpId);
677                 delete mpId;
678                 mpId = 0;
679             }
680 
681             if(pfId)
682             {
683                 mpId = new rtl::OUString(*pfId);
684                 mrDocument.addSvgNodeToMapper(*mpId, *this);
685             }
686         }
687 
688         void SvgNode::setClass(const rtl::OUString* pfClass)
689         {
690             if(mpClass)
691             {
692                 mrDocument.removeSvgNodeFromMapper(*mpClass);
693                 delete mpClass;
694                 mpClass = 0;
695             }
696 
697             if(pfClass)
698             {
699                 mpClass = new rtl::OUString(*pfClass);
700                 mrDocument.addSvgNodeToMapper(*mpClass, *this);
701             }
702         }
703 
704         XmlSpace SvgNode::getXmlSpace() const
705         {
706             if(maXmlSpace != XmlSpace_notset)
707             {
708                 return maXmlSpace;
709             }
710 
711             if(getParent())
712             {
713                 return getParent()->getXmlSpace();
714             }
715 
716             // default is XmlSpace_default
717             return XmlSpace_default;
718         }
719 
720     } // end of namespace svgreader
721 } // end of namespace svgio
722 
723 //////////////////////////////////////////////////////////////////////////////
724 // eof
725