BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / apps / icon-o-matic / import_export / svg / SVGParser.cpp
blobd640d7e157c5da7666617fdfbcb0ba8cf2f92779
1 /*
2 * Copyright 2006, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Stephan Aßmus <superstippi@gmx.de>
7 */
9 //----------------------------------------------------------------------------
10 // Anti-Grain Geometry - Version 2.2
11 // Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
13 // Permission to copy, use, modify, sell and distribute this software
14 // is granted provided this copyright notice appears in all copies.
15 // This software is provided "as is" without express or implied
16 // warranty, and with no claim as to its suitability for any purpose.
18 //----------------------------------------------------------------------------
19 // Contact: mcseem@antigrain.com
20 // mcseemagg@yahoo.com
21 // http://www.antigrain.com
22 //----------------------------------------------------------------------------
24 #include "SVGParser.h"
26 #include <stdio.h>
27 #include <string.h>
28 #include <ctype.h>
30 #include <expat.h>
32 #include "SVGGradients.h"
34 namespace agg {
35 namespace svg {
37 struct named_color
39 char name[22];
40 int8u r, g, b, a;
43 named_color colors[] =
45 { "aliceblue",240,248,255, 255 },
46 { "antiquewhite",250,235,215, 255 },
47 { "aqua",0,255,255, 255 },
48 { "aquamarine",127,255,212, 255 },
49 { "azure",240,255,255, 255 },
50 { "beige",245,245,220, 255 },
51 { "bisque",255,228,196, 255 },
52 { "black",0,0,0, 255 },
53 { "blanchedalmond",255,235,205, 255 },
54 { "blue",0,0,255, 255 },
55 { "blueviolet",138,43,226, 255 },
56 { "brown",165,42,42, 255 },
57 { "burlywood",222,184,135, 255 },
58 { "cadetblue",95,158,160, 255 },
59 { "chartreuse",127,255,0, 255 },
60 { "chocolate",210,105,30, 255 },
61 { "coral",255,127,80, 255 },
62 { "cornflowerblue",100,149,237, 255 },
63 { "cornsilk",255,248,220, 255 },
64 { "crimson",220,20,60, 255 },
65 { "cyan",0,255,255, 255 },
66 { "darkblue",0,0,139, 255 },
67 { "darkcyan",0,139,139, 255 },
68 { "darkgoldenrod",184,134,11, 255 },
69 { "darkgray",169,169,169, 255 },
70 { "darkgreen",0,100,0, 255 },
71 { "darkgrey",169,169,169, 255 },
72 { "darkkhaki",189,183,107, 255 },
73 { "darkmagenta",139,0,139, 255 },
74 { "darkolivegreen",85,107,47, 255 },
75 { "darkorange",255,140,0, 255 },
76 { "darkorchid",153,50,204, 255 },
77 { "darkred",139,0,0, 255 },
78 { "darksalmon",233,150,122, 255 },
79 { "darkseagreen",143,188,143, 255 },
80 { "darkslateblue",72,61,139, 255 },
81 { "darkslategray",47,79,79, 255 },
82 { "darkslategrey",47,79,79, 255 },
83 { "darkturquoise",0,206,209, 255 },
84 { "darkviolet",148,0,211, 255 },
85 { "deeppink",255,20,147, 255 },
86 { "deepskyblue",0,191,255, 255 },
87 { "dimgray",105,105,105, 255 },
88 { "dimgrey",105,105,105, 255 },
89 { "dodgerblue",30,144,255, 255 },
90 { "firebrick",178,34,34, 255 },
91 { "floralwhite",255,250,240, 255 },
92 { "forestgreen",34,139,34, 255 },
93 { "fuchsia",255,0,255, 255 },
94 { "gainsboro",220,220,220, 255 },
95 { "ghostwhite",248,248,255, 255 },
96 { "gold",255,215,0, 255 },
97 { "goldenrod",218,165,32, 255 },
98 { "gray",128,128,128, 255 },
99 { "green",0,128,0, 255 },
100 { "greenyellow",173,255,47, 255 },
101 { "grey",128,128,128, 255 },
102 { "honeydew",240,255,240, 255 },
103 { "hotpink",255,105,180, 255 },
104 { "indianred",205,92,92, 255 },
105 { "indigo",75,0,130, 255 },
106 { "ivory",255,255,240, 255 },
107 { "khaki",240,230,140, 255 },
108 { "lavender",230,230,250, 255 },
109 { "lavenderblush",255,240,245, 255 },
110 { "lawngreen",124,252,0, 255 },
111 { "lemonchiffon",255,250,205, 255 },
112 { "lightblue",173,216,230, 255 },
113 { "lightcoral",240,128,128, 255 },
114 { "lightcyan",224,255,255, 255 },
115 { "lightgoldenrodyellow",250,250,210, 255 },
116 { "lightgray",211,211,211, 255 },
117 { "lightgreen",144,238,144, 255 },
118 { "lightgrey",211,211,211, 255 },
119 { "lightpink",255,182,193, 255 },
120 { "lightsalmon",255,160,122, 255 },
121 { "lightseagreen",32,178,170, 255 },
122 { "lightskyblue",135,206,250, 255 },
123 { "lightslategray",119,136,153, 255 },
124 { "lightslategrey",119,136,153, 255 },
125 { "lightsteelblue",176,196,222, 255 },
126 { "lightyellow",255,255,224, 255 },
127 { "lime",0,255,0, 255 },
128 { "limegreen",50,205,50, 255 },
129 { "linen",250,240,230, 255 },
130 { "magenta",255,0,255, 255 },
131 { "maroon",128,0,0, 255 },
132 { "mediumaquamarine",102,205,170, 255 },
133 { "mediumblue",0,0,205, 255 },
134 { "mediumorchid",186,85,211, 255 },
135 { "mediumpurple",147,112,219, 255 },
136 { "mediumseagreen",60,179,113, 255 },
137 { "mediumslateblue",123,104,238, 255 },
138 { "mediumspringgreen",0,250,154, 255 },
139 { "mediumturquoise",72,209,204, 255 },
140 { "mediumvioletred",199,21,133, 255 },
141 { "midnightblue",25,25,112, 255 },
142 { "mintcream",245,255,250, 255 },
143 { "mistyrose",255,228,225, 255 },
144 { "moccasin",255,228,181, 255 },
145 { "navajowhite",255,222,173, 255 },
146 { "navy",0,0,128, 255 },
147 { "oldlace",253,245,230, 255 },
148 { "olive",128,128,0, 255 },
149 { "olivedrab",107,142,35, 255 },
150 { "orange",255,165,0, 255 },
151 { "orangered",255,69,0, 255 },
152 { "orchid",218,112,214, 255 },
153 { "palegoldenrod",238,232,170, 255 },
154 { "palegreen",152,251,152, 255 },
155 { "paleturquoise",175,238,238, 255 },
156 { "palevioletred",219,112,147, 255 },
157 { "papayawhip",255,239,213, 255 },
158 { "peachpuff",255,218,185, 255 },
159 { "peru",205,133,63, 255 },
160 { "pink",255,192,203, 255 },
161 { "plum",221,160,221, 255 },
162 { "powderblue",176,224,230, 255 },
163 { "purple",128,0,128, 255 },
164 { "red",255,0,0, 255 },
165 { "rosybrown",188,143,143, 255 },
166 { "royalblue",65,105,225, 255 },
167 { "saddlebrown",139,69,19, 255 },
168 { "salmon",250,128,114, 255 },
169 { "sandybrown",244,164,96, 255 },
170 { "seagreen",46,139,87, 255 },
171 { "seashell",255,245,238, 255 },
172 { "sienna",160,82,45, 255 },
173 { "silver",192,192,192, 255 },
174 { "skyblue",135,206,235, 255 },
175 { "slateblue",106,90,205, 255 },
176 { "slategray",112,128,144, 255 },
177 { "slategrey",112,128,144, 255 },
178 { "snow",255,250,250, 255 },
179 { "springgreen",0,255,127, 255 },
180 { "steelblue",70,130,180, 255 },
181 { "tan",210,180,140, 255 },
182 { "teal",0,128,128, 255 },
183 { "thistle",216,191,216, 255 },
184 { "tomato",255,99,71, 255 },
185 { "turquoise",64,224,208, 255 },
186 { "violet",238,130,238, 255 },
187 { "wheat",245,222,179, 255 },
188 { "white",255,255,255, 255 },
189 { "whitesmoke",245,245,245, 255 },
190 { "yellow",255,255,0, 255 },
191 { "yellowgreen",154,205,50, 255 },
192 { "zzzzzzzzzzz",0,0,0, 0 }
197 // cmp_color
199 cmp_color(const void* p1, const void* p2)
201 return strcmp(((named_color*)p1)->name, ((named_color*)p2)->name);
204 // parse_color
205 rgba8
206 parse_color(const char* str)
208 while(*str == ' ') ++str;
209 if (*str == '#') {
210 str++;
211 int32 length = strlen(str);
212 unsigned c = 0;
213 if (length == 3) {
214 // if there are only 3 byte, than it means that we
215 // need to expand the color (#f60 -> #ff6600)
216 // TODO: There must be an easier way...
217 char expanded[7];
218 expanded[0] = *str;
219 expanded[1] = *str++;
220 expanded[2] = *str;
221 expanded[3] = *str++;
222 expanded[4] = *str;
223 expanded[5] = *str++;
224 expanded[6] = 0;
225 sscanf(expanded, "%x", &c);
226 } else {
227 sscanf(str, "%x", &c);
229 return rgb8_packed(c);
230 } else {
231 named_color c;
232 unsigned len = strlen(str);
233 if(len > sizeof(c.name) - 1)
235 throw exception("parse_color: Invalid color name '%s'", str);
237 strcpy(c.name, str);
238 const void* p = bsearch(&c,
239 colors,
240 sizeof(colors) / sizeof(colors[0]),
241 sizeof(colors[0]),
242 cmp_color);
243 if(p == 0)
245 throw exception("parse_color: Invalid color name '%s'", str);
247 const named_color* pc = (const named_color*)p;
248 return rgba8(pc->r, pc->g, pc->b, pc->a);
252 // parse_double
253 double
254 parse_double(const char* str)
256 while(*str == ' ') ++str;
257 double value = atof(str);
258 // handle percent
259 int32 length = strlen(str);
260 if (str[length - 1] == '%')
261 value /= 100.0;
262 return value;
265 // parse_url
266 char*
267 parse_url(const char* str)
269 const char* begin = str;
270 while (*begin != '#')
271 begin++;
273 begin++;
274 const char* end = begin;
275 while (*end != ')')
276 end++;
278 end--;
280 int32 length = end - begin + 2;
281 char* result = new char[length];
282 memcpy(result, begin, length - 1);
283 result[length - 1] = 0;
285 return result;
289 // #pragma mark -
291 // constructor
292 Parser::Parser(DocumentBuilder& builder)
293 : fBuilder(builder),
294 fPathTokenizer(),
295 fBuffer(new char[buf_size]),
296 fTitle(new char[256]),
297 fTitleLength(0),
299 fTitleFlag(false),
300 fPathFlag(false),
302 fAttrName(new char[128]),
303 fAttrValue(new char[1024]),
304 fAttrNameLength(127),
305 fAttrValueLength(1023),
307 fTagsIgnored(false)
309 fTitle[0] = 0;
312 // destructor
313 Parser::~Parser()
315 delete[] fAttrValue;
316 delete[] fAttrName;
317 delete[] fBuffer;
318 delete[] fTitle;
321 // parse
322 void
323 Parser::parse(const char* pathToFile)
325 char msg[1024];
326 XML_Parser p = XML_ParserCreate(NULL);
327 if (p == 0) {
328 throw exception("Couldn't allocate memory for Parser");
331 XML_SetUserData(p, this);
332 XML_SetElementHandler(p, start_element, end_element);
333 XML_SetCharacterDataHandler(p, content);
335 FILE* fd = fopen(pathToFile, "r");
336 if (fd == 0) {
337 sprintf(msg, "Couldn't open file %s", pathToFile);
338 throw exception(msg);
341 bool done = false;
342 do {
343 size_t len = fread(fBuffer, 1, buf_size, fd);
344 done = len < buf_size;
345 if (!XML_Parse(p, fBuffer, len, done)) {
346 sprintf(msg, "%s at line %ld\n",
347 XML_ErrorString(XML_GetErrorCode(p)),
348 XML_GetCurrentLineNumber(p));
349 throw exception(msg);
351 } while (!done);
353 fclose(fd);
354 XML_ParserFree(p);
356 char* ts = fTitle;
357 while (*ts) {
358 if (*ts < ' ') *ts = ' ';
359 ++ts;
363 // start_element
364 void
365 Parser::start_element(void* data, const char* el, const char** attr)
367 // printf("Parser::start_element(%s)\n", el);
368 Parser& self = *(Parser*)data;
370 if (strcmp(el, "svg") == 0)
372 self.parse_svg(attr);
374 else
375 if (strcmp(el, "title") == 0)
377 self.fTitleFlag = true;
379 else
380 if (strcmp(el, "g") == 0)
382 self.fBuilder.push_attr();
383 self.parse_attr(attr);
385 else
386 if (strcmp(el, "path") == 0)
388 if (self.fPathFlag) {
389 throw exception("start_element: Nested path");
391 self.fBuilder.begin_path();
392 self.parse_path(attr);
393 self.fBuilder.end_path();
394 self.fPathFlag = true;
396 else
397 if (strcmp(el, "circle") == 0)
399 self.parse_circle(attr);
401 else
402 if (strcmp(el, "ellipse") == 0)
404 self.parse_ellipse(attr);
406 else
407 if (strcmp(el, "rect") == 0)
409 self.parse_rect(attr);
411 else
412 if (strcmp(el, "line") == 0)
414 self.parse_line(attr);
416 else
417 if (strcmp(el, "polyline") == 0)
419 self.parse_poly(attr, false);
421 else
422 if (strcmp(el, "polygon") == 0)
424 self.parse_poly(attr, true);
426 else
427 if (strcmp(el, "linearGradient") == 0 || strcmp(el, "radialGradient") == 0)
429 self.parse_gradient(attr, strcmp(el, "radialGradient") == 0);
431 else
432 if (strcmp(el, "stop") == 0)
434 self.parse_gradient_stop(attr);
436 //else
437 //if(strcmp(el, "<OTHER_ELEMENTS>") == 0)
440 // . . .
441 else
443 fprintf(stderr, "SVGParser igoring tag: \"%s\"\n", el);
444 self.fTagsIgnored = true;
448 // end_element
449 void
450 Parser::end_element(void* data, const char* el)
452 Parser& self = *(Parser*)data;
454 if (strcmp(el, "title") == 0)
456 self.fTitleFlag = false;
457 self.fBuilder.SetTitle(self.fTitle);
459 else
460 if (strcmp(el, "g") == 0)
462 self.fBuilder.pop_attr();
464 else
465 if (strcmp(el, "path") == 0)
467 self.fPathFlag = false;
469 else
470 if (strcmp(el, "linearGradient") == 0 || strcmp(el, "radialGradient") == 0)
472 self.fBuilder.EndGradient();
474 //else
475 //if(strcmp(el, "<OTHER_ELEMENTS>") == 0)
478 // . . .
481 // content
482 void
483 Parser::content(void* data, const char* s, int len)
485 Parser& self = *(Parser*)data;
487 // fTitleFlag signals that the <title> tag is being parsed now.
488 // The following code concatenates the pieces of content of the <title> tag.
489 if(self.fTitleFlag)
491 if(len + self.fTitleLength > 255) len = 255 - self.fTitleLength;
492 if(len > 0)
494 memcpy(self.fTitle + self.fTitleLength, s, len);
495 self.fTitleLength += len;
496 self.fTitle[self.fTitleLength] = 0;
501 // parse_svg
502 void Parser::parse_svg(const char** attr)
504 double width = 0.0;
505 double height = 0.0;
506 BRect viewBox(0.0, 0.0, -1.0, -1.0);
508 for (int i = 0; attr[i]; i += 2) {
509 if (strcmp(attr[i], "width") == 0)
511 width = parse_double(attr[i + 1]);
513 else
514 if (strcmp(attr[i], "height") == 0)
516 height = parse_double(attr[i + 1]);
518 else
519 if (strcmp(attr[i], "viewBox") == 0)
521 fPathTokenizer.set_path_str(attr[i + 1]);
522 if(!fPathTokenizer.next())
524 throw exception("parse_svg (viewBox): Too few coordinates");
526 viewBox.left = fPathTokenizer.last_number();
527 if(!fPathTokenizer.next())
529 throw exception("parse_svg (viewBox): Too few coordinates");
531 viewBox.top = fPathTokenizer.last_number();
532 if(!fPathTokenizer.next())
534 throw exception("parse_svg (viewBox): Too few coordinates");
536 viewBox.right = fPathTokenizer.last_number();
537 if(!fPathTokenizer.next())
539 throw exception("parse_svg (viewBox): Too few coordinates");
541 viewBox.bottom = fPathTokenizer.last_number();
544 if (width >= 0.0 && height >= 0.0) {
545 fBuilder.SetDimensions((uint32)ceil(width), (uint32)ceil(height), viewBox);
546 } else {
547 throw exception("parse_svg: Invalid width or height\n");
551 // parse_attr
552 void Parser::parse_attr(const char** attr)
554 for (int i = 0; attr[i]; i += 2) {
555 if (strcmp(attr[i], "style") == 0) {
556 parse_style(attr[i + 1]);
557 } else {
558 parse_attr(attr[i], attr[i + 1]);
563 // parse_path
564 void Parser::parse_path(const char** attr)
566 int i;
568 for(i = 0; attr[i]; i += 2)
570 // The <path> tag can consist of the path itself ("d=")
571 // as well as of other parameters like "style=", "transform=", etc.
572 // In the last case we simply rely on the function of parsing
573 // attributes (see 'else' branch).
574 if(strcmp(attr[i], "d") == 0)
576 fPathTokenizer.set_path_str(attr[i + 1]);
577 fBuilder.parse_path(fPathTokenizer);
579 else
581 // Create a temporary single pair "name-value" in order
582 // to avoid multiple calls for the same attribute.
583 const char* tmp[4];
584 tmp[0] = attr[i];
585 tmp[1] = attr[i + 1];
586 tmp[2] = 0;
587 tmp[3] = 0;
588 parse_attr(tmp);
593 // parse_attr
594 bool
595 Parser::parse_attr(const char* name, const char* value)
597 if(strcmp(name, "style") == 0) {
598 parse_style(value);
599 } else
600 if(strcmp(name, "opacity") == 0) {
601 fBuilder.opacity(parse_double(value));
602 } else
603 if(strcmp(name, "fill") == 0) {
604 if(strcmp(value, "none") == 0) {
605 fBuilder.fill_none();
606 } else if (strncmp(value, "url", 3) == 0) {
607 char* url = parse_url(value);
608 fBuilder.fill_url(url);
609 delete[] url;
610 } else {
611 fBuilder.fill(parse_color(value));
613 } else
614 if(strcmp(name, "fill-opacity") == 0) {
615 fBuilder.fill_opacity(parse_double(value));
616 } else
617 if(strcmp(name, "fill-rule") == 0) {
618 fBuilder.even_odd(strcmp(value, "evenodd") == 0);
619 } else
620 if(strcmp(name, "stroke") == 0) {
621 if(strcmp(value, "none") == 0) {
622 fBuilder.stroke_none();
623 } else if (strncmp(value, "url", 3) == 0) {
624 char* url = parse_url(value);
625 fBuilder.stroke_url(url);
626 delete[] url;
627 } else {
628 fBuilder.stroke(parse_color(value));
630 } else
631 if(strcmp(name, "stroke-width") == 0) {
632 fBuilder.stroke_width(parse_double(value));
633 } else
634 if(strcmp(name, "stroke-linecap") == 0) {
635 if(strcmp(value, "butt") == 0) fBuilder.line_cap(butt_cap);
636 else if(strcmp(value, "round") == 0) fBuilder.line_cap(round_cap);
637 else if(strcmp(value, "square") == 0) fBuilder.line_cap(square_cap);
638 } else
639 if(strcmp(name, "stroke-linejoin") == 0) {
640 if(strcmp(value, "miter") == 0) fBuilder.line_join(miter_join);
641 else if(strcmp(value, "round") == 0) fBuilder.line_join(round_join);
642 else if(strcmp(value, "bevel") == 0) fBuilder.line_join(bevel_join);
643 } else
644 if(strcmp(name, "stroke-miterlimit") == 0) {
645 fBuilder.miter_limit(parse_double(value));
646 } else
647 if(strcmp(name, "stroke-opacity") == 0) {
648 fBuilder.stroke_opacity(parse_double(value));
649 } else
650 if(strcmp(name, "transform") == 0) {
651 fBuilder.transform().premultiply(parse_transform(value));
652 } else
653 if (strcmp(name, "stop-color") == 0) {
654 fGradientStopColor = parse_color(value);
655 } else
656 if (strcmp(name, "stop-opacity") == 0) {
657 fGradientStopColor.opacity(parse_double(value));
659 //else
660 //if(strcmp(el, "<OTHER_ATTRIBUTES>") == 0)
663 // . . .
664 else
666 return false;
668 return true;
671 // copy_name
672 void Parser::copy_name(const char* start, const char* end)
674 unsigned len = unsigned(end - start);
675 if(fAttrNameLength == 0 || len > fAttrNameLength)
677 delete [] fAttrName;
678 fAttrName = new char[len + 1];
679 fAttrNameLength = len;
681 if(len) memcpy(fAttrName, start, len);
682 fAttrName[len] = 0;
685 // copy_value
686 void Parser::copy_value(const char* start, const char* end)
688 unsigned len = unsigned(end - start);
689 if(fAttrValueLength == 0 || len > fAttrValueLength)
691 delete [] fAttrValue;
692 fAttrValue = new char[len + 1];
693 fAttrValueLength = len;
695 if(len) memcpy(fAttrValue, start, len);
696 fAttrValue[len] = 0;
699 // parse_name_value
700 bool Parser::parse_name_value(const char* nv_start, const char* nv_end)
702 const char* str = nv_start;
703 while(str < nv_end && *str != ':') ++str;
705 const char* val = str;
707 // Right Trim
708 while(str > nv_start &&
709 (*str == ':' || isspace(*str))) --str;
710 ++str;
712 copy_name(nv_start, str);
714 while(val < nv_end && (*val == ':' || isspace(*val))) ++val;
716 copy_value(val, nv_end);
717 return parse_attr(fAttrName, fAttrValue);
720 // parse_style
721 void Parser::parse_style(const char* str)
723 while(*str)
725 // Left Trim
726 while(*str && isspace(*str)) ++str;
727 const char* nv_start = str;
728 while(*str && *str != ';') ++str;
729 const char* nv_end = str;
731 // Right Trim
732 while(nv_end > nv_start &&
733 (*nv_end == ';' || isspace(*nv_end))) --nv_end;
734 ++nv_end;
736 parse_name_value(nv_start, nv_end);
737 if(*str) ++str;
742 // parse_circle
743 void
744 Parser::parse_circle(const char** attr)
746 int i;
747 double cx = 0.0;
748 double cy = 0.0;
749 double r = 0.0;
751 fBuilder.begin_path();
752 for(i = 0; attr[i]; i += 2) {
753 if (!parse_attr(attr[i], attr[i + 1])) {
754 if(strcmp(attr[i], "cx") == 0) cx = parse_double(attr[i + 1]);
755 if(strcmp(attr[i], "cy") == 0) cy = parse_double(attr[i + 1]);
756 if(strcmp(attr[i], "r") == 0) r = parse_double(attr[i + 1]);
761 if (r != 0.0) {
762 if (r < 0.0) throw exception("parse_circle: Invalid radius: %f", r);
764 fBuilder.move_to(cx, cy - r);
765 fBuilder.curve4(cx + r * 0.56, cy - r,
766 cx + r, cy - r * 0.56,
767 cx + r, cy);
768 fBuilder.curve4(cx + r, cy + r * 0.56,
769 cx + r * 0.56, cy + r,
770 cx, cy + r);
771 fBuilder.curve4(cx - r * 0.56, cy + r,
772 cx - r, cy + r * 0.56,
773 cx - r, cy);
774 fBuilder.curve4(cx - r, cy - r * 0.56,
775 cx - r * 0.56, cy - r,
776 cx, cy - r);
777 fBuilder.close_subpath();
779 fBuilder.end_path();
782 // parse_ellipse
783 void
784 Parser::parse_ellipse(const char** attr)
786 int i;
787 double cx = 0.0;
788 double cy = 0.0;
789 double rx = 0.0;
790 double ry = 0.0;
792 fBuilder.begin_path();
793 for(i = 0; attr[i]; i += 2) {
794 if (!parse_attr(attr[i], attr[i + 1])) {
795 if(strcmp(attr[i], "cx") == 0) cx = parse_double(attr[i + 1]);
796 if(strcmp(attr[i], "cy") == 0) cy = parse_double(attr[i + 1]);
797 if(strcmp(attr[i], "rx") == 0) rx = parse_double(attr[i + 1]);
798 if(strcmp(attr[i], "ry") == 0) ry = parse_double(attr[i + 1]);
803 if (rx != 0.0 && ry != 0.0) {
804 if (rx < 0.0) throw exception("parse_ellipse: Invalid x-radius: %f", rx);
805 if (ry < 0.0) throw exception("parse_ellipse: Invalid y-radius: %f", ry);
807 fBuilder.move_to(cx, cy - ry);
808 fBuilder.curve4(cx + rx * 0.56, cy - ry,
809 cx + rx, cy - ry * 0.56,
810 cx + rx, cy);
811 fBuilder.curve4(cx + rx, cy + ry * 0.56,
812 cx + rx * 0.56, cy + ry,
813 cx, cy + ry);
814 fBuilder.curve4(cx - rx * 0.56, cy + ry,
815 cx - rx, cy + ry * 0.56,
816 cx - rx, cy);
817 fBuilder.curve4(cx - rx, cy - ry * 0.56,
818 cx - rx * 0.56, cy - ry,
819 cx, cy - ry);
820 fBuilder.close_subpath();
822 fBuilder.end_path();
825 // parse_rect
826 void
827 Parser::parse_rect(const char** attr)
829 int i;
830 double x = 0.0;
831 double y = 0.0;
832 double w = 0.0;
833 double h = 0.0;
835 fBuilder.begin_path();
836 for(i = 0; attr[i]; i += 2)
838 if(!parse_attr(attr[i], attr[i + 1]))
840 if(strcmp(attr[i], "x") == 0) x = parse_double(attr[i + 1]);
841 if(strcmp(attr[i], "y") == 0) y = parse_double(attr[i + 1]);
842 if(strcmp(attr[i], "width") == 0) w = parse_double(attr[i + 1]);
843 if(strcmp(attr[i], "height") == 0) h = parse_double(attr[i + 1]);
844 // rx - to be implemented
845 // ry - to be implemented
850 if(w != 0.0 && h != 0.0)
852 if(w < 0.0) throw exception("parse_rect: Invalid width: %f", w);
853 if(h < 0.0) throw exception("parse_rect: Invalid height: %f", h);
855 fBuilder.move_to(x, y);
856 fBuilder.line_to(x + w, y);
857 fBuilder.line_to(x + w, y + h);
858 fBuilder.line_to(x, y + h);
859 fBuilder.close_subpath();
861 fBuilder.end_path();
864 // parse_line
865 void
866 Parser::parse_line(const char** attr)
868 int i;
869 double x1 = 0.0;
870 double y1 = 0.0;
871 double x2 = 0.0;
872 double y2 = 0.0;
874 fBuilder.begin_path();
875 for(i = 0; attr[i]; i += 2)
877 if(!parse_attr(attr[i], attr[i + 1]))
879 if(strcmp(attr[i], "x1") == 0) x1 = parse_double(attr[i + 1]);
880 if(strcmp(attr[i], "y1") == 0) y1 = parse_double(attr[i + 1]);
881 if(strcmp(attr[i], "x2") == 0) x2 = parse_double(attr[i + 1]);
882 if(strcmp(attr[i], "y2") == 0) y2 = parse_double(attr[i + 1]);
886 fBuilder.move_to(x1, y1);
887 fBuilder.line_to(x2, y2);
888 fBuilder.end_path();
891 // parse_poly
892 void
893 Parser::parse_poly(const char** attr, bool close_flag)
895 int i;
896 double x = 0.0;
897 double y = 0.0;
899 fBuilder.begin_path();
900 for (i = 0; attr[i]; i += 2) {
901 if (!parse_attr(attr[i], attr[i + 1])) {
902 if (strcmp(attr[i], "points") == 0) {
903 fPathTokenizer.set_path_str(attr[i + 1]);
904 if (!fPathTokenizer.next())
905 throw exception("parse_poly: Too few coordinates");
906 x = fPathTokenizer.last_number();
907 if (!fPathTokenizer.next())
908 throw exception("parse_poly: Too few coordinates");
909 y = fPathTokenizer.last_number();
910 fBuilder.move_to(x, y);
911 while (fPathTokenizer.next()) {
912 x = fPathTokenizer.last_number();
913 if (!fPathTokenizer.next())
914 throw exception("parse_poly: Odd number of coordinates");
915 y = fPathTokenizer.last_number();
916 fBuilder.line_to(x, y);
921 if (close_flag)
922 fBuilder.close_subpath();
923 fBuilder.end_path();
926 // parse_transform
927 trans_affine
928 Parser::parse_transform(const char* str)
930 trans_affine transform;
931 while (*str) {
932 if (islower(*str)) {
933 if (strncmp(str, "matrix", 6) == 0) str += parse_matrix(str, transform); else
934 if (strncmp(str, "translate", 9) == 0) str += parse_translate(str, transform); else
935 if (strncmp(str, "rotate", 6) == 0) str += parse_rotate(str, transform); else
936 if (strncmp(str, "scale", 5) == 0) str += parse_scale(str, transform); else
937 if (strncmp(str, "skewX", 5) == 0) str += parse_skew_x(str, transform); else
938 if (strncmp(str, "skewY", 5) == 0) str += parse_skew_y(str, transform); else
940 ++str;
943 else
945 ++str;
948 return transform;
951 // parse_gradient
952 void
953 Parser::parse_gradient(const char** attr, bool radial)
955 // printf("Parser::parse_gradient(%s)\n", attr[0]);
957 fBuilder.StartGradient(radial);
959 for (int32 i = 0; attr[i]; i += 2)
961 /* if(!parse_attr(attr[i], attr[i + 1]))
963 if (strcmp(attr[i], "id") == 0)
964 fBuilder.CurrentGradient()->SetID(attr[i + 1]);
965 else if(strcmp(attr[i], "gradientTransform") == 0) {
966 fBuilder.CurrentGradient()->SetTransformation(parse_transform(attr[i + 1]));
967 } else
968 fBuilder.CurrentGradient()->AddString(attr[i], attr[i + 1]);
969 /* }*/
973 // parse_gradient_stop
974 void
975 Parser::parse_gradient_stop(const char** attr)
977 // printf("Parser::parse_gradient_stop(%s)\n", attr[0]);
979 float offset = 0.0;
980 rgba8 color;
981 for (int32 i = 0; attr[i]; i += 2) {
982 if (strcmp(attr[i], "offset") == 0) {
983 offset = parse_double(attr[i + 1]);
984 } else
985 if (strcmp(attr[i], "style") == 0) {
986 parse_style(attr[i + 1]);
987 // here we get a bit hacky, in order not to change too much code at once...
988 // historically, parse_style() was for parsing path attributes only, but
989 // it comes in handy here as well, and I added "stop-color" and "stop-opacity"
990 // to parse_name_value(). It remembers the color in "fGradientStopColor".
991 // The color will of course be broken if the "style" attribute did not contain
992 // any valid stuff.
993 color = fGradientStopColor;
994 } else
995 if (strcmp(attr[i], "stop-color") == 0) {
996 color = parse_color(attr[i + 1]);
997 } else
998 if (strcmp(attr[i], "stop-opacity") == 0) {
999 color.opacity(parse_double(attr[i + 1]));
1003 // printf(" offset: %f, color: %d, %d, %d, %d\n", offset, color.r, color.g, color.b, color.a);
1005 if (SVGGradient* gradient = fBuilder.CurrentGradient()) {
1006 gradient->AddStop(offset, color);
1007 } else {
1008 throw exception("parse_gradient_stop() outside of gradient tag!\n");
1012 // is_numeric
1013 static bool
1014 is_numeric(char c)
1016 return strchr("0123456789+-.eE", c) != 0;
1019 // parse_transform_args
1020 static unsigned
1021 parse_transform_args(const char* str,
1022 double* args,
1023 unsigned max_na,
1024 unsigned* na)
1026 *na = 0;
1027 const char* ptr = str;
1028 while(*ptr && *ptr != '(') ++ptr;
1029 if(*ptr == 0)
1031 throw exception("parse_transform_args: Invalid syntax");
1033 const char* end = ptr;
1034 while(*end && *end != ')') ++end;
1035 if(*end == 0)
1037 throw exception("parse_transform_args: Invalid syntax");
1040 while(ptr < end)
1042 if(is_numeric(*ptr))
1044 if(*na >= max_na)
1046 throw exception("parse_transform_args: Too many arguments");
1048 args[(*na)++] = atof(ptr);
1049 while(ptr < end && is_numeric(*ptr)) ++ptr;
1051 else
1053 ++ptr;
1056 return unsigned(end - str);
1059 // parse_matrix
1060 unsigned
1061 Parser::parse_matrix(const char* str, trans_affine& transform)
1063 double args[6];
1064 unsigned na = 0;
1065 unsigned len = parse_transform_args(str, args, 6, &na);
1066 if(na != 6)
1068 throw exception("parse_matrix: Invalid number of arguments");
1070 transform.premultiply(trans_affine(args[0], args[1], args[2], args[3], args[4], args[5]));
1071 return len;
1074 // parse_translate
1075 unsigned
1076 Parser::parse_translate(const char* str, trans_affine& transform)
1078 double args[2];
1079 unsigned na = 0;
1080 unsigned len = parse_transform_args(str, args, 2, &na);
1081 if(na == 1) args[1] = 0.0;
1082 transform.premultiply(trans_affine_translation(args[0], args[1]));
1083 return len;
1086 // parse_rotate
1087 unsigned
1088 Parser::parse_rotate(const char* str, trans_affine& transform)
1090 double args[3];
1091 unsigned na = 0;
1092 unsigned len = parse_transform_args(str, args, 3, &na);
1093 if(na == 1)
1095 transform.premultiply(trans_affine_rotation(deg2rad(args[0])));
1097 else if(na == 3)
1099 trans_affine t = trans_affine_translation(-args[1], -args[2]);
1100 t *= trans_affine_rotation(deg2rad(args[0]));
1101 t *= trans_affine_translation(args[1], args[2]);
1102 transform.premultiply(t);
1104 else
1106 throw exception("parse_rotate: Invalid number of arguments");
1108 return len;
1111 // parse_scale
1112 unsigned Parser::parse_scale(const char* str, trans_affine& transform)
1114 double args[2];
1115 unsigned na = 0;
1116 unsigned len = parse_transform_args(str, args, 2, &na);
1117 if(na == 1) args[1] = args[0];
1118 transform.premultiply(trans_affine_scaling(args[0], args[1]));
1119 return len;
1122 // parse_skew_x
1123 unsigned
1124 Parser::parse_skew_x(const char* str, trans_affine& transform)
1126 double arg;
1127 unsigned na = 0;
1128 unsigned len = parse_transform_args(str, &arg, 1, &na);
1129 transform.premultiply(trans_affine_skewing(deg2rad(arg), 0.0));
1130 return len;
1133 // parse_skew_y
1134 unsigned
1135 Parser::parse_skew_y(const char* str, trans_affine& transform)
1137 double arg;
1138 unsigned na = 0;
1139 unsigned len = parse_transform_args(str, &arg, 1, &na);
1140 transform.premultiply(trans_affine_skewing(0.0, deg2rad(arg)));
1141 return len;
1145 } // namespace svg
1146 } // namespace agg