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