Imported Upstream version 2.13.2
[platform/upstream/freetype2.git] / 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 */