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