/* * This is a set of routines for dvips that are used to process color * commands in the TeX file (passed by \special commands). This was * orignally written by J. Hafner, E. Blanz and M. Flickner of IBM * Research, Almaden Research Center. Contact hafner@almaden.ibm.com. * And then it was completely rewritten by Tomas Rokicki to: * * - Be easier on the memory allocator (malloc/free) * - Use less memory overall (by a great deal) and be faster * - Work with the -C, -a, and other options * - Be more adaptable to other drivers and previewers. * * The motivating idea: we want to be able to process 5,000 page * documents using lots of colors on each page on a 640K IBM PC * relatively quickly. */ #include "dvips.h" /* The copyright notice in that file is included too! */ #include /* * Externals we use. */ #include "protos.h" extern integer pagenum ; extern FILE *dvifile ; /* * Here we set some limits on some color stuff. */ #define COLORHASH (89) #define MAXCOLORLEN (120) /* maximum color length for background */ #define INITCOLORLEN (3000) /* initial stack size in chars */ /* * This is where we store the color information for a particular page. * If we free all of these, we free all of the allocated color * stuff; we do no allocations (but one) other than when we allocate * these. */ static struct colorpage { struct colorpage *next ; integer boploc ; /* we use the bop loc as a page indicator */ char *bg ; char colordat[2] ; } *colorhash[COLORHASH] ; static char *cstack, *csp, *cend, *bg ; /* * This routine sends a color command out. If the command is a * single `word' or starts with a double quote, we send it out * exactly as is (except for any double quote.) If the command * is a word followed by arguments, we send out the arguments and * then the word prefixed by "TeXcolor". */ void colorcmdout(char *s) { char *p ; char tempword[100] ; while (*s && *s <= ' ') s++ ; if (*s == '"') { cmdout(s+1) ; return ; } for (p=s; *p && *p > ' '; p++) ; for (; *p && *p <= ' '; p++) ; if (*p == 0) { cmdout(s) ; return ; } cmdout(p) ; strcpy(tempword, "TeXcolor") ; for (p=tempword + strlen(tempword); *s && *s > ' '; p++, s++) *p = *s ; *p = 0 ; cmdout(tempword) ; return ; } /* * For a new dvi file, call this. Frees all allocated memory. */ #define DEFAULTCOLOR "Black" void initcolor(void) { int i ; struct colorpage *p, *q ; for (i=0; inext ; free(p) ; } colorhash[i] = 0 ; } cstack = (char *)mymalloc(INITCOLORLEN) ; strcpy(cstack, "\n") ; strcat(cstack, DEFAULTCOLOR) ; csp = cstack + strlen(cstack) ; cend = cstack + INITCOLORLEN - 3 ; /* for conservativeness */ bg = 0 ; } /* * This takes a call from predospecial to set the background color for * the current page. It is saved in stackdepth and backed down the * stack during popcolors. */ void background(char *bkgrnd) { if (bkgrnd && *bkgrnd) { if (strlen(bkgrnd) > MAXCOLORLEN) error(" color name too long; ignored") ; else strcpy(bg, bkgrnd) ; } } /* * This routine puts a call from \special for color on the colorstack * and sets the color in the PostScript. */ void pushcolor(char *p, Boolean outtops) { while (strlen(p) + csp > cend) { int newlen = 3 * (cend - cstack) ; char *newcs = (char *)mymalloc(newlen) ; strcpy(newcs, cstack) ; csp = newcs + (csp - cstack) ; cend = newcs + newlen - 3 ; cstack = newcs ; } *csp++ = '\n' ; strcpy(csp, p) ; csp += strlen(p) ; if (outtops) { colorcmdout(p) ; } } /* * This routine removes a color from the colorstack and resets the color * in the PostScript to the previous color. */ void popcolor(Boolean outtops) { char *p = csp - 1 ; while (p >= cstack && *p != '\n') p-- ; if (p == cstack) return ; /* We don't pop the last color as that is global */ *p = 0 ; csp = p ; for (p--; p >= cstack && *p != '\n'; p--) ; p++ ; if ( outtops ) { colorcmdout(p) ; } } /* * This routine clears the colorstack, pushes a new color onto the stack * (this is now the root or global color). */ void resetcolorstack(char * p, int outtops) { char *q = csp - 1 ; while (q > cstack && *q != '\n') q-- ; if (q > cstack && outtops == 0) { #ifdef SHORTINT (void)fprintf(stderr, "You've mistakenly made a global color change ") ; (void)fprintf(stderr, "to %s within nested colors\n", p) ; (void)fprintf(stderr, "on page %ld. Will try to recover.\n", pagenum) ; #else /* ~SHORTINT */ (void)fprintf(stderr, "You've mistakenly made a global color change ") ; (void)fprintf(stderr, "to %s within nested colors\n", p) ; (void)fprintf(stderr, "on page %d. Will try to recover.\n", pagenum) ; #endif /* ~SHORTINT */ } csp = cstack ; *csp = 0 ; pushcolor(p, outtops) ; } /* * This routine is a bit magic. It looks up the page in the current * hash table. If the page is already entered in the hash table, then * it restores the color to what that page had, and sets the last * color. This will occur if this is not the first time that this * page has been encountered. * * If, on the other hand, this is the first time that the page has * been encountered, then it will create a new hash entry and copy the * current color information into it. Since we can only encounter a * new page after having just finished scanning the previous page, * this is safe. */ void bopcolor(int outtops) { integer pageloc = ftell(dvifile) ; int h = pageloc % COLORHASH ; struct colorpage *p = colorhash[h] ; while (p) { if (p->boploc == pageloc) break ; else p = p->next ; } if (p) { strcpy(cstack, p->colordat) ; csp = cstack + strlen(cstack) ; bg = p->bg ; if (outtops && strcmp(bg, "White")!=0 && bg[0]) { cmdout("gsave") ; colorcmdout(bg) ; cmdout("clippath fill grestore") ; } } else { p = (struct colorpage *)mymalloc((integer) (strlen(cstack) + sizeof(struct colorpage) + MAXCOLORLEN)) ; p->next = colorhash[h] ; p->boploc = pageloc ; strcpy(p->colordat, cstack) ; p->bg = p->colordat + strlen(cstack) + 1 ; if (bg) strcpy(p->bg, bg) ; else *(p->bg) = 0 ; bg = p->bg ; colorhash[h] = p ; } if (outtops) { char *pp = csp - 1 ; while (pp >= cstack && *pp != '\n') pp-- ; pp++ ; if (strcmp(pp, DEFAULTCOLOR)!=0) { colorcmdout(pp) ; } } }