Commit 42d3ddb
More kerning pairs in GPOS extension subtable
Changed files (4)
modifiedlibbirdfont/OpenFontFormat/GposTable.valamodifiedlibbirdfont/OpenFontFormat/KernList.valaaddedlibbirdfont/OpenFontFormat/KernSplitter.valamodifiedlibbirdfont/OpenFontFormat/KerningPair.vala
Diff
@@ -1,5 +1,5 @@ /*- Copyright (C) 2012, 2013, 2014 Johan Mattsson+ Copyright (C) 2012 2013 2014 2017 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 @@ -34,14 +34,14 @@ public class GposTable : OtfTable { this.glyf_table = glyf_table; this.pairs = new KernList (glyf_table); - printd ("Process GPOS\n");+ printd ("Processing GPOS\n"); fd.add_ulong (0x00010000); // table version fd.add_ushort (10); // offset to script list fd.add_ushort (30); // offset to feature list fd.add_ushort (44); // offset to lookup list - // script list ?+ // script list fd.add_ushort (1); // number of items in script list fd.add_tag ("DFLT"); // default script fd.add_ushort (8); // offset to script table from script list@@ -65,33 +65,107 @@ public class GposTable : OtfTable { fd.add_ushort (0); // feature prameters (null) fd.add_ushort (1); // number of lookups fd.add_ushort (0); // lookup index+ + Gee.ArrayList<FontData> pair_set_data = new Gee.ArrayList<FontData>(); + pairs.fetch_all_pairs ();+ + KernSplitter kern_splitter = new KernSplitter (pairs);+ + while (true) {+ if (offset < 0) {+ break;+ }+ + KernList pairs_subset = kern_splitter.get_subset (offset);+ + if (pairs_subset.get_length () == 0) {+ break;+ }+ + FontData pairs = get_pair_pos_format1 (pairs_subset);+ pair_set_data.add (pairs);+ + offset += pairs_subset.get_length ();+ } // lookup table fd.add_ushort (1); // number of lookups fd.add_ushort (4); // offset to lookup 1 - fd.add_ushort (2); // lookup type - fd.add_ushort (0); // lookup flags- fd.add_ushort (1); // number of subtables- fd.add_ushort (8); // array of offsets to subtables+ int16 extention_pos = (int16) (pair_set_data.size * 2); - // MarkFilteringSet + for (int16 index = 0; index < pair_set_data.size; index++) {+ extention_pos += index * 8;+ }+ + int64 pair_set_data_length = 0;+ int64 extension_length = 8;+ + foreach (FontData pair_set in pair_set_data) {+ pair_set_data_length += pair_set.length_with_padding ();+ extension_length += 8;+ }+ + if (pair_set_data.size > 0) {+ FontData last_pair_set = pair_set_data.get (pair_set_data.size - 1);+ uint64 last_externsion_offset = extension_length + pair_set_data_length; - fd.append (get_pair_pos_format1 ());+ last_externsion_offset -= 8;+ last_externsion_offset -= last_pair_set.length ();+ + if (last_externsion_offset > uint32.MAX) {+ warning ("Too manu kerning pairs for the extension positioning table."+ + @"last_externsion_offset: $last_externsion_offset > "+ + @"$(uint32.MAX)");+ pair_set_data.clear ();+ }+ }+ + fd.add_ushort (9); // lookup type + fd.add_ushort (0); // lookup flags+ fd.add_ushort ((uint16) pair_set_data.size); // number of subtables+ + if (pair_set_data.size > 0) {+ for (int j = 0; j < pair_set_data.size; j++) {+ uint16 lookup_size = (uint16) pair_set_data.size * 2;+ uint16 offset_to_extension = (uint16) (6 + lookup_size + j * 8);+ fd.add_ushort (offset_to_extension); // array of offsets to subtables + } + + // extension positioning table+ int k = pair_set_data.size - 1;+ while (extension_length > 0 && k >= 0) {+ FontData pair_set = pair_set_data.get (k);+ pair_set_data_length -= pair_set.length ();+ extension_length -= 8;+ + uint64 externsion_offset = extension_length + pair_set_data_length;+ + if (externsion_offset > uint32.MAX) {+ warning ("Too manu kerning pairs for extension positioning table.");+ }+ + fd.add_ushort (1); // format+ fd.add_ushort (2); // lookup type + fd.add_ulong ((uint32) externsion_offset); // extension offset+ k--;+ }+ + // MarkFilteringSet ++ foreach (FontData pair_set in pair_set_data) {+ fd.append (pair_set);+ } + } fd.pad (); 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 get_pair_pos_format1 (KernList pairs_subset) throws GLib.Error { FontData fd = new FontData (); uint coverage_offset;- uint16 max_pairs_per_table = get_max_pairs (); uint16 num_pairs; int i; uint pair_set_offset;@@ -100,26 +174,19 @@ public class GposTable : OtfTable { uint last_gid_left; uint last_gid_right; uint16 pair_set_count;- - 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;+ num_pairs = (uint16) pairs_subset.get_length (); - pair_set_count = (uint16) pairs.get_length_left (); // FIXME: boundaries+ pair_set_count = (uint16) pairs_subset.get_length_left (); // FIXME: boundaries - coverage_offset = 10 + pairs_offset_length () + pairs_set_length ();+ coverage_offset = 10 + pairs_offset_length (pairs_subset) + pairs_set_length (pairs_subset); if (coverage_offset > uint16.MAX) {- warning (@"Invalid coverage offset. Number of pairs: $(pairs.get_length ())");+ warning (@"Invalid coverage offset." ++ @"Total: $(pairs.get_length ()), " ++ @"subset: $(pairs_subset.get_length ())" ++ @"coverage_offset: $(coverage_offset) > 65535");+ num_pairs = 0; coverage_offset = 10; }@@ -132,11 +199,11 @@ public class GposTable : OtfTable { fd.add_ushort (pair_set_count); // n pairs // pair offsets orderd by coverage index- pair_set_offset = 10 + pairs_offset_length ();+ pair_set_offset = 10 + pairs_offset_length (pairs_subset); written = 0; written_pairs = 0;- pairs.all_pairs_format1 ((k) => {+ pairs_subset.all_pairs_format1 ((k) => { try { if (pair_set_offset > uint16.MAX) { warning ("Invalid offset.");@@ -156,8 +223,8 @@ public class GposTable : OtfTable { } }, num_pairs); - if (unlikely (written != pairs_offset_length ())) {- warning (@"Bad pairs_offset_length () calculated: $(pairs_offset_length ()), real length $written");+ if (unlikely (written != pairs_offset_length (pairs_subset))) {+ warning (@"Bad pairs_offset_length () calculated: $(pairs_offset_length (pairs_subset)), real length $written"); } // pair table @@ -165,7 +232,7 @@ public class GposTable : OtfTable { written = 0; last_gid_left = 0; last_gid_right = 0;- pairs.all_pairs_format1 ((pn) => {+ pairs_subset.all_pairs_format1 ((pn) => { try { PairFormat1 p = pn; uint pairset_length = p.pairs.size;@@ -216,13 +283,13 @@ public class GposTable : OtfTable { } }, num_pairs); - if (unlikely (pairs_set_length () != written)) {- warning (@"Bad pair set length: $(pairs_set_length ()), real length: $written");+ if (unlikely (pairs_set_length (pairs_subset) != written)) {+ warning (@"Bad pair set length: $(pairs_set_length (pairs_subset)), real length: $written"); } 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 ())");+ warning (@"pairs_offset_length: $(pairs_offset_length (pairs_subset)) pairs_set_length: $(pairs_set_length (pairs_subset))"); } // coverage@@ -230,7 +297,7 @@ public class GposTable : OtfTable { fd.add_ushort (pair_set_count); written = 0;- pairs.all_pairs_format1 ((p) => {+ pairs_subset.all_pairs_format1 ((p) => { try { fd.add_ushort (p.left); // gid written += 2;@@ -240,24 +307,24 @@ public class GposTable : OtfTable { }, num_pairs); if (unlikely (written != 2 * pair_set_count)) {- warning ("written != 2 * pair_set_count");+ warning (@"written != 2 * pair_set_count: $written != 2 * $(pair_set_count)"); } return fd; } - public uint pairs_set_length () {+ public static uint pairs_set_length (KernList kerning_list) { uint len = 0; - pairs.all_pairs_format1 ((p) => {+ kerning_list.all_pairs_format1 ((p) => { len += 2 + 4 * p.pairs.size; }); return len; } - public uint pairs_offset_length () {- return 2 * pairs.pairs.size;+ public static uint pairs_offset_length (KernList kerning_list) {+ return 2 * kerning_list.pairs.size; } } @@ -20,15 +20,15 @@ public class KernList : GLib.Object { public delegate void KernIterator (Kern k); public delegate void PairFormat1Iterator (PairFormat1 k); - GlyfTable glyf_table;- uint num_pairs;+ public GlyfTable glyf_table;+ public uint num_pairs; public KernList (GlyfTable glyf_table) { this.glyf_table = glyf_table; num_pairs = 0; pairs = new Gee.ArrayList<PairFormat1> (); }- + /** @return number of pairs. */ public uint fetch_all_pairs () { PairFormat1 current_pairs = new PairFormat1 ();@@ -50,7 +50,7 @@ public class KernList : GLib.Object { 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;@@ -124,6 +124,18 @@ public class KernList : GLib.Object { i++; } }+ + public void all_single_kern (PairFormat1Iterator iter) {+ foreach (PairFormat1 p in pairs) {+ foreach (Kern k in p.pairs) {+ PairFormat1 single = new PairFormat1 ();+ single.left = p.left;+ single.pairs.add (k);+ + iter (single);+ }+ }+ } } }new file mode 100644@@ -0,0 +1,76 @@+/*+ Copyright (C) 2017 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 KernSplitter : GLib.Object {+ public Gee.ArrayList<PairFormat1> pairs;+ private KernList source_list;+ + public KernSplitter (KernList kerning_list) {+ source_list = kerning_list;+ pairs = new Gee.ArrayList<PairFormat1> (); + + kerning_list.all_single_kern((pair) => {+ pairs.add (pair);+ });+ }+ + /** Prevent integer overflow in coverage table of the PairPos1 subtable. */+ public bool is_full (KernList kerning_pairs) {+ uint length = 10;+ length += GposTable.pairs_offset_length (kerning_pairs);+ length += GposTable.pairs_set_length (kerning_pairs);+ + return length > (uint16.MAX - 10);+ }+ + public KernList get_subset (uint offset) {+ int count = 0;+ KernList result = new KernList (source_list.glyf_table);+ + PairFormat1 current = new PairFormat1 ();+ current.left = 0xFFFF;+ + for (uint i = offset; i < pairs.size; i++) {+ PairFormat1 next = pairs.get ((int) i);+ + if (is_full (result)) {+ break;+ }+ + if (next.left != current.left) {+ current = new PairFormat1 ();+ current.left = next.left;+ result.pairs.add (current);+ }+ + if (unlikely (next.pairs.size != 1)) {+ warning ("Splitting kerning pairs failed. "+ + @"next.pairs.size: != $(next.pairs.size)");+ }+ + current.pairs.add (next.pairs.get (0));+ count++;+ }++ foreach (PairFormat1 kerning in result.pairs) {+ result.num_pairs += kerning.pairs.size;+ }+ + return result;+ }+}++}@@ -21,8 +21,7 @@ namespace BirdFont { public class KerningPair : GLib.Object { public Glyph character; public Gee.ArrayList<Kerning> kerning;-- Gee.ArrayList<Glyph> right;+ public Gee.ArrayList<Glyph> right; public KerningPair (Glyph left) { character = left;