static char rcsid[] = "$Header: /usr/jjc/dvitops/RCS/special.c,v 1.9 90/08/14 13:56:04 jjc Rel $";
 
#include "dvitops.h"
 
struct bounding_box { double llx, lly, urx, ury; };
 
#ifdef PROTO
static void do_import(char *arg, integer x, integer y,
	int region, struct page_info *page, FILE *psfp);
int strprefix(char *prefix, char *s);
static void read_import_file(char *filename);
#else
static void do_import();
int strprefix();
static void read_import_file();
#endif
 
/* this holds the list of files to be imported on the current page */
static struct import {
	integer x, y;
	int region;
	struct import *next;
	char arg[1];
} *import_list;
 
 
static struct import_file {
	struct bounding_box bb;
	struct depend_list *font;
	struct import_file *next;
	char file[1];
} *doc_import_list;
 
/* this routine is used to include a PostScript file
it will be enhanced later to be quicker and possibly to support
%%IncludeFont and %%IncludeFile comments 
return 0 on success, -1 on error
*/
 
int include_file(filename, psfp)
char *filename;
FILE *psfp;
{
	char buf[256];
	FILE *infp;
	if ((infp = xfopen(filename, FALSE, texinputs, (char *)NULL)) == NULL)
		return EOF;
#ifdef HAVE_SETVBUF
	setvbuf(infp, (char *)NULL, _IOFBF, 16384);
#endif
	while (fgets(buf, sizeof(buf), infp) != NULL)
		if (buf[0] != '%')
			fputs(buf, psfp);
	fclose(infp);
	return 0;
}
 
/* return 0 on error */
 
int bigpoint(str, ptr, val)
char *str, **ptr;
double *val;
{
	double x;
	int n;
	while (isspace(*str))
		++str;
	if (sscanf(str, "%lf", &x) != 1)
		goto bad;
	while (isdigit(*str) || *str == '.')
		str++;
	while (isspace(*str))
		str++;
	n = isupper(str[0]) ? tolower(str[0]) : str[0];
	n <<= 8;
	n |= isupper(str[1]) ? tolower(str[1]) : str[1];
	switch (n) {
	case ('b' << 8) + 'p' :
		break;
	case ('i' << 8) + 'n' :
		x *= 72.0;
		break;
	case ('c' << 8) + 'm' :
		x *= 72.0/2.54;
		break;
	case ('m' << 8) + 'm' :
		x *= 72.0/25.4;
		break;
	case ('s' << 8) + 'p' :
		x *= 72.0/(72.27*65536.0);
		break;
	case ('c' << 8) + 'c' :
		x *= 12.0*1238.0*72.0/(1157.0*72.27);
		break;
	case ('d' << 8) + 'd' :
		x *= 1238.0*72.0/(1157.0*72.27);
		break;
	case ('p' << 8) + 't' :
		x *= 72.0/72.27;
		break;
	case ('p' << 8) + 'c' :
		x *= 12.0*72.0/72.27;
		break;
	default:
		goto bad;
	}
	str += 2;
	if (ptr != NULL)
		*ptr = str;
	*val = x;
	return 1;
bad:
	if (ptr != NULL)
		*ptr = str;
	return 0;
}
 
static void do_import(arg, x, y, region, page, psfp)
char *arg;
integer x, y;
int region;
struct page_info *page;
FILE *psfp;
{
	double xscale, yscale;
	integer gap;
	double h, w;
	integer width, height;
	char *filename;
	struct import_file *p;
	struct depend_list *q;
	struct bounding_box bb;
	integer llx, lly, urx, ury;
	double sf = (double)page->den*254000.0/((double)page->num*72.0);
	char *ptr = arg;
	typedef enum { CENTER, LEFT, RIGHT, TOP, BOTTOM, FILL } adjust_t;
	adjust_t hadj = CENTER, vadj = CENTER;
	while (isspace(*ptr))
		ptr++;
	filename = ptr;
	while (!isspace(*ptr) && *ptr != '\0')
		ptr++;
	if (*ptr == '\0') {
		message(ERROR, "import \\special syntax error: abandoning import");
		return;
	}
	*ptr++ = '\0';
	if (bigpoint(ptr, &ptr, &w) == 0
			|| bigpoint(ptr, &ptr, &h) == 0) {
		message(ERROR, "special import syntax error: abandoning import");
		return;
	}
	ptr = strtok(ptr, "\r\n\t ");
	while (ptr != NULL) {
		int i;
		for (i = 0; ptr[i] != '\0'; i++)
			ptr[i] = isupper(ptr[i]) ? tolower(ptr[i]) : ptr[i];
		if (strcmp(ptr, "top") == 0)
			vadj = TOP;
		else if (strcmp(ptr, "bottom") == 0)
			vadj = BOTTOM;
		else if (strcmp(ptr, "fill") == 0)
			hadj = vadj = FILL;
		else if (strcmp(ptr, "left") == 0)
			hadj = LEFT;
		else if (strcmp(ptr, "right") == 0)
			hadj = RIGHT;
		else
			message(ERROR, "bad option in import \\special: %s: ignored",ptr);
		ptr = strtok((char *)NULL, " \r\n\t");
	}
	/* convert to dvi units */
	width = (integer)(w*sf);
	height = (integer)(h*sf);
	for (p = doc_import_list; p != NULL; p = p->next)
		if (strcmp(p->file, filename) == 0)
			break;
	if (p == NULL) {
		message(ERROR, "can't import %s", filename);
		return;
	}
	fputs("BO /showpage {} def\n", psfp);
	for (q = p->font; q != NULL; q = q->next)
		if (!q->f->in_prolog)
			emit_ps_font(q->f->s, psfp);
	if (region != NO_REGION) {
		integer ox = 0, oy = 0;
		do_transform(region, &ox, &oy, psfp);
		x -= ox;
		y -= oy;
	}
	bb = p->bb;
	if (bb.urx == bb.llx) {
		message(ERROR, "bad bounding box: no width");
		return;
	}
	if (bb.ury == bb.lly) {
		message(ERROR, "bad bounding box: no height");
		return;
	}
	xscale = width/(bb.urx - bb.llx);
	yscale = height/(bb.ury - bb.lly);
	if (xscale > yscale) {
		lly = y;
		ury = y - height;
		gap = (integer)(width - yscale*(bb.urx - bb.llx));
		switch(hadj) {
		case CENTER :
			llx = x + gap/2;
			urx = x + width - gap/2;
			break;
		case LEFT :
			llx = x;
			urx = x + width - gap;
			break;
		case RIGHT :
			llx = x + gap;
			urx = x + width;
			break;
		case FILL :
			llx = x;
			urx = x + width;
			break;
		default :
			cant_happen();
		}
	}
	else {
		llx = x;
		urx = x + width;
		gap = (integer)(height - xscale*(bb.ury - bb.lly));
		switch(vadj) {
		case CENTER :
			lly = y - gap/2;
			ury = y - height + gap/2;
			break;
		case TOP :
			lly = y - gap;
			ury = y - height;
			break;
		case BOTTOM :
			lly = y;
			ury = y - height + gap;
			break;
		case FILL :
			lly = y;
			ury = y - height;
			break;
		default :
			cant_happen();
		}
	}
	fprintf(psfp, "%lg %lg %lg %lg ", bb.llx, bb.lly, bb.urx, bb.ury);
	
	put_dim((long)llx, psfp);
	putc(' ', psfp);
	put_dim((long)lly, psfp);
	putc(' ', psfp);
	put_dim((long)urx, psfp);
	putc(' ', psfp);
	put_dim((long)ury, psfp);
	fputs(" Locate\n", psfp);
	fprintf(psfp, "%%%%BeginDocument: %s\n", filename);
	if (include_file(filename, psfp) == EOF)
		message(ERROR, "can't find import file %s: abandoning import",
			filename);
	fputs("\n%%EndDocument\nEO\n", psfp);
}
 
/* this is called at the end of each page by eop to deal with
any import specials on the current page; it calls do_import to do
the real work */
 
void p_import_list(psfp, page)
FILE *psfp;
struct page_info *page;
{
	if (import_list == NULL)
		return;
	while (import_list != NULL) {
		struct import *q = import_list;
		do_import(import_list->arg, import_list->x, import_list->y, 
				 import_list->region, page, psfp);
		import_list = import_list->next;
		free((char *)q);
	}
}
 
 
 
/* this holds a list of all the pieces of inline code for the current page */
 
static struct inline_code {
	int region;
	integer h, v;
	struct inline_code *next;
	char s[1];
} *inline_list = NULL;
 
/* this is called by eop at the end of every page to emit any inline
specials */
 
void p_inline_list(psfp)
FILE *psfp;
{
	struct inline_code *p = NULL;
	integer ox = 0, oy = 0;
	int r = NO_REGION;
	/* reverse the list */
	if (inline_list == NULL)
		return;
	fputs("BO\n", psfp);
	while (inline_list != NULL) {
		struct inline_code *q = inline_list;
		inline_list = inline_list->next;
		q->next = p;
		p = q;
	}
	while (p != NULL) {
		struct inline_code *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->h-ox), psfp);
		putc(' ', psfp);
		put_dim((long)(p->v-oy), psfp);
		fputs(" M\n", psfp);
		fputs("gsave\n", psfp);
		put_dim(1L, psfp);
		fputs(" dup scale\n", psfp);
		fputs(p->s, psfp);
		putc('\n', psfp);
		fputs("grestore\n", psfp);
		p = p->next;
		free((char *)q);
	}
	inline_list = NULL;
	if (r != NO_REGION)
		fputs("grestore\n", psfp);
	fputs("EO\n", psfp);
}
/* this list is used during pass 1 to hold a list of all prologue files */
 
struct string_list {
	struct string_list *next;
	char s[1];
};
 
static struct string_list *prologue_list;
 
/* this is called by special during pass 1 to add a prologue file to 
prologue_list */
 
 
/* this is called while the prologue is being written to include all the
prologue files that have been specified in the document */
 
void p_special_prologues(psfp)
FILE *psfp;
{
	while (prologue_list != NULL) {
		struct string_list *temp = prologue_list;
		if (include_file(prologue_list->s, psfp) == EOF)
			message(ERROR, "can't open prologue file %s: ignoring it", 
					prologue_list->s);
		prologue_list = prologue_list->next;
		free((char *)temp);
	}
}
 
#ifdef PROTO 
typedef void special_t(int pass, char *arg, integer x, integer y);
static special_t landscape_special;
static special_t magnification_special;
static special_t prolog_special;
static special_t inline_special;
static special_t import_special;
static special_t rotate_special;
static special_t transform_special;
static special_t origin_special;
static special_t begin_special;
static special_t end_special;
static special_t form_special;
static special_t hsbcolor_special;
static special_t rgbcolor_special;
static special_t gray_special;
#else
static void landscape_special();
static void magnification_special();
static void prolog_special();
static void inline_special();
static void import_special();
static void rotate_special();
static void transform_special();
static void origin_special();
static void begin_special();
static void end_special();
static void form_special();
static void hsbcolor_special();
static void rgbcolor_special();
static void gray_special();
#endif
 
 
static struct {
	char *name;
#ifdef PROTO
	special_t *proc;
#else
	void (*proc)();
#endif
} special_table[] = {
	"landscape", landscape_special,
	"magnification", magnification_special,
	"prolog", prolog_special,
	"inline", inline_special,
	"import", import_special,
	"rotate", rotate_special,
	"transform", transform_special,
	"origin", origin_special,
	"begin", begin_special,
	"end", end_special,
	"form", form_special,
	"rgbcolor", rgbcolor_special,
	"hsbcolor", hsbcolor_special,
	"gray", gray_special,
	NULL
};
 
static void landscape_special(pass, arg, x, y)
int pass;
char *arg;
integer x, y;
{
	if (pass == PASS2)
		landscape = TRUE;
}

static void magnification_special(pass, arg, x, y)
int pass;
char *arg;
integer x, y;
{
	if (pass == PASS2) {
		int n;
		n = atoi(arg);
		if (n <= 0)
			message(ERROR, "bad magnification");
		else
			magnification = n;
	}
}
 

static void prolog_special(pass, arg, x, y)
int pass;
char *arg;
integer x, y;
{
	char *p = arg;
	struct string_list *ptr;
	if (pass != PASS1)
		return;
	while (!isspace(*p) && *p != '\0')
		p++;
	*p = '\0';
	for (ptr = prologue_list; ptr != NULL; ptr = ptr->next)
		if (strcmp(arg, ptr->s) == 0)
			return;
	if ((ptr = (struct string_list *)
			malloc(sizeof(struct string_list) + strlen(arg))) == NULL)
		out_of_memory();
	strcpy(ptr->s, arg);
	ptr->next = prologue_list;
	prologue_list = ptr;
}
 
	
static void inline_special(pass, arg, x, y)
int pass;
char *arg;
integer x;
integer y;
{
	struct inline_code *p;
	if (pass != PASS2)
		return;
	p = (struct inline_code *)
	  malloc(sizeof(struct inline_code) + strlen(arg));
	if (p == NULL)
		out_of_memory();
	p->h = x;
	p->v = y;
	p->region = current_region;
	strcpy(p->s, arg);
	p->next = inline_list;
	inline_list = p;
}
 
 
 
static void import_special(pass, arg, x, y)
int pass;
char *arg;
integer x, y;
{
	if (pass == PASS2) {
		struct import *p;
		if ((p = (struct import *)
				malloc(sizeof(struct import)+strlen(arg))) == NULL)
			out_of_memory();
		p->x = x;
		p->y = y;
		p->region = current_region;
		strcpy(p->arg, arg);
		p->next = import_list;
		import_list = p;
	}
	else
		read_import_file(strtok(arg, " \n\t\r"));
}
		
/* x and y are 0 on PASS1 */
 
void special(pass, s, x, y)
int pass;
char *s;
integer x, y;
{
	char *q;
	int i;
	char *p = s;
	if (pass != PASS1 && pass != PASS2)
		cant_happen();
#ifdef TPIC_SUPPORT
	if (tpic_special(pass, s, x, y))
		return;
#endif
	while (isspace(*p))
		p++;
	q = p;
	while (*q != '\0' && *q != ':') {
		*q = isupper(*q) ? tolower(*q) : *q;
		q++;
	}
	if (*q == '\0') {
		if (pass == PASS2)
			message(WARNING, 
				"a \\special without the `dvitops:' tag was ignored");
		return;
	}
	*q++ = '\0';
	if (strcmp(p, "dvitops") != 0) {
		if (pass == PASS2)
			message(WARNING, 
				"a \\special without the `dvitops:' tag was ignored");
		return;
	}
	while (isspace(*q))
		q++;
	p = q;
	while (*q != '\0' && !isspace(*q)) {
		*q = isupper(*q) ? tolower(*q) : *q;
		q++;
	}
	if (*q != '\0')
		*q++ = '\0';
	for (i = 0; special_table[i].name != NULL; i++)
		if (strcmp(p, special_table[i].name) == 0) {
			(*special_table[i].proc)(pass, q, x, y);
			return;
		}
	if (pass == PASS2)
		message(ERROR, "\\special keyword not recognised: %s", p);
}
 
 
#ifndef M_PI
#define M_PI 3.14159265358979324
#endif
 
#define REGION_MAX 256
#define RNAME_MAX 128
static char *WS = " \n\r\t";
/* we represent a two-dimensional transformation with the `matrix' 
structure; it represents the matrix
|a	b |
|c	d |
*/
 
typedef struct {
	double a, b, c, d;
} matrix_t;
 
 
enum color_type { NO_COLOR, RGB_COLOR, HSB_COLOR, GRAY_COLOR };

typedef struct  {
	char name[RNAME_MAX];
	integer origin_x;
	integer origin_y;
	matrix_t matrix;
	enum color_type color_type;
	double color[3];
} region_t;
 
static region_t *region[REGION_MAX];
#ifdef PROTO
static int lookup_region(char *name);
static void multiply(matrix_t *m, matrix_t *n, matrix_t *mn);
#else
static int lookup_region();
static void multiply();
#endif 
 
/* m, n and mn must all be distinct */

static void multiply(m, n, mn)
matrix_t *m, *n, *mn;
{
	mn->a = m->a*n->a + m->b*n->c;
	mn->b = m->a*n->b + m->b*n->d;
	mn->c = m->c*n->a + m->d*n->c;
	mn->d = m->c*n->b + m->d*n->d;
}
 
 
static int lookup_region(name)
char *name;
{
	int i;
	for (i = 0; i < nregions; i++) {
		if (region[i] == NULL)
			cant_happen();
		if (strncmp(region[i]->name, name, RNAME_MAX) == 0)
			return i;
	}
	if (nregions >= REGION_MAX)
		message(FATAL_ERROR, "too many regions");
	if (region[nregions] == NULL
			&& (region[nregions] = (region_t *)
							malloc(sizeof(region_t))) == NULL)
		out_of_memory();
	strncpy(region[nregions]->name, name, RNAME_MAX);
	region[nregions]->origin_x = 0;
	region[nregions]->origin_y = 0;
	region[nregions]->matrix.a = 1.0;
	region[nregions]->matrix.b = 0.0;
	region[nregions]->matrix.c = 0.0;
	region[nregions]->matrix.d = 1.0;
	region[nregions]->color_type = NO_COLOR;
	return nregions++;
}
 
 
static void rotate_special(pass, arg, x, y)
int pass;
char *arg;
integer x, y;
{
	char name[128];
	int n;
	double theta;
	matrix_t m;
	matrix_t temp;
	if (pass != PASS2)
		return;
	if (sscanf(arg, "%127s %lf", name, &theta) != 2) {
		message(ERROR, "rotate \\special syntax error");
		return;
	}
	n = lookup_region(name);
	theta *= M_PI/180.0;
	m.a = cos(theta);
	m.b = sin(theta);
	m.c = -sin(theta);
	m.d = cos(theta);
	multiply(&(region[n]->matrix), &m, &temp);
	memcpy((char *)&(region[n]->matrix), (char *)&temp, sizeof(matrix_t));
}
 
 
static void transform_special(pass, arg, x, y)
int pass;
char *arg;
integer x, y;
{
	char name[128];
	int n;
	matrix_t m, temp;
	if (pass != PASS2)
		return;
	if (sscanf(arg, " %127s %lf %lf %lf %lf", name,
			&m.a, &m.b, &m.c, &m.d) != 5) {
		message(ERROR, "transform \\special syntax error");
		return;
	}
	n = lookup_region(name);
	multiply(&(region[n]->matrix), &m, &temp);
	memcpy((char *)&(region[n]->matrix), (char *)&temp, sizeof(matrix_t));
}

static void rgbcolor_special(pass, arg, x, y)
int pass;
char *arg;
integer x, y;
{
	double color[3];
	char name[128];
	int i, n;
	int bad = FALSE;
	if (pass != PASS2)
		return;
	if (sscanf(arg, "%127s %lf %lf %lf", name, color, color + 1, color + 2)
		!= 4) {
		message(ERROR, "rgbcolor \\special syntax error");
		return;
	}
	n = lookup_region(name);
	for (i = 0; i < 3 && !bad; i++)
		if (color[i] < 0.0 || color[i] > 1.0) {
			bad = TRUE;
			message(ERROR, "color parameter must be between 0.0 and 1.0");
		}
		else
			region[n]->color[i] = color[i];
	region[n]->color_type = bad ? NO_COLOR : RGB_COLOR;
}

static void hsbcolor_special(pass, arg, x, y)
int pass;
char *arg;
integer x, y;
{
	double color[3];
	char name[128];
	int i, n;
	int bad = FALSE;
	if (pass != PASS2)
		return;
	if (sscanf(arg, "%127s %lf %lf %lf", name, color, color + 1, color + 2)
		!= 4) {
		message(ERROR, "rgbcolor \\special syntax error");
		return;
	}
	n = lookup_region(name);
	for (i = 0; i < 3 && !bad; i++)
		if (color[i] < 0.0 || color[i] > 1.0) {
			bad = TRUE;
			message(ERROR, "color parameter must be between 0.0 and 1.0");
		}
		else
			region[n]->color[i] = color[i];
	region[n]->color_type = bad ? NO_COLOR : RGB_COLOR;
}

static void gray_special(pass, arg, x, y)
int pass;
char *arg;
integer x, y;
{
	double gray;
	char name[128];
	int n;
	if (pass != PASS2)
		return;
	if (sscanf(arg, "%127s %lf", name, &gray) != 2) {
		message(ERROR, "gray \\special syntax error");
		return;
	}
	n = lookup_region(name);
	if (gray < 0.0 || gray > 1.0) {
		message(ERROR, "gray parameter must be between 0.0 and 1.0");
		region[n]->color_type = NO_COLOR;
	}
	else {
		region[n]->color[0] = gray;
		region[n]->color_type = GRAY_COLOR;
	}
}


static void origin_special(pass, arg, x, y)
int pass;
char *arg;
integer x, y;
{
	int n;
	if (pass != PASS2)
		return;
	arg = strtok(arg, WS);
	if (arg == NULL)
		message(ERROR, "origin \\special syntax error");
	n = lookup_region(arg);
	if (region[n]->origin_x != 0 || region[n]->origin_y != 0)
		message(WARNING, "multiple origins for object %s", arg);
	region[n]->origin_x = x;
	region[n]->origin_y = y;
}
 
 
 
static void begin_special(pass, arg, x, y)
int pass;
char *arg;
integer x, y;
{
	if (pass != PASS2)
		return;
	arg = strtok(arg, WS);
	if (current_region != NO_REGION)
		message(ERROR, "can't have  nested begin \\special");
	current_region = lookup_region(arg);
}
 
static void end_special(pass, arg, x, y)
int pass;
char *arg;
integer x, y;
{
	if (pass != PASS2)
		return;
	if (current_region == NO_REGION)
		message(ERROR, "end with no begin");
	else
		current_region = NO_REGION;
}
 
void do_transform(n, origin_x, origin_y, psfp)
int n;
integer *origin_x, *origin_y;
FILE *psfp;
{
	if (n >= nregions)
		message(FATAL_ERROR, "too many regions");
	*origin_x = region[n]->origin_x;
	*origin_y = region[n]->origin_y;
	if (*origin_x != 0 || *origin_y != 0) {
		put_dim((long)*origin_x, psfp);
		putc(' ', psfp);
		put_dim((long)*origin_y, psfp);
		fputs(" translate\n", psfp);
	}
	fprintf(psfp, "[%g %g %g %g 0 0] concat\n",
		region[n]->matrix.a, region[n]->matrix.b,
		region[n]->matrix.c, region[n]->matrix.d);
	switch(region[n]->color_type) {
	case GRAY_COLOR:
		fprintf(psfp, "%g setgray\n", region[n]->color[0]);
		break;
	case RGB_COLOR:
		fprintf(psfp, "%g %g %g setrgbcolor\n", region[n]->color[0],
				region[n]->color[1], region[n]->color[2]);
		break;
	case HSB_COLOR:
		fprintf(psfp, "%g %g %g sethsbcolor\n", region[n]->color[0],
				region[n]->color[1], region[n]->color[2]);
		break;
	case NO_COLOR:
		break;
	default:
		cant_happen();
	}
}
 
int strprefix(prefix, s)
char *prefix, *s;
{
	while (*prefix != '\0')
		if (*prefix++ != *s++)
			return FALSE;
	return TRUE;
}
 
/* this needs rewriting */

static void read_import_file(filename)
char *filename;
{
	FILE *fp;
	char buf[256];
	struct import_file *p;
	struct depend_list *q;
	int atend = FALSE, had_bb = FALSE, in_body = FALSE;
	int in_trailer = FALSE;
	p = (struct import_file *)
			malloc(sizeof(struct import_file) + strlen(filename));
	if (p == NULL)
		out_of_memory();
	strcpy(p->file, filename);
	p->font = NULL;
	p->next = doc_import_list;
	if ((fp = xfopen(filename, FALSE, texinputs, (char *)NULL)) == NULL) {
		message(ERROR, "can't open %s", filename);
		return;
	}
	if (fgets(buf, sizeof(buf), fp) == NULL) {
		message(WARNING, "empty import file %s", filename);
		fclose(fp);
		return;
	}
	if (!strprefix("%!", buf)) {
		message(ERROR, "%s not PostScript: doesn't start with %%!", filename);
		fclose(fp);
		return;
	}
	if (fgets(buf, sizeof(buf), fp) == NULL) {
		message(WARNING, "empty import file %s", filename);
		fclose(fp);
		return;
	}
	for (;;) {
		char *k;
		k = strtok(buf + 2, ": \t\r\n");
		if (k == NULL || !strprefix("%%", buf) 
				|| strcmp(k, "EndComments") == 0) {
			if (atend) {
				in_body = TRUE;
			}
			else
				break;
		}
		else if (in_body && !in_trailer) {
			if (strcmp(k, "Trailer") == 0)
				in_trailer = TRUE;
		}
		else if (strcmp(k, "DocumentFonts") == 0) {
			for (;;) {
				struct postscript_f_list *f;
				char *ptr = strtok((char *)NULL, " \t\r\n");
				if (ptr != NULL && strcmp(ptr, "(atend)") == 0) {
					atend = TRUE;
					break;
				}
				while (ptr == NULL) {
					if (fgets(buf, sizeof(buf), fp) == NULL) {
						buf[0] = '\0';
						goto out;
					}
					if (memcmp(buf, "%%+", 3) != 0)
						goto out;
					ptr = strtok(buf+3, "\n\r\t ");
				}
				f = add_postscript_font(ptr);
				q = (struct depend_list *)malloc(sizeof(struct depend_list));
				q->f = f;
				q->next = p->font;
				p->font = q;
			}
			out: continue; /* don't read a line */
		}
		else if (strcmp(k, "BoundingBox") == 0) {
			int i;
			for (i = 0; i < 4; i++) {
				char *ptr = strtok((char *)NULL, "\r\t\n ");
				double x;
				if (ptr == NULL) {
					message(ERROR, "BoundingBox comment empty");
					fclose(fp);
					return;
				}
				if (i == 0 && strcmp(ptr, "(atend)") == 0) {
					atend = TRUE;
					break;
				}
				had_bb = TRUE;
				x = atof(ptr);
				switch(i) {
				case 0:
					p->bb.llx = x;
					break;
				case 1:
					p->bb.lly = x;
					break;
				case 2:
					p->bb.urx = x;
					break;
				case 3:
					p->bb.ury = x;
					break;
				default:
					cant_happen();
				}
			}
		}
		if (fgets(buf, sizeof(buf), fp) == NULL)
			break;
	}
	if (!had_bb) {
		message(ERROR, "%s: no BoundingBox comment", filename);
		fclose(fp);
		return;
	}
	doc_import_list = p;
	fclose(fp);
}
 
static struct string_list *form_list;
 
static void form_special(pass, arg, x, y)
int pass;
char *arg;
integer x;
integer y;
{
	struct string_list *s;
	if (pass != PASS2)
		return;
	if ((s = (struct string_list *)malloc(sizeof(struct string_list) + strlen(arg))) == NULL)
		out_of_memory();
	s->next = form_list;
	form_list = s;
	strcpy(s->s, arg);
}
 
void p_form_list(psfp)
FILE *psfp;
{
	while (form_list != NULL) {
		struct string_list *s = form_list;
		fputs("/showpage {} def\n", psfp);
		fprintf(psfp, "/--save-- save def\n%%%%BeginDocument: %s\n", s->s);
		if (include_file(s->s, psfp) == EOF)
			message(ERROR, "can't find %s", s->s);
		fputs("\n%%EndDocument\n--save-- restore\n", psfp);
		form_list = s->next;
		free((char *)s);
	}
}

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