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     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
parse_fond(char * fond_data,short * have_sfnt,ResID * sfnt_id,Str255 lwfn_file_name,short face_index)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
lookup_lwfn_by_fond(const UInt8 * path_fond,ConstStr255Param base_lwfn,UInt8 * path_lwfn,size_t path_size)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
count_faces(Handle fond,const UInt8 * pathname)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
read_lwfn(FT_Memory memory,ResFileRefNum res,FT_Byte ** pfb_data,FT_ULong * size)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
FT_New_Face_From_LWFN(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)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
FT_New_Face_From_SFNT(FT_Library library,ResID sfnt_id,FT_Long face_index,FT_Face * aface)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
FT_New_Face_From_Suitcase(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)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 )
FT_New_Face_From_FOND(FT_Library library,Handle fond,FT_Long face_index,FT_Face * aface)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
FT_New_Face_From_Resource(FT_Library library,const UInt8 * pathname,FT_Long face_index,FT_Face * aface)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 )
FT_New_Face(FT_Library library,const char * pathname,FT_Long face_index,FT_Face * aface)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     *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