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