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