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