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