1/*
2	Copyright (C) 2012 - 2016 Johan Mattsson
3
4	This library is free software; you can redistribute it and/or modify
5	it under the terms of the GNU Lesser General Public License as
6	published by the Free Software Foundation; either version 3 of the
7	License, or (at your option) any later version.
8
9	This library is distributed in the hope that it will be useful, but
10	WITHOUT ANY WARRANTY; without even the implied warranty of
11	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12	Lesser General Public License for more details.
13*/
14
15using B;
16using Math;
17
18namespace BirdFont {
19
20public enum SvgFormat {
21	NONE,
22	INKSCAPE,
23	ILLUSTRATOR
24}
25
26public class SvgParser {
27
28	SvgFormat format = SvgFormat.ILLUSTRATOR;
29
30	public SvgParser () {
31	}
32
33	public void set_format (SvgFormat f) {
34		format = f;
35	}
36
37	public static void import () {
38		FileChooser fc = new FileChooser ();
39		fc.file_selected.connect ((p) => {
40			string path;
41
42			if (p == null) {
43				return;
44			}
45
46			path = (!) p;
47			import_svg (path);
48		});
49
50		fc.add_extension ("svg");
51		MainWindow.file_chooser (t_("Import"), fc, FileChooser.LOAD);
52	}
53
54	public static void import_folder () {
55		FileChooser fc = new FileChooser ();
56		fc.file_selected.connect ((p) => {
57			string path;
58			File svg_folder;
59			File svg;
60			bool imported;
61			FileEnumerator enumerator;
62			FileInfo? file_info;
63			string file_name;
64			Font font;
65
66			if (p == null) {
67				return;
68			}
69
70			path = (!) p;
71			svg_folder = File.new_for_path (path);
72			font = BirdFont.get_current_font ();
73
74			try {
75				enumerator = svg_folder.enumerate_children (FileAttribute.STANDARD_NAME, 0);
76				while ((file_info = enumerator.next_file ()) != null) {
77					file_name = ((!) file_info).get_name ();
78
79					if (file_name.has_suffix (".svg")) {
80						svg = get_child (svg_folder, file_name);
81						imported = import_svg_file (font, svg);
82
83						if (!imported) {
84							warning ("Can't import %s.", (!) svg.get_path ());
85						} else {
86							font.touch ();
87						}
88					}
89				}
90			} catch (Error e) {
91				warning (e.message);
92			}
93		});
94
95		MainWindow.file_chooser (t_("Import"), fc, FileChooser.LOAD | FileChooser.DIRECTORY);
96	}
97
98	public static void import_svg_data (string xml_data, SvgFormat format = SvgFormat.NONE) {
99		PathList path_list = new PathList ();
100		Glyph glyph;
101		string[] lines = xml_data.split ("\n");
102		bool has_format = false;
103		SvgParser parser = new SvgParser ();
104		XmlParser xmlparser;
105
106		foreach (string l in lines) {
107			if (l.index_of ("Illustrator") > -1 || l.index_of ("illustrator") > -1) {
108				parser.set_format (SvgFormat.ILLUSTRATOR);
109				has_format = true;
110			}
111
112			if (l.index_of ("Inkscape") > -1 || l.index_of ("inkscape") > -1) {
113				parser.set_format (SvgFormat.INKSCAPE);
114				has_format = true;
115			}
116		}
117
118		if (format != SvgFormat.NONE) {
119			parser.set_format (format);
120		}
121
122		// parse the file
123		if (!has_format) {
124			warn_if_test ("No format identifier found in SVG parser.\n");
125		}
126
127		xmlparser = new XmlParser (xml_data);
128
129		if (!xmlparser.validate()) {
130			warning("Invalid XML in SVG parser.");
131		}
132
133		path_list = parser.parse_svg_file (xmlparser.get_root_tag ());
134
135		glyph = MainWindow.get_current_glyph ();
136		foreach (Path p in path_list.paths) {
137			glyph.add_path (p);
138		}
139
140		foreach (Path p in path_list.paths) {
141			glyph.add_active_path (null, p); // FIXME: groups
142			p.update_region_boundaries ();
143		}
144
145		glyph.close_path ();
146		glyph.fix_curve_orientation ();
147	}
148
149	public static string replace (string content, string start, string stop, string replacement) {
150		int i_tag = content.index_of (start);
151		int end_tag = content.index_of (stop, i_tag);
152		string c = "";
153
154		if (i_tag > -1) {
155			c = content.substring (0, i_tag)
156				+ replacement
157				+ content.substring (end_tag + stop.length);
158		} else {
159			c = content;
160		}
161
162		return c;
163	}
164
165	public static void import_svg (string path) {
166		string svg_data;
167		try {
168			FileUtils.get_contents (path, out svg_data);
169		} catch (GLib.Error e) {
170			warning (e.message);
171		}
172		import_svg_data (svg_data);
173	}
174
175	private PathList parse_svg_file (Tag tag) {
176		Layer pl = new Layer ();
177
178		foreach (Tag t in tag) {
179
180			if (t.get_name () == "g") {
181				parse_layer (t, pl);
182			}
183
184			if (t.get_name () == "switch") {
185				parse_layer (t, pl);
186			}
187
188			if (t.get_name () == "path") {
189				parse_path (t, pl);
190			}
191
192			if (t.get_name () == "polygon") {
193				parse_polygon (t, pl);
194			}
195
196			if (t.get_name () == "polyline") {
197				parse_polyline (t, pl);
198			}
199
200			if (t.get_name () == "circle") {
201				parse_circle (t, pl);
202			}
203
204			if (t.get_name () == "ellipse") {
205				parse_ellipse (t, pl);
206			}
207
208			if (t.get_name () == "line") {
209				parse_line (t, pl);
210			}
211		}
212
213		return pl.get_all_paths ();
214	}
215
216	private void parse_layer (Tag tag, Layer pl) {
217		Layer layer;
218		bool hidden = false;
219
220		foreach (Attribute attr in tag.get_attributes ()) {
221			if (attr.get_name () == "display" && attr.get_content () == "none") {
222				hidden = true;
223			}
224
225			if (attr.get_name () == "visibility"
226				&& (attr.get_content () == "hidden"
227					|| attr.get_content () == "collapse")) {
228				hidden = true;
229			}
230		}
231
232		if (hidden) {
233			return;
234		}
235
236		foreach (Tag t in tag) {
237			if (t.get_name () == "path") {
238				parse_path (t, pl);
239			}
240
241			if (t.get_name () == "g") {
242				layer = new Layer ();
243				parse_layer (t, layer);
244				pl.subgroups.add (layer);
245			}
246
247			if (t.get_name () == "polygon") {
248				parse_polygon (t, pl);
249			}
250
251			if (t.get_name () == "polyline") {
252				parse_polyline (t, pl);
253			}
254
255			if (t.get_name () == "rect") {
256				parse_rect (t, pl);
257			}
258
259			if (t.get_name () == "circle") {
260				parse_circle (t, pl);
261			}
262
263			if (t.get_name () == "ellipse") {
264				parse_ellipse (t, pl);
265			}
266
267			if (t.get_name () == "line") {
268				parse_line (t, pl);
269			}
270		}
271
272		foreach (Attribute attr in tag.get_attributes ()) {
273			if (attr.get_name () == "transform") {
274				transform (attr.get_content (), pl);
275			}
276		}
277	}
278
279	private void transform (string transform_functions, Layer layer) {
280		transform_paths (transform_functions, layer.paths);
281		transform_subgroups (transform_functions, layer);
282	}
283
284	private void transform_subgroups (string transform_functions, Layer layer) {
285		foreach (Layer subgroup in layer.subgroups) {
286			transform (transform_functions, subgroup);
287		}
288	}
289
290	private void transform_paths (string transform_functions, PathList pl) {
291		string data = transform_functions.dup ();
292		string[] functions;
293
294		// use only a single space as separator
295		while (data.index_of ("  ") > -1) {
296			data = data.replace ("  ", " ");
297		}
298
299		return_if_fail (data.index_of (")") > -1);
300
301		 // add separator
302		data = data.replace (") ", "|");
303		data = data.replace (")", "|");
304		functions = data.split ("|");
305
306		for (int i = functions.length - 1; i >= 0; i--) {
307			if (functions[i].has_prefix ("translate")) {
308				translate (functions[i], pl);
309			}
310
311			if (functions[i].has_prefix ("scale")) {
312				scale (functions[i], pl);
313			}
314
315			if (functions[i].has_prefix ("matrix")) {
316				matrix (functions[i], pl);
317			}
318
319			// TODO: rotate etc.
320		}
321	}
322
323	/** @param path a path in the cartesian coordinate system
324	 * The other parameters are in the SVG coordinate system.
325	 */
326	public static void apply_matrix (Path path, double a, double b, double c,
327		double d, double e, double f){
328
329		double dx, dy;
330		Font font = BirdFont.get_current_font ();
331		Glyph glyph = MainWindow.get_current_glyph ();
332
333		foreach (EditPoint ep in path.points) {
334			ep.tie_handles = false;
335			ep.reflective_point = false;
336		}
337
338		foreach (EditPoint ep in path.points) {
339			apply_matrix_on_handle (ep.get_right_handle (), a, b, c, d, e, f);
340
341			EditPointHandle left = ep.get_left_handle ();
342			if (left.type == PointType.QUADRATIC || left.type == PointType.LINE_QUADRATIC) {
343				ep.get_right_handle ().process_connected_handle ();
344			} else {
345				apply_matrix_on_handle (left, a, b, c, d, e, f);
346			}
347
348			ep.independent_y = font.top_position - ep.independent_y;
349			ep.independent_x -= glyph.left_limit;
350
351			dx = a * ep.independent_x + c * ep.independent_y + e;
352			dy = b * ep.independent_x + d * ep.independent_y + f;
353
354			ep.independent_x = dx;
355			ep.independent_y = dy;
356
357			ep.independent_y = font.top_position - ep.independent_y;
358			ep.independent_x += glyph.left_limit;
359		}
360	}
361
362	public static void apply_matrix_on_handle (EditPointHandle h,
363		double a, double b, double c,
364		double d, double e, double f){
365
366		double dx, dy;
367		Font font = BirdFont.get_current_font ();
368		Glyph glyph = MainWindow.get_current_glyph ();
369
370		h.y = font.top_position - h.y;
371		h.x -= glyph.left_limit;
372
373		dx = a * h.x + c * h.y + e;
374		dy = b * h.x + d * h.y + f;
375
376		h.x = dx;
377		h.y = dy;
378
379		h.y = font.top_position - h.y;
380		h.x += glyph.left_limit;
381	}
382
383
384	private void matrix (string function, PathList pl) {
385		string parameters = get_transform_parameters (function);
386		string[] p = parameters.split (" ");
387
388		if (p.length != 6) {
389			warning ("Expecting six parameters for matrix transformation.");
390			return;
391		}
392
393		foreach (Path path in pl.paths) {
394			apply_matrix (path, parse_double (p[0]), parse_double (p[1]),
395				parse_double (p[2]), parse_double (p[3]),
396				parse_double (p[4]), parse_double (p[5]));
397		}
398	}
399
400	private void scale (string function, PathList pl) {
401		string parameters = get_transform_parameters (function);
402		string[] p = parameters.split (" ");
403		double x, y;
404
405		x = 1;
406		y = 1;
407
408		if (p.length > 0) {
409			x = parse_double (p[0]);
410		}
411
412		if (p.length > 1) {
413			y = parse_double (p[1]);
414		}
415
416		foreach (Path path in pl.paths) {
417			path.scale (-x, y);
418		}
419	}
420
421	private void translate (string function, PathList pl) {
422		string parameters = get_transform_parameters (function);
423		string[] p = parameters.split (" ");
424		double x, y;
425
426		x = 0;
427		y = 0;
428
429		if (p.length > 0) {
430			x = parse_double (p[0]);
431		}
432
433		if (p.length > 1) {
434			y = parse_double (p[1]);
435		}
436
437		foreach (Path path in pl.paths) {
438			path.move (x, -y);
439		}
440	}
441
442	private string get_transform_parameters (string function) {
443		int i;
444		string param = "";
445
446		i = function.index_of ("(");
447		return_val_if_fail (i != -1, param);
448		param = function.substring (i);
449
450		param = param.replace ("(", "");
451		param = param.replace ("\n", " ");
452		param = param.replace ("\t", " ");
453		param = param.replace (",", " ");
454
455		while (param.index_of ("  ") > -1) {
456			param = param.replace ("  ", " ");
457		}
458
459		return param.strip();
460	}
461
462	private void parse_circle (Tag tag, Layer pl) {
463		Path p;
464		double x, y, r;
465		Glyph g;
466		PathList npl;
467		BezierPoints[] bezier_points;
468		SvgStyle style = new SvgStyle ();
469		bool hidden = false;
470
471		npl = new PathList ();
472
473		x = 0;
474		y = 0;
475		r = 0;
476
477		foreach (Attribute attr in tag.get_attributes ()) {
478			if (attr.get_name () == "cx") {
479				x = parse_double (attr.get_content ());
480			}
481
482			if (attr.get_name () == "cy") {
483				y = -parse_double (attr.get_content ());
484			}
485
486			if (attr.get_name () == "r") {
487				r = parse_double (attr.get_content ());
488			}
489
490			if (attr.get_name () == "display" && attr.get_content () == "none") {
491				hidden = true;
492			}
493		}
494
495		style = SvgStyle.parse (tag.get_attributes ());
496
497		if (hidden) {
498			return;
499		}
500
501		bezier_points = new BezierPoints[1];
502		bezier_points[0] = new BezierPoints ();
503		bezier_points[0].type == 'L';
504		bezier_points[0].x0 = x;
505		bezier_points[0].y0 = y;
506
507		g = MainWindow.get_current_glyph ();
508		move_and_resize (bezier_points, 1, false, 1, g);
509
510		p = CircleTool.create_circle (bezier_points[0].x0,
511			bezier_points[0].y0, r, PointType.CUBIC);
512
513		npl.add (p);
514
515		foreach (Attribute attr in tag.get_attributes ()) {
516			if (attr.get_name () == "transform") {
517				transform_paths (attr.get_content (), npl);
518			}
519		}
520
521		style.apply (npl);
522		pl.paths.append (npl);
523	}
524
525	private void parse_ellipse (Tag tag, Layer pl) {
526		Path p;
527		double x, y, rx, ry;
528		Glyph g;
529		PathList npl;
530		BezierPoints[] bezier_points;
531		SvgStyle style = new SvgStyle ();
532		bool hidden = false;
533
534		npl = new PathList ();
535
536		x = 0;
537		y = 0;
538		rx = 0;
539		ry = 0;
540
541		foreach (Attribute attr in tag.get_attributes ()) {
542			if (attr.get_name () == "cx") {
543				x = parse_double (attr.get_content ());
544			}
545
546			if (attr.get_name () == "cy") {
547				y = -parse_double (attr.get_content ());
548			}
549
550			if (attr.get_name () == "rx") {
551				rx = parse_double (attr.get_content ());
552			}
553
554			if (attr.get_name () == "ry") {
555				ry = parse_double (attr.get_content ());
556			}
557
558			if (attr.get_name () == "display" && attr.get_content () == "none") {
559				hidden = true;
560			}
561		}
562
563		style = SvgStyle.parse (tag.get_attributes ());
564
565		if (hidden) {
566			return;
567		}
568
569		bezier_points = new BezierPoints[1];
570		bezier_points[0] = new BezierPoints ();
571		bezier_points[0].type == 'L';
572		bezier_points[0].x0 = x;
573		bezier_points[0].y0 = y;
574
575		g = MainWindow.get_current_glyph ();
576		move_and_resize (bezier_points, 1, false, 1, g);
577
578		p = CircleTool.create_ellipse (bezier_points[0].x0,
579			bezier_points[0].y0, rx, ry, PointType.CUBIC);
580
581		npl.add (p);
582
583		foreach (Attribute attr in tag.get_attributes ()) {
584			if (attr.get_name () == "transform") {
585				transform_paths (attr.get_content (), npl);
586			}
587		}
588
589		style.apply (npl);
590		pl.paths.append (npl);
591	}
592
593	private void parse_line (Tag tag, Layer pl) {
594		Path p;
595		double x1, y1, x2, y2;
596		BezierPoints[] bezier_points;
597		Glyph g;
598		PathList npl = new PathList ();
599		SvgStyle style = new SvgStyle ();
600		bool hidden = false;
601
602		x1 = 0;
603		y1 = 0;
604		x2 = 0;
605		y2 = 0;
606
607		foreach (Attribute attr in tag.get_attributes ()) {
608			if (attr.get_name () == "x1") {
609				x1 = parse_double (attr.get_content ());
610			}
611
612			if (attr.get_name () == "y1") {
613				y1 = -parse_double (attr.get_content ());
614			}
615
616			if (attr.get_name () == "x2") {
617				x2 = parse_double (attr.get_content ());
618			}
619
620			if (attr.get_name () == "xy") {
621				y2 = -parse_double (attr.get_content ());
622			}
623
624			if (attr.get_name () == "display" && attr.get_content () == "none") {
625				hidden = true;
626			}
627		}
628
629		style = SvgStyle.parse (tag.get_attributes ());
630
631		if (hidden) {
632			return;
633		}
634
635		bezier_points = new BezierPoints[2];
636		bezier_points[0] = new BezierPoints ();
637		bezier_points[0].type == 'L';
638		bezier_points[0].x0 = x1;
639		bezier_points[0].y0 = y1;
640
641		bezier_points[1] = new BezierPoints ();
642		bezier_points[1].type == 'L';
643		bezier_points[1].x0 = x2;
644		bezier_points[1].y0 = y2;
645
646		g = MainWindow.get_current_glyph ();
647		move_and_resize (bezier_points, 2, false, 1, g);
648
649		p = new Path ();
650
651		p.add (bezier_points[0].x0, bezier_points[0].y0);
652		p.add (bezier_points[1].x0, bezier_points[1].y0);
653
654		p.close ();
655		p.create_list ();
656		p.recalculate_linear_handles ();
657
658		npl.add (p);
659
660		foreach (Attribute attr in tag.get_attributes ()) {
661			if (attr.get_name () == "transform") {
662				transform_paths (attr.get_content (), npl);
663			}
664		}
665
666		style.apply (npl);
667		pl.paths.append (npl);
668	}
669
670	private void parse_rect (Tag tag, Layer pl) {
671		Path p;
672		double x, y, x2, y2;
673		BezierPoints[] bezier_points;
674		Glyph g;
675		PathList npl = new PathList ();
676		SvgStyle style = new SvgStyle ();
677		bool hidden = false;
678		EditPoint ep;
679
680		x = 0;
681		y = 0;
682		x2 = 0;
683		y2 = 0;
684
685		foreach (Attribute attr in tag.get_attributes ()) {
686			if (attr.get_name () == "x") {
687				x = parse_double (attr.get_content ());
688			}
689
690			if (attr.get_name () == "y") {
691				y = -parse_double (attr.get_content ());
692			}
693
694			if (attr.get_name () == "width") {
695				x2 = parse_double (attr.get_content ());
696			}
697
698			if (attr.get_name () == "height") {
699				y2 = -parse_double (attr.get_content ());
700			}
701
702			if (attr.get_name () == "display" && attr.get_content () == "none") {
703				hidden = true;
704			}
705		}
706
707		style = SvgStyle.parse (tag.get_attributes ());
708
709		if (hidden) {
710			return;
711		}
712
713		x2 += x;
714		y2 += y;
715
716		bezier_points = new BezierPoints[4];
717		bezier_points[0] = new BezierPoints ();
718		bezier_points[0].type == 'L';
719		bezier_points[0].x0 = x;
720		bezier_points[0].y0 = y;
721
722		bezier_points[1] = new BezierPoints ();
723		bezier_points[1].type == 'L';
724		bezier_points[1].x0 = x2;
725		bezier_points[1].y0 = y;
726
727		bezier_points[2] = new BezierPoints ();
728		bezier_points[2].type == 'L';
729		bezier_points[2].x0 = x2;
730		bezier_points[2].y0 = y2;
731
732		bezier_points[3] = new BezierPoints ();
733		bezier_points[3].type == 'L';
734		bezier_points[3].x0 = x;
735		bezier_points[3].y0 = y2;
736
737		g = MainWindow.get_current_glyph ();
738		move_and_resize (bezier_points, 4, false, 1, g);
739
740		p = new Path ();
741
742		ep = p.add (bezier_points[0].x0, bezier_points[0].y0);
743		ep.set_point_type (PointType.CUBIC);
744
745		ep = p.add (bezier_points[1].x0, bezier_points[1].y0);
746		ep.set_point_type (PointType.CUBIC);
747
748		ep = p.add (bezier_points[2].x0, bezier_points[2].y0);
749		ep.set_point_type (PointType.CUBIC);
750
751		ep = p.add (bezier_points[3].x0, bezier_points[3].y0);
752		ep.set_point_type (PointType.CUBIC);
753
754		p.close ();
755		p.create_list ();
756		p.recalculate_linear_handles ();
757
758		npl.add (p);
759
760		// FIXME: right layer for other transforms
761		foreach (Attribute attr in tag.get_attributes ()) {
762			if (attr.get_name () == "transform") {
763				transform_paths (attr.get_content (), npl);
764			}
765		}
766
767		style.apply (npl);
768		pl.paths.append (npl);
769	}
770
771	private void parse_polygon (Tag tag, Layer pl) {
772		PathList path_list = get_polyline (tag);
773
774		foreach (Path p in path_list.paths) {
775			p.close ();
776		}
777
778		pl.paths.append (path_list);
779	}
780
781
782	private void parse_polyline (Tag tag, Layer pl) {
783		pl.paths.append (get_polyline (tag));
784	}
785
786	private PathList get_polyline (Tag tag) {
787		Path p = new Path ();
788		bool hidden = false;
789		PathList path_list = new PathList ();
790		SvgStyle style = new SvgStyle ();
791
792		foreach (Attribute attr in tag.get_attributes ()) {
793			if (attr.get_name () == "points") {
794				p = parse_poly_data (attr.get_content ());
795			}
796
797			if (attr.get_name () == "display" && attr.get_content () == "none") {
798				hidden = true;
799			}
800		}
801
802		style = SvgStyle.parse (tag.get_attributes ());
803
804		if (hidden) {
805			return path_list;
806		}
807
808		path_list.add (p);
809		style.apply (path_list);
810
811		foreach (Attribute attr in tag.get_attributes ()) {
812			if (attr.get_name () == "transform") {
813				transform_paths (attr.get_content (), path_list);
814			}
815		}
816
817		return path_list;
818	}
819
820	private void parse_path (Tag tag, Layer pl) {
821		Glyph glyph = MainWindow.get_current_glyph ();
822		PathList path_list = new PathList ();
823		SvgStyle style = new SvgStyle ();
824		bool hidden = false;
825
826		foreach (Attribute attr in tag.get_attributes ()) {
827			if (attr.get_name () == "d") {
828				path_list = parse_svg_data (attr.get_content (), glyph);
829			}
830
831			if (attr.get_name () == "display" && attr.get_content () == "none") {
832				hidden = true;
833			}
834
835			if (attr.get_name () == "visibility"
836				&& (attr.get_content () == "hidden"
837					|| attr.get_content () == "collapse")) {
838				hidden = true;
839			}
840		}
841
842		style = SvgStyle.parse (tag.get_attributes ());
843
844		if (hidden) {
845			return;
846		}
847
848		pl.paths.append (path_list);
849		style.apply (path_list);
850
851		// assume the even odd rule is applied and convert the path
852		// to a path using the non-zero rule
853		int inside_count;
854		bool inside;
855		foreach (Path p1 in pl.paths.paths) {
856			inside_count = 0;
857
858			foreach (Path p2 in pl.paths.paths) {
859				if (p1 != p2) {
860					inside = true;
861
862					foreach (EditPoint ep in p1.points) {
863						if (!is_inside (ep, p2)) {
864							inside = false;
865						}
866					}
867
868					if (inside) {
869						inside_count++;
870					}
871				}
872			}
873
874			if (inside_count % 2 == 0) {
875				p1.force_direction (Direction.CLOCKWISE);
876			} else {
877				p1.force_direction (Direction.COUNTER_CLOCKWISE);
878			}
879		}
880
881		foreach (Attribute attr in tag.get_attributes ()) {
882			if (attr.get_name () == "transform") {
883				transform_paths (attr.get_content (), path_list);
884			}
885		}
886	}
887
888	public static void create_lines_for_segment (Path path, EditPoint start, EditPoint end, double tolerance) {
889		double x1, x2, x3;
890		double y1, y2, y3;
891		double step_start, step, step_end;
892
893		path.add (start.x, start.y);
894
895		step_start = 0;
896		step = 0.5;
897		step_end = 1;
898
899		while (true) {
900			Path.get_point_for_step (start, end, step_start, out x1, out y1);
901			Path.get_point_for_step (start, end, step, out x2, out y2);
902			Path.get_point_for_step (start, end, step_end, out x3, out y3);
903
904			if (!StrokeTool.is_flat (x1, y1, x2, y2, x3, y3, tolerance)
905				&& step_end - step / 2.0 > step_start
906				&& step_end - step / 2.0 > 0.1
907				&& step > 0.05
908				&& Path.distance_to_point (start, end) > 1) {
909
910				step /= 2.0;
911
912				if (step < 0.05) {
913					step = 0.05;
914				} else {
915					step_end = step_start + 2 * step;
916				}
917			} else {
918				path.add (x3, y3);
919
920				if (step_end + step < 1) {
921					step_start = step_end;
922					step_end += step;
923				} else {
924					break;
925				}
926			}
927		}
928	}
929
930	public static Path get_lines (Path p) {
931		EditPoint start;
932		Path path = new Path ();
933
934		if (p.points.size == 0) {
935			return path;
936		}
937
938		// create a set of straight lines
939		start = p.points.get (p.points.size - 1);
940
941		foreach (EditPoint end in p.points) {
942			create_lines_for_segment (path, start, end, 1);
943			start = end;
944		}
945
946		return path;
947	}
948
949	/** Check if a point is inside using the even odd fill rule.
950	 * The path should only have straight lines.
951	 */
952	public static bool is_inside (EditPoint point, Path path) {
953		EditPoint prev;
954		bool inside = false;
955
956		if (path.points.size <= 1) {
957			return false;
958		}
959
960		if (!(path.xmin <= point.x <= path.xmax)) {
961			return false;
962		}
963
964		if (!(path.ymin <= point.y <= path.ymax)) {
965			return false;
966		}
967
968		prev = path.points.get (path.points.size - 1);
969
970		foreach (EditPoint p in path.points) {
971			if  ((p.y > point.y) != (prev.y > point.y)
972				&& point.x < (prev.x - p.x) * (point.y - p.y) / (prev.y - p.y) + p.x) {
973				inside = !inside;
974			}
975
976			prev = p;
977		}
978
979		return inside;
980	}
981
982	/** Add space as separator to svg data.
983	 * @param d svg data
984	 */
985	static string add_separators (string d) {
986		string data = d;
987
988		data = data.replace (",", " ");
989		data = data.replace ("a", " a ");
990		data = data.replace ("A", " A ");
991		data = data.replace ("m", " m ");
992		data = data.replace ("M", " M ");
993		data = data.replace ("h", " h ");
994		data = data.replace ("H", " H ");
995		data = data.replace ("v", " v ");
996		data = data.replace ("V", " V ");
997		data = data.replace ("l", " l ");
998		data = data.replace ("L", " L ");
999		data = data.replace ("q", " q ");
1000		data = data.replace ("Q", " Q ");
1001		data = data.replace ("c", " c ");
1002		data = data.replace ("C", " C ");
1003		data = data.replace ("t", " t ");
1004		data = data.replace ("T", " T ");
1005		data = data.replace ("s", " s ");
1006		data = data.replace ("S", " S ");
1007		data = data.replace ("zM", " z M ");
1008		data = data.replace ("zm", " z m ");
1009		data = data.replace ("z", " z ");
1010		data = data.replace ("Z", " Z ");
1011		data = data.replace ("-", " -");
1012		data = data.replace ("e -", "e-"); // minus can be either separator or a negative exponent
1013		data = data.replace ("\t", " ");
1014		data = data.replace ("\r\n", " ");
1015		data = data.replace ("\n", " ");
1016		data = data.replace ("+", " ");
1017
1018		// use only a single space as separator
1019		while (data.index_of ("  ") > -1) {
1020			data = data.replace ("  ", " ");
1021		}
1022
1023		return data;
1024	}
1025
1026	public void add_path_to_glyph (string d, Glyph g, bool svg_glyph = false, double units = 1) {
1027		PathList p = parse_svg_data (d, g, svg_glyph, units);
1028		foreach (Path path in p.paths) {
1029			g.add_path (path);
1030		}
1031	}
1032
1033	/**
1034	 * @param d svg data
1035	 * @param glyph use lines from this glyph but don't add the generated paths
1036	 * @param svg_glyph parse svg glyph with origo in lower left corner
1037	 *
1038	 * @return the new paths
1039	 */
1040	public PathList parse_svg_data (string d, Glyph glyph, bool svg_glyph = false, double units = 1) {
1041		double px = 0;
1042		double py = 0;
1043		double px2 = 0;
1044		double py2 = 0;
1045		double cx = 0;
1046		double cy = 0;
1047		string data;
1048		Font font;
1049		PathList path_list = new PathList ();
1050		BezierPoints[] bezier_points;
1051		string[] c;
1052		double arc_rx, arc_ry;
1053		double arc_rotation;
1054		int large_arc;
1055		int arc_sweep;
1056		double arc_dest_x, arc_dest_y;
1057		double first_point_x = 0;
1058		double first_point_y = 0;
1059		bool set_first = true;
1060
1061		font = BirdFont.get_current_font ();
1062
1063		data = add_separators (d);
1064		c = data.split (" ");
1065		bezier_points = new BezierPoints[8 * c.length + 2]; // the arc instruction can use up to eight points
1066
1067		for (int i = 0; i < 2 * c.length + 1; i++) {
1068			bezier_points[i] = new BezierPoints ();
1069		}
1070
1071		int bi = 0;
1072
1073		// parse path
1074		int i = -1;
1075		while (++i < c.length && bi < bezier_points.length) {
1076			if (c[i] == "m") {
1077				while (i + 2 < c.length && is_point (c[i + 1])) {
1078					bezier_points[bi].type = 'M';
1079					bezier_points[bi].svg_type = 'm';
1080
1081					px += parse_double (c[++i]);
1082
1083					if (svg_glyph) {
1084						py += parse_double (c[++i]);
1085					} else {
1086						py += -parse_double (c[++i]);
1087					}
1088
1089					bezier_points[bi].x0 = px;
1090					bezier_points[bi].y0 = py;
1091
1092					first_point_x = px;
1093					first_point_y = py;
1094					set_first = false;
1095
1096					bi++;
1097				}
1098			} else if (c[i] == "M") {
1099				while (i + 2 < c.length && is_point (c[i + 1])) {
1100					bezier_points[bi].type = 'M';
1101					bezier_points[bi].svg_type = 'M';
1102
1103					px = parse_double (c[++i]);
1104
1105					if (svg_glyph) {
1106						py = parse_double (c[++i]);
1107					} else {
1108						py = -parse_double (c[++i]);
1109					}
1110
1111					bezier_points[bi].x0 = px;
1112					bezier_points[bi].y0 = py;
1113
1114					first_point_x = px;
1115					first_point_y = py;
1116					set_first = false;
1117
1118					bi++;
1119				}
1120			} else if (c[i] == "h") {
1121				while (i + 1 < c.length && is_point (c[i + 1])) {
1122					bezier_points[bi].type = 'L';
1123					bezier_points[bi].svg_type = 'h';
1124
1125					px += parse_double (c[++i]);
1126
1127					bezier_points[bi].x0 = px;
1128					bezier_points[bi].y0 = py;
1129					bi++;
1130				}
1131			} else if (c[i] == "H") {
1132				while (i + 1 < c.length && is_point (c[i + 1])) {
1133					bezier_points[bi].type = 'L';
1134					bezier_points[bi].svg_type = 'H';
1135
1136					px = parse_double (c[++i]);
1137
1138					bezier_points[bi].x0 = px;
1139					bezier_points[bi].y0 = py;
1140					bi++;
1141				}
1142			} else if (c[i] == "v") {
1143				while (i + 1 < c.length && is_point (c[i + 1])) {
1144					bezier_points[bi].type = 'L';
1145					bezier_points[bi].svg_type = 'v';
1146
1147					if (svg_glyph) {
1148						py = py + parse_double (c[++i]);
1149					} else {
1150						py = py - parse_double (c[++i]);
1151					}
1152
1153					bezier_points[bi].x0 = px;
1154					bezier_points[bi].y0 = py;
1155					bi++;
1156				}
1157			} else if (i + 1 < c.length && c[i] == "V") {
1158				while (is_point (c[i + 1])) {
1159					bezier_points[bi].type = 'L';
1160					bezier_points[bi].svg_type = 'V';
1161
1162					if (svg_glyph) {
1163						py = parse_double (c[++i]);
1164					} else {
1165						py = -parse_double (c[++i]);
1166					}
1167
1168					bezier_points[bi].x0 = px;
1169					bezier_points[bi].y0 = py;
1170					bi++;
1171				}
1172			} else if (c[i] == "l") {
1173				while (i + 2 < c.length && is_point (c[i + 1])) {
1174					bezier_points[bi].type = 'L';
1175					bezier_points[bi].svg_type = 'l';
1176
1177					cx = px + parse_double (c[++i]);
1178
1179					if (svg_glyph) {
1180						cy = py + parse_double (c[++i]);
1181					} else {
1182						cy = py - parse_double (c[++i]);
1183					}
1184
1185					px = cx;
1186					py = cy;
1187
1188					bezier_points[bi].x0 = cx;
1189					bezier_points[bi].y0 = cy;
1190					bi++;
1191				}
1192			} else if (c[i] == "L") {
1193				while (i + 2 < c.length && is_point (c[i + 1])) {
1194					bezier_points[bi].type = 'L';
1195					bezier_points[bi].svg_type = 'L';
1196
1197					cx = parse_double (c[++i]);
1198
1199					if (svg_glyph) {
1200						cy = parse_double (c[++i]);
1201					} else {
1202						cy = -parse_double (c[++i]);
1203					}
1204
1205					px = cx;
1206					py = cy;
1207
1208					bezier_points[bi].x0 = cx;
1209					bezier_points[bi].y0 = cy;
1210					bi++;
1211				}
1212			} else if (c[i] == "c") {
1213				while (i + 6 < c.length && is_point (c[i + 1])) {
1214					bezier_points[bi].type = 'C';
1215					bezier_points[bi].svg_type = 'C';
1216
1217					cx = px + parse_double (c[++i]);
1218
1219					if (svg_glyph) {
1220						cy = py + parse_double (c[++i]);
1221					} else {
1222						cy = py - parse_double (c[++i]);
1223					}
1224
1225					bezier_points[bi].x0 = cx;
1226					bezier_points[bi].y0 = cy;
1227
1228					cx = px + parse_double (c[++i]);
1229
1230					if (svg_glyph) {
1231						cy = py + parse_double (c[++i]);
1232					} else {
1233						cy = py - parse_double (c[++i]);
1234					}
1235
1236					px2 = cx;
1237					py2 = cy;
1238
1239					bezier_points[bi].x1 = px2;
1240					bezier_points[bi].y1 = py2;
1241
1242					cx = px + parse_double (c[++i]);
1243
1244					if (svg_glyph) {
1245						cy = py + parse_double (c[++i]);
1246					} else {
1247						cy = py + -parse_double (c[++i]);
1248					}
1249
1250					bezier_points[bi].x2 = cx;
1251					bezier_points[bi].y2 = cy;
1252
1253					px = cx;
1254					py = cy;
1255
1256					bi++;
1257				}
1258			} else if (c[i] == "C") {
1259				while (i + 6 < c.length && is_point (c[i + 1])) {
1260					bezier_points[bi].type = 'C';
1261					bezier_points[bi].svg_type = 'C';
1262
1263					cx = parse_double (c[++i]);
1264
1265					if (svg_glyph) {
1266						cy = parse_double (c[++i]);
1267					} else {
1268						cy = -parse_double (c[++i]);
1269					}
1270
1271					bezier_points[bi].x0 = cx;
1272					bezier_points[bi].y0 = cy;
1273
1274					cx = parse_double (c[++i]);
1275
1276					if (svg_glyph) {
1277						cy = parse_double (c[++i]);
1278					} else {
1279						cy = -parse_double (c[++i]);
1280					}
1281
1282					px2 = cx;
1283					py2 = cy;
1284
1285					bezier_points[bi].x1 = cx;
1286					bezier_points[bi].y1 = cy;
1287
1288					cx = parse_double (c[++i]);
1289
1290					if (svg_glyph) {
1291						cy = parse_double (c[++i]);
1292					} else {
1293						cy = -parse_double (c[++i]);
1294					}
1295
1296					bezier_points[bi].x2 = cx;
1297					bezier_points[bi].y2 = cy;
1298
1299					px = cx;
1300					py = cy;
1301
1302					bi++;
1303				}
1304			} else if (c[i] == "q") {
1305				while (i + 4 < c.length && is_point (c[i + 1])) {
1306					bezier_points[bi].type = 'Q';
1307					bezier_points[bi].svg_type = 'q';
1308
1309					cx = px + parse_double (c[++i]);
1310
1311					if (svg_glyph) {
1312						cy = py + parse_double (c[++i]);
1313					} else {
1314						cy = py - parse_double (c[++i]);
1315					}
1316
1317					bezier_points[bi].x0 = cx;
1318					bezier_points[bi].y0 = cy;
1319
1320					px2 = cx;
1321					py2 = cy;
1322
1323					cx = px + parse_double (c[++i]);
1324
1325					if (svg_glyph) {
1326						cy = py + parse_double (c[++i]);
1327					} else {
1328						cy = py - parse_double (c[++i]);
1329					}
1330
1331					bezier_points[bi].x1 = cx;
1332					bezier_points[bi].y1 = cy;
1333
1334					px = cx;
1335					py = cy;
1336
1337					bi++;
1338				}
1339			} else if (c[i] == "Q") {
1340
1341				while (i + 4 < c.length && is_point (c[i + 1])) {
1342					bezier_points[bi].type = 'Q';
1343					bezier_points[bi].svg_type = 'Q';
1344
1345					cx = parse_double (c[++i]);
1346
1347					if (svg_glyph) {
1348						cy = parse_double (c[++i]);
1349					} else {
1350						cy = -parse_double (c[++i]);
1351					}
1352
1353					bezier_points[bi].x0 = cx;
1354					bezier_points[bi].y0 = cy;
1355
1356					px2 = cx;
1357					py2 = cy;
1358
1359					cx = parse_double (c[++i]);
1360
1361					if (svg_glyph) {
1362						cy = parse_double (c[++i]);
1363					} else {
1364						cy = -parse_double (c[++i]);
1365					}
1366
1367					px = cx;
1368					py = cy;
1369
1370					bezier_points[bi].x1 = cx;
1371					bezier_points[bi].y1 = cy;
1372
1373					bi++;
1374				}
1375			} else if (c[i] == "t") {
1376				while (i + 2 < c.length && is_point (c[i + 1])) {
1377					bezier_points[bi].type = 'Q';
1378					bezier_points[bi].svg_type = 't';
1379
1380					// the first point is the reflection
1381					cx = 2 * px - px2;
1382					cy = 2 * py - py2; // if (svg_glyph) ?
1383
1384					bezier_points[bi].x0 = cx;
1385					bezier_points[bi].y0 = cy;
1386
1387					px2 = cx;
1388					py2 = cy;
1389
1390					cx = px + parse_double (c[++i]);
1391
1392					if (svg_glyph) {
1393						cy = py + parse_double (c[++i]);
1394					} else {
1395						cy = py - parse_double (c[++i]);
1396					}
1397
1398					px = cx;
1399					py = cy;
1400
1401					bezier_points[bi].x1 = px;
1402					bezier_points[bi].y1 = py;
1403
1404					bi++;
1405				}
1406			} else if (c[i] == "T") {
1407				while (i + 2 < c.length && is_point (c[i + 1])) {
1408					bezier_points[bi].type = 'Q';
1409					bezier_points[bi].svg_type = 'T';
1410
1411					// the reflection
1412					cx = 2 * px - px2;
1413					cy = 2 * py - py2; // if (svg_glyph) ?
1414
1415					bezier_points[bi].x0 = cx;
1416					bezier_points[bi].y0 = cy;
1417
1418					px2 = cx;
1419					py2 = cy;
1420
1421					cx = parse_double (c[++i]);
1422
1423					if (svg_glyph) {
1424						cy = parse_double (c[++i]);
1425					} else {
1426						cy = -parse_double (c[++i]);
1427					}
1428
1429					px = cx;
1430					py = cy;
1431
1432					bezier_points[bi].x1 = px;
1433					bezier_points[bi].y1 = py;
1434
1435					bi++;
1436				}
1437			} else if (c[i] == "s") {
1438				while (i + 4 < c.length && is_point (c[i + 1])) {
1439					bezier_points[bi].type = 'C';
1440					bezier_points[bi].svg_type = 's';
1441
1442					// the first point is the reflection
1443					cx = 2 * px - px2;
1444					cy = 2 * py - py2; // if (svg_glyph) ?
1445
1446					bezier_points[bi].x0 = cx;
1447					bezier_points[bi].y0 = cy;
1448
1449					cx = px + parse_double (c[++i]);
1450
1451					if (svg_glyph) {
1452						cy = py + parse_double (c[++i]);
1453					} else {
1454						cy = py - parse_double (c[++i]);
1455					}
1456
1457					px2 = cx;
1458					py2 = cy;
1459
1460					bezier_points[bi].x1 = px2;
1461					bezier_points[bi].y1 = py2;
1462
1463					cx = px + parse_double (c[++i]);
1464
1465					if (svg_glyph) {
1466						cy = py + parse_double (c[++i]);
1467					} else {
1468						cy = py - parse_double (c[++i]);
1469					}
1470
1471					bezier_points[bi].x2 = cx;
1472					bezier_points[bi].y2 = cy;
1473
1474					px = cx;
1475					py = cy;
1476
1477					bi++;
1478				}
1479			} else if (c[i] == "S") {
1480				while (i + 4 < c.length && is_point (c[i + 1])) {
1481					bezier_points[bi].type = 'C';
1482					bezier_points[bi].svg_type = 'S';
1483
1484					// the reflection
1485					cx = 2 * px - px2;
1486					cy = 2 * py - py2; // if (svg_glyph) ?
1487
1488					bezier_points[bi].x0 = cx;
1489					bezier_points[bi].y0 = cy;
1490
1491					// the other two are regular cubic points
1492					cx = parse_double (c[++i]);
1493
1494					if (svg_glyph) {
1495						cy = parse_double (c[++i]);
1496					} else {
1497						cy = -parse_double (c[++i]);
1498					}
1499
1500					px2 = cx;
1501					py2 = cy;
1502
1503					bezier_points[bi].x1 = px2;
1504					bezier_points[bi].y1 = py2;
1505
1506					cx = parse_double (c[++i]);
1507
1508					if (svg_glyph) {
1509						cy = parse_double (c[++i]);
1510					} else {
1511						cy = -parse_double (c[++i]);
1512					}
1513
1514					bezier_points[bi].x2 = cx;
1515					bezier_points[bi].y2 = cy;
1516
1517					px = cx;
1518					py = cy;
1519
1520					bi++;
1521				}
1522			} else if (c[i] == "a") {
1523				while (i + 7 < c.length && is_point (c[i + 1])) {
1524					arc_rx = parse_double (c[++i]);
1525					arc_ry = parse_double (c[++i]);
1526
1527					arc_rotation = parse_double (c[++i]);
1528					large_arc = parse_int (c[++i]);
1529					arc_sweep = parse_int (c[++i]);
1530
1531					cx = px + parse_double (c[++i]);
1532
1533					if (svg_glyph) {
1534						cy = py + parse_double (c[++i]);
1535					} else {
1536						cy = py - parse_double (c[++i]);
1537					}
1538
1539					arc_dest_x = cx;
1540					arc_dest_y = cy;
1541
1542					add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy);
1543
1544					px = cx;
1545					py = cy;
1546				}
1547			} else if (i + 7 < c.length && c[i] == "A") {
1548				while (is_point (c[i + 1])) {
1549					arc_rx = parse_double (c[++i]);
1550					arc_ry = parse_double (c[++i]);
1551
1552					arc_rotation = parse_double (c[++i]);
1553					large_arc = parse_int (c[++i]);
1554					arc_sweep = parse_int (c[++i]);
1555
1556					cx = parse_double (c[++i]);
1557
1558					if (svg_glyph) {
1559						cy = parse_double (c[++i]);
1560					} else {
1561						cy = -parse_double (c[++i]);
1562					}
1563
1564					arc_dest_x = cx;
1565					arc_dest_y = cy;
1566
1567					add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy);
1568
1569					px = cx;
1570					py = cy;
1571
1572
1573				}
1574			} else if (c[i] == "z" || c[i] == "Z") {
1575				if (Path.distance (px, first_point_x, py, first_point_y) > 0.001) {
1576
1577					px = first_point_x;
1578					py = first_point_y;
1579
1580					bezier_points[bi].type = 'L';
1581					bezier_points[bi].svg_type = 'l';
1582					bezier_points[bi].x0 = px;
1583					bezier_points[bi].y0 = py;
1584
1585					bi++;
1586				}
1587
1588				bezier_points[bi].type = 'z';
1589				bezier_points[bi].svg_type = 'z';
1590
1591				set_first = true;
1592				bi++;
1593			} else if (c[i] == "") {
1594			} else if (c[i] == " ") {
1595			} else {
1596
1597				print (@"\n\nSVG:");
1598				for (int dd = 0; dd <= i; dd++) {
1599					print (@"$(c[dd]) ");
1600				}
1601				print (@"\n");
1602
1603				warning (@"Unknown instruction: $(c[i]), i: $(i)");
1604			}
1605		}
1606
1607		if (bi == 0) {
1608			warning ("No points in path.");
1609			return path_list;
1610		}
1611
1612		move_and_resize (bezier_points, bi, svg_glyph, units, glyph);
1613
1614		if (format == SvgFormat.ILLUSTRATOR) {
1615			path_list = create_paths_illustrator (bezier_points, bi);
1616		} else {
1617			path_list = create_paths_inkscape (bezier_points, bi);
1618		}
1619
1620		// TODO: Find out if it is possible to tie handles.
1621		return path_list;
1622	}
1623
1624	void move_and_resize (BezierPoints[] b, int num_b, bool svg_glyph, double units, Glyph glyph) {
1625		Font font = BirdFont.get_current_font ();
1626
1627		for (int i = 0; i < num_b; i++) {
1628			// resize all points
1629			b[i].x0 *= units;
1630			b[i].y0 *= units;
1631			b[i].x1 *= units;
1632			b[i].y1 *= units;
1633			b[i].x2 *= units;
1634			b[i].y2 *= units;
1635
1636			// move all points
1637			if (svg_glyph) {
1638				b[i].x0 += glyph.left_limit;
1639				b[i].y0 += font.base_line;
1640				b[i].x1 += glyph.left_limit;
1641				b[i].y1 += font.base_line;
1642				b[i].x2 += glyph.left_limit;
1643				b[i].y2 += font.base_line;
1644			} else {
1645				b[i].x0 += glyph.left_limit;
1646				b[i].y0 += font.top_limit;
1647				b[i].x1 += glyph.left_limit;
1648				b[i].y1 += font.top_limit;
1649				b[i].x2 += glyph.left_limit;
1650				b[i].y2 += font.top_limit;
1651			}
1652		}
1653	}
1654
1655	void find_last_handle (int start_index, BezierPoints[] b, int num_b, out double left_x, out double left_y, out PointType last_type) {
1656		BezierPoints last = new BezierPoints ();
1657		bool found = false;
1658
1659		left_x = 0;
1660		left_y = 0;
1661		last_type = PointType.NONE;
1662
1663		return_if_fail (b.length != 0);
1664		return_if_fail (b[0].type != 'z');
1665		return_if_fail (num_b < b.length);
1666
1667		if (num_b == 2) {
1668			left_x = b[0].x0 + (b[1].x0 - b[0].x0) / 3.0;
1669			left_y = b[0].y0 + (b[1].y0 - b[0].y0) / 3.0;
1670			last_type = PointType.LINE_CUBIC;
1671			return;
1672		}
1673
1674		for (int i = start_index; i < num_b; i++) {
1675			switch (b[i].type) {
1676				case 'Q':
1677					break;
1678				case 'C':
1679					break;
1680				case 'z':
1681					found = true;
1682					break;
1683				default:
1684					break;
1685			}
1686
1687			if (found || i + 1 == num_b) {
1688
1689				return_if_fail (i >= 1);
1690
1691				if (b[i - 1].type == 'Q') {
1692					return_if_fail (i >= 1);
1693					left_x = b[i - 1].x0;
1694					left_y = b[i - 1].y0;
1695					last_type = PointType.QUADRATIC;
1696				} else if (b[i - 1].type == 'C') {
1697					return_if_fail (i >= 1);
1698					left_x = b[i - 1].x1;
1699					left_y = b[i - 1].y1;
1700					last_type = PointType.CUBIC;
1701				} else if (b[i - 1].type == 'S') {
1702					return_if_fail (i >= 1);
1703					left_x = b[i - 1].x1;
1704					left_y = b[i - 1].y1;
1705					last_type = PointType.CUBIC;
1706				} else if (b[i - 1].type == 'L' || last.type == 'M') {
1707					return_if_fail (i >= 2); // FIXME: -2 can be C or L
1708					left_x = b[i - 2].x0 + (b[i - 1].x0 - b[i - 2].x0) / 3.0;
1709					left_y = b[i - 2].y0 + (b[i - 1].y0 - b[i - 2].y0) / 3.0;
1710					last_type = PointType.LINE_CUBIC;
1711				} else {
1712					warning (@"Unexpected type. $(b[i - 1])\n");
1713				}
1714				return;
1715			}
1716
1717			last = b[i];
1718		}
1719
1720		warning ("Last point not found.");
1721	}
1722
1723	PathList create_paths_inkscape (BezierPoints[] b, int num_b) {
1724		double last_x;
1725		double last_y;
1726		PointType last_type;
1727		Path path;
1728		PathList path_list = new PathList ();
1729		EditPoint ep = new EditPoint ();
1730		Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> ();
1731
1732		path = new Path ();
1733
1734		if (num_b == 0) {
1735			warning ("No SVG data");
1736			return path_list;
1737		}
1738
1739		if (b[0].type != 'M') {
1740			warning ("Path must begin with M or m. Not $(b[0].type	)");
1741			return path_list;
1742		}
1743
1744		find_last_handle (0, b, num_b, out last_x, out last_y, out last_type);
1745
1746		for (int i = 0; i < num_b; i++) {
1747			if (b[i].type == '\0') {
1748				warning ("Parser error.");
1749				return path_list;
1750			}
1751
1752			if (b[i].type == 'z') {
1753				path.close ();
1754				path.create_list ();
1755				path.recalculate_linear_handles ();
1756				path_list.add (path);
1757				path = new Path ();
1758
1759				if (i + 1 >= num_b) {
1760					break;
1761				} else {
1762					find_last_handle (i + 1, b, num_b, out last_x, out last_y, out last_type);
1763				}
1764			}
1765
1766			if (i >= num_b) {
1767				break;
1768			}
1769
1770			if (b[i].type == 'M') {
1771				ep = path.add (b[i].x0, b[i].y0);
1772				ep.set_point_type (PointType.CUBIC);
1773
1774				ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
1775
1776				if (i == 0 || (b[i - 1].type == 'z')) {
1777					ep.get_left_handle ().set_point_type (last_type);
1778					ep.get_left_handle ().move_to_coordinate (last_x, last_y);
1779				} else {
1780					if (b[i - 1].type == 'C' || b[i - 1].type == 'S') {
1781						ep.get_left_handle ().set_point_type (PointType.CUBIC);
1782						ep.get_left_handle ().move_to_coordinate (b[i + 1].x1, b[i + 1].y1);
1783					}
1784
1785					if (b[i + 1].type == 'C' || b[i - 1].type == 'S') {
1786						ep.get_right_handle ().set_point_type (PointType.CUBIC);
1787						ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);
1788					} else if (b[i + 1].type == 'L' || b[i + 1].type == 'M') {
1789						ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1790					}
1791				}
1792			}
1793
1794			if (b[i].type == 'L') {
1795				return_val_if_fail (i != 0, path_list);
1796
1797				ep = path.add (b[i].x0, b[i].y0);
1798				ep.set_point_type (PointType.CUBIC);
1799				ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1800				ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
1801
1802				if (b[i + 1].type == 'L' || b[i + 1].type == 'M' || b[i + 1].type == 'z') {
1803					ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1804				}
1805
1806				if (b[i -1].type == 'L' || b[i - 1].type == 'M') {
1807					ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
1808				}
1809			}
1810
1811			if (b[i].type == 'Q') {
1812				return_val_if_fail (i != 0, path_list);
1813
1814				ep.set_point_type (PointType.QUADRATIC);
1815
1816				ep.get_right_handle ().set_point_type (PointType.QUADRATIC);
1817				ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0);
1818
1819				if (b[i + 1].type != 'z') {
1820					ep = path.add (b[i].x1, b[i].y1);
1821
1822					ep.get_left_handle ().set_point_type (PointType.QUADRATIC);
1823					ep.get_left_handle ().move_to_coordinate (b[i].x0, b[i].y0);
1824				}
1825			}
1826
1827			if (b[i].type == 'C' || b[i].type == 'S') {
1828				return_val_if_fail (i != 0, path_list);
1829
1830				ep.set_point_type (PointType.CUBIC);
1831
1832				ep.get_right_handle ().set_point_type (PointType.CUBIC);
1833				ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0);
1834
1835				if (b[i].type == 'S') {
1836					smooth_points.add (ep);
1837				}
1838
1839				if (b[i + 1].type != 'z') {
1840					ep = path.add (b[i].x2, b[i].y2);
1841
1842					ep.get_left_handle ().set_point_type (PointType.CUBIC);
1843					ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1);
1844				}
1845			}
1846		}
1847
1848		foreach (EditPoint e in smooth_points) {
1849			e.set_point_type (PointType.LINE_DOUBLE_CURVE);
1850			e.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);
1851			e.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);
1852		}
1853
1854		foreach (EditPoint e in smooth_points) {
1855			path.recalculate_linear_handles_for_point (e);
1856		}
1857
1858		for (int i = 0; i < 3; i++) {
1859			foreach (EditPoint e in smooth_points) {
1860				e.set_tie_handle (true);
1861				e.process_tied_handle ();
1862			}
1863		}
1864
1865		if (path.points.size > 0) {
1866			path_list.add (path);
1867		}
1868
1869		foreach (Path p in path_list.paths) {
1870			p.remove_points_on_points (0.2);
1871		}
1872
1873		return path_list;
1874	}
1875
1876	PathList create_paths_illustrator (BezierPoints[] b, int num_b) {
1877		Path path;
1878		PathList path_list = new PathList ();
1879		EditPoint ep;
1880		bool first_point = true;
1881		double first_left_x, first_left_y;
1882		Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> ();
1883
1884		if (num_b > b.length) {
1885			warning ("num_b > b.length: $num_b > $(b.length)");
1886			return path_list;
1887		}
1888
1889		path = new Path ();
1890
1891		if (num_b <= 1) {
1892			warning ("No SVG data");
1893			return path_list;
1894		}
1895
1896		first_left_x = 0;
1897		first_left_y = 0;
1898
1899		ep = new EditPoint ();
1900
1901		for (int i = 0; i < num_b; i++) {
1902			if (b[i].type == '\0') {
1903				warning ("Parser error.");
1904				return path_list;
1905			} else if (b[i].type == 'z') {
1906				path.close ();
1907				path.create_list ();
1908
1909				int first_index = 1;
1910
1911				for (int j = i - 1; j >= 1; j--) {
1912					if (b[j].type == 'z') {
1913						first_index = j + 1; // from z to M
1914					}
1915				}
1916
1917				if (b[first_index].type == 'C' || b[first_index].type == 'S') {
1918					return_val_if_fail (path.points.size != 0, path_list);
1919					ep = path.points.get (path.points.size - 1);
1920
1921					if (b[i - 1].type != 'L' ) {
1922						ep.get_right_handle ().set_point_type (PointType.CUBIC);
1923						ep.get_right_handle ().move_to_coordinate (b[first_index].x0, b[first_index].y0);
1924					}
1925				} else if (b[first_index].type == 'L') {
1926					return_val_if_fail (path.points.size != 0, path_list);
1927					ep = path.points.get (path.points.size - 1);
1928					ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1929					path.recalculate_linear_handles_for_point (ep);
1930				} else {
1931					warning ("Unexpected type: %s", (!) b[first_index].type.to_string ());
1932				}
1933
1934				path.recalculate_linear_handles ();
1935				path_list.add (path);
1936
1937				path = new Path ();
1938				first_point = true;
1939			} else if (b[i].type == 'L' || b[i].type == 'M') {
1940
1941				if (first_point) {
1942					first_left_x = b[i].x0;
1943					first_left_y = b[i].y0;
1944				}
1945
1946				ep = path.add (b[i].x0, b[i].y0);
1947				ep.set_point_type (PointType.CUBIC); // TODO: quadratic
1948				ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1949
1950				if (first_point || b[i -1].type == 'L') {
1951					// ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
1952				} else {
1953					ep.get_left_handle ().set_point_type (PointType.CUBIC);
1954				}
1955
1956				if (b[i + 1].type == 'C' || b[i + 1].type == 'S') {
1957					return_val_if_fail (i + 1 < num_b, path_list);
1958					ep.get_right_handle ().set_point_type (PointType.CUBIC);
1959					ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);
1960				}
1961
1962				first_point = false;
1963			} else if (b[i].type == 'Q') {
1964				warning ("Illustrator does not support quadratic control points.");
1965				warning (@"$(b[i])\n");
1966			} else if (b[i].type == 'C' || b[i].type == 'S') {
1967
1968				if (first_point) {
1969					first_left_x = b[i].x0;
1970					first_left_y = b[i].y0;
1971				}
1972
1973				ep = path.add (b[i].x2, b[i].y2);
1974				ep.set_point_type (PointType.CUBIC);
1975
1976				ep.get_right_handle ().set_point_type (PointType.CUBIC);
1977				ep.get_left_handle ().set_point_type (PointType.CUBIC);
1978
1979				ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1);
1980
1981				if (b[i].type == 'S') {
1982					smooth_points.add (ep);
1983				}
1984
1985				if (b[i + 1].type != 'z' && i != num_b - 1) {
1986					ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);
1987				} else {
1988					ep.get_right_handle ().move_to_coordinate (first_left_x, first_left_y);
1989				}
1990
1991				first_point = false;
1992			} else {
1993				warning ("Unknown control point type.");
1994				warning (@"$(b[i])\n");
1995			}
1996		}
1997
1998		foreach (EditPoint e in smooth_points) {
1999			e.set_point_type (PointType.LINE_CUBIC);
2000			e.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
2001			e.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
2002		}
2003
2004		foreach (EditPoint e in smooth_points) {
2005			path.recalculate_linear_handles_for_point (e);
2006		}
2007
2008		for (int i = 0; i < 3; i++) {
2009			foreach (EditPoint e in smooth_points) {
2010				e.set_tie_handle (true);
2011				e.get_right_handle ().set_point_type (PointType.CUBIC);
2012				e.get_left_handle ().set_point_type (PointType.CUBIC);
2013				e.process_tied_handle ();
2014			}
2015		}
2016
2017		if (path.points.size > 0) {
2018			path_list.add (path);
2019		}
2020
2021		foreach (Path p in path_list.paths) {
2022			p.remove_points_on_points ();
2023		}
2024
2025		return path_list;
2026	}
2027
2028	// TODO: implement a default svg parser
2029
2030	static int parse_int (string? s) {
2031		if (is_null (s)) {
2032			warning ("null instead of string");
2033			return 0;
2034		}
2035
2036		if (!is_point ((!) s)) {
2037			warning (@"Expecting an integer got: $((!) s)");
2038			return 0;
2039		}
2040
2041		return int.parse ((!) s);
2042	}
2043
2044	static double parse_double (string? s) {
2045		if (is_null (s)) {
2046			warning ("Got null instead of expected string.");
2047			return 0;
2048		}
2049
2050		if (!is_point ((!) s)) {
2051			warning (@"Expecting a double got: $((!) s)");
2052			return 0;
2053		}
2054
2055		string d = (!) s;
2056		d = d.replace ("px", "");
2057
2058		return double.parse (d);
2059	}
2060
2061	static bool is_point (string? s) {
2062		if (s == null) {
2063			warning ("s is null");
2064			return false;
2065		}
2066
2067		return double.try_parse ((!) s);
2068	}
2069
2070	Path parse_poly_data (string polygon_points) {
2071		string data = add_separators (polygon_points);
2072		string[] c = data.split (" ");
2073		Path path;
2074		BezierPoints[] bezier_points = new BezierPoints[c.length + 1];
2075		int bi;
2076		Glyph g;
2077		EditPoint ep;
2078
2079		bi = 0;
2080		for (int i = 0; i < c.length - 1; i += 2) {
2081			if (i + 1 >= c.length) {
2082				warning ("No y value.");
2083				break;
2084			}
2085
2086			if (bi >= bezier_points.length) {
2087				warning ("End of bezier_points");
2088				break;
2089			}
2090
2091			bezier_points[bi] = new BezierPoints ();
2092			bezier_points[bi].type == 'L';
2093			bezier_points[bi].x0 = parse_double (c[i]);
2094			bezier_points[bi].y0 = -parse_double (c[i + 1]);
2095			bi++;
2096		}
2097
2098		g = MainWindow.get_current_glyph ();
2099		move_and_resize (bezier_points, bi, false, 1, g);
2100
2101		path = new Path ();
2102		for (int i = 0; i < bi; i++) {
2103			ep = path.add (bezier_points[i].x0, bezier_points[i].y0);
2104			ep.set_point_type (PointType.LINE_CUBIC);
2105		}
2106
2107		path.create_list ();
2108		path.recalculate_linear_handles ();
2109
2110		return path;
2111	}
2112}
2113
2114}
2115