Commit bf23771
store kerning in hash table (using libgee)
Changed files (12)
modifiedREADMEmodifieddodo.pymodifiedlibbirdfont/BirdFontFile.valamodifiedlibbirdfont/DrawingTools.valamodifiedlibbirdfont/GlyphRange.valamodifiedlibbirdfont/KerningClasses.valamodifiedlibbirdfont/KerningDisplay.valamodifiedlibbirdfont/OpenFontFormat/load_font.cmodifiedlibbirdfont/Path.valamodifiedlibbirdfont/PenTool.valamodifiedlibbirdfont/SpinButton.valamodifiedlibbirdfont/TestCases.vala
Diff
@@ -18,6 +18,7 @@ with a -dev or -devel affix. Install the following packages: valac +libgee-dev libglib2.0-dev libgtk2.0-dev libxml2-dev @@ -45,6 +45,7 @@ LIBS = [ 'cairo', 'gdk-pixbuf-2.0', 'webkit-1.0',+ 'gee-1.0' ] def task_build ():@@ -65,6 +65,8 @@ class BirdFontFile { Xml.Doc* doc; Xml.Node* root; + print (xml_data);+ Parser.init (); font.font_file = "typeface.bf"; // FIXME@@ -89,6 +91,7 @@ class BirdFontFile { public bool write_font_file (string path, bool backup = false) { try { File file = File.new_for_path (path);+ uint num_kerning_pairs; if (file.query_file_type (0) == FileType.DIRECTORY) { stderr.printf (@"Can not save font. $path is a directory.");@@ -199,15 +202,15 @@ class BirdFontFile { } }); - uint num_kerning_pairs = KerningClasses.get_instance ().classes_first.length ();+ num_kerning_pairs = KerningClasses.get_instance ().classes_first.length (); for (uint i = 0; i < num_kerning_pairs; i++) { os.put_string ("<kerning "); os.put_string ("left=\"");- os.put_string (KerningClasses.get_instance ().classes_first.nth (i).data.get_all_ranges ());+ os.put_string (KerningClasses.get_instance ().classes_first.nth (i).data.get_all_ranges ().replace ("\"", "quote")); os.put_string ("\" "); os.put_string ("right=\"");- os.put_string (KerningClasses.get_instance ().classes_last.nth (i).data.get_all_ranges ());+ os.put_string (KerningClasses.get_instance ().classes_last.nth (i).data.get_all_ranges ().replace ("\"", "quote")); os.put_string ("\" "); os.put_string ("hadjustment=\"");@@ -498,11 +501,25 @@ class BirdFontFile { } } + private string unserialize (string s) {+ StringBuilder b;+ string r;+ r = s.replace ("quote", "\"");+ + if (s.has_prefix ("U+")) {+ b = new StringBuilder ();+ b.append_unichar (Font.to_unichar (s));+ r = @"$(b.str)";+ }+ + return r;+ }+ private void parse_kerning (Xml.Node* node) { string attr_name; string attr_content; GlyphRange range_left, range_right;- double hadjusment = 0;+ double hadjustment = 0; KerningRange kerning_range; try {@@ -514,16 +531,22 @@ class BirdFontFile { attr_content = prop->children->content; if (attr_name == "left") {- range_left.parse_ranges (attr_content);+ range_left.parse_ranges (unserialize (attr_content)); } if (attr_name == "right") {- range_right.parse_ranges (attr_content);+ range_right.parse_ranges (unserialize (attr_content)); } if (attr_name == "hadjustment") {- hadjusment = double.parse (attr_content);+ hadjustment = double.parse (attr_content);+ print (@"$hadjustment"); }+ + print (attr_name);+ print (" ");+ print (attr_content);+ print ("\n"); } if (range_left.get_length () > 1) {@@ -538,7 +561,8 @@ class BirdFontFile { KerningTools.add_unique_class (kerning_range); } - KerningClasses.get_instance ().set_kerning (range_left, range_right, hadjusment);+ print (@"kern: $(range_left.get_all_ranges ()) $(range_right.get_all_ranges ()), $hadjustment\n");+ KerningClasses.get_instance ().set_kerning (range_left, range_right, hadjustment); } catch (MarkupError e) { warning (e.message);@@ -198,33 +198,6 @@ public class DrawingTools : ToolCollection { }); path_tool_modifiers.add_tool (flip_horizontal); - // adjust precision- string precision_value = Preferences.get ("precision");- - precision = new SpinButton ("precision", _("Set precision"));- - if (precision_value != "") {- precision.set_value (precision_value);- } else {- precision.set_value_round (1);- }- - precision.new_value_action.connect ((self) => {- MainWindow.get_toolbox ().select_tool (precision);- - Preferences.set ("precision", self.get_display_value ());- MainWindow.get_toolbox ().redraw ((int) precision.x, (int) precision.y, 70, 70);- });-- precision.select_action.connect((self) => {- pen_tool.set_precision (((SpinButton)self).get_value ());- });- - precision.set_min (0.001);- precision.set_max (1);- - path_tool_modifiers.add_tool (precision);- // Character set tools Tool full_unicode = new Tool ("utf_8", _("Show full unicode characters set"), 'f', CTRL); full_unicode.select_action.connect ((self) => {@@ -438,7 +411,34 @@ public class DrawingTools : ToolCollection { Tool rectangle = new RectangleTool ("rectangle"); shape_tools.add_tool (rectangle);- ++ // adjust precision+ string precision_value = Preferences.get ("precision");+ + precision = new SpinButton ("precision", _("Set precision"));+ + if (precision_value != "") {+ precision.set_value (precision_value);+ } else {+ precision.set_value_round (1);+ }+ + precision.new_value_action.connect ((self) => {+ MainWindow.get_toolbox ().select_tool (precision);+ + Preferences.set ("precision", self.get_display_value ());+ MainWindow.get_toolbox ().redraw ((int) precision.x, (int) precision.y, 70, 70);+ });++ precision.select_action.connect((self) => {+ pen_tool.set_precision (((SpinButton)self).get_value ());+ });+ + precision.set_min (0.001);+ precision.set_max (1);+ + shape_tools.add_tool (precision);+ // background tools background_scale = new SpinButton ("scale_background", _("Set size for background image")); background_scale.set_int_value ("1.000");@@ -648,9 +648,10 @@ public class DrawingTools : ToolCollection { foreach (Tool t in draw_tools.tool) { t.persistent = true; }+ move_background.persistent = true; cut_background.persistent = true;-+ foreach (Tool t in shape_tools.tool) { t.persistent = true; }@@ -148,18 +148,23 @@ public class GlyphRange { * this string. This function is used for storing ranges in th .bf format. */ public string get_all_ranges () {+ bool first = true; StringBuilder s = new StringBuilder (); foreach (UniRange u in ranges) { + if (!first) {+ s.append (" ");+ }+ if (u.start == u.stop) { s.append (get_serialized_char (u.start));- s.append (" "); } else { s.append (get_serialized_char (u.start)); s.append ("-"); s.append (get_serialized_char (u.stop));- s.append (" "); }+ + first = false; } return s.str; }@@ -174,7 +179,7 @@ public class GlyphRange { if (c == '-') { return "divis"; }- + s.append_unichar (c); return s.str; }@@ -187,7 +192,7 @@ public class GlyphRange { if (c == "divis") { return "-"; }- + return c; } @@ -278,9 +283,12 @@ public class GlyphRange { return len; } - public bool has_character (string c) - requires (c.char_count () == 1) {- unichar s = c.get_char ();+ public bool has_character (string c) {+ unichar s;+ if (c.char_count () != 1) {+ warning (@"Expecting a single character got $c");+ }+ s = c.get_char (); return !unique (s, s); } @@ -316,20 +324,6 @@ public class GlyphRange { stdout.printf ("Ranges:\n"); stdout.printf (get_all_ranges ()); }- - // FIXME: DELETE- /*- public string get_all_ranges () {- StringBuilder s = new StringBuilder ();- foreach (UniRange u in ranges) {- s.append (Font.to_hex_code (u.start));- s.append (" - ");- s.append (Font.to_hex_code (u.stop));- s.append (" ");- }- return s.str;- }- */ } }@@ -15,106 +15,83 @@ using Cairo; using Xml; using Math;+using Gee; namespace BirdFont { public class KerningClasses : GLib.Object { // kerning for classes- public List<GlyphRange> classes_first;- public List<GlyphRange> classes_last;- public List<Kerning> classes_kerning;+ public GLib.List<GlyphRange> classes_first;+ public GLib.List<GlyphRange> classes_last;+ public GLib.List<Kerning> classes_kerning; + // kerning for single glyphs+ Gee.HashMap<string, double?> single_kerning;+ public delegate void KerningIterator (string left, string right, double kerning); public KerningClasses () {- classes_first = new List<GlyphRange> ();- classes_last = new List<GlyphRange> ();- classes_kerning = new List<Kerning> ();+ classes_first = new GLib.List<GlyphRange> ();+ classes_last = new GLib.List<GlyphRange> ();+ classes_kerning = new GLib.List<Kerning> ();+ + single_kerning = new HashMap<string, double?> ();+ + KerningTools.remove_all_kerning_classes (); // TODO: move to menu tab } public static KerningClasses get_instance () { return BirdFont.get_current_font ().get_kerning_classes (); } + public double? get_kerning_for_single_glyphs (string l, string r) {+ return single_kerning.get (@"$l$r");+ } ++ public void set_kerning_for_single_glyphs (string l, string r, double k) {+ single_kerning.set (@"$l$r", k);+ } + public void set_kerning (GlyphRange left_range, GlyphRange right_range, double k) {- int index = get_kerning_item_index (left_range, right_range);+ int index;+ + if (left_range.get_length () == 1 && right_range.get_length () == 1) {+ set_kerning_for_single_glyphs (left_range.get_all_ranges (), right_range.get_all_ranges (), k);+ return;+ }+ + index = get_kerning_item_index (left_range, right_range); // keep the list sorted (classes first then single glyphs) if (index == -1) { classes_first.append (left_range); classes_last.append (right_range); classes_kerning.append (new Kerning (k));- update_order (classes_first.length () - 1); } else { return_if_fail (0 <= index <= classes_first.length ()); classes_kerning.nth (index).data.val = k;- update_order (index);- }- }- - void update_order (uint i) {- unowned List<GlyphRange> l;- unowned List<GlyphRange> r;- unowned List<Kerning> k;- int first;- - if (!(0 <= i < classes_first.length ())) {- warning (@"Index is out of range. Index: $i Length: $(classes_first.length ())");- return;- }-- l = classes_first.nth (i);- r = classes_last.nth (i);- k = classes_kerning.nth (i);- first = get_first_non_class ();-- if (first < 0) {- return;- }- - if (first > classes_first.length ()) {- warning (@"Index is out of range. Index: $first Length: $(classes_first.length ())");- return;- }- - if (l.data.is_class () || r.data.is_class ()) {- classes_first.insert_before (classes_first.nth (first), l.data);- classes_last.insert_before (classes_last.nth (first), r.data);- classes_kerning.insert_before (classes_kerning.nth (first), k.data);-- classes_first.remove_link (l);- classes_last.remove_link (r);- classes_kerning.remove_link (k);- } else {- classes_first.append (l.data);- classes_last.append (r.data);- classes_kerning.append (k.data);-- classes_first.remove_link (l);- classes_last.remove_link (r);- classes_kerning.remove_link (k); } } public double get_kerning_for_range (GlyphRange range_first, GlyphRange range_last) {- unowned List<GlyphRange> r;- unowned List<GlyphRange> l;+ unowned GLib.List<GlyphRange> r;+ unowned GLib.List<GlyphRange> l; int len = (int) classes_first.length ();- bool search_range_is_class = range_first.is_class () || range_last.is_class ();-+ len = (int) classes_first.length (); return_val_if_fail (len == classes_last.length (), 0); return_val_if_fail (len == classes_kerning.length (), 0);++ if (!(range_first.is_class () || range_last.is_class ())) {+ warning ("Expecting a class");+ return 0;+ } for (int i = len - 1; i >= 0; i--) { l = classes_first.nth (i); r = classes_last.nth (i);-- if (search_range_is_class && !l.data.is_class () && !r.data.is_class ()) {- continue;- }- + if (l.data.get_all_ranges () == range_first.get_all_ranges () && r.data.get_all_ranges () == range_last.get_all_ranges ()) { return classes_kerning.nth (i).data.val;@@ -124,47 +101,24 @@ public class KerningClasses : GLib.Object { return 0; } - public int get_first_non_class () {- unowned List<GlyphRange> r;- unowned List<GlyphRange> l;- int len = (int) classes_first.length ();- - len = (int)classes_first.length ();- return_val_if_fail (len == classes_last.length (), 0);- return_val_if_fail (len == classes_kerning.length (), 0);- - for (int i = 0; i < len; i++) {- l = classes_first.nth (i);- r = classes_last.nth (i);- - if (!l.data.is_class () && !r.data.is_class ()) {- return i;- }- }-- return -1;- }- public int get_kerning_item_index (GlyphRange range_first, GlyphRange range_last) {- unowned List<GlyphRange> r;- unowned List<GlyphRange> l;+ unowned GLib.List<GlyphRange> r;+ unowned GLib.List<GlyphRange> l; int len = (int) classes_first.length ();- bool search_range_is_class; len = (int)classes_first.length (); return_val_if_fail (len == classes_last.length (), 0); return_val_if_fail (len == classes_kerning.length (), 0);- - search_range_is_class = range_first.is_class () || range_last.is_class ();++ if (!(range_first.is_class () || range_last.is_class ())) {+ warning ("Expecting a class");+ return -1;+ } for (int i = len - 1; i >= 0; i--) { l = classes_first.nth (i); r = classes_last.nth (i); - if (!l.data.is_class () && !r.data.is_class () && search_range_is_class) {- continue;- }- if (l.data.get_all_ranges () == range_first.get_all_ranges () && r.data.get_all_ranges () == range_last.get_all_ranges ()) { return i;@@ -175,9 +129,15 @@ public class KerningClasses : GLib.Object { } public double get_kerning (string left_glyph, string right_glyph) {- unowned List<GlyphRange> r;- unowned List<GlyphRange> l;+ unowned GLib.List<GlyphRange> r;+ unowned GLib.List<GlyphRange> l; int len = (int) classes_first.length ();+ double? d;+ + d = get_kerning_for_single_glyphs (left_glyph, right_glyph);+ if (d != null) {+ return (!)d;+ } len = (int)classes_first.length (); return_val_if_fail (len == classes_last.length (), 0);@@ -197,22 +157,22 @@ public class KerningClasses : GLib.Object { } public double get_kern_for_range_to_char (GlyphRange left_range, string right_char) {- unowned List<GlyphRange> r;- unowned List<GlyphRange> l;+ unowned GLib.List<GlyphRange> r;+ unowned GLib.List<GlyphRange> l; int len = (int) classes_first.length ();- bool search_range_is_class = left_range.is_class (); len = (int)classes_first.length (); return_val_if_fail (len == classes_last.length (), 0); return_val_if_fail (len == classes_kerning.length (), 0); + if (!left_range.is_class ()) {+ warning ("Expecting a class");+ return -1;+ }+ for (int i = len - 1; i >= 0; i--) { l = classes_first.nth (i); r = classes_last.nth (i);-- if (!l.data.is_class () && !r.data.is_class () && search_range_is_class) {- continue;- } if (l.data.get_all_ranges () == left_range.get_all_ranges () && r.data.has_character (right_char)) {@@ -224,22 +184,22 @@ public class KerningClasses : GLib.Object { } public double get_kern_for_char_to_range (string left_char, GlyphRange right_range) {- unowned List<GlyphRange> r;- unowned List<GlyphRange> l;+ unowned GLib.List<GlyphRange> r;+ unowned GLib.List<GlyphRange> l; int len = (int) classes_first.length ();- bool search_range_is_class = right_range.is_class (); len = (int)classes_first.length (); return_val_if_fail (len == classes_last.length (), 0); return_val_if_fail (len == classes_kerning.length (), 0); + if (!right_range.is_class ()) {+ warning ("Expecting a class");+ return 0;+ }+ for (int i = len - 1; i >= 0; i--) { l = classes_first.nth (i); r = classes_last.nth (i);-- if (search_range_is_class && !l.data.is_class () && !r.data.is_class ()) {- continue;- } if (l.data.has_character (left_char) && r.data.get_all_ranges () == right_range.get_all_ranges ()) {@@ -254,7 +214,7 @@ public class KerningClasses : GLib.Object { print ("Kernings:\n"); for (int i = 0; i < classes_first.length (); i++) { print (classes_first.nth (i).data.get_all_ranges ());- print (@"\t\t");+ print ("\t\t"); print (classes_last.nth (i).data.get_all_ranges ()); print ("\t\t"); print (@"$(classes_kerning.nth (i).data.val)");@@ -266,6 +226,12 @@ public class KerningClasses : GLib.Object { print ("\n"); }+ + foreach (string key in single_kerning.keys) {+ print (key);+ print ("\t\t");+ print (@"$((!) single_kerning.get (key))\n");+ } } public void all_pairs (KerningIterator kerningIterator) {@@ -66,7 +66,7 @@ public class KerningDisplay : FontDisplay { public void draw_error_message (WidgetAllocation allocation, Context cr) { string line1 = _("The current kerning class is malformed."); string line2 = _("Add single characters separated by space and ranges on the form A-Z.");- string line3 = _("Type “space” to kern the space character.");+ string line3 = _("Type “space” to kern the space character and “divis” to kern -."); cr.save (); cr.set_source_rgba (1, 1, 1, 1);@@ -237,7 +237,7 @@ public class KerningDisplay : FontDisplay { return_if_fail (wi - 1 >= 0); if (word_with_ligatures.ranges.length () != word_with_ligatures.glyph.length ()) {- warning ("ranges and glyphs does not match.");+ warning (@"ranges and glyphs does not match. $(word_with_ligatures.ranges.length ()) != $(word_with_ligatures.glyph.length ())"); } gr_left = word_with_ligatures.ranges.nth (wi - 1).data;@@ -416,7 +416,7 @@ public class KerningDisplay : FontDisplay { y = ((Math.fabs (ey - begin_handle_y) / 100) + 1); } - k = (ex - last_handle_x) / y; // y-axis is variable precision+ k = (ex - last_handle_x) / y; // y-axis is for variable precision set_kerning (selected_handle, k); MainWindow.get_glyph_canvas ().redraw (); }@@ -576,13 +576,14 @@ FT_ULong get_charcode (FT_Face face, FT_UInt gid) { /** Obtain kerning data in .bf format for one glyph. * @param gid glyph id */-GString* get_kerning_data (FT_Face face, FT_Long gid) {+GString* get_kerning_data (FT_Face face, FT_Long gid, double units) { FT_Error error; FT_Vector kerning; GString* bf = g_string_new (""); FT_ULong char_left; FT_ULong char_right; FT_Long right;+ gchar kern[80]; char_left = get_charcode (face, gid); @@ -609,8 +610,9 @@ GString* get_kerning_data (FT_Face face, FT_Long gid) { return bf; } - if (kerning.x > 0 && char_left > 31) {- g_string_append_printf (bf, "<hkern left=\"U+%x\" right=\"U+%x\" kerning=\"%d\"/>\n", char_left, char_right, kerning.x);+ if (kerning.x != 0 && char_left > 31 && char_right > 31) {+ g_ascii_formatd (&kern, 80, "%f", kerning.x * units);+ g_string_append_printf (bf, "<kerning left=\"U+%x\" right=\"U+%x\" hadjustment=\"%s\"/>\n", char_left, char_right, kern); } } @@ -796,7 +798,7 @@ GString* get_bf_font (FT_Face face, char* file, int* err) { // kerning for (i = 0; i < face->num_glyphs; i++) {- hkern = get_kerning_data (face, i);+ hkern = get_kerning_data (face, i, units); g_string_append (bf, hkern->str); g_string_free (hkern, 0); bird_font_progress_bar_set_progress ((double)(face->num_glyphs - i) / face->num_glyphs);@@ -1243,6 +1243,7 @@ public class Path { if (right == PointType.LINE_QUADRATIC && left == PointType.LINE_QUADRATIC) { ep.get_right_handle ().set_point_type (PointType.LINE_QUADRATIC); ep.get_left_handle ().set_point_type (PointType.LINE_QUADRATIC);+ ep.type = PointType.QUADRATIC; ep.recalculate_linear_handles (); } else if (right == PointType.LINE_CUBIC && left == PointType.LINE_CUBIC) { ep.get_right_handle ().set_point_type (PointType.LINE_CUBIC);@@ -1253,6 +1254,7 @@ public class Path { ep.get_right_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE); ep.get_left_handle ().set_point_type (PointType.LINE_DOUBLE_CURVE); ep.recalculate_linear_handles ();+ ep.type = PointType.DOUBLE_CURVE; } else if (right == PointType.DOUBLE_CURVE || left == PointType.DOUBLE_CURVE) { double_bezier_vector (position, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x, out x0, out x1); double_bezier_vector (position, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y, out y0, out y1);@@ -1262,6 +1264,7 @@ public class Path { ep.get_left_handle ().set_point_type (PointType.DOUBLE_CURVE); ep.get_right_handle ().set_point_type (PointType.DOUBLE_CURVE);+ ep.type = PointType.DOUBLE_CURVE; } else if (right == PointType.QUADRATIC) { x0 = quadratic_bezier_vector (1 - position, stop.x, start.get_right_handle ().x (), start.x); y0 = quadratic_bezier_vector (1 - position, stop.y, start.get_right_handle ().y (), start.y);@@ -1270,7 +1273,9 @@ public class Path { ep.get_left_handle ().set_point_type (PointType.QUADRATIC); ep.get_right_handle ().set_point_type (PointType.QUADRATIC); - ep.get_left_handle ().move_to_coordinate_internal (0, 0); + ep.get_left_handle ().move_to_coordinate_internal (0, 0);+ + ep.type = PointType.QUADRATIC; } else { bezier_vector (position, start.x, start.get_right_handle ().x (), stop.get_left_handle ().x (), stop.x, out x0, out x1); bezier_vector (position, start.y, start.get_right_handle ().y (), stop.get_left_handle ().y (), stop.y, out y0, out y1);@@ -1746,11 +1751,13 @@ public class Path { ep.set_tie_handle (false); ep.set_reflective_handles (false); ep.get_left_handle ().type = PenTool.to_line (ep.type);+ ep.type = PenTool.to_curve (ep.type); path_list.paths.append (current_path); if (!is_null (current_path.points.last ())) { ep = current_path.points.last ().data; ep.get_right_handle ().type = PenTool.to_line (ep.type);+ ep.type = PenTool.to_curve (ep.get_right_handle ().type); } } } else {@@ -1759,25 +1766,28 @@ public class Path { ep.set_tie_handle (false); ep.set_reflective_handles (false); ep.get_left_handle ().type = PenTool.to_line (ep.type);+ ep.type = PenTool.to_curve (ep.type); set_new_start (current_path.points.first ().data); path_list.paths.append (current_path); if (!is_null (current_path.points.last ())) { ep = current_path.points.last ().data; ep.get_right_handle ().type = PenTool.to_line (ep.type);+ ep.type = PenTool.to_curve (ep.get_right_handle ().type); } } if (remaining_points.points.length () > 0) { remaining_points.points.first ().data.set_tie_handle (false); remaining_points.points.first ().data.set_reflective_handles (false);- remaining_points.points.first ().data.type = PenTool.to_line (remaining_points.points.first ().data.type);+ remaining_points.points.first ().data.type = remaining_points.points.first ().data.type; set_new_start (remaining_points.points.first ().data); path_list.paths.append (remaining_points); if (!is_null (current_path.points.last ())) { ep = current_path.points.last ().data; ep.get_right_handle ().type = PenTool.to_line (ep.type);+ ep.type = PenTool.to_curve (ep.get_right_handle ().type); } } }@@ -1877,7 +1887,6 @@ public class Path { e = new EditPoint (); p1.get_closest_point_on_path (e, inter.x, inter.y); inter.editpoint_b = e;- inter.editpoint_b.x = inter.editpoint_a.x; inter.editpoint_b.y = inter.editpoint_a.y; @@ -1885,6 +1894,9 @@ public class Path { if (!p1.has_edit_point (e)) { p1.insert_new_point_on_path (e); }+ + inter.editpoint_a.type = PenTool.to_curve (inter.editpoint_a.get_right_handle ().type);+ inter.editpoint_b.type = PenTool.to_curve (inter.editpoint_b.get_right_handle ().type); } if (il.points.length () < 2) {@@ -1413,6 +1413,20 @@ public class PenTool : Tool { } return t; }++ public static PointType to_curve (PointType t) {+ switch (DrawingTools.point_type) {+ case PointType.LINE_QUADRATIC:+ return PointType.QUADRATIC;+ case PointType.LINE_DOUBLE_CURVE:+ return PointType.DOUBLE_CURVE;+ case PointType.LINE_CUBIC:+ return PointType.CUBIC;+ default:+ break;+ }+ return t;+ } public static void set_converted_handle_length (EditPointHandle e) { if (e.type == PointType.QUADRATIC && DrawingTools.point_type == PointType.DOUBLE_CURVE) {@@ -49,8 +49,8 @@ public class SpinButton : Tool { n = (button == 3) ? 10 : 1; for (int i = 0; i < n; i++) {- if (py < 28) increase ();- if (py > 35) decrease ();+ if (py < 51) increase ();+ if (py > 60) decrease (); } } @@ -44,6 +44,7 @@ class TestCases { add (test_parse_quadratic_paths, "Quadratic paths"); add (test_freetype, "Freetype"); add (test_preview, "Preview");+ add (test_kerning, "Kerning"); } private void add (Callback callback, string name) {@@ -1449,6 +1450,35 @@ class TestCases { private static void add_point_on_path (Path p, int x, int y) { p.add (Glyph.path_coordinate_x (x), Glyph.path_coordinate_y (y)); }+ + private static void test_kerning () {+ MenuTab.show_kerning_context ();+ Font font = BirdFont.get_current_font ();+ KerningDisplay kerning_display = KerningDisplay.get_singleton ();+ unichar a, b;+ StringBuilder sb1, sb2;+ string sa, sb;+ GlyphRange? r, l;+ + for (int i = 0; i < 100; i++) {+ l = null;+ r = null;+ + // kern single glyph+ sb1 = new StringBuilder ();+ sb2 = new StringBuilder ();+ + a = (unichar)Random.int_range ((int)'a', (int)'ʙ');+ b = (unichar)Random.int_range ((int)'a', (int)'ʙ');+ + sb1.append_unichar (a);+ sb2.append_unichar (b);+ sa = (!)sb1.str;+ sb = (!)sb2.str;+ + kerning_display.set_kerning_pair (sa, sb, ref r, ref l, Random.int_range (0, 300));+ }+ } } }