Home | History | Annotate | Download | only in base
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  ftmac.c                                                                */
      4 /*                                                                         */
      5 /*    Mac FOND support.  Written by just (at) 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-2018 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 (at) 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   FT_EXPORT_DEF( FT_Error )
   1010   FT_New_Face_From_FSRef( FT_Library    library,
   1011                           const FSRef*  ref,
   1012                           FT_Long       face_index,
   1013                           FT_Face*      aface )
   1014   {
   1015     FT_Error      error;
   1016     FT_Open_Args  args;
   1017 
   1018     OSErr  err;
   1019     UInt8  pathname[PATH_MAX];
   1020 
   1021 
   1022     /* check of `library' and `aface' delayed to */
   1023     /* `FT_New_Face_From_Resource'               */
   1024 
   1025     if ( !ref )
   1026       return FT_THROW( Invalid_Argument );
   1027 
   1028     err = FSRefMakePath( ref, pathname, sizeof ( pathname ) );
   1029     if ( err )
   1030       error = FT_THROW( Cannot_Open_Resource );
   1031 
   1032     error = FT_New_Face_From_Resource( library, pathname, face_index, aface );
   1033     if ( error || *aface )
   1034       return error;
   1035 
   1036     /* fallback to datafork font */
   1037     args.flags    = FT_OPEN_PATHNAME;
   1038     args.pathname = (char*)pathname;
   1039     return FT_Open_Face( library, &args, face_index, aface );
   1040   }
   1041 
   1042 
   1043   /*************************************************************************/
   1044   /*                                                                       */
   1045   /* <Function>                                                            */
   1046   /*    FT_New_Face_From_FSSpec                                            */
   1047   /*                                                                       */
   1048   /* <Description>                                                         */
   1049   /*    FT_New_Face_From_FSSpec is identical to FT_New_Face except it      */
   1050   /*    accepts an FSSpec instead of a path.                               */
   1051   /*                                                                       */
   1052   /* This function is deprecated because FSSpec is deprecated in Mac OS X  */
   1053   FT_EXPORT_DEF( FT_Error )
   1054   FT_New_Face_From_FSSpec( FT_Library     library,
   1055                            const FSSpec*  spec,
   1056                            FT_Long        face_index,
   1057                            FT_Face*       aface )
   1058   {
   1059 #if ( __LP64__ ) || ( defined( MAC_OS_X_VERSION_10_5 ) && \
   1060       ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) )
   1061     FT_UNUSED( library );
   1062     FT_UNUSED( spec );
   1063     FT_UNUSED( face_index );
   1064     FT_UNUSED( aface );
   1065 
   1066     return FT_THROW( Unimplemented_Feature );
   1067 #else
   1068     FSRef  ref;
   1069 
   1070 
   1071     /* check of `library' and `aface' delayed to `FT_New_Face_From_FSRef' */
   1072 
   1073     if ( !spec || FSpMakeFSRef( spec, &ref ) != noErr )
   1074       return FT_THROW( Invalid_Argument );
   1075     else
   1076       return FT_New_Face_From_FSRef( library, &ref, face_index, aface );
   1077 #endif
   1078   }
   1079 
   1080 #else /* !FT_MACINTOSH */
   1081 
   1082   /* ANSI C doesn't like empty source files */
   1083   typedef int  _ft_mac_dummy;
   1084 
   1085 #endif /* !FT_MACINTOSH */
   1086 
   1087 
   1088 /* END */
   1089