#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_INTERNAL_STREAM_H
#include "truetype/ttobjs.h"
#include "type1/t1objs.h"
#ifdef __GNUC__
#include <Carbon/Carbon.h>
#else
#include <Resources.h>
#include <Fonts.h>
#include <Errors.h>
#include <Files.h>
#include <TextUtils.h>
#endif
#include FT_MAC_H
#ifndef PREFER_LWFN
#define PREFER_LWFN 1
#endif
static int
file_spec_from_path( const char* pathname,
FSSpec* spec )
{
#if TARGET_API_MAC_CARBON
OSErr e;
FSRef ref;
e = FSPathMakeRef( (UInt8 *)pathname, &ref, false );
if ( e == noErr )
e = FSGetCatalogInfo( &ref, kFSCatInfoNone, NULL, NULL, spec, NULL );
return ( e == noErr ) ? 0 : (-1);
#else
Str255 p_path;
FT_ULong path_len;
path_len = ft_strlen( pathname );
if ( path_len > 255 )
return -1;
p_path[0] = (unsigned char)path_len;
ft_strncpy( (char*)p_path + 1, pathname, path_len );
if ( FSMakeFSSpec( 0, 0, p_path, spec ) != noErr )
return -1;
else
return 0;
#endif
}
static OSType
get_file_type( FSSpec* spec )
{
FInfo finfo;
if ( FSpGetFInfo( spec, &finfo ) != noErr )
return 0;
return finfo.fdType;
}
#if TARGET_API_MAC_CARBON
static Boolean
is_dfont( FSSpec* spec )
{
int nameLen = spec->name[0];
return nameLen >= 6 &&
!memcmp( spec->name + nameLen - 5, ".dfont", 6 );
}
#endif
static void
create_lwfn_name( char* ps_name,
Str255 lwfn_file_name )
{
int max = 5, count = 0;
FT_Byte* p = lwfn_file_name;
FT_Byte* q = (FT_Byte*)ps_name;
lwfn_file_name[0] = 0;
while ( *q )
{
if ( isupper( *q ) )
{
if ( count )
max = 3;
count = 0;
}
if ( count < max && ( ft_isalnum( *q ) || *q == '_' ) )
{
*++p = *q;
lwfn_file_name[0]++;
count++;
}
q++;
}
}
static FT_Error
get_file_location( short ref_num,
short* v_ref_num,
long* dir_id,
unsigned char* file_name )
{
FCBPBRec pb;
OSErr error;
pb.ioNamePtr = file_name;
pb.ioVRefNum = 0;
pb.ioRefNum = ref_num;
pb.ioFCBIndx = 0;
error = PBGetFCBInfoSync( &pb );
if ( error == noErr )
{
*v_ref_num = pb.ioFCBVRefNum;
*dir_id = pb.ioFCBParID;
}
return error;
}
static FT_Error
make_lwfn_spec( Handle fond,
unsigned char* file_name,
FSSpec* spec )
{
FT_Error error;
short ref_num, v_ref_num;
long dir_id;
Str255 fond_file_name;
ref_num = HomeResFile( fond );
error = ResError();
if ( !error )
error = get_file_location( ref_num, &v_ref_num,
&dir_id, fond_file_name );
if ( !error )
error = FSMakeFSSpec( v_ref_num, dir_id, file_name, spec );
return error;
}
static void
parse_fond( char* fond_data,
short* have_sfnt,
short* 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;
assoc += face_index;
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 )
{
unsigned char* p = (unsigned char*)fond_data;
StyleTable* style;
unsigned short string_count;
char ps_name[256];
unsigned char* names[64];
int i;
p += fond->ffStylOff;
style = (StyleTable*)p;
p += sizeof ( StyleTable );
string_count = *(unsigned short*)(p);
p += sizeof ( short );
for ( i = 0 ; i < string_count && i < 64; i++ )
{
names[i] = p;
p += names[i][0];
p++;
}
{
size_t ps_name_len = (size_t)names[0][0];
if ( ps_name_len != 0 )
{
memcpy(ps_name, names[0] + 1, ps_name_len);
ps_name[ps_name_len] = 0;
}
if ( style->indexes[0] > 1 )
{
unsigned char* suffixes = names[style->indexes[0] - 1];
for ( i = 1; i < suffixes[0]; i++ )
{
unsigned char* s;
size_t j = suffixes[i] - 1;
if ( j < string_count && ( s = names[j] ) != NULL )
{
size_t s_len = (size_t)s[0];
if ( s_len != 0 && ps_name_len + s_len < sizeof ( ps_name ) )
{
memcpy( ps_name + ps_name_len, s + 1, s_len );
ps_name_len += s_len;
ps_name[ps_name_len] = 0;
}
}
}
}
}
create_lwfn_name( ps_name, lwfn_file_name );
}
}
static FT_Error
read_lwfn( FT_Memory memory,
FSSpec* lwfn_spec,
FT_Byte** pfb_data,
FT_ULong* size )
{
FT_Error error = FT_Err_Ok;
short res_ref, res_id;
unsigned char *buffer, *p, *size_p = NULL;
FT_ULong total_size = 0;
FT_ULong post_size, pfb_chunk_size;
Handle post_data;
char code, last_code;
res_ref = FSpOpenResFile( lwfn_spec, fsRdPerm );
if ( ResError() )
return FT_Err_Out_Of_Memory;
UseResFile( res_ref );
res_id = 501;
last_code = -1;
for (;;)
{
post_data = Get1Resource( 'POST', res_id++ );
if ( post_data == NULL )
break;
code = (*post_data)[0];
if ( code != last_code )
{
if ( code == 5 )
total_size += 2;
else
total_size += 6;
}
total_size += GetHandleSize( post_data ) - 2;
last_code = code;
}
if ( FT_ALLOC( buffer, (FT_Long)total_size ) )
goto Error;
p = buffer;
res_id = 501;
last_code = -1;
pfb_chunk_size = 0;
for (;;)
{
post_data = Get1Resource( 'POST', res_id++ );
if ( post_data == NULL )
break;
post_size = (FT_ULong)GetHandleSize( post_data ) - 2;
code = (*post_data)[0];
if ( code != last_code )
{
if ( last_code != -1 )
{
if ( size_p != NULL )
{
*size_p++ = (FT_Byte)( pfb_chunk_size & 0xFF );
*size_p++ = (FT_Byte)( ( pfb_chunk_size >> 8 ) & 0xFF );
*size_p++ = (FT_Byte)( ( pfb_chunk_size >> 16 ) & 0xFF );
*size_p++ = (FT_Byte)( ( pfb_chunk_size >> 24 ) & 0xFF );
}
pfb_chunk_size = 0;
}
*p++ = 0x80;
if ( code == 5 )
*p++ = 0x03;
else if ( code == 2 )
*p++ = 0x02;
else
*p++ = 0x01;
if ( code != 5 )
{
size_p = p;
p += 4;
}
}
ft_memcpy( p, *post_data + 2, post_size );
pfb_chunk_size += post_size;
p += post_size;
last_code = code;
}
*pfb_data = buffer;
*size = total_size;
Error:
CloseResFile( res_ref );
return error;
}
static void
memory_stream_close( FT_Stream stream )
{
FT_Memory memory = stream->memory;
FT_FREE( stream->base );
stream->size = 0;
stream->base = 0;
stream->close = 0;
}
static FT_Error
new_memory_stream( FT_Library library,
FT_Byte* base,
FT_ULong size,
FT_Stream_CloseFunc close,
FT_Stream *astream )
{
FT_Error error;
FT_Memory memory;
FT_Stream stream;
if ( !library )
return FT_Err_Invalid_Library_Handle;
if ( !base )
return FT_Err_Invalid_Argument;
*astream = 0;
memory = library->memory;
if ( FT_NEW( stream ) )
goto Exit;
FT_Stream_OpenMemory( stream, base, size );
stream->close = close;
*astream = stream;
Exit:
return error;
}
static FT_Error
open_face_from_buffer( FT_Library library,
FT_Byte* base,
FT_ULong size,
FT_Long face_index,
char* driver_name,
FT_Face *aface )
{
FT_Open_Args args;
FT_Error error;
FT_Stream stream;
FT_Memory memory = library->memory;
error = new_memory_stream( library,
base,
size,
memory_stream_close,
&stream );
if ( error )
{
FT_FREE( base );
return error;
}
args.flags = FT_OPEN_STREAM;
args.stream = stream;
if ( driver_name )
{
args.flags = args.flags | FT_OPEN_DRIVER;
args.driver = FT_Get_Module( library, driver_name );
}
error = FT_Open_Face( library, &args, face_index, aface );
if ( error == FT_Err_Ok )
(*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
return error;
}
static FT_Error
FT_New_Face_From_LWFN( FT_Library library,
FSSpec* spec,
FT_Long face_index,
FT_Face *aface )
{
FT_Byte* pfb_data;
FT_ULong pfb_size;
FT_Error error;
error = read_lwfn( library->memory, spec, &pfb_data, &pfb_size );
if ( error )
return error;
return open_face_from_buffer( library,
pfb_data,
pfb_size,
face_index,
"type1",
aface );
}
static FT_Error
FT_New_Face_From_SFNT( FT_Library library,
short sfnt_id,
FT_Long face_index,
FT_Face *aface )
{
Handle sfnt = NULL;
FT_Byte* sfnt_data;
size_t sfnt_size;
FT_Error error = 0;
FT_Memory memory = library->memory;
sfnt = GetResource( 'sfnt', sfnt_id );
if ( ResError() )
return FT_Err_Invalid_Handle;
sfnt_size = (FT_ULong)GetHandleSize( sfnt );
if ( FT_ALLOC( sfnt_data, (FT_Long)sfnt_size ) )
{
ReleaseResource( sfnt );
return error;
}
HLock( sfnt );
ft_memcpy( sfnt_data, *sfnt, sfnt_size );
HUnlock( sfnt );
ReleaseResource( sfnt );
return open_face_from_buffer( library,
sfnt_data,
sfnt_size,
face_index,
"truetype",
aface );
}
static FT_Error
FT_New_Face_From_Suitcase( 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;
res_ref = FSpOpenResFile( spec, fsRdPerm );
if ( ResError() )
return FT_Err_Cannot_Open_Resource;
UseResFile( res_ref );
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;
}
#if TARGET_API_MAC_CARBON
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 );
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
FT_EXPORT_DEF( FT_Error )
FT_New_Face_From_FOND( FT_Library library,
Handle fond,
FT_Long face_index,
FT_Face *aface )
{
short sfnt_id, have_sfnt, have_lwfn = 0;
Str255 lwfn_file_name;
short fond_id;
OSType fond_type;
Str255 fond_name;
FSSpec lwfn_spec;
GetResInfo( fond, &fond_id, &fond_type, fond_name );
if ( ResError() != noErr || fond_type != 'FOND' )
return FT_Err_Invalid_File_Format;
HLock( fond );
parse_fond( *fond, &have_sfnt, &sfnt_id, lwfn_file_name, face_index );
HUnlock( fond );
if ( lwfn_file_name[0] )
{
if ( make_lwfn_spec( fond, lwfn_file_name, &lwfn_spec ) == FT_Err_Ok )
have_lwfn = 1;
else
have_lwfn = 0;
}
if ( have_lwfn && ( !have_sfnt || PREFER_LWFN ) )
return FT_New_Face_From_LWFN( library,
&lwfn_spec,
face_index,
aface );
else if ( have_sfnt )
return FT_New_Face_From_SFNT( library,
sfnt_id,
face_index,
aface );
return FT_Err_Unknown_File_Format;
}
FT_EXPORT_DEF( FT_Error )
FT_GetFile_From_Mac_Name( char* fontName,
FSSpec* pathSpec,
FT_Long* face_index )
{
OptionBits options = kFMUseGlobalScopeOption;
FMFontFamilyIterator famIter;
OSStatus status = FMCreateFontFamilyIterator( NULL, NULL,
options,
&famIter );
FMFont the_font = NULL;
FMFontFamily family = NULL;
*face_index = 0;
while ( status == 0 && !the_font )
{
status = FMGetNextFontFamily( &famIter, &family );
if ( status == 0 )
{
int stat2;
FMFontFamilyInstanceIterator instIter;
Str255 famNameStr;
char famName[256];
FMGetFontFamilyName( family, famNameStr );
CopyPascalStringToC( famNameStr, famName );
FMCreateFontFamilyInstanceIterator( family, &instIter );
*face_index = 0;
stat2 = 0;
while ( stat2 == 0 && !the_font )
{
FMFontStyle style;
FMFontSize size;
FMFont font;
stat2 = FMGetNextFontFamilyInstance( &instIter, &font,
&style, &size );
if ( stat2 == 0 && size == 0 )
{
char fullName[256];
ft_strcpy( fullName, famName );
if ( style & bold )
strcat( fullName, " Bold" );
if ( style & italic )
strcat( fullName, " Italic" );
if ( ft_strcmp( fullName, fontName ) == 0 )
{
the_font = font;
}
else
++(*face_index);
}
}
FMDisposeFontFamilyInstanceIterator( &instIter );
}
}
FMDisposeFontFamilyIterator( &famIter );
if ( the_font )
{
FMGetFontContainer( the_font, pathSpec );
return FT_Err_Ok;
}
else
return FT_Err_Unknown_File_Format;
}
static long
ResourceForkSize(FSSpec* spec)
{
long len;
short refNum;
OSErr e;
e = FSpOpenRF( spec, fsRdPerm, &refNum );
if ( e == noErr )
{
e = GetEOF( refNum, &len );
FSClose( refNum );
}
return ( e == noErr ) ? len : 0;
}
FT_EXPORT_DEF( FT_Error )
FT_New_Face( FT_Library library,
const char* pathname,
FT_Long face_index,
FT_Face *aface )
{
FT_Open_Args args;
FSSpec spec;
OSType file_type;
if ( !pathname )
return FT_Err_Invalid_Argument;
if ( file_spec_from_path( pathname, &spec ) )
return FT_Err_Invalid_Argument;
if ( ResourceForkSize( &spec ) != 0 )
{
file_type = get_file_type( &spec );
if ( file_type == 'FFIL' || file_type == 'tfil' )
return FT_New_Face_From_Suitcase( library, &spec, face_index, aface );
if ( file_type == 'LWFN' )
return FT_New_Face_From_LWFN( library, &spec, face_index, aface );
}
#if TARGET_API_MAC_CARBON
if ( is_dfont( &spec ) )
return FT_New_Face_From_dfont( library, &spec, face_index, aface );
#endif
args.flags = FT_OPEN_PATHNAME;
args.pathname = (char*)pathname;
return FT_Open_Face( library, &args, face_index, aface );
}