Commit 76c9ad8
Linear gradients and matrix tranform on gradients
Changed files (12)
modifiedlibbirdfont/Layer.valamodifiedlibbirdfont/Svg/Color.valaaddedlibbirdfont/Svg/Defs.valamodifiedlibbirdfont/Svg/Gradient.valamodifiedlibbirdfont/Svg/Object.valamodifiedlibbirdfont/Svg/Stop.valamodifiedlibbirdfont/Svg/SvgDrawing.valamodifiedlibbirdfont/Svg/SvgFile.valamodifiedlibbirdfont/Svg/SvgParser.valamodifiedlibbirdfont/Svg/SvgStyle.valamodifiedlibbirdfont/Svg/SvgTransform.valaaddedlibbirdfont/Svg/SvgTransforms.vala
Diff
@@ -25,12 +25,12 @@ public class Layer : GLib.Object { public Gradient? gradient = null; public bool single_path = false; - public Gee.ArrayList<SvgTransform> transforms;+ public SvgTransforms transforms; public Layer () { objects = new ObjectGroup (); subgroups = new Gee.ArrayList<Layer> ();- transforms = new Gee.ArrayList<SvgTransform> ();+ transforms = new SvgTransforms (); } public int index_of (Layer sublayer) {@@ -266,7 +266,8 @@ public class Color { } public string to_string () {- return @"r: $r, g: $g, b: $b, a: $a";+ string alpha = Font.to_hex_code ((unichar) Math.rint (a * 254));+ return @"$(to_rgb_hex ())" + alpha; } public Color copy () {new file mode 100644@@ -0,0 +1,85 @@+/*+ 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 BirdFont {++public class Defs {+ public Gee.ArrayList<Gradient> gradients = new Gee.ArrayList<Gradient> ();++ public void add (Gradient g) {+ gradients.add (g);+ }+ + public Gradient? get_gradient_for_url (string? url) {+ if (url == null) {+ return null;+ }+ + string tag_id = (!) url;++ if (unlikely (!is_url (tag_id))) {+ warning ("Not an URL: " + tag_id);+ return null;+ }++ int p1 = tag_id.index_of ("(");+ if (unlikely (p1 == -1)) {+ warning ("Not an URL: " + tag_id);+ return null;+ }++ int p2 = tag_id.index_of (")");+ if (unlikely (p2 == -1 || p2 < p1)) {+ warning ("Not an URL: " + tag_id);+ return null;+ }+ + p1 += "(".length;+ int length = p2 - p1;+ tag_id = tag_id.substring (p1, length);+ + return get_gradient_for_id (tag_id);+ } ++ public Gradient? get_gradient_for_id (string id) {+ string tag_id;+ + if (id.has_prefix ("#")) {+ tag_id = id.substring ("#".length);+ } else {+ tag_id = id;+ }+ + foreach (Gradient gradient in gradients) {+ if (gradient.id == tag_id) {+ return gradient;+ }+ }+ + return null; + }++ public static bool is_url (string? attribute) {+ if (attribute == null) {+ return false;+ }+ + return ((!) attribute).has_prefix ("url");+ }+}++}@@ -25,7 +25,9 @@ public class Gradient : GLib.Object { public Gee.ArrayList<Stop> stops; - public int id = -1;+ public string id = "";+ public string? href = null;+ public SvgTransforms transforms; public Gradient () { x1 = 0;@@ -33,6 +35,7 @@ public class Gradient : GLib.Object { x2 = 0; y2 = 0; stops = new Gee.ArrayList<Stop> ();+ transforms = new SvgTransforms (); } public Gradient copy () {@@ -48,6 +51,29 @@ public class Gradient : GLib.Object { return g; }+ + public void copy_stops (Gradient g) {+ foreach (Stop stop in g.stops) {+ stops.add (stop.copy ());+ }+ }+ + public string to_string () {+ StringBuilder description = new StringBuilder ();+ description.append ("Gradient: ");+ description.append (@"x1=$x1, y1=$y1, x2=$x2, y2=$y2");+ + foreach (Stop stop in stops) {+ description.append (" ");+ description.append (stop.to_string ());+ }+ + return description.str;+ }+ + public Matrix get_matrix () {+ return transforms.get_matrix ();+ } } }@@ -22,7 +22,7 @@ public abstract class Object : GLib.Object { public bool visible = true; public SvgStyle style = new SvgStyle ();- public Gee.ArrayList<SvgTransform> transforms = new Gee.ArrayList<SvgTransform> ();+ public SvgTransforms transforms = new SvgTransforms (); public virtual Color? color { get; set; } // FIXME: keep this in svg style public virtual Color? stroke_color { get; set; }@@ -94,9 +94,7 @@ public abstract class Object : GLib.Object { if (style.fill_gradient != null) { apply_gradient (cr, (!) style.fill_gradient);- }- - if (style.fill != null) {+ } else if (style.fill != null) { fill = (!) style.fill; cr.set_source_rgba (fill.r, fill.g, fill.b, fill.a); }@@ -111,9 +109,7 @@ public abstract class Object : GLib.Object { if (style.stroke_gradient != null) { apply_gradient (cr, (!) style.stroke_gradient);- }- - if (style.stroke != null) {+ } else if (style.stroke != null) { stroke = (!) style.stroke; cr.set_source_rgba (stroke.r, stroke.g, stroke.b, stroke.a); }@@ -141,45 +137,17 @@ public abstract class Object : GLib.Object { pattern.add_color_stop_rgba (s.offset, c.r, c.g, c.b, c.a); } + pattern.set_matrix (g.get_matrix ());+ cr.set_source (pattern); } } public void apply_transform (Context cr) {- foreach (SvgTransform transform in transforms) {- if (transform.type == TransformType.SCALE) {- if (transform.arguments.size == 1) {- double s = transform.arguments.get_double (0);- cr.scale (s, s);- } else if (transform.arguments.size == 2) {- double s0 = transform.arguments.get_double (0);- double s1 = transform.arguments.get_double (1);- cr.scale (s0, s1);- }- } else if (transform.type == TransformType.TRANSLATE) {- if (transform.arguments.size == 1) {- double s = transform.arguments.get_double (0);- cr.translate (s, 0);- } else if (transform.arguments.size == 2) {- double s0 = transform.arguments.get_double (0);- double s1 = transform.arguments.get_double (1);- cr.translate (s0, s1);- }- } else if (transform.type == TransformType.MATRIX) {- if (transform.arguments.size == 1) {- double s0 = transform.arguments.get_double (0);- double s1 = transform.arguments.get_double (1);- double s2 = transform.arguments.get_double (2);- double s3 = transform.arguments.get_double (3); - double s4 = transform.arguments.get_double (4);- double s5 = transform.arguments.get_double (5);- - Matrix matrix = Matrix (s0, s1, s2, s3, s4, s5);- cr.set_matrix (matrix);- }- }- }+ Matrix matrix = transforms.get_matrix ();+ cr.set_matrix (matrix); }+ } }@@ -30,6 +30,10 @@ public class Stop : GLib.Object { s.offset = offset; return s; }+ + public string to_string () {+ return @"Stop: $(offset), " + color.to_string ();+ } } }@@ -19,7 +19,7 @@ namespace BirdFont { public class SvgDrawing { public Layer root_layer = new Layer ();- public Gee.ArrayList<Gradient> gradients = new Gee.ArrayList<Gradient> ();+ public Defs defs = new Defs (); } }@@ -18,6 +18,8 @@ namespace BirdFont { public class SvgFile : GLib.Object { + SvgDrawing drawing;+ public SvgFile () { } @@ -42,7 +44,7 @@ public class SvgFile : GLib.Object { } private SvgDrawing parse_svg_file (Tag tag) {- SvgDrawing drawing = new SvgDrawing ();+ drawing = new SvgDrawing (); foreach (Tag t in tag) { string name = t.get_name ();@@ -108,15 +110,37 @@ public class SvgFile : GLib.Object { parse_linear_gradient (drawing, t); } }+ + foreach (Gradient gradient in drawing.defs.gradients) {+ if (gradient.href != null) {+ Gradient? referenced;+ referenced = drawing.defs.get_gradient_for_id ((!) gradient.href);+ + if (referenced != null) {+ gradient.copy_stops ((!) referenced);+ }+ + gradient.href = null;+ }+ } } void parse_linear_gradient (SvgDrawing drawing, Tag tag) { Gradient gradient = new Gradient (); + drawing.defs.add (gradient);+ foreach (Attribute attr in tag.get_attributes ()) { string name = attr.get_name (); - if (name == "href") { + // FIXME: gradientUnits+ + if (name == "gradientTransform") {+ gradient.transforms = parse_transform (attr.get_content ());+ }++ if (name == "href") {+ gradient.href = attr.get_content (); } if (name == "x1") {@@ -134,6 +158,10 @@ public class SvgFile : GLib.Object { if (name == "y2") { gradient.y2 = parse_number (attr.get_content ()); }++ if (name == "id") {+ gradient.id = attr.get_content ();+ } } foreach (Tag t in tag) {@@ -141,15 +169,17 @@ public class SvgFile : GLib.Object { string name = t.get_name (); if (name == "stop") {- parse_stop (gradient, tag);+ parse_stop (gradient, t); } } } void parse_stop (Gradient gradient, Tag tag) {- SvgStyle style = SvgStyle.parse (tag.get_attributes ());+ SvgStyle style = SvgStyle.parse (drawing.defs, tag.get_attributes ()); Stop stop = new Stop (); + gradient.stops.add (stop);+ foreach (Attribute attr in tag.get_attributes ()) { string name = attr.get_name (); @@ -252,7 +282,7 @@ public class SvgFile : GLib.Object { } rectangle.transforms = get_transform (tag.get_attributes ());- rectangle.style = SvgStyle.parse (tag.get_attributes ());+ rectangle.style = SvgStyle.parse (drawing.defs,tag.get_attributes ()); rectangle.visible = is_visible (tag); layer.add_object (rectangle);@@ -268,12 +298,12 @@ public class SvgFile : GLib.Object { } // FIXME: reverse order?- public Gee.ArrayList<SvgTransform> parse_transform (string transforms) {+ public SvgTransforms parse_transform (string transforms) { string[] functions; string transform = transforms;- Gee.ArrayList<SvgTransform> transform_functions;+ SvgTransforms transform_functions; - transform_functions = new Gee.ArrayList<SvgTransform> ();+ transform_functions = new SvgTransforms (); transform = transform.replace ("\t", " "); transform = transform.replace ("\n", " ");@@ -438,14 +468,14 @@ public class SvgFile : GLib.Object { return !hidden; } - private Gee.ArrayList<SvgTransform> get_transform (Attributes attributes) {+ private SvgTransforms get_transform (Attributes attributes) { foreach (Attribute attr in attributes) { if (attr.get_name () == "transform") { return parse_transform (attr.get_content ()); } } - return new Gee.ArrayList<SvgTransform> ();+ return new SvgTransforms (); } private void parse_path (Layer layer, Tag tag) {@@ -458,7 +488,7 @@ public class SvgFile : GLib.Object { } path.transforms = get_transform (tag.get_attributes ());- path.style = SvgStyle.parse (tag.get_attributes ());+ path.style = SvgStyle.parse (drawing.defs, tag.get_attributes ()); path.visible = is_visible (tag); layer.add_object (path);@@ -508,7 +508,7 @@ public class SvgParser { } } - style = SvgStyle.parse (tag.get_attributes ());+ style = SvgStyle.parse (null, tag.get_attributes ()); if (hidden) { return;@@ -576,7 +576,7 @@ public class SvgParser { } } - style = SvgStyle.parse (tag.get_attributes ());+ style = SvgStyle.parse (null, tag.get_attributes ()); if (hidden) { return;@@ -642,7 +642,7 @@ public class SvgParser { } } - style = SvgStyle.parse (tag.get_attributes ());+ style = SvgStyle.parse (null, tag.get_attributes ()); if (hidden) { return;@@ -720,7 +720,7 @@ public class SvgParser { } } - style = SvgStyle.parse (tag.get_attributes ());+ style = SvgStyle.parse (null, tag.get_attributes ()); if (hidden) { return;@@ -815,7 +815,7 @@ public class SvgParser { } } - style = SvgStyle.parse (tag.get_attributes ());+ style = SvgStyle.parse (null, tag.get_attributes ()); if (hidden) { return path_list;@@ -855,7 +855,7 @@ public class SvgParser { } } - style = SvgStyle.parse (tag.get_attributes ());+ style = SvgStyle.parse (null, tag.get_attributes ()); if (hidden) { return;@@ -68,7 +68,7 @@ public class SvgStyle { return double.parse (style.get ("stroke-width")); } - public static SvgStyle parse (Attributes attributes) {+ public static SvgStyle parse (Defs? d, Attributes attributes) { SvgStyle s = new SvgStyle (); double fill_opacity = 1; double stroke_opacity = 1;@@ -99,8 +99,17 @@ public class SvgStyle { } } + 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")); + if (d != null) {+ Defs defs = (!) d;++ s.stroke_gradient = defs.get_gradient_for_url (s.style.get ("stroke"));+ s.fill_gradient = defs.get_gradient_for_url (s.style.get ("fill"));+ }+ if (s.fill != null) { Color color = (!) s.fill; color.a = fill_opacity;@@ -111,9 +120,6 @@ public class SvgStyle { color.a = stroke_opacity; } - s.stroke_width = SvgFile.parse_number (s.style.get ("stroke-width"));- s.fill = Color.parse (s.style.get ("fill"));- return s; } @@ -13,6 +13,7 @@ */ using B;+using Cairo; namespace BirdFont { @@ -29,6 +30,46 @@ public class SvgTransform : GLib.Object { public SvgTransform () { }+ + public Matrix get_matrix () {+ Matrix matrix;+ + matrix = Matrix.identity ();+ + if (type == TransformType.SCALE) {+ if (arguments.size == 1) {+ double s = arguments.get_double (0);+ matrix.scale (s, s);+ return matrix;+ } else if (arguments.size == 2) {+ double s0 = arguments.get_double (0);+ double s1 = arguments.get_double (1);+ matrix.scale (s0, s1);+ }+ } else if (type == TransformType.TRANSLATE) {+ if (arguments.size == 1) {+ double s = arguments.get_double (0);+ matrix.translate (s, 0);+ } else if (arguments.size == 2) {+ double s0 = arguments.get_double (0);+ double s1 = arguments.get_double (1);+ matrix.translate (s0, s1);+ }+ } else if (type == TransformType.MATRIX) {+ if (arguments.size == 6) {+ double s0 = arguments.get_double (0);+ double s1 = arguments.get_double (1);+ double s2 = arguments.get_double (2);+ double s3 = arguments.get_double (3); + double s4 = arguments.get_double (4);+ double s5 = arguments.get_double (5);+ + matrix = Matrix (s0, s1, s2, s3, s4, s5);+ }+ }+ + return matrix;+ } } }new file mode 100644@@ -0,0 +1,47 @@+/*+ 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 Cairo;++namespace BirdFont {++public class SvgTransforms : GLib.Object {+ public Gee.ArrayList<SvgTransform> transforms;+ + public SvgTransforms () {+ transforms = new Gee.ArrayList<SvgTransform> ();+ }++ public void add (SvgTransform transform) {+ transforms.add (transform);+ }+ + public Matrix get_matrix () {+ Matrix matrix = Matrix.identity ();+ + if (transforms.size == 0) {+ return Matrix.identity ();+ }+ + matrix = transforms.get (0).get_matrix ();+ + for (int i = 1; i < transforms.size; i++) {+ matrix.multiply (matrix, transforms.get (i).get_matrix ());+ }+ + return matrix;+ }+}++}