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