int ft_max_majmin(char *folder, const char **args, int mode, int i) { char **array; struct stat info; long int size; int len; int mlen; mlen = 0; array = list_dir(args, array_size((char **)args), folder); array = ft_qsort(array, array_size(array)); if (ft_strchr(ft_get_flags(args), 't') != NULL) array = ft_sort_by_time(array, array_size(array), folder); while (lstat(array[i], &info) != -1) { if ((len = 0) == 0 && mode == 1) size = (long)major(info.st_rdev); else size = (long)minor(info.st_rdev); while ((size = (size / 10)) != 0) len++; if (is_dot(array[i], folder, args) && len > mlen) mlen = len; i++; } return (mlen + 1); }
/* parse a PFM file -- for now, only read the kerning pairs */ static FT_Error T1_Read_PFM( FT_Face t1_face, FT_Stream stream, AFM_FontInfo fi ) { FT_Error error = FT_Err_Ok; FT_Memory memory = stream->memory; FT_Byte* start; FT_Byte* limit; FT_Byte* p; AFM_KernPair kp; 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 = FT_THROW( Unknown_File_Format ); goto Exit; } width_table_length = FT_PEEK_USHORT_LE( p ); p += 18 + width_table_length; 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 + FT_PEEK_ULONG_LE( p ); if ( p == start ) /* zero offset means no table */ goto Exit; if ( p + 2 > limit ) { error = FT_THROW( Unknown_File_Format ); goto Exit; } fi->NumKernPair = FT_PEEK_USHORT_LE( p ); p += 2; if ( p + 4 * fi->NumKernPair > limit ) { error = FT_THROW( Unknown_File_Format ); goto Exit; } /* Actually, kerning pairs are simply optional! */ if ( fi->NumKernPair == 0 ) goto Exit; /* allocate the pairs */ if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) ) goto Exit; /* now, read each kern pair */ 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 */ /* 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 ) { kp->index1 = FT_Get_Char_Index( t1_face, p[0] ); kp->index2 = FT_Get_Char_Index( t1_face, p[1] ); kp->x = (FT_Int)FT_PEEK_SHORT_LE(p + 2); kp->y = 0; kp++; } 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( fi->KernPairs, fi->NumKernPair, sizeof ( AFM_KernPairRec ), compare_kern_pairs ); Exit: if ( error ) { FT_FREE( fi->KernPairs ); fi->NumKernPair = 0; } return error; }
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; }
extern void FT_DumpMemory( FT_Memory memory ) { FT_MemTable table = (FT_MemTable)memory->user; if ( table ) { FT_MemSource* bucket = table->sources; FT_MemSource* limit = bucket + FT_MEM_SOURCE_BUCKETS; FT_MemSource* sources; FT_UInt nn, count; const char* fmt; count = 0; for ( ; bucket < limit; bucket++ ) { FT_MemSource source = *bucket; for ( ; source; source = source->link ) count++; } sources = (FT_MemSource*)ft_mem_table_alloc( table, sizeof ( *sources ) * count ); count = 0; for ( bucket = table->sources; bucket < limit; bucket++ ) { FT_MemSource source = *bucket; for ( ; source; source = source->link ) sources[count++] = source; } ft_qsort( sources, count, sizeof ( *sources ), ft_mem_source_compare ); FT_PRINTF( "FreeType Memory Dump: " "current=%ld max=%ld total=%ld count=%ld\n", table->alloc_current, table->alloc_max, table->alloc_total, table->alloc_count ); FT_PRINTF( " block block sizes sizes sizes source\n" ); FT_PRINTF( " count high sum highsum max location\n" ); FT_PRINTF( "-------------------------------------------------\n" ); fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n"; for ( nn = 0; nn < count; nn++ ) { FT_MemSource source = sources[nn]; FT_PRINTF( fmt, source->cur_blocks, source->max_blocks, source->cur_size, source->max_size, source->cur_max, FT_FILENAME( source->file_name ), source->line_no ); } FT_PRINTF( "------------------------------------------------\n" ); ft_mem_table_free( table, sources ); } }
/* Builds a table that maps Unicode values to glyph indices */ static FT_Error ps_unicodes_init( FT_Memory memory, FT_UInt num_glyphs, const char** glyph_names, PS_Unicodes* table ) { FT_Error error; /* we first allocate the table */ table->num_maps = 0; table->maps = 0; if ( !FT_NEW_ARRAY( table->maps, num_glyphs ) ) { FT_UInt n; FT_UInt count; PS_UniMap* map; FT_UInt32 uni_char; map = table->maps; for ( n = 0; n < num_glyphs; n++ ) { const char* gname = glyph_names[n]; if ( gname ) { uni_char = ps_unicode_value( gname ); if ( uni_char != 0 && uni_char != 0xFFFFL ) { map->unicode = (FT_UInt)uni_char; map->glyph_index = n; map++; } } } /* now, compress the table a bit */ count = (FT_UInt)( map - table->maps ); if ( count > 0 && FT_REALLOC( table->maps, num_glyphs * sizeof ( PS_UniMap ), count * sizeof ( PS_UniMap ) ) ) count = 0; if ( count == 0 ) { FT_FREE( table->maps ); if ( !error ) error = PSnames_Err_Invalid_Argument; /* no unicode chars here! */ } else /* sort the table in increasing order of unicode values */ ft_qsort( table->maps, count, sizeof ( PS_UniMap ), compare_uni_maps ); table->num_maps = count; } return error; }
static FT_Error pfr_sort_kerning_pairs( FT_Stream stream, PFR_PhyFont phy_font ) { FT_Error error; FT_Memory memory = stream->memory; PFR_KernPair pairs; PFR_KernItem item; PFR_Char chars = phy_font->chars; FT_UInt num_chars = phy_font->num_chars; FT_UInt count; /* create kerning pairs array */ if ( FT_NEW_ARRAY( phy_font->kern_pairs, phy_font->num_kern_pairs ) ) goto Exit; /* load all kerning items into the array, * converting character codes into glyph indices */ pairs = phy_font->kern_pairs; item = phy_font->kern_items; count = 0; for ( ; item; item = item->next ) { FT_UInt limit = count + item->pair_count; FT_Byte* p; if ( limit > phy_font->num_kern_pairs ) { error = PFR_Err_Invalid_Table; goto Exit; } if ( FT_STREAM_SEEK( item->offset ) || FT_FRAME_ENTER( item->pair_count * item->pair_size ) ) goto Exit; p = stream->cursor; for ( ; count < limit; count++ ) { PFR_KernPair pair = pairs + count; FT_UInt char1, char2; FT_Int kerning; if ( item->flags & PFR_KERN_2BYTE_CHAR ) { char1 = FT_NEXT_USHORT( p ); char2 = FT_NEXT_USHORT( p ); } else { char1 = FT_NEXT_BYTE( p ); char2 = FT_NEXT_BYTE( p ); } if ( item->flags & PFR_KERN_2BYTE_ADJ ) kerning = item->base_adj + FT_NEXT_SHORT( p ); else kerning = item->base_adj + FT_NEXT_CHAR( p ); pair->glyph1 = pfr_get_gindex( chars, num_chars, char1 ); pair->glyph2 = pfr_get_gindex( chars, num_chars, char2 ); pair->kerning = kerning; } FT_FRAME_EXIT(); } /* sort the resulting array */ ft_qsort( pairs, count, sizeof ( PFR_KernPairRec ), pfr_compare_kern_pairs ); Exit: if ( error ) { /* disable kerning data in case of error */ phy_font->num_kern_pairs = 0; } return error; }
static FT_Error woff_open_font( FT_Stream stream, TT_Face face ) { FT_Memory memory = stream->memory; FT_Error error = FT_Err_Ok; WOFF_HeaderRec woff; WOFF_Table tables = NULL; WOFF_Table* indices = NULL; FT_ULong woff_offset; FT_Byte* sfnt = NULL; FT_Stream sfnt_stream = NULL; FT_Byte* sfnt_header; FT_ULong sfnt_offset; FT_Int nn; FT_ULong old_tag = 0; static const FT_Frame_Field woff_header_fields[] = { #undef FT_STRUCTURE #define FT_STRUCTURE WOFF_HeaderRec FT_FRAME_START( 44 ), FT_FRAME_ULONG ( signature ), FT_FRAME_ULONG ( flavor ), FT_FRAME_ULONG ( length ), FT_FRAME_USHORT( num_tables ), FT_FRAME_USHORT( reserved ), FT_FRAME_ULONG ( totalSfntSize ), FT_FRAME_USHORT( majorVersion ), FT_FRAME_USHORT( minorVersion ), FT_FRAME_ULONG ( metaOffset ), FT_FRAME_ULONG ( metaLength ), FT_FRAME_ULONG ( metaOrigLength ), FT_FRAME_ULONG ( privOffset ), FT_FRAME_ULONG ( privLength ), FT_FRAME_END }; FT_ASSERT( stream == face->root.stream ); FT_ASSERT( FT_STREAM_POS() == 0 ); if ( FT_STREAM_READ_FIELDS( woff_header_fields, &woff ) ) return error; /* Make sure we don't recurse back here or hit TTC code. */ if ( woff.flavor == TTAG_wOFF || woff.flavor == TTAG_ttcf ) return FT_THROW( Invalid_Table ); /* Miscellaneous checks. */ if ( woff.length != stream->size || woff.num_tables == 0 || 44 + woff.num_tables * 20UL >= woff.length || 12 + woff.num_tables * 16UL >= woff.totalSfntSize || ( woff.totalSfntSize & 3 ) != 0 || ( woff.metaOffset == 0 && ( woff.metaLength != 0 || woff.metaOrigLength != 0 ) ) || ( woff.metaLength != 0 && woff.metaOrigLength == 0 ) || ( woff.privOffset == 0 && woff.privLength != 0 ) ) return FT_THROW( Invalid_Table ); if ( FT_ALLOC( sfnt, woff.totalSfntSize ) || FT_NEW( sfnt_stream ) ) goto Exit; sfnt_header = sfnt; /* Write sfnt header. */ { FT_UInt searchRange, entrySelector, rangeShift, x; x = woff.num_tables; entrySelector = 0; while ( x ) { x >>= 1; entrySelector += 1; } entrySelector--; searchRange = ( 1 << entrySelector ) * 16; rangeShift = woff.num_tables * 16 - searchRange; WRITE_ULONG ( sfnt_header, woff.flavor ); WRITE_USHORT( sfnt_header, woff.num_tables ); WRITE_USHORT( sfnt_header, searchRange ); WRITE_USHORT( sfnt_header, entrySelector ); WRITE_USHORT( sfnt_header, rangeShift ); } /* While the entries in the sfnt header must be sorted by the */ /* tag value, the tables themselves are not. We thus have to */ /* sort them by offset and check that they don't overlap. */ if ( FT_NEW_ARRAY( tables, woff.num_tables ) || FT_NEW_ARRAY( indices, woff.num_tables ) ) goto Exit; FT_TRACE2(( "\n" " tag offset compLen origLen checksum\n" " -------------------------------------------\n" )); if ( FT_FRAME_ENTER( 20L * woff.num_tables ) ) goto Exit; for ( nn = 0; nn < woff.num_tables; nn++ ) { WOFF_Table table = tables + nn; table->Tag = FT_GET_TAG4(); table->Offset = FT_GET_ULONG(); table->CompLength = FT_GET_ULONG(); table->OrigLength = FT_GET_ULONG(); table->CheckSum = FT_GET_ULONG(); FT_TRACE2(( " %c%c%c%c %08lx %08lx %08lx %08lx\n", (FT_Char)( table->Tag >> 24 ), (FT_Char)( table->Tag >> 16 ), (FT_Char)( table->Tag >> 8 ), (FT_Char)( table->Tag ), table->Offset, table->CompLength, table->OrigLength, table->CheckSum )); if ( table->Tag <= old_tag ) { FT_FRAME_EXIT(); error = FT_THROW( Invalid_Table ); goto Exit; } old_tag = table->Tag; indices[nn] = table; } FT_FRAME_EXIT(); /* Sort by offset. */ ft_qsort( indices, woff.num_tables, sizeof ( WOFF_Table ), compare_offsets ); /* Check offsets and lengths. */ woff_offset = 44 + woff.num_tables * 20L; sfnt_offset = 12 + woff.num_tables * 16L; for ( nn = 0; nn < woff.num_tables; nn++ ) { WOFF_Table table = indices[nn]; if ( table->Offset != woff_offset || table->CompLength > woff.length || table->Offset > woff.length - table->CompLength || table->OrigLength > woff.totalSfntSize || sfnt_offset > woff.totalSfntSize - table->OrigLength || table->CompLength > table->OrigLength ) { error = FT_THROW( Invalid_Table ); goto Exit; } table->OrigOffset = sfnt_offset; /* The offsets must be multiples of 4. */ woff_offset += ( table->CompLength + 3 ) & ~3U; sfnt_offset += ( table->OrigLength + 3 ) & ~3U; } /* * Final checks! * * We don't decode and check the metadata block. * We don't check table checksums either. * But other than those, I think we implement all * `MUST' checks from the spec. */ if ( woff.metaOffset ) { if ( woff.metaOffset != woff_offset || woff.metaOffset + woff.metaLength > woff.length ) { error = FT_THROW( Invalid_Table ); goto Exit; } /* We have padding only ... */ woff_offset += woff.metaLength; } if ( woff.privOffset ) { /* ... if it isn't the last block. */ woff_offset = ( woff_offset + 3 ) & ~3U; if ( woff.privOffset != woff_offset || woff.privOffset + woff.privLength > woff.length ) { error = FT_THROW( Invalid_Table ); goto Exit; } /* No padding for the last block. */ woff_offset += woff.privLength; } if ( sfnt_offset != woff.totalSfntSize || woff_offset != woff.length ) { error = FT_THROW( Invalid_Table ); goto Exit; } /* Write the tables. */ for ( nn = 0; nn < woff.num_tables; nn++ ) { WOFF_Table table = tables + nn; /* Write SFNT table entry. */ WRITE_ULONG( sfnt_header, table->Tag ); WRITE_ULONG( sfnt_header, table->CheckSum ); WRITE_ULONG( sfnt_header, table->OrigOffset ); WRITE_ULONG( sfnt_header, table->OrigLength ); /* Write table data. */ if ( FT_STREAM_SEEK( table->Offset ) || FT_FRAME_ENTER( table->CompLength ) ) goto Exit; if ( table->CompLength == table->OrigLength ) { /* Uncompressed data; just copy. */ ft_memcpy( sfnt + table->OrigOffset, stream->cursor, table->OrigLength ); } else { #ifdef FT_CONFIG_OPTION_USE_ZLIB /* Uncompress with zlib. */ FT_ULong output_len = table->OrigLength; error = FT_Gzip_Uncompress( memory, sfnt + table->OrigOffset, &output_len, stream->cursor, table->CompLength ); if ( error ) goto Exit; if ( output_len != table->OrigLength ) { error = FT_THROW( Invalid_Table ); goto Exit; } #else /* !FT_CONFIG_OPTION_USE_ZLIB */ error = FT_THROW( Unimplemented_Feature ); goto Exit; #endif /* !FT_CONFIG_OPTION_USE_ZLIB */ } FT_FRAME_EXIT(); /* We don't check whether the padding bytes in the WOFF file are */ /* actually '\0'. For the output, however, we do set them properly. */ sfnt_offset = table->OrigOffset + table->OrigLength; while ( sfnt_offset & 3 ) { sfnt[sfnt_offset] = '\0'; sfnt_offset++; } } /* Ok! Finally ready. Swap out stream and return. */ FT_Stream_OpenMemory( sfnt_stream, sfnt, woff.totalSfntSize ); sfnt_stream->memory = stream->memory; sfnt_stream->close = sfnt_stream_close; FT_Stream_Free( face->root.stream, ( face->root.face_flags & FT_FACE_FLAG_EXTERNAL_STREAM ) != 0 ); face->root.stream = sfnt_stream; face->root.face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM; Exit: FT_FREE( tables ); FT_FREE( indices ); if ( error ) { FT_FREE( sfnt ); FT_Stream_Close( sfnt_stream ); FT_FREE( sfnt_stream ); } return error; }
/* 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 + 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 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; }