Clone in a terminal:
KernTable.vala
/*
	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 : OtfTable {
	
	public const uint16 HORIZONTAL = 1;
	public const uint16 MINIMUM = 1 << 1;
	public const uint16 CROSS_STREAM = 1 << 2;
	public const uint16 OVERRIDE = 1 << 3;
	public const uint16 FORMAT = 1 << 8;
	
	GlyfTable glyf_table;
	
	KernList pairs;
	
	// Only used for loading pairs
	public Gee.ArrayList<Kern> kerning = new Gee.ArrayList<Kern> ();
	public Gee.ArrayList<FkKern> fk_kerning = new Gee.ArrayList<FkKern> (); // Also only used for loading pairs
		
	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.add (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;
	}
}


}

Contact

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