1 /**************************************************************************** 2 * 3 * t1afm.c 4 * 5 * AFM support for Type 1 fonts (body). 6 * 7 * Copyright 1996-2018 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 trace_t1afm 37 38 39 FT_LOCAL_DEF( void ) T1_Done_Metrics(FT_Memory memory,AFM_FontInfo fi)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 t1_get_index(const char * name,FT_Offset len,void * user_data)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 ) compare_kern_pairs(const void * a,const void * b)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 T1_Read_PFM(FT_Face t1_face,FT_Stream stream,AFM_FontInfo fi)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 ) T1_Read_Metrics(FT_Face t1_face,FT_Stream stream)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 ) T1_Get_Kerning(AFM_FontInfo fi,FT_UInt glyph1,FT_UInt glyph2,FT_Vector * kerning)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 ) T1_Get_Track_Kerning(FT_Face face,FT_Fixed ptsize,FT_Int degree,FT_Fixed * kerning)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