source: trunk/poppler/freetype2/src/base/ftmac.c @ 165

Last change on this file since 165 was 165, checked in by Eugene Romanenko, 15 years ago

update to latest freetype cvs, (closes #76)

File size: 41.3 KB
Line 
1/***************************************************************************/
2/*                                                                         */
3/*  ftmac.c                                                                */
4/*                                                                         */
5/*    Mac FOND support.  Written by just@letterror.com.                    */
6/*  Heavily Fixed by mpsuzuki, George Williams and Sean McBride            */
7/*                                                                         */
8/*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006 by                   */
9/*  Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg.     */
10/*                                                                         */
11/*  This file is part of the FreeType project, and may only be used,       */
12/*  modified, and distributed under the terms of the FreeType project      */
13/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
14/*  this file you indicate that you have read the license and              */
15/*  understand and accept it fully.                                        */
16/*                                                                         */
17/***************************************************************************/
18
19
20  /*
21    Notes
22
23    Mac suitcase files can (and often do!) contain multiple fonts.  To
24    support this I use the face_index argument of FT_(Open|New)_Face()
25    functions, and pretend the suitcase file is a collection.
26
27    Warning: fbit and NFNT bitmap resources are not supported yet.  In old
28    sfnt fonts, bitmap glyph data for each size is stored in each `NFNT'
29    resources instead of the `bdat' table in the sfnt resource.  Therefore,
30    face->num_fixed_sizes is set to 0, because bitmap data in `NFNT'
31    resource is unavailable at present.
32
33    The Mac FOND support works roughly like this:
34
35    - Check whether the offered stream points to a Mac suitcase file.  This
36      is done by checking the file type: it has to be 'FFIL' or 'tfil'.  The
37      stream that gets passed to our init_face() routine is a stdio stream,
38      which isn't usable for us, since the FOND resources live in the
39      resource fork.  So we just grab the stream->pathname field.
40
41    - Read the FOND resource into memory, then check whether there is a
42      TrueType font and/or(!) a Type 1 font available.
43
44    - If there is a Type 1 font available (as a separate `LWFN' file), read
45      its data into memory, massage it slightly so it becomes PFB data, wrap
46      it into a memory stream, load the Type 1 driver and delegate the rest
47      of the work to it by calling FT_Open_Face().  (XXX TODO: after this
48      has been done, the kerning data from the FOND resource should be
49      appended to the face: On the Mac there are usually no AFM files
50      available.  However, this is tricky since we need to map Mac char
51      codes to ps glyph names to glyph ID's...)
52
53    - If there is a TrueType font (an `sfnt' resource), read it into memory,
54      wrap it into a memory stream, load the TrueType driver and delegate
55      the rest of the work to it, by calling FT_Open_Face().
56
57    - Some suitcase fonts (notably Onyx) might point the `LWFN' file to
58      itself, even though it doesn't contains `POST' resources.  To handle
59      this special case without opening the file an extra time, we just
60      ignore errors from the `LWFN' and fallback to the `sfnt' if both are
61      available.
62  */
63
64
65#include <ft2build.h>
66#include FT_FREETYPE_H
67#include FT_INTERNAL_STREAM_H
68
69#if defined( __GNUC__ ) || defined( __IBMC__ )
70  /* This is for Mac OS X.  Without redefinition, OS_INLINE */
71  /* expands to `static inline' which doesn't survive the   */
72  /* -ansi compilation flag of GCC.                         */
73#undef  OS_INLINE
74#define OS_INLINE   static __inline__
75#include <Carbon/Carbon.h>
76#else
77#include <Resources.h>
78#include <Fonts.h>
79#include <Endian.h>
80#include <Errors.h>
81#include <Files.h>
82#include <TextUtils.h>
83#endif
84
85#if defined( __MWERKS__ ) && !TARGET_RT_MAC_MACHO
86#include <FSp_fopen.h>
87#endif
88
89#include FT_MAC_H
90
91
92  /* FSSpec functions are deprecated since Mac OS X 10.4 */
93#ifndef HAVE_FSSPEC
94#if TARGET_API_MAC_OS8 || TARGET_API_MAC_CARBON
95#define HAVE_FSSPEC  1
96#else
97#define HAVE_FSSPEC  0
98#endif
99#endif
100
101  /* most FSRef functions were introduced since Mac OS 9 */
102#ifndef HAVE_FSREF
103#if TARGET_API_MAC_OSX
104#define HAVE_FSREF  1
105#else
106#define HAVE_FSREF  0
107#endif
108#endif
109
110#ifndef HFS_MAXPATHLEN
111#define HFS_MAXPATHLEN  1024
112#endif
113
114
115  /* QuickDraw is deprecated since Mac OS X 10.4 */
116#ifndef HAVE_QUICKDRAW_CARBON
117#if TARGET_API_MAC_OS8 || TARGET_API_MAC_CARBON
118#define HAVE_QUICKDRAW_CARBON  1
119#else
120#define HAVE_QUICKDRAW_CARBON  0
121#endif
122#endif
123
124  /* AppleTypeService is available since Mac OS X */
125#ifndef HAVE_ATS
126#if TARGET_API_MAC_OSX
127#define HAVE_ATS  1
128#else
129#define HAVE_ATS  0
130#endif
131#endif
132
133  /* Set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over
134     TrueType in case *both* are available (this is not common,
135     but it *is* possible). */
136#ifndef PREFER_LWFN
137#define PREFER_LWFN  1
138#endif
139
140
141#if !HAVE_QUICKDRAW_CARBON  /* QuickDraw is deprecated since Mac OS X 10.4 */
142
143  FT_EXPORT_DEF( FT_Error )
144  FT_GetFile_From_Mac_Name( const char*  fontName,
145                            FSSpec*      pathSpec,
146                            FT_Long*     face_index )
147  {
148    return FT_Err_Unimplemented_Feature;
149  }
150
151#else
152
153  FT_EXPORT_DEF( FT_Error )
154  FT_GetFile_From_Mac_Name( const char*  fontName,
155                            FSSpec*      pathSpec,
156                            FT_Long*     face_index )
157  {
158    OptionBits            options = kFMUseGlobalScopeOption;
159
160    FMFontFamilyIterator  famIter;
161    OSStatus              status = FMCreateFontFamilyIterator( NULL, NULL,
162                                                               options,
163                                                               &famIter );
164    FMFont                the_font = 0;
165    FMFontFamily          family   = 0;
166
167
168    *face_index = 0;
169    while ( status == 0 && !the_font )
170    {
171      status = FMGetNextFontFamily( &famIter, &family );
172      if ( status == 0 )
173      {
174        int                           stat2;
175        FMFontFamilyInstanceIterator  instIter;
176        Str255                        famNameStr;
177        char                          famName[256];
178
179
180        /* get the family name */
181        FMGetFontFamilyName( family, famNameStr );
182        CopyPascalStringToC( famNameStr, famName );
183
184        /* iterate through the styles */
185        FMCreateFontFamilyInstanceIterator( family, &instIter );
186
187        *face_index = 0;
188        stat2       = 0;
189
190        while ( stat2 == 0 && !the_font )
191        {
192          FMFontStyle  style;
193          FMFontSize   size;
194          FMFont       font;
195
196
197          stat2 = FMGetNextFontFamilyInstance( &instIter, &font,
198                                               &style, &size );
199          if ( stat2 == 0 && size == 0 )
200          {
201            char  fullName[256];
202
203
204            /* build up a complete face name */
205            ft_strcpy( fullName, famName );
206            if ( style & bold )
207              ft_strcat( fullName, " Bold" );
208            if ( style & italic )
209              ft_strcat( fullName, " Italic" );
210
211            /* compare with the name we are looking for */
212            if ( ft_strcmp( fullName, fontName ) == 0 )
213            {
214              /* found it! */
215              the_font = font;
216            }
217            else
218              ++(*face_index);
219          }
220        }
221
222        FMDisposeFontFamilyInstanceIterator( &instIter );
223      }
224    }
225
226    FMDisposeFontFamilyIterator( &famIter );
227
228    if ( the_font )
229    {
230      FMGetFontContainer( the_font, pathSpec );
231      return FT_Err_Ok;
232    }
233    else
234      return FT_Err_Unknown_File_Format;
235  }
236
237#endif /* HAVE_QUICKDRAW_CARBON */
238
239
240#if !HAVE_ATS
241
242  FT_EXPORT_DEF( FT_Error )
243  FT_GetFile_From_Mac_ATS_Name( const char*  fontName,
244                                FSSpec*      pathSpec,
245                                FT_Long*     face_index )
246  {
247    return FT_Err_Unimplemented_Feature;
248  }
249
250#else
251
252  FT_EXPORT_DEF( FT_Error )
253  FT_GetFile_From_Mac_ATS_Name( const char*  fontName,
254                                FSSpec*      pathSpec,
255                                FT_Long*     face_index )
256  {
257    CFStringRef  cf_fontName;
258    ATSFontRef   ats_font_id;
259
260
261    *face_index = 0;
262
263    cf_fontName = CFStringCreateWithCString( NULL, fontName,
264                                             kCFStringEncodingMacRoman );
265    ats_font_id = ATSFontFindFromName( cf_fontName,
266                                       kATSOptionFlagsUnRestrictedScope );
267
268    if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
269      return FT_Err_Unknown_File_Format;
270
271    if ( 0 != ATSFontGetFileSpecification( ats_font_id, pathSpec ) )
272      return FT_Err_Unknown_File_Format;
273
274    /* face_index calculation by searching preceding fontIDs */
275    /* with same FSRef                                       */
276    {
277      int     i;
278      FSSpec  f;
279
280
281      for ( i = 1; i < ats_font_id; i++ )
282      {
283        if ( 0 != ATSFontGetFileSpecification( ats_font_id - i,
284                                               &f               ) ||
285             f.vRefNum != pathSpec->vRefNum                       ||
286             f.parID   != pathSpec->parID                         ||
287             f.name[0] != pathSpec->name[0]                       ||
288             0 != ft_strncmp( (char *)f.name + 1,
289                              (char *)pathSpec->name + 1,
290                              f.name[0]                           ) )
291          break;
292      }
293      *face_index = ( i - 1 );
294    }
295    return FT_Err_Ok;
296  }
297
298#endif /* HAVE_ATS */
299
300
301#if defined( __MWERKS__ ) && !TARGET_RT_MAC_MACHO
302
303#define STREAM_FILE( stream )  ( (FT_FILE*)stream->descriptor.pointer )
304
305
306  FT_CALLBACK_DEF( void )
307  ft_FSp_stream_close( FT_Stream  stream )
308  {
309    ft_fclose( STREAM_FILE( stream ) );
310
311    stream->descriptor.pointer = NULL;
312    stream->size               = 0;
313    stream->base               = 0;
314  }
315
316
317  FT_CALLBACK_DEF( unsigned long )
318  ft_FSp_stream_io( FT_Stream       stream,
319                    unsigned long   offset,
320                    unsigned char*  buffer,
321                    unsigned long   count )
322  {
323    FT_FILE*  file;
324
325
326    file = STREAM_FILE( stream );
327
328    ft_fseek( file, offset, SEEK_SET );
329
330    return (unsigned long)ft_fread( buffer, 1, count, file );
331  }
332
333#endif  /* __MWERKS__ && !TARGET_RT_MAC_MACHO */
334
335
336#if HAVE_FSSPEC && !HAVE_FSREF
337
338  static OSErr
339  FT_FSPathMakeSpec( const UInt8*  pathname,
340                     FSSpec*       spec_p,
341                     Boolean       isDirectory )
342  {
343    const char  *p, *q;
344    short       vRefNum;
345    long        dirID;
346    Str255      nodeName;
347    OSErr       err;
348
349
350    p = q = (const char *)pathname;
351    dirID   = 0;
352    vRefNum = 0;
353
354    while ( 1 )
355    {
356      q = p + FT_MIN( 255, ft_strlen( p ) );
357
358      if ( q == p )
359        return 0;
360
361      if ( 255 < ft_strlen( (char *)pathname ) )
362      {
363        while ( p < q && *q != ':' )
364          q--;
365      }
366
367      if ( p < q )
368        *(char *)nodeName = q - p;
369      else if ( ft_strlen( p ) < 256 )
370        *(char *)nodeName = ft_strlen( p );
371      else
372        return errFSNameTooLong;
373
374      ft_strncpy( (char *)nodeName + 1, (char *)p, *(char *)nodeName );
375      err = FSMakeFSSpec( vRefNum, dirID, nodeName, spec_p );
376      if ( err || '\0' == *q )
377        return err;
378
379      vRefNum = spec_p->vRefNum;
380      dirID   = spec_p->parID;
381
382      p = q;
383    }
384  }
385
386
387  static OSErr
388  FT_FSpMakePath( const FSSpec*  spec_p,
389                  UInt8*         path,
390                  UInt32         maxPathSize )
391  {
392    OSErr   err;
393    FSSpec  spec = *spec_p;
394    short   vRefNum;
395    long    dirID;
396    Str255  parDir_name;
397
398
399    FT_MEM_SET( path, 0, maxPathSize );
400    while ( 1 )
401    {
402      int             child_namelen = ft_strlen( (char *)path );
403      unsigned char   node_namelen  = spec.name[0];
404      unsigned char*  node_name     = spec.name + 1;
405
406
407      if ( node_namelen + child_namelen > maxPathSize )
408        return errFSNameTooLong;
409
410      FT_MEM_MOVE( path + node_namelen + 1, path, child_namelen );
411      FT_MEM_COPY( path, node_name, node_namelen );
412      if ( child_namelen > 0 )
413        path[node_namelen] = ':';
414
415      vRefNum        = spec.vRefNum;
416      dirID          = spec.parID;
417      parDir_name[0] = '\0';
418      err = FSMakeFSSpec( vRefNum, dirID, parDir_name, &spec );
419      if ( noErr != err || dirID == spec.parID )
420        break;
421    }
422    return noErr;
423  }
424
425#endif /* HAVE_FSSPEC && !HAVE_FSREF */
426
427
428  static OSErr
429  FT_FSPathMakeRes( const UInt8*  pathname,
430                    short*        res )
431  {
432
433#if HAVE_FSREF
434
435    OSErr  err;
436    FSRef  ref;
437
438
439    if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
440      return FT_Err_Cannot_Open_Resource;
441
442    /* at present, no support for dfont format */
443    err = FSOpenResourceFile( &ref, 0, NULL, fsRdPerm, res );
444    if ( noErr == err )
445      return err;
446
447    /* fallback to original resource-fork font */
448    *res = FSOpenResFile( &ref, fsRdPerm );
449    err  = ResError();
450
451#else
452
453    OSErr   err;
454    FSSpec  spec;
455
456
457    if ( noErr != FT_FSPathMakeSpec( pathname, &spec, FALSE ) )
458      return FT_Err_Cannot_Open_Resource;
459
460    /* at present, no support for dfont format without FSRef */
461    /* (see above), try original resource-fork font          */
462    *res = FSpOpenResFile( &spec, fsRdPerm );
463    err  = ResError();
464
465#endif /* HAVE_FSREF */
466
467    return err;
468  }
469
470
471  /* Return the file type for given pathname */
472  static OSType
473  get_file_type_from_path( const UInt8*  pathname )
474  {
475
476#if HAVE_FSREF
477
478    FSRef          ref;
479    FSCatalogInfo  info;
480
481
482    if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
483      return ( OSType ) 0;
484
485    if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoFinderInfo, &info,
486                                    NULL, NULL, NULL ) )
487      return ( OSType ) 0;
488
489    return ((FInfo *)(info.finderInfo))->fdType;
490
491#else
492
493    FSSpec  spec;
494    FInfo  finfo;
495
496
497    if ( noErr != FT_FSPathMakeSpec( pathname, &spec, FALSE ) )
498      return ( OSType ) 0;
499
500    if ( noErr != FSpGetFInfo( &spec, &finfo ) )
501      return ( OSType ) 0;
502
503    return finfo.fdType;
504
505#endif /* HAVE_FSREF */
506
507  }
508
509
510  /* Given a PostScript font name, create the Macintosh LWFN file name. */
511  static void
512  create_lwfn_name( char*   ps_name,
513                    Str255  lwfn_file_name )
514  {
515    int       max = 5, count = 0;
516    FT_Byte*  p = lwfn_file_name;
517    FT_Byte*  q = (FT_Byte*)ps_name;
518
519
520    lwfn_file_name[0] = 0;
521
522    while ( *q )
523    {
524      if ( ft_isupper( *q ) )
525      {
526        if ( count )
527          max = 3;
528        count = 0;
529      }
530      if ( count < max && ( ft_isalnum( *q ) || *q == '_' ) )
531      {
532        *++p = *q;
533        lwfn_file_name[0]++;
534        count++;
535      }
536      q++;
537    }
538  }
539
540
541  static short
542  count_faces_sfnt( char*  fond_data )
543  {
544    /* The count is 1 greater than the value in the FOND.  */
545    /* Isn't that cute? :-)                                */
546
547    return EndianS16_BtoN( *( (short*)( fond_data +
548                                        sizeof ( FamRec ) ) ) ) + 1;
549  }
550
551
552  static short
553  count_faces_scalable( char*  fond_data )
554  {
555    AsscEntry*  assoc;
556    FamRec*     fond;
557    short       i, face, face_all;
558
559
560    fond     = (FamRec*)fond_data;
561    face_all = EndianS16_BtoN( *( (short *)( fond_data +
562                                             sizeof ( FamRec ) ) ) ) + 1;
563    assoc    = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
564    face     = 0;
565
566    for ( i = 0; i < face_all; i++ )
567    {
568      if ( 0 == EndianS16_BtoN( assoc[i].fontSize ) )
569        face++;
570    }
571    return face;
572  }
573
574
575  /* Look inside the FOND data, answer whether there should be an SFNT
576     resource, and answer the name of a possible LWFN Type 1 file.
577
578     Thanks to Paul Miller (paulm@profoundeffects.com) for the fix
579     to load a face OTHER than the first one in the FOND!
580  */
581
582
583  static void
584  parse_fond( char*   fond_data,
585              short*  have_sfnt,
586              short*  sfnt_id,
587              Str255  lwfn_file_name,
588              short   face_index )
589  {
590    AsscEntry*  assoc;
591    AsscEntry*  base_assoc;
592    FamRec*     fond;
593
594
595    *sfnt_id          = 0;
596    *have_sfnt        = 0;
597    lwfn_file_name[0] = 0;
598
599    fond       = (FamRec*)fond_data;
600    assoc      = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
601    base_assoc = assoc;
602
603    /* Let's do a little range checking before we get too excited here */
604    if ( face_index < count_faces_sfnt( fond_data ) )
605    {
606      assoc += face_index;        /* add on the face_index! */
607
608      /* if the face at this index is not scalable,
609         fall back to the first one (old behavior) */
610      if ( EndianS16_BtoN( assoc->fontSize ) == 0 )
611      {
612        *have_sfnt = 1;
613        *sfnt_id   = EndianS16_BtoN( assoc->fontID );
614      }
615      else if ( base_assoc->fontSize == 0 )
616      {
617        *have_sfnt = 1;
618        *sfnt_id   = EndianS16_BtoN( base_assoc->fontID );
619      }
620    }
621
622    if ( EndianS32_BtoN( fond->ffStylOff ) )
623    {
624      unsigned char*  p = (unsigned char*)fond_data;
625      StyleTable*     style;
626      unsigned short  string_count;
627      char            ps_name[256];
628      unsigned char*  names[64];
629      int             i;
630
631
632      p += EndianS32_BtoN( fond->ffStylOff );
633      style = (StyleTable*)p;
634      p += sizeof ( StyleTable );
635      string_count = EndianS16_BtoN( *(short*)(p) );
636      p += sizeof ( short );
637
638      for ( i = 0; i < string_count && i < 64; i++ )
639      {
640        names[i] = p;
641        p       += names[i][0];
642        p++;
643      }
644
645      {
646        size_t  ps_name_len = (size_t)names[0][0];
647
648
649        if ( ps_name_len != 0 )
650        {
651          ft_memcpy(ps_name, names[0] + 1, ps_name_len);
652          ps_name[ps_name_len] = 0;
653        }
654        if ( style->indexes[0] > 1 )
655        {
656          unsigned char*  suffixes = names[style->indexes[0] - 1];
657
658
659          for ( i = 1; i <= suffixes[0]; i++ )
660          {
661            unsigned char*  s;
662            size_t          j = suffixes[i] - 1;
663
664
665            if ( j < string_count && ( s = names[j] ) != NULL )
666            {
667              size_t  s_len = (size_t)s[0];
668
669
670              if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) )
671              {
672                ft_memcpy( ps_name + ps_name_len, s + 1, s_len );
673                ps_name_len += s_len;
674                ps_name[ps_name_len] = 0;
675              }
676            }
677          }
678        }
679      }
680
681      create_lwfn_name( ps_name, lwfn_file_name );
682    }
683  }
684
685
686  static  FT_Error
687  lookup_lwfn_by_fond( const UInt8*     path_fond,
688                       const StringPtr  base_lwfn,
689                       UInt8*           path_lwfn,
690                       int              path_size )
691  {
692
693#if HAVE_FSREF
694
695    FSRef  ref, par_ref;
696    int    dirname_len;
697
698
699    /* Pathname for FSRef can be in various formats: HFS, HFS+, and POSIX. */
700    /* We should not extract parent directory by string manipulation.      */
701
702    if ( noErr != FSPathMakeRef( path_fond, &ref, FALSE ) )
703      return FT_Err_Invalid_Argument;
704
705    if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
706                                    NULL, NULL, NULL, &par_ref ) )
707      return FT_Err_Invalid_Argument;
708
709    if ( noErr != FSRefMakePath( &par_ref, path_lwfn, path_size ) )
710      return FT_Err_Invalid_Argument;
711
712    if ( ft_strlen( (char *)path_lwfn ) + 1 + base_lwfn[0] > path_size )
713      return FT_Err_Invalid_Argument;
714
715    /* now we have absolute dirname in lookup_path */
716    if ( path_lwfn[0] == '/' )
717      ft_strcat( (char *)path_lwfn, "/" );
718    else
719      ft_strcat( (char *)path_lwfn, ":" );
720
721    dirname_len = ft_strlen( (char *)path_lwfn );
722    ft_strcat( (char *)path_lwfn, (char *)base_lwfn + 1 );
723    path_lwfn[dirname_len + base_lwfn[0]] = '\0';
724
725    if ( noErr != FSPathMakeRef( path_lwfn, &ref, FALSE ) )
726      return FT_Err_Cannot_Open_Resource;
727
728    if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
729                                    NULL, NULL, NULL, NULL ) )
730      return FT_Err_Cannot_Open_Resource;
731
732    return FT_Err_Ok;
733
734#else
735
736    int     i;
737    FSSpec  spec;
738
739
740    /* pathname for FSSpec is always HFS format */
741    if ( ft_strlen( (char *)path_fond ) > path_size )
742      return FT_Err_Invalid_Argument;
743
744    ft_strcpy( (char *)path_lwfn, (char *)path_fond );
745
746    i = ft_strlen( (char *)path_lwfn ) - 1;
747    while ( i > 0 && ':' != path_lwfn[i] )
748      i--;
749
750    if ( i + 1 + base_lwfn[0] > path_size )
751      return FT_Err_Invalid_Argument;
752
753    if ( ':' == path_lwfn[i] )
754    {
755      ft_strcpy( (char *)path_lwfn + i + 1, (char *)base_lwfn + 1 );
756      path_lwfn[i + 1 + base_lwfn[0]] = '\0';
757    }
758    else
759    {
760      ft_strcpy( (char *)path_lwfn, (char *)base_lwfn + 1 );
761      path_lwfn[base_lwfn[0]] = '\0';
762    }
763
764    if ( noErr != FT_FSPathMakeSpec( path_lwfn, &spec, FALSE ) )
765      return FT_Err_Cannot_Open_Resource;
766
767    return FT_Err_Ok;
768
769#endif /* HAVE_FSREF */
770
771  }
772
773
774  static short
775  count_faces( Handle        fond,
776               const UInt8*  pathname )
777  {
778    short     sfnt_id;
779    short     have_sfnt, have_lwfn;
780    Str255    lwfn_file_name;
781    UInt8     buff[HFS_MAXPATHLEN];
782    FT_Error  err;
783    short     num_faces;
784
785
786    have_sfnt = have_lwfn = 0;
787
788    HLock( fond );
789    parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 );
790
791    if ( lwfn_file_name[0] )
792    {
793      err = lookup_lwfn_by_fond( pathname, lwfn_file_name,
794                                 buff, sizeof ( buff )  );
795      if ( FT_Err_Ok == err )
796        have_lwfn = 1;
797    }
798
799    if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
800      num_faces = 1;
801    else
802      num_faces = count_faces_scalable( *fond );
803
804    HUnlock( fond );
805    return num_faces;
806  }
807
808
809  /* Read Type 1 data from the POST resources inside the LWFN file,
810     return a PFB buffer.  This is somewhat convoluted because the FT2
811     PFB parser wants the ASCII header as one chunk, and the LWFN
812     chunks are often not organized that way, so we glue chunks
813     of the same type together. */
814  static FT_Error
815  read_lwfn( FT_Memory  memory,
816             short      res,
817             FT_Byte**  pfb_data,
818             FT_ULong*  size )
819  {
820    FT_Error       error = FT_Err_Ok;
821    short          res_id;
822    unsigned char  *buffer, *p, *size_p = NULL;
823    FT_ULong       total_size = 0;
824    FT_ULong       old_total_size = 0;
825    FT_ULong       post_size, pfb_chunk_size;
826    Handle         post_data;
827    char           code, last_code;
828
829
830    UseResFile( res );
831
832    /* First pass: load all POST resources, and determine the size of */
833    /* the output buffer.                                             */
834    res_id    = 501;
835    last_code = -1;
836
837    for (;;)
838    {
839      post_data = Get1Resource( 'POST', res_id++ );
840      if ( post_data == NULL )
841        break;  /* we are done */
842
843      code = (*post_data)[0];
844
845      if ( code != last_code )
846      {
847        if ( code == 5 )
848          total_size += 2; /* just the end code */
849        else
850          total_size += 6; /* code + 4 bytes chunk length */
851      }
852
853      total_size += GetHandleSize( post_data ) - 2;
854      last_code = code;
855
856      /* detect integer overflows */
857      if ( total_size < old_total_size )
858      {
859        error = FT_Err_Array_Too_Large;
860        goto Error;
861      }
862
863      old_total_size = total_size;
864    }
865
866    if ( FT_ALLOC( buffer, (FT_Long)total_size ) )
867      goto Error;
868
869    /* Second pass: append all POST data to the buffer, add PFB fields. */
870    /* Glue all consecutive chunks of the same type together.           */
871    p              = buffer;
872    res_id         = 501;
873    last_code      = -1;
874    pfb_chunk_size = 0;
875
876    for (;;)
877    {
878      post_data = Get1Resource( 'POST', res_id++ );
879      if ( post_data == NULL )
880        break;  /* we are done */
881
882      post_size = (FT_ULong)GetHandleSize( post_data ) - 2;
883      code = (*post_data)[0];
884
885      if ( code != last_code )
886      {
887        if ( last_code != -1 )
888        {
889          /* we are done adding a chunk, fill in the size field */
890          if ( size_p != NULL )
891          {
892            *size_p++ = (FT_Byte)(   pfb_chunk_size         & 0xFF );
893            *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8  ) & 0xFF );
894            *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF );
895            *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF );
896          }
897          pfb_chunk_size = 0;
898        }
899
900        *p++ = 0x80;
901        if ( code == 5 )
902          *p++ = 0x03;  /* the end */
903        else if ( code == 2 )
904          *p++ = 0x02;  /* binary segment */
905        else
906          *p++ = 0x01;  /* ASCII segment */
907
908        if ( code != 5 )
909        {
910          size_p = p;   /* save for later */
911          p += 4;       /* make space for size field */
912        }
913      }
914
915      ft_memcpy( p, *post_data + 2, post_size );
916      pfb_chunk_size += post_size;
917      p += post_size;
918      last_code = code;
919    }
920
921    *pfb_data = buffer;
922    *size = total_size;
923
924  Error:
925    CloseResFile( res );
926    return error;
927  }
928
929
930  /* Finalizer for a memory stream; gets called by FT_Done_Face().
931     It frees the memory it uses. */
932  static void
933  memory_stream_close( FT_Stream  stream )
934  {
935    FT_Memory  memory = stream->memory;
936
937
938    FT_FREE( stream->base );
939
940    stream->size  = 0;
941    stream->base  = 0;
942    stream->close = 0;
943  }
944
945
946  /* Create a new memory stream from a buffer and a size. */
947  static FT_Error
948  new_memory_stream( FT_Library           library,
949                     FT_Byte*             base,
950                     FT_ULong             size,
951                     FT_Stream_CloseFunc  close,
952                     FT_Stream*           astream )
953  {
954    FT_Error   error;
955    FT_Memory  memory;
956    FT_Stream  stream;
957
958
959    if ( !library )
960      return FT_Err_Invalid_Library_Handle;
961
962    if ( !base )
963      return FT_Err_Invalid_Argument;
964
965    *astream = 0;
966    memory = library->memory;
967    if ( FT_NEW( stream ) )
968      goto Exit;
969
970    FT_Stream_OpenMemory( stream, base, size );
971
972    stream->close = close;
973
974    *astream = stream;
975
976  Exit:
977    return error;
978  }
979
980
981  /* Create a new FT_Face given a buffer and a driver name. */
982  static FT_Error
983  open_face_from_buffer( FT_Library  library,
984                         FT_Byte*    base,
985                         FT_ULong    size,
986                         FT_Long     face_index,
987                         char*       driver_name,
988                         FT_Face*    aface )
989  {
990    FT_Open_Args  args;
991    FT_Error      error;
992    FT_Stream     stream;
993    FT_Memory     memory = library->memory;
994
995
996    error = new_memory_stream( library,
997                               base,
998                               size,
999                               memory_stream_close,
1000                               &stream );
1001    if ( error )
1002    {
1003      FT_FREE( base );
1004      return error;
1005    }
1006
1007    args.flags  = FT_OPEN_STREAM;
1008    args.stream = stream;
1009    if ( driver_name )
1010    {
1011      args.flags  = args.flags | FT_OPEN_DRIVER;
1012      args.driver = FT_Get_Module( library, driver_name );
1013    }
1014
1015    /* At this point, face_index has served its purpose;      */
1016    /* whoever calls this function has already used it to     */
1017    /* locate the correct font data.  We should not propagate */
1018    /* this index to FT_Open_Face() (unless it is negative).  */
1019
1020    if ( face_index > 0 )
1021      face_index = 0;
1022
1023    error = FT_Open_Face( library, &args, face_index, aface );
1024    if ( error == FT_Err_Ok )
1025      (*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
1026    else
1027      FT_Stream_Free( stream, 0 );
1028
1029    return error;
1030  }
1031
1032
1033  /* Create a new FT_Face from a file spec to an LWFN file. */
1034  static FT_Error
1035  FT_New_Face_From_LWFN( FT_Library    library,
1036                         const UInt8*  pathname,
1037                         FT_Long       face_index,
1038                         FT_Face*      aface )
1039  {
1040    FT_Byte*  pfb_data;
1041    FT_ULong  pfb_size;
1042    FT_Error  error;
1043    short     res;
1044
1045
1046    if ( noErr != FT_FSPathMakeRes( pathname, &res ) )
1047      return FT_Err_Cannot_Open_Resource;
1048
1049    pfb_data = NULL;
1050    pfb_size = 0;
1051    error = read_lwfn( library->memory, res, &pfb_data, &pfb_size );
1052    CloseResFile( res ); /* PFB is already loaded, useless anymore */
1053    if ( error )
1054      return error;
1055
1056    return open_face_from_buffer( library,
1057                                  pfb_data,
1058                                  pfb_size,
1059                                  face_index,
1060                                  "type1",
1061                                  aface );
1062  }
1063
1064
1065  /* Create a new FT_Face from an SFNT resource, specified by res ID. */
1066  static FT_Error
1067  FT_New_Face_From_SFNT( FT_Library  library,
1068                         short       sfnt_id,
1069                         FT_Long     face_index,
1070                         FT_Face*    aface )
1071  {
1072    Handle     sfnt = NULL;
1073    FT_Byte*   sfnt_data;
1074    size_t     sfnt_size;
1075    FT_Error   error  = FT_Err_Ok;
1076    FT_Memory  memory = library->memory;
1077    int        is_cff;
1078
1079
1080    sfnt = GetResource( 'sfnt', sfnt_id );
1081    if ( ResError() )
1082      return FT_Err_Invalid_Handle;
1083
1084    sfnt_size = (FT_ULong)GetHandleSize( sfnt );
1085    if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) )
1086    {
1087      ReleaseResource( sfnt );
1088      return error;
1089    }
1090
1091    HLock( sfnt );
1092    ft_memcpy( sfnt_data, *sfnt, sfnt_size );
1093    HUnlock( sfnt );
1094    ReleaseResource( sfnt );
1095
1096    is_cff = sfnt_size > 4 && sfnt_data[0] == 'O' &&
1097                              sfnt_data[1] == 'T' &&
1098                              sfnt_data[2] == 'T' &&
1099                              sfnt_data[3] == 'O';
1100
1101    return open_face_from_buffer( library,
1102                                  sfnt_data,
1103                                  sfnt_size,
1104                                  face_index,
1105                                  is_cff ? "cff" : "truetype",
1106                                  aface );
1107  }
1108
1109
1110  /* Create a new FT_Face from a file spec to a suitcase file. */
1111  static FT_Error
1112  FT_New_Face_From_Suitcase( FT_Library    library,
1113                             const UInt8*  pathname,
1114                             FT_Long       face_index,
1115                             FT_Face*      aface )
1116  {
1117    FT_Error  error = FT_Err_Cannot_Open_Resource;
1118    short     res_ref, res_index;
1119    Handle    fond;
1120    short     num_faces_in_res, num_faces_in_fond;
1121
1122
1123    if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) )
1124      return FT_Err_Cannot_Open_Resource;
1125
1126    UseResFile( res_ref );
1127    if ( ResError() )
1128      return FT_Err_Cannot_Open_Resource;
1129
1130    num_faces_in_res = 0;
1131    for ( res_index = 1; ; ++res_index )
1132    {
1133      fond = Get1IndResource( 'FOND', res_index );
1134      if ( ResError() )
1135        break;
1136
1137      num_faces_in_fond  = count_faces( fond, pathname );
1138      num_faces_in_res  += num_faces_in_fond;
1139
1140      if ( 0 <= face_index && face_index < num_faces_in_fond && error )
1141        error = FT_New_Face_From_FOND( library, fond, face_index, aface );
1142
1143      face_index -= num_faces_in_fond;
1144    }
1145
1146    CloseResFile( res_ref );
1147    if ( FT_Err_Ok == error && NULL != aface )
1148      (*aface)->num_faces = num_faces_in_res;
1149    return error;
1150  }
1151
1152
1153  /* documentation is in ftmac.h */
1154
1155  FT_EXPORT_DEF( FT_Error )
1156  FT_New_Face_From_FOND( FT_Library  library,
1157                         Handle      fond,
1158                         FT_Long     face_index,
1159                         FT_Face*    aface )
1160  {
1161    short     sfnt_id, have_sfnt, have_lwfn = 0;
1162    short     fond_id;
1163    OSType    fond_type;
1164    Str255    fond_name;
1165    Str255    lwfn_file_name;
1166    UInt8     path_lwfn[HFS_MAXPATHLEN];
1167    OSErr     err;
1168    FT_Error  error = FT_Err_Ok;
1169
1170
1171    GetResInfo( fond, &fond_id, &fond_type, fond_name );
1172    if ( ResError() != noErr || fond_type != 'FOND' )
1173      return FT_Err_Invalid_File_Format;
1174
1175    HLock( fond );
1176    parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index );
1177    HUnlock( fond );
1178
1179    if ( lwfn_file_name[0] )
1180    {
1181      short  res;
1182
1183
1184      res = HomeResFile( fond );
1185      if ( noErr != ResError() )
1186        goto found_no_lwfn_file;
1187
1188#if HAVE_FSREF
1189
1190      {
1191        UInt8  path_fond[HFS_MAXPATHLEN];
1192        FSRef  ref;
1193
1194
1195        err = FSGetForkCBInfo( res, kFSInvalidVolumeRefNum,
1196                               NULL, NULL, NULL, &ref, NULL );
1197        if ( noErr != err )
1198          goto found_no_lwfn_file;
1199
1200        err = FSRefMakePath( &ref, path_fond, sizeof ( path_fond ) );
1201        if ( noErr != err )
1202          goto found_no_lwfn_file;
1203
1204        error = lookup_lwfn_by_fond( path_fond, lwfn_file_name,
1205                                     path_lwfn, sizeof ( path_lwfn ) );
1206        if ( FT_Err_Ok == error )
1207          have_lwfn = 1;
1208      }
1209
1210#elif HAVE_FSSPEC
1211
1212      {
1213        UInt8     path_fond[HFS_MAXPATHLEN];
1214        FCBPBRec  pb;
1215        Str255    fond_file_name;
1216        FSSpec    spec;
1217
1218
1219        FT_MEM_SET( &spec, 0, sizeof ( FSSpec ) );
1220        FT_MEM_SET( &pb,   0, sizeof ( FCBPBRec ) );
1221
1222        pb.ioNamePtr = fond_file_name;
1223        pb.ioVRefNum = 0;
1224        pb.ioRefNum  = res;
1225        pb.ioFCBIndx = 0;
1226
1227        err = PBGetFCBInfoSync( &pb );
1228        if ( noErr != err )
1229          goto found_no_lwfn_file;
1230
1231        err = FSMakeFSSpec( pb.ioFCBVRefNum, pb.ioFCBParID,
1232                            fond_file_name, &spec );
1233        if ( noErr != err )
1234          goto found_no_lwfn_file;
1235
1236        err = FT_FSpMakePath( &spec, path_fond, sizeof ( path_fond ) );
1237        if ( noErr != err )
1238          goto found_no_lwfn_file;
1239
1240        error = lookup_lwfn_by_fond( path_fond, lwfn_file_name,
1241                                     path_lwfn, sizeof ( path_lwfn ) );
1242        if ( FT_Err_Ok == error )
1243          have_lwfn = 1;
1244      }
1245
1246#endif /* HAVE_FSREF, HAVE_FSSPEC */
1247
1248    }
1249
1250    if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
1251      error = FT_New_Face_From_LWFN( library,
1252                                     path_lwfn,
1253                                     face_index,
1254                                     aface );
1255    else
1256      error = FT_Err_Unknown_File_Format;
1257
1258  found_no_lwfn_file:
1259    if ( have_sfnt && FT_Err_Ok != error )
1260      error = FT_New_Face_From_SFNT( library,
1261                                     sfnt_id,
1262                                     face_index,
1263                                     aface );
1264
1265    return error;
1266  }
1267
1268
1269  /* Common function to load a new FT_Face from a resource file. */
1270  static FT_Error
1271  FT_New_Face_From_Resource( FT_Library    library,
1272                             const UInt8*  pathname,
1273                             FT_Long       face_index,
1274                             FT_Face*      aface )
1275  {
1276    OSType    file_type;
1277    FT_Error  error;
1278
1279
1280    /* LWFN is a (very) specific file format, check for it explicitly */
1281    file_type = get_file_type_from_path( pathname );
1282    if ( file_type == 'LWFN' )
1283      return FT_New_Face_From_LWFN( library, pathname, face_index, aface );
1284
1285    /* Otherwise the file type doesn't matter (there are more than  */
1286    /* `FFIL' and `tfil').  Just try opening it as a font suitcase; */
1287    /* if it works, fine.                                           */
1288
1289    error = FT_New_Face_From_Suitcase( library, pathname, face_index, aface );
1290    if ( error == 0 )
1291      return error;
1292
1293    /* let it fall through to normal loader (.ttf, .otf, etc.); */
1294    /* we signal this by returning no error and no FT_Face      */
1295    *aface = NULL;
1296    return 0;
1297  }
1298
1299
1300  /*************************************************************************/
1301  /*                                                                       */
1302  /* <Function>                                                            */
1303  /*    FT_New_Face                                                        */
1304  /*                                                                       */
1305  /* <Description>                                                         */
1306  /*    This is the Mac-specific implementation of FT_New_Face.  In        */
1307  /*    addition to the standard FT_New_Face() functionality, it also      */
1308  /*    accepts pathnames to Mac suitcase files.  For further              */
1309  /*    documentation see the original FT_New_Face() in freetype.h.        */
1310  /*                                                                       */
1311  FT_EXPORT_DEF( FT_Error )
1312  FT_New_Face( FT_Library   library,
1313               const char*  pathname,
1314               FT_Long      face_index,
1315               FT_Face*     aface )
1316  {
1317    FT_Open_Args  args;
1318    FT_Error      error;
1319
1320
1321    /* test for valid `library' and `aface' delayed to FT_Open_Face() */
1322    if ( !pathname )
1323      return FT_Err_Invalid_Argument;
1324
1325    error  = FT_Err_Ok;
1326    *aface = NULL;
1327
1328    /* try resourcefork based font: LWFN, FFIL */
1329    error = FT_New_Face_From_Resource( library, (UInt8 *)pathname,
1330                                       face_index, aface );
1331    if ( error != 0 || *aface != NULL )
1332      return error;
1333
1334    /* let it fall through to normal loader (.ttf, .otf, etc.) */
1335    args.flags    = FT_OPEN_PATHNAME;
1336    args.pathname = (char*)pathname;
1337    return FT_Open_Face( library, &args, face_index, aface );
1338  }
1339
1340
1341  /*************************************************************************/
1342  /*                                                                       */
1343  /* <Function>                                                            */
1344  /*    FT_New_Face_From_FSRef                                             */
1345  /*                                                                       */
1346  /* <Description>                                                         */
1347  /*    FT_New_Face_From_FSRef is identical to FT_New_Face except it       */
1348  /*    accepts an FSRef instead of a path.                                */
1349  /*                                                                       */
1350  FT_EXPORT_DEF( FT_Error )
1351  FT_New_Face_From_FSRef( FT_Library    library,
1352                          const FSRef*  ref,
1353                          FT_Long       face_index,
1354                          FT_Face*      aface )
1355  {
1356
1357#if !HAVE_FSREF
1358
1359    return FT_Err_Unimplemented_Feature;
1360
1361#else
1362
1363    FT_Error      error;
1364    FT_Open_Args  args;
1365    OSErr   err;
1366    UInt8   pathname[HFS_MAXPATHLEN];
1367
1368
1369    if ( !ref )
1370      return FT_Err_Invalid_Argument;
1371
1372    err = FSRefMakePath( ref, pathname, sizeof ( pathname ) );
1373    if ( err )
1374      error = FT_Err_Cannot_Open_Resource;
1375
1376    error = FT_New_Face_From_Resource( library, pathname, face_index, aface );
1377    if ( error != 0 || *aface != NULL )
1378      return error;
1379
1380    /* fallback to datafork font */
1381    args.flags    = FT_OPEN_PATHNAME;
1382    args.pathname = (char*)pathname;
1383    return FT_Open_Face( library, &args, face_index, aface );
1384
1385#endif /* HAVE_FSREF */
1386
1387  }
1388
1389
1390  /*************************************************************************/
1391  /*                                                                       */
1392  /* <Function>                                                            */
1393  /*    FT_New_Face_From_FSSpec                                            */
1394  /*                                                                       */
1395  /* <Description>                                                         */
1396  /*    FT_New_Face_From_FSSpec is identical to FT_New_Face except it      */
1397  /*    accepts an FSSpec instead of a path.                               */
1398  /*                                                                       */
1399  FT_EXPORT_DEF( FT_Error )
1400  FT_New_Face_From_FSSpec( FT_Library     library,
1401                           const FSSpec*  spec,
1402                           FT_Long        face_index,
1403                           FT_Face*       aface )
1404  {
1405
1406#if HAVE_FSREF
1407
1408    FSRef  ref;
1409
1410
1411    if ( !spec || FSpMakeFSRef( spec, &ref ) != noErr )
1412      return FT_Err_Invalid_Argument;
1413    else
1414      return FT_New_Face_From_FSRef( library, &ref, face_index, aface );
1415
1416#elif HAVE_FSSPEC
1417
1418    FT_Error      error;
1419    FT_Open_Args  args;
1420    OSErr         err;
1421    UInt8         pathname[HFS_MAXPATHLEN];
1422
1423
1424    if ( !spec )
1425      return FT_Err_Invalid_Argument;
1426
1427    err = FT_FSpMakePath( spec, pathname, sizeof ( pathname ) );
1428    if ( err )
1429      error = FT_Err_Cannot_Open_Resource;
1430
1431    error = FT_New_Face_From_Resource( library, pathname, face_index, aface );
1432    if ( error != 0 || *aface != NULL )
1433      return error;
1434
1435    /* fallback to datafork font */
1436    args.flags    = FT_OPEN_PATHNAME;
1437    args.pathname = (char*)pathname;
1438    return FT_Open_Face( library, &args, face_index, aface );
1439
1440#else
1441
1442    return FT_Err_Unimplemented_Feature;
1443
1444#endif /* HAVE_FSREF, HAVE_FSSPEC */
1445
1446  }
1447
1448
1449/* END */
Note: See TracBrowser for help on using the repository browser.