Commit 841122b
Move the SVG parser and the SVG rendering code to libsvgbird
Changed files (68)
modifiedbuild.pymodifiedconfiguremodifieddodo.pymodifiedlibbirdfont/BackgroundTab.valamodifiedlibbirdfont/BirdFontFile.valamodifiedlibbirdfont/ClipTool.valamodifiedlibbirdfont/Color.valamodifiedlibbirdfont/ColorPicker.valadeletedlibbirdfont/Doubles.valamodifiedlibbirdfont/DrawingTools.valamodifiedlibbirdfont/EmbeddedSvg.valamodifiedlibbirdfont/ExportTool.valamodifiedlibbirdfont/Glyph.valadeletedlibbirdfont/Layer.valamodifiedlibbirdfont/LayerLabel.valaaddedlibbirdfont/LayerUtils.valamodifiedlibbirdfont/MenuTab.valamodifiedlibbirdfont/MoveTool.valadeletedlibbirdfont/ObjectGroup.valamodifiedlibbirdfont/OverView.valamodifiedlibbirdfont/Path.valamodifiedlibbirdfont/PathList.valamodifiedlibbirdfont/PathObject.valamodifiedlibbirdfont/PenTool.valamodifiedlibbirdfont/ResizeTool.valamodifiedlibbirdfont/StrokeTool.valaaddedlibbirdfont/Svg.valadeletedlibbirdfont/Svg/BezierPoints.valadeletedlibbirdfont/Svg/Defs.valadeletedlibbirdfont/Svg/Gradient.valadeletedlibbirdfont/Svg/Object.valadeletedlibbirdfont/Svg/Points.valadeletedlibbirdfont/Svg/Rectangle.valadeletedlibbirdfont/Svg/Stop.valadeletedlibbirdfont/Svg/Svg.valadeletedlibbirdfont/Svg/SvgArc.valadeletedlibbirdfont/Svg/SvgDrawing.valadeletedlibbirdfont/Svg/SvgFile.valadeletedlibbirdfont/Svg/SvgFont.valadeletedlibbirdfont/Svg/SvgFontFormatWriter.valadeletedlibbirdfont/Svg/SvgParser.valadeletedlibbirdfont/Svg/SvgPath.valadeletedlibbirdfont/Svg/SvgStyle.valadeletedlibbirdfont/Svg/SvgTransform.valadeletedlibbirdfont/Svg/SvgTransforms.valaaddedlibbirdfont/SvgFont.valaaddedlibbirdfont/SvgFontFormatWriter.valaaddedlibbirdfont/SvgParser.valaaddedlibsvgbird/BezierPoints.valaaddedlibsvgbird/Color.valaaddedlibsvgbird/Defs.valaaddedlibsvgbird/Doubles.valaaddedlibsvgbird/Gradient.valaaddedlibsvgbird/Layer.valaaddedlibsvgbird/LineCap.valaaddedlibsvgbird/Object.valaaddedlibsvgbird/ObjectGroup.valaaddedlibsvgbird/Points.valaaddedlibsvgbird/Rectangle.valaaddedlibsvgbird/Stop.valaaddedlibsvgbird/SvgArc.valaaddedlibsvgbird/SvgDrawing.valaaddedlibsvgbird/SvgFile.valaaddedlibsvgbird/SvgPath.valaaddedlibsvgbird/SvgStyle.valaaddedlibsvgbird/SvgTransform.valaaddedlibsvgbird/SvgTransforms.valamodifiedscripts/version.py
Diff
@@ -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())@@ -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)@@ -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',@@ -14,6 +14,7 @@ using Math; using Cairo;+using SvgBird; namespace BirdFont { @@ -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); }+ } }@@ -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; } } }@@ -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;- } } }@@ -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);deleted file mode 100644@@ -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];- }-}--}-@@ -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; }@@ -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;@@ -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) {@@ -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);deleted file mode 100644@@ -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;- }-}--}@@ -13,6 +13,7 @@ */ using Cairo;+using SvgBird; namespace BirdFont { new file mode 100644@@ -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;+ }+}++}@@ -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) {@@ -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); }deleted file mode 100644@@ -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;- }-}--}@@ -13,6 +13,7 @@ */ using Cairo;+using SvgBird; namespace BirdFont { @@ -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;@@ -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 (); + }+ } } }@@ -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); } @@ -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 ();@@ -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); } @@ -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); } }new file mode 100644@@ -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;+}+ +}deleted file mode 100644@@ -1,33 +0,0 @@-/*- Copyright (C) 2014 Johan Mattsson-- This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version.-- This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details.-*/--namespace BirdFont {--/** Bezier point container for the SVG parser. */-public class BezierPoints {- public unichar type = '\0';- public unichar svg_type = '\0';- public double x0 = 0;- public double y0 = 0;- public double x1 = 0;- public double y1 = 0;- public double x2 = 0;- public double y2 = 0;- - public string to_string () {- return @"$((!)type.to_string ()) $x0,$y0 $x1,$y1 $x2,$y2 SVG:$((!)svg_type.to_string ())";- }-}--}deleted file mode 100644@@ -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;- }--}--}deleted file mode 100644@@ -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 ();- }-}--}deleted file mode 100644@@ -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);- }- -}--}deleted file mode 100644@@ -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);- }-}--}-deleted file mode 100644@@ -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";- }-}--}deleted file mode 100644@@ -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 ();- }-}--}deleted file mode 100644@@ -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;-}- -}deleted file mode 100644@@ -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++;- }-}--}- deleted file mode 100644@@ -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) {- }-}--}deleted file mode 100644@@ -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;- }-}--}deleted file mode 100644@@ -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, m 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);- }-}--}deleted file mode 100644@@ -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");- }--}---}deleted file mode 100644@@ -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;- }-}--}deleted file mode 100644@@ -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";- }-}--}deleted file mode 100644@@ -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 (); - }- }-}--}deleted file mode 100644@@ -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;- }-}--}deleted file mode 100644@@ -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;- }-}--}new file mode 100644@@ -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, m 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);+ }+}++}new file mode 100644@@ -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");+ }++}+++}new file mode 100644@@ -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);+ }++}++}new file mode 100644@@ -0,0 +1,33 @@+/*+ Copyright (C) 2014 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details.+*/++namespace 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 ())";+ }+}++}new file mode 100644@@ -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);+ }++}++}new file mode 100644@@ -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;+ }++}++}new file mode 100644@@ -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];+ }+}++}+new file mode 100644@@ -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 ();+ }+}++}new file mode 100644@@ -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);+ }+ }+}++}new file mode 100644@@ -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+}++}new file mode 100644@@ -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);+ }+ +}++}new file mode 100644@@ -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;+ }+}++}new file mode 100644@@ -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);+ }+}++}+new file mode 100644@@ -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";+ }+}++}new file mode 100644@@ -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 ();+ }+}++}new file mode 100644@@ -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++;+ }+}++}+ new file mode 100644@@ -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) {+ }+}++}new file mode 100644@@ -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;+ }++}++}new file mode 100644@@ -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";+ }+}++}new file mode 100644@@ -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);+ }+ }+}++}new file mode 100644@@ -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;+ }+}++}new file mode 100644@@ -0,0 +1,47 @@+/*+ Copyright (C) 2016 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details.+*/++using Cairo;++namespace 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;+ }+}++}@@ -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