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