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