Commit e2de178
Write the context for contextual substitutions (ligatures)
Changed files (8)
modifiedlibbirdfont/LigatureList.valamodifiedlibbirdfont/Ligatures.valamodifiedlibbirdfont/OpenFontFormat/ContextualLigature.valaaddedlibbirdfont/OpenFontFormat/ContextualLigatureSet.valamodifiedlibbirdfont/OpenFontFormat/GsubTable.valamodifiedlibbirdfont/OpenFontFormat/Ligature.valamodifiedlibbirdfont/OpenFontFormat/LigatureSet.valamodifiedlibbirdfont/OpenFontFormat/LigatureSetList.vala
Diff
@@ -29,10 +29,10 @@ public class LigatureList : Table { return rows; } - void add_contextual_ligature (string backtrack, string input, string lookahead) {+ void add_contextual_ligature (string ligature, string backtrack, string input, string lookahead) { Font font = BirdFont.get_current_font (); Ligatures ligatures = font.get_ligatures ();- ligatures.add_contextual_ligature (backtrack, input, lookahead);+ ligatures.add_contextual_ligature (ligature, backtrack, input, lookahead); } void add_ligature (string subst, string liga) {@@ -52,13 +52,14 @@ public class LigatureList : Table { TabContent.hide_text_input (); } else if (row.get_index () == NEW_LIGATURE && column == 1) { if (BirdFont.has_argument ("--test")) {- add_contextual_ligature (t_("beginning"), t_("middle"), t_("end"));+ add_contextual_ligature (t_("substitution"), t_("beginning"), t_("middle"), t_("end")); TabContent.hide_text_input (); } } else if (row.has_row_data ()) { i = row.get_index (); cl = (ContextualLigature) ((!) row.get_row_data ()); + /* // FIXME: DELETE if (delete_button) { cl.remove_ligature_at (i); } else if (column == 0) {@@ -66,6 +67,7 @@ public class LigatureList : Table { } else if (column == 2) { cl.set_substitution (i); }+ */ } else if (row.get_index () < ligatures.count ()) { if (ligatures.count () != 0) { if (delete_button) {@@ -87,7 +89,10 @@ public class LigatureList : Table { if (delete_button) { ligatures.remove_contextual_ligatures_at (i); TabContent.hide_text_input ();- } else if (column == 0) {+ }+ + /* FIXME: DELETE+ else if (column == 0) { ligatures.set_beginning (i); } else if (column == 1) { ligatures.set_middle (i);@@ -96,6 +101,7 @@ public class LigatureList : Table { } else if (column == 3) { ligatures.add_substitution_at (i); }+ */ } } @@ -127,20 +133,8 @@ public class LigatureList : Table { }); ligatures.get_contextual_ligatures ((liga) => {- int j;- - row = new Row.columns_4 (liga.backtrack, liga.input, liga.lookahead,- t_("Add Ligature"), i);+ row = new Row.columns_4 (liga.ligatures, liga.backtrack, liga.input, liga.lookahead, i); rows.add (row);- - j = 0;- foreach (Ligature l in liga.ligatures) {- row = new Row.columns_3 (l.ligature, "", l.substitution, j);- row.set_row_data (liga);- rows.add (row);- j++;- }- i++; }); @@ -92,13 +92,7 @@ public class Ligatures : GLib.Object { return_if_fail (0 <= i < contextual_ligatures.size); contextual_ligatures.remove_at (i); }- - /** Add ligaure for a chained substitution. */- public void add_substitution_at (int index) {- return_if_fail (0 <= index < contextual_ligatures.size);- contextual_ligatures.get (index).add_ligature ();- }- + public void set_beginning (int index) { ContextualLigature lig; TextListener listener;@@ -185,8 +179,8 @@ public class Ligatures : GLib.Object { sort_ligatures (); } - public void add_contextual_ligature (string backtrack, string input, string lookahead) {- ContextualLigature l = new ContextualLigature (backtrack, input, lookahead);+ public void add_contextual_ligature (string ligature, string backtrack, string input, string lookahead) {+ ContextualLigature l = new ContextualLigature (ligature, backtrack, input, lookahead); contextual_ligatures.insert (0, l); sort_ligatures (); }@@ -222,10 +216,6 @@ public class Ligatures : GLib.Object { return chars_next - chars_first; });- - foreach (ContextualLigature c in contextual_ligatures) {- c.sort ();- } } } @@ -15,54 +15,29 @@ namespace BirdFont { public class ContextualLigature : GLib.Object {-+ public string backtrack = ""; public string input = ""; public string lookahead = "";+ public string ligatures = ""; - public Gee.ArrayList<Ligature> ligatures;-- public ContextualLigature (string backtrack, string input, string lookahead) {+ /** All arguments are list of glyph names separated by space. */+ public ContextualLigature (string ligatures, string backtrack, string input, string lookahead) { this.backtrack = backtrack; this.input = input; this.lookahead = lookahead;- - ligatures = new Gee.ArrayList<Ligature> ();- }-- public void remove_ligature_at (int i) {- return_if_fail (0 <= i < ligatures.size);- ligatures.remove_at (i);- }-- public void add_ligature () {- Ligature l = new Ligature (t_("ligature"), t_("glyph sequence"));- ligatures.add (l); - }- - public void set_ligature (int i) {- return_if_fail (0 <= i < ligatures.size);- ligatures.get (i).set_ligature ();+ this.ligatures = ligatures; } - public void set_substitution (int i) {- return_if_fail (0 <= i < ligatures.size);- ligatures.get (i).set_substitution (this);- }- - public void sort () {- ligatures.sort ((a, b) => {- Ligature first, next;- int chars_first, chars_next;- - first = (Ligature) a;- next = (Ligature) b;- - chars_first = first.substitution.split (" ").length;- chars_next = next.substitution.split (" ").length;- - return chars_next - chars_first;- }); + public Gee.ArrayList<Ligature> get_ligatures () {+ Gee.ArrayList<Ligature> ligature_list = new Gee.ArrayList<Ligature> ();+ string[] ligatures = ligatures.split (" ");+ + foreach (string ligature_name in ligatures) {+ ligature_list.add (new Ligature (ligature_name)); + }+ + return ligature_list; } } new file mode 100644@@ -0,0 +1,38 @@+/*+ Copyright (C) 2015 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 ContextualLigatureSet : GLib.Object {++ public Gee.ArrayList<ContextualLigature> ligature_context;+ public LigatureSetList ligatures;+ + public ContextualLigatureSet (GlyfTable glyf_table) {+ ligature_context = new Gee.ArrayList<ContextualLigature> (); + add_contextual_ligatures ();+ ligatures = new LigatureSetList.contextual (glyf_table, this);+ }+ + void add_contextual_ligatures () {+ ContextualLigature ligature ;+ LigatureSet s;+ + ligature = new ContextualLigature ("ralt", "a", "r", "t t");+ ligature_context.add (ligature);+ }+}++}@@ -35,7 +35,7 @@ public class GsubTable : OtfTable { uint16 length; LigatureSetList clig;- LigatureSetList contextual;+ ContextualLigatureSet contextual; fd.add_ulong (0x00010000); // table version fd.add_ushort (10); // offset to script list@@ -66,46 +66,50 @@ public class GsubTable : OtfTable { fd.add_ushort (0); // feature prameters (null) fd.add_ushort (2); // number of lookups- fd.add_ushort (1); // lookup chained_context+ fd.add_ushort (2); // lookup chained_context (etc.) The chained context tables are listed here but the actual ligature table is only referenced in the context table fd.add_ushort (0); // lookup clig_subtable clig = new LigatureSetList.clig (glyf_table);- contextual = new LigatureSetList.context (glyf_table);+ contextual = new ContextualLigatureSet (glyf_table); clig_subtable = get_ligature_subtable (clig);- // FIXME: put back contextual ligatures- //chained_context = get_chaining_contextual_substition_subtable ();- //chained_ligatures = get_ligature_subtable (contextual);+ chained_ligatures = get_ligature_subtable (contextual.ligatures);+ chained_context = get_chaining_contextual_substition_subtable (contextual); // lookup table- fd.add_ushort (1); // number of lookups FIXME: should be 3- fd.add_ushort (4); // offset to lookup 1 FIXME: should be 8- //fd.add_ushort (16); // offset to lookup 2- //fd.add_ushort (24); // offset to lookup 3+ fd.add_ushort (3); // number of lookups+ fd.add_ushort (8); // offset to lookup 1 + fd.add_ushort (16); // offset to lookup 2+ fd.add_ushort (24); // offset to lookup 3 + uint16 lookups_end = 3 * 8; length = 0; fd.add_ushort (4); // lookup type fd.add_ushort (0); // lookup flags fd.add_ushort (1); // number of subtables- fd.add_ushort (8 + length); // array of offsets to subtable FIXME: should be 24+ fd.add_ushort (lookups_end + length); // array of offsets to subtable length += (uint16) clig_subtable.length_with_padding ();--/*- fd.add_ushort (6); // lookup type - fd.add_ushort (0); // lookup flags- fd.add_ushort (1); // number of subtables- fd.add_ushort (16 + length); // array of offsets to subtable- length += (uint16) chained_context.length_with_padding ();+ lookups_end -= 8; fd.add_ushort (4); // lookup type fd.add_ushort (0); // lookup flags fd.add_ushort (1); // number of subtables- fd.add_ushort (8 + length); // array of offsets to subtable+ fd.add_ushort (lookups_end + length); // array of offsets to subtable length += (uint16) chained_ligatures.length_with_padding ();-*/ + lookups_end -= 8;++ fd.add_ushort (6); // lookup type + fd.add_ushort (0); // lookup flags+ fd.add_ushort (1); // number of subtables+ fd.add_ushort (lookups_end + length); // array of offsets to subtable+ length += (uint16) chained_context.length_with_padding ();+ lookups_end -= 8;+ + warn_if_fail (lookups_end == 0);+ fd.append (clig_subtable);- // fd.append (chained_context);- // fd.append (chained_ligatures);+ fd.append (chained_ligatures);+ fd.append (chained_context); fd.pad (); @@ -114,39 +118,39 @@ public class GsubTable : OtfTable { FontData get_ligature_subtable (LigatureSetList liga_list) throws GLib.Error { FontData set_data;- Gee.ArrayList<LigatureSet> liga_sets;+ Gee.ArrayList<LigatureSet> ligature_sets; uint16 ligature_pos; uint16 table_start; FontData fd;- liga_sets = liga_list.liga_sets;- + + ligature_sets = liga_list.ligature_sets; fd = new FontData (); // 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+ fd.add_ushort (6 + (uint16) 2 * ligature_sets.size); // offset to coverage+ fd.add_ushort ((uint16) ligature_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;+ foreach (LigatureSet l in ligature_sets) {+ ligature_pos = 10 + (uint16) ligature_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);+ fd.add_ushort ((uint16) ligature_sets.size); // coverage gid:- foreach (LigatureSet l in liga_sets) {+ foreach (LigatureSet l in ligature_sets) { fd.add_ushort ((uint16) glyf_table.get_gid (l.get_coverage_char ())); } - foreach (LigatureSet l in liga_sets) {+ foreach (LigatureSet l in ligature_sets) { set_data = l.get_set_data (); fd.append (set_data); }@@ -155,48 +159,77 @@ public class GsubTable : OtfTable { } // chaining contextual substitution format3- FontData get_chaining_contextual_substition_subtable () throws GLib.Error {+ FontData get_chaining_contextual_substition_subtable (ContextualLigatureSet contexts) throws GLib.Error { FontData fd = new FontData (); + ContextualLigature context = contexts.ligature_context.get (0); //FIXME:+ + Gee.ArrayList<string> backtrack = get_names (context.backtrack);+ Gee.ArrayList<string> input = get_names (context.input);+ Gee.ArrayList<string> lookahead = get_names (context.lookahead);+ + uint16 lookahead_offset, input_offset, backtrack_offset;+ fd.add_ushort (3); // format identifier - fd.add_ushort (1); // backtrack glyph count- // array of offsets to coverage table- fd.add_ushort (20);+ backtrack_offset = 14 + (uint16) (lookahead.size * 2) + (uint16) (input.size * 2) + (uint16) (backtrack.size * 2);+ fd.add_ushort ((uint16) backtrack.size); // backtrack glyph count+ for (uint16 i = 0; i < input.size; i++) {+ fd.add_ushort (backtrack_offset + 6 * i); // array of offsets to coverage table+ } - fd.add_ushort (1); // input glyph count (middle)- // array of offsets to coverage table- fd.add_ushort (20 + 6);+ input_offset = 14 + (uint16) (lookahead.size * 2) + (uint16) (input.size * 2) + (uint16) (backtrack.size * (2 + 6));+ fd.add_ushort ((uint16) input.size); // input glyph count (middle)+ for (uint16 i = 0; i < input.size; i++) {+ fd.add_ushort (input_offset + 6 * i); // array of offsets to coverage table+ } - fd.add_ushort (1); // lookahead glyph count- // array of offsets to coverage table- fd.add_ushort (20 + 2 * 6);-- fd.add_ushort (1); // substitute count, (ligatures)+ lookahead_offset = 14 + (uint16) (lookahead.size * 2) + (uint16) (input.size * (2 + 6)) + (uint16) (backtrack.size * (2 + 6));+ fd.add_ushort ((uint16) lookahead.size); // lookahead glyph count+ for (uint16 i = 0; i < lookahead.size; i++) {+ fd.add_ushort (lookahead_offset + 6 * i); // array of offsets to coverage table+ }+ + fd.add_ushort (1); // substitute count // substitution lookup records fd.add_ushort (0); // glyph sequence index- fd.add_ushort (2); // go to the ligature substitution via lookup table- + fd.add_ushort (1); // go to the ligature substitution via lookup table+ // backtrack coverage table1- fd.add_ushort (1); // format- fd.add_ushort (1); // coverage array length- - // gid array- fd.add_ushort ((uint16) glyf_table.get_gid ("a"));+ if (fd.length_with_padding () != backtrack_offset) {+ warning (@"Wrong backtrack offset: $backtrack_offset != $(fd.length_with_padding ())");+ } + // gid array + foreach (string glyph_name in backtrack) {+ fd.add_ushort (1); // format+ fd.add_ushort ((uint16) backtrack.size); // coverage array length+ fd.add_ushort ((uint16) glyf_table.get_gid (glyph_name));+ }+ // input coverage table1- fd.add_ushort (1); // format- fd.add_ushort (1); // coverage array length+ if (fd.length_with_padding () != input_offset) {+ warning (@"Wrong input offset: $input_offset != $(fd.length_with_padding ())");+ } - // gid array- fd.add_ushort ((uint16) glyf_table.get_gid ("r"));+ // gid array + foreach (string glyph_name in input) {+ fd.add_ushort (1); // format+ fd.add_ushort ((uint16) input.size); // coverage array length+ fd.add_ushort ((uint16) glyf_table.get_gid (glyph_name));+ } // lookahead coverage table1- fd.add_ushort (1); // format- fd.add_ushort (1); // coverage array length- - // gid array- fd.add_ushort ((uint16) glyf_table.get_gid ("t"));+ if (fd.length_with_padding () != lookahead_offset) {+ warning (@"Wrong lookahead offset: $lookahead_offset != $(fd.length_with_padding ())");+ }++ // gid array + foreach (string glyph_name in lookahead) {+ fd.add_ushort (1); // format+ fd.add_ushort ((uint16) lookahead.size); // coverage array length+ fd.add_ushort ((uint16) glyf_table.get_gid (glyph_name));+ } return fd; }@@ -247,6 +280,31 @@ public class GsubTable : OtfTable { } } }++ /** + * @param glyphs Name of glyphs or unicode values separated by space.+ * @return glyph names+ */+ public static Gee.ArrayList<string> get_names (string glyphs) {+ Gee.ArrayList<string> names = new Gee.ArrayList<string> ();+ Font font = BirdFont.get_current_font ();+ string[] parts = glyphs.split (" ");+ + foreach (string p in parts) { + if (p.has_prefix ("U+") || p.has_prefix ("u+")) {+ p = (!) Font.to_unichar (p).to_string ();+ }+ + if (!font.has_glyph (p)) {+ warning (@"The character $p does have a glyph.");+ return new Gee.ArrayList<string> ();+ }+ + names.add (p);+ }+ + return names;+ } } }@@ -18,7 +18,7 @@ public class Ligature : GLib.Object { public string ligature = ""; public string substitution = ""; - public Ligature (string ligature, string substitution) {+ public Ligature (string ligature, string substitution = "") { this.ligature = ligature; this.substitution = substitution; }@@ -44,9 +44,8 @@ public class Ligature : GLib.Object { TabContent.show_text_input (listener); } - public void set_substitution (ContextualLigature? clig = null) {+ public void set_substitution () { TextListener listener;- ContextualLigature? cl = clig; listener = new TextListener (t_("Text"), substitution, t_("Set")); @@ -55,10 +54,6 @@ public class Ligature : GLib.Object { Ligatures l = f.get_ligatures (); substitution = text; l.sort_ligatures ();- - if (cl != null) {- ((!) cl).sort ();- } }); listener.signal_submit.connect (() => {@@ -22,10 +22,10 @@ public class LigatureSet : GLib.Object { glyf_table = gt; } - public void add (Ligature lig) {+ public void add (Ligature lig) { ligatures.add (lig); }- + public bool starts_with (string s) { if (ligatures.size == 0) { return false;@@ -78,7 +78,8 @@ public class LigatureSet : GLib.Object { return fd; }- ++ /** Add ligature to fd */ void add_ligature (FontData fd, Ligature ligature) throws GLib.Error { string[] parts = ligature.substitution.split (" "); bool first = true;@@ -17,77 +17,83 @@ namespace BirdFont { public class LigatureSetList : GLib.Object { - public Gee.ArrayList<LigatureSet> liga_sets;+ public Gee.ArrayList<LigatureSet> ligature_sets;++ LigatureSet lig_set;+ LigatureSet last_set; public LigatureSetList.clig (GlyfTable glyf_table) {- liga_sets = new Gee.ArrayList<LigatureSet> (); - add_clig_ligatures (glyf_table);- }- - public LigatureSetList.context (GlyfTable glyf_table) {- liga_sets = new Gee.ArrayList<LigatureSet> (); - add_contextual_ligatures (glyf_table);- }- - void add_contextual_ligatures (GlyfTable glyf_table) {- LigatureSet lig_set;- + ligature_sets = new Gee.ArrayList<LigatureSet> (); lig_set = new LigatureSet (glyf_table);- lig_set.add (new Ligature ("ralt", "r"));+ last_set = new LigatureSet (glyf_table); - liga_sets.add (lig_set);+ add_clig_ligatures (glyf_table); }- - // create ligature list- void add_clig_ligatures (GlyfTable glyf_table) {- Font font;- Ligatures ligatures;- LigatureSet lig_set;- LigatureSet last_set;- - font = BirdFont.get_current_font ();- ligatures = font.get_ligatures (); + public LigatureSetList.contextual (GlyfTable glyf_table, ContextualLigatureSet ligatures) {+ ligature_sets = new Gee.ArrayList<LigatureSet> (); lig_set = new LigatureSet (glyf_table); last_set = new LigatureSet (glyf_table);- ligatures.get_ligatures ((s, lig) => {- string[] parts = s.split (" ");- string l = lig; - if (l.has_prefix ("U+") || l.has_prefix ("u+")) {- l = (!) Font.to_unichar (l).to_string ();- }- - if (!font.has_glyph (l)) {- warning (@"Ligature $l does not correspond to a glyph in this font.");- return;- }+ add_contextual_ligatures (glyf_table, ligatures);+ }++ void add_clig_ligatures (GlyfTable glyf_table) {+ Font font = BirdFont.get_current_font ();+ Ligatures ligatures = font.get_ligatures (); + + ligatures.get_ligatures ((parts, ligature) => {+ add_ligatures (glyf_table, parts, ligature);+ }); + }++ void add_contextual_ligatures (GlyfTable glyf_table, ContextualLigatureSet ligatures) {+ foreach (ContextualLigature c in ligatures.ligature_context) {+ add_ligatures (glyf_table, "r", c.ligatures); // FIXME: parts = ""+ }+ }+ + // FIXME: what if parts equals "" ? Which set does ligatures becomes a part of?+ public void add_ligatures (GlyfTable glyf_table, string characters, string ligatures) + requires (!is_null (lig_set) && !is_null (last_set)) { - foreach (string p in parts) { - if (p.has_prefix ("U+") || p.has_prefix ("u+")) {- p = (!) Font.to_unichar (p).to_string ();- }- - if (!font.has_glyph (p)) {- warning (@"Ligature substitution of $p is not possible, the character does have a glyph.");- return;- }+ Font font = BirdFont.get_current_font ();+ string[] parts = characters.split (" ");+ string l = ligatures;++ if (l.has_prefix ("U+") || l.has_prefix ("u+")) {+ l = (!) Font.to_unichar (l).to_string ();+ }+ + if (!font.has_glyph (l)) {+ warning (@"Ligature $l does not correspond to a glyph in this font.");+ return;+ }+ + foreach (string p in parts) { + if (p.has_prefix ("U+") || p.has_prefix ("u+")) {+ p = (!) Font.to_unichar (p).to_string (); } - if (parts.length == 0) {- warning ("No parts.");+ if (!font.has_glyph (p)) {+ warning (@"Ligature substitution of $p is not possible, the character does have a glyph."); 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;- }- }); + }+ + if (parts.length == 0) {+ warning ("No parts.");+ return;+ }+ + if (last_set.starts_with (parts[0])) {+ last_set.add (new Ligature (l, characters));+ } else {+ lig_set = new LigatureSet (glyf_table);+ lig_set.add (new Ligature (l, characters));+ ligature_sets.add (lig_set);+ last_set = lig_set;+ } } }