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