Commit a05227b
Add unassigned glyphs (ligatures) to ttf fonts and kerning tab.
Changed files (19)
modifiedlibbirdfont/Font.valamodifiedlibbirdfont/Glyph.valamodifiedlibbirdfont/GlyphCollection.valamodifiedlibbirdfont/GlyphRange.valamodifiedlibbirdfont/GlyphSequence.valamodifiedlibbirdfont/KerningClasses.valamodifiedlibbirdfont/KerningDisplay.valamodifiedlibbirdfont/KerningList.valamodifiedlibbirdfont/LigatureList.valamodifiedlibbirdfont/Ligatures.valamodifiedlibbirdfont/OpenFontFormat/CmapSubtableFormat0.valamodifiedlibbirdfont/OpenFontFormat/CmapSubtableFormat12.valamodifiedlibbirdfont/OpenFontFormat/CmapSubtableFormat4.valamodifiedlibbirdfont/OpenFontFormat/DirectoryTable.valamodifiedlibbirdfont/OpenFontFormat/GlyfTable.valamodifiedlibbirdfont/OpenFontFormat/Gsub.valamodifiedlibbirdfont/OpenFontFormat/LocaTable.valamodifiedlibbirdfont/SvgFont.valamodifiedlibbirdxml/Xml.vala
Diff
@@ -93,6 +93,8 @@ public class Font : GLib.Object { BirdFontPart bfp_file; public Gee.ArrayList<Glyph> deleted_glyphs;++ Ligatures ligatures_substitution; public Font () { postscript_name = "Typeface";@@ -122,6 +124,11 @@ public class Font : GLib.Object { bfp_file = new BirdFontPart (this); deleted_glyphs = new Gee.ArrayList<Glyph> ();+ ligatures_substitution = new Ligatures ();+ }++ public Ligatures get_ligatures () {+ return ligatures_substitution; } public void set_weight (string w) {@@ -391,9 +398,7 @@ public class Font : GLib.Object { return; } - if (glyph_collection.get_name () != "") {- glyph_name.insert (glyph_collection.get_name (), glyph_collection); - }+ glyph_name.insert (glyph_collection.get_name (), glyph_collection); if (glyph_collection.get_unicode () != "") { glyph_cache.insert ((!) glyph_collection.get_unicode (), glyph_collection);@@ -424,11 +429,9 @@ public class Font : GLib.Object { public void delete_glyph (GlyphCollection glyph) { glyph_cache.remove (glyph.get_unicode ());+ glyph_cache.remove (glyph.get_name ()); glyph_name.remove (glyph.get_name ());- - if (glyph.length () > 0) {- ligature.remove (glyph.get_current ().get_ligature_string ());- }+ ligature.remove (glyph.get_current ().get_name ()); foreach (Glyph g in glyph.get_version_list ().glyphs) { deleted_glyphs.add (g);@@ -82,9 +82,6 @@ public class Glyph : FontDisplay { string glyph_sequence = ""; bool open = true;-- bool ligature = false;- string substitution = ""; public static Glyph? background_glyph = null; @@ -140,33 +137,6 @@ public class Glyph : FontDisplay { undo_list.clear (); redo_list.clear (); }- - public string get_ligature_string () {- return substitution;- }- - public GlyphSequence get_ligature () {- int index = 0;- unichar c;- GlyphSequence gs = new GlyphSequence ();- Font font = BirdFont.get_current_font ();- StringBuilder s;- Glyph? g;- - while (substitution.get_next_char (ref index, out c)) {- s = new StringBuilder ();- s.append_unichar (c);- g = font.get_glyph (s.str);- - if (g == null) {- warning ("Glyph for $(s.str) does not exsist.");- } else {- gs.glyph.add (g);- }- }-- return gs; - } public void set_empty_ttf (bool e) { empty = e;@@ -175,15 +145,6 @@ public class Glyph : FontDisplay { public bool is_empty_ttf () { return empty; }- - public void set_ligature_substitution (string glyph_sequence) {- ligature = true;- substitution = glyph_sequence;- }- - public bool is_ligature () {- return ligature;- } public void clear_active_paths () { active_paths.clear ();@@ -1645,10 +1606,7 @@ public class Glyph : FontDisplay { g.background_image = ((!) background_image).copy (); } - g.ligature = ligature;- g.substitution = substitution; g.empty = empty;- g.open = open; return g;@@ -114,6 +114,7 @@ public class GlyphCollection : GLib.Object { } n.versions.set_selected_version (versions.current_version_id);+ n.unassigned = unassigned; return n; }@@ -1,5 +1,5 @@ /*- Copyright (C) 2012 Johan Mattsson+ Copyright (C) 2012 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 @@ -30,6 +30,11 @@ public class GlyphRange { unassigned = new Gee.ArrayList<string> (); } + public void add_unassigned (string glyph_name) {+ unassigned.add (glyph_name);+ len++;+ }+ public bool is_class () { return range_is_class || len > 1; }@@ -157,7 +162,7 @@ public class GlyphRange { } else if (w == "ampersand") { add_single ('&'); } else {- throw new MarkupError.PARSE (@"$w is not a single letter or a unicode range.");+ unassigned.add (w); } } }@@ -169,7 +174,6 @@ public class GlyphRange { bool first = true; StringBuilder s = new StringBuilder (); foreach (UniRange u in ranges) {- if (!first) { s.append (" "); }@@ -184,6 +188,16 @@ public class GlyphRange { first = false; }++ foreach (string ur in unassigned) {+ if (!first) {+ s.append (" ");+ }+ + s.append (ur);+ + first = false;+ } return s.str; } @@ -226,8 +240,7 @@ public class GlyphRange { } if (s.char_count () > 1) {- warning (@"Expecting a single glyph ($s)");- return s;+ return s; // ligature } return get_serialized_char (s.get_char (0));@@ -424,7 +437,13 @@ public class GlyphRange { public bool has_character (string c) { unichar s;- string uns = unserialize (c);+ string uns;+ + if (unassigned.index_of (c) != -1) {+ return true;+ }+ + uns = unserialize (c); if (uns.char_count () != 1) { warning (@"Expecting a single character got $c");@@ -31,43 +31,38 @@ public class GlyphSequence { */ public GlyphSequence process_ligatures () { // FIXME add range to ligature- GlyphSequence ligatures = new GlyphSequence ();+ GlyphSequence ligature_sequence = new GlyphSequence (); Font font = BirdFont.get_current_font ();- Glyph liga;- GlyphCollection? gc; bool has_range = false;+ Ligatures ligatures; foreach (Glyph? g in glyph) {- ligatures.glyph.add (g);+ ligature_sequence.glyph.add (g); } foreach (GlyphRange? r in ranges) { - ligatures.ranges.add (r);+ ligature_sequence.ranges.add (r); if (r != null) { has_range = true; } } - // FIXME: ligatures make this list invalid // skip ligature substitution if this sequence contains ranges if (has_range) {- return ligatures;+ return ligature_sequence; } - - for (uint i = 0; ; i++) {- gc = font.get_ligature (i);- - if (gc == null) {- break;- }-- liga = ((!) gc).get_current (); - ligatures.replace (liga.get_ligature (), liga);- i++;+ ligatures = font.get_ligatures ();+ ligatures.get_single_substitution_ligatures ((substitute, ligature) => {+ ligature_sequence.replace (substitute, ligature.get_current ());+ });+ + ligature_sequence.ranges.clear ();+ foreach (Glyph? g in ligature_sequence.glyph) {+ ligature_sequence.ranges.add (null); } - return ligatures;+ return ligature_sequence; } void replace (GlyphSequence old, Glyph replacement) {@@ -118,8 +113,7 @@ public class GlyphSequence { i++; } - glyph = new_list;- + glyph = new_list; } } @@ -89,8 +89,8 @@ public class KerningClasses : GLib.Object { public void set_kerning_for_single_glyphs (string le, string ri, double k) { string left = GlyphRange.serialize (le); string right = GlyphRange.serialize (ri);- string cleft = (!)GlyphRange.unserialize (left).get_char ().to_string ();- string cright = (!)GlyphRange.unserialize (right).get_char ().to_string ();+ string cleft = (!)GlyphRange.unserialize (left);+ string cright = (!)GlyphRange.unserialize (right); // FIXME: get_char? if (protect_map) { warning ("Map is protected.");@@ -258,7 +258,7 @@ public class KerningClasses : GLib.Object { return classes_kerning.get (i).val; } }- + return 0; } @@ -319,13 +319,11 @@ public class KerningDisplay : FontDisplay { if (handle == wi && row_index == 0) { if (wi >= word_with_ligatures.ranges.size) {- warning (@"$wi > $(word_with_ligatures.ranges.size) Number of glyphs: $(word_with_ligatures.glyph.size)"); return false; } return_val_if_fail (wi - 1 >= 0, false); if (word_with_ligatures.ranges.size != word_with_ligatures.glyph.size) {- warning (@"ranges and glyphs does not match. $(word_with_ligatures.ranges.size) != $(word_with_ligatures.glyph.size)"); return false; } @@ -423,7 +421,6 @@ public class KerningDisplay : FontDisplay { n = grr.get_all_ranges (); has_kerning = classes.has_kerning (f, n); undo_items.add (new UndoItem (f, n, kern, has_kerning));- redo_items.clear (); first_update = false; }@@ -438,6 +435,7 @@ public class KerningDisplay : FontDisplay { /** Class based gpos kerning. */ public double get_kerning_for_pair (string a, string b, GlyphRange? gr_left, GlyphRange? gr_right) {+ double k; GlyphRange grl, grr; try { if (gr_left == null) {@@ -467,10 +465,10 @@ public class KerningDisplay : FontDisplay { } if (gr_left == null && gr_right == null) {- return KerningClasses.get_instance ().get_kerning (a, b);+ k = KerningClasses.get_instance ().get_kerning (a, b);+ return k; } } catch (MarkupError e) {- // FIXME: unassigned glyphs and ligatures warning (e.message); } @@ -889,14 +887,14 @@ public class KerningDisplay : FontDisplay { r = GlyphRange.unserialize (ui.next); try {+ glyph_range_first = new GlyphRange ();+ glyph_range_next = new GlyphRange ();+ + glyph_range_first.parse_ranges (ui.first);+ glyph_range_next.parse_ranges (ui.next);+ if (!ui.has_kerning) {- if (l.char_count () > 1 || r.char_count () > 1) {- glyph_range_first = new GlyphRange ();- glyph_range_next = new GlyphRange ();- - glyph_range_first.parse_ranges (ui.first);- glyph_range_next.parse_ranges (ui.next);- + if (glyph_range_first.is_class () || glyph_range_next.is_class ()) { redo_state.first = glyph_range_first.get_all_ranges (); redo_state.next = glyph_range_next.get_all_ranges (); redo_state.has_kerning = true;@@ -918,7 +916,7 @@ public class KerningDisplay : FontDisplay { classes.delete_kerning_for_pair (ui.first, ui.next); }- } else if (ui.first.char_count () > 1 || ui.next.char_count () > 1) {+ } else if (glyph_range_first.is_class () || glyph_range_next.is_class ()) { glyph_range_first = new GlyphRange (); glyph_range_next = new GlyphRange (); @@ -938,7 +936,6 @@ public class KerningDisplay : FontDisplay { classes.set_kerning (glyph_range_first, glyph_range_next, ui.kerning); } else {- redo_state.first = ui.first; redo_state.next = ui.next; redo_state.has_kerning = true;@@ -173,16 +173,16 @@ public class KerningList : FontDisplay { l = GlyphRange.unserialize (left); r = GlyphRange.unserialize (right);+ + glyph_range_first = new GlyphRange ();+ glyph_range_next = new GlyphRange (); + glyph_range_first.parse_ranges (left);+ glyph_range_next.parse_ranges (right);+ try { if (left != "" && right != "") {- - if (l.char_count () > 1 || r.char_count () > 1) {- glyph_range_first = new GlyphRange ();- glyph_range_next = new GlyphRange ();- - glyph_range_first.parse_ranges (left);- glyph_range_next.parse_ranges (right);+ if (glyph_range_first.is_class () || glyph_range_next.is_class ()) { kerning = classes.get_kerning_for_range (glyph_range_first, glyph_range_next); class_index = classes.get_kerning_item_index (glyph_range_first, glyph_range_next);@@ -280,14 +280,14 @@ public class KerningList : FontDisplay { } ui = undo_items.get (undo_items.size - 1);++ glyph_range_first = new GlyphRange ();+ glyph_range_next = new GlyphRange (); - if (ui.first.char_count () > 1 || ui.next.char_count () > 1) {- glyph_range_first = new GlyphRange ();- glyph_range_next = new GlyphRange ();- - glyph_range_first.parse_ranges (ui.first);- glyph_range_next.parse_ranges (ui.next);- + glyph_range_first.parse_ranges (ui.first);+ glyph_range_next.parse_ranges (ui.next);+ + if (glyph_range_first.is_class () || glyph_range_next.is_class ()) { classes.set_kerning (glyph_range_first, glyph_range_next, ui.kerning, ui.class_priority); } else { classes.set_kerning_for_single_glyphs (ui.first, ui.next, ui.kerning);@@ -19,18 +19,16 @@ namespace BirdFont { public class LigatureList : FontDisplay { - Ligatures ligatures;- int scroll = 0; int visible_rows = 0; WidgetAllocation allocation; public LigatureList () { allocation = new WidgetAllocation ();- ligatures = new Ligatures (); } public override void draw (WidgetAllocation allocation, Context cr) {+ Ligatures ligatures = BirdFont.get_current_font ().get_ligatures (); int y = 20; int s = 0; bool color = (scroll % 2) == 0;@@ -90,6 +88,7 @@ public class LigatureList : FontDisplay { } public override void scroll_wheel_down (double x, double y) {+ Ligatures ligatures = BirdFont.get_current_font ().get_ligatures (); uint liga = ligatures.count (); scroll += 3; @@ -122,6 +121,7 @@ public class LigatureList : FontDisplay { } public void update_scrollbar () {+ Ligatures ligatures = BirdFont.get_current_font ().get_ligatures (); uint rows = ligatures.count (); if (rows == 0 || visible_rows == 0) {@@ -134,6 +134,7 @@ public class LigatureList : FontDisplay { } public override void scroll_to (double percent) {+ Ligatures ligatures = BirdFont.get_current_font ().get_ligatures (); uint liga = ligatures.count (); scroll = (int) (percent * liga); @@ -20,6 +20,7 @@ namespace BirdFont { public class Ligatures : GLib.Object { public delegate void LigatureIterator (string substitution, string ligature);+ public delegate void SingleLigatureIterator (GlyphSequence substitution, GlyphCollection ligature); public Ligatures () { }@@ -27,10 +28,43 @@ public class Ligatures : GLib.Object { public void get_ligatures (LigatureIterator iter) { iter ("f i", "fi"); }- ++ public void get_single_substitution_ligatures (SingleLigatureIterator iter) {+ get_ligatures ((substitution, ligature) => {+ Font font = BirdFont.get_current_font ();+ GlyphCollection? gc;+ GlyphCollection li;+ GlyphSequence gs;+ string[] subst_names = substitution.split (" ");+ + gc = font.get_glyph_collection_by_name (ligature);+ + if (gc == null) {+ return;+ }+ + li = (!) gc;+ + gs = new GlyphSequence ();+ foreach (string s in subst_names) {+ gc = font.get_glyph_collection_by_name (s);+ + if (gc == null) {+ return;+ }+ + gs.glyph.add (((!) gc).get_current ());+ }+ + iter (gs, li);+ });+ }+ public int count () { return 1; }+ + } }@@ -37,7 +37,7 @@ public class CmapSubtableFormat0 : GLib.Object { uint8 get_gid_for_unichar (unichar c, GlyfTable glyf_table) { uint32 indice = 0; foreach (GlyphCollection g in glyf_table.glyphs) {- if (g.get_unicode_character () == c) {+ if (g.get_unicode_character () == c && !g.is_unassigned ()) { return (indice <= uint8.MAX) ? (uint8) indice : 0; } indice++;@@ -24,7 +24,7 @@ public class CmapSubtableFormat12 : GLib.Object { GlyphRange glyph_range = new GlyphRange (); Gee.ArrayList<UniRange> ranges; FontData fd = new FontData ();- uint32 first_assigned = 1;+ uint32 first_assigned = 1 + glyf_table.number_of_unassigned_glyphs; uint32 indice; foreach (GlyphCollection g in glyf_table.glyphs) {@@ -188,7 +188,7 @@ public class CmapSubtableFormat4 : GLib.Object { uint16 gid_length = 0; uint32 indice;- uint32 first_assigned = 1;+ uint32 first_assigned = 1 + glyf_table.number_of_unassigned_glyphs; foreach (GlyphCollection g in glyf_table.glyphs) { if (!g.is_unassigned () && g.get_unicode_character () < 0xFFFF) {@@ -66,9 +66,9 @@ public class DirectoryTable : Table { public void process () throws GLib.Error { // generate font data glyf_table.process ();+ gsub_table.process (); gasp_table.process (); gdef_table.process ();- // FIXME: implement gsub_table.process (); cmap_table.process (glyf_table); cvt_table.process (); hmtx_table.process ();@@ -92,9 +92,7 @@ public class DirectoryTable : Table { tables.add (this); tables.add (gpos_table);- - // FIXME: implement it- // tables.append (gsub_table);+ //tables.add (gsub_table); tables.add (os_2_table); @@ -109,7 +107,6 @@ public class DirectoryTable : Table { tables.add (hhea_table); tables.add (hmtx_table); - // FIXME: Remove the kern table. // It looks like the old kerning table is no longer needed // since the most browsers uses the GPOS table // but Windows does not accept fonts without a kern table.@@ -41,11 +41,9 @@ public class GlyfTable : Table { public Gee.ArrayList<uint32> location_offsets; - // a list of glyphs sorted in the order we expect to find them in a- // ttf font. notdef is the firs glyph followed by null and nonmarkingreturn.- // after that will all assigned glyphs appear in sorted (unicode) order, all - // remaining unassigned glyphs will be added in the last section of the file. + // sorted array of glyphs public Gee.ArrayList<GlyphCollection> glyphs;+ public uint number_of_unassigned_glyphs = 0; uint16 max_points = 0; uint16 max_contours = 0;@@ -141,7 +139,8 @@ public class GlyfTable : Table { GlyphCollection? gcn; Font font = OpenFontFormatWriter.get_current_font (); uint32 indice;- Gee.ArrayList<Glyph> unassigned_glyphs;+ Gee.ArrayList<GlyphCollection> unassigned_glyphs;+ bool unassigned; // add notdef character and other special chars first glyphs.add (font.get_not_def_character ());@@ -149,16 +148,25 @@ public class GlyfTable : Table { glyphs.add (font.get_nonmarking_return ()); glyphs.add (font.get_space ()); - unassigned_glyphs = new Gee.ArrayList<Glyph> ();+ unassigned_glyphs = new Gee.ArrayList<GlyphCollection> (); if (font.get_glyph_indice (0) == null) { warning ("No glyphs in font."); }- + // add glyphs, first all assigned then the unassigned ones for (indice = 0; (gcn = font.get_glyph_collection_indice (indice)) != null; indice++) { gc = (!) gcn; g = gc.get_current ().copy ();+ g.remove_empty_paths ();+ unassigned = gc.is_unassigned ();+ + gc = new GlyphCollection (gc.get_unicode_character (), gc.get_name ());+ gc.add_glyph (g);++ if (unassigned) {+ unassigned_glyphs.add (gc);+ } if (g.unichar_code <= 27) { // skip control characters continue;@@ -171,24 +179,16 @@ public class GlyfTable : Table { if (g.name == ".notdef") { continue; }- - g.remove_empty_paths (); if (!gc.is_unassigned ()) { glyphs.add (gc);- } else {- printd ("Adding unassigned glyph.");- unassigned_glyphs.add (g); } } - // FIXME: ligatures- /*- foreach (Glyph ug in unassigned_glyphs) {- glyphs.append (ug);+ foreach (GlyphCollection ug in unassigned_glyphs) {+ glyphs.insert (1, ug);+ number_of_unassigned_glyphs++; }- */- } public void process_glyph (Glyph g, FontData fd) throws GLib.Error {@@ -342,7 +342,7 @@ public class GlyfTable : Table { Glyph parse_index (int index, FontData dis, LocaTable loca, HmtxTable hmtx_table, HeadTable head_table, PostTable post_table) throws GLib.Error { -// FIXME: DELETE done by freetype+// FIXME: DELETE parse with freetype Glyph glyph = new Glyph (""); /* double xmin, xmax;@@ -21,6 +21,7 @@ public class GsubTable : Table { public GsubTable (GlyfTable glyf_table) { this.glyf_table = glyf_table;+ id = "GSUB"; } public override void parse (FontData dis) throws Error {@@ -84,7 +85,6 @@ public class GsubTable : Table { fd.add_ushort (1); // num glyphs fd.add_ushort ((uint16) glyf_table.get_gid ("f")); // gid - fd.pad (); this.font_data = fd; }@@ -24,7 +24,9 @@ public class LocaTable : Table { } ~LocaTable () {- if (glyph_offsets != null) delete glyph_offsets;+ if (glyph_offsets != null) {+ delete glyph_offsets;+ } } public uint32 get_offset (uint32 i) {@@ -251,9 +251,12 @@ class SvgFont : GLib.Object { parser.add_path_to_glyph (svg, glyph, true, units); glyph.right_limit = glyph.left_limit + advance * units; + // FIXME: add svg font ligatures+ /* if (ligature != "") { glyph.set_ligature_substitution (ligature); }+ */ glyph_collection = new GlyphCollection (unicode_value, glyph_name); glyph_collection.insert_glyph (glyph, true);@@ -25,13 +25,11 @@ namespace Bird { * Example: * {{{ * - * /* Print all tags and attributes in an XML document. - * *- * * Expected output:- * * tag1- * * tag2- * * attribute1- * */+ * // Print all tags and attributes in an XML document. + * // Expected output:+ * // tag1+ * // tag2+ * // attribute1 * public static int main (string[] arg) { * Tag root; * XmlParser parser;@@ -102,7 +100,7 @@ public class XmlParser : GLib.Object { Attributes attributes = tag.get_attributes (); foreach (Attribute a in attributes) {- if (tag.has_failed ()) {+ if (tag.has_failed () || a.get_name () == "") { error = true; return; }