/* Copyright (C) 2000-2008 by George Williams */
/*
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:

 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.

 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.

 * The name of the author may not be used to endorse or promote products
 * derived from this software without specific prior written permission.

 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "fontforgevw.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <ustring.h>
#include "ttf.h"
#include "psfont.h"
#ifdef LUA_FF_LIB
#undef __Mac
#endif
#if __Mac
# include <ctype.h>
# include </Developer/Headers/FlatCarbon/Files.h>
#else
# include <utype.h>
#undef __Mac
#define __Mac 0
#endif

const int mac_dpi = 72;
/* I had always assumed that the mac still believed in 72dpi screens, but I */
/*  see that in geneva under OS/9, the pointsize does not match the pixel */
/*  size of the font. But the dpi is not constant (and the differences */
/*  excede those supplied by rounding errors) varying between 96 and 84dpi */

/* A Mac Resource fork */
/*  http://developer.apple.com/techpubs/mac/MoreToolbox/MoreToolbox-9.html */
/*    begins with a 16 byte header containing: */
/*	resource start offset */
/*	map start offset */
/*	resource length */
/*	map length */
/*    then 256-16 bytes of zeros */
/*    the resource section consists of (many) */
/*	4 byte length count */
/*	resource data	*/
/*    the map section contains */
/*	A copy of the 16 byte header */
/*	a 4 byte mac internal value (I hope) */
/*	another 4 bytes of mac internal values (I hope) */
/*	a 2 byte offset from the start of the map section to the list of resource types */
/*	a 2 byte offset from the start of the map section to the list of resource names */
/*	The resource type list consists of */
/*	    a 2 byte count of the number of resource types (-1) */
/*	    (many copies of) */
/*		a 4 byte resource type ('FOND' for example) */
/*		a 2 byte count of the number of resources of this type (-1) */
/*		a 2 byte offset from the type list start to the resource table */
/*	    a resource table looks like */
/*		a 2 byte offset from the resource name table to a pascal */
/*			string containing this resource's name (or 0xffff for none) */
/*		1 byte of resource flags */
/*		3 bytes of offset from the resource section to the length & */
/*			data of this instance of the resource type */
/*		4 bytes of 0 */
/*	The resource name section consists of */
/*	    a bunch of pascal strings (ie. preceded by a length byte) */

/* The POST resource isn't noticeably documented, it's pretty much a */
/*  straight copy of the pfb file cut up into 0x800 byte chunks. */
/*  (each section of the pfb file has it's own set of chunks, the last may be smaller than 0x800) */
/* The NFNT resource http://developer.apple.com/techpubs/mac/Text/Text-250.html */
/* The FOND resource http://developer.apple.com/techpubs/mac/Text/Text-269.html */
/* The sfnt resource is basically a copy of the ttf file */

/* A MacBinary file */
/*  http://www.lazerware.com/formats/macbinary.html */
/*    begins with a 128 byte header */
/*	(which specifies lengths for data/resource forks) */
/*	(and contains mac type/creator data) */
/*	(and other stuff) */
/*	(and finally a crc checksum) */
/*    is followed by the data section (padded to a mult of 128 bytes) */
/*    is followed by the resource section (padded to a mult of 128 bytes) */

/* ******************************** Creation ******************************** */


struct resource {
    uint32 pos;
    uint8 flags;
    uint16 id;
    char *name;
    uint32 nameloc;
    uint32 nameptloc;
};

struct resourcetype {
    uint32 tag;
    struct resource *res;
    uint32 resloc;
};

struct macbinaryheader {
    char *macfilename;
    char *binfilename;		/* if macfilename is null and this is set we will figure out macfilename by removing .bin */
    uint32 type;
    uint32 creator;
};
	    

enum psstyle_flags { psf_bold = 1, psf_italic = 2, psf_outline = 4,
	psf_shadow = 0x8, psf_condense = 0x10, psf_extend = 0x20 };

uint16 _MacStyleCode( char *styles, SplineFont *sf, uint16 *psstylecode ) {
    unsigned short stylecode= 0, psstyle=0;

    if ( strstrmatch( styles, "Bold" ) || strstrmatch(styles,"Demi") ||
	    strstrmatch( styles,"Heav") || strstrmatch(styles,"Blac") ||
/* A few fonts have German/French styles in their names */
	    strstrmatch( styles,"Fett") || strstrmatch(styles,"Gras") ) {
	stylecode = sf_bold;
	psstyle = psf_bold;
    } else if ( sf!=NULL && sf->weight!=NULL &&
	    (strstrmatch( sf->weight, "Bold" ) || strstrmatch(sf->weight,"Demi") ||
	     strstrmatch( sf->weight,"Heav") || strstrmatch(sf->weight,"Blac") ||
	     strstrmatch( sf->weight,"Fett") || strstrmatch(sf->weight,"Gras")) ) {
	stylecode = sf_bold;
	psstyle = psf_bold;
    }
    /* URW uses four leter abbreviations of Italic and Oblique */
    /* Somebody else uses two letter abbrevs */
    if ( (sf!=NULL && sf->italicangle!=0) ||
	    strstrmatch( styles, "Ital" ) ||
	    strstrmatch( styles, "Obli" ) ||
	    strstrmatch(styles, "Slanted") ||
	    strstrmatch(styles, "Kurs") ||
	    strstr( styles,"It" ) ) {
	stylecode |= sf_italic;
	psstyle |= psf_italic;
    }
    if ( strstrmatch( styles, "Underline" ) ) {
	stylecode |= sf_underline;
    }
    if ( strstrmatch( styles, "Outl" ) ) {
	stylecode |= sf_outline;
	psstyle |= psf_outline;
    }
    if ( strstr(styles,"Shadow")!=NULL ) {
	stylecode |= sf_shadow;
	psstyle |= psf_shadow;
    }
    if ( strstrmatch( styles, "Cond" ) || strstr( styles,"Cn") ||
	    strstrmatch( styles, "Narrow") ) {
	stylecode |= sf_condense;
	psstyle |= psf_condense;
    }
    if ( strstrmatch( styles, "Exte" ) || strstr( styles,"Ex") ) {
	stylecode |= sf_extend;
	psstyle |= psf_extend;
    }
    if ( (psstyle&psf_extend) && (psstyle&psf_condense) ) {
	if ( sf!=NULL )
	    LogError( _("Warning: %s(%s) is both extended and condensed. That's impossible.\n"),
		    sf->fontname, sf->origname );
	else
	    LogError( _("Warning: Both extended and condensed. That's impossible.\n") );
	psstyle &= ~psf_extend;
	stylecode &= ~sf_extend;
    }
    if ( psstylecode!=NULL )
	*psstylecode = psstyle;
return( stylecode );
}



/* ******************************** Reading ********************************* */

static SplineFont *SearchPostscriptResources(FILE *f,long rlistpos,int subcnt,long rdata_pos,
	long name_list, int flags) {
    long here = ftell(f);
    long *offsets, lenpos;
    int rname = -1, tmp;
    int ch1, ch2;
    int len, type, i, j, rlen;
    unsigned short id, *rsrcids;
    /* I don't pretend to understand the rational behind the format of a */
    /*  postscript font. It appears to be split up into chunks where the */
    /*  maximum chunk size is 0x800, each section (ascii, binary, ascii, eof) */
    /*  has its own set of chunks (ie chunks don't cross sections) */
    char *buffer=NULL;
    int max = 0;
    FILE *pfb;
    FontDict *fd;
    SplineFont *sf;
    (void)name_list;
    fseek(f,rlistpos,SEEK_SET);
    rsrcids = gcalloc(subcnt,sizeof(short));
    offsets = gcalloc(subcnt,sizeof(long));
    for ( i=0; i<subcnt; ++i ) {
	rsrcids[i] = getushort(f);
	tmp = (short) getushort(f);
	if ( rname==-1 ) rname = tmp;
	/* flags = */ getc(f);
	ch1 = getc(f); ch2 = getc(f);
	offsets[i] = rdata_pos+((ch1<<16)|(ch2<<8)|getc(f));
	/* mbz = */ getlong(f);
    }

    pfb = tmpfile();
    if ( pfb==NULL ) {
	LogError( _("Can't open temporary file for postscript output\n") );
	fseek(f,here,SEEK_SET );
	free(offsets);
return(NULL);
    }

    putc(0x80,pfb);
    putc(1,pfb);
    lenpos = ftell(pfb);
    putc(0,pfb);
    putc(0,pfb);
    putc(0,pfb);
    putc(0,pfb);
    len = 0; type = 1;
    id = 501;
    for ( i=0; i<subcnt; ++i ) {
	for ( j=0; j<subcnt; ++j )
	    if ( rsrcids[j]==id )
		break;
	if ( j == subcnt ) {
	    LogError( _("Missing POST resource %u\n"), id );
	    break;
	}
	id = id + 1;
	fseek(f,offsets[j],SEEK_SET);
	rlen = getlong(f);
	ch1 = getc(f); ch2 = getc(f);
	rlen -= 2;	/* those two bytes don't count as real data */
	if ( ch1==type )
	    len += rlen;
	else {
	    long hold = ftell(pfb);
	    fseek(pfb,lenpos,SEEK_SET);
	    putc(len>>24,pfb);
	    putc((len>>16)&0xff,pfb);
	    putc((len>>8)&0xff,pfb);
	    putc(len&0xff,pfb);
	    fseek(pfb,hold,SEEK_SET);
	    if ( ch1==5 )	/* end of font mark */
    break;
	    putc(0x80,pfb);
	    putc(ch1,pfb);
	    lenpos = ftell(pfb);
	    putc(0,pfb);
	    putc(0,pfb);
	    putc(0,pfb);
	    putc(0,pfb);
	    type = ch1;
	    len = rlen;
	}
	if ( rlen>max ) {
	    free(buffer);
	    max = rlen;
	    if ( max<0x800 ) max = 0x800;
	    buffer=galloc(max);
	    if ( buffer==NULL ) {
		LogError( _("Out of memory\n") );
		exit( 1 );
	    }
	}
	fread(buffer,1,rlen,f);
	fwrite(buffer,1,rlen,pfb);
    }
    free(buffer);
    free(offsets);
	free(rsrcids);
    putc(0x80,pfb);
    putc(3,pfb);
    fseek(pfb,lenpos,SEEK_SET);
    putc(len>>24,pfb);
    putc((len>>16)&0xff,pfb);
    putc((len>>8)&0xff,pfb);
    putc(len&0xff,pfb);
    fseek(f,here,SEEK_SET);
    rewind(pfb);
    if ( flags&ttf_onlynames )
return( (SplineFont *) _NamesReadPostscript(pfb) );	/* This closes the font for us */

    fd = _ReadPSFont(pfb);
    sf = NULL;
    if ( fd!=NULL ) {
	sf = SplineFontFromPSFont(fd);
	PSFontFree(fd);
	/* There is no FOND in a postscript file, so we can't read any kerning*/
    }
    fclose(pfb);
return( sf );
}

static SplineFont *SearchTtfResources(FILE *f,long rlistpos,int subcnt,long rdata_pos,
	long name_list,char *filename,int flags,enum openflags openflags) {
    long here, start = ftell(f);
    long roff;
    int rname = -1;
    int ch1, ch2;
    int len, i, rlen, ilen;
    /* The sfnt resource is just a copy of the ttf file */
    char *buffer=NULL;
    int max = 0;
    FILE *ttf;
    SplineFont *sf;
    int which = 0;
    char **names;
    char *pt,*lparen, *rparen;
    char *chosenname=NULL;
    (void)name_list;
    fseek(f,rlistpos,SEEK_SET);
    if ( subcnt>1 || (flags&ttf_onlynames) ) {
	names = gcalloc(subcnt+1,sizeof(char *));
	for ( i=0; i<subcnt; ++i ) {
	    /* resource id = */ getushort(f);
	    /* rname = (short) */ getushort(f);
	    /* flags = */ getc(f);
	    ch1 = getc(f); ch2 = getc(f);
	    roff = rdata_pos+((ch1<<16)|(ch2<<8)|getc(f));
	    /* mbz = */ getlong(f);
	    here = ftell(f);
	    names[i] = TTFGetFontName(f,roff+4,roff+4);
	    if ( names[i]==NULL ) {
		char buffer[32];
		sprintf( buffer, "Nameless%d", i );
		names[i] = copy(buffer);
	    }
	    fseek(f,here,SEEK_SET);
	}
	if ( flags&ttf_onlynames ) {
return( (SplineFont *) names );
	}
	if ((pt = strrchr(filename,'/'))==NULL ) pt = filename;
	/* Someone gave me a font "Nafees Nastaleeq(Updated).ttf" and complained */
	/*  that ff wouldn't open it */
	/* Now someone will complain about "Nafees(Updated).ttc(fo(ob)ar)" */
	if ( (lparen = strrchr(pt,'('))!=NULL &&
		(rparen = strrchr(lparen,')'))!=NULL &&
		rparen[1]=='\0' ) {
	    char *find = copy(lparen+1);
	    pt = strchr(find,')');
	    if ( pt!=NULL ) *pt='\0';
	    for ( which=subcnt-1; which>=0; --which )
		if ( strcmp(names[which],find)==0 )
	    break;
	    if ( which==-1 ) {
		char *end;
		which = strtol(find,&end,10);
		if ( *end!='\0' )
		    which = -1;
	    }
	    if ( which==-1 ) {
		char *fn = copy(filename);
		fn[lparen-filename] = '\0';
		ff_post_error(_("Not in Collection"),_("%s is not in %.100s"),find,fn);
		free(fn);
	    }
	    free(find);
	} else
	    which = 0;
	if ( lparen==NULL && which!=-1 )
	    chosenname = copy(names[which]);
	for ( i=0; i<subcnt; ++i )
	    free(names[i]);
	free(names);
	fseek(f,rlistpos,SEEK_SET);
    }

    for ( i=0; i<subcnt; ++i ) {
	/* resource id = */ getushort(f);
	rname = (short) getushort(f);
	/* flags = */ getc(f);
	ch1 = getc(f); ch2 = getc(f);
	roff = rdata_pos+((ch1<<16)|(ch2<<8)|getc(f));
	/* mbz = */ getlong(f);
	if ( i!=which )
    continue;
	here = ftell(f);

	ttf = tmpfile();
	if ( ttf==NULL ) {
	    LogError( _("Can't open temporary file for truetype output.\n") );
    continue;
	}

	fseek(f,roff,SEEK_SET);
	ilen = rlen = getlong(f);
	if ( rlen>16*1024 )
	    ilen = 16*1024;
	if ( ilen>max ) {
	    free(buffer);
	    max = ilen;
	    if ( max<0x800 ) max = 0x800;
	    buffer=malloc(max);
	}
	for ( len=0; len<rlen; ) {
	    int temp = ilen;
	    if ( rlen-len<ilen ) temp = rlen-len;
	    temp = fread(buffer,1,temp,f);
	    if ( temp==EOF )
	break;
	    fwrite(buffer,1,temp,ttf);
	    len += temp;
	}
	rewind(ttf);
	sf = _SFReadTTF(ttf,flags,openflags,NULL,NULL);
	fclose(ttf);
	if ( sf!=NULL ) {
	    free(buffer);
	    fseek(f,start,SEEK_SET);
	    if ( sf->chosenname==NULL ) sf->chosenname = chosenname;
return( sf );
	}
	fseek(f,here,SEEK_SET);
    }
    free(chosenname);
    free(buffer);
    fseek(f,start,SEEK_SET);
return( NULL );
}

typedef struct fond {
    char *fondname;
    int first, last;
    int assoc_cnt;
    struct assoc {
	short size, style, id;
    } *assoc;
	/* size==0 => scalable */
	/* style>>8 is the bit depth (0=>1, 1=>2, 2=>4, 3=>8) */
	/* search order for ID is sfnt, NFNT, FONT */
    int stylewidthcnt;
    struct stylewidths {
	short style;
	short *widthtab;		/* 4.12 fixed number with the width specified as a fraction of an em */
    } *stylewidths;
    int stylekerncnt;
    struct stylekerns {
	short style;
	int kernpairs;
	struct kerns {
	    unsigned char ch1, ch2;
	    short offset;		/* 4.12 */
	} *kerns;
    } *stylekerns;
    char *psnames[48];
    struct fond *next;
} FOND;

struct MacFontRec {
   short fontType;
   short firstChar;
   short lastChar;
   short widthMax;
   short kernMax;		/* bb learing */
   short Descent;		/* maximum negative distance below baseline*/
   short fRectWidth;		/* bounding box width */
   short fRectHeight;		/* bounding box height */
   unsigned short *offsetWidths;/* offset to start of offset/width table */
   	/* 0xffff => undefined, else high byte is offset in locTable, */
   	/*  low byte is width */
   short ascent;
   short descent;
   short leading;
   short rowWords;		/* shorts per row */
   unsigned short *fontImage;	/* rowWords*fRectHeight */
   	/* Images for all characters plus one extra for undefined */
   unsigned short *locs;	/* lastchar-firstchar+3 words */
   	/* Horizontal offset to start of n'th character. Note: applies */
   	/*  to each row. Missing characters have same loc as following */
};

static void FondListFree(FOND *list) {
    FOND *next;
    int i;

    while ( list!=NULL ) {
	next = list->next;
	free(list->assoc);
	for ( i=0; i<list->stylewidthcnt; ++i )
	    free(list->stylewidths[i].widthtab);
	free(list->stylewidths);
	for ( i=0; i<list->stylekerncnt; ++i )
	    free(list->stylekerns[i].kerns);
	free(list->stylekerns);
	for ( i=0; i<48; ++i )
	    free(list->psnames[i]);
	free(list);
	list = next;
    }
}

/* There's probably only one fond in the file, but there could be more so be */
/*  prepared... */
/* I want the fond: */
/*  to get the fractional widths for the SWIDTH entry on bdf */
/*  to get the font name */
/*  to get the font association tables */
/*  to get the style flags */
/* http://developer.apple.com/techpubs/mac/Text/Text-269.html */
static FOND *BuildFondList(FILE *f,long rlistpos,int subcnt,long rdata_pos,
	long name_list,int flags) {
    long here, start = ftell(f);
    long offset;
    int rname = -1;
    char name[300];
    int ch1, ch2;
    int i, j, k, cnt, isfixed;
    FOND *head=NULL, *cur;
    long widoff, kernoff, styleoff;

    fseek(f,rlistpos,SEEK_SET);
    for ( i=0; i<subcnt; ++i ) {
	/* resource id = */ getushort(f);
	rname = (short) getushort(f);
	/* flags = */ getc(f);
	ch1 = getc(f); ch2 = getc(f);
	offset = rdata_pos+((ch1<<16)|(ch2<<8)|getc(f));
	/* mbz = */ getlong(f);
	here = ftell(f);

	cur = gcalloc(1,sizeof(FOND));
	cur->next = head;
	head = cur;

	if ( rname!=-1 ) {
	    fseek(f,name_list+rname,SEEK_SET);
	    ch1 = getc(f);
	    fread(name,1,ch1,f);
	    name[ch1] = '\0';
	    cur->fondname = copy(name);
	}

	offset += 4;
	fseek(f,offset,SEEK_SET);
	isfixed = getushort(f)&0x8000?1:0;
	/* family id = */ getushort(f);
	cur->first = getushort(f);
	cur->last = getushort(f);
/* on a 1 point font... */
	/* ascent = */ getushort(f);
	/* descent = (short) */ getushort(f);
	/* leading = */ getushort(f);
	/* widmax = */ getushort(f);
	if ( (widoff = getlong(f))!=0 ) widoff += offset;
	if ( (kernoff = getlong(f))!=0 ) kernoff += offset;
	if ( (styleoff = getlong(f))!=0 ) styleoff += offset;
	for ( j=0; j<9; ++j )
	    getushort(f);
	/* internal & undefined, for international scripts = */ getlong(f);
	/* version = */ getushort(f);
	cur->assoc_cnt = getushort(f)+1;
	cur->assoc = gcalloc(cur->assoc_cnt,sizeof(struct assoc));
	for ( j=0; j<cur->assoc_cnt; ++j ) {
	    cur->assoc[j].size = getushort(f);
	    cur->assoc[j].style = getushort(f);
	    cur->assoc[j].id = getushort(f);
	}
	if ( widoff!=0 ) {
	    fseek(f,widoff,SEEK_SET);
	    cnt = getushort(f)+1;
	    cur->stylewidthcnt = cnt;
	    cur->stylewidths = gcalloc(cnt,sizeof(struct stylewidths));
	    for ( j=0; j<cnt; ++j ) {
		cur->stylewidths[j].style = getushort(f);
		cur->stylewidths[j].widthtab = galloc((cur->last-cur->first+3)*sizeof(short));
		for ( k=cur->first; k<=cur->last+2; ++k )
		    cur->stylewidths[j].widthtab[k] = getushort(f);
	    }
	}
	if ( kernoff!=0 && (flags&ttf_onlykerns) ) {
	    fseek(f,kernoff,SEEK_SET);
	    cnt = getushort(f)+1;
	    cur->stylekerncnt = cnt;
	    cur->stylekerns = gcalloc(cnt,sizeof(struct stylekerns));
	    for ( j=0; j<cnt; ++j ) {
		cur->stylekerns[j].style = getushort(f);
		cur->stylekerns[j].kernpairs = getushort(f);
		cur->stylekerns[j].kerns = galloc(cur->stylekerns[j].kernpairs*sizeof(struct kerns));
		for ( k=0; k<cur->stylekerns[j].kernpairs; ++k ) {
		    cur->stylekerns[j].kerns[k].ch1 = getc(f);
		    cur->stylekerns[j].kerns[k].ch2 = getc(f);
		    cur->stylekerns[j].kerns[k].offset = getushort(f);
		}
	    }
	}
	if ( styleoff!=0 ) {
	    uint8 stringoffsets[48];
	    int strcnt, stringlen, format;
	    char **strings, *pt;
	    fseek(f,styleoff,SEEK_SET);
	    /* class = */ getushort(f);
	    /* glyph encoding offset = */ getlong(f);
	    /* reserved = */ getlong(f);
	    for ( j=0; j<48; ++j )
		stringoffsets[j] = getc(f);
	    strcnt = getushort(f);
	    strings = galloc(strcnt*sizeof(char *));
	    for ( j=0; j<strcnt; ++j ) {
		stringlen = getc(f);
		strings[j] = galloc(stringlen+2);
		strings[j][0] = stringlen;
		strings[j][stringlen+1] = '\0';
		for ( k=0; k<stringlen; ++k )
		    strings[j][k+1] = getc(f);
	    }
	    for ( j=0; j<48; ++j ) {
		for ( k=j-1; k>=0; --k )
		    if ( stringoffsets[j]==stringoffsets[k] )
		break;
		if ( k!=-1 )
	    continue;		/* this style doesn't exist */
		format = stringoffsets[j]-1;
		stringlen = strings[0][0];
		if ( format!=0 )
		    for ( k=0; k<strings[format][0]; ++k )
			stringlen += strings[ strings[format][k+1]-1 ][0];
		pt = cur->psnames[j] = galloc(stringlen+1);
		strcpy(pt,strings[ 0 ]+1);
		pt += strings[ 0 ][0];
		if ( format!=0 )
		    for ( k=0; k<strings[format][0]; ++k ) {
			strcpy(pt,strings[ strings[format][k+1]-1 ]+1);
			pt += strings[ strings[format][k+1]-1 ][0];
		    }
		*pt = '\0';
	    }
	    for ( j=0; j<strcnt; ++j )
		free(strings[j]);
	    free(strings);
	}
	fseek(f,here,SEEK_SET);
    }
    fseek(f,start,SEEK_SET);
return( head );
}

static char *BuildName(char *family,int style) {
    char buffer[350];

    strncpy(buffer,family,200);
    if ( style!=0 )
	strcat(buffer,"-");
    if ( style&sf_bold )
	strcat(buffer,"Bold");
    if ( style&sf_italic )
	strcat(buffer,"Italic");
    if ( style&sf_underline )
	strcat(buffer,"Underline");
    if ( style&sf_outline )
	strcat(buffer,"Outline");
    if ( style&sf_shadow )
	strcat(buffer,"Shadow");
    if ( style&sf_condense )
	strcat(buffer,"Condensed");
    if ( style&sf_extend )
	strcat(buffer,"Extended");
return( copy(buffer));
}

static int GuessStyle(char *fontname,int *styles,int style_cnt) {
    int which, style;
    char *stylenames = _GetModifiers(fontname,NULL,NULL);

    style = _MacStyleCode(stylenames,NULL,NULL);
    for ( which = style_cnt; which>=0; --which )
	if ( styles[which] == style )
return( which );

return( -1 );
}

static FOND *PickFOND(FOND *fondlist,char *filename,char **name, int *style) {
    int i,j;
    FOND *test;
    uint8 stylesused[96];
    char **names;
    FOND **fonds = NULL, *fond = NULL;
    int *styles = NULL;
    int cnt, which;
    char *pt, *lparen;
    char *find = NULL;

    if ((pt = strrchr(filename,'/'))!=NULL ) pt = filename;
    if ( (lparen = strchr(filename,'('))!=NULL && strchr(lparen,')')!=NULL ) {
	find = copy(lparen+1);
	pt = strchr(find,')');
	if ( pt!=NULL ) *pt='\0';
	for ( test=fondlist; test!=NULL; test=test->next ) {
	    for ( i=0; i<48; ++i )
		if ( test->psnames[i]!=NULL && strcmp(find,test->psnames[i])==0 ) {
		    *style = (i&3) | ((i&~3)<<1);	/* PS styles skip underline bit */
		    *name = copy(test->psnames[i]);
return( test );
		}
	}
    }

    /* The file may contain multiple families, and each family may contain */
    /*  multiple styles (and each style may contain multiple sizes, but that's */
    /*  not an issue for us here) */
    names = NULL;
    for ( i=0; i<2; ++i ) {
	cnt = 0;
	for ( test=fondlist; test!=NULL; test=test->next ) if ( test->fondname!=NULL ) {
	    memset(stylesused,0,sizeof(stylesused));
	    for ( j=0; j<test->assoc_cnt; ++j ) {
		if ( test->assoc[j].size!=0 && !stylesused[test->assoc[j].style]) {
		    stylesused[test->assoc[j].style]=true;
		    if ( names!=NULL ) {
			names[cnt] = BuildName(test->fondname,test->assoc[j].style);
			styles[cnt] = test->assoc[j].style;
			fonds[cnt] = test;
		    }
		    ++cnt;
		}
	    }
	}
	if ( names==NULL ) {
	    names = gcalloc(cnt+1,sizeof(char *));
	    fonds = galloc(cnt*sizeof(FOND *));
	    styles = galloc(cnt*sizeof(int));
	}
    }

    if ( find!=NULL ) {
	for ( which=cnt-1; which>=0; --which )
	    if ( strcmp(names[which],find)==0 )
	break;
	if ( which==-1 && strstrmatch(find,test->fondname)!=NULL )
	    which = GuessStyle(find,styles,cnt);
	if ( which==-1 ) {
	    char *fn = copy(filename);
	    fn[lparen-filename] = '\0';
	    ff_post_error(_("Not in Collection"),_("%s is not in %.100s"),find,fn);
	    free(fn);
	}
	free(find);
    } else 
	which = 0;

    if ( which!=-1 ) {
	fond = fonds[which];
	*name = copy(names[which]);
	*style = styles[which];
    }
    for ( i=0; i<cnt; ++i )
	free(names[i]);
    free(names); free(fonds); free(styles);
    if ( which==-1 )
return( NULL );

return( fond );
}


/* Look for kerning info and merge it into the currently existing font "into" */
static SplineFont *FindFamilyStyleKerns(SplineFont *into,EncMap *map,FOND *fondlist,char *filename) {
    char *name;
    int style;
    FOND *fond;
    int i,j;
    int ch1, ch2, offset;
    KernPair *kp;
    SplineChar *sc1, *sc2;

    fond = PickFOND(fondlist,filename,&name,&style);
    if ( fond==NULL || into==NULL )
return( NULL );
    for ( i=0; i<fond->stylekerncnt; ++i )
	if ( fond->stylekerns[i].style==style )
    break;
    if ( i==fond->stylekerncnt ) {
	LogError(_("No kerning table for %s\n"), name );
	free(name);
return( NULL );
    }
    for ( j=0; j<fond->stylekerns[i].kernpairs; ++j ) {
	ch1 = fond->stylekerns[i].kerns[j].ch1;
	ch2 = fond->stylekerns[i].kerns[j].ch2;
	offset = (fond->stylekerns[i].kerns[j].offset*(into->ascent+into->descent)+(1<<11))>>12;
	sc1 = SFMakeChar(into,map,ch1);
	sc2 = SFMakeChar(into,map,ch2);
	for ( kp=sc1->kerns; kp!=NULL; kp=kp->next )
	    if ( kp->sc==sc2 )
	break;
	if ( kp==NULL ) {
	    uint32 script;
	    kp = chunkalloc(sizeof(KernPair));
	    kp->sc = sc2;
	    kp->next = sc1->kerns;
	    sc1->kerns = kp;
	    script = SCScriptFromUnicode(sc1);
	    if ( script==DEFAULT_SCRIPT )
		script = SCScriptFromUnicode(sc2);
	    kp->subtable = SFSubTableFindOrMake(sc1->parent,CHR('k','e','r','n'),
		    script, gpos_pair);
	}
	kp->off = offset;
    }
return( into );
}

/* Look for a bare truetype font in a binhex/macbinary wrapper */
static SplineFont *MightBeTrueType(FILE *binary,int32 pos,int32 dlen,int flags,
	enum openflags openflags) {
    FILE *temp = tmpfile();
    char *buffer = galloc(8192);
    int len;
    SplineFont *sf;

    if ( flags&ttf_onlynames ) {
	char **ret;
	char *temp = TTFGetFontName(binary,pos,pos);
	if ( temp==NULL )
return( NULL );
	ret = galloc(2*sizeof(char *));
	ret[0] = temp;
	ret[1] = NULL;
return( (SplineFont *) ret );
    }

    fseek(binary,pos,SEEK_SET);
    while ( dlen>0 ) {
	len = dlen > 8192 ? 8192 : dlen;
	len = fread(buffer,1,dlen > 8192 ? 8192 : dlen,binary);
	if ( len==0 )
    break;
	fwrite(buffer,1,len,temp);
	dlen -= len;
    }
    rewind(temp);
    sf = _SFReadTTF(temp,flags,openflags,NULL,NULL);
    fclose(temp);
    free(buffer);
return( sf );
}

static SplineFont *IsResourceFork(FILE *f, long offset,char *filename,int flags,
	enum openflags openflags, SplineFont *into,EncMap *map) {
    /* If it is a good resource fork then the first 16 bytes are repeated */
    /*  at the location specified in bytes 4-7 */
    /* We include an offset because if we are looking at a mac binary file */
    /*  the resource fork will actually start somewhere in the middle of the */
    /*  file, not at the beginning */
    unsigned char buffer[16], buffer2[16];
    long rdata_pos, map_pos, type_list, name_list, rpos;
    int32 rdata_len, map_len;
    uint32 nfnt_pos, font_pos, fond_pos;
    unsigned long tag;
    int i, cnt, subcnt, nfnt_subcnt=0, font_subcnt=0, fond_subcnt=0;
    SplineFont *sf;
    FOND *fondlist=NULL;
    fond_pos=0;
    fseek(f,offset,SEEK_SET);
    if ( fread(buffer,1,16,f)!=16 )
return( NULL );
    rdata_pos = offset + ((buffer[0]<<24)|(buffer[1]<<16)|(buffer[2]<<8)|buffer[3]);
    map_pos = offset + ((buffer[4]<<24)|(buffer[5]<<16)|(buffer[6]<<8)|buffer[7]);
    rdata_len = ((buffer[8]<<24)|(buffer[9]<<16)|(buffer[10]<<8)|buffer[11]);
    map_len = ((buffer[12]<<24)|(buffer[13]<<16)|(buffer[14]<<8)|buffer[15]);
    if ( rdata_pos+rdata_len!=map_pos || rdata_len==0 )
return( NULL );
    fseek(f,map_pos,SEEK_SET);
    buffer2[15] = buffer[15]+1;	/* make it be different */
    if ( fread(buffer2,1,16,f)!=16 )
return( NULL );

/* Apple's data fork resources appear to have a bunch of zeroes here instead */
/*  of a copy of the first 16 bytes */
    for ( i=0; i<16; ++i )
	if ( buffer2[i]!=0 )
    break;
    if ( i!=16 ) {
	for ( i=0; i<16; ++i )
	    if ( buffer[i]!=buffer2[i] )
return( NULL );
    }
    getlong(f);		/* skip the handle to the next resource map */
    getushort(f);	/* skip the file resource number */
    getushort(f);	/* skip the attributes */
    type_list = map_pos + getushort(f);
    name_list = map_pos + getushort(f);

    fseek(f,type_list,SEEK_SET);
    cnt = getushort(f)+1;
    for ( i=0; i<cnt; ++i ) {
	tag = getlong(f);
 /* printf( "%c%c%c%c\n", tag>>24, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff );*/
	subcnt = getushort(f)+1;
	rpos = type_list+getushort(f);
	sf = NULL;
	if ( tag==CHR('P','O','S','T') && !(flags&(ttf_onlystrikes|ttf_onlykerns)))		/* No FOND */
	    sf = SearchPostscriptResources(f,rpos,subcnt,rdata_pos,name_list,flags);
	else if ( tag==CHR('s','f','n','t') && !(flags&ttf_onlykerns))
	    sf = SearchTtfResources(f,rpos,subcnt,rdata_pos,name_list,filename,flags,openflags);
	else if ( tag==CHR('N','F','N','T') ) {
	    nfnt_pos = rpos;
	    nfnt_subcnt = subcnt;
	} else if ( tag==CHR('F','O','N','T') ) {
	    font_pos = rpos;
	    font_subcnt = subcnt;
	} else if ( tag==CHR('F','O','N','D') ) {
	    fond_pos = rpos;
	    fond_subcnt = subcnt;
	}
	if ( sf!=NULL )
return( sf );
    }
    if ( flags&ttf_onlynames )			/* Not interested in bitmap resources here */
return( NULL );

    if ( flags&ttf_onlykerns ) {		/* For kerns */
	if ( fond_subcnt!=0 )
	    fondlist = BuildFondList(f,fond_pos,fond_subcnt,rdata_pos,name_list,flags);
	into = FindFamilyStyleKerns(into,map,fondlist,filename);
	FondListFree(fondlist);
return( into );
    }
    /* Ok. If no outline font, try for a bitmap */
    if ( nfnt_subcnt==0 ) {
	nfnt_pos = font_pos;
	nfnt_subcnt = font_subcnt;
    }
return( (SplineFont *) -1 );	/* It's a valid resource file, but just has no fonts */
}


static SplineFont *IsResourceInBinary(FILE *f,char *filename,int flags,
	enum openflags openflags, SplineFont *into,EncMap *map) {
    unsigned char header[128];
    unsigned long offset, dlen, rlen;

    if ( fread(header,1,128,f)!=128 )
return( NULL );
    if ( header[0]!=0 || header[74]!=0 || header[82]!=0 || header[1]<=0 ||
	    header[1]>33 || header[63]!=0 || header[2+header[1]]!=0 )
return( NULL );
    dlen = ((header[0x53]<<24)|(header[0x54]<<16)|(header[0x55]<<8)|header[0x56]);
    rlen = ((header[0x57]<<24)|(header[0x58]<<16)|(header[0x59]<<8)|header[0x5a]);
	/* 128 bytes for header, then the dlen is padded to a 128 byte boundary */
    offset = 128 + ((dlen+127)&~127);
/* Look for a bare truetype font in a binhex/macbinary wrapper */
    if ( dlen!=0 && rlen<=dlen) {
	int pos = ftell(f);
	fread(header,1,4,f);
	header[5] = '\0';
	if ( strcmp((char *) header,"OTTO")==0 || strcmp((char *) header,"true")==0 ||
		strcmp((char *) header,"ttcf")==0 ||
		(header[0]==0 && header[1]==1 && header[2]==0 && header[3]==0))
return( MightBeTrueType(f,pos,dlen,flags,openflags));
    }
return( IsResourceFork(f,offset,filename,flags,openflags,into,map));
}

static int lastch=0, repeat = 0;
static void outchr(FILE *binary, int ch) {
    int i;

    if ( repeat ) {
	if ( ch==0 ) {
	    /* no repeat, output a literal 0x90 (the repeat flag) */
	    lastch=0x90;
	    putc(lastch,binary);
	} else {
	    for ( i=1; i<ch; ++i )
		putc(lastch,binary);
	}
	repeat = 0;
    } else if ( ch==0x90 ) {
	repeat = 1;
    } else {
	putc(ch,binary);
	lastch = ch;
    }
}

static SplineFont *IsResourceInHex(FILE *f,char *filename,int flags,enum openflags openflags,
	SplineFont *into,EncMap *map) {
    /* convert file from 6bit to 8bit */
    /* interesting data is enclosed between two colons */
    FILE *binary = tmpfile();
    char *sixbit = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
    int ch, val, cnt, i, dlen, rlen;
    unsigned char header[20]; char *pt;
    SplineFont *ret;

    if ( binary==NULL ) {
	LogError( _("can't create temporary file\n") );
return( NULL );
    }

    lastch = repeat = 0;
    while ( (ch=getc(f))!=':' );	/* There may be comments before file start */
    cnt = val = 0;
    while ( (ch=getc(f))!=':' ) {
	if ( isspace(ch))
    continue;
	for ( pt=sixbit; *pt!=ch && *pt!='\0'; ++pt );
	if ( *pt=='\0' ) {
	    fclose(binary);
return( NULL );
	}
	val = (val<<6) | (pt-sixbit);
	if ( ++cnt==4 ) {
	    outchr(binary,(val>>16)&0xff);
	    outchr(binary,(val>>8)&0xff);
	    outchr(binary,val&0xff);
	    val = cnt = 0;
	}
    }
    if ( cnt!=0 ) {
	if ( cnt==1 )
	    outchr(binary,val<<2);
	else if ( cnt==2 ) {
	    val<<=4;
	    outchr(binary,(val>>8)&0xff);
	    outchr(binary,val&0xff);
	} else if ( cnt==3 ) {
	    val<<=6;
	    outchr(binary,(val>>16)&0xff);
	    outchr(binary,(val>>8)&0xff);
	    outchr(binary,val&0xff);
	}
    }

    rewind(binary);
    ch = getc(binary);	/* Name length */
    /* skip name */
    for ( i=0; i<ch; ++i )
	getc(binary);
    if ( getc(binary)!='\0' ) {
	fclose(binary);
return( NULL );
    }
    fread(header,1,20,binary);
    dlen = (header[10]<<24)|(header[11]<<16)|(header[12]<<8)|header[13];
    rlen = (header[14]<<24)|(header[15]<<16)|(header[16]<<8)|header[17];
/* Look for a bare truetype font in a binhex/macbinary wrapper */
    if ( dlen!=0 && rlen<dlen ) {
	int pos = ftell(binary);
	fread(header,1,4,binary);
	header[5] = '\0';
	if ( strcmp((char *) header,"OTTO")==0 || strcmp((char *) header,"true")==0 ||
		strcmp((char *) header,"ttcf")==0 ||
		(header[0]==0 && header[1]==1 && header[2]==0 && header[3]==0)) {
	    ret = MightBeTrueType(binary,pos,dlen,flags,openflags);
	    fclose(binary);
return( ret );
	}
    }
    if ( rlen==0 ) {
	fclose(binary);
return( NULL );
    }

    ret = IsResourceFork(binary,ftell(binary)+dlen+2,filename,flags,openflags,into,map);

    fclose(binary);
return( ret );
}

static SplineFont *IsResourceInFile(char *filename,int flags,enum openflags openflags,
	SplineFont *into, EncMap *map) {
    FILE *f;
    char *spt, *pt;
    SplineFont *sf;
    char *temp=filename, *lparen;

    if (( pt=strrchr(filename,'/'))==NULL ) pt = filename;
    if ( (lparen = strchr(pt,'('))!=NULL && strchr(lparen,')')!=NULL ) {
	temp = copy(filename);
	temp[lparen-filename] = '\0';
    }
    f = fopen(temp,"rb");
    if ( temp!=filename ) free(temp);
    if ( f==NULL )
return( NULL );
    spt = strrchr(filename,'/');
    if ( spt==NULL ) spt = filename;
    pt = strrchr(spt,'.');
    if ( pt!=NULL && (pt[1]=='b' || pt[1]=='B') && (pt[2]=='i' || pt[2]=='I') &&
	    (pt[3]=='n' || pt[3]=='N') && (pt[4]=='\0' || pt[4]=='(') ) {
	if ( (sf = IsResourceInBinary(f,filename,flags,openflags,into,map))) {
	    fclose(f);
return( sf );
	}
    } else if ( pt!=NULL && (pt[1]=='h' || pt[1]=='H') && (pt[2]=='q' || pt[2]=='Q') &&
	    (pt[3]=='x' || pt[3]=='X') && (pt[4]=='\0' || pt[4]=='(')) {
	if ( (sf = IsResourceInHex(f,filename,flags,openflags,into,map))) {
	    fclose(f);
return( sf );
	}
    }

    sf = IsResourceFork(f,0,filename,flags,openflags,into,map);
    fclose(f);
#if __Mac
    if ( sf==NULL )
	sf = HasResourceFork(filename,flags,openflags,into,map);
#endif
return( sf );
}

static SplineFont *FindResourceFile(char *filename,int flags,enum openflags openflags,
	SplineFont *into,EncMap *map) {
    char *spt, *pt, *dpt;
    char buffer[1400];
    SplineFont *sf;

    if ( (sf = IsResourceInFile(filename,flags,openflags,into,map)))
return( sf );

    /* Well, look in the resource fork directory (if it exists), the resource */
    /*  fork is placed there in a seperate file on (some) non-Mac disks */
    strcpy(buffer,filename);
    spt = strrchr(buffer,'/');
    if ( spt==NULL ) { spt = buffer; pt = filename; }
    else { ++spt; pt = filename + (spt-buffer); }
    strcpy(spt,"resource.frk/");
    strcat(spt,pt);
    if ( (sf=IsResourceInFile(buffer,flags,openflags,into,map)))
return( sf );

    /* however the resource fork does not appear to do long names properly */
    /*  names are always lower case 8.3, do some simple things to check */
    spt = strrchr(buffer,'/')+1;
    for ( pt=spt; *pt; ++pt )
	if ( isupper( *pt ))
	    *pt = tolower( *pt );
    dpt = strchr(spt,'.');
    if ( dpt==NULL ) dpt = spt+strlen(spt);
    if ( dpt-spt>8 || strlen(dpt)>4 ) {
	char exten[8];
	strncpy(exten,dpt,7);
	exten[4] = '\0';	/* it includes the dot */
	if ( dpt-spt>6 )
	    dpt = spt+6;
	*dpt++ = '~';
	*dpt++ = '1';
	strcpy(dpt,exten);
    }
return( IsResourceInFile(buffer,flags,openflags,into,map));
}

SplineFont *SFReadMacBinary(char *filename,int flags,enum openflags openflags) {
    SplineFont *sf = FindResourceFile(filename,flags,openflags,NULL,NULL);

    if ( sf==NULL )
	LogError( _("Couldn't find a font file named %s\n"), filename );
    else if ( sf==(SplineFont *) (-1) ) {
	LogError( _("%s is a mac resource file but contains no postscript or truetype fonts\n"), filename );
	sf = NULL;
    }
return( sf );
}

char **NamesReadMacBinary(char *filename) {
return( (char **) FindResourceFile(filename,ttf_onlynames,0,NULL,NULL));
}

/* should try to optimize this */
SplineFont *SFReadMacBinaryInfo(char *filename,int flags,enum openflags openflags) {
    SplineFont *sf = FindResourceFile(filename,flags,openflags,NULL,NULL);

    if ( sf==NULL )
	LogError( _("Couldn't find a font file named %s\n"), filename );
    else if ( sf==(SplineFont *) (-1) ) {
	LogError( _("%s is a mac resource file but contains no postscript or truetype fonts\n"), filename );
	sf = NULL;
    }
return( sf );
}
