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