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