762 lines
19 KiB
C
762 lines
19 KiB
C
/***************************************************************************/
|
|
/* */
|
|
/* t1parse.c */
|
|
/* */
|
|
/* Type 1 parser (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/internal/ftdebug.h>
|
|
#include <freetype/internal/t1types.h>
|
|
|
|
|
|
#ifdef FT_FLAT_COMPILE
|
|
|
|
#include "t1parse.h"
|
|
|
|
#else
|
|
|
|
#include <type1/t1parse.h>
|
|
|
|
#endif
|
|
|
|
|
|
#include <stdio.h> /* for sscanf() */
|
|
#include <string.h> /* for strncpy() */
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* T1_New_Table */
|
|
/* */
|
|
/* <Description> */
|
|
/* Initializes a T1_Table structure. */
|
|
/* */
|
|
/* <InOut> */
|
|
/* table :: The address of the target table. */
|
|
/* */
|
|
/* <Input> */
|
|
/* count :: The table size (i.e. maximum number of elements). */
|
|
/* memory :: The memory object to use for all subsequent */
|
|
/* reallocations. */
|
|
/* */
|
|
/* <Return> */
|
|
/* FreeType error code. 0 means success. */
|
|
/* */
|
|
LOCAL_FUNC
|
|
FT_Error T1_New_Table( T1_Table* table,
|
|
FT_Int count,
|
|
FT_Memory memory )
|
|
{
|
|
FT_Error error;
|
|
|
|
|
|
table->memory = memory;
|
|
|
|
if ( ALLOC_ARRAY( table->elements, count, FT_Byte* ) )
|
|
return error;
|
|
|
|
if ( ALLOC_ARRAY( table->lengths, count, FT_Byte* ) )
|
|
{
|
|
FREE( table->elements );
|
|
return error;
|
|
}
|
|
|
|
table->max_elems = count;
|
|
table->num_elems = 0;
|
|
|
|
table->block = 0;
|
|
table->capacity = 0;
|
|
table->cursor = 0;
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
static
|
|
FT_Error reallocate_t1_table( T1_Table* table,
|
|
FT_Int new_size )
|
|
{
|
|
FT_Memory memory = table->memory;
|
|
FT_Byte* old_base = table->block;
|
|
FT_Error error;
|
|
|
|
|
|
/* reallocate the base block */
|
|
if ( REALLOC( table->block, table->capacity, new_size ) )
|
|
return error;
|
|
table->capacity = new_size;
|
|
|
|
/* shift all offsets if necessary */
|
|
if ( old_base )
|
|
{
|
|
FT_Long delta = table->block - old_base;
|
|
FT_Byte** offset = table->elements;
|
|
FT_Byte** limit = offset + table->max_elems;
|
|
|
|
|
|
if ( delta )
|
|
for ( ; offset < limit; offset ++ )
|
|
if (offset[0])
|
|
offset[0] += delta;
|
|
}
|
|
|
|
return T1_Err_Ok;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* T1_Add_Table */
|
|
/* */
|
|
/* <Description> */
|
|
/* Adds an object to a T1_Table, possibly growing its memory block. */
|
|
/* */
|
|
/* <InOut> */
|
|
/* table :: The target table. */
|
|
/* */
|
|
/* <Input> */
|
|
/* index :: The index of the object in the table. */
|
|
/* */
|
|
/* object :: The address of the object to copy in memory. */
|
|
/* */
|
|
/* length :: The length in bytes of the source object. */
|
|
/* */
|
|
/* <Return> */
|
|
/* FreeType error code. 0 means success. An error is returned if a */
|
|
/* reallocation failed. */
|
|
/* */
|
|
LOCAL_FUNC
|
|
FT_Error T1_Add_Table( T1_Table* table,
|
|
FT_Int index,
|
|
void* object,
|
|
FT_Int length )
|
|
{
|
|
if ( index < 0 || index > table->max_elems )
|
|
{
|
|
FT_ERROR(( "T1_Add_Table: invalid index\n" ));
|
|
return T1_Err_Syntax_Error;
|
|
}
|
|
|
|
/* grow the base block if needed */
|
|
if ( table->cursor + length > table->capacity )
|
|
{
|
|
FT_Error error;
|
|
FT_Int new_size = table->capacity;
|
|
|
|
|
|
while ( new_size < table->cursor + length )
|
|
new_size += 1024;
|
|
|
|
error = reallocate_t1_table( table, new_size );
|
|
if ( error )
|
|
return error;
|
|
}
|
|
|
|
/* add the object to the base block and adjust offset */
|
|
table->elements[index] = table->block + table->cursor;
|
|
table->lengths [index] = length;
|
|
MEM_Copy( table->block + table->cursor, object, length );
|
|
|
|
table->cursor += length;
|
|
|
|
return T1_Err_Ok;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* <Function> */
|
|
/* T1_Done_Table */
|
|
/* */
|
|
/* <Description> */
|
|
/* Finalize a T1_Table (reallocate it to its current cursor). */
|
|
/* */
|
|
/* <Input> */
|
|
/* table :: The target table. */
|
|
/* */
|
|
/* <Note> */
|
|
/* This function does NOT release the heap's memory block. It is up */
|
|
/* to the caller to clean it, or reference it in its own structures. */
|
|
/* */
|
|
LOCAL_FUNC
|
|
void T1_Done_Table( T1_Table* table )
|
|
{
|
|
FT_Memory memory = table->memory;
|
|
FT_Error error;
|
|
FT_Byte* old_base;
|
|
|
|
|
|
/* should never fail, as rec.cursor <= rec.size */
|
|
old_base = table->block;
|
|
if ( !old_base )
|
|
return;
|
|
|
|
(void)REALLOC( table->block, table->capacity, table->cursor );
|
|
table->capacity = table->cursor;
|
|
|
|
if ( old_base != table->block )
|
|
{
|
|
FT_Long delta = table->block - old_base;
|
|
FT_Byte** element = table->elements;
|
|
FT_Byte** limit = element + table->max_elems;
|
|
|
|
|
|
for ( ; element < limit; element++ )
|
|
if ( element[0] )
|
|
element[0] += delta;
|
|
}
|
|
}
|
|
|
|
|
|
LOCAL_FUNC
|
|
FT_String* CopyString( T1_Parser* parser )
|
|
{
|
|
FT_String* string = NULL;
|
|
T1_Token* token = parser->args++;
|
|
FT_Memory memory = parser->tokenizer->memory;
|
|
FT_Error error;
|
|
|
|
|
|
if ( token->kind == tok_string )
|
|
{
|
|
FT_Int len = token->len - 2;
|
|
|
|
|
|
if ( ALLOC( string, len + 1 ) )
|
|
{
|
|
parser->error = error;
|
|
return 0;
|
|
}
|
|
|
|
MEM_Copy( string, parser->tokenizer->base + token->start + 1, len );
|
|
string[len] = '\0';
|
|
|
|
parser->error = T1_Err_Ok;
|
|
}
|
|
else
|
|
{
|
|
FT_ERROR(( "T1_CopyString: syntax error, string token expected!\n" ));
|
|
parser->error = T1_Err_Syntax_Error;
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
|
|
static
|
|
FT_Error parse_int( FT_Byte* base,
|
|
FT_Byte* limit,
|
|
FT_Long* result )
|
|
{
|
|
FT_Bool sign = 0;
|
|
FT_Long sum = 0;
|
|
|
|
|
|
if ( base >= limit )
|
|
goto Fail;
|
|
|
|
/* check sign */
|
|
if ( *base == '+' )
|
|
base++;
|
|
|
|
else if ( *base == '-' )
|
|
{
|
|
sign++;
|
|
base++;
|
|
}
|
|
|
|
/* parse digits */
|
|
if ( base >= limit )
|
|
goto Fail;
|
|
|
|
do
|
|
{
|
|
sum = ( 10 * sum + ( *base++ - '0' ) );
|
|
|
|
} while ( base < limit );
|
|
|
|
if ( sign )
|
|
sum = -sum;
|
|
|
|
*result = sum;
|
|
return T1_Err_Ok;
|
|
|
|
Fail:
|
|
FT_ERROR(( "parse_int: integer expected\n" ));
|
|
*result = 0;
|
|
return T1_Err_Syntax_Error;
|
|
}
|
|
|
|
|
|
static
|
|
FT_Error parse_float( FT_Byte* base,
|
|
FT_Byte* limit,
|
|
FT_Long scale,
|
|
FT_Long* result )
|
|
{
|
|
#if 1
|
|
|
|
/* XXX: We are simply much too lazy to code this function */
|
|
/* properly for now. We will do that when the rest of */
|
|
/* the driver works properly. */
|
|
char temp[32];
|
|
int len = limit - base;
|
|
double value;
|
|
|
|
|
|
if ( len > 31 )
|
|
goto Fail;
|
|
|
|
strncpy( temp, (char*)base, len );
|
|
temp[len] = '\0';
|
|
if ( sscanf( temp, "%lf", &value ) != 1 )
|
|
goto Fail;
|
|
|
|
*result = (FT_Long)( scale * value );
|
|
return 0;
|
|
|
|
#else
|
|
|
|
FT_Byte* cur;
|
|
FT_Bool sign = 0; /* sign */
|
|
FT_Long number_int = 0; /* integer part */
|
|
FT_Long number_frac = 0; /* fractional part */
|
|
FT_Long exponent = 0; /* exponent value */
|
|
FT_Int num_frac = 0; /* number of fractional digits */
|
|
|
|
|
|
/* check sign */
|
|
if ( *base == '+' )
|
|
base++;
|
|
|
|
else if ( *base == '-' )
|
|
{
|
|
sign++;
|
|
base++;
|
|
}
|
|
|
|
/* find integer part */
|
|
cur = base;
|
|
while ( cur < limit )
|
|
{
|
|
FT_Byte c = *cur;
|
|
|
|
|
|
if ( c == '.' || c == 'e' || c == 'E' )
|
|
break;
|
|
|
|
cur++;
|
|
}
|
|
|
|
if ( cur > base )
|
|
{
|
|
error = parse_integer( base, cur, &number_int );
|
|
if ( error )
|
|
goto Fail;
|
|
}
|
|
|
|
/* read fractional part, if any */
|
|
if ( *cur == '.' )
|
|
{
|
|
cur++;
|
|
base = cur;
|
|
while ( cur < limit )
|
|
{
|
|
FT_Byte c = *cur;
|
|
|
|
|
|
if ( c == 'e' || c == 'E' )
|
|
break;
|
|
cur++;
|
|
}
|
|
|
|
num_frac = cur - base;
|
|
|
|
if ( cur > base )
|
|
{
|
|
error = parse_integer( base, cur, &number_frac );
|
|
if ( error )
|
|
goto Fail;
|
|
base = cur;
|
|
}
|
|
}
|
|
|
|
/* read exponent, if any */
|
|
if ( *cur == 'e' || *cur == 'E' )
|
|
{
|
|
cur++;
|
|
base = cur;
|
|
error = parse_integer( base, limit, &exponent );
|
|
if ( error )
|
|
goto Fail;
|
|
|
|
/* now check that exponent is within `correct bounds' */
|
|
/* i.e. between -6 and 6 */
|
|
if ( exponent < -6 || exponent > 6 )
|
|
goto Fail;
|
|
}
|
|
|
|
/* now adjust integer value and exponent for fractional part */
|
|
while ( num_frac > 0 )
|
|
{
|
|
number_int *= 10;
|
|
exponent--;
|
|
num_frac--;
|
|
}
|
|
|
|
number_int += num_frac;
|
|
|
|
/* skip point if any, read fractional part */
|
|
if ( cur + 1 < limit )
|
|
{
|
|
if (*cur..
|
|
}
|
|
|
|
/* now compute scaled float value */
|
|
/* XXX: incomplete! */
|
|
|
|
#endif /* 1 */
|
|
|
|
Fail:
|
|
FT_ERROR(( "parse_float: syntax error!\n" ));
|
|
return T1_Err_Syntax_Error;
|
|
}
|
|
|
|
|
|
static
|
|
FT_Error parse_integer( FT_Byte* base,
|
|
FT_Byte* limit,
|
|
FT_Long* result )
|
|
{
|
|
FT_Byte* cur;
|
|
|
|
|
|
/* the lexical analyser accepts floats as well as integers */
|
|
/* now; check that we really have an int in this token */
|
|
cur = base;
|
|
while ( cur < limit )
|
|
{
|
|
FT_Byte c = *cur++;
|
|
|
|
|
|
if ( c == '.' || c == 'e' || c == 'E' )
|
|
goto Float_Number;
|
|
}
|
|
|
|
/* now read the number's value */
|
|
return parse_int( base, limit, result );
|
|
|
|
Float_Number:
|
|
/* we really have a float there; simply call parse_float in this */
|
|
/* case with a scale of `10' to perform round */
|
|
{
|
|
FT_Error error;
|
|
|
|
|
|
error = parse_float( base, limit, 10, result );
|
|
if ( !error )
|
|
{
|
|
if ( *result >= 0 )
|
|
*result = ( *result + 5 ) / 10; /* round value */
|
|
else
|
|
*result = -( ( 5 - *result ) / 10 );
|
|
}
|
|
return error;
|
|
}
|
|
}
|
|
|
|
|
|
LOCAL_FUNC
|
|
FT_Long CopyInteger( T1_Parser* parser )
|
|
{
|
|
FT_Long sum = 0;
|
|
T1_Token* token = parser->args++;
|
|
|
|
|
|
if ( token->kind == tok_number )
|
|
{
|
|
FT_Byte* base = parser->tokenizer->base + token->start;
|
|
FT_Byte* limit = base + token->len;
|
|
|
|
|
|
/* now read the number's value */
|
|
parser->error = parse_integer( base, limit, &sum );
|
|
return sum;
|
|
}
|
|
|
|
FT_ERROR(( "CopyInteger: number expected\n" ));
|
|
parser->args--;
|
|
parser->error = T1_Err_Syntax_Error;
|
|
return 0;
|
|
}
|
|
|
|
|
|
LOCAL_FUNC
|
|
FT_Bool CopyBoolean( T1_Parser* parser )
|
|
{
|
|
FT_Error error = T1_Err_Ok;
|
|
FT_Bool result = 0;
|
|
T1_Token* token = parser->args++;
|
|
|
|
|
|
if ( token->kind == tok_keyword )
|
|
{
|
|
if ( token->kind2 == key_false )
|
|
result = 0;
|
|
|
|
else if ( token->kind2 == key_true )
|
|
result = !0;
|
|
|
|
else
|
|
goto Fail;
|
|
}
|
|
else
|
|
{
|
|
Fail:
|
|
FT_ERROR(( "CopyBoolean:" ));
|
|
FT_ERROR(( " syntax error; `false' or `true' expected\n" ));
|
|
error = T1_Err_Syntax_Error;
|
|
}
|
|
parser->error = error;
|
|
return result;
|
|
}
|
|
|
|
|
|
LOCAL_FUNC
|
|
FT_Long CopyFloat( T1_Parser* parser,
|
|
FT_Int scale )
|
|
{
|
|
FT_Error error;
|
|
FT_Long sum = 0;
|
|
T1_Token* token = parser->args++;
|
|
|
|
|
|
if ( token->kind == tok_number )
|
|
{
|
|
FT_Byte* base = parser->tokenizer->base + token->start;
|
|
FT_Byte* limit = base + token->len;
|
|
|
|
|
|
error = parser->error = parse_float( base, limit, scale, &sum );
|
|
if ( error )
|
|
goto Fail;
|
|
|
|
return sum;
|
|
}
|
|
|
|
Fail:
|
|
FT_ERROR(( "CopyFloat: syntax error!\n" ));
|
|
parser->error = T1_Err_Syntax_Error;
|
|
return 0;
|
|
}
|
|
|
|
|
|
LOCAL_FUNC
|
|
void CopyBBox( T1_Parser* parser,
|
|
FT_BBox* bbox )
|
|
{
|
|
T1_Token* token = parser->args++;
|
|
FT_Int n;
|
|
FT_Error error;
|
|
|
|
|
|
if ( token->kind == tok_program ||
|
|
token->kind == tok_array )
|
|
{
|
|
/* get rid of `['/`]', or `{'/`}' */
|
|
FT_Byte* base = parser->tokenizer->base + token->start + 1;
|
|
FT_Byte* limit = base + token->len - 2;
|
|
FT_Byte* cur;
|
|
FT_Byte* start;
|
|
|
|
|
|
/* read each parameter independently */
|
|
cur = base;
|
|
for ( n = 0; n < 4; n++ )
|
|
{
|
|
FT_Long* result;
|
|
|
|
|
|
/* skip whitespace */
|
|
while ( cur < limit && *cur == ' ' )
|
|
cur++;
|
|
|
|
/* skip numbers */
|
|
start = cur;
|
|
while ( cur < limit && *cur != ' ' )
|
|
cur++;
|
|
|
|
/* compute result address */
|
|
switch ( n )
|
|
{
|
|
case 0:
|
|
result = &bbox->xMin;
|
|
break;
|
|
case 1:
|
|
result = &bbox->yMin;
|
|
break;
|
|
case 2:
|
|
result = &bbox->xMax;
|
|
break;
|
|
default:
|
|
result = &bbox->yMax;
|
|
}
|
|
|
|
error = parse_integer( start, cur, result );
|
|
if ( error )
|
|
goto Fail;
|
|
}
|
|
parser->error = 0;
|
|
return;
|
|
}
|
|
|
|
Fail:
|
|
FT_ERROR(( "CopyBBox: syntax error!\n" ));
|
|
parser->error = T1_Err_Syntax_Error;
|
|
}
|
|
|
|
|
|
LOCAL_FUNC
|
|
void CopyMatrix( T1_Parser* parser,
|
|
FT_Matrix* matrix )
|
|
{
|
|
T1_Token* token = parser->args++;
|
|
FT_Error error;
|
|
|
|
|
|
if ( token->kind == tok_array )
|
|
{
|
|
/* get rid of `[' and `]' */
|
|
FT_Byte* base = parser->tokenizer->base + token->start + 1;
|
|
FT_Byte* limit = base + token->len - 2;
|
|
FT_Byte* cur;
|
|
FT_Byte* start;
|
|
FT_Int n;
|
|
|
|
|
|
/* read each parameter independently */
|
|
cur = base;
|
|
for ( n = 0; n < 4; n++ )
|
|
{
|
|
FT_Long* result;
|
|
|
|
|
|
/* skip whitespace */
|
|
while ( cur < limit && *cur == ' ' )
|
|
cur++;
|
|
|
|
/* skip numbers */
|
|
start = cur;
|
|
while ( cur < limit && *cur != ' ')
|
|
cur++;
|
|
|
|
/* compute result address */
|
|
switch ( n )
|
|
{
|
|
case 0:
|
|
result = &matrix->xx;
|
|
break;
|
|
case 1:
|
|
result = &matrix->yx;
|
|
break;
|
|
case 2:
|
|
result = &matrix->xy;
|
|
break;
|
|
default:
|
|
result = &matrix->yy;
|
|
}
|
|
|
|
error = parse_float( start, cur, 65536000L, result );
|
|
if ( error )
|
|
goto Fail;
|
|
}
|
|
parser->error = 0;
|
|
return;
|
|
}
|
|
|
|
Fail:
|
|
FT_ERROR(( "CopyMatrix: syntax error!\n" ));
|
|
parser->error = T1_Err_Syntax_Error;
|
|
}
|
|
|
|
|
|
LOCAL_FUNC
|
|
void CopyArray( T1_Parser* parser,
|
|
FT_Byte* num_elements,
|
|
FT_Short* elements,
|
|
FT_Int max_elements )
|
|
{
|
|
T1_Token* token = parser->args++;
|
|
FT_Error error;
|
|
|
|
|
|
if ( token->kind == tok_array ||
|
|
token->kind == tok_program ) /* in the case of MinFeature */
|
|
{
|
|
/* get rid of `['/`]', or `{'/`}' */
|
|
FT_Byte* base = parser->tokenizer->base + token->start + 1;
|
|
FT_Byte* limit = base + token->len - 2;
|
|
FT_Byte* cur;
|
|
FT_Byte* start;
|
|
FT_Int n;
|
|
|
|
|
|
/* read each parameter independently */
|
|
cur = base;
|
|
for ( n = 0; n < max_elements; n++ )
|
|
{
|
|
FT_Long result;
|
|
|
|
|
|
/* test end of string */
|
|
if ( cur >= limit )
|
|
break;
|
|
|
|
/* skip whitespace */
|
|
while ( cur < limit && *cur == ' ' )
|
|
cur++;
|
|
|
|
/* end of list? */
|
|
if ( cur >= limit )
|
|
break;
|
|
|
|
/* skip numbers */
|
|
start = cur;
|
|
while ( cur < limit && *cur != ' ' )
|
|
cur++;
|
|
|
|
error = parse_integer( start, cur, &result );
|
|
if ( error )
|
|
goto Fail;
|
|
|
|
*elements++ = (FT_Short)result;
|
|
}
|
|
|
|
if ( num_elements )
|
|
*num_elements = (FT_Byte)n;
|
|
|
|
parser->error = 0;
|
|
return;
|
|
}
|
|
|
|
Fail:
|
|
FT_ERROR(( "CopyArray: syntax error!\n" ));
|
|
parser->error = T1_Err_Syntax_Error;
|
|
}
|
|
|
|
|
|
/* END */
|