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
Circle boundaries heads/master
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 (string deleted_glyph in font.deleted_glyphs) { 96 file_name = deleted_glyph; 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 // selected glyph 161 foreach (GlyphMaster master in gc.glyph_masters) { 162 os = create_file (@"selected_$(selected_file_name)_$(master.get_id ()).bfp", "glyphs", dir_name); 163 bf.write_root_tag (os); 164 bf.write_glyph_collection_start (gc, master, os); 165 bf.write_selected ((!) master, os); 166 bf.write_glyph_collection_end (os); 167 bf.write_closing_root_tag (os); 168 os.close (); 169 } 170 171 172 foreach (GlyphMaster master in gc.glyph_masters) { 173 foreach (Glyph g in master.glyphs) { 174 try { 175 write_glyph (bf, gc, master, g); 176 write_glyph_background_image (bf, gc, g); 177 } catch (GLib.Error e) { 178 warning (e.message); 179 } 180 } 181 } 182 } catch (GLib.Error e) { 183 warning (@"Can not save bfp files to $root_directory\n"); 184 warning (@"$(e.message) \n"); 185 error = true; 186 } 187 }); 188 189 os = create_file ("kerning.bfp"); 190 bf.write_root_tag (os); 191 bf.write_kerning (os); 192 bf.write_closing_root_tag (os); 193 os.close (); 194 195 os = create_file ("images.bfp"); 196 bf.write_root_tag (os); 197 bf.write_images (os); 198 bf.write_closing_root_tag (os); 199 os.close (); 200 201 } catch (GLib.Error e) { 202 warning (@"Failed to save bfp files to $root_directory\n"); 203 warning (@"$(e.message) \n"); 204 error = true; 205 } 206 207 return !error; 208 } 209 210 void copy_backgrounds (string folder) throws GLib.Error { 211 FileInfo info; 212 FileInfo? fi; 213 FileEnumerator e; 214 string name; 215 File image_dir; 216 BackgroundImage bg; 217 File found; 218 File parts; 219 File dest; 220 221 image_dir = File.new_for_path (folder); 222 223 if (image_dir.query_exists ()) { 224 info = image_dir.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 225 if (info.get_file_type () != FileType.DIRECTORY) { 226 warning (@"$((!) image_dir.get_path ()) is not a directory."); 227 throw new FileError.NOTDIR ("Not a directory."); 228 } 229 230 e = image_dir.enumerate_children (FILE_ATTRIBUTES, 0); 231 while ((fi = e.next_file ()) != null) { 232 info = (!) fi; 233 name = info.get_name (); 234 235 if (info.get_file_type () == FileType.DIRECTORY) { 236 found = get_child (image_dir, name); 237 copy_backgrounds ((!) found.get_path ()); 238 } 239 240 if (name.has_suffix (".png")) { 241 found = get_child (image_dir, name); 242 parts = get_child (font.get_backgrounds_folder (), "parts"); 243 dest = get_child (parts, name); 244 bg = new BackgroundImage ((!) found.get_path ()); 245 bg.create_background_folders (font); 246 bg.copy_if_new (dest); 247 } 248 } 249 } 250 } 251 252 static string get_first_number_in_unicode (Glyph g) { 253 string s = Font.to_hex (g.unichar_code); 254 s = s.replace ("U+", ""); 255 return s; 256 } 257 258 public static string get_glyph_base_file_name (Glyph g, GlyphMaster master) { 259 string s = get_first_number_in_unicode (g); 260 s = @"U+$(s)_$(g.version_id)_$(master.get_id ())"; 261 return s; 262 } 263 264 public string get_subdir_name (string file_name) { 265 string d = file_name; 266 267 if (file_name.has_prefix ("U+")) { 268 d = file_name.replace ("U+", ""); 269 } 270 271 return (!) d.get_char ().to_string (); 272 } 273 274 void write_glyph (BirdFontFile bf, GlyphCollection gc, GlyphMaster master, Glyph g) throws GLib.Error { 275 string file_name; 276 string dir_name; 277 DataOutputStream os; 278 279 file_name = get_glyph_base_file_name (g, master); 280 dir_name = get_subdir_name (file_name); 281 282 os = create_file (@"$(file_name).bfp", "glyphs", dir_name); 283 bf.write_root_tag (os); 284 bf.write_glyph_collection_start (gc, master, os); 285 bf.write_glyph (g, os); 286 bf.write_glyph_collection_end (os); 287 bf.write_closing_root_tag (os); 288 os.close (); 289 } 290 291 void write_glyph_background_image (BirdFontFile bf, GlyphCollection gc, Glyph g) throws GLib.Error { 292 string file_name; 293 string dir_name; 294 BackgroundImage bg; 295 File file; 296 297 if (g.get_background_image () != null) { 298 bg = (!) g.get_background_image (); 299 300 if (bg.is_valid ()) { 301 file_name = @"$(bg.get_sha1 ()).png"; 302 dir_name = get_subdir_name (file_name); 303 file = get_destination_file (file_name, "images", dir_name); 304 bg.copy_if_new (file); 305 306 // FIXME: GIT ADD 307 } 308 } 309 } 310 311 public void create_directory (string directory) throws GLib.Error { 312 File dir = File.new_for_path (directory); 313 File bfp_dir; 314 int i = 2; 315 316 if (directory.has_suffix (font.get_full_name ())) { 317 bfp_dir = dir; 318 } else { 319 bfp_dir = get_child (dir, font.get_full_name ()); 320 } 321 322 while (bfp_dir.query_exists ()) { 323 bfp_dir = get_child (dir, @"$(font.get_full_name ())_$(i)"); 324 i++; 325 } 326 327 if (!dir.query_exists ()) { 328 DirUtils.create ((!) dir.get_path (), 0755); 329 } 330 331 root_directory = (!) bfp_dir.get_path (); 332 DirUtils.create (root_directory, 0755); 333 } 334 335 private void find_all_parts (string bfp_file) throws GLib.Error { 336 File start = File.new_for_path (bfp_file); 337 FileInfo info; 338 File root; 339 340 info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 341 if (info.get_file_type () != FileType.DIRECTORY) { 342 start = (!) start.get_parent (); 343 } 344 345 root = find_root ((!)start.get_path ()); 346 root_directory = (!)root.get_path (); 347 348 find_parts (root_directory); 349 } 350 351 private void find_parts (string directory) throws GLib.Error { 352 File start = File.new_for_path (directory); 353 File found; 354 FileInfo info; 355 FileInfo? fi; 356 FileEnumerator e; 357 string name; 358 359 360 info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 361 if (info.get_file_type () != FileType.DIRECTORY) { 362 warning (@"$directory is not a directory."); 363 throw new FileError.NOTDIR ("Not a directory."); 364 } 365 366 e = start.enumerate_children (FILE_ATTRIBUTES, 0); 367 while ((fi = e.next_file ()) != null) { 368 info = (!) fi; 369 name = info.get_name (); 370 if (info.get_file_type () == FileType.DIRECTORY) { 371 find_parts ((!) ((!) get_child (start, name)).get_path ()); 372 } else if (name.has_suffix (".bfp")) { 373 found = get_child (start, name); 374 parts.add ((!) found.get_path ()); 375 } 376 } 377 } 378 379 private File find_root (string directory) throws GLib.Error { 380 File start = File.new_for_path (directory); 381 FileInfo info; 382 FileInfo? fi; 383 FileEnumerator e; 384 385 info = start.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 386 if (info.get_file_type () != FileType.DIRECTORY) { 387 warning ("Not a directory."); 388 throw new FileError.NOTDIR ("Not a directory."); 389 } 390 391 e = start.enumerate_children (FILE_ATTRIBUTES, 0); 392 while ((fi = e.next_file ()) != null) { 393 info = (!) fi; 394 if (info.get_name () == "description.bfp") { 395 return start; 396 } 397 } 398 399 if (start.get_parent () == null) { 400 warning ("description.bfp not found"); 401 throw new FileError.FAILED ("description.bfp not found"); 402 } 403 404 return find_root ((!)((!) start.get_parent ()).get_path ()); 405 } 406 407 private File new_subdirectory (File d, string subdir) throws GLib.Error { 408 FileInfo info; 409 File dir; 410 411 dir = d; 412 dir = get_child (dir, subdir); 413 414 if (!dir.query_exists ()) { 415 DirUtils.create ((!) dir.get_path (), 0755); 416 } else { 417 info = dir.query_info (FILE_ATTRIBUTES, FileQueryInfoFlags.NONE); 418 if (info.get_file_type () != FileType.DIRECTORY) { 419 throw new FileError.FAILED (@"Can't save font, $subdir is not a directory."); 420 } 421 } 422 return dir; 423 } 424 425 private File get_destination_file (string name, string subdir1 = "", string subdir2 = "") throws GLib.Error { 426 File file; 427 File dir; 428 429 dir = File.new_for_path (root_directory); 430 431 if (subdir1 != "") { 432 dir = new_subdirectory (dir, subdir1); 433 } 434 435 if (subdir2 != "") { 436 dir = new_subdirectory (dir, subdir2); 437 } 438 439 file = get_child (dir, name); 440 441 if (file.query_file_type (0) == FileType.DIRECTORY) { 442 throw new FileError.FAILED (@"Can't save font, $name is a directory."); 443 } 444 445 return file; 446 } 447 448 private DataOutputStream create_file (string name, string subdir1 = "", string subdir2 = "") throws GLib.Error { 449 DataOutputStream os; 450 File file; 451 string git_path; 452 453 file = get_destination_file (name, subdir1, subdir2); 454 455 if (file.query_exists ()) { 456 file.delete (); 457 } 458 459 os = new DataOutputStream (file.create (FileCreateFlags.REPLACE_DESTINATION)); 460 461 if (subdir2 != "") { 462 git_path = subdir1 + "/" + subdir2 + "/" + name; 463 } else if (subdir1 != "") { 464 git_path = subdir1 + "/" + name; 465 } else { 466 git_path = name; 467 } 468 469 // FIXME: git_index.add_path (git_path); 470 471 return os; 472 } 473 } 474 475 } 476