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