xref: /aoo42x/main/svgio/source/svgreader/svgnode.cxx (revision 52cb04b8)
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 
206         const SvgStyleAttributes* SvgNode::checkForCssStyle(const rtl::OUString& rClassStr, const SvgStyleAttributes& rOriginal) const
207         {
208             if(!mbCssStyleVectorBuilt)
209             {
210                 // build needed CssStyleVector for local node
211                 const_cast< SvgNode* >(this)->fillCssStyleVector(rClassStr);
212             }
213 
214             if(maCssStyleVector.empty())
215             {
216                 // return given original if no CssStlyes found
217                 return &rOriginal;
218             }
219             else
220             {
221                 // #125293# rOriginal will be the last element in the linked list; use no CssStyleParent
222                 // there (reset it) to ensure that the parent hierarchy will be used when it's base
223                 // is referenced. This new chaning inserts the CssStyles before the original style,
224                 // this makes the whole process much safer since the original style when used will
225                 // be not different to the situation without CssStyles; thus loops which may be caused
226                 // by trying to use the parent hierarchy of the owner of the style will be avoided
227                 // already in this mechanism. It's still good to keep the supportsParentStyle
228                 // from #125258# in place, though.
229                 // This chain building using pointers will be done every time when checkForCssStyle
230                 // is used (not the search, only the chaining). This is needed since the CssStyles
231                 // themselves will be potentially used multiple times. It is not expensive since it's
232                 // only changing some pointers.
233                 // The alternative would be to create the style hierarchy for every element (or even
234                 // for the element containing the hierarchy) in a vector of pointers and to use that.
235                 // Resetting the CssStyleParent on rOriginal is probably not needeed
236                 // but simply safer to do.
237                 const_cast< SvgStyleAttributes& >(rOriginal).setCssStyleParent(0);
238 
239                 // loop over the existing CssStyles and link them. There is a first one, take
240                 // as current
241                 SvgStyleAttributes* pCurrent = const_cast< SvgStyleAttributes* >(maCssStyleVector[0]);
242 
243                 for(sal_uInt32 a(1); a < maCssStyleVector.size(); a++)
244                 {
245                     SvgStyleAttributes* pNext = const_cast< SvgStyleAttributes* >(maCssStyleVector[a]);
246 
247                     pCurrent->setCssStyleParent(pNext);
248                     pCurrent = pNext;
249                 }
250 
251                 // pCurrent is the last used CssStyle, let it point to the original style
252                 pCurrent->setCssStyleParent(&rOriginal);
253 
254                 // return 1st CssStyle as style chain start element (only for the
255                 // local element, still no hierarchy used here)
256                 return maCssStyleVector[0];
257             }
258         }
259 
260         SvgNode::SvgNode(
261             SVGToken aType,
262             SvgDocument& rDocument,
263             SvgNode* pParent)
264         :   maType(aType),
265             mrDocument(rDocument),
266             mpParent(pParent),
267             mpAlternativeParent(0),
268             maChildren(),
269             mpId(0),
270             mpClass(0),
271             maXmlSpace(XmlSpace_notset),
272             maDisplay(Display_inline),
273             maCssStyleVector(),
274             mpLocalCssStyle(0),
275             mbCssStyleVectorBuilt(false)
276         {
277             OSL_ENSURE(SVGTokenUnknown != maType, "SvgNode with unknown type created (!)");
278 
279             if(pParent)
280             {
281                 pParent->maChildren.push_back(this);
282             }
283             else
284             {
285 #ifdef DBG_UTIL
286                 if(SVGTokenSvg != getType())
287                 {
288                     OSL_ENSURE(false, "No parent for this node (!)");
289                 }
290 #endif
291             }
292         }
293 
294         SvgNode::~SvgNode()
295         {
296             while(maChildren.size())
297             {
298                 delete maChildren[maChildren.size() - 1];
299                 maChildren.pop_back();
300             }
301 
302             if(mpId)
303             {
304                 delete mpId;
305             }
306 
307             if(mpClass)
308             {
309                 delete mpClass;
310             }
311 
312             if(mpLocalCssStyle)
313             {
314                 delete mpLocalCssStyle;
315             }
316         }
317 
318         void SvgNode::readLocalCssStyle(const rtl::OUString& aContent)
319         {
320             if(!mpLocalCssStyle)
321             {
322                 // create LocalCssStyle if needed but not yet added
323                 mpLocalCssStyle = new SvgStyleAttributes(*this);
324             }
325             else
326             {
327                 // 2nd fill would be an error
328                 OSL_ENSURE(false, "Svg node has two local CssStyles, this may lead to problems (!)");
329             }
330 
331             if(mpLocalCssStyle)
332             {
333                 // parse and set values to it
334                 mpLocalCssStyle->readCssStyle(aContent);
335             }
336             else
337             {
338                 OSL_ENSURE(false, "Could not get/create a local CssStyle for a node (!)");
339             }
340         }
341 
342         void SvgNode::parseAttributes(const com::sun::star::uno::Reference< com::sun::star::xml::sax::XAttributeList >& xAttribs)
343         {
344             // no longer need to pre-sort moving 'style' entries to the back so that
345             // values get overwritten - that was the previous, not complete solution for
346             // handling the priorities between svg and Css properties
347             const sal_uInt32 nAttributes(xAttribs->getLength());
348 
349             for(sal_uInt32 a(0); a < nAttributes; a++)
350             {
351                 const ::rtl::OUString aTokenName(xAttribs->getNameByIndex(a));
352                 const SVGToken aSVGToken(StrToSVGToken(aTokenName, false));
353 
354                 parseAttribute(aTokenName, aSVGToken, xAttribs->getValueByIndex(a));
355             }
356         }
357 
358         Display getDisplayFromContent(const rtl::OUString& aContent)
359         {
360             if(aContent.getLength())
361             {
362                 static rtl::OUString aStrInline(rtl::OUString::createFromAscii("inline"));
363                 static rtl::OUString aStrBlock(rtl::OUString::createFromAscii("block"));
364                 static rtl::OUString aStrList_item(rtl::OUString::createFromAscii("list-item"));
365                 static rtl::OUString aStrRun_in(rtl::OUString::createFromAscii("run-in"));
366                 static rtl::OUString aStrCompact(rtl::OUString::createFromAscii("compact"));
367                 static rtl::OUString aStrMarker(rtl::OUString::createFromAscii("marker"));
368                 static rtl::OUString aStrTable(rtl::OUString::createFromAscii("table"));
369                 static rtl::OUString aStrInline_table(rtl::OUString::createFromAscii("inline-table"));
370                 static rtl::OUString aStrTable_row_group(rtl::OUString::createFromAscii("table-row-group"));
371                 static rtl::OUString aStrTable_header_group(rtl::OUString::createFromAscii("table-header-group"));
372                 static rtl::OUString aStrTable_footer_group(rtl::OUString::createFromAscii("table-footer-group"));
373                 static rtl::OUString aStrTable_row(rtl::OUString::createFromAscii("table-row"));
374                 static rtl::OUString aStrTable_column_group(rtl::OUString::createFromAscii("table-column-group"));
375                 static rtl::OUString aStrTable_column(rtl::OUString::createFromAscii("table-column"));
376                 static rtl::OUString aStrTable_cell(rtl::OUString::createFromAscii("table-cell"));
377                 static rtl::OUString aStrTable_caption(rtl::OUString::createFromAscii("table-caption"));
378                 static rtl::OUString aStrNone(rtl::OUString::createFromAscii("none"));
379                 static rtl::OUString aStrInherit(rtl::OUString::createFromAscii("inherit"));
380 
381                 if(aContent.match(aStrInline))
382                 {
383                     return Display_inline;
384                 }
385                 else if(aContent.match(aStrNone))
386                 {
387                     return Display_none;
388                 }
389                 else if(aContent.match(aStrInherit))
390                 {
391                     return Display_inherit;
392                 }
393                 else if(aContent.match(aStrBlock))
394                 {
395                     return Display_block;
396                 }
397                 else if(aContent.match(aStrList_item))
398                 {
399                     return Display_list_item;
400                 }
401                 else if(aContent.match(aStrRun_in))
402                 {
403                     return Display_run_in;
404                 }
405                 else if(aContent.match(aStrCompact))
406                 {
407                     return Display_compact;
408                 }
409                 else if(aContent.match(aStrMarker))
410                 {
411                     return Display_marker;
412                 }
413                 else if(aContent.match(aStrTable))
414                 {
415                     return Display_table;
416                 }
417                 else if(aContent.match(aStrInline_table))
418                 {
419                     return Display_inline_table;
420                 }
421                 else if(aContent.match(aStrTable_row_group))
422                 {
423                     return Display_table_row_group;
424                 }
425                 else if(aContent.match(aStrTable_header_group))
426                 {
427                     return Display_table_header_group;
428                 }
429                 else if(aContent.match(aStrTable_footer_group))
430                 {
431                     return Display_table_footer_group;
432                 }
433                 else if(aContent.match(aStrTable_row))
434                 {
435                     return Display_table_row;
436                 }
437                 else if(aContent.match(aStrTable_column_group))
438                 {
439                     return Display_table_column_group;
440                 }
441                 else if(aContent.match(aStrTable_column))
442                 {
443                     return Display_table_column;
444                 }
445                 else if(aContent.match(aStrTable_cell))
446                 {
447                     return Display_table_cell;
448                 }
449                 else if(aContent.match(aStrTable_caption))
450                 {
451                     return Display_table_caption;
452                 }
453             }
454 
455             // return the default
456             return Display_inline;
457         }
458 
459         void SvgNode::parseAttribute(const rtl::OUString& /*rTokenName*/, SVGToken aSVGToken, const rtl::OUString& aContent)
460         {
461             switch(aSVGToken)
462             {
463                 case SVGTokenId:
464                 {
465                     if(aContent.getLength())
466                     {
467                         setId(&aContent);
468                     }
469                     break;
470                 }
471                 case SVGTokenClass:
472                 {
473                     if(aContent.getLength())
474                     {
475                         setClass(&aContent);
476                     }
477                     break;
478                 }
479                 case SVGTokenXmlSpace:
480                 {
481                     if(aContent.getLength())
482                     {
483                         static rtl::OUString aStrDefault(rtl::OUString::createFromAscii("default"));
484                         static rtl::OUString aStrPreserve(rtl::OUString::createFromAscii("preserve"));
485 
486                         if(aContent.match(aStrDefault))
487                         {
488                             setXmlSpace(XmlSpace_default);
489                         }
490                         else if(aContent.match(aStrPreserve))
491                         {
492                             setXmlSpace(XmlSpace_preserve);
493                         }
494                     }
495                     break;
496                 }
497                 case SVGTokenDisplay:
498                 {
499                     if(aContent.getLength())
500                     {
501                         setDisplay(getDisplayFromContent(aContent));
502                     }
503                     break;
504                 }
505                 default:
506                 {
507                     break;
508                 }
509             }
510         }
511 
512         void SvgNode::decomposeSvgNode(drawinglayer::primitive2d::Primitive2DSequence& rTarget, bool bReferenced) const
513         {
514             if(Display_none == getDisplay())
515             {
516                 return;
517             }
518 
519             if(!bReferenced)
520             {
521                 if(SVGTokenDefs == getType() ||
522                     SVGTokenSymbol == getType() ||
523                     SVGTokenClipPathNode == getType() ||
524                     SVGTokenMask == getType() ||
525                     SVGTokenMarker == getType() ||
526                     SVGTokenPattern == getType())
527                 {
528                     // do not decompose defs or symbol nodes (these hold only style-like
529                     // objects which may be used by referencing them) except when doing
530                     // so controlled referenced
531 
532                     // also do not decompose ClipPaths and Masks. These should be embedded
533                     // in a defs node (which gets not decomposed by itself), but you never
534                     // know
535 
536                     // also not directly used are Markers and Patterns, only indirecty used
537                     // by reference
538 
539                     // #121656# also do not decompose nodes which have display="none" set
540                     // as property
541                     return;
542                 }
543             }
544 
545             const SvgNodeVector& rChildren = getChildren();
546 
547             if(!rChildren.empty())
548             {
549                 const sal_uInt32 nCount(rChildren.size());
550 
551                 for(sal_uInt32 a(0); a < nCount; a++)
552                 {
553                     SvgNode* pCandidate = rChildren[a];
554 
555                     if(pCandidate && Display_none != pCandidate->getDisplay())
556                     {
557                         drawinglayer::primitive2d::Primitive2DSequence aNewTarget;
558 
559                         pCandidate->decomposeSvgNode(aNewTarget, bReferenced);
560 
561                         if(aNewTarget.hasElements())
562                         {
563                             drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aNewTarget);
564                         }
565                     }
566                     else
567                     {
568                         OSL_ENSURE(false, "Null-Pointer in child node list (!)");
569                     }
570                 }
571 
572                 if(rTarget.hasElements())
573                 {
574                     const SvgStyleAttributes* pStyles = getSvgStyleAttributes();
575 
576                     if(pStyles)
577                     {
578                         // check if we have Title or Desc
579                         const rtl::OUString& rTitle = pStyles->getTitle();
580                         const rtl::OUString& rDesc = pStyles->getDesc();
581 
582                         if(rTitle.getLength() || rDesc.getLength())
583                         {
584                             // default object name is empty
585                             rtl::OUString aObjectName;
586 
587                             // use path as object name when outmost element
588                             if(SVGTokenSvg == getType())
589                             {
590                                 aObjectName = getDocument().getAbsolutePath();
591 
592                                 if(aObjectName.getLength())
593                                 {
594                             		INetURLObject aURL(aObjectName);
595 
596                                     aObjectName = aURL.getName(
597                                         INetURLObject::LAST_SEGMENT,
598                                         true,
599                                         INetURLObject::DECODE_WITH_CHARSET);
600                                 }
601                             }
602 
603                             // pack in ObjectInfoPrimitive2D group
604                             const drawinglayer::primitive2d::Primitive2DReference xRef(
605                                 new drawinglayer::primitive2d::ObjectInfoPrimitive2D(
606                                     rTarget,
607                                     aObjectName,
608                                     rTitle,
609                                     rDesc));
610 
611                             rTarget = drawinglayer::primitive2d::Primitive2DSequence(&xRef, 1);
612                         }
613                     }
614                 }
615             }
616         }
617 
618         const basegfx::B2DRange SvgNode::getCurrentViewPort() const
619         {
620             if(getParent())
621             {
622                 return getParent()->getCurrentViewPort();
623             }
624             else
625             {
626                 return basegfx::B2DRange(); // return empty B2DRange
627             }
628         }
629 
630         double SvgNode::getCurrentFontSize() const
631         {
632             if(getSvgStyleAttributes())
633             {
634                 return getSvgStyleAttributes()->getFontSize().solve(*this, xcoordinate);
635             }
636             else if(getParent())
637             {
638                 return getParent()->getCurrentFontSize();
639             }
640             else
641             {
642                 return 0.0;
643             }
644         }
645 
646         double SvgNode::getCurrentXHeight() const
647         {
648             if(getSvgStyleAttributes())
649             {
650                 // for XHeight, use FontSize currently
651                 return getSvgStyleAttributes()->getFontSize().solve(*this, ycoordinate);
652             }
653             else if(getParent())
654             {
655                 return getParent()->getCurrentXHeight();
656             }
657             else
658             {
659                 return 0.0;
660             }
661         }
662 
663         void SvgNode::setId(const rtl::OUString* pfId)
664         {
665             if(mpId)
666             {
667                 mrDocument.removeSvgNodeFromMapper(*mpId);
668                 delete mpId;
669                 mpId = 0;
670             }
671 
672             if(pfId)
673             {
674                 mpId = new rtl::OUString(*pfId);
675                 mrDocument.addSvgNodeToMapper(*mpId, *this);
676             }
677         }
678 
679         void SvgNode::setClass(const rtl::OUString* pfClass)
680         {
681             if(mpClass)
682             {
683                 mrDocument.removeSvgNodeFromMapper(*mpClass);
684                 delete mpClass;
685                 mpClass = 0;
686             }
687 
688             if(pfClass)
689             {
690                 mpClass = new rtl::OUString(*pfClass);
691                 mrDocument.addSvgNodeToMapper(*mpClass, *this);
692             }
693         }
694 
695         XmlSpace SvgNode::getXmlSpace() const
696         {
697             if(maXmlSpace != XmlSpace_notset)
698             {
699                 return maXmlSpace;
700             }
701 
702             if(getParent())
703             {
704                 return getParent()->getXmlSpace();
705             }
706 
707             // default is XmlSpace_default
708             return XmlSpace_default;
709         }
710 
711     } // end of namespace svgreader
712 } // end of namespace svgio
713 
714 //////////////////////////////////////////////////////////////////////////////
715 // eof
716