1 /***************************************************************************/ 2 /* */ 3 /* ftmac.c */ 4 /* */ 5 /* Mac FOND support. Written by just@letterror.com. */ 6 /* */ 7 /* Copyright 1996-2001, 2002 by */ 8 /* Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. */ 9 /* */ 10 /* This file is part of the FreeType project, and may only be used, */ 11 /* modified, and distributed under the terms of the FreeType project */ 12 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ 13 /* this file you indicate that you have read the license and */ 14 /* understand and accept it fully. */ 15 /* */ 16 /***************************************************************************/ 17 18 19 /* 20 Notes 21 22 Mac suitcase files can (and often do!) contain multiple fonts. To 23 support this I use the face_index argument of FT_(Open|New)_Face() 24 functions, and pretend the suitcase file is a collection. 25 26 Warning: Although the FOND driver sets face->num_faces field to the 27 number of available fonts, but the Type 1 driver sets it to 1 anyway. 28 So this field is currently not reliable, and I don't see a clean way 29 to resolve that. The face_index argument translates to 30 31 Get1IndResource( 'FOND', face_index + 1 ); 32 33 so clients should figure out the resource index of the FOND. 34 (I'll try to provide some example code for this at some point.) 35 36 The Mac FOND support works roughly like this: 37 38 - Check whether the offered stream points to a Mac suitcase file. 39 This is done by checking the file type: it has to be 'FFIL' or 'tfil'. 40 The stream that gets passed to our init_face() routine is a stdio 41 stream, which isn't usable for us, since the FOND resources live 42 in the resource fork. So we just grab the stream->pathname field. 43 44 - Read the FOND resource into memory, then check whether there is 45 a TrueType font and/or(!) a Type 1 font available. 46 47 - If there is a Type 1 font available (as a separate 'LWFN' file), 48 read its data into memory, massage it slightly so it becomes 49 PFB data, wrap it into a memory stream, load the Type 1 driver 50 and delegate the rest of the work to it by calling FT_Open_Face(). 51 (XXX TODO: after this has been done, the kerning data from the FOND 52 resource should be appended to the face: On the Mac there are usually 53 no AFM files available. However, this is tricky since we need to map 54 Mac char codes to ps glyph names to glyph ID's...) 55 56 - If there is a TrueType font (an 'sfnt' resource), read it into 57 memory, wrap it into a memory stream, load the TrueType driver 58 and delegate the rest of the work to it, by calling FT_Open_Face(). 59 */ 60 61 62 #include <ft2build.h> 63 #include FT_FREETYPE_H 64 #include FT_INTERNAL_STREAM_H 65 #include "truetype/ttobjs.h" 66 #include "type1/t1objs.h" 67 68 #include <Resources.h> 69 #include <Fonts.h> 70 #include <Errors.h> 71 #include <Files.h> 72 #include <TextUtils.h> 73 74 75 #include FT_MAC_H 76 77 78 /* Set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over 79 TrueType in case *both* are available (this is not common, 80 but it *is* possible). */ 81 #ifndef PREFER_LWFN 82 #define PREFER_LWFN 1 83 #endif 84 85 /* Given a pathname, fill in a file spec. */ 86 static int file_spec_from_path(const char * pathname,FSSpec * spec)87 file_spec_from_path( const char* pathname, 88 FSSpec* spec ) 89 { 90 #if TARGET_API_MAC_CARBON 91 92 OSErr e; 93 FSRef ref; 94 95 96 e = FSPathMakeRef( (UInt8 *)pathname, &ref, false /* not a directory */ ); 97 if ( e == noErr ) 98 e = FSGetCatalogInfo( &ref, kFSCatInfoNone, NULL, NULL, spec, NULL ); 99 100 return ( e == noErr ) ? 0 : (-1); 101 102 #else 103 104 Str255 p_path; 105 FT_ULong path_len; 106 107 108 /* convert path to a pascal string */ 109 path_len = ft_strlen( pathname ); 110 if ( path_len > 255 ) 111 return -1; 112 p_path[0] = (unsigned char)path_len; 113 ft_strncpy( (char*)p_path + 1, pathname, path_len ); 114 115 if ( FSMakeFSSpec( 0, 0, p_path, spec ) != noErr ) 116 return -1; 117 else 118 return 0; 119 120 #endif 121 } 122 123 124 /* Return the file type of the file specified by spec. */ 125 static OSType get_file_type(FSSpec * spec)126 get_file_type( FSSpec* spec ) 127 { 128 FInfo finfo; 129 130 131 if ( FSpGetFInfo( spec, &finfo ) != noErr ) 132 return 0; /* file might not exist */ 133 134 return finfo.fdType; 135 } 136 137 138 #if TARGET_API_MAC_CARBON 139 140 /* is this a Mac OS X .dfont file */ 141 static Boolean is_dfont(FSSpec * spec)142 is_dfont( FSSpec* spec ) 143 { 144 int nameLen = spec->name[0]; 145 146 147 return nameLen >= 6 && 148 !memcmp( spec->name + nameLen - 5, ".dfont", 6 ); 149 } 150 151 #endif 152 153 154 /* Given a PostScript font name, create the Macintosh LWFN file name. */ 155 static void create_lwfn_name(char * ps_name,Str255 lwfn_file_name)156 create_lwfn_name( char* ps_name, 157 Str255 lwfn_file_name ) 158 { 159 int max = 5, count = 0; 160 FT_Byte* p = lwfn_file_name; 161 FT_Byte* q = (FT_Byte*)ps_name; 162 163 164 lwfn_file_name[0] = 0; 165 166 while ( *q ) 167 { 168 if ( isupper( *q ) ) 169 { 170 if ( count ) 171 max = 3; 172 count = 0; 173 } 174 if ( count < max && ( ft_isalnum( *q ) || *q == '_' ) ) 175 { 176 *++p = *q; 177 lwfn_file_name[0]++; 178 count++; 179 } 180 q++; 181 } 182 } 183 184 185 /* Given a file reference, answer its location as a vRefNum 186 and a dirID. */ 187 static FT_Error get_file_location(short ref_num,short * v_ref_num,long * dir_id,unsigned char * file_name)188 get_file_location( short ref_num, 189 short* v_ref_num, 190 long* dir_id, 191 unsigned char* file_name ) 192 { 193 FCBPBRec pb; 194 OSErr error; 195 196 197 pb.ioNamePtr = file_name; 198 pb.ioVRefNum = 0; 199 pb.ioRefNum = ref_num; 200 pb.ioFCBIndx = 0; 201 202 error = PBGetFCBInfoSync( &pb ); 203 if ( error == noErr ) 204 { 205 *v_ref_num = pb.ioFCBVRefNum; 206 *dir_id = pb.ioFCBParID; 207 } 208 return error; 209 } 210 211 212 /* Make a file spec for an LWFN file from a FOND resource and 213 a file name. */ 214 static FT_Error make_lwfn_spec(Handle fond,unsigned char * file_name,FSSpec * spec)215 make_lwfn_spec( Handle fond, 216 unsigned char* file_name, 217 FSSpec* spec ) 218 { 219 FT_Error error; 220 short ref_num, v_ref_num; 221 long dir_id; 222 Str255 fond_file_name; 223 224 225 ref_num = HomeResFile( fond ); 226 227 error = ResError(); 228 if ( !error ) 229 error = get_file_location( ref_num, &v_ref_num, 230 &dir_id, fond_file_name ); 231 if ( !error ) 232 error = FSMakeFSSpec( v_ref_num, dir_id, file_name, spec ); 233 234 return error; 235 } 236 237 238 /* Look inside the FOND data, answer whether there should be an SFNT 239 resource, and answer the name of a possible LWFN Type 1 file. 240 241 Thanks to Paul Miller (paulm@profoundeffects.com) for the fix 242 to load a face OTHER than the first one in the FOND! 243 */ 244 static void parse_fond(char * fond_data,short * have_sfnt,short * sfnt_id,Str255 lwfn_file_name,short face_index)245 parse_fond( char* fond_data, 246 short* have_sfnt, 247 short* sfnt_id, 248 Str255 lwfn_file_name, 249 short face_index ) 250 { 251 AsscEntry* assoc; 252 AsscEntry* base_assoc; 253 FamRec* fond; 254 255 256 *sfnt_id = 0; 257 *have_sfnt = 0; 258 lwfn_file_name[0] = 0; 259 260 fond = (FamRec*)fond_data; 261 assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 ); 262 base_assoc = assoc; 263 assoc += face_index; /* add on the face_index! */ 264 265 /* if the face at this index is not scalable, 266 fall back to the first one (old behavior) */ 267 if ( assoc->fontSize == 0 ) 268 { 269 *have_sfnt = 1; 270 *sfnt_id = assoc->fontID; 271 } 272 else if ( base_assoc->fontSize == 0 ) 273 { 274 *have_sfnt = 1; 275 *sfnt_id = base_assoc->fontID; 276 } 277 278 if ( fond->ffStylOff ) 279 { 280 unsigned char* p = (unsigned char*)fond_data; 281 StyleTable* style; 282 unsigned short string_count; 283 char ps_name[256]; 284 unsigned char* names[64]; 285 int i; 286 287 288 p += fond->ffStylOff; 289 style = (StyleTable*)p; 290 p += sizeof ( StyleTable ); 291 string_count = *(unsigned short*)(p); 292 p += sizeof ( short ); 293 294 for ( i = 0 ; i < string_count && i < 64; i++ ) 295 { 296 names[i] = p; 297 p += names[i][0]; 298 p++; 299 } 300 301 { 302 size_t ps_name_len = (size_t)names[0][0]; 303 304 305 if ( ps_name_len != 0 ) 306 { 307 memcpy(ps_name, names[0] + 1, ps_name_len); 308 ps_name[ps_name_len] = 0; 309 } 310 if ( style->indexes[0] > 1 ) 311 { 312 unsigned char* suffixes = names[style->indexes[0] - 1]; 313 314 315 for ( i = 1; i < suffixes[0]; i++ ) 316 { 317 unsigned char* s; 318 size_t j = suffixes[i] - 1; 319 320 321 if ( j < string_count && ( s = names[j] ) != NULL ) 322 { 323 size_t s_len = (size_t)s[0]; 324 325 326 if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) ) 327 { 328 memcpy( ps_name + ps_name_len, s + 1, s_len ); 329 ps_name_len += s_len; 330 ps_name[ps_name_len] = 0; 331 } 332 } 333 } 334 } 335 } 336 337 create_lwfn_name( ps_name, lwfn_file_name ); 338 } 339 } 340 341 342 /* Read Type 1 data from the POST resources inside the LWFN file, 343 return a PFB buffer. This is somewhat convoluted because the FT2 344 PFB parser wants the ASCII header as one chunk, and the LWFN 345 chunks are often not organized that way, so we'll glue chunks 346 of the same type together. */ 347 static FT_Error read_lwfn(FT_Memory memory,FSSpec * lwfn_spec,FT_Byte ** pfb_data,FT_ULong * size)348 read_lwfn( FT_Memory memory, 349 FSSpec* lwfn_spec, 350 FT_Byte** pfb_data, 351 FT_ULong* size ) 352 { 353 FT_Error error = FT_Err_Ok; 354 short res_ref, res_id; 355 unsigned char *buffer, *p, *size_p = NULL; 356 FT_ULong total_size = 0; 357 FT_ULong post_size, pfb_chunk_size; 358 Handle post_data; 359 char code, last_code; 360 361 362 res_ref = FSpOpenResFile( lwfn_spec, fsRdPerm ); 363 if ( ResError() ) 364 return FT_Err_Out_Of_Memory; 365 UseResFile( res_ref ); 366 367 /* First pass: load all POST resources, and determine the size of 368 the output buffer. */ 369 res_id = 501; 370 last_code = -1; 371 372 for (;;) 373 { 374 post_data = Get1Resource( 'POST', res_id++ ); 375 if ( post_data == NULL ) 376 break; /* we're done */ 377 378 code = (*post_data)[0]; 379 380 if ( code != last_code ) 381 { 382 if ( code == 5 ) 383 total_size += 2; /* just the end code */ 384 else 385 total_size += 6; /* code + 4 bytes chunk length */ 386 } 387 388 total_size += GetHandleSize( post_data ) - 2; 389 last_code = code; 390 } 391 392 if ( FT_ALLOC( buffer, (FT_Long)total_size ) ) 393 goto Error; 394 395 /* Second pass: append all POST data to the buffer, add PFB fields. 396 Glue all consecutive chunks of the same type together. */ 397 p = buffer; 398 res_id = 501; 399 last_code = -1; 400 pfb_chunk_size = 0; 401 402 for (;;) 403 { 404 post_data = Get1Resource( 'POST', res_id++ ); 405 if ( post_data == NULL ) 406 break; /* we're done */ 407 408 post_size = (FT_ULong)GetHandleSize( post_data ) - 2; 409 code = (*post_data)[0]; 410 411 if ( code != last_code ) 412 { 413 if ( last_code != -1 ) 414 { 415 /* we're done adding a chunk, fill in the size field */ 416 if ( size_p != NULL ) 417 { 418 *size_p++ = (FT_Byte)( pfb_chunk_size & 0xFF ); 419 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8 ) & 0xFF ); 420 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF ); 421 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF ); 422 } 423 pfb_chunk_size = 0; 424 } 425 426 *p++ = 0x80; 427 if ( code == 5 ) 428 *p++ = 0x03; /* the end */ 429 else if ( code == 2 ) 430 *p++ = 0x02; /* binary segment */ 431 else 432 *p++ = 0x01; /* ASCII segment */ 433 434 if ( code != 5 ) 435 { 436 size_p = p; /* save for later */ 437 p += 4; /* make space for size field */ 438 } 439 } 440 441 ft_memcpy( p, *post_data + 2, post_size ); 442 pfb_chunk_size += post_size; 443 p += post_size; 444 last_code = code; 445 } 446 447 *pfb_data = buffer; 448 *size = total_size; 449 450 Error: 451 CloseResFile( res_ref ); 452 return error; 453 } 454 455 456 /* Finalizer for a memory stream; gets called by FT_Done_Face(). 457 It frees the memory it uses. */ 458 static void memory_stream_close(FT_Stream stream)459 memory_stream_close( FT_Stream stream ) 460 { 461 FT_Memory memory = stream->memory; 462 463 464 FT_FREE( stream->base ); 465 466 stream->size = 0; 467 stream->base = 0; 468 stream->close = 0; 469 } 470 471 472 /* Create a new memory stream from a buffer and a size. */ 473 static FT_Error new_memory_stream(FT_Library library,FT_Byte * base,FT_ULong size,FT_Stream_CloseFunc close,FT_Stream * astream)474 new_memory_stream( FT_Library library, 475 FT_Byte* base, 476 FT_ULong size, 477 FT_Stream_CloseFunc close, 478 FT_Stream *astream ) 479 { 480 FT_Error error; 481 FT_Memory memory; 482 FT_Stream stream; 483 484 485 if ( !library ) 486 return FT_Err_Invalid_Library_Handle; 487 488 if ( !base ) 489 return FT_Err_Invalid_Argument; 490 491 *astream = 0; 492 memory = library->memory; 493 if ( FT_NEW( stream ) ) 494 goto Exit; 495 496 FT_Stream_OpenMemory( stream, base, size ); 497 498 stream->close = close; 499 500 *astream = stream; 501 502 Exit: 503 return error; 504 } 505 506 507 /* Create a new FT_Face given a buffer and a driver name. */ 508 static FT_Error open_face_from_buffer(FT_Library library,FT_Byte * base,FT_ULong size,FT_Long face_index,char * driver_name,FT_Face * aface)509 open_face_from_buffer( FT_Library library, 510 FT_Byte* base, 511 FT_ULong size, 512 FT_Long face_index, 513 char* driver_name, 514 FT_Face *aface ) 515 { 516 FT_Open_Args args; 517 FT_Error error; 518 FT_Stream stream; 519 FT_Memory memory = library->memory; 520 521 522 error = new_memory_stream( library, 523 base, 524 size, 525 memory_stream_close, 526 &stream ); 527 if ( error ) 528 { 529 FT_FREE( base ); 530 return error; 531 } 532 533 args.flags = FT_OPEN_STREAM; 534 args.stream = stream; 535 if ( driver_name ) 536 { 537 args.flags = args.flags | FT_OPEN_DRIVER; 538 args.driver = FT_Get_Module( library, driver_name ); 539 } 540 541 error = FT_Open_Face( library, &args, face_index, aface ); 542 if ( error == FT_Err_Ok ) 543 (*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM; 544 else 545 { 546 FT_Stream_CloseFunc( stream ); 547 FT_FREE( stream ); 548 } 549 550 return error; 551 } 552 553 554 /* Create a new FT_Face from a file spec to an LWFN file. */ 555 static FT_Error FT_New_Face_From_LWFN(FT_Library library,FSSpec * spec,FT_Long face_index,FT_Face * aface)556 FT_New_Face_From_LWFN( FT_Library library, 557 FSSpec* spec, 558 FT_Long face_index, 559 FT_Face *aface ) 560 { 561 FT_Byte* pfb_data; 562 FT_ULong pfb_size; 563 FT_Error error; 564 565 566 error = read_lwfn( library->memory, spec, &pfb_data, &pfb_size ); 567 if ( error ) 568 return error; 569 570 return open_face_from_buffer( library, 571 pfb_data, 572 pfb_size, 573 face_index, 574 "type1", 575 aface ); 576 } 577 578 579 /* Create a new FT_Face from an SFNT resource, specified by res ID. */ 580 static FT_Error FT_New_Face_From_SFNT(FT_Library library,short sfnt_id,FT_Long face_index,FT_Face * aface)581 FT_New_Face_From_SFNT( FT_Library library, 582 short sfnt_id, 583 FT_Long face_index, 584 FT_Face *aface ) 585 { 586 Handle sfnt = NULL; 587 FT_Byte* sfnt_data; 588 size_t sfnt_size; 589 FT_Error error = 0; 590 FT_Memory memory = library->memory; 591 592 593 sfnt = GetResource( 'sfnt', sfnt_id ); 594 if ( ResError() ) 595 return FT_Err_Invalid_Handle; 596 597 sfnt_size = (FT_ULong)GetHandleSize( sfnt ); 598 if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) ) 599 { 600 ReleaseResource( sfnt ); 601 return error; 602 } 603 604 HLock( sfnt ); 605 ft_memcpy( sfnt_data, *sfnt, sfnt_size ); 606 HUnlock( sfnt ); 607 ReleaseResource( sfnt ); 608 609 return open_face_from_buffer( library, 610 sfnt_data, 611 sfnt_size, 612 face_index, 613 "truetype", 614 aface ); 615 } 616 617 618 /* Create a new FT_Face from a file spec to a suitcase file. */ 619 static FT_Error FT_New_Face_From_Suitcase(FT_Library library,FSSpec * spec,FT_Long face_index,FT_Face * aface)620 FT_New_Face_From_Suitcase( FT_Library library, 621 FSSpec* spec, 622 FT_Long face_index, 623 FT_Face *aface ) 624 { 625 FT_Error error = FT_Err_Ok; 626 short res_ref, res_index; 627 Handle fond; 628 629 630 res_ref = FSpOpenResFile( spec, fsRdPerm ); 631 if ( ResError() ) 632 return FT_Err_Cannot_Open_Resource; 633 UseResFile( res_ref ); 634 635 /* face_index may be -1, in which case we 636 just need to do a sanity check */ 637 if ( face_index < 0 ) 638 res_index = 1; 639 else 640 { 641 res_index = (short)( face_index + 1 ); 642 face_index = 0; 643 } 644 fond = Get1IndResource( 'FOND', res_index ); 645 if ( ResError() ) 646 { 647 error = FT_Err_Cannot_Open_Resource; 648 goto Error; 649 } 650 651 error = FT_New_Face_From_FOND( library, fond, face_index, aface ); 652 653 Error: 654 CloseResFile( res_ref ); 655 return error; 656 } 657 658 659 #if TARGET_API_MAC_CARBON 660 661 /* Create a new FT_Face from a file spec to a suitcase file. */ 662 static FT_Error FT_New_Face_From_dfont(FT_Library library,FSSpec * spec,FT_Long face_index,FT_Face * aface)663 FT_New_Face_From_dfont( FT_Library library, 664 FSSpec* spec, 665 FT_Long face_index, 666 FT_Face* aface ) 667 { 668 FT_Error error = FT_Err_Ok; 669 short res_ref, res_index; 670 Handle fond; 671 FSRef hostContainerRef; 672 673 674 error = FSpMakeFSRef( spec, &hostContainerRef ); 675 if ( error == noErr ) 676 error = FSOpenResourceFile( &hostContainerRef, 677 0, NULL, fsRdPerm, &res_ref ); 678 679 if ( error != noErr ) 680 return FT_Err_Cannot_Open_Resource; 681 682 UseResFile( res_ref ); 683 684 /* face_index may be -1, in which case we 685 just need to do a sanity check */ 686 if ( face_index < 0 ) 687 res_index = 1; 688 else 689 { 690 res_index = (short)( face_index + 1 ); 691 face_index = 0; 692 } 693 fond = Get1IndResource( 'FOND', res_index ); 694 if ( ResError() ) 695 { 696 error = FT_Err_Cannot_Open_Resource; 697 goto Error; 698 } 699 700 error = FT_New_Face_From_FOND( library, fond, face_index, aface ); 701 702 Error: 703 CloseResFile( res_ref ); 704 return error; 705 } 706 707 #endif 708 709 710 /* documentation is in ftmac.h */ 711 712 FT_EXPORT_DEF( FT_Error ) FT_New_Face_From_FOND(FT_Library library,Handle fond,FT_Long face_index,FT_Face * aface)713 FT_New_Face_From_FOND( FT_Library library, 714 Handle fond, 715 FT_Long face_index, 716 FT_Face *aface ) 717 { 718 short sfnt_id, have_sfnt, have_lwfn = 0; 719 Str255 lwfn_file_name; 720 short fond_id; 721 OSType fond_type; 722 Str255 fond_name; 723 FSSpec lwfn_spec; 724 725 726 GetResInfo( fond, &fond_id, &fond_type, fond_name ); 727 if ( ResError() != noErr || fond_type != 'FOND' ) 728 return FT_Err_Invalid_File_Format; 729 730 HLock( fond ); 731 parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index ); 732 HUnlock( fond ); 733 734 if ( lwfn_file_name[0] ) 735 { 736 if ( make_lwfn_spec( fond, lwfn_file_name, &lwfn_spec ) == FT_Err_Ok ) 737 have_lwfn = 1; /* yeah, we got one! */ 738 else 739 have_lwfn = 0; /* no LWFN file found */ 740 } 741 742 if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) ) 743 return FT_New_Face_From_LWFN( library, 744 &lwfn_spec, 745 face_index, 746 aface ); 747 else if ( have_sfnt ) 748 return FT_New_Face_From_SFNT( library, 749 sfnt_id, 750 face_index, 751 aface ); 752 753 return FT_Err_Unknown_File_Format; 754 } 755 756 757 /* documentation is in ftmac.h */ 758 759 FT_EXPORT_DEF( FT_Error ) FT_GetFile_From_Mac_Name(char * fontName,FSSpec * pathSpec,FT_Long * face_index)760 FT_GetFile_From_Mac_Name( char* fontName, 761 FSSpec* pathSpec, 762 FT_Long* face_index ) 763 { 764 OptionBits options = kFMUseGlobalScopeOption; 765 766 FMFontFamilyIterator famIter; 767 OSStatus status = FMCreateFontFamilyIterator( NULL, NULL, 768 options, 769 &famIter ); 770 FMFont the_font = NULL; 771 FMFontFamily family = NULL; 772 773 774 *face_index = 0; 775 while ( status == 0 && !the_font ) 776 { 777 status = FMGetNextFontFamily( &famIter, &family ); 778 if ( status == 0 ) 779 { 780 int stat2; 781 FMFontFamilyInstanceIterator instIter; 782 Str255 famNameStr; 783 char famName[256]; 784 785 786 /* get the family name */ 787 FMGetFontFamilyName( family, famNameStr ); 788 CopyPascalStringToC( famNameStr, famName ); 789 790 /* iterate through the styles */ 791 FMCreateFontFamilyInstanceIterator( family, &instIter ); 792 793 *face_index = 0; 794 stat2 = 0; 795 while ( stat2 == 0 && !the_font ) 796 { 797 FMFontStyle style; 798 FMFontSize size; 799 FMFont font; 800 801 802 stat2 = FMGetNextFontFamilyInstance( &instIter, &font, 803 &style, &size ); 804 if ( stat2 == 0 && size == 0 ) 805 { 806 char fullName[256]; 807 808 809 /* build up a complete face name */ 810 ft_strcpy( fullName, famName ); 811 if ( style & bold ) 812 strcat( fullName, " Bold" ); 813 if ( style & italic ) 814 strcat( fullName, " Italic" ); 815 816 /* compare with the name we are looking for */ 817 if ( ft_strcmp( fullName, fontName ) == 0 ) 818 { 819 /* found it! */ 820 the_font = font; 821 } 822 else 823 ++(*face_index); 824 } 825 } 826 827 FMDisposeFontFamilyInstanceIterator( &instIter ); 828 } 829 } 830 831 FMDisposeFontFamilyIterator( &famIter ); 832 833 if ( the_font ) 834 { 835 FMGetFontContainer( the_font, pathSpec ); 836 return FT_Err_Ok; 837 } 838 else 839 return FT_Err_Unknown_File_Format; 840 } 841 842 843 static long ResourceForkSize(FSSpec * spec)844 ResourceForkSize(FSSpec* spec) 845 { 846 long len; 847 short refNum; 848 OSErr e; 849 850 851 e = FSpOpenRF( spec, fsRdPerm, &refNum ); /* I.M. Files 2-155 */ 852 if ( e == noErr ) 853 { 854 e = GetEOF( refNum, &len ); 855 FSClose( refNum ); 856 } 857 858 return ( e == noErr ) ? len : 0; 859 } 860 861 862 /*************************************************************************/ 863 /* */ 864 /* <Function> */ 865 /* FT_New_Face */ 866 /* */ 867 /* <Description> */ 868 /* This is the Mac-specific implementation of FT_New_Face. In */ 869 /* addition to the standard FT_New_Face() functionality, it also */ 870 /* accepts pathnames to Mac suitcase files. For further */ 871 /* documentation see the original FT_New_Face() in freetype.h. */ 872 /* */ 873 FT_EXPORT_DEF( FT_Error ) FT_New_Face(FT_Library library,const char * pathname,FT_Long face_index,FT_Face * aface)874 FT_New_Face( FT_Library library, 875 const char* pathname, 876 FT_Long face_index, 877 FT_Face *aface ) 878 { 879 FT_Open_Args args; 880 FSSpec spec; 881 OSType file_type; 882 883 884 /* test for valid `library' and `aface' delayed to FT_Open_Face() */ 885 if ( !pathname ) 886 return FT_Err_Invalid_Argument; 887 888 if ( file_spec_from_path( pathname, &spec ) ) 889 return FT_Err_Invalid_Argument; 890 891 /* Regardless of type, don't try to use the resource fork if it is */ 892 /* empty. Some TTF fonts have type `FFIL', for example, but they */ 893 /* only have data forks. */ 894 895 if ( ResourceForkSize( &spec ) != 0 ) 896 { 897 file_type = get_file_type( &spec ); 898 if ( file_type == 'FFIL' || file_type == 'tfil' ) 899 return FT_New_Face_From_Suitcase( library, &spec, face_index, aface ); 900 901 if ( file_type == 'LWFN' ) 902 return FT_New_Face_From_LWFN( library, &spec, face_index, aface ); 903 } 904 905 #if TARGET_API_MAC_CARBON 906 907 if ( is_dfont( &spec ) ) 908 return FT_New_Face_From_dfont( library, &spec, face_index, aface ); 909 910 #endif 911 912 /* let it fall through to normal loader (.ttf, .otf, etc.) */ 913 args.flags = FT_OPEN_PATHNAME; 914 args.pathname = (char*)pathname; 915 return FT_Open_Face( library, &args, face_index, aface ); 916 } 917 918 919 /* END */ 920