Commit 5266176
Write and parse ligature subsitutions in GPOS table
Changed files (7)
modifiedlibbirdfont/GlyphRange.valamodifiedlibbirdfont/Ligatures.valamodifiedlibbirdfont/OpenFontFormat/FontData.valadeletedlibbirdfont/OpenFontFormat/Gsub.valaaddedlibbirdfont/OpenFontFormat/GsubTable.valaaddedlibbirdfont/OpenFontFormat/Ligature.valaaddedlibbirdfont/OpenFontFormat/LigatureSet.vala
Diff
@@ -32,7 +32,6 @@ public class GlyphRange { public void add_unassigned (string glyph_name) { unassigned.add (glyph_name);- len++; } public bool is_class () {@@ -24,9 +24,12 @@ public class Ligatures : GLib.Object { public Ligatures () { }-+ + // FIXME: keep ligatures sorted, long strings first public void get_ligatures (LigatureIterator iter) {- iter ("f i", "fi");+ iter ("a f", "af");+ iter ("f f i", "ffi");+ iter ("f i", "fi"); } public void get_single_substitution_ligatures (SingleLigatureIterator iter) {@@ -21,8 +21,7 @@ public class FontData : Object { // Write pointer uint wp = 0;- - // length without padding+ uint32 len = 0; uint32 padding = 0; @@ -42,6 +41,10 @@ public class FontData : Object { } } + public int get_read_pos () {+ return (int) rp;+ }+ public void write_at (uint pos, uint8 new_data) throws GLib.Error requires (pos <= capacity) { deleted file mode 100644@@ -1,108 +0,0 @@-/*- 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.-*/-using Math;--namespace BirdFont {--public class GsubTable : Table {- - GlyfTable glyf_table;- - public GsubTable (GlyfTable glyf_table) {- this.glyf_table = glyf_table;- id = "GSUB";- }- - public override void parse (FontData dis) throws Error {- }-- public void process () throws GLib.Error {- FontData fd = new FontData ();-- 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- 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- - // script table- fd.add_ushort (4); // offset to default language system- fd.add_ushort (0); // number of languages- - // LangSys table - fd.add_ushort (0); // reserved- fd.add_ushort (0); // required features (0xFFFF is none)- fd.add_ushort (1); // number of features- fd.add_ushort (0); // feature index- - // feature table- fd.add_ushort (1); // number of features- - fd.add_tag ("clig"); // feature tag- fd.add_ushort (8); // offset to feature- - fd.add_ushort (0); // feature prameters (null)- fd.add_ushort (1); // number of lookups- fd.add_ushort (0); // lookup indice- - // lookup table- fd.add_ushort (1); // number of lookups- fd.add_ushort (4); // offset to lookup 1-- fd.add_ushort (4); // lookup type // FIXME - fd.add_ushort (0); // lookup flags- fd.add_ushort (1); // number of subtables- fd.add_ushort (8); // array of offsets to subtable- - // ligature substitution subtable- fd.add_ushort (1); // format identifier- fd.add_ushort (8); // offset to coverage- fd.add_ushort (1); // number of ligature set tables- fd.add_ushort (18); // array of offsets to ligature sets- - // coverage- fd.add_ushort (2); // format- fd.add_ushort (1); // num ranges- fd.add_ushort ((uint16) glyf_table.get_gid ("f")); // start gid- fd.add_ushort ((uint16) glyf_table.get_gid ("f")); // end gid- fd.add_ushort (0); // coverage start index- - // ligature- fd.add_ushort (3); // number of ligatures in this set- fd.add_ushort (8); // offset to ligature- fd.add_ushort (14); // offset to ligature- fd.add_ushort (20); // offset to ligature-- fd.add_ushort ((uint16) glyf_table.get_gid ("fi")); // DELETE);- fd.add_ushort (2); // number of components- fd.add_ushort ((uint16) glyf_table.get_gid ("i")); // gid to component -- fd.add_ushort ((uint16) glyf_table.get_gid ("fi")); // DELETE);- fd.add_ushort (2); // number of components- fd.add_ushort ((uint16) glyf_table.get_gid ("i")); // gid to component -- fd.add_ushort ((uint16) glyf_table.get_gid ("fi")); // DELETE);- fd.add_ushort (2); // number of components- fd.add_ushort ((uint16) glyf_table.get_gid ("i")); // gid to component - - fd.pad (); - this.font_data = fd;- }-}--}new file mode 100644@@ -0,0 +1,197 @@+/*+ 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.+*/+using Math;++namespace BirdFont {++public class GsubTable : Table {+ + GlyfTable glyf_table;+ + public GsubTable (GlyfTable glyf_table) {+ this.glyf_table = glyf_table;+ id = "GSUB";+ }+ + public override void parse (FontData dis) throws Error {+ }++ public void process () throws GLib.Error {+ FontData fd = new FontData ();+ Font font = BirdFont.get_current_font ();+ Ligatures ligatures = font.get_ligatures ();+ LigatureSet lig_set;+ LigatureSet last_set;+ FontData set_data;+ Gee.ArrayList<LigatureSet> liga_sets;+ uint16 ligature_pos;+ uint16 table_start;+ + 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+ 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+ + // script table+ fd.add_ushort (4); // offset to default language system+ fd.add_ushort (0); // number of languages+ + // LangSys table + fd.add_ushort (0); // reserved+ fd.add_ushort (0); // required features (0xFFFF is none)+ fd.add_ushort (1); // number of features+ fd.add_ushort (0); // feature index+ + // feature table+ fd.add_ushort (1); // number of features+ + fd.add_tag ("clig"); // feature tag+ fd.add_ushort (8); // offset to feature+ + fd.add_ushort (0); // feature prameters (null)+ fd.add_ushort (1); // number of lookups+ fd.add_ushort (0); // lookup indice+ + // lookup table+ fd.add_ushort (1); // number of lookups+ fd.add_ushort (4); // offset to lookup 1++ fd.add_ushort (4); // lookup type + fd.add_ushort (0); // lookup flags+ fd.add_ushort (1); // number of subtables+ fd.add_ushort (8); // array of offsets to subtable++ // add existing ligatures+ liga_sets = new Gee.ArrayList<LigatureSet> ();+ lig_set = new LigatureSet (glyf_table);+ last_set = new LigatureSet (glyf_table);+ ligatures.get_ligatures ((s, l) => {+ string[] parts = s.split (" ");+ + if (!font.has_glyph (l)) {+ warning (@"Ligature $l does not correspond to a glyph in this font.");+ return;+ }+ + foreach (string p in parts) {+ if (!font.has_glyph (p)) {+ warning (@"Ligature substitution of $p is not possible, the character does have a glyph.");+ return;+ }+ }+ + if (parts.length == 0) {+ warning ("No parts.");+ return;+ }+ + if (last_set.starts_with (parts[0])) {+ last_set.add (new Ligature (l, s));+ } else {+ lig_set = new LigatureSet (glyf_table);+ lig_set.add (new Ligature (l, s));+ liga_sets.add (lig_set);+ last_set = lig_set;+ }+ + });+ + // ligature substitution subtable+ table_start = (uint16) fd.length_with_padding ();++ fd.add_ushort (1); // format identifier+ fd.add_ushort (6 + (uint16) 2 * liga_sets.size); // offset to coverage+ fd.add_ushort ((uint16) liga_sets.size); // number of ligature set tables++ // array of offsets to ligature sets+ uint16 size = 0;+ foreach (LigatureSet l in liga_sets) {+ ligature_pos = 10 + (uint16) liga_sets.size * 4 + size;+ fd.add_ushort (ligature_pos);+ size += (uint16) l.get_set_data ().length_with_padding ();+ }++ // coverage+ fd.add_ushort (1); // format+ fd.add_ushort ((uint16) liga_sets.size);++ // coverage gid:+ foreach (LigatureSet l in liga_sets) {+ fd.add_ushort ((uint16) glyf_table.get_gid (l.get_coverage_char ()));+ }+ + foreach (LigatureSet l in liga_sets) {+ set_data = l.get_set_data ();+ fd.append (set_data);+ }+ + fd.pad ();+ + this.font_data = fd;+ }++ void parse_ligatures (FontData fd, int table_start) {+ fd.seek (table_start);+ + uint16 identifier = fd.read_ushort ();+ + if (identifier != 1) {+ warning (@"Bad identifier expecting 1 found $identifier");+ }+ + uint16 coverage_offset = fd.read_ushort ();+ uint16 num_sets = fd.read_ushort ();+ + Gee.ArrayList<int> liga_set_offsets = new Gee.ArrayList<int> ();+ for (int i = 0; i < num_sets; i++) {+ uint16 liga_set_offset = fd.read_ushort ();+ liga_set_offsets.add (table_start + liga_set_offset);+ }+ + foreach (int liga_set_pos in liga_set_offsets) {+ fd.seek (liga_set_pos);+ parse_ligature_set (fd);+ } + }+ + /** Parse ligature set at the current position. */+ void parse_ligature_set (FontData fd) {+ int liga_start = fd.get_read_pos ();+ uint nliga = fd.read_ushort ();+ Gee.ArrayList<int> offsets = new Gee.ArrayList<int> ();+ + for (uint i = 0; i < nliga; i++) {+ int off = fd.read_ushort ();+ offsets.add (off);+ }++ foreach (int off in offsets) {+ fd.seek (liga_start);+ fd.seek_relative (off);+ uint16 lig = fd.read_ushort ();+ uint16 nlig_comp = fd.read_ushort ();+ + for (int i = 1; i < nlig_comp; i++) {+ uint16 lig_comp = fd.read_ushort ();+ } + }+ }+}++}new file mode 100644@@ -0,0 +1,27 @@+/*+ 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 Ligature : GLib.Object {+ public string ligature = "";+ public string substitution = "";+ + public Ligature (string ligature, string substitution) {+ this.ligature = ligature;+ this.substitution = substitution;+ }+}++}new file mode 100644@@ -0,0 +1,96 @@+/*+ 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 LigatureSet : GLib.Object {+ public Gee.ArrayList<Ligature> ligatures = new Gee.ArrayList<Ligature> ();+ GlyfTable glyf_table;+ + public LigatureSet (GlyfTable gt) {+ glyf_table = gt;+ }+ + public void add (Ligature lig) {+ ligatures.add (lig);+ }+ + public bool starts_with (string s) {+ if (ligatures.size == 0) {+ return false;+ }+ + return ligatures.get (0).substitution.has_prefix (s);+ }+ + public string get_coverage_char () {+ if (ligatures.size == 0) {+ warning ("No ligatures in set.");+ return "";+ }+ + return (!) ligatures.get (0).substitution.get (0).to_string ();+ }+ + public FontData get_set_data () {+ FontData fd, ligature_data;+ uint16 o, pos;+ + fd = new FontData ();++ // number of ligatures in this set+ fd.add_ushort ((uint16) ligatures.size); + + ligature_data = new FontData ();+ foreach (Ligature l in ligatures) {+ // offset to ligatures+ o = 2 + 2 * ligatures.size;+ + pos = (uint16) (o + ligature_data.length_with_padding ());+ + fd.add_ushort (pos);+ add_ligature (ligature_data, l);+ }+ + fd.append (ligature_data);+ + return fd;+ }+ + void add_ligature (FontData fd, Ligature ligature) throws GLib.Error {+ string[] parts = ligature.substitution.split (" ");+ bool first = true;+ int gid = glyf_table.get_gid (ligature.ligature);+ + if (gid == -1) {+ warning (@"No glyph ID for ligature $(ligature.ligature).");+ gid = 0;+ }+ + fd.add_ushort ((uint16) gid);+ + // number of components including the coverage glyph+ fd.add_ushort ((uint16) parts.length); ++ foreach (string p in parts) {+ if (!first) {+ fd.add_ushort ((uint16) glyf_table.get_gid (p));+ }+ + first = false;+ }+ }+}++}