Commit 0496f4d
Style sheets for SVG files
Changed files (11)
modifiedconfiguremodifiedlibbirdfont/Glyph.valamodifiedlibbirdfont/OpenFontFormat/SvgTable.valamodifiedlibbirdfont/Path.valaaddedlibsvgbird/AttributePattern.valamodifiedlibsvgbird/Defs.valaaddedlibsvgbird/Selector.valaaddedlibsvgbird/SelectorTag.valaaddedlibsvgbird/StyleSheet.valamodifiedlibsvgbird/SvgFile.valamodifiedlibsvgbird/SvgStyle.vala
Diff
@@ -114,7 +114,7 @@ def configure(gtk, libbgee): 'fontconfig', ] - test_library_version ('xmlbird', True, '1.1.0')+ test_library_version ('xmlbird', True, '1.2.0') for lib in libs: test_library_version (lib)@@ -278,7 +278,6 @@ public class Glyph : FontDisplay { // FIXME: delete group public void add_active_object (Layer? group, SvgBird.Object? o) { SvgBird.Object object;- Layer g; if (o != null) { object = (!) o;@@ -34,7 +34,6 @@ public class SvgTable : OtfTable { Font font = OpenFontFormatWriter.get_current_font (); GlyphCollection? glyph_collection; GlyphCollection glyphs;- string? svg_data; int gid; Gee.ArrayList<EmbeddedSvg> embedded_svg; @@ -967,6 +967,7 @@ public class Path : GLib.Object { points.add (p); p.prev = previous_point; p.next = previous_point.next;+ previous_point.next = p; } last_point = p;new file mode 100644@@ -0,0 +1,112 @@+/*+ Copyright (C) 2016 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details.+*/++using B;+using Math;++namespace SvgBird {++public enum AttributePatternType {+ NONE,+ ANYTHING,+ LIST,+ EQUALS,+ STARTS_WITH+}++public class AttributePattern : GLib.Object {+ public string name = "";+ public string? content = null;+ public AttributePatternType type = AttributePatternType.NONE;+ + public bool match (Attributes attributes) {+ switch (type) {+ case AttributePatternType.ANYTHING:+ return match_attribute_name (attributes);+ case AttributePatternType.LIST:+ return match_list (attributes);+ case AttributePatternType.EQUALS:+ return attribute_equals (attributes);+ case AttributePatternType.STARTS_WITH:+ return attribute_start_with (attributes);+ }+ + return false;+ }++ string remove_hypen (string content) {+ int hyphen = content.index_of ("-");+ + if (hyphen == -1) {+ return content;+ }+ + return content.substring (0, hyphen);+ }++ bool attribute_start_with (Attributes attributes) {+ foreach (Attribute attribute in attributes) {+ if (attribute.get_name () == name + && remove_hypen (attribute.get_content ()) == content) {+ return true;+ }+ }+ + return false;+ }++ bool attribute_equals (Attributes attributes) {+ foreach (Attribute attribute in attributes) {+ if (attribute.get_name () == name + && attribute.get_content () == content) {+ return true;+ }+ }+ + return false;+ }+ + bool match_attribute_name (Attributes attributes) {+ foreach (Attribute attribute in attributes) {+ if (attribute.get_name () == name) {+ return true;+ }+ }+ + return false;+ }+ + bool match_list (Attributes attributes) {+ if (content == null) {+ return false;+ }+ + string[] list = ((!) content).split (" ");+ foreach (Attribute attribute in attributes) {+ if (attribute.get_name () == name) {+ + string attribute_content = attribute.get_content ();+ foreach (string list_item in list) {+ if (attribute_content == list_item) {+ return true;+ }+ }+ }+ }+ + return false;+ }+}++}@@ -32,7 +32,6 @@ public class Defs { string tag_id = (!) url; if (unlikely (!is_url (tag_id))) {- warning ("Not an URL: " + tag_id); return null; } new file mode 100644@@ -0,0 +1,41 @@+/*+ Copyright (C) 2016 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details.+*/++using B;+using Math;++namespace SvgBird {++public class Selector : GLib.Object {+ + Gee.ArrayList<SelectorPattern> patterns;+ public SvgStyle style { get; set; }++ public Selector (string pattern, SvgStyle style) {+ this.patterns = new Gee.ArrayList<SelectorPattern> ();+ string[] selector_patterns = pattern.split (",");+ + for (int i = 0; i < selector_patterns.length; i++) {+ patterns.add (new SelectorPattern (selector_patterns[i]));+ }++ this.style = style;+ }+ + public bool match (string tag, string? id, string? css_class) {+ return false;+ }+}++}new file mode 100644@@ -0,0 +1,138 @@+/*+ Copyright (C) 2016 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details.+*/++using B;+using Math;++namespace SvgBird {++/** A part of a CSS selector pattern. */+public class SelectorTag : GLib.Object {+ public string name;+ string? id = null;+ string? css_class = null;+ Gee.ArrayList<AttributePattern>? attribute_patterns = null;++ public SelectorTag (string pattern) {+ string tag_pattern = pattern.strip ();+ int id_separator = tag_pattern.index_of ("#");+ int class_separator = tag_pattern.index_of (".");+ int attribute_separator = tag_pattern.index_of ("[");+ + if (id_separator != -1) {+ name = tag_pattern.substring (0, id_separator);+ id = tag_pattern.substring (id_separator + "#".length);+ } else if (class_separator != -1) {+ name = tag_pattern.substring (0, class_separator);+ css_class = tag_pattern.substring (class_separator + ".".length);+ } else if (attribute_separator != -1) {+ parse_attributes (tag_pattern.substring (attribute_separator));+ } else {+ name = tag_pattern;+ }+ }++ void parse_attributes (string attributes) {+ int index = 0;+ Gee.ArrayList<AttributePattern> patterns = new Gee.ArrayList<AttributePattern> ();+ + while (index != -1) {+ int start = attributes.index_of ("[", index);+ + if (start == -1) {+ return;+ }+ + int stop = attributes.index_of ("]", start);+ + if (stop == -1) {+ return;+ }+ + AttributePattern pattern;+ pattern = parse_attribute (attributes.substring (start, stop));+ patterns.add (pattern);+ + index = stop + "]".length;+ }+ + attribute_patterns = patterns;+ }+ + AttributePattern parse_attribute (string attribute) {+ int starts_with = attribute.index_of ("|=");+ int in_list = attribute.index_of ("~=");+ int equals = attribute.index_of ("=");+ AttributePattern pattern = new AttributePattern ();++ if (starts_with != -1) {+ pattern.name = attribute.substring (0, starts_with);+ pattern.type = AttributePatternType.STARTS_WITH;+ pattern.content = attribute.substring (0, equals + "~=".length);+ } else if (in_list != -1) {+ pattern.name = attribute.substring (0, in_list);+ pattern.type = AttributePatternType.LIST;+ pattern.content = attribute.substring (0, equals + "~=".length);+ } else if (equals != -1) {+ pattern.name = attribute.substring (0, equals);+ pattern.type = AttributePatternType.EQUALS;+ pattern.content = attribute.substring (0, equals + "=".length);+ } else {+ pattern.name = attribute;+ pattern.type = AttributePatternType.ANYTHING;+ }+ + return pattern;+ }++ public bool match (XmlElement tag, string? id, string? css_class) {+ string tag_name = tag.get_name ();+ + if (this.name != "*" && this.name != "" && this.name != tag_name) {+ return false;+ }+ + if (this.id != null) {+ if (id == null) {+ return false;+ } + + if (this.id != id) {+ return false;+ }+ }+ + if (this.css_class != null) {+ if (css_class == null) {+ return false;+ } + + if (this.css_class != css_class) {+ return false;+ }+ }+ + if (attribute_patterns != null) {+ foreach (AttributePattern pattern in (!) attribute_patterns) {+ if (!pattern.match (tag.get_attributes ())) {+ return false;+ }+ }+ }+ + return true;+ }+}++}new file mode 100644@@ -0,0 +1,112 @@+/*+ Copyright (C) 2016 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details.+*/++using B;+using Math;++namespace SvgBird {++public class StyleSheet : GLib.Object {+ + public Gee.ArrayList<Selector> styles;+ + public StyleSheet () {+ styles = new Gee.ArrayList<Selector> ();+ }+ + public static StyleSheet parse (Defs defs, Tag style_tag) {+ StyleSheet style_sheet = new StyleSheet ();+ string css = style_tag.get_content ();+ css = get_cdata (css);+ css = add_separators (css);+ css = replace_whitespace (css);+ + int index = 0;+ int start_bracket_length = "{".length;+ int end_bracket_length = "}".length;+ int css_length = css.length;+ + while (index < css_length) {+ int style_rules_start = css.index_of ("{", index);+ int style_rules_end = css.index_of ("}", style_rules_start);+ + if (style_rules_start == -1 || style_rules_end == -1) {+ break;+ }+ + int selector_end = style_rules_start - start_bracket_length;+ string selectors = css.substring (index, selector_end - index);+ + int style_start = style_rules_start + start_bracket_length;+ int style_end = style_rules_end;+ string style_rules = css.substring (style_start, style_end - style_start);+ + index = style_rules_end + end_bracket_length;+ SvgStyle style = new SvgStyle.for_properties (defs, style_rules);+ + Selector selector = new Selector (selectors, style);+ style_sheet.styles.add (selector);+ }+ + return style_sheet;+ }+ + public static string get_cdata (string tag_content) {+ StringBuilder data = new StringBuilder ();+ + int index = 0;+ int cdata_tag_length = "<![CDATA[".length;+ int cdata_end_tag_length = "]]>".length;+ int content_length = tag_content.length;+ + while (index < content_length) {+ int cdata_start = tag_content.index_of ("<![CDATA[", index);+ int cdata_end = tag_content.index_of ("]]>", cdata_start);+ + if (cdata_start == -1 || cdata_end == -1) {+ break;+ }+ + cdata_start += cdata_tag_length;+ data.append (tag_content.substring (cdata_start, cdata_end - cdata_start));+ index = cdata_end + cdata_end_tag_length;+ }+ + if (index < tag_content.length) {+ data.append (tag_content.substring (index));+ }+ + return data.str;+ }++ public static string add_separators (string data) {+ string style_data = data.replace (">", " > ");+ style_data = data.replace ("+", " + ");+ return style_data;+ }+ + public static string replace_whitespace (string data) {+ string style_data = data.replace ("\n", " ");+ style_data = style_data.replace ("\r", " ");+ style_data = style_data.replace ("\t", " ");+ + while (style_data.index_of (" ") != -1) {+ style_data = style_data.replace (" ", " ");+ }+ + return style_data;+ }+}++}@@ -24,7 +24,9 @@ public class SvgFile : GLib.Object { public SvgFile () { } - public SvgDrawing parse_svg_file (Tag tag) {+ public SvgDrawing parse_svg_file (Tag svg_tag) {+ XmlTree tree = new XmlTree.for_tag (svg_tag);+ XmlElement tag = tree.get_root (); drawing = new SvgDrawing (); SvgStyle style = new SvgStyle ();@@ -40,7 +42,7 @@ public class SvgFile : GLib.Object { } } - foreach (Tag t in tag) {+ foreach (XmlElement t in tag) { string name = t.get_name (); if (name == "g") {@@ -61,7 +63,7 @@ public class SvgFile : GLib.Object { return drawing; } - private void parse_layer (Layer layer, SvgStyle parent_style, Tag tag) {+ private void parse_layer (Layer layer, SvgStyle parent_style, XmlElement tag) { bool hidden = false; foreach (Attribute attr in tag.get_attributes ()) { @@ -82,7 +84,7 @@ public class SvgFile : GLib.Object { SvgStyle style = SvgStyle.parse (drawing.defs, parent_style, tag.get_attributes ()); - foreach (Tag t in tag) {+ foreach (XmlElement t in tag) { string name = t.get_name (); if (name == "g") {@@ -105,8 +107,8 @@ public class SvgFile : GLib.Object { } } - void parse_defs (SvgDrawing drawing, Tag tag) {- foreach (Tag t in tag) {+ void parse_defs (SvgDrawing drawing, XmlElement tag) {+ foreach (XmlElement t in tag) { // FIXME: radial string name = t.get_name (); @@ -129,7 +131,7 @@ public class SvgFile : GLib.Object { } } - void parse_linear_gradient (SvgDrawing drawing, Tag tag) {+ void parse_linear_gradient (SvgDrawing drawing, XmlElement tag) { Gradient gradient = new Gradient (); drawing.defs.add (gradient);@@ -168,7 +170,7 @@ public class SvgFile : GLib.Object { } } - foreach (Tag t in tag) {+ foreach (XmlElement t in tag) { // FIXME: radial string name = t.get_name (); @@ -178,7 +180,7 @@ public class SvgFile : GLib.Object { } } - void parse_stop (Gradient gradient, Tag tag) {+ void parse_stop (Gradient gradient, XmlElement tag) { SvgStyle parent_style = new SvgStyle (); // not inherited SvgStyle style = SvgStyle.parse (drawing.defs, parent_style, tag.get_attributes ()); Stop stop = new Stop ();@@ -218,11 +220,11 @@ public class SvgFile : GLib.Object { } // links are ignored, add the content to the layer- void parse_link (Layer layer, SvgStyle parent_style, Tag tag) {+ void parse_link (Layer layer, SvgStyle parent_style, XmlElement tag) { parse_layer (layer, parent_style, tag); } - void parse_object (Layer layer, SvgStyle parent_style, Tag tag) {+ void parse_object (Layer layer, SvgStyle parent_style, XmlElement tag) { string name = tag.get_name (); if (name == "path") {@@ -254,7 +256,7 @@ public class SvgFile : GLib.Object { } } - private void parse_polygon (Layer layer, SvgStyle parent_style, Tag tag) {+ private void parse_polygon (Layer layer, SvgStyle parent_style, XmlElement tag) { Polygon polygon = new Polygon (); foreach (Attribute attr in tag.get_attributes ()) {@@ -276,7 +278,7 @@ public class SvgFile : GLib.Object { } - private void parse_polyline (Layer layer, SvgStyle parent_style, Tag tag) {+ private void parse_polyline (Layer layer, SvgStyle parent_style, XmlElement tag) { Polyline polyline = new Polyline (); foreach (Attribute attr in tag.get_attributes ()) {@@ -297,7 +299,7 @@ public class SvgFile : GLib.Object { layer.add_object (polyline); } - private void parse_rect (Layer layer, SvgStyle parent_style, Tag tag) {+ private void parse_rect (Layer layer, SvgStyle parent_style, XmlElement tag) { Rectangle rectangle = new Rectangle (); foreach (Attribute attr in tag.get_attributes ()) {@@ -335,7 +337,7 @@ public class SvgFile : GLib.Object { layer.add_object (rectangle); } - private void parse_circle (Layer layer, SvgStyle parent_style, Tag tag) {+ private void parse_circle (Layer layer, SvgStyle parent_style, XmlElement tag) { Circle circle = new Circle (); foreach (Attribute attr in tag.get_attributes ()) {@@ -361,7 +363,7 @@ public class SvgFile : GLib.Object { layer.add_object (circle); } - private void parse_ellipse (Layer layer, SvgStyle parent_style, Tag tag) {+ private void parse_ellipse (Layer layer, SvgStyle parent_style, XmlElement tag) { Ellipse ellipse = new Ellipse (); foreach (Attribute attr in tag.get_attributes ()) {@@ -391,7 +393,7 @@ public class SvgFile : GLib.Object { layer.add_object (ellipse); } - private void parse_line (Layer layer, SvgStyle parent_style, Tag tag) {+ private void parse_line (Layer layer, SvgStyle parent_style, XmlElement tag) { Line line = new Line (); foreach (Attribute attr in tag.get_attributes ()) {@@ -574,7 +576,7 @@ public class SvgFile : GLib.Object { return param.strip(); } - private bool is_visible (Tag tag) {+ private bool is_visible (XmlElement tag) { bool hidden = false; foreach (Attribute attr in tag.get_attributes ()) {@@ -602,7 +604,7 @@ public class SvgFile : GLib.Object { return new SvgTransforms (); } - private void parse_path (Layer layer, SvgStyle parent_style, Tag tag) {+ private void parse_path (Layer layer, SvgStyle parent_style, XmlElement tag) { SvgPath path = new SvgPath (); foreach (Attribute attr in tag.get_attributes ()) {@@ -17,7 +17,7 @@ using Math; namespace SvgBird { -public class SvgStyle {+public class SvgStyle : GLib.Object { public Gee.HashMap<string, string> style; @@ -31,7 +31,12 @@ public class SvgStyle { public SvgStyle () { style = new Gee.HashMap<string, string> (); }- ++ public SvgStyle.for_properties (Defs? defs, string style) {+ parse_key_value_pairs (style);+ set_style_properties (defs, this);+ }+ public LineCap get_line_cap () { string l; @@ -66,8 +71,6 @@ public class SvgStyle { public static SvgStyle parse (Defs? d, SvgStyle inherited, Attributes attributes) { SvgStyle s = new SvgStyle ();- double fill_opacity = 1;- double stroke_opacity = 1; s.style.set ("fill", "#000000"); // default fill value @@ -102,6 +105,14 @@ public class SvgStyle { } } + set_style_properties (d, s);+ return s;+ }+ + static void set_style_properties (Defs? d, SvgStyle s) {+ double fill_opacity = 1;+ double stroke_opacity = 1;+ s.stroke_width = SvgFile.parse_number (s.style.get ("stroke-width")); s.stroke = Color.parse (s.style.get ("stroke")); s.fill = Color.parse (s.style.get ("fill"));@@ -131,9 +142,7 @@ public class SvgStyle { if (s.stroke != null) { Color color = (!) s.stroke; color.a = stroke_opacity;- }- - return s;+ } } void parse_key_value_pairs (string svg_style) {