.
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
15 using B;
16 using Math;
17
18 namespace BirdFont {
19
20 public enum SvgFormat {
21 NONE,
22 INKSCAPE,
23 ILLUSTRATOR
24 }
25
26 public 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
1017 // use only a single space as separator
1018 while (data.index_of (" ") > -1) {
1019 data = data.replace (" ", " ");
1020 }
1021
1022 return data;
1023 }
1024
1025 public void add_path_to_glyph (string d, Glyph g, bool svg_glyph = false, double units = 1) {
1026 PathList p = parse_svg_data (d, g, svg_glyph, units);
1027 foreach (Path path in p.paths) {
1028 g.add_path (path);
1029 }
1030 }
1031
1032 /**
1033 * @param d svg data
1034 * @param glyph use lines from this glyph but don't add the generated paths
1035 * @param svg_glyph parse svg glyph with origo in lower left corner
1036 *
1037 * @return the new paths
1038 */
1039 public PathList parse_svg_data (string d, Glyph glyph, bool svg_glyph = false, double units = 1) {
1040 double px = 0;
1041 double py = 0;
1042 double px2 = 0;
1043 double py2 = 0;
1044 double cx = 0;
1045 double cy = 0;
1046 string data;
1047 Font font;
1048 PathList path_list = new PathList ();
1049 BezierPoints[] bezier_points;
1050 string[] c;
1051 double arc_rx, arc_ry;
1052 double arc_rotation;
1053 int large_arc;
1054 int arc_sweep;
1055 double arc_dest_x, arc_dest_y;
1056 double first_point_x = 0;
1057 double first_point_y = 0;
1058 bool set_first = true;
1059
1060 font = BirdFont.get_current_font ();
1061
1062 data = add_separators (d);
1063 c = data.split (" ");
1064 bezier_points = new BezierPoints[8 * c.length + 2]; // the arc instruction can use up to eight points
1065
1066 for (int i = 0; i < 2 * c.length + 1; i++) {
1067 bezier_points[i] = new BezierPoints ();
1068 }
1069
1070 int bi = 0;
1071
1072 // parse path
1073 int i = -1;
1074 while (++i < c.length && bi < bezier_points.length) {
1075 if (c[i] == "m") {
1076 while (i + 2 < c.length && is_point (c[i + 1])) {
1077 bezier_points[bi].type = 'M';
1078 bezier_points[bi].svg_type = 'm';
1079
1080 px += parse_double (c[++i]);
1081
1082 if (svg_glyph) {
1083 py += parse_double (c[++i]);
1084 } else {
1085 py += -parse_double (c[++i]);
1086 }
1087
1088 bezier_points[bi].x0 = px;
1089 bezier_points[bi].y0 = py;
1090
1091 first_point_x = px;
1092 first_point_y = py;
1093 set_first = false;
1094
1095 bi++;
1096 }
1097 } else if (c[i] == "M") {
1098 while (i + 2 < c.length && is_point (c[i + 1])) {
1099 bezier_points[bi].type = 'M';
1100 bezier_points[bi].svg_type = 'M';
1101
1102 px = parse_double (c[++i]);
1103
1104 if (svg_glyph) {
1105 py = parse_double (c[++i]);
1106 } else {
1107 py = -parse_double (c[++i]);
1108 }
1109
1110 bezier_points[bi].x0 = px;
1111 bezier_points[bi].y0 = py;
1112
1113 first_point_x = px;
1114 first_point_y = py;
1115 set_first = false;
1116
1117 bi++;
1118 }
1119 } else if (c[i] == "h") {
1120 while (i + 1 < c.length && is_point (c[i + 1])) {
1121 bezier_points[bi].type = 'L';
1122 bezier_points[bi].svg_type = 'h';
1123
1124 px += parse_double (c[++i]);
1125
1126 bezier_points[bi].x0 = px;
1127 bezier_points[bi].y0 = py;
1128 bi++;
1129 }
1130 } else if (c[i] == "H") {
1131 while (i + 1 < c.length && is_point (c[i + 1])) {
1132 bezier_points[bi].type = 'L';
1133 bezier_points[bi].svg_type = 'H';
1134
1135 px = parse_double (c[++i]);
1136
1137 bezier_points[bi].x0 = px;
1138 bezier_points[bi].y0 = py;
1139 bi++;
1140 }
1141 } else if (c[i] == "v") {
1142 while (i + 1 < c.length && is_point (c[i + 1])) {
1143 bezier_points[bi].type = 'L';
1144 bezier_points[bi].svg_type = 'v';
1145
1146 if (svg_glyph) {
1147 py = py + parse_double (c[++i]);
1148 } else {
1149 py = py - parse_double (c[++i]);
1150 }
1151
1152 bezier_points[bi].x0 = px;
1153 bezier_points[bi].y0 = py;
1154 bi++;
1155 }
1156 } else if (i + 1 < c.length && c[i] == "V") {
1157 while (is_point (c[i + 1])) {
1158 bezier_points[bi].type = 'L';
1159 bezier_points[bi].svg_type = 'V';
1160
1161 if (svg_glyph) {
1162 py = parse_double (c[++i]);
1163 } else {
1164 py = -parse_double (c[++i]);
1165 }
1166
1167 bezier_points[bi].x0 = px;
1168 bezier_points[bi].y0 = py;
1169 bi++;
1170 }
1171 } else if (c[i] == "l") {
1172 while (i + 2 < c.length && is_point (c[i + 1])) {
1173 bezier_points[bi].type = 'L';
1174 bezier_points[bi].svg_type = 'l';
1175
1176 cx = px + parse_double (c[++i]);
1177
1178 if (svg_glyph) {
1179 cy = py + parse_double (c[++i]);
1180 } else {
1181 cy = py - parse_double (c[++i]);
1182 }
1183
1184 px = cx;
1185 py = cy;
1186
1187 bezier_points[bi].x0 = cx;
1188 bezier_points[bi].y0 = cy;
1189 bi++;
1190 }
1191 } else if (c[i] == "L") {
1192 while (i + 2 < c.length && is_point (c[i + 1])) {
1193 bezier_points[bi].type = 'L';
1194 bezier_points[bi].svg_type = 'L';
1195
1196 cx = parse_double (c[++i]);
1197
1198 if (svg_glyph) {
1199 cy = parse_double (c[++i]);
1200 } else {
1201 cy = -parse_double (c[++i]);
1202 }
1203
1204 px = cx;
1205 py = cy;
1206
1207 bezier_points[bi].x0 = cx;
1208 bezier_points[bi].y0 = cy;
1209 bi++;
1210 }
1211 } else if (c[i] == "c") {
1212 while (i + 6 < c.length && is_point (c[i + 1])) {
1213 bezier_points[bi].type = 'C';
1214 bezier_points[bi].svg_type = 'C';
1215
1216 cx = px + parse_double (c[++i]);
1217
1218 if (svg_glyph) {
1219 cy = py + parse_double (c[++i]);
1220 } else {
1221 cy = py - parse_double (c[++i]);
1222 }
1223
1224 bezier_points[bi].x0 = cx;
1225 bezier_points[bi].y0 = cy;
1226
1227 cx = px + parse_double (c[++i]);
1228
1229 if (svg_glyph) {
1230 cy = py + parse_double (c[++i]);
1231 } else {
1232 cy = py - parse_double (c[++i]);
1233 }
1234
1235 px2 = cx;
1236 py2 = cy;
1237
1238 bezier_points[bi].x1 = px2;
1239 bezier_points[bi].y1 = py2;
1240
1241 cx = px + parse_double (c[++i]);
1242
1243 if (svg_glyph) {
1244 cy = py + parse_double (c[++i]);
1245 } else {
1246 cy = py + -parse_double (c[++i]);
1247 }
1248
1249 bezier_points[bi].x2 = cx;
1250 bezier_points[bi].y2 = cy;
1251
1252 px = cx;
1253 py = cy;
1254
1255 bi++;
1256 }
1257 } else if (c[i] == "C") {
1258 while (i + 6 < c.length && is_point (c[i + 1])) {
1259 bezier_points[bi].type = 'C';
1260 bezier_points[bi].svg_type = 'C';
1261
1262 cx = parse_double (c[++i]);
1263
1264 if (svg_glyph) {
1265 cy = parse_double (c[++i]);
1266 } else {
1267 cy = -parse_double (c[++i]);
1268 }
1269
1270 bezier_points[bi].x0 = cx;
1271 bezier_points[bi].y0 = cy;
1272
1273 cx = parse_double (c[++i]);
1274
1275 if (svg_glyph) {
1276 cy = parse_double (c[++i]);
1277 } else {
1278 cy = -parse_double (c[++i]);
1279 }
1280
1281 px2 = cx;
1282 py2 = cy;
1283
1284 bezier_points[bi].x1 = cx;
1285 bezier_points[bi].y1 = cy;
1286
1287 cx = parse_double (c[++i]);
1288
1289 if (svg_glyph) {
1290 cy = parse_double (c[++i]);
1291 } else {
1292 cy = -parse_double (c[++i]);
1293 }
1294
1295 bezier_points[bi].x2 = cx;
1296 bezier_points[bi].y2 = cy;
1297
1298 px = cx;
1299 py = cy;
1300
1301 bi++;
1302 }
1303 } else if (c[i] == "q") {
1304 while (i + 4 < c.length && is_point (c[i + 1])) {
1305 bezier_points[bi].type = 'Q';
1306 bezier_points[bi].svg_type = 'q';
1307
1308 cx = px + parse_double (c[++i]);
1309
1310 if (svg_glyph) {
1311 cy = py + parse_double (c[++i]);
1312 } else {
1313 cy = py - parse_double (c[++i]);
1314 }
1315
1316 bezier_points[bi].x0 = cx;
1317 bezier_points[bi].y0 = cy;
1318
1319 px2 = cx;
1320 py2 = cy;
1321
1322 cx = px + parse_double (c[++i]);
1323
1324 if (svg_glyph) {
1325 cy = py + parse_double (c[++i]);
1326 } else {
1327 cy = py - parse_double (c[++i]);
1328 }
1329
1330 bezier_points[bi].x1 = cx;
1331 bezier_points[bi].y1 = cy;
1332
1333 px = cx;
1334 py = cy;
1335
1336 bi++;
1337 }
1338 } else if (c[i] == "Q") {
1339
1340 while (i + 4 < c.length && is_point (c[i + 1])) {
1341 bezier_points[bi].type = 'Q';
1342 bezier_points[bi].svg_type = 'Q';
1343
1344 cx = parse_double (c[++i]);
1345
1346 if (svg_glyph) {
1347 cy = parse_double (c[++i]);
1348 } else {
1349 cy = -parse_double (c[++i]);
1350 }
1351
1352 bezier_points[bi].x0 = cx;
1353 bezier_points[bi].y0 = cy;
1354
1355 px2 = cx;
1356 py2 = cy;
1357
1358 cx = parse_double (c[++i]);
1359
1360 if (svg_glyph) {
1361 cy = parse_double (c[++i]);
1362 } else {
1363 cy = -parse_double (c[++i]);
1364 }
1365
1366 px = cx;
1367 py = cy;
1368
1369 bezier_points[bi].x1 = cx;
1370 bezier_points[bi].y1 = cy;
1371
1372 bi++;
1373 }
1374 } else if (c[i] == "t") {
1375 while (i + 2 < c.length && is_point (c[i + 1])) {
1376 bezier_points[bi].type = 'Q';
1377 bezier_points[bi].svg_type = 't';
1378
1379 // the first point is the reflection
1380 cx = 2 * px - px2;
1381 cy = 2 * py - py2; // if (svg_glyph) ?
1382
1383 bezier_points[bi].x0 = cx;
1384 bezier_points[bi].y0 = cy;
1385
1386 px2 = cx;
1387 py2 = cy;
1388
1389 cx = px + parse_double (c[++i]);
1390
1391 if (svg_glyph) {
1392 cy = py + parse_double (c[++i]);
1393 } else {
1394 cy = py - parse_double (c[++i]);
1395 }
1396
1397 px = cx;
1398 py = cy;
1399
1400 bezier_points[bi].x1 = px;
1401 bezier_points[bi].y1 = py;
1402
1403 bi++;
1404 }
1405 } else if (c[i] == "T") {
1406 while (i + 2 < c.length && is_point (c[i + 1])) {
1407 bezier_points[bi].type = 'Q';
1408 bezier_points[bi].svg_type = 'T';
1409
1410 // the reflection
1411 cx = 2 * px - px2;
1412 cy = 2 * py - py2; // if (svg_glyph) ?
1413
1414 bezier_points[bi].x0 = cx;
1415 bezier_points[bi].y0 = cy;
1416
1417 px2 = cx;
1418 py2 = cy;
1419
1420 cx = parse_double (c[++i]);
1421
1422 if (svg_glyph) {
1423 cy = parse_double (c[++i]);
1424 } else {
1425 cy = -parse_double (c[++i]);
1426 }
1427
1428 px = cx;
1429 py = cy;
1430
1431 bezier_points[bi].x1 = px;
1432 bezier_points[bi].y1 = py;
1433
1434 bi++;
1435 }
1436 } else if (c[i] == "s") {
1437 while (i + 4 < c.length && is_point (c[i + 1])) {
1438 bezier_points[bi].type = 'C';
1439 bezier_points[bi].svg_type = 's';
1440
1441 // the first point is the reflection
1442 cx = 2 * px - px2;
1443 cy = 2 * py - py2; // if (svg_glyph) ?
1444
1445 bezier_points[bi].x0 = cx;
1446 bezier_points[bi].y0 = cy;
1447
1448 cx = px + parse_double (c[++i]);
1449
1450 if (svg_glyph) {
1451 cy = py + parse_double (c[++i]);
1452 } else {
1453 cy = py - parse_double (c[++i]);
1454 }
1455
1456 px2 = cx;
1457 py2 = cy;
1458
1459 bezier_points[bi].x1 = px2;
1460 bezier_points[bi].y1 = py2;
1461
1462 cx = px + parse_double (c[++i]);
1463
1464 if (svg_glyph) {
1465 cy = py + parse_double (c[++i]);
1466 } else {
1467 cy = py - parse_double (c[++i]);
1468 }
1469
1470 bezier_points[bi].x2 = cx;
1471 bezier_points[bi].y2 = cy;
1472
1473 px = cx;
1474 py = cy;
1475
1476 bi++;
1477 }
1478 } else if (c[i] == "S") {
1479 while (i + 4 < c.length && is_point (c[i + 1])) {
1480 bezier_points[bi].type = 'C';
1481 bezier_points[bi].svg_type = 'S';
1482
1483 // the reflection
1484 cx = 2 * px - px2;
1485 cy = 2 * py - py2; // if (svg_glyph) ?
1486
1487 bezier_points[bi].x0 = cx;
1488 bezier_points[bi].y0 = cy;
1489
1490 // the other two are regular cubic points
1491 cx = parse_double (c[++i]);
1492
1493 if (svg_glyph) {
1494 cy = parse_double (c[++i]);
1495 } else {
1496 cy = -parse_double (c[++i]);
1497 }
1498
1499 px2 = cx;
1500 py2 = cy;
1501
1502 bezier_points[bi].x1 = px2;
1503 bezier_points[bi].y1 = py2;
1504
1505 cx = parse_double (c[++i]);
1506
1507 if (svg_glyph) {
1508 cy = parse_double (c[++i]);
1509 } else {
1510 cy = -parse_double (c[++i]);
1511 }
1512
1513 bezier_points[bi].x2 = cx;
1514 bezier_points[bi].y2 = cy;
1515
1516 px = cx;
1517 py = cy;
1518
1519 bi++;
1520 }
1521 } else if (c[i] == "a") {
1522 while (i + 7 < c.length && is_point (c[i + 1])) {
1523 arc_rx = parse_double (c[++i]);
1524 arc_ry = parse_double (c[++i]);
1525
1526 arc_rotation = parse_double (c[++i]);
1527 large_arc = parse_int (c[++i]);
1528 arc_sweep = parse_int (c[++i]);
1529
1530 cx = px + parse_double (c[++i]);
1531
1532 if (svg_glyph) {
1533 cy = py + parse_double (c[++i]);
1534 } else {
1535 cy = py - parse_double (c[++i]);
1536 }
1537
1538 arc_dest_x = cx;
1539 arc_dest_y = cy;
1540
1541 add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy);
1542
1543 px = cx;
1544 py = cy;
1545 }
1546 } else if (i + 7 < c.length && c[i] == "A") {
1547 while (is_point (c[i + 1])) {
1548 arc_rx = parse_double (c[++i]);
1549 arc_ry = parse_double (c[++i]);
1550
1551 arc_rotation = parse_double (c[++i]);
1552 large_arc = parse_int (c[++i]);
1553 arc_sweep = parse_int (c[++i]);
1554
1555 cx = parse_double (c[++i]);
1556
1557 if (svg_glyph) {
1558 cy = parse_double (c[++i]);
1559 } else {
1560 cy = -parse_double (c[++i]);
1561 }
1562
1563 arc_dest_x = cx;
1564 arc_dest_y = cy;
1565
1566 add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy);
1567
1568 px = cx;
1569 py = cy;
1570
1571
1572 }
1573 } else if (c[i] == "z" || c[i] == "Z") {
1574 if (Path.distance (px, first_point_x, py, first_point_y) > 0.001) {
1575
1576 px = first_point_x;
1577 py = first_point_y;
1578
1579 bezier_points[bi].type = 'L';
1580 bezier_points[bi].svg_type = 'l';
1581 bezier_points[bi].x0 = px;
1582 bezier_points[bi].y0 = py;
1583
1584 bi++;
1585 }
1586
1587 bezier_points[bi].type = 'z';
1588 bezier_points[bi].svg_type = 'z';
1589
1590 set_first = true;
1591 bi++;
1592 } else if (c[i] == "") {
1593 } else if (c[i] == " ") {
1594 } else {
1595
1596 print (@"\n\nSVG:");
1597 for (int dd = 0; dd <= i; dd++) {
1598 print (@"$(c[dd]) ");
1599 }
1600 print (@"\n");
1601
1602 warning (@"Unknown instruction: $(c[i]), i: $(i)");
1603 }
1604 }
1605
1606 if (bi == 0) {
1607 warning ("No points in path.");
1608 return path_list;
1609 }
1610
1611 move_and_resize (bezier_points, bi, svg_glyph, units, glyph);
1612
1613 if (format == SvgFormat.ILLUSTRATOR) {
1614 path_list = create_paths_illustrator (bezier_points, bi);
1615 } else {
1616 path_list = create_paths_inkscape (bezier_points, bi);
1617 }
1618
1619 // TODO: Find out if it is possible to tie handles.
1620 return path_list;
1621 }
1622
1623 void move_and_resize (BezierPoints[] b, int num_b, bool svg_glyph, double units, Glyph glyph) {
1624 Font font = BirdFont.get_current_font ();
1625
1626 for (int i = 0; i < num_b; i++) {
1627 // resize all points
1628 b[i].x0 *= units;
1629 b[i].y0 *= units;
1630 b[i].x1 *= units;
1631 b[i].y1 *= units;
1632 b[i].x2 *= units;
1633 b[i].y2 *= units;
1634
1635 // move all points
1636 if (svg_glyph) {
1637 b[i].x0 += glyph.left_limit;
1638 b[i].y0 += font.base_line;
1639 b[i].x1 += glyph.left_limit;
1640 b[i].y1 += font.base_line;
1641 b[i].x2 += glyph.left_limit;
1642 b[i].y2 += font.base_line;
1643 } else {
1644 b[i].x0 += glyph.left_limit;
1645 b[i].y0 += font.top_limit;
1646 b[i].x1 += glyph.left_limit;
1647 b[i].y1 += font.top_limit;
1648 b[i].x2 += glyph.left_limit;
1649 b[i].y2 += font.top_limit;
1650 }
1651 }
1652 }
1653
1654 void find_last_handle (int start_index, BezierPoints[] b, int num_b, out double left_x, out double left_y, out PointType last_type) {
1655 BezierPoints last = new BezierPoints ();
1656 bool found = false;
1657
1658 left_x = 0;
1659 left_y = 0;
1660 last_type = PointType.NONE;
1661
1662 return_if_fail (b.length != 0);
1663 return_if_fail (b[0].type != 'z');
1664 return_if_fail (num_b < b.length);
1665
1666 if (num_b == 2) {
1667 left_x = b[0].x0 + (b[1].x0 - b[0].x0) / 3.0;
1668 left_y = b[0].y0 + (b[1].y0 - b[0].y0) / 3.0;
1669 last_type = PointType.LINE_CUBIC;
1670 return;
1671 }
1672
1673 for (int i = start_index; i < num_b; i++) {
1674 switch (b[i].type) {
1675 case 'Q':
1676 break;
1677 case 'C':
1678 break;
1679 case 'z':
1680 found = true;
1681 break;
1682 default:
1683 break;
1684 }
1685
1686 if (found || i + 1 == num_b) {
1687
1688 return_if_fail (i >= 1);
1689
1690 if (b[i - 1].type == 'Q') {
1691 return_if_fail (i >= 1);
1692 left_x = b[i - 1].x0;
1693 left_y = b[i - 1].y0;
1694 last_type = PointType.QUADRATIC;
1695 } else if (b[i - 1].type == 'C') {
1696 return_if_fail (i >= 1);
1697 left_x = b[i - 1].x1;
1698 left_y = b[i - 1].y1;
1699 last_type = PointType.CUBIC;
1700 } else if (b[i - 1].type == 'S') {
1701 return_if_fail (i >= 1);
1702 left_x = b[i - 1].x1;
1703 left_y = b[i - 1].y1;
1704 last_type = PointType.CUBIC;
1705 } else if (b[i - 1].type == 'L' || last.type == 'M') {
1706 return_if_fail (i >= 2); // FIXME: -2 can be C or L
1707 left_x = b[i - 2].x0 + (b[i - 1].x0 - b[i - 2].x0) / 3.0;
1708 left_y = b[i - 2].y0 + (b[i - 1].y0 - b[i - 2].y0) / 3.0;
1709 last_type = PointType.LINE_CUBIC;
1710 } else {
1711 warning (@"Unexpected type. $(b[i - 1])\n");
1712 }
1713 return;
1714 }
1715
1716 last = b[i];
1717 }
1718
1719 warning ("Last point not found.");
1720 }
1721
1722 PathList create_paths_inkscape (BezierPoints[] b, int num_b) {
1723 double last_x;
1724 double last_y;
1725 PointType last_type;
1726 Path path;
1727 PathList path_list = new PathList ();
1728 EditPoint ep = new EditPoint ();
1729 Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> ();
1730
1731 path = new Path ();
1732
1733 if (num_b == 0) {
1734 warning ("No SVG data");
1735 return path_list;
1736 }
1737
1738 if (b[0].type != 'M') {
1739 warning ("Path must begin with M or m. Not $(b[0].type )");
1740 return path_list;
1741 }
1742
1743 find_last_handle (0, b, num_b, out last_x, out last_y, out last_type);
1744
1745 for (int i = 0; i < num_b; i++) {
1746 if (b[i].type == '\0') {
1747 warning ("Parser error.");
1748 return path_list;
1749 }
1750
1751 if (b[i].type == 'z') {
1752 path.close ();
1753 path.create_list ();
1754 path.recalculate_linear_handles ();
1755 path_list.add (path);
1756 path = new Path ();
1757
1758 if (i + 1 >= num_b) {
1759 break;
1760 } else {
1761 find_last_handle (i + 1, b, num_b, out last_x, out last_y, out last_type);
1762 }
1763 }
1764
1765 if (i >= num_b) {
1766 break;
1767 }
1768
1769 if (b[i].type == 'M') {
1770 ep = path.add (b[i].x0, b[i].y0);
1771 ep.set_point_type (PointType.CUBIC);
1772
1773 ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
1774
1775 if (i == 0 || (b[i - 1].type == 'z')) {
1776 ep.get_left_handle ().set_point_type (last_type);
1777 ep.get_left_handle ().move_to_coordinate (last_x, last_y);
1778 } else {
1779 if (b[i - 1].type == 'C' || b[i - 1].type == 'S') {
1780 ep.get_left_handle ().set_point_type (PointType.CUBIC);
1781 ep.get_left_handle ().move_to_coordinate (b[i + 1].x1, b[i + 1].y1);
1782 }
1783
1784 if (b[i + 1].type == 'C' || b[i - 1].type == 'S') {
1785 ep.get_right_handle ().set_point_type (PointType.CUBIC);
1786 ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);
1787 } else if (b[i + 1].type == 'L' || b[i + 1].type == 'M') {
1788 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1789 }
1790 }
1791 }
1792
1793 if (b[i].type == 'L') {
1794 return_val_if_fail (i != 0, path_list);
1795
1796 ep = path.add (b[i].x0, b[i].y0);
1797 ep.set_point_type (PointType.CUBIC);
1798 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1799 ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
1800
1801 if (b[i + 1].type == 'L' || b[i + 1].type == 'M' || b[i + 1].type == 'z') {
1802 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1803 }
1804
1805 if (b[i -1].type == 'L' || b[i - 1].type == 'M') {
1806 ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
1807 }
1808 }
1809
1810 if (b[i].type == 'Q') {
1811 return_val_if_fail (i != 0, path_list);
1812
1813 ep.set_point_type (PointType.QUADRATIC);
1814
1815 ep.get_right_handle ().set_point_type (PointType.QUADRATIC);
1816 ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0);
1817
1818 if (b[i + 1].type != 'z') {
1819 ep = path.add (b[i].x1, b[i].y1);
1820
1821 ep.get_left_handle ().set_point_type (PointType.QUADRATIC);
1822 ep.get_left_handle ().move_to_coordinate (b[i].x0, b[i].y0);
1823 }
1824 }
1825
1826 if (b[i].type == 'C' || b[i].type == 'S') {
1827 return_val_if_fail (i != 0, path_list);
1828
1829 ep.set_point_type (PointType.CUBIC);
1830
1831 ep.get_right_handle ().set_point_type (PointType.CUBIC);
1832 ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0);
1833
1834 if (b[i].type == 'S') {
1835 smooth_points.add (ep);
1836 }
1837
1838 if (b[i + 1].type != 'z') {
1839 ep = path.add (b[i].x2, b[i].y2);
1840
1841 ep.get_left_handle ().set_point_type (PointType.CUBIC);
1842 ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1);
1843 }
1844 }
1845 }
1846
1847 foreach (EditPoint e in smooth_points) {
1848 e.set_point_type (PointType.LINE_DOUBLE_CURVE);
1849 e.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);
1850 e.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);
1851 }
1852
1853 foreach (EditPoint e in smooth_points) {
1854 path.recalculate_linear_handles_for_point (e);
1855 }
1856
1857 for (int i = 0; i < 3; i++) {
1858 foreach (EditPoint e in smooth_points) {
1859 e.set_tie_handle (true);
1860 e.process_tied_handle ();
1861 }
1862 }
1863
1864 if (path.points.size > 0) {
1865 path_list.add (path);
1866 }
1867
1868 foreach (Path p in path_list.paths) {
1869 p.remove_points_on_points (0.2);
1870 }
1871
1872 return path_list;
1873 }
1874
1875 PathList create_paths_illustrator (BezierPoints[] b, int num_b) {
1876 Path path;
1877 PathList path_list = new PathList ();
1878 EditPoint ep;
1879 bool first_point = true;
1880 double first_left_x, first_left_y;
1881 Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> ();
1882
1883 if (num_b > b.length) {
1884 warning ("num_b > b.length: $num_b > $(b.length)");
1885 return path_list;
1886 }
1887
1888 path = new Path ();
1889
1890 if (num_b <= 1) {
1891 warning ("No SVG data");
1892 return path_list;
1893 }
1894
1895 first_left_x = 0;
1896 first_left_y = 0;
1897
1898 ep = new EditPoint ();
1899
1900 for (int i = 0; i < num_b; i++) {
1901 if (b[i].type == '\0') {
1902 warning ("Parser error.");
1903 return path_list;
1904 } else if (b[i].type == 'z') {
1905 path.close ();
1906 path.create_list ();
1907
1908 int first_index = 1;
1909
1910 for (int j = i - 1; j >= 1; j--) {
1911 if (b[j].type == 'z') {
1912 first_index = j + 1; // from z to M
1913 }
1914 }
1915
1916 if (b[first_index].type == 'C' || b[first_index].type == 'S') {
1917 return_val_if_fail (path.points.size != 0, path_list);
1918 ep = path.points.get (path.points.size - 1);
1919
1920 if (b[i - 1].type != 'L' ) {
1921 ep.get_right_handle ().set_point_type (PointType.CUBIC);
1922 ep.get_right_handle ().move_to_coordinate (b[first_index].x0, b[first_index].y0);
1923 }
1924 } else if (b[first_index].type == 'L') {
1925 return_val_if_fail (path.points.size != 0, path_list);
1926 ep = path.points.get (path.points.size - 1);
1927 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1928 path.recalculate_linear_handles_for_point (ep);
1929 } else {
1930 warning ("Unexpected type: %s", (!) b[first_index].type.to_string ());
1931 }
1932
1933 path.recalculate_linear_handles ();
1934 path_list.add (path);
1935
1936 path = new Path ();
1937 first_point = true;
1938 } else if (b[i].type == 'L' || b[i].type == 'M') {
1939
1940 if (first_point) {
1941 first_left_x = b[i].x0;
1942 first_left_y = b[i].y0;
1943 }
1944
1945 ep = path.add (b[i].x0, b[i].y0);
1946 ep.set_point_type (PointType.CUBIC); // TODO: quadratic
1947 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1948
1949 if (first_point || b[i -1].type == 'L') {
1950 // ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
1951 } else {
1952 ep.get_left_handle ().set_point_type (PointType.CUBIC);
1953 }
1954
1955 if (b[i + 1].type == 'C' || b[i + 1].type == 'S') {
1956 return_val_if_fail (i + 1 < num_b, path_list);
1957 ep.get_right_handle ().set_point_type (PointType.CUBIC);
1958 ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);
1959 }
1960
1961 first_point = false;
1962 } else if (b[i].type == 'Q') {
1963 warning ("Illustrator does not support quadratic control points.");
1964 warning (@"$(b[i])\n");
1965 } else if (b[i].type == 'C' || b[i].type == 'S') {
1966
1967 if (first_point) {
1968 first_left_x = b[i].x0;
1969 first_left_y = b[i].y0;
1970 }
1971
1972 ep = path.add (b[i].x2, b[i].y2);
1973 ep.set_point_type (PointType.CUBIC);
1974
1975 ep.get_right_handle ().set_point_type (PointType.CUBIC);
1976 ep.get_left_handle ().set_point_type (PointType.CUBIC);
1977
1978 ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1);
1979
1980 if (b[i].type == 'S') {
1981 smooth_points.add (ep);
1982 }
1983
1984 if (b[i + 1].type != 'z' && i != num_b - 1) {
1985 ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);
1986 } else {
1987 ep.get_right_handle ().move_to_coordinate (first_left_x, first_left_y);
1988 }
1989
1990 first_point = false;
1991 } else {
1992 warning ("Unknown control point type.");
1993 warning (@"$(b[i])\n");
1994 }
1995 }
1996
1997 foreach (EditPoint e in smooth_points) {
1998 e.set_point_type (PointType.LINE_CUBIC);
1999 e.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
2000 e.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
2001 }
2002
2003 foreach (EditPoint e in smooth_points) {
2004 path.recalculate_linear_handles_for_point (e);
2005 }
2006
2007 for (int i = 0; i < 3; i++) {
2008 foreach (EditPoint e in smooth_points) {
2009 e.set_tie_handle (true);
2010 e.get_right_handle ().set_point_type (PointType.CUBIC);
2011 e.get_left_handle ().set_point_type (PointType.CUBIC);
2012 e.process_tied_handle ();
2013 }
2014 }
2015
2016 if (path.points.size > 0) {
2017 path_list.add (path);
2018 }
2019
2020 foreach (Path p in path_list.paths) {
2021 p.remove_points_on_points ();
2022 }
2023
2024 return path_list;
2025 }
2026
2027 // TODO: implement a default svg parser
2028
2029 static int parse_int (string? s) {
2030 if (is_null (s)) {
2031 warning ("null instead of string");
2032 return 0;
2033 }
2034
2035 if (!is_point ((!) s)) {
2036 warning (@"Expecting an integer got: $((!) s)");
2037 return 0;
2038 }
2039
2040 return int.parse ((!) s);
2041 }
2042
2043 static double parse_double (string? s) {
2044 if (is_null (s)) {
2045 warning ("Got null instead of expected string.");
2046 return 0;
2047 }
2048
2049 if (!is_point ((!) s)) {
2050 warning (@"Expecting a double got: $((!) s)");
2051 return 0;
2052 }
2053
2054 string d = (!) s;
2055 d = d.replace ("px", "");
2056
2057 return double.parse (d);
2058 }
2059
2060 static bool is_point (string? s) {
2061 if (s == null) {
2062 warning ("s is null");
2063 return false;
2064 }
2065
2066 return double.try_parse ((!) s);
2067 }
2068
2069 Path parse_poly_data (string polygon_points) {
2070 string data = add_separators (polygon_points);
2071 string[] c = data.split (" ");
2072 Path path;
2073 BezierPoints[] bezier_points = new BezierPoints[c.length + 1];
2074 int bi;
2075 Glyph g;
2076 EditPoint ep;
2077
2078 bi = 0;
2079 for (int i = 0; i < c.length - 1; i += 2) {
2080 if (i + 1 >= c.length) {
2081 warning ("No y value.");
2082 break;
2083 }
2084
2085 if (bi >= bezier_points.length) {
2086 warning ("End of bezier_points");
2087 break;
2088 }
2089
2090 bezier_points[bi] = new BezierPoints ();
2091 bezier_points[bi].type == 'L';
2092 bezier_points[bi].x0 = parse_double (c[i]);
2093 bezier_points[bi].y0 = -parse_double (c[i + 1]);
2094 bi++;
2095 }
2096
2097 g = MainWindow.get_current_glyph ();
2098 move_and_resize (bezier_points, bi, false, 1, g);
2099
2100 path = new Path ();
2101 for (int i = 0; i < bi; i++) {
2102 ep = path.add (bezier_points[i].x0, bezier_points[i].y0);
2103 ep.set_point_type (PointType.LINE_CUBIC);
2104 }
2105
2106 path.create_list ();
2107 path.recalculate_linear_handles ();
2108
2109 return path;
2110 }
2111 }
2112
2113 }
2114