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