#include "dvitops.h"

static char rcsid[] = "$Header: /usr/jjc/dvitops/RCS/code.c,v 1.5 90/08/14 13:07:42 jjc Rel $";

static struct {
	char *name;
	int width;
	int height;
} paper_table[] = 
{
"letter", 612, 792,
"lettersmall", 612, 792,
"a4", 595, 842,
"A4", 595, 842,
"a4small", 595, 842,
"legal", 612, 1008,
"b5", 516, 729,
"b4", 729, 1032,
"tabloid", 792, 1224,
"ledger", 1224, 792,
"statement", 396, 612,
"executive", 540, 720,
"a3", 842, 1190,
"a5", 420, 595,
"folio", 612, 936,
"quarto", 610, 780,
"10x17", 720, 1224, 
NULL };

#define MAXCOLS 70
 
static FILE *psfp;
static int cols = 0; /* the number of characters on the current line */
 
#define NS 200
typedef struct string {
	size_t i; /* index into pool */
	int n; /* number of characters */
	int region;
	integer h;
	integer v;
	struct string *next;
} STRING;
 
struct rule {
	int region;
	integer a,b,h,v;
	struct rule *next;
} *rule_list = NULL;
 
static unsigned char *pool = NULL;
static size_t pool_size = 0, pool_used = 0;
 
STRING *string_list[MAXFONTS]; /* one list of strings for each font */
 
STRING *free_string_list;
 
#ifdef PROTO
void ps_string(unsigned char *s, int n);
void print_string_list(STRING *s);
static void print_rule_list(void);
#else
void ps_string();
void print_string_list();
static void print_rule_list();
#endif
 
static int page_number = 0;
int landscape = FALSE;
int magnification = 0;
 
void print_string_list(s)
STRING *s;
{
	int cc;
	integer  v;
	integer ox = 0, oy = 0; /* current origin */
	int r = NO_REGION; /* the current region */
	if (s == NULL)
		return;
	v = s->v + 1;  /* use the 'Z' command not the 'X' command */
	while (s != NULL) {
		if (s->region != r) {
			fprintf(psfp, "%% region %d\n", s->region);
			if (r != NO_REGION)
				fputs("grestore\n", psfp);
			if (s->region != NO_REGION) {
				fputs("gsave\n", psfp);
				do_transform(s->region, &ox, &oy, psfp);
			}
			else
				ox = oy = 0;
			r = s->region;
			v = s->v + 1; /* use the 'Z' command not the 'X' command */
		}
		if (s->v == v) {
			cols = put_dim((long)(s->h - ox), psfp);
			cc = 'X';
		}
		else {
			cols = put_dim((long)(s->h - ox), psfp);
			putc(' ', psfp);
			cols++;
			cols += put_dim((long)(s->v - oy), psfp);
			cc = 'Z';
		}
		v = s->v;
		ps_string(pool + s->i, s->n);
		putc(cc, psfp);
		putc('\n', psfp);
		s = s->next;
	}
	if (r != NO_REGION)
		fputs("grestore\n", psfp);
}
 
void set_string(p, n, f, h, v)
char *p;
int n, f;
integer h, v;
{
	STRING *s;
	if (!free_string_list) {
		int i;
		free_string_list = (STRING *)malloc(sizeof(STRING)*NS);
		if (free_string_list == NULL)
			out_of_memory();
		for (i = 0; i < NS - 1; ++i)
			free_string_list[i].next = free_string_list + i + 1;
		free_string_list[NS - 1].next = NULL;
	}
	s = free_string_list;
	free_string_list = free_string_list->next;
	if (pool == NULL || pool_size - pool_used < n) {
		if (pool == NULL) {
			pool_size = 5000;
			pool = (unsigned char *)malloc(pool_size);
			pool_used = 0;
		}
		else {
			if (pool_used > SIZE_MAX - n)
				message(FATAL_ERROR, "too many characters on the page");
			pool_size = pool_used + n;
			if (pool_size > SIZE_MAX/2)
				pool_size = SIZE_MAX;
			else
				pool_size <<= 1;
			pool = (unsigned char *)realloc((char *)pool, pool_size);
		}
		if (pool == NULL)
			out_of_memory();
	}
	s->i = pool_used;
	memcpy((char *)(pool + pool_used), p, n);
	pool_used += n;
	s->h = h;
	s->v = v;
	s->n = n;
	s->next = string_list[f];
	s->region = current_region;
	string_list[f] = s;
}
 
void set_rule(a, b, h, v)
integer a, b, h, v;
{
	struct rule *p;
	if (a <= 0 || b <= 0)
		return;
	p = (struct rule *)malloc(sizeof(struct rule));
	if (p == NULL)
		out_of_memory();
	p->a = a;
	p->b = b;
	p->h = h;
	p->v = v;
	p->region = current_region;
	p->next = rule_list;
	rule_list = p;
}
 
static void print_rule_list()
{
	int r = NO_REGION;
	integer ox = 0, oy = 0;
	struct rule *p = rule_list;
	while (p != NULL) {
		struct rule *q = p;
		if (p->region != r) {
			fprintf(psfp, "%% region %d\n", p->region);
			if (r != NO_REGION)
				fputs("grestore\n", psfp);
			if (p->region != NO_REGION) {
				fputs("gsave\n", psfp);
				do_transform(p->region, &ox, &oy, psfp);
			}
			else
				ox = oy = 0;
			r = p->region;
		}
		put_dim((long)(p->a), psfp);
		putc(' ', psfp);
		put_dim((long)(p->b), psfp);
		putc(' ', psfp);
		put_dim((long)(p->h - ox), psfp);
		putc(' ', psfp);
		put_dim((long)(p->v - oy), psfp);
		fputs(" R\n", psfp);
		p = p->next;
		free((char *)q);
	}
	rule_list = NULL;
	if (r != NO_REGION)
		fputs("grestore\n", psfp);
}
 
void eop(page)
struct page_info *page;
{
	char page_label[100];
	int i, j;
	char *ptr;
	int page_height, page_width;
	for (i = 0; paper_table[i].name != NULL; i++)
		if (strcmp(paper_table[i].name, page->paper) == 0)
			break;
	if (paper_table[i].name == NULL) {
		page_height = paper_table[0].height;
		page_width = paper_table[0].width;
		message(ERROR, "unrecognised page type %s", optarg);
	}
	else {
		page_height = paper_table[i].height;
		page_width = paper_table[i].width;
	}
	ptr = page_label;
	for (i = 9; page->count[i] == 0 && i > 0; --i)
		;
	for (j = 0; j < i; ++j) {
		sprintf(ptr, "%ld.", (long)(page->count[j]));
		ptr += strlen(ptr);
	}
	sprintf(ptr, "%ld", (long)page->count[i]);
	if (!quiet) 
		fprintf(stderr, "[%s", page_label);
	++page_number;
	fprintf(psfp, "\n%%%%Page: %s %d\n", page_label, page_number);
	fputs("dvitops begin\n", psfp);
	fprintf(psfp, "/#copies %d def BP\n", page->copies);
	p_form_list(psfp);
	if (landscape) {
		int n;
		fprintf(psfp, "%d landscape\n", page_width);
		n = page_height;
		page_height = page_width;
		page_width = n;
		landscape = FALSE;
	}
	if (magnification > 0) {
		page->mag = magnification;
		magnification = 0;
	}
	fprintf(psfp, "%ld ", (long)(page->num));
	put_dim((long)(page->den), psfp);
	fprintf(psfp, " %d %g %g %d SC\n", 
			page->mag, page->hoffset, page->voffset, page_height);
	p_inline_list(psfp);
	p_import_list(psfp, page);
#ifdef TPIC_SUPPORT
	p_tpic_list(psfp, page);
#endif
	print_rule_list();
	for (j = 0; j < MAXFONTS; ++j) {
		int emitted = FALSE;
		for (i = j; i != EOF; i = same_font(i)) {
			STRING *p = NULL;
			STRING *q = string_list[i];
			STRING *last = q;
			if (string_list[i] == NULL)
				continue;
			/* reverse the string list */
			while (q != NULL) {
				STRING *temp = q;
				q = q->next;
				temp->next = p;
				p = temp;
			}
			if (!f_is_blank(i)) {
				if (!emitted) {
					fputs("BO\n", psfp);
					f_emit(i, psfp);
					emitted = TRUE;
				}
				f_set(i, psfp);
				print_string_list(p);
			}
			last->next = free_string_list;
			free_string_list = p;
			string_list[i] = NULL;
		}
		if (emitted)
			fputs("EO\n", psfp);
	}
	pool_used = 0;
	fputs("\nEP end", psfp);
	if (ferror(psfp))
		message(FATAL_ERROR, "write error: disk full?");
	if (!quiet)
		fputs("] ", stderr);
	pool_used = 0;
	current_region = NO_REGION;
	nregions = 0;
}
 
void trailer()
{
	fputs("\n%%Trailer\n", psfp);
	if (!quiet)
		fputc('\n', stderr);
}
 
void prologue(fp, npages, is_reverse, paper)
FILE *fp;
int npages, is_reverse;
char *paper;
{
	time_t t;
	psfp = fp;
	(void)time(&t);
	fputs("%!PS-Adobe-2.0\n", psfp);
	fputs("%%Creator: dvitops\n", psfp);
	f_comment(psfp);
	fprintf(psfp, "%%%%CreationDate: %s", ctime(&t));
	fprintf(psfp, "%%%%DocumentPaperSizes: %s\n", paper);
	fprintf(psfp, "%%%%Pages: %d %d\n", npages, (is_reverse ? -1 : 1));
	fputs("%%EndComments\n", psfp);
	p_special_prologues(psfp);
	if (include_file("dvitops.pro", psfp) == EOF)
		message(FATAL_ERROR, "can't open dvitops.pro");
	fputs("dvitops begin\n", psfp);
	f_prologue(psfp);
	fputs("end\n", psfp);
	fputs("%%EndProlog\n", psfp);
	fprintf(psfp, "%%%%BeginSetup\n");
	fprintf(psfp, "%%%%PaperSize: %s\n", paper);
	fprintf(psfp, "%%%%EndSetup\n");
}


/* we asssume all seven-bit printable ascii characters are representable in the
   host character set */

#define ASCII_LEFT_PARENTHESIS 050
#define ASCII_RIGHT_PARENTHESIS 051
#define ASCII_BACKSLASH 0134

void ps_string(s, n)
unsigned char *s;
int n;
{
	cols++;
	putc('(', psfp);
	while (n--) {
		unsigned char c = *s++;
		if (c == ASCII_LEFT_PARENTHESIS 
			|| c == ASCII_RIGHT_PARENTHESIS 
			|| c == ASCII_BACKSLASH) {
			if (cols + 2 > MAXCOLS)
				cols = 0, fputs("\\\n", psfp);
			fputc('\\', psfp); fputc(XCHAR(c), psfp);
			cols += 2;
		}
		else if (c >= 040 && c < 0177) {
			if (cols + 1 > MAXCOLS)
				cols = 0, fputs("\\\n", psfp);
			putc(XCHAR(c), psfp);
			cols++;
		}
		else {
			if (cols + 4 > MAXCOLS)
				cols = 0, fputs("\\\n", psfp);
			fprintf(psfp, "\\%03o", c);
			cols += 4;
		}
	}
	putc(')', psfp);
}

static int fixed_point = 4;

#define LONG_DIGITS 10

int put_dim(n, fp)
long n;
FILE *fp;
{
	int len;
	/* room for a -, LONG_DIGITS digits, a decimal point,
	   and a terminating '\0' */
	char buf[LONG_DIGITS + 3];			
	char *p = buf + LONG_DIGITS + 2;
	int point = 0;
	buf[LONG_DIGITS + 2] = '\0';
	if (n >= 0) {
		do {
			*--p = '0' + (n % 10);
			n /= 10;
			if (++point == fixed_point)
				*--p = '.';
		} while (n != 0 || point < fixed_point);
	}
	else {						/* n < 0 */
		do {
			*--p = '0' - (n % 10);
			n /= 10;
			if (++point == fixed_point)
				*--p = '.';
		} while (n != 0 || point < fixed_point);
		*--p = '-';
	}
	if (fixed_point > 0) {
		char *q;
		/* there must be a dot, so this will terminate */
		for (q = buf + LONG_DIGITS + 2; q[-1] == '0'; --q)
			;
		if (q[-1] == '.') {
			if (q - 1 == p) {
				q[-1] = '0';
				q[0] = '\0';
			}
			else
				q[-1] = '\0';
		}
		else
			*q = '\0';
	}
	len = strlen(p);
	fputs(p, fp);
	return len;
}

/*
Local Variables:
tab-width: 4
c-indent-level: 4
c-continued-statement-offset: 4
c-brace-offset: -4
c-argdecl-indent: 0
c-label-offset: -4
End:
*/