Clone in a terminal:
Commit 3ecac50
Johan Mattsson - 2014-03-24T16:46:54+00:00
3ecac5056a670c01fcaf56d67ace47f87a11cfc6johan.mattsson.m@gmail.com

Speed optimizations of TTF export code Exporting kerning pairs is now more than a thousand times faster for big fonts.

Changed files (15)

Diff

diff --git a/libbirdfont/Font.vala b/libbirdfont/Font.valaindex b9ef770..76696d8 100644--- a/libbirdfont/Font.vala+++ b/libbirdfont/Font.vala@@ -355,10 +355,6 @@ public class Font : GLib.Object { 	} 	 	public string get_name_for_character (unichar c) {-		uint i = 0;-		Glyph? gl;-		Glyph g;-		 		StringBuilder sb; 		 		// FIXME: this is too slowdiff --git a/libbirdfont/Glyph.vala b/libbirdfont/Glyph.valaindex 27075fd..97a53e3 100644--- a/libbirdfont/Glyph.vala+++ b/libbirdfont/Glyph.vala@@ -506,6 +506,17 @@ public class Glyph : FontDisplay { 		return unichar_code; 	} +	public string get_unichar_string () {+		string? s = (!)get_unichar ().to_string ();+		+		if (unlikely (s == null)) {+			warning ("Invalid character.");+			return "".dup ();+		}+		+		return (!) s;+	}+ 	public bool get_show_help_lines () { 		return show_help_lines; 	}diff --git a/libbirdfont/GlyphRange.vala b/libbirdfont/GlyphRange.valaindex dcf46f7..93552ea 100644--- a/libbirdfont/GlyphRange.vala+++ b/libbirdfont/GlyphRange.vala@@ -16,8 +16,9 @@ namespace BirdFont {  public class GlyphRange { 	-	List<UniRange> ranges;+	public List<UniRange> ranges; 	+	/** Glyphs without a corresponding unicode value (ligatures). */ 	public List<string> unassigned; 	 	uint32 len = 0;@@ -371,6 +372,10 @@ public class GlyphRange { 		return !unique (s, s); 	} +	public bool has_unichar (unichar c) {+		return !unique (c, c);+	}+	 	private bool unique (unichar start, unichar stop) { 		foreach (UniRange u in ranges) { 			if (inside (start, u.start, u.stop)) return false;diff --git a/libbirdfont/Kerning.vala b/libbirdfont/Kerning.valaindex 8e60c9f..ab0ee52 100644--- a/libbirdfont/Kerning.vala+++ b/libbirdfont/Kerning.vala@@ -19,9 +19,25 @@ namespace BirdFont {  public class Kerning : GLib.Object { 	public double val;+	public Glyph? glyph; 	 	public Kerning (double v) { 		val = v;+		glyph = null;+	}+	+	public Kerning.for_glyph (Glyph? g, double v) {+		val = v;+		glyph = g;+	}+	+	public Glyph get_glyph () {+		if (unlikely (glyph == null)) {+			warning ("No glyph");+			return new Glyph ("");+		}+		+		return (!) glyph; 	} } diff --git a/libbirdfont/KerningClasses.vala b/libbirdfont/KerningClasses.valaindex 20dc7bc..b5b647b 100644--- a/libbirdfont/KerningClasses.vala+++ b/libbirdfont/KerningClasses.vala@@ -1,5 +1,5 @@ /*-    Copyright (C) 2013 Johan Mattsson+    Copyright (C) 2013, 2014 Johan Mattsson      This library is free software; you can redistribute it and/or modify      it under the terms of the GNU Lesser General Public License as @@ -28,8 +28,11 @@ public class KerningClasses : GLib.Object {  	// kerning for single glyphs 	Gee.HashMap<string, double?> single_kerning;+	public GLib.List<string> single_kerning_letters_left; // FIXME: this needs to be fast and sorted+	public GLib.List<string> single_kerning_letters_right;  	-	public delegate void KerningIterator (string left, string right, double kerning);+	public delegate void KerningIterator (KerningPair list);+	public delegate void KerningClassIterator (string left, string right, double kerning);  	public KerningClasses () { 		classes_first = new GLib.List<GlyphRange> ();@@ -52,6 +55,17 @@ public class KerningClasses : GLib.Object { 	public void set_kerning_for_single_glyphs (string l, string r, double k) { 		string left = GlyphRange.serialize (l); 		string right = GlyphRange.serialize (r);+		string cleft = (!)GlyphRange.unserialize (left).get_char ().to_string ();+		string cright = (!)GlyphRange.unserialize (right).get_char ().to_string ();+		+		if (single_kerning_letters_left.index (cleft) < 0) {+			single_kerning_letters_left.append (cleft);+		}++		if (single_kerning_letters_right.index (cright) < 0) {+			single_kerning_letters_right.append (cright);+		}+				 		single_kerning.set (@"$left - $right", k); 	}  @@ -140,12 +154,15 @@ public class KerningClasses : GLib.Object { 		unowned GLib.List<GlyphRange> l; 		int len = (int) classes_first.length (); 		double? d;+		double time; 		 		d = get_kerning_for_single_glyphs (left_glyph, right_glyph); 		if (d != null) { 			return (!)d; 		} +		time = GLib.get_real_time ();+		 		len = (int)classes_first.length (); 		return_val_if_fail (len == classes_last.length (), 0); 		return_val_if_fail (len == classes_kerning.length (), 0);@@ -156,10 +173,11 @@ public class KerningClasses : GLib.Object { 			 			if (l.data.has_character (left_glyph) 				&& r.data.has_character (right_glyph)) {+ 				return classes_kerning.nth (i).data.val; 			} 		}-		+	 		return 0; 	} @@ -243,7 +261,7 @@ public class KerningClasses : GLib.Object { 		} 	} -	public void get_classes (KerningIterator kerningIterator) {+	public void get_classes (KerningClassIterator kerningIterator) { 		for (int i = 0; i < classes_first.length (); i++) { 			kerningIterator (classes_first.nth (i).data.get_all_ranges (), 				classes_last.nth (i).data.get_all_ranges (),@@ -251,7 +269,7 @@ public class KerningClasses : GLib.Object { 		} 	} 	-	public void get_single_position_pairs (KerningIterator kerningIterator) {+	public void get_single_position_pairs (KerningClassIterator kerningIterator) { 		double k = 0; 		 		foreach (string key in single_kerning.keys) {@@ -266,35 +284,92 @@ public class KerningClasses : GLib.Object { 		} 	} 	-	public void all_pairs (KerningIterator kerningIterator) {-		// FIXME: THIS CODE IS TOO SLOW-		Font f = BirdFont.get_current_font ();-		Glyph? g1, g2;-		Glyph glyph1, glyph2;-		double k;-		int i, j;-		-		i = 0;-		g1 = f.get_glyph_indice (i);-		while (g1 != null) {-			glyph1 = (!) g1;+	public void each_pair (KerningClassIterator iter) {+		all_pairs ((kl) => {+			Glyph g2;+			KerningPair kerning_list = kl;+			string g1 = kerning_list.character.get_name ();+			unowned GLib.List<Kerning> kerning = kerning_list.kerning.first (); 			-			j = 0;-			g2 = f.get_glyph_indice (j);-			while (g2 != null) {-				glyph2 = (!) g2;-				-				k = KerningClasses.get_instance ().get_kerning (glyph1.get_name (), glyph2.get_name ());-				if (k != 0) {-					kerningIterator (glyph1.get_name (), glyph2.get_name (), k);+			if (unlikely (is_null (kerning))) {+				warning ("No kerning values.");+			}+			+			foreach (Kerning k in kerning_list.kerning) {+				g2 = k.get_glyph ();+				iter (g1, g2.get_name (), kerning.data.val);+				kerning = kerning.next;+			}+		});+	}+	+	public void all_pairs (KerningIterator kerningIterator) {+		Font font = BirdFont.get_current_font ();+		GLib.List<Glyph> left_glyphs = new GLib.List<Glyph> ();+		GLib.List<KerningPair> pairs = new GLib.List<KerningPair> ();+		double kerning;+		string right;+		+		// Create a list of first glyph in all pairs+		foreach (GlyphRange r in classes_first) {+			foreach (UniRange u in r.ranges) {+				for (unichar c = u.start; c <= u.stop; c++) {+					string name = (!)c.to_string ();+					Glyph? g = font.get_glyph (name);+					if (g != null && left_glyphs.index ((!) g) < 0) {+						left_glyphs.append ((!) g);+					} 				}+			} 				-				j++;-				g2 = f.get_glyph_indice (j);+			// TODO: GlyphRange.unassigned+		}+		+		foreach (string name in single_kerning_letters_left) {+			Glyph? g = font.get_glyph (name);+			if (g != null && left_glyphs.index ((!) g) < 0) {+				left_glyphs.append ((!) g);+			}+		}+		+		left_glyphs.sort ((a, b) => {+			return strcmp (a.get_name (), b.get_name ());+		});++		// add the right hand glyph and the kerning value+		foreach (Glyph character in left_glyphs) {+			KerningPair kl = new KerningPair (character);+			pairs.append (kl);++			foreach (GlyphRange r in classes_last) {+				foreach (UniRange u in r.ranges) {+					for (unichar c = u.start; c <= u.stop; c++) {+						right = (!)c.to_string ();+						+						if (font.has_glyph (right)) {+							kerning = get_kerning (character.get_name (), right);+							kl.add_unique ((!) font.get_glyph (right), kerning);+						}+					}+				}+				// TODO: GlyphRange.unassigned+			}++			// TODO: The get_kerning () function is still rather slow. Optimize it.+			foreach (string right_glyph_name in single_kerning_letters_right) {+				Glyph? gl = font.get_glyph (right_glyph_name);+				if (gl != null) {+					kerning = get_kerning (character.get_name (), right_glyph_name);+					kl.add_unique ((!) gl , kerning);+				} 			} 			-			i++;-			g1 = f.get_glyph_indice (i);+			kl.sort ();+		}+		+		// obtain the kerning value+		foreach (KerningPair p in pairs) {+			kerningIterator (p); 		} 	} @@ -312,7 +387,16 @@ public class KerningClasses : GLib.Object { 		while (classes_kerning.length () > 0) { 			classes_kerning.remove_link (classes_kerning.first ()); 		}++		while (single_kerning_letters_left.length () > 0) {+			single_kerning_letters_left.remove_link (single_kerning_letters_left.first ());+		}		++		while (single_kerning_letters_right.length () > 0) {+			single_kerning_letters_right.remove_link (single_kerning_letters_right.first ());+		}		 		+				 		GlyphCanvas.redraw (); 		 		if (!is_null (MainWindow.get_toolbox ())) { // FIXME: reorganizediff --git a/libbirdfont/KerningPair.vala b/libbirdfont/KerningPair.valanew file mode 100644index 0000000..d345d9d--- /dev/null+++ b/libbirdfont/KerningPair.vala@@ -0,0 +1,47 @@+/*+    Copyright (C) 2014 Johan Mattsson++    This library is free software; you can redistribute it and/or modify +    it under the terms of the GNU Lesser General Public License as +    published by the Free Software Foundation; either version 3 of the +    License, or (at your option) any later version.++    This library 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 +    Lesser General Public License for more details.+*/++using Cairo;+using Xml;++namespace BirdFont {++/** A list of all kerning pairs for one glyph. */+public class KerningPair : GLib.Object {+	public Glyph character;+	public GLib.List<Kerning> kerning;++	GLib.List<Glyph> right; // FIXME: should be fast and sorted+	+	public KerningPair (Glyph left) {+		character = left;+		right = new GLib.List<Glyph> ();+		kerning = new GLib.List<Kerning> ();+	}+	+	public void add_unique (Glyph g, double k) {+		if (right.index ((!) g) < 0) {+			right.append ((!) g);+			kerning.append (new Kerning.for_glyph (g, k));+		}+	}+	+	public void sort () {+		kerning.sort ((a, b) => {+			return strcmp (((!)a.glyph).get_unichar_string (), ((!)b.glyph).get_unichar_string ());+		});		+	}+}+	+}diff --git a/libbirdfont/Line.vala b/libbirdfont/Line.valaindex 01c0d3e..0f1315d 100644--- a/libbirdfont/Line.vala+++ b/libbirdfont/Line.vala@@ -135,7 +135,6 @@ public class Line : GLib.Object { 		double ivz = 1/g.view_zoom; 		double margin = 10; 		double none = 0;-		double tpy; 		 		if (!moveable) { 			return false;diff --git a/libbirdfont/OpenFontFormat/GlyfTable.vala b/libbirdfont/OpenFontFormat/GlyfTable.valaindex 6d19981..6184305 100644--- a/libbirdfont/OpenFontFormat/GlyfTable.vala+++ b/libbirdfont/OpenFontFormat/GlyfTable.vala@@ -70,6 +70,7 @@ public class GlyfTable : Table { 			i++; 		} 		+		warning (@"Glyph $name not found in font."); 		return -1; 	} diff --git a/libbirdfont/OpenFontFormat/GposTable.vala b/libbirdfont/OpenFontFormat/GposTable.valaindex 2f92473..15a2350 100644--- a/libbirdfont/OpenFontFormat/GposTable.vala+++ b/libbirdfont/OpenFontFormat/GposTable.vala@@ -15,16 +15,10 @@ using Math;  namespace BirdFont { -public class PairFormat1 : GLib.Object {-	public uint16 left = -1;-	public List<Kern> pairs = new List<Kern> ();-}- public class GposTable : Table { 	 	GlyfTable glyf_table;-	List<Kern> kerning_pairs = new List<Kern> ();-	List<PairFormat1> pairs = new List<PairFormat1> ();+	KernList pairs; 	 	public GposTable () { 		id = "GPOS";@@ -38,6 +32,8 @@ public class GposTable : Table { 		FontData fd = new FontData (); 		 		this.glyf_table = glyf_table;+		this.pairs = new KernList (glyf_table);+  		printd ("Process GPOS\n"); @@ -88,24 +84,46 @@ public class GposTable : Table { 		this.font_data = fd; 	} +	uint16 get_max_pairs () {+		return (uint16) ((uint16.MAX - 10) / 3.0);+	}+ 	// PairPosFormat1 subtable 	FontData get_pair_pos_format1 () throws GLib.Error { 		FontData fd = new FontData ();-		uint16 pair_set_count; 		uint coverage_offset;+		uint16 max_pairs_per_table = get_max_pairs ();+		uint16 num_pairs;+		int i;+		uint pair_set_offset;+		uint written;+		uint written_pairs;+		uint last_gid_left;+		uint last_gid_right;+		uint16 pair_set_count; 		-		create_kerning_pairs ();+		if (pairs.get_length () == 0) {+			pairs.fetch_all_pairs ();+		}++		// FIXME: add another table instead of truncating it+		if (pairs.get_length () > max_pairs_per_table) {+			warning (@"Too many kerning pairs. $(pairs.get_length ())");+			num_pairs = max_pairs_per_table;+		}++		num_pairs = (pairs.get_length () < max_pairs_per_table) ? +			 (uint16) pairs.get_length () : max_pairs_per_table; 		-		coverage_offset = 10 + pairs_offset_length () + pairs_set_length ();+		pair_set_count = (uint16) pairs.get_length_left (); // FIXME: boundaries 		-		// FIXME: add more then current maximum of pairs+		coverage_offset = 10 + pairs_offset_length () + pairs_set_length (); 		-		if (pairs.length () > uint16.MAX || coverage_offset > uint16.MAX) {-			print_pairs ();-			warning (@"Too many kerning pairs. $(pairs.length ())");+		if (coverage_offset > uint16.MAX) {+			warning (@"Invalid coverage offset. Number of pairs: $(pairs.get_length ())");+			num_pairs = 0;+			coverage_offset = 10; 		}-		-		pair_set_count = (uint16) pairs.length (); 			 		fd.add_ushort (1); // position format 		// offset to coverage table from beginning of kern pair table@@ -115,65 +133,133 @@ public class GposTable : Table { 		fd.add_ushort (pair_set_count); // n pairs 		 		// pair offsets orderd by coverage index-		uint pair_set_offset = 10 + pairs_offset_length ();-		foreach (PairFormat1 k in pairs) {-			fd.add_ushort ((uint16) pair_set_offset);-			pair_set_offset += 2;-			-			foreach (Kern pk in k.pairs) {-				pair_set_offset += 4;+		pair_set_offset = 10 + pairs_offset_length ();+		+		written = 0;+		written_pairs = 0;+		pairs.all_pairs_format1 ((k) => {+			try {+				if (pair_set_offset > uint16.MAX) {+					warning ("Invalid offset.");+					return;+				}+				+				fd.add_ushort ((uint16) pair_set_offset);+				written += 2;+				+				pair_set_offset += 2;+				+				foreach (Kern pk in k.pairs) {+					pair_set_offset += 4;+				}+			} catch (Error e) {+				warning (e.message); 			}+		}, num_pairs);+		+		if (unlikely (written != pairs_offset_length ())) {+			warning (@"Bad pairs_offset_length () calculated: $(pairs_offset_length ()), real length $written"); 		} 		 		// pair table -		foreach (PairFormat1 p in pairs) {-			fd.add_ushort ((uint16) p.pairs.length ()); -			foreach (Kern k in p.pairs) {-				// pair value record	-				fd.add_ushort (k.right); // gid to second glyph-				fd.add_short (k.kerning); // value of ValueFormat1, horizontal adjustment for advance			-				// value of ValueFormat2 is null+		i = 0;+		written = 0;+		last_gid_left = 0;+		last_gid_right = 0;+		pairs.all_pairs_format1 ((pn) => {+			try {+				PairFormat1 p = pn;+				uint pairset_length = p.pairs.length ();+				+				if (pairset_length > uint16.MAX) {+					warning ("Too many pairs");+					pairset_length = uint16.MAX;+				}+				+				if (unlikely (p.left < last_gid_left)) {+					warning (@"Kerning table is not sorted $(p.left) < $last_gid_left.");+				}++				last_gid_left = p.left;+				+				fd.add_ushort ((uint16) pairset_length); +				written += 2;+				last_gid_right = 0;+				written_pairs = 0;+				foreach (Kern k in p.pairs) {+					// pair value record	+					fd.add_ushort (k.right); // gid to second glyph+					fd.add_short (k.kerning); // value of ValueFormat1, horizontal adjustment for advance+					// value of ValueFormat2 is null+					+					if (unlikely (k.right < last_gid_right)) {+						warning (@"Kerning table is not sorted $(k.right) < $last_gid_right).");+					}+				+					last_gid_right = k.right;++					written += 4;+					written_pairs++;+				}		+				+				if (unlikely (written_pairs != p.pairs.length ())) {+					warning (@"written_pairs != p.pairs.length () $(written_pairs) != $(pairs.get_length ())   pairset_length: $pairset_length");+				}+				+				i++;+			} catch (Error e) {+				warning (e.message); 			}+		}, num_pairs);+		+		if (unlikely (pairs_set_length () != written)) {+			warning (@"Bad pair set length: $(pairs_set_length ()), real length: $written"); 		} 		 		ProgressBar.set_progress (0); // reset progress bar 		-		if (fd.length () != coverage_offset) {-			warning (@"Bad coverage offset, coverage_offset: $coverage_offset, length: $(fd.length ())");+		if (unlikely (fd.length () != coverage_offset)) {+			warning (@"Bad coverage offset, coverage_offset: $coverage_offset, real length: $(fd.length ())"); 			warning (@"pairs_offset_length: $(pairs_offset_length ()) pairs_set_length: $(pairs_set_length ())"); 		} 		 		// coverage 		fd.add_ushort (1); // format-		fd.add_ushort ((uint16) pairs.length ());-		foreach (PairFormat1 p in pairs) {-			fd.add_ushort (p.left); // gid-		}+		fd.add_ushort (pair_set_count); 		-		return fd;-	}-	-	public void print_pairs () {-		foreach (PairFormat1 p in pairs) {-			print (@"\nGid: $(p.left)\n");-			foreach (Kern k in p.pairs) {-				print (@"\tKern $(k.right)\t$(k.kerning)\n");+		written = 0;+		pairs.all_pairs_format1 ((p) => {+			try {+				fd.add_ushort (p.left); // gid+				written += 2;+			} catch (Error e) {+				warning (e.message); 			}+		}, num_pairs);+		+		if (unlikely (written != 2 * pair_set_count)) {+			warning ("written != 2 * pair_set_count"); 		}+		+		return fd; 	} 	 	public uint pairs_set_length () { 		uint len = 0;-		foreach (PairFormat1 p in pairs) {+		+		pairs.all_pairs_format1 ((p) => { 			len += 2 + 4 * p.pairs.length ();-		}+		});+		 		return len; 	} 	 	public uint pairs_offset_length () {-		return 2 * pairs.length ();+		return 2 * pairs.pairs.length (); 	} +/*+// FIXME: DELETE 	public int get_pair_index (int gid) { 		int i = 0; 		foreach (PairFormat1 p in pairs) {@@ -184,43 +270,7 @@ public class GposTable : Table { 		} 		return -1; 	}-	-	/** Create kerning pairs from classes. */-	public void create_kerning_pairs () {-		while (kerning_pairs.length () > 0) {-			kerning_pairs.remove_link (kerning_pairs.first ());-		}-		-		KerningClasses.get_instance ().all_pairs ((left, right, kerning) => {-			uint16 gid1, gid2;-			PairFormat1 pair;-			int pair_index;-			-			gid1 = (uint16) glyf_table.get_gid (left);-			gid2 = (uint16) glyf_table.get_gid (right);-			-			if (gid1 == -1) {-				warning (@"gid is -1 for \"$left\"");-				return;-			}-			-			if (gid2 == -1) {-				warning (@"gid is -1 for \"$right\"");-				return;-			}-			-			pair_index = get_pair_index (gid1);-			if (pair_index == -1) {-				pair = new PairFormat1 ();-				pair.left = gid1; -				pairs.append (pair);-			} else {-				pair = pairs.nth (pair_index).data;-			}-			-			pair.pairs.append (new Kern (gid1, gid2, (int16)(kerning * HeadTable.UNITS)));-		});-	}+*/ }  }diff --git a/libbirdfont/OpenFontFormat/Kern.vala b/libbirdfont/OpenFontFormat/Kern.valadeleted file mode 100644index 6fc1c35..0000000--- a/libbirdfont/OpenFontFormat/Kern.vala+++ /dev/null@@ -1,169 +0,0 @@-/*-    Copyright (C) 2012, 2013 Johan Mattsson--    This library is free software; you can redistribute it and/or modify -    it under the terms of the GNU Lesser General Public License as -    published by the Free Software Foundation; either version 3 of the -    License, or (at your option) any later version.--    This library 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 -    Lesser General Public License for more details.-*/--namespace BirdFont {--public class Kern : GLib.Object {-	public uint16 left;-	public uint16 right;-	public int16 kerning;-	-	public Kern (uint16 l, uint16 r, int16 k) {-		left = l;-		right = r;-		kerning = k;-	}-}--public class KernTable : Table {-	-	public static const uint16 HORIZONTAL = 1;-	public static const uint16 MINIMUM = 1 << 1;-	public static const uint16 CROSS_STREAM = 1 << 2;-	public static const uint16 OVERRIDE = 1 << 3;-	public static const uint16 FORMAT = 1 << 8;-	-	GlyfTable glyf_table;-	-	public List<Kern> kerning = new List<Kern> ();-	public int kerning_pairs = 0;-	-	public KernTable (GlyfTable gt) {-		glyf_table = gt;-		id = "kern";-	}-	-	public override void parse (FontData dis) throws GLib.Error {-		uint16 version;-		uint16 sub_tables;-		-		uint16 subtable_version;-		uint16 subtable_length;-		uint16 subtable_flags;--		uint16 search_range;-		uint16 entry_selector;-		uint16 range_shift;-		-		uint16 n_pairs;-			-		dis.seek (offset);-		-		version = dis.read_ushort ();-		warn_if_fail (version == 0);-		sub_tables = dis.read_ushort ();-		-		for (uint16 i = 0; i < sub_tables; i++) {-			subtable_version = dis.read_ushort ();			-			subtable_length = dis.read_ushort ();			-			subtable_flags = dis.read_ushort ();--			n_pairs = dis.read_ushort ();-			search_range = dis.read_ushort ();-			entry_selector = dis.read_ushort ();-			range_shift = dis.read_ushort ();-						-			// TODO: check more flags-			if ((subtable_flags & HORIZONTAL) > 0 && (subtable_flags & CROSS_STREAM) == 0 && (subtable_flags & MINIMUM) == 0) {-				parse_pairs (dis, n_pairs);-			}-		}-	}-	-	public void parse_pairs (FontData dis, uint16 n_pairs) throws Error {-		uint16 left;-		uint16 right;-		int16 k;-		-		for (int i = 0; i < n_pairs; i++) {-			left = dis.read_ushort ();-			right = dis.read_ushort ();-			k = dis.read_short ();-						-			kerning.append (new Kern (left, right, k));-		}		-	}-	-	public void process () throws GLib.Error {-		FontData fd = new FontData ();-		uint16 n_pairs = 0;-		-		uint16 gid_left;-		-		uint16 range_shift = 0;-		uint16 entry_selector = 0;-		uint16 search_range = 0;-		-		int i;-		-		fd.add_ushort (0); // version -		fd.add_ushort (1); // n subtables--		fd.add_ushort (0); // subtable version --		KerningClasses.get_instance ().all_pairs ((left, right, k) => {-			n_pairs++;-		});-		-		if (n_pairs > (uint16.MAX - 14) / 6.0) {-			warning ("Too many kerning pairs!"); - 			n_pairs = (uint16) ((uint16.MAX - 14) / 6.0);-		}-		-		this.kerning_pairs = n_pairs;-		-		fd.add_ushort (6 * n_pairs + 14); // subtable length-		fd.add_ushort (HORIZONTAL); // subtable flags--		fd.add_ushort (n_pairs);-		-		search_range = 6 * largest_pow2 (n_pairs);-		entry_selector = largest_pow2_exponent (n_pairs);-		range_shift = 6 * n_pairs - search_range;-		-		fd.add_ushort (search_range);-		fd.add_ushort (entry_selector);-		fd.add_ushort (range_shift);--		gid_left = 0;-		-		i = 0;-		-		KerningClasses.get_instance ().all_pairs ((left, right, k) => {-			uint16 gid1, gid2;-			-			try {-				// n_pairs is used to truncate this table to prevent buffer overflow-				if (i++ < n_pairs) {-					gid1 = (uint16) glyf_table.get_gid (left);-					gid2 = (uint16) glyf_table.get_gid (right);-					-					fd.add_ushort (gid1);-					fd.add_ushort (gid2);-					fd.add_short ((int16) (k * HeadTable.UNITS));-				}-			} catch (GLib.Error e) {-				warning (e.message);-			}-		});-		-		fd.pad ();-		this.font_data = fd;-	}--}---}-diff --git a/libbirdfont/OpenFontFormat/KernList.vala b/libbirdfont/OpenFontFormat/KernList.valanew file mode 100644index 0000000..62fe20e--- /dev/null+++ b/libbirdfont/OpenFontFormat/KernList.vala@@ -0,0 +1,125 @@+/*+    Copyright (C) 2014 Johan Mattsson++    This library is free software; you can redistribute it and/or modify +    it under the terms of the GNU Lesser General Public License as +    published by the Free Software Foundation; either version 3 of the +    License, or (at your option) any later version.++    This library 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 +    Lesser General Public License for more details.+*/++namespace BirdFont {++public class KernList : GLib.Object {+	public List<PairFormat1> pairs;++	public delegate void KernIterator (Kern k);+	public delegate void PairFormat1Iterator (PairFormat1 k);++	GlyfTable glyf_table;+	uint num_pairs;+	+	public KernList (GlyfTable glyf_table) {+		this.glyf_table = glyf_table;+		num_pairs = 0;+	}+	+	/** @return number of pairs. */+	public uint fetch_all_pairs () {+		double time = GLib.get_real_time ();+		PairFormat1 current_pairs = new PairFormat1 ();+		+		if (pairs.length () > 0 || num_pairs > 0) {+			warning ("Pairs already loaded.");+		}+		+		num_pairs = 0;+		while (pairs.length () > 0) {+			pairs.remove_link (pairs.first ());+		}+		+		KerningClasses.get_instance ().all_pairs ((kp) => {+			uint16 gid_left, gid_right;+			unowned List<Kerning> kerning;+			KerningPair kerning_pair = kp;+			+			if (unlikely (kerning_pair.character.get_name () == "")) {+				warning ("No name for glyph");+			}++			current_pairs = new PairFormat1 ();+			gid_left = (uint16) glyf_table.get_gid (kerning_pair.character.get_name ());+			current_pairs.left = gid_left;+			pairs.append (current_pairs);+			+			kerning = kerning_pair.kerning.first ();+			+			if (unlikely (is_null (kerning))) {+				warning ("No kerning");+			}+			+			if (unlikely (kerning_pair.kerning.length () == 0)) {+				warning ("No pairs.");+			}+			+			num_pairs += kerning_pair.kerning.length ();+			foreach (Kerning k in kerning_pair.kerning) {+				gid_right = (uint16) glyf_table.get_gid (k.get_glyph ().get_name ());+				current_pairs.pairs.append (new Kern (gid_left, gid_right, (int16) (kerning.data.val * HeadTable.UNITS)));+				kerning = kerning.next;+			}+		});+		+		Test.print_time (@"Feching $(num_pairs) pairs in ", time);+		+		return num_pairs;+	}++	/** @return the number of glyphs that is kerned on the left side in +	 * pair pos format 1.+	 */+	public uint get_length_left () {+		return pairs.length ();+	}+		+	/** @return the total number of pairs. */+	public uint get_length () {+		return num_pairs;+	}+	+	public void all_kern (KernIterator iter, int limit) {+		int i = 0;+		foreach (PairFormat1 p in pairs) {+			foreach (Kern k in p.pairs) {+				if (unlikely (i >= limit)) {+					warning (@"Too many pairs. Limit: $limit");+					return;+				}+				+				iter (k);+				i++;+			}+		}+	}++	public void all_pairs_format1 (PairFormat1Iterator iter, int limit = -1) {+		uint i = 0;+		+		foreach (PairFormat1 p in pairs) {+			if (unlikely (i >= limit && limit != -1)) {+				warning (@"Too many pairs. Limit: $limit");+				return;+			}+			+			iter (p);+			+			i++;+		}+	}+}++}diff --git a/libbirdfont/OpenFontFormat/KernTable.vala b/libbirdfont/OpenFontFormat/KernTable.valanew file mode 100644index 0000000..a77339b--- /dev/null+++ b/libbirdfont/OpenFontFormat/KernTable.vala@@ -0,0 +1,182 @@+/*+    Copyright (C) 2012, 2013, 2014 Johan Mattsson++    This library is free software; you can redistribute it and/or modify +    it under the terms of the GNU Lesser General Public License as +    published by the Free Software Foundation; either version 3 of the +    License, or (at your option) any later version.++    This library 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 +    Lesser General Public License for more details.+*/++namespace BirdFont {++public class KernTable : Table {+	+	public static const uint16 HORIZONTAL = 1;+	public static const uint16 MINIMUM = 1 << 1;+	public static const uint16 CROSS_STREAM = 1 << 2;+	public static const uint16 OVERRIDE = 1 << 3;+	public static const uint16 FORMAT = 1 << 8;+	+	GlyfTable glyf_table;+	+	KernList pairs;+	+	// Only used for loading pairs+	public List<Kern> kerning = new List<Kern> (); // TODO: replace with the KernList+	public uint kerning_pairs = 0;+	+	public KernTable (GlyfTable gt) {+		glyf_table = gt;+		id = "kern";+		pairs = new KernList (gt);+	}+	+	public override void parse (FontData dis) throws GLib.Error {+		uint16 version;+		uint16 sub_tables;+		+		uint16 subtable_version;+		uint16 subtable_length;+		uint16 subtable_flags;++		uint16 search_range;+		uint16 entry_selector;+		uint16 range_shift;+		+		uint16 n_pairs;+			+		dis.seek (offset);+		+		version = dis.read_ushort ();+		warn_if_fail (version == 0);+		sub_tables = dis.read_ushort ();+		+		for (uint16 i = 0; i < sub_tables; i++) {+			subtable_version = dis.read_ushort ();			+			subtable_length = dis.read_ushort ();			+			subtable_flags = dis.read_ushort ();++			n_pairs = dis.read_ushort ();+			search_range = dis.read_ushort ();+			entry_selector = dis.read_ushort ();+			range_shift = dis.read_ushort ();+						+			// TODO: check more flags+			if ((subtable_flags & HORIZONTAL) > 0 && (subtable_flags & CROSS_STREAM) == 0 && (subtable_flags & MINIMUM) == 0) {+				parse_pairs (dis, n_pairs);+			}+		}+	}+	+	public void parse_pairs (FontData dis, uint16 n_pairs) throws Error {+		uint16 left;+		uint16 right;+		int16 k;+		+		for (int i = 0; i < n_pairs; i++) {+			left = dis.read_ushort ();+			right = dis.read_ushort ();+			k = dis.read_short ();+						+			kerning.append (new Kern (left, right, k));+		}		+	}+	+	public void process () throws GLib.Error {+		FontData fd = new FontData ();+		uint16 n_pairs = 0;+		+		uint16 gid_left;+		+		uint16 range_shift = 0;+		uint16 entry_selector = 0;+		uint16 search_range = 0;+		+		int i;+		+		uint last_gid_left;+		uint last_gid_right;+		+		if (pairs.get_length () == 0) {+			pairs.fetch_all_pairs ();+		}+		+		fd.add_ushort (0); // version +		fd.add_ushort (1); // n subtables++		fd.add_ushort (0); // subtable version +		+		if (pairs.get_length () > (uint16.MAX - 14) / 6.0) {+			warning ("Too many kerning pairs!"); + 			n_pairs = (uint16) ((uint16.MAX - 14) / 6.0);+		} else {+			n_pairs = (uint16) pairs.get_length ();+		}++		this.kerning_pairs = n_pairs;++		fd.add_ushort (6 * n_pairs + 14); // subtable length+		fd.add_ushort (HORIZONTAL); // subtable flags++		fd.add_ushort (n_pairs);+		+		search_range = 6 * largest_pow2 (n_pairs);+		entry_selector = largest_pow2_exponent (n_pairs);+		range_shift = 6 * n_pairs - search_range;+		+		fd.add_ushort (search_range);+		fd.add_ushort (entry_selector);+		fd.add_ushort (range_shift);++		gid_left = 0;+		i = 0;		+		last_gid_left  = 0;+		last_gid_right  = 0;++		pairs.all_kern ((kr) => {+			Kern k = kr;+			+			try {+				if (k.left != last_gid_left) {+					last_gid_right = 0;+				}+				+				if (unlikely (k.right < last_gid_right)) {+					warning (@"Kerning table is not sorted $(k.right) < $last_gid_right");+				}+				+				if (k.left < last_gid_left || k.right < last_gid_right) {+					warning (@"Kerning table is not sorted. $(k.left) < $last_gid_left");+				}+								+				last_gid_left = k.left;			+				last_gid_right = k.right;+				+				fd.add_ushort (k.left);+				fd.add_ushort (k.right);+				fd.add_short (k.kerning);+			} catch (GLib.Error e) {+				warning (e.message);+			}+		}, n_pairs);++		fd.pad ();+		this.font_data = fd;+	}++	void print_all () {+		print ("All pairs in kern table:\n");+		foreach (Kern k in kerning) {+			print (@"$(k.left)  $(k.right)  $(k.kerning)\n");+		}+	}+}+++}+diff --git a/libbirdfont/OpenFontFormat/PairFormat1.vala b/libbirdfont/OpenFontFormat/PairFormat1.valanew file mode 100644index 0000000..76ad6c5--- /dev/null+++ b/libbirdfont/OpenFontFormat/PairFormat1.vala@@ -0,0 +1,26 @@+/*+    Copyright (C) 2014 Johan Mattsson++    This library is free software; you can redistribute it and/or modify +    it under the terms of the GNU Lesser General Public License as +    published by the Free Software Foundation; either version 3 of the +    License, or (at your option) any later version.++    This library 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 +    Lesser General Public License for more details.+*/+using Math;++namespace BirdFont {+	+/** GSUB pairwise positioning format 1. + * A class that stores kerning information for one letter. + */+public class PairFormat1 : GLib.Object {+	public uint16 left = -1;+	public List<Kern> pairs = new List<Kern> ();+}++}diff --git a/libbirdfont/PreviewTools.vala b/libbirdfont/PreviewTools.valaindex 5627f54..8bf5572 100644--- a/libbirdfont/PreviewTools.vala+++ b/libbirdfont/PreviewTools.vala@@ -49,8 +49,6 @@ public class PreviewTools : ToolCollection  { 	 	/** Export fonts and update html canvas. */ 	public static void update_preview () {-		Font font = BirdFont.get_current_font ();- 		export_fonts ();  		if (!Preview.has_html_document ()) {diff --git a/libbirdfont/SvgFontFormatWriter.vala b/libbirdfont/SvgFontFormatWriter.valaindex 0f7fad2..c7fb5da 100644--- a/libbirdfont/SvgFontFormatWriter.vala+++ b/libbirdfont/SvgFontFormatWriter.vala@@ -86,7 +86,7 @@ class SvgFontFormatWriter : Object  { 		} 		 		// FIXME: ligatures-		KerningClasses.get_instance ().all_pairs ((left, right, k) => {+		KerningClasses.get_instance ().each_pair ((left, right, k) => { 			string l, r; 			Font f = BirdFont.get_current_font (); 			Glyph? gr = f.get_glyph (right);

Contact

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