Commit e48648e
Split .bf files in several smaller .bfp files This commit adds support for the .bfp file format.
Changed files (15)
modifiedbirdfont/GtkWindow.valamodifiedlibbirdfont/BirdFont.valamodifiedlibbirdfont/BirdFontFile.valaaddedlibbirdfont/BirdFontPart.valamodifiedlibbirdfont/DropMenu.valamodifiedlibbirdfont/FileTab.valamodifiedlibbirdfont/Font.valamodifiedlibbirdfont/Glyph.valamodifiedlibbirdfont/GlyphBackgroundImage.valamodifiedlibbirdfont/GlyphCollection.valamodifiedlibbirdfont/MenuTab.valamodifiedlibbirdfont/OverView.valamodifiedlibbirdfont/SvgFont.valamodifiedlibbirdfont/TabBar.valamodifiedlibbirdfont/VersionList.vala
Diff
@@ -414,6 +414,7 @@ public class GtkWindow : Gtk.Window, NativeWindow { Gtk.Menu tab_menu = new Gtk.Menu (); Gtk.Menu tool_menu = new Gtk.Menu (); Gtk.Menu kerning_menu = new Gtk.Menu ();+ Gtk.Menu git_menu = new Gtk.Menu (); AccelGroup accel_group = new Gtk.AccelGroup(); add_accel_group (accel_group);@@ -704,6 +705,13 @@ public class GtkWindow : Gtk.Window, NativeWindow { kerning_menu.append (select_previous_kerning_pair); select_previous_kerning_pair.activate.connect (() => { KerningDisplay.previous_pair (); }); + // Git+ Gtk.MenuItem save_as_bfp = new Gtk.MenuItem.with_mnemonic (t_("_Save as .bfp"));+ git_menu.append (save_as_bfp);+ save_as_bfp.activate.connect (() => { + MenuTab.save_as_bfp ();+ });+ // Add menus Gtk.MenuItem file_launcher = new Gtk.MenuItem.with_mnemonic (t_("_File")); file_launcher.set_submenu (file_menu);@@ -719,13 +727,20 @@ public class GtkWindow : Gtk.Window, NativeWindow { Gtk.MenuItem kerning_launcher = new Gtk.MenuItem.with_mnemonic (t_("_Kerning")); kerning_launcher.set_submenu (kerning_menu);- ++ Gtk.MenuItem git_launcher = new Gtk.MenuItem.with_mnemonic ("_Git");+ git_launcher.set_submenu (git_menu);+ menubar.append (file_launcher); menubar.append (edit_launcher); menubar.append (tab_launcher); menubar.append (tool_launcher); menubar.append (kerning_launcher); + if (BirdFont.has_argument ("--test")) {+ menubar.append (git_launcher);+ }+ return menubar; } @@ -410,7 +410,7 @@ public class BirdFont { return backup; } - internal static bool has_argument (string param) {+ public static bool has_argument (string param) { if (is_null (args)) { warning ("args is null"); return false;@@ -19,7 +19,7 @@ namespace BirdFont { * BirdFont file format. This class can parse both the old ffi format * and the new bf format. */-class BirdFontFile {+class BirdFontFile : GLib.Object { Font font; @@ -36,6 +36,10 @@ class BirdFontFile { Parser.init (); + while (font.background_images.length () > 0) {+ font.background_images.remove_link (font.background_images.first ());+ }+ font.font_file = path; tr = new TextReader.filename (path); ok = load_xml (tr);@@ -45,6 +49,17 @@ class BirdFontFile { return ok; } + public bool load_part (string bfp_file) {+ TextReader tr;+ bool ok;+ Parser.init ();+ tr = new TextReader.filename (bfp_file);+ ok = load_xml (tr);+ Parser.cleanup ();+ + return ok;+ }+ /** Load a new .bf file. * @param xml_data data for a valid .bf file */@@ -76,7 +91,6 @@ class BirdFontFile { create_background_files (root); ok = parse_file (root);- tr.close (); return ok;@@ -85,12 +99,10 @@ class BirdFontFile { public bool write_font_file (string path, bool backup = false) { try { File file = File.new_for_path (path);- uint num_kerning_pairs; uint progress;- string range; if (file.query_file_type (0) == FileType.DIRECTORY) {- warning (@"Can not save font. $path is a directory.");+ warning (@"Can't save font. $path is a directory."); return false; } @@ -98,12 +110,9 @@ class BirdFontFile { file.delete (); } - DataOutputStream os = new DataOutputStream(file.create(FileCreateFlags.REPLACE_DESTINATION));+ DataOutputStream os = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION)); - os.put_string ("""<?xml version="1.0" encoding="utf-8" standalone="yes"?>""");- os.put_string ("\n");- - os.put_string ("<font>\n");+ write_root_tag (os); // this a backup of another font if (backup) {@@ -113,38 +122,17 @@ class BirdFontFile { } os.put_string ("\n");- os.put_string (@"<postscript_name>$(font.postscript_name)</postscript_name>\n");- os.put_string (@"<name>$(font.name)</name>\n");- os.put_string (@"<subfamily>$(font.subfamily)</subfamily>\n");- os.put_string (@"<bold>$(font.bold)</bold>\n");- os.put_string (@"<italic>$(font.italic)</italic>\n"); - os.put_string (@"<full_name>$(font.full_name)</full_name>\n");- os.put_string (@"<unique_identifier>$(font.unique_identifier)</unique_identifier>\n");- os.put_string (@"<version>$(font.version)</version>\n");- os.put_string (@"<description>$(font.description)</description>\n");- os.put_string (@"<copyright>$(font.copyright)</copyright>\n");+ write_description (os); os.put_string ("\n");- os.put_string ("<horizontal>\n");- os.put_string (@"\t<top_limit>$(font.top_limit)</top_limit>\n");- os.put_string (@"\t<top_position>$(font.top_position)</top_position>\n");- os.put_string (@"\t<x-height>$(font.xheight_position)</x-height>\n");- os.put_string (@"\t<base_line>$(font.base_line)</base_line>\n");- os.put_string (@"\t<bottom_position>$(font.bottom_position)</bottom_position>\n");- os.put_string (@"\t<bottom_limit>$(font.bottom_limit)</bottom_limit>\n");- os.put_string ("</horizontal>\n\n");+ write_lines (os); - foreach (string gv in font.grid_width) {- os.put_string (@"<grid width=\"$(gv)\"/>\n");- }- - if (GridTool.sizes.length () > 0) {- os.put_string ("\n");- }+ os.put_string ("\n");+ write_settings (os); - os.put_string (@"<background scale=\"$(font.background_scale)\" />\n"); os.put_string ("\n"); + // FIXME: add this to a font specific settings file if (font.background_images.length () > 0) { os.put_string (@"<images>\n"); @@ -203,6 +191,35 @@ class BirdFontFile { TooltipArea.show_text (t_("Saving")); }); + write_kerning (os);+ write_closing_root_tag (os);+ + os.close ();+ } catch (GLib.Error e) {+ warning (@"Failed to save $path \n");+ warning (@"$(e.message) \n");+ return false;+ }+ + TooltipArea.show_text ("");+ return true;+ }+ + public void write_root_tag (DataOutputStream os) throws GLib.Error {+ os.put_string ("""<?xml version="1.0" encoding="utf-8" standalone="yes"?>""");+ os.put_string ("\n");+ os.put_string ("<font>\n");+ }+ + public void write_closing_root_tag (DataOutputStream os) throws GLib.Error {+ os.put_string ("</font>\n");+ }+ + public void write_kerning (DataOutputStream os) throws GLib.Error {+ uint num_kerning_pairs;+ uint progress;+ string range;+ num_kerning_pairs = KerningClasses.get_instance ().classes_first.length (); progress = num_kerning_pairs; ProgressBar.set_progress (1);@@ -252,20 +269,80 @@ class BirdFontFile { ProgressBar.set_progress (0); TooltipArea.show_text (t_("Saving")); });- - os.put_string ("</font>");- - } catch (GLib.Error e) {- warning (@"Failed to save $path \n");- warning (@"$(e.message) \n");- return false;+ }+ + public void write_settings (DataOutputStream os) throws GLib.Error {+ foreach (string gv in font.grid_width) {+ os.put_string (@"<grid width=\"$(gv)\"/>\n"); } - TooltipArea.show_text ("");- return true;+ if (GridTool.sizes.length () > 0) {+ os.put_string ("\n");+ }+ + os.put_string (@"<background scale=\"$(font.background_scale)\" />\n"); + }++ public void write_description (DataOutputStream os) throws GLib.Error {+ os.put_string (@"<postscript_name>$(font.postscript_name)</postscript_name>\n");+ os.put_string (@"<name>$(font.name)</name>\n");+ os.put_string (@"<subfamily>$(font.subfamily)</subfamily>\n");+ os.put_string (@"<bold>$(font.bold)</bold>\n");+ os.put_string (@"<italic>$(font.italic)</italic>\n"); + os.put_string (@"<full_name>$(font.full_name)</full_name>\n");+ os.put_string (@"<unique_identifier>$(font.unique_identifier)</unique_identifier>\n");+ os.put_string (@"<version>$(font.version)</version>\n");+ os.put_string (@"<description>$(font.description)</description>\n");+ os.put_string (@"<copyright>$(font.copyright)</copyright>\n"); } - private string float_point (double d) {+ public void write_lines (DataOutputStream os) throws GLib.Error {+ os.put_string ("<horizontal>\n");+ os.put_string (@"\t<top_limit>$(font.top_limit)</top_limit>\n");+ os.put_string (@"\t<top_position>$(font.top_position)</top_position>\n");+ os.put_string (@"\t<x-height>$(font.xheight_position)</x-height>\n");+ os.put_string (@"\t<base_line>$(font.base_line)</base_line>\n");+ os.put_string (@"\t<bottom_position>$(font.bottom_position)</bottom_position>\n");+ os.put_string (@"\t<bottom_limit>$(font.bottom_limit)</bottom_limit>\n");+ os.put_string ("</horizontal>\n");+ }++ public void write_glyph_collection_start (GlyphCollection gc, DataOutputStream os) throws GLib.Error {+ os.put_string (@"<collection unicode=\"$(Font.to_hex (gc.get_current ().unichar_code))\">\n"); + }++ public void write_glyph_collection_end (DataOutputStream os) throws GLib.Error {+ os.put_string ("</collection>\n");+ }++ public void write_selected (GlyphCollection gc, DataOutputStream os) throws GLib.Error {+ os.put_string (@"\t<selected id=\"$(gc.get_selected_id ())\"/>\n");+ }++ public void write_glyph_collection (GlyphCollection gc, DataOutputStream os) throws GLib.Error {+ write_glyph_collection_start (gc, os);+ write_selected (gc, os);+ foreach (Glyph g in gc.get_version_list ().glyphs) {+ write_glyph (g, gc, os);+ }+ write_glyph_collection_end (os);+ } ++ public void write_glyph (Glyph g, GlyphCollection gc, DataOutputStream os) throws GLib.Error {+ string data;+ + os.put_string (@"\t<glyph id=\"$(g.version_id)\" left=\"$(g.left_limit)\" right=\"$(g.right_limit)\">\n");+ foreach (Path p in g.path_list) {+ data = get_point_data (p);+ if (data != "") {+ os.put_string (@"\t\t<path stroke=\"$(p.stroke)\" data=\"$(data)\" />\n");+ }+ }+ write_glyph_background (g, os);+ os.put_string ("\t</glyph>\n");+ }+ + public static string float_point (double d) { string s = @"$d"; return s.replace (",", "."); }@@ -419,32 +496,7 @@ class BirdFontFile { data.append (@"T"); } }-- private void write_glyph_collection (GlyphCollection gc, DataOutputStream os) throws GLib.Error {- os.put_string (@"<collection unicode=\"$(Font.to_hex (gc.get_current ().unichar_code))\">\n");- os.put_string (@"\t<selected id=\"$(gc.get_selected_id ())\"/>\n");- foreach (Glyph g in gc.get_version_list ().glyphs) {- write_glyph (g, gc, os);- }- os.put_string ("</collection>\n");- } -- private void write_glyph (Glyph g, GlyphCollection gc, DataOutputStream os) throws GLib.Error {- string data;- - os.put_string (@"\t<glyph id=\"$(g.version_id)\" left=\"$(g.left_limit)\" right=\"$(g.right_limit)\">\n");- foreach (Path p in g.path_list) {- data = get_point_data (p);- if (data != "") {- os.put_string (@"\t\t<path stroke=\"$(p.stroke)\" data=\"$(data)\" />\n");- }- }- - write_glyph_background (g, os);- - os.put_string ("\t</glyph>\n");- }-+ private void write_glyph_background (Glyph g, DataOutputStream os) throws GLib.Error { GlyphBackgroundImage? bg; GlyphBackgroundImage background_image;@@ -452,6 +504,7 @@ class BirdFontFile { bg = g.get_background_image (); + // FIXME: use the coordinate system if (bg != null) { background_image = (!) bg; @@ -574,12 +627,7 @@ class BirdFontFile { } private void create_background_files (Xml.Node* root) requires (root != null) {- while (font.background_images.length () > 0) {- font.background_images.remove_link (font.background_images.first ());- }- - for (Xml.Node* iter = root->children; iter != null; iter = iter->next) {- + for (Xml.Node* iter = root->children; iter != null; iter = iter->next) { if (iter->name == "name" && iter->children != null) { font.set_name (iter->children->content); }@@ -895,7 +943,7 @@ class BirdFontFile { string attr_content; StringBuilder b; string name = "";- int selected_id = 1;+ int selected_id = -1; for (Xml.Attr* prop = node->properties; prop != null; prop = prop->next) { attr_name = prop->name;@@ -915,7 +963,7 @@ class BirdFontFile { current_gc = font.get_glyph_collection_by_name (name); new_glyph_collection = (current_gc == null);- gc = (!new_glyph_collection) ? (!) current_gc : new GlyphCollection ();+ gc = (!new_glyph_collection) ? (!) current_gc : new GlyphCollection (unicode, name); for (Xml.Node* iter = node->children; iter != null; iter = iter->next) { if (iter->name == "selected") {@@ -1372,7 +1420,8 @@ class BirdFontFile { } if (gc == null) {- gc = new GlyphCollection (g);+ gc = new GlyphCollection (uni, name);+ ((!) gc).insert_glyph (g, selected); font.add_glyph_collection ((!) gc); } else { ((!)gc).insert_glyph (g, selected);@@ -1463,7 +1512,7 @@ class BirdFontFile { } } - img.set_position(img.img_x, img.img_y); + img.set_position (img.img_x, img.img_y); } private void parse_point (Path p, Xml.Node* iter) {new file mode 100644@@ -0,0 +1,404 @@+/*+ Copyright (C) 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 + 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 {++/** BirdFontPart is a class for parsing .bfp files. The file format is + * identical to .bf but the font is split in many parts. Each part+ * contains a few elements and all parent nodes from the root node and+ * downwards. The .bfp files can be parsed in any order. The root directory+ * of a .bfp tree must have a file with the name "description.bfp", this file + * tells the parser that bfp files in parent directories should be excluded.+ */+class BirdFontPart : GLib.Object{+ Font font;+ List<string> parts;+ string root_directory;++ static string FILE_ATTRIBUTES = "standard::*";++ public BirdFontPart (Font font) { + this.font = font;+ parts = new List<string> ();+ root_directory = "";+ }++ public bool load (string bfp_file) {+ BirdFontFile bf = new BirdFontFile (font);+ File bfp_dir;+ File image_dir;+ + try {+ find_all_parts (bfp_file);+ font.set_bfp (true);+ + while (font.background_images.length () > 0) {+ font.background_images.remove_link (font.background_images.first ());+ }+ + bfp_dir = File.new_for_path (root_directory);+ image_dir = bfp_dir.get_child ("images");+ copy_backgrounds ((!) image_dir.get_path ());++ foreach (string fn in parts) {+ bf.load_part (fn);+ }+ } catch (GLib.Error e) {+ warning (e.message);+ return false;+ }+ + return true;+ }+ + public string get_path () {+ string path = "";+ + try {+ path = (!) get_destination_file (@"$(font.full_name).bfp").get_path ();+ } catch (GLib.Error e) {+ warning (e.message);+ }+ + return path;+ }+ + public bool save () {+ DataOutputStream os;+ BirdFontFile bf = new BirdFontFile (font);+ bool error = false;+ + if (root_directory == "") {+ warning ("No directory is created for this birdfont part.");+ return false;+ }+ + try {+ os = create_file (@"$(font.full_name).bfp");+ bf.write_root_tag (os);+ bf.write_closing_root_tag (os);+ os.close ();+ + os = create_file ("description.bfp");+ bf.write_root_tag (os);+ bf.write_description (os);+ bf.write_closing_root_tag (os);+ os.close ();++ os = create_file ("lines.bfp");+ bf.write_root_tag (os);+ bf.write_lines (os);+ bf.write_closing_root_tag (os);+ os.close ();++ os = create_file ("settings.bfp");+ bf.write_root_tag (os);+ bf.write_settings (os);+ bf.write_closing_root_tag (os);+ os.close ();++ font.glyph_cache.for_each ((gc) => {+ try {+ string file_name;+ string dir_name;+ + if (is_null (gc)) {+ warning ("No glyph collection");+ }++ file_name = get_first_number_in_unicode (((!)gc).get_current ());+ dir_name = (!)file_name.get_char ().to_string ();+ + os = create_file (@"selected_$(file_name).bfp", "glyphs", dir_name);+ bf.write_root_tag (os);+ bf.write_glyph_collection_start (gc, os);+ bf.write_selected ((!) gc, os);+ bf.write_glyph_collection_end (os);+ bf.write_closing_root_tag (os);+ os.close ();+ + foreach (Glyph g in gc.get_version_list ().glyphs) {+ try {+ write_glyph (bf, gc, g, os);+ write_glyph_background_image (bf, gc, g, os);+ } catch (GLib.Error e) {+ warning (e.message);+ }+ }+ } catch (GLib.Error e) {+ warning (@"Can not save bfp files to $root_directory\n");+ warning (@"$(e.message) \n");+ error = true;+ }+ });++ os = create_file ("kerning.bfp");+ bf.write_root_tag (os);+ bf.write_kerning (os);+ bf.write_closing_root_tag (os);+ os.close ();+ + } catch (GLib.Error e) {+ warning (@"Failed to save bfp files to $root_directory\n");+ warning (@"$(e.message) \n");+ return false;+ }+ + return error;+ }++ void copy_backgrounds (string folder) throws GLib.Error {+ FileInfo info;+ FileInfo? fi;+ FileEnumerator e;+ string name;+ File image_dir;+ GlyphBackgroundImage bg;+ File found;+ File dest;+ + image_dir = File.new_for_path (folder);+ + if (image_dir.query_exists ()) {+ info = image_dir.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE);+ if (info.get_file_type () != FileType.DIRECTORY) {+ warning (@"$((!) image_dir.get_path ()) is not a directory.");+ throw new FileError.NOTDIR ("Not a directory.");+ }++ e = image_dir.enumerate_children (FILE_ATTRIBUTES, 0);+ while ((fi = e.next_file ()) != null) {+ info = (!) fi;+ name = info.get_name ();+ + if (info.get_file_type () == FileType.DIRECTORY) {+ found = image_dir.get_child (name);+ copy_backgrounds ((!) found.get_path ());+ }+ + if (name.has_suffix (".png")) {+ found = image_dir.get_child (name);+ dest = font.get_backgrounds_folder ().get_child ("parts").get_child (name);+ bg = new GlyphBackgroundImage ((!) found.get_path ());+ bg.create_background_folders (font);+ bg.copy_if_new (dest);+ }+ }+ }+ }++ string get_first_number_in_unicode (Glyph g) throws GLib.Error {+ string s = Font.to_hex (g.unichar_code);+ s = s.replace ("U+", "");+ return s;+ }+ + string get_glyph_base_file_name (Glyph g) throws GLib.Error {+ string s = get_first_number_in_unicode (g);+ s = @"$(s)_$(g.version_id)";+ return s;+ }++ void write_glyph (BirdFontFile bf, GlyphCollection gc, Glyph g, DataOutputStream os) throws GLib.Error {+ string file_name;+ string dir_name;+ + file_name = get_glyph_base_file_name (g);+ dir_name = (!)file_name.get_char ().to_string ();+ + os = create_file (@"$(file_name).bfp", "glyphs", dir_name);+ bf.write_root_tag (os);+ bf.write_glyph_collection_start (gc, os);+ bf.write_glyph (g, gc, os);+ bf.write_glyph_collection_end (os);+ bf.write_closing_root_tag (os);+ os.close ();+ }++ void write_glyph_background_image (BirdFontFile bf, GlyphCollection gc, Glyph g, DataOutputStream os) throws GLib.Error {+ string file_name;+ string dir_name;+ GlyphBackgroundImage bg;+ File file;+ + if (g.get_background_image () != null) {+ bg = (!) g.get_background_image ();++ if (bg.is_valid ()) {+ file_name = @"$(bg.get_sha1 ()).png";+ dir_name = (!)file_name.get_char ().to_string ();+ file = get_destination_file (file_name, "images", dir_name);+ bg.copy_if_new (file); + + // FIXME: GIT ADD+ } + }+ }+ + public void create_directory (string directory) throws GLib.Error { + File dir = File.new_for_path (directory);+ File bfp_dir;+ int i = 2;+ + if (directory.has_suffix (font.get_full_name ())) {+ bfp_dir = dir;+ } else {+ bfp_dir = dir.get_child (font.get_full_name ());+ }+ + while (bfp_dir.query_exists ()) {+ bfp_dir = dir.get_child (@"$(font.get_full_name ())_$(i)");+ i++;+ }+ + if (!dir.query_exists ()) {+ DirUtils.create ((!) dir.get_path (), 0xFFFFFF);+ }+ + root_directory = (!) bfp_dir.get_path ();+ DirUtils.create (root_directory, 0xFFFFFF);+ }++ private void find_all_parts (string bfp_file) throws GLib.Error { + File start = File.new_for_path (bfp_file);+ FileInfo info;+ File root;+ + info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE);+ if (info.get_file_type () != FileType.DIRECTORY) {+ start = (!) start.get_parent ();+ }++ root = find_root ((!)start.get_path ());+ root_directory = (!)root.get_path ();++ find_parts (root_directory);+ }++ private void find_parts (string directory) throws GLib.Error { + File start = File.new_for_path (directory);+ File found;+ FileInfo info;+ FileInfo? fi;+ FileEnumerator e;+ string name;+ + + info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE);+ if (info.get_file_type () != FileType.DIRECTORY) {+ warning (@"$directory is not a directory.");+ throw new FileError.NOTDIR ("Not a directory.");+ }++ e = start.enumerate_children (FILE_ATTRIBUTES, 0);+ while ((fi = e.next_file ()) != null) {+ info = (!) fi;+ name = info.get_name ();+ if (info.get_file_type () == FileType.DIRECTORY) {+ find_parts ((!) ((!) start.get_child (name)).get_path ());+ } else if (name.has_suffix (".bfp")) {+ found = start.get_child (name);+ parts.append ((!) found.get_path ());+ }+ }+ }+ + private File find_root (string directory) throws GLib.Error {+ File start = File.new_for_path (directory);+ FileInfo info;+ FileInfo? fi;+ FileEnumerator e;+ + info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE);+ if (info.get_file_type () != FileType.DIRECTORY) {+ warning ("Not a directory.");+ throw new FileError.NOTDIR ("Not a directory.");+ }++ e = start.enumerate_children (FILE_ATTRIBUTES, 0);+ while ((fi = e.next_file ()) != null) {+ info = (!) fi;+ if (info.get_name () == "description.bfp") {+ return start;+ }+ }+ + if (start.get_parent () == null) {+ warning ("description.bfp not found");+ throw new FileError.FAILED ("description.bfp not found");+ }+ + return find_root ((!)((!) start.get_parent ()).get_path ());+ }+ + private File new_subdirectory (File d, string subdir) throws GLib.Error {+ FileInfo info;+ File dir;+ + dir = d;+ dir = dir.get_child (subdir);+ + if (!dir.query_exists ()) {+ DirUtils.create ((!) dir.get_path (), 0xFFFFFF);+ } else {+ info = dir.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE);+ if (info.get_file_type () != FileType.DIRECTORY) {+ throw new FileError.FAILED (@"Can't save font, $subdir is not a directory.");+ }+ }+ return dir;+ }+ + private File get_destination_file (string name, string subdir1 = "", string subdir2 = "") throws GLib.Error {+ File file;+ File dir;++ dir = File.new_for_path (root_directory);+ + if (subdir1 != "") {+ dir = new_subdirectory (dir, subdir1);+ }++ if (subdir2 != "") {+ dir = new_subdirectory (dir, subdir2);+ }+ + file = dir.get_child (name);++ if (file.query_file_type (0) == FileType.DIRECTORY) {+ throw new FileError.FAILED (@"Can't save font, $name is a directory.");+ }+ + return file;+ }+ + private DataOutputStream create_file (string name, string subdir1 = "", string subdir2 = "") throws GLib.Error {+ DataOutputStream os;+ File file;+ + file = get_destination_file (name, subdir1, subdir2);+ + if (file.query_exists ()) {+ file.delete ();+ }+ + os = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION));+ + // FIXME: GIT ADD+ + return os;+ }+}++}@@ -49,6 +49,14 @@ public class DropMenu : GLib.Object { } } + public MenuAction get_action_index (int index) {+ if (!(0 <= index < actions.length ())) {+ warning (@"No action for index $index.");+ return new MenuAction ("None");+ }+ return actions.nth (index).data;+ }+ public void recreate_index () { int i = -1; foreach (MenuAction a in actions) {@@ -44,7 +44,11 @@ public class FileTab : FontDisplay { dialog.signal_discard.connect (() => { Font f; bool loaded;- ++ if (MenuTab.suppress_event) {+ return;+ }+ f = BirdFont.new_font (); f.delete_backup (); @@ -69,6 +73,10 @@ public class FileTab : FontDisplay { }); dialog.signal_save.connect (() => {+ if (MenuTab.suppress_event) {+ return;+ }+ MenuTab.save (); dialog.signal_discard (); });@@ -115,6 +123,10 @@ public class FileTab : FontDisplay { public override void button_release (int button, double ex, double ey) { int r, i; + if (MenuTab.suppress_event) {+ return;+ }+ return_if_fail (!is_null(this)); r = (int) rint ((ey - 17) / row_height) + scroll;@@ -1,5 +1,5 @@ /*- Copyright (C) 2012, 2013 Johan Mattsson+ Copyright (C) 2012, 2013, 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 @@ -18,6 +18,7 @@ namespace BirdFont { public enum FontFormat { BIRDFONT,+ BIRDFONT_PART, FFI, SVG, FREETYPE@@ -87,6 +88,10 @@ public class Font : GLib.Object { bool read_only = false; + /** Save font as many .bfp files instead of one big .bf */+ bool bfp = false;+ BirdFontPart bfp_file;+ public Font () { postscript_name = "Typeface"; name = "Typeface";@@ -105,14 +110,14 @@ public class Font : GLib.Object { kerning_classes = new KerningClasses (); - // positions in pixels at first zoom level- // default x-height should be 60 in 1:1 top_limit = 84 ; top_position = 72; xheight_position = 56; base_line = 0; bottom_position = -20; bottom_limit = -27;+ + bfp_file = new BirdFontPart (this); } public void set_weight (string w) {@@ -324,25 +329,29 @@ public class Font : GLib.Object { return g; }- + + // FIXME: DELETE? public void add_glyph (Glyph glyph) { GlyphCollection? gc = get_glyph_collection (glyph.get_name ());-+ GlyphCollection c;+ if (gc == null) {- add_glyph_collection (new GlyphCollection (glyph));+ c = new GlyphCollection (glyph.get_unichar (), glyph.get_name ());+ c.insert_glyph (glyph, false);+ add_glyph_collection (c); } } public void add_glyph_collection (GlyphCollection glyph_collection) { GlyphCollection? gc; - if (glyph_collection.get_name () == "") {- warning ("Refusing to insert glyph with name \"\", null character should be named null.");+ if (unlikely (glyph_collection.get_name () == "")) {+ warning ("Refusing to add glyph with name \"\", null character should be named null."); return; } gc = glyph_name.get (glyph_collection.get_name ());- if (gc != null) {+ if (unlikely (gc != null)) { warning ("glyph has already been added"); return; }@@ -352,8 +361,6 @@ public class Font : GLib.Object { } glyph_cache.insert (glyph_collection.get_unicode (), glyph_collection);- - printd (@"Adding $(glyph_collection.get_current ().get_name ())\n"); } public string get_name_for_character (unichar c) {@@ -389,7 +396,7 @@ public class Font : GLib.Object { ligature.remove (glyph.get_current ().get_ligature_string ()); } - // FIXME: order of ligature substitutions is important+ // FIXME: the order of ligature substitutions public GlyphCollection? get_ligature (uint indice) { return ligature.nth (indice); }@@ -404,7 +411,6 @@ public class Font : GLib.Object { g = otf.read_glyph (glyph); if (g != null) {- add_glyph_callback ((!) g); return get_cached_glyph_collection (glyph); } }@@ -514,6 +520,31 @@ public class Font : GLib.Object { return backup_file; } + public void init_bfp (string directory) {+ try {+ bfp_file = new BirdFontPart (this);+ bfp_file.create_directory (directory);+ bfp_file.save ();+ this.bfp = true;+ Preferences.add_recent_files (bfp_file.get_path ());+ } catch (GLib.Error e) {+ warning (e.message);+ // FIXME: notify user+ }+ }++ public void set_bfp (bool bfp) {+ this.bfp = bfp;+ }++ public bool is_bfp () {+ return bfp;+ }+ + public bool save_bfp () {+ return bfp_file.save ();+ }+ public bool save (string path) { Font font; BirdFontFile birdfont_file = new BirdFontFile (this);@@ -594,6 +625,12 @@ public class Font : GLib.Object { loaded = parse_bf_file (path); format = FontFormat.BIRDFONT; }++ if (path.has_suffix (".bfp")) {+ font_file = path;+ loaded = parse_bfp_file (path);+ format = FontFormat.BIRDFONT_PART;+ } if (path.has_suffix (".ttf")) { font_file = path;@@ -626,15 +663,7 @@ public class Font : GLib.Object { format = FontFormat.FREETYPE; } - - /* // TODO: Remove the old way of loading ttfs when testing of the OTF writer is complete.- if (BirdFont.experimental) {- if (path.has_suffix (".ttf")) {- font_file = path;- loaded = parse_otf_file (path);- }- }*/- + if (recent) { Preferences.add_recent_files (get_path ()); }@@ -642,6 +671,10 @@ public class Font : GLib.Object { return loaded; } + private bool parse_bfp_file (string path) {+ return bfp_file.load (path);+ }+ private bool parse_bf_file (string path) { BirdFontFile font = new BirdFontFile (this); return font.load (path);@@ -684,31 +717,6 @@ public class Font : GLib.Object { return true; } - /** Callback function for loading glyph in a separate thread. */- public void add_glyph_callback (Glyph g) {- GlyphCollection? gcl;- GlyphCollection gc;- string liga;- - gcl = get_cached_glyph_collection (g.get_name ());- - if (gcl != null) {- warning (@"glyph \"$(g.get_name ())\" does already exist");- }- - if (g.is_unassigned ()) {- gc = new GlyphCollection (g);- }-- gc = new GlyphCollection (g);- add_glyph_collection (gc);-- if (g.is_ligature ()) {- liga = g.get_ligature_string ();- ligature.insert (liga, gc);- }- }- public bool parse_otf_file (string path) throws GLib.Error { otf = new OpenFontFormatReader (); otf_font = true;@@ -53,7 +53,9 @@ public class Glyph : FontDisplay { public double move_offset_y = 0; public WidgetAllocation allocation = new WidgetAllocation ();- ++ bool unassigned = false;+ public unichar unichar_code = 0; // FIXME: name and unichar should be moved to to glyph collection public string name; public double left_limit;@@ -67,9 +69,6 @@ public class Glyph : FontDisplay { bool xheight_lines_visible = false; bool margin_boundaries_visible = false; - bool unassigned = false;- public unichar unichar_code = 0;- List<Glyph> undo_list = new List<Glyph> (); string glyph_sequence = "";@@ -296,12 +295,11 @@ public class Glyph : FontDisplay { if (b == null) { background_image = null;+ } else { + bg = (!) b;+ background_image = bg; } - bg = (!) b;- - background_image = bg;- BirdFont.get_current_font ().touch (); } @@ -64,8 +64,8 @@ public class GlyphBackgroundImage { } } - public GlyphBackgroundImage (string fn) {- path = fn;+ public GlyphBackgroundImage (string file_name) {+ path = file_name; } public GlyphBackgroundImage copy () {@@ -105,7 +105,7 @@ public class GlyphBackgroundImage { } public ImageSurface get_img () {- if (path.index_of (".png") == -1) {+ if (!path.has_suffix (".png")) { create_png (); } @@ -148,12 +148,12 @@ public class GlyphBackgroundImage { DataInputStream png_stream; if (!file.query_exists ()) {- warning (@"Failed to save image $path, file does not exist.");+ warning (@"Can't to save image $path, file does not exist."); return ""; } if (is_null (buffer)) {- warning (@"Colud not allocate a buffer of $(file_info.get_size ()) bytes to store $path.");+ warning (@"Can not allocate a buffer of $(file_info.get_size ()) bytes to store $path."); return ""; } @@ -168,6 +168,65 @@ public class GlyphBackgroundImage { } return "";++ }++ public void create_background_folders (Font font) {+ File dir;+ try {+ dir = BirdFont.get_settings_directory ();+ if (!dir.query_exists ()) {+ DirUtils.create ((!) dir.get_path (), 0xFFFFFF);+ }+ + dir = font.get_backgrounds_folder ();+ if (!dir.query_exists ()) {+ DirUtils.create ((!) dir.get_path (), 0xFFFFFF);+ }++ dir = font.get_backgrounds_folder ().get_child ("parts");+ if (!dir.query_exists ()) {+ DirUtils.create ((!) dir.get_path (), 0xFFFFFF);+ }+ } catch (GLib.Error e) {+ warning (e.message);+ }+ }++ public void copy_if_new (File destination) {+ if (!destination.query_exists ()) {+ copy_file (destination);+ }+ }++ public void copy_file (File destination) {+ File source;+ FileInfo info;+ + try {+ if (destination.query_exists ()) {+ info = destination.query_info ("standard::*", FileQueryInfoFlags.NONE);+ if (info.get_file_type () == FileType.DIRECTORY) {+ warning (@"$((!) destination.get_path ()) is a directory.");+ return;+ }+ }+ + if (!((!)destination.get_parent ()).query_exists ()) {+ warning (@"Directory for file $((!) destination.get_path ()) is not created.");+ return;+ }+ + if (destination.query_exists ()) {+ warning (@"Image $((!) destination.get_path ()) is already created.");+ return;+ }+ + source = File.new_for_path (path);+ source.copy (destination, FileCopyFlags.NONE);+ } catch (GLib.Error e) {+ warning (e.message);+ } } public string get_sha1 () {@@ -291,7 +350,7 @@ public class GlyphBackgroundImage { double wc, hc; if (unlikely (get_img ().status () != Cairo.Status.SUCCESS)) {- stderr.printf (@"Background image is invalid. (\"$path\")\n");+ warning (@"Background image is invalid. (\"$path\")\n"); MainWindow.get_current_glyph ().set_background_visible (false); return; }@@ -734,7 +793,7 @@ public class GlyphBackgroundImage { h = img.get_height(); if (unlikely (img.status () != Cairo.Status.SUCCESS)) {- warning ("Err");+ warning ("Error"); return p; } @@ -19,9 +19,13 @@ namespace BirdFont { public class GlyphCollection : GLib.Object { VersionList versions;+ unichar unicode_character;+ string name; - public GlyphCollection (Glyph? current = null) {- versions = new VersionList (current);+ public GlyphCollection (unichar unicode_character, string name) {+ this.unicode_character = unicode_character;+ this.name = name;+ versions = new VersionList (null); } public VersionList get_version_list () {@@ -43,12 +47,12 @@ public class GlyphCollection : GLib.Object { public string get_unicode () { StringBuilder unicode = new StringBuilder ();- unicode.append_unichar (get_current ().unichar_code);+ unicode.append_unichar (unicode_character); return unicode.str; } public string get_name () {- return get_current ().get_name ();+ return name; } public int get_selected_id () {@@ -102,6 +102,7 @@ public class MenuTab : FontDisplay { save (); saved = true; }+ return saved; } @@ -112,39 +113,68 @@ public class MenuTab : FontDisplay { if (suppress_event) { return false;- } + } if (!set_suppress_event (true)) { return false; } f = BirdFont.get_current_font ();- f.delete_backup ();- - fn = f.get_path ();- - if (f.font_file != null && fn.has_suffix (".bf")) {- f.background_scale = MainWindow.get_drawing_tools ().background_scale.get_display_value ();- - while (f.grid_width.length () > 0) {- f.grid_width.remove_link (f.grid_width.first ());- }- - foreach (SpinButton s in GridTool.sizes) {- f.grid_width.append (s.get_display_value ());- }- - f.save (fn);- saved = true;++ if (f.is_bfp ()) {+ saved = f.save_bfp (); set_suppress_event (false); } else {- set_suppress_event (false);- saved = save_as ();+ f.delete_backup ();+ fn = f.get_path ();+ + if (f.font_file != null && fn.has_suffix (".bf")) {+ set_font_setting_from_tools (f); + saved = f.save (fn);+ set_suppress_event (false);+ } else {+ set_suppress_event (false);+ saved = save_as ();+ } } return saved; } + public static void set_font_setting_from_tools (Font f) { + f.background_scale = MainWindow.get_drawing_tools ().background_scale.get_display_value ();+ + while (f.grid_width.length () > 0) {+ f.grid_width.remove_link (f.grid_width.first ());+ }+ + foreach (SpinButton s in GridTool.sizes) {+ f.grid_width.append (s.get_display_value ());+ }+ }+ + public static void save_as_bfp () {+ Font f;+ string? fn;+ + if (suppress_event) {+ return;+ } + + if (!set_suppress_event (true)) {+ return;+ }+ + f = BirdFont.get_current_font (); + fn = MainWindow.file_chooser_save (t_("Save"));+ + if (fn != null) {+ f.init_bfp ((!) fn);+ }+ + set_suppress_event (false);+ }+ public static void new_file () { Font font; SaveDialogListener dialog = new SaveDialogListener ();@@ -244,6 +244,11 @@ public class OverView : FontDisplay { } OverViewItem get_selected_item () {+ if (visible_items.length () == 0) {+ warning ("No items.");+ return new OverViewItem (null, '\0', 0, 0);+ }+ if (unlikely (!(0 <= selected < visible_items.length ()))) { warning (@"0 <= $selected < $(visible_items.length ())"); new OverViewItem (null, '\0', 0, 0);@@ -273,7 +273,7 @@ class SvgFont : GLib.Object { glyph.set_ligature_substitution (ligature); } - font.add_glyph_callback (glyph);+ font.add_glyph (glyph); } } @@ -125,6 +125,11 @@ public class TabBar : GLib.Object { */ public bool select_char (string s) { int i = 0;+ + if (MenuTab.suppress_event) {+ return false;+ }+ foreach (Tab t in tabs) { if (t.get_display ().get_name () == s) { select_tab (i);@@ -137,10 +142,18 @@ public class TabBar : GLib.Object { } public bool select_tab_name (string s) {+ if (MenuTab.suppress_event) {+ return false;+ }+ return select_char (s); } public void select_overview () {+ if (MenuTab.suppress_event) {+ return;+ }+ select_tab_name ("Overview"); } @@ -148,6 +161,10 @@ public class TabBar : GLib.Object { Tab t; bool open; + if (MenuTab.suppress_event) {+ return;+ }+ if (previous_tab == null) { return; }@@ -163,6 +180,10 @@ public class TabBar : GLib.Object { public void close_display (FontDisplay f) { int i = -1; + if (MenuTab.suppress_event) {+ return;+ }+ if (tabs.length () > 1) { foreach (var t in tabs) { ++i;@@ -178,6 +199,10 @@ public class TabBar : GLib.Object { } public void close_all_tabs () {+ if (MenuTab.suppress_event) {+ return;+ }+ for (int i = 0; i < get_length (); i++) { if (close_tab (i, false, true)) { close_all_tabs ();@@ -190,7 +215,11 @@ public class TabBar : GLib.Object { Tab t; EmptyTab empty_tab_canvas; Tab empty_tab;- + + if (MenuTab.suppress_event) {+ return false;+ }+ if (!(0 <= index < tabs.length ())) { return false; }@@ -233,7 +262,11 @@ public class TabBar : GLib.Object { public bool close_by_name (string name, bool background_tab = false) { int i = 0;- ++ if (MenuTab.suppress_event) {+ return false;+ }+ foreach (var t in tabs) { if (t.get_display ().get_name () == name) { return close_tab (i, background_tab);@@ -246,12 +279,21 @@ public class TabBar : GLib.Object { } public void close_background_tab_by_name (string name) {+ if (MenuTab.suppress_event) {+ return;+ }+ close_by_name (name, true); } /** Select a tab and return true if it is open. */ public bool selected_open_tab (Tab t) { int i = 0;++ if (MenuTab.suppress_event) {+ return false;+ }+ foreach (var n in tabs) { if (n == t) { select_tab (i);@@ -284,6 +326,11 @@ public class TabBar : GLib.Object { public bool selected_open_tab_by_name (string t) { int i = 0;+ + if (MenuTab.suppress_event) {+ return false;+ }+ foreach (var n in tabs) { if (n.get_display ().get_name () == t) { select_tab (i);@@ -310,7 +357,11 @@ public class TabBar : GLib.Object { public void select_tab (int index, bool signal_selected = true) { Tab t;- ++ if (MenuTab.suppress_event) {+ return;+ }+ // always close any pending text input if the user switches tab MainWindow.native_window.hide_text_input (); @@ -455,7 +506,11 @@ public class TabBar : GLib.Object { double tab_width = -1; bool always_open = false; int s = (tabs.length () == 0) ? 0 : selected + 1;- ++ if (MenuTab.suppress_event) {+ return;+ }+ if (tab_width < 0) { tab_width = 9 * display_item.get_label ().char_count (); tab_width += 36;@@ -467,7 +522,13 @@ public class TabBar : GLib.Object { /** Returns true if the new item was added to the bar. */ public bool add_unique_tab (FontDisplay display_item, bool signal_selected = true) {- bool i = select_tab_name (display_item.get_name ());+ bool i;+ + if (MenuTab.suppress_event) {+ return false;+ }+ + i = select_tab_name (display_item.get_name ()); if (!i) { add_tab (display_item, signal_selected);@@ -77,6 +77,7 @@ public class VersionList : DropMenu { public void set_selected_version (int version_id) { current_version_id = version_id;+ update_selection (); } public Glyph get_current () {@@ -195,9 +196,7 @@ public class VersionList : DropMenu { ma.index = (int) glyphs.length () - 1; ma.action = (self) => {- Font font;- - font = BirdFont.get_current_font ();+ Font font = BirdFont.get_current_font (); set_selected_item (self); font.touch (); };@@ -205,6 +204,23 @@ public class VersionList : DropMenu { if (selected) { set_selected_item (ma); }+ + update_selection ();+ }+ + bool has_version (int id) {+ foreach (Glyph g in glyphs) {+ if (g.version_id == id) {+ return true;+ }+ }+ return false;+ }+ + void update_selection () {+ if (has_version (current_version_id)) {+ set_selected_item (get_action_index (get_current_version_index () + 1)); // the first item is the "new version"+ } } }