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