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