The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

BirdFontPart.vala in libbirdfont

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git

Revisions

View the latest version of libbirdfont/BirdFontPart.vala.
Fix zero length handles
1 /* 2 Copyright (C) 2014 Johan Mattsson 3 4 This library is free software; you can redistribute it and/or modify 5 it under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 3 of the 7 License, or (at your option) any later version. 8 9 This library is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 Lesser General Public License for more details. 13 */ 14 15 namespace BirdFont { 16 17 /** BirdFontPart is a class for parsing .bfp files. The file format is 18 * identical to .bf but the font is split in many parts. Each part 19 * contains a few elements and all parent nodes from the root node and 20 * downwards. The .bfp files can be parsed in any order. The root directory 21 * of a .bfp tree must have a file with the name "description.bfp", this file 22 * tells the parser that bfp files in parent directories should be excluded. 23 */ 24 public class BirdFontPart : GLib.Object{ 25 unowned Font font; 26 Gee.ArrayList<string> parts; 27 string root_directory; 28 29 static string FILE_ATTRIBUTES = "standard::*"; 30 31 public BirdFontPart (Font font) { 32 this.font = font; 33 34 font.font_deleted.connect (() => { 35 this.font = Font.empty; 36 }); 37 38 parts = new Gee.ArrayList<string> (); 39 root_directory = ""; 40 } 41 42 public bool load (string bfp_file) { 43 BirdFontFile bf = new BirdFontFile (font); 44 File bfp_dir; 45 File image_dir; 46 47 try { 48 find_all_parts (bfp_file); 49 font.set_bfp (true); 50 51 font.background_images.clear (); 52 53 bfp_dir = File.new_for_path (root_directory); 54 image_dir = get_child (bfp_dir, "images"); 55 copy_backgrounds ((!) image_dir.get_path ()); 56 57 foreach (string fn in parts) { 58 bf.load_part (fn); 59 } 60 } catch (GLib.Error e) { 61 warning (e.message); 62 return false; 63 } 64 65 return true; 66 } 67 68 public string get_path () { 69 string path = ""; 70 71 try { 72 path = (!) get_destination_file (@"$(font.full_name).bfp").get_path (); 73 } catch (GLib.Error e) { 74 warning (e.message); 75 } 76 77 return path; 78 } 79 80 public bool save () { 81 DataOutputStream os; 82 BirdFontFile bf = new BirdFontFile (font); 83 bool error = false; 84 string file_name; 85 string glyph_dir_name; 86 File glyph_file; 87 88 if (root_directory == "") { 89 warning ("No directory is created for this birdfont part."); 90 return false; 91 } 92 93 try { 94 // remove deleted glyphs 95 foreach (Glyph g in font.deleted_glyphs) { 96 file_name = get_glyph_base_file_name (g) + ".bfp"; 97 glyph_dir_name = get_subdir_name (file_name); 98 glyph_file = get_destination_file (file_name, "glyphs", glyph_dir_name); 99 100 if (glyph_file.query_exists ()) { 101 glyph_file.delete (); 102 } 103 104 print (@"$((!)glyph_file.get_path ())\n"); 105 } 106 107 os = create_file (@"$(font.full_name).bfp"); 108 bf.write_root_tag (os); 109 bf.write_closing_root_tag (os); 110 os.close (); 111 112 os = create_file ("description.bfp"); 113 bf.write_root_tag (os); 114 bf.write_description (os); 115 bf.write_closing_root_tag (os); 116 os.close (); 117 118 os = create_file ("lines.bfp"); 119 bf.write_root_tag (os); 120 bf.write_lines (os); 121 bf.write_closing_root_tag (os); 122 os.close (); 123 124 os = create_file ("settings.bfp"); 125 bf.write_root_tag (os); 126 bf.write_settings (os); 127 bf.write_closing_root_tag (os); 128 os.close (); 129 130 os = create_file ("spacing.bfp"); 131 bf.write_root_tag (os); 132 bf.write_spacing_classes (os); 133 bf.write_closing_root_tag (os); 134 os.close (); 135 136 os = create_file ("ligatures.bfp"); 137 bf.write_root_tag (os); 138 bf.write_ligatures (os); 139 bf.write_closing_root_tag (os); 140 os.close (); 141 142 os = create_file ("alternates.bfp"); 143 bf.write_root_tag (os); 144 bf.write_alternates (os); 145 bf.write_closing_root_tag (os); 146 os.close (); 147 148 font.glyph_cache.for_each ((gc) => { 149 try { 150 string selected_file_name; 151 string dir_name; 152 153 if (is_null (gc)) { 154 warning ("No glyph collection"); 155 } 156 157 selected_file_name = get_first_number_in_unicode (((!)gc).get_current ()); 158 dir_name = get_subdir_name (selected_file_name); 159 160 os = create_file (@"selected_$(selected_file_name).bfp", "glyphs", dir_name); 161 bf.write_root_tag (os); 162 bf.write_glyph_collection_start (gc, os); 163 bf.write_selected ((!) gc, os); 164 bf.write_glyph_collection_end (os); 165 bf.write_closing_root_tag (os); 166 os.close (); 167 168 foreach (Glyph g in gc.glyphs) { 169 try { 170 write_glyph (bf, gc, g); 171 write_glyph_background_image (bf, gc, g); 172 } catch (GLib.Error e) { 173 warning (e.message); 174 } 175 } 176 } catch (GLib.Error e) { 177 warning (@"Can not save bfp files to $root_directory\n"); 178 warning (@"$(e.message) \n"); 179 error = true; 180 } 181 }); 182 183 os = create_file ("kerning.bfp"); 184 bf.write_root_tag (os); 185 bf.write_kerning (os); 186 bf.write_closing_root_tag (os); 187 os.close (); 188 189 os = create_file ("images.bfp"); 190 bf.write_root_tag (os); 191 bf.write_images (os); 192 bf.write_closing_root_tag (os); 193 os.close (); 194 195 } catch (GLib.Error e) { 196 warning (@"Failed to save bfp files to $root_directory\n"); 197 warning (@"$(e.message) \n"); 198 error = true; 199 } 200 201 return !error; 202 } 203 204 void copy_backgrounds (string folder) throws GLib.Error { 205 FileInfo info; 206 FileInfo? fi; 207 FileEnumerator e; 208 string name; 209 File image_dir; 210 BackgroundImage bg; 211 File found; 212 File parts; 213 File dest; 214 215 image_dir = File.new_for_path (folder); 216 217 if (image_dir.query_exists ()) { 218 info = image_dir.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 219 if (info.get_file_type () != FileType.DIRECTORY) { 220 warning (@"$((!) image_dir.get_path ()) is not a directory."); 221 throw new FileError.NOTDIR ("Not a directory."); 222 } 223 224 e = image_dir.enumerate_children (FILE_ATTRIBUTES, 0); 225 while ((fi = e.next_file ()) != null) { 226 info = (!) fi; 227 name = info.get_name (); 228 229 if (info.get_file_type () == FileType.DIRECTORY) { 230 found = get_child (image_dir, name); 231 copy_backgrounds ((!) found.get_path ()); 232 } 233 234 if (name.has_suffix (".png")) { 235 found = get_child (image_dir, name); 236 parts = get_child (font.get_backgrounds_folder (), "parts"); 237 dest = get_child (parts, name); 238 bg = new BackgroundImage ((!) found.get_path ()); 239 bg.create_background_folders (font); 240 bg.copy_if_new (dest); 241 } 242 } 243 } 244 } 245 246 string get_first_number_in_unicode (Glyph g) throws GLib.Error { 247 string s = Font.to_hex (g.unichar_code); 248 s = s.replace ("U+", ""); 249 return s; 250 } 251 252 string get_glyph_base_file_name (Glyph g) throws GLib.Error { 253 string s = get_first_number_in_unicode (g); 254 s = @"U+$(s)_$(g.version_id)"; 255 return s; 256 } 257 258 public string get_subdir_name (string file_name) { 259 string d = file_name; 260 261 if (file_name.has_prefix ("U+")) { 262 d = file_name.replace ("U+", ""); 263 } 264 265 return (!) d.get_char ().to_string (); 266 } 267 268 void write_glyph (BirdFontFile bf, GlyphCollection gc, Glyph g) throws GLib.Error { 269 string file_name; 270 string dir_name; 271 DataOutputStream os; 272 273 file_name = get_glyph_base_file_name (g); 274 dir_name = get_subdir_name (file_name); 275 276 os = create_file (@"$(file_name).bfp", "glyphs", dir_name); 277 bf.write_root_tag (os); 278 bf.write_glyph_collection_start (gc, os); 279 bf.write_glyph (g, os); 280 bf.write_glyph_collection_end (os); 281 bf.write_closing_root_tag (os); 282 os.close (); 283 } 284 285 void write_glyph_background_image (BirdFontFile bf, GlyphCollection gc, Glyph g) throws GLib.Error { 286 string file_name; 287 string dir_name; 288 BackgroundImage bg; 289 File file; 290 291 if (g.get_background_image () != null) { 292 bg = (!) g.get_background_image (); 293 294 if (bg.is_valid ()) { 295 file_name = @"$(bg.get_sha1 ()).png"; 296 dir_name = get_subdir_name (file_name); 297 file = get_destination_file (file_name, "images", dir_name); 298 bg.copy_if_new (file); 299 300 // FIXME: GIT ADD 301 } 302 } 303 } 304 305 public void create_directory (string directory) throws GLib.Error { 306 File dir = File.new_for_path (directory); 307 File bfp_dir; 308 int i = 2; 309 310 if (directory.has_suffix (font.get_full_name ())) { 311 bfp_dir = dir; 312 } else { 313 bfp_dir = get_child (dir, font.get_full_name ()); 314 } 315 316 while (bfp_dir.query_exists ()) { 317 bfp_dir = get_child (dir, @"$(font.get_full_name ())_$(i)"); 318 i++; 319 } 320 321 if (!dir.query_exists ()) { 322 DirUtils.create ((!) dir.get_path (), 0755); 323 } 324 325 root_directory = (!) bfp_dir.get_path (); 326 DirUtils.create (root_directory, 0755); 327 } 328 329 private void find_all_parts (string bfp_file) throws GLib.Error { 330 File start = File.new_for_path (bfp_file); 331 FileInfo info; 332 File root; 333 334 info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 335 if (info.get_file_type () != FileType.DIRECTORY) { 336 start = (!) start.get_parent (); 337 } 338 339 root = find_root ((!)start.get_path ()); 340 root_directory = (!)root.get_path (); 341 342 find_parts (root_directory); 343 } 344 345 private void find_parts (string directory) throws GLib.Error { 346 File start = File.new_for_path (directory); 347 File found; 348 FileInfo info; 349 FileInfo? fi; 350 FileEnumerator e; 351 string name; 352 353 354 info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 355 if (info.get_file_type () != FileType.DIRECTORY) { 356 warning (@"$directory is not a directory."); 357 throw new FileError.NOTDIR ("Not a directory."); 358 } 359 360 e = start.enumerate_children (FILE_ATTRIBUTES, 0); 361 while ((fi = e.next_file ()) != null) { 362 info = (!) fi; 363 name = info.get_name (); 364 if (info.get_file_type () == FileType.DIRECTORY) { 365 find_parts ((!) ((!) get_child (start, name)).get_path ()); 366 } else if (name.has_suffix (".bfp")) { 367 found = get_child (start, name); 368 parts.add ((!) found.get_path ()); 369 } 370 } 371 } 372 373 private File find_root (string directory) throws GLib.Error { 374 File start = File.new_for_path (directory); 375 FileInfo info; 376 FileInfo? fi; 377 FileEnumerator e; 378 379 info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 380 if (info.get_file_type () != FileType.DIRECTORY) { 381 warning ("Not a directory."); 382 throw new FileError.NOTDIR ("Not a directory."); 383 } 384 385 e = start.enumerate_children (FILE_ATTRIBUTES, 0); 386 while ((fi = e.next_file ()) != null) { 387 info = (!) fi; 388 if (info.get_name () == "description.bfp") { 389 return start; 390 } 391 } 392 393 if (start.get_parent () == null) { 394 warning ("description.bfp not found"); 395 throw new FileError.FAILED ("description.bfp not found"); 396 } 397 398 return find_root ((!)((!) start.get_parent ()).get_path ()); 399 } 400 401 private File new_subdirectory (File d, string subdir) throws GLib.Error { 402 FileInfo info; 403 File dir; 404 405 dir = d; 406 dir = get_child (dir, subdir); 407 408 if (!dir.query_exists ()) { 409 DirUtils.create ((!) dir.get_path (), 0755); 410 } else { 411 info = dir.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 412 if (info.get_file_type () != FileType.DIRECTORY) { 413 throw new FileError.FAILED (@"Can't save font, $subdir is not a directory."); 414 } 415 } 416 return dir; 417 } 418 419 private File get_destination_file (string name, string subdir1 = "", string subdir2 = "") throws GLib.Error { 420 File file; 421 File dir; 422 423 dir = File.new_for_path (root_directory); 424 425 if (subdir1 != "") { 426 dir = new_subdirectory (dir, subdir1); 427 } 428 429 if (subdir2 != "") { 430 dir = new_subdirectory (dir, subdir2); 431 } 432 433 file = get_child (dir, name); 434 435 if (file.query_file_type (0) == FileType.DIRECTORY) { 436 throw new FileError.FAILED (@"Can't save font, $name is a directory."); 437 } 438 439 return file; 440 } 441 442 private DataOutputStream create_file (string name, string subdir1 = "", string subdir2 = "") throws GLib.Error { 443 DataOutputStream os; 444 File file; 445 string git_path; 446 447 file = get_destination_file (name, subdir1, subdir2); 448 449 if (file.query_exists ()) { 450 file.delete (); 451 } 452 453 os = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION)); 454 455 if (subdir2 != "") { 456 git_path = subdir1 + "/" + subdir2 + "/" + name; 457 } else if (subdir1 != "") { 458 git_path = subdir1 + "/" + name; 459 } else { 460 git_path = name; 461 } 462 463 // FIXME: git_index.add_path (git_path); 464 465 return os; 466 } 467 } 468 469 } 470