1 /*
2  *  This is a set of routines for dvips that are used to process color
3  *  commands in the TeX file (passed by \special commands).  This was
4  *  orignally written by J. Hafner, E. Blanz and M. Flickner of IBM
5  *  Research, Almaden Research Center.  Contact hafner@almaden.ibm.com.
6  *  And then it was completely rewritten by Tomas Rokicki to:
7  *
8  *      - Be easier on the memory allocator (malloc/free)
9  *      - Use less memory overall (by a great deal) and be faster
10  *      - Work with the -C, -a, and other options
11  *      - Be more adaptable to other drivers and previewers.
12  *
13  *  The motivating idea:  we want to be able to process 5,000 page
14  *  documents using lots of colors on each page on a 640K IBM PC
15  *  relatively quickly.
16  */
17 #include "dvips.h" /* The copyright notice in that file is included too! */
18 #include <stdio.h>
19 /*
20  *   The external declarations:
21  */
22 #include "protos.h"
23 
24 /*
25  *   Here we set some limits on some color stuff.
26  */
27 #define COLORHASH (89)
28 #define MAXCOLORLEN (120)     /* maximum color length for background */
29 #define INITCOLORLEN (10000)  /* initial stack size in chars */
30 /*
31  *   This is where we store the color information for a particular page.
32  *   If we free all of these, we free all of the allocated color
33  *   stuff; we do no allocations (but one) other than when we allocate
34  *   these.
35  */
36 static struct colorpage {
37    struct colorpage *next;
38    integer boploc; /* we use the bop loc as a page indicator */
39    char *bg;
40    char colordat[2];
41 } *colorhash[COLORHASH];
42 static char *cstack, *csp, *cend, *bg;
43 /*
44  *   This routine sends a color command out.  If the command is a
45  *   single `word' or starts with a double quote, we send it out
46  *   exactly as is (except for any double quote.)  If the command
47  *   is a word followed by arguments, we send out the arguments and
48  *   then the word prefixed by "TeXcolor".
49  */
50 static void
colorcmdout(char * s)51 colorcmdout(char *s)
52 {
53    char *p;
54    char tempword[100];
55 
56    while (*s && *s <= ' ')
57       s++;
58    if (*s == '"') {
59       cmdout(s+1);
60       return;
61    }
62    for (p=s; *p && *p > ' '; p++);
63    for (; *p && *p <= ' '; p++);
64    if (*p == 0) {
65       cmdout(s);
66       return;
67    }
68    cmdout(p);
69    strcpy(tempword, "TeXcolor");
70    for (p=tempword + strlen(tempword); *s && *s > ' '; p++, s++)
71       *p = *s;
72    *p = 0;
73    cmdout(tempword);
74    return;
75 }
76 /*
77  *   For a new dvi file, call this.  Frees all allocated memory.
78  */
79 #define DEFAULTCOLOR "Black"
initcolor(void)80 void initcolor(void) {
81    int i;
82    struct colorpage *p, *q;
83 
84    for (i=0; i<COLORHASH; i++) {
85       for (p=colorhash[i]; p; p = q) {
86          q = p->next;
87          free(p);
88       }
89       colorhash[i] = 0;
90    }
91    cstack = (char *)mymalloc(INITCOLORLEN);
92    strcpy(cstack, "\n");
93    strcat(cstack, DEFAULTCOLOR);
94    csp = cstack + strlen(cstack);
95    cend = cstack + INITCOLORLEN - 3; /* for conservativeness */
96    bg = 0;
97 }
98 /*
99  * This takes a call from predospecial to set the background color for
100  * the current page.  It is saved in stackdepth and backed down the
101  * stack during popcolors.
102  */
103 void
background(char * bkgrnd)104 background(char *bkgrnd)
105 {
106    if (bkgrnd && *bkgrnd) {
107       if (strlen(bkgrnd) > MAXCOLORLEN)
108          error(" color name too long; ignored");
109       else
110          strcpy(bg, bkgrnd);
111    }
112 }
113 /*
114  * This routine puts a call from \special for color on the colorstack
115  * and sets the color in the PostScript.
116  */
117 void
pushcolor(char * p,Boolean outtops)118 pushcolor(char *p, Boolean outtops)
119 {
120    while (strlen(p) + csp > cend) {
121       int newlen = 3 * (cend - cstack);
122       char *newcs = (char *)mymalloc(newlen);
123       strcpy(newcs, cstack);
124       csp = newcs + (csp - cstack);
125       cend = newcs + newlen - 3;
126       cstack = newcs;
127    }
128    *csp++ = '\n';
129    strcpy(csp, p);
130    csp += strlen(p);
131    if (outtops) {
132       colorcmdout(p);
133    }
134 }
135 /*
136  * This routine removes a color from the colorstack and resets the color
137  * in the PostScript to the previous color.
138  */
139 void
popcolor(Boolean outtops)140 popcolor(Boolean outtops)
141 {
142    char *p = csp - 1;
143 
144    while (p >= cstack && *p != '\n')
145       p--;
146    if (p == cstack)
147       return;  /* We don't pop the last color as that is global */
148    *p = 0;
149    csp = p;
150    for (p--; p >= cstack && *p != '\n'; p--);
151    p++;
152    if ( outtops ) {
153       colorcmdout(p);
154    }
155 }
156 /*
157  * This routine clears the colorstack, pushes a new color onto the stack
158  * (this is now the root or global color).
159  */
160 void
resetcolorstack(char * p,int outtops)161 resetcolorstack(char * p, int outtops)
162 {
163    char *q = csp - 1;
164 
165    while (q > cstack && *q != '\n')
166       q--;
167    if (q > cstack && outtops == 0) {
168 #ifdef SHORTINT
169      fprintf(stderr, "You've mistakenly made a global color change ");
170      fprintf(stderr, "to %s within nested colors\n", p);
171      fprintf(stderr, "on page %ld. Will try to recover.\n", pagenum);
172 #else   /* ~SHORTINT */
173      fprintf(stderr, "You've mistakenly made a global color change ");
174      fprintf(stderr, "to %s within nested colors\n", p);
175      fprintf(stderr, "on page %d. Will try to recover.\n", pagenum);
176 #endif  /* ~SHORTINT */
177    }
178    csp = cstack;
179    *csp = 0;
180    pushcolor(p, outtops);
181 }
182 /*
183  *   This routine is a bit magic.  It looks up the page in the current
184  *   hash table.  If the page is already entered in the hash table, then
185  *   it restores the color to what that page had, and sets the last
186  *   color.  This will occur if this is not the first time that this
187  *   page has been encountered.
188  *
189  *   If, on the other hand, this is the first time that the page has
190  *   been encountered, then it will create a new hash entry and copy the
191  *   current color information into it.  Since we can only encounter a
192  *   new page after having just finished scanning the previous page,
193  *   this is safe.
194  */
195 void
bopcolor(int outtops)196 bopcolor(int outtops)
197 {
198    integer pageloc = ftell(dvifile);
199    int h = pageloc % COLORHASH;
200    struct colorpage *p = colorhash[h];
201 
202    while (p) {
203       if (p->boploc == pageloc)
204          break;
205       else
206          p = p->next;
207    }
208    if (p) {
209       strcpy(cstack, p->colordat);
210       csp = cstack + strlen(cstack);
211       bg = p->bg;
212       if (outtops && strcmp(bg, "White")!=0 && bg[0]) {
213          cmdout("gsave");
214          colorcmdout(bg);
215          cmdout("clippath fill grestore");
216       }
217    } else {
218       p = (struct colorpage *)mymalloc((integer)
219                   (strlen(cstack) + sizeof(struct colorpage) + MAXCOLORLEN));
220       p->next = colorhash[h];
221       p->boploc = pageloc;
222       strcpy(p->colordat, cstack);
223       p->bg = p->colordat + strlen(cstack) + 1;
224       if (bg)
225          strcpy(p->bg, bg);
226       else
227          *(p->bg) = 0;
228       bg = p->bg;
229       colorhash[h] = p;
230    }
231    if (outtops) {
232       char *pp = csp - 1;
233       while (pp >= cstack && *pp != '\n')
234          pp--;
235       pp++;
236       if (strcmp(pp, DEFAULTCOLOR)!=0) {
237          colorcmdout(pp);
238       }
239    }
240 }
241