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