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