1% pdfcolorstack.w
2%
3% Copyright 2009-2011 Taco Hoekwater <taco@@luatex.org>
4%
5% This file is part of LuaTeX.
6%
7% LuaTeX is free software; you can redistribute it and/or modify it under
8% the terms of the GNU General Public License as published by the Free
9% Software Foundation; either version 2 of the License, or (at your
10% option) any later version.
11%
12% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
13% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14% FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15% License for more details.
16%
17% You should have received a copy of the GNU General Public License along
18% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
19
20@ @c
21
22
23#include "ptexlib.h"
24
25@* Color Stack and Matrix Transformation Support.
26
27
28@ In the following array and especially stack data structures are used.
29
30They have the following properties:
31
32    \item{-} They automatically grow dynamically.
33    \item{-} The size never decreases.
34    \item{-} The variable with name ending in "size" contains the number how many
35      entries the data structure can hold.
36    \item{-} The variable with name ending in "used" contains the number of
37      actually used entries.
38    \item{-} Memory of strings in stack entries must be allocated and
39      freed if the stack is cleared.
40
41
42@ Color Stack
43@c
44#define MAX_COLORSTACKS 32768
45/* The colorstack number is stored in two bytes (info field of the node) */
46/* Condition (newcolorstack): |MAX_COLORSTACKS mod STACK_INCREMENT = 0| */
47
48#define COLOR_DEFAULT "0 g 0 G"
49/* |literal_mode|s, see pdftex.web */
50#define SET_ORIGIN 0
51#define DIRECT_PAGE 1
52#define DIRECT_ALWAYS 2
53
54typedef struct {
55    char **page_stack;
56    char **form_stack;
57    char *page_current;
58    char *form_current;
59    char *form_init;
60    int page_size;
61    int form_size;
62    int page_used;
63    int form_used;
64    int literal_mode;
65    boolean page_start;
66} colstack_type;
67
68static colstack_type *colstacks = NULL;
69static int colstacks_size = 0;
70static int colstacks_used = 0;
71
72@ Initialization is done, if the color stacks are used,
73    |init_colorstacks()| is defined as macro to avoid unnecessary
74    procedure calls.
75@c
76#define init_colorstacks() if (colstacks_size == 0) colstacks_first_init();
77
78static void colstacks_first_init(void)
79{
80    colstacks_size = STACK_INCREMENT;
81    colstacks = xtalloc((unsigned) colstacks_size, colstack_type);
82    colstacks_used = 1;
83    colstacks[0].page_stack = NULL;
84    colstacks[0].form_stack = NULL;
85    colstacks[0].page_size = 0;
86    colstacks[0].form_size = 0;
87    colstacks[0].page_used = 0;
88    colstacks[0].form_used = 0;
89    colstacks[0].page_current = xstrdup(COLOR_DEFAULT);
90    colstacks[0].form_current = xstrdup(COLOR_DEFAULT);
91    colstacks[0].form_init = xstrdup(COLOR_DEFAULT);
92    colstacks[0].literal_mode = DIRECT_ALWAYS;
93    colstacks[0].page_start = true;
94}
95
96@ @c
97int colorstackused(void)
98{
99    init_colorstacks();
100    return colstacks_used;
101}
102
103@  |newcolorstack()|
104    A new color stack is setup with the given parameters.
105    The stack number is returned or -1 in case of error (no room).
106@c
107int newcolorstack(int s, int literal_mode, boolean page_start)
108{
109    colstack_type *colstack;
110    int colstack_num;
111    char *str;
112
113    init_colorstacks();
114
115    /* make room */
116    if (colstacks_used == MAX_COLORSTACKS) {
117        return -1;
118    }
119    if (colstacks_used == colstacks_size) {
120        colstacks_size += STACK_INCREMENT;
121        /* If |(MAX_COLORSTACKS mod STACK_INCREMENT = 0)| then we don't
122           need to check the case that size overruns |MAX_COLORSTACKS|. */
123        colstacks =
124            xreallocarray(colstacks, colstack_type, (unsigned) colstacks_size);
125    }
126    /* claim new color stack */
127    colstack_num = colstacks_used++;
128    colstack = &colstacks[colstack_num];
129    /* configure the new color stack */
130    colstack->page_stack = NULL;
131    colstack->form_stack = NULL;
132    colstack->page_size = 0;
133    colstack->page_used = 0;
134    colstack->form_size = 0;
135    colstack->form_used = 0;
136    colstack->literal_mode = literal_mode;
137    colstack->page_start = page_start;
138    str = makecstring(s);
139    if (*str == 0) {
140        colstack->page_current = NULL;
141        colstack->form_current = NULL;
142        colstack->form_init = NULL;
143    } else {
144        colstack->page_current = xstrdup(str);
145        colstack->form_current = xstrdup(str);
146        colstack->form_init = xstrdup(str);
147    }
148    free(str);
149    return colstack_num;
150}
151
152@ @c
153#define get_colstack(n) (&colstacks[n])
154
155@ Puts a string on top of the string pool and updates |pool_ptr|.
156@c
157static void put_cstring_on_str_pool(char *str)
158{
159    int save_selector = selector;
160    selector = new_string;
161    if (str == NULL || *str == 0) {
162        return;
163    }
164    tprint(str);
165    selector = save_selector;
166}
167
168@ @c
169static int colorstackset(int colstack_no, str_number s)
170{
171    colstack_type *colstack = get_colstack(colstack_no);
172
173    if (global_shipping_mode == SHIPPING_PAGE) {
174        xfree(colstack->page_current);
175        colstack->page_current = makecstring(s);
176    } else {
177        xfree(colstack->form_current);
178        colstack->form_current = makecstring(s);
179    }
180    return colstack->literal_mode;
181}
182
183@ @c
184int colorstackcurrent(int colstack_no)
185{
186    colstack_type *colstack = get_colstack(colstack_no);
187
188    if (global_shipping_mode == SHIPPING_PAGE) {
189        put_cstring_on_str_pool(colstack->page_current);
190    } else {
191        put_cstring_on_str_pool(colstack->form_current);
192    }
193    return colstack->literal_mode;
194}
195
196@ @c
197static int colorstackpush(int colstack_no, str_number s)
198{
199    colstack_type *colstack = get_colstack(colstack_no);
200    char *str;
201    if (global_shipping_mode == SHIPPING_PAGE) {
202        if (colstack->page_used == colstack->page_size) {
203            colstack->page_size += STACK_INCREMENT;
204            xretalloc(colstack->page_stack, (unsigned) colstack->page_size,
205                      char *);
206        }
207        colstack->page_stack[colstack->page_used++] = colstack->page_current;
208        str = makecstring(s);
209        if (*str == 0) {
210            colstack->page_current = NULL;
211        } else {
212            colstack->page_current = xstrdup(str);
213        }
214        free(str);
215    } else {
216        if (colstack->form_used == colstack->form_size) {
217            colstack->form_size += STACK_INCREMENT;
218            xretalloc(colstack->form_stack, (unsigned) colstack->form_size,
219                      char *);
220        }
221        colstack->form_stack[colstack->form_used++] = colstack->form_current;
222        str = makecstring(s);
223        if (*str == 0) {
224            colstack->form_current = NULL;
225        } else {
226            colstack->form_current = xstrdup(str);
227        }
228        free(str);
229    }
230    return colstack->literal_mode;
231}
232
233@ @c
234int colorstackpop(int colstack_no)
235{
236    colstack_type *colstack = get_colstack(colstack_no);
237
238    if (global_shipping_mode == SHIPPING_PAGE) {
239        if (colstack->page_used == 0) {
240            luatex_warn("pop empty color page stack %u",
241                        (unsigned int) colstack_no);
242            return colstack->literal_mode;
243        }
244        xfree(colstack->page_current);
245        colstack->page_current = colstack->page_stack[--colstack->page_used];
246        put_cstring_on_str_pool(colstack->page_current);
247    } else {
248        if (colstack->form_used == 0) {
249            luatex_warn("pop empty color form stack %u",
250                        (unsigned int) colstack_no);
251            return colstack->literal_mode;
252        }
253        xfree(colstack->form_current);
254        colstack->form_current = colstack->form_stack[--colstack->form_used];
255        put_cstring_on_str_pool(colstack->form_current);
256    }
257    return colstack->literal_mode;
258}
259
260@ @c
261void colorstackpagestart(void)
262{
263    int i, j;
264    colstack_type *colstack;
265
266    if (global_shipping_mode == SHIPPING_PAGE) {
267        /* see procedure |pdf_out_colorstack_startpage| */
268        return;
269    }
270
271    for (i = 0; i < colstacks_used; i++) {
272        colstack = &colstacks[i];
273        for (j = 0; j < colstack->form_used; j++) {
274            xfree(colstack->form_stack[j]);
275        }
276        colstack->form_used = 0;
277        xfree(colstack->form_current);
278        if (colstack->form_init == NULL) {
279            colstack->form_current = NULL;
280        } else {
281            colstack->form_current = xstrdup(colstack->form_init);
282        }
283    }
284}
285
286@ @c
287int colorstackskippagestart(int colstack_no)
288{
289    colstack_type *colstack = get_colstack(colstack_no);
290
291    if (!colstack->page_start) {
292        return 1;
293    }
294    if (colstack->page_current == NULL) {
295        return 0;
296    }
297    if (strcmp(COLOR_DEFAULT, colstack->page_current) == 0) {
298        return 2;
299    }
300    return 0;
301}
302
303
304@ @c
305void pdf_out_colorstack(PDF pdf, halfword p)
306{
307    int old_setting;
308    str_number s;
309    int cmd;
310    int stack_no;
311    int literal_mode;
312    cmd = pdf_colorstack_cmd(p);
313    stack_no = pdf_colorstack_stack(p);
314    literal_mode = 0;
315    if (stack_no >= colorstackused()) {
316        tprint_nl("");
317        tprint("Color stack ");
318        print_int(stack_no);
319        tprint(" is not initialized for use!");
320        tprint_nl("");
321        return;
322    }
323    switch (cmd) {
324    case colorstack_set:
325    case colorstack_push:
326        old_setting = selector;
327        selector = new_string;
328        show_token_list(token_link(pdf_colorstack_data(p)), null, -1);
329        selector = old_setting;
330        s = make_string();
331        if (cmd == colorstack_set)
332            literal_mode = colorstackset(stack_no, s);
333        else
334            literal_mode = colorstackpush(stack_no, s);
335        if (str_length(s) > 0)
336            pdf_literal(pdf, s, literal_mode, false);
337        flush_str(s);
338        return;
339        break;
340    case colorstack_pop:
341        literal_mode = colorstackpop(stack_no);
342        break;
343    case colorstack_current:
344        literal_mode = colorstackcurrent(stack_no);
345        break;
346    default:
347        break;
348    }
349    if (cur_length > 0) {
350        s = make_string();
351        pdf_literal(pdf, s, literal_mode, false);
352        flush_str(s);
353    }
354}
355
356@ @c
357void pdf_out_colorstack_startpage(PDF pdf)
358{
359    int i;
360    int max;
361    int start_status;
362    int literal_mode;
363    str_number s;
364    i = 0;
365    max = colorstackused();
366    while (i < max) {
367        start_status = colorstackskippagestart(i);
368        if (start_status == 0) {
369            literal_mode = colorstackcurrent(i);
370            if (cur_length > 0) {
371                s = make_string();
372                pdf_literal(pdf, s, literal_mode, false);
373                flush_str(s);
374            }
375        }
376        i++;
377    }
378}
379