forked from minhngoc25a/freetype2
1595 lines
51 KiB
C
1595 lines
51 KiB
C
/***************************************************************************/
|
|
/* */
|
|
/* 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 */
|