--- /dev/null
+/***************************************************************************/
+/* */
+/* t1load.c */
+/* */
+/* Type 1 font loader (body). */
+/* */
+/* Copyright 1996-2000 by */
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */
+/* */
+/* 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 <freetype/config/ftconfig.h>
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/t1types.h>
+
+
+#ifdef FT_FLAT_COMPILE
+
+#include "t1tokens.h"
+#include "t1parse.h"
+
+#else
+
+#include <type1/t1tokens.h>
+#include <type1/t1parse.h>
+
+#endif
+
+
+#include <stdio.h>
+
+#include <string.h> /* for strncpy(), strncmp(), strlen() */
+
+
+ /*************************************************************************/
+ /* */
+ /* 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_t1load
+
+
+ typedef FT_Error (*T1_Parse_Func)( T1_Parser* parser );
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Init_T1_Parser */
+ /* */
+ /* <Description> */
+ /* Initializes a given parser object to build a given T1_Face. */
+ /* */
+ /* <InOut> */
+ /* parser :: A handle to the newly built parser object. */
+ /* */
+ /* <Input> */
+ /* face :: A handle to the target Type 1 face object. */
+ /* */
+ /* tokenizer :: A handle to the target Type 1 token manager. */
+ /* */
+ 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.private_dict.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 :: A handle to the source parser. */
+ /* */
+ /* <Output> */
+ /* token :: The extracted token descriptor. */
+ /* */
+ /* <Return> */
+ /* FreeTyoe error code. 0 means success. */
+ /* */
+ LOCAL_FUNC
+ FT_Error Next_T1_Token( T1_Parser* parser,
+ T1_Token* token )
+ {
+ FT_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:
+ ;
+ }
+
+#if defined( FT_DEBUG_LEVEL_ERROR ) || defined( FT_DEBUG_LEVEL_TRACE )
+
+ /* Dump the token when requested. This feature is only available */
+ /* in the `error' and `trace' debug levels. */
+ if ( parser->dump_tokens )
+ {
+ FT_String temp_string[128];
+ FT_Int len;
+
+
+ len = token->len;
+ if ( len > 127 )
+ len = 127;
+ strncpy( temp_string,
+ (FT_String*)tokzer->base + token->start,
+ len );
+ temp_string[len] = '\0';
+ FT_ERROR(( "%s\n", temp_string ));
+ }
+
+#endif /* FT_DEBUG_LEVEL_ERROR or FT_DEBUG_LEVEL_TRACE */
+
+ return T1_Err_Ok;
+ }
+
+
+ static
+ FT_Error Expect_Keyword( T1_Parser* parser,
+ T1_TokenType keyword )
+ {
+ T1_Token token;
+ FT_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(( "Expect_Keyword: keyword `%s' expected.\n",
+ t1_keywords[keyword - key_first_] ));
+ }
+
+ Exit:
+ return error;
+ }
+
+
+ static
+ FT_Error Expect_Keyword2( T1_Parser* parser,
+ T1_TokenType keyword1,
+ T1_TokenType keyword2 )
+ {
+ T1_Token token;
+ FT_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(( "Expect_Keyword2: 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;
+ FT_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, FT_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;
+ break;
+
+ default:
+ parser->encoding_type = t1_encoding_standard;
+ break;
+ }
+ }
+ else
+ {
+ FT_ERROR(( "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' if in the Font dictionary. Its */
+ /* purpose is to build the T1_Face attributes directly from the */
+ /* stream. */
+ /* */
+ /* <InOut> */
+ /* parser :: A handle to the current parser. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static
+ FT_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;
+ FT_Error error;
+ FT_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 = (FT_Byte)CopyInteger( parser );
+ break;
+
+ case imm_FontType:
+ type1->font_type = (FT_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->private_dict.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' if in the FontInfo dictionary. Its */
+ /* purpose is to build the T1_FontInfo structure directly from the */
+ /* stream. */
+ /* */
+ /* <InOut> */
+ /* parser :: A handle to the current parser. */
+ /* */
+ /* <Return> */
+ /* FreeTyoe error code. 0 means success. */
+ /* */
+ static
+ FT_Error Do_Def_FontInfo( T1_Parser* parser )
+ {
+ T1_Token* top = parser->top;
+ T1_FontInfo* info = &parser->face->type1.font_info;
+
+
+ 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 = (FT_Short)CopyInteger( parser );
+ break;
+
+ case imm_UnderlineThickness:
+ info->underline_thickness = (FT_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' if in the Private dictionary. Its */
+ /* purpose is to build the T1_Private structure directly from the */
+ /* stream. */
+ /* */
+ /* <InOut> */
+ /* parser :: A handle to the current parser. */
+ /* */
+ /* <Return> */
+ /* FreeTyoe error code. 0 means success. */
+ /* */
+ static
+ FT_Error Do_Def_Private( T1_Parser* parser )
+ {
+ T1_Token* top = parser->top;
+ T1_Private* priv = &parser->face->type1.private_dict;
+
+
+ switch ( top[0].kind2 )
+ {
+ /* Ignore the definitions of RD, NP, ND, and their alternate forms */
+ case imm_RD:
+ case imm_RD_alternate:
+ case imm_ND:
+ case imm_ND_alternate:
+ case imm_NP:
+ case imm_NP_alternate:
+ parser->error = T1_Err_Ok;
+ break;
+
+ case imm_BlueValues:
+ CopyArray( parser, &priv->num_blue_values,
+ 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, 0x10000L );
+ 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, (FT_Short*)&priv->standard_width, 1 );
+ break;
+
+ case imm_StdVW:
+ CopyArray( parser, 0, (FT_Short*)&priv->standard_height, 1 );
+ break;
+
+ case imm_StemSnapH:
+ CopyArray( parser, &priv->num_snap_widths,
+ priv->snap_widths, 12 );
+ break;
+
+ case imm_StemSnapV:
+ CopyArray( parser, &priv->num_snap_heights,
+ priv->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 */
+ /* used for the `def' keyword if in the `encoding', `subrs', */
+ /* `othersubrs', and `charstrings' dictionary states. */
+ /* */
+ /* <InOut> */
+ /* parser :: A handle to the current parser. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static
+ FT_Error Do_Def_Error( T1_Parser* parser )
+ {
+ FT_ERROR(( "Do_Def_Error:" ));
+ FT_ERROR(( " `def' keyword encountered in bad dictionary/array\n" ));
+
+ parser->error = T1_Err_Syntax_Error;
+
+ return parser->error;
+ }
+
+
+ static
+ FT_Error Do_Def_Ignore( T1_Parser* parser )
+ {
+ FT_UNUSED( 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' if 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. */
+ /* */
+ /* <InOut> */
+ /* parser :: A handle to the current parser. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static
+ FT_Error Do_Put_Encoding( T1_Parser* parser )
+ {
+ FT_Error error = T1_Err_Ok;
+ T1_Face face = parser->face;
+ T1_Token* top = parser->top;
+ T1_Encoding* encode = &face->type1.encoding;
+ FT_Int index;
+
+
+ /* record and check the character code */
+ if ( top[0].kind != tok_number )
+ {
+ FT_TRACE4(( "Do_Put_Encoding: number expected\n" ));
+ goto Syntax_Error;
+ }
+ index = (FT_Int)CopyInteger( parser );
+ if ( parser->error )
+ return parser->error;
+
+ if ( index < 0 || index >= encode->num_chars )
+ {
+ FT_TRACE4(( "Do_Put_Encoding: invalid character code\n" ));
+ goto Syntax_Error;
+ }
+
+ /* record the immediate name */
+ if ( top[1].kind != tok_immediate )
+ {
+ FT_TRACE4(( "Do_Put_Encoding: 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
+ {
+ FT_String temp_name[128];
+ T1_Token* token = top + 1;
+ FT_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,
+ (FT_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(( "Do_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 an `RD' if in the Subrs dictionary. It */
+ /* simply records the array of bytecodes/charstrings corresponding to */
+ /* the sub-routine. */
+ /* */
+ /* <InOut> */
+ /* parser :: A handle to the current parser. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static
+ FT_Error Do_RD_Subrs( T1_Parser* parser )
+ {
+ FT_Error error = T1_Err_Ok;
+ T1_Face face = parser->face;
+ T1_Token* top = parser->top;
+ T1_Tokenizer tokzer = parser->tokenizer;
+ FT_Int index, count;
+
+
+ /* record and check the character code */
+ if ( top[0].kind != tok_number ||
+ top[1].kind != tok_number )
+ {
+ FT_ERROR(( "Do_RD_Subrs: number expected\n" ));
+ goto Syntax_Error;
+ }
+ index = (FT_Int)CopyInteger( parser );
+ error = parser->error;
+ if ( error )
+ goto Exit;
+
+ count = (FT_Int)CopyInteger( parser );
+ error = parser->error;
+ if ( error )
+ goto Exit;
+
+ if ( index < 0 || index >= face->type1.num_subrs )
+ {
+ FT_ERROR(( "Do_RD_Subrs: invalid character code\n" ));
+ goto Syntax_Error;
+ }
+
+ /* decrypt charstring and skip it */
+ {
+ FT_Byte* base = tokzer->base + tokzer->cursor;
+
+
+ tokzer->cursor += count;
+
+ /* some fonts use a value of -1 for lenIV to indicate that */
+ /* the charstrings are unencoded. */
+ /* */
+ /* Thanks to Tom Kacvinsky for pointing this out. */
+ /* */
+ if ( face->type1.private_dict.lenIV >= 0 )
+ {
+ t1_decrypt( base, count, 4330 );
+
+ base += face->type1.private_dict.lenIV;
+ count -= face->type1.private_dict.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 an `RD' if in the CharStrings dictionary. */
+ /* It simply records the array of bytecodes/charstrings corresponding */
+ /* to the glyph program string. */
+ /* */
+ /* <InOut> */
+ /* parser :: A handle to the current parser. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ static
+ FT_Error Do_RD_Charstrings( T1_Parser* parser )
+ {
+ FT_Error error = T1_Err_Ok;
+ T1_Face face = parser->face;
+ T1_Token* top = parser->top;
+ T1_Tokenizer tokzer = parser->tokenizer;
+ FT_Int index, count;
+
+
+ /* check the character name argument */
+ if ( top[0].kind != tok_immediate )
+ {
+ FT_ERROR(( "Do_RD_Charstrings: immediate character name expected\n" ));
+ goto Syntax_Error;
+ }
+
+ /* check the count argument */
+ if ( top[1].kind != tok_number )
+ {
+ FT_ERROR(( "Do_RD_Charstrings: number expected\n" ));
+ goto Syntax_Error;
+ }
+
+ parser->args++;
+ count = (FT_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
+ {
+ FT_String temp_name[128];
+ T1_Token* token = top;
+ FT_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,
+ (FT_Byte*)temp_name, len + 1 );
+ if ( error )
+ goto Exit;
+ }
+
+ /* decrypt and record charstring, then skip them */
+ {
+ FT_Byte* base = tokzer->base + tokzer->cursor;
+
+
+ tokzer->cursor += count; /* skip */
+
+ if ( face->type1.private_dict.lenIV >= 0 )
+ {
+ t1_decrypt( base, count, 4330 );
+
+ base += face->type1.private_dict.lenIV;
+ count -= face->type1.private_dict.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
+ FT_Error Expect_Dict_Arguments( T1_Parser* parser,
+ FT_Int num_args,
+ T1_TokenType immediate,
+ T1_DictState new_state,
+ FT_Int* count )
+ {
+ /* check that we have enough arguments in the stack, including */
+ /* the `dict' keyword */
+ if ( parser->top - parser->stack < num_args )
+ {
+ FT_ERROR(( "Expect_Dict_Arguments: 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(( "Expect_Dict_Arguments: 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(( "Expect_Dict_Arguments:" ));
+ FT_ERROR(( " 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
+ FT_Error Expect_Array_Arguments( T1_Parser* parser )
+ {
+ T1_Token* top = parser->top;
+ FT_Error error = T1_Err_Ok;
+ T1_DictState new_state;
+ FT_Int count;
+ T1_Face face = parser->face;
+ FT_Memory memory = face->root.memory;
+
+
+ /* Check arguments format */
+ if ( top - parser->stack < 2 )
+ {
+ FT_ERROR(( "Expect_Array_Arguments: 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(( "Expect_Array_Arguments:" ));
+ FT_ERROR(( " first argument must be an immediate name\n" ));
+ goto Syntax_Error;
+ }
+
+ if ( top[1].kind != tok_number )
+ {
+ FT_ERROR(( "Expect_Array_Arguments:" ));
+ FT_ERROR(( " second argument must be a number\n" ));
+ goto Syntax_Error;
+ }
+
+ count = (FT_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 indices. The table of */
+ /* character names is allocated through init_t1_recorder(). */
+ if ( ALLOC_ARRAY( encode->char_index, count, FT_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
+ FT_Error Finalize_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;
+
+ FT_Int num_glyphs;
+ FT_Int n;
+ FT_Error error;
+
+
+ num_glyphs = type1->num_glyphs = parser->cur_name;
+
+ /* allocate glyph names and charstrings arrays */
+ if ( ALLOC_ARRAY( type1->glyph_names, num_glyphs, FT_String* ) ||
+ ALLOC_ARRAY( type1->charstrings, num_glyphs, FT_Byte* ) ||
+ ALLOC_ARRAY( type1->charstrings_len, num_glyphs, FT_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] = (FT_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(( "Finalize_Parsing: `PSNames' module missing!\n" ));
+ return T1_Err_Unimplemented_Feature;
+ }
+
+ /* compute encoding if required */
+ if ( parser->encoding_type == t1_encoding_none )
+ {
+ FT_ERROR(( "Finalize_Parsing: no encoding specified in font file\n" ));
+ return T1_Err_Syntax_Error;
+ }
+
+ {
+ FT_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++ )
+ {
+ FT_String** names;
+ FT_Int index;
+ FT_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 = (FT_String**)parser->encoding_offsets;
+ }
+
+ encode->char_index[n] = 0;
+
+ if ( index )
+ {
+ FT_String* name;
+
+
+ if ( names )
+ name = names[index];
+ else
+ name = (FT_String*)psnames->adobe_std_strings(index);
+
+ if ( name )
+ {
+ FT_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;
+ }
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* Parse_T1_FontProgram */
+ /* */
+ /* <Description> */
+ /* Parses a given Type 1 font file and builds its face object. */
+ /* */
+ /* <InOut> */
+ /* parser :: A handle to the target parser object. */
+ /* */
+ /* <Return> */
+ /* FreeType error code. 0 means success. */
+ /* */
+ /* <Note> */
+ /* The parser contains a handle to the target face object. */
+ /* */
+ LOCAL_FUNC
+ FT_Error Parse_T1_FontProgram( T1_Parser* parser )
+ {
+ FT_Error error;
+ T1_Font* type1 = &parser->face->type1;
+
+
+ for (;;)
+ {
+ T1_Token token;
+ T1_Token* top;
+ T1_DictState dict_state;
+ FT_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 has been detected */
+ case tok_keyword:
+ switch ( token.kind2 )
+ {
+ case key_dict:
+ switch ( dict_state )
+ {
+ case dict_none:
+ /* All right, we are beginning 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;
+ FT_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,
+ (FT_Byte*)".notdef", 8 );
+ parser->cur_name = 1;
+ /* XXX: DO SOMETHING HERE */
+ }
+ break;
+
+ default:
+ /* All other uses are invalid */
+ FT_ERROR(( "Parse_T1_FontProgram:" ));
+ FT_ERROR(( " invalid use of `dict' keyword\n" ));
+ goto Syntax_Error;
+ }
+ break;
+
+ case key_array:
+ /* Are we in an array yet? If 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(( "Parse_T1_FontProgram: 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, finalize 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;
+ type1->subrs_block = parser->table.block;
+
+ parser->state_index--;
+ break;
+
+ case dict_charstrings:
+ case dict_othersubrs:
+ case dict_unknown_array:
+ FT_ERROR(( "Parse_T1_FontProgram: 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 */
+ /* stupid Postscript code. We simply ignore */
+ /* an invalid `def' by clearing the stack. */
+#if 0
+ FT_ERROR(( "Parse_T1_FontProgram: immediate expected\n" ));
+ goto Syntax_Error;
+#else
+ parser->top = parser->stack;
+#endif
+ }
+ }
+ else
+ {
+ FT_ERROR(( "Parse_T1_FontProgram: not enough arguments\n" ));
+ goto Stack_Underflow;
+ }
+ }
+ break;
+
+ case key_index:
+ if ( top <= parser->stack )
+ {
+ FT_ERROR(( "Parse_T1_FontProgram: 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(( "Parse_T1_FontProgram: 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(( "Parse_T1_FontProgram: invalid context\n" ));
+ goto Syntax_Error;
+#else
+ /* invalid context; simply ignore the `put' and */
+ /* clear the stack (stupid Postscript code) */
+ FT_TRACE4(( "Parse_T1_FontProgram: 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(( "Parse_T1_FontProgram: 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(( "Parse_T1_FontProgram: 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(( "Parse_T1_FontProgram: no dictionary defined\n" ));
+ goto Syntax_Error;
+ }
+
+ switch ( dict_state )
+ {
+ /* jump to the private dictionary if we are closing the */
+ /* `/Font' dictionary */
+ case dict_font:
+ goto Open_Private;
+
+ /* exit the parser when closing the CharStrings dictionary */
+ case dict_charstrings:
+ return Finalize_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 `NP' instead of `def' or `put', so */
+ /* we simply ignore the next 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(( "Parse_T1_FontProgram: 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(( "Parse_T1_FontProgram:" ));
+ FT_ERROR(( " invalid keyword in context\n" ));
+ error = T1_Err_Syntax_Error;
+ }
+ break;
+
+ /* check for the presence of `/BlendAxisTypes' -- we cannot deal */
+ /* with multiple master fonts, so we must return a correct error */
+ /* code to allow another driver to load them */
+ case tok_immediate:
+ if ( token.kind2 == imm_BlendAxisTypes )
+ {
+ error = FT_Err_Unknown_File_Format;
+ goto Exit;
+ }
+ /* fallthrough */
+
+ /* A number was detected */
+ case tok_string:
+ case tok_program:
+ 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 encounter 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;
+ }
+
+
+/* END */