diff --git a/ChangeLog b/ChangeLog index 92105ee3f..57914c5ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,36 @@ +2004-02-25 Werner Lemberg + + * docs/CHANGES: Updated. + +2004-02-25 Garrick Meeker + + Improve MacOS fond support. Provide a new API + `FT_New_Face_From_FSSpec' similar to `FT_New_Face'. + + * src/base/ftmac.c [__MWERKS__]: Include FSp_fpopen.h. + STREAM_FILE [__MWERKS__]: New macro. + (ft_FSp_stream_close, ft_FSp_stream_io) [__MWERKS__]: New functions. + (file_spec_from_path) [__MWERKS__]: Updated #if statement. + (get_file_type, make_lwfn_spec): Use `const' for argument. + (is_dfont) [TARGET_API_MAC_CARBON]: Removed. + (count_face_sfnt, count_faces): New functions. + (parse_fond): Do some range checking. + (read_lwfn): Change type of second argument. + No longer call FSpOpenResFile. + (OpenFileAsResource): New function. + (FT_New_Face_From_LWFN): Use `const' for second argument. + Use OpenFileAsResource. + (FT_New_Face_From_Suitcase): Change type of second argument. + No longer call FSpOpenResFile. + Loop over all resource indices. + (FT_New_Face_From_dfont) [TARGET_API_MAC_CARBON]: Removed. + (FT_GetFile_From_Mac_Name): Use `const' for first argument. + (ResourceForkSize): Removed. + (FT_New_Face): Updated to use new functions. + (FT_New_Face_From_FSSpec): New function. + + * include/freetype/ftmac.h: Updated. + 2004-02-24 Malcolm Taylor * src/autohint/ahhint.c (ah_hinter_load) : diff --git a/docs/CHANGES b/docs/CHANGES index 77e815f2e..7c532645d 100644 --- a/docs/CHANGES +++ b/docs/CHANGES @@ -67,6 +67,10 @@ LATEST CHANGES BETWEEN 2.1.8 and 2.1.7 forks on non-MacOS platforms (for example, Linux can mount MacOS file systems). + - Support for MacOS has been improved; there is now a new function + `FT_New_Face_From_FSSpec' similar to `FT_New_Face' except that + it accepts an FSSpec instead of a path. + - The cache sub-system has been rewritten. - There is now support for deinstallation of faces. diff --git a/include/freetype/ftmac.h b/include/freetype/ftmac.h index 5c29b11be..78ed77947 100644 --- a/include/freetype/ftmac.h +++ b/include/freetype/ftmac.h @@ -112,9 +112,43 @@ FT_BEGIN_HEADER /* FreeType error code. 0 means success. */ /* */ FT_EXPORT( FT_Error ) - FT_GetFile_From_Mac_Name( char* fontName, - FSSpec* pathSpec, - FT_Long* face_index ); + FT_GetFile_From_Mac_Name( const char* fontName, + FSSpec* pathSpec, + FT_Long* face_index ); + + + /*************************************************************************/ + /* */ + /* */ + /* FT_New_Face_From_FSSpec */ + /* */ + /* */ + /* Creates a new face object from a given resource and typeface index */ + /* using an FSSpec to the font file. */ + /* */ + /* */ + /* library :: A handle to the library resource. */ + /* */ + /* */ + /* spec :: FSSpec to the font file. */ + /* */ + /* face_index :: The index of the face within the resource. The */ + /* first face has index 0. */ + /* */ + /* aface :: A handle to a new face object. */ + /* */ + /* */ + /* FreeType error code. 0 means success. */ + /* */ + /* */ + /* @FT_New_Face_From_FSSpec is identical to @FT_New_Face except */ + /* it accepts an FSSpec instead of a path. */ + /* */ + FT_EXPORT( FT_Error ) + FT_New_Face_From_FSSpec( FT_Library library, + const FSSpec *spec, + FT_Long face_index, + FT_Face *aface ); /* */ diff --git a/src/base/ftmac.c b/src/base/ftmac.c index 04c03d99e..7f05f3b61 100644 --- a/src/base/ftmac.c +++ b/src/base/ftmac.c @@ -4,7 +4,7 @@ /* */ /* Mac FOND support. Written by just@letterror.com. */ /* */ -/* Copyright 1996-2001, 2002, 2003 by */ +/* Copyright 1996-2001, 2002, 2003, 2004 by */ /* Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* This file is part of the FreeType project, and may only be used, */ @@ -77,6 +77,10 @@ #include #endif +#if defined( __MWERKS__ ) && !TARGET_RT_MAC_MACHO +#include +#endif + #include FT_MAC_H @@ -87,12 +91,50 @@ #define PREFER_LWFN 1 #endif + +#if defined( __MWERKS__ ) && !TARGET_RT_MAC_MACHO + +#define STREAM_FILE( stream ) ( (FILE*)stream->descriptor.pointer ) + + + FT_CALLBACK_DEF( void ) + ft_FSp_stream_close( FT_Stream stream ) + { + fclose( STREAM_FILE( stream ) ); + + stream->descriptor.pointer = NULL; + stream->size = 0; + stream->base = 0; + } + + + FT_CALLBACK_DEF( unsigned long ) + ft_FSp_stream_io( FT_Stream stream, + unsigned long offset, + unsigned char* buffer, + unsigned long count ) + { + FILE* file; + + + file = STREAM_FILE( stream ); + + fseek( file, offset, SEEK_SET ); + + return (unsigned long)fread( buffer, 1, count, file ); + } + +#endif /* __MWERKS__ && !TARGET_RT_MAC_MACHO */ + + /* Given a pathname, fill in a file spec. */ static int file_spec_from_path( const char* pathname, FSSpec* spec ) { -#if defined( TARGET_API_MAC_CARBON ) && !defined( __MWERKS__ ) + +#if TARGET_API_MAC_CARBON && \ + !( defined( __MWERKS__ ) && !TARGET_RT_MAC_MACHO ) OSErr e; FSRef ref; @@ -123,12 +165,13 @@ return 0; #endif + } /* Return the file type of the file specified by spec. */ static OSType - get_file_type( FSSpec* spec ) + get_file_type( const FSSpec* spec ) { FInfo finfo; @@ -140,22 +183,6 @@ } -#ifdef TARGET_API_MAC_CARBON - - /* is this a Mac OS X .dfont file */ - static Boolean - is_dfont( FSSpec* spec ) - { - int nameLen = spec->name[0]; - - - return nameLen >= 6 && - !ft_memcmp( spec->name + nameLen - 5, ".dfont", 6 ); - } - -#endif - - /* Given a PostScript font name, create the Macintosh LWFN file name. */ static void create_lwfn_name( char* ps_name, @@ -217,9 +244,9 @@ /* Make a file spec for an LWFN file from a FOND resource and a file name. */ static FT_Error - make_lwfn_spec( Handle fond, - unsigned char* file_name, - FSSpec* spec ) + make_lwfn_spec( Handle fond, + const unsigned char* file_name, + FSSpec* spec ) { FT_Error error; short ref_num, v_ref_num; @@ -240,12 +267,24 @@ } + static short + count_faces_sfnt( char *fond_data ) + { + /* The count is 1 greater than the value in the FOND. */ + /* Isn't that cute? :-) */ + + return 1 + *( (short *)( fond_data + sizeof ( FamRec ) ) ); + } + + /* Look inside the FOND data, answer whether there should be an SFNT resource, and answer the name of a possible LWFN Type 1 file. Thanks to Paul Miller (paulm@profoundeffects.com) for the fix to load a face OTHER than the first one in the FOND! */ + + static void parse_fond( char* fond_data, short* have_sfnt, @@ -265,19 +304,24 @@ fond = (FamRec*)fond_data; assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 ); base_assoc = assoc; - assoc += face_index; /* add on the face_index! */ - /* if the face at this index is not scalable, - fall back to the first one (old behavior) */ - if ( assoc->fontSize == 0 ) + /* Let's do a little range checking before we get too excited here */ + if ( face_index < count_faces_sfnt( fond_data ) ) { - *have_sfnt = 1; - *sfnt_id = assoc->fontID; - } - else if ( base_assoc->fontSize == 0 ) - { - *have_sfnt = 1; - *sfnt_id = base_assoc->fontID; + assoc += face_index; /* add on the face_index! */ + + /* if the face at this index is not scalable, + fall back to the first one (old behavior) */ + if ( assoc->fontSize == 0 ) + { + *have_sfnt = 1; + *sfnt_id = assoc->fontID; + } + else if ( base_assoc->fontSize == 0 ) + { + *have_sfnt = 1; + *sfnt_id = base_assoc->fontID; + } } if ( fond->ffStylOff ) @@ -344,6 +388,33 @@ } + static short + count_faces( Handle fond ) + { + short sfnt_id, have_sfnt, have_lwfn = 0; + Str255 lwfn_file_name; + FSSpec lwfn_spec; + + + HLock( fond ); + parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 ); + HUnlock( fond ); + + if ( lwfn_file_name[0] ) + { + if ( make_lwfn_spec( fond, lwfn_file_name, &lwfn_spec ) == FT_Err_Ok ) + have_lwfn = 1; /* yeah, we got one! */ + else + have_lwfn = 0; /* no LWFN file found */ + } + + if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) ) + return 1; + else + return count_faces_sfnt( *fond ); + } + + /* Read Type 1 data from the POST resources inside the LWFN file, return a PFB buffer. This is somewhat convoluted because the FT2 PFB parser wants the ASCII header as one chunk, and the LWFN @@ -351,12 +422,12 @@ of the same type together. */ static FT_Error read_lwfn( FT_Memory memory, - FSSpec* lwfn_spec, + short res_ref, FT_Byte** pfb_data, FT_ULong* size ) { FT_Error error = FT_Err_Ok; - short res_ref, res_id; + short res_id; unsigned char *buffer, *p, *size_p = NULL; FT_ULong total_size = 0; FT_ULong post_size, pfb_chunk_size; @@ -364,13 +435,10 @@ char code, last_code; - res_ref = FSpOpenResFile( lwfn_spec, fsRdPerm ); - if ( ResError() ) - return FT_Err_Out_Of_Memory; UseResFile( res_ref ); - /* First pass: load all POST resources, and determine the size of - the output buffer. */ + /* First pass: load all POST resources, and determine the size of */ + /* the output buffer. */ res_id = 501; last_code = -1; @@ -397,8 +465,8 @@ if ( FT_ALLOC( buffer, (FT_Long)total_size ) ) goto Error; - /* Second pass: append all POST data to the buffer, add PFB fields. - Glue all consecutive chunks of the same type together. */ + /* Second pass: append all POST data to the buffer, add PFB fields. */ + /* Glue all consecutive chunks of the same type together. */ p = buffer; res_id = 501; last_code = -1; @@ -551,19 +619,59 @@ } + static FT_Error + OpenFileAsResource( const FSSpec* spec, + short *p_res_ref ) + { + FT_Error error; + +#if TARGET_API_MAC_CARBON + + FSRef hostContainerRef; + + + error = FSpMakeFSRef( spec, &hostContainerRef ); + if ( error == noErr ) + error = FSOpenResourceFile( &hostContainerRef, + 0, NULL, fsRdPerm, p_res_ref ); + + /* If the above fails, then it is probably not a resource file */ + /* However, it has been reported that FSOpenResourceFile() sometimes */ + /* fails on some old resource-fork files, which FSpOpenResFile() can */ + /* open. So, just try again with FSpOpenResFile() and see what */ + /* happens :-) */ + + if ( error != noErr ) + +#endif /* TARGET_API_MAC_CARBON */ + + { + *p_res_ref = FSpOpenResFile( spec, fsRdPerm ); + error = ResError(); + } + + return error ? FT_Err_Cannot_Open_Resource : FT_Err_Ok; + } + + /* Create a new FT_Face from a file spec to an LWFN file. */ static FT_Error - FT_New_Face_From_LWFN( FT_Library library, - FSSpec* spec, - FT_Long face_index, - FT_Face *aface ) + FT_New_Face_From_LWFN( FT_Library library, + const FSSpec* lwfn_spec, + FT_Long face_index, + FT_Face *aface ) { FT_Byte* pfb_data; FT_ULong pfb_size; FT_Error error; + short res_ref; - error = read_lwfn( library->memory, spec, &pfb_data, &pfb_size ); + error = OpenFileAsResource( lwfn_spec, &res_ref ); + if ( error ) + return error; + + error = read_lwfn( library->memory, res_ref, &pfb_data, &pfb_size ); if ( error ) return error; @@ -624,34 +732,34 @@ /* Create a new FT_Face from a file spec to a suitcase file. */ static FT_Error FT_New_Face_From_Suitcase( FT_Library library, - FSSpec* spec, + short res_ref, FT_Long face_index, FT_Face *aface ) { FT_Error error = FT_Err_Ok; - short res_ref, res_index; + short res_index; Handle fond; + short num_faces; - res_ref = FSpOpenResFile( spec, fsRdPerm ); - if ( ResError() ) - return FT_Err_Cannot_Open_Resource; UseResFile( res_ref ); - /* face_index may be -1, in which case we - just need to do a sanity check */ - if ( face_index < 0 ) - res_index = 1; - else + for ( res_index = 1; ; ++res_index ) { - res_index = (short)( face_index + 1 ); - face_index = 0; - } - fond = Get1IndResource( 'FOND', res_index ); - if ( ResError() ) - { - error = FT_Err_Cannot_Open_Resource; - goto Error; + fond = Get1IndResource( 'FOND', res_index ); + if ( ResError() ) + { + error = FT_Err_Cannot_Open_Resource; + goto Error; + } + if ( face_index < 0 ) + break; + + num_faces = count_faces( fond ); + if ( face_index < num_faces ) + break; + + face_index -= num_faces; } error = FT_New_Face_From_FOND( library, fond, face_index, aface ); @@ -662,57 +770,6 @@ } -#ifdef TARGET_API_MAC_CARBON - - /* Create a new FT_Face from a file spec to a suitcase file. */ - static FT_Error - FT_New_Face_From_dfont( FT_Library library, - FSSpec* spec, - FT_Long face_index, - FT_Face* aface ) - { - FT_Error error = FT_Err_Ok; - short res_ref, res_index; - Handle fond; - FSRef hostContainerRef; - - - error = FSpMakeFSRef( spec, &hostContainerRef ); - if ( error == noErr ) - error = FSOpenResourceFile( &hostContainerRef, - 0, NULL, fsRdPerm, &res_ref ); - - if ( error != noErr ) - return FT_Err_Cannot_Open_Resource; - - UseResFile( res_ref ); - - /* face_index may be -1, in which case we - just need to do a sanity check */ - if ( face_index < 0 ) - res_index = 1; - else - { - res_index = (short)( face_index + 1 ); - face_index = 0; - } - fond = Get1IndResource( 'FOND', res_index ); - if ( ResError() ) - { - error = FT_Err_Cannot_Open_Resource; - goto Error; - } - - error = FT_New_Face_From_FOND( library, fond, face_index, aface ); - - Error: - CloseResFile( res_ref ); - return error; - } - -#endif - - /* documentation is in ftmac.h */ FT_EXPORT_DEF( FT_Error ) @@ -763,9 +820,9 @@ /* documentation is in ftmac.h */ FT_EXPORT_DEF( FT_Error ) - FT_GetFile_From_Mac_Name( char* fontName, - FSSpec* pathSpec, - FT_Long* face_index ) + FT_GetFile_From_Mac_Name( const char* fontName, + FSSpec* pathSpec, + FT_Long* face_index ) { OptionBits options = kFMUseGlobalScopeOption; @@ -846,25 +903,6 @@ } - static long - ResourceForkSize(FSSpec* spec) - { - long len; - short refNum; - OSErr e; - - - e = FSpOpenRF( spec, fsRdPerm, &refNum ); /* I.M. Files 2-155 */ - if ( e == noErr ) - { - e = GetEOF( refNum, &len ); - FSClose( refNum ); - } - - return ( e == noErr ) ? len : 0; - } - - /*************************************************************************/ /* */ /* */ @@ -885,6 +923,8 @@ FT_Open_Args args; FSSpec spec; OSType file_type; + short res_ref; + FT_Error result; /* test for valid `library' and `aface' delayed to FT_Open_Face() */ @@ -894,27 +934,29 @@ if ( file_spec_from_path( pathname, &spec ) ) return FT_Err_Invalid_Argument; - /* Regardless of type, don't try to use the resource fork if it is */ - /* empty. Some TTF fonts have type `FFIL', for example, but they */ - /* only have data forks. */ - - if ( ResourceForkSize( &spec ) != 0 ) + if ( OpenFileAsResource( &spec, &res_ref ) == FT_Err_Ok ) { - file_type = get_file_type( &spec ); - if ( file_type == 'FFIL' || file_type == 'tfil' ) - return FT_New_Face_From_Suitcase( library, &spec, face_index, aface ); + /* LWFN is a (very) specific file format, check for it explicitly */ + file_type = get_file_type( &spec ); if ( file_type == 'LWFN' ) return FT_New_Face_From_LWFN( library, &spec, face_index, aface ); + + /* Otherwise the file type doesn't matter (there are more than */ + /* `FFIL' and `tfil') -- just try opening it as a font suitcase; */ + /* if it works, fine. */ + + result = FT_New_Face_From_Suitcase( library, res_ref, + face_index, aface ); + if ( result == 0 ) + return result; + + /* else forget about the resource fork and fall through to */ + /* data fork formats */ + + CloseResFile( res_ref ); } -#ifdef TARGET_API_MAC_CARBON - - if ( is_dfont( &spec ) ) - return FT_New_Face_From_dfont( library, &spec, face_index, aface ); - -#endif - /* let it fall through to normal loader (.ttf, .otf, etc.) */ args.flags = FT_OPEN_PATHNAME; args.pathname = (char*)pathname; @@ -922,4 +964,116 @@ } + /*************************************************************************/ + /* */ + /* */ + /* FT_New_Face_From_FSSpec */ + /* */ + /* */ + /* FT_New_Face_From_FSSpec is identical to FT_New_Face except it */ + /* accepts an FSSpec instead of a path. */ + /* */ + FT_EXPORT_DEF( FT_Error ) + FT_New_Face_From_FSSpec( FT_Library library, + const FSSpec *spec, + FT_Long face_index, + FT_Face *aface ) + { + FT_Open_Args args; + OSType file_type; + short res_ref; + FT_Error error; + FT_Stream stream; + FILE* file; + FT_Memory memory; + + + /* test for valid `library' and `aface' delayed to FT_Open_Face() */ + if ( !spec ) + return FT_Err_Invalid_Argument; + + if ( OpenFileAsResource( spec, &res_ref ) == FT_Err_Ok ) + { + /* LWFN is a (very) specific file format, check for it explicitly */ + + file_type = get_file_type( spec ); + if ( file_type == 'LWFN' ) + return FT_New_Face_From_LWFN( library, spec, face_index, aface ); + + /* Otherwise the file type doesn't matter (there are more than */ + /* `FFIL' and `tfil') -- just try opening it as a font suitcase; */ + /* if it works, fine. */ + + error = FT_New_Face_From_Suitcase( library, res_ref, + face_index, aface ); + if ( error == 0 ) + return error; + + /* else forget about the resource fork and fall through to */ + /* data fork formats */ + + CloseResFile( res_ref ); + } + + /* let it fall through to normal loader (.ttf, .otf, etc.) */ + +#if defined( __MWERKS__ ) && !TARGET_RT_MAC_MACHO + + /* Codewarrior's C library can open a FILE from a FSSpec */ +#include + + memory = library->memory; + + if ( FT_NEW( stream ) ) + return error; + stream->memory = memory; + + file = FSp_fopen( spec, "rb" ); + if ( !file ) + return FT_Err_Cannot_Open_Resource; + + fseek( file, 0, SEEK_END ); + stream->size = ftell( file ); + fseek( file, 0, SEEK_SET ); + + stream->descriptor.pointer = file; + stream->pathname.pointer = NULL; + stream->pos = 0; + + stream->read = ft_FSp_stream_io; + stream->close = ft_FSp_stream_close; + + args.flags = FT_OPEN_STREAM; + args.stream = stream; + + error = FT_Open_Face( library, &args, face_index, aface ); + if ( error == FT_Err_Ok ) + (*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM; + +#else /* !(__MWERKS__ && !TARGET_RT_MAC_MACHO) */ + + { + FSRef ref; + UInt8 path[256]; + OSErr err; + + + err = FSpMakeFSRef(spec, &ref); + if ( !err ) + { + err = FSRefMakePath( &ref, path, sizeof ( path ) ); + if ( !err ) + error = FT_New_Face( library, (const char*)path, + face_index, aface ); + } + if ( err ) + error = FT_Err_Cannot_Open_Resource; + } + +#endif /* !(__MWERKS__ && !TARGET_RT_MAC_MACHO) */ + + return error; + } + + /* END */