The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

SvgFile.vala in libbirdfont/Svg

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git

Revisions

View the latest version of libbirdfont/Svg/SvgFile.vala.
Use SVG transform, parse opacity and gradients
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 17 namespace BirdFont { 18 19 public class SvgFile : GLib.Object { 20 21 public SvgFile () { 22 } 23 24 public Layer parse (string path) { 25 string xml_data; 26 27 try { 28 FileUtils.get_contents (path, out xml_data); 29 XmlParser xmlparser = new XmlParser (xml_data); 30 31 if (xmlparser.validate ()) { 32 Tag root = xmlparser.get_root_tag (); 33 return parse_svg_file (root); 34 } else { 35 warning ("Invalid xml file."); 36 } 37 } catch (GLib.Error error) { 38 warning (error.message); 39 } 40 41 return new Layer (); 42 } 43 44 private Layer parse_svg_file (Tag tag) { 45 Layer layer = new Layer (); 46 47 foreach (Tag t in tag) { 48 string name = t.get_name (); 49 50 if (name == "g") { 51 parse_layer (layer, t); 52 } 53 54 parse_object (layer, t); 55 } 56 57 return layer; 58 } 59 60 private void parse_layer (Layer layer, Tag tag) { 61 bool hidden = false; 62 63 foreach (Attribute attr in tag.get_attributes ()) { 64 if (attr.get_name () == "display" && attr.get_content () == "none") { 65 hidden = true; 66 } 67 68 if (attr.get_name () == "visibility" 69 && (attr.get_content () == "hidden" 70 || attr.get_content () == "collapse")) { 71 hidden = true; 72 } 73 } 74 75 if (hidden) { 76 layer.visible = !hidden; 77 } 78 79 foreach (Tag t in tag) { 80 if (t.get_name () == "g") { 81 Layer sublayer = new Layer (); 82 parse_layer (layer, t); 83 layer.subgroups.add (sublayer); 84 } 85 86 parse_object (layer, t); 87 } 88 89 foreach (Attribute attr in tag.get_attributes ()) { 90 if (attr.get_name () == "transform") { 91 layer.transforms = parse_transform (attr.get_content ()); 92 } 93 } 94 } 95 96 void parse_object (Layer layer, Tag tag) { 97 string name = tag.get_name (); 98 99 if (name == "path") { 100 parse_path (layer, tag); 101 } 102 103 if (name == "polygon") { 104 parse_polygon (layer, tag); 105 } 106 107 if (name == "polyline") { 108 parse_polyline (layer, tag); 109 } 110 111 if (name == "rect") { 112 parse_rect (layer, tag); 113 } 114 115 if (name == "circle") { 116 parse_circle (layer, tag); 117 } 118 119 if (name == "ellipse") { 120 parse_ellipse (layer, tag); 121 } 122 123 if (name == "line") { 124 parse_line (layer, tag); 125 } 126 } 127 128 private void parse_polygon (Layer layer, Tag tag) { 129 } 130 131 private void parse_polyline (Layer layer, Tag tag) { 132 } 133 134 private void parse_rect (Layer layer, Tag tag) { 135 Rectangle rectangle = new Rectangle (); 136 137 foreach (Attribute attr in tag.get_attributes ()) { 138 string attribute = attr.get_name (); 139 140 if (attribute == "x") { 141 rectangle.x = parse_number (attr.get_content ()); 142 } 143 144 if (attribute == "y") { 145 rectangle.y = parse_number (attr.get_content ()); 146 } 147 148 if (attribute == "width") { 149 rectangle.width = parse_number (attr.get_content ()); 150 } 151 152 if (attribute == "height") { 153 rectangle.height = parse_number (attr.get_content ()); 154 } 155 156 if (attribute == "rx") { 157 rectangle.rx = parse_number (attr.get_content ()); 158 } 159 160 if (attribute == "ry") { 161 rectangle.ry = parse_number (attr.get_content ()); 162 } 163 } 164 165 rectangle.transforms = get_transform (tag.get_attributes ()); 166 rectangle.style = SvgStyle.parse (tag.get_attributes ()); 167 rectangle.visible = is_visible (tag); 168 169 layer.add_object (rectangle); 170 } 171 172 private void parse_circle (Layer layer, Tag tag) { 173 } 174 175 private void parse_ellipse (Layer layer, Tag tag) { 176 } 177 178 private void parse_line (Layer layer, Tag tag) { 179 } 180 181 // FIXME: reverse order? 182 public Gee.ArrayList<SvgTransform> parse_transform (string transforms) { 183 string[] functions; 184 string transform = transforms; 185 Gee.ArrayList<SvgTransform> transform_functions; 186 187 transform_functions = new Gee.ArrayList<SvgTransform> (); 188 189 transform = transform.replace ("\t", " "); 190 transform = transform.replace ("\n", " "); 191 transform = transform.replace ("\r", " "); 192 193 // use only a single space as separator 194 while (transform.index_of (" ") > -1) { 195 transform = transform.replace (" ", " "); 196 } 197 198 if (unlikely (transform.index_of (")") == -1)) { 199 warning ("No parenthesis in transform function."); 200 return transform_functions; 201 } 202 203 // add separator 204 transform = transform.replace (") ", "|"); 205 transform = transform.replace (")", "|"); 206 functions = transform.split ("|"); 207 208 for (int i = 0; i < functions.length; i++) { 209 if (functions[i].has_prefix ("translate")) { 210 transform_functions.add (translate (functions[i])); 211 } 212 213 if (functions[i].has_prefix ("scale")) { 214 transform_functions.add (scale (functions[i])); 215 } 216 217 if (functions[i].has_prefix ("matrix")) { 218 transform_functions.add (matrix (functions[i])); 219 } 220 221 // TODO: rotate etc. 222 } 223 224 return transform_functions; 225 } 226 227 private SvgTransform matrix (string function) { 228 string parameters = get_transform_parameters (function); 229 string[] p = parameters.split (" "); 230 SvgTransform transform = new SvgTransform (); 231 transform.type = TransformType.MATRIX; 232 233 if (unlikely (p.length != 6)) { 234 warning ("Expecting six parameters for matrix transformation."); 235 return transform; 236 } 237 238 for (int i = 0; i < 6; i++) { 239 double argument = SvgParser.parse_double (p[i]); 240 transform.arguments.add (argument); 241 } 242 243 return transform; 244 } 245 246 private string remove_unit (string d) { 247 string s = d.replace ("pt", ""); 248 s = s.replace ("pc", ""); 249 s = s.replace ("mm", ""); 250 s = s.replace ("cm", ""); 251 s = s.replace ("in", ""); 252 return s; 253 } 254 255 private double parse_number (string d) { 256 string s = remove_unit (d); 257 double n = SvgParser.parse_double (s); 258 259 if (d.has_suffix ("pt")) { 260 n *= 1.25; 261 } else if (d.has_suffix ("pc")) { 262 n *= 15; 263 } else if (d.has_suffix ("mm")) { 264 n *= 3.543307; 265 } else if (d.has_suffix ("cm")) { 266 n *= 35.43307; 267 } else if (d.has_suffix ("in")) { 268 n *= 90; 269 } 270 271 return n; 272 } 273 274 private SvgTransform scale (string function) { 275 string parameters = get_transform_parameters (function); 276 string[] p = parameters.split (" "); 277 SvgTransform transform = new SvgTransform (); 278 transform.type = TransformType.SCALE; 279 280 if (p.length > 0) { 281 transform.arguments.add (SvgParser.parse_double (p[0])); 282 } 283 284 if (p.length > 1) { 285 transform.arguments.add (SvgParser.parse_double (p[1])); 286 } 287 288 return transform; 289 } 290 291 private SvgTransform translate (string function) { 292 string parameters = get_transform_parameters (function); 293 string[] p = parameters.split (" "); 294 SvgTransform transform = new SvgTransform (); 295 transform.type = TransformType.TRANSLATE; 296 297 if (p.length > 0) { 298 transform.arguments.add (SvgParser.parse_double (p[0])); 299 } 300 301 if (p.length > 1) { 302 transform.arguments.add (SvgParser.parse_double (p[1])); 303 } 304 305 return transform; 306 } 307 308 private string get_transform_parameters (string function) { 309 int i; 310 string param = ""; 311 312 i = function.index_of ("("); 313 return_val_if_fail (i != -1, param); 314 param = function.substring (i); 315 316 param = param.replace ("(", ""); 317 param = param.replace ("\n", " "); 318 param = param.replace ("\t", " "); 319 param = param.replace (",", " "); 320 321 while (param.index_of (" ") > -1) { 322 param.replace (" ", " "); 323 } 324 325 return param.strip(); 326 } 327 328 private bool is_visible (Tag tag) { 329 bool hidden = false; 330 331 foreach (Attribute attr in tag.get_attributes ()) { 332 if (attr.get_name () == "display" && attr.get_content () == "none") { 333 hidden = true; 334 } 335 336 if (attr.get_name () == "visibility" 337 && (attr.get_content () == "hidden" 338 || attr.get_content () == "collapse")) { 339 hidden = true; 340 } 341 } 342 343 return !hidden; 344 } 345 346 private Gee.ArrayList<SvgTransform> get_transform (Attributes attributes) { 347 foreach (Attribute attr in attributes) { 348 if (attr.get_name () == "transform") { 349 return parse_transform (attr.get_content ()); 350 } 351 } 352 353 return new Gee.ArrayList<SvgTransform> (); 354 } 355 356 private void parse_path (Layer layer, Tag tag) { 357 SvgPath path = new SvgPath (); 358 359 foreach (Attribute attr in tag.get_attributes ()) { 360 if (attr.get_name () == "d") { 361 path.points = parse_points (attr.get_content ()); 362 } 363 } 364 365 path.transforms = get_transform (tag.get_attributes ()); 366 path.style = SvgStyle.parse (tag.get_attributes ()); 367 path.visible = is_visible (tag); 368 369 layer.add_object (path); 370 } 371 372 public Gee.ArrayList<Points> parse_points (string data) { 373 Gee.ArrayList<Points> path_data = new Gee.ArrayList<Points> (); 374 Points points = new Points (); 375 BezierPoints[] bezier_points; 376 int points_size; 377 378 SvgParser.get_bezier_points (data, out bezier_points, out points_size, true); 379 380 for (int i = 0; i < points_size; i++) { 381 // FIXME: add more types 382 if (bezier_points[i].type == 'M') { 383 points.x = bezier_points[i].x0; 384 points.y = bezier_points[i].y0; 385 } else if (bezier_points[i].type == 'C') { 386 points.add (bezier_points[i].x0); 387 points.add (bezier_points[i].y0); 388 points.add (bezier_points[i].x1); 389 points.add (bezier_points[i].y1); 390 points.add (bezier_points[i].x2); 391 points.add (bezier_points[i].y2); 392 } else if (bezier_points[i].type == 'L') { 393 points.add (bezier_points[i].x0); 394 points.add (bezier_points[i].y0); 395 points.add (bezier_points[i].x0); 396 points.add (bezier_points[i].y0); 397 points.add (bezier_points[i].x0); 398 points.add (bezier_points[i].y0); 399 } else if (bezier_points[i].type == 'z') { 400 path_data.add (points); 401 points = new Points (); 402 } else { 403 string type = (!) bezier_points[i].type.to_string (); 404 warning (@"SVG conversion not implemented for $type"); 405 } 406 } 407 408 return path_data; 409 } 410 } 411 412 } 413