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