The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

SvgParser.vala in libbirdfont

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git

Revisions

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