[comp.sys.atari.st] VFont to GDOS font conversion utility

rwa@auvax.UUCP (Ross Alexander) (11/22/87)

Many apologies to Mr Turner, whom I cannot seem to reach via email; here's
the code I kept threatening to post ;-). [ I really would prefer to do this
the right way, but its been 6 weeks now...]

The following program attempts to convert Berkeley "Varian Fonts" (vfonts)
to Atari GDOS fonts, with middling success.  It is written in MWC (I use
version 2.0, but I know of no reason why it wouldn't go together in version
1.2 just as easily).  You Lattice and/or Megamax users are welcome to see
what can be done with this stuff; I can't do it myself.  I suspect Alcyon
owners are SOL.

I hereby place this code in the public domain; do whatever you like
with it, attribute it to me or not as you please, et c., et c.  I
make no representation of fitness for any purpose, neither will I be
held responsible for any damage direct or indirect, or any claims
arising from the use of this code in any way.

In a word it's a gift and you shouldn't expect too much from it ;-).
Enjoy.  

Ross Alexander @ Athabasca University,
alberta!auvax!rwa

--------------------------makefile----------------------------
CFLAGS=-A

vtoa.prg: vtoa.o
	cc -o vtoa.prg vtoa.o

vtoa.o:	\include\stdio.h \include\stat.h \include\ctype.h

clean:
	rm *.o vtoa.prg
--------------------------C source---------------------------
/*
 *-----------------------------------------------------------------------------
 *	Vfont - to - Atari font conversion utility
 *
 *	vtoa [-a] [-d] [-v] <infile> <outfile>
 *
 *	-a:  generate Atari (MC68000) ordered font; defaults to Intel order
 *	-d:  generate char info dump on stdout
 *	-v:  generate char map output on stdout
 *
 *	Beyond all the bl**dy byte-sex problems, this is pretty straight-
 *	forward code.  I refer you to /usr/man/man5/vfont.5 and Appendix G,
 *	D.R. VDI Programmers Guide for specifics.
 *-----------------------------------------------------------------------------
 */

#include <stdio.h>
#include <stat.h>
#include <ctype.h>

extern char * malloc();			/* mem allocator function */

#define	VGLYPHS		256		/* how many glyphs in a vfont file */
#define	VMAGIC		0x011E		/* magic at start of vfont file */
#define	VCIGAM		0x1E01		/* what it looks like to a vax :-) */

int	sflag = 0;			/* input byte-swapping flag */
int	aflag = 0;			/* output byte-swapping flag */
int	dflag = 0;			/* dump flag */
int	vflag = 0;			/* verbose dump flag */

struct	v_header {			/* Bezerkely vfont file header */
	short		magic;		/* file id 'magic number' == VMAGIC */
	unsigned short	size;		/* bytes in the glyphs' bitmaps */
	short		maxx;		/* pixels wide, widest glyph */
	short		maxy;		/* pixels high, highest glyph */
	short		xtnd;		/* spare field for future expansion */
};

struct	v_dispatch {			/* descriptor entry for a glyph */
	unsigned short	addr;		/* offset from start of glyphs */
	short		nbytes;		/* how many bytes in the bitmap */
	char		up;		/* pixels above basepoint */
	char		down;		/* pixels below basepoint */
	char		left;		/* pixels to left of basepoint */
	char		right;		/* pixels to right of basepoint */
	short		width;		/* offset to next glyph basepoint */
};

/*
 *-----------------------------------------------------------------------------
 * Read a Bezerkely vfont file into internal datastructures.  Try to be clever.
 *-----------------------------------------------------------------------------
 */

struct v_header * v_info;
struct v_dispatch * v_glyph;
char * v_bitmap[ VGLYPHS ];

char * read_vfont( name ) char * name; {
    struct stat statbuf;
    int handle;
    int t, max_x, max_y, high, wide;

    /*
     * make sure the input is a real file and not a directory
     */
    if ( stat( name, & statbuf ) == -1 )
	return "file not found";
    if ( ( statbuf.st_mode & ( S_IJVOL | S_IJDIR ) ) != 0 )
	return "not a file";

    /*
     * allocate a chunk of ram big enough to hold the whole file
     */
    if ( ( v_info = (struct v_header *)
    		malloc( (unsigned int) statbuf.st_size ) )
	    == (struct v_header *) NULL )
	return "not enough memory";

    /*
     * suck in the file and close it
     */
    if ( ( handle = open( name, 0 ) ) == -1 )
	return "open failed";
    if ( read( handle, (char *) v_info, (int) statbuf.st_size )
	    != (int) statbuf.st_size )
	return "read failed";
    close( handle );

    /*
     * establish the byte sex by examining the 'magic number'.
     * then flip words as appropriate, in the header and the
     * glyph descriptor array.
     */
    sflag = v_info->magic == VCIGAM;
    if ( v_info->magic != VMAGIC && v_info->magic != VCIGAM )
	return "bad magic number";

    if ( sflag )			/* need to swap some bytes... */
	swab( (char *) v_info,
	      (char *) v_info,		/* info struct is q&d */
	      sizeof (struct v_header) );

    v_glyph = (struct v_dispatch *)
	( (char *) v_info + sizeof (struct v_dispatch) );

    if ( sflag )			/* need to swap some bytes... */
	for ( t = 0; t < VGLYPHS; ++ t ) {
	    swab( (char *) & v_glyph[ t ].addr,
		  (char *) & v_glyph[ t ].addr,
		  2 * sizeof (short int) );
	    swab( (char *) & v_glyph[ t ].width,
		  (char *) & v_glyph[ t ].width,
		  sizeof (short int) );
	}

    /*
     * build the edge vector to point at the glyph bitmaps.  this isn't
     * really essential, but makes other parts of the code simpler.
     */
    for ( t = 0; t < VGLYPHS; ++ t )
	v_bitmap[ t ] = v_glyph[ t ].nbytes
	    ? (char *) ( v_glyph + VGLYPHS ) + v_glyph[ t ].addr
	    : (char *) NULL;

    /*
     * run a consistency check: see that the header's idea of the
     * largest cell and the descriptor's idea of the largest cell
     * agree.  the manual entry for the data structure is a little
     * vague on whether they really do or not.
     */
    for ( max_x = max_y = t = 0; t < VGLYPHS; ++ t )
	if ( v_glyph[ t ].nbytes ) {
	    high = v_glyph[ t ].up + v_glyph[ t ].down;
	    if ( high > max_y )
		max_y = high;
	    wide = v_glyph[ t ].left + v_glyph[ t ].right;
	    if ( wide > max_x )
		max_x = wide;
	}

    v_info->maxx = max_x;
    v_info->maxy = max_y;

    if ( dflag )
	fprintf( stderr,
		 "max x = %d, max y = %d\n",
		 v_info->maxx,
		 v_info->maxy );

    return (char *) NULL;
}

/*
 *-----------------------------------------------------------------------------
 * utility function to test a bit in a vfont glyph image. PFM technique code.
 *-----------------------------------------------------------------------------
 */

int test_v_bit( glyph, x, y ) int glyph, x, y; {
    register int wide;
    register char * pix_base;

    wide = ( v_glyph[ glyph ].right + v_glyph[ glyph ].left + 7 ) >> 3;
    pix_base = v_bitmap[ glyph ] + y * wide;
    return ( * ( pix_base + ( x >> 3 ) ) & ( 0x80 >> ( x & 7 ) ) ) != 0;
}

/*
 *-----------------------------------------------------------------------------
 * Dump out info on a vfont file.  Never trust the documentation :-)
 *-----------------------------------------------------------------------------
 */

void dump_vfont() {
    int t;

    fprintf( stderr,
	 "Header: @ %08lx = magic = %04x, size = %d, maxx= %d, maxy = %d\n",
	 v_info,
	 v_info->magic,
	 v_info->size,
	 v_info->maxx,
	 v_info->maxy );

    for ( t = 0; t < VGLYPHS; ++ t )
	if ( v_glyph[ t ].nbytes ) {
	    fprintf( stderr,
		     "%03o %c @ %08lx : nbytes = %d, ",
		     t,
		     isprint( t ) ? t : ' ',
		     v_bitmap[ t ],
		     v_glyph[ t ].nbytes );
	    fprintf( stderr,
		     "u = %d, d = %d, l = %d, r = %d, w = %d\n",
		     v_glyph[ t ].up,
		     v_glyph[ t ].down,
		     v_glyph[ t ].right,
		     v_glyph[ t ].left,
		     v_glyph[ t ].width );

	    if ( vflag ) {
		/*
		 * this code drops out 'tty' plots of the glyph images,
		 * and was written mainly to confirm that I understood the
		 * internal representation of the glyphs correctly
		 */
		int x, y, high, wide, until_base;

		wide = v_glyph[ t ].right + v_glyph[ t ].left;
		high = v_glyph[ t ].up + v_glyph[ t ].down;
		until_base = v_glyph[ t ].up * wide + v_glyph[ t ].left;

		for ( y = 0; y < high; ++ y ) {
		    for ( x = 0; x < wide; ++ x )
			fprintf( stderr, "%c%c",
				 test_v_bit( t, x, y ) ? '@' : ' ',
				  -- until_base ? ' ' : '.' );
		    fprintf( stderr, "\n" );
		}
	    }
	}
}

/*
 *-----------------------------------------------------------------------------
 * take the bezerkley stuff and build one of ours from it.
 *-----------------------------------------------------------------------------
 */

struct	font_hdr {			/* atari font header */
	int		id;		/* id for vst_font() */
	int		size;		/* font size in points */
	char		facename[32];	/* face name for vqt_name() */
	int		ADE_lo;		/* lowest ADE in the font */
	int		ADE_hi;		/* highest ADE in the font */
	int		top_dist;	/* top line distance */
	int		asc_dist;	/* ascent line distance */
	int		hlf_dist;	/* half line distance */
	int		des_dist;	/* descent line distance */
	int		bot_dist;	/* bottom line distance */
	int		wchr_wdt;	/* width of widest char */
	int		wcel_wdt;	/* width of widest cell */
	int		lft_ofst;	/* left offset */
	int		rgt_ofst;	/* right offset */
	int		thckning;	/* pixels to thicken by */
	int		undrline;	/* width of underline */
	int		lghtng_m;	/* fading mask */
	int		skewng_m;	/* skewing mask */
	int		flags;		/* default|horz offset|byte sex|mono */
	char		*hz_ofst;	/* horizontal offset table */
	int		*ch_ofst;	/* char offset table */
	char		*fnt_dta;	/* font table proper */
	int		frm_wdt;	/* form width */
	int		frm_hgt;	/* and height */
	char		*nxt_fnt;	/* link to next font */
};

struct font_hdr * a_info;		/* global data block for atari */
char * a_glyph;				/* place for the form block */
int * a_offset;				/* place for char pixel offsets */

/*
 *-----------------------------------------------------------------------------
 * set a bit in the atari charset form to a specified value.
 *-----------------------------------------------------------------------------
 */

void set_a_bit( glyph, x, y, to ) int glyph, x, y, to; {
    register int form_x_bit, form_x_byte;

    form_x_bit  =   ( a_offset[ glyph ] + x )  & 7;
    form_x_byte = ( ( a_offset[ glyph ] + x ) >> 3 ) + a_info->frm_wdt * y;

    if ( to ) a_glyph[ form_x_byte ] |= 0x0080 >> form_x_bit;
    else      a_glyph[ form_x_byte ] &= 0xFF7F >> form_x_bit;
}

/*
 *-----------------------------------------------------------------------------
 * another utility function, to swap words analgous to swab()
 *-----------------------------------------------------------------------------
 */

void swaw( f, t, wc ) int * f, * t, wc; {
    register int tmp;

    do {
	tmp = f[ 0 ];
	t[ 0 ] = f[ 1 ];
	t[ 1 ] = tmp;
	f += 2;
	t += 2;
    } while ( ( wc -= 2 ) > 0 );
}

/*
 *-----------------------------------------------------------------------------
 * build an atari font description and write it out to a file.  lots of
 * guessing and patching to slide over the empty places in the doc :-).
 *-----------------------------------------------------------------------------
 */

char * write_afont( name ) char * name; {
    int t, pix_wide, pix_above, pix_below, handle;

    /*
     * begin by allocating ram for font global information
     */
    if ( ( a_info = (struct font_hdr *)
	    malloc( (unsigned) sizeof (struct font_hdr) ) )
	    == (struct font_hdr *) NULL )
	return "can't allocate header";

    /* allocate and zero out the pixel-offset array */
    if ( ( a_offset = (int *)
		malloc( (unsigned) sizeof (int) * ( VGLYPHS + 1 ) ) )
	    == (int *) NULL )
	return "can't allocate offset vector";
    for ( t = 0; t < VGLYPHS; ++ t )
	a_offset[ t ] = 0;

    /*
     * this stuff needs to be set up with a tool like GEMFED or
     * whatever; there's not enough information available to me
     * to do it now.
     */
    a_info->id = 0;
    a_info->size = v_info->maxy;	/* this is all 'best-guess' */
    strcpy( a_info->facename, name );

    /*
     * figure out the low and high ADE values
     */
    for ( t = 0; t < VGLYPHS; ++ t )
	if ( v_glyph[ t ].nbytes ) break;
    a_info->ADE_lo = t;
    for ( t = VGLYPHS; t --; )
	if ( v_glyph[ t ].nbytes ) break;
    a_info->ADE_hi = t;

    /*
     * scan through the descriptor list, collecting three pieces of
     * information:  total width of the 'form', and the maximum number
     * of scan lines above and below the base line.  From this we can
     * figure out how much memory to allocate for the form array.
     * the descision to allocate 8 pixel-wide cells to null chars is
     * entirely arbitrary, but it had to be made :-).  while doing this,
     * set up the pixel-offset values in a_offset.
     */
    pix_wide = pix_above = pix_below = 0;
    for ( t = a_info->ADE_lo; t <= a_info->ADE_hi; ++ t ) {
	a_offset[ t ] = pix_wide;
	if ( v_glyph[ t ].nbytes ) {
	    pix_wide += v_glyph[ t ].left + v_glyph[ t ].right;
	    if ( pix_above < v_glyph[ t ].up )
		pix_above = v_glyph[ t ].up;
	    if ( pix_below < v_glyph[ t ].down )
		pix_below = v_glyph[ t ].down;
	}
	else
	    pix_wide += 8;
    }
    a_offset[ t ] = pix_wide;		/* to establish width of last char */

    /*
     * allocate the form array, handling the pad-to-word-length requirement
     * and including enough scan lines for the tallest and lowest char cells
     */
    a_info->frm_wdt = ( ( pix_wide + 15 ) >> 4 ) << 1;    
    a_info->frm_hgt = pix_above + pix_below;
    if ( ( a_glyph = malloc( (unsigned) a_info->frm_wdt * a_info->frm_hgt ) )
	    == (char *) NULL )
	return "can't allocate form";

    /*
     * make further guesses as to the values of stuff; use GEMFED to
     * clean this up, or else ignore the d*mned things.
     */
    if ( dflag )
	fprintf( stderr, "above = %d, below = %d\n", pix_above, pix_below );

    a_info->top_dist = pix_above;
    a_info->asc_dist = pix_above - 1;
    a_info->hlf_dist = pix_above / 2;
    a_info->des_dist = pix_below - 1;
    a_info->bot_dist = pix_below;

    /*
     * describe maximum cell and char.
     */
    a_info->wchr_wdt = a_info->wcel_wdt = v_info->maxx;
    a_info->lft_ofst = a_info->rgt_ofst = 1;

    /*
     * just keep on guessing...
     */
    a_info->thckning = a_info->undrline = 1;
    a_info->lghtng_m = a_info->skewng_m = 0x5555;

    /*
     * for now, flags == 4 (intel format, no other options)
     */
    a_info->flags = 4;

    /*
     * set up both offset tables just past the global info structure
     */
    a_info->hz_ofst = (char *) NULL;
    a_info->ch_ofst = (int *) sizeof (struct font_hdr);

    /*
     * this field is required to be null
     */
    a_info->nxt_fnt = (char *) NULL;

    /*
     * and now la piece de resistance: insert the glyph info into the
     * atari form.  Remember that characters in the form do not observe
     * any byte alignment, and also that Intel word order may be enforced
     * later (blechh).
     */
    for ( t = a_info->ADE_lo; t <= a_info->ADE_hi; ++ t )
	if ( v_glyph[ t ].nbytes ) {
	    register int x, y;
	    int wide, high;

	    high = v_glyph[ t ].up + v_glyph[ t ].down;
	    wide = v_glyph[ t ].left + v_glyph[ t ].right;
	    for ( x = 0; x < wide; ++ x )
		for ( y = 0; y < high; ++ y )
		    set_a_bit( t, x, y, test_v_bit( t, x, y ) );
	}

    /*
     * now the great tediousness of writing all this stuff back
     * figure out offset to font form data, etc, flip bytes, etc.
     * note also that the byte flipping means _we_ can't read the
     * d*mned info structs anymore, hence the caching of the ADE
     * values :-(
     */
    {
	int high, low, wide, deep;

	high = a_info->ADE_hi;
	low  = a_info->ADE_lo;
	wide = a_info->frm_wdt;
	deep = a_info->frm_hgt;

	a_info->fnt_dta = (char *)
		( a_info->ch_ofst + ( a_info->ADE_hi - a_info->ADE_lo + 2 ) );

	swab( (char *) & a_info->id,
	      (char *) & a_info->id,
	      2 * sizeof (int) );

	swab( (char *) & a_info->ADE_lo,
	      (char *) & a_info->ADE_lo,
	      18 * sizeof (int) + 3 * sizeof (char *) + sizeof (int *) );

	swaw( (int *) & a_info->hz_ofst,
	      (int *) & a_info->hz_ofst,
	      2 );
	swaw( (int *) & a_info->ch_ofst,
	      (int *) & a_info->ch_ofst,
	      2 );
	swaw( (int *) & a_info->fnt_dta,
	      (int *) & a_info->fnt_dta,
	      2 );
	swaw( (int *) & a_info->nxt_fnt,
	      (int *) & a_info->nxt_fnt,
	      2 );

	swab( (char *) a_offset,
	      (char *) a_offset,
	      VGLYPHS * sizeof (int) );

	swab( a_glyph,
	      a_glyph,
	      wide * deep );

	if ( ( handle = creat( name, 0 ) ) == -1 )
	    return "creat failed";

	if ( write( handle,
		    (char *) a_info,
		    sizeof * a_info )
		 != sizeof * a_info )
	    return "header write failure";

	if ( write( handle,
		    (char *) & a_offset[ low ],
		    ( high - low + 2 ) * sizeof (int) )
		 != ( high - low + 2 ) * sizeof (int) )
		    return "offset table write failure";

	if ( write( handle,
		    a_glyph,
		    wide * deep )
		 != wide * deep )
	    return "char form write failure";

	close( handle );
	}

    return (char *) NULL;
}

/*
 *-----------------------------------------------------------------------------
 * Go away, leaving only a bad smell and a grease spot.
 *-----------------------------------------------------------------------------
 */

void abrt( msg, status ) char * msg; int status; {
    fputs( msg, stderr );
    fputc( '\n', stderr );
    exit( status );
}

/*
 *-----------------------------------------------------------------------------
 * and now for that anti-climax, main().  The linker says ya gotta have one.
 *-----------------------------------------------------------------------------
 */

int main( argc, argv ) int argc; char * * argv; {
    char * err_msg;

    while ( -- argc && * * ++ argv == '-' )
	switch ( ( * argv )[ 1 ] ) {
	    case 'v' :
		++ vflag;
	    case 'd' :
		++ dflag;
		break;
	    case 'a' :
		++ aflag;
		break;
	    default:
		abrt( "bad switch", 2 );
	}

    if ( argc != 2 )
	abrt( "usage: vtoa [-v] [-d] [-a] <vfont-file> <afont-file>", 3 );

    fprintf( stdout, "%s ", argv[ 0 ] );
    if ( ( err_msg = read_vfont( argv[ 0 ] ) ) != (char *) NULL )
	abrt( err_msg, 1 );

    if ( dflag )
	dump_vfont();

    fprintf( stdout, "-> %s", argv[ 1 ] );
    if ( ( err_msg = write_afont( argv[ 1 ] ) ) != (char *) NULL )
	abrt( err_msg, 4 );

    fprintf( stdout, "\n" );
    exit( 0 );
}
---------------------the end-------------------------