T1_Get_Private_Dict( T1_Parser parser, PSAux_Service psaux ) { FT_Stream stream = parser->stream; FT_Memory memory = parser->root.memory; FT_Error error = FT_Err_Ok; FT_ULong size; if ( parser->in_pfb ) { /* in the case of the PFB format, the private dictionary can be */ /* made of several segments. We thus first read the number of */ /* segments to compute the total size of the private dictionary */ /* then re-read them into memory. */ FT_Long start_pos = FT_STREAM_POS(); FT_UShort tag; parser->private_len = 0; for (;;) { error = read_pfb_tag( stream, &tag, &size ); if ( error ) goto Fail; if ( tag != 0x8002U ) break; parser->private_len += size; if ( FT_STREAM_SKIP( size ) ) goto Fail; } /* Check that we have a private dictionary there */ /* and allocate private dictionary buffer */ if ( parser->private_len == 0 ) { FT_ERROR(( "T1_Get_Private_Dict:" " invalid private dictionary section\n" )); error = FT_THROW( Invalid_File_Format ); goto Fail; } if ( FT_STREAM_SEEK( start_pos ) || FT_ALLOC( parser->private_dict, parser->private_len ) ) goto Fail; parser->private_len = 0; for (;;) { error = read_pfb_tag( stream, &tag, &size ); if ( error || tag != 0x8002U ) { error = FT_Err_Ok; break; } if ( FT_STREAM_READ( parser->private_dict + parser->private_len, size ) ) goto Fail; parser->private_len += size; } } else { /* We have already `loaded' the whole PFA font file into memory; */ /* if this is a memory resource, allocate a new block to hold */ /* the private dict. Otherwise, simply overwrite into the base */ /* dictionary block in the heap. */ /* first of all, look at the `eexec' keyword */ FT_Byte* cur = parser->base_dict; FT_Byte* limit = cur + parser->base_len; FT_Byte c; Again: for (;;) { c = cur[0]; if ( c == 'e' && cur + 9 < limit ) /* 9 = 5 letters for `eexec' + */ /* whitespace + 4 chars */ { if ( cur[1] == 'e' && cur[2] == 'x' && cur[3] == 'e' && cur[4] == 'c' ) break; } cur++; if ( cur >= limit ) { FT_ERROR(( "T1_Get_Private_Dict:" " could not find `eexec' keyword\n" )); error = FT_THROW( Invalid_File_Format ); goto Exit; } } /* check whether `eexec' was real -- it could be in a comment */ /* or string (as e.g. in u003043t.gsf from ghostscript) */ parser->root.cursor = parser->base_dict; /* set limit to `eexec' + whitespace + 4 characters */ parser->root.limit = cur + 10; cur = parser->root.cursor; limit = parser->root.limit; while ( cur < limit ) { if ( *cur == 'e' && ft_strncmp( (char*)cur, "eexec", 5 ) == 0 ) goto Found; T1_Skip_PS_Token( parser ); if ( parser->root.error ) break; T1_Skip_Spaces ( parser ); cur = parser->root.cursor; } /* we haven't found the correct `eexec'; go back and continue */ /* searching */ cur = limit; limit = parser->base_dict + parser->base_len; goto Again; /* now determine where to write the _encrypted_ binary private */ /* dictionary. We overwrite the base dictionary for disk-based */ /* resources and allocate a new block otherwise */ Found: parser->root.limit = parser->base_dict + parser->base_len; T1_Skip_PS_Token( parser ); cur = parser->root.cursor; limit = parser->root.limit; /* according to the Type1 spec, the first cipher byte must not be */ /* an ASCII whitespace character code (blank, tab, carriage return */ /* or line feed). We have seen Type 1 fonts with two line feed */ /* characters... So skip now all whitespace character codes. */ /* SumatraPDF: stop at \r if it's not used for EOL - cf. https://code.google.com/p/sumatrapdf/issues/detail?id=2408 */ c = !memchr(cur, '\n', limit - cur) || memchr(cur, '\n', limit - cur) > memchr(cur, '\r', limit - cur); while ( cur < limit && ( *cur == ' ' || *cur == '\t' || ( c && *cur == '\r' ) || *cur == '\n' ) ) ++cur; if ( cur >= limit ) { FT_ERROR(( "T1_Get_Private_Dict:" " `eexec' not properly terminated\n" )); error = FT_THROW( Invalid_File_Format ); goto Exit; } size = (FT_ULong)( parser->base_len - ( cur - parser->base_dict ) ); if ( parser->in_memory ) { /* note that we allocate one more byte to put a terminating `0' */ if ( FT_ALLOC( parser->private_dict, size + 1 ) ) goto Fail; parser->private_len = size; } else { parser->single_block = 1; parser->private_dict = parser->base_dict; parser->private_len = size; parser->base_dict = 0; parser->base_len = 0; } /* now determine whether the private dictionary is encoded in binary */ /* or hexadecimal ASCII format -- decode it accordingly */ /* we need to access the next 4 bytes (after the final whitespace */ /* following the `eexec' keyword); if they all are hexadecimal */ /* digits, then we have a case of ASCII storage */ if ( cur + 3 < limit && ft_isxdigit( cur[0] ) && ft_isxdigit( cur[1] ) && ft_isxdigit( cur[2] ) && ft_isxdigit( cur[3] ) ) { /* ASCII hexadecimal encoding */ FT_Long len; parser->root.cursor = cur; (void)psaux->ps_parser_funcs->to_bytes( &parser->root, parser->private_dict, parser->private_len, &len, 0 ); parser->private_len = len; /* put a safeguard */ parser->private_dict[len] = '\0'; } else /* binary encoding -- copy the private dict */ FT_MEM_MOVE( parser->private_dict, cur, size ); } /* we now decrypt the encoded binary private dictionary */ psaux->t1_decrypt( parser->private_dict, parser->private_len, 55665U ); if ( parser->private_len < 4 ) { FT_ERROR(( "T1_Get_Private_Dict:" " invalid private dictionary section\n" )); error = FT_THROW( Invalid_File_Format ); goto Fail; } /* replace the four random bytes at the beginning with whitespace */ parser->private_dict[0] = ' '; parser->private_dict[1] = ' '; parser->private_dict[2] = ' '; parser->private_dict[3] = ' '; parser->root.base = parser->private_dict; parser->root.cursor = parser->private_dict; parser->root.limit = parser->root.cursor + parser->private_len; Fail: Exit: return error; }
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_UInt n; FT_UInt 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 = (FT_UInt)T1_ToInt( parser ); if ( parser->root.error ) return; } else if ( *parser->root.cursor == '<' ) { /* We have `<< ... >>'. Count the number of `/' in the dictionary */ /* to get its size. */ FT_UInt 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 */ 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_PtrDist 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 = 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 = 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 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 ); T1_Skip_Spaces( parser ); if ( parser->root.cursor >= limit ) return; /* 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 ); } cur = parser->root.cursor; if ( cur + 2 < limit && *cur == '/' && n < count ) { FT_PtrDist len; cur++; parser->root.cursor = cur; T1_Skip_PS_Token( parser ); if ( parser->root.cursor >= limit ) return; if ( parser->root.error ) return; len = 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 type1 */ /* specification (it might be an encoding for a CID type1 */ /* font, however), so we conclude that this font is NOT a */ /* type1 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_THROW( Ignore ); } }
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_ULong count; FT_Long 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 ) { 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_Long)( ( 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 ) ) { if ( allocated ) { FT_ERROR(( "t42_parse_sfnts: " "can't handle mixed binary and hex strings\n" )); error = FT_THROW( Invalid_File_Format ); goto Fail; } string_size = T1_ToInt( parser ); if ( string_size < 0 ) { FT_ERROR(( "t42_parse_sfnts: invalid string size\n" )); error = FT_THROW( Invalid_File_Format ); goto Fail; } T1_Skip_PS_Token( parser ); /* `RD' */ if ( parser->root.error ) return; string_buf = parser->root.cursor + 1; /* one space after `RD' */ if ( 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; } 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_ULong)( limit - parser->root.cursor ) < 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 ); /* Pad to a 4-byte boundary length */ face->ttf_size += ( len + 3 ) & ~3; } status = OTHER_TABLES; /* there are no more than 256 tables, so no size check here */ 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 ); }
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_PtrDist len; cur++; parser->root.cursor = cur; T1_Skip_PS_Token( parser ); if ( parser->root.error ) goto Exit; len = 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_PtrDist)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; }