1 /***************************************************************************/ 2 /* */ 3 /* t1afm.c */ 4 /* */ 5 /* AFM support for Type 1 fonts (body). */ 6 /* */ 7 /* Copyright 1996-2011, 2013 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 /*************************************************************************/ 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 ) T1_Done_Metrics(FT_Memory memory,AFM_FontInfo fi)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 t1_get_index(const char * name,FT_Offset len,void * user_data)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 ) compare_kern_pairs(const void * a,const void * b)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 T1_Read_PFM(FT_Face t1_face,FT_Stream stream,AFM_FontInfo fi)107 T1_Read_PFM( FT_Face t1_face, 108 FT_Stream stream, 109 AFM_FontInfo fi ) 110 { 111 FT_Error error = FT_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 126 /* Figure out how long the width table is. */ 127 /* This info is a little-endian short at offset 99. */ 128 p = start + 99; 129 if ( p + 2 > limit ) 130 { 131 error = FT_THROW( Unknown_File_Format ); 132 goto Exit; 133 } 134 width_table_length = FT_PEEK_USHORT_LE( p ); 135 136 p += 18 + width_table_length; 137 if ( p + 0x12 > limit || FT_PEEK_USHORT_LE( p ) < 0x12 ) 138 /* extension table is probably optional */ 139 goto Exit; 140 141 /* Kerning offset is 14 bytes from start of extensions table. */ 142 p += 14; 143 p = start + FT_PEEK_ULONG_LE( p ); 144 145 if ( p == start ) 146 /* zero offset means no table */ 147 goto Exit; 148 149 if ( p + 2 > limit ) 150 { 151 error = FT_THROW( Unknown_File_Format ); 152 goto Exit; 153 } 154 155 fi->NumKernPair = FT_PEEK_USHORT_LE( p ); 156 p += 2; 157 if ( p + 4 * fi->NumKernPair > limit ) 158 { 159 error = FT_THROW( Unknown_File_Format ); 160 goto Exit; 161 } 162 163 /* Actually, kerning pairs are simply optional! */ 164 if ( fi->NumKernPair == 0 ) 165 goto Exit; 166 167 /* allocate the pairs */ 168 if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) ) 169 goto Exit; 170 171 /* now, read each kern pair */ 172 kp = fi->KernPairs; 173 limit = p + 4 * fi->NumKernPair; 174 175 /* PFM kerning data are stored by encoding rather than glyph index, */ 176 /* so find the PostScript charmap of this font and install it */ 177 /* temporarily. If we find no PostScript charmap, then just use */ 178 /* the default and hope it is the right one. */ 179 oldcharmap = t1_face->charmap; 180 charmap = NULL; 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 error = FT_Set_Charmap( t1_face, charmap ); 189 if ( error ) 190 goto Exit; 191 break; 192 } 193 } 194 195 /* Kerning info is stored as: */ 196 /* */ 197 /* encoding of first glyph (1 byte) */ 198 /* encoding of second glyph (1 byte) */ 199 /* offset (little-endian short) */ 200 for ( ; p < limit ; p += 4 ) 201 { 202 kp->index1 = FT_Get_Char_Index( t1_face, p[0] ); 203 kp->index2 = FT_Get_Char_Index( t1_face, p[1] ); 204 205 kp->x = (FT_Int)FT_PEEK_SHORT_LE(p + 2); 206 kp->y = 0; 207 208 kp++; 209 } 210 211 if ( oldcharmap != NULL ) 212 error = FT_Set_Charmap( t1_face, oldcharmap ); 213 if ( error ) 214 goto Exit; 215 216 /* now, sort the kern pairs according to their glyph indices */ 217 ft_qsort( fi->KernPairs, fi->NumKernPair, sizeof ( AFM_KernPairRec ), 218 compare_kern_pairs ); 219 220 Exit: 221 if ( error ) 222 { 223 FT_FREE( fi->KernPairs ); 224 fi->NumKernPair = 0; 225 } 226 227 return error; 228 } 229 230 231 /* parse a metrics file -- either AFM or PFM depending on what */ 232 /* it turns out to be */ 233 FT_LOCAL_DEF( FT_Error ) T1_Read_Metrics(FT_Face t1_face,FT_Stream stream)234 T1_Read_Metrics( FT_Face t1_face, 235 FT_Stream stream ) 236 { 237 PSAux_Service psaux; 238 FT_Memory memory = stream->memory; 239 AFM_ParserRec parser; 240 AFM_FontInfo fi = NULL; 241 FT_Error error = FT_ERR( Unknown_File_Format ); 242 T1_Font t1_font = &( (T1_Face)t1_face )->type1; 243 244 245 if ( FT_NEW( fi ) || 246 FT_FRAME_ENTER( stream->size ) ) 247 goto Exit; 248 249 fi->FontBBox = t1_font->font_bbox; 250 fi->Ascender = t1_font->font_bbox.yMax; 251 fi->Descender = t1_font->font_bbox.yMin; 252 253 psaux = (PSAux_Service)( (T1_Face)t1_face )->psaux; 254 if ( psaux->afm_parser_funcs ) 255 { 256 error = psaux->afm_parser_funcs->init( &parser, 257 stream->memory, 258 stream->cursor, 259 stream->limit ); 260 261 if ( !error ) 262 { 263 parser.FontInfo = fi; 264 parser.get_index = t1_get_index; 265 parser.user_data = t1_font; 266 267 error = psaux->afm_parser_funcs->parse( &parser ); 268 psaux->afm_parser_funcs->done( &parser ); 269 } 270 } 271 272 if ( FT_ERR_EQ( error, Unknown_File_Format ) ) 273 { 274 FT_Byte* start = stream->cursor; 275 276 277 /* MS Windows allows versions up to 0x3FF without complaining */ 278 if ( stream->size > 6 && 279 start[1] < 4 && 280 FT_PEEK_ULONG_LE( start + 2 ) == stream->size ) 281 error = T1_Read_PFM( t1_face, stream, fi ); 282 } 283 284 if ( !error ) 285 { 286 t1_font->font_bbox = fi->FontBBox; 287 288 t1_face->bbox.xMin = fi->FontBBox.xMin >> 16; 289 t1_face->bbox.yMin = fi->FontBBox.yMin >> 16; 290 /* no `U' suffix here to 0xFFFF! */ 291 t1_face->bbox.xMax = ( fi->FontBBox.xMax + 0xFFFF ) >> 16; 292 t1_face->bbox.yMax = ( fi->FontBBox.yMax + 0xFFFF ) >> 16; 293 294 /* no `U' suffix here to 0x8000! */ 295 t1_face->ascender = (FT_Short)( ( fi->Ascender + 0x8000 ) >> 16 ); 296 t1_face->descender = (FT_Short)( ( fi->Descender + 0x8000 ) >> 16 ); 297 298 if ( fi->NumKernPair ) 299 { 300 t1_face->face_flags |= FT_FACE_FLAG_KERNING; 301 ( (T1_Face)t1_face )->afm_data = fi; 302 fi = NULL; 303 } 304 } 305 306 FT_FRAME_EXIT(); 307 308 Exit: 309 if ( fi != NULL ) 310 T1_Done_Metrics( memory, fi ); 311 312 return error; 313 } 314 315 316 /* find the kerning for a given glyph pair */ 317 FT_LOCAL_DEF( void ) T1_Get_Kerning(AFM_FontInfo fi,FT_UInt glyph1,FT_UInt glyph2,FT_Vector * kerning)318 T1_Get_Kerning( AFM_FontInfo fi, 319 FT_UInt glyph1, 320 FT_UInt glyph2, 321 FT_Vector* kerning ) 322 { 323 AFM_KernPair min, mid, max; 324 FT_ULong idx = KERN_INDEX( glyph1, glyph2 ); 325 326 327 /* simple binary search */ 328 min = fi->KernPairs; 329 max = min + fi->NumKernPair - 1; 330 331 while ( min <= max ) 332 { 333 FT_ULong midi; 334 335 336 mid = min + ( max - min ) / 2; 337 midi = KERN_INDEX( mid->index1, mid->index2 ); 338 339 if ( midi == idx ) 340 { 341 kerning->x = mid->x; 342 kerning->y = mid->y; 343 344 return; 345 } 346 347 if ( midi < idx ) 348 min = mid + 1; 349 else 350 max = mid - 1; 351 } 352 353 kerning->x = 0; 354 kerning->y = 0; 355 } 356 357 358 FT_LOCAL_DEF( FT_Error ) T1_Get_Track_Kerning(FT_Face face,FT_Fixed ptsize,FT_Int degree,FT_Fixed * kerning)359 T1_Get_Track_Kerning( FT_Face face, 360 FT_Fixed ptsize, 361 FT_Int degree, 362 FT_Fixed* kerning ) 363 { 364 AFM_FontInfo fi = (AFM_FontInfo)( (T1_Face)face )->afm_data; 365 FT_Int i; 366 367 368 if ( !fi ) 369 return FT_THROW( Invalid_Argument ); 370 371 for ( i = 0; i < fi->NumTrackKern; i++ ) 372 { 373 AFM_TrackKern tk = fi->TrackKerns + i; 374 375 376 if ( tk->degree != degree ) 377 continue; 378 379 if ( ptsize < tk->min_ptsize ) 380 *kerning = tk->min_kern; 381 else if ( ptsize > tk->max_ptsize ) 382 *kerning = tk->max_kern; 383 else 384 { 385 *kerning = FT_MulDiv( ptsize - tk->min_ptsize, 386 tk->max_kern - tk->min_kern, 387 tk->max_ptsize - tk->min_ptsize ) + 388 tk->min_kern; 389 } 390 } 391 392 return FT_Err_Ok; 393 } 394 395 396 /* END */ 397