Code

fix 1432089: stroke is not drawn not only when it's not set but also when it's too...
[inkscape.git] / src / dom / svgparser.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 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  */
31 #include "svgparser.h"
32 #include "cssparser.h"
33 #include "charclass.h"
35 #include <stdarg.h>
37 #define SVG_NAMESPACE "http://www.w3.org/2000/svg"
39 namespace org
40 {
41 namespace w3c
42 {
43 namespace dom
44 {
45 namespace svg
46 {
49 //#########################################################################
50 //# M E S S A G E S
51 //#########################################################################
54 /**
55  *
56  */
57 void SvgParser::error(char *fmt, ...)
58 {
59     va_list args;
60     fprintf(stderr, "SvgParser:error:");
61     va_start(args, fmt);
62     vfprintf(stderr, fmt, args);
63     va_end(args) ;
64     fprintf(stderr, "\n");
65 }
69 //#########################################################################
70 //# P A R S I N G
71 //#########################################################################
73 /**
74  *  Get the character at the position and record the fact
75  */
76 XMLCh SvgParser::get(int p)
77 {
78     if (p >= parselen)
79         return 0;
80     XMLCh ch = parsebuf[p];
81     //printf("%c", ch);
82     lastPosition = p;
83     return ch;
84 }
88 /**
89  *  Test if the given substring exists at the given position
90  *  in parsebuf.  Use get() in case of out-of-bounds
91  */
92 bool SvgParser::match(int pos, char *str)
93 {
94     while (*str)
95        {
96        if (get(pos++) != *str++)
97            return false;
98        }
99    return true;
102 /**
103  *
104  */
105 int SvgParser::skipwhite(int p)
107   while (p < parselen)
108     {
109     //# XML COMMENT
110     if (match(p, "<!--"))
111         {
112         p+=4;
113         bool done=false;
114         while (p<parselen)
115             {
116             if (match(p, "-->"))
117                 {
118                 p+=3;
119                 done=true;
120                 break;
121                 }
122             p++;
123             }
124         lastPosition = p;
125         if (!done)
126             {
127             error("unterminated <!-- .. --> comment");
128             return -1;
129             }
130         }
131     //# C comment
132     else if (match(p, "/*"))
133         {
134         p+=2;
135         bool done=false;
136         while (p<parselen)
137             {
138             if (match(p, "*/"))
139                 {
140                 p+=2;
141                 done=true;
142                 break;
143                 }
144             p++;
145             }
146         lastPosition = p;
147         if (!done)
148             {
149             error("unterminated /* .. */ comment");
150             return -1;
151             }
152         }
153     else if (!isspace(get(p)))
154         break;
155     else
156         p++;
157     }
158   lastPosition = p;
159   return p;
162 /**
163  * get a word from the buffer
164  */
165 int SvgParser::getWord(int p, DOMString &result)
167     XMLCh ch = get(p);
168     if (!isLetter(ch))
169         return p;
170     DOMString str;
171     str.push_back(ch);
172     p++;
174     while (p < parselen)
175         {
176         ch = get(p);
177         if (isLetterOrDigit(ch) || ch=='-' || ch=='_')
178             {
179             str.push_back(ch);
180             p++;
181             }
182         else if (ch == '\\')
183             {
184             p+=2;
185             }
186         else
187             break;
188         }
189     result = str;
190     return p;
194 # if 0
195 /**
196  * get a word from the buffer
197  */
198 int SvgParser::getNumber(int p0, double &result)
200     int p=p0;
202     DOMString str;
204     //allow sign
205     if (get(p) == '-')
206         {
207         p++;
208         }
210     while (p < parselen)
211         {
212         XMLCh ch = get(p);
213         if (ch<'0' || ch>'9')
214             break;
215         str.push_back(ch);
216         p++;
217         }
218     if (get(p) == '.' && get(p+1)>='0' && get(p+1)<='9')
219         {
220         p++;
221         str.push_back('.');
222         while (p < parselen)
223             {
224             XMLCh ch = get(p);
225             if (ch<'0' || ch>'9')
226                 break;
227             str.push_back(ch);
228             p++;
229             }
230         }
231     if (p>p0)
232         {
233         char *start = (char *)str.c_str();
234         char *end   = NULL;
235         double val = strtod(start, &end);
236         if (end > start)
237             {
238             result = val;
239             return p;
240             }
241         }
243     //not a number
244     return p0;
246 #endif
249 /**
250  * get a word from the buffer
251  */
252 int SvgParser::getNumber(int p0, double &result)
254     int p=p0;
256     char buf[64];
258     int i;
259     for (i=0 ; i<63 && p<parselen ; i++)
260         {
261         buf[i] = (char) get(p++);
262         }
263     buf[i] = '\0';
265     char *start = buf;
266     char *end   = NULL;
267     double val = strtod(start, &end);
268     if (end > start)
269         {
270         result = val;
271         int count = (int)(end - start);
272         p = p0 + count;
273         return p;
274         }
276     //not a number
277     return p0;
281 bool SvgParser::parseTransform(const DOMString &str)
283     parsebuf = str;
284     parselen = str.size();
286     //printf("transform:%s\n", str.c_str());
288     SVGTransformList transformList;
290     int p = 0;
292     while (p < parselen)
293         {
294         p = skipwhite(p);
295         DOMString name;
296         int p2 = getWord(p, name);
297         if (p2<0)
298             return false;
299         if (p2<=p)
300             {
301             error("transform: need transform name");
302             //return false;
303             break;
304             }
305         p = p2;
306         //printf("transform name:%s\n", name.c_str());
308         //######### MATRIX
309         if (name == "matrix")
310             {
311             p = skipwhite(p);
312             if (get(p++) != '(')
313                 {
314                 error("matrix transform needs opening '('");
315                 return false;
316                 }
317             int nrVals = 0;
318             double vals[6];
319             bool seenBrace = false;
320             while (p < parselen && nrVals < 6)
321                 {
322                 p = skipwhite(p);
323                 double val;
324                 p2 = getNumber(p, val);
325                 if (p2<0)
326                     return false;
327                 if (p2<=p)
328                     {
329                     error("matrix() expected number");
330                     return false;
331                     }
332                 vals[nrVals++] = val;
333                 p = skipwhite(p2);
334                 XMLCh ch = get(p);
335                 if (ch == ',')
336                     {
337                     p++;
338                     p = skipwhite(p);
339                     ch = get(p);
340                     }
341                 if (ch == ')')
342                     {
343                     seenBrace = true;
344                     p++;
345                     break;
346                     }
347                 }
348             if (!seenBrace)
349                 {
350                 error("matrix() needs closing brace");
351                 return false;
352                 }
353             if (nrVals != 6)
354                 {
355                 error("matrix() requires exactly 6 arguments");
356                 return false;
357                 }
358             //We got our arguments
359             //printf("translate: %f %f %f %f %f %f\n",
360             //      vals[0], vals[1], vals[2], vals[3], vals[4], vals[5]);
361             SVGMatrix matrix(vals[0], vals[1], vals[2],
362                              vals[3], vals[4], vals[5]);
363             SVGTransform transform;
364             transform.setMatrix(matrix);
365             transformList.appendItem(transform);
366             }
368         //######### TRANSLATE
369         else if (name == "translate")
370             {
371             p = skipwhite(p);
372             if (get(p++) != '(')
373                 {
374                 error("matrix transform needs opening '('");
375                 return false;
376                 }
377             p = skipwhite(p);
378             double x;
379             p2 = getNumber(p, x);
380             if (p2<0)
381                 return false;
382             if (p2<=p)
383                 {
384                 error("translate() expected 'x' value");
385                 return false;
386                 }
387             p = skipwhite(p2);
388             if (get(p) == ',')
389                 {
390                 p++;
391                 p = skipwhite(p);
392                 }
393             double y;
394             p2 = getNumber(p, y);
395             if (p2<0)
396                 return false;
397             if (p2<=p) //no y specified. use default
398                 y = 0.0;
399             p = skipwhite(p2);
400             if (get(p++) != ')')
401                 {
402                 error("translate() needs closing ')'");
403                 return false;
404                 }
405             //printf("translate: %f %f\n", x, y);
406             SVGTransform transform;
407             transform.setTranslate(x, y);
408             transformList.appendItem(transform);
409             }
411         //######### SCALE
412         else if (name == "scale")
413             {
414             p = skipwhite(p);
415             if (get(p++) != '(')
416                 {
417                 error("scale transform needs opening '('");
418                 return false;
419                 }
420             p = skipwhite(p);
421             double x;
422             p2 = getNumber(p, x);
423             if (p2<0)
424                 return false;
425             if (p2<=p)
426                 {
427                 error("scale() expected 'x' value");
428                 return false;
429                 }
430             p = skipwhite(p2);
431             if (get(p) == ',')
432                 {
433                 p++;
434                 p = skipwhite(p);
435                 }
436             double y;
437             p2 = getNumber(p, y);
438             if (p2<0)
439                 return false;
440             if (p2<=p) //no y specified. use default
441                 y = x; // y is same as x.  uniform scaling
442             p = skipwhite(p2);
443             if (get(p++) != ')')
444                 {
445                 error("scale() needs closing ')'");
446                 return false;
447                 }
448             //printf("scale: %f %f\n", x, y);
449             SVGTransform transform;
450             transform.setScale(x, y);
451             transformList.appendItem(transform);
452             }
454         //######### ROTATE
455         else if (name == "rotate")
456             {
457             p = skipwhite(p);
458             if (get(p++) != '(')
459                 {
460                 error("rotate transform needs opening '('");
461                 return false;
462                 }
463             p = skipwhite(p);
464             double angle;
465             p2 = getNumber(p, angle);
466             if (p2<0)
467                 return false;
468             if (p2<=p)
469                 {
470                 error("rotate() expected 'angle' value");
471                 return false;
472                 }
473             p = skipwhite(p2);
474             if (get(p) == ',')
475                 {
476                 p++;
477                 p = skipwhite(p);
478                 }
479             double cx = 0.0;
480             double cy = 0.0;
481             p2 = getNumber(p, cx);
482             if (p2>p)
483                 {
484                 p = skipwhite(p2);
485                 if (get(p) == ',')
486                     {
487                     p++;
488                     p = skipwhite(p);
489                     }
490                 p2 = getNumber(p, cy);
491                 if (p2<0)
492                     return false;
493                 if (p2<=p)
494                     {
495                     error("rotate() arguments should be either rotate(angle) or rotate(angle, cx, cy)");
496                     return false;
497                     }
498                 p = skipwhite(p2);
499                 }
500             if (get(p++) != ')')
501                 {
502                 error("rotate() needs closing ')'");
503                 return false;
504                 }
505             //printf("rotate: %f %f %f\n", angle, cx, cy);
506             SVGTransform transform;
507             transform.setRotate(angle, cx, cy);
508             transformList.appendItem(transform);
509             }
511         //######### SKEWX
512         else if (name == "skewX")
513             {
514             p = skipwhite(p);
515             if (get(p++) != '(')
516                 {
517                 error("skewX transform needs opening '('");
518                 return false;
519                 }
520             p = skipwhite(p);
521             double x;
522             p2 = getNumber(p, x);
523             if (p2<0)
524                 return false;
525             if (p2<=p)
526                 {
527                 error("skewX() expected 'x' value");
528                 return false;
529                 }
530             p = skipwhite(p2);
531             if (get(p++) != ')')
532                 {
533                 error("skewX() needs closing ')'");
534                 return false;
535                 }
536             //printf("skewX: %f\n", x);
537             SVGTransform transform;
538             transform.setSkewX(x);
539             transformList.appendItem(transform);
540             }
542         //######### SKEWY
543         else if (name == "skewY")
544             {
545             p = skipwhite(p);
546             if (get(p++) != '(')
547                 {
548                 error("skewY transform needs opening '('");
549                 return false;
550                 }
551             p = skipwhite(p);
552             double y;
553             p2 = getNumber(p, y);
554             if (p2<0)
555                 return false;
556             if (p2<=p)
557                 {
558                 error("skewY() expected 'y' value");
559                 return false;
560                 }
561             p = skipwhite(p2);
562             if (get(p++) != ')')
563                 {
564                 error("skewY() needs closing ')'");
565                 return false;
566                 }
567             //printf("skewY: %f\n", y);
568             SVGTransform transform;
569             transform.setSkewY(y);
570             transformList.appendItem(transform);
571             }
573         //### NONE OF THE ABOVE
574         else
575             {
576             error("unknown transform type:'%s'", name.c_str());
577             }
579         p = skipwhite(p);
580         XMLCh ch = get(p);
581         if (ch == ',')
582             {
583             p++;
584             p = skipwhite(p);
585             }
587         }//WHILE p<parselen
589     return true;
593 /**
594  *
595  */
596 bool SvgParser::parseElement(ElementImpl *parent, ElementImpl *sourceElem)
598     if (!parent || !sourceElem)
599         {
600         error("NULL source element");
601         return false;
602         }
604     DOMString namespaceURI = sourceElem->getNamespaceURI();
605     //printf("namespaceURI:%s\n", namespaceURI.c_str());
606     DOMString tagName      = sourceElem->getTagName();
607     printf("tag name:%s\n", tagName.c_str());
609     ElementImpl *newElement = NULL;
610     if (namespaceURI != SVG_NAMESPACE)
611         {
612         newElement = new SVGSVGElementImpl();
613         newElement->assign(*sourceElem);
614         parent->appendChild(newElement);
615         }
616     else //## SVG!!
617         {
619         //####################################################
620         //## ATTRIBUTES
621         //####################################################
622         DOMString style = sourceElem->getAttribute("style");
623         if (style.size() > 0)
624             {
625             css::CssParser parser;
626             style.insert(0, "{");
627             style.append("}");
628             //printf("CSS:%s\n", style.c_str());
629             if (!parser.parse(style))
630                 {
631                 error("parsing style attribute");
632                 }
633             else
634                 {
635                 //printf("##parsed!\n");
636                 }
637             }
639         DOMString transform = sourceElem->getAttribute("transform");
640         if (transform.size() > 0)
641             {
642             if (!parseTransform(transform))
643                 {
644                 error("parsing transform attribute");
645                 }
646             else
647                 {
648                 //printf("##parsed!\n");
649                 }
650             }
652         //####################################################
653         //## ELEMENT - SPECIFIC
654         //####################################################
655         if (tagName == "svg")
656             {
657             newElement = new SVGSVGElementImpl();
658             newElement->assign(*sourceElem);
659             parent->appendChild(newElement);
660             }
661         else if (tagName == "title")
662             {
663             newElement = new SVGTitleElementImpl();
664             newElement->assign(*sourceElem);
665             parent->appendChild(newElement);
666             }
667         else if (tagName == "desc")
668             {
669             newElement = new SVGDescElementImpl();
670             newElement->assign(*sourceElem);
671             parent->appendChild(newElement);
672             }
673         else if (tagName == "defs")
674             {
675             newElement = new SVGDefsElementImpl();
676             newElement->assign(*sourceElem);
677             parent->appendChild(newElement);
678             }
679         else if (tagName == "style")
680             {
681             newElement = new SVGStyleElementImpl();
682             newElement->assign(*sourceElem);
683             parent->appendChild(newElement);
684             }
685         else if (tagName == "g")
686             {
687             newElement = new SVGGElementImpl();
688             newElement->assign(*sourceElem);
689             parent->appendChild(newElement);
690             }
691         else if (tagName == "path")
692             {
693             newElement = new SVGPathElementImpl();
694             newElement->assign(*sourceElem);
695             parent->appendChild(newElement);
696             }
697         }
699     NodeList children = sourceElem->getChildNodes();
700     int nodeCount = children.getLength();
701     for (int i=0 ; i<nodeCount ; i++)
702         {
703         Node *child = children.item(i);
704         int typ = child->getNodeType();
705         if (typ == Node::TEXT_NODE)
706             {
707             Node *newNode = doc->createTextNode(child->getNodeValue());
708             parent->appendChild(newNode);
709             }
710         else if (typ == Node::CDATA_SECTION_NODE)
711             {
712             Node *newNode = doc->createCDATASection(child->getNodeValue());
713             parent->appendChild(newNode);
714             }
715         else if (newElement && typ == Node::ELEMENT_NODE)
716             {
717             ElementImpl *childElement = dynamic_cast<ElementImpl *>(child);
718             parseElement(newElement, childElement);
719             }
720         }
721     return true;
725 /**
726  *
727  */
728 Document *SvgParser::parse(const Document *sourceDoc)
730     if (!sourceDoc)
731         {
732         error("NULL source document");
733         return NULL;
734         }
736     Document *src = (Document *)sourceDoc;
737     DOMImplementationImpl impl;
738     doc = new SVGDocumentImpl(&impl, SVG_NAMESPACE, "svg" , NULL);
740     ElementImpl *destElem = dynamic_cast<ElementImpl *>(doc->getDocumentElement());
741     ElementImpl *srcElem  = dynamic_cast<ElementImpl *>(src->getDocumentElement());
742     if (!parseElement(destElem, srcElem))
743         {
744         delete doc;
745         return NULL;
746         }
748     return doc;
754 }  //namespace svg
755 }  //namespace dom
756 }  //namespace w3c
757 }  //namespace org
759 /*#########################################################################
760 ## E N D    O F    F I L E
761 #########################################################################*/