Commit 35c3bcd
Move SVG classes to a subdirectory
Changed files (14)
modifieddodo.pydeletedlibbirdfont/Svg.valaaddedlibbirdfont/Svg/Svg.valaaddedlibbirdfont/Svg/SvgArc.valaaddedlibbirdfont/Svg/SvgFile.valaaddedlibbirdfont/Svg/SvgFont.valaaddedlibbirdfont/Svg/SvgFontFormatWriter.valaaddedlibbirdfont/Svg/SvgParser.valaaddedlibbirdfont/Svg/SvgStyle.valadeletedlibbirdfont/SvgArc.valadeletedlibbirdfont/SvgFont.valadeletedlibbirdfont/SvgFontFormatWriter.valadeletedlibbirdfont/SvgParser.valadeletedlibbirdfont/SvgStyle.vala
Diff
@@ -302,6 +302,7 @@ def make_libbirdfont(target_binary, deps): libbirdfont/*.vala \ libbirdfont/OpenFontFormat/*.vala \ libbirdfont/TextRendering/*.vala \+ libbirdfont/Svg/*.vala \ --pkg """ + config.GEE + """ \ --pkg gio-2.0 \ --pkg cairo \deleted file mode 100644@@ -1,353 +0,0 @@-/*- Copyright (C) 2012 Johan Mattsson-- This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version.-- This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details.-*/--using Cairo;--namespace BirdFont {--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;-}- -}new file mode 100644@@ -0,0 +1,353 @@+/*+ 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 {++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;+}+ +}new file mode 100644@@ -0,0 +1,181 @@+/*+ * BirdFont code from SVG Salamander+ * + * Copyright (c) 2004, Mark McKay+ * Copyright (c) 2014, Johan Mattsson+ * All rights reserved.+ *+ * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following+ * conditions are met:+ *+ * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following+ * disclaimer.+ * - Redistributions in binary form must reproduce the above+ * copyright notice, this list of conditions and the following+ * disclaimer in the documentation and/or other materials + * provided with the distribution.+ *+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED+ * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Mark McKay can be contacted at mark@kitfox.com. Salamander and other+ * projects can be found at http://www.kitfox.com+ *+ * Created on January 26, 2004, 8:40 PM+ * Adapded to BirdFont on Juli 2, 2014, 5:01 PM+ */++using Math;++namespace 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++;+ }+}++}+ new file mode 100644@@ -0,0 +1,21 @@+/*+ 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 SvgFile : GLib.Object {++}++}new file mode 100644@@ -0,0 +1,264 @@+/*+ Copyright (C) 2013 2014 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details.+*/++using B;++namespace BirdFont {++class SvgFont : GLib.Object {+ Font font;+ double units = 1;+ double font_advance = 0;+ + public SvgFont (Font f) {+ this.font = f;+ }+ + /** Load svg font from file. */+ public void load (string path) {+ string data;+ XmlParser xml_parser;+ try {+ FileUtils.get_contents (path, out data);+ xml_parser = new XmlParser (data);+ parse_svg_font (xml_parser.get_root_tag ());+ } catch (GLib.Error e) {+ warning (e.message);+ }+ }+ + void parse_svg_font (Tag tag) {+ foreach (Tag t in tag) {+ if (t.get_name () == "defs") {+ parse_svg_font (t);+ }+ + if (t.get_name () == "font") {+ parse_font_tag (t);+ parse_svg_font (t);+ }++ if (t.get_name () == "font-face") {+ parse_font_limits (t);+ }++ if (t.get_name () == "hkern") {+ parse_hkern (t);+ }+ + if (t.get_name () == "glyph") {+ parse_glyph (t);+ }+ } + }++ void parse_hkern (Tag tag) {+ string left = "";+ string right = "";+ string left_name = "";+ string right_name = "";+ double kerning = 0;+ unichar l, r;+ StringBuilder sl, sr;+ GlyphRange grr, grl;+ KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes ();+ + foreach (Attribute attr in tag.get_attributes ()) {+ // left+ if (attr.get_name () == "u1") {+ left = attr.get_content ();+ } + + // right + if (attr.get_name () == "u2") {+ right = attr.get_content ();+ }++ if (attr.get_name () == "g1") {+ left_name = attr.get_content ();+ }+ + if (attr.get_name () == "g2") {+ right_name = attr.get_content ();+ }+ + // kerning+ if (attr.get_name () == "k") {+ kerning = double.parse (attr.get_content ()) * units;+ }+ }+ + // FIXME: ranges and sequences for u1 & u2 + g1 & g2+ foreach (string lk in left.split (",")) {+ foreach (string rk in right.split (",")) {+ l = get_unichar (lk);+ r = get_unichar (rk);+ + sl = new StringBuilder ();+ sl.append_unichar (l);+ + sr = new StringBuilder ();+ sr.append_unichar (r);+ + try {+ grl = new GlyphRange ();+ grl.parse_ranges (sl.str);+ + grr = new GlyphRange ();+ grr.parse_ranges (sr.str);+ + classes.set_kerning (grl, grr, -kerning);+ } catch (MarkupError e) {+ warning (e.message);+ } + }+ }+ }++ void parse_font_limits (Tag tag) {+ double top_limit = 0;+ double bottom_limit = 0;+ + foreach (Attribute attr in tag.get_attributes ()) {+ if (attr.get_name () == "units-per-em") {+ units = 100.0 / double.parse (attr.get_content ());+ } + }++ foreach (Attribute attr in tag.get_attributes ()) { + if (attr.get_name () == "ascent") {+ top_limit = double.parse (attr.get_content ());+ }+ + if (attr.get_name () == "descent") {+ bottom_limit = double.parse (attr.get_content ());+ } + }+ + top_limit *= units;+ bottom_limit *= units;+ + font.bottom_limit = bottom_limit;+ font.top_limit = top_limit;+ }+ + void parse_font_tag (Tag tag) {+ foreach (Attribute attr in tag.get_attributes ()) {+ if (attr.get_name () == "horiz-adv-x") {+ font_advance = double.parse (attr.get_content ());+ }+ + if (attr.get_name () == "id") {+ font.set_name (attr.get_content ());+ }+ }+ }+ + /** Obtain unichar value from either a character, a hex representation of+ * a character or series of characters (f, m or ffi).+ */+ static unichar get_unichar (string val) {+ string v = val;+ unichar unicode_value;+ + if (val == "&") {+ return '&';+ }+ + // TODO: parse ligatures+ if (v.has_prefix ("&")) {+ // parse hex value+ v = v.substring (0, v.index_of (";"));+ v = v.replace ("&#x", "U+");+ v = v.replace (";", "");+ unicode_value = Font.to_unichar (v);+ } else {+ // obtain unicode value+ + if (v.char_count () > 1) {+ warning ("font contains ligatures");+ return '\0';+ }+ + unicode_value = v.get_char (0);+ }+ + return unicode_value; + }+ + bool is_ligature (string v) {+ if (v.has_prefix ("&")) {+ return false;+ }+ + return v.char_count () > 1;+ }+ + void parse_glyph (Tag tag) {+ unichar unicode_value = 0;+ string glyph_name = "";+ string svg = "";+ Glyph glyph;+ GlyphCollection glyph_collection;+ double advance = font_advance;+ string ligature = "";+ SvgParser parser = new SvgParser ();+ StringBuilder unicode_name; ++ parser.set_format (SvgFormat.INKSCAPE);++ foreach (Attribute attr in tag.get_attributes ()) {+ if (attr.get_name () == "unicode") {+ unicode_value = get_unichar (attr.get_content ());+ + if (glyph_name == "") {+ glyph_name = attr.get_content ();+ }+ + if (is_ligature (attr.get_content ())) {+ ligature = attr.get_content ();+ }+ }+ + // svg data+ if (attr.get_name () == "d") {+ svg = attr.get_content ();+ }+ + if (attr.get_name () == "glyph-name") {+ glyph_name = attr.get_content ();+ }++ if (attr.get_name () == "horiz-adv-x") {+ advance = double.parse (attr.get_content ());+ }+ }++ unicode_name = new StringBuilder ();+ unicode_name.append_unichar (unicode_value);++ glyph = new Glyph (unicode_name.str, unicode_value);+ parser.add_path_to_glyph (svg, glyph, true, units); + glyph.right_limit = glyph.left_limit + advance * units;+ + // FIXME: add svg font ligatures+ + glyph_collection = new GlyphCollection (unicode_value, glyph_name);+ glyph_collection.insert_glyph (glyph, true);+ + font.add_glyph_collection (glyph_collection);+ }+}++}new file mode 100644@@ -0,0 +1,132 @@+/*+ Copyright (C) 2012, 2014 Johan Mattsson++ This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 3 of the + License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details.+*/++namespace BirdFont {++class SvgFontFormatWriter : GLib.Object {++ DataOutputStream os;++ public SvgFontFormatWriter () {+ }++ public void open (File file) throws Error {+ if (file.query_exists ()) {+ throw new FileError.EXIST ("SvgFontFormatWriter: file exists.");+ }+ + os = new DataOutputStream (file.create(FileCreateFlags.REPLACE_DESTINATION));+ }++ public void close () throws Error {+ os.close ();+ }+ + public void write_font_file (Font font) throws Error {+ string font_name = font.get_full_name ();+ + int units_per_em = 100;+ + int ascent = 80;+ int descent = -20;+ + StringBuilder b;+ + Glyph? g;+ Glyph glyph;+ unichar index = 0;+ + string uni;+ + KerningClasses classes;+ + put ("""<?xml version="1.0" standalone="no"?>""");+ put ("""<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >""");+ put ("""<svg>""");++ // (metadata goes here)++ put ("<defs>");++ put (@"<font id=\"$font_name\" horiz-adv-x=\"250\" >");+ put (@"<font-face units-per-em=\"$(to_float (units_per_em))\" ascent=\"$(to_float (ascent))\" descent=\"$(to_float (descent))\" />");++ // (missing-glyph goes here)++ // regular glyphs + while (true) {+ g = font.get_glyph_index (index++);+ + if (g == null) {+ break;+ }+ + glyph = (!) g;+ + b = new StringBuilder ();+ b.append_unichar (glyph.get_unichar ());++ if (glyph.get_unichar () >= ' ' && b.str.validate ()) {+ if (b.str == "\"" || b.str == "&" || b.str == "<" || b.str == ">") {+ uni = Font.to_hex_code (glyph.get_unichar ());+ put (@"<glyph unicode=\"&#x$(uni);\" horiz-adv-x=\"$(to_float (glyph.get_width ()))\" d=\"$(glyph.get_svg_data ())\" />"); + } else {+ put (@"<glyph unicode=\"$(b.str)\" horiz-adv-x=\"$(to_float (glyph.get_width ()))\" d=\"$(glyph.get_svg_data ())\" />");+ }+ }+ }+ + // FIXME: ligatures+ classes = BirdFont.get_current_font ().get_kerning_classes ();+ classes.all_pairs ((kerning) => {+ string l, r;+ + foreach (Kerning k in kerning.kerning) {+ try {+ if (k.glyph != null) {+ l = Font.to_hex_code (kerning.character.unichar_code);+ r = Font.to_hex_code (((!)k.glyph).unichar_code); + os.put_string (@"<hkern u1=\"&#x$l;\" u2=\"&#x$r;\" k=\"$(to_float (-k.val))\"/>\n");+ } else {+ warning ("No glyph.");+ }+ } catch (GLib.Error e) {+ warning (e.message);+ }+ }+ }); ++ put ("</font>");+ put ("</defs>");+ put ("</svg>");+ }++ string to_float (double d) {+ string s = @"$d";+ if (s.index_of ("e") != -1) {+ return "0".dup ();+ }+ return s.replace (",", ".");+ }++ /** Write a new line */+ private void put (string line) throws Error {+ os.put_string (line);+ os.put_string ("\n");+ }++}+++}new file mode 100644@@ -0,0 +1,2114 @@+/*+ 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) {+ try {+ string data;+ FileUtils.get_contents (path, out data);+ glyph.color_svg_data = data;++ if (glyph.color_svg_data != null) {+ glyph.svg_x = glyph.left_limit;+ glyph.svg_y = BirdFont.get_current_font ().top_position;+ }+ } catch (GLib.Error e) {+ warning (e.message);+ }+ }+ + 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) {+ FastPath path = new FastPath.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 FastPath) {+ path_list.add (((FastPath) 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 (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 (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 (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 (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 (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 (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 FastPath) {+ Path p1 = ((FastPath) o1).get_path ();+ inside_count = 0;+ + foreach (Object o2 in layer.objects) {+ if (o2 is FastPath) {+ Path p2 = ((FastPath) 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+ */+ 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);+ }+ }+ + /** + * @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) {+ double px = 0;+ double py = 0;+ double px2 = 0;+ double py2 = 0;+ double cx = 0;+ double cy = 0;+ string data;+ Font font;+ PathList path_list = new PathList ();+ BezierPoints[] bezier_points;+ string[] c;+ double arc_rx, arc_ry;+ double arc_rotation;+ int large_arc;+ int arc_sweep;+ double arc_dest_x, arc_dest_y;++ font = BirdFont.get_current_font ();+ + data = add_separators (d);+ c = data.split (" ");+ bezier_points = new BezierPoints[8 * c.length + 1]; // the arc instruction can use up to eight points+ + for (int i = 0; i < 2 * c.length + 1; i++) {+ bezier_points[i] = new BezierPoints ();+ }+ + int bi = 0;+ + // 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; // if (svg_glyph) ?+ + 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.");+ return path_list; + }+ + move_and_resize (bezier_points, bi, svg_glyph, units, glyph);+ + if (format == SvgFormat.ILLUSTRATOR) {+ path_list = create_paths_illustrator (bezier_points, bi);+ } else {+ path_list = create_paths_inkscape (bezier_points, bi);+ }++ // 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);+ }+ + static double parse_double (string? s) {+ if (is_null (s)) {+ warning ("Got null instead of expected string.");+ return 0;+ }+ + if (!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;+ }+}++}new file mode 100644@@ -0,0 +1,124 @@+/*+ 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 {+ + Gee.HashMap<string, string> style;+ + 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 (Attributes attributes) {+ SvgStyle s = new SvgStyle ();+ + 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 ());+ }+ }+ + return s;+ }+ + void parse_key_value_pairs (string svg_style) {+ string[] p = svg_style.split (";");+ string[] pair;+ string k, v;+ + foreach (string kv in p) {+ pair = kv.split (":");+ + if (pair.length != 2) {+ warning ("pair.length != 2");+ continue;+ }+ + k = pair[0];+ v = pair[1];+ + style.set (k, v);+ }+ }+ + public void apply (PathList path_list) {+ foreach (Path p in path_list.paths) {+ if (has_stroke ()) {+ p.stroke = get_stroke_width ();+ } else {+ p.stroke = 0;+ }+ + p.line_cap = get_line_cap ();+ p.reset_stroke ();+ p.update_region_boundaries (); + }+ }+}++}deleted file mode 100644@@ -1,181 +0,0 @@-/*- * BirdFont code from SVG Salamander- * - * Copyright (c) 2004, Mark McKay- * Copyright (c) 2014, Johan Mattsson- * All rights reserved.- *- * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following- * conditions are met:- *- * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following- * disclaimer.- * - Redistributions in binary form must reproduce the above- * copyright notice, this list of conditions and the following- * disclaimer in the documentation and/or other materials - * provided with the distribution.- *- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE- * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED- * OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Mark McKay can be contacted at mark@kitfox.com. Salamander and other- * projects can be found at http://www.kitfox.com- *- * Created on January 26, 2004, 8:40 PM- * Adapded to BirdFont on Juli 2, 2014, 5:01 PM- */--using Math;--namespace BirdFont {--/** Convert an SVG arc instruction to a Beziรฉr path. */-static void add_arc_points (BezierPoints[] bezier_points, ref int bi, double x0, double y0, double rx, double ry, double angle, bool largeArcFlag, bool sweepFlag, double x, double y) {-- //- // Elliptical arc implementation based on the SVG specification notes- //-- double dx2, dy2, cosAngle, sinAngle;- double x1, y1, Prx, Pry, Px1, Py1, radiiCheck;- double sign, sq, coef, cx1, cy1;- double sx2, sy2, cx, cy;- double ux, uy, vx, vy, p, n;- double angleStart, angleExtent;- double s, step, theta;- - // Compute the half distance between the current and the final point- dx2 = (x0 - x) / 2.0;- dy2 = (y0 - y) / 2.0;- - // Convert angle from degrees to radians- angle = 2 * PI * ((angle % 360.0) / 360.0);- - cosAngle = cos (angle);- sinAngle = sin (angle);-- //- // Step 1 : Compute (x1, y1)- //- x1 = cosAngle * dx2 + sinAngle * dy2;- y1 = -sinAngle * dx2 + cosAngle * dy2;- - // Ensure radii are large enough- rx = fabs(rx);- ry = fabs(ry);- Prx = rx * rx;- Pry = ry * ry;- Px1 = x1 * x1;- Py1 = y1 * y1;-- - // Check that radii are large enough- radiiCheck = Px1 / Prx + Py1 / Pry;- - if (radiiCheck > 1) {- rx = sqrt (radiiCheck) * rx;- ry = sqrt (radiiCheck) * ry;- Prx = rx * rx;- Pry = ry * ry;- }-- //- // Step 2 : Compute (cx1, cy1)- //- sign = (largeArcFlag == sweepFlag) ? -1 : 1;- sq = ((Prx * Pry) - (Prx * Py1) - (Pry * Px1)) / ((Prx * Py1) + (Pry * Px1));- sq = (sq < 0) ? 0 : sq;- coef = (sign * Math.sqrt(sq));- cx1 = coef * ((rx * y1) / ry);- cy1 = coef * -((ry * x1) / rx);-- //- // Step 3 : Compute (cx, cy) from (cx1, cy1)- //- - sx2 = (x0 + x) / 2.0;- sy2 = (y0 + y) / 2.0;- cx = sx2 - (cosAngle * cx1 - sinAngle * cy1);- cy = sy2 - (sinAngle * cx1 + cosAngle * cy1);-- //- // Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle)- //- - ux = (x1 - cx1) / rx;- uy = (y1 - cy1) / ry;- vx = (-x1 - cx1) / rx;- vy = (-y1 - cy1) / ry;-- // Compute the angle start- n = sqrt((ux * ux) + (uy * uy));- p = ux; // (1 * ux) + (0 * uy)- sign = (uy < 0) ? -1d : 1d;- angleStart = sign * acos(p / n);-- // Compute the angle extent- n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));- p = ux * vx + uy * vy;- sign = (ux * vy - uy * vx < 0) ? -1d : 1d;- angleExtent = sign * Math.acos(p / n);- - if(!sweepFlag && angleExtent > 0) {- angleExtent -= 2 *PI;- } else if (sweepFlag && angleExtent < 0) {- angleExtent += 2 *PI;- }- angleExtent %= 2 * PI;- angleStart %= 2 * PI;-- angleExtent *= -1;- angleStart *= -1;- - // Approximate the path with Beziรฉr points- s = (angleExtent > 0) ? 1 : -1;- step = fabs (angleExtent) / (2 * fabs (angleExtent));-- theta = PI - angleStart - angleExtent;- - bezier_points[bi].type = 'L';- bezier_points[bi].svg_type = 'a';-- bezier_points[bi].x0 = cx + rx * cos (theta);- bezier_points[bi].y0 = cy + ry * sin (theta);- - bi++;- - for (double a = 0; a < fabs (angleExtent); a += step) {- theta = PI - angleStart - angleExtent + s * a;-- return_if_fail (0 <= bi < bezier_points.length);-- bezier_points[bi].type = 'S';- bezier_points[bi].svg_type = 'a';-- bezier_points[bi].x0 = cx + rx * cos (theta);- bezier_points[bi].y0 = cy + ry * sin (theta);-- bezier_points[bi].x1 = cx + rx * cos (theta + 1 * step / 4);- bezier_points[bi].y1 = cy + ry * sin (theta + 1 * step / 4);- - bezier_points[bi].x2 = cx + rx * cos (theta + 2 * step / 4);- bezier_points[bi].y2 = cy + ry * sin (theta + 2 * step / 4);- - bi++;- }-}--}- deleted file mode 100644@@ -1,264 +0,0 @@-/*- Copyright (C) 2013 2014 Johan Mattsson-- This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version.-- This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details.-*/--using B;--namespace BirdFont {--class SvgFont : GLib.Object {- Font font;- double units = 1;- double font_advance = 0;- - public SvgFont (Font f) {- this.font = f;- }- - /** Load svg font from file. */- public void load (string path) {- string data;- XmlParser xml_parser;- try {- FileUtils.get_contents (path, out data);- xml_parser = new XmlParser (data);- parse_svg_font (xml_parser.get_root_tag ());- } catch (GLib.Error e) {- warning (e.message);- }- }- - void parse_svg_font (Tag tag) {- foreach (Tag t in tag) {- if (t.get_name () == "defs") {- parse_svg_font (t);- }- - if (t.get_name () == "font") {- parse_font_tag (t);- parse_svg_font (t);- }-- if (t.get_name () == "font-face") {- parse_font_limits (t);- }-- if (t.get_name () == "hkern") {- parse_hkern (t);- }- - if (t.get_name () == "glyph") {- parse_glyph (t);- }- } - }-- void parse_hkern (Tag tag) {- string left = "";- string right = "";- string left_name = "";- string right_name = "";- double kerning = 0;- unichar l, r;- StringBuilder sl, sr;- GlyphRange grr, grl;- KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes ();- - foreach (Attribute attr in tag.get_attributes ()) {- // left- if (attr.get_name () == "u1") {- left = attr.get_content ();- } - - // right - if (attr.get_name () == "u2") {- right = attr.get_content ();- }-- if (attr.get_name () == "g1") {- left_name = attr.get_content ();- }- - if (attr.get_name () == "g2") {- right_name = attr.get_content ();- }- - // kerning- if (attr.get_name () == "k") {- kerning = double.parse (attr.get_content ()) * units;- }- }- - // FIXME: ranges and sequences for u1 & u2 + g1 & g2- foreach (string lk in left.split (",")) {- foreach (string rk in right.split (",")) {- l = get_unichar (lk);- r = get_unichar (rk);- - sl = new StringBuilder ();- sl.append_unichar (l);- - sr = new StringBuilder ();- sr.append_unichar (r);- - try {- grl = new GlyphRange ();- grl.parse_ranges (sl.str);- - grr = new GlyphRange ();- grr.parse_ranges (sr.str);- - classes.set_kerning (grl, grr, -kerning);- } catch (MarkupError e) {- warning (e.message);- } - }- }- }-- void parse_font_limits (Tag tag) {- double top_limit = 0;- double bottom_limit = 0;- - foreach (Attribute attr in tag.get_attributes ()) {- if (attr.get_name () == "units-per-em") {- units = 100.0 / double.parse (attr.get_content ());- } - }-- foreach (Attribute attr in tag.get_attributes ()) { - if (attr.get_name () == "ascent") {- top_limit = double.parse (attr.get_content ());- }- - if (attr.get_name () == "descent") {- bottom_limit = double.parse (attr.get_content ());- } - }- - top_limit *= units;- bottom_limit *= units;- - font.bottom_limit = bottom_limit;- font.top_limit = top_limit;- }- - void parse_font_tag (Tag tag) {- foreach (Attribute attr in tag.get_attributes ()) {- if (attr.get_name () == "horiz-adv-x") {- font_advance = double.parse (attr.get_content ());- }- - if (attr.get_name () == "id") {- font.set_name (attr.get_content ());- }- }- }- - /** Obtain unichar value from either a character, a hex representation of- * a character or series of characters (f, m or ffi).- */- static unichar get_unichar (string val) {- string v = val;- unichar unicode_value;- - if (val == "&") {- return '&';- }- - // TODO: parse ligatures- if (v.has_prefix ("&")) {- // parse hex value- v = v.substring (0, v.index_of (";"));- v = v.replace ("&#x", "U+");- v = v.replace (";", "");- unicode_value = Font.to_unichar (v);- } else {- // obtain unicode value- - if (v.char_count () > 1) {- warning ("font contains ligatures");- return '\0';- }- - unicode_value = v.get_char (0);- }- - return unicode_value; - }- - bool is_ligature (string v) {- if (v.has_prefix ("&")) {- return false;- }- - return v.char_count () > 1;- }- - void parse_glyph (Tag tag) {- unichar unicode_value = 0;- string glyph_name = "";- string svg = "";- Glyph glyph;- GlyphCollection glyph_collection;- double advance = font_advance;- string ligature = "";- SvgParser parser = new SvgParser ();- StringBuilder unicode_name; -- parser.set_format (SvgFormat.INKSCAPE);-- foreach (Attribute attr in tag.get_attributes ()) {- if (attr.get_name () == "unicode") {- unicode_value = get_unichar (attr.get_content ());- - if (glyph_name == "") {- glyph_name = attr.get_content ();- }- - if (is_ligature (attr.get_content ())) {- ligature = attr.get_content ();- }- }- - // svg data- if (attr.get_name () == "d") {- svg = attr.get_content ();- }- - if (attr.get_name () == "glyph-name") {- glyph_name = attr.get_content ();- }-- if (attr.get_name () == "horiz-adv-x") {- advance = double.parse (attr.get_content ());- }- }-- unicode_name = new StringBuilder ();- unicode_name.append_unichar (unicode_value);-- glyph = new Glyph (unicode_name.str, unicode_value);- parser.add_path_to_glyph (svg, glyph, true, units); - glyph.right_limit = glyph.left_limit + advance * units;- - // FIXME: add svg font ligatures- - glyph_collection = new GlyphCollection (unicode_value, glyph_name);- glyph_collection.insert_glyph (glyph, true);- - font.add_glyph_collection (glyph_collection);- }-}--}deleted file mode 100644@@ -1,132 +0,0 @@-/*- Copyright (C) 2012, 2014 Johan Mattsson-- This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version.-- This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details.-*/--namespace BirdFont {--class SvgFontFormatWriter : GLib.Object {-- DataOutputStream os;-- public SvgFontFormatWriter () {- }-- public void open (File file) throws Error {- if (file.query_exists ()) {- throw new FileError.EXIST ("SvgFontFormatWriter: file exists.");- }- - os = new DataOutputStream (file.create(FileCreateFlags.REPLACE_DESTINATION));- }-- public void close () throws Error {- os.close ();- }- - public void write_font_file (Font font) throws Error {- string font_name = font.get_full_name ();- - int units_per_em = 100;- - int ascent = 80;- int descent = -20;- - StringBuilder b;- - Glyph? g;- Glyph glyph;- unichar index = 0;- - string uni;- - KerningClasses classes;- - put ("""<?xml version="1.0" standalone="no"?>""");- put ("""<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >""");- put ("""<svg>""");-- // (metadata goes here)-- put ("<defs>");-- put (@"<font id=\"$font_name\" horiz-adv-x=\"250\" >");- put (@"<font-face units-per-em=\"$(to_float (units_per_em))\" ascent=\"$(to_float (ascent))\" descent=\"$(to_float (descent))\" />");-- // (missing-glyph goes here)-- // regular glyphs - while (true) {- g = font.get_glyph_index (index++);- - if (g == null) {- break;- }- - glyph = (!) g;- - b = new StringBuilder ();- b.append_unichar (glyph.get_unichar ());-- if (glyph.get_unichar () >= ' ' && b.str.validate ()) {- if (b.str == "\"" || b.str == "&" || b.str == "<" || b.str == ">") {- uni = Font.to_hex_code (glyph.get_unichar ());- put (@"<glyph unicode=\"&#x$(uni);\" horiz-adv-x=\"$(to_float (glyph.get_width ()))\" d=\"$(glyph.get_svg_data ())\" />"); - } else {- put (@"<glyph unicode=\"$(b.str)\" horiz-adv-x=\"$(to_float (glyph.get_width ()))\" d=\"$(glyph.get_svg_data ())\" />");- }- }- }- - // FIXME: ligatures- classes = BirdFont.get_current_font ().get_kerning_classes ();- classes.all_pairs ((kerning) => {- string l, r;- - foreach (Kerning k in kerning.kerning) {- try {- if (k.glyph != null) {- l = Font.to_hex_code (kerning.character.unichar_code);- r = Font.to_hex_code (((!)k.glyph).unichar_code); - os.put_string (@"<hkern u1=\"&#x$l;\" u2=\"&#x$r;\" k=\"$(to_float (-k.val))\"/>\n");- } else {- warning ("No glyph.");- }- } catch (GLib.Error e) {- warning (e.message);- }- }- }); -- put ("</font>");- put ("</defs>");- put ("</svg>");- }-- string to_float (double d) {- string s = @"$d";- if (s.index_of ("e") != -1) {- return "0".dup ();- }- return s.replace (",", ".");- }-- /** Write a new line */- private void put (string line) throws Error {- os.put_string (line);- os.put_string ("\n");- }--}---}deleted file mode 100644@@ -1,2114 +0,0 @@-/*- Copyright (C) 2012 2013 2014 2015 Johan Mattsson-- This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version.-- This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details.-*/--using B;-using Math;--namespace BirdFont {--public enum SvgFormat {- NONE,- INKSCAPE,- ILLUSTRATOR-}--public enum SvgType {- COLOR,- REGULAR-}--public class SvgParser {- - SvgFormat format = SvgFormat.ILLUSTRATOR;- - public SvgParser () {- }- - public void set_format (SvgFormat f) {- format = f;- }- - public static void import (SvgType type) {- FileChooser fc = new FileChooser ();- fc.file_selected.connect ((p) => {- string path;- - if (p == null) {- return;- }- - path = (!) p;- - if (type == SvgType.REGULAR) {- import_svg (path);- } else if (type == SvgType.COLOR) {- Glyph glyph = MainWindow.get_current_glyph ();- import_color_svg (glyph, path);- }- });- - fc.add_extension ("svg");- MainWindow.file_chooser (t_("Import"), fc, FileChooser.LOAD);- }- - public static void import_color_svg (Glyph glyph, string path) {- try {- string data;- FileUtils.get_contents (path, out data);- glyph.color_svg_data = data;-- if (glyph.color_svg_data != null) {- glyph.svg_x = glyph.left_limit;- glyph.svg_y = BirdFont.get_current_font ().top_position;- }- } catch (GLib.Error e) {- warning (e.message);- }- }- - 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) {- FastPath path = new FastPath.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 FastPath) {- path_list.add (((FastPath) 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 (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 (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 (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 (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 (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 (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 FastPath) {- Path p1 = ((FastPath) o1).get_path ();- inside_count = 0;- - foreach (Object o2 in layer.objects) {- if (o2 is FastPath) {- Path p2 = ((FastPath) 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- */- 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);- }- }- - /** - * @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) {- double px = 0;- double py = 0;- double px2 = 0;- double py2 = 0;- double cx = 0;- double cy = 0;- string data;- Font font;- PathList path_list = new PathList ();- BezierPoints[] bezier_points;- string[] c;- double arc_rx, arc_ry;- double arc_rotation;- int large_arc;- int arc_sweep;- double arc_dest_x, arc_dest_y;-- font = BirdFont.get_current_font ();- - data = add_separators (d);- c = data.split (" ");- bezier_points = new BezierPoints[8 * c.length + 1]; // the arc instruction can use up to eight points- - for (int i = 0; i < 2 * c.length + 1; i++) {- bezier_points[i] = new BezierPoints ();- }- - int bi = 0;- - // 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; // if (svg_glyph) ?- - 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.");- return path_list; - }- - move_and_resize (bezier_points, bi, svg_glyph, units, glyph);- - if (format == SvgFormat.ILLUSTRATOR) {- path_list = create_paths_illustrator (bezier_points, bi);- } else {- path_list = create_paths_inkscape (bezier_points, bi);- }-- // 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);- }- - static double parse_double (string? s) {- if (is_null (s)) {- warning ("Got null instead of expected string.");- return 0;- }- - if (!is_point ((!) s)) {- warning (@"Expecting a double got: $((!) s)");- return 0;- }- - return double.parse ((!) s);- }- - static bool is_point (string? s) {- if (s == null) {- warning ("s is null");- return false;- }- - return double.try_parse ((!) s);- }- - Path parse_poly_data (string polygon_points) {- string data = add_separators (polygon_points);- string[] c = data.split (" ");- Path path;- BezierPoints[] bezier_points = new BezierPoints[c.length + 1];- int bi;- Glyph g;- EditPoint ep;- - bi = 0;- for (int i = 0; i < c.length - 1; i += 2) { - if (i + 1 >= c.length) {- warning ("No y value.");- break;- }-- if (bi >= bezier_points.length) {- warning ("End of bezier_points");- break;- }-- bezier_points[bi] = new BezierPoints ();- bezier_points[bi].type == 'L';- bezier_points[bi].x0 = parse_double (c[i]);- bezier_points[bi].y0 = -parse_double (c[i + 1]);- bi++;- }- - g = MainWindow.get_current_glyph ();- move_and_resize (bezier_points, bi, false, 1, g);- - path = new Path ();- for (int i = 0; i < bi; i++) { - ep = path.add (bezier_points[i].x0, bezier_points[i].y0);- ep.set_point_type (PointType.LINE_CUBIC);- }- - path.create_list ();- path.recalculate_linear_handles ();- - return path;- }-}--}deleted file mode 100644@@ -1,124 +0,0 @@-/*- Copyright (C) 2015 Johan Mattsson-- This library is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 3 of the - License, or (at your option) any later version.-- This library is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details.-*/--using B;-using Math;--namespace BirdFont {--public class SvgStyle {- - Gee.HashMap<string, string> style;- - 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 (Attributes attributes) {- SvgStyle s = new SvgStyle ();- - 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 ());- }- }- - 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 (); - }- }-}--}