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;
106 }
108 /**
109 *
110 */
111 int SvgReader::skipwhite(int p)
112 {
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;
166 }
168 /**
169 * get a word from the buffer
170 */
171 int SvgReader::getWord(int p, DOMString &result)
172 {
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;
197 }
200 # if 0
201 /**
202 * get a word from the buffer
203 */
204 int SvgReader::getNumber(int p0, double &result)
205 {
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;
251 }
252 #endif
255 /**
256 * get a word from the buffer
257 */
258 int SvgReader::getNumber(int p0, double &result)
259 {
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;
284 }
287 bool SvgReader::parseTransform(const DOMString &str)
288 {
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;
596 }
599 /**
600 *
601 */
602 bool SvgReader::parseElement(SVGElementImplPtr parent,
603 ElementImplPtr sourceElem)
604 {
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;
729 }
732 /**
733 *
734 */
735 SVGDocumentPtr SvgReader::parse(const DocumentPtr src)
736 {
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;
754 }
758 /**
759 *
760 */
761 SVGDocumentPtr SvgReader::parse(const DOMString &buf)
762 {
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;
773 }
777 /**
778 *
779 */
780 SVGDocumentPtr SvgReader::parseFile(const DOMString &fileName)
781 {
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;
792 }
797 } //namespace svg
798 } //namespace dom
799 } //namespace w3c
800 } //namespace org
802 /*#########################################################################
803 ## E N D O F F I L E
804 #########################################################################*/