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.
Rotation
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 using Math; 18 19 namespace SvgBird { 20 21 public enum SvgFormat { 22 NONE, 23 INKSCAPE, 24 ILLUSTRATOR 25 } 26 27 public class SvgFile : GLib.Object { 28 29 SvgDrawing drawing; 30 SvgFormat format = SvgFormat.ILLUSTRATOR; 31 32 public SvgFile () { 33 } 34 35 public SvgDrawing parse_svg_data (string xml_data) { 36 XmlTree tree = new XmlTree (xml_data); 37 38 if (xml_data.index_of ("Illustrator") > -1 || xml_data.index_of ("illustrator") > -1) { 39 format = SvgFormat.ILLUSTRATOR; 40 } else if (xml_data.index_of ("Inkscape") > -1 || xml_data.index_of ("inkscape") > -1) { 41 format = SvgFormat.INKSCAPE; 42 } 43 44 return parse_svg_file (tree.get_root (), format); 45 } 46 47 public SvgDrawing parse_svg_file (XmlElement svg_tag, SvgFormat format) { 48 drawing = new SvgDrawing (); 49 this.format = format; 50 51 SvgStyle style = new SvgStyle (); 52 SvgStyle.parse (drawing.defs, style, svg_tag, null); 53 54 foreach (Attribute attr in svg_tag.get_attributes ()) { 55 if (attr.get_name () == "width") { 56 drawing.width = parse_number (attr.get_content ()); 57 } 58 59 if (attr.get_name () == "height") { 60 drawing.height = parse_number (attr.get_content ()); 61 } 62 63 if (attr.get_name () == "viewBox") { 64 drawing.view_box = parse_view_box (svg_tag); 65 } 66 } 67 68 foreach (XmlElement t in svg_tag) { 69 string name = t.get_name (); 70 71 if (name == "g") { 72 parse_layer (drawing.root_layer, style, t); 73 } 74 75 if (name == "svg") { 76 SvgDrawing embedded = parse_svg_file (t, format); 77 drawing.root_layer.add_object (embedded); 78 } 79 80 if (name == "defs") { 81 parse_defs (drawing, t); 82 } 83 84 if (name == "a") { 85 parse_link (drawing.root_layer, style, svg_tag); 86 } 87 88 parse_object (drawing.root_layer, style, t); 89 } 90 91 set_object_properties (drawing, new SvgStyle (), svg_tag); 92 93 return drawing; 94 } 95 96 public static ViewBox? parse_view_box (XmlElement tag) { 97 string arguments; 98 string parameters = ""; 99 string aspect_ratio = ""; 100 bool slice = true; 101 bool preserve_aspect_ratio = true; 102 103 foreach (Attribute attribute in tag.get_attributes ()) { 104 if (attribute.get_name () == "viewBox") { 105 parameters = attribute.get_content (); 106 } 107 108 if (attribute.get_name () == "preserveAspectRatio") { 109 aspect_ratio = attribute.get_content (); 110 111 string[] preserveSettings = aspect_ratio.split (" "); 112 113 if (preserveSettings.length >= 1) { 114 aspect_ratio = preserveSettings[0]; 115 } 116 117 if (preserveSettings.length >= 2) { 118 slice = preserveSettings[1] == "slice"; 119 } 120 121 preserve_aspect_ratio = false; 122 } 123 } 124 125 arguments = parameters.replace (",", " "); 126 127 while (arguments.index_of (" ") > -1) { 128 arguments = arguments.replace (" ", " "); 129 } 130 131 string[] view_box_parameters = arguments.split (" "); 132 133 if (view_box_parameters.length != 4) { 134 warning ("Expecting four arguments in view box."); 135 return null; 136 } 137 138 double minx = parse_number (view_box_parameters[0]); 139 double miny = parse_number (view_box_parameters[1]); 140 double width = parse_number (view_box_parameters[2]); 141 double height = parse_number (view_box_parameters[3]); 142 143 uint alignment = ViewBox.XMID_YMID; 144 aspect_ratio = aspect_ratio.up (); 145 146 if (aspect_ratio == "NONE") { 147 alignment = ViewBox.NONE; 148 } else if (aspect_ratio == "XMINYMIN") { 149 alignment = ViewBox.XMIN_YMIN; 150 } else if (aspect_ratio == "XMINYMIN") { 151 alignment = ViewBox.XMIN_YMIN; 152 } else if (aspect_ratio == "XMAXYMIN") { 153 alignment = ViewBox.XMIN_YMIN; 154 } else if (aspect_ratio == "XMINYMID") { 155 alignment = ViewBox.XMIN_YMIN; 156 } else if (aspect_ratio == "XMIDYMID") { 157 alignment = ViewBox.XMIN_YMIN; 158 } else if (aspect_ratio == "XMAXYMID") { 159 alignment = ViewBox.XMIN_YMIN; 160 } else if (aspect_ratio == "XMINYMAX") { 161 alignment = ViewBox.XMIN_YMIN; 162 } else if (aspect_ratio == "XMIDYMAX") { 163 alignment = ViewBox.XMIN_YMIN; 164 } else if (aspect_ratio == "XMAXYMAX") { 165 alignment = ViewBox.XMIN_YMIN; 166 } 167 168 return new ViewBox (minx, miny, width, height, alignment, slice, preserve_aspect_ratio); 169 } 170 171 private void parse_layer (Layer layer, SvgStyle parent_style, XmlElement tag) { 172 foreach (XmlElement t in tag) { 173 string name = t.get_name (); 174 175 if (name == "g") { 176 Layer sublayer = new Layer (); 177 parse_layer (sublayer, parent_style, t); 178 layer.objects.add (sublayer); 179 } 180 181 if (name == "a") { 182 parse_link (layer, parent_style, t); 183 } 184 185 parse_object (layer, parent_style, t); 186 } 187 188 set_object_properties (layer, parent_style, tag); 189 } 190 191 void parse_clip_path (SvgDrawing drawing, XmlElement tag) { 192 ClipPath clip_path; 193 194 Layer layer = new Layer (); 195 parse_layer (layer, new SvgStyle (), tag); 196 clip_path = new ClipPath (layer); 197 198 drawing.defs.clip_paths.add (clip_path); 199 } 200 201 void parse_defs (SvgDrawing drawing, XmlElement tag) { 202 foreach (XmlElement t in tag) { 203 string name = t.get_name (); 204 205 if (name == "linearGradient") { 206 parse_linear_gradient (drawing, t); 207 } else if (name == "radialGradient") { 208 parse_radial_gradient (drawing, t); 209 } else if (name == "clipPath") { 210 parse_clip_path (drawing, t); 211 } 212 } 213 214 foreach (Gradient gradient in drawing.defs.gradients) { 215 if (gradient.href != null) { 216 Gradient? referenced; 217 referenced = drawing.defs.get_gradient_for_id ((!) gradient.href); 218 219 if (referenced != null) { 220 gradient.copy_stops ((!) referenced); 221 } 222 223 gradient.href = null; 224 } 225 } 226 227 foreach (XmlElement t in tag) { 228 // FIXME: radial 229 string name = t.get_name (); 230 231 if (name == "style") { 232 drawing.defs.style_sheet = StyleSheet.parse (drawing.defs, t); 233 } 234 } 235 236 } 237 238 void parse_radial_gradient (SvgDrawing drawing, XmlElement tag) { 239 RadialGradient gradient = new RadialGradient (); 240 241 drawing.defs.add_radial_gradient (gradient); 242 243 foreach (Attribute attr in tag.get_attributes ()) { 244 string name = attr.get_name (); 245 246 // FIXME: gradientUnits 247 248 if (name == "gradientTransform") { 249 gradient.transforms = parse_transform (attr.get_content ()); 250 } 251 252 if (name == "href") { 253 gradient.href = attr.get_content (); 254 } 255 256 if (name == "cx") { 257 gradient.cx = parse_number (attr.get_content ()); 258 } 259 260 if (name == "cy") { 261 gradient.cy = parse_number (attr.get_content ()); 262 } 263 264 if (name == "fx") { 265 gradient.fx = parse_number (attr.get_content ()); 266 } 267 268 if (name == "fy") { 269 gradient.fy = parse_number (attr.get_content ()); 270 } 271 272 if (name == "r") { 273 gradient.r = parse_number (attr.get_content ()); 274 } 275 276 if (name == "id") { 277 gradient.id = attr.get_content (); 278 } 279 } 280 281 foreach (XmlElement t in tag) { 282 string name = t.get_name (); 283 284 if (name == "stop") { 285 parse_stop (gradient, t); 286 } 287 } 288 } 289 290 void parse_linear_gradient (SvgDrawing drawing, XmlElement tag) { 291 LinearGradient gradient = new LinearGradient (); 292 293 drawing.defs.add_linear_gradient (gradient); 294 295 foreach (Attribute attr in tag.get_attributes ()) { 296 string name = attr.get_name (); 297 298 // FIXME: gradientUnits 299 300 if (name == "gradientTransform") { 301 gradient.transforms = parse_transform (attr.get_content ()); 302 } 303 304 if (name == "href") { 305 gradient.href = attr.get_content (); 306 } 307 308 if (name == "x1") { 309 gradient.x1 = parse_number (attr.get_content ()); 310 } 311 312 if (name == "y1") { 313 gradient.y1 = parse_number (attr.get_content ()); 314 } 315 316 if (name == "x2") { 317 gradient.x2 = parse_number (attr.get_content ()); 318 } 319 320 if (name == "y2") { 321 gradient.y2 = parse_number (attr.get_content ()); 322 } 323 324 if (name == "id") { 325 gradient.id = attr.get_content (); 326 } 327 } 328 329 foreach (XmlElement t in tag) { 330 string name = t.get_name (); 331 332 if (name == "stop") { 333 parse_stop (gradient, t); 334 } 335 } 336 } 337 338 void parse_stop (Gradient gradient, XmlElement tag) { 339 SvgStyle parent_style = new SvgStyle (); // not inherited 340 SvgStyle style = SvgStyle.parse (drawing.defs, parent_style, tag, null); 341 Stop stop = new Stop (); 342 343 gradient.stops.add (stop); 344 345 foreach (Attribute attr in tag.get_attributes ()) { 346 string name = attr.get_name (); 347 348 if (name == "offset") { 349 string stop_offset = attr.get_content (); 350 351 if (stop_offset.index_of ("%") > -1) { 352 stop_offset = stop_offset.replace ("%", ""); 353 stop.offset = parse_number (stop_offset) / 100.0; 354 } else { 355 stop.offset = parse_number (stop_offset); 356 } 357 } 358 } 359 360 string? stop_color = style.style.get ("stop-color"); 361 string? stop_opacity = style.style.get ("stop-opacity"); 362 Color? color = new Color (0, 0, 0, 1); 363 364 if (stop_color != null) { 365 color = Color.parse (stop_color); 366 367 if (color != null) { 368 stop.color = (!) color; 369 } 370 } 371 372 if (stop_opacity != null && color != null) { 373 ((!) color).a = parse_number (stop_opacity); 374 } 375 } 376 377 // links are ignored, add the content to the layer 378 void parse_link (Layer layer, SvgStyle parent_style, XmlElement tag) { 379 parse_layer (layer, parent_style, tag); 380 } 381 382 void parse_object (Layer layer, SvgStyle parent_style, XmlElement tag) { 383 string name = tag.get_name (); 384 385 if (name == "path") { 386 parse_path (layer, parent_style, tag); 387 } 388 389 if (name == "polygon") { 390 parse_polygon (layer, parent_style, tag); 391 } 392 393 if (name == "polyline") { 394 parse_polyline (layer, parent_style, tag); 395 } 396 397 if (name == "rect") { 398 parse_rect (layer, parent_style, tag); 399 } 400 401 if (name == "circle") { 402 parse_circle (layer, parent_style, tag); 403 } 404 405 if (name == "ellipse") { 406 parse_ellipse (layer, parent_style, tag); 407 } 408 409 if (name == "line") { 410 parse_line (layer, parent_style, tag); 411 } 412 413 if (name == "text") { 414 parse_text (layer, parent_style, tag); 415 } 416 417 if (name == "svg") { 418 SvgDrawing embedded = parse_svg_file (tag, format); 419 layer.add_object (embedded); 420 } 421 422 } 423 424 private void parse_polygon (Layer layer, SvgStyle parent_style, XmlElement tag) { 425 Polygon polygon = new Polygon (); 426 427 foreach (Attribute attr in tag.get_attributes ()) { 428 if (attr.get_name () == "points") { 429 string data = add_separators (attr.get_content ()); 430 string[] point_data = data.split (" "); 431 432 for (int i = 0; i < point_data.length - 1; i++) { 433 string number_x = point_data[i]; 434 string number_y = point_data[i + 1]; 435 polygon.points.add_type (POINT_LINE); 436 polygon.points.add (parse_number (number_x)); 437 polygon.points.add (parse_number (number_y)); 438 polygon.points.add (0); 439 polygon.points.add (0); 440 polygon.points.add (0); 441 polygon.points.add (0); 442 polygon.points.add (0); 443 } 444 } 445 } 446 447 set_object_properties (polygon, parent_style, tag); 448 layer.add_object (polygon); 449 } 450 451 void set_object_properties (Object object, SvgStyle parent_style, XmlElement tag) { 452 Attributes attributes = tag.get_attributes (); 453 454 foreach (Attribute attribute in attributes) { 455 string name = attribute.get_name (); 456 457 if (name == "id") { 458 object.id = attribute.get_content (); 459 } else if (name == "class") { 460 object.css_class = attribute.get_content (); 461 } 462 } 463 464 object.clip_path = get_clip_path (attributes); 465 object.transforms = get_transform (attributes); 466 object.style = SvgStyle.parse (drawing.defs, parent_style, tag, null); 467 object.visible = is_visible (tag); 468 } 469 470 ClipPath? get_clip_path (Attributes attributes) { 471 foreach (Attribute attribute in attributes) { 472 if (attribute.get_name () == "clip-path") { 473 return drawing.defs.get_clip_path_for_url (attribute.get_content ()); 474 } 475 } 476 477 return null; 478 } 479 480 private void parse_polyline (Layer layer, SvgStyle parent_style, XmlElement tag) { 481 Polyline polyline = new Polyline (); 482 483 foreach (Attribute attr in tag.get_attributes ()) { 484 if (attr.get_name () == "points") { 485 string data = add_separators (attr.get_content ()); 486 string[] point_data = data.split (" "); 487 488 foreach (string number in point_data) { 489 polyline.points.add (parse_number (number)); 490 } 491 } 492 } 493 494 set_object_properties (polyline, parent_style, tag); 495 layer.add_object (polyline); 496 } 497 498 private void parse_rect (Layer layer, SvgStyle parent_style, XmlElement tag) { 499 Rectangle rectangle = new Rectangle (); 500 501 foreach (Attribute attr in tag.get_attributes ()) { 502 string attribute = attr.get_name (); 503 504 if (attribute == "x") { 505 rectangle.x = parse_number (attr.get_content ()); 506 } 507 508 if (attribute == "y") { 509 rectangle.y = parse_number (attr.get_content ()); 510 } 511 512 if (attribute == "width") { 513 rectangle.width = parse_number (attr.get_content ()); 514 } 515 516 if (attribute == "height") { 517 rectangle.height = parse_number (attr.get_content ()); 518 } 519 520 if (attribute == "rx") { 521 rectangle.rx = parse_number (attr.get_content ()); 522 } 523 524 if (attribute == "ry") { 525 rectangle.ry = parse_number (attr.get_content ()); 526 } 527 } 528 529 set_object_properties (rectangle, parent_style, tag); 530 layer.add_object (rectangle); 531 } 532 533 private void parse_circle (Layer layer, SvgStyle parent_style, XmlElement tag) { 534 Circle circle = new Circle (); 535 536 foreach (Attribute attr in tag.get_attributes ()) { 537 string name = attr.get_name (); 538 539 if (name == "cx") { 540 circle.cx = parse_number (attr.get_content ()); 541 } 542 543 if (name == "cy") { 544 circle.cy = parse_number (attr.get_content ()); 545 } 546 547 if (name == "r") { 548 circle.r = parse_number (attr.get_content ()); 549 } 550 } 551 552 set_object_properties (circle, parent_style, tag); 553 layer.add_object (circle); 554 } 555 556 private void parse_ellipse (Layer layer, SvgStyle parent_style, XmlElement tag) { 557 Ellipse ellipse = new Ellipse (); 558 559 foreach (Attribute attr in tag.get_attributes ()) { 560 string name = attr.get_name (); 561 562 if (name == "cx") { 563 ellipse.cx = parse_number (attr.get_content ()); 564 } 565 566 if (name == "cy") { 567 ellipse.cy = parse_number (attr.get_content ()); 568 } 569 570 if (name == "rx") { 571 ellipse.rx = parse_number (attr.get_content ()); 572 } 573 574 if (name == "ry") { 575 ellipse.ry = parse_number (attr.get_content ()); 576 } 577 } 578 579 set_object_properties (ellipse, parent_style, tag); 580 layer.add_object (ellipse); 581 } 582 583 private void parse_line (Layer layer, SvgStyle parent_style, XmlElement tag) { 584 Line line = new Line (); 585 586 foreach (Attribute attr in tag.get_attributes ()) { 587 string name = attr.get_name (); 588 589 if (name == "x1") { 590 line.x1 = parse_number (attr.get_content ()); 591 } 592 593 if (name == "y1") { 594 line.y1 = parse_number (attr.get_content ()); 595 } 596 597 if (name == "x2") { 598 line.x2 = parse_number (attr.get_content ()); 599 } 600 601 if (name == "y2") { 602 line.y2 = parse_number (attr.get_content ()); 603 } 604 } 605 606 set_object_properties (line, parent_style, tag); 607 layer.add_object (line); 608 } 609 610 private void parse_text (Layer layer, SvgStyle parent_style, XmlElement tag) { 611 Text text = new Text (); 612 613 foreach (Attribute attr in tag.get_attributes ()) { 614 string name = attr.get_name (); 615 616 if (name == "font-size") { 617 text.set_font_size ((int) parse_number (attr.get_content ())); 618 } 619 620 if (name == "font-family") { 621 text.set_font (attr.get_content ()); 622 } 623 624 if (name == "x") { 625 text.x = parse_number (attr.get_content ()); 626 } 627 628 if (name == "y") { 629 text.y = parse_number (attr.get_content ()); 630 } 631 } 632 633 text.set_text (tag.get_content ()); 634 635 set_object_properties (text, parent_style, tag); 636 layer.add_object (text); 637 } 638 639 // FIXME: reverse order? 640 public static SvgTransforms parse_transform (string transforms) { 641 string[] functions; 642 string transform = transforms; 643 SvgTransforms transform_functions; 644 645 transform_functions = new SvgTransforms (); 646 647 if (transforms == "") { 648 return transform_functions; 649 } 650 651 transform = transform.replace ("\t", " "); 652 transform = transform.replace ("\n", " "); 653 transform = transform.replace ("\r", " "); 654 655 // use only a single space as separator 656 while (transform.index_of (" ") > -1) { 657 transform = transform.replace (" ", " "); 658 } 659 660 if (unlikely (transform.index_of (")") == -1)) { 661 warning (@"No parenthesis in transform function: $transform"); 662 return transform_functions; 663 } 664 665 // add separator 666 transform = transform.replace (") ", "|"); 667 transform = transform.replace (")", "|"); 668 functions = transform.split ("|"); 669 670 for (int i = 0; i < functions.length; i++) { 671 if (functions[i].has_prefix ("translate")) { 672 transform_functions.add (translate (functions[i])); 673 } 674 675 if (functions[i].has_prefix ("scale")) { 676 transform_functions.add (scale (functions[i])); 677 } 678 679 if (functions[i].has_prefix ("matrix")) { 680 transform_functions.add (matrix (functions[i])); 681 } 682 683 if (functions[i].has_prefix ("rotate")) { 684 transform_functions.add (rotate (functions[i])); 685 } 686 // TODO: rotate etc. 687 } 688 689 return transform_functions; 690 } 691 692 private static SvgTransform matrix (string function) { 693 string parameters = get_transform_parameters (function); 694 string[] p = parameters.split (" "); 695 SvgTransform transform = new SvgTransform (); 696 transform.type = TransformType.MATRIX; 697 698 if (unlikely (p.length != 6)) { 699 warning ("Expecting six parameters for matrix transformation."); 700 return transform; 701 } 702 703 for (int i = 0; i < 6; i++) { 704 double argument = parse_double (p[i]); 705 transform.arguments.add (argument); 706 } 707 708 return transform; 709 } 710 711 private static string remove_unit (string d) { 712 string s = d.replace ("pt", ""); 713 s = s.replace ("pc", ""); 714 s = s.replace ("mm", ""); 715 s = s.replace ("cm", ""); 716 s = s.replace ("in", ""); 717 s = s.replace ("px", ""); 718 return s; 719 } 720 721 public static double parse_number (string? number_with_unit) { 722 if (number_with_unit == null) { 723 return 0; 724 } 725 726 string d = (!) number_with_unit; 727 string s = remove_unit (d); 728 double n = parse_double (s); 729 730 if (d.has_suffix ("pt")) { 731 n *= 1.25; 732 } else if (d.has_suffix ("pc")) { 733 n *= 15; 734 } else if (d.has_suffix ("mm")) { 735 n *= 3.543307; 736 } else if (d.has_suffix ("cm")) { 737 n *= 35.43307; 738 } else if (d.has_suffix ("in")) { 739 n *= 90; 740 } 741 742 return n; 743 } 744 745 private static SvgTransform rotate (string function) { 746 string parameters = get_transform_parameters (function); 747 string[] p = parameters.split (" "); 748 SvgTransform transform = new SvgTransform (); 749 transform.type = TransformType.ROTATE; 750 751 if (p.length > 0) { 752 transform.arguments.add (parse_double (p[0])); 753 } 754 755 return transform; 756 } 757 758 private static SvgTransform scale (string function) { 759 string parameters = get_transform_parameters (function); 760 string[] p = parameters.split (" "); 761 SvgTransform transform = new SvgTransform (); 762 transform.type = TransformType.SCALE; 763 764 if (p.length > 0) { 765 transform.arguments.add (parse_double (p[0])); 766 } 767 768 if (p.length > 1) { 769 transform.arguments.add (parse_double (p[1])); 770 } 771 772 return transform; 773 } 774 775 private static SvgTransform translate (string function) { 776 string parameters = get_transform_parameters (function); 777 string[] p = parameters.split (" "); 778 SvgTransform transform = new SvgTransform (); 779 transform.type = TransformType.TRANSLATE; 780 781 if (p.length > 0) { 782 transform.arguments.add (parse_double (p[0])); 783 } 784 785 if (p.length > 1) { 786 transform.arguments.add (parse_double (p[1])); 787 } 788 789 return transform; 790 } 791 792 private static string get_transform_parameters (string function) { 793 int i; 794 string param = ""; 795 796 i = function.index_of ("("); 797 return_val_if_fail (i != -1, param); 798 param = function.substring (i); 799 800 param = param.replace ("(", ""); 801 param = param.replace ("\n", " "); 802 param = param.replace ("\t", " "); 803 param = param.replace (",", " "); 804 805 while (param.index_of (" ") > -1) { 806 param = param.replace (" ", " "); 807 } 808 809 return param.strip(); 810 } 811 812 private bool is_visible (XmlElement tag) { 813 bool hidden = false; 814 815 foreach (Attribute attr in tag.get_attributes ()) { 816 if (attr.get_name () == "display" && attr.get_content () == "none") { 817 hidden = true; 818 } 819 820 if (attr.get_name () == "visibility" 821 && (attr.get_content () == "hidden" 822 || attr.get_content () == "collapse")) { 823 hidden = true; 824 } 825 } 826 827 return !hidden; 828 } 829 830 private SvgTransforms get_transform (Attributes attributes) { 831 foreach (Attribute attr in attributes) { 832 if (attr.get_name () == "transform") { 833 return parse_transform (attr.get_content ()); 834 } 835 } 836 837 return new SvgTransforms (); 838 } 839 840 private void parse_path (Layer layer, SvgStyle parent_style, XmlElement tag) { 841 SvgPath path = new SvgPath (); 842 843 foreach (Attribute attr in tag.get_attributes ()) { 844 if (attr.get_name () == "d") { 845 path.points = parse_points (attr.get_content (), format); 846 } 847 } 848 849 set_object_properties (path, parent_style, tag); 850 layer.add_object (path); 851 } 852 853 public static Gee.ArrayList<Points> parse_points (string data, SvgFormat format) { 854 Gee.ArrayList<Points> path_data = new Gee.ArrayList<Points> (); 855 Points points = new Points (); 856 BezierPoints[] bezier_points; 857 int points_size; 858 859 get_bezier_points (data, out bezier_points, out points_size, true); 860 861 // all instructions are padded 862 863 double first_x = 0; 864 double first_y = 0; 865 double last_x = 0; 866 double last_y = 0; 867 868 if (points_size > 0) { 869 first_x = bezier_points[0].x0; 870 first_y = bezier_points[0].y0; 871 } 872 873 for (int i = 0; i < points_size; i++) { 874 // FIXME: add more types 875 if (bezier_points[i].type == 'M') { 876 points.add_type (POINT_LINE); 877 points.add (bezier_points[i].x0); 878 points.add (bezier_points[i].y0); 879 points.add (0); 880 points.add (0); 881 points.add (0); 882 points.add (0); 883 points.add (0); 884 last_x = bezier_points[i].x0; 885 last_y = bezier_points[i].y0; 886 } else if (bezier_points[i].type == 'C') { 887 points.add_type (POINT_CUBIC); 888 points.add (bezier_points[i].x0); 889 points.add (bezier_points[i].y0); 890 points.add (bezier_points[i].x1); 891 points.add (bezier_points[i].y1); 892 points.add (bezier_points[i].x2); 893 points.add (bezier_points[i].y2); 894 points.add (0); 895 last_x = bezier_points[i].x2; 896 last_y = bezier_points[i].y2; 897 } else if (bezier_points[i].type == 'L') { 898 points.add_type (POINT_LINE); 899 points.add (bezier_points[i].x0); 900 points.add (bezier_points[i].y0); 901 points.add (0); 902 points.add (0); 903 points.add (0); 904 points.add (0); 905 points.add (0); 906 last_x = bezier_points[i].x0; 907 last_y = bezier_points[i].y0; 908 } else if (bezier_points[i].type == 'A') { 909 BezierPoints b = bezier_points[i]; 910 points.add_type (POINT_ARC); 911 points.add (b.rx); 912 points.add (b.ry); 913 points.add (b.rotation); 914 points.add (b.large_arc ? 1 : 0); 915 points.add (b.sweep ? 1 : 0); 916 points.add (b.x1); 917 points.add (b.y1); 918 919 last_x = bezier_points[i].x1; 920 last_y = bezier_points[i].y1; 921 } else if (bezier_points[i].type == 'z') { 922 points.closed = true; 923 924 if (fabs (first_x - last_x) > 0.0001 && fabs (first_y - last_y) > 0.0001) { 925 points.add_type (POINT_LINE); 926 points.add (first_x); 927 points.add (first_y); 928 points.add (0); 929 points.add (0); 930 points.add (0); 931 points.add (0); 932 points.add (0); 933 } 934 935 path_data.add (points); 936 points = new Points (); 937 938 if (i + 1 < points_size) { 939 first_x = bezier_points[i + 1].x0; 940 first_y = bezier_points[i + 1].y0; 941 } 942 } else { 943 string type = (!) bezier_points[i].type.to_string (); 944 warning (@"SVG conversion not implemented for $type"); 945 } 946 } 947 948 if (points.size > 0) { 949 path_data.add (points); 950 } 951 952 if (format == SvgFormat.ILLUSTRATOR) { 953 Gee.ArrayList<Points> illustrator_path_data = new Gee.ArrayList<Points> (); 954 955 foreach (Points p in path_data) { 956 return_val_if_fail (p.point_data.size % 8 == 0, path_data); 957 958 if (p.point_data.size > 8) { 959 Points illustrator_points = new Points (); 960 961 if (p.point_data.get_point_type (p.point_data.size - 8) == POINT_CUBIC) { 962 illustrator_points.insert_type (0, POINT_LINE); 963 illustrator_points.insert (1, p.point_data.get_double (p.point_data.size - 3)); 964 illustrator_points.insert (2, p.point_data.get_double (p.point_data.size - 2)); 965 illustrator_points.insert (3, 0); 966 illustrator_points.insert (4, 0); 967 illustrator_points.insert (5, 0); 968 illustrator_points.insert (6, 0); 969 illustrator_points.insert (7, 0); 970 } else { 971 illustrator_points.insert_type (0, POINT_LINE); 972 illustrator_points.insert (1, p.point_data.get_double (p.point_data.size - 7)); 973 illustrator_points.insert (2, p.point_data.get_double (p.point_data.size - 6)); 974 illustrator_points.insert (3, 0); 975 illustrator_points.insert (4, 0); 976 illustrator_points.insert (5, 0); 977 illustrator_points.insert (6, 0); 978 illustrator_points.insert (7, 0); 979 } 980 981 int start = 0; 982 983 for (int i = start; i < p.point_data.size; i += 1) { 984 illustrator_points.point_data.add (p.point_data.get_double (i)); 985 } 986 987 illustrator_points.closed = p.closed; 988 illustrator_path_data.add (illustrator_points); 989 } 990 } 991 992 path_data = illustrator_path_data; 993 } 994 995 return path_data; 996 } 997 998 public static double parse_double (string? s) { 999 if (unlikely (s == null)) { 1000 warning ("number is null"); 1001 return 0; 1002 } 1003 1004 if (unlikely (!double.try_parse ((!) s))) { 1005 warning (@"Expecting a double got: $((!) s)"); 1006 return 0; 1007 } 1008 1009 return double.parse ((!) s); 1010 } 1011 1012 public static void get_bezier_points (string point_data, 1013 out BezierPoints[] bezier_points, out int points, bool svg_glyph) { 1014 1015 double px = 0; 1016 double py = 0; 1017 double px2 = 0; 1018 double py2 = 0; 1019 double cx = 0; 1020 double cy = 0; 1021 string[] c; 1022 double arc_rx, arc_ry; 1023 double arc_rotation; 1024 int large_arc; 1025 int arc_sweep; 1026 double arc_dest_x, arc_dest_y; 1027 1028 int bi = 0; 1029 1030 string data = add_separators (point_data); 1031 c = data.split (" "); 1032 1033 // the arc instruction can use up to eight points 1034 int bezier_points_length = 8 * c.length + 1; 1035 bezier_points = new BezierPoints[bezier_points_length]; 1036 1037 for (int i = 0; i < bezier_points_length; i++) { 1038 bezier_points[i] = new BezierPoints (); 1039 } 1040 1041 // parse path 1042 int i = -1; 1043 while (++i < c.length && bi < bezier_points.length) { 1044 if (c[i] == "m") { 1045 while (i + 2 < c.length && is_point (c[i + 1])) { 1046 bezier_points[bi].type = 'M'; 1047 bezier_points[bi].svg_type = 'm'; 1048 1049 px += parse_double (c[++i]); 1050 1051 if (svg_glyph) { 1052 py += parse_double (c[++i]); 1053 } else { 1054 py += -parse_double (c[++i]); 1055 } 1056 1057 bezier_points[bi].x0 = px; 1058 bezier_points[bi].y0 = py; 1059 bi++; 1060 } 1061 } else if (c[i] == "M") { 1062 while (i + 2 < c.length && is_point (c[i + 1])) { 1063 bezier_points[bi].type = 'M'; 1064 bezier_points[bi].svg_type = 'M'; 1065 1066 px = parse_double (c[++i]); 1067 1068 if (svg_glyph) { 1069 py = parse_double (c[++i]); 1070 } else { 1071 py = -parse_double (c[++i]); 1072 } 1073 1074 bezier_points[bi].x0 = px; 1075 bezier_points[bi].y0 = py; 1076 bi++; 1077 } 1078 } else if (c[i] == "h") { 1079 while (i + 1 < c.length && is_point (c[i + 1])) { 1080 bezier_points[bi].type = 'L'; 1081 bezier_points[bi].svg_type = 'h'; 1082 1083 px += parse_double (c[++i]); 1084 1085 bezier_points[bi].x0 = px; 1086 bezier_points[bi].y0 = py; 1087 bi++; 1088 } 1089 } else if (i + 1 < c.length && c[i] == "H") { 1090 while (is_point (c[i + 1])) { 1091 bezier_points[bi].type = 'L'; 1092 bezier_points[bi].svg_type = 'H'; 1093 1094 px = parse_double (c[++i]); 1095 1096 bezier_points[bi].x0 = px; 1097 bezier_points[bi].y0 = py; 1098 bi++; 1099 } 1100 } else if (c[i] == "v") { 1101 while (i + 1 < c.length && is_point (c[i + 1])) { 1102 bezier_points[bi].type = 'L'; 1103 bezier_points[bi].svg_type = 'v'; 1104 1105 if (svg_glyph) { 1106 py = py + parse_double (c[++i]); 1107 } else { 1108 py = py - parse_double (c[++i]); 1109 } 1110 1111 bezier_points[bi].x0 = px; 1112 bezier_points[bi].y0 = py; 1113 bi++; 1114 } 1115 } else if (i + 1 < c.length && c[i] == "V") { 1116 while (is_point (c[i + 1])) { 1117 bezier_points[bi].type = 'L'; 1118 bezier_points[bi].svg_type = 'V'; 1119 1120 if (svg_glyph) { 1121 py = parse_double (c[++i]); 1122 } else { 1123 py = -parse_double (c[++i]); 1124 } 1125 1126 bezier_points[bi].x0 = px; 1127 bezier_points[bi].y0 = py; 1128 bi++; 1129 } 1130 } else if (c[i] == "l") { 1131 while (i + 2 < c.length && is_point (c[i + 1])) { 1132 bezier_points[bi].type = 'L'; 1133 bezier_points[bi].svg_type = 'l'; 1134 1135 cx = px + parse_double (c[++i]); 1136 1137 if (svg_glyph) { 1138 cy = py + parse_double (c[++i]); 1139 } else { 1140 cy = py - parse_double (c[++i]); 1141 } 1142 1143 px = cx; 1144 py = cy; 1145 1146 bezier_points[bi].x0 = cx; 1147 bezier_points[bi].y0 = cy; 1148 bi++; 1149 } 1150 } else if (c[i] == "L") { 1151 while (i + 2 < c.length && is_point (c[i + 1])) { 1152 bezier_points[bi].type = 'L'; 1153 bezier_points[bi].svg_type = 'L'; 1154 1155 cx = parse_double (c[++i]); 1156 1157 if (svg_glyph) { 1158 cy = parse_double (c[++i]); 1159 } else { 1160 cy = -parse_double (c[++i]); 1161 } 1162 1163 px = cx; 1164 py = cy; 1165 1166 bezier_points[bi].x0 = cx; 1167 bezier_points[bi].y0 = cy; 1168 bi++; 1169 } 1170 } else if (c[i] == "c") { 1171 while (i + 6 < c.length && is_point (c[i + 1])) { 1172 bezier_points[bi].type = 'C'; 1173 bezier_points[bi].svg_type = 'C'; 1174 1175 cx = px + parse_double (c[++i]); 1176 1177 if (svg_glyph) { 1178 cy = py + parse_double (c[++i]); 1179 } else { 1180 cy = py - parse_double (c[++i]); 1181 } 1182 1183 bezier_points[bi].x0 = cx; 1184 bezier_points[bi].y0 = cy; 1185 1186 cx = px + parse_double (c[++i]); 1187 1188 if (svg_glyph) { 1189 cy = py + parse_double (c[++i]); 1190 } else { 1191 cy = py - parse_double (c[++i]); 1192 } 1193 1194 px2 = cx; 1195 py2 = cy; 1196 1197 bezier_points[bi].x1 = px2; 1198 bezier_points[bi].y1 = py2; 1199 1200 cx = px + parse_double (c[++i]); 1201 1202 if (svg_glyph) { 1203 cy = py + parse_double (c[++i]); 1204 } else { 1205 cy = py + -parse_double (c[++i]); 1206 } 1207 1208 bezier_points[bi].x2 = cx; 1209 bezier_points[bi].y2 = cy; 1210 1211 px = cx; 1212 py = cy; 1213 1214 bi++; 1215 } 1216 } else if (c[i] == "C") { 1217 while (i + 6 < c.length && is_point (c[i + 1])) { 1218 bezier_points[bi].type = 'C'; 1219 bezier_points[bi].svg_type = 'C'; 1220 1221 cx = parse_double (c[++i]); 1222 1223 if (svg_glyph) { 1224 cy = parse_double (c[++i]); 1225 } else { 1226 cy = -parse_double (c[++i]); 1227 } 1228 1229 bezier_points[bi].x0 = cx; 1230 bezier_points[bi].y0 = cy; 1231 1232 cx = parse_double (c[++i]); 1233 1234 if (svg_glyph) { 1235 cy = parse_double (c[++i]); 1236 } else { 1237 cy = -parse_double (c[++i]); 1238 } 1239 1240 px2 = cx; 1241 py2 = cy; 1242 1243 bezier_points[bi].x1 = cx; 1244 bezier_points[bi].y1 = cy; 1245 1246 cx = parse_double (c[++i]); 1247 1248 if (svg_glyph) { 1249 cy = parse_double (c[++i]); 1250 } else { 1251 cy = -parse_double (c[++i]); 1252 } 1253 1254 bezier_points[bi].x2 = cx; 1255 bezier_points[bi].y2 = cy; 1256 1257 px = cx; 1258 py = cy; 1259 1260 bi++; 1261 } 1262 } else if (c[i] == "q") { 1263 while (i + 4 < c.length && is_point (c[i + 1])) { 1264 bezier_points[bi].type = 'Q'; 1265 bezier_points[bi].svg_type = 'q'; 1266 1267 cx = px + parse_double (c[++i]); 1268 1269 if (svg_glyph) { 1270 cy = py + parse_double (c[++i]); 1271 } else { 1272 cy = py - parse_double (c[++i]); 1273 } 1274 1275 bezier_points[bi].x0 = cx; 1276 bezier_points[bi].y0 = cy; 1277 1278 px2 = cx; 1279 py2 = cy; 1280 1281 cx = px + parse_double (c[++i]); 1282 1283 if (svg_glyph) { 1284 cy = py + parse_double (c[++i]); 1285 } else { 1286 cy = py - parse_double (c[++i]); 1287 } 1288 1289 bezier_points[bi].x1 = cx; 1290 bezier_points[bi].y1 = cy; 1291 1292 px = cx; 1293 py = cy; 1294 1295 bi++; 1296 } 1297 } else if (c[i] == "Q") { 1298 while (i + 4 < c.length && is_point (c[i + 1])) { 1299 bezier_points[bi].type = 'Q'; 1300 bezier_points[bi].svg_type = 'Q'; 1301 1302 cx = parse_double (c[++i]); 1303 1304 if (svg_glyph) { 1305 cy = parse_double (c[++i]); 1306 } else { 1307 cy = -parse_double (c[++i]); 1308 } 1309 1310 bezier_points[bi].x0 = cx; 1311 bezier_points[bi].y0 = cy; 1312 1313 px2 = cx; 1314 py2 = cy; 1315 1316 cx = parse_double (c[++i]); 1317 1318 if (svg_glyph) { 1319 cy = parse_double (c[++i]); 1320 } else { 1321 cy = -parse_double (c[++i]); 1322 } 1323 1324 px = cx; 1325 py = cy; 1326 1327 bezier_points[bi].x1 = cx; 1328 bezier_points[bi].y1 = cy; 1329 1330 bi++; 1331 } 1332 } else if (c[i] == "t") { 1333 while (i + 2 < c.length && is_point (c[i + 1])) { 1334 bezier_points[bi].type = 'Q'; 1335 bezier_points[bi].svg_type = 't'; 1336 1337 // the first point is the reflection 1338 cx = 2 * px - px2; 1339 cy = 2 * py - py2; // if (svg_glyph) ? 1340 1341 bezier_points[bi].x0 = cx; 1342 bezier_points[bi].y0 = cy; 1343 1344 px2 = cx; 1345 py2 = cy; 1346 1347 cx = px + parse_double (c[++i]); 1348 1349 if (svg_glyph) { 1350 cy = py + parse_double (c[++i]); 1351 } else { 1352 cy = py - parse_double (c[++i]); 1353 } 1354 1355 px = cx; 1356 py = cy; 1357 1358 bezier_points[bi].x1 = px; 1359 bezier_points[bi].y1 = py; 1360 1361 bi++; 1362 } 1363 } else if (c[i] == "T") { 1364 while (i + 2 < c.length && is_point (c[i + 1])) { 1365 bezier_points[bi].type = 'Q'; 1366 bezier_points[bi].svg_type = 'T'; 1367 1368 // the reflection 1369 cx = 2 * px - px2; 1370 cy = 2 * py - py2; 1371 1372 bezier_points[bi].x0 = cx; 1373 bezier_points[bi].y0 = cy; 1374 1375 px2 = cx; 1376 py2 = cy; 1377 1378 cx = parse_double (c[++i]); 1379 1380 if (svg_glyph) { 1381 cy = parse_double (c[++i]); 1382 } else { 1383 cy = -parse_double (c[++i]); 1384 } 1385 1386 px = cx; 1387 py = cy; 1388 1389 bezier_points[bi].x1 = px; 1390 bezier_points[bi].y1 = py; 1391 1392 bi++; 1393 } 1394 } else if (c[i] == "s") { 1395 while (i + 4 < c.length && is_point (c[i + 1])) { 1396 bezier_points[bi].type = 'C'; 1397 bezier_points[bi].svg_type = 's'; 1398 1399 // the first point is the reflection 1400 cx = 2 * px - px2; 1401 cy = 2 * py - py2; // if (svg_glyph) ? 1402 1403 bezier_points[bi].x0 = cx; 1404 bezier_points[bi].y0 = cy; 1405 1406 cx = px + parse_double (c[++i]); 1407 1408 if (svg_glyph) { 1409 cy = py + parse_double (c[++i]); 1410 } else { 1411 cy = py - parse_double (c[++i]); 1412 } 1413 1414 px2 = cx; 1415 py2 = cy; 1416 1417 bezier_points[bi].x1 = px2; 1418 bezier_points[bi].y1 = py2; 1419 1420 cx = px + parse_double (c[++i]); 1421 1422 if (svg_glyph) { 1423 cy = py + parse_double (c[++i]); 1424 } else { 1425 cy = py - parse_double (c[++i]); 1426 } 1427 1428 bezier_points[bi].x2 = cx; 1429 bezier_points[bi].y2 = cy; 1430 1431 px = cx; 1432 py = cy; 1433 1434 bi++; 1435 } 1436 } else if (c[i] == "S") { 1437 while (i + 4 < c.length && is_point (c[i + 1])) { 1438 bezier_points[bi].type = 'C'; 1439 bezier_points[bi].svg_type = 'S'; 1440 1441 // the reflection 1442 cx = 2 * px - px2; 1443 cy = 2 * py - py2; // if (svg_glyph) ? 1444 1445 bezier_points[bi].x0 = cx; 1446 bezier_points[bi].y0 = cy; 1447 1448 // the other two are regular cubic points 1449 cx = parse_double (c[++i]); 1450 1451 if (svg_glyph) { 1452 cy = parse_double (c[++i]); 1453 } else { 1454 cy = -parse_double (c[++i]); 1455 } 1456 1457 px2 = cx; 1458 py2 = cy; 1459 1460 bezier_points[bi].x1 = px2; 1461 bezier_points[bi].y1 = py2; 1462 1463 cx = parse_double (c[++i]); 1464 1465 if (svg_glyph) { 1466 cy = parse_double (c[++i]); 1467 } else { 1468 cy = -parse_double (c[++i]); 1469 } 1470 1471 bezier_points[bi].x2 = cx; 1472 bezier_points[bi].y2 = cy; 1473 1474 px = cx; 1475 py = cy; 1476 1477 bi++; 1478 } 1479 } else if (c[i] == "a") { 1480 while (i + 7 < c.length && is_point (c[i + 1])) { 1481 arc_rx = parse_double (c[++i]); 1482 arc_ry = parse_double (c[++i]); 1483 1484 arc_rotation = PI * (parse_double (c[++i]) / 180.0); 1485 large_arc = parse_int (c[++i]); 1486 arc_sweep = parse_int (c[++i]); 1487 1488 cx = px + parse_double (c[++i]); 1489 1490 if (svg_glyph) { 1491 cy = py + parse_double (c[++i]); 1492 } else { 1493 cy = py - parse_double (c[++i]); 1494 } 1495 1496 arc_dest_x = cx; 1497 arc_dest_y = cy; 1498 1499 bezier_points[bi].type = 'A'; 1500 bezier_points[bi].svg_type = 'a'; 1501 bezier_points[bi].x0 = px; 1502 bezier_points[bi].y0 = py; 1503 bezier_points[bi].x1 = cx; 1504 bezier_points[bi].y1 = cy; 1505 bezier_points[bi].rx = arc_rx; 1506 bezier_points[bi].ry = arc_ry; 1507 bezier_points[bi].rotation = arc_rotation; 1508 bezier_points[bi].large_arc = large_arc == 1; 1509 bezier_points[bi].sweep = arc_sweep == 1; 1510 bi++; 1511 1512 px = cx; 1513 py = cy; 1514 } 1515 } else if (i + 7 < c.length && c[i] == "A") { 1516 while (is_point (c[i + 1])) { 1517 arc_rx = parse_double (c[++i]); 1518 arc_ry = parse_double (c[++i]); 1519 1520 arc_rotation = PI * (parse_double (c[++i]) / 180.0); 1521 large_arc = parse_int (c[++i]); 1522 arc_sweep = parse_int (c[++i]); 1523 1524 cx = parse_double (c[++i]); 1525 1526 if (svg_glyph) { 1527 cy = parse_double (c[++i]); 1528 } else { 1529 cy = -parse_double (c[++i]); 1530 } 1531 1532 arc_dest_x = cx; 1533 arc_dest_y = cy; 1534 1535 bezier_points[bi].type = 'A'; 1536 bezier_points[bi].svg_type = 'A'; 1537 bezier_points[bi].x0 = px; 1538 bezier_points[bi].y0 = py; 1539 bezier_points[bi].x1 = cx; 1540 bezier_points[bi].y1 = cy; 1541 bezier_points[bi].rx = arc_rx; 1542 bezier_points[bi].ry = arc_ry; 1543 bezier_points[bi].rotation = arc_rotation; 1544 bezier_points[bi].large_arc = large_arc == 1; 1545 bezier_points[bi].sweep = arc_sweep == 1; 1546 bi++; 1547 1548 px = cx; 1549 py = cy; 1550 } 1551 } else if (c[i] == "z") { 1552 bezier_points[bi].type = 'z'; 1553 bezier_points[bi].svg_type = 'z'; 1554 1555 bi++; 1556 } else if (c[i] == "Z") { 1557 bezier_points[bi].type = 'z'; 1558 bezier_points[bi].svg_type = 'z'; 1559 1560 bi++; 1561 } else if (c[i] == "") { 1562 } else if (c[i] == " ") { 1563 } else { 1564 warning (@"Unknown instruction: $(c[i])"); 1565 } 1566 } 1567 1568 if (bi == 0) { 1569 warning ("No points in path."); 1570 } 1571 1572 points = bi; 1573 } 1574 1575 static int parse_int (string? s) { 1576 if (unlikely (s == null)) { 1577 warning ("null instead of string"); 1578 return 0; 1579 } 1580 1581 if (unlikely (!int64.try_parse ((!) s))) { 1582 warning (@"Expecting an integer: $((!) s)"); 1583 return 0; 1584 } 1585 1586 return int.parse ((!) s); 1587 } 1588 1589 static bool is_point (string? s) { 1590 if (unlikely (s == null)) { 1591 warning ("s is null"); 1592 return false; 1593 } 1594 1595 return double.try_parse ((!) s); 1596 } 1597 1598 /** Add space as separator to svg data. 1599 * @param d svg data 1600 */ 1601 public static string add_separators (string d) { 1602 string data = d; 1603 1604 data = data.replace (",", " "); 1605 data = data.replace ("a", " a "); 1606 data = data.replace ("A", " A "); 1607 data = data.replace ("m", " m "); 1608 data = data.replace ("M", " M "); 1609 data = data.replace ("h", " h "); 1610 data = data.replace ("H", " H "); 1611 data = data.replace ("v", " v "); 1612 data = data.replace ("V", " V "); 1613 data = data.replace ("l", " l "); 1614 data = data.replace ("L", " L "); 1615 data = data.replace ("q", " q "); 1616 data = data.replace ("Q", " Q "); 1617 data = data.replace ("c", " c "); 1618 data = data.replace ("C", " C "); 1619 data = data.replace ("t", " t "); 1620 data = data.replace ("T", " T "); 1621 data = data.replace ("s", " s "); 1622 data = data.replace ("S", " S "); 1623 data = data.replace ("zM", " z M "); 1624 data = data.replace ("zm", " z m "); 1625 data = data.replace ("z", " z "); 1626 data = data.replace ("Z", " Z "); 1627 data = data.replace ("-", " -"); 1628 data = data.replace ("e -", "e-"); // minus can be either separator or a negative exponent 1629 data = data.replace ("\t", " "); 1630 data = data.replace ("\r\n", " "); 1631 data = data.replace ("\n", " "); 1632 1633 // use only a single space as separator 1634 while (data.index_of (" ") > -1) { 1635 data = data.replace (" ", " "); 1636 } 1637 1638 return data; 1639 } 1640 } 1641 1642 } 1643