XFA: merge patch from CL 817753002
[pdfium.git] / core / src / fxge / fx_freetype / fxft2.5.01 / 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 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 "../../include/ft2build.h"
69 #include "../../include/freetype/freetype.h"
70 #include "../../include/freetype/tttags.h"
71 #include "../../include/freetype/internal/ftstream.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 "../../include/freetype/ftmac.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     err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index );
231     if ( err )
232       return err;
233
234     if ( noErr != FSRefMakePath( &ref, path, maxPathSize ) )
235       return FT_THROW( Unknown_File_Format );
236
237     return FT_Err_Ok;
238   }
239
240
241   /* This function is deprecated because FSSpec is deprecated in Mac OS X  */
242   FT_EXPORT_DEF( FT_Error )
243   FT_GetFile_From_Mac_ATS_Name( const char*  fontName,
244                                 FSSpec*      pathSpec,
245                                 FT_Long*     face_index )
246   {
247 #if ( __LP64__ ) || ( defined( MAC_OS_X_VERSION_10_5 ) && \
248       ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) )
249     FT_UNUSED( fontName );
250     FT_UNUSED( pathSpec );
251     FT_UNUSED( face_index );
252
253     return FT_THROW( Unimplemented_Feature );
254 #else
255     FSRef     ref;
256     FT_Error  err;
257
258
259     err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index );
260     if ( err )
261       return err;
262
263     if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, NULL, NULL,
264                                     pathSpec, NULL ) )
265       return FT_THROW( Unknown_File_Format );
266
267     return FT_Err_Ok;
268 #endif
269   }
270
271
272   static OSErr
273   FT_FSPathMakeRes( const UInt8*    pathname,
274                     ResFileRefNum*  res )
275   {
276     OSErr  err;
277     FSRef  ref;
278
279
280     if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
281       return FT_THROW( Cannot_Open_Resource );
282
283     /* at present, no support for dfont format */
284     err = FSOpenResourceFile( &ref, 0, NULL, fsRdPerm, res );
285     if ( noErr == err )
286       return err;
287
288     /* fallback to original resource-fork font */
289     *res = FSOpenResFile( &ref, fsRdPerm );
290     err  = ResError();
291
292     return err;
293   }
294
295
296   /* Return the file type for given pathname */
297   static OSType
298   get_file_type_from_path( const UInt8*  pathname )
299   {
300     FSRef          ref;
301     FSCatalogInfo  info;
302
303
304     if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
305       return ( OSType ) 0;
306
307     if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoFinderInfo, &info,
308                                     NULL, NULL, NULL ) )
309       return ( OSType ) 0;
310
311     return ((FInfo *)(info.finderInfo))->fdType;
312   }
313
314
315   /* Given a PostScript font name, create the Macintosh LWFN file name. */
316   static void
317   create_lwfn_name( char*   ps_name,
318                     Str255  lwfn_file_name )
319   {
320     int       max = 5, count = 0;
321     FT_Byte*  p = lwfn_file_name;
322     FT_Byte*  q = (FT_Byte*)ps_name;
323
324
325     lwfn_file_name[0] = 0;
326
327     while ( *q )
328     {
329       if ( ft_isupper( *q ) )
330       {
331         if ( count )
332           max = 3;
333         count = 0;
334       }
335       if ( count < max && ( ft_isalnum( *q ) || *q == '_' ) )
336       {
337         *++p = *q;
338         lwfn_file_name[0]++;
339         count++;
340       }
341       q++;
342     }
343   }
344
345
346   static short
347   count_faces_sfnt( char*  fond_data )
348   {
349     /* The count is 1 greater than the value in the FOND.  */
350     /* Isn't that cute? :-)                                */
351
352     return EndianS16_BtoN( *( (short*)( fond_data +
353                                         sizeof ( FamRec ) ) ) ) + 1;
354   }
355
356
357   static short
358   count_faces_scalable( char*  fond_data )
359   {
360     AsscEntry*  assoc;
361     short       i, face, face_all;
362
363
364     face_all = EndianS16_BtoN( *( (short *)( fond_data +
365                                              sizeof ( FamRec ) ) ) ) + 1;
366     assoc    = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
367     face     = 0;
368
369     for ( i = 0; i < face_all; i++ )
370     {
371       if ( 0 == EndianS16_BtoN( assoc[i].fontSize ) )
372         face++;
373     }
374     return face;
375   }
376
377
378   /* Look inside the FOND data, answer whether there should be an SFNT
379      resource, and answer the name of a possible LWFN Type 1 file.
380
381      Thanks to Paul Miller (paulm@profoundeffects.com) for the fix
382      to load a face OTHER than the first one in the FOND!
383   */
384
385
386   static void
387   parse_fond( char*   fond_data,
388               short*  have_sfnt,
389               ResID*  sfnt_id,
390               Str255  lwfn_file_name,
391               short   face_index )
392   {
393     AsscEntry*  assoc;
394     AsscEntry*  base_assoc;
395     FamRec*     fond;
396
397
398     *sfnt_id          = 0;
399     *have_sfnt        = 0;
400     lwfn_file_name[0] = 0;
401
402     fond       = (FamRec*)fond_data;
403     assoc      = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
404     base_assoc = assoc;
405
406     /* the maximum faces in a FOND is 48, size of StyleTable.indexes[] */
407     if ( 47 < face_index )
408       return;
409
410     /* Let's do a little range checking before we get too excited here */
411     if ( face_index < count_faces_sfnt( fond_data ) )
412     {
413       assoc += face_index;        /* add on the face_index! */
414
415       /* if the face at this index is not scalable,
416          fall back to the first one (old behavior) */
417       if ( EndianS16_BtoN( assoc->fontSize ) == 0 )
418       {
419         *have_sfnt = 1;
420         *sfnt_id   = EndianS16_BtoN( assoc->fontID );
421       }
422       else if ( base_assoc->fontSize == 0 )
423       {
424         *have_sfnt = 1;
425         *sfnt_id   = EndianS16_BtoN( base_assoc->fontID );
426       }
427     }
428
429     if ( EndianS32_BtoN( fond->ffStylOff ) )
430     {
431       unsigned char*  p = (unsigned char*)fond_data;
432       StyleTable*     style;
433       unsigned short  string_count;
434       char            ps_name[256];
435       unsigned char*  names[64];
436       int             i;
437
438
439       p += EndianS32_BtoN( fond->ffStylOff );
440       style = (StyleTable*)p;
441       p += sizeof ( StyleTable );
442       string_count = EndianS16_BtoN( *(short*)(p) );
443       p += sizeof ( short );
444
445       for ( i = 0; i < string_count && i < 64; i++ )
446       {
447         names[i] = p;
448         p       += names[i][0];
449         p++;
450       }
451
452       {
453         size_t  ps_name_len = (size_t)names[0][0];
454
455
456         if ( ps_name_len != 0 )
457         {
458           ft_memcpy(ps_name, names[0] + 1, ps_name_len);
459           ps_name[ps_name_len] = 0;
460         }
461         if ( style->indexes[face_index] > 1 &&
462              style->indexes[face_index] <= FT_MIN( string_count, 64 ) )
463         {
464           unsigned char*  suffixes = names[style->indexes[face_index] - 1];
465
466
467           for ( i = 1; i <= suffixes[0]; i++ )
468           {
469             unsigned char*  s;
470             size_t          j = suffixes[i] - 1;
471
472
473             if ( j < string_count && ( s = names[j] ) != NULL )
474             {
475               size_t  s_len = (size_t)s[0];
476
477
478               if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) )
479               {
480                 ft_memcpy( ps_name + ps_name_len, s + 1, s_len );
481                 ps_name_len += s_len;
482                 ps_name[ps_name_len] = 0;
483               }
484             }
485           }
486         }
487       }
488
489       create_lwfn_name( ps_name, lwfn_file_name );
490     }
491   }
492
493
494   static  FT_Error
495   lookup_lwfn_by_fond( const UInt8*      path_fond,
496                        ConstStr255Param  base_lwfn,
497                        UInt8*            path_lwfn,
498                        size_t            path_size )
499   {
500     FSRef   ref, par_ref;
501     size_t  dirname_len;
502
503
504     /* Pathname for FSRef can be in various formats: HFS, HFS+, and POSIX. */
505     /* We should not extract parent directory by string manipulation.      */
506
507     if ( noErr != FSPathMakeRef( path_fond, &ref, FALSE ) )
508       return FT_THROW( Invalid_Argument );
509
510     if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
511                                     NULL, NULL, NULL, &par_ref ) )
512       return FT_THROW( Invalid_Argument );
513
514     if ( noErr != FSRefMakePath( &par_ref, path_lwfn, path_size ) )
515       return FT_THROW( Invalid_Argument );
516
517     if ( ft_strlen( (char *)path_lwfn ) + 1 + base_lwfn[0] > path_size )
518       return FT_THROW( Invalid_Argument );
519
520     /* now we have absolute dirname in path_lwfn */
521     ft_strcat( (char *)path_lwfn, "/" );
522     dirname_len = ft_strlen( (char *)path_lwfn );
523     ft_strcat( (char *)path_lwfn, (char *)base_lwfn + 1 );
524     path_lwfn[dirname_len + base_lwfn[0]] = '\0';
525
526     if ( noErr != FSPathMakeRef( path_lwfn, &ref, FALSE ) )
527       return FT_THROW( Cannot_Open_Resource );
528
529     if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
530                                     NULL, NULL, NULL, NULL ) )
531       return FT_THROW( Cannot_Open_Resource );
532
533     return FT_Err_Ok;
534   }
535
536
537   static short
538   count_faces( Handle        fond,
539                const UInt8*  pathname )
540   {
541     ResID     sfnt_id;
542     short     have_sfnt, have_lwfn;
543     Str255    lwfn_file_name;
544     UInt8     buff[PATH_MAX];
545     FT_Error  err;
546     short     num_faces;
547
548
549     have_sfnt = have_lwfn = 0;
550
551     parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 );
552
553     if ( lwfn_file_name[0] )
554     {
555       err = lookup_lwfn_by_fond( pathname, lwfn_file_name,
556                                  buff, sizeof ( buff )  );
557       if ( !err )
558         have_lwfn = 1;
559     }
560
561     if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
562       num_faces = 1;
563     else
564       num_faces = count_faces_scalable( *fond );
565
566     return num_faces;
567   }
568
569
570   /* Read Type 1 data from the POST resources inside the LWFN file,
571      return a PFB buffer.  This is somewhat convoluted because the FT2
572      PFB parser wants the ASCII header as one chunk, and the LWFN
573      chunks are often not organized that way, so we glue chunks
574      of the same type together. */
575   static FT_Error
576   read_lwfn( FT_Memory      memory,
577              ResFileRefNum  res,
578              FT_Byte**      pfb_data,
579              FT_ULong*      size )
580   {
581     FT_Error       error = FT_Err_Ok;
582     ResID          res_id;
583     unsigned char  *buffer, *p, *size_p = NULL;
584     FT_ULong       total_size = 0;
585     FT_ULong       old_total_size = 0;
586     FT_ULong       post_size, pfb_chunk_size;
587     Handle         post_data;
588     char           code, last_code;
589
590
591     UseResFile( res );
592
593     /* First pass: load all POST resources, and determine the size of */
594     /* the output buffer.                                             */
595     res_id    = 501;
596     last_code = -1;
597
598     for (;;)
599     {
600       post_data = Get1Resource( TTAG_POST, res_id++ );
601       if ( post_data == NULL )
602         break;  /* we are done */
603
604       code = (*post_data)[0];
605
606       if ( code != last_code )
607       {
608         if ( code == 5 )
609           total_size += 2; /* just the end code */
610         else
611           total_size += 6; /* code + 4 bytes chunk length */
612       }
613
614       total_size += GetHandleSize( post_data ) - 2;
615       last_code = code;
616
617       /* detect integer overflows */
618       if ( total_size < old_total_size )
619       {
620         error = FT_THROW( Array_Too_Large );
621         goto Error;
622       }
623
624       old_total_size = total_size;
625     }
626
627     if ( FT_ALLOC( buffer, (FT_Long)total_size ) )
628       goto Error;
629
630     /* Second pass: append all POST data to the buffer, add PFB fields. */
631     /* Glue all consecutive chunks of the same type together.           */
632     p              = buffer;
633     res_id         = 501;
634     last_code      = -1;
635     pfb_chunk_size = 0;
636
637     for (;;)
638     {
639       post_data = Get1Resource( TTAG_POST, res_id++ );
640       if ( post_data == NULL )
641         break;  /* we are done */
642
643       post_size = (FT_ULong)GetHandleSize( post_data ) - 2;
644       code = (*post_data)[0];
645
646       if ( code != last_code )
647       {
648         if ( last_code != -1 )
649         {
650           /* we are done adding a chunk, fill in the size field */
651           if ( size_p != NULL )
652           {
653             *size_p++ = (FT_Byte)(   pfb_chunk_size         & 0xFF );
654             *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8  ) & 0xFF );
655             *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF );
656             *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF );
657           }
658           pfb_chunk_size = 0;
659         }
660
661         *p++ = 0x80;
662         if ( code == 5 )
663           *p++ = 0x03;  /* the end */
664         else if ( code == 2 )
665           *p++ = 0x02;  /* binary segment */
666         else
667           *p++ = 0x01;  /* ASCII segment */
668
669         if ( code != 5 )
670         {
671           size_p = p;   /* save for later */
672           p += 4;       /* make space for size field */
673         }
674       }
675
676       ft_memcpy( p, *post_data + 2, post_size );
677       pfb_chunk_size += post_size;
678       p += post_size;
679       last_code = code;
680     }
681
682     *pfb_data = buffer;
683     *size = total_size;
684
685   Error:
686     CloseResFile( res );
687     return error;
688   }
689
690
691   /* Create a new FT_Face from a file path to an LWFN file. */
692   static FT_Error
693   FT_New_Face_From_LWFN( FT_Library    library,
694                          const UInt8*  pathname,
695                          FT_Long       face_index,
696                          FT_Face*      aface )
697   {
698     FT_Byte*       pfb_data;
699     FT_ULong       pfb_size;
700     FT_Error       error;
701     ResFileRefNum  res;
702
703
704     if ( noErr != FT_FSPathMakeRes( pathname, &res ) )
705       return FT_THROW( Cannot_Open_Resource );
706
707     pfb_data = NULL;
708     pfb_size = 0;
709     error = read_lwfn( library->memory, res, &pfb_data, &pfb_size );
710     CloseResFile( res ); /* PFB is already loaded, useless anymore */
711     if ( error )
712       return error;
713
714     return open_face_from_buffer( library,
715                                   pfb_data,
716                                   pfb_size,
717                                   face_index,
718                                   "type1",
719                                   aface );
720   }
721
722
723   /* Create a new FT_Face from an SFNT resource, specified by res ID. */
724   static FT_Error
725   FT_New_Face_From_SFNT( FT_Library  library,
726                          ResID       sfnt_id,
727                          FT_Long     face_index,
728                          FT_Face*    aface )
729   {
730     Handle     sfnt = NULL;
731     FT_Byte*   sfnt_data;
732     size_t     sfnt_size;
733     FT_Error   error  = FT_Err_Ok;
734     FT_Memory  memory = library->memory;
735     int        is_cff, is_sfnt_ps;
736
737
738     sfnt = GetResource( TTAG_sfnt, sfnt_id );
739     if ( sfnt == NULL )
740       return FT_THROW( Invalid_Handle );
741
742     sfnt_size = (FT_ULong)GetHandleSize( sfnt );
743     if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) )
744     {
745       ReleaseResource( sfnt );
746       return error;
747     }
748
749     ft_memcpy( sfnt_data, *sfnt, sfnt_size );
750     ReleaseResource( sfnt );
751
752     is_cff     = sfnt_size > 4 && !ft_memcmp( sfnt_data, "OTTO", 4 );
753     is_sfnt_ps = sfnt_size > 4 && !ft_memcmp( sfnt_data, "typ1", 4 );
754
755     if ( is_sfnt_ps )
756     {
757       FT_Stream  stream;
758
759
760       if ( FT_NEW( stream ) )
761         goto Try_OpenType;
762
763       FT_Stream_OpenMemory( stream, sfnt_data, sfnt_size );
764       if ( !open_face_PS_from_sfnt_stream( library,
765                                            stream,
766                                            face_index,
767                                            0, NULL,
768                                            aface ) )
769       {
770         FT_Stream_Close( stream );
771         FT_FREE( stream );
772         FT_FREE( sfnt_data );
773         goto Exit;
774       }
775
776       FT_FREE( stream );
777     }
778   Try_OpenType:
779     error = open_face_from_buffer( library,
780                                    sfnt_data,
781                                    sfnt_size,
782                                    face_index,
783                                    is_cff ? "cff" : "truetype",
784                                    aface );
785   Exit:
786     return error;
787   }
788
789
790   /* Create a new FT_Face from a file path to a suitcase file. */
791   static FT_Error
792   FT_New_Face_From_Suitcase( FT_Library    library,
793                              const UInt8*  pathname,
794                              FT_Long       face_index,
795                              FT_Face*      aface )
796   {
797     FT_Error       error = FT_ERR( Cannot_Open_Resource );
798     ResFileRefNum  res_ref;
799     ResourceIndex  res_index;
800     Handle         fond;
801     short          num_faces_in_res;
802
803
804     if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) )
805       return FT_THROW( Cannot_Open_Resource );
806
807     UseResFile( res_ref );
808     if ( ResError() )
809       return FT_THROW( Cannot_Open_Resource );
810
811     num_faces_in_res = 0;
812     for ( res_index = 1; ; ++res_index )
813     {
814       short  num_faces_in_fond;
815
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 ( !error && aface && *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_THROW( 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 ( !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_THROW( Unknown_File_Format );
898
899   found_no_lwfn_file:
900     if ( have_sfnt && 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_THROW( 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_THROW( Invalid_Argument );
1007
1008     err = FSRefMakePath( ref, pathname, sizeof ( pathname ) );
1009     if ( err )
1010       error = FT_THROW( 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_THROW( Unimplemented_Feature );
1047 #else
1048     FSRef  ref;
1049
1050
1051     if ( !spec || FSpMakeFSRef( spec, &ref ) != noErr )
1052       return FT_THROW( 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 */