ref: a8635cc6269ec8236a6bb1123219d8bf00ce52a1
parent: cda32b71ba25d313264ad52021106e42acfae573
author: David Turner <[email protected]>
date: Thu Feb 10 11:15:11 EST 2000
Very simple AFM Parser
--- /dev/null
+++ b/src/type1/t1afm.c
@@ -1,0 +1,222 @@
+/***************************************************************************
+ *
+ * t1afm.c - support for reading Type 1 AFM files
+ *
+ *
+ ***************************************************************************/
+
+#include <t1afm.h>
+#include <ftstream.h>
+#include <t1types.h>
+#include <stdlib.h> /* 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;
+ }
+
--- /dev/null
+++ b/src/type1/t1afm.h
@@ -1,0 +1,47 @@
+/***************************************************************************
+ *
+ * t1afm.h - support for reading Type 1 AFM files
+ *
+ *
+ ***************************************************************************/
+
+#ifndef T1AFM_H
+#define T1AFM_H
+
+#include <ftobjs.h>
+
+/* 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 */