/***************************************************************************/ | |
/* */ | |
/* t42parse.c */ | |
/* */ | |
/* Type 42 font parser (body). */ | |
/* */ | |
/* Copyright 2002-2015 by */ | |
/* Roberto Alameda. */ | |
/* */ | |
/* This file is part of the FreeType project, and may only be used, */ | |
/* modified, and distributed under the terms of the FreeType project */ | |
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ | |
/* this file you indicate that you have read the license and */ | |
/* understand and accept it fully. */ | |
/* */ | |
/***************************************************************************/ | |
#include "t42parse.h" | |
#include "t42error.h" | |
#include FT_INTERNAL_DEBUG_H | |
#include FT_INTERNAL_STREAM_H | |
#include FT_INTERNAL_POSTSCRIPT_AUX_H | |
/*************************************************************************/ | |
/* */ | |
/* The macro FT_COMPONENT is used in trace mode. It is an implicit */ | |
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ | |
/* messages during execution. */ | |
/* */ | |
#undef FT_COMPONENT | |
#define FT_COMPONENT trace_t42 | |
static void | |
t42_parse_font_matrix( T42_Face face, | |
T42_Loader loader ); | |
static void | |
t42_parse_encoding( T42_Face face, | |
T42_Loader loader ); | |
static void | |
t42_parse_charstrings( T42_Face face, | |
T42_Loader loader ); | |
static void | |
t42_parse_sfnts( T42_Face face, | |
T42_Loader loader ); | |
/* as Type42 fonts have no Private dict, */ | |
/* we set the last argument of T1_FIELD_XXX to 0 */ | |
static const | |
T1_FieldRec t42_keywords[] = | |
{ | |
#undef FT_STRUCTURE | |
#define FT_STRUCTURE T1_FontInfo | |
#undef T1CODE | |
#define T1CODE T1_FIELD_LOCATION_FONT_INFO | |
T1_FIELD_STRING( "version", version, 0 ) | |
T1_FIELD_STRING( "Notice", notice, 0 ) | |
T1_FIELD_STRING( "FullName", full_name, 0 ) | |
T1_FIELD_STRING( "FamilyName", family_name, 0 ) | |
T1_FIELD_STRING( "Weight", weight, 0 ) | |
T1_FIELD_NUM ( "ItalicAngle", italic_angle, 0 ) | |
T1_FIELD_BOOL ( "isFixedPitch", is_fixed_pitch, 0 ) | |
T1_FIELD_NUM ( "UnderlinePosition", underline_position, 0 ) | |
T1_FIELD_NUM ( "UnderlineThickness", underline_thickness, 0 ) | |
#undef FT_STRUCTURE | |
#define FT_STRUCTURE PS_FontExtraRec | |
#undef T1CODE | |
#define T1CODE T1_FIELD_LOCATION_FONT_EXTRA | |
T1_FIELD_NUM ( "FSType", fs_type, 0 ) | |
#undef FT_STRUCTURE | |
#define FT_STRUCTURE T1_FontRec | |
#undef T1CODE | |
#define T1CODE T1_FIELD_LOCATION_FONT_DICT | |
T1_FIELD_KEY ( "FontName", font_name, 0 ) | |
T1_FIELD_NUM ( "PaintType", paint_type, 0 ) | |
T1_FIELD_NUM ( "FontType", font_type, 0 ) | |
T1_FIELD_FIXED( "StrokeWidth", stroke_width, 0 ) | |
#undef FT_STRUCTURE | |
#define FT_STRUCTURE FT_BBox | |
#undef T1CODE | |
#define T1CODE T1_FIELD_LOCATION_BBOX | |
T1_FIELD_BBOX("FontBBox", xMin, 0 ) | |
T1_FIELD_CALLBACK( "FontMatrix", t42_parse_font_matrix, 0 ) | |
T1_FIELD_CALLBACK( "Encoding", t42_parse_encoding, 0 ) | |
T1_FIELD_CALLBACK( "CharStrings", t42_parse_charstrings, 0 ) | |
T1_FIELD_CALLBACK( "sfnts", t42_parse_sfnts, 0 ) | |
{ 0, T1_FIELD_LOCATION_CID_INFO, T1_FIELD_TYPE_NONE, 0, 0, 0, 0, 0, 0 } | |
}; | |
#define T1_Add_Table( p, i, o, l ) (p)->funcs.add( (p), i, o, l ) | |
#define T1_Release_Table( p ) \ | |
do \ | |
{ \ | |
if ( (p)->funcs.release ) \ | |
(p)->funcs.release( p ); \ | |
} while ( 0 ) | |
#define T1_Skip_Spaces( p ) (p)->root.funcs.skip_spaces( &(p)->root ) | |
#define T1_Skip_PS_Token( p ) (p)->root.funcs.skip_PS_token( &(p)->root ) | |
#define T1_ToInt( p ) \ | |
(p)->root.funcs.to_int( &(p)->root ) | |
#define T1_ToBytes( p, b, m, n, d ) \ | |
(p)->root.funcs.to_bytes( &(p)->root, b, m, n, d ) | |
#define T1_ToFixedArray( p, m, f, t ) \ | |
(p)->root.funcs.to_fixed_array( &(p)->root, m, f, t ) | |
#define T1_ToToken( p, t ) \ | |
(p)->root.funcs.to_token( &(p)->root, t ) | |
#define T1_Load_Field( p, f, o, m, pf ) \ | |
(p)->root.funcs.load_field( &(p)->root, f, o, m, pf ) | |
#define T1_Load_Field_Table( p, f, o, m, pf ) \ | |
(p)->root.funcs.load_field_table( &(p)->root, f, o, m, pf ) | |
/********************* Parsing Functions ******************/ | |
FT_LOCAL_DEF( FT_Error ) | |
t42_parser_init( T42_Parser parser, | |
FT_Stream stream, | |
FT_Memory memory, | |
PSAux_Service psaux ) | |
{ | |
FT_Error error = FT_Err_Ok; | |
FT_Long size; | |
psaux->ps_parser_funcs->init( &parser->root, NULL, NULL, memory ); | |
parser->stream = stream; | |
parser->base_len = 0; | |
parser->base_dict = NULL; | |
parser->in_memory = 0; | |
/*******************************************************************/ | |
/* */ | |
/* Here a short summary of what is going on: */ | |
/* */ | |
/* When creating a new Type 42 parser, we try to locate and load */ | |
/* the base dictionary, loading the whole font into memory. */ | |
/* */ | |
/* When `loading' the base dictionary, we only set up pointers */ | |
/* in the case of a memory-based stream. Otherwise, we allocate */ | |
/* and load the base dictionary in it. */ | |
/* */ | |
/* parser->in_memory is set if we have a memory stream. */ | |
/* */ | |
if ( FT_STREAM_SEEK( 0L ) || | |
FT_FRAME_ENTER( 17 ) ) | |
goto Exit; | |
if ( ft_memcmp( stream->cursor, "%!PS-TrueTypeFont", 17 ) != 0 ) | |
{ | |
FT_TRACE2(( " not a Type42 font\n" )); | |
error = FT_THROW( Unknown_File_Format ); | |
} | |
FT_FRAME_EXIT(); | |
if ( error || FT_STREAM_SEEK( 0 ) ) | |
goto Exit; | |
size = (FT_Long)stream->size; | |
/* now, try to load `size' bytes of the `base' dictionary we */ | |
/* found previously */ | |
/* if it is a memory-based resource, set up pointers */ | |
if ( !stream->read ) | |
{ | |
parser->base_dict = (FT_Byte*)stream->base + stream->pos; | |
parser->base_len = size; | |
parser->in_memory = 1; | |
/* check that the `size' field is valid */ | |
if ( FT_STREAM_SKIP( size ) ) | |
goto Exit; | |
} | |
else | |
{ | |
/* read segment in memory */ | |
if ( FT_ALLOC( parser->base_dict, size ) || | |
FT_STREAM_READ( parser->base_dict, size ) ) | |
goto Exit; | |
parser->base_len = size; | |
} | |
parser->root.base = parser->base_dict; | |
parser->root.cursor = parser->base_dict; | |
parser->root.limit = parser->root.cursor + parser->base_len; | |
Exit: | |
if ( error && !parser->in_memory ) | |
FT_FREE( parser->base_dict ); | |
return error; | |
} | |
FT_LOCAL_DEF( void ) | |
t42_parser_done( T42_Parser parser ) | |
{ | |
FT_Memory memory = parser->root.memory; | |
/* free the base dictionary only when we have a disk stream */ | |
if ( !parser->in_memory ) | |
FT_FREE( parser->base_dict ); | |
parser->root.funcs.done( &parser->root ); | |
} | |
static int | |
t42_is_space( FT_Byte c ) | |
{ | |
return ( c == ' ' || c == '\t' || | |
c == '\r' || c == '\n' || c == '\f' || | |
c == '\0' ); | |
} | |
static void | |
t42_parse_font_matrix( T42_Face face, | |
T42_Loader loader ) | |
{ | |
T42_Parser parser = &loader->parser; | |
FT_Matrix* matrix = &face->type1.font_matrix; | |
FT_Vector* offset = &face->type1.font_offset; | |
FT_Fixed temp[6]; | |
FT_Fixed temp_scale; | |
FT_Int result; | |
result = T1_ToFixedArray( parser, 6, temp, 0 ); | |
if ( result < 6 ) | |
{ | |
parser->root.error = FT_THROW( Invalid_File_Format ); | |
return; | |
} | |
temp_scale = FT_ABS( temp[3] ); | |
if ( temp_scale == 0 ) | |
{ | |
FT_ERROR(( "t42_parse_font_matrix: invalid font matrix\n" )); | |
parser->root.error = FT_THROW( Invalid_File_Format ); | |
return; | |
} | |
/* atypical case */ | |
if ( temp_scale != 0x10000L ) | |
{ | |
temp[0] = FT_DivFix( temp[0], temp_scale ); | |
temp[1] = FT_DivFix( temp[1], temp_scale ); | |
temp[2] = FT_DivFix( temp[2], temp_scale ); | |
temp[4] = FT_DivFix( temp[4], temp_scale ); | |
temp[5] = FT_DivFix( temp[5], temp_scale ); | |
temp[3] = temp[3] < 0 ? -0x10000L : 0x10000L; | |
} | |
matrix->xx = temp[0]; | |
matrix->yx = temp[1]; | |
matrix->xy = temp[2]; | |
matrix->yy = temp[3]; | |
/* note that the offsets must be expressed in integer font units */ | |
offset->x = temp[4] >> 16; | |
offset->y = temp[5] >> 16; | |
} | |
static void | |
t42_parse_encoding( T42_Face face, | |
T42_Loader loader ) | |
{ | |
T42_Parser parser = &loader->parser; | |
FT_Byte* cur; | |
FT_Byte* limit = parser->root.limit; | |
PSAux_Service psaux = (PSAux_Service)face->psaux; | |
T1_Skip_Spaces( parser ); | |
cur = parser->root.cursor; | |
if ( cur >= limit ) | |
{ | |
FT_ERROR(( "t42_parse_encoding: out of bounds\n" )); | |
parser->root.error = FT_THROW( Invalid_File_Format ); | |
return; | |
} | |
/* if we have a number or `[', the encoding is an array, */ | |
/* and we must load it now */ | |
if ( ft_isdigit( *cur ) || *cur == '[' ) | |
{ | |
T1_Encoding encode = &face->type1.encoding; | |
FT_Int count, n; | |
PS_Table char_table = &loader->encoding_table; | |
FT_Memory memory = parser->root.memory; | |
FT_Error error; | |
FT_Bool only_immediates = 0; | |
/* read the number of entries in the encoding; should be 256 */ | |
if ( *cur == '[' ) | |
{ | |
count = 256; | |
only_immediates = 1; | |
parser->root.cursor++; | |
} | |
else | |
count = (FT_Int)T1_ToInt( parser ); | |
/* only composite fonts (which we don't support) */ | |
/* can have larger values */ | |
if ( count > 256 ) | |
{ | |
FT_ERROR(( "t42_parse_encoding: invalid encoding array size\n" )); | |
parser->root.error = FT_THROW( Invalid_File_Format ); | |
return; | |
} | |
T1_Skip_Spaces( parser ); | |
if ( parser->root.cursor >= limit ) | |
return; | |
/* PostScript happily allows overwriting of encoding arrays */ | |
if ( encode->char_index ) | |
{ | |
FT_FREE( encode->char_index ); | |
FT_FREE( encode->char_name ); | |
T1_Release_Table( char_table ); | |
} | |
/* we use a T1_Table to store our charnames */ | |
loader->num_chars = encode->num_chars = count; | |
if ( FT_NEW_ARRAY( encode->char_index, count ) || | |
FT_NEW_ARRAY( encode->char_name, count ) || | |
FT_SET_ERROR( psaux->ps_table_funcs->init( | |
char_table, count, memory ) ) ) | |
{ | |
parser->root.error = error; | |
return; | |
} | |
/* We need to `zero' out encoding_table.elements */ | |
for ( n = 0; n < count; n++ ) | |
{ | |
char* notdef = (char *)".notdef"; | |
(void)T1_Add_Table( char_table, n, notdef, 8 ); | |
} | |
/* Now we need to read records of the form */ | |
/* */ | |
/* ... charcode /charname ... */ | |
/* */ | |
/* for each entry in our table. */ | |
/* */ | |
/* We simply look for a number followed by an immediate */ | |
/* name. Note that this ignores correctly the sequence */ | |
/* that is often seen in type42 fonts: */ | |
/* */ | |
/* 0 1 255 { 1 index exch /.notdef put } for dup */ | |
/* */ | |
/* used to clean the encoding array before anything else. */ | |
/* */ | |
/* Alternatively, if the array is directly given as */ | |
/* */ | |
/* /Encoding [ ... ] */ | |
/* */ | |
/* we only read immediates. */ | |
n = 0; | |
T1_Skip_Spaces( parser ); | |
while ( parser->root.cursor < limit ) | |
{ | |
cur = parser->root.cursor; | |
/* we stop when we encounter `def' or `]' */ | |
if ( *cur == 'd' && cur + 3 < limit ) | |
{ | |
if ( cur[1] == 'e' && | |
cur[2] == 'f' && | |
t42_is_space( cur[3] ) ) | |
{ | |
FT_TRACE6(( "encoding end\n" )); | |
cur += 3; | |
break; | |
} | |
} | |
if ( *cur == ']' ) | |
{ | |
FT_TRACE6(( "encoding end\n" )); | |
cur++; | |
break; | |
} | |
/* check whether we have found an entry */ | |
if ( ft_isdigit( *cur ) || only_immediates ) | |
{ | |
FT_Int charcode; | |
if ( only_immediates ) | |
charcode = n; | |
else | |
{ | |
charcode = (FT_Int)T1_ToInt( parser ); | |
T1_Skip_Spaces( parser ); | |
/* protect against invalid charcode */ | |
if ( cur == parser->root.cursor ) | |
{ | |
parser->root.error = FT_THROW( Unknown_File_Format ); | |
return; | |
} | |
} | |
cur = parser->root.cursor; | |
if ( cur + 2 < limit && *cur == '/' && n < count ) | |
{ | |
FT_UInt len; | |
cur++; | |
parser->root.cursor = cur; | |
T1_Skip_PS_Token( parser ); | |
if ( parser->root.cursor >= limit ) | |
return; | |
if ( parser->root.error ) | |
return; | |
len = (FT_UInt)( parser->root.cursor - cur ); | |
parser->root.error = T1_Add_Table( char_table, charcode, | |
cur, len + 1 ); | |
if ( parser->root.error ) | |
return; | |
char_table->elements[charcode][len] = '\0'; | |
n++; | |
} | |
else if ( only_immediates ) | |
{ | |
/* Since the current position is not updated for */ | |
/* immediates-only mode we would get an infinite loop if */ | |
/* we don't do anything here. */ | |
/* */ | |
/* This encoding array is not valid according to the */ | |
/* type42 specification (it might be an encoding for a CID */ | |
/* type42 font, however), so we conclude that this font is */ | |
/* NOT a type42 font. */ | |
parser->root.error = FT_THROW( Unknown_File_Format ); | |
return; | |
} | |
} | |
else | |
{ | |
T1_Skip_PS_Token( parser ); | |
if ( parser->root.error ) | |
return; | |
} | |
T1_Skip_Spaces( parser ); | |
} | |
face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY; | |
parser->root.cursor = cur; | |
} | |
/* Otherwise, we should have either `StandardEncoding', */ | |
/* `ExpertEncoding', or `ISOLatin1Encoding' */ | |
else | |
{ | |
if ( cur + 17 < limit && | |
ft_strncmp( (const char*)cur, "StandardEncoding", 16 ) == 0 ) | |
face->type1.encoding_type = T1_ENCODING_TYPE_STANDARD; | |
else if ( cur + 15 < limit && | |
ft_strncmp( (const char*)cur, "ExpertEncoding", 14 ) == 0 ) | |
face->type1.encoding_type = T1_ENCODING_TYPE_EXPERT; | |
else if ( cur + 18 < limit && | |
ft_strncmp( (const char*)cur, "ISOLatin1Encoding", 17 ) == 0 ) | |
face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1; | |
else | |
parser->root.error = FT_ERR( Ignore ); | |
} | |
} | |
typedef enum T42_Load_Status_ | |
{ | |
BEFORE_START, | |
BEFORE_TABLE_DIR, | |
OTHER_TABLES | |
} T42_Load_Status; | |
static void | |
t42_parse_sfnts( T42_Face face, | |
T42_Loader loader ) | |
{ | |
T42_Parser parser = &loader->parser; | |
FT_Memory memory = parser->root.memory; | |
FT_Byte* cur; | |
FT_Byte* limit = parser->root.limit; | |
FT_Error error; | |
FT_Int num_tables = 0; | |
FT_Long count; | |
FT_ULong n, string_size, old_string_size, real_size; | |
FT_Byte* string_buf = NULL; | |
FT_Bool allocated = 0; | |
T42_Load_Status status; | |
/* The format is */ | |
/* */ | |
/* /sfnts [ <hexstring> <hexstring> ... ] def */ | |
/* */ | |
/* or */ | |
/* */ | |
/* /sfnts [ */ | |
/* <num_bin_bytes> RD <binary data> */ | |
/* <num_bin_bytes> RD <binary data> */ | |
/* ... */ | |
/* ] def */ | |
/* */ | |
/* with exactly one space after the `RD' token. */ | |
T1_Skip_Spaces( parser ); | |
if ( parser->root.cursor >= limit || *parser->root.cursor++ != '[' ) | |
{ | |
FT_ERROR(( "t42_parse_sfnts: can't find begin of sfnts vector\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
T1_Skip_Spaces( parser ); | |
status = BEFORE_START; | |
string_size = 0; | |
old_string_size = 0; | |
count = 0; | |
while ( parser->root.cursor < limit ) | |
{ | |
FT_ULong size; | |
cur = parser->root.cursor; | |
if ( *cur == ']' ) | |
{ | |
parser->root.cursor++; | |
goto Exit; | |
} | |
else if ( *cur == '<' ) | |
{ | |
T1_Skip_PS_Token( parser ); | |
if ( parser->root.error ) | |
goto Exit; | |
/* don't include delimiters */ | |
string_size = (FT_ULong)( ( parser->root.cursor - cur - 2 + 1 ) / 2 ); | |
if ( !string_size ) | |
{ | |
FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
if ( FT_REALLOC( string_buf, old_string_size, string_size ) ) | |
goto Fail; | |
allocated = 1; | |
parser->root.cursor = cur; | |
(void)T1_ToBytes( parser, string_buf, string_size, &real_size, 1 ); | |
old_string_size = string_size; | |
string_size = real_size; | |
} | |
else if ( ft_isdigit( *cur ) ) | |
{ | |
FT_Long tmp; | |
if ( allocated ) | |
{ | |
FT_ERROR(( "t42_parse_sfnts: " | |
"can't handle mixed binary and hex strings\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
tmp = T1_ToInt( parser ); | |
if ( tmp < 0 ) | |
{ | |
FT_ERROR(( "t42_parse_sfnts: invalid string size\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
else | |
string_size = (FT_ULong)tmp; | |
T1_Skip_PS_Token( parser ); /* `RD' */ | |
if ( parser->root.error ) | |
return; | |
string_buf = parser->root.cursor + 1; /* one space after `RD' */ | |
if ( (FT_ULong)( limit - parser->root.cursor ) <= string_size ) | |
{ | |
FT_ERROR(( "t42_parse_sfnts: too much binary data\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
else | |
parser->root.cursor += string_size + 1; | |
} | |
if ( !string_buf ) | |
{ | |
FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
/* A string can have a trailing zero (odd) byte for padding. */ | |
/* Ignore it. */ | |
if ( ( string_size & 1 ) && string_buf[string_size - 1] == 0 ) | |
string_size--; | |
if ( !string_size ) | |
{ | |
FT_ERROR(( "t42_parse_sfnts: invalid string\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
/* The whole TTF is now loaded into `string_buf'. We are */ | |
/* checking its contents while copying it to `ttf_data'. */ | |
size = (FT_ULong)( limit - parser->root.cursor ); | |
for ( n = 0; n < string_size; n++ ) | |
{ | |
switch ( status ) | |
{ | |
case BEFORE_START: | |
/* load offset table, 12 bytes */ | |
if ( count < 12 ) | |
{ | |
face->ttf_data[count++] = string_buf[n]; | |
continue; | |
} | |
else | |
{ | |
num_tables = 16 * face->ttf_data[4] + face->ttf_data[5]; | |
status = BEFORE_TABLE_DIR; | |
face->ttf_size = 12 + 16 * num_tables; | |
if ( (FT_Long)size < face->ttf_size ) | |
{ | |
FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
if ( FT_REALLOC( face->ttf_data, 12, face->ttf_size ) ) | |
goto Fail; | |
} | |
/* fall through */ | |
case BEFORE_TABLE_DIR: | |
/* the offset table is read; read the table directory */ | |
if ( count < face->ttf_size ) | |
{ | |
face->ttf_data[count++] = string_buf[n]; | |
continue; | |
} | |
else | |
{ | |
int i; | |
FT_ULong len; | |
for ( i = 0; i < num_tables; i++ ) | |
{ | |
FT_Byte* p = face->ttf_data + 12 + 16 * i + 12; | |
len = FT_PEEK_ULONG( p ); | |
if ( len > size || | |
face->ttf_size > (FT_Long)( size - len ) ) | |
{ | |
FT_ERROR(( "t42_parse_sfnts:" | |
" invalid data in sfnts array\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
/* Pad to a 4-byte boundary length */ | |
face->ttf_size += (FT_Long)( ( len + 3 ) & ~3U ); | |
} | |
status = OTHER_TABLES; | |
if ( FT_REALLOC( face->ttf_data, 12 + 16 * num_tables, | |
face->ttf_size + 1 ) ) | |
goto Fail; | |
} | |
/* fall through */ | |
case OTHER_TABLES: | |
/* all other tables are just copied */ | |
if ( count >= face->ttf_size ) | |
{ | |
FT_ERROR(( "t42_parse_sfnts: too much binary data\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
face->ttf_data[count++] = string_buf[n]; | |
} | |
} | |
T1_Skip_Spaces( parser ); | |
} | |
/* if control reaches this point, the format was not valid */ | |
error = FT_THROW( Invalid_File_Format ); | |
Fail: | |
parser->root.error = error; | |
Exit: | |
if ( allocated ) | |
FT_FREE( string_buf ); | |
} | |
static void | |
t42_parse_charstrings( T42_Face face, | |
T42_Loader loader ) | |
{ | |
T42_Parser parser = &loader->parser; | |
PS_Table code_table = &loader->charstrings; | |
PS_Table name_table = &loader->glyph_names; | |
PS_Table swap_table = &loader->swap_table; | |
FT_Memory memory = parser->root.memory; | |
FT_Error error; | |
PSAux_Service psaux = (PSAux_Service)face->psaux; | |
FT_Byte* cur; | |
FT_Byte* limit = parser->root.limit; | |
FT_Int n; | |
FT_Int notdef_index = 0; | |
FT_Byte notdef_found = 0; | |
T1_Skip_Spaces( parser ); | |
if ( parser->root.cursor >= limit ) | |
{ | |
FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
if ( ft_isdigit( *parser->root.cursor ) ) | |
{ | |
loader->num_glyphs = T1_ToInt( parser ); | |
if ( parser->root.error ) | |
return; | |
if ( loader->num_glyphs < 0 ) | |
{ | |
FT_ERROR(( "t42_parse_encoding: invalid number of glyphs\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
/* we certainly need more than 4 bytes per glyph */ | |
if ( loader->num_glyphs > ( limit - parser->root.cursor ) >> 2 ) | |
{ | |
FT_TRACE0(( "t42_parse_charstrings: adjusting number of glyphs" | |
" (from %d to %d)\n", | |
loader->num_glyphs, | |
( limit - parser->root.cursor ) >> 2 )); | |
loader->num_glyphs = ( limit - parser->root.cursor ) >> 2; | |
} | |
} | |
else if ( *parser->root.cursor == '<' ) | |
{ | |
/* We have `<< ... >>'. Count the number of `/' in the dictionary */ | |
/* to get its size. */ | |
FT_Int count = 0; | |
T1_Skip_PS_Token( parser ); | |
if ( parser->root.error ) | |
return; | |
T1_Skip_Spaces( parser ); | |
cur = parser->root.cursor; | |
while ( parser->root.cursor < limit ) | |
{ | |
if ( *parser->root.cursor == '/' ) | |
count++; | |
else if ( *parser->root.cursor == '>' ) | |
{ | |
loader->num_glyphs = count; | |
parser->root.cursor = cur; /* rewind */ | |
break; | |
} | |
T1_Skip_PS_Token( parser ); | |
if ( parser->root.error ) | |
return; | |
T1_Skip_Spaces( parser ); | |
} | |
} | |
else | |
{ | |
FT_ERROR(( "t42_parse_charstrings: invalid token\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
if ( parser->root.cursor >= limit ) | |
{ | |
FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
/* initialize tables */ | |
/* contrary to Type1, we disallow multiple CharStrings arrays */ | |
if ( swap_table->init ) | |
{ | |
FT_ERROR(( "t42_parse_charstrings:" | |
" only one CharStrings array allowed\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
error = psaux->ps_table_funcs->init( code_table, | |
loader->num_glyphs, | |
memory ); | |
if ( error ) | |
goto Fail; | |
error = psaux->ps_table_funcs->init( name_table, | |
loader->num_glyphs, | |
memory ); | |
if ( error ) | |
goto Fail; | |
/* Initialize table for swapping index notdef_index and */ | |
/* index 0 names and codes (if necessary). */ | |
error = psaux->ps_table_funcs->init( swap_table, 4, memory ); | |
if ( error ) | |
goto Fail; | |
n = 0; | |
for (;;) | |
{ | |
/* The format is simple: */ | |
/* `/glyphname' + index [+ def] */ | |
T1_Skip_Spaces( parser ); | |
cur = parser->root.cursor; | |
if ( cur >= limit ) | |
break; | |
/* We stop when we find an `end' keyword or '>' */ | |
if ( *cur == 'e' && | |
cur + 3 < limit && | |
cur[1] == 'n' && | |
cur[2] == 'd' && | |
t42_is_space( cur[3] ) ) | |
break; | |
if ( *cur == '>' ) | |
break; | |
T1_Skip_PS_Token( parser ); | |
if ( parser->root.cursor >= limit ) | |
{ | |
FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
if ( parser->root.error ) | |
return; | |
if ( *cur == '/' ) | |
{ | |
FT_UInt len; | |
if ( cur + 2 >= limit ) | |
{ | |
FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
cur++; /* skip `/' */ | |
len = (FT_UInt)( parser->root.cursor - cur ); | |
error = T1_Add_Table( name_table, n, cur, len + 1 ); | |
if ( error ) | |
goto Fail; | |
/* add a trailing zero to the name table */ | |
name_table->elements[n][len] = '\0'; | |
/* record index of /.notdef */ | |
if ( *cur == '.' && | |
ft_strcmp( ".notdef", | |
(const char*)(name_table->elements[n]) ) == 0 ) | |
{ | |
notdef_index = n; | |
notdef_found = 1; | |
} | |
T1_Skip_Spaces( parser ); | |
cur = parser->root.cursor; | |
(void)T1_ToInt( parser ); | |
if ( parser->root.cursor >= limit ) | |
{ | |
FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
len = (FT_UInt)( parser->root.cursor - cur ); | |
error = T1_Add_Table( code_table, n, cur, len + 1 ); | |
if ( error ) | |
goto Fail; | |
code_table->elements[n][len] = '\0'; | |
n++; | |
if ( n >= loader->num_glyphs ) | |
break; | |
} | |
} | |
loader->num_glyphs = n; | |
if ( !notdef_found ) | |
{ | |
FT_ERROR(( "t42_parse_charstrings: no /.notdef glyph\n" )); | |
error = FT_THROW( Invalid_File_Format ); | |
goto Fail; | |
} | |
/* if /.notdef does not occupy index 0, do our magic. */ | |
if ( ft_strcmp( (const char*)".notdef", | |
(const char*)name_table->elements[0] ) ) | |
{ | |
/* Swap glyph in index 0 with /.notdef glyph. First, add index 0 */ | |
/* name and code entries to swap_table. Then place notdef_index */ | |
/* name and code entries into swap_table. Then swap name and code */ | |
/* entries at indices notdef_index and 0 using values stored in */ | |
/* swap_table. */ | |
/* Index 0 name */ | |
error = T1_Add_Table( swap_table, 0, | |
name_table->elements[0], | |
name_table->lengths [0] ); | |
if ( error ) | |
goto Fail; | |
/* Index 0 code */ | |
error = T1_Add_Table( swap_table, 1, | |
code_table->elements[0], | |
code_table->lengths [0] ); | |
if ( error ) | |
goto Fail; | |
/* Index notdef_index name */ | |
error = T1_Add_Table( swap_table, 2, | |
name_table->elements[notdef_index], | |
name_table->lengths [notdef_index] ); | |
if ( error ) | |
goto Fail; | |
/* Index notdef_index code */ | |
error = T1_Add_Table( swap_table, 3, | |
code_table->elements[notdef_index], | |
code_table->lengths [notdef_index] ); | |
if ( error ) | |
goto Fail; | |
error = T1_Add_Table( name_table, notdef_index, | |
swap_table->elements[0], | |
swap_table->lengths [0] ); | |
if ( error ) | |
goto Fail; | |
error = T1_Add_Table( code_table, notdef_index, | |
swap_table->elements[1], | |
swap_table->lengths [1] ); | |
if ( error ) | |
goto Fail; | |
error = T1_Add_Table( name_table, 0, | |
swap_table->elements[2], | |
swap_table->lengths [2] ); | |
if ( error ) | |
goto Fail; | |
error = T1_Add_Table( code_table, 0, | |
swap_table->elements[3], | |
swap_table->lengths [3] ); | |
if ( error ) | |
goto Fail; | |
} | |
return; | |
Fail: | |
parser->root.error = error; | |
} | |
static FT_Error | |
t42_load_keyword( T42_Face face, | |
T42_Loader loader, | |
T1_Field field ) | |
{ | |
FT_Error error; | |
void* dummy_object; | |
void** objects; | |
FT_UInt max_objects = 0; | |
/* if the keyword has a dedicated callback, call it */ | |
if ( field->type == T1_FIELD_TYPE_CALLBACK ) | |
{ | |
field->reader( (FT_Face)face, loader ); | |
error = loader->parser.root.error; | |
goto Exit; | |
} | |
/* now the keyword is either a simple field or a table of fields; */ | |
/* we are now going to take care of it */ | |
switch ( field->location ) | |
{ | |
case T1_FIELD_LOCATION_FONT_INFO: | |
dummy_object = &face->type1.font_info; | |
break; | |
case T1_FIELD_LOCATION_FONT_EXTRA: | |
dummy_object = &face->type1.font_extra; | |
break; | |
case T1_FIELD_LOCATION_BBOX: | |
dummy_object = &face->type1.font_bbox; | |
break; | |
default: | |
dummy_object = &face->type1; | |
} | |
objects = &dummy_object; | |
if ( field->type == T1_FIELD_TYPE_INTEGER_ARRAY || | |
field->type == T1_FIELD_TYPE_FIXED_ARRAY ) | |
error = T1_Load_Field_Table( &loader->parser, field, | |
objects, max_objects, 0 ); | |
else | |
error = T1_Load_Field( &loader->parser, field, | |
objects, max_objects, 0 ); | |
Exit: | |
return error; | |
} | |
FT_LOCAL_DEF( FT_Error ) | |
t42_parse_dict( T42_Face face, | |
T42_Loader loader, | |
FT_Byte* base, | |
FT_Long size ) | |
{ | |
T42_Parser parser = &loader->parser; | |
FT_Byte* limit; | |
FT_Int n_keywords = (FT_Int)( sizeof ( t42_keywords ) / | |
sizeof ( t42_keywords[0] ) ); | |
parser->root.cursor = base; | |
parser->root.limit = base + size; | |
parser->root.error = FT_Err_Ok; | |
limit = parser->root.limit; | |
T1_Skip_Spaces( parser ); | |
while ( parser->root.cursor < limit ) | |
{ | |
FT_Byte* cur; | |
cur = parser->root.cursor; | |
/* look for `FontDirectory' which causes problems for some fonts */ | |
if ( *cur == 'F' && cur + 25 < limit && | |
ft_strncmp( (char*)cur, "FontDirectory", 13 ) == 0 ) | |
{ | |
FT_Byte* cur2; | |
/* skip the `FontDirectory' keyword */ | |
T1_Skip_PS_Token( parser ); | |
T1_Skip_Spaces ( parser ); | |
cur = cur2 = parser->root.cursor; | |
/* look up the `known' keyword */ | |
while ( cur < limit ) | |
{ | |
if ( *cur == 'k' && cur + 5 < limit && | |
ft_strncmp( (char*)cur, "known", 5 ) == 0 ) | |
break; | |
T1_Skip_PS_Token( parser ); | |
if ( parser->root.error ) | |
goto Exit; | |
T1_Skip_Spaces ( parser ); | |
cur = parser->root.cursor; | |
} | |
if ( cur < limit ) | |
{ | |
T1_TokenRec token; | |
/* skip the `known' keyword and the token following it */ | |
T1_Skip_PS_Token( parser ); | |
T1_ToToken( parser, &token ); | |
/* if the last token was an array, skip it! */ | |
if ( token.type == T1_TOKEN_TYPE_ARRAY ) | |
cur2 = parser->root.cursor; | |
} | |
parser->root.cursor = cur2; | |
} | |
/* look for immediates */ | |
else if ( *cur == '/' && cur + 2 < limit ) | |
{ | |
FT_UInt len; | |
cur++; | |
parser->root.cursor = cur; | |
T1_Skip_PS_Token( parser ); | |
if ( parser->root.error ) | |
goto Exit; | |
len = (FT_UInt)( parser->root.cursor - cur ); | |
if ( len > 0 && len < 22 && parser->root.cursor < limit ) | |
{ | |
int i; | |
/* now compare the immediate name to the keyword table */ | |
/* loop through all known keywords */ | |
for ( i = 0; i < n_keywords; i++ ) | |
{ | |
T1_Field keyword = (T1_Field)&t42_keywords[i]; | |
FT_Byte *name = (FT_Byte*)keyword->ident; | |
if ( !name ) | |
continue; | |
if ( cur[0] == name[0] && | |
len == ft_strlen( (const char *)name ) && | |
ft_memcmp( cur, name, len ) == 0 ) | |
{ | |
/* we found it -- run the parsing callback! */ | |
parser->root.error = t42_load_keyword( face, | |
loader, | |
keyword ); | |
if ( parser->root.error ) | |
return parser->root.error; | |
break; | |
} | |
} | |
} | |
} | |
else | |
{ | |
T1_Skip_PS_Token( parser ); | |
if ( parser->root.error ) | |
goto Exit; | |
} | |
T1_Skip_Spaces( parser ); | |
} | |
Exit: | |
return parser->root.error; | |
} | |
FT_LOCAL_DEF( void ) | |
t42_loader_init( T42_Loader loader, | |
T42_Face face ) | |
{ | |
FT_UNUSED( face ); | |
FT_MEM_ZERO( loader, sizeof ( *loader ) ); | |
loader->num_glyphs = 0; | |
loader->num_chars = 0; | |
/* initialize the tables -- simply set their `init' field to 0 */ | |
loader->encoding_table.init = 0; | |
loader->charstrings.init = 0; | |
loader->glyph_names.init = 0; | |
} | |
FT_LOCAL_DEF( void ) | |
t42_loader_done( T42_Loader loader ) | |
{ | |
T42_Parser parser = &loader->parser; | |
/* finalize tables */ | |
T1_Release_Table( &loader->encoding_table ); | |
T1_Release_Table( &loader->charstrings ); | |
T1_Release_Table( &loader->glyph_names ); | |
T1_Release_Table( &loader->swap_table ); | |
/* finalize parser */ | |
t42_parser_done( parser ); | |
} | |
/* END */ |