diff --git a/src/type1/t1afm.c b/src/type1/t1afm.c new file mode 100644 index 000000000..d82390826 --- /dev/null +++ b/src/type1/t1afm.c @@ -0,0 +1,222 @@ +/*************************************************************************** + * + * t1afm.c - support for reading Type 1 AFM files + * + * + ***************************************************************************/ + +#include +#include +#include +#include /* for qsort */ + + LOCAL_FUNC + void T1_Done_AFM( FT_Memory memory, T1_AFM* afm ) + { + FREE( afm->kern_pairs ); + afm->num_pairs = 0; + } + +#undef IS_KERN_PAIR +#define IS_KERN_PAIR(p) ( p[0] == 'K' && p[1] == 'P' ) + +#define IS_ALPHANUM(c) ( (c >= 'A' && c <= 'Z') || \ + (c >= 'a' && c <= 'z') || \ + (c >= '0' && c <= '9') || \ + (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 ) + { + FT_Byte* p = *start; + FT_Int len; + FT_UInt result = 0; + char temp[64]; + + /* skip whitespace */ + while ( (*p == ' ' || *p == '\t' || *p == ':' || *p == ';') && p < limit ) + p++; + *start = p; + + /* now, read glyph name */ + while ( IS_ALPHANUM(*p) && p < limit ) p++; + len = p - *start; + if (len > 0 && len < 64) + { + FT_UInt n; + + /* copy glyph name to intermediate array */ + 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; + + if ( gname && gname[0] == temp[0] && 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; + + /* skip everything that is not a number */ + while ( p < limit && (*p < '0' || *p > '9') ) + p++; + + while ( p < limit && (*p >= '0' || *p < '9') ) + { + sum = sum*10 + (*p - '0'); + p++; + } + *start = p; + return sum; + } + + +#undef KERN_INDEX +#define KERN_INDEX(g1,g2) (((T1_ULong)g1 << 16) | g2) + + /* compare two kerning pairs */ + static + int 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; + + T1_ULong index1 = KERN_INDEX(pair1->glyph1,pair1->glyph2); + T1_ULong index2 = KERN_INDEX(pair2->glyph1,pair2->glyph2); + + return ( index1 < index2 ? -1 : + ( index1 > index2 ? 1 : 0 )); + } + + + /* parse an AFM file - for now, only read the kerning pairs */ + LOCAL_FUNC + FT_Error T1_Read_AFM( FT_Stream stream, + FT_Face t1_face ) + { + FT_Error error; + 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; + + if ( !ACCESS_Frame(stream->size) ) + return error; + + start = stream->cursor; + limit = 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 ( ALLOC( afm, sizeof(*afm ) || + ALLOC_ARRAY( afm->kern_pairs, count, T1_Kern_Pair ) ) + 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; + + 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 */ + qsort( afm->kern_pairs, count, sizeof(T1_Kern_Pair), compare_kern_pairs ); + + Exit: + if (error) + FREE( afm ); + + FORGET_Frame(); + return error; + } + + + /* find the kerning for a given glyph pair */ + LOCAL_FUNC + void T1_Get_Kerning( T1_AFM* afm, + FT_UInt glyph1, + FT_UInt glyph2, + FT_Vector* kerning ) + { + T1_Kern_Pair *min, *mid, *max; + T1_ULong index = KERN_INDEX(glyph1,glyph2); + + /* simple binary search */ + min = afm->kern_pairs; + max = min + afm->num_pairs-1; + + while (min <= max) + { + T1_ULong midi; + + mid = min + (max-min)/2; + midi = KERN_INDEX(mid->glyph1,mid->glyph2); + if ( midi == index ) + { + *kerning = mid->kerning; + return; + } + + if ( midi < index ) min = mid+1; + else max = mid-1; + } + kerning->x = 0; + kerning->y = 0; + } + diff --git a/src/type1/t1afm.h b/src/type1/t1afm.h new file mode 100644 index 000000000..39bbc4bea --- /dev/null +++ b/src/type1/t1afm.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * + * t1afm.h - support for reading Type 1 AFM files + * + * + ***************************************************************************/ + +#ifndef T1AFM_H +#define T1AFM_H + +#include + +/* In this version, we only read the kerning table from the */ +/* AFM file. We may add support for ligatures a bit later.. */ + +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; + + +LOCAL_DEF +FT_Error T1_Read_AFM( FT_Stream stream, + FT_Face face ); + +LOCAL_DEF +void T1_Done_AFM( FT_Memory memory, + T1_AFM* afm ); + +LOCAL_DEF +void T1_Get_Kerning( T1_AFM* afm, + FT_UInt glyph1, + FT_UInt glyph2, + FT_Vector* kerning ); + +#endif /* T1AFM_H */