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