1 /**************************************************************************** 2 * 3 * ftmac.c 4 * 5 * Mac FOND support. Written by just@letterror.com. 6 * Heavily modified by mpsuzuki, George Williams, and Sean McBride. 7 * 8 * This file is for Mac OS X only; see builds/mac/ftoldmac.c for 9 * classic platforms built by MPW. 10 * 11 * Copyright 1996-2018 by 12 * Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. 13 * 14 * This file is part of the FreeType project, and may only be used, 15 * modified, and distributed under the terms of the FreeType project 16 * license, LICENSE.TXT. By continuing to use, modify, or distribute 17 * this file you indicate that you have read the license and 18 * understand and accept it fully. 19 * 20 */ 21 22 23 /* 24 Notes 25 26 Mac suitcase files can (and often do!) contain multiple fonts. To 27 support this I use the face_index argument of FT_(Open|New)_Face() 28 functions, and pretend the suitcase file is a collection. 29 30 Warning: fbit and NFNT bitmap resources are not supported yet. In old 31 sfnt fonts, bitmap glyph data for each size is stored in each `NFNT' 32 resources instead of the `bdat' table in the sfnt resource. Therefore, 33 face->num_fixed_sizes is set to 0, because bitmap data in `NFNT' 34 resource is unavailable at present. 35 36 The Mac FOND support works roughly like this: 37 38 - Check whether the offered stream points to a Mac suitcase file. This 39 is done by checking the file type: it has to be 'FFIL' or 'tfil'. The 40 stream that gets passed to our init_face() routine is a stdio stream, 41 which isn't usable for us, since the FOND resources live in the 42 resource fork. So we just grab the stream->pathname field. 43 44 - Read the FOND resource into memory, then check whether there is a 45 TrueType font and/or(!) a Type 1 font available. 46 47 - If there is a Type 1 font available (as a separate `LWFN' file), read 48 its data into memory, massage it slightly so it becomes PFB data, wrap 49 it into a memory stream, load the Type 1 driver and delegate the rest 50 of the work to it by calling FT_Open_Face(). (XXX TODO: after this 51 has been done, the kerning data from the FOND resource should be 52 appended to the face: On the Mac there are usually no AFM files 53 available. However, this is tricky since we need to map Mac char 54 codes to ps glyph names to glyph ID's...) 55 56 - If there is a TrueType font (an `sfnt' resource), read it into memory, 57 wrap it into a memory stream, load the TrueType driver and delegate 58 the rest of the work to it, by calling FT_Open_Face(). 59 60 - Some suitcase fonts (notably Onyx) might point the `LWFN' file to 61 itself, even though it doesn't contains `POST' resources. To handle 62 this special case without opening the file an extra time, we just 63 ignore errors from the `LWFN' and fallback to the `sfnt' if both are 64 available. 65 */ 66 67 68 #include <ft2build.h> 69 #include FT_FREETYPE_H 70 #include FT_TRUETYPE_TAGS_H 71 #include FT_INTERNAL_STREAM_H 72 #include "ftbase.h" 73 74 75 #ifdef FT_MACINTOSH 76 77 /* This is for Mac OS X. Without redefinition, OS_INLINE */ 78 /* expands to `static inline' which doesn't survive the */ 79 /* -ansi compilation flag of GCC. */ 80 #if !HAVE_ANSI_OS_INLINE 81 #undef OS_INLINE 82 #define OS_INLINE static __inline__ 83 #endif 84 85 /* `configure' checks the availability of `ResourceIndex' strictly */ 86 /* and sets HAVE_TYPE_RESOURCE_INDEX 1 or 0 always. If it is */ 87 /* not set (e.g., a build without `configure'), the availability */ 88 /* is guessed from the SDK version. */ 89 #ifndef HAVE_TYPE_RESOURCE_INDEX 90 #if !defined( MAC_OS_X_VERSION_10_5 ) || \ 91 ( MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 ) 92 #define HAVE_TYPE_RESOURCE_INDEX 0 93 #else 94 #define HAVE_TYPE_RESOURCE_INDEX 1 95 #endif 96 #endif /* !HAVE_TYPE_RESOURCE_INDEX */ 97 98 #if ( HAVE_TYPE_RESOURCE_INDEX == 0 ) 99 typedef short ResourceIndex; 100 #endif 101 102 #include <CoreServices/CoreServices.h> 103 #include <ApplicationServices/ApplicationServices.h> 104 #include <sys/syslimits.h> /* PATH_MAX */ 105 106 /* Don't want warnings about our own use of deprecated functions. */ 107 #define FT_DEPRECATED_ATTRIBUTE 108 109 #include FT_MAC_H 110 111 #ifndef kATSOptionFlagsUnRestrictedScope /* since Mac OS X 10.1 */ 112 #define kATSOptionFlagsUnRestrictedScope kATSOptionFlagsDefault 113 #endif 114 115 116 /* Set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over 117 TrueType in case *both* are available (this is not common, 118 but it *is* possible). */ 119 #ifndef PREFER_LWFN 120 #define PREFER_LWFN 1 121 #endif 122 123 124 /* This function is deprecated because FSSpec is deprecated in Mac OS X */ 125 FT_EXPORT_DEF( FT_Error ) FT_GetFile_From_Mac_Name(const char * fontName,FSSpec * pathSpec,FT_Long * face_index)126 FT_GetFile_From_Mac_Name( const char* fontName, 127 FSSpec* pathSpec, 128 FT_Long* face_index ) 129 { 130 FT_UNUSED( fontName ); 131 FT_UNUSED( pathSpec ); 132 FT_UNUSED( face_index ); 133 134 return FT_THROW( Unimplemented_Feature ); 135 } 136 137 138 /* Private function. */ 139 /* The FSSpec type has been discouraged for a long time, */ 140 /* unfortunately an FSRef replacement API for */ 141 /* ATSFontGetFileSpecification() is only available in */ 142 /* Mac OS X 10.5 and later. */ 143 static OSStatus FT_ATSFontGetFileReference(ATSFontRef ats_font_id,FSRef * ats_font_ref)144 FT_ATSFontGetFileReference( ATSFontRef ats_font_id, 145 FSRef* ats_font_ref ) 146 { 147 #if defined( MAC_OS_X_VERSION_10_5 ) && \ 148 ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) 149 150 OSStatus err; 151 152 err = ATSFontGetFileReference( ats_font_id, ats_font_ref ); 153 154 return err; 155 #elif __LP64__ /* No 64bit Carbon API on legacy platforms */ 156 FT_UNUSED( ats_font_id ); 157 FT_UNUSED( ats_font_ref ); 158 159 160 return fnfErr; 161 #else /* 32bit Carbon API on legacy platforms */ 162 OSStatus err; 163 FSSpec spec; 164 165 166 err = ATSFontGetFileSpecification( ats_font_id, &spec ); 167 if ( noErr == err ) 168 err = FSpMakeFSRef( &spec, ats_font_ref ); 169 170 return err; 171 #endif 172 } 173 174 175 static FT_Error FT_GetFileRef_From_Mac_ATS_Name(const char * fontName,FSRef * ats_font_ref,FT_Long * face_index)176 FT_GetFileRef_From_Mac_ATS_Name( const char* fontName, 177 FSRef* ats_font_ref, 178 FT_Long* face_index ) 179 { 180 CFStringRef cf_fontName; 181 ATSFontRef ats_font_id; 182 183 184 *face_index = 0; 185 186 cf_fontName = CFStringCreateWithCString( NULL, fontName, 187 kCFStringEncodingMacRoman ); 188 ats_font_id = ATSFontFindFromName( cf_fontName, 189 kATSOptionFlagsUnRestrictedScope ); 190 CFRelease( cf_fontName ); 191 192 if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL ) 193 return FT_THROW( Unknown_File_Format ); 194 195 if ( noErr != FT_ATSFontGetFileReference( ats_font_id, ats_font_ref ) ) 196 return FT_THROW( Unknown_File_Format ); 197 198 /* face_index calculation by searching preceding fontIDs */ 199 /* with same FSRef */ 200 { 201 ATSFontRef id2 = ats_font_id - 1; 202 FSRef ref2; 203 204 205 while ( id2 > 0 ) 206 { 207 if ( noErr != FT_ATSFontGetFileReference( id2, &ref2 ) ) 208 break; 209 if ( noErr != FSCompareFSRefs( ats_font_ref, &ref2 ) ) 210 break; 211 212 id2 --; 213 } 214 *face_index = ats_font_id - ( id2 + 1 ); 215 } 216 217 return FT_Err_Ok; 218 } 219 220 221 FT_EXPORT_DEF( FT_Error ) FT_GetFilePath_From_Mac_ATS_Name(const char * fontName,UInt8 * path,UInt32 maxPathSize,FT_Long * face_index)222 FT_GetFilePath_From_Mac_ATS_Name( const char* fontName, 223 UInt8* path, 224 UInt32 maxPathSize, 225 FT_Long* face_index ) 226 { 227 FSRef ref; 228 FT_Error err; 229 230 231 if ( !fontName || !face_index ) 232 return FT_THROW( Invalid_Argument); 233 234 err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index ); 235 if ( err ) 236 return err; 237 238 if ( noErr != FSRefMakePath( &ref, path, maxPathSize ) ) 239 return FT_THROW( Unknown_File_Format ); 240 241 return FT_Err_Ok; 242 } 243 244 245 /* This function is deprecated because FSSpec is deprecated in Mac OS X */ 246 FT_EXPORT_DEF( FT_Error ) FT_GetFile_From_Mac_ATS_Name(const char * fontName,FSSpec * pathSpec,FT_Long * face_index)247 FT_GetFile_From_Mac_ATS_Name( const char* fontName, 248 FSSpec* pathSpec, 249 FT_Long* face_index ) 250 { 251 #if ( __LP64__ ) || ( defined( MAC_OS_X_VERSION_10_5 ) && \ 252 ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) ) 253 FT_UNUSED( fontName ); 254 FT_UNUSED( pathSpec ); 255 FT_UNUSED( face_index ); 256 257 return FT_THROW( Unimplemented_Feature ); 258 #else 259 FSRef ref; 260 FT_Error err; 261 262 263 if ( !fontName || !face_index ) 264 return FT_THROW( Invalid_Argument ); 265 266 err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index ); 267 if ( err ) 268 return err; 269 270 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, NULL, NULL, 271 pathSpec, NULL ) ) 272 return FT_THROW( Unknown_File_Format ); 273 274 return FT_Err_Ok; 275 #endif 276 } 277 278 279 static OSErr FT_FSPathMakeRes(const UInt8 * pathname,ResFileRefNum * res)280 FT_FSPathMakeRes( const UInt8* pathname, 281 ResFileRefNum* res ) 282 { 283 OSErr err; 284 FSRef ref; 285 286 287 if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) ) 288 return FT_THROW( Cannot_Open_Resource ); 289 290 /* at present, no support for dfont format */ 291 err = FSOpenResourceFile( &ref, 0, NULL, fsRdPerm, res ); 292 if ( noErr == err ) 293 return err; 294 295 /* fallback to original resource-fork font */ 296 *res = FSOpenResFile( &ref, fsRdPerm ); 297 err = ResError(); 298 299 return err; 300 } 301 302 303 /* Return the file type for given pathname */ 304 static OSType get_file_type_from_path(const UInt8 * pathname)305 get_file_type_from_path( const UInt8* pathname ) 306 { 307 FSRef ref; 308 FSCatalogInfo info; 309 310 311 if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) ) 312 return ( OSType ) 0; 313 314 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoFinderInfo, &info, 315 NULL, NULL, NULL ) ) 316 return ( OSType ) 0; 317 318 return ((FInfo *)(info.finderInfo))->fdType; 319 } 320 321 322 /* Given a PostScript font name, create the Macintosh LWFN file name. */ 323 static void create_lwfn_name(char * ps_name,Str255 lwfn_file_name)324 create_lwfn_name( char* ps_name, 325 Str255 lwfn_file_name ) 326 { 327 int max = 5, count = 0; 328 FT_Byte* p = lwfn_file_name; 329 FT_Byte* q = (FT_Byte*)ps_name; 330 331 332 lwfn_file_name[0] = 0; 333 334 while ( *q ) 335 { 336 if ( ft_isupper( *q ) ) 337 { 338 if ( count ) 339 max = 3; 340 count = 0; 341 } 342 if ( count < max && ( ft_isalnum( *q ) || *q == '_' ) ) 343 { 344 *++p = *q; 345 lwfn_file_name[0]++; 346 count++; 347 } 348 q++; 349 } 350 } 351 352 353 static short count_faces_sfnt(char * fond_data)354 count_faces_sfnt( char* fond_data ) 355 { 356 /* The count is 1 greater than the value in the FOND. */ 357 /* Isn't that cute? :-) */ 358 359 return EndianS16_BtoN( *( (short*)( fond_data + 360 sizeof ( FamRec ) ) ) ) + 1; 361 } 362 363 364 static short count_faces_scalable(char * fond_data)365 count_faces_scalable( char* fond_data ) 366 { 367 AsscEntry* assoc; 368 short i, face, face_all; 369 370 371 face_all = EndianS16_BtoN( *( (short *)( fond_data + 372 sizeof ( FamRec ) ) ) ) + 1; 373 assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 ); 374 face = 0; 375 376 for ( i = 0; i < face_all; i++ ) 377 { 378 if ( 0 == EndianS16_BtoN( assoc[i].fontSize ) ) 379 face++; 380 } 381 return face; 382 } 383 384 385 /* Look inside the FOND data, answer whether there should be an SFNT 386 resource, and answer the name of a possible LWFN Type 1 file. 387 388 Thanks to Paul Miller (paulm@profoundeffects.com) for the fix 389 to load a face OTHER than the first one in the FOND! 390 */ 391 392 393 static void parse_fond(char * fond_data,short * have_sfnt,ResID * sfnt_id,Str255 lwfn_file_name,short face_index)394 parse_fond( char* fond_data, 395 short* have_sfnt, 396 ResID* sfnt_id, 397 Str255 lwfn_file_name, 398 short face_index ) 399 { 400 AsscEntry* assoc; 401 AsscEntry* base_assoc; 402 FamRec* fond; 403 404 405 *sfnt_id = 0; 406 *have_sfnt = 0; 407 lwfn_file_name[0] = 0; 408 409 fond = (FamRec*)fond_data; 410 assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 ); 411 base_assoc = assoc; 412 413 /* the maximum faces in a FOND is 48, size of StyleTable.indexes[] */ 414 if ( 47 < face_index ) 415 return; 416 417 /* Let's do a little range checking before we get too excited here */ 418 if ( face_index < count_faces_sfnt( fond_data ) ) 419 { 420 assoc += face_index; /* add on the face_index! */ 421 422 /* if the face at this index is not scalable, 423 fall back to the first one (old behavior) */ 424 if ( EndianS16_BtoN( assoc->fontSize ) == 0 ) 425 { 426 *have_sfnt = 1; 427 *sfnt_id = EndianS16_BtoN( assoc->fontID ); 428 } 429 else if ( base_assoc->fontSize == 0 ) 430 { 431 *have_sfnt = 1; 432 *sfnt_id = EndianS16_BtoN( base_assoc->fontID ); 433 } 434 } 435 436 if ( EndianS32_BtoN( fond->ffStylOff ) ) 437 { 438 unsigned char* p = (unsigned char*)fond_data; 439 StyleTable* style; 440 unsigned short string_count; 441 char ps_name[256]; 442 unsigned char* names[64]; 443 int i; 444 445 446 p += EndianS32_BtoN( fond->ffStylOff ); 447 style = (StyleTable*)p; 448 p += sizeof ( StyleTable ); 449 string_count = EndianS16_BtoN( *(short*)(p) ); 450 string_count = FT_MIN( 64, string_count ); 451 p += sizeof ( short ); 452 453 for ( i = 0; i < string_count; i++ ) 454 { 455 names[i] = p; 456 p += names[i][0]; 457 p++; 458 } 459 460 { 461 size_t ps_name_len = (size_t)names[0][0]; 462 463 464 if ( ps_name_len != 0 ) 465 { 466 ft_memcpy(ps_name, names[0] + 1, ps_name_len); 467 ps_name[ps_name_len] = 0; 468 } 469 if ( style->indexes[face_index] > 1 && 470 style->indexes[face_index] <= string_count ) 471 { 472 unsigned char* suffixes = names[style->indexes[face_index] - 1]; 473 474 475 for ( i = 1; i <= suffixes[0]; i++ ) 476 { 477 unsigned char* s; 478 size_t j = suffixes[i] - 1; 479 480 481 if ( j < string_count && ( s = names[j] ) != NULL ) 482 { 483 size_t s_len = (size_t)s[0]; 484 485 486 if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) ) 487 { 488 ft_memcpy( ps_name + ps_name_len, s + 1, s_len ); 489 ps_name_len += s_len; 490 ps_name[ps_name_len] = 0; 491 } 492 } 493 } 494 } 495 } 496 497 create_lwfn_name( ps_name, lwfn_file_name ); 498 } 499 } 500 501 502 static FT_Error lookup_lwfn_by_fond(const UInt8 * path_fond,ConstStr255Param base_lwfn,UInt8 * path_lwfn,size_t path_size)503 lookup_lwfn_by_fond( const UInt8* path_fond, 504 ConstStr255Param base_lwfn, 505 UInt8* path_lwfn, 506 size_t path_size ) 507 { 508 FSRef ref, par_ref; 509 size_t dirname_len; 510 511 512 /* Pathname for FSRef can be in various formats: HFS, HFS+, and POSIX. */ 513 /* We should not extract parent directory by string manipulation. */ 514 515 if ( noErr != FSPathMakeRef( path_fond, &ref, FALSE ) ) 516 return FT_THROW( Invalid_Argument ); 517 518 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, 519 NULL, NULL, NULL, &par_ref ) ) 520 return FT_THROW( Invalid_Argument ); 521 522 if ( noErr != FSRefMakePath( &par_ref, path_lwfn, path_size ) ) 523 return FT_THROW( Invalid_Argument ); 524 525 if ( ft_strlen( (char *)path_lwfn ) + 1 + base_lwfn[0] > path_size ) 526 return FT_THROW( Invalid_Argument ); 527 528 /* now we have absolute dirname in path_lwfn */ 529 ft_strcat( (char *)path_lwfn, "/" ); 530 dirname_len = ft_strlen( (char *)path_lwfn ); 531 ft_strcat( (char *)path_lwfn, (char *)base_lwfn + 1 ); 532 path_lwfn[dirname_len + base_lwfn[0]] = '\0'; 533 534 if ( noErr != FSPathMakeRef( path_lwfn, &ref, FALSE ) ) 535 return FT_THROW( Cannot_Open_Resource ); 536 537 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, 538 NULL, NULL, NULL, NULL ) ) 539 return FT_THROW( Cannot_Open_Resource ); 540 541 return FT_Err_Ok; 542 } 543 544 545 static short count_faces(Handle fond,const UInt8 * pathname)546 count_faces( Handle fond, 547 const UInt8* pathname ) 548 { 549 ResID sfnt_id; 550 short have_sfnt, have_lwfn; 551 Str255 lwfn_file_name; 552 UInt8 buff[PATH_MAX]; 553 FT_Error err; 554 short num_faces; 555 556 557 have_sfnt = have_lwfn = 0; 558 559 parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 ); 560 561 if ( lwfn_file_name[0] ) 562 { 563 err = lookup_lwfn_by_fond( pathname, lwfn_file_name, 564 buff, sizeof ( buff ) ); 565 if ( !err ) 566 have_lwfn = 1; 567 } 568 569 if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) ) 570 num_faces = 1; 571 else 572 num_faces = count_faces_scalable( *fond ); 573 574 return num_faces; 575 } 576 577 578 /* Read Type 1 data from the POST resources inside the LWFN file, 579 return a PFB buffer. This is somewhat convoluted because the FT2 580 PFB parser wants the ASCII header as one chunk, and the LWFN 581 chunks are often not organized that way, so we glue chunks 582 of the same type together. */ 583 static FT_Error read_lwfn(FT_Memory memory,ResFileRefNum res,FT_Byte ** pfb_data,FT_ULong * size)584 read_lwfn( FT_Memory memory, 585 ResFileRefNum res, 586 FT_Byte** pfb_data, 587 FT_ULong* size ) 588 { 589 FT_Error error = FT_Err_Ok; 590 ResID res_id; 591 unsigned char *buffer, *p, *size_p = NULL; 592 FT_ULong total_size = 0; 593 FT_ULong old_total_size = 0; 594 FT_ULong post_size, pfb_chunk_size; 595 Handle post_data; 596 char code, last_code; 597 598 599 UseResFile( res ); 600 601 /* First pass: load all POST resources, and determine the size of */ 602 /* the output buffer. */ 603 res_id = 501; 604 last_code = -1; 605 606 for (;;) 607 { 608 post_data = Get1Resource( TTAG_POST, res_id++ ); 609 if ( !post_data ) 610 break; /* we are done */ 611 612 code = (*post_data)[0]; 613 614 if ( code != last_code ) 615 { 616 if ( code == 5 ) 617 total_size += 2; /* just the end code */ 618 else 619 total_size += 6; /* code + 4 bytes chunk length */ 620 } 621 622 total_size += (FT_ULong)GetHandleSize( post_data ) - 2; 623 last_code = code; 624 625 /* detect resource fork overflow */ 626 if ( FT_MAC_RFORK_MAX_LEN < total_size ) 627 { 628 error = FT_THROW( Array_Too_Large ); 629 goto Error; 630 } 631 632 old_total_size = total_size; 633 } 634 635 if ( FT_ALLOC( buffer, (FT_Long)total_size ) ) 636 goto Error; 637 638 /* Second pass: append all POST data to the buffer, add PFB fields. */ 639 /* Glue all consecutive chunks of the same type together. */ 640 p = buffer; 641 res_id = 501; 642 last_code = -1; 643 pfb_chunk_size = 0; 644 645 for (;;) 646 { 647 post_data = Get1Resource( TTAG_POST, res_id++ ); 648 if ( !post_data ) 649 break; /* we are done */ 650 651 post_size = (FT_ULong)GetHandleSize( post_data ) - 2; 652 code = (*post_data)[0]; 653 654 if ( code != last_code ) 655 { 656 if ( last_code != -1 ) 657 { 658 /* we are done adding a chunk, fill in the size field */ 659 if ( size_p ) 660 { 661 *size_p++ = (FT_Byte)( pfb_chunk_size & 0xFF ); 662 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8 ) & 0xFF ); 663 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF ); 664 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF ); 665 } 666 pfb_chunk_size = 0; 667 } 668 669 *p++ = 0x80; 670 if ( code == 5 ) 671 *p++ = 0x03; /* the end */ 672 else if ( code == 2 ) 673 *p++ = 0x02; /* binary segment */ 674 else 675 *p++ = 0x01; /* ASCII segment */ 676 677 if ( code != 5 ) 678 { 679 size_p = p; /* save for later */ 680 p += 4; /* make space for size field */ 681 } 682 } 683 684 ft_memcpy( p, *post_data + 2, post_size ); 685 pfb_chunk_size += post_size; 686 p += post_size; 687 last_code = code; 688 } 689 690 *pfb_data = buffer; 691 *size = total_size; 692 693 Error: 694 CloseResFile( res ); 695 return error; 696 } 697 698 699 /* Create a new FT_Face from a file path to an LWFN file. */ 700 static FT_Error FT_New_Face_From_LWFN(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)701 FT_New_Face_From_LWFN( FT_Library library, 702 const UInt8* pathname, 703 FT_Long face_index, 704 FT_Face* aface ) 705 { 706 FT_Byte* pfb_data; 707 FT_ULong pfb_size; 708 FT_Error error; 709 ResFileRefNum res; 710 711 712 if ( noErr != FT_FSPathMakeRes( pathname, &res ) ) 713 return FT_THROW( Cannot_Open_Resource ); 714 715 pfb_data = NULL; 716 pfb_size = 0; 717 error = read_lwfn( library->memory, res, &pfb_data, &pfb_size ); 718 CloseResFile( res ); /* PFB is already loaded, useless anymore */ 719 if ( error ) 720 return error; 721 722 return open_face_from_buffer( library, 723 pfb_data, 724 pfb_size, 725 face_index, 726 "type1", 727 aface ); 728 } 729 730 731 /* Create a new FT_Face from an SFNT resource, specified by res ID. */ 732 static FT_Error FT_New_Face_From_SFNT(FT_Library library,ResID sfnt_id,FT_Long face_index,FT_Face * aface)733 FT_New_Face_From_SFNT( FT_Library library, 734 ResID sfnt_id, 735 FT_Long face_index, 736 FT_Face* aface ) 737 { 738 Handle sfnt = NULL; 739 FT_Byte* sfnt_data; 740 size_t sfnt_size; 741 FT_Error error = FT_Err_Ok; 742 FT_Memory memory = library->memory; 743 int is_cff, is_sfnt_ps; 744 745 746 sfnt = GetResource( TTAG_sfnt, sfnt_id ); 747 if ( !sfnt ) 748 return FT_THROW( Invalid_Handle ); 749 750 sfnt_size = (FT_ULong)GetHandleSize( sfnt ); 751 752 /* detect resource fork overflow */ 753 if ( FT_MAC_RFORK_MAX_LEN < sfnt_size ) 754 return FT_THROW( Array_Too_Large ); 755 756 if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) ) 757 { 758 ReleaseResource( sfnt ); 759 return error; 760 } 761 762 ft_memcpy( sfnt_data, *sfnt, sfnt_size ); 763 ReleaseResource( sfnt ); 764 765 is_cff = sfnt_size > 4 && !ft_memcmp( sfnt_data, "OTTO", 4 ); 766 is_sfnt_ps = sfnt_size > 4 && !ft_memcmp( sfnt_data, "typ1", 4 ); 767 768 if ( is_sfnt_ps ) 769 { 770 FT_Stream stream; 771 772 773 if ( FT_NEW( stream ) ) 774 goto Try_OpenType; 775 776 FT_Stream_OpenMemory( stream, sfnt_data, sfnt_size ); 777 if ( !open_face_PS_from_sfnt_stream( library, 778 stream, 779 face_index, 780 0, NULL, 781 aface ) ) 782 { 783 FT_Stream_Close( stream ); 784 FT_FREE( stream ); 785 FT_FREE( sfnt_data ); 786 goto Exit; 787 } 788 789 FT_FREE( stream ); 790 } 791 Try_OpenType: 792 error = open_face_from_buffer( library, 793 sfnt_data, 794 sfnt_size, 795 face_index, 796 is_cff ? "cff" : "truetype", 797 aface ); 798 Exit: 799 return error; 800 } 801 802 803 /* Create a new FT_Face from a file path to a suitcase file. */ 804 static FT_Error FT_New_Face_From_Suitcase(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)805 FT_New_Face_From_Suitcase( FT_Library library, 806 const UInt8* pathname, 807 FT_Long face_index, 808 FT_Face* aface ) 809 { 810 FT_Error error = FT_ERR( Cannot_Open_Resource ); 811 ResFileRefNum res_ref; 812 ResourceIndex res_index; 813 Handle fond; 814 short num_faces_in_res; 815 816 817 if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) ) 818 return FT_THROW( Cannot_Open_Resource ); 819 820 UseResFile( res_ref ); 821 if ( ResError() ) 822 return FT_THROW( Cannot_Open_Resource ); 823 824 num_faces_in_res = 0; 825 for ( res_index = 1; ; res_index++ ) 826 { 827 short num_faces_in_fond; 828 829 830 fond = Get1IndResource( TTAG_FOND, res_index ); 831 if ( ResError() ) 832 break; 833 834 num_faces_in_fond = count_faces( fond, pathname ); 835 num_faces_in_res += num_faces_in_fond; 836 837 if ( 0 <= face_index && face_index < num_faces_in_fond && error ) 838 error = FT_New_Face_From_FOND( library, fond, face_index, aface ); 839 840 face_index -= num_faces_in_fond; 841 } 842 843 CloseResFile( res_ref ); 844 if ( !error && aface && *aface ) 845 (*aface)->num_faces = num_faces_in_res; 846 return error; 847 } 848 849 850 /* documentation is in ftmac.h */ 851 852 FT_EXPORT_DEF( FT_Error ) FT_New_Face_From_FOND(FT_Library library,Handle fond,FT_Long face_index,FT_Face * aface)853 FT_New_Face_From_FOND( FT_Library library, 854 Handle fond, 855 FT_Long face_index, 856 FT_Face* aface ) 857 { 858 short have_sfnt, have_lwfn = 0; 859 ResID sfnt_id, fond_id; 860 OSType fond_type; 861 Str255 fond_name; 862 Str255 lwfn_file_name; 863 UInt8 path_lwfn[PATH_MAX]; 864 OSErr err; 865 FT_Error error = FT_Err_Ok; 866 867 868 /* check of `library' and `aface' delayed to `FT_New_Face_From_XXX' */ 869 870 GetResInfo( fond, &fond_id, &fond_type, fond_name ); 871 if ( ResError() != noErr || fond_type != TTAG_FOND ) 872 return FT_THROW( Invalid_File_Format ); 873 874 parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index ); 875 876 if ( lwfn_file_name[0] ) 877 { 878 ResFileRefNum res; 879 880 881 res = HomeResFile( fond ); 882 if ( noErr != ResError() ) 883 goto found_no_lwfn_file; 884 885 { 886 UInt8 path_fond[PATH_MAX]; 887 FSRef ref; 888 889 890 err = FSGetForkCBInfo( res, kFSInvalidVolumeRefNum, 891 NULL, NULL, NULL, &ref, NULL ); 892 if ( noErr != err ) 893 goto found_no_lwfn_file; 894 895 err = FSRefMakePath( &ref, path_fond, sizeof ( path_fond ) ); 896 if ( noErr != err ) 897 goto found_no_lwfn_file; 898 899 error = lookup_lwfn_by_fond( path_fond, lwfn_file_name, 900 path_lwfn, sizeof ( path_lwfn ) ); 901 if ( !error ) 902 have_lwfn = 1; 903 } 904 } 905 906 if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) ) 907 error = FT_New_Face_From_LWFN( library, 908 path_lwfn, 909 face_index, 910 aface ); 911 else 912 error = FT_THROW( Unknown_File_Format ); 913 914 found_no_lwfn_file: 915 if ( have_sfnt && error ) 916 error = FT_New_Face_From_SFNT( library, 917 sfnt_id, 918 face_index, 919 aface ); 920 921 return error; 922 } 923 924 925 /* Common function to load a new FT_Face from a resource file. */ 926 static FT_Error FT_New_Face_From_Resource(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)927 FT_New_Face_From_Resource( FT_Library library, 928 const UInt8* pathname, 929 FT_Long face_index, 930 FT_Face* aface ) 931 { 932 OSType file_type; 933 FT_Error error; 934 935 936 /* LWFN is a (very) specific file format, check for it explicitly */ 937 file_type = get_file_type_from_path( pathname ); 938 if ( file_type == TTAG_LWFN ) 939 return FT_New_Face_From_LWFN( library, pathname, face_index, aface ); 940 941 /* Otherwise the file type doesn't matter (there are more than */ 942 /* `FFIL' and `tfil'). Just try opening it as a font suitcase; */ 943 /* if it works, fine. */ 944 945 error = FT_New_Face_From_Suitcase( library, pathname, face_index, aface ); 946 if ( error ) 947 { 948 /* let it fall through to normal loader (.ttf, .otf, etc.); */ 949 /* we signal this by returning no error and no FT_Face */ 950 *aface = NULL; 951 } 952 953 return FT_Err_Ok; 954 } 955 956 957 /************************************************************************** 958 * 959 * @Function: 960 * FT_New_Face 961 * 962 * @Description: 963 * This is the Mac-specific implementation of FT_New_Face. In 964 * addition to the standard FT_New_Face() functionality, it also 965 * accepts pathnames to Mac suitcase files. For further 966 * documentation see the original FT_New_Face() in freetype.h. 967 */ 968 FT_EXPORT_DEF( FT_Error ) FT_New_Face(FT_Library library,const char * pathname,FT_Long face_index,FT_Face * aface)969 FT_New_Face( FT_Library library, 970 const char* pathname, 971 FT_Long face_index, 972 FT_Face* aface ) 973 { 974 FT_Open_Args args; 975 FT_Error error; 976 977 978 /* test for valid `library' and `aface' delayed to FT_Open_Face() */ 979 if ( !pathname ) 980 return FT_THROW( Invalid_Argument ); 981 982 *aface = NULL; 983 984 /* try resourcefork based font: LWFN, FFIL */ 985 error = FT_New_Face_From_Resource( library, (UInt8 *)pathname, 986 face_index, aface ); 987 if ( error || *aface ) 988 return error; 989 990 /* let it fall through to normal loader (.ttf, .otf, etc.) */ 991 args.flags = FT_OPEN_PATHNAME; 992 args.pathname = (char*)pathname; 993 994 return FT_Open_Face( library, &args, face_index, aface ); 995 } 996 997 998 /************************************************************************** 999 * 1000 * @Function: 1001 * FT_New_Face_From_FSRef 1002 * 1003 * @Description: 1004 * FT_New_Face_From_FSRef is identical to FT_New_Face except it 1005 * accepts an FSRef instead of a path. 1006 * 1007 * This function is deprecated because Carbon data types (FSRef) 1008 * are not cross-platform, and thus not suitable for the FreeType API. 1009 */ 1010 FT_EXPORT_DEF( FT_Error ) FT_New_Face_From_FSRef(FT_Library library,const FSRef * ref,FT_Long face_index,FT_Face * aface)1011 FT_New_Face_From_FSRef( FT_Library library, 1012 const FSRef* ref, 1013 FT_Long face_index, 1014 FT_Face* aface ) 1015 { 1016 FT_Error error; 1017 FT_Open_Args args; 1018 1019 OSErr err; 1020 UInt8 pathname[PATH_MAX]; 1021 1022 1023 /* check of `library' and `aface' delayed to */ 1024 /* `FT_New_Face_From_Resource' */ 1025 1026 if ( !ref ) 1027 return FT_THROW( Invalid_Argument ); 1028 1029 err = FSRefMakePath( ref, pathname, sizeof ( pathname ) ); 1030 if ( err ) 1031 error = FT_THROW( Cannot_Open_Resource ); 1032 1033 error = FT_New_Face_From_Resource( library, pathname, face_index, aface ); 1034 if ( error || *aface ) 1035 return error; 1036 1037 /* fallback to datafork font */ 1038 args.flags = FT_OPEN_PATHNAME; 1039 args.pathname = (char*)pathname; 1040 return FT_Open_Face( library, &args, face_index, aface ); 1041 } 1042 1043 1044 /************************************************************************** 1045 * 1046 * @Function: 1047 * FT_New_Face_From_FSSpec 1048 * 1049 * @Description: 1050 * FT_New_Face_From_FSSpec is identical to FT_New_Face except it 1051 * accepts an FSSpec instead of a path. 1052 * 1053 * This function is deprecated because FSSpec is deprecated in Mac OS X 1054 */ 1055 FT_EXPORT_DEF( FT_Error ) FT_New_Face_From_FSSpec(FT_Library library,const FSSpec * spec,FT_Long face_index,FT_Face * aface)1056 FT_New_Face_From_FSSpec( FT_Library library, 1057 const FSSpec* spec, 1058 FT_Long face_index, 1059 FT_Face* aface ) 1060 { 1061 #if ( __LP64__ ) || ( defined( MAC_OS_X_VERSION_10_5 ) && \ 1062 ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) ) 1063 FT_UNUSED( library ); 1064 FT_UNUSED( spec ); 1065 FT_UNUSED( face_index ); 1066 FT_UNUSED( aface ); 1067 1068 return FT_THROW( Unimplemented_Feature ); 1069 #else 1070 FSRef ref; 1071 1072 1073 /* check of `library' and `aface' delayed to `FT_New_Face_From_FSRef' */ 1074 1075 if ( !spec || FSpMakeFSRef( spec, &ref ) != noErr ) 1076 return FT_THROW( Invalid_Argument ); 1077 else 1078 return FT_New_Face_From_FSRef( library, &ref, face_index, aface ); 1079 #endif 1080 } 1081 1082 #else /* !FT_MACINTOSH */ 1083 1084 /* ANSI C doesn't like empty source files */ 1085 typedef int _ft_mac_dummy; 1086 1087 #endif /* !FT_MACINTOSH */ 1088 1089 1090 /* END */ 1091