Clone in a terminal:
Commit 841122b
Johan Mattsson - 2016-01-10T17:00:12+00:00
841122b548200adcc391be1588e55d973356e24fjohan.mattsson.m@gmail.com

Move the SVG parser and the SVG rendering code to libsvgbird

Changed files (68)

Diff

diff --git a/build.py b/build.pyindex 265deeb..a71e729 100755--- a/build.py+++ b/build.py@@ -10,19 +10,25 @@ from scripts import version  if platform == 'msys': 	process_tasks(dodo.make_libbirdgems('libbirdgems.dll', []))-	process_tasks(dodo.make_libbirdfont('libbirdfont.dll', ['libbirdgems.dll']))-	process_tasks(dodo.make_libbirdfont('libbirdfont.dll', ['libbirdgems.dll']))-	process_tasks(dodo.make_birdfont_test('birdfont-test.exe', ['libbirdgems.so', 'libbirdfont.so']))+	process_tasks(dodo.make_libbirdgems('libsvgbird.dll', []))+	process_tasks(dodo.make_libbirdfont('libbirdfont.dll', ['libbirdgems.dll', 'libsvgbird.dll']))+	process_tasks(dodo.make_libbirdfont('libbirdfont.dll', ['libbirdgems.dll', 'libsvgbird.dll']))+	process_tasks(dodo.make_birdfont_test('birdfont-test.exe', +		['libbirdgems.dll', 'libbirdfont.dll', 'libsvgbird.dll'])) elif platform == 'darwin': 	gems = "libbirdgems." + str(version.LIBBIRDGEMS_SO_VERSION) + '.dylib' 	bird = "libbirdfont." + str(version.SO_VERSION) + '.dylib';+	svg = "libsvgbird." + str(version.LIBSVGBIRD_SO_VERSION) + '.dylib';+	process_tasks(dodo.make_libsvgbird(svg, [])) 	process_tasks(dodo.make_libbirdgems(gems, [])) 	process_tasks(dodo.make_libbirdfont(bird, [gems])) 	process_tasks(dodo.task_man()) else:+	process_tasks(dodo.task_libsvgbird()) 	process_tasks(dodo.task_libbirdgems()) 	process_tasks(dodo.task_libbirdfont())-	process_tasks(dodo.make_birdfont_test('birdfont-test', ['libbirdgems.so', 'libbirdfont.so']))+	process_tasks(dodo.make_birdfont_test('birdfont-test', +		['libsvgbird.so', 'libbirdgems.so', 'libbirdfont.so']))  if config.GTK: 	process_tasks(dodo.task_birdfont())diff --git a/configure b/configureindex d4e800d..ac5fd92 100755--- a/configure+++ b/configure@@ -11,7 +11,8 @@ import re from scripts.run import run  TARGETS = ['libbirdfont', -           'libbirdgems', +           'libbirdgems',+           'libsvgbird',            'birdfont',             'birdfont-autotrace',            'birdfont-export',@@ -206,12 +207,12 @@ configure(options.gtk, options.gee)  configfile.write_config(options.prefix) configfile.write_compile_parameters(options.prefix,-									 options.dest,-									 options.cc,-									 gee,-									 options.valac,-									 options.nonnull,-									 valacflags,-									 cflags,-									 ldflags,-									 options.gtk)+									options.dest,+									options.cc,+									gee,+									options.valac,+									options.nonnull,+									valacflags,+									cflags,+									ldflags,+									options.gtk)diff --git a/dodo.py b/dodo.pyindex 239d65e..fe207c6 100644--- a/dodo.py+++ b/dodo.py@@ -1,5 +1,5 @@ """-Copyright (C) 2012 2013 2014 2015 Eduardo Naufel Schettino and Johan Mattsson+Copyright (C) 2012 - 2016 Eduardo Naufel Schettino and Johan Mattsson  This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by@@ -48,6 +48,13 @@ else:     LIBBIRDGEMS_SO_VERSION=version.LIBBIRDGEMS_SO_VERSION  if "kfreebsd" in sys.platform:+    LIBSVGBIRD_SO_VERSION=version.LIBSVGBIRD_SO_VERSION+elif "openbsd" in sys.platform:+    LIBSVGBIRD_SO_VERSION='${LIBbirdgems_VERSION}'+else:+    LIBSVGBIRD_SO_VERSION=version.LIBSVGBIRD_SO_VERSION++if "kfreebsd" in sys.platform:     SO_VERSION=version.SO_VERSION elif "openbsd" in sys.platform:     SO_VERSION='${LIBbirdfont_VERSION}'@@ -78,13 +85,15 @@ def make_birdfont(target_binary, deps): 		--pkg webkitgtk-3.0 \ 		--pkg libnotify \ 		--pkg xmlbird \-		--pkg libbirdfont+		--pkg libbirdfont \+		--pkg libsvgbird \         """              cc_command = config.CC + " " + config.CFLAGS.get("birdfont", "") + """ \         -c C_SOURCE \ 		-D 'GETTEXT_PACKAGE="birdfont"' \         -I./build/libbirdfont \+        -I./build/libsvgbird \ 		$(pkg-config --cflags sqlite3) \ 		$(pkg-config --cflags """ + config.GEE + """) \ 		$(pkg-config --cflags gio-2.0) \@@ -107,7 +116,7 @@ def make_birdfont(target_binary, deps): 		$(pkg-config --libs webkitgtk-3.0) \ 		$(pkg-config --libs xmlbird) \ 		$(pkg-config --libs libnotify) \-		-L./build -L./build/bin -l birdgems\+		-L./build -L./build/bin -l birdgems -l svgbird \         -o build/bin/""" + target_binary      birdfont = Builder('birdfont',@@ -121,7 +130,7 @@ def make_birdfont(target_binary, deps):     yield birdfont.build()  def task_birdfont():-    yield make_birdfont('birdfont', ['libbirdgems.so', 'libbirdfont.so'])+    yield make_birdfont('birdfont', ['libbirdgems.so', 'libbirdfont.so', 'libsvgbird.so'])  def make_birdfont_export(target_binary, deps):     valac_command = config.VALAC + """ \@@ -136,13 +145,16 @@ def make_birdfont_export(target_binary, deps): 		--pkg gio-2.0  \ 		--pkg cairo \ 		--pkg xmlbird \-		--pkg libbirdfont+		--pkg libsvgbird \+		--pkg libbirdfont \+		--pkg libsvgbird \         """      cc_command = config.CC + " " + config.CFLAGS.get("birdfont-export", "") + """ \         -c C_SOURCE \ 		-D 'GETTEXT_PACKAGE="birdfont"' \         -I./build/libbirdfont \+        -I./build/libsvgbird \ 		$(pkg-config --cflags sqlite3) \ 		$(pkg-config --cflags """ + config.GEE + """) \ 		$(pkg-config --cflags gio-2.0) \@@ -160,7 +172,7 @@ def make_birdfont_export(target_binary, deps): 		$(pkg-config --libs cairo) \ 		$(pkg-config --libs glib-2.0) \ 		$(pkg-config --libs xmlbird) \-		-L./build -L./build/bin -l birdgems\+		-L./build -L./build/bin -l birdgems -l svgbird \ 		-o ./build/bin/""" + target_binary      birdfont_export = Builder('birdfont-export',@@ -174,7 +186,7 @@ def make_birdfont_export(target_binary, deps):     yield birdfont_export.build()  def task_birdfont_export():-    yield make_birdfont_export('birdfont-export', ['libbirdgems.so', 'libbirdfont.so'])+    yield make_birdfont_export('birdfont-export', ['libbirdgems.so', 'libbirdfont.so', 'libsvgbird.so'])  def make_birdfont_import(target_binary, deps):     valac_command = config.VALAC + """\@@ -190,12 +202,14 @@ def make_birdfont_import(target_binary, deps): 		--pkg cairo \ 		--pkg xmlbird \ 		--pkg libbirdfont \+		--pkg libsvgbird \         """              cc_command = config.CC + " " + config.CFLAGS.get("birdfont-import", "") + """ \         -c C_SOURCE \ 		-D 'GETTEXT_PACKAGE="birdfont"' \         -I./build/libbirdfont \+        -I./build/libsvgbird \ 		$(pkg-config --cflags sqlite3) \ 		$(pkg-config --cflags """ + config.GEE + """) \ 		$(pkg-config --cflags gio-2.0) \@@ -213,7 +227,7 @@ def make_birdfont_import(target_binary, deps): 		$(pkg-config --libs cairo) \ 		$(pkg-config --libs glib-2.0) \ 		$(pkg-config --libs xmlbird) \-		-L./build -L./build/bin -l birdgems\+		-L./build -L./build/bin -l birdgems -l svgbird \ 		-o ./build/bin/""" + target_binary      birdfont_import = Builder('birdfont-import',@@ -227,7 +241,7 @@ def make_birdfont_import(target_binary, deps):     yield birdfont_import.build()  def task_birdfont_import():-    yield make_birdfont_import('birdfont-import', ['libbirdgems.so', 'libbirdfont.so'])+    yield make_birdfont_import('birdfont-import', ['libbirdgems.so', 'libbirdfont.so', 'libsvgbird.so']) 	 def make_birdfont_autotrace(target_binary, deps):     valac_command = config.VALAC + """\@@ -243,12 +257,14 @@ def make_birdfont_autotrace(target_binary, deps): 		--pkg cairo \ 		--pkg xmlbird \ 		--pkg libbirdfont \+		--pkg libsvgbird \         """              cc_command = config.CC + " " + config.CFLAGS.get("birdfont-autotrace", "") + """ \         -c C_SOURCE \ 		-D 'GETTEXT_PACKAGE="birdfont"' \         -I./build/libbirdfont \+        -I./build/libsvgbird \ 		$(pkg-config --cflags sqlite3) \ 		$(pkg-config --cflags """ + config.GEE + """) \ 		$(pkg-config --cflags gio-2.0) \@@ -259,6 +275,7 @@ def make_birdfont_autotrace(target_binary, deps):     linker_command = config.CC + " " + config.LDFLAGS.get("birdfont-autotrace", "") + """ \ 		build/birdfont-autotrace/*.o \         -I./build/libbirdfont \+        -I./build/libsvgbird \ 		-Lbuild/bin/ -lbirdfont \ 		-lm \ 		$(pkg-config --libs sqlite3) \@@ -267,7 +284,7 @@ def make_birdfont_autotrace(target_binary, deps): 		$(pkg-config --libs cairo) \ 		$(pkg-config --libs glib-2.0) \ 		$(pkg-config --libs xmlbird) \-		-L./build -L./build/bin -l birdgems\+		-L./build -L./build/bin -l birdgems -l svgbird\ 		-o ./build/bin/""" + target_binary      birdfont_autotrace = Builder('birdfont-autotrace',@@ -281,7 +298,7 @@ def make_birdfont_autotrace(target_binary, deps):     yield birdfont_autotrace.build()  def task_birdfont_autotrace():-    yield make_birdfont_autotrace('birdfont-autotrace', ['libbirdgems.so', 'libbirdfont.so'])+    yield make_birdfont_autotrace('birdfont-autotrace', ['libbirdgems.so', 'libbirdfont.so', 'libsvgbird.so'])      def make_libbirdfont(target_binary, deps):     valac_command = config.VALAC + """\@@ -296,13 +313,13 @@ def make_libbirdfont(target_binary, deps):         libbirdfont/*.vala \         libbirdfont/OpenFontFormat/*.vala \         libbirdfont/TextRendering/*.vala \-        libbirdfont/Svg/*.vala \         --pkg posix \         --pkg """ + config.GEE + """ \         --pkg gio-2.0 \         --pkg cairo \         --pkg xmlbird \         --pkg libbirdgems \+        --pkg libsvgbird \         --pkg sqlite3 \         --pkg gdk-pixbuf-2.0 \         """@@ -313,6 +330,7 @@ def make_libbirdfont(target_binary, deps):             -D 'GETTEXT_PACKAGE="birdfont"' \             -I ./build/libbirdfont \             -I ./build/libbirdgems \+            -I ./build/libsvgbird \             $(pkg-config --cflags sqlite3) \             $(pkg-config --cflags fontconfig) \             $(pkg-config --cflags """ + config.GEE + """) \@@ -334,7 +352,7 @@ def make_libbirdfont(target_binary, deps):             $(pkg-config --libs cairo) \             $(pkg-config --libs glib-2.0) \             $(pkg-config --libs xmlbird) \-            -L./build -L./build/bin -l birdgems\+            -L./build -L./build/bin -l birdgems -l svgbird \             -o ./build/bin/""" + target_binary      libbirdfont = Builder('libbirdfont',@@ -348,7 +366,64 @@ def make_libbirdfont(target_binary, deps):     yield libbirdfont.build()  def task_libbirdfont():-    yield make_libbirdfont('libbirdfont.so.' + SO_VERSION, ['libbirdgems.so'])+    yield make_libbirdfont('libbirdfont.so.' + SO_VERSION, ['libbirdgems.so', 'libsvgbird.so'])++def make_libsvgbird(target_binary, deps):+    valac_command = config.VALAC + """\+        -C \+        --vapidir=./ \+        --basedir build/libsvgbird/ \+        """ + config.NON_NULL + """ \+        """ + config.VALACFLAGS.get("libsvgbird", "") + """ \+        --enable-experimental \+        --library libsvgbird \+        -H build/libsvgbird/svgbird.h \+        libsvgbird/*.vala \+        --pkg posix \+        --pkg """ + config.GEE + """ \+        --pkg gio-2.0 \+        --pkg cairo \+        --pkg xmlbird \+        """++    cc_command = config.CC + " " + config.CFLAGS.get("libsvgbird", "") + """ \+            -c C_SOURCE \+            -fPIC \+            -I ./build/libsvgbird \+            $(pkg-config --cflags """ + config.GEE + """) \+            $(pkg-config --cflags gio-2.0) \+            $(pkg-config --cflags cairo) \+            $(pkg-config --cflags glib-2.0) \+            $(pkg-config --cflags xmlbird) \+            -o OBJECT_FILE"""++    linker_command = config.CC + " " + config.LDFLAGS.get("libsvgbird", "") + """ \+            -shared \+            """ + soname(target_binary) + """ \+            build/libsvgbird/*.o \+            $(pkg-config --libs """ + config.GEE + """) \+            $(pkg-config --libs gio-2.0) \+            $(pkg-config --libs cairo) \+            $(pkg-config --libs glib-2.0) \+            $(pkg-config --libs xmlbird) \+            -L./build -L./build/bin \+            -o ./build/bin/""" + target_binary++    link_name = 'libsvgbird.so'+    source_directory = 'libsvgbird'+    +    libsvgbird = Builder(source_directory,+                         valac_command, +                         cc_command,+                         linker_command,+                         target_binary,+                         link_name,+                         deps)+			+    yield libsvgbird.build()++def task_libsvgbird():+    yield make_libsvgbird('libsvgbird.so.' + LIBSVGBIRD_SO_VERSION, [])      def make_libbirdgems(target_binary, deps):     valac_command = config.VALAC + """\@@ -444,12 +519,14 @@ def make_birdfont_test(target_binary, deps): 		--pkg cairo \ 		--pkg xmlbird \ 		--pkg libbirdfont \+		--pkg libsvgbird \         """      cc_command = config.CC + " " + config.CFLAGS.get("birdfont-test", "") + """ \         -c C_SOURCE \ 		-D 'GETTEXT_PACKAGE="birdfont"' \         -I./build/libbirdfont \+        -I./build/libsvgbird \ 		$(pkg-config --cflags sqlite3) \ 		$(pkg-config --cflags """ + config.GEE + """) \ 		$(pkg-config --cflags gio-2.0) \@@ -466,7 +543,7 @@ def make_birdfont_test(target_binary, deps): 		$(pkg-config --libs cairo) \ 		$(pkg-config --libs glib-2.0) \ 		$(pkg-config --libs xmlbird) \-		-L./build -L./build/bin -l birdgems\+		-L./build -L./build/bin -l birdgems -l svgbird\         -o build/bin/""" + target_binary      test = Builder('birdfont-test',diff --git a/libbirdfont/BackgroundTab.vala b/libbirdfont/BackgroundTab.valaindex c154e23..976cfb9 100644--- a/libbirdfont/BackgroundTab.vala+++ b/libbirdfont/BackgroundTab.vala@@ -14,6 +14,7 @@  using Math; using Cairo;+using SvgBird;  namespace BirdFont { diff --git a/libbirdfont/BirdFontFile.vala b/libbirdfont/BirdFontFile.valaindex ddf27b0..948a66a 100644--- a/libbirdfont/BirdFontFile.vala+++ b/libbirdfont/BirdFontFile.vala@@ -11,7 +11,9 @@ 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU  	Lesser General Public License for more details. */+ using B;+using SvgBird;  namespace BirdFont { @@ -504,7 +506,7 @@ class BirdFontFile : GLib.Object { 	void write_layer (Layer layer, DataOutputStream os) throws GLib.Error { 		os.put_string (@"\t\t<layer name= \"$(layer.name)\" visible=\"$(layer.visible)\">\n"); 		-		foreach (Object o in layer.get_all_objects ().objects) {+		foreach (SvgBird.Object o in layer.get_all_objects ().objects) { 			 			if (o is EmbeddedSvg) { 				write_embedded_svg ((EmbeddedSvg) o, os);@@ -1486,8 +1488,7 @@ class BirdFontFile : GLib.Object { 		} 		 		if (type == "svg") {-			SvgFile svg_file = new SvgFile ();-			EmbeddedSvg svg = svg_file.parse_data (tag.get_content ());+			EmbeddedSvg svg = SvgParser.parse_embedded_svg_data (tag.get_content ()); 			svg.x = x; 			svg.y = y; 			layer.add_object (svg);@@ -1511,7 +1512,7 @@ class BirdFontFile : GLib.Object { 		foreach (Tag t in tag) { 			if (t.get_name () == "path") { 				path = parse_path (t);-				layer.add_path (path);+				LayerUtils.add_path (layer, path); 			} 			 			if (t.get_name () == "embedded") {@@ -1956,6 +1957,7 @@ class BirdFontFile : GLib.Object { 		ligatures = font.get_ligatures (); 		ligatures.add_ligature (sequence, ligature); 	}+ }  }diff --git a/libbirdfont/ClipTool.vala b/libbirdfont/ClipTool.valaindex 06ce551..5f9ab14 100644--- a/libbirdfont/ClipTool.vala+++ b/libbirdfont/ClipTool.vala@@ -12,6 +12,8 @@ 	Lesser General Public License for more details. */ +using SvgBird;+ namespace BirdFont {  public class ClipTool : Tool {@@ -95,7 +97,7 @@ public class ClipTool : Tool { 			dx = g.motion_x - x - w / 2.0; 			dy = g.motion_y - y + h / 2.0; 			-			foreach (Object path in g.active_paths) {+			foreach (SvgBird.Object path in g.active_paths) { 				path.move (dx, dy); 			} 		} else if (fd is KerningDisplay) {@@ -405,9 +407,9 @@ public class ClipTool : Tool { 				string cap = p.replace ("cap: ", ""); 				 				if (cap == "round") {-					path.line_cap = LineCap.ROUND;+					path.line_cap = SvgBird.LineCap.ROUND; 				} else if (cap == "square") {-					path.line_cap = LineCap.SQUARE;+					path.line_cap = SvgBird.LineCap.SQUARE; 				} 			} 		}diff --git a/libbirdfont/Color.vala b/libbirdfont/Color.valaindex 1e8c47f..f930aa1 100644--- a/libbirdfont/Color.vala+++ b/libbirdfont/Color.vala@@ -22,24 +22,19 @@  namespace BirdFont { -public class Color {-	public double r;-	public double g;-	public double b;-	public double a;-	+public class Color : SvgBird.Color { 	public Color (double r, double g, double b, double a) {-		this.r = r;-		this.g = g;-		this.b = b;-		this.a = a;+		base (r, g, b, a); 	} +	public Color.create_copy (SvgBird.Color color) {+		base (color.r, color.g, color.b, color.a);+	}+	 	public Color.hsba (double h, double s, double v, double a) { 		double hue, saturation, value; 		double f, p, q, t;--		this.a = a;+		double r, g, b;  		if (s == 0.0) { 			r = v;@@ -100,67 +95,9 @@ public class Color { 				assert_not_reached (); 			} 		}-	}--	public static Color? parse (string? svg_color) {-		if (svg_color == null) {-			return null;-		} 		-		string color = ((!) svg_color).replace ("#", "");-		uint32 c;-		string[] arguments;-		Color parsed = black ();--		if (color == "none") {-			return null;-		}-		-		if (color.char_count () == 6) {-			color.scanf ("%x", out c);-			parsed.r = (uint8)((c & 0xFF0000) >> 16) / 254.0;-			parsed.g = (uint8)((c & 0x00FF00) >> 8)/ 254.0;-			parsed.b = (uint8)(c & 0x0000FF) / 254.0;-		} else if (color.char_count () == 3) {-			color.scanf ("%x", out c);-			parsed.r = (uint8)(((c & 0xF00) >> 4) | ((c & 0xF00) >> 8)) / 254.0;-			parsed.g = (uint8)((c & 0x0F0) | ((c & 0x0F0) >> 4)) / 254.0;-			parsed.b = (uint8)(((c & 0x00F) << 4) | (c & 0x00F)) / 254.0;-		} else if (color.index_of ("%") > -1) {-			color = color.replace ("rgb", "");-			color = color.replace (" ", "");-			color = color.replace ("\t", "");-			color = color.replace ("%", "");-			arguments = color.split (",");-			-			return_val_if_fail (arguments.length == 3, parsed);-			arguments[0].scanf ("%lf", out parsed.r);-			arguments[1].scanf ("%lf", out parsed.g);-			arguments[2].scanf ("%lf", out parsed.b);-		} else if (color.index_of ("rgb") > -1) {-			color = color.replace ("rgb", "");-			color = color.replace (" ", "");-			color = color.replace ("\t", "");-			arguments = color.split (",");-			-			return_val_if_fail (arguments.length == 3, parsed);-			-			int r, g, b;-			arguments[0].scanf ("%d", out r);-			parsed.r = r / 254.0;-			-			arguments[1].scanf ("%d", out g);-			parsed.g = g / 254.0;-			-			arguments[2].scanf ("%d", out b);-			parsed.b = b / 254.0;-		} else {-			warning ("Unknown color type: " + color);-		}-		-		-		return parsed;- 	}+		base (r, g, b, a);+	}  	public void to_hsva (out double h, out double s, out double v, out double a) { 		double red, green, blue;@@ -264,23 +201,10 @@ public class Color { 	public static Color magenta () { 		return new Color (103.0 / 255, 33.0 / 255, 120.0 / 255, 1); 	}-	-	public string to_string () {-		string alpha = Font.to_hex_code ((unichar) Math.rint (a  * 254));-		return @"$(to_rgb_hex ())" + alpha;-	}-	-	public Color copy () {+		+	public new Color copy () { 		return new Color (r, g, b, a); 	}--	public string to_rgb_hex () {-		string s = "#";-		s += Font.to_hex_code ((unichar) Math.rint (r  * 254));-		s += Font.to_hex_code ((unichar) Math.rint (g  * 254));-		s += Font.to_hex_code ((unichar) Math.rint (b  * 254));-		return s;-	} }  }diff --git a/libbirdfont/ColorPicker.vala b/libbirdfont/ColorPicker.valaindex e99493e..31188cf 100644--- a/libbirdfont/ColorPicker.vala+++ b/libbirdfont/ColorPicker.vala@@ -13,6 +13,7 @@ */  using Cairo;+using SvgBird;  namespace BirdFont { @@ -119,7 +120,7 @@ public class ColorPicker : Tool { 				int g = (int) ((tx / Toolbox.allocation_width) * gradient.stops.size); 				return_if_fail (0 <= g < gradient.stops.size); 				current_stop = gradient.stops.get (g);-				set_color (current_stop.color);+				set_color (new Color.create_copy (current_stop.color)); 			} 		} 		@@ -236,7 +237,7 @@ public class ColorPicker : Tool { 			int stop_size = (int) ((double) Toolbox.allocation_width / gradient.stops.size); 			for (int i = 0; i < gradient.stops.size; i++) { 				Stop s = gradient.stops.get (i);-				c = s.color;+				c = new Color.create_copy (s.color); 				cr.save (); 				cr.set_source_rgba (c.r, c.g, c.b, c.a); 				cr.rectangle (i * stop_size, y + 4 * bar_height, stop_size, bar_height);diff --git a/libbirdfont/Doubles.vala b/libbirdfont/Doubles.valadeleted file mode 100644index 9f158e3..0000000--- a/libbirdfont/Doubles.vala+++ /dev/null@@ -1,68 +0,0 @@-/*-	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 = 0;-	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++;-	}-	-	public double get_double (int index) {-		if (unlikely (index < 0)) {-			warning ("index < 0");-			return 0;-		}--		if (unlikely (index >= size)) {-			warning ("index >= size");-			return 0;-		}-		-		return data[index];-	}-}--}-diff --git a/libbirdfont/DrawingTools.vala b/libbirdfont/DrawingTools.valaindex 5d02755..c79c488 100644--- a/libbirdfont/DrawingTools.vala+++ b/libbirdfont/DrawingTools.vala@@ -14,6 +14,7 @@  using Cairo; using Math;+using SvgBird;  namespace BirdFont { @@ -297,7 +298,7 @@ public class DrawingTools : ToolCollection  { 			glyph.selection_boundaries (out x, out y, out w, out h); 			delta = x_coordinate.get_value () - x + glyph.left_limit; 			-			foreach (Object path in glyph.active_paths) {+			foreach (SvgBird.Object path in glyph.active_paths) { 				path.move (delta, 0); 			} 			@@ -380,7 +381,7 @@ public class DrawingTools : ToolCollection  { 			double x, y, w, h; 			Glyph glyph = MainWindow.get_current_glyph (); 			double angle = (self.get_value () / 360) * 2 * PI;-			Object last_path;+			SvgBird.Object last_path; 			glyph.selection_boundaries (out x, out y, out w, out h); 			 			x += w / 2;@@ -603,7 +604,7 @@ public class DrawingTools : ToolCollection  { 			Glyph g = MainWindow.get_current_glyph (); 			Layer layer = g.get_current_layer (); 			-			foreach (Object p in g.active_paths) {+			foreach (SvgBird.Object p in g.active_paths) { 				layer.remove (p); 				layer.objects.objects.insert (0, p); 			}@@ -803,12 +804,12 @@ public class DrawingTools : ToolCollection  { 			g.store_undo_state (); 		 			if (StrokeTool.add_stroke) {-				foreach (Object p in g.active_paths) {+				foreach (SvgBird.Object p in g.active_paths) { 					p.stroke = StrokeTool.stroke_width; 					p.line_cap = StrokeTool.line_cap; 				} 			} else {-				foreach (Object p in g.active_paths) {+				foreach (SvgBird.Object p in g.active_paths) { 					p.stroke = 0; 				}	 			}@@ -843,7 +844,7 @@ public class DrawingTools : ToolCollection  { 			StrokeTool.stroke_width = object_stroke.get_value (); 					 			if (tool && StrokeTool.add_stroke) {-				foreach (Object p in g.active_paths) {+				foreach (SvgBird.Object p in g.active_paths) { 					p.stroke = StrokeTool.stroke_width; 					 					if (p is PathObject) {@@ -885,15 +886,15 @@ public class DrawingTools : ToolCollection  { 			g = MainWindow.get_current_glyph (); 			g.store_undo_state (); 			-			foreach (Object p in g.active_paths) {-				p.line_cap = LineCap.BUTT;+			foreach (SvgBird.Object p in g.active_paths) {+				p.line_cap = SvgBird.LineCap.BUTT; 				 				if (p is PathObject) { 					((PathObject) p).get_path ().reset_stroke (); 				} 			} 			-			StrokeTool.line_cap = LineCap.BUTT;+			StrokeTool.line_cap = SvgBird.LineCap.BUTT; 			Font f = BirdFont.get_current_font (); 			f.settings.set_setting ("line_cap", @"butt"); @@ -912,15 +913,15 @@ public class DrawingTools : ToolCollection  { 			g = MainWindow.get_current_glyph (); 			g.store_undo_state (); 			-			foreach (Object p in g.active_paths) {-				p.line_cap = LineCap.ROUND;+			foreach (SvgBird.Object p in g.active_paths) {+				p.line_cap = SvgBird.LineCap.ROUND; 				 				if (p is PathObject) { 					((PathObject) p).get_path ().reset_stroke (); 				} 			} 			-			StrokeTool.line_cap = LineCap.ROUND;+			StrokeTool.line_cap = SvgBird.LineCap.ROUND;  			Font f = BirdFont.get_current_font (); 			f.settings.set_setting ("line_cap", @"round");@@ -940,15 +941,15 @@ public class DrawingTools : ToolCollection  { 			g = MainWindow.get_current_glyph (); 			g.store_undo_state (); 			-			foreach (Object p in g.active_paths) {-				p.line_cap = LineCap.SQUARE;+			foreach (SvgBird.Object p in g.active_paths) {+				p.line_cap = SvgBird.LineCap.SQUARE;  				if (p is PathObject) { 					((PathObject) p).get_path ().reset_stroke (); 				} 			} 			-			StrokeTool.line_cap = LineCap.SQUARE;+			StrokeTool.line_cap = SvgBird.LineCap.SQUARE;  			Font f = BirdFont.get_current_font (); 			f.settings.set_setting ("line_cap", @"square");@@ -1267,7 +1268,7 @@ public class DrawingTools : ToolCollection  { 		bool stroke = false; 		Glyph g = MainWindow.get_current_glyph (); 		-		foreach (Object p in g.active_paths) {+		foreach (SvgBird.Object p in g.active_paths) { 			if (p.stroke > 0) { 				stroke = true; 			}diff --git a/libbirdfont/EmbeddedSvg.vala b/libbirdfont/EmbeddedSvg.valaindex 2ae8a5c..4992d5c 100644--- a/libbirdfont/EmbeddedSvg.vala+++ b/libbirdfont/EmbeddedSvg.vala@@ -15,10 +15,11 @@ using B; using Math; using Cairo;+using SvgBird;  namespace BirdFont { -public class EmbeddedSvg : Object {+public class EmbeddedSvg : SvgBird.Object { 	public string svg_data = ""; 	public SvgDrawing drawing = new SvgDrawing (); 	@@ -86,7 +87,7 @@ public class EmbeddedSvg : Object { 		cr.restore (); 	} 	-	public override Object copy () {+	public override SvgBird.Object copy () { 		EmbeddedSvg svg = new EmbeddedSvg (drawing); 		svg.svg_data = svg_data; 		return svg;diff --git a/libbirdfont/ExportTool.vala b/libbirdfont/ExportTool.valaindex 7ace92f..00b5d14 100644--- a/libbirdfont/ExportTool.vala+++ b/libbirdfont/ExportTool.vala@@ -13,6 +13,7 @@ */  using B;+using SvgBird;  namespace BirdFont { @@ -95,14 +96,14 @@ public class ExportTool : GLib.Object { 		 		name = glyph.get_name (); 		-		Gee.ArrayList<Object> pl;+		Gee.ArrayList<SvgBird.Object> pl; 		 		s = new StringBuilder (); 		glyph_svg = "";  		pl = only_selected_paths ? glyph.active_paths : glyph.get_visible_objects (); 		-		foreach (Object o in pl) {+		foreach (SvgBird.Object o in pl) { 			 			if (o is PathObject) { 				Path p = ((PathObject) o).get_path ();@@ -131,7 +132,7 @@ public class ExportTool : GLib.Object { 		} 		 		if (only_selected_paths) {-			foreach (Object p in glyph.active_paths) {+			foreach (SvgBird.Object p in glyph.active_paths) { 				if (p is PathObject) { 					Path path = ((PathObject) p).get_path (); 					if (path.stroke == 0) {diff --git a/libbirdfont/Glyph.vala b/libbirdfont/Glyph.valaindex a1e5c61..c560033 100644--- a/libbirdfont/Glyph.vala+++ b/libbirdfont/Glyph.vala@@ -16,6 +16,7 @@ using Cairo; using Math; using Gee; using B;+using SvgBird;  namespace BirdFont { @@ -132,7 +133,7 @@ public class Glyph : FontDisplay {  	public Layer layers = new Layer (); 	public int current_layer = 0;-	public Gee.ArrayList<Object> active_paths = new Gee.ArrayList<Object> ();+	public Gee.ArrayList<SvgBird.Object> active_paths = new Gee.ArrayList<SvgBird.Object> ();  	// used if this glyph originates from a fallback font 	public double top_limit = 0;@@ -178,28 +179,28 @@ public class Glyph : FontDisplay { 		warning ("Layer is not added to glyph."); 	} -	public Gee.ArrayList<Object> get_visible_objects () {+	public Gee.ArrayList<SvgBird.Object> get_visible_objects () { 		return layers.get_visible_objects ().objects; 	}  	public Gee.ArrayList<Path> get_visible_paths () {-		return layers.get_visible_paths ().paths;+		return LayerUtils.get_visible_paths (layers).paths; 	}  	public PathList get_visible_path_list () {-		return layers.get_visible_paths ();+		return LayerUtils.get_visible_paths (layers); 	} -	public Gee.ArrayList<Object> get_objects_in_current_layer () {+	public Gee.ArrayList<SvgBird.Object> get_objects_in_current_layer () { 		return get_current_layer ().get_all_objects ().objects; 	}  	public Gee.ArrayList<Path> get_paths_in_current_layer () {-		return get_current_layer ().get_all_paths ().paths;+		return LayerUtils.get_all_paths (get_current_layer ()).paths; 	}  	public Gee.ArrayList<Path> get_all_paths () {-		return layers.get_all_paths ().paths;+		return LayerUtils.get_all_paths (layers).paths; 	}  	public void add_new_layer () {@@ -275,8 +276,8 @@ public class Glyph : FontDisplay { 	} 	 	// FIXME: delete group-	public void add_active_object (Layer? group, Object? o) {-		Object object;+	public void add_active_object (Layer? group, SvgBird.Object? o) {+		SvgBird.Object object; 		Layer g;  		if (o != null) {@@ -299,7 +300,7 @@ public class Glyph : FontDisplay { 		} 	} -	public bool active_paths_contains (Object object) {+	public bool active_paths_contains (SvgBird.Object object) { 		Glyph glyph = MainWindow.get_current_glyph ();  		if (glyph.active_paths.contains (object)) {@@ -309,7 +310,7 @@ public class Glyph : FontDisplay { 		if (object is PathObject) { 			PathObject path = (PathObject) object; 			-			foreach (Object active in glyph.active_paths) {+			foreach (SvgBird.Object active in glyph.active_paths) { 				if (active is PathObject) { 					PathObject path_active = (PathObject) active; 					if (path_active.get_path () == path.get_path ()) {@@ -377,7 +378,7 @@ public class Glyph : FontDisplay { 		px2 = -10000; 		py2 = -10000; -		foreach (Object p in active_paths) {+		foreach (SvgBird.Object p in active_paths) { 			if (p.xmin < px) { 				px = p.xmin; 			}@@ -486,10 +487,10 @@ public class Glyph : FontDisplay { 			layers.add_layer (new Layer ()); 		} -		get_current_layer ().add_path (p);+		LayerUtils.add_path (get_current_layer (), p); 	} -	public void add_object (Object object) {+	public void add_object (SvgBird.Object object) { 		if (layers.subgroups.size == 0) { 			layers.add_layer (new Layer ()); 		}@@ -785,12 +786,12 @@ public class Glyph : FontDisplay { 		} 	} -	public void delete_object (Object o) {+	public void delete_object (SvgBird.Object o) { 		layers.remove (o); 	}  	public void delete_path (Path p) {-		layers.remove_path(p);+		LayerUtils.remove_path(layers, p); 	}  	public string get_svg_data () {@@ -1199,8 +1200,8 @@ public class Glyph : FontDisplay { 		return -y; 	} -	public Object? get_object_at (double x, double y) {-		foreach (Object o in get_current_layer ().objects) {+	public SvgBird.Object? get_object_at (double x, double y) {+		foreach (SvgBird.Object o in get_current_layer ().objects) { 			if (o.is_over (x, y)) { 				return o; 			}@@ -1275,7 +1276,7 @@ public class Glyph : FontDisplay { 			return; 		} -		foreach (Object object in active_paths) {+		foreach (SvgBird.Object object in active_paths) { 			EditPoint p; 			Path path = ((PathObject) object).get_path (); 			EditPoint pl = path.get_last_point ();@@ -1581,7 +1582,7 @@ public class Glyph : FontDisplay { 		cr.save (); 		cr.new_path (); 		-		foreach (Object o in get_visible_objects ()) {+		foreach (SvgBird.Object o in get_visible_objects ()) { 			if (o is PathObject) { 				((PathObject) o).draw_path (cr, c); 			} else {@@ -1598,7 +1599,7 @@ public class Glyph : FontDisplay { 		Color color;  		// FIXME: layer transforms-		foreach (Object o in get_visible_objects ()) {+		foreach (SvgBird.Object o in get_visible_objects ()) { 			if (!(o is PathObject)) { 				o.draw (cr); 			}@@ -1629,12 +1630,12 @@ public class Glyph : FontDisplay { 			&& !(MainWindow.get_toolbox ().get_current_tool () is BezierTool)) { 			cr.save (); 			cr.new_path ();-			foreach (Object o in active_paths) {+			foreach (SvgBird.Object o in active_paths) { 				if (o is PathObject) { 					Path p = ((PathObject) o).get_path (); 					if (p.stroke > 0) { 						stroke = p.get_stroke_fast ();-						color = Theme.get_color ("Selected Objects");+						color = Theme.get_color ("Selected SvgBird.Objects"); 						PathObject.draw_path_list (stroke, cr, color); 					} 				}@@ -1674,7 +1675,7 @@ public class Glyph : FontDisplay { 			cr.new_path (); 			 			// FIXME: layer transforms-			foreach (Object o in get_visible_objects ()) {+			foreach (SvgBird.Object o in get_visible_objects ()) { 				if (o is PathObject) { 					Path p = ((PathObject) o).get_path (); @@ -1689,7 +1690,7 @@ public class Glyph : FontDisplay { 			cr.fill (); 			cr.restore (); -			foreach (Object o in active_paths) {+			foreach (SvgBird.Object o in active_paths) { 				if (o is PathObject) { 					Path p = ((PathObject) o).get_path (); 					cr.save ();@@ -1758,7 +1759,7 @@ public class Glyph : FontDisplay { 		}  		if (unlikely (Preferences.draw_boundaries)) {-			foreach (Object o in get_visible_objects ()) {+			foreach (SvgBird.Object o in get_visible_objects ()) { 				draw_boundaries (o, cmp); 			} 		}@@ -1859,7 +1860,7 @@ public class Glyph : FontDisplay {  		g.layers = layers.copy (); -		foreach (Object o in active_paths) {+		foreach (SvgBird.Object o in active_paths) { 			g.active_paths.add (o); 		} @@ -1947,7 +1948,7 @@ public class Glyph : FontDisplay { 		}  		clear_active_paths ();-		foreach (Object p in g.active_paths) {+		foreach (SvgBird.Object p in g.active_paths) { 			add_active_object (null, p); 		} @@ -2442,11 +2443,11 @@ public class Glyph : FontDisplay { 		return g2; 	} 	-	// FIXME: convert everything to the new Object code+	// FIXME: convert everything to the new SvgBird.Object code 	public Gee.ArrayList<Path> get_active_paths () { 		Gee.ArrayList<Path> paths = new Gee.ArrayList<Path> (); 		-		foreach (Object object in active_paths) {+		foreach (SvgBird.Object object in active_paths) { 			if (object is PathObject) { 				paths.add (((PathObject) object).get_path ()); 			}@@ -2455,7 +2456,7 @@ public class Glyph : FontDisplay { 		return paths; 	} 	-	public void draw_boundaries  (Object object, Context cr) {+	public void draw_boundaries  (SvgBird.Object object, Context cr) { 		double x = Glyph.reverse_path_coordinate_x (object.xmin);  		double y = Glyph.reverse_path_coordinate_y (object.ymin); 		double x2 = Glyph.reverse_path_coordinate_x (object.xmax);diff --git a/libbirdfont/Layer.vala b/libbirdfont/Layer.valadeleted file mode 100644index 91385f8..0000000--- a/libbirdfont/Layer.vala+++ /dev/null@@ -1,264 +0,0 @@-/*-	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.-*/--namespace BirdFont {--public class Layer : GLib.Object {-	public ObjectGroup objects;-	-	public Gee.ArrayList<Layer> subgroups;-	public bool visible = true;-	public string name = "Layer";-	-	public bool is_counter = false;-	public Gradient? gradient = null;-	public bool single_path = false;-	-	public SvgTransforms transforms;-	-	public Layer () {-		objects = new ObjectGroup ();-		subgroups = new Gee.ArrayList<Layer> ();-		transforms = new SvgTransforms ();-	}--	public int index_of (Layer sublayer) {-		return subgroups.index_of (sublayer);-	}--	public ObjectGroup get_all_objects () {-		ObjectGroup o = new ObjectGroup ();-		-		o.append (objects);-		-		foreach (Layer sublayer in subgroups) {-			o.append (sublayer.get_all_objects ());-		}-		-		return o;-	}-		-	public PathList get_all_paths () {-		PathList paths = new PathList ();-		-		foreach (Object o in objects) {-			if (o is PathObject) {-				PathObject p = (PathObject) o;-				paths.add (p.get_path ());-			}-		}-		-		foreach (Layer sublayer in subgroups) {-			paths.append (sublayer.get_all_paths ());-		}-		-		return paths;-	}--	public ObjectGroup get_visible_objects () {-		ObjectGroup object_group = new ObjectGroup ();-		-		if (visible) {-			foreach (Object o in objects) {-				object_group.add (o);-			}-		}-		-		foreach (Layer sublayer in subgroups) {-			if (sublayer.visible) {-				object_group.append (sublayer.get_visible_objects ());-			}-		}-		-		return object_group;		-	}--	public PathList get_visible_paths () {-		PathList paths = new PathList ();-		-		if (visible) {-			foreach (Object o in objects) {-				if (o is PathObject) {-					PathObject p = (PathObject) o;-					paths.add (p.get_path ());-				}-			}-		}-		-		foreach (Layer sublayer in subgroups) {-			if (sublayer.visible) {-				paths.append (sublayer.get_visible_paths ());-			}-		}-		-		return paths;-	}-		-	public void add_layer (Layer layer) {-		subgroups.add (layer);-	}--	public void add_path (Path path) {-		PathObject p = new PathObject.for_path (path);-		objects.add (p);-	}--	public void add_object (Object object) {-		objects.add (object);-	}--	public void append_paths (PathList path_list) {-		foreach (Path p in path_list.paths) {-			add_path (p);-		}-	}--	private PathObject? get_fast_path (Path path) {-		foreach (Object o in objects) {-			if (o is PathObject) {-				PathObject p = (PathObject) o;-				if (p.get_path () == path) {-					return p;-				}-			}-		}-		-		return null;-	}-	-	public void remove_path (Path path) {-		PathObject? p = get_fast_path (path);-		-		if (p != null) {-			objects.remove ((!) p);-		}--		foreach (Layer sublayer in subgroups) {-			sublayer.remove_path (path);-		}-	}--	public void remove (Object o) {-		objects.remove (o);-	}-	-	public void remove_layer (Layer layer) {-		subgroups.remove (layer);-		-		foreach (Layer sublayer in subgroups) {-			sublayer.remove_layer (layer);-		}-	}-		-	public Layer copy () {-		Layer layer = new Layer ();-		-		layer.name = name;-		layer.objects = objects.copy ();-		layer.visible = visible;-		-		foreach (Layer l in subgroups) {-			layer.subgroups.add (l.copy ());-		}--		if (gradient != null) {-			layer.gradient = ((!) gradient).copy ();-		}-		-		layer.single_path = single_path;-			-		return layer;-	}--	public void get_boundaries (out double x, out double y, out double w, out double h) {-		double px, py, px2, py2;-		-		px = Glyph.CANVAS_MAX;-		py = Glyph.CANVAS_MAX;-		px2 = Glyph.CANVAS_MIN;-		py2 = Glyph.CANVAS_MIN;-		-		foreach (Object p in get_all_objects ().objects) {-			if (px > p.xmin) {-				px = p.xmin;-			} --			if (py > p.ymin) {-				py = p.ymin;-			}--			if (px2 < p.xmax) {-				px2 = p.xmax;-			}-			-			if (py2 < p.ymax) {-				py2 = p.ymax;-			}-		}-		-		w = px2 - px;-		h = py2 - py;-		x = px;-		y = py2;-	}-		-	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 (@"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");-		}-		-		foreach (Layer l in subgroups) {-			for (int i = 0; i < indent; i++) {-				stdout.printf ("\t");-			}-			stdout.printf ("%s\n", l.name);-			l.print (indent + 1);-		}-	}--	public PathList get_paths_in_layer () {-		PathList paths = new PathList ();-		-		foreach (Object object in objects) {-			if (object is PathObject) {-				paths.add (((PathObject) object).get_path ());-			}-		}-		-		return paths;-	}-}--}diff --git a/libbirdfont/LayerLabel.vala b/libbirdfont/LayerLabel.valaindex a6dbfba..80e87c0 100644--- a/libbirdfont/LayerLabel.vala+++ b/libbirdfont/LayerLabel.vala@@ -13,6 +13,7 @@ */  using Cairo;+using SvgBird;  namespace BirdFont { diff --git a/libbirdfont/LayerUtils.vala b/libbirdfont/LayerUtils.valanew file mode 100644index 0000000..5035655--- /dev/null+++ b/libbirdfont/LayerUtils.vala@@ -0,0 +1,98 @@+/*+	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 SvgBird;++namespace BirdFont {++public class LayerUtils {+	+	public static PathList get_all_paths (Layer layer) {+		ObjectGroup objects = layer.get_all_objects ();+		PathList paths = new PathList ();+		+		foreach (SvgBird.Object o in objects) {+			if (o is PathObject) {+				PathObject p = (PathObject) o;+				paths.add (p.get_path ());+			}+		}+		+		return paths;+	}++	public static PathList get_visible_paths (Layer layer) {+		ObjectGroup objects = layer.get_visible_objects ();+		PathList paths = new PathList ();+		+		foreach (SvgBird.Object o in objects) {+			if (o is PathObject) {+				PathObject p = (PathObject) o;+				paths.add (p.get_path ());+			}+		}+		+		return paths;+	}+	+	public static void add_path (Layer layer, Path path) {+		PathObject p = new PathObject.for_path (path);+		layer.add_object (p);+	}++	public static void append_paths (Layer layer, PathList path_list) {+		foreach (Path p in path_list.paths) {+			add_path (layer, p);+		}+	}++	private static PathObject? get_object_path (Layer layer, Path path) {+		foreach (SvgBird.Object o in layer.objects) {+			if (o is PathObject) {+				PathObject p = (PathObject) o;+				if (p.get_path () == path) {+					return p;+				}+			}+		}+		+		return null;+	}+	+	public static void remove_path (Layer layer, Path path) {+		PathObject? p = get_object_path (layer, path);+		+		if (p != null) {+			layer.objects.remove ((!) p);+		}++		foreach (SvgBird.Layer sublayer in layer.subgroups) {+			remove_path (sublayer, path);+		}+	}++	public static PathList get_paths_in_layer (Layer layer) {+		PathList paths = new PathList ();+		+		foreach (SvgBird.Object object in layer.objects) {+			if (object is PathObject) {+				paths.add (((PathObject) object).get_path ());+			}+		}+		+		return paths;+	}+}++}diff --git a/libbirdfont/MenuTab.vala b/libbirdfont/MenuTab.valaindex 63963d4..6283739 100644--- a/libbirdfont/MenuTab.vala+++ b/libbirdfont/MenuTab.vala@@ -12,6 +12,8 @@ 	Lesser General Public License for more details. */ +using SvgBird;+ namespace BirdFont {  public class MenuTab : FontDisplay {@@ -275,6 +277,7 @@ public class MenuTab : FontDisplay { 		 		DrawingTools.set_stroke_tool_visibility (); + 		string lock_grid = f.settings.get_setting ("lock_grid"); 		bool lg = bool.parse (lock_grid);		 		GridTool.lock_grid = lg;@@ -661,12 +664,12 @@ public class MenuTab : FontDisplay { 		 		g.store_undo_state (); 		-		foreach (Object o in g.active_paths) {+		foreach (SvgBird.Object o in g.active_paths) { 			g.layers.remove (o); 		}  		foreach (Path p in g.get_active_paths ()) {-			g.layers.remove_path (p);+			LayerUtils.remove_path (g.layers, p); 		} 				 		foreach (Path p in paths) {diff --git a/libbirdfont/MoveTool.vala b/libbirdfont/MoveTool.valaindex 1f3b2fd..76c5f00 100644--- a/libbirdfont/MoveTool.vala+++ b/libbirdfont/MoveTool.vala@@ -14,6 +14,7 @@  using Math; using Cairo;+using SvgBird;  namespace BirdFont { @@ -94,9 +95,9 @@ public class MoveTool : Tool { 				g.store_undo_state (); 			} 			-			foreach (Object p in g.active_paths) {+			foreach (SvgBird.Object p in g.active_paths) { 				if (p is PathObject) {-					g.layers.remove_path (((PathObject) p).get_path ());+					LayerUtils.remove_path (g.layers, ((PathObject) p).get_path ()); 				} else { 					g.layers.remove (p); 				}@@ -128,7 +129,7 @@ public class MoveTool : Tool { 			delta_x = -dx; 			delta_y = -dy; 			-			foreach (Object object in glyph.active_paths) {+			foreach (SvgBird.Object object in glyph.active_paths) { 				object.move (delta_x, delta_y); 			} 		}@@ -156,7 +157,7 @@ public class MoveTool : Tool { 		if (GridTool.is_visible () && moved) { 			tie_paths_to_grid (glyph); 		} else if (GridTool.has_ttf_grid ()) {-			foreach (Object p in glyph.active_paths) {+			foreach (SvgBird.Object p in glyph.active_paths) { 				tie_path_to_ttf_grid (p); 			} 		} @@ -173,7 +174,7 @@ public class MoveTool : Tool { 			objects_moved (); 			DrawingTools.resize_tool.signal_objects_rotated (); 			-			foreach (Object o in glyph.active_paths) {+			foreach (SvgBird.Object o in glyph.active_paths) { 				if (o is PathObject) { 					PathObject path = (PathObject) o; 					path.get_path ().create_full_stroke ();@@ -186,9 +187,9 @@ public class MoveTool : Tool { 		 	public void press (int b, int x, int y) { 		Glyph glyph = MainWindow.get_current_glyph ();-		Object object;+		SvgBird.Object object; 		bool selected = false;-		Object? o;+		SvgBird.Object? o; 		 		glyph.store_undo_state ();	 		double px = Glyph.path_coordinate_x (x);@@ -239,7 +240,7 @@ public class MoveTool : Tool { 		 		glyph.clear_active_paths (); 		-		foreach (Object p in glyph.get_objects_in_current_layer ()) {+		foreach (SvgBird.Object p in glyph.get_objects_in_current_layer ()) { 			if (p.xmin > x1 && p.xmax < x2 && p.ymin < y1 && p.ymax > y2) { 				if (!p.is_empty ()) { 					glyph.add_active_object (null, p);@@ -263,7 +264,7 @@ public class MoveTool : Tool { 		 		get_selection_box_boundaries (out x, out y, out w, out h); 		-		foreach (Object path in glyph.active_paths) {+		foreach (SvgBird.Object path in glyph.active_paths) { 			path.move (glyph.left_limit - x + w / 2, font.base_line - y + h / 2); 		} 		@@ -281,7 +282,7 @@ public class MoveTool : Tool { 		px2 = -10000; 		py2 = -10000; 		-		foreach (Object o in glyph.active_paths) {+		foreach (SvgBird.Object o in glyph.active_paths) { 			if (o is PathObject) { 				Path p = ((PathObject) o).get_path (); 				p.update_region_boundaries ();@@ -334,7 +335,7 @@ public class MoveTool : Tool { 				break; 		} 		-		foreach (Object path in glyph.active_paths) {+		foreach (SvgBird.Object path in glyph.active_paths) { 			path.move (x * Glyph.ivz (), y * Glyph.ivz ()); 		} 		@@ -345,7 +346,7 @@ public class MoveTool : Tool { 		glyph.redraw_area (0, 0, glyph.allocation.width, glyph.allocation.height); 	} -	static void tie_path_to_ttf_grid (Object p) {+	static void tie_path_to_ttf_grid (SvgBird.Object p) { 		double sx, sy, qx, qy;	  		sx = p.xmax;@@ -395,7 +396,7 @@ public class MoveTool : Tool { 		dx_min = Math.fabs (qx - minx); 		dx_max = Math.fabs (sx - maxx); 		-		foreach (Object p in g.active_paths) {+		foreach (SvgBird.Object p in g.active_paths) { 			if (dy_min < dy_max) { 				p.move (0, qy - miny); 			} else {@@ -414,7 +415,7 @@ public class MoveTool : Tool { 	 	public static void update_boundaries_for_selection () { 		Glyph glyph = MainWindow.get_current_glyph ();-		foreach (Object o in glyph.active_paths) {+		foreach (SvgBird.Object o in glyph.active_paths) { 			if (o is PathObject) { 				((PathObject)o).get_path ().update_region_boundaries (); 			}@@ -439,7 +440,7 @@ public class MoveTool : Tool { 		xc = selection_box_center_x; 		yc = selection_box_center_y; -		foreach (Object p in glyph.active_paths) {+		foreach (SvgBird.Object p in glyph.active_paths) { 			if (p is PathObject) { 				Path path = ((PathObject) p).get_path (); 				@@ -459,7 +460,7 @@ public class MoveTool : Tool { 		dx = -(xc2 - xc); 		dy = -(yc2 - yc); 		-		foreach (Object p in glyph.active_paths) {+		foreach (SvgBird.Object p in glyph.active_paths) { 			p.move (dx, dy); 		} 		@@ -473,7 +474,7 @@ public class MoveTool : Tool { 		Glyph g = MainWindow.get_current_glyph (); 		 		g.clear_active_paths ();-		foreach (Object p in g.get_objects_in_current_layer ()) {+		foreach (SvgBird.Object p in g.get_objects_in_current_layer ()) { 			if (!p.is_empty ()) { 				g.add_active_object (null, p); 			}diff --git a/libbirdfont/ObjectGroup.vala b/libbirdfont/ObjectGroup.valadeleted file mode 100644index b2a9ca3..0000000--- a/libbirdfont/ObjectGroup.vala+++ /dev/null@@ -1,63 +0,0 @@-/*-	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.-*/--namespace BirdFont {--public class ObjectGroup : GLib.Object {-	public Gee.ArrayList<Object> objects;-	-	public int size { -		get {-			return objects.size;-		}-	}-	-	public ObjectGroup () {-		 objects = new Gee.ArrayList<Object> ();-	}--	public Gee.Iterator<Object> iterator () {-		return objects.iterator ();-	}--	public void remove (Object p) {-		objects.remove (p);-	}-	-	public void add (Object p) {-		objects.add (p);-	}-	-	public void clear () {-		objects.clear ();-	}--	public void append (ObjectGroup group) {-		foreach (Object o in group.objects) {-			objects.add (o);-		}-	}-	-	public ObjectGroup copy () {-		ObjectGroup objects_copy = new ObjectGroup ();-		-		foreach (Object o in objects) {-			objects_copy.add (o.copy ());-		}-		-		return objects_copy;-	}-}--}diff --git a/libbirdfont/OverView.vala b/libbirdfont/OverView.valaindex 61e1cfe..ce453dd 100644--- a/libbirdfont/OverView.vala+++ b/libbirdfont/OverView.vala@@ -13,6 +13,7 @@ */  using Cairo;+using SvgBird;  namespace BirdFont { diff --git a/libbirdfont/Path.vala b/libbirdfont/Path.valaindex 488f395..606f4f8 100644--- a/libbirdfont/Path.vala+++ b/libbirdfont/Path.vala@@ -14,6 +14,7 @@  using Cairo; using Math;+using SvgBird;  namespace BirdFont { @@ -67,7 +68,7 @@ public class Path : GLib.Object { 	 	private double path_stroke_width = 0; 	-	public LineCap line_cap = LineCap.BUTT;+	public SvgBird.LineCap line_cap = SvgBird.LineCap.BUTT; 	public PathList? full_stroke = null; 	PathList? fast_stroke = null; 	StrokeTask? stroke_creator;diff --git a/libbirdfont/PathList.vala b/libbirdfont/PathList.valaindex 84bf7b4..a14e2c2 100644--- a/libbirdfont/PathList.vala+++ b/libbirdfont/PathList.vala@@ -12,6 +12,8 @@ 	Lesser General Public License for more details. */ +using SvgBird;+ namespace BirdFont {  public class PathList : GLib.Object {@@ -78,6 +80,20 @@ public class PathList : GLib.Object { 		 		return pl; 	}++	public void apply_style (SvgStyle style) {+		foreach (Path p in paths) {+			if (style.has_stroke ()) {+				p.stroke = style.get_stroke_width ();+			} else {+				p.stroke = 0;+			}+			+			p.line_cap = style.get_line_cap ();+			p.reset_stroke ();+			p.update_region_boundaries ();		+		}+	} }  }diff --git a/libbirdfont/PathObject.vala b/libbirdfont/PathObject.valaindex b31e334..ba836a0 100644--- a/libbirdfont/PathObject.vala+++ b/libbirdfont/PathObject.vala@@ -13,83 +13,15 @@ */  using Cairo;+using SvgBird;  namespace BirdFont { -public class PathObject : Object {+public class PathObject : SvgBird.Object {  	Path path;-	-	public override double stroke {-		get {-			return path.stroke;-		}--		set {-			path.stroke = value;-		}-	}--	public override double rotation {-		get {-			return path.rotation;-		}--		set {-			path.rotation = value;-		}-	}--	public override LineCap line_cap {-		get {-			return path.line_cap;-		}--		set {-			path.line_cap = value;-		}-	}--	public override bool fill {-		get {-			return path.fill;-		}--		set {-			path.fill = value;-		}-	}--	public override Color? color {-		get {-			return path.color;-		}--		set {-			path.color = value;-		}-	}--	public override Color? stroke_color {-		get {-			return path.stroke_color;-		}--		set {-			path.stroke_color = value;-		}-	}--	public override Gradient? gradient {-		get {-			return path.gradient;-		}--		set {-			path.gradient = value;-		}-	} +	// FIXME: flip y axis 	public override double xmin { 		get { 			return path.xmin;@@ -170,7 +102,7 @@ public class PathObject : Object { 		if (c != null) { 			path_color = (!) c; 		} else if (color != null) {-			path_color = (!) color;+			path_color = new Color.create_copy ((!) color); 		} else { 			path_color = Color.black (); 		}@@ -228,7 +160,7 @@ public class PathObject : Object { 		path.reset_stroke (); 	} 	-	public override Object copy () {+	public override SvgBird.Object copy () { 		return new PathObject.create_copy (this); 	} diff --git a/libbirdfont/PenTool.vala b/libbirdfont/PenTool.valaindex ba993e3..40e1709 100644--- a/libbirdfont/PenTool.vala+++ b/libbirdfont/PenTool.vala@@ -1058,7 +1058,7 @@ public class PenTool : Tool { 				 				// don't use set point to reflective to on open ends 				reflective = true;-				foreach (Object path in MainWindow.get_current_glyph ().active_paths) {+				foreach (SvgBird.Object path in MainWindow.get_current_glyph ().active_paths) { 					if (path.is_open () && !path.is_empty () && path is PathObject) { 						Path p = ((PathObject) path).get_path (); 						if (selected_handle.parent == p.get_first_point ()	@@ -1789,7 +1789,7 @@ public class PenTool : Tool { 		active_edit_point = new_point.point; 		 		return_val_if_fail (glyph.active_paths.size > 0, new PointSelection.empty ());-		Object object = glyph.active_paths.get (glyph.active_paths.size - 1);+		SvgBird.Object object = glyph.active_paths.get (glyph.active_paths.size - 1); 		 		if (object is PathObject) {		 			Path path = ((PathObject) object).get_path ();diff --git a/libbirdfont/ResizeTool.vala b/libbirdfont/ResizeTool.valaindex d43b78d..e685152 100644--- a/libbirdfont/ResizeTool.vala+++ b/libbirdfont/ResizeTool.vala@@ -14,6 +14,7 @@  using Math; using Cairo;+using SvgBird;  namespace BirdFont { @@ -21,7 +22,7 @@ public class ResizeTool : Tool { 	bool resize_path_proportional = false; 	bool resize_width = false; -	Object? resized_path = null;+	SvgBird.Object? resized_path = null; 	double last_resize_y; 	double last_resize_x; @@ -63,13 +64,13 @@ public class ResizeTool : Tool { 		}); 				 		press_action.connect((self, b, x, y) => {-			Object last_path;+			SvgBird.Object last_path; 			Glyph glyph; 			 			glyph = MainWindow.get_current_glyph (); 			glyph.store_undo_state (); 			-			foreach (Object p in glyph.active_paths) {+			foreach (SvgBird.Object p in glyph.active_paths) { 				if (is_over_resize_handle (p, x, y)) { 					resize_path_proportional = true; 					resized_path = p;@@ -87,7 +88,7 @@ public class ResizeTool : Tool { 				} 			} -			foreach (Object p in glyph.active_paths) {+			foreach (SvgBird.Object p in glyph.active_paths) { 				if (is_over_rotate_handle (p, x, y)) { 					rotate_path = true; 					return;@@ -121,7 +122,7 @@ public class ResizeTool : Tool { 			update_selection_box (); 			GlyphCanvas.redraw (); 			-			foreach (Object p in MainWindow.get_current_glyph ().active_paths) {+			foreach (SvgBird.Object p in MainWindow.get_current_glyph ().active_paths) { 				if (p is PathObject) { 					PathObject path = (PathObject) p; 					path.get_path ().create_full_stroke ();@@ -233,9 +234,9 @@ public class ResizeTool : Tool { 	public void rotate_selected_paths (double angle, double cx, double cy) { 		Glyph glyph = MainWindow.get_current_glyph ();   		double dx, dy, xc2, yc2, w, h;-		Object last_path;+		SvgBird.Object last_path; 		-		foreach (Object p in glyph.active_paths) {+		foreach (SvgBird.Object p in glyph.active_paths) { 			p.rotate (angle, cx, cy); 		} @@ -244,7 +245,7 @@ public class ResizeTool : Tool { 		dx = -(xc2 - cx); 		dy = -(yc2 - cy); 		-		foreach (Object p in glyph.active_paths) {+		foreach (SvgBird.Object p in glyph.active_paths) { 			p.move (dx, dy); 		} 		@@ -286,7 +287,7 @@ public class ResizeTool : Tool { 		rotate_selected_paths (rotation - last_rotate, selection_box_center_x, selection_box_center_y); 	} -	static bool is_over_rotate_handle (Object p, double x, double y) {+	static bool is_over_rotate_handle (SvgBird.Object p, double x, double y) { 		double cx, cy, hx, hy; 		double size = 10; 		bool inx, iny;@@ -363,7 +364,7 @@ public class ResizeTool : Tool { 		if (!selected) { 			glyph.clear_active_paths (); 			-			foreach (Object path in glyph.get_visible_objects ()) {+			foreach (SvgBird.Object path in glyph.get_visible_objects ()) { 				glyph.add_active_object (null, path); 			} 		}@@ -371,7 +372,7 @@ public class ResizeTool : Tool { 		get_selection_min (out resize_pos_x, out resize_pos_y); 		 		// resize paths-		foreach (Object selected_path in glyph.active_paths) {+		foreach (SvgBird.Object selected_path in glyph.active_paths) { 			selected_path.resize (ratio_x, ratio_y); 		} 		@@ -380,7 +381,7 @@ public class ResizeTool : Tool { 		dx = resize_pos_x - selection_minx; 		dy = resize_pos_y - selection_miny; 		-		foreach (Object selected_path in glyph.active_paths) {+		foreach (SvgBird.Object selected_path in glyph.active_paths) { 			selected_path.move (dx, dy);			 		} 		@@ -468,7 +469,7 @@ public class ResizeTool : Tool { 		 		DrawingTools.move_tool.move_to_baseline (); -		foreach (Object path in glyph.active_paths) {+		foreach (SvgBird.Object path in glyph.active_paths) { 			path.move (0, -descender * scale); 		} 		@@ -479,7 +480,7 @@ public class ResizeTool : Tool { 		Glyph glyph = MainWindow.get_current_glyph (); 		x = double.MAX; 		y = double.MAX;-		foreach (Object p in glyph.active_paths) {+		foreach (SvgBird.Object p in glyph.active_paths) { 			if (p.xmin < x) { 				x = p.xmin; 			}@@ -495,7 +496,7 @@ public class ResizeTool : Tool { 		double h, w; 		double ratio = get_resize_ratio (x, y); 		-		foreach (Object selected_path in glyph.active_paths) {+		foreach (SvgBird.Object selected_path in glyph.active_paths) { 			h = selected_path.ymax - selected_path.ymin; 			w = selected_path.xmax - selected_path.xmin; 			@@ -511,13 +512,13 @@ public class ResizeTool : Tool { 		return true; 	} -	bool is_over_resize_handle (Object p, double x, double y) {+	bool is_over_resize_handle (SvgBird.Object p, double x, double y) { 		double handle_x, handle_y; 		get_resize_handle_position (out handle_x, out handle_y); 		return Path.distance (handle_x, x, handle_y, y) < 12 * MainWindow.units; 	} -	bool is_over_horizontal_resize_handle (Object p, double x, double y) {+	bool is_over_horizontal_resize_handle (SvgBird.Object p, double x, double y) { 		double handle_x, handle_y; 		get_horizontal_reseize_handle_position (out handle_x, out handle_y); 		return Path.distance (handle_x, x, handle_y, y) < 12 * MainWindow.units;		@@ -538,14 +539,14 @@ public class ResizeTool : Tool { 		if (!selected_paths) { 			glyph.clear_active_paths (); 			-			foreach (Object path in glyph.get_visible_objects ()) {+			foreach (SvgBird.Object path in glyph.get_visible_objects ()) { 				glyph.add_active_object (null, path); 			} 		}  		glyph.selection_boundaries (out x, out y, out w, out h); 	-		foreach (Object path in glyph.active_paths) {+		foreach (SvgBird.Object path in glyph.active_paths) { 			if (path is PathObject) { // FIXME: other objects 				Path p = ((PathObject) path).get_path (); 				SvgParser.apply_matrix (p, 1, 0, s, 1, 0, 0);@@ -558,7 +559,7 @@ public class ResizeTool : Tool { 		 		dx = -(nx - x); 		-		foreach (Object p in glyph.active_paths) {+		foreach (SvgBird.Object p in glyph.active_paths) { 			p.move (dx, 0); 		} 		diff --git a/libbirdfont/StrokeTool.vala b/libbirdfont/StrokeTool.valaindex fc51550..a019db6 100644--- a/libbirdfont/StrokeTool.vala+++ b/libbirdfont/StrokeTool.vala@@ -14,15 +14,10 @@  using Cairo; using Math;+using SvgBird;  namespace BirdFont { -public enum LineCap {-	BUTT,-	SQUARE,-	ROUND-}- public class StrokeTool : GLib.Object { 	 	public static double stroke_width = 0;@@ -31,7 +26,7 @@ public class StrokeTool : GLib.Object { 	public static bool show_stroke_tools = false; 	public static bool convert_stroke = false; 	-	public static LineCap line_cap = LineCap.BUTT;+	public static SvgBird.LineCap line_cap = SvgBird.LineCap.BUTT; 	 	StrokeTask task; @@ -51,7 +46,7 @@ public class StrokeTool : GLib.Object { 		convert_stroke = true;	 		g.store_undo_state (); 		-		foreach (Object o in g.active_paths) {+		foreach (SvgBird.Object o in g.active_paths) { 			if (o is PathObject) { 				PathObject path = (PathObject) o; 				Path p = path.get_path ();@@ -63,7 +58,7 @@ public class StrokeTool : GLib.Object { 		}  		if (paths.paths.size > 0) {-			foreach (Object o in g.active_paths) {+			foreach (SvgBird.Object o in g.active_paths) { 				g.layers.remove (o); 			} 			@@ -139,7 +134,7 @@ public class StrokeTool : GLib.Object { 		 		g.store_undo_state (); -		foreach (Object object in g.active_paths) {+		foreach (SvgBird.Object object in g.active_paths) { 			if (object is PathObject) { 				Path p = ((PathObject) object).get_path (); 				@@ -226,7 +221,7 @@ public class StrokeTool : GLib.Object { 			return; 		} 				-		foreach (Object object in g.active_paths) {+		foreach (SvgBird.Object object in g.active_paths) { 			g.layers.remove (object); 		} 		@@ -1151,9 +1146,9 @@ public class StrokeTool : GLib.Object { 	}  	void add_line_cap (Path path, Path stroke1, Path stroke2, bool last_cap) {-		if (path.line_cap == LineCap.SQUARE) {+		if (path.line_cap == SvgBird.LineCap.SQUARE) { 			add_square_cap (path, stroke1, stroke2, last_cap);-		} else if (path.line_cap == LineCap.ROUND) {+		} else if (path.line_cap == SvgBird.LineCap.ROUND) { 			add_round_cap (path, stroke1, stroke2, last_cap); 		} 	}diff --git a/libbirdfont/Svg.vala b/libbirdfont/Svg.valanew file mode 100644index 0000000..82eaf9a--- /dev/null+++ b/libbirdfont/Svg.vala@@ -0,0 +1,355 @@+/*+	Copyright (C) 2012 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 {++// FIXME: substrings++public class Svg {++	/** Export to svg glyph data. */+	public static string to_svg_glyph (Glyph g) {	+		StringBuilder svg = new StringBuilder ();+		PathList stroke_list;+		+		foreach (Path p in g.get_visible_paths ()) {+			if (p.stroke == 0) {+				write_path_as_glyph (p, svg, g);+			} else {+				stroke_list = p.get_completed_stroke ();+				write_paths_as_glyph (stroke_list, svg, g);+			}+		}+		+		return svg.str;+	}++	/** Export to svg-font data. */+	public static string to_svg_path (Path pl, Glyph g) {	+		StringBuilder svg = new StringBuilder ();+		pl.create_list ();+		write_path (pl, svg, g, false);+		return svg.str;+	}++	private static void write_paths_as_glyph (PathList pl, StringBuilder svg, Glyph g) {+		foreach (Path p in pl.paths) {+			write_path_as_glyph (p, svg, g);+		}+	}++	private static void write_path_as_glyph (Path pl, StringBuilder svg, Glyph g) {+		write_path (pl, svg, g, true);+	}++	private static void write_path (Path p, StringBuilder svg, Glyph g, bool do_glyph) {+		int i = 0;+		EditPoint? n = null;+		EditPoint m;+		+		if (p.points.size < 2) {+			return;+		}++		p.create_list ();+			+		foreach (var e in p.points) {+			if (i == 0) {+				add_abs_start (e, svg, g, do_glyph);+				i++;+				n = e;+				continue;+			}+			+			m = (!) n;++			add_abs_next (m, e, svg, g, do_glyph);+			+			n = e;+			i++;+		}++		if (!p.is_open ()) {+			m = p.points.get (0);	+			add_abs_next ((!) n, m, svg, g, do_glyph);+			close_path (svg);+		}+	}++	private static void add_abs_next (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool do_glyph) {+		if (start.right_handle.type == PointType.LINE_QUADRATIC) {+			add_abs_line_to (start, end, svg, g, do_glyph);+		} else if (start.right_handle.type == PointType.LINE_CUBIC && end.left_handle.type == PointType.LINE_CUBIC) {+			add_abs_line_to (start, end, svg, g, do_glyph);+		} else if (end.left_handle.type == PointType.QUADRATIC || start.right_handle.type == PointType.QUADRATIC) {+			add_quadratic_abs_path (start, end, svg, g, do_glyph);+		} else if (end.left_handle.type == PointType.DOUBLE_CURVE || start.right_handle.type == PointType.DOUBLE_CURVE) {+			add_double_quadratic_abs_path (start, end, svg, g, do_glyph);+		} else {+			add_cubic_abs_path (start, end, svg, g, do_glyph);+		}+	}++	private static void add_abs_start (EditPoint ep, StringBuilder svg, Glyph g, bool to_glyph) {		+		double left = g.left_limit;+		double baseline = -BirdFont.get_current_font ().base_line;+		double height = g.get_height ();+		+		svg.append_printf ("M");+		+		if (!to_glyph) {+			svg.append_printf ("%s ",  round (ep.x - left));+			svg.append_printf ("%s ",  round (-ep.y + height / 2));+		} else {+			svg.append_printf ("%s ",  round (ep.x - left));+			svg.append_printf ("%s ",  round (ep.y + baseline));+		}+	}+		+	private static void close_path (StringBuilder svg) {+		svg.append ("z");+	}	+	+	private static void add_abs_line_to (EditPoint start, EditPoint stop, StringBuilder svg, Glyph g, bool to_glyph) {+		double baseline = -BirdFont.get_current_font ().base_line;+		double left = g.left_limit;+		double height = g.get_height ();+		+		double xa, ya, xb, yb;+		+		Path.get_line_points (start, stop, out xa, out ya, out xb, out yb);++		double center_x = Glyph.xc ();+		double center_y = Glyph.yc ();+		+		svg.append ("L");+	+		if (!to_glyph) {+			svg.append_printf ("%s ", round (xb - center_x - left));+			svg.append_printf ("%s ", round (yb - center_y + height / 2));	+		} else {+			svg.append_printf ("%s ", round (xb - center_x - left));+			svg.append_printf ("%s ", round (-yb + center_y + baseline));+		}+	}+	+	private static void add_double_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g,  bool to_glyph) {+		EditPoint middle;+		double x, y;+		+		x = start.get_right_handle ().x + (end.get_left_handle ().x - start.get_right_handle ().x) / 2;+		y = start.get_right_handle ().y + (end.get_left_handle ().y - start.get_right_handle ().y) / 2;+		+		middle = new EditPoint (x, y, PointType.QUADRATIC);+		middle.right_handle = end.get_left_handle ().copy ();+		+		add_quadratic_abs_path (start, middle, svg, g, to_glyph);+		add_quadratic_abs_path (middle, end, svg, g, to_glyph);+	}+		+	private static void add_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g,  bool to_glyph) {+		double left = g.left_limit;+		double baseline = -BirdFont.get_current_font ().base_line;+		double height = g.get_height ();+		+		double xa, ya, xb, yb, xc, yc, xd, yd;+		+		Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd);++		double center_x = Glyph.xc ();+		double center_y = Glyph.yc ();++		// cubic path+		if (!to_glyph) {+			svg.append_printf ("Q");++			svg.append_printf ("%s ", round (xb - center_x - left));+			svg.append_printf ("%s ", round (yb - center_y + height / 2));+			+			svg.append_printf ("%s ", round (xd - center_x - left));+			svg.append_printf ("%s ", round (yd - center_y + height / 2));++		} else {		+			svg.append_printf ("Q");++			svg.append_printf ("%s ", round (xb - center_x - left));+			svg.append_printf ("%s ", round (-yb + center_y + baseline));+			+			svg.append_printf ("%s ", round (xd - center_x - left));+			svg.append_printf ("%s ", round (-yd + center_y + baseline));	+		}+	}+			+	private static void add_cubic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g,  bool to_glyph) {+		double left = g.left_limit;+		double baseline = -BirdFont.get_current_font ().base_line;+		double height = g.get_height ();+		+		double xa, ya, xb, yb, xc, yc, xd, yd;+		+		Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd);++		double center_x = Glyph.xc ();+		double center_y = Glyph.yc ();+		+		// cubic path+		if (!to_glyph) {+			svg.append_printf ("C");++			svg.append_printf ("%s ", round (xb - center_x - left));+			svg.append_printf ("%s ", round (yb - center_y + height / 2));+			+			svg.append_printf ("%s ", round (xc - center_x - left));+			svg.append_printf ("%s ", round (yc - center_y + height / 2));+			+			svg.append_printf ("%s ", round (xd - center_x - left));+			svg.append_printf ("%s ", round (yd - center_y + height / 2));	++		} else {		+			svg.append_printf ("C");++			svg.append_printf ("%s ", round (xb - center_x - left));+			svg.append_printf ("%s ", round (-yb + center_y + baseline));+			+			svg.append_printf ("%s ", round (xc - center_x - left));+			svg.append_printf ("%s ", round (-yc + center_y + baseline));	+			+			svg.append_printf ("%s ", round (xd - center_x - left));+			svg.append_printf ("%s ", round (-yd + center_y + baseline));	+		}+	}+	+	/** Draw path from svg font data. */+	public static void draw_svg_path (Context cr, string svg, double x, double y) {+		double x1, x2, x3;+		double y1, y2, y3;+		double px, py;+		string[] d = svg.split (" ");++		if (d.length == 0) {+			return;+		}+		+		px = 0;+		py = 0;+		+		cr.save ();++		cr.set_line_width (0);+		+		if (svg == "") {+			return;+		}+		+		for (int i = 0; i < d.length; i++) {+			+			// trim off leading white space	+			while (d[i].index_of (" ") == 0) { +				d[i] = d[i].substring (1); // FIXME: maybe no ascii+			}+			+			if (d[i].index_of ("L") == 0) {+				x1 = double.parse (d[i].substring (1)) + x;+				y1 = -double.parse (d[i+1]) + y;+				cr.line_to (x1, y1);+				+				px = x1;+				py = y1;			+				continue;+			}++			if (d[i].index_of ("Q") == 0) {+				x1 = double.parse (d[i].substring (1)) + x;+				y1 = -double.parse (d[i+1]) + y;++				x2 = double.parse (d[i+2]) + x;+				y2 = -double.parse (d[i+3]) + y;+											+				cr.curve_to ((px + 2 * x1) / 3, (py + 2 * y1) / 3, (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, x2, y2);+				+				px = x2;+				py = y2;+				continue;+			}+			+			if (d[i].index_of ("C") == 0) {+				x1 = double.parse (d[i].substring (1)) + x;+				y1 = -double.parse (d[i+1]) + y;++				x2 = double.parse (d[i+2]) + x;+				y2 = -double.parse (d[i+3]) + y;++				x3 = double.parse (d[i+4]) + x;+				y3 = -double.parse (d[i+5]) + y;+																+				cr.curve_to (x1, y1, x2, y2, x3, y3);+				+				px = x3;+				py = y3;+				continue;+			}++			if (d[i].index_of ("M") == 0) {+				x1 = double.parse (d[i].substring (1)) + x;+				y1 = -double.parse (d[i+1]) + y;+				+				cr.move_to (x1, y1);++				px = x1;+				py = y1;+				continue;+			}+								+			if (d[i].index_of ("zM") == 0) {+				cr.close_path ();+				+				x1 = double.parse (d[i].substring (2)) + x;+				y1 = -double.parse (d[i+1]) + y;+				+				cr.move_to (x1, y1);++				px = x1;+				py = y1;+				continue;+			}++			if (d[i].index_of ("z") == 0) {+				cr.close_path ();+				continue;+			}+		+		}+		+		cr.fill ();+		cr.restore ();+	}++}++internal static string round (double p) {+	string v = p.to_string ();+	char[] c = new char [501];+	+	v = p.format (c, "%3.15f");+	+	if (v.index_of ("e") != -1) {	+		return "0.0";+	}+	+	return v;+}+	+}diff --git a/libbirdfont/Svg/BezierPoints.vala b/libbirdfont/Svg/BezierPoints.valadeleted file mode 100644index 609f075..0000000--- a/libbirdfont/Svg/BezierPoints.vala+++ /dev/null@@ -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 ())";-	}-}--}diff --git a/libbirdfont/Svg/Defs.vala b/libbirdfont/Svg/Defs.valadeleted file mode 100644index 4183321..0000000--- a/libbirdfont/Svg/Defs.vala+++ /dev/null@@ -1,96 +0,0 @@-/*-	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");-	}--	public Defs copy () {-		Defs d = new Defs ();-		-		foreach (Gradient g in gradients) {-			d.add (g);-		}-		-		return d;-	}--}--}diff --git a/libbirdfont/Svg/Gradient.vala b/libbirdfont/Svg/Gradient.valadeleted file mode 100644index 959f53b..0000000--- a/libbirdfont/Svg/Gradient.vala+++ /dev/null@@ -1,79 +0,0 @@-/*-	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;-using Math;--namespace BirdFont {--public class Gradient : GLib.Object {-	public double x1;-	public double y1;-	public double x2;-	public double y2;--	public Gee.ArrayList<Stop> stops;-	-	public string id = "";-	public string? href = null;-	public SvgTransforms transforms;--	public Gradient () {-		x1 = 0;-		y1 = 0;-		x2 = 0;-		y2 = 0;-		stops = new Gee.ArrayList<Stop> ();-		transforms = new SvgTransforms ();-	}-	-	public Gradient copy () {-		Gradient g = new Gradient ();-		g.x1 = x1;-		g.y1 = y1;-		g.x2 = x2;-		g.y2 = y2;-		-		foreach (Stop s in stops) {-			g.stops.add (s.copy ());-		}-		-		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 ();-	}-}--}diff --git a/libbirdfont/Svg/Object.vala b/libbirdfont/Svg/Object.valadeleted file mode 100644index 556cb68..0000000--- a/libbirdfont/Svg/Object.vala+++ /dev/null@@ -1,154 +0,0 @@-/*-	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;-using Math;--namespace BirdFont {--public abstract class Object : GLib.Object {-	bool open = false;-	-	public bool visible = true;-	public SvgStyle style = new SvgStyle ();-	public SvgTransforms transforms = new SvgTransforms ();-	-	public virtual Color? color { get; set; } // FIXME: keep this in svg style-	public virtual Color? stroke_color { get; set; }-	public virtual Gradient? gradient { get; set; }--	/** Path boundaries */-	public virtual double xmax { get; set; }-	public virtual double xmin { get; set; }-	public virtual double ymax { get; set; }-	public virtual double ymin { get; set; }-	-	public virtual double rotation { get; set; }-	public virtual double stroke { get; set; }-	public virtual LineCap line_cap { get; set; default = LineCap.BUTT; }-	public virtual bool fill { get; set; }-		-	public Object () {	-	}--	public Object.create_copy (Object o) {	-		open = o.open;-	}-		-	public void set_open (bool open) {-		this.open = open;-	}-	-	public bool is_open () {-		return open;-	}--	public abstract void update_region_boundaries ();-	public abstract bool is_over (double x, double y);-	public abstract void draw (Context cr);-	public abstract Object copy ();-	public abstract void move (double dx, double dy);-	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";-	}--	public void paint (Context cr) {-		Color fill, stroke;-		bool need_fill = style.fill_gradient != null || style.fill != null;-		bool need_stroke = style.stroke_gradient != null || style.stroke != null;--		cr.set_line_width (style.stroke_width);-	-		if (style.fill_gradient != null) {-			apply_gradient (cr, (!) style.fill_gradient);-		} else if (style.fill != null) {-			fill = (!) style.fill;-			cr.set_source_rgba (fill.r, fill.g, fill.b, fill.a);-		}--		if (need_fill) {-			if (need_stroke) {-				cr.fill_preserve ();-			} else {-				cr.fill ();-			}	-		}--		if (style.stroke_gradient != null) {-			apply_gradient (cr, (!) style.stroke_gradient);-		} else if (style.stroke != null) {-			stroke = (!) style.stroke;-			cr.set_source_rgba (stroke.r, stroke.g, stroke.b, stroke.a);-		}--		if (need_stroke) {-			cr.stroke ();-		}-	}-	-	public void apply_gradient (Context cr, Gradient? gradient) {-		Cairo.Pattern pattern;-		Gradient g;-		-		if (gradient != null) {-			g = (!) gradient;-			-			pattern = new Cairo.Pattern.linear (g.x1, g.y1, g.x2, g.y2);--			Matrix gradient_matrix = g.get_matrix ();-			gradient_matrix.invert ();-			pattern.set_matrix (gradient_matrix);-			-			foreach (Stop s in g.stops) {-				Color c = s.color;-				pattern.add_color_stop_rgba (s.offset, c.r, c.g, c.b, c.a);-			}-					-			cr.set_source (pattern);-		}-	}-	-	public void apply_transform (Context cr) {-		Matrix view_matrix = cr.get_matrix ();-		Matrix object_matrix = transforms.get_matrix ();-		-		object_matrix.multiply (object_matrix, view_matrix);-		cr.set_matrix (object_matrix);-	}-					-}--}diff --git a/libbirdfont/Svg/Points.vala b/libbirdfont/Svg/Points.valadeleted file mode 100644index 2f4dd25..0000000--- a/libbirdfont/Svg/Points.vala+++ /dev/null@@ -1,31 +0,0 @@-/*-	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 Points : GLib.Object {-	public Doubles point_data = new Doubles.for_capacity (100);-	public double x = 0;-	public double y = 0;-	public bool closed = false;--	public void add (double p) {-		point_data.add (p);-	}-}--}-diff --git a/libbirdfont/Svg/Rectangle.vala b/libbirdfont/Svg/Rectangle.valadeleted file mode 100644index 541ae5f..0000000--- a/libbirdfont/Svg/Rectangle.vala+++ /dev/null@@ -1,103 +0,0 @@-/*-	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;-using Math;--namespace BirdFont {--public class Rectangle : Object {--	public double x = 0;-	public double y = 0;-	public double width = 0;-	public double height = 0;-	public double rx = 0;-	public double ry = 0;--	public Rectangle () {-	}-	-	public override bool is_over (double x, double y) {-		return false;-	}-			-	public override void draw (Context cr) {-		cr.save ();-		apply_transform (cr);-		-		if (rx == 0 && ry == 0) {-			cr.rectangle (x, y, width, height);-		} else {-			draw_rounded_corners (cr);-		}-		-		paint (cr);-		cr.restore ();-	}-	-	public void draw_rounded_corners (Context cr) {-		cr.new_path ();-		elliptical_arc (cr, x + width - rx, y + ry, -PI / 2, 0);-		elliptical_arc (cr, x + width - rx, y + height - ry, 0, PI / 2);-		elliptical_arc (cr, x + rx, y + height - ry, PI / 2, PI);-		elliptical_arc (cr, x + rx, y + ry, PI, PI + PI / 2);-		cr.close_path ();-	}-	-	public void elliptical_arc (Context cr, double x, double y, double angle_start, double angle_stop) {-		cr.save ();-		cr.translate (x + rx, y + ry);-		cr.scale (rx, ry);-		cr.arc (0, 0, 1, angle_start, angle_stop);-		cr.restore ();-	}-	-	public override void move (double dx, double dy) {-		x += dx;-		y += 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 () {-		Rectangle r = new Rectangle ();-		-		r.x = x;-		r.y = y;-		r.width = width;-		r.height = height;-		-		Object.copy_attributes (this, r);-		-		return r;-	}--	public override string to_string () {-		return "Rectangle";-	}-}--}diff --git a/libbirdfont/Svg/Stop.vala b/libbirdfont/Svg/Stop.valadeleted file mode 100644index bec777f..0000000--- a/libbirdfont/Svg/Stop.vala+++ /dev/null@@ -1,39 +0,0 @@-/*-	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;-using Math;--namespace BirdFont {--public class Stop : GLib.Object {-	public Color color = Color.black ();-	public double offset = 0;-	-	public Stop () {-	}-	-	public Stop copy () {-		Stop s = new Stop ();-		s.color = color.copy  ();-		s.offset = offset;-		return s;-	}-	-	public string to_string () {-		return @"Stop: $(offset), " + color.to_string ();-	}-}--}diff --git a/libbirdfont/Svg/Svg.vala b/libbirdfont/Svg/Svg.valadeleted file mode 100644index 82eaf9a..0000000--- a/libbirdfont/Svg/Svg.vala+++ /dev/null@@ -1,355 +0,0 @@-/*-	Copyright (C) 2012 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 {--// FIXME: substrings--public class Svg {--	/** Export to svg glyph data. */-	public static string to_svg_glyph (Glyph g) {	-		StringBuilder svg = new StringBuilder ();-		PathList stroke_list;-		-		foreach (Path p in g.get_visible_paths ()) {-			if (p.stroke == 0) {-				write_path_as_glyph (p, svg, g);-			} else {-				stroke_list = p.get_completed_stroke ();-				write_paths_as_glyph (stroke_list, svg, g);-			}-		}-		-		return svg.str;-	}--	/** Export to svg-font data. */-	public static string to_svg_path (Path pl, Glyph g) {	-		StringBuilder svg = new StringBuilder ();-		pl.create_list ();-		write_path (pl, svg, g, false);-		return svg.str;-	}--	private static void write_paths_as_glyph (PathList pl, StringBuilder svg, Glyph g) {-		foreach (Path p in pl.paths) {-			write_path_as_glyph (p, svg, g);-		}-	}--	private static void write_path_as_glyph (Path pl, StringBuilder svg, Glyph g) {-		write_path (pl, svg, g, true);-	}--	private static void write_path (Path p, StringBuilder svg, Glyph g, bool do_glyph) {-		int i = 0;-		EditPoint? n = null;-		EditPoint m;-		-		if (p.points.size < 2) {-			return;-		}--		p.create_list ();-			-		foreach (var e in p.points) {-			if (i == 0) {-				add_abs_start (e, svg, g, do_glyph);-				i++;-				n = e;-				continue;-			}-			-			m = (!) n;--			add_abs_next (m, e, svg, g, do_glyph);-			-			n = e;-			i++;-		}--		if (!p.is_open ()) {-			m = p.points.get (0);	-			add_abs_next ((!) n, m, svg, g, do_glyph);-			close_path (svg);-		}-	}--	private static void add_abs_next (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool do_glyph) {-		if (start.right_handle.type == PointType.LINE_QUADRATIC) {-			add_abs_line_to (start, end, svg, g, do_glyph);-		} else if (start.right_handle.type == PointType.LINE_CUBIC && end.left_handle.type == PointType.LINE_CUBIC) {-			add_abs_line_to (start, end, svg, g, do_glyph);-		} else if (end.left_handle.type == PointType.QUADRATIC || start.right_handle.type == PointType.QUADRATIC) {-			add_quadratic_abs_path (start, end, svg, g, do_glyph);-		} else if (end.left_handle.type == PointType.DOUBLE_CURVE || start.right_handle.type == PointType.DOUBLE_CURVE) {-			add_double_quadratic_abs_path (start, end, svg, g, do_glyph);-		} else {-			add_cubic_abs_path (start, end, svg, g, do_glyph);-		}-	}--	private static void add_abs_start (EditPoint ep, StringBuilder svg, Glyph g, bool to_glyph) {		-		double left = g.left_limit;-		double baseline = -BirdFont.get_current_font ().base_line;-		double height = g.get_height ();-		-		svg.append_printf ("M");-		-		if (!to_glyph) {-			svg.append_printf ("%s ",  round (ep.x - left));-			svg.append_printf ("%s ",  round (-ep.y + height / 2));-		} else {-			svg.append_printf ("%s ",  round (ep.x - left));-			svg.append_printf ("%s ",  round (ep.y + baseline));-		}-	}-		-	private static void close_path (StringBuilder svg) {-		svg.append ("z");-	}	-	-	private static void add_abs_line_to (EditPoint start, EditPoint stop, StringBuilder svg, Glyph g, bool to_glyph) {-		double baseline = -BirdFont.get_current_font ().base_line;-		double left = g.left_limit;-		double height = g.get_height ();-		-		double xa, ya, xb, yb;-		-		Path.get_line_points (start, stop, out xa, out ya, out xb, out yb);--		double center_x = Glyph.xc ();-		double center_y = Glyph.yc ();-		-		svg.append ("L");-	-		if (!to_glyph) {-			svg.append_printf ("%s ", round (xb - center_x - left));-			svg.append_printf ("%s ", round (yb - center_y + height / 2));	-		} else {-			svg.append_printf ("%s ", round (xb - center_x - left));-			svg.append_printf ("%s ", round (-yb + center_y + baseline));-		}-	}-	-	private static void add_double_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g,  bool to_glyph) {-		EditPoint middle;-		double x, y;-		-		x = start.get_right_handle ().x + (end.get_left_handle ().x - start.get_right_handle ().x) / 2;-		y = start.get_right_handle ().y + (end.get_left_handle ().y - start.get_right_handle ().y) / 2;-		-		middle = new EditPoint (x, y, PointType.QUADRATIC);-		middle.right_handle = end.get_left_handle ().copy ();-		-		add_quadratic_abs_path (start, middle, svg, g, to_glyph);-		add_quadratic_abs_path (middle, end, svg, g, to_glyph);-	}-		-	private static void add_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g,  bool to_glyph) {-		double left = g.left_limit;-		double baseline = -BirdFont.get_current_font ().base_line;-		double height = g.get_height ();-		-		double xa, ya, xb, yb, xc, yc, xd, yd;-		-		Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd);--		double center_x = Glyph.xc ();-		double center_y = Glyph.yc ();--		// cubic path-		if (!to_glyph) {-			svg.append_printf ("Q");--			svg.append_printf ("%s ", round (xb - center_x - left));-			svg.append_printf ("%s ", round (yb - center_y + height / 2));-			-			svg.append_printf ("%s ", round (xd - center_x - left));-			svg.append_printf ("%s ", round (yd - center_y + height / 2));--		} else {		-			svg.append_printf ("Q");--			svg.append_printf ("%s ", round (xb - center_x - left));-			svg.append_printf ("%s ", round (-yb + center_y + baseline));-			-			svg.append_printf ("%s ", round (xd - center_x - left));-			svg.append_printf ("%s ", round (-yd + center_y + baseline));	-		}-	}-			-	private static void add_cubic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g,  bool to_glyph) {-		double left = g.left_limit;-		double baseline = -BirdFont.get_current_font ().base_line;-		double height = g.get_height ();-		-		double xa, ya, xb, yb, xc, yc, xd, yd;-		-		Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd);--		double center_x = Glyph.xc ();-		double center_y = Glyph.yc ();-		-		// cubic path-		if (!to_glyph) {-			svg.append_printf ("C");--			svg.append_printf ("%s ", round (xb - center_x - left));-			svg.append_printf ("%s ", round (yb - center_y + height / 2));-			-			svg.append_printf ("%s ", round (xc - center_x - left));-			svg.append_printf ("%s ", round (yc - center_y + height / 2));-			-			svg.append_printf ("%s ", round (xd - center_x - left));-			svg.append_printf ("%s ", round (yd - center_y + height / 2));	--		} else {		-			svg.append_printf ("C");--			svg.append_printf ("%s ", round (xb - center_x - left));-			svg.append_printf ("%s ", round (-yb + center_y + baseline));-			-			svg.append_printf ("%s ", round (xc - center_x - left));-			svg.append_printf ("%s ", round (-yc + center_y + baseline));	-			-			svg.append_printf ("%s ", round (xd - center_x - left));-			svg.append_printf ("%s ", round (-yd + center_y + baseline));	-		}-	}-	-	/** Draw path from svg font data. */-	public static void draw_svg_path (Context cr, string svg, double x, double y) {-		double x1, x2, x3;-		double y1, y2, y3;-		double px, py;-		string[] d = svg.split (" ");--		if (d.length == 0) {-			return;-		}-		-		px = 0;-		py = 0;-		-		cr.save ();--		cr.set_line_width (0);-		-		if (svg == "") {-			return;-		}-		-		for (int i = 0; i < d.length; i++) {-			-			// trim off leading white space	-			while (d[i].index_of (" ") == 0) { -				d[i] = d[i].substring (1); // FIXME: maybe no ascii-			}-			-			if (d[i].index_of ("L") == 0) {-				x1 = double.parse (d[i].substring (1)) + x;-				y1 = -double.parse (d[i+1]) + y;-				cr.line_to (x1, y1);-				-				px = x1;-				py = y1;			-				continue;-			}--			if (d[i].index_of ("Q") == 0) {-				x1 = double.parse (d[i].substring (1)) + x;-				y1 = -double.parse (d[i+1]) + y;--				x2 = double.parse (d[i+2]) + x;-				y2 = -double.parse (d[i+3]) + y;-											-				cr.curve_to ((px + 2 * x1) / 3, (py + 2 * y1) / 3, (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, x2, y2);-				-				px = x2;-				py = y2;-				continue;-			}-			-			if (d[i].index_of ("C") == 0) {-				x1 = double.parse (d[i].substring (1)) + x;-				y1 = -double.parse (d[i+1]) + y;--				x2 = double.parse (d[i+2]) + x;-				y2 = -double.parse (d[i+3]) + y;--				x3 = double.parse (d[i+4]) + x;-				y3 = -double.parse (d[i+5]) + y;-																-				cr.curve_to (x1, y1, x2, y2, x3, y3);-				-				px = x3;-				py = y3;-				continue;-			}--			if (d[i].index_of ("M") == 0) {-				x1 = double.parse (d[i].substring (1)) + x;-				y1 = -double.parse (d[i+1]) + y;-				-				cr.move_to (x1, y1);--				px = x1;-				py = y1;-				continue;-			}-								-			if (d[i].index_of ("zM") == 0) {-				cr.close_path ();-				-				x1 = double.parse (d[i].substring (2)) + x;-				y1 = -double.parse (d[i+1]) + y;-				-				cr.move_to (x1, y1);--				px = x1;-				py = y1;-				continue;-			}--			if (d[i].index_of ("z") == 0) {-				cr.close_path ();-				continue;-			}-		-		}-		-		cr.fill ();-		cr.restore ();-	}--}--internal static string round (double p) {-	string v = p.to_string ();-	char[] c = new char [501];-	-	v = p.format (c, "%3.15f");-	-	if (v.index_of ("e") != -1) {	-		return "0.0";-	}-	-	return v;-}-	-}diff --git a/libbirdfont/Svg/SvgArc.vala b/libbirdfont/Svg/SvgArc.valadeleted file mode 100644index 92f3689..0000000--- a/libbirdfont/Svg/SvgArc.vala+++ /dev/null@@ -1,181 +0,0 @@-/*- * BirdFont code from SVG Salamander- * - * Copyright (c) 2004, Mark McKay- * Copyright (c) 2014, Johan Mattsson- * All rights reserved.- *- * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following- * conditions are met:- *- *   - Redistributions of source code must retain the above - *     copyright notice, this list of conditions and the following- *     disclaimer.- *   - Redistributions in binary form must reproduce the above- *     copyright notice, this list of conditions and the following- *     disclaimer in the documentation and/or other materials - *     provided with the distribution.- *- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE- * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED- * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Mark McKay can be contacted at mark@kitfox.com.  Salamander and other- * projects can be found at http://www.kitfox.com- *- * Created on January 26, 2004, 8:40 PM- * Adapded to BirdFont on Juli 2, 2014, 5:01 PM- */--using Math;--namespace BirdFont {--/** Convert an SVG arc instruction to a Beziรฉr path. */-static void add_arc_points (BezierPoints[] bezier_points, ref int bi, double x0, double y0, double rx, double ry, double angle, bool largeArcFlag, bool sweepFlag, double x, double y) {--	//-	// Elliptical arc implementation based on the SVG specification notes-	//--	double dx2, dy2, cosAngle, sinAngle;-	double x1, y1, Prx, Pry, Px1, Py1, radiiCheck;-	double sign, sq, coef, cx1, cy1;-	double sx2, sy2, cx, cy;-	double ux, uy, vx, vy, p, n;-	double angleStart, angleExtent;-	double s, step, theta;-	-	// Compute the half distance between the current and the final point-	dx2 = (x0 - x) / 2.0;-	dy2 = (y0 - y) / 2.0;-	-	// Convert angle from degrees to radians-	angle = 2 * PI * ((angle % 360.0) / 360.0);-	-	cosAngle = cos (angle);-	sinAngle = sin (angle);--	//-	// Step 1 : Compute (x1, y1)-	//-	x1 = cosAngle * dx2 + sinAngle * dy2;-	y1 = -sinAngle * dx2 + cosAngle * dy2;-	-	// Ensure radii are large enough-	rx = fabs(rx);-	ry = fabs(ry);-	Prx = rx * rx;-	Pry = ry * ry;-	Px1 = x1 * x1;-	Py1 = y1 * y1;--	-	// Check that radii are large enough-	radiiCheck = Px1 / Prx + Py1 / Pry;-	-	if (radiiCheck > 1) {-		rx = sqrt (radiiCheck) * rx;-		ry = sqrt (radiiCheck) * ry;-		Prx = rx * rx;-		Pry = ry * ry;-	}--	//-	// Step 2 : Compute (cx1, cy1)-	//-	sign = (largeArcFlag == sweepFlag) ? -1 : 1;-	sq = ((Prx * Pry) - (Prx * Py1) - (Pry * Px1)) / ((Prx * Py1) + (Pry * Px1));-	sq = (sq < 0) ? 0 : sq;-	coef = (sign * Math.sqrt(sq));-	cx1 = coef * ((rx * y1) / ry);-	cy1 = coef * -((ry * x1) / rx);--	//-	// Step 3 : Compute (cx, cy) from (cx1, cy1)-	//-	-	sx2 = (x0 + x) / 2.0;-	sy2 = (y0 + y) / 2.0;-	cx = sx2 - (cosAngle * cx1 - sinAngle * cy1);-	cy = sy2 - (sinAngle * cx1 + cosAngle * cy1);--	//-	// Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle)-	//-	-	ux = (x1 - cx1) / rx;-	uy = (y1 - cy1) / ry;-	vx = (-x1 - cx1) / rx;-	vy = (-y1 - cy1) / ry;--	// Compute the angle start-	n = sqrt((ux * ux) + (uy * uy));-	p = ux; // (1 * ux) + (0 * uy)-	sign = (uy < 0) ? -1d : 1d;-	angleStart = sign * acos(p / n);--	// Compute the angle extent-	n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));-	p = ux * vx + uy * vy;-	sign = (ux * vy - uy * vx < 0) ? -1d : 1d;-	angleExtent = sign * Math.acos(p / n);-	-	if(!sweepFlag && angleExtent > 0) {-		angleExtent -= 2 *PI;-	} else if (sweepFlag && angleExtent < 0) {-		angleExtent += 2 *PI;-	}-	angleExtent %= 2 * PI;-	angleStart %= 2 * PI;--	angleExtent *= -1;-	angleStart *= -1;-	-	// Approximate the path with Beziรฉr points-	s = (angleExtent > 0) ? 1 : -1;-	step = fabs (angleExtent) / (2 * fabs (angleExtent));--	theta = PI - angleStart - angleExtent;-	-	bezier_points[bi].type = 'L';-	bezier_points[bi].svg_type = 'a';--	bezier_points[bi].x0 = cx + rx * cos (theta);-	bezier_points[bi].y0 = cy + ry * sin (theta);-									-	bi++;-					-	for (double a = 0; a < fabs (angleExtent); a += step) {-		theta = PI - angleStart - angleExtent + s * a;--		return_if_fail (0 <= bi < bezier_points.length);--		bezier_points[bi].type = 'S';-		bezier_points[bi].svg_type = 'a';--		bezier_points[bi].x0 = cx + rx * cos (theta);-		bezier_points[bi].y0 = cy + ry * sin (theta);--		bezier_points[bi].x1 = cx + rx * cos (theta + 1 * step / 4);-		bezier_points[bi].y1 = cy + ry * sin (theta + 1 * step / 4);-		-		bezier_points[bi].x2 = cx + rx * cos (theta + 2 * step / 4);-		bezier_points[bi].y2 = cy + ry * sin (theta + 2 * step / 4);-						-		bi++;-	}-}--}-	diff --git a/libbirdfont/Svg/SvgDrawing.vala b/libbirdfont/Svg/SvgDrawing.valadeleted file mode 100644index 84cb925..0000000--- a/libbirdfont/Svg/SvgDrawing.vala+++ /dev/null@@ -1,73 +0,0 @@-/*-	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 Cairo;-using Math;--namespace BirdFont {--public class SvgDrawing : Object {-	public Layer root_layer = new Layer ();-	public Defs defs = new Defs ();--	public double x = 0;-	public double y = 0;-	public double width = 0;-	public double height = 0;-		-	public override void update_region_boundaries () {-	}-	-	public override bool is_over (double x, double y) {-		return (this.x <= x <= this.x + width) -			&& (this.y <= y <= this.y + height);-	}-	-	public override void draw (Context cr) {-		cr.save ();-		cr.translate (x, y);--		foreach (Object o in root_layer.get_visible_objects ().objects) {-			o.draw (cr);-		}--		cr.restore ();-	}-	-	public override Object copy () {-		SvgDrawing drawing = new SvgDrawing ();-		drawing.root_layer = root_layer.copy ();-		drawing.defs = defs.copy ();-		return drawing;-	}-	-	public override void move (double dx, double dy) {-		x += dx;-		y += dy;-	}-	-	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) {-	}-}--}diff --git a/libbirdfont/Svg/SvgFile.vala b/libbirdfont/Svg/SvgFile.valadeleted file mode 100644index a8f8b61..0000000--- a/libbirdfont/Svg/SvgFile.vala+++ /dev/null@@ -1,564 +0,0 @@-/*-	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 Cairo;--namespace BirdFont {--public class SvgFile : GLib.Object {--	SvgDrawing drawing;--	public SvgFile () {		-	}-	-	public EmbeddedSvg parse (string path) {-		string xml_data;-		-		try {-			FileUtils.get_contents (path, out xml_data);-			return parse_data (xml_data);-		} catch (GLib.Error error) {-			warning (error.message);-		}-		-		SvgDrawing drawing = new SvgDrawing ();-		return new EmbeddedSvg (drawing);-	}--	public EmbeddedSvg parse_data (string xml_data) {-		XmlParser xmlparser = new XmlParser (xml_data);-		SvgDrawing drawing = new SvgDrawing ();	-		-		if (xmlparser.validate ()) {-			Tag root = xmlparser.get_root_tag ();-			drawing = parse_svg_file (root);-			EmbeddedSvg svg = new EmbeddedSvg (drawing);-			svg.svg_data = xml_data;-			return svg;-		} else {-			warning ("Invalid xml file.");-		}-	-		return new EmbeddedSvg (drawing);-	}-	-	private SvgDrawing parse_svg_file (Tag tag) {-		drawing = new SvgDrawing ();--		foreach (Attribute attr in tag.get_attributes ()) {	-			if (attr.get_name () == "width") {-				drawing.width = parse_number (attr.get_content ());-			}--			if (attr.get_name () == "height") {-				drawing.height = parse_number (attr.get_content ());-			}-		}-				-		foreach (Tag t in tag) {-			string name = t.get_name ();-			-			if (name == "g") {-				parse_layer (drawing.root_layer, t);-			}--			if (name == "defs") {-				parse_defs (drawing, t);-			}-			-			parse_object (drawing.root_layer, t);-		}-		-		return drawing;-	}--	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) {-			string name = t.get_name ();-			-			if (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_defs (SvgDrawing drawing, Tag tag) {-		foreach (Tag t in tag) {-			// FIXME: radial-			string name = t.get_name ();-			-			if (name == "linearGradient") {-				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 ();-			-			// FIXME: gradientUnits-			-			if (name == "gradientTransform") {-				gradient.transforms = parse_transform (attr.get_content ());-			}--			if (name == "href") {-				gradient.href = attr.get_content ();-			}--			if (name == "x1") {-				gradient.x1 = parse_number (attr.get_content ());-			}--			if (name == "y1") {	-				gradient.y1 = parse_number (attr.get_content ());-			}-			-			if (name == "x2") {-				gradient.x2 = parse_number (attr.get_content ());-			}--			if (name == "y2") {-				gradient.y2 = parse_number (attr.get_content ());-			}--			if (name == "id") {-				gradient.id = attr.get_content ();-			}-		}-		-		foreach (Tag t in tag) {-			// FIXME: radial-			string name = t.get_name ();-			-			if (name == "stop") {-				parse_stop (gradient, t);-			}-		}-	}--	void parse_stop (Gradient gradient, Tag tag) {-		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 ();-			-			if (name == "offset") {-				string stop_offset = attr.get_content ();-				-				if (stop_offset.index_of ("%") > -1) {-					stop_offset = stop_offset.replace ("%", "");-					stop.offset = parse_number (stop_offset) / 100.0;-				} else {-					stop.offset = parse_number (stop_offset);-				}-			}-		}-		-		string? stop_color = style.style.get ("stop-color");-		string? stop_opacity = style.style.get ("stop-opacity");-		Color? color = Color.black ();-		-		if (stop_color != null) {-			color = Color.parse (stop_color);-			-			if (color != null) {-				stop.color = (!) color;-			}-		}--		if (stop_opacity != null && color != null) {-			((!) color).a = parse_number (stop_opacity);-		}-	}-	-	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) {-		Rectangle rectangle = new Rectangle ();--		foreach (Attribute attr in tag.get_attributes ()) {-			string attribute = attr.get_name ();-			-			if (attribute == "x") {-				rectangle.x = parse_number (attr.get_content ());-			}--			if (attribute == "y") {-				rectangle.y = parse_number (attr.get_content ());-			}--			if (attribute == "width") {-				rectangle.width = parse_number (attr.get_content ());-			}--			if (attribute == "height") {-				rectangle.height = parse_number (attr.get_content ());-			}-			-			if (attribute == "rx") {-				rectangle.rx = parse_number (attr.get_content ());-			}--			if (attribute == "ry") {-				rectangle.ry = parse_number (attr.get_content ());-			}-		}-		-		rectangle.transforms = get_transform (tag.get_attributes ());-		rectangle.style = SvgStyle.parse (drawing.defs,tag.get_attributes ());-		rectangle.visible = is_visible (tag);	-		-		layer.add_object (rectangle);-	}-	-	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 SvgTransforms parse_transform (string transforms) {-		string[] functions;-		string transform = transforms;-		SvgTransforms transform_functions;-		-		transform_functions = new SvgTransforms ();-		-		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 static string remove_unit (string d) {-		string s = d.replace ("pt", "");-		s = s.replace ("pc", "");-		s = s.replace ("mm", "");-		s = s.replace ("cm", "");-		s = s.replace ("in", "");-		s = s.replace ("px", "");-		return s;-	}-    -	public static double parse_number (string? number_with_unit) {-		if (number_with_unit == null) {-			return 0;-		}-		-		string d = (!) number_with_unit;-		string s = remove_unit (d);-		double n = SvgParser.parse_double (s);-		-		if (d.has_suffix ("pt")) {-			n *= 1.25;-		} else if (d.has_suffix ("pc")) {-			n *= 15;-		} else if (d.has_suffix ("mm")) {-			n *= 3.543307;-		} else if (d.has_suffix ("cm")) {-			n *= 35.43307;-		} else if (d.has_suffix ("in")) {-			n *= 90;-		}-		-		return n;-	}-	-	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 SvgTransforms get_transform (Attributes attributes) {-		foreach (Attribute attr in attributes) {-			if (attr.get_name () == "transform") {-				return parse_transform (attr.get_content ());-			}-		}-		-		return new SvgTransforms ();-	}-	-	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 ());-			}-		}-		-		path.transforms = get_transform (tag.get_attributes ());-		path.style = SvgStyle.parse (drawing.defs, 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++) {-			// FIXME: add more types-			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') {-				points.closed = true;-				path_data.add (points);-				points = new Points ();-			} else {-				string type = (!) bezier_points[i].type.to_string ();-				warning (@"SVG conversion not implemented for $type");-			}-		}--		if (points.point_data.size > 0) {-			path_data.add (points);-		}--		return path_data;-	}-}--}diff --git a/libbirdfont/Svg/SvgFont.vala b/libbirdfont/Svg/SvgFont.valadeleted file mode 100644index 7223c2f..0000000--- a/libbirdfont/Svg/SvgFont.vala+++ /dev/null@@ -1,264 +0,0 @@-/*-	Copyright (C) 2013 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.-*/--using B;--namespace BirdFont {--class SvgFont : GLib.Object {-	Font font;-	double units = 1;-	double font_advance = 0;-	-	public SvgFont (Font f) {-		this.font = f;-	}-	-	/** Load svg font from file. */-	public void load (string path) {-		string data;-		XmlParser xml_parser;-		try {-			FileUtils.get_contents (path, out data);-			xml_parser = new XmlParser (data);-			parse_svg_font (xml_parser.get_root_tag ());-		} catch (GLib.Error e) {-			warning (e.message);-		}-	}-	-	void parse_svg_font (Tag tag) {-		foreach (Tag t in tag) {-			if (t.get_name () == "defs") {-				parse_svg_font (t);-			}-			-			if (t.get_name () == "font") {-				parse_font_tag (t);-				parse_svg_font (t);-			}--			if (t.get_name () == "font-face") {-				parse_font_limits (t);-			}--			if (t.get_name () == "hkern") {-				parse_hkern (t);-			}-									-			if (t.get_name () == "glyph") {-				parse_glyph (t);-			}-		}		-	}--	void parse_hkern (Tag tag) {-		string left = "";-		string right = "";-		string left_name = "";-		string right_name = "";-		double kerning = 0;-		unichar l, r;-		StringBuilder sl, sr;-		GlyphRange grr, grl;-		KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes ();-		-		foreach (Attribute attr in tag.get_attributes ()) {-			// left-			if (attr.get_name () == "u1") {-				left = attr.get_content ();-			}	-					-			// right	-			if (attr.get_name () == "u2") {-				right = attr.get_content ();-			}--			if (attr.get_name () == "g1") {-				left_name = attr.get_content ();-			}-			-			if (attr.get_name () == "g2") {-				right_name = attr.get_content ();-			}-				-			// kerning-			if (attr.get_name () == "k") {-				kerning = double.parse (attr.get_content ()) * units;-			}-		}-				-		// FIXME: ranges and sequences for u1 & u2 + g1 & g2-		foreach (string lk in left.split (",")) {-			foreach (string rk in right.split (",")) {-				l = get_unichar (lk);-				r = get_unichar (rk);-				-				sl = new StringBuilder ();-				sl.append_unichar (l);-				-				sr = new StringBuilder ();-				sr.append_unichar (r);-				-				try {-					grl = new GlyphRange ();-					grl.parse_ranges (sl.str);-					-					grr = new GlyphRange ();-					grr.parse_ranges (sr.str);-				-					classes.set_kerning (grl, grr, -kerning);-				} catch (MarkupError e) {-					warning (e.message);-				}		-			}-		}-	}--	void parse_font_limits (Tag tag) {-		double top_limit = 0;-		double bottom_limit = 0;-		-		foreach (Attribute attr in tag.get_attributes ()) {-			if (attr.get_name () == "units-per-em") {-				units = 100.0 / double.parse (attr.get_content ());-			}	-		}--		foreach (Attribute attr in tag.get_attributes ()) {	-			if (attr.get_name () == "ascent") {-				top_limit = double.parse (attr.get_content ());-			}-			-			if (attr.get_name () == "descent") {-				bottom_limit = double.parse (attr.get_content ());-			}		-		}-		-		top_limit *= units;-		bottom_limit *= units;-		-		font.bottom_limit = bottom_limit;-		font.top_limit = top_limit;-	}-	-	void parse_font_tag (Tag tag) {-		foreach (Attribute attr in tag.get_attributes ()) {-			if (attr.get_name () == "horiz-adv-x") {-				font_advance = double.parse (attr.get_content ());-			}-						-			if (attr.get_name () == "id") {-				font.set_name (attr.get_content ());-			}-		}-	}-	-	/** Obtain unichar value from either a character, a hex representation of-	 *  a character or series of characters (f, &#x6d; or ffi).-	 */-	static unichar get_unichar (string val) {-		string v = val;-		unichar unicode_value;-		-		if (val == "&") {-			return '&';-		}-		-		// TODO: parse ligatures-		if (v.has_prefix ("&")) {-			// parse hex value-			v = v.substring (0, v.index_of (";"));-			v = v.replace ("&#x", "U+");-			v = v.replace (";", "");-			unicode_value = Font.to_unichar (v);-		} else {-			// obtain unicode value-			-			if (v.char_count () > 1) {-				warning ("font contains ligatures");-				return '\0';-			}-			-			unicode_value = v.get_char (0);-		}-		-		return unicode_value;	-	}-	-	bool is_ligature (string v) {-		if (v.has_prefix ("&")) {-			return false;-		}-		-		return v.char_count () > 1;-	}-	-	void parse_glyph (Tag tag) {-		unichar unicode_value = 0;-		string glyph_name = "";-		string svg = "";-		Glyph glyph;-		GlyphCollection glyph_collection;-		double advance = font_advance;-		string ligature = "";-		SvgParser parser = new SvgParser ();-		StringBuilder unicode_name; --		parser.set_format (SvgFormat.INKSCAPE);--		foreach (Attribute attr in tag.get_attributes ()) {-			if (attr.get_name () == "unicode") {-				unicode_value = get_unichar (attr.get_content ());-				-				if (glyph_name == "") {-					glyph_name = attr.get_content ();-				}-				-				if (is_ligature (attr.get_content ())) {-					ligature = attr.get_content ();-				}-			}-			-			// svg data-			if (attr.get_name () == "d") {-				svg = attr.get_content ();-			}-			-			if (attr.get_name () == "glyph-name") {-				glyph_name = attr.get_content ();-			}--			if (attr.get_name () == "horiz-adv-x") {-				advance = double.parse (attr.get_content ());-			}-		}--		unicode_name = new StringBuilder ();-		unicode_name.append_unichar (unicode_value);--		glyph = new Glyph (unicode_name.str, unicode_value);-		parser.add_path_to_glyph (svg, glyph, true, units);			-		glyph.right_limit = glyph.left_limit + advance * units;-		-		// FIXME: add svg font ligatures-		-		glyph_collection = new GlyphCollection (unicode_value, glyph_name);-		glyph_collection.insert_glyph (glyph, true);-		-		font.add_glyph_collection (glyph_collection);-	}-}--}diff --git a/libbirdfont/Svg/SvgFontFormatWriter.vala b/libbirdfont/Svg/SvgFontFormatWriter.valadeleted file mode 100644index b2e907b..0000000--- a/libbirdfont/Svg/SvgFontFormatWriter.vala+++ /dev/null@@ -1,132 +0,0 @@-/*-	Copyright (C) 2012, 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 {--class SvgFontFormatWriter : GLib.Object  {--	DataOutputStream os;--	public SvgFontFormatWriter () {-	}--	public void open (File file) throws Error {-		if (file.query_exists ()) {-			throw new FileError.EXIST ("SvgFontFormatWriter: file exists.");-		}-		-		os = new DataOutputStream (file.create(FileCreateFlags.REPLACE_DESTINATION));-	}--	public void close () throws Error {-		os.close ();-	}-	-	public void write_font_file (Font font) throws Error {-		string font_name = font.get_full_name ();-		-		int units_per_em = 100;-		-		int ascent = 80;-		int descent = -20;-		-		StringBuilder b;-		-		Glyph? g;-		Glyph glyph;-		unichar index = 0;-		-		string uni;-		-		KerningClasses classes;-		-		put ("""<?xml version="1.0" standalone="no"?>""");-		put ("""<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >""");-		put ("""<svg>""");--		// (metadata goes here)--		put ("<defs>");--		put (@"<font id=\"$font_name\" horiz-adv-x=\"250\" >");-		put (@"<font-face units-per-em=\"$(to_float (units_per_em))\" ascent=\"$(to_float (ascent))\" descent=\"$(to_float (descent))\" />");--		// (missing-glyph goes here)--		// regular glyphs -		while (true) {-			g = font.get_glyph_index (index++);-			-			if (g == null) {-				break;-			}-			-			glyph = (!) g;-			-			b = new StringBuilder ();-			b.append_unichar (glyph.get_unichar ());--			if (glyph.get_unichar () >= ' ' && b.str.validate ()) {-				if (b.str == "\"" || b.str == "&" || b.str == "<" || b.str == ">") {-					uni = Font.to_hex_code (glyph.get_unichar ());-					put (@"<glyph unicode=\"&#x$(uni);\" horiz-adv-x=\"$(to_float (glyph.get_width ()))\" d=\"$(glyph.get_svg_data ())\" />");			-				} else {-					put (@"<glyph unicode=\"$(b.str)\" horiz-adv-x=\"$(to_float (glyph.get_width ()))\" d=\"$(glyph.get_svg_data ())\" />");-				}-			}-		}-		-		// FIXME: ligatures-		classes = BirdFont.get_current_font ().get_kerning_classes ();-		classes.all_pairs ((kerning) => {-			string l, r;-			-			foreach (Kerning k in kerning.kerning) {-				try {-					if (k.glyph != null) {-						l = Font.to_hex_code (kerning.character.unichar_code);-						r = Font.to_hex_code (((!)k.glyph).unichar_code);	-						os.put_string (@"<hkern u1=\"&#x$l;\" u2=\"&#x$r;\" k=\"$(to_float (-k.val))\"/>\n");-					} else {-						warning ("No glyph.");-					}-				} catch (GLib.Error e) {-					warning (e.message);-				}-			}-		});	--		put ("</font>");-		put ("</defs>");-		put ("</svg>");-	}--	string to_float (double d) {-		string s = @"$d";-		if (s.index_of ("e") != -1) {-			return "0".dup ();-		}-		return s.replace (",", ".");-	}--	/** Write a new line */-	private void put (string line) throws Error {-		os.put_string (line);-		os.put_string ("\n");-	}--}---}diff --git a/libbirdfont/Svg/SvgParser.vala b/libbirdfont/Svg/SvgParser.valadeleted file mode 100644index 58b1493..0000000--- a/libbirdfont/Svg/SvgParser.vala+++ /dev/null@@ -1,2122 +0,0 @@-/*-	Copyright (C) 2012 2013 2014 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;-using Math;--namespace BirdFont {--public enum SvgFormat {-	NONE,-	INKSCAPE,-	ILLUSTRATOR-}--public enum SvgType {-	COLOR,-	REGULAR-}--public class SvgParser {-	-	SvgFormat format = SvgFormat.ILLUSTRATOR;-	-	public SvgParser () {-	}-	-	public void set_format (SvgFormat f) {-		format = f;-	}-	-	public static void import (SvgType type) {-		FileChooser fc = new FileChooser ();-		fc.file_selected.connect ((p) => {-			string path;-				-			if (p == null) {-				return;-			}-			-			path = (!) p;-			-			if (type == SvgType.REGULAR) {-				import_svg (path);-			} else if (type == SvgType.COLOR) {-				Glyph glyph = MainWindow.get_current_glyph ();-				import_color_svg (glyph, path);-			}-		});-		-		fc.add_extension ("svg");-		MainWindow.file_chooser (t_("Import"), fc, FileChooser.LOAD);-	}-		-	public static void import_color_svg (Glyph glyph, string path) {-		SvgFile svg_file = new SvgFile ();-		EmbeddedSvg drawing = svg_file.parse (path);-		-		Layer layer = new Layer ();-		layer.name = "SVG";-		layer.add_object (drawing);-		-		glyph.add_layer (layer);-		-		// FIXME: update GUI-	}-	-	public static void import_folder (SvgType type) {-		FileChooser fc = new FileChooser ();-		fc.file_selected.connect ((p) => {-			string path;-			File svg_folder;-			File svg;-			bool imported;-			FileEnumerator enumerator;-			FileInfo? file_info;-			string file_name;-			Font font;-			-			if (p == null) {-				return;-			}-			-			path = (!) p;-			svg_folder = File.new_for_path (path);-			font = BirdFont.get_current_font ();-				-			try {-				enumerator = svg_folder.enumerate_children (FileAttribute.STANDARD_NAME, 0);-				while ((file_info = enumerator.next_file ()) != null) {-					file_name = ((!) file_info).get_name ();-					-					if (file_name.has_suffix (".svg")) {-						svg = get_child (svg_folder, file_name);-						imported = import_svg_file (font, svg, type);-						-						if (!imported) {-							warning ("Can't import %s.", (!) svg.get_path ());-						} else {-							font.touch ();-						}-					}-				}-			} catch (Error e) {-				warning (e.message);-			}-		});-		-		MainWindow.file_chooser (t_("Import"), fc, FileChooser.LOAD | FileChooser.DIRECTORY);-	}-	-	public static void import_svg_data (string xml_data, SvgFormat format = SvgFormat.NONE) {-		PathList path_list = new PathList ();-		Glyph glyph; -		string[] lines = xml_data.split ("\n");-		bool has_format = false;-		SvgParser parser = new SvgParser ();-		XmlParser xmlparser;--		foreach (string l in lines) {-			if (l.index_of ("Illustrator") > -1 || l.index_of ("illustrator") > -1) {-				parser.set_format (SvgFormat.ILLUSTRATOR);-				has_format = true;-			}-			-			if (l.index_of ("Inkscape") > -1 || l.index_of ("inkscape") > -1) {-				parser.set_format (SvgFormat.INKSCAPE);-				has_format = true;-			}-		}-		-		if (format != SvgFormat.NONE) {-			parser.set_format (format);-		}--		// parse the file-		if (!has_format) {-			warn_if_test ("No format identifier found in SVG parser.\n");-		}--		xmlparser = new XmlParser (xml_data);-		-		if (!xmlparser.validate()) {-			warning("Invalid XML in SVG parser."); -		}-		-		path_list = parser.parse_svg_file (xmlparser.get_root_tag ());-	-		glyph = MainWindow.get_current_glyph ();-		foreach (Path p in path_list.paths) {-			PathObject path = new PathObject.for_path (p);-			glyph.add_object (path);-			glyph.add_active_object (null, path); // FIXME: groups-			path.update_region_boundaries ();-		}-		-		glyph.close_path ();	-	}-	-	public static string replace (string content, string start, string stop, string replacement) {-		int i_tag = content.index_of (start);-		int end_tag = content.index_of (stop, i_tag);-		string c = "";-		-		if (i_tag > -1) {-			c = content.substring (0, i_tag) -				+ replacement-				+ content.substring (end_tag + stop.length);-		} else {-			c = content;-		}-		-		return c;-	}-	-	public static void import_svg (string path) {-		string svg_data;-		try {-			FileUtils.get_contents (path, out svg_data);-		} catch (GLib.Error e) {-			warning (e.message);-		}-		import_svg_data (svg_data);-	}-	-	private PathList parse_svg_file (Tag tag) {-		Layer pl = new Layer ();-	-		foreach (Tag t in tag) {-			-			if (t.get_name () == "g") {-				parse_layer (t, pl);-			}--			if (t.get_name () == "switch") {-				parse_layer (t, pl);-			}-						-			if (t.get_name () == "path") {-				parse_path (t, pl);-			}-			-			if (t.get_name () == "polygon") {-				parse_polygon (t, pl);-			}--			if (t.get_name () == "polyline") {-				parse_polyline (t, pl);-			}-						-			if (t.get_name () == "circle") {-				parse_circle (t, pl);-			}-			-			if (t.get_name () == "ellipse") {-				parse_ellipse (t, pl);-			}--			if (t.get_name () == "line") {-				parse_line (t, pl);-			}			-		}-		-		return pl.get_all_paths ();-	}-	-	private void parse_layer (Tag tag, Layer pl) {-		Layer layer;-		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) {-			return;-		}-					-		foreach (Tag t in tag) {-			if (t.get_name () == "path") {-				parse_path (t, pl);-			}-			-			if (t.get_name () == "g") {-				layer = new Layer ();-				parse_layer (t, layer);-				pl.subgroups.add (layer);-			}-			-			if (t.get_name () == "polygon") {-				parse_polygon (t, pl);-			}--			if (t.get_name () == "polyline") {-				parse_polyline (t, pl);-			}-			-			if (t.get_name () == "rect") {-				parse_rect (t, pl);-			}--			if (t.get_name () == "circle") {-				parse_circle (t, pl);-			}--			if (t.get_name () == "ellipse") {-				parse_ellipse (t, pl);-			}-			-			if (t.get_name () == "line") {-				parse_line (t, pl);-			}-		}--		foreach (Attribute attr in tag.get_attributes ()) {	-			if (attr.get_name () == "transform") {-				transform (attr.get_content (), pl);-			}-		}-	}-	-	private void transform (string transform_functions, Layer layer) {-		PathList path_list = new PathList ();-		-		foreach (Object o in layer.objects) {-			if (o is PathObject) {-				path_list.add (((PathObject) o).get_path ());-			}-		}-		-		transform_paths (transform_functions, path_list);-		transform_subgroups (transform_functions, layer);-	}-	-	private void transform_subgroups (string transform_functions, Layer layer) {-		foreach (Layer subgroup in layer.subgroups) {-			transform (transform_functions, subgroup);-		}-	}-	-	private void transform_paths (string transform_functions, PathList pl) {-		string data = transform_functions.dup ();-		string[] functions;-				-		// use only a single space as separator-		while (data.index_of ("  ") > -1) {-			data = data.replace ("  ", " ");-		}-		-		return_if_fail (data.index_of (")") > -1);-		-		 // add separator-		data = data.replace (") ", "|");-		data = data.replace (")", "|"); -		functions = data.split ("|");-		-		for (int i = functions.length - 1; i >= 0; i--) {-			if (functions[i].has_prefix ("translate")) {-				translate (functions[i], pl);-			}-			-			if (functions[i].has_prefix ("scale")) {-				scale (functions[i], pl);-			}--			if (functions[i].has_prefix ("matrix")) {-				matrix (functions[i], pl);-			}-			-			// TODO: rotate etc.-		}-	}--	/** @param path a path in the cartesian coordinate system-	 * The other parameters are in the SVG coordinate system.-	 */-	public static void apply_matrix (Path path, double a, double b, double c, -		double d, double e, double f){-		-		double dx, dy;-		Font font = BirdFont.get_current_font ();-		Glyph glyph = MainWindow.get_current_glyph ();-		-		foreach (EditPoint ep in path.points) {-			ep.tie_handles = false;-			ep.reflective_point = false;-		}-		-		foreach (EditPoint ep in path.points) {-			apply_matrix_on_handle (ep.get_right_handle (), a, b, c, d, e, f);-			apply_matrix_on_handle (ep.get_left_handle (), a, b, c, d, e, f);--			ep.independent_y = font.top_position - ep.independent_y;-			ep.independent_x -= glyph.left_limit;-			-			dx = a * ep.independent_x + c * ep.independent_y + e;-			dy = b * ep.independent_x + d * ep.independent_y + f;-			-			ep.independent_x = dx;-			ep.independent_y = dy;-			-			ep.independent_y = font.top_position - ep.independent_y;-			ep.independent_x += glyph.left_limit;-		}-	}--	public static void apply_matrix_on_handle (EditPointHandle h, -		double a, double b, double c, -		double d, double e, double f){-		-		double dx, dy;-		Font font = BirdFont.get_current_font ();-		Glyph glyph = MainWindow.get_current_glyph ();--		h.y = font.top_position - h.y;-		h.x -= glyph.left_limit;-		-		dx = a * h.x + c * h.y + e;-		dy = b * h.x + d * h.y + f;-		-		h.x = dx;-		h.y = dy;-		-		h.y = font.top_position - h.y;-		h.x += glyph.left_limit;-	}---	private void matrix (string function, PathList pl) {-		string parameters = get_transform_parameters (function);-		string[] p = parameters.split (" ");--		if (p.length != 6) {-			warning ("Expecting six parameters for matrix transformation.");-			return;-		}--		foreach (Path path in pl.paths) {-			apply_matrix (path, parse_double (p[0]), parse_double (p[1]), -				parse_double (p[2]), parse_double (p[3]), -				parse_double (p[4]), parse_double (p[5]));-		}-	}-		-	private void scale (string function, PathList pl) {-		string parameters = get_transform_parameters (function);-		string[] p = parameters.split (" ");-		double x, y;-		-		x = 1;-		y = 1;-		-		if (p.length > 0) {-			x = parse_double (p[0]);-		}-		-		if (p.length > 1) {-			y = parse_double (p[1]);-		}-		-		foreach (Path path in pl.paths) {-			path.scale (-x, y);-		}-	}-	-	private void translate (string function, PathList pl) {-		string parameters = get_transform_parameters (function);-		string[] p = parameters.split (" ");-		double x, y;-		-		x = 0;-		y = 0;-		-		if (p.length > 0) {-			x = parse_double (p[0]);-		}-		-		if (p.length > 1) {-			y = parse_double (p[1]);-		}-		-		foreach (Path path in pl.paths) {-			path.move (x, -y);-		}-	}--	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 void parse_circle (Tag tag, Layer pl) {-		Path p;-		double x, y, r;-		Glyph g;-		PathList npl;-		BezierPoints[] bezier_points;-		SvgStyle style = new SvgStyle ();-		bool hidden = false;-		-		npl = new PathList ();-		-		x = 0;-		y = 0;-		r = 0;-			-		foreach (Attribute attr in tag.get_attributes ()) {			-			if (attr.get_name () == "cx") {-				x = parse_double (attr.get_content ());-			}-			-			if (attr.get_name () == "cy") {-				y = -parse_double (attr.get_content ());-			}--			if (attr.get_name () == "r") {-				r = parse_double (attr.get_content ());-			}-	-			if (attr.get_name () == "display" && attr.get_content () == "none") {-				hidden = true;-			}-		}-		-		style = SvgStyle.parse (null, tag.get_attributes ());-		-		if (hidden) {-			return;-		}-		-		bezier_points = new BezierPoints[1];-		bezier_points[0] = new BezierPoints ();-		bezier_points[0].type == 'L';-		bezier_points[0].x0 = x;-		bezier_points[0].y0 = y;--		g = MainWindow.get_current_glyph ();-		move_and_resize (bezier_points, 1, false, 1, g);-			-		p = CircleTool.create_circle (bezier_points[0].x0,-			bezier_points[0].y0, r, PointType.CUBIC);--		npl.add (p);-		-		foreach (Attribute attr in tag.get_attributes ()) {-			if (attr.get_name () == "transform") {-				transform_paths (attr.get_content (), npl);-			}-		}-		-		style.apply (npl);-		pl.append_paths (npl);-	}--	private void parse_ellipse (Tag tag, Layer pl) {-		Path p;-		double x, y, rx, ry;-		Glyph g;-		PathList npl;-		BezierPoints[] bezier_points;-		SvgStyle style = new SvgStyle ();-		bool hidden = false;-		-		npl = new PathList ();-		-		x = 0;-		y = 0;-		rx = 0;-		ry = 0;-		-		foreach (Attribute attr in tag.get_attributes ()) {			-			if (attr.get_name () == "cx") {-				x = parse_double (attr.get_content ());-			}-			-			if (attr.get_name () == "cy") {-				y = -parse_double (attr.get_content ());-			}--			if (attr.get_name () == "rx") {-				rx = parse_double (attr.get_content ());-			}--			if (attr.get_name () == "ry") {-				ry = parse_double (attr.get_content ());-			}-	-			if (attr.get_name () == "display" && attr.get_content () == "none") {-				hidden = true;-			}-		}-		-		style = SvgStyle.parse (null, tag.get_attributes ());-		-		if (hidden) {-			return;-		}-		-		bezier_points = new BezierPoints[1];-		bezier_points[0] = new BezierPoints ();-		bezier_points[0].type == 'L';-		bezier_points[0].x0 = x;-		bezier_points[0].y0 = y;--		g = MainWindow.get_current_glyph ();-		move_and_resize (bezier_points, 1, false, 1, g);-			-		p = CircleTool.create_ellipse (bezier_points[0].x0,-			bezier_points[0].y0, rx, ry, PointType.CUBIC);--		npl.add (p);-		-		foreach (Attribute attr in tag.get_attributes ()) {-			if (attr.get_name () == "transform") {-				transform_paths (attr.get_content (), npl);-			}-		}-		-		style.apply (npl);-		pl.append_paths (npl);-	}--	private void parse_line (Tag tag, Layer pl) {-		Path p;-		double x1, y1, x2, y2;-		BezierPoints[] bezier_points;-		Glyph g;-		PathList npl = new PathList ();-		SvgStyle style = new SvgStyle ();-		bool hidden = false;-		-		x1 = 0;-		y1 = 0;-		x2 = 0;-		y2 = 0;-			-		foreach (Attribute attr in tag.get_attributes ()) {			-			if (attr.get_name () == "x1") {-				x1 = parse_double (attr.get_content ());-			}-			-			if (attr.get_name () == "y1") {-				y1 = -parse_double (attr.get_content ());-			}--			if (attr.get_name () == "x2") {-				x2 = parse_double (attr.get_content ());-			}-			-			if (attr.get_name () == "xy") {-				y2 = -parse_double (attr.get_content ());-			}-	-			if (attr.get_name () == "display" && attr.get_content () == "none") {-				hidden = true;-			}-		}-		-		style = SvgStyle.parse (null, tag.get_attributes ());-		-		if (hidden) {-			return;-		}--		bezier_points = new BezierPoints[2];-		bezier_points[0] = new BezierPoints ();-		bezier_points[0].type == 'L';-		bezier_points[0].x0 = x1;-		bezier_points[0].y0 = y1;--		bezier_points[1] = new BezierPoints ();-		bezier_points[1].type == 'L';-		bezier_points[1].x0 = x2;-		bezier_points[1].y0 = y2;-		-		g = MainWindow.get_current_glyph ();-		move_and_resize (bezier_points, 4, false, 1, g);-					-		p = new Path ();	-		-		p.add (bezier_points[0].x0, bezier_points[0].y0);-		p.add (bezier_points[1].x0, bezier_points[1].y0);-						-		p.close ();-		p.create_list ();-		p.recalculate_linear_handles ();		-		-		npl.add (p);-		-		foreach (Attribute attr in tag.get_attributes ()) {-			if (attr.get_name () == "transform") {-				transform_paths (attr.get_content (), npl);-			}-		}-		-		style.apply (npl);-		pl.append_paths (npl);-	}-		-	private void parse_rect (Tag tag, Layer layer) {-		Path p;-		double x, y, x2, y2;-		BezierPoints[] bezier_points;-		Glyph g;-		PathList npl = new PathList ();-		SvgStyle style = new SvgStyle ();-		bool hidden = false;-		EditPoint ep;-		-		x = 0;-		y = 0;-		x2 = 0;-		y2 = 0;-			-		foreach (Attribute attr in tag.get_attributes ()) {			-			if (attr.get_name () == "x") {-				x = parse_double (attr.get_content ());-			}-			-			if (attr.get_name () == "y") {-				y = -parse_double (attr.get_content ());-			}--			if (attr.get_name () == "width") {-				x2 = parse_double (attr.get_content ());-			}-			-			if (attr.get_name () == "height") {-				y2 = -parse_double (attr.get_content ());-			}-	-			if (attr.get_name () == "display" && attr.get_content () == "none") {-				hidden = true;-			}-		}-		-		style = SvgStyle.parse (null, tag.get_attributes ());-		-		if (hidden) {-			return;-		}-		-		x2 += x;-		y2 += y;--		bezier_points = new BezierPoints[4];-		bezier_points[0] = new BezierPoints ();-		bezier_points[0].type == 'L';-		bezier_points[0].x0 = x;-		bezier_points[0].y0 = y;--		bezier_points[1] = new BezierPoints ();-		bezier_points[1].type == 'L';-		bezier_points[1].x0 = x2;-		bezier_points[1].y0 = y;--		bezier_points[2] = new BezierPoints ();-		bezier_points[2].type == 'L';-		bezier_points[2].x0 = x2;-		bezier_points[2].y0 = y2;--		bezier_points[3] = new BezierPoints ();-		bezier_points[3].type == 'L';-		bezier_points[3].x0 = x;-		bezier_points[3].y0 = y2;-		-		g = MainWindow.get_current_glyph ();-		move_and_resize (bezier_points, 4, false, 1, g);-					-		p = new Path ();	-		-		ep = p.add (bezier_points[0].x0, bezier_points[0].y0);-		ep.set_point_type (PointType.CUBIC);-		-		ep = p.add (bezier_points[1].x0, bezier_points[1].y0);-		ep.set_point_type (PointType.CUBIC);-		-		ep = p.add (bezier_points[2].x0, bezier_points[2].y0);-		ep.set_point_type (PointType.CUBIC);-		-		ep = p.add (bezier_points[3].x0, bezier_points[3].y0);-		ep.set_point_type (PointType.CUBIC);-						-		p.close ();-		p.create_list ();-		p.recalculate_linear_handles ();		-		-		npl.add (p);-		-		// FIXME: right layer for other transforms-		foreach (Attribute attr in tag.get_attributes ()) {-			if (attr.get_name () == "transform") {-				transform_paths (attr.get_content (), npl);-			}-		}-		-		style.apply (npl);-		layer.append_paths (npl);-	}-	-	private void parse_polygon (Tag tag, Layer layer) {-		PathList path_list = get_polyline (tag);-		-		foreach (Path p in path_list.paths) {-			p.close ();-		}-		-		layer.append_paths (path_list);-	}--	-	private void parse_polyline (Tag tag, Layer layer) {	-		layer.append_paths (get_polyline (tag));-	}-	-	private PathList get_polyline (Tag tag) {-		Path p = new Path ();-		bool hidden = false;-		PathList path_list = new PathList ();-		SvgStyle style = new SvgStyle ();-				-		foreach (Attribute attr in tag.get_attributes ()) {-			if (attr.get_name () == "points") {-				p = parse_poly_data (attr.get_content ());-			}-	-			if (attr.get_name () == "display" && attr.get_content () == "none") {-				hidden = true;-			}-		}--		style = SvgStyle.parse (null, tag.get_attributes ());-		-		if (hidden) {-			return path_list;-		}-		-		path_list.add (p);-		style.apply (path_list);-		-		foreach (Attribute attr in tag.get_attributes ()) {-			if (attr.get_name () == "transform") {-				transform_paths (attr.get_content (), path_list);-			}-		}-		-		return path_list;-	}-	-	private void parse_path (Tag tag, Layer layer) {-		Glyph glyph = MainWindow.get_current_glyph ();-		PathList path_list = new PathList ();-		SvgStyle style = new SvgStyle ();-		bool hidden = false;--		foreach (Attribute attr in tag.get_attributes ()) {-			if (attr.get_name () == "d") {-				path_list = parse_svg_data (attr.get_content (), glyph);-			}-	-			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;-			}-		}-		-		style = SvgStyle.parse (null, tag.get_attributes ());-		-		if (hidden) {-			return;-		}-		-		foreach (Path path in path_list.paths) {-			layer.add_path (path);-		}-		-		style.apply (path_list);--		// assume the even odd rule is applied and convert the path-		// to a path using the non-zero rule-		int inside_count;-		bool inside;-		foreach (Object o1 in layer.objects) {-			if (o1 is PathObject) {-				Path p1 = ((PathObject) o1).get_path ();-				inside_count = 0;-				-				foreach (Object o2 in layer.objects) {-					if (o2 is PathObject) {-						Path p2 = ((PathObject) o2).get_path ();-					-						if (p1 != p2) {-							inside = true;-							-							foreach (EditPoint ep in p1.points) {-								if (!is_inside (ep, p2)) {-									inside = false;-								}-							}--							if (inside) {-								inside_count++; -							}-						}-					}-				}-				-				if (inside_count % 2 == 0) {-					p1.force_direction (Direction.CLOCKWISE);-				} else {-					p1.force_direction (Direction.COUNTER_CLOCKWISE);-				}-			}-		}-		-		foreach (Attribute attr in tag.get_attributes ()) {-			if (attr.get_name () == "transform") {-				transform_paths (attr.get_content (), path_list);-			}-		}-	}--	public static void create_lines_for_segment (Path path, EditPoint start, EditPoint end, double tolerance) {-		double x1, x2, x3;-		double y1, y2, y3;-		double step_start, step, step_end;--		path.add (start.x, start.y);--		step_start = 0;-		step = 0.5;-		step_end = 1;-					-		while (true) {-			Path.get_point_for_step (start, end, step_start, out x1, out y1);-			Path.get_point_for_step (start, end, step, out x2, out y2);-			Path.get_point_for_step (start, end, step_end, out x3, out y3);-		-			if (!StrokeTool.is_flat (x1, y1, x2, y2, x3, y3, tolerance)-				&& step_end - step / 2.0 > step_start -				&& step_end - step / 2.0 > 0.1-				&& step > 0.05-				&& Path.distance_to_point (start, end) > 1) {-				-				step /= 2.0;-	-				if (step < 0.05) {-					step = 0.05;-				} else {-					step_end = step_start + 2 * step;-				}-			} else {-				path.add (x3, y3);-				-				if (step_end + step < 1) {-					step_start = step_end;-					step_end += step;-				} else {-					break;-				}-			}-		}-	}--	public static Path get_lines (Path p) {-		EditPoint start;-		Path path = new Path ();-		-		if (p.points.size == 0) {-			return path;-		}-		-		// create a set of straight lines-		start = p.points.get (p.points.size - 1);-		-		foreach (EditPoint end in p.points) {-			create_lines_for_segment (path, start, end, 1);-			start = end;-		}-						-		return path;-	}--	/** Check if a point is inside using the even odd fill rule.-	 * The path should only have straight lines.-	 */-	public static bool is_inside (EditPoint point, Path path) {-		EditPoint prev;-		bool inside = false;-		-		if (path.points.size <= 1) {-			return false;-		}--		if (!(path.xmin <= point.x <= path.xmax)) {-			return false;-		}-		-		if (!(path.ymin <= point.y <= path.ymax)) {-			return false;-		}-				-		prev = path.points.get (path.points.size - 1);-		-		foreach (EditPoint p in path.points) {-			if  ((p.y > point.y) != (prev.y > point.y) -				&& point.x < (prev.x - p.x) * (point.y - p.y) / (prev.y - p.y) + p.x) {-				inside = !inside;-			}-			-			prev = p;-		}-		-		return inside;-	}--	/** Add space as separator to svg data. -	 * @param d svg data-	 */-	public static string add_separators (string d) {-		string data = d;-		-		data = data.replace (",", " ");-		data = data.replace ("a", " a ");-		data = data.replace ("A", " A ");-		data = data.replace ("m", " m ");-		data = data.replace ("M", " M ");-		data = data.replace ("h", " h ");-		data = data.replace ("H", " H ");-		data = data.replace ("v", " v ");-		data = data.replace ("V", " V ");-		data = data.replace ("l", " l ");-		data = data.replace ("L", " L ");-		data = data.replace ("q", " q ");-		data = data.replace ("Q", " Q ");		-		data = data.replace ("c", " c ");-		data = data.replace ("C", " C ");-		data = data.replace ("t", " t ");-		data = data.replace ("T", " T ");-		data = data.replace ("s", " s ");-		data = data.replace ("S", " S ");-		data = data.replace ("zM", " z M ");-		data = data.replace ("zm", " z m ");-		data = data.replace ("z", " z ");-		data = data.replace ("Z", " Z ");-		data = data.replace ("-", " -");-		data = data.replace ("e -", "e-"); // minus can be either separator or a negative exponent-		data = data.replace ("\t", " ");-		data = data.replace ("\r\n", " ");-		data = data.replace ("\n", " ");-		-		// use only a single space as separator-		while (data.index_of ("  ") > -1) {-			data = data.replace ("  ", " ");-		}-		-		return data;-	}-	-	public void add_path_to_glyph (string d, Glyph g, bool svg_glyph = false, double units = 1) {-		PathList p = parse_svg_data (d, g, svg_glyph, units);-		foreach (Path path in p.paths) {-			g.add_path (path);-		}-	}--	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[] c;-		double arc_rx, arc_ry;-		double arc_rotation;-		int large_arc;-		int arc_sweep;-		double arc_dest_x, arc_dest_y;-		-		int bi = 0;--		string data = add_separators (point_data);-		c = data.split (" ");--		// 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 < bezier_points_length; i++) {-			bezier_points[i] = new BezierPoints ();-		}--		// parse path-		int i = -1;-		while (++i < c.length && bi < bezier_points.length) {	-			if (c[i] == "m") {-				while (i + 2 < c.length && is_point (c[i + 1])) {-					bezier_points[bi].type = 'M';-					bezier_points[bi].svg_type = 'm';-					-					px += parse_double (c[++i]);-					-					if (svg_glyph) {-						py += parse_double (c[++i]);-					} else {-						py += -parse_double (c[++i]);-					}-					-					bezier_points[bi].x0 = px;-					bezier_points[bi].y0 = py;-					bi++;-				}-			} else if (c[i] == "M") {-				while (i + 2 < c.length && is_point (c[i + 1])) {-					bezier_points[bi].type = 'M';-					bezier_points[bi].svg_type = 'M';-					-					px = parse_double (c[++i]);-					-					if (svg_glyph) {-						py = parse_double (c[++i]);-					} else {-						py = -parse_double (c[++i]);-					}-					-					bezier_points[bi].x0 = px;-					bezier_points[bi].y0 = py;-					bi++;-				}-			} else if (c[i] == "h") {-				while (i + 1 < c.length && is_point (c[i + 1])) {-					bezier_points[bi].type = 'L';-					bezier_points[bi].svg_type = 'h';-					-					px += parse_double (c[++i]);--					bezier_points[bi].x0 = px;-					bezier_points[bi].y0 = py;-					bi++;-				}-			} else if (i + 1 < c.length && c[i] == "H") {-				while (is_point (c[i + 1])) {-					bezier_points[bi].type = 'L';-					bezier_points[bi].svg_type = 'H';-					-					px = parse_double (c[++i]);--					bezier_points[bi].x0 = px;-					bezier_points[bi].y0 = py;-					bi++;-				}-			} else if (c[i] == "v") {-				while (i + 1 < c.length && is_point (c[i + 1])) {-					bezier_points[bi].type = 'L';-					bezier_points[bi].svg_type = 'v';-										-					if (svg_glyph) {-						py = py + parse_double (c[++i]);-					} else {-						py = py - parse_double (c[++i]);-					}-					-					bezier_points[bi].x0 = px;-					bezier_points[bi].y0 = py;-					bi++;-				}				-			} else if (i + 1 < c.length && c[i] == "V") {-				while (is_point (c[i + 1])) {-					bezier_points[bi].type = 'L';-					bezier_points[bi].svg_type = 'V';-										-					if (svg_glyph) {-						py = parse_double (c[++i]);-					} else {-						py = -parse_double (c[++i]);-					}-					-					bezier_points[bi].x0 = px;-					bezier_points[bi].y0 = py;-					bi++;-				}-			} else if (c[i] == "l") {-				while (i + 2 < c.length && is_point (c[i + 1])) {-					bezier_points[bi].type = 'L';-					bezier_points[bi].svg_type = 'l';-										-					cx = px + parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = py + parse_double (c[++i]);-					} else {-						cy = py - parse_double (c[++i]);-					}-					-					px = cx;-					py = cy;--					bezier_points[bi].x0 = cx;-					bezier_points[bi].y0 = cy;-					bi++;-				}-			} else if (c[i] == "L") {-				while (i + 2 < c.length && is_point (c[i + 1])) {-					bezier_points[bi].type = 'L';-					bezier_points[bi].svg_type = 'L';-										-					cx = parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = parse_double (c[++i]);-					} else {-						cy = -parse_double (c[++i]);-					}-					-					px = cx;-					py = cy;-					-					bezier_points[bi].x0 = cx;-					bezier_points[bi].y0 = cy;-					bi++;				-				}	-			} else if (c[i] == "c") {-				while (i + 6 < c.length && is_point (c[i + 1])) {-					bezier_points[bi].type = 'C';-					bezier_points[bi].svg_type = 'C';-										-					cx = px + parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = py + parse_double (c[++i]);-					} else {-						cy = py - parse_double (c[++i]);-					}-					-					bezier_points[bi].x0 = cx;-					bezier_points[bi].y0 = cy;-										-					cx = px + parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = py + parse_double (c[++i]);-					} else {-						cy = py - parse_double (c[++i]);-					}-					-					px2 = cx;-					py2 = cy;-										-					bezier_points[bi].x1 = px2;-					bezier_points[bi].y1 = py2;-										-					cx = px + parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = py + parse_double (c[++i]);-					} else {-						cy = py + -parse_double (c[++i]);-					}-					-					bezier_points[bi].x2 = cx;-					bezier_points[bi].y2 = cy;-										-					px = cx;-					py = cy;-					-					bi++;-				}-			} else if (c[i] == "C") {-				while (i + 6 < c.length && is_point (c[i + 1])) {-					bezier_points[bi].type = 'C';-					bezier_points[bi].svg_type = 'C';-										-					cx = parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = parse_double (c[++i]);-					} else {-						cy = -parse_double (c[++i]);-					}-									-					bezier_points[bi].x0 = cx;-					bezier_points[bi].y0 = cy;-					-					cx = parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = parse_double (c[++i]);-					} else {-						cy = -parse_double (c[++i]);-					}-					-					px2 = cx;-					py2 = cy;-					-					bezier_points[bi].x1 = cx;-					bezier_points[bi].y1 = cy;-										-					cx = parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = parse_double (c[++i]);-					} else {-						cy = -parse_double (c[++i]);-					}-					-					bezier_points[bi].x2 = cx;-					bezier_points[bi].y2 = cy;-										-					px = cx;-					py = cy;-					-					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';-										-					cx = px + parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = py + parse_double (c[++i]);-					} else {-						cy = py - parse_double (c[++i]);-					}-					-					bezier_points[bi].x0 = cx;-					bezier_points[bi].y0 = cy;-										-					px2 = cx;-					py2 = cy;-										-					cx = px + parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = py + parse_double (c[++i]);-					} else {-						cy = py - parse_double (c[++i]);-					}-					-					bezier_points[bi].x1 = cx;-					bezier_points[bi].y1 = cy;-										-					px = cx;-					py = cy;-					-					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';-										-					cx = parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = parse_double (c[++i]);-					} else {-						cy = -parse_double (c[++i]);-					}-					-					bezier_points[bi].x0 = cx;-					bezier_points[bi].y0 = cy;-					-					px2 = cx;-					py2 = cy;-										-					cx = parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = parse_double (c[++i]);-					} else {-						cy = -parse_double (c[++i]);-					}-					-					px = cx;-					py = cy;--					bezier_points[bi].x1 = cx;-					bezier_points[bi].y1 = cy;-										-					bi++;					-				}	-			} else if (c[i] == "t") {-				while (i + 2 < c.length && is_point (c[i + 1])) {-					bezier_points[bi].type = 'Q';-					bezier_points[bi].svg_type = 't';-										-					// the first point is the reflection-					cx = 2 * px - px2;-					cy = 2 * py - py2; // if (svg_glyph) ?-					-					bezier_points[bi].x0 = cx;-					bezier_points[bi].y0 = cy;-					-					px2 = cx;-					py2 = cy;-										-					cx = px + parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = py + parse_double (c[++i]);-					} else {-						cy = py - parse_double (c[++i]);-					}-					-					px = cx;-					py = cy;-					-					bezier_points[bi].x1 = px;-					bezier_points[bi].y1 = py;-										-					bi++;-				}-			} else if (c[i] == "T") {-				while (i + 2 < c.length && is_point (c[i + 1])) {-					bezier_points[bi].type = 'Q';-					bezier_points[bi].svg_type = 'T';-										-					// the reflection-					cx = 2 * px - px2;-					cy = 2 * py - py2;-					-					bezier_points[bi].x0 = cx;-					bezier_points[bi].y0 = cy;-										-					px2 = cx;-					py2 = cy;-					-					cx = parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = parse_double (c[++i]);-					} else {-						cy = -parse_double (c[++i]);-					}-					-					px = cx;-					py = cy;-					-					bezier_points[bi].x1 = px;-					bezier_points[bi].y1 = py;-										-					bi++;				-				}-			} else if (c[i] == "s") {-				while (i + 4 < c.length && is_point (c[i + 1])) {-					bezier_points[bi].type = 'C';-					bezier_points[bi].svg_type = 's';-										-					// the first point is the reflection-					cx = 2 * px - px2;-					cy = 2 * py - py2; // if (svg_glyph) ?--					bezier_points[bi].x0 = cx;-					bezier_points[bi].y0 = cy;-															-					cx = px + parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = py + parse_double (c[++i]);-					} else {-						cy = py - parse_double (c[++i]);-					}-					-					px2 = cx;-					py2 = cy;-					-					bezier_points[bi].x1 = px2;-					bezier_points[bi].y1 = py2;-					-					cx = px + parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = py + parse_double (c[++i]);-					} else {-						cy = py - parse_double (c[++i]);-					}-					-					bezier_points[bi].x2 = cx;-					bezier_points[bi].y2 = cy;-												-					px = cx;-					py = cy;-					-					bi++;-				}-			} else if (c[i] == "S") {-				while (i + 4 < c.length && is_point (c[i + 1])) {-					bezier_points[bi].type = 'C';-					bezier_points[bi].svg_type = 'S';-										-					// the reflection-					cx = 2 * px - px2;-					cy = 2 * py - py2; // if (svg_glyph) ?			--					bezier_points[bi].x0 = cx;-					bezier_points[bi].y0 = cy;-					-					// the other two are regular cubic points-					cx = parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = parse_double (c[++i]);-					} else {-						cy = -parse_double (c[++i]);-					}-					-					px2 = cx;-					py2 = cy;-					-					bezier_points[bi].x1 = px2;-					bezier_points[bi].y1 = py2;-					-					cx = parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = parse_double (c[++i]);-					} else {-						cy = -parse_double (c[++i]);-					}-					-					bezier_points[bi].x2 = cx;-					bezier_points[bi].y2 = cy;-					-					px = cx;-					py = cy;	-					-					bi++;-				}-			} else if (c[i] == "a") {-				while (i + 7 < c.length && is_point (c[i + 1])) {					-					arc_rx = parse_double (c[++i]);-					arc_ry = parse_double (c[++i]);-					-					arc_rotation = parse_double (c[++i]);-					large_arc = parse_int (c[++i]);-					arc_sweep = parse_int (c[++i]);-							-					cx = px + parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = py + parse_double (c[++i]);-					} else {-						cy = py - parse_double (c[++i]);-					}-					-					arc_dest_x = cx;-					arc_dest_y = cy;-					-					add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy);-					-					px = cx;-					py = cy;-				}-			} else if (i + 7 < c.length && c[i] == "A") {-				while (is_point (c[i + 1])) {					-					arc_rx = parse_double (c[++i]);-					arc_ry = parse_double (c[++i]);-					-					arc_rotation = parse_double (c[++i]);-					large_arc = parse_int (c[++i]);-					arc_sweep = parse_int (c[++i]);-							-					cx = parse_double (c[++i]);-					-					if (svg_glyph) {-						cy = parse_double (c[++i]);-					} else {-						cy = -parse_double (c[++i]);-					}-					-					arc_dest_x = cx;-					arc_dest_y = cy;-					-					add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy);--					px = cx;-					py = cy;-					-					-				}-			} else if (c[i] == "z") {-				bezier_points[bi].type = 'z';-				bezier_points[bi].svg_type = 'z';-				-				bi++;-			} else if (c[i] == "Z") {-				bezier_points[bi].type = 'z';-				bezier_points[bi].svg_type = 'z';-									-				bi++;-			} else if (c[i] == "") {-			} else if (c[i] == " ") {-			} else {-				warning (@"Unknown instruction: $(c[i])");-			}-		}-		-		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, points, svg_glyph, units, glyph);-		-		if (format == SvgFormat.ILLUSTRATOR) {-			path_list = create_paths_illustrator (bezier_points, points);-		} else {-			path_list = create_paths_inkscape (bezier_points, points);-		}--		// TODO: Find out if it is possible to tie handles.-		return path_list;-	}--	void move_and_resize (BezierPoints[] b, int num_b, bool svg_glyph, double units, Glyph glyph) {-		Font font = BirdFont.get_current_font ();-		-		for (int i = 0; i < num_b; i++) {-			// resize all points-			b[i].x0 *= units;-			b[i].y0 *= units;-			b[i].x1 *= units;-			b[i].y1 *= units;-			b[i].x2 *= units;-			b[i].y2 *= units;--			// move all points-			if (svg_glyph) {-				b[i].x0 += glyph.left_limit;-				b[i].y0 += font.base_line;-				b[i].x1 += glyph.left_limit;-				b[i].y1 += font.base_line;-				b[i].x2 += glyph.left_limit;-				b[i].y2 += font.base_line;-			} else {-				b[i].x0 += glyph.left_limit;-				b[i].y0 += font.top_position;-				b[i].x1 += glyph.left_limit;-				b[i].y1 += font.top_position;-				b[i].x2 += glyph.left_limit;-				b[i].y2 += font.top_position;-			}-		}-	}-	-	void find_last_handle (int start_index, BezierPoints[] b, int num_b, out double left_x, out double left_y, out PointType last_type) {-		BezierPoints last = new BezierPoints ();-		bool found = false;-		-		left_x = 0;-		left_y = 0;-		last_type = PointType.NONE;-		-		return_if_fail (b.length != 0);-		return_if_fail (b[0].type != 'z');-		return_if_fail (num_b < b.length);--		if (num_b == 2) {-			left_x = b[0].x0 + (b[1].x0 - b[0].x0) / 3.0;-			left_y = b[0].y0 + (b[1].y0 - b[0].y0) / 3.0;-			last_type = PointType.LINE_CUBIC;-			return;-		}-		-		for (int i = start_index; i < num_b; i++) {-			switch (b[i].type) {-				case 'Q':-					break;-				case 'C':-					break;-				case 'z':-					found = true;-					break;-				default:-					break;-			}-			-			if (found || i + 1 == num_b) {-				-				return_if_fail (i >= 1);-				-				if (b[i - 1].type == 'Q') {-					return_if_fail (i >= 1);-					left_x = b[i - 1].x0;-					left_y = b[i - 1].y0;-					last_type = PointType.QUADRATIC;-				} else if (b[i - 1].type == 'C') {-					return_if_fail (i >= 1);-					left_x = b[i - 1].x1;-					left_y = b[i - 1].y1;-					last_type = PointType.CUBIC;-				} else if (b[i - 1].type == 'S') {-					return_if_fail (i >= 1);-					left_x = b[i - 1].x1;-					left_y = b[i - 1].y1;-					last_type = PointType.CUBIC;-				} else if (b[i - 1].type == 'L' || last.type == 'M') {-					return_if_fail (i >= 2); // FIXME: -2 can be C or L-					left_x = b[i - 2].x0 + (b[i - 1].x0 - b[i - 2].x0) / 3.0;-					left_y = b[i - 2].y0 + (b[i - 1].y0 - b[i - 2].y0) / 3.0;-					last_type = PointType.LINE_CUBIC;-				} else {-					warning (@"Unexpected type. $(b[i - 1])\n");-				}-				return;	-			}-			-			last = b[i];-		}-		-		warning ("Last point not found.");-	}--	PathList create_paths_inkscape (BezierPoints[] b, int num_b) {-		double last_x;-		double last_y; -		PointType last_type;-		Path path;-		PathList path_list = new PathList ();-		EditPoint ep = new EditPoint ();-		Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> ();-				-		path = new Path ();-		-		if (num_b == 0) {-			warning ("No SVG data");-			return path_list;-		}--		if (b[0].type != 'M') {-			warning ("Path must begin with M or m.");-			return path_list;-		}-		-		find_last_handle (0, b, num_b, out last_x, out last_y, out last_type);--		for (int i = 0; i < num_b; i++) {-			if (b[i].type == '\0') {-				warning ("Parser error.");-				return path_list;-			}--			if (b[i].type == 'z') {-				path.close ();-				path.create_list ();-				path.recalculate_linear_handles ();-				path_list.add (path);-				path = new Path ();-				-				if (i + 1 >= num_b) {-					break;-				} else {-					find_last_handle (i + 1, b, num_b, out last_x, out last_y, out last_type);-				}-			}-			-			if (i >= num_b) {-				break;-			}-			-			if (b[i].type == 'M') {-				ep = path.add (b[i].x0, b[i].y0);-				ep.set_point_type (PointType.CUBIC);--				ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);-				-				if (i == 0 || (b[i - 1].type == 'z')) {-					ep.get_left_handle ().set_point_type (last_type);-					ep.get_left_handle ().move_to_coordinate (last_x, last_y);-				} else {-					if (b[i - 1].type == 'C' || b[i - 1].type == 'S') {-						ep.get_left_handle ().set_point_type (PointType.CUBIC);-						ep.get_left_handle ().move_to_coordinate (b[i + 1].x1, b[i + 1].y1);-					} -					-					if (b[i + 1].type == 'C' || b[i - 1].type == 'S') {-						ep.get_right_handle ().set_point_type (PointType.CUBIC);-						ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);-					} else if (b[i + 1].type == 'L' || b[i + 1].type == 'M') {-						ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);					-					}-				}-			}--			if (b[i].type == 'L') {-				return_val_if_fail (i != 0, path_list);-				-				ep = path.add (b[i].x0, b[i].y0);-				ep.set_point_type (PointType.CUBIC);-				ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);-				ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);--				if (b[i + 1].type == 'L' || b[i + 1].type == 'M' || b[i + 1].type == 'z') {-					ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);-				}--				if (b[i -1].type == 'L' || b[i - 1].type == 'M') {-					ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);-				}-			}-			-			if (b[i].type == 'Q') {-				return_val_if_fail (i != 0, path_list);--				ep.set_point_type (PointType.QUADRATIC);-				-				ep.get_right_handle ().set_point_type (PointType.QUADRATIC);-				ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0);-				-				if (b[i + 1].type != 'z') {-					ep = path.add (b[i].x1, b[i].y1);--					ep.get_left_handle ().set_point_type (PointType.QUADRATIC);-					ep.get_left_handle ().move_to_coordinate (b[i].x0, b[i].y0);-				}-			}-	-			if (b[i].type == 'C' || b[i].type == 'S') {-				return_val_if_fail (i != 0, path_list);--				ep.set_point_type (PointType.CUBIC);-				-				ep.get_right_handle ().set_point_type (PointType.CUBIC);-				ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0);-				-				if (b[i].type == 'S') {-					smooth_points.add (ep);-				}-				-				if (b[i + 1].type != 'z') {-					ep = path.add (b[i].x2, b[i].y2);--					ep.get_left_handle ().set_point_type (PointType.CUBIC);-					ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1);-				}-			}-		}--		foreach (EditPoint e in smooth_points) {-			e.set_point_type (PointType.LINE_DOUBLE_CURVE);-			e.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);-			e.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);-		}--		foreach (EditPoint e in smooth_points) {-			path.recalculate_linear_handles_for_point (e);-		}-		-		for (int i = 0; i < 3; i++) {-			foreach (EditPoint e in smooth_points) {-				e.set_tie_handle (true);-				e.process_tied_handle ();-			}-		}-		-		if (path.points.size > 0) {-			path_list.add (path);-		}--		foreach (Path p in path_list.paths) {-			p.remove_points_on_points ();-		}-				-		return path_list;-	}--	PathList create_paths_illustrator (BezierPoints[] b, int num_b) {-		Path path;-		PathList path_list = new PathList ();-		EditPoint ep;-		bool first_point = true;-		double first_left_x, first_left_y;-		Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> ();-		-		if (num_b > b.length) {-			warning ("num_b > b.length: $num_b > $(b.length)");-			return path_list;-		}-		-		path = new Path ();-				-		if (num_b <= 1) {-			warning ("No SVG data");-			return path_list;-		}-		-		first_left_x = 0;-		first_left_y = 0;--		// FIXME: array boundaries-		for (int i = 0; i < num_b; i++) {			-			if (b[i].type == '\0') {-				warning ("Parser error.");-				return path_list;-			} else if (b[i].type == 'z') {-				path.close ();-				path.create_list ();-				-				int first_index = 1;--				for (int j = i - 1; j >= 1; j--) {-					if (b[j].type == 'z') {-						first_index = j + 1; // from z to M -					}-				}-				-				if (b[first_index].type == 'C' || b[first_index].type == 'S') {-					return_val_if_fail (path.points.size != 0, path_list);-					ep = path.points.get (path.points.size - 1);-					-					if (b[i - 1].type != 'L' ) {-						ep.get_right_handle ().set_point_type (PointType.CUBIC);-						ep.get_right_handle ().move_to_coordinate (b[first_index].x0, b[first_index].y0);-					}-				} else if (b[first_index].type == 'L') {-					return_val_if_fail (path.points.size != 0, path_list);-					ep = path.points.get (path.points.size - 1);-					ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);-					path.recalculate_linear_handles_for_point (ep);-				} else {-					warning ("Unexpected type: %s", (!) b[first_index].type.to_string ());-				}-				-				path.recalculate_linear_handles ();-				path_list.add (path);-				-				path = new Path ();-				first_point = true;				-			} else if (b[i].type == 'M') {-			} else if (b[i].type == 'L') {--				if (first_point) {-					first_left_x = b[i].x0;-					first_left_y = b[i].y0;-				}-				-				ep = path.add (b[i].x0, b[i].y0);-				ep.set_point_type (PointType.LINE_CUBIC); // TODO: quadratic-				ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);--				if (b[i -1].type == 'L' || first_point) {-					// ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);-				}-				-				if (b[i + 1].type == 'C' || b[i + 1].type == 'S') {-					return_val_if_fail (i + 1 < num_b, path_list);-					ep.get_right_handle ().set_point_type (PointType.CUBIC);-					ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);-				}-				-				first_point = false;-			} else if (b[i].type == 'Q') {-				warning ("Illustrator does not support quadratic control points.");-				warning (@"$(b[i])\n");-			} else if (b[i].type == 'C' || b[i].type == 'S') {-				-				if (first_point) {-					first_left_x = b[i].x0;-					first_left_y = b[i].y0;-				}-				-				ep = path.add (b[i].x2, b[i].y2);-				ep.set_point_type (PointType.CUBIC);--				ep.get_right_handle ().set_point_type (PointType.CUBIC);-				ep.get_left_handle ().set_point_type (PointType.CUBIC);-				-				ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1);--				if (b[i].type == 'S') {-					smooth_points.add (ep);-				}		--				if (b[i + 1].type != 'z') {-					ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);-				} else {-					ep.get_right_handle ().move_to_coordinate (first_left_x, first_left_y);-				}-								-				first_point = false;-			} else {-				warning ("Unknown control point type.");-				warning (@"$(b[i])\n");-			}-		}-		-		foreach (EditPoint e in smooth_points) {-			e.set_point_type (PointType.LINE_DOUBLE_CURVE);-			e.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);-			e.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);-		}--		foreach (EditPoint e in smooth_points) {-			path.recalculate_linear_handles_for_point (e);-		}-		-		for (int i = 0; i < 3; i++) {-			foreach (EditPoint e in smooth_points) {-				e.set_tie_handle (true);-				e.process_tied_handle ();-			}-		}-				-		if (path.points.size > 0) {-			path_list.add (path);-		}-		-		foreach (Path p in path_list.paths) {-			p.remove_points_on_points ();-		}-		-		return path_list;-	}-	-	// TODO: implement a default svg parser--	static int parse_int (string? s) {-		if (is_null (s)) {-			warning ("null instead of string");-			return 0;-		}-		-		if (!is_point ((!) s)) {-			warning (@"Expecting an integer got: $((!) s)");-			return 0;-		}-		-		return int.parse ((!) s);-	}-	-	public static double parse_double (string? s) {-		if (unlikely (is_null (s))) {-			warning ("Got null instead of expected string.");-			return 0;-		}-		-		if (unlikely (!is_point ((!) s))) {-			warning (@"Expecting a double got: $((!) s)");-			return 0;-		}-		-		return double.parse ((!) s);-	}-	-	static bool is_point (string? s) {-		if (s == null) {-			warning ("s is null");-			return false;-		}-		-		return double.try_parse ((!) s);-	}-	-	Path parse_poly_data (string polygon_points) {-		string data = add_separators (polygon_points);-		string[] c = data.split (" ");-		Path path;-		BezierPoints[] bezier_points = new BezierPoints[c.length + 1];-		int bi;-		Glyph g;-		EditPoint ep;-		-		bi = 0;-		for (int i = 0; i < c.length - 1; i += 2) {	-			if (i + 1 >= c.length) {-				warning ("No y value.");-				break;-			}--			if (bi >= bezier_points.length) {-				warning ("End of bezier_points");-				break;-			}--			bezier_points[bi] = new BezierPoints ();-			bezier_points[bi].type = 'L';-			bezier_points[bi].x0 = parse_double (c[i]);-			bezier_points[bi].y0 = -parse_double (c[i + 1]);-			bi++;-		}-			-		g = MainWindow.get_current_glyph ();-		move_and_resize (bezier_points, bi, false, 1, g);-		-		path = new Path ();-		for (int i = 0; i < bi; i++) {	-			ep = path.add (bezier_points[i].x0, bezier_points[i].y0);-			ep.set_point_type (PointType.LINE_CUBIC);-		}-		-		path.create_list ();-		path.recalculate_linear_handles ();-		-		return path;-	}-}--}diff --git a/libbirdfont/Svg/SvgPath.vala b/libbirdfont/Svg/SvgPath.valadeleted file mode 100644index 6e965f5..0000000--- a/libbirdfont/Svg/SvgPath.vala+++ /dev/null@@ -1,88 +0,0 @@-/*-	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 SvgPath : Object {	-	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) {-		cr.save ();-		cr.new_path ();-		-		foreach (Points p in points) {-			cr.move_to (p.x, p.y);-			draw_points (cr, p);-			-			if (p.closed) {-				cr.close_path ();-			}-		}-			-		apply_transform (cr);		-		paint (cr);-		cr.restore ();-	}--	public void draw_points (Context cr, Points points) {-		Doubles p = points.point_data;-		-		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]);-		}		-	}--	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";-	}-}--}diff --git a/libbirdfont/Svg/SvgStyle.vala b/libbirdfont/Svg/SvgStyle.valadeleted file mode 100644index a59bd11..0000000--- a/libbirdfont/Svg/SvgStyle.vala+++ /dev/null@@ -1,161 +0,0 @@-/*-	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;-using Math;--namespace BirdFont {--public class SvgStyle {-	-	public Gee.HashMap<string, string> style;-	-	public Color? stroke = null;-	public Color? fill = null;-	public Gradient? stroke_gradient = null;-	public Gradient? fill_gradient = null;--	public double stroke_width = 0;-	-	public SvgStyle () {-		style = new Gee.HashMap<string, string> ();-	}-	-	public LineCap get_line_cap () {-		string l;-		-		if (!style.has_key ("stroke-linecap")) {-			return LineCap.BUTT;-		}-		-		l = style.get ("stroke-linecap");-		-		if (l == "round") {-			return LineCap.ROUND;-		} else if (l == "square") {-			return LineCap.SQUARE;-		}-		-		return LineCap.BUTT; 	-	}-	-	public bool has_stroke () {-		bool s = true;-		-		if (style.has_key ("stroke")) {-			s = style.get ("stroke") != "none";-		}-				-		return get_stroke_width () > 0 && s;-	}-		-	public double get_stroke_width () {-		if (!style.has_key ("stroke-width")) {-			return 0;-		}--		return double.parse (style.get ("stroke-width"));-	}-	-	public static SvgStyle parse (Defs? d, Attributes attributes) {-		SvgStyle s = new SvgStyle ();-		double fill_opacity = 1;-		double stroke_opacity = 1;-		-		foreach (Attribute a in attributes) {-			if (a.get_name () == "style") {-				s.parse_key_value_pairs (a.get_content ());-			}-			-			if (a.get_name () == "stroke-width") {-				s.style.set ("stroke-width", a.get_content ());-			}--			if (a.get_name () == "stroke") {-				s.style.set ("stroke", a.get_content ());-			}--			if (a.get_name () == "fill") {-				s.style.set ("fill", a.get_content ());-			}--			if (a.get_name () == "fill-opacity") {-				fill_opacity = SvgFile.parse_number (a.get_content ());-			}--			if (a.get_name () == "stroke-opacity") {-				stroke_opacity = SvgFile.parse_number (a.get_content ());-			}-		}-	-		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;-		}-		-		if (s.stroke != null) {-			Color color = (!) s.stroke;-			color.a = stroke_opacity;-		}-		-		return s;-	}-	-	void parse_key_value_pairs (string svg_style) {-		string[] p = svg_style.split (";");-		string[] pair;-		string k, v;-		-		foreach (string kv in p) {-			pair = kv.split (":");-			-			if (pair.length != 2) {-				warning ("pair.length != 2");-				continue;-			}-			-			k = pair[0];-			v = pair[1];-			-			style.set (k, v);-		}-	}-	-	public void apply (PathList path_list) {-		foreach (Path p in path_list.paths) {-			if (has_stroke ()) {-				p.stroke = get_stroke_width ();-			} else {-				p.stroke = 0;-			}-			-			p.line_cap = get_line_cap ();-			p.reset_stroke ();-			p.update_region_boundaries ();		-		}-	}-}--}diff --git a/libbirdfont/Svg/SvgTransform.vala b/libbirdfont/Svg/SvgTransform.valadeleted file mode 100644index 4721a7d..0000000--- a/libbirdfont/Svg/SvgTransform.vala+++ /dev/null@@ -1,75 +0,0 @@-/*-	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 Cairo;--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 () {-	}-	-	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;-	}-}--}diff --git a/libbirdfont/Svg/SvgTransforms.vala b/libbirdfont/Svg/SvgTransforms.valadeleted file mode 100644index e36e55b..0000000--- a/libbirdfont/Svg/SvgTransforms.vala+++ /dev/null@@ -1,47 +0,0 @@-/*-	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;-	}-}--}diff --git a/libbirdfont/SvgFont.vala b/libbirdfont/SvgFont.valanew file mode 100644index 0000000..7223c2f--- /dev/null+++ b/libbirdfont/SvgFont.vala@@ -0,0 +1,264 @@+/*+	Copyright (C) 2013 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.+*/++using B;++namespace BirdFont {++class SvgFont : GLib.Object {+	Font font;+	double units = 1;+	double font_advance = 0;+	+	public SvgFont (Font f) {+		this.font = f;+	}+	+	/** Load svg font from file. */+	public void load (string path) {+		string data;+		XmlParser xml_parser;+		try {+			FileUtils.get_contents (path, out data);+			xml_parser = new XmlParser (data);+			parse_svg_font (xml_parser.get_root_tag ());+		} catch (GLib.Error e) {+			warning (e.message);+		}+	}+	+	void parse_svg_font (Tag tag) {+		foreach (Tag t in tag) {+			if (t.get_name () == "defs") {+				parse_svg_font (t);+			}+			+			if (t.get_name () == "font") {+				parse_font_tag (t);+				parse_svg_font (t);+			}++			if (t.get_name () == "font-face") {+				parse_font_limits (t);+			}++			if (t.get_name () == "hkern") {+				parse_hkern (t);+			}+									+			if (t.get_name () == "glyph") {+				parse_glyph (t);+			}+		}		+	}++	void parse_hkern (Tag tag) {+		string left = "";+		string right = "";+		string left_name = "";+		string right_name = "";+		double kerning = 0;+		unichar l, r;+		StringBuilder sl, sr;+		GlyphRange grr, grl;+		KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes ();+		+		foreach (Attribute attr in tag.get_attributes ()) {+			// left+			if (attr.get_name () == "u1") {+				left = attr.get_content ();+			}	+					+			// right	+			if (attr.get_name () == "u2") {+				right = attr.get_content ();+			}++			if (attr.get_name () == "g1") {+				left_name = attr.get_content ();+			}+			+			if (attr.get_name () == "g2") {+				right_name = attr.get_content ();+			}+				+			// kerning+			if (attr.get_name () == "k") {+				kerning = double.parse (attr.get_content ()) * units;+			}+		}+				+		// FIXME: ranges and sequences for u1 & u2 + g1 & g2+		foreach (string lk in left.split (",")) {+			foreach (string rk in right.split (",")) {+				l = get_unichar (lk);+				r = get_unichar (rk);+				+				sl = new StringBuilder ();+				sl.append_unichar (l);+				+				sr = new StringBuilder ();+				sr.append_unichar (r);+				+				try {+					grl = new GlyphRange ();+					grl.parse_ranges (sl.str);+					+					grr = new GlyphRange ();+					grr.parse_ranges (sr.str);+				+					classes.set_kerning (grl, grr, -kerning);+				} catch (MarkupError e) {+					warning (e.message);+				}		+			}+		}+	}++	void parse_font_limits (Tag tag) {+		double top_limit = 0;+		double bottom_limit = 0;+		+		foreach (Attribute attr in tag.get_attributes ()) {+			if (attr.get_name () == "units-per-em") {+				units = 100.0 / double.parse (attr.get_content ());+			}	+		}++		foreach (Attribute attr in tag.get_attributes ()) {	+			if (attr.get_name () == "ascent") {+				top_limit = double.parse (attr.get_content ());+			}+			+			if (attr.get_name () == "descent") {+				bottom_limit = double.parse (attr.get_content ());+			}		+		}+		+		top_limit *= units;+		bottom_limit *= units;+		+		font.bottom_limit = bottom_limit;+		font.top_limit = top_limit;+	}+	+	void parse_font_tag (Tag tag) {+		foreach (Attribute attr in tag.get_attributes ()) {+			if (attr.get_name () == "horiz-adv-x") {+				font_advance = double.parse (attr.get_content ());+			}+						+			if (attr.get_name () == "id") {+				font.set_name (attr.get_content ());+			}+		}+	}+	+	/** Obtain unichar value from either a character, a hex representation of+	 *  a character or series of characters (f, &#x6d; or ffi).+	 */+	static unichar get_unichar (string val) {+		string v = val;+		unichar unicode_value;+		+		if (val == "&") {+			return '&';+		}+		+		// TODO: parse ligatures+		if (v.has_prefix ("&")) {+			// parse hex value+			v = v.substring (0, v.index_of (";"));+			v = v.replace ("&#x", "U+");+			v = v.replace (";", "");+			unicode_value = Font.to_unichar (v);+		} else {+			// obtain unicode value+			+			if (v.char_count () > 1) {+				warning ("font contains ligatures");+				return '\0';+			}+			+			unicode_value = v.get_char (0);+		}+		+		return unicode_value;	+	}+	+	bool is_ligature (string v) {+		if (v.has_prefix ("&")) {+			return false;+		}+		+		return v.char_count () > 1;+	}+	+	void parse_glyph (Tag tag) {+		unichar unicode_value = 0;+		string glyph_name = "";+		string svg = "";+		Glyph glyph;+		GlyphCollection glyph_collection;+		double advance = font_advance;+		string ligature = "";+		SvgParser parser = new SvgParser ();+		StringBuilder unicode_name; ++		parser.set_format (SvgFormat.INKSCAPE);++		foreach (Attribute attr in tag.get_attributes ()) {+			if (attr.get_name () == "unicode") {+				unicode_value = get_unichar (attr.get_content ());+				+				if (glyph_name == "") {+					glyph_name = attr.get_content ();+				}+				+				if (is_ligature (attr.get_content ())) {+					ligature = attr.get_content ();+				}+			}+			+			// svg data+			if (attr.get_name () == "d") {+				svg = attr.get_content ();+			}+			+			if (attr.get_name () == "glyph-name") {+				glyph_name = attr.get_content ();+			}++			if (attr.get_name () == "horiz-adv-x") {+				advance = double.parse (attr.get_content ());+			}+		}++		unicode_name = new StringBuilder ();+		unicode_name.append_unichar (unicode_value);++		glyph = new Glyph (unicode_name.str, unicode_value);+		parser.add_path_to_glyph (svg, glyph, true, units);			+		glyph.right_limit = glyph.left_limit + advance * units;+		+		// FIXME: add svg font ligatures+		+		glyph_collection = new GlyphCollection (unicode_value, glyph_name);+		glyph_collection.insert_glyph (glyph, true);+		+		font.add_glyph_collection (glyph_collection);+	}+}++}diff --git a/libbirdfont/SvgFontFormatWriter.vala b/libbirdfont/SvgFontFormatWriter.valanew file mode 100644index 0000000..b2e907b--- /dev/null+++ b/libbirdfont/SvgFontFormatWriter.vala@@ -0,0 +1,132 @@+/*+	Copyright (C) 2012, 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 {++class SvgFontFormatWriter : GLib.Object  {++	DataOutputStream os;++	public SvgFontFormatWriter () {+	}++	public void open (File file) throws Error {+		if (file.query_exists ()) {+			throw new FileError.EXIST ("SvgFontFormatWriter: file exists.");+		}+		+		os = new DataOutputStream (file.create(FileCreateFlags.REPLACE_DESTINATION));+	}++	public void close () throws Error {+		os.close ();+	}+	+	public void write_font_file (Font font) throws Error {+		string font_name = font.get_full_name ();+		+		int units_per_em = 100;+		+		int ascent = 80;+		int descent = -20;+		+		StringBuilder b;+		+		Glyph? g;+		Glyph glyph;+		unichar index = 0;+		+		string uni;+		+		KerningClasses classes;+		+		put ("""<?xml version="1.0" standalone="no"?>""");+		put ("""<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >""");+		put ("""<svg>""");++		// (metadata goes here)++		put ("<defs>");++		put (@"<font id=\"$font_name\" horiz-adv-x=\"250\" >");+		put (@"<font-face units-per-em=\"$(to_float (units_per_em))\" ascent=\"$(to_float (ascent))\" descent=\"$(to_float (descent))\" />");++		// (missing-glyph goes here)++		// regular glyphs +		while (true) {+			g = font.get_glyph_index (index++);+			+			if (g == null) {+				break;+			}+			+			glyph = (!) g;+			+			b = new StringBuilder ();+			b.append_unichar (glyph.get_unichar ());++			if (glyph.get_unichar () >= ' ' && b.str.validate ()) {+				if (b.str == "\"" || b.str == "&" || b.str == "<" || b.str == ">") {+					uni = Font.to_hex_code (glyph.get_unichar ());+					put (@"<glyph unicode=\"&#x$(uni);\" horiz-adv-x=\"$(to_float (glyph.get_width ()))\" d=\"$(glyph.get_svg_data ())\" />");			+				} else {+					put (@"<glyph unicode=\"$(b.str)\" horiz-adv-x=\"$(to_float (glyph.get_width ()))\" d=\"$(glyph.get_svg_data ())\" />");+				}+			}+		}+		+		// FIXME: ligatures+		classes = BirdFont.get_current_font ().get_kerning_classes ();+		classes.all_pairs ((kerning) => {+			string l, r;+			+			foreach (Kerning k in kerning.kerning) {+				try {+					if (k.glyph != null) {+						l = Font.to_hex_code (kerning.character.unichar_code);+						r = Font.to_hex_code (((!)k.glyph).unichar_code);	+						os.put_string (@"<hkern u1=\"&#x$l;\" u2=\"&#x$r;\" k=\"$(to_float (-k.val))\"/>\n");+					} else {+						warning ("No glyph.");+					}+				} catch (GLib.Error e) {+					warning (e.message);+				}+			}+		});	++		put ("</font>");+		put ("</defs>");+		put ("</svg>");+	}++	string to_float (double d) {+		string s = @"$d";+		if (s.index_of ("e") != -1) {+			return "0".dup ();+		}+		return s.replace (",", ".");+	}++	/** Write a new line */+	private void put (string line) throws Error {+		os.put_string (line);+		os.put_string ("\n");+	}++}+++}diff --git a/libbirdfont/SvgParser.vala b/libbirdfont/SvgParser.valanew file mode 100644index 0000000..f53d73c--- /dev/null+++ b/libbirdfont/SvgParser.vala@@ -0,0 +1,1559 @@+/*+	Copyright (C) 2012 2013 2014 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;+using Math;+using SvgBird;++namespace BirdFont {++public enum SvgFormat {+	NONE,+	INKSCAPE,+	ILLUSTRATOR+}++public enum SvgType {+	COLOR,+	REGULAR+}++public class SvgParser {+	+	SvgFormat format = SvgFormat.ILLUSTRATOR;+	+	public SvgParser () {+	}+	+	public void set_format (SvgFormat f) {+		format = f;+	}+	+	public static void import (SvgType type) {+		FileChooser fc = new FileChooser ();+		fc.file_selected.connect ((p) => {+			string path;+				+			if (p == null) {+				return;+			}+			+			path = (!) p;+			+			if (type == SvgType.REGULAR) {+				import_svg (path);+			} else if (type == SvgType.COLOR) {+				Glyph glyph = MainWindow.get_current_glyph ();+				import_color_svg (glyph, path);+			}+		});+		+		fc.add_extension ("svg");+		MainWindow.file_chooser (t_("Import"), fc, FileChooser.LOAD);+	}+		+	public static void import_color_svg (Glyph glyph, string path) {+		EmbeddedSvg drawing = SvgParser.parse_embedded_svg_file (path);+		+		Layer layer = new Layer ();+		layer.name = "SVG";+		layer.add_object (drawing);+		+		glyph.add_layer (layer);+		+		// FIXME: update GUI+	}+	+	public static void import_folder (SvgType type) {+		FileChooser fc = new FileChooser ();+		fc.file_selected.connect ((p) => {+			string path;+			File svg_folder;+			File svg;+			bool imported;+			FileEnumerator enumerator;+			FileInfo? file_info;+			string file_name;+			Font font;+			+			if (p == null) {+				return;+			}+			+			path = (!) p;+			svg_folder = File.new_for_path (path);+			font = BirdFont.get_current_font ();+				+			try {+				enumerator = svg_folder.enumerate_children (FileAttribute.STANDARD_NAME, 0);+				while ((file_info = enumerator.next_file ()) != null) {+					file_name = ((!) file_info).get_name ();+					+					if (file_name.has_suffix (".svg")) {+						svg = get_child (svg_folder, file_name);+						imported = import_svg_file (font, svg, type);+						+						if (!imported) {+							warning ("Can't import %s.", (!) svg.get_path ());+						} else {+							font.touch ();+						}+					}+				}+			} catch (Error e) {+				warning (e.message);+			}+		});+		+		MainWindow.file_chooser (t_("Import"), fc, FileChooser.LOAD | FileChooser.DIRECTORY);+	}+	+	public static void import_svg_data (string xml_data, SvgFormat format = SvgFormat.NONE) {+		PathList path_list = new PathList ();+		Glyph glyph; +		string[] lines = xml_data.split ("\n");+		bool has_format = false;+		SvgParser parser = new SvgParser ();+		XmlParser xmlparser;++		foreach (string l in lines) {+			if (l.index_of ("Illustrator") > -1 || l.index_of ("illustrator") > -1) {+				parser.set_format (SvgFormat.ILLUSTRATOR);+				has_format = true;+			}+			+			if (l.index_of ("Inkscape") > -1 || l.index_of ("inkscape") > -1) {+				parser.set_format (SvgFormat.INKSCAPE);+				has_format = true;+			}+		}+		+		if (format != SvgFormat.NONE) {+			parser.set_format (format);+		}++		// parse the file+		if (!has_format) {+			warn_if_test ("No format identifier found in SVG parser.\n");+		}++		xmlparser = new XmlParser (xml_data);+		+		if (!xmlparser.validate()) {+			warning("Invalid XML in SVG parser."); +		}+		+		path_list = parser.parse_svg_file (xmlparser.get_root_tag ());+	+		glyph = MainWindow.get_current_glyph ();+		foreach (Path p in path_list.paths) {+			PathObject path = new PathObject.for_path (p);+			glyph.add_object (path);+			glyph.add_active_object (null, path); // FIXME: groups+			path.update_region_boundaries ();+		}+		+		glyph.close_path ();	+	}+	+	public static string replace (string content, string start, string stop, string replacement) {+		int i_tag = content.index_of (start);+		int end_tag = content.index_of (stop, i_tag);+		string c = "";+		+		if (i_tag > -1) {+			c = content.substring (0, i_tag) +				+ replacement+				+ content.substring (end_tag + stop.length);+		} else {+			c = content;+		}+		+		return c;+	}+	+	public static void import_svg (string path) {+		string svg_data;+		try {+			FileUtils.get_contents (path, out svg_data);+		} catch (GLib.Error e) {+			warning (e.message);+		}+		import_svg_data (svg_data);+	}+	+	private PathList parse_svg_file (Tag tag) {+		Layer pl = new Layer ();+	+		foreach (Tag t in tag) {+			+			if (t.get_name () == "g") {+				parse_layer (t, pl);+			}++			if (t.get_name () == "switch") {+				parse_layer (t, pl);+			}+						+			if (t.get_name () == "path") {+				parse_path (t, pl);+			}+			+			if (t.get_name () == "polygon") {+				parse_polygon (t, pl);+			}++			if (t.get_name () == "polyline") {+				parse_polyline (t, pl);+			}+						+			if (t.get_name () == "circle") {+				parse_circle (t, pl);+			}+			+			if (t.get_name () == "ellipse") {+				parse_ellipse (t, pl);+			}++			if (t.get_name () == "line") {+				parse_line (t, pl);+			}			+		}+		+		return LayerUtils.get_all_paths (pl);+	}+	+	private void parse_layer (Tag tag, Layer pl) {+		Layer layer;+		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) {+			return;+		}+					+		foreach (Tag t in tag) {+			if (t.get_name () == "path") {+				parse_path (t, pl);+			}+			+			if (t.get_name () == "g") {+				layer = new Layer ();+				parse_layer (t, layer);+				pl.subgroups.add (layer);+			}+			+			if (t.get_name () == "polygon") {+				parse_polygon (t, pl);+			}++			if (t.get_name () == "polyline") {+				parse_polyline (t, pl);+			}+			+			if (t.get_name () == "rect") {+				parse_rect (t, pl);+			}++			if (t.get_name () == "circle") {+				parse_circle (t, pl);+			}++			if (t.get_name () == "ellipse") {+				parse_ellipse (t, pl);+			}+			+			if (t.get_name () == "line") {+				parse_line (t, pl);+			}+		}++		foreach (Attribute attr in tag.get_attributes ()) {	+			if (attr.get_name () == "transform") {+				transform (attr.get_content (), pl);+			}+		}+	}+	+	private void transform (string transform_functions, Layer layer) {+		PathList path_list = new PathList ();+		+		foreach (SvgBird.Object o in layer.objects) {+			if (o is PathObject) {+				path_list.add (((PathObject) o).get_path ());+			}+		}+		+		transform_paths (transform_functions, path_list);+		transform_subgroups (transform_functions, layer);+	}+	+	private void transform_subgroups (string transform_functions, Layer layer) {+		foreach (Layer subgroup in layer.subgroups) {+			transform (transform_functions, subgroup);+		}+	}+	+	private void transform_paths (string transform_functions, PathList pl) {+		string data = transform_functions.dup ();+		string[] functions;+				+		// use only a single space as separator+		while (data.index_of ("  ") > -1) {+			data = data.replace ("  ", " ");+		}+		+		return_if_fail (data.index_of (")") > -1);+		+		 // add separator+		data = data.replace (") ", "|");+		data = data.replace (")", "|"); +		functions = data.split ("|");+		+		for (int i = functions.length - 1; i >= 0; i--) {+			if (functions[i].has_prefix ("translate")) {+				translate (functions[i], pl);+			}+			+			if (functions[i].has_prefix ("scale")) {+				scale (functions[i], pl);+			}++			if (functions[i].has_prefix ("matrix")) {+				matrix (functions[i], pl);+			}+			+			// TODO: rotate etc.+		}+	}++	/** @param path a path in the cartesian coordinate system+	 * The other parameters are in the SVG coordinate system.+	 */+	public static void apply_matrix (Path path, double a, double b, double c, +		double d, double e, double f){+		+		double dx, dy;+		Font font = BirdFont.get_current_font ();+		Glyph glyph = MainWindow.get_current_glyph ();+		+		foreach (EditPoint ep in path.points) {+			ep.tie_handles = false;+			ep.reflective_point = false;+		}+		+		foreach (EditPoint ep in path.points) {+			apply_matrix_on_handle (ep.get_right_handle (), a, b, c, d, e, f);+			apply_matrix_on_handle (ep.get_left_handle (), a, b, c, d, e, f);++			ep.independent_y = font.top_position - ep.independent_y;+			ep.independent_x -= glyph.left_limit;+			+			dx = a * ep.independent_x + c * ep.independent_y + e;+			dy = b * ep.independent_x + d * ep.independent_y + f;+			+			ep.independent_x = dx;+			ep.independent_y = dy;+			+			ep.independent_y = font.top_position - ep.independent_y;+			ep.independent_x += glyph.left_limit;+		}+	}++	public static void apply_matrix_on_handle (EditPointHandle h, +		double a, double b, double c, +		double d, double e, double f){+		+		double dx, dy;+		Font font = BirdFont.get_current_font ();+		Glyph glyph = MainWindow.get_current_glyph ();++		h.y = font.top_position - h.y;+		h.x -= glyph.left_limit;+		+		dx = a * h.x + c * h.y + e;+		dy = b * h.x + d * h.y + f;+		+		h.x = dx;+		h.y = dy;+		+		h.y = font.top_position - h.y;+		h.x += glyph.left_limit;+	}+++	private void matrix (string function, PathList pl) {+		string parameters = get_transform_parameters (function);+		string[] p = parameters.split (" ");++		if (p.length != 6) {+			warning ("Expecting six parameters for matrix transformation.");+			return;+		}++		foreach (Path path in pl.paths) {+			apply_matrix (path, parse_double (p[0]), parse_double (p[1]), +				parse_double (p[2]), parse_double (p[3]), +				parse_double (p[4]), parse_double (p[5]));+		}+	}+		+	private void scale (string function, PathList pl) {+		string parameters = get_transform_parameters (function);+		string[] p = parameters.split (" ");+		double x, y;+		+		x = 1;+		y = 1;+		+		if (p.length > 0) {+			x = parse_double (p[0]);+		}+		+		if (p.length > 1) {+			y = parse_double (p[1]);+		}+		+		foreach (Path path in pl.paths) {+			path.scale (-x, y);+		}+	}+	+	private void translate (string function, PathList pl) {+		string parameters = get_transform_parameters (function);+		string[] p = parameters.split (" ");+		double x, y;+		+		x = 0;+		y = 0;+		+		if (p.length > 0) {+			x = parse_double (p[0]);+		}+		+		if (p.length > 1) {+			y = parse_double (p[1]);+		}+		+		foreach (Path path in pl.paths) {+			path.move (x, -y);+		}+	}++	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 void parse_circle (Tag tag, Layer pl) {+		Path p;+		double x, y, r;+		Glyph g;+		PathList npl;+		BezierPoints[] bezier_points;+		SvgStyle style = new SvgStyle ();+		bool hidden = false;+		+		npl = new PathList ();+		+		x = 0;+		y = 0;+		r = 0;+			+		foreach (Attribute attr in tag.get_attributes ()) {			+			if (attr.get_name () == "cx") {+				x = parse_double (attr.get_content ());+			}+			+			if (attr.get_name () == "cy") {+				y = -parse_double (attr.get_content ());+			}++			if (attr.get_name () == "r") {+				r = parse_double (attr.get_content ());+			}+	+			if (attr.get_name () == "display" && attr.get_content () == "none") {+				hidden = true;+			}+		}+		+		style = SvgStyle.parse (null, tag.get_attributes ());+		+		if (hidden) {+			return;+		}+		+		bezier_points = new BezierPoints[1];+		bezier_points[0] = new BezierPoints ();+		bezier_points[0].type == 'L';+		bezier_points[0].x0 = x;+		bezier_points[0].y0 = y;++		g = MainWindow.get_current_glyph ();+		move_and_resize (bezier_points, 1, false, 1, g);+			+		p = CircleTool.create_circle (bezier_points[0].x0,+			bezier_points[0].y0, r, PointType.CUBIC);++		npl.add (p);+		+		foreach (Attribute attr in tag.get_attributes ()) {+			if (attr.get_name () == "transform") {+				transform_paths (attr.get_content (), npl);+			}+		}+		+		npl.apply_style (style);+		append_paths (pl, npl);+	}++	private void parse_ellipse (Tag tag, Layer pl) {+		Path p;+		double x, y, rx, ry;+		Glyph g;+		PathList npl;+		BezierPoints[] bezier_points;+		SvgStyle style = new SvgStyle ();+		bool hidden = false;+		+		npl = new PathList ();+		+		x = 0;+		y = 0;+		rx = 0;+		ry = 0;+		+		foreach (Attribute attr in tag.get_attributes ()) {			+			if (attr.get_name () == "cx") {+				x = parse_double (attr.get_content ());+			}+			+			if (attr.get_name () == "cy") {+				y = -parse_double (attr.get_content ());+			}++			if (attr.get_name () == "rx") {+				rx = parse_double (attr.get_content ());+			}++			if (attr.get_name () == "ry") {+				ry = parse_double (attr.get_content ());+			}+	+			if (attr.get_name () == "display" && attr.get_content () == "none") {+				hidden = true;+			}+		}+		+		style = SvgStyle.parse (null, tag.get_attributes ());+		+		if (hidden) {+			return;+		}+		+		bezier_points = new BezierPoints[1];+		bezier_points[0] = new BezierPoints ();+		bezier_points[0].type == 'L';+		bezier_points[0].x0 = x;+		bezier_points[0].y0 = y;++		g = MainWindow.get_current_glyph ();+		move_and_resize (bezier_points, 1, false, 1, g);+			+		p = CircleTool.create_ellipse (bezier_points[0].x0,+			bezier_points[0].y0, rx, ry, PointType.CUBIC);++		npl.add (p);+		+		foreach (Attribute attr in tag.get_attributes ()) {+			if (attr.get_name () == "transform") {+				transform_paths (attr.get_content (), npl);+			}+		}+		+		npl.apply_style (style);+		append_paths (pl, npl);+	}++	private void parse_line (Tag tag, Layer pl) {+		Path p;+		double x1, y1, x2, y2;+		BezierPoints[] bezier_points;+		Glyph g;+		PathList npl = new PathList ();+		SvgStyle style = new SvgStyle ();+		bool hidden = false;+		+		x1 = 0;+		y1 = 0;+		x2 = 0;+		y2 = 0;+			+		foreach (Attribute attr in tag.get_attributes ()) {			+			if (attr.get_name () == "x1") {+				x1 = parse_double (attr.get_content ());+			}+			+			if (attr.get_name () == "y1") {+				y1 = -parse_double (attr.get_content ());+			}++			if (attr.get_name () == "x2") {+				x2 = parse_double (attr.get_content ());+			}+			+			if (attr.get_name () == "xy") {+				y2 = -parse_double (attr.get_content ());+			}+	+			if (attr.get_name () == "display" && attr.get_content () == "none") {+				hidden = true;+			}+		}+		+		style = SvgStyle.parse (null, tag.get_attributes ());+		+		if (hidden) {+			return;+		}++		bezier_points = new BezierPoints[2];+		bezier_points[0] = new BezierPoints ();+		bezier_points[0].type == 'L';+		bezier_points[0].x0 = x1;+		bezier_points[0].y0 = y1;++		bezier_points[1] = new BezierPoints ();+		bezier_points[1].type == 'L';+		bezier_points[1].x0 = x2;+		bezier_points[1].y0 = y2;+		+		g = MainWindow.get_current_glyph ();+		move_and_resize (bezier_points, 4, false, 1, g);+					+		p = new Path ();	+		+		p.add (bezier_points[0].x0, bezier_points[0].y0);+		p.add (bezier_points[1].x0, bezier_points[1].y0);+						+		p.close ();+		p.create_list ();+		p.recalculate_linear_handles ();		+		+		npl.add (p);+		+		foreach (Attribute attr in tag.get_attributes ()) {+			if (attr.get_name () == "transform") {+				transform_paths (attr.get_content (), npl);+			}+		}+		+		npl.apply_style (style);+		append_paths (pl, npl);+	}+		+	private void parse_rect (Tag tag, Layer layer) {+		Path p;+		double x, y, x2, y2;+		BezierPoints[] bezier_points;+		Glyph g;+		PathList npl = new PathList ();+		SvgStyle style = new SvgStyle ();+		bool hidden = false;+		EditPoint ep;+		+		x = 0;+		y = 0;+		x2 = 0;+		y2 = 0;+			+		foreach (Attribute attr in tag.get_attributes ()) {			+			if (attr.get_name () == "x") {+				x = parse_double (attr.get_content ());+			}+			+			if (attr.get_name () == "y") {+				y = -parse_double (attr.get_content ());+			}++			if (attr.get_name () == "width") {+				x2 = parse_double (attr.get_content ());+			}+			+			if (attr.get_name () == "height") {+				y2 = -parse_double (attr.get_content ());+			}+	+			if (attr.get_name () == "display" && attr.get_content () == "none") {+				hidden = true;+			}+		}+		+		style = SvgStyle.parse (null, tag.get_attributes ());+		+		if (hidden) {+			return;+		}+		+		x2 += x;+		y2 += y;++		bezier_points = new BezierPoints[4];+		bezier_points[0] = new BezierPoints ();+		bezier_points[0].type == 'L';+		bezier_points[0].x0 = x;+		bezier_points[0].y0 = y;++		bezier_points[1] = new BezierPoints ();+		bezier_points[1].type == 'L';+		bezier_points[1].x0 = x2;+		bezier_points[1].y0 = y;++		bezier_points[2] = new BezierPoints ();+		bezier_points[2].type == 'L';+		bezier_points[2].x0 = x2;+		bezier_points[2].y0 = y2;++		bezier_points[3] = new BezierPoints ();+		bezier_points[3].type == 'L';+		bezier_points[3].x0 = x;+		bezier_points[3].y0 = y2;+		+		g = MainWindow.get_current_glyph ();+		move_and_resize (bezier_points, 4, false, 1, g);+					+		p = new Path ();	+		+		ep = p.add (bezier_points[0].x0, bezier_points[0].y0);+		ep.set_point_type (PointType.CUBIC);+		+		ep = p.add (bezier_points[1].x0, bezier_points[1].y0);+		ep.set_point_type (PointType.CUBIC);+		+		ep = p.add (bezier_points[2].x0, bezier_points[2].y0);+		ep.set_point_type (PointType.CUBIC);+		+		ep = p.add (bezier_points[3].x0, bezier_points[3].y0);+		ep.set_point_type (PointType.CUBIC);+						+		p.close ();+		p.create_list ();+		p.recalculate_linear_handles ();		+		+		npl.add (p);+		+		// FIXME: right layer for other transforms+		foreach (Attribute attr in tag.get_attributes ()) {+			if (attr.get_name () == "transform") {+				transform_paths (attr.get_content (), npl);+			}+		}+		+		npl.apply_style (style);+		append_paths (layer, npl);+	}+	+	private void parse_polygon (Tag tag, Layer layer) {+		PathList path_list = get_polyline (tag);+		+		foreach (Path p in path_list.paths) {+			p.close ();+		}+		+		append_paths (layer, path_list);+	}++	static void append_paths (Layer layer, PathList pl) {+		LayerUtils.append_paths (layer, pl);+	}+	+	private void parse_polyline (Tag tag, Layer layer) {	+		append_paths (layer, get_polyline (tag));+	}+	+	private PathList get_polyline (Tag tag) {+		Path p = new Path ();+		bool hidden = false;+		PathList path_list = new PathList ();+		SvgStyle style = new SvgStyle ();+				+		foreach (Attribute attr in tag.get_attributes ()) {+			if (attr.get_name () == "points") {+				p = parse_poly_data (attr.get_content ());+			}+	+			if (attr.get_name () == "display" && attr.get_content () == "none") {+				hidden = true;+			}+		}++		style = SvgStyle.parse (null, tag.get_attributes ());+		+		if (hidden) {+			return path_list;+		}+		+		path_list.add (p);+		path_list.apply_style (style);+		+		foreach (Attribute attr in tag.get_attributes ()) {+			if (attr.get_name () == "transform") {+				transform_paths (attr.get_content (), path_list);+			}+		}+		+		return path_list;+	}+	+	private void parse_path (Tag tag, Layer layer) {+		Glyph glyph = MainWindow.get_current_glyph ();+		PathList path_list = new PathList ();+		SvgStyle style = new SvgStyle ();+		bool hidden = false;++		foreach (Attribute attr in tag.get_attributes ()) {+			if (attr.get_name () == "d") {+				path_list = parse_svg_data (attr.get_content (), glyph);+			}+	+			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;+			}+		}+		+		style = SvgStyle.parse (null, tag.get_attributes ());+		+		if (hidden) {+			return;+		}+		+		foreach (Path path in path_list.paths) {+			LayerUtils.add_path (layer, path);+		}+		+		path_list.apply_style (style);++		// assume the even odd rule is applied and convert the path+		// to a path using the non-zero rule+		int inside_count;+		bool inside;+		foreach (SvgBird.Object o1 in layer.objects) {+			if (o1 is PathObject) {+				Path p1 = ((PathObject) o1).get_path ();+				inside_count = 0;+				+				foreach (SvgBird.Object o2 in layer.objects) {+					if (o2 is PathObject) {+						Path p2 = ((PathObject) o2).get_path ();+					+						if (p1 != p2) {+							inside = true;+							+							foreach (EditPoint ep in p1.points) {+								if (!is_inside (ep, p2)) {+									inside = false;+								}+							}++							if (inside) {+								inside_count++; +							}+						}+					}+				}+				+				if (inside_count % 2 == 0) {+					p1.force_direction (Direction.CLOCKWISE);+				} else {+					p1.force_direction (Direction.COUNTER_CLOCKWISE);+				}+			}+		}+		+		foreach (Attribute attr in tag.get_attributes ()) {+			if (attr.get_name () == "transform") {+				transform_paths (attr.get_content (), path_list);+			}+		}+	}++	public static void create_lines_for_segment (Path path, EditPoint start, EditPoint end, double tolerance) {+		double x1, x2, x3;+		double y1, y2, y3;+		double step_start, step, step_end;++		path.add (start.x, start.y);++		step_start = 0;+		step = 0.5;+		step_end = 1;+					+		while (true) {+			Path.get_point_for_step (start, end, step_start, out x1, out y1);+			Path.get_point_for_step (start, end, step, out x2, out y2);+			Path.get_point_for_step (start, end, step_end, out x3, out y3);+		+			if (!StrokeTool.is_flat (x1, y1, x2, y2, x3, y3, tolerance)+				&& step_end - step / 2.0 > step_start +				&& step_end - step / 2.0 > 0.1+				&& step > 0.05+				&& Path.distance_to_point (start, end) > 1) {+				+				step /= 2.0;+	+				if (step < 0.05) {+					step = 0.05;+				} else {+					step_end = step_start + 2 * step;+				}+			} else {+				path.add (x3, y3);+				+				if (step_end + step < 1) {+					step_start = step_end;+					step_end += step;+				} else {+					break;+				}+			}+		}+	}++	public static Path get_lines (Path p) {+		EditPoint start;+		Path path = new Path ();+		+		if (p.points.size == 0) {+			return path;+		}+		+		// create a set of straight lines+		start = p.points.get (p.points.size - 1);+		+		foreach (EditPoint end in p.points) {+			create_lines_for_segment (path, start, end, 1);+			start = end;+		}+						+		return path;+	}++	/** Check if a point is inside using the even odd fill rule.+	 * The path should only have straight lines.+	 */+	public static bool is_inside (EditPoint point, Path path) {+		EditPoint prev;+		bool inside = false;+		+		if (path.points.size <= 1) {+			return false;+		}++		if (!(path.xmin <= point.x <= path.xmax)) {+			return false;+		}+		+		if (!(path.ymin <= point.y <= path.ymax)) {+			return false;+		}+				+		prev = path.points.get (path.points.size - 1);+		+		foreach (EditPoint p in path.points) {+			if  ((p.y > point.y) != (prev.y > point.y) +				&& point.x < (prev.x - p.x) * (point.y - p.y) / (prev.y - p.y) + p.x) {+				inside = !inside;+			}+			+			prev = p;+		}+		+		return inside;+	}+	+	public void add_path_to_glyph (string d, Glyph g, bool svg_glyph = false, double units = 1) {+		PathList p = parse_svg_data (d, g, svg_glyph, units);+		foreach (Path path in p.paths) {+			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) {+		Font font;+		PathList path_list = new PathList ();+		BezierPoints[] bezier_points;+		int points;++		font = BirdFont.get_current_font ();+		SvgFile.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, points, svg_glyph, units, glyph);+		+		if (format == SvgFormat.ILLUSTRATOR) {+			path_list = create_paths_illustrator (bezier_points, points);+		} else {+			path_list = create_paths_inkscape (bezier_points, points);+		}++		// TODO: Find out if it is possible to tie handles.+		return path_list;+	}++	void move_and_resize (BezierPoints[] b, int num_b, bool svg_glyph, double units, Glyph glyph) {+		Font font = BirdFont.get_current_font ();+		+		for (int i = 0; i < num_b; i++) {+			// resize all points+			b[i].x0 *= units;+			b[i].y0 *= units;+			b[i].x1 *= units;+			b[i].y1 *= units;+			b[i].x2 *= units;+			b[i].y2 *= units;++			// move all points+			if (svg_glyph) {+				b[i].x0 += glyph.left_limit;+				b[i].y0 += font.base_line;+				b[i].x1 += glyph.left_limit;+				b[i].y1 += font.base_line;+				b[i].x2 += glyph.left_limit;+				b[i].y2 += font.base_line;+			} else {+				b[i].x0 += glyph.left_limit;+				b[i].y0 += font.top_position;+				b[i].x1 += glyph.left_limit;+				b[i].y1 += font.top_position;+				b[i].x2 += glyph.left_limit;+				b[i].y2 += font.top_position;+			}+		}+	}+	+	void find_last_handle (int start_index, BezierPoints[] b, int num_b, out double left_x, out double left_y, out PointType last_type) {+		BezierPoints last = new BezierPoints ();+		bool found = false;+		+		left_x = 0;+		left_y = 0;+		last_type = PointType.NONE;+		+		return_if_fail (b.length != 0);+		return_if_fail (b[0].type != 'z');+		return_if_fail (num_b < b.length);++		if (num_b == 2) {+			left_x = b[0].x0 + (b[1].x0 - b[0].x0) / 3.0;+			left_y = b[0].y0 + (b[1].y0 - b[0].y0) / 3.0;+			last_type = PointType.LINE_CUBIC;+			return;+		}+		+		for (int i = start_index; i < num_b; i++) {+			switch (b[i].type) {+				case 'Q':+					break;+				case 'C':+					break;+				case 'z':+					found = true;+					break;+				default:+					break;+			}+			+			if (found || i + 1 == num_b) {+				+				return_if_fail (i >= 1);+				+				if (b[i - 1].type == 'Q') {+					return_if_fail (i >= 1);+					left_x = b[i - 1].x0;+					left_y = b[i - 1].y0;+					last_type = PointType.QUADRATIC;+				} else if (b[i - 1].type == 'C') {+					return_if_fail (i >= 1);+					left_x = b[i - 1].x1;+					left_y = b[i - 1].y1;+					last_type = PointType.CUBIC;+				} else if (b[i - 1].type == 'S') {+					return_if_fail (i >= 1);+					left_x = b[i - 1].x1;+					left_y = b[i - 1].y1;+					last_type = PointType.CUBIC;+				} else if (b[i - 1].type == 'L' || last.type == 'M') {+					return_if_fail (i >= 2); // FIXME: -2 can be C or L+					left_x = b[i - 2].x0 + (b[i - 1].x0 - b[i - 2].x0) / 3.0;+					left_y = b[i - 2].y0 + (b[i - 1].y0 - b[i - 2].y0) / 3.0;+					last_type = PointType.LINE_CUBIC;+				} else {+					warning (@"Unexpected type. $(b[i - 1])\n");+				}+				return;	+			}+			+			last = b[i];+		}+		+		warning ("Last point not found.");+	}++	PathList create_paths_inkscape (BezierPoints[] b, int num_b) {+		double last_x;+		double last_y; +		PointType last_type;+		Path path;+		PathList path_list = new PathList ();+		EditPoint ep = new EditPoint ();+		Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> ();+				+		path = new Path ();+		+		if (num_b == 0) {+			warning ("No SVG data");+			return path_list;+		}++		if (b[0].type != 'M') {+			warning ("Path must begin with M or m.");+			return path_list;+		}+		+		find_last_handle (0, b, num_b, out last_x, out last_y, out last_type);++		for (int i = 0; i < num_b; i++) {+			if (b[i].type == '\0') {+				warning ("Parser error.");+				return path_list;+			}++			if (b[i].type == 'z') {+				path.close ();+				path.create_list ();+				path.recalculate_linear_handles ();+				path_list.add (path);+				path = new Path ();+				+				if (i + 1 >= num_b) {+					break;+				} else {+					find_last_handle (i + 1, b, num_b, out last_x, out last_y, out last_type);+				}+			}+			+			if (i >= num_b) {+				break;+			}+			+			if (b[i].type == 'M') {+				ep = path.add (b[i].x0, b[i].y0);+				ep.set_point_type (PointType.CUBIC);++				ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);+				+				if (i == 0 || (b[i - 1].type == 'z')) {+					ep.get_left_handle ().set_point_type (last_type);+					ep.get_left_handle ().move_to_coordinate (last_x, last_y);+				} else {+					if (b[i - 1].type == 'C' || b[i - 1].type == 'S') {+						ep.get_left_handle ().set_point_type (PointType.CUBIC);+						ep.get_left_handle ().move_to_coordinate (b[i + 1].x1, b[i + 1].y1);+					} +					+					if (b[i + 1].type == 'C' || b[i - 1].type == 'S') {+						ep.get_right_handle ().set_point_type (PointType.CUBIC);+						ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);+					} else if (b[i + 1].type == 'L' || b[i + 1].type == 'M') {+						ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);					+					}+				}+			}++			if (b[i].type == 'L') {+				return_val_if_fail (i != 0, path_list);+				+				ep = path.add (b[i].x0, b[i].y0);+				ep.set_point_type (PointType.CUBIC);+				ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);+				ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);++				if (b[i + 1].type == 'L' || b[i + 1].type == 'M' || b[i + 1].type == 'z') {+					ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);+				}++				if (b[i -1].type == 'L' || b[i - 1].type == 'M') {+					ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);+				}+			}+			+			if (b[i].type == 'Q') {+				return_val_if_fail (i != 0, path_list);++				ep.set_point_type (PointType.QUADRATIC);+				+				ep.get_right_handle ().set_point_type (PointType.QUADRATIC);+				ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0);+				+				if (b[i + 1].type != 'z') {+					ep = path.add (b[i].x1, b[i].y1);++					ep.get_left_handle ().set_point_type (PointType.QUADRATIC);+					ep.get_left_handle ().move_to_coordinate (b[i].x0, b[i].y0);+				}+			}+	+			if (b[i].type == 'C' || b[i].type == 'S') {+				return_val_if_fail (i != 0, path_list);++				ep.set_point_type (PointType.CUBIC);+				+				ep.get_right_handle ().set_point_type (PointType.CUBIC);+				ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0);+				+				if (b[i].type == 'S') {+					smooth_points.add (ep);+				}+				+				if (b[i + 1].type != 'z') {+					ep = path.add (b[i].x2, b[i].y2);++					ep.get_left_handle ().set_point_type (PointType.CUBIC);+					ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1);+				}+			}+		}++		foreach (EditPoint e in smooth_points) {+			e.set_point_type (PointType.LINE_DOUBLE_CURVE);+			e.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);+			e.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);+		}++		foreach (EditPoint e in smooth_points) {+			path.recalculate_linear_handles_for_point (e);+		}+		+		for (int i = 0; i < 3; i++) {+			foreach (EditPoint e in smooth_points) {+				e.set_tie_handle (true);+				e.process_tied_handle ();+			}+		}+		+		if (path.points.size > 0) {+			path_list.add (path);+		}++		foreach (Path p in path_list.paths) {+			p.remove_points_on_points ();+		}+				+		return path_list;+	}++	PathList create_paths_illustrator (BezierPoints[] b, int num_b) {+		Path path;+		PathList path_list = new PathList ();+		EditPoint ep;+		bool first_point = true;+		double first_left_x, first_left_y;+		Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> ();+		+		if (num_b > b.length) {+			warning ("num_b > b.length: $num_b > $(b.length)");+			return path_list;+		}+		+		path = new Path ();+				+		if (num_b <= 1) {+			warning ("No SVG data");+			return path_list;+		}+		+		first_left_x = 0;+		first_left_y = 0;++		// FIXME: array boundaries+		for (int i = 0; i < num_b; i++) {			+			if (b[i].type == '\0') {+				warning ("Parser error.");+				return path_list;+			} else if (b[i].type == 'z') {+				path.close ();+				path.create_list ();+				+				int first_index = 1;++				for (int j = i - 1; j >= 1; j--) {+					if (b[j].type == 'z') {+						first_index = j + 1; // from z to M +					}+				}+				+				if (b[first_index].type == 'C' || b[first_index].type == 'S') {+					return_val_if_fail (path.points.size != 0, path_list);+					ep = path.points.get (path.points.size - 1);+					+					if (b[i - 1].type != 'L' ) {+						ep.get_right_handle ().set_point_type (PointType.CUBIC);+						ep.get_right_handle ().move_to_coordinate (b[first_index].x0, b[first_index].y0);+					}+				} else if (b[first_index].type == 'L') {+					return_val_if_fail (path.points.size != 0, path_list);+					ep = path.points.get (path.points.size - 1);+					ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);+					path.recalculate_linear_handles_for_point (ep);+				} else {+					warning ("Unexpected type: %s", (!) b[first_index].type.to_string ());+				}+				+				path.recalculate_linear_handles ();+				path_list.add (path);+				+				path = new Path ();+				first_point = true;				+			} else if (b[i].type == 'M') {+			} else if (b[i].type == 'L') {++				if (first_point) {+					first_left_x = b[i].x0;+					first_left_y = b[i].y0;+				}+				+				ep = path.add (b[i].x0, b[i].y0);+				ep.set_point_type (PointType.LINE_CUBIC); // TODO: quadratic+				ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);++				if (b[i -1].type == 'L' || first_point) {+					// ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC);+				}+				+				if (b[i + 1].type == 'C' || b[i + 1].type == 'S') {+					return_val_if_fail (i + 1 < num_b, path_list);+					ep.get_right_handle ().set_point_type (PointType.CUBIC);+					ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);+				}+				+				first_point = false;+			} else if (b[i].type == 'Q') {+				warning ("Illustrator does not support quadratic control points.");+				warning (@"$(b[i])\n");+			} else if (b[i].type == 'C' || b[i].type == 'S') {+				+				if (first_point) {+					first_left_x = b[i].x0;+					first_left_y = b[i].y0;+				}+				+				ep = path.add (b[i].x2, b[i].y2);+				ep.set_point_type (PointType.CUBIC);++				ep.get_right_handle ().set_point_type (PointType.CUBIC);+				ep.get_left_handle ().set_point_type (PointType.CUBIC);+				+				ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1);++				if (b[i].type == 'S') {+					smooth_points.add (ep);+				}		++				if (b[i + 1].type != 'z') {+					ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0);+				} else {+					ep.get_right_handle ().move_to_coordinate (first_left_x, first_left_y);+				}+								+				first_point = false;+			} else {+				warning ("Unknown control point type.");+				warning (@"$(b[i])\n");+			}+		}+		+		foreach (EditPoint e in smooth_points) {+			e.set_point_type (PointType.LINE_DOUBLE_CURVE);+			e.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);+			e.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE);+		}++		foreach (EditPoint e in smooth_points) {+			path.recalculate_linear_handles_for_point (e);+		}+		+		for (int i = 0; i < 3; i++) {+			foreach (EditPoint e in smooth_points) {+				e.set_tie_handle (true);+				e.process_tied_handle ();+			}+		}+				+		if (path.points.size > 0) {+			path_list.add (path);+		}+		+		foreach (Path p in path_list.paths) {+			p.remove_points_on_points ();+		}+		+		return path_list;+	}+	+	public static double parse_double (string? s) {+		if (unlikely (is_null (s))) {+			warning ("Got null instead of expected string.");+			return 0;+		}+		+		if (unlikely (!is_point ((!) s))) {+			warning (@"Expecting a double got: $((!) s)");+			return 0;+		}+		+		return double.parse ((!) s);+	}+	+	static bool is_point (string? s) {+		if (s == null) {+			warning ("s is null");+			return false;+		}+		+		return double.try_parse ((!) s);+	}+	+	Path parse_poly_data (string polygon_points) {+		string data = SvgFile.add_separators (polygon_points);+		string[] c = data.split (" ");+		Path path;+		BezierPoints[] bezier_points = new BezierPoints[c.length + 1];+		int bi;+		Glyph g;+		EditPoint ep;+		+		bi = 0;+		for (int i = 0; i < c.length - 1; i += 2) {	+			if (i + 1 >= c.length) {+				warning ("No y value.");+				break;+			}++			if (bi >= bezier_points.length) {+				warning ("End of bezier_points");+				break;+			}++			bezier_points[bi] = new BezierPoints ();+			bezier_points[bi].type = 'L';+			bezier_points[bi].x0 = parse_double (c[i]);+			bezier_points[bi].y0 = -parse_double (c[i + 1]);+			bi++;+		}+			+		g = MainWindow.get_current_glyph ();+		move_and_resize (bezier_points, bi, false, 1, g);+		+		path = new Path ();+		for (int i = 0; i < bi; i++) {	+			ep = path.add (bezier_points[i].x0, bezier_points[i].y0);+			ep.set_point_type (PointType.LINE_CUBIC);+		}+		+		path.create_list ();+		path.recalculate_linear_handles ();+		+		return path;+	}++	public static EmbeddedSvg parse_embedded_svg_file (string path) {+		string xml_data;+		SvgFile svg_file = new SvgFile (); +		+		try {+			FileUtils.get_contents (path, out xml_data);+			return parse_embedded_svg_data (xml_data);+		} catch (GLib.Error error) {+			warning (error.message);+		}+		+		SvgDrawing drawing = new SvgDrawing ();+		return new EmbeddedSvg (drawing);+	}++	public static EmbeddedSvg parse_embedded_svg_data (string xml_data) {+		XmlParser xmlparser = new XmlParser (xml_data);+		SvgDrawing drawing = new SvgDrawing ();	+		SvgFile svg_file = new SvgFile (); +		+		if (xmlparser.validate ()) {+			Tag root = xmlparser.get_root_tag ();+			drawing = svg_file.parse_svg_file (root);+			EmbeddedSvg svg = new EmbeddedSvg (drawing);+			svg.svg_data = xml_data;+			return svg;+		} else {+			warning ("Invalid xml file.");+		}+	+		return new EmbeddedSvg (drawing);+	}++}++}diff --git a/libsvgbird/BezierPoints.vala b/libsvgbird/BezierPoints.valanew file mode 100644index 0000000..5d5256d--- /dev/null+++ b/libsvgbird/BezierPoints.vala@@ -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 SvgBird {++/** 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 ())";+	}+}++}diff --git a/libsvgbird/Color.vala b/libsvgbird/Color.valanew file mode 100644index 0000000..e5b0936--- /dev/null+++ b/libsvgbird/Color.vala@@ -0,0 +1,113 @@+/*+	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.+*/++namespace SvgBird {++public class Color {+	public double r;+	public double g;+	public double b;+	public double a;+	+	public Color (double r, double g, double b, double a) {+		this.r = r;+		this.g = g;+		this.b = b;+		this.a = a;+	}++	public static Color? parse (string? svg_color) {+		if (svg_color == null) {+			return null;+		}+		+		string color = ((!) svg_color).replace ("#", "");+		uint32 c;+		string[] arguments;+		Color parsed = new Color (0, 0, 0, 1);++		if (color == "none") {+			return null;+		}+		+		if (color.char_count () == 6) {+			color.scanf ("%x", out c);+			parsed.r = (uint8)((c & 0xFF0000) >> 16) / 254.0;+			parsed.g = (uint8)((c & 0x00FF00) >> 8)/ 254.0;+			parsed.b = (uint8)(c & 0x0000FF) / 254.0;+		} else if (color.char_count () == 3) {+			color.scanf ("%x", out c);+			parsed.r = (uint8)(((c & 0xF00) >> 4) | ((c & 0xF00) >> 8)) / 254.0;+			parsed.g = (uint8)((c & 0x0F0) | ((c & 0x0F0) >> 4)) / 254.0;+			parsed.b = (uint8)(((c & 0x00F) << 4) | (c & 0x00F)) / 254.0;+		} else if (color.index_of ("%") > -1) {+			color = color.replace ("rgb", "");+			color = color.replace (" ", "");+			color = color.replace ("\t", "");+			color = color.replace ("%", "");+			arguments = color.split (",");+			+			return_val_if_fail (arguments.length == 3, parsed);+			arguments[0].scanf ("%lf", out parsed.r);+			arguments[1].scanf ("%lf", out parsed.g);+			arguments[2].scanf ("%lf", out parsed.b);+		} else if (color.index_of ("rgb") > -1) {+			color = color.replace ("rgb", "");+			color = color.replace (" ", "");+			color = color.replace ("\t", "");+			arguments = color.split (",");+			+			return_val_if_fail (arguments.length == 3, parsed);+			+			int r, g, b;+			arguments[0].scanf ("%d", out r);+			parsed.r = r / 254.0;+			+			arguments[1].scanf ("%d", out g);+			parsed.g = g / 254.0;+			+			arguments[2].scanf ("%d", out b);+			parsed.b = b / 254.0;+		} else {+			warning ("Unknown color type: " + color);+		}+		+		+		return parsed;+ 	}++	public string to_rgb_hex () {+		StringBuilder rgb = new StringBuilder ();+		rgb.append ("#");+		rgb.append_printf ("%x", (int) Math.rint (r  * 254));+		rgb.append_printf ("%x", (int) Math.rint (g  * 254));+		rgb.append_printf ("%x", (int) Math.rint (b  * 254));+		return rgb.str;+	}++	public string to_string () {+		StringBuilder rgba = new StringBuilder ();+		rgba.append (to_rgb_hex ());+		rgba.append_printf ("%x", (int) Math.rint (a  * 254));+		return rgba.str;+	}+++	public Color copy () {+		return new Color (r, g, b, a);+	}++}++}diff --git a/libsvgbird/Defs.vala b/libsvgbird/Defs.valanew file mode 100644index 0000000..a1a022f--- /dev/null+++ b/libsvgbird/Defs.vala@@ -0,0 +1,96 @@+/*+	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 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");+	}++	public Defs copy () {+		Defs d = new Defs ();+		+		foreach (Gradient g in gradients) {+			d.add (g);+		}+		+		return d;+	}++}++}diff --git a/libsvgbird/Doubles.vala b/libsvgbird/Doubles.valanew file mode 100644index 0000000..43e896e--- /dev/null+++ b/libsvgbird/Doubles.vala@@ -0,0 +1,68 @@+/*+	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 SvgBird {++public class Doubles : GLib.Object {+	public double* data;+	public int size = 0;+	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++;+	}+	+	public double get_double (int index) {+		if (unlikely (index < 0)) {+			warning ("index < 0");+			return 0;+		}++		if (unlikely (index >= size)) {+			warning ("index >= size");+			return 0;+		}+		+		return data[index];+	}+}++}+diff --git a/libsvgbird/Gradient.vala b/libsvgbird/Gradient.valanew file mode 100644index 0000000..50b2927--- /dev/null+++ b/libsvgbird/Gradient.vala@@ -0,0 +1,79 @@+/*+	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;+using Math;++namespace SvgBird {++public class Gradient : GLib.Object {+	public double x1;+	public double y1;+	public double x2;+	public double y2;++	public Gee.ArrayList<Stop> stops;+	+	public string id = "";+	public string? href = null;+	public SvgTransforms transforms;++	public Gradient () {+		x1 = 0;+		y1 = 0;+		x2 = 0;+		y2 = 0;+		stops = new Gee.ArrayList<Stop> ();+		transforms = new SvgTransforms ();+	}+	+	public Gradient copy () {+		Gradient g = new Gradient ();+		g.x1 = x1;+		g.y1 = y1;+		g.x2 = x2;+		g.y2 = y2;+		+		foreach (Stop s in stops) {+			g.stops.add (s.copy ());+		}+		+		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 ();+	}+}++}diff --git a/libsvgbird/Layer.vala b/libsvgbird/Layer.valanew file mode 100644index 0000000..7101b97--- /dev/null+++ b/libsvgbird/Layer.vala@@ -0,0 +1,170 @@+/*+	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.+*/++namespace SvgBird {++public class Layer : GLib.Object {+	public ObjectGroup objects;+	+	public Gee.ArrayList<Layer> subgroups;+	public bool visible = true;+	public string name = "Layer";+	+	public SvgTransforms transforms;+	+	public Layer () {+		objects = new ObjectGroup ();+		subgroups = new Gee.ArrayList<Layer> ();+		transforms = new SvgTransforms ();+	}++	public int index_of (Layer sublayer) {+		return subgroups.index_of (sublayer);+	}++	public ObjectGroup get_all_objects () {+		ObjectGroup o = new ObjectGroup ();+		+		o.append (objects);+		+		foreach (Layer sublayer in subgroups) {+			o.append (sublayer.get_all_objects ());+		}+		+		return o;+	}+		+	public ObjectGroup get_visible_objects () {+		ObjectGroup object_group = new ObjectGroup ();+		+		if (visible) {+			foreach (Object o in objects) {+				object_group.add (o);+			}+		}+		+		foreach (Layer sublayer in subgroups) {+			if (sublayer.visible) {+				object_group.append (sublayer.get_visible_objects ());+			}+		}+		+		return object_group;		+	}+		+	public void add_layer (Layer layer) {+		subgroups.add (layer);+	}+	+	public void add_object (Object object) {+		objects.add (object);+	}++	public void remove (Object o) {+		objects.remove (o);+	}+	+	public void remove_layer (Layer layer) {+		subgroups.remove (layer);+		+		foreach (Layer sublayer in subgroups) {+			sublayer.remove_layer (layer);+		}+	}+	+	public static void copy_layer (Layer from, Layer to) {+		to.name = from.name;+		to.objects = from.objects.copy ();+		to.visible = from.visible;+		+		foreach (Layer l in from.subgroups) {+			to.subgroups.add (l.copy ());+		}	+	}+	+	public Layer copy () {+		Layer layer = new Layer ();+		copy_layer (this, layer);+		return layer;+	}++	public void get_boundaries (out double x, out double y, out double w, out double h) {+		double px, py, px2, py2;+		+		px = double.MAX;+		py = double.MAX;+		px2 = -double.MAX;+		py2 = -double.MAX;+		+		foreach (Object p in get_all_objects ().objects) {+			if (px > p.xmin) {+				px = p.xmin;+			} ++			if (py > p.ymin) {+				py = p.ymin;+			}++			if (px2 < p.xmax) {+				px2 = p.xmax;+			}+			+			if (py2 < p.ymax) {+				py2 = p.ymax;+			}+		}+		+		w = px2 - px;+		h = py2 - py;+		x = px;+		y = py2;+	}+		+	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 (@"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");+		}+		+		foreach (Layer l in subgroups) {+			for (int i = 0; i < indent; i++) {+				stdout.printf ("\t");+			}+			stdout.printf ("%s\n", l.name);+			l.print (indent + 1);+		}+	}+}++}diff --git a/libsvgbird/LineCap.vala b/libsvgbird/LineCap.valanew file mode 100644index 0000000..ba027b9--- /dev/null+++ b/libsvgbird/LineCap.vala@@ -0,0 +1,26 @@+/*+	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;+using Math;++namespace SvgBird {++public enum LineCap {+	BUTT,+	SQUARE,+	ROUND+}++}diff --git a/libsvgbird/Object.vala b/libsvgbird/Object.valanew file mode 100644index 0000000..82af6c5--- /dev/null+++ b/libsvgbird/Object.vala@@ -0,0 +1,154 @@+/*+	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;+using Math;++namespace SvgBird {++public abstract class Object : GLib.Object {+	bool open = false;+	+	public bool visible = true;+	public SvgStyle style = new SvgStyle ();+	public SvgTransforms transforms = new SvgTransforms ();+	+	public virtual Color? color { get; set; } // FIXME: keep this in svg style+	public virtual Color? stroke_color { get; set; }+	public virtual Gradient? gradient { get; set; }++	/** Path boundaries */+	public virtual double xmax { get; set; }+	public virtual double xmin { get; set; }+	public virtual double ymax { get; set; }+	public virtual double ymin { get; set; }+	+	public virtual double rotation { get; set; }+	public virtual double stroke { get; set; }+	public virtual LineCap line_cap { get; set; default = LineCap.BUTT; }+	public virtual bool fill { get; set; }+		+	public Object () {	+	}++	public Object.create_copy (Object o) {	+		open = o.open;+	}+		+	public void set_open (bool open) {+		this.open = open;+	}+	+	public bool is_open () {+		return open;+	}++	public abstract void update_region_boundaries ();+	public abstract bool is_over (double x, double y);+	public abstract void draw (Context cr);+	public abstract Object copy ();+	public abstract void move (double dx, double dy);+	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";+	}++	public void paint (Context cr) {+		Color fill, stroke;+		bool need_fill = style.fill_gradient != null || style.fill != null;+		bool need_stroke = style.stroke_gradient != null || style.stroke != null;++		cr.set_line_width (style.stroke_width);+	+		if (style.fill_gradient != null) {+			apply_gradient (cr, (!) style.fill_gradient);+		} else if (style.fill != null) {+			fill = (!) style.fill;+			cr.set_source_rgba (fill.r, fill.g, fill.b, fill.a);+		}++		if (need_fill) {+			if (need_stroke) {+				cr.fill_preserve ();+			} else {+				cr.fill ();+			}	+		}++		if (style.stroke_gradient != null) {+			apply_gradient (cr, (!) style.stroke_gradient);+		} else if (style.stroke != null) {+			stroke = (!) style.stroke;+			cr.set_source_rgba (stroke.r, stroke.g, stroke.b, stroke.a);+		}++		if (need_stroke) {+			cr.stroke ();+		}+	}+	+	public void apply_gradient (Context cr, Gradient? gradient) {+		Cairo.Pattern pattern;+		Gradient g;+		+		if (gradient != null) {+			g = (!) gradient;+			+			pattern = new Cairo.Pattern.linear (g.x1, g.y1, g.x2, g.y2);++			Matrix gradient_matrix = g.get_matrix ();+			gradient_matrix.invert ();+			pattern.set_matrix (gradient_matrix);+			+			foreach (Stop s in g.stops) {+				Color c = s.color;+				pattern.add_color_stop_rgba (s.offset, c.r, c.g, c.b, c.a);+			}+					+			cr.set_source (pattern);+		}+	}+	+	public void apply_transform (Context cr) {+		Matrix view_matrix = cr.get_matrix ();+		Matrix object_matrix = transforms.get_matrix ();+		+		object_matrix.multiply (object_matrix, view_matrix);+		cr.set_matrix (object_matrix);+	}+					+}++}diff --git a/libsvgbird/ObjectGroup.vala b/libsvgbird/ObjectGroup.valanew file mode 100644index 0000000..3beb2b1--- /dev/null+++ b/libsvgbird/ObjectGroup.vala@@ -0,0 +1,63 @@+/*+	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.+*/++namespace SvgBird {++public class ObjectGroup : GLib.Object {+	public Gee.ArrayList<Object> objects;+	+	public int size { +		get {+			return objects.size;+		}+	}+	+	public ObjectGroup () {+		 objects = new Gee.ArrayList<Object> ();+	}++	public Gee.Iterator<Object> iterator () {+		return objects.iterator ();+	}++	public void remove (Object p) {+		objects.remove (p);+	}+	+	public void add (Object p) {+		objects.add (p);+	}+	+	public void clear () {+		objects.clear ();+	}++	public void append (ObjectGroup group) {+		foreach (Object o in group.objects) {+			objects.add (o);+		}+	}+	+	public ObjectGroup copy () {+		ObjectGroup objects_copy = new ObjectGroup ();+		+		foreach (Object o in objects) {+			objects_copy.add (o.copy ());+		}+		+		return objects_copy;+	}+}++}diff --git a/libsvgbird/Points.vala b/libsvgbird/Points.valanew file mode 100644index 0000000..f6bbea5--- /dev/null+++ b/libsvgbird/Points.vala@@ -0,0 +1,31 @@+/*+	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 SvgBird {++public class Points : GLib.Object {+	public Doubles point_data = new Doubles.for_capacity (100);+	public double x = 0;+	public double y = 0;+	public bool closed = false;++	public void add (double p) {+		point_data.add (p);+	}+}++}+diff --git a/libsvgbird/Rectangle.vala b/libsvgbird/Rectangle.valanew file mode 100644index 0000000..ea86ed4--- /dev/null+++ b/libsvgbird/Rectangle.vala@@ -0,0 +1,103 @@+/*+	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;+using Math;++namespace SvgBird {++public class Rectangle : Object {++	public double x = 0;+	public double y = 0;+	public double width = 0;+	public double height = 0;+	public double rx = 0;+	public double ry = 0;++	public Rectangle () {+	}+	+	public override bool is_over (double x, double y) {+		return false;+	}+			+	public override void draw (Context cr) {+		cr.save ();+		apply_transform (cr);+		+		if (rx == 0 && ry == 0) {+			cr.rectangle (x, y, width, height);+		} else {+			draw_rounded_corners (cr);+		}+		+		paint (cr);+		cr.restore ();+	}+	+	public void draw_rounded_corners (Context cr) {+		cr.new_path ();+		elliptical_arc (cr, x + width - rx, y + ry, -PI / 2, 0);+		elliptical_arc (cr, x + width - rx, y + height - ry, 0, PI / 2);+		elliptical_arc (cr, x + rx, y + height - ry, PI / 2, PI);+		elliptical_arc (cr, x + rx, y + ry, PI, PI + PI / 2);+		cr.close_path ();+	}+	+	public void elliptical_arc (Context cr, double x, double y, double angle_start, double angle_stop) {+		cr.save ();+		cr.translate (x + rx, y + ry);+		cr.scale (rx, ry);+		cr.arc (0, 0, 1, angle_start, angle_stop);+		cr.restore ();+	}+	+	public override void move (double dx, double dy) {+		x += dx;+		y += 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 () {+		Rectangle r = new Rectangle ();+		+		r.x = x;+		r.y = y;+		r.width = width;+		r.height = height;+		+		Object.copy_attributes (this, r);+		+		return r;+	}++	public override string to_string () {+		return "Rectangle";+	}+}++}diff --git a/libsvgbird/Stop.vala b/libsvgbird/Stop.valanew file mode 100644index 0000000..8da338a--- /dev/null+++ b/libsvgbird/Stop.vala@@ -0,0 +1,39 @@+/*+	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;+using Math;++namespace SvgBird {++public class Stop : GLib.Object {+	public Color color = new Color (0, 0, 0, 1);+	public double offset = 0;+	+	public Stop () {+	}+	+	public Stop copy () {+		Stop s = new Stop ();+		s.color = color.copy  ();+		s.offset = offset;+		return s;+	}+	+	public string to_string () {+		return @"Stop: $(offset), " + color.to_string ();+	}+}++}diff --git a/libsvgbird/SvgArc.vala b/libsvgbird/SvgArc.valanew file mode 100644index 0000000..9569e12--- /dev/null+++ b/libsvgbird/SvgArc.vala@@ -0,0 +1,181 @@+/*+ * BirdFont code from SVG Salamander+ * + * Copyright (c) 2004, Mark McKay+ * Copyright (c) 2014, Johan Mattsson+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following+ * conditions are met:+ *+ *   - Redistributions of source code must retain the above + *     copyright notice, this list of conditions and the following+ *     disclaimer.+ *   - Redistributions in binary form must reproduce the above+ *     copyright notice, this list of conditions and the following+ *     disclaimer in the documentation and/or other materials + *     provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED+ * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Mark McKay can be contacted at mark@kitfox.com.  Salamander and other+ * projects can be found at http://www.kitfox.com+ *+ * Created on January 26, 2004, 8:40 PM+ * Adapded to BirdFont on Juli 2, 2014, 5:01 PM+ */++using Math;++namespace SvgBird {++/** Convert an SVG arc instruction to a Beziรฉr path. */+static void add_arc_points (BezierPoints[] bezier_points, ref int bi, double x0, double y0, double rx, double ry, double angle, bool largeArcFlag, bool sweepFlag, double x, double y) {++	//+	// Elliptical arc implementation based on the SVG specification notes+	//++	double dx2, dy2, cosAngle, sinAngle;+	double x1, y1, Prx, Pry, Px1, Py1, radiiCheck;+	double sign, sq, coef, cx1, cy1;+	double sx2, sy2, cx, cy;+	double ux, uy, vx, vy, p, n;+	double angleStart, angleExtent;+	double s, step, theta;+	+	// Compute the half distance between the current and the final point+	dx2 = (x0 - x) / 2.0;+	dy2 = (y0 - y) / 2.0;+	+	// Convert angle from degrees to radians+	angle = 2 * PI * ((angle % 360.0) / 360.0);+	+	cosAngle = cos (angle);+	sinAngle = sin (angle);++	//+	// Step 1 : Compute (x1, y1)+	//+	x1 = cosAngle * dx2 + sinAngle * dy2;+	y1 = -sinAngle * dx2 + cosAngle * dy2;+	+	// Ensure radii are large enough+	rx = fabs(rx);+	ry = fabs(ry);+	Prx = rx * rx;+	Pry = ry * ry;+	Px1 = x1 * x1;+	Py1 = y1 * y1;++	+	// Check that radii are large enough+	radiiCheck = Px1 / Prx + Py1 / Pry;+	+	if (radiiCheck > 1) {+		rx = sqrt (radiiCheck) * rx;+		ry = sqrt (radiiCheck) * ry;+		Prx = rx * rx;+		Pry = ry * ry;+	}++	//+	// Step 2 : Compute (cx1, cy1)+	//+	sign = (largeArcFlag == sweepFlag) ? -1 : 1;+	sq = ((Prx * Pry) - (Prx * Py1) - (Pry * Px1)) / ((Prx * Py1) + (Pry * Px1));+	sq = (sq < 0) ? 0 : sq;+	coef = (sign * Math.sqrt(sq));+	cx1 = coef * ((rx * y1) / ry);+	cy1 = coef * -((ry * x1) / rx);++	//+	// Step 3 : Compute (cx, cy) from (cx1, cy1)+	//+	+	sx2 = (x0 + x) / 2.0;+	sy2 = (y0 + y) / 2.0;+	cx = sx2 - (cosAngle * cx1 - sinAngle * cy1);+	cy = sy2 - (sinAngle * cx1 + cosAngle * cy1);++	//+	// Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle)+	//+	+	ux = (x1 - cx1) / rx;+	uy = (y1 - cy1) / ry;+	vx = (-x1 - cx1) / rx;+	vy = (-y1 - cy1) / ry;++	// Compute the angle start+	n = sqrt((ux * ux) + (uy * uy));+	p = ux; // (1 * ux) + (0 * uy)+	sign = (uy < 0) ? -1d : 1d;+	angleStart = sign * acos(p / n);++	// Compute the angle extent+	n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));+	p = ux * vx + uy * vy;+	sign = (ux * vy - uy * vx < 0) ? -1d : 1d;+	angleExtent = sign * Math.acos(p / n);+	+	if(!sweepFlag && angleExtent > 0) {+		angleExtent -= 2 *PI;+	} else if (sweepFlag && angleExtent < 0) {+		angleExtent += 2 *PI;+	}+	angleExtent %= 2 * PI;+	angleStart %= 2 * PI;++	angleExtent *= -1;+	angleStart *= -1;+	+	// Approximate the path with Beziรฉr points+	s = (angleExtent > 0) ? 1 : -1;+	step = fabs (angleExtent) / (2 * fabs (angleExtent));++	theta = PI - angleStart - angleExtent;+	+	bezier_points[bi].type = 'L';+	bezier_points[bi].svg_type = 'a';++	bezier_points[bi].x0 = cx + rx * cos (theta);+	bezier_points[bi].y0 = cy + ry * sin (theta);+									+	bi++;+					+	for (double a = 0; a < fabs (angleExtent); a += step) {+		theta = PI - angleStart - angleExtent + s * a;++		return_if_fail (0 <= bi < bezier_points.length);++		bezier_points[bi].type = 'S';+		bezier_points[bi].svg_type = 'a';++		bezier_points[bi].x0 = cx + rx * cos (theta);+		bezier_points[bi].y0 = cy + ry * sin (theta);++		bezier_points[bi].x1 = cx + rx * cos (theta + 1 * step / 4);+		bezier_points[bi].y1 = cy + ry * sin (theta + 1 * step / 4);+		+		bezier_points[bi].x2 = cx + rx * cos (theta + 2 * step / 4);+		bezier_points[bi].y2 = cy + ry * sin (theta + 2 * step / 4);+						+		bi++;+	}+}++}+	diff --git a/libsvgbird/SvgDrawing.vala b/libsvgbird/SvgDrawing.valanew file mode 100644index 0000000..c2c8a58--- /dev/null+++ b/libsvgbird/SvgDrawing.vala@@ -0,0 +1,73 @@+/*+	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 Cairo;+using Math;++namespace SvgBird {++public class SvgDrawing : Object {+	public Layer root_layer = new Layer ();+	public Defs defs = new Defs ();++	public double x = 0;+	public double y = 0;+	public double width = 0;+	public double height = 0;+		+	public override void update_region_boundaries () {+	}+	+	public override bool is_over (double x, double y) {+		return (this.x <= x <= this.x + width) +			&& (this.y <= y <= this.y + height);+	}+	+	public override void draw (Context cr) {+		cr.save ();+		cr.translate (x, y);++		foreach (Object o in root_layer.get_visible_objects ().objects) {+			o.draw (cr);+		}++		cr.restore ();+	}+	+	public override Object copy () {+		SvgDrawing drawing = new SvgDrawing ();+		drawing.root_layer = root_layer.copy ();+		drawing.defs = defs.copy ();+		return drawing;+	}+	+	public override void move (double dx, double dy) {+		x += dx;+		y += dy;+	}+	+	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) {+	}+}++}diff --git a/libsvgbird/SvgFile.vala b/libsvgbird/SvgFile.valanew file mode 100644index 0000000..6798c5c--- /dev/null+++ b/libsvgbird/SvgFile.vala@@ -0,0 +1,1156 @@+/*+	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 Cairo;++namespace SvgBird {++public class SvgFile : GLib.Object {++	SvgDrawing drawing;++	public SvgFile () {		+	}+	+	public SvgDrawing parse_svg_file (Tag tag) {+		drawing = new SvgDrawing ();++		foreach (Attribute attr in tag.get_attributes ()) {	+			if (attr.get_name () == "width") {+				drawing.width = parse_number (attr.get_content ());+			}++			if (attr.get_name () == "height") {+				drawing.height = parse_number (attr.get_content ());+			}+		}+				+		foreach (Tag t in tag) {+			string name = t.get_name ();+			+			if (name == "g") {+				parse_layer (drawing.root_layer, t);+			}++			if (name == "defs") {+				parse_defs (drawing, t);+			}+			+			parse_object (drawing.root_layer, t);+		}+		+		return drawing;+	}++	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) {+			string name = t.get_name ();+			+			if (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_defs (SvgDrawing drawing, Tag tag) {+		foreach (Tag t in tag) {+			// FIXME: radial+			string name = t.get_name ();+			+			if (name == "linearGradient") {+				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 ();+			+			// FIXME: gradientUnits+			+			if (name == "gradientTransform") {+				gradient.transforms = parse_transform (attr.get_content ());+			}++			if (name == "href") {+				gradient.href = attr.get_content ();+			}++			if (name == "x1") {+				gradient.x1 = parse_number (attr.get_content ());+			}++			if (name == "y1") {	+				gradient.y1 = parse_number (attr.get_content ());+			}+			+			if (name == "x2") {+				gradient.x2 = parse_number (attr.get_content ());+			}++			if (name == "y2") {+				gradient.y2 = parse_number (attr.get_content ());+			}++			if (name == "id") {+				gradient.id = attr.get_content ();+			}+		}+		+		foreach (Tag t in tag) {+			// FIXME: radial+			string name = t.get_name ();+			+			if (name == "stop") {+				parse_stop (gradient, t);+			}+		}+	}++	void parse_stop (Gradient gradient, Tag tag) {+		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 ();+			+			if (name == "offset") {+				string stop_offset = attr.get_content ();+				+				if (stop_offset.index_of ("%") > -1) {+					stop_offset = stop_offset.replace ("%", "");+					stop.offset = parse_number (stop_offset) / 100.0;+				} else {+					stop.offset = parse_number (stop_offset);+				}+			}+		}+		+		string? stop_color = style.style.get ("stop-color");+		string? stop_opacity = style.style.get ("stop-opacity");+		Color? color = new Color (0, 0, 0, 1);+		+		if (stop_color != null) {+			color = Color.parse (stop_color);+			+			if (color != null) {+				stop.color = (!) color;+			}+		}++		if (stop_opacity != null && color != null) {+			((!) color).a = parse_number (stop_opacity);+		}+	}+	+	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) {+		Rectangle rectangle = new Rectangle ();++		foreach (Attribute attr in tag.get_attributes ()) {+			string attribute = attr.get_name ();+			+			if (attribute == "x") {+				rectangle.x = parse_number (attr.get_content ());+			}++			if (attribute == "y") {+				rectangle.y = parse_number (attr.get_content ());+			}++			if (attribute == "width") {+				rectangle.width = parse_number (attr.get_content ());+			}++			if (attribute == "height") {+				rectangle.height = parse_number (attr.get_content ());+			}+			+			if (attribute == "rx") {+				rectangle.rx = parse_number (attr.get_content ());+			}++			if (attribute == "ry") {+				rectangle.ry = parse_number (attr.get_content ());+			}+		}+		+		rectangle.transforms = get_transform (tag.get_attributes ());+		rectangle.style = SvgStyle.parse (drawing.defs,tag.get_attributes ());+		rectangle.visible = is_visible (tag);	+		+		layer.add_object (rectangle);+	}+	+	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 SvgTransforms parse_transform (string transforms) {+		string[] functions;+		string transform = transforms;+		SvgTransforms transform_functions;+		+		transform_functions = new SvgTransforms ();+		+		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 = parse_double (p[i]);+			transform.arguments.add (argument);+		}+		+		return transform;+	}++    private static string remove_unit (string d) {+		string s = d.replace ("pt", "");+		s = s.replace ("pc", "");+		s = s.replace ("mm", "");+		s = s.replace ("cm", "");+		s = s.replace ("in", "");+		s = s.replace ("px", "");+		return s;+	}+    +	public static double parse_number (string? number_with_unit) {+		if (number_with_unit == null) {+			return 0;+		}+		+		string d = (!) number_with_unit;+		string s = remove_unit (d);+		double n = parse_double (s);+		+		if (d.has_suffix ("pt")) {+			n *= 1.25;+		} else if (d.has_suffix ("pc")) {+			n *= 15;+		} else if (d.has_suffix ("mm")) {+			n *= 3.543307;+		} else if (d.has_suffix ("cm")) {+			n *= 35.43307;+		} else if (d.has_suffix ("in")) {+			n *= 90;+		}+		+		return n;+	}+	+	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 (parse_double (p[0]));+		}+		+		if (p.length > 1) {+			transform.arguments.add (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 (parse_double (p[0]));+		}+		+		if (p.length > 1) {+			transform.arguments.add (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 SvgTransforms get_transform (Attributes attributes) {+		foreach (Attribute attr in attributes) {+			if (attr.get_name () == "transform") {+				return parse_transform (attr.get_content ());+			}+		}+		+		return new SvgTransforms ();+	}+	+	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 ());+			}+		}+		+		path.transforms = get_transform (tag.get_attributes ());+		path.style = SvgStyle.parse (drawing.defs, 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;+	+		get_bezier_points (data, out bezier_points, out points_size, true);++		for (int i = 0; i < points_size; i++) {+			// FIXME: add more types+			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') {+				points.closed = true;+				path_data.add (points);+				points = new Points ();+			} else {+				string type = (!) bezier_points[i].type.to_string ();+				warning (@"SVG conversion not implemented for $type");+			}+		}++		if (points.point_data.size > 0) {+			path_data.add (points);+		}++		return path_data;+	}++	public static double parse_double (string? s) {+		if (unlikely (s == null)) {+			warning ("number is null");+			return 0;+		}+		+		if (unlikely (!double.try_parse ((!) s))) {+			warning (@"Expecting a double got: $((!) s)");+			return 0;+		}+		+		return double.parse ((!) s);+	}+	++	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[] c;+		double arc_rx, arc_ry;+		double arc_rotation;+		int large_arc;+		int arc_sweep;+		double arc_dest_x, arc_dest_y;+		+		int bi = 0;++		string data = add_separators (point_data);+		c = data.split (" ");++		// 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 < bezier_points_length; i++) {+			bezier_points[i] = new BezierPoints ();+		}++		// parse path+		int i = -1;+		while (++i < c.length && bi < bezier_points.length) {	+			if (c[i] == "m") {+				while (i + 2 < c.length && is_point (c[i + 1])) {+					bezier_points[bi].type = 'M';+					bezier_points[bi].svg_type = 'm';+					+					px += parse_double (c[++i]);+					+					if (svg_glyph) {+						py += parse_double (c[++i]);+					} else {+						py += -parse_double (c[++i]);+					}+					+					bezier_points[bi].x0 = px;+					bezier_points[bi].y0 = py;+					bi++;+				}+			} else if (c[i] == "M") {+				while (i + 2 < c.length && is_point (c[i + 1])) {+					bezier_points[bi].type = 'M';+					bezier_points[bi].svg_type = 'M';+					+					px = parse_double (c[++i]);+					+					if (svg_glyph) {+						py = parse_double (c[++i]);+					} else {+						py = -parse_double (c[++i]);+					}+					+					bezier_points[bi].x0 = px;+					bezier_points[bi].y0 = py;+					bi++;+				}+			} else if (c[i] == "h") {+				while (i + 1 < c.length && is_point (c[i + 1])) {+					bezier_points[bi].type = 'L';+					bezier_points[bi].svg_type = 'h';+					+					px += parse_double (c[++i]);++					bezier_points[bi].x0 = px;+					bezier_points[bi].y0 = py;+					bi++;+				}+			} else if (i + 1 < c.length && c[i] == "H") {+				while (is_point (c[i + 1])) {+					bezier_points[bi].type = 'L';+					bezier_points[bi].svg_type = 'H';+					+					px = parse_double (c[++i]);++					bezier_points[bi].x0 = px;+					bezier_points[bi].y0 = py;+					bi++;+				}+			} else if (c[i] == "v") {+				while (i + 1 < c.length && is_point (c[i + 1])) {+					bezier_points[bi].type = 'L';+					bezier_points[bi].svg_type = 'v';+										+					if (svg_glyph) {+						py = py + parse_double (c[++i]);+					} else {+						py = py - parse_double (c[++i]);+					}+					+					bezier_points[bi].x0 = px;+					bezier_points[bi].y0 = py;+					bi++;+				}				+			} else if (i + 1 < c.length && c[i] == "V") {+				while (is_point (c[i + 1])) {+					bezier_points[bi].type = 'L';+					bezier_points[bi].svg_type = 'V';+										+					if (svg_glyph) {+						py = parse_double (c[++i]);+					} else {+						py = -parse_double (c[++i]);+					}+					+					bezier_points[bi].x0 = px;+					bezier_points[bi].y0 = py;+					bi++;+				}+			} else if (c[i] == "l") {+				while (i + 2 < c.length && is_point (c[i + 1])) {+					bezier_points[bi].type = 'L';+					bezier_points[bi].svg_type = 'l';+										+					cx = px + parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = py + parse_double (c[++i]);+					} else {+						cy = py - parse_double (c[++i]);+					}+					+					px = cx;+					py = cy;++					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+					bi++;+				}+			} else if (c[i] == "L") {+				while (i + 2 < c.length && is_point (c[i + 1])) {+					bezier_points[bi].type = 'L';+					bezier_points[bi].svg_type = 'L';+										+					cx = parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = parse_double (c[++i]);+					} else {+						cy = -parse_double (c[++i]);+					}+					+					px = cx;+					py = cy;+					+					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+					bi++;				+				}	+			} else if (c[i] == "c") {+				while (i + 6 < c.length && is_point (c[i + 1])) {+					bezier_points[bi].type = 'C';+					bezier_points[bi].svg_type = 'C';+										+					cx = px + parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = py + parse_double (c[++i]);+					} else {+						cy = py - parse_double (c[++i]);+					}+					+					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+										+					cx = px + parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = py + parse_double (c[++i]);+					} else {+						cy = py - parse_double (c[++i]);+					}+					+					px2 = cx;+					py2 = cy;+										+					bezier_points[bi].x1 = px2;+					bezier_points[bi].y1 = py2;+										+					cx = px + parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = py + parse_double (c[++i]);+					} else {+						cy = py + -parse_double (c[++i]);+					}+					+					bezier_points[bi].x2 = cx;+					bezier_points[bi].y2 = cy;+										+					px = cx;+					py = cy;+					+					bi++;+				}+			} else if (c[i] == "C") {+				while (i + 6 < c.length && is_point (c[i + 1])) {+					bezier_points[bi].type = 'C';+					bezier_points[bi].svg_type = 'C';+										+					cx = parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = parse_double (c[++i]);+					} else {+						cy = -parse_double (c[++i]);+					}+									+					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+					+					cx = parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = parse_double (c[++i]);+					} else {+						cy = -parse_double (c[++i]);+					}+					+					px2 = cx;+					py2 = cy;+					+					bezier_points[bi].x1 = cx;+					bezier_points[bi].y1 = cy;+										+					cx = parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = parse_double (c[++i]);+					} else {+						cy = -parse_double (c[++i]);+					}+					+					bezier_points[bi].x2 = cx;+					bezier_points[bi].y2 = cy;+										+					px = cx;+					py = cy;+					+					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';+										+					cx = px + parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = py + parse_double (c[++i]);+					} else {+						cy = py - parse_double (c[++i]);+					}+					+					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+										+					px2 = cx;+					py2 = cy;+										+					cx = px + parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = py + parse_double (c[++i]);+					} else {+						cy = py - parse_double (c[++i]);+					}+					+					bezier_points[bi].x1 = cx;+					bezier_points[bi].y1 = cy;+										+					px = cx;+					py = cy;+					+					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';+										+					cx = parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = parse_double (c[++i]);+					} else {+						cy = -parse_double (c[++i]);+					}+					+					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+					+					px2 = cx;+					py2 = cy;+										+					cx = parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = parse_double (c[++i]);+					} else {+						cy = -parse_double (c[++i]);+					}+					+					px = cx;+					py = cy;++					bezier_points[bi].x1 = cx;+					bezier_points[bi].y1 = cy;+										+					bi++;					+				}	+			} else if (c[i] == "t") {+				while (i + 2 < c.length && is_point (c[i + 1])) {+					bezier_points[bi].type = 'Q';+					bezier_points[bi].svg_type = 't';+										+					// the first point is the reflection+					cx = 2 * px - px2;+					cy = 2 * py - py2; // if (svg_glyph) ?+					+					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+					+					px2 = cx;+					py2 = cy;+										+					cx = px + parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = py + parse_double (c[++i]);+					} else {+						cy = py - parse_double (c[++i]);+					}+					+					px = cx;+					py = cy;+					+					bezier_points[bi].x1 = px;+					bezier_points[bi].y1 = py;+										+					bi++;+				}+			} else if (c[i] == "T") {+				while (i + 2 < c.length && is_point (c[i + 1])) {+					bezier_points[bi].type = 'Q';+					bezier_points[bi].svg_type = 'T';+										+					// the reflection+					cx = 2 * px - px2;+					cy = 2 * py - py2;+					+					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+										+					px2 = cx;+					py2 = cy;+					+					cx = parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = parse_double (c[++i]);+					} else {+						cy = -parse_double (c[++i]);+					}+					+					px = cx;+					py = cy;+					+					bezier_points[bi].x1 = px;+					bezier_points[bi].y1 = py;+										+					bi++;				+				}+			} else if (c[i] == "s") {+				while (i + 4 < c.length && is_point (c[i + 1])) {+					bezier_points[bi].type = 'C';+					bezier_points[bi].svg_type = 's';+										+					// the first point is the reflection+					cx = 2 * px - px2;+					cy = 2 * py - py2; // if (svg_glyph) ?++					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+															+					cx = px + parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = py + parse_double (c[++i]);+					} else {+						cy = py - parse_double (c[++i]);+					}+					+					px2 = cx;+					py2 = cy;+					+					bezier_points[bi].x1 = px2;+					bezier_points[bi].y1 = py2;+					+					cx = px + parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = py + parse_double (c[++i]);+					} else {+						cy = py - parse_double (c[++i]);+					}+					+					bezier_points[bi].x2 = cx;+					bezier_points[bi].y2 = cy;+												+					px = cx;+					py = cy;+					+					bi++;+				}+			} else if (c[i] == "S") {+				while (i + 4 < c.length && is_point (c[i + 1])) {+					bezier_points[bi].type = 'C';+					bezier_points[bi].svg_type = 'S';+										+					// the reflection+					cx = 2 * px - px2;+					cy = 2 * py - py2; // if (svg_glyph) ?			++					bezier_points[bi].x0 = cx;+					bezier_points[bi].y0 = cy;+					+					// the other two are regular cubic points+					cx = parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = parse_double (c[++i]);+					} else {+						cy = -parse_double (c[++i]);+					}+					+					px2 = cx;+					py2 = cy;+					+					bezier_points[bi].x1 = px2;+					bezier_points[bi].y1 = py2;+					+					cx = parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = parse_double (c[++i]);+					} else {+						cy = -parse_double (c[++i]);+					}+					+					bezier_points[bi].x2 = cx;+					bezier_points[bi].y2 = cy;+					+					px = cx;+					py = cy;	+					+					bi++;+				}+			} else if (c[i] == "a") {+				while (i + 7 < c.length && is_point (c[i + 1])) {					+					arc_rx = parse_double (c[++i]);+					arc_ry = parse_double (c[++i]);+					+					arc_rotation = parse_double (c[++i]);+					large_arc = parse_int (c[++i]);+					arc_sweep = parse_int (c[++i]);+							+					cx = px + parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = py + parse_double (c[++i]);+					} else {+						cy = py - parse_double (c[++i]);+					}+					+					arc_dest_x = cx;+					arc_dest_y = cy;+					+					add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy);+					+					px = cx;+					py = cy;+				}+			} else if (i + 7 < c.length && c[i] == "A") {+				while (is_point (c[i + 1])) {					+					arc_rx = parse_double (c[++i]);+					arc_ry = parse_double (c[++i]);+					+					arc_rotation = parse_double (c[++i]);+					large_arc = parse_int (c[++i]);+					arc_sweep = parse_int (c[++i]);+							+					cx = parse_double (c[++i]);+					+					if (svg_glyph) {+						cy = parse_double (c[++i]);+					} else {+						cy = -parse_double (c[++i]);+					}+					+					arc_dest_x = cx;+					arc_dest_y = cy;+					+					add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy);++					px = cx;+					py = cy;+					+					+				}+			} else if (c[i] == "z") {+				bezier_points[bi].type = 'z';+				bezier_points[bi].svg_type = 'z';+				+				bi++;+			} else if (c[i] == "Z") {+				bezier_points[bi].type = 'z';+				bezier_points[bi].svg_type = 'z';+									+				bi++;+			} else if (c[i] == "") {+			} else if (c[i] == " ") {+			} else {+				warning (@"Unknown instruction: $(c[i])");+			}+		}+		+		if (bi == 0) {+			warning ("No points in path.");+		}++		points = bi;+	}++	static int parse_int (string? s) {+		if (unlikely (s == null)) {+			warning ("null instead of string");+			return 0;+		}+		+		if (unlikely (!int64.try_parse ((!) s))) {+			warning (@"Expecting an integer: $((!) s)");+			return 0;+		}+		+		return int.parse ((!) s);+	}++	static bool is_point (string? s) {+		if (unlikely (s == null)) {+			warning ("s is null");+			return false;+		}+		+		return double.try_parse ((!) s);+	}++	/** Add space as separator to svg data. +	 * @param d svg data+	 */+	public static string add_separators (string d) {+		string data = d;+		+		data = data.replace (",", " ");+		data = data.replace ("a", " a ");+		data = data.replace ("A", " A ");+		data = data.replace ("m", " m ");+		data = data.replace ("M", " M ");+		data = data.replace ("h", " h ");+		data = data.replace ("H", " H ");+		data = data.replace ("v", " v ");+		data = data.replace ("V", " V ");+		data = data.replace ("l", " l ");+		data = data.replace ("L", " L ");+		data = data.replace ("q", " q ");+		data = data.replace ("Q", " Q ");		+		data = data.replace ("c", " c ");+		data = data.replace ("C", " C ");+		data = data.replace ("t", " t ");+		data = data.replace ("T", " T ");+		data = data.replace ("s", " s ");+		data = data.replace ("S", " S ");+		data = data.replace ("zM", " z M ");+		data = data.replace ("zm", " z m ");+		data = data.replace ("z", " z ");+		data = data.replace ("Z", " Z ");+		data = data.replace ("-", " -");+		data = data.replace ("e -", "e-"); // minus can be either separator or a negative exponent+		data = data.replace ("\t", " ");+		data = data.replace ("\r\n", " ");+		data = data.replace ("\n", " ");+		+		// use only a single space as separator+		while (data.index_of ("  ") > -1) {+			data = data.replace ("  ", " ");+		}+		+		return data;+	}++}++}diff --git a/libsvgbird/SvgPath.vala b/libsvgbird/SvgPath.valanew file mode 100644index 0000000..a6bf077--- /dev/null+++ b/libsvgbird/SvgPath.vala@@ -0,0 +1,88 @@+/*+	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 SvgBird {++public class SvgPath : Object {	+	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) {+		cr.save ();+		cr.new_path ();+		+		foreach (Points p in points) {+			cr.move_to (p.x, p.y);+			draw_points (cr, p);+			+			if (p.closed) {+				cr.close_path ();+			}+		}+			+		apply_transform (cr);		+		paint (cr);+		cr.restore ();+	}++	public void draw_points (Context cr, Points points) {+		Doubles p = points.point_data;+		+		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]);+		}		+	}++	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";+	}+}++}diff --git a/libsvgbird/SvgStyle.vala b/libsvgbird/SvgStyle.valanew file mode 100644index 0000000..a0b86e4--- /dev/null+++ b/libsvgbird/SvgStyle.vala@@ -0,0 +1,147 @@+/*+	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;+using Math;++namespace SvgBird {++public class SvgStyle {+	+	public Gee.HashMap<string, string> style;+	+	public Color? stroke = null;+	public Color? fill = null;+	public Gradient? stroke_gradient = null;+	public Gradient? fill_gradient = null;++	public double stroke_width = 0;+	+	public SvgStyle () {+		style = new Gee.HashMap<string, string> ();+	}+	+	public LineCap get_line_cap () {+		string l;+		+		if (!style.has_key ("stroke-linecap")) {+			return LineCap.BUTT;+		}+		+		l = style.get ("stroke-linecap");+		+		if (l == "round") {+			return LineCap.ROUND;+		} else if (l == "square") {+			return LineCap.SQUARE;+		}+		+		return LineCap.BUTT; 	+	}+	+	public bool has_stroke () {+		bool s = true;+		+		if (style.has_key ("stroke")) {+			s = style.get ("stroke") != "none";+		}+				+		return get_stroke_width () > 0 && s;+	}+		+	public double get_stroke_width () {+		if (!style.has_key ("stroke-width")) {+			return 0;+		}++		return double.parse (style.get ("stroke-width"));+	}+	+	public static SvgStyle parse (Defs? d, Attributes attributes) {+		SvgStyle s = new SvgStyle ();+		double fill_opacity = 1;+		double stroke_opacity = 1;+		+		foreach (Attribute a in attributes) {+			if (a.get_name () == "style") {+				s.parse_key_value_pairs (a.get_content ());+			}+			+			if (a.get_name () == "stroke-width") {+				s.style.set ("stroke-width", a.get_content ());+			}++			if (a.get_name () == "stroke") {+				s.style.set ("stroke", a.get_content ());+			}++			if (a.get_name () == "fill") {+				s.style.set ("fill", a.get_content ());+			}++			if (a.get_name () == "fill-opacity") {+				fill_opacity = SvgFile.parse_number (a.get_content ());+			}++			if (a.get_name () == "stroke-opacity") {+				stroke_opacity = SvgFile.parse_number (a.get_content ());+			}+		}+	+		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;+		}+		+		if (s.stroke != null) {+			Color color = (!) s.stroke;+			color.a = stroke_opacity;+		}+		+		return s;+	}+	+	void parse_key_value_pairs (string svg_style) {+		string[] p = svg_style.split (";");+		string[] pair;+		string k, v;+		+		foreach (string kv in p) {+			pair = kv.split (":");+			+			if (pair.length != 2) {+				warning ("pair.length != 2");+				continue;+			}+			+			k = pair[0];+			v = pair[1];+			+			style.set (k, v);+		}+	}+}++}diff --git a/libsvgbird/SvgTransform.vala b/libsvgbird/SvgTransform.valanew file mode 100644index 0000000..e1fc43a--- /dev/null+++ b/libsvgbird/SvgTransform.vala@@ -0,0 +1,75 @@+/*+	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 Cairo;++namespace SvgBird {++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 () {+	}+	+	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;+	}+}++}diff --git a/libsvgbird/SvgTransforms.vala b/libsvgbird/SvgTransforms.valanew file mode 100644index 0000000..a799aff--- /dev/null+++ b/libsvgbird/SvgTransforms.vala@@ -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 SvgBird {++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;+	}+}++}diff --git a/scripts/version.py b/scripts/version.pyindex 8097f14..16cb94a 100644--- a/scripts/version.py+++ b/scripts/version.py@@ -1,6 +1,6 @@ #!/usr/bin/python3 """-Copyright (C) 2013, 2014 Johan Mattsson+Copyright (C) 2013 2014 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 @@ -20,4 +20,8 @@ SO_VERSION = SO_VERSION_MAJOR + '.' + SO_VERSION_MINOR  LIBBIRDGEMS_SO_VERSION_MAJOR = '0' LIBBIRDGEMS_SO_VERSION_MINOR = '0'-LIBBIRDGEMS_SO_VERSION = LIBBIRDGEMS_SO_VERSION_MAJOR + '.' + LIBBIRDGEMS_SO_VERSION_MINOR;+LIBBIRDGEMS_SO_VERSION = LIBBIRDGEMS_SO_VERSION_MAJOR + '.' + LIBBIRDGEMS_SO_VERSION_MINOR++LIBSVGBIRD_SO_VERSION_MAJOR = '0'+LIBSVGBIRD_SO_VERSION_MINOR = '0'+LIBSVGBIRD_SO_VERSION = LIBBIRDGEMS_SO_VERSION_MAJOR + '.' + LIBBIRDGEMS_SO_VERSION_MINOR

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112