Commit ec89776
Merge branch 'master' of https://github.com/johanmattssonm/birdfont into HEAD
Changed files (19)
modifiedlibbirdfont/BirdFontFile.valamodifiedlibbirdfont/ClickMap.valamodifiedlibbirdfont/Font.valamodifiedlibbirdfont/ForesightTool.valamodifiedlibbirdfont/Glyph.valamodifiedlibbirdfont/KerningClasses.valamodifiedlibbirdfont/KerningRange.valamodifiedlibbirdfont/KerningTools.valamodifiedlibbirdfont/Line.valamodifiedlibbirdfont/Menu.valamodifiedlibbirdfont/MenuTab.valamodifiedlibbirdfont/OpenFontFormat/GsubTable.valamodifiedlibbirdfont/OpenFontFormat/KernList.valamodifiedlibbirdfont/Path.valamodifiedlibbirdfont/PenTool.valamodifiedlibbirdfont/Renderer/TextArea.valamodifiedlibbirdfont/Row.valamodifiedlibbirdfont/SpacingData.valamodifiedlibbirdfont/TabContent.vala
Diff
@@ -923,13 +923,13 @@ class BirdFontFile : GLib.Object { } if (range_left.get_length () > 1) {- kerning_range = new KerningRange ();+ kerning_range = new KerningRange (font); kerning_range.set_ranges (range_left.get_all_ranges ()); KerningTools.add_unique_class (kerning_range); } if (range_right.get_length () > 1) {- kerning_range = new KerningRange ();+ kerning_range = new KerningRange (font); kerning_range.set_ranges (range_right.get_all_ranges ()); KerningTools.add_unique_class (kerning_range); }@@ -1,5 +1,5 @@ /*- Copyright (C) 2014 Johan Mattsson+ Copyright (C) 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 @@ -12,177 +12,57 @@ Lesser General Public License for more details. */ +using Cairo;+ namespace BirdFont { -// This is a refcounted container in order to avoid double free -// corruption in lambda functions. public class ClickMap : GLib.Object {- char* map;+ ImageSurface map; int width;- int margin;- int map_size;-+ public ClickMap (int width) { this.width = width;- margin = 10;- map_size = width + margin;- map = malloc (map_size * map_size);+ map = new Cairo.ImageSurface (Cairo.Format.ARGB32, width, width); }- - ~ClickMap () {- delete map;- }- - public char get_value (int x, int y) { - if (unlikely (!(0 <= x < map_size && 0 <= y < map_size))) {- warning ("Array index out of bounds.");- return '\0';- }+ public bool get_value (int x, int y) {+ unowned uchar[] d = map.get_data ();+ bool transparent; - return map[y * width + x];- }- - public void set_value (int x, int y, char val) {- if (unlikely (!(0 <= x < map_size && 0 <= y < map_size))) {+ if (unlikely (!(0 <= x < width && 0 <= y < width))) { warning ("Array index out of bounds.");- return;+ return true; } - map[y * width + x] = val;+ transparent = d[y * map.get_stride () + 4 * x + 3] == 0;+ + return !transparent; }+ + public void create_click_map (Path path) {+ double w, h;+ Context c;+ + c = new Context (map);+ + c.save ();+ + c.set_source_rgba (0, 0, 0, 1);+ c.new_path (); - private void map_outline (Path path) { path.all_of_path ((cx, cy, ct) => { int px = (int) (width * ((cx - path.xmin) / (path.xmax - path.xmin))); int py = (int) (width * ((cy - path.ymin) / (path.ymax - path.ymin))); - set_value (px, py, '#');+ c.line_to (px, py); return true; }, 2 * width);- }-- private void close_outline (Path path) {- // first to last point in case the path is open- Path.all_of (path.get_last_point (), path.get_first_point (), (cx, cy, ct) => {- int px = (int) (width * ((cx - path.xmin) / (path.xmax - path.xmin)));- int py = (int) (width * ((cy - path.ymin) / (path.ymax - path.ymin)));- - set_value (px, py, '#');- - return true;- }, 2 * width);- }- - public void create_click_map (Path path) {- // Clear the map- for (int i = 0; i < map_size; i++) {- for (int j = 0; j < map_size; j++) {- set_value (i, j, '\0');- }- } - // Create outline- map_outline (path);- close_outline (path);+ c.close_path ();+ c.fill (); - // Fill the map- for (int j = 0; j < width; j++) {- for (int k = 0; k < width; k++) {- if (get_value (k, j) == '#') {- - k++;- while (k < width && get_value (k, j) == '#') {- k++;- }-- while (k < width && get_value (k, j) == '\0') {- set_value (k, j, 'o');- k++;- }-- k++;- while (k < width && get_value (k, j) == '#') {- k++;- }- }- }- }-- // Remove fill from the out side- for (int k = 0; k < width; k++) {- - if (get_value (k, 0) == 'o') {- set_value (k, 0, '\0'); - }- - for (int l = width - 1; l >= 0; l--) {- if (get_value (k, l) != '#') {- if (get_value (k, l + 1) == '\0') {- set_value (k, l, '\0');- }- - if (get_value (k, l + 1) == 'o') {- set_value (k, l, 'o');- }- }- }- } -- for (int k = 0; k < width; k++) {- if (get_value (0, k) == 'o') {- set_value (0, k,'\0'); - }- - for (int l = width - 1; l >= 0; l--) {- if (get_value (l, k) != '#') {- if (get_value (l + 1, k) == '\0') {- set_value (l, k, '\0');- }- - if (get_value (l + 1, k) == 'o') {- set_value (l, k, 'o');- }- }- }- }-- for (int k = width - 1; k > 0; k--) {- if (get_value (k, 0) == 'o') {- set_value (k, 0,'\0'); - }- - for (int l = width - 1; l >= 0; l--) {- if (get_value (k, l) != '#') { - if (get_value (k, l + 1) == 'o') {- set_value (k, l, 'o');- }- }- }- }- }- - public void print () {- char c;- for (int i = width - 1; i >= 0; i--) {- for (int j = 0; j < width; j++) {- c = get_value (j, i);- if (c == '\0') {- stdout.printf (" ");- } else if (c == '#') {- stdout.printf ("#");- } else if (c == 'o') {- stdout.printf ("o");- } else if (c == 'X') {- stdout.printf ("X");- } else {- stdout.printf ("?");- }- }- - stdout.printf ("\n");- }+ c.restore (); } } @@ -87,7 +87,7 @@ public class Font : GLib.Object { /** File format. */ public FontFormat format = FontFormat.BIRDFONT; - SpacingData spacing;+ public SpacingData spacing; bool read_only = false; @@ -94,11 +94,9 @@ public class ForesightTool : Tool { ps = new PointSelection (PenTool.active_path.points.get (PenTool.active_path.points.size - 1), PenTool.active_path); ps.point.set_tie_handle (false);- ps.point.convert_to_curve (); ps = new PointSelection (PenTool.active_path.points.get (0), PenTool.active_path); ps.point.set_tie_handle (false);- ps.point.convert_to_curve (); first_point = PenTool.active_path.points.get (0); clockwise = PenTool.active_path.is_clockwise ();@@ -197,6 +195,7 @@ public class ForesightTool : Tool { return; } last = PenTool.selected_points.get (PenTool.selected_points.size - 1);+ last.point.convert_to_curve (); } PenTool.move_selected_handle = true;@@ -186,13 +186,13 @@ public class Glyph : FontDisplay { return active_paths.get (active_paths.size - 1); } - public void boundaries (out double x1, out double y1, out double x2, out double y2) {+ public bool boundaries (out double x1, out double y1, out double x2, out double y2) { if (path_list.size == 0) { x1 = 0; y1 = 0; x2 = 0; y2 = 0;- return;+ return false; } x1 = double.MAX;@@ -221,6 +221,8 @@ public class Glyph : FontDisplay { } } }+ + return x1 != double.MAX; } public void selection_boundaries (out double x, out double y, out double w, out double h) {@@ -484,8 +486,7 @@ public class Glyph : FontDisplay { public double get_left_side_bearing () { double x1, y1, x2, y2; - if (path_list.size > 0 && path_list.get (0).points.size > 0) {- boundaries (out x1, out y1, out x2, out y2);+ if (boundaries (out x1, out y1, out x2, out y2)) { return x1 - left_limit; } else { return right_limit - left_limit;@@ -495,8 +496,7 @@ public class Glyph : FontDisplay { public double get_right_side_bearing () { double x1, y1, x2, y2; - if (path_list.size > 0 && path_list.get (0).points.size > 0) {- boundaries (out x1, out y1, out x2, out y2);+ if (boundaries (out x1, out y1, out x2, out y2)) { return right_limit - x2; } else { return right_limit - left_limit;@@ -52,6 +52,30 @@ public class KerningClasses : GLib.Object { single_kerning = new HashMap<string, double?> (); } + public void update_range (GlyphRange old, GlyphRange new_range) {+ string o = old.get_all_ranges ();+ + foreach (GlyphRange gr in classes_first) {+ try {+ if (gr.get_all_ranges () == o) {+ gr.parse_ranges (new_range.get_all_ranges ());+ }+ } catch (GLib.MarkupError e) {+ warning (e.message);+ }+ }+ + foreach (GlyphRange gr in classes_last) {+ try {+ if (gr.get_all_ranges () == o) {+ gr.parse_ranges (new_range.get_all_ranges ());+ }+ } catch (GLib.MarkupError e) {+ warning (e.message);+ }+ }+ }+ /** Class based gpos kerning. */ public double get_kerning_for_pair (string a, string b, GlyphRange? gr_left, GlyphRange? gr_right) { double k = 0;@@ -492,7 +516,6 @@ public class KerningClasses : GLib.Object { // add the right hand glyph and the kerning value foreach (Glyph character in left_glyphs) { KerningPair kl = new KerningPair (character);- pairs.add (kl); foreach (GlyphRange r in classes_last) { foreach (UniRange u in r.ranges) {@@ -523,6 +546,15 @@ public class KerningClasses : GLib.Object { } } + if (kl.kerning.size > 0) {+ pairs.add (kl);+ }+ + if (kl.kerning.size == 0) {+ warning (@"No kerning pairs for character: $((kl.character.get_name ()))");+ }+ + kl.sort (); } @@ -22,9 +22,13 @@ public class KerningRange : Tool { public string ranges = ""; public GlyphRange glyph_range; bool malformed = false;+ Font font; - public KerningRange (string? name = null, string tip = "") {+ public KerningRange (Font f, string? name = null, string tip = "") { base (null , tip);++ font = f;+ glyph_range = new GlyphRange (); if (name != null) {@@ -57,7 +61,6 @@ public class KerningRange : Tool { public void set_ranges (string r) { GlyphRange glyph_range = new GlyphRange ();- Font font = BirdFont.get_current_font (); SpacingData spacing = font.get_spacing (); string new_range; string ch;@@ -90,12 +93,19 @@ public class KerningRange : Tool { } private void set_one_range (string r) throws MarkupError {+ GlyphRange old = new GlyphRange ();+ + old.parse_ranges (ranges);+ old.set_class (true);+ ranges = r; name = r; glyph_range.empty (); glyph_range.parse_ranges (r); glyph_range.set_class (true);++ font.spacing.kerning_classes.update_range (old, glyph_range); } public void update_kerning_classes () {@@ -124,19 +134,21 @@ public class KerningRange : Tool { public override void draw (Context cr) { double xt, yt;+ Text label_text; xt = x + 5; yt = y + 10; cr.save (); - // FIXME: use color to indicate error- Theme.color (cr, "Foreground 1");+ label_text = new Text ();+ label_text.set_text (name);+ Theme.text_color (label_text, "Foreground 2");+ label_text.set_font_size (18);+ label_text.widget_x = x;+ label_text.widget_y = y;+ label_text.draw (cr); - cr.set_font_size (10);- cr.select_font_face ("Cantarell", FontSlant.NORMAL, FontWeight.NORMAL);- cr.move_to (xt, yt);- cr.show_text (name); cr.restore (); } }@@ -54,7 +54,10 @@ public class KerningTools : ToolCollection { Tool new_kerning_class = new Tool ("kerning_class", t_("Create new kerning class.")); new_kerning_class.select_action.connect ((self) => {- classes.add_tool (new KerningRange (@"Kerning class $(++next_class)"));+ Font font = BirdFont.get_current_font ();+ string label = t_("Kerning class");+ KerningRange kr = new KerningRange (font, @"$label $(++next_class)");+ classes.add_tool (kr); Toolbox.redraw_tool_box (); }); kerning_tools.add_tool (new_kerning_class);@@ -131,7 +134,8 @@ public class KerningTools : ToolCollection { } public static void update_kerning_classes () {- KerningClasses k = BirdFont.get_current_font ().get_kerning_classes ();+ Font font = BirdFont.get_current_font ();+ KerningClasses k = font.get_kerning_classes (); KerningRange kr; GlyphRange r; int i;@@ -141,14 +145,14 @@ public class KerningTools : ToolCollection { for (i = 0; i < k.classes_first.size; i++) { r = k.classes_first.get (i); if (r.is_class ()) {- kr = new KerningRange ();+ kr = new KerningRange (font); kr.set_ranges (r.get_all_ranges ()); add_unique_class (kr); } r = k.classes_last.get (i); if (r.is_class ()) {- kr = new KerningRange ();+ kr = new KerningRange (font); kr.set_ranges (r.get_all_ranges ()); add_unique_class (kr); }@@ -172,16 +172,7 @@ public class Line : GLib.Object { } void redraw_line () {- double p;- Glyph g = MainWindow.get_current_glyph ();-- if (vertical) {- p = Glyph.reverse_path_coordinate_x (pos);- queue_draw_area ((int)p - 100, 0, 200, g.allocation.height);- } else {- p = Glyph.reverse_path_coordinate_y (pos);- queue_draw_area (0, (int)p - 100, g.allocation.width, 300);- }+ GlyphCanvas.redraw (); } public void move_line_to (int x, int y, WidgetAllocation allocation) {@@ -441,7 +441,7 @@ public class Menu : GLib.Object { show_menu = false; }); menu.items.add (description);- + current_menu = menu; top_menu = menu; allocation = new WidgetAllocation ();@@ -465,7 +465,7 @@ public class Menu : GLib.Object { display = current_display.get_name (); - if (!(display == "Kerning" && item.modifiers == NONE)) {+ if (!(display == "Kerning" && display == "Spacing" && display != "Preview" && item.modifiers == NONE)) { if (!SettingsDisplay.update_key_bindings && (item.display == "" || item.display == display)) { item.action ();@@ -476,8 +476,10 @@ public class Menu : GLib.Object { if (!tm.tool.is_tool_modifier) { MainWindow.get_toolbox ().set_current_tool (tm.tool);+ return; } else { tm.tool.select_action (tm.tool);+ return; } } }@@ -360,6 +360,7 @@ public class MenuTab : FontDisplay { } dialog.overwrite_signal.connect (() => {+ KeyBindings.set_modifier (NONE); tab_bar.add_unique_tab (new Preview (), true); PreviewTools.update_preview (); });@@ -465,6 +466,11 @@ public class MenuTab : FontDisplay { } public static void simplify_path () {+ if (suppress_event) {+ warn_if_test ("Event suppressed");+ return;+ }+ Task t = new Task (); t.task.connect (simplify); MainWindow.native_window.run_background_thread (t);@@ -79,7 +79,7 @@ public class GsubTable : OtfTable { // lookup table fd.add_ushort (1); // number of lookups FIXME: should be 3- fd.add_ushort (8); // offset to lookup 1+ 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 @@ -87,7 +87,7 @@ public class GsubTable : OtfTable { fd.add_ushort (4); // lookup type fd.add_ushort (0); // lookup flags fd.add_ushort (1); // number of subtables- fd.add_ushort (24 + length); // array of offsets to subtable+ fd.add_ushort (8 + length); // array of offsets to subtable FIXME: should be 24 length += (uint16) clig_subtable.length_with_padding (); /*@@ -57,7 +57,7 @@ public class KernList : GLib.Object { pairs.add (current_pairs); if (unlikely (kerning_pair.kerning.size == 0)) {- warning ("No pairs.");+ warning (@"No kerning pairs for character: $((kerning_pair.character.get_name ()))"); } i = 0;@@ -736,7 +736,6 @@ public class Path { /** Variable precision */ public bool is_over_coordinate_var (double x, double y) { PathList pathlist;- bool in_path = false; int width; ClickMap click_map; int px, py;@@ -774,11 +773,7 @@ public class Path { click_x = (int) (width * ((x - xmin) / (xmax - xmin))); click_y = (int) (width * ((y - ymin) / (ymax - ymin))); - in_path = click_map.get_value (click_x, click_y) != '\0';- - click_map.set_value (click_x, click_y, 'X');- - return in_path;+ return click_map.get_value (click_x, click_y); } public bool is_over_boundry (double x, double y) {@@ -125,7 +125,9 @@ public class PenTool : Tool { move_action (this, x, y); - press (b, x, y, false);+ if (!has_join_icon ()) {+ press (b, x, y, false);+ } if (BirdFont.android) { point_selection_image = true;@@ -157,7 +159,7 @@ public class PenTool : Tool { x = Glyph.path_coordinate_x (ix); y = Glyph.path_coordinate_y (iy);- + if (has_join_icon ()) { join_paths (x, y); }@@ -195,7 +197,7 @@ public class PenTool : Tool { move_action.connect ((self, x, y) => { selection_box_last_x = x; selection_box_last_y = y;- + if (Path.distance (begin_action_x, x, begin_action_y, y) > 10 * MainWindow.units) { point_selection_image = false; }@@ -1013,7 +1015,9 @@ public class PenTool : Tool { Path union, second_path; EditPoint first_point; int px, py;- + bool last_segment_is_line;+ bool first_segment_is_line;+ if (glyph.path_list.size == 0) { return; }@@ -1024,12 +1028,14 @@ public class PenTool : Tool { return; } path = (!) p;- + if (!path.is_open ()) { warning ("Path is closed."); return; } + return_if_fail (path.points.size > 1);+ if (active_edit_point == path.points.get (0)) { path.reverse (); update_selection ();@@ -1049,6 +1055,10 @@ public class PenTool : Tool { py = Glyph.reverse_path_coordinate_y (((!) active_edit_point).y); if (is_endpoint ((!) active_edit_point) && is_close_to_point (path.points.get (0), px, py)) {+ + last_segment_is_line = path.get_last_point ().get_left_handle ().is_line ();+ first_segment_is_line = path.get_first_point ().get_right_handle ().is_line ();+ // TODO: set point type path.points.get (0).left_handle.move_to_coordinate ( path.points.get (path.points.size - 1).left_handle.x,@@ -1068,6 +1078,16 @@ public class PenTool : Tool { path.close (); glyph.close_path ();++ if (last_segment_is_line) {+ path.get_first_point ().get_left_handle ().convert_to_line ();+ path.get_first_point ().recalculate_linear_handles ();+ }++ if (first_segment_is_line) {+ path.get_first_point ().get_right_handle ().convert_to_line ();+ path.get_first_point ().recalculate_linear_handles ();+ } force_direction (); @@ -1077,6 +1097,7 @@ public class PenTool : Tool { } remove_all_selected_points ();+ return; } @@ -130,21 +130,21 @@ public class TextArea : Widget { add_character (keyval); break; case 'a':- if (KeyBindings.has_ctrl ()) {+ if (KeyBindings.has_ctrl () || KeyBindings.has_logo ()) { select_all (); } else { add_character (keyval); } break; case 'c':- if (KeyBindings.has_ctrl ()) {+ if (KeyBindings.has_ctrl () || KeyBindings.has_logo ()) { ClipTool.copy_text (this); } else { add_character (keyval); } break; case 'v':- if (KeyBindings.has_ctrl ()) {+ if (KeyBindings.has_ctrl () || KeyBindings.has_logo ()) { ClipTool.paste_text (this); store_undo_state_at_next_event = true; } else {@@ -152,14 +152,14 @@ public class TextArea : Widget { } break; case 'y':- if (KeyBindings.has_ctrl ()) {+ if (KeyBindings.has_ctrl () || KeyBindings.has_logo ()) { redo (); } else { add_character (keyval); } break; case 'z':- if (KeyBindings.has_ctrl ()) {+ if (KeyBindings.has_ctrl () || KeyBindings.has_logo ()) { undo (); } else { add_character (keyval);@@ -811,8 +811,8 @@ public class TextArea : Widget { } if (unlikely (c.paragraph < 0)) {- c.paragraph = 0;- c.character_index = 0;+ c.paragraph = paragraphs.size > 0 ? paragraphs.size - 1 : 0;+ c.character_index = paragraphs.size > 0 ? paragraphs.get (c.paragraph).text.length : 0; } store_undo_state_at_next_event = true;@@ -20,7 +20,7 @@ public class Row : GLib.Object { int index = 0; bool delete_button = true;- Gee.ArrayList<string> columns_labels = new Gee.ArrayList<string> ();+ public Gee.ArrayList<string> columns_labels = new Gee.ArrayList<string> (); GLib.Object? row_data = null; public int columns {@@ -21,7 +21,7 @@ public class SpacingData : GLib.Object { public KerningClasses kerning_classes; public Gee.ArrayList<SpacingClass> classes = new Gee.ArrayList<SpacingClass> ();- Gee.ArrayList<String> connections = new Gee.ArrayList<String> ();+ Gee.ArrayList<string> connections = new Gee.ArrayList<string> (); public SpacingData (KerningClasses kerning) { kerning_classes = kerning;@@ -32,15 +32,14 @@ public class SpacingData : GLib.Object { } public Gee.ArrayList<string> get_all_connections (string glyph) {- String s; Gee.ArrayList<string> c = new Gee.ArrayList<string> (); connections.clear (); add_connections (glyph); - foreach (String t in connections) {- c.add (t.data.dup ());+ foreach (string t in connections) {+ c.add (t.dup ()); } connections.clear ();@@ -49,8 +48,8 @@ public class SpacingData : GLib.Object { } bool has_connection (string s) {- foreach (String t in connections) {- if (t.data == s) {+ foreach (string t in connections) {+ if (t == s) { return true; } }@@ -59,7 +58,7 @@ public class SpacingData : GLib.Object { } public void add_connections (string glyph) {- String sb = new String (glyph);+ string sb = glyph; connections.add (sb); @@ -119,15 +118,6 @@ public class SpacingData : GLib.Object { KerningTools.update_spacing_classes (); }- - class String : GLib.Object {- - public string data;- - public String (string s) {- data = s.dup ();- } - } } }@@ -23,6 +23,7 @@ public class TabContent : GLib.Object { static LineTextArea text_input; static Button text_input_button; static bool text_input_visible = false;+ static TextListener text_callback; static const int TEXT_INPUT_HEIGHT = 51; @@ -319,6 +320,8 @@ public class TabContent : GLib.Object { } public static void show_text_input (TextListener tl) {+ text_callback = tl;+ text_input_label = new Text (tl.label); text_input = new LineTextArea (20 * MainWindow.units); text_input_button = new Button (tl.button_label);