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