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