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 )
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
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 )
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
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 )
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 )
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 )
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