The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

SvgFile.vala in libsvgbird

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 libsvgbird/SvgFile.vala.
Move the SVG parser and the SVG rendering code to libsvgbird
1 /* 2 Copyright (C) 2016 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 B; 16 using Cairo; 17 18 namespace SvgBird { 19 20 public class SvgFile : GLib.Object { 21 22 SvgDrawing drawing; 23 24 public SvgFile () { 25 } 26 27 public SvgDrawing parse_svg_file (Tag tag) { 28 drawing = new SvgDrawing (); 29 30 foreach (Attribute attr in tag.get_attributes ()) { 31 if (attr.get_name () == "width") { 32 drawing.width = parse_number (attr.get_content ()); 33 } 34 35 if (attr.get_name () == "height") { 36 drawing.height = parse_number (attr.get_content ()); 37 } 38 } 39 40 foreach (Tag t in tag) { 41 string name = t.get_name (); 42 43 if (name == "g") { 44 parse_layer (drawing.root_layer, t); 45 } 46 47 if (name == "defs") { 48 parse_defs (drawing, t); 49 } 50 51 parse_object (drawing.root_layer, t); 52 } 53 54 return drawing; 55 } 56 57 private void parse_layer (Layer layer, Tag tag) { 58 bool hidden = false; 59 60 foreach (Attribute attr in tag.get_attributes ()) { 61 if (attr.get_name () == "display" && attr.get_content () == "none") { 62 hidden = true; 63 } 64 65 if (attr.get_name () == "visibility" 66 && (attr.get_content () == "hidden" 67 || attr.get_content () == "collapse")) { 68 hidden = true; 69 } 70 } 71 72 if (hidden) { 73 layer.visible = !hidden; 74 } 75 76 foreach (Tag t in tag) { 77 string name = t.get_name (); 78 79 if (name == "g") { 80 Layer sublayer = new Layer (); 81 parse_layer (layer, t); 82 layer.subgroups.add (sublayer); 83 } 84 85 parse_object (layer, t); 86 } 87 88 foreach (Attribute attr in tag.get_attributes ()) { 89 if (attr.get_name () == "transform") { 90 layer.transforms = parse_transform (attr.get_content ()); 91 } 92 } 93 } 94 95 void parse_defs (SvgDrawing drawing, Tag tag) { 96 foreach (Tag t in tag) { 97 // FIXME: radial 98 string name = t.get_name (); 99 100 if (name == "linearGradient") { 101 parse_linear_gradient (drawing, t); 102 } 103 } 104 105 foreach (Gradient gradient in drawing.defs.gradients) { 106 if (gradient.href != null) { 107 Gradient? referenced; 108 referenced = drawing.defs.get_gradient_for_id ((!) gradient.href); 109 110 if (referenced != null) { 111 gradient.copy_stops ((!) referenced); 112 } 113 114 gradient.href = null; 115 } 116 } 117 } 118 119 void parse_linear_gradient (SvgDrawing drawing, Tag tag) { 120 Gradient gradient = new Gradient (); 121 122 drawing.defs.add (gradient); 123 124 foreach (Attribute attr in tag.get_attributes ()) { 125 string name = attr.get_name (); 126 127 // FIXME: gradientUnits 128 129 if (name == "gradientTransform") { 130 gradient.transforms = parse_transform (attr.get_content ()); 131 } 132 133 if (name == "href") { 134 gradient.href = attr.get_content (); 135 } 136 137 if (name == "x1") { 138 gradient.x1 = parse_number (attr.get_content ()); 139 } 140 141 if (name == "y1") { 142 gradient.y1 = parse_number (attr.get_content ()); 143 } 144 145 if (name == "x2") { 146 gradient.x2 = parse_number (attr.get_content ()); 147 } 148 149 if (name == "y2") { 150 gradient.y2 = parse_number (attr.get_content ()); 151 } 152 153 if (name == "id") { 154 gradient.id = attr.get_content (); 155 } 156 } 157 158 foreach (Tag t in tag) { 159 // FIXME: radial 160 string name = t.get_name (); 161 162 if (name == "stop") { 163 parse_stop (gradient, t); 164 } 165 } 166 } 167 168 void parse_stop (Gradient gradient, Tag tag) { 169 SvgStyle style = SvgStyle.parse (drawing.defs, tag.get_attributes ()); 170 Stop stop = new Stop (); 171 172 gradient.stops.add (stop); 173 174 foreach (Attribute attr in tag.get_attributes ()) { 175 string name = attr.get_name (); 176 177 if (name == "offset") { 178 string stop_offset = attr.get_content (); 179 180 if (stop_offset.index_of ("%") > -1) { 181 stop_offset = stop_offset.replace ("%", ""); 182 stop.offset = parse_number (stop_offset) / 100.0; 183 } else { 184 stop.offset = parse_number (stop_offset); 185 } 186 } 187 } 188 189 string? stop_color = style.style.get ("stop-color"); 190 string? stop_opacity = style.style.get ("stop-opacity"); 191 Color? color = new Color (0, 0, 0, 1); 192 193 if (stop_color != null) { 194 color = Color.parse (stop_color); 195 196 if (color != null) { 197 stop.color = (!) color; 198 } 199 } 200 201 if (stop_opacity != null && color != null) { 202 ((!) color).a = parse_number (stop_opacity); 203 } 204 } 205 206 void parse_object (Layer layer, Tag tag) { 207 string name = tag.get_name (); 208 209 if (name == "path") { 210 parse_path (layer, tag); 211 } 212 213 if (name == "polygon") { 214 parse_polygon (layer, tag); 215 } 216 217 if (name == "polyline") { 218 parse_polyline (layer, tag); 219 } 220 221 if (name == "rect") { 222 parse_rect (layer, tag); 223 } 224 225 if (name == "circle") { 226 parse_circle (layer, tag); 227 } 228 229 if (name == "ellipse") { 230 parse_ellipse (layer, tag); 231 } 232 233 if (name == "line") { 234 parse_line (layer, tag); 235 } 236 } 237 238 private void parse_polygon (Layer layer, Tag tag) { 239 } 240 241 private void parse_polyline (Layer layer, Tag tag) { 242 } 243 244 private void parse_rect (Layer layer, Tag tag) { 245 Rectangle rectangle = new Rectangle (); 246 247 foreach (Attribute attr in tag.get_attributes ()) { 248 string attribute = attr.get_name (); 249 250 if (attribute == "x") { 251 rectangle.x = parse_number (attr.get_content ()); 252 } 253 254 if (attribute == "y") { 255 rectangle.y = parse_number (attr.get_content ()); 256 } 257 258 if (attribute == "width") { 259 rectangle.width = parse_number (attr.get_content ()); 260 } 261 262 if (attribute == "height") { 263 rectangle.height = parse_number (attr.get_content ()); 264 } 265 266 if (attribute == "rx") { 267 rectangle.rx = parse_number (attr.get_content ()); 268 } 269 270 if (attribute == "ry") { 271 rectangle.ry = parse_number (attr.get_content ()); 272 } 273 } 274 275 rectangle.transforms = get_transform (tag.get_attributes ()); 276 rectangle.style = SvgStyle.parse (drawing.defs,tag.get_attributes ()); 277 rectangle.visible = is_visible (tag); 278 279 layer.add_object (rectangle); 280 } 281 282 private void parse_circle (Layer layer, Tag tag) { 283 } 284 285 private void parse_ellipse (Layer layer, Tag tag) { 286 } 287 288 private void parse_line (Layer layer, Tag tag) { 289 } 290 291 // FIXME: reverse order? 292 public SvgTransforms parse_transform (string transforms) { 293 string[] functions; 294 string transform = transforms; 295 SvgTransforms transform_functions; 296 297 transform_functions = new SvgTransforms (); 298 299 transform = transform.replace ("\t", " "); 300 transform = transform.replace ("\n", " "); 301 transform = transform.replace ("\r", " "); 302 303 // use only a single space as separator 304 while (transform.index_of (" ") > -1) { 305 transform = transform.replace (" ", " "); 306 } 307 308 if (unlikely (transform.index_of (")") == -1)) { 309 warning ("No parenthesis in transform function."); 310 return transform_functions; 311 } 312 313 // add separator 314 transform = transform.replace (") ", "|"); 315 transform = transform.replace (")", "|"); 316 functions = transform.split ("|"); 317 318 for (int i = 0; i < functions.length; i++) { 319 if (functions[i].has_prefix ("translate")) { 320 transform_functions.add (translate (functions[i])); 321 } 322 323 if (functions[i].has_prefix ("scale")) { 324 transform_functions.add (scale (functions[i])); 325 } 326 327 if (functions[i].has_prefix ("matrix")) { 328 transform_functions.add (matrix (functions[i])); 329 } 330 331 // TODO: rotate etc. 332 } 333 334 return transform_functions; 335 } 336 337 private SvgTransform matrix (string function) { 338 string parameters = get_transform_parameters (function); 339 string[] p = parameters.split (" "); 340 SvgTransform transform = new SvgTransform (); 341 transform.type = TransformType.MATRIX; 342 343 if (unlikely (p.length != 6)) { 344 warning ("Expecting six parameters for matrix transformation."); 345 return transform; 346 } 347 348 for (int i = 0; i < 6; i++) { 349 double argument = parse_double (p[i]); 350 transform.arguments.add (argument); 351 } 352 353 return transform; 354 } 355 356 private static string remove_unit (string d) { 357 string s = d.replace ("pt", ""); 358 s = s.replace ("pc", ""); 359 s = s.replace ("mm", ""); 360 s = s.replace ("cm", ""); 361 s = s.replace ("in", ""); 362 s = s.replace ("px", ""); 363 return s; 364 } 365 366 public static double parse_number (string? number_with_unit) { 367 if (number_with_unit == null) { 368 return 0; 369 } 370 371 string d = (!) number_with_unit; 372 string s = remove_unit (d); 373 double n = parse_double (s); 374 375 if (d.has_suffix ("pt")) { 376 n *= 1.25; 377 } else if (d.has_suffix ("pc")) { 378 n *= 15; 379 } else if (d.has_suffix ("mm")) { 380 n *= 3.543307; 381 } else if (d.has_suffix ("cm")) { 382 n *= 35.43307; 383 } else if (d.has_suffix ("in")) { 384 n *= 90; 385 } 386 387 return n; 388 } 389 390 private SvgTransform scale (string function) { 391 string parameters = get_transform_parameters (function); 392 string[] p = parameters.split (" "); 393 SvgTransform transform = new SvgTransform (); 394 transform.type = TransformType.SCALE; 395 396 if (p.length > 0) { 397 transform.arguments.add (parse_double (p[0])); 398 } 399 400 if (p.length > 1) { 401 transform.arguments.add (parse_double (p[1])); 402 } 403 404 return transform; 405 } 406 407 private SvgTransform translate (string function) { 408 string parameters = get_transform_parameters (function); 409 string[] p = parameters.split (" "); 410 SvgTransform transform = new SvgTransform (); 411 transform.type = TransformType.TRANSLATE; 412 413 if (p.length > 0) { 414 transform.arguments.add (parse_double (p[0])); 415 } 416 417 if (p.length > 1) { 418 transform.arguments.add (parse_double (p[1])); 419 } 420 421 return transform; 422 } 423 424 private string get_transform_parameters (string function) { 425 int i; 426 string param = ""; 427 428 i = function.index_of ("("); 429 return_val_if_fail (i != -1, param); 430 param = function.substring (i); 431 432 param = param.replace ("(", ""); 433 param = param.replace ("\n", " "); 434 param = param.replace ("\t", " "); 435 param = param.replace (",", " "); 436 437 while (param.index_of (" ") > -1) { 438 param.replace (" ", " "); 439 } 440 441 return param.strip(); 442 } 443 444 private bool is_visible (Tag tag) { 445 bool hidden = false; 446 447 foreach (Attribute attr in tag.get_attributes ()) { 448 if (attr.get_name () == "display" && attr.get_content () == "none") { 449 hidden = true; 450 } 451 452 if (attr.get_name () == "visibility" 453 && (attr.get_content () == "hidden" 454 || attr.get_content () == "collapse")) { 455 hidden = true; 456 } 457 } 458 459 return !hidden; 460 } 461 462 private SvgTransforms get_transform (Attributes attributes) { 463 foreach (Attribute attr in attributes) { 464 if (attr.get_name () == "transform") { 465 return parse_transform (attr.get_content ()); 466 } 467 } 468 469 return new SvgTransforms (); 470 } 471 472 private void parse_path (Layer layer, Tag tag) { 473 SvgPath path = new SvgPath (); 474 475 foreach (Attribute attr in tag.get_attributes ()) { 476 if (attr.get_name () == "d") { 477 path.points = parse_points (attr.get_content ()); 478 } 479 } 480 481 path.transforms = get_transform (tag.get_attributes ()); 482 path.style = SvgStyle.parse (drawing.defs, tag.get_attributes ()); 483 path.visible = is_visible (tag); 484 485 layer.add_object (path); 486 } 487 488 public Gee.ArrayList<Points> parse_points (string data) { 489 Gee.ArrayList<Points> path_data = new Gee.ArrayList<Points> (); 490 Points points = new Points (); 491 BezierPoints[] bezier_points; 492 int points_size; 493 494 get_bezier_points (data, out bezier_points, out points_size, true); 495 496 for (int i = 0; i < points_size; i++) { 497 // FIXME: add more types 498 if (bezier_points[i].type == 'M') { 499 points.x = bezier_points[i].x0; 500 points.y = bezier_points[i].y0; 501 } else if (bezier_points[i].type == 'C') { 502 points.add (bezier_points[i].x0); 503 points.add (bezier_points[i].y0); 504 points.add (bezier_points[i].x1); 505 points.add (bezier_points[i].y1); 506 points.add (bezier_points[i].x2); 507 points.add (bezier_points[i].y2); 508 } else if (bezier_points[i].type == 'L') { 509 points.add (bezier_points[i].x0); 510 points.add (bezier_points[i].y0); 511 points.add (bezier_points[i].x0); 512 points.add (bezier_points[i].y0); 513 points.add (bezier_points[i].x0); 514 points.add (bezier_points[i].y0); 515 } else if (bezier_points[i].type == 'z') { 516 points.closed = true; 517 path_data.add (points); 518 points = new Points (); 519 } else { 520 string type = (!) bezier_points[i].type.to_string (); 521 warning (@"SVG conversion not implemented for $type"); 522 } 523 } 524 525 if (points.point_data.size > 0) { 526 path_data.add (points); 527 } 528 529 return path_data; 530 } 531 532 public static double parse_double (string? s) { 533 if (unlikely (s == null)) { 534 warning ("number is null"); 535 return 0; 536 } 537 538 if (unlikely (!double.try_parse ((!) s))) { 539 warning (@"Expecting a double got: $((!) s)"); 540 return 0; 541 } 542 543 return double.parse ((!) s); 544 } 545 546 547 public static void get_bezier_points (string point_data, out BezierPoints[] bezier_points, out int points, bool svg_glyph) { 548 double px = 0; 549 double py = 0; 550 double px2 = 0; 551 double py2 = 0; 552 double cx = 0; 553 double cy = 0; 554 string[] c; 555 double arc_rx, arc_ry; 556 double arc_rotation; 557 int large_arc; 558 int arc_sweep; 559 double arc_dest_x, arc_dest_y; 560 561 int bi = 0; 562 563 string data = add_separators (point_data); 564 c = data.split (" "); 565 566 // the arc instruction can use up to eight points 567 int bezier_points_length = 8 * c.length + 1; 568 bezier_points = new BezierPoints[bezier_points_length]; 569 570 for (int i = 0; i < bezier_points_length; i++) { 571 bezier_points[i] = new BezierPoints (); 572 } 573 574 // parse path 575 int i = -1; 576 while (++i < c.length && bi < bezier_points.length) { 577 if (c[i] == "m") { 578 while (i + 2 < c.length && is_point (c[i + 1])) { 579 bezier_points[bi].type = 'M'; 580 bezier_points[bi].svg_type = 'm'; 581 582 px += parse_double (c[++i]); 583 584 if (svg_glyph) { 585 py += parse_double (c[++i]); 586 } else { 587 py += -parse_double (c[++i]); 588 } 589 590 bezier_points[bi].x0 = px; 591 bezier_points[bi].y0 = py; 592 bi++; 593 } 594 } else if (c[i] == "M") { 595 while (i + 2 < c.length && is_point (c[i + 1])) { 596 bezier_points[bi].type = 'M'; 597 bezier_points[bi].svg_type = 'M'; 598 599 px = parse_double (c[++i]); 600 601 if (svg_glyph) { 602 py = parse_double (c[++i]); 603 } else { 604 py = -parse_double (c[++i]); 605 } 606 607 bezier_points[bi].x0 = px; 608 bezier_points[bi].y0 = py; 609 bi++; 610 } 611 } else if (c[i] == "h") { 612 while (i + 1 < c.length && is_point (c[i + 1])) { 613 bezier_points[bi].type = 'L'; 614 bezier_points[bi].svg_type = 'h'; 615 616 px += parse_double (c[++i]); 617 618 bezier_points[bi].x0 = px; 619 bezier_points[bi].y0 = py; 620 bi++; 621 } 622 } else if (i + 1 < c.length && c[i] == "H") { 623 while (is_point (c[i + 1])) { 624 bezier_points[bi].type = 'L'; 625 bezier_points[bi].svg_type = 'H'; 626 627 px = parse_double (c[++i]); 628 629 bezier_points[bi].x0 = px; 630 bezier_points[bi].y0 = py; 631 bi++; 632 } 633 } else if (c[i] == "v") { 634 while (i + 1 < c.length && is_point (c[i + 1])) { 635 bezier_points[bi].type = 'L'; 636 bezier_points[bi].svg_type = 'v'; 637 638 if (svg_glyph) { 639 py = py + parse_double (c[++i]); 640 } else { 641 py = py - parse_double (c[++i]); 642 } 643 644 bezier_points[bi].x0 = px; 645 bezier_points[bi].y0 = py; 646 bi++; 647 } 648 } else if (i + 1 < c.length && c[i] == "V") { 649 while (is_point (c[i + 1])) { 650 bezier_points[bi].type = 'L'; 651 bezier_points[bi].svg_type = 'V'; 652 653 if (svg_glyph) { 654 py = parse_double (c[++i]); 655 } else { 656 py = -parse_double (c[++i]); 657 } 658 659 bezier_points[bi].x0 = px; 660 bezier_points[bi].y0 = py; 661 bi++; 662 } 663 } else if (c[i] == "l") { 664 while (i + 2 < c.length && is_point (c[i + 1])) { 665 bezier_points[bi].type = 'L'; 666 bezier_points[bi].svg_type = 'l'; 667 668 cx = px + parse_double (c[++i]); 669 670 if (svg_glyph) { 671 cy = py + parse_double (c[++i]); 672 } else { 673 cy = py - parse_double (c[++i]); 674 } 675 676 px = cx; 677 py = cy; 678 679 bezier_points[bi].x0 = cx; 680 bezier_points[bi].y0 = cy; 681 bi++; 682 } 683 } else if (c[i] == "L") { 684 while (i + 2 < c.length && is_point (c[i + 1])) { 685 bezier_points[bi].type = 'L'; 686 bezier_points[bi].svg_type = 'L'; 687 688 cx = parse_double (c[++i]); 689 690 if (svg_glyph) { 691 cy = parse_double (c[++i]); 692 } else { 693 cy = -parse_double (c[++i]); 694 } 695 696 px = cx; 697 py = cy; 698 699 bezier_points[bi].x0 = cx; 700 bezier_points[bi].y0 = cy; 701 bi++; 702 } 703 } else if (c[i] == "c") { 704 while (i + 6 < c.length && is_point (c[i + 1])) { 705 bezier_points[bi].type = 'C'; 706 bezier_points[bi].svg_type = 'C'; 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].x0 = cx; 717 bezier_points[bi].y0 = cy; 718 719 cx = px + parse_double (c[++i]); 720 721 if (svg_glyph) { 722 cy = py + parse_double (c[++i]); 723 } else { 724 cy = py - parse_double (c[++i]); 725 } 726 727 px2 = cx; 728 py2 = cy; 729 730 bezier_points[bi].x1 = px2; 731 bezier_points[bi].y1 = py2; 732 733 cx = px + parse_double (c[++i]); 734 735 if (svg_glyph) { 736 cy = py + parse_double (c[++i]); 737 } else { 738 cy = py + -parse_double (c[++i]); 739 } 740 741 bezier_points[bi].x2 = cx; 742 bezier_points[bi].y2 = cy; 743 744 px = cx; 745 py = cy; 746 747 bi++; 748 } 749 } else if (c[i] == "C") { 750 while (i + 6 < c.length && is_point (c[i + 1])) { 751 bezier_points[bi].type = 'C'; 752 bezier_points[bi].svg_type = 'C'; 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].x0 = cx; 763 bezier_points[bi].y0 = cy; 764 765 cx = parse_double (c[++i]); 766 767 if (svg_glyph) { 768 cy = parse_double (c[++i]); 769 } else { 770 cy = -parse_double (c[++i]); 771 } 772 773 px2 = cx; 774 py2 = cy; 775 776 bezier_points[bi].x1 = cx; 777 bezier_points[bi].y1 = cy; 778 779 cx = parse_double (c[++i]); 780 781 if (svg_glyph) { 782 cy = parse_double (c[++i]); 783 } else { 784 cy = -parse_double (c[++i]); 785 } 786 787 bezier_points[bi].x2 = cx; 788 bezier_points[bi].y2 = cy; 789 790 px = cx; 791 py = cy; 792 793 bi++; 794 } 795 } else if (c[i] == "q") { 796 while (i + 4 < c.length && is_point (c[i + 1])) { 797 bezier_points[bi].type = 'Q'; 798 bezier_points[bi].svg_type = 'q'; 799 800 cx = px + parse_double (c[++i]); 801 802 if (svg_glyph) { 803 cy = py + parse_double (c[++i]); 804 } else { 805 cy = py - parse_double (c[++i]); 806 } 807 808 bezier_points[bi].x0 = cx; 809 bezier_points[bi].y0 = cy; 810 811 px2 = cx; 812 py2 = cy; 813 814 cx = px + parse_double (c[++i]); 815 816 if (svg_glyph) { 817 cy = py + parse_double (c[++i]); 818 } else { 819 cy = py - parse_double (c[++i]); 820 } 821 822 bezier_points[bi].x1 = cx; 823 bezier_points[bi].y1 = cy; 824 825 px = cx; 826 py = cy; 827 828 bi++; 829 } 830 } else if (c[i] == "Q") { 831 while (i + 4 < c.length && is_point (c[i + 1])) { 832 bezier_points[bi].type = 'Q'; 833 bezier_points[bi].svg_type = 'Q'; 834 835 cx = parse_double (c[++i]); 836 837 if (svg_glyph) { 838 cy = parse_double (c[++i]); 839 } else { 840 cy = -parse_double (c[++i]); 841 } 842 843 bezier_points[bi].x0 = cx; 844 bezier_points[bi].y0 = cy; 845 846 px2 = cx; 847 py2 = cy; 848 849 cx = parse_double (c[++i]); 850 851 if (svg_glyph) { 852 cy = parse_double (c[++i]); 853 } else { 854 cy = -parse_double (c[++i]); 855 } 856 857 px = cx; 858 py = cy; 859 860 bezier_points[bi].x1 = cx; 861 bezier_points[bi].y1 = cy; 862 863 bi++; 864 } 865 } else if (c[i] == "t") { 866 while (i + 2 < c.length && is_point (c[i + 1])) { 867 bezier_points[bi].type = 'Q'; 868 bezier_points[bi].svg_type = 't'; 869 870 // the first point is the reflection 871 cx = 2 * px - px2; 872 cy = 2 * py - py2; // if (svg_glyph) ? 873 874 bezier_points[bi].x0 = cx; 875 bezier_points[bi].y0 = cy; 876 877 px2 = cx; 878 py2 = cy; 879 880 cx = px + parse_double (c[++i]); 881 882 if (svg_glyph) { 883 cy = py + parse_double (c[++i]); 884 } else { 885 cy = py - parse_double (c[++i]); 886 } 887 888 px = cx; 889 py = cy; 890 891 bezier_points[bi].x1 = px; 892 bezier_points[bi].y1 = py; 893 894 bi++; 895 } 896 } else if (c[i] == "T") { 897 while (i + 2 < c.length && is_point (c[i + 1])) { 898 bezier_points[bi].type = 'Q'; 899 bezier_points[bi].svg_type = 'T'; 900 901 // the reflection 902 cx = 2 * px - px2; 903 cy = 2 * py - py2; 904 905 bezier_points[bi].x0 = cx; 906 bezier_points[bi].y0 = cy; 907 908 px2 = cx; 909 py2 = cy; 910 911 cx = parse_double (c[++i]); 912 913 if (svg_glyph) { 914 cy = parse_double (c[++i]); 915 } else { 916 cy = -parse_double (c[++i]); 917 } 918 919 px = cx; 920 py = cy; 921 922 bezier_points[bi].x1 = px; 923 bezier_points[bi].y1 = py; 924 925 bi++; 926 } 927 } else if (c[i] == "s") { 928 while (i + 4 < c.length && is_point (c[i + 1])) { 929 bezier_points[bi].type = 'C'; 930 bezier_points[bi].svg_type = 's'; 931 932 // the first point is the reflection 933 cx = 2 * px - px2; 934 cy = 2 * py - py2; // if (svg_glyph) ? 935 936 bezier_points[bi].x0 = cx; 937 bezier_points[bi].y0 = cy; 938 939 cx = px + parse_double (c[++i]); 940 941 if (svg_glyph) { 942 cy = py + parse_double (c[++i]); 943 } else { 944 cy = py - parse_double (c[++i]); 945 } 946 947 px2 = cx; 948 py2 = cy; 949 950 bezier_points[bi].x1 = px2; 951 bezier_points[bi].y1 = py2; 952 953 cx = px + parse_double (c[++i]); 954 955 if (svg_glyph) { 956 cy = py + parse_double (c[++i]); 957 } else { 958 cy = py - parse_double (c[++i]); 959 } 960 961 bezier_points[bi].x2 = cx; 962 bezier_points[bi].y2 = cy; 963 964 px = cx; 965 py = cy; 966 967 bi++; 968 } 969 } else if (c[i] == "S") { 970 while (i + 4 < c.length && is_point (c[i + 1])) { 971 bezier_points[bi].type = 'C'; 972 bezier_points[bi].svg_type = 'S'; 973 974 // the reflection 975 cx = 2 * px - px2; 976 cy = 2 * py - py2; // if (svg_glyph) ? 977 978 bezier_points[bi].x0 = cx; 979 bezier_points[bi].y0 = cy; 980 981 // the other two are regular cubic points 982 cx = parse_double (c[++i]); 983 984 if (svg_glyph) { 985 cy = parse_double (c[++i]); 986 } else { 987 cy = -parse_double (c[++i]); 988 } 989 990 px2 = cx; 991 py2 = cy; 992 993 bezier_points[bi].x1 = px2; 994 bezier_points[bi].y1 = py2; 995 996 cx = parse_double (c[++i]); 997 998 if (svg_glyph) { 999 cy = parse_double (c[++i]); 1000 } else { 1001 cy = -parse_double (c[++i]); 1002 } 1003 1004 bezier_points[bi].x2 = cx; 1005 bezier_points[bi].y2 = cy; 1006 1007 px = cx; 1008 py = cy; 1009 1010 bi++; 1011 } 1012 } else if (c[i] == "a") { 1013 while (i + 7 < c.length && is_point (c[i + 1])) { 1014 arc_rx = parse_double (c[++i]); 1015 arc_ry = parse_double (c[++i]); 1016 1017 arc_rotation = parse_double (c[++i]); 1018 large_arc = parse_int (c[++i]); 1019 arc_sweep = parse_int (c[++i]); 1020 1021 cx = px + parse_double (c[++i]); 1022 1023 if (svg_glyph) { 1024 cy = py + parse_double (c[++i]); 1025 } else { 1026 cy = py - parse_double (c[++i]); 1027 } 1028 1029 arc_dest_x = cx; 1030 arc_dest_y = cy; 1031 1032 add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy); 1033 1034 px = cx; 1035 py = cy; 1036 } 1037 } else if (i + 7 < c.length && c[i] == "A") { 1038 while (is_point (c[i + 1])) { 1039 arc_rx = parse_double (c[++i]); 1040 arc_ry = parse_double (c[++i]); 1041 1042 arc_rotation = parse_double (c[++i]); 1043 large_arc = parse_int (c[++i]); 1044 arc_sweep = parse_int (c[++i]); 1045 1046 cx = parse_double (c[++i]); 1047 1048 if (svg_glyph) { 1049 cy = parse_double (c[++i]); 1050 } else { 1051 cy = -parse_double (c[++i]); 1052 } 1053 1054 arc_dest_x = cx; 1055 arc_dest_y = cy; 1056 1057 add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy); 1058 1059 px = cx; 1060 py = cy; 1061 1062 1063 } 1064 } else if (c[i] == "z") { 1065 bezier_points[bi].type = 'z'; 1066 bezier_points[bi].svg_type = 'z'; 1067 1068 bi++; 1069 } else if (c[i] == "Z") { 1070 bezier_points[bi].type = 'z'; 1071 bezier_points[bi].svg_type = 'z'; 1072 1073 bi++; 1074 } else if (c[i] == "") { 1075 } else if (c[i] == " ") { 1076 } else { 1077 warning (@"Unknown instruction: $(c[i])"); 1078 } 1079 } 1080 1081 if (bi == 0) { 1082 warning ("No points in path."); 1083 } 1084 1085 points = bi; 1086 } 1087 1088 static int parse_int (string? s) { 1089 if (unlikely (s == null)) { 1090 warning ("null instead of string"); 1091 return 0; 1092 } 1093 1094 if (unlikely (!int64.try_parse ((!) s))) { 1095 warning (@"Expecting an integer: $((!) s)"); 1096 return 0; 1097 } 1098 1099 return int.parse ((!) s); 1100 } 1101 1102 static bool is_point (string? s) { 1103 if (unlikely (s == null)) { 1104 warning ("s is null"); 1105 return false; 1106 } 1107 1108 return double.try_parse ((!) s); 1109 } 1110 1111 /** Add space as separator to svg data. 1112 * @param d svg data 1113 */ 1114 public static string add_separators (string d) { 1115 string data = d; 1116 1117 data = data.replace (",", " "); 1118 data = data.replace ("a", " a "); 1119 data = data.replace ("A", " A "); 1120 data = data.replace ("m", " m "); 1121 data = data.replace ("M", " M "); 1122 data = data.replace ("h", " h "); 1123 data = data.replace ("H", " H "); 1124 data = data.replace ("v", " v "); 1125 data = data.replace ("V", " V "); 1126 data = data.replace ("l", " l "); 1127 data = data.replace ("L", " L "); 1128 data = data.replace ("q", " q "); 1129 data = data.replace ("Q", " Q "); 1130 data = data.replace ("c", " c "); 1131 data = data.replace ("C", " C "); 1132 data = data.replace ("t", " t "); 1133 data = data.replace ("T", " T "); 1134 data = data.replace ("s", " s "); 1135 data = data.replace ("S", " S "); 1136 data = data.replace ("zM", " z M "); 1137 data = data.replace ("zm", " z m "); 1138 data = data.replace ("z", " z "); 1139 data = data.replace ("Z", " Z "); 1140 data = data.replace ("-", " -"); 1141 data = data.replace ("e -", "e-"); // minus can be either separator or a negative exponent 1142 data = data.replace ("\t", " "); 1143 data = data.replace ("\r\n", " "); 1144 data = data.replace ("\n", " "); 1145 1146 // use only a single space as separator 1147 while (data.index_of (" ") > -1) { 1148 data = data.replace (" ", " "); 1149 } 1150 1151 return data; 1152 } 1153 1154 } 1155 1156 } 1157