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