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