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: */