Code

Finish changing "Parser" to "Reader"
[inkscape.git] / src / dom / svgreader.cpp
1 /**
2  * Phoebe DOM Implementation.
3  *
4  * This is a C++ approximation of the W3C DOM model, which follows
5  * fairly closely the specifications in the various .idl files, copies of
6  * which are provided for reference.  Most important is this one:
7  *
8  * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html
9  *
10  * Authors:
11  *   Bob Jamison
12  *
13  * Copyright (C) 2005-2008 Bob Jamison
14  *
15  *  This library is free software; you can redistribute it and/or
16  *  modify it under the terms of the GNU Lesser General Public
17  *  License as published by the Free Software Foundation; either
18  *  version 2.1 of the License, or (at your option) any later version.
19  *
20  *  This library is distributed in the hope that it will be useful,
21  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23  *  Lesser General Public License for more details.
24  *
25  *  You should have received a copy of the GNU Lesser General Public
26  *  License along with this library; if not, write to the Free Software
27  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
28  *  
29  * =======================================================================
30  * NOTES
31  * 
32  *      
33  */
36 #include "svgreader.h"
37 #include "dom/cssreader.h"
38 #include "dom/ucd.h"
39 #include "xmlreader.h"
41 #include <stdarg.h>
43 #define SVG_NAMESPACE "http://www.w3.org/2000/svg"
45 namespace org
46 {
47 namespace w3c
48 {
49 namespace dom
50 {
51 namespace svg
52 {
55 //#########################################################################
56 //# M E S S A G E S
57 //#########################################################################
60 /**
61  *
62  */
63 void SvgReader::error(char const *fmt, ...)
64 {
65     va_list args;
66     fprintf(stderr, "SvgReader:error:");
67     va_start(args, fmt);
68     vfprintf(stderr, fmt, args);
69     va_end(args) ;
70     fprintf(stderr, "\n");
71 }
75 //#########################################################################
76 //# P A R S I N G
77 //#########################################################################
79 /**
80  *  Get the character at the position and record the fact
81  */
82 XMLCh SvgReader::get(int p)
83 {
84     if (p >= parselen)
85         return 0;
86     XMLCh ch = parsebuf[p];
87     //printf("%c", ch);
88     lastPosition = p;
89     return ch;
90 }
94 /**
95  *  Test if the given substring exists at the given position
96  *  in parsebuf.  Use get() in case of out-of-bounds
97  */
98 bool SvgReader::match(int pos, char const *str)
99 {
100     while (*str)
101        {
102        if (get(pos++) != (XMLCh) *str++)
103            return false;
104        }
105    return true;
108 /**
109  *
110  */
111 int SvgReader::skipwhite(int p)
113   while (p < parselen)
114     {
115     //# XML COMMENT
116     if (match(p, "<!--"))
117         {
118         p+=4;
119         bool done=false;
120         while (p<parselen)
121             {
122             if (match(p, "-->"))
123                 {
124                 p+=3;
125                 done=true;
126                 break;
127                 }
128             p++;
129             }
130         lastPosition = p;
131         if (!done)
132             {
133             error("unterminated <!-- .. --> comment");
134             return -1;
135             }
136         }
137     //# C comment
138     else if (match(p, "/*"))
139         {
140         p+=2;
141         bool done=false;
142         while (p<parselen)
143             {
144             if (match(p, "*/"))
145                 {
146                 p+=2;
147                 done=true;
148                 break;
149                 }
150             p++;
151             }
152         lastPosition = p;
153         if (!done)
154             {
155             error("unterminated /* .. */ comment");
156             return -1;
157             }
158         }
159     else if (!uni_is_space(get(p)))
160         break;
161     else
162         p++;
163     }
164   lastPosition = p;
165   return p;
168 /**
169  * get a word from the buffer
170  */
171 int SvgReader::getWord(int p, DOMString &result)
173     XMLCh ch = get(p);
174     if (!uni_is_letter(ch))
175         return p;
176     DOMString str;
177     str.push_back(ch);
178     p++;
180     while (p < parselen)
181         {
182         ch = get(p);
183         if (uni_is_letter_or_digit(ch) || ch=='-' || ch=='_')
184             {
185             str.push_back(ch);
186             p++;
187             }
188         else if (ch == '\\')
189             {
190             p+=2;
191             }
192         else
193             break;
194         }
195     result = str;
196     return p;
200 # if 0
201 /**
202  * get a word from the buffer
203  */
204 int SvgReader::getNumber(int p0, double &result)
206     int p=p0;
208     DOMString str;
210     //allow sign
211     if (get(p) == '-')
212         {
213         p++;
214         }
216     while (p < parselen)
217         {
218         XMLCh ch = get(p);
219         if (ch<'0' || ch>'9')
220             break;
221         str.push_back(ch);
222         p++;
223         }
224     if (get(p) == '.' && get(p+1)>='0' && get(p+1)<='9')
225         {
226         p++;
227         str.push_back('.');
228         while (p < parselen)
229             {
230             XMLCh ch = get(p);
231             if (ch<'0' || ch>'9')
232                 break;
233             str.push_back(ch);
234             p++;
235             }
236         }
237     if (p>p0)
238         {
239         char *start = (char *)str.c_str();
240         char *end   = NULL;
241         double val = strtod(start, &end);
242         if (end > start)
243             {
244             result = val;
245             return p;
246             }
247         }
249     //not a number
250     return p0;
252 #endif
255 /**
256  * get a word from the buffer
257  */
258 int SvgReader::getNumber(int p0, double &result)
260     int p=p0;
262     char buf[64];
264     int i;
265     for (i=0 ; i<63 && p<parselen ; i++)
266         {
267         buf[i] = (char) get(p++);
268         }
269     buf[i] = '\0';
271     char *start = buf;
272     char *end   = NULL;
273     double val = strtod(start, &end);
274     if (end > start)
275         {
276         result = val;
277         int count = (int)(end - start);
278         p = p0 + count;
279         return p;
280         }
282     //not a number
283     return p0;
287 bool SvgReader::parseTransform(const DOMString &str)
289     parsebuf = str;
290     parselen = str.size();
292     //printf("transform:%s\n", str.c_str());
294     SVGTransformList transformList;
296     int p = 0;
298     while (p < parselen)
299         {
300         p = skipwhite(p);
301         DOMString name;
302         int p2 = getWord(p, name);
303         if (p2<0)
304             return false;
305         if (p2<=p)
306             {
307             error("transform: need transform name");
308             //return false;
309             break;
310             }
311         p = p2;
312         //printf("transform name:%s\n", name.c_str());
314         //######### MATRIX
315         if (name == "matrix")
316             {
317             p = skipwhite(p);
318             if (get(p++) != '(')
319                 {
320                 error("matrix transform needs opening '('");
321                 return false;
322                 }
323             int nrVals = 0;
324             double vals[6];
325             bool seenBrace = false;
326             while (p < parselen && nrVals < 6)
327                 {
328                 p = skipwhite(p);
329                 double val = 0.0;
330                 p2 = getNumber(p, val);
331                 if (p2<0)
332                     return false;
333                 if (p2<=p)
334                     {
335                     error("matrix() expected number");
336                     return false;
337                     }
338                 vals[nrVals++] = val;
339                 p = skipwhite(p2);
340                 XMLCh ch = get(p);
341                 if (ch == ',')
342                     {
343                     p++;
344                     p = skipwhite(p);
345                     ch = get(p);
346                     }
347                 if (ch == ')')
348                     {
349                     seenBrace = true;
350                     p++;
351                     break;
352                     }
353                 }
354             if (!seenBrace)
355                 {
356                 error("matrix() needs closing brace");
357                 return false;
358                 }
359             if (nrVals != 6)
360                 {
361                 error("matrix() requires exactly 6 arguments");
362                 return false;
363                 }
364             //We got our arguments
365             //printf("translate: %f %f %f %f %f %f\n",
366             //      vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
367             SVGMatrix matrix(vals[0], vals[1], vals[2],
368                              vals[3], vals[4], vals[5]);
369             SVGTransform transform;
370             transform.setMatrix(matrix);
371             transformList.appendItem(transform);
372             }
374         //######### TRANSLATE
375         else if (name == "translate")
376             {
377             p = skipwhite(p);
378             if (get(p++) != '(')
379                 {
380                 error("matrix transform needs opening '('");
381                 return false;
382                 }
383             p = skipwhite(p);
384             double x = 0.0;
385             p2 = getNumber(p, x);
386             if (p2<0)
387                 return false;
388             if (p2<=p)
389                 {
390                 error("translate() expected 'x' value");
391                 return false;
392                 }
393             p = skipwhite(p2);
394             if (get(p) == ',')
395                 {
396                 p++;
397                 p = skipwhite(p);
398                 }
399             double y = 0.0;
400             p2 = getNumber(p, y);
401             if (p2<0)
402                 return false;
403             if (p2<=p) //no y specified. use default
404                 y = 0.0;
405             p = skipwhite(p2);
406             if (get(p++) != ')')
407                 {
408                 error("translate() needs closing ')'");
409                 return false;
410                 }
411             //printf("translate: %f %f\n", x, y);
412             SVGTransform transform;
413             transform.setTranslate(x, y);
414             transformList.appendItem(transform);
415             }
417         //######### SCALE
418         else if (name == "scale")
419             {
420             p = skipwhite(p);
421             if (get(p++) != '(')
422                 {
423                 error("scale transform needs opening '('");
424                 return false;
425                 }
426             p = skipwhite(p);
427             double x = 0.0;
428             p2 = getNumber(p, x);
429             if (p2<0)
430                 return false;
431             if (p2<=p)
432                 {
433                 error("scale() expected 'x' value");
434                 return false;
435                 }
436             p = skipwhite(p2);
437             if (get(p) == ',')
438                 {
439                 p++;
440                 p = skipwhite(p);
441                 }
442             double y = 0.0;
443             p2 = getNumber(p, y);
444             if (p2<0)
445                 return false;
446             if (p2<=p) //no y specified. use default
447                 y = x; // y is same as x.  uniform scaling
448             p = skipwhite(p2);
449             if (get(p++) != ')')
450                 {
451                 error("scale() needs closing ')'");
452                 return false;
453                 }
454             //printf("scale: %f %f\n", x, y);
455             SVGTransform transform;
456             transform.setScale(x, y);
457             transformList.appendItem(transform);
458             }
460         //######### ROTATE
461         else if (name == "rotate")
462             {
463             p = skipwhite(p);
464             if (get(p++) != '(')
465                 {
466                 error("rotate transform needs opening '('");
467                 return false;
468                 }
469             p = skipwhite(p);
470             double angle = 0.0;
471             p2 = getNumber(p, angle);
472             if (p2<0)
473                 return false;
474             if (p2<=p)
475                 {
476                 error("rotate() expected 'angle' value");
477                 return false;
478                 }
479             p = skipwhite(p2);
480             if (get(p) == ',')
481                 {
482                 p++;
483                 p = skipwhite(p);
484                 }
485             double cx = 0.0;
486             double cy = 0.0;
487             p2 = getNumber(p, cx);
488             if (p2>p)
489                 {
490                 p = skipwhite(p2);
491                 if (get(p) == ',')
492                     {
493                     p++;
494                     p = skipwhite(p);
495                     }
496                 p2 = getNumber(p, cy);
497                 if (p2<0)
498                     return false;
499                 if (p2<=p)
500                     {
501                     error("rotate() arguments should be either rotate(angle) or rotate(angle, cx, cy)");
502                     return false;
503                     }
504                 p = skipwhite(p2);
505                 }
506             if (get(p++) != ')')
507                 {
508                 error("rotate() needs closing ')'");
509                 return false;
510                 }
511             //printf("rotate: %f %f %f\n", angle, cx, cy);
512             SVGTransform transform;
513             transform.setRotate(angle, cx, cy);
514             transformList.appendItem(transform);
515             }
517         //######### SKEWX
518         else if (name == "skewX")
519             {
520             p = skipwhite(p);
521             if (get(p++) != '(')
522                 {
523                 error("skewX transform needs opening '('");
524                 return false;
525                 }
526             p = skipwhite(p);
527             double x = 0.0;
528             p2 = getNumber(p, x);
529             if (p2<0)
530                 return false;
531             if (p2<=p)
532                 {
533                 error("skewX() expected 'x' value");
534                 return false;
535                 }
536             p = skipwhite(p2);
537             if (get(p++) != ')')
538                 {
539                 error("skewX() needs closing ')'");
540                 return false;
541                 }
542             //printf("skewX: %f\n", x);
543             SVGTransform transform;
544             transform.setSkewX(x);
545             transformList.appendItem(transform);
546             }
548         //######### SKEWY
549         else if (name == "skewY")
550             {
551             p = skipwhite(p);
552             if (get(p++) != '(')
553                 {
554                 error("skewY transform needs opening '('");
555                 return false;
556                 }
557             p = skipwhite(p);
558             double y = 0.0;
559             p2 = getNumber(p, y);
560             if (p2<0)
561                 return false;
562             if (p2<=p)
563                 {
564                 error("skewY() expected 'y' value");
565                 return false;
566                 }
567             p = skipwhite(p2);
568             if (get(p++) != ')')
569                 {
570                 error("skewY() needs closing ')'");
571                 return false;
572                 }
573             //printf("skewY: %f\n", y);
574             SVGTransform transform;
575             transform.setSkewY(y);
576             transformList.appendItem(transform);
577             }
579         //### NONE OF THE ABOVE
580         else
581             {
582             error("unknown transform type:'%s'", name.c_str());
583             }
585         p = skipwhite(p);
586         XMLCh ch = get(p);
587         if (ch == ',')
588             {
589             p++;
590             p = skipwhite(p);
591             }
593         }//WHILE p<parselen
595     return true;
599 /**
600  *
601  */
602 bool SvgReader::parseElement(SVGElementImplPtr parent,
603                              ElementImplPtr sourceElem)
605     if (!parent || !sourceElem)
606         {
607         error("NULL source element");
608         return false;
609         }
611     DOMString namespaceURI = sourceElem->getNamespaceURI();
612     //printf("namespaceURI:%s\n", namespaceURI.c_str());
613     DOMString tagName      = sourceElem->getTagName();
614     printf("tag name:%s\n", tagName.c_str());
616     ElementImplPtr newElement = NULL;
617     if (namespaceURI != SVG_NAMESPACE)
618         {
619         newElement = new SVGSVGElementImpl();
620         newElement->assign(*sourceElem);
621         parent->appendChild(newElement);
622         }
623     else //## SVG!!
624         {
626         //####################################################
627         //## ATTRIBUTES
628         //####################################################
629         DOMString style = sourceElem->getAttribute("style");
630         if (style.size() > 0)
631             {
632             css::CssReader parser;
633             style.insert(0, "{");
634             style.append("}");
635             //printf("CSS:%s\n", style.c_str());
636             if (!parser.parse(style))
637                 {
638                 error("parsing style attribute");
639                 }
640             else
641                 {
642                 //printf("##parsed!\n");
643                 }
644             }
646         DOMString transform = sourceElem->getAttribute("transform");
647         if (transform.size() > 0)
648             {
649             if (!parseTransform(transform))
650                 {
651                 error("parsing transform attribute");
652                 }
653             else
654                 {
655                 //printf("##parsed!\n");
656                 }
657             }
659         //####################################################
660         //## ELEMENT - SPECIFIC
661         //####################################################
662         if (tagName == "svg")
663             {
664             newElement = new SVGSVGElementImpl();
665             newElement->assign(*sourceElem);
666             parent->appendChild(newElement);
667             }
668         else if (tagName == "title")
669             {
670             newElement = new SVGTitleElementImpl();
671             newElement->assign(*sourceElem);
672             parent->appendChild(newElement);
673             }
674         else if (tagName == "desc")
675             {
676             newElement = new SVGDescElementImpl();
677             newElement->assign(*sourceElem);
678             parent->appendChild(newElement);
679             }
680         else if (tagName == "defs")
681             {
682             newElement = new SVGDefsElementImpl();
683             newElement->assign(*sourceElem);
684             parent->appendChild(newElement);
685             }
686         else if (tagName == "style")
687             {
688             newElement = new SVGStyleElementImpl();
689             newElement->assign(*sourceElem);
690             parent->appendChild(newElement);
691             }
692         else if (tagName == "g")
693             {
694             newElement = new SVGGElementImpl();
695             newElement->assign(*sourceElem);
696             parent->appendChild(newElement);
697             }
698         else if (tagName == "path")
699             {
700             newElement = new SVGPathElementImpl();
701             newElement->assign(*sourceElem);
702             parent->appendChild(newElement);
703             }
704         }
706     NodeList children = sourceElem->getChildNodes();
707     int nodeCount = children.getLength();
708     for (int i=0 ; i<nodeCount ; i++)
709         {
710         NodePtr child = children.item(i);
711         int typ = child->getNodeType();
712         if (typ == Node::TEXT_NODE)
713             {
714             NodePtr newNode = doc->createTextNode(child->getNodeValue());
715             parent->appendChild(newNode);
716             }
717         else if (typ == Node::CDATA_SECTION_NODE)
718             {
719             NodePtr newNode = doc->createCDATASection(child->getNodeValue());
720             parent->appendChild(newNode);
721             }
722         else if (newElement.get() && typ == Node::ELEMENT_NODE)
723             {
724             //ElementImplPtr childElement = dynamic_cast<ElementImpl *>(child.get());
725             //parseElement(newElement, childElement);
726             }
727         }
728     return true;
732 /**
733  *
734  */
735 SVGDocumentPtr SvgReader::parse(const DocumentPtr src)
737     if (!src)
738         {
739         error("NULL source document");
740         return NULL;
741         }
743     DOMImplementationImpl impl;
744     doc = new SVGDocumentImpl(&impl, SVG_NAMESPACE, "svg" , NULL);
746     SVGElementImplPtr destElem = dynamic_cast<SVGElementImpl *>(doc->getRootElement().get());
747     ElementImplPtr    srcElem  = dynamic_cast<ElementImpl *>(src->getDocumentElement().get());
748     if (!parseElement(destElem, srcElem))
749         {
750         return NULL;
751         }
753     return doc;
758 /**
759  *
760  */
761 SVGDocumentPtr SvgReader::parse(const DOMString &buf)
763     /* remember, smartptrs are null-testable*/
764     SVGDocumentPtr svgdoc;
765     XmlReader parser;
766     DocumentPtr doc = parser.parse(buf);
767     if (!doc)
768         {
769         return svgdoc;
770         }
771     svgdoc = parse(doc);
772     return svgdoc;
777 /**
778  *
779  */
780 SVGDocumentPtr SvgReader::parseFile(const DOMString &fileName)
782     /* remember, smartptrs are null-testable*/
783     SVGDocumentPtr svgdoc;
784     XmlReader parser;
785     DocumentPtr doc = parser.parseFile(fileName);
786     if (!doc)
787         {
788         return svgdoc;
789         }
790     svgdoc = parse(doc);
791     return svgdoc;
797 }  //namespace svg
798 }  //namespace dom
799 }  //namespace w3c
800 }  //namespace org
802 /*#########################################################################
803 ## E N D    O F    F I L E
804 #########################################################################*/