update for HEAD-2003050101
[reactos.git] / lib / freetype / src / base / ftmac.c
1 /***************************************************************************/
2 /*                                                                         */
3 /*  ftmac.c                                                                */
4 /*                                                                         */
5 /*    Mac FOND support.  Written by just@letterror.com.                    */
6 /*                                                                         */
7 /*  Copyright 1996-2001, 2002 by                                           */
8 /*  Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg.     */
9 /*                                                                         */
10 /*  This file is part of the FreeType project, and may only be used,       */
11 /*  modified, and distributed under the terms of the FreeType project      */
12 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
13 /*  this file you indicate that you have read the license and              */
14 /*  understand and accept it fully.                                        */
15 /*                                                                         */
16 /***************************************************************************/
17
18
19   /*
20     Notes
21
22     Mac suitcase files can (and often do!) contain multiple fonts.  To
23     support this I use the face_index argument of FT_(Open|New)_Face()
24     functions, and pretend the suitcase file is a collection.
25
26     Warning: Although the FOND driver sets face->num_faces field to the
27     number of available fonts, but the Type 1 driver sets it to 1 anyway.
28     So this field is currently not reliable, and I don't see a clean way
29     to  resolve that.  The face_index argument translates to
30
31       Get1IndResource( 'FOND', face_index + 1 );
32
33     so clients should figure out the resource index of the FOND.
34     (I'll try to provide some example code for this at some point.)
35
36     The Mac FOND support works roughly like this:
37
38     - Check whether the offered stream points to a Mac suitcase file.
39       This is done by checking the file type: it has to be 'FFIL' or 'tfil'.
40       The stream that gets passed to our init_face() routine is a stdio
41       stream, which isn't usable for us, since the FOND resources live
42       in the resource fork.  So we just grab the stream->pathname field.
43
44     - Read the FOND resource into memory, then check whether there is
45       a TrueType font and/or(!) a Type 1 font available.
46
47     - If there is a Type 1 font available (as a separate 'LWFN' file),
48       read its data into memory, massage it slightly so it becomes
49       PFB data, wrap it into a memory stream, load the Type 1 driver
50       and delegate the rest of the work to it by calling FT_Open_Face().
51       (XXX TODO: after this has been done, the kerning data from the FOND
52       resource should be appended to the face: On the Mac there are usually
53       no AFM files available.  However, this is tricky since we need to map
54       Mac char codes to ps glyph names to glyph ID's...)
55
56     - If there is a TrueType font (an 'sfnt' resource), read it into
57       memory, wrap it into a memory stream, load the TrueType driver
58       and delegate the rest of the work to it, by calling FT_Open_Face().
59   */
60
61
62 #include <ft2build.h>
63 #include FT_FREETYPE_H
64 #include FT_INTERNAL_STREAM_H
65 #include "truetype/ttobjs.h"
66 #include "type1/t1objs.h"
67
68 #include <Resources.h>
69 #include <Fonts.h>
70 #include <Errors.h>
71 #include <Files.h>
72 #include <TextUtils.h>
73
74
75 #include FT_MAC_H
76
77
78   /* Set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over
79      TrueType in case *both* are available (this is not common,
80      but it *is* possible). */
81 #ifndef PREFER_LWFN
82 #define PREFER_LWFN 1
83 #endif
84
85   /* Given a pathname, fill in a file spec. */
86   static int
87   file_spec_from_path( const char*  pathname,
88                        FSSpec*      spec )
89   {
90 #if TARGET_API_MAC_CARBON
91
92     OSErr  e;
93     FSRef  ref;
94
95
96     e = FSPathMakeRef( (UInt8 *)pathname, &ref, false /* not a directory */ );
97     if ( e == noErr )
98       e = FSGetCatalogInfo( &ref, kFSCatInfoNone, NULL, NULL, spec, NULL );
99
100     return ( e == noErr ) ? 0 : (-1);
101
102 #else
103
104     Str255    p_path;
105     FT_ULong  path_len;
106
107
108     /* convert path to a pascal string */
109     path_len = ft_strlen( pathname );
110     if ( path_len > 255 )
111       return -1;
112     p_path[0] = (unsigned char)path_len;
113     ft_strncpy( (char*)p_path + 1, pathname, path_len );
114
115     if ( FSMakeFSSpec( 0, 0, p_path, spec ) != noErr )
116       return -1;
117     else
118       return 0;
119
120 #endif
121   }
122
123
124   /* Return the file type of the file specified by spec. */
125   static OSType
126   get_file_type( FSSpec*  spec )
127   {
128     FInfo  finfo;
129
130
131     if ( FSpGetFInfo( spec, &finfo ) != noErr )
132       return 0;  /* file might not exist */
133
134     return finfo.fdType;
135   }
136
137
138 #if TARGET_API_MAC_CARBON
139
140   /* is this a Mac OS X .dfont file */
141   static Boolean
142   is_dfont( FSSpec*  spec )
143   {
144     int  nameLen = spec->name[0];
145
146
147     return nameLen >= 6                                   &&
148            !memcmp( spec->name + nameLen - 5, ".dfont", 6 );
149   }
150
151 #endif
152
153
154   /* Given a PostScript font name, create the Macintosh LWFN file name. */
155   static void
156   create_lwfn_name( char*   ps_name,
157                     Str255  lwfn_file_name )
158   {
159     int       max = 5, count = 0;
160     FT_Byte*  p = lwfn_file_name;
161     FT_Byte*  q = (FT_Byte*)ps_name;
162
163
164     lwfn_file_name[0] = 0;
165
166     while ( *q )
167     {
168       if ( isupper( *q ) )
169       {
170         if ( count )
171           max = 3;
172         count = 0;
173       }
174       if ( count < max && ( ft_isalnum( *q ) || *q == '_' ) )
175       {
176         *++p = *q;
177         lwfn_file_name[0]++;
178         count++;
179       }
180       q++;
181     }
182   }
183
184
185   /* Given a file reference, answer its location as a vRefNum
186      and a dirID. */
187   static FT_Error
188   get_file_location( short           ref_num,
189                      short*          v_ref_num,
190                      long*           dir_id,
191                      unsigned char*  file_name )
192   {
193     FCBPBRec  pb;
194     OSErr     error;
195
196
197     pb.ioNamePtr = file_name;
198     pb.ioVRefNum = 0;
199     pb.ioRefNum  = ref_num;
200     pb.ioFCBIndx = 0;
201
202     error = PBGetFCBInfoSync( &pb );
203     if ( error == noErr )
204     {
205       *v_ref_num = pb.ioFCBVRefNum;
206       *dir_id    = pb.ioFCBParID;
207     }
208     return error;
209   }
210
211
212   /* Make a file spec for an LWFN file from a FOND resource and
213      a file name. */
214   static FT_Error
215   make_lwfn_spec( Handle          fond,
216                   unsigned char*  file_name,
217                   FSSpec*         spec )
218   {
219     FT_Error  error;
220     short     ref_num, v_ref_num;
221     long      dir_id;
222     Str255    fond_file_name;
223
224
225     ref_num = HomeResFile( fond );
226
227     error = ResError();
228     if ( !error )
229       error = get_file_location( ref_num, &v_ref_num,
230                                  &dir_id, fond_file_name );
231     if ( !error )
232       error = FSMakeFSSpec( v_ref_num, dir_id, file_name, spec );
233
234     return error;
235   }
236
237
238   /* Look inside the FOND data, answer whether there should be an SFNT
239      resource, and answer the name of a possible LWFN Type 1 file.
240
241      Thanks to Paul Miller (paulm@profoundeffects.com) for the fix
242      to load a face OTHER than the first one in the FOND!
243   */
244   static void
245   parse_fond( char*   fond_data,
246               short*  have_sfnt,
247               short*  sfnt_id,
248               Str255  lwfn_file_name,
249               short   face_index )
250   {
251     AsscEntry*  assoc;
252     AsscEntry*  base_assoc;
253     FamRec*     fond;
254
255
256     *sfnt_id          = 0;
257     *have_sfnt        = 0;
258     lwfn_file_name[0] = 0;
259
260     fond       = (FamRec*)fond_data;
261     assoc      = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
262     base_assoc = assoc;
263     assoc     += face_index;        /* add on the face_index! */
264
265     /* if the face at this index is not scalable,
266        fall back to the first one (old behavior) */
267     if ( assoc->fontSize == 0 )
268     {
269       *have_sfnt = 1;
270       *sfnt_id   = assoc->fontID;
271     }
272     else if ( base_assoc->fontSize == 0 )
273     {
274       *have_sfnt = 1;
275       *sfnt_id   = base_assoc->fontID;
276     }
277
278     if ( fond->ffStylOff )
279     {
280       unsigned char*  p = (unsigned char*)fond_data;
281       StyleTable*     style;
282       unsigned short  string_count;
283       char            ps_name[256];
284       unsigned char*  names[64];
285       int             i;
286
287
288       p += fond->ffStylOff;
289       style = (StyleTable*)p;
290       p += sizeof ( StyleTable );
291       string_count = *(unsigned short*)(p);
292       p += sizeof ( short );
293
294       for ( i = 0 ; i < string_count && i < 64; i++ )
295       {
296         names[i] = p;
297         p += names[i][0];
298         p++;
299       }
300
301       {
302         size_t  ps_name_len = (size_t)names[0][0];
303
304
305         if ( ps_name_len != 0 )
306         {
307           memcpy(ps_name, names[0] + 1, ps_name_len);
308           ps_name[ps_name_len] = 0;
309         }
310         if ( style->indexes[0] > 1 )
311         {
312           unsigned char*  suffixes = names[style->indexes[0] - 1];
313
314
315           for ( i = 1; i < suffixes[0]; i++ )
316           {
317             unsigned char*  s;
318             size_t          j = suffixes[i] - 1;
319
320
321             if ( j < string_count && ( s = names[j] ) != NULL )
322             {
323               size_t  s_len = (size_t)s[0];
324
325
326               if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) )
327               {
328                 memcpy( ps_name + ps_name_len, s + 1, s_len );
329                 ps_name_len += s_len;
330                 ps_name[ps_name_len] = 0;
331               }
332             }
333           }
334         }
335       }
336
337       create_lwfn_name( ps_name, lwfn_file_name );
338     }
339   }
340
341
342   /* Read Type 1 data from the POST resources inside the LWFN file,
343      return a PFB buffer. This is somewhat convoluted because the FT2
344      PFB parser wants the ASCII header as one chunk, and the LWFN
345      chunks are often not organized that way, so we'll glue chunks
346      of the same type together. */
347   static FT_Error
348   read_lwfn( FT_Memory  memory,
349              FSSpec*    lwfn_spec,
350              FT_Byte**  pfb_data,
351              FT_ULong*  size )
352   {
353     FT_Error       error = FT_Err_Ok;
354     short          res_ref, res_id;
355     unsigned char  *buffer, *p, *size_p = NULL;
356     FT_ULong       total_size = 0;
357     FT_ULong       post_size, pfb_chunk_size;
358     Handle         post_data;
359     char           code, last_code;
360
361
362     res_ref = FSpOpenResFile( lwfn_spec, fsRdPerm );
363     if ( ResError() )
364       return FT_Err_Out_Of_Memory;
365     UseResFile( res_ref );
366
367     /* First pass: load all POST resources, and determine the size of
368        the output buffer. */
369     res_id    = 501;
370     last_code = -1;
371
372     for (;;)
373     {
374       post_data = Get1Resource( 'POST', res_id++ );
375       if ( post_data == NULL )
376         break;  /* we're done */
377
378       code = (*post_data)[0];
379
380       if ( code != last_code )
381       {
382         if ( code == 5 )
383           total_size += 2; /* just the end code */
384         else
385           total_size += 6; /* code + 4 bytes chunk length */
386       }
387
388       total_size += GetHandleSize( post_data ) - 2;
389       last_code = code;
390     }
391
392     if ( FT_ALLOC( buffer, (FT_Long)total_size ) )
393       goto Error;
394
395     /* Second pass: append all POST data to the buffer, add PFB fields.
396        Glue all consecutive chunks of the same type together. */
397     p              = buffer;
398     res_id         = 501;
399     last_code      = -1;
400     pfb_chunk_size = 0;
401
402     for (;;)
403     {
404       post_data = Get1Resource( 'POST', res_id++ );
405       if ( post_data == NULL )
406         break;  /* we're done */
407
408       post_size = (FT_ULong)GetHandleSize( post_data ) - 2;
409       code = (*post_data)[0];
410
411       if ( code != last_code )
412       {
413         if ( last_code != -1 )
414         {
415           /* we're done adding a chunk, fill in the size field */
416           if ( size_p != NULL )
417           {
418             *size_p++ = (FT_Byte)(   pfb_chunk_size         & 0xFF );
419             *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8  ) & 0xFF );
420             *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF );
421             *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF );
422           }
423           pfb_chunk_size = 0;
424         }
425
426         *p++ = 0x80;
427         if ( code == 5 )
428           *p++ = 0x03;  /* the end */
429         else if ( code == 2 )
430           *p++ = 0x02;  /* binary segment */
431         else
432           *p++ = 0x01;  /* ASCII segment */
433
434         if ( code != 5 )
435         {
436           size_p = p;   /* save for later */
437           p += 4;       /* make space for size field */
438         }
439       }
440
441       ft_memcpy( p, *post_data + 2, post_size );
442       pfb_chunk_size += post_size;
443       p += post_size;
444       last_code = code;
445     }
446
447     *pfb_data = buffer;
448     *size = total_size;
449
450   Error:
451     CloseResFile( res_ref );
452     return error;
453   }
454
455
456   /* Finalizer for a memory stream; gets called by FT_Done_Face().
457      It frees the memory it uses. */
458   static void
459   memory_stream_close( FT_Stream  stream )
460   {
461     FT_Memory  memory = stream->memory;
462
463
464     FT_FREE( stream->base );
465
466     stream->size  = 0;
467     stream->base  = 0;
468     stream->close = 0;
469   }
470
471
472   /* Create a new memory stream from a buffer and a size. */
473   static FT_Error
474   new_memory_stream( FT_Library           library,
475                      FT_Byte*             base,
476                      FT_ULong             size,
477                      FT_Stream_CloseFunc  close,
478                      FT_Stream           *astream )
479   {
480     FT_Error   error;
481     FT_Memory  memory;
482     FT_Stream  stream;
483
484
485     if ( !library )
486       return FT_Err_Invalid_Library_Handle;
487
488     if ( !base )
489       return FT_Err_Invalid_Argument;
490
491     *astream = 0;
492     memory = library->memory;
493     if ( FT_NEW( stream ) )
494       goto Exit;
495
496     FT_Stream_OpenMemory( stream, base, size );
497
498     stream->close = close;
499
500     *astream = stream;
501
502   Exit:
503     return error;
504   }
505
506
507   /* Create a new FT_Face given a buffer and a driver name. */
508   static FT_Error
509   open_face_from_buffer( FT_Library  library,
510                          FT_Byte*    base,
511                          FT_ULong    size,
512                          FT_Long     face_index,
513                          char*       driver_name,
514                          FT_Face    *aface )
515   {
516     FT_Open_Args  args;
517     FT_Error      error;
518     FT_Stream     stream;
519     FT_Memory     memory = library->memory;
520
521
522     error = new_memory_stream( library,
523                                base,
524                                size,
525                                memory_stream_close,
526                                &stream );
527     if ( error )
528     {
529       FT_FREE( base );
530       return error;
531     }
532
533     args.flags = FT_OPEN_STREAM;
534     args.stream = stream;
535     if ( driver_name )
536     {
537       args.flags = args.flags | FT_OPEN_DRIVER;
538       args.driver = FT_Get_Module( library, driver_name );
539     }
540
541     error = FT_Open_Face( library, &args, face_index, aface );
542     if ( error == FT_Err_Ok )
543       (*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
544     else
545     {
546       FT_Stream_CloseFunc( stream );
547       FT_FREE( stream );
548     }
549
550     return error;
551   }
552
553
554   /* Create a new FT_Face from a file spec to an LWFN file. */
555   static FT_Error
556   FT_New_Face_From_LWFN( FT_Library  library,
557                          FSSpec*     spec,
558                          FT_Long     face_index,
559                          FT_Face    *aface )
560   {
561     FT_Byte*  pfb_data;
562     FT_ULong  pfb_size;
563     FT_Error  error;
564
565
566     error = read_lwfn( library->memory, spec, &pfb_data, &pfb_size );
567     if ( error )
568       return error;
569
570     return open_face_from_buffer( library,
571                                   pfb_data,
572                                   pfb_size,
573                                   face_index,
574                                   "type1",
575                                   aface );
576   }
577
578
579   /* Create a new FT_Face from an SFNT resource, specified by res ID. */
580   static FT_Error
581   FT_New_Face_From_SFNT( FT_Library  library,
582                          short       sfnt_id,
583                          FT_Long     face_index,
584                          FT_Face    *aface )
585   {
586     Handle     sfnt = NULL;
587     FT_Byte*   sfnt_data;
588     size_t     sfnt_size;
589     FT_Error   error = 0;
590     FT_Memory  memory = library->memory;
591
592
593     sfnt = GetResource( 'sfnt', sfnt_id );
594     if ( ResError() )
595       return FT_Err_Invalid_Handle;
596
597     sfnt_size = (FT_ULong)GetHandleSize( sfnt );
598     if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) )
599     {
600       ReleaseResource( sfnt );
601       return error;
602     }
603
604     HLock( sfnt );
605     ft_memcpy( sfnt_data, *sfnt, sfnt_size );
606     HUnlock( sfnt );
607     ReleaseResource( sfnt );
608
609     return open_face_from_buffer( library,
610                                   sfnt_data,
611                                   sfnt_size,
612                                   face_index,
613                                   "truetype",
614                                   aface );
615   }
616
617
618   /* Create a new FT_Face from a file spec to a suitcase file. */
619   static FT_Error
620   FT_New_Face_From_Suitcase( FT_Library  library,
621                              FSSpec*     spec,
622                              FT_Long     face_index,
623                              FT_Face    *aface )
624   {
625     FT_Error  error = FT_Err_Ok;
626     short     res_ref, res_index;
627     Handle    fond;
628
629
630     res_ref = FSpOpenResFile( spec, fsRdPerm );
631     if ( ResError() )
632       return FT_Err_Cannot_Open_Resource;
633     UseResFile( res_ref );
634
635     /* face_index may be -1, in which case we
636        just need to do a sanity check */
637     if ( face_index < 0 )
638       res_index = 1;
639     else
640     {
641       res_index = (short)( face_index + 1 );
642       face_index = 0;
643     }
644     fond = Get1IndResource( 'FOND', res_index );
645     if ( ResError() )
646     {
647       error = FT_Err_Cannot_Open_Resource;
648       goto Error;
649     }
650
651     error = FT_New_Face_From_FOND( library, fond, face_index, aface );
652
653   Error:
654     CloseResFile( res_ref );
655     return error;
656   }
657
658
659 #if TARGET_API_MAC_CARBON
660
661   /* Create a new FT_Face from a file spec to a suitcase file. */
662   static FT_Error
663   FT_New_Face_From_dfont( FT_Library  library,
664                           FSSpec*     spec,
665                           FT_Long     face_index,
666                           FT_Face*    aface )
667   {
668     FT_Error  error = FT_Err_Ok;
669     short     res_ref, res_index;
670     Handle    fond;
671     FSRef     hostContainerRef;
672
673
674     error = FSpMakeFSRef( spec, &hostContainerRef );
675     if ( error == noErr )
676       error = FSOpenResourceFile( &hostContainerRef,
677                                   0, NULL, fsRdPerm, &res_ref );
678
679     if ( error != noErr )
680       return FT_Err_Cannot_Open_Resource;
681
682     UseResFile( res_ref );
683
684     /* face_index may be -1, in which case we
685        just need to do a sanity check */
686     if ( face_index < 0 )
687       res_index = 1;
688     else
689     {
690       res_index = (short)( face_index + 1 );
691       face_index = 0;
692     }
693     fond = Get1IndResource( 'FOND', res_index );
694     if ( ResError() )
695     {
696       error = FT_Err_Cannot_Open_Resource;
697       goto Error;
698     }
699
700     error = FT_New_Face_From_FOND( library, fond, face_index, aface );
701
702   Error:
703     CloseResFile( res_ref );
704     return error;
705   }
706
707 #endif
708
709
710   /* documentation is in ftmac.h */
711
712   FT_EXPORT_DEF( FT_Error )
713   FT_New_Face_From_FOND( FT_Library  library,
714                          Handle      fond,
715                          FT_Long     face_index,
716                          FT_Face    *aface )
717   {
718     short   sfnt_id, have_sfnt, have_lwfn = 0;
719     Str255  lwfn_file_name;
720     short   fond_id;
721     OSType  fond_type;
722     Str255  fond_name;
723     FSSpec  lwfn_spec;
724
725
726     GetResInfo( fond, &fond_id, &fond_type, fond_name );
727     if ( ResError() != noErr || fond_type != 'FOND' )
728       return FT_Err_Invalid_File_Format;
729
730     HLock( fond );
731     parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index );
732     HUnlock( fond );
733
734     if ( lwfn_file_name[0] )
735     {
736       if ( make_lwfn_spec( fond, lwfn_file_name, &lwfn_spec ) == FT_Err_Ok )
737         have_lwfn = 1;  /* yeah, we got one! */
738       else
739         have_lwfn = 0;  /* no LWFN file found */
740     }
741
742     if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
743       return FT_New_Face_From_LWFN( library,
744                                     &lwfn_spec,
745                                     face_index,
746                                     aface );
747     else if ( have_sfnt )
748       return FT_New_Face_From_SFNT( library,
749                                     sfnt_id,
750                                     face_index,
751                                     aface );
752
753     return FT_Err_Unknown_File_Format;
754   }
755
756
757   /* documentation is in ftmac.h */
758
759   FT_EXPORT_DEF( FT_Error )
760   FT_GetFile_From_Mac_Name( char*     fontName,
761                             FSSpec*   pathSpec,
762                             FT_Long*  face_index )
763   {
764     OptionBits            options = kFMUseGlobalScopeOption;
765
766     FMFontFamilyIterator  famIter;
767     OSStatus              status = FMCreateFontFamilyIterator( NULL, NULL,
768                                                                options,
769                                                                &famIter );
770     FMFont                the_font = NULL;
771     FMFontFamily          family   = NULL;
772
773
774     *face_index = 0;
775     while ( status == 0 && !the_font )
776     {
777       status = FMGetNextFontFamily( &famIter, &family );
778       if ( status == 0 )
779       {
780         int                           stat2;
781         FMFontFamilyInstanceIterator  instIter;
782         Str255                        famNameStr;
783         char                          famName[256];
784
785
786         /* get the family name */
787         FMGetFontFamilyName( family, famNameStr );
788         CopyPascalStringToC( famNameStr, famName );
789
790         /* iterate through the styles */
791         FMCreateFontFamilyInstanceIterator( family, &instIter );
792
793         *face_index = 0;
794         stat2 = 0;
795         while ( stat2 == 0 && !the_font )
796         {
797           FMFontStyle  style;
798           FMFontSize   size;
799           FMFont       font;
800
801
802           stat2 = FMGetNextFontFamilyInstance( &instIter, &font,
803                                                &style, &size );
804           if ( stat2 == 0 && size == 0 )
805           {
806             char  fullName[256];
807
808
809             /* build up a complete face name */
810             ft_strcpy( fullName, famName );
811             if ( style & bold )
812               strcat( fullName, " Bold" );
813             if ( style & italic )
814               strcat( fullName, " Italic" );
815
816             /* compare with the name we are looking for */
817             if ( ft_strcmp( fullName, fontName ) == 0 )
818             {
819               /* found it! */
820               the_font = font;
821             }
822             else
823                ++(*face_index);
824           }
825         }
826
827         FMDisposeFontFamilyInstanceIterator( &instIter );
828       }
829     }
830
831     FMDisposeFontFamilyIterator( &famIter );
832
833     if ( the_font )
834     {
835       FMGetFontContainer( the_font, pathSpec );
836       return FT_Err_Ok;
837     }
838     else
839       return FT_Err_Unknown_File_Format;
840   }
841
842
843   static long
844   ResourceForkSize(FSSpec*  spec)
845   {
846     long   len;
847     short  refNum;
848     OSErr  e;
849
850
851     e = FSpOpenRF( spec, fsRdPerm, &refNum ); /* I.M. Files 2-155 */
852     if ( e == noErr )
853     {
854       e = GetEOF( refNum, &len );
855       FSClose( refNum );
856     }
857
858     return ( e == noErr ) ? len : 0;
859   }
860
861
862   /*************************************************************************/
863   /*                                                                       */
864   /* <Function>                                                            */
865   /*    FT_New_Face                                                        */
866   /*                                                                       */
867   /* <Description>                                                         */
868   /*    This is the Mac-specific implementation of FT_New_Face.  In        */
869   /*    addition to the standard FT_New_Face() functionality, it also      */
870   /*    accepts pathnames to Mac suitcase files.  For further              */
871   /*    documentation see the original FT_New_Face() in freetype.h.        */
872   /*                                                                       */
873   FT_EXPORT_DEF( FT_Error )
874   FT_New_Face( FT_Library   library,
875                const char*  pathname,
876                FT_Long      face_index,
877                FT_Face     *aface )
878   {
879     FT_Open_Args  args;
880     FSSpec        spec;
881     OSType        file_type;
882
883
884     /* test for valid `library' and `aface' delayed to FT_Open_Face() */
885     if ( !pathname )
886       return FT_Err_Invalid_Argument;
887
888     if ( file_spec_from_path( pathname, &spec ) )
889       return FT_Err_Invalid_Argument;
890
891     /* Regardless of type, don't try to use the resource fork if it is */
892     /* empty.  Some TTF fonts have type `FFIL', for example, but they  */
893     /* only have data forks.                                           */
894
895     if ( ResourceForkSize( &spec ) != 0 )
896     {
897       file_type = get_file_type( &spec );
898       if ( file_type == 'FFIL' || file_type == 'tfil' )
899         return FT_New_Face_From_Suitcase( library, &spec, face_index, aface );
900
901       if ( file_type == 'LWFN' )
902         return FT_New_Face_From_LWFN( library, &spec, face_index, aface );
903     }
904
905 #if TARGET_API_MAC_CARBON
906
907     if ( is_dfont( &spec ) )
908       return FT_New_Face_From_dfont( library, &spec, face_index, aface );
909
910 #endif
911
912     /* let it fall through to normal loader (.ttf, .otf, etc.) */
913     args.flags    = FT_OPEN_PATHNAME;
914     args.pathname = (char*)pathname;
915     return FT_Open_Face( library, &args, face_index, aface );
916   }
917
918
919 /* END */