ref: 8def3c897f3dc31797796220c443fffd625e55dc
dir: /src/type1/t1afm.c/
/***************************************************************************/ /* */ /* t1afm.c */ /* */ /* AFM support for Type 1 fonts (body). */ /* */ /* Copyright 1996-2001, 2002, 2003, 2004, 2005 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 "t1afm.h" #include "t1errors.h" #include FT_INTERNAL_STREAM_H #include FT_INTERNAL_TYPE1_TYPES_H /*************************************************************************/ /* */ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ /* messages during execution. */ /* */ #undef FT_COMPONENT #define FT_COMPONENT trace_t1afm FT_LOCAL_DEF( void ) T1_Done_Metrics( FT_Memory memory, T1_AFM* afm ) { FT_FREE( afm->kern_pairs ); afm->num_pairs = 0; FT_FREE( afm ); } #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 ) { FT_Byte* p = *start; FT_PtrDist len; FT_UInt result = 0; char temp[64]; /* 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 ) { FT_Int 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++; } while ( p < limit && isdigit( *p ) ) { sum = sum * 10 + ( *p - '0' ); p++; } *start = p; return sum * sign; } #undef KERN_INDEX #define KERN_INDEX( g1, g2 ) ( ( (FT_ULong)g1 << 16 ) | g2 ) /* compare two kerning pairs */ FT_CALLBACK_DEF( 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; FT_ULong index1 = KERN_INDEX( pair1->glyph1, pair1->glyph2 ); FT_ULong index2 = KERN_INDEX( pair2->glyph1, pair2->glyph2 ); 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 ) { 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; FT_Int width_table_length; FT_CharMap oldcharmap; FT_CharMap charmap; FT_Int n; start = (FT_Byte*)stream->cursor; limit = (FT_Byte*)stream->limit; p = start; /* Figure out how long the width table is. */ /* This info is a little-endian short at offset 99. */ p = start + 99; if ( p + 2 > limit ) { error = T1_Err_Unknown_File_Format; goto Exit; } width_table_length = LITTLE_ENDIAN_USHORT( p ); p += 18 + width_table_length; if ( p + 0x12 > limit || LITTLE_ENDIAN_USHORT( 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 ); if ( p == start ) /* zero offset means no table */ goto Exit; if ( p + 2 > limit ) { error = T1_Err_Unknown_File_Format; goto Exit; } kern_count = LITTLE_ENDIAN_USHORT( p ); p += 2; if ( p + 4 * kern_count > limit ) { error = T1_Err_Unknown_File_Format; goto Exit; } /* Actually, kerning pairs are simply optional! */ if ( kern_count == 0 ) goto Exit; /* allocate the pairs */ if ( FT_NEW( afm ) || FT_NEW_ARRAY( afm->kern_pairs, kern_count ) ) 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; /* PFM kerning data are stored by encoding rather than glyph index, */ /* so find the PostScript charmap of this font and install it */ /* temporarily. If we find no PostScript charmap, then just use */ /* the default and hope it is the right one. */ oldcharmap = t1_face->charmap; charmap = NULL; for ( n = 0; n < t1_face->num_charmaps; n++ ) { charmap = t1_face->charmaps[n]; /* check against PostScript pseudo platform */ if ( charmap->platform_id == 7 ) { error = FT_Set_Charmap( t1_face, charmap ); if ( error ) goto Exit; break; } } /* Kerning info is stored as: */ /* */ /* encoding of first glyph (1 byte) */ /* encoding of second glyph (1 byte) */ /* offset (little-endian short) */ 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] ); pair->kerning.x = (FT_Short)LITTLE_ENDIAN_USHORT(p + 2); pair->kerning.y = 0; pair++; } if ( oldcharmap != NULL ) error = FT_Set_Charmap( t1_face, oldcharmap ); if ( error ) goto Exit; /* now, sort the kern pairs according to their glyph indices */ ft_qsort( afm->kern_pairs, kern_count, sizeof ( T1_Kern_Pair ), compare_kern_pairs ); Exit: if ( error ) FT_FREE( afm ); return error; } /* parse a metrics file -- either AFM or PFM depending on what */ /* it turns out to be */ FT_LOCAL_DEF( FT_Error ) T1_Read_Metrics( FT_Face t1_face, FT_Stream stream ) { FT_Error error; FT_Byte* start; if ( FT_FRAME_ENTER( stream->size ) ) return error; start = (FT_Byte*)stream->cursor; if ( stream->size >= ft_strlen( "StartFontMetrics" ) && ft_strncmp( (const char*)start, "StartFontMetrics", ft_strlen( "StartFontMetrics" ) ) == 0 ) error = T1_Read_AFM( t1_face, stream ); 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 ); else error = T1_Err_Unknown_File_Format; FT_FRAME_EXIT(); return error; } /* 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_Kern_Pair *min, *mid, *max; FT_ULong idx = KERN_INDEX( glyph1, glyph2 ); /* simple binary search */ min = afm->kern_pairs; max = min + afm->num_pairs - 1; while ( min <= max ) { FT_ULong midi; mid = min + ( max - min ) / 2; midi = KERN_INDEX( mid->glyph1, mid->glyph2 ); if ( midi == idx ) { *kerning = mid->kerning; return; } if ( midi < idx ) min = mid + 1; else max = mid - 1; } kerning->x = 0; kerning->y = 0; } /* END */