/* 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 Wyse60 terminal or Wyse99GT in native emulation (personality 
*	per Wyse jargon).
*
*	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 could just copy it to the terminal but
* in most cases you will want to use a shell script.  This script will set up
* the terminal correctly and then activate the appropriate font bank, etc.
* See the Wyse Programmers's Guide p.8-6 to 8-10 for how to generate
* characters and p.7-4 to 7-5 on how to assign font banks.  The
* secondary bank (bank 1 by defualt) seems to display as meta characters
* (8th bit set).
*
* 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 
nchars_coded = 0,
Bank = 1,	/* Bank of Wyse terminal to load font into.  Command line opt.*/
Cell_height,	/* = cell_height++ since index-origin = 0.  Read from header.*/
Cell_width,	/* = cell_width++ since index-origin = 0.  Read from header.*/
First_char = 0x20,	/* Hex no. of first character. Command line option */
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,
			char line[][]); /* error message*/
/*--------------------start of main------------------------------------------*/
void 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, "b:f:p:s") ) != EOF) {
	switch (ch) {
	case 'b': Bank = atoi(optarg);
		  fprintf(stderr, "Bank = %d \n", Bank);
		  break;
	case 'f': First_char = (int) strtol (optarg, (char **)NULL, 16);
		  fprintf(stderr, "First_char = %X hex. \n", First_char);
		  break;
	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 first 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 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(header_line1, stdout);	/* Header line at end of output file */
fputs(header_line2, stdout);	/* Header line at end of output file */
fprintf( stdout,"Bank = %d      ", Bank);
fprintf( stdout,"First_char = %x hex      ", First_char );
fprintf( stdout,"Pad_level = %d nulls/line \n", Pad_level);
fprintf (stderr,"Finished.  Encoded %d characters for a Wyse (native) 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.
*                ..*.*.. 		
*                .*...*.  <---------	Band[0][3] = 01000100 = 88 hex.
*                *.....* 		[0] is the 0th character matrix
*                *******		in the band of 8 chars (0-7)
*                *.....*		Col. 8 is absent (but assumed all dots) 
*                *.....*                88 hex is stored as one ascii char.
*                *.....* 
*                *.....*		band [char_no][row] 
*                *.....* 		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 1st letter has bits (0,0)-(0,6)	(0,0) is the low-order bit.
*     The 2nd letter has bits (1,0)-(1,6)
*     The 3rd letter has bits (2,0)-(2,6)
*     .........				
*     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, 			/* Loop index counter. */
 	i,			/* loop index counter used in padding 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 */
	line[8][Cell_width + 1];    /* for reading a line of input pat. file */
unsigned char
	letter,		/* letter encoded from a row of a pattern matrix */
	band[8][Cell_height];   /* A band of 8 pattern_chars. See drawing above.
				 * band array: band [char_no] [row]
				 * Element is the ascii char encoding for a row
				 */
/*--------------------------------start---------------------------------------*/
/* A band of 8 large_characters is read from the input file pointed to by fp 
 * and is put 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 ++) {
/*-------------------------------read a line----------------------------------*/
    for (char_no = 0; char_no <= 7; char_no ++) {
	nscanned = fscanf(fp, "%[^ \t\n]%*[ \t]", line[char_no]);
	if (nscanned != 1)
	    scan_err(nscanned, row);	/* print error message.  No exit      */
     }
    fgets(junk, sizeof(junk), fp);		/* seek to end of line	     */	
/*-------------------------encode line into band------------------------------*/
    for (char_no = 0 ; char_no <= 7 ; char_no ++) {
	bit_value = 128;
	letter = 0;						/* initialize */
	for (col = 0 ; col <= cell_width ; col ++) {
	    ch = line [char_no] [col];
	    if (ch == '*')
		letter += bit_value;
	      else if (ch != '.')
		char_read_err(ch, nchars_coded, char_no, row, col, line);
	    bit_value >>= 1;			/* halve bit_value */	
	 } 		    /* end for on cols.  Have a letter for a row*/
	band [char_no] [row] = letter;
     }		/* end for on char_no.  Have encoded row crossing 8 chars */
 }		/* end for on row.  Have encoded all rows (and completed band)*/
/*-----------------------write band to stdout---------------------------------*/
for (char_no = 0 ; char_no <= 7 ; char_no ++) {
    printf("\033cA%1d%02X", Bank, nchars_coded + First_char);
    for (row = 0; row <= cell_height; row ++)
	printf("%02X", band [char_no] [row]);
    putchar('\031');				/* ^Y for Wyse 60 terminals.  */
						/* Not needed for Wyse 99GT   */
/*--------------------------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 2nd for on char_no.  8 Pattern-chars done. */
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,
			char line[][Cell_width + 1]) {
fprintf(stderr, "ERROR in reading pattern file.  Found an illegal char: %c\
 (= Hex %X)\nin band %d the %d th character",
	ch, ch, nchars_coded / 8, char_no);
fprintf(stderr, ", located at row %d col %d of character matrix.\n", row, col); 
fprintf(stderr, " This error is within the last header line shown above.\n");
fprintf(stderr, "The offending line (white space becomes a single space) is:\
	\n%s %s %s %s %s %s %s %s \n",
	line[0], line[1], line[2], line[3], line[4], line[5], line[6], line[7]);

if (ch == '\0' )
    fprintf(stderr, "The null character found is likely not really in your 
source file but is a null (Hex 0) terminator for a string which is generated
 because there is a white-space character where a . or * should be.\n");
exit (-1);
}
/*****************************scan_err()***************************************/
void	scan_err(int nscanned, int row) {
fprintf(stderr, "***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\
\n row %d of current band (see last header line above) \n\n",
    nscanned, nchars_coded, row + 1);     /* Should exit w/char_read_err */
  }
/*********************************usage()**************************************/
void usage() {
fprintf(stderr, "Usage:  gfontv [-p 80]   -s | input_file \
   \n-b 2 => Bank 2 will get font (default = 1).\
   \n-f 0 => first character is 0 hex in the character code chart.  Default\
   \n   is 20 hex (=40 octal, = 16 decimal, = the <space> character in ascii)\
   \n-p 80 => append 80 (or whatever) padding nulls to each line of output.\
   \n-s use standard input.\n");
exit (-1);
}
