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