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