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