diff --git a/ChangeLog b/ChangeLog index 931a533bd..f23262661 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +2006-01-16 Chia-I Wu + + * src/psaux/afmparse.c, src/psaux/afmparse.h: New files which + implement an AFM parser. It is used to parse an AFM file. + + * src/psaux/psconv.c, src/psaux/psconv.h: New files to provide + conversion functions (e.g, PS real number => FT_Fixed) for the + PS_Parser and AFM_Parser. Some of the functions are taken, with some + modifications, from the psobjs.c + + * src/psaux/psobjs.c: Use functions from psconv.c. + + * include/freetype/internal/psaux.h, src/psaux/psauxmod.c:: Add + `AFM_Parser' to the `psaux' service. + + * src/psaux/psaux.c, src/psaux/rules.mk: Include those new files. + + * src/tools/test_afm.c: A test program for AFM parser. + + * include/freetype/internal/services/svkern.h, + include/freetype/internal/ftserv.h: New service `Kerning'. It is + currently only used to get the track kerning information. + + * src/type1/t1driver.c, src/type1/t1objs.c, src/type1/t1afm.c, + src/type1/t1afm.h: Update to use the AFM parser. + Provide the `Kerning' service. + + * include/freetype/freetype.h, src/base/ftobjs.c: New API + `FT_Get_Track_Kerning'. + 2006-01-15 Chia-I Wu * include/freetype/internal/ftobjs.h, src/base/ftobjs.c, diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h index 401d6699a..64ee1b81e 100644 --- a/include/freetype/freetype.h +++ b/include/freetype/freetype.h @@ -192,6 +192,7 @@ FT_BEGIN_HEADER /* FT_Render_Mode */ /* FT_Get_Kerning */ /* FT_Kerning_Mode */ + /* FT_Get_Track_Kerning */ /* FT_Get_Glyph_Name */ /* FT_Get_Postscript_Name */ /* */ @@ -2711,6 +2712,34 @@ FT_BEGIN_HEADER FT_Vector *akerning ); + /*************************************************************************/ + /* */ + /* */ + /* FT_Get_Track_Kerning */ + /* */ + /* */ + /* Return the track kerning for a given face object at a given size. */ + /* */ + /* */ + /* face :: A handle to a source face object. */ + /* */ + /* point_size :: The point size in 16.16 fractional points. */ + /* */ + /* degree :: The degree of tightness. */ + /* */ + /* */ + /* akerning :: The kerning in in 16.16 fractional points. */ + /* */ + /* */ + /* FreeType error code. 0 means success. */ + /* */ + FT_EXPORT( FT_Error ) + FT_Get_Track_Kerning( FT_Face face, + FT_Fixed point_size, + FT_Int degree, + FT_Fixed* akerning ); + + /*************************************************************************/ /* */ /* */ diff --git a/include/freetype/internal/ftserv.h b/include/freetype/internal/ftserv.h index 3dd6b3e7e..d690bc94f 100644 --- a/include/freetype/internal/ftserv.h +++ b/include/freetype/internal/ftserv.h @@ -313,6 +313,7 @@ FT_BEGIN_HEADER #define FT_SERVICE_TT_CMAP_H #define FT_SERVICE_WINFNT_H #define FT_SERVICE_XFREE86_NAME_H +#define FT_SERVICE_KERNING_H /* */ diff --git a/include/freetype/internal/psaux.h b/include/freetype/internal/psaux.h index cf1abf4c9..06fe1647a 100644 --- a/include/freetype/internal/psaux.h +++ b/include/freetype/internal/psaux.h @@ -685,6 +685,96 @@ FT_BEGIN_HEADER } T1_DecoderRec; + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** AFM PARSER *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + typedef struct AFM_ParserRec_* AFM_Parser; + + typedef struct AFM_TrackKernRec_ + { + FT_Int degree; + FT_Fixed min_ptsize; + FT_Fixed min_kern; + FT_Fixed max_ptsize; + FT_Fixed max_kern; + } AFM_TrackKernRec, *AFM_TrackKern; + + typedef struct AFM_KernPairRec_ + { + FT_Int index1; + FT_Int index2; + FT_Int x; + FT_Int y; + } AFM_KernPairRec, *AFM_KernPair; + + typedef struct AFM_FontInfoRec_ + { + FT_Bool IsCIDFont; + AFM_TrackKern TrackKerns; /* free if non-NULL */ + FT_Int NumTrackKern; + AFM_KernPair KernPairs; /* free if non-NULL */ + FT_Int NumKernPair; + } AFM_FontInfoRec, *AFM_FontInfo; + + typedef struct AFM_Parser_FuncsRec_ + { + FT_Error + (*init)( AFM_Parser parser, + FT_Memory memory, + FT_Byte* base, + FT_Byte* limit ); + + void + (*done)( AFM_Parser parser ); + + FT_Error + (*parse)( AFM_Parser parser ); + + } AFM_Parser_FuncsRec; + + typedef struct AFM_StreamRec_* AFM_Stream; + + /*************************************************************************/ + /* */ + /* */ + /* AFM_ParserRec */ + /* */ + /* */ + /* An AFM_Parser is a parser for the AFM files. */ + /* */ + /* */ + /* memory :: The object used for memory operations */ + /* (alloc/realloc). */ + /* */ + /* stream :: This is an opaque object. */ + /* */ + /* FontInfo :: The result will be stored here. */ + /* */ + /* get_index :: An user provided function to get glyph index by its */ + /* name. */ + /* */ + typedef struct AFM_ParserRec_ + { + FT_Memory memory; + AFM_Stream stream; + + AFM_FontInfo FontInfo; + + FT_Int + (*get_index)( const char* name, + FT_UInt len, + void* user_data ); + + void* user_data; + + } AFM_ParserRec; + + /*************************************************************************/ /*************************************************************************/ /***** *****/ @@ -720,6 +810,7 @@ FT_BEGIN_HEADER const PS_Parser_FuncsRec* ps_parser_funcs; const T1_Builder_FuncsRec* t1_builder_funcs; const T1_Decoder_FuncsRec* t1_decoder_funcs; + const AFM_Parser_FuncsRec* afm_parser_funcs; void (*t1_decrypt)( FT_Byte* buffer, diff --git a/include/freetype/internal/services/svkern.h b/include/freetype/internal/services/svkern.h new file mode 100644 index 000000000..95921f8ba --- /dev/null +++ b/include/freetype/internal/services/svkern.h @@ -0,0 +1,51 @@ +/***************************************************************************/ +/* */ +/* svkern.h */ +/* */ +/* The FreeType Kerning service (specification). */ +/* */ +/* Copyright 2003, 2004 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. */ +/* */ +/***************************************************************************/ + + +#ifndef __SVKERN_H__ +#define __SVKERN_H__ + +#include FT_INTERNAL_SERVICE_H +#include FT_TRUETYPE_TABLES_H + + +FT_BEGIN_HEADER + +#define FT_SERVICE_ID_KERNING "kerning" + + + typedef FT_Error + (*FT_Kerning_TrackGetFunc)( FT_Face face, + FT_Fixed point_size, + FT_Int degree, + FT_Fixed* akerning ); + + FT_DEFINE_SERVICE( Kerning ) + { + FT_Kerning_TrackGetFunc get_track; + }; + + /* */ + + +FT_END_HEADER + + +#endif /* __SVKERN_H__ */ + + +/* END */ diff --git a/src/base/ftobjs.c b/src/base/ftobjs.c index 59542577e..5ee4710b2 100644 --- a/src/base/ftobjs.c +++ b/src/base/ftobjs.c @@ -33,6 +33,7 @@ #include FT_SERVICE_POSTSCRIPT_NAME_H #include FT_SERVICE_GLYPH_DICT_H #include FT_SERVICE_TT_CMAP_H +#include FT_SERVICE_KERNING_H FT_BASE_DEF( FT_Pointer ) @@ -2376,6 +2377,40 @@ } + /* documentation is in freetype.h */ + + FT_EXPORT_DEF( FT_Error ) + FT_Get_Track_Kerning( FT_Face face, + FT_Fixed point_size, + FT_Int degree, + FT_Fixed* akerning ) + { + FT_Service_Kerning service; + FT_Error error = FT_Err_Ok; + FT_Driver driver; + + + if ( !face ) + return FT_Err_Invalid_Face_Handle; + + if ( !akerning ) + return FT_Err_Invalid_Argument; + + driver = face->driver; + + FT_FACE_FIND_SERVICE( face, service, KERNING ); + if ( !service ) + return FT_Err_Unimplemented_Feature; + + error = service->get_track( face, + point_size, + degree, + akerning ); + + return error; + } + + /* documentation is in freetype.h */ FT_EXPORT_DEF( FT_Error ) diff --git a/src/psaux/afmparse.c b/src/psaux/afmparse.c new file mode 100644 index 000000000..999f72a2e --- /dev/null +++ b/src/psaux/afmparse.c @@ -0,0 +1,925 @@ +/***************************************************************************/ +/* */ +/* afmparse.c */ +/* */ +/* AFM parser (body). */ +/* */ +/* Copyright 2006 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 FT_INTERNAL_POSTSCRIPT_AUX_H +#include FT_INTERNAL_DEBUG_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; + + + 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 val 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_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* 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" + }; + + +#define AFM_MAX_ARGUMENTS 5 + + static AFM_ValueRec shared_vals[AFM_MAX_ARGUMENTS]; + + + /* + * `afm_parser_read_vals' and `afm_parser_next_key' provides + * high-level operations to an AFM_Stream. The rest of the + * parser functions should use them and should not access + * the AFM_Stream directly. + */ + + FT_LOCAL_DEF( FT_Int ) + afm_parser_read_vals( AFM_Parser parser, + AFM_Value vals, + FT_Int n ) + { + AFM_Stream stream = parser->stream; + char* str; + FT_Int i; + + + if ( n > AFM_MAX_ARGUMENTS ) + return 0; + + for ( i = 0; i < n; i++ ) + { + FT_UInt len; + + + if ( vals[i].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 ( vals[i].type ) + { + case AFM_VALUE_TYPE_STRING: + case AFM_VALUE_TYPE_NAME: + if ( !FT_QAlloc( parser->memory, len + 1, (void**)&vals[i].u.s ) ) + { + ft_memcpy( vals[i].u.s, str, len ); + vals[i].u.s[len] = '\0'; + } + break; + case AFM_VALUE_TYPE_FIXED: + vals[i].u.f = PS_Conv_ToFixed( (FT_Byte**)&str, + (FT_Byte*)str + len, + 0 ); + break; + case AFM_VALUE_TYPE_INTEGER: + vals[i].u.i = PS_Conv_ToInt( (FT_Byte**)&str, + (FT_Byte*)str + len ); + break; + case AFM_VALUE_TYPE_BOOL: + vals[i].u.b = ( len == 4 && + ft_strncmp( str, "true", 4 ) == 0 ); + break; + case AFM_VALUE_TYPE_INDEX: + if ( parser->get_index ) + vals[i].u.i = parser->get_index( str, + len, + parser->user_data ); + else + vals[i].u.i = 0; + break; + } + } + + return i; + } + + + FT_LOCAL_DEF( char* ) + afm_parser_next_key( AFM_Parser parser, + FT_Bool line, + FT_UInt* len ) + { + AFM_Stream stream = parser->stream; + char* key; + + + 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 ) ? AFM_STREAM_KEY_LEN( stream, key ) + : 0; + + return key; + } + + + static AFM_Token + afm_tokenize( const char* key, + FT_UInt 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 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; + FT_Error error; + + + if ( FT_NEW( stream ) ) + return error; + + stream->cursor = stream->base = base; + stream->limit = limit; + + /* so that the first call won't skip the first line */ + 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_UInt 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; + + + FT_QNEW_ARRAY( fi->TrackKerns, fi->NumTrackKern ); + if ( error ) + return error; + } + + while ( ( key = afm_parser_next_key( parser, 1, &len ) ) ) + { + 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; + break; + + case AFM_TOKEN_UNKNOWN: + break; + + default: + goto Fail; + break; + } + } + + 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 ); + + + return (int)( index1 - index2 ); + } + + + static FT_Error + afm_parse_kern_pairs( AFM_Parser parser ) + { + AFM_FontInfo fi = parser->FontInfo; + AFM_KernPair kp; + char* key; + FT_UInt 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; + + + FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ); + if ( error ) + return error; + } + + while ( ( key = afm_parser_next_key( parser, 1, &len ) ) ) + { + AFM_Token token = afm_tokenize( key, len ); + + + switch ( token ) + { + case AFM_TOKEN_KP: + case AFM_TOKEN_KPX: + case AFM_TOKEN_KPY: + { + FT_Int r; + + + 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; + break; + + case AFM_TOKEN_UNKNOWN: + break; + + default: + goto Fail; + break; + } + } + + Fail: + return PSaux_Err_Syntax_Error; + } + + + static FT_Error + afm_parse_kern_data( AFM_Parser parser ) + { + FT_Error error; + char* key; + FT_UInt len; + + + while ( ( key = afm_parser_next_key( parser, 1, &len ) ) ) + { + 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; + break; + + case AFM_TOKEN_UNKNOWN: + break; + + default: + goto Fail; + break; + } + } + + 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_UInt 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 ) ) ) + { + 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_UInt 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 ) ) ) + { + 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_STARTCHARMETRICS: + { + FT_Int n; + + + 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; + /* no break since we only support kern data */ + + case AFM_TOKEN_ENDFONTMETRICS: + return PSaux_Err_Ok; + break; + + default: + break; + } + } + + Fail: + FT_FREE( fi->TrackKerns ); + fi->NumTrackKern = 0; + + FT_FREE( fi->KernPairs ); + fi->NumKernPair = 0; + + fi->IsCIDFont = 0; + + return error; + } diff --git a/src/psaux/afmparse.h b/src/psaux/afmparse.h new file mode 100644 index 000000000..bb41cfb75 --- /dev/null +++ b/src/psaux/afmparse.h @@ -0,0 +1,85 @@ +/***************************************************************************/ +/* */ +/* afmparse.h */ +/* */ +/* AFM parser (specification). */ +/* */ +/* Copyright 2006 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. */ +/* */ +/***************************************************************************/ + + +#ifndef __AFMPARSE_H__ +#define __AFMPARSE_H__ + + +#include +#include FT_INTERNAL_POSTSCRIPT_AUX_H + + +FT_BEGIN_HEADER + + + FT_LOCAL( FT_Error ) + afm_parser_init( AFM_Parser parser, + FT_Memory memory, + FT_Byte* base, + FT_Byte* limit ); + + + FT_LOCAL( void ) + afm_parser_done( AFM_Parser parser ); + + + FT_LOCAL( FT_Error ) + afm_parser_parse( AFM_Parser parser ); + + + enum AFM_ValueType_ + { + AFM_VALUE_TYPE_STRING, + AFM_VALUE_TYPE_NAME, + AFM_VALUE_TYPE_FIXED, /* real number */ + AFM_VALUE_TYPE_INTEGER, + AFM_VALUE_TYPE_BOOL, + AFM_VALUE_TYPE_INDEX /* glyph index */ + }; + + + typedef struct + { + enum AFM_ValueType_ type; + union { + char* s; + FT_Fixed f; + FT_Int i; + FT_Bool b; + } u; + } AFM_ValueRec, *AFM_Value; + + + FT_LOCAL( FT_Int ) + afm_parser_read_vals( AFM_Parser parser, + AFM_Value vals, + FT_Int n ); + + + /* read the next key from the next line or column */ + FT_LOCAL( char* ) + afm_parser_next_key( AFM_Parser parser, + FT_Bool line, + FT_UInt* len ); + +FT_END_HEADER + +#endif /* __AFMPARSE_H__ */ + + +/* END */ diff --git a/src/psaux/psaux.c b/src/psaux/psaux.c index 992818414..e5d6d262f 100644 --- a/src/psaux/psaux.c +++ b/src/psaux/psaux.c @@ -23,6 +23,8 @@ #include "psauxmod.c" #include "t1decode.c" #include "t1cmap.c" +#include "afmparse.c" +#include "psconv.c" /* END */ diff --git a/src/psaux/psauxmod.c b/src/psaux/psauxmod.c index 756ec1c56..42330d543 100644 --- a/src/psaux/psauxmod.c +++ b/src/psaux/psauxmod.c @@ -21,6 +21,7 @@ #include "psobjs.h" #include "t1decode.h" #include "t1cmap.h" +#include "afmparse.h" FT_CALLBACK_TABLE_DEF @@ -75,6 +76,15 @@ }; + FT_CALLBACK_TABLE_DEF + const AFM_Parser_FuncsRec afm_parser_funcs = + { + afm_parser_init, + afm_parser_done, + afm_parser_parse + }; + + FT_CALLBACK_TABLE_DEF const T1_CMap_ClassesRec t1_cmap_classes = { @@ -92,6 +102,7 @@ &ps_parser_funcs, &t1_builder_funcs, &t1_decoder_funcs, + &afm_parser_funcs, t1_decrypt, diff --git a/src/psaux/psconv.c b/src/psaux/psconv.c new file mode 100644 index 000000000..66ad547d4 --- /dev/null +++ b/src/psaux/psconv.c @@ -0,0 +1,394 @@ +/***************************************************************************/ +/* */ +/* psconv.c */ +/* */ +/* Some convenient conversions (body). */ +/* */ +/* Copyright 2006 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 +#include FT_INTERNAL_POSTSCRIPT_AUX_H +#include FT_INTERNAL_DEBUG_H + +#include "psobjs.h" +#include "psauxerr.h" + + +/* The following array is used by various functions to quickly convert */ +/* digits (both decimal and non-decimal) into numbers. */ + +#if 'A' == 65 + /* ASCII */ + + static const char ft_char_table[128] = + { + /* 0x00 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + }; + + /* no character >= 0x80 can represent a valid number */ +#define OP >= + +#endif /* 'A' == 65 */ + +#if 'A' == 193 + /* EBCDIC */ + + static const char ft_char_table[128] = + { + /* 0x80 */ + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1, + -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, + -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1, + -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, + -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + }; + + /* no character < 0x80 can represent a valid number */ +#define OP < + +#endif /* 'A' == 193 */ + + FT_LOCAL_DEF( FT_Int ) + PS_Conv_Strtol( FT_Byte** cursor, + FT_Byte* limit, + FT_Int base ) + { + FT_Byte* p = *cursor; + FT_Int num = 0; + FT_Bool sign = 0; + + + if ( p == limit || base < 2 || base > 36 ) + return 0; + + if ( *p == '-' || *p == '+' ) + { + sign = ( *p == '-' ); + + p++; + if ( p == limit ) + return 0; + } + + for ( ; p < limit; p++ ) + { + char c; + + + if ( IS_PS_SPACE( *p ) || *p OP 0x80 ) + break; + + c = ft_char_table[*p & 0x7f]; + + if ( c < 0 || c >= base ) + break; + + num = num * base + c; + } + + if ( sign ) + num = -num; + + *cursor = p; + + return num; + } + + + FT_LOCAL_DEF( FT_Int ) + PS_Conv_ToInt( FT_Byte** cursor, + FT_Byte* limit ) + + { + FT_Byte* p; + FT_Int num; + + + num = PS_Conv_Strtol( cursor, limit, 10 ); + p = *cursor; + + if ( p < limit && *p == '#' ) + { + *cursor = p + 1; + + return PS_Conv_Strtol( cursor, limit, num ); + } + else + return num; + } + + + FT_LOCAL_DEF( FT_Fixed ) + PS_Conv_ToFixed( FT_Byte** cursor, + FT_Byte* limit, + FT_Int power_ten ) + { + FT_Byte* p = *cursor; + FT_Fixed integral; + FT_Long decimal = 0, divider = 1; + FT_Bool sign = 0; + + + if ( p == limit ) + return 0; + + if ( *p == '-' || *p == '+' ) + { + sign = ( *p == '-' ); + + p++; + if ( p == limit ) + return 0; + } + + if ( *p != '.' ) + { + integral = PS_Conv_ToInt( &p, limit ) << 16; + + if ( p == limit ) + goto Exit; + } + else + integral = 0; + + /* read the decimal part */ + if ( *p == '.' ) + { + p++; + + for ( ; p < limit; p++ ) + { + char c; + + + if ( IS_PS_SPACE( *p ) || *p OP 0x80 ) + break; + + c = ft_char_table[*p & 0x7f]; + + if ( c < 0 || c >= 10 ) + break; + + if ( divider < 10000000L ) + { + decimal = decimal * 10 + c; + divider *= 10; + } + } + } + + /* read exponent, if any */ + if ( p + 1 < limit && ( *p == 'e' || *p == 'E' ) ) + { + p++; + power_ten += PS_Conv_ToInt( &p, limit ); + } + +Exit: + while ( power_ten > 0 ) + { + integral *= 10; + decimal *= 10; + power_ten--; + } + + while ( power_ten < 0 ) + { + integral /= 10; + divider *= 10; + power_ten++; + } + + if ( decimal ) + integral += FT_DivFix( decimal, divider ); + + if ( sign ) + integral = -integral; + + *cursor = p; + + return integral; + } + + +#if 0 + FT_LOCAL_DEF( FT_UInt ) + PS_Conv_StringDecode( FT_Byte** cursor, + FT_Byte* limit, + FT_Byte* buffer, + FT_UInt n ) + { + FT_Byte* p; + FT_UInt r = 0; + + + for ( p = *cursor; r < n && p < limit; p++ ) + { + FT_Byte b; + + + if ( *p != '\\' ) + { + buffer[r++] = *p; + + continue; + } + + p++; + + switch ( *p ) + { + case 'n': + b = '\n'; + break; + case 'r': + b = '\r'; + break; + case 't': + b = '\t'; + break; + case 'b': + b = '\b'; + break; + case 'f': + b = '\f'; + break; + case '\r': + p++; + if ( *p != '\n' ) + { + b = *p; + + break; + } + /* no break */ + case '\n': + continue; + break; + default: + if ( IS_PS_DIGIT( *p ) ) + { + b = *p - '0'; + + p++; + + if ( IS_PS_DIGIT( *p ) ) + { + b = b * 8 + *p - '0'; + + p++; + + if ( IS_PS_DIGIT( *p ) ) + b = b * 8 + *p - '0'; + else + { + buffer[r++] = b; + b = *p; + } + } + else + { + buffer[r++] = b; + b = *p; + } + } + else + b = *p; + break; + } + + buffer[r++] = b; + } + + *cursor = p; + + return r; + } +#endif + + + FT_LOCAL_DEF( FT_UInt ) + PS_Conv_ASCIIHexDecode( FT_Byte** cursor, + FT_Byte* limit, + FT_Byte* buffer, + FT_UInt n ) + { + FT_Byte* p; + FT_UInt r = 0; + + + for ( p = *cursor; r < 2 * n && p < limit; p++ ) + { + char c; + + + if ( IS_PS_SPACE( *p ) ) + continue; + + if ( *p OP 0x80 ) + break; + + c = ft_char_table[*p & 0x7f]; + + if ( c < 0 || c >= 16 ) + break; + + if ( r % 2 ) + *buffer++ += c; + else + *buffer = c << 4; + + r++; + } + + *cursor = p; + + return ( r + 1 ) / 2; + } + + + FT_LOCAL_DEF( FT_UInt ) + PS_Conv_EexecDecode( FT_Byte** cursor, + FT_Byte* limit, + FT_Byte* buffer, + FT_UInt n, + FT_UShort* seed ) + { + FT_Byte* p; + FT_UInt r; + + + for ( r = 0, p = *cursor; r < n && p < limit; r++, p++ ) + { + FT_Byte b = ( *p ^ ( *seed >> 8 ) ); + + + *seed = (FT_UShort)( ( *p + *seed ) * 52845U + 22719 ); + *buffer++ = b; + } + + *cursor = p; + + return r; + } diff --git a/src/psaux/psconv.h b/src/psaux/psconv.h new file mode 100644 index 000000000..823e05a10 --- /dev/null +++ b/src/psaux/psconv.h @@ -0,0 +1,106 @@ +/***************************************************************************/ +/* */ +/* psconv.h */ +/* */ +/* Some convenient conversions (specification). */ +/* */ +/* Copyright 2006 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. */ +/* */ +/***************************************************************************/ + + +#ifndef __PSCONV_H__ +#define __PSCONV_H__ + + +#include +#include FT_INTERNAL_POSTSCRIPT_AUX_H + +FT_BEGIN_HEADER + + + FT_LOCAL_DEF( FT_Int ) + PS_Conv_Strtol( FT_Byte** cursor, + FT_Byte* limit, + FT_Int base ); + + + FT_LOCAL( FT_Int ) + PS_Conv_ToInt( FT_Byte** cursor, + FT_Byte* limit ); + + FT_LOCAL( FT_Fixed ) + PS_Conv_ToFixed( FT_Byte** cursor, + FT_Byte* limit, + FT_Int power_ten ); + +#if 0 + FT_LOCAL( FT_UInt ) + PS_Conv_StringDecode( FT_Byte** cursor, + FT_Byte* limit, + FT_Byte* buffer, + FT_UInt n ); +#endif + + FT_LOCAL( FT_UInt ) + PS_Conv_ASCIIHexDecode( FT_Byte** cursor, + FT_Byte* limit, + FT_Byte* buffer, + FT_UInt n ); + + FT_LOCAL( FT_UInt ) + PS_Conv_EexecDecode( FT_Byte** cursor, + FT_Byte* limit, + FT_Byte* buffer, + FT_UInt n, + FT_UShort* seed ); + +#define IS_PS_NEWLINE( ch ) \ + ( ( ch ) == '\r' || \ + ( ch ) == '\n' ) + +#define IS_PS_SPACE( ch ) \ + ( ( ch ) == ' ' || \ + IS_PS_NEWLINE( ch ) || \ + ( ch ) == '\t' || \ + ( ch ) == '\f' || \ + ( ch ) == '\0' ) + +#define IS_PS_SPECIAL( ch ) \ + ( ( ch ) == '/' || \ + ( ch ) == '(' || \ + ( ch ) == ')' || \ + ( ch ) == '<' || \ + ( ch ) == '>' || \ + ( ch ) == '[' || \ + ( ch ) == ']' || \ + ( ch ) == '{' || \ + ( ch ) == '}' || \ + ( ch ) == '%' ) + +#define IS_PS_DELIM( ch ) \ + ( IS_PS_SPACE( ch ) || \ + IS_PS_SPECIAL( ch ) ) + +#define IS_PS_DIGIT( ch ) ( ( ch ) >= '0' && ( ch ) <= '9' ) + +#define IS_PS_XDIGIT( ch ) \ + ( IS_PS_DIGIT( ( ch ) ) || \ + ( ( ch ) >= 'A' && ( ch ) <= 'F' ) || \ + ( ( ch ) >= 'a' && ( ch ) <= 'f' ) ) + +#define IS_PS_BASE85( ch ) ( ( ch ) >= '!' && ( ch ) <= 'u' ) + +FT_END_HEADER + +#endif /* __PSCONV_H__ */ + + +/* END */ diff --git a/src/psaux/psobjs.c b/src/psaux/psobjs.c index 5676ac255..98f4e0b21 100644 --- a/src/psaux/psobjs.c +++ b/src/psaux/psobjs.c @@ -21,6 +21,7 @@ #include FT_INTERNAL_DEBUG_H #include "psobjs.h" +#include "psconv.h" #include "psauxerr.h" @@ -266,64 +267,6 @@ /*************************************************************************/ /*************************************************************************/ - /* In the PostScript Language Reference Manual (PLRM) the following */ - /* characters are called `whitespace characters'. */ -#define IS_T1_WHITESPACE( c ) ( (c) == ' ' || (c) == '\t' ) -#define IS_T1_LINESPACE( c ) ( (c) == '\r' || (c) == '\n' || (c) == '\f' ) -#define IS_T1_NULLSPACE( c ) ( (c) == '\0' ) - - /* According to the PLRM all whitespace characters are equivalent, */ - /* except in comments and strings. */ -#define IS_T1_SPACE( c ) ( IS_T1_WHITESPACE( c ) || \ - IS_T1_LINESPACE( c ) || \ - IS_T1_NULLSPACE( c ) ) - - - /* The following array is used by various functions to quickly convert */ - /* digits (both decimal and non-decimal) into numbers. */ - -#if 'A' == 65 - /* ASCII */ - - static const char ft_char_table[128] = - { - /* 0x00 */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, - -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, - -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, - }; - - /* no character >= 0x80 can represent a valid number */ -#define OP >= - -#endif /* 'A' == 65 */ - -#if 'A' == 193 - /* EBCDIC */ - - static const char ft_char_table[128] = - { - /* 0x80 */ - -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1, - -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, - -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1, - -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, - -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, - } - - /* no character < 0x80 can represent a valid number */ -#define OP < - -#endif /* 'A' == 193 */ - /* first character must be already part of the comment */ @@ -336,7 +279,7 @@ while ( cur < limit ) { - if ( IS_T1_LINESPACE( *cur ) ) + if ( IS_PS_NEWLINE( *cur ) ) break; cur++; } @@ -354,7 +297,7 @@ while ( cur < limit ) { - if ( !IS_T1_SPACE( *cur ) ) + if ( !IS_PS_SPACE( *cur ) ) { if ( *cur == '%' ) /* According to the PLRM, a comment is equal to a space. */ @@ -412,19 +355,12 @@ while ( ++cur < limit ) { - int d; - - /* All whitespace characters are ignored. */ skip_spaces( &cur, limit ); if ( cur >= limit ) break; - if ( *cur OP 0x80 ) - break; - - d = ft_char_table[*cur & 0x7F]; - if ( d < 0 || d >= 16 ) + if ( !IS_PS_XDIGIT( *cur ) ) break; } @@ -510,15 +446,6 @@ /* anything else */ while ( cur < limit ) { - if ( IS_T1_SPACE( *cur ) || - *cur == '(' || - *cur == '/' || - *cur == '%' || - *cur == '[' || *cur == ']' || - *cur == '{' || *cur == '}' || - *cur == '<' || *cur == '>' ) - break; - if ( *cur == ')' ) { FT_ERROR(( "ps_parser_skip_PS_token: " @@ -526,6 +453,8 @@ parser->error = PSaux_Err_Invalid_File_Format; goto Exit; } + else if ( IS_PS_DELIM( *cur ) ) + break; cur++; } @@ -692,270 +621,6 @@ } - /* first character must be already part of the number */ - - static FT_Long - ps_radix( FT_Long radixBase, - FT_Byte* *acur, - FT_Byte* limit ) - { - FT_Long result = 0; - FT_Byte* cur = *acur; - - - if ( radixBase < 2 || radixBase > 36 ) - return 0; - - while ( cur < limit ) - { - int d; - - - if ( *cur OP 0x80 ) - break; - - d = ft_char_table[*cur & 0x7F]; - if ( d < 0 || d >= radixBase ) - break; - - result = result * radixBase + d; - - cur++; - } - - *acur = cur; - - return result; - } - - - /* first character must be already part of the number */ - - static FT_Long - ps_toint( FT_Byte* *acur, - FT_Byte* limit ) - { - FT_Long result = 0; - FT_Byte* cur = *acur; - FT_Byte c; - - - if ( cur >= limit ) - goto Exit; - - c = *cur; - if ( c == '-' ) - cur++; - - while ( cur < limit ) - { - int d; - - - if ( *cur == '#' ) - { - cur++; - result = ps_radix( result, &cur, limit ); - break; - } - - if ( *cur OP 0x80 ) - break; - - d = ft_char_table[*cur & 0x7F]; - if ( d < 0 || d >= 10 ) - break; - result = result * 10 + d; - - cur++; - }; - - if ( c == '-' ) - result = -result; - - Exit: - *acur = cur; - return result; - } - - - /* first character must be `<' if `delimiters' is non-zero */ - - static FT_Error - ps_tobytes( FT_Byte* *acur, - FT_Byte* limit, - FT_Long max_bytes, - FT_Byte* bytes, - FT_Long* pnum_bytes, - FT_Bool delimiters ) - { - FT_Error error = PSaux_Err_Ok; - - FT_Byte* cur = *acur; - FT_Long n = 0; - - - if ( cur >= limit ) - goto Exit; - - if ( delimiters ) - { - if ( *cur != '<' ) - { - FT_ERROR(( "ps_tobytes: Missing starting delimiter `<'\n" )); - error = PSaux_Err_Invalid_File_Format; - goto Exit; - } - - cur++; - } - - max_bytes = max_bytes * 2; - - for ( n = 0; cur < limit; n++, cur++ ) - { - int d; - - - if ( n >= max_bytes ) - /* buffer is full */ - goto Exit; - - /* All whitespace characters are ignored. */ - skip_spaces( &cur, limit ); - if ( cur >= limit ) - break; - - if ( *cur OP 0x80 ) - break; - - d = ft_char_table[*cur & 0x7F]; - if ( d < 0 || d >= 16 ) - break; - - /* == != <0f> */ - bytes[n / 2] = (FT_Byte)( ( n % 2 ) ? bytes[n / 2] + d - : d * 16 ); - } - - if ( delimiters ) - { - if ( cur < limit && *cur != '>' ) - { - FT_ERROR(( "ps_tobytes: Missing closing delimiter `>'\n" )); - error = PSaux_Err_Invalid_File_Format; - goto Exit; - } - - cur++; - } - - *acur = cur; - - Exit: - *pnum_bytes = ( n + 1 ) / 2; - - return error; - } - - - /* first character must be already part of the number */ - - static FT_Long - ps_tofixed( FT_Byte* *acur, - FT_Byte* limit, - FT_Long power_ten ) - { - FT_Byte* cur = *acur; - FT_Long num, divider, result; - FT_Int sign = 0; - - - if ( cur >= limit ) - return 0; - - /* first of all, check the sign */ - if ( *cur == '-' && cur + 1 < limit ) - { - sign = 1; - cur++; - } - - /* then, read the integer part, if any */ - if ( *cur != '.' ) - result = ps_toint( &cur, limit ) << 16; - else - result = 0; - - num = 0; - divider = 1; - - if ( cur >= limit ) - goto Exit; - - /* read decimal part, if any */ - if ( *cur == '.' && cur + 1 < limit ) - { - cur++; - - for (;;) - { - int d; - - - if ( *cur OP 0x80 ) - break; - - d = ft_char_table[*cur & 0x7F]; - if ( d < 0 || d >= 10 ) - break; - - if ( divider < 10000000L ) - { - num = num * 10 + d; - divider *= 10; - } - - cur++; - if ( cur >= limit ) - break; - } - } - - /* read exponent, if any */ - if ( cur + 1 < limit && ( *cur == 'e' || *cur == 'E' ) ) - { - cur++; - power_ten += ps_toint( &cur, limit ); - } - - Exit: - /* raise to power of ten if needed */ - while ( power_ten > 0 ) - { - result = result * 10; - num = num * 10; - power_ten--; - } - - while ( power_ten < 0 ) - { - result = result / 10; - divider = divider * 10; - power_ten++; - } - - if ( num ) - result += FT_DivFix( num, divider ); - - if ( sign ) - result = -result; - - *acur = cur; - return result; - } - - /* first character must be a delimiter or a part of a number */ static FT_Int @@ -1003,7 +668,8 @@ break; } - coords[count] = (FT_Short)( ps_tofixed( &cur, limit, 0 ) >> 16 ); + coords[count] = + (FT_Short)( PS_Conv_ToFixed( &cur, limit, 0 ) >> 16 ); count++; if ( !ender ) @@ -1064,7 +730,7 @@ break; } - values[count] = ps_tofixed( &cur, limit, power_ten ); + values[count] = PS_Conv_ToFixed( &cur, limit, power_ten ); count++; if ( !ender ) @@ -1250,15 +916,15 @@ goto Store_Integer; case T1_FIELD_TYPE_FIXED: - val = ps_tofixed( &cur, limit, 0 ); + val = PS_Conv_ToFixed( &cur, limit, 0 ); goto Store_Integer; case T1_FIELD_TYPE_FIXED_1000: - val = ps_tofixed( &cur, limit, 3 ); + val = PS_Conv_ToFixed( &cur, limit, 3 ); goto Store_Integer; case T1_FIELD_TYPE_INTEGER: - val = ps_toint( &cur, limit ); + val = PS_Conv_ToInt( &cur, limit ); /* fall through */ Store_Integer: @@ -1424,10 +1090,12 @@ ps_parser_to_int( PS_Parser parser ) { ps_parser_skip_spaces( parser ); - return ps_toint( &parser->cursor, parser->limit ); + return PS_Conv_ToInt( &parser->cursor, parser->limit ); } + /* first character must be `<' if `delimiters' is non-zero */ + FT_LOCAL_DEF( FT_Error ) ps_parser_to_bytes( PS_Parser parser, FT_Byte* bytes, @@ -1435,13 +1103,49 @@ FT_Long* pnum_bytes, FT_Bool delimiters ) { + FT_Error error = PSaux_Err_Ok; + FT_Byte* cur; + + ps_parser_skip_spaces( parser ); - return ps_tobytes( &parser->cursor, - parser->limit, - max_bytes, - bytes, - pnum_bytes, - delimiters ); + cur = parser->cursor; + + if ( cur >= parser->limit ) + goto Exit; + + if ( delimiters ) + { + if ( *cur != '<' ) + { + FT_ERROR(( "ps_tobytes: Missing starting delimiter `<'\n" )); + error = PSaux_Err_Invalid_File_Format; + goto Exit; + } + + cur++; + } + + *pnum_bytes = PS_Conv_ASCIIHexDecode( &cur, + parser->limit, + bytes, + max_bytes ); + + if ( delimiters ) + { + if ( cur < parser->limit && *cur != '>' ) + { + FT_ERROR(( "ps_tobytes: Missing closing delimiter `>'\n" )); + error = PSaux_Err_Invalid_File_Format; + goto Exit; + } + + cur++; + } + + parser->cursor = cur; + + Exit: + return error; } @@ -1450,7 +1154,7 @@ FT_Int power_ten ) { ps_parser_skip_spaces( parser ); - return ps_tofixed( &parser->cursor, parser->limit, power_ten ); + return PS_Conv_ToFixed( &parser->cursor, parser->limit, power_ten ); } @@ -1780,16 +1484,11 @@ FT_Offset length, FT_UShort seed ) { - while ( length > 0 ) - { - FT_Byte plain; - - - plain = (FT_Byte)( *buffer ^ ( seed >> 8 ) ); - seed = (FT_UShort)( ( *buffer + seed ) * 52845U + 22719 ); - *buffer++ = plain; - length--; - } + PS_Conv_EexecDecode( &buffer, + buffer + length, + buffer, + length, + &seed ); } diff --git a/src/psaux/rules.mk b/src/psaux/rules.mk index 7338d3657..50a1f033d 100644 --- a/src/psaux/rules.mk +++ b/src/psaux/rules.mk @@ -28,6 +28,8 @@ PSAUX_COMPILE := $(FT_COMPILE) $I$(subst /,$(COMPILER_SEP),$(PSAUX_DIR)) PSAUX_DRV_SRC := $(PSAUX_DIR)/psobjs.c \ $(PSAUX_DIR)/t1decode.c \ $(PSAUX_DIR)/t1cmap.c \ + $(PSAUX_DIR)/afmparse.c \ + $(PSAUX_DIR)/psconv.c \ $(PSAUX_DIR)/psauxmod.c # PSAUX driver headers diff --git a/src/tools/test_afm.c b/src/tools/test_afm.c new file mode 100644 index 000000000..d82f454ae --- /dev/null +++ b/src/tools/test_afm.c @@ -0,0 +1,146 @@ +#include +#include FT_FREETYPE_H +#include FT_INTERNAL_STREAM_H +#include FT_INTERNAL_POSTSCRIPT_AUX_H + + void dump_fontinfo( AFM_FontInfo fi ) + { + FT_Int i; + + + printf( "This AFM is for %sCID font.\n\n", + ( fi->IsCIDFont ) ? "" : "non-" ); + + if ( fi->NumTrackKern ) + printf( "There are %d sets of track kernings:\n", + fi->NumTrackKern ); + else + printf( "There is no track kerning.\n" ); + + for ( i = 0; i < fi->NumTrackKern; i++ ) + { + AFM_TrackKern tk = fi->TrackKerns + i; + + + printf( "\t%2d: %5.2f %5.2f %5.2f %5.2f\n", tk->degree, + tk->min_ptsize / 65536., + tk->min_kern / 65536., + tk->max_ptsize / 65536., + tk->max_kern / 65536. ); + } + + printf( "\n" ); + + if ( fi->NumTrackKern ) + printf( "There are %d kerning pairs:\n", + fi->NumKernPair ); + else + printf( "There is no kerning pair.\n" ); + + for ( i = 0; i < fi->NumKernPair; i++ ) + { + AFM_KernPair kp = fi->KernPairs + i; + + + printf( "\t%3d + %3d => (%4d, %4d)\n", kp->index1, + kp->index2, + kp->x, + kp->y ); + } + + } + + int + dummy_get_index( const char* name, + FT_UInt len, + void* user_data ) + { + if ( len ) + return name[0]; + else + return 0; + } + + FT_Error + parse_afm( FT_Library library, + FT_Stream stream, + AFM_FontInfo fi ) + { + PSAux_Service psaux; + AFM_ParserRec parser; + FT_Error error = FT_Err_Ok; + + + psaux = (PSAux_Service)FT_Get_Module_Interface( library, "psaux" ); + if ( !psaux || !psaux->afm_parser_funcs ) + return -1; + + error = FT_Stream_EnterFrame( stream, stream->size ); + if ( error ) + return error; + + error = psaux->afm_parser_funcs->init( &parser, + library->memory, + stream->cursor, + stream->limit ); + if ( error ) + return error; + + parser.FontInfo = fi; + parser.get_index = dummy_get_index; + + error = psaux->afm_parser_funcs->parse( &parser ); + + psaux->afm_parser_funcs->done( &parser ); + + return error; + } + + + int main( int argc, + char** argv ) + { + FT_Library library; + FT_StreamRec stream; + FT_Error error = FT_Err_Ok; + AFM_FontInfoRec fi; + + + if ( argc < 2 ) + return FT_Err_Invalid_Argument; + + error = FT_Init_FreeType( &library ); + if ( error ) + return error; + + FT_ZERO( &stream ); + error = FT_Stream_Open( &stream, argv[1] ); + if ( error ) + goto Exit; + stream.memory = library->memory; + + FT_ZERO( &fi ); + error = parse_afm( library, &stream, &fi ); + + if ( !error ) + { + FT_Memory memory = library->memory; + + + dump_fontinfo( &fi ); + + if ( fi.KernPairs ) + FT_FREE( fi.KernPairs ); + if ( fi.TrackKerns ) + FT_FREE( fi.TrackKerns ); + } + else + printf( "parse error\n" ); + + FT_Stream_Close( &stream ); + + Exit: + FT_Done_FreeType( library ); + + return error; + } diff --git a/src/type1/t1afm.c b/src/type1/t1afm.c index 83a8aa327..d128e8a12 100644 --- a/src/type1/t1afm.c +++ b/src/type1/t1afm.c @@ -34,102 +34,41 @@ FT_LOCAL_DEF( void ) - T1_Done_Metrics( FT_Memory memory, - T1_AFM* afm ) + T1_Done_Metrics( FT_Memory memory, + AFM_FontInfo fi ) { - FT_FREE( afm->kern_pairs ); - afm->num_pairs = 0; - FT_FREE( afm ); + FT_FREE( fi->KernPairs ); + fi->NumKernPair = 0; + + FT_FREE( fi->TrackKerns ); + fi->NumTrackKern = 0; + + FT_FREE( fi ); } -#undef IS_KERN_PAIR -#define IS_KERN_PAIR( p ) ( p[0] == 'K' && p[1] == 'P' ) - -#define IS_ALPHANUM( c ) ( ft_isalnum( c ) || \ - c == '_' || \ - c == '.' ) - - /* read a glyph name and return the equivalent glyph index */ - static FT_UInt - afm_atoindex( FT_Byte** start, - FT_Byte* limit, - T1_Font type1 ) + static FT_Int + t1_get_index( const char* name, + FT_UInt len, + void* user_data ) { - FT_Byte* p = *start; - FT_PtrDist len; - FT_UInt result = 0; - char temp[64]; + T1_Font type1 = (T1_Font)user_data; + FT_Int n; - /* skip whitespace */ - while ( p < limit && - ( *p == ' ' || *p == '\t' || *p == ':' || *p == ';' ) ) - p++; - *start = p; - - /* now, read glyph name */ - while ( p < limit && IS_ALPHANUM( *p ) ) - p++; - - len = p - *start; - - if ( len > 0 && len < 64 ) + for ( n = 0; n < type1->num_glyphs; n++ ) { - FT_Int n; + char* gname = (char*)type1->glyph_names[n]; - /* copy glyph name to intermediate array */ - FT_MEM_COPY( temp, *start, len ); - temp[len] = 0; - - /* lookup glyph name in face array */ - for ( n = 0; n < type1->num_glyphs; n++ ) - { - char* gname = (char*)type1->glyph_names[n]; - - - if ( gname && gname[0] == temp[0] && ft_strcmp( gname, temp ) == 0 ) - { - result = n; - break; - } - } - } - *start = p; - return result; - } - - - /* read an integer */ - static int - afm_atoi( FT_Byte** start, - FT_Byte* limit ) - { - FT_Byte* p = *start; - int sum = 0; - int sign = 1; - - - /* skip everything that is not a number */ - while ( p < limit && !isdigit( *p ) ) - { - sign = 1; - if ( *p == '-' ) - sign = -1; - - p++; + if ( gname && gname[0] == name[0] && + ft_strlen( gname ) == len && + ft_strncmp( gname, name, len ) == 0 ) + return n; } - while ( p < limit && isdigit( *p ) ) - { - sum = sum * 10 + ( *p - '0' ); - p++; - } - *start = p; - - return sum * sign; + return 0; } @@ -142,121 +81,29 @@ compare_kern_pairs( const void* a, const void* b ) { - T1_Kern_Pair* pair1 = (T1_Kern_Pair*)a; - T1_Kern_Pair* pair2 = (T1_Kern_Pair*)b; + AFM_KernPair pair1 = (AFM_KernPair)a; + AFM_KernPair pair2 = (AFM_KernPair)b; - FT_ULong index1 = KERN_INDEX( pair1->glyph1, pair1->glyph2 ); - FT_ULong index2 = KERN_INDEX( pair2->glyph1, pair2->glyph2 ); + FT_ULong index1 = KERN_INDEX( pair1->index1, pair1->index2 ); + FT_ULong index2 = KERN_INDEX( pair2->index1, pair2->index2 ); return (int)( index1 - index2 ); } - /* parse an AFM file -- for now, only read the kerning pairs */ - static FT_Error - T1_Read_AFM( FT_Face t1_face, - FT_Stream stream ) - { - FT_Error error = T1_Err_Ok; - FT_Memory memory = stream->memory; - FT_Byte* start; - FT_Byte* limit; - FT_Byte* p; - FT_Int count = 0; - T1_Kern_Pair* pair; - T1_Font type1 = &((T1_Face)t1_face)->type1; - T1_AFM* afm = 0; - - - start = (FT_Byte*)stream->cursor; - limit = (FT_Byte*)stream->limit; - p = start; - - /* we are now going to count the occurences of `KP' or `KPX' in */ - /* the AFM file */ - count = 0; - for ( p = start; p < limit - 3; p++ ) - { - if ( IS_KERN_PAIR( p ) ) - count++; - } - - /* Actually, kerning pairs are simply optional! */ - if ( count == 0 ) - goto Exit; - - /* allocate the pairs */ - if ( FT_NEW( afm ) || FT_NEW_ARRAY( afm->kern_pairs, count ) ) - goto Exit; - - /* now, read each kern pair */ - pair = afm->kern_pairs; - afm->num_pairs = count; - - /* save in face object */ - ((T1_Face)t1_face)->afm_data = afm; - - t1_face->face_flags |= FT_FACE_FLAG_KERNING; - - for ( p = start; p < limit - 3; p++ ) - { - if ( IS_KERN_PAIR( p ) ) - { - FT_Byte* q; - - - /* skip keyword (KP or KPX) */ - q = p + 2; - if ( *q == 'X' ) - q++; - - pair->glyph1 = afm_atoindex( &q, limit, type1 ); - pair->glyph2 = afm_atoindex( &q, limit, type1 ); - pair->kerning.x = afm_atoi( &q, limit ); - - pair->kerning.y = 0; - if ( p[2] != 'X' ) - pair->kerning.y = afm_atoi( &q, limit ); - - pair++; - } - } - - /* now, sort the kern pairs according to their glyph indices */ - ft_qsort( afm->kern_pairs, count, sizeof ( T1_Kern_Pair ), - compare_kern_pairs ); - - Exit: - if ( error ) - FT_FREE( afm ); - - return error; - } - - -#define LITTLE_ENDIAN_USHORT( p ) (FT_UShort)( ( (p)[0] ) | \ - ( (p)[1] << 8 ) ) - -#define LITTLE_ENDIAN_UINT( p ) (FT_UInt)( ( (p)[0] ) | \ - ( (p)[1] << 8 ) | \ - ( (p)[2] << 16 ) | \ - ( (p)[3] << 24 ) ) - - /* parse a PFM file -- for now, only read the kerning pairs */ static FT_Error - T1_Read_PFM( FT_Face t1_face, - FT_Stream stream ) + T1_Read_PFM( FT_Face t1_face, + FT_Stream stream, + AFM_FontInfo fi ) { FT_Error error = T1_Err_Ok; FT_Memory memory = stream->memory; FT_Byte* start; FT_Byte* limit; FT_Byte* p; - FT_Int kern_count = 0; - T1_Kern_Pair* pair; - T1_AFM* afm = 0; + AFM_KernPair kp; FT_Int width_table_length; FT_CharMap oldcharmap; FT_CharMap charmap; @@ -275,16 +122,16 @@ error = T1_Err_Unknown_File_Format; goto Exit; } - width_table_length = LITTLE_ENDIAN_USHORT( p ); + width_table_length = FT_PEEK_USHORT_LE( p ); p += 18 + width_table_length; - if ( p + 0x12 > limit || LITTLE_ENDIAN_USHORT( p ) < 0x12 ) + if ( p + 0x12 > limit || FT_PEEK_USHORT_LE( p ) < 0x12 ) /* extension table is probably optional */ goto Exit; /* Kerning offset is 14 bytes from start of extensions table. */ p += 14; - p = start + LITTLE_ENDIAN_UINT( p ); + p = start + FT_PEEK_ULONG_LE( p ); if ( p == start ) /* zero offset means no table */ @@ -296,31 +143,25 @@ goto Exit; } - kern_count = LITTLE_ENDIAN_USHORT( p ); + fi->NumKernPair = FT_PEEK_USHORT_LE( p ); p += 2; - if ( p + 4 * kern_count > limit ) + if ( p + 4 * fi->NumKernPair > limit ) { error = T1_Err_Unknown_File_Format; goto Exit; } /* Actually, kerning pairs are simply optional! */ - if ( kern_count == 0 ) + if ( fi->NumKernPair == 0 ) goto Exit; /* allocate the pairs */ - if ( FT_NEW( afm ) || FT_NEW_ARRAY( afm->kern_pairs, kern_count ) ) + if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) ) goto Exit; - /* save in face object */ - ((T1_Face)t1_face)->afm_data = afm; - - t1_face->face_flags |= FT_FACE_FLAG_KERNING; - /* now, read each kern pair */ - pair = afm->kern_pairs; - afm->num_pairs = kern_count; - limit = p + 4 * kern_count; + kp = fi->KernPairs; + limit = p + 4 * fi->NumKernPair; /* PFM kerning data are stored by encoding rather than glyph index, */ /* so find the PostScript charmap of this font and install it */ @@ -347,15 +188,15 @@ /* encoding of first glyph (1 byte) */ /* encoding of second glyph (1 byte) */ /* offset (little-endian short) */ - for ( ; p < limit ; p+=4 ) + for ( ; p < limit ; p += 4 ) { - pair->glyph1 = FT_Get_Char_Index( t1_face, p[0] ); - pair->glyph2 = FT_Get_Char_Index( t1_face, p[1] ); + kp->index1 = FT_Get_Char_Index( t1_face, p[0] ); + kp->index2 = FT_Get_Char_Index( t1_face, p[1] ); - pair->kerning.x = (FT_Short)LITTLE_ENDIAN_USHORT(p + 2); - pair->kerning.y = 0; + kp->x = (FT_Int)FT_PEEK_USHORT_LE(p + 2); + kp->y = 0; - pair++; + kp++; } if ( oldcharmap != NULL ) @@ -364,12 +205,15 @@ goto Exit; /* now, sort the kern pairs according to their glyph indices */ - ft_qsort( afm->kern_pairs, kern_count, sizeof ( T1_Kern_Pair ), + ft_qsort( fi->KernPairs, fi->NumKernPair, sizeof ( AFM_KernPairRec ), compare_kern_pairs ); Exit: if ( error ) - FT_FREE( afm ); + { + FT_FREE( fi->KernPairs ); + fi->NumKernPair = 0; + } return error; } @@ -381,27 +225,55 @@ T1_Read_Metrics( FT_Face t1_face, FT_Stream stream ) { - FT_Error error; - FT_Byte* start; + PSAux_Service psaux; + FT_Memory memory = stream->memory; + AFM_ParserRec parser; + AFM_FontInfo fi; + FT_Error error = T1_Err_Unknown_File_Format; if ( FT_FRAME_ENTER( stream->size ) ) return error; - start = (FT_Byte*)stream->cursor; + FT_NEW( fi ); + if ( error ) + return error; - if ( stream->size >= ft_strlen( "StartFontMetrics" ) && - ft_strncmp( (const char*)start, "StartFontMetrics", - ft_strlen( "StartFontMetrics" ) ) == 0 ) - error = T1_Read_AFM( t1_face, stream ); + psaux = (PSAux_Service)( (T1_Face)t1_face )->psaux; + if ( psaux && psaux->afm_parser_funcs ) + { + error = psaux->afm_parser_funcs->init( &parser, + stream->memory, + stream->cursor, + stream->limit ); - else if ( stream->size > 6 && - start[0] == 0x00 && start[1] == 0x01 && - LITTLE_ENDIAN_UINT( start + 2 ) == stream->size ) - error = T1_Read_PFM( t1_face, stream ); + if ( !error ) + { + parser.FontInfo = fi; + parser.get_index = t1_get_index; + parser.user_data = &( (T1_Face)t1_face )->type1; - else - error = T1_Err_Unknown_File_Format; + error = psaux->afm_parser_funcs->parse( &parser ); + psaux->afm_parser_funcs->done( &parser ); + } + } + + if ( error == T1_Err_Unknown_File_Format ) + { + FT_Byte* start = stream->cursor; + + + if ( stream->size > 6 && + start[0] == 0x00 && start[1] == 0x01 && + FT_PEEK_ULONG_LE( start + 2 ) == stream->size ) + error = T1_Read_PFM( t1_face, stream, fi ); + } + + if ( !error && fi->NumKernPair ) + { + t1_face->face_flags |= FT_FACE_FLAG_KERNING; + ( (T1_Face)t1_face )->afm_data = fi; + } FT_FRAME_EXIT(); @@ -411,18 +283,18 @@ /* find the kerning for a given glyph pair */ FT_LOCAL_DEF( void ) - T1_Get_Kerning( T1_AFM* afm, - FT_UInt glyph1, - FT_UInt glyph2, - FT_Vector* kerning ) + T1_Get_Kerning( AFM_FontInfo fi, + FT_UInt glyph1, + FT_UInt glyph2, + FT_Vector* kerning ) { - T1_Kern_Pair *min, *mid, *max; + AFM_KernPair min, mid, max; FT_ULong idx = KERN_INDEX( glyph1, glyph2 ); /* simple binary search */ - min = afm->kern_pairs; - max = min + afm->num_pairs - 1; + min = fi->KernPairs; + max = min + fi->NumKernPair - 1; while ( min <= max ) { @@ -430,11 +302,13 @@ mid = min + ( max - min ) / 2; - midi = KERN_INDEX( mid->glyph1, mid->glyph2 ); + midi = KERN_INDEX( mid->index1, mid->index2 ); if ( midi == idx ) { - *kerning = mid->kerning; + kerning->x = mid->x; + kerning->y = mid->y; + return; } @@ -449,4 +323,42 @@ } + FT_LOCAL_DEF( FT_Error ) + T1_Get_Track_Kerning( FT_Face face, + FT_Fixed ptsize, + FT_Int degree, + FT_Fixed* kerning ) + { + AFM_FontInfo fi = (AFM_FontInfo)( (T1_Face)face )->afm_data; + FT_Int i; + + + if ( !fi ) + return T1_Err_Invalid_Argument; + + for ( i = 0; i < fi->NumTrackKern; i++ ) + { + AFM_TrackKern tk = fi->TrackKerns + i; + + + if ( tk->degree != degree ) + continue; + + if ( ptsize < tk->min_ptsize ) + *kerning = tk->min_kern; + else if ( ptsize > tk->max_ptsize ) + *kerning = tk->max_kern; + else + { + *kerning = FT_MulDiv( ptsize - tk->min_ptsize, + tk->max_kern - tk->min_kern, + tk->max_ptsize - tk->min_ptsize ) + + tk->min_kern; + } + } + + return T1_Err_Ok; + } + + /* END */ diff --git a/src/type1/t1afm.h b/src/type1/t1afm.h index 5aaeb67a7..2bc8c3a51 100644 --- a/src/type1/t1afm.h +++ b/src/type1/t1afm.h @@ -26,37 +26,25 @@ FT_BEGIN_HEADER - typedef struct T1_Kern_Pair_ - { - FT_UInt glyph1; - FT_UInt glyph2; - FT_Vector kerning; - - } T1_Kern_Pair; - - - typedef struct T1_AFM_ - { - FT_Int num_pairs; - T1_Kern_Pair* kern_pairs; - - } T1_AFM; - - FT_LOCAL( FT_Error ) T1_Read_Metrics( FT_Face face, FT_Stream stream ); FT_LOCAL( void ) - T1_Done_Metrics( FT_Memory memory, - T1_AFM* afm ); + T1_Done_Metrics( FT_Memory memory, + AFM_FontInfo fi ); FT_LOCAL( void ) - T1_Get_Kerning( T1_AFM* afm, - FT_UInt glyph1, - FT_UInt glyph2, - FT_Vector* kerning ); + T1_Get_Kerning( AFM_FontInfo fi, + FT_UInt glyph1, + FT_UInt glyph2, + FT_Vector* kerning ); + FT_LOCAL( FT_Error ) + T1_Get_Track_Kerning( FT_Face face, + FT_Fixed ptsize, + FT_Int degree, + FT_Fixed* kerning ); FT_END_HEADER diff --git a/src/type1/t1driver.c b/src/type1/t1driver.c index 1ca8a0cdb..bbe70e992 100644 --- a/src/type1/t1driver.c +++ b/src/type1/t1driver.c @@ -36,6 +36,7 @@ #include FT_SERVICE_POSTSCRIPT_NAME_H #include FT_SERVICE_POSTSCRIPT_CMAPS_H #include FT_SERVICE_POSTSCRIPT_INFO_H +#include FT_SERVICE_KERNING_H /*************************************************************************/ /* */ @@ -176,6 +177,10 @@ (PS_GetFontPrivateFunc)t1_ps_get_font_private, }; + static const FT_Service_KerningRec t1_service_kerning = + { + T1_Get_Track_Kerning, + }; /* * SERVICE LIST @@ -188,6 +193,7 @@ { FT_SERVICE_ID_GLYPH_DICT, &t1_service_glyph_dict }, { FT_SERVICE_ID_XF86_NAME, FT_XF86_FORMAT_TYPE_1 }, { FT_SERVICE_ID_POSTSCRIPT_INFO, &t1_service_ps_info }, + { FT_SERVICE_ID_KERNING, &t1_service_kerning }, #ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT { FT_SERVICE_ID_MULTI_MASTERS, &t1_service_multi_masters }, @@ -246,15 +252,14 @@ FT_UInt right_glyph, FT_Vector* kerning ) { - T1_AFM* afm; - - kerning->x = 0; kerning->y = 0; - afm = (T1_AFM*)face->afm_data; - if ( afm ) - T1_Get_Kerning( afm, left_glyph, right_glyph, kerning ); + if ( face->afm_data ) + T1_Get_Kerning( (AFM_FontInfo)face->afm_data, + left_glyph, + right_glyph, + kerning ); return T1_Err_Ok; } diff --git a/src/type1/t1objs.c b/src/type1/t1objs.c index 4cd460d39..642654a3b 100644 --- a/src/type1/t1objs.c +++ b/src/type1/t1objs.c @@ -233,7 +233,7 @@ #ifndef T1_CONFIG_OPTION_NO_AFM /* release afm data if present */ if ( face->afm_data ) - T1_Done_Metrics( memory, (T1_AFM*)face->afm_data ); + T1_Done_Metrics( memory, (AFM_FontInfo)face->afm_data ); #endif /* release unicode map, if any */