Imported Upstream version 2.13.2
[platform/upstream/freetype2.git] / builds / mac / ftmac.c
1 /***************************************************************************/
2 /*                                                                         */
3 /*  ftmac.c                                                                */
4 /*                                                                         */
5 /*    Mac FOND support.  Written by just@letterror.com.                    */
6 /*  Heavily Fixed by mpsuzuki, George Williams and Sean McBride            */
7 /*                                                                         */
8 /*  Copyright (C) 1996-2023 by                                             */
9 /*  Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg.     */
10 /*                                                                         */
11 /*  This file is part of the FreeType project, and may only be used,       */
12 /*  modified, and distributed under the terms of the FreeType project      */
13 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
14 /*  this file you indicate that you have read the license and              */
15 /*  understand and accept it fully.                                        */
16 /*                                                                         */
17 /***************************************************************************/
18
19
20   /*
21     Notes
22
23     Mac suitcase files can (and often do!) contain multiple fonts.  To
24     support this I use the face_index argument of FT_(Open|New)_Face()
25     functions, and pretend the suitcase file is a collection.
26
27     Warning: fbit and NFNT bitmap resources are not supported yet.  In old
28     sfnt fonts, bitmap glyph data for each size is stored in each `NFNT'
29     resources instead of the `bdat' table in the sfnt resource.  Therefore,
30     face->num_fixed_sizes is set to 0, because bitmap data in `NFNT'
31     resource is unavailable at present.
32
33     The Mac FOND support works roughly like this:
34
35     - Check whether the offered stream points to a Mac suitcase file.  This
36       is done by checking the file type: it has to be 'FFIL' or 'tfil'.  The
37       stream that gets passed to our init_face() routine is a stdio stream,
38       which isn't usable for us, since the FOND resources live in the
39       resource fork.  So we just grab the stream->pathname field.
40
41     - Read the FOND resource into memory, then check whether there is a
42       TrueType font and/or(!) a Type 1 font available.
43
44     - If there is a Type 1 font available (as a separate `LWFN' file), read
45       its data into memory, massage it slightly so it becomes PFB data, wrap
46       it into a memory stream, load the Type 1 driver and delegate the rest
47       of the work to it by calling FT_Open_Face().  (XXX TODO: after this
48       has been done, the kerning data from the FOND resource should be
49       appended to the face: On the Mac there are usually no AFM files
50       available.  However, this is tricky since we need to map Mac char
51       codes to ps glyph names to glyph ID's...)
52
53     - If there is a TrueType font (an `sfnt' resource), read it into memory,
54       wrap it into a memory stream, load the TrueType driver and delegate
55       the rest of the work to it, by calling FT_Open_Face().
56
57     - Some suitcase fonts (notably Onyx) might point the `LWFN' file to
58       itself, even though it doesn't contains `POST' resources.  To handle
59       this special case without opening the file an extra time, we just
60       ignore errors from the `LWFN' and fallback to the `sfnt' if both are
61       available.
62   */
63
64
65 #include <freetype/freetype.h>
66 #include <freetype/tttags.h>
67 #include <freetype/internal/ftstream.h>
68 #include "ftbase.h"
69
70 #if defined( __GNUC__ ) || defined( __IBMC__ )
71   /* This is for Mac OS X.  Without redefinition, OS_INLINE */
72   /* expands to `static inline' which doesn't survive the   */
73   /* -ansi compilation flag of GCC.                         */
74 #if !HAVE_ANSI_OS_INLINE
75 #undef  OS_INLINE
76 #define OS_INLINE   static __inline__
77 #endif
78 #include <CoreServices/CoreServices.h>
79 #include <ApplicationServices/ApplicationServices.h>
80 #include <sys/syslimits.h> /* PATH_MAX */
81 #else
82 #include <Resources.h>
83 #include <Fonts.h>
84 #include <Endian.h>
85 #include <Errors.h>
86 #include <Files.h>
87 #include <TextUtils.h>
88 #endif
89
90 #ifndef PATH_MAX
91 #define PATH_MAX 1024 /* same with Mac OS X's syslimits.h */
92 #endif
93
94 #if defined( __MWERKS__ ) && !TARGET_RT_MAC_MACHO
95 #include <FSp_fopen.h>
96 #endif
97
98 #define FT_DEPRECATED_ATTRIBUTE
99
100 #include <freetype/ftmac.h>
101
102   /* undefine blocking-macros in ftmac.h */
103 #undef FT_GetFile_From_Mac_Name
104 #undef FT_GetFile_From_Mac_ATS_Name
105 #undef FT_New_Face_From_FOND
106 #undef FT_New_Face_From_FSSpec
107 #undef FT_New_Face_From_FSRef
108
109
110   /* FSSpec functions are deprecated since Mac OS X 10.4 */
111 #ifndef HAVE_FSSPEC
112 #if TARGET_API_MAC_OS8 || TARGET_API_MAC_CARBON
113 #define HAVE_FSSPEC  1
114 #else
115 #define HAVE_FSSPEC  0
116 #endif
117 #endif
118
119   /* most FSRef functions were introduced since Mac OS 9 */
120 #ifndef HAVE_FSREF
121 #if TARGET_API_MAC_OSX
122 #define HAVE_FSREF  1
123 #else
124 #define HAVE_FSREF  0
125 #endif
126 #endif
127
128   /* QuickDraw is deprecated since Mac OS X 10.4 */
129 #ifndef HAVE_QUICKDRAW_CARBON
130 #if TARGET_API_MAC_OS8 || TARGET_API_MAC_CARBON
131 #define HAVE_QUICKDRAW_CARBON  1
132 #else
133 #define HAVE_QUICKDRAW_CARBON  0
134 #endif
135 #endif
136
137   /* AppleTypeService is available since Mac OS X */
138 #ifndef HAVE_ATS
139 #if TARGET_API_MAC_OSX
140 #define HAVE_ATS  1
141 #ifndef kATSOptionFlagsUnRestrictedScope /* since Mac OS X 10.1 */
142 #define kATSOptionFlagsUnRestrictedScope kATSOptionFlagsDefault
143 #endif
144 #else
145 #define HAVE_ATS  0
146 #endif
147 #endif
148
149   /* `configure' checks the availability of `ResourceIndex' strictly */
150   /* and sets HAVE_TYPE_RESOURCE_INDEX to 1 or 0 always.  If it is   */
151   /* not set (e.g., a build without `configure'), the availability   */
152   /* is guessed from the SDK version.                                */
153 #ifndef HAVE_TYPE_RESOURCE_INDEX
154 #if !defined( MAC_OS_X_VERSION_10_5 ) || \
155     ( MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 )
156 #define HAVE_TYPE_RESOURCE_INDEX 0
157 #else
158 #define HAVE_TYPE_RESOURCE_INDEX 1
159 #endif
160 #endif /* !HAVE_TYPE_RESOURCE_INDEX */
161
162 #if ( HAVE_TYPE_RESOURCE_INDEX == 0 )
163 typedef short ResourceIndex;
164 #endif
165
166   /* Set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over
167      TrueType in case *both* are available (this is not common,
168      but it *is* possible). */
169 #ifndef PREFER_LWFN
170 #define PREFER_LWFN  1
171 #endif
172
173 #ifdef FT_MACINTOSH
174
175 #if !HAVE_QUICKDRAW_CARBON  /* QuickDraw is deprecated since Mac OS X 10.4 */
176
177   FT_EXPORT_DEF( FT_Error )
178   FT_GetFile_From_Mac_Name( const char*  fontName,
179                             FSSpec*      pathSpec,
180                             FT_Long*     face_index )
181   {
182     FT_UNUSED( fontName );
183     FT_UNUSED( pathSpec );
184     FT_UNUSED( face_index );
185
186     return FT_THROW( Unimplemented_Feature );
187   }
188
189 #else
190
191   FT_EXPORT_DEF( FT_Error )
192   FT_GetFile_From_Mac_Name( const char*  fontName,
193                             FSSpec*      pathSpec,
194                             FT_Long*     face_index )
195   {
196     OptionBits            options = kFMUseGlobalScopeOption;
197
198     FMFontFamilyIterator  famIter;
199     OSStatus              status = FMCreateFontFamilyIterator( NULL, NULL,
200                                                                options,
201                                                                &famIter );
202     FMFont                the_font = 0;
203     FMFontFamily          family   = 0;
204
205
206     if ( !fontName || !face_index )
207       return FT_THROW( Invalid_Argument );
208
209     *face_index = 0;
210     while ( status == 0 && !the_font )
211     {
212       status = FMGetNextFontFamily( &famIter, &family );
213       if ( status == 0 )
214       {
215         int                           stat2;
216         FMFontFamilyInstanceIterator  instIter;
217         Str255                        famNameStr;
218         char                          famName[256];
219
220
221         /* get the family name */
222         FMGetFontFamilyName( family, famNameStr );
223         CopyPascalStringToC( famNameStr, famName );
224
225         /* iterate through the styles */
226         FMCreateFontFamilyInstanceIterator( family, &instIter );
227
228         *face_index = 0;
229         stat2       = 0;
230
231         while ( stat2 == 0 && !the_font )
232         {
233           FMFontStyle  style;
234           FMFontSize   size;
235           FMFont       font;
236
237
238           stat2 = FMGetNextFontFamilyInstance( &instIter, &font,
239                                                &style, &size );
240           if ( stat2 == 0 && size == 0 )
241           {
242             char  fullName[256];
243
244
245             /* build up a complete face name */
246             ft_strcpy( fullName, famName );
247             if ( style & bold )
248               ft_strcat( fullName, " Bold" );
249             if ( style & italic )
250               ft_strcat( fullName, " Italic" );
251
252             /* compare with the name we are looking for */
253             if ( ft_strcmp( fullName, fontName ) == 0 )
254             {
255               /* found it! */
256               the_font = font;
257             }
258             else
259               ++(*face_index);
260           }
261         }
262
263         FMDisposeFontFamilyInstanceIterator( &instIter );
264       }
265     }
266
267     FMDisposeFontFamilyIterator( &famIter );
268
269     if ( the_font )
270     {
271       FMGetFontContainer( the_font, pathSpec );
272       return FT_Err_Ok;
273     }
274     else
275       return FT_THROW( Unknown_File_Format );
276   }
277
278 #endif /* HAVE_QUICKDRAW_CARBON */
279
280
281 #if HAVE_ATS
282
283   /* Private function.                                         */
284   /* The FSSpec type has been discouraged for a long time,     */
285   /* unfortunately an FSRef replacement API for                */
286   /* ATSFontGetFileSpecification() is only available in        */
287   /* Mac OS X 10.5 and later.                                  */
288   static OSStatus
289   FT_ATSFontGetFileReference( ATSFontRef  ats_font_id,
290                               FSRef*      ats_font_ref )
291   {
292     OSStatus  err;
293
294 #if !defined( MAC_OS_X_VERSION_10_5 ) || \
295     MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
296     FSSpec    spec;
297
298
299     err = ATSFontGetFileSpecification( ats_font_id, &spec );
300     if ( noErr == err )
301       err = FSpMakeFSRef( &spec, ats_font_ref );
302 #else
303     err = ATSFontGetFileReference( ats_font_id, ats_font_ref );
304 #endif
305
306     return err;
307   }
308
309
310   static FT_Error
311   FT_GetFileRef_From_Mac_ATS_Name( const char*  fontName,
312                                    FSRef*       ats_font_ref,
313                                    FT_Long*     face_index )
314   {
315     CFStringRef  cf_fontName;
316     ATSFontRef   ats_font_id;
317
318
319     *face_index = 0;
320
321     cf_fontName = CFStringCreateWithCString( NULL, fontName,
322                                              kCFStringEncodingMacRoman );
323     ats_font_id = ATSFontFindFromName( cf_fontName,
324                                        kATSOptionFlagsUnRestrictedScope );
325     CFRelease( cf_fontName );
326
327     if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
328       return FT_THROW( Unknown_File_Format );
329
330     if ( noErr != FT_ATSFontGetFileReference( ats_font_id, ats_font_ref ) )
331       return FT_THROW( Unknown_File_Format );
332
333     /* face_index calculation by searching preceding fontIDs */
334     /* with same FSRef                                       */
335     {
336       ATSFontRef  id2 = ats_font_id - 1;
337       FSRef       ref2;
338
339
340       while ( id2 > 0 )
341       {
342         if ( noErr != FT_ATSFontGetFileReference( id2, &ref2 ) )
343           break;
344         if ( noErr != FSCompareFSRefs( ats_font_ref, &ref2 ) )
345           break;
346
347         id2--;
348       }
349       *face_index = ats_font_id - ( id2 + 1 );
350     }
351
352     return FT_Err_Ok;
353   }
354
355 #endif
356
357 #if !HAVE_ATS
358
359   FT_EXPORT_DEF( FT_Error )
360   FT_GetFilePath_From_Mac_ATS_Name( const char*  fontName,
361                                     UInt8*       path,
362                                     UInt32       maxPathSize,
363                                     FT_Long*     face_index )
364   {
365     FT_UNUSED( fontName );
366     FT_UNUSED( path );
367     FT_UNUSED( maxPathSize );
368     FT_UNUSED( face_index );
369
370     return FT_THROW( Unimplemented_Feature );
371   }
372
373 #else
374
375   FT_EXPORT_DEF( FT_Error )
376   FT_GetFilePath_From_Mac_ATS_Name( const char*  fontName,
377                                     UInt8*       path,
378                                     UInt32       maxPathSize,
379                                     FT_Long*     face_index )
380   {
381     FSRef     ref;
382     FT_Error  err;
383
384
385     err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index );
386     if ( err )
387       return err;
388
389     if ( noErr != FSRefMakePath( &ref, path, maxPathSize ) )
390       return FT_THROW( Unknown_File_Format );
391
392     return FT_Err_Ok;
393   }
394
395 #endif /* HAVE_ATS */
396
397
398 #if !HAVE_FSSPEC || !HAVE_ATS
399
400   FT_EXPORT_DEF( FT_Error )
401   FT_GetFile_From_Mac_ATS_Name( const char*  fontName,
402                                 FSSpec*      pathSpec,
403                                 FT_Long*     face_index )
404   {
405     FT_UNUSED( fontName );
406     FT_UNUSED( pathSpec );
407     FT_UNUSED( face_index );
408
409     return FT_THROW( Unimplemented_Feature );
410   }
411
412 #else
413
414   /* This function is deprecated because FSSpec is deprecated in Mac OS X. */
415   FT_EXPORT_DEF( FT_Error )
416   FT_GetFile_From_Mac_ATS_Name( const char*  fontName,
417                                 FSSpec*      pathSpec,
418                                 FT_Long*     face_index )
419   {
420     FSRef     ref;
421     FT_Error  err;
422
423
424     err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index );
425     if ( err )
426       return err;
427
428     if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, NULL, NULL,
429                                     pathSpec, NULL ) )
430       return FT_THROW( Unknown_File_Format );
431
432     return FT_Err_Ok;
433   }
434
435 #endif
436
437
438 #if defined( __MWERKS__ ) && !TARGET_RT_MAC_MACHO
439
440 #define STREAM_FILE( stream )  ( (FT_FILE*)stream->descriptor.pointer )
441
442
443   FT_CALLBACK_DEF( void )
444   ft_FSp_stream_close( FT_Stream  stream )
445   {
446     ft_fclose( STREAM_FILE( stream ) );
447
448     stream->descriptor.pointer = NULL;
449     stream->size               = 0;
450     stream->base               = NULL;
451   }
452
453
454   FT_CALLBACK_DEF( unsigned long )
455   ft_FSp_stream_io( FT_Stream       stream,
456                     unsigned long   offset,
457                     unsigned char*  buffer,
458                     unsigned long   count )
459   {
460     FT_FILE*  file;
461
462
463     file = STREAM_FILE( stream );
464
465     ft_fseek( file, offset, SEEK_SET );
466
467     return (unsigned long)ft_fread( buffer, 1, count, file );
468   }
469
470 #endif  /* __MWERKS__ && !TARGET_RT_MAC_MACHO */
471
472
473 #if HAVE_FSSPEC && !HAVE_FSREF
474
475   /* isDirectory is a dummy to synchronize API with FSPathMakeRef() */
476   static OSErr
477   FT_FSPathMakeSpec( const UInt8*  pathname,
478                      FSSpec*       spec_p,
479                      Boolean       isDirectory )
480   {
481     const char  *p, *q;
482     short       vRefNum;
483     long        dirID;
484     Str255      nodeName;
485     OSErr       err;
486     FT_UNUSED( isDirectory );
487
488
489     p = q = (const char *)pathname;
490     dirID   = 0;
491     vRefNum = 0;
492
493     while ( 1 )
494     {
495       int  len = ft_strlen( p );
496
497
498       if ( len > 255 )
499         len = 255;
500
501       q = p + len;
502
503       if ( q == p )
504         return 0;
505
506       if ( 255 < ft_strlen( (char *)pathname ) )
507       {
508         while ( p < q && *q != ':' )
509           q--;
510       }
511
512       if ( p < q )
513         *(char *)nodeName = q - p;
514       else if ( ft_strlen( p ) < 256 )
515         *(char *)nodeName = ft_strlen( p );
516       else
517         return errFSNameTooLong;
518
519       ft_strncpy( (char *)nodeName + 1, (char *)p, *(char *)nodeName );
520       err = FSMakeFSSpec( vRefNum, dirID, nodeName, spec_p );
521       if ( err || '\0' == *q )
522         return err;
523
524       vRefNum = spec_p->vRefNum;
525       dirID   = spec_p->parID;
526
527       p = q;
528     }
529   }
530
531
532   static OSErr
533   FT_FSpMakePath( const FSSpec*  spec_p,
534                   UInt8*         path,
535                   UInt32         maxPathSize )
536   {
537     OSErr   err;
538     FSSpec  spec = *spec_p;
539     short   vRefNum;
540     long    dirID;
541     Str255  parDir_name;
542
543
544     FT_MEM_SET( path, 0, maxPathSize );
545     while ( 1 )
546     {
547       int             child_namelen = ft_strlen( (char *)path );
548       unsigned char   node_namelen  = spec.name[0];
549       unsigned char*  node_name     = spec.name + 1;
550
551
552       if ( node_namelen + child_namelen > maxPathSize )
553         return errFSNameTooLong;
554
555       FT_MEM_MOVE( path + node_namelen + 1, path, child_namelen );
556       FT_MEM_COPY( path, node_name, node_namelen );
557       if ( child_namelen > 0 )
558         path[node_namelen] = ':';
559
560       vRefNum        = spec.vRefNum;
561       dirID          = spec.parID;
562       parDir_name[0] = '\0';
563       err = FSMakeFSSpec( vRefNum, dirID, parDir_name, &spec );
564       if ( noErr != err || dirID == spec.parID )
565         break;
566     }
567     return noErr;
568   }
569
570 #endif /* HAVE_FSSPEC && !HAVE_FSREF */
571
572
573   static OSErr
574   FT_FSPathMakeRes( const UInt8*    pathname,
575                     ResFileRefNum*  res )
576   {
577
578 #if HAVE_FSREF
579
580     OSErr  err;
581     FSRef  ref;
582
583
584     if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
585       return FT_THROW( Cannot_Open_Resource );
586
587     /* at present, no support for dfont format */
588     err = FSOpenResourceFile( &ref, 0, NULL, fsRdPerm, res );
589     if ( noErr == err )
590       return err;
591
592     /* fallback to original resource-fork font */
593     *res = FSOpenResFile( &ref, fsRdPerm );
594     err  = ResError();
595
596 #else
597
598     OSErr   err;
599     FSSpec  spec;
600
601
602     if ( noErr != FT_FSPathMakeSpec( pathname, &spec, FALSE ) )
603       return FT_THROW( Cannot_Open_Resource );
604
605     /* at present, no support for dfont format without FSRef */
606     /* (see above), try original resource-fork font          */
607     *res = FSpOpenResFile( &spec, fsRdPerm );
608     err  = ResError();
609
610 #endif /* HAVE_FSREF */
611
612     return err;
613   }
614
615
616   /* Return the file type for given pathname */
617   static OSType
618   get_file_type_from_path( const UInt8*  pathname )
619   {
620
621 #if HAVE_FSREF
622
623     FSRef          ref;
624     FSCatalogInfo  info;
625
626
627     if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
628       return ( OSType ) 0;
629
630     if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoFinderInfo, &info,
631                                     NULL, NULL, NULL ) )
632       return ( OSType ) 0;
633
634     return ((FInfo *)(info.finderInfo))->fdType;
635
636 #else
637
638     FSSpec  spec;
639     FInfo   finfo;
640
641
642     if ( noErr != FT_FSPathMakeSpec( pathname, &spec, FALSE ) )
643       return ( OSType ) 0;
644
645     if ( noErr != FSpGetFInfo( &spec, &finfo ) )
646       return ( OSType ) 0;
647
648     return finfo.fdType;
649
650 #endif /* HAVE_FSREF */
651
652   }
653
654
655   /* Given a PostScript font name, create the Macintosh LWFN file name. */
656   static void
657   create_lwfn_name( char*   ps_name,
658                     Str255  lwfn_file_name )
659   {
660     int       max = 5, count = 0;
661     FT_Byte*  p = lwfn_file_name;
662     FT_Byte*  q = (FT_Byte*)ps_name;
663
664
665     lwfn_file_name[0] = 0;
666
667     while ( *q )
668     {
669       if ( ft_isupper( *q ) )
670       {
671         if ( count )
672           max = 3;
673         count = 0;
674       }
675       if ( count < max && ( ft_isalnum( *q ) || *q == '_' ) )
676       {
677         *++p = *q;
678         lwfn_file_name[0]++;
679         count++;
680       }
681       q++;
682     }
683   }
684
685
686   static short
687   count_faces_sfnt( char*  fond_data )
688   {
689     /* The count is 1 greater than the value in the FOND.  */
690     /* Isn't that cute? :-)                                */
691
692     return EndianS16_BtoN( *( (short*)( fond_data +
693                                         sizeof ( FamRec ) ) ) ) + 1;
694   }
695
696
697   static short
698   count_faces_scalable( char*  fond_data )
699   {
700     AsscEntry*  assoc;
701     short       i, face, face_all;
702
703
704     face_all = EndianS16_BtoN( *( (short *)( fond_data +
705                                              sizeof ( FamRec ) ) ) ) + 1;
706     assoc    = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
707     face     = 0;
708
709     for ( i = 0; i < face_all; i++ )
710     {
711       if ( 0 == EndianS16_BtoN( assoc[i].fontSize ) )
712         face++;
713     }
714     return face;
715   }
716
717
718   /* Look inside the FOND data, answer whether there should be an SFNT
719      resource, and answer the name of a possible LWFN Type 1 file.
720
721      Thanks to Paul Miller (paulm@profoundeffects.com) for the fix
722      to load a face OTHER than the first one in the FOND!
723   */
724
725   static void
726   parse_fond( char*   fond_data,
727               short*  have_sfnt,
728               ResID*  sfnt_id,
729               Str255  lwfn_file_name,
730               short   face_index )
731   {
732     AsscEntry*  assoc;
733     AsscEntry*  base_assoc;
734     FamRec*     fond;
735
736
737     *sfnt_id          = 0;
738     *have_sfnt        = 0;
739     lwfn_file_name[0] = 0;
740
741     fond       = (FamRec*)fond_data;
742     assoc      = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
743     base_assoc = assoc;
744
745     /* the maximum faces in a FOND is 48, size of StyleTable.indexes[] */
746     if ( 47 < face_index )
747       return;
748
749     /* Let's do a little range checking before we get too excited here */
750     if ( face_index < count_faces_sfnt( fond_data ) )
751     {
752       assoc += face_index;        /* add on the face_index! */
753
754       /* if the face at this index is not scalable,
755          fall back to the first one (old behavior) */
756       if ( EndianS16_BtoN( assoc->fontSize ) == 0 )
757       {
758         *have_sfnt = 1;
759         *sfnt_id   = EndianS16_BtoN( assoc->fontID );
760       }
761       else if ( base_assoc->fontSize == 0 )
762       {
763         *have_sfnt = 1;
764         *sfnt_id   = EndianS16_BtoN( base_assoc->fontID );
765       }
766     }
767
768     if ( EndianS32_BtoN( fond->ffStylOff ) )
769     {
770       unsigned char*  p = (unsigned char*)fond_data;
771       StyleTable*     style;
772       unsigned short  string_count;
773       char            ps_name[256];
774       unsigned char*  names[64];
775       int             i;
776
777
778       p += EndianS32_BtoN( fond->ffStylOff );
779       style = (StyleTable*)p;
780       p += sizeof ( StyleTable );
781       string_count = EndianS16_BtoN( *(short*)(p) );
782       string_count = FT_MIN( 64, string_count );
783       p += sizeof ( short );
784
785       for ( i = 0; i < string_count; i++ )
786       {
787         names[i] = p;
788         p       += names[i][0];
789         p++;
790       }
791
792       {
793         size_t  ps_name_len = (size_t)names[0][0];
794
795
796         if ( ps_name_len != 0 )
797         {
798           ft_memcpy(ps_name, names[0] + 1, ps_name_len);
799           ps_name[ps_name_len] = 0;
800         }
801         if ( style->indexes[face_index] > 1 &&
802              style->indexes[face_index] <= string_count )
803         {
804           unsigned char*  suffixes = names[style->indexes[face_index] - 1];
805
806
807           for ( i = 1; i <= suffixes[0]; i++ )
808           {
809             unsigned char*  s;
810             size_t          j = suffixes[i] - 1;
811
812
813             if ( j < string_count && ( s = names[j] ) != NULL )
814             {
815               size_t  s_len = (size_t)s[0];
816
817
818               if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) )
819               {
820                 ft_memcpy( ps_name + ps_name_len, s + 1, s_len );
821                 ps_name_len += s_len;
822                 ps_name[ps_name_len] = 0;
823               }
824             }
825           }
826         }
827       }
828
829       create_lwfn_name( ps_name, lwfn_file_name );
830     }
831   }
832
833
834   static  FT_Error
835   lookup_lwfn_by_fond( const UInt8*      path_fond,
836                        ConstStr255Param  base_lwfn,
837                        UInt8*            path_lwfn,
838                        int               path_size )
839   {
840
841 #if HAVE_FSREF
842
843     FSRef  ref, par_ref;
844     int    dirname_len;
845
846
847     /* Pathname for FSRef can be in various formats: HFS, HFS+, and POSIX. */
848     /* We should not extract parent directory by string manipulation.      */
849
850     if ( noErr != FSPathMakeRef( path_fond, &ref, FALSE ) )
851       return FT_THROW( Invalid_Argument );
852
853     if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
854                                     NULL, NULL, NULL, &par_ref ) )
855       return FT_THROW( Invalid_Argument );
856
857     if ( noErr != FSRefMakePath( &par_ref, path_lwfn, path_size ) )
858       return FT_THROW( Invalid_Argument );
859
860     if ( ft_strlen( (char *)path_lwfn ) + 1 + base_lwfn[0] > path_size )
861       return FT_THROW( Invalid_Argument );
862
863     /* now we have absolute dirname in path_lwfn */
864     if ( path_lwfn[0] == '/' )
865       ft_strcat( (char *)path_lwfn, "/" );
866     else
867       ft_strcat( (char *)path_lwfn, ":" );
868
869     dirname_len = ft_strlen( (char *)path_lwfn );
870     ft_strcat( (char *)path_lwfn, (char *)base_lwfn + 1 );
871     path_lwfn[dirname_len + base_lwfn[0]] = '\0';
872
873     if ( noErr != FSPathMakeRef( path_lwfn, &ref, FALSE ) )
874       return FT_THROW( Cannot_Open_Resource );
875
876     if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
877                                     NULL, NULL, NULL, NULL ) )
878       return FT_THROW( Cannot_Open_Resource );
879
880     return FT_Err_Ok;
881
882 #else
883
884     int     i;
885     FSSpec  spec;
886
887
888     /* pathname for FSSpec is always HFS format */
889     if ( ft_strlen( (char *)path_fond ) > path_size )
890       return FT_THROW( Invalid_Argument );
891
892     ft_strcpy( (char *)path_lwfn, (char *)path_fond );
893
894     i = ft_strlen( (char *)path_lwfn ) - 1;
895     while ( i > 0 && ':' != path_lwfn[i] )
896       i--;
897
898     if ( i + 1 + base_lwfn[0] > path_size )
899       return FT_THROW( Invalid_Argument );
900
901     if ( ':' == path_lwfn[i] )
902     {
903       ft_strcpy( (char *)path_lwfn + i + 1, (char *)base_lwfn + 1 );
904       path_lwfn[i + 1 + base_lwfn[0]] = '\0';
905     }
906     else
907     {
908       ft_strcpy( (char *)path_lwfn, (char *)base_lwfn + 1 );
909       path_lwfn[base_lwfn[0]] = '\0';
910     }
911
912     if ( noErr != FT_FSPathMakeSpec( path_lwfn, &spec, FALSE ) )
913       return FT_THROW( Cannot_Open_Resource );
914
915     return FT_Err_Ok;
916
917 #endif /* HAVE_FSREF */
918
919   }
920
921
922   static short
923   count_faces( Handle        fond,
924                const UInt8*  pathname )
925   {
926     ResID     sfnt_id;
927     short     have_sfnt, have_lwfn;
928     Str255    lwfn_file_name;
929     UInt8     buff[PATH_MAX];
930     FT_Error  err;
931     short     num_faces;
932
933
934     have_sfnt = have_lwfn = 0;
935
936     HLock( fond );
937     parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 );
938
939     if ( lwfn_file_name[0] )
940     {
941       err = lookup_lwfn_by_fond( pathname, lwfn_file_name,
942                                  buff, sizeof ( buff ) );
943       if ( !err )
944         have_lwfn = 1;
945     }
946
947     if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
948       num_faces = 1;
949     else
950       num_faces = count_faces_scalable( *fond );
951
952     HUnlock( fond );
953     return num_faces;
954   }
955
956
957   /* Read Type 1 data from the POST resources inside the LWFN file,
958      return a PFB buffer.  This is somewhat convoluted because the FT2
959      PFB parser wants the ASCII header as one chunk, and the LWFN
960      chunks are often not organized that way, so we glue chunks
961      of the same type together. */
962   static FT_Error
963   read_lwfn( FT_Memory      memory,
964              ResFileRefNum  res,
965              FT_Byte**      pfb_data,
966              FT_ULong*      size )
967   {
968     FT_Error       error = FT_Err_Ok;
969     ResID          res_id;
970     unsigned char  *buffer, *p, *size_p = NULL;
971     FT_ULong       total_size = 0;
972     FT_ULong       old_total_size = 0;
973     FT_ULong       post_size, pfb_chunk_size;
974     Handle         post_data;
975     char           code, last_code;
976
977
978     UseResFile( res );
979
980     /* First pass: load all POST resources, and determine the size of */
981     /* the output buffer.                                             */
982     res_id    = 501;
983     last_code = -1;
984
985     for (;;)
986     {
987       post_data = Get1Resource( TTAG_POST, res_id++ );
988       if ( post_data == NULL )
989         break;  /* we are done */
990
991       code = (*post_data)[0];
992
993       if ( code != last_code )
994       {
995         if ( code == 5 )
996           total_size += 2; /* just the end code */
997         else
998           total_size += 6; /* code + 4 bytes chunk length */
999       }
1000
1001       total_size += GetHandleSize( post_data ) - 2;
1002       last_code = code;
1003
1004       /* detect integer overflows */
1005       if ( total_size < old_total_size )
1006       {
1007         error = FT_ERR( Array_Too_Large );
1008         goto Error;
1009       }
1010
1011       old_total_size = total_size;
1012     }
1013
1014     if ( FT_QALLOC( buffer, (FT_Long)total_size ) )
1015       goto Error;
1016
1017     /* Second pass: append all POST data to the buffer, add PFB fields. */
1018     /* Glue all consecutive chunks of the same type together.           */
1019     p              = buffer;
1020     res_id         = 501;
1021     last_code      = -1;
1022     pfb_chunk_size = 0;
1023
1024     for (;;)
1025     {
1026       post_data = Get1Resource( TTAG_POST, res_id++ );
1027       if ( post_data == NULL )
1028         break;  /* we are done */
1029
1030       post_size = (FT_ULong)GetHandleSize( post_data ) - 2;
1031       code = (*post_data)[0];
1032
1033       if ( code != last_code )
1034       {
1035         if ( last_code != -1 )
1036         {
1037           /* we are done adding a chunk, fill in the size field */
1038           if ( size_p != NULL )
1039           {
1040             *size_p++ = (FT_Byte)(   pfb_chunk_size         & 0xFF );
1041             *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8  ) & 0xFF );
1042             *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF );
1043             *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF );
1044           }
1045           pfb_chunk_size = 0;
1046         }
1047
1048         *p++ = 0x80;
1049         if ( code == 5 )
1050           *p++ = 0x03;  /* the end */
1051         else if ( code == 2 )
1052           *p++ = 0x02;  /* binary segment */
1053         else
1054           *p++ = 0x01;  /* ASCII segment */
1055
1056         if ( code != 5 )
1057         {
1058           size_p = p;   /* save for later */
1059           p += 4;       /* make space for size field */
1060         }
1061       }
1062
1063       ft_memcpy( p, *post_data + 2, post_size );
1064       pfb_chunk_size += post_size;
1065       p += post_size;
1066       last_code = code;
1067     }
1068
1069     *pfb_data = buffer;
1070     *size = total_size;
1071
1072   Error:
1073     CloseResFile( res );
1074     return error;
1075   }
1076
1077
1078   /* Create a new FT_Face from a file spec to an LWFN file. */
1079   static FT_Error
1080   FT_New_Face_From_LWFN( FT_Library    library,
1081                          const UInt8*  pathname,
1082                          FT_Long       face_index,
1083                          FT_Face*      aface )
1084   {
1085     FT_Byte*       pfb_data;
1086     FT_ULong       pfb_size;
1087     FT_Error       error;
1088     ResFileRefNum  res;
1089
1090
1091     if ( noErr != FT_FSPathMakeRes( pathname, &res ) )
1092       return FT_THROW( Cannot_Open_Resource );
1093
1094     pfb_data = NULL;
1095     pfb_size = 0;
1096     error = read_lwfn( library->memory, res, &pfb_data, &pfb_size );
1097     CloseResFile( res ); /* PFB is already loaded, useless anymore */
1098     if ( error )
1099       return error;
1100
1101     return open_face_from_buffer( library,
1102                                   pfb_data,
1103                                   pfb_size,
1104                                   face_index,
1105                                   "type1",
1106                                   aface );
1107   }
1108
1109
1110   /* Create a new FT_Face from an SFNT resource, specified by res ID. */
1111   static FT_Error
1112   FT_New_Face_From_SFNT( FT_Library  library,
1113                          ResID       sfnt_id,
1114                          FT_Long     face_index,
1115                          FT_Face*    aface )
1116   {
1117     Handle     sfnt = NULL;
1118     FT_Byte*   sfnt_data;
1119     size_t     sfnt_size;
1120     FT_Error   error  = FT_Err_Ok;
1121     FT_Memory  memory = library->memory;
1122     int        is_cff, is_sfnt_ps;
1123
1124
1125     sfnt = GetResource( TTAG_sfnt, sfnt_id );
1126     if ( sfnt == NULL )
1127       return FT_THROW( Invalid_Handle );
1128
1129     sfnt_size = (FT_ULong)GetHandleSize( sfnt );
1130     if ( FT_QALLOC( sfnt_data, (FT_Long)sfnt_size ) )
1131     {
1132       ReleaseResource( sfnt );
1133       return error;
1134     }
1135
1136     HLock( sfnt );
1137     ft_memcpy( sfnt_data, *sfnt, sfnt_size );
1138     HUnlock( sfnt );
1139     ReleaseResource( sfnt );
1140
1141     is_cff     = sfnt_size > 4 && !ft_memcmp( sfnt_data, "OTTO", 4 );
1142     is_sfnt_ps = sfnt_size > 4 && !ft_memcmp( sfnt_data, "typ1", 4 );
1143
1144     if ( is_sfnt_ps )
1145     {
1146       FT_Stream  stream;
1147
1148
1149       if ( FT_NEW( stream ) )
1150         goto Try_OpenType;
1151
1152       FT_Stream_OpenMemory( stream, sfnt_data, sfnt_size );
1153       if ( !open_face_PS_from_sfnt_stream( library,
1154                                            stream,
1155                                            face_index,
1156                                            0, NULL,
1157                                            aface ) )
1158       {
1159         FT_Stream_Close( stream );
1160         FT_FREE( stream );
1161         FT_FREE( sfnt_data );
1162         goto Exit;
1163       }
1164
1165       FT_FREE( stream );
1166     }
1167   Try_OpenType:
1168     error = open_face_from_buffer( library,
1169                                    sfnt_data,
1170                                    sfnt_size,
1171                                    face_index,
1172                                    is_cff ? "cff" : "truetype",
1173                                    aface );
1174   Exit:
1175     return error;
1176   }
1177
1178
1179   /* Create a new FT_Face from a file spec to a suitcase file. */
1180   static FT_Error
1181   FT_New_Face_From_Suitcase( FT_Library    library,
1182                              const UInt8*  pathname,
1183                              FT_Long       face_index,
1184                              FT_Face*      aface )
1185   {
1186     FT_Error       error = FT_ERR( Cannot_Open_Resource );
1187     ResFileRefNum  res_ref;
1188     ResourceIndex  res_index;
1189     Handle         fond;
1190     short          num_faces_in_res;
1191
1192
1193     if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) )
1194       return FT_THROW( Cannot_Open_Resource );
1195
1196     UseResFile( res_ref );
1197     if ( ResError() )
1198       return FT_THROW( Cannot_Open_Resource );
1199
1200     num_faces_in_res = 0;
1201     for ( res_index = 1; ; ++res_index )
1202     {
1203       short  num_faces_in_fond;
1204
1205
1206       fond = Get1IndResource( TTAG_FOND, res_index );
1207       if ( ResError() )
1208         break;
1209
1210       num_faces_in_fond  = count_faces( fond, pathname );
1211       num_faces_in_res  += num_faces_in_fond;
1212
1213       if ( 0 <= face_index && face_index < num_faces_in_fond && error )
1214         error = FT_New_Face_From_FOND( library, fond, face_index, aface );
1215
1216       face_index -= num_faces_in_fond;
1217     }
1218
1219     CloseResFile( res_ref );
1220     if ( !error && aface )
1221       (*aface)->num_faces = num_faces_in_res;
1222     return error;
1223   }
1224
1225
1226   /* documentation is in ftmac.h */
1227
1228   FT_EXPORT_DEF( FT_Error )
1229   FT_New_Face_From_FOND( FT_Library  library,
1230                          Handle      fond,
1231                          FT_Long     face_index,
1232                          FT_Face*    aface )
1233   {
1234     short     have_sfnt, have_lwfn = 0;
1235     ResID     sfnt_id, fond_id;
1236     OSType    fond_type;
1237     Str255    fond_name;
1238     Str255    lwfn_file_name;
1239     UInt8     path_lwfn[PATH_MAX];
1240     OSErr     err;
1241     FT_Error  error = FT_Err_Ok;
1242
1243
1244     /* test for valid `aface' and `library' delayed to */
1245     /* `FT_New_Face_From_XXX'                          */
1246
1247     GetResInfo( fond, &fond_id, &fond_type, fond_name );
1248     if ( ResError() != noErr || fond_type != TTAG_FOND )
1249       return FT_THROW( Invalid_File_Format );
1250
1251     HLock( fond );
1252     parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index );
1253     HUnlock( fond );
1254
1255     if ( lwfn_file_name[0] )
1256     {
1257       ResFileRefNum  res;
1258
1259
1260       res = HomeResFile( fond );
1261       if ( noErr != ResError() )
1262         goto found_no_lwfn_file;
1263
1264 #if HAVE_FSREF
1265
1266       {
1267         UInt8  path_fond[PATH_MAX];
1268         FSRef  ref;
1269
1270
1271         err = FSGetForkCBInfo( res, kFSInvalidVolumeRefNum,
1272                                NULL, NULL, NULL, &ref, NULL );
1273         if ( noErr != err )
1274           goto found_no_lwfn_file;
1275
1276         err = FSRefMakePath( &ref, path_fond, sizeof ( path_fond ) );
1277         if ( noErr != err )
1278           goto found_no_lwfn_file;
1279
1280         error = lookup_lwfn_by_fond( path_fond, lwfn_file_name,
1281                                      path_lwfn, sizeof ( path_lwfn ) );
1282         if ( !error )
1283           have_lwfn = 1;
1284       }
1285
1286 #elif HAVE_FSSPEC
1287
1288       {
1289         UInt8     path_fond[PATH_MAX];
1290         FCBPBRec  pb;
1291         Str255    fond_file_name;
1292         FSSpec    spec;
1293
1294
1295         FT_MEM_SET( &spec, 0, sizeof ( FSSpec ) );
1296         FT_MEM_SET( &pb,   0, sizeof ( FCBPBRec ) );
1297
1298         pb.ioNamePtr = fond_file_name;
1299         pb.ioVRefNum = 0;
1300         pb.ioRefNum  = res;
1301         pb.ioFCBIndx = 0;
1302
1303         err = PBGetFCBInfoSync( &pb );
1304         if ( noErr != err )
1305           goto found_no_lwfn_file;
1306
1307         err = FSMakeFSSpec( pb.ioFCBVRefNum, pb.ioFCBParID,
1308                             fond_file_name, &spec );
1309         if ( noErr != err )
1310           goto found_no_lwfn_file;
1311
1312         err = FT_FSpMakePath( &spec, path_fond, sizeof ( path_fond ) );
1313         if ( noErr != err )
1314           goto found_no_lwfn_file;
1315
1316         error = lookup_lwfn_by_fond( path_fond, lwfn_file_name,
1317                                      path_lwfn, sizeof ( path_lwfn ) );
1318         if ( !error )
1319           have_lwfn = 1;
1320       }
1321
1322 #endif /* HAVE_FSREF, HAVE_FSSPEC */
1323
1324     }
1325
1326     if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
1327       error = FT_New_Face_From_LWFN( library,
1328                                      path_lwfn,
1329                                      face_index,
1330                                      aface );
1331     else
1332       error = FT_ERR( Unknown_File_Format );
1333
1334   found_no_lwfn_file:
1335     if ( have_sfnt && error )
1336       error = FT_New_Face_From_SFNT( library,
1337                                      sfnt_id,
1338                                      face_index,
1339                                      aface );
1340
1341     return error;
1342   }
1343
1344
1345   /* Common function to load a new FT_Face from a resource file. */
1346   static FT_Error
1347   FT_New_Face_From_Resource( FT_Library    library,
1348                              const UInt8*  pathname,
1349                              FT_Long       face_index,
1350                              FT_Face*      aface )
1351   {
1352     OSType    file_type;
1353     FT_Error  error;
1354
1355
1356     /* LWFN is a (very) specific file format, check for it explicitly */
1357     file_type = get_file_type_from_path( pathname );
1358     if ( file_type == TTAG_LWFN )
1359       return FT_New_Face_From_LWFN( library, pathname, face_index, aface );
1360
1361     /* Otherwise the file type doesn't matter (there are more than  */
1362     /* `FFIL' and `tfil').  Just try opening it as a font suitcase; */
1363     /* if it works, fine.                                           */
1364
1365     error = FT_New_Face_From_Suitcase( library, pathname, face_index, aface );
1366     if ( !error )
1367       return error;
1368
1369     /* let it fall through to normal loader (.ttf, .otf, etc.); */
1370     /* we signal this by returning no error and no FT_Face      */
1371     *aface = NULL;
1372     return 0;
1373   }
1374
1375
1376   /*************************************************************************/
1377   /*                                                                       */
1378   /* <Function>                                                            */
1379   /*    FT_New_Face                                                        */
1380   /*                                                                       */
1381   /* <Description>                                                         */
1382   /*    This is the Mac-specific implementation of FT_New_Face.  In        */
1383   /*    addition to the standard FT_New_Face() functionality, it also      */
1384   /*    accepts pathnames to Mac suitcase files.  For further              */
1385   /*    documentation see the original FT_New_Face() in freetype.h.        */
1386   /*                                                                       */
1387   FT_EXPORT_DEF( FT_Error )
1388   FT_New_Face( FT_Library   library,
1389                const char*  pathname,
1390                FT_Long      face_index,
1391                FT_Face*     aface )
1392   {
1393     FT_Open_Args  args;
1394     FT_Error      error;
1395
1396
1397     /* test for valid `library' and `aface' delayed to FT_Open_Face() */
1398     if ( !pathname )
1399       return FT_THROW( Invalid_Argument );
1400
1401     *aface = NULL;
1402
1403     /* try resourcefork based font: LWFN, FFIL */
1404     error = FT_New_Face_From_Resource( library, (UInt8 *)pathname,
1405                                        face_index, aface );
1406     if ( error || *aface )
1407       return error;
1408
1409     /* let it fall through to normal loader (.ttf, .otf, etc.) */
1410     args.flags    = FT_OPEN_PATHNAME;
1411     args.pathname = (char*)pathname;
1412     return FT_Open_Face( library, &args, face_index, aface );
1413   }
1414
1415
1416   /*************************************************************************/
1417   /*                                                                       */
1418   /* <Function>                                                            */
1419   /*    FT_New_Face_From_FSRef                                             */
1420   /*                                                                       */
1421   /* <Description>                                                         */
1422   /*    FT_New_Face_From_FSRef is identical to FT_New_Face except it       */
1423   /*    accepts an FSRef instead of a path.                                */
1424   /*                                                                       */
1425   /* This function is deprecated because Carbon data types (FSRef)         */
1426   /* are not cross-platform, and thus not suitable for the FreeType API.   */
1427   FT_EXPORT_DEF( FT_Error )
1428   FT_New_Face_From_FSRef( FT_Library    library,
1429                           const FSRef*  ref,
1430                           FT_Long       face_index,
1431                           FT_Face*      aface )
1432   {
1433
1434 #if !HAVE_FSREF
1435
1436     FT_UNUSED( library );
1437     FT_UNUSED( ref );
1438     FT_UNUSED( face_index );
1439     FT_UNUSED( aface );
1440
1441     return FT_THROW( Unimplemented_Feature );
1442
1443 #else
1444
1445     FT_Error      error;
1446     FT_Open_Args  args;
1447     OSErr   err;
1448     UInt8   pathname[PATH_MAX];
1449
1450
1451     /* test for valid `library' and `aface' delayed to `FT_Open_Face' */
1452
1453     if ( !ref )
1454       return FT_THROW( Invalid_Argument );
1455
1456     err = FSRefMakePath( ref, pathname, sizeof ( pathname ) );
1457     if ( err )
1458       error = FT_ERR( Cannot_Open_Resource );
1459
1460     error = FT_New_Face_From_Resource( library, pathname, face_index, aface );
1461     if ( error || *aface )
1462       return error;
1463
1464     /* fallback to datafork font */
1465     args.flags    = FT_OPEN_PATHNAME;
1466     args.pathname = (char*)pathname;
1467     return FT_Open_Face( library, &args, face_index, aface );
1468
1469 #endif /* HAVE_FSREF */
1470
1471   }
1472
1473
1474   /*************************************************************************/
1475   /*                                                                       */
1476   /* <Function>                                                            */
1477   /*    FT_New_Face_From_FSSpec                                            */
1478   /*                                                                       */
1479   /* <Description>                                                         */
1480   /*    FT_New_Face_From_FSSpec is identical to FT_New_Face except it      */
1481   /*    accepts an FSSpec instead of a path.                               */
1482   /*                                                                       */
1483   /* This function is deprecated because Carbon data types (FSSpec)        */
1484   /* are not cross-platform, and thus not suitable for the FreeType API.   */
1485   FT_EXPORT_DEF( FT_Error )
1486   FT_New_Face_From_FSSpec( FT_Library     library,
1487                            const FSSpec*  spec,
1488                            FT_Long        face_index,
1489                            FT_Face*       aface )
1490   {
1491
1492 #if HAVE_FSREF
1493
1494     FSRef  ref;
1495
1496
1497     if ( !spec || FSpMakeFSRef( spec, &ref ) != noErr )
1498       return FT_THROW( Invalid_Argument );
1499     else
1500       return FT_New_Face_From_FSRef( library, &ref, face_index, aface );
1501
1502 #elif HAVE_FSSPEC
1503
1504     FT_Error      error;
1505     FT_Open_Args  args;
1506     OSErr         err;
1507     UInt8         pathname[PATH_MAX];
1508
1509
1510     if ( !spec )
1511       return FT_THROW( Invalid_Argument );
1512
1513     err = FT_FSpMakePath( spec, pathname, sizeof ( pathname ) );
1514     if ( err )
1515       error = FT_ERR( Cannot_Open_Resource );
1516
1517     error = FT_New_Face_From_Resource( library, pathname, face_index, aface );
1518     if ( error || *aface )
1519       return error;
1520
1521     /* fallback to datafork font */
1522     args.flags    = FT_OPEN_PATHNAME;
1523     args.pathname = (char*)pathname;
1524     return FT_Open_Face( library, &args, face_index, aface );
1525
1526 #else
1527
1528     FT_UNUSED( library );
1529     FT_UNUSED( spec );
1530     FT_UNUSED( face_index );
1531     FT_UNUSED( aface );
1532
1533     return FT_THROW( Unimplemented_Feature );
1534
1535 #endif /* HAVE_FSREF, HAVE_FSSPEC */
1536
1537   }
1538
1539 #endif /* FT_MACINTOSH */
1540
1541
1542 /* END */