source: trunk/poppler/freetype-2.1.10/src/type1/t1afm.c @ 2

Last change on this file since 2 was 2, checked in by Eugene Romanenko, 16 years ago

First import

File size: 11.8 KB
Line 
1/***************************************************************************/
2/*                                                                         */
3/*  t1afm.c                                                                */
4/*                                                                         */
5/*    AFM support for Type 1 fonts (body).                                 */
6/*                                                                         */
7/*  Copyright 1996-2001, 2002, 2003, 2004 by                               */
8/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
9/*                                                                         */
10/*  This file is part of the FreeType project, and may only be used,       */
11/*  modified, and distributed under the terms of the FreeType project      */
12/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
13/*  this file you indicate that you have read the license and              */
14/*  understand and accept it fully.                                        */
15/*                                                                         */
16/***************************************************************************/
17
18
19#include <ft2build.h>
20#include "t1afm.h"
21#include "t1errors.h"
22#include FT_INTERNAL_STREAM_H
23#include FT_INTERNAL_TYPE1_TYPES_H
24
25
26  /*************************************************************************/
27  /*                                                                       */
28  /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
29  /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
30  /* messages during execution.                                            */
31  /*                                                                       */
32#undef  FT_COMPONENT
33#define FT_COMPONENT  trace_t1afm
34
35
36  FT_LOCAL_DEF( void )
37  T1_Done_Metrics( FT_Memory  memory,
38                   T1_AFM*    afm )
39  {
40    FT_FREE( afm->kern_pairs );
41    afm->num_pairs = 0;
42    FT_FREE( afm );
43  }
44
45
46#undef  IS_KERN_PAIR
47#define IS_KERN_PAIR( p )  ( p[0] == 'K' && p[1] == 'P' )
48
49#define IS_ALPHANUM( c )  ( ft_isalnum( c ) || \
50                            c == '_'        || \
51                            c == '.'        )
52
53
54  /* read a glyph name and return the equivalent glyph index */
55  static FT_UInt
56  afm_atoindex( FT_Byte**  start,
57                FT_Byte*   limit,
58                T1_Font    type1 )
59  {
60    FT_Byte*    p = *start;
61    FT_PtrDist  len;
62    FT_UInt     result = 0;
63    char        temp[64];
64
65
66    /* skip whitespace */
67    while ( p < limit                                             &&
68            ( *p == ' ' || *p == '\t' || *p == ':' || *p == ';' ) )
69      p++;
70    *start = p;
71
72    /* now, read glyph name */
73    while ( p < limit && IS_ALPHANUM( *p ) )
74      p++;
75
76    len = p - *start;
77
78    if ( len > 0 && len < 64 )
79    {
80      FT_Int  n;
81
82
83      /* copy glyph name to intermediate array */
84      FT_MEM_COPY( temp, *start, len );
85      temp[len] = 0;
86
87      /* lookup glyph name in face array */
88      for ( n = 0; n < type1->num_glyphs; n++ )
89      {
90        char*  gname = (char*)type1->glyph_names[n];
91
92
93        if ( gname && gname[0] == temp[0] && ft_strcmp( gname, temp ) == 0 )
94        {
95          result = n;
96          break;
97        }
98      }
99    }
100    *start = p;
101    return result;
102  }
103
104
105  /* read an integer */
106  static int
107  afm_atoi( FT_Byte**  start,
108            FT_Byte*   limit )
109  {
110    FT_Byte*  p    = *start;
111    int       sum  = 0;
112    int       sign = 1;
113
114
115    /* skip everything that is not a number */
116    while ( p < limit && !isdigit( *p ) )
117    {
118      sign = 1;
119      if ( *p == '-' )
120        sign = -1;
121
122      p++;
123    }
124
125    while ( p < limit && isdigit( *p ) )
126    {
127      sum = sum * 10 + ( *p - '0' );
128      p++;
129    }
130    *start = p;
131
132    return sum * sign;
133  }
134
135
136#undef  KERN_INDEX
137#define KERN_INDEX( g1, g2 )  ( ( (FT_ULong)g1 << 16 ) | g2 )
138
139
140  /* compare two kerning pairs */
141  FT_CALLBACK_DEF( int )
142  compare_kern_pairs( const void*  a,
143                      const void*  b )
144  {
145    T1_Kern_Pair*  pair1 = (T1_Kern_Pair*)a;
146    T1_Kern_Pair*  pair2 = (T1_Kern_Pair*)b;
147
148    FT_ULong  index1 = KERN_INDEX( pair1->glyph1, pair1->glyph2 );
149    FT_ULong  index2 = KERN_INDEX( pair2->glyph1, pair2->glyph2 );
150
151
152    return (int)( index1 - index2 );
153  }
154
155
156  /* parse an AFM file -- for now, only read the kerning pairs */
157  static FT_Error
158  T1_Read_AFM( FT_Face    t1_face,
159               FT_Stream  stream )
160  {
161    FT_Error       error = T1_Err_Ok;
162    FT_Memory      memory = stream->memory;
163    FT_Byte*       start;
164    FT_Byte*       limit;
165    FT_Byte*       p;
166    FT_Int         count = 0;
167    T1_Kern_Pair*  pair;
168    T1_Font        type1 = &((T1_Face)t1_face)->type1;
169    T1_AFM*        afm   = 0;
170
171
172    start = (FT_Byte*)stream->cursor;
173    limit = (FT_Byte*)stream->limit;
174    p     = start;
175
176    /* we are now going to count the occurences of `KP' or `KPX' in */
177    /* the AFM file                                                 */
178    count = 0;
179    for ( p = start; p < limit - 3; p++ )
180    {
181      if ( IS_KERN_PAIR( p ) )
182        count++;
183    }
184
185    /* Actually, kerning pairs are simply optional! */
186    if ( count == 0 )
187      goto Exit;
188
189    /* allocate the pairs */
190    if ( FT_NEW( afm ) || FT_NEW_ARRAY( afm->kern_pairs, count ) )
191      goto Exit;
192
193    /* now, read each kern pair */
194    pair           = afm->kern_pairs;
195    afm->num_pairs = count;
196
197    /* save in face object */
198    ((T1_Face)t1_face)->afm_data = afm;
199
200    t1_face->face_flags |= FT_FACE_FLAG_KERNING;
201
202    for ( p = start; p < limit - 3; p++ )
203    {
204      if ( IS_KERN_PAIR( p ) )
205      {
206        FT_Byte*  q;
207
208
209        /* skip keyword (KP or KPX) */
210        q = p + 2;
211        if ( *q == 'X' )
212          q++;
213
214        pair->glyph1    = afm_atoindex( &q, limit, type1 );
215        pair->glyph2    = afm_atoindex( &q, limit, type1 );
216        pair->kerning.x = afm_atoi( &q, limit );
217
218        pair->kerning.y = 0;
219        if ( p[2] != 'X' )
220          pair->kerning.y = afm_atoi( &q, limit );
221
222        pair++;
223      }
224    }
225
226    /* now, sort the kern pairs according to their glyph indices */
227    ft_qsort( afm->kern_pairs, count, sizeof ( T1_Kern_Pair ),
228              compare_kern_pairs );
229
230  Exit:
231    if ( error )
232      FT_FREE( afm );
233
234    return error;
235  }
236
237
238#define LITTLE_ENDIAN_USHORT( p )  (FT_UShort)( ( (p)[0]       ) | \
239                                                ( (p)[1] <<  8 ) )
240
241#define LITTLE_ENDIAN_UINT( p )  (FT_UInt)( ( (p)[0]       ) | \
242                                            ( (p)[1] <<  8 ) | \
243                                            ( (p)[2] << 16 ) | \
244                                            ( (p)[3] << 24 ) )
245
246
247  /* parse a PFM file -- for now, only read the kerning pairs */
248  static FT_Error
249  T1_Read_PFM( FT_Face    t1_face,
250               FT_Stream  stream )
251  {
252    FT_Error       error = T1_Err_Ok;
253    FT_Memory      memory = stream->memory;
254    FT_Byte*       start;
255    FT_Byte*       limit;
256    FT_Byte*       p;
257    FT_Int         kern_count = 0;
258    T1_Kern_Pair*  pair;
259    T1_AFM*        afm = 0;
260    FT_Int         width_table_length;
261    FT_CharMap     oldcharmap;
262    FT_CharMap     charmap;
263    FT_Int         n;
264
265
266    start = (FT_Byte*)stream->cursor;
267    limit = (FT_Byte*)stream->limit;
268    p     = start;
269
270    /* Figure out how long the width table is.          */
271    /* This info is a little-endian short at offset 99. */
272    p = start + 99;
273    if ( p + 2 > limit )
274    {
275      error = T1_Err_Unknown_File_Format;
276      goto Exit;
277    }
278    width_table_length = LITTLE_ENDIAN_USHORT( p );
279
280    p += 18 + width_table_length;
281    if ( p + 0x12 > limit || LITTLE_ENDIAN_USHORT( p ) < 0x12 )
282      /* extension table is probably optional */
283      goto Exit;
284
285    /* Kerning offset is 14 bytes from start of extensions table. */
286    p += 14;
287    p = start + LITTLE_ENDIAN_UINT( p );
288    if ( p + 2 > limit )
289    {
290      error = T1_Err_Unknown_File_Format;
291      goto Exit;
292    }
293
294    kern_count = LITTLE_ENDIAN_USHORT( p );
295    p += 2;
296    if ( p + 4 * kern_count > limit )
297    {
298      error = T1_Err_Unknown_File_Format;
299      goto Exit;
300    }
301
302    /* Actually, kerning pairs are simply optional! */
303    if ( kern_count == 0 )
304      goto Exit;
305
306    /* allocate the pairs */
307    if ( FT_NEW( afm ) || FT_NEW_ARRAY( afm->kern_pairs, kern_count ) )
308      goto Exit;
309
310    /* save in face object */
311    ((T1_Face)t1_face)->afm_data = afm;
312
313    t1_face->face_flags |= FT_FACE_FLAG_KERNING;
314
315    /* now, read each kern pair */
316    pair           = afm->kern_pairs;
317    afm->num_pairs = kern_count;
318    limit          = p + 4 * kern_count;
319
320    /* PFM kerning data are stored by encoding rather than glyph index, */
321    /* so find the PostScript charmap of this font and install it       */
322    /* temporarily.  If we find no PostScript charmap, then just use    */
323    /* the default and hope it is the right one.                        */
324    oldcharmap = t1_face->charmap;
325    charmap    = NULL;
326
327    for ( n = 0; n < t1_face->num_charmaps; n++ )
328    {
329      charmap = t1_face->charmaps[n];
330      /* check against PostScript pseudo platform */
331      if ( charmap->platform_id == 7 )
332      {
333        error = FT_Set_Charmap( t1_face, charmap );
334        if ( error )
335          goto Exit;
336        break;
337      }
338    }
339
340    /* Kerning info is stored as:             */
341    /*                                        */
342    /*   encoding of first glyph (1 byte)     */
343    /*   encoding of second glyph (1 byte)    */
344    /*   offset (little-endian short)         */
345    for ( ; p < limit ; p+=4 )
346    {
347      pair->glyph1 = FT_Get_Char_Index( t1_face, p[0] );
348      pair->glyph2 = FT_Get_Char_Index( t1_face, p[1] );
349
350      pair->kerning.x = (FT_Short)LITTLE_ENDIAN_USHORT(p + 2);
351      pair->kerning.y = 0;
352
353      pair++;
354    }
355
356    if ( oldcharmap != NULL )
357      error = FT_Set_Charmap( t1_face, oldcharmap );
358    if ( error )
359      goto Exit;
360
361    /* now, sort the kern pairs according to their glyph indices */
362    ft_qsort( afm->kern_pairs, kern_count, sizeof ( T1_Kern_Pair ),
363              compare_kern_pairs );
364
365  Exit:
366    if ( error )
367      FT_FREE( afm );
368
369    return error;
370  }
371
372
373  /* parse a metrics file -- either AFM or PFM depending on what */
374  /* it turns out to be                                          */
375  FT_LOCAL_DEF( FT_Error )
376  T1_Read_Metrics( FT_Face    t1_face,
377                   FT_Stream  stream )
378  {
379    FT_Error  error;
380    FT_Byte*  start;
381
382
383    if ( FT_FRAME_ENTER( stream->size ) )
384      return error;
385
386    start = (FT_Byte*)stream->cursor;
387
388    if ( stream->size >= ft_strlen( "StartFontMetrics" )    &&
389         ft_strncmp( (const char*)start, "StartFontMetrics",
390                     ft_strlen( "StartFontMetrics" ) ) == 0 )
391      error = T1_Read_AFM( t1_face, stream );
392
393    else if ( stream->size > 6                                &&
394              start[0] == 0x00 && start[1] == 0x01            &&
395              LITTLE_ENDIAN_UINT( start + 2 ) == stream->size )
396      error = T1_Read_PFM( t1_face, stream );
397
398    else
399      error = T1_Err_Unknown_File_Format;
400
401    FT_FRAME_EXIT();
402
403    return error;
404  }
405
406
407  /* find the kerning for a given glyph pair */
408  FT_LOCAL_DEF( void )
409  T1_Get_Kerning( T1_AFM*     afm,
410                  FT_UInt     glyph1,
411                  FT_UInt     glyph2,
412                  FT_Vector*  kerning )
413  {
414    T1_Kern_Pair  *min, *mid, *max;
415    FT_ULong      idx = KERN_INDEX( glyph1, glyph2 );
416
417
418    /* simple binary search */
419    min = afm->kern_pairs;
420    max = min + afm->num_pairs - 1;
421
422    while ( min <= max )
423    {
424      FT_ULong  midi;
425
426
427      mid  = min + ( max - min ) / 2;
428      midi = KERN_INDEX( mid->glyph1, mid->glyph2 );
429
430      if ( midi == idx )
431      {
432        *kerning = mid->kerning;
433        return;
434      }
435
436      if ( midi < idx )
437        min = mid + 1;
438      else
439        max = mid - 1;
440    }
441
442    kerning->x = 0;
443    kerning->y = 0;
444  }
445
446
447/* END */
Note: See TracBrowser for help on using the repository browser.