Commit 1d23236
load svg fonts
Changed files (4)
modifiedlibbirdfont/Font.valamodifiedlibbirdfont/ImportSvg.valaaddedlibbirdfont/SvgFont.valamodifiedlibbirdfont/TestCases.vala
Diff
@@ -433,8 +433,8 @@ public class Font : GLib.Object { gl = get_glyph (a); - if (gl == null) {- warning ("glyph is not parsed yet cannot add kerning");+ if (unlikely (gl == null)) {+ warning (@"glyph \"$a\" is not parsed yet cannot add kerning"); return; } @@ -737,6 +737,11 @@ public class Font : GLib.Object { glyph_cache.remove_all (); unassigned_glyphs.remove_all (); + if (path.has_suffix (".svg")) {+ font_file = path;+ loaded = parse_svg_file (path);+ }+ if (path.has_suffix (".ffi")) { font_file = path; loaded = parse_file (path);@@ -761,6 +766,13 @@ public class Font : GLib.Object { return loaded; } + private bool parse_svg_file (string path) {+ print ("parse_svg_file\n");+ SvgFont svg_font = new SvgFont (this);+ svg_font.load (path);+ return true;+ }+ private void add_thumbnail () { File f = Supplement.get_thumbnail_directory ().get_child (@"$((!) get_file_name ()).png"); Glyph? gl = get_glyph ("a");@@ -39,12 +39,14 @@ public class ImportSvg { if (Supplement.win32) { string svg; string xml;+ Glyph glyph = MainWindow.get_current_glyph ();+ xml = xml_data.replace ("id", "__"); foreach (string svg_data in xml.split ("d=\"")) { svg = svg_data.substring (0, svg_data.index_of ("\"")); if (svg.has_prefix ("M") || svg.has_prefix ("m")) {- parse_svg_data (svg);+ parse_svg_data (svg, glyph); } } } else {@@ -139,34 +141,24 @@ public class ImportSvg { private static void parse_path (Xml.Node* node) { string attr_name = ""; string attr_content;- + Glyph glyph = MainWindow.get_current_glyph ();+ for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) { attr_name = prop->name; attr_content = prop->children->content; if (attr_name == "d") {- parse_svg_data (attr_content);+ parse_svg_data (attr_content, glyph); } } } - private static void parse_svg_data (string d) {- string[] c;- string[] command;- int ci = 0;- double px = 0;- double py = 0;- double px2 = 0;- double py2 = 0;- double cx = 0;- double cy = 0;- double[] p;- int pi = 0;+ /** Add space as separator to svg data. + * @param d svg data+ */+ static string add_separators (string d) { string data = d;- Glyph glyph = MainWindow.get_current_glyph ();- Font font = Supplement.get_current_font (); - // add separators data = data.replace (",", " "); data = data.replace ("m", " m "); data = data.replace ("M", " M ");@@ -184,15 +176,41 @@ public class ImportSvg { data = data.replace ("zm", " z m "); data = data.replace ("z", " z "); data = data.replace ("-", " -");- data = data.replace ("e -", "e-"); // - can be either separator or a negative exponent+ 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;+ }+ + /** + * Add svg paths to glyph.+ * + * @param d svg data+ * @param glyph add paths to this glyph+ * @param svg_glyph parse svg glyph (origo in lower left corner)+ */+ public static void parse_svg_data (string d, Glyph glyph, bool svg_glyph = false, double units = 1) {+ string[] c;+ string[] command;+ int ci = 0;+ double px = 0;+ double py = 0;+ double px2 = 0;+ double py2 = 0;+ double cx = 0;+ double cy = 0;+ double[] p;+ int pi = 0;+ string data = add_separators (d);+ Font font = Supplement.get_current_font ();+ c = data.split (" "); p = new double[2 * c.length]; command = new string[2 * c.length];@@ -210,7 +228,12 @@ public class ImportSvg { command[ci++] = "M"; px += parse_double (c[++i]);- py += -parse_double (c[++i]);+ + if (svg_glyph) {+ py += parse_double (c[++i]);+ } else {+ py += -parse_double (c[++i]);+ } p[pi++] = px; p[pi++] = py;@@ -222,7 +245,12 @@ public class ImportSvg { command[ci++] = "M"; px = parse_double (c[++i]);- py = -parse_double (c[++i]);+ + if (svg_glyph) {+ py = parse_double (c[++i]);+ } else {+ py = -parse_double (c[++i]);+ } p[pi++] = px; p[pi++] = py;@@ -255,7 +283,11 @@ public class ImportSvg { while (is_point (c[i + 1])) { command[ci++] = "L"; - py = py + -parse_double (c[++i]);+ if (svg_glyph) {+ py = py + parse_double (c[++i]);+ } else {+ py = py + -parse_double (c[++i]);+ } p[pi++] = px; p[pi++] = py;@@ -265,7 +297,12 @@ public class ImportSvg { if (c[i] == "V") { while (is_point (c[i + 1])) { command[ci++] = "L";- py = -parse_double (c[++i]);+ + if (svg_glyph) {+ py = parse_double (c[++i]);+ } else {+ py = -parse_double (c[++i]);+ } p[pi++] = px; p[pi++] = py;@@ -277,7 +314,13 @@ public class ImportSvg { command[ci++] = "L"; cx = px + parse_double (c[++i]);- cy = py + -parse_double (c[++i]);+ + if (svg_glyph) {+ cy = py + parse_double (c[++i]);+ } else {+ cy = py + -parse_double (c[++i]);+ }+ p[pi++] = cx; p[pi++] = cy; @@ -291,7 +334,13 @@ public class ImportSvg { command[ci++] = "L"; cx = parse_double (c[++i]);- cy = -parse_double (c[++i]);+ + if (svg_glyph) {+ cy = parse_double (c[++i]);+ } else {+ cy = -parse_double (c[++i]);+ }+ p[pi++] = cx; p[pi++] = cy; @@ -305,12 +354,23 @@ public class ImportSvg { command[ci++] = "C"; cx = px + parse_double (c[++i]);- cy = py + -parse_double (c[++i]);+ + if (svg_glyph) {+ cy = py + parse_double (c[++i]);+ } else {+ cy = py + -parse_double (c[++i]);+ }+ p[pi++] = cx; p[pi++] = cy; cx = px + parse_double (c[++i]);- cy = py + -parse_double (c[++i]);+ + if (svg_glyph) {+ cy = py + parse_double (c[++i]);+ } else {+ cy = py + -parse_double (c[++i]);+ } px2 = cx; py2 = cy;@@ -319,7 +379,13 @@ public class ImportSvg { p[pi++] = py2; cx = px + parse_double (c[++i]);- cy = py + -parse_double (c[++i]);+ + if (svg_glyph) {+ cy = py + parse_double (c[++i]);+ } else {+ cy = py + -parse_double (c[++i]);+ }+ p[pi++] = cx; p[pi++] = cy; @@ -334,17 +400,35 @@ public class ImportSvg { command[ci++] = "C"; cx = parse_double (c[++i]);- cy = -parse_double (c[++i]);+ + if (svg_glyph) {+ cy = parse_double (c[++i]);+ } else {+ cy = -parse_double (c[++i]);+ }+ p[pi++] = cx; p[pi++] = cy; cx = parse_double (c[++i]);- cy = -parse_double (c[++i]);+ + if (svg_glyph) {+ cy = parse_double (c[++i]);+ } else {+ cy = -parse_double (c[++i]);+ }+ p[pi++] = cx; p[pi++] = cy; cx = parse_double (c[++i]);- cy = -parse_double (c[++i]);+ + if (svg_glyph) {+ cy = parse_double (c[++i]);+ } else {+ cy = -parse_double (c[++i]);+ }+ p[pi++] = cx; p[pi++] = cy; @@ -359,12 +443,17 @@ public class ImportSvg { // the first point is the reflection cx = 2 * px - px2;- cy = 2 * py - py2;+ cy = 2 * py - py2; // if (svg_glyph) ? p[pi++] = cx; p[pi++] = cy; cx = px + parse_double (c[++i]);- cy = py + -parse_double (c[++i]);+ + if (svg_glyph) {+ cy = py + parse_double (c[++i]);+ } else {+ cy = py + -parse_double (c[++i]);+ } px2 = cx; py2 = cy;@@ -373,7 +462,13 @@ public class ImportSvg { p[pi++] = py2; cx = px + parse_double (c[++i]);- cy = py + -parse_double (c[++i]);+ + if (svg_glyph) {+ cy = py + parse_double (c[++i]);+ } else {+ cy = py + -parse_double (c[++i]);+ }+ p[pi++] = cx; p[pi++] = cy; @@ -388,13 +483,18 @@ public class ImportSvg { // the reflection again cx = 2 * px - px2;- cy = 2 * py - py2;+ cy = 2 * py - py2; // if (svg_glyph) ? p[pi++] = cx; p[pi++] = cy; // the other two are regular cubic points cx = parse_double (c[++i]);- cy = -parse_double (c[++i]);+ + if (svg_glyph) {+ cy = parse_double (c[++i]);+ } else {+ cy = -parse_double (c[++i]);+ } px2 = cx; py2 = cy;@@ -403,7 +503,13 @@ public class ImportSvg { p[pi++] = py2; cx = parse_double (c[++i]);- cy = -parse_double (c[++i]);+ + if (svg_glyph) {+ cy = parse_double (c[++i]);+ } else {+ cy = -parse_double (c[++i]);+ }+ p[pi++] = cx; p[pi++] = cy; @@ -424,10 +530,19 @@ public class ImportSvg { Path path = new Path (); // move- for (int i = 0; i < pi; i += 2) {- p[i] += glyph.left_limit;- p[i+1] -= font.top_position;- } + if (svg_glyph) {+ for (int i = 0; i < pi; i += 2) {+ p[i] *= units; // TODO: test it.+ p[i] += glyph.left_limit;+ //p[i+1] += font.top_position;+ p[i+1] -= font.base_line; // FIXME: is the font at the baseline?+ }+ } else {+ for (int i = 0; i < pi; i += 2) {+ p[i] += glyph.left_limit;+ p[i+1] -= font.top_position;+ }+ } // add points int ic = 0;new file mode 100644@@ -0,0 +1,233 @@+/*+ Copyright (C) 2013 Johan Mattsson++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program 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 General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+*/++using Xml;++namespace Supplement {++class SvgFont : GLib.Object {+ Font font;+ double units = 1;+ + public SvgFont (Font f) {+ this.font = f;+ }+ + public void load (string path) {+ Xml.Doc* doc;+ Xml.Node* root = null;+ + Parser.init ();+ + doc = Parser.parse_file (path);+ root = doc->get_root_element ();+ return_if_fail (root != null);+ parse_svg_font (root);++ delete doc;+ Parser.cleanup ();+ }+ + void parse_svg_font (Xml.Node* node) {+ print ("parse_svg_font" + node->name + "\n");+ for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {+ print (iter->name + "\n");+ + if (iter->name == "defs") {+ parse_svg_font (iter);+ }+ + if (iter->name == "font") {+ parse_font_name (iter);+ parse_svg_font (iter);+ }++ if (iter->name == "font-face") {+ parse_font_limits (iter);+ }++ if (iter->name == "hkern") {+ parse_hkern (iter);+ }+ + if (iter->name == "glyph") {+ parse_glyph (iter);+ }+ } + }++ void parse_hkern (Xml.Node* node) {+ string left = "";+ string right = "";+ double kerning = 0;+ unichar l, r;+ StringBuilder sl, sr;+ string attr_name = "";+ string attr_content;+ + for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {+ attr_name = prop->name;+ attr_content = prop->children->content;+ + print (attr_name + "\n");+ + // left+ if (attr_name == "u1") {+ left = attr_content;+ } + + // right + if (attr_name == "u2") {+ right = attr_content;+ }+ + // kerning+ if (attr_name == "k") {+ kerning = double.parse (attr_content);+ }+ + // TODO: ranges and sequences for u1 & u2 + g1 & g2+ }+ + print ("L " + left + "\n");+ + l = get_unichar (left);+ r = get_unichar (right);+ + sl = new StringBuilder ();+ sl.append_unichar (l);+ + print ("L " + sl.str + "\n");+ + sr = new StringBuilder ();+ sr.append_unichar (r);+ + font.set_kerning (sl.str, sr.str, -kerning);+ }++ void parse_font_limits (Xml.Node* node) {+ string attr_name = "";+ string attr_content;+ double top_limit = 0;+ double bottom_limit = 0;+ + for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {+ attr_name = prop->name;+ attr_content = prop->children->content;+ + if (attr_name == "units-per-em") {+ units = 100.0 / double.parse (attr_content);+ } + + if (attr_name == "ascent") {+ top_limit = double.parse (attr_content);+ }+ + if (attr_name == "descent") {+ bottom_limit = double.parse (attr_content);+ } + }+ + top_limit *= units;+ bottom_limit *= units;+ + font.bottom_limit = bottom_limit;+ font.top_limit = top_limit;+ }+ + void parse_font_name (Xml.Node* node) {+ string attr_name = "";+ string attr_content;+ + for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {+ attr_name = prop->name;+ attr_content = prop->children->content;+ + if (attr_name == "id") {+ font.set_name (attr_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;+ + // 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+ unicode_value = v.get_char (0);+ }+ + return unicode_value; + }+ + void parse_glyph (Xml.Node* node) {+ string attr_name = "";+ string attr_content;+ unichar unicode_value = 0;+ string glyph_name = "";+ string svg = "";+ Glyph glyph;+ double advance = 100;++ for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) {+ attr_name = prop->name;+ attr_content = prop->children->content;++ if (attr_name == "unicode") {+ unicode_value = get_unichar (attr_content);+ + if (glyph_name == "") {+ glyph_name = attr_content;+ }+ }+ + // svg data+ if (attr_name == "d") {+ svg = attr_content;+ }+ + if (attr_name == "glyph-name") {+ glyph_name = attr_content;+ }++ if (attr_name == "horiz-adv-x") {+ advance = double.parse (attr_content);+ print (@"horiz-adv-x $advance\n");+ }+ }+ + glyph = new Glyph (glyph_name, unicode_value);+ ImportSvg.parse_svg_data (svg, glyph, true, units);+ + glyph.right_limit = glyph.left_limit + advance * units;+ + font.add_glyph_callback (glyph);+ }+}++}@@ -152,7 +152,7 @@ class TestCases { temp_file.delete (); - MainWindow.get_toolbox ().select_tool_by_name ("full_glyph");+ Toolbox.select_tool_by_name ("full_glyph"); } catch (GLib.Error e) { warning (e.message); }