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