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