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