summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvipsk/color.c
blob: 3366f00b390c89952507d804449847de746b39bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/* $Id$
 *  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 <stdio.h>
/*
 *   The external declarations:
 */
#include "protos.h"

/*
 *   Here we set some limits on some color stuff.
 */
#define COLORHASH (89)
#define MAXCOLORLEN (120)     /* maximum color length for background */
#define INITCOLORLEN (10000)  /* initial stack size in chars */
#define OUTBUFSIZ (100)       /* base limit on special length */
/*
 *   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;
} *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".
 */
static void
colorcmdout(char *s)
{
   char *p;
   char tempword[OUTBUFSIZ];
   integer templen;

   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");
   templen = strlen("TeXcolor") + strlen(s) + 2; /* 2 is slop, just in case */
   if (templen >= OUTBUFSIZ) {
      /* PostScript names get truncated to around 128 characters anyway,
         so there's no reason to allow longer here. */
      sprintf(errbuf, "! TeXcolor special name longer than %d characters",
              OUTBUFSIZ);
      error (errbuf);
   }
     
   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; i<COLORHASH; i++) {
      for (p=colorhash[i]; p; p = q) {
         q = p->next;
         free(p->colordat);
         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
     fprintf(stderr, "You've mistakenly made a global color change ");
     fprintf_str(stderr, "to %s within nested colors\n", p);
     fprintf(stderr, "on page %ld. Will try to recover.\n", pagenum);
#else   /* ~SHORTINT */
     fprintf(stderr, "You've mistakenly made a global color change ");
     fprintf_str(stderr, "to %s within nested colors\n", p);
     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)
                  (sizeof(struct colorpage)));
      p->colordat = mymalloc(strlen(cstack) + MAXCOLORLEN + 2);
      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);
      }
   }
}