The Birdfont Source Code


All Repositories / birdfont.git / commit – RSS feed

Apply OTF substitution in kerning tab

These changes was commited to the Birdfont repository Fri, 02 Oct 2015 18:34:09 +0000.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git
author Johan Mattsson <johan.mattsson.m@gmail.com>
Fri, 02 Oct 2015 18:34:09 +0000 (20:34 +0200)
committer Johan Mattsson <johan.mattsson.m@gmail.com>
Fri, 02 Oct 2015 18:34:09 +0000 (20:34 +0200)
commit 13b9d75ae930e4bac1f33dadde65e3978b433911
tree 4a3a1c598fec8e189e7fa646bffc26b8ec8323f7
parent a031f6c5db1a3622fc82d71d0488cb2de2c42243
Apply OTF substitution in kerning tab

libbirdfont/BirdFontFile.vala
libbirdfont/GlyphSequence.vala
libbirdfont/KerningDisplay.vala
libbirdfont/KerningTools.vala
libbirdfont/OtfFeatureTable.vala
libbirdfont/OtfLabel.vala [new ]
libbirdfont/SpacingTab.vala
libbirdfont/ToolCollection.vala
libbirdfont/Toolbox.vala
--- a/libbirdfont/BirdFontFile.vala +++ b/libbirdfont/BirdFontFile.vala @@ -848,6 +848,21 @@ if (attribute.get_name () == "tag") { alt_tag = attribute.get_content (); } + } + + if (glyph_name == "") { + warning ("No name for source glyph in alternate."); + return; + } + + if (alt == "") { + warning ("No name for alternate."); + return; + } + + if (alt_tag == "") { + warning ("No tag for alternate."); + return; } font.add_alternate (glyph_name, alt, alt_tag);
--- a/libbirdfont/GlyphSequence.vala +++ b/libbirdfont/GlyphSequence.vala @@ -20,10 +20,17 @@ /** A list of corresponding glyph ranges if applicable. */ public Gee.ArrayList<GlyphRange?> ranges; + + OtfTags otf_tags; public GlyphSequence () { glyph = new Gee.ArrayList<Glyph?> (); ranges = new Gee.ArrayList<GlyphRange?> (); + otf_tags = new OtfTags (); + } + + public void set_otf_tags (OtfTags tags) { + this.otf_tags = tags; } public int length () { @@ -45,7 +52,8 @@ } } - /** Do ligature substitution. + /** Perform glyph substitution. + * @param tags enable otf features * @return a new sequence with ligatures */ public GlyphSequence process_ligatures (Font font) { @@ -80,6 +88,37 @@ ligatures.get_single_substitution_ligatures ((substitute, ligature) => { ligature_sequence.replace (substitute, ligature); }); + + // salt and similar tags + foreach (string tag in otf_tags.elements) { + Gee.ArrayList<Alternate> alternates; + alternates = font.alternates.get_alt (tag); + + foreach (Alternate a in alternates) { + GlyphSequence old = new GlyphSequence (); + Glyph? g = font.get_glyph_by_name (a.glyph_name); + + if (g != null) { + old.add (g); + + if (a.alternates.size > 0) { + // FIXME: pick one of several alternates + string alt_name = a.alternates.get (0); + Glyph? alt = font.get_glyph_by_name (alt_name); + + if (alt != null) { + GlyphSequence replacement = new GlyphSequence (); + replacement.add (alt); + ligature_sequence.replace (old, replacement); + } else { + warning (@"Alternate does not exist: $(alt_name)"); + } + } + } else { + warning (@"Alternative for a missing glyph: $(a.glyph_name)"); + } + } + } ligature_sequence.ranges.clear (); for (int i = 0; i < ligature_sequence.glyph.size; i++) {
--- a/libbirdfont/KerningDisplay.vala +++ b/libbirdfont/KerningDisplay.vala @@ -1,5 +1,5 @@ /* - Copyright (C) 2012, 2014 Johan Mattsson + Copyright (C) 2012 2014 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 @@ -21,7 +21,9 @@ public bool suppress_input = false; - Gee.ArrayList <GlyphSequence> row; + Gee.ArrayList <GlyphSequence> first_row; + Gee.ArrayList <GlyphSequence> rows; + int active_handle = -1; int selected_handle = -1; bool moving = false; @@ -39,7 +41,6 @@ Gee.ArrayList<UndoItem> redo_items; bool first_update = true; - Font current_font = new Font (); Text kerning_label = new Text (); public bool adjust_side_bearings = false; @@ -47,14 +48,49 @@ public KerningDisplay () { GlyphSequence w = new GlyphSequence (); - row = new Gee.ArrayList <GlyphSequence> (); + rows = new Gee.ArrayList <GlyphSequence> (); + first_row = new Gee.ArrayList <GlyphSequence> (); undo_items = new Gee.ArrayList <UndoItem> (); redo_items = new Gee.ArrayList <UndoItem> (); - row.add (w); + w.set_otf_tags (KerningTools.get_otf_tags ()); + first_row.add (w); } public GlyphSequence get_first_row () { - return row.size > 0 ? row.get (0) : new GlyphSequence (); + GlyphSequence first = new GlyphSequence (); + Font font = BirdFont.get_current_font (); + + foreach (GlyphSequence s in first_row) { + first.append (s.process_ligatures (font)); + } + + return first; + } + + public void new_segment () { + GlyphSequence s = new GlyphSequence (); + s.set_otf_tags (KerningTools.get_otf_tags ()); + first_row.add (s); + } + + public GlyphSequence get_last_segment () { + if (first_row.size == 0) { + new_segment (); + } + + return first_row.get (first_row.size - 1); + } + + Gee.ArrayList <GlyphSequence> get_all_rows () { + Gee.ArrayList <GlyphSequence> r; + + r = new Gee.ArrayList <GlyphSequence> (); + r.add (get_first_row ()); + foreach (GlyphSequence s in rows) { + r.add (s); + } + + return r; } public override string get_label () { @@ -78,6 +114,7 @@ } public double get_row_height () { + Font current_font = BirdFont.get_current_font (); return current_font.top_limit - current_font.bottom_limit; } @@ -92,11 +129,10 @@ GlyphRange? gr_left, gr_right; bool first_row = true; double row_height; - Font font; + Font font = BirdFont.get_current_font (); double item_size = 1.0 / KerningTools.font_size; double item_size2 = 2.0 / KerningTools.font_size; - - font = current_font; + i = 0; // bg color @@ -120,7 +156,7 @@ prev = null; kern = 0; - foreach (GlyphSequence word in row) { + foreach (GlyphSequence word in get_all_rows ()) { wi = 0; word_with_ligatures = word.process_ligatures (font); gr_left = null; @@ -253,8 +289,8 @@ } } - for (int j = row.size - 1; j > 30; j--) { - row.remove_at (j); + for (int j = rows.size - 1; j > 30; j--) { + rows.remove_at (j); } cr.fill (); @@ -270,7 +306,7 @@ double kern = get_kerning_for_handle (h); active_handle = h; - if (1 <= active_handle < row.get (0).glyph.size) { + if (1 <= active_handle < get_first_row ().glyph.size) { display_kerning_value (kern); } } @@ -298,9 +334,8 @@ int ranges_index = 0; GlyphRange? gr_left, gr_right; int row_index = 0; - - font = current_font; + font = BirdFont.get_current_font (); font.touch (); a = ""; @@ -315,46 +350,42 @@ return false; } - foreach (GlyphSequence word in row) { - word_with_ligatures = word.process_ligatures (font); - ranges_index = 0; - foreach (Glyph? g in word_with_ligatures.glyph) { + word_with_ligatures = get_first_row (); + ranges_index = 0; + foreach (Glyph? g in word_with_ligatures.glyph) { + + if (g == null) { + continue; + } + + b = ((!) g).get_name (); + + if (handle == wi && row_index == 0) { + if (wi >= word_with_ligatures.ranges.size) { + return false; + } + return_val_if_fail (wi - 1 >= 0, false); - if (g == null) { - continue; + if (word_with_ligatures.ranges.size != word_with_ligatures.glyph.size) { + return false; } - b = ((!) g).get_name (); + gr_left = word_with_ligatures.ranges.get (wi - 1); + gr_right = word_with_ligatures.ranges.get (wi); - if (handle == wi && row_index == 0) { - if (wi >= word_with_ligatures.ranges.size) { - return false; - } - return_val_if_fail (wi - 1 >= 0, false); - - if (word_with_ligatures.ranges.size != word_with_ligatures.glyph.size) { - return false; - } - - gr_left = word_with_ligatures.ranges.get (wi - 1); - gr_right = word_with_ligatures.ranges.get (wi); - - left = a; - right = b; - range_left = gr_left; - range_right = gr_right; - - return true; - } - - wi++; + left = a; + right = b; + range_left = gr_left; + range_right = gr_right; - a = b; + return true; } - row_index++; + wi++; + + a = b; } - + return false; } @@ -378,7 +409,7 @@ Font font; GlyphRange? gr_left, gr_right; - font = current_font; + font = BirdFont.get_current_font (); font.touch (); if (!adjust_side_bearings) { @@ -410,7 +441,7 @@ bool has_kerning; Font font; - font = current_font; + font = BirdFont.get_current_font (); font.touch (); classes = font.get_kerning_classes (); @@ -453,34 +484,10 @@ public static double get_kerning_for_pair (string a, string b, GlyphRange? gr_left, GlyphRange? gr_right) { KerningClasses k = BirdFont.get_current_font ().get_kerning_classes (); return k.get_kerning_for_pair (a, b, gr_left, gr_right); - } - - public void set_current_font (Font f) { - current_font = f; } public override void selected_canvas () { - Glyph g; - GlyphSequence w; - StringBuilder s = new StringBuilder (); - bool append_char = false; - - current_font = BirdFont.get_current_font (); - - KeyBindings.set_require_modifier (true); - - g = MainWindow.get_current_glyph (); - s.append_unichar (g.get_unichar ()); - - if (row.size == 0) { - append_char = true; - } - - if (append_char) { - w = new GlyphSequence (); - row.add (w); - w.glyph.insert (0, current_font.get_glyph (s.str)); - } + KeyBindings.set_require_modifier (true); } public void add_kerning_class (int index) { @@ -488,8 +495,9 @@ } public void add_range (GlyphRange range) { - Font font = current_font; + Font font = BirdFont.get_current_font (); Glyph? glyph; + GlyphSequence s; glyph = font.get_glyph_by_name (range.get_char (0)); @@ -498,8 +506,15 @@ return; } - row.get (0).glyph.add ((!) glyph); - row.get (0).ranges.add (range); + if (first_row.size == 0) { + s = new GlyphSequence (); + first_row.add (s); + } else { + s = first_row.get (first_row.size - 1); + } + + s.glyph.add ((!) glyph); + s.ranges.add (range); GlyphCanvas.redraw (); } @@ -508,9 +523,8 @@ Glyph? g; selected_handle = handle; GlyphSequence sequence_with_ligatures; - Font font = BirdFont.get_current_font (); - sequence_with_ligatures = row.get (0).process_ligatures (font); + sequence_with_ligatures = get_first_row (); if (selected_handle <= 0) { selected_handle = 1; @@ -625,12 +639,11 @@ || KeyBindings.modifier == SHIFT || KeyBindings.modifier == ALT) { - if (keyval == Key.BACK_SPACE && row.size > 0 && row.get (0).glyph.size > 0) { - row.get (0).glyph.remove_at (row.get (0).glyph.size - 1); - row.get (0).ranges.remove_at (row.get (0).ranges.size - 1); + if (keyval == Key.BACK_SPACE) { + remove_last_character (); } - if (row.size == 0 || c == Key.ENTER) { + if (c == Key.ENTER) { new_line (); } @@ -639,6 +652,21 @@ } GlyphCanvas.redraw (); + } + + void remove_last_character () { + if (first_row.size > 0) { + GlyphSequence gs = first_row.get (first_row.size - 1); + + if (gs.glyph.size > 0) { + gs.glyph.remove_at (gs.glyph.size - 1); + return_if_fail (gs.ranges.size > 0); + gs.ranges.remove_at (gs.ranges.size - 1); + } else { + first_row.remove_at (first_row.size - 1); + remove_last_character (); + } + } } public void insert_unichar () { @@ -647,7 +675,6 @@ string unicodestart; unicodestart = (KeyBindings.has_shift ()) ? "" : "U+"; - listener = new TextListener (t_("Unicode"), unicodestart, t_("Insert")); listener.signal_text_input.connect ((text) => { @@ -681,34 +708,44 @@ } public void new_line () { - row.insert (0, new GlyphSequence ()); + rows.insert (0, get_first_row ()); + first_row = new Gee.ArrayList<GlyphSequence> (); + GlyphSequence gs = new GlyphSequence (); + gs.set_otf_tags (KerningTools.get_otf_tags ()); + first_row.add (gs); } void add_character (unichar c) { Glyph? g; string name; - Font f; if (MenuTab.suppress_event) { return; } - - f = current_font; if (!is_modifier_key (c) && c.validate ()) { name = (!) c.to_string (); - g = f.get_glyph_by_name (name); + g = BirdFont.get_current_font ().get_glyph_by_name (name); inser_glyph (g); } } public void inser_glyph (Glyph? g) { + int handle; + + if (first_row.size == 0) { + GlyphSequence gs = new GlyphSequence (); + gs.set_otf_tags (KerningTools.get_otf_tags ()); + first_row.add (gs); + } + if (g != null) { - row.get (0).glyph.add (g); - row.get (0).ranges.add (null); + first_row.get (first_row.size - 1).glyph.add (g); + first_row.get (first_row.size - 1).ranges.add (null); - set_selected_handle ((int) row.get (0).glyph.size - 1); - set_active_handle_index (selected_handle); + handle = get_first_row ().glyph.size - 1; + set_selected_handle (handle); + set_active_handle_index (handle); } } @@ -745,7 +782,6 @@ int row_index = 0; int col_index = 0; Glyph glyph = new Glyph.no_lines (""); - Font font = BirdFont.get_current_font (); double fs = KerningTools.font_size; double x = 20; @@ -755,68 +791,65 @@ string gl_name = ""; GlyphSequence word_with_ligatures; - foreach (GlyphSequence word in row) { - col_index = 0; + col_index = 0; + + word_with_ligatures = get_first_row (); + foreach (Glyph? g in word_with_ligatures.glyph) { + if (g == null) { + w = 50; + warning ("glyph does not exist"); + } else { + glyph = (!) g; + w = glyph.get_width (); + } + + gl_name = glyph.get_name (); - word_with_ligatures = word.process_ligatures (font); - foreach (Glyph? g in word_with_ligatures.glyph) { - if (g == null) { - w = 50; - warning ("glyph does not exist"); - } else { - glyph = (!) g; - w = glyph.get_width (); - } + if (prev == null && col_index != 0) { + warning (@"previous glyph does not exist row: $row_index column: $col_index"); + } + + if (prev == null || col_index == 0) { + kern = 0; + } else { + return_if_fail (col_index < word_with_ligatures.ranges.size); + return_if_fail (col_index - 1 >= 0); - gl_name = glyph.get_name (); + gr_left = word_with_ligatures.ranges.get (col_index - 1); + gr_right = word_with_ligatures.ranges.get (col_index); + + kern = get_kerning_for_pair (((!)prev).get_name (), ((!)g).get_name (), gr_left, gr_right); + } + + d = Math.pow (fs * (x + kern) - ex, 2); + + if (d < min) { + min = d; - if (prev == null && col_index != 0) { - warning (@"previous glyph does not exist row: $row_index column: $col_index"); + if (ex != fs * (x + kern)) { // don't swap direction after button release + right_side_bearing = ex < fs * (x + kern); // right or left side bearing handle } - if (prev == null || col_index == 0) { - kern = 0; - } else { - return_if_fail (col_index < word_with_ligatures.ranges.size); - return_if_fail (col_index - 1 >= 0); - - gr_left = word_with_ligatures.ranges.get (col_index - 1); - gr_right = word_with_ligatures.ranges.get (col_index); - - kern = get_kerning_for_pair (((!)prev).get_name (), ((!)g).get_name (), gr_left, gr_right); + if (active_handle != i - row_index) { + set_active_handle_index (i - row_index); + GlyphCanvas.redraw (); } - - d = Math.pow (fs * (x + kern) - ex, 2); - if (d < min) { - min = d; - - if (ex != fs * (x + kern)) { // don't swap direction after button release - right_side_bearing = ex < fs * (x + kern); // right or left side bearing handle - } - - if (active_handle != i - row_index) { - set_active_handle_index (i - row_index); - GlyphCanvas.redraw (); - } - - if (col_index == word.glyph.size || col_index == 0) { - set_active_handle_index (-1); - } else { - set_active_handle_index (active_handle + row_index); - } + if (col_index == word_with_ligatures.glyph.size || col_index == 0) { + set_active_handle_index (-1); + } else { + set_active_handle_index (active_handle + row_index); } - - prev = g; - x += w + kern; - i++; - col_index++; } - row_index++; - x = 20; - return; + prev = g; + x += w + kern; + i++; + col_index++; } + + row_index++; + x = 20; } public override void button_release (int button, double ex, double ey) { @@ -935,7 +968,7 @@ public UndoItem apply_undo (UndoItem ui) { KerningClasses classes = BirdFont.get_current_font ().get_kerning_classes (); GlyphRange glyph_range_first, glyph_range_next; - Font font = current_font; + Font font = BirdFont.get_current_font (); string l, r; UndoItem redo_state = new UndoItem ("", "", 0, false); double? k;
--- a/libbirdfont/KerningTools.vala +++ b/libbirdfont/KerningTools.vala @@ -27,12 +27,31 @@ public static Tool previous_kerning_string; public static Tool next_kerning_string; + + public static Expander otf_features; + + static OtfTags active_otf_features; public KerningTools () { + active_otf_features = new OtfTags (); + selected (); + } + + public override void selected () { init (); } + public static OtfTags get_otf_tags () { + if (is_null (active_otf_features)) { + return new OtfTags (); + } + + return active_otf_features; + } + public static void init () { + Font font = BirdFont.get_current_font (); + Expander kerning_tools = new Expander (t_("Kerning Tools")); classes = new Expander (); expanders = new Gee.ArrayList<Expander> (); @@ -61,9 +80,9 @@ Tool new_kerning_class = new Tool ("kerning_class", t_("Create new kerning class.")); new_kerning_class.select_action.connect ((self) => { - Font font = BirdFont.get_current_font (); + Font f = BirdFont.get_current_font (); string label = t_("Kerning class"); - KerningRange kr = new KerningRange (font, @"$label $(++next_class)"); + KerningRange kr = new KerningRange (f, @"$label $(++next_class)"); classes.add_tool (kr); self.set_selected (false); classes.redraw (); @@ -141,9 +160,15 @@ }); kerning_tools.add_tool (next_kerning_string); + otf_features = new Expander (t_("Substitutions")); + + foreach (string tag in font.alternates.get_all_tags ()) { + add_otf_label (tag); + } + kerning_tools.set_persistent (false); kerning_tools.set_unique (false); - + classes.set_persistent (true); classes.set_unique (true); @@ -151,6 +176,33 @@ expanders.add (zoom_expander); expanders.add (kerning_tools); expanders.add (classes); + expanders.add (otf_features); + } + + public static void add_otf_label (string tag) { + OtfLabel otf_label = new OtfLabel (tag); + otf_features.add_tool (otf_label); + otf_label.otf_feature_activity.connect ((enable, tag) => { + OtfTags tags = active_otf_features.copy (); + KerningDisplay kd = MainWindow.get_kerning_display (); + kd.new_segment (); + + // create a new feature set in order to keep the features + // for other parts of the text in kerning tab + active_otf_features = tags; + + if (enable) { + print ("enable " + tag + "\n"); + tags.add (tag); + } else { + print ("disable " + tag + "\n"); + tags.remove (tag); + } + + kd.get_last_segment ().set_otf_tags (tags); + + GlyphCanvas.redraw (); + }); } public static void add_unique_class (KerningRange kerning_class) { @@ -271,7 +323,8 @@ d.add ("Spacing"); return d; } + } }
--- a/libbirdfont/OtfFeatureTable.vala +++ b/libbirdfont/OtfFeatureTable.vala @@ -91,19 +91,19 @@ row = new Row.headline (t_("Tag")); rows.add (row); - row = new Row.columns_1 (t_("Stylistic Alternate") + " (salt)", OTF_FEATURE, false); + row = new Row.columns_1 (OtfLabel.get_string ("salt"), OTF_FEATURE, false); row.set_row_data (new String ("salt")); rows.add (row); - row = new Row.columns_1 (t_("Small Caps") + " (smcp)", OTF_FEATURE, false); + row = new Row.columns_1 (OtfLabel.get_string ("smcp"), OTF_FEATURE, false); row.set_row_data (new String ("smcp")); rows.add (row); - row = new Row.columns_1 (t_("Capitals to Small Caps") + " (c2sc)", OTF_FEATURE, false); + row = new Row.columns_1 (OtfLabel.get_string ("c2sc"), OTF_FEATURE, false); row.set_row_data (new String ("c2sc")); rows.add (row); - row = new Row.columns_1 (t_("Swashes") + " (swsh)", OTF_FEATURE, false); + row = new Row.columns_1 (OtfLabel.get_string ("swsh"), OTF_FEATURE, false); row.set_row_data (new String ("swsh")); rows.add (row);
diff --git libbirdfont/OtfLabel.vala(new)
--- /dev/null +++ b/libbirdfont/OtfLabel.vala @@ -1,1 +1,58 @@ + /* + 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. + */ + + namespace BirdFont { + + /** Gsub substitutions will be performed kening and spacing tab when + * this tool is selected. + */ + public class OtfLabel : LabelTool { + + public bool active_substitution = false; + public string tag; + + public signal void otf_feature_activity (bool enable, string tag); + + public OtfLabel (string tag) { + string label = get_string(tag); + base (label); + + this.tag = tag; + + select_action.connect ((self) => { + active_substitution = !active_substitution; + self.set_selected (active_substitution); + otf_feature_activity (active_substitution, tag); + }); + } + + /** @return translated string representation of a OTF feature tag. */ + public static string get_string (string tag) { + if (tag == "salt") { + return t_("Stylistic Alternate") + " (salt)"; + } else if (tag == "smcp") { + return t_("Small Caps") + " (smcp)"; + } else if (tag == "c2sc") { + return t_("Capitals to Small Caps") + " (c2sc)"; + } else if (tag == "swsh") { + return t_("Swashes") + " (swsh)"; + } + + warning (@"Unknown tag: $tag"); + return tag; + } + } + + }
--- a/libbirdfont/SpacingTab.vala +++ b/libbirdfont/SpacingTab.vala @@ -70,7 +70,6 @@ cr.stroke (); cr.restore (); - // TODO: add button for processing ligatures row = get_first_row ().process_ligatures (font); index = 0; foreach (Glyph? g in row.glyph) {
--- a/libbirdfont/ToolCollection.vala +++ b/libbirdfont/ToolCollection.vala @@ -31,8 +31,11 @@ foreach (Expander e in get_expanders ()) { e.redraw (); } + } + + public virtual void selected () { } } }
--- a/libbirdfont/Toolbox.vala +++ b/libbirdfont/Toolbox.vala @@ -81,6 +81,7 @@ tool_sets.add (hidden_tools); // tools without a button current_set = file_tools; + current_set.selected (); tab_bar.signal_tab_selected.connect ((tab) => { string tab_name = tab.get_display ().get_name (); @@ -120,6 +121,7 @@ current_set = (ToolCollection) file_tools; } + current_set.selected (); MainWindow.get_toolbox ().update_expanders (); redraw_tool_box (); }