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