Clone in a terminal:
Commit 97d7d95
Johan Mattsson - 2013-02-15T19:23:07+00:00
97d7d95b86e03c5ffbb6d8d78b80de10b1e09daejohan.mattsson.m@gmail.com

Load TTF and OTF files with Freetype2

Changed files (12)

Diff

diff --git a/libbirdfont/ExportTool.vala b/libbirdfont/ExportTool.valaindex 6c0e940..7033f57 100644--- a/libbirdfont/ExportTool.vala+++ b/libbirdfont/ExportTool.vala@@ -481,6 +481,7 @@ os.put_string ( 			} 			 			if (!async) {+				print ("sync\n"); 				export_thread.run (); 			} 			diff --git a/libbirdfont/Font.vala b/libbirdfont/Font.valaindex b41b643..cdb48ea 100644--- a/libbirdfont/Font.vala+++ b/libbirdfont/Font.vala@@ -22,7 +22,8 @@ namespace BirdFont {  public enum FontFormat { 	FFI,-	SVG+	SVG,+	FREETYPE }  public class Font : GLib.Object {@@ -842,12 +843,19 @@ public class Font : GLib.Object { 				format = FontFormat.FFI; 			} 			+			if (path.has_suffix (".ttf") || path.has_suffix (".otf")) {+				font_file = path;+				loaded = parse_freetype_file (path);+				format = FontFormat.FREETYPE;+			}			+			+			/* // TODO: Remove the old way of loading ttfs when testing of the OTF writer is complete. 			if (BirdFont.experimental) { 				if (path.has_suffix (".ttf")) { 					font_file = path; 					loaded = parse_otf_file (path); 				}-			}+			}*/ 			 			if (recent) { 				add_thumbnail ();@@ -861,6 +869,30 @@ public class Font : GLib.Object { 		return loaded; 	} +	private bool parse_freetype_file (string path) {+		string svg;+		StringBuilder? data;+		int error;+		SvgFont svg_loader = new SvgFont (this);+		+		data = load_svg_font (path, out error);+		+		if (error != 0) {+			warning ("Failed to load font.");+			return false;+		}+		+		if (data == null) {+			warning ("No svg data.");+			return false;+		}+		+		svg = ((!) data).str;+		svg_loader.load_svg_data (svg);+		+		return true;+	}+ 	private bool parse_svg_file (string path) { 		print ("parse_svg_file\n"); 		SvgFont svg_font = new SvgFont (this);diff --git a/libbirdfont/GlyphSequence.vala b/libbirdfont/GlyphSequence.valaindex 9af0b75..cdfbc34 100644--- a/libbirdfont/GlyphSequence.vala+++ b/libbirdfont/GlyphSequence.vala@@ -45,6 +45,8 @@ public class GlyphSequence { 				break; 			} 			+			print (@"LIGA: $(((!) gc).get_current ().get_ligature_string ())\n");+			 			liga = ((!) gc).get_current ();		 			ligatures.replace (liga.get_ligature (), liga); 			i++;@@ -62,7 +64,7 @@ public class GlyphSequence { 			} else { 				i++; 			}-		}		+		} 	} 	 	bool starts_with (GlyphSequence old, uint index) {@@ -101,7 +103,9 @@ public class GlyphSequence { 			i++; 		} 		-		while (glyph.length () > 0) {+		// remove all links in order to prevent the g_list to delete all items+		// when the list is deleted.+		while (glyph.length () > 0) {  			glyph.remove_link (glyph.first ()); 		} 		diff --git a/libbirdfont/MenuTab.vala b/libbirdfont/MenuTab.valaindex 4ca50d1..e15f0cc 100644--- a/libbirdfont/MenuTab.vala+++ b/libbirdfont/MenuTab.vala@@ -420,7 +420,7 @@ c.append (""" 			tab_bar.add_unique_tab (new Preview (), 80);	 		}); 				-		if (format == FontFormat.SVG && !OverwriteDialog.ignore) {+		if ((format == FontFormat.SVG || format == FontFormat.FREETYPE) && !OverwriteDialog.ignore) { 			tab_bar.add_unique_tab (overwrite); 		} else { 			overwrite.finished ();diff --git a/libbirdfont/OpenFontFormatReader.vala b/libbirdfont/OpenFontFormatReader.valaindex 616d7fa..2e46f2d 100644--- a/libbirdfont/OpenFontFormatReader.vala+++ b/libbirdfont/OpenFontFormatReader.vala@@ -16,7 +16,7 @@ */  [CCode (cname = "load_svg_font")]-public extern static string? load_svg_font (string file, out bool error);+public extern static StringBuilder? load_svg_font (string file, out int error);  namespace BirdFont { diff --git a/libbirdfont/OpenFontFormatWriter.vala b/libbirdfont/OpenFontFormatWriter.valaindex 7d5448a..3f0fddf 100644--- a/libbirdfont/OpenFontFormatWriter.vala+++ b/libbirdfont/OpenFontFormatWriter.vala@@ -1169,7 +1169,7 @@ class GlyfTable : Table { 					} else {
 						edit_point.recalculate_linear_handles ();
 					}
-									
+					
 					prev_is_curve = false;
 				} else {
 									
diff --git a/libbirdfont/SvgFont.vala b/libbirdfont/SvgFont.valaindex 9192642..14b05cf 100644--- a/libbirdfont/SvgFont.vala+++ b/libbirdfont/SvgFont.vala@@ -28,19 +28,34 @@ class SvgFont : GLib.Object { 		this.font = f; 	} 	+	/** 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 (); 		-		doc = Parser.parse_file (path);+		if (file) {+			doc = Parser.parse_file (d);+		} else {+			doc = Parser.parse_doc (d);+		}+		 		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/SvgFontFormatWriter.vala b/libbirdfont/SvgFontFormatWriter.valaindex 1f708ac..5c47c21 100644--- a/libbirdfont/SvgFontFormatWriter.vala+++ b/libbirdfont/SvgFontFormatWriter.vala@@ -65,6 +65,7 @@ class SvgFontFormatWriter : Object  {  		// (missing-glyph goes here) +		// regular glyphs  		while (true) { 			g = font.get_glyph_indice (indice++); 			@@ -86,6 +87,8 @@ class SvgFontFormatWriter : Object  { 				} 			} 		}+		+		// FIXME: ligatures  		indice = 0; 		while (true) {diff --git a/libbirdfont/TestCases.vala b/libbirdfont/TestCases.valaindex 0d985b7..bf2f645 100644--- a/libbirdfont/TestCases.vala+++ b/libbirdfont/TestCases.vala@@ -50,13 +50,32 @@ class TestCases { 	}  	public static void test_freetype () {-		string svg;-		string? data;-		bool error;+		StringBuilder? data;+		int error;+		File f = BirdFont.get_settings_directory ();+		Font font = BirdFont.get_current_font ();++		font.set_name ("TEST_FONT");+				+		// draw some test glyphs+		test_illustrator_import ();+		test_inkscape_import ();+		+		Tool.yield ();+		+		if (!ExportTool.export_ttf_font_path (f, false)) {+			warning ("TTF export failed.");+		}+		+		f = f.get_child (font.get_name () + ".ttf");+		+		if (!f.query_exists ()) {+			warning ("File does not exist.");+		} 		-		data = load_svg_font ("", out error);+		data = load_svg_font ((!) f.get_path (), out error); 		-		if (error) {+		if (error != 0) { 			warning ("Failed to load font."); 			return; 		}@@ -66,9 +85,9 @@ class TestCases { 			return; 		} 		-		svg = (!) data;+		Tool.yield (); 		-		print (@"svg: $svg\n");+		font.load ((!) f.get_path ()); 	}  	public static void test_parse_quadratic_paths () {diff --git a/libbirdfont/Tool.vala b/libbirdfont/Tool.valaindex 21704d0..769dc54 100644--- a/libbirdfont/Tool.vala+++ b/libbirdfont/Tool.vala@@ -306,18 +306,18 @@ public class Tool : GLib.Object { 	/** Run pending events in main loop before continue. */ 	public static void @yield () { 		int t = 0;-		var time = new TimeoutSource(500);+		var time = new TimeoutSource (500); 		bool timeout;  		if (TestBirdFont.is_slow_test ()) { 			timeout = false; 			-			time.set_callback(() => {+			time.set_callback (() => { 				timeout = true; 				return false; 			}); -			time.attach(null);		+			time.attach (null);		 		} else { 			timeout = true; 		}diff --git a/libbirdfont/VersionList.vala b/libbirdfont/VersionList.valaindex b9493cc..bd1c9f7 100644--- a/libbirdfont/VersionList.vala+++ b/libbirdfont/VersionList.vala@@ -123,7 +123,7 @@ public class VersionList : DropMenu { 			ug = font.get_glyph (uni.str); 			 			if (ug == null) {-				warning ("display is null");+				warning (@"display is null for tab $(tab.get_label ())"); 				return; 			} 			diff --git a/libbirdfont/load_svg_font.c b/libbirdfont/load_svg_font.cindex 151d9e4..8326f97 100644--- a/libbirdfont/load_svg_font.c+++ b/libbirdfont/load_svg_font.c@@ -1,3 +1,20 @@+/*+    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 <stdio.h> #include <string.h>@@ -9,24 +26,288 @@ #include FT_OPENTYPE_VALIDATE_H #include FT_TRUETYPE_TABLES_H +#include <glib.h>++/** Error codes. */ #define OK 0 -char* get_svg_font (FT_Face face, int* err) {-	char* svg = (char*) malloc (2 * sizeof (char));-	*err = 	OK;+/** Point flags. */+#define ON_CURVE 1+#define CUBIC_CURVE 2+#define QUADRATIC_OFF_CURVE 0++uint is_cubic (char* flags, int index, int len) {+	return flags[index] & CUBIC_CURVE && (flags[index] & ON_CURVE) == 0;+}++uint is_quadratic (char* flags, int index, int len) {+	return (flags[index] & CUBIC_CURVE) == 0 && (flags[index] & ON_CURVE) == 0;+}++uint is_line (char* flags, int index, int len) {+	return flags[index] & 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;+	uint 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 (points, len - 1, len);+	+	if (is_quadratic (flags, 0, len)) {+		printf ("WARNING: path begins at off curve point.");+	} ++	// FIXME: What if first and last point is off curve?+	+	j = 0;+	for (i = 0; i < len; i++) {+		if (is_quadratic (flags, i, len)) {+			if (prev_is_curve && j != 0) { // i == 0?+				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, len)) {+			prev_is_curve = FALSE;+		} else if (is_cubic (flags, i, len)) {+			prev_is_curve = TRUE;+		} else {+			printf ("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) { // j == 0?+		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++;+				+		x = p[j - 1].x + ((p[0].x - p[j - 1].x) / 2.0);+		y = p[j - 1].y + ((p[0].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 {+		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, length)) {+			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, length)) {+			g_string_append_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, length)) {+			g_string_printf (contour, "L %d,%d ", (int)new_points[i].x, (int)new_points[i].y);+			i += 1;+		} else {+			printf ("WARNING Can not parse outline.\n");+			err = 1;+		}+		+		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) {+		printf ("Freetype error no points for outline in glyph.\n"); // FIXME: DELETE+		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 	-	// TODO: parse the file.+	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_svg_font (FT_Face face, int* err) {+	GString* svg = g_string_new ("");+	GString* svg_data;+	GString* font_element = g_string_new ("");+	GString* glyph_element;+	FT_Error error;+	FT_Long i;+	FT_ULong charcode;++	*err = OK;+	+	g_string_append (svg, "<?xml version=\"1.0\" standalone=\"no\"?>\n");+	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);++	g_string_printf (font_element, "<font-face units-per-em=\"%d\" ascent=\"%d\" descent=\"%d\" />\n", (int) face->units_per_EM, (int) face->ascender, (int) face->descender);	+	g_string_append (svg, font_element->str);+	+	for (i = 0; i < face->num_glyphs; i++) {+		error = FT_Load_Glyph (face, i, FT_LOAD_DEFAULT);+		if (error) {+			printf ("Freetype failed to load glyph %d.\n", i);+			printf ("FT_Load_Glyph error %d\n", error);+			*err = error;+			return svg;+		}++		charcode = get_charcode (face, i);+		glyph_element = g_string_new ("<glyph ");+		+		if (charcode != 0) {+			g_string_append (glyph_element,"unicode=\"&#x");+			g_string_append_printf (glyph_element, "%x", (uint)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) {+			printf ("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");+	}++	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;+	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 = OK;  	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);@@ -45,15 +326,15 @@ int validate_font (FT_Face face) { 	return error; } -/** Load typeface and return the result as a SVG font. +/** 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.  */-char* load_svg_font (char* file, int* err) {-	char* svg = NULL;+GString* load_svg_font (char* file, int* err) {+	GString* svg = NULL;  	FT_Library library; 	FT_Face face;-	FT_Error error;+	int error; 	FT_Glyph glyph; 	FT_UInt glyph_index; @@ -64,7 +345,7 @@ char* load_svg_font (char* file, int* err) { 		return svg; 	} -	error = FT_New_Face (library, "FONT FILE", 0, &face);+	error = FT_New_Face (library, file, 0, &face); 	if (error) { 		printf ("Freetype font face error %d\n", error); 		*err = error;@@ -74,10 +355,17 @@ char* load_svg_font (char* file, int* err) { 	error = validate_font (face); 	if (error) { 		*err = error;-		return svg;		+		return svg;+	}++	error = FT_Set_Char_Size (face, 0, 800, 300, 300);+	if (error) {+		printf ("Freetype FT_Set_Char_Size failed, error: %d.\n", error);+		*err = error;+		return svg; 	} -	svg = get_svg_font (face, error);+	svg = get_svg_font (face, &error); 	if (error != OK) { 		printf ("Failed to parse font.\n"); 		*err = error;

Contact

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