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