/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2007 Jonas Hurrelmann * * A command-line tool to convert ttf file to bitmap fonts * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #define BITMAP_WORDS(x) (((x)+15)/16) /* image size in words*/ #include #include FT_FREETYPE_H #include FT_GLYPH_H #include #ifdef WIN32 #include #else #include #include #endif #include FT_SFNT_NAMES_H #include FT_TRUETYPE_TABLES_H #include /* * Set the default values used to generate a BDF font. */ #ifndef DEFAULT_PLATFORM_ID #define DEFAULT_PLATFORM_ID 3 #endif #ifndef DEFAULT_ENCODING_ID #define DEFAULT_ENCODING_ID 1 #endif /* * nameID macros for getting strings from the OT font. */ enum { BDFOTF_COPYRIGHT_STRING = 0, BDFOTF_FAMILY_STRING, BDFOTF_SUBFAMILY_STRING, BDFOTF_UNIQUEID_STRING, BDFOTF_FULLNAME_STRING, BDFOTF_VENDOR_STRING, BDFOTF_POSTSCRIPT_STRING, BDFOTF_TRADEMARK_STRING, }; /* * String names for the string indexes. Used for error messages. */ static char *string_names[] = { "\"Copyright\"", "\"Family\"", "\"SubFamily\"", "\"Unique ID\"", "\"Full Name\"", "\"Vendor\"", "\"Postscript Name\"", "\"Trademark\"" }; /* * The default platform and encoding ID's. */ static int pid = DEFAULT_PLATFORM_ID; static int eid = DEFAULT_ENCODING_ID; /* * A flag indicating if a CMap was found or not. */ static FT_UShort nocmap; int pct = 0; /* display ttc table if it is not zero. */ unsigned long max_char = 65535; int pixel_size = 15; unsigned long start_char = 0; unsigned long limit_char; unsigned long firstchar = 0; unsigned long lastchar; FT_Long ttc_index = -1; int flg_all_ttc = 0; const int depth = 2; short antialias = 1; /* smooth fonts with gray levels */ int oflag = 0; char outfile[1024]; int between_chr = 0; int between_row = 0; struct font_header_struct { char header[4]; /* magic number and version bytes */ unsigned short maxwidth; /* max width in pixels */ unsigned short height; /* height in pixels */ unsigned short ascent; /* ascent (baseline) height */ unsigned short depth; /* depth 0=1-bit, 1=4-bit */ unsigned long firstchar; /* first character in font */ unsigned long defaultchar; /* default character in font */ unsigned long size; /* # characters in font */ unsigned long nbits; /* # bytes imagebits data in file */ /* = bits_size */ FT_Long noffset; /* # longs offset data in file */ FT_Long nwidth; /* # bytes width data in file */ }; struct font_struct { struct font_header_struct header; unsigned char *chars_data; unsigned short *offset; FT_Long *offset_long; unsigned char *width; }; struct ttc_table{ FT_Long ttc_count; char **ttf_name; }; /* exit the program with given message */ static void panic( const char* message ) { fprintf( stderr, "%s\n", message ); exit( 1 ); } /* print usage information */ void usage(void) { char help[] = { "Usage: convttf [options] [input-files]\n" " convttf [options] [-o output-file] [single-input-file]\n\n" " Default output-file : 'input-files name removed extension'_'font-size'_['depth'].fnt.\n" " When '-ta' or '-tc' is specified in command line,\n " " default output-file is \n" " 'internal postscript-name of input-file'_'font-size'_['depth'].fnt.\n" "Options:\n" " -s N Start output at character encodings >= N\n" " -l N Limit output to character encodings <= N\n" " -p N Font size N in pixel (default N=15)\n" " -c N Character separation in pixel.Insert space between lines.\n" " -r N Row separation in pixel.Insert space between characters\n" " -tt Display the True Type Collection tables available in the font\n" " -t N Index of true type collection. It must be start from 0.(default N=0).\n" " -ta Convert all fonts in ttc\n" " \"-o output-file\" specified is ignored when \"-ta\" is specified.\n" }; fprintf(stderr, help); exit( 1 ); } /* remove directory prefix and file suffix from full path*/ char *basename(char *path) { char *p, *b; static char base[256]; /* remove prepended path and extension*/ b = path; for (p=path; *p; ++p) { if (*p == '/') b = p + 1; } strcpy(base, b); for (p=base; *p; ++p) { if (*p == '.') { *p = 0; break; } } return base; } void setcharmap(FT_Face face) { FT_Long i; /* * Get the requested cmap. */ for (i = 0; i < face->num_charmaps; i++) { if (face->charmaps[i]->platform_id == pid && face->charmaps[i]->encoding_id == eid) break; } if (i == face->num_charmaps && pid == 3 && eid == 1) { /* * Make a special case when this fails with pid == 3 and eid == 1. * Change to eid == 0 and try again. This captures the two possible * cases for MS fonts. Some other method should be used to cycle * through all the alternatives later. */ for (i = 0; i < face->num_charmaps; i++) { if (face->charmaps[i]->platform_id == pid && face->charmaps[i]->encoding_id == 0) break; } if (i < face->num_charmaps) { pid = 3; eid = 1; FT_Set_Charmap(face, face->charmaps[i]); } else { /* * No CMAP was found. */ nocmap = 1; pid = eid = -1; } } else { FT_Set_Charmap(face, face->charmaps[i]); nocmap = 0; } } /* * quote in otf2bdf. * A generic routine to get a name from the OT name table. This routine * always looks for English language names and checks three possibilities: * 1. English names with the MS Unicode encoding ID. * 2. English names with the MS unknown encoding ID. * 3. English names with the Apple Unicode encoding ID. * * The particular name ID mut be provided (e.g. nameID = 0 for copyright * string, nameID = 6 for Postscript name, nameID = 1 for typeface name. * * If the `dash_to_space' flag is non-zero, all dashes (-) in the name will be * replaced with the character passed. * * Returns the number of bytes added. */ static int otf_get_english_string(FT_Face face, int nameID, int dash_to_space, char *name, int name_size) { int j, encid; FT_UInt i, nrec; FT_SfntName sfntName; unsigned char *s; unsigned short slen; nrec = FT_Get_Sfnt_Name_Count(face); for (encid = 1, j = 0; j < 2; j++, encid--) { /* * Locate one of the MS English font names. */ for (i = 0; i < nrec; i++) { FT_Get_Sfnt_Name(face, i, &sfntName); if (sfntName.platform_id == 3 && sfntName.encoding_id == encid && sfntName.name_id == nameID && (sfntName.language_id == 0x0409 || sfntName.language_id == 0x0809 || sfntName.language_id == 0x0c09 || sfntName.language_id == 0x1009 || sfntName.language_id == 0x1409 || sfntName.language_id == 0x1809)) { s = sfntName.string; slen = sfntName.string_len; break; } } if (i < nrec) { if (slen >> 1 >= name_size) { fprintf(stderr, "warning: %s string longer than buffer. Truncating to %d bytes.\n", string_names[nameID], name_size); slen = name_size << 1; } /* * Found one of the MS English font names. The name is by * definition encoded in Unicode, so copy every second byte into * the `name' parameter, assuming there is enough space. */ for (i = 1; i < slen; i += 2) { if (dash_to_space) *name++ = (s[i] != '-') ? s[i] : ' '; else if (s[i] == '\r' || s[i] == '\n') { if (s[i] == '\r' && i + 2 < slen && s[i + 2] == '\n') i += 2; *name++ = ' '; *name++ = ' '; } else *name++ = s[i]; } *name = 0; return (slen >> 1); } } /* * No MS English name found, attempt to find an Apple Unicode English * name. */ for (i = 0; i < nrec; i++) { FT_Get_Sfnt_Name(face, i, &sfntName); if (sfntName.platform_id == 0 && sfntName.language_id == 0 && sfntName.name_id == nameID) { s = sfntName.string; slen = sfntName.string_len; break; } } if (i < nrec) { if (slen >> 1 >= name_size) { fprintf(stderr, "warning: %s string longer than buffer. Truncating to %d bytes.\n", string_names[nameID], name_size); slen = name_size << 1; } /* * Found the Apple Unicode English name. The name is by definition * encoded in Unicode, so copy every second byte into the `name' * parameter, assuming there is enough space. */ for (i = 1; i < slen; i += 2) { if (dash_to_space) *name++ = (s[i] != '-') ? s[i] : ' '; else if (s[i] == '\r' || s[i] == '\n') { if (s[i] == '\r' && i + 2 < slen && s[i + 2] == '\n') i += 2; *name++ = ' '; *name++ = ' '; } else *name++ = s[i]; } *name = 0; return (slen >> 1); } return 0; } int get_ttc_table(char *path, struct ttc_table *ttcname ) { FT_Error error; FT_Library library; FT_Face face; FT_Long i; char xlfd[BUFSIZ]; /* init number of ttf in ttc */ ttcname->ttc_count = 0; /* Initialize engine */ if ( ( error = FT_Init_FreeType( &library ) ) != 0 ) { panic( "Error while initializing engine" ); return error; } /* Load face */ error = FT_New_Face( library, path, (FT_Long) 0, &face ); if ( error == FT_Err_Cannot_Open_Stream ) { panic( "Could not find/open font resource" ); return error; } ttcname->ttc_count = face->num_faces; ttcname->ttf_name = malloc( sizeof(char*) * ttcname->ttc_count); for(i = 0; i < ttcname->ttc_count; i++) { error = FT_New_Face( library, path, i, &face ); if ( error == FT_Err_Cannot_Open_Stream ) panic( "Could not find/open font resource\n" ); otf_get_english_string(face, BDFOTF_POSTSCRIPT_STRING, 0, xlfd, sizeof(xlfd)); ttcname->ttf_name[i] = malloc(sizeof(char) * (strlen(xlfd) + 1 )); strcpy(ttcname->ttf_name[i], xlfd); } return 0; } void print_ttc_table(char* path) { struct ttc_table ttcname; FT_Long i; get_ttc_table(path, &ttcname); printf("ttc header count = %ld \n\n", ttcname.ttc_count); printf("Encoding tables available in the true type collection\n\n"); printf("INDEX\tPOSTSCRIPT NAME\n"); printf("-----------------------------------------------------\n"); for(i = 0; i < ttcname.ttc_count; i++) { printf("%ld\t%s\n", i, ttcname.ttf_name[i]); } for(i = 0; i < ttcname.ttc_count; i++) { free(ttcname.ttf_name[i]); } printf("\n\n"); free(ttcname.ttf_name); return; } FT_Long getcharindex(FT_Face face, FT_Long code) { FT_Long idx; if (nocmap) { if (code >= face->num_glyphs) idx = 0; else idx = code; } else idx = FT_Get_Char_Index( face, code); if ( idx <= 0 || idx > face->num_glyphs) return 0; else return idx; } void convttf(char* path, FT_Long face_index) { FT_Error error; FT_Library library; FT_Face face; int w,w1,h,w_half; char w_is_odd; int row,col; FT_Long charindex; FT_Long index = 0; FT_Long code; unsigned char bit_shift = 1 << depth; unsigned char pixel_per_byte = 8 >> depth; /* Initialize engine */ if ( ( error = FT_Init_FreeType( &library ) ) != 0 ) panic( "Error while initializing engine" ); /* Load face */ error = FT_New_Face( library, path, (FT_Long) face_index, &face ); if ( error == FT_Err_Cannot_Open_Stream ) panic( "Could not find/open font resource" ); else if ( error ) panic( "Error while opening font resource" ); setcharmap( face ); /* create size */ error = FT_Set_Pixel_Sizes( face, pixel_size, pixel_size ); if ( error ) panic( "Could not reset instance" ); printf("Please wait, converting %s to %s:\n",path,outfile); if ( limit_char == 0 ) limit_char = max_char; /* "face->num_glyphs" is NG.; */ if ( limit_char > max_char ) limit_char = max_char; FT_Long char_count = 0; /* Set font header data */ struct font_struct export_font; export_font.header.header[0] = 'R'; export_font.header.header[1] = 'B'; export_font.header.header[2] = '1'; export_font.header.header[3] = '2'; export_font.header.maxwidth = face->size->metrics.max_advance >> 6; export_font.header.height = ((face->size->metrics.ascender - face->size->metrics.descender + (2 << 6)) >> 6) + between_row; export_font.header.ascent = face->size->metrics.ascender >> 6; export_font.header.depth = depth; firstchar = limit_char; lastchar = start_char; /* calculate memory usage */ for(code = start_char; code <= limit_char ; code++ ) { charindex = getcharindex( face, code); if ( !(charindex) ) continue; error = FT_Load_Glyph( face, charindex, (FT_LOAD_RENDER | FT_LOAD_NO_BITMAP) ); if ( error ) continue; w = face->glyph->bitmap.pitch; if (w == 0) w = face->glyph->metrics.horiAdvance >> 6; w1 = (face->glyph->metrics.width) >> 6; if(w < w1) w = w1; if (w == 0) continue; int start_x = face->glyph->metrics.horiBearingX >> 6; w += abs(start_x); w += between_chr; if (export_font.header.maxwidth < face->glyph->metrics.width >>6) export_font.header.maxwidth = face->glyph->metrics.width >>6; char_count++; h = export_font.header.height; int numbits = pixel_per_byte; /* we could use a closed form here */ for(row=0; row < h; row++) { for(col=0; col < w; col++) { if (--numbits == 0) { numbits = pixel_per_byte; index++; } } } if (numbits != pixel_per_byte) index++; if (code >= lastchar) lastchar = code; if (code <= firstchar) firstchar = code; } export_font.header.defaultchar = firstchar; export_font.header.firstchar = firstchar; export_font.header.size = lastchar - firstchar + 1; export_font.header.nbits = index; export_font.header.noffset = export_font.header.size; export_font.header.nwidth = export_font.header.size; /* check if we need to use long offsets */ char long_offset = (export_font.header.nbits >= 0xFFDB ); /* allocate memory */ if (long_offset) export_font.offset_long = malloc( sizeof(FT_Long)* export_font.header.noffset ); else export_font.offset = malloc( sizeof(unsigned short)* export_font.header.noffset ); export_font.width = malloc( sizeof(unsigned char) * export_font.header.nwidth ); export_font.chars_data = malloc( sizeof(unsigned char) * export_font.header.nbits ); /* for now we use the full height for each character */ h = export_font.header.height; index = 0; int done = 0; char char_name[1024]; int converted_char_count = 0; int failed_char_count = 0; for( code = firstchar; code <= lastchar; code++ ) { /* Get gylph index from the char and render it */ charindex = getcharindex( face, code); if ( !charindex ) { if ( long_offset ) export_font.offset_long[code - firstchar] = export_font.offset_long[0]; else export_font.offset[code - firstchar] = export_font.offset[0]; export_font.width[code - firstchar] = export_font.width[0]; continue; } error = FT_Load_Glyph( face, charindex , (FT_LOAD_RENDER | FT_LOAD_NO_BITMAP) ); if ( error ) { continue; } if FT_HAS_GLYPH_NAMES( face ) FT_Get_Glyph_Name( face, charindex, char_name, 16); else char_name[0] = '\0'; FT_GlyphSlot slot = face->glyph; FT_Glyph_Metrics glyph_metrics = slot->metrics; FT_Bitmap* source = &slot->bitmap; w = face->glyph->bitmap.pitch; if (w == 0) w = face->glyph->metrics.horiAdvance >> 6; w1 = (face->glyph->metrics.width) >> 6; if(w < w1) w = w1; if (w == 0) continue; int start_x = glyph_metrics.horiBearingX >> 6; w += abs(start_x); w += between_chr; w_is_odd = ( w % 2 == 1 ); w_half = w_is_odd ? (w - 1) >> 1 : w >> 1; if ( long_offset ) export_font.offset_long[code - firstchar] = index; else export_font.offset[code - firstchar] = index; export_font.width[code - firstchar] = w; /* copy the glyph bitmap to a full sized glyph bitmap */ unsigned char* src = source->buffer; unsigned char* tmpbuf = malloc(sizeof(unsigned char) * w * h); memset(tmpbuf, 0xff, w*h); int start_y = export_font.header.ascent - slot->bitmap_top; int glyph_height = glyph_metrics.height >> 6; int glyph_width = glyph_metrics.width >>6; unsigned char* buf = tmpbuf; if ( start_y > 0 ) buf += w * start_y; unsigned char* endbuf = tmpbuf + w*h; for(row=0; row < glyph_height; row++) { if ( row + start_y < 0 || row > h ) { src += glyph_width; } else { if (start_x > 0) buf += start_x; for(col=0; col < glyph_width; col++) { /* if ( col + start_x < 0 ) { buf++; src++; } */ if ( col <= w && buf < endbuf && buf >= tmpbuf ) (*buf++)= 0xff - ( *src++ ); else src++; } if (start_x < 0 && col <= w) buf += w - col; /* almost same as start_x = w - col */ } } buf = tmpbuf; int numbits; unsigned int field; field = 0; numbits = pixel_per_byte; for(row=0; row < h; row++) { for(col=0; col < w; col++) { /* TODO: find a more sophisticated dithering than just using the upper bits */ unsigned int cur_col = *(buf) >> (8-bit_shift); buf++; field = field | (cur_col << (bit_shift*(pixel_per_byte-numbits))); if (--numbits == 0) { export_font.chars_data[index++] = (unsigned char)field; numbits = pixel_per_byte; field = 0; } } } /* Pad last byte */ if (numbits != pixel_per_byte) { export_font.chars_data[index++] = (unsigned char)field; } #if 0 /* debug: dump char */ unsigned char bit_max = (1 << bit_shift) - 1; if ( code > 32 && code < 255 ) { row = h; buf = &(export_font.chars_data[export_font.offset_long[code - firstchar]]); unsigned char current_data; unsigned char font_bits; numbits = pixel_per_byte; current_data = *buf; do { col = w; do { font_bits = current_data & bit_max; if (font_bits==bit_max) printf(" "); else printf("%x",font_bits); if (--numbits == 0) { current_data = *(++buf); numbits = pixel_per_byte; } else { current_data >>= bit_shift; } } while (--col); printf("\n"); } while (--row); } buf = NULL; #endif free(tmpbuf); converted_char_count++; done = (100*(converted_char_count))/char_count; printf("Converted %s %d (%d%%)\e[K\r",char_name,converted_char_count,done); fflush(stdout); } int n; FILE *file = fopen(outfile, "w"); n = fwrite(&export_font.header, 1, sizeof(struct font_header_struct), file); n = fwrite( (char*)export_font.chars_data, 1, export_font.header.nbits, file); free(export_font.chars_data); int skip; char pad[] = {0,0,0,0}; if ( long_offset ) { skip = ((export_font.header.nbits + 3) & ~3) - export_font.header.nbits; n = fwrite(pad, 1, skip, file); /* pad */ n = fwrite( (char*)export_font.offset_long, 1, sizeof(FT_Long) * export_font.header.noffset, file ); } else { skip = ((export_font.header.nbits + 1) & ~1) - export_font.header.nbits; n = fwrite(pad, 1, skip, file); /* pad */ n = fwrite( (char*)export_font.offset, 1, sizeof(unsigned short)*export_font.header.noffset, file ); } n = fwrite((char*)export_font.width, 1, export_font.header.nwidth, file); free(export_font.width); if ( long_offset ) free(export_font.offset_long); else free(export_font.offset); fclose(file); FT_Done_Face( face ); FT_Done_FreeType( library ); printf("done (converted %d glyphs, %d errors).\e[K\n\n",converted_char_count, failed_char_count); } void convttc(char* path) { struct ttc_table ttcname; FT_Long i; char ext_str[20]; get_ttc_table(path, &ttcname); if (ttcname.ttc_count == 0) { printf("This file is a not true type font.\n"); return; } /* defalut */ if (!flg_all_ttc && ttc_index == -1) { if (!oflag) { strcpy(outfile, basename(path)); sprintf(ext_str, "_%d.fnt", pixel_size); strcat(outfile, ext_str); } convttf(path, (FT_Long) 0); } /* set face_index of ttc */ else if (!flg_all_ttc) { print_ttc_table(path); if ( !oflag ) { if (ttc_index >= 0 && ttc_index < ttcname.ttc_count) { if (strcmp(ttcname.ttf_name[ttc_index], "") != 0) { strcpy(outfile, ttcname.ttf_name[ttc_index]); sprintf(ext_str, "_%d.fnt", pixel_size); } else { strcpy(outfile, basename(path)); sprintf(ext_str, "%ld_%d.fnt", ttc_index,pixel_size); } strcat(outfile, ext_str); } else { printf("illegal face index of ttc.\n"); } } convttf(path, ttc_index); } else { /* convert all fonts */ print_ttc_table(path); for(i = 0; i < ttcname.ttc_count; i++) { strcpy(outfile, ttcname.ttf_name[i]); sprintf(ext_str, "_%d.fnt", pixel_size); strcat(outfile, ext_str); convttf(path, i); } } for(i = 0; i < ttcname.ttc_count; i++) { free(ttcname.ttf_name[i]); } free(ttcname.ttf_name); } /* parse command line options*/ void getopts(int *pac, char ***pav) { char *p; char **av; int ac; ac = *pac; av = *pav; limit_char = max_char; start_char = 0; while (ac > 0 && av[0][0] == '-') { p = &av[0][1]; while( *p) switch(*p++) { case 'h':case 'H': usage(); break; case ' ': /* multiple -args on av[]*/ while( *p && *p == ' ') p++; if( *p++ != '-') /* next option must have dash*/ p = ""; break; /* proceed to next option*/ case 'o':case 'O': /* set output file*/ oflag = 1; if (*p) { strcpy(outfile, p); while (*p && *p != ' ') p++; } else { av++; ac--; if (ac > 0) strcpy(outfile, av[0]); } break; case 'l':case 'L': /* set encoding limit*/ if (*p) { limit_char = atoi(p); while (*p && *p != ' ') p++; } else { av++; ac--; if (ac > 0) limit_char = atoi(av[0]); } break; case 's':case 'S': /* set encoding start*/ if (*p) { start_char = atol(p); while (*p && *p != ' ') p++; } else { av++; ac--; if (ac > 0) start_char = atol(av[0]); } break; case 'p':case 'P': /* set pixel size*/ if (*p) { pixel_size = atoi(p); while (*p && *p != ' ') p++; } else { av++; ac--; if (ac > 0) pixel_size = atoi(av[0]); } break; case 'c':case 'C': /* set spaece between characters */ if (*p) { between_chr = atoi(p); while (*p && *p != ' ') p++; } else { av++; ac--; if (ac > 0) between_chr = atoi(av[0]); } break; case 'r':case 'R': /* set spaece between rows */ if (*p) { between_row = atoi(p); while (*p && *p != ' ') p++; } else { av++; ac--; if (ac > 0) between_row = atoi(av[0]); } break; case 't':case 'T': /* display ttc table */ if (*p == 't' || *p == 'T') { pct = 1; while (*p && *p != ' ') p++; } else if (*p == 'a' || *p == 'A') { flg_all_ttc = 1; while (*p && *p != ' ') p++; } else if (*p) { ttc_index = atoi(p); while (*p && *p != ' ') p++; } else { av++; ac--; if (ac > 0) ttc_index = atoi(av[0]); } break; default: fprintf(stderr, "Unknown option ignored: %c\r\n", *(p-1)); } ++av; --ac; } *pac = ac; *pav = av; } int main(int ac, char **av) { int ret = 0; ++av; --ac; /* skip av[0]*/ getopts(&ac, &av); /* read command line options*/ if (ac < 1) { usage(); } if (oflag) { if (ac > 1) { usage(); } } if (limit_char < start_char) { usage(); exit(0); } while (pct && ac > 0) { print_ttc_table(av[0]); ++av; --ac; exit(0); } while (ac > 0) { convttc(av[0]); ++av; --ac; } exit(ret); } /* * Trie node structure. */ typedef struct { unsigned short key; /* Key value. */ unsigned short val; /* Data for the key. */ unsigned long sibs; /* Offset of siblings from trie beginning. */ unsigned long kids; /* Offset of children from trie beginning. */ } node_t; /* * The trie used for remapping codes. */ static node_t *nodes; static unsigned long nodes_used = 0; int otf2bdf_remap(unsigned short *code) { unsigned long i, n, t; unsigned short c, codes[2]; /* * If no mapping table was loaded, then simply return the code. */ if (nodes_used == 0) return 1; c = *code; codes[0] = (c >> 8) & 0xff; codes[1] = c & 0xff; for (i = n = 0; i < 2; i++) { t = nodes[n].kids; if (t == 0) return 0; for (; nodes[t].sibs && nodes[t].key != codes[i]; t = nodes[t].sibs); if (nodes[t].key != codes[i]) return 0; n = t; } *code = nodes[n].val; return 1; }