The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

BirdFont.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/BirdFont.vala.
Debug info for file utility
1 /* 2 Copyright (C) 2012 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 using BirdFont; 15 16 public const string GETTEXT_PACKAGE = "birdfont"; 17 18 namespace BirdFont { 19 20 public static string? settings_directory = null; 21 22 static void print_import_help (string[] arg) { 23 stdout.printf (t_("Usage:")); 24 stdout.printf (arg[0]); 25 stdout.printf (" " + t_("BF-FILE") + " " + t_("SVG-FILES ...") +"\n"); 26 stdout.printf ("\n"); 27 } 28 29 public static int run_import (string[] arg) { 30 string bf_file = ""; 31 Gee.ArrayList<string> svg_files = new Gee.ArrayList<string> (); 32 File bf; 33 File svg; 34 Font font; 35 bool imported; 36 37 Preferences.load (); 38 BirdFont.args = new Argument (""); 39 BirdFont.current_font = new Font (); 40 BirdFont.current_glyph_collection = new GlyphCollection.with_glyph ('\0', ""); 41 MainWindow.init (); 42 43 if (arg.length < 3) { 44 print_import_help (arg); 45 return -1; 46 } 47 48 bf_file = build_absoulute_path (arg[1]); 49 50 for (int i = 2; i < arg.length; i++) { 51 svg_files.add (arg[i]); 52 } 53 54 bf = File.new_for_path (bf_file); 55 foreach (string f in svg_files) { 56 svg = File.new_for_path (f); 57 58 if (!svg.query_exists ()) { 59 stdout.printf (@"$f " + t_("does not exist.") + "\n"); 60 return -1; 61 } 62 } 63 64 font = BirdFont.get_current_font (); 65 66 if (!bf.query_exists ()) { 67 stdout.printf (@"$bf_file " + t_("does not exist.") + " "); 68 stdout.printf (t_("A new font will be created.") + "\n"); 69 font.set_file (bf_file); 70 } else { 71 font.set_file (bf_file); 72 if (!font.load ()) { 73 warning (@"Failed to load font $bf_file.\n"); 74 75 if (!bf_file.has_suffix (".bf")) { 76 warning (@"Is it a .bf file?\n"); 77 } 78 79 return -1; 80 } 81 } 82 83 font.save_backup (); 84 85 foreach (string f in svg_files) { 86 svg = File.new_for_path (f); 87 imported = import_svg_file (font, svg); 88 89 if (!imported) { 90 stdout.printf (t_("Failed to import") + " " + f + "\n"); 91 stdout.printf (t_("Aborting") + "\n"); 92 return -1; 93 } 94 } 95 96 font.save_bf (); 97 98 return 0; 99 } 100 101 internal static string build_absoulute_path (string file_name) { 102 File f = File.new_for_path (file_name); 103 return (!) f.get_path (); 104 } 105 106 static bool import_svg_file (Font font, File svg_file) { 107 string file_name = (!) svg_file.get_basename (); 108 string glyph_name; 109 StringBuilder n; 110 Glyph glyph; 111 GlyphCollection? gc = null; 112 GlyphCollection glyph_collection; 113 unichar character; 114 GlyphCanvas canvas; 115 116 glyph_name = file_name.replace (".svg", ""); 117 glyph_name = glyph_name.replace (".SVG", ""); 118 119 if (glyph_name.char_count () > 1) { 120 if (glyph_name.has_prefix ("U+")) { 121 n = new StringBuilder (); 122 n.append_unichar (Font.to_unichar (glyph_name)); 123 glyph_name = n.str; 124 gc = font.get_glyph_collection (glyph_name); 125 } else { 126 gc = font.get_glyph_collection_by_name (glyph_name); 127 128 if (gc == null) { 129 stdout.printf (file_name + " " + t_("is not the name of a glyph or a Unicode value.") + "\n"); 130 stdout.printf (t_("Unicode values must start with U+.") + "\n"); 131 return false; 132 } 133 } 134 } else { 135 gc = font.get_glyph_collection (glyph_name); 136 } 137 138 if (gc != null) { 139 glyph_collection = (!) gc; 140 character = glyph_collection.get_unicode_character (); 141 glyph = new Glyph (glyph_collection.get_name (), character); 142 glyph.version_id = glyph_collection.get_last_id () + 1; 143 glyph_collection.insert_glyph (glyph, true); 144 } else { 145 return_val_if_fail (glyph_name.char_count () == 1, false); 146 character = glyph_name.get_char (0); 147 glyph_collection = new GlyphCollection (character, glyph_name); 148 glyph = new Glyph (glyph_name, character); 149 glyph_collection.insert_glyph (glyph, true); 150 font.add_glyph_collection (glyph_collection); 151 } 152 153 canvas = MainWindow.get_glyph_canvas (); 154 canvas.set_current_glyph_collection (glyph_collection); 155 156 stdout.printf (t_("Adding")); 157 stdout.printf (" "); 158 stdout.printf ((!) svg_file.get_basename ()); 159 stdout.printf (" "); 160 stdout.printf (t_("to")); 161 stdout.printf (" "); 162 stdout.printf (t_("Glyph")); 163 stdout.printf (": "); 164 stdout.printf (glyph.get_name ()); 165 stdout.printf (" "); 166 stdout.printf (t_("Version")); 167 stdout.printf (": "); 168 stdout.printf (@"$(glyph.version_id)"); 169 stdout.printf ("\n"); 170 171 SvgParser.import_svg ((!) svg_file.get_path ()); 172 173 return true; 174 } 175 176 static void print_export_help (string[] arg) { 177 stdout.printf (t_("Usage:")); 178 stdout.printf (arg[0]); 179 stdout.printf (" [" + t_("OPTION") + "...] " + t_("FILE") +"\n"); 180 stdout.printf ("-h, --help " + t_("print this message") + "\n"); 181 stdout.printf ("-o, --output [DIRECTORY] " + t_("write files to this directory") + "\n"); 182 stdout.printf ("-s, --svg " + t_("write svg file") + "\n"); 183 stdout.printf ("-t, --ttf " + t_("write ttf and eot file") + "\n"); 184 stdout.printf ("\n"); 185 } 186 187 public static string get_version () { 188 return VERSION; 189 } 190 191 public static string get_build_stamp () { 192 return BUILD_TIMESTAMP; 193 } 194 195 public static int run_export (string[] arg) { 196 string output_directory = "."; 197 string file_name = ""; 198 bool specific_formats = false; 199 bool write_ttf = false; 200 bool write_svg = false; 201 File directory; 202 Font font; 203 MainWindow main_window; 204 205 stdout.printf ("birdfont-export version %s\n", VERSION); 206 stdout.printf ("built on %s\n", BUILD_TIMESTAMP); 207 208 if (arg.length < 2) { 209 print_export_help (arg); 210 return -1; 211 } 212 213 BirdFont.current_font = BirdFont.new_font (); 214 BirdFont.current_glyph_collection = new GlyphCollection.with_glyph ( '\0', "null"); 215 main_window = new MainWindow (); 216 217 // FIXME: create a option for this and add structure the log messages 218 219 if (BirdFont.logging) { 220 init_logfile (); 221 } 222 223 for (int i = 1; i < arg.length; i++) { 224 225 if (arg[i] == "-f" || arg[i] == "--fatal-warnings") { 226 BirdFont.fatal_wanings = true; 227 return 0; 228 } 229 230 if (arg[i] == "-h" || arg[i] == "--help") { 231 print_export_help (arg); 232 return 0; 233 } 234 235 if ((arg[i] == "-o" || arg[i] == "--output") && i + 1 < arg.length) { 236 output_directory = arg[i + 1]; 237 i++; 238 continue; 239 } 240 241 if (arg[i] == "-s" || arg[i] == "--svg") { 242 write_svg = true; 243 specific_formats = true; 244 continue; 245 } 246 247 if (arg[i] == "-t" || arg[i] == "--ttf") { 248 write_ttf = true; 249 specific_formats = true; 250 continue; 251 } 252 253 if (arg[i].has_prefix ("-")) { 254 print_export_help (arg); 255 return 1; 256 } 257 258 if (!arg[i].has_prefix ("-")) { 259 file_name = arg[i]; 260 261 if (i != arg.length - 1) { 262 print_export_help (arg); 263 return 1; 264 } 265 266 break; 267 } 268 } 269 270 if (BirdFont.fatal_wanings) { 271 LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING; 272 Log.set_handler (null, levels, BirdFont.fatal_warning); 273 } 274 275 Preferences.load (); 276 277 BirdFont.args = new Argument (""); 278 BirdFont.current_glyph_collection = new GlyphCollection.with_glyph ('\0', ""); 279 280 file_name = build_absoulute_path (file_name); 281 282 font = BirdFont.get_current_font (); 283 font.set_file (file_name); 284 if (!font.load ()) { 285 warning (@"Failed to load font $file_name.\n"); 286 287 if (!file_name.has_suffix (".bf")) { 288 warning (@"Is it a .bf file?\n"); 289 } 290 291 return 1; 292 } 293 294 directory = File.new_for_path (output_directory); 295 296 if (!directory.query_exists ()) { 297 stderr.printf (t_("Can't find output directory") + @"$((!)directory.get_path ())\n"); 298 return 1; 299 } 300 301 if (!specific_formats || write_svg) { 302 print (@"Writing $(ExportSettings.get_file_name (font)).svg to $output_directory\n"); 303 ExportTool.export_svg_font_path (File.new_for_path (output_directory)); 304 } 305 306 if (!specific_formats || write_ttf) { 307 print (@"Writing $(ExportSettings.get_file_name (font)).ttf to $output_directory\n"); 308 ExportTool.export_ttf_font_path (File.new_for_path (output_directory)); 309 } 310 311 return 0; 312 } 313 314 public static void set_logging (bool log) { 315 BirdFont.logging = log; 316 } 317 318 public static string wine_to_unix_path (string exec_path) { 319 bool drive_c, drive_z; 320 int i; 321 string p, q; 322 323 p = exec_path; 324 p = p.replace ("\\", "/"); 325 326 drive_c = exec_path.index_of ("C:") == 0; 327 drive_z = exec_path.index_of ("Z:") == 0; 328 329 i = p.index_of (":"); 330 331 if (i != -1) { 332 p = p.substring (i + 2); 333 } 334 335 if (drive_c) { 336 q = @"/home/$(Environment.get_user_name ())/.wine/drive_c/" + p; 337 338 if (File.new_for_path (q).query_exists ()) { 339 return q; 340 } else { 341 return p; 342 } 343 } 344 345 if (drive_z) { 346 return ("/" + p).dup (); 347 } 348 349 return exec_path.dup (); 350 } 351 352 public bool is_null (void* n) { 353 return n == null; 354 } 355 356 public bool has_flag (uint32 flag, uint32 mask) { 357 return (flag & mask) > 0; 358 } 359 360 public class BirdFont { 361 public static Argument args; 362 public static bool experimental = false; 363 public static bool show_coordinates = false; 364 public static bool fatal_wanings = false; 365 public static bool win32 = false; 366 public static bool mac = false; 367 public static bool android = false; 368 public static string exec_path = ""; 369 public static string? bundle_path = null; 370 371 public static bool logging = false; 372 public static DataOutputStream? logstream = null; 373 374 public static Font current_font; 375 public static GlyphCollection current_glyph_collection; 376 377 public static Drawing? drawing = null; 378 379 public static string? settings_subdirectory = null; 380 381 public BirdFont () { 382 set_defaul_drawing_callbacks (); 383 } 384 385 void set_defaul_drawing_callbacks () { 386 if (drawing == null) { 387 drawing = new Drawing (); 388 } 389 } 390 391 /** 392 * @param arg command line arguments 393 * @param program path 394 * @param setting subdirectory 395 */ 396 public void init (string[] arg, string? program_path, string? settings_subdir) { 397 int err_arg; 398 int i; 399 File font_file; 400 string exec_path; 401 string theme; 402 int default_theme_version; 403 string theme_version; 404 CharDatabaseParser parser; 405 CodePageBits codepage_bits; 406 407 set_settings_subdir (settings_subdir); 408 409 args = new Argument.command_line (arg); 410 Font.empty = new Font (); 411 412 #if ANDROID 413 BirdFont.logging = true; 414 415 __android_log_print (ANDROID_LOG_WARN, "BirdFont", @"libbirdfont version $VERSION"); 416 LogLevelFlags log_levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING; 417 Log.set_handler (null, log_levels, android_warning); 418 419 android = true; 420 #else 421 stdout.printf ("birdfont version %s\n", VERSION); 422 stdout.printf ("built on %s\n", BUILD_TIMESTAMP); 423 424 android = args.has_argument ("--android"); 425 426 if (!BirdFont.logging) { 427 BirdFont.logging = args.has_argument ("--log"); 428 } 429 #endif 430 431 if (BirdFont.logging) { 432 init_logfile (); 433 } 434 435 if (!args.has_argument ("--no-translation")) { 436 init_gettext (); 437 } 438 439 if (args.has_argument ("--help")) { 440 args.print_help (); 441 Process.exit (0); 442 } 443 444 err_arg = args.validate (); 445 if (err_arg != 0) { 446 stdout.printf (@"Unknown parameter $(arg [err_arg])\n\n"); 447 args.print_help (); 448 Process.exit (0); 449 } 450 451 Preferences.load (); 452 453 // always load default theme when names in theme does change 454 default_theme_version = 1; 455 theme = Preferences.get ("theme"); 456 theme_version = Preferences.get ("theme_version"); 457 458 Theme.set_default_colors (); 459 460 if (theme_version == "" || int.parse (theme_version) < default_theme_version) { 461 462 Theme.load_theme ("dark.theme"); 463 Preferences.set ("theme", "dark.theme"); 464 } else { 465 if (theme != "") { 466 Theme.load_theme (theme); 467 } else { 468 Theme.load_theme ("dark.theme"); 469 } 470 } 471 472 Preferences.set ("theme_version", @"$default_theme_version"); 473 474 current_font = new Font (); 475 current_font.set_name (""); 476 current_font.initialised = false; 477 current_glyph_collection = new GlyphCollection.with_glyph ('\0', ""); 478 479 experimental = args.has_argument ("--test"); 480 show_coordinates = args.has_argument ("--show-coordinates") || experimental; 481 fatal_wanings = args.has_argument ("--fatal-warning"); 482 win32 = (arg[0].index_of (".exe") > -1) 483 || arg[0] == "wine" 484 || args.has_argument ("--windows"); 485 486 #if MAC 487 mac = true; 488 #else 489 mac = args.has_argument ("--mac"); 490 #endif 491 492 if (program_path == null) { 493 exec_path = ""; 494 495 if (win32) { 496 // wine hack to get "." folder in win32 environment 497 i = arg[0].last_index_of ("\\"); 498 499 if (i != -1) { 500 exec_path = arg[0]; 501 exec_path = exec_path.substring (0, i); 502 exec_path = wine_to_unix_path (exec_path); 503 } 504 } else { 505 exec_path = "./"; 506 } 507 } else { 508 exec_path = (!) program_path; 509 } 510 511 if (args.get_file () != "") { 512 font_file = File.new_for_path (args.get_file ()); 513 514 if (!font_file.query_exists ()) { 515 stderr.printf (@"The file \"$(args.get_file ())\" was not found.\n"); 516 Process.exit (-1); 517 } 518 } 519 520 if (fatal_wanings) { 521 LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING; 522 Log.set_handler (null, levels, fatal_warning); 523 } 524 525 Preferences.set_last_file (get_current_font ().get_path ()); 526 527 DefaultCharacterSet.create_default_character_sets (); 528 DefaultCharacterSet.get_characters_for_prefered_language (); 529 530 HeadTable.init (); 531 532 if (TestBirdFont.get_singleton ().test_cases_to_run != "All") { 533 TestBirdFont.run_tests (); 534 } 535 536 if (has_argument ("--parse-ucd")) { 537 parser = new CharDatabaseParser (); 538 parser.regenerate_database (); 539 } 540 541 if (has_argument ("--codepages")) { 542 codepage_bits = new CodePageBits (); 543 codepage_bits.generate_codepage_database (); 544 } 545 } 546 547 public static Argument get_arguments () { 548 return args; 549 } 550 551 public static void set_bundle_path (string path) { 552 bundle_path = path; 553 } 554 555 public static void init_gettext () { 556 // FIXME: android, this should be OK now 557 #if !ANDROID 558 string locale_directory = SearchPaths.get_locale_directory (); 559 Intl.setlocale (LocaleCategory.MESSAGES, ""); 560 Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "utf-8"); 561 Intl.bindtextdomain (GETTEXT_PACKAGE, locale_directory); 562 #endif 563 } 564 565 public static void load_font_from_command_line () { 566 string file = args.get_file (); 567 if (file != "") { 568 RecentFiles.load_font (file); 569 } 570 } 571 572 public static Font get_current_font () { 573 return current_font; 574 } 575 576 internal static void fatal_warning (string? log_domain, LogLevelFlags log_levels, string message) { 577 bool fatal = true; 578 579 if (log_domain != null) { 580 stderr.printf ("%s: \n", (!) log_domain); 581 } 582 583 stderr.printf ("\n%s\n\n", message); 584 assert (!fatal); 585 } 586 587 #if ANDROID 588 internal static void android_warning (string? log_domain, LogLevelFlags log_levels, string message) { 589 __android_log_print (ANDROID_LOG_WARN, "BirdFont", message); 590 } 591 #endif 592 593 public static Font new_font () { 594 current_font = new Font (); 595 596 if (!is_null (MainWindow.tools)) { 597 MainWindow.get_drawing_tools ().remove_all_grid_buttons (); 598 DrawingTools.add_new_grid (1, false); 599 DrawingTools.add_new_grid (2, false); 600 DrawingTools.add_new_grid (4, false); 601 } 602 603 if (!is_null (Toolbox.background_tools)) { 604 Toolbox.background_tools.remove_images (); 605 } 606 607 KerningTools.update_kerning_classes (); 608 609 return current_font; 610 } 611 612 public static void set_settings_directory (string directory) { 613 settings_subdirectory = directory; 614 } 615 616 public static File get_preview_directory () { 617 File settings = get_settings_directory (); 618 File backup = get_child(settings, "preview"); 619 620 if (!backup.query_exists ()) { 621 DirUtils.create ((!) backup.get_path (), 0755); 622 } 623 624 return backup; 625 } 626 627 public static void set_settings_subdir (string? subdir) { 628 settings_subdirectory = subdir; 629 } 630 631 internal static File get_settings_directory () { 632 string home_path; 633 File home; 634 File settings; 635 636 #if ANDROID 637 home_path = "/data/data/org.birdfont.sefyr/files"; 638 home = File.new_for_path (home_path); 639 640 if (!home.query_exists ()) { 641 printd ("Create settings directory."); 642 DirUtils.create ((!) home.get_path (),0755); 643 } 644 #else 645 home_path = (settings_directory != null) 646 ? (!) settings_directory : Environment.get_user_config_dir (); 647 648 if (is_null (home_path)) { 649 warning ("No home directory set."); 650 home_path = "."; 651 } 652 653 home = File.new_for_path (home_path); 654 #endif 655 656 if (settings_subdirectory != null) { 657 settings = get_child(home, (!) settings_subdirectory); 658 } else { 659 settings = get_child(home, "birdfont"); 660 } 661 662 if (!settings.query_exists ()) { 663 DirUtils.create ((!) settings.get_path (), 0755); 664 } 665 666 return settings; 667 } 668 669 internal static File get_backup_directory () { 670 File settings = get_settings_directory (); 671 File backup = get_child (settings, "backup"); 672 673 if (!backup.query_exists ()) { 674 DirUtils.create ((!) backup.get_path (), 0755); 675 } 676 677 return backup; 678 } 679 680 public static bool has_argument (string param) { 681 if (is_null (args)) { 682 return false; 683 } 684 685 return args.has_argument (param); 686 } 687 688 internal static string? get_argument (string param) { 689 return args.get_argument (param); 690 } 691 } 692 693 void init_logfile () { 694 DateTime t; 695 File settings; 696 string s; 697 File log; 698 699 try { 700 t = new DateTime.now_local (); 701 settings = BirdFont.get_settings_directory (); 702 s = t.to_string ().replace (":", "_"); 703 log = get_child (settings, @"birdfont_$s.log"); 704 705 BirdFont.logstream = new DataOutputStream (log.create (FileCreateFlags.REPLACE_DESTINATION)); 706 ((!)BirdFont.logstream).put_string ((!) log.get_path ()); 707 ((!)BirdFont.logstream).put_string ("\n"); 708 709 warning ("Logging to " + (!) log.get_path ()); 710 } catch (GLib.Error e) { 711 warning (e.message); 712 warning ((!) log.get_path ()); 713 } 714 715 LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING | LogLevelFlags.LEVEL_DEBUG; 716 Log.set_handler (null, levels, log_warning); 717 718 BirdFont.logging = true; 719 720 printd (@"Program version: $(VERSION)\n"); 721 printd (@"built on $(BUILD_TIMESTAMP)\n"); 722 } 723 724 internal static void log_warning (string? log_domain, LogLevelFlags log_levels, string message) { 725 if (log_domain != null) { 726 printd ((!) log_domain); 727 } 728 729 printd ("\n"); 730 printd (message); 731 printd ("\n"); 732 printd ("\n"); 733 } 734 735 /** Write debug output to logfile. */ 736 public static void printd (string s) { 737 #if ANDROID 738 __android_log_print (ANDROID_LOG_WARN, "BirdFont", s); 739 #else 740 if (unlikely (BirdFont.logging)) { 741 try { 742 if (BirdFont.logstream != null) { 743 ((!)BirdFont.logstream).put_string (s); 744 } else { 745 warning ("No logstream."); 746 } 747 748 stderr.printf (s); 749 } catch (GLib.Error e) { 750 warning (e.message); 751 } 752 } 753 #endif 754 } 755 756 /** Translate string */ 757 public string t_ (string t) { 758 #if ANDROID 759 return t; 760 #else 761 return _(t); 762 #endif 763 } 764 765 /** Translate mac menu items */ 766 public static string translate_mac (string t) { 767 string s = t_(t); 768 return s.replace ("_", ""); 769 } 770 771 /** Print a warning if Birdfont was started with the --test argument. */ 772 public static void warn_if_test (string message) { 773 if (BirdFont.has_argument ("--test")) { 774 warning (message); 775 } 776 } 777 778 /** Obtain a handle to a file in a folder. */ 779 public static File get_child (File folder, string file_name) { 780 string f; 781 string s; 782 string n; 783 784 // avoid drive letter problems on windows 785 786 f = (!) folder.get_path (); 787 788 #if LINUX 789 s = "/"; 790 #else 791 s = (BirdFont.win32) ? "\\" : "/"; 792 #endif 793 794 n = file_name; 795 if (unlikely (BirdFont.win32 && file_name.index_of ("\\") != -1)) { 796 warning (@"File name contains path separator: $file_name, Directory: $f"); 797 n = n.substring (n.last_index_of ("\\")).replace ("\\", ""); 798 } 799 800 if (!f.has_suffix (s)) { 801 f += s; 802 } 803 804 printd (@"File: Directory: $f Name: $n\n"); 805 806 return File.new_for_path (f + n); 807 } 808 809 public static void set_drawing_callbacks (Drawing callbacks) { 810 BirdFont.drawing = callbacks; 811 } 812 813 } 814