The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

Font.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/Font.vala.
Fix warning
1 /* 2 Copyright (C) 2012 2013 2014 2015 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 using Cairo; 16 17 namespace BirdFont { 18 19 public enum FontFormat { 20 BIRDFONT, 21 BIRDFONT_PART, 22 FFI, 23 SVG, 24 FREETYPE 25 } 26 27 public class Font : GLib.Object { 28 29 /** Table with glyphs sorted by their unicode value. */ 30 public GlyphTable glyph_cache; 31 32 /** Table with glyphs sorted by their name. */ 33 public GlyphTable glyph_name; 34 35 /** Table with ligatures. */ 36 public GlyphTable ligature; 37 38 /** List of alternate glyphs. */ 39 public AlternateSets alternates; 40 41 public Gee.ArrayList<BackgroundImage> background_images; 42 public string background_scale = "1"; 43 44 /** Top margin */ 45 public double top_limit; 46 47 /** Height of upper case letters. */ 48 public double top_position; 49 50 /** x-height upper bearing from origo. */ 51 public double xheight_position; 52 53 /** Base line coordinate from origo. */ 54 public double base_line; 55 56 /** Descender position */ 57 public double bottom_position; 58 59 /** Bottom margin */ 60 public double bottom_limit; 61 62 /** Custom guides. */ 63 public Gee.ArrayList<Line> custom_guides = new Gee.ArrayList<Line> (); 64 65 public string? font_file = null; 66 public string? export_directory = null; 67 68 bool modified = false; 69 70 // name table strings 71 public string postscript_name; 72 public string name; 73 public string subfamily; 74 public string full_name; 75 public string unique_identifier; 76 public string version; 77 public string description; 78 public string copyright; 79 public string license; 80 public string license_url; 81 public string trademark; 82 public string manufacturer; 83 public string designer; 84 public string vendor_url; 85 public string designer_url; 86 87 public bool bold = false; 88 public bool italic = false; 89 public int weight = 400; 90 91 public bool initialised = true; 92 93 OpenFontFormatReader otf; 94 bool otf_font = false; 95 96 /** Grid sizes. */ 97 public Gee.ArrayList<string> grid_width; 98 99 /** File format. */ 100 public FontFormat format = FontFormat.BIRDFONT; 101 102 public SpacingData spacing; 103 104 bool read_only = false; 105 106 /** Save font as many small .bfp files instead of one big .bf file */ 107 bool bfp = false; 108 BirdFontPart bfp_file; 109 110 /** File names of deleted glyphs in bfp directories. */ 111 public Gee.ArrayList<string> deleted_glyphs; 112 113 public Ligatures ligature_substitution; 114 115 public static string? default_license = null; 116 public static string? default_license_url = null; 117 118 public FontSettings settings; 119 public KerningStrings kerning_strings; 120 121 public signal void font_deleted (); 122 123 public static Font empty; 124 125 public int format_major = 0; 126 public int format_minor = 0; 127 128 public int units_per_em = 1024; 129 public bool has_svg = false; 130 131 public Font () { 132 KerningClasses kerning_classes; 133 134 postscript_name = "Typeface"; 135 name = "Typeface"; 136 subfamily = "Regular"; 137 full_name = "Typeface"; 138 unique_identifier = "Typeface"; 139 version = "Version 1.0"; 140 description = ""; 141 copyright = ""; 142 license = ""; 143 license_url = ""; 144 trademark = ""; 145 manufacturer = ""; 146 designer = ""; 147 vendor_url = ""; 148 designer_url = ""; 149 150 if (default_license != null) { 151 copyright = (!) default_license; 152 license = (!) default_license; 153 } 154 155 if (default_license_url != null) { 156 license_url = (!) default_license_url; 157 } 158 159 glyph_cache = new GlyphTable (); 160 glyph_name = new GlyphTable (); 161 ligature = new GlyphTable (); 162 163 grid_width = new Gee.ArrayList<string> (); 164 165 kerning_classes = new KerningClasses (this); 166 spacing = new SpacingData (kerning_classes); 167 168 top_limit = 84 ; 169 top_position = 72; 170 xheight_position = 56; 171 base_line = 0; 172 bottom_position = -20; 173 bottom_limit = -27; 174 175 bfp_file = new BirdFontPart (this); 176 177 deleted_glyphs = new Gee.ArrayList<string> (); 178 ligature_substitution = new Ligatures (this); 179 180 background_images = new Gee.ArrayList<BackgroundImage> (); 181 182 settings = new FontSettings (); 183 kerning_strings = new KerningStrings (); 184 185 alternates = new AlternateSets (); 186 } 187 188 ~Font () { 189 font_deleted (); 190 } 191 192 public string? get_export_directory () { 193 #if MAC 194 return export_directory; 195 #endif 196 return get_folder_path (); 197 } 198 199 public void add_default_characters () { 200 add_glyph_collection (get_notdef_character ()); 201 add_glyph_collection (get_space ()); 202 } 203 204 public Alternate? get_alternate (string glyph_name, string tag) { 205 Gee.ArrayList<Alternate> alt = alternates.get_alt (tag); 206 207 foreach (Alternate a in alt) { 208 if (a.glyph_name == glyph_name) { 209 return a; 210 } 211 } 212 213 return null; 214 } 215 216 public void add_new_alternate (GlyphCollection glyph, 217 GlyphCollection alternate, string tag) { 218 219 Alternate a; 220 Alternate? alt = get_alternate (glyph.get_name (), tag); 221 222 if (alt == null) { 223 a = new Alternate (glyph.get_name (), tag); 224 alternates.add (a); 225 } else { 226 a = (!) alt; 227 } 228 229 a.add (alternate.get_name ()); 230 glyph_name.insert (alternate.get_name (), alternate); 231 glyph_cache.insert (alternate.get_name (), alternate); 232 } 233 234 public void add_alternate (string glyph_name, string alternate, 235 string tag) { 236 237 Alternate a; 238 Alternate? alt = get_alternate (glyph_name, tag); 239 240 if (alt == null) { 241 a = new Alternate (glyph_name, tag); 242 alternates.add (a); 243 } else { 244 a = (!) alt; 245 } 246 247 a.add (alternate); 248 } 249 250 public bool has_compatible_format () { 251 return !newer_format () && !older_format (); 252 } 253 254 public bool older_format () { 255 if (format_major < BirdFontFile.MIN_FORMAT_MAJOR) { 256 return true; 257 } 258 259 if (format_major == BirdFontFile.MIN_FORMAT_MAJOR 260 && format_minor < BirdFontFile.MIN_FORMAT_MINOR) { 261 return true; 262 } 263 264 return false; 265 } 266 267 public bool newer_format () { 268 if (format_major > BirdFontFile.FORMAT_MAJOR) { 269 return true; 270 } 271 272 if (BirdFontFile.FORMAT_MAJOR == format_major 273 && format_minor > BirdFontFile.FORMAT_MINOR) { 274 return true; 275 } 276 277 return false; 278 } 279 280 public static void set_default_license (string license, string url) { 281 default_license = license; 282 default_license_url = url; 283 } 284 285 public Ligatures get_ligatures () { 286 return ligature_substitution; 287 } 288 289 public void set_weight (string w) { 290 int wi = int.parse (w); 291 292 if (wi > 0) { 293 weight = wi; 294 } 295 } 296 297 public string get_weight () { 298 return @"$weight"; 299 } 300 301 public void touch () { 302 modified = true; 303 } 304 305 public KerningClasses get_kerning_classes () { 306 return spacing.get_kerning_classes (); 307 } 308 309 public SpacingData get_spacing () { 310 return spacing; 311 } 312 313 public File get_backgrounds_folder () { 314 string fn = @"$(get_name ()) backgrounds"; 315 File f = get_child (BirdFont.get_settings_directory (), fn); 316 return f; 317 } 318 319 /** Retuns true if the current font has be modified */ 320 public bool is_modified () { 321 return modified; 322 } 323 324 /** Full path to this font file. */ 325 public string get_path () { 326 int i = 0; 327 string fn; 328 File f; 329 File file; 330 331 if (font_file != null) { 332 fn = (!) font_file; 333 334 // assume only absolute paths are used on windows 335 if (BirdFont.win32) { 336 return fn; 337 } else { 338 file = File.new_for_path (fn); 339 return (!) file.resolve_relative_path (".").get_path (); 340 } 341 } 342 343 StringBuilder sb = new StringBuilder (); 344 sb.append (Environment.get_home_dir ()); 345 sb.append (@"/$(get_name ()).birdfont"); 346 347 f = File.new_for_path (sb.str); 348 349 while (f.query_exists ()) { 350 sb.erase (); 351 sb.append (Environment.get_home_dir ()); 352 sb.append (@"/$(get_name ())$(++i).birdfont"); 353 f = File.new_for_path (sb.str); 354 } 355 356 return sb.str; 357 } 358 359 public static string get_file_from_full_path (string path) { 360 string p = path; 361 int i = p.last_index_of ("/"); 362 363 if (i == -1) { 364 i = p.last_index_of ("\\"); 365 } 366 367 p = p.substring (i + 1); 368 return p; 369 } 370 371 public string get_file_name () { 372 return get_file_from_full_path (get_path ()); 373 } 374 375 /** @return an absolute path to the font folder. */ 376 public File get_folder () { 377 string p = get_folder_path (); 378 File fp = File.new_for_path (p); 379 380 if (BirdFont.win32) { 381 if (p.index_of (":\\") == -1) { 382 p = (!) fp.resolve_relative_path ("").get_path (); 383 } 384 } else { 385 if (!p.has_prefix ("/")) { 386 p = (!) fp.resolve_relative_path ("").get_path (); 387 } 388 } 389 390 return File.new_for_path (p); 391 } 392 393 /** @return a path to the font folder, it can be relative. */ 394 public string get_folder_path () { 395 string p = get_path (); 396 int i = p.last_index_of ("/"); 397 398 if (i == -1) { 399 i = p.last_index_of ("\\"); 400 } 401 402 if (i == -1) { 403 warning (@"Can not find folder in $p."); 404 p = "."; 405 } else { 406 p = p.substring (0, i); 407 } 408 409 if (p.index_of (":") != -1 && p.char_count () == 2) { 410 p += "\\"; 411 } 412 413 return p; 414 } 415 416 public double get_height () { 417 double r = top_position - base_line; 418 return (r > 0) ? r : -r; 419 } 420 421 public void set_name (string name) { 422 string n = name; 423 this.name = n; 424 } 425 426 public string get_full_name () { 427 return full_name; 428 } 429 430 public string get_name () { 431 return name; 432 } 433 434 public void print_all () { 435 stdout.printf ("Unicode:\n"); 436 glyph_cache.for_each((g) => { 437 stdout.printf (@"$(g.get_unicode ())\n"); 438 }); 439 440 stdout.printf ("Names:\n"); 441 glyph_name.for_each((g) => { 442 stdout.printf (@"$(g.get_name ())\n"); 443 }); 444 } 445 446 public bool has_glyph (string n) { 447 return get_glyph (n) != null; 448 } 449 450 public GlyphCollection get_nonmarking_return () { 451 Glyph g; 452 GlyphCollection gc; 453 GlyphCollection? non_marking; 454 455 if (has_glyph ("nonmarkingreturn")) { 456 non_marking = get_glyph_collection ("nonmarkingreturn"); 457 458 if (non_marking == null) { 459 warning ("Non marking return not created."); 460 } else { 461 return (!)non_marking; 462 } 463 } 464 465 gc = new GlyphCollection ('\r', "nonmarkingreturn"); 466 467 g = new Glyph ("nonmarkingreturn", '\r'); 468 g.left_limit = 0; 469 g.right_limit = 0; 470 g.remove_empty_paths (); 471 472 gc.set_unassigned (false); 473 474 GlyphMaster master = new GlyphMaster (); 475 master.add_glyph (g); 476 gc.add_master (master); 477 478 return gc; 479 } 480 481 public GlyphCollection get_null_character () { 482 Glyph n; 483 GlyphCollection gc; 484 GlyphCollection? none; 485 486 if (has_glyph ("null")) { 487 none = get_glyph_collection ("null"); 488 489 if (none == null) { 490 warning("Null character not created."); 491 } else { 492 return (!) none; 493 } 494 } 495 496 gc = new GlyphCollection ('\0', "null"); 497 n = new Glyph ("null", '\0'); 498 499 GlyphMaster master = new GlyphMaster (); 500 master.add_glyph (n); 501 gc.add_master (master); 502 503 gc.set_unassigned (false); 504 505 n.left_limit = 0; 506 n.right_limit = 0; 507 n.remove_empty_paths (); 508 509 return gc; 510 } 511 512 public GlyphCollection get_space () { 513 Glyph n; 514 GlyphCollection gc; 515 GlyphCollection? space = null; 516 517 if (has_glyph (" ")) { 518 space = get_glyph_collection (" "); 519 } 520 521 if (has_glyph ("space")) { 522 space = get_glyph_collection ("space"); 523 } 524 525 if (space != null) { 526 return (!) space; 527 } 528 529 gc = new GlyphCollection (' ', "space"); 530 531 n = new Glyph (" ", ' '); 532 n.left_limit = 0; 533 n.right_limit = 27; 534 n.remove_empty_paths (); 535 536 GlyphMaster master = new GlyphMaster (); 537 master.add_glyph (n); 538 gc.add_master (master); 539 540 gc.set_unassigned (false); 541 542 return gc; 543 } 544 545 public GlyphCollection get_notdef_character () { 546 Glyph g; 547 GlyphCollection gc; 548 549 Path p; 550 Path i; 551 552 if (has_glyph (".notdef")) { 553 return (!) get_glyph_collection (".notdef"); 554 } 555 556 gc = new GlyphCollection ('\0', ".notdef"); 557 g = new Glyph (".notdef", 0); 558 p = new Path (); 559 i = new Path (); 560 561 gc.set_unassigned (true); 562 563 GlyphMaster master = new GlyphMaster (); 564 master.add_glyph (g); 565 gc.add_master (master); 566 567 g.left_limit = -20; 568 g.right_limit = 33; 569 570 g.add_help_lines (); 571 572 p.add (-20, top_position - 5); 573 p.add (20, top_position - 5); 574 p.add (20, base_line + 5); 575 p.add (-20, base_line + 5); 576 p.close (); 577 578 i.add (-15, top_position - 10); 579 i.add (15, top_position - 10); 580 i.add (15, base_line + 10); 581 i.add (-15, base_line + 10); 582 i.reverse (); 583 i.close (); 584 585 g.add_path (i); 586 g.add_path (p); 587 588 i.recalculate_linear_handles (); 589 p.recalculate_linear_handles (); 590 591 return gc; 592 } 593 594 public void add_glyph_collection (GlyphCollection glyph_collection) { 595 GlyphCollection? gc; 596 597 if (unlikely (glyph_collection.get_name () == "")) { 598 warning ("Refusing to add glyph with name \"\", null character should be named null."); 599 return; 600 } 601 602 string name = glyph_collection.get_name (); 603 gc = glyph_name.get (name); 604 if (unlikely (gc != null)) { 605 warning ("glyph has already been added: " + name); 606 return; 607 } 608 609 glyph_name.insert (glyph_collection.get_name (), glyph_collection); 610 611 if (glyph_collection.get_unicode () != "") { 612 glyph_cache.insert ((!) glyph_collection.get_unicode (), glyph_collection); 613 } else { 614 glyph_cache.insert ((!) glyph_collection.get_name (), glyph_collection); 615 } 616 617 if (glyph_collection.is_unassigned ()) { 618 ligature.insert (glyph_collection.get_name (), glyph_collection); 619 } 620 } 621 622 public static string get_name_for_character (unichar c) { 623 StringBuilder sb; 624 625 if (c == 0) { 626 return ".null".dup (); 627 } 628 629 sb = new StringBuilder (); 630 sb.append_unichar (c); 631 return sb.str; 632 } 633 634 public bool has_name (string name) { 635 return glyph_name.has_key (name); 636 } 637 638 public void delete_glyph (GlyphCollection glyph) { 639 glyph_cache.remove (glyph.get_unicode ()); 640 glyph_cache.remove (glyph.get_name ()); 641 glyph_name.remove (glyph.get_name ()); 642 ligature.remove (glyph.get_current ().get_name ()); 643 644 foreach (Alternate a in alternates.alternates) { 645 a.remove (glyph); 646 } 647 648 foreach (GlyphMaster master in glyph.glyph_masters) { 649 foreach (Glyph g in master.glyphs) { 650 add_deleted_glyph (g, master); 651 } 652 } 653 } 654 655 public void add_deleted_glyph (Glyph g, GlyphMaster master) { 656 string file_name; 657 file_name = BirdFontPart.get_glyph_base_file_name (g, master) + ".bfp"; 658 deleted_glyphs.add (file_name); 659 } 660 661 // FIXME: the order of ligature substitutions 662 public GlyphCollection? get_ligature (uint index) { 663 return ligature.nth (index); 664 } 665 666 /** Obtain all versions and alterntes for this glyph. */ 667 public GlyphCollection? get_glyph_collection (string unichar_code) { 668 GlyphCollection? gc = null; 669 gc = glyph_cache.get (unichar_code); 670 return gc; 671 } 672 673 /** Get glyph collection by name. */ 674 public GlyphCollection? get_glyph_collection_by_name (string? glyph) { 675 GlyphCollection? gc = null; 676 677 if (glyph != null) { 678 gc = glyph_name.get ((!) glyph); 679 } 680 681 return gc; 682 } 683 684 /** Get glyph by name. */ 685 public Glyph? get_glyph_by_name (string glyph) { 686 GlyphCollection? gc = get_glyph_collection_by_name (glyph); 687 688 if (gc == null) { 689 return null; 690 } 691 692 return ((!)gc).get_current (); 693 } 694 695 public Glyph? get_glyph (string name) { 696 GlyphCollection? gc = null; 697 gc = glyph_name.get (name); 698 699 if (gc == null || ((!)gc).length () == 0) { 700 return null; 701 } 702 703 return ((!)gc).get_current (); 704 } 705 706 public GlyphCollection? get_glyph_collection_index (unichar glyph_index) { 707 if (!(0 <= glyph_index < glyph_name.length ())) { 708 return null; 709 } 710 711 return glyph_name.nth (glyph_index); 712 } 713 714 public Glyph? get_glyph_index (unichar glyph_index) { 715 GlyphCollection? gc; 716 717 gc = get_glyph_collection_index (glyph_index); 718 719 if (gc != null) { 720 return ((!) gc).get_current (); 721 } 722 723 return null; 724 } 725 726 public void add_background_image (BackgroundImage image) { 727 background_images.add (image); 728 } 729 730 public void init_bfp (string directory) { 731 try { 732 bfp_file = new BirdFontPart (this); 733 bfp_file.create_directory (directory); 734 bfp_file.save (); 735 this.bfp = true; 736 } catch (GLib.Error e) { 737 warning (e.message); 738 // FIXME: notify user 739 } 740 } 741 742 public void set_bfp (bool bfp) { 743 this.bfp = bfp; 744 } 745 746 public bool is_bfp () { 747 return bfp; 748 } 749 750 public void save () { 751 if (is_bfp ()) { 752 save_bfp (); 753 } else { 754 save_bf (); 755 } 756 757 settings.save (get_file_name ()); 758 } 759 760 private void save_backups () throws GLib.Error { 761 string num_backups = Preferences.get ("num_backups"); 762 763 if (num_backups == "") { 764 num_backups = "20"; 765 } 766 767 int backups = int.parse (num_backups); 768 769 if (backups == 0) { 770 printd ("No backups according to settings. Skipping it."); 771 delete_old_backups (backups); 772 return; 773 } 774 775 if (backups > 0) { 776 string path = (!) font_file; 777 string bf_data = ""; 778 779 if (FileUtils.get_contents (path, out bf_data)) { 780 DateTime now = new DateTime.now_local (); 781 string time_stamp = now.to_string (); 782 783 time_stamp = time_stamp.replace (":", "_"); 784 time_stamp = time_stamp.replace ("-", "_"); 785 786 string fn = get_file_name (); 787 File backup_directory_for_font = Preferences.get_backup_directory_for_font (fn); 788 789 if (!backup_directory_for_font.query_exists ()) { 790 int error = DirUtils.create ((!) backup_directory_for_font.get_path (), 0766); 791 792 if (error == -1) { 793 stderr.printf (@"Failed to create backup directory: $((!) backup_directory_for_font.get_path ())\n"); 794 } 795 } 796 797 string file_name = get_file_name (); 798 799 if (file_name.has_suffix (".bf")) { 800 file_name = file_name.substring (0, file_name.length - ".bf".length); 801 } 802 803 if (file_name.has_suffix (".birdfont")) { 804 file_name = file_name.substring (0, file_name.length - ".birdfont".length); 805 } 806 807 string backup_file_name = file_name + @"-$(time_stamp).bf_backup"; 808 File backup_file = get_child (backup_directory_for_font, backup_file_name); 809 printd (@"Saving backup to: $((!) backup_file.get_path ())\n"); 810 811 FileUtils.set_contents ((!) backup_file.get_path (), bf_data); 812 } 813 } 814 815 delete_old_backups (backups); 816 } 817 818 public static Gee.ArrayList<string> get_sorted_backups (string font_file_name) { 819 Gee.ArrayList<string> backups = new Gee.ArrayList<string> (); 820 821 try { 822 File backup_directory_for_font = Preferences.get_backup_directory_for_font (font_file_name); 823 Dir dir = Dir.open ((!) backup_directory_for_font.get_path (), 0); 824 825 string? name = null; 826 while ((name = dir.read_name ()) != null) { 827 string file_name = (!) name; 828 829 printd (@"backup_directory_for_font: $((!) backup_directory_for_font.get_path ())\n"); 830 printd (@"file_name $file_name\n"); 831 832 File backup_file = get_child (backup_directory_for_font, file_name); 833 834 if (FileUtils.test ((!) backup_file.get_path (), FileTest.IS_REGULAR) 835 && file_name.has_suffix (".bf_backup")) { 836 backups.add ((!) backup_file.get_path ()); 837 } else { 838 warning (@"$file_name does not seem to be a backup file."); 839 } 840 } 841 } catch (GLib.Error error) { 842 warning (error.message); 843 warning("Can't fetch backup files."); 844 } 845 846 backups.sort (); 847 848 return backups; 849 } 850 851 public void delete_old_backups (int keep) { 852 try { 853 string file_name = get_file_name (); 854 Gee.ArrayList<string> backups = get_sorted_backups (file_name); 855 Gee.ArrayList<string> old_backups = new Gee.ArrayList<string> (); 856 857 for (int i = 0; i < backups.size - keep; i++) { 858 string b = backups.get (i); 859 old_backups.add (b); 860 } 861 862 foreach (string path in old_backups) { 863 printd (@"Deleting backup: $(path)\n"); 864 File file = File.new_for_path (path); 865 file.delete (); 866 } 867 } catch (GLib.Error error) { 868 warning (error.message); 869 warning("Can't delet backup."); 870 } 871 } 872 873 private bool save_bfp () { 874 return bfp_file.save (); 875 } 876 877 private void save_bf () { 878 BirdFontFile birdfont_file = new BirdFontFile (this); 879 string path; 880 bool file_written; 881 882 if (font_file == null) { 883 warning ("File name not set."); 884 return; 885 } 886 887 path = (!) font_file; 888 file_written = birdfont_file.write_font_file (path); 889 890 if (read_only) { 891 warning (@"$path is write protected."); 892 return; 893 } 894 895 if (!path.has_suffix (".bf") && !path.has_suffix (".birdfont")) { 896 warning ("Expecting .bf or .birdfont format."); 897 return; 898 } 899 900 try { 901 save_backups (); 902 } catch (GLib.Error e) { 903 warning (e.message); 904 warning ("Can't save backup."); 905 } 906 907 modified = false; 908 } 909 910 public void set_font_file (string path) { 911 font_file = path; 912 modified = false; 913 } 914 915 /** Number of glyphs in this font. */ 916 public uint length () { 917 return glyph_name.length (); 918 } 919 920 public bool is_empty () { 921 return (glyph_name.length () == 0); 922 } 923 924 public void set_file (string path) { 925 font_file = path; 926 } 927 928 public bool load () { 929 string path; 930 bool loaded = false; 931 932 initialised = true; 933 otf_font = false; 934 935 if (font_file == null) { 936 warning ("No file name."); 937 return false; 938 } 939 940 path = (!) font_file; 941 942 grid_width.clear (); 943 944 // empty cache and fill it with new glyphs from disk 945 glyph_cache.remove_all (); 946 glyph_name.remove_all (); 947 ligature.remove_all (); 948 949 if (path.has_suffix (".svg") || path.has_suffix (".SVG")) { 950 Toolbox.select_tool_by_name ("cubic_points"); 951 loaded = parse_svg_file (path); 952 953 if (!loaded) { 954 warning ("Failed to load SVG font."); 955 } 956 957 format = FontFormat.SVG; 958 } 959 960 if (path.has_suffix (".ffi")) { 961 loaded = parse_bf_file (path); 962 format = FontFormat.FFI; 963 } 964 965 if (path.has_suffix (".bf") 966 || path.has_suffix (".BF") 967 || path.has_suffix (".BIRDFONT") 968 || path.has_suffix (".birdfont") 969 || path.has_suffix (".bf_backup")) { 970 971 loaded = parse_bf_file (path); 972 format = FontFormat.BIRDFONT; 973 974 if (path.has_suffix (".bf_backup")) { 975 font_file = null; 976 } 977 } 978 979 if (path.has_suffix (".bfp") || path.has_suffix (".BFP")) { 980 loaded = parse_bfp_file (path); 981 format = FontFormat.BIRDFONT_PART; 982 } 983 984 if (path.has_suffix (".ttf") || path.has_suffix (".TTF")) { 985 loaded = parse_freetype_file (path); 986 987 if (!loaded) { 988 warning ("Failed to load TTF font."); 989 } 990 991 format = FontFormat.FREETYPE; 992 993 // run the old parser for debugging puposes 994 if (BirdFont.has_argument ("--test")) { 995 try { 996 OpenFontFormatReader or = new OpenFontFormatReader (); 997 or.parse_index (path); 998 } catch (GLib.Error e) { 999 warning (e.message); 1000 } 1001 } 1002 1003 font_file = null; // make sure BirdFont asks where to save the file 1004 } 1005 1006 if (path.has_suffix (".otf") || path.has_suffix (".OTF")) { 1007 loaded = parse_freetype_file (path); 1008 1009 if (!loaded) { 1010 warning ("Failed to load OTF font."); 1011 } 1012 1013 format = FontFormat.FREETYPE; 1014 1015 font_file = null; 1016 } 1017 1018 if (loaded) { 1019 settings.load (get_file_name ()); 1020 kerning_strings.load (this); 1021 add_default_characters (); 1022 } 1023 1024 return loaded; 1025 } 1026 1027 private bool parse_bfp_file (string path) { 1028 return bfp_file.load (path); 1029 } 1030 1031 private bool parse_bf_file (string path) { 1032 BirdFontFile font = new BirdFontFile (this); 1033 return font.load (path); 1034 } 1035 1036 private bool parse_freetype_file (string path) { 1037 string font_data; 1038 StringBuilder? data; 1039 int error; 1040 bool parsed; 1041 BirdFontFile bf_font = new BirdFontFile (this); 1042 1043 data = load_freetype_font (path, out error); 1044 1045 if (error != 0) { 1046 warning ("Failed to load freetype font."); 1047 return false; 1048 } 1049 1050 if (data == null) { 1051 warning ("No svg data."); 1052 return false; 1053 } 1054 1055 font_data = ((!) data).str; 1056 parsed = bf_font.load_data (font_data); 1057 1058 if (!parsed) { 1059 warning ("Failed to parse loaded freetype font."); 1060 } 1061 1062 return parsed; 1063 } 1064 1065 private bool parse_svg_file (string path) { 1066 SvgFont svg_font = new SvgFont (this); 1067 svg_font.load (path); 1068 return true; 1069 } 1070 1071 public bool parse_otf_file (string path) throws GLib.Error { 1072 otf = new OpenFontFormatReader (); 1073 otf_font = true; 1074 otf.parse_index (path); 1075 return true; 1076 } 1077 1078 public void set_read_only (bool r) { 1079 read_only = r; 1080 } 1081 1082 /** 1083 * @param glyphs Name of glyphs or unicode values separated by space. 1084 * @return glyph names 1085 */ 1086 public Gee.ArrayList<string> get_names (string glyphs) { 1087 return get_names_order (glyphs, false); 1088 } 1089 1090 public Gee.ArrayList<string> get_names_in_reverse_order (string glyphs) { 1091 return get_names_order (glyphs, true); 1092 } 1093 1094 public Gee.ArrayList<string> get_names_order (string glyphs, bool reverse) { 1095 Gee.ArrayList<string> names = new Gee.ArrayList<string> (); 1096 string[] parts = glyphs.strip ().split (" "); 1097 1098 foreach (string p in parts) { 1099 if (p.has_prefix ("U+") || p.has_prefix ("u+")) { 1100 p = (!) to_unichar (p).to_string (); 1101 } 1102 1103 if (p == "space") { 1104 p = " "; 1105 } 1106 1107 if (p == "divis") { 1108 p = "-"; 1109 } 1110 1111 if (!has_glyph (p)) { 1112 warning (@"The character $p does not have a glyph in " + get_file_name ()); 1113 p = ".notdef"; 1114 } 1115 1116 if (p != "") { 1117 if (reverse) { 1118 names.insert (0, p); 1119 } else { 1120 names.add (p); 1121 } 1122 } 1123 } 1124 1125 return names; 1126 } 1127 1128 public static unichar to_unichar (string unicode) { 1129 int index = 2; 1130 int i = 0; 1131 unichar c; 1132 unichar rc = 0; 1133 bool r; 1134 1135 if (unlikely (!unicode.has_prefix ("U+") && !unicode.has_prefix ("u+"))) { 1136 warning (@"All unicode values must begin with U+ ($unicode)"); 1137 return '\0'; 1138 } 1139 1140 try { 1141 while (r = unicode.get_next_char (ref index, out c)) { 1142 rc <<= 4; 1143 rc += hex_to_oct (c); 1144 1145 if (++i > 6) { 1146 throw new ConvertError.FAILED ("i > 6"); 1147 } 1148 } 1149 } catch (ConvertError e) { 1150 warning (@"unicode: $unicode\n"); 1151 warning (e.message); 1152 rc = '\0'; 1153 } 1154 1155 return rc; 1156 } 1157 1158 private static string oct_to_hex (uint8 o) { 1159 switch (o) { 1160 case 10: return "a"; 1161 case 11: return "b"; 1162 case 12: return "c"; 1163 case 13: return "d"; 1164 case 14: return "e"; 1165 case 15: return "f"; 1166 } 1167 1168 return_val_if_fail (0 <= o <= 9, "-".dup ()); 1169 1170 return o.to_string (); 1171 } 1172 1173 private static uint8 hex_to_oct (unichar o) 1174 throws ConvertError { 1175 StringBuilder s = new StringBuilder (); 1176 s.append_unichar (o); 1177 1178 switch (o) { 1179 case 'a': return 10; 1180 case 'b': return 11; 1181 case 'c': return 12; 1182 case 'd': return 13; 1183 case 'e': return 14; 1184 case 'f': return 15; 1185 case 'A': return 10; 1186 case 'B': return 11; 1187 case 'C': return 12; 1188 case 'D': return 13; 1189 case 'E': return 14; 1190 case 'F': return 15; 1191 } 1192 1193 if (!('0' <= o <= '9')) { 1194 throw new ConvertError.FAILED (@"Expecting a number ($(s.str))."); 1195 } 1196 1197 return (uint8) (o - '0'); 1198 } 1199 1200 public static string to_hex (unichar ch) { 1201 StringBuilder s = new StringBuilder (); 1202 s.append ("U+"); 1203 s.append (to_hex_code (ch)); 1204 return s.str; 1205 } 1206 1207 public static string to_hex_code (unichar ch) { 1208 StringBuilder s = new StringBuilder (); 1209 1210 uint8 a = (uint8)(ch & 0x00000F); 1211 uint8 b = (uint8)((ch & 0x0000F0) >> 4 * 1); 1212 uint8 c = (uint8)((ch & 0x000F00) >> 4 * 2); 1213 uint8 d = (uint8)((ch & 0x00F000) >> 4 * 3); 1214 uint8 e = (uint8)((ch & 0x0F0000) >> 4 * 4); 1215 uint8 f = (uint8)((ch & 0xF00000) >> 4 * 5); 1216 1217 if (e != 0 || f != 0) { 1218 s.append (oct_to_hex (f)); 1219 s.append (oct_to_hex (e)); 1220 } 1221 1222 if (c != 0 || d != 0) { 1223 s.append (oct_to_hex (d)); 1224 s.append (oct_to_hex (c)); 1225 } 1226 1227 s.append (oct_to_hex (b)); 1228 s.append (oct_to_hex (a)); 1229 1230 return s.str; 1231 } 1232 } 1233 1234 } 1235