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