The Birdfont Source Code


All Repositories / birdfont.git / commitdiff – RSS feed

Move the SVG parser and the SVG rendering code to libsvgbird

These changes was commited to the Birdfont repository Sun, 10 Jan 2016 17:00:12 +0000.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git
[Sun, 10 Jan 2016 17:00:12 +0000]

Updated Files

build.py
configure
dodo.py
libbirdfont/BackgroundTab.vala
libbirdfont/BirdFontFile.vala
libbirdfont/ClipTool.vala
libbirdfont/Color.vala
libbirdfont/ColorPicker.vala
libbirdfont/Doubles.vala
libbirdfont/DrawingTools.vala
libbirdfont/EmbeddedSvg.vala
libbirdfont/ExportTool.vala
libbirdfont/Glyph.vala
libbirdfont/Layer.vala
libbirdfont/LayerLabel.vala
libbirdfont/LayerUtils.vala
libbirdfont/MenuTab.vala
libbirdfont/MoveTool.vala
libbirdfont/ObjectGroup.vala
libbirdfont/OverView.vala
libbirdfont/Path.vala
libbirdfont/PathList.vala
libbirdfont/PathObject.vala
libbirdfont/PenTool.vala
libbirdfont/ResizeTool.vala
libbirdfont/StrokeTool.vala
libbirdfont/Svg/BezierPoints.vala
libbirdfont/Svg/Defs.vala
libbirdfont/Svg/Gradient.vala
libbirdfont/Svg/Object.vala
libbirdfont/Svg/Points.vala
libbirdfont/Svg/Rectangle.vala
libbirdfont/Svg/Stop.vala
libbirdfont/Svg/Svg.vala
libbirdfont/Svg/SvgArc.vala
libbirdfont/Svg/SvgDrawing.vala
libbirdfont/Svg/SvgFile.vala
libbirdfont/Svg/SvgFont.vala
libbirdfont/Svg/SvgFontFormatWriter.vala
libbirdfont/Svg/SvgParser.vala
libbirdfont/Svg/SvgPath.vala
libbirdfont/Svg/SvgStyle.vala
libbirdfont/Svg/SvgTransform.vala
libbirdfont/Svg/SvgTransforms.vala
libbirdfont/Svg.vala
libbirdfont/SvgFont.vala
libbirdfont/SvgFontFormatWriter.vala
libbirdfont/SvgParser.vala
libsvgbird/BezierPoints.vala
libsvgbird/Color.vala
libsvgbird/Defs.vala
libsvgbird/Doubles.vala
libsvgbird/Gradient.vala
libsvgbird/Layer.vala
libsvgbird/LineCap.vala
libsvgbird/Object.vala
libsvgbird/ObjectGroup.vala
libsvgbird/Points.vala
libsvgbird/Rectangle.vala
libsvgbird/Stop.vala
libsvgbird/SvgArc.vala
libsvgbird/SvgDrawing.vala
libsvgbird/SvgFile.vala
libsvgbird/SvgPath.vala
libsvgbird/SvgStyle.vala
libsvgbird/SvgTransform.vala
libsvgbird/SvgTransforms.vala
scripts/version.py
diff --git a/build.py b/build.py
--- a/build.py +++ b/build.py @@ -10,19 +10,25 @@ 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())
--- a/configure +++ b/configure @@ -11,7 +11,8 @@ from scripts.run import run TARGETS = ['libbirdfont', - 'libbirdgems', + 'libbirdgems', + 'libsvgbird', 'birdfont', 'birdfont-autotrace', 'birdfont-export', @@ -206,13 +207,13 @@ configfile.write_config(options.prefix) configfile.write_compile_parameters(options.prefix, - options.dest, - options.cc, - gee, - options.valac, - options.nonnull, - valacflags, - cflags, - ldflags, - options.gtk) + options.dest, + options.cc, + gee, + options.valac, + options.nonnull, + valacflags, + cflags, + ldflags, + options.gtk)
diff --git a/dodo.py b/dodo.py
--- a/dodo.py +++ b/dodo.py @@ -1,5 +1,5 @@ """ - Copyright (C) 2012 2013 2014 2015 Eduardo Naufel Schettino and Johan Mattsson + Copyright (C) 2012 - 2016 Eduardo Naufel Schettino and Johan Mattsson This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -46,6 +46,13 @@ LIBBIRDGEMS_SO_VERSION='${LIBbirdgems_VERSION}' 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 @@ -78,13 +85,15 @@ --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 @@ $(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 @@ 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 @@ --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 @@ $(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 @@ 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 @@ --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 @@ $(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 @@ 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 @@ --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 @@ 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 @@ $(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 @@ 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 @@ 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 @@ -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 @@ $(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 @@ 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 @@ --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 @@ $(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',
--- a/libbirdfont/BackgroundTab.vala +++ b/libbirdfont/BackgroundTab.vala @@ -14,6 +14,7 @@ using Math; using Cairo; + using SvgBird; namespace BirdFont {
--- a/libbirdfont/BirdFontFile.vala +++ b/libbirdfont/BirdFontFile.vala @@ -11,7 +11,9 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */ + using B; + using SvgBird; namespace BirdFont { @@ -504,7 +506,7 @@ 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 @@ } 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 @@ 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,7 +1957,8 @@ ligatures = font.get_ligatures (); ligatures.add_ligature (sequence, ligature); } + } }
--- a/libbirdfont/ClipTool.vala +++ b/libbirdfont/ClipTool.vala @@ -11,6 +11,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */ + + using SvgBird; namespace BirdFont { @@ -95,7 +97,7 @@ 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 @@ 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; } } }
--- a/libbirdfont/Color.vala +++ b/libbirdfont/Color.vala @@ -22,24 +22,19 @@ namespace BirdFont { - public class Color { - public double r; - public double g; - public double b; - public double a; - + public class Color : SvgBird.Color { public Color (double r, double g, double b, double a) { - this.r = r; - this.g = g; - this.b = b; - this.a = a; + base (r, g, b, a); } + public Color.create_copy (SvgBird.Color color) { + base (color.r, color.g, color.b, color.a); + } + public Color.hsba (double h, double s, double v, double a) { double hue, saturation, value; double f, p, q, t; - - this.a = a; + double r, g, b; if (s == 0.0) { r = v; @@ -100,67 +95,9 @@ assert_not_reached (); } } + + base (r, g, b, 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 = 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; - } public void to_hsva (out double h, out double s, out double v, out double a) { double red, green, blue; @@ -264,24 +201,11 @@ 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; } } }
--- a/libbirdfont/ColorPicker.vala +++ b/libbirdfont/ColorPicker.vala @@ -13,6 +13,7 @@ */ using Cairo; + using SvgBird; namespace BirdFont { @@ -119,7 +120,7 @@ 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 @@ int stop_size = (int) ((double) Toolbox.allocation_width / gradient.stops.size); for (int i = 0; i < gradient.stops.size; i++) { Stop s = gradient.stops.get (i); - c = s.color; + c = new Color.create_copy (s.color); cr.save (); cr.set_source_rgba (c.r, c.g, c.b, c.a); cr.rectangle (i * stop_size, y + 4 * bar_height, stop_size, bar_height);
diff --git libbirdfont/Doubles.vala(deleted)
--- a/libbirdfont/Doubles.vala +++ /dev/null @@ -1,69 +1,1 @@ - /* - 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]; - } - } - - } -
--- a/libbirdfont/DrawingTools.vala +++ b/libbirdfont/DrawingTools.vala @@ -14,6 +14,7 @@ using Cairo; using Math; + using SvgBird; namespace BirdFont { @@ -297,7 +298,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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; }
--- a/libbirdfont/EmbeddedSvg.vala +++ b/libbirdfont/EmbeddedSvg.vala @@ -15,10 +15,11 @@ using B; using Math; using Cairo; + using SvgBird; namespace BirdFont { - public class EmbeddedSvg : Object { + public class EmbeddedSvg : SvgBird.Object { public string svg_data = ""; public SvgDrawing drawing = new SvgDrawing (); @@ -86,7 +87,7 @@ cr.restore (); } - public override Object copy () { + public override SvgBird.Object copy () { EmbeddedSvg svg = new EmbeddedSvg (drawing); svg.svg_data = svg_data; return svg;
--- a/libbirdfont/ExportTool.vala +++ b/libbirdfont/ExportTool.vala @@ -13,6 +13,7 @@ */ using B; + using SvgBird; namespace BirdFont { @@ -95,14 +96,14 @@ 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 @@ } 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) {
--- a/libbirdfont/Glyph.vala +++ b/libbirdfont/Glyph.vala @@ -16,6 +16,7 @@ using Math; using Gee; using B; + using SvgBird; namespace BirdFont { @@ -132,7 +133,7 @@ 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 @@ 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 @@ } // 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 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 @@ 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 @@ 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 @@ 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 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ && !(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 @@ 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 @@ 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 @@ } 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 @@ 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 @@ } 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 @@ 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 @@ return paths; } - public void draw_boundaries (Object object, Context cr) { + public void draw_boundaries (SvgBird.Object object, Context cr) { double x = Glyph.reverse_path_coordinate_x (object.xmin); double y = Glyph.reverse_path_coordinate_y (object.ymin); double x2 = Glyph.reverse_path_coordinate_x (object.xmax);
diff --git libbirdfont/Layer.vala(deleted)
--- a/libbirdfont/Layer.vala +++ /dev/null @@ -1,265 +1,1 @@ - /* - 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; - } - } - - }
--- a/libbirdfont/LayerLabel.vala +++ b/libbirdfont/LayerLabel.vala @@ -13,6 +13,7 @@ */ using Cairo; + using SvgBird; namespace BirdFont {
diff --git libbirdfont/LayerUtils.vala(new)
--- /dev/null +++ b/libbirdfont/LayerUtils.vala @@ -1,1 +1,99 @@ + /* + 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; + } + } + + }
--- a/libbirdfont/MenuTab.vala +++ b/libbirdfont/MenuTab.vala @@ -11,6 +11,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */ + + using SvgBird; namespace BirdFont { @@ -274,6 +276,7 @@ } DrawingTools.set_stroke_tool_visibility (); + string lock_grid = f.settings.get_setting ("lock_grid"); bool lg = bool.parse (lock_grid); @@ -661,12 +664,12 @@ 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) {
--- a/libbirdfont/MoveTool.vala +++ b/libbirdfont/MoveTool.vala @@ -14,6 +14,7 @@ using Math; using Cairo; + using SvgBird; namespace BirdFont { @@ -94,9 +95,9 @@ 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 @@ 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 @@ 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 @@ 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 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 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 @@ 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 @@ 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 @@ Glyph g = MainWindow.get_current_glyph (); g.clear_active_paths (); - foreach (Object p in g.get_objects_in_current_layer ()) { + foreach (SvgBird.Object p in g.get_objects_in_current_layer ()) { if (!p.is_empty ()) { g.add_active_object (null, p); }
diff --git libbirdfont/ObjectGroup.vala(deleted)
--- a/libbirdfont/ObjectGroup.vala +++ /dev/null @@ -1,64 +1,1 @@ - /* - 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; - } - } - - }
--- a/libbirdfont/OverView.vala +++ b/libbirdfont/OverView.vala @@ -13,6 +13,7 @@ */ using Cairo; + using SvgBird; namespace BirdFont {
--- a/libbirdfont/Path.vala +++ b/libbirdfont/Path.vala @@ -14,6 +14,7 @@ using Cairo; using Math; + using SvgBird; namespace BirdFont { @@ -67,7 +68,7 @@ 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;
--- a/libbirdfont/PathList.vala +++ b/libbirdfont/PathList.vala @@ -11,6 +11,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. */ + + using SvgBird; namespace BirdFont { @@ -77,8 +79,22 @@ } 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 (); + } } } }
--- a/libbirdfont/PathObject.vala +++ b/libbirdfont/PathObject.vala @@ -13,83 +13,15 @@ */ using Cairo; + using SvgBird; namespace BirdFont { - public class PathObject : Object { + public class PathObject : SvgBird.Object { Path path; - - public override double stroke { - get { - return path.stroke; - } - - set { - path.stroke = value; - } - } - - public override double rotation { - get { - return path.rotation; - } - - set { - path.rotation = value; - } - } - - public override LineCap line_cap { - get { - return path.line_cap; - } - - set { - path.line_cap = value; - } - } - - public override bool fill { - get { - return path.fill; - } - - set { - path.fill = value; - } - } - - public override Color? color { - get { - return path.color; - } - - set { - path.color = value; - } - } - - public override Color? stroke_color { - get { - return path.stroke_color; - } - - set { - path.stroke_color = value; - } - } - - public override Gradient? gradient { - get { - return path.gradient; - } - - set { - path.gradient = value; - } - } + // FIXME: flip y axis public override double xmin { get { return path.xmin; @@ -170,7 +102,7 @@ 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 @@ path.reset_stroke (); } - public override Object copy () { + public override SvgBird.Object copy () { return new PathObject.create_copy (this); }
--- a/libbirdfont/PenTool.vala +++ b/libbirdfont/PenTool.vala @@ -1058,7 +1058,7 @@ // 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 @@ 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 ();
--- a/libbirdfont/ResizeTool.vala +++ b/libbirdfont/ResizeTool.vala @@ -14,6 +14,7 @@ using Math; using Cairo; + using SvgBird; namespace BirdFont { @@ -21,7 +22,7 @@ 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 @@ }); 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 @@ } } - 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 @@ 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 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ dx = -(nx - x); - foreach (Object p in glyph.active_paths) { + foreach (SvgBird.Object p in glyph.active_paths) { p.move (dx, 0); }
--- a/libbirdfont/StrokeTool.vala +++ b/libbirdfont/StrokeTool.vala @@ -14,14 +14,9 @@ using Cairo; using Math; + using SvgBird; namespace BirdFont { - - public enum LineCap { - BUTT, - SQUARE, - ROUND - } public class StrokeTool : GLib.Object { @@ -31,7 +26,7 @@ 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 @@ 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 @@ } 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 @@ 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 @@ return; } - foreach (Object object in g.active_paths) { + foreach (SvgBird.Object object in g.active_paths) { g.layers.remove (object); } @@ -1151,9 +1146,9 @@ } void add_line_cap (Path path, Path stroke1, Path stroke2, bool last_cap) { - if (path.line_cap == LineCap.SQUARE) { + if (path.line_cap == SvgBird.LineCap.SQUARE) { add_square_cap (path, stroke1, stroke2, last_cap); - } else if (path.line_cap == LineCap.ROUND) { + } else if (path.line_cap == SvgBird.LineCap.ROUND) { add_round_cap (path, stroke1, stroke2, last_cap); } }
diff --git libbirdfont/Svg/BezierPoints.vala(deleted)
--- a/libbirdfont/Svg/BezierPoints.vala +++ /dev/null @@ -1,34 +1,1 @@ - /* - Copyright (C) 2014 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - namespace BirdFont { - - /** Bezier point container for the SVG parser. */ - public class BezierPoints { - public unichar type = '\0'; - public unichar svg_type = '\0'; - public double x0 = 0; - public double y0 = 0; - public double x1 = 0; - public double y1 = 0; - public double x2 = 0; - public double y2 = 0; - - public string to_string () { - return @"$((!)type.to_string ()) $x0,$y0 $x1,$y1 $x2,$y2 SVG:$((!)svg_type.to_string ())"; - } - } - - }
diff --git libbirdfont/Svg/Defs.vala(deleted)
--- a/libbirdfont/Svg/Defs.vala +++ /dev/null @@ -1,97 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using B; - using Math; - - namespace BirdFont { - - public class Defs { - public Gee.ArrayList<Gradient> gradients = new Gee.ArrayList<Gradient> (); - - public void add (Gradient g) { - gradients.add (g); - } - - public Gradient? get_gradient_for_url (string? url) { - if (url == null) { - return null; - } - - string tag_id = (!) url; - - if (unlikely (!is_url (tag_id))) { - warning ("Not an URL: " + tag_id); - return null; - } - - int p1 = tag_id.index_of ("("); - if (unlikely (p1 == -1)) { - warning ("Not an URL: " + tag_id); - return null; - } - - int p2 = tag_id.index_of (")"); - if (unlikely (p2 == -1 || p2 < p1)) { - warning ("Not an URL: " + tag_id); - return null; - } - - p1 += "(".length; - int length = p2 - p1; - tag_id = tag_id.substring (p1, length); - - return get_gradient_for_id (tag_id); - } - - public Gradient? get_gradient_for_id (string id) { - string tag_id; - - if (id.has_prefix ("#")) { - tag_id = id.substring ("#".length); - } else { - tag_id = id; - } - - foreach (Gradient gradient in gradients) { - if (gradient.id == tag_id) { - return gradient; - } - } - - return null; - } - - public static bool is_url (string? attribute) { - if (attribute == null) { - return false; - } - - return ((!) attribute).has_prefix ("url"); - } - - public Defs copy () { - Defs d = new Defs (); - - foreach (Gradient g in gradients) { - d.add (g); - } - - return d; - } - - } - - }
diff --git libbirdfont/Svg/Gradient.vala(deleted)
--- a/libbirdfont/Svg/Gradient.vala +++ /dev/null @@ -1,80 +1,1 @@ - /* - Copyright (C) 2015 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Cairo; - using Math; - - namespace BirdFont { - - public class Gradient : GLib.Object { - public double x1; - public double y1; - public double x2; - public double y2; - - public Gee.ArrayList<Stop> stops; - - public string id = ""; - public string? href = null; - public SvgTransforms transforms; - - public Gradient () { - x1 = 0; - y1 = 0; - x2 = 0; - y2 = 0; - stops = new Gee.ArrayList<Stop> (); - transforms = new SvgTransforms (); - } - - public Gradient copy () { - Gradient g = new Gradient (); - g.x1 = x1; - g.y1 = y1; - g.x2 = x2; - g.y2 = y2; - - foreach (Stop s in stops) { - g.stops.add (s.copy ()); - } - - return g; - } - - public void copy_stops (Gradient g) { - foreach (Stop stop in g.stops) { - stops.add (stop.copy ()); - } - } - - public string to_string () { - StringBuilder description = new StringBuilder (); - description.append ("Gradient: "); - description.append (@"x1=$x1, y1=$y1, x2=$x2, y2=$y2"); - - foreach (Stop stop in stops) { - description.append (" "); - description.append (stop.to_string ()); - } - - return description.str; - } - - public Matrix get_matrix () { - return transforms.get_matrix (); - } - } - - }
diff --git libbirdfont/Svg/Object.vala(deleted)
--- a/libbirdfont/Svg/Object.vala +++ /dev/null @@ -1,155 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Cairo; - using Math; - - namespace BirdFont { - - public abstract class Object : GLib.Object { - bool open = false; - - public bool visible = true; - public SvgStyle style = new SvgStyle (); - public SvgTransforms transforms = new SvgTransforms (); - - public virtual Color? color { get; set; } // FIXME: keep this in svg style - public virtual Color? stroke_color { get; set; } - public virtual Gradient? gradient { get; set; } - - /** Path boundaries */ - public virtual double xmax { get; set; } - public virtual double xmin { get; set; } - public virtual double ymax { get; set; } - public virtual double ymin { get; set; } - - public virtual double rotation { get; set; } - public virtual double stroke { get; set; } - public virtual LineCap line_cap { get; set; default = LineCap.BUTT; } - public virtual bool fill { get; set; } - - public Object () { - } - - public Object.create_copy (Object o) { - open = o.open; - } - - public void set_open (bool open) { - this.open = open; - } - - public bool is_open () { - return open; - } - - public abstract void update_region_boundaries (); - public abstract bool is_over (double x, double y); - public abstract void draw (Context cr); - public abstract Object copy (); - public abstract void move (double dx, double dy); - public abstract void rotate (double theta, double xc, double yc); - public abstract bool is_empty (); - public abstract void resize (double ratio_x, double ratio_y); - - public static void copy_attributes (Object from, Object to) { - to.open = from.open; - - to.color = from.color; - to.stroke_color = from.stroke_color; - to.gradient = from.gradient; - - to.xmax = from.xmax; - to.xmin = from.xmin; - to.ymax = from.ymax; - to.ymin = from.ymin; - - to.rotation = from.rotation; - to.stroke = from.stroke; - to.line_cap = from.line_cap; - to.fill = from.fill; - } - - public virtual string to_string () { - return "Object"; - } - - public void paint (Context cr) { - Color fill, stroke; - bool need_fill = style.fill_gradient != null || style.fill != null; - bool need_stroke = style.stroke_gradient != null || style.stroke != null; - - cr.set_line_width (style.stroke_width); - - if (style.fill_gradient != null) { - apply_gradient (cr, (!) style.fill_gradient); - } else if (style.fill != null) { - fill = (!) style.fill; - cr.set_source_rgba (fill.r, fill.g, fill.b, fill.a); - } - - if (need_fill) { - if (need_stroke) { - cr.fill_preserve (); - } else { - cr.fill (); - } - } - - if (style.stroke_gradient != null) { - apply_gradient (cr, (!) style.stroke_gradient); - } else if (style.stroke != null) { - stroke = (!) style.stroke; - cr.set_source_rgba (stroke.r, stroke.g, stroke.b, stroke.a); - } - - if (need_stroke) { - cr.stroke (); - } - } - - public void apply_gradient (Context cr, Gradient? gradient) { - Cairo.Pattern pattern; - Gradient g; - - if (gradient != null) { - g = (!) gradient; - - pattern = new Cairo.Pattern.linear (g.x1, g.y1, g.x2, g.y2); - - Matrix gradient_matrix = g.get_matrix (); - gradient_matrix.invert (); - pattern.set_matrix (gradient_matrix); - - foreach (Stop s in g.stops) { - Color c = s.color; - pattern.add_color_stop_rgba (s.offset, c.r, c.g, c.b, c.a); - } - - cr.set_source (pattern); - } - } - - public void apply_transform (Context cr) { - Matrix view_matrix = cr.get_matrix (); - Matrix object_matrix = transforms.get_matrix (); - - object_matrix.multiply (object_matrix, view_matrix); - cr.set_matrix (object_matrix); - } - - } - - }
diff --git libbirdfont/Svg/Points.vala(deleted)
--- a/libbirdfont/Svg/Points.vala +++ /dev/null @@ -1,32 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Cairo; - - namespace BirdFont { - - public class Points : GLib.Object { - public Doubles point_data = new Doubles.for_capacity (100); - public double x = 0; - public double y = 0; - public bool closed = false; - - public void add (double p) { - point_data.add (p); - } - } - - } -
diff --git libbirdfont/Svg/Rectangle.vala(deleted)
--- a/libbirdfont/Svg/Rectangle.vala +++ /dev/null @@ -1,104 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Cairo; - using Math; - - namespace BirdFont { - - public class Rectangle : Object { - - public double x = 0; - public double y = 0; - public double width = 0; - public double height = 0; - public double rx = 0; - public double ry = 0; - - public Rectangle () { - } - - public override bool is_over (double x, double y) { - return false; - } - - public override void draw (Context cr) { - cr.save (); - apply_transform (cr); - - if (rx == 0 && ry == 0) { - cr.rectangle (x, y, width, height); - } else { - draw_rounded_corners (cr); - } - - paint (cr); - cr.restore (); - } - - public void draw_rounded_corners (Context cr) { - cr.new_path (); - elliptical_arc (cr, x + width - rx, y + ry, -PI / 2, 0); - elliptical_arc (cr, x + width - rx, y + height - ry, 0, PI / 2); - elliptical_arc (cr, x + rx, y + height - ry, PI / 2, PI); - elliptical_arc (cr, x + rx, y + ry, PI, PI + PI / 2); - cr.close_path (); - } - - public void elliptical_arc (Context cr, double x, double y, double angle_start, double angle_stop) { - cr.save (); - cr.translate (x + rx, y + ry); - cr.scale (rx, ry); - cr.arc (0, 0, 1, angle_start, angle_stop); - cr.restore (); - } - - public override void move (double dx, double dy) { - x += dx; - y += dy; - } - - public override void update_region_boundaries () { - } - - public override void rotate (double theta, double xc, double yc) { - } - - public override bool is_empty () { - return false; - } - - public override void resize (double ratio_x, double ratio_y) { - } - - public override Object copy () { - Rectangle r = new Rectangle (); - - r.x = x; - r.y = y; - r.width = width; - r.height = height; - - Object.copy_attributes (this, r); - - return r; - } - - public override string to_string () { - return "Rectangle"; - } - } - - }
diff --git libbirdfont/Svg/Stop.vala(deleted)
--- a/libbirdfont/Svg/Stop.vala +++ /dev/null @@ -1,40 +1,1 @@ - /* - Copyright (C) 2015 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Cairo; - using Math; - - namespace BirdFont { - - public class Stop : GLib.Object { - public Color color = Color.black (); - public double offset = 0; - - public Stop () { - } - - public Stop copy () { - Stop s = new Stop (); - s.color = color.copy (); - s.offset = offset; - return s; - } - - public string to_string () { - return @"Stop: $(offset), " + color.to_string (); - } - } - - }
diff --git libbirdfont/Svg/Svg.vala(deleted)
--- a/libbirdfont/Svg/Svg.vala +++ /dev/null @@ -1,356 +1,1 @@ - /* - Copyright (C) 2012 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Cairo; - - namespace BirdFont { - - // FIXME: substrings - - public class Svg { - - /** Export to svg glyph data. */ - public static string to_svg_glyph (Glyph g) { - StringBuilder svg = new StringBuilder (); - PathList stroke_list; - - foreach (Path p in g.get_visible_paths ()) { - if (p.stroke == 0) { - write_path_as_glyph (p, svg, g); - } else { - stroke_list = p.get_completed_stroke (); - write_paths_as_glyph (stroke_list, svg, g); - } - } - - return svg.str; - } - - /** Export to svg-font data. */ - public static string to_svg_path (Path pl, Glyph g) { - StringBuilder svg = new StringBuilder (); - pl.create_list (); - write_path (pl, svg, g, false); - return svg.str; - } - - private static void write_paths_as_glyph (PathList pl, StringBuilder svg, Glyph g) { - foreach (Path p in pl.paths) { - write_path_as_glyph (p, svg, g); - } - } - - private static void write_path_as_glyph (Path pl, StringBuilder svg, Glyph g) { - write_path (pl, svg, g, true); - } - - private static void write_path (Path p, StringBuilder svg, Glyph g, bool do_glyph) { - int i = 0; - EditPoint? n = null; - EditPoint m; - - if (p.points.size < 2) { - return; - } - - p.create_list (); - - foreach (var e in p.points) { - if (i == 0) { - add_abs_start (e, svg, g, do_glyph); - i++; - n = e; - continue; - } - - m = (!) n; - - add_abs_next (m, e, svg, g, do_glyph); - - n = e; - i++; - } - - if (!p.is_open ()) { - m = p.points.get (0); - add_abs_next ((!) n, m, svg, g, do_glyph); - close_path (svg); - } - } - - private static void add_abs_next (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool do_glyph) { - if (start.right_handle.type == PointType.LINE_QUADRATIC) { - add_abs_line_to (start, end, svg, g, do_glyph); - } else if (start.right_handle.type == PointType.LINE_CUBIC && end.left_handle.type == PointType.LINE_CUBIC) { - add_abs_line_to (start, end, svg, g, do_glyph); - } else if (end.left_handle.type == PointType.QUADRATIC || start.right_handle.type == PointType.QUADRATIC) { - add_quadratic_abs_path (start, end, svg, g, do_glyph); - } else if (end.left_handle.type == PointType.DOUBLE_CURVE || start.right_handle.type == PointType.DOUBLE_CURVE) { - add_double_quadratic_abs_path (start, end, svg, g, do_glyph); - } else { - add_cubic_abs_path (start, end, svg, g, do_glyph); - } - } - - private static void add_abs_start (EditPoint ep, StringBuilder svg, Glyph g, bool to_glyph) { - double left = g.left_limit; - double baseline = -BirdFont.get_current_font ().base_line; - double height = g.get_height (); - - svg.append_printf ("M"); - - if (!to_glyph) { - svg.append_printf ("%s ", round (ep.x - left)); - svg.append_printf ("%s ", round (-ep.y + height / 2)); - } else { - svg.append_printf ("%s ", round (ep.x - left)); - svg.append_printf ("%s ", round (ep.y + baseline)); - } - } - - private static void close_path (StringBuilder svg) { - svg.append ("z"); - } - - private static void add_abs_line_to (EditPoint start, EditPoint stop, StringBuilder svg, Glyph g, bool to_glyph) { - double baseline = -BirdFont.get_current_font ().base_line; - double left = g.left_limit; - double height = g.get_height (); - - double xa, ya, xb, yb; - - Path.get_line_points (start, stop, out xa, out ya, out xb, out yb); - - double center_x = Glyph.xc (); - double center_y = Glyph.yc (); - - svg.append ("L"); - - if (!to_glyph) { - svg.append_printf ("%s ", round (xb - center_x - left)); - svg.append_printf ("%s ", round (yb - center_y + height / 2)); - } else { - svg.append_printf ("%s ", round (xb - center_x - left)); - svg.append_printf ("%s ", round (-yb + center_y + baseline)); - } - } - - private static void add_double_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool to_glyph) { - EditPoint middle; - double x, y; - - x = start.get_right_handle ().x + (end.get_left_handle ().x - start.get_right_handle ().x) / 2; - y = start.get_right_handle ().y + (end.get_left_handle ().y - start.get_right_handle ().y) / 2; - - middle = new EditPoint (x, y, PointType.QUADRATIC); - middle.right_handle = end.get_left_handle ().copy (); - - add_quadratic_abs_path (start, middle, svg, g, to_glyph); - add_quadratic_abs_path (middle, end, svg, g, to_glyph); - } - - private static void add_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool to_glyph) { - double left = g.left_limit; - double baseline = -BirdFont.get_current_font ().base_line; - double height = g.get_height (); - - double xa, ya, xb, yb, xc, yc, xd, yd; - - Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd); - - double center_x = Glyph.xc (); - double center_y = Glyph.yc (); - - // cubic path - if (!to_glyph) { - svg.append_printf ("Q"); - - svg.append_printf ("%s ", round (xb - center_x - left)); - svg.append_printf ("%s ", round (yb - center_y + height / 2)); - - svg.append_printf ("%s ", round (xd - center_x - left)); - svg.append_printf ("%s ", round (yd - center_y + height / 2)); - - } else { - svg.append_printf ("Q"); - - svg.append_printf ("%s ", round (xb - center_x - left)); - svg.append_printf ("%s ", round (-yb + center_y + baseline)); - - svg.append_printf ("%s ", round (xd - center_x - left)); - svg.append_printf ("%s ", round (-yd + center_y + baseline)); - } - } - - private static void add_cubic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool to_glyph) { - double left = g.left_limit; - double baseline = -BirdFont.get_current_font ().base_line; - double height = g.get_height (); - - double xa, ya, xb, yb, xc, yc, xd, yd; - - Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd); - - double center_x = Glyph.xc (); - double center_y = Glyph.yc (); - - // cubic path - if (!to_glyph) { - svg.append_printf ("C"); - - svg.append_printf ("%s ", round (xb - center_x - left)); - svg.append_printf ("%s ", round (yb - center_y + height / 2)); - - svg.append_printf ("%s ", round (xc - center_x - left)); - svg.append_printf ("%s ", round (yc - center_y + height / 2)); - - svg.append_printf ("%s ", round (xd - center_x - left)); - svg.append_printf ("%s ", round (yd - center_y + height / 2)); - - } else { - svg.append_printf ("C"); - - svg.append_printf ("%s ", round (xb - center_x - left)); - svg.append_printf ("%s ", round (-yb + center_y + baseline)); - - svg.append_printf ("%s ", round (xc - center_x - left)); - svg.append_printf ("%s ", round (-yc + center_y + baseline)); - - svg.append_printf ("%s ", round (xd - center_x - left)); - svg.append_printf ("%s ", round (-yd + center_y + baseline)); - } - } - - /** Draw path from svg font data. */ - public static void draw_svg_path (Context cr, string svg, double x, double y) { - double x1, x2, x3; - double y1, y2, y3; - double px, py; - string[] d = svg.split (" "); - - if (d.length == 0) { - return; - } - - px = 0; - py = 0; - - cr.save (); - - cr.set_line_width (0); - - if (svg == "") { - return; - } - - for (int i = 0; i < d.length; i++) { - - // trim off leading white space - while (d[i].index_of (" ") == 0) { - d[i] = d[i].substring (1); // FIXME: maybe no ascii - } - - if (d[i].index_of ("L") == 0) { - x1 = double.parse (d[i].substring (1)) + x; - y1 = -double.parse (d[i+1]) + y; - cr.line_to (x1, y1); - - px = x1; - py = y1; - continue; - } - - if (d[i].index_of ("Q") == 0) { - x1 = double.parse (d[i].substring (1)) + x; - y1 = -double.parse (d[i+1]) + y; - - x2 = double.parse (d[i+2]) + x; - y2 = -double.parse (d[i+3]) + y; - - cr.curve_to ((px + 2 * x1) / 3, (py + 2 * y1) / 3, (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, x2, y2); - - px = x2; - py = y2; - continue; - } - - if (d[i].index_of ("C") == 0) { - x1 = double.parse (d[i].substring (1)) + x; - y1 = -double.parse (d[i+1]) + y; - - x2 = double.parse (d[i+2]) + x; - y2 = -double.parse (d[i+3]) + y; - - x3 = double.parse (d[i+4]) + x; - y3 = -double.parse (d[i+5]) + y; - - cr.curve_to (x1, y1, x2, y2, x3, y3); - - px = x3; - py = y3; - continue; - } - - if (d[i].index_of ("M") == 0) { - x1 = double.parse (d[i].substring (1)) + x; - y1 = -double.parse (d[i+1]) + y; - - cr.move_to (x1, y1); - - px = x1; - py = y1; - continue; - } - - if (d[i].index_of ("zM") == 0) { - cr.close_path (); - - x1 = double.parse (d[i].substring (2)) + x; - y1 = -double.parse (d[i+1]) + y; - - cr.move_to (x1, y1); - - px = x1; - py = y1; - continue; - } - - if (d[i].index_of ("z") == 0) { - cr.close_path (); - continue; - } - - } - - cr.fill (); - cr.restore (); - } - - } - - internal static string round (double p) { - string v = p.to_string (); - char[] c = new char [501]; - - v = p.format (c, "%3.15f"); - - if (v.index_of ("e") != -1) { - return "0.0"; - } - - return v; - } - - }
diff --git libbirdfont/Svg/SvgArc.vala(deleted)
--- a/libbirdfont/Svg/SvgArc.vala +++ /dev/null @@ -1,182 +1,1 @@ - /* - * BirdFont code from SVG Salamander - * - * Copyright (c) 2004, Mark McKay - * Copyright (c) 2014, Johan Mattsson - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * disclaimer. - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Mark McKay can be contacted at mark@kitfox.com. Salamander and other - * projects can be found at http://www.kitfox.com - * - * Created on January 26, 2004, 8:40 PM - * Adapded to BirdFont on Juli 2, 2014, 5:01 PM - */ - - using Math; - - namespace BirdFont { - - /** Convert an SVG arc instruction to a Beziér path. */ - static void add_arc_points (BezierPoints[] bezier_points, ref int bi, double x0, double y0, double rx, double ry, double angle, bool largeArcFlag, bool sweepFlag, double x, double y) { - - // - // Elliptical arc implementation based on the SVG specification notes - // - - double dx2, dy2, cosAngle, sinAngle; - double x1, y1, Prx, Pry, Px1, Py1, radiiCheck; - double sign, sq, coef, cx1, cy1; - double sx2, sy2, cx, cy; - double ux, uy, vx, vy, p, n; - double angleStart, angleExtent; - double s, step, theta; - - // Compute the half distance between the current and the final point - dx2 = (x0 - x) / 2.0; - dy2 = (y0 - y) / 2.0; - - // Convert angle from degrees to radians - angle = 2 * PI * ((angle % 360.0) / 360.0); - - cosAngle = cos (angle); - sinAngle = sin (angle); - - // - // Step 1 : Compute (x1, y1) - // - x1 = cosAngle * dx2 + sinAngle * dy2; - y1 = -sinAngle * dx2 + cosAngle * dy2; - - // Ensure radii are large enough - rx = fabs(rx); - ry = fabs(ry); - Prx = rx * rx; - Pry = ry * ry; - Px1 = x1 * x1; - Py1 = y1 * y1; - - - // Check that radii are large enough - radiiCheck = Px1 / Prx + Py1 / Pry; - - if (radiiCheck > 1) { - rx = sqrt (radiiCheck) * rx; - ry = sqrt (radiiCheck) * ry; - Prx = rx * rx; - Pry = ry * ry; - } - - // - // Step 2 : Compute (cx1, cy1) - // - sign = (largeArcFlag == sweepFlag) ? -1 : 1; - sq = ((Prx * Pry) - (Prx * Py1) - (Pry * Px1)) / ((Prx * Py1) + (Pry * Px1)); - sq = (sq < 0) ? 0 : sq; - coef = (sign * Math.sqrt(sq)); - cx1 = coef * ((rx * y1) / ry); - cy1 = coef * -((ry * x1) / rx); - - // - // Step 3 : Compute (cx, cy) from (cx1, cy1) - // - - sx2 = (x0 + x) / 2.0; - sy2 = (y0 + y) / 2.0; - cx = sx2 - (cosAngle * cx1 - sinAngle * cy1); - cy = sy2 - (sinAngle * cx1 + cosAngle * cy1); - - // - // Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle) - // - - ux = (x1 - cx1) / rx; - uy = (y1 - cy1) / ry; - vx = (-x1 - cx1) / rx; - vy = (-y1 - cy1) / ry; - - // Compute the angle start - n = sqrt((ux * ux) + (uy * uy)); - p = ux; // (1 * ux) + (0 * uy) - sign = (uy < 0) ? -1d : 1d; - angleStart = sign * acos(p / n); - - // Compute the angle extent - n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy)); - p = ux * vx + uy * vy; - sign = (ux * vy - uy * vx < 0) ? -1d : 1d; - angleExtent = sign * Math.acos(p / n); - - if(!sweepFlag && angleExtent > 0) { - angleExtent -= 2 *PI; - } else if (sweepFlag && angleExtent < 0) { - angleExtent += 2 *PI; - } - angleExtent %= 2 * PI; - angleStart %= 2 * PI; - - angleExtent *= -1; - angleStart *= -1; - - // Approximate the path with Beziér points - s = (angleExtent > 0) ? 1 : -1; - step = fabs (angleExtent) / (2 * fabs (angleExtent)); - - theta = PI - angleStart - angleExtent; - - bezier_points[bi].type = 'L'; - bezier_points[bi].svg_type = 'a'; - - bezier_points[bi].x0 = cx + rx * cos (theta); - bezier_points[bi].y0 = cy + ry * sin (theta); - - bi++; - - for (double a = 0; a < fabs (angleExtent); a += step) { - theta = PI - angleStart - angleExtent + s * a; - - return_if_fail (0 <= bi < bezier_points.length); - - bezier_points[bi].type = 'S'; - bezier_points[bi].svg_type = 'a'; - - bezier_points[bi].x0 = cx + rx * cos (theta); - bezier_points[bi].y0 = cy + ry * sin (theta); - - bezier_points[bi].x1 = cx + rx * cos (theta + 1 * step / 4); - bezier_points[bi].y1 = cy + ry * sin (theta + 1 * step / 4); - - bezier_points[bi].x2 = cx + rx * cos (theta + 2 * step / 4); - bezier_points[bi].y2 = cy + ry * sin (theta + 2 * step / 4); - - bi++; - } - } - - } -
diff --git libbirdfont/Svg/SvgDrawing.vala(deleted)
--- a/libbirdfont/Svg/SvgDrawing.vala +++ /dev/null @@ -1,74 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - - using B; - using Cairo; - using Math; - - namespace BirdFont { - - public class SvgDrawing : Object { - public Layer root_layer = new Layer (); - public Defs defs = new Defs (); - - public double x = 0; - public double y = 0; - public double width = 0; - public double height = 0; - - public override void update_region_boundaries () { - } - - public override bool is_over (double x, double y) { - return (this.x <= x <= this.x + width) - && (this.y <= y <= this.y + height); - } - - public override void draw (Context cr) { - cr.save (); - cr.translate (x, y); - - foreach (Object o in root_layer.get_visible_objects ().objects) { - o.draw (cr); - } - - cr.restore (); - } - - public override Object copy () { - SvgDrawing drawing = new SvgDrawing (); - drawing.root_layer = root_layer.copy (); - drawing.defs = defs.copy (); - return drawing; - } - - public override void move (double dx, double dy) { - x += dx; - y += dy; - } - - public override void rotate (double theta, double xc, double yc) { - } - - public override bool is_empty () { - return false; - } - - public override void resize (double ratio_x, double ratio_y) { - } - } - - }
diff --git libbirdfont/Svg/SvgFile.vala(deleted)
--- a/libbirdfont/Svg/SvgFile.vala +++ /dev/null @@ -1,565 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using B; - using Cairo; - - namespace BirdFont { - - public class SvgFile : GLib.Object { - - SvgDrawing drawing; - - public SvgFile () { - } - - public EmbeddedSvg parse (string path) { - string xml_data; - - try { - FileUtils.get_contents (path, out xml_data); - return parse_data (xml_data); - } catch (GLib.Error error) { - warning (error.message); - } - - SvgDrawing drawing = new SvgDrawing (); - return new EmbeddedSvg (drawing); - } - - public EmbeddedSvg parse_data (string xml_data) { - XmlParser xmlparser = new XmlParser (xml_data); - SvgDrawing drawing = new SvgDrawing (); - - if (xmlparser.validate ()) { - Tag root = xmlparser.get_root_tag (); - drawing = parse_svg_file (root); - EmbeddedSvg svg = new EmbeddedSvg (drawing); - svg.svg_data = xml_data; - return svg; - } else { - warning ("Invalid xml file."); - } - - return new EmbeddedSvg (drawing); - } - - private SvgDrawing parse_svg_file (Tag tag) { - drawing = new SvgDrawing (); - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "width") { - drawing.width = parse_number (attr.get_content ()); - } - - if (attr.get_name () == "height") { - drawing.height = parse_number (attr.get_content ()); - } - } - - foreach (Tag t in tag) { - string name = t.get_name (); - - if (name == "g") { - parse_layer (drawing.root_layer, t); - } - - if (name == "defs") { - parse_defs (drawing, t); - } - - parse_object (drawing.root_layer, t); - } - - return drawing; - } - - private void parse_layer (Layer layer, Tag tag) { - bool hidden = false; - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "display" && attr.get_content () == "none") { - hidden = true; - } - - if (attr.get_name () == "visibility" - && (attr.get_content () == "hidden" - || attr.get_content () == "collapse")) { - hidden = true; - } - } - - if (hidden) { - layer.visible = !hidden; - } - - foreach (Tag t in tag) { - string name = t.get_name (); - - if (name == "g") { - Layer sublayer = new Layer (); - parse_layer (layer, t); - layer.subgroups.add (sublayer); - } - - parse_object (layer, t); - } - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "transform") { - layer.transforms = parse_transform (attr.get_content ()); - } - } - } - - void parse_defs (SvgDrawing drawing, Tag tag) { - foreach (Tag t in tag) { - // FIXME: radial - string name = t.get_name (); - - if (name == "linearGradient") { - parse_linear_gradient (drawing, t); - } - } - - foreach (Gradient gradient in drawing.defs.gradients) { - if (gradient.href != null) { - Gradient? referenced; - referenced = drawing.defs.get_gradient_for_id ((!) gradient.href); - - if (referenced != null) { - gradient.copy_stops ((!) referenced); - } - - gradient.href = null; - } - } - } - - void parse_linear_gradient (SvgDrawing drawing, Tag tag) { - Gradient gradient = new Gradient (); - - drawing.defs.add (gradient); - - foreach (Attribute attr in tag.get_attributes ()) { - string name = attr.get_name (); - - // FIXME: gradientUnits - - if (name == "gradientTransform") { - gradient.transforms = parse_transform (attr.get_content ()); - } - - if (name == "href") { - gradient.href = attr.get_content (); - } - - if (name == "x1") { - gradient.x1 = parse_number (attr.get_content ()); - } - - if (name == "y1") { - gradient.y1 = parse_number (attr.get_content ()); - } - - if (name == "x2") { - gradient.x2 = parse_number (attr.get_content ()); - } - - if (name == "y2") { - gradient.y2 = parse_number (attr.get_content ()); - } - - if (name == "id") { - gradient.id = attr.get_content (); - } - } - - foreach (Tag t in tag) { - // FIXME: radial - string name = t.get_name (); - - if (name == "stop") { - parse_stop (gradient, t); - } - } - } - - void parse_stop (Gradient gradient, Tag tag) { - SvgStyle style = SvgStyle.parse (drawing.defs, tag.get_attributes ()); - Stop stop = new Stop (); - - gradient.stops.add (stop); - - foreach (Attribute attr in tag.get_attributes ()) { - string name = attr.get_name (); - - if (name == "offset") { - string stop_offset = attr.get_content (); - - if (stop_offset.index_of ("%") > -1) { - stop_offset = stop_offset.replace ("%", ""); - stop.offset = parse_number (stop_offset) / 100.0; - } else { - stop.offset = parse_number (stop_offset); - } - } - } - - string? stop_color = style.style.get ("stop-color"); - string? stop_opacity = style.style.get ("stop-opacity"); - Color? color = Color.black (); - - if (stop_color != null) { - color = Color.parse (stop_color); - - if (color != null) { - stop.color = (!) color; - } - } - - if (stop_opacity != null && color != null) { - ((!) color).a = parse_number (stop_opacity); - } - } - - void parse_object (Layer layer, Tag tag) { - string name = tag.get_name (); - - if (name == "path") { - parse_path (layer, tag); - } - - if (name == "polygon") { - parse_polygon (layer, tag); - } - - if (name == "polyline") { - parse_polyline (layer, tag); - } - - if (name == "rect") { - parse_rect (layer, tag); - } - - if (name == "circle") { - parse_circle (layer, tag); - } - - if (name == "ellipse") { - parse_ellipse (layer, tag); - } - - if (name == "line") { - parse_line (layer, tag); - } - } - - private void parse_polygon (Layer layer, Tag tag) { - } - - private void parse_polyline (Layer layer, Tag tag) { - } - - private void parse_rect (Layer layer, Tag tag) { - Rectangle rectangle = new Rectangle (); - - foreach (Attribute attr in tag.get_attributes ()) { - string attribute = attr.get_name (); - - if (attribute == "x") { - rectangle.x = parse_number (attr.get_content ()); - } - - if (attribute == "y") { - rectangle.y = parse_number (attr.get_content ()); - } - - if (attribute == "width") { - rectangle.width = parse_number (attr.get_content ()); - } - - if (attribute == "height") { - rectangle.height = parse_number (attr.get_content ()); - } - - if (attribute == "rx") { - rectangle.rx = parse_number (attr.get_content ()); - } - - if (attribute == "ry") { - rectangle.ry = parse_number (attr.get_content ()); - } - } - - rectangle.transforms = get_transform (tag.get_attributes ()); - rectangle.style = SvgStyle.parse (drawing.defs,tag.get_attributes ()); - rectangle.visible = is_visible (tag); - - layer.add_object (rectangle); - } - - private void parse_circle (Layer layer, Tag tag) { - } - - private void parse_ellipse (Layer layer, Tag tag) { - } - - private void parse_line (Layer layer, Tag tag) { - } - - // FIXME: reverse order? - public SvgTransforms parse_transform (string transforms) { - string[] functions; - string transform = transforms; - SvgTransforms transform_functions; - - transform_functions = new SvgTransforms (); - - transform = transform.replace ("\t", " "); - transform = transform.replace ("\n", " "); - transform = transform.replace ("\r", " "); - - // use only a single space as separator - while (transform.index_of (" ") > -1) { - transform = transform.replace (" ", " "); - } - - if (unlikely (transform.index_of (")") == -1)) { - warning ("No parenthesis in transform function."); - return transform_functions; - } - - // add separator - transform = transform.replace (") ", "|"); - transform = transform.replace (")", "|"); - functions = transform.split ("|"); - - for (int i = 0; i < functions.length; i++) { - if (functions[i].has_prefix ("translate")) { - transform_functions.add (translate (functions[i])); - } - - if (functions[i].has_prefix ("scale")) { - transform_functions.add (scale (functions[i])); - } - - if (functions[i].has_prefix ("matrix")) { - transform_functions.add (matrix (functions[i])); - } - - // TODO: rotate etc. - } - - return transform_functions; - } - - private SvgTransform matrix (string function) { - string parameters = get_transform_parameters (function); - string[] p = parameters.split (" "); - SvgTransform transform = new SvgTransform (); - transform.type = TransformType.MATRIX; - - if (unlikely (p.length != 6)) { - warning ("Expecting six parameters for matrix transformation."); - return transform; - } - - for (int i = 0; i < 6; i++) { - double argument = SvgParser.parse_double (p[i]); - transform.arguments.add (argument); - } - - return transform; - } - - private static string remove_unit (string d) { - string s = d.replace ("pt", ""); - s = s.replace ("pc", ""); - s = s.replace ("mm", ""); - s = s.replace ("cm", ""); - s = s.replace ("in", ""); - s = s.replace ("px", ""); - return s; - } - - public static double parse_number (string? number_with_unit) { - if (number_with_unit == null) { - return 0; - } - - string d = (!) number_with_unit; - string s = remove_unit (d); - double n = SvgParser.parse_double (s); - - if (d.has_suffix ("pt")) { - n *= 1.25; - } else if (d.has_suffix ("pc")) { - n *= 15; - } else if (d.has_suffix ("mm")) { - n *= 3.543307; - } else if (d.has_suffix ("cm")) { - n *= 35.43307; - } else if (d.has_suffix ("in")) { - n *= 90; - } - - return n; - } - - private SvgTransform scale (string function) { - string parameters = get_transform_parameters (function); - string[] p = parameters.split (" "); - SvgTransform transform = new SvgTransform (); - transform.type = TransformType.SCALE; - - if (p.length > 0) { - transform.arguments.add (SvgParser.parse_double (p[0])); - } - - if (p.length > 1) { - transform.arguments.add (SvgParser.parse_double (p[1])); - } - - return transform; - } - - private SvgTransform translate (string function) { - string parameters = get_transform_parameters (function); - string[] p = parameters.split (" "); - SvgTransform transform = new SvgTransform (); - transform.type = TransformType.TRANSLATE; - - if (p.length > 0) { - transform.arguments.add (SvgParser.parse_double (p[0])); - } - - if (p.length > 1) { - transform.arguments.add (SvgParser.parse_double (p[1])); - } - - return transform; - } - - private string get_transform_parameters (string function) { - int i; - string param = ""; - - i = function.index_of ("("); - return_val_if_fail (i != -1, param); - param = function.substring (i); - - param = param.replace ("(", ""); - param = param.replace ("\n", " "); - param = param.replace ("\t", " "); - param = param.replace (",", " "); - - while (param.index_of (" ") > -1) { - param.replace (" ", " "); - } - - return param.strip(); - } - - private bool is_visible (Tag tag) { - bool hidden = false; - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "display" && attr.get_content () == "none") { - hidden = true; - } - - if (attr.get_name () == "visibility" - && (attr.get_content () == "hidden" - || attr.get_content () == "collapse")) { - hidden = true; - } - } - - return !hidden; - } - - private SvgTransforms get_transform (Attributes attributes) { - foreach (Attribute attr in attributes) { - if (attr.get_name () == "transform") { - return parse_transform (attr.get_content ()); - } - } - - return new SvgTransforms (); - } - - private void parse_path (Layer layer, Tag tag) { - SvgPath path = new SvgPath (); - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "d") { - path.points = parse_points (attr.get_content ()); - } - } - - path.transforms = get_transform (tag.get_attributes ()); - path.style = SvgStyle.parse (drawing.defs, tag.get_attributes ()); - path.visible = is_visible (tag); - - layer.add_object (path); - } - - public Gee.ArrayList<Points> parse_points (string data) { - Gee.ArrayList<Points> path_data = new Gee.ArrayList<Points> (); - Points points = new Points (); - BezierPoints[] bezier_points; - int points_size; - - SvgParser.get_bezier_points (data, out bezier_points, out points_size, true); - - for (int i = 0; i < points_size; i++) { - // FIXME: add more types - if (bezier_points[i].type == 'M') { - points.x = bezier_points[i].x0; - points.y = bezier_points[i].y0; - } else if (bezier_points[i].type == 'C') { - points.add (bezier_points[i].x0); - points.add (bezier_points[i].y0); - points.add (bezier_points[i].x1); - points.add (bezier_points[i].y1); - points.add (bezier_points[i].x2); - points.add (bezier_points[i].y2); - } else if (bezier_points[i].type == 'L') { - points.add (bezier_points[i].x0); - points.add (bezier_points[i].y0); - points.add (bezier_points[i].x0); - points.add (bezier_points[i].y0); - points.add (bezier_points[i].x0); - points.add (bezier_points[i].y0); - } else if (bezier_points[i].type == 'z') { - points.closed = true; - path_data.add (points); - points = new Points (); - } else { - string type = (!) bezier_points[i].type.to_string (); - warning (@"SVG conversion not implemented for $type"); - } - } - - if (points.point_data.size > 0) { - path_data.add (points); - } - - return path_data; - } - } - - }
diff --git libbirdfont/Svg/SvgFont.vala(deleted)
--- a/libbirdfont/Svg/SvgFont.vala +++ /dev/null @@ -1,265 +1,1 @@ - /* - Copyright (C) 2013 2014 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using B; - - namespace BirdFont { - - class SvgFont : GLib.Object { - Font font; - double units = 1; - double font_advance = 0; - - public SvgFont (Font f) { - this.font = f; - } - - /** Load svg font from file. */ - public void load (string path) { - string data; - XmlParser xml_parser; - try { - FileUtils.get_contents (path, out data); - xml_parser = new XmlParser (data); - parse_svg_font (xml_parser.get_root_tag ()); - } catch (GLib.Error e) { - warning (e.message); - } - } - - void parse_svg_font (Tag tag) { - foreach (Tag t in tag) { - if (t.get_name () == "defs") { - parse_svg_font (t); - } - - if (t.get_name () == "font") { - parse_font_tag (t); - parse_svg_font (t); - } - - if (t.get_name () == "font-face") { - parse_font_limits (t); - } - - if (t.get_name () == "hkern") { - parse_hkern (t); - } - - if (t.get_name () == "glyph") { - parse_glyph (t); - } - } - } - - void parse_hkern (Tag tag) { - string left = ""; - string right = ""; - string left_name = ""; - string right_name = ""; - double kerning = 0; - unichar l, r; - StringBuilder sl, sr; - GlyphRange grr, grl; - KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes (); - - foreach (Attribute attr in tag.get_attributes ()) { - // left - if (attr.get_name () == "u1") { - left = attr.get_content (); - } - - // right - if (attr.get_name () == "u2") { - right = attr.get_content (); - } - - if (attr.get_name () == "g1") { - left_name = attr.get_content (); - } - - if (attr.get_name () == "g2") { - right_name = attr.get_content (); - } - - // kerning - if (attr.get_name () == "k") { - kerning = double.parse (attr.get_content ()) * units; - } - } - - // FIXME: ranges and sequences for u1 & u2 + g1 & g2 - foreach (string lk in left.split (",")) { - foreach (string rk in right.split (",")) { - l = get_unichar (lk); - r = get_unichar (rk); - - sl = new StringBuilder (); - sl.append_unichar (l); - - sr = new StringBuilder (); - sr.append_unichar (r); - - try { - grl = new GlyphRange (); - grl.parse_ranges (sl.str); - - grr = new GlyphRange (); - grr.parse_ranges (sr.str); - - classes.set_kerning (grl, grr, -kerning); - } catch (MarkupError e) { - warning (e.message); - } - } - } - } - - void parse_font_limits (Tag tag) { - double top_limit = 0; - double bottom_limit = 0; - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "units-per-em") { - units = 100.0 / double.parse (attr.get_content ()); - } - } - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "ascent") { - top_limit = double.parse (attr.get_content ()); - } - - if (attr.get_name () == "descent") { - bottom_limit = double.parse (attr.get_content ()); - } - } - - top_limit *= units; - bottom_limit *= units; - - font.bottom_limit = bottom_limit; - font.top_limit = top_limit; - } - - void parse_font_tag (Tag tag) { - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "horiz-adv-x") { - font_advance = double.parse (attr.get_content ()); - } - - if (attr.get_name () == "id") { - font.set_name (attr.get_content ()); - } - } - } - - /** Obtain unichar value from either a character, a hex representation of - * a character or series of characters (f, &#x6d; or ffi). - */ - static unichar get_unichar (string val) { - string v = val; - unichar unicode_value; - - if (val == "&") { - return '&'; - } - - // TODO: parse ligatures - if (v.has_prefix ("&")) { - // parse hex value - v = v.substring (0, v.index_of (";")); - v = v.replace ("&#x", "U+"); - v = v.replace (";", ""); - unicode_value = Font.to_unichar (v); - } else { - // obtain unicode value - - if (v.char_count () > 1) { - warning ("font contains ligatures"); - return '\0'; - } - - unicode_value = v.get_char (0); - } - - return unicode_value; - } - - bool is_ligature (string v) { - if (v.has_prefix ("&")) { - return false; - } - - return v.char_count () > 1; - } - - void parse_glyph (Tag tag) { - unichar unicode_value = 0; - string glyph_name = ""; - string svg = ""; - Glyph glyph; - GlyphCollection glyph_collection; - double advance = font_advance; - string ligature = ""; - SvgParser parser = new SvgParser (); - StringBuilder unicode_name; - - parser.set_format (SvgFormat.INKSCAPE); - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "unicode") { - unicode_value = get_unichar (attr.get_content ()); - - if (glyph_name == "") { - glyph_name = attr.get_content (); - } - - if (is_ligature (attr.get_content ())) { - ligature = attr.get_content (); - } - } - - // svg data - if (attr.get_name () == "d") { - svg = attr.get_content (); - } - - if (attr.get_name () == "glyph-name") { - glyph_name = attr.get_content (); - } - - if (attr.get_name () == "horiz-adv-x") { - advance = double.parse (attr.get_content ()); - } - } - - unicode_name = new StringBuilder (); - unicode_name.append_unichar (unicode_value); - - glyph = new Glyph (unicode_name.str, unicode_value); - parser.add_path_to_glyph (svg, glyph, true, units); - glyph.right_limit = glyph.left_limit + advance * units; - - // FIXME: add svg font ligatures - - glyph_collection = new GlyphCollection (unicode_value, glyph_name); - glyph_collection.insert_glyph (glyph, true); - - font.add_glyph_collection (glyph_collection); - } - } - - }
--- a/libbirdfont/Svg/SvgFontFormatWriter.vala +++ /dev/null @@ -1,133 +1,1 @@ - /* - Copyright (C) 2012, 2014 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - namespace BirdFont { - - class SvgFontFormatWriter : GLib.Object { - - DataOutputStream os; - - public SvgFontFormatWriter () { - } - - public void open (File file) throws Error { - if (file.query_exists ()) { - throw new FileError.EXIST ("SvgFontFormatWriter: file exists."); - } - - os = new DataOutputStream (file.create(FileCreateFlags.REPLACE_DESTINATION)); - } - - public void close () throws Error { - os.close (); - } - - public void write_font_file (Font font) throws Error { - string font_name = font.get_full_name (); - - int units_per_em = 100; - - int ascent = 80; - int descent = -20; - - StringBuilder b; - - Glyph? g; - Glyph glyph; - unichar index = 0; - - string uni; - - KerningClasses classes; - - put ("""<?xml version="1.0" standalone="no"?>"""); - put ("""<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >"""); - put ("""<svg>"""); - - // (metadata goes here) - - put ("<defs>"); - - put (@"<font id=\"$font_name\" horiz-adv-x=\"250\" >"); - put (@"<font-face units-per-em=\"$(to_float (units_per_em))\" ascent=\"$(to_float (ascent))\" descent=\"$(to_float (descent))\" />"); - - // (missing-glyph goes here) - - // regular glyphs - while (true) { - g = font.get_glyph_index (index++); - - if (g == null) { - break; - } - - glyph = (!) g; - - b = new StringBuilder (); - b.append_unichar (glyph.get_unichar ()); - - if (glyph.get_unichar () >= ' ' && b.str.validate ()) { - if (b.str == "\"" || b.str == "&" || b.str == "<" || b.str == ">") { - uni = Font.to_hex_code (glyph.get_unichar ()); - put (@"<glyph unicode=\"&#x$(uni);\" horiz-adv-x=\"$(to_float (glyph.get_width ()))\" d=\"$(glyph.get_svg_data ())\" />"); - } else { - put (@"<glyph unicode=\"$(b.str)\" horiz-adv-x=\"$(to_float (glyph.get_width ()))\" d=\"$(glyph.get_svg_data ())\" />"); - } - } - } - - // FIXME: ligatures - classes = BirdFont.get_current_font ().get_kerning_classes (); - classes.all_pairs ((kerning) => { - string l, r; - - foreach (Kerning k in kerning.kerning) { - try { - if (k.glyph != null) { - l = Font.to_hex_code (kerning.character.unichar_code); - r = Font.to_hex_code (((!)k.glyph).unichar_code); - os.put_string (@"<hkern u1=\"&#x$l;\" u2=\"&#x$r;\" k=\"$(to_float (-k.val))\"/>\n"); - } else { - warning ("No glyph."); - } - } catch (GLib.Error e) { - warning (e.message); - } - } - }); - - put ("</font>"); - put ("</defs>"); - put ("</svg>"); - } - - string to_float (double d) { - string s = @"$d"; - if (s.index_of ("e") != -1) { - return "0".dup (); - } - return s.replace (",", "."); - } - - /** Write a new line */ - private void put (string line) throws Error { - os.put_string (line); - os.put_string ("\n"); - } - - } - - - }
diff --git libbirdfont/Svg/SvgParser.vala(deleted)
--- a/libbirdfont/Svg/SvgParser.vala +++ /dev/null @@ -1,2123 +1,1 @@ - /* - Copyright (C) 2012 2013 2014 2015 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using B; - using Math; - - namespace BirdFont { - - public enum SvgFormat { - NONE, - INKSCAPE, - ILLUSTRATOR - } - - public enum SvgType { - COLOR, - REGULAR - } - - public class SvgParser { - - SvgFormat format = SvgFormat.ILLUSTRATOR; - - public SvgParser () { - } - - public void set_format (SvgFormat f) { - format = f; - } - - public static void import (SvgType type) { - FileChooser fc = new FileChooser (); - fc.file_selected.connect ((p) => { - string path; - - if (p == null) { - return; - } - - path = (!) p; - - if (type == SvgType.REGULAR) { - import_svg (path); - } else if (type == SvgType.COLOR) { - Glyph glyph = MainWindow.get_current_glyph (); - import_color_svg (glyph, path); - } - }); - - fc.add_extension ("svg"); - MainWindow.file_chooser (t_("Import"), fc, FileChooser.LOAD); - } - - public static void import_color_svg (Glyph glyph, string path) { - SvgFile svg_file = new SvgFile (); - EmbeddedSvg drawing = svg_file.parse (path); - - Layer layer = new Layer (); - layer.name = "SVG"; - layer.add_object (drawing); - - glyph.add_layer (layer); - - // FIXME: update GUI - } - - public static void import_folder (SvgType type) { - FileChooser fc = new FileChooser (); - fc.file_selected.connect ((p) => { - string path; - File svg_folder; - File svg; - bool imported; - FileEnumerator enumerator; - FileInfo? file_info; - string file_name; - Font font; - - if (p == null) { - return; - } - - path = (!) p; - svg_folder = File.new_for_path (path); - font = BirdFont.get_current_font (); - - try { - enumerator = svg_folder.enumerate_children (FileAttribute.STANDARD_NAME, 0); - while ((file_info = enumerator.next_file ()) != null) { - file_name = ((!) file_info).get_name (); - - if (file_name.has_suffix (".svg")) { - svg = get_child (svg_folder, file_name); - imported = import_svg_file (font, svg, type); - - if (!imported) { - warning ("Can't import %s.", (!) svg.get_path ()); - } else { - font.touch (); - } - } - } - } catch (Error e) { - warning (e.message); - } - }); - - MainWindow.file_chooser (t_("Import"), fc, FileChooser.LOAD | FileChooser.DIRECTORY); - } - - public static void import_svg_data (string xml_data, SvgFormat format = SvgFormat.NONE) { - PathList path_list = new PathList (); - Glyph glyph; - string[] lines = xml_data.split ("\n"); - bool has_format = false; - SvgParser parser = new SvgParser (); - XmlParser xmlparser; - - foreach (string l in lines) { - if (l.index_of ("Illustrator") > -1 || l.index_of ("illustrator") > -1) { - parser.set_format (SvgFormat.ILLUSTRATOR); - has_format = true; - } - - if (l.index_of ("Inkscape") > -1 || l.index_of ("inkscape") > -1) { - parser.set_format (SvgFormat.INKSCAPE); - has_format = true; - } - } - - if (format != SvgFormat.NONE) { - parser.set_format (format); - } - - // parse the file - if (!has_format) { - warn_if_test ("No format identifier found in SVG parser.\n"); - } - - xmlparser = new XmlParser (xml_data); - - if (!xmlparser.validate()) { - warning("Invalid XML in SVG parser."); - } - - path_list = parser.parse_svg_file (xmlparser.get_root_tag ()); - - glyph = MainWindow.get_current_glyph (); - foreach (Path p in path_list.paths) { - PathObject path = new PathObject.for_path (p); - glyph.add_object (path); - glyph.add_active_object (null, path); // FIXME: groups - path.update_region_boundaries (); - } - - glyph.close_path (); - } - - public static string replace (string content, string start, string stop, string replacement) { - int i_tag = content.index_of (start); - int end_tag = content.index_of (stop, i_tag); - string c = ""; - - if (i_tag > -1) { - c = content.substring (0, i_tag) - + replacement - + content.substring (end_tag + stop.length); - } else { - c = content; - } - - return c; - } - - public static void import_svg (string path) { - string svg_data; - try { - FileUtils.get_contents (path, out svg_data); - } catch (GLib.Error e) { - warning (e.message); - } - import_svg_data (svg_data); - } - - private PathList parse_svg_file (Tag tag) { - Layer pl = new Layer (); - - foreach (Tag t in tag) { - - if (t.get_name () == "g") { - parse_layer (t, pl); - } - - if (t.get_name () == "switch") { - parse_layer (t, pl); - } - - if (t.get_name () == "path") { - parse_path (t, pl); - } - - if (t.get_name () == "polygon") { - parse_polygon (t, pl); - } - - if (t.get_name () == "polyline") { - parse_polyline (t, pl); - } - - if (t.get_name () == "circle") { - parse_circle (t, pl); - } - - if (t.get_name () == "ellipse") { - parse_ellipse (t, pl); - } - - if (t.get_name () == "line") { - parse_line (t, pl); - } - } - - return pl.get_all_paths (); - } - - private void parse_layer (Tag tag, Layer pl) { - Layer layer; - bool hidden = false; - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "display" && attr.get_content () == "none") { - hidden = true; - } - - if (attr.get_name () == "visibility" - && (attr.get_content () == "hidden" - || attr.get_content () == "collapse")) { - hidden = true; - } - } - - if (hidden) { - return; - } - - foreach (Tag t in tag) { - if (t.get_name () == "path") { - parse_path (t, pl); - } - - if (t.get_name () == "g") { - layer = new Layer (); - parse_layer (t, layer); - pl.subgroups.add (layer); - } - - if (t.get_name () == "polygon") { - parse_polygon (t, pl); - } - - if (t.get_name () == "polyline") { - parse_polyline (t, pl); - } - - if (t.get_name () == "rect") { - parse_rect (t, pl); - } - - if (t.get_name () == "circle") { - parse_circle (t, pl); - } - - if (t.get_name () == "ellipse") { - parse_ellipse (t, pl); - } - - if (t.get_name () == "line") { - parse_line (t, pl); - } - } - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "transform") { - transform (attr.get_content (), pl); - } - } - } - - private void transform (string transform_functions, Layer layer) { - PathList path_list = new PathList (); - - foreach (Object o in layer.objects) { - if (o is PathObject) { - path_list.add (((PathObject) o).get_path ()); - } - } - - transform_paths (transform_functions, path_list); - transform_subgroups (transform_functions, layer); - } - - private void transform_subgroups (string transform_functions, Layer layer) { - foreach (Layer subgroup in layer.subgroups) { - transform (transform_functions, subgroup); - } - } - - private void transform_paths (string transform_functions, PathList pl) { - string data = transform_functions.dup (); - string[] functions; - - // use only a single space as separator - while (data.index_of (" ") > -1) { - data = data.replace (" ", " "); - } - - return_if_fail (data.index_of (")") > -1); - - // add separator - data = data.replace (") ", "|"); - data = data.replace (")", "|"); - functions = data.split ("|"); - - for (int i = functions.length - 1; i >= 0; i--) { - if (functions[i].has_prefix ("translate")) { - translate (functions[i], pl); - } - - if (functions[i].has_prefix ("scale")) { - scale (functions[i], pl); - } - - if (functions[i].has_prefix ("matrix")) { - matrix (functions[i], pl); - } - - // TODO: rotate etc. - } - } - - /** @param path a path in the cartesian coordinate system - * The other parameters are in the SVG coordinate system. - */ - public static void apply_matrix (Path path, double a, double b, double c, - double d, double e, double f){ - - double dx, dy; - Font font = BirdFont.get_current_font (); - Glyph glyph = MainWindow.get_current_glyph (); - - foreach (EditPoint ep in path.points) { - ep.tie_handles = false; - ep.reflective_point = false; - } - - foreach (EditPoint ep in path.points) { - apply_matrix_on_handle (ep.get_right_handle (), a, b, c, d, e, f); - apply_matrix_on_handle (ep.get_left_handle (), a, b, c, d, e, f); - - ep.independent_y = font.top_position - ep.independent_y; - ep.independent_x -= glyph.left_limit; - - dx = a * ep.independent_x + c * ep.independent_y + e; - dy = b * ep.independent_x + d * ep.independent_y + f; - - ep.independent_x = dx; - ep.independent_y = dy; - - ep.independent_y = font.top_position - ep.independent_y; - ep.independent_x += glyph.left_limit; - } - } - - public static void apply_matrix_on_handle (EditPointHandle h, - double a, double b, double c, - double d, double e, double f){ - - double dx, dy; - Font font = BirdFont.get_current_font (); - Glyph glyph = MainWindow.get_current_glyph (); - - h.y = font.top_position - h.y; - h.x -= glyph.left_limit; - - dx = a * h.x + c * h.y + e; - dy = b * h.x + d * h.y + f; - - h.x = dx; - h.y = dy; - - h.y = font.top_position - h.y; - h.x += glyph.left_limit; - } - - - private void matrix (string function, PathList pl) { - string parameters = get_transform_parameters (function); - string[] p = parameters.split (" "); - - if (p.length != 6) { - warning ("Expecting six parameters for matrix transformation."); - return; - } - - foreach (Path path in pl.paths) { - apply_matrix (path, parse_double (p[0]), parse_double (p[1]), - parse_double (p[2]), parse_double (p[3]), - parse_double (p[4]), parse_double (p[5])); - } - } - - private void scale (string function, PathList pl) { - string parameters = get_transform_parameters (function); - string[] p = parameters.split (" "); - double x, y; - - x = 1; - y = 1; - - if (p.length > 0) { - x = parse_double (p[0]); - } - - if (p.length > 1) { - y = parse_double (p[1]); - } - - foreach (Path path in pl.paths) { - path.scale (-x, y); - } - } - - private void translate (string function, PathList pl) { - string parameters = get_transform_parameters (function); - string[] p = parameters.split (" "); - double x, y; - - x = 0; - y = 0; - - if (p.length > 0) { - x = parse_double (p[0]); - } - - if (p.length > 1) { - y = parse_double (p[1]); - } - - foreach (Path path in pl.paths) { - path.move (x, -y); - } - } - - private string get_transform_parameters (string function) { - int i; - string param = ""; - - i = function.index_of ("("); - return_val_if_fail (i != -1, param); - param = function.substring (i); - - param = param.replace ("(", ""); - param = param.replace ("\n", " "); - param = param.replace ("\t", " "); - param = param.replace (",", " "); - - while (param.index_of (" ") > -1) { - param.replace (" ", " "); - } - - return param.strip(); - } - - private void parse_circle (Tag tag, Layer pl) { - Path p; - double x, y, r; - Glyph g; - PathList npl; - BezierPoints[] bezier_points; - SvgStyle style = new SvgStyle (); - bool hidden = false; - - npl = new PathList (); - - x = 0; - y = 0; - r = 0; - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "cx") { - x = parse_double (attr.get_content ()); - } - - if (attr.get_name () == "cy") { - y = -parse_double (attr.get_content ()); - } - - if (attr.get_name () == "r") { - r = parse_double (attr.get_content ()); - } - - if (attr.get_name () == "display" && attr.get_content () == "none") { - hidden = true; - } - } - - style = SvgStyle.parse (null, tag.get_attributes ()); - - if (hidden) { - return; - } - - bezier_points = new BezierPoints[1]; - bezier_points[0] = new BezierPoints (); - bezier_points[0].type == 'L'; - bezier_points[0].x0 = x; - bezier_points[0].y0 = y; - - g = MainWindow.get_current_glyph (); - move_and_resize (bezier_points, 1, false, 1, g); - - p = CircleTool.create_circle (bezier_points[0].x0, - bezier_points[0].y0, r, PointType.CUBIC); - - npl.add (p); - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "transform") { - transform_paths (attr.get_content (), npl); - } - } - - style.apply (npl); - pl.append_paths (npl); - } - - private void parse_ellipse (Tag tag, Layer pl) { - Path p; - double x, y, rx, ry; - Glyph g; - PathList npl; - BezierPoints[] bezier_points; - SvgStyle style = new SvgStyle (); - bool hidden = false; - - npl = new PathList (); - - x = 0; - y = 0; - rx = 0; - ry = 0; - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "cx") { - x = parse_double (attr.get_content ()); - } - - if (attr.get_name () == "cy") { - y = -parse_double (attr.get_content ()); - } - - if (attr.get_name () == "rx") { - rx = parse_double (attr.get_content ()); - } - - if (attr.get_name () == "ry") { - ry = parse_double (attr.get_content ()); - } - - if (attr.get_name () == "display" && attr.get_content () == "none") { - hidden = true; - } - } - - style = SvgStyle.parse (null, tag.get_attributes ()); - - if (hidden) { - return; - } - - bezier_points = new BezierPoints[1]; - bezier_points[0] = new BezierPoints (); - bezier_points[0].type == 'L'; - bezier_points[0].x0 = x; - bezier_points[0].y0 = y; - - g = MainWindow.get_current_glyph (); - move_and_resize (bezier_points, 1, false, 1, g); - - p = CircleTool.create_ellipse (bezier_points[0].x0, - bezier_points[0].y0, rx, ry, PointType.CUBIC); - - npl.add (p); - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "transform") { - transform_paths (attr.get_content (), npl); - } - } - - style.apply (npl); - pl.append_paths (npl); - } - - private void parse_line (Tag tag, Layer pl) { - Path p; - double x1, y1, x2, y2; - BezierPoints[] bezier_points; - Glyph g; - PathList npl = new PathList (); - SvgStyle style = new SvgStyle (); - bool hidden = false; - - x1 = 0; - y1 = 0; - x2 = 0; - y2 = 0; - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "x1") { - x1 = parse_double (attr.get_content ()); - } - - if (attr.get_name () == "y1") { - y1 = -parse_double (attr.get_content ()); - } - - if (attr.get_name () == "x2") { - x2 = parse_double (attr.get_content ()); - } - - if (attr.get_name () == "xy") { - y2 = -parse_double (attr.get_content ()); - } - - if (attr.get_name () == "display" && attr.get_content () == "none") { - hidden = true; - } - } - - style = SvgStyle.parse (null, tag.get_attributes ()); - - if (hidden) { - return; - } - - bezier_points = new BezierPoints[2]; - bezier_points[0] = new BezierPoints (); - bezier_points[0].type == 'L'; - bezier_points[0].x0 = x1; - bezier_points[0].y0 = y1; - - bezier_points[1] = new BezierPoints (); - bezier_points[1].type == 'L'; - bezier_points[1].x0 = x2; - bezier_points[1].y0 = y2; - - g = MainWindow.get_current_glyph (); - move_and_resize (bezier_points, 4, false, 1, g); - - p = new Path (); - - p.add (bezier_points[0].x0, bezier_points[0].y0); - p.add (bezier_points[1].x0, bezier_points[1].y0); - - p.close (); - p.create_list (); - p.recalculate_linear_handles (); - - npl.add (p); - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "transform") { - transform_paths (attr.get_content (), npl); - } - } - - style.apply (npl); - pl.append_paths (npl); - } - - private void parse_rect (Tag tag, Layer layer) { - Path p; - double x, y, x2, y2; - BezierPoints[] bezier_points; - Glyph g; - PathList npl = new PathList (); - SvgStyle style = new SvgStyle (); - bool hidden = false; - EditPoint ep; - - x = 0; - y = 0; - x2 = 0; - y2 = 0; - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "x") { - x = parse_double (attr.get_content ()); - } - - if (attr.get_name () == "y") { - y = -parse_double (attr.get_content ()); - } - - if (attr.get_name () == "width") { - x2 = parse_double (attr.get_content ()); - } - - if (attr.get_name () == "height") { - y2 = -parse_double (attr.get_content ()); - } - - if (attr.get_name () == "display" && attr.get_content () == "none") { - hidden = true; - } - } - - style = SvgStyle.parse (null, tag.get_attributes ()); - - if (hidden) { - return; - } - - x2 += x; - y2 += y; - - bezier_points = new BezierPoints[4]; - bezier_points[0] = new BezierPoints (); - bezier_points[0].type == 'L'; - bezier_points[0].x0 = x; - bezier_points[0].y0 = y; - - bezier_points[1] = new BezierPoints (); - bezier_points[1].type == 'L'; - bezier_points[1].x0 = x2; - bezier_points[1].y0 = y; - - bezier_points[2] = new BezierPoints (); - bezier_points[2].type == 'L'; - bezier_points[2].x0 = x2; - bezier_points[2].y0 = y2; - - bezier_points[3] = new BezierPoints (); - bezier_points[3].type == 'L'; - bezier_points[3].x0 = x; - bezier_points[3].y0 = y2; - - g = MainWindow.get_current_glyph (); - move_and_resize (bezier_points, 4, false, 1, g); - - p = new Path (); - - ep = p.add (bezier_points[0].x0, bezier_points[0].y0); - ep.set_point_type (PointType.CUBIC); - - ep = p.add (bezier_points[1].x0, bezier_points[1].y0); - ep.set_point_type (PointType.CUBIC); - - ep = p.add (bezier_points[2].x0, bezier_points[2].y0); - ep.set_point_type (PointType.CUBIC); - - ep = p.add (bezier_points[3].x0, bezier_points[3].y0); - ep.set_point_type (PointType.CUBIC); - - p.close (); - p.create_list (); - p.recalculate_linear_handles (); - - npl.add (p); - - // FIXME: right layer for other transforms - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "transform") { - transform_paths (attr.get_content (), npl); - } - } - - style.apply (npl); - layer.append_paths (npl); - } - - private void parse_polygon (Tag tag, Layer layer) { - PathList path_list = get_polyline (tag); - - foreach (Path p in path_list.paths) { - p.close (); - } - - layer.append_paths (path_list); - } - - - private void parse_polyline (Tag tag, Layer layer) { - layer.append_paths (get_polyline (tag)); - } - - private PathList get_polyline (Tag tag) { - Path p = new Path (); - bool hidden = false; - PathList path_list = new PathList (); - SvgStyle style = new SvgStyle (); - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "points") { - p = parse_poly_data (attr.get_content ()); - } - - if (attr.get_name () == "display" && attr.get_content () == "none") { - hidden = true; - } - } - - style = SvgStyle.parse (null, tag.get_attributes ()); - - if (hidden) { - return path_list; - } - - path_list.add (p); - style.apply (path_list); - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "transform") { - transform_paths (attr.get_content (), path_list); - } - } - - return path_list; - } - - private void parse_path (Tag tag, Layer layer) { - Glyph glyph = MainWindow.get_current_glyph (); - PathList path_list = new PathList (); - SvgStyle style = new SvgStyle (); - bool hidden = false; - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "d") { - path_list = parse_svg_data (attr.get_content (), glyph); - } - - if (attr.get_name () == "display" && attr.get_content () == "none") { - hidden = true; - } - - if (attr.get_name () == "visibility" - && (attr.get_content () == "hidden" - || attr.get_content () == "collapse")) { - hidden = true; - } - } - - style = SvgStyle.parse (null, tag.get_attributes ()); - - if (hidden) { - return; - } - - foreach (Path path in path_list.paths) { - layer.add_path (path); - } - - style.apply (path_list); - - // assume the even odd rule is applied and convert the path - // to a path using the non-zero rule - int inside_count; - bool inside; - foreach (Object o1 in layer.objects) { - if (o1 is PathObject) { - Path p1 = ((PathObject) o1).get_path (); - inside_count = 0; - - foreach (Object o2 in layer.objects) { - if (o2 is PathObject) { - Path p2 = ((PathObject) o2).get_path (); - - if (p1 != p2) { - inside = true; - - foreach (EditPoint ep in p1.points) { - if (!is_inside (ep, p2)) { - inside = false; - } - } - - if (inside) { - inside_count++; - } - } - } - } - - if (inside_count % 2 == 0) { - p1.force_direction (Direction.CLOCKWISE); - } else { - p1.force_direction (Direction.COUNTER_CLOCKWISE); - } - } - } - - foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "transform") { - transform_paths (attr.get_content (), path_list); - } - } - } - - public static void create_lines_for_segment (Path path, EditPoint start, EditPoint end, double tolerance) { - double x1, x2, x3; - double y1, y2, y3; - double step_start, step, step_end; - - path.add (start.x, start.y); - - step_start = 0; - step = 0.5; - step_end = 1; - - while (true) { - Path.get_point_for_step (start, end, step_start, out x1, out y1); - Path.get_point_for_step (start, end, step, out x2, out y2); - Path.get_point_for_step (start, end, step_end, out x3, out y3); - - if (!StrokeTool.is_flat (x1, y1, x2, y2, x3, y3, tolerance) - && step_end - step / 2.0 > step_start - && step_end - step / 2.0 > 0.1 - && step > 0.05 - && Path.distance_to_point (start, end) > 1) { - - step /= 2.0; - - if (step < 0.05) { - step = 0.05; - } else { - step_end = step_start + 2 * step; - } - } else { - path.add (x3, y3); - - if (step_end + step < 1) { - step_start = step_end; - step_end += step; - } else { - break; - } - } - } - } - - public static Path get_lines (Path p) { - EditPoint start; - Path path = new Path (); - - if (p.points.size == 0) { - return path; - } - - // create a set of straight lines - start = p.points.get (p.points.size - 1); - - foreach (EditPoint end in p.points) { - create_lines_for_segment (path, start, end, 1); - start = end; - } - - return path; - } - - /** Check if a point is inside using the even odd fill rule. - * The path should only have straight lines. - */ - public static bool is_inside (EditPoint point, Path path) { - EditPoint prev; - bool inside = false; - - if (path.points.size <= 1) { - return false; - } - - if (!(path.xmin <= point.x <= path.xmax)) { - return false; - } - - if (!(path.ymin <= point.y <= path.ymax)) { - return false; - } - - prev = path.points.get (path.points.size - 1); - - foreach (EditPoint p in path.points) { - if ((p.y > point.y) != (prev.y > point.y) - && point.x < (prev.x - p.x) * (point.y - p.y) / (prev.y - p.y) + p.x) { - inside = !inside; - } - - prev = p; - } - - return inside; - } - - /** Add space as separator to svg data. - * @param d svg data - */ - public static string add_separators (string d) { - string data = d; - - data = data.replace (",", " "); - data = data.replace ("a", " a "); - data = data.replace ("A", " A "); - data = data.replace ("m", " m "); - data = data.replace ("M", " M "); - data = data.replace ("h", " h "); - data = data.replace ("H", " H "); - data = data.replace ("v", " v "); - data = data.replace ("V", " V "); - data = data.replace ("l", " l "); - data = data.replace ("L", " L "); - data = data.replace ("q", " q "); - data = data.replace ("Q", " Q "); - data = data.replace ("c", " c "); - data = data.replace ("C", " C "); - data = data.replace ("t", " t "); - data = data.replace ("T", " T "); - data = data.replace ("s", " s "); - data = data.replace ("S", " S "); - data = data.replace ("zM", " z M "); - data = data.replace ("zm", " z m "); - data = data.replace ("z", " z "); - data = data.replace ("Z", " Z "); - data = data.replace ("-", " -"); - data = data.replace ("e -", "e-"); // minus can be either separator or a negative exponent - data = data.replace ("\t", " "); - data = data.replace ("\r\n", " "); - data = data.replace ("\n", " "); - - // use only a single space as separator - while (data.index_of (" ") > -1) { - data = data.replace (" ", " "); - } - - return data; - } - - public void add_path_to_glyph (string d, Glyph g, bool svg_glyph = false, double units = 1) { - PathList p = parse_svg_data (d, g, svg_glyph, units); - foreach (Path path in p.paths) { - g.add_path (path); - } - } - - public static void get_bezier_points (string point_data, out BezierPoints[] bezier_points, out int points, bool svg_glyph) { - double px = 0; - double py = 0; - double px2 = 0; - double py2 = 0; - double cx = 0; - double cy = 0; - string[] c; - double arc_rx, arc_ry; - double arc_rotation; - int large_arc; - int arc_sweep; - double arc_dest_x, arc_dest_y; - - int bi = 0; - - string data = add_separators (point_data); - c = data.split (" "); - - // the arc instruction can use up to eight points - int bezier_points_length = 8 * c.length + 1; - bezier_points = new BezierPoints[bezier_points_length]; - - for (int i = 0; i < bezier_points_length; i++) { - bezier_points[i] = new BezierPoints (); - } - - // parse path - int i = -1; - while (++i < c.length && bi < bezier_points.length) { - if (c[i] == "m") { - while (i + 2 < c.length && is_point (c[i + 1])) { - bezier_points[bi].type = 'M'; - bezier_points[bi].svg_type = 'm'; - - px += parse_double (c[++i]); - - if (svg_glyph) { - py += parse_double (c[++i]); - } else { - py += -parse_double (c[++i]); - } - - bezier_points[bi].x0 = px; - bezier_points[bi].y0 = py; - bi++; - } - } else if (c[i] == "M") { - while (i + 2 < c.length && is_point (c[i + 1])) { - bezier_points[bi].type = 'M'; - bezier_points[bi].svg_type = 'M'; - - px = parse_double (c[++i]); - - if (svg_glyph) { - py = parse_double (c[++i]); - } else { - py = -parse_double (c[++i]); - } - - bezier_points[bi].x0 = px; - bezier_points[bi].y0 = py; - bi++; - } - } else if (c[i] == "h") { - while (i + 1 < c.length && is_point (c[i + 1])) { - bezier_points[bi].type = 'L'; - bezier_points[bi].svg_type = 'h'; - - px += parse_double (c[++i]); - - bezier_points[bi].x0 = px; - bezier_points[bi].y0 = py; - bi++; - } - } else if (i + 1 < c.length && c[i] == "H") { - while (is_point (c[i + 1])) { - bezier_points[bi].type = 'L'; - bezier_points[bi].svg_type = 'H'; - - px = parse_double (c[++i]); - - bezier_points[bi].x0 = px; - bezier_points[bi].y0 = py; - bi++; - } - } else if (c[i] == "v") { - while (i + 1 < c.length && is_point (c[i + 1])) { - bezier_points[bi].type = 'L'; - bezier_points[bi].svg_type = 'v'; - - if (svg_glyph) { - py = py + parse_double (c[++i]); - } else { - py = py - parse_double (c[++i]); - } - - bezier_points[bi].x0 = px; - bezier_points[bi].y0 = py; - bi++; - } - } else if (i + 1 < c.length && c[i] == "V") { - while (is_point (c[i + 1])) { - bezier_points[bi].type = 'L'; - bezier_points[bi].svg_type = 'V'; - - if (svg_glyph) { - py = parse_double (c[++i]); - } else { - py = -parse_double (c[++i]); - } - - bezier_points[bi].x0 = px; - bezier_points[bi].y0 = py; - bi++; - } - } else if (c[i] == "l") { - while (i + 2 < c.length && is_point (c[i + 1])) { - bezier_points[bi].type = 'L'; - bezier_points[bi].svg_type = 'l'; - - cx = px + parse_double (c[++i]); - - if (svg_glyph) { - cy = py + parse_double (c[++i]); - } else { - cy = py - parse_double (c[++i]); - } - - px = cx; - py = cy; - - bezier_points[bi].x0 = cx; - bezier_points[bi].y0 = cy; - bi++; - } - } else if (c[i] == "L") { - while (i + 2 < c.length && is_point (c[i + 1])) { - bezier_points[bi].type = 'L'; - bezier_points[bi].svg_type = 'L'; - - cx = parse_double (c[++i]); - - if (svg_glyph) { - cy = parse_double (c[++i]); - } else { - cy = -parse_double (c[++i]); - } - - px = cx; - py = cy; - - bezier_points[bi].x0 = cx; - bezier_points[bi].y0 = cy; - bi++; - } - } else if (c[i] == "c") { - while (i + 6 < c.length && is_point (c[i + 1])) { - bezier_points[bi].type = 'C'; - bezier_points[bi].svg_type = 'C'; - - cx = px + parse_double (c[++i]); - - if (svg_glyph) { - cy = py + parse_double (c[++i]); - } else { - cy = py - parse_double (c[++i]); - } - - bezier_points[bi].x0 = cx; - bezier_points[bi].y0 = cy; - - cx = px + parse_double (c[++i]); - - if (svg_glyph) { - cy = py + parse_double (c[++i]); - } else { - cy = py - parse_double (c[++i]); - } - - px2 = cx; - py2 = cy; - - bezier_points[bi].x1 = px2; - bezier_points[bi].y1 = py2; - - cx = px + parse_double (c[++i]); - - if (svg_glyph) { - cy = py + parse_double (c[++i]); - } else { - cy = py + -parse_double (c[++i]); - } - - bezier_points[bi].x2 = cx; - bezier_points[bi].y2 = cy; - - px = cx; - py = cy; - - bi++; - } - } else if (c[i] == "C") { - while (i + 6 < c.length && is_point (c[i + 1])) { - bezier_points[bi].type = 'C'; - bezier_points[bi].svg_type = 'C'; - - cx = parse_double (c[++i]); - - if (svg_glyph) { - cy = parse_double (c[++i]); - } else { - cy = -parse_double (c[++i]); - } - - bezier_points[bi].x0 = cx; - bezier_points[bi].y0 = cy; - - cx = parse_double (c[++i]); - - if (svg_glyph) { - cy = parse_double (c[++i]); - } else { - cy = -parse_double (c[++i]); - } - - px2 = cx; - py2 = cy; - - bezier_points[bi].x1 = cx; - bezier_points[bi].y1 = cy; - - cx = parse_double (c[++i]); - - if (svg_glyph) { - cy = parse_double (c[++i]); - } else { - cy = -parse_double (c[++i]); - } - - bezier_points[bi].x2 = cx; - bezier_points[bi].y2 = cy; - - px = cx; - py = cy; - - bi++; - } - } else if (c[i] == "q") { - while (i + 4 < c.length && is_point (c[i + 1])) { - bezier_points[bi].type = 'Q'; - bezier_points[bi].svg_type = 'q'; - - cx = px + parse_double (c[++i]); - - if (svg_glyph) { - cy = py + parse_double (c[++i]); - } else { - cy = py - parse_double (c[++i]); - } - - bezier_points[bi].x0 = cx; - bezier_points[bi].y0 = cy; - - px2 = cx; - py2 = cy; - - cx = px + parse_double (c[++i]); - - if (svg_glyph) { - cy = py + parse_double (c[++i]); - } else { - cy = py - parse_double (c[++i]); - } - - bezier_points[bi].x1 = cx; - bezier_points[bi].y1 = cy; - - px = cx; - py = cy; - - bi++; - } - } else if (c[i] == "Q") { - while (i + 4 < c.length && is_point (c[i + 1])) { - bezier_points[bi].type = 'Q'; - bezier_points[bi].svg_type = 'Q'; - - cx = parse_double (c[++i]); - - if (svg_glyph) { - cy = parse_double (c[++i]); - } else { - cy = -parse_double (c[++i]); - } - - bezier_points[bi].x0 = cx; - bezier_points[bi].y0 = cy; - - px2 = cx; - py2 = cy; - - cx = parse_double (c[++i]); - - if (svg_glyph) { - cy = parse_double (c[++i]); - } else { - cy = -parse_double (c[++i]); - } - - px = cx; - py = cy; - - bezier_points[bi].x1 = cx; - bezier_points[bi].y1 = cy; - - bi++; - } - } else if (c[i] == "t") { - while (i + 2 < c.length && is_point (c[i + 1])) { - bezier_points[bi].type = 'Q'; - bezier_points[bi].svg_type = 't'; - - // the first point is the reflection - cx = 2 * px - px2; - cy = 2 * py - py2; // if (svg_glyph) ? - - bezier_points[bi].x0 = cx; - bezier_points[bi].y0 = cy; - - px2 = cx; - py2 = cy; - - cx = px + parse_double (c[++i]); - - if (svg_glyph) { - cy = py + parse_double (c[++i]); - } else { - cy = py - parse_double (c[++i]); - } - - px = cx; - py = cy; - - bezier_points[bi].x1 = px; - bezier_points[bi].y1 = py; - - bi++; - } - } else if (c[i] == "T") { - while (i + 2 < c.length && is_point (c[i + 1])) { - bezier_points[bi].type = 'Q'; - bezier_points[bi].svg_type = 'T'; - - // the reflection - cx = 2 * px - px2; - cy = 2 * py - py2; - - bezier_points[bi].x0 = cx; - bezier_points[bi].y0 = cy; - - px2 = cx; - py2 = cy; - - cx = parse_double (c[++i]); - - if (svg_glyph) { - cy = parse_double (c[++i]); - } else { - cy = -parse_double (c[++i]); - } - - px = cx; - py = cy; - - bezier_points[bi].x1 = px; - bezier_points[bi].y1 = py; - - bi++; - } - } else if (c[i] == "s") { - while (i + 4 < c.length && is_point (c[i + 1])) { - bezier_points[bi].type = 'C'; - bezier_points[bi].svg_type = 's'; - - // the first point is the reflection - cx = 2 * px - px2; - cy = 2 * py - py2; // if (svg_glyph) ? - - bezier_points[bi].x0 = cx; - bezier_points[bi].y0 = cy; - - cx = px + parse_double (c[++i]); - - if (svg_glyph) { - cy = py + parse_double (c[++i]); - } else { - cy = py - parse_double (c[++i]); - } - - px2 = cx; - py2 = cy; - - bezier_points[bi].x1 = px2; - bezier_points[bi].y1 = py2; - - cx = px + parse_double (c[++i]); - - if (svg_glyph) { - cy = py + parse_double (c[++i]); - } else { - cy = py - parse_double (c[++i]); - } - - bezier_points[bi].x2 = cx; - bezier_points[bi].y2 = cy; - - px = cx; - py = cy; - - bi++; - } - } else if (c[i] == "S") { - while (i + 4 < c.length && is_point (c[i + 1])) { - bezier_points[bi].type = 'C'; - bezier_points[bi].svg_type = 'S'; - - // the reflection - cx = 2 * px - px2; - cy = 2 * py - py2; // if (svg_glyph) ? - - bezier_points[bi].x0 = cx; - bezier_points[bi].y0 = cy; - - // the other two are regular cubic points - cx = parse_double (c[++i]); - - if (svg_glyph) { - cy = parse_double (c[++i]); - } else { - cy = -parse_double (c[++i]); - } - - px2 = cx; - py2 = cy; - - bezier_points[bi].x1 = px2; - bezier_points[bi].y1 = py2; - - cx = parse_double (c[++i]); - - if (svg_glyph) { - cy = parse_double (c[++i]); - } else { - cy = -parse_double (c[++i]); - } - - bezier_points[bi].x2 = cx; - bezier_points[bi].y2 = cy; - - px = cx; - py = cy; - - bi++; - } - } else if (c[i] == "a") { - while (i + 7 < c.length && is_point (c[i + 1])) { - arc_rx = parse_double (c[++i]); - arc_ry = parse_double (c[++i]); - - arc_rotation = parse_double (c[++i]); - large_arc = parse_int (c[++i]); - arc_sweep = parse_int (c[++i]); - - cx = px + parse_double (c[++i]); - - if (svg_glyph) { - cy = py + parse_double (c[++i]); - } else { - cy = py - parse_double (c[++i]); - } - - arc_dest_x = cx; - arc_dest_y = cy; - - add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy); - - px = cx; - py = cy; - } - } else if (i + 7 < c.length && c[i] == "A") { - while (is_point (c[i + 1])) { - arc_rx = parse_double (c[++i]); - arc_ry = parse_double (c[++i]); - - arc_rotation = parse_double (c[++i]); - large_arc = parse_int (c[++i]); - arc_sweep = parse_int (c[++i]); - - cx = parse_double (c[++i]); - - if (svg_glyph) { - cy = parse_double (c[++i]); - } else { - cy = -parse_double (c[++i]); - } - - arc_dest_x = cx; - arc_dest_y = cy; - - add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy); - - px = cx; - py = cy; - - - } - } else if (c[i] == "z") { - bezier_points[bi].type = 'z'; - bezier_points[bi].svg_type = 'z'; - - bi++; - } else if (c[i] == "Z") { - bezier_points[bi].type = 'z'; - bezier_points[bi].svg_type = 'z'; - - bi++; - } else if (c[i] == "") { - } else if (c[i] == " ") { - } else { - warning (@"Unknown instruction: $(c[i])"); - } - } - - if (bi == 0) { - warning ("No points in path."); - } - - points = bi; - } - - /** - * @param d svg data - * @param glyph use lines from this glyph but don't add the generated paths - * @param svg_glyph parse svg glyph with origo in lower left corner - * - * @return the new paths - */ - public PathList parse_svg_data (string d, Glyph glyph, bool svg_glyph = false, double units = 1) { - Font font; - PathList path_list = new PathList (); - BezierPoints[] bezier_points; - int points; - - font = BirdFont.get_current_font (); - get_bezier_points (d, out bezier_points, out points, svg_glyph); - - if (points == 0) { - warning ("No points in path."); - return path_list; - } - - move_and_resize (bezier_points, points, svg_glyph, units, glyph); - - if (format == SvgFormat.ILLUSTRATOR) { - path_list = create_paths_illustrator (bezier_points, points); - } else { - path_list = create_paths_inkscape (bezier_points, points); - } - - // TODO: Find out if it is possible to tie handles. - return path_list; - } - - void move_and_resize (BezierPoints[] b, int num_b, bool svg_glyph, double units, Glyph glyph) { - Font font = BirdFont.get_current_font (); - - for (int i = 0; i < num_b; i++) { - // resize all points - b[i].x0 *= units; - b[i].y0 *= units; - b[i].x1 *= units; - b[i].y1 *= units; - b[i].x2 *= units; - b[i].y2 *= units; - - // move all points - if (svg_glyph) { - b[i].x0 += glyph.left_limit; - b[i].y0 += font.base_line; - b[i].x1 += glyph.left_limit; - b[i].y1 += font.base_line; - b[i].x2 += glyph.left_limit; - b[i].y2 += font.base_line; - } else { - b[i].x0 += glyph.left_limit; - b[i].y0 += font.top_position; - b[i].x1 += glyph.left_limit; - b[i].y1 += font.top_position; - b[i].x2 += glyph.left_limit; - b[i].y2 += font.top_position; - } - } - } - - void find_last_handle (int start_index, BezierPoints[] b, int num_b, out double left_x, out double left_y, out PointType last_type) { - BezierPoints last = new BezierPoints (); - bool found = false; - - left_x = 0; - left_y = 0; - last_type = PointType.NONE; - - return_if_fail (b.length != 0); - return_if_fail (b[0].type != 'z'); - return_if_fail (num_b < b.length); - - if (num_b == 2) { - left_x = b[0].x0 + (b[1].x0 - b[0].x0) / 3.0; - left_y = b[0].y0 + (b[1].y0 - b[0].y0) / 3.0; - last_type = PointType.LINE_CUBIC; - return; - } - - for (int i = start_index; i < num_b; i++) { - switch (b[i].type) { - case 'Q': - break; - case 'C': - break; - case 'z': - found = true; - break; - default: - break; - } - - if (found || i + 1 == num_b) { - - return_if_fail (i >= 1); - - if (b[i - 1].type == 'Q') { - return_if_fail (i >= 1); - left_x = b[i - 1].x0; - left_y = b[i - 1].y0; - last_type = PointType.QUADRATIC; - } else if (b[i - 1].type == 'C') { - return_if_fail (i >= 1); - left_x = b[i - 1].x1; - left_y = b[i - 1].y1; - last_type = PointType.CUBIC; - } else if (b[i - 1].type == 'S') { - return_if_fail (i >= 1); - left_x = b[i - 1].x1; - left_y = b[i - 1].y1; - last_type = PointType.CUBIC; - } else if (b[i - 1].type == 'L' || last.type == 'M') { - return_if_fail (i >= 2); // FIXME: -2 can be C or L - left_x = b[i - 2].x0 + (b[i - 1].x0 - b[i - 2].x0) / 3.0; - left_y = b[i - 2].y0 + (b[i - 1].y0 - b[i - 2].y0) / 3.0; - last_type = PointType.LINE_CUBIC; - } else { - warning (@"Unexpected type. $(b[i - 1])\n"); - } - return; - } - - last = b[i]; - } - - warning ("Last point not found."); - } - - PathList create_paths_inkscape (BezierPoints[] b, int num_b) { - double last_x; - double last_y; - PointType last_type; - Path path; - PathList path_list = new PathList (); - EditPoint ep = new EditPoint (); - Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> (); - - path = new Path (); - - if (num_b == 0) { - warning ("No SVG data"); - return path_list; - } - - if (b[0].type != 'M') { - warning ("Path must begin with M or m."); - return path_list; - } - - find_last_handle (0, b, num_b, out last_x, out last_y, out last_type); - - for (int i = 0; i < num_b; i++) { - if (b[i].type == '\0') { - warning ("Parser error."); - return path_list; - } - - if (b[i].type == 'z') { - path.close (); - path.create_list (); - path.recalculate_linear_handles (); - path_list.add (path); - path = new Path (); - - if (i + 1 >= num_b) { - break; - } else { - find_last_handle (i + 1, b, num_b, out last_x, out last_y, out last_type); - } - } - - if (i >= num_b) { - break; - } - - if (b[i].type == 'M') { - ep = path.add (b[i].x0, b[i].y0); - ep.set_point_type (PointType.CUBIC); - - ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC); - - if (i == 0 || (b[i - 1].type == 'z')) { - ep.get_left_handle ().set_point_type (last_type); - ep.get_left_handle ().move_to_coordinate (last_x, last_y); - } else { - if (b[i - 1].type == 'C' || b[i - 1].type == 'S') { - ep.get_left_handle ().set_point_type (PointType.CUBIC); - ep.get_left_handle ().move_to_coordinate (b[i + 1].x1, b[i + 1].y1); - } - - if (b[i + 1].type == 'C' || b[i - 1].type == 'S') { - ep.get_right_handle ().set_point_type (PointType.CUBIC); - ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0); - } else if (b[i + 1].type == 'L' || b[i + 1].type == 'M') { - ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); - } - } - } - - if (b[i].type == 'L') { - return_val_if_fail (i != 0, path_list); - - ep = path.add (b[i].x0, b[i].y0); - ep.set_point_type (PointType.CUBIC); - ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); - ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC); - - if (b[i + 1].type == 'L' || b[i + 1].type == 'M' || b[i + 1].type == 'z') { - ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); - } - - if (b[i -1].type == 'L' || b[i - 1].type == 'M') { - ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC); - } - } - - if (b[i].type == 'Q') { - return_val_if_fail (i != 0, path_list); - - ep.set_point_type (PointType.QUADRATIC); - - ep.get_right_handle ().set_point_type (PointType.QUADRATIC); - ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0); - - if (b[i + 1].type != 'z') { - ep = path.add (b[i].x1, b[i].y1); - - ep.get_left_handle ().set_point_type (PointType.QUADRATIC); - ep.get_left_handle ().move_to_coordinate (b[i].x0, b[i].y0); - } - } - - if (b[i].type == 'C' || b[i].type == 'S') { - return_val_if_fail (i != 0, path_list); - - ep.set_point_type (PointType.CUBIC); - - ep.get_right_handle ().set_point_type (PointType.CUBIC); - ep.get_right_handle ().move_to_coordinate (b[i].x0, b[i].y0); - - if (b[i].type == 'S') { - smooth_points.add (ep); - } - - if (b[i + 1].type != 'z') { - ep = path.add (b[i].x2, b[i].y2); - - ep.get_left_handle ().set_point_type (PointType.CUBIC); - ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1); - } - } - } - - foreach (EditPoint e in smooth_points) { - e.set_point_type (PointType.LINE_DOUBLE_CURVE); - e.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE); - e.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE); - } - - foreach (EditPoint e in smooth_points) { - path.recalculate_linear_handles_for_point (e); - } - - for (int i = 0; i < 3; i++) { - foreach (EditPoint e in smooth_points) { - e.set_tie_handle (true); - e.process_tied_handle (); - } - } - - if (path.points.size > 0) { - path_list.add (path); - } - - foreach (Path p in path_list.paths) { - p.remove_points_on_points (); - } - - return path_list; - } - - PathList create_paths_illustrator (BezierPoints[] b, int num_b) { - Path path; - PathList path_list = new PathList (); - EditPoint ep; - bool first_point = true; - double first_left_x, first_left_y; - Gee.ArrayList<EditPoint> smooth_points = new Gee.ArrayList<EditPoint> (); - - if (num_b > b.length) { - warning ("num_b > b.length: $num_b > $(b.length)"); - return path_list; - } - - path = new Path (); - - if (num_b <= 1) { - warning ("No SVG data"); - return path_list; - } - - first_left_x = 0; - first_left_y = 0; - - // FIXME: array boundaries - for (int i = 0; i < num_b; i++) { - if (b[i].type == '\0') { - warning ("Parser error."); - return path_list; - } else if (b[i].type == 'z') { - path.close (); - path.create_list (); - - int first_index = 1; - - for (int j = i - 1; j >= 1; j--) { - if (b[j].type == 'z') { - first_index = j + 1; // from z to M - } - } - - if (b[first_index].type == 'C' || b[first_index].type == 'S') { - return_val_if_fail (path.points.size != 0, path_list); - ep = path.points.get (path.points.size - 1); - - if (b[i - 1].type != 'L' ) { - ep.get_right_handle ().set_point_type (PointType.CUBIC); - ep.get_right_handle ().move_to_coordinate (b[first_index].x0, b[first_index].y0); - } - } else if (b[first_index].type == 'L') { - return_val_if_fail (path.points.size != 0, path_list); - ep = path.points.get (path.points.size - 1); - ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); - path.recalculate_linear_handles_for_point (ep); - } else { - warning ("Unexpected type: %s", (!) b[first_index].type.to_string ()); - } - - path.recalculate_linear_handles (); - path_list.add (path); - - path = new Path (); - first_point = true; - } else if (b[i].type == 'M') { - } else if (b[i].type == 'L') { - - if (first_point) { - first_left_x = b[i].x0; - first_left_y = b[i].y0; - } - - ep = path.add (b[i].x0, b[i].y0); - ep.set_point_type (PointType.LINE_CUBIC); // TODO: quadratic - ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC); - - if (b[i -1].type == 'L' || first_point) { - // ep.get_left_handle ().set_point_type (PointType.LINE_CUBIC); - } - - if (b[i + 1].type == 'C' || b[i + 1].type == 'S') { - return_val_if_fail (i + 1 < num_b, path_list); - ep.get_right_handle ().set_point_type (PointType.CUBIC); - ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0); - } - - first_point = false; - } else if (b[i].type == 'Q') { - warning ("Illustrator does not support quadratic control points."); - warning (@"$(b[i])\n"); - } else if (b[i].type == 'C' || b[i].type == 'S') { - - if (first_point) { - first_left_x = b[i].x0; - first_left_y = b[i].y0; - } - - ep = path.add (b[i].x2, b[i].y2); - ep.set_point_type (PointType.CUBIC); - - ep.get_right_handle ().set_point_type (PointType.CUBIC); - ep.get_left_handle ().set_point_type (PointType.CUBIC); - - ep.get_left_handle ().move_to_coordinate (b[i].x1, b[i].y1); - - if (b[i].type == 'S') { - smooth_points.add (ep); - } - - if (b[i + 1].type != 'z') { - ep.get_right_handle ().move_to_coordinate (b[i + 1].x0, b[i + 1].y0); - } else { - ep.get_right_handle ().move_to_coordinate (first_left_x, first_left_y); - } - - first_point = false; - } else { - warning ("Unknown control point type."); - warning (@"$(b[i])\n"); - } - } - - foreach (EditPoint e in smooth_points) { - e.set_point_type (PointType.LINE_DOUBLE_CURVE); - e.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE); - e.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE); - } - - foreach (EditPoint e in smooth_points) { - path.recalculate_linear_handles_for_point (e); - } - - for (int i = 0; i < 3; i++) { - foreach (EditPoint e in smooth_points) { - e.set_tie_handle (true); - e.process_tied_handle (); - } - } - - if (path.points.size > 0) { - path_list.add (path); - } - - foreach (Path p in path_list.paths) { - p.remove_points_on_points (); - } - - return path_list; - } - - // TODO: implement a default svg parser - - static int parse_int (string? s) { - if (is_null (s)) { - warning ("null instead of string"); - return 0; - } - - if (!is_point ((!) s)) { - warning (@"Expecting an integer got: $((!) s)"); - return 0; - } - - return int.parse ((!) s); - } - - public static double parse_double (string? s) { - if (unlikely (is_null (s))) { - warning ("Got null instead of expected string."); - return 0; - } - - if (unlikely (!is_point ((!) s))) { - warning (@"Expecting a double got: $((!) s)"); - return 0; - } - - return double.parse ((!) s); - } - - static bool is_point (string? s) { - if (s == null) { - warning ("s is null"); - return false; - } - - return double.try_parse ((!) s); - } - - Path parse_poly_data (string polygon_points) { - string data = add_separators (polygon_points); - string[] c = data.split (" "); - Path path; - BezierPoints[] bezier_points = new BezierPoints[c.length + 1]; - int bi; - Glyph g; - EditPoint ep; - - bi = 0; - for (int i = 0; i < c.length - 1; i += 2) { - if (i + 1 >= c.length) { - warning ("No y value."); - break; - } - - if (bi >= bezier_points.length) { - warning ("End of bezier_points"); - break; - } - - bezier_points[bi] = new BezierPoints (); - bezier_points[bi].type = 'L'; - bezier_points[bi].x0 = parse_double (c[i]); - bezier_points[bi].y0 = -parse_double (c[i + 1]); - bi++; - } - - g = MainWindow.get_current_glyph (); - move_and_resize (bezier_points, bi, false, 1, g); - - path = new Path (); - for (int i = 0; i < bi; i++) { - ep = path.add (bezier_points[i].x0, bezier_points[i].y0); - ep.set_point_type (PointType.LINE_CUBIC); - } - - path.create_list (); - path.recalculate_linear_handles (); - - return path; - } - } - - }
diff --git libbirdfont/Svg/SvgPath.vala(deleted)
--- a/libbirdfont/Svg/SvgPath.vala +++ /dev/null @@ -1,89 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Cairo; - - namespace BirdFont { - - public class SvgPath : Object { - public Gee.ArrayList<Points> points = new Gee.ArrayList<Points> (); - - public SvgPath () { - } - - public SvgPath.create_copy (SvgPath p) { - Object.copy_attributes (p, this); - } - - public override bool is_over (double x, double y) { - return false; - } - - public override void draw (Context cr) { - cr.save (); - cr.new_path (); - - foreach (Points p in points) { - cr.move_to (p.x, p.y); - draw_points (cr, p); - - if (p.closed) { - cr.close_path (); - } - } - - apply_transform (cr); - paint (cr); - cr.restore (); - } - - public void draw_points (Context cr, Points points) { - Doubles p = points.point_data; - - return_if_fail (p.size % 6 == 0); - - for (int i = 0; i < p.size; i += 6) { - cr.curve_to (p.data[i], p.data[i + 1], - p.data[i + 2], p.data[i + 3], - p.data[i + 4], p.data[i + 5]); - } - } - - public override void move (double dx, double dy) { - } - - public override void update_region_boundaries () { - } - - public override void rotate (double theta, double xc, double yc) { - } - - public override bool is_empty () { - return false; - } - - public override void resize (double ratio_x, double ratio_y) { - } - - public override Object copy () { - return new SvgPath.create_copy (this); - } - - public override string to_string () { - return "SvgPath"; - } - } - - }
diff --git libbirdfont/Svg/SvgStyle.vala(deleted)
--- a/libbirdfont/Svg/SvgStyle.vala +++ /dev/null @@ -1,162 +1,1 @@ - /* - Copyright (C) 2015 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using B; - using Math; - - namespace BirdFont { - - public class SvgStyle { - - public Gee.HashMap<string, string> style; - - public Color? stroke = null; - public Color? fill = null; - public Gradient? stroke_gradient = null; - public Gradient? fill_gradient = null; - - public double stroke_width = 0; - - public SvgStyle () { - style = new Gee.HashMap<string, string> (); - } - - public LineCap get_line_cap () { - string l; - - if (!style.has_key ("stroke-linecap")) { - return LineCap.BUTT; - } - - l = style.get ("stroke-linecap"); - - if (l == "round") { - return LineCap.ROUND; - } else if (l == "square") { - return LineCap.SQUARE; - } - - return LineCap.BUTT; - } - - public bool has_stroke () { - bool s = true; - - if (style.has_key ("stroke")) { - s = style.get ("stroke") != "none"; - } - - return get_stroke_width () > 0 && s; - } - - public double get_stroke_width () { - if (!style.has_key ("stroke-width")) { - return 0; - } - - return double.parse (style.get ("stroke-width")); - } - - public static SvgStyle parse (Defs? d, Attributes attributes) { - SvgStyle s = new SvgStyle (); - double fill_opacity = 1; - double stroke_opacity = 1; - - foreach (Attribute a in attributes) { - if (a.get_name () == "style") { - s.parse_key_value_pairs (a.get_content ()); - } - - if (a.get_name () == "stroke-width") { - s.style.set ("stroke-width", a.get_content ()); - } - - if (a.get_name () == "stroke") { - s.style.set ("stroke", a.get_content ()); - } - - if (a.get_name () == "fill") { - s.style.set ("fill", a.get_content ()); - } - - if (a.get_name () == "fill-opacity") { - fill_opacity = SvgFile.parse_number (a.get_content ()); - } - - if (a.get_name () == "stroke-opacity") { - stroke_opacity = SvgFile.parse_number (a.get_content ()); - } - } - - s.stroke_width = SvgFile.parse_number (s.style.get ("stroke-width")); - s.stroke = Color.parse (s.style.get ("stroke")); - s.fill = Color.parse (s.style.get ("fill")); - - if (d != null) { - Defs defs = (!) d; - - s.stroke_gradient = defs.get_gradient_for_url (s.style.get ("stroke")); - s.fill_gradient = defs.get_gradient_for_url (s.style.get ("fill")); - } - - if (s.fill != null) { - Color color = (!) s.fill; - color.a = fill_opacity; - } - - if (s.stroke != null) { - Color color = (!) s.stroke; - color.a = stroke_opacity; - } - - return s; - } - - void parse_key_value_pairs (string svg_style) { - string[] p = svg_style.split (";"); - string[] pair; - string k, v; - - foreach (string kv in p) { - pair = kv.split (":"); - - if (pair.length != 2) { - warning ("pair.length != 2"); - continue; - } - - k = pair[0]; - v = pair[1]; - - style.set (k, v); - } - } - - public void apply (PathList path_list) { - foreach (Path p in path_list.paths) { - if (has_stroke ()) { - p.stroke = get_stroke_width (); - } else { - p.stroke = 0; - } - - p.line_cap = get_line_cap (); - p.reset_stroke (); - p.update_region_boundaries (); - } - } - } - - }
diff --git libbirdfont/Svg/SvgTransform.vala(deleted)
--- a/libbirdfont/Svg/SvgTransform.vala +++ /dev/null @@ -1,76 +1,1 @@ - /* - 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; - } - } - - }
--- a/libbirdfont/Svg/SvgTransforms.vala +++ /dev/null @@ -1,48 +1,1 @@ - /* - Copyright (C) 2016 Johan Mattsson - - This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - */ - - using Cairo; - - namespace BirdFont { - - public class SvgTransforms : GLib.Object { - public Gee.ArrayList<SvgTransform> transforms; - - public SvgTransforms () { - transforms = new Gee.ArrayList<SvgTransform> (); - } - - public void add (SvgTransform transform) { - transforms.add (transform); - } - - public Matrix get_matrix () { - Matrix matrix = Matrix.identity (); - - if (transforms.size == 0) { - return Matrix.identity (); - } - - matrix = transforms.get (0).get_matrix (); - - for (int i = 1; i < transforms.size; i++) { - matrix.multiply (matrix, transforms.get (i).get_matrix ()); - } - - return matrix; - } - } - - }
diff --git libbirdfont/Svg.vala(new)
--- /dev/null +++ b/libbirdfont/Svg.vala @@ -1,1 +1,356 @@ + /* + Copyright (C) 2012 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + + namespace BirdFont { + + // FIXME: substrings + + public class Svg { + + /** Export to svg glyph data. */ + public static string to_svg_glyph (Glyph g) { + StringBuilder svg = new StringBuilder (); + PathList stroke_list; + + foreach (Path p in g.get_visible_paths ()) { + if (p.stroke == 0) { + write_path_as_glyph (p, svg, g); + } else { + stroke_list = p.get_completed_stroke (); + write_paths_as_glyph (stroke_list, svg, g); + } + } + + return svg.str; + } + + /** Export to svg-font data. */ + public static string to_svg_path (Path pl, Glyph g) { + StringBuilder svg = new StringBuilder (); + pl.create_list (); + write_path (pl, svg, g, false); + return svg.str; + } + + private static void write_paths_as_glyph (PathList pl, StringBuilder svg, Glyph g) { + foreach (Path p in pl.paths) { + write_path_as_glyph (p, svg, g); + } + } + + private static void write_path_as_glyph (Path pl, StringBuilder svg, Glyph g) { + write_path (pl, svg, g, true); + } + + private static void write_path (Path p, StringBuilder svg, Glyph g, bool do_glyph) { + int i = 0; + EditPoint? n = null; + EditPoint m; + + if (p.points.size < 2) { + return; + } + + p.create_list (); + + foreach (var e in p.points) { + if (i == 0) { + add_abs_start (e, svg, g, do_glyph); + i++; + n = e; + continue; + } + + m = (!) n; + + add_abs_next (m, e, svg, g, do_glyph); + + n = e; + i++; + } + + if (!p.is_open ()) { + m = p.points.get (0); + add_abs_next ((!) n, m, svg, g, do_glyph); + close_path (svg); + } + } + + private static void add_abs_next (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool do_glyph) { + if (start.right_handle.type == PointType.LINE_QUADRATIC) { + add_abs_line_to (start, end, svg, g, do_glyph); + } else if (start.right_handle.type == PointType.LINE_CUBIC && end.left_handle.type == PointType.LINE_CUBIC) { + add_abs_line_to (start, end, svg, g, do_glyph); + } else if (end.left_handle.type == PointType.QUADRATIC || start.right_handle.type == PointType.QUADRATIC) { + add_quadratic_abs_path (start, end, svg, g, do_glyph); + } else if (end.left_handle.type == PointType.DOUBLE_CURVE || start.right_handle.type == PointType.DOUBLE_CURVE) { + add_double_quadratic_abs_path (start, end, svg, g, do_glyph); + } else { + add_cubic_abs_path (start, end, svg, g, do_glyph); + } + } + + private static void add_abs_start (EditPoint ep, StringBuilder svg, Glyph g, bool to_glyph) { + double left = g.left_limit; + double baseline = -BirdFont.get_current_font ().base_line; + double height = g.get_height (); + + svg.append_printf ("M"); + + if (!to_glyph) { + svg.append_printf ("%s ", round (ep.x - left)); + svg.append_printf ("%s ", round (-ep.y + height / 2)); + } else { + svg.append_printf ("%s ", round (ep.x - left)); + svg.append_printf ("%s ", round (ep.y + baseline)); + } + } + + private static void close_path (StringBuilder svg) { + svg.append ("z"); + } + + private static void add_abs_line_to (EditPoint start, EditPoint stop, StringBuilder svg, Glyph g, bool to_glyph) { + double baseline = -BirdFont.get_current_font ().base_line; + double left = g.left_limit; + double height = g.get_height (); + + double xa, ya, xb, yb; + + Path.get_line_points (start, stop, out xa, out ya, out xb, out yb); + + double center_x = Glyph.xc (); + double center_y = Glyph.yc (); + + svg.append ("L"); + + if (!to_glyph) { + svg.append_printf ("%s ", round (xb - center_x - left)); + svg.append_printf ("%s ", round (yb - center_y + height / 2)); + } else { + svg.append_printf ("%s ", round (xb - center_x - left)); + svg.append_printf ("%s ", round (-yb + center_y + baseline)); + } + } + + private static void add_double_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool to_glyph) { + EditPoint middle; + double x, y; + + x = start.get_right_handle ().x + (end.get_left_handle ().x - start.get_right_handle ().x) / 2; + y = start.get_right_handle ().y + (end.get_left_handle ().y - start.get_right_handle ().y) / 2; + + middle = new EditPoint (x, y, PointType.QUADRATIC); + middle.right_handle = end.get_left_handle ().copy (); + + add_quadratic_abs_path (start, middle, svg, g, to_glyph); + add_quadratic_abs_path (middle, end, svg, g, to_glyph); + } + + private static void add_quadratic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool to_glyph) { + double left = g.left_limit; + double baseline = -BirdFont.get_current_font ().base_line; + double height = g.get_height (); + + double xa, ya, xb, yb, xc, yc, xd, yd; + + Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd); + + double center_x = Glyph.xc (); + double center_y = Glyph.yc (); + + // cubic path + if (!to_glyph) { + svg.append_printf ("Q"); + + svg.append_printf ("%s ", round (xb - center_x - left)); + svg.append_printf ("%s ", round (yb - center_y + height / 2)); + + svg.append_printf ("%s ", round (xd - center_x - left)); + svg.append_printf ("%s ", round (yd - center_y + height / 2)); + + } else { + svg.append_printf ("Q"); + + svg.append_printf ("%s ", round (xb - center_x - left)); + svg.append_printf ("%s ", round (-yb + center_y + baseline)); + + svg.append_printf ("%s ", round (xd - center_x - left)); + svg.append_printf ("%s ", round (-yd + center_y + baseline)); + } + } + + private static void add_cubic_abs_path (EditPoint start, EditPoint end, StringBuilder svg, Glyph g, bool to_glyph) { + double left = g.left_limit; + double baseline = -BirdFont.get_current_font ().base_line; + double height = g.get_height (); + + double xa, ya, xb, yb, xc, yc, xd, yd; + + Path.get_bezier_points (start, end, out xa, out ya, out xb, out yb, out xc, out yc, out xd, out yd); + + double center_x = Glyph.xc (); + double center_y = Glyph.yc (); + + // cubic path + if (!to_glyph) { + svg.append_printf ("C"); + + svg.append_printf ("%s ", round (xb - center_x - left)); + svg.append_printf ("%s ", round (yb - center_y + height / 2)); + + svg.append_printf ("%s ", round (xc - center_x - left)); + svg.append_printf ("%s ", round (yc - center_y + height / 2)); + + svg.append_printf ("%s ", round (xd - center_x - left)); + svg.append_printf ("%s ", round (yd - center_y + height / 2)); + + } else { + svg.append_printf ("C"); + + svg.append_printf ("%s ", round (xb - center_x - left)); + svg.append_printf ("%s ", round (-yb + center_y + baseline)); + + svg.append_printf ("%s ", round (xc - center_x - left)); + svg.append_printf ("%s ", round (-yc + center_y + baseline)); + + svg.append_printf ("%s ", round (xd - center_x - left)); + svg.append_printf ("%s ", round (-yd + center_y + baseline)); + } + } + + /** Draw path from svg font data. */ + public static void draw_svg_path (Context cr, string svg, double x, double y) { + double x1, x2, x3; + double y1, y2, y3; + double px, py; + string[] d = svg.split (" "); + + if (d.length == 0) { + return; + } + + px = 0; + py = 0; + + cr.save (); + + cr.set_line_width (0); + + if (svg == "") { + return; + } + + for (int i = 0; i < d.length; i++) { + + // trim off leading white space + while (d[i].index_of (" ") == 0) { + d[i] = d[i].substring (1); // FIXME: maybe no ascii + } + + if (d[i].index_of ("L") == 0) { + x1 = double.parse (d[i].substring (1)) + x; + y1 = -double.parse (d[i+1]) + y; + cr.line_to (x1, y1); + + px = x1; + py = y1; + continue; + } + + if (d[i].index_of ("Q") == 0) { + x1 = double.parse (d[i].substring (1)) + x; + y1 = -double.parse (d[i+1]) + y; + + x2 = double.parse (d[i+2]) + x; + y2 = -double.parse (d[i+3]) + y; + + cr.curve_to ((px + 2 * x1) / 3, (py + 2 * y1) / 3, (x2 + 2 * x1) / 3, (y2 + 2 * y1) / 3, x2, y2); + + px = x2; + py = y2; + continue; + } + + if (d[i].index_of ("C") == 0) { + x1 = double.parse (d[i].substring (1)) + x; + y1 = -double.parse (d[i+1]) + y; + + x2 = double.parse (d[i+2]) + x; + y2 = -double.parse (d[i+3]) + y; + + x3 = double.parse (d[i+4]) + x; + y3 = -double.parse (d[i+5]) + y; + + cr.curve_to (x1, y1, x2, y2, x3, y3); + + px = x3; + py = y3; + continue; + } + + if (d[i].index_of ("M") == 0) { + x1 = double.parse (d[i].substring (1)) + x; + y1 = -double.parse (d[i+1]) + y; + + cr.move_to (x1, y1); + + px = x1; + py = y1; + continue; + } + + if (d[i].index_of ("zM") == 0) { + cr.close_path (); + + x1 = double.parse (d[i].substring (2)) + x; + y1 = -double.parse (d[i+1]) + y; + + cr.move_to (x1, y1); + + px = x1; + py = y1; + continue; + } + + if (d[i].index_of ("z") == 0) { + cr.close_path (); + continue; + } + + } + + cr.fill (); + cr.restore (); + } + + } + + internal static string round (double p) { + string v = p.to_string (); + char[] c = new char [501]; + + v = p.format (c, "%3.15f"); + + if (v.index_of ("e") != -1) { + return "0.0"; + } + + return v; + } + + }
diff --git libbirdfont/SvgFont.vala(new)
--- /dev/null +++ b/libbirdfont/SvgFont.vala @@ -1,1 +1,265 @@ + /* + Copyright (C) 2013 2014 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using B; + + namespace BirdFont { + + class SvgFont : GLib.Object { + Font font; + double units = 1; + double font_advance = 0; + + public SvgFont (Font f) { + this.font = f; + } + + /** Load svg font from file. */ + public void load (string path) { + string data; + XmlParser xml_parser; + try { + FileUtils.get_contents (path, out data); + xml_parser = new XmlParser (data); + parse_svg_font (xml_parser.get_root_tag ()); + } catch (GLib.Error e) { + warning (e.message); + } + } + + void parse_svg_font (Tag tag) { + foreach (Tag t in tag) { + if (t.get_name () == "defs") { + parse_svg_font (t); + } + + if (t.get_name () == "font") { + parse_font_tag (t); + parse_svg_font (t); + } + + if (t.get_name () == "font-face") { + parse_font_limits (t); + } + + if (t.get_name () == "hkern") { + parse_hkern (t); + } + + if (t.get_name () == "glyph") { + parse_glyph (t); + } + } + } + + void parse_hkern (Tag tag) { + string left = ""; + string right = ""; + string left_name = ""; + string right_name = ""; + double kerning = 0; + unichar l, r; + StringBuilder sl, sr; + GlyphRange grr, grl; + KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes (); + + foreach (Attribute attr in tag.get_attributes ()) { + // left + if (attr.get_name () == "u1") { + left = attr.get_content (); + } + + // right + if (attr.get_name () == "u2") { + right = attr.get_content (); + } + + if (attr.get_name () == "g1") { + left_name = attr.get_content (); + } + + if (attr.get_name () == "g2") { + right_name = attr.get_content (); + } + + // kerning + if (attr.get_name () == "k") { + kerning = double.parse (attr.get_content ()) * units; + } + } + + // FIXME: ranges and sequences for u1 & u2 + g1 & g2 + foreach (string lk in left.split (",")) { + foreach (string rk in right.split (",")) { + l = get_unichar (lk); + r = get_unichar (rk); + + sl = new StringBuilder (); + sl.append_unichar (l); + + sr = new StringBuilder (); + sr.append_unichar (r); + + try { + grl = new GlyphRange (); + grl.parse_ranges (sl.str); + + grr = new GlyphRange (); + grr.parse_ranges (sr.str); + + classes.set_kerning (grl, grr, -kerning); + } catch (MarkupError e) { + warning (e.message); + } + } + } + } + + void parse_font_limits (Tag tag) { + double top_limit = 0; + double bottom_limit = 0; + + foreach (Attribute attr in tag.get_attributes ()) { + if (attr.get_name () == "units-per-em") { + units = 100.0 / double.parse (attr.get_content ()); + } + } + + foreach (Attribute attr in tag.get_attributes ()) { + if (attr.get_name () == "ascent") { + top_limit = double.parse (attr.get_content ()); + } + + if (attr.get_name () == "descent") { + bottom_limit = double.parse (attr.get_content ()); + } + } + + top_limit *= units; + bottom_limit *= units; + + font.bottom_limit = bottom_limit; + font.top_limit = top_limit; + } + + void parse_font_tag (Tag tag) { + foreach (Attribute attr in tag.get_attributes ()) { + if (attr.get_name () == "horiz-adv-x") { + font_advance = double.parse (attr.get_content ()); + } + + if (attr.get_name () == "id") { + font.set_name (attr.get_content ()); + } + } + } + + /** Obtain unichar value from either a character, a hex representation of + * a character or series of characters (f, &#x6d; or ffi). + */ + static unichar get_unichar (string val) { + string v = val; + unichar unicode_value; + + if (val == "&") { + return '&'; + } + + // TODO: parse ligatures + if (v.has_prefix ("&")) { + // parse hex value + v = v.substring (0, v.index_of (";")); + v = v.replace ("&#x", "U+"); + v = v.replace (";", ""); + unicode_value = Font.to_unichar (v); + } else { + // obtain unicode value + + if (v.char_count () > 1) { + warning ("font contains ligatures"); + return '\0'; + } + + unicode_value = v.get_char (0); + } + + return unicode_value; + } + + bool is_ligature (string v) { + if (v.has_prefix ("&")) { + return false; + } + + return v.char_count () > 1; + } + + void parse_glyph (Tag tag) { + unichar unicode_value = 0; + string glyph_name = ""; + string svg = ""; + Glyph glyph; + GlyphCollection glyph_collection; + double advance = font_advance; + string ligature = ""; + SvgParser parser = new SvgParser (); + StringBuilder unicode_name; + + parser.set_format (SvgFormat.INKSCAPE); + + foreach (Attribute attr in tag.get_attributes ()) { + if (attr.get_name () == "unicode") { + unicode_value = get_unichar (attr.get_content ()); + + if (glyph_name == "") { + glyph_name = attr.get_content (); + } + + if (is_ligature (attr.get_content ())) { + ligature = attr.get_content (); + } + } + + // svg data + if (attr.get_name () == "d") { + svg = attr.get_content (); + } + + if (attr.get_name () == "glyph-name") { + glyph_name = attr.get_content (); + } + + if (attr.get_name () == "horiz-adv-x") { + advance = double.parse (attr.get_content ()); + } + } + + unicode_name = new StringBuilder (); + unicode_name.append_unichar (unicode_value); + + glyph = new Glyph (unicode_name.str, unicode_value); + parser.add_path_to_glyph (svg, glyph, true, units); + glyph.right_limit = glyph.left_limit + advance * units; + + // FIXME: add svg font ligatures + + glyph_collection = new GlyphCollection (unicode_value, glyph_name); + glyph_collection.insert_glyph (glyph, true); + + font.add_glyph_collection (glyph_collection); + } + } + + }
--- /dev/null +++ b/libbirdfont/SvgFontFormatWriter.vala @@ -1,1 +1,133 @@ + /* + Copyright (C) 2012, 2014 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + namespace BirdFont { + + class SvgFontFormatWriter : GLib.Object { + + DataOutputStream os; + + public SvgFontFormatWriter () { + } + + public void open (File file) throws Error { + if (file.query_exists ()) { + throw new FileError.EXIST ("SvgFontFormatWriter: file exists."); + } + + os = new DataOutputStream (file.create(FileCreateFlags.REPLACE_DESTINATION)); + } + + public void close () throws Error { + os.close (); + } + + public void write_font_file (Font font) throws Error { + string font_name = font.get_full_name (); + + int units_per_em = 100; + + int ascent = 80; + int descent = -20; + + StringBuilder b; + + Glyph? g; + Glyph glyph; + unichar index = 0; + + string uni; + + KerningClasses classes; + + put ("""<?xml version="1.0" standalone="no"?>"""); + put ("""<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >"""); + put ("""<svg>"""); + + // (metadata goes here) + + put ("<defs>"); + + put (@"<font id=\"$font_name\" horiz-adv-x=\"250\" >"); + put (@"<font-face units-per-em=\"$(to_float (units_per_em))\" ascent=\"$(to_float (ascent))\" descent=\"$(to_float (descent))\" />"); + + // (missing-glyph goes here) + + // regular glyphs + while (true) { + g = font.get_glyph_index (index++); + + if (g == null) { + break; + } + + glyph = (!) g; + + b = new StringBuilder (); + b.append_unichar (glyph.get_unichar ()); + + if (glyph.get_unichar () >= ' ' && b.str.validate ()) { + if (b.str == "\"" || b.str == "&" || b.str == "<" || b.str == ">") { + uni = Font.to_hex_code (glyph.get_unichar ()); + put (@"<glyph unicode=\"&#x$(uni);\" horiz-adv-x=\"$(to_float (glyph.get_width ()))\" d=\"$(glyph.get_svg_data ())\" />"); + } else { + put (@"<glyph unicode=\"$(b.str)\" horiz-adv-x=\"$(to_float (glyph.get_width ()))\" d=\"$(glyph.get_svg_data ())\" />"); + } + } + } + + // FIXME: ligatures + classes = BirdFont.get_current_font ().get_kerning_classes (); + classes.all_pairs ((kerning) => { + string l, r; + + foreach (Kerning k in kerning.kerning) { + try { + if (k.glyph != null) { + l = Font.to_hex_code (kerning.character.unichar_code); + r = Font.to_hex_code (((!)k.glyph).unichar_code); + os.put_string (@"<hkern u1=\"&#x$l;\" u2=\"&#x$r;\" k=\"$(to_float (-k.val))\"/>\n"); + } else { + warning ("No glyph."); + } + } catch (GLib.Error e) { + warning (e.message); + } + } + }); + + put ("</font>"); + put ("</defs>"); + put ("</svg>"); + } + + string to_float (double d) { + string s = @"$d"; + if (s.index_of ("e") != -1) { + return "0".dup (); + } + return s.replace (",", "."); + } + + /** Write a new line */ + private void put (string line) throws Error { + os.put_string (line); + os.put_string ("\n"); + } + + } + + + }
diff --git libbirdfont/SvgParser.vala(new)
--- /dev/null +++ b/libbirdfont/SvgParser.vala @@ -1,1 +1,1560 @@ + /* + 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); + } + + } + + }
--- /dev/null +++ b/libsvgbird/BezierPoints.vala @@ -1,1 +1,34 @@ + /* + Copyright (C) 2014 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + namespace SvgBird { + + /** Bezier point container for the SVG parser. */ + public class BezierPoints { + public unichar type = '\0'; + public unichar svg_type = '\0'; + public double x0 = 0; + public double y0 = 0; + public double x1 = 0; + public double y1 = 0; + public double x2 = 0; + public double y2 = 0; + + public string to_string () { + return @"$((!)type.to_string ()) $x0,$y0 $x1,$y1 $x2,$y2 SVG:$((!)svg_type.to_string ())"; + } + } + + }
diff --git libsvgbird/Color.vala(new)
--- /dev/null +++ b/libsvgbird/Color.vala @@ -1,1 +1,114 @@ + /* + Copyright (C) 2016 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + namespace SvgBird { + + public class Color { + public double r; + public double g; + public double b; + public double a; + + public Color (double r, double g, double b, double a) { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + public static Color? parse (string? svg_color) { + if (svg_color == null) { + return null; + } + + string color = ((!) svg_color).replace ("#", ""); + uint32 c; + string[] arguments; + Color parsed = new Color (0, 0, 0, 1); + + if (color == "none") { + return null; + } + + if (color.char_count () == 6) { + color.scanf ("%x", out c); + parsed.r = (uint8)((c & 0xFF0000) >> 16) / 254.0; + parsed.g = (uint8)((c & 0x00FF00) >> 8)/ 254.0; + parsed.b = (uint8)(c & 0x0000FF) / 254.0; + } else if (color.char_count () == 3) { + color.scanf ("%x", out c); + parsed.r = (uint8)(((c & 0xF00) >> 4) | ((c & 0xF00) >> 8)) / 254.0; + parsed.g = (uint8)((c & 0x0F0) | ((c & 0x0F0) >> 4)) / 254.0; + parsed.b = (uint8)(((c & 0x00F) << 4) | (c & 0x00F)) / 254.0; + } else if (color.index_of ("%") > -1) { + color = color.replace ("rgb", ""); + color = color.replace (" ", ""); + color = color.replace ("\t", ""); + color = color.replace ("%", ""); + arguments = color.split (","); + + return_val_if_fail (arguments.length == 3, parsed); + arguments[0].scanf ("%lf", out parsed.r); + arguments[1].scanf ("%lf", out parsed.g); + arguments[2].scanf ("%lf", out parsed.b); + } else if (color.index_of ("rgb") > -1) { + color = color.replace ("rgb", ""); + color = color.replace (" ", ""); + color = color.replace ("\t", ""); + arguments = color.split (","); + + return_val_if_fail (arguments.length == 3, parsed); + + int r, g, b; + arguments[0].scanf ("%d", out r); + parsed.r = r / 254.0; + + arguments[1].scanf ("%d", out g); + parsed.g = g / 254.0; + + arguments[2].scanf ("%d", out b); + parsed.b = b / 254.0; + } else { + warning ("Unknown color type: " + color); + } + + + return parsed; + } + + public string to_rgb_hex () { + StringBuilder rgb = new StringBuilder (); + rgb.append ("#"); + rgb.append_printf ("%x", (int) Math.rint (r * 254)); + rgb.append_printf ("%x", (int) Math.rint (g * 254)); + rgb.append_printf ("%x", (int) Math.rint (b * 254)); + return rgb.str; + } + + public string to_string () { + StringBuilder rgba = new StringBuilder (); + rgba.append (to_rgb_hex ()); + rgba.append_printf ("%x", (int) Math.rint (a * 254)); + return rgba.str; + } + + + public Color copy () { + return new Color (r, g, b, a); + } + + } + + }
diff --git libsvgbird/Defs.vala(new)
--- /dev/null +++ b/libsvgbird/Defs.vala @@ -1,1 +1,97 @@ + /* + Copyright (C) 2016 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using B; + using Math; + + namespace SvgBird { + + public class Defs { + public Gee.ArrayList<Gradient> gradients = new Gee.ArrayList<Gradient> (); + + public void add (Gradient g) { + gradients.add (g); + } + + public Gradient? get_gradient_for_url (string? url) { + if (url == null) { + return null; + } + + string tag_id = (!) url; + + if (unlikely (!is_url (tag_id))) { + warning ("Not an URL: " + tag_id); + return null; + } + + int p1 = tag_id.index_of ("("); + if (unlikely (p1 == -1)) { + warning ("Not an URL: " + tag_id); + return null; + } + + int p2 = tag_id.index_of (")"); + if (unlikely (p2 == -1 || p2 < p1)) { + warning ("Not an URL: " + tag_id); + return null; + } + + p1 += "(".length; + int length = p2 - p1; + tag_id = tag_id.substring (p1, length); + + return get_gradient_for_id (tag_id); + } + + public Gradient? get_gradient_for_id (string id) { + string tag_id; + + if (id.has_prefix ("#")) { + tag_id = id.substring ("#".length); + } else { + tag_id = id; + } + + foreach (Gradient gradient in gradients) { + if (gradient.id == tag_id) { + return gradient; + } + } + + return null; + } + + public static bool is_url (string? attribute) { + if (attribute == null) { + return false; + } + + return ((!) attribute).has_prefix ("url"); + } + + public Defs copy () { + Defs d = new Defs (); + + foreach (Gradient g in gradients) { + d.add (g); + } + + return d; + } + + } + + }
diff --git libsvgbird/Doubles.vala(new)
--- /dev/null +++ b/libsvgbird/Doubles.vala @@ -1,1 +1,69 @@ + /* + Copyright (C) 2015 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + + namespace SvgBird { + + public class Doubles : GLib.Object { + public double* data; + public int size = 0; + int capacity = 10; + + public Doubles () { + data = new double[capacity]; + } + + ~Doubles () { + delete data; + data = null; + } + + public Doubles.for_capacity (int capacity) { + data = new double[capacity]; + this.capacity = capacity; + } + + public void add(double d) { + if (size >= capacity) { + int new_capacity = 2 * capacity; + double* new_data = new double[new_capacity]; + Posix.memcpy (new_data, data, sizeof (double) * size); + delete data; + data = new_data; + capacity = new_capacity; + } + + data[size] = d; + size++; + } + + public double get_double (int index) { + if (unlikely (index < 0)) { + warning ("index < 0"); + return 0; + } + + if (unlikely (index >= size)) { + warning ("index >= size"); + return 0; + } + + return data[index]; + } + } + + } +
diff --git libsvgbird/Gradient.vala(new)
--- /dev/null +++ b/libsvgbird/Gradient.vala @@ -1,1 +1,80 @@ + /* + Copyright (C) 2015 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + using Math; + + namespace SvgBird { + + public class Gradient : GLib.Object { + public double x1; + public double y1; + public double x2; + public double y2; + + public Gee.ArrayList<Stop> stops; + + public string id = ""; + public string? href = null; + public SvgTransforms transforms; + + public Gradient () { + x1 = 0; + y1 = 0; + x2 = 0; + y2 = 0; + stops = new Gee.ArrayList<Stop> (); + transforms = new SvgTransforms (); + } + + public Gradient copy () { + Gradient g = new Gradient (); + g.x1 = x1; + g.y1 = y1; + g.x2 = x2; + g.y2 = y2; + + foreach (Stop s in stops) { + g.stops.add (s.copy ()); + } + + return g; + } + + public void copy_stops (Gradient g) { + foreach (Stop stop in g.stops) { + stops.add (stop.copy ()); + } + } + + public string to_string () { + StringBuilder description = new StringBuilder (); + description.append ("Gradient: "); + description.append (@"x1=$x1, y1=$y1, x2=$x2, y2=$y2"); + + foreach (Stop stop in stops) { + description.append (" "); + description.append (stop.to_string ()); + } + + return description.str; + } + + public Matrix get_matrix () { + return transforms.get_matrix (); + } + } + + }
diff --git libsvgbird/Layer.vala(new)
--- /dev/null +++ b/libsvgbird/Layer.vala @@ -1,1 +1,171 @@ + /* + Copyright (C) 2015 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + namespace SvgBird { + + public class Layer : GLib.Object { + public ObjectGroup objects; + + public Gee.ArrayList<Layer> subgroups; + public bool visible = true; + public string name = "Layer"; + + public SvgTransforms transforms; + + public Layer () { + objects = new ObjectGroup (); + subgroups = new Gee.ArrayList<Layer> (); + transforms = new SvgTransforms (); + } + + public int index_of (Layer sublayer) { + return subgroups.index_of (sublayer); + } + + public ObjectGroup get_all_objects () { + ObjectGroup o = new ObjectGroup (); + + o.append (objects); + + foreach (Layer sublayer in subgroups) { + o.append (sublayer.get_all_objects ()); + } + + return o; + } + + public ObjectGroup get_visible_objects () { + ObjectGroup object_group = new ObjectGroup (); + + if (visible) { + foreach (Object o in objects) { + object_group.add (o); + } + } + + foreach (Layer sublayer in subgroups) { + if (sublayer.visible) { + object_group.append (sublayer.get_visible_objects ()); + } + } + + return object_group; + } + + public void add_layer (Layer layer) { + subgroups.add (layer); + } + + public void add_object (Object object) { + objects.add (object); + } + + public void remove (Object o) { + objects.remove (o); + } + + public void remove_layer (Layer layer) { + subgroups.remove (layer); + + foreach (Layer sublayer in subgroups) { + sublayer.remove_layer (layer); + } + } + + public static void copy_layer (Layer from, Layer to) { + to.name = from.name; + to.objects = from.objects.copy (); + to.visible = from.visible; + + foreach (Layer l in from.subgroups) { + to.subgroups.add (l.copy ()); + } + } + + public Layer copy () { + Layer layer = new Layer (); + copy_layer (this, layer); + return layer; + } + + public void get_boundaries (out double x, out double y, out double w, out double h) { + double px, py, px2, py2; + + px = double.MAX; + py = double.MAX; + px2 = -double.MAX; + py2 = -double.MAX; + + foreach (Object p in get_all_objects ().objects) { + if (px > p.xmin) { + px = p.xmin; + } + + if (py > p.ymin) { + py = p.ymin; + } + + if (px2 < p.xmax) { + px2 = p.xmax; + } + + if (py2 < p.ymax) { + py2 = p.ymax; + } + } + + w = px2 - px; + h = py2 - py; + x = px; + y = py2; + } + + public void print (int indent = 0) { + stdout.printf (@"Layer: $(name)"); + + if (!visible) { + stdout.printf (" hidden"); + } + + stdout.printf (@"\n"); + + foreach (Object o in objects) { + for (int i = 0; i < indent; i++) { + stdout.printf ("\t"); + } + stdout.printf (@"Object $(o.to_string ())"); + + if (o.color != null) { + stdout.printf (" %s", ((!) o.color).to_rgb_hex ()); + } + + if (!o.visible) { + stdout.printf (" hidden"); + } + + stdout.printf ("\n"); + } + + foreach (Layer l in subgroups) { + for (int i = 0; i < indent; i++) { + stdout.printf ("\t"); + } + stdout.printf ("%s\n", l.name); + l.print (indent + 1); + } + } + } + + }
diff --git libsvgbird/LineCap.vala(new)
--- /dev/null +++ b/libsvgbird/LineCap.vala @@ -1,1 +1,27 @@ + /* + Copyright (C) 2015 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using B; + using Math; + + namespace SvgBird { + + public enum LineCap { + BUTT, + SQUARE, + ROUND + } + + }
diff --git libsvgbird/Object.vala(new)
--- /dev/null +++ b/libsvgbird/Object.vala @@ -1,1 +1,155 @@ + /* + Copyright (C) 2016 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + using Math; + + namespace SvgBird { + + public abstract class Object : GLib.Object { + bool open = false; + + public bool visible = true; + public SvgStyle style = new SvgStyle (); + public SvgTransforms transforms = new SvgTransforms (); + + public virtual Color? color { get; set; } // FIXME: keep this in svg style + public virtual Color? stroke_color { get; set; } + public virtual Gradient? gradient { get; set; } + + /** Path boundaries */ + public virtual double xmax { get; set; } + public virtual double xmin { get; set; } + public virtual double ymax { get; set; } + public virtual double ymin { get; set; } + + public virtual double rotation { get; set; } + public virtual double stroke { get; set; } + public virtual LineCap line_cap { get; set; default = LineCap.BUTT; } + public virtual bool fill { get; set; } + + public Object () { + } + + public Object.create_copy (Object o) { + open = o.open; + } + + public void set_open (bool open) { + this.open = open; + } + + public bool is_open () { + return open; + } + + public abstract void update_region_boundaries (); + public abstract bool is_over (double x, double y); + public abstract void draw (Context cr); + public abstract Object copy (); + public abstract void move (double dx, double dy); + public abstract void rotate (double theta, double xc, double yc); + public abstract bool is_empty (); + public abstract void resize (double ratio_x, double ratio_y); + + public static void copy_attributes (Object from, Object to) { + to.open = from.open; + + to.color = from.color; + to.stroke_color = from.stroke_color; + to.gradient = from.gradient; + + to.xmax = from.xmax; + to.xmin = from.xmin; + to.ymax = from.ymax; + to.ymin = from.ymin; + + to.rotation = from.rotation; + to.stroke = from.stroke; + to.line_cap = from.line_cap; + to.fill = from.fill; + } + + public virtual string to_string () { + return "Object"; + } + + public void paint (Context cr) { + Color fill, stroke; + bool need_fill = style.fill_gradient != null || style.fill != null; + bool need_stroke = style.stroke_gradient != null || style.stroke != null; + + cr.set_line_width (style.stroke_width); + + if (style.fill_gradient != null) { + apply_gradient (cr, (!) style.fill_gradient); + } else if (style.fill != null) { + fill = (!) style.fill; + cr.set_source_rgba (fill.r, fill.g, fill.b, fill.a); + } + + if (need_fill) { + if (need_stroke) { + cr.fill_preserve (); + } else { + cr.fill (); + } + } + + if (style.stroke_gradient != null) { + apply_gradient (cr, (!) style.stroke_gradient); + } else if (style.stroke != null) { + stroke = (!) style.stroke; + cr.set_source_rgba (stroke.r, stroke.g, stroke.b, stroke.a); + } + + if (need_stroke) { + cr.stroke (); + } + } + + public void apply_gradient (Context cr, Gradient? gradient) { + Cairo.Pattern pattern; + Gradient g; + + if (gradient != null) { + g = (!) gradient; + + pattern = new Cairo.Pattern.linear (g.x1, g.y1, g.x2, g.y2); + + Matrix gradient_matrix = g.get_matrix (); + gradient_matrix.invert (); + pattern.set_matrix (gradient_matrix); + + foreach (Stop s in g.stops) { + Color c = s.color; + pattern.add_color_stop_rgba (s.offset, c.r, c.g, c.b, c.a); + } + + cr.set_source (pattern); + } + } + + public void apply_transform (Context cr) { + Matrix view_matrix = cr.get_matrix (); + Matrix object_matrix = transforms.get_matrix (); + + object_matrix.multiply (object_matrix, view_matrix); + cr.set_matrix (object_matrix); + } + + } + + }
diff --git libsvgbird/ObjectGroup.vala(new)
--- /dev/null +++ b/libsvgbird/ObjectGroup.vala @@ -1,1 +1,64 @@ + /* + Copyright (C) 2015 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + namespace SvgBird { + + public class ObjectGroup : GLib.Object { + public Gee.ArrayList<Object> objects; + + public int size { + get { + return objects.size; + } + } + + public ObjectGroup () { + objects = new Gee.ArrayList<Object> (); + } + + public Gee.Iterator<Object> iterator () { + return objects.iterator (); + } + + public void remove (Object p) { + objects.remove (p); + } + + public void add (Object p) { + objects.add (p); + } + + public void clear () { + objects.clear (); + } + + public void append (ObjectGroup group) { + foreach (Object o in group.objects) { + objects.add (o); + } + } + + public ObjectGroup copy () { + ObjectGroup objects_copy = new ObjectGroup (); + + foreach (Object o in objects) { + objects_copy.add (o.copy ()); + } + + return objects_copy; + } + } + + }
diff --git libsvgbird/Points.vala(new)
--- /dev/null +++ b/libsvgbird/Points.vala @@ -1,1 +1,32 @@ + /* + Copyright (C) 2016 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + + namespace SvgBird { + + public class Points : GLib.Object { + public Doubles point_data = new Doubles.for_capacity (100); + public double x = 0; + public double y = 0; + public bool closed = false; + + public void add (double p) { + point_data.add (p); + } + } + + } +
diff --git libsvgbird/Rectangle.vala(new)
--- /dev/null +++ b/libsvgbird/Rectangle.vala @@ -1,1 +1,104 @@ + /* + Copyright (C) 2016 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + using Math; + + namespace SvgBird { + + public class Rectangle : Object { + + public double x = 0; + public double y = 0; + public double width = 0; + public double height = 0; + public double rx = 0; + public double ry = 0; + + public Rectangle () { + } + + public override bool is_over (double x, double y) { + return false; + } + + public override void draw (Context cr) { + cr.save (); + apply_transform (cr); + + if (rx == 0 && ry == 0) { + cr.rectangle (x, y, width, height); + } else { + draw_rounded_corners (cr); + } + + paint (cr); + cr.restore (); + } + + public void draw_rounded_corners (Context cr) { + cr.new_path (); + elliptical_arc (cr, x + width - rx, y + ry, -PI / 2, 0); + elliptical_arc (cr, x + width - rx, y + height - ry, 0, PI / 2); + elliptical_arc (cr, x + rx, y + height - ry, PI / 2, PI); + elliptical_arc (cr, x + rx, y + ry, PI, PI + PI / 2); + cr.close_path (); + } + + public void elliptical_arc (Context cr, double x, double y, double angle_start, double angle_stop) { + cr.save (); + cr.translate (x + rx, y + ry); + cr.scale (rx, ry); + cr.arc (0, 0, 1, angle_start, angle_stop); + cr.restore (); + } + + public override void move (double dx, double dy) { + x += dx; + y += dy; + } + + public override void update_region_boundaries () { + } + + public override void rotate (double theta, double xc, double yc) { + } + + public override bool is_empty () { + return false; + } + + public override void resize (double ratio_x, double ratio_y) { + } + + public override Object copy () { + Rectangle r = new Rectangle (); + + r.x = x; + r.y = y; + r.width = width; + r.height = height; + + Object.copy_attributes (this, r); + + return r; + } + + public override string to_string () { + return "Rectangle"; + } + } + + }
diff --git libsvgbird/Stop.vala(new)
--- /dev/null +++ b/libsvgbird/Stop.vala @@ -1,1 +1,40 @@ + /* + Copyright (C) 2015 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + using Math; + + namespace SvgBird { + + public class Stop : GLib.Object { + public Color color = new Color (0, 0, 0, 1); + public double offset = 0; + + public Stop () { + } + + public Stop copy () { + Stop s = new Stop (); + s.color = color.copy (); + s.offset = offset; + return s; + } + + public string to_string () { + return @"Stop: $(offset), " + color.to_string (); + } + } + + }
diff --git libsvgbird/SvgArc.vala(new)
--- /dev/null +++ b/libsvgbird/SvgArc.vala @@ -1,1 +1,182 @@ + /* + * BirdFont code from SVG Salamander + * + * Copyright (c) 2004, Mark McKay + * Copyright (c) 2014, Johan Mattsson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Mark McKay can be contacted at mark@kitfox.com. Salamander and other + * projects can be found at http://www.kitfox.com + * + * Created on January 26, 2004, 8:40 PM + * Adapded to BirdFont on Juli 2, 2014, 5:01 PM + */ + + using Math; + + namespace SvgBird { + + /** Convert an SVG arc instruction to a Beziér path. */ + static void add_arc_points (BezierPoints[] bezier_points, ref int bi, double x0, double y0, double rx, double ry, double angle, bool largeArcFlag, bool sweepFlag, double x, double y) { + + // + // Elliptical arc implementation based on the SVG specification notes + // + + double dx2, dy2, cosAngle, sinAngle; + double x1, y1, Prx, Pry, Px1, Py1, radiiCheck; + double sign, sq, coef, cx1, cy1; + double sx2, sy2, cx, cy; + double ux, uy, vx, vy, p, n; + double angleStart, angleExtent; + double s, step, theta; + + // Compute the half distance between the current and the final point + dx2 = (x0 - x) / 2.0; + dy2 = (y0 - y) / 2.0; + + // Convert angle from degrees to radians + angle = 2 * PI * ((angle % 360.0) / 360.0); + + cosAngle = cos (angle); + sinAngle = sin (angle); + + // + // Step 1 : Compute (x1, y1) + // + x1 = cosAngle * dx2 + sinAngle * dy2; + y1 = -sinAngle * dx2 + cosAngle * dy2; + + // Ensure radii are large enough + rx = fabs(rx); + ry = fabs(ry); + Prx = rx * rx; + Pry = ry * ry; + Px1 = x1 * x1; + Py1 = y1 * y1; + + + // Check that radii are large enough + radiiCheck = Px1 / Prx + Py1 / Pry; + + if (radiiCheck > 1) { + rx = sqrt (radiiCheck) * rx; + ry = sqrt (radiiCheck) * ry; + Prx = rx * rx; + Pry = ry * ry; + } + + // + // Step 2 : Compute (cx1, cy1) + // + sign = (largeArcFlag == sweepFlag) ? -1 : 1; + sq = ((Prx * Pry) - (Prx * Py1) - (Pry * Px1)) / ((Prx * Py1) + (Pry * Px1)); + sq = (sq < 0) ? 0 : sq; + coef = (sign * Math.sqrt(sq)); + cx1 = coef * ((rx * y1) / ry); + cy1 = coef * -((ry * x1) / rx); + + // + // Step 3 : Compute (cx, cy) from (cx1, cy1) + // + + sx2 = (x0 + x) / 2.0; + sy2 = (y0 + y) / 2.0; + cx = sx2 - (cosAngle * cx1 - sinAngle * cy1); + cy = sy2 - (sinAngle * cx1 + cosAngle * cy1); + + // + // Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle) + // + + ux = (x1 - cx1) / rx; + uy = (y1 - cy1) / ry; + vx = (-x1 - cx1) / rx; + vy = (-y1 - cy1) / ry; + + // Compute the angle start + n = sqrt((ux * ux) + (uy * uy)); + p = ux; // (1 * ux) + (0 * uy) + sign = (uy < 0) ? -1d : 1d; + angleStart = sign * acos(p / n); + + // Compute the angle extent + n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy)); + p = ux * vx + uy * vy; + sign = (ux * vy - uy * vx < 0) ? -1d : 1d; + angleExtent = sign * Math.acos(p / n); + + if(!sweepFlag && angleExtent > 0) { + angleExtent -= 2 *PI; + } else if (sweepFlag && angleExtent < 0) { + angleExtent += 2 *PI; + } + angleExtent %= 2 * PI; + angleStart %= 2 * PI; + + angleExtent *= -1; + angleStart *= -1; + + // Approximate the path with Beziér points + s = (angleExtent > 0) ? 1 : -1; + step = fabs (angleExtent) / (2 * fabs (angleExtent)); + + theta = PI - angleStart - angleExtent; + + bezier_points[bi].type = 'L'; + bezier_points[bi].svg_type = 'a'; + + bezier_points[bi].x0 = cx + rx * cos (theta); + bezier_points[bi].y0 = cy + ry * sin (theta); + + bi++; + + for (double a = 0; a < fabs (angleExtent); a += step) { + theta = PI - angleStart - angleExtent + s * a; + + return_if_fail (0 <= bi < bezier_points.length); + + bezier_points[bi].type = 'S'; + bezier_points[bi].svg_type = 'a'; + + bezier_points[bi].x0 = cx + rx * cos (theta); + bezier_points[bi].y0 = cy + ry * sin (theta); + + bezier_points[bi].x1 = cx + rx * cos (theta + 1 * step / 4); + bezier_points[bi].y1 = cy + ry * sin (theta + 1 * step / 4); + + bezier_points[bi].x2 = cx + rx * cos (theta + 2 * step / 4); + bezier_points[bi].y2 = cy + ry * sin (theta + 2 * step / 4); + + bi++; + } + } + + } +
diff --git libsvgbird/SvgDrawing.vala(new)
--- /dev/null +++ b/libsvgbird/SvgDrawing.vala @@ -1,1 +1,74 @@ + /* + Copyright (C) 2016 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + + using B; + using Cairo; + using Math; + + namespace SvgBird { + + public class SvgDrawing : Object { + public Layer root_layer = new Layer (); + public Defs defs = new Defs (); + + public double x = 0; + public double y = 0; + public double width = 0; + public double height = 0; + + public override void update_region_boundaries () { + } + + public override bool is_over (double x, double y) { + return (this.x <= x <= this.x + width) + && (this.y <= y <= this.y + height); + } + + public override void draw (Context cr) { + cr.save (); + cr.translate (x, y); + + foreach (Object o in root_layer.get_visible_objects ().objects) { + o.draw (cr); + } + + cr.restore (); + } + + public override Object copy () { + SvgDrawing drawing = new SvgDrawing (); + drawing.root_layer = root_layer.copy (); + drawing.defs = defs.copy (); + return drawing; + } + + public override void move (double dx, double dy) { + x += dx; + y += dy; + } + + public override void rotate (double theta, double xc, double yc) { + } + + public override bool is_empty () { + return false; + } + + public override void resize (double ratio_x, double ratio_y) { + } + } + + }
diff --git libsvgbird/SvgFile.vala(new)
--- /dev/null +++ b/libsvgbird/SvgFile.vala @@ -1,1 +1,1157 @@ + /* + Copyright (C) 2016 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using B; + using Cairo; + + namespace SvgBird { + + public class SvgFile : GLib.Object { + + SvgDrawing drawing; + + public SvgFile () { + } + + public SvgDrawing parse_svg_file (Tag tag) { + drawing = new SvgDrawing (); + + foreach (Attribute attr in tag.get_attributes ()) { + if (attr.get_name () == "width") { + drawing.width = parse_number (attr.get_content ()); + } + + if (attr.get_name () == "height") { + drawing.height = parse_number (attr.get_content ()); + } + } + + foreach (Tag t in tag) { + string name = t.get_name (); + + if (name == "g") { + parse_layer (drawing.root_layer, t); + } + + if (name == "defs") { + parse_defs (drawing, t); + } + + parse_object (drawing.root_layer, t); + } + + return drawing; + } + + private void parse_layer (Layer layer, Tag tag) { + bool hidden = false; + + foreach (Attribute attr in tag.get_attributes ()) { + if (attr.get_name () == "display" && attr.get_content () == "none") { + hidden = true; + } + + if (attr.get_name () == "visibility" + && (attr.get_content () == "hidden" + || attr.get_content () == "collapse")) { + hidden = true; + } + } + + if (hidden) { + layer.visible = !hidden; + } + + foreach (Tag t in tag) { + string name = t.get_name (); + + if (name == "g") { + Layer sublayer = new Layer (); + parse_layer (layer, t); + layer.subgroups.add (sublayer); + } + + parse_object (layer, t); + } + + foreach (Attribute attr in tag.get_attributes ()) { + if (attr.get_name () == "transform") { + layer.transforms = parse_transform (attr.get_content ()); + } + } + } + + void parse_defs (SvgDrawing drawing, Tag tag) { + foreach (Tag t in tag) { + // FIXME: radial + string name = t.get_name (); + + if (name == "linearGradient") { + parse_linear_gradient (drawing, t); + } + } + + foreach (Gradient gradient in drawing.defs.gradients) { + if (gradient.href != null) { + Gradient? referenced; + referenced = drawing.defs.get_gradient_for_id ((!) gradient.href); + + if (referenced != null) { + gradient.copy_stops ((!) referenced); + } + + gradient.href = null; + } + } + } + + void parse_linear_gradient (SvgDrawing drawing, Tag tag) { + Gradient gradient = new Gradient (); + + drawing.defs.add (gradient); + + foreach (Attribute attr in tag.get_attributes ()) { + string name = attr.get_name (); + + // FIXME: gradientUnits + + if (name == "gradientTransform") { + gradient.transforms = parse_transform (attr.get_content ()); + } + + if (name == "href") { + gradient.href = attr.get_content (); + } + + if (name == "x1") { + gradient.x1 = parse_number (attr.get_content ()); + } + + if (name == "y1") { + gradient.y1 = parse_number (attr.get_content ()); + } + + if (name == "x2") { + gradient.x2 = parse_number (attr.get_content ()); + } + + if (name == "y2") { + gradient.y2 = parse_number (attr.get_content ()); + } + + if (name == "id") { + gradient.id = attr.get_content (); + } + } + + foreach (Tag t in tag) { + // FIXME: radial + string name = t.get_name (); + + if (name == "stop") { + parse_stop (gradient, t); + } + } + } + + void parse_stop (Gradient gradient, Tag tag) { + SvgStyle style = SvgStyle.parse (drawing.defs, tag.get_attributes ()); + Stop stop = new Stop (); + + gradient.stops.add (stop); + + foreach (Attribute attr in tag.get_attributes ()) { + string name = attr.get_name (); + + if (name == "offset") { + string stop_offset = attr.get_content (); + + if (stop_offset.index_of ("%") > -1) { + stop_offset = stop_offset.replace ("%", ""); + stop.offset = parse_number (stop_offset) / 100.0; + } else { + stop.offset = parse_number (stop_offset); + } + } + } + + string? stop_color = style.style.get ("stop-color"); + string? stop_opacity = style.style.get ("stop-opacity"); + Color? color = new Color (0, 0, 0, 1); + + if (stop_color != null) { + color = Color.parse (stop_color); + + if (color != null) { + stop.color = (!) color; + } + } + + if (stop_opacity != null && color != null) { + ((!) color).a = parse_number (stop_opacity); + } + } + + void parse_object (Layer layer, Tag tag) { + string name = tag.get_name (); + + if (name == "path") { + parse_path (layer, tag); + } + + if (name == "polygon") { + parse_polygon (layer, tag); + } + + if (name == "polyline") { + parse_polyline (layer, tag); + } + + if (name == "rect") { + parse_rect (layer, tag); + } + + if (name == "circle") { + parse_circle (layer, tag); + } + + if (name == "ellipse") { + parse_ellipse (layer, tag); + } + + if (name == "line") { + parse_line (layer, tag); + } + } + + private void parse_polygon (Layer layer, Tag tag) { + } + + private void parse_polyline (Layer layer, Tag tag) { + } + + private void parse_rect (Layer layer, Tag tag) { + Rectangle rectangle = new Rectangle (); + + foreach (Attribute attr in tag.get_attributes ()) { + string attribute = attr.get_name (); + + if (attribute == "x") { + rectangle.x = parse_number (attr.get_content ()); + } + + if (attribute == "y") { + rectangle.y = parse_number (attr.get_content ()); + } + + if (attribute == "width") { + rectangle.width = parse_number (attr.get_content ()); + } + + if (attribute == "height") { + rectangle.height = parse_number (attr.get_content ()); + } + + if (attribute == "rx") { + rectangle.rx = parse_number (attr.get_content ()); + } + + if (attribute == "ry") { + rectangle.ry = parse_number (attr.get_content ()); + } + } + + rectangle.transforms = get_transform (tag.get_attributes ()); + rectangle.style = SvgStyle.parse (drawing.defs,tag.get_attributes ()); + rectangle.visible = is_visible (tag); + + layer.add_object (rectangle); + } + + private void parse_circle (Layer layer, Tag tag) { + } + + private void parse_ellipse (Layer layer, Tag tag) { + } + + private void parse_line (Layer layer, Tag tag) { + } + + // FIXME: reverse order? + public SvgTransforms parse_transform (string transforms) { + string[] functions; + string transform = transforms; + SvgTransforms transform_functions; + + transform_functions = new SvgTransforms (); + + transform = transform.replace ("\t", " "); + transform = transform.replace ("\n", " "); + transform = transform.replace ("\r", " "); + + // use only a single space as separator + while (transform.index_of (" ") > -1) { + transform = transform.replace (" ", " "); + } + + if (unlikely (transform.index_of (")") == -1)) { + warning ("No parenthesis in transform function."); + return transform_functions; + } + + // add separator + transform = transform.replace (") ", "|"); + transform = transform.replace (")", "|"); + functions = transform.split ("|"); + + for (int i = 0; i < functions.length; i++) { + if (functions[i].has_prefix ("translate")) { + transform_functions.add (translate (functions[i])); + } + + if (functions[i].has_prefix ("scale")) { + transform_functions.add (scale (functions[i])); + } + + if (functions[i].has_prefix ("matrix")) { + transform_functions.add (matrix (functions[i])); + } + + // TODO: rotate etc. + } + + return transform_functions; + } + + private SvgTransform matrix (string function) { + string parameters = get_transform_parameters (function); + string[] p = parameters.split (" "); + SvgTransform transform = new SvgTransform (); + transform.type = TransformType.MATRIX; + + if (unlikely (p.length != 6)) { + warning ("Expecting six parameters for matrix transformation."); + return transform; + } + + for (int i = 0; i < 6; i++) { + double argument = parse_double (p[i]); + transform.arguments.add (argument); + } + + return transform; + } + + private static string remove_unit (string d) { + string s = d.replace ("pt", ""); + s = s.replace ("pc", ""); + s = s.replace ("mm", ""); + s = s.replace ("cm", ""); + s = s.replace ("in", ""); + s = s.replace ("px", ""); + return s; + } + + public static double parse_number (string? number_with_unit) { + if (number_with_unit == null) { + return 0; + } + + string d = (!) number_with_unit; + string s = remove_unit (d); + double n = parse_double (s); + + if (d.has_suffix ("pt")) { + n *= 1.25; + } else if (d.has_suffix ("pc")) { + n *= 15; + } else if (d.has_suffix ("mm")) { + n *= 3.543307; + } else if (d.has_suffix ("cm")) { + n *= 35.43307; + } else if (d.has_suffix ("in")) { + n *= 90; + } + + return n; + } + + private SvgTransform scale (string function) { + string parameters = get_transform_parameters (function); + string[] p = parameters.split (" "); + SvgTransform transform = new SvgTransform (); + transform.type = TransformType.SCALE; + + if (p.length > 0) { + transform.arguments.add (parse_double (p[0])); + } + + if (p.length > 1) { + transform.arguments.add (parse_double (p[1])); + } + + return transform; + } + + private SvgTransform translate (string function) { + string parameters = get_transform_parameters (function); + string[] p = parameters.split (" "); + SvgTransform transform = new SvgTransform (); + transform.type = TransformType.TRANSLATE; + + if (p.length > 0) { + transform.arguments.add (parse_double (p[0])); + } + + if (p.length > 1) { + transform.arguments.add (parse_double (p[1])); + } + + return transform; + } + + private string get_transform_parameters (string function) { + int i; + string param = ""; + + i = function.index_of ("("); + return_val_if_fail (i != -1, param); + param = function.substring (i); + + param = param.replace ("(", ""); + param = param.replace ("\n", " "); + param = param.replace ("\t", " "); + param = param.replace (",", " "); + + while (param.index_of (" ") > -1) { + param.replace (" ", " "); + } + + return param.strip(); + } + + private bool is_visible (Tag tag) { + bool hidden = false; + + foreach (Attribute attr in tag.get_attributes ()) { + if (attr.get_name () == "display" && attr.get_content () == "none") { + hidden = true; + } + + if (attr.get_name () == "visibility" + && (attr.get_content () == "hidden" + || attr.get_content () == "collapse")) { + hidden = true; + } + } + + return !hidden; + } + + private SvgTransforms get_transform (Attributes attributes) { + foreach (Attribute attr in attributes) { + if (attr.get_name () == "transform") { + return parse_transform (attr.get_content ()); + } + } + + return new SvgTransforms (); + } + + private void parse_path (Layer layer, Tag tag) { + SvgPath path = new SvgPath (); + + foreach (Attribute attr in tag.get_attributes ()) { + if (attr.get_name () == "d") { + path.points = parse_points (attr.get_content ()); + } + } + + path.transforms = get_transform (tag.get_attributes ()); + path.style = SvgStyle.parse (drawing.defs, tag.get_attributes ()); + path.visible = is_visible (tag); + + layer.add_object (path); + } + + public Gee.ArrayList<Points> parse_points (string data) { + Gee.ArrayList<Points> path_data = new Gee.ArrayList<Points> (); + Points points = new Points (); + BezierPoints[] bezier_points; + int points_size; + + get_bezier_points (data, out bezier_points, out points_size, true); + + for (int i = 0; i < points_size; i++) { + // FIXME: add more types + if (bezier_points[i].type == 'M') { + points.x = bezier_points[i].x0; + points.y = bezier_points[i].y0; + } else if (bezier_points[i].type == 'C') { + points.add (bezier_points[i].x0); + points.add (bezier_points[i].y0); + points.add (bezier_points[i].x1); + points.add (bezier_points[i].y1); + points.add (bezier_points[i].x2); + points.add (bezier_points[i].y2); + } else if (bezier_points[i].type == 'L') { + points.add (bezier_points[i].x0); + points.add (bezier_points[i].y0); + points.add (bezier_points[i].x0); + points.add (bezier_points[i].y0); + points.add (bezier_points[i].x0); + points.add (bezier_points[i].y0); + } else if (bezier_points[i].type == 'z') { + points.closed = true; + path_data.add (points); + points = new Points (); + } else { + string type = (!) bezier_points[i].type.to_string (); + warning (@"SVG conversion not implemented for $type"); + } + } + + if (points.point_data.size > 0) { + path_data.add (points); + } + + return path_data; + } + + public static double parse_double (string? s) { + if (unlikely (s == null)) { + warning ("number is null"); + return 0; + } + + if (unlikely (!double.try_parse ((!) s))) { + warning (@"Expecting a double got: $((!) s)"); + return 0; + } + + return double.parse ((!) s); + } + + + public static void get_bezier_points (string point_data, out BezierPoints[] bezier_points, out int points, bool svg_glyph) { + double px = 0; + double py = 0; + double px2 = 0; + double py2 = 0; + double cx = 0; + double cy = 0; + string[] c; + double arc_rx, arc_ry; + double arc_rotation; + int large_arc; + int arc_sweep; + double arc_dest_x, arc_dest_y; + + int bi = 0; + + string data = add_separators (point_data); + c = data.split (" "); + + // the arc instruction can use up to eight points + int bezier_points_length = 8 * c.length + 1; + bezier_points = new BezierPoints[bezier_points_length]; + + for (int i = 0; i < bezier_points_length; i++) { + bezier_points[i] = new BezierPoints (); + } + + // parse path + int i = -1; + while (++i < c.length && bi < bezier_points.length) { + if (c[i] == "m") { + while (i + 2 < c.length && is_point (c[i + 1])) { + bezier_points[bi].type = 'M'; + bezier_points[bi].svg_type = 'm'; + + px += parse_double (c[++i]); + + if (svg_glyph) { + py += parse_double (c[++i]); + } else { + py += -parse_double (c[++i]); + } + + bezier_points[bi].x0 = px; + bezier_points[bi].y0 = py; + bi++; + } + } else if (c[i] == "M") { + while (i + 2 < c.length && is_point (c[i + 1])) { + bezier_points[bi].type = 'M'; + bezier_points[bi].svg_type = 'M'; + + px = parse_double (c[++i]); + + if (svg_glyph) { + py = parse_double (c[++i]); + } else { + py = -parse_double (c[++i]); + } + + bezier_points[bi].x0 = px; + bezier_points[bi].y0 = py; + bi++; + } + } else if (c[i] == "h") { + while (i + 1 < c.length && is_point (c[i + 1])) { + bezier_points[bi].type = 'L'; + bezier_points[bi].svg_type = 'h'; + + px += parse_double (c[++i]); + + bezier_points[bi].x0 = px; + bezier_points[bi].y0 = py; + bi++; + } + } else if (i + 1 < c.length && c[i] == "H") { + while (is_point (c[i + 1])) { + bezier_points[bi].type = 'L'; + bezier_points[bi].svg_type = 'H'; + + px = parse_double (c[++i]); + + bezier_points[bi].x0 = px; + bezier_points[bi].y0 = py; + bi++; + } + } else if (c[i] == "v") { + while (i + 1 < c.length && is_point (c[i + 1])) { + bezier_points[bi].type = 'L'; + bezier_points[bi].svg_type = 'v'; + + if (svg_glyph) { + py = py + parse_double (c[++i]); + } else { + py = py - parse_double (c[++i]); + } + + bezier_points[bi].x0 = px; + bezier_points[bi].y0 = py; + bi++; + } + } else if (i + 1 < c.length && c[i] == "V") { + while (is_point (c[i + 1])) { + bezier_points[bi].type = 'L'; + bezier_points[bi].svg_type = 'V'; + + if (svg_glyph) { + py = parse_double (c[++i]); + } else { + py = -parse_double (c[++i]); + } + + bezier_points[bi].x0 = px; + bezier_points[bi].y0 = py; + bi++; + } + } else if (c[i] == "l") { + while (i + 2 < c.length && is_point (c[i + 1])) { + bezier_points[bi].type = 'L'; + bezier_points[bi].svg_type = 'l'; + + cx = px + parse_double (c[++i]); + + if (svg_glyph) { + cy = py + parse_double (c[++i]); + } else { + cy = py - parse_double (c[++i]); + } + + px = cx; + py = cy; + + bezier_points[bi].x0 = cx; + bezier_points[bi].y0 = cy; + bi++; + } + } else if (c[i] == "L") { + while (i + 2 < c.length && is_point (c[i + 1])) { + bezier_points[bi].type = 'L'; + bezier_points[bi].svg_type = 'L'; + + cx = parse_double (c[++i]); + + if (svg_glyph) { + cy = parse_double (c[++i]); + } else { + cy = -parse_double (c[++i]); + } + + px = cx; + py = cy; + + bezier_points[bi].x0 = cx; + bezier_points[bi].y0 = cy; + bi++; + } + } else if (c[i] == "c") { + while (i + 6 < c.length && is_point (c[i + 1])) { + bezier_points[bi].type = 'C'; + bezier_points[bi].svg_type = 'C'; + + cx = px + parse_double (c[++i]); + + if (svg_glyph) { + cy = py + parse_double (c[++i]); + } else { + cy = py - parse_double (c[++i]); + } + + bezier_points[bi].x0 = cx; + bezier_points[bi].y0 = cy; + + cx = px + parse_double (c[++i]); + + if (svg_glyph) { + cy = py + parse_double (c[++i]); + } else { + cy = py - parse_double (c[++i]); + } + + px2 = cx; + py2 = cy; + + bezier_points[bi].x1 = px2; + bezier_points[bi].y1 = py2; + + cx = px + parse_double (c[++i]); + + if (svg_glyph) { + cy = py + parse_double (c[++i]); + } else { + cy = py + -parse_double (c[++i]); + } + + bezier_points[bi].x2 = cx; + bezier_points[bi].y2 = cy; + + px = cx; + py = cy; + + bi++; + } + } else if (c[i] == "C") { + while (i + 6 < c.length && is_point (c[i + 1])) { + bezier_points[bi].type = 'C'; + bezier_points[bi].svg_type = 'C'; + + cx = parse_double (c[++i]); + + if (svg_glyph) { + cy = parse_double (c[++i]); + } else { + cy = -parse_double (c[++i]); + } + + bezier_points[bi].x0 = cx; + bezier_points[bi].y0 = cy; + + cx = parse_double (c[++i]); + + if (svg_glyph) { + cy = parse_double (c[++i]); + } else { + cy = -parse_double (c[++i]); + } + + px2 = cx; + py2 = cy; + + bezier_points[bi].x1 = cx; + bezier_points[bi].y1 = cy; + + cx = parse_double (c[++i]); + + if (svg_glyph) { + cy = parse_double (c[++i]); + } else { + cy = -parse_double (c[++i]); + } + + bezier_points[bi].x2 = cx; + bezier_points[bi].y2 = cy; + + px = cx; + py = cy; + + bi++; + } + } else if (c[i] == "q") { + while (i + 4 < c.length && is_point (c[i + 1])) { + bezier_points[bi].type = 'Q'; + bezier_points[bi].svg_type = 'q'; + + cx = px + parse_double (c[++i]); + + if (svg_glyph) { + cy = py + parse_double (c[++i]); + } else { + cy = py - parse_double (c[++i]); + } + + bezier_points[bi].x0 = cx; + bezier_points[bi].y0 = cy; + + px2 = cx; + py2 = cy; + + cx = px + parse_double (c[++i]); + + if (svg_glyph) { + cy = py + parse_double (c[++i]); + } else { + cy = py - parse_double (c[++i]); + } + + bezier_points[bi].x1 = cx; + bezier_points[bi].y1 = cy; + + px = cx; + py = cy; + + bi++; + } + } else if (c[i] == "Q") { + while (i + 4 < c.length && is_point (c[i + 1])) { + bezier_points[bi].type = 'Q'; + bezier_points[bi].svg_type = 'Q'; + + cx = parse_double (c[++i]); + + if (svg_glyph) { + cy = parse_double (c[++i]); + } else { + cy = -parse_double (c[++i]); + } + + bezier_points[bi].x0 = cx; + bezier_points[bi].y0 = cy; + + px2 = cx; + py2 = cy; + + cx = parse_double (c[++i]); + + if (svg_glyph) { + cy = parse_double (c[++i]); + } else { + cy = -parse_double (c[++i]); + } + + px = cx; + py = cy; + + bezier_points[bi].x1 = cx; + bezier_points[bi].y1 = cy; + + bi++; + } + } else if (c[i] == "t") { + while (i + 2 < c.length && is_point (c[i + 1])) { + bezier_points[bi].type = 'Q'; + bezier_points[bi].svg_type = 't'; + + // the first point is the reflection + cx = 2 * px - px2; + cy = 2 * py - py2; // if (svg_glyph) ? + + bezier_points[bi].x0 = cx; + bezier_points[bi].y0 = cy; + + px2 = cx; + py2 = cy; + + cx = px + parse_double (c[++i]); + + if (svg_glyph) { + cy = py + parse_double (c[++i]); + } else { + cy = py - parse_double (c[++i]); + } + + px = cx; + py = cy; + + bezier_points[bi].x1 = px; + bezier_points[bi].y1 = py; + + bi++; + } + } else if (c[i] == "T") { + while (i + 2 < c.length && is_point (c[i + 1])) { + bezier_points[bi].type = 'Q'; + bezier_points[bi].svg_type = 'T'; + + // the reflection + cx = 2 * px - px2; + cy = 2 * py - py2; + + bezier_points[bi].x0 = cx; + bezier_points[bi].y0 = cy; + + px2 = cx; + py2 = cy; + + cx = parse_double (c[++i]); + + if (svg_glyph) { + cy = parse_double (c[++i]); + } else { + cy = -parse_double (c[++i]); + } + + px = cx; + py = cy; + + bezier_points[bi].x1 = px; + bezier_points[bi].y1 = py; + + bi++; + } + } else if (c[i] == "s") { + while (i + 4 < c.length && is_point (c[i + 1])) { + bezier_points[bi].type = 'C'; + bezier_points[bi].svg_type = 's'; + + // the first point is the reflection + cx = 2 * px - px2; + cy = 2 * py - py2; // if (svg_glyph) ? + + bezier_points[bi].x0 = cx; + bezier_points[bi].y0 = cy; + + cx = px + parse_double (c[++i]); + + if (svg_glyph) { + cy = py + parse_double (c[++i]); + } else { + cy = py - parse_double (c[++i]); + } + + px2 = cx; + py2 = cy; + + bezier_points[bi].x1 = px2; + bezier_points[bi].y1 = py2; + + cx = px + parse_double (c[++i]); + + if (svg_glyph) { + cy = py + parse_double (c[++i]); + } else { + cy = py - parse_double (c[++i]); + } + + bezier_points[bi].x2 = cx; + bezier_points[bi].y2 = cy; + + px = cx; + py = cy; + + bi++; + } + } else if (c[i] == "S") { + while (i + 4 < c.length && is_point (c[i + 1])) { + bezier_points[bi].type = 'C'; + bezier_points[bi].svg_type = 'S'; + + // the reflection + cx = 2 * px - px2; + cy = 2 * py - py2; // if (svg_glyph) ? + + bezier_points[bi].x0 = cx; + bezier_points[bi].y0 = cy; + + // the other two are regular cubic points + cx = parse_double (c[++i]); + + if (svg_glyph) { + cy = parse_double (c[++i]); + } else { + cy = -parse_double (c[++i]); + } + + px2 = cx; + py2 = cy; + + bezier_points[bi].x1 = px2; + bezier_points[bi].y1 = py2; + + cx = parse_double (c[++i]); + + if (svg_glyph) { + cy = parse_double (c[++i]); + } else { + cy = -parse_double (c[++i]); + } + + bezier_points[bi].x2 = cx; + bezier_points[bi].y2 = cy; + + px = cx; + py = cy; + + bi++; + } + } else if (c[i] == "a") { + while (i + 7 < c.length && is_point (c[i + 1])) { + arc_rx = parse_double (c[++i]); + arc_ry = parse_double (c[++i]); + + arc_rotation = parse_double (c[++i]); + large_arc = parse_int (c[++i]); + arc_sweep = parse_int (c[++i]); + + cx = px + parse_double (c[++i]); + + if (svg_glyph) { + cy = py + parse_double (c[++i]); + } else { + cy = py - parse_double (c[++i]); + } + + arc_dest_x = cx; + arc_dest_y = cy; + + add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy); + + px = cx; + py = cy; + } + } else if (i + 7 < c.length && c[i] == "A") { + while (is_point (c[i + 1])) { + arc_rx = parse_double (c[++i]); + arc_ry = parse_double (c[++i]); + + arc_rotation = parse_double (c[++i]); + large_arc = parse_int (c[++i]); + arc_sweep = parse_int (c[++i]); + + cx = parse_double (c[++i]); + + if (svg_glyph) { + cy = parse_double (c[++i]); + } else { + cy = -parse_double (c[++i]); + } + + arc_dest_x = cx; + arc_dest_y = cy; + + add_arc_points (bezier_points, ref bi, px, py, arc_rx, arc_ry, arc_rotation, large_arc == 1, arc_sweep == 1, cx, cy); + + px = cx; + py = cy; + + + } + } else if (c[i] == "z") { + bezier_points[bi].type = 'z'; + bezier_points[bi].svg_type = 'z'; + + bi++; + } else if (c[i] == "Z") { + bezier_points[bi].type = 'z'; + bezier_points[bi].svg_type = 'z'; + + bi++; + } else if (c[i] == "") { + } else if (c[i] == " ") { + } else { + warning (@"Unknown instruction: $(c[i])"); + } + } + + if (bi == 0) { + warning ("No points in path."); + } + + points = bi; + } + + static int parse_int (string? s) { + if (unlikely (s == null)) { + warning ("null instead of string"); + return 0; + } + + if (unlikely (!int64.try_parse ((!) s))) { + warning (@"Expecting an integer: $((!) s)"); + return 0; + } + + return int.parse ((!) s); + } + + static bool is_point (string? s) { + if (unlikely (s == null)) { + warning ("s is null"); + return false; + } + + return double.try_parse ((!) s); + } + + /** Add space as separator to svg data. + * @param d svg data + */ + public static string add_separators (string d) { + string data = d; + + data = data.replace (",", " "); + data = data.replace ("a", " a "); + data = data.replace ("A", " A "); + data = data.replace ("m", " m "); + data = data.replace ("M", " M "); + data = data.replace ("h", " h "); + data = data.replace ("H", " H "); + data = data.replace ("v", " v "); + data = data.replace ("V", " V "); + data = data.replace ("l", " l "); + data = data.replace ("L", " L "); + data = data.replace ("q", " q "); + data = data.replace ("Q", " Q "); + data = data.replace ("c", " c "); + data = data.replace ("C", " C "); + data = data.replace ("t", " t "); + data = data.replace ("T", " T "); + data = data.replace ("s", " s "); + data = data.replace ("S", " S "); + data = data.replace ("zM", " z M "); + data = data.replace ("zm", " z m "); + data = data.replace ("z", " z "); + data = data.replace ("Z", " Z "); + data = data.replace ("-", " -"); + data = data.replace ("e -", "e-"); // minus can be either separator or a negative exponent + data = data.replace ("\t", " "); + data = data.replace ("\r\n", " "); + data = data.replace ("\n", " "); + + // use only a single space as separator + while (data.index_of (" ") > -1) { + data = data.replace (" ", " "); + } + + return data; + } + + } + + }
diff --git libsvgbird/SvgPath.vala(new)
--- /dev/null +++ b/libsvgbird/SvgPath.vala @@ -1,1 +1,89 @@ + /* + Copyright (C) 2016 Johan Mattsson + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + */ + + using Cairo; + + namespace SvgBird { + + public class SvgPath : Object { + public Gee.ArrayList<Points> points = new Gee.ArrayList<Points> (); + + public SvgPath () { + } + + public SvgPath.create_copy (SvgPath p) { + Object.copy_attributes (p, this); + } + + public override bool is_over (double x, double y) { + return false; + } + + public override void draw (Context cr) { + cr.save (); + cr.new_path (); + + foreach (Points p in points) { + cr.move_to (p.x, p.y); + draw_points (cr, p); + + if (p.closed) { + cr.close_path (); + } + } + + apply_transform (cr); + paint (cr); + cr.restore (); + } + + public void draw_points (Context cr, Points points) { + Doubles p = points.point_data; + + return_if_fail (p.size % 6 == 0); + + for (int i = 0; i < p.size; i += 6) { + cr.curve_to (p.data[i], p.data[i + 1], + p.data[i + 2], p.data[i + 3], + p.data[i + 4], p.data[i + 5]); + } + } + + public override void move (double dx, double dy) { + } + + public override void update_region_boundaries () { + } + + public override void rotate (double theta, double xc, double yc) { + } + + public override bool is_empty () { + return false; + } + + public override void resize (double ratio_x, double ratio_y) { + } + + public override Object copy () { + return new SvgPath.create_copy (this); + } + + public override string to_string () { + return "SvgPath"; + } + } + + }
diff --git libsvgbird/SvgStyle.vala(new)
--- /dev/null +++ b/libsvgbird/SvgStyle.vala @@ -1,1 +1,148 @@ + /* + 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); + } + } + } + + }
--- /dev/null +++ b/libsvgbird/SvgTransform.vala @@ -1,1 +1,76 @@ + /* + 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; + } + } + + }
--- /dev/null +++ b/libsvgbird/SvgTransforms.vala @@ -1,1 +1,48 @@ + /* + 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; + } + } + + }
--- a/scripts/version.py +++ b/scripts/version.py @@ -2,5 +2,5 @@ """ - 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,5 +20,9 @@ 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