| /****************************************************************************/ |
| /* */ |
| /* t1dump.c 1.0 */ |
| /* */ |
| /* Copyright 1999 - The FreeType Project http://www.freetype.org */ |
| /* */ |
| /* T1Dump is a very simply Type 1 font dumper. It can be used to */ |
| /* write the following information to the standard ouput, or any */ |
| /* given file: */ |
| /* */ |
| /* - a description of the font file (including name, properties, etc..) */ |
| /* - the decrypted private dictionary, 'as is', i.e. in binary form */ |
| /* - the stream of tokens from the font file (useful to debug the */ |
| /* Type1 driver or to look at the font's internal structure..) */ |
| /* - the charstring commands of a given subroutine */ |
| /* - the charstring commands of a given glyph */ |
| /* - the encoding */ |
| /* - the glyph names */ |
| /* */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include "freetype.h" |
| #include <t1tokens.h> |
| #include <t1gload.h> |
| #include <t1load.h> |
| #include <t1parse.h> |
| |
| FT_Library library; /* root library object */ |
| FT_Face face; /* truetype face */ |
| T1_Face t1_face; |
| FT_Error error; |
| FILE* target; |
| |
| void Panic( const char* message ) |
| { |
| fprintf( stderr, "%s\n error code = 0x%04x\n", message, error ); |
| exit(1); |
| } |
| |
| |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /********** *********/ |
| /********** *********/ |
| /********** DUMP FONT INFORMATION *********/ |
| /********** *********/ |
| /********** *********/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| |
| |
| static |
| T1_Error Dump_Font_Info( void ) |
| { |
| T1_FontInfo* info = &t1_face->font_info; |
| T1_Private* priv = &t1_face->private_dict; |
| T1_Int n; |
| |
| fprintf( target, "Font Name : %s\n", t1_face->font_name ); |
| |
| fprintf( target, "Version : %s\n", info->version ); |
| fprintf( target, "Full Name : %s\n", info->full_name ); |
| fprintf( target, "Family : %s\n", info->family_name ); |
| fprintf( target, "Weight : %s\n", info->weight ); |
| fprintf( target, "Italic angle : %ld\n", info->italic_angle ); |
| |
| fprintf( target, "Fixed pitch : %s\n", |
| info->is_fixed_pitch ? "yes" : "no" ); |
| |
| fprintf( target, "Underline : pos %d, thickness %d\n", |
| info->underline_position, |
| info->underline_thickness ); |
| |
| fprintf( target, "Unique ID : %d\n", priv->unique_id ); |
| fprintf( target, "lenIV : %d\n", priv->lenIV ); |
| |
| fprintf( target, "blues : [" ); |
| for ( n = 0; n < priv->num_blues; n++ ) |
| fprintf( target, " %d", priv->blue_values[n] ); |
| fprintf( target, " ]\n" ); |
| |
| fprintf( target, "other blues : [" ); |
| for ( n = 0; n < priv->num_other_blues; n++ ) |
| fprintf( target, " %d", priv->other_blues[n] ); |
| fprintf( target, " ]\n" ); |
| |
| fprintf( target, "family blues : [" ); |
| for ( n = 0; n < priv->num_family_blues; n++ ) |
| fprintf( target, " %d", priv->family_blues[n] ); |
| fprintf( target, " ]\n" ); |
| |
| fprintf( target, "family other : [" ); |
| for ( n = 0; n < priv->num_family_other_blues; n++ ) |
| fprintf( target, " %d", priv->family_other_blues[n] ); |
| fprintf( target, " ]\n" ); |
| |
| fprintf( target, "Blue scale : %f\n", priv->blue_scale*1.0/65536.0 ); |
| fprintf( target, "Blue shift : %d\n", priv->blue_shift ); |
| fprintf( target, "Blue fuzz : %d\n", priv->blue_fuzz ); |
| |
| fprintf( target, "Std width : %d\n", priv->standard_width ); |
| fprintf( target, "Std height : %d\n", priv->standard_height ); |
| fprintf( target, "Force bold : %s\n", priv->force_bold ? "yes" : "no" ); |
| fprintf( target, "Round stem : %s\n", priv->round_stem_up ? "yes" : "no" ); |
| |
| fprintf( target, "Stem snap W : [" ); |
| for ( n = 0; n < priv->num_snap_widths; n++ ) |
| fprintf( target, " %d", priv->stem_snap_widths[n] ); |
| fprintf( target, " ]\n" ); |
| |
| fprintf( target, "Stem snap H : [" ); |
| for ( n = 0; n < priv->num_snap_heights; n++ ) |
| fprintf( target, " %d", priv->stem_snap_heights[n] ); |
| fprintf( target, " ]\n" ); |
| |
| fprintf( target, "Language : %ld\n", priv->language_group ); |
| fprintf( target, "Password : %ld\n", priv->password ); |
| fprintf( target, "Min feature : [ %d %d ]\n", |
| priv->min_feature[0], |
| priv->min_feature[1] ); |
| |
| fprintf( target, "Font BBOX : [ %ld %ld %ld %ld ]\n", |
| t1_face->font_bbox.xMin, |
| t1_face->font_bbox.yMin, |
| t1_face->font_bbox.xMax, |
| t1_face->font_bbox.yMax ); |
| |
| fprintf( target, "Font matrix : [ %f %f %f %f ]\n", |
| 1.0*t1_face->font_matrix.xx/65536000.0, |
| 1.0*t1_face->font_matrix.xy/65536000.0, |
| 1.0*t1_face->font_matrix.yx/65536000.0, |
| 1.0*t1_face->font_matrix.yy/65536000.0 ); |
| #if 0 |
| fprintf( target, |
| fprintf( target, |
| fprintf( target, |
| fprintf( target, |
| fprintf( target, |
| fprintf( target, |
| #endif |
| fprintf( target, "Num glyphs : %d\n", t1_face->num_glyphs ); |
| fprintf( target, "Num subrs : %d\n", t1_face->num_subrs ); |
| |
| return 0; |
| } |
| |
| |
| |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /********** *********/ |
| /********** *********/ |
| /********** DUMP PRIVATE DICT IN RAW FORM *********/ |
| /********** *********/ |
| /********** *********/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| |
| |
| static |
| T1_Error parse_int( T1_Tokenizer tokzer, |
| T1_Long* result ) |
| { |
| T1_Bool sign = 0; |
| T1_Long sum = 0; |
| T1_Token* token = &tokzer->token; |
| T1_Byte* base = tokzer->base + token->start; |
| T1_Byte* limit = base + token->len; |
| |
| if (base >= limit) |
| goto Fail; |
| |
| /* check sign */ |
| if ( *base == '+' ) |
| base++; |
| |
| else if ( *base == '-' ) |
| { |
| sign++; |
| base++; |
| } |
| |
| /* parse digits */ |
| if ( base >= limit ) |
| goto Fail; |
| |
| do |
| { |
| sum = ( 10*sum + (*base++ - '0') ); |
| |
| } while (base < limit); |
| |
| if (sign) |
| sum = -sum; |
| |
| *result = sum; |
| return T1_Err_Ok; |
| |
| Fail: |
| *result = 0; |
| return T1_Err_Syntax_Error; |
| } |
| |
| |
| |
| |
| |
| static |
| T1_Error Dump_Private_Dict( const char* filename ) |
| { |
| struct FT_StreamRec_ stream_rec; |
| FT_Stream stream = &stream_rec; |
| T1_Error error; |
| T1_Tokenizer tokenizer; |
| |
| error = FT_New_Stream( filename, stream ); |
| if (error) return error; |
| |
| stream->memory = library->memory; |
| |
| error = New_Tokenizer( stream, &tokenizer ); |
| if (error) goto Exit; |
| |
| /* go directly to the Private dictionary */ |
| error = Open_PrivateDict( tokenizer ); |
| if (error) |
| Panic( "Could not open private dictionary !!" ); |
| |
| /* Write it to the target file */ |
| fwrite( tokenizer->base, tokenizer->limit, 1, target ); |
| |
| Exit: |
| if (stream->close) |
| stream->close(stream); |
| |
| return error; |
| } |
| |
| |
| |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /********** *********/ |
| /********** *********/ |
| /********** DUMP TYPE 1 TOKEN STREAM *********/ |
| /********** *********/ |
| /********** *********/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| |
| |
| static |
| T1_Error Dump_Type1_Tokens( const char* filename ) |
| { |
| struct FT_StreamRec_ stream_rec; |
| FT_Stream stream = &stream_rec; |
| |
| T1_Error error; |
| T1_Tokenizer tokenizer; |
| |
| error = FT_New_Stream( filename, stream ); |
| if (error) return error; |
| |
| stream->memory = library->memory; |
| |
| error = New_Tokenizer( stream, &tokenizer ); |
| if (error) goto Exit; |
| |
| /* Dump the first segment of the Type1 font */ |
| do |
| { |
| T1_Token* token; |
| T1_String temp_string[128]; |
| T1_Int len; |
| |
| error = Read_Token( tokenizer ); |
| if (error) { error = 0; break; } |
| |
| /* dump the token */ |
| token = &tokenizer->token; |
| len = token->len; |
| if (len > 127) len = 127; |
| |
| strncpy( temp_string, |
| (T1_String*)(tokenizer->base + token->start), |
| len ); |
| temp_string[len] = '\0'; |
| |
| fprintf( target, "%s\n", temp_string ); |
| |
| /* Exit the loop when we encounter a "currentfile" token */ |
| if ( token->kind == tok_keyword && |
| token->kind2 == key_currentfile ) |
| break; |
| |
| } while (1); |
| |
| error = Open_PrivateDict( tokenizer ); |
| if (error) |
| Panic( "** could not open private dictionary **\n" ); |
| else |
| { |
| T1_Int num = 0; |
| T1_Bool last_num = 0; |
| |
| do |
| { |
| T1_Token* token; |
| T1_String temp_string[128]; |
| T1_Int len; |
| |
| error = Read_Token( tokenizer ); |
| if (error) { error = 0; break; } |
| |
| /* dump the token */ |
| token = &tokenizer->token; |
| len = token->len; |
| if (len > 127) len = 127; |
| |
| strncpy( temp_string, |
| (T1_String*)(tokenizer->base + token->start), |
| len ); |
| temp_string[len] = '\0'; |
| |
| /* detect "RD" uses */ |
| if ( token->kind == tok_keyword && |
| ( token->kind2 == key_RD || |
| token->kind2 == key_RD_alternate ) && |
| last_num ) |
| { |
| fprintf( target, "%s [%d binary bytes] ", temp_string, num ); |
| tokenizer->cursor += num; |
| } |
| else |
| { |
| fprintf( target, "%s\n", temp_string ); |
| |
| /* exit dump when we encounter a 'closefile' */ |
| if ( token->kind == tok_keyword && |
| token->kind2 == key_closefile ) |
| break; |
| |
| /* record numerical value if any */ |
| if ( token->kind == tok_number ) |
| { |
| T1_Long sum; |
| |
| if ( !parse_int( tokenizer, &sum ) ) |
| { |
| num = sum; |
| last_num = 1; |
| } |
| else |
| last_num = 0; |
| } |
| else |
| last_num = 0; |
| } |
| |
| } while (1); |
| } |
| |
| Exit: |
| if (stream->close) |
| stream->close(stream); |
| return error; |
| } |
| |
| |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /********** *********/ |
| /********** *********/ |
| /********** DUMP CHARACTER ENCODING *********/ |
| /********** *********/ |
| /********** *********/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| |
| |
| static |
| void Dump_Encoding( void ) |
| { |
| T1_Encoding* encode = &t1_face->encoding; |
| int n; |
| |
| fprintf( target, "characters count = %d\n", encode->num_chars ); |
| |
| fprintf( target, "first code = %d, last code = %d\n", |
| encode->code_first, encode->code_last ); |
| |
| for ( n = 0; n < encode->num_chars; n++ ) |
| { |
| int code = (int)encode->char_index[n]; |
| |
| if (code || n == 0) |
| fprintf( target, "%3d %s\n", n, t1_face->glyph_names[code] ); |
| } |
| } |
| |
| |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /********** *********/ |
| /********** *********/ |
| /********** DUMP SUBROUTINES AND GLYPH CHARSTRINGS *********/ |
| /********** *********/ |
| /********** *********/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| |
| |
| static |
| void Dump_CharStrings( T1_Byte* base, |
| T1_Int len ) |
| { |
| T1_Byte* cur = base; |
| T1_Byte* limit = base + len; |
| T1_String temp_name[128]; |
| T1_String* string; |
| |
| T1_Int x = 0; |
| |
| while ( cur < limit ) |
| { |
| switch (*cur++) |
| { |
| case 1: string = "hstem"; break; |
| |
| case 3: string = "vstem"; break; |
| case 4: string = "vmoveto"; break; |
| case 5: string = "rlineto"; break; |
| case 6: string = "hlineto"; break; |
| case 7: string = "vlineto"; break; |
| case 8: string = "rrcurveto"; break; |
| case 9: string = "closepath"; break; |
| case 10: string = "callsubr"; break; |
| case 11: string = "return"; break; |
| |
| case 13: string = "hsbw"; break; |
| case 14: string = "endchar"; break; |
| |
| case 21: string = "rmoveto"; break; |
| case 22: string = "hmoveto"; break; |
| |
| case 30: string = "vhcurveto"; break; |
| case 31: string = "hvcurveto"; break; |
| |
| case 12: |
| { |
| if (cur > limit) |
| Panic( "invalid charstrings stream\n" ); |
| |
| switch (*cur++) |
| { |
| case 0: string = "dotsection"; break; |
| case 1: string = "vstem3"; break; |
| case 2: string = "hstem3"; break; |
| case 6: string = "seac"; break; |
| case 7: string = "sbw"; break; |
| case 12: string = "div"; break; |
| case 16: string = "callothersubr"; break; |
| case 17: string = "pop"; break; |
| case 33: string = "setcurrentpoint"; break; |
| |
| default: |
| sprintf( temp_name, "escape(12)+unknown(%d)", cur[1] ); |
| string = temp_name; |
| } |
| } |
| break; |
| |
| case 255: /* four bytes integer */ |
| { |
| T1_Long sum; |
| |
| if (cur+4 > limit) |
| Panic( "invalid charstrings stream\n" ); |
| |
| sum = ((long)cur[0] << 24) | |
| ((long)cur[1] << 16) | |
| ((long)cur[2] << 8) | |
| cur[3]; |
| sprintf( temp_name, "%ld ", sum ); |
| string = temp_name; |
| cur += 4; |
| } |
| break; |
| |
| default: |
| if (cur[-1] >= 32) |
| { |
| if (cur[-1] < 247) |
| { |
| sprintf( temp_name, "%ld", (long)cur[-1] - 139 ); |
| } |
| else if (cur[-1] < 251) |
| { |
| cur++; |
| sprintf( temp_name, "%ld", |
| ((long)(cur[-2]-247) << 8) + cur[-1] + 108 ); |
| } |
| else |
| { |
| cur++; |
| sprintf( temp_name, "%ld", |
| -((long)(cur[-2]-251) << 8) - cur[-1] - 108 ); |
| } |
| string = temp_name; |
| } |
| else |
| { |
| sprintf( temp_name, "unknown(%d)", cur[-1] ); |
| string = temp_name; |
| } |
| } |
| |
| /* now print the charstring command */ |
| { |
| int len = strlen(string)+1; |
| |
| if ( x+len > 60 ) |
| { |
| x = 0; |
| fprintf( target, "\n" ); |
| } |
| else |
| fprintf( target, " " ); |
| |
| fprintf( target, "%s", string ); |
| x += len; |
| } |
| } |
| } |
| |
| |
| |
| static |
| void Dump_Glyph( int glyph_index ) |
| { |
| fprintf( target, "glyph name: %s\n", t1_face->glyph_names[glyph_index] ); |
| Dump_CharStrings( t1_face->charstrings [glyph_index], |
| t1_face->charstrings_len [glyph_index] ); |
| } |
| |
| static |
| void Dump_Subrs( int subrs_index ) |
| { |
| Dump_CharStrings( t1_face->subrs [ subrs_index ], |
| t1_face->subrs_len[ subrs_index ] ); |
| } |
| |
| |
| |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /********** *********/ |
| /********** *********/ |
| /********** EXECUTE GLYPH CHARSTRINGS *********/ |
| /********** *********/ |
| /********** *********/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| |
| |
| static |
| T1_Error operator_endchar( T1_Builder* builder ) |
| { |
| (void)builder; |
| fprintf( target, "endchar\n" ); |
| return 0; |
| } |
| |
| |
| static |
| T1_Error operator_sbw( T1_Builder* builder, |
| T1_Pos sbx, |
| T1_Pos sby, |
| T1_Pos wx, |
| T1_Pos wy ) |
| { |
| (void)builder; |
| fprintf( target, "set bearing [%ld,%ld] width [%ld,%ld]\n", |
| sbx, sby, wx, wy ); |
| return 0; |
| } |
| |
| #if 0 |
| static |
| T1_Error operator_seac( T1_Builder* builder, |
| T1_Pos asb, |
| T1_Pos adx, |
| T1_Pos ady, |
| T1_Int bchar, |
| T1_Int achar ) |
| { |
| (void)builder; |
| fprintf( target, "accented char: %ld [%ld,%ld] b=%d, a=%d\n", |
| asb, adx, ady, bchar, achar ); |
| return 0; |
| } |
| #endif |
| |
| static |
| T1_Error operator_closepath( T1_Builder* builder ) |
| { |
| (void)builder; |
| fprintf( target, "closepath\n" ); |
| return 0; |
| } |
| |
| |
| static |
| T1_Error operator_rlineto( T1_Builder* builder, |
| T1_Pos dx, |
| T1_Pos dy ) |
| { |
| (void)builder; |
| fprintf( target, "%ld %ld rlineto\n", dx, dy ); |
| return 0; |
| } |
| |
| |
| static |
| T1_Error operator_rmoveto( T1_Builder* builder, |
| T1_Pos dx, |
| T1_Pos dy ) |
| { |
| (void)builder; |
| fprintf( target, "%ld %ld rmoveto\n", dx, dy ); |
| return 0; |
| } |
| |
| |
| static |
| T1_Error operator_rrcurveto( T1_Builder* builder, |
| T1_Pos dx1, |
| T1_Pos dy1, |
| T1_Pos dx2, |
| T1_Pos dy2, |
| T1_Pos dx3, |
| T1_Pos dy3 ) |
| { |
| (void)builder; |
| fprintf( target, "%ld %ld %ld %ld %ld %ld rrcurveto\n", |
| dx1, dy1, dx2, dy2, dx3, dy3 ); |
| return 0; |
| } |
| |
| |
| static |
| T1_Error operator_dotsection( T1_Builder* builder ) |
| { |
| (void)builder; |
| fprintf( target, "dotsection\n" ); |
| return 0; |
| } |
| |
| |
| static |
| T1_Error operator_stem( T1_Builder* builder, |
| T1_Pos pos, |
| T1_Pos width, |
| T1_Bool vertical ) |
| { |
| (void)builder; |
| fprintf( target, "%ld %ld %s\n", pos, width, |
| vertical ? "vstem" : "hstem" ); |
| return 0; |
| } |
| |
| |
| static |
| T1_Error operator_stem3( T1_Builder* builder, |
| T1_Pos pos0, |
| T1_Pos width0, |
| T1_Pos pos1, |
| T1_Pos width1, |
| T1_Pos pos2, |
| T1_Pos width2, |
| T1_Bool vertical ) |
| { |
| (void)builder; |
| fprintf( target, "%ld %ld %ld %ld %ld %ld %s\n", |
| pos0, width0, pos1, width1, pos2, width2, |
| vertical ? "vstem3" : "hstem3" ); |
| return 0; |
| } |
| |
| |
| #if 0 |
| static |
| T1_Error operator_flex( T1_Builder* builder, |
| T1_Pos threshold, |
| T1_Pos end_x, |
| T1_Pos end_y ) |
| { |
| (void)builder; |
| fprintf( target, "%ld %ld %ld flex\n", threshold, end_x, end_y ); |
| return 0; |
| } |
| #endif |
| |
| static |
| T1_Error operator_changehints( T1_Builder* builder ) |
| { |
| (void)builder; |
| fprintf( target, "-- change hints --\n" ); |
| return 0; |
| } |
| |
| |
| static |
| T1_Error Execute_CharString( int glyph_index ) |
| { |
| static const T1_Builder_Funcs builds = |
| { |
| operator_endchar, |
| operator_sbw, |
| operator_closepath, |
| operator_rlineto, |
| operator_rmoveto, |
| operator_rrcurveto, |
| }; |
| |
| static const T1_Hinter_Funcs hints = |
| { |
| operator_dotsection, |
| operator_changehints, |
| operator_stem, |
| operator_stem3, |
| }; |
| |
| T1_Decoder decoder; |
| T1_Error error; |
| |
| T1_Init_Decoder( &decoder, &hints ); |
| T1_Init_Builder( &decoder.builder, t1_face, 0, 0, &builds ); |
| |
| error = T1_Parse_CharStrings( &decoder, |
| t1_face->charstrings [glyph_index], |
| t1_face->charstrings_len[glyph_index], |
| t1_face->num_subrs, |
| t1_face->subrs, |
| t1_face->subrs_len ); |
| return error; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /********** *********/ |
| /********** *********/ |
| /********** DEBUG FONT LOADING *********/ |
| /********** *********/ |
| /********** *********/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| |
| static |
| T1_Error Debug_Type1_Font( const char* filename ) |
| { |
| struct FT_StreamRec_ stream_rec; |
| T1_FaceRec t1facerec; |
| T1_Tokenizer tokenizer; |
| T1_Parser parser; |
| T1_Error error; |
| FT_Stream stream = &stream_rec; |
| |
| error = FT_New_Stream( filename, stream ); |
| if (error) goto Exit; |
| |
| stream->memory = library->memory; |
| |
| /* create an empty face record */ |
| memset( &t1facerec, 0, sizeof(t1facerec) ); |
| t1facerec.root.memory = library->memory; |
| t1facerec.root.stream = stream; |
| |
| t1_face = &t1facerec; |
| |
| /* open the tokenizer, this will also check the font format */ |
| error = New_Tokenizer( stream, &tokenizer ); |
| if (error) goto Fail; |
| |
| /* Now, load the font program into the face object */ |
| Init_T1_Parser( &parser, t1_face, tokenizer ); |
| |
| /* force token dump */ |
| parser.dump_tokens = 1; |
| |
| error = Parse_T1_FontProgram( &parser ); |
| |
| Done_Tokenizer( tokenizer ); |
| |
| Fail: |
| if (stream->close) |
| stream->close( stream ); |
| Exit: |
| return error; |
| } |
| |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /********** *********/ |
| /********** *********/ |
| /********** MAIN PROGRAM *********/ |
| /********** *********/ |
| /********** *********/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| /**************************************************************************/ |
| |
| |
| static |
| void Usage() |
| { |
| fprintf( stderr, "t1dump - a simple Type 1 font dumper\n" ); |
| fprintf( stderr, "(c) The FreeType project - www.freetype.org\n" ); |
| fprintf( stderr, "-------------------------------------------\n\n" ); |
| |
| fprintf( stderr, "usage : t1dump [options] fontfile(.pfb|.pfa)\n\n" ); |
| |
| fprintf( stderr, " options\n" ); |
| fprintf( stderr, " -o filename : dumps to a specific file\n" ); |
| fprintf( stderr, " -g index : dump glyph charstring\n" ); |
| fprintf( stderr, " -s index : dump subrs charstring\n" ); |
| fprintf( stderr, " -x index : execute glyph charstring\n" ); |
| fprintf( stderr, " -e : dump encoding\n" ); |
| fprintf( stderr, " -t : dumps the Type 1 token stream\n" ); |
| fprintf( stderr, " -d : debug font loading\n" ); |
| fprintf( stderr, " -p : dumps private dictionary 'as is'\n\n" ); |
| |
| exit(1); |
| } |
| |
| |
| typedef enum Request_ |
| { |
| req_dump_info, |
| req_dump_private, |
| req_dump_tokens, |
| req_dump_encoding, |
| req_dump_glyph, |
| req_dump_subr, |
| req_load_font, |
| req_execute_glyph, |
| req_debug_font |
| |
| } Request; |
| |
| |
| static char* file_name; |
| static int glyph_index; |
| static Request request = req_dump_info; |
| |
| static FT_Driver t1_driver; |
| |
| int main( int argc, char** argv ) |
| { |
| char valid; |
| |
| |
| /* Check number of arguments */ |
| if ( argc < 2 ) Usage(); |
| |
| /* Check options */ |
| target = stdout; |
| |
| argv++; |
| while (argv[0][0] == '-') |
| { |
| valid = 0; |
| switch (argv[0][1]) |
| { |
| case 'p': |
| request = req_dump_private; |
| valid = 1; |
| break; |
| |
| case 't': |
| request = req_dump_tokens; |
| valid = 1; |
| break; |
| |
| case 'e': |
| request = req_dump_encoding; |
| valid = 1; |
| break; |
| |
| case 'd': |
| request = req_debug_font; |
| valid = 1; |
| break; |
| |
| case 'o': |
| if (argc < 2) Usage(); |
| target = fopen( argv[1], "w" ); |
| if (!target) |
| Panic( "Could not open/create destination file" ); |
| argv++; |
| argc--; |
| valid = 1; |
| break; |
| |
| case 'g': |
| case 's': |
| case 'x': |
| if (argc < 2) Usage(); |
| if ( sscanf( argv[1], "%d", &glyph_index ) != 1 ) |
| Usage(); |
| |
| switch (argv[0][1]) |
| { |
| case 'g': request = req_dump_glyph; break; |
| case 's': request = req_dump_subr; break; |
| case 'x': request = req_execute_glyph; break; |
| } |
| argv++; |
| argc--; |
| valid = 1; |
| break; |
| |
| default: |
| ; |
| } |
| |
| if (valid) |
| { |
| argv++; |
| argc--; |
| if (argc < 2) Usage(); |
| } |
| else |
| break; |
| } |
| |
| /* Get file name */ |
| file_name = argv[0]; |
| |
| /* Instead of calling FT_Init_FreeType, we set up our own system */ |
| /* object and library. This is reserved for FreeType 2 wizards !! */ |
| |
| /* Init library, read face object, get driver, create size */ |
| error = FT_Init_FreeType( &library ); |
| if (error) Panic( "could not initialise FreeType library" ); |
| |
| t1_driver = FT_Get_Driver( library, "type1" ); |
| if (!t1_driver) Panic( "no Type1 driver in current FreeType lib" ); |
| |
| error = FT_New_Face( library, file_name, 0, &face ); |
| if (error) Panic( "could not find/open/create font file" ); |
| |
| if (face->driver != t1_driver) |
| Panic( "font format is not Type 1 !" ); |
| |
| switch (request) |
| { |
| case req_dump_private: |
| error = Dump_Private_Dict(file_name); |
| break; |
| |
| case req_dump_tokens: |
| error = Dump_Type1_Tokens(file_name); |
| break; |
| |
| case req_debug_font: |
| error = Debug_Type1_Font(file_name); |
| break; |
| |
| default: |
| error = FT_New_Face( library, file_name, 0, &face ); |
| if (error) Panic( "could not load Type 1 font" ); |
| |
| t1_face = (T1_Face)face; |
| |
| /* check glyph index, it is 0 by default */ |
| if ( glyph_index < 0 || glyph_index >= t1_face->num_glyphs ) |
| Panic( "invalid glyph index\n" ); |
| |
| switch (request) |
| { |
| case req_dump_glyph: |
| Dump_Glyph( glyph_index ); |
| break; |
| |
| case req_dump_subr: |
| Dump_Subrs( glyph_index ); |
| break; |
| |
| case req_execute_glyph: |
| Execute_CharString( glyph_index ); |
| break; |
| |
| case req_dump_encoding: |
| Dump_Encoding(); |
| break; |
| |
| default: |
| Dump_Font_Info(); |
| } |
| } |
| |
| if (error) Panic( "could not dump Type 1 font" ); |
| |
| FT_Done_FreeType( library ); |
| return 0; |
| } |