Clone in a terminal:
Commit 1d23236
Johan Mattsson - 2013-02-08T21:33:13+00:00
1d23236a2e16c6b5e63a6af95fc959b3d018f90ejohan.mattsson.m@gmail.com

load svg fonts

Changed files (4)

Diff

diff --git a/libbirdfont/Font.vala b/libbirdfont/Font.valaindex e57ed6a..521334b 100644--- a/libbirdfont/Font.vala+++ b/libbirdfont/Font.vala@@ -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");diff --git a/libbirdfont/ImportSvg.vala b/libbirdfont/ImportSvg.valaindex 235c593..86bdc4e 100644--- a/libbirdfont/ImportSvg.vala+++ b/libbirdfont/ImportSvg.vala@@ -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;diff --git a/libbirdfont/SvgFont.vala b/libbirdfont/SvgFont.valanew file mode 100644index 0000000..81b099a--- /dev/null+++ b/libbirdfont/SvgFont.vala@@ -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, &#x6d; 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);+	}+}++}diff --git a/libbirdfont/TestCases.vala b/libbirdfont/TestCases.valaindex 5e7417f..4fdc3ab 100644--- a/libbirdfont/TestCases.vala+++ b/libbirdfont/TestCases.vala@@ -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); 		}

Contact

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