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