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