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 (C) 1996-2020 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 <freetype/freetype.h>
69 #include <freetype/tttags.h>
70 #include <freetype/internal/ftstream.h>
71 #include "ftbase.h"
72 
73 
74 #ifdef FT_MACINTOSH
75 
76   /* This is for Mac OS X.  Without redefinition, OS_INLINE */
77   /* expands to `static inline' which doesn't survive the   */
78   /* -ansi compilation flag of GCC.                         */
79 #if !HAVE_ANSI_OS_INLINE
80 #undef  OS_INLINE
81 #define OS_INLINE  static __inline__
82 #endif
83 
84   /* `configure' checks the availability of `ResourceIndex' strictly */
85   /* and sets HAVE_TYPE_RESOURCE_INDEX 1 or 0 always.  If it is      */
86   /* not set (e.g., a build without `configure'), the availability   */
87   /* is guessed from the SDK version.                                */
88 #ifndef HAVE_TYPE_RESOURCE_INDEX
89 #if !defined( MAC_OS_X_VERSION_10_5 ) || \
90     ( MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 )
91 #define HAVE_TYPE_RESOURCE_INDEX 0
92 #else
93 #define HAVE_TYPE_RESOURCE_INDEX 1
94 #endif
95 #endif /* !HAVE_TYPE_RESOURCE_INDEX */
96 
97 #if ( HAVE_TYPE_RESOURCE_INDEX == 0 )
98   typedef short  ResourceIndex;
99 #endif
100 
101 #include <CoreServices/CoreServices.h>
102 #include <ApplicationServices/ApplicationServices.h>
103 #include <sys/syslimits.h> /* PATH_MAX */
104 
105   /* Don't want warnings about our own use of deprecated functions. */
106 #define FT_DEPRECATED_ATTRIBUTE
107 
108 #include FT_MAC_H
109 
110 #ifndef kATSOptionFlagsUnRestrictedScope /* since Mac OS X 10.1 */
111 #define kATSOptionFlagsUnRestrictedScope kATSOptionFlagsDefault
112 #endif
113 
114 
115   /* Set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over
116      TrueType in case *both* are available (this is not common,
117      but it *is* possible). */
118 #ifndef PREFER_LWFN
119 #define PREFER_LWFN  1
120 #endif
121 
122 
123   /* This function is deprecated because FSSpec is deprecated in Mac OS X  */
124   FT_EXPORT_DEF( FT_Error )
FT_GetFile_From_Mac_Name(const char * fontName,FSSpec * pathSpec,FT_Long * face_index)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
FT_ATSFontGetFileReference(ATSFontRef ats_font_id,FSRef * ats_font_ref)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
FT_GetFileRef_From_Mac_ATS_Name(const char * fontName,FSRef * ats_font_ref,FT_Long * face_index)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 )
FT_GetFilePath_From_Mac_ATS_Name(const char * fontName,UInt8 * path,UInt32 maxPathSize,FT_Long * face_index)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 )
FT_GetFile_From_Mac_ATS_Name(const char * fontName,FSSpec * pathSpec,FT_Long * face_index)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
FT_FSPathMakeRes(const UInt8 * pathname,ResFileRefNum * res)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
get_file_type_from_path(const UInt8 * pathname)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
create_lwfn_name(char * ps_name,Str255 lwfn_file_name)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
count_faces_sfnt(char * fond_data)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
count_faces_scalable(char * fond_data)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
parse_fond(char * fond_data,short * have_sfnt,ResID * sfnt_id,Str255 lwfn_file_name,short face_index)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
lookup_lwfn_by_fond(const UInt8 * path_fond,ConstStr255Param base_lwfn,UInt8 * path_lwfn,size_t path_size)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
count_faces(Handle fond,const UInt8 * pathname)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
read_lwfn(FT_Memory memory,ResFileRefNum res,FT_Byte ** pfb_data,FT_ULong * size)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 )
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 += (FT_ULong)GetHandleSize( post_data ) - 2;
622       last_code = code;
623 
624       /* detect resource fork overflow */
625       if ( FT_MAC_RFORK_MAX_LEN < 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 )
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 )
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
FT_New_Face_From_LWFN(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)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
FT_New_Face_From_SFNT(FT_Library library,ResID sfnt_id,FT_Long face_index,FT_Face * aface)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 )
747       return FT_THROW( Invalid_Handle );
748 
749     sfnt_size = (FT_ULong)GetHandleSize( sfnt );
750 
751     /* detect resource fork overflow */
752     if ( FT_MAC_RFORK_MAX_LEN < sfnt_size )
753       return FT_THROW( Array_Too_Large );
754 
755     if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) )
756     {
757       ReleaseResource( sfnt );
758       return error;
759     }
760 
761     ft_memcpy( sfnt_data, *sfnt, sfnt_size );
762     ReleaseResource( sfnt );
763 
764     is_cff     = sfnt_size > 4 && !ft_memcmp( sfnt_data, "OTTO", 4 );
765     is_sfnt_ps = sfnt_size > 4 && !ft_memcmp( sfnt_data, "typ1", 4 );
766 
767     if ( is_sfnt_ps )
768     {
769       FT_Stream  stream;
770 
771 
772       if ( FT_NEW( stream ) )
773         goto Try_OpenType;
774 
775       FT_Stream_OpenMemory( stream, sfnt_data, sfnt_size );
776       if ( !open_face_PS_from_sfnt_stream( library,
777                                            stream,
778                                            face_index,
779                                            0, NULL,
780                                            aface ) )
781       {
782         FT_Stream_Close( stream );
783         FT_FREE( stream );
784         FT_FREE( sfnt_data );
785         goto Exit;
786       }
787 
788       FT_FREE( stream );
789     }
790   Try_OpenType:
791     error = open_face_from_buffer( library,
792                                    sfnt_data,
793                                    sfnt_size,
794                                    face_index,
795                                    is_cff ? "cff" : "truetype",
796                                    aface );
797   Exit:
798     return error;
799   }
800 
801 
802   /* Create a new FT_Face from a file path to a suitcase file. */
803   static FT_Error
FT_New_Face_From_Suitcase(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)804   FT_New_Face_From_Suitcase( FT_Library    library,
805                              const UInt8*  pathname,
806                              FT_Long       face_index,
807                              FT_Face*      aface )
808   {
809     FT_Error       error = FT_ERR( Cannot_Open_Resource );
810     ResFileRefNum  res_ref;
811     ResourceIndex  res_index;
812     Handle         fond;
813     short          num_faces_in_res;
814 
815 
816     if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) )
817       return FT_THROW( Cannot_Open_Resource );
818 
819     UseResFile( res_ref );
820     if ( ResError() )
821       return FT_THROW( Cannot_Open_Resource );
822 
823     num_faces_in_res = 0;
824     for ( res_index = 1; ; res_index++ )
825     {
826       short  num_faces_in_fond;
827 
828 
829       fond = Get1IndResource( TTAG_FOND, res_index );
830       if ( ResError() )
831         break;
832 
833       num_faces_in_fond  = count_faces( fond, pathname );
834       num_faces_in_res  += num_faces_in_fond;
835 
836       if ( 0 <= face_index && face_index < num_faces_in_fond && error )
837         error = FT_New_Face_From_FOND( library, fond, face_index, aface );
838 
839       face_index -= num_faces_in_fond;
840     }
841 
842     CloseResFile( res_ref );
843     if ( !error && aface && *aface )
844       (*aface)->num_faces = num_faces_in_res;
845     return error;
846   }
847 
848 
849   /* documentation is in ftmac.h */
850 
851   FT_EXPORT_DEF( FT_Error )
FT_New_Face_From_FOND(FT_Library library,Handle fond,FT_Long face_index,FT_Face * aface)852   FT_New_Face_From_FOND( FT_Library  library,
853                          Handle      fond,
854                          FT_Long     face_index,
855                          FT_Face*    aface )
856   {
857     short     have_sfnt, have_lwfn = 0;
858     ResID     sfnt_id, fond_id;
859     OSType    fond_type;
860     Str255    fond_name;
861     Str255    lwfn_file_name;
862     UInt8     path_lwfn[PATH_MAX];
863     OSErr     err;
864     FT_Error  error = FT_Err_Ok;
865 
866 
867     /* check of `library' and `aface' delayed to `FT_New_Face_From_XXX' */
868 
869     GetResInfo( fond, &fond_id, &fond_type, fond_name );
870     if ( ResError() != noErr || fond_type != TTAG_FOND )
871       return FT_THROW( Invalid_File_Format );
872 
873     parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index );
874 
875     if ( lwfn_file_name[0] )
876     {
877       ResFileRefNum  res;
878 
879 
880       res = HomeResFile( fond );
881       if ( noErr != ResError() )
882         goto found_no_lwfn_file;
883 
884       {
885         UInt8  path_fond[PATH_MAX];
886         FSRef  ref;
887 
888 
889         err = FSGetForkCBInfo( res, kFSInvalidVolumeRefNum,
890                                NULL, NULL, NULL, &ref, NULL );
891         if ( noErr != err )
892           goto found_no_lwfn_file;
893 
894         err = FSRefMakePath( &ref, path_fond, sizeof ( path_fond ) );
895         if ( noErr != err )
896           goto found_no_lwfn_file;
897 
898         error = lookup_lwfn_by_fond( path_fond, lwfn_file_name,
899                                      path_lwfn, sizeof ( path_lwfn ) );
900         if ( !error )
901           have_lwfn = 1;
902       }
903     }
904 
905     if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
906       error = FT_New_Face_From_LWFN( library,
907                                      path_lwfn,
908                                      face_index,
909                                      aface );
910     else
911       error = FT_THROW( Unknown_File_Format );
912 
913   found_no_lwfn_file:
914     if ( have_sfnt && error )
915       error = FT_New_Face_From_SFNT( library,
916                                      sfnt_id,
917                                      face_index,
918                                      aface );
919 
920     return error;
921   }
922 
923 
924   /* Common function to load a new FT_Face from a resource file. */
925   static FT_Error
FT_New_Face_From_Resource(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)926   FT_New_Face_From_Resource( FT_Library    library,
927                              const UInt8*  pathname,
928                              FT_Long       face_index,
929                              FT_Face*      aface )
930   {
931     OSType    file_type;
932     FT_Error  error;
933 
934 
935     /* LWFN is a (very) specific file format, check for it explicitly */
936     file_type = get_file_type_from_path( pathname );
937     if ( file_type == TTAG_LWFN )
938       return FT_New_Face_From_LWFN( library, pathname, face_index, aface );
939 
940     /* Otherwise the file type doesn't matter (there are more than  */
941     /* `FFIL' and `tfil').  Just try opening it as a font suitcase; */
942     /* if it works, fine.                                           */
943 
944     error = FT_New_Face_From_Suitcase( library, pathname, face_index, aface );
945     if ( error )
946     {
947       /* let it fall through to normal loader (.ttf, .otf, etc.); */
948       /* we signal this by returning no error and no FT_Face      */
949       *aface = NULL;
950     }
951 
952     return FT_Err_Ok;
953   }
954 
955 
956   /**************************************************************************
957    *
958    * @Function:
959    *   FT_New_Face
960    *
961    * @Description:
962    *   This is the Mac-specific implementation of FT_New_Face.  In
963    *   addition to the standard FT_New_Face() functionality, it also
964    *   accepts pathnames to Mac suitcase files.  For further
965    *   documentation see the original FT_New_Face() in freetype.h.
966    */
967   FT_EXPORT_DEF( FT_Error )
FT_New_Face(FT_Library library,const char * pathname,FT_Long face_index,FT_Face * aface)968   FT_New_Face( FT_Library   library,
969                const char*  pathname,
970                FT_Long      face_index,
971                FT_Face*     aface )
972   {
973     FT_Open_Args  args;
974     FT_Error      error;
975 
976 
977     /* test for valid `library' and `aface' delayed to FT_Open_Face() */
978     if ( !pathname )
979       return FT_THROW( Invalid_Argument );
980 
981     *aface = NULL;
982 
983     /* try resourcefork based font: LWFN, FFIL */
984     error = FT_New_Face_From_Resource( library, (UInt8 *)pathname,
985                                        face_index, aface );
986     if ( error || *aface )
987       return error;
988 
989     /* let it fall through to normal loader (.ttf, .otf, etc.) */
990     args.flags    = FT_OPEN_PATHNAME;
991     args.pathname = (char*)pathname;
992 
993     return FT_Open_Face( library, &args, face_index, aface );
994   }
995 
996 
997   /**************************************************************************
998    *
999    * @Function:
1000    *   FT_New_Face_From_FSRef
1001    *
1002    * @Description:
1003    *   FT_New_Face_From_FSRef is identical to FT_New_Face except it
1004    *   accepts an FSRef instead of a path.
1005    *
1006    * This function is deprecated because Carbon data types (FSRef)
1007    * are not cross-platform, and thus not suitable for the FreeType API.
1008    */
1009   FT_EXPORT_DEF( FT_Error )
FT_New_Face_From_FSRef(FT_Library library,const FSRef * ref,FT_Long face_index,FT_Face * aface)1010   FT_New_Face_From_FSRef( FT_Library    library,
1011                           const FSRef*  ref,
1012                           FT_Long       face_index,
1013                           FT_Face*      aface )
1014   {
1015     FT_Error      error;
1016     FT_Open_Args  args;
1017 
1018     OSErr  err;
1019     UInt8  pathname[PATH_MAX];
1020 
1021 
1022     /* check of `library' and `aface' delayed to */
1023     /* `FT_New_Face_From_Resource'               */
1024 
1025     if ( !ref )
1026       return FT_THROW( Invalid_Argument );
1027 
1028     err = FSRefMakePath( ref, pathname, sizeof ( pathname ) );
1029     if ( err )
1030       error = FT_THROW( Cannot_Open_Resource );
1031 
1032     error = FT_New_Face_From_Resource( library, pathname, face_index, aface );
1033     if ( error || *aface )
1034       return error;
1035 
1036     /* fallback to datafork font */
1037     args.flags    = FT_OPEN_PATHNAME;
1038     args.pathname = (char*)pathname;
1039     return FT_Open_Face( library, &args, face_index, aface );
1040   }
1041 
1042 
1043   /**************************************************************************
1044    *
1045    * @Function:
1046    *   FT_New_Face_From_FSSpec
1047    *
1048    * @Description:
1049    *   FT_New_Face_From_FSSpec is identical to FT_New_Face except it
1050    *   accepts an FSSpec instead of a path.
1051    *
1052    * This function is deprecated because FSSpec is deprecated in Mac OS X
1053    */
1054   FT_EXPORT_DEF( FT_Error )
FT_New_Face_From_FSSpec(FT_Library library,const FSSpec * spec,FT_Long face_index,FT_Face * aface)1055   FT_New_Face_From_FSSpec( FT_Library     library,
1056                            const FSSpec*  spec,
1057                            FT_Long        face_index,
1058                            FT_Face*       aface )
1059   {
1060 #if ( __LP64__ ) || ( defined( MAC_OS_X_VERSION_10_5 ) && \
1061       ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) )
1062     FT_UNUSED( library );
1063     FT_UNUSED( spec );
1064     FT_UNUSED( face_index );
1065     FT_UNUSED( aface );
1066 
1067     return FT_THROW( Unimplemented_Feature );
1068 #else
1069     FSRef  ref;
1070 
1071 
1072     /* check of `library' and `aface' delayed to `FT_New_Face_From_FSRef' */
1073 
1074     if ( !spec || FSpMakeFSRef( spec, &ref ) != noErr )
1075       return FT_THROW( Invalid_Argument );
1076     else
1077       return FT_New_Face_From_FSRef( library, &ref, face_index, aface );
1078 #endif
1079   }
1080 
1081 #else /* !FT_MACINTOSH */
1082 
1083   /* ANSI C doesn't like empty source files */
1084   typedef int  _ft_mac_dummy;
1085 
1086 #endif /* !FT_MACINTOSH */
1087 
1088 
1089 /* END */
1090