/*
 *  slpr -- small size print
 *
 *  revision history
 *	0.0: Mar. 14, 93 by Dai Ishijima
 *	0.7: Dec. 25, 95
 *	1.0: Feb.  9, 96
 *	1.1: Mar. 10, 96
 */

#include <stdio.h>
#include <string.h>
#ifndef NO_STDLIB_H
#include <stdlib.h>
#endif
#include "slpr.h"


#ifdef MSDOS
#include <fcntl.h>
#include <io.h>
#ifndef HAVE_FSETBIN
#define fsetbin(fp) setmode(fileno(fp), O_BINARY);
#endif
#endif

#ifdef PC98_PRTBIOS
#include "pb98.h"
#define putchr(c) lputc(c)
#define putstr(s) lputs(s)
#else
#define putchr(c) putc(c, stdout)
#define putstr(s) fputs(s, stdout)
#endif

#define ISKANJIL 0x01
#define ISKANJIR 0x02

#define NORM 0x00
#define DBLS 0x10	/* 重ね打ち */
#define UNDL 0x20	/* アンダーライン */
#define REVS 0x40	/* 反転 (まだ使えません ;^^) */

/* 文字コード */
#define EOS 0
#define CR 13
#define LF 10
#define HT 9
#define BS 8
#define VT 12

typedef struct {
    unsigned char code;
    unsigned char attr;
} charbuf;

charbuf pagebuf[LINES][COLUMNS];
unsigned char fontbuf[NCHARS][CHARHEIGHT];
int xoff, yoff, coff;

#define shift --argc; ++argv
char *prog;


/* 2バイト文字かどうか (シフトJIS漢字の1バイト目か) をチェック */
#define iskanji(ch) (((0x80 <= ch) && (ch < 0xa0)) || (0xe0 <= ch))


/* SJIS -> JIS にコード変換 */
int stoj(int ch1, int ch2)
{
    int x, y;

    if (ch1 >= 0xe0) {
	ch1 -= (0xe0 - 0xa0);
    }
    if (ch2 > 0x9e) {
	x = (ch1 - 0x80) * 2 + 0x20;
	y = ch2 + 0x20 - 0x9e;
    }
    else {
	x = (ch1 - 0x80) * 2 + 0x20 - 1;
	if (ch2 > 0x7f) {
	    --ch2;
	}
	y = ch2 - 0x1f;
    }
    return(x * 256 + y);
}


/* ページを空にする */
void clearpage()
{
    int x, y;
    extern charbuf pagebuf[LINES][COLUMNS];
    
    for (y = 0; y < LINES; y++) {
	for (x = 0; x < COLUMNS; x++) {
	    pagebuf[y][x].code = ' ';
	    pagebuf[y][x].attr = NORM;
	}
    }
}


#define isblank(c) (((c) == ' ') || ((c) == 0))


/* (x, y) に1バイト文字 cを置く */
void putank(int x, int y, int c)
{
    extern charbuf pagebuf[LINES][COLUMNS];

    if (pagebuf[y][x].code == c) {	/* すでに同じ文字があれば重ね打ち */
	pagebuf[y][x].attr |= DBLS;
    }
    else if (pagebuf[y][x].code == '_') { /* '_'ならアンダーライン */
	pagebuf[y][x].code = c;
	pagebuf[y][x].attr |= UNDL;
    }
    else if ((c == '_') && (!(isblank(pagebuf[y][x].code)))) {
	pagebuf[y][x].attr |= UNDL;
    }
    else {
	pagebuf[y][x].code = c;
    }
}


/* (x, y), (x + 1, y)  に2バイト文字を置く */
void putkanji(int x, int y, int c)
{
    extern charbuf pagebuf[LINES][COLUMNS];
    
    if (pagebuf[y][x].code == highbyte(c)) {
	pagebuf[y][x].attr |= DBLS;
	pagebuf[y][x + 1].attr |= DBLS;
    }
    else if (pagebuf[y][x].code == '_') {
	pagebuf[y][x].code = highbyte(c);
	pagebuf[y][x + 1].code = lowbyte(c);
	pagebuf[y][x].attr = ISKANJIL | UNDL;
	pagebuf[y][x + 1].attr = ISKANJIR | UNDL;
    }
    else {
	pagebuf[y][x].code = highbyte(c);
	pagebuf[y][x + 1].code = lowbyte(c);
	pagebuf[y][x].attr = ISKANJIL;
	pagebuf[y][x + 1].attr = ISKANJIR;
    }
}


/* ファイルから1ページ分読む */
int fillpage(FILE *fp)
{
    int n;
    int x, y;
    int ch, ch2;

    clearpage();
    n = x = y = 0;
    while ((y < LINES) && ((ch = getc(fp)) != EOF)) {
	if (iskanji(ch)) {
	    if (x >= COLUMNS - 2) { /* 行の端なら次の行に */
		++y;
		x = 0;
		if (y >= LINES) { /* 最下行の端なら次のページに */
		    ungetc(ch, fp);
		    break;
		}
	    }
	    ch2 = getc(fp);
	    putkanji(x, y, stoj(ch, ch2));
	    ++n;
	    x += 2;
	}
	/* コントロール文字の処理 */
	else if (ch == BS) {
	    --x;
	    if (x < 0) {
		x = 0;
	    }
	}
	else if (ch == CR) {
	    x = 0;
	}
	else if (ch == LF) {
	    x = 0;
	    ++y;
	}
	else if (ch == HT) {
	    x = ((x + TABSIZE) / TABSIZE) * TABSIZE;
	}
	else if (ch == VT) {
	    if ((x != 0) || (y != 0)) {
		y = LINES;
		x = 0;
	    }
	}
	/* 普通の文字 */
	else {
	    putank(x, y, ch);
	    ++n;
	    ++x;
	}
	if (x >= COLUMNS) {
	    ++y;
	    x = 0;
	}
    }
    return(n);
}


/* フォントバッファを空にする */
void clrfontbuf(void)
{
    int x, y;
    extern unsigned char fontbuf[NCHARS][CHARHEIGHT];

    for (x = 0; x < NCHARS; x++) {
	for (y = 0; y < CHARHEIGHT; y++) {
	    fontbuf[x][y] = 0;
	}
    }
}


/* 重ね打ち、アンダーラインの処理 */
void setattr(int attr, unsigned char *fontbuf)
{
    int y;

    if (attr & DBLS) {
	for (y = 0; y < FONTHEIGHT; y++) {
	    fontbuf[y + 1] |= (fontbuf[y + 1] << 1);
	}
    }
    else if (attr & UNDL) {
	fontbuf[0] = 0xff;
    }
}


/* (xx, yy)..(xx+NCHARS-1, yy) の文字のフォントを得る */
void setfont(int xx, int yy)
{
    int x, y;
    unsigned char *buf;
    extern charbuf pagebuf[LINES][COLUMNS];
    extern unsigned char fontbuf[NCHARS][CHARHEIGHT];

    clrfontbuf();
    for (x = 0; x < NCHARS; x++) {
	if (pagebuf[yy][xx + x].attr & ISKANJIR) {
	    if (x == 0) {
		buf = font(pagebuf[yy][xx + x - 1].code * 256
			   + pagebuf[yy][xx + x].code);
	    }
	    for (y = 0; y < FONTHEIGHT; y++) {
		fontbuf[x][CHARHEIGHT - y - 2] = buf[2 * y + 1];
	    }
	}
	else if (pagebuf[yy][xx + x].attr & ISKANJIL) {
	    buf = font(pagebuf[yy][xx + x].code * 256 
		       + pagebuf[yy][xx + x + 1].code);
	    for (y = 0; y < FONTHEIGHT; y++) {
		fontbuf[x][CHARHEIGHT - y - 2] = buf[2 * y];
	    }
	}
	else {
	    buf = font(pagebuf[yy][xx + x].code);
	    for (y = 0; y < FONTHEIGHT; y++) {
		fontbuf[x][CHARHEIGHT - y - 2] = buf[y];
	    }
	}
	setattr(pagebuf[yy][xx + x].attr, fontbuf[x]);
    }
}


#define isblanks(x,y) ( \
		isblank(pagebuf[y][x].code) \
		&& isblank(pagebuf[y][x + 1].code) \
		&& isblank(pagebuf[y][x + 2].code) \
	)


/* 1ページ分を出力する */
void showpage()
{
    int x, y;
    int xx, yy;
    extern charbuf pagebuf[LINES][COLUMNS];
    extern unsigned char fontbuf[NCHARS][CHARHEIGHT];
    extern int xoff;
    
    for (x = 0; x < COLUMNS; x += NCHARS) {
	putstr(SKIP);
	putchr(lowbyte(xoff));
	putchr(highbyte(xoff));
	for (y = LINES - 1; y >= 0; y--) {
	    if (isblanks(x, y)) {
		    putstr(SKIP);
		    putchr(CHARHEIGHT);
		    putchr(0);
		}
	    else {
		setfont(x, y);
		putstr(BITIMG);
		putchr(CHARHEIGHT);
		putchr(0);
		for (xx = 0; xx < CHARHEIGHT; xx++) {
		    for (yy = 0; yy < NCHARS; yy++) {
			putchr(fontbuf[yy][xx]);
		    }
		}
	    }
	}
	putchr(CR);
	putchr(LF);
    }
}


/* ファイルを縮小印刷する */
void smallprint(FILE *fp)
{
    int page;
    extern int yoff;
    extern int coff;
    
    putstr(PINIT);
    page = 0;
    while (fillpage(fp) > 0) {
	++page;
	if (page % 2 == 1) {
	    putstr(VSKIP);
	    putchr(yoff);
	}
	else {
	    putstr(VSKIP);
	    putchr(coff);
	}
	showpage();
	if (page % 2 == 0) {
	    putchr(VT);
	}
    }
    if (page % 2 == 1) {
	putchr(VT);
    }
}


void usage()
{
    fprintf(stderr, "Usage: %s [options] [file]...\n", prog);
    fputs("    options:\n", stderr);
    fputs("\t-xoff x offset\n", stderr);
    fputs("\t-yoff y offset\n", stderr);
    fputs("\t-coff center offset\n", stderr);
#ifndef PC98_ROMFONT
    fputs("\t-fk dbcs font\n", stderr);
    fputs("\t-fn sbcs font\n", stderr);
#endif    
}


int setval(char *s, int x)
{
    int y;

    sscanf(s, "%d", &y);
    if ((*s == '+') || (*s == '-')) {
	return(x + y);
    }
    else {
	return(y);
    }
}


void main(int argc, char *argv[])
{
    FILE *fp;
    extern int xoff;
    extern int yoff;
    extern int coff;
#ifndef PC98_ROMFONT    
    char *dbcsfont;
    char *sbcsfont;
#endif    
    
    prog = *argv;
    shift;
    xoff = M_LEF;
    yoff = M_TOP;
    coff = M_CEN;
#ifdef MSDOS
    fsetbin(stdout);
#endif
#ifndef PC98_ROMFONT    
    dbcsfont = DBCSFONT;
    sbcsfont = SBCSFONT;
#endif    
    while ((argc > 0) && (argv[0][0] == '-') && (argv[0][1] != EOS)) {
	if (strcmp(*argv, "-xoff") == 0) {
	    shift;
	    xoff = setval(*argv, xoff);
	}
	else if (strcmp(*argv, "-yoff") == 0) {
	    shift;
	    yoff = setval(*argv, yoff);
	}
	else if (strcmp(*argv, "-coff") == 0) {
	    shift;
	    coff = setval(*argv, coff);
	}
#ifndef PC98_ROMFONT
	else if (strcmp(*argv, "-fk") == 0) {
	    shift;
	    dbcsfont = *argv;
	}
	else if (strcmp(*argv, "-fn") == 0) {
	    shift;
	    sbcsfont = *argv;
	}
#endif	
	else {
	    usage();
	    exit(1);
	}
	shift;
    }
#ifdef PC98_PRTBIOS
    initpb();	/* initialize printer bios */
#endif
#ifndef PC98_ROMFONT
    initdbcs(dbcsfont);	/* initialize font cache */
    initsbcs(sbcsfont);
#endif
    if (argc > 0) {
	while (argc > 0) {
	    if (argv[0][0] == '-') {
		fp = stdin;
	    }
	    else if ((fp = fopen(*argv, "r")) == NULL) {
		fprintf(stderr, "%s: can't open %s\n", prog, *argv);
		exit(1);
	    }
	    smallprint(fp);
	    if (fp != stdin) {
		fclose(fp);
	    }
	    shift;
	}
    }
    else {
	smallprint(stdin);
    }
    exit(0);
}
