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 gc = glyph_name.get (glyph_collection.get_name ()); 603 if (unlikely (gc != null)) { 604 warning ("glyph has already been added"); 605 return; 606 } 607 608 glyph_name.insert (glyph_collection.get_name (), glyph_collection); 609 610 if (glyph_collection.get_unicode () != "") { 611 glyph_cache.insert ((!) glyph_collection.get_unicode (), glyph_collection); 612 } else { 613 glyph_cache.insert ((!) glyph_collection.get_name (), glyph_collection); 614 } 615 616 if (glyph_collection.is_unassigned ()) { 617 ligature.insert (glyph_collection.get_name (), glyph_collection); 618 } 619 } 620 621 public static string get_name_for_character (unichar c) { 622 StringBuilder sb; 623 624 if (c == 0) { 625 return ".null".dup (); 626 } 627 628 sb = new StringBuilder (); 629 sb.append_unichar (c); 630 return sb.str; 631 } 632 633 public bool has_name (string name) { 634 return glyph_name.has_key (name); 635 } 636 637 public void delete_glyph (GlyphCollection glyph) { 638 glyph_cache.remove (glyph.get_unicode ()); 639 glyph_cache.remove (glyph.get_name ()); 640 glyph_name.remove (glyph.get_name ()); 641 ligature.remove (glyph.get_current ().get_name ()); 642 643 foreach (Alternate a in alternates.alternates) { 644 a.remove (glyph); 645 } 646 647 foreach (GlyphMaster master in glyph.glyph_masters) { 648 foreach (Glyph g in master.glyphs) { 649 add_deleted_glyph (g, master); 650 } 651 } 652 } 653 654 public void add_deleted_glyph (Glyph g, GlyphMaster master) { 655 string file_name; 656 file_name = BirdFontPart.get_glyph_base_file_name (g, master) + ".bfp"; 657 deleted_glyphs.add (file_name); 658 } 659 660 // FIXME: the order of ligature substitutions 661 public GlyphCollection? get_ligature (uint index) { 662 return ligature.nth (index); 663 } 664 665 /** Obtain all versions and alterntes for this glyph. */ 666 public GlyphCollection? get_glyph_collection (string unichar_code) { 667 GlyphCollection? gc = null; 668 gc = glyph_cache.get (unichar_code); 669 return gc; 670 } 671 672 /** Get glyph collection by name. */ 673 public GlyphCollection? get_glyph_collection_by_name (string? glyph) { 674 GlyphCollection? gc = null; 675 676 if (glyph != null) { 677 gc = glyph_name.get ((!) glyph); 678 } 679 680 return gc; 681 } 682 683 /** Get glyph by name. */ 684 public Glyph? get_glyph_by_name (string glyph) { 685 GlyphCollection? gc = get_glyph_collection_by_name (glyph); 686 687 if (gc == null) { 688 return null; 689 } 690 691 return ((!)gc).get_current (); 692 } 693 694 public Glyph? get_glyph (string name) { 695 GlyphCollection? gc = null; 696 gc = glyph_name.get (name); 697 698 if (gc == null || ((!)gc).length () == 0) { 699 return null; 700 } 701 702 return ((!)gc).get_current (); 703 } 704 705 public GlyphCollection? get_glyph_collection_index (unichar glyph_index) { 706 if (!(0 <= glyph_index < glyph_name.length ())) { 707 return null; 708 } 709 710 return glyph_name.nth (glyph_index); 711 } 712 713 public Glyph? get_glyph_index (unichar glyph_index) { 714 GlyphCollection? gc; 715 716 gc = get_glyph_collection_index (glyph_index); 717 718 if (gc != null) { 719 return ((!) gc).get_current (); 720 } 721 722 return null; 723 } 724 725 public void add_background_image (BackgroundImage image) { 726 background_images.add (image); 727 } 728 729 public void init_bfp (string directory) { 730 try { 731 bfp_file = new BirdFontPart (this); 732 bfp_file.create_directory (directory); 733 bfp_file.save (); 734 this.bfp = true; 735 } catch (GLib.Error e) { 736 warning (e.message); 737 // FIXME: notify user 738 } 739 } 740 741 public void set_bfp (bool bfp) { 742 this.bfp = bfp; 743 } 744 745 public bool is_bfp () { 746 return bfp; 747 } 748 749 public void save () { 750 if (is_bfp ()) { 751 save_bfp (); 752 } else { 753 save_bf (); 754 } 755 756 settings.save (get_file_name ()); 757 } 758 759 private void save_backups () throws GLib.Error { 760 string num_backups = Preferences.get ("num_backups"); 761 762 if (num_backups == "") { 763 num_backups = "20"; 764 } 765 766 int backups = int.parse (num_backups); 767 768 if (backups == 0) { 769 printd ("No backups according to settings. Skipping it."); 770 delete_old_backups (backups); 771 return; 772 } 773 774 if (backups > 0) { 775 string path = (!) font_file; 776 string bf_data = ""; 777 778 if (FileUtils.get_contents (path, out bf_data)) { 779 DateTime now = new DateTime.now_local (); 780 string time_stamp = now.to_string (); 781 782 time_stamp = time_stamp.replace (":", "_"); 783 time_stamp = time_stamp.replace ("-", "_"); 784 785 string fn = get_file_name (); 786 File backup_directory_for_font = Preferences.get_backup_directory_for_font (fn); 787 788 if (!backup_directory_for_font.query_exists ()) { 789 int error = DirUtils.create ((!) backup_directory_for_font.get_path (), 0766); 790 791 if (error == -1) { 792 stderr.printf (@"Failed to create backup directory: $((!) backup_directory_for_font.get_path ())\n"); 793 } 794 } 795 796 string file_name = get_file_name (); 797 798 if (file_name.has_suffix (".bf")) { 799 file_name = file_name.substring (0, file_name.length - ".bf".length); 800 } 801 802 if (file_name.has_suffix (".birdfont")) { 803 file_name = file_name.substring (0, file_name.length - ".birdfont".length); 804 } 805 806 string backup_file_name = file_name + @"-$(time_stamp).bf_backup"; 807 File backup_file = get_child (backup_directory_for_font, backup_file_name); 808 printd (@"Saving backup to: $((!) backup_file.get_path ())\n"); 809 810 FileUtils.set_contents ((!) backup_file.get_path (), bf_data); 811 } 812 } 813 814 delete_old_backups (backups); 815 } 816 817 public static Gee.ArrayList<string> get_sorted_backups (string font_file_name) { 818 Gee.ArrayList<string> backups = new Gee.ArrayList<string> (); 819 820 try { 821 File backup_directory_for_font = Preferences.get_backup_directory_for_font (font_file_name); 822 Dir dir = Dir.open ((!) backup_directory_for_font.get_path (), 0); 823 824 string? name = null; 825 while ((name = dir.read_name ()) != null) { 826 string file_name = (!) name; 827 828 printd (@"backup_directory_for_font: $((!) backup_directory_for_font.get_path ())\n"); 829 printd (@"file_name $file_name\n"); 830 831 File backup_file = get_child (backup_directory_for_font, file_name); 832 833 if (FileUtils.test ((!) backup_file.get_path (), FileTest.IS_REGULAR) 834 && file_name.has_suffix (".bf_backup")) { 835 backups.add ((!) backup_file.get_path ()); 836 } else { 837 warning (@"$file_name does not seem to be a backup file."); 838 } 839 } 840 } catch (GLib.Error error) { 841 warning (error.message); 842 warning("Can't fetch backup files."); 843 } 844 845 backups.sort (); 846 847 return backups; 848 } 849 850 public void delete_old_backups (int keep) { 851 try { 852 string file_name = get_file_name (); 853 Gee.ArrayList<string> backups = get_sorted_backups (file_name); 854 Gee.ArrayList<string> old_backups = new Gee.ArrayList<string> (); 855 856 for (int i = 0; i < backups.size - keep; i++) { 857 string b = backups.get (i); 858 old_backups.add (b); 859 } 860 861 foreach (string path in old_backups) { 862 printd (@"Deleting backup: $(path)\n"); 863 File file = File.new_for_path (path); 864 file.delete (); 865 } 866 } catch (GLib.Error error) { 867 warning (error.message); 868 warning("Can't delet backup."); 869 } 870 } 871 872 private bool save_bfp () { 873 return bfp_file.save (); 874 } 875 876 private void save_bf () { 877 BirdFontFile birdfont_file = new BirdFontFile (this); 878 string path; 879 bool file_written; 880 881 if (font_file == null) { 882 warning ("File name not set."); 883 return; 884 } 885 886 path = (!) font_file; 887 file_written = birdfont_file.write_font_file (path); 888 889 if (read_only) { 890 warning (@"$path is write protected."); 891 return; 892 } 893 894 if (!path.has_suffix (".bf") && !path.has_suffix (".birdfont")) { 895 warning ("Expecting .bf or .birdfont format."); 896 return; 897 } 898 899 try { 900 save_backups (); 901 } catch (GLib.Error e) { 902 warning (e.message); 903 warning ("Can't save backup."); 904 } 905 906 modified = false; 907 } 908 909 public void set_font_file (string path) { 910 font_file = path; 911 modified = false; 912 } 913 914 /** Number of glyphs in this font. */ 915 public uint length () { 916 return glyph_name.length (); 917 } 918 919 public bool is_empty () { 920 return (glyph_name.length () == 0); 921 } 922 923 public void set_file (string path) { 924 font_file = path; 925 } 926 927 public bool load () { 928 string path; 929 bool loaded = false; 930 931 initialised = true; 932 otf_font = false; 933 934 if (font_file == null) { 935 warning ("No file name."); 936 return false; 937 } 938 939 path = (!) font_file; 940 941 grid_width.clear (); 942 943 // empty cache and fill it with new glyphs from disk 944 glyph_cache.remove_all (); 945 glyph_name.remove_all (); 946 ligature.remove_all (); 947 948 if (path.has_suffix (".svg") || path.has_suffix (".SVG")) { 949 Toolbox.select_tool_by_name ("cubic_points"); 950 loaded = parse_svg_file (path); 951 952 if (!loaded) { 953 warning ("Failed to load SVG font."); 954 } 955 956 format = FontFormat.SVG; 957 } 958 959 if (path.has_suffix (".ffi")) { 960 loaded = parse_bf_file (path); 961 format = FontFormat.FFI; 962 } 963 964 if (path.has_suffix (".bf") 965 || path.has_suffix (".BF") 966 || path.has_suffix (".BIRDFONT") 967 || path.has_suffix (".birdfont") 968 || path.has_suffix (".bf_backup")) { 969 970 loaded = parse_bf_file (path); 971 format = FontFormat.BIRDFONT; 972 973 if (path.has_suffix (".bf_backup")) { 974 font_file = null; 975 } 976 } 977 978 if (path.has_suffix (".bfp") || path.has_suffix (".BFP")) { 979 loaded = parse_bfp_file (path); 980 format = FontFormat.BIRDFONT_PART; 981 } 982 983 if (path.has_suffix (".ttf") || path.has_suffix (".TTF")) { 984 loaded = parse_freetype_file (path); 985 986 if (!loaded) { 987 warning ("Failed to load TTF font."); 988 } 989 990 format = FontFormat.FREETYPE; 991 992 // run the old parser for debugging puposes 993 if (BirdFont.has_argument ("--test")) { 994 try { 995 OpenFontFormatReader or = new OpenFontFormatReader (); 996 or.parse_index (path); 997 } catch (GLib.Error e) { 998 warning (e.message); 999 } 1000 } 1001 1002 font_file = null; // make sure BirdFont asks where to save the file 1003 } 1004 1005 if (path.has_suffix (".otf") || path.has_suffix (".OTF")) { 1006 loaded = parse_freetype_file (path); 1007 1008 if (!loaded) { 1009 warning ("Failed to load OTF font."); 1010 } 1011 1012 format = FontFormat.FREETYPE; 1013 1014 font_file = null; 1015 } 1016 1017 if (loaded) { 1018 settings.load (get_file_name ()); 1019 kerning_strings.load (this); 1020 add_default_characters (); 1021 } 1022 1023 return loaded; 1024 } 1025 1026 private bool parse_bfp_file (string path) { 1027 return bfp_file.load (path); 1028 } 1029 1030 private bool parse_bf_file (string path) { 1031 BirdFontFile font = new BirdFontFile (this); 1032 return font.load (path); 1033 } 1034 1035 private bool parse_freetype_file (string path) { 1036 string font_data; 1037 StringBuilder? data; 1038 int error; 1039 bool parsed; 1040 BirdFontFile bf_font = new BirdFontFile (this); 1041 1042 data = load_freetype_font (path, out error); 1043 1044 if (error != 0) { 1045 warning ("Failed to load freetype font."); 1046 return false; 1047 } 1048 1049 if (data == null) { 1050 warning ("No svg data."); 1051 return false; 1052 } 1053 1054 font_data = ((!) data).str; 1055 parsed = bf_font.load_data (font_data); 1056 1057 if (!parsed) { 1058 warning ("Failed to parse loaded freetype font."); 1059 } 1060 1061 return parsed; 1062 } 1063 1064 private bool parse_svg_file (string path) { 1065 SvgFont svg_font = new SvgFont (this); 1066 svg_font.load (path); 1067 return true; 1068 } 1069 1070 public bool parse_otf_file (string path) throws GLib.Error { 1071 otf = new OpenFontFormatReader (); 1072 otf_font = true; 1073 otf.parse_index (path); 1074 return true; 1075 } 1076 1077 public void set_read_only (bool r) { 1078 read_only = r; 1079 } 1080 1081 /** 1082 * @param glyphs Name of glyphs or unicode values separated by space. 1083 * @return glyph names 1084 */ 1085 public Gee.ArrayList<string> get_names (string glyphs) { 1086 return get_names_order (glyphs, false); 1087 } 1088 1089 public Gee.ArrayList<string> get_names_in_reverse_order (string glyphs) { 1090 return get_names_order (glyphs, true); 1091 } 1092 1093 public Gee.ArrayList<string> get_names_order (string glyphs, bool reverse) { 1094 Gee.ArrayList<string> names = new Gee.ArrayList<string> (); 1095 string[] parts = glyphs.strip ().split (" "); 1096 1097 foreach (string p in parts) { 1098 if (p.has_prefix ("U+") || p.has_prefix ("u+")) { 1099 p = (!) to_unichar (p).to_string (); 1100 } 1101 1102 if (p == "space") { 1103 p = " "; 1104 } 1105 1106 if (p == "divis") { 1107 p = "-"; 1108 } 1109 1110 if (!has_glyph (p)) { 1111 warning (@"The character $p does not have a glyph in " + get_file_name ()); 1112 p = ".notdef"; 1113 } 1114 1115 if (p != "") { 1116 if (reverse) { 1117 names.insert (0, p); 1118 } else { 1119 names.add (p); 1120 } 1121 } 1122 } 1123 1124 return names; 1125 } 1126 1127 public static unichar to_unichar (string unicode) { 1128 int index = 2; 1129 int i = 0; 1130 unichar c; 1131 unichar rc = 0; 1132 bool r; 1133 1134 if (unlikely (!unicode.has_prefix ("U+") && !unicode.has_prefix ("u+"))) { 1135 warning (@"All unicode values must begin with U+ ($unicode)"); 1136 return '\0'; 1137 } 1138 1139 try { 1140 while (r = unicode.get_next_char (ref index, out c)) { 1141 rc <<= 4; 1142 rc += hex_to_oct (c); 1143 1144 if (++i > 6) { 1145 throw new ConvertError.FAILED ("i > 6"); 1146 } 1147 } 1148 } catch (ConvertError e) { 1149 warning (@"unicode: $unicode\n"); 1150 warning (e.message); 1151 rc = '\0'; 1152 } 1153 1154 return rc; 1155 } 1156 1157 private static string oct_to_hex (uint8 o) { 1158 switch (o) { 1159 case 10: return "a"; 1160 case 11: return "b"; 1161 case 12: return "c"; 1162 case 13: return "d"; 1163 case 14: return "e"; 1164 case 15: return "f"; 1165 } 1166 1167 return_val_if_fail (0 <= o <= 9, "-".dup ()); 1168 1169 return o.to_string (); 1170 } 1171 1172 private static uint8 hex_to_oct (unichar o) 1173 throws ConvertError { 1174 StringBuilder s = new StringBuilder (); 1175 s.append_unichar (o); 1176 1177 switch (o) { 1178 case 'a': return 10; 1179 case 'b': return 11; 1180 case 'c': return 12; 1181 case 'd': return 13; 1182 case 'e': return 14; 1183 case 'f': return 15; 1184 case 'A': return 10; 1185 case 'B': return 11; 1186 case 'C': return 12; 1187 case 'D': return 13; 1188 case 'E': return 14; 1189 case 'F': return 15; 1190 } 1191 1192 if (!('0' <= o <= '9')) { 1193 throw new ConvertError.FAILED (@"Expecting a number ($(s.str))."); 1194 } 1195 1196 return (uint8) (o - '0'); 1197 } 1198 1199 public static string to_hex (unichar ch) { 1200 StringBuilder s = new StringBuilder (); 1201 s.append ("U+"); 1202 s.append (to_hex_code (ch)); 1203 return s.str; 1204 } 1205 1206 public static string to_hex_code (unichar ch) { 1207 StringBuilder s = new StringBuilder (); 1208 1209 uint8 a = (uint8)(ch & 0x00000F); 1210 uint8 b = (uint8)((ch & 0x0000F0) >> 4 * 1); 1211 uint8 c = (uint8)((ch & 0x000F00) >> 4 * 2); 1212 uint8 d = (uint8)((ch & 0x00F000) >> 4 * 3); 1213 uint8 e = (uint8)((ch & 0x0F0000) >> 4 * 4); 1214 uint8 f = (uint8)((ch & 0xF00000) >> 4 * 5); 1215 1216 if (e != 0 || f != 0) { 1217 s.append (oct_to_hex (f)); 1218 s.append (oct_to_hex (e)); 1219 } 1220 1221 if (c != 0 || d != 0) { 1222 s.append (oct_to_hex (d)); 1223 s.append (oct_to_hex (c)); 1224 } 1225 1226 s.append (oct_to_hex (b)); 1227 s.append (oct_to_hex (a)); 1228 1229 return s.str; 1230 } 1231 } 1232 1233 } 1234