1 /***************************************************************************/ 2 /* */ 3 /* ftmac.c */ 4 /* */ 5 /* Mac FOND support. Written by just (at) letterror.com. */ 6 /* Heavily Fixed by mpsuzuki, George Williams and Sean McBride */ 7 /* */ 8 /* Copyright 1996-2018 by */ 9 /* Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. */ 10 /* */ 11 /* This file is part of the FreeType project, and may only be used, */ 12 /* modified, and distributed under the terms of the FreeType project */ 13 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ 14 /* this file you indicate that you have read the license and */ 15 /* understand and accept it fully. */ 16 /* */ 17 /***************************************************************************/ 18 19 20 /* 21 Notes 22 23 Mac suitcase files can (and often do!) contain multiple fonts. To 24 support this I use the face_index argument of FT_(Open|New)_Face() 25 functions, and pretend the suitcase file is a collection. 26 27 Warning: fbit and NFNT bitmap resources are not supported yet. In old 28 sfnt fonts, bitmap glyph data for each size is stored in each `NFNT' 29 resources instead of the `bdat' table in the sfnt resource. Therefore, 30 face->num_fixed_sizes is set to 0, because bitmap data in `NFNT' 31 resource is unavailable at present. 32 33 The Mac FOND support works roughly like this: 34 35 - Check whether the offered stream points to a Mac suitcase file. This 36 is done by checking the file type: it has to be 'FFIL' or 'tfil'. The 37 stream that gets passed to our init_face() routine is a stdio stream, 38 which isn't usable for us, since the FOND resources live in the 39 resource fork. So we just grab the stream->pathname field. 40 41 - Read the FOND resource into memory, then check whether there is a 42 TrueType font and/or(!) a Type 1 font available. 43 44 - If there is a Type 1 font available (as a separate `LWFN' file), read 45 its data into memory, massage it slightly so it becomes PFB data, wrap 46 it into a memory stream, load the Type 1 driver and delegate the rest 47 of the work to it by calling FT_Open_Face(). (XXX TODO: after this 48 has been done, the kerning data from the FOND resource should be 49 appended to the face: On the Mac there are usually no AFM files 50 available. However, this is tricky since we need to map Mac char 51 codes to ps glyph names to glyph ID's...) 52 53 - If there is a TrueType font (an `sfnt' resource), read it into memory, 54 wrap it into a memory stream, load the TrueType driver and delegate 55 the rest of the work to it, by calling FT_Open_Face(). 56 57 - Some suitcase fonts (notably Onyx) might point the `LWFN' file to 58 itself, even though it doesn't contains `POST' resources. To handle 59 this special case without opening the file an extra time, we just 60 ignore errors from the `LWFN' and fallback to the `sfnt' if both are 61 available. 62 */ 63 64 65 #include <ft2build.h> 66 #include FT_FREETYPE_H 67 #include FT_TRUETYPE_TAGS_H 68 #include FT_INTERNAL_STREAM_H 69 #include "ftbase.h" 70 71 #if defined( __GNUC__ ) || defined( __IBMC__ ) 72 /* This is for Mac OS X. Without redefinition, OS_INLINE */ 73 /* expands to `static inline' which doesn't survive the */ 74 /* -ansi compilation flag of GCC. */ 75 #if !HAVE_ANSI_OS_INLINE 76 #undef OS_INLINE 77 #define OS_INLINE static __inline__ 78 #endif 79 #include <CoreServices/CoreServices.h> 80 #include <ApplicationServices/ApplicationServices.h> 81 #include <sys/syslimits.h> /* PATH_MAX */ 82 #else 83 #include <Resources.h> 84 #include <Fonts.h> 85 #include <Endian.h> 86 #include <Errors.h> 87 #include <Files.h> 88 #include <TextUtils.h> 89 #endif 90 91 #ifndef PATH_MAX 92 #define PATH_MAX 1024 /* same with Mac OS X's syslimits.h */ 93 #endif 94 95 #if defined( __MWERKS__ ) && !TARGET_RT_MAC_MACHO 96 #include <FSp_fopen.h> 97 #endif 98 99 #define FT_DEPRECATED_ATTRIBUTE 100 101 #include FT_MAC_H 102 103 /* undefine blocking-macros in ftmac.h */ 104 #undef FT_GetFile_From_Mac_Name 105 #undef FT_GetFile_From_Mac_ATS_Name 106 #undef FT_New_Face_From_FOND 107 #undef FT_New_Face_From_FSSpec 108 #undef FT_New_Face_From_FSRef 109 110 111 /* FSSpec functions are deprecated since Mac OS X 10.4 */ 112 #ifndef HAVE_FSSPEC 113 #if TARGET_API_MAC_OS8 || TARGET_API_MAC_CARBON 114 #define HAVE_FSSPEC 1 115 #else 116 #define HAVE_FSSPEC 0 117 #endif 118 #endif 119 120 /* most FSRef functions were introduced since Mac OS 9 */ 121 #ifndef HAVE_FSREF 122 #if TARGET_API_MAC_OSX 123 #define HAVE_FSREF 1 124 #else 125 #define HAVE_FSREF 0 126 #endif 127 #endif 128 129 /* QuickDraw is deprecated since Mac OS X 10.4 */ 130 #ifndef HAVE_QUICKDRAW_CARBON 131 #if TARGET_API_MAC_OS8 || TARGET_API_MAC_CARBON 132 #define HAVE_QUICKDRAW_CARBON 1 133 #else 134 #define HAVE_QUICKDRAW_CARBON 0 135 #endif 136 #endif 137 138 /* AppleTypeService is available since Mac OS X */ 139 #ifndef HAVE_ATS 140 #if TARGET_API_MAC_OSX 141 #define HAVE_ATS 1 142 #ifndef kATSOptionFlagsUnRestrictedScope /* since Mac OS X 10.1 */ 143 #define kATSOptionFlagsUnRestrictedScope kATSOptionFlagsDefault 144 #endif 145 #else 146 #define HAVE_ATS 0 147 #endif 148 #endif 149 150 /* `configure' checks the availability of `ResourceIndex' strictly */ 151 /* and sets HAVE_TYPE_RESOURCE_INDEX to 1 or 0 always. If it is */ 152 /* not set (e.g., a build without `configure'), the availability */ 153 /* is guessed from the SDK version. */ 154 #ifndef HAVE_TYPE_RESOURCE_INDEX 155 #if !defined( MAC_OS_X_VERSION_10_5 ) || \ 156 ( MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 ) 157 #define HAVE_TYPE_RESOURCE_INDEX 0 158 #else 159 #define HAVE_TYPE_RESOURCE_INDEX 1 160 #endif 161 #endif /* !HAVE_TYPE_RESOURCE_INDEX */ 162 163 #if ( HAVE_TYPE_RESOURCE_INDEX == 0 ) 164 typedef short ResourceIndex; 165 #endif 166 167 /* Set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over 168 TrueType in case *both* are available (this is not common, 169 but it *is* possible). */ 170 #ifndef PREFER_LWFN 171 #define PREFER_LWFN 1 172 #endif 173 174 #ifdef FT_MACINTOSH 175 176 #if !HAVE_QUICKDRAW_CARBON /* QuickDraw is deprecated since Mac OS X 10.4 */ 177 178 FT_EXPORT_DEF( FT_Error ) 179 FT_GetFile_From_Mac_Name( const char* fontName, 180 FSSpec* pathSpec, 181 FT_Long* face_index ) 182 { 183 FT_UNUSED( fontName ); 184 FT_UNUSED( pathSpec ); 185 FT_UNUSED( face_index ); 186 187 return FT_THROW( Unimplemented_Feature ); 188 } 189 190 #else 191 192 FT_EXPORT_DEF( FT_Error ) 193 FT_GetFile_From_Mac_Name( const char* fontName, 194 FSSpec* pathSpec, 195 FT_Long* face_index ) 196 { 197 OptionBits options = kFMUseGlobalScopeOption; 198 199 FMFontFamilyIterator famIter; 200 OSStatus status = FMCreateFontFamilyIterator( NULL, NULL, 201 options, 202 &famIter ); 203 FMFont the_font = 0; 204 FMFontFamily family = 0; 205 206 207 if ( !fontName || !face_index ) 208 return FT_THROW( Invalid_Argument ); 209 210 *face_index = 0; 211 while ( status == 0 && !the_font ) 212 { 213 status = FMGetNextFontFamily( &famIter, &family ); 214 if ( status == 0 ) 215 { 216 int stat2; 217 FMFontFamilyInstanceIterator instIter; 218 Str255 famNameStr; 219 char famName[256]; 220 221 222 /* get the family name */ 223 FMGetFontFamilyName( family, famNameStr ); 224 CopyPascalStringToC( famNameStr, famName ); 225 226 /* iterate through the styles */ 227 FMCreateFontFamilyInstanceIterator( family, &instIter ); 228 229 *face_index = 0; 230 stat2 = 0; 231 232 while ( stat2 == 0 && !the_font ) 233 { 234 FMFontStyle style; 235 FMFontSize size; 236 FMFont font; 237 238 239 stat2 = FMGetNextFontFamilyInstance( &instIter, &font, 240 &style, &size ); 241 if ( stat2 == 0 && size == 0 ) 242 { 243 char fullName[256]; 244 245 246 /* build up a complete face name */ 247 ft_strcpy( fullName, famName ); 248 if ( style & bold ) 249 ft_strcat( fullName, " Bold" ); 250 if ( style & italic ) 251 ft_strcat( fullName, " Italic" ); 252 253 /* compare with the name we are looking for */ 254 if ( ft_strcmp( fullName, fontName ) == 0 ) 255 { 256 /* found it! */ 257 the_font = font; 258 } 259 else 260 ++(*face_index); 261 } 262 } 263 264 FMDisposeFontFamilyInstanceIterator( &instIter ); 265 } 266 } 267 268 FMDisposeFontFamilyIterator( &famIter ); 269 270 if ( the_font ) 271 { 272 FMGetFontContainer( the_font, pathSpec ); 273 return FT_Err_Ok; 274 } 275 else 276 return FT_THROW( Unknown_File_Format ); 277 } 278 279 #endif /* HAVE_QUICKDRAW_CARBON */ 280 281 282 #if HAVE_ATS 283 284 /* Private function. */ 285 /* The FSSpec type has been discouraged for a long time, */ 286 /* unfortunately an FSRef replacement API for */ 287 /* ATSFontGetFileSpecification() is only available in */ 288 /* Mac OS X 10.5 and later. */ 289 static OSStatus 290 FT_ATSFontGetFileReference( ATSFontRef ats_font_id, 291 FSRef* ats_font_ref ) 292 { 293 OSStatus err; 294 295 #if !defined( MAC_OS_X_VERSION_10_5 ) || \ 296 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 297 FSSpec spec; 298 299 300 err = ATSFontGetFileSpecification( ats_font_id, &spec ); 301 if ( noErr == err ) 302 err = FSpMakeFSRef( &spec, ats_font_ref ); 303 #else 304 err = ATSFontGetFileReference( ats_font_id, ats_font_ref ); 305 #endif 306 307 return err; 308 } 309 310 311 static FT_Error 312 FT_GetFileRef_From_Mac_ATS_Name( const char* fontName, 313 FSRef* ats_font_ref, 314 FT_Long* face_index ) 315 { 316 CFStringRef cf_fontName; 317 ATSFontRef ats_font_id; 318 319 320 *face_index = 0; 321 322 cf_fontName = CFStringCreateWithCString( NULL, fontName, 323 kCFStringEncodingMacRoman ); 324 ats_font_id = ATSFontFindFromName( cf_fontName, 325 kATSOptionFlagsUnRestrictedScope ); 326 CFRelease( cf_fontName ); 327 328 if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL ) 329 return FT_THROW( Unknown_File_Format ); 330 331 if ( noErr != FT_ATSFontGetFileReference( ats_font_id, ats_font_ref ) ) 332 return FT_THROW( Unknown_File_Format ); 333 334 /* face_index calculation by searching preceding fontIDs */ 335 /* with same FSRef */ 336 { 337 ATSFontRef id2 = ats_font_id - 1; 338 FSRef ref2; 339 340 341 while ( id2 > 0 ) 342 { 343 if ( noErr != FT_ATSFontGetFileReference( id2, &ref2 ) ) 344 break; 345 if ( noErr != FSCompareFSRefs( ats_font_ref, &ref2 ) ) 346 break; 347 348 id2--; 349 } 350 *face_index = ats_font_id - ( id2 + 1 ); 351 } 352 353 return FT_Err_Ok; 354 } 355 356 #endif 357 358 #if !HAVE_ATS 359 360 FT_EXPORT_DEF( FT_Error ) 361 FT_GetFilePath_From_Mac_ATS_Name( const char* fontName, 362 UInt8* path, 363 UInt32 maxPathSize, 364 FT_Long* face_index ) 365 { 366 FT_UNUSED( fontName ); 367 FT_UNUSED( path ); 368 FT_UNUSED( maxPathSize ); 369 FT_UNUSED( face_index ); 370 371 return FT_THROW( Unimplemented_Feature ); 372 } 373 374 #else 375 376 FT_EXPORT_DEF( FT_Error ) 377 FT_GetFilePath_From_Mac_ATS_Name( const char* fontName, 378 UInt8* path, 379 UInt32 maxPathSize, 380 FT_Long* face_index ) 381 { 382 FSRef ref; 383 FT_Error err; 384 385 386 err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index ); 387 if ( err ) 388 return err; 389 390 if ( noErr != FSRefMakePath( &ref, path, maxPathSize ) ) 391 return FT_THROW( Unknown_File_Format ); 392 393 return FT_Err_Ok; 394 } 395 396 #endif /* HAVE_ATS */ 397 398 399 #if !HAVE_FSSPEC || !HAVE_ATS 400 401 FT_EXPORT_DEF( FT_Error ) 402 FT_GetFile_From_Mac_ATS_Name( const char* fontName, 403 FSSpec* pathSpec, 404 FT_Long* face_index ) 405 { 406 FT_UNUSED( fontName ); 407 FT_UNUSED( pathSpec ); 408 FT_UNUSED( face_index ); 409 410 return FT_THROW( Unimplemented_Feature ); 411 } 412 413 #else 414 415 /* This function is deprecated because FSSpec is deprecated in Mac OS X. */ 416 FT_EXPORT_DEF( FT_Error ) 417 FT_GetFile_From_Mac_ATS_Name( const char* fontName, 418 FSSpec* pathSpec, 419 FT_Long* face_index ) 420 { 421 FSRef ref; 422 FT_Error err; 423 424 425 err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index ); 426 if ( err ) 427 return err; 428 429 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, NULL, NULL, 430 pathSpec, NULL ) ) 431 return FT_THROW( Unknown_File_Format ); 432 433 return FT_Err_Ok; 434 } 435 436 #endif 437 438 439 #if defined( __MWERKS__ ) && !TARGET_RT_MAC_MACHO 440 441 #define STREAM_FILE( stream ) ( (FT_FILE*)stream->descriptor.pointer ) 442 443 444 FT_CALLBACK_DEF( void ) 445 ft_FSp_stream_close( FT_Stream stream ) 446 { 447 ft_fclose( STREAM_FILE( stream ) ); 448 449 stream->descriptor.pointer = NULL; 450 stream->size = 0; 451 stream->base = 0; 452 } 453 454 455 FT_CALLBACK_DEF( unsigned long ) 456 ft_FSp_stream_io( FT_Stream stream, 457 unsigned long offset, 458 unsigned char* buffer, 459 unsigned long count ) 460 { 461 FT_FILE* file; 462 463 464 file = STREAM_FILE( stream ); 465 466 ft_fseek( file, offset, SEEK_SET ); 467 468 return (unsigned long)ft_fread( buffer, 1, count, file ); 469 } 470 471 #endif /* __MWERKS__ && !TARGET_RT_MAC_MACHO */ 472 473 474 #if HAVE_FSSPEC && !HAVE_FSREF 475 476 /* isDirectory is a dummy to synchronize API with FSPathMakeRef() */ 477 static OSErr 478 FT_FSPathMakeSpec( const UInt8* pathname, 479 FSSpec* spec_p, 480 Boolean isDirectory ) 481 { 482 const char *p, *q; 483 short vRefNum; 484 long dirID; 485 Str255 nodeName; 486 OSErr err; 487 FT_UNUSED( isDirectory ); 488 489 490 p = q = (const char *)pathname; 491 dirID = 0; 492 vRefNum = 0; 493 494 while ( 1 ) 495 { 496 int len = ft_strlen( p ); 497 498 499 if ( len > 255 ) 500 len = 255; 501 502 q = p + len; 503 504 if ( q == p ) 505 return 0; 506 507 if ( 255 < ft_strlen( (char *)pathname ) ) 508 { 509 while ( p < q && *q != ':' ) 510 q--; 511 } 512 513 if ( p < q ) 514 *(char *)nodeName = q - p; 515 else if ( ft_strlen( p ) < 256 ) 516 *(char *)nodeName = ft_strlen( p ); 517 else 518 return errFSNameTooLong; 519 520 ft_strncpy( (char *)nodeName + 1, (char *)p, *(char *)nodeName ); 521 err = FSMakeFSSpec( vRefNum, dirID, nodeName, spec_p ); 522 if ( err || '\0' == *q ) 523 return err; 524 525 vRefNum = spec_p->vRefNum; 526 dirID = spec_p->parID; 527 528 p = q; 529 } 530 } 531 532 533 static OSErr 534 FT_FSpMakePath( const FSSpec* spec_p, 535 UInt8* path, 536 UInt32 maxPathSize ) 537 { 538 OSErr err; 539 FSSpec spec = *spec_p; 540 short vRefNum; 541 long dirID; 542 Str255 parDir_name; 543 544 545 FT_MEM_SET( path, 0, maxPathSize ); 546 while ( 1 ) 547 { 548 int child_namelen = ft_strlen( (char *)path ); 549 unsigned char node_namelen = spec.name[0]; 550 unsigned char* node_name = spec.name + 1; 551 552 553 if ( node_namelen + child_namelen > maxPathSize ) 554 return errFSNameTooLong; 555 556 FT_MEM_MOVE( path + node_namelen + 1, path, child_namelen ); 557 FT_MEM_COPY( path, node_name, node_namelen ); 558 if ( child_namelen > 0 ) 559 path[node_namelen] = ':'; 560 561 vRefNum = spec.vRefNum; 562 dirID = spec.parID; 563 parDir_name[0] = '\0'; 564 err = FSMakeFSSpec( vRefNum, dirID, parDir_name, &spec ); 565 if ( noErr != err || dirID == spec.parID ) 566 break; 567 } 568 return noErr; 569 } 570 571 #endif /* HAVE_FSSPEC && !HAVE_FSREF */ 572 573 574 static OSErr 575 FT_FSPathMakeRes( const UInt8* pathname, 576 ResFileRefNum* res ) 577 { 578 579 #if HAVE_FSREF 580 581 OSErr err; 582 FSRef ref; 583 584 585 if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) ) 586 return FT_THROW( Cannot_Open_Resource ); 587 588 /* at present, no support for dfont format */ 589 err = FSOpenResourceFile( &ref, 0, NULL, fsRdPerm, res ); 590 if ( noErr == err ) 591 return err; 592 593 /* fallback to original resource-fork font */ 594 *res = FSOpenResFile( &ref, fsRdPerm ); 595 err = ResError(); 596 597 #else 598 599 OSErr err; 600 FSSpec spec; 601 602 603 if ( noErr != FT_FSPathMakeSpec( pathname, &spec, FALSE ) ) 604 return FT_THROW( Cannot_Open_Resource ); 605 606 /* at present, no support for dfont format without FSRef */ 607 /* (see above), try original resource-fork font */ 608 *res = FSpOpenResFile( &spec, fsRdPerm ); 609 err = ResError(); 610 611 #endif /* HAVE_FSREF */ 612 613 return err; 614 } 615 616 617 /* Return the file type for given pathname */ 618 static OSType 619 get_file_type_from_path( const UInt8* pathname ) 620 { 621 622 #if HAVE_FSREF 623 624 FSRef ref; 625 FSCatalogInfo info; 626 627 628 if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) ) 629 return ( OSType ) 0; 630 631 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoFinderInfo, &info, 632 NULL, NULL, NULL ) ) 633 return ( OSType ) 0; 634 635 return ((FInfo *)(info.finderInfo))->fdType; 636 637 #else 638 639 FSSpec spec; 640 FInfo finfo; 641 642 643 if ( noErr != FT_FSPathMakeSpec( pathname, &spec, FALSE ) ) 644 return ( OSType ) 0; 645 646 if ( noErr != FSpGetFInfo( &spec, &finfo ) ) 647 return ( OSType ) 0; 648 649 return finfo.fdType; 650 651 #endif /* HAVE_FSREF */ 652 653 } 654 655 656 /* Given a PostScript font name, create the Macintosh LWFN file name. */ 657 static void 658 create_lwfn_name( char* ps_name, 659 Str255 lwfn_file_name ) 660 { 661 int max = 5, count = 0; 662 FT_Byte* p = lwfn_file_name; 663 FT_Byte* q = (FT_Byte*)ps_name; 664 665 666 lwfn_file_name[0] = 0; 667 668 while ( *q ) 669 { 670 if ( ft_isupper( *q ) ) 671 { 672 if ( count ) 673 max = 3; 674 count = 0; 675 } 676 if ( count < max && ( ft_isalnum( *q ) || *q == '_' ) ) 677 { 678 *++p = *q; 679 lwfn_file_name[0]++; 680 count++; 681 } 682 q++; 683 } 684 } 685 686 687 static short 688 count_faces_sfnt( char* fond_data ) 689 { 690 /* The count is 1 greater than the value in the FOND. */ 691 /* Isn't that cute? :-) */ 692 693 return EndianS16_BtoN( *( (short*)( fond_data + 694 sizeof ( FamRec ) ) ) ) + 1; 695 } 696 697 698 static short 699 count_faces_scalable( char* fond_data ) 700 { 701 AsscEntry* assoc; 702 short i, face, face_all; 703 704 705 face_all = EndianS16_BtoN( *( (short *)( fond_data + 706 sizeof ( FamRec ) ) ) ) + 1; 707 assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 ); 708 face = 0; 709 710 for ( i = 0; i < face_all; i++ ) 711 { 712 if ( 0 == EndianS16_BtoN( assoc[i].fontSize ) ) 713 face++; 714 } 715 return face; 716 } 717 718 719 /* Look inside the FOND data, answer whether there should be an SFNT 720 resource, and answer the name of a possible LWFN Type 1 file. 721 722 Thanks to Paul Miller (paulm (at) profoundeffects.com) for the fix 723 to load a face OTHER than the first one in the FOND! 724 */ 725 726 static void 727 parse_fond( char* fond_data, 728 short* have_sfnt, 729 ResID* sfnt_id, 730 Str255 lwfn_file_name, 731 short face_index ) 732 { 733 AsscEntry* assoc; 734 AsscEntry* base_assoc; 735 FamRec* fond; 736 737 738 *sfnt_id = 0; 739 *have_sfnt = 0; 740 lwfn_file_name[0] = 0; 741 742 fond = (FamRec*)fond_data; 743 assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 ); 744 base_assoc = assoc; 745 746 /* the maximum faces in a FOND is 48, size of StyleTable.indexes[] */ 747 if ( 47 < face_index ) 748 return; 749 750 /* Let's do a little range checking before we get too excited here */ 751 if ( face_index < count_faces_sfnt( fond_data ) ) 752 { 753 assoc += face_index; /* add on the face_index! */ 754 755 /* if the face at this index is not scalable, 756 fall back to the first one (old behavior) */ 757 if ( EndianS16_BtoN( assoc->fontSize ) == 0 ) 758 { 759 *have_sfnt = 1; 760 *sfnt_id = EndianS16_BtoN( assoc->fontID ); 761 } 762 else if ( base_assoc->fontSize == 0 ) 763 { 764 *have_sfnt = 1; 765 *sfnt_id = EndianS16_BtoN( base_assoc->fontID ); 766 } 767 } 768 769 if ( EndianS32_BtoN( fond->ffStylOff ) ) 770 { 771 unsigned char* p = (unsigned char*)fond_data; 772 StyleTable* style; 773 unsigned short string_count; 774 char ps_name[256]; 775 unsigned char* names[64]; 776 int i; 777 778 779 p += EndianS32_BtoN( fond->ffStylOff ); 780 style = (StyleTable*)p; 781 p += sizeof ( StyleTable ); 782 string_count = EndianS16_BtoN( *(short*)(p) ); 783 p += sizeof ( short ); 784 785 for ( i = 0; i < string_count && i < 64; i++ ) 786 { 787 names[i] = p; 788 p += names[i][0]; 789 p++; 790 } 791 792 { 793 size_t ps_name_len = (size_t)names[0][0]; 794 795 796 if ( ps_name_len != 0 ) 797 { 798 ft_memcpy(ps_name, names[0] + 1, ps_name_len); 799 ps_name[ps_name_len] = 0; 800 } 801 if ( style->indexes[face_index] > 1 && 802 style->indexes[face_index] <= FT_MIN( string_count, 64 ) ) 803 { 804 unsigned char* suffixes = names[style->indexes[face_index] - 1]; 805 806 807 for ( i = 1; i <= suffixes[0]; i++ ) 808 { 809 unsigned char* s; 810 size_t j = suffixes[i] - 1; 811 812 813 if ( j < string_count && ( s = names[j] ) != NULL ) 814 { 815 size_t s_len = (size_t)s[0]; 816 817 818 if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) ) 819 { 820 ft_memcpy( ps_name + ps_name_len, s + 1, s_len ); 821 ps_name_len += s_len; 822 ps_name[ps_name_len] = 0; 823 } 824 } 825 } 826 } 827 } 828 829 create_lwfn_name( ps_name, lwfn_file_name ); 830 } 831 } 832 833 834 static FT_Error 835 lookup_lwfn_by_fond( const UInt8* path_fond, 836 ConstStr255Param base_lwfn, 837 UInt8* path_lwfn, 838 int path_size ) 839 { 840 841 #if HAVE_FSREF 842 843 FSRef ref, par_ref; 844 int dirname_len; 845 846 847 /* Pathname for FSRef can be in various formats: HFS, HFS+, and POSIX. */ 848 /* We should not extract parent directory by string manipulation. */ 849 850 if ( noErr != FSPathMakeRef( path_fond, &ref, FALSE ) ) 851 return FT_THROW( Invalid_Argument ); 852 853 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, 854 NULL, NULL, NULL, &par_ref ) ) 855 return FT_THROW( Invalid_Argument ); 856 857 if ( noErr != FSRefMakePath( &par_ref, path_lwfn, path_size ) ) 858 return FT_THROW( Invalid_Argument ); 859 860 if ( ft_strlen( (char *)path_lwfn ) + 1 + base_lwfn[0] > path_size ) 861 return FT_THROW( Invalid_Argument ); 862 863 /* now we have absolute dirname in path_lwfn */ 864 if ( path_lwfn[0] == '/' ) 865 ft_strcat( (char *)path_lwfn, "/" ); 866 else 867 ft_strcat( (char *)path_lwfn, ":" ); 868 869 dirname_len = ft_strlen( (char *)path_lwfn ); 870 ft_strcat( (char *)path_lwfn, (char *)base_lwfn + 1 ); 871 path_lwfn[dirname_len + base_lwfn[0]] = '\0'; 872 873 if ( noErr != FSPathMakeRef( path_lwfn, &ref, FALSE ) ) 874 return FT_THROW( Cannot_Open_Resource ); 875 876 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, 877 NULL, NULL, NULL, NULL ) ) 878 return FT_THROW( Cannot_Open_Resource ); 879 880 return FT_Err_Ok; 881 882 #else 883 884 int i; 885 FSSpec spec; 886 887 888 /* pathname for FSSpec is always HFS format */ 889 if ( ft_strlen( (char *)path_fond ) > path_size ) 890 return FT_THROW( Invalid_Argument ); 891 892 ft_strcpy( (char *)path_lwfn, (char *)path_fond ); 893 894 i = ft_strlen( (char *)path_lwfn ) - 1; 895 while ( i > 0 && ':' != path_lwfn[i] ) 896 i--; 897 898 if ( i + 1 + base_lwfn[0] > path_size ) 899 return FT_THROW( Invalid_Argument ); 900 901 if ( ':' == path_lwfn[i] ) 902 { 903 ft_strcpy( (char *)path_lwfn + i + 1, (char *)base_lwfn + 1 ); 904 path_lwfn[i + 1 + base_lwfn[0]] = '\0'; 905 } 906 else 907 { 908 ft_strcpy( (char *)path_lwfn, (char *)base_lwfn + 1 ); 909 path_lwfn[base_lwfn[0]] = '\0'; 910 } 911 912 if ( noErr != FT_FSPathMakeSpec( path_lwfn, &spec, FALSE ) ) 913 return FT_THROW( Cannot_Open_Resource ); 914 915 return FT_Err_Ok; 916 917 #endif /* HAVE_FSREF */ 918 919 } 920 921 922 static short 923 count_faces( Handle fond, 924 const UInt8* pathname ) 925 { 926 ResID sfnt_id; 927 short have_sfnt, have_lwfn; 928 Str255 lwfn_file_name; 929 UInt8 buff[PATH_MAX]; 930 FT_Error err; 931 short num_faces; 932 933 934 have_sfnt = have_lwfn = 0; 935 936 HLock( fond ); 937 parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 ); 938 939 if ( lwfn_file_name[0] ) 940 { 941 err = lookup_lwfn_by_fond( pathname, lwfn_file_name, 942 buff, sizeof ( buff ) ); 943 if ( !err ) 944 have_lwfn = 1; 945 } 946 947 if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) ) 948 num_faces = 1; 949 else 950 num_faces = count_faces_scalable( *fond ); 951 952 HUnlock( fond ); 953 return num_faces; 954 } 955 956 957 /* Read Type 1 data from the POST resources inside the LWFN file, 958 return a PFB buffer. This is somewhat convoluted because the FT2 959 PFB parser wants the ASCII header as one chunk, and the LWFN 960 chunks are often not organized that way, so we glue chunks 961 of the same type together. */ 962 static FT_Error 963 read_lwfn( FT_Memory memory, 964 ResFileRefNum res, 965 FT_Byte** pfb_data, 966 FT_ULong* size ) 967 { 968 FT_Error error = FT_Err_Ok; 969 ResID res_id; 970 unsigned char *buffer, *p, *size_p = NULL; 971 FT_ULong total_size = 0; 972 FT_ULong old_total_size = 0; 973 FT_ULong post_size, pfb_chunk_size; 974 Handle post_data; 975 char code, last_code; 976 977 978 UseResFile( res ); 979 980 /* First pass: load all POST resources, and determine the size of */ 981 /* the output buffer. */ 982 res_id = 501; 983 last_code = -1; 984 985 for (;;) 986 { 987 post_data = Get1Resource( TTAG_POST, res_id++ ); 988 if ( post_data == NULL ) 989 break; /* we are done */ 990 991 code = (*post_data)[0]; 992 993 if ( code != last_code ) 994 { 995 if ( code == 5 ) 996 total_size += 2; /* just the end code */ 997 else 998 total_size += 6; /* code + 4 bytes chunk length */ 999 } 1000 1001 total_size += GetHandleSize( post_data ) - 2; 1002 last_code = code; 1003 1004 /* detect integer overflows */ 1005 if ( total_size < old_total_size ) 1006 { 1007 error = FT_ERR( Array_Too_Large ); 1008 goto Error; 1009 } 1010 1011 old_total_size = total_size; 1012 } 1013 1014 if ( FT_ALLOC( buffer, (FT_Long)total_size ) ) 1015 goto Error; 1016 1017 /* Second pass: append all POST data to the buffer, add PFB fields. */ 1018 /* Glue all consecutive chunks of the same type together. */ 1019 p = buffer; 1020 res_id = 501; 1021 last_code = -1; 1022 pfb_chunk_size = 0; 1023 1024 for (;;) 1025 { 1026 post_data = Get1Resource( TTAG_POST, res_id++ ); 1027 if ( post_data == NULL ) 1028 break; /* we are done */ 1029 1030 post_size = (FT_ULong)GetHandleSize( post_data ) - 2; 1031 code = (*post_data)[0]; 1032 1033 if ( code != last_code ) 1034 { 1035 if ( last_code != -1 ) 1036 { 1037 /* we are done adding a chunk, fill in the size field */ 1038 if ( size_p != NULL ) 1039 { 1040 *size_p++ = (FT_Byte)( pfb_chunk_size & 0xFF ); 1041 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8 ) & 0xFF ); 1042 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF ); 1043 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF ); 1044 } 1045 pfb_chunk_size = 0; 1046 } 1047 1048 *p++ = 0x80; 1049 if ( code == 5 ) 1050 *p++ = 0x03; /* the end */ 1051 else if ( code == 2 ) 1052 *p++ = 0x02; /* binary segment */ 1053 else 1054 *p++ = 0x01; /* ASCII segment */ 1055 1056 if ( code != 5 ) 1057 { 1058 size_p = p; /* save for later */ 1059 p += 4; /* make space for size field */ 1060 } 1061 } 1062 1063 ft_memcpy( p, *post_data + 2, post_size ); 1064 pfb_chunk_size += post_size; 1065 p += post_size; 1066 last_code = code; 1067 } 1068 1069 *pfb_data = buffer; 1070 *size = total_size; 1071 1072 Error: 1073 CloseResFile( res ); 1074 return error; 1075 } 1076 1077 1078 /* Create a new FT_Face from a file spec to an LWFN file. */ 1079 static FT_Error 1080 FT_New_Face_From_LWFN( FT_Library library, 1081 const UInt8* pathname, 1082 FT_Long face_index, 1083 FT_Face* aface ) 1084 { 1085 FT_Byte* pfb_data; 1086 FT_ULong pfb_size; 1087 FT_Error error; 1088 ResFileRefNum res; 1089 1090 1091 if ( noErr != FT_FSPathMakeRes( pathname, &res ) ) 1092 return FT_THROW( Cannot_Open_Resource ); 1093 1094 pfb_data = NULL; 1095 pfb_size = 0; 1096 error = read_lwfn( library->memory, res, &pfb_data, &pfb_size ); 1097 CloseResFile( res ); /* PFB is already loaded, useless anymore */ 1098 if ( error ) 1099 return error; 1100 1101 return open_face_from_buffer( library, 1102 pfb_data, 1103 pfb_size, 1104 face_index, 1105 "type1", 1106 aface ); 1107 } 1108 1109 1110 /* Create a new FT_Face from an SFNT resource, specified by res ID. */ 1111 static FT_Error 1112 FT_New_Face_From_SFNT( FT_Library library, 1113 ResID sfnt_id, 1114 FT_Long face_index, 1115 FT_Face* aface ) 1116 { 1117 Handle sfnt = NULL; 1118 FT_Byte* sfnt_data; 1119 size_t sfnt_size; 1120 FT_Error error = FT_Err_Ok; 1121 FT_Memory memory = library->memory; 1122 int is_cff, is_sfnt_ps; 1123 1124 1125 sfnt = GetResource( TTAG_sfnt, sfnt_id ); 1126 if ( sfnt == NULL ) 1127 return FT_THROW( Invalid_Handle ); 1128 1129 sfnt_size = (FT_ULong)GetHandleSize( sfnt ); 1130 if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) ) 1131 { 1132 ReleaseResource( sfnt ); 1133 return error; 1134 } 1135 1136 HLock( sfnt ); 1137 ft_memcpy( sfnt_data, *sfnt, sfnt_size ); 1138 HUnlock( sfnt ); 1139 ReleaseResource( sfnt ); 1140 1141 is_cff = sfnt_size > 4 && !ft_memcmp( sfnt_data, "OTTO", 4 ); 1142 is_sfnt_ps = sfnt_size > 4 && !ft_memcmp( sfnt_data, "typ1", 4 ); 1143 1144 if ( is_sfnt_ps ) 1145 { 1146 FT_Stream stream; 1147 1148 1149 if ( FT_NEW( stream ) ) 1150 goto Try_OpenType; 1151 1152 FT_Stream_OpenMemory( stream, sfnt_data, sfnt_size ); 1153 if ( !open_face_PS_from_sfnt_stream( library, 1154 stream, 1155 face_index, 1156 0, NULL, 1157 aface ) ) 1158 { 1159 FT_Stream_Close( stream ); 1160 FT_FREE( stream ); 1161 FT_FREE( sfnt_data ); 1162 goto Exit; 1163 } 1164 1165 FT_FREE( stream ); 1166 } 1167 Try_OpenType: 1168 error = open_face_from_buffer( library, 1169 sfnt_data, 1170 sfnt_size, 1171 face_index, 1172 is_cff ? "cff" : "truetype", 1173 aface ); 1174 Exit: 1175 return error; 1176 } 1177 1178 1179 /* Create a new FT_Face from a file spec to a suitcase file. */ 1180 static FT_Error 1181 FT_New_Face_From_Suitcase( FT_Library library, 1182 const UInt8* pathname, 1183 FT_Long face_index, 1184 FT_Face* aface ) 1185 { 1186 FT_Error error = FT_ERR( Cannot_Open_Resource ); 1187 ResFileRefNum res_ref; 1188 ResourceIndex res_index; 1189 Handle fond; 1190 short num_faces_in_res; 1191 1192 1193 if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) ) 1194 return FT_THROW( Cannot_Open_Resource ); 1195 1196 UseResFile( res_ref ); 1197 if ( ResError() ) 1198 return FT_THROW( Cannot_Open_Resource ); 1199 1200 num_faces_in_res = 0; 1201 for ( res_index = 1; ; ++res_index ) 1202 { 1203 short num_faces_in_fond; 1204 1205 1206 fond = Get1IndResource( TTAG_FOND, res_index ); 1207 if ( ResError() ) 1208 break; 1209 1210 num_faces_in_fond = count_faces( fond, pathname ); 1211 num_faces_in_res += num_faces_in_fond; 1212 1213 if ( 0 <= face_index && face_index < num_faces_in_fond && error ) 1214 error = FT_New_Face_From_FOND( library, fond, face_index, aface ); 1215 1216 face_index -= num_faces_in_fond; 1217 } 1218 1219 CloseResFile( res_ref ); 1220 if ( !error && aface ) 1221 (*aface)->num_faces = num_faces_in_res; 1222 return error; 1223 } 1224 1225 1226 /* documentation is in ftmac.h */ 1227 1228 FT_EXPORT_DEF( FT_Error ) 1229 FT_New_Face_From_FOND( FT_Library library, 1230 Handle fond, 1231 FT_Long face_index, 1232 FT_Face* aface ) 1233 { 1234 short have_sfnt, have_lwfn = 0; 1235 ResID sfnt_id, fond_id; 1236 OSType fond_type; 1237 Str255 fond_name; 1238 Str255 lwfn_file_name; 1239 UInt8 path_lwfn[PATH_MAX]; 1240 OSErr err; 1241 FT_Error error = FT_Err_Ok; 1242 1243 1244 /* test for valid `aface' and `library' delayed to */ 1245 /* `FT_New_Face_From_XXX' */ 1246 1247 GetResInfo( fond, &fond_id, &fond_type, fond_name ); 1248 if ( ResError() != noErr || fond_type != TTAG_FOND ) 1249 return FT_THROW( Invalid_File_Format ); 1250 1251 HLock( fond ); 1252 parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index ); 1253 HUnlock( fond ); 1254 1255 if ( lwfn_file_name[0] ) 1256 { 1257 ResFileRefNum res; 1258 1259 1260 res = HomeResFile( fond ); 1261 if ( noErr != ResError() ) 1262 goto found_no_lwfn_file; 1263 1264 #if HAVE_FSREF 1265 1266 { 1267 UInt8 path_fond[PATH_MAX]; 1268 FSRef ref; 1269 1270 1271 err = FSGetForkCBInfo( res, kFSInvalidVolumeRefNum, 1272 NULL, NULL, NULL, &ref, NULL ); 1273 if ( noErr != err ) 1274 goto found_no_lwfn_file; 1275 1276 err = FSRefMakePath( &ref, path_fond, sizeof ( path_fond ) ); 1277 if ( noErr != err ) 1278 goto found_no_lwfn_file; 1279 1280 error = lookup_lwfn_by_fond( path_fond, lwfn_file_name, 1281 path_lwfn, sizeof ( path_lwfn ) ); 1282 if ( !error ) 1283 have_lwfn = 1; 1284 } 1285 1286 #elif HAVE_FSSPEC 1287 1288 { 1289 UInt8 path_fond[PATH_MAX]; 1290 FCBPBRec pb; 1291 Str255 fond_file_name; 1292 FSSpec spec; 1293 1294 1295 FT_MEM_SET( &spec, 0, sizeof ( FSSpec ) ); 1296 FT_MEM_SET( &pb, 0, sizeof ( FCBPBRec ) ); 1297 1298 pb.ioNamePtr = fond_file_name; 1299 pb.ioVRefNum = 0; 1300 pb.ioRefNum = res; 1301 pb.ioFCBIndx = 0; 1302 1303 err = PBGetFCBInfoSync( &pb ); 1304 if ( noErr != err ) 1305 goto found_no_lwfn_file; 1306 1307 err = FSMakeFSSpec( pb.ioFCBVRefNum, pb.ioFCBParID, 1308 fond_file_name, &spec ); 1309 if ( noErr != err ) 1310 goto found_no_lwfn_file; 1311 1312 err = FT_FSpMakePath( &spec, path_fond, sizeof ( path_fond ) ); 1313 if ( noErr != err ) 1314 goto found_no_lwfn_file; 1315 1316 error = lookup_lwfn_by_fond( path_fond, lwfn_file_name, 1317 path_lwfn, sizeof ( path_lwfn ) ); 1318 if ( !error ) 1319 have_lwfn = 1; 1320 } 1321 1322 #endif /* HAVE_FSREF, HAVE_FSSPEC */ 1323 1324 } 1325 1326 if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) ) 1327 error = FT_New_Face_From_LWFN( library, 1328 path_lwfn, 1329 face_index, 1330 aface ); 1331 else 1332 error = FT_ERR( Unknown_File_Format ); 1333 1334 found_no_lwfn_file: 1335 if ( have_sfnt && error ) 1336 error = FT_New_Face_From_SFNT( library, 1337 sfnt_id, 1338 face_index, 1339 aface ); 1340 1341 return error; 1342 } 1343 1344 1345 /* Common function to load a new FT_Face from a resource file. */ 1346 static FT_Error 1347 FT_New_Face_From_Resource( FT_Library library, 1348 const UInt8* pathname, 1349 FT_Long face_index, 1350 FT_Face* aface ) 1351 { 1352 OSType file_type; 1353 FT_Error error; 1354 1355 1356 /* LWFN is a (very) specific file format, check for it explicitly */ 1357 file_type = get_file_type_from_path( pathname ); 1358 if ( file_type == TTAG_LWFN ) 1359 return FT_New_Face_From_LWFN( library, pathname, face_index, aface ); 1360 1361 /* Otherwise the file type doesn't matter (there are more than */ 1362 /* `FFIL' and `tfil'). Just try opening it as a font suitcase; */ 1363 /* if it works, fine. */ 1364 1365 error = FT_New_Face_From_Suitcase( library, pathname, face_index, aface ); 1366 if ( !error ) 1367 return error; 1368 1369 /* let it fall through to normal loader (.ttf, .otf, etc.); */ 1370 /* we signal this by returning no error and no FT_Face */ 1371 *aface = NULL; 1372 return 0; 1373 } 1374 1375 1376 /*************************************************************************/ 1377 /* */ 1378 /* <Function> */ 1379 /* FT_New_Face */ 1380 /* */ 1381 /* <Description> */ 1382 /* This is the Mac-specific implementation of FT_New_Face. In */ 1383 /* addition to the standard FT_New_Face() functionality, it also */ 1384 /* accepts pathnames to Mac suitcase files. For further */ 1385 /* documentation see the original FT_New_Face() in freetype.h. */ 1386 /* */ 1387 FT_EXPORT_DEF( FT_Error ) 1388 FT_New_Face( FT_Library library, 1389 const char* pathname, 1390 FT_Long face_index, 1391 FT_Face* aface ) 1392 { 1393 FT_Open_Args args; 1394 FT_Error error; 1395 1396 1397 /* test for valid `library' and `aface' delayed to FT_Open_Face() */ 1398 if ( !pathname ) 1399 return FT_THROW( Invalid_Argument ); 1400 1401 *aface = NULL; 1402 1403 /* try resourcefork based font: LWFN, FFIL */ 1404 error = FT_New_Face_From_Resource( library, (UInt8 *)pathname, 1405 face_index, aface ); 1406 if ( error || *aface ) 1407 return error; 1408 1409 /* let it fall through to normal loader (.ttf, .otf, etc.) */ 1410 args.flags = FT_OPEN_PATHNAME; 1411 args.pathname = (char*)pathname; 1412 return FT_Open_Face( library, &args, face_index, aface ); 1413 } 1414 1415 1416 /*************************************************************************/ 1417 /* */ 1418 /* <Function> */ 1419 /* FT_New_Face_From_FSRef */ 1420 /* */ 1421 /* <Description> */ 1422 /* FT_New_Face_From_FSRef is identical to FT_New_Face except it */ 1423 /* accepts an FSRef instead of a path. */ 1424 /* */ 1425 /* This function is deprecated because Carbon data types (FSRef) */ 1426 /* are not cross-platform, and thus not suitable for the FreeType API. */ 1427 FT_EXPORT_DEF( FT_Error ) 1428 FT_New_Face_From_FSRef( FT_Library library, 1429 const FSRef* ref, 1430 FT_Long face_index, 1431 FT_Face* aface ) 1432 { 1433 1434 #if !HAVE_FSREF 1435 1436 FT_UNUSED( library ); 1437 FT_UNUSED( ref ); 1438 FT_UNUSED( face_index ); 1439 FT_UNUSED( aface ); 1440 1441 return FT_THROW( Unimplemented_Feature ); 1442 1443 #else 1444 1445 FT_Error error; 1446 FT_Open_Args args; 1447 OSErr err; 1448 UInt8 pathname[PATH_MAX]; 1449 1450 1451 /* test for valid `library' and `aface' delayed to `FT_Open_Face' */ 1452 1453 if ( !ref ) 1454 return FT_THROW( Invalid_Argument ); 1455 1456 err = FSRefMakePath( ref, pathname, sizeof ( pathname ) ); 1457 if ( err ) 1458 error = FT_ERR( Cannot_Open_Resource ); 1459 1460 error = FT_New_Face_From_Resource( library, pathname, face_index, aface ); 1461 if ( error || *aface ) 1462 return error; 1463 1464 /* fallback to datafork font */ 1465 args.flags = FT_OPEN_PATHNAME; 1466 args.pathname = (char*)pathname; 1467 return FT_Open_Face( library, &args, face_index, aface ); 1468 1469 #endif /* HAVE_FSREF */ 1470 1471 } 1472 1473 1474 /*************************************************************************/ 1475 /* */ 1476 /* <Function> */ 1477 /* FT_New_Face_From_FSSpec */ 1478 /* */ 1479 /* <Description> */ 1480 /* FT_New_Face_From_FSSpec is identical to FT_New_Face except it */ 1481 /* accepts an FSSpec instead of a path. */ 1482 /* */ 1483 /* This function is deprecated because Carbon data types (FSSpec) */ 1484 /* are not cross-platform, and thus not suitable for the FreeType API. */ 1485 FT_EXPORT_DEF( FT_Error ) 1486 FT_New_Face_From_FSSpec( FT_Library library, 1487 const FSSpec* spec, 1488 FT_Long face_index, 1489 FT_Face* aface ) 1490 { 1491 1492 #if HAVE_FSREF 1493 1494 FSRef ref; 1495 1496 1497 if ( !spec || FSpMakeFSRef( spec, &ref ) != noErr ) 1498 return FT_THROW( Invalid_Argument ); 1499 else 1500 return FT_New_Face_From_FSRef( library, &ref, face_index, aface ); 1501 1502 #elif HAVE_FSSPEC 1503 1504 FT_Error error; 1505 FT_Open_Args args; 1506 OSErr err; 1507 UInt8 pathname[PATH_MAX]; 1508 1509 1510 if ( !spec ) 1511 return FT_THROW( Invalid_Argument ); 1512 1513 err = FT_FSpMakePath( spec, pathname, sizeof ( pathname ) ); 1514 if ( err ) 1515 error = FT_ERR( Cannot_Open_Resource ); 1516 1517 error = FT_New_Face_From_Resource( library, pathname, face_index, aface ); 1518 if ( error || *aface ) 1519 return error; 1520 1521 /* fallback to datafork font */ 1522 args.flags = FT_OPEN_PATHNAME; 1523 args.pathname = (char*)pathname; 1524 return FT_Open_Face( library, &args, face_index, aface ); 1525 1526 #else 1527 1528 FT_UNUSED( library ); 1529 FT_UNUSED( spec ); 1530 FT_UNUSED( face_index ); 1531 FT_UNUSED( aface ); 1532 1533 return FT_THROW( Unimplemented_Feature ); 1534 1535 #endif /* HAVE_FSREF, HAVE_FSSPEC */ 1536 1537 } 1538 1539 #endif /* FT_MACINTOSH */ 1540 1541 1542 /* END */ 1543