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