ref: 65a60dc12ea895cbcff06bbf5ef781526fa9492a
parent: 5ae477c4f2569e1136035605cac4a4aa48765806
author: Just van Rossum <[email protected]>
date: Fri Mar 17 06:53:17 EST 2000
Here's the Mac FOND driver! It seems to work well, but it turns out the Type 1 drivers (old as well as new) reject about half the fonts I have.
--- /dev/null
+++ b/src/macfond/fonddrvr.c
@@ -1,0 +1,590 @@
+/***************************************************************************/
+/* */
+/* fonddrvr.c */
+/* */
+/* Mac FOND font driver. Written by [email protected]. */
+/* */
+/* Copyright 1996-2000 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: although the FOND driver sets face->num_faces field to the
+ number of available fonts, but the Type 1 driver sets it to 1 anyway.
+ So this field is currently not reliable, and I don't see a clean way
+ to resolve that. The face_index argument translates to
+ Get1IndResource( 'FOND', face_index + 1 );
+ so clients should figure out the resource index of the FOND.
+ (I'll try to provide some example code for this at some point.)
+
+ The Mac FOND driver 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 it's 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
+ t1_driver->interface.init_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
+ tt_driver->interface.init_face( ... )
+
+ - In both cases, the original stream gets closed and *reinitialized*
+ to become a memory stream. Additionally, the face->driver field --
+ which is set to the FOND driver upon entering our init_face() --
+ gets *reset* to either the TT or the T1 driver. I had to make a minor
+ change to ftobjs.c to make this work.
+
+ - Small note about memory: I use malloc() to allocate the buffer
+ for the Type 1 data. Of course it would be better to do this
+ through the library->memory stuff, but I can't access that from
+ my stream->close() method! Maybe it would be better to use the
+ Mac native NewPtr() and DisposePtr() calls.
+*/
+
+#include <ttobjs.h>
+#include <t1objs.h>
+
+#include <Resources.h>
+#include <Fonts.h>
+#include <Errors.h>
+
+#include <ctype.h> /* for isupper() and isalnum() */
+#include <stdlib.h> /* for malloc() and free() */
+
+
+/* set PREFER_LWFN to 1 if LWFN (Type 1) is preferred over
+ TrueType in case *both* are available */
+#ifndef PREFER_LWFN
+#define PREFER_LWFN 1
+#endif
+
+
+ static
+ FT_Error init_driver( FT_Driver driver )
+ {
+ /* we don't keep no stinkin' state ;-) */
+ return FT_Err_Ok;
+ }
+
+ static
+ FT_Error done_driver( FT_Driver driver )
+ {
+ return FT_Err_Ok;
+ }
+
+
+ /* MacRoman glyph names, needed for FOND kerning support. */
+ /* XXX which is not implemented yet! */
+ static const char* mac_roman_glyph_names[256] = {
+ ".null",
+ };
+
+
+ /* The FOND face object is just a union of TT and T1: both is possible,
+ and we don't need anything else. */
+ typedef union FOND_FaceRec_
+ {
+ TT_FaceRec tt;
+ T1_FaceRec t1;
+ } FOND_FaceRec, *FOND_Face;
+
+
+ /* given a pathname, fill in a File Spec */
+ static
+ int make_file_spec( char* pathname, FSSpec *spec )
+ {
+ Str255 p_path;
+ int path_len;
+
+ /* convert path to a pascal string */
+ path_len = strlen( pathname );
+ if ( path_len > 255 )
+ return -1;
+ p_path[0] = path_len;
+ strncpy( (char*)p_path+1, pathname, path_len );
+
+ if ( FSMakeFSSpec( 0, 0, p_path, spec ) != noErr )
+ return -1;
+ else
+ return 0;
+ }
+
+
+ /* is_suitcase() returns true if the file specified by 'pathname'
+ is a Mac suitcase file, and false if it ain't. */
+ static
+ int is_suitcase( FSSpec *spec )
+ {
+ FInfo finfo;
+
+ if ( FSpGetFInfo( spec, &finfo ) != noErr )
+ return 0;
+ if ( finfo.fdType == 'FFIL' || finfo.fdType == 'tfil' )
+ return 1;
+ else
+ return 0;
+ }
+
+
+ /* Quick 'n' Dirty Pascal string to C string converter. */
+ static
+ char * p2c_str( unsigned char *pstr )
+ {
+ static char cstr[256];
+
+ strncpy( cstr, (char*)pstr+1, pstr[0] );
+ cstr[pstr[0]] = '\0';
+ return cstr;
+ }
+
+
+ /* Given a PostScript font name, create the Macintosh LWFN file name */
+ static
+ void create_lwfn_name( char* ps_name, Str255 lwfn_file_name )
+ {
+ int max = 5, count = 0;
+ unsigned char* p = lwfn_file_name;
+ char* q = ps_name;
+
+ lwfn_file_name[0] = 0;
+
+ while ( *q )
+ {
+ if ( isupper(*q) )
+ {
+ if ( count )
+ max = 3;
+ count = 0;
+ }
+ if ( count < max && (isalnum(*q) || *q == '_' ) )
+ {
+ *++p = *q;
+ lwfn_file_name[0]++;
+ count++;
+ }
+ q++;
+ }
+ }
+
+
+ /* Suck the relevant info out of the FOND data */
+ static
+ FT_Error parse_fond( char* fond_data,
+ short *have_sfnt,
+ short *sfnt_id,
+ Str255 lwfn_file_name )
+ {
+ AsscEntry* assoc;
+ FamRec* fond;
+
+ *sfnt_id = *have_sfnt = 0;
+ lwfn_file_name[0] = 0;
+
+ fond = (FamRec*)fond_data;
+ assoc = (AsscEntry*)(fond_data + sizeof(FamRec) + 2);
+
+ if ( assoc->fontSize == 0 )
+ {
+ *have_sfnt = 1;
+ *sfnt_id = assoc->fontID;
+ }
+
+ if ( fond->ffStylOff )
+ {
+ unsigned char* p = (unsigned char*)fond_data;
+ StyleTable* style;
+ unsigned short string_count;
+ unsigned char* name_table = 0;
+ 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++;
+ }
+ strcpy(ps_name, p2c_str(names[0])); /* Family name */
+
+ if ( style->indexes[0] > 1 )
+ {
+ unsigned char* suffixes = names[style->indexes[0]-1];
+ for ( i=1; i<=suffixes[0]; i++ )
+ strcat( ps_name, p2c_str(names[suffixes[i]-1]) );
+ }
+ create_lwfn_name( ps_name, lwfn_file_name );
+ }
+ return FT_Err_Ok;
+ }
+
+
+ /* Read Type 1 data from the POST resources inside the LWFN file, return a
+ PFB buffer -- apparently FT doesn't like a pure binary T1 stream. */
+ static
+ char* read_type1_data( FSSpec* lwfn_spec, unsigned long *size )
+ {
+ short res_ref, res_id;
+ unsigned char *buffer, *p;
+ unsigned long total_size = 0;
+ Handle post_data;
+
+ res_ref = FSpOpenResFile( lwfn_spec, fsRdPerm );
+ if ( ResError() )
+ return NULL;
+ UseResFile( res_ref );
+
+ /* first pass: load all POST resources, and determine the size of
+ the output buffer */
+ res_id = 501;
+ for (;;)
+ {
+ post_data = Get1Resource( 'POST', res_id++ );
+ if ( post_data == NULL ) break;
+ if ( (*post_data)[0] != 5 )
+ total_size += GetHandleSize( post_data ) + 4;
+ else
+ total_size += 2;
+ }
+
+ buffer = malloc( total_size );
+ if ( !buffer )
+ goto error;
+
+ /* second pass: append all POST data to the buffer, add PFB fields */
+ p = buffer;
+ res_id = 501;
+ for (;;)
+ {
+ long chunk_size;
+ char code;
+
+ post_data = Get1Resource( 'POST', res_id++ );
+ if ( post_data == NULL ) break;
+ chunk_size = GetHandleSize( post_data ) - 2;
+
+ *p++ = 128;
+
+ code = (*post_data)[0];
+ if ( code == 5 )
+ *p++ = 3;
+ else if ( code == 2 )
+ *p++ = 2;
+ else
+ *p++ = 1;
+ if ( code != 5 )
+ {
+ *p++ = chunk_size & 0xFF;
+ *p++ = (chunk_size >> 8) & 0xFF;
+ *p++ = (chunk_size >> 16) & 0xFF;
+ *p++ = (chunk_size >> 24) & 0xFF;
+ }
+ memcpy( p, *post_data + 2, chunk_size );
+ p += chunk_size;
+ }
+
+ CloseResFile( res_ref );
+
+ *size = total_size;
+ return (char*)buffer;
+
+error:
+ CloseResFile( res_ref );
+ return NULL;
+ }
+
+
+ /* Finalizer for the sfnt stream */
+ static
+ void sfnt_stream_close( FT_Stream stream )
+ {
+ Handle sfnt_data = stream->descriptor.pointer;
+ HUnlock( sfnt_data );
+ DisposeHandle( sfnt_data );
+
+ stream->descriptor.pointer = NULL;
+ stream->size = 0;
+ stream->base = 0;
+ stream->close = 0;
+ }
+
+
+ /* Finalizer for the LWFN stream */
+ static
+ void lwfn_stream_close( FT_Stream stream )
+ {
+ free( stream->base );
+ stream->descriptor.pointer = NULL;
+ stream->size = 0;
+ stream->base = 0;
+ stream->close = 0;
+ }
+
+
+ /* Main entry point. Determine whether we're dealing with a Mac
+ suitcase or not; then determine if we're dealing with Type 1
+ or TrueType; delegate the work to the proper driver. */
+ static
+ FT_Error init_face( FT_Stream stream,
+ FT_Long face_index,
+ FT_Face face )
+ {
+ FT_Error err;
+ FSSpec suit_spec, lwfn_spec;
+ short res_ref;
+ Handle fond_data, sfnt_data;
+ short res_index, sfnt_id, have_sfnt;
+ Str255 lwfn_file_name;
+
+ if ( !stream->pathname.pointer )
+ return FT_Err_Invalid_Argument;
+
+ if ( make_file_spec( stream->pathname.pointer, &suit_spec ) )
+ return FT_Err_Invalid_Argument;
+
+ if ( !is_suitcase( &suit_spec ) )
+ return FT_Err_Invalid_File_Format;
+
+ res_ref = FSpOpenResFile( &suit_spec, fsRdPerm );
+ if ( ResError() )
+ return FT_Err_Invalid_File_Format;
+ 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 = face_index + 1;
+ fond_data = Get1IndResource( 'FOND', res_index );
+ if ( ResError() )
+ {
+ CloseResFile( res_ref );
+ return FT_Err_Invalid_File_Format;
+ }
+ /* Set the number of faces. Not that it helps much: the t1 driver
+ just sets it to 1 anyway :-( */
+ face->num_faces = Count1Resources('FOND');
+
+ HLock( fond_data );
+ err = parse_fond( *fond_data, &have_sfnt, &sfnt_id, lwfn_file_name );
+ HUnlock( fond_data );
+ if ( err )
+ {
+ CloseResFile( res_ref );
+ return FT_Err_Invalid_Resource_Handle;
+ }
+
+ if ( lwfn_file_name[0] )
+ {
+ /* We look for the LWFN file in the same directory as the suitcase
+ file. ATM would look in other places, too, but this is the usual
+ situation. */
+ err = FSMakeFSSpec( suit_spec.vRefNum, suit_spec.parID, lwfn_file_name, &lwfn_spec );
+ if ( err != noErr )
+ lwfn_file_name[0] = 0; /* no LWFN file found */
+ }
+
+ if ( lwfn_file_name[0] && ( !have_sfnt || PREFER_LWFN ) )
+ {
+ FT_Driver t1_driver;
+ char* type1_data;
+ unsigned long size;
+
+ CloseResFile( res_ref ); /* XXX still need to read kerning! */
+
+ type1_data = read_type1_data( &lwfn_spec, &size );
+ if ( !type1_data )
+ {
+ return FT_Err_Out_Of_Memory;
+ }
+
+ #if 1
+ {
+ FILE* f;
+
+ f = fopen("Test.PFB", "wb");
+ if ( f )
+ {
+ fwrite( type1_data, 1, size, f );
+ fclose( f );
+ }
+ }
+#endif
+
+ /* reinitialize the stream */
+ if ( stream->close )
+ stream->close( stream );
+ stream->close = lwfn_stream_close;
+ stream->read = 0; /* it's now memory based */
+ stream->base = type1_data;
+ stream->size = size;
+ stream->pos = 0; /* just in case */
+
+ /* delegate the work to the Type 1 driver */
+ t1_driver = FT_Get_Driver( face->driver->library, "type1" );
+ if ( t1_driver )
+ {
+ face->driver = t1_driver;
+ return t1_driver->interface.init_face( stream, 0, face );
+ }
+ else
+ return FT_Err_Invalid_Driver_Handle;
+ }
+ else if ( have_sfnt )
+ {
+ FT_Driver tt_driver;
+
+ sfnt_data = Get1Resource( 'sfnt', sfnt_id );
+ if ( ResError() )
+ {
+ CloseResFile( res_ref );
+ return FT_Err_Invalid_Resource_Handle;
+ }
+ DetachResource( sfnt_data );
+ CloseResFile( res_ref );
+ HLockHi( sfnt_data );
+
+ /* reinitialize the stream */
+ if ( stream->close )
+ stream->close( stream );
+ stream->close = sfnt_stream_close;
+ stream->descriptor.pointer = sfnt_data;
+ stream->read = 0; /* it's now memory based */
+ stream->base = *sfnt_data;
+ stream->size = GetHandleSize( sfnt_data );
+ stream->pos = 0; /* just in case */
+
+ /* delegate the work to the TrueType driver */
+ tt_driver = FT_Get_Driver( face->driver->library, "truetype" );
+ if ( tt_driver )
+ {
+ face->driver = tt_driver;
+ return tt_driver->interface.init_face( stream, 0, face );
+ }
+ else
+ return FT_Err_Invalid_Driver_Handle;
+ }
+ else
+ {
+ CloseResFile( res_ref );
+ }
+ return FT_Err_Invalid_File_Format;
+ }
+
+
+ static
+ void done_face( FOND_Face face )
+ {
+ /*
+ We'll *only* get here if init_face() doesn't succeed,
+ since if it *does* succeed, it has set the face->driver
+ to either the TrueType driver or the Type 1 driver.
+ And since we promise not leave any garbage if init_face()
+ fails, there's nothing left to do.
+ */
+ }
+
+
+ /* The FT_DriverInterface structure is defined in ftdriver.h. */
+
+ const FT_DriverInterface fond_driver_interface =
+ {
+ sizeof ( FT_DriverRec ),
+ sizeof ( FOND_FaceRec ),
+ 0,
+ 0,
+
+ "fond", /* driver name */
+ 100, /* driver version == 1.0 */
+ 200, /* driver requires FreeType 2.0 or above */
+
+ (void*)0,
+
+ (FTDriver_initDriver) init_driver,
+ (FTDriver_doneDriver) done_driver,
+ (FTDriver_getInterface) 0,
+
+ (FTDriver_initFace) init_face,
+ (FTDriver_doneFace) done_face,
+ (FTDriver_getKerning) 0,
+
+ (FTDriver_initSize) 0,
+ (FTDriver_doneSize) 0,
+ (FTDriver_setCharSizes) 0,
+ (FTDriver_setPixelSizes) 0,
+
+ (FTDriver_initGlyphSlot) 0,
+ (FTDriver_doneGlyphSlot) 0,
+ (FTDriver_loadGlyph) 0,
+
+ (FTDriver_getCharIndex) 0,
+ };
+
+
+
+ /*************************************************************************/
+ /* */
+ /* <Function> */
+ /* getDriverInterface */
+ /* */
+ /* <Description> */
+ /* This function is used when compiling the FOND driver as a */
+ /* shared library (`.DLL' or `.so'). It will be used by the */
+ /* high-level library of FreeType to retrieve the address of the */
+ /* driver's generic interface. */
+ /* */
+ /* It shouldn't be implemented in a static build, as each driver must */
+ /* have the same function as an exported entry point. */
+ /* */
+ /* <Return> */
+ /* The address of the TrueType's driver generic interface. The */
+ /* format-specific interface can then be retrieved through the method */
+ /* interface->get_format_interface. */
+ /* */
+#ifdef FT_CONFIG_OPTION_DYNAMIC_DRIVERS
+
+ EXPORT_FUNC
+ FT_DriverInterface* getDriverInterface( void )
+ {
+ return &fond_driver_interface;
+ }
+
+#endif /* CONFIG_OPTION_DYNAMIC_DRIVERS */
+
+
+/* END */