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;
100 }
102 /**
103 *
104 */
105 int SvgParser::skipwhite(int p)
106 {
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 (!isWhitespace(get(p)))
154 break;
155 else
156 p++;
157 }
158 lastPosition = p;
159 return p;
160 }
162 /**
163 * get a word from the buffer
164 */
165 int SvgParser::getWord(int p, DOMString &result)
166 {
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;
191 }
194 # if 0
195 /**
196 * get a word from the buffer
197 */
198 int SvgParser::getNumber(int p0, double &result)
199 {
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;
245 }
246 #endif
249 /**
250 * get a word from the buffer
251 */
252 int SvgParser::getNumber(int p0, double &result)
253 {
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;
278 }
281 bool SvgParser::parseTransform(const DOMString &str)
282 {
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;
590 }
593 /**
594 *
595 */
596 bool SvgParser::parseElement(ElementImpl *parent, ElementImpl *sourceElem)
597 {
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;
722 }
725 /**
726 *
727 */
728 Document *SvgParser::parse(const Document *sourceDoc)
729 {
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;
749 }
754 } //namespace svg
755 } //namespace dom
756 } //namespace w3c
757 } //namespace org
759 /*#########################################################################
760 ## E N D O F F I L E
761 #########################################################################*/