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.
Add contextual ligatures to the GUI and fix directory privileges
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 // FIXME: using Git; 16 17 namespace BirdFont { 18 19 /** BirdFontPart is a class for parsing .bfp files. The file format is 20 * identical to .bf but the font is split in many parts. Each part 21 * contains a few elements and all parent nodes from the root node and 22 * downwards. The .bfp files can be parsed in any order. The root directory 23 * of a .bfp tree must have a file with the name "description.bfp", this file 24 * tells the parser that bfp files in parent directories should be excluded. 25 */ 26 public class BirdFontPart : GLib.Object{ 27 Font font; 28 Gee.ArrayList<string> parts; 29 string root_directory; 30 // Index git_index; 31 // Repository? git_repository; 32 33 static string FILE_ATTRIBUTES = "standard::*"; 34 35 static bool running_git = false; 36 37 public BirdFontPart (Font font) { 38 this.font = font; 39 parts = new Gee.ArrayList<string> (); 40 root_directory = ""; 41 } 42 43 public static void git_init () { 44 if (!running_git) { 45 // FIXME: Git.Threads.init (); 46 } 47 48 running_git = true; 49 } 50 51 public static void git_shutdown () { 52 if (running_git) { 53 // FIXME: Git.Threads.shutdown (); 54 } 55 56 running_git = false; 57 } 58 59 public bool load (string bfp_file) { 60 BirdFontFile bf = new BirdFontFile (font); 61 File bfp_dir; 62 File image_dir; 63 64 try { 65 find_all_parts (bfp_file); 66 font.set_bfp (true); 67 68 font.background_images.clear (); 69 70 bfp_dir = File.new_for_path (root_directory); 71 image_dir = bfp_dir.get_child ("images"); 72 copy_backgrounds ((!) image_dir.get_path ()); 73 74 foreach (string fn in parts) { 75 bf.load_part (fn); 76 } 77 } catch (GLib.Error e) { 78 warning (e.message); 79 return false; 80 } 81 82 return true; 83 } 84 85 public string get_path () { 86 string path = ""; 87 88 try { 89 path = (!) get_destination_file (@"$(font.full_name).bfp").get_path (); 90 } catch (GLib.Error e) { 91 warning (e.message); 92 } 93 94 return path; 95 } 96 97 bool create_git_repository () { 98 // FIXME: 99 /* 100 File root = File.new_for_path (root_directory); 101 File git = root.get_child (".git"); 102 Repository repo; 103 104 if (!git.query_exists ()) { 105 if (Repository.init (out repo, root_directory, false) != Git.Error.OK) { 106 warning ("Can not create git repository."); 107 return false; 108 } 109 } 110 111 if (Repository.open (out git_repository, root_directory) != Git.Error.OK) { 112 warning ("Can not open git repository."); 113 return false; 114 } 115 116 // FIXME: ((!) git_repository).get_index (out git_index); 117 */ 118 return true; 119 } 120 121 public bool save () { 122 DataOutputStream os; 123 BirdFontFile bf = new BirdFontFile (font); 124 bool error = false; 125 string file_name; 126 string glyph_dir_name; 127 File glyph_file; 128 // FIXME: Signature? sig; 129 // FIXME: object_id id; 130 // FIXME: Git.Tree tree; 131 int nparents; 132 // FIXME: Git.Commit[] parents; 133 134 if (root_directory == "") { 135 warning ("No directory is created for this birdfont part."); 136 return false; 137 } 138 139 git_init (); 140 141 if (!create_git_repository ()) { 142 return false; 143 } 144 145 // FIXME: return_val_if_fail (!is_null (git_repository), false); 146 /* 147 if (git_index.write_tree (out id) != Git.Error.OK) { 148 warning ("write_tree failed"); 149 error = true; 150 } 151 152 Signature.create_now (out sig, "Test Namn", "test@test.com"); 153 154 if (((!) git_repository).lookup_tree(out tree, id) != Git.Error.OK) { 155 warning ("Can't find tree."); 156 error = true; 157 } 158 159 try { 160 // remove deleted glyphs 161 foreach (Glyph g in font.deleted_glyphs) { 162 file_name = get_glyph_base_file_name (g) + ".bfp"; 163 glyph_dir_name = get_subdir_name (file_name); 164 glyph_file = get_destination_file (file_name, "glyphs", glyph_dir_name); 165 166 if (glyph_file.query_exists ()) { 167 glyph_file.delete (); 168 } 169 170 print (@"$((!)glyph_file.get_path ())\n"); 171 } 172 173 os = create_file (@"$(font.full_name).bfp"); 174 bf.write_root_tag (os); 175 bf.write_closing_root_tag (os); 176 os.close (); 177 178 os = create_file ("description.bfp"); 179 bf.write_root_tag (os); 180 bf.write_description (os); 181 bf.write_closing_root_tag (os); 182 os.close (); 183 184 os = create_file ("lines.bfp"); 185 bf.write_root_tag (os); 186 bf.write_lines (os); 187 bf.write_closing_root_tag (os); 188 os.close (); 189 190 os = create_file ("settings.bfp"); 191 bf.write_root_tag (os); 192 bf.write_settings (os); 193 bf.write_closing_root_tag (os); 194 os.close (); 195 196 os = create_file ("spacing.bfp"); 197 bf.write_root_tag (os); 198 bf.write_spacing_classes (os); 199 bf.write_closing_root_tag (os); 200 os.close (); 201 202 os = create_file ("ligatures.bfp"); 203 bf.write_root_tag (os); 204 bf.write_ligatures (os); 205 bf.write_closing_root_tag (os); 206 os.close (); 207 208 font.glyph_cache.for_each ((gc) => { 209 try { 210 string selected_file_name; 211 string dir_name; 212 213 if (is_null (gc)) { 214 warning ("No glyph collection"); 215 } 216 217 selected_file_name = get_first_number_in_unicode (((!)gc).get_current ()); 218 dir_name = get_subdir_name (selected_file_name); 219 220 os = create_file (@"selected_$(selected_file_name).bfp", "glyphs", dir_name); 221 bf.write_root_tag (os); 222 bf.write_glyph_collection_start (gc, os); 223 bf.write_selected ((!) gc, os); 224 bf.write_glyph_collection_end (os); 225 bf.write_closing_root_tag (os); 226 os.close (); 227 228 foreach (Glyph g in gc.get_version_list ().glyphs) { 229 try { 230 write_glyph (bf, gc, g); 231 write_glyph_background_image (bf, gc, g); 232 } catch (GLib.Error e) { 233 warning (e.message); 234 } 235 } 236 } catch (GLib.Error e) { 237 warning (@"Can not save bfp files to $root_directory\n"); 238 warning (@"$(e.message) \n"); 239 error = true; 240 } 241 }); 242 243 os = create_file ("kerning.bfp"); 244 bf.write_root_tag (os); 245 bf.write_kerning (os); 246 bf.write_closing_root_tag (os); 247 os.close (); 248 249 } catch (GLib.Error e) { 250 warning (@"Failed to save bfp files to $root_directory\n"); 251 warning (@"$(e.message) \n"); 252 error = true; 253 } 254 255 if (((!) git_repository).is_empty) { 256 nparents = 0; 257 } else { 258 nparents = 0; 259 } 260 261 parents = new Git.Commit[0]; 262 if (((!) git_repository).create_commit (id, "HEAD", (!) sig, (!) sig, 263 "utf-8", "Improved typeface", tree, parents) != Git.Error.OK) { 264 warning ("Can't commit"); 265 error = true; 266 } 267 268 git_shutdown (); 269 */ 270 return !error; 271 } 272 273 void copy_backgrounds (string folder) throws GLib.Error { 274 FileInfo info; 275 FileInfo? fi; 276 FileEnumerator e; 277 string name; 278 File image_dir; 279 BackgroundImage bg; 280 File found; 281 File dest; 282 283 image_dir = File.new_for_path (folder); 284 285 if (image_dir.query_exists ()) { 286 info = image_dir.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 287 if (info.get_file_type () != FileType.DIRECTORY) { 288 warning (@"$((!) image_dir.get_path ()) is not a directory."); 289 throw new FileError.NOTDIR ("Not a directory."); 290 } 291 292 e = image_dir.enumerate_children (FILE_ATTRIBUTES, 0); 293 while ((fi = e.next_file ()) != null) { 294 info = (!) fi; 295 name = info.get_name (); 296 297 if (info.get_file_type () == FileType.DIRECTORY) { 298 found = image_dir.get_child (name); 299 copy_backgrounds ((!) found.get_path ()); 300 } 301 302 if (name.has_suffix (".png")) { 303 found = image_dir.get_child (name); 304 dest = font.get_backgrounds_folder ().get_child ("parts").get_child (name); 305 bg = new BackgroundImage ((!) found.get_path ()); 306 bg.create_background_folders (font); 307 bg.copy_if_new (dest); 308 } 309 } 310 } 311 } 312 313 string get_first_number_in_unicode (Glyph g) throws GLib.Error { 314 string s = Font.to_hex (g.unichar_code); 315 s = s.replace ("U+", ""); 316 return s; 317 } 318 319 string get_glyph_base_file_name (Glyph g) throws GLib.Error { 320 string s = get_first_number_in_unicode (g); 321 s = @"U+$(s)_$(g.version_id)"; 322 return s; 323 } 324 325 public string get_subdir_name (string file_name) { 326 string d = file_name; 327 328 if (file_name.has_prefix ("U+")) { 329 d = file_name.replace ("U+", ""); 330 } 331 332 return (!) d.get_char ().to_string (); 333 } 334 335 void write_glyph (BirdFontFile bf, GlyphCollection gc, Glyph g) throws GLib.Error { 336 string file_name; 337 string dir_name; 338 DataOutputStream os; 339 340 file_name = get_glyph_base_file_name (g); 341 dir_name = get_subdir_name (file_name); 342 343 os = create_file (@"$(file_name).bfp", "glyphs", dir_name); 344 bf.write_root_tag (os); 345 bf.write_glyph_collection_start (gc, os); 346 bf.write_glyph (g, gc, os); 347 bf.write_glyph_collection_end (os); 348 bf.write_closing_root_tag (os); 349 os.close (); 350 } 351 352 void write_glyph_background_image (BirdFontFile bf, GlyphCollection gc, Glyph g) throws GLib.Error { 353 string file_name; 354 string dir_name; 355 BackgroundImage bg; 356 File file; 357 358 if (g.get_background_image () != null) { 359 bg = (!) g.get_background_image (); 360 361 if (bg.is_valid ()) { 362 file_name = @"$(bg.get_sha1 ()).png"; 363 dir_name = get_subdir_name (file_name); 364 file = get_destination_file (file_name, "images", dir_name); 365 bg.copy_if_new (file); 366 367 // FIXME: GIT ADD 368 } 369 } 370 } 371 372 public void create_directory (string directory) throws GLib.Error { 373 File dir = File.new_for_path (directory); 374 File bfp_dir; 375 int i = 2; 376 377 if (directory.has_suffix (font.get_full_name ())) { 378 bfp_dir = dir; 379 } else { 380 bfp_dir = dir.get_child (font.get_full_name ()); 381 } 382 383 while (bfp_dir.query_exists ()) { 384 bfp_dir = dir.get_child (@"$(font.get_full_name ())_$(i)"); 385 i++; 386 } 387 388 if (!dir.query_exists ()) { 389 DirUtils.create ((!) dir.get_path (), 0xFFFFFF); 390 } 391 392 root_directory = (!) bfp_dir.get_path (); 393 DirUtils.create (root_directory, 0xFFFFFF); 394 } 395 396 private void find_all_parts (string bfp_file) throws GLib.Error { 397 File start = File.new_for_path (bfp_file); 398 FileInfo info; 399 File root; 400 401 info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 402 if (info.get_file_type () != FileType.DIRECTORY) { 403 start = (!) start.get_parent (); 404 } 405 406 root = find_root ((!)start.get_path ()); 407 root_directory = (!)root.get_path (); 408 409 find_parts (root_directory); 410 } 411 412 private void find_parts (string directory) throws GLib.Error { 413 File start = File.new_for_path (directory); 414 File found; 415 FileInfo info; 416 FileInfo? fi; 417 FileEnumerator e; 418 string name; 419 420 421 info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 422 if (info.get_file_type () != FileType.DIRECTORY) { 423 warning (@"$directory is not a directory."); 424 throw new FileError.NOTDIR ("Not a directory."); 425 } 426 427 e = start.enumerate_children (FILE_ATTRIBUTES, 0); 428 while ((fi = e.next_file ()) != null) { 429 info = (!) fi; 430 name = info.get_name (); 431 if (info.get_file_type () == FileType.DIRECTORY) { 432 find_parts ((!) ((!) start.get_child (name)).get_path ()); 433 } else if (name.has_suffix (".bfp")) { 434 found = start.get_child (name); 435 parts.add ((!) found.get_path ()); 436 } 437 } 438 } 439 440 private File find_root (string directory) throws GLib.Error { 441 File start = File.new_for_path (directory); 442 FileInfo info; 443 FileInfo? fi; 444 FileEnumerator e; 445 446 info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 447 if (info.get_file_type () != FileType.DIRECTORY) { 448 warning ("Not a directory."); 449 throw new FileError.NOTDIR ("Not a directory."); 450 } 451 452 e = start.enumerate_children (FILE_ATTRIBUTES, 0); 453 while ((fi = e.next_file ()) != null) { 454 info = (!) fi; 455 if (info.get_name () == "description.bfp") { 456 return start; 457 } 458 } 459 460 if (start.get_parent () == null) { 461 warning ("description.bfp not found"); 462 throw new FileError.FAILED ("description.bfp not found"); 463 } 464 465 return find_root ((!)((!) start.get_parent ()).get_path ()); 466 } 467 468 private File new_subdirectory (File d, string subdir) throws GLib.Error { 469 FileInfo info; 470 File dir; 471 472 dir = d; 473 dir = dir.get_child (subdir); 474 475 if (!dir.query_exists ()) { 476 DirUtils.create ((!) dir.get_path (), 0xFFFFFF); 477 } else { 478 info = dir.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 479 if (info.get_file_type () != FileType.DIRECTORY) { 480 throw new FileError.FAILED (@"Can't save font, $subdir is not a directory."); 481 } 482 } 483 return dir; 484 } 485 486 private File get_destination_file (string name, string subdir1 = "", string subdir2 = "") throws GLib.Error { 487 File file; 488 File dir; 489 490 dir = File.new_for_path (root_directory); 491 492 if (subdir1 != "") { 493 dir = new_subdirectory (dir, subdir1); 494 } 495 496 if (subdir2 != "") { 497 dir = new_subdirectory (dir, subdir2); 498 } 499 500 file = dir.get_child (name); 501 502 if (file.query_file_type (0) == FileType.DIRECTORY) { 503 throw new FileError.FAILED (@"Can't save font, $name is a directory."); 504 } 505 506 return file; 507 } 508 509 private DataOutputStream create_file (string name, string subdir1 = "", string subdir2 = "") throws GLib.Error { 510 DataOutputStream os; 511 File file; 512 string git_path; 513 514 file = get_destination_file (name, subdir1, subdir2); 515 516 if (file.query_exists ()) { 517 file.delete (); 518 } 519 520 os = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION)); 521 522 if (subdir2 != "") { 523 git_path = subdir1 + "/" + subdir2 + "/" + name; 524 } else if (subdir1 != "") { 525 git_path = subdir1 + "/" + name; 526 } else { 527 git_path = name; 528 } 529 530 // FIXME: git_index.add_path (git_path); 531 532 return os; 533 } 534 } 535 536 } 537