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