Clone in a terminal:
Commit e71a9a3
Johan Mattsson - 2012-08-13T23:48:17+00:00
e71a9a305c4ce07ac38c9abe78736ccd51be3639johan.mattsson.m@gmail.com

ttf kerning table

Changed files (6)

Diff

diff --git a/src/Context.vala b/src/Context.valaindex 7b3d0c6..045cbd0 100644--- a/src/Context.vala+++ b/src/Context.vala@@ -71,12 +71,13 @@ class ContextDisplay : FontDisplay { 			 			foreach (string s in word.glyph) {				 				g = (!) Supplement.get_current_font ().get_glyph (s);	-				kern = get_kerning (prev, s); 				 				if (s == "") { 					continue; 				} 				+				kern = (prev != "") ? get_kerning (prev, s) : 0;+				 				// draw glyph				 				if (g == null) { 					w = 50;@@ -157,6 +158,7 @@ class ContextDisplay : FontDisplay { 				b = s; 				 				if (handle == wi) {+					print (@"SET get $a\n"); 					kern = font.get_kerning (a, b) + val; 					font.set_kerning (a, b, kern); 				}@@ -272,7 +274,7 @@ class ContextDisplay : FontDisplay { 				 				// draw glyph 				g = (!) Supplement.get_current_font ().get_glyph (s);	-				kern = get_kerning (prev, s);+				kern = (prev != "") ? get_kerning (prev, s) : 0; 				 				if (g == null) { 					w = 50;	@@ -332,7 +334,6 @@ class Word { 		glyph = new List <string> (); 		glyph.append (""); 	}- }  }diff --git a/src/Font.vala b/src/Font.valaindex 2f8c5f9..0f4ff31 100644--- a/src/Font.vala+++ b/src/Font.vala@@ -31,8 +31,6 @@ class Font : GLib.Object {  	/** Last unassigned index */ 	int next_unindexed = 0;-		-	public HashTable <string, Kerning> kerning = new HashTable <string, Kerning> (str_hash, str_equal); 	 	public List <string> background_images = new List <string> (); 	@@ -439,38 +437,32 @@ class Font : GLib.Object { 	}  	public double get_kerning (string a, string b) {-		Kerning? kern;-		StringBuilder key = new StringBuilder ();-		-		key.append (a);-		key.append (b);-		-		kern = kerning.lookup (key.str);+		Glyph? gl = get_glyph (a);+		Glyph g; 		-		if (kern != null) {-			return ((!) kern).val;+		if (gl == null) {+			warning (@"glyph \"$a\" does not exist cannot obtain kerning");+			return 0; 		} 		-		return 0;+		g = (!) gl;+		+		return g.get_kerning (b); 	}  	public void set_kerning (string a, string b, double val) {-		Kerning? kern;-		Kerning k;-		StringBuilder key = new StringBuilder ();-		-		key.append (a);-		key.append (b);+		Glyph? gl;+		Glyph g; 		-		kern = kerning.lookup (key.str);+		gl = get_glyph (a); 		-		if (kern != null) {-			k = (!) kern;-			k.val = val;-		} else {-			k = new Kerning (a, b, val);-			kerning.insert (key.str, k);+		if (gl == null) {+			warning ("glyph is not parsed yet cannot add kerning");+			return; 		}+		+		g = (!) gl;+		g.add_kerning (b, val); 	} 		 	public void save_backup () {@@ -582,19 +574,27 @@ class Font : GLib.Object { 				} 			}); 		-			// FIXME: implement kerning in webkit -			kerning.for_each ((key, kern) => {-				try {-					string l = to_hex (kern.left.get_char (0));-					string r = to_hex (kern.right.get_char (0));+			glyph_cache.for_each ((gc) => {+				Glyph glyph = gc.get_current ();+				+				foreach (Kerning k in glyph.kerning) {+					string l, r;+					Glyph? gr = get_glyph (k.glyph_right);+					Glyph glyph_right; 					-					os.put_string (@"<hkern left=\"$l\" right=\"$r\" kerning=\"$(kern.val)\"/>\n");-				} catch (GLib.Error ef) {-					stderr.printf (@"Failed to save $path \n");-					stderr.printf (@"$(ef.message) \n");+					if (gr == null) {+						warning ("kerning glyph that does not exist.");+					}+					+					glyph_right = (!) gr;+					+					l = Font.to_hex_code (glyph.unichar_code);+					r = Font.to_hex_code (glyph_right.unichar_code);+									+					os.put_string (@"<hkern left=\"U+$l\" right=\"U+$r\" kerning=\"$(k.val)\"/>\n"); 				} 			});-						+			 			os.put_string ("</font>"); 			 		} catch (GLib.Error e) {@@ -900,10 +900,9 @@ class Font : GLib.Object { 		string left = ""; 		string right = ""; 		string kern = "";-		Kerning k;+ 		StringBuilder b; 		-		 		for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) { 			attr_name = prop->name; 			attr_content = prop->children->content;@@ -925,9 +924,7 @@ class Font : GLib.Object { 			} 		} 		-		k = new Kerning.from_attribute (left, right, kern);-		-		this.kerning.insert (@"$left$right", k);+		set_kerning (left, right, double.parse (kern)); 	} 	 	private void parse_background (Xml.Node* node) diff --git a/src/Glyph.vala b/src/Glyph.valaindex 12150c3..b094470 100644--- a/src/Glyph.vala+++ b/src/Glyph.vala@@ -89,7 +89,9 @@ class Glyph : FontDisplay { 	 	bool editable = true; 	-	private static List<Glyph> undo_list = new List<Glyph> ();+	static List<Glyph> undo_list = new List<Glyph> ();++	public List<Kerning> kerning = new List<Kerning> ();  	public Glyph (string name, unichar unichar_code = 0) { 		this.name = name;@@ -115,6 +117,32 @@ class Glyph : FontDisplay { 		path_list.append (new Path ()); 	} 	+	public void add_kerning (string right_glyph, double val) {+		Kerning? kl = null;++		foreach (Kerning k in kerning) {+			if (k.glyph_right == right_glyph) {+				kl = k;+			}+		}+		+		if (kl == null) {+			kerning.append (new Kerning (right_glyph, val));+		} else {+			((!)kl).val = val;+		}+	}++	public double get_kerning (string right_glyph) {+		foreach (Kerning k in kerning) {+			if (k.glyph_right == right_glyph) {+				return k.val;+			}+		}+		+		return 0;+	}+		 	public void set_unassigned (bool u) { 		unassigned = u; 	}diff --git a/src/Kerning.vala b/src/Kerning.valaindex 1e10562..af54573 100644--- a/src/Kerning.vala+++ b/src/Kerning.vala@@ -19,21 +19,14 @@ using Cairo; using Xml;  namespace Supplement {-class Kerning {++class Kerning : GLib.Object {+	public string glyph_right; 	public double val;-	public string left;-	public string right; 	-	public Kerning (string l, string r, double v) {+	public Kerning (string right, double v) {+		glyph_right = right; 		val = v;-		left = l;-		right = r;-	}-	-	public Kerning.from_attribute (string l, string r, string xml_attr) {-		val = double.parse (xml_attr); -		left = l;-		right = r; 	} } diff --git a/src/OpenFontFormatWriter.vala b/src/OpenFontFormatWriter.valaindex 79cd9c0..578709f 100644--- a/src/OpenFontFormatWriter.vala+++ b/src/OpenFontFormatWriter.vala@@ -686,6 +686,7 @@ class GlyfTable : Table { 	public LocaTable loca_table;
 	public CmapTable cmap_table; // hack, hack, hack. cmap and post is null when inistialized and set in parse method
 	public PostTable post_table;
+	public KernTable kern_table;
 	
 	public List<uint32> location_offsets; 
 
@@ -712,6 +713,19 @@ class GlyfTable : Table { 		read_lock = new Mutex ();
 	}	
 
+	public int get_gid (string name) {
+		int i = 0;
+		foreach (Glyph g in glyphs) {
+			if (g.name == name) {
+				return i;
+			}
+		
+			i++;
+		}
+		
+		return -1;
+	}
+
 	public int16 get_space_gid () {
 		int16 i = 0;
 		foreach (Glyph g in glyphs) {
@@ -766,10 +780,14 @@ class GlyfTable : Table { 		return (uint16)g.unichar_code; 
 	}
 	
-	int x = 0;
 	public Glyph? read_glyph (string name) {
-		Glyph glyph;
+		Glyph? glyph;
+		Glyph g;
 		int i;
+		KernList kl;
+		string right;
+		double units_per_em;
+		double kern_val;
 		
 		i = post_table.get_gid (name);
 		
@@ -777,23 +795,26 @@ class GlyfTable : Table { 			return null;
 		}
 		
-		return parse_index (i, dis, loca_table, hmtx_table, head_table, post_table);
-	}
-			
-	/** Add this glyph from thread safe callback to the running gui. */
-	void add_glyph (Glyph g) {
-		IdleSource idle = new IdleSource ();
-
-		idle.set_callback (() => {
-			Font font = Supplement.get_current_font ();
-			font.add_glyph_callback (g);
-			return false;
-		});
-
-		idle.attach (null);
+		glyph = parse_index (i, dis, loca_table, hmtx_table, head_table, post_table);
+		
+		if (glyph != null) {
+			printd (@"kern: ");
+			units_per_em = head_table.get_units_per_em ();
+			kl = kern_table.get_all_pairs (i);
+			foreach (Kern k in kl.kernings) {
+				g = (!) glyph;
+				right = post_table.get_name (k.right);
+				kern_val = k.kerning * 1000.0 / units_per_em;
+				g.add_kerning (right, kern_val);
+				
+				printd (@"add kerning $(g.get_name ()) to $right $(k.kerning)\n");
+			}
+		}
+		
+		return glyph;
 	}
 	
-	public void parse (FontData dis, CmapTable cmap_table, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table) throws GLib.Error {
+	public void parse (FontData dis, CmapTable cmap_table, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table, KernTable kern_table) throws GLib.Error {
 		printd (@"loca.size: $(loca.size)\n");
 			
 		Glyph glyph;
@@ -806,6 +827,7 @@ class GlyfTable : Table { 		this.loca_table = loca;
 		this.hmtx_table = hmtx_table;
 		this.head_table = head_table;
+		this.kern_table = kern_table;
 		this.dis = dis;
 		
 		// post_table.print_all ();
@@ -951,8 +973,7 @@ class GlyfTable : Table { 		return glyph;
 	}
 	
-	Glyph parse_next_glyf (FontData dis, unichar character, int gid, out double xmin, out double xmax, double units_per_em) throws Error 
-	{
+	Glyph parse_next_glyf (FontData dis, unichar character, int gid, out double xmin, out double xmax, double units_per_em) throws Error {
 
 		uint16* end_points = null;
 		uint8* instructions = null;
@@ -4311,6 +4332,163 @@ class CvtTable : Table { 
 }
 
+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;
+	}
+}
+
+class KernList : GLib.Object {
+	public List<Kern> kernings;
+	
+	public KernList () {
+		kernings = new List<Kern> ();
+	}
+}
+
+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;
+	
+	List<Kern> kernings = new List<Kern> ();
+	
+	public KernTable (GlyfTable gt) {
+		glyf_table = gt;
+		id = "kern";
+	}
+	
+	public KernList get_all_pairs (int gid) {
+		KernList kl = new KernList ();
+		
+		foreach (Kern k in kernings) {
+			if (k.left == gid) {
+				kl.kernings.append (k);
+			}
+		}
+		
+		return kl;
+	}
+	
+	public override void parse (FontData dis) {
+		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 kerning;
+		
+		for (int i = 0; i < n_pairs; i++) {
+			left = dis.read_ushort ();
+			right = dis.read_ushort ();
+			kerning = dis.read_short ();
+			
+			printd (@"gid $left -> gid $right  kern $kerning\n");
+			
+			kernings.append (new Kern (left, right, kerning));
+		}		
+	}
+	
+	public void process () {
+		FontData fd = new FontData ();
+		uint16 n_pairs = 0;
+		
+		uint16 gid_left;
+		int gid_right;
+		
+		uint16 range_shift = 0;
+		uint16 entry_selector = 0;
+		uint16 search_range = 0;
+		
+		fd.add_ushort (0); // version 
+		fd.add_ushort (1); // n subtables
+
+		fd.add_ushort (0); // subtable version 
+
+		foreach (Glyph g in glyf_table.glyphs) {
+			foreach (Kerning k in g.kerning) {
+				n_pairs++;
+			}
+		}
+		
+		fd.add_ushort (6 * n_pairs + 14); // subtable length
+		fd.add_ushort (HORIZONTAL); // subtable flags
+
+		fd.add_ushort (n_pairs);
+		
+		fd.add_ushort (search_range);
+		fd.add_ushort (entry_selector);
+		fd.add_ushort (range_shift);
+
+		gid_left = 0;
+		foreach (Glyph g in glyf_table.glyphs) {
+			
+			foreach (Kerning k in g.kerning) {
+				gid_right = glyf_table.get_gid (k.glyph_right);
+				
+				if (gid_right == -1) {
+					warning ("right glyph not found in kerning table");
+				}
+				
+				fd.add_ushort (gid_left);
+				fd.add_ushort ((uint16)gid_right);
+				fd.add_short ((int16)k.val);
+			}
+			
+			gid_left++;
+		}
+
+		fd.pad ();
+		this.font_data = fd;
+	}
+
+}
+
 /** Table with list of tables sorted by table tag. */
 class DirectoryTable : Table {
 	
@@ -4322,6 +4500,7 @@ class DirectoryTable : Table { 	public HeadTable head_table;
 	public HheaTable hhea_table;
 	public HmtxTable hmtx_table;
+	public KernTable kern_table;
 	public MaxpTable maxp_table;
 	public NameTable name_table;
 	public Os2Table  os_2_table;
@@ -4344,6 +4523,7 @@ class DirectoryTable : Table { 		head_table = new HeadTable (glyf_table);
 		hmtx_table = new HmtxTable (head_table, glyf_table);
 		hhea_table = new HheaTable (glyf_table, head_table, hmtx_table);
+		kern_table = new KernTable (glyf_table);
 		maxp_table = new MaxpTable (glyf_table);
 		name_table = new NameTable ();
 		os_2_table = new Os2Table (); 
@@ -4367,7 +4547,7 @@ class DirectoryTable : Table { 		head_table.process ();
 		loca_table.process (glyf_table, head_table);
 		post_table.process ();
-		
+		kern_table.process ();
 		
 		offset_table.process ();
 		process_directory (); // this table
@@ -4387,6 +4567,7 @@ class DirectoryTable : Table { 			tables.append (head_table);
 			tables.append (hhea_table);
 			tables.append (hmtx_table);
+			tables.append (kern_table);
 			tables.append (loca_table);
 			tables.append (maxp_table);
 			tables.append (name_table);
@@ -4487,8 +4668,12 @@ class DirectoryTable : Table { 				post_table.checksum = checksum;
 				post_table.offset = offset;
 				post_table.length = length;
+			} else if (tag.str == "kern") {
+				kern_table.id = tag.str;
+				kern_table.checksum = checksum;
+				kern_table.offset = offset;
+				kern_table.length = length;
 			}
-			
 		}
 		
 		// FIXA: delete
@@ -4516,7 +4701,8 @@ class DirectoryTable : Table { 		loca_table.parse (dis, head_table, maxp_table);
 		hmtx_table.parse (dis, hhea_table, loca_table);
 		cmap_table.parse (dis);
-		glyf_table.parse (dis, cmap_table, loca_table, hmtx_table, head_table, post_table);
+		kern_table.parse (dis);
+		glyf_table.parse (dis, cmap_table, loca_table, hmtx_table, head_table, post_table, kern_table);
 		gasp_table.parse (dis);
 		
 		cvt_table.parse (dis);
@@ -4581,6 +4767,12 @@ class DirectoryTable : Table { 				warning ("post_table has invalid checksum");
 				valid = false;
 			}
+			
+			if (!kern_table.validate (dis)) {
+				warning ("kern_table has invalid checksum");
+				valid = false;
+			}
+			
 		} catch (GLib.Error e) {
 			warning (e.message);
 			valid = false;
@@ -4698,11 +4890,8 @@ class DirectoryTable : Table { 	
 }
 
-// developers could benifit from error detection and validation at a fine grained level in
-// this class. At some later point should this code present meaningfull info to the user but 
-// for now is the approach just to put a lot of info in the console if we need to do some debugging.
 void printd (string s) {
-	print (s);
+	// print (s);
 }
 
 }
diff --git a/src/SvgFontFormatWriter.vala b/src/SvgFontFormatWriter.valaindex c48eaec..2b8c2a0 100644--- a/src/SvgFontFormatWriter.vala+++ b/src/SvgFontFormatWriter.vala@@ -86,19 +86,35 @@ class SvgFontFormatWriter : Object  { 				} 			} 		}-		-		font.kerning.for_each ((key, kern) => {-			try {-				string l = Font.to_hex_code (kern.left.get_char (0));-				string r = Font.to_hex_code (kern.right.get_char (0));++		while (true) {+			g = font.get_glyph_indice (indice++);+			+			if (g == null) {+				break;+			}+			+			glyph = (!) g;+			+			foreach (Kerning k in glyph.kerning) {+				string l, r;+				Font f = Supplement.get_current_font ();+				Glyph? gr = f.get_glyph (k.glyph_right);+				Glyph glyph_right;+				+				if (gr == null) {+					warning ("kerning glyph that does not exist.");+				}+				+				glyph_right = (!) gr; 				-				os.put_string (@"<hkern u1=\"&#x$l;\" u2=\"&#x$r;\" k=\"$(kern.val)\"/>\n");-			} catch (GLib.Error ef) {-				stderr.printf (@"Failed export font \n");-				stderr.printf (@"$(ef.message) \n");+				l = Font.to_hex_code (glyph.unichar_code);+				r = Font.to_hex_code (glyph_right.unichar_code);+								+				os.put_string (@"<hkern u1=\"&#x$l;\" u2=\"&#x$r;\" k=\"$(k.val)\"/>\n"); 			}-		});-		+		}		+ 		put ("</font>"); 		put ("</defs>"); 		put ("</svg>");

Contact

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