Clone in a terminal:
Commit 314b9a4
Johan Mattsson - 2013-03-08T19:38:37+00:00
314b9a4e7bccaa87d38b089d95789ed0eb0e6075johan.mattsson.m@gmail.com

use .bf as intermediate format in freetype parser

Changed files (5)

Diff

diff --git a/libbirdfont/BirdFontFile.vala b/libbirdfont/BirdFontFile.valaindex a9b9aad..8f7f47a 100644--- a/libbirdfont/BirdFontFile.vala+++ b/libbirdfont/BirdFontFile.vala@@ -30,6 +30,65 @@ class BirdFontFile { 		font = f; 	} +	/** Load a new .bf file.+	 * @param path path to a valid .bf file+	 */+	public bool load (string path) {+		bool ok = true;+		Xml.Doc* doc;+		Xml.Node* root;+		+		Parser.init ();++		font.font_file = path; // this will be updated if we are loading a backup file++		doc = Parser.parse_file (path);+		root = doc->get_root_element ();+		+		if (root == null) {+			warning ("no root element");+			delete doc;+			return false;+		}++		create_background_files (root);+		ok = parse_file (root);++		delete doc;+		Parser.cleanup ();+		+		return ok;+	}++	/** Load a new .bf file.+	 * @param xml_data data for a valid .bf file+	 */+	public bool load_data (string xml_data) {+		bool ok = true;+		Xml.Doc* doc;+		Xml.Node* root;+		+		Parser.init ();+		+		font.font_file = "typeface.bf"; // FIXME+		doc = Parser.parse_doc (xml_data);+		root = doc->get_root_element ();+		+		if (root == null) {+			warning ("no root element");+			delete doc;+			return false;+		}++		create_background_files (root);+		ok = parse_file (root);++		delete doc;+		Parser.cleanup ();+		+		return ok;+	}+ 	public bool write_font_file (string path, bool backup = false) { 		try { 			File file = File.new_for_path (path);@@ -326,41 +385,8 @@ class BirdFontFile { 		}			 	} -	public bool load (string path) {-		try {-			return parse_file (path);-		} catch (GLib.Error e) {-			warning (e.message);-		}-		return false;-	}--	private bool parse_file (string path) throws GLib.Error {-		Parser.init ();-		-		Xml.Doc* doc;-		Xml.Node* root;-		Xml.Node* node;--		// set this path as file for this font, it will be updated if this is a backup-		font.font_file = path;--		while (font.background_images.length () > 0) {-			font.background_images.remove_link (font.background_images.first ());-		}--		create_background_files (path);--		doc = Parser.parse_file (path);-		root = doc->get_root_element ();-		-		if (root == null) {-			warning ("no root element");-			delete doc;-			return false;-		}--		node = root;+	private bool parse_file (Xml.Node* root) {+		Xml.Node* node = root; 		 		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) { @@ -403,28 +429,16 @@ class BirdFontFile { 				parse_kerning (iter); 			} 		}-    -		delete doc;-		Parser.cleanup ();  		return true; 	} 	-	private void create_background_files (string path) {-		Xml.Doc* doc = Parser.parse_file (path);-		Xml.Node* root;-		Xml.Node* node;-		-		root = doc->get_root_element ();-		-		if (root == null) {-			delete doc;-			return;+	private void create_background_files (Xml.Node* root) requires (root != null) {+		while (font.background_images.length () > 0) {+			font.background_images.remove_link (font.background_images.first ()); 		}--		node = root;-		-		for (Xml.Node* iter = node->children; iter != null; iter = iter->next) {+	+		for (Xml.Node* iter = root->children; iter != null; iter = iter->next) { 			 			if (iter->name == "name" && iter->children != null) { 				font.set_name (iter->children->content);@@ -434,9 +448,6 @@ class BirdFontFile { 				parse_background_image (iter); 			} 		}-    -		delete doc;-		Parser.cleanup (); 	} 	 	private void parse_kerning (Xml.Node* node) {diff --git a/libbirdfont/Font.vala b/libbirdfont/Font.valaindex 25a5895..9aee0c9 100644--- a/libbirdfont/Font.vala+++ b/libbirdfont/Font.vala@@ -597,7 +597,7 @@ public class Font : GLib.Object { 		} 		 		if (path.has_suffix (".ttf")) {-			Toolbox.select_tool_by_name ("quadratic_points");+			Toolbox.select_tool_by_name ("double_points"); 			font_file = path; 			loaded = parse_freetype_file (path); 			format = FontFormat.FREETYPE;@@ -632,10 +632,11 @@ public class Font : GLib.Object { 	}  	private bool parse_freetype_file (string path) {-		string svg;+		string font_data; 		StringBuilder? data; 		int error;-		SvgFont svg_loader = new SvgFont (this);+		bool parsed;+		BirdFontFile bf_font = new BirdFontFile (this); 		 		data = load_freetype_font (path, out error); 		@@ -649,10 +650,14 @@ public class Font : GLib.Object { 			return false; 		} 		-		svg = ((!) data).str;-		svg_loader.load_svg_data (svg);--		return true;+		font_data = ((!) data).str;+		parsed = bf_font.load_data (font_data);+		+		if (parsed) {+			Preferences.add_recent_files (path);+		}+		+		return parsed; 	}  	private bool parse_svg_file (string path) {diff --git a/libbirdfont/OpenFontFormat/load_font.c b/libbirdfont/OpenFontFormat/load_font.cnew file mode 100644index 0000000..2afb688--- /dev/null+++ b/libbirdfont/OpenFontFormat/load_font.c@@ -0,0 +1,724 @@+/*+    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/>.+*/++#include <assert.h>+#include <glib.h>+#include <stdio.h>+#include <string.h>+#include <stdlib.h>+#include <ft2build.h>+#include FT_FREETYPE_H+#include FT_GLYPH_H+#include FT_OPENTYPE_VALIDATE_H+#include FT_TRUETYPE_TABLES_H++/** Error codes. */+#define OK 0++/** Point flags. */+#define QUADRATIC_OFF_CURVE 0+#define ON_CURVE 1+#define CUBIC_CURVE 2+#define DOUBLE_CURVE 4+#define HIDDEN_CURVE 8++/** Convert units per em in font file format to the BirdFont format. */+double get_units (double units_per_em) {+	units_per_em *= (1000.0 / units_per_em) * (10.0 / 3.0);+	return 100.0 / units_per_em;+}++/** @return a non zero value if the point is a part of two off curve + * points with a hidden on curve point in between.+ */+guint is_double_curve (char flag) {+	return flag & DOUBLE_CURVE;+}++/** @return a non zero value if the point is a cubic off curve point. */+guint is_cubic (char flag) {+	return flag & CUBIC_CURVE && (flag & ON_CURVE) == 0;+}++/** @return a non zero value if the point is a quadratic off curve point. */+guint is_quadratic (char flag) {+	return (flag & CUBIC_CURVE) == 0 && (flag & ON_CURVE) == 0;+}++/** @return a non zero value if the point is a on curve point */+guint is_line (char flag) {+	return flag & ON_CURVE;+}++/** @return a non zero value if the point is a hidden on curve point */+guint is_hidden (char flag) {+	return flag & HIDDEN_CURVE;+}++/** Convert every second hidden curve to a double curve and keep the + * other points as on curve points.+ */ +void set_double_curves (char* flag, int length) {+	int i;+	guint double_curve = FALSE;+	+	for (i = 1; i < length; i++) {+		if (is_line (flag[i])) {+			double_curve = FALSE;+		} else if (is_hidden (flag[i])) {+			if (!double_curve) {+				if (is_quadratic (flag[i - 1]) && is_quadratic (flag[i + 1])) {+					flag[i - 1] = DOUBLE_CURVE;+					flag[i] = HIDDEN_CURVE;+					flag[i + 1] = DOUBLE_CURVE;+					double_curve = TRUE;+				} else {+					flag[i] = ON_CURVE;+					double_curve = FALSE;+				}+			} else {+				flag[i] = ON_CURVE;+				double_curve = FALSE;+			} +		}+		// ignore off curve points+	}+}++/** @return half the distance between two points. */+double half_way (double prev, double current) {+	return prev + ((current - prev) / 2.0);+} ++/** Remove hidden points.+ * @return length after removal+ */+double remove_hidden_points (FT_Vector* points, char* flags, guint length, guint capacity) {+	int k;+	int l;+	+	l = 0;+	for (k = 0; k < length; k++) {+		if (!is_hidden (flags[k])) {+			points[l] = points[k];+			flags[l] = flags[k];+			l++;+		}+	}+	+	for (k = l; k < capacity; k++) {+		points[l].x = 0;+		points[l].y = 0;+		flags[l] = 0;	+	}++	return l;+}	++/** Add extra point where two ore more off curve points follow each other. */+void create_contour (guint unicode, FT_Vector* points, char* flags, int* length, FT_Vector** new_points, char** new_flags, int* err) {+	int i;+	int j;+	guint prev_is_curve;+	guint first_is_curve = FALSE;+	guint first_normal_off_curve = FALSE;+	double x = 0;+	double y = 0;+	FT_Vector* p;+	char* f;+	int len = *length;+	+	*new_points = malloc (4 * len * sizeof (FT_Vector));+	*new_flags = malloc (4 * len * sizeof (char));+	+	p = *new_points;+	f = *new_flags;+	+	for (i = 0; i < 4 * len; i++) {+		p[i].x = 0;+		p[i].y = 0;+		f[i] = 0;+	}+	+	if (len == 0) {+		return;+	}+	+	prev_is_curve = is_quadratic (flags[len - 1]);++	j = 0;+	i = 0;+	+	if (length > 2 && is_quadratic (flags[0]) && is_quadratic (flags[1])) {+		p[j].x = half_way (points[0].x, points[1].x);+		p[j].y = half_way (points[0].y, points[1].y);+						+		f[j] = ON_CURVE;+		prev_is_curve = FALSE;+		first_is_curve = TRUE;+		j++;+		i++;		+	} +	+	if (length > 2 && is_quadratic (flags[0]) && !is_quadratic (flags[1])) { // first point is of curve but it is not part of a double curve+		first_normal_off_curve = TRUE;+	}+	+	while (i < len) {+		if (is_quadratic (flags[i])) {+			if (prev_is_curve && j != 0) {+				// add a new point if half way between two off curve points+				x = half_way (points[i].x, p[j - 1].x);+				y = half_way (points[i].y, p[j - 1].y);++				p[j].x = x;+				p[j].y = y;+				+				f[j] = HIDDEN_CURVE;++				j++;+			}+			+			f[j] = QUADRATIC_OFF_CURVE;+			prev_is_curve = TRUE;+		} else if (is_line (flags[i])) {+			prev_is_curve = FALSE;+			f[j] = ON_CURVE;+		} else if (is_cubic (flags[i])) {+			prev_is_curve = FALSE;+			f[j] = CUBIC_CURVE;+		} else {+			fprintf (stderr, "WARNING invalid point flags: %d index: %d.\n", flags[i], i);+			prev_is_curve = FALSE;+			f[j] = ON_CURVE;+		}+		+		p[j] = points[i];+		j++;+		i++;+	}++	// last to first point+	if (first_is_curve && !prev_is_curve && is_quadratic (flags[i])) {+		p[j] = points[i];+		f[j] = QUADRATIC_OFF_CURVE;+		j++;+		i++;+		+		p[j].x = half_way (p[j - 1].x, points[0].x);+		p[j].y = half_way (p[j - 1].y, points[0].y);					+		f[j] = ON_CURVE; // FIXME+		prev_is_curve = FALSE;+		j++;+		i++;++		p[j] = points[0];+		f[j] = QUADRATIC_OFF_CURVE;+		j++;+		i++;+						+		p[j] = p[0];+		f[j] = f[0];+		j++;+	} else if (first_is_curve && !prev_is_curve && is_line (flags[i])) {+		p[j] = points[i];+		f[j] = ON_CURVE;+		j++;+		i++;	++		p[j] = points[0];+		f[j] = QUADRATIC_OFF_CURVE;+		j++;+		i++;+		+		p[j] = p[0];+		f[j] = f[0];+		j++;+	} else if (first_is_curve && prev_is_curve && is_quadratic (flags[i])) {+		x = half_way (p[j - 1].x, points[i].x);+		y = half_way (p[j - 1].y, points[i].y);+		p[j].x = x;+		p[j].y = y;+		f[j] = HIDDEN_CURVE;+		j++;++		p[j] = points[i];+		f[j] = flags[i];+		j++;+		i++;++		p[j].x = half_way (p[j - 1].x, points[0].x);+		p[j].y = half_way (p[j - 1].y, points[0].y);					+		f[j] = HIDDEN_CURVE;+		prev_is_curve = FALSE;+		+		j++;+		i++;++		p[j] = points[0];+		f[j] = QUADRATIC_OFF_CURVE;+		j++;+		+		p[j] = p[0];+		f[j] = ON_CURVE;+		j++;++		prev_is_curve = TRUE;+	} else if (prev_is_curve && (flags[0] & ON_CURVE) == 0) {+		if (is_quadratic (f[j - 1]) && is_quadratic (flags[i])) {+			x = half_way (p[j - 1].x, points[i].x); +			y = half_way (p[j - 1].y, points[i].y);+			p[j].x = x;+			p[j].y = y;+			f[j] = HIDDEN_CURVE;+			j++;+		}++		p[j] = points[i];+		if (is_line (flags[i])) {+			f[j] = ON_CURVE;+		} else {+			f[j] = QUADRATIC_OFF_CURVE;+		}+		j++;		++		if (is_quadratic (f[0]) && is_quadratic (flags[0])) {+			x = half_way (p[j - 1].x, points[0].x);+			y = half_way (p[j - 1].y, points[0].y);+			p[j].x = x;+			p[j].y = y;+			f[j] = HIDDEN_CURVE;+			j++;+		}+		+		x = points[0].x;+		y = points[0].y;++		p[j].x = x;+		p[j].y = y;++		f[j] = QUADRATIC_OFF_CURVE;+		j++;		+	} else if (prev_is_curve && is_quadratic (flags[i])) {+		x = p[j - 1].x + ((points[i].x - p[j - 1].x) / 2.0);+		y = p[j - 1].y + ((points[i].y - p[j - 1].y) / 2.0);+		p[j].x = x;+		p[j].y = y;+		f[j] = HIDDEN_CURVE;+		j++;++		p[j] = points[i];+		f[j] = QUADRATIC_OFF_CURVE;+		j++;+		i++;++		if (is_quadratic (f[0])) {+			x = half_way (p[j - 1].x, points[i].x);+			y = half_way (p[j - 1].y, points[i].y);+			p[j].x = x;+			p[j].y = y;+			f[j] = HIDDEN_CURVE;+			j++;+			+			p[j] = p[0];+			f[j] = f[0];+			j++;+		} else { +			p[j] = p[0];+			f[j] = f[0];+			j++;+		}+		+		prev_is_curve = TRUE;+	} else {+		p[j] = points[i];+		if (is_quadratic (flags[i])) +			f[j] = QUADRATIC_OFF_CURVE;+		else +			f[j] = ON_CURVE;+		j++;++		p[j] = p[0];+		if (is_quadratic (flags[i])) +			f[j] = QUADRATIC_OFF_CURVE;+		else +			f[j] = ON_CURVE;+		j++;+	}++	set_double_curves (f, j);+	*length = remove_hidden_points (p, f, j, 2 * len);+}++/** Get path data in .bf format. See BirdFontFile.get_point_data () for + * format specification.+ */+GString* get_bf_contour_data (guint unicode, FT_Vector* points, char* flags, int length, double units_per_em, int* err) {+	GString* bf = g_string_new ("");+	GString* contour;+	int i = 0;+	FT_Vector* new_points;+	char* new_flags;+	double x0, x1, x2;+	double y0, y1, y2;+	double units = get_units (units_per_em);+	guint prev_is_curve;+	+	if (length == 0) {+		return bf;+	}+	+	create_contour (unicode, points, flags, &length, &new_points, &new_flags, err);+	+	x0 = new_points[0].x * units;+	y0 = new_points[0].y * units;+	g_string_printf (bf, "S %f,%f", x0, y0); // FIXME: ???++	i = 0;+	while (i < length) {+		contour = g_string_new ("");+		+		if (is_hidden (new_flags[i])) {+			// Skip it+			g_string_printf (contour, "");+			i++;+		} else if (is_cubic (new_flags[i])) {+			x0 = new_points[i].x * units;+			y0 = new_points[i].y * units;+			x1 = new_points[i+1].x * units;+			y1 = new_points[i+1].y * units;+			x2 = new_points[i+2].x * units;+			y2 = new_points[i+2].y * units;+			g_string_printf (contour, " C %f,%f %f,%f %f,%f", x0, y0, x1, y1, x2, y2);+			i += 3;+		} else if (is_double_curve (new_flags[i])) {+			x0 = new_points[i].x * units;+			y0 = new_points[i].y * units;+			x1 = new_points[i+1].x * units;+			y1 = new_points[i+1].y * units;+			x2 = new_points[i+2].x * units;+			y2 = new_points[i+2].y * units;+			g_string_printf (contour, " D %f,%f %f,%f %f,%f", x0, y0, x1, y1, x2, y2);+			i += 3;+		} else if (is_quadratic (new_flags[i])) {+			x0 = new_points[i].x * units;+			y0 = new_points[i].y * units;+			x1 = new_points[i+1].x * units;+			y1 = new_points[i+1].y * units;			+			g_string_printf (contour, " Q %f,%f %f,%f", x0, y0, x1, y1);+			i += 2;+		} else if (is_line (new_flags[i])) {+			x0 = new_points[i].x * units;+			y0 = new_points[i].y * units;+			g_string_printf (contour, " L %f,%f", x0, y0);+			i += 1;+		} else {+			g_string_printf (contour, "");+			fprintf (stderr, "WARNING Can not parse outline.\n");+			*err = 1;+			i++;+		}+		+		g_string_append (bf, contour->str);+		g_string_free (contour, 0);+	}+	+	free (new_points);+	free (new_flags);+	+	return bf;+}++/** Get path element in .bf format. */+GString* get_bf_path (guint unicode, FT_Face face, double units_per_em, int* err) {+	GString* bf = g_string_new ("");+	GString* contour;+	FT_Error error;+	int i;+	int start;+	int end;+	+	if (face->glyph->outline.n_points == 0) {+		return bf;			+	}++	start = 0;+	for (i = 0; i < face->glyph->outline.n_contours; i++) {+		end = face->glyph->outline.contours [i];+		contour = get_bf_contour_data (unicode, face->glyph->outline.points + start, face->glyph->outline.tags + start, end - start, units_per_em, err);+		g_string_append_printf (bf, "\t\t<path data=\"%s\" />\n", contour->str);+		g_string_free (contour, 0);+		start = face->glyph->outline.contours [i] + 1;+	}++	return bf;+}++/** Get char code for a glyph.+ * @gid glyph id+ * @return character code+ */+FT_ULong get_charcode (FT_Face face, FT_UInt gid) {+	FT_ULong charcode;+	FT_UInt gindex;+	+	// TODO: find the lookup function in freetype+	+	charcode = FT_Get_First_Char (face, &gindex);+	while (gindex != 0) {+		charcode = FT_Get_Next_Char (face, charcode, &gindex);+		if (gindex == gid) {+			return charcode;			+		}+	}+	+	return 0;+}++/** Obtain kerning data in .bf format for one glyph.+ * @param gid glyph id+ */+GString* get_kerning_data (FT_Face face, FT_Long gid) {+	FT_Error error;+	FT_Vector kerning;+	GString* bf = g_string_new ("");+	FT_ULong char_left;+	FT_ULong char_right;+	FT_Long right;+	+	char_left = get_charcode (face, gid);+	+	if (char_left <= 31) {+		fprintf (stderr, "Ignoring kerning for control character.\n");+		return bf;+	}+	+	if (char_left == 0) {+		fprintf (stderr, "No character code could be found for left kerning value.\n");+		return bf;+	}+	+	for (right = 0; right < face->num_glyphs; right++) {+		error = FT_Get_Kerning (face, gid, right, FT_KERNING_UNSCALED, &kerning);+		if (error) {+			fprintf (stderr, "Failed to obtain kerning value.\n");+			break;+		}+		+		char_right = get_charcode (face, right);+		if (char_left == 0) {+			fprintf (stderr, "No character code could be found for right kerning value.\n");+			return bf;+		}++		if (kerning.x > 0 && char_left > 31) {+			g_string_append_printf (bf, "<hkern left=\"U+%x;\" right=\"U+%x;\" kerning=\"%d\"/>\n", char_left, char_right, kerning.x);+		}+	}+	+	return bf;+}++/** Convert font to bf format.+ * @param face freetype font face+ * @param err error code+ * @return xml representation of a bf font+ */+GString* get_bf_font (FT_Face face, char* file, int* err) {+	GString* bf = g_string_new ("");+	GString* bf_data;+	GString* hkern;+	GString* glyph;+	FT_Error error;+	FT_Long i;+	FT_ULong charcode;+	double units_per_em;+	double units;+	+	*err = OK;++	units_per_em = face->units_per_EM;+	units = get_units (units_per_em);+	+	g_string_append (bf, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n");+	+	// libxml2 fails on doctype declaration in windows.+		+	// TODO: load from font+	g_string_append (bf, "<font>\n");++	g_string_append_printf (bf, "<name>%s</name>\n", face->family_name);+	g_string_append_printf (bf, "<backup>%s</backup>\n", file);++	g_string_append_printf (bf, "<lines>\n");+	g_string_append_printf (bf, "\t<top_limit>-66</top_limit>\n");+	g_string_append_printf (bf, "\t<top_position>%f</top_position>\n", -face->ascender * units);+	g_string_append_printf (bf, "\t<x-height>-38</x-height>\n");+	g_string_append_printf (bf, "\t<base_line>0</base_line>\n");+	g_string_append_printf (bf, "\t<bottom_position>%f</bottom_position>\n", -face->descender * units);+	g_string_append_printf (bf, "\t<bottom_limit>45</bottom_limit>\n");		+	g_string_append_printf (bf, "</lines>\n");++	// glyph outlines+	for (i = 0; i < face->num_glyphs; i++) {+		error = FT_Load_Glyph (face, i, FT_LOAD_DEFAULT);+		if (error) {+			fprintf (stderr, "Freetype failed to load glyph %d.\n", (int)i);+			fprintf (stderr, "FT_Load_Glyph error %d\n", error);+			*err = error;+			return bf;+		}++		if (face->glyph->format != ft_glyph_format_outline) {+			fprintf (stderr, "Freetype error no outline found in glyph.\n");+			*err = 1;+			return bf;+		}++		charcode = get_charcode (face, i);+		glyph = g_string_new ("");+						+		if (charcode > 31) { // if not control character+			g_string_append_printf (glyph, "<collection unicode=\"U+%x\">\n", (guint)charcode);+			g_string_append_printf (glyph, "\t<glyph left=\"%f\" right=\"%f\" selected=\"true\">\n", 0.0, face->glyph->metrics.horiAdvance * units);++			bf_data = get_bf_path (charcode, face, units_per_em, err);+			g_string_append (glyph, bf_data->str);	+			+			g_string_append (glyph, "\t</glyph>\n");+			g_string_append_printf (glyph, "</collection>\n");+		}++		g_string_append (bf, glyph->str);+		g_string_free (glyph, 0);		+		bird_font_progress_bar_set_progress ((double)i / face->num_glyphs);+	}++	// kerning+	for (i = 0; i < face->num_glyphs; i++) {+		hkern = get_kerning_data (face, i);+		g_string_append (bf, hkern->str);+		g_string_free (hkern, 0);+		bird_font_progress_bar_set_progress ((double)(face->num_glyphs - i) / face->num_glyphs);+	}++	g_string_append (bf, "</font>\n");+	+	return bf;+}++int validate_font (FT_Face face) {+	// these tables can be validated+	const FT_Byte* BASE_table = NULL; +	const FT_Byte* GDEF_table = NULL;+	const FT_Byte* GPOS_table = NULL;+	const FT_Byte* GSUB_table = NULL;+	const FT_Byte* JSTF_table = NULL;+	int error;++	error = FT_OpenType_Validate (face, FT_VALIDATE_BASE | FT_VALIDATE_GDEF | FT_VALIDATE_GPOS | FT_VALIDATE_GSUB | FT_VALIDATE_JSTF, &BASE_table, &GDEF_table, &GPOS_table, &GSUB_table, &JSTF_table);++	if (error) {+		fprintf (stderr, "Freetype validation error %d\n", error);+		return error;+	} +	+	FT_OpenType_Free (face, BASE_table);+	FT_OpenType_Free (face, GDEF_table);+	FT_OpenType_Free (face, GPOS_table);+	FT_OpenType_Free (face, GSUB_table);+	FT_OpenType_Free (face, JSTF_table);+	+	return error;+}++/** Load typeface with freetype2 and return the result as a bf font. + *  Parameter err will be set to non zero vaule if an error occurs.+ */+GString* load_freetype_font (char* file, int* err) {+	GString* bf = NULL;++	FT_Library library;+	FT_Face face;+	int error;+	FT_Glyph glyph;+	FT_UInt glyph_index;++	error = FT_Init_FreeType (&library);+	if (error != OK) {+		fprintf (stderr, "Freetype init error %d.\n", error);+		*err = error;+		return bf;+	}++	error = FT_New_Face (library, file, 0, &face);+	if (error) {+		fprintf (stderr, "Freetype font face error %d\n", error);+		*err = error;+		return bf;+	}++	error = FT_Set_Char_Size (face, 0, 800, 300, 300);+	if (error) {+		fprintf (stderr, "Freetype FT_Set_Char_Size failed, error: %d.\n", error);+		*err = error;+		return bf;+	}++	bf = get_bf_font (face, file, &error);+	if (error != OK) {+		fprintf (stderr, "Failed to parse font.\n");+		*err = error;+		return bf;	+	}++	FT_Done_Face ( face );+	FT_Done_FreeType( library );+	+	*err = OK;+	return bf;+}++/** Validate font a font with FreeType */+guint validate_freetype_font (char* file) {+	FT_Library library;+	FT_Face face;+	int error;+	+	error = FT_Init_FreeType (&library);+	if (error != OK) {+		fprintf (stderr, "Freetype init error %d\n", error);+		return FALSE;+	}++	error = FT_New_Face (library, file, 0, &face);+	if (error) {+		fprintf (stderr, "Freetype font face error %d\n", error);+		return FALSE;+	}+		+	error = validate_font (face);+	if (error) {+		fprintf (stderr, "Validation failed.\n", error);+		return FALSE;+	}++	FT_Done_Face ( face );+	FT_Done_FreeType( library );+	+	return TRUE;+}diff --git a/libbirdfont/SvgFont.vala b/libbirdfont/SvgFont.valaindex 544af18..773c75b 100644--- a/libbirdfont/SvgFont.vala+++ b/libbirdfont/SvgFont.vala@@ -30,32 +30,16 @@ class SvgFont : GLib.Object { 	 	/** Load svg font from file. */ 	public void load (string path) {-		load_font_data (path, true);-	}--	/** Parse svg data. */-	public void load_svg_data (string path) {-		load_font_data (path, false);-	}-	-	void load_font_data (string d, bool file) { 		Xml.Doc* doc; 		Xml.Node* root = null; 		 		Parser.init ();-		-		if (file) {-			doc = Parser.parse_file (d);-		} else {-			doc = Parser.parse_doc (d);-		}-		+		doc = Parser.parse_file (path); 		root = doc->get_root_element (); 		return_if_fail (root != null); 		parse_svg_font (root);- 		delete doc;-		Parser.cleanup ();		+		Parser.cleanup ();	 	} 	 	void parse_svg_font (Xml.Node* node) {diff --git a/libbirdfont/load_svg_font.c b/libbirdfont/load_svg_font.cdeleted file mode 100644index eac3a76..0000000--- a/libbirdfont/load_svg_font.c+++ /dev/null@@ -1,482 +0,0 @@-/*-    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/>.-*/--#include <assert.h>-#include <glib.h>-#include <stdio.h>-#include <string.h>-#include <stdlib.h>-#include <ft2build.h>-#include FT_FREETYPE_H-#include FT_GLYPH_H-#include FT_OPENTYPE_VALIDATE_H-#include FT_TRUETYPE_TABLES_H--//#include "birdfont.h"--/** Error codes. */-#define OK 0--/** Point flags. */-#define ON_CURVE 1-#define CUBIC_CURVE 2-#define QUADRATIC_OFF_CURVE 0--guint is_cubic (char flag) {-	return flag & CUBIC_CURVE && (flag & ON_CURVE) == 0;-}--guint is_quadratic (char flag) {-	return (flag & CUBIC_CURVE) == 0 && (flag & ON_CURVE) == 0;-}--guint is_line (char flag) {-	return flag & ON_CURVE;-}--/** Add extra point where two ore more off curve points follow each other. */-void create_contour (FT_Vector* points, char* flags, int* length, FT_Vector** new_points, char** new_flags, int* err) {-	int i;-	int j;-	guint prev_is_curve;-	double x = 0;-	double y = 0;-	FT_Vector* p;-	char* f;-	int len = *length;-	-	*new_points = malloc (4 * len * sizeof (FT_Vector));-	*new_flags = malloc (4 * len * sizeof (char));-	-	p = *new_points;-	f = *new_flags;-	-	for (i = 0; i < 4 * len; i++) {-		p[i].x = 0;-		p[i].y = 0;-		f[i] = 0;-	}-	-	if (len == 0) {-		return;-	}-	-	prev_is_curve = is_quadratic (flags[len - 1]);-	-	if (is_quadratic (flags[0])) {-		fprintf (stderr, "WARNING: path begins at off curve point.\n");-	} -	-	j = 0;-	for (i = 0; i < len; i++) {-		if (is_quadratic (flags[i])) {-			if (prev_is_curve && j != 0) {-				// add a new point if half way between two off curve points-				x = p[j - 1].x + ((points[i].x - p[j - 1].x) / 2.0);-				y = p[j - 1].y + ((points[i].y - p[j - 1].y) / 2.0);--				p[j].x = x;-				p[j].y = y;-				f[j] = ON_CURVE;-				j++;-			}-			-			prev_is_curve = TRUE;-		} else if (is_line (flags[i])) {-			prev_is_curve = FALSE;-		} else if (is_cubic (flags[i])) {-			prev_is_curve = TRUE;-		} else {-			fprintf (stderr, "WARNING invalid point flags: %d index: %d.\n", flags[i], i);-			prev_is_curve = TRUE;-		}-		-		p[j] = points[i];-		f[j] = flags[i];-		j++;-	}-	-	// last to first-	if (prev_is_curve && (flags[0] & ON_CURVE) == 0) {-		// First point off curve-		x = p[j - 1].x + ((points[i].x - p[j - 1].x) / 2.0); -		y = p[j - 1].y + ((points[i].y - p[j - 1].y) / 2.0);-		p[j].x = x;-		p[j].y = y;-		f[j] = ON_CURVE;-		j++;-		-		p[j] = points[i];-		f[j] = flags[i];-		j++;		--		x = p[j - 1].x + ((points[0].x - p[j - 1].x) / 2.0);-		y = p[j - 1].y + ((points[0].y - p[j - 1].y) / 2.0);--		p[j].x = x;-		p[j].y = y;-		f[j] = ON_CURVE;-		j++;-	} else if (prev_is_curve && is_quadratic (flags[i])) {-		// add a new point if half way between first and last point-		x = p[j - 1].x + ((points[i].x - p[j - 1].x) / 2.0);-		y = p[j - 1].y + ((points[i].y - p[j - 1].y) / 2.0);-		p[j].x = x;-		p[j].y = y;-		f[j] = ON_CURVE;-		j++;--		p[j] = points[i];-		f[j] = QUADRATIC_OFF_CURVE;-		j++;-		i++;--		p[j] = p[0];-		f[j] = f[0];-		j++;-			-		prev_is_curve = TRUE;--	} else {-		// close the path-		p[j] = points[i];-		f[j] = flags[i];-		j++;--		p[j] = p[0];-		f[j] = f[0];-	}--	*length = j;	-}--GString* get_svg_contour_data (FT_Vector* points, char* flags, int length, int* err) {-	GString* svg = g_string_new ("");-	GString* contour;-	int i = 0;-	FT_Vector* new_points;-	char* new_flags;-	 -	if (length == 0) {-		return svg;-	}-	-	create_contour (points, flags, &length, &new_points, &new_flags, err);-	-	g_string_printf (svg, "M %d,%d ", (int)new_points[length-1].x, (int)new_points[length-1].y); // FIXME: ????--	i = 0;-	while (i < length) {-		contour = g_string_new ("");-		-		if (is_cubic (new_flags[i])) {-			g_string_printf (contour, "C %d,%d  %d,%d  %d,%d ", (int)new_points[i].x, (int)new_points[i].y, (int)new_points[i+1].x, (int)new_points[i+1].y, (int)new_points[i+2].x, (int)new_points[i+2].y);-			i += 3;-		} else if (is_quadratic (new_flags[i])) {-			g_string_printf (contour, "Q %d,%d %d,%d ", (int)new_points[i].x, (int)new_points[i].y, (int)new_points[i+1].x, (int)new_points[i+1].y);	-			i += 2;-		} else if (is_line (new_flags[i])) {-			g_string_printf (contour, "L %d,%d ", (int)new_points[i].x, (int)new_points[i].y);-			i += 1;-		} else {-			fprintf (stderr, "WARNING Can not parse outline.\n");-			*err = 1;-			i++;-		}-		-		g_string_append (svg, contour->str);-		g_string_free (contour, 0);-	}-	-	g_string_append (svg, "z");-	-	free (new_points);-	free (new_flags);-	-	return svg;-}--GString* get_svg_glyph_data (FT_Face face, int* err) {-	GString* svg = g_string_new ("");-	GString* contour;-	FT_Error error;-	int i;-	int start;-	int end;-	-	if (face->glyph->outline.n_points == 0) {-		return svg;			-	}--	start = 0;-	for (i = 0; i < face->glyph->outline.n_contours; i++) {-		end = face->glyph->outline.contours [i];-		contour = get_svg_contour_data (face->glyph->outline.points + start, face->glyph->outline.tags + start, end - start, err);-		g_string_append (svg, contour->str);-		g_string_free (contour, 0);-		start = face->glyph->outline.contours [i] + 1;-	}--	return svg;-}--/** Get char code for a glyph.- * @gid glyph id- * @return character code- */-FT_ULong get_charcode (FT_Face face, FT_UInt gid) {-	FT_ULong charcode;-	FT_UInt gindex;-	-	// TODO: find the lookup function in freetype-	-	charcode = FT_Get_First_Char (face, &gindex);-	while (gindex != 0) {-		charcode = FT_Get_Next_Char (face, charcode, &gindex);-		if (gindex == gid) {-			return charcode;			-		}-	}-	-	return 0;-}--GString* get_kerning_data (FT_Face face, FT_Long gid) {-	FT_Error error;-	FT_Vector kerning;-	GString* svg = g_string_new ("");-	FT_ULong char_left;-	FT_ULong char_right;-	FT_Long right;-	-	char_left = get_charcode (face, gid);-	-	if (char_left <= 31) {-		fprintf (stderr, "Ignoring kerning for control character.\n");-		return svg;-	}-	-	if (char_left == 0) {-		fprintf (stderr, "No character code could be found for left kerning value.\n");-		return svg;-	}-	-	for (right = 0; right < face->num_glyphs; right++) {-		error = FT_Get_Kerning (face, gid, right, FT_KERNING_UNSCALED, &kerning);-		if (error) {-			fprintf (stderr, "Failed to obtain kerning value.\n");-			break;-		}-		-		char_right = get_charcode (face, right);-		if (char_left == 0) {-			fprintf (stderr, "No character code could be found for right kerning value.\n");-			return svg;-		}--		if (kerning.x > 0 && char_left > 31) {-			g_string_append_printf (svg, "<hkern u1=\"&#x%x;\" u2=\"&#x%x;\" k=\"%d\" />\n", char_left, char_right, kerning.x);-		}-	}-	-	return svg;-}--GString* get_svg_font (FT_Face face, int* err) {-	GString* svg = g_string_new ("");-	GString* svg_data;-	GString* hkern;-	GString* font_element = g_string_new ("");-	GString* glyph_element;-	FT_Error error;-	FT_Long i;-	FT_ULong charcode;-	double units_per_em;--	*err = OK;-	-	g_string_append (svg, "<?xml version=\"1.0\" standalone=\"no\"?>\n");-	-	// libxml2 fails on this in windows:-	// g_string_append (svg, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\" >\n");-	-	g_string_append (svg, "<svg xmlns=\"http://www.w3.org/2000/svg\">\n");-	g_string_append (svg, "<defs>\n");-	g_string_append_printf (svg, "<font id=\"%s\" horiz-adv-x=\"250\">\n", face->family_name);--	units_per_em = face->units_per_EM;-	units_per_em *= (1000.0 / face->units_per_EM) * (10.0 / 3.0); 	// TODO: find out why size is different in svg fonts-	g_string_printf (font_element, "<font-face units-per-em=\"%f\" ascent=\"%d\" descent=\"%d\" />\n", units_per_em, (int) face->ascender, (int) face->descender);	-	g_string_append (svg, font_element->str);-	-	// glyph outlines-	for (i = 0; i < face->num_glyphs; i++) {-		error = FT_Load_Glyph (face, i, FT_LOAD_DEFAULT);-		if (error) {-			fprintf (stderr, "Freetype failed to load glyph %d.\n", (int)i);-			fprintf (stderr, "FT_Load_Glyph error %d\n", error);-			*err = error;-			return svg;-		}--		charcode = get_charcode (face, i);-		glyph_element = g_string_new ("<glyph ");-		-		if (charcode > 31) {-			g_string_append (glyph_element,"unicode=\"&#x");-			g_string_append_printf (glyph_element, "%x", (guint)charcode);-			g_string_append (glyph_element, ";\" ");-		}-		-		g_string_append_printf (glyph_element, "horiz-adv-x=\"%d\" ", (int)face->glyph->metrics.horiAdvance);-		g_string_append (svg, glyph_element->str);-		g_string_free (glyph_element, 0);-		-		g_string_append (svg, "d=\"");-				-		if (face->glyph->format != ft_glyph_format_outline) {-			fprintf (stderr, "Freetype error no outline found in glyph.\n");-			*err = 1;-			return svg;-		}--		svg_data = get_svg_glyph_data (face, err);-		g_string_append (svg, svg_data->str);-		-		g_string_append (svg, "\" />\n");-		-		bird_font_progress_bar_set_progress ((double)i / face->num_glyphs);-	}--	// kerning-	for (i = 0; i < face->num_glyphs; i++) {-		hkern = get_kerning_data (face, i);-		g_string_append (svg, hkern->str);-		g_string_free (hkern, 0);--		bird_font_progress_bar_set_progress ((double)(face->num_glyphs - i) / face->num_glyphs);-	}--	g_string_append (svg, "</font>\n");-	g_string_append (svg, "</defs>\n");-	g_string_append (svg, "</svg>");-	-	g_string_free (font_element, 0);-	-	return svg;-}--int validate_font (FT_Face face) {-	// these tables can be validated-	const FT_Byte* BASE_table = NULL; -	const FT_Byte* GDEF_table = NULL;-	const FT_Byte* GPOS_table = NULL;-	const FT_Byte* GSUB_table = NULL;-	const FT_Byte* JSTF_table = NULL;-	int error;--	error = FT_OpenType_Validate (face, FT_VALIDATE_BASE | FT_VALIDATE_GDEF | FT_VALIDATE_GPOS | FT_VALIDATE_GSUB | FT_VALIDATE_JSTF, &BASE_table, &GDEF_table, &GPOS_table, &GSUB_table, &JSTF_table);--	if (error) {-		fprintf (stderr, "Freetype validation error %d\n", error);-		return error;-	} -	-	FT_OpenType_Free (face, BASE_table);-	FT_OpenType_Free (face, GDEF_table);-	FT_OpenType_Free (face, GPOS_table);-	FT_OpenType_Free (face, GSUB_table);-	FT_OpenType_Free (face, JSTF_table);-	-	return error;-}--/** Load typeface with freetype2 and return the result as a SVG font. - *  Parameter err will be set to non zero vaule if an error occurs.- */-GString* load_freetype_font (char* file, int* err) {-	GString* svg = NULL;--	FT_Library library;-	FT_Face face;-	int error;-	FT_Glyph glyph;-	FT_UInt glyph_index;--	error = FT_Init_FreeType (&library);-	if (error != OK) {-		fprintf (stderr, "Freetype init error %d.\n", error);-		*err = error;-		return svg;-	}--	error = FT_New_Face (library, file, 0, &face);-	if (error) {-		fprintf (stderr, "Freetype font face error %d\n", error);-		*err = error;-		return svg;-	}--	error = FT_Set_Char_Size (face, 0, 800, 300, 300);-	if (error) {-		fprintf (stderr, "Freetype FT_Set_Char_Size failed, error: %d.\n", error);-		*err = error;-		return svg;-	}--	svg = get_svg_font (face, &error);-	if (error != OK) {-		fprintf (stderr, "Failed to parse font.\n");-		*err = error;-		return svg;	-	}--	FT_Done_Face ( face );-	FT_Done_FreeType( library );-	-	*err = OK;-	return svg;-}--guint validate_freetype_font (char* file) {-	FT_Library library;-	FT_Face face;-	int error;-	-	error = FT_Init_FreeType (&library);-	if (error != OK) {-		fprintf (stderr, "Freetype init error %d\n", error);-		return FALSE;-	}--	error = FT_New_Face (library, file, 0, &face);-	if (error) {-		fprintf (stderr, "Freetype font face error %d\n", error);-		return FALSE;-	}-		-	error = validate_font (face);-	if (error) {-		fprintf (stderr, "Validation failed.\n", error);-		return FALSE;-	}--	FT_Done_Face ( face );-	FT_Done_FreeType( library );-	-	return TRUE;-}

Contact

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