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