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