1999-12-17 00:11:37 +01:00
|
|
|
/*******************************************************************
|
|
|
|
*
|
|
|
|
* t1tokens.c
|
|
|
|
*
|
|
|
|
* Type 1 tokenizer
|
|
|
|
*
|
|
|
|
* Copyright 1996 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.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* The tokenizer is in charge of loading and reading a Type1 font
|
|
|
|
* file (either in PFB or PFA format), and extract successive tokens
|
|
|
|
* and keywords from its two streams (i.e. the font program, and the
|
|
|
|
* private dictionary).
|
|
|
|
*
|
|
|
|
* Eexec decryption is performed automatically when entering the
|
|
|
|
* private dictionary, or when retrieving char strings..
|
|
|
|
*
|
|
|
|
******************************************************************/
|
|
|
|
|
2000-05-11 20:23:52 +02:00
|
|
|
#include <freetype/internal/ftstream.h>
|
|
|
|
#include <freetype/internal/ftdebug.h>
|
1999-12-17 00:11:37 +01:00
|
|
|
|
|
|
|
#include <t1tokens.h>
|
|
|
|
#include <t1load.h>
|
|
|
|
|
|
|
|
#undef READ_BUFFER_INCREMENT
|
|
|
|
#define READ_BUFFER_INCREMENT 0x400
|
|
|
|
|
|
|
|
#undef FT_COMPONENT
|
|
|
|
#define FT_COMPONENT trace_t1load
|
|
|
|
|
|
|
|
/* array of Type1 keywords supported by this engine. This table places */
|
|
|
|
/* the keyword in lexicographical order. It should always correspond */
|
|
|
|
/* to the enums key_XXXX !! */
|
|
|
|
/* */
|
|
|
|
const char* t1_keywords[ key_max - key_first_ ] =
|
|
|
|
{
|
|
|
|
"-|", "ExpertEncoding", "ND", "NP", "RD", "StandardEncoding", "array",
|
|
|
|
"begin", "closefile", "currentdict", "currentfile", "def", "dict", "dup",
|
|
|
|
"eexec", "end", "executeonly", "false", "for", "index", "noaccess",
|
|
|
|
"put", "readonly", "true", "userdict", "|", "|-"
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char* t1_immediates[ imm_max - imm_first_ ] =
|
|
|
|
{
|
2000-05-26 04:16:06 +02:00
|
|
|
"-|", ".notdef", "BlendAxisTypes", "BlueFuzz", "BlueScale", "BlueShift",
|
|
|
|
"BlueValues", "CharStrings", "Encoding", "FamilyBlues", "FamilyName",
|
|
|
|
"FamilyOtherBlues", "FID", "FontBBox", "FontID", "FontInfo", "FontMatrix",
|
|
|
|
"FontName", "FontType", "ForceBold", "FullName", "ItalicAngle",
|
|
|
|
"LanguageGroup", "Metrics", "MinFeature", "ND", "NP", "Notice",
|
|
|
|
"OtherBlues", "OtherSubrs", "PaintType", "Private", "RD", "RndStemUp",
|
|
|
|
"StdHW", "StdVW", "StemSnapH", "StemSnapV", "StrokeWidth", "Subrs",
|
|
|
|
"UnderlinePosition", "UnderlineThickness", "UniqueID", "Weight",
|
|
|
|
"isFixedPitch", "lenIV", "password", "version", "|", "|-"
|
1999-12-17 00:11:37 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* lexicographic comparison of two strings */
|
|
|
|
static
|
|
|
|
int lexico_strcmp( const char* str1,
|
|
|
|
int str1_len,
|
|
|
|
const char* str2 )
|
|
|
|
{
|
|
|
|
int c2 = 0;
|
|
|
|
|
|
|
|
for ( ; str1_len > 0; str1_len-- )
|
|
|
|
{
|
|
|
|
int c1, diff;
|
|
|
|
|
|
|
|
c1 = *str1++;
|
|
|
|
c2 = *str2++;
|
|
|
|
|
|
|
|
diff = c1 - c2;
|
|
|
|
if (diff) return diff;
|
|
|
|
};
|
|
|
|
return -*str2;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Find a given token/name, perform binary search */
|
|
|
|
static
|
|
|
|
int Find_Name( char* base, int length,
|
|
|
|
const char** table, int table_len )
|
|
|
|
{
|
|
|
|
/* performs a binary search */
|
|
|
|
|
|
|
|
T1_Int left, right;
|
|
|
|
|
|
|
|
left = 0;
|
|
|
|
right = table_len-1;
|
|
|
|
|
|
|
|
while ( right-left > 1 )
|
|
|
|
{
|
|
|
|
T1_Int middle = left + (( right-left ) >> 1);
|
|
|
|
T1_Int cmp;
|
|
|
|
|
|
|
|
cmp = lexico_strcmp( base, length, table[middle] );
|
|
|
|
if (!cmp) return middle;
|
|
|
|
|
|
|
|
if ( cmp < 0 )
|
|
|
|
right = middle;
|
|
|
|
else
|
|
|
|
left = middle;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !lexico_strcmp( base, length, table[left ] ) ) return left;
|
|
|
|
if ( !lexico_strcmp( base, length, table[right] ) ) return right;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* read the small PFB section header */
|
|
|
|
static
|
|
|
|
T1_Error Read_PFB_Tag( FT_Stream stream,
|
|
|
|
T1_UShort* atag,
|
|
|
|
T1_ULong* asize )
|
|
|
|
{
|
|
|
|
T1_UShort tag;
|
|
|
|
T1_ULong size;
|
|
|
|
T1_Error error;
|
|
|
|
|
|
|
|
FT_TRACE2(( "Read_PFB_Tag : reading\n" ));
|
|
|
|
|
|
|
|
if ( ACCESS_Frame( 6L ) ) return error;
|
|
|
|
|
|
|
|
tag = GET_UShort();
|
|
|
|
size = GET_ULong();
|
|
|
|
|
|
|
|
FORGET_Frame();
|
|
|
|
|
|
|
|
*atag = tag;
|
|
|
|
*asize = ( (size & 0xFF) << 24 ) |
|
|
|
|
( ((size >> 8) & 0xFF) << 16 ) |
|
|
|
|
( ((size >> 16) & 0xFF) << 8 ) |
|
|
|
|
( ((size >> 24) & 0xFF) );
|
|
|
|
|
|
|
|
FT_TRACE2(( " tag = %04x\n", tag ));
|
|
|
|
FT_TRACE4(( " asze = %08x\n", size ));
|
|
|
|
FT_TRACE2(( " size = %08x\n", *asize ));
|
|
|
|
|
|
|
|
return T1_Err_Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
T1_Error grow( T1_Tokenizer tokzer )
|
|
|
|
{
|
|
|
|
T1_Error error;
|
|
|
|
T1_Long left_bytes;
|
|
|
|
FT_Memory memory = tokzer->memory;
|
|
|
|
|
|
|
|
left_bytes = tokzer->max - tokzer->limit;
|
|
|
|
|
|
|
|
if ( left_bytes > 0 )
|
|
|
|
{
|
|
|
|
FT_Stream stream = tokzer->stream;
|
|
|
|
|
|
|
|
if ( left_bytes > READ_BUFFER_INCREMENT )
|
|
|
|
left_bytes = READ_BUFFER_INCREMENT;
|
|
|
|
|
|
|
|
FT_TRACE2(( "Growing tokenizer buffer by %d bytes\n", left_bytes ));
|
|
|
|
|
2000-05-17 01:44:38 +02:00
|
|
|
if ( !REALLOC( tokzer->base, tokzer->limit,
|
1999-12-17 00:11:37 +01:00
|
|
|
tokzer->limit + left_bytes ) &&
|
|
|
|
!FILE_Read( tokzer->base + tokzer->limit, left_bytes ) )
|
|
|
|
tokzer->limit += left_bytes;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FT_ERROR(( "Unexpected end of Type1 fragment !!\n" ));
|
|
|
|
error = T1_Err_Invalid_File_Format;
|
|
|
|
}
|
|
|
|
|
|
|
|
tokzer->error = error;
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOCAL_FUNC
|
|
|
|
void t1_decrypt( T1_Byte* buffer,
|
|
|
|
T1_Int length,
|
|
|
|
T1_UShort seed )
|
|
|
|
{
|
|
|
|
while ( length > 0 )
|
|
|
|
{
|
|
|
|
T1_Byte plain;
|
|
|
|
|
|
|
|
plain = (*buffer ^ (seed >> 8));
|
|
|
|
seed = (*buffer+seed)*52845+22719;
|
|
|
|
*buffer++ = plain;
|
|
|
|
length--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* <Function> New_Tokenizer */
|
|
|
|
/* */
|
|
|
|
/* <Description> */
|
|
|
|
/* Creates a new tokenizer from a given input stream. This function */
|
|
|
|
/* automatically recognizes "pfa" or "pfb" files. The function */
|
|
|
|
/* "Read_Token" can then be used to extract successive tokens from */
|
|
|
|
/* the stream.. */
|
|
|
|
/* */
|
|
|
|
/* <Input> */
|
|
|
|
/* stream :: input stream */
|
|
|
|
/* */
|
|
|
|
/* <Output> */
|
|
|
|
/* tokenizer :: handle to new tokenizer object.. */
|
|
|
|
/* */
|
|
|
|
/* <Return> */
|
|
|
|
/* Type1 error code. 0 means success.. */
|
|
|
|
/* */
|
|
|
|
/* <Note> */
|
|
|
|
/* This function copies the stream handle within the object. Callers */
|
|
|
|
/* should not discard "stream". This is done by the Done_Tokenizer */
|
|
|
|
/* function.. */
|
|
|
|
/* */
|
|
|
|
|
|
|
|
LOCAL_FUNC
|
|
|
|
T1_Error New_Tokenizer( FT_Stream stream,
|
|
|
|
T1_Tokenizer* tokenizer )
|
|
|
|
{
|
|
|
|
FT_Memory memory = stream->memory;
|
|
|
|
T1_Tokenizer tokzer;
|
|
|
|
T1_Error error;
|
|
|
|
T1_UShort tag;
|
|
|
|
T1_ULong size;
|
|
|
|
|
|
|
|
T1_Byte* tok_base;
|
|
|
|
T1_ULong tok_limit;
|
|
|
|
T1_ULong tok_max;
|
|
|
|
|
|
|
|
*tokenizer = 0;
|
|
|
|
|
|
|
|
/* allocate object */
|
|
|
|
if ( FILE_Seek( 0L ) ||
|
|
|
|
ALLOC( tokzer, sizeof(*tokzer) ) )
|
|
|
|
return error;
|
|
|
|
|
|
|
|
tokzer->stream = stream;
|
|
|
|
tokzer->memory = stream->memory;
|
|
|
|
|
|
|
|
tokzer->in_pfb = 0;
|
|
|
|
tokzer->in_private = 0;
|
|
|
|
|
|
|
|
tok_base = 0;
|
|
|
|
tok_limit = 0;
|
|
|
|
tok_max = stream->size;
|
|
|
|
|
|
|
|
error = Read_PFB_Tag( stream, &tag, &size );
|
|
|
|
if (error) goto Fail;
|
|
|
|
|
|
|
|
if ( tag != 0x8001 )
|
|
|
|
{
|
|
|
|
/* assume that it is a PFA file - an error will be produced later */
|
|
|
|
/* if a character with value > 127 is encountered.. */
|
|
|
|
|
|
|
|
/* rewind to start of file */
|
|
|
|
if ( FILE_Seek(0L) ) goto Fail;
|
|
|
|
|
|
|
|
size = stream->size;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
tokzer->in_pfb = 1;
|
|
|
|
|
|
|
|
/* if it's a memory-based resource, set up pointer */
|
|
|
|
if ( !stream->read )
|
|
|
|
{
|
|
|
|
tok_base = (T1_Byte*)stream->base + stream->pos;
|
|
|
|
tok_limit = size;
|
|
|
|
tok_max = size;
|
|
|
|
|
|
|
|
/* check that the "size" field is valid */
|
|
|
|
if ( FILE_Skip(size) ) goto Fail;
|
|
|
|
}
|
|
|
|
else if ( tag == 0x8001 )
|
|
|
|
{
|
|
|
|
/* read segment in memory */
|
|
|
|
if ( ALLOC( tok_base, size ) )
|
|
|
|
goto Fail;
|
|
|
|
|
|
|
|
if ( FILE_Read( tok_base, size ) )
|
|
|
|
{
|
|
|
|
FREE( tok_base );
|
|
|
|
goto Fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
tok_limit = size;
|
|
|
|
tok_max = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
tokzer->base = tok_base;
|
|
|
|
tokzer->limit = tok_limit;
|
|
|
|
tokzer->max = tok_max;
|
|
|
|
tokzer->cursor = 0;
|
|
|
|
|
|
|
|
*tokenizer = tokzer;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
/* Now check font format, we must see a '%!PS-AdobeFont-1' */
|
|
|
|
/* or a '%!FontType' */
|
|
|
|
{
|
|
|
|
if ( 16 > tokzer->limit )
|
|
|
|
grow( tokzer );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
if ( tokzer->limit <= 16 ||
|
|
|
|
( strncmp( (const char*)tokzer->base, "%!PS-AdobeFont-1", 16 ) &&
|
|
|
|
strncmp( (const char*)tokzer->base, "%!FontType", 10 ) ) )
|
|
|
|
{
|
|
|
|
FT_TRACE2(( "Not a Type1 font\n" ));
|
2000-05-12 12:19:41 +02:00
|
|
|
error = FT_Err_Unknown_File_Format;
|
1999-12-17 00:11:37 +01:00
|
|
|
goto Fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return T1_Err_Ok;
|
|
|
|
|
|
|
|
Fail:
|
|
|
|
FREE( tokzer );
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* return the value of an hexadecimal digit */
|
|
|
|
static
|
|
|
|
int hexa_value( char c )
|
|
|
|
{
|
|
|
|
unsigned int d;
|
|
|
|
|
|
|
|
d = (unsigned int)(c-'0');
|
|
|
|
if ( d <= 9 ) return (int)d;
|
|
|
|
|
|
|
|
d = (unsigned int)(c-'a');
|
|
|
|
if ( d <= 5 ) return (int)(d+10);
|
|
|
|
|
|
|
|
d = (unsigned int)(c-'A');
|
|
|
|
if ( d <= 5 ) return (int)(d+10);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* <Function> Done_Tokenizer */
|
|
|
|
/* */
|
|
|
|
/* <Description> */
|
|
|
|
/* Closes a given tokenizer. This function will also close the */
|
|
|
|
/* stream embedded in the object.. */
|
|
|
|
/* */
|
|
|
|
/* <Input> */
|
|
|
|
/* tokenizer :: target tokenizer object */
|
|
|
|
/* */
|
|
|
|
/* <Return> */
|
|
|
|
/* Type1 error code. 0 means success.. */
|
|
|
|
/* */
|
|
|
|
|
|
|
|
LOCAL_FUNC
|
|
|
|
T1_Error Done_Tokenizer( T1_Tokenizer tokenizer )
|
|
|
|
{
|
|
|
|
FT_Memory memory = tokenizer->memory;
|
|
|
|
|
|
|
|
/* clear read buffer if needed (disk-based resources) */
|
|
|
|
if ( tokenizer->in_private || !tokenizer->stream->base )
|
|
|
|
FREE( tokenizer->base );
|
|
|
|
|
|
|
|
FREE( tokenizer );
|
|
|
|
return T1_Err_Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* <Function> Open_PrivateDict */
|
|
|
|
/* */
|
|
|
|
/* <Description> */
|
|
|
|
/* This function must be called to set the tokenizer to the private */
|
|
|
|
/* section of the Type1 file. It recognizes automatically the */
|
|
|
|
/* the kind of eexec encryption used (ascii or binary).. */
|
|
|
|
/* */
|
|
|
|
/* <Input> */
|
|
|
|
/* tokenizer :: target tokenizer object */
|
|
|
|
/* lenIV :: value of the "lenIV" variable.. */
|
|
|
|
/* */
|
|
|
|
/* <Return> */
|
|
|
|
/* Type1 error code. 0 means success.. */
|
|
|
|
/* */
|
|
|
|
|
|
|
|
LOCAL_FUNC
|
|
|
|
T1_Error Open_PrivateDict( T1_Tokenizer tokenizer )
|
|
|
|
{
|
|
|
|
T1_Tokenizer tokzer = tokenizer;
|
|
|
|
FT_Stream stream = tokzer->stream;
|
|
|
|
FT_Memory memory = tokzer->memory;
|
|
|
|
T1_Error error = 0;
|
|
|
|
|
|
|
|
T1_UShort tag;
|
|
|
|
T1_ULong size;
|
|
|
|
|
|
|
|
T1_Byte* private;
|
|
|
|
|
|
|
|
/* are we already in the private dictionary ? */
|
|
|
|
if ( tokzer->in_private )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ( tokzer->in_pfb )
|
|
|
|
{
|
|
|
|
/* in the case of the PFB format, the private dictionary can be */
|
|
|
|
/* made of several segments. We thus first read the number of */
|
|
|
|
/* segments to compute the total size of the private dictionary */
|
|
|
|
/* then re-read them into memory.. */
|
|
|
|
T1_Long start_pos = FILE_Pos();
|
|
|
|
T1_ULong private_size = 0;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
error = Read_PFB_Tag( stream, &tag, &size );
|
|
|
|
if (error || tag != 0x8002) break;
|
|
|
|
|
|
|
|
private_size += size;
|
|
|
|
|
|
|
|
if ( FILE_Skip(size) )
|
|
|
|
goto Fail;
|
|
|
|
}
|
|
|
|
while (1);
|
|
|
|
|
|
|
|
/* Check that we have a private dictionary there */
|
|
|
|
/* and allocate private dictionary buffer */
|
|
|
|
if ( private_size == 0 )
|
|
|
|
{
|
|
|
|
FT_ERROR(( "T1.Open_Private: invalid private dictionary section\n" ));
|
|
|
|
error = T1_Err_Invalid_File_Format;
|
|
|
|
goto Fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ALLOC( private, private_size ) )
|
|
|
|
goto Fail;
|
|
|
|
|
|
|
|
/* read all sections into buffer */
|
|
|
|
if ( FILE_Seek( start_pos ) )
|
|
|
|
goto Fail_Private;
|
|
|
|
|
|
|
|
private_size = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
error = Read_PFB_Tag( stream, &tag, &size );
|
|
|
|
if (error || tag != 0x8002) { error = 0; break; }
|
|
|
|
|
|
|
|
if ( FILE_Read( private + private_size, size ) )
|
|
|
|
goto Fail_Private;
|
|
|
|
|
|
|
|
private_size += size;
|
|
|
|
}
|
|
|
|
while (1);
|
|
|
|
|
|
|
|
tokzer->base = private;
|
|
|
|
tokzer->cursor = 0;
|
|
|
|
tokzer->limit = private_size;
|
|
|
|
tokzer->max = private_size;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char* base;
|
|
|
|
|
|
|
|
/* we're in a PFA file, read each token until we find "eexec" */
|
|
|
|
while ( tokzer->token.kind2 != key_eexec )
|
|
|
|
{
|
|
|
|
error = Read_Token( tokzer );
|
|
|
|
if (error) goto Fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* now determine wether the private dictionary is encoded in binary */
|
|
|
|
/* or hexadecimal ASCII format.. */
|
|
|
|
|
|
|
|
/* we need to access the next 4 bytes (after the final \r following */
|
|
|
|
/* the 'eexec' keyword..) if they all are hexadecimal digits, then */
|
|
|
|
/*we have a case of ASCII storage.. */
|
|
|
|
while ( tokzer->cursor+5 > tokzer->limit )
|
|
|
|
{
|
|
|
|
error = grow( tokzer );
|
|
|
|
if (error) goto Fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* skip whitespace/line feed after "eexec" */
|
|
|
|
base = (char*)tokzer->base + tokzer->cursor + 1;
|
|
|
|
if ( ( hexa_value( base[0] ) | hexa_value( base[1] ) |
|
|
|
|
hexa_value( base[2] ) | hexa_value( base[3] ) ) < 0 )
|
|
|
|
{
|
|
|
|
/* binary encoding - "simply" read the stream */
|
|
|
|
|
|
|
|
/* if it's a memory-based resource, we need to allocate a new */
|
|
|
|
/* storage buffer for the private dictionary, as it needs to */
|
|
|
|
/* be decrypted later.. */
|
|
|
|
if ( stream->base )
|
|
|
|
{
|
|
|
|
size = stream->size - tokzer->cursor-1; /* remaining bytes */
|
|
|
|
|
|
|
|
if ( ALLOC( private, size ) ) /* allocate private dict buffer */
|
|
|
|
goto Fail;
|
|
|
|
|
|
|
|
/* copy eexec-encrypted bytes */
|
|
|
|
MEM_Copy( private, tokzer->base + tokzer->cursor+1, size );
|
|
|
|
|
|
|
|
/* reset pointers - forget about file mapping */
|
|
|
|
tokzer->base = private;
|
|
|
|
tokzer->limit = size;
|
|
|
|
tokzer->max = size;
|
|
|
|
tokzer->cursor = 0;
|
|
|
|
}
|
|
|
|
/* on the opposite, for disk based resources, we simply grow */
|
|
|
|
/* the current buffer until its completion, and decrypt the */
|
|
|
|
/* bytes within it. In all cases, the "base" buffer will be */
|
|
|
|
/* discarded on DoneTokenizer if we're in the private dict.. */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* grow the read buffer to the full file.. */
|
|
|
|
while ( tokzer->limit < tokzer->max )
|
|
|
|
{
|
|
|
|
error = grow( tokenizer );
|
|
|
|
if (error) goto Fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set up cursor to first encrypted byte */
|
|
|
|
tokzer->cursor++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* ASCII hexadecimal encoding.. This sucks.. */
|
|
|
|
T1_Byte* write;
|
|
|
|
T1_Byte* cur;
|
|
|
|
T1_Byte* limit;
|
|
|
|
T1_Int count;
|
|
|
|
|
|
|
|
/* Allocate a buffer, read each one byte at a time .. */
|
|
|
|
count = ( stream->size - tokzer->cursor );
|
|
|
|
size = count/2;
|
|
|
|
|
|
|
|
if ( ALLOC( private, size ) ) /* allocate private dict buffer */
|
|
|
|
goto Fail;
|
|
|
|
|
|
|
|
write = private;
|
|
|
|
cur = tokzer->base + tokzer->cursor;
|
|
|
|
limit = tokzer->base + tokzer->limit;
|
|
|
|
|
|
|
|
/* read each bytes */
|
|
|
|
while ( count > 0 )
|
|
|
|
{
|
|
|
|
/* ensure that we can read the next 2 bytes !! */
|
|
|
|
while ( cur+2 > limit )
|
|
|
|
{
|
|
|
|
int cursor = cur - tokzer->base;
|
|
|
|
error = grow( tokzer );
|
|
|
|
if (error) goto Fail_Private;
|
|
|
|
cur = tokzer->base + cursor;
|
|
|
|
limit = tokzer->base + tokzer->limit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check for new line */
|
|
|
|
if ( cur[0] == '\r' || cur[0] == '\n' )
|
|
|
|
{
|
|
|
|
cur++;
|
|
|
|
count--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int hex1 = hexa_value(cur[0]);
|
|
|
|
|
|
|
|
/* exit if we have a non hexa-decimal digit which isn't */
|
|
|
|
/* a new-line character.. */
|
|
|
|
if (hex1 < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* otherwise, store byte */
|
|
|
|
*write++ = ( hex1 << 4 ) | hexa_value( cur[1] );
|
|
|
|
cur += 2;
|
|
|
|
count -= 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get rid of old buffer in the case of disk-based resources */
|
|
|
|
if ( !stream->base )
|
|
|
|
FREE( tokzer->base );
|
|
|
|
|
|
|
|
/* set up pointers */
|
|
|
|
tokzer->base = private;
|
|
|
|
tokzer->limit = size;
|
|
|
|
tokzer->max = size;
|
|
|
|
tokzer->cursor = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* finally, decrypt the private dictionary - and skip the lenIV bytes */
|
|
|
|
t1_decrypt( tokzer->base, tokzer->limit, 55665 );
|
|
|
|
tokzer->cursor += 4;
|
|
|
|
|
|
|
|
Fail:
|
|
|
|
return error;
|
|
|
|
|
|
|
|
Fail_Private:
|
|
|
|
FREE( private );
|
|
|
|
goto Fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* <Function> Read_Token */
|
|
|
|
/* */
|
|
|
|
/* <Description> */
|
|
|
|
/* Read a new token from the current input stream. This function */
|
|
|
|
/* extracts a token from the font program until "Open_PrivateDict" */
|
|
|
|
/* has been called. After this, it returns tokens from the */
|
|
|
|
/* (eexec-encrypted) private dictionnary.. */
|
|
|
|
/* */
|
|
|
|
/* <Input> */
|
|
|
|
/* tokenizer :: target tokenizer object */
|
|
|
|
/* */
|
|
|
|
/* <Return> */
|
|
|
|
/* Type1 error code. 0 means success.. */
|
|
|
|
/* */
|
|
|
|
/* <Note> */
|
|
|
|
/* One should use the function Read_CharStrings to read the binary */
|
|
|
|
/* charstrings from the private dict.. */
|
|
|
|
/* */
|
|
|
|
LOCAL_FUNC
|
|
|
|
T1_Error Read_Token( T1_Tokenizer tokenizer )
|
|
|
|
{
|
|
|
|
T1_Tokenizer tok = tokenizer;
|
|
|
|
T1_Long cur, limit;
|
|
|
|
T1_Byte* base;
|
|
|
|
char c, starter, ender;
|
|
|
|
T1_Bool token_started;
|
|
|
|
|
|
|
|
T1_TokenType kind;
|
|
|
|
|
|
|
|
tok->error = T1_Err_Ok;
|
|
|
|
tok->token.kind = tok_any;
|
|
|
|
|
|
|
|
base = tok->base;
|
|
|
|
limit = tok->limit;
|
|
|
|
cur = tok->cursor;
|
|
|
|
|
|
|
|
token_started = 0;
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
if ( cur >= limit )
|
|
|
|
{
|
|
|
|
if ( grow( tok ) ) goto Exit;
|
|
|
|
base = tok->base;
|
|
|
|
limit = tok->limit;
|
|
|
|
}
|
|
|
|
|
|
|
|
c = (char)base[cur++];
|
|
|
|
|
|
|
|
/* check that we have an ASCII character */
|
|
|
|
if ( (T1_Byte)c > 127 )
|
|
|
|
{
|
|
|
|
FT_ERROR(( "Unexpected binary data in Type1 fragment !!\n" ));
|
|
|
|
tok->error = T1_Err_Invalid_File_Format;
|
|
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (c)
|
|
|
|
{
|
|
|
|
case '\r' :
|
|
|
|
case '\n' :
|
|
|
|
case ' ' :
|
|
|
|
case '\t' : /* skip initial whitespace => skip to next */
|
|
|
|
if (token_started)
|
|
|
|
{
|
|
|
|
/* possibly a name, keyword, wathever */
|
|
|
|
tok->token.kind = tok_any;
|
|
|
|
tok->token.len = cur-tok->token.start-1;
|
|
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
/* otherwise, skip everything */
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case '%' : /* this is a comment - skip everything */
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
T1_Int left = limit - cur;
|
|
|
|
|
|
|
|
while (left > 0)
|
|
|
|
{
|
|
|
|
c = (char)base[cur++];
|
|
|
|
if ( c == '\r' || c == '\n' )
|
|
|
|
goto Next;
|
|
|
|
left--;
|
|
|
|
}
|
|
|
|
if ( grow( tokenizer ) ) goto Exit;
|
|
|
|
base = tok->base;
|
|
|
|
limit = tok->limit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case '(' : /* a Postscript string */
|
|
|
|
kind = tok_string;
|
|
|
|
ender = ')';
|
|
|
|
|
|
|
|
L1:
|
|
|
|
if (!token_started)
|
|
|
|
{
|
|
|
|
token_started = 1;
|
|
|
|
tok->token.start = cur-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
T1_Int nest_level = 1;
|
|
|
|
|
|
|
|
starter = c;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
T1_Int left = limit-cur;
|
|
|
|
while (left > 0)
|
|
|
|
{
|
|
|
|
c = (char)base[cur++];
|
|
|
|
|
|
|
|
if ( c == starter )
|
|
|
|
nest_level++;
|
|
|
|
|
|
|
|
else if ( c == ender )
|
|
|
|
{
|
|
|
|
nest_level--;
|
|
|
|
if (nest_level <= 0)
|
|
|
|
{
|
|
|
|
tok->token.kind = kind;
|
|
|
|
tok->token.len = cur - tok->token.start;
|
|
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
left--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( grow( tok ) ) goto Exit;
|
|
|
|
base = tok->base;
|
|
|
|
limit = tok->limit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case '[' : /* a Postscript array */
|
|
|
|
if (token_started)
|
|
|
|
goto Any_Token;
|
|
|
|
|
|
|
|
kind = tok_array;
|
|
|
|
ender = ']';
|
|
|
|
goto L1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case '{' : /* a Postscript program */
|
|
|
|
if (token_started)
|
|
|
|
goto Any_Token;
|
|
|
|
|
|
|
|
kind = tok_program;
|
|
|
|
ender = '}';
|
|
|
|
goto L1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case '<' : /* a Postscript hex byte array ?? */
|
|
|
|
if (token_started)
|
|
|
|
goto Any_Token;
|
|
|
|
|
|
|
|
kind = tok_hexarray;
|
|
|
|
ender = '>';
|
|
|
|
goto L1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case '0': /* any number */
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
case '5':
|
|
|
|
case '6':
|
|
|
|
case '7':
|
|
|
|
case '8':
|
|
|
|
case '9':
|
|
|
|
if (token_started)
|
|
|
|
goto Next;
|
|
|
|
|
|
|
|
tok->token.kind = tok_number;
|
|
|
|
token_started = 1;
|
|
|
|
tok->token.start = cur-1;
|
|
|
|
L2:
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
T1_Int left = limit-cur;
|
|
|
|
while (left > 0)
|
|
|
|
{
|
|
|
|
c = (char)base[cur++];
|
|
|
|
|
|
|
|
switch (c)
|
|
|
|
{
|
|
|
|
case '[':
|
|
|
|
case '{':
|
|
|
|
case '(':
|
|
|
|
case '<':
|
|
|
|
case '/':
|
2000-05-17 01:44:38 +02:00
|
|
|
goto Any_Token;
|
1999-12-17 00:11:37 +01:00
|
|
|
|
|
|
|
case ' ':
|
|
|
|
case '\r':
|
|
|
|
case '\t':
|
|
|
|
case '\n':
|
|
|
|
tok->token.len = cur - tok->token.start - 1;
|
|
|
|
goto Exit;
|
|
|
|
|
|
|
|
default:
|
|
|
|
;
|
|
|
|
}
|
|
|
|
left--;
|
|
|
|
}
|
|
|
|
if (grow( tok )) goto Exit;
|
|
|
|
base = tok->base;
|
|
|
|
limit = tok->limit;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
case '.': /* maybe a number */
|
|
|
|
case '-':
|
|
|
|
case '+':
|
|
|
|
if (token_started)
|
|
|
|
goto Next;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
token_started = 1;
|
|
|
|
tok->token.start = cur-1;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
T1_Int left = limit-cur;
|
|
|
|
if ( left > 0 )
|
|
|
|
{
|
|
|
|
/* test for any following digit, interpreted as number */
|
|
|
|
c = (char)base[cur];
|
|
|
|
tok->token.kind = ( c >= '0' && c <= '9' ? tok_number : tok_any );
|
|
|
|
goto L2;
|
|
|
|
}
|
|
|
|
if (grow( tok )) goto Exit;
|
|
|
|
base = tok->base;
|
|
|
|
limit = tok->limit;
|
|
|
|
}
|
|
|
|
|
|
|
|
case '/': /* maybe an immediate name */
|
|
|
|
if (!token_started)
|
|
|
|
{
|
|
|
|
token_started = 1;
|
|
|
|
tok->token.start = cur-1;
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
T1_Int left = limit-cur;
|
|
|
|
if ( left > 0 )
|
|
|
|
{
|
|
|
|
/* test for single '/', interpreted as garbage */
|
|
|
|
c = (char)base[cur];
|
|
|
|
tok->token.kind = ( c == ' ' || c == '\t' ||
|
|
|
|
c == '\r' || c == '\n' ?
|
|
|
|
tok_any : tok_immediate );
|
|
|
|
goto L2;
|
|
|
|
}
|
|
|
|
if (grow( tok )) goto Exit;
|
|
|
|
base = tok->base;
|
|
|
|
limit = tok->limit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Any_Token: /* possibly a name or wathever */
|
|
|
|
cur--;
|
|
|
|
tok->token.len = cur - tok->token.start;
|
|
|
|
goto Exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (!token_started)
|
|
|
|
{
|
|
|
|
token_started = 1;
|
|
|
|
tok->token.start = cur-1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Next:
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
|
|
|
Exit:
|
|
|
|
tok->cursor = cur;
|
|
|
|
|
|
|
|
if (!tok->error)
|
|
|
|
{
|
|
|
|
/* now, tries to match keywords and immediate names */
|
|
|
|
T1_Int index;
|
|
|
|
|
|
|
|
switch ( tok->token.kind )
|
|
|
|
{
|
|
|
|
case tok_immediate : /* immediate name */
|
|
|
|
index = Find_Name( (char*)(tok->base + tok->token.start+1),
|
|
|
|
tok->token.len-1,
|
|
|
|
t1_immediates,
|
|
|
|
imm_max - imm_first_ );
|
|
|
|
tok->token.kind2 = ( index >= 0 ? imm_first_ + index : 0 );
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case tok_any : /* test for keyword */
|
|
|
|
index = Find_Name( (char*)(tok->base + tok->token.start),
|
|
|
|
tok->token.len,
|
|
|
|
t1_keywords,
|
|
|
|
key_max - key_first_ );
|
|
|
|
if ( index >= 0 )
|
|
|
|
{
|
|
|
|
tok->token.kind = tok_keyword;
|
|
|
|
tok->token.kind2 = key_first_ + index;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
tok->token.kind2 = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
tok->token.kind2 = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return tokenizer->error;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-03-06 10:51:19 +01:00
|
|
|
#if 0
|
1999-12-17 00:11:37 +01:00
|
|
|
/*************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* <Function> Read_CharStrings */
|
|
|
|
/* */
|
|
|
|
/* <Description> */
|
|
|
|
/* Read a charstrings from the current input stream. These are */
|
|
|
|
/* binary bytes that encode each individual glyph outline. */
|
|
|
|
/* */
|
|
|
|
/* The caller is responsible for skipping the "lenIV" bytes at */
|
|
|
|
/* the start of the record.. */
|
|
|
|
/* */
|
|
|
|
/* <Input> */
|
|
|
|
/* tokenizer :: target tokenizer object */
|
|
|
|
/* num_chars :: number of binary bytes to read */
|
|
|
|
/* */
|
|
|
|
/* <Output> */
|
|
|
|
/* buffer :: target array of bytes. These are eexec-decrypted.. */
|
|
|
|
/* */
|
|
|
|
/* <Return> */
|
|
|
|
/* Type1 error code. 0 means success.. */
|
|
|
|
/* */
|
|
|
|
/* <Note> */
|
|
|
|
/* One should use the function Read_CharStrings to read the binary */
|
|
|
|
/* charstrings from the private dict.. */
|
|
|
|
/* */
|
|
|
|
LOCAL_FUNC
|
|
|
|
T1_Error Read_CharStrings( T1_Tokenizer tokenizer,
|
|
|
|
T1_Int num_chars,
|
|
|
|
T1_Byte* buffer )
|
|
|
|
{
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
T1_Int left = tokenizer->limit - tokenizer->cursor;
|
|
|
|
|
|
|
|
if ( left >= num_chars )
|
|
|
|
{
|
|
|
|
MEM_Copy( buffer, tokenizer->base + tokenizer->cursor, num_chars );
|
|
|
|
t1_decrypt( buffer, num_chars, 4330 );
|
|
|
|
tokenizer->cursor += num_chars;
|
|
|
|
return T1_Err_Ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( grow(tokenizer) ) return tokenizer->error;
|
|
|
|
}
|
|
|
|
}
|
2000-03-06 10:51:19 +01:00
|
|
|
#endif
|