The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

SvgParser.vala in libbirdfont/Svg

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