Commit 6022f25
Add support for FK kerning table
Changed files (6)
modifiedlibbirdfont/OpenFontFormat/DirectoryTable.valaaddedlibbirdfont/OpenFontFormat/FkKern.valaaddedlibbirdfont/OpenFontFormat/FkTable.valamodifiedlibbirdfont/OpenFontFormat/FontData.valamodifiedlibbirdfont/OpenFontFormat/KernTable.valamodifiedlibbirdfont/OpenFontFormat/OpenFontFormatReader.vala
Diff
@@ -19,6 +19,7 @@ public class DirectoryTable : OtfTable { public CmapTable cmap_table; public CvtTable cvt_table;+ public FkTable fk_table; public GaspTable gasp_table; public GdefTable gdef_table; public GlyfTable glyf_table;@@ -57,6 +58,7 @@ public class DirectoryTable : OtfTable { name_table = new NameTable (); os_2_table = new Os2Table (glyf_table, hmtx_table, hhea_table); post_table = new PostTable (glyf_table);+ fk_table = new FkTable (glyf_table, kern_table); id = "Directory table"; @@ -96,6 +98,8 @@ public class DirectoryTable : OtfTable { if (tables.size == 0) { tables.add (offset_table); tables.add (this);++ tables.add (fk_table); tables.add (gpos_table); tables.add (gsub_table);@@ -154,8 +158,13 @@ public class DirectoryTable : OtfTable { length = dis.read_ulong (); printd (@"$(tag.str) \toffset: $offset \tlength: $length \tchecksum: $checksum.\n");- - if (tag.str == "cvt") {++ if (tag.str == "FK ") {+ fk_table.id = tag.str;+ fk_table.checksum = checksum;+ fk_table.offset = offset;+ fk_table.length = length;+ } else if (tag.str == "cvt") { cvt_table.id = tag.str; cvt_table.checksum = checksum; cvt_table.offset = offset;@@ -262,8 +271,20 @@ public class DirectoryTable : OtfTable { if (kern_table.has_data ()) { cvt_table.parse (dis); }+ + if (fk_table.has_data ()) {+ fk_table.parse (dis);+ } }- ++ public void parse_fk_table (FontData dis) throws Error {+ if (fk_table.has_data ()) {+ fk_table.parse (dis);+ } else {+ warning ("Fk table is empty.");+ }+ }+ public void parse_kern_table (FontData dis) throws Error { if (kern_table.has_data ()) { kern_table.parse (dis);@@ -352,7 +373,12 @@ public class DirectoryTable : OtfTable { warning ("kern_table has invalid checksum"); valid = false; }- ++ if (fk_table.has_data () && !fk_table.validate (dis)) {+ warning ("fk_table has invalid checksum");+ valid = false;+ }+ if (!gpos_table.validate (dis)) { warning (@"gpos_table has invalid checksum"); new file mode 100644@@ -0,0 +1,45 @@+/*+ Copyright (C) 2022 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 FkKern : GLib.Object {+ public int left;+ public int right;+ public double kerning;+ + public FkKern (int l, int r, double k) {+ if (l < 0) {+ warning ("Negative gid (left)");+ }++ if (r < 0) {+ warning ("Negative gid (right)");+ }+ + left = l;+ right = r;+ kerning = k;+ }+ + public FkKern copy () {+ return new FkKern (left, right, kerning);+ }+ + public string to_string () {+ return @"left: $left, right: $right, kerning: $kerning";+ }+}++}new file mode 100644@@ -0,0 +1,150 @@+/*+ Copyright (C) 2022 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 FkTable : OtfTable {+ GlyfTable glyf_table;+ KernTable kern_table;++ public FkTable (GlyfTable glyf_table, KernTable kern_table) {+ this.glyf_table = glyf_table;+ this.kern_table = kern_table;+ id = "FK ";+ }++ public static int32 to_fixed (double d) {+ int32 val = (int32) Math.floor (d);+ int32 mant = (int32) Math.floor (0x10000 * (d - val));+ val = (val << 16) | mant;+ return val;+ }++ public static double from_fixed (int32 val) {+ return val / 65536.0;+ }+ + public override void parse (FontData dis) throws GLib.Error {+ uint16 version_upper;+ uint16 version_lower;++ uint32 n_pairs;+ + dis.seek (offset);+ + version_upper = dis.read_ushort ();+ version_lower = dis.read_ushort ();++ if (!(version_upper == 1 && version_lower == 0)) {+ warning (@"Expecting version 1.0. Found version: $version_upper.$version_lower");+ return;+ }+ + n_pairs = dis.read_ulong ();+ + printd (@"Pairs in fk table $n_pairs\n");++ uint32 gid1 = -1;+ uint32 gid2 = -1;+ int32 k = -1;+ double kerning = -1;+ + for (uint32 i = 0; i < n_pairs; i++) {+ gid1 = dis.read_ulong ();+ gid2 = dis.read_ulong ();+ k = dis.read_int32 ();+ kerning = from_fixed (k);+ kern_table.fk_kerning.add (new FkKern ((int) gid1, (int) gid2, kerning));+ }+ + if (dis.get_read_pos () != dis.length_with_padding ()) {+ warning (@"Data left in fk table. Read pos $(dis.get_read_pos ()), length: $(dis.length_with_padding ())");+ }+ }+ + public void process () throws GLib.Error {+ FontData fd = new FontData ();+ KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes ();+ + fd.add_ushort (1); // version upper+ fd.add_ushort (0); // version lower++ write_kerning_pairs(fd);++ fd.pad ();+ this.font_data = fd;+ }++ public void write_kerning_pairs (FontData fd) throws GLib.Error { + KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes ();+ Gee.ArrayList<FkKern> pairs = new Gee.ArrayList<FkKern> ();+ + classes.each_pair ((g1, g2, kerning) => {+ int gid1 = glyf_table.get_gid (g1);+ int gid2 = glyf_table.get_gid (g2);+ + if (gid1 == -1) {+ warning (@"Glyph id not found for $g1");+ return;+ }++ if (gid2 == -1) {+ warning (@"Glyph not found for $g2");+ return;+ }+ + pairs.add (new FkKern ((int) gid1, (int) gid2, kerning));+ });++ pairs.sort ((a, b) => {+ FkKern first = (FkKern) a;+ FkKern next = (FkKern) b;+ + if (first.left == next.left) {+ return first.right - next.right;+ }+ + return first.left - next.left;+ });++ uint32 num_pairs = (uint32) pairs.size;+ fd.add_ulong (num_pairs);+ + foreach (FkKern k in pairs) {+ write_pair (fd, k.left, k.right, k.kerning);+ }+ }+ + public void write_pair (FontData fd, int gid1, int gid2, double kerning) throws GLib.Error {+ if (gid1 < 0) {+ warning (@"Negative gid1.");+ throw new FileError.FAILED (@"gid1 is $gid1");+ }++ if (gid2 < 0) {+ warning (@"Negative gid2.");+ throw new FileError.FAILED (@"gid2 is $gid2");+ }+ + int32 fixed_kerning = to_fixed (kerning * HeadTable.UNITS);+ + fd.add_ulong ((uint) gid1); // left gid+ fd.add_ulong ((uint) gid2); // right gid+ fd.add_long (fixed_kerning); // kerning+ }+}+++}+@@ -195,6 +195,15 @@ public class FontData : Object { return f; } + public int32 read_int32 () {+ int32 f;+ f = read () << 8 * 3;+ f += read () << 8 * 2;+ f += read () << 8 * 1;+ f += read () << 8 * 0;+ return f;+ }+ public uint64 read_uint64 () { uint64 f; f = (uint64) read () << 8 * 7;@@ -278,7 +287,18 @@ public class FontData : Object { public void add_ulong (uint32 d) throws Error { add_u32 (d); }++ public void add_long (int32 d) throws Error {+ add_32 (d);+ }++ public void add_32 (int32 i) throws GLib.Error {+ uint32 s = (uint16) (i >> 16); + add_u16 ((uint16) s);+ add_u16 ((uint16) (i - (s << 16)));+ }+ public void add_byte (uint8 b) throws Error { add (b); }@@ -27,7 +27,9 @@ public class KernTable : OtfTable { KernList pairs; // Only used for loading pairs- public Gee.ArrayList<Kern> kerning = new Gee.ArrayList<Kern> (); // TODO: replace with the KernList+ 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) {@@ -109,7 +109,11 @@ public class OpenFontFormatReader : Object { public void parse_head_table () throws Error { directory_table.parse_head_table (font_data); }- ++ public void parse_fk_table () throws Error {+ directory_table.parse_fk_table (font_data);+ }+ public void set_limits () { Font f = OpenFontFormatWriter.font; @@ -144,6 +148,7 @@ public class OpenFontFormatReader : Object { reader.parse_kern_table (); reader.parse_cmap_table (); reader.parse_head_table ();+ reader.parse_fk_table (); kern_table = reader.directory_table.kern_table; cmap_table = reader.directory_table.cmap_table;@@ -152,33 +157,48 @@ public class OpenFontFormatReader : Object { npairs = kern_table.kerning.size; units_per_em = HeadTable.units_per_em;- + foreach (Kern k in kern_table.kerning) { left = cmap_table.get_char (k.left); right = cmap_table.get_char (k.right); kerning = 100 * (k.kerning / units_per_em); - if (left <= 0x1F || right <= 0x1F) {- warning ("Ignoring kerning of control character.");- } else {- if (@"$kerning" != "0") {- bf_kerning.append ("<kerning left=\"");- bf_kerning.append (BirdFontFile.serialize_unichar (left));- bf_kerning.append ("\" ");- bf_kerning.append ("right=\"");- bf_kerning.append (BirdFontFile.serialize_unichar (right));- bf_kerning.append ("\" ");- bf_kerning.append ("hadjustment=\"");- bf_kerning.append (@"$kerning".replace (",", "."));- bf_kerning.append ("\" />\n");- }- }+ add_kerning (bf_kerning, left, right, kerning); }++ foreach (FkKern k in kern_table.fk_kerning) {+ left = cmap_table.get_char (k.left);+ right = cmap_table.get_char (k.right);+ kerning = 100 * (k.kerning / units_per_em);+ + add_kerning (bf_kerning, left, right, kerning);+ }+ + printd (@"FK kerning pairs: $(kern_table.fk_kerning.size)\n"); } catch (GLib.Error e) { warning (@"Failed to parse font. $(e.message)"); } return bf_kerning.str; }++ static void add_kerning (StringBuilder bf_kerning, unichar left, unichar right, double kerning) {+ if (left <= 0x1F || right <= 0x1F) {+ warning ("Ignoring kerning of control character.");+ } else {+ if (@"$kerning" != "0") {+ bf_kerning.append ("<kerning left=\"");+ bf_kerning.append (BirdFontFile.encode ((!) left.to_string ()));+ bf_kerning.append ("\" ");+ bf_kerning.append ("right=\"");+ bf_kerning.append (BirdFontFile.encode ((!) right.to_string ()));+ bf_kerning.append ("\" ");+ bf_kerning.append ("hadjustment=\"");+ bf_kerning.append (@"$kerning".replace (",", "."));+ bf_kerning.append ("\" />\n");+ }+ }+ } }-}++}\ No newline at end of file