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 <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 )
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     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 )
FT_GetFile_From_Mac_ATS_Name(const char * fontName,FSSpec * pathSpec,FT_Long * face_index)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
FT_FSPathMakeRes(const UInt8 * pathname,ResFileRefNum * res)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
get_file_type_from_path(const UInt8 * pathname)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
create_lwfn_name(char * ps_name,Str255 lwfn_file_name)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
count_faces_sfnt(char * fond_data)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
count_faces_scalable(char * fond_data)358   count_faces_scalable( char*  fond_data )
359   {
360     AsscEntry*  assoc;
361     FamRec*     fond;
362     short       i, face, face_all;
363 
364 
365     fond     = (FamRec*)fond_data;
366     face_all = EndianS16_BtoN( *( (short *)( fond_data +
367                                              sizeof ( FamRec ) ) ) ) + 1;
368     assoc    = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
369     face     = 0;
370 
371     for ( i = 0; i < face_all; i++ )
372     {
373       if ( 0 == EndianS16_BtoN( assoc[i].fontSize ) )
374         face++;
375     }
376     return face;
377   }
378 
379 
380   /* Look inside the FOND data, answer whether there should be an SFNT
381      resource, and answer the name of a possible LWFN Type 1 file.
382 
383      Thanks to Paul Miller (paulm@profoundeffects.com) for the fix
384      to load a face OTHER than the first one in the FOND!
385   */
386 
387 
388   static void
parse_fond(char * fond_data,short * have_sfnt,ResID * sfnt_id,Str255 lwfn_file_name,short face_index)389   parse_fond( char*   fond_data,
390               short*  have_sfnt,
391               ResID*  sfnt_id,
392               Str255  lwfn_file_name,
393               short   face_index )
394   {
395     AsscEntry*  assoc;
396     AsscEntry*  base_assoc;
397     FamRec*     fond;
398 
399 
400     *sfnt_id          = 0;
401     *have_sfnt        = 0;
402     lwfn_file_name[0] = 0;
403 
404     fond       = (FamRec*)fond_data;
405     assoc      = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
406     base_assoc = assoc;
407 
408     /* the maximum faces in a FOND is 48, size of StyleTable.indexes[] */
409     if ( 47 < face_index )
410       return;
411 
412     /* Let's do a little range checking before we get too excited here */
413     if ( face_index < count_faces_sfnt( fond_data ) )
414     {
415       assoc += face_index;        /* add on the face_index! */
416 
417       /* if the face at this index is not scalable,
418          fall back to the first one (old behavior) */
419       if ( EndianS16_BtoN( assoc->fontSize ) == 0 )
420       {
421         *have_sfnt = 1;
422         *sfnt_id   = EndianS16_BtoN( assoc->fontID );
423       }
424       else if ( base_assoc->fontSize == 0 )
425       {
426         *have_sfnt = 1;
427         *sfnt_id   = EndianS16_BtoN( base_assoc->fontID );
428       }
429     }
430 
431     if ( EndianS32_BtoN( fond->ffStylOff ) )
432     {
433       unsigned char*  p = (unsigned char*)fond_data;
434       StyleTable*     style;
435       unsigned short  string_count;
436       char            ps_name[256];
437       unsigned char*  names[64];
438       int             i;
439 
440 
441       p += EndianS32_BtoN( fond->ffStylOff );
442       style = (StyleTable*)p;
443       p += sizeof ( StyleTable );
444       string_count = EndianS16_BtoN( *(short*)(p) );
445       p += sizeof ( short );
446 
447       for ( i = 0; i < string_count && i < 64; i++ )
448       {
449         names[i] = p;
450         p       += names[i][0];
451         p++;
452       }
453 
454       {
455         size_t  ps_name_len = (size_t)names[0][0];
456 
457 
458         if ( ps_name_len != 0 )
459         {
460           ft_memcpy(ps_name, names[0] + 1, ps_name_len);
461           ps_name[ps_name_len] = 0;
462         }
463         if ( style->indexes[face_index] > 1 &&
464              style->indexes[face_index] <= FT_MIN( string_count, 64 ) )
465         {
466           unsigned char*  suffixes = names[style->indexes[face_index] - 1];
467 
468 
469           for ( i = 1; i <= suffixes[0]; i++ )
470           {
471             unsigned char*  s;
472             size_t          j = suffixes[i] - 1;
473 
474 
475             if ( j < string_count && ( s = names[j] ) != NULL )
476             {
477               size_t  s_len = (size_t)s[0];
478 
479 
480               if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) )
481               {
482                 ft_memcpy( ps_name + ps_name_len, s + 1, s_len );
483                 ps_name_len += s_len;
484                 ps_name[ps_name_len] = 0;
485               }
486             }
487           }
488         }
489       }
490 
491       create_lwfn_name( ps_name, lwfn_file_name );
492     }
493   }
494 
495 
496   static  FT_Error
lookup_lwfn_by_fond(const UInt8 * path_fond,ConstStr255Param base_lwfn,UInt8 * path_lwfn,size_t path_size)497   lookup_lwfn_by_fond( const UInt8*      path_fond,
498                        ConstStr255Param  base_lwfn,
499                        UInt8*            path_lwfn,
500                        size_t            path_size )
501   {
502     FSRef   ref, par_ref;
503     size_t  dirname_len;
504 
505 
506     /* Pathname for FSRef can be in various formats: HFS, HFS+, and POSIX. */
507     /* We should not extract parent directory by string manipulation.      */
508 
509     if ( noErr != FSPathMakeRef( path_fond, &ref, FALSE ) )
510       return FT_THROW( Invalid_Argument );
511 
512     if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
513                                     NULL, NULL, NULL, &par_ref ) )
514       return FT_THROW( Invalid_Argument );
515 
516     if ( noErr != FSRefMakePath( &par_ref, path_lwfn, path_size ) )
517       return FT_THROW( Invalid_Argument );
518 
519     if ( ft_strlen( (char *)path_lwfn ) + 1 + base_lwfn[0] > path_size )
520       return FT_THROW( Invalid_Argument );
521 
522     /* now we have absolute dirname in path_lwfn */
523     ft_strcat( (char *)path_lwfn, "/" );
524     dirname_len = ft_strlen( (char *)path_lwfn );
525     ft_strcat( (char *)path_lwfn, (char *)base_lwfn + 1 );
526     path_lwfn[dirname_len + base_lwfn[0]] = '\0';
527 
528     if ( noErr != FSPathMakeRef( path_lwfn, &ref, FALSE ) )
529       return FT_THROW( Cannot_Open_Resource );
530 
531     if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
532                                     NULL, NULL, NULL, NULL ) )
533       return FT_THROW( Cannot_Open_Resource );
534 
535     return FT_Err_Ok;
536   }
537 
538 
539   static short
count_faces(Handle fond,const UInt8 * pathname)540   count_faces( Handle        fond,
541                const UInt8*  pathname )
542   {
543     ResID     sfnt_id;
544     short     have_sfnt, have_lwfn;
545     Str255    lwfn_file_name;
546     UInt8     buff[PATH_MAX];
547     FT_Error  err;
548     short     num_faces;
549 
550 
551     have_sfnt = have_lwfn = 0;
552 
553     parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 );
554 
555     if ( lwfn_file_name[0] )
556     {
557       err = lookup_lwfn_by_fond( pathname, lwfn_file_name,
558                                  buff, sizeof ( buff )  );
559       if ( !err )
560         have_lwfn = 1;
561     }
562 
563     if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
564       num_faces = 1;
565     else
566       num_faces = count_faces_scalable( *fond );
567 
568     return num_faces;
569   }
570 
571 
572   /* Read Type 1 data from the POST resources inside the LWFN file,
573      return a PFB buffer.  This is somewhat convoluted because the FT2
574      PFB parser wants the ASCII header as one chunk, and the LWFN
575      chunks are often not organized that way, so we glue chunks
576      of the same type together. */
577   static FT_Error
read_lwfn(FT_Memory memory,ResFileRefNum res,FT_Byte ** pfb_data,FT_ULong * size)578   read_lwfn( FT_Memory      memory,
579              ResFileRefNum  res,
580              FT_Byte**      pfb_data,
581              FT_ULong*      size )
582   {
583     FT_Error       error = FT_Err_Ok;
584     ResID          res_id;
585     unsigned char  *buffer, *p, *size_p = NULL;
586     FT_ULong       total_size = 0;
587     FT_ULong       old_total_size = 0;
588     FT_ULong       post_size, pfb_chunk_size;
589     Handle         post_data;
590     char           code, last_code;
591 
592 
593     UseResFile( res );
594 
595     /* First pass: load all POST resources, and determine the size of */
596     /* the output buffer.                                             */
597     res_id    = 501;
598     last_code = -1;
599 
600     for (;;)
601     {
602       post_data = Get1Resource( TTAG_POST, res_id++ );
603       if ( post_data == NULL )
604         break;  /* we are done */
605 
606       code = (*post_data)[0];
607 
608       if ( code != last_code )
609       {
610         if ( code == 5 )
611           total_size += 2; /* just the end code */
612         else
613           total_size += 6; /* code + 4 bytes chunk length */
614       }
615 
616       total_size += GetHandleSize( post_data ) - 2;
617       last_code = code;
618 
619       /* detect integer overflows */
620       if ( total_size < old_total_size )
621       {
622         error = FT_THROW( Array_Too_Large );
623         goto Error;
624       }
625 
626       old_total_size = total_size;
627     }
628 
629     if ( FT_ALLOC( buffer, (FT_Long)total_size ) )
630       goto Error;
631 
632     /* Second pass: append all POST data to the buffer, add PFB fields. */
633     /* Glue all consecutive chunks of the same type together.           */
634     p              = buffer;
635     res_id         = 501;
636     last_code      = -1;
637     pfb_chunk_size = 0;
638 
639     for (;;)
640     {
641       post_data = Get1Resource( TTAG_POST, res_id++ );
642       if ( post_data == NULL )
643         break;  /* we are done */
644 
645       post_size = (FT_ULong)GetHandleSize( post_data ) - 2;
646       code = (*post_data)[0];
647 
648       if ( code != last_code )
649       {
650         if ( last_code != -1 )
651         {
652           /* we are done adding a chunk, fill in the size field */
653           if ( size_p != NULL )
654           {
655             *size_p++ = (FT_Byte)(   pfb_chunk_size         & 0xFF );
656             *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8  ) & 0xFF );
657             *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF );
658             *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF );
659           }
660           pfb_chunk_size = 0;
661         }
662 
663         *p++ = 0x80;
664         if ( code == 5 )
665           *p++ = 0x03;  /* the end */
666         else if ( code == 2 )
667           *p++ = 0x02;  /* binary segment */
668         else
669           *p++ = 0x01;  /* ASCII segment */
670 
671         if ( code != 5 )
672         {
673           size_p = p;   /* save for later */
674           p += 4;       /* make space for size field */
675         }
676       }
677 
678       ft_memcpy( p, *post_data + 2, post_size );
679       pfb_chunk_size += post_size;
680       p += post_size;
681       last_code = code;
682     }
683 
684     *pfb_data = buffer;
685     *size = total_size;
686 
687   Error:
688     CloseResFile( res );
689     return error;
690   }
691 
692 
693   /* Create a new FT_Face from a file path to an LWFN file. */
694   static FT_Error
FT_New_Face_From_LWFN(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)695   FT_New_Face_From_LWFN( FT_Library    library,
696                          const UInt8*  pathname,
697                          FT_Long       face_index,
698                          FT_Face*      aface )
699   {
700     FT_Byte*       pfb_data;
701     FT_ULong       pfb_size;
702     FT_Error       error;
703     ResFileRefNum  res;
704 
705 
706     if ( noErr != FT_FSPathMakeRes( pathname, &res ) )
707       return FT_THROW( Cannot_Open_Resource );
708 
709     pfb_data = NULL;
710     pfb_size = 0;
711     error = read_lwfn( library->memory, res, &pfb_data, &pfb_size );
712     CloseResFile( res ); /* PFB is already loaded, useless anymore */
713     if ( error )
714       return error;
715 
716     return open_face_from_buffer( library,
717                                   pfb_data,
718                                   pfb_size,
719                                   face_index,
720                                   "type1",
721                                   aface );
722   }
723 
724 
725   /* Create a new FT_Face from an SFNT resource, specified by res ID. */
726   static FT_Error
FT_New_Face_From_SFNT(FT_Library library,ResID sfnt_id,FT_Long face_index,FT_Face * aface)727   FT_New_Face_From_SFNT( FT_Library  library,
728                          ResID       sfnt_id,
729                          FT_Long     face_index,
730                          FT_Face*    aface )
731   {
732     Handle     sfnt = NULL;
733     FT_Byte*   sfnt_data;
734     size_t     sfnt_size;
735     FT_Error   error  = FT_Err_Ok;
736     FT_Memory  memory = library->memory;
737     int        is_cff, is_sfnt_ps;
738 
739 
740     sfnt = GetResource( TTAG_sfnt, sfnt_id );
741     if ( sfnt == NULL )
742       return FT_THROW( Invalid_Handle );
743 
744     sfnt_size = (FT_ULong)GetHandleSize( sfnt );
745     if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) )
746     {
747       ReleaseResource( sfnt );
748       return error;
749     }
750 
751     ft_memcpy( sfnt_data, *sfnt, sfnt_size );
752     ReleaseResource( sfnt );
753 
754     is_cff     = sfnt_size > 4 && !ft_memcmp( sfnt_data, "OTTO", 4 );
755     is_sfnt_ps = sfnt_size > 4 && !ft_memcmp( sfnt_data, "typ1", 4 );
756 
757     if ( is_sfnt_ps )
758     {
759       FT_Stream  stream;
760 
761 
762       if ( FT_NEW( stream ) )
763         goto Try_OpenType;
764 
765       FT_Stream_OpenMemory( stream, sfnt_data, sfnt_size );
766       if ( !open_face_PS_from_sfnt_stream( library,
767                                            stream,
768                                            face_index,
769                                            0, NULL,
770                                            aface ) )
771       {
772         FT_Stream_Close( stream );
773         FT_FREE( stream );
774         FT_FREE( sfnt_data );
775         goto Exit;
776       }
777 
778       FT_FREE( stream );
779     }
780   Try_OpenType:
781     error = open_face_from_buffer( library,
782                                    sfnt_data,
783                                    sfnt_size,
784                                    face_index,
785                                    is_cff ? "cff" : "truetype",
786                                    aface );
787   Exit:
788     return error;
789   }
790 
791 
792   /* Create a new FT_Face from a file path to a suitcase file. */
793   static FT_Error
FT_New_Face_From_Suitcase(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)794   FT_New_Face_From_Suitcase( FT_Library    library,
795                              const UInt8*  pathname,
796                              FT_Long       face_index,
797                              FT_Face*      aface )
798   {
799     FT_Error       error = FT_ERR( Cannot_Open_Resource );
800     ResFileRefNum  res_ref;
801     ResourceIndex  res_index;
802     Handle         fond;
803     short          num_faces_in_res, num_faces_in_fond;
804 
805 
806     if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) )
807       return FT_THROW( Cannot_Open_Resource );
808 
809     UseResFile( res_ref );
810     if ( ResError() )
811       return FT_THROW( Cannot_Open_Resource );
812 
813     num_faces_in_res = 0;
814     for ( res_index = 1; ; ++res_index )
815     {
816       fond = Get1IndResource( TTAG_FOND, res_index );
817       if ( ResError() )
818         break;
819 
820       num_faces_in_fond  = count_faces( fond, pathname );
821       num_faces_in_res  += num_faces_in_fond;
822 
823       if ( 0 <= face_index && face_index < num_faces_in_fond && error )
824         error = FT_New_Face_From_FOND( library, fond, face_index, aface );
825 
826       face_index -= num_faces_in_fond;
827     }
828 
829     CloseResFile( res_ref );
830     if ( !error && aface && *aface )
831       (*aface)->num_faces = num_faces_in_res;
832     return error;
833   }
834 
835 
836   /* documentation is in ftmac.h */
837 
838   FT_EXPORT_DEF( FT_Error )
FT_New_Face_From_FOND(FT_Library library,Handle fond,FT_Long face_index,FT_Face * aface)839   FT_New_Face_From_FOND( FT_Library  library,
840                          Handle      fond,
841                          FT_Long     face_index,
842                          FT_Face*    aface )
843   {
844     short     have_sfnt, have_lwfn = 0;
845     ResID     sfnt_id, fond_id;
846     OSType    fond_type;
847     Str255    fond_name;
848     Str255    lwfn_file_name;
849     UInt8     path_lwfn[PATH_MAX];
850     OSErr     err;
851     FT_Error  error = FT_Err_Ok;
852 
853 
854     GetResInfo( fond, &fond_id, &fond_type, fond_name );
855     if ( ResError() != noErr || fond_type != TTAG_FOND )
856       return FT_THROW( Invalid_File_Format );
857 
858     parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index );
859 
860     if ( lwfn_file_name[0] )
861     {
862       ResFileRefNum  res;
863 
864 
865       res = HomeResFile( fond );
866       if ( noErr != ResError() )
867         goto found_no_lwfn_file;
868 
869       {
870         UInt8  path_fond[PATH_MAX];
871         FSRef  ref;
872 
873 
874         err = FSGetForkCBInfo( res, kFSInvalidVolumeRefNum,
875                                NULL, NULL, NULL, &ref, NULL );
876         if ( noErr != err )
877           goto found_no_lwfn_file;
878 
879         err = FSRefMakePath( &ref, path_fond, sizeof ( path_fond ) );
880         if ( noErr != err )
881           goto found_no_lwfn_file;
882 
883         error = lookup_lwfn_by_fond( path_fond, lwfn_file_name,
884                                      path_lwfn, sizeof ( path_lwfn ) );
885         if ( !error )
886           have_lwfn = 1;
887       }
888     }
889 
890     if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
891       error = FT_New_Face_From_LWFN( library,
892                                      path_lwfn,
893                                      face_index,
894                                      aface );
895     else
896       error = FT_THROW( Unknown_File_Format );
897 
898   found_no_lwfn_file:
899     if ( have_sfnt && error )
900       error = FT_New_Face_From_SFNT( library,
901                                      sfnt_id,
902                                      face_index,
903                                      aface );
904 
905     return error;
906   }
907 
908 
909   /* Common function to load a new FT_Face from a resource file. */
910   static FT_Error
FT_New_Face_From_Resource(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)911   FT_New_Face_From_Resource( FT_Library    library,
912                              const UInt8*  pathname,
913                              FT_Long       face_index,
914                              FT_Face*      aface )
915   {
916     OSType    file_type;
917     FT_Error  error;
918 
919 
920     /* LWFN is a (very) specific file format, check for it explicitly */
921     file_type = get_file_type_from_path( pathname );
922     if ( file_type == TTAG_LWFN )
923       return FT_New_Face_From_LWFN( library, pathname, face_index, aface );
924 
925     /* Otherwise the file type doesn't matter (there are more than  */
926     /* `FFIL' and `tfil').  Just try opening it as a font suitcase; */
927     /* if it works, fine.                                           */
928 
929     error = FT_New_Face_From_Suitcase( library, pathname, face_index, aface );
930     if ( error == 0 )
931       return error;
932 
933     /* let it fall through to normal loader (.ttf, .otf, etc.); */
934     /* we signal this by returning no error and no FT_Face      */
935     *aface = NULL;
936     return 0;
937   }
938 
939 
940   /*************************************************************************/
941   /*                                                                       */
942   /* <Function>                                                            */
943   /*    FT_New_Face                                                        */
944   /*                                                                       */
945   /* <Description>                                                         */
946   /*    This is the Mac-specific implementation of FT_New_Face.  In        */
947   /*    addition to the standard FT_New_Face() functionality, it also      */
948   /*    accepts pathnames to Mac suitcase files.  For further              */
949   /*    documentation see the original FT_New_Face() in freetype.h.        */
950   /*                                                                       */
951   FT_EXPORT_DEF( FT_Error )
FT_New_Face(FT_Library library,const char * pathname,FT_Long face_index,FT_Face * aface)952   FT_New_Face( FT_Library   library,
953                const char*  pathname,
954                FT_Long      face_index,
955                FT_Face*     aface )
956   {
957     FT_Open_Args  args;
958     FT_Error      error;
959 
960 
961     /* test for valid `library' and `aface' delayed to FT_Open_Face() */
962     if ( !pathname )
963       return FT_THROW( Invalid_Argument );
964 
965     error  = FT_Err_Ok;
966     *aface = NULL;
967 
968     /* try resourcefork based font: LWFN, FFIL */
969     error = FT_New_Face_From_Resource( library, (UInt8 *)pathname,
970                                        face_index, aface );
971     if ( error != 0 || *aface != NULL )
972       return error;
973 
974     /* let it fall through to normal loader (.ttf, .otf, etc.) */
975     args.flags    = FT_OPEN_PATHNAME;
976     args.pathname = (char*)pathname;
977     return FT_Open_Face( library, &args, face_index, aface );
978   }
979 
980 
981   /*************************************************************************/
982   /*                                                                       */
983   /* <Function>                                                            */
984   /*    FT_New_Face_From_FSRef                                             */
985   /*                                                                       */
986   /* <Description>                                                         */
987   /*    FT_New_Face_From_FSRef is identical to FT_New_Face except it       */
988   /*    accepts an FSRef instead of a path.                                */
989   /*                                                                       */
990   /* This function is deprecated because Carbon data types (FSRef)         */
991   /* are not cross-platform, and thus not suitable for the freetype API.   */
992   FT_EXPORT_DEF( FT_Error )
FT_New_Face_From_FSRef(FT_Library library,const FSRef * ref,FT_Long face_index,FT_Face * aface)993   FT_New_Face_From_FSRef( FT_Library    library,
994                           const FSRef*  ref,
995                           FT_Long       face_index,
996                           FT_Face*      aface )
997   {
998     FT_Error      error;
999     FT_Open_Args  args;
1000     OSErr   err;
1001     UInt8   pathname[PATH_MAX];
1002 
1003 
1004     if ( !ref )
1005       return FT_THROW( Invalid_Argument );
1006 
1007     err = FSRefMakePath( ref, pathname, sizeof ( pathname ) );
1008     if ( err )
1009       error = FT_THROW( Cannot_Open_Resource );
1010 
1011     error = FT_New_Face_From_Resource( library, pathname, face_index, aface );
1012     if ( error != 0 || *aface != NULL )
1013       return error;
1014 
1015     /* fallback to datafork font */
1016     args.flags    = FT_OPEN_PATHNAME;
1017     args.pathname = (char*)pathname;
1018     return FT_Open_Face( library, &args, face_index, aface );
1019   }
1020 
1021 
1022   /*************************************************************************/
1023   /*                                                                       */
1024   /* <Function>                                                            */
1025   /*    FT_New_Face_From_FSSpec                                            */
1026   /*                                                                       */
1027   /* <Description>                                                         */
1028   /*    FT_New_Face_From_FSSpec is identical to FT_New_Face except it      */
1029   /*    accepts an FSSpec instead of a path.                               */
1030   /*                                                                       */
1031   /* This function is deprecated because FSSpec is deprecated in Mac OS X  */
1032   FT_EXPORT_DEF( FT_Error )
FT_New_Face_From_FSSpec(FT_Library library,const FSSpec * spec,FT_Long face_index,FT_Face * aface)1033   FT_New_Face_From_FSSpec( FT_Library     library,
1034                            const FSSpec*  spec,
1035                            FT_Long        face_index,
1036                            FT_Face*       aface )
1037   {
1038 #if ( __LP64__ ) || ( defined( MAC_OS_X_VERSION_10_5 ) && \
1039       ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) )
1040     FT_UNUSED( library );
1041     FT_UNUSED( spec );
1042     FT_UNUSED( face_index );
1043     FT_UNUSED( aface );
1044 
1045     return FT_THROW( Unimplemented_Feature );
1046 #else
1047     FSRef  ref;
1048 
1049 
1050     if ( !spec || FSpMakeFSRef( spec, &ref ) != noErr )
1051       return FT_THROW( Invalid_Argument );
1052     else
1053       return FT_New_Face_From_FSRef( library, &ref, face_index, aface );
1054 #endif
1055   }
1056 
1057 #endif /* FT_MACINTOSH */
1058 
1059 
1060 /* END */
1061