.
1 /*
2 Copyright (C) 2012, 2013, 2014 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 Bird;
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 MainWindow.file_chooser (t_("Import"), fc, FileChooser.LOAD);
51 }
52
53 public static void import_svg_data (string xml_data, SvgFormat format = SvgFormat.NONE) {
54 PathList path_list = new PathList ();
55 Glyph glyph;
56 string[] lines = xml_data.split ("\n");
57 bool has_format = false;
58 SvgParser parser = new SvgParser ();
59 XmlParser xmlparser;
60
61 foreach (string l in lines) {
62 if (l.index_of ("Illustrator") > -1 || l.index_of ("illustrator") > -1) {
63 parser.set_format (SvgFormat.ILLUSTRATOR);
64 has_format = true;
65 }
66
67 if (l.index_of ("Inkscape") > -1 || l.index_of ("inkscape") > -1) {
68 parser.set_format (SvgFormat.INKSCAPE);
69 has_format = true;
70 }
71 }
72
73 if (format != SvgFormat.NONE) {
74 parser.set_format (format);
75 }
76
77 // parse the file
78 if (!has_format) {
79 warn_if_test ("No format identifier found in SVG parser.\n");
80 }
81
82 xmlparser = new XmlParser (xml_data);
83
84 path_list = parser.parse_svg_file (xmlparser.get_root_tag ());
85
86 glyph = MainWindow.get_current_glyph ();
87 foreach (Path p in path_list.paths) {
88 glyph.add_path (p);
89 }
90
91 foreach (Path p in path_list.paths) {
92 glyph.add_active_path (p);
93 p.update_region_boundaries ();
94 }
95
96 glyph.close_path ();
97 }
98
99 public static string replace (string content, string start, string stop, string replacement) {
100 int i_tag = content.index_of (start);
101 int end_tag = content.index_of (stop, i_tag);
102 string c = "";
103
104 if (i_tag > -1) {
105 c = content.substring (0, i_tag)
106 + replacement
107 + content.substring (end_tag + stop.length);
108 } else {
109 c = content;
110 }
111
112 return c;
113 }
114
115 public static void import_svg (string path) {
116 string svg_data;
117 try {
118 FileUtils.get_contents (path, out svg_data);
119 } catch (GLib.Error e) {
120 warning (e.message);
121 }
122 import_svg_data (svg_data);
123 }
124
125 private PathList parse_svg_file (Tag tag) {
126 Layer pl = new Layer ();
127
128 foreach (Tag t in tag) {
129
130 if (t.get_name () == "g") {
131 parse_layer (t, pl);
132 }
133
134 if (t.get_name () == "switch") {
135 parse_layer (t, pl);
136 }
137
138 if (t.get_name () == "path") {
139 parse_path (t, pl);
140 }
141
142 if (t.get_name () == "polygon") {
143 parse_polygon (t, pl);
144 }
145 }
146
147 return pl.get_all_paths ();
148 }
149
150 private void parse_layer (Tag tag, Layer pl) {
151 Layer layer;
152
153 foreach (Tag t in tag) {
154 if (t.get_name () == "path") {
155 parse_path (t, pl);
156 }
157
158 if (t.get_name () == "g") {
159 layer = new Layer ();
160 parse_layer (t, layer);
161 pl.subgroups.add (layer);
162 }
163
164 if (t.get_name () == "polygon") {
165 parse_polygon (t, pl);
166 }
167
168 if (t.get_name () == "rect") {
169 parse_rect (t, pl);
170 }
171 }
172
173 foreach (Attribute attr in tag.get_attributes ()) {
174 if (attr.get_name () == "transform") {
175 transform (attr.get_content (), pl);
176 }
177 }
178 }
179
180 private void transform (string transform_functions, Layer layer) {
181 transform_paths (transform_functions, layer.paths);
182 transform_subgroups (transform_functions, layer);
183 }
184
185 private void transform_subgroups (string transform_functions, Layer layer) {
186 foreach (Layer subgroup in layer.subgroups) {
187 transform (transform_functions, subgroup);
188 }
189 }
190
191 private void transform_paths (string transform_functions, PathList pl) {
192 string data = transform_functions.dup ();
193 string[] functions;
194
195 // use only a single space as separator
196 while (data.index_of (" ") > -1) {
197 data = data.replace (" ", " ");
198 }
199
200 return_if_fail (data.index_of (")") > -1);
201
202 // add separator
203 data = data.replace (") ", "|");
204 data = data.replace (")", "|");
205 functions = data.split ("|");
206
207 for (int i = functions.length - 1; i >= 0; i--) {
208 if (functions[i].has_prefix ("translate")) {
209 translate (functions[i], pl);
210 }
211
212 if (functions[i].has_prefix ("scale")) {
213 scale (functions[i], pl);
214 }
215
216 if (functions[i].has_prefix ("matrix")) {
217 matrix (functions[i], pl);
218 }
219
220 // TODO: rotate etc.
221 }
222 }
223
224 /** @param path a path in the cartesian coordinate system
225 * The other parameters are in the SVG coordinate system.
226 */
227 public static void apply_matrix (Path path, double a, double b, double c,
228 double d, double e, double f){
229
230 double dx, dy;
231 Font font = BirdFont.get_current_font ();
232 Glyph glyph = MainWindow.get_current_glyph ();
233
234 foreach (EditPoint ep in path.points) {
235 apply_matrix_on_handle (ep.get_right_handle (), a, b, c, d, e, f);
236 apply_matrix_on_handle (ep.get_left_handle (), a, b, c, d, e, f);
237
238 ep.independent_y = font.top_position - ep.independent_y;
239 ep.independent_x -= glyph.left_limit;
240
241 dx = a * ep.independent_x + c * ep.independent_y + e;
242 dy = b * ep.independent_x + d * ep.independent_y + f;
243
244 ep.independent_x = dx;
245 ep.independent_y = dy;
246
247 ep.independent_y = font.top_position - ep.independent_y;
248 ep.independent_x += glyph.left_limit;
249 }
250 }
251
252 public static void apply_matrix_on_handle (EditPointHandle h,
253 double a, double b, double c,
254 double d, double e, double f){
255
256 double dx, dy;
257 Font font = BirdFont.get_current_font ();
258 Glyph glyph = MainWindow.get_current_glyph ();
259
260 h.y = font.top_position - h.y;
261 h.x -= glyph.left_limit;
262
263 dx = a * h.x + c * h.y + e;
264 dy = b * h.x + d * h.y + f;
265
266 h.x = dx;
267 h.y = dy;
268
269 h.y = font.top_position - h.y;
270 h.x += glyph.left_limit;
271 }
272
273
274 private void matrix (string function, PathList pl) {
275 string parameters = get_transform_parameters (function);
276 string[] p = parameters.split (" ");
277
278 if (p.length != 6) {
279 warning ("Expecting six parameters for matrix transformation.");
280 return;
281 }
282
283 foreach (Path path in pl.paths) {
284 apply_matrix (path, parse_double (p[0]), parse_double (p[1]),
285 parse_double (p[2]), parse_double (p[3]),
286 parse_double (p[4]), parse_double (p[5]));
287 }
288 }
289
290 private void scale (string function, PathList pl) {
291 string parameters = get_transform_parameters (function);
292 string[] p = parameters.split (" ");
293 double x, y;
294
295 x = 1;
296 y = 1;
297
298 if (p.length > 0) {
299 x = parse_double (p[0]);
300 }
301
302 if (p.length > 1) {
303 y = parse_double (p[1]);
304 }
305
306 foreach (Path path in pl.paths) {
307 path.scale (-x, y);
308 }
309 }
310
311 private void translate (string function, PathList pl) {
312 string parameters = get_transform_parameters (function);
313 string[] p = parameters.split (" ");
314 double x, y;
315
316 x = 0;
317 y = 0;
318
319 if (p.length > 0) {
320 x = parse_double (p[0]);
321 }
322
323 if (p.length > 1) {
324 y = parse_double (p[1]);
325 }
326
327 foreach (Path path in pl.paths) {
328 path.move (x, -y);
329 }
330 }
331
332 private string get_transform_parameters (string function) {
333 int i;
334 string param = "";
335
336 i = function.index_of ("(");
337 return_val_if_fail (i != -1, param);
338 param = function.substring (i);
339
340 param = param.replace ("(", "");
341 param = param.replace ("\n", " ");
342 param = param.replace ("\t", " ");
343 param = param.replace (",", " ");
344
345 while (param.index_of (" ") > -1) {
346 param.replace (" ", " ");
347 }
348
349 return param.strip();
350 }
351
352 private void parse_rect (Tag tag, Layer pl) {
353 Path p;
354 double x, y, x2, y2;
355 BezierPoints[] bezier_points;
356 Glyph g;
357 PathList npl = new PathList ();
358
359 x = 0;
360 y = 0;
361 x2 = 0;
362 y2 = 0;
363
364 foreach (Attribute attr in tag.get_attributes ()) {
365 if (attr.get_name () == "x") {
366 x = parse_double (attr.get_content ());
367 }
368
369 if (attr.get_name () == "y") {
370 y = -parse_double (attr.get_content ());
371 }
372
373 if (attr.get_name () == "width") {
374 x2 = parse_double (attr.get_content ());
375 }
376
377 if (attr.get_name () == "height") {
378 y2 = -parse_double (attr.get_content ());
379 }
380 }
381
382 x2 += x;
383 y2 += y;
384
385 bezier_points = new BezierPoints[4];
386 bezier_points[0] = new BezierPoints ();
387 bezier_points[0].type == 'L';
388 bezier_points[0].x0 = x;
389 bezier_points[0].y0 = y;
390
391 bezier_points[1] = new BezierPoints ();
392 bezier_points[1].type == 'L';
393 bezier_points[1].x0 = x2;
394 bezier_points[1].y0 = y;
395
396 bezier_points[2] = new BezierPoints ();
397 bezier_points[2].type == 'L';
398 bezier_points[2].x0 = x2;
399 bezier_points[2].y0 = y2;
400
401 bezier_points[3] = new BezierPoints ();
402 bezier_points[3].type == 'L';
403 bezier_points[3].x0 = x;
404 bezier_points[3].y0 = y2;
405
406 g = MainWindow.get_current_glyph ();
407 move_and_resize (bezier_points, 4, false, 1, g);
408
409 p = new Path ();
410
411 p.add (bezier_points[0].x0, bezier_points[0].y0);
412 p.add (bezier_points[1].x0, bezier_points[1].y0);
413 p.add (bezier_points[2].x0, bezier_points[2].y0);
414 p.add (bezier_points[3].x0, bezier_points[3].y0);
415
416 p.close ();
417 p.create_list ();
418 p.recalculate_linear_handles ();
419
420 npl.add (p);
421
422 // FIXME: right layer for other transforms
423 foreach (Attribute attr in tag.get_attributes ()) {
424 if (attr.get_name () == "transform") {
425 transform_paths (attr.get_content (), npl);
426 }
427 }
428
429 pl.paths.append (npl);
430 }
431
432 private void parse_polygon (Tag tag, Layer pl) {
433 Path p;
434
435 foreach (Attribute attr in tag.get_attributes ()) {
436 if (attr.get_name () == "points") {
437 p = parse_polygon_data (attr.get_content ());
438 pl.paths.add (p);
439 }
440 }
441 }
442
443 private void parse_path (Tag tag, Layer pl) {
444 Glyph glyph = MainWindow.get_current_glyph ();
445 PathList path_list = new PathList ();
446
447 foreach (Attribute attr in tag.get_attributes ()) {
448 if (attr.get_name () == "d") {
449 path_list = parse_svg_data (attr.get_content (), glyph);
450 pl.paths.append (path_list);
451 }
452 }
453
454 // assume the even odd rule is applied and convert the path
455 // to a path using the non-zero rule
456 foreach (Path p1 in pl.paths.paths) {
457 if (Path.is_counter (pl.paths, p1)) {
458 p1.force_direction (Direction.COUNTER_CLOCKWISE);
459 } else {
460 p1.force_direction (Direction.CLOCKWISE);
461 }
462 }
463
464 foreach (Attribute attr in tag.get_attributes ()) {
465 if (attr.get_name () == "transform") {
466 transform_paths (attr.get_content (), path_list);
467 }
468 }
469 }
470
471 /** Check if a point is inside using the even odd fill rule. */
472 public static bool is_inside (EditPoint point, Path path) {
473 EditPoint prev;
474 bool inside = false;
475
476 if (path.points.size == 0) {
477 return false;
478 }
479
480 prev = path.get_last_point ();
481
482 foreach (EditPoint p in path.points) {
483 if ((p.y > point.y) != (prev.y > point.y)
484 && point.x < (prev.x - p.x) * (point.y - p.y) / (prev.y - p.y) + p.x) {
485 inside = !inside;
486 }
487
488 prev = p;
489 }
490
491 return inside;
492 }
493
494 /** Add space as separator to svg data.
495 * @param d svg data
496 */
497 static string add_separators (string d) {
498 string data = d;
499
500 data = data.replace (",", " ");
501 data = data.replace ("a", " a ");
502 data = data.replace ("A", " A ");
503 data = data.replace ("m", " m ");
504 data = data.replace ("M", " M ");
505 data = data.replace ("h", " h ");
506 data = data.replace ("H", " H ");
507 data = data.replace ("v", " v ");
508 data = data.replace ("V", " V ");
509 data = data.replace ("l", " l ");
510 data = data.replace ("L", " L ");
511 data = data.replace ("q", " q ");
512 data = data.replace ("Q", " Q ");
513 data = data.replace ("c", " c ");
514 data = data.replace ("C", " C ");
515 data = data.replace ("t", " t ");
516 data = data.replace ("T", " T ");
517 data = data.replace ("s", " s ");
518 data = data.replace ("S", " S ");
519 data = data.replace ("zM", " z M ");
520 data = data.replace ("zm", " z m ");
521 data = data.replace ("z", " z ");
522 data = data.replace ("Z", " Z ");
523 data = data.replace ("-", " -");
524 data = data.replace ("e -", "e-"); // minus can be either separator or a negative exponent
525 data = data.replace ("\t", " ");
526 data = data.replace ("\r\n", " ");
527 data = data.replace ("\n", " ");
528
529 // use only a single space as separator
530 while (data.index_of (" ") > -1) {
531 data = data.replace (" ", " ");
532 }
533
534 return data;
535 }
536
537 public void add_path_to_glyph (string d, Glyph g, bool svg_glyph = false, double units = 1) {
538 PathList p = parse_svg_data (d, g, svg_glyph, units);
539 foreach (Path path in p.paths) {
540 g.add_path (path);
541 }
542 }
543
544 /**
545 * @param d svg data
546 * @param glyph use lines from this glyph but don't add the generated paths
547 * @param svg_glyph parse svg glyph with origo in lower left corner
548 *
549 * @return the new paths
550 */
551 public PathList parse_svg_data (string d, Glyph glyph, bool svg_glyph = false, double units = 1) {
552 double px = 0;
553 double py = 0;
554 double px2 = 0;
555 double py2 = 0;
556 double cx = 0;
557 double cy = 0;
558 string data;
559 Font font;
560 PathList path_list = new PathList ();
561 BezierPoints[] bezier_points;
562 string[] c;
563 double arc_rx, arc_ry;
564 double arc_rotation;
565 int large_arc;
566 int arc_sweep;
567 double arc_dest_x, arc_dest_y;
568
569 font = BirdFont.get_current_font ();
570
571 data = add_separators (d);
572 c = data.split (" ");
573 bezier_points = new BezierPoints[8 * c.length + 1]; // the arc instruction can use up to eight points
574
575 for (int i = 0; i < 2 * c.length + 1; i++) {
576 bezier_points[i] = new BezierPoints ();
577 }
578
579 int bi = 0;
580
581 // parse path
582 int i = -1;
583 while (++i < c.length && bi < bezier_points.length) {
584 if (c[i] == "m") {
585 while (i + 2 < c.length && is_point (c[i + 1])) { // FIXME: check array bounds
586 bezier_points[bi].type = 'M';
587 bezier_points[bi].svg_type = 'm';
588
589 px += parse_double (c[++i]);
590
591 if (svg_glyph) {
592 py += parse_double (c[++i]);
593 } else {
594 py += -parse_double (c[++i]);
595 }
596
597 bezier_points[bi].x0 = px;
598 bezier_points[bi].y0 = py;
599 bi++;
600 }
601 } else if (c[i] == "M") {
602 while (i + 2 < c.length && is_point (c[i + 1])) {
603 bezier_points[bi].type = 'M';
604 bezier_points[bi].svg_type = 'M';
605
606 px = parse_double (c[++i]);
607
608 if (svg_glyph) {
609 py = parse_double (c[++i]);
610 } else {
611 py = -parse_double (c[++i]);
612 }
613
614 bezier_points[bi].x0 = px;
615 bezier_points[bi].y0 = py;
616 bi++;
617 }
618 } else if (c[i] == "h") {
619 while (i + 1 < c.length && is_point (c[i + 1])) {
620 bezier_points[bi].type = 'L';
621 bezier_points[bi].svg_type = 'h';
622
623 px += parse_double (c[++i]);
624
625 bezier_points[bi].x0 = px;
626 bezier_points[bi].y0 = py;
627 bi++;
628 }
629 } else if (i + 1 < c.length && c[i] == "H") {
630 while (is_point (c[i + 1])) {
631 bezier_points[bi].type = 'L';
632 bezier_points[bi].svg_type = 'H';
633
634 px = parse_double (c[++i]);
635
636 bezier_points[bi].x0 = px;
637 bezier_points[bi].y0 = py;
638 bi++;
639 }
640 } else if (c[i] == "v") {
641 while (i + 1 < c.length && is_point (c[i + 1])) {
642 bezier_points[bi].type = 'L';
643 bezier_points[bi].svg_type = 'v';
644
645 if (svg_glyph) {
646 py = py + parse_double (c[++i]);
647 } else {
648 py = py - parse_double (c[++i]);
649 }
650
651 bezier_points[bi].x0 = px;
652 bezier_points[bi].y0 = py;
653 bi++;
654 }
655 } else if (i + 1 < c.length && c[i] == "V") {
656 while (is_point (c[i + 1])) {
657 bezier_points[bi].type = 'L';
658 bezier_points[bi].svg_type = 'V';
659
660 if (svg_glyph) {
661 py = parse_double (c[++i]);
662 } else {
663 py = -parse_double (c[++i]);
664 }
665
666 bezier_points[bi].x0 = px;
667 bezier_points[bi].y0 = py;
668 bi++;
669 }
670 } else if (c[i] == "l") {
671 while (i + 2 < c.length && is_point (c[i + 1])) {
672 bezier_points[bi].type = 'L';
673 bezier_points[bi].svg_type = 'l';
674
675 cx = px + parse_double (c[++i]);
676
677 if (svg_glyph) {
678 cy = py + parse_double (c[++i]);
679 } else {
680 cy = py - parse_double (c[++i]);
681 }
682
683 px = cx;
684 py = cy;
685
686 bezier_points[bi].x0 = cx;
687 bezier_points[bi].y0 = cy;
688 bi++;
689 }
690 } else if (c[i] == "L") {
691 while (i + 2 < c.length && is_point (c[i + 1])) {
692 bezier_points[bi].type = 'L';
693 bezier_points[bi].svg_type = 'L';
694
695 cx = parse_double (c[++i]);
696
697 if (svg_glyph) {
698 cy = parse_double (c[++i]);
699 } else {
700 cy = -parse_double (c[++i]);
701 }
702
703 px = cx;
704 py = cy;
705
706 bezier_points[bi].x0 = cx;
707 bezier_points[bi].y0 = cy;
708 bi++;
709 }
710 } else if (c[i] == "c") {
711 while (i + 6 < c.length && is_point (c[i + 1])) {
712 bezier_points[bi].type = 'C';
713 bezier_points[bi].svg_type = 'C';
714
715 cx = px + parse_double (c[++i]);
716
717 if (svg_glyph) {
718 cy = py + parse_double (c[++i]);
719 } else {
720 cy = py - parse_double (c[++i]);
721 }
722
723 bezier_points[bi].x0 = cx;
724 bezier_points[bi].y0 = cy;
725
726 cx = px + parse_double (c[++i]);
727
728 if (svg_glyph) {
729 cy = py + parse_double (c[++i]);
730 } else {
731 cy = py - parse_double (c[++i]);
732 }
733
734 px2 = cx;
735 py2 = cy;
736
737 bezier_points[bi].x1 = px2;
738 bezier_points[bi].y1 = py2;
739
740 cx = px + parse_double (c[++i]);
741
742 if (svg_glyph) {
743 cy = py + parse_double (c[++i]);
744 } else {
745 cy = py + -parse_double (c[++i]);
746 }
747
748 bezier_points[bi].x2 = cx;
749 bezier_points[bi].y2 = cy;
750
751 px = cx;
752 py = cy;
753
754 bi++;
755 }
756 } else if (c[i] == "C") {
757 while (i + 6 < c.length && is_point (c[i + 1])) {
758 bezier_points[bi].type = 'C';
759 bezier_points[bi].svg_type = 'C';
760
761 cx = parse_double (c[++i]);
762
763 if (svg_glyph) {
764 cy = parse_double (c[++i]);
765 } else {
766 cy = -parse_double (c[++i]);
767 }
768
769 bezier_points[bi].x0 = cx;
770 bezier_points[bi].y0 = cy;
771
772 cx = parse_double (c[++i]);
773
774 if (svg_glyph) {
775 cy = parse_double (c[++i]);
776 } else {
777 cy = -parse_double (c[++i]);
778 }
779
780 px2 = cx;
781 py2 = cy;
782
783 bezier_points[bi].x1 = cx;
784 bezier_points[bi].y1 = cy;
785
786 cx = parse_double (c[++i]);
787
788 if (svg_glyph) {
789 cy = parse_double (c[++i]);
790 } else {
791 cy = -parse_double (c[++i]);
792 }
793
794 bezier_points[bi].x2 = cx;
795 bezier_points[bi].y2 = cy;
796
797 px = cx;
798 py = cy;
799
800 bi++;
801 }
802 } else if (c[i] == "q") {
803 while (i + 4 < c.length && is_point (c[i + 1])) {
804 bezier_points[bi].type = 'Q';
805 bezier_points[bi].svg_type = 'q';
806
807 cx = px + parse_double (c[++i]);
808
809 if (svg_glyph) {
810 cy = py + parse_double (c[++i]);
811 } else {
812 cy = py - parse_double (c[++i]);
813 }
814
815 bezier_points[bi].x0 = cx;
816 bezier_points[bi].y0 = cy;
817
818 px2 = cx;
819 py2 = cy;
820
821 cx = px + parse_double (c[++i]);
822
823 if (svg_glyph) {
824 cy = py + parse_double (c[++i]);
825 } else {
826 cy = py - parse_double (c[++i]);
827 }
828
829 bezier_points[bi].x1 = cx;
830 bezier_points[bi].y1 = cy;
831
832 px = cx;
833 py = cy;
834
835 bi++;
836 }
837 } else if (c[i] == "Q") {
838
839 while (i + 4 < c.length && is_point (c[i + 1])) {
840 bezier_points[bi].type = 'Q';
841 bezier_points[bi].svg_type = 'Q';
842
843 cx = parse_double (c[++i]);
844
845 if (svg_glyph) {
846 cy = parse_double (c[++i]);
847 } else {
848 cy = -parse_double (c[++i]);
849 }
850
851 bezier_points[bi].x0 = cx;
852 bezier_points[bi].y0 = cy;
853
854 px2 = cx;
855 py2 = cy;
856
857 cx = parse_double (c[++i]);
858
859 if (svg_glyph) {
860 cy = parse_double (c[++i]);
861 } else {
862 cy = -parse_double (c[++i]);
863 }
864
865 px = cx;
866 py = cy;
867
868 bezier_points[bi].x1 = cx;
869 bezier_points[bi].y1 = cy;
870
871 bi++;
872 }
873 } else if (c[i] == "t") {
874 while (i + 2 < c.length && is_point (c[i + 1])) {
875 bezier_points[bi].type = 'Q';
876 bezier_points[bi].svg_type = 't';
877
878 // the first point is the reflection
879 cx = 2 * px - px2;
880 cy = 2 * py - py2; // if (svg_glyph) ?
881
882 bezier_points[bi].x0 = cx;
883 bezier_points[bi].y0 = cy;
884
885 px2 = cx;
886 py2 = cy;
887
888 cx = px + parse_double (c[++i]);
889
890 if (svg_glyph) {
891 cy = py + parse_double (c[++i]);
892 } else {
893 cy = py - parse_double (c[++i]);
894 }
895
896 px = cx;
897 py = cy;
898
899 bezier_points[bi].x1 = px;
900 bezier_points[bi].y1 = py;
901
902 bi++;
903 }
904 } else if (c[i] == "T") {
905 while (i + 2 < c.length && is_point (c[i + 1])) {
906 bezier_points[bi].type = 'Q';
907 bezier_points[bi].svg_type = 'T';
908
909 // the reflection
910 cx = 2 * px - px2;
911 cy = 2 * py - py2; // if (svg_glyph) ?
912
913 bezier_points[bi].x0 = cx;
914 bezier_points[bi].y0 = cy;
915
916 px2 = cx;
917 py2 = cy;
918
919 cx = parse_double (c[++i]);
920
921 if (svg_glyph) {
922 cy = parse_double (c[++i]);
923 } else {
924 cy = -parse_double (c[++i]);
925 }
926
927 px = cx;
928 py = cy;
929
930 bezier_points[bi].x1 = px;
931 bezier_points[bi].y1 = py;
932
933 bi++;
934 }
935 } else if (c[i] == "s") {
936 while (i + 4 < c.length && is_point (c[i + 1])) {
937 bezier_points[bi].type = 'C';
938 bezier_points[bi].svg_type = 's';
939
940 // the first point is the reflection
941 cx = 2 * px - px2;
942 cy = 2 * py - py2; // if (svg_glyph) ?
943
944 bezier_points[bi].x0 = cx;
945 bezier_points[bi].y0 = cy;
946
947 cx = px + parse_double (c[++i]);
948
949 if (svg_glyph) {
950 cy = py + parse_double (c[++i]);
951 } else {
952 cy = py - parse_double (c[++i]);
953 }
954
955 px2 = cx;
956 py2 = cy;
957
958 bezier_points[bi].x1 = px2;
959 bezier_points[bi].y1 = py2;
960
961 cx = px + parse_double (c[++i]);
962
963 if (svg_glyph) {
964 cy = py + parse_double (c[++i]);
965 } else {
966 cy = py - parse_double (c[++i]);
967 }
968
969 bezier_points[bi].x2 = cx;
970 bezier_points[bi].y2 = cy;
971
972 px = cx;
973 py = cy;
974
975 bi++;
976 }
977 } else if (c[i] == "S") {
978 while (i + 4 < c.length && is_point (c[i + 1])) {
979 bezier_points[bi].type = 'C';
980 bezier_points[bi].svg_type = 'S';
981
982 // the reflection
983 cx = 2 * px - px2;
984 cy = 2 * py - py2; // if (svg_glyph) ?
985
986 bezier_points[bi].x0 = cx;
987 bezier_points[bi].y0 = cy;
988
989 // the other two are regular cubic points
990 cx = parse_double (c[++i]);
991
992 if (svg_glyph) {
993 cy = parse_double (c[++i]);
994 } else {
995 cy = -parse_double (c[++i]);
996 }
997
998 px2 = cx;
999 py2 = cy;
1000
1001 bezier_points[bi].x1 = px2;
1002 bezier_points[bi].y1 = py2;
1003
1004 cx = parse_double (c[++i]);
1005
1006 if (svg_glyph) {
1007 cy = parse_double (c[++i]);
1008 } else {
1009 cy = -parse_double (c[++i]);
1010 }
1011
1012 bezier_points[bi].x2 = cx;
1013 bezier_points[bi].y2 = cy;
1014
1015 px = cx;
1016 py = cy;
1017
1018 bi++;
1019 }
1020 } else if (c[i] == "a") {
1021 while (i + 7 < c.length && is_point (c[i + 1])) {
1022 arc_rx = parse_double (c[++i]);
1023 arc_ry = parse_double (c[++i]);
1024
1025 arc_rotation = parse_double (c[++i]);
1026 large_arc = parse_int (c[++i]);
1027 arc_sweep = parse_int (c[++i]);
1028
1029 cx = px + parse_double (c[++i]);
1030
1031 if (svg_glyph) {
1032 cy = py + parse_double (c[++i]);
1033 } else {
1034 cy = py - parse_double (c[++i]);
1035 }
1036
1037 arc_dest_x = cx;
1038 arc_dest_y = cy;
1039
1040 add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy);
1041
1042 px = cx;
1043 py = cy;
1044
1045
1046 }
1047 } else if (i + 7 < c.length && c[i] == "A") {
1048 while (is_point (c[i + 1])) {
1049 arc_rx = parse_double (c[++i]);
1050 arc_ry = parse_double (c[++i]);
1051
1052 arc_rotation = parse_double (c[++i]);
1053 large_arc = parse_int (c[++i]);
1054 arc_sweep = parse_int (c[++i]);
1055
1056 cx = parse_double (c[++i]);
1057
1058 if (svg_glyph) {
1059 cy = parse_double (c[++i]);
1060 } else {
1061 cy = -parse_double (c[++i]);
1062 }
1063
1064 arc_dest_x = cx;
1065 arc_dest_y = cy;
1066
1067 add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy);
1068
1069 px = cx;
1070 py = cy;
1071
1072
1073 }
1074 } else if (c[i] == "z") {
1075 bezier_points[bi].type = 'z';
1076 bezier_points[bi].svg_type = 'z';
1077
1078 bi++;
1079 } else if (c[i] == "Z") {
1080 bezier_points[bi].type = 'z';
1081 bezier_points[bi].svg_type = 'z';
1082
1083 bi++;
1084 } else if (c[i] == "") {
1085 } else if (c[i] == " ") {
1086 } else {
1087 warning (@"Unknown instruction: $(c[i])");
1088 }
1089 }
1090
1091 if (bi == 0) {
1092 warning ("No points in path.");
1093 return path_list;
1094 }
1095
1096 // TODO: this code assumes that all paths are closed since stroke has not been implemented yet
1097 if (bezier_points[bi - 1].type != 'z') {
1098 bezier_points[bi].type = 'z';
1099 bezier_points[bi].svg_type = 'z';
1100 bi++;
1101 }
1102
1103 move_and_resize (bezier_points, bi, svg_glyph, units, glyph);
1104
1105 if (format == SvgFormat.ILLUSTRATOR) {
1106 path_list = create_paths_illustrator (bezier_points, bi);
1107 } else {
1108 path_list = create_paths_inkscape (bezier_points, bi);
1109 }
1110
1111 // TODO: Find out if it is possible to tie handles.
1112 return path_list;
1113 }
1114
1115 void move_and_resize (BezierPoints[] b, int num_b, bool svg_glyph, double units, Glyph glyph) {
1116 Font font = BirdFont.get_current_font ();
1117
1118 for (int i = 0; i < num_b; i++) {
1119 // resize all points
1120 b[i].x0 *= units;
1121 b[i].y0 *= units;
1122 b[i].x1 *= units;
1123 b[i].y1 *= units;
1124 b[i].x2 *= units;
1125 b[i].y2 *= units;
1126
1127 // move all points
1128 if (svg_glyph) {
1129 b[i].x0 += glyph.left_limit;
1130 b[i].y0 += font.base_line;
1131 b[i].x1 += glyph.left_limit;
1132 b[i].y1 += font.base_line;
1133 b[i].x2 += glyph.left_limit;
1134 b[i].y2 += font.base_line;
1135 } else {
1136 b[i].x0 += glyph.left_limit;
1137 b[i].y0 += font.top_position;
1138 b[i].x1 += glyph.left_limit;
1139 b[i].y1 += font.top_position;
1140 b[i].x2 += glyph.left_limit;
1141 b[i].y2 += font.top_position;
1142 }
1143 }
1144 }
1145
1146 void find_last_handle (int start_index, BezierPoints[] b, int num_b, out double left_x, out double left_y, out PointType last_type) {
1147 BezierPoints last = new BezierPoints ();
1148
1149 left_x = 0;
1150 left_y = 0;
1151 last_type = PointType.NONE;
1152
1153 return_if_fail (b.length != 0);
1154 return_if_fail (b[0].type != 'z');
1155 return_if_fail (num_b < b.length);
1156
1157 for (int i = start_index; i < num_b; i++) {
1158 switch (b[i].type) {
1159 case 'Q':
1160 break;
1161 case 'C':
1162 break;
1163 case 'z':
1164 if (b[i - 1].type == 'Q') {
1165 return_if_fail (i >= 1);
1166 left_x = b[i - 1].x0;
1167 left_y = b[i - 1].y0;
1168 last_type = PointType.QUADRATIC;
1169 } else if (b[i - 1].type == 'C') {
1170 return_if_fail (i >= 1);
1171 left_x = b[i - 1].x1;
1172 left_y = b[i - 1].y1;
1173 last_type = PointType.CUBIC;
1174 } else if (b[i - 1].type == 'S') {
1175 return_if_fail (i >= 1);
1176 left_x = b[i - 1].x1;
1177 left_y = b[i - 1].y1;
1178 last_type = PointType.CUBIC;
1179 }else if (b[i - 1].type == 'L' || last.type == 'M') {
1180 return_if_fail (i >= 2); // FIXME: -2 can be C or L
1181 left_x = b[i - 2].x0 + (b[i - 1].x0 - b[i - 2].x0) / 3.0;
1182 left_y = b[i - 2].y0 + (b[i - 1].y0 - b[i - 2].y0) / 3.0;
1183 last_type = PointType.LINE_CUBIC;
1184 } else {
1185 warning (@"Unexpected type. $(b[i - 1])\n");
1186 }
1187 return;
1188 default:
1189 break;
1190 }
1191
1192 last = b[i];
1193 }
1194
1195 warning ("Expecting z");
1196 }
1197
1198 PathList create_paths_inkscape (BezierPoints[] b, int num_b) {
1199 double last_x;
1200 double last_y;
1201 PointType last_type;
1202 Path path;
1203 PathList path_list = new PathList ();
1204 EditPoint ep = new EditPoint ();
1205 Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> ();
1206
1207 path = new Path ();
1208
1209 if (num_b == 0) {
1210 warning ("No SVG data");
1211 return path_list;
1212 }
1213
1214 if (b[0].type != 'M') {
1215 warning ("Path must begin with M or m.");
1216 return path_list;
1217 }
1218
1219 find_last_handle (0, b, num_b, out last_x, out last_y, out last_type);
1220
1221 for (int i = 0; i < num_b; i++) {
1222 if (b[i].type == '\0') {
1223 warning ("Parser error.");
1224 return path_list;
1225 }
1226
1227 if (b[i].type == 'z') {
1228 path.close ();
1229 path.create_list ();
1230 path.recalculate_linear_handles ();
1231 path_list.add (path);
1232 path = new Path ();
1233
1234 if (i + 1 >= num_b) {
1235 break;
1236 } else {
1237 find_last_handle (i + 1, b, num_b, out last_x, out last_y, out last_type);
1238 }
1239 }
1240
1241 return_val_if_fail (i + 1 < num_b, path_list);
1242
1243 if (b[i].type == 'M') {
1244 ep = path.add (b[i].x0, b[i].y0);
1245 ep.set_point_type (PointType.CUBIC);
1246
1247 ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
1248
1249 if (i == 0 || (b[i - 1].type == 'z')) {
1250 ep.get_left_handle ().set_point_type (last_type);
1251 ep.get_left_handle ().move_to_coordinate (last_x, last_y);
1252 } else {
1253 if (b[i - 1].type == 'C' || b[i - 1].type == 'S') {
1254 ep.get_left_handle ().set_point_type (PointType.CUBIC);
1255 ep.get_left_handle ().move_to_coordinate (b[i + 1].x1, b[i + 1].y1);
1256 }
1257
1258 if (b[i + 1].type == 'C' || b[i - 1].type == 'S') {
1259 ep.get_right_handle ().set_point_type (PointType.CUBIC);
1260 ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);
1261 } else if (b[i + 1].type == 'L' || b[i + 1].type == 'M') {
1262 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1263 }
1264 }
1265 }
1266
1267 if (b[i].type == 'L') {
1268 return_val_if_fail (i != 0, path_list);
1269
1270 ep = path.add (b[i].x0, b[i].y0);
1271 ep.set_point_type (PointType.CUBIC);
1272 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1273 ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
1274
1275 if (b[i + 1].type == 'L' || b[i + 1].type == 'M' || b[i + 1].type == 'z') {
1276 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1277 }
1278
1279 if (b[i -1].type == 'L' || b[i - 1].type == 'M') {
1280 ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
1281 }
1282 }
1283
1284 if (b[i].type == 'Q') {
1285 return_val_if_fail (i != 0, path_list);
1286
1287 ep.set_point_type (PointType.QUADRATIC);
1288
1289 ep.get_right_handle ().set_point_type (PointType.QUADRATIC);
1290 ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0);
1291
1292 if (b[i + 1].type != 'z') {
1293 ep = path.add (b[i].x1, b[i].y1);
1294
1295 ep.get_left_handle ().set_point_type (PointType.QUADRATIC);
1296 ep.get_left_handle ().move_to_coordinate (b[i].x0, b[i].y0);
1297 }
1298 }
1299
1300 if (b[i].type == 'C' || b[i].type == 'S') {
1301 return_val_if_fail (i != 0, path_list);
1302
1303 ep.set_point_type (PointType.CUBIC);
1304
1305 ep.get_right_handle ().set_point_type (PointType.CUBIC);
1306 ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0);
1307
1308 if (b[i].type == 'S') {
1309 smooth_points.add (ep);
1310 }
1311
1312 if (b[i + 1].type != 'z') {
1313 ep = path.add (b[i].x2, b[i].y2);
1314
1315 ep.get_left_handle ().set_point_type (PointType.CUBIC);
1316 ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1);
1317 }
1318 }
1319 }
1320
1321 foreach (EditPoint e in smooth_points) {
1322 e.set_point_type (PointType.LINE_DOUBLE_CURVE);
1323 e.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);
1324 e.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);
1325 }
1326
1327 foreach (EditPoint e in smooth_points) {
1328 e.recalculate_linear_handles ();
1329 }
1330
1331
1332 for (int i = 0; i < 3; i++) {
1333 foreach (EditPoint e in smooth_points) {
1334 e.set_tie_handle (true);
1335 e.process_tied_handle ();
1336 }
1337 }
1338
1339 if (path.points.size > 0) {
1340 path_list.add (path);
1341 }
1342
1343 foreach (Path p in path_list.paths) {
1344 p.remove_points_on_points ();
1345 }
1346
1347 return path_list;
1348 }
1349
1350 PathList create_paths_illustrator (BezierPoints[] b, int num_b) {
1351 Path path;
1352 PathList path_list = new PathList ();
1353 EditPoint ep;
1354 bool first_point = true;
1355 double first_left_x, first_left_y;
1356 Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> ();
1357
1358 if (num_b > b.length) {
1359 warning ("num_b > b.length: $num_b > $(b.length)");
1360 return path_list;
1361 }
1362
1363 path = new Path ();
1364
1365 if (num_b <= 1) {
1366 warning ("No SVG data");
1367 return path_list;
1368 }
1369
1370 first_left_x = 0;
1371 first_left_y = 0;
1372
1373 for (int i = 0; i < num_b; i++) {
1374
1375 if (b[i].type == '\0') {
1376 warning ("Parser error.");
1377 return path_list;
1378 } else if (b[i].type == 'z') {
1379 path.close ();
1380 path.create_list ();
1381
1382 if (b[1].type == 'C' || b[1].type == 'S') {
1383 return_val_if_fail (path.points.size != 0, path_list);
1384 ep = path.points.get (path.points.size - 1);
1385 ep.get_right_handle ().set_point_type (PointType.CUBIC);
1386 ep.get_right_handle ().move_to_coordinate (b[1].x0, b[1].y0);
1387 }
1388
1389 path.recalculate_linear_handles ();
1390 path_list.add (path);
1391
1392 path = new Path ();
1393 first_point = true;
1394 } else if (b[i].type == 'M') {
1395 } else if (b[i].type == 'L') {
1396
1397 if (first_point) {
1398 first_left_x = b[i].x0;
1399 first_left_y = b[i].y0;
1400 }
1401
1402 ep = path.add (b[i].x0, b[i].y0);
1403 ep.set_point_type (PointType.LINE_CUBIC); // TODO: quadratic
1404 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);
1405
1406 if (b[i -1].type == 'L' || first_point) {
1407 //ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);
1408 }
1409
1410 if (b[i + 1].type == 'C' || b[i + 1].type == 'S') {
1411 return_val_if_fail (i + 1 < num_b, path_list);
1412 ep.get_right_handle ().set_point_type (PointType.CUBIC);
1413 ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);
1414 }
1415
1416 first_point = false;
1417 } else if (b[i].type == 'Q') {
1418 warning ("Illustrator does not support quadratic control points.");
1419 warning (@"$(b[i])\n");
1420 } else if (b[i].type == 'C' || b[i].type == 'S') {
1421
1422 if (first_point) {
1423 first_left_x = b[i].x0;
1424 first_left_y = b[i].y0;
1425 }
1426
1427 ep = path.add (b[i].x2, b[i].y2);
1428 ep.set_point_type (PointType.CUBIC);
1429
1430 ep.get_right_handle ().set_point_type (PointType.CUBIC);
1431 ep.get_left_handle ().set_point_type (PointType.CUBIC);
1432
1433 ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1);
1434
1435 if (b[i].type == 'S') {
1436 smooth_points.add (ep);
1437 }
1438
1439 if (b[i + 1].type != 'z') {
1440 ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);
1441 } else {
1442 ep.get_right_handle ().move_to_coordinate (first_left_x, first_left_y);
1443 }
1444
1445 first_point = false;
1446 } else {
1447 warning ("Unknown control point type.");
1448 warning (@"$(b[i])\n");
1449 }
1450 }
1451
1452 foreach (EditPoint e in smooth_points) {
1453 e.set_point_type (PointType.LINE_DOUBLE_CURVE);
1454 e.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);
1455 e.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);
1456 }
1457
1458 foreach (EditPoint e in smooth_points) {
1459 e.recalculate_linear_handles ();
1460 }
1461
1462
1463 for (int i = 0; i < 3; i++) {
1464 foreach (EditPoint e in smooth_points) {
1465 e.set_tie_handle (true);
1466 e.process_tied_handle ();
1467 }
1468 }
1469
1470 if (path.points.size > 0) {
1471 warning ("Open path.");
1472 path_list.add (path);
1473 }
1474
1475 foreach (Path p in path_list.paths) {
1476 p.remove_points_on_points ();
1477 }
1478
1479 return path_list;
1480 }
1481
1482 // TODO: implement a default svg parser
1483
1484 static int parse_int (string? s) {
1485 if (is_null (s)) {
1486 warning ("null instead of string");
1487 return 0;
1488 }
1489
1490 if (!is_point ((!) s)) {
1491 warning (@"Expecting an integer got: $((!) s)");
1492 return 0;
1493 }
1494
1495 return int.parse ((!) s);
1496 }
1497
1498 static double parse_double (string? s) {
1499 if (is_null (s)) {
1500 warning ("Got null instead of expected string.");
1501 return 0;
1502 }
1503
1504 if (!is_point ((!) s)) {
1505 warning (@"Expecting a double got: $((!) s)");
1506 return 0;
1507 }
1508
1509 return double.parse ((!) s);
1510 }
1511
1512 static bool is_point (string? s) {
1513 if (s == null) {
1514 warning ("s is null");
1515 return false;
1516 }
1517
1518 return double.try_parse ((!) s);
1519 }
1520
1521 Path parse_polygon_data (string polygon_points) {
1522 string data = add_separators (polygon_points);
1523 string[] c = data.split (" ");
1524 Path path;
1525 BezierPoints[] bezier_points = new BezierPoints[c.length + 1];
1526 int bi;
1527 Glyph g;
1528
1529 bi = 0;
1530 for (int i = 0; i < c.length - 1; i += 2) {
1531 if (i + 1 >= c.length) {
1532 warning ("No y value.");
1533 break;
1534 }
1535
1536 if (bi >= bezier_points.length) {
1537 warning ("End of bezier_points");
1538 break;
1539 }
1540
1541 bezier_points[bi] = new BezierPoints ();
1542 bezier_points[bi].type == 'L';
1543 bezier_points[bi].x0 = parse_double (c[i]);
1544 bezier_points[bi].y0 = -parse_double (c[i + 1]);
1545 bi++;
1546 }
1547
1548 g = MainWindow.get_current_glyph ();
1549 move_and_resize (bezier_points, bi, false, 1, g);
1550
1551 path = new Path ();
1552 for (int i = 0; i < bi; i++) {
1553 path.add (bezier_points[i].x0, bezier_points[i].y0);
1554 }
1555
1556 path.close ();
1557 path.create_list ();
1558 path.recalculate_linear_handles ();
1559
1560 return path;
1561 }
1562 }
1563
1564 }
1565