/* A program in the C Language for compiling on Unix-like systems. 
*
*	Converts a pattern font file to the code required to load the font
* 	into a terminal in VT220 mode.
*
*	Copyright (c) by David S. Lawyer, July 1990 and may be freely used, 
*	copied, and modified by anyone provided that any modifications 
*	of major significance be also made freely available.  If sold, the
*	purchaser must be clearly informed (by larger than fine print) prior 
*	to the sale that this is free software.
*	This copyright notice must not be removed.
*
*	Thanks to Victor Poon for his Wyse60 font programs which provided
*	some of the ideas used in this program.
*Version: 1.1	 
*
* Input is a pattern file containing character patterns, 8 to a row.  It is
* either std. input [use -s flag] or is given as an argument.  Each such
* pattern should be similar to the large "A" character shown on the next page.
* Output goes to stdout and thus all messages must go to stderr.  To run this
* program (after you compile it) create a pattern file named say my_patterns
* by using any convenient editor.  Then put my_patterns thru this program (as
* a filter) thereby obtaining the font code in your output file named say
* my_font.  To download this font you will want to use a shell script.  This 
* script should set up the terminal correctly, send escape sequence telling
* where to load the font etc.  See the sample shell script in the vt200.doc
* file.
*
* Many terminals can't download font at 9600 baud.  There are 3 types of
* fixes for this problem: 1. Lower the baud rate.  2. Enable Xon/Xoff
* flow control (Doesn't always solve the problem on some inferior
* terminals).  3. Add null padding to your my_font file by using a flag
* such as -p 80 when generating the softfont.   On a Wyse 99Gt terminals 
* it was necessary to use 80 nulls per line of my_font.  
*/       
#include <stdio.h>   /* For some C compliers you should: #include <stdlib.h> */
/*--------------------external variables--------------------------------------*/
extern char *optarg;
extern int optind;
/*--------------------global variables---------------------------------------*/
int 
i,				/* loop index counter used only within loops */
nchars_coded = 0,
Cell_height,	/* = cell_height++ since index-origin = 0.  Read from header.*/
Cell_width,	/* = cell_width++ since index-origin = 0.  Read from header.*/
Pad_level = 0;	/* How many nulls to pad each line with. Command line option.*/

char
junk[100];	/* For reading junk characters from file (comments etc.)     */
FILE	*fp;					/* Pointer to input file */ 
/*--------------------forward declarations-----------------------------------*/
int encode_band();			/* Encodes 8 large-chars in band */
void usage();				/* Usage help & error message */
void	scan_err(int nscanned, int row);	/* error message	*/ 
void char_read_err( char ch,int nchars_coded, int char_no, int row, int col,
			unsigned char band[][][]);	 /* error message*/
/*--------------------start of main------------------------------------------*/
int main(int argc, char **argv) {
char	header_line1[100],		/* For storing 1st header line */
	header_line2[100],		/* For storing 2nd header line */
	ch;				/* For reading first char of header*/
/*-------------------parse command line--------------------------------------*/
if 	  ( argc == 1 ) usage();
while (( ch = getopt(argc, argv, "p:s") ) != EOF) {
	switch (ch) {
	case 'p': Pad_level = atoi(optarg);
		  fprintf(stderr, "Pad_level = %d nulls/line. \n", Pad_level);
		  break;
	case 's': fp = stdin;
		  fprintf(stderr, "Using standard input\n");
		  break;
	case '?': usage();
	}
}
if (fp  != stdin ) {
	if ( (fp = fopen( argv[optind], "r")) == NULL)
	     {   fprintf(stderr, "Can't open output file: %s\n", argv[optind]);
		     usage();
	     }  
}
/*------------------parse header lines--------------------------------------*/
while( '#' == (ch = getc(fp) ) )	/* Discard comment lines (seek) */
    fgets(junk, sizeof(junk), fp);	/* Seek to end of line. fscanf broken */
ungetc( ch, fp);			/* Put back non-# ch on input file. */

fprintf (stderr,"Start of encoding font.  Following are header lines from your\
 input file:\n");

/* The header line of the source file must look something like: 12x7 pattern. */

fgets( header_line1, sizeof(header_line1), fp);		/* Read first line */
fputs( header_line1, stderr);			/* Echo it to your terminal */
sscanf( header_line1, "%ix%i",&Cell_height, &Cell_width );
						/* Extract vars. from it */
/* The second header line of the source file is just a brief description of the
 * font which will be echoed to the terminal each time the font is loaded.
 */
fgets( header_line2, sizeof(header_line2), fp);		/* Read second line */
fputs( header_line2, stderr);			/* Echo it to your terminal */
/*----------------------call encode_band-------------------------------------*/

while (encode_band() != NULL);  /* Encodes 8 chars until EOF  */
fputs("\033\\\n", stdout);	/* End of downloading softfont to term */
				/* =ESC\RET    \033=ESC    \\=\   \n=RET */
fputs(header_line1, stdout);	/* Header line at end of output file */
fputs(header_line2, stdout);	/* Header line at end of output file */
fprintf( stdout,"Pad_level = %d nulls/line \n", Pad_level);
fprintf (stderr,"Finished.  Encoded %d characters for a VT200 series terminal.\
	\n", nchars_coded);
}
/*---------------------end of main-------------------------------------------*/
/*		EXAMPLE OF A 12x7 PATTERN: 
*	It is also called a 7x12 pattern and it might be displayed within a
*	larger 10x13 (or 13x10) cell on a Wyse terminal.  This 12x7 pattern
*	is also said to fit into a 12x7 "cell" which in turn will go
*	into a larger cell on the physical terminal screen.
*
*      The character matrix (or pattern) has character (either . or *) pixels.
*	Eight such matrices in a row form a "band" of 8 character matrices.
*           (0,0).......(0,6)
*                ...*... 		Note index origin = 0, but error
*                ..*.*.. 		messages use index origin = 1.	
*                .*...*.  <---------	The * at (3,1) is band[0][3][1]
*                *.....* 		[0] is the 0th character matrix
*                *******		in the band of 8 chars (0-7)
*                *.....*		Col. 7 is absent (but assumed all dots) 
*                *.....*                
*                *.....* 
*                *.....*		band [char_no] [row] [col] 
*                *.....* 		Use last 2 rows for descenders.
*                ....... 		Cell_height = 12
*          (11,0).......(11,6)		cell_height = Cell_height - 1 = 11 
*					Cell_width = 7 
*					cell_width = Cell_width - 1 = 6
*     The above pattern must be encoded as two sequences of 7 ascii characters
*     (of letters) with a slash \ separating the sequences.
*     The 1st letter has bits (5,0)-(0,0)	(0,0) is the low-order bit.
*     The 2nd letter has bits (5,0)-(0,1)
*     The 3rd letter has bits (5,0)-(0,2)	A "half column" contains 6
*     .........					pixels (5 to 0)	
*     The 7th letter has bits (5,6)-(0,6)
*     Now a slash / is added
*     The 8th letter has bits (11,0)-(6,0)	If Cell_height=13 these half
*     The 9th letter has bits (11,1)-(6,1)	columns would contain 7 pixels
*     .........					instead of 6.
*     The 14th letter has bits (11,6)-(6,6)
*     Now a semicolon / is added to terminate the character definition
*     The letter(Cell_height) has bits (cell_height,0)-(cell_height,cell_width)	
*
* INPUT FILE FORMAT:  Look at a sample file (named ...pat) before
* reading this.  The file begins with optional comment lines each
* starting with #.  The next two lines are file-header lines for the
* entire file.  The first gives the size of the character cells in the
* pattern file.  For example: 12x7 cells.  "10x8 patterns" would also be
* an acceptable line (don't type in the quotes).  The second line should 
* be an important comment which will be displayed on the crt each time you
* download the font.  Then comes thick-rows of Character matrices 
* (patterns).  Each such thick-row is called a "band" and must be
* preceded by a horizontal separator-header line.  The horizontal
* separator-header lines should contain the character numbers etc.  but
* may contain anything (including a blank line).  Each character matrix
* is separated from the adjacent one to its right (or left) by vertical
* "columns" of white space.  Each such "column" may be wider than one
* space if desired.  There must be exactly 8 character matrices
* (patterns) in each band (= thick-row).
*/
/**************************encode_band()***************************************/
int encode_band() {			/* Parameters passed vis global vars. */
int row, col, start_row, stop_row, 	/* Loop variables. Row range for loop */
	cell_height = Cell_height - 1,
	cell_width = Cell_width - 1,
	bit_value,	/* increment letter with this */
	char_no,	/* =0 to 7; sequence no. of pattern-char */
	nscanned;	/* No. of strings scanned by fscanf.  Should be 1. */

char	ch,			/* char read from pattern == * or . */
	header[100];		/* used for storage of a header line */
unsigned char
	letter,		/* letter encoded from a row of a pattern matrix */
	band[8][Cell_height][Cell_width + 1];
		/* A band of 8 pattern_chars. See drawing above.
		 * band array: band [char_no] [row] [col]
		 * Each element is either a * of a dot.
		 * The + 1 above is for the null terminator for a string in C
		 */
enum {upper_half = 1, lower_half = 2}
	half;		     /* Which half of the pattern are we doing now? */
/*--------------------------------read band-----------------------------------*/
/* A band of 8 large_characters is read from the input file pointed to by fp 
 * and is put (unencoded) into the band array.
 */
if ( fgets(header, sizeof(header), fp) == NULL) 	/* Get header line */
	return 0;				/* probably EOF */
fputs(header, stderr);				/* echo header line of band */
for (row = 0; row <= cell_height; row ++) {
    for (char_no = 0; char_no <= 7; char_no ++) {
	nscanned = fscanf(fp, "%[^ \t\n]%*[ \t]", band[char_no] [row]);
	if (nscanned != 1)
	    scan_err(nscanned, row);	/* print error message.  No exit      */
     }
    fgets(junk, sizeof(junk), fp);		/* seek to end of line	     */	
 } 
/*-------------------------encode band to letters-----------------------------*/
for (char_no = 0 ; char_no <= 7 ; char_no ++) {
    for (half = upper_half; half <= 2; half++) { /* half = lower_half for 2nd */
						 /* (and final) iteration     */
	switch (half) {				 /* Which half of pattern?    */
	case upper_half: start_row = 0;  stop_row = 5;	 	  break;
	case lower_half: start_row = 6;  stop_row = cell_height;  break;
	}
	for (col = 0 ; col <= cell_width ; col ++) {
	    bit_value = 1;
	    letter = 0;						/* initialize */
	    for (row = start_row;  row <= stop_row;  row++) {
		ch = band [char_no] [row] [col];
		if (ch == '*')
		    letter += bit_value;
		  else if (ch != '.')
		    char_read_err(ch, nchars_coded, char_no, row, col, band);
		bit_value <<= 1 ;			/* double bit_value */
	     }	   /* end for on row. Bump bit_value. Have encoded a half col */
	    putchar ( letter + 0x3F );		/* Makes chars printable      */
	 }    /* end for on col.  Encode half-col. Have encoded half a pattern*/
/*-----------------------output /; separators---------------------------------*/
	switch(half) {			       /* Which half of pattern done? */
	case upper_half:  putchar('/');	break; 	      /* after 1st iteration  */
	case lower_half:  putchar(';');	break;        /* after 2nd iteration  */
	}						
     }	/* end for on half (case). Encode half pat. Have encoded full pattern.*/
/*------------------------------add padding?----------------------------------*/
    for (i = 1; i <= Pad_level; i++)	/* Append padding nulls to char */
	putchar('\0');
/*------------------------finish up------------------------------------------*/
    putchar('\n');		/* End of large_char encoding (EOL) */
    nchars_coded ++;
 } 	     /* end for on char_no.  Encode a char.  Have encoded all chars. */
return nchars_coded;		/* Must return a non-zero value */
}
/*----------------------end encode_band()-------------------------------------*/
/***********************char_read_err******************************************/
void char_read_err( char ch, int nchars_coded, int char_no, int row, int col,
			unsigned char band[][Cell_height][Cell_width + 1] ) {
fprintf(stderr, "\n###ERROR### in reading pattern file.  Found an illegal char:\
 %c (= Hex %X)  \nin band %d the %d th character,",
	ch, ch, (nchars_coded / 8) +1, char_no +1);
fprintf(stderr, " located at row %d col %d of character matrix.\n",
	row + 1, col + 1); 
fprintf(stderr, "The offending line (white space becomes a single space) is:\
	\n%s %s %s %s %s %s %s %s \n",
	band[0][row], band[1][row], band[2][row], band[3][row], 
	band[4][row], band[5][row], band[6][row], band[7][row]);

if (ch == '\0' )
fprintf(stderr, "The null character found is likely not really in your source\
 file but is a  \nnull (Hex 0) terminator for a string which is generated\
 because there is a  \nwhite-space character where a . or * should be.\n");
exit (-1);
}
/*****************************scan_err()***************************************/
void	scan_err(int nscanned, int row) {
fprintf(stderr,"\n***ERROR***  fscanf returned %d instead of 1.  If 0 it failed\
 to find a char.  \nBefore scanning this band you encoded %d characters.  Error\
 is in row %d of  \ncurrent band (see last header line above). \n\n",
    nscanned, nchars_coded, row + 1);     
}	/* Will likely exit later w/char_read_err */
/*********************************usage()**************************************/
void usage() {
fprintf(stderr, "Usage:  GenFontVT200 [-p 80]   -s | input_file \
   \n-p 80 => append 80 (or whatever) padding nulls to each line of output.\
   \n-s use standard input.\n");
exit (-1);
}
