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