So. Some themes have objects that seem to be large natural extensions of the landscape. Masks allow maintaining that. Lemme know if it doesn't look good. If it doesn't, can still use for ice/bounce/indestructible. Indestructible bunker object for example.
/***************************************************************************/
/* */
/* afmparse.c */
/* */
/* AFM parser (body). */
/* */
/* Copyright 2006, 2007, 2008, 2009, 2010 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 <ft2build.h>
#include FT_FREETYPE_H
#include FT_INTERNAL_POSTSCRIPT_AUX_H
#include "afmparse.h"
#include "psconv.h"
#include "psauxerr.h"
/***************************************************************************/
/* */
/* AFM_Stream */
/* */
/* The use of AFM_Stream is largely inspired by parseAFM.[ch] from t1lib. */
/* */
/* */
enum
{
AFM_STREAM_STATUS_NORMAL,
AFM_STREAM_STATUS_EOC,
AFM_STREAM_STATUS_EOL,
AFM_STREAM_STATUS_EOF
};
typedef struct AFM_StreamRec_
{
FT_Byte* cursor;
FT_Byte* base;
FT_Byte* limit;
FT_Int status;
} AFM_StreamRec;
#ifndef EOF
#define EOF -1
#endif
/* this works because empty lines are ignored */
#define AFM_IS_NEWLINE( ch ) ( (ch) == '\r' || (ch) == '\n' )
#define AFM_IS_EOF( ch ) ( (ch) == EOF || (ch) == '\x1a' )
#define AFM_IS_SPACE( ch ) ( (ch) == ' ' || (ch) == '\t' )
/* column separator; there is no `column' in the spec actually */
#define AFM_IS_SEP( ch ) ( (ch) == ';' )
#define AFM_GETC() \
( ( (stream)->cursor < (stream)->limit ) ? *(stream)->cursor++ \
: EOF )
#define AFM_STREAM_KEY_BEGIN( stream ) \
(char*)( (stream)->cursor - 1 )
#define AFM_STREAM_KEY_LEN( stream, key ) \
( (char*)(stream)->cursor - key - 1 )
#define AFM_STATUS_EOC( stream ) \
( (stream)->status >= AFM_STREAM_STATUS_EOC )
#define AFM_STATUS_EOL( stream ) \
( (stream)->status >= AFM_STREAM_STATUS_EOL )
#define AFM_STATUS_EOF( stream ) \
( (stream)->status >= AFM_STREAM_STATUS_EOF )
static int
afm_stream_skip_spaces( AFM_Stream stream )
{
int ch = 0; /* make stupid compiler happy */
if ( AFM_STATUS_EOC( stream ) )
return ';';
while ( 1 )
{
ch = AFM_GETC();
if ( !AFM_IS_SPACE( ch ) )
break;
}
if ( AFM_IS_NEWLINE( ch ) )
stream->status = AFM_STREAM_STATUS_EOL;
else if ( AFM_IS_SEP( ch ) )
stream->status = AFM_STREAM_STATUS_EOC;
else if ( AFM_IS_EOF( ch ) )
stream->status = AFM_STREAM_STATUS_EOF;
return ch;
}
/* read a key or value in current column */
static char*
afm_stream_read_one( AFM_Stream stream )
{
char* str;
int ch;
afm_stream_skip_spaces( stream );
if ( AFM_STATUS_EOC( stream ) )
return NULL;
str = AFM_STREAM_KEY_BEGIN( stream );
while ( 1 )
{
ch = AFM_GETC();
if ( AFM_IS_SPACE( ch ) )
break;
else if ( AFM_IS_NEWLINE( ch ) )
{
stream->status = AFM_STREAM_STATUS_EOL;
break;
}
else if ( AFM_IS_SEP( ch ) )
{
stream->status = AFM_STREAM_STATUS_EOC;
break;
}
else if ( AFM_IS_EOF( ch ) )
{
stream->status = AFM_STREAM_STATUS_EOF;
break;
}
}
return str;
}
/* read a string (i.e., read to EOL) */
static char*
afm_stream_read_string( AFM_Stream stream )
{
char* str;
int ch;
afm_stream_skip_spaces( stream );
if ( AFM_STATUS_EOL( stream ) )
return NULL;
str = AFM_STREAM_KEY_BEGIN( stream );
/* scan to eol */
while ( 1 )
{
ch = AFM_GETC();
if ( AFM_IS_NEWLINE( ch ) )
{
stream->status = AFM_STREAM_STATUS_EOL;
break;
}
else if ( AFM_IS_EOF( ch ) )
{
stream->status = AFM_STREAM_STATUS_EOF;
break;
}
}
return str;
}
/*************************************************************************/
/* */
/* AFM_Parser */
/* */
/* */
/* all keys defined in Ch. 7-10 of 5004.AFM_Spec.pdf */
typedef enum AFM_Token_
{
AFM_TOKEN_ASCENDER,
AFM_TOKEN_AXISLABEL,
AFM_TOKEN_AXISTYPE,
AFM_TOKEN_B,
AFM_TOKEN_BLENDAXISTYPES,
AFM_TOKEN_BLENDDESIGNMAP,
AFM_TOKEN_BLENDDESIGNPOSITIONS,
AFM_TOKEN_C,
AFM_TOKEN_CC,
AFM_TOKEN_CH,
AFM_TOKEN_CAPHEIGHT,
AFM_TOKEN_CHARWIDTH,
AFM_TOKEN_CHARACTERSET,
AFM_TOKEN_CHARACTERS,
AFM_TOKEN_DESCENDER,
AFM_TOKEN_ENCODINGSCHEME,
AFM_TOKEN_ENDAXIS,
AFM_TOKEN_ENDCHARMETRICS,
AFM_TOKEN_ENDCOMPOSITES,
AFM_TOKEN_ENDDIRECTION,
AFM_TOKEN_ENDFONTMETRICS,
AFM_TOKEN_ENDKERNDATA,
AFM_TOKEN_ENDKERNPAIRS,
AFM_TOKEN_ENDTRACKKERN,
AFM_TOKEN_ESCCHAR,
AFM_TOKEN_FAMILYNAME,
AFM_TOKEN_FONTBBOX,
AFM_TOKEN_FONTNAME,
AFM_TOKEN_FULLNAME,
AFM_TOKEN_ISBASEFONT,
AFM_TOKEN_ISCIDFONT,
AFM_TOKEN_ISFIXEDPITCH,
AFM_TOKEN_ISFIXEDV,
AFM_TOKEN_ITALICANGLE,
AFM_TOKEN_KP,
AFM_TOKEN_KPH,
AFM_TOKEN_KPX,
AFM_TOKEN_KPY,
AFM_TOKEN_L,
AFM_TOKEN_MAPPINGSCHEME,
AFM_TOKEN_METRICSSETS,
AFM_TOKEN_N,
AFM_TOKEN_NOTICE,
AFM_TOKEN_PCC,
AFM_TOKEN_STARTAXIS,
AFM_TOKEN_STARTCHARMETRICS,
AFM_TOKEN_STARTCOMPOSITES,
AFM_TOKEN_STARTDIRECTION,
AFM_TOKEN_STARTFONTMETRICS,
AFM_TOKEN_STARTKERNDATA,
AFM_TOKEN_STARTKERNPAIRS,
AFM_TOKEN_STARTKERNPAIRS0,
AFM_TOKEN_STARTKERNPAIRS1,
AFM_TOKEN_STARTTRACKKERN,
AFM_TOKEN_STDHW,
AFM_TOKEN_STDVW,
AFM_TOKEN_TRACKKERN,
AFM_TOKEN_UNDERLINEPOSITION,
AFM_TOKEN_UNDERLINETHICKNESS,
AFM_TOKEN_VV,
AFM_TOKEN_VVECTOR,
AFM_TOKEN_VERSION,
AFM_TOKEN_W,
AFM_TOKEN_W0,
AFM_TOKEN_W0X,
AFM_TOKEN_W0Y,
AFM_TOKEN_W1,
AFM_TOKEN_W1X,
AFM_TOKEN_W1Y,
AFM_TOKEN_WX,
AFM_TOKEN_WY,
AFM_TOKEN_WEIGHT,
AFM_TOKEN_WEIGHTVECTOR,
AFM_TOKEN_XHEIGHT,
N_AFM_TOKENS,
AFM_TOKEN_UNKNOWN
} AFM_Token;
static const char* const afm_key_table[N_AFM_TOKENS] =
{
"Ascender",
"AxisLabel",
"AxisType",
"B",
"BlendAxisTypes",
"BlendDesignMap",
"BlendDesignPositions",
"C",
"CC",
"CH",
"CapHeight",
"CharWidth",
"CharacterSet",
"Characters",
"Descender",
"EncodingScheme",
"EndAxis",
"EndCharMetrics",
"EndComposites",
"EndDirection",
"EndFontMetrics",
"EndKernData",
"EndKernPairs",
"EndTrackKern",
"EscChar",
"FamilyName",
"FontBBox",
"FontName",
"FullName",
"IsBaseFont",
"IsCIDFont",
"IsFixedPitch",
"IsFixedV",
"ItalicAngle",
"KP",
"KPH",
"KPX",
"KPY",
"L",
"MappingScheme",
"MetricsSets",
"N",
"Notice",
"PCC",
"StartAxis",
"StartCharMetrics",
"StartComposites",
"StartDirection",
"StartFontMetrics",
"StartKernData",
"StartKernPairs",
"StartKernPairs0",
"StartKernPairs1",
"StartTrackKern",
"StdHW",
"StdVW",
"TrackKern",
"UnderlinePosition",
"UnderlineThickness",
"VV",
"VVector",
"Version",
"W",
"W0",
"W0X",
"W0Y",
"W1",
"W1X",
"W1Y",
"WX",
"WY",
"Weight",
"WeightVector",
"XHeight"
};
/*
* `afm_parser_read_vals' and `afm_parser_next_key' provide
* high-level operations to an AFM_Stream. The rest of the
* parser functions should use them without accessing the
* AFM_Stream directly.
*/
FT_LOCAL_DEF( FT_Int )
afm_parser_read_vals( AFM_Parser parser,
AFM_Value vals,
FT_UInt n )
{
AFM_Stream stream = parser->stream;
char* str;
FT_UInt i;
if ( n > AFM_MAX_ARGUMENTS )
return 0;
for ( i = 0; i < n; i++ )
{
FT_Offset len;
AFM_Value val = vals + i;
if ( val->type == AFM_VALUE_TYPE_STRING )
str = afm_stream_read_string( stream );
else
str = afm_stream_read_one( stream );
if ( !str )
break;
len = AFM_STREAM_KEY_LEN( stream, str );
switch ( val->type )
{
case AFM_VALUE_TYPE_STRING:
case AFM_VALUE_TYPE_NAME:
{
FT_Memory memory = parser->memory;
FT_Error error;
if ( !FT_QALLOC( val->u.s, len + 1 ) )
{
ft_memcpy( val->u.s, str, len );
val->u.s[len] = '\0';
}
}
break;
case AFM_VALUE_TYPE_FIXED:
val->u.f = PS_Conv_ToFixed( (FT_Byte**)(void*)&str,
(FT_Byte*)str + len, 0 );
break;
case AFM_VALUE_TYPE_INTEGER:
val->u.i = PS_Conv_ToInt( (FT_Byte**)(void*)&str,
(FT_Byte*)str + len );
break;
case AFM_VALUE_TYPE_BOOL:
val->u.b = FT_BOOL( len == 4 &&
!ft_strncmp( str, "true", 4 ) );
break;
case AFM_VALUE_TYPE_INDEX:
if ( parser->get_index )
val->u.i = parser->get_index( str, len, parser->user_data );
else
val->u.i = 0;
break;
}
}
return i;
}
FT_LOCAL_DEF( char* )
afm_parser_next_key( AFM_Parser parser,
FT_Bool line,
FT_Offset* len )
{
AFM_Stream stream = parser->stream;
char* key = 0; /* make stupid compiler happy */
if ( line )
{
while ( 1 )
{
/* skip current line */
if ( !AFM_STATUS_EOL( stream ) )
afm_stream_read_string( stream );
stream->status = AFM_STREAM_STATUS_NORMAL;
key = afm_stream_read_one( stream );
/* skip empty line */
if ( !key &&
!AFM_STATUS_EOF( stream ) &&
AFM_STATUS_EOL( stream ) )
continue;
break;
}
}
else
{
while ( 1 )
{
/* skip current column */
while ( !AFM_STATUS_EOC( stream ) )
afm_stream_read_one( stream );
stream->status = AFM_STREAM_STATUS_NORMAL;
key = afm_stream_read_one( stream );
/* skip empty column */
if ( !key &&
!AFM_STATUS_EOF( stream ) &&
AFM_STATUS_EOC( stream ) )
continue;
break;
}
}
if ( len )
*len = ( key ) ? (FT_Offset)AFM_STREAM_KEY_LEN( stream, key )
: 0;
return key;
}
static AFM_Token
afm_tokenize( const char* key,
FT_Offset len )
{
int n;
for ( n = 0; n < N_AFM_TOKENS; n++ )
{
if ( *( afm_key_table[n] ) == *key )
{
for ( ; n < N_AFM_TOKENS; n++ )
{
if ( *( afm_key_table[n] ) != *key )
return AFM_TOKEN_UNKNOWN;
if ( ft_strncmp( afm_key_table[n], key, len ) == 0 )
return (AFM_Token) n;
}
}
}
return AFM_TOKEN_UNKNOWN;
}
FT_LOCAL_DEF( FT_Error )
afm_parser_init( AFM_Parser parser,
FT_Memory memory,
FT_Byte* base,
FT_Byte* limit )
{
AFM_Stream stream = NULL;
FT_Error error;
if ( FT_NEW( stream ) )
return error;
stream->cursor = stream->base = base;
stream->limit = limit;
/* don't skip the first line during the first call */
stream->status = AFM_STREAM_STATUS_EOL;
parser->memory = memory;
parser->stream = stream;
parser->FontInfo = NULL;
parser->get_index = NULL;
return PSaux_Err_Ok;
}
FT_LOCAL( void )
afm_parser_done( AFM_Parser parser )
{
FT_Memory memory = parser->memory;
FT_FREE( parser->stream );
}
FT_LOCAL_DEF( FT_Error )
afm_parser_read_int( AFM_Parser parser,
FT_Int* aint )
{
AFM_ValueRec val;
val.type = AFM_VALUE_TYPE_INTEGER;
if ( afm_parser_read_vals( parser, &val, 1 ) == 1 )
{
*aint = val.u.i;
return PSaux_Err_Ok;
}
else
return PSaux_Err_Syntax_Error;
}
static FT_Error
afm_parse_track_kern( AFM_Parser parser )
{
AFM_FontInfo fi = parser->FontInfo;
AFM_TrackKern tk;
char* key;
FT_Offset len;
int n = -1;
if ( afm_parser_read_int( parser, &fi->NumTrackKern ) )
goto Fail;
if ( fi->NumTrackKern )
{
FT_Memory memory = parser->memory;
FT_Error error;
if ( FT_QNEW_ARRAY( fi->TrackKerns, fi->NumTrackKern ) )
return error;
}
while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
{
AFM_ValueRec shared_vals[5];
switch ( afm_tokenize( key, len ) )
{
case AFM_TOKEN_TRACKKERN:
n++;
if ( n >= fi->NumTrackKern )
goto Fail;
tk = fi->TrackKerns + n;
shared_vals[0].type = AFM_VALUE_TYPE_INTEGER;
shared_vals[1].type = AFM_VALUE_TYPE_FIXED;
shared_vals[2].type = AFM_VALUE_TYPE_FIXED;
shared_vals[3].type = AFM_VALUE_TYPE_FIXED;
shared_vals[4].type = AFM_VALUE_TYPE_FIXED;
if ( afm_parser_read_vals( parser, shared_vals, 5 ) != 5 )
goto Fail;
tk->degree = shared_vals[0].u.i;
tk->min_ptsize = shared_vals[1].u.f;
tk->min_kern = shared_vals[2].u.f;
tk->max_ptsize = shared_vals[3].u.f;
tk->max_kern = shared_vals[4].u.f;
/* is this correct? */
if ( tk->degree < 0 && tk->min_kern > 0 )
tk->min_kern = -tk->min_kern;
break;
case AFM_TOKEN_ENDTRACKKERN:
case AFM_TOKEN_ENDKERNDATA:
case AFM_TOKEN_ENDFONTMETRICS:
fi->NumTrackKern = n + 1;
return PSaux_Err_Ok;
case AFM_TOKEN_UNKNOWN:
break;
default:
goto Fail;
}
}
Fail:
return PSaux_Err_Syntax_Error;
}
#undef KERN_INDEX
#define KERN_INDEX( g1, g2 ) ( ( (FT_ULong)g1 << 16 ) | g2 )
/* compare two kerning pairs */
FT_CALLBACK_DEF( int )
afm_compare_kern_pairs( const void* a,
const void* b )
{
AFM_KernPair kp1 = (AFM_KernPair)a;
AFM_KernPair kp2 = (AFM_KernPair)b;
FT_ULong index1 = KERN_INDEX( kp1->index1, kp1->index2 );
FT_ULong index2 = KERN_INDEX( kp2->index1, kp2->index2 );
if ( index1 > index2 )
return 1;
else if ( index1 < index2 )
return -1;
else
return 0;
}
static FT_Error
afm_parse_kern_pairs( AFM_Parser parser )
{
AFM_FontInfo fi = parser->FontInfo;
AFM_KernPair kp;
char* key;
FT_Offset len;
int n = -1;
if ( afm_parser_read_int( parser, &fi->NumKernPair ) )
goto Fail;
if ( fi->NumKernPair )
{
FT_Memory memory = parser->memory;
FT_Error error;
if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) )
return error;
}
while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
{
AFM_Token token = afm_tokenize( key, len );
switch ( token )
{
case AFM_TOKEN_KP:
case AFM_TOKEN_KPX:
case AFM_TOKEN_KPY:
{
FT_Int r;
AFM_ValueRec shared_vals[4];
n++;
if ( n >= fi->NumKernPair )
goto Fail;
kp = fi->KernPairs + n;
shared_vals[0].type = AFM_VALUE_TYPE_INDEX;
shared_vals[1].type = AFM_VALUE_TYPE_INDEX;
shared_vals[2].type = AFM_VALUE_TYPE_INTEGER;
shared_vals[3].type = AFM_VALUE_TYPE_INTEGER;
r = afm_parser_read_vals( parser, shared_vals, 4 );
if ( r < 3 )
goto Fail;
kp->index1 = shared_vals[0].u.i;
kp->index2 = shared_vals[1].u.i;
if ( token == AFM_TOKEN_KPY )
{
kp->x = 0;
kp->y = shared_vals[2].u.i;
}
else
{
kp->x = shared_vals[2].u.i;
kp->y = ( token == AFM_TOKEN_KP && r == 4 )
? shared_vals[3].u.i : 0;
}
}
break;
case AFM_TOKEN_ENDKERNPAIRS:
case AFM_TOKEN_ENDKERNDATA:
case AFM_TOKEN_ENDFONTMETRICS:
fi->NumKernPair = n + 1;
ft_qsort( fi->KernPairs, fi->NumKernPair,
sizeof( AFM_KernPairRec ),
afm_compare_kern_pairs );
return PSaux_Err_Ok;
case AFM_TOKEN_UNKNOWN:
break;
default:
goto Fail;
}
}
Fail:
return PSaux_Err_Syntax_Error;
}
static FT_Error
afm_parse_kern_data( AFM_Parser parser )
{
FT_Error error;
char* key;
FT_Offset len;
while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
{
switch ( afm_tokenize( key, len ) )
{
case AFM_TOKEN_STARTTRACKKERN:
error = afm_parse_track_kern( parser );
if ( error )
return error;
break;
case AFM_TOKEN_STARTKERNPAIRS:
case AFM_TOKEN_STARTKERNPAIRS0:
error = afm_parse_kern_pairs( parser );
if ( error )
return error;
break;
case AFM_TOKEN_ENDKERNDATA:
case AFM_TOKEN_ENDFONTMETRICS:
return PSaux_Err_Ok;
case AFM_TOKEN_UNKNOWN:
break;
default:
goto Fail;
}
}
Fail:
return PSaux_Err_Syntax_Error;
}
static FT_Error
afm_parser_skip_section( AFM_Parser parser,
FT_UInt n,
AFM_Token end_section )
{
char* key;
FT_Offset len;
while ( n-- > 0 )
{
key = afm_parser_next_key( parser, 1, NULL );
if ( !key )
goto Fail;
}
while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
{
AFM_Token token = afm_tokenize( key, len );
if ( token == end_section || token == AFM_TOKEN_ENDFONTMETRICS )
return PSaux_Err_Ok;
}
Fail:
return PSaux_Err_Syntax_Error;
}
FT_LOCAL_DEF( FT_Error )
afm_parser_parse( AFM_Parser parser )
{
FT_Memory memory = parser->memory;
AFM_FontInfo fi = parser->FontInfo;
FT_Error error = PSaux_Err_Syntax_Error;
char* key;
FT_Offset len;
FT_Int metrics_sets = 0;
if ( !fi )
return PSaux_Err_Invalid_Argument;
key = afm_parser_next_key( parser, 1, &len );
if ( !key || len != 16 ||
ft_strncmp( key, "StartFontMetrics", 16 ) != 0 )
return PSaux_Err_Unknown_File_Format;
while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )
{
AFM_ValueRec shared_vals[4];
switch ( afm_tokenize( key, len ) )
{
case AFM_TOKEN_METRICSSETS:
if ( afm_parser_read_int( parser, &metrics_sets ) )
goto Fail;
if ( metrics_sets != 0 && metrics_sets != 2 )
{
error = PSaux_Err_Unimplemented_Feature;
goto Fail;
}
break;
case AFM_TOKEN_ISCIDFONT:
shared_vals[0].type = AFM_VALUE_TYPE_BOOL;
if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 )
goto Fail;
fi->IsCIDFont = shared_vals[0].u.b;
break;
case AFM_TOKEN_FONTBBOX:
shared_vals[0].type = AFM_VALUE_TYPE_FIXED;
shared_vals[1].type = AFM_VALUE_TYPE_FIXED;
shared_vals[2].type = AFM_VALUE_TYPE_FIXED;
shared_vals[3].type = AFM_VALUE_TYPE_FIXED;
if ( afm_parser_read_vals( parser, shared_vals, 4 ) != 4 )
goto Fail;
fi->FontBBox.xMin = shared_vals[0].u.f;
fi->FontBBox.yMin = shared_vals[1].u.f;
fi->FontBBox.xMax = shared_vals[2].u.f;
fi->FontBBox.yMax = shared_vals[3].u.f;
break;
case AFM_TOKEN_ASCENDER:
shared_vals[0].type = AFM_VALUE_TYPE_FIXED;
if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 )
goto Fail;
fi->Ascender = shared_vals[0].u.f;
break;
case AFM_TOKEN_DESCENDER:
shared_vals[0].type = AFM_VALUE_TYPE_FIXED;
if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 )
goto Fail;
fi->Descender = shared_vals[0].u.f;
break;
case AFM_TOKEN_STARTCHARMETRICS:
{
FT_Int n = 0;
if ( afm_parser_read_int( parser, &n ) )
goto Fail;
error = afm_parser_skip_section( parser, n,
AFM_TOKEN_ENDCHARMETRICS );
if ( error )
return error;
}
break;
case AFM_TOKEN_STARTKERNDATA:
error = afm_parse_kern_data( parser );
if ( error )
goto Fail;
/* fall through since we only support kern data */
case AFM_TOKEN_ENDFONTMETRICS:
return PSaux_Err_Ok;
default:
break;
}
}
Fail:
FT_FREE( fi->TrackKerns );
fi->NumTrackKern = 0;
FT_FREE( fi->KernPairs );
fi->NumKernPair = 0;
fi->IsCIDFont = 0;
return error;
}
/* END */