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