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