/*************************************************************************** * * 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_Int 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_Face t1_face, FT_Stream stream ) { 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 = (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 ( 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; }