[net.sources] Banner program for Hershey fonts

guy@enmasse.UUCP (Computer Guy) (10/23/86)

Hbanner: A program to print banners on 132-column line printers,
using the Hershey fonts.  Unshar and enjoy.

                                           -- guy k hillyer
               {alliant,panda,drilex}!enmasse!guy
                                panda!enmasse!guy@talcott.arpa

P.S.  Yes we have no man pages!
--------8<--------8<--------8<--------8<--------8<--------8<--------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	README
#	Makefile
#	mk_font.sh
#	hbanner.c
# This archive created: Wed Oct 22 19:10:56 1986
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
cat << \SHAR_EOF > 'README'
To quote from the README file in the Hershey font distribution:

>Additional Work To Be Done (volunteers welcome!):
>	- Write a banner-style program using Hershey Fonts for input and
>		non-graphic terminals or printers for output.

I had yet to see such a program, so I wrote it myself, and herewith
present the fruit of my labors to the banner-hungry denizens of Usenet.

Included in this package are:
	1) this file (README),
	2) a Makefile,
	3) the hbanner.c program source,
	4) mk_font, a shell script for translating the hershey database
into an hbanner database.

I assume that you have the hershey font distribution, including:
	1) hersh.oc[1234],
	2) all of the occidental translation tables (*.hmp) in the same
directory as hersh.oc[1234].

If you don't have the hershey fonts, they are available from the
mod.sources archive (sources-request@mirror.uucp).  Please do not post
requests for hershey fonts to net.sources!

I also assume that you have a 132-column line printer.

To configure hbanner, you must first edit the Makefile, setting these
two variables:

	HERSHDIR= <the directory where the hersh.oc? & *.hmp files reside>
	FONTDIR= <the directory where you want the hbanner font files to go>

After you run make, you may begin to compile the font database.  The
mk_font shell script is invoked thusly:

	mk_font <fontname> &

where fontname is the root of the name of any of the *.hmp files in
HERSHDIR.  I suggest running this in the background, as it will take a
number of minutes.

Once you have an hbanner font file, you are ready to make a banner.
Surely you can think of SOMETHING that deserves to be printed in GIANT
letters?

	hbanner <fontname>
	Fee, fie, foh, fum; I smell the blood of an Englishman!
	^D

Note that hbanner with no arguments will print a list of the available
fonts.

If you have any trouble with this beastie, or if you thought of a better
way to do it, by all means contact me.

						Guy K. Hillyer
						guy@enmasse.uucp
				{panda,drilex,alliant}!enmasse!guy
SHAR_EOF
fi
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
# makefile for hbanner & mk_font

HERSHDIR = /stuff/hershey
FONTDIR = /local/lib/hbfonts

CFLAGS= -O

all: hbanner mk_font

hbanner: hbanner.c
	cc -o hbanner $(CFLAGS) -DFONTDIR=\"$(FONTDIR)\" hbanner.c

mk_font: mk_font.sh
	sed -e "s|%HERSHDIR%|$(HERSHDIR)|" -e "s|%FONTDIR%|$(FONTDIR)|" \
		< mk_font.sh > mk_font
	chmod 775 mk_font

shar: all
	shar README Makefile mk_font.sh hbanner.c > hbanner.shar
SHAR_EOF
fi
if test -f 'mk_font.sh'
then
	echo shar: "will not over-write existing file 'mk_font.sh'"
else
cat << \SHAR_EOF > 'mk_font.sh'
HERSHDIR=%HERSHDIR%
FONTDIR=%FONTDIR%

HERSHFILES=$HERSHDIR/hersh.oc[1234]
HMPFILE=$HERSHDIR/$1.hmp
FNTFILE=$FONTDIR/$1.fnt
FNTLINES=96

if [ ! -f $HMPFILE ]
then
	echo "$HMPFILE does not exist.  Usage: mk_font <fontname>";
	exit 1;
fi

# filter the hershey database to merge
# character descriptions into single lines

trap "rm -f /tmp/hersh.$$;exit 1" 0 1 2 15

cat $HERSHFILES | awk '{
	if (substr ($0, 1, 1) == " ") {
		catnum = substr ($0, 1, 5);
		if (catnum+0 != 0) {
			printf ("\n");
		}
	}
	printf ("%s", $0);
}' > /tmp/hersh.$$
	
# now grep for each entry in the hmp file.
# the awk script simply expands entries in the hmp file
# of the form "2551-2576" into the range of numbers indicated.

awk '{
	for (i = 1; i <= NF; i++) {
		n = split ($i, a, "-");

		if (n == 1) {
			print $i;
		} else {
			for (j = a[1]; j <= a[2]; j++) {
				print j;
			}
		}
	}
}' < $HMPFILE | xargs -i egrep "^ *{}" /tmp/hersh.$$ > $FNTFILE

# superficially check the resulting .fnt file for validity

fntlines=`wc -l $FNTFILE | awk '{print $1}'`
if [ $fntlines -ne $FNTLINES ]
then
	echo "$FNTFILE has only $fntlines lines... should have $FNTLINES";
	echo "It is likely that one of the following files is corrupted:";
	echo "";
	echo $HERSHFILES $HMPFILE;
	echo "";
	echo "In any case, $FNTFILE is not a valid database for hbanner.";
	exit 1
fi
SHAR_EOF
chmod +x 'mk_font.sh'
fi
if test -f 'hbanner.c'
then
	echo shar: "will not over-write existing file 'hbanner.c'"
else
cat << \SHAR_EOF > 'hbanner.c'
/*
 *	Hbanner: A program to print banners on 132-column
 *	line printers, using the Hershey fonts.  
 *
 *	Copyright (c) 1986 by Guy K. Hillyer.
 *	All rights reserved.  Permission is hereby
 *	granted for personal, non-commercial reproduction
 *	and use of this program, provided that this
 *	notice is included in any copy.
 */

#include <stdio.h>

#ifndef FONTDIR
#define FONTDIR "/local/lib/hfonts"
#endif

#define FONTSZ		96	/* number of characters in font */
#define FONTBEGIN	0x20	/* font begins at ascii space (' ') */
#define LINESZ		512	/* sizeof multi-purpose line buffer */
#define YORIGIN		64	/* center of y-axis */
#define MAXY	(YORIGIN*2)	/* y dimension of character bitmaps */
#define MAXX		64	/* x dimension of bitmaps */
#define CUSHION		20	/* space at beginning and end of message */
#define DISPCHAR	'O'	/* a lit pixel */

#define decode(x) ((x) - 'R')

char line[LINESZ];		/* multi-purpose line buffer */
char *font[FONTSZ];		/* table of character descriptions */
char bitmap[3][MAXY][MAXX/8+1];	/* holds previous, current, and next char */
int curr, prev, next;		/* indexes into bitmap[] */
int currwidth, prevwidth;	/* x-axis size of current and previous char */
int lwidth, rwidth;		/* displacement from 0 of L & R sides of char */
int xorigin, yorigin;		/* offset for zero-based graph */

/* Bitmaps exist for the previous, current, and next characters,
because some of the script fonts have characters that overlap adjacent 
character positions.  If the previous character overlapped to the 
right (such as 'l'), the current character bitmap may contain pieces 
of the previous character.  Likewise, The current character bitmap is 
not printed until the next character has been built, because the next 
character may be one of those (such as 'f') that overlap to the left. */

main (argc, argv)
int argc;
char *argv[];
{
	register int c;

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

	read_font (argv[1]);

	init_bitmaps();

	while ((c = getchar ()) != EOF) {
		build_char (c);
		print_char (prev, prevwidth);
		rotate_bitmaps ();
	}
	print_char (prev, prevwidth);
	print_char (curr, CUSHION);
}

read_font (fontname)
char *fontname;
{
	FILE *fp;
	int i, dlen;
	char *malloc();

	/* assemble font file name */

	strcpy (line, FONTDIR);
	strcat (line, "/");
	strcat (line, fontname);
	strcat (line, ".fnt");

	if ((fp = fopen (line, "r")) == NULL) {
		fprintf (stderr, "Cannot open %s\n", line);
		perror ("");
		fprintf (stderr, "There is no font named %s.\n", fontname);
		pr_avail_fonts ();
		exit (-1);
	}

	for (i = 0; i < FONTSZ; i++) {
		if (fgets (line, LINESZ, fp) == NULL) {
			strcpy (line, FONTDIR);
			strcat (line, "/");
			strcat (line, fontname);
			strcat (line, ".fnt");
			fprintf (stderr,
			   "The font description file %s is corrupted.\n", line);
			exit (-1);
		}
		dlen = strlen (line);
		line[dlen-1] = '\0';		/* toss out the newline */
		if ((font[i] = malloc (dlen-5)) == NULL) {
			fprintf (stderr, "Oops.  Ran out of memory.\n");
			exit (-1);
		}
		strcpy (font[i], line+5);	/* +5 skips catalogue number */
	}

	fclose (fp);
}

build_char (c)
char c;
{
	char *dp;
	char nbuf[4];
	int npoints;
	int oldx, newx, oldy, newy;
	int skip, i, fchi;

	fchi = (c & 0x7f) - FONTBEGIN;
	if (fchi >= FONTSZ || fchi < 0)		/* character out of range */
		fchi = 0;			/* translate into space ' ' */

	dp = font[fchi];

	/* get number of points in this description */

	nbuf[0] = *dp++;
	nbuf[1] = *dp++;
	nbuf[2] = *dp++;
	nbuf[3] = '\0';
	npoints = atoi (nbuf);

	/* figure left & right horizontal displacement */

	lwidth = abs (decode (*dp++));
	rwidth = decode (*dp++);
	npoints--;

	currwidth = lwidth + rwidth;
	xorigin = lwidth;
	yorigin = YORIGIN;

	skip++;
	for (i = 0; i < npoints; i++) {

		if (*dp == ' ') {	/* "lift pen" */
			dp += 2;
			skip++;
			continue;
		}

		if (skip) {
			skip = 0;
			oldx = decode (*dp++);
			oldy = decode (*dp++);
			continue;
		}

		newx = decode (*dp++);
		newy = decode (*dp++);

		drawline (oldx, oldy, newx, newy);

		oldx = newx;
		oldy = newy;
	}
}


drawline(xfrom, yfrom, xto, yto)
int xfrom, yfrom, xto, yto;
{
	double m, b, dx, dy;
	int x, y;

	/*
	 *	Multiplying y values by two compensates for
	 *	the aspect ratio of a typical display device.
	 *	Adding the origin offsets the numbers so
	 *	that they can be used to index into the bitmap
	 *	array 
	 */

	yfrom *= 2;
	yto *= 2;
	xfrom += xorigin;
	xto += xorigin;
	yfrom += yorigin;
	yto += yorigin;

	dx = (double)(xto - xfrom);
	dy = (double)(yto - yfrom);

	if (abs((int)dx) >= abs((int)dy)) {	/* increment along x-axis */
		if (xfrom > xto) {		/* swap endpoints */
			x = xfrom;
			xfrom = xto;
			xto = x;
			y = yfrom;
			yfrom = yto;
			yto = y;
			dx = (double)(xto - xfrom);
			dy = (double)(yto - yfrom);
		}

		m = dy / dx;				/* slope */
		b = (double)yfrom - m * (double)xfrom;	/* y-intercept */

		for (x = xfrom; x < xto; x++) {
			y = round (m * x + b);
			plot (x, y);
		}
	} else {				/* increment along y-axis */
		if (yfrom > yto) {		/* swap endpoints */
			x = xfrom;
			xfrom = xto;
			xto = x;
			y = yfrom;
			yfrom = yto;
			yto = y;
			dx = (double)(xto - xfrom);
			dy = (double)(yto - yfrom);
		}

		m = dx / dy;
		b = (double)xfrom - m * (double)yfrom;	/* x-intercept */

		for (y = yfrom; y < yto; y++) {
			x = round (m * y + b);
			plot (x, y);
		}
	}
}

round (n)
double n;
{
	int i;

	i = (int)n;
	n -= (double)i;
	if (n > 0.5)
		i++;
	else if (n < -0.5)
		i--;
	return (i);
}

plot (x, y)
int x, y;
{
	int i;

	if (x < 0) {			/* overlap to left */
		x += prevwidth;
		i = prev;
	} else if (x >= currwidth) {	/* overlap to right */
		x -= currwidth;
		i = next;
	} else {			/* within range of current bitmap */
		i = curr;
	}
	bitmap[i][y][x>>3] |= (01 << (7 - (x&07)));
}

print_char (which, width)
int which, width;
{
	int x, y, xlimit, ylimit, lowest_y;

	xlimit = width;
	ylimit = yorigin * 2;

	for (x = 0; x < xlimit; x++) {
		/*
		 *	Look for the last lit pixel on the line.
		 *	This is here to limit the unnecessary printing
		 *	of spaces at the end of each line.
		 */

		for (y = 0; y < ylimit; y++) {
			if (bitmap[which][y][x>>3] & (01 << (7 - (x&07))))
				break;
		}
		lowest_y = y;

		for (y = ylimit-1; y >= lowest_y; y--) {
			if (bitmap[which][y][x>>3] & (01 << (7 - (x&07)))) {
				putchar (DISPCHAR);
			} else {
				putchar (' ');
			}
		}
		putchar ('\n');
	}
}

init_bitmaps ()
{
	prev = 0;
	curr = 1;
	next = 2;

	prevwidth = currwidth = CUSHION;
}

rotate_bitmaps ()
{
	int tmp;

	tmp = prev;
	prev = curr;
	curr = next;
	next = tmp;

	prevwidth = currwidth;

	clear_bitmap (next);
}

clear_bitmap (which)
int which;
{
	int x, y;

	for (y = 0; y < MAXY; y++)
		for (x = 0; x < MAXX/8+1; x++)
			bitmap[which][y][x] = '\0';
}

pr_avail_fonts ()
{
	fprintf (stderr, "Available fonts are:\n");
	sprintf (line,
	   "ls %s/*.fnt | sed -e \"s/^.*\\//	/\" -e \"s/.fnt$//\" 1>&2",
	   FONTDIR);
	system (line);
}
SHAR_EOF
fi
exit 0
#	End of shell archive
-- 
                                           -- guy k hillyer
               {alliant,panda,drilex}!enmasse!guy
                                panda!enmasse!guy@talcott.arpa