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