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