1 /***************************************************************************/ |
|
2 /* */ |
|
3 /* t1afm.c */ |
|
4 /* */ |
|
5 /* AFM support for Type 1 fonts (body). */ |
|
6 /* */ |
|
7 /* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, */ |
|
8 /* 2010 by */ |
|
9 /* David Turner, Robert Wilhelm, and Werner Lemberg. */ |
|
10 /* */ |
|
11 /* This file is part of the FreeType project, and may only be used, */ |
|
12 /* modified, and distributed under the terms of the FreeType project */ |
|
13 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ |
|
14 /* this file you indicate that you have read the license and */ |
|
15 /* understand and accept it fully. */ |
|
16 /* */ |
|
17 /***************************************************************************/ |
|
18 |
|
19 |
|
20 #include <ft2build.h> |
|
21 #include "t1afm.h" |
|
22 #include "t1errors.h" |
|
23 #include FT_INTERNAL_STREAM_H |
|
24 #include FT_INTERNAL_POSTSCRIPT_AUX_H |
|
25 |
|
26 |
|
27 /*************************************************************************/ |
|
28 /* */ |
|
29 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ |
|
30 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ |
|
31 /* messages during execution. */ |
|
32 /* */ |
|
33 #undef FT_COMPONENT |
|
34 #define FT_COMPONENT trace_t1afm |
|
35 |
|
36 |
|
37 FT_LOCAL_DEF( void ) |
|
38 T1_Done_Metrics( FT_Memory memory, |
|
39 AFM_FontInfo fi ) |
|
40 { |
|
41 FT_FREE( fi->KernPairs ); |
|
42 fi->NumKernPair = 0; |
|
43 |
|
44 FT_FREE( fi->TrackKerns ); |
|
45 fi->NumTrackKern = 0; |
|
46 |
|
47 FT_FREE( fi ); |
|
48 } |
|
49 |
|
50 |
|
51 /* read a glyph name and return the equivalent glyph index */ |
|
52 static FT_Int |
|
53 t1_get_index( const char* name, |
|
54 FT_Offset len, |
|
55 void* user_data ) |
|
56 { |
|
57 T1_Font type1 = (T1_Font)user_data; |
|
58 FT_Int n; |
|
59 |
|
60 |
|
61 /* PS string/name length must be < 16-bit */ |
|
62 if ( len > 0xFFFFU ) |
|
63 return 0; |
|
64 |
|
65 for ( n = 0; n < type1->num_glyphs; n++ ) |
|
66 { |
|
67 char* gname = (char*)type1->glyph_names[n]; |
|
68 |
|
69 |
|
70 if ( gname && gname[0] == name[0] && |
|
71 ft_strlen( gname ) == len && |
|
72 ft_strncmp( gname, name, len ) == 0 ) |
|
73 return n; |
|
74 } |
|
75 |
|
76 return 0; |
|
77 } |
|
78 |
|
79 |
|
80 #undef KERN_INDEX |
|
81 #define KERN_INDEX( g1, g2 ) ( ( (FT_ULong)(g1) << 16 ) | (g2) ) |
|
82 |
|
83 |
|
84 /* compare two kerning pairs */ |
|
85 FT_CALLBACK_DEF( int ) |
|
86 compare_kern_pairs( const void* a, |
|
87 const void* b ) |
|
88 { |
|
89 AFM_KernPair pair1 = (AFM_KernPair)a; |
|
90 AFM_KernPair pair2 = (AFM_KernPair)b; |
|
91 |
|
92 FT_ULong index1 = KERN_INDEX( pair1->index1, pair1->index2 ); |
|
93 FT_ULong index2 = KERN_INDEX( pair2->index1, pair2->index2 ); |
|
94 |
|
95 |
|
96 if ( index1 > index2 ) |
|
97 return 1; |
|
98 else if ( index1 < index2 ) |
|
99 return -1; |
|
100 else |
|
101 return 0; |
|
102 } |
|
103 |
|
104 |
|
105 /* parse a PFM file -- for now, only read the kerning pairs */ |
|
106 static FT_Error |
|
107 T1_Read_PFM( FT_Face t1_face, |
|
108 FT_Stream stream, |
|
109 AFM_FontInfo fi ) |
|
110 { |
|
111 FT_Error error = T1_Err_Ok; |
|
112 FT_Memory memory = stream->memory; |
|
113 FT_Byte* start; |
|
114 FT_Byte* limit; |
|
115 FT_Byte* p; |
|
116 AFM_KernPair kp; |
|
117 FT_Int width_table_length; |
|
118 FT_CharMap oldcharmap; |
|
119 FT_CharMap charmap; |
|
120 FT_Int n; |
|
121 |
|
122 |
|
123 start = (FT_Byte*)stream->cursor; |
|
124 limit = (FT_Byte*)stream->limit; |
|
125 p = start; |
|
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 = T1_Err_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 = T1_Err_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 = T1_Err_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 charmap = NULL; |
|
182 |
|
183 for ( n = 0; n < t1_face->num_charmaps; n++ ) |
|
184 { |
|
185 charmap = t1_face->charmaps[n]; |
|
186 /* check against PostScript pseudo platform */ |
|
187 if ( charmap->platform_id == 7 ) |
|
188 { |
|
189 error = FT_Set_Charmap( t1_face, charmap ); |
|
190 if ( error ) |
|
191 goto Exit; |
|
192 break; |
|
193 } |
|
194 } |
|
195 |
|
196 /* Kerning info is stored as: */ |
|
197 /* */ |
|
198 /* encoding of first glyph (1 byte) */ |
|
199 /* encoding of second glyph (1 byte) */ |
|
200 /* offset (little-endian short) */ |
|
201 for ( ; p < limit ; p += 4 ) |
|
202 { |
|
203 kp->index1 = FT_Get_Char_Index( t1_face, p[0] ); |
|
204 kp->index2 = FT_Get_Char_Index( t1_face, p[1] ); |
|
205 |
|
206 kp->x = (FT_Int)FT_PEEK_SHORT_LE(p + 2); |
|
207 kp->y = 0; |
|
208 |
|
209 kp++; |
|
210 } |
|
211 |
|
212 if ( oldcharmap != NULL ) |
|
213 error = FT_Set_Charmap( t1_face, oldcharmap ); |
|
214 if ( error ) |
|
215 goto Exit; |
|
216 |
|
217 /* now, sort the kern pairs according to their glyph indices */ |
|
218 ft_qsort( fi->KernPairs, fi->NumKernPair, sizeof ( AFM_KernPairRec ), |
|
219 compare_kern_pairs ); |
|
220 |
|
221 Exit: |
|
222 if ( error ) |
|
223 { |
|
224 FT_FREE( fi->KernPairs ); |
|
225 fi->NumKernPair = 0; |
|
226 } |
|
227 |
|
228 return error; |
|
229 } |
|
230 |
|
231 |
|
232 /* parse a metrics file -- either AFM or PFM depending on what */ |
|
233 /* it turns out to be */ |
|
234 FT_LOCAL_DEF( FT_Error ) |
|
235 T1_Read_Metrics( FT_Face t1_face, |
|
236 FT_Stream stream ) |
|
237 { |
|
238 PSAux_Service psaux; |
|
239 FT_Memory memory = stream->memory; |
|
240 AFM_ParserRec parser; |
|
241 AFM_FontInfo fi = NULL; |
|
242 FT_Error error = T1_Err_Unknown_File_Format; |
|
243 T1_Font t1_font = &( (T1_Face)t1_face )->type1; |
|
244 |
|
245 |
|
246 if ( FT_NEW( fi ) || |
|
247 FT_FRAME_ENTER( stream->size ) ) |
|
248 goto Exit; |
|
249 |
|
250 fi->FontBBox = t1_font->font_bbox; |
|
251 fi->Ascender = t1_font->font_bbox.yMax; |
|
252 fi->Descender = t1_font->font_bbox.yMin; |
|
253 |
|
254 psaux = (PSAux_Service)( (T1_Face)t1_face )->psaux; |
|
255 if ( psaux && psaux->afm_parser_funcs ) |
|
256 { |
|
257 error = psaux->afm_parser_funcs->init( &parser, |
|
258 stream->memory, |
|
259 stream->cursor, |
|
260 stream->limit ); |
|
261 |
|
262 if ( !error ) |
|
263 { |
|
264 parser.FontInfo = fi; |
|
265 parser.get_index = t1_get_index; |
|
266 parser.user_data = t1_font; |
|
267 |
|
268 error = psaux->afm_parser_funcs->parse( &parser ); |
|
269 psaux->afm_parser_funcs->done( &parser ); |
|
270 } |
|
271 } |
|
272 |
|
273 if ( error == T1_Err_Unknown_File_Format ) |
|
274 { |
|
275 FT_Byte* start = stream->cursor; |
|
276 |
|
277 |
|
278 /* MS Windows allows versions up to 0x3FF without complaining */ |
|
279 if ( stream->size > 6 && |
|
280 start[1] < 4 && |
|
281 FT_PEEK_ULONG_LE( start + 2 ) == stream->size ) |
|
282 error = T1_Read_PFM( t1_face, stream, fi ); |
|
283 } |
|
284 |
|
285 if ( !error ) |
|
286 { |
|
287 t1_font->font_bbox = fi->FontBBox; |
|
288 |
|
289 t1_face->bbox.xMin = fi->FontBBox.xMin >> 16; |
|
290 t1_face->bbox.yMin = fi->FontBBox.yMin >> 16; |
|
291 /* no `U' suffix here to 0xFFFF! */ |
|
292 t1_face->bbox.xMax = ( fi->FontBBox.xMax + 0xFFFF ) >> 16; |
|
293 t1_face->bbox.yMax = ( fi->FontBBox.yMax + 0xFFFF ) >> 16; |
|
294 |
|
295 /* no `U' suffix here to 0x8000! */ |
|
296 t1_face->ascender = (FT_Short)( ( fi->Ascender + 0x8000 ) >> 16 ); |
|
297 t1_face->descender = (FT_Short)( ( fi->Descender + 0x8000 ) >> 16 ); |
|
298 |
|
299 if ( fi->NumKernPair ) |
|
300 { |
|
301 t1_face->face_flags |= FT_FACE_FLAG_KERNING; |
|
302 ( (T1_Face)t1_face )->afm_data = fi; |
|
303 fi = NULL; |
|
304 } |
|
305 } |
|
306 |
|
307 FT_FRAME_EXIT(); |
|
308 |
|
309 Exit: |
|
310 if ( fi != NULL ) |
|
311 T1_Done_Metrics( memory, fi ); |
|
312 |
|
313 return error; |
|
314 } |
|
315 |
|
316 |
|
317 /* find the kerning for a given glyph pair */ |
|
318 FT_LOCAL_DEF( void ) |
|
319 T1_Get_Kerning( AFM_FontInfo fi, |
|
320 FT_UInt glyph1, |
|
321 FT_UInt glyph2, |
|
322 FT_Vector* kerning ) |
|
323 { |
|
324 AFM_KernPair min, mid, max; |
|
325 FT_ULong idx = KERN_INDEX( glyph1, glyph2 ); |
|
326 |
|
327 |
|
328 /* simple binary search */ |
|
329 min = fi->KernPairs; |
|
330 max = min + fi->NumKernPair - 1; |
|
331 |
|
332 while ( min <= max ) |
|
333 { |
|
334 FT_ULong midi; |
|
335 |
|
336 |
|
337 mid = min + ( max - min ) / 2; |
|
338 midi = KERN_INDEX( mid->index1, mid->index2 ); |
|
339 |
|
340 if ( midi == idx ) |
|
341 { |
|
342 kerning->x = mid->x; |
|
343 kerning->y = mid->y; |
|
344 |
|
345 return; |
|
346 } |
|
347 |
|
348 if ( midi < idx ) |
|
349 min = mid + 1; |
|
350 else |
|
351 max = mid - 1; |
|
352 } |
|
353 |
|
354 kerning->x = 0; |
|
355 kerning->y = 0; |
|
356 } |
|
357 |
|
358 |
|
359 FT_LOCAL_DEF( FT_Error ) |
|
360 T1_Get_Track_Kerning( FT_Face face, |
|
361 FT_Fixed ptsize, |
|
362 FT_Int degree, |
|
363 FT_Fixed* kerning ) |
|
364 { |
|
365 AFM_FontInfo fi = (AFM_FontInfo)( (T1_Face)face )->afm_data; |
|
366 FT_Int i; |
|
367 |
|
368 |
|
369 if ( !fi ) |
|
370 return T1_Err_Invalid_Argument; |
|
371 |
|
372 for ( i = 0; i < fi->NumTrackKern; i++ ) |
|
373 { |
|
374 AFM_TrackKern tk = fi->TrackKerns + i; |
|
375 |
|
376 |
|
377 if ( tk->degree != degree ) |
|
378 continue; |
|
379 |
|
380 if ( ptsize < tk->min_ptsize ) |
|
381 *kerning = tk->min_kern; |
|
382 else if ( ptsize > tk->max_ptsize ) |
|
383 *kerning = tk->max_kern; |
|
384 else |
|
385 { |
|
386 *kerning = FT_MulDiv( ptsize - tk->min_ptsize, |
|
387 tk->max_kern - tk->min_kern, |
|
388 tk->max_ptsize - tk->min_ptsize ) + |
|
389 tk->min_kern; |
|
390 } |
|
391 } |
|
392 |
|
393 return T1_Err_Ok; |
|
394 } |
|
395 |
|
396 |
|
397 /* END */ |
|