| #include <ftdebug.h> |
| |
| #include <t1types.h> |
| #include <t1tokens.h> |
| #include <t1config.h> |
| #include <t1parse.h> |
| |
| #include <stdio.h> |
| |
| #undef FT_COMPONENT |
| #define FT_COMPONENT trace_t1load |
| |
| typedef T1_Error (*T1_Parse_Func)( T1_Parser* parser ); |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> Init_T1_Parser */ |
| /* */ |
| /* <Description> */ |
| /* Initialise a given parser object to build a given T1_Face */ |
| /* */ |
| /* <Input> */ |
| /* parser :: handle to the newly built parser object */ |
| /* face :: handle to target T1 face object */ |
| /* */ |
| LOCAL_FUNC |
| void Init_T1_Parser( T1_Parser* parser, |
| T1_Face face, |
| T1_Tokenizer tokenizer ) |
| { |
| parser->error = 0; |
| parser->face = face; |
| parser->tokenizer = tokenizer; |
| parser->top = parser->stack; |
| parser->limit = parser->stack + T1_MAX_STACK_DEPTH; |
| |
| parser->state_index = 0; |
| parser->state_stack[0] = dict_none; |
| |
| parser->encoding_type = t1_encoding_none; |
| parser->encoding_names = 0; |
| parser->encoding_offsets = 0; |
| parser->encoding_lengths = 0; |
| |
| parser->dump_tokens = 0; |
| face->type1.lenIV = 4; /* XXX : is it sure ?? */ |
| } |
| |
| |
| |
| /*************************************************************************/ |
| /* */ |
| /* <Function> Next_T1_Token */ |
| /* */ |
| /* <Description> */ |
| /* grabs the next significant token from a parser's input stream. */ |
| /* this function ignores a number of tokens, and translates */ |
| /* alternate forms into their common ones.. */ |
| /* */ |
| /* <Input> */ |
| /* parser :: handle to source parser */ |
| /* */ |
| /* <Output> */ |
| /* token :: the extracted token descriptor */ |
| /* */ |
| /* <Return> */ |
| /* Error code. 0 means success */ |
| /* */ |
| LOCAL_FUNC |
| T1_Error Next_T1_Token( T1_Parser* parser, |
| T1_Token* token ) |
| { |
| T1_Error error; |
| T1_Tokenizer tokzer = parser->tokenizer; |
| |
| L1: |
| error = Read_Token( tokzer ); |
| if (error) return error; |
| |
| /* We now must ignore a number of tokens like "dup", "executeonly", */ |
| /* "readonly", etc... */ |
| *token = tokzer->token; |
| if ( token->kind == tok_keyword ) |
| switch( token->kind2 ) |
| { |
| case key_dup: |
| case key_execonly: |
| case key_readonly: |
| case key_noaccess: |
| case key_userdict: |
| /* do nothing - loop */ |
| goto L1; |
| |
| /* We also translate some other keywords from their alternative */ |
| /* to their "normal" form.. */ |
| |
| case key_NP_alternate: |
| token->kind2 = key_NP; |
| break; |
| |
| case key_RD_alternate: |
| token->kind2 = key_RD; |
| break; |
| |
| case key_ND_alternate: |
| token->kind2 = key_ND; |
| break; |
| |
| default: |
| ; |
| } |
| |
| /* Dump the token when requested. This feature is only available */ |
| /* in the 'error' and 'trace' debug levels.. */ |
| #if defined( FT_DEBUG_LEVEL_ERROR ) || defined( FT_DEBUG_LEVEL_TRACE ) |
| if ( parser->dump_tokens ) |
| { |
| T1_String temp_string[128]; |
| T1_Int len; |
| |
| len = token->len; |
| if ( len > 127 ) len = 127; |
| strncpy( temp_string, |
| (T1_String*)tokzer->base + token->start, |
| len ); |
| temp_string[len] = '\0'; |
| FT_ERROR(( "%s\n", temp_string )); |
| } |
| #endif |
| |
| return T1_Err_Ok; |
| } |
| |
| |
| |
| static |
| T1_Error Expect_Keyword( T1_Parser* parser, |
| T1_TokenType keyword ) |
| { |
| T1_Token token; |
| T1_Error error; |
| |
| error = Next_T1_Token( parser, &token ); |
| if (error) goto Exit; |
| |
| if ( token.kind != tok_keyword || |
| token.kind2 != keyword ) |
| { |
| error = T1_Err_Syntax_Error; |
| FT_ERROR(( "T1.Parse: keyword '%s' expected.\n", |
| t1_keywords[ keyword - key_first_ ] )); |
| } |
| |
| Exit: |
| return error; |
| } |
| |
| |
| |
| static |
| T1_Error Expect_Keyword2( T1_Parser* parser, |
| T1_TokenType keyword1, |
| T1_TokenType keyword2 ) |
| { |
| T1_Token token; |
| T1_Error error; |
| |
| error = Next_T1_Token( parser, &token ); |
| if (error) goto Exit; |
| |
| if ( token.kind != tok_keyword || |
| ( token.kind2 != keyword1 && |
| token.kind2 != keyword2 ) ) |
| { |
| error = T1_Err_Syntax_Error; |
| FT_ERROR(( "T1.Parse: keyword '%s' or '%s' expected.\n", |
| t1_keywords[ keyword1 - key_first_ ], |
| t1_keywords[ keyword2 - key_first_ ] )); |
| } |
| |
| Exit: |
| return error; |
| } |
| |
| |
| |
| static |
| void Parse_Encoding( T1_Parser* parser ) |
| { |
| T1_Token* token = parser->top+1; |
| FT_Memory memory = parser->face->root.memory; |
| T1_Encoding* encode = &parser->face->type1.encoding; |
| T1_Error error = 0; |
| |
| if (token->kind == tok_keyword && |
| (token->kind2 == key_StandardEncoding || |
| token->kind2 == key_ExpertEncoding ) ) |
| { |
| encode->num_chars = 256; |
| encode->code_first = 32; |
| encode->code_last = 255; |
| |
| if ( ALLOC_ARRAY( encode->char_index, 256, T1_Short ) ) |
| goto Exit; |
| |
| encode->char_name = 0; /* no need to store glyph names */ |
| |
| /* Now copy the encoding */ |
| switch (token->kind2) |
| { |
| case key_ExpertEncoding : parser->encoding_type = t1_encoding_expert; |
| default : parser->encoding_type = t1_encoding_standard; break; |
| } |
| } |
| else |
| { |
| FT_ERROR(( "T1.Parse_Encoding: invalid encoding type\n" )); |
| error = T1_Err_Syntax_Error; |
| } |
| |
| Exit: |
| parser->error = error; |
| } |
| |
| |
| |
| |
| |
| |
| |
| /**********************************************************************/ |
| /* */ |
| /* */ |
| /* IMPLEMENTATION OF THE "DEF" KEYWORD DEPENDING ON */ |
| /* CURRENT DICTIONARY STATE */ |
| /* */ |
| /* */ |
| /**********************************************************************/ |
| |
| |
| /**************************************************************************/ |
| /* */ |
| /* <Function> Do_Def_Font */ |
| /* */ |
| /* <Description> */ |
| /* This function performs a 'def' when in the Font dictionary */ |
| /* Its purpose is to build the T1_Face attributes directly from */ |
| /* the stream.. */ |
| /* */ |
| /* <Input> */ |
| /* parser :: handle to current parser. */ |
| /* */ |
| /* <Return> */ |
| /* Error code. 0 means success */ |
| /* */ |
| static |
| T1_Error Do_Def_Font( T1_Parser* parser ) |
| { |
| T1_Token* top = parser->top; |
| T1_Face face = parser->face; |
| T1_Font* type1 = &face->type1; |
| |
| switch ( top[0].kind2 ) |
| { |
| case imm_FontName: |
| /* in some cases, the /FontName is an immediate like */ |
| /* /TimesNewRoman. In this case, we simply copy the */ |
| /* token string (without the /).. */ |
| if (top[1].kind == tok_immediate) |
| { |
| FT_Memory memory = parser->tokenizer->memory; |
| T1_Error error; |
| T1_Int len = top[1].len; |
| |
| if ( ALLOC( type1->font_name, len+1 ) ) |
| { |
| parser->error = error; |
| return error; |
| } |
| |
| MEM_Copy( type1->font_name, |
| parser->tokenizer->base + top[1].start, |
| len ); |
| type1->font_name[len] = '\0'; |
| } |
| else |
| type1->font_name = CopyString( parser ); |
| break; |
| |
| case imm_Encoding: |
| Parse_Encoding( parser ); |
| break; |
| |
| case imm_PaintType: |
| type1->paint_type = (T1_Byte)CopyInteger( parser ); |
| break; |
| |
| case imm_FontType: |
| type1->font_type = (T1_Byte)CopyInteger( parser ); |
| break; |
| |
| case imm_FontMatrix: |
| CopyMatrix( parser, &type1->font_matrix ); |
| break; |
| |
| case imm_FontBBox: |
| CopyBBox( parser, &type1->font_bbox ); |
| break; |
| |
| case imm_UniqueID: |
| type1->unique_id = CopyInteger( parser ); |
| break; |
| |
| case imm_StrokeWidth: |
| type1->stroke_width = CopyInteger( parser ); |
| break; |
| |
| case imm_FontID: |
| type1->font_id = CopyInteger( parser ); |
| break; |
| |
| default: |
| /* ignore all other things */ |
| parser->error = T1_Err_Ok; |
| } |
| return parser->error; |
| } |
| |
| |
| |
| /**************************************************************************/ |
| /* */ |
| /* <Function> Do_Def_FontInfo */ |
| /* */ |
| /* <Description> */ |
| /* This function performs a 'def' when in the FontInfo dictionary */ |
| /* Its purpose is to build the T1_FontInfo structure directly from */ |
| /* the stream.. */ |
| /* */ |
| /* <Input> */ |
| /* parser :: handle to current parser. */ |
| /* */ |
| /* <Return> */ |
| /* Error code. 0 means success */ |
| /* */ |
| static |
| T1_Error Do_Def_FontInfo( T1_Parser* parser ) |
| { |
| T1_Token* top = parser->top; |
| T1_Font* info = &parser->face->type1; |
| |
| switch ( top[0].kind2 ) |
| { |
| case imm_version: |
| info->version = CopyString( parser ); |
| break; |
| |
| case imm_Notice: |
| info->notice = CopyString( parser ); |
| break; |
| |
| case imm_FullName: |
| info->full_name = CopyString( parser ); |
| break; |
| |
| case imm_FamilyName: |
| info->family_name = CopyString( parser ); |
| break; |
| |
| case imm_Weight: |
| info->weight = CopyString( parser ); |
| break; |
| |
| case imm_ItalicAngle: |
| info->italic_angle = CopyInteger( parser ); |
| break; |
| |
| case imm_isFixedPitch: |
| info->is_fixed_pitch = CopyBoolean( parser ); |
| break; |
| |
| case imm_UnderlinePosition: |
| info->underline_position = (T1_Short)CopyInteger( parser ); |
| break; |
| |
| case imm_UnderlineThickness: |
| info->underline_thickness = (T1_Short)CopyInteger( parser ); |
| break; |
| |
| default: |
| /* ignore all other things */ |
| parser->error = T1_Err_Ok; |
| } |
| return parser->error; |
| } |
| |
| |
| |
| /**************************************************************************/ |
| /* */ |
| /* <Function> Do_Def_Private */ |
| /* */ |
| /* <Description> */ |
| /* This function performs a 'def' when in the Private dictionary */ |
| /* Its purpose is to build the T1_Private structure directly from */ |
| /* the stream.. */ |
| /* */ |
| /* <Input> */ |
| /* parser :: handle to current parser. */ |
| /* */ |
| /* <Return> */ |
| /* Error code. 0 means success */ |
| /* */ |
| static |
| T1_Error Do_Def_Private( T1_Parser* parser ) |
| { |
| T1_Token* top = parser->top; |
| T1_Font* priv = &parser->face->type1; |
| |
| switch ( top[0].kind2 ) |
| { |
| case imm_RD: case imm_RD_alternate: /* Ignore the definitions */ |
| case imm_ND: case imm_ND_alternate: /* of RD, NP, ND and their */ |
| case imm_NP: case imm_NP_alternate: /* alternate forms ... */ |
| parser->error = T1_Err_Ok; |
| break; |
| |
| |
| case imm_BlueValues: |
| CopyArray( parser, &priv->num_blues, |
| priv->blue_values, 14 ); |
| break; |
| |
| |
| case imm_OtherBlues: |
| CopyArray( parser, &priv->num_other_blues, |
| priv->other_blues, 10 ); |
| break; |
| |
| |
| case imm_FamilyBlues: |
| CopyArray( parser, &priv->num_family_blues, |
| priv->family_blues, 14 ); |
| break; |
| |
| |
| case imm_FamilyOtherBlues: |
| CopyArray( parser, &priv->num_family_other_blues, |
| priv->family_other_blues, 10 ); |
| break; |
| |
| |
| case imm_BlueScale: |
| priv->blue_scale = CopyFloat( parser, 0x10000 ); |
| break; |
| |
| |
| case imm_BlueShift: |
| priv->blue_shift = CopyInteger( parser ); |
| break; |
| |
| |
| case imm_BlueFuzz: |
| priv->blue_fuzz = CopyInteger( parser ); |
| break; |
| |
| |
| case imm_StdHW: |
| CopyArray( parser, 0, (T1_Short*)&priv->standard_width, 1 ); |
| break; |
| |
| |
| case imm_StdVW: |
| CopyArray( parser, 0, (T1_Short*)&priv->standard_height, 1 ); |
| break; |
| |
| |
| case imm_StemSnapH: |
| CopyArray( parser, &priv->num_snap_widths, |
| priv->stem_snap_widths, 12 ); |
| break; |
| |
| |
| case imm_StemSnapV: |
| CopyArray( parser, &priv->num_snap_heights, |
| priv->stem_snap_heights, 12 ); |
| break; |
| |
| |
| case imm_ForceBold: |
| priv->force_bold = CopyBoolean( parser ); |
| break; |
| |
| |
| case imm_LanguageGroup: |
| priv->language_group = CopyInteger( parser ); |
| break; |
| |
| |
| case imm_password: |
| priv->password = CopyInteger( parser ); |
| break; |
| |
| |
| case imm_UniqueID: |
| priv->unique_id = CopyInteger( parser ); |
| break; |
| |
| |
| case imm_lenIV: |
| priv->lenIV = CopyInteger( parser ); |
| break; |
| |
| |
| case imm_MinFeature: |
| CopyArray( parser, 0, priv->min_feature, 2 ); |
| break; |
| |
| |
| default: |
| /* ignore all other things */ |
| parser->error = T1_Err_Ok; |
| } |
| return parser->error; |
| } |
| |
| |
| |
| /**************************************************************************/ |
| /* */ |
| /* <Function> Do_Def_Error */ |
| /* */ |
| /* <Description> */ |
| /* This function returns a simple syntax error when invoked. It is */ |
| /* ued for the "def" keyword when in the "encoding", "subrs", */ |
| /* "othersubrs" and "charstrings" dictionary states.. */ |
| /* */ |
| /* <Input> */ |
| /* parser :: handle to current parser. */ |
| /* */ |
| /* <Return> */ |
| /* Error code. 0 means success */ |
| /* */ |
| static |
| T1_Error Do_Def_Error( T1_Parser* parser ) |
| { |
| FT_ERROR(( "T1.Load : 'def' keyword encountered in bad dictionary/array\n" )); |
| parser->error = T1_Err_Syntax_Error; |
| return parser->error; |
| } |
| |
| |
| static |
| T1_Error Do_Def_Ignore( T1_Parser* parser ) |
| { |
| (void)parser; |
| return T1_Err_Ok; |
| } |
| |
| static |
| T1_Parse_Func def_funcs[ dict_max ] = |
| { |
| Do_Def_Error, |
| Do_Def_Font, |
| Do_Def_FontInfo, |
| Do_Def_Ignore, |
| Do_Def_Private, |
| Do_Def_Ignore, |
| Do_Def_Ignore, |
| Do_Def_Ignore, |
| Do_Def_Ignore, |
| Do_Def_Ignore, |
| Do_Def_Ignore, |
| }; |
| |
| |
| /**********************************************************************/ |
| /* */ |
| /* */ |
| /* IMPLEMENTATION OF THE "PUT" KEYWORD DEPENDING ON */ |
| /* CURRENT DICTIONARY STATE */ |
| /* */ |
| /* */ |
| /**********************************************************************/ |
| |
| /**************************************************************************/ |
| /* */ |
| /* <Function> Do_Put_Encoding */ |
| /* */ |
| /* <Description> */ |
| /* This function performs a 'put' when in the Encoding array */ |
| /* The glyph name is copied into the T1 recorder, and the charcode */ |
| /* and glyph name pointer are written into the face object encoding */ |
| /* */ |
| /* <Input> */ |
| /* parser :: handle to current parser. */ |
| /* */ |
| /* <Return> */ |
| /* Error code. 0 means success */ |
| /* */ |
| static |
| T1_Error Do_Put_Encoding( T1_Parser* parser ) |
| { |
| T1_Error error = T1_Err_Ok; |
| T1_Face face = parser->face; |
| T1_Token* top = parser->top; |
| T1_Encoding* encode = &face->type1.encoding; |
| T1_Int index; |
| |
| /* record and check the character code */ |
| if ( top[0].kind != tok_number ) |
| { |
| FT_TRACE4(( "T1.Parse.put: number expected\n" )); |
| goto Syntax_Error; |
| } |
| index = (T1_Int)CopyInteger( parser ); |
| if (parser->error) return parser->error; |
| |
| if ( index < 0 || index >= encode->num_chars ) |
| { |
| FT_TRACE4(( "T1.Parse.put: invalid character code\n" )); |
| goto Syntax_Error; |
| } |
| |
| /* record the immediate name */ |
| if ( top[1].kind != tok_immediate ) |
| { |
| FT_TRACE4(( "T1.Parse.put: immediate name expected\n" )); |
| goto Syntax_Error; |
| } |
| |
| /* if the glyph name is '.notdef', store a NULL char name */ |
| /* otherwise, record the glyph name.. */ |
| if ( top[1].kind == imm_notdef ) |
| { |
| parser->table.elements[ index ] = 0; |
| parser->table.lengths [ index ] = 0; |
| } |
| else |
| { |
| T1_String temp_name[128]; |
| T1_Token* token = top+1; |
| T1_Int len = token->len-1; |
| |
| /* copy immediate name */ |
| if (len > 127) len = 127; |
| MEM_Copy( temp_name, parser->tokenizer->base + token->start+1, len ); |
| temp_name[len] = '\0'; |
| |
| error = T1_Add_Table( &parser->table, index, (T1_Byte*)temp_name, len+1 ); |
| |
| /* adjust code_first and code_last */ |
| if ( index < encode->code_first ) encode->code_first = index; |
| if ( index > encode->code_last ) encode->code_last = index; |
| } |
| return error; |
| |
| Syntax_Error: |
| /* ignore the error, and simply clear the stack */ |
| FT_TRACE4(( "T1.Put.Encoding: invalid syntax encountered\n" )); |
| parser->top = parser->stack; |
| return T1_Err_Ok; |
| } |
| |
| /**********************************************************************/ |
| /* */ |
| /* */ |
| /* IMPLEMENTATION OF THE "RD" KEYWORD DEPENDING ON */ |
| /* CURRENT DICTIONARY STATE */ |
| /* */ |
| /* */ |
| /**********************************************************************/ |
| |
| /**************************************************************************/ |
| /* */ |
| /* <Function> Do_RD_Subrs */ |
| /* */ |
| /* <Description> */ |
| /* This function performs a 'RD' when in the Subrs dictionary */ |
| /* It simply records the array of bytecodes/charstrings corresponding */ |
| /* to the sub-routine.. */ |
| /* */ |
| /* <Input> */ |
| /* parser :: handle to current parser. */ |
| /* */ |
| /* <Return> */ |
| /* Error code. 0 means success */ |
| /* */ |
| static |
| T1_Error Do_RD_Subrs( T1_Parser* parser ) |
| { |
| T1_Error error = T1_Err_Ok; |
| T1_Face face = parser->face; |
| T1_Token* top = parser->top; |
| T1_Tokenizer tokzer = parser->tokenizer; |
| T1_Int index, count; |
| |
| /* record and check the character code */ |
| if ( top[0].kind != tok_number || |
| top[1].kind != tok_number ) |
| { |
| FT_ERROR(( "T1.Parse.put: number expected\n" )); |
| goto Syntax_Error; |
| } |
| index = (T1_Int)CopyInteger( parser ); |
| error = parser->error; if (error) goto Exit; |
| |
| count = (T1_Int)CopyInteger( parser ); |
| error = parser->error; if (error) goto Exit; |
| |
| if ( index < 0 || index >= face->type1.num_subrs ) |
| { |
| FT_ERROR(( "T1.Parse.put: invalid character code\n" )); |
| goto Syntax_Error; |
| } |
| |
| /* decrypt charstring and skip them */ |
| { |
| T1_Byte* base = tokzer->base + tokzer->cursor; |
| |
| t1_decrypt( base, count, 4330 ); |
| tokzer->cursor += count; |
| |
| base += face->type1.lenIV; |
| count -= face->type1.lenIV; |
| |
| error = T1_Add_Table( &parser->table, index, base, count ); |
| } |
| |
| /* consume the closing NP or 'put' */ |
| error = Expect_Keyword2( parser, key_NP, key_put ); |
| |
| Exit: |
| return error; |
| |
| Syntax_Error: |
| return T1_Err_Syntax_Error; |
| } |
| |
| |
| /**************************************************************************/ |
| /* */ |
| /* <Function> Do_RD_CharStrings */ |
| /* */ |
| /* <Description> */ |
| /* This function performs a 'RD' when in the CharStrings dictionary */ |
| /* It simply records the array of bytecodes/charstrings corresponding */ |
| /* to the glyph program string. */ |
| /* */ |
| /* <Input> */ |
| /* parser :: handle to current parser. */ |
| /* */ |
| /* <Return> */ |
| /* Error code. 0 means success */ |
| /* */ |
| static |
| T1_Error Do_RD_Charstrings( T1_Parser* parser ) |
| { |
| T1_Error error = T1_Err_Ok; |
| T1_Face face = parser->face; |
| T1_Token* top = parser->top; |
| T1_Tokenizer tokzer = parser->tokenizer; |
| T1_Int index, count; |
| |
| /* check the character name argument */ |
| if ( top[0].kind != tok_immediate ) |
| { |
| FT_ERROR(( "T1.Parse.RD: immediate character name expected\n" )); |
| goto Syntax_Error; |
| } |
| |
| /* check the count argument */ |
| if ( top[1].kind != tok_number ) |
| { |
| FT_ERROR(( "T1.Parse.put: number expected\n" )); |
| goto Syntax_Error; |
| } |
| parser->args++; |
| count = (T1_Int)CopyInteger( parser ); |
| error = parser->error; if (error) goto Exit; |
| |
| /* record the glyph name and get the corresponding glyph index */ |
| if ( top[0].kind2 == imm_notdef ) |
| index = 0; |
| else |
| { |
| T1_String temp_name[128]; |
| T1_Token* token = top; |
| T1_Int len = token->len-1; |
| |
| /* copy immediate name */ |
| if (len > 127) len = 127; |
| MEM_Copy( temp_name, parser->tokenizer->base + token->start+1, len ); |
| temp_name[len] = '\0'; |
| |
| index = parser->cur_name++; |
| error = T1_Add_Table( &parser->table, index*2, (T1_Byte*)temp_name, len+1 ); |
| if (error) goto Exit; |
| } |
| |
| /* decrypt and record charstring, then skip them */ |
| { |
| T1_Byte* base = tokzer->base + tokzer->cursor; |
| |
| t1_decrypt( base, count, 4330 ); |
| tokzer->cursor += count; /* skip */ |
| |
| base += face->type1.lenIV; |
| count -= face->type1.lenIV; |
| |
| error = T1_Add_Table( &parser->table, index*2+1, base, count ); |
| } |
| |
| /* consume the closing ND */ |
| if (!error) |
| error = Expect_Keyword( parser, key_ND ); |
| |
| Exit: |
| return error; |
| |
| Syntax_Error: |
| return T1_Err_Syntax_Error; |
| } |
| |
| |
| |
| |
| |
| |
| static |
| T1_Error Expect_Dict_Arguments( T1_Parser* parser, |
| T1_Int num_args, |
| T1_TokenType immediate, |
| T1_DictState new_state, |
| T1_Int *count ) |
| { |
| /* check that we have enough arguments in the stack, including */ |
| /* the 'dict' keyword.. */ |
| if ( parser->top - parser->stack < num_args ) |
| { |
| FT_ERROR(( "T1.Parse.Dict : expecting at least %d arguments", |
| num_args )); |
| goto Syntax_Error; |
| } |
| |
| /* check that we have the correct immediate, if needed */ |
| if ( num_args == 2 ) |
| { |
| if ( parser->top[-2].kind != tok_immediate || |
| parser->top[-2].kind2 != immediate ) |
| { |
| FT_ERROR(( "T1.Parse.Dict : expecting '/%s' dictionary\n", |
| t1_immediates[ immediate - imm_first_ ] )); |
| goto Syntax_Error; |
| } |
| } |
| |
| parser->args = parser->top-1; |
| |
| /* check that the count argument is a number */ |
| if ( parser->args->kind != tok_number ) |
| { |
| FT_ERROR(( "T1.Parse.Dict : expecting numerical count argument for 'dict'\n" )); |
| goto Syntax_Error; |
| } |
| if (count) |
| { |
| *count = CopyInteger( parser ); |
| if (parser->error) return parser->error; |
| } |
| |
| /* save the dictionary state */ |
| parser->state_stack[ ++parser->state_index ] = new_state; |
| |
| /* consume the 'begin' keyword, and clear the stack */ |
| parser->top -= num_args; |
| return Expect_Keyword( parser, key_begin ); |
| |
| Syntax_Error: |
| return T1_Err_Syntax_Error; |
| } |
| |
| |
| |
| |
| |
| static |
| T1_Error Expect_Array_Arguments( T1_Parser* parser ) |
| { |
| T1_Token* top = parser->top; |
| T1_Error error = T1_Err_Ok; |
| T1_DictState new_state; |
| T1_Int count; |
| T1_Face face = parser->face; |
| FT_Memory memory = face->root.memory; |
| |
| /* Check arguments format */ |
| if ( top - parser->stack < 2 ) |
| { |
| FT_ERROR(( "T1.Parse.array: two arguments expected\n" )); |
| error = T1_Err_Stack_Underflow; |
| goto Exit; |
| } |
| |
| parser->top -= 2; |
| top -= 2; |
| parser->args = top + 1; |
| |
| if ( top[0].kind != tok_immediate ) |
| { |
| FT_ERROR(( "T1.Parse.array: first argument must be an immediate name\n" )); |
| goto Syntax_Error; |
| } |
| |
| if ( top[1].kind != tok_number ) |
| { |
| FT_ERROR(( "T1.Parse.array: second argument must be a number\n" )); |
| goto Syntax_Error; |
| } |
| count = (T1_Int)CopyInteger( parser ); |
| |
| /* Is this an array we know about ?? */ |
| switch ( top[0].kind2 ) |
| { |
| case imm_Encoding: |
| { |
| T1_Encoding* encode = &face->type1.encoding; |
| |
| new_state = dict_encoding; |
| |
| encode->code_first = count; |
| encode->code_last = 0; |
| encode->num_chars = count; |
| |
| /* allocate the table of character indexes. The table of */ |
| /* character names is allocated through init_t1_recorder */ |
| if ( ALLOC_ARRAY( encode->char_index, count, T1_Short ) ) |
| return error; |
| |
| error = T1_New_Table( &parser->table, count, memory ); |
| if (error) goto Exit; |
| |
| parser->encoding_type = t1_encoding_array; |
| } |
| break; |
| |
| |
| case imm_Subrs: |
| { |
| new_state = dict_subrs; |
| face->type1.num_subrs = count; |
| |
| error = T1_New_Table( &parser->table, count, memory ); |
| if (error) goto Exit; |
| } |
| break; |
| |
| |
| case imm_CharStrings: |
| new_state = dict_charstrings; |
| break; |
| |
| |
| default: |
| new_state = dict_unknown_array; |
| } |
| parser->state_stack[ ++parser->state_index ] = new_state; |
| |
| Exit: |
| return error; |
| |
| Syntax_Error: |
| return T1_Err_Syntax_Error; |
| } |
| |
| |
| |
| |
| static |
| T1_Error Finalise_Parsing( T1_Parser* parser ) |
| { |
| T1_Face face = parser->face; |
| T1_Font* type1 = &face->type1; |
| FT_Memory memory = face->root.memory; |
| T1_Table* strings = &parser->table; |
| PSNames_Interface* psnames = (PSNames_Interface*)face->psnames; |
| T1_Int num_glyphs; |
| T1_Int n; |
| T1_Error error; |
| |
| num_glyphs = type1->num_glyphs = parser->cur_name; |
| |
| /* allocate glyph names and charstrings arrays */ |
| if ( ALLOC_ARRAY( type1->glyph_names , num_glyphs, T1_String* ) || |
| ALLOC_ARRAY( type1->charstrings , num_glyphs, T1_Byte* ) || |
| ALLOC_ARRAY( type1->charstrings_len, num_glyphs, T1_Int* ) ) |
| return error; |
| |
| /* copy glyph names and charstrings offsets and lengths */ |
| type1->charstrings_block = strings->block; |
| for ( n = 0; n < num_glyphs; n++ ) |
| { |
| type1->glyph_names[n] = (T1_String*)strings->elements[2*n]; |
| type1->charstrings[n] = strings->elements[2*n+1]; |
| type1->charstrings_len[n] = strings->lengths [2*n+1]; |
| } |
| |
| /* now free the old tables */ |
| FREE( strings->elements ); |
| FREE( strings->lengths ); |
| |
| if (!psnames) |
| { |
| FT_ERROR(( "T1.Parse.Finalise : PSNames module missing !!\n" )); |
| return T1_Err_Unimplemented_Feature; |
| } |
| |
| /* Compute encoding if required. */ |
| if (parser->encoding_type == t1_encoding_none) |
| { |
| FT_ERROR(( "T1.Parse.Finalise : no encoding specified in font file\n" )); |
| return T1_Err_Syntax_Error; |
| } |
| |
| { |
| T1_Int n; |
| T1_Encoding* encode = &type1->encoding; |
| |
| encode->code_first = encode->num_chars-1; |
| encode->code_last = 0; |
| |
| for ( n = 0; n < encode->num_chars; n++ ) |
| { |
| T1_String** names; |
| T1_Int index; |
| T1_Int m; |
| |
| switch (parser->encoding_type) |
| { |
| case t1_encoding_standard: |
| index = psnames->adobe_std_encoding[n]; |
| names = 0; |
| break; |
| |
| case t1_encoding_expert: |
| index = psnames->adobe_expert_encoding[n]; |
| names = 0; |
| break; |
| |
| default: |
| index = n; |
| names = (T1_String**)parser->encoding_offsets; |
| } |
| encode->char_index[n] = 0; |
| if (index) |
| { |
| T1_String* name; |
| |
| if (names) |
| name = names[index]; |
| else |
| name = (T1_String*)psnames->adobe_std_strings(index); |
| |
| if ( name ) |
| { |
| T1_Int len = strlen(name); |
| |
| /* lookup glyph index from name */ |
| for ( m = 0; m < num_glyphs; m++ ) |
| { |
| if ( strncmp( type1->glyph_names[m], name, len ) == 0 ) |
| { |
| encode->char_index[n] = m; |
| break; |
| } |
| } |
| |
| if ( n < encode->code_first ) encode->code_first = n; |
| if ( n > encode->code_last ) encode->code_last = n; |
| } |
| } |
| } |
| |
| parser->encoding_type = t1_encoding_none; |
| FREE( parser->encoding_names ); |
| FREE( parser->encoding_lengths ); |
| FREE( parser->encoding_offsets ); |
| } |
| |
| return T1_Err_Ok; |
| } |
| |
| |
| |
| |
| |
| LOCAL_FUNC |
| T1_Error Parse_T1_FontProgram( T1_Parser* parser ) |
| { |
| T1_Error error; |
| T1_Font* type1 = &parser->face->type1; |
| |
| for (;;) |
| { |
| T1_Token token; |
| T1_Token* top; |
| T1_DictState dict_state; |
| T1_Int dict_index; |
| |
| error = Next_T1_Token( parser, &token ); |
| top = parser->top; |
| dict_index = parser->state_index; |
| dict_state = parser->state_stack[ dict_index ]; |
| |
| switch ( token.kind ) |
| { |
| /* A keyword was detected */ |
| case tok_keyword: |
| switch (token.kind2) |
| { |
| case key_dict: |
| |
| switch (dict_state) |
| { |
| case dict_none: |
| /* All right, we're beggining the font dictionary */ |
| /* check that we only have one number argument, then */ |
| /* consume the 'begin' and change to 'dict_font' */ |
| /* state.. */ |
| error = Expect_Dict_Arguments( parser, 1, tok_error, |
| dict_font, 0 ); |
| if (error) goto Exit; |
| |
| /* clear stack from all the previous content. This */ |
| /* could be some stupid Postscript code ... */ |
| parser->top = parser->stack; |
| break; |
| |
| |
| case dict_font: |
| /* This must be the /FontInfo dictionary, so check */ |
| /* That we have at least two arguments, that they */ |
| /* are "/FontInfo" and a number, then change the */ |
| /* dictionary state.. */ |
| error = Expect_Dict_Arguments( parser, 2, imm_FontInfo, |
| dict_fontinfo, 0 ); |
| if (error) goto Exit; |
| break; |
| |
| |
| case dict_none2: |
| error = Expect_Dict_Arguments( parser, 2, imm_Private, |
| dict_private, 0 ); |
| if (error) goto Exit; |
| break; |
| |
| |
| case dict_private: |
| { |
| T1_Face face = parser->face; |
| T1_Int count; |
| |
| error = Expect_Dict_Arguments( parser, 2, imm_CharStrings, |
| dict_charstrings, &count ); |
| if (error) goto Exit; |
| |
| type1->num_glyphs = count; |
| error = T1_New_Table( &parser->table, count*2, face->root.memory ); |
| if (error) goto Exit; |
| |
| /* record '.notdef' as the first glyph in the font */ |
| error = T1_Add_Table( &parser->table, 0, (T1_Byte*)".notdef", 8 ); |
| parser->cur_name = 1; |
| /* XXXXX : DO SOMETHING HERE */ |
| } |
| break; |
| |
| default: |
| /* All other uses are invalid */ |
| FT_ERROR(( "T1.Parse: invalid use of the 'dict' keyword\n" )); |
| goto Syntax_Error; |
| } |
| break; |
| |
| |
| case key_array: |
| /* Are we in an array yet ? Is so, raise an error */ |
| switch (dict_state) |
| { |
| case dict_encoding: case dict_subrs: |
| case dict_othersubrs: case dict_charstrings: |
| case dict_unknown_array: |
| FT_ERROR(( "T1.Parse.array: nested array definitions\n" )); |
| goto Syntax_Error; |
| |
| default: |
| ; |
| } |
| error = Expect_Array_Arguments( parser ); |
| if (error) goto Exit; |
| break; |
| |
| |
| case key_ND: |
| case key_NP: |
| case key_def: |
| /* Are we in an array ? If so, finalise it.. */ |
| switch ( dict_state ) |
| { |
| case dict_encoding: /* finish encoding array */ |
| { |
| /* copy table names to the face object */ |
| T1_Done_Table( &parser->table ); |
| |
| parser->encoding_names = parser->table.block; |
| parser->encoding_lengths = parser->table.lengths; |
| parser->encoding_offsets = parser->table.elements; |
| |
| parser->state_index--; |
| } |
| break; |
| |
| |
| case dict_subrs: |
| { |
| /* copy recorder sub-routines */ |
| T1_Done_Table( &parser->table ); |
| |
| parser->subrs = parser->table.block; |
| type1->subrs = parser->table.elements; |
| type1->subrs_len = parser->table.lengths; |
| |
| parser->state_index--; |
| } |
| break; |
| |
| case dict_charstrings: |
| case dict_othersubrs: |
| case dict_unknown_array: |
| FT_ERROR(( "T1.Parser.def: unsupported array\n" )); |
| goto Syntax_Error; |
| break; |
| |
| default: /* normal 'def' processing */ |
| { |
| /* Check that we have sufficient operands in the stack */ |
| if ( top >= parser->stack+2 ) |
| { |
| /* Now check that the first operand is an immediate */ |
| /* If so, call the appropriate "def" routine based */ |
| /* on the current parser state.. */ |
| if ( top[-2].kind == tok_immediate ) |
| { |
| parser->top -= 2; |
| parser->args = parser->top + 1; |
| error = def_funcs[dict_state](parser); |
| } |
| else |
| { |
| /* This is an error, but some fonts contain some */ |
| /* stupid Postscript code. We simply ignore */ |
| /* an invalid 'def' by clearing the stack */ |
| #if 0 |
| FT_ERROR(( "T1.Parse.def: immediate expected\n" )); |
| goto Syntax_Error; |
| #else |
| parser->top = parser->stack; |
| #endif |
| } |
| } |
| else |
| { |
| FT_ERROR(( "T1.Parse.def: not enough arguments\n" )); |
| goto Stack_Underflow; |
| } |
| } |
| } |
| break; |
| |
| |
| |
| case key_index: |
| if ( top <= parser->stack ) |
| { |
| FT_ERROR(( "T1.Parse.index: not enough arguments\n" )); |
| goto Stack_Underflow; |
| } |
| |
| /* simply ignore ?? */ |
| parser->top --; |
| break; |
| |
| |
| case key_put: |
| /* Check that we have sufficient operands in stack */ |
| if ( top < parser->stack+2 ) |
| { |
| FT_ERROR(( "T1.Parse.put: not enough arguments\n" )); |
| goto Stack_Underflow; |
| } |
| |
| parser->top -= 2; |
| parser->args = parser->top; |
| switch (dict_state) |
| { |
| case dict_encoding: |
| error = Do_Put_Encoding( parser ); |
| if (error) goto Exit; |
| break; |
| |
| case dict_unknown_array: /* ignore the put */ |
| break; |
| |
| default: |
| #if 0 |
| FT_ERROR(( "T1.Parse.put: invalid context\n" )); |
| goto Syntax_Error; |
| #else |
| /* invalid context, simply ignore the put and */ |
| /* clear the stack (stupid Postscript code..) */ |
| FT_TRACE4(( "T1.Parse.put: invalid context. ignored.\n" )); |
| parser->top = parser->stack; |
| #endif |
| } |
| break; |
| |
| |
| |
| case key_RD: |
| /* Check that we have sufficient operands in stack */ |
| if ( top < parser->stack+2 ) |
| { |
| FT_ERROR(( "T1.Parse.RD: not enough arguments\n" )); |
| goto Stack_Underflow; |
| } |
| |
| parser->top -= 2; |
| parser->args = parser->top; |
| switch (dict_state) |
| { |
| case dict_subrs: |
| error = Do_RD_Subrs( parser ); |
| if (error) goto Exit; |
| break; |
| |
| case dict_charstrings: |
| error = Do_RD_Charstrings( parser ); |
| if (error) goto Exit; |
| break; |
| |
| default: |
| FT_ERROR(( "T1.Parse.RD: invalid context\n" )); |
| goto Syntax_Error; |
| } |
| break; |
| |
| |
| |
| case key_end: |
| /* Were we in a dictionary or in an array ? */ |
| if ( dict_index <= 0 ) |
| { |
| FT_ERROR(( "T1.Parse.end: no dictionary defined\n" )); |
| goto Syntax_Error; |
| } |
| |
| switch (dict_state) |
| { |
| /* Jump to the private dictionary if we're closing the */ |
| /* /Font dictionary.. */ |
| case dict_font: |
| goto Open_Private; |
| |
| /* Exit the parser when closing the CharStrings dictionary */ |
| case dict_charstrings: |
| return Finalise_Parsing( parser ); |
| |
| default: |
| /* Pop the current dictionary state and return to previous */ |
| /* one. Consume the "def".. */ |
| |
| /* Because some buggy fonts (BitStream) have incorrect */ |
| /* syntax, we never escape from the private dictionary */ |
| if (dict_state != dict_private) |
| parser->state_index--; |
| |
| /* many fonts use a NP instead of def or put, so */ |
| /* we simply ignore the nest token.. */ |
| #if 0 |
| error = Expect_Keyword2( parser, key_def, key_put ); |
| if (error) goto Exit; |
| #else |
| (void)Expect_Keyword2( parser, key_def, key_put ); |
| #endif |
| } |
| break; |
| |
| |
| |
| case key_for: |
| /* check that we have four arguments, and simply */ |
| /* ignore them.. */ |
| if ( top - parser->stack < 4 ) |
| { |
| FT_ERROR(( "T1.Parse.for: not enough arguments\n" )); |
| goto Stack_Underflow; |
| } |
| |
| parser->top -= 4; |
| break; |
| |
| |
| |
| case key_currentdict: |
| |
| Open_Private: |
| parser->state_index = 0; |
| parser->state_stack[0] = dict_none2; |
| error = Open_PrivateDict( parser->tokenizer ); |
| if (error) goto Exit; |
| break; |
| |
| |
| case key_true: |
| case key_false: |
| case key_StandardEncoding: |
| case key_ExpertEncoding: |
| goto Push_Element; |
| |
| |
| default: |
| FT_ERROR(( "T1.Parser: invalid keyword in context\n" )); |
| error = T1_Err_Syntax_Error; |
| } |
| break; |
| |
| /* A number was detected */ |
| case tok_string: |
| case tok_program: |
| case tok_immediate: |
| case tok_array: |
| case tok_hexarray: |
| case tok_any: |
| case tok_number: /* push number on stack */ |
| |
| Push_Element: |
| if ( top >= parser->limit ) |
| { |
| error = T1_Err_Stack_Overflow; |
| goto Exit; |
| } |
| else |
| *parser->top++ = token; |
| break; |
| |
| /* anything else is an error per se the spec, but we */ |
| /* frequently encountre stupid postscript code in fonts, */ |
| /* so just ignore them.. */ |
| default: |
| error = T1_Err_Ok; /* ignore token */ |
| } |
| |
| if (error) |
| return error; |
| } |
| Exit: |
| return error; |
| |
| Syntax_Error: |
| return T1_Err_Syntax_Error; |
| |
| Stack_Underflow: |
| return T1_Err_Stack_Underflow; |
| } |
| |