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