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 FamRec* fond; 362 short i, face, face_all; 363 364 365 fond = (FamRec*)fond_data; 366 face_all = EndianS16_BtoN( *( (short *)( fond_data + 367 sizeof ( FamRec ) ) ) ) + 1; 368 assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 ); 369 face = 0; 370 371 for ( i = 0; i < face_all; i++ ) 372 { 373 if ( 0 == EndianS16_BtoN( assoc[i].fontSize ) ) 374 face++; 375 } 376 return face; 377 } 378 379 380 /* Look inside the FOND data, answer whether there should be an SFNT 381 resource, and answer the name of a possible LWFN Type 1 file. 382 383 Thanks to Paul Miller (paulm@profoundeffects.com) for the fix 384 to load a face OTHER than the first one in the FOND! 385 */ 386 387 388 static void parse_fond(char * fond_data,short * have_sfnt,ResID * sfnt_id,Str255 lwfn_file_name,short face_index)389 parse_fond( char* fond_data, 390 short* have_sfnt, 391 ResID* sfnt_id, 392 Str255 lwfn_file_name, 393 short face_index ) 394 { 395 AsscEntry* assoc; 396 AsscEntry* base_assoc; 397 FamRec* fond; 398 399 400 *sfnt_id = 0; 401 *have_sfnt = 0; 402 lwfn_file_name[0] = 0; 403 404 fond = (FamRec*)fond_data; 405 assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 ); 406 base_assoc = assoc; 407 408 /* the maximum faces in a FOND is 48, size of StyleTable.indexes[] */ 409 if ( 47 < face_index ) 410 return; 411 412 /* Let's do a little range checking before we get too excited here */ 413 if ( face_index < count_faces_sfnt( fond_data ) ) 414 { 415 assoc += face_index; /* add on the face_index! */ 416 417 /* if the face at this index is not scalable, 418 fall back to the first one (old behavior) */ 419 if ( EndianS16_BtoN( assoc->fontSize ) == 0 ) 420 { 421 *have_sfnt = 1; 422 *sfnt_id = EndianS16_BtoN( assoc->fontID ); 423 } 424 else if ( base_assoc->fontSize == 0 ) 425 { 426 *have_sfnt = 1; 427 *sfnt_id = EndianS16_BtoN( base_assoc->fontID ); 428 } 429 } 430 431 if ( EndianS32_BtoN( fond->ffStylOff ) ) 432 { 433 unsigned char* p = (unsigned char*)fond_data; 434 StyleTable* style; 435 unsigned short string_count; 436 char ps_name[256]; 437 unsigned char* names[64]; 438 int i; 439 440 441 p += EndianS32_BtoN( fond->ffStylOff ); 442 style = (StyleTable*)p; 443 p += sizeof ( StyleTable ); 444 string_count = EndianS16_BtoN( *(short*)(p) ); 445 p += sizeof ( short ); 446 447 for ( i = 0; i < string_count && i < 64; i++ ) 448 { 449 names[i] = p; 450 p += names[i][0]; 451 p++; 452 } 453 454 { 455 size_t ps_name_len = (size_t)names[0][0]; 456 457 458 if ( ps_name_len != 0 ) 459 { 460 ft_memcpy(ps_name, names[0] + 1, ps_name_len); 461 ps_name[ps_name_len] = 0; 462 } 463 if ( style->indexes[face_index] > 1 && 464 style->indexes[face_index] <= FT_MIN( string_count, 64 ) ) 465 { 466 unsigned char* suffixes = names[style->indexes[face_index] - 1]; 467 468 469 for ( i = 1; i <= suffixes[0]; i++ ) 470 { 471 unsigned char* s; 472 size_t j = suffixes[i] - 1; 473 474 475 if ( j < string_count && ( s = names[j] ) != NULL ) 476 { 477 size_t s_len = (size_t)s[0]; 478 479 480 if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) ) 481 { 482 ft_memcpy( ps_name + ps_name_len, s + 1, s_len ); 483 ps_name_len += s_len; 484 ps_name[ps_name_len] = 0; 485 } 486 } 487 } 488 } 489 } 490 491 create_lwfn_name( ps_name, lwfn_file_name ); 492 } 493 } 494 495 496 static FT_Error lookup_lwfn_by_fond(const UInt8 * path_fond,ConstStr255Param base_lwfn,UInt8 * path_lwfn,size_t path_size)497 lookup_lwfn_by_fond( const UInt8* path_fond, 498 ConstStr255Param base_lwfn, 499 UInt8* path_lwfn, 500 size_t path_size ) 501 { 502 FSRef ref, par_ref; 503 size_t dirname_len; 504 505 506 /* Pathname for FSRef can be in various formats: HFS, HFS+, and POSIX. */ 507 /* We should not extract parent directory by string manipulation. */ 508 509 if ( noErr != FSPathMakeRef( path_fond, &ref, FALSE ) ) 510 return FT_THROW( Invalid_Argument ); 511 512 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, 513 NULL, NULL, NULL, &par_ref ) ) 514 return FT_THROW( Invalid_Argument ); 515 516 if ( noErr != FSRefMakePath( &par_ref, path_lwfn, path_size ) ) 517 return FT_THROW( Invalid_Argument ); 518 519 if ( ft_strlen( (char *)path_lwfn ) + 1 + base_lwfn[0] > path_size ) 520 return FT_THROW( Invalid_Argument ); 521 522 /* now we have absolute dirname in path_lwfn */ 523 ft_strcat( (char *)path_lwfn, "/" ); 524 dirname_len = ft_strlen( (char *)path_lwfn ); 525 ft_strcat( (char *)path_lwfn, (char *)base_lwfn + 1 ); 526 path_lwfn[dirname_len + base_lwfn[0]] = '\0'; 527 528 if ( noErr != FSPathMakeRef( path_lwfn, &ref, FALSE ) ) 529 return FT_THROW( Cannot_Open_Resource ); 530 531 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, 532 NULL, NULL, NULL, NULL ) ) 533 return FT_THROW( Cannot_Open_Resource ); 534 535 return FT_Err_Ok; 536 } 537 538 539 static short count_faces(Handle fond,const UInt8 * pathname)540 count_faces( Handle fond, 541 const UInt8* pathname ) 542 { 543 ResID sfnt_id; 544 short have_sfnt, have_lwfn; 545 Str255 lwfn_file_name; 546 UInt8 buff[PATH_MAX]; 547 FT_Error err; 548 short num_faces; 549 550 551 have_sfnt = have_lwfn = 0; 552 553 parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 ); 554 555 if ( lwfn_file_name[0] ) 556 { 557 err = lookup_lwfn_by_fond( pathname, lwfn_file_name, 558 buff, sizeof ( buff ) ); 559 if ( !err ) 560 have_lwfn = 1; 561 } 562 563 if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) ) 564 num_faces = 1; 565 else 566 num_faces = count_faces_scalable( *fond ); 567 568 return num_faces; 569 } 570 571 572 /* Read Type 1 data from the POST resources inside the LWFN file, 573 return a PFB buffer. This is somewhat convoluted because the FT2 574 PFB parser wants the ASCII header as one chunk, and the LWFN 575 chunks are often not organized that way, so we glue chunks 576 of the same type together. */ 577 static FT_Error read_lwfn(FT_Memory memory,ResFileRefNum res,FT_Byte ** pfb_data,FT_ULong * size)578 read_lwfn( FT_Memory memory, 579 ResFileRefNum res, 580 FT_Byte** pfb_data, 581 FT_ULong* size ) 582 { 583 FT_Error error = FT_Err_Ok; 584 ResID res_id; 585 unsigned char *buffer, *p, *size_p = NULL; 586 FT_ULong total_size = 0; 587 FT_ULong old_total_size = 0; 588 FT_ULong post_size, pfb_chunk_size; 589 Handle post_data; 590 char code, last_code; 591 592 593 UseResFile( res ); 594 595 /* First pass: load all POST resources, and determine the size of */ 596 /* the output buffer. */ 597 res_id = 501; 598 last_code = -1; 599 600 for (;;) 601 { 602 post_data = Get1Resource( TTAG_POST, res_id++ ); 603 if ( post_data == NULL ) 604 break; /* we are done */ 605 606 code = (*post_data)[0]; 607 608 if ( code != last_code ) 609 { 610 if ( code == 5 ) 611 total_size += 2; /* just the end code */ 612 else 613 total_size += 6; /* code + 4 bytes chunk length */ 614 } 615 616 total_size += GetHandleSize( post_data ) - 2; 617 last_code = code; 618 619 /* detect integer overflows */ 620 if ( total_size < old_total_size ) 621 { 622 error = FT_THROW( Array_Too_Large ); 623 goto Error; 624 } 625 626 old_total_size = total_size; 627 } 628 629 if ( FT_ALLOC( buffer, (FT_Long)total_size ) ) 630 goto Error; 631 632 /* Second pass: append all POST data to the buffer, add PFB fields. */ 633 /* Glue all consecutive chunks of the same type together. */ 634 p = buffer; 635 res_id = 501; 636 last_code = -1; 637 pfb_chunk_size = 0; 638 639 for (;;) 640 { 641 post_data = Get1Resource( TTAG_POST, res_id++ ); 642 if ( post_data == NULL ) 643 break; /* we are done */ 644 645 post_size = (FT_ULong)GetHandleSize( post_data ) - 2; 646 code = (*post_data)[0]; 647 648 if ( code != last_code ) 649 { 650 if ( last_code != -1 ) 651 { 652 /* we are done adding a chunk, fill in the size field */ 653 if ( size_p != NULL ) 654 { 655 *size_p++ = (FT_Byte)( pfb_chunk_size & 0xFF ); 656 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8 ) & 0xFF ); 657 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF ); 658 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF ); 659 } 660 pfb_chunk_size = 0; 661 } 662 663 *p++ = 0x80; 664 if ( code == 5 ) 665 *p++ = 0x03; /* the end */ 666 else if ( code == 2 ) 667 *p++ = 0x02; /* binary segment */ 668 else 669 *p++ = 0x01; /* ASCII segment */ 670 671 if ( code != 5 ) 672 { 673 size_p = p; /* save for later */ 674 p += 4; /* make space for size field */ 675 } 676 } 677 678 ft_memcpy( p, *post_data + 2, post_size ); 679 pfb_chunk_size += post_size; 680 p += post_size; 681 last_code = code; 682 } 683 684 *pfb_data = buffer; 685 *size = total_size; 686 687 Error: 688 CloseResFile( res ); 689 return error; 690 } 691 692 693 /* Create a new FT_Face from a file path to an LWFN file. */ 694 static FT_Error FT_New_Face_From_LWFN(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)695 FT_New_Face_From_LWFN( FT_Library library, 696 const UInt8* pathname, 697 FT_Long face_index, 698 FT_Face* aface ) 699 { 700 FT_Byte* pfb_data; 701 FT_ULong pfb_size; 702 FT_Error error; 703 ResFileRefNum res; 704 705 706 if ( noErr != FT_FSPathMakeRes( pathname, &res ) ) 707 return FT_THROW( Cannot_Open_Resource ); 708 709 pfb_data = NULL; 710 pfb_size = 0; 711 error = read_lwfn( library->memory, res, &pfb_data, &pfb_size ); 712 CloseResFile( res ); /* PFB is already loaded, useless anymore */ 713 if ( error ) 714 return error; 715 716 return open_face_from_buffer( library, 717 pfb_data, 718 pfb_size, 719 face_index, 720 "type1", 721 aface ); 722 } 723 724 725 /* Create a new FT_Face from an SFNT resource, specified by res ID. */ 726 static FT_Error FT_New_Face_From_SFNT(FT_Library library,ResID sfnt_id,FT_Long face_index,FT_Face * aface)727 FT_New_Face_From_SFNT( FT_Library library, 728 ResID sfnt_id, 729 FT_Long face_index, 730 FT_Face* aface ) 731 { 732 Handle sfnt = NULL; 733 FT_Byte* sfnt_data; 734 size_t sfnt_size; 735 FT_Error error = FT_Err_Ok; 736 FT_Memory memory = library->memory; 737 int is_cff, is_sfnt_ps; 738 739 740 sfnt = GetResource( TTAG_sfnt, sfnt_id ); 741 if ( sfnt == NULL ) 742 return FT_THROW( Invalid_Handle ); 743 744 sfnt_size = (FT_ULong)GetHandleSize( sfnt ); 745 if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) ) 746 { 747 ReleaseResource( sfnt ); 748 return error; 749 } 750 751 ft_memcpy( sfnt_data, *sfnt, sfnt_size ); 752 ReleaseResource( sfnt ); 753 754 is_cff = sfnt_size > 4 && !ft_memcmp( sfnt_data, "OTTO", 4 ); 755 is_sfnt_ps = sfnt_size > 4 && !ft_memcmp( sfnt_data, "typ1", 4 ); 756 757 if ( is_sfnt_ps ) 758 { 759 FT_Stream stream; 760 761 762 if ( FT_NEW( stream ) ) 763 goto Try_OpenType; 764 765 FT_Stream_OpenMemory( stream, sfnt_data, sfnt_size ); 766 if ( !open_face_PS_from_sfnt_stream( library, 767 stream, 768 face_index, 769 0, NULL, 770 aface ) ) 771 { 772 FT_Stream_Close( stream ); 773 FT_FREE( stream ); 774 FT_FREE( sfnt_data ); 775 goto Exit; 776 } 777 778 FT_FREE( stream ); 779 } 780 Try_OpenType: 781 error = open_face_from_buffer( library, 782 sfnt_data, 783 sfnt_size, 784 face_index, 785 is_cff ? "cff" : "truetype", 786 aface ); 787 Exit: 788 return error; 789 } 790 791 792 /* Create a new FT_Face from a file path to a suitcase file. */ 793 static FT_Error FT_New_Face_From_Suitcase(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)794 FT_New_Face_From_Suitcase( FT_Library library, 795 const UInt8* pathname, 796 FT_Long face_index, 797 FT_Face* aface ) 798 { 799 FT_Error error = FT_ERR( Cannot_Open_Resource ); 800 ResFileRefNum res_ref; 801 ResourceIndex res_index; 802 Handle fond; 803 short num_faces_in_res, num_faces_in_fond; 804 805 806 if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) ) 807 return FT_THROW( Cannot_Open_Resource ); 808 809 UseResFile( res_ref ); 810 if ( ResError() ) 811 return FT_THROW( Cannot_Open_Resource ); 812 813 num_faces_in_res = 0; 814 for ( res_index = 1; ; ++res_index ) 815 { 816 fond = Get1IndResource( TTAG_FOND, res_index ); 817 if ( ResError() ) 818 break; 819 820 num_faces_in_fond = count_faces( fond, pathname ); 821 num_faces_in_res += num_faces_in_fond; 822 823 if ( 0 <= face_index && face_index < num_faces_in_fond && error ) 824 error = FT_New_Face_From_FOND( library, fond, face_index, aface ); 825 826 face_index -= num_faces_in_fond; 827 } 828 829 CloseResFile( res_ref ); 830 if ( !error && aface && *aface ) 831 (*aface)->num_faces = num_faces_in_res; 832 return error; 833 } 834 835 836 /* documentation is in ftmac.h */ 837 838 FT_EXPORT_DEF( FT_Error ) FT_New_Face_From_FOND(FT_Library library,Handle fond,FT_Long face_index,FT_Face * aface)839 FT_New_Face_From_FOND( FT_Library library, 840 Handle fond, 841 FT_Long face_index, 842 FT_Face* aface ) 843 { 844 short have_sfnt, have_lwfn = 0; 845 ResID sfnt_id, fond_id; 846 OSType fond_type; 847 Str255 fond_name; 848 Str255 lwfn_file_name; 849 UInt8 path_lwfn[PATH_MAX]; 850 OSErr err; 851 FT_Error error = FT_Err_Ok; 852 853 854 GetResInfo( fond, &fond_id, &fond_type, fond_name ); 855 if ( ResError() != noErr || fond_type != TTAG_FOND ) 856 return FT_THROW( Invalid_File_Format ); 857 858 parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index ); 859 860 if ( lwfn_file_name[0] ) 861 { 862 ResFileRefNum res; 863 864 865 res = HomeResFile( fond ); 866 if ( noErr != ResError() ) 867 goto found_no_lwfn_file; 868 869 { 870 UInt8 path_fond[PATH_MAX]; 871 FSRef ref; 872 873 874 err = FSGetForkCBInfo( res, kFSInvalidVolumeRefNum, 875 NULL, NULL, NULL, &ref, NULL ); 876 if ( noErr != err ) 877 goto found_no_lwfn_file; 878 879 err = FSRefMakePath( &ref, path_fond, sizeof ( path_fond ) ); 880 if ( noErr != err ) 881 goto found_no_lwfn_file; 882 883 error = lookup_lwfn_by_fond( path_fond, lwfn_file_name, 884 path_lwfn, sizeof ( path_lwfn ) ); 885 if ( !error ) 886 have_lwfn = 1; 887 } 888 } 889 890 if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) ) 891 error = FT_New_Face_From_LWFN( library, 892 path_lwfn, 893 face_index, 894 aface ); 895 else 896 error = FT_THROW( Unknown_File_Format ); 897 898 found_no_lwfn_file: 899 if ( have_sfnt && error ) 900 error = FT_New_Face_From_SFNT( library, 901 sfnt_id, 902 face_index, 903 aface ); 904 905 return error; 906 } 907 908 909 /* Common function to load a new FT_Face from a resource file. */ 910 static FT_Error FT_New_Face_From_Resource(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)911 FT_New_Face_From_Resource( FT_Library library, 912 const UInt8* pathname, 913 FT_Long face_index, 914 FT_Face* aface ) 915 { 916 OSType file_type; 917 FT_Error error; 918 919 920 /* LWFN is a (very) specific file format, check for it explicitly */ 921 file_type = get_file_type_from_path( pathname ); 922 if ( file_type == TTAG_LWFN ) 923 return FT_New_Face_From_LWFN( library, pathname, face_index, aface ); 924 925 /* Otherwise the file type doesn't matter (there are more than */ 926 /* `FFIL' and `tfil'). Just try opening it as a font suitcase; */ 927 /* if it works, fine. */ 928 929 error = FT_New_Face_From_Suitcase( library, pathname, face_index, aface ); 930 if ( error == 0 ) 931 return error; 932 933 /* let it fall through to normal loader (.ttf, .otf, etc.); */ 934 /* we signal this by returning no error and no FT_Face */ 935 *aface = NULL; 936 return 0; 937 } 938 939 940 /*************************************************************************/ 941 /* */ 942 /* <Function> */ 943 /* FT_New_Face */ 944 /* */ 945 /* <Description> */ 946 /* This is the Mac-specific implementation of FT_New_Face. In */ 947 /* addition to the standard FT_New_Face() functionality, it also */ 948 /* accepts pathnames to Mac suitcase files. For further */ 949 /* documentation see the original FT_New_Face() in freetype.h. */ 950 /* */ 951 FT_EXPORT_DEF( FT_Error ) FT_New_Face(FT_Library library,const char * pathname,FT_Long face_index,FT_Face * aface)952 FT_New_Face( FT_Library library, 953 const char* pathname, 954 FT_Long face_index, 955 FT_Face* aface ) 956 { 957 FT_Open_Args args; 958 FT_Error error; 959 960 961 /* test for valid `library' and `aface' delayed to FT_Open_Face() */ 962 if ( !pathname ) 963 return FT_THROW( Invalid_Argument ); 964 965 error = FT_Err_Ok; 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