1/*
2 * Copyright 2006, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan A��mus <superstippi@gmx.de>
7 */
8
9//----------------------------------------------------------------------------
10// Anti-Grain Geometry - Version 2.2
11// Copyright (C) 2002-2004 Maxim Shemanarev (http://www.antigrain.com)
12//
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.
17//
18//----------------------------------------------------------------------------
19// Contact: mcseem@antigrain.com
20//		  mcseemagg@yahoo.com
21//		  http://www.antigrain.com
22//----------------------------------------------------------------------------
23
24#include "SVGParser.h"
25
26#include <stdio.h>
27#include <string.h>
28#include <ctype.h>
29
30#include <expat.h>
31
32#include "SVGGradients.h"
33
34namespace agg {
35namespace svg {
36
37struct named_color
38{
39	char  name[22];
40	int8u r, g, b, a;
41};
42
43named_color colors[] =
44{
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 }
193};
194
195
196
197// cmp_color
198int
199cmp_color(const void* p1, const void* p2)
200{
201	return strcmp(((named_color*)p1)->name, ((named_color*)p2)->name);
202}
203
204// parse_color
205rgba8
206parse_color(const char* str)
207{
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);
228		}
229		return rgb8_packed(c);
230	} else {
231		named_color c;
232		unsigned len = strlen(str);
233		if(len > sizeof(c.name) - 1)
234		{
235			throw exception("parse_color: Invalid color name '%s'", str);
236		}
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)
244		{
245			throw exception("parse_color: Invalid color name '%s'", str);
246		}
247		const named_color* pc = (const named_color*)p;
248		return rgba8(pc->r, pc->g, pc->b, pc->a);
249	}
250}
251
252// parse_double
253double
254parse_double(const char* str)
255{
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;
263}
264
265// parse_url
266char*
267parse_url(const char* str)
268{
269	const char* begin = str;
270	while (*begin != '#')
271		begin++;
272
273	begin++;
274	const char* end = begin;
275	while (*end != ')')
276		end++;
277
278	end--;
279
280	int32 length = end - begin + 2;
281	char* result = new char[length];
282	memcpy(result, begin, length - 1);
283	result[length - 1] = 0;
284
285	return result;
286}
287
288
289// #pragma mark -
290
291// constructor
292Parser::Parser(DocumentBuilder& builder)
293	: fBuilder(builder),
294	  fPathTokenizer(),
295	  fBuffer(new char[buf_size]),
296	  fTitle(new char[256]),
297	  fTitleLength(0),
298
299	  fTitleFlag(false),
300	  fPathFlag(false),
301
302	  fAttrName(new char[128]),
303	  fAttrValue(new char[1024]),
304	  fAttrNameLength(127),
305	  fAttrValueLength(1023),
306
307	  fTagsIgnored(false)
308{
309	fTitle[0] = 0;
310}
311
312// destructor
313Parser::~Parser()
314{
315	delete[] fAttrValue;
316	delete[] fAttrName;
317	delete[] fBuffer;
318	delete[] fTitle;
319}
320
321// parse
322void
323Parser::parse(const char* pathToFile)
324{
325	char msg[1024];
326	XML_Parser p = XML_ParserCreate(NULL);
327	if (p == 0) {
328		throw exception("Couldn't allocate memory for Parser");
329	}
330
331	XML_SetUserData(p, this);
332	XML_SetElementHandler(p, start_element, end_element);
333	XML_SetCharacterDataHandler(p, content);
334
335	FILE* fd = fopen(pathToFile, "r");
336	if (fd == 0) {
337		sprintf(msg, "Couldn't open file %s", pathToFile);
338		throw exception(msg);
339	}
340
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 %d\n",
347					XML_ErrorString(XML_GetErrorCode(p)),
348					XML_GetCurrentLineNumber(p));
349			throw exception(msg);
350		}
351	} while (!done);
352
353	fclose(fd);
354	XML_ParserFree(p);
355
356	char* ts = fTitle;
357	while (*ts) {
358		if (*ts < ' ') *ts = ' ';
359		++ts;
360	}
361}
362
363// start_element
364void
365Parser::start_element(void* data, const char* el, const char** attr)
366{
367// printf("Parser::start_element(%s)\n", el);
368	Parser& self = *(Parser*)data;
369
370	if (strcmp(el, "svg") == 0)
371	{
372		self.parse_svg(attr);
373	}
374	else
375	if (strcmp(el, "title") == 0)
376	{
377		self.fTitleFlag = true;
378	}
379	else
380	if (strcmp(el, "g") == 0)
381	{
382		self.fBuilder.push_attr();
383		self.parse_attr(attr);
384	}
385	else
386	if (strcmp(el, "path") == 0)
387	{
388		if (self.fPathFlag) {
389			throw exception("start_element: Nested path");
390		}
391		self.fBuilder.begin_path();
392		self.parse_path(attr);
393		self.fBuilder.end_path();
394		self.fPathFlag = true;
395	}
396	else
397	if (strcmp(el, "circle") == 0)
398	{
399		self.parse_circle(attr);
400	}
401	else
402	if (strcmp(el, "ellipse") == 0)
403	{
404		self.parse_ellipse(attr);
405	}
406	else
407	if (strcmp(el, "rect") == 0)
408	{
409		self.parse_rect(attr);
410	}
411	else
412	if (strcmp(el, "line") == 0)
413	{
414		self.parse_line(attr);
415	}
416	else
417	if (strcmp(el, "polyline") == 0)
418	{
419		self.parse_poly(attr, false);
420	}
421	else
422	if (strcmp(el, "polygon") == 0)
423	{
424		self.parse_poly(attr, true);
425	}
426	else
427	if (strcmp(el, "linearGradient") == 0 || strcmp(el, "radialGradient") == 0)
428	{
429		self.parse_gradient(attr, strcmp(el, "radialGradient") == 0);
430	}
431	else
432	if (strcmp(el, "stop") == 0)
433	{
434		self.parse_gradient_stop(attr);
435	}
436	//else
437	//if(strcmp(el, "<OTHER_ELEMENTS>") == 0)
438	//{
439	//}
440	// . . .
441	else
442	{
443		fprintf(stderr, "SVGParser igoring tag: \"%s\"\n", el);
444		self.fTagsIgnored = true;
445	}
446}
447
448// end_element
449void
450Parser::end_element(void* data, const char* el)
451{
452	Parser& self = *(Parser*)data;
453
454	if (strcmp(el, "title") == 0)
455	{
456		self.fTitleFlag = false;
457		self.fBuilder.SetTitle(self.fTitle);
458	}
459	else
460	if (strcmp(el, "g") == 0)
461	{
462		self.fBuilder.pop_attr();
463	}
464	else
465	if (strcmp(el, "path") == 0)
466	{
467		self.fPathFlag = false;
468	}
469	else
470	if (strcmp(el, "linearGradient") == 0 || strcmp(el, "radialGradient") == 0)
471	{
472		self.fBuilder.EndGradient();
473	}
474	//else
475	//if(strcmp(el, "<OTHER_ELEMENTS>") == 0)
476	//{
477	//}
478	// . . .
479}
480
481// content
482void
483Parser::content(void* data, const char* s, int len)
484{
485	Parser& self = *(Parser*)data;
486
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)
490	{
491		if(len + self.fTitleLength > 255) len = 255 - self.fTitleLength;
492		if(len > 0)
493		{
494			memcpy(self.fTitle + self.fTitleLength, s, len);
495			self.fTitleLength += len;
496			self.fTitle[self.fTitleLength] = 0;
497		}
498	}
499}
500
501// parse_svg
502void Parser::parse_svg(const char** attr)
503{
504	double width = 0.0;
505	double height = 0.0;
506	BRect viewBox(0.0, 0.0, -1.0, -1.0);
507
508	for (int i = 0; attr[i]; i += 2) {
509		if (strcmp(attr[i], "width") == 0)
510		{
511			width = parse_double(attr[i + 1]);
512		}
513		else
514		if (strcmp(attr[i], "height") == 0)
515		{
516			height = parse_double(attr[i + 1]);
517		}
518		else
519		if (strcmp(attr[i], "viewBox") == 0)
520		{
521			fPathTokenizer.set_path_str(attr[i + 1]);
522			if(!fPathTokenizer.next())
523			{
524				throw exception("parse_svg (viewBox): Too few coordinates");
525			}
526			viewBox.left = fPathTokenizer.last_number();
527			if(!fPathTokenizer.next())
528			{
529				throw exception("parse_svg (viewBox): Too few coordinates");
530			}
531			viewBox.top = fPathTokenizer.last_number();
532			if(!fPathTokenizer.next())
533			{
534				throw exception("parse_svg (viewBox): Too few coordinates");
535			}
536			viewBox.right = fPathTokenizer.last_number();
537			if(!fPathTokenizer.next())
538			{
539				throw exception("parse_svg (viewBox): Too few coordinates");
540			}
541			viewBox.bottom = fPathTokenizer.last_number();
542		}
543	}
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");
548	}
549}
550
551// parse_attr
552void Parser::parse_attr(const char** attr)
553{
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]);
559		}
560	}
561}
562
563// parse_path
564void Parser::parse_path(const char** attr)
565{
566	int i;
567
568	for(i = 0; attr[i]; i += 2)
569	{
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)
575		{
576			fPathTokenizer.set_path_str(attr[i + 1]);
577			fBuilder.parse_path(fPathTokenizer);
578		}
579		else
580		{
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);
589		}
590	}
591}
592
593// parse_attr
594bool
595Parser::parse_attr(const char* name, const char* value)
596{
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));
612		}
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));
629		}
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));
658	}
659	//else
660	//if(strcmp(el, "<OTHER_ATTRIBUTES>") == 0)
661	//{
662	//}
663	// . . .
664	else
665	{
666		return false;
667	}
668	return true;
669}
670
671// copy_name
672void Parser::copy_name(const char* start, const char* end)
673{
674	unsigned len = unsigned(end - start);
675	if(fAttrNameLength == 0 || len > fAttrNameLength)
676	{
677		delete [] fAttrName;
678		fAttrName = new char[len + 1];
679		fAttrNameLength = len;
680	}
681	if(len) memcpy(fAttrName, start, len);
682	fAttrName[len] = 0;
683}
684
685// copy_value
686void Parser::copy_value(const char* start, const char* end)
687{
688	unsigned len = unsigned(end - start);
689	if(fAttrValueLength == 0 || len > fAttrValueLength)
690	{
691		delete [] fAttrValue;
692		fAttrValue = new char[len + 1];
693		fAttrValueLength = len;
694	}
695	if(len) memcpy(fAttrValue, start, len);
696	fAttrValue[len] = 0;
697}
698
699// parse_name_value
700bool Parser::parse_name_value(const char* nv_start, const char* nv_end)
701{
702	const char* str = nv_start;
703	while(str < nv_end && *str != ':') ++str;
704
705	const char* val = str;
706
707	// Right Trim
708	while(str > nv_start &&
709		(*str == ':' || isspace(*str))) --str;
710	++str;
711
712	copy_name(nv_start, str);
713
714	while(val < nv_end && (*val == ':' || isspace(*val))) ++val;
715
716	copy_value(val, nv_end);
717	return parse_attr(fAttrName, fAttrValue);
718}
719
720// parse_style
721void Parser::parse_style(const char* str)
722{
723	while(*str)
724	{
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;
730
731		// Right Trim
732		while(nv_end > nv_start &&
733			(*nv_end == ';' || isspace(*nv_end))) --nv_end;
734		++nv_end;
735
736		parse_name_value(nv_start, nv_end);
737		if(*str) ++str;
738	}
739
740}
741
742// parse_circle
743void
744Parser::parse_circle(const char** attr)
745{
746	int i;
747	double cx = 0.0;
748	double cy = 0.0;
749	double r = 0.0;
750
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]);
757		}
758	}
759
760
761	if (r != 0.0) {
762		if (r < 0.0) throw exception("parse_circle: Invalid radius: %f", r);
763
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();
778	}
779	fBuilder.end_path();
780}
781
782// parse_ellipse
783void
784Parser::parse_ellipse(const char** attr)
785{
786	int i;
787	double cx = 0.0;
788	double cy = 0.0;
789	double rx = 0.0;
790	double ry = 0.0;
791
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]);
799		}
800	}
801
802
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);
806
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();
821	}
822	fBuilder.end_path();
823}
824
825// parse_rect
826void
827Parser::parse_rect(const char** attr)
828{
829	int i;
830	double x = 0.0;
831	double y = 0.0;
832	double w = 0.0;
833	double h = 0.0;
834
835	fBuilder.begin_path();
836	for(i = 0; attr[i]; i += 2)
837	{
838		if(!parse_attr(attr[i], attr[i + 1]))
839		{
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
846		}
847	}
848
849
850	if(w != 0.0 && h != 0.0)
851	{
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);
854
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();
860	}
861	fBuilder.end_path();
862}
863
864// parse_line
865void
866Parser::parse_line(const char** attr)
867{
868	int i;
869	double x1 = 0.0;
870	double y1 = 0.0;
871	double x2 = 0.0;
872	double y2 = 0.0;
873
874	fBuilder.begin_path();
875	for(i = 0; attr[i]; i += 2)
876	{
877		if(!parse_attr(attr[i], attr[i + 1]))
878		{
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]);
883		}
884	}
885
886	fBuilder.move_to(x1, y1);
887	fBuilder.line_to(x2, y2);
888	fBuilder.end_path();
889}
890
891// parse_poly
892void
893Parser::parse_poly(const char** attr, bool close_flag)
894{
895	int i;
896	double x = 0.0;
897	double y = 0.0;
898
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);
917				}
918			}
919		}
920	}
921	if (close_flag)
922		fBuilder.close_subpath();
923	fBuilder.end_path();
924}
925
926// parse_transform
927trans_affine
928Parser::parse_transform(const char* str)
929{
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
939			{
940				++str;
941			}
942		}
943		else
944		{
945			++str;
946		}
947	}
948	return transform;
949}
950
951// parse_gradient
952void
953Parser::parse_gradient(const char** attr, bool radial)
954{
955//	printf("Parser::parse_gradient(%s)\n", attr[0]);
956
957	fBuilder.StartGradient(radial);
958
959	for (int32 i = 0; attr[i]; i += 2)
960	{
961/*		if(!parse_attr(attr[i], attr[i + 1]))
962		{*/
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/*		}*/
970	}
971}
972
973// parse_gradient_stop
974void
975Parser::parse_gradient_stop(const char** attr)
976{
977//	printf("Parser::parse_gradient_stop(%s)\n", attr[0]);
978
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]));
1000		}
1001	}
1002
1003//	printf("  offset: %f, color: %d, %d, %d, %d\n", offset, color.r, color.g, color.b, color.a);
1004
1005	if (SVGGradient* gradient = fBuilder.CurrentGradient()) {
1006		gradient->AddStop(offset, color);
1007	} else {
1008		throw exception("parse_gradient_stop() outside of gradient tag!\n");
1009	}
1010}
1011
1012// is_numeric
1013static bool
1014is_numeric(char c)
1015{
1016	return strchr("0123456789+-.eE", c) != 0;
1017}
1018
1019// parse_transform_args
1020static unsigned
1021parse_transform_args(const char* str,
1022					 double* args,
1023					 unsigned max_na,
1024					 unsigned* na)
1025{
1026	*na = 0;
1027	const char* ptr = str;
1028	while(*ptr && *ptr != '(') ++ptr;
1029	if(*ptr == 0)
1030	{
1031		throw exception("parse_transform_args: Invalid syntax");
1032	}
1033	const char* end = ptr;
1034	while(*end && *end != ')') ++end;
1035	if(*end == 0)
1036	{
1037		throw exception("parse_transform_args: Invalid syntax");
1038	}
1039
1040	while(ptr < end)
1041	{
1042		if(is_numeric(*ptr))
1043		{
1044			if(*na >= max_na)
1045			{
1046				throw exception("parse_transform_args: Too many arguments");
1047			}
1048			args[(*na)++] = atof(ptr);
1049			while(ptr < end && is_numeric(*ptr)) ++ptr;
1050		}
1051		else
1052		{
1053			++ptr;
1054		}
1055	}
1056	return unsigned(end - str);
1057}
1058
1059// parse_matrix
1060unsigned
1061Parser::parse_matrix(const char* str, trans_affine& transform)
1062{
1063	double args[6];
1064	unsigned na = 0;
1065	unsigned len = parse_transform_args(str, args, 6, &na);
1066	if(na != 6)
1067	{
1068		throw exception("parse_matrix: Invalid number of arguments");
1069	}
1070	transform.premultiply(trans_affine(args[0], args[1], args[2], args[3], args[4], args[5]));
1071	return len;
1072}
1073
1074// parse_translate
1075unsigned
1076Parser::parse_translate(const char* str, trans_affine& transform)
1077{
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;
1084}
1085
1086// parse_rotate
1087unsigned
1088Parser::parse_rotate(const char* str, trans_affine& transform)
1089{
1090	double args[3];
1091	unsigned na = 0;
1092	unsigned len = parse_transform_args(str, args, 3, &na);
1093	if(na == 1)
1094	{
1095		transform.premultiply(trans_affine_rotation(deg2rad(args[0])));
1096	}
1097	else if(na == 3)
1098	{
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);
1103	}
1104	else
1105	{
1106		throw exception("parse_rotate: Invalid number of arguments");
1107	}
1108	return len;
1109}
1110
1111// parse_scale
1112unsigned Parser::parse_scale(const char* str, trans_affine& transform)
1113{
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;
1120}
1121
1122// parse_skew_x
1123unsigned
1124Parser::parse_skew_x(const char* str, trans_affine& transform)
1125{
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;
1131}
1132
1133// parse_skew_y
1134unsigned
1135Parser::parse_skew_y(const char* str, trans_affine& transform)
1136{
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;
1142}
1143
1144
1145} // namespace svg
1146} // namespace agg
1147
1148
1149
1150