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