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