Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / modules / freetype2 / builds / mac / ftmac.c
blob6e91a8f29228f0cc17cff35c4b2308e1644e64a6
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, 2007, 2008 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 /***************************************************************************/
21 Notes
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.
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.
33 The Mac FOND support works roughly like this:
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.
41 - Read the FOND resource into memory, then check whether there is a
42 TrueType font and/or(!) a Type 1 font available.
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...)
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().
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.
65 #include <ft2build.h>
66 #include FT_FREETYPE_H
67 #include FT_INTERNAL_STREAM_H
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 #if !HAVE_ANSI_OS_INLINE
74 #undef OS_INLINE
75 #define OS_INLINE static __inline__
76 #endif
77 #include <CoreServices/CoreServices.h>
78 #include <ApplicationServices/ApplicationServices.h>
79 #include <sys/syslimits.h> /* PATH_MAX */
80 #else
81 #include <Resources.h>
82 #include <Fonts.h>
83 #include <Endian.h>
84 #include <Errors.h>
85 #include <Files.h>
86 #include <TextUtils.h>
87 #endif
89 #ifndef PATH_MAX
90 #define PATH_MAX 1024 /* same with Mac OS X's syslimits.h */
91 #endif
93 #if defined( __MWERKS__ ) && !TARGET_RT_MAC_MACHO
94 #include <FSp_fopen.h>
95 #endif
97 #define FT_DEPRECATED_ATTRIBUTE
99 #include FT_MAC_H
101 /* undefine blocking-macros in ftmac.h */
102 #undef FT_GetFile_From_Mac_Name
103 #undef FT_GetFile_From_Mac_ATS_Name
104 #undef FT_New_Face_From_FOND
105 #undef FT_New_Face_From_FSSpec
106 #undef FT_New_Face_From_FSRef
109 /* FSSpec functions are deprecated since Mac OS X 10.4 */
110 #ifndef HAVE_FSSPEC
111 #if TARGET_API_MAC_OS8 || TARGET_API_MAC_CARBON
112 #define HAVE_FSSPEC 1
113 #else
114 #define HAVE_FSSPEC 0
115 #endif
116 #endif
118 /* most FSRef functions were introduced since Mac OS 9 */
119 #ifndef HAVE_FSREF
120 #if TARGET_API_MAC_OSX
121 #define HAVE_FSREF 1
122 #else
123 #define HAVE_FSREF 0
124 #endif
125 #endif
127 /* QuickDraw is deprecated since Mac OS X 10.4 */
128 #ifndef HAVE_QUICKDRAW_CARBON
129 #if TARGET_API_MAC_OS8 || TARGET_API_MAC_CARBON
130 #define HAVE_QUICKDRAW_CARBON 1
131 #else
132 #define HAVE_QUICKDRAW_CARBON 0
133 #endif
134 #endif
136 /* AppleTypeService is available since Mac OS X */
137 #ifndef HAVE_ATS
138 #if TARGET_API_MAC_OSX
139 #define HAVE_ATS 1
140 #ifndef kATSOptionFlagsUnRestrictedScope /* since Mac OS X 10.1 */
141 #define kATSOptionFlagsUnRestrictedScope kATSOptionFlagsDefault
142 #endif
143 #else
144 #define HAVE_ATS 0
145 #endif
146 #endif
148 /* Some portable types are unavailable on legacy SDKs */
149 #ifndef MAC_OS_X_VERSION_10_5
150 typedef short ResourceIndex;
151 #endif
153 /* Set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over
154 TrueType in case *both* are available (this is not common,
155 but it *is* possible). */
156 #ifndef PREFER_LWFN
157 #define PREFER_LWFN 1
158 #endif
161 #if !HAVE_QUICKDRAW_CARBON /* QuickDraw is deprecated since Mac OS X 10.4 */
163 FT_EXPORT_DEF( FT_Error )
164 FT_GetFile_From_Mac_Name( const char* fontName,
165 FSSpec* pathSpec,
166 FT_Long* face_index )
168 FT_UNUSED( fontName );
169 FT_UNUSED( pathSpec );
170 FT_UNUSED( face_index );
172 return FT_Err_Unimplemented_Feature;
175 #else
177 FT_EXPORT_DEF( FT_Error )
178 FT_GetFile_From_Mac_Name( const char* fontName,
179 FSSpec* pathSpec,
180 FT_Long* face_index )
182 OptionBits options = kFMUseGlobalScopeOption;
184 FMFontFamilyIterator famIter;
185 OSStatus status = FMCreateFontFamilyIterator( NULL, NULL,
186 options,
187 &famIter );
188 FMFont the_font = 0;
189 FMFontFamily family = 0;
192 *face_index = 0;
193 while ( status == 0 && !the_font )
195 status = FMGetNextFontFamily( &famIter, &family );
196 if ( status == 0 )
198 int stat2;
199 FMFontFamilyInstanceIterator instIter;
200 Str255 famNameStr;
201 char famName[256];
204 /* get the family name */
205 FMGetFontFamilyName( family, famNameStr );
206 CopyPascalStringToC( famNameStr, famName );
208 /* iterate through the styles */
209 FMCreateFontFamilyInstanceIterator( family, &instIter );
211 *face_index = 0;
212 stat2 = 0;
214 while ( stat2 == 0 && !the_font )
216 FMFontStyle style;
217 FMFontSize size;
218 FMFont font;
221 stat2 = FMGetNextFontFamilyInstance( &instIter, &font,
222 &style, &size );
223 if ( stat2 == 0 && size == 0 )
225 char fullName[256];
228 /* build up a complete face name */
229 ft_strcpy( fullName, famName );
230 if ( style & bold )
231 ft_strcat( fullName, " Bold" );
232 if ( style & italic )
233 ft_strcat( fullName, " Italic" );
235 /* compare with the name we are looking for */
236 if ( ft_strcmp( fullName, fontName ) == 0 )
238 /* found it! */
239 the_font = font;
241 else
242 ++(*face_index);
246 FMDisposeFontFamilyInstanceIterator( &instIter );
250 FMDisposeFontFamilyIterator( &famIter );
252 if ( the_font )
254 FMGetFontContainer( the_font, pathSpec );
255 return FT_Err_Ok;
257 else
258 return FT_Err_Unknown_File_Format;
261 #endif /* HAVE_QUICKDRAW_CARBON */
264 #if HAVE_ATS
266 /* Private function. */
267 /* The FSSpec type has been discouraged for a long time, */
268 /* unfortunately an FSRef replacement API for */
269 /* ATSFontGetFileSpecification() is only available in */
270 /* Mac OS X 10.5 and later. */
271 static OSStatus
272 FT_ATSFontGetFileReference( ATSFontRef ats_font_id,
273 FSRef* ats_font_ref )
275 OSStatus err;
277 #if !defined( MAC_OS_X_VERSION_10_5 ) || \
278 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
279 FSSpec spec;
282 err = ATSFontGetFileSpecification( ats_font_id, &spec );
283 if ( noErr == err )
284 err = FSpMakeFSRef( &spec, ats_font_ref );
285 #else
286 err = ATSFontGetFileReference( ats_font_id, ats_font_ref );
287 #endif
289 return err;
293 static FT_Error
294 FT_GetFileRef_From_Mac_ATS_Name( const char* fontName,
295 FSRef* ats_font_ref,
296 FT_Long* face_index )
298 CFStringRef cf_fontName;
299 ATSFontRef ats_font_id;
302 *face_index = 0;
304 cf_fontName = CFStringCreateWithCString( NULL, fontName,
305 kCFStringEncodingMacRoman );
306 ats_font_id = ATSFontFindFromName( cf_fontName,
307 kATSOptionFlagsUnRestrictedScope );
308 CFRelease( cf_fontName );
310 if ( ats_font_id == 0 || ats_font_id == 0xFFFFFFFFUL )
311 return FT_Err_Unknown_File_Format;
313 if ( noErr != FT_ATSFontGetFileReference( ats_font_id, ats_font_ref ) )
314 return FT_Err_Unknown_File_Format;
316 /* face_index calculation by searching preceding fontIDs */
317 /* with same FSRef */
319 ATSFontRef id2 = ats_font_id - 1;
320 FSRef ref2;
323 while ( id2 > 0 )
325 if ( noErr != FT_ATSFontGetFileReference( id2, &ref2 ) )
326 break;
327 if ( noErr != FSCompareFSRefs( ats_font_ref, &ref2 ) )
328 break;
330 id2--;
332 *face_index = ats_font_id - ( id2 + 1 );
335 return FT_Err_Ok;
338 #endif
340 #if !HAVE_ATS
342 FT_EXPORT_DEF( FT_Error )
343 FT_GetFilePath_From_Mac_ATS_Name( const char* fontName,
344 UInt8* path,
345 UInt32 maxPathSize,
346 FT_Long* face_index )
348 FT_UNUSED( fontName );
349 FT_UNUSED( path );
350 FT_UNUSED( maxPathSize );
351 FT_UNUSED( face_index );
353 return FT_Err_Unimplemented_Feature;
356 #else
358 FT_EXPORT_DEF( FT_Error )
359 FT_GetFilePath_From_Mac_ATS_Name( const char* fontName,
360 UInt8* path,
361 UInt32 maxPathSize,
362 FT_Long* face_index )
364 FSRef ref;
365 FT_Error err;
368 err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index );
369 if ( FT_Err_Ok != err )
370 return err;
372 if ( noErr != FSRefMakePath( &ref, path, maxPathSize ) )
373 return FT_Err_Unknown_File_Format;
375 return FT_Err_Ok;
378 #endif /* HAVE_ATS */
381 #if !HAVE_FSSPEC || !HAVE_ATS
383 FT_EXPORT_DEF( FT_Error )
384 FT_GetFile_From_Mac_ATS_Name( const char* fontName,
385 FSSpec* pathSpec,
386 FT_Long* face_index )
388 FT_UNUSED( fontName );
389 FT_UNUSED( pathSpec );
390 FT_UNUSED( face_index );
392 return FT_Err_Unimplemented_Feature;
395 #else
397 /* This function is deprecated because FSSpec is deprecated in Mac OS X. */
398 FT_EXPORT_DEF( FT_Error )
399 FT_GetFile_From_Mac_ATS_Name( const char* fontName,
400 FSSpec* pathSpec,
401 FT_Long* face_index )
403 FSRef ref;
404 FT_Error err;
407 err = FT_GetFileRef_From_Mac_ATS_Name( fontName, &ref, face_index );
408 if ( FT_Err_Ok != err )
409 return err;
411 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone, NULL, NULL,
412 pathSpec, NULL ) )
413 return FT_Err_Unknown_File_Format;
415 return FT_Err_Ok;
418 #endif
421 #if defined( __MWERKS__ ) && !TARGET_RT_MAC_MACHO
423 #define STREAM_FILE( stream ) ( (FT_FILE*)stream->descriptor.pointer )
426 FT_CALLBACK_DEF( void )
427 ft_FSp_stream_close( FT_Stream stream )
429 ft_fclose( STREAM_FILE( stream ) );
431 stream->descriptor.pointer = NULL;
432 stream->size = 0;
433 stream->base = 0;
437 FT_CALLBACK_DEF( unsigned long )
438 ft_FSp_stream_io( FT_Stream stream,
439 unsigned long offset,
440 unsigned char* buffer,
441 unsigned long count )
443 FT_FILE* file;
446 file = STREAM_FILE( stream );
448 ft_fseek( file, offset, SEEK_SET );
450 return (unsigned long)ft_fread( buffer, 1, count, file );
453 #endif /* __MWERKS__ && !TARGET_RT_MAC_MACHO */
456 #if HAVE_FSSPEC && !HAVE_FSREF
458 /* isDirectory is a dummy to synchronize API with FSPathMakeRef() */
459 static OSErr
460 FT_FSPathMakeSpec( const UInt8* pathname,
461 FSSpec* spec_p,
462 Boolean isDirectory )
464 const char *p, *q;
465 short vRefNum;
466 long dirID;
467 Str255 nodeName;
468 OSErr err;
469 FT_UNUSED( isDirectory );
472 p = q = (const char *)pathname;
473 dirID = 0;
474 vRefNum = 0;
476 while ( 1 )
478 int len = ft_strlen( p );
481 if ( len > 255 )
482 len = 255;
484 q = p + len;
486 if ( q == p )
487 return 0;
489 if ( 255 < ft_strlen( (char *)pathname ) )
491 while ( p < q && *q != ':' )
492 q--;
495 if ( p < q )
496 *(char *)nodeName = q - p;
497 else if ( ft_strlen( p ) < 256 )
498 *(char *)nodeName = ft_strlen( p );
499 else
500 return errFSNameTooLong;
502 ft_strncpy( (char *)nodeName + 1, (char *)p, *(char *)nodeName );
503 err = FSMakeFSSpec( vRefNum, dirID, nodeName, spec_p );
504 if ( err || '\0' == *q )
505 return err;
507 vRefNum = spec_p->vRefNum;
508 dirID = spec_p->parID;
510 p = q;
515 static OSErr
516 FT_FSpMakePath( const FSSpec* spec_p,
517 UInt8* path,
518 UInt32 maxPathSize )
520 OSErr err;
521 FSSpec spec = *spec_p;
522 short vRefNum;
523 long dirID;
524 Str255 parDir_name;
527 FT_MEM_SET( path, 0, maxPathSize );
528 while ( 1 )
530 int child_namelen = ft_strlen( (char *)path );
531 unsigned char node_namelen = spec.name[0];
532 unsigned char* node_name = spec.name + 1;
535 if ( node_namelen + child_namelen > maxPathSize )
536 return errFSNameTooLong;
538 FT_MEM_MOVE( path + node_namelen + 1, path, child_namelen );
539 FT_MEM_COPY( path, node_name, node_namelen );
540 if ( child_namelen > 0 )
541 path[node_namelen] = ':';
543 vRefNum = spec.vRefNum;
544 dirID = spec.parID;
545 parDir_name[0] = '\0';
546 err = FSMakeFSSpec( vRefNum, dirID, parDir_name, &spec );
547 if ( noErr != err || dirID == spec.parID )
548 break;
550 return noErr;
553 #endif /* HAVE_FSSPEC && !HAVE_FSREF */
556 static OSErr
557 FT_FSPathMakeRes( const UInt8* pathname,
558 ResFileRefNum* res )
561 #if HAVE_FSREF
563 OSErr err;
564 FSRef ref;
567 if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
568 return FT_Err_Cannot_Open_Resource;
570 /* at present, no support for dfont format */
571 err = FSOpenResourceFile( &ref, 0, NULL, fsRdPerm, res );
572 if ( noErr == err )
573 return err;
575 /* fallback to original resource-fork font */
576 *res = FSOpenResFile( &ref, fsRdPerm );
577 err = ResError();
579 #else
581 OSErr err;
582 FSSpec spec;
585 if ( noErr != FT_FSPathMakeSpec( pathname, &spec, FALSE ) )
586 return FT_Err_Cannot_Open_Resource;
588 /* at present, no support for dfont format without FSRef */
589 /* (see above), try original resource-fork font */
590 *res = FSpOpenResFile( &spec, fsRdPerm );
591 err = ResError();
593 #endif /* HAVE_FSREF */
595 return err;
599 /* Return the file type for given pathname */
600 static OSType
601 get_file_type_from_path( const UInt8* pathname )
604 #if HAVE_FSREF
606 FSRef ref;
607 FSCatalogInfo info;
610 if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) )
611 return ( OSType ) 0;
613 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoFinderInfo, &info,
614 NULL, NULL, NULL ) )
615 return ( OSType ) 0;
617 return ((FInfo *)(info.finderInfo))->fdType;
619 #else
621 FSSpec spec;
622 FInfo finfo;
625 if ( noErr != FT_FSPathMakeSpec( pathname, &spec, FALSE ) )
626 return ( OSType ) 0;
628 if ( noErr != FSpGetFInfo( &spec, &finfo ) )
629 return ( OSType ) 0;
631 return finfo.fdType;
633 #endif /* HAVE_FSREF */
638 /* Given a PostScript font name, create the Macintosh LWFN file name. */
639 static void
640 create_lwfn_name( char* ps_name,
641 Str255 lwfn_file_name )
643 int max = 5, count = 0;
644 FT_Byte* p = lwfn_file_name;
645 FT_Byte* q = (FT_Byte*)ps_name;
648 lwfn_file_name[0] = 0;
650 while ( *q )
652 if ( ft_isupper( *q ) )
654 if ( count )
655 max = 3;
656 count = 0;
658 if ( count < max && ( ft_isalnum( *q ) || *q == '_' ) )
660 *++p = *q;
661 lwfn_file_name[0]++;
662 count++;
664 q++;
669 static short
670 count_faces_sfnt( char* fond_data )
672 /* The count is 1 greater than the value in the FOND. */
673 /* Isn't that cute? :-) */
675 return EndianS16_BtoN( *( (short*)( fond_data +
676 sizeof ( FamRec ) ) ) ) + 1;
680 static short
681 count_faces_scalable( char* fond_data )
683 AsscEntry* assoc;
684 FamRec* fond;
685 short i, face, face_all;
688 fond = (FamRec*)fond_data;
689 face_all = EndianS16_BtoN( *( (short *)( fond_data +
690 sizeof ( FamRec ) ) ) ) + 1;
691 assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
692 face = 0;
694 for ( i = 0; i < face_all; i++ )
696 if ( 0 == EndianS16_BtoN( assoc[i].fontSize ) )
697 face++;
699 return face;
703 /* Look inside the FOND data, answer whether there should be an SFNT
704 resource, and answer the name of a possible LWFN Type 1 file.
706 Thanks to Paul Miller (paulm@profoundeffects.com) for the fix
707 to load a face OTHER than the first one in the FOND!
710 static void
711 parse_fond( char* fond_data,
712 short* have_sfnt,
713 ResID* sfnt_id,
714 Str255 lwfn_file_name,
715 short face_index )
717 AsscEntry* assoc;
718 AsscEntry* base_assoc;
719 FamRec* fond;
722 *sfnt_id = 0;
723 *have_sfnt = 0;
724 lwfn_file_name[0] = 0;
726 fond = (FamRec*)fond_data;
727 assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 );
728 base_assoc = assoc;
730 /* the maximum faces in a FOND is 48, size of StyleTable.indexes[] */
731 if ( 47 < face_index )
732 return;
734 /* Let's do a little range checking before we get too excited here */
735 if ( face_index < count_faces_sfnt( fond_data ) )
737 assoc += face_index; /* add on the face_index! */
739 /* if the face at this index is not scalable,
740 fall back to the first one (old behavior) */
741 if ( EndianS16_BtoN( assoc->fontSize ) == 0 )
743 *have_sfnt = 1;
744 *sfnt_id = EndianS16_BtoN( assoc->fontID );
746 else if ( base_assoc->fontSize == 0 )
748 *have_sfnt = 1;
749 *sfnt_id = EndianS16_BtoN( base_assoc->fontID );
753 if ( EndianS32_BtoN( fond->ffStylOff ) )
755 unsigned char* p = (unsigned char*)fond_data;
756 StyleTable* style;
757 unsigned short string_count;
758 char ps_name[256];
759 unsigned char* names[64];
760 int i;
763 p += EndianS32_BtoN( fond->ffStylOff );
764 style = (StyleTable*)p;
765 p += sizeof ( StyleTable );
766 string_count = EndianS16_BtoN( *(short*)(p) );
767 p += sizeof ( short );
769 for ( i = 0; i < string_count && i < 64; i++ )
771 names[i] = p;
772 p += names[i][0];
773 p++;
777 size_t ps_name_len = (size_t)names[0][0];
780 if ( ps_name_len != 0 )
782 ft_memcpy(ps_name, names[0] + 1, ps_name_len);
783 ps_name[ps_name_len] = 0;
785 if ( style->indexes[face_index] > 1 &&
786 style->indexes[face_index] <= FT_MIN( string_count, 64 ) )
788 unsigned char* suffixes = names[style->indexes[face_index] - 1];
791 for ( i = 1; i <= suffixes[0]; i++ )
793 unsigned char* s;
794 size_t j = suffixes[i] - 1;
797 if ( j < string_count && ( s = names[j] ) != NULL )
799 size_t s_len = (size_t)s[0];
802 if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) )
804 ft_memcpy( ps_name + ps_name_len, s + 1, s_len );
805 ps_name_len += s_len;
806 ps_name[ps_name_len] = 0;
813 create_lwfn_name( ps_name, lwfn_file_name );
818 static FT_Error
819 lookup_lwfn_by_fond( const UInt8* path_fond,
820 ConstStr255Param base_lwfn,
821 UInt8* path_lwfn,
822 int path_size )
825 #if HAVE_FSREF
827 FSRef ref, par_ref;
828 int dirname_len;
831 /* Pathname for FSRef can be in various formats: HFS, HFS+, and POSIX. */
832 /* We should not extract parent directory by string manipulation. */
834 if ( noErr != FSPathMakeRef( path_fond, &ref, FALSE ) )
835 return FT_Err_Invalid_Argument;
837 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
838 NULL, NULL, NULL, &par_ref ) )
839 return FT_Err_Invalid_Argument;
841 if ( noErr != FSRefMakePath( &par_ref, path_lwfn, path_size ) )
842 return FT_Err_Invalid_Argument;
844 if ( ft_strlen( (char *)path_lwfn ) + 1 + base_lwfn[0] > path_size )
845 return FT_Err_Invalid_Argument;
847 /* now we have absolute dirname in path_lwfn */
848 if ( path_lwfn[0] == '/' )
849 ft_strcat( (char *)path_lwfn, "/" );
850 else
851 ft_strcat( (char *)path_lwfn, ":" );
853 dirname_len = ft_strlen( (char *)path_lwfn );
854 ft_strcat( (char *)path_lwfn, (char *)base_lwfn + 1 );
855 path_lwfn[dirname_len + base_lwfn[0]] = '\0';
857 if ( noErr != FSPathMakeRef( path_lwfn, &ref, FALSE ) )
858 return FT_Err_Cannot_Open_Resource;
860 if ( noErr != FSGetCatalogInfo( &ref, kFSCatInfoNone,
861 NULL, NULL, NULL, NULL ) )
862 return FT_Err_Cannot_Open_Resource;
864 return FT_Err_Ok;
866 #else
868 int i;
869 FSSpec spec;
872 /* pathname for FSSpec is always HFS format */
873 if ( ft_strlen( (char *)path_fond ) > path_size )
874 return FT_Err_Invalid_Argument;
876 ft_strcpy( (char *)path_lwfn, (char *)path_fond );
878 i = ft_strlen( (char *)path_lwfn ) - 1;
879 while ( i > 0 && ':' != path_lwfn[i] )
880 i--;
882 if ( i + 1 + base_lwfn[0] > path_size )
883 return FT_Err_Invalid_Argument;
885 if ( ':' == path_lwfn[i] )
887 ft_strcpy( (char *)path_lwfn + i + 1, (char *)base_lwfn + 1 );
888 path_lwfn[i + 1 + base_lwfn[0]] = '\0';
890 else
892 ft_strcpy( (char *)path_lwfn, (char *)base_lwfn + 1 );
893 path_lwfn[base_lwfn[0]] = '\0';
896 if ( noErr != FT_FSPathMakeSpec( path_lwfn, &spec, FALSE ) )
897 return FT_Err_Cannot_Open_Resource;
899 return FT_Err_Ok;
901 #endif /* HAVE_FSREF */
906 static short
907 count_faces( Handle fond,
908 const UInt8* pathname )
910 ResID sfnt_id;
911 short have_sfnt, have_lwfn;
912 Str255 lwfn_file_name;
913 UInt8 buff[PATH_MAX];
914 FT_Error err;
915 short num_faces;
918 have_sfnt = have_lwfn = 0;
920 HLock( fond );
921 parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 );
923 if ( lwfn_file_name[0] )
925 err = lookup_lwfn_by_fond( pathname, lwfn_file_name,
926 buff, sizeof ( buff ) );
927 if ( FT_Err_Ok == err )
928 have_lwfn = 1;
931 if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
932 num_faces = 1;
933 else
934 num_faces = count_faces_scalable( *fond );
936 HUnlock( fond );
937 return num_faces;
941 /* Read Type 1 data from the POST resources inside the LWFN file,
942 return a PFB buffer. This is somewhat convoluted because the FT2
943 PFB parser wants the ASCII header as one chunk, and the LWFN
944 chunks are often not organized that way, so we glue chunks
945 of the same type together. */
946 static FT_Error
947 read_lwfn( FT_Memory memory,
948 ResFileRefNum res,
949 FT_Byte** pfb_data,
950 FT_ULong* size )
952 FT_Error error = FT_Err_Ok;
953 ResID res_id;
954 unsigned char *buffer, *p, *size_p = NULL;
955 FT_ULong total_size = 0;
956 FT_ULong old_total_size = 0;
957 FT_ULong post_size, pfb_chunk_size;
958 Handle post_data;
959 char code, last_code;
962 UseResFile( res );
964 /* First pass: load all POST resources, and determine the size of */
965 /* the output buffer. */
966 res_id = 501;
967 last_code = -1;
969 for (;;)
971 post_data = Get1Resource( FT_MAKE_TAG( 'P', 'O', 'S', 'T' ),
972 res_id++ );
973 if ( post_data == NULL )
974 break; /* we are done */
976 code = (*post_data)[0];
978 if ( code != last_code )
980 if ( code == 5 )
981 total_size += 2; /* just the end code */
982 else
983 total_size += 6; /* code + 4 bytes chunk length */
986 total_size += GetHandleSize( post_data ) - 2;
987 last_code = code;
989 /* detect integer overflows */
990 if ( total_size < old_total_size )
992 error = FT_Err_Array_Too_Large;
993 goto Error;
996 old_total_size = total_size;
999 if ( FT_ALLOC( buffer, (FT_Long)total_size ) )
1000 goto Error;
1002 /* Second pass: append all POST data to the buffer, add PFB fields. */
1003 /* Glue all consecutive chunks of the same type together. */
1004 p = buffer;
1005 res_id = 501;
1006 last_code = -1;
1007 pfb_chunk_size = 0;
1009 for (;;)
1011 post_data = Get1Resource( FT_MAKE_TAG( 'P', 'O', 'S', 'T' ),
1012 res_id++ );
1013 if ( post_data == NULL )
1014 break; /* we are done */
1016 post_size = (FT_ULong)GetHandleSize( post_data ) - 2;
1017 code = (*post_data)[0];
1019 if ( code != last_code )
1021 if ( last_code != -1 )
1023 /* we are done adding a chunk, fill in the size field */
1024 if ( size_p != NULL )
1026 *size_p++ = (FT_Byte)( pfb_chunk_size & 0xFF );
1027 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8 ) & 0xFF );
1028 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF );
1029 *size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF );
1031 pfb_chunk_size = 0;
1034 *p++ = 0x80;
1035 if ( code == 5 )
1036 *p++ = 0x03; /* the end */
1037 else if ( code == 2 )
1038 *p++ = 0x02; /* binary segment */
1039 else
1040 *p++ = 0x01; /* ASCII segment */
1042 if ( code != 5 )
1044 size_p = p; /* save for later */
1045 p += 4; /* make space for size field */
1049 ft_memcpy( p, *post_data + 2, post_size );
1050 pfb_chunk_size += post_size;
1051 p += post_size;
1052 last_code = code;
1055 *pfb_data = buffer;
1056 *size = total_size;
1058 Error:
1059 CloseResFile( res );
1060 return error;
1064 /* Finalizer for a memory stream; gets called by FT_Done_Face().
1065 It frees the memory it uses. */
1066 static void
1067 memory_stream_close( FT_Stream stream )
1069 FT_Memory memory = stream->memory;
1072 FT_FREE( stream->base );
1074 stream->size = 0;
1075 stream->base = 0;
1076 stream->close = 0;
1080 /* Create a new memory stream from a buffer and a size. */
1081 static FT_Error
1082 new_memory_stream( FT_Library library,
1083 FT_Byte* base,
1084 FT_ULong size,
1085 FT_Stream_CloseFunc close,
1086 FT_Stream* astream )
1088 FT_Error error;
1089 FT_Memory memory;
1090 FT_Stream stream;
1093 if ( !library )
1094 return FT_Err_Invalid_Library_Handle;
1096 if ( !base )
1097 return FT_Err_Invalid_Argument;
1099 *astream = 0;
1100 memory = library->memory;
1101 if ( FT_NEW( stream ) )
1102 goto Exit;
1104 FT_Stream_OpenMemory( stream, base, size );
1106 stream->close = close;
1108 *astream = stream;
1110 Exit:
1111 return error;
1115 /* Create a new FT_Face given a buffer and a driver name. */
1116 static FT_Error
1117 open_face_from_buffer( FT_Library library,
1118 FT_Byte* base,
1119 FT_ULong size,
1120 FT_Long face_index,
1121 char* driver_name,
1122 FT_Face* aface )
1124 FT_Open_Args args;
1125 FT_Error error;
1126 FT_Stream stream;
1127 FT_Memory memory = library->memory;
1130 error = new_memory_stream( library,
1131 base,
1132 size,
1133 memory_stream_close,
1134 &stream );
1135 if ( error )
1137 FT_FREE( base );
1138 return error;
1141 args.flags = FT_OPEN_STREAM;
1142 args.stream = stream;
1143 if ( driver_name )
1145 args.flags = args.flags | FT_OPEN_DRIVER;
1146 args.driver = FT_Get_Module( library, driver_name );
1149 /* At this point, face_index has served its purpose; */
1150 /* whoever calls this function has already used it to */
1151 /* locate the correct font data. We should not propagate */
1152 /* this index to FT_Open_Face() (unless it is negative). */
1154 if ( face_index > 0 )
1155 face_index = 0;
1157 error = FT_Open_Face( library, &args, face_index, aface );
1158 if ( error == FT_Err_Ok )
1159 (*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
1160 else
1161 FT_Stream_Free( stream, 0 );
1163 return error;
1167 /* Create a new FT_Face from a file spec to an LWFN file. */
1168 static FT_Error
1169 FT_New_Face_From_LWFN( FT_Library library,
1170 const UInt8* pathname,
1171 FT_Long face_index,
1172 FT_Face* aface )
1174 FT_Byte* pfb_data;
1175 FT_ULong pfb_size;
1176 FT_Error error;
1177 ResFileRefNum res;
1180 if ( noErr != FT_FSPathMakeRes( pathname, &res ) )
1181 return FT_Err_Cannot_Open_Resource;
1183 pfb_data = NULL;
1184 pfb_size = 0;
1185 error = read_lwfn( library->memory, res, &pfb_data, &pfb_size );
1186 CloseResFile( res ); /* PFB is already loaded, useless anymore */
1187 if ( error )
1188 return error;
1190 return open_face_from_buffer( library,
1191 pfb_data,
1192 pfb_size,
1193 face_index,
1194 "type1",
1195 aface );
1199 /* Create a new FT_Face from an SFNT resource, specified by res ID. */
1200 static FT_Error
1201 FT_New_Face_From_SFNT( FT_Library library,
1202 ResID sfnt_id,
1203 FT_Long face_index,
1204 FT_Face* aface )
1206 Handle sfnt = NULL;
1207 FT_Byte* sfnt_data;
1208 size_t sfnt_size;
1209 FT_Error error = FT_Err_Ok;
1210 FT_Memory memory = library->memory;
1211 int is_cff;
1214 sfnt = GetResource( FT_MAKE_TAG( 's', 'f', 'n', 't' ), sfnt_id );
1215 if ( sfnt == NULL )
1216 return FT_Err_Invalid_Handle;
1218 sfnt_size = (FT_ULong)GetHandleSize( sfnt );
1219 if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) )
1221 ReleaseResource( sfnt );
1222 return error;
1225 HLock( sfnt );
1226 ft_memcpy( sfnt_data, *sfnt, sfnt_size );
1227 HUnlock( sfnt );
1228 ReleaseResource( sfnt );
1230 is_cff = sfnt_size > 4 && sfnt_data[0] == 'O' &&
1231 sfnt_data[1] == 'T' &&
1232 sfnt_data[2] == 'T' &&
1233 sfnt_data[3] == 'O';
1235 return open_face_from_buffer( library,
1236 sfnt_data,
1237 sfnt_size,
1238 face_index,
1239 is_cff ? "cff" : "truetype",
1240 aface );
1244 /* Create a new FT_Face from a file spec to a suitcase file. */
1245 static FT_Error
1246 FT_New_Face_From_Suitcase( FT_Library library,
1247 const UInt8* pathname,
1248 FT_Long face_index,
1249 FT_Face* aface )
1251 FT_Error error = FT_Err_Cannot_Open_Resource;
1252 ResFileRefNum res_ref;
1253 ResourceIndex res_index;
1254 Handle fond;
1255 short num_faces_in_res, num_faces_in_fond;
1258 if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) )
1259 return FT_Err_Cannot_Open_Resource;
1261 UseResFile( res_ref );
1262 if ( ResError() )
1263 return FT_Err_Cannot_Open_Resource;
1265 num_faces_in_res = 0;
1266 for ( res_index = 1; ; ++res_index )
1268 fond = Get1IndResource( FT_MAKE_TAG( 'F', 'O', 'N', 'D' ),
1269 res_index );
1270 if ( ResError() )
1271 break;
1273 num_faces_in_fond = count_faces( fond, pathname );
1274 num_faces_in_res += num_faces_in_fond;
1276 if ( 0 <= face_index && face_index < num_faces_in_fond && error )
1277 error = FT_New_Face_From_FOND( library, fond, face_index, aface );
1279 face_index -= num_faces_in_fond;
1282 CloseResFile( res_ref );
1283 if ( FT_Err_Ok == error && NULL != aface )
1284 (*aface)->num_faces = num_faces_in_res;
1285 return error;
1289 /* documentation is in ftmac.h */
1291 FT_EXPORT_DEF( FT_Error )
1292 FT_New_Face_From_FOND( FT_Library library,
1293 Handle fond,
1294 FT_Long face_index,
1295 FT_Face* aface )
1297 short have_sfnt, have_lwfn = 0;
1298 ResID sfnt_id, fond_id;
1299 OSType fond_type;
1300 Str255 fond_name;
1301 Str255 lwfn_file_name;
1302 UInt8 path_lwfn[PATH_MAX];
1303 OSErr err;
1304 FT_Error error = FT_Err_Ok;
1307 GetResInfo( fond, &fond_id, &fond_type, fond_name );
1308 if ( ResError() != noErr ||
1309 fond_type != FT_MAKE_TAG( 'F', 'O', 'N', 'D' ) )
1310 return FT_Err_Invalid_File_Format;
1312 HLock( fond );
1313 parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index );
1314 HUnlock( fond );
1316 if ( lwfn_file_name[0] )
1318 ResFileRefNum res;
1321 res = HomeResFile( fond );
1322 if ( noErr != ResError() )
1323 goto found_no_lwfn_file;
1325 #if HAVE_FSREF
1328 UInt8 path_fond[PATH_MAX];
1329 FSRef ref;
1332 err = FSGetForkCBInfo( res, kFSInvalidVolumeRefNum,
1333 NULL, NULL, NULL, &ref, NULL );
1334 if ( noErr != err )
1335 goto found_no_lwfn_file;
1337 err = FSRefMakePath( &ref, path_fond, sizeof ( path_fond ) );
1338 if ( noErr != err )
1339 goto found_no_lwfn_file;
1341 error = lookup_lwfn_by_fond( path_fond, lwfn_file_name,
1342 path_lwfn, sizeof ( path_lwfn ) );
1343 if ( FT_Err_Ok == error )
1344 have_lwfn = 1;
1347 #elif HAVE_FSSPEC
1350 UInt8 path_fond[PATH_MAX];
1351 FCBPBRec pb;
1352 Str255 fond_file_name;
1353 FSSpec spec;
1356 FT_MEM_SET( &spec, 0, sizeof ( FSSpec ) );
1357 FT_MEM_SET( &pb, 0, sizeof ( FCBPBRec ) );
1359 pb.ioNamePtr = fond_file_name;
1360 pb.ioVRefNum = 0;
1361 pb.ioRefNum = res;
1362 pb.ioFCBIndx = 0;
1364 err = PBGetFCBInfoSync( &pb );
1365 if ( noErr != err )
1366 goto found_no_lwfn_file;
1368 err = FSMakeFSSpec( pb.ioFCBVRefNum, pb.ioFCBParID,
1369 fond_file_name, &spec );
1370 if ( noErr != err )
1371 goto found_no_lwfn_file;
1373 err = FT_FSpMakePath( &spec, path_fond, sizeof ( path_fond ) );
1374 if ( noErr != err )
1375 goto found_no_lwfn_file;
1377 error = lookup_lwfn_by_fond( path_fond, lwfn_file_name,
1378 path_lwfn, sizeof ( path_lwfn ) );
1379 if ( FT_Err_Ok == error )
1380 have_lwfn = 1;
1383 #endif /* HAVE_FSREF, HAVE_FSSPEC */
1387 if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
1388 error = FT_New_Face_From_LWFN( library,
1389 path_lwfn,
1390 face_index,
1391 aface );
1392 else
1393 error = FT_Err_Unknown_File_Format;
1395 found_no_lwfn_file:
1396 if ( have_sfnt && FT_Err_Ok != error )
1397 error = FT_New_Face_From_SFNT( library,
1398 sfnt_id,
1399 face_index,
1400 aface );
1402 return error;
1406 /* Common function to load a new FT_Face from a resource file. */
1407 static FT_Error
1408 FT_New_Face_From_Resource( FT_Library library,
1409 const UInt8* pathname,
1410 FT_Long face_index,
1411 FT_Face* aface )
1413 OSType file_type;
1414 FT_Error error;
1417 /* LWFN is a (very) specific file format, check for it explicitly */
1418 file_type = get_file_type_from_path( pathname );
1419 if ( file_type == FT_MAKE_TAG( 'L', 'W', 'F', 'N' ) )
1420 return FT_New_Face_From_LWFN( library, pathname, face_index, aface );
1422 /* Otherwise the file type doesn't matter (there are more than */
1423 /* `FFIL' and `tfil'). Just try opening it as a font suitcase; */
1424 /* if it works, fine. */
1426 error = FT_New_Face_From_Suitcase( library, pathname, face_index, aface );
1427 if ( error == 0 )
1428 return error;
1430 /* let it fall through to normal loader (.ttf, .otf, etc.); */
1431 /* we signal this by returning no error and no FT_Face */
1432 *aface = NULL;
1433 return 0;
1437 /*************************************************************************/
1438 /* */
1439 /* <Function> */
1440 /* FT_New_Face */
1441 /* */
1442 /* <Description> */
1443 /* This is the Mac-specific implementation of FT_New_Face. In */
1444 /* addition to the standard FT_New_Face() functionality, it also */
1445 /* accepts pathnames to Mac suitcase files. For further */
1446 /* documentation see the original FT_New_Face() in freetype.h. */
1447 /* */
1448 FT_EXPORT_DEF( FT_Error )
1449 FT_New_Face( FT_Library library,
1450 const char* pathname,
1451 FT_Long face_index,
1452 FT_Face* aface )
1454 FT_Open_Args args;
1455 FT_Error error;
1458 /* test for valid `library' and `aface' delayed to FT_Open_Face() */
1459 if ( !pathname )
1460 return FT_Err_Invalid_Argument;
1462 error = FT_Err_Ok;
1463 *aface = NULL;
1465 /* try resourcefork based font: LWFN, FFIL */
1466 error = FT_New_Face_From_Resource( library, (UInt8 *)pathname,
1467 face_index, aface );
1468 if ( error != 0 || *aface != NULL )
1469 return error;
1471 /* let it fall through to normal loader (.ttf, .otf, etc.) */
1472 args.flags = FT_OPEN_PATHNAME;
1473 args.pathname = (char*)pathname;
1474 return FT_Open_Face( library, &args, face_index, aface );
1478 /*************************************************************************/
1479 /* */
1480 /* <Function> */
1481 /* FT_New_Face_From_FSRef */
1482 /* */
1483 /* <Description> */
1484 /* FT_New_Face_From_FSRef is identical to FT_New_Face except it */
1485 /* accepts an FSRef instead of a path. */
1486 /* */
1487 /* This function is deprecated because Carbon data types (FSRef) */
1488 /* are not cross-platform, and thus not suitable for the freetype API. */
1489 FT_EXPORT_DEF( FT_Error )
1490 FT_New_Face_From_FSRef( FT_Library library,
1491 const FSRef* ref,
1492 FT_Long face_index,
1493 FT_Face* aface )
1496 #if !HAVE_FSREF
1498 FT_UNUSED( library );
1499 FT_UNUSED( ref );
1500 FT_UNUSED( face_index );
1501 FT_UNUSED( aface );
1503 return FT_Err_Unimplemented_Feature;
1505 #else
1507 FT_Error error;
1508 FT_Open_Args args;
1509 OSErr err;
1510 UInt8 pathname[PATH_MAX];
1513 if ( !ref )
1514 return FT_Err_Invalid_Argument;
1516 err = FSRefMakePath( ref, pathname, sizeof ( pathname ) );
1517 if ( err )
1518 error = FT_Err_Cannot_Open_Resource;
1520 error = FT_New_Face_From_Resource( library, pathname, face_index, aface );
1521 if ( error != 0 || *aface != NULL )
1522 return error;
1524 /* fallback to datafork font */
1525 args.flags = FT_OPEN_PATHNAME;
1526 args.pathname = (char*)pathname;
1527 return FT_Open_Face( library, &args, face_index, aface );
1529 #endif /* HAVE_FSREF */
1534 /*************************************************************************/
1535 /* */
1536 /* <Function> */
1537 /* FT_New_Face_From_FSSpec */
1538 /* */
1539 /* <Description> */
1540 /* FT_New_Face_From_FSSpec is identical to FT_New_Face except it */
1541 /* accepts an FSSpec instead of a path. */
1542 /* */
1543 /* This function is deprecated because Carbon data types (FSSpec) */
1544 /* are not cross-platform, and thus not suitable for the freetype API. */
1545 FT_EXPORT_DEF( FT_Error )
1546 FT_New_Face_From_FSSpec( FT_Library library,
1547 const FSSpec* spec,
1548 FT_Long face_index,
1549 FT_Face* aface )
1552 #if HAVE_FSREF
1554 FSRef ref;
1557 if ( !spec || FSpMakeFSRef( spec, &ref ) != noErr )
1558 return FT_Err_Invalid_Argument;
1559 else
1560 return FT_New_Face_From_FSRef( library, &ref, face_index, aface );
1562 #elif HAVE_FSSPEC
1564 FT_Error error;
1565 FT_Open_Args args;
1566 OSErr err;
1567 UInt8 pathname[PATH_MAX];
1570 if ( !spec )
1571 return FT_Err_Invalid_Argument;
1573 err = FT_FSpMakePath( spec, pathname, sizeof ( pathname ) );
1574 if ( err )
1575 error = FT_Err_Cannot_Open_Resource;
1577 error = FT_New_Face_From_Resource( library, pathname, face_index, aface );
1578 if ( error != 0 || *aface != NULL )
1579 return error;
1581 /* fallback to datafork font */
1582 args.flags = FT_OPEN_PATHNAME;
1583 args.pathname = (char*)pathname;
1584 return FT_Open_Face( library, &args, face_index, aface );
1586 #else
1588 FT_UNUSED( library );
1589 FT_UNUSED( spec );
1590 FT_UNUSED( face_index );
1591 FT_UNUSED( aface );
1593 return FT_Err_Unimplemented_Feature;
1595 #endif /* HAVE_FSREF, HAVE_FSSPEC */
1600 /* END */