blob: 780ab33bf7ef54fe79a59d837573272f0789d5e3 [file] [log] [blame]
Werner Lembergbd5ae402000-07-05 04:32:02 +00001/***************************************************************************/
2/* */
Werner Lembergb5084e12000-10-28 17:10:06 +00003/* t1afm.c */
Werner Lembergbd5ae402000-07-05 04:32:02 +00004/* */
5/* AFM support for Type 1 fonts (body). */
6/* */
Werner Lembergc8f5b982010-07-12 21:13:22 +02007/* Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, */
8/* 2010 by */
Werner Lembergbd5ae402000-07-05 04:32:02 +00009/* 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
Werner Lembergcc069be2000-12-08 16:17:16 +000019
David Turner19ed8af2000-12-08 02:42:29 +000020#include <ft2build.h>
David Turner8d3a4012001-03-20 11:14:24 +000021#include "t1afm.h"
Werner Lemberg645af082004-09-06 07:06:56 +000022#include "t1errors.h"
David Turner19ed8af2000-12-08 02:42:29 +000023#include FT_INTERNAL_STREAM_H
David Turner6a681fa2006-01-27 12:11:22 +000024#include FT_INTERNAL_POSTSCRIPT_AUX_H
Werner Lembergbd5ae402000-07-05 04:32:02 +000025
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
Werner Lembergb5084e12000-10-28 17:10:06 +000034#define FT_COMPONENT trace_t1afm
Werner Lembergbd5ae402000-07-05 04:32:02 +000035
David Turner95bec282000-02-15 12:55:57 +000036
David Turnerbc82f1b2002-03-01 02:26:22 +000037 FT_LOCAL_DEF( void )
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +000038 T1_Done_Metrics( FT_Memory memory,
39 AFM_FontInfo fi )
David Turner95bec282000-02-15 12:55:57 +000040 {
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +000041 FT_FREE( fi->KernPairs );
42 fi->NumKernPair = 0;
43
44 FT_FREE( fi->TrackKerns );
45 fi->NumTrackKern = 0;
46
47 FT_FREE( fi );
David Turner95bec282000-02-15 12:55:57 +000048 }
49
Werner Lembergbd5ae402000-07-05 04:32:02 +000050
Werner Lembergbd5ae402000-07-05 04:32:02 +000051 /* read a glyph name and return the equivalent glyph index */
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +000052 static FT_Int
53 t1_get_index( const char* name,
suzuki toshiyad1ee3782009-08-01 00:30:16 +090054 FT_Offset len,
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +000055 void* user_data )
David Turner95bec282000-02-15 12:55:57 +000056 {
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +000057 T1_Font type1 = (T1_Font)user_data;
58 FT_Int n;
Werner Lembergbd5ae402000-07-05 04:32:02 +000059
David Turner95bec282000-02-15 12:55:57 +000060
suzuki toshiyaf7ecdbc2009-08-01 00:30:12 +090061 /* PS string/name length must be < 16-bit */
Werner Lemberg934d0542009-11-20 06:26:22 +010062 if ( len > 0xFFFFU )
suzuki toshiyaf7ecdbc2009-08-01 00:30:12 +090063 return 0;
64
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +000065 for ( n = 0; n < type1->num_glyphs; n++ )
David Turner95bec282000-02-15 12:55:57 +000066 {
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +000067 char* gname = (char*)type1->glyph_names[n];
David Turnere49ab252000-05-16 23:44:38 +000068
Werner Lembergbd5ae402000-07-05 04:32:02 +000069
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +000070 if ( gname && gname[0] == name[0] &&
71 ft_strlen( gname ) == len &&
72 ft_strncmp( gname, name, len ) == 0 )
73 return n;
David Turner9c848872000-02-28 11:32:54 +000074 }
David Turnere49ab252000-05-16 23:44:38 +000075
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +000076 return 0;
David Turner95bec282000-02-15 12:55:57 +000077 }
78
79
80#undef KERN_INDEX
David Turner5f1f7502006-06-07 05:11:50 +000081#define KERN_INDEX( g1, g2 ) ( ( (FT_ULong)(g1) << 16 ) | (g2) )
David Turner95bec282000-02-15 12:55:57 +000082
Werner Lembergbd5ae402000-07-05 04:32:02 +000083
84 /* compare two kerning pairs */
Werner Lemberg4a2305c2001-06-28 07:17:51 +000085 FT_CALLBACK_DEF( int )
David Turnerdee78132001-06-27 09:26:46 +000086 compare_kern_pairs( const void* a,
87 const void* b )
David Turner95bec282000-02-15 12:55:57 +000088 {
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +000089 AFM_KernPair pair1 = (AFM_KernPair)a;
90 AFM_KernPair pair2 = (AFM_KernPair)b;
David Turnere49ab252000-05-16 23:44:38 +000091
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +000092 FT_ULong index1 = KERN_INDEX( pair1->index1, pair1->index2 );
93 FT_ULong index2 = KERN_INDEX( pair2->index1, pair2->index2 );
David Turnere49ab252000-05-16 23:44:38 +000094
Werner Lembergbd5ae402000-07-05 04:32:02 +000095
Werner Lemberga4cb0d92008-08-23 19:54:06 +000096 if ( index1 > index2 )
97 return 1;
98 else if ( index1 < index2 )
99 return -1;
100 else
101 return 0;
David Turner95bec282000-02-15 12:55:57 +0000102 }
103
104
Werner Lembergd232f592004-08-02 05:38:33 +0000105 /* parse a PFM file -- for now, only read the kerning pairs */
106 static FT_Error
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000107 T1_Read_PFM( FT_Face t1_face,
108 FT_Stream stream,
109 AFM_FontInfo fi )
Werner Lembergd232f592004-08-02 05:38:33 +0000110 {
Werner Lembergce94c7a2006-01-16 22:35:33 +0000111 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;
Werner Lembergd232f592004-08-02 05:38:33 +0000121
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 }
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000135 width_table_length = FT_PEEK_USHORT_LE( p );
Werner Lembergd232f592004-08-02 05:38:33 +0000136
137 p += 18 + width_table_length;
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000138 if ( p + 0x12 > limit || FT_PEEK_USHORT_LE( p ) < 0x12 )
Werner Lembergd232f592004-08-02 05:38:33 +0000139 /* extension table is probably optional */
140 goto Exit;
141
142 /* Kerning offset is 14 bytes from start of extensions table. */
143 p += 14;
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000144 p = start + FT_PEEK_ULONG_LE( p );
Werner Lembergea4c3dc2005-11-08 09:10:16 +0000145
146 if ( p == start )
147 /* zero offset means no table */
148 goto Exit;
149
Werner Lembergd232f592004-08-02 05:38:33 +0000150 if ( p + 2 > limit )
151 {
152 error = T1_Err_Unknown_File_Format;
153 goto Exit;
154 }
155
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000156 fi->NumKernPair = FT_PEEK_USHORT_LE( p );
Werner Lembergd232f592004-08-02 05:38:33 +0000157 p += 2;
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000158 if ( p + 4 * fi->NumKernPair > limit )
Werner Lembergd232f592004-08-02 05:38:33 +0000159 {
160 error = T1_Err_Unknown_File_Format;
161 goto Exit;
162 }
163
164 /* Actually, kerning pairs are simply optional! */
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000165 if ( fi->NumKernPair == 0 )
Werner Lembergd232f592004-08-02 05:38:33 +0000166 goto Exit;
167
168 /* allocate the pairs */
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000169 if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) )
Werner Lembergd232f592004-08-02 05:38:33 +0000170 goto Exit;
171
Werner Lembergd232f592004-08-02 05:38:33 +0000172 /* now, read each kern pair */
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000173 kp = fi->KernPairs;
174 limit = p + 4 * fi->NumKernPair;
Werner Lembergd232f592004-08-02 05:38:33 +0000175
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) */
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000201 for ( ; p < limit ; p += 4 )
Werner Lembergd232f592004-08-02 05:38:33 +0000202 {
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000203 kp->index1 = FT_Get_Char_Index( t1_face, p[0] );
204 kp->index2 = FT_Get_Char_Index( t1_face, p[1] );
Werner Lembergd232f592004-08-02 05:38:33 +0000205
David Turner085bc6e2007-03-05 17:40:03 +0000206 kp->x = (FT_Int)FT_PEEK_SHORT_LE(p + 2);
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000207 kp->y = 0;
Werner Lembergd232f592004-08-02 05:38:33 +0000208
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000209 kp++;
Werner Lembergd232f592004-08-02 05:38:33 +0000210 }
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 */
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000218 ft_qsort( fi->KernPairs, fi->NumKernPair, sizeof ( AFM_KernPairRec ),
Werner Lembergd232f592004-08-02 05:38:33 +0000219 compare_kern_pairs );
220
221 Exit:
222 if ( error )
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000223 {
224 FT_FREE( fi->KernPairs );
225 fi->NumKernPair = 0;
226 }
Werner Lembergd232f592004-08-02 05:38:33 +0000227
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 {
Werner Lembergce94c7a2006-01-16 22:35:33 +0000238 PSAux_Service psaux;
Werner Lembergc8f5b982010-07-12 21:13:22 +0200239 FT_Memory memory = stream->memory;
Werner Lembergce94c7a2006-01-16 22:35:33 +0000240 AFM_ParserRec parser;
Werner Lembergc8f5b982010-07-12 21:13:22 +0200241 AFM_FontInfo fi = NULL;
242 FT_Error error = T1_Err_Unknown_File_Format;
Wu, Chia-I (吳佳一)98d802b2006-01-31 07:01:24 +0000243 T1_Font t1_font = &( (T1_Face)t1_face )->type1;
Werner Lembergd232f592004-08-02 05:38:33 +0000244
245
David Turner5f1f7502006-06-07 05:11:50 +0000246 if ( FT_NEW( fi ) ||
247 FT_FRAME_ENTER( stream->size ) )
248 goto Exit;
Werner Lembergd232f592004-08-02 05:38:33 +0000249
Wu, Chia-I (吳佳一)98d802b2006-01-31 07:01:24 +0000250 fi->FontBBox = t1_font->font_bbox;
251 fi->Ascender = t1_font->font_bbox.yMax;
252 fi->Descender = t1_font->font_bbox.yMin;
253
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000254 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 );
Werner Lembergd232f592004-08-02 05:38:33 +0000261
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000262 if ( !error )
263 {
264 parser.FontInfo = fi;
265 parser.get_index = t1_get_index;
Wu, Chia-I (吳佳一)98d802b2006-01-31 07:01:24 +0000266 parser.user_data = t1_font;
Werner Lembergd232f592004-08-02 05:38:33 +0000267
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000268 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
Werner Lemberg281c1482007-01-13 22:50:51 +0000278 /* MS Windows allows versions up to 0x3FF without complaining */
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000279 if ( stream->size > 6 &&
Werner Lemberg281c1482007-01-13 22:50:51 +0000280 start[1] < 4 &&
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000281 FT_PEEK_ULONG_LE( start + 2 ) == stream->size )
282 error = T1_Read_PFM( t1_face, stream, fi );
283 }
284
Wu, Chia-I (吳佳一)98d802b2006-01-31 07:01:24 +0000285 if ( !error )
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000286 {
Wu, Chia-I (吳佳一)98d802b2006-01-31 07:01:24 +0000287 t1_font->font_bbox = fi->FontBBox;
288
Werner Lemberg012552f2009-11-04 10:06:04 +0100289 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;
Wu, Chia-I (吳佳一)98d802b2006-01-31 07:01:24 +0000294
Werner Lemberg012552f2009-11-04 10:06:04 +0100295 /* 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 );
Wu, Chia-I (吳佳一)98d802b2006-01-31 07:01:24 +0000298
299 if ( fi->NumKernPair )
300 {
301 t1_face->face_flags |= FT_FACE_FLAG_KERNING;
302 ( (T1_Face)t1_face )->afm_data = fi;
David Turner5f1f7502006-06-07 05:11:50 +0000303 fi = NULL;
Wu, Chia-I (吳佳一)98d802b2006-01-31 07:01:24 +0000304 }
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000305 }
Werner Lembergd232f592004-08-02 05:38:33 +0000306
David Turner7d3a2642002-03-20 10:49:31 +0000307 FT_FRAME_EXIT();
Werner Lembergbd5ae402000-07-05 04:32:02 +0000308
David Turner5f1f7502006-06-07 05:11:50 +0000309 Exit:
310 if ( fi != NULL )
311 T1_Done_Metrics( memory, fi );
312
David Turner95bec282000-02-15 12:55:57 +0000313 return error;
314 }
315
316
Werner Lembergbd5ae402000-07-05 04:32:02 +0000317 /* find the kerning for a given glyph pair */
David Turnerbc82f1b2002-03-01 02:26:22 +0000318 FT_LOCAL_DEF( void )
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000319 T1_Get_Kerning( AFM_FontInfo fi,
320 FT_UInt glyph1,
321 FT_UInt glyph2,
322 FT_Vector* kerning )
David Turner95bec282000-02-15 12:55:57 +0000323 {
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000324 AFM_KernPair min, mid, max;
Werner Lemberg0d9165e2002-03-07 21:59:59 +0000325 FT_ULong idx = KERN_INDEX( glyph1, glyph2 );
Werner Lembergbd5ae402000-07-05 04:32:02 +0000326
David Turnere49ab252000-05-16 23:44:38 +0000327
David Turner95bec282000-02-15 12:55:57 +0000328 /* simple binary search */
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000329 min = fi->KernPairs;
330 max = min + fi->NumKernPair - 1;
David Turnere49ab252000-05-16 23:44:38 +0000331
Werner Lembergbd5ae402000-07-05 04:32:02 +0000332 while ( min <= max )
David Turner95bec282000-02-15 12:55:57 +0000333 {
David Turnerf9b8dec2000-06-16 19:34:52 +0000334 FT_ULong midi;
David Turnere49ab252000-05-16 23:44:38 +0000335
Werner Lembergbd5ae402000-07-05 04:32:02 +0000336
337 mid = min + ( max - min ) / 2;
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000338 midi = KERN_INDEX( mid->index1, mid->index2 );
Werner Lembergbd5ae402000-07-05 04:32:02 +0000339
Werner Lemberg0d9165e2002-03-07 21:59:59 +0000340 if ( midi == idx )
David Turner95bec282000-02-15 12:55:57 +0000341 {
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000342 kerning->x = mid->x;
343 kerning->y = mid->y;
344
David Turner95bec282000-02-15 12:55:57 +0000345 return;
346 }
David Turnere49ab252000-05-16 23:44:38 +0000347
Werner Lemberg0d9165e2002-03-07 21:59:59 +0000348 if ( midi < idx )
Werner Lembergbd5ae402000-07-05 04:32:02 +0000349 min = mid + 1;
350 else
351 max = mid - 1;
David Turner95bec282000-02-15 12:55:57 +0000352 }
Werner Lembergbd5ae402000-07-05 04:32:02 +0000353
David Turner95bec282000-02-15 12:55:57 +0000354 kerning->x = 0;
355 kerning->y = 0;
356 }
357
Werner Lembergbd5ae402000-07-05 04:32:02 +0000358
Wu, Chia-I (吳佳一)108fdbb2006-01-16 15:35:56 +0000359 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
Werner Lembergbd5ae402000-07-05 04:32:02 +0000397/* END */