Commit dfde4f4
Parse SVG files
Changed files (14)
modifieddodo.pydeletedlibbirdfont/BezierPoints.valaaddedlibbirdfont/Doubles.valamodifiedlibbirdfont/FastPath.valamodifiedlibbirdfont/Glyph.valamodifiedlibbirdfont/Layer.valamodifiedlibbirdfont/Object.valaaddedlibbirdfont/Svg/BezierPoints.valaaddedlibbirdfont/Svg/Points.valamodifiedlibbirdfont/Svg/Svg.valamodifiedlibbirdfont/Svg/SvgFile.valamodifiedlibbirdfont/Svg/SvgParser.valaaddedlibbirdfont/Svg/SvgPath.valaaddedlibbirdfont/Svg/SvgTransform.vala
Diff
@@ -303,6 +303,7 @@ def make_libbirdfont(target_binary, deps): libbirdfont/OpenFontFormat/*.vala \ libbirdfont/TextRendering/*.vala \ libbirdfont/Svg/*.vala \+ --pkg posix \ --pkg """ + config.GEE + """ \ --pkg gio-2.0 \ --pkg cairo \deleted file mode 100644@@ -1,33 +0,0 @@-/*- Copyright (C) 2014 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.-*/--namespace BirdFont {--/** Bezier point container for the SVG parser. */-public class BezierPoints {- public unichar type = '\0';- public unichar svg_type = '\0';- public double x0 = 0;- public double y0 = 0;- public double x1 = 0;- public double y1 = 0;- public double x2 = 0;- public double y2 = 0;- - public string to_string () {- return @"$((!)type.to_string ()) $x0,$y0 $x1,$y1 $x2,$y2 SVG:$((!)svg_type.to_string ())";- }-}--}new file mode 100644@@ -0,0 +1,54 @@+/*+ Copyright (C) 2015 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 Doubles : GLib.Object {+ public double* data;+ public int size = 10;+ int capacity = 10;+ + public Doubles () {+ data = new double[capacity];+ }++ ~Doubles () {+ delete data;+ data = null;+ }+ + public Doubles.for_capacity (int capacity) {+ data = new double[capacity];+ this.capacity = capacity;+ }+ + public void add(double d) {+ if (size >= capacity) {+ int new_capacity = 2 * capacity;+ double* new_data = new double[new_capacity];+ Posix.memcpy (new_data, data, sizeof (double) * size);+ delete data;+ data = new_data;+ capacity = new_capacity;+ }+ + data[size] = d;+ size++;+ }+}++}+@@ -227,6 +227,11 @@ public class FastPath : Object { public override Object copy () { return new FastPath.create_copy (this); }++ public override string to_string () {+ return "FastPath";+ }+ } }@@ -242,6 +242,11 @@ public class Glyph : FontDisplay { current_layer = layers.subgroups.size - 1; } + public void add_layer (Layer layer) {+ layers.add_layer (layer);+ current_layer = layers.subgroups.size - 1;+ }+ public int get_layer_index (Layer layer) { return layers.index_of (layer); }@@ -1704,7 +1709,6 @@ public class Glyph : FontDisplay { /** Draw filled paths. */ public void draw_paths (Context cr, Color? c = null) {- cr.save (); cr.new_path (); @@ -1788,10 +1792,20 @@ public class Glyph : FontDisplay { cr.save (); cr.new_path ();- foreach (Path p in get_visible_paths ()) {- if (p.stroke == 0) {- color = p.color == null ? Color.black () : (!) p.color;- p.draw_path (cr, color);+ + get_current_layer ().print();+ + foreach (Object o in get_visible_objects ()) {+ print("visible obj\n");+ if (o is FastPath) {+ Path p = ((FastPath) o).get_path ();++ if (p.stroke == 0) {+ color = p.color == null ? Color.black () : (!) p.color;+ p.draw_path (cr, color);+ }+ } else {+ o.draw (cr); } } cr.close_path ();@@ -25,9 +25,12 @@ public class Layer : GLib.Object { public Gradient? gradient = null; public bool single_path = false; + public Gee.ArrayList<SvgTransform> transforms;+ public Layer () { objects = new ObjectGroup (); subgroups = new Gee.ArrayList<Layer> ();+ transforms = new Gee.ArrayList<SvgTransform> (); } public int index_of (Layer sublayer) {@@ -64,21 +67,21 @@ public class Layer : GLib.Object { } public ObjectGroup get_visible_objects () {- ObjectGroup paths = new ObjectGroup ();+ ObjectGroup object_group = new ObjectGroup (); if (visible) { foreach (Object o in objects) {- paths.add (o);+ object_group.add (o); } } foreach (Layer sublayer in subgroups) { if (sublayer.visible) {- paths.append (sublayer.get_visible_objects ());+ object_group.append (sublayer.get_visible_objects ()); } } - return paths; + return object_group; } public PathList get_visible_paths () {@@ -211,15 +214,27 @@ public class Layer : GLib.Object { } public void print (int indent = 0) {+ stdout.printf (@"Layer: $(name)");++ if (!visible) {+ stdout.printf (" hidden");+ }+ + stdout.printf (@"\n");+ foreach (Object o in objects) { for (int i = 0; i < indent; i++) { stdout.printf ("\t"); }- stdout.printf (@"Path open: $(o.is_open ())");+ stdout.printf (@"Object $(o.to_string ())"); if (o.color != null) { stdout.printf (" %s", ((!) o.color).to_rgb_hex ()); }++ if (!o.visible) {+ stdout.printf (" hidden");+ } stdout.printf ("\n"); }@@ -20,6 +20,10 @@ namespace BirdFont { public abstract class Object : GLib.Object { bool open = false; + public bool visible = true;+ public SvgStyle style = new SvgStyle ();+ public Gee.ArrayList<SvgTransform> transforms = new Gee.ArrayList<SvgTransform> ();+ public abstract Color? color { get; set; } public abstract Color? stroke_color { get; set; } public abstract Gradient? gradient { get; set; }@@ -58,6 +62,28 @@ public abstract class Object : GLib.Object { public abstract void rotate (double theta, double xc, double yc); public abstract bool is_empty (); public abstract void resize (double ratio_x, double ratio_y);++ public static void copy_attributes (Object from, Object to) {+ to.open = from.open;++ to.color = from.color;+ to.stroke_color = from.stroke_color;+ to.gradient = from.gradient;+ + to.xmax = from.xmax;+ to.xmin = from.xmin;+ to.ymax = from.ymax;+ to.ymin = from.ymin;+ + to.rotation = from.rotation;+ to.stroke = from.stroke;+ to.line_cap = from.line_cap;+ to.fill = from.fill; + }+ + public virtual string to_string () {+ return "Object";+ } } }new file mode 100644@@ -0,0 +1,33 @@+/*+ Copyright (C) 2014 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.+*/++namespace BirdFont {++/** Bezier point container for the SVG parser. */+public class BezierPoints {+ public unichar type = '\0';+ public unichar svg_type = '\0';+ public double x0 = 0;+ public double y0 = 0;+ public double x1 = 0;+ public double y1 = 0;+ public double x2 = 0;+ public double y2 = 0;+ + public string to_string () {+ return @"$((!)type.to_string ()) $x0,$y0 $x1,$y1 $x2,$y2 SVG:$((!)svg_type.to_string ())";+ }+}++}new file mode 100644@@ -0,0 +1,30 @@+/*+ Copyright (C) 2015 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 Points : GLib.Object {+ public Doubles point_data = new Doubles.for_capacity (100);+ public double x = 0;+ public double y = 0;++ public void add (double p) {+ point_data.add (p);+ }+}++}+@@ -16,6 +16,8 @@ using Cairo; namespace BirdFont { +// FIXME: substrings+ public class Svg { /** Export to svg glyph data. */@@ -12,10 +12,330 @@ Lesser General Public License for more details. */ +using B;+ namespace BirdFont { public class SvgFile : GLib.Object { + public SvgFile () { + }+ + public Layer parse (string path) {+ string xml_data;+ + try {+ FileUtils.get_contents (path, out xml_data);+ XmlParser xmlparser = new XmlParser (xml_data);+ + if (xmlparser.validate ()) {+ Tag root = xmlparser.get_root_tag ();+ return parse_svg_file (root);+ } else {+ warning ("Invalid xml file.");+ }+ } catch (GLib.Error error) {+ warning (error.message);+ }+ + return new Layer ();+ }+ + private Layer parse_svg_file (Tag tag) {+ Layer layer = new Layer ();+ + foreach (Tag t in tag) {+ string name = t.get_name ();+ + if (name == "g") {+ parse_layer (layer, t);+ }+ + parse_object (layer, t);+ }+ + return layer;+ }++ private void parse_layer (Layer layer, Tag tag) {+ bool hidden = false;++ foreach (Attribute attr in tag.get_attributes ()) { + if (attr.get_name () == "display" && attr.get_content () == "none") {+ hidden = true;+ }+ + if (attr.get_name () == "visibility"+ && (attr.get_content () == "hidden" + || attr.get_content () == "collapse")) {+ hidden = true;+ }+ }+ + if (hidden) {+ layer.visible = !hidden;+ }+ + foreach (Tag t in tag) {+ if (t.get_name () == "g") {+ Layer sublayer = new Layer ();+ parse_layer (layer, t);+ layer.subgroups.add (sublayer);+ }++ parse_object (layer, t);+ }++ foreach (Attribute attr in tag.get_attributes ()) { + if (attr.get_name () == "transform") {+ layer.transforms = parse_transform (attr.get_content ());+ }+ }+ }++ void parse_object (Layer layer, Tag tag) {+ string name = tag.get_name ();+ + if (name == "path") {+ parse_path (layer, tag);+ }+ + if (name == "polygon") {+ parse_polygon (layer, tag);+ }++ if (name == "polyline") {+ parse_polyline (layer, tag);+ }+ + if (name == "rect") {+ parse_rect (layer, tag);+ }++ if (name == "circle") {+ parse_circle (layer, tag);+ }++ if (name == "ellipse") {+ parse_ellipse (layer, tag);+ }+ + if (name == "line") {+ parse_line (layer, tag);+ }+ }++ private void parse_polygon (Layer layer, Tag tag) {+ }+ + private void parse_polyline (Layer layer, Tag tag) {+ }+ + private void parse_rect (Layer layer, Tag tag) {+ }+ + private void parse_circle (Layer layer, Tag tag) {+ }+ + private void parse_ellipse (Layer layer, Tag tag) {+ }+ + private void parse_line (Layer layer, Tag tag) {+ }+ + // FIXME: reverse order?+ public Gee.ArrayList<SvgTransform> parse_transform (string transforms) {+ string[] functions;+ string transform = transforms;+ Gee.ArrayList<SvgTransform> transform_functions;+ + transform_functions = new Gee.ArrayList<SvgTransform> ();+ + transform = transform.replace ("\t", " ");+ transform = transform.replace ("\n", " ");+ transform = transform.replace ("\r", " ");+ + // use only a single space as separator+ while (transform.index_of (" ") > -1) {+ transform = transform.replace (" ", " ");+ }+ + if (unlikely (transform.index_of (")") > -1)) {+ warning ("No parenthesis in transform function.");+ return transform_functions;+ }+ + // add separator+ transform = transform.replace (") ", "|");+ transform = transform.replace (")", "|"); + functions = transform.split ("|");+ + for (int i = 0; i < functions.length; i++) {+ if (functions[i].has_prefix ("translate")) {+ transform_functions.add (translate (functions[i]));+ }+ + if (functions[i].has_prefix ("scale")) {+ transform_functions.add (scale (functions[i]));+ }++ if (functions[i].has_prefix ("matrix")) {+ transform_functions.add (matrix (functions[i]));+ }+ + // TODO: rotate etc.+ }+ + return transform_functions;+ }++ private SvgTransform matrix (string function) {+ string parameters = get_transform_parameters (function);+ string[] p = parameters.split (" ");+ SvgTransform transform = new SvgTransform ();+ transform.type = TransformType.MATRIX;+ + if (unlikely (p.length != 6)) {+ warning ("Expecting six parameters for matrix transformation.");+ return transform;+ }+ + for (int i = 0; i < 6; i++) {+ double argument = SvgParser.parse_double (p[i]);+ transform.arguments.add (argument);+ }+ + return transform;+ }+ + private SvgTransform scale (string function) {+ string parameters = get_transform_parameters (function);+ string[] p = parameters.split (" ");+ SvgTransform transform = new SvgTransform ();+ transform.type = TransformType.SCALE;++ if (p.length > 0) {+ transform.arguments.add (SvgParser.parse_double (p[0]));+ }+ + if (p.length > 1) {+ transform.arguments.add (SvgParser.parse_double (p[1]));+ }+ + return transform;+ }+ + private SvgTransform translate (string function) {+ string parameters = get_transform_parameters (function);+ string[] p = parameters.split (" ");+ SvgTransform transform = new SvgTransform ();+ transform.type = TransformType.TRANSLATE;+ + if (p.length > 0) {+ transform.arguments.add (SvgParser.parse_double (p[0]));+ }+ + if (p.length > 1) {+ transform.arguments.add (SvgParser.parse_double (p[1]));+ }+ + return transform;+ }++ private string get_transform_parameters (string function) {+ int i;+ string param = "";+ + i = function.index_of ("(");+ return_val_if_fail (i != -1, param);+ param = function.substring (i);++ param = param.replace ("(", "");+ param = param.replace ("\n", " ");+ param = param.replace ("\t", " ");+ param = param.replace (",", " ");+ + while (param.index_of (" ") > -1) {+ param.replace (" ", " ");+ }+ + return param.strip(); + }++ private bool is_visible (Tag tag) {+ bool hidden = false;++ foreach (Attribute attr in tag.get_attributes ()) {+ if (attr.get_name () == "display" && attr.get_content () == "none") {+ hidden = true;+ }+ + if (attr.get_name () == "visibility"+ && (attr.get_content () == "hidden" + || attr.get_content () == "collapse")) {+ hidden = true;+ }+ }+ + return !hidden;+ }++ private void parse_path (Layer layer, Tag tag) {+ SvgPath path = new SvgPath ();++ foreach (Attribute attr in tag.get_attributes ()) {+ if (attr.get_name () == "d") {+ path.points = parse_points (attr.get_content ());+ }++ if (attr.get_name () == "transform") {+ path.transforms = parse_transform (attr.get_content ());+ }+ }+ + path.style = SvgStyle.parse (tag.get_attributes ());+ path.visible = is_visible (tag); + + layer.add_object (path);+ }++ public Gee.ArrayList<Points> parse_points (string data) {+ Gee.ArrayList<Points> path_data = new Gee.ArrayList<Points> ();+ Points points = new Points ();+ BezierPoints[] bezier_points;+ int points_size;+ + SvgParser.get_bezier_points (data, out bezier_points, out points_size, true);++ for (int i = 0; i < points_size; i++) {+ if (bezier_points[i].type == 'M') {+ points.x = bezier_points[i].x0;+ points.y = bezier_points[i].y0;+ } else if (bezier_points[i].type == 'C') {+ points.add (bezier_points[i].x0);+ points.add (bezier_points[i].y0);+ points.add (bezier_points[i].x1);+ points.add (bezier_points[i].y1);+ points.add (bezier_points[i].x2);+ points.add (bezier_points[i].y2);+ } else if (bezier_points[i].type == 'L') {+ points.add (bezier_points[i].x0);+ points.add (bezier_points[i].y0);+ points.add (bezier_points[i].x0);+ points.add (bezier_points[i].y0);+ points.add (bezier_points[i].x0);+ points.add (bezier_points[i].y0);+ } else if (bezier_points[i].type == 'z') {+ path_data.add (points);+ points = new Points ();+ } else {+ string type = (!) bezier_points[i].type.to_string ();+ warning (@"SVG conversion not implemented for $type");+ }+ }++ return path_data;+ } } }@@ -63,18 +63,9 @@ public class SvgParser { } public static void import_color_svg (Glyph glyph, string path) {- try {- string data;- FileUtils.get_contents (path, out data);- glyph.color_svg_data = data;-- if (glyph.color_svg_data != null) {- glyph.svg_x = glyph.left_limit;- glyph.svg_y = BirdFont.get_current_font ().top_position;- }- } catch (GLib.Error e) {- warning (e.message);- }+ SvgFile svg_file = new SvgFile ();+ Layer layer = svg_file.parse (path);+ glyph.add_layer (layer); } public static void import_folder (SvgType type) {@@ -1017,7 +1008,7 @@ public class SvgParser { /** Add space as separator to svg data. * @param d svg data */- static string add_separators (string d) {+ public static string add_separators (string d) { string data = d; data = data.replace (",", " ");@@ -1063,44 +1054,34 @@ public class SvgParser { g.add_path (path); } }- - /** - * @param d svg data- * @param glyph use lines from this glyph but don't add the generated paths- * @param svg_glyph parse svg glyph with origo in lower left corner- * - * @return the new paths- */- public PathList parse_svg_data (string d, Glyph glyph, bool svg_glyph = false, double units = 1) {++ public static void get_bezier_points (string point_data, out BezierPoints[] bezier_points, out int points, bool svg_glyph) { double px = 0; double py = 0; double px2 = 0; double py2 = 0; double cx = 0; double cy = 0;- string data;- Font font;- PathList path_list = new PathList ();- BezierPoints[] bezier_points; string[] c; double arc_rx, arc_ry; double arc_rotation; int large_arc; int arc_sweep; double arc_dest_x, arc_dest_y;-- font = BirdFont.get_current_font (); - data = add_separators (d);+ int bi = 0;++ string data = add_separators (point_data); c = data.split (" ");- bezier_points = new BezierPoints[8 * c.length + 1]; // the arc instruction can use up to eight points++ // the arc instruction can use up to eight points+ int bezier_points_length = 8 * c.length + 1;+ bezier_points = new BezierPoints[bezier_points_length]; - for (int i = 0; i < 2 * c.length + 1; i++) {+ for (int i = 0; i < bezier_points_length; i++) { bezier_points[i] = new BezierPoints (); }- - int bi = 0;- + // parse path int i = -1; while (++i < c.length && bi < bezier_points.length) { @@ -1358,7 +1339,6 @@ public class SvgParser { bi++; } } else if (c[i] == "Q") {- while (i + 4 < c.length && is_point (c[i + 1])) { bezier_points[bi].type = 'Q'; bezier_points[bi].svg_type = 'Q';@@ -1431,7 +1411,7 @@ public class SvgParser { // the reflection cx = 2 * px - px2;- cy = 2 * py - py2; // if (svg_glyph) ?+ cy = 2 * py - py2; bezier_points[bi].x0 = cx; bezier_points[bi].y0 = cy;@@ -1538,7 +1518,7 @@ public class SvgParser { px = cx; py = cy; - bi++; + bi++; } } else if (c[i] == "a") { while (i + 7 < c.length && is_point (c[i + 1])) { @@ -1564,8 +1544,6 @@ public class SvgParser { px = cx; py = cy;- - } } else if (i + 7 < c.length && c[i] == "A") { while (is_point (c[i + 1])) { @@ -1613,15 +1591,38 @@ public class SvgParser { if (bi == 0) { warning ("No points in path.");+ }++ points = bi;+ }+ + /** + * @param d svg data+ * @param glyph use lines from this glyph but don't add the generated paths+ * @param svg_glyph parse svg glyph with origo in lower left corner+ * + * @return the new paths+ */+ public PathList parse_svg_data (string d, Glyph glyph, bool svg_glyph = false, double units = 1) {+ Font font;+ PathList path_list = new PathList ();+ BezierPoints[] bezier_points;+ int points;++ font = BirdFont.get_current_font ();+ get_bezier_points (d, out bezier_points, out points, svg_glyph);+ + if (points == 0) {+ warning ("No points in path."); return path_list; } - move_and_resize (bezier_points, bi, svg_glyph, units, glyph);+ move_and_resize (bezier_points, points, svg_glyph, units, glyph); if (format == SvgFormat.ILLUSTRATOR) {- path_list = create_paths_illustrator (bezier_points, bi);+ path_list = create_paths_illustrator (bezier_points, points); } else {- path_list = create_paths_inkscape (bezier_points, bi);+ path_list = create_paths_inkscape (bezier_points, points); } // TODO: Find out if it is possible to tie handles.@@ -2044,13 +2045,13 @@ public class SvgParser { return int.parse ((!) s); } - static double parse_double (string? s) {- if (is_null (s)) {+ public static double parse_double (string? s) {+ if (unlikely (is_null (s))) { warning ("Got null instead of expected string."); return 0; } - if (!is_point ((!) s)) {+ if (unlikely (!is_point ((!) s))) { warning (@"Expecting a double got: $((!) s)"); return 0; }@@ -2089,7 +2090,7 @@ public class SvgParser { } bezier_points[bi] = new BezierPoints ();- bezier_points[bi].type == 'L';+ bezier_points[bi].type = 'L'; bezier_points[bi].x0 = parse_double (c[i]); bezier_points[bi].y0 = -parse_double (c[i + 1]); bi++;new file mode 100644@@ -0,0 +1,127 @@+/*+ Copyright (C) 2015 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 SvgPath : Object {+ public override double stroke { get; set; }+ public override double rotation { get; set; }+ public override LineCap line_cap { get; set; }+ public override bool fill { get; set; }++ public override Color? color { get; set; }+ public override Color? stroke_color { get; set; }+ public override Gradient? gradient { get; set; }++ public override double xmin { get; set; }+ public override double xmax { get; set; }+ public override double ymin { get; set; }+ public override double ymax { get; set; }+ + public Gee.ArrayList<Points> points = new Gee.ArrayList<Points> ();+ + public SvgPath () {+ }++ public SvgPath.create_copy (SvgPath p) {+ Object.copy_attributes (p, this);+ }+ + public override bool is_over (double x, double y) {+ return false;+ }+ + public override void draw (Context cr, Color? c = null) {+ cr.save ();+ + foreach (Points p in points) {+ cr.new_path ();+ cr.move_to (p.x, p.y);+ + draw_points (cr, p);+ }+ + cr.fill (); // FIXME: stroke etc.+ cr.restore ();+ }++ public void draw_points (Context cr, Points points) {+ Doubles p = points.point_data;++ print(@"draw $(p.size)\n");+ + return_if_fail (p.size % 6 != 0);+ + for (int i = 0; i < p.size; i += 6) {+ cr.curve_to (p.data[i], p.data[i + 1], + p.data[i + 2], p.data[i + 3],+ p.data[i + 4], p.data[i + 5]);+ + print(@"$(p.data[i])");+ }+ + cr.set_source_rgba (0, 0.5, 0, 1);+ cr.fill ();+ + cr.close_path ();+ cr.new_path ();+ cr.move_to (points.x, points.y);+ cr.set_source_rgba (0, 0, 0.5, 0.5);+ + for (int i = 0; i < p.size; i += 2) {+ cr.line_to (p.data[i], p.data[i + 1]);+ }++ cr.fill ();++ cr.close_path ();+ cr.new_path ();+ cr.move_to (points.x, points.y);+ cr.set_source_rgba (0.5, 0, 0, 0.5);+ + for (int i = 0; i < p.size; i += 6) {+ cr.line_to (p.data[i + 4], p.data[i + 5]);+ }++ cr.fill (); + }++ public override void move (double dx, double dy) {+ }+ + public override void update_region_boundaries () {+ }++ public override void rotate (double theta, double xc, double yc) {+ }+ + public override bool is_empty () {+ return false;+ }+ + public override void resize (double ratio_x, double ratio_y) {+ }+ + public override Object copy () {+ return new SvgPath.create_copy (this);+ }++ public override string to_string () {+ return "SvgPath";+ }+}++}new file mode 100644@@ -0,0 +1,34 @@+/*+ Copyright (C) 2015 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;++namespace BirdFont {++public enum TransformType {+ NONE,+ TRANSLATE,+ MATRIX,+ SCALE+}++public class SvgTransform : GLib.Object {+ public TransformType type = TransformType.NONE;+ public Doubles arguments = new Doubles.for_capacity (10);+ + public SvgTransform () {+ }+}++}