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