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