The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

SvgParser.vala in libbirdfont

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git

Revisions

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