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.
Check if a point is inside of a path with curves.
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 // assume the even odd rule is applied and convert the path 455 // to a path using the non-zero rule 456 foreach (Path p1 in pl.paths.paths) { 457 if (Path.is_counter (pl.paths, p1)) { 458 p1.force_direction (Direction.COUNTER_CLOCKWISE); 459 } else { 460 p1.force_direction (Direction.CLOCKWISE); 461 } 462 } 463 464 foreach (Attribute attr in tag.get_attributes ()) { 465 if (attr.get_name () == "transform") { 466 transform_paths (attr.get_content (), path_list); 467 } 468 } 469 } 470 471 public static void create_lines_for_segment (Path path, EditPoint start, EditPoint end) { 472 double x1, x2, x3; 473 double y1, y2, y3; 474 double step_start, step, step_end; 475 476 path.add (start.x, start.y); 477 478 step_start = 0; 479 step = 0.5; 480 step_end = 1; 481 482 while (true) { 483 Path.get_point_for_step (start, end, step_start, out x1, out y1); 484 Path.get_point_for_step (start, end, step, out x2, out y2); 485 Path.get_point_for_step (start, end, step_end, out x3, out y3); 486 487 if (!StrokeTool.is_flat (x1, y1, x2, y2, x3, y3, 1) 488 && step_end - step / 2.0 > step_start 489 && step_end - step / 2.0 > 0.1 490 && step > 0.05) { 491 492 step /= 2.0; 493 494 if (step < 0.05) { 495 step = 0.05; 496 } else { 497 step_end = step_start + 2 * step; 498 } 499 } else { 500 path.add (x3, y3); 501 502 if (step_end + step < 1) { 503 step_start = step_end; 504 step_end += step; 505 } else { 506 break; 507 } 508 } 509 } 510 } 511 512 // FIXME: cache lines in path 513 public static Path get_lines (Path p) { 514 EditPoint start; 515 Path path = new Path (); 516 517 if (p.points.size == 0) { 518 return path; 519 } 520 521 // create a set of straight lines 522 start = p.points.get (p.points.size - 1); 523 524 foreach (EditPoint end in p.points) { 525 create_lines_for_segment (path, start, end); 526 start = end; 527 } 528 529 if (StrokeTool.stroke_selected) { // FIXME: DELETE 530 ((!) BirdFont.get_current_font ().get_glyph ("b")).add_path (path); 531 path.recalculate_linear_handles (); 532 } 533 534 return path; 535 } 536 537 /** Check if a point is inside using the even odd fill rule. 538 * The path should only have straight lines. 539 */ 540 public static bool is_inside (EditPoint point, Path path) { 541 EditPoint prev; 542 bool inside = false; 543 Path lines = path; 544 545 if (path.points.size <= 1) { 546 return false; 547 } 548 549 prev = path.get_last_point (); 550 551 foreach (EditPoint start in path.points) { 552 if (start.x == point.x && point.y == start.y) { 553 inside = true; 554 break; 555 } else if ((start.y > point.y) != (prev.y > point.y) 556 && point.x < (prev.x - start.x) * (point.y - start.y) / (prev.y - start.y) + start.x) { 557 inside = !inside; 558 } 559 560 prev = start; 561 } 562 563 return inside; 564 } 565 566 /** Add space as separator to svg data. 567 * @param d svg data 568 */ 569 static string add_separators (string d) { 570 string data = d; 571 572 data = data.replace (",", " "); 573 data = data.replace ("a", " a "); 574 data = data.replace ("A", " A "); 575 data = data.replace ("m", " m "); 576 data = data.replace ("M", " M "); 577 data = data.replace ("h", " h "); 578 data = data.replace ("H", " H "); 579 data = data.replace ("v", " v "); 580 data = data.replace ("V", " V "); 581 data = data.replace ("l", " l "); 582 data = data.replace ("L", " L "); 583 data = data.replace ("q", " q "); 584 data = data.replace ("Q", " Q "); 585 data = data.replace ("c", " c "); 586 data = data.replace ("C", " C "); 587 data = data.replace ("t", " t "); 588 data = data.replace ("T", " T "); 589 data = data.replace ("s", " s "); 590 data = data.replace ("S", " S "); 591 data = data.replace ("zM", " z M "); 592 data = data.replace ("zm", " z m "); 593 data = data.replace ("z", " z "); 594 data = data.replace ("Z", " Z "); 595 data = data.replace ("-", " -"); 596 data = data.replace ("e -", "e-"); // minus can be either separator or a negative exponent 597 data = data.replace ("\t", " "); 598 data = data.replace ("\r\n", " "); 599 data = data.replace ("\n", " "); 600 601 // use only a single space as separator 602 while (data.index_of (" ") > -1) { 603 data = data.replace (" ", " "); 604 } 605 606 return data; 607 } 608 609 public void add_path_to_glyph (string d, Glyph g, bool svg_glyph = false, double units = 1) { 610 PathList p = parse_svg_data (d, g, svg_glyph, units); 611 foreach (Path path in p.paths) { 612 g.add_path (path); 613 } 614 } 615 616 /** 617 * @param d svg data 618 * @param glyph use lines from this glyph but don't add the generated paths 619 * @param svg_glyph parse svg glyph with origo in lower left corner 620 * 621 * @return the new paths 622 */ 623 public PathList parse_svg_data (string d, Glyph glyph, bool svg_glyph = false, double units = 1) { 624 double px = 0; 625 double py = 0; 626 double px2 = 0; 627 double py2 = 0; 628 double cx = 0; 629 double cy = 0; 630 string data; 631 Font font; 632 PathList path_list = new PathList (); 633 BezierPoints[] bezier_points; 634 string[] c; 635 double arc_rx, arc_ry; 636 double arc_rotation; 637 int large_arc; 638 int arc_sweep; 639 double arc_dest_x, arc_dest_y; 640 641 font = BirdFont.get_current_font (); 642 643 data = add_separators (d); 644 c = data.split (" "); 645 bezier_points = new BezierPoints[8 * c.length + 1]; // the arc instruction can use up to eight points 646 647 for (int i = 0; i < 2 * c.length + 1; i++) { 648 bezier_points[i] = new BezierPoints (); 649 } 650 651 int bi = 0; 652 653 // parse path 654 int i = -1; 655 while (++i < c.length && bi < bezier_points.length) { 656 if (c[i] == "m") { 657 while (i + 2 < c.length && is_point (c[i + 1])) { // FIXME: check array bounds 658 bezier_points[bi].type = 'M'; 659 bezier_points[bi].svg_type = 'm'; 660 661 px += parse_double (c[++i]); 662 663 if (svg_glyph) { 664 py += parse_double (c[++i]); 665 } else { 666 py += -parse_double (c[++i]); 667 } 668 669 bezier_points[bi].x0 = px; 670 bezier_points[bi].y0 = py; 671 bi++; 672 } 673 } else if (c[i] == "M") { 674 while (i + 2 < c.length && is_point (c[i + 1])) { 675 bezier_points[bi].type = 'M'; 676 bezier_points[bi].svg_type = 'M'; 677 678 px = parse_double (c[++i]); 679 680 if (svg_glyph) { 681 py = parse_double (c[++i]); 682 } else { 683 py = -parse_double (c[++i]); 684 } 685 686 bezier_points[bi].x0 = px; 687 bezier_points[bi].y0 = py; 688 bi++; 689 } 690 } else if (c[i] == "h") { 691 while (i + 1 < c.length && is_point (c[i + 1])) { 692 bezier_points[bi].type = 'L'; 693 bezier_points[bi].svg_type = 'h'; 694 695 px += parse_double (c[++i]); 696 697 bezier_points[bi].x0 = px; 698 bezier_points[bi].y0 = py; 699 bi++; 700 } 701 } else if (i + 1 < c.length && c[i] == "H") { 702 while (is_point (c[i + 1])) { 703 bezier_points[bi].type = 'L'; 704 bezier_points[bi].svg_type = 'H'; 705 706 px = parse_double (c[++i]); 707 708 bezier_points[bi].x0 = px; 709 bezier_points[bi].y0 = py; 710 bi++; 711 } 712 } else if (c[i] == "v") { 713 while (i + 1 < c.length && is_point (c[i + 1])) { 714 bezier_points[bi].type = 'L'; 715 bezier_points[bi].svg_type = 'v'; 716 717 if (svg_glyph) { 718 py = py + parse_double (c[++i]); 719 } else { 720 py = py - parse_double (c[++i]); 721 } 722 723 bezier_points[bi].x0 = px; 724 bezier_points[bi].y0 = py; 725 bi++; 726 } 727 } else if (i + 1 < c.length && c[i] == "V") { 728 while (is_point (c[i + 1])) { 729 bezier_points[bi].type = 'L'; 730 bezier_points[bi].svg_type = 'V'; 731 732 if (svg_glyph) { 733 py = parse_double (c[++i]); 734 } else { 735 py = -parse_double (c[++i]); 736 } 737 738 bezier_points[bi].x0 = px; 739 bezier_points[bi].y0 = py; 740 bi++; 741 } 742 } else if (c[i] == "l") { 743 while (i + 2 < c.length && is_point (c[i + 1])) { 744 bezier_points[bi].type = 'L'; 745 bezier_points[bi].svg_type = 'l'; 746 747 cx = px + parse_double (c[++i]); 748 749 if (svg_glyph) { 750 cy = py + parse_double (c[++i]); 751 } else { 752 cy = py - parse_double (c[++i]); 753 } 754 755 px = cx; 756 py = cy; 757 758 bezier_points[bi].x0 = cx; 759 bezier_points[bi].y0 = cy; 760 bi++; 761 } 762 } else if (c[i] == "L") { 763 while (i + 2 < c.length && is_point (c[i + 1])) { 764 bezier_points[bi].type = 'L'; 765 bezier_points[bi].svg_type = 'L'; 766 767 cx = parse_double (c[++i]); 768 769 if (svg_glyph) { 770 cy = parse_double (c[++i]); 771 } else { 772 cy = -parse_double (c[++i]); 773 } 774 775 px = cx; 776 py = cy; 777 778 bezier_points[bi].x0 = cx; 779 bezier_points[bi].y0 = cy; 780 bi++; 781 } 782 } else if (c[i] == "c") { 783 while (i + 6 < c.length && is_point (c[i + 1])) { 784 bezier_points[bi].type = 'C'; 785 bezier_points[bi].svg_type = 'C'; 786 787 cx = px + parse_double (c[++i]); 788 789 if (svg_glyph) { 790 cy = py + parse_double (c[++i]); 791 } else { 792 cy = py - parse_double (c[++i]); 793 } 794 795 bezier_points[bi].x0 = cx; 796 bezier_points[bi].y0 = cy; 797 798 cx = px + parse_double (c[++i]); 799 800 if (svg_glyph) { 801 cy = py + parse_double (c[++i]); 802 } else { 803 cy = py - parse_double (c[++i]); 804 } 805 806 px2 = cx; 807 py2 = cy; 808 809 bezier_points[bi].x1 = px2; 810 bezier_points[bi].y1 = py2; 811 812 cx = px + parse_double (c[++i]); 813 814 if (svg_glyph) { 815 cy = py + parse_double (c[++i]); 816 } else { 817 cy = py + -parse_double (c[++i]); 818 } 819 820 bezier_points[bi].x2 = cx; 821 bezier_points[bi].y2 = cy; 822 823 px = cx; 824 py = cy; 825 826 bi++; 827 } 828 } else if (c[i] == "C") { 829 while (i + 6 < c.length && is_point (c[i + 1])) { 830 bezier_points[bi].type = 'C'; 831 bezier_points[bi].svg_type = 'C'; 832 833 cx = parse_double (c[++i]); 834 835 if (svg_glyph) { 836 cy = parse_double (c[++i]); 837 } else { 838 cy = -parse_double (c[++i]); 839 } 840 841 bezier_points[bi].x0 = cx; 842 bezier_points[bi].y0 = cy; 843 844 cx = parse_double (c[++i]); 845 846 if (svg_glyph) { 847 cy = parse_double (c[++i]); 848 } else { 849 cy = -parse_double (c[++i]); 850 } 851 852 px2 = cx; 853 py2 = cy; 854 855 bezier_points[bi].x1 = cx; 856 bezier_points[bi].y1 = cy; 857 858 cx = parse_double (c[++i]); 859 860 if (svg_glyph) { 861 cy = parse_double (c[++i]); 862 } else { 863 cy = -parse_double (c[++i]); 864 } 865 866 bezier_points[bi].x2 = cx; 867 bezier_points[bi].y2 = cy; 868 869 px = cx; 870 py = cy; 871 872 bi++; 873 } 874 } else if (c[i] == "q") { 875 while (i + 4 < c.length && is_point (c[i + 1])) { 876 bezier_points[bi].type = 'Q'; 877 bezier_points[bi].svg_type = 'q'; 878 879 cx = px + parse_double (c[++i]); 880 881 if (svg_glyph) { 882 cy = py + parse_double (c[++i]); 883 } else { 884 cy = py - parse_double (c[++i]); 885 } 886 887 bezier_points[bi].x0 = cx; 888 bezier_points[bi].y0 = cy; 889 890 px2 = cx; 891 py2 = cy; 892 893 cx = px + parse_double (c[++i]); 894 895 if (svg_glyph) { 896 cy = py + parse_double (c[++i]); 897 } else { 898 cy = py - parse_double (c[++i]); 899 } 900 901 bezier_points[bi].x1 = cx; 902 bezier_points[bi].y1 = cy; 903 904 px = cx; 905 py = cy; 906 907 bi++; 908 } 909 } else if (c[i] == "Q") { 910 911 while (i + 4 < c.length && is_point (c[i + 1])) { 912 bezier_points[bi].type = 'Q'; 913 bezier_points[bi].svg_type = 'Q'; 914 915 cx = parse_double (c[++i]); 916 917 if (svg_glyph) { 918 cy = parse_double (c[++i]); 919 } else { 920 cy = -parse_double (c[++i]); 921 } 922 923 bezier_points[bi].x0 = cx; 924 bezier_points[bi].y0 = cy; 925 926 px2 = cx; 927 py2 = cy; 928 929 cx = parse_double (c[++i]); 930 931 if (svg_glyph) { 932 cy = parse_double (c[++i]); 933 } else { 934 cy = -parse_double (c[++i]); 935 } 936 937 px = cx; 938 py = cy; 939 940 bezier_points[bi].x1 = cx; 941 bezier_points[bi].y1 = cy; 942 943 bi++; 944 } 945 } else if (c[i] == "t") { 946 while (i + 2 < c.length && is_point (c[i + 1])) { 947 bezier_points[bi].type = 'Q'; 948 bezier_points[bi].svg_type = 't'; 949 950 // the first point is the reflection 951 cx = 2 * px - px2; 952 cy = 2 * py - py2; // if (svg_glyph) ? 953 954 bezier_points[bi].x0 = cx; 955 bezier_points[bi].y0 = cy; 956 957 px2 = cx; 958 py2 = cy; 959 960 cx = px + parse_double (c[++i]); 961 962 if (svg_glyph) { 963 cy = py + parse_double (c[++i]); 964 } else { 965 cy = py - parse_double (c[++i]); 966 } 967 968 px = cx; 969 py = cy; 970 971 bezier_points[bi].x1 = px; 972 bezier_points[bi].y1 = py; 973 974 bi++; 975 } 976 } else if (c[i] == "T") { 977 while (i + 2 < c.length && is_point (c[i + 1])) { 978 bezier_points[bi].type = 'Q'; 979 bezier_points[bi].svg_type = 'T'; 980 981 // the reflection 982 cx = 2 * px - px2; 983 cy = 2 * py - py2; // if (svg_glyph) ? 984 985 bezier_points[bi].x0 = cx; 986 bezier_points[bi].y0 = cy; 987 988 px2 = cx; 989 py2 = cy; 990 991 cx = parse_double (c[++i]); 992 993 if (svg_glyph) { 994 cy = parse_double (c[++i]); 995 } else { 996 cy = -parse_double (c[++i]); 997 } 998 999 px = cx; 1000 py = cy; 1001 1002 bezier_points[bi].x1 = px; 1003 bezier_points[bi].y1 = py; 1004 1005 bi++; 1006 } 1007 } else if (c[i] == "s") { 1008 while (i + 4 < c.length && is_point (c[i + 1])) { 1009 bezier_points[bi].type = 'C'; 1010 bezier_points[bi].svg_type = 's'; 1011 1012 // the first point is the reflection 1013 cx = 2 * px - px2; 1014 cy = 2 * py - py2; // if (svg_glyph) ? 1015 1016 bezier_points[bi].x0 = cx; 1017 bezier_points[bi].y0 = cy; 1018 1019 cx = px + parse_double (c[++i]); 1020 1021 if (svg_glyph) { 1022 cy = py + parse_double (c[++i]); 1023 } else { 1024 cy = py - parse_double (c[++i]); 1025 } 1026 1027 px2 = cx; 1028 py2 = cy; 1029 1030 bezier_points[bi].x1 = px2; 1031 bezier_points[bi].y1 = py2; 1032 1033 cx = px + parse_double (c[++i]); 1034 1035 if (svg_glyph) { 1036 cy = py + parse_double (c[++i]); 1037 } else { 1038 cy = py - parse_double (c[++i]); 1039 } 1040 1041 bezier_points[bi].x2 = cx; 1042 bezier_points[bi].y2 = cy; 1043 1044 px = cx; 1045 py = cy; 1046 1047 bi++; 1048 } 1049 } else if (c[i] == "S") { 1050 while (i + 4 < c.length && is_point (c[i + 1])) { 1051 bezier_points[bi].type = 'C'; 1052 bezier_points[bi].svg_type = 'S'; 1053 1054 // the reflection 1055 cx = 2 * px - px2; 1056 cy = 2 * py - py2; // if (svg_glyph) ? 1057 1058 bezier_points[bi].x0 = cx; 1059 bezier_points[bi].y0 = cy; 1060 1061 // the other two are regular cubic points 1062 cx = parse_double (c[++i]); 1063 1064 if (svg_glyph) { 1065 cy = parse_double (c[++i]); 1066 } else { 1067 cy = -parse_double (c[++i]); 1068 } 1069 1070 px2 = cx; 1071 py2 = cy; 1072 1073 bezier_points[bi].x1 = px2; 1074 bezier_points[bi].y1 = py2; 1075 1076 cx = parse_double (c[++i]); 1077 1078 if (svg_glyph) { 1079 cy = parse_double (c[++i]); 1080 } else { 1081 cy = -parse_double (c[++i]); 1082 } 1083 1084 bezier_points[bi].x2 = cx; 1085 bezier_points[bi].y2 = cy; 1086 1087 px = cx; 1088 py = cy; 1089 1090 bi++; 1091 } 1092 } else if (c[i] == "a") { 1093 while (i + 7 < c.length && is_point (c[i + 1])) { 1094 arc_rx = parse_double (c[++i]); 1095 arc_ry = parse_double (c[++i]); 1096 1097 arc_rotation = parse_double (c[++i]); 1098 large_arc = parse_int (c[++i]); 1099 arc_sweep = parse_int (c[++i]); 1100 1101 cx = px + parse_double (c[++i]); 1102 1103 if (svg_glyph) { 1104 cy = py + parse_double (c[++i]); 1105 } else { 1106 cy = py - parse_double (c[++i]); 1107 } 1108 1109 arc_dest_x = cx; 1110 arc_dest_y = cy; 1111 1112 add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy); 1113 1114 px = cx; 1115 py = cy; 1116 1117 1118 } 1119 } else if (i + 7 < c.length && c[i] == "A") { 1120 while (is_point (c[i + 1])) { 1121 arc_rx = parse_double (c[++i]); 1122 arc_ry = parse_double (c[++i]); 1123 1124 arc_rotation = parse_double (c[++i]); 1125 large_arc = parse_int (c[++i]); 1126 arc_sweep = parse_int (c[++i]); 1127 1128 cx = parse_double (c[++i]); 1129 1130 if (svg_glyph) { 1131 cy = parse_double (c[++i]); 1132 } else { 1133 cy = -parse_double (c[++i]); 1134 } 1135 1136 arc_dest_x = cx; 1137 arc_dest_y = cy; 1138 1139 add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy); 1140 1141 px = cx; 1142 py = cy; 1143 1144 1145 } 1146 } else if (c[i] == "z") { 1147 bezier_points[bi].type = 'z'; 1148 bezier_points[bi].svg_type = 'z'; 1149 1150 bi++; 1151 } else if (c[i] == "Z") { 1152 bezier_points[bi].type = 'z'; 1153 bezier_points[bi].svg_type = 'z'; 1154 1155 bi++; 1156 } else if (c[i] == "") { 1157 } else if (c[i] == " ") { 1158 } else { 1159 warning (@"Unknown instruction: $(c[i])"); 1160 } 1161 } 1162 1163 if (bi == 0) { 1164 warning ("No points in path."); 1165 return path_list; 1166 } 1167 1168 // TODO: this code assumes that all paths are closed since stroke has not been implemented yet 1169 if (bezier_points[bi - 1].type != 'z') { 1170 bezier_points[bi].type = 'z'; 1171 bezier_points[bi].svg_type = 'z'; 1172 bi++; 1173 } 1174 1175 move_and_resize (bezier_points, bi, svg_glyph, units, glyph); 1176 1177 if (format == SvgFormat.ILLUSTRATOR) { 1178 path_list = create_paths_illustrator (bezier_points, bi); 1179 } else { 1180 path_list = create_paths_inkscape (bezier_points, bi); 1181 } 1182 1183 // TODO: Find out if it is possible to tie handles. 1184 return path_list; 1185 } 1186 1187 void move_and_resize (BezierPoints[] b, int num_b, bool svg_glyph, double units, Glyph glyph) { 1188 Font font = BirdFont.get_current_font (); 1189 1190 for (int i = 0; i < num_b; i++) { 1191 // resize all points 1192 b[i].x0 *= units; 1193 b[i].y0 *= units; 1194 b[i].x1 *= units; 1195 b[i].y1 *= units; 1196 b[i].x2 *= units; 1197 b[i].y2 *= units; 1198 1199 // move all points 1200 if (svg_glyph) { 1201 b[i].x0 += glyph.left_limit; 1202 b[i].y0 += font.base_line; 1203 b[i].x1 += glyph.left_limit; 1204 b[i].y1 += font.base_line; 1205 b[i].x2 += glyph.left_limit; 1206 b[i].y2 += font.base_line; 1207 } else { 1208 b[i].x0 += glyph.left_limit; 1209 b[i].y0 += font.top_position; 1210 b[i].x1 += glyph.left_limit; 1211 b[i].y1 += font.top_position; 1212 b[i].x2 += glyph.left_limit; 1213 b[i].y2 += font.top_position; 1214 } 1215 } 1216 } 1217 1218 void find_last_handle (int start_index, BezierPoints[] b, int num_b, out double left_x, out double left_y, out PointType last_type) { 1219 BezierPoints last = new BezierPoints (); 1220 1221 left_x = 0; 1222 left_y = 0; 1223 last_type = PointType.NONE; 1224 1225 return_if_fail (b.length != 0); 1226 return_if_fail (b[0].type != 'z'); 1227 return_if_fail (num_b < b.length); 1228 1229 for (int i = start_index; i < num_b; i++) { 1230 switch (b[i].type) { 1231 case 'Q': 1232 break; 1233 case 'C': 1234 break; 1235 case 'z': 1236 if (b[i - 1].type == 'Q') { 1237 return_if_fail (i >= 1); 1238 left_x = b[i - 1].x0; 1239 left_y = b[i - 1].y0; 1240 last_type = PointType.QUADRATIC; 1241 } else if (b[i - 1].type == 'C') { 1242 return_if_fail (i >= 1); 1243 left_x = b[i - 1].x1; 1244 left_y = b[i - 1].y1; 1245 last_type = PointType.CUBIC; 1246 } else if (b[i - 1].type == 'S') { 1247 return_if_fail (i >= 1); 1248 left_x = b[i - 1].x1; 1249 left_y = b[i - 1].y1; 1250 last_type = PointType.CUBIC; 1251 }else if (b[i - 1].type == 'L' || last.type == 'M') { 1252 return_if_fail (i >= 2); // FIXME: -2 can be C or L 1253 left_x = b[i - 2].x0 + (b[i - 1].x0 - b[i - 2].x0) / 3.0; 1254 left_y = b[i - 2].y0 + (b[i - 1].y0 - b[i - 2].y0) / 3.0; 1255 last_type = PointType.LINE_CUBIC; 1256 } else { 1257 warning (@"Unexpected type. $(b[i - 1])\n"); 1258 } 1259 return; 1260 default: 1261 break; 1262 } 1263 1264 last = b[i]; 1265 } 1266 1267 warning ("Expecting z"); 1268 } 1269 1270 PathList create_paths_inkscape (BezierPoints[] b, int num_b) { 1271 double last_x; 1272 double last_y; 1273 PointType last_type; 1274 Path path; 1275 PathList path_list = new PathList (); 1276 EditPoint ep = new EditPoint (); 1277 Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> (); 1278 1279 path = new Path (); 1280 1281 if (num_b == 0) { 1282 warning ("No SVG data"); 1283 return path_list; 1284 } 1285 1286 if (b[0].type != 'M') { 1287 warning ("Path must begin with M or m."); 1288 return path_list; 1289 } 1290 1291 find_last_handle (0, b, num_b, out last_x, out last_y, out last_type); 1292 1293 for (int i = 0; i < num_b; i++) { 1294 if (b[i].type == '\0') { 1295 warning ("Parser error."); 1296 return path_list; 1297 } 1298 1299 if (b[i].type == 'z') { 1300 path.close (); 1301 path.create_list (); 1302 path.recalculate_linear_handles (); 1303 path_list.add (path); 1304 path = new Path (); 1305 1306 if (i + 1 >= num_b) { 1307 break; 1308 } else { 1309 find_last_handle (i + 1, b, num_b, out last_x, out last_y, out last_type); 1310 } 1311 } 1312 1313 return_val_if_fail (i + 1 < num_b, path_list); 1314 1315 if (b[i].type == 'M') { 1316 ep = path.add (b[i].x0, b[i].y0); 1317 ep.set_point_type (PointType.CUBIC); 1318 1319 ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC); 1320 1321 if (i == 0 || (b[i - 1].type == 'z')) { 1322 ep.get_left_handle ().set_point_type (last_type); 1323 ep.get_left_handle ().move_to_coordinate (last_x, last_y); 1324 } else { 1325 if (b[i - 1].type == 'C' || b[i - 1].type == 'S') { 1326 ep.get_left_handle ().set_point_type (PointType.CUBIC); 1327 ep.get_left_handle ().move_to_coordinate (b[i + 1].x1, b[i + 1].y1); 1328 } 1329 1330 if (b[i + 1].type == 'C' || b[i - 1].type == 'S') { 1331 ep.get_right_handle ().set_point_type (PointType.CUBIC); 1332 ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0); 1333 } else if (b[i + 1].type == 'L' || b[i + 1].type == 'M') { 1334 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); 1335 } 1336 } 1337 } 1338 1339 if (b[i].type == 'L') { 1340 return_val_if_fail (i != 0, path_list); 1341 1342 ep = path.add (b[i].x0, b[i].y0); 1343 ep.set_point_type (PointType.CUBIC); 1344 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); 1345 ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC); 1346 1347 if (b[i + 1].type == 'L' || b[i + 1].type == 'M' || b[i + 1].type == 'z') { 1348 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); 1349 } 1350 1351 if (b[i -1].type == 'L' || b[i - 1].type == 'M') { 1352 ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC); 1353 } 1354 } 1355 1356 if (b[i].type == 'Q') { 1357 return_val_if_fail (i != 0, path_list); 1358 1359 ep.set_point_type (PointType.QUADRATIC); 1360 1361 ep.get_right_handle ().set_point_type (PointType.QUADRATIC); 1362 ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0); 1363 1364 if (b[i + 1].type != 'z') { 1365 ep = path.add (b[i].x1, b[i].y1); 1366 1367 ep.get_left_handle ().set_point_type (PointType.QUADRATIC); 1368 ep.get_left_handle ().move_to_coordinate (b[i].x0, b[i].y0); 1369 } 1370 } 1371 1372 if (b[i].type == 'C' || b[i].type == 'S') { 1373 return_val_if_fail (i != 0, path_list); 1374 1375 ep.set_point_type (PointType.CUBIC); 1376 1377 ep.get_right_handle ().set_point_type (PointType.CUBIC); 1378 ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0); 1379 1380 if (b[i].type == 'S') { 1381 smooth_points.add (ep); 1382 } 1383 1384 if (b[i + 1].type != 'z') { 1385 ep = path.add (b[i].x2, b[i].y2); 1386 1387 ep.get_left_handle ().set_point_type (PointType.CUBIC); 1388 ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1); 1389 } 1390 } 1391 } 1392 1393 foreach (EditPoint e in smooth_points) { 1394 e.set_point_type (PointType.LINE_DOUBLE_CURVE); 1395 e.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE); 1396 e.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE); 1397 } 1398 1399 foreach (EditPoint e in smooth_points) { 1400 e.recalculate_linear_handles (); 1401 } 1402 1403 1404 for (int i = 0; i < 3; i++) { 1405 foreach (EditPoint e in smooth_points) { 1406 e.set_tie_handle (true); 1407 e.process_tied_handle (); 1408 } 1409 } 1410 1411 if (path.points.size > 0) { 1412 path_list.add (path); 1413 } 1414 1415 foreach (Path p in path_list.paths) { 1416 p.remove_points_on_points (); 1417 } 1418 1419 return path_list; 1420 } 1421 1422 PathList create_paths_illustrator (BezierPoints[] b, int num_b) { 1423 Path path; 1424 PathList path_list = new PathList (); 1425 EditPoint ep; 1426 bool first_point = true; 1427 double first_left_x, first_left_y; 1428 Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> (); 1429 1430 if (num_b > b.length) { 1431 warning ("num_b > b.length: $num_b > $(b.length)"); 1432 return path_list; 1433 } 1434 1435 path = new Path (); 1436 1437 if (num_b <= 1) { 1438 warning ("No SVG data"); 1439 return path_list; 1440 } 1441 1442 first_left_x = 0; 1443 first_left_y = 0; 1444 1445 for (int i = 0; i < num_b; i++) { 1446 1447 if (b[i].type == '\0') { 1448 warning ("Parser error."); 1449 return path_list; 1450 } else if (b[i].type == 'z') { 1451 path.close (); 1452 path.create_list (); 1453 1454 if (b[1].type == 'C' || b[1].type == 'S') { 1455 return_val_if_fail (path.points.size != 0, path_list); 1456 ep = path.points.get (path.points.size - 1); 1457 ep.get_right_handle ().set_point_type (PointType.CUBIC); 1458 ep.get_right_handle ().move_to_coordinate (b[1].x0, b[1].y0); 1459 } 1460 1461 path.recalculate_linear_handles (); 1462 path_list.add (path); 1463 1464 path = new Path (); 1465 first_point = true; 1466 } else if (b[i].type == 'M') { 1467 } else if (b[i].type == 'L') { 1468 1469 if (first_point) { 1470 first_left_x = b[i].x0; 1471 first_left_y = b[i].y0; 1472 } 1473 1474 ep = path.add (b[i].x0, b[i].y0); 1475 ep.set_point_type (PointType.LINE_CUBIC); // TODO: quadratic 1476 ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); 1477 1478 if (b[i -1].type == 'L' || first_point) { 1479 //ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC); 1480 } 1481 1482 if (b[i + 1].type == 'C' || b[i + 1].type == 'S') { 1483 return_val_if_fail (i + 1 < num_b, path_list); 1484 ep.get_right_handle ().set_point_type (PointType.CUBIC); 1485 ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0); 1486 } 1487 1488 first_point = false; 1489 } else if (b[i].type == 'Q') { 1490 warning ("Illustrator does not support quadratic control points."); 1491 warning (@"$(b[i])\n"); 1492 } else if (b[i].type == 'C' || b[i].type == 'S') { 1493 1494 if (first_point) { 1495 first_left_x = b[i].x0; 1496 first_left_y = b[i].y0; 1497 } 1498 1499 ep = path.add (b[i].x2, b[i].y2); 1500 ep.set_point_type (PointType.CUBIC); 1501 1502 ep.get_right_handle ().set_point_type (PointType.CUBIC); 1503 ep.get_left_handle ().set_point_type (PointType.CUBIC); 1504 1505 ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1); 1506 1507 if (b[i].type == 'S') { 1508 smooth_points.add (ep); 1509 } 1510 1511 if (b[i + 1].type != 'z') { 1512 ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0); 1513 } else { 1514 ep.get_right_handle ().move_to_coordinate (first_left_x, first_left_y); 1515 } 1516 1517 first_point = false; 1518 } else { 1519 warning ("Unknown control point type."); 1520 warning (@"$(b[i])\n"); 1521 } 1522 } 1523 1524 foreach (EditPoint e in smooth_points) { 1525 e.set_point_type (PointType.LINE_DOUBLE_CURVE); 1526 e.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE); 1527 e.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE); 1528 } 1529 1530 foreach (EditPoint e in smooth_points) { 1531 e.recalculate_linear_handles (); 1532 } 1533 1534 1535 for (int i = 0; i < 3; i++) { 1536 foreach (EditPoint e in smooth_points) { 1537 e.set_tie_handle (true); 1538 e.process_tied_handle (); 1539 } 1540 } 1541 1542 if (path.points.size > 0) { 1543 warning ("Open path."); 1544 path_list.add (path); 1545 } 1546 1547 foreach (Path p in path_list.paths) { 1548 p.remove_points_on_points (); 1549 } 1550 1551 return path_list; 1552 } 1553 1554 // TODO: implement a default svg parser 1555 1556 static int parse_int (string? s) { 1557 if (is_null (s)) { 1558 warning ("null instead of string"); 1559 return 0; 1560 } 1561 1562 if (!is_point ((!) s)) { 1563 warning (@"Expecting an integer got: $((!) s)"); 1564 return 0; 1565 } 1566 1567 return int.parse ((!) s); 1568 } 1569 1570 static double parse_double (string? s) { 1571 if (is_null (s)) { 1572 warning ("Got null instead of expected string."); 1573 return 0; 1574 } 1575 1576 if (!is_point ((!) s)) { 1577 warning (@"Expecting a double got: $((!) s)"); 1578 return 0; 1579 } 1580 1581 return double.parse ((!) s); 1582 } 1583 1584 static bool is_point (string? s) { 1585 if (s == null) { 1586 warning ("s is null"); 1587 return false; 1588 } 1589 1590 return double.try_parse ((!) s); 1591 } 1592 1593 Path parse_polygon_data (string polygon_points) { 1594 string data = add_separators (polygon_points); 1595 string[] c = data.split (" "); 1596 Path path; 1597 BezierPoints[] bezier_points = new BezierPoints[c.length + 1]; 1598 int bi; 1599 Glyph g; 1600 1601 bi = 0; 1602 for (int i = 0; i < c.length - 1; i += 2) { 1603 if (i + 1 >= c.length) { 1604 warning ("No y value."); 1605 break; 1606 } 1607 1608 if (bi >= bezier_points.length) { 1609 warning ("End of bezier_points"); 1610 break; 1611 } 1612 1613 bezier_points[bi] = new BezierPoints (); 1614 bezier_points[bi].type == 'L'; 1615 bezier_points[bi].x0 = parse_double (c[i]); 1616 bezier_points[bi].y0 = -parse_double (c[i + 1]); 1617 bi++; 1618 } 1619 1620 g = MainWindow.get_current_glyph (); 1621 move_and_resize (bezier_points, bi, false, 1, g); 1622 1623 path = new Path (); 1624 for (int i = 0; i < bi; i++) { 1625 path.add (bezier_points[i].x0, bezier_points[i].y0); 1626 } 1627 1628 path.close (); 1629 path.create_list (); 1630 path.recalculate_linear_handles (); 1631 1632 return path; 1633 } 1634 } 1635 1636 } 1637