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