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