1 /****************************************************************************
2  *
3  * t1afm.c
4  *
5  *   AFM support for Type 1 fonts (body).
6  *
7  * Copyright (C) 1996-2019 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 FT_INTERNAL_DEBUG_H
22 #include FT_INTERNAL_STREAM_H
23 #include FT_INTERNAL_POSTSCRIPT_AUX_H
24 #include "t1errors.h"
25 
26 
27 #ifndef T1_CONFIG_OPTION_NO_AFM
28 
29   /**************************************************************************
30    *
31    * The macro FT_COMPONENT is used in trace mode.  It is an implicit
32    * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
33    * messages during execution.
34    */
35 #undef  FT_COMPONENT
36 #define FT_COMPONENT  t1afm
37 
38 
39   FT_LOCAL_DEF( void )
T1_Done_Metrics(FT_Memory memory,AFM_FontInfo fi)40   T1_Done_Metrics( FT_Memory     memory,
41                    AFM_FontInfo  fi )
42   {
43     FT_FREE( fi->KernPairs );
44     fi->NumKernPair = 0;
45 
46     FT_FREE( fi->TrackKerns );
47     fi->NumTrackKern = 0;
48 
49     FT_FREE( fi );
50   }
51 
52 
53   /* read a glyph name and return the equivalent glyph index */
54   static FT_Int
t1_get_index(const char * name,FT_Offset len,void * user_data)55   t1_get_index( const char*  name,
56                 FT_Offset    len,
57                 void*        user_data )
58   {
59     T1_Font  type1 = (T1_Font)user_data;
60     FT_Int   n;
61 
62 
63     /* PS string/name length must be < 16-bit */
64     if ( len > 0xFFFFU )
65       return 0;
66 
67     for ( n = 0; n < type1->num_glyphs; n++ )
68     {
69       char*  gname = (char*)type1->glyph_names[n];
70 
71 
72       if ( gname && gname[0] == name[0]        &&
73            ft_strlen( gname ) == len           &&
74            ft_strncmp( gname, name, len ) == 0 )
75         return n;
76     }
77 
78     return 0;
79   }
80 
81 
82 #undef  KERN_INDEX
83 #define KERN_INDEX( g1, g2 )  ( ( (FT_ULong)(g1) << 16 ) | (g2) )
84 
85 
86   /* compare two kerning pairs */
87   FT_CALLBACK_DEF( int )
compare_kern_pairs(const void * a,const void * b)88   compare_kern_pairs( const void*  a,
89                       const void*  b )
90   {
91     AFM_KernPair  pair1 = (AFM_KernPair)a;
92     AFM_KernPair  pair2 = (AFM_KernPair)b;
93 
94     FT_ULong  index1 = KERN_INDEX( pair1->index1, pair1->index2 );
95     FT_ULong  index2 = KERN_INDEX( pair2->index1, pair2->index2 );
96 
97 
98     if ( index1 > index2 )
99       return 1;
100     else if ( index1 < index2 )
101       return -1;
102     else
103       return 0;
104   }
105 
106 
107   /* parse a PFM file -- for now, only read the kerning pairs */
108   static FT_Error
T1_Read_PFM(FT_Face t1_face,FT_Stream stream,AFM_FontInfo fi)109   T1_Read_PFM( FT_Face       t1_face,
110                FT_Stream     stream,
111                AFM_FontInfo  fi )
112   {
113     FT_Error      error  = FT_Err_Ok;
114     FT_Memory     memory = stream->memory;
115     FT_Byte*      start;
116     FT_Byte*      limit;
117     FT_Byte*      p;
118     AFM_KernPair  kp;
119     FT_Int        width_table_length;
120     FT_CharMap    oldcharmap;
121     FT_CharMap    charmap;
122     FT_Int        n;
123 
124 
125     start = (FT_Byte*)stream->cursor;
126     limit = (FT_Byte*)stream->limit;
127 
128     /* Figure out how long the width table is.          */
129     /* This info is a little-endian short at offset 99. */
130     p = start + 99;
131     if ( p + 2 > limit )
132     {
133       error = FT_THROW( Unknown_File_Format );
134       goto Exit;
135     }
136     width_table_length = FT_PEEK_USHORT_LE( p );
137 
138     p += 18 + width_table_length;
139     if ( p + 0x12 > limit || FT_PEEK_USHORT_LE( p ) < 0x12 )
140       /* extension table is probably optional */
141       goto Exit;
142 
143     /* Kerning offset is 14 bytes from start of extensions table. */
144     p += 14;
145     p = start + FT_PEEK_ULONG_LE( p );
146 
147     if ( p == start )
148       /* zero offset means no table */
149       goto Exit;
150 
151     if ( p + 2 > limit )
152     {
153       error = FT_THROW( Unknown_File_Format );
154       goto Exit;
155     }
156 
157     fi->NumKernPair = FT_PEEK_USHORT_LE( p );
158     p += 2;
159     if ( p + 4 * fi->NumKernPair > limit )
160     {
161       error = FT_THROW( Unknown_File_Format );
162       goto Exit;
163     }
164 
165     /* Actually, kerning pairs are simply optional! */
166     if ( fi->NumKernPair == 0 )
167       goto Exit;
168 
169     /* allocate the pairs */
170     if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) )
171       goto Exit;
172 
173     /* now, read each kern pair */
174     kp    = fi->KernPairs;
175     limit = p + 4 * fi->NumKernPair;
176 
177     /* PFM kerning data are stored by encoding rather than glyph index, */
178     /* so find the PostScript charmap of this font and install it       */
179     /* temporarily.  If we find no PostScript charmap, then just use    */
180     /* the default and hope it is the right one.                        */
181     oldcharmap = t1_face->charmap;
182     charmap    = NULL;
183 
184     for ( n = 0; n < t1_face->num_charmaps; n++ )
185     {
186       charmap = t1_face->charmaps[n];
187       /* check against PostScript pseudo platform */
188       if ( charmap->platform_id == 7 )
189       {
190         error = FT_Set_Charmap( t1_face, charmap );
191         if ( error )
192           goto Exit;
193         break;
194       }
195     }
196 
197     /* Kerning info is stored as:             */
198     /*                                        */
199     /*   encoding of first glyph (1 byte)     */
200     /*   encoding of second glyph (1 byte)    */
201     /*   offset (little-endian short)         */
202     for ( ; p < limit; p += 4 )
203     {
204       kp->index1 = FT_Get_Char_Index( t1_face, p[0] );
205       kp->index2 = FT_Get_Char_Index( t1_face, p[1] );
206 
207       kp->x = (FT_Int)FT_PEEK_SHORT_LE(p + 2);
208       kp->y = 0;
209 
210       kp++;
211     }
212 
213     if ( oldcharmap )
214       error = FT_Set_Charmap( t1_face, oldcharmap );
215     if ( error )
216       goto Exit;
217 
218     /* now, sort the kern pairs according to their glyph indices */
219     ft_qsort( fi->KernPairs, fi->NumKernPair, sizeof ( AFM_KernPairRec ),
220               compare_kern_pairs );
221 
222   Exit:
223     if ( error )
224     {
225       FT_FREE( fi->KernPairs );
226       fi->NumKernPair = 0;
227     }
228 
229     return error;
230   }
231 
232 
233   /* parse a metrics file -- either AFM or PFM depending on what */
234   /* it turns out to be                                          */
235   FT_LOCAL_DEF( FT_Error )
T1_Read_Metrics(FT_Face t1_face,FT_Stream stream)236   T1_Read_Metrics( FT_Face    t1_face,
237                    FT_Stream  stream )
238   {
239     PSAux_Service  psaux;
240     FT_Memory      memory  = stream->memory;
241     AFM_ParserRec  parser;
242     AFM_FontInfo   fi      = NULL;
243     FT_Error       error   = FT_ERR( Unknown_File_Format );
244     T1_Face        face    = (T1_Face)t1_face;
245     T1_Font        t1_font = &face->type1;
246 
247 
248     if ( face->afm_data )
249     {
250       FT_TRACE1(( "T1_Read_Metrics:"
251                   " Freeing previously attached metrics data.\n" ));
252       T1_Done_Metrics( memory, (AFM_FontInfo)face->afm_data );
253 
254       face->afm_data = NULL;
255     }
256 
257     if ( FT_NEW( fi )                   ||
258          FT_FRAME_ENTER( stream->size ) )
259       goto Exit;
260 
261     fi->FontBBox  = t1_font->font_bbox;
262     fi->Ascender  = t1_font->font_bbox.yMax;
263     fi->Descender = t1_font->font_bbox.yMin;
264 
265     psaux = (PSAux_Service)face->psaux;
266     if ( psaux->afm_parser_funcs )
267     {
268       error = psaux->afm_parser_funcs->init( &parser,
269                                              stream->memory,
270                                              stream->cursor,
271                                              stream->limit );
272 
273       if ( !error )
274       {
275         parser.FontInfo  = fi;
276         parser.get_index = t1_get_index;
277         parser.user_data = t1_font;
278 
279         error = psaux->afm_parser_funcs->parse( &parser );
280         psaux->afm_parser_funcs->done( &parser );
281       }
282     }
283 
284     if ( FT_ERR_EQ( error, Unknown_File_Format ) )
285     {
286       FT_Byte*  start = stream->cursor;
287 
288 
289       /* MS Windows allows versions up to 0x3FF without complaining */
290       if ( stream->size > 6                              &&
291            start[1] < 4                                  &&
292            FT_PEEK_ULONG_LE( start + 2 ) == stream->size )
293         error = T1_Read_PFM( t1_face, stream, fi );
294     }
295 
296     if ( !error )
297     {
298       t1_font->font_bbox = fi->FontBBox;
299 
300       t1_face->bbox.xMin =   fi->FontBBox.xMin            >> 16;
301       t1_face->bbox.yMin =   fi->FontBBox.yMin            >> 16;
302       /* no `U' suffix here to 0xFFFF! */
303       t1_face->bbox.xMax = ( fi->FontBBox.xMax + 0xFFFF ) >> 16;
304       t1_face->bbox.yMax = ( fi->FontBBox.yMax + 0xFFFF ) >> 16;
305 
306       /* no `U' suffix here to 0x8000! */
307       t1_face->ascender  = (FT_Short)( ( fi->Ascender  + 0x8000 ) >> 16 );
308       t1_face->descender = (FT_Short)( ( fi->Descender + 0x8000 ) >> 16 );
309 
310       if ( fi->NumKernPair )
311       {
312         t1_face->face_flags |= FT_FACE_FLAG_KERNING;
313         face->afm_data       = fi;
314         fi                   = NULL;
315       }
316     }
317 
318     FT_FRAME_EXIT();
319 
320   Exit:
321     if ( fi )
322       T1_Done_Metrics( memory, fi );
323 
324     return error;
325   }
326 
327 
328   /* find the kerning for a given glyph pair */
329   FT_LOCAL_DEF( void )
T1_Get_Kerning(AFM_FontInfo fi,FT_UInt glyph1,FT_UInt glyph2,FT_Vector * kerning)330   T1_Get_Kerning( AFM_FontInfo  fi,
331                   FT_UInt       glyph1,
332                   FT_UInt       glyph2,
333                   FT_Vector*    kerning )
334   {
335     AFM_KernPair  min, mid, max;
336     FT_ULong      idx = KERN_INDEX( glyph1, glyph2 );
337 
338 
339     /* simple binary search */
340     min = fi->KernPairs;
341     max = min + fi->NumKernPair - 1;
342 
343     while ( min <= max )
344     {
345       FT_ULong  midi;
346 
347 
348       mid  = min + ( max - min ) / 2;
349       midi = KERN_INDEX( mid->index1, mid->index2 );
350 
351       if ( midi == idx )
352       {
353         kerning->x = mid->x;
354         kerning->y = mid->y;
355 
356         return;
357       }
358 
359       if ( midi < idx )
360         min = mid + 1;
361       else
362         max = mid - 1;
363     }
364 
365     kerning->x = 0;
366     kerning->y = 0;
367   }
368 
369 
370   FT_LOCAL_DEF( FT_Error )
T1_Get_Track_Kerning(FT_Face face,FT_Fixed ptsize,FT_Int degree,FT_Fixed * kerning)371   T1_Get_Track_Kerning( FT_Face    face,
372                         FT_Fixed   ptsize,
373                         FT_Int     degree,
374                         FT_Fixed*  kerning )
375   {
376     AFM_FontInfo  fi = (AFM_FontInfo)( (T1_Face)face )->afm_data;
377     FT_UInt       i;
378 
379 
380     if ( !fi )
381       return FT_THROW( Invalid_Argument );
382 
383     for ( i = 0; i < fi->NumTrackKern; i++ )
384     {
385       AFM_TrackKern  tk = fi->TrackKerns + i;
386 
387 
388       if ( tk->degree != degree )
389         continue;
390 
391       if ( ptsize < tk->min_ptsize )
392         *kerning = tk->min_kern;
393       else if ( ptsize > tk->max_ptsize )
394         *kerning = tk->max_kern;
395       else
396       {
397         *kerning = FT_MulDiv( ptsize - tk->min_ptsize,
398                               tk->max_kern - tk->min_kern,
399                               tk->max_ptsize - tk->min_ptsize ) +
400                    tk->min_kern;
401       }
402     }
403 
404     return FT_Err_Ok;
405   }
406 
407 #else /* T1_CONFIG_OPTION_NO_AFM */
408 
409   /* ANSI C doesn't like empty source files */
410   typedef int  _t1_afm_dummy;
411 
412 #endif /* T1_CONFIG_OPTION_NO_AFM */
413 
414 
415 /* END */
416