[comp.sources.amiga] Mac font converter

ain@j.cc.purdue.edu (Patrick White) (03/19/88)

Program Name:	fontconv  (part 1 of 1)
Submitted By:	Rico Mariani <oscvax!rico>
Summary:	Converts Macintosh suitcase fonts to Amiga fonts.
Poster Boy:  Pat White  (ain@j.cc.purdue.edu)
Untested.

NOTES:
   I reshared it to separate the sources fromthe binaries.
   The README documentation file is small, and I'm feeling lazy today, so
there is a copy in each shar.
   This is the sources -- didn't have time to try and compile it... the
below notes are from the binaries posting, but I think they are valuable so
I've included them here too.

   Well, this come to ya all after *much* delay (sorry Rico).. seems we had
a corrupt data file and the unpacker tended to GURU every machine we tried
it on -- the moral of this story is make sure your data file is ok.  I think
ours was kermit'ed up but in text mode.. anyway, someone along the way ate
all the 0x0d's in the file.
   We did get it to kinda work, and when it did, we *had* to use the -x
option on the unpacker.
   Rico assures me that it does work, and since getting another copy of
a font to test it with is like pulling teeth arround here (actually harder
and more painful :-), I decided to take Rico's word that it works and post
it.


-- Pat White   (co-moderator comp.sources/binaries.amiga)
UUCP: j.cc.purdue.edu!ain  BITNET: PATWHITE@PURCCVM   PHONE: (317) 743-8421
U.S.  Mail:  320 Brown St. apt. 406,    West Lafayette, IN 47906

========================================

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	README
#	Makefile
#	convert.c
#	unpack.c
#	extract.h
# This archive created: Fri Mar 18 15:21:41 1988
# By:	Patrick White (PUCC Land, USA)
echo shar: extracting README '(3480 characters)'
cat << \SHAR_EOF > README
			Macintosh font conversion package
			---------------------------------

First off, I'd like to thank John O'Neill for writing the Mac suitcase
unpacker and for generally letting me in on what's going on with Mac
fonts.   Thanks John!

Conversion of fonts is a 3 step process:


1) Transfer a Mac font suitcase file to the Amiga using the method of 
   your choice.  The closer you can get to sending the exact unmodified
   binary the happier you'll be.  The fonts that I've converted (yes
   I have test this beast :-) ) we're shipped from the Mac to our
   VAX 750 using FreeTerm and then from the VAX to the Amiga using
   the kermit binary transfer facility in vt100 v2.6.  This works for me.

2) Unpack the suitcase you have just downloaded to the Amiga using the
   'unpack' program.  The -x option forces the program to ignore the
   first 128 bytes, this option is included because the transfer method
   described above caused a 128 byte .info header to be prepended to the
   suitcase data.  This was originally always done as part of the unpacking
   but I turned it into an option just in case...  The output of unpack
   is a zillion files, one for each font at each size in the suitcase.  
   The files are all of the form  Font_Name.pointsize.

3) Convert the raw Mac font resources into Amiga fonts using the 'convert'
   program.  Convert only converts one font at a time... so in practices
   you use you're favourite shell and something like a 

   	foreach i ( *.* ) "convert $i"

   loop.  Convert creates the required .font files, makes the directorys
   required and creates the font executable (did you know that Amiga
   fonts are executable?).  Note:  Mac fonts are named by their point
   size, e.g.  Chicago 12 is a 12 point font.  Amiga fonts are named
   by their pixel height, e.g. Topaz 11 is 11 pixels high.  This means
   that the font will convert into a different size number, e.g.
   Beverly Hills 72 becomes Beverly Hills 84.  Further sometimes there
   are serveral point sizes that end up being the same number of pixels
   high (due to positition of the baseline, leading, etc.) this means
   that two Mac fonts might map onto the same Amiga font size.  Currently
   the convert program just overwrites the old font in that case.

   I recommend you reassign fonts: to somewhere convenient before
   performing the conversion as there will likely be many new fonts
   added to the system and there are only a few Amiga programs that deal
   with many fonts gracefully.  (This is only a problem when running
   software that tries to let you choose your font from a menu or
   a requestor, it isn't a problem with the way the OS deal with fonts).


Permission is granted to freely redistribute any or all of the font
conversion package sources, documents & executeable provided that there is
no charge for such redistribution above a normal disk copying fee or
download/connect time charge.  e.g. it's OK to put these on any BBS,
online service, or disk library; it's not OK to market them for profit.
Any use other than indicated above requires written permission from
both authors.

Please be aware when doing font conversions that many Mac fonts are
copyrighted by various individuals/companies and that doing a conversion
of such a font may be in violation of the laws of your country.  There
are many nice public domain Mac fonts and it will please me no end if
the use of this program is completely limited to those fonts.

	-Rico
SHAR_EOF
if test 3480 -ne "`wc -c README`"
then
echo shar: error transmitting README '(should have been 3480 characters)'
fi
echo shar: extracting Makefile '(131 characters)'
cat << \SHAR_EOF > Makefile

CFLAGS= +L

all: unpack convert

unpack: unpack.o
	ln -o unpack unpack.o -lc32

convert: convert.o
	ln -o convert convert.o -lc32
SHAR_EOF
if test 131 -ne "`wc -c Makefile`"
then
echo shar: error transmitting Makefile '(should have been 131 characters)'
fi
echo shar: extracting convert.c '(6495 characters)'
cat << \SHAR_EOF > convert.c
/*
 * Macintosh font conversion program
 * (C)1987 Rico Mariani
 */

#include <stdio.h>
#include <exec/types.h>
#include <exec/nodes.h>
#include <libraries/diskfont.h>

#define HUNK_HEADER  0x3f3
#define HUNK_CODE    0x3e9
#define HUNK_RELOC32 0x3ec
#define HUNK_END     0x3f2

typedef struct {
	UBYTE FontType;
	UBYTE Padding;
	UWORD FirstChar;
	UWORD LastChar;
	UWORD WidMax;
	WORD  KernMax;
	WORD  NDescent;
	UWORD FRectWidth;
	UWORD FRectHeight;
	UWORD OWTLoc;
	UWORD Ascent;
	UWORD Descent;
	UWORD Leading;
	UWORD RowWords;
} FHEADER;

unsigned char *BitImage;
unsigned short LocTable[260];
unsigned short OWTable[260];
FILE *f;

char *rindex();
void *malloc();

main(argc,argv)
int argc;
char *argv[];
{
	int i,j,c;
	FHEADER h;
	ULONG RowBytes;
	ULONG num_chars, fontName, fontData, fontLoc, fontSpace;
	ULONG fontKern, fontEnd, fontLength;
	ULONG IStart, IEnd, CWidth, OW, Offset, Kern;
	ULONG uc, lock;
	char *s, buf[128];
	WORD entries, size;
	int padbytes;

	if(argc != 2) {
		fprintf(stderr,"Usage: %s fontname\n",argv[0]);
		exit(1);
	}

	s = rindex(argv[1],'.');
	if (!s) {
		fprintf(stderr,"Error fontname must be of the form:\n");
		fprintf(stderr,"\tname.pointsize\n");
		exit(1);
	}

	f = fopen(argv[1],"r");
	if (!f){
		fprintf(stderr,"Error can't open file '%s'\n",argv[1]);
		exit(1);
	}


	fread( &h, sizeof(h), 1, f);

	printf("Font Data for %s:\n",argv[1]);
	*s = '\0'; /* remove the suffix */
	printf("\tChars %d to %d\n",h.FirstChar, h.LastChar);
	printf("\tFont Rectangle (W,H) = (%d,%d)\n" ,
		h.FRectWidth, h.FRectHeight);
	printf("\tAscent = %d,  Descent = %d,  Leading = %d\n",
		h.Ascent, h.Descent, h.Leading);

	RowBytes = 2*h.RowWords;

	BitImage = malloc(h.FRectHeight * RowBytes);
	if (!BitImage) {
		printf("Error, no memory for bit image\n");
		exit(-1);
	}

	fread(BitImage, h.FRectHeight * RowBytes, 1, f);

	for(i = h.FirstChar; i <= h.LastChar+2; i++) {
		uc = fgetc(f);
		LocTable[i] = uc << 8 | fgetc(f);
	}

	for(i = h.FirstChar; i <= h.LastChar+2; i++) {
		uc = fgetc(f);
		OWTable[i] = uc << 8 | fgetc(f);
	}

	fclose(f);

	sprintf(buf,"fonts:%s",argv[1]);
	lock = CreateDir(buf);
	if (lock) UnLock(lock);

	sprintf(buf,"fonts:%s/%d",argv[1],h.FRectHeight);
	f = fopen(buf,"w");
	if (!f) {
		fprintf(stderr,"Error, can't open '%s' for write\n",buf);
		exit(1);
	}

	/* compute offsets into the code hunk */

	num_chars  = h.LastChar - h.FirstChar + 2;
	fontName   = 0x1a;
	fontData   = 0x6e;
	fontLoc    = fontData  + RowBytes * h.FRectHeight;
	fontSpace  = fontLoc   + (num_chars<<2);
	fontKern   = fontSpace + (num_chars<<1);
	fontEnd    = fontKern  + (num_chars<<1);

	/* long word align fontEnd */
	padbytes = 0;
	while (fontEnd & 3) {
		padbytes++;
		fontEnd++;
	}
	fontLength = fontEnd   - 0x3a;	         /* the font: label */


	outL(HUNK_HEADER);
	outL(0);		/* no hunk names */
	outL(1);		/* Size of hunk table */
	outL(0);		/* First Hunk */
	outL(0);		/* Last Hunk */
	outL(fontEnd>>2);	/* Size of Hunk 0 */

	outL(HUNK_CODE);
	outL(fontEnd>>2);	/* Size of this hunk */

	/* now here comes the data for the hunk */
	/* this first part is in case someone tries to run this */

	outW(0x7000);		/* MOVEQ.L #0,D0 */
	outW(0x4e75);		/* RTS */

	outL(0);		/* ln_Succ */
	outL(0);		/* ln_Pred */
	outB(NT_FONT);		/* ln_Type */
	outB(0);		/* ln_Pri  */
	outL(0x1a);		/* ln_Name -- will be relocated */
	outW(DFH_ID);		/* FileID */
	outW(1);		/* Revision */
	outL(0);		/* Segment */

/* fontName: */

	outZ(MAXFONTNAME);	/* output 32 zeros */

/* font: */

	outL(0);		/* ln_Succ */
	outL(0);		/* ln_Pred */
	outB(NT_FONT);		/* ln_Type */
	outB(0);		/* ln_Pri  */
	outL(0x1a);		/* ln_Name -- will be relocated */
	outL(0);		/* mn_ReplyPort */
	outW(fontLength);	/* nm_Length */
	outW(h.FRectHeight);	/* tf_YSize */
	outB(0);		/* tf_Style */
	outB(0x62);		/* tf_Flags */
	outW(h.FRectWidth);	/* tf_XSize */
	outW(h.Ascent);		/* ft_BaseLine */
	outW(1);		/* tf_BoldSmear */
	outW(0);		/* tf_Accessors */
	outB(h.FirstChar);	/* tf_LoChar */
	outB(h.LastChar);	/* tf_HiChar */
	outL(fontData);		/* tf_CharData */
	outW(RowBytes);		/* tf_Modulo */
	outL(fontLoc);		/* tf_CharLoc */
	outL(fontSpace);	/* tf_CharSpace */
	outL(fontKern);		/* tf_CharKern */

/* fontData: */

	fwrite(BitImage, RowBytes * h.FRectHeight, 1, f);

/* fontLoc: */

	/* note that there is an extra character in every font on both  */
	/* that Amiga and the Mac which is used when a character not in */
	/* the font is requested */

	for (i=h.FirstChar; i<= h.LastChar + 1; i++) {
		if (OWTable[i] == -1)
			c = h.LastChar+1;
		else
			c = i;

		IStart = LocTable[c];
		IEnd   = LocTable[c+1];
		outW(IStart);
		outW(IEnd-IStart);
	}

/* fontSpace: */

	for (i=h.FirstChar; i<= h.LastChar+1; i++) {
		OW = OWTable[i];
		if (OWTable[i] == -1) OW = OWTable[h.LastChar+1];

		CWidth = OW & 0xff;
		outW(CWidth);
	}

/* fontKern: */

	for (i=h.FirstChar; i<= h.LastChar+1; i++) {
		OW = OWTable[i];
		if (OWTable[i] == -1) OW = OWTable[h.LastChar+1];

		Offset = OW >> 8;
		Kern = h.KernMax + Offset;
		outW(Kern);

	}

	for (i=0;i<padbytes;i++) outB(0);

/* fontEnd: */

	outL(HUNK_RELOC32);
	outL(6);		/* of relocates number in this hunk */
	outL(0);		/* hunk 0 */
	outL(0x0e);		/* relocate reference to fontName: 1st */
	outL(0x44);		/* relocate reference to fontName: 2nd */
	outL(0x5c);		/* relocate reference to fontData:  */
	outL(0x62);		/* relocate reference to fontLoc:   */
	outL(0x66);		/* relocate reference to fontSpace: */
	outL(0x6a);		/* relocate reference to fontKern:  */
	outL(0);		/* zero to mark end */
	outL(HUNK_END);

	fclose(f);

	sprintf(buf,"fonts:%s.font",argv[1]);
	f = fopen(buf,"r+");
	if (!f) {
		f = fopen(buf,"w+");
		if (!f) {
			fprintf(stderr,"Error, can't create font header\n");
			exit(1);
		}
		outW(0x0f00);	/* font header */
		outW(0x0000);	/* number of font items */
	}

	fseek(f,2,0);

	fread(&entries,2,1,f);

	for (i=0;i<entries;i++) {
		fseek(f, 4+(260*i)+256, 0);
		fread(&size,2,1,f);	/* read in ysize */
		if (size == h.FRectHeight) {
		    printf("Amiga font of this size already exists");
		    printf("... overwritten\n");
		    exit(1);
		}
	}

	fseek(f,2,0);
	outW(++entries);
	fseek(f,0,2);
	sprintf(buf,"%s/%d",argv[1],h.FRectHeight);
	fwrite(buf,strlen(buf),1,f);
	for (i=strlen(buf);i<256;i++) fputc(0,f);
	outW(h.FRectHeight);
	outB(0);
	outB(0x62);
	fclose(f);
}

outB(b)
{
	fputc(b,f);
}

outW(w)
{
	fputc(w>>8,f);
	fputc(w&0xff,f);
}

outL(l)
{
	fwrite(&l,4,1,f);
}

outZ(n)
{
	int i;

	for (i=n;--i>=0;) fputc(0,f);
}
SHAR_EOF
if test 6495 -ne "`wc -c convert.c`"
then
echo shar: error transmitting convert.c '(should have been 6495 characters)'
fi
echo shar: extracting unpack.c '(4542 characters)'
cat << \SHAR_EOF > unpack.c
/*
 * Macintosh font suitcase unpacker by John O'Neill
 *
 * Amiga modifications by Rico Mariani
 *
 * The -x flag is used to skip the 128 info section that is placed
 * at the head of the file when the suitcase is uploaded from
 * the Mac by some versions of xmodem.
 *
 * (C)1987 John O'Neill & Rico Mariani
 *
 */

#include <exec/types.h>
#include "extract.h"
#include <stdio.h>

void *malloc();
void *PtoC();
int debug = 1;

main(argc,argv)
int argc;
char **argv;
{
	unsigned char *res, rheader[16];
	int i, j, c;
	int Data_loc, Map_loc, Data_length, Map_length, Res_length;
	int Type_List_loc, Name_List_loc;
	int Num_Res_Types, Type_loc, Num_Fonts, Ref_List_loc;
	int Ref_loc, Res_ID, temp, Res_Name_loc, Res_Attributes;
	int Res_Data_loc;
	int Font_ID, Point_Size, Font_Num, Font_Size;
	unsigned char **Font_Name;
	unsigned char File_Name[32];
	FILE *f,*fontfile;
	int skip = 0;


	/* Start up */

	if (argc < 2 || argc > 4) {
		fprintf(stderr,"Usage: %s [-x] FontFile\n",argv[0]);
		exit(1);
	}

	if (!strcmp(argv[1],"-x"))
		skip = 1;

	f = fopen(argv[1+skip],"r");
	if (!f) {
		fprintf(stderr,"%s: Cannot open %s\n",argv[0],argv[1+skip]);
		exit(1);
	}


	/* Skip .info file sent by XMODEM */

	if (skip) for(i = 0; i < 128; i++) fgetc(f);

	/* Get resource file header */

	fread( rheader, sizeof(rheader), 1, f);

	Data_loc    = X_LONG(rheader + 0);
	Map_loc	    = X_LONG(rheader + 4);
	Data_length = X_LONG(rheader + 8);
	Map_length  = X_LONG(rheader + 12);
	Res_length  = Data_length + Map_length + 256;

	/* allocate memory for the rest of the resource */
	res = malloc(Res_length);

	/* read in everything but the header */
	fread( res+16, Res_length-16, 1, f );
	fclose(f);

	/* Look for FONT resources in the resource map */

	Type_List_loc = Map_loc + X_UWORD(res + Map_loc + 24);
	Name_List_loc = Map_loc + X_UWORD(res + Map_loc + 26);

	Num_Res_Types = 1 + X_UWORD(res + Type_List_loc);

	printf("There are %d Res Types in the file\n", Num_Res_Types);

	for(i = 0; i < Num_Res_Types; i++) {
		Type_loc = Type_List_loc + i*10 + 2;
		if (X_ULONG(res + Type_loc) == 'FONT') {
			Num_Fonts = 1 + X_UWORD(res+Type_loc+4);
			Ref_List_loc = Type_List_loc +
				X_UWORD(res + Type_loc + 6);
			printf("Found FONT rsrc with %d fonts\n", Num_Fonts);

		}
	}

	/* Get the FONT names first: this is the name of the Resources
	   which have ResId a multiple of 128 */

	Font_Num = 1;
	/* need one pointer for each possible font id */
	Font_Name = malloc(sizeof(char *) * 256);
	if (!Font_Name) {
		printf("Error, no memory for font name pointer array\n");
		exit(-1);
	}

	for(i = 0; i < Num_Fonts; i++) {
		Ref_loc    = Ref_List_loc + 12*i;
		Res_ID     = X_UWORD(res + Ref_loc);
		Font_ID    = Res_ID >> 7;
		Point_Size = Res_ID & 0x7f;

		if (Point_Size == 0) {
			temp = X_UWORD(res + Ref_loc + 2);

			if (temp == 0xffff) {
				Font_Name[Font_ID] = malloc(10);
				if (!Font_Name[Font_ID]) {
				    printf("Error, no memory for name\n");
				    exit(-1);
				}

				sprintf(Font_Name[Font_ID],"FONT%d",
					Font_Num );

				Font_Num++;
			}
			else {
				Res_Name_loc = Name_List_loc + temp;
				Font_Name[Font_ID] = PtoC(res+Res_Name_loc);
			}
		}
	}

	/* Get the info on each of the FONT resources found */

	for(i = 0; i < Num_Fonts; i++) {
		Ref_loc = Ref_List_loc + 12*i;
		Res_ID  = X_UWORD(res + Ref_loc );
		Font_ID = Res_ID >> 7;
		Point_Size = Res_ID & 0x7f;

		temp 	       = X_UWORD(res + Ref_loc + 2);
		Res_Name_loc   = (temp == 0xffff) ? -1 : Name_List_loc+temp;
		Res_Attributes = res[Ref_loc + 4];
		Res_Data_loc   = Data_loc + X_UTRIP(res + Ref_loc + 5);
		Font_Size      = X_ULONG( res + Res_Data_loc );

		if (Point_Size != 0) {
			for(j = 0;; j++) {
				c = Font_Name[Font_ID][j];
				if (c == ' ') c = '_';
				if (c != 0)
					File_Name[j] = c;
				else
					break;
			}

			File_Name[j] = '.';
			j++;
			sprintf(&File_Name[j],"%d",Point_Size);

			printf("Font:%s %d -> File:%s  (Size = %d)\n",
				Font_Name[Font_ID],Point_Size,File_Name,
				Font_Size);

			fontfile = fopen(File_Name,"w");
			if (!fontfile) {
				fprintf(stderr,"%s: Cannot open %s\n",
					argv[0],File_Name);
				exit(1);
			}

			fwrite( res+Res_Data_loc+4, Font_Size, 1, fontfile);
			fclose(fontfile);
		}
	}
}

void *PtoC(s1)
register unsigned char *s1;
{
	register unsigned char *s2;
	register int i;
	int len;
	unsigned char *s;

	len = *s1++;

	s = s2 = malloc(len + 1);
	if (!s2) {
		printf("Error, not enough memory to make C string\n");
		exit(-1);
	}

	for (i= len; --i>=0; ) *s2++ = *s1++;

	*s2 = 0;
	return(s);
}
SHAR_EOF
if test 4542 -ne "`wc -c unpack.c`"
then
echo shar: error transmitting unpack.c '(should have been 4542 characters)'
fi
echo shar: extracting extract.h '(1166 characters)'
cat << \SHAR_EOF > extract.h
/* These macros are for extracting an integer of a certain type
 * from the midst of an arbitrary chunk of memory.
 *
 * Due to the offset based Macintosh resource system you have to
 * do this a lot...
 *
 */

#define UC(p) ((unsigned char *)(p))

#ifdef MPU68000

/* this will work on any machine with 68000 style endain ordering */
/* p should be a pointer to char */

#define X_ULONG(p) (*((ULONG *)(p)))
#define X_UWORD(p) (*((UWORD *)(p)))
#define X_UBYTE(p) (*((UBYTE *)(p)))

#define X_LONG(p) (*((LONG *)(p)))
#define X_WORD(p) (*((WORD *)(p)))
#define X_BYTE(p) (*((BYTE *)(p)))

#else

/* this will work on any machine but it's not as fast as the above */


#define X_ULONG(p) (ULONG)(UC(p)[0]<<24|UC(p)[1]<<16|UC(p)[2]<<8|UC(p)[3])
#define X_UWORD(p) (UWORD)(UC(p)[0]<<8|UC(p)[1])
#define X_UBYTE(p) (UBYTE)(UC(p)[0])
#define X_LONG(p)  (LONG)((UC(p)[0]<<24|UC(p)[1]<<16|UC(p)[2]<<8|UC(p)[3])
#define X_WORD(p)  (WORD)(UC(p)[0]<<8|UC(p)[1])
#define X_BYTE(p)  (BYTE)(UC(p)[0])

#endif

/* artificial types... have to fake them */

#define X_UTRIP(p) (ULONG)(UC(p)[0]<<16|UC(p)[1]<<8|UC(p)[2])
#define X_TRIP(p)  (LONG)(UC(p)[0]<<16|UC(p)[1]<<8|UC(p)[2])
SHAR_EOF
if test 1166 -ne "`wc -c extract.h`"
then
echo shar: error transmitting extract.h '(should have been 1166 characters)'
fi
#	End of shell archive
exit 0