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