1032 lines
32 KiB
C
1032 lines
32 KiB
C
/****************************************************************************/
|
|
/* */
|
|
/* 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;
|
|
}
|