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/svgdocumenthandler.hxx>
26 #include <svgio/svgreader/svgtoken.hxx>
27 #include <svgio/svgreader/svgsvgnode.hxx>
28 #include <svgio/svgreader/svggnode.hxx>
29 #include <svgio/svgreader/svgnode.hxx>
30 #include <svgio/svgreader/svgpathnode.hxx>
31 #include <svgio/svgreader/svgrectnode.hxx>
32 #include <svgio/svgreader/svggradientnode.hxx>
33 #include <svgio/svgreader/svggradientstopnode.hxx>
34 #include <svgio/svgreader/svgsymbolnode.hxx>
35 #include <svgio/svgreader/svgusenode.hxx>
36 #include <svgio/svgreader/svgcirclenode.hxx>
37 #include <svgio/svgreader/svgellipsenode.hxx>
38 #include <svgio/svgreader/svglinenode.hxx>
39 #include <svgio/svgreader/svgpolynode.hxx>
40 #include <svgio/svgreader/svgsymbolnode.hxx>
41 #include <svgio/svgreader/svgtextnode.hxx>
42 #include <svgio/svgreader/svgcharacternode.hxx>
43 #include <svgio/svgreader/svgtspannode.hxx>
44 #include <svgio/svgreader/svgtrefnode.hxx>
45 #include <svgio/svgreader/svgtextpathnode.hxx>
46 #include <svgio/svgreader/svgstylenode.hxx>
47 #include <svgio/svgreader/svgimagenode.hxx>
48 #include <svgio/svgreader/svgclippathnode.hxx>
49 #include <svgio/svgreader/svgmasknode.hxx>
50 #include <svgio/svgreader/svgmarkernode.hxx>
51 #include <svgio/svgreader/svgpatternnode.hxx>
52 #include <svgio/svgreader/svgtitledescnode.hxx>
53 
54 //////////////////////////////////////////////////////////////////////////////
55 
56 using namespace com::sun::star;
57 
58 //////////////////////////////////////////////////////////////////////////////
59 
60 namespace
61 {
62     svgio::svgreader::SvgCharacterNode* whiteSpaceHandling(svgio::svgreader::SvgNode* pNode, svgio::svgreader::SvgCharacterNode* pLast)
63     {
64         if(pNode)
65         {
66             const svgio::svgreader::SvgNodeVector& rChilds = pNode->getChildren();
67             const sal_uInt32 nCount(rChilds.size());
68 
69             for(sal_uInt32 a(0); a < nCount; a++)
70             {
71                 svgio::svgreader::SvgNode* pCandidate = rChilds[a];
72 
73                 if(pCandidate)
74                 {
75                     switch(pCandidate->getType())
76                     {
77                         case svgio::svgreader::SVGTokenCharacter:
78                         {
79                             // clean whitespace in text span
80                             svgio::svgreader::SvgCharacterNode* pCharNode = static_cast< svgio::svgreader::SvgCharacterNode* >(pCandidate);
81                             pCharNode->whiteSpaceHandling();
82 
83                             // pCharNode may have lost all text. If that's the case, ignore
84                             // as invalid character node
85                             if(pCharNode->getText().getLength())
86                             {
87                                 if(pLast)
88                                 {
89                                     bool bAddGap(true);
90                                     static bool bNoGapsForBaselineShift(true);
91 
92                                     if(bNoGapsForBaselineShift)
93                                     {
94                                         // With this option a baseline shift between two char parts ('words')
95                                         // will not add a space 'gap' to the end of the (non-last) word. This
96                                         // seems to be the standard behaviour, see last bugdoc attached #122524#
97                                         const svgio::svgreader::SvgStyleAttributes* pStyleLast = pLast->getSvgStyleAttributes();
98                                         const svgio::svgreader::SvgStyleAttributes* pStyleCurrent = pCandidate->getSvgStyleAttributes();
99 
100                                         if(pStyleLast && pStyleCurrent && pStyleLast->getBaselineShift() != pStyleCurrent->getBaselineShift())
101                                         {
102                                             bAddGap = false;
103                                         }
104                                     }
105 
106                                     // add in-between whitespace (single space) to last
107                                     // known character node
108                                     if(bAddGap)
109                                     {
110                                         pLast->addGap();
111                                     }
112                                 }
113 
114                                 // remember new last corected character node
115                                 pLast = pCharNode;
116                             }
117                             break;
118                         }
119                         case svgio::svgreader::SVGTokenTspan:
120                         case svgio::svgreader::SVGTokenTextPath:
121                         case svgio::svgreader::SVGTokenTref:
122                         {
123                             // recursively clean whitespaces in subhierarchy
124                             pLast = whiteSpaceHandling(pCandidate, pLast);
125                             break;
126                         }
127                         default:
128                         {
129                             OSL_ENSURE(false, "Unexpected token inside SVGTokenText (!)");
130                             break;
131                         }
132                     }
133                 }
134             }
135         }
136 
137         return pLast;
138     }
139 }
140 
141 //////////////////////////////////////////////////////////////////////////////
142 
143 namespace svgio
144 {
145     namespace svgreader
146     {
147         SvgDocHdl::SvgDocHdl(const rtl::OUString& aAbsolutePath)
148         :   maDocument(aAbsolutePath),
149             mpTarget(0),
150             maCssContents()
151         {
152         }
153 
154         SvgDocHdl::~SvgDocHdl()
155         {
156 #ifdef DBG_UTIL
157             if(mpTarget)
158             {
159                 OSL_ENSURE(false, "SvgDocHdl destructed with active target (!)");
160                 delete mpTarget;
161             }
162             OSL_ENSURE(!maCssContents.size(), "SvgDocHdl destructed with active css style stack entry (!)");
163 #endif
164         }
165 
166         void SvgDocHdl::startDocument(  ) throw (xml::sax::SAXException, uno::RuntimeException)
167         {
168             OSL_ENSURE(!mpTarget, "Already a target at document start (!)");
169             OSL_ENSURE(!maCssContents.size(), "SvgDocHdl startDocument with active css style stack entry (!)");
170         }
171 
172         void SvgDocHdl::endDocument(  ) throw (xml::sax::SAXException, uno::RuntimeException)
173         {
174             OSL_ENSURE(!mpTarget, "Still a target at document end (!)");
175             OSL_ENSURE(!maCssContents.size(), "SvgDocHdl endDocument with active css style stack entry (!)");
176         }
177 
178         void SvgDocHdl::startElement( const ::rtl::OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs ) throw (xml::sax::SAXException, uno::RuntimeException)
179         {
180             if(aName.getLength())
181             {
182                 const SVGToken aSVGToken(StrToSVGToken(aName));
183 
184                 switch(aSVGToken)
185                 {
186                     /// structural elements
187                     case SVGTokenSymbol:
188                     {
189                         /// new basic node for Symbol. Content gets scanned, but
190                         /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
191                         mpTarget = new SvgSymbolNode(maDocument, mpTarget);
192                         mpTarget->parseAttributes(xAttribs);
193                         break;
194                     }
195                     case SVGTokenDefs:
196                     case SVGTokenG:
197                     {
198                         /// new node for Defs/G
199                         mpTarget = new SvgGNode(aSVGToken, maDocument, mpTarget);
200                         mpTarget->parseAttributes(xAttribs);
201                         break;
202                     }
203                     case SVGTokenSvg:
204                     {
205                         /// new node for Svg
206                         mpTarget = new SvgSvgNode(maDocument, mpTarget);
207                         mpTarget->parseAttributes(xAttribs);
208                         break;
209                     }
210                     case SVGTokenUse:
211                     {
212                         /// new node for Use
213                         mpTarget = new SvgUseNode(maDocument, mpTarget);
214                         mpTarget->parseAttributes(xAttribs);
215                         break;
216                     }
217 
218                     /// shape elements
219                     case SVGTokenCircle:
220                     {
221                         /// new node for Circle
222                         mpTarget = new SvgCircleNode(maDocument, mpTarget);
223                         mpTarget->parseAttributes(xAttribs);
224                         break;
225                     }
226                     case SVGTokenEllipse:
227                     {
228                         /// new node for Ellipse
229                         mpTarget = new SvgEllipseNode(maDocument, mpTarget);
230                         mpTarget->parseAttributes(xAttribs);
231                         break;
232                     }
233                     case SVGTokenLine:
234                     {
235                         /// new node for Line
236                         mpTarget = new SvgLineNode(maDocument, mpTarget);
237                         mpTarget->parseAttributes(xAttribs);
238                         break;
239                     }
240                     case SVGTokenPath:
241                     {
242                         /// new node for Path
243                         mpTarget = new SvgPathNode(maDocument, mpTarget);
244                         mpTarget->parseAttributes(xAttribs);
245                         break;
246                     }
247                     case SVGTokenPolygon:
248                     {
249                         /// new node for Polygon
250                         mpTarget = new SvgPolyNode(maDocument, mpTarget, false);
251                         mpTarget->parseAttributes(xAttribs);
252                         break;
253                     }
254                     case SVGTokenPolyline:
255                     {
256                         /// new node for Polyline
257                         mpTarget = new SvgPolyNode(maDocument, mpTarget, true);
258                         mpTarget->parseAttributes(xAttribs);
259                         break;
260                     }
261                     case SVGTokenRect:
262                     {
263                         /// new node for Rect
264                         mpTarget = new SvgRectNode(maDocument, mpTarget);
265                         mpTarget->parseAttributes(xAttribs);
266                         break;
267                     }
268                     case SVGTokenImage:
269                     {
270                         /// new node for Image
271                         mpTarget = new SvgImageNode(maDocument, mpTarget);
272                         mpTarget->parseAttributes(xAttribs);
273                         break;
274                     }
275 
276                     /// title and description
277                     case SVGTokenTitle:
278                     case SVGTokenDesc:
279                     {
280                         /// new node for Title and/or Desc
281                         mpTarget = new SvgTitleDescNode(aSVGToken, maDocument, mpTarget);
282                         break;
283                     }
284 
285                     /// gradients
286                     case SVGTokenLinearGradient:
287                     case SVGTokenRadialGradient:
288                     {
289                         mpTarget = new SvgGradientNode(aSVGToken, maDocument, mpTarget);
290                         mpTarget->parseAttributes(xAttribs);
291                         break;
292                     }
293 
294                     /// gradient stops
295                     case SVGTokenStop:
296                     {
297                         mpTarget = new SvgGradientStopNode(maDocument, mpTarget);
298                         mpTarget->parseAttributes(xAttribs);
299                         break;
300                     }
301 
302                     /// text
303                     case SVGTokenText:
304                     {
305                         mpTarget = new SvgTextNode(maDocument, mpTarget);
306                         mpTarget->parseAttributes(xAttribs);
307                         break;
308                     }
309                     case SVGTokenTspan:
310                     {
311                         mpTarget = new SvgTspanNode(maDocument, mpTarget);
312                         mpTarget->parseAttributes(xAttribs);
313                         break;
314                     }
315                     case SVGTokenTref:
316                     {
317                         mpTarget = new SvgTrefNode(maDocument, mpTarget);
318                         mpTarget->parseAttributes(xAttribs);
319                         break;
320                     }
321                     case SVGTokenTextPath:
322                     {
323                         mpTarget = new SvgTextPathNode(maDocument, mpTarget);
324                         mpTarget->parseAttributes(xAttribs);
325                         break;
326                     }
327 
328                     /// styles (as stylesheets)
329                     case SVGTokenStyle:
330                     {
331                         SvgStyleNode* pNew = new SvgStyleNode(maDocument, mpTarget);
332                         mpTarget = pNew;
333                         mpTarget->parseAttributes(xAttribs);
334 
335                         if(pNew->isTextCss())
336                         {
337                             maCssContents.push_back(rtl::OUString());
338                         }
339                         break;
340                     }
341 
342                     /// structural elements clip-path and mask. Content gets scanned, but
343                     /// will not be decomposed (see SvgNode::decomposeSvgNode and bReferenced)
344                     case SVGTokenClipPathNode:
345                     {
346                         /// new node for ClipPath
347                         mpTarget = new SvgClipPathNode(maDocument, mpTarget);
348                         mpTarget->parseAttributes(xAttribs);
349                         break;
350                     }
351                     case SVGTokenMask:
352                     {
353                         /// new node for Mask
354                         mpTarget = new SvgMaskNode(maDocument, mpTarget);
355                         mpTarget->parseAttributes(xAttribs);
356                         break;
357                     }
358 
359                     /// structural element marker
360                     case SVGTokenMarker:
361                     {
362                         /// new node for marker
363                         mpTarget = new SvgMarkerNode(maDocument, mpTarget);
364                         mpTarget->parseAttributes(xAttribs);
365                         break;
366                     }
367 
368                     /// structural element pattern
369                     case SVGTokenPattern:
370                     {
371                         /// new node for pattern
372                         mpTarget = new SvgPatternNode(maDocument, mpTarget);
373                         mpTarget->parseAttributes(xAttribs);
374                         break;
375                     }
376 
377                     default:
378                     {
379                         /// invalid token, ignore
380 #ifdef DBG_UTIL
381                         myAssert(
382                             rtl::OUString::createFromAscii("Unknown Base SvgToken <") +
383                             aName +
384                             rtl::OUString::createFromAscii("> (!)"));
385 #endif
386                         break;
387                     }
388                 }
389             }
390         }
391 
392         void SvgDocHdl::endElement( const ::rtl::OUString& aName ) throw (xml::sax::SAXException, uno::RuntimeException)
393         {
394             if(aName.getLength())
395             {
396                 const SVGToken aSVGToken(StrToSVGToken(aName));
397                 SvgNode* pWhitespaceCheck(SVGTokenText == aSVGToken ? mpTarget : 0);
398                 SvgStyleNode* pCssStyle(SVGTokenStyle == aSVGToken ? static_cast< SvgStyleNode* >(mpTarget) : 0);
399                 SvgTitleDescNode* pSvgTitleDescNode(SVGTokenTitle == aSVGToken || SVGTokenDesc == aSVGToken ? static_cast< SvgTitleDescNode* >(mpTarget) : 0);
400 
401                 switch(aSVGToken)
402                 {
403                     /// valid tokens for which a new one was created
404 
405                     /// structural elements
406                     case SVGTokenDefs:
407                     case SVGTokenG:
408                     case SVGTokenSvg:
409                     case SVGTokenSymbol:
410                     case SVGTokenUse:
411 
412                     /// shape elements
413                     case SVGTokenCircle:
414                     case SVGTokenEllipse:
415                     case SVGTokenLine:
416                     case SVGTokenPath:
417                     case SVGTokenPolygon:
418                     case SVGTokenPolyline:
419                     case SVGTokenRect:
420                     case SVGTokenImage:
421 
422                     /// title and description
423                     case SVGTokenTitle:
424                     case SVGTokenDesc:
425 
426                     /// gradients
427                     case SVGTokenLinearGradient:
428                     case SVGTokenRadialGradient:
429 
430                     /// gradient stops
431                     case SVGTokenStop:
432 
433                     /// text
434                     case SVGTokenText:
435                     case SVGTokenTspan:
436                     case SVGTokenTextPath:
437                     case SVGTokenTref:
438 
439                     /// styles (as stylesheets)
440                     case SVGTokenStyle:
441 
442                     /// structural elements clip-path and mask
443                     case SVGTokenClipPathNode:
444                     case SVGTokenMask:
445 
446                     /// structural element marker
447                     case SVGTokenMarker:
448 
449                     /// structural element pattern
450                     case SVGTokenPattern:
451 
452                     /// content handling after parsing
453                     {
454                         if(mpTarget)
455                         {
456                             if(!mpTarget->getParent())
457                             {
458                                 // last element closing, save this tree
459                                 maDocument.appendNode(mpTarget);
460                             }
461 
462                             mpTarget = const_cast< SvgNode* >(mpTarget->getParent());
463                         }
464                         else
465                         {
466                             OSL_ENSURE(false, "Closing token, but no context (!)");
467                         }
468                         break;
469                     }
470                     default:
471                     {
472                         /// invalid token, ignore
473                     }
474                 }
475 
476                 if(pSvgTitleDescNode && mpTarget)
477                 {
478                     const rtl::OUString aText(pSvgTitleDescNode->getText());
479 
480                     if(aText.getLength())
481                     {
482                         if(SVGTokenTitle == aSVGToken)
483                         {
484                             mpTarget->parseAttribute(getStrTitle(), aSVGToken, aText);
485                         }
486                         else // if(SVGTokenDesc == aSVGToken)
487                         {
488                             mpTarget->parseAttribute(getStrDesc(), aSVGToken, aText);
489                         }
490                     }
491                 }
492 
493                 if(pCssStyle && pCssStyle->isTextCss())
494                 {
495                     // css style parsing
496                     if(maCssContents.size())
497                     {
498                         // need to interpret css styles and remember them as StyleSheets
499                         pCssStyle->addCssStyleSheet(*(maCssContents.end() - 1));
500                         maCssContents.pop_back();
501                     }
502                     else
503                     {
504                         OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
505                     }
506                 }
507 
508                 if(pWhitespaceCheck)
509                 {
510                     // cleanup read strings
511                     whiteSpaceHandling(pWhitespaceCheck, 0);
512                 }
513             }
514         }
515 
516         void SvgDocHdl::characters( const ::rtl::OUString& aChars ) throw (xml::sax::SAXException, uno::RuntimeException)
517         {
518             const sal_uInt32 nLength(aChars.getLength());
519 
520             if(mpTarget && nLength)
521             {
522                 switch(mpTarget->getType())
523                 {
524                     case SVGTokenText:
525                     case SVGTokenTspan:
526                     case SVGTokenTextPath:
527                     {
528                         const SvgNodeVector& rChilds = mpTarget->getChildren();
529                         SvgCharacterNode* pTarget = 0;
530 
531                         if(rChilds.size())
532                         {
533                             pTarget = dynamic_cast< SvgCharacterNode* >(rChilds[rChilds.size() - 1]);
534                         }
535 
536                         if(pTarget)
537                         {
538                             // concatenate to current character span
539                             pTarget->concatenate(aChars);
540                         }
541                         else
542                         {
543                             // add character span as simplified tspan (no arguments)
544                             // as direct child of SvgTextNode/SvgTspanNode/SvgTextPathNode
545                             new SvgCharacterNode(maDocument, mpTarget, aChars);
546                         }
547                         break;
548                     }
549                     case SVGTokenStyle:
550                     {
551                         SvgStyleNode& rSvgStyleNode = static_cast< SvgStyleNode& >(*mpTarget);
552 
553                         if(rSvgStyleNode.isTextCss())
554                         {
555                             // collect characters for css style
556                             if(maCssContents.size())
557                             {
558                                 const ::rtl::OUString aTrimmedChars(aChars.trim());
559 
560                                 if(aTrimmedChars.getLength())
561                                 {
562                                     std::vector< rtl::OUString >::iterator aString(maCssContents.end() - 1);
563                                     (*aString) += aTrimmedChars;
564                                 }
565                             }
566                             else
567                             {
568                                 OSL_ENSURE(false, "Closing CssStyle, but no collector string on stack (!)");
569                             }
570                         }
571                         break;
572                     }
573                     case SVGTokenTitle:
574                     case SVGTokenDesc:
575                     {
576                         SvgTitleDescNode& rSvgTitleDescNode = static_cast< SvgTitleDescNode& >(*mpTarget);
577 
578                         // add text directly to SvgTitleDescNode
579                         rSvgTitleDescNode.concatenate(aChars);
580                         break;
581                     }
582                     default:
583                     {
584                         // characters not used by a known node
585                         break;
586                     }
587                 }
588             }
589         }
590 
591         void SvgDocHdl::ignorableWhitespace(const ::rtl::OUString& /*aWhitespaces*/) throw (xml::sax::SAXException, uno::RuntimeException)
592         {
593         }
594 
595         void SvgDocHdl::processingInstruction(const ::rtl::OUString& /*aTarget*/, const ::rtl::OUString& /*aData*/) throw (xml::sax::SAXException, uno::RuntimeException)
596         {
597         }
598 
599         void SvgDocHdl::setDocumentLocator(const uno::Reference< xml::sax::XLocator >& /*xLocator*/) throw (xml::sax::SAXException, uno::RuntimeException)
600         {
601         }
602     } // end of namespace svgreader
603 } // end of namespace svgio
604 
605 //////////////////////////////////////////////////////////////////////////////
606 // eof
607