The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

SvgStyle.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/SvgStyle.vala.
CSS margin
1 /* 2 Copyright (C) 2015 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 Math; 17 18 namespace SvgBird { 19 20 public class SvgStyle : GLib.Object { 21 public Gee.HashMap<string, string> style = new Gee.HashMap<string, string> (); 22 23 public Color? stroke = null; 24 public Color? fill = null; 25 public Gradient? stroke_gradient = null; 26 public Gradient? fill_gradient = null; 27 28 public double stroke_width = 0; 29 30 private static Gee.HashMap<string, string>? inheritance; 31 private static Mutex inheritance_mutex = new Mutex (); 32 33 public SvgStyle () { 34 } 35 36 public SvgStyle.for_properties (Defs? defs, string style) { 37 parse_key_value_pairs (style); 38 set_style_properties (defs, this); 39 } 40 41 public void set_style (string property, string style) { 42 this.style.set (property, style); 43 } 44 45 public bool property_equals (string property, string value) { 46 string? p = get_css_property (property); 47 48 if (p == null) { 49 return false; 50 } 51 52 string css_property = (!) p; 53 return css_property == value; 54 } 55 56 public bool has_css_property (string property) { 57 return style.has_key (property); 58 } 59 60 public string? get_css_property (string property) { 61 string p = property.down (); 62 63 if (!has_css_property (p)) { 64 return null; 65 } 66 67 return style.get (p); 68 } 69 70 public static bool is_inherited (string property) { 71 inheritance_mutex.lock (); 72 if (unlikely (inheritance == null)) { 73 create_inheritance_table (); 74 } 75 76 Gee.HashMap<string, string> inheritance = (!) inheritance; 77 string? inherited = inheritance.get (property); 78 inheritance_mutex.unlock (); 79 80 if (inherited == null) { 81 return false; 82 } 83 84 string inherited_property = (!) inherited; 85 return inherited_property == "yes"; 86 } 87 88 /** Specify inheritance for a CSS property. */ 89 public static void set_inheritance (string property, bool inherit) { 90 inheritance_mutex.lock (); 91 if (unlikely (inheritance == null)) { 92 create_inheritance_table (); 93 } 94 95 Gee.HashMap<string, string> inheritance = (!) inheritance; 96 string inherit_property = inherit ? "yes" : "no"; 97 inheritance.set (property, inherit_property); 98 inheritance_mutex.unlock (); 99 } 100 101 public SvgStyle copy () { 102 SvgStyle style_copy = new SvgStyle (); 103 104 foreach (string key in style.keys) { 105 style_copy.style.set (key, style.get (key)); 106 } 107 108 if (stroke != null) { 109 style_copy.stroke = ((!) stroke).copy (); 110 } 111 112 if (fill != null) { 113 style_copy.fill = ((!) fill).copy (); 114 } 115 116 if (stroke_gradient != null) { 117 style_copy.stroke_gradient = ((!) stroke_gradient).copy (); 118 } 119 120 if (fill_gradient != null) { 121 style_copy.fill_gradient = ((!) fill_gradient).copy (); 122 } 123 124 style_copy.stroke_width = stroke_width; 125 126 return style_copy; 127 } 128 129 public LineCap get_line_cap () { 130 string l; 131 132 if (!style.has_key ("stroke-linecap")) { 133 return LineCap.BUTT; 134 } 135 136 l = style.get ("stroke-linecap"); 137 138 if (l == "round") { 139 return LineCap.ROUND; 140 } else if (l == "square") { 141 return LineCap.SQUARE; 142 } 143 144 return LineCap.BUTT; 145 } 146 147 public bool has_stroke () { 148 bool s = true; 149 150 if (style.has_key ("stroke")) { 151 s = style.get ("stroke") != "none"; 152 } 153 154 return stroke_width > 0 && s; 155 } 156 157 public double get_stroke_width () { 158 return stroke_width; 159 } 160 161 public string to_string () { 162 StringBuilder description = new StringBuilder (); 163 164 description.append ("SvgStyle: "); 165 166 foreach (string key in style.keys) { 167 description.append (key); 168 description.append (": "); 169 description.append (style.get (key)); 170 description.append ("; "); 171 } 172 173 return description.str.strip (); 174 } 175 176 public void inherit (SvgStyle inherited) { 177 foreach (string key in inherited.style.keys) { 178 if (is_inherited (key)) { 179 style.set (key, inherited.style.get (key)); 180 } 181 } 182 } 183 184 public void apply (SvgStyle inherited) { 185 foreach (string key in inherited.style.keys) { 186 style.set (key, inherited.style.get (key)); 187 } 188 } 189 190 public static SvgStyle parse (Defs? d, SvgStyle inherited, XmlElement tag) { 191 SvgStyle s = new SvgStyle (); 192 Attributes attributes = tag.get_attributes (); 193 194 s.style.set ("fill", "#000000"); // default fill value 195 s.inherit (inherited); 196 197 if (d != null) { 198 StyleSheet style_sheet = ((!) d).style_sheet; 199 style_sheet.apply_style (tag, s); 200 } 201 202 foreach (Attribute a in attributes) { 203 if (a.get_name () == "style") { 204 s.parse_key_value_pairs (a.get_content ()); 205 } 206 207 if (a.get_name () == "stroke-width") { 208 s.style.set ("stroke-width", a.get_content ()); 209 } 210 211 if (a.get_name () == "stroke") { 212 s.style.set ("stroke", a.get_content ()); 213 } 214 215 if (a.get_name () == "fill") { 216 s.style.set ("fill", a.get_content ()); 217 } 218 219 if (a.get_name () == "fill-opacity") { 220 s.style.set ("fill-opacity", a.get_content ()); 221 } 222 223 if (a.get_name () == "stroke-opacity") { 224 s.style.set ("stroke-opacity", a.get_content ()); 225 } 226 } 227 228 set_style_properties (d, s); 229 return s; 230 } 231 232 static void set_style_properties (Defs? d, SvgStyle s) { 233 double fill_opacity = 1; 234 double stroke_opacity = 1; 235 236 s.stroke_width = SvgFile.parse_number (s.style.get ("stroke-width")); 237 s.stroke = Color.parse (s.style.get ("stroke")); 238 s.fill = Color.parse (s.style.get ("fill")); 239 240 string? opacity = s.style.get ("fill-opacity"); 241 if (opacity != null) { 242 fill_opacity = SvgFile.parse_number ((!) opacity); 243 } 244 245 opacity = s.style.get ("stroke-opacity"); 246 if (opacity != null) { 247 stroke_opacity = SvgFile.parse_number ((!) opacity); 248 } 249 250 if (d != null) { 251 Defs defs = (!) d; 252 253 s.stroke_gradient = defs.get_gradient_for_url (s.style.get ("stroke")); 254 s.fill_gradient = defs.get_gradient_for_url (s.style.get ("fill")); 255 } 256 257 if (s.fill != null) { 258 Color color = (!) s.fill; 259 color.a = fill_opacity; 260 } 261 262 if (s.stroke != null) { 263 Color color = (!) s.stroke; 264 color.a = stroke_opacity; 265 } 266 } 267 268 void parse_key_value_pairs (string svg_style) { 269 string[] p = svg_style.strip ().split (";"); 270 string[] pair; 271 string k, v; 272 273 foreach (string kv in p) { 274 if (kv.index_of (":") != -1) { 275 pair = kv.split (":"); 276 277 if (pair.length != 2) { 278 warning ("pair.length != 2 in " + svg_style); 279 continue; 280 } 281 282 k = pair[0].strip ().down (); 283 v = pair[1].strip ().down (); 284 285 if (k == "padding") { 286 parse_padding_shorthand (v); 287 } else if (k == "margin") { 288 parse_margin_shorthand (v); 289 } else { 290 style.set (k, v); 291 } 292 } 293 } 294 } 295 296 void parse_margin_shorthand (string arguments) { 297 string[] args = StyleSheet.replace_whitespace (arguments).split (" "); 298 299 if (args.length > 0) { 300 style.set ("margin-top", args[0]); 301 } 302 303 if (args.length > 1) { 304 style.set ("margin-right", args[1]); 305 } 306 307 if (args.length > 2) { 308 style.set ("margin-bottom", args[2]); 309 } 310 311 if (args.length > 3) { 312 style.set ("margin-left", args[3]); 313 } 314 } 315 316 void parse_padding_shorthand (string arguments) { 317 string[] args = StyleSheet.replace_whitespace (arguments).split (" "); 318 319 if (args.length > 0) { 320 style.set ("padding-top", args[0]); 321 } 322 323 if (args.length > 1) { 324 style.set ("padding-right", args[1]); 325 } 326 327 if (args.length > 2) { 328 style.set ("padding-bottom", args[2]); 329 } 330 331 if (args.length > 3) { 332 style.set ("padding-left", args[3]); 333 } 334 } 335 336 private static void create_inheritance_table () { 337 inheritance = new Gee.HashMap<string, string> (); 338 339 Gee.HashMap<string, string> inherited = (!) inheritance; 340 341 inherited.set ("azimuth", "yes"); 342 inherited.set ("background-attachment", "no"); 343 inherited.set ("background-color", "no"); 344 inherited.set ("background-image", "no"); 345 inherited.set ("background-position", "no"); 346 inherited.set ("background-repeat", "no"); 347 inherited.set ("background", "no"); 348 inherited.set ("border-collapse", "yes"); 349 inherited.set ("border-color", "no"); 350 inherited.set ("border-spacing", "yes"); 351 inherited.set ("border-style", "no"); 352 inherited.set ("border-top", "no"); 353 inherited.set ("border-right", "no"); 354 inherited.set ("border-bottom", "no"); 355 inherited.set ("border-left", "no"); 356 inherited.set ("border-top-color", "no"); 357 inherited.set ("border-right-color", "no"); 358 inherited.set ("border-bottom-color", "no"); 359 inherited.set ("border-left-color", "no"); 360 inherited.set ("border-top-style", "no"); 361 inherited.set ("border-right-style", "no"); 362 inherited.set ("border-bottom-style", "no"); 363 inherited.set ("border-left-style", "no"); 364 inherited.set ("border-top-width", "no"); 365 inherited.set ("border-right-width", "no"); 366 inherited.set ("border-bottom-width", "no"); 367 inherited.set ("border-left-width", "no"); 368 inherited.set ("border-width", "no"); 369 inherited.set ("border", "no"); 370 inherited.set ("bottom", "no"); 371 inherited.set ("caption-side", "yes"); 372 inherited.set ("clear", "no"); 373 inherited.set ("clip", "no"); 374 inherited.set ("color", "yes"); 375 inherited.set ("content", "no"); 376 inherited.set ("counter-increment", "no"); 377 inherited.set ("counter-reset", "no"); 378 inherited.set ("cue-after", "no"); 379 inherited.set ("cue-before", "no"); 380 inherited.set ("cue", "no"); 381 inherited.set ("cursor", "yes"); 382 inherited.set ("direction", "yes"); 383 inherited.set ("display", "no"); 384 inherited.set ("elevation", "yes"); 385 inherited.set ("empty-cells", "yes"); 386 inherited.set ("float", "no"); 387 inherited.set ("font-family", "yes"); 388 inherited.set ("font-size", "yes"); 389 inherited.set ("font-style", "yes"); 390 inherited.set ("font-variant", "yes"); 391 inherited.set ("font-weight", "yes"); 392 inherited.set ("font", "yes"); 393 inherited.set ("height", "no"); 394 inherited.set ("left", "no"); 395 inherited.set ("letter-spacing", "yes"); 396 inherited.set ("line-height", "yes"); 397 inherited.set ("list-style-image", "yes"); 398 inherited.set ("list-style-position", "yes"); 399 inherited.set ("list-style-type", "yes"); 400 inherited.set ("list-style", "yes"); 401 inherited.set ("margin-right", "no"); 402 inherited.set ("margin-left", "no"); 403 inherited.set ("margin-top", "no"); 404 inherited.set ("margin-bottom", "no"); 405 inherited.set ("margin", "no"); 406 inherited.set ("max-height", "no"); 407 inherited.set ("max-width", "no"); 408 inherited.set ("min-height", "no"); 409 inherited.set ("min-width", "no"); 410 inherited.set ("orphans", "yes"); 411 inherited.set ("outline-color", "no"); 412 inherited.set ("outline-style", "no"); 413 inherited.set ("outline-width", "no"); 414 inherited.set ("outline", "no"); 415 inherited.set ("overflow", "no"); 416 inherited.set ("padding-top", "no"); 417 inherited.set ("padding-right", "no"); 418 inherited.set ("padding-bottom", "no"); 419 inherited.set ("padding-left", "no"); 420 inherited.set ("padding", "no"); 421 inherited.set ("page-break-after", "no"); 422 inherited.set ("page-break-before", "no"); 423 inherited.set ("page-break-inside", "no"); 424 inherited.set ("pause-after", "no"); 425 inherited.set ("pause-before", "no"); 426 inherited.set ("pause", "no"); 427 inherited.set ("pitch-range", "yes"); 428 inherited.set ("pitch", "yes"); 429 inherited.set ("play-during", "no"); 430 inherited.set ("position", "no"); 431 inherited.set ("quotes", "yes"); 432 inherited.set ("richness", "yes"); 433 inherited.set ("right", "no"); 434 inherited.set ("speak-header", "yes"); 435 inherited.set ("speak-numeral", "yes"); 436 inherited.set ("speak-punctuation", "yes"); 437 inherited.set ("speak", "yes"); 438 inherited.set ("speech-rate", "yes"); 439 inherited.set ("stress", "yes"); 440 inherited.set ("table-layout", "no"); 441 inherited.set ("text-align", "yes"); 442 inherited.set ("text-decoration", "no"); 443 inherited.set ("text-indent", "yes"); 444 inherited.set ("text-transform", "yes"); 445 inherited.set ("top", "no"); 446 inherited.set ("unicode-bidi", "no"); 447 inherited.set ("vertical-align", "no"); 448 inherited.set ("visibility", "yes"); 449 inherited.set ("voice-family", "yes"); 450 inherited.set ("volume", "yes"); 451 inherited.set ("white-space", "yes"); 452 inherited.set ("widows", "yes"); 453 inherited.set ("width", "no"); 454 inherited.set ("word-spacing", "yes"); 455 inherited.set ("z-index", "no"); 456 } 457 } 458 459 } 460