/*************************************************************************** * __________ __ ___. * 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. * ****************************************************************************/ #include #include FT_FREETYPE_H #include FT_GLYPH_H #include #include #include int pixel_size = 15; unsigned short start_char = 0; unsigned short limit_char = 0; int depth = 2; short antialias = 1; /* smooth fonts with gray levels */ int oflag = 0; char outfile[1024]; 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 */ unsigned long noffset; /* # longs offset data in file */ unsigned long nwidth; /* # bytes width data in file */ }; struct font_struct { struct font_header_struct header; unsigned char *chars_data; unsigned short *offset; unsigned long *offset_long; unsigned char *width; }; /* 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" "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" " -d N Depth of bitmap font in 2^N bpp where 0<=N<=3 (default N=2)\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 convttf(char* path) { FT_Error error; FT_Library library; FT_Face face; int w,h,w_half; char w_is_odd; int row,col; unsigned long index = 0; unsigned long c; unsigned char bit_shift = 1 << depth; unsigned char pixel_per_byte = 8 >> depth; /*unsigned char bit_max = (1 << bit_shift) - 1;*/ if (!oflag) { strcpy(outfile, basename(path)); char ext_str[20]; sprintf(ext_str, "_%d_[%dbpp].fnt", pixel_size,bit_shift); strcat(outfile, ext_str); } /* Initialize engine */ if ( ( error = FT_Init_FreeType( &library ) ) != 0 ) panic( "Error while initializing engine" ); /* Load face */ error = FT_New_Face( library, path, 0, &face ); if ( error == FT_Err_Cannot_Open_Stream ) panic( "Could not find/open font resource" ); else if ( error ) panic( "Error while opening font resource" ); /* 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 = face->num_glyphs; if ( limit_char > face->num_glyphs ) limit_char = face->num_glyphs; unsigned long char_count = limit_char-start_char+1; /* 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; export_font.header.ascent = face->size->metrics.ascender >> 6; export_font.header.depth = depth; export_font.header.firstchar = start_char; export_font.header.defaultchar = start_char; export_font.header.size = char_count; /* calculate memory usage */ for(c = start_char; c <= limit_char; c++ ) { error = FT_Load_Glyph( face, FT_Get_Char_Index( face, c), FT_LOAD_DEFAULT ); if ( error ) continue; w = face->glyph->metrics.horiAdvance >> 6; 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++; } export_font.header.nbits = index; export_font.header.noffset = char_count; export_font.header.nwidth = char_count; /* 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(unsigned 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( c = start_char; c <= limit_char; c++ ) { /* Get gylph index from the char and render it */ int glyph_index = FT_Get_Char_Index(face, c); error = FT_Load_Char( face, c , FT_LOAD_RENDER ); FT_Get_Glyph_Name( face, glyph_index, char_name, 16); if ( error ) { printf("Error loading glyph: %s\n", char_name); failed_char_count++; continue; } FT_GlyphSlot slot = face->glyph; FT_Glyph_Metrics glyph_metrics = slot->metrics; FT_Bitmap* source = &slot->bitmap; w = glyph_metrics.horiAdvance >> 6; w_is_odd = ( w % 2 == 1 ); w_half = w_is_odd ? (w-1) >> 1 : w >> 1; if ( long_offset ) export_font.offset_long[c-start_char] = index; else export_font.offset[c-start_char] = index; export_font.width[c-start_char] = 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; unsigned char* endbuf = tmpbuf + w*h; if ( start_y > 0 ) { buf += w*start_y; } if ( w == 0 ) { free(tmpbuf); continue; } int start_x = glyph_metrics.horiBearingX >> 6; for(row=0; row < glyph_height; row++) { if ( row + start_y < 0 || row > h ) { src += glyph_width; } else { buf += start_x; for(col=0; col < glyph_width; col++) { if ( col + start_x < 0 ) { buf++; src++; } else if ( col <= w && buf < endbuf && buf >= tmpbuf ) (*buf++)=255-(*src++); else src++; } buf += w-glyph_width-start_x; } } 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); #if 0 /* we need to find some nice thresholds for 2-bit fonts => those thresholds probably need to implemented in the renderer too */ if ( depth == 99 ) { if ( *buf < 64 ) cur_col = 0; else if ( *buf < 119 ) cur_col = 1; else if ( *buf < 187 ) cur_col = 2; else cur_col = 3; } #endif 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 ( c > 90 && c < 100 ) { row = h; buf = &(export_font.chars_data[export_font.offset_long[c-start_char]]); 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); } #endif free(tmpbuf); converted_char_count++; done = (100*(c-start_char))/char_count; printf("Converted %s %ld (%d%%)\e[K\r",char_name,c,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(unsigned long)*export_font.header.noffset, file ); free(export_font.offset_long); } 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 ); free(export_font.offset); } n = fwrite((char*)export_font.width, 1, export_font.header.nwidth, file); free(export_font.width); fclose(file); FT_Done_Face( face ); FT_Done_FreeType( library ); printf("done (converted %d glyphs, %d errors).\e[K\n",converted_char_count, failed_char_count); } /* parse command line options*/ void getopts(int *pac, char ***pav) { char *p; char **av; int ac; ac = *pac; av = *pav; while (ac > 0 && av[0][0] == '-') { p = &av[0][1]; while( *p) switch(*p++) { 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': /* 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': /* 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': /* set encoding start*/ if (*p) { start_char = atoi(p); while (*p && *p != ' ') p++; } else { av++; ac--; if (ac > 0) start_char = atoi(av[0]); } break; case 'd': /* set depth*/ if (*p) { depth = atoi(p); while (*p && *p != ' ') p++; } else { av++; ac--; if (ac > 0) depth = atoi(av[0]); } if ( depth < 0 || depth > 3 ) usage(); break; 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; 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(); } } while (ac > 0) { convttf(av[0]); ++av; --ac; } exit(ret); }