/***************************************************************************/ /* */ /* ftmac.c */ /* */ /* Mac FOND support. Written by just@letterror.com. */ /* Heavily modified by mpsuzuki, George Williams, and Sean McBride. */ /* */ /* This file is for Mac OS X only; see builds/mac/ftoldmac.c for */ /* classic platforms built by MPW. */ /* */ /* Copyright 1996-2018 by */ /* Just van Rossum, David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* This file is part of the FreeType project, and may only be used, */ /* modified, and distributed under the terms of the FreeType project */ /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ /* this file you indicate that you have read the license and */ /* understand and accept it fully. */ /* */ /***************************************************************************/ /* Notes Mac suitcase files can (and often do!) contain multiple fonts. To support this I use the face_index argument of FT_(Open|New)_Face() functions, and pretend the suitcase file is a collection. Warning: fbit and NFNT bitmap resources are not supported yet. In old sfnt fonts, bitmap glyph data for each size is stored in each `NFNT' resources instead of the `bdat' table in the sfnt resource. Therefore, face->num_fixed_sizes is set to 0, because bitmap data in `NFNT' resource is unavailable at present. The Mac FOND support works roughly like this: - Check whether the offered stream points to a Mac suitcase file. This is done by checking the file type: it has to be 'FFIL' or 'tfil'. The stream that gets passed to our init_face() routine is a stdio stream, which isn't usable for us, since the FOND resources live in the resource fork. So we just grab the stream->pathname field. - Read the FOND resource into memory, then check whether there is a TrueType font and/or(!) a Type 1 font available. - If there is a Type 1 font available (as a separate `LWFN' file), read its data into memory, massage it slightly so it becomes PFB data, wrap it into a memory stream, load the Type 1 driver and delegate the rest of the work to it by calling FT_Open_Face(). (XXX TODO: after this has been done, the kerning data from the FOND resource should be appended to the face: On the Mac there are usually no AFM files available. However, this is tricky since we need to map Mac char codes to ps glyph names to glyph ID's...) - If there is a TrueType font (an `sfnt' resource), read it into memory, wrap it into a memory stream, load the TrueType driver and delegate the rest of the work to it, by calling FT_Open_Face(). - Some suitcase fonts (notably Onyx) might point the `LWFN' file to itself, even though it doesn't contains `POST' resources. To handle this special case without opening the file an extra time, we just ignore errors from the `LWFN' and fallback to the `sfnt' if both are available. */ #include "ft2build.h" #include FT_FREETYPE_H #include FT_TRUETYPE_TAGS_H #include FT_INTERNAL_STREAM_H #include "ftbase.h" #ifdef FT_MACINTOSH /* This is for Mac OS X. Without redefinition, OS_INLINE */ /* expands to `static inline' which doesn't survive the */ /* -ansi compilation flag of GCC. */ #if !HAVE_ANSI_OS_INLINE #undef OS_INLINE #define OS_INLINE static __inline__ #endif /* `configure' checks the availability of `ResourceIndex' strictly */ /* and sets HAVE_TYPE_RESOURCE_INDEX 1 or 0 always. If it is */ /* not set (e.g., a build without `configure'), the availability */ /* is guessed from the SDK version. */ #ifndef HAVE_TYPE_RESOURCE_INDEX #if !defined( MAC_OS_X_VERSION_10_5 ) || \ ( MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 ) #define HAVE_TYPE_RESOURCE_INDEX 0 #else #define HAVE_TYPE_RESOURCE_INDEX 1 #endif #endif /* !HAVE_TYPE_RESOURCE_INDEX */ #if ( HAVE_TYPE_RESOURCE_INDEX == 0 ) typedef short ResourceIndex; #endif #include #include /* Don't want warnings about our own use of deprecated functions. */ #define FT_DEPRECATED_ATTRIBUTE #include FT_MAC_H static OSErr FT_FSPathMakeRes( const UInt8* pathname, ResFileRefNum* res ) { OSErr err; FSRef ref; if ( noErr != FSPathMakeRef( pathname, &ref, FALSE ) ) return FT_THROW( Cannot_Open_Resource ); /* at present, no support for dfont format */ err = FSOpenResourceFile( &ref, 0, NULL, fsRdPerm, res ); if ( noErr == err ) return err; /* fallback to original resource-fork font */ *res = FSOpenResFile( &ref, fsRdPerm ); err = ResError(); return err; } static short count_faces_sfnt( char* fond_data ) { /* The count is 1 greater than the value in the FOND. */ /* Isn't that cute? :-) */ return EndianS16_BtoN( *( (short*)( fond_data + sizeof ( FamRec ) ) ) ) + 1; } static short count_faces_scalable( char* fond_data ) { AsscEntry* assoc; short i, face, face_all; face_all = EndianS16_BtoN( *( (short *)( fond_data + sizeof ( FamRec ) ) ) ) + 1; assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 ); face = 0; for ( i = 0; i < face_all; i++ ) { if ( 0 == EndianS16_BtoN( assoc[i].fontSize ) ) face++; } return face; } /* 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, ResID* sfnt_id, Str255 lwfn_file_name, short face_index ) { AsscEntry* assoc; AsscEntry* base_assoc; FamRec* fond; *sfnt_id = 0; *have_sfnt = 0; lwfn_file_name[0] = 0; fond = (FamRec*)fond_data; assoc = (AsscEntry*)( fond_data + sizeof ( FamRec ) + 2 ); base_assoc = assoc; /* the maximum faces in a FOND is 48, size of StyleTable.indexes[] */ if ( 47 < face_index ) return; /* Let's do a little range checking before we get too excited here */ if ( face_index < count_faces_sfnt( fond_data ) ) { 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 ( EndianS16_BtoN( assoc->fontSize ) == 0 ) { *have_sfnt = 1; *sfnt_id = EndianS16_BtoN( assoc->fontID ); } else if ( base_assoc->fontSize == 0 ) { *have_sfnt = 1; *sfnt_id = EndianS16_BtoN( base_assoc->fontID ); } } } static short count_faces( Handle fond, const UInt8* pathname ) { ResID sfnt_id; short have_sfnt; Str255 lwfn_file_name; have_sfnt = 0; parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, 0 ); return count_faces_scalable( *fond ); } /* Create a new FT_Face from an SFNT resource, specified by res ID. */ static FT_Error FT_New_Face_From_SFNT( FT_Library library, ResID sfnt_id, FT_Long face_index, FT_Face* aface ) { Handle sfnt = NULL; FT_Byte* sfnt_data; size_t sfnt_size; FT_Error error = FT_Err_Ok; FT_Memory memory = library->memory; int is_cff, is_sfnt_ps; sfnt = GetResource( TTAG_sfnt, sfnt_id ); if ( !sfnt ) return FT_THROW( Invalid_Handle ); sfnt_size = (FT_ULong)GetHandleSize( sfnt ); /* detect resource fork overflow */ if ( FT_MAC_RFORK_MAX_LEN < sfnt_size ) return FT_THROW( Array_Too_Large ); if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) ) { ReleaseResource( sfnt ); return error; } ft_memcpy( sfnt_data, *sfnt, sfnt_size ); ReleaseResource( sfnt ); is_cff = sfnt_size > 4 && !ft_memcmp( sfnt_data, "OTTO", 4 ); is_sfnt_ps = sfnt_size > 4 && !ft_memcmp( sfnt_data, "typ1", 4 ); if ( is_sfnt_ps ) { FT_Stream stream; if ( FT_NEW( stream ) ) goto Try_OpenType; FT_Stream_OpenMemory( stream, sfnt_data, sfnt_size ); if ( !open_face_PS_from_sfnt_stream( library, stream, face_index, 0, NULL, aface ) ) { FT_Stream_Close( stream ); FT_FREE( stream ); FT_FREE( sfnt_data ); goto Exit; } FT_FREE( stream ); } Try_OpenType: error = open_face_from_buffer( library, sfnt_data, sfnt_size, face_index, is_cff ? "cff" : "truetype", aface ); Exit: return error; } /* Create a new FT_Face from a file path to a suitcase file. */ static FT_Error FT_New_Face_From_Suitcase( FT_Library library, const UInt8* pathname, FT_Long face_index, FT_Face* aface ) { FT_Error error = FT_ERR( Cannot_Open_Resource ); ResFileRefNum res_ref; ResourceIndex res_index; Handle fond; short num_faces_in_res; if ( noErr != FT_FSPathMakeRes( pathname, &res_ref ) ) return FT_THROW( Cannot_Open_Resource ); UseResFile( res_ref ); if ( ResError() ) return FT_THROW( Cannot_Open_Resource ); num_faces_in_res = 0; for ( res_index = 1; ; res_index++ ) { short num_faces_in_fond; fond = Get1IndResource( TTAG_FOND, res_index ); if ( ResError() ) break; num_faces_in_fond = count_faces( fond, pathname ); num_faces_in_res += num_faces_in_fond; if ( 0 <= face_index && face_index < num_faces_in_fond && error ) error = FT_New_Face_From_FOND( library, fond, face_index, aface ); face_index -= num_faces_in_fond; } CloseResFile( res_ref ); if ( !error && aface && *aface ) (*aface)->num_faces = num_faces_in_res; return error; } /* documentation is in ftmac.h */ FT_EXPORT_DEF( FT_Error ) FT_New_Face_From_FOND( FT_Library library, Handle fond, FT_Long face_index, FT_Face* aface ) { short have_sfnt = 0; ResID sfnt_id, fond_id; OSType fond_type; Str255 fond_name; Str255 lwfn_file_name; /* check of `library' and `aface' delayed to `FT_New_Face_From_XXX' */ GetResInfo( fond, &fond_id, &fond_type, fond_name ); if ( ResError() != noErr || fond_type != TTAG_FOND ) return FT_THROW( Invalid_File_Format ); parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index ); return FT_New_Face_From_SFNT( library, sfnt_id, face_index, aface ); } /*************************************************************************/ /* */ /* */ /* FT_New_Face */ /* */ /* */ /* This is the Mac-specific implementation of FT_New_Face. In */ /* addition to the standard FT_New_Face() functionality, it also */ /* accepts pathnames to Mac suitcase files. For further */ /* documentation see the original FT_New_Face() in freetype.h. */ /* */ FT_EXPORT_DEF( FT_Error ) FT_New_Face( FT_Library library, const FT_Open_Args* args, FT_Long face_index, FT_Face* aface ) { FT_Error error; /* test for valid `library' and `aface' delayed to FT_Open_Face() */ if ( !args || !args->pathname ) return FT_THROW( Invalid_Argument ); *aface = NULL; error = FT_New_Face_From_Suitcase( library, args->pathname, face_index, aface ); if ( *aface ) return FT_Err_Ok; /* let it fall through to normal loader (.ttf, .otf, etc.) */ return FT_Open_Face( library, args, face_index, aface ); } #else /* !FT_MACINTOSH */ /* ANSI C doesn't like empty source files */ typedef int _ft_mac_dummy; #endif /* !FT_MACINTOSH */ /* END */