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 580 x = 0; 581 y = 0; 582 x2 = 0; 583 y2 = 0; 584 585 foreach (Attribute attr in tag.get_attributes ()) { 586 if (attr.get_name () == "x") { 587 x = parse_double (attr.get_content ()); 588 } 589 590 if (attr.get_name () == "y") { 591 y = -parse_double (attr.get_content ()); 592 } 593 594 if (attr.get_name () == "width") { 595 x2 = parse_double (attr.get_content ()); 596 } 597 598 if (attr.get_name () == "height") { 599 y2 = -parse_double (attr.get_content ()); 600 } 601 } 602 603 x2 += x; 604 y2 += y; 605 606 bezier_points = new BezierPoints[4]; 607 bezier_points[0] = new BezierPoints (); 608 bezier_points[0].type == 'L'; 609 bezier_points[0].x0 = x; 610 bezier_points[0].y0 = y; 611 612 bezier_points[1] = new BezierPoints (); 613 bezier_points[1].type == 'L'; 614 bezier_points[1].x0 = x2; 615 bezier_points[1].y0 = y; 616 617 bezier_points[2] = new BezierPoints (); 618 bezier_points[2].type == 'L'; 619 bezier_points[2].x0 = x2; 620 bezier_points[2].y0 = y2; 621 622 bezier_points[3] = new BezierPoints (); 623 bezier_points[3].type == 'L'; 624 bezier_points[3].x0 = x; 625 bezier_points[3].y0 = y2; 626 627 g = MainWindow.get_current_glyph (); 628 move_and_resize (bezier_points, 4, false, 1, g); 629 630 p = new Path (); 631 632 p.add (bezier_points[0].x0, bezier_points[0].y0); 633 p.add (bezier_points[1].x0, bezier_points[1].y0); 634 p.add (bezier_points[2].x0, bezier_points[2].y0); 635 p.add (bezier_points[3].x0, bezier_points[3].y0); 636 637 p.close (); 638 p.create_list (); 639 p.recalculate_linear_handles (); 640 641 npl.add (p); 642 643 // FIXME: right layer for other transforms 644 foreach (Attribute attr in tag.get_attributes ()) { 645 if (attr.get_name () == "transform") { 646 transform_paths (attr.get_content (), npl); 647 } 648 } 649 650 pl.paths.append (npl); 651 } 652 653 private void parse_polygon (Tag tag, Layer pl) { 654 Path p; 655 656 foreach (Attribute attr in tag.get_attributes ()) { 657 if (attr.get_name () == "points") { 658 p = parse_polygon_data (attr.get_content ()); 659 pl.paths.add (p); 660 } 661 } 662 } 663 664 private void parse_path (Tag tag, Layer pl) { 665 Glyph glyph = MainWindow.get_current_glyph (); 666 PathList path_list = new PathList (); 667 SvgStyle style = new SvgStyle (); 668 bool hidden = false; 669 670 foreach (Attribute attr in tag.get_attributes ()) { 671 if (attr.get_name () == "d") { 672 path_list = parse_svg_data (attr.get_content (), glyph); 673 } 674 675 if (attr.get_name () == "style") { 676 style = SvgStyle.parse (attr.get_content ()); 677 } 678 679 if (attr.get_name () == "display" && attr.get_content () == "none") { 680 hidden = true; 681 } 682 683 if (attr.get_name () == "visibility" 684 && (attr.get_content () == "hidden" 685 || attr.get_content () == "collapse")) { 686 hidden = true; 687 } 688 } 689 690 if (hidden) { 691 return; 692 } 693 694 pl.paths.append (path_list); 695 style.apply (path_list); 696 697 // assume the even odd rule is applied and convert the path 698 // to a path using the non-zero rule 699 int inside_count; 700 bool inside; 701 foreach (Path p1 in pl.paths.paths) { 702 inside_count = 0; 703 704 foreach (Path p2 in pl.paths.paths) { 705 if (p1 != p2) { 706 inside = true; 707 708 foreach (EditPoint ep in p1.points) { 709 if (!is_inside (ep, p2)) { 710 inside = false; 711 } 712 } 713 714 if (inside) { 715 inside_count++; 716 } 717 } 718 } 719 720 if (inside_count % 2 == 0) { 721 p1.force_direction (Direction.CLOCKWISE); 722 } else { 723 p1.force_direction (Direction.COUNTER_CLOCKWISE); 724 } 725 } 726 727 foreach (Attribute attr in tag.get_attributes ()) { 728 if (attr.get_name () == "transform") { 729 transform_paths (attr.get_content (), path_list); 730 } 731 } 732 } 733 734 public static void create_lines_for_segment (Path path, EditPoint start, EditPoint end, double tolerance) { 735 double x1, x2, x3; 736 double y1, y2, y3; 737 double step_start, step, step_end; 738 739 path.add (start.x, start.y); 740 741 step_start = 0; 742 step = 0.5; 743 step_end = 1; 744 745 while (true) { 746 Path.get_point_for_step (start, end, step_start, out x1, out y1); 747 Path.get_point_for_step (start, end, step, out x2, out y2); 748 Path.get_point_for_step (start, end, step_end, out x3, out y3); 749 750 if (!StrokeTool.is_flat (x1, y1, x2, y2, x3, y3, tolerance) 751 && step_end - step / 2.0 > step_start 752 && step_end - step / 2.0 > 0.1 753 && step > 0.05 754 && Path.distance_to_point (start, end) > 1) { 755 756 step /= 2.0; 757 758 if (step < 0.05) { 759 step = 0.05; 760 } else { 761 step_end = step_start + 2 * step; 762 } 763 } else { 764 path.add (x3, y3); 765 766 if (step_end + step < 1) { 767 step_start = step_end; 768 step_end += step; 769 } else { 770 break; 771 } 772 } 773 } 774 } 775 776 public static Path get_lines (Path p) { 777 EditPoint start; 778 Path path = new Path (); 779 780 if (p.points.size == 0) { 781 return path; 782 } 783 784 // create a set of straight lines 785 start = p.points.get (p.points.size - 1); 786 787 foreach (EditPoint end in p.points) { 788 create_lines_for_segment (path, start, end, 1); 789 start = end; 790 } 791 792 return path; 793 } 794 795 /** Check if a point is inside using the even odd fill rule. 796 * The path should only have straight lines. 797 */ 798 public static bool is_inside (EditPoint point, Path path) { 799 EditPoint prev; 800 bool inside = false; 801 802 if (path.points.size <= 1) { 803 return false; 804 } 805 806 if (!(path.xmin <= point.x <= path.xmax)) { 807 return false; 808 } 809 810 if (!(path.ymin <= point.y <= path.ymax)) { 811 return false; 812 } 813 814 prev = path.points.get (path.points.size - 1); 815 816 foreach (EditPoint p in path.points) { 817 if ((p.y > point.y) != (prev.y > point.y) 818 && point.x < (prev.x - p.x) * (point.y - p.y) / (prev.y - p.y) + p.x) { 819 inside = !inside; 820 } 821 822 prev = p; 823 } 824 825 return inside; 826 } 827 828 /** Add space as separator to svg data. 829 * @param d svg data 830 */ 831 static string add_separators (string d) { 832 string data = d; 833 834 data = data.replace (",", " "); 835 data = data.replace ("a", " a "); 836 data = data.replace ("A", " A "); 837 data = data.replace ("m", " m "); 838 data = data.replace ("M", " M "); 839 data = data.replace ("h", " h "); 840 data = data.replace ("H", " H "); 841 data = data.replace ("v", " v "); 842 data = data.replace ("V", " V "); 843 data = data.replace ("l", " l "); 844 data = data.replace ("L", " L "); 845 data = data.replace ("q", " q "); 846 data = data.replace ("Q", " Q "); 847 data = data.replace ("c", " c "); 848 data = data.replace ("C", " C "); 849 data = data.replace ("t", " t "); 850 data = data.replace ("T", " T "); 851 data = data.replace ("s", " s "); 852 data = data.replace ("S", " S "); 853 data = data.replace ("zM", " z M "); 854 data = data.replace ("zm", " z m "); 855 data = data.replace ("z", " z "); 856 data = data.replace ("Z", " Z "); 857 data = data.replace ("-", " -"); 858 data = data.replace ("e -", "e-"); // minus can be either separator or a negative exponent 859 data = data.replace ("\t", " "); 860 data = data.replace ("\r\n", " "); 861 data = data.replace ("\n", " "); 862 863 // use only a single space as separator 864 while (data.index_of (" ") > -1) { 865 data = data.replace (" ", " "); 866 } 867 868 return data; 869 } 870 871 public void add_path_to_glyph (string d, Glyph g, bool svg_glyph = false, double units = 1) { 872 PathList p = parse_svg_data (d, g, svg_glyph, units); 873 foreach (Path path in p.paths) { 874 g.add_path (path); 875 } 876 } 877 878 /** 879 * @param d svg data 880 * @param glyph use lines from this glyph but don't add the generated paths 881 * @param svg_glyph parse svg glyph with origo in lower left corner 882 * 883 * @return the new paths 884 */ 885 public PathList parse_svg_data (string d, Glyph glyph, bool svg_glyph = false, double units = 1) { 886 double px = 0; 887 double py = 0; 888 double px2 = 0; 889 double py2 = 0; 890 double cx = 0; 891 double cy = 0; 892 string data; 893 Font font; 894 PathList path_list = new PathList (); 895 BezierPoints[] bezier_points; 896 string[] c; 897 double arc_rx, arc_ry; 898 double arc_rotation; 899 int large_arc; 900 int arc_sweep; 901 double arc_dest_x, arc_dest_y; 902 903 font = BirdFont.get_current_font (); 904 905 data = add_separators (d); 906 c = data.split (" "); 907 bezier_points = new BezierPoints[8 * c.length + 1]; // the arc instruction can use up to eight points 908 909 for (int i = 0; i < 2 * c.length + 1; i++) { 910 bezier_points[i] = new BezierPoints (); 911 } 912 913 int bi = 0; 914 915 // parse path 916 int i = -1; 917 while (++i < c.length && bi < bezier_points.length) { 918 if (c[i] == "m") { 919 while (i + 2 < c.length && is_point (c[i + 1])) { 920 bezier_points[bi].type = 'M'; 921 bezier_points[bi].svg_type = 'm'; 922 923 px += parse_double (c[++i]); 924 925 if (svg_glyph) { 926 py += parse_double (c[++i]); 927 } else { 928 py += -parse_double (c[++i]); 929 } 930 931 bezier_points[bi].x0 = px; 932 bezier_points[bi].y0 = py; 933 bi++; 934 } 935 } else if (c[i] == "M") { 936 while (i + 2 < c.length && is_point (c[i + 1])) { 937 bezier_points[bi].type = 'M'; 938 bezier_points[bi].svg_type = 'M'; 939 940 px = parse_double (c[++i]); 941 942 if (svg_glyph) { 943 py = parse_double (c[++i]); 944 } else { 945 py = -parse_double (c[++i]); 946 } 947 948 bezier_points[bi].x0 = px; 949 bezier_points[bi].y0 = py; 950 bi++; 951 } 952 } else if (c[i] == "h") { 953 while (i + 1 < c.length && is_point (c[i + 1])) { 954 bezier_points[bi].type = 'L'; 955 bezier_points[bi].svg_type = 'h'; 956 957 px += parse_double (c[++i]); 958 959 bezier_points[bi].x0 = px; 960 bezier_points[bi].y0 = py; 961 bi++; 962 } 963 } else if (i + 1 < c.length && c[i] == "H") { 964 while (is_point (c[i + 1])) { 965 bezier_points[bi].type = 'L'; 966 bezier_points[bi].svg_type = 'H'; 967 968 px = parse_double (c[++i]); 969 970 bezier_points[bi].x0 = px; 971 bezier_points[bi].y0 = py; 972 bi++; 973 } 974 } else if (c[i] == "v") { 975 while (i + 1 < c.length && is_point (c[i + 1])) { 976 bezier_points[bi].type = 'L'; 977 bezier_points[bi].svg_type = 'v'; 978 979 if (svg_glyph) { 980 py = py + parse_double (c[++i]); 981 } else { 982 py = py - parse_double (c[++i]); 983 } 984 985 bezier_points[bi].x0 = px; 986 bezier_points[bi].y0 = py; 987 bi++; 988 } 989 } else if (i + 1 < c.length && c[i] == "V") { 990 while (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 = parse_double (c[++i]); 996 } else { 997 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 (c[i] == "l") { 1005 while (i + 2 < c.length && is_point (c[i + 1])) { 1006 bezier_points[bi].type = 'L'; 1007 bezier_points[bi].svg_type = 'l'; 1008 1009 cx = px + parse_double (c[++i]); 1010 1011 if (svg_glyph) { 1012 cy = py + parse_double (c[++i]); 1013 } else { 1014 cy = py - parse_double (c[++i]); 1015 } 1016 1017 px = cx; 1018 py = cy; 1019 1020 bezier_points[bi].x0 = cx; 1021 bezier_points[bi].y0 = cy; 1022 bi++; 1023 } 1024 } else if (c[i] == "L") { 1025 while (i + 2 < c.length && is_point (c[i + 1])) { 1026 bezier_points[bi].type = 'L'; 1027 bezier_points[bi].svg_type = 'L'; 1028 1029 cx = parse_double (c[++i]); 1030 1031 if (svg_glyph) { 1032 cy = parse_double (c[++i]); 1033 } else { 1034 cy = -parse_double (c[++i]); 1035 } 1036 1037 px = cx; 1038 py = cy; 1039 1040 bezier_points[bi].x0 = cx; 1041 bezier_points[bi].y0 = cy; 1042 bi++; 1043 } 1044 } else if (c[i] == "c") { 1045 while (i + 6 < c.length && is_point (c[i + 1])) { 1046 bezier_points[bi].type = 'C'; 1047 bezier_points[bi].svg_type = 'C'; 1048 1049 cx = px + parse_double (c[++i]); 1050 1051 if (svg_glyph) { 1052 cy = py + parse_double (c[++i]); 1053 } else { 1054 cy = py - parse_double (c[++i]); 1055 } 1056 1057 bezier_points[bi].x0 = cx; 1058 bezier_points[bi].y0 = cy; 1059 1060 cx = px + parse_double (c[++i]); 1061 1062 if (svg_glyph) { 1063 cy = py + parse_double (c[++i]); 1064 } else { 1065 cy = py - parse_double (c[++i]); 1066 } 1067 1068 px2 = cx; 1069 py2 = cy; 1070 1071 bezier_points[bi].x1 = px2; 1072 bezier_points[bi].y1 = py2; 1073 1074 cx = px + parse_double (c[++i]); 1075 1076 if (svg_glyph) { 1077 cy = py + parse_double (c[++i]); 1078 } else { 1079 cy = py + -parse_double (c[++i]); 1080 } 1081 1082 bezier_points[bi].x2 = cx; 1083 bezier_points[bi].y2 = cy; 1084 1085 px = cx; 1086 py = cy; 1087 1088 bi++; 1089 } 1090 } else if (c[i] == "C") { 1091 while (i + 6 < c.length && is_point (c[i + 1])) { 1092 bezier_points[bi].type = 'C'; 1093 bezier_points[bi].svg_type = 'C'; 1094 1095 cx = parse_double (c[++i]); 1096 1097 if (svg_glyph) { 1098 cy = parse_double (c[++i]); 1099 } else { 1100 cy = -parse_double (c[++i]); 1101 } 1102 1103 bezier_points[bi].x0 = cx; 1104 bezier_points[bi].y0 = cy; 1105 1106 cx = parse_double (c[++i]); 1107 1108 if (svg_glyph) { 1109 cy = parse_double (c[++i]); 1110 } else { 1111 cy = -parse_double (c[++i]); 1112 } 1113 1114 px2 = cx; 1115 py2 = cy; 1116 1117 bezier_points[bi].x1 = cx; 1118 bezier_points[bi].y1 = cy; 1119 1120 cx = parse_double (c[++i]); 1121 1122 if (svg_glyph) { 1123 cy = parse_double (c[++i]); 1124 } else { 1125 cy = -parse_double (c[++i]); 1126 } 1127 1128 bezier_points[bi].x2 = cx; 1129 bezier_points[bi].y2 = cy; 1130 1131 px = cx; 1132 py = cy; 1133 1134 bi++; 1135 } 1136 } else if (c[i] == "q") { 1137 while (i + 4 < c.length && is_point (c[i + 1])) { 1138 bezier_points[bi].type = 'Q'; 1139 bezier_points[bi].svg_type = 'q'; 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 bezier_points[bi].x0 = cx; 1150 bezier_points[bi].y0 = cy; 1151 1152 px2 = cx; 1153 py2 = cy; 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].x1 = cx; 1164 bezier_points[bi].y1 = cy; 1165 1166 px = cx; 1167 py = cy; 1168 1169 bi++; 1170 } 1171 } else if (c[i] == "Q") { 1172 1173 while (i + 4 < c.length && is_point (c[i + 1])) { 1174 bezier_points[bi].type = 'Q'; 1175 bezier_points[bi].svg_type = 'Q'; 1176 1177 cx = parse_double (c[++i]); 1178 1179 if (svg_glyph) { 1180 cy = parse_double (c[++i]); 1181 } else { 1182 cy = -parse_double (c[++i]); 1183 } 1184 1185 bezier_points[bi].x0 = cx; 1186 bezier_points[bi].y0 = cy; 1187 1188 px2 = cx; 1189 py2 = cy; 1190 1191 cx = parse_double (c[++i]); 1192 1193 if (svg_glyph) { 1194 cy = parse_double (c[++i]); 1195 } else { 1196 cy = -parse_double (c[++i]); 1197 } 1198 1199 px = cx; 1200 py = cy; 1201 1202 bezier_points[bi].x1 = cx; 1203 bezier_points[bi].y1 = cy; 1204 1205 bi++; 1206 } 1207 } else if (c[i] == "t") { 1208 while (i + 2 < c.length && is_point (c[i + 1])) { 1209 bezier_points[bi].type = 'Q'; 1210 bezier_points[bi].svg_type = 't'; 1211 1212 // the first point is the reflection 1213 cx = 2 * px - px2; 1214 cy = 2 * py - py2; // if (svg_glyph) ? 1215 1216 bezier_points[bi].x0 = cx; 1217 bezier_points[bi].y0 = cy; 1218 1219 px2 = cx; 1220 py2 = cy; 1221 1222 cx = px + parse_double (c[++i]); 1223 1224 if (svg_glyph) { 1225 cy = py + parse_double (c[++i]); 1226 } else { 1227 cy = py - parse_double (c[++i]); 1228 } 1229 1230 px = cx; 1231 py = cy; 1232 1233 bezier_points[bi].x1 = px; 1234 bezier_points[bi].y1 = py; 1235 1236 bi++; 1237 } 1238 } else if (c[i] == "T") { 1239 while (i + 2 < c.length && is_point (c[i + 1])) { 1240 bezier_points[bi].type = 'Q'; 1241 bezier_points[bi].svg_type = 'T'; 1242 1243 // the reflection 1244 cx = 2 * px - px2; 1245 cy = 2 * py - py2; // if (svg_glyph) ? 1246 1247 bezier_points[bi].x0 = cx; 1248 bezier_points[bi].y0 = cy; 1249 1250 px2 = cx; 1251 py2 = cy; 1252 1253 cx = parse_double (c[++i]); 1254 1255 if (svg_glyph) { 1256 cy = parse_double (c[++i]); 1257 } else { 1258 cy = -parse_double (c[++i]); 1259 } 1260 1261 px = cx; 1262 py = cy; 1263 1264 bezier_points[bi].x1 = px; 1265 bezier_points[bi].y1 = py; 1266 1267 bi++; 1268 } 1269 } else if (c[i] == "s") { 1270 while (i + 4 < c.length && is_point (c[i + 1])) { 1271 bezier_points[bi].type = 'C'; 1272 bezier_points[bi].svg_type = 's'; 1273 1274 // the first point is the reflection 1275 cx = 2 * px - px2; 1276 cy = 2 * py - py2; // if (svg_glyph) ? 1277 1278 bezier_points[bi].x0 = cx; 1279 bezier_points[bi].y0 = cy; 1280 1281 cx = px + parse_double (c[++i]); 1282 1283 if (svg_glyph) { 1284 cy = py + parse_double (c[++i]); 1285 } else { 1286 cy = py - parse_double (c[++i]); 1287 } 1288 1289 px2 = cx; 1290 py2 = cy; 1291 1292 bezier_points[bi].x1 = px2; 1293 bezier_points[bi].y1 = py2; 1294 1295 cx = px + parse_double (c[++i]); 1296 1297 if (svg_glyph) { 1298 cy = py + parse_double (c[++i]); 1299 } else { 1300 cy = py - parse_double (c[++i]); 1301 } 1302 1303 bezier_points[bi].x2 = cx; 1304 bezier_points[bi].y2 = cy; 1305 1306 px = cx; 1307 py = cy; 1308 1309 bi++; 1310 } 1311 } else if (c[i] == "S") { 1312 while (i + 4 < c.length && is_point (c[i + 1])) { 1313 bezier_points[bi].type = 'C'; 1314 bezier_points[bi].svg_type = 'S'; 1315 1316 // the reflection 1317 cx = 2 * px - px2; 1318 cy = 2 * py - py2; // if (svg_glyph) ? 1319 1320 bezier_points[bi].x0 = cx; 1321 bezier_points[bi].y0 = cy; 1322 1323 // the other two are regular cubic points 1324 cx = parse_double (c[++i]); 1325 1326 if (svg_glyph) { 1327 cy = parse_double (c[++i]); 1328 } else { 1329 cy = -parse_double (c[++i]); 1330 } 1331 1332 px2 = cx; 1333 py2 = cy; 1334 1335 bezier_points[bi].x1 = px2; 1336 bezier_points[bi].y1 = py2; 1337 1338 cx = parse_double (c[++i]); 1339 1340 if (svg_glyph) { 1341 cy = parse_double (c[++i]); 1342 } else { 1343 cy = -parse_double (c[++i]); 1344 } 1345 1346 bezier_points[bi].x2 = cx; 1347 bezier_points[bi].y2 = cy; 1348 1349 px = cx; 1350 py = cy; 1351 1352 bi++; 1353 } 1354 } else if (c[i] == "a") { 1355 while (i + 7 < c.length && is_point (c[i + 1])) { 1356 arc_rx = parse_double (c[++i]); 1357 arc_ry = parse_double (c[++i]); 1358 1359 arc_rotation = parse_double (c[++i]); 1360 large_arc = parse_int (c[++i]); 1361 arc_sweep = parse_int (c[++i]); 1362 1363 cx = px + parse_double (c[++i]); 1364 1365 if (svg_glyph) { 1366 cy = py + parse_double (c[++i]); 1367 } else { 1368 cy = py - parse_double (c[++i]); 1369 } 1370 1371 arc_dest_x = cx; 1372 arc_dest_y = cy; 1373 1374 add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy); 1375 1376 px = cx; 1377 py = cy; 1378 1379 1380 } 1381 } else if (i + 7 < c.length && c[i] == "A") { 1382 while (is_point (c[i + 1])) { 1383 arc_rx = parse_double (c[++i]); 1384 arc_ry = parse_double (c[++i]); 1385 1386 arc_rotation = parse_double (c[++i]); 1387 large_arc = parse_int (c[++i]); 1388 arc_sweep = parse_int (c[++i]); 1389 1390 cx = parse_double (c[++i]); 1391 1392 if (svg_glyph) { 1393 cy = parse_double (c[++i]); 1394 } else { 1395 cy = -parse_double (c[++i]); 1396 } 1397 1398 arc_dest_x = cx; 1399 arc_dest_y = cy; 1400 1401 add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy); 1402 1403 px = cx; 1404 py = cy; 1405 1406 1407 } 1408 } else if (c[i] == "z") { 1409 bezier_points[bi].type = 'z'; 1410 bezier_points[bi].svg_type = 'z'; 1411 1412 bi++; 1413 } else if (c[i] == "Z") { 1414 bezier_points[bi].type = 'z'; 1415 bezier_points[bi].svg_type = 'z'; 1416 1417 bi++; 1418 } else if (c[i] == "") { 1419 } else if (c[i] == " ") { 1420 } else { 1421 warning (@"Unknown instruction: $(c[i])"); 1422 } 1423 } 1424 1425 if (bi == 0) { 1426 warning ("No points in path."); 1427 return path_list; 1428 } 1429 1430 move_and_resize (bezier_points, bi, svg_glyph, units, glyph); 1431 1432 if (format == SvgFormat.ILLUSTRATOR) { 1433 path_list = create_paths_illustrator (bezier_points, bi); 1434 } else { 1435 path_list = create_paths_inkscape (bezier_points, bi); 1436 } 1437 1438 // TODO: Find out if it is possible to tie handles. 1439 return path_list; 1440 } 1441 1442 void move_and_resize (BezierPoints[] b, int num_b, bool svg_glyph, double units, Glyph glyph) { 1443 Font font = BirdFont.get_current_font (); 1444 1445 for (int i = 0; i < num_b; i++) { 1446 // resize all points 1447 b[i].x0 *= units; 1448 b[i].y0 *= units; 1449 b[i].x1 *= units; 1450 b[i].y1 *= units; 1451 b[i].x2 *= units; 1452 b[i].y2 *= units; 1453 1454 // move all points 1455 if (svg_glyph) { 1456 b[i].x0 += glyph.left_limit; 1457 b[i].y0 += font.base_line; 1458 b[i].x1 += glyph.left_limit; 1459 b[i].y1 += font.base_line; 1460 b[i].x2 += glyph.left_limit; 1461 b[i].y2 += font.base_line; 1462 } else { 1463 b[i].x0 += glyph.left_limit; 1464 b[i].y0 += font.top_position; 1465 b[i].x1 += glyph.left_limit; 1466 b[i].y1 += font.top_position; 1467 b[i].x2 += glyph.left_limit; 1468 b[i].y2 += font.top_position; 1469 } 1470 } 1471 } 1472 1473 void find_last_handle (int start_index, BezierPoints[] b, int num_b, out double left_x, out double left_y, out PointType last_type) { 1474 BezierPoints last = new BezierPoints (); 1475 bool found = false; 1476 1477 left_x = 0; 1478 left_y = 0; 1479 last_type = PointType.NONE; 1480 1481 return_if_fail (b.length != 0); 1482 return_if_fail (b[0].type != 'z'); 1483 return_if_fail (num_b < b.length); 1484 1485 for (int i = start_index; i < num_b; i++) { 1486 switch (b[i].type) { 1487 case 'Q': 1488 break; 1489 case 'C': 1490 break; 1491 case 'z': 1492 found = true; 1493 break; 1494 default: 1495 break; 1496 } 1497 1498 if (found || i + 1 == num_b) { 1499 if (b[i - 1].type == 'Q') { 1500 return_if_fail (i >= 1); 1501 left_x = b[i - 1].x0; 1502 left_y = b[i - 1].y0; 1503 last_type = PointType.QUADRATIC; 1504 } else if (b[i - 1].type == 'C') { 1505 return_if_fail (i >= 1); 1506 left_x = b[i - 1].x1; 1507 left_y = b[i - 1].y1; 1508 last_type = PointType.CUBIC; 1509 } else if (b[i - 1].type == 'S') { 1510 return_if_fail (i >= 1); 1511 left_x = b[i - 1].x1; 1512 left_y = b[i - 1].y1; 1513 last_type = PointType.CUBIC; 1514 }else if (b[i - 1].type == 'L' || last.type == 'M') { 1515 return_if_fail (i >= 2); // FIXME: -2 can be C or L 1516 left_x = b[i - 2].x0 + (b[i - 1].x0 - b[i - 2].x0) / 3.0; 1517 left_y = b[i - 2].y0 + (b[i - 1].y0 - b[i - 2].y0) / 3.0; 1518 last_type = PointType.LINE_CUBIC; 1519 } else { 1520 warning (@"Unexpected type. $(b[i - 1])\n"); 1521 } 1522 return; 1523 } 1524 1525 last = b[i]; 1526 } 1527 1528 warning ("Last point not found."); 1529 } 1530 1531 PathList create_paths_inkscape (BezierPoints[] b, int num_b) { 1532 double last_x; 1533 double last_y; 1534 PointType last_type; 1535 Path path; 1536 PathList path_list = new PathList (); 1537 EditPoint ep = new EditPoint (); 1538 Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> (); 1539 1540 path = new Path (); 1541 1542 if (num_b == 0) { 1543 warning ("No SVG data"); 1544 return path_list; 1545 } 1546 1547 if (b[0].type != 'M') { 1548 warning ("Path must begin with M or m."); 1549 return path_list; 1550 } 1551 1552 find_last_handle (0, b, num_b, out last_x, out last_y, out last_type); 1553 1554 for (int i = 0; i < num_b; i++) { 1555 if (b[i].type == '\0') { 1556 warning ("Parser error."); 1557 return path_list; 1558 } 1559 1560 if (b[i].type == 'z') { 1561 path.close (); 1562 path.create_list (); 1563 path.recalculate_linear_handles (); 1564 path_list.add (path); 1565 path = new Path (); 1566 1567 if (i + 1 >= num_b) { 1568 break; 1569 } else { 1570 find_last_handle (i + 1, b, num_b, out last_x, out last_y, out last_type); 1571 } 1572 } 1573 1574 if (i >= num_b) { 1575 break; 1576 } 1577 1578 if (b[i].type == 'M') { 1579 ep = path.add (b[i].x0, b[i].y0); 1580 ep.set_point_type (PointType.CUBIC); 1581 1582 ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC); 1583 1584 if (i == 0 || (b[i - 1].type == 'z')) { 1585 ep.get_left_handle ().set_point_type (last_type); 1586 ep.get_left_handle ().move_to_coordinate (last_x, last_y); 1587 } else { 1588 if (b[i - 1].type == 'C' || b[i - 1].type == 'S') { 1589 ep.get_left_handle ().set_point_type (PointType.CUBIC); 1590 ep.get_left_handle ().move_to_coordinate (b[i + 1].x1, b[i + 1].y1); 1591 } 1592 1593 if (b[i + 1].type == 'C' || b[i - 1].type == 'S') { 1594 ep.get_right_handle ().set_point_type (PointType.CUBIC); 1595 ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0); 1596 } else if (b[i + 1].type == 'L' || b[i + 1].type == 'M') { 1597 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); 1598 } 1599 } 1600 } 1601 1602 if (b[i].type == 'L') { 1603 return_val_if_fail (i != 0, path_list); 1604 1605 ep = path.add (b[i].x0, b[i].y0); 1606 ep.set_point_type (PointType.CUBIC); 1607 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); 1608 ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC); 1609 1610 if (b[i + 1].type == 'L' || b[i + 1].type == 'M' || b[i + 1].type == 'z') { 1611 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); 1612 } 1613 1614 if (b[i -1].type == 'L' || b[i - 1].type == 'M') { 1615 ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC); 1616 } 1617 } 1618 1619 if (b[i].type == 'Q') { 1620 return_val_if_fail (i != 0, path_list); 1621 1622 ep.set_point_type (PointType.QUADRATIC); 1623 1624 ep.get_right_handle ().set_point_type (PointType.QUADRATIC); 1625 ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0); 1626 1627 if (b[i + 1].type != 'z') { 1628 ep = path.add (b[i].x1, b[i].y1); 1629 1630 ep.get_left_handle ().set_point_type (PointType.QUADRATIC); 1631 ep.get_left_handle ().move_to_coordinate (b[i].x0, b[i].y0); 1632 } 1633 } 1634 1635 if (b[i].type == 'C' || b[i].type == 'S') { 1636 return_val_if_fail (i != 0, path_list); 1637 1638 ep.set_point_type (PointType.CUBIC); 1639 1640 ep.get_right_handle ().set_point_type (PointType.CUBIC); 1641 ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0); 1642 1643 if (b[i].type == 'S') { 1644 smooth_points.add (ep); 1645 } 1646 1647 if (b[i + 1].type != 'z') { 1648 ep = path.add (b[i].x2, b[i].y2); 1649 1650 ep.get_left_handle ().set_point_type (PointType.CUBIC); 1651 ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1); 1652 } 1653 } 1654 } 1655 1656 foreach (EditPoint e in smooth_points) { 1657 e.set_point_type (PointType.LINE_DOUBLE_CURVE); 1658 e.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE); 1659 e.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE); 1660 } 1661 1662 foreach (EditPoint e in smooth_points) { 1663 e.recalculate_linear_handles (); 1664 } 1665 1666 for (int i = 0; i < 3; i++) { 1667 foreach (EditPoint e in smooth_points) { 1668 e.set_tie_handle (true); 1669 e.process_tied_handle (); 1670 } 1671 } 1672 1673 if (path.points.size > 0) { 1674 path_list.add (path); 1675 } 1676 1677 foreach (Path p in path_list.paths) { 1678 p.remove_points_on_points (); 1679 } 1680 1681 return path_list; 1682 } 1683 1684 PathList create_paths_illustrator (BezierPoints[] b, int num_b) { 1685 Path path; 1686 PathList path_list = new PathList (); 1687 EditPoint ep; 1688 bool first_point = true; 1689 double first_left_x, first_left_y; 1690 Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> (); 1691 1692 if (num_b > b.length) { 1693 warning ("num_b > b.length: $num_b > $(b.length)"); 1694 return path_list; 1695 } 1696 1697 path = new Path (); 1698 1699 if (num_b <= 1) { 1700 warning ("No SVG data"); 1701 return path_list; 1702 } 1703 1704 first_left_x = 0; 1705 first_left_y = 0; 1706 1707 // FIXME: array boundaries 1708 for (int i = 0; i < num_b; i++) { 1709 if (b[i].type == '\0') { 1710 warning ("Parser error."); 1711 return path_list; 1712 } else if (b[i].type == 'z') { 1713 path.close (); 1714 path.create_list (); 1715 1716 int first_index = 1; 1717 1718 for (int j = i - 1; j >= 1; j--) { 1719 if (b[j].type == 'z') { 1720 first_index = j + 1; // from z to M 1721 } 1722 } 1723 1724 if (b[first_index].type == 'C' || b[first_index].type == 'S') { 1725 return_val_if_fail (path.points.size != 0, path_list); 1726 ep = path.points.get (path.points.size - 1); 1727 1728 if (b[i - 1].type != 'L' ) { 1729 ep.get_right_handle ().set_point_type (PointType.CUBIC); 1730 ep.get_right_handle ().move_to_coordinate (b[first_index].x0, b[first_index].y0); 1731 } 1732 } else if (b[first_index].type == 'L') { 1733 return_val_if_fail (path.points.size != 0, path_list); 1734 ep = path.points.get (path.points.size - 1); 1735 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); 1736 ep.recalculate_linear_handles (); 1737 } else { 1738 warning ("Unexpected type: %s", (!) b[first_index].type.to_string ()); 1739 } 1740 1741 path.recalculate_linear_handles (); 1742 path_list.add (path); 1743 1744 path = new Path (); 1745 first_point = true; 1746 } else if (b[i].type == 'M') { 1747 } else if (b[i].type == 'L') { 1748 1749 if (first_point) { 1750 first_left_x = b[i].x0; 1751 first_left_y = b[i].y0; 1752 } 1753 1754 ep = path.add (b[i].x0, b[i].y0); 1755 ep.set_point_type (PointType.LINE_CUBIC); // TODO: quadratic 1756 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); 1757 1758 if (b[i -1].type == 'L' || first_point) { 1759 // ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC); 1760 } 1761 1762 if (b[i + 1].type == 'C' || b[i + 1].type == 'S') { 1763 return_val_if_fail (i + 1 < num_b, path_list); 1764 ep.get_right_handle ().set_point_type (PointType.CUBIC); 1765 ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0); 1766 } 1767 1768 first_point = false; 1769 } else if (b[i].type == 'Q') { 1770 warning ("Illustrator does not support quadratic control points."); 1771 warning (@"$(b[i])\n"); 1772 } else if (b[i].type == 'C' || b[i].type == 'S') { 1773 1774 if (first_point) { 1775 first_left_x = b[i].x0; 1776 first_left_y = b[i].y0; 1777 } 1778 1779 ep = path.add (b[i].x2, b[i].y2); 1780 ep.set_point_type (PointType.CUBIC); 1781 1782 ep.get_right_handle ().set_point_type (PointType.CUBIC); 1783 ep.get_left_handle ().set_point_type (PointType.CUBIC); 1784 1785 ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1); 1786 1787 if (b[i].type == 'S') { 1788 smooth_points.add (ep); 1789 } 1790 1791 if (b[i + 1].type != 'z') { 1792 ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0); 1793 } else { 1794 ep.get_right_handle ().move_to_coordinate (first_left_x, first_left_y); 1795 } 1796 1797 first_point = false; 1798 } else { 1799 warning ("Unknown control point type."); 1800 warning (@"$(b[i])\n"); 1801 } 1802 } 1803 1804 foreach (EditPoint e in smooth_points) { 1805 e.set_point_type (PointType.LINE_DOUBLE_CURVE); 1806 e.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE); 1807 e.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE); 1808 } 1809 1810 foreach (EditPoint e in smooth_points) { 1811 e.recalculate_linear_handles (); 1812 } 1813 1814 for (int i = 0; i < 3; i++) { 1815 foreach (EditPoint e in smooth_points) { 1816 e.set_tie_handle (true); 1817 e.process_tied_handle (); 1818 } 1819 } 1820 1821 if (path.points.size > 0) { 1822 path_list.add (path); 1823 } 1824 1825 foreach (Path p in path_list.paths) { 1826 p.remove_points_on_points (); 1827 } 1828 1829 return path_list; 1830 } 1831 1832 // TODO: implement a default svg parser 1833 1834 static int parse_int (string? s) { 1835 if (is_null (s)) { 1836 warning ("null instead of string"); 1837 return 0; 1838 } 1839 1840 if (!is_point ((!) s)) { 1841 warning (@"Expecting an integer got: $((!) s)"); 1842 return 0; 1843 } 1844 1845 return int.parse ((!) s); 1846 } 1847 1848 static double parse_double (string? s) { 1849 if (is_null (s)) { 1850 warning ("Got null instead of expected string."); 1851 return 0; 1852 } 1853 1854 if (!is_point ((!) s)) { 1855 warning (@"Expecting a double got: $((!) s)"); 1856 return 0; 1857 } 1858 1859 return double.parse ((!) s); 1860 } 1861 1862 static bool is_point (string? s) { 1863 if (s == null) { 1864 warning ("s is null"); 1865 return false; 1866 } 1867 1868 return double.try_parse ((!) s); 1869 } 1870 1871 Path parse_polygon_data (string polygon_points) { 1872 string data = add_separators (polygon_points); 1873 string[] c = data.split (" "); 1874 Path path; 1875 BezierPoints[] bezier_points = new BezierPoints[c.length + 1]; 1876 int bi; 1877 Glyph g; 1878 1879 bi = 0; 1880 for (int i = 0; i < c.length - 1; i += 2) { 1881 if (i + 1 >= c.length) { 1882 warning ("No y value."); 1883 break; 1884 } 1885 1886 if (bi >= bezier_points.length) { 1887 warning ("End of bezier_points"); 1888 break; 1889 } 1890 1891 bezier_points[bi] = new BezierPoints (); 1892 bezier_points[bi].type == 'L'; 1893 bezier_points[bi].x0 = parse_double (c[i]); 1894 bezier_points[bi].y0 = -parse_double (c[i + 1]); 1895 bi++; 1896 } 1897 1898 g = MainWindow.get_current_glyph (); 1899 move_and_resize (bezier_points, bi, false, 1, g); 1900 1901 path = new Path (); 1902 for (int i = 0; i < bi; i++) { 1903 path.add (bezier_points[i].x0, bezier_points[i].y0); 1904 } 1905 1906 path.close (); 1907 path.create_list (); 1908 path.recalculate_linear_handles (); 1909 1910 return path; 1911 } 1912 } 1913 1914 } 1915