| /*************************************************************************** |
| * __________ __ ___. |
| * 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 <ft2build.h> |
| #include FT_FREETYPE_H |
| #include FT_GLYPH_H |
| |
| #include <stdio.h> |
| #ifdef WIN32 |
| #ifdef _MSC_VER |
| #define snprintf _snprintf |
| #endif |
| #else |
| #include <stdlib.h> |
| #include <unistd.h> |
| #endif |
| #include FT_SFNT_NAMES_H |
| #include FT_TRUETYPE_TABLES_H |
| |
| #include <string.h> |
| /* |
| * 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 |
| |
| #define ABS(x) (((x) < 0) ? -(x) : (x)) |
| |
| #define VERSION "RB12" |
| /* |
| * 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. */ |
| FT_Long max_char = 65535; |
| int pixel_size = 15; |
| FT_Long start_char = 0; |
| FT_Long limit_char; |
| FT_Long firstchar = 0; |
| FT_Long lastchar; |
| FT_Long ttc_index = -1; |
| int flg_all_ttc = 0; |
| short antialias = 1; /* smooth fonts with gray levels */ |
| int oflag = 0; |
| char outfile[1024]; |
| float between_chr = 0.0f; |
| float between_row = 0.0f; |
| int hv_resolution = 60; |
| int dump_glyphs = 0; |
| int digits_equally_wide = 1; /* Try to make digits equally wide */ |
| int trimming = 0; |
| int trim_dp = 0; /* trim descent percent */ |
| int trim_da = 0; /* trim descnet actual */ |
| int trim_ap = 0; /* trim ascent precent */ |
| int trim_aa = 0; /* trim ascent actual */ |
| int ft_load_opts = FT_LOAD_RENDER | FT_LOAD_NO_BITMAP; |
| |
| 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 ); |
| } |
| |
| static void |
| arg_panic( const char* message, const char* arg ) |
| { |
| fprintf( stderr, "%s: %s\n", message, arg ); |
| exit( 1 ); |
| } |
| |
| static int writebyte(FILE *fp, unsigned char c) |
| { |
| return putc(c, fp) != EOF; |
| } |
| |
| static int writeshort(FILE *fp, unsigned short s) |
| { |
| putc(s, fp); |
| return putc(s>>8, fp) != EOF; |
| } |
| |
| static int writeint(FILE *fp, unsigned int l) |
| { |
| putc(l, fp); |
| putc(l>>8, fp); |
| putc(l>>16, fp); |
| return putc(l>>24, fp) != EOF; |
| } |
| |
| static int writestr(FILE *fp, char *str, int count) |
| { |
| return (int)fwrite(str, 1, count, fp) == count; |
| } |
| |
| /* 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 : <font-size>-<basename>.fnt.\n" |
| " When '-ta' or '-tc' is specified in command line,\n " |
| " default output-file is: \n" |
| " <font-size>-<internal postscript-name of input-file>.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" |
| " -x Trim glyphs horizontally of nearly empty space\n" |
| " (to improve spacing on V's W's, etc.)\n" |
| " -X Set the horizontal and vertical resolution (default: 60)\n" |
| " -TA N Trim vertical ascent (N percent)\n" |
| " -TD N Trim vertical descent (N percent)\n" |
| " -Ta N Trim vertical ascent (N pixels)\n" |
| " -Td N Trim vertical descent (N pixels)\n" |
| " -r N Row separation in pixel.Insert space between characters\n" |
| " -d Debug: print converted glyph images\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 (ignores outfile option)\n" |
| " -w Don't try to make digits (0-9) equally wide\n" |
| " -L Use lighter hinting algorithm\n" |
| }; |
| fprintf(stderr, "%s", 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) { |
| #ifdef WIN32 |
| if (*p == '/' || *p == '\\') |
| #else |
| if (*p == '/') |
| #endif |
| 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 = NULL; |
| unsigned short slen = 0; |
| |
| 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 err; |
| 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 ( ( err = FT_Init_FreeType( &library ) ) != 0 ) |
| { |
| panic( "Error while initializing engine" ); |
| return err; |
| } |
| |
| |
| /* Load face */ |
| err = FT_New_Face( library, path, (FT_Long) 0, &face ); |
| if ( err ) |
| { |
| arg_panic( "Could not find/open font", path ); |
| return err; |
| } |
| |
| ttcname->ttc_count = face->num_faces; |
| ttcname->ttf_name = malloc( sizeof(char*) * ttcname->ttc_count); |
| |
| for(i = 0; i < ttcname->ttc_count; i++) |
| { |
| err = FT_New_Face( library, path, i, &face ); |
| if ( err == FT_Err_Cannot_Open_Stream ) |
| arg_panic( "Could not find/open font", path ); |
| 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 print_raw_glyph( FT_Face face) |
| { |
| int pixel,row,col,width; |
| width = face->glyph->metrics.width >> 6; |
| |
| printf("\n---Raw-Glyph---\n"); |
| for(row=0; row < face->glyph->metrics.height >> 6; row++) |
| { |
| printf("_"); |
| for(col=0; col < width; col++) |
| { |
| pixel = *(face->glyph->bitmap.buffer+width*row+col)/26; |
| if ( pixel ) printf("%d",pixel); else printf(" "); |
| } |
| printf("_\n"); |
| } |
| printf("----End-----\n"); |
| } |
| |
| int glyph_width( FT_Face face, FT_Long code, FT_Long digit_width ) |
| { |
| int width; |
| |
| if (code >= '0' && code <= '9' && digit_width) |
| { |
| width = digit_width; |
| } |
| else |
| { |
| int pitch, h_adv; |
| unsigned spacing = (unsigned)(between_chr * (1<<6));/* convert to fixed point */ |
| |
| pitch = ABS(face->glyph->bitmap.pitch); |
| h_adv = face->glyph->metrics.horiAdvance >> 6; |
| width = (face->glyph->metrics.width + spacing) >> 6; |
| |
| if(pitch == 0) pitch = h_adv; |
| if(width < pitch) width = pitch; |
| } |
| |
| return width; |
| } |
| |
| FT_Long check_digit_width( FT_Face face ) |
| { |
| FT_Long code; |
| FT_Long last_advance = -1; |
| |
| for (code='0'; code <= '9'; ++code) |
| { |
| FT_Glyph_Metrics* metrics; |
| |
| FT_Load_Char(face, code, ft_load_opts); |
| metrics = &face->glyph->metrics; |
| |
| if ((last_advance != -1 && last_advance != metrics->horiAdvance) || |
| metrics->horiBearingX < 0) |
| { |
| last_advance = 0; |
| break; |
| } |
| |
| last_advance = metrics->horiAdvance; |
| } |
| |
| return last_advance >> 6; |
| } |
| |
| void trim_glyph( FT_GlyphSlot glyph, int *empty_first_col, |
| int *empty_last_col, int *width ) |
| { |
| int row; |
| int stride = glyph->bitmap.pitch; |
| int end = stride-1; |
| int trim_left = 2, trim_right = 2; |
| |
| const unsigned char limit = 64u; |
| const unsigned char *image = glyph->bitmap.buffer; |
| |
| if (*width < 2) |
| return; /* nothing to do? */ |
| |
| for(row=0; row< glyph->metrics.height >> 6; row++) |
| { |
| const unsigned char *column = image+row*stride; |
| if (*column++ < limit && trim_left) |
| { |
| if (*column >= limit/2) |
| trim_left = 1; |
| } |
| else |
| trim_left = 0; |
| |
| column = image+row*stride+end; |
| if (*column-- < limit && trim_right) |
| { |
| if (*column >= limit/2) |
| trim_right = 1; |
| } |
| else |
| trim_right = 0; |
| } |
| |
| |
| (*width) -= trim_left + trim_right; |
| if (*width < 0) *width = 0; |
| |
| *empty_first_col = trim_left; |
| *empty_last_col = trim_right; |
| } |
| |
| void convttf(char* path, char* destfile, FT_Long face_index) |
| { |
| FT_Error err; |
| FT_Library library; |
| FT_Face face; |
| int w,h; |
| int row,col; |
| int empty_first_col, empty_last_col; |
| FT_Long charindex; |
| FT_Long idx = 0; |
| FT_Long code; |
| FT_Long digit_width = 0; |
| float extra_space; |
| FT_Long char_count; |
| char use_long_offset; |
| int done = 0; |
| char char_name[1024]; |
| int converted_char_count = 0; |
| int failed_char_count = 0; |
| |
| int depth = 2; |
| unsigned char bit_shift = 1u << depth; |
| unsigned char pixel_per_byte = CHAR_BIT / bit_shift; |
| struct font_struct export_font; |
| char pad[] = {0,0,0,0}; |
| int skip,i; |
| FILE *file; |
| |
| /* Initialize engine */ |
| if ( ( err = FT_Init_FreeType( &library ) ) != 0 ) |
| panic( "Error while initializing engine" ); |
| |
| /* Load face */ |
| err = FT_New_Face( library, path, (FT_Long) face_index, &face ); |
| if ( err == FT_Err_Cannot_Open_Stream ) |
| arg_panic( "Could not find/open font\n", path ); |
| else if ( err ) |
| arg_panic( "Error while opening font\n", path ); |
| |
| |
| setcharmap( face ); |
| /* Set font header data */ |
| |
| export_font.header.header[0] = 'R'; |
| export_font.header.header[1] = 'B'; |
| export_font.header.header[2] = '1'; |
| export_font.header.header[3] = '2'; |
| #if 0 |
| export_font.header.height = 0; |
| export_font.header.ascent = 0; |
| #endif |
| |
| extra_space = (float)(between_row-trim_aa-trim_da); |
| FT_Set_Char_Size( face, 0, pixel_size << 6, hv_resolution, hv_resolution ); |
| export_font.header.ascent = |
| ((face->size->metrics.ascender*(100-trim_ap)/100) >> 6) - trim_aa; |
| |
| export_font.header.height = |
| (((face->size->metrics.ascender*(100-trim_ap)/100) - |
| (face->size->metrics.descender*(100-trim_dp)/100)) >> 6) + extra_space; |
| |
| printf("\n"); |
| printf("Please wait, converting %s:\n", path); |
| |
| /* "face->num_glyphs" is NG.; */ |
| if ( limit_char == 0 ) limit_char = max_char; |
| if ( limit_char > max_char ) limit_char = max_char; |
| |
| char_count = 0; |
| |
| |
| |
| export_font.header.maxwidth = 1; |
| export_font.header.depth = 1; |
| firstchar = limit_char; |
| lastchar = start_char; |
| |
| if (digits_equally_wide) |
| digit_width = check_digit_width(face); |
| |
| /* calculate memory usage */ |
| for(code = start_char; code <= limit_char ; code++ ) |
| { |
| charindex = getcharindex( face, code); |
| if ( !(charindex) ) continue; |
| err = FT_Load_Glyph(face, charindex, ft_load_opts); |
| if ( err ) continue; |
| |
| w = glyph_width( face, code, digit_width ); |
| if (w == 0) continue; |
| empty_first_col = empty_last_col = 0; |
| if(trimming) |
| trim_glyph( face->glyph, &empty_first_col, &empty_last_col, &w); |
| |
| if (export_font.header.maxwidth < w) |
| export_font.header.maxwidth = w; |
| |
| |
| char_count++; |
| idx += (w*export_font.header.height + pixel_per_byte - 1)/pixel_per_byte; |
| |
| 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 = idx; |
| export_font.header.noffset = export_font.header.size; |
| export_font.header.nwidth = export_font.header.size; |
| |
| /* check if we need to use long offsets */ |
| use_long_offset = (export_font.header.nbits >= 0xFFDB ); |
| |
| /* allocate memory */ |
| export_font.offset = NULL; |
| export_font.offset_long = NULL; |
| if (use_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; |
| |
| idx = 0; |
| |
| for( code = firstchar; code <= lastchar; code++ ) |
| { |
| FT_GlyphSlot slot; |
| FT_Bitmap* source; |
| unsigned char* src; |
| unsigned char* tmpbuf; |
| int start_y; |
| |
| int glyph_height; |
| int stride; |
| unsigned char* buf; |
| unsigned char* endbuf; |
| |
| /* insert empty pixels on the left */ |
| int col_off; |
| int numbits; |
| unsigned int field; |
| |
| /* Get gylph index from the char and render it */ |
| charindex = getcharindex( face, code); |
| if ( !charindex ) |
| { |
| if ( use_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; |
| } |
| |
| err = FT_Load_Glyph(face, charindex, ft_load_opts); |
| if ( err ) { |
| continue; |
| } |
| if FT_HAS_GLYPH_NAMES( face ) |
| FT_Get_Glyph_Name( face, charindex, char_name, 16); |
| else |
| char_name[0] = '\0'; |
| |
| slot = face->glyph; |
| source = &slot->bitmap; |
| #if 0 |
| print_raw_glyph( face ); |
| #endif |
| w = glyph_width( face, code, digit_width ); |
| if (w == 0) continue; |
| empty_first_col = empty_last_col = 0; |
| |
| if(trimming) |
| trim_glyph( face->glyph, &empty_first_col, &empty_last_col, &w ); |
| |
| if ( use_long_offset ) |
| export_font.offset_long[code - firstchar] = idx; |
| else |
| export_font.offset[code - firstchar] = idx; |
| |
| export_font.width[code - firstchar] = w; |
| |
| /* copy the glyph bitmap to a full sized glyph bitmap */ |
| src = source->buffer; |
| tmpbuf = malloc(sizeof(unsigned char) * w * h); |
| memset(tmpbuf, 0xff, w*h); |
| start_y = export_font.header.ascent - slot->bitmap_top; |
| |
| glyph_height = source->rows; |
| stride = source->pitch; |
| buf = tmpbuf; |
| endbuf = tmpbuf + w*h; |
| |
| err = 0; |
| /* insert empty pixels on the left */ |
| col_off = w - stride; |
| if (col_off > 1) col_off /= 2; |
| if (col_off < 0) col_off = 0; |
| |
| for(row=0; row < glyph_height; row++) |
| { |
| if(row+start_y < 0 || row+start_y >= h) |
| continue; |
| for(col = empty_first_col; col < stride; col++) |
| { |
| unsigned char *tsrc, *dst; |
| dst = buf + (w*(start_y+row)) + col + col_off; |
| tsrc = src + stride*row + col; |
| if (dst < endbuf && dst >= tmpbuf) |
| *dst = 0xff - *tsrc; |
| else { |
| err = 1; |
| printf("Error! row: %3d col: %3d\n", row, col); |
| } |
| } |
| } |
| if(err) print_raw_glyph(face); |
| |
| buf = tmpbuf; |
| field = 0; |
| numbits = pixel_per_byte; |
| |
| for(row=0; row < h; row++) |
| { |
| for(col=0; col < w; col++) |
| { |
| unsigned int src2 = *buf++; |
| unsigned int cur_col = (src2 + 8) / 17; |
| field |= (cur_col << (bit_shift*(pixel_per_byte-numbits))); |
| |
| if (--numbits == 0) |
| { |
| export_font.chars_data[idx++] = (unsigned char)field; |
| numbits = pixel_per_byte; |
| field = 0; |
| } |
| } |
| } |
| |
| /* Pad last byte */ |
| if (numbits != pixel_per_byte) |
| { |
| export_font.chars_data[idx++] = (unsigned char)field; |
| } |
| |
| if( dump_glyphs ) |
| { |
| /* debug: dump char */ |
| unsigned char bit_max = (1 << bit_shift) - 1; |
| printf("\n---Converted Glyph Dump---\n"); |
| |
| if ( code > 32 && code < 255 ) { |
| unsigned char current_data; |
| unsigned char font_bits; |
| |
| row = h; |
| if(use_long_offset) |
| buf = &(export_font.chars_data[export_font.offset_long[ |
| code - firstchar]]); |
| else |
| buf = &(export_font.chars_data[export_font.offset[ |
| code - firstchar]]); |
| numbits = pixel_per_byte; |
| current_data = *buf; |
| do |
| { |
| col = w; |
| printf("-"); |
| do |
| { |
| font_bits = current_data & bit_max; |
| if (font_bits==bit_max) |
| printf(" "); |
| else |
| { |
| if(font_bits > bit_max/2) |
| printf("."); |
| else |
| printf("@"); |
| } |
| if (--numbits == 0) |
| { |
| current_data = *(++buf); |
| numbits = pixel_per_byte; |
| } |
| else |
| { |
| current_data >>= bit_shift; |
| } |
| } while (--col); |
| printf("-\n"); |
| } while (--row); |
| } |
| buf = NULL; |
| printf("---End Glyph Dump---\n"); |
| } |
| |
| 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); |
| } |
| |
| file = fopen(destfile, "wb"); |
| printf("Writing %s\n", destfile); |
| |
| /* font info */ |
| writestr(file, VERSION, 4); |
| writeshort(file, export_font.header.maxwidth); |
| writeshort(file, export_font.header.height); |
| writeshort(file, export_font.header.ascent); |
| writeshort(file, export_font.header.depth); |
| writeint(file, export_font.header.firstchar); |
| writeint(file, export_font.header.defaultchar); |
| writeint(file, export_font.header.size); |
| writeint(file, export_font.header.nbits); |
| writeint(file, export_font.header.noffset); |
| writeint(file, export_font.header.nwidth); |
| |
| fwrite( (char*)export_font.chars_data, 1, |
| export_font.header.nbits, file); |
| free(export_font.chars_data); |
| |
| |
| |
| if ( use_long_offset ) |
| { |
| skip = ((export_font.header.nbits + 3) & ~3) - |
| export_font.header.nbits; |
| fwrite(pad, 1, skip, file); /* pad */ |
| for(i = 0; i < export_font.header.noffset; i++) |
| writeint(file, export_font.offset_long[i]); |
| } |
| else |
| { |
| skip = ((export_font.header.nbits + 1) & ~1) - |
| export_font.header.nbits; |
| fwrite(pad, 1, skip, file); /* pad */ |
| for(i = 0; i < export_font.header.noffset; i++) |
| writeshort(file, export_font.offset[i]); |
| } |
| |
| for(i = 0; i < export_font.header.nwidth; i++) |
| writebyte(file, export_font.width[i]); |
| free(export_font.width); |
| |
| if ( use_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; |
| |
| get_ttc_table(path, &ttcname); |
| |
| if (ttcname.ttc_count == 0) |
| { |
| printf("This file is a not true type font.\n"); |
| return; |
| } |
| |
| /* default */ |
| if (!flg_all_ttc && ttc_index == -1) |
| { |
| if (!oflag) |
| { /* generate filename */ |
| snprintf(outfile, sizeof(outfile), |
| "%d-%s.fnt", pixel_size, basename(path)); |
| } |
| convttf(path, outfile, (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) |
| { |
| snprintf(outfile, sizeof(outfile), "%d-%s.fnt", |
| pixel_size, ttcname.ttf_name[ttc_index]); |
| } |
| else |
| { |
| snprintf(outfile, sizeof(outfile), "%d-%s-%ld.fnt", |
| pixel_size, basename(path), ttc_index); |
| } |
| } |
| else |
| { |
| printf("illegal face index of ttc.\n"); |
| } |
| } |
| convttf(path, outfile, ttc_index); |
| } |
| else { /* convert all fonts */ |
| print_ttc_table(path); |
| for(i = 0; i < ttcname.ttc_count; i++) |
| { |
| snprintf(outfile, sizeof(outfile), "%d-%s.fnt", |
| pixel_size, ttcname.ttf_name[i]); |
| convttf(path, outfile, 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': /* 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 = atol(p); |
| while (*p && *p != ' ') |
| p++; |
| } |
| else { |
| av++; ac--; |
| if (ac > 0) |
| start_char = atol(av[0]); |
| } |
| 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; |
| case 'c': /* set spaece between characters */ |
| { |
| if (*p) { |
| between_chr = atof(p); |
| while (*p && *p != ' ') |
| p++; |
| } |
| else { |
| av++; ac--; |
| if (ac > 0) |
| between_chr = atof(av[0]); |
| } |
| break; |
| } |
| case 'd': |
| dump_glyphs = 1; |
| while (*p && *p != ' ') |
| p++; |
| break; |
| case 'x': |
| trimming = 1; |
| while (*p && *p != ' ') |
| p++; |
| break; |
| case 'X': |
| if (*p) { |
| hv_resolution = atoi(p); |
| while (*p && *p != ' ') |
| p++; |
| } |
| else { |
| av++; ac--; |
| if (ac > 0) |
| hv_resolution = atoi(av[0]); |
| } |
| break; |
| case 'r': |
| if (*p) { |
| between_row = atof(p); |
| while (*p && *p != ' ') |
| p++; |
| } |
| else { |
| av++; ac--; |
| if (ac > 0) |
| between_row = atof(av[0]); |
| } |
| break; |
| case 'T': |
| if(*p == 'A') { |
| if(*(++p)) { |
| trim_ap = atoi(p); |
| while (*p && *p != ' ') |
| p++; |
| } |
| else { |
| av++; ac--; |
| if (ac > 0) |
| trim_ap = atoi(av[0]); |
| } |
| break; |
| } |
| if(*p == 'D') { |
| if(*(++p)) { |
| trim_dp = atoi(p); |
| while (*p && *p != ' ') |
| p++; |
| } |
| else { |
| av++; ac--; |
| if (ac > 0) |
| trim_dp = atoi(av[0]); |
| } |
| break; |
| } |
| if(*p == 'a') { |
| if(*(++p)) { |
| trim_aa = atoi(p); |
| while (*p && *p != ' ') |
| p++; |
| } |
| else { |
| av++; ac--; |
| if (ac > 0) |
| trim_aa = atoi(av[0]); |
| } |
| break; |
| } |
| if(*p == 'd') { |
| if(*(++p)) { |
| trim_da = atoi(p); |
| |
| } |
| else { |
| av++; ac--; |
| if (ac > 0) |
| trim_da = atoi(av[0]); |
| } |
| break; |
| } |
| fprintf(stderr, "Unknown option ignored: %s\n", p-1); |
| while (*p && *p != ' ') |
| p++; |
| break; |
| case 't': /* display ttc table */ |
| if (*p == 't') { |
| pct = 1; |
| while (*p && *p != ' ') |
| p++; |
| } |
| |
| else if (*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; |
| case 'w': /* Don't try to make digits equally wide */ |
| digits_equally_wide = 0; |
| while (*p && *p != ' ') |
| p++; |
| break; |
| case 'L': /* Light rendering algorithm */ |
| ft_load_opts |= FT_LOAD_TARGET_LIGHT; |
| break; |
| |
| default: |
| fprintf(stderr, "Unknown option ignored: %s\n", p-1); |
| while (*p && *p != ' ') |
| p++; |
| } |
| ++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; |
| } |