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