[comp.lang.postscript] Epson to PS converter.

jonnyg@umd5.umd.edu (Jon Greenblatt) (03/05/88)

        A few weeks ago I posted an espon to ps converter in the hopes it
would grow. Thanks to Mark Alexander the program has some more functionality.
Added is Italics, variable line spacing and Emphasized mode. Some bugs have
been fixed also. Well here it is, enjoy! BTW this program can also be used
as an ASCII listing filter.

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by rover!jonnyg on Fri Mar  4 14:19:31 EST 1988
# Contents:  makefile epson.c set.c set.h crlf.c
 
echo x - makefile
sed 's/^@//' > "makefile" <<'@//E*O*F makefile//'
CC=cc

all: epson crlf

epson: epson.o set.o
	$(CC) epson.o set.o -o epson

epson.o: epson.c set.h
	$(CC) -O -c epson.c

set.o: set.c set.h
	$(CC) -O -c set.c

crlf: crlf.c
	$(CC) -O crlf.c -o crlf
@//E*O*F makefile//
chmod u=rw,g=r,o=r makefile
 
echo x - epson.c
sed 's/^@//' > "epson.c" <<'@//E*O*F epson.c//'
/*
 *  Version 0.6
 *
 *  IBM Graphics printer to postscript translator.
 *
 *	Copyright (c) 1988, Jonathan Greenblatt,
 *		<jonnyg@rover.umd.edu> (128.8.2.73)
 *		<jonnyg@umd5.umd.edu> (128.8.10.5)
 *		<pcproj@gymble.umd.edu> (128.8.128.16)
 *      This program may be redistributed in source form,
 *      provided no fee is charged and this copyright notice is preserved.
 *
 *  DESCRIPTION:
 *	This program uses standard input and output to convert epson printer
 *	codes to Postcript. This program is an execlent ASCII listing printer.
 *	The crlf program is include to add PC crlf to end of lines for
 *	printing UNIX listings. This program was written on an IBM RT running
 *	4.3. The RT does not do crlf conversions by default so some changes
 *	may have to be made on other systems. This is a working subset of
 *	what I plan to complete. I am distributing this in hopes someone else
 *	will take interest in this code.
 *
 *  SUPPORTS:
 *	1: ASCII text.
 *	2: Underlining.
 *	3: Overstrike.
 *	4: Double width.
 *	5: Highlighting <Esc>G
 *	6: Super/Subscript
 *	7: 80x66 character on a page.
 *	8: Variable line spacing in 1/72 inch units <Esc>A(n),<Esc>2
 *	9: Italics
 *	10: Emphasized <Esc>E
 *
 *  NEEDED:
 *	1: Compressed mode.
 *	2: Variable line spacing in 1/216 units <Esc>3(n)
 *	3: Option for handling CRLF
 *	4: Non ASCII characters.
 *	5: Graphics escapes.
 *	6: I make an assumption that the font has a 6/10 ration, there are
 *	   better ways of doing this.
 *	7: Elite mode <Esc>M.
 *
 *  CONTRIBUTIONS:
 *
 *	The Following was contributed by:
 *		 Mark Alexander <uunet!amdahl!drivax!alexande@umd5.UMD.EDU>
 *
 *  - Added 1/72 line-spacing support (ESC-A).  This required changing
 *    the scaling in the Y dimension to 792, so that we have 72 points
 *    per inch in Y, instead of 36.
 *  - Changed the BNDY definition to compute page breaks correctly
 *    when we're right at the page break.
 *  - If variable line spacing is being used to slew past the end
 *    of page, don't reset cy to the top of page; instead, set it
 *    to top of page + n, where n is the position it would have
 *    been on continuous paper in an Epson.
 *  - Allowed spaces to be underlined.
 *  - Added support for emphasized mode.  It's treated exactly
 *    the same way as doublestrike.
 *  - Added support for italics.  Italics-bold hasn't been tested, though.
 *  - Print unknown ESC codes on standard error.
 *  - Allow an optional input filename to specified on the command line.
 *  - Changed the indentation to make it more readable to me; you
 *    may not like my style at all (sorry).
 *
 *
 *
 *  BUGS:
 *	1: Seems to timeout the apple laserwriter when I send large prinouts
 *	   through the tty line using no special line control.
 *	2: Underlining works, but the lines chop right through the descenders
 *	   on some lower case characters.  I haven't tried to fix this.
 *
 *
 *  PORTABILITY:
 *	I have not tried this anywhere else but it should be easily portable
 *	to the IBM PC with turbo or Microsoft C.
 *	(Note: it works fine with Datalight C 3.12.   --Mark Alexander).
 *	
 *
 *  FILES:
 *	crlf.c:	Convert UNIX file to pc Carriage return linefeed format.
 *	epson.c: Main program.
 *	set.c: ASCII set functions, like pascal, will be used more as I write
 *		more.
 *	set.h: Include file for set handling code.
 *	makefile: Unix makefile.
 *
 */

#include	<stdio.h>
#include	"set.h" /* Set functions for characters */

#define	DEBUG	1	/* Print bad things to stderr */

#define	MAXX	480			/* X scaled to 60 points per inch */
#define	MAXY	792			/* Y scaled to 72 points per inch */
#define	XSTART	0
#define	YSTART	point_sizey
#define	YSTOP	MAXY
#define	XSTOP	MAXX

#define	BNDX	(cx > (XSTOP - point_sizex))
#define	BNDY	(cy > YSTOP)

#define	NUM_TABS	28
int tabs[NUM_TABS+1] = {8,16,32,40,48,56,64,72,80,88,96,104,112,124,
		-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};

char *printables =
" abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890`,./\\[]=-!@#$%^&*()_+|{}:\"<>?~;'";
char *needesc = "()\\";

typedef enum {normal,bold,doublenorm,doublebold,italicnorm,italicbold}
	_font_type;

char *font_descr[] = {"Courier","Courier-Bold","Courier","Courier-Bold",
	"Courier-Oblique", "Courier-BoldOblique"};
char *font_name[] = {"normalfont","boldfont","doublefont","doublebold",
	"italicnorm","italicbold"};

/* Size of the fonts (x,y) in characters per inch.
 */
int font_size[][2] = {{10,6},{10,6},{5,6},{5,6},{10,6},{10,6}};

char_set printable_set;
char_set needesc_set;

_font_type font_type;

int cx, cy;
int point_sizex, point_sizey, line_space;

int underline = 0;
int subscript =  0;
int doublewidth = 0;
int highlight = 0;
int emphasize = 0;
int italic = 0;

char *start_doc[] = {
	"/saveobj1 save def",
	"/mt {moveto} def",
	"/s {show} def",
	"clippath pathbbox",
	"/ymax exch def /xmax exch def /ymin exch def /xmin exch def",
	"xmin ymax translate",
	"xmax xmin sub 480 div",
	"ymin ymax sub 792 div",
	"scale",
	"newpath",
	".5 setlinewidth",
	NULL};

char *start_page[] = {
	"/saveobj2 save def",
	NULL};

char *end_page[] = {
	"showpage",
	"saveobj2 restore",
	NULL};

char *end_doc[] = {
	"saveobj1 restore",
	"\004",
	NULL};

define_font(name,font,sizex,sizey)
char *name, *font;
{
	printf("/%s {/%s findfont [%d 10 6 div mul 0 0 -%d 0 0] makefont setfont} def\n",name,font,sizex,sizey);
}

new_page()
{
	cx = XSTART;
	cy = YSTART;
	plist(start_page);
	set_font();
}

set_font()
{
	int	boldface;

	boldface = highlight || emphasize;

	if (doublewidth)
		font_type = boldface ? doublebold : doublenorm;
	else if (italic)
		font_type = boldface ? italicbold : italicnorm;
	else
		font_type = boldface ? bold: normal;

	point_sizex = get_pointx(font_size[(int)font_type][0]);
	point_sizey = get_pointy(font_size[(int)font_type][1]);
	printf("%s\n",font_name[(int)font_type]);
}

eject_page()
{
	plist(end_page);
	new_page();
}

/* Check if cy has gone past a page boundary, and skip to a new
 * page if necessary.  Set the new y-position not to the top of
 * page, but the point it would have been had this been a real
 * Epson printer with continuous forms.
 */
check_page()
{
	int oldcy;

	if (BNDY)
	{
		oldcy = cy;
		eject_page();
		cy = oldcy - YSTOP;
	}
}
		
init_sets()
{
	clear_set(printable_set);
	clear_set(needesc_set);
	str_add_set(printable_set,printables);
	str_add_set(needesc_set,needesc);
}

get_pointx(char_per_inch)
{
	return(60/char_per_inch);
}

get_pointy(char_per_inch)
{
	return(72/char_per_inch);
}

init_printer()
{
	point_sizex = get_pointx(font_size[(int)normal][0]);
	point_sizey = line_space = get_pointy(font_size[(int)normal][1]);
	plist(start_doc);

	/* Define the normal-width fonts.
	 */
	define_font(font_name[(int)normal],font_descr[(int)normal],point_sizex,point_sizey);
	define_font(font_name[(int)bold],font_descr[(int)bold],point_sizex,point_sizey);
	define_font(font_name[(int)italicnorm],font_descr[(int)italicnorm],point_sizex,point_sizey);
	define_font(font_name[(int)italicbold],font_descr[(int)italicbold],point_sizex,point_sizey);

	/* Define the double-width fonts.
	 */
	point_sizex = get_pointx(font_size[(int)doublenorm][0]);
	point_sizey = get_pointy(font_size[(int)doublenorm][1]);
	define_font(font_name[(int)doublenorm],font_descr[(int)doublenorm],point_sizex,point_sizey);
	define_font(font_name[(int)doublebold],font_descr[(int)doublebold],point_sizex,point_sizey);
	font_type = normal;
	new_page();
}

plist(l)
char **l;
{
	while (*l != NULL)
		puts(*l++);
}

#define	LINE_SIZE	256
char pline[256];
int ptr = 0;

char outline[258];

putline()
{
	char *s = pline;
	int p;
	int lx;

	check_page();

	/* Skip over leading spaces, unless underlining is enabled
	 * (we want to underline spaces)
	 */
	if (underline == 0)
	{
		while (*s == ' ' && ptr > 0)
		{
			s++;
			ptr--;
			if (BNDX)
			{
				cx = XSTART;
				newline();
			}
			cx += point_sizex;
		}
	}

	while (ptr > 0)
	{
		p = 0;
		check_page();
		if (BNDX)
		{
			cx = XSTART;
			newline();
		}
		lx = cx;
		while ((lx+point_sizex-1) < XSTOP && ptr > 0)
		{
			if (in_set(needesc_set,*s))
				outline[p++] = '\\';
			outline[p++] = *s++;
			ptr--;
			lx += point_sizex;
		}
		outline[p] = 0;
		if (p > 0)
		{
			int i = strlen(outline);
			/* Forward slash won't work at the end of a string */
			if (outline[i-1] == '/')
			{
				outline[i] = ' ';
				outline[i+1] = '\0';
			}
			if (subscript)
			{
				if (subscript == 1) /* Subscript */
					printf("%d %d mt gsave 1 .5 scale (%s) s grestore\n",cx,cy,outline);
				else /* Superscript */
				printf("%d %d mt gsave 1 .5 scale (%s) s grestore\n",cx,cy-(point_sizey>>1),outline);
			}
			else /* Normal printing */
				printf("%d %d mt (%s) s\n",cx,cy,outline);
			if (underline)
				printf("%d %d mt %d %d lineto stroke\n",cx,cy+1,lx-1,cy+1);
		}
		cx = lx;
	}
}

newline()
{
	cy += line_space;
	check_page();
}

typedef enum {S_BASE,S_ESC,S_UNDERLINE,S_SUBSCRIPT,S_DOUBLE,S_LINESPACE} states;

states state = S_BASE;

dochar(c)
{
	if (ptr >= LINE_SIZE) putline();
	switch (state)
	{
	case S_UNDERLINE:
		state = S_BASE;
		if (c == '\0')	underline = 0;
		else if (c == '\1') underline = 1;
		break;
	case S_DOUBLE:
		state = S_BASE;
		if (c == '\0')	doublewidth = 0;
		else if (c == '\1') doublewidth = 1;
		set_font();
		break;
	case S_SUBSCRIPT:
		state = S_BASE;
		if (c == '\0') subscript = 2;
		else if (c == '\1') subscript = 1;
		break;
	case S_LINESPACE:
		state = S_BASE;
		line_space = c;
		break;
	case S_ESC:
		state = S_BASE;
		switch (c)
		{
		case '2':	/* set line spacing to 1/6 inch	*/
			line_space = 12;
			break;
		case '4':
			italic = 1;
			set_font();
			break;
		case '5':
			italic = 0;
			set_font();
			break;
		case 'A':
			state = S_LINESPACE;
			break;
		case 'E':
			emphasize = 1;
			set_font();
			break;
		case 'F':
			emphasize = 0;
			set_font();
			break;
		case 'G':
			highlight = 1;
			set_font();
			break;
		case 'H':
			highlight = 0;
			set_font();
			break;
		case '-':
			state = S_UNDERLINE;
			break;
		case 'T':
			subscript = 0;
			break;
		case 'S':
			state = S_SUBSCRIPT;
			break;
		case 'W':
			state = S_DOUBLE;
			break;
		default:
#ifdef	DEBUG
			fprintf(stderr,"Unknown sequence: ESC 0x%x\n",c);
#endif
			break;
		}
		break;
	   case S_BASE:
		switch(c)
		{
		case '\011':
			{
				int i,l;

				l = cx / point_sizex + ptr;
				for (i = 0; i < NUM_TABS && tabs[i] <= l; i++)
					;
				if (tabs[i] == -1) break;
				i = tabs[i] - l;
				while (i-- > 0) dochar(' ')
					;
			}
			break;
		case '\033':
			putline();
			state = S_ESC;
			break;
		case '\014':
			putline();
			eject_page();
			break;
		case '\n':
			putline();
			newline();
			break;
		case '\r':
			putline();
			cx = XSTART;
			break;
		case 26:	/* CTRL-Z */
			break;
		default:
			if (in_set(printable_set,c))
				pline[ptr++] = c;
			else
				pline[ptr++] = ' ';
			break;
		}
		break;
	}
}

main(argc,argv)
int argc;
char *argv[];
{
	int n;
	int file;
	char *p;
	static char istr[128];

	if (argc > 2)
	{
#ifdef	DEBUG
		fprintf(stderr,"Usage: epson [file]\n");
#endif
		exit(1);
	}
	if (argc == 2)
	{
		if ((file = open(argv[1],0)) < 0)
		{
#ifdef	DEBUG
			fprintf(stderr,"Unable to open %s\n",argv[1]);
#endif
			exit(2);
		}
	}
	else
		file = 0;		/* standard input	*/

	init_sets();
	init_printer();
	while ((n = read(file,p = istr,128)) > 0)
		while (n--) dochar(*p++)
			;
	if (cx > XSTART || cy > YSTART)
		plist(end_page);
	else
		printf("saveobj2 restore\n");
	plist(end_doc);
	exit(0);
}
@//E*O*F epson.c//
chmod u=rw,g=r,o=r epson.c
 
echo x - set.c
sed 's/^@//' > "set.c" <<'@//E*O*F set.c//'
#include "set.h"

clear_set(set)
char_set set;
	{
	int i;
	for (i = 0; i < INTS_IN_SET; i++) set[i] = 0;
	}

fill_set(set)
char_set set;
	{
	int i;
	for (i = 0; i < INTS_IN_SET; i++) set[i] = 0xffff;
	}

not_set(set)
char_set set;
	{
	int i;
	for (i = 0; i < INTS_IN_SET; i++) set[i] ^= 0xffff;
	}

and_set(set,set1)
char_set set, set1;
	{
	int i;
	for (i = 0; i < INTS_IN_SET; i++) set[i] &= set1[i];
	}

or_set(set,set1)
char_set set,set1;
	{
	int i;
	for (i = 0; i < INTS_IN_SET; i++) set[i] |= set1[i];
	}

add_set(set,c)
char_set set;
unsigned int c;
	{
	c &= 0xff;
	set[c >> 4] |= 1 << (c & 15);
	}

del_set(set,c)
char_set set;
unsigned int c;
	{
	c &= 0xff;
	set[c >> 4] &= ~(1 << (c & 15));
	}

in_set(set,c)
char_set set;
unsigned int c;
	{
	c &= 0xff;
	return ((set[c >> 4] & (1 << (c & 15))) != 0);
	}

str_add_set(set,s)
char_set set;
char *s;
	{
	unsigned int c;
	while (*s) {
		c = *s++;
		c &= 0xff;
		set[c >> 4] |= 1 << (c & 15);
		}
	}

str_del_set(set,s)
char_set set;
char *s;
	{
	unsigned int c;
	while (*s) {
		c = *s++;
		c &= 0xff;
		set[c >> 4] &= ~(1 << (c & 15));
		}
	}
@//E*O*F set.c//
chmod u=rw,g=r,o=r set.c
 
echo x - set.h
sed 's/^@//' > "set.h" <<'@//E*O*F set.h//'
#define INTS_IN_SET	16

typedef unsigned int char_set[INTS_IN_SET];
@//E*O*F set.h//
chmod u=rw,g=r,o=r set.h
 
echo x - crlf.c
sed 's/^@//' > "crlf.c" <<'@//E*O*F crlf.c//'
main() {
	char ibuff[512], *ip;
	char obuff[1024];
	int ic, oc;

	while ((ic = read(0,ip = ibuff,512)) > 0) {
		oc = 0;
		while (ic--)
			if (*ip == '\n')
				{
				ip++;
				obuff[oc++] = '\r';
				obuff[oc++] = '\n';
				}
			else	obuff[oc++] = *ip++;
		write(1,obuff,oc);
		}
	}
@//E*O*F crlf.c//
chmod u=rw,g=r,o=r crlf.c
 
exit 0