.
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