2000-03-17 12:53:17 +01:00
|
|
|
/***************************************************************************/
|
|
|
|
/* */
|
|
|
|
/* fonddrvr.c */
|
|
|
|
/* */
|
|
|
|
/* Mac FOND font driver. Written by just@letterror.com. */
|
|
|
|
/* */
|
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
|
2000-05-11 20:23:52 +02:00
|
|
|
#include <freetype/internal/ttobjs.h>
|
|
|
|
#include <freetype/internal/t1objs.h>
|
2000-03-17 12:53:17 +01:00
|
|
|
|
|
|
|
#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
|
2000-03-17 14:07:21 +01:00
|
|
|
char* read_type1_data( FT_Memory memory, FSSpec* lwfn_spec, unsigned long *size )
|
2000-03-17 12:53:17 +01:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2000-03-17 14:07:21 +01:00
|
|
|
buffer = memory->alloc( memory, total_size );
|
2000-03-17 12:53:17 +01:00
|
|
|
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;
|
|
|
|
|
2000-03-28 21:41:56 +02:00
|
|
|
*p++ = 0x80;
|
2000-03-17 12:53:17 +01:00
|
|
|
|
|
|
|
code = (*post_data)[0];
|
|
|
|
if ( code == 5 )
|
2000-03-28 21:41:56 +02:00
|
|
|
*p++ = 0x03; /* the end */
|
2000-03-17 12:53:17 +01:00
|
|
|
else if ( code == 2 )
|
2000-03-28 21:41:56 +02:00
|
|
|
*p++ = 0x02; /* binary segment */
|
2000-03-17 12:53:17 +01:00
|
|
|
else
|
2000-03-28 21:41:56 +02:00
|
|
|
*p++ = 0x01; /* ASCII segment */
|
2000-03-17 12:53:17 +01:00
|
|
|
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 )
|
|
|
|
{
|
2000-03-17 14:07:21 +01:00
|
|
|
stream->memory->free( stream->memory, stream->base );
|
2000-03-17 12:53:17 +01:00
|
|
|
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! */
|
|
|
|
|
2000-03-17 14:07:21 +01:00
|
|
|
type1_data = read_type1_data( stream->memory, &lwfn_spec, &size );
|
2000-03-17 12:53:17 +01:00
|
|
|
if ( !type1_data )
|
|
|
|
{
|
|
|
|
return FT_Err_Out_Of_Memory;
|
|
|
|
}
|
|
|
|
|
2000-03-17 14:07:21 +01:00
|
|
|
#if 0
|
2000-03-17 12:53:17 +01:00
|
|
|
{
|
|
|
|
FILE* f;
|
2000-03-28 21:41:56 +02:00
|
|
|
char * path;
|
2000-03-17 12:53:17 +01:00
|
|
|
|
2000-03-28 21:41:56 +02:00
|
|
|
path = p2c_str( lwfn_file_name );
|
|
|
|
strcat( path, ".PFB" );
|
|
|
|
f = fopen(path, "wb");
|
2000-03-17 12:53:17 +01:00
|
|
|
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 */
|