| /**************************************************************************** |
| * |
| * t42parse.c |
| * |
| * Type 42 font parser (body). |
| * |
| * Copyright (C) 2002-2022 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 <freetype/internal/ftdebug.h> |
| #include <freetype/internal/ftstream.h> |
| #include <freetype/internal/psaux.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 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_QALLOC( 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 ); |
| |
| if ( parser->root.funcs.done ) |
| 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]; |
| |
| if ( !FT_Matrix_Check( matrix ) ) |
| { |
| FT_ERROR(( "t42_parse_font_matrix: invalid font matrix\n" )); |
| parser->root.error = FT_THROW( Invalid_File_Format ); |
| return; |
| } |
| |
| /* 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++ ) |
| (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 ttf_count; |
| FT_Long ttf_reserved; |
| |
| FT_ULong n, string_size, old_string_size, real_size; |
| FT_Byte* string_buf = NULL; |
| FT_Bool allocated = 0; |
| |
| T42_Load_Status status; |
| |
| /** There should only be one sfnts array, but free any previous. */ |
| FT_FREE( face->ttf_data ); |
| face->ttf_size = 0; |
| |
| /* 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; |
| ttf_count = 0; |
| ttf_reserved = 12; |
| if ( FT_QALLOC( face->ttf_data, ttf_reserved ) ) |
| goto Fail; |
| |
| FT_TRACE2(( "\n" )); |
| FT_TRACE2(( "t42_parse_sfnts:\n" )); |
| |
| while ( parser->root.cursor < limit ) |
| { |
| FT_ULong size; |
| |
| |
| cur = parser->root.cursor; |
| |
| if ( *cur == ']' ) |
| { |
| parser->root.cursor++; |
| face->ttf_size = ttf_count; |
| goto Exit; |
| } |
| |
| else if ( *cur == '<' ) |
| { |
| if ( string_buf && !allocated ) |
| { |
| FT_ERROR(( "t42_parse_sfnts: " |
| "can't handle mixed binary and hex strings\n" )); |
| error = FT_THROW( Invalid_File_Format ); |
| goto Fail; |
| } |
| |
| 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_QREALLOC( 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; |
| } |
| |
| FT_TRACE2(( " PS string size %5lu bytes, offset 0x%08lx (%lu)\n", |
| string_size, ttf_count, ttf_count )); |
| |
| /* 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 ( ttf_count < 12 ) |
| { |
| face->ttf_data[ttf_count++] = string_buf[n]; |
| continue; |
| } |
| else |
| { |
| FT_Long ttf_reserved_prev = ttf_reserved; |
| |
| |
| num_tables = 16 * face->ttf_data[4] + face->ttf_data[5]; |
| status = BEFORE_TABLE_DIR; |
| ttf_reserved = 12 + 16 * num_tables; |
| |
| FT_TRACE2(( " SFNT directory contains %d tables\n", |
| num_tables )); |
| |
| if ( (FT_Long)size < ttf_reserved ) |
| { |
| FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array\n" )); |
| error = FT_THROW( Invalid_File_Format ); |
| goto Fail; |
| } |
| |
| if ( FT_QREALLOC( face->ttf_data, ttf_reserved_prev, |
| ttf_reserved ) ) |
| goto Fail; |
| } |
| /* fall through */ |
| |
| case BEFORE_TABLE_DIR: |
| /* the offset table is read; read the table directory */ |
| if ( ttf_count < ttf_reserved ) |
| { |
| face->ttf_data[ttf_count++] = string_buf[n]; |
| continue; |
| } |
| else |
| { |
| int i; |
| FT_ULong len; |
| FT_Long ttf_reserved_prev = ttf_reserved; |
| |
| |
| FT_TRACE2(( "\n" )); |
| FT_TRACE2(( " table length\n" )); |
| FT_TRACE2(( " ------------------------------\n" )); |
| |
| for ( i = 0; i < num_tables; i++ ) |
| { |
| FT_Byte* p = face->ttf_data + 12 + 16 * i + 12; |
| |
| |
| len = FT_PEEK_ULONG( p ); |
| FT_TRACE2(( " %4i 0x%08lx (%lu)\n", i, len, len )); |
| |
| if ( len > size || |
| ttf_reserved > (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 */ |
| ttf_reserved += (FT_Long)( ( len + 3 ) & ~3U ); |
| } |
| ttf_reserved += 1; |
| |
| status = OTHER_TABLES; |
| |
| FT_TRACE2(( "\n" )); |
| FT_TRACE2(( " allocating %ld bytes\n", ttf_reserved )); |
| FT_TRACE2(( "\n" )); |
| |
| if ( FT_QREALLOC( face->ttf_data, ttf_reserved_prev, |
| ttf_reserved ) ) |
| goto Fail; |
| } |
| /* fall through */ |
| |
| case OTHER_TABLES: |
| /* all other tables are just copied */ |
| if ( ttf_count >= ttf_reserved ) |
| { |
| FT_ERROR(( "t42_parse_sfnts: too much binary data\n" )); |
| error = FT_THROW( Invalid_File_Format ); |
| goto Fail; |
| } |
| face->ttf_data[ttf_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 ( parser->root.error ) |
| { |
| FT_FREE( face->ttf_data ); |
| face->ttf_size = 0; |
| } |
| 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 %ld)\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 (;;) |
| { |
| /* We support two formats. */ |
| /* */ |
| /* `/glyphname' + index [+ `def'] */ |
| /* `(glyphname)' [+ `cvn'] + index [+ `def'] */ |
| /* */ |
| /* The latter format gets created by the */ |
| /* LilyPond typesetting program. */ |
| |
| 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 == '/' || *cur == '(' ) |
| { |
| FT_UInt len; |
| FT_Bool have_literal = FT_BOOL( *cur == '(' ); |
| |
| |
| if ( cur + ( have_literal ? 3 : 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 ); |
| if ( have_literal ) |
| len--; |
| |
| 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 ); |
| |
| if ( have_literal ) |
| T1_Skip_PS_Token( 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( ".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_ZERO( 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 */ |