Commit 97d7d95
Load TTF and OTF files with Freetype2
Changed files (12)
modifiedlibbirdfont/ExportTool.valamodifiedlibbirdfont/Font.valamodifiedlibbirdfont/GlyphSequence.valamodifiedlibbirdfont/MenuTab.valamodifiedlibbirdfont/OpenFontFormatReader.valamodifiedlibbirdfont/OpenFontFormatWriter.valamodifiedlibbirdfont/SvgFont.valamodifiedlibbirdfont/SvgFontFormatWriter.valamodifiedlibbirdfont/TestCases.valamodifiedlibbirdfont/Tool.valamodifiedlibbirdfont/VersionList.valamodifiedlibbirdfont/load_svg_font.c
Diff
@@ -481,6 +481,7 @@ os.put_string ( } if (!async) {+ print ("sync\n"); export_thread.run (); } @@ -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);@@ -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 ()); } @@ -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 ();@@ -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 { @@ -1169,7 +1169,7 @@ class GlyfTable : Table { } else { edit_point.recalculate_linear_handles (); } - + prev_is_curve = false; } else { @@ -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) {@@ -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) {@@ -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 () {@@ -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; }@@ -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; } @@ -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;