tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ftmac.c (31624B)


      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-2025 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 <freetype/freetype.h>
     69 #include <freetype/tttags.h>
     70 #include <freetype/internal/ftdebug.h>
     71 #include <freetype/internal/ftstream.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 <freetype/ftmac.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_QALLOC( 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_QALLOC( 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    FT_Long        count;
    816 
    817 
    818    if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) )
    819      return FT_THROW( Cannot_Open_Resource );
    820 
    821    UseResFile( res_ref );
    822    if ( ResError() )
    823      return FT_THROW( Cannot_Open_Resource );
    824 
    825    res_index        = 1;
    826    num_faces_in_res = 0;
    827    count            = face_index;
    828    while ( count >= 0 )
    829    {
    830      short  num_faces_in_fond;
    831 
    832 
    833      fond = Get1IndResource( TTAG_FOND, res_index );
    834      if ( ResError() )
    835        break;
    836 
    837      num_faces_in_fond  = count_faces( fond, pathname );
    838      num_faces_in_res  += num_faces_in_fond;
    839 
    840      if ( count < num_faces_in_fond )
    841        error = FT_New_Face_From_FOND( library, fond, count, aface );
    842 
    843      res_index++;
    844      count -= num_faces_in_fond;
    845    }
    846 
    847    CloseResFile( res_ref );
    848 
    849    if ( !error && aface && *aface )
    850    {
    851      (*aface)->num_faces  = num_faces_in_res;
    852      (*aface)->face_index = face_index;
    853    }
    854 
    855    return error;
    856  }
    857 
    858 
    859  /* documentation is in ftmac.h */
    860 
    861  FT_EXPORT_DEF( FT_Error )
    862  FT_New_Face_From_FOND( FT_Library  library,
    863                         Handle      fond,
    864                         FT_Long     face_index,
    865                         FT_Face*    aface )
    866  {
    867    short     have_sfnt, have_lwfn = 0;
    868    ResID     sfnt_id, fond_id;
    869    OSType    fond_type;
    870    Str255    fond_name;
    871    Str255    lwfn_file_name;
    872    UInt8     path_lwfn[PATH_MAX];
    873    OSErr     err;
    874    FT_Error  error = FT_Err_Ok;
    875 
    876 
    877    /* check of `library' and `aface' delayed to `FT_New_Face_From_XXX' */
    878 
    879    GetResInfo( fond, &fond_id, &fond_type, fond_name );
    880    if ( ResError() != noErr || fond_type != TTAG_FOND )
    881      return FT_THROW( Invalid_File_Format );
    882 
    883    parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index );
    884 
    885    if ( lwfn_file_name[0] )
    886    {
    887      ResFileRefNum  res;
    888 
    889 
    890      res = HomeResFile( fond );
    891      if ( noErr != ResError() )
    892        goto found_no_lwfn_file;
    893 
    894      {
    895        UInt8  path_fond[PATH_MAX];
    896        FSRef  ref;
    897 
    898 
    899        err = FSGetForkCBInfo( res, kFSInvalidVolumeRefNum,
    900                               NULL, NULL, NULL, &ref, NULL );
    901        if ( noErr != err )
    902          goto found_no_lwfn_file;
    903 
    904        err = FSRefMakePath( &ref, path_fond, sizeof ( path_fond ) );
    905        if ( noErr != err )
    906          goto found_no_lwfn_file;
    907 
    908        error = lookup_lwfn_by_fond( path_fond, lwfn_file_name,
    909                                     path_lwfn, sizeof ( path_lwfn ) );
    910        if ( !error )
    911          have_lwfn = 1;
    912      }
    913    }
    914 
    915    if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
    916      error = FT_New_Face_From_LWFN( library,
    917                                     path_lwfn,
    918                                     face_index,
    919                                     aface );
    920    else
    921      error = FT_THROW( Unknown_File_Format );
    922 
    923  found_no_lwfn_file:
    924    if ( have_sfnt && error )
    925      error = FT_New_Face_From_SFNT( library,
    926                                     sfnt_id,
    927                                     face_index,
    928                                     aface );
    929 
    930    return error;
    931  }
    932 
    933 
    934  /* Common function to load a new FT_Face from a resource file. */
    935  static FT_Error
    936  FT_New_Face_From_Resource( FT_Library    library,
    937                             const UInt8*  pathname,
    938                             FT_Long       face_index,
    939                             FT_Face*      aface )
    940  {
    941    OSType    file_type;
    942    FT_Error  error;
    943 
    944 
    945    /* LWFN is a (very) specific file format, check for it explicitly */
    946    file_type = get_file_type_from_path( pathname );
    947    if ( file_type == TTAG_LWFN )
    948      return FT_New_Face_From_LWFN( library, pathname, face_index, aface );
    949 
    950    /* Otherwise the file type doesn't matter (there are more than  */
    951    /* `FFIL' and `tfil').  Just try opening it as a font suitcase; */
    952    /* if it works, fine.                                           */
    953 
    954    error = FT_New_Face_From_Suitcase( library, pathname, face_index, aface );
    955    if ( error )
    956    {
    957      /* let it fall through to normal loader (.ttf, .otf, etc.); */
    958      /* we signal this by returning no error and no FT_Face      */
    959      *aface = NULL;
    960    }
    961 
    962    return FT_Err_Ok;
    963  }
    964 
    965 
    966  /**************************************************************************
    967   *
    968   * @Function:
    969   *   FT_New_Face
    970   *
    971   * @Description:
    972   *   This is the Mac-specific implementation of FT_New_Face.  In
    973   *   addition to the standard FT_New_Face() functionality, it also
    974   *   accepts pathnames to Mac suitcase files.  For further
    975   *   documentation see the original FT_New_Face() in freetype.h.
    976   */
    977  FT_EXPORT_DEF( FT_Error )
    978  FT_New_Face( FT_Library   library,
    979               const char*  pathname,
    980               FT_Long      face_index,
    981               FT_Face*     aface )
    982  {
    983    FT_Open_Args  args;
    984    FT_Error      error;
    985 
    986 
    987    /* test for valid `library' and `aface' delayed to FT_Open_Face() */
    988    if ( !pathname )
    989      return FT_THROW( Invalid_Argument );
    990 
    991    *aface = NULL;
    992 
    993    /* try resourcefork based font: LWFN, FFIL */
    994    error = FT_New_Face_From_Resource( library, (UInt8 *)pathname,
    995                                       face_index, aface );
    996    if ( error || *aface )
    997      return error;
    998 
    999    /* let it fall through to normal loader (.ttf, .otf, etc.) */
   1000    args.flags    = FT_OPEN_PATHNAME;
   1001    args.pathname = (char*)pathname;
   1002 
   1003    return FT_Open_Face( library, &args, face_index, aface );
   1004  }
   1005 
   1006 
   1007  /**************************************************************************
   1008   *
   1009   * @Function:
   1010   *   FT_New_Face_From_FSRef
   1011   *
   1012   * @Description:
   1013   *   FT_New_Face_From_FSRef is identical to FT_New_Face except it
   1014   *   accepts an FSRef instead of a path.
   1015   *
   1016   * This function is deprecated because Carbon data types (FSRef)
   1017   * are not cross-platform, and thus not suitable for the FreeType API.
   1018   */
   1019  FT_EXPORT_DEF( FT_Error )
   1020  FT_New_Face_From_FSRef( FT_Library    library,
   1021                          const FSRef*  ref,
   1022                          FT_Long       face_index,
   1023                          FT_Face*      aface )
   1024  {
   1025    FT_Error      error;
   1026    FT_Open_Args  args;
   1027 
   1028    OSErr  err;
   1029    UInt8  pathname[PATH_MAX];
   1030 
   1031 
   1032    /* check of `library' and `aface' delayed to */
   1033    /* `FT_New_Face_From_Resource'               */
   1034 
   1035    if ( !ref )
   1036      return FT_THROW( Invalid_Argument );
   1037 
   1038    err = FSRefMakePath( ref, pathname, sizeof ( pathname ) );
   1039    if ( err )
   1040      error = FT_THROW( Cannot_Open_Resource );
   1041 
   1042    error = FT_New_Face_From_Resource( library, pathname, face_index, aface );
   1043    if ( error || *aface )
   1044      return error;
   1045 
   1046    /* fallback to datafork font */
   1047    args.flags    = FT_OPEN_PATHNAME;
   1048    args.pathname = (char*)pathname;
   1049    return FT_Open_Face( library, &args, face_index, aface );
   1050  }
   1051 
   1052 
   1053  /**************************************************************************
   1054   *
   1055   * @Function:
   1056   *   FT_New_Face_From_FSSpec
   1057   *
   1058   * @Description:
   1059   *   FT_New_Face_From_FSSpec is identical to FT_New_Face except it
   1060   *   accepts an FSSpec instead of a path.
   1061   *
   1062   * This function is deprecated because FSSpec is deprecated in Mac OS X
   1063   */
   1064  FT_EXPORT_DEF( FT_Error )
   1065  FT_New_Face_From_FSSpec( FT_Library     library,
   1066                           const FSSpec*  spec,
   1067                           FT_Long        face_index,
   1068                           FT_Face*       aface )
   1069  {
   1070 #if ( __LP64__ ) || ( defined( MAC_OS_X_VERSION_10_5 ) && \
   1071      ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) )
   1072    FT_UNUSED( library );
   1073    FT_UNUSED( spec );
   1074    FT_UNUSED( face_index );
   1075    FT_UNUSED( aface );
   1076 
   1077    return FT_THROW( Unimplemented_Feature );
   1078 #else
   1079    FSRef  ref;
   1080 
   1081 
   1082    /* check of `library' and `aface' delayed to `FT_New_Face_From_FSRef' */
   1083 
   1084    if ( !spec || FSpMakeFSRef( spec, &ref ) != noErr )
   1085      return FT_THROW( Invalid_Argument );
   1086    else
   1087      return FT_New_Face_From_FSRef( library, &ref, face_index, aface );
   1088 #endif
   1089  }
   1090 
   1091 #else /* !FT_MACINTOSH */
   1092 
   1093  /* ANSI C doesn't like empty source files */
   1094  typedef int  ft_mac_dummy_;
   1095 
   1096 #endif /* !FT_MACINTOSH */
   1097 
   1098 
   1099 /* END */