/* Copyright 2009-2011 Taco Hoekwater This file is part of LuaTeX. LuaTeX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU General Public License along with LuaTeX; if not, see . */ #include "ptexlib.h" /*tex This module implements color stack support. The following array and especially stack data structures are used. \startitemize \startitem They automatically grow dynamically. \stopitem \startitem The size never decreases. \stopitem \startitem The variable with name ending in "size" contains the number how many entries the data structure can hold. \stopitem \startitem The variable with name ending in "used" contains the number of actually used entries. \stopitem \startitem Memory of strings in stack entries must be allocated and freed if the stack is cleared. \stopitem \stopitemize */ #define MAX_COLORSTACKS 32768 /* The colorstack number is stored in two bytes (info field of the node); condition (newcolorstack): |MAX_COLORSTACKS mod STACK_INCREMENT = 0| */ #define COLOR_DEFAULT "0 g 0 G" typedef struct { char **page_stack; char **form_stack; char *page_current; char *form_current; char *form_init; int page_size; int form_size; int page_used; int form_used; int literal_mode; boolean page_start; } colstack_type; static colstack_type *colstacks = NULL; static int colstacks_size = 0; static int colstacks_used = 0; /*tex Initialization is done, if the color stacks are used, |init_colorstacks()| is defined as macro to avoid unnecessary procedure calls. */ #define init_colorstacks() if (colstacks_size == 0) colstacks_first_init(); static void colstacks_first_init(void) { colstacks_size = STACK_INCREMENT; colstacks = xtalloc((unsigned) colstacks_size, colstack_type); colstacks_used = 1; colstacks[0].page_stack = NULL; colstacks[0].form_stack = NULL; colstacks[0].page_size = 0; colstacks[0].form_size = 0; colstacks[0].page_used = 0; colstacks[0].form_used = 0; colstacks[0].page_current = xstrdup(COLOR_DEFAULT); colstacks[0].form_current = xstrdup(COLOR_DEFAULT); colstacks[0].form_init = xstrdup(COLOR_DEFAULT); colstacks[0].literal_mode = direct_always; colstacks[0].page_start = true; } int colorstackused(void) { init_colorstacks(); return colstacks_used; } /*tex A new color stack is setup with the given parameters. The stack number is returned or -1 in case of error (no room). */ int newcolorstack(const char *str, int literal_mode, boolean page_start) { colstack_type *colstack; int colstack_num; init_colorstacks(); /*tex Make room: */ if (colstacks_used == MAX_COLORSTACKS) { return -1; } if (colstacks_used == colstacks_size) { colstacks_size += STACK_INCREMENT; /*tex If |(MAX_COLORSTACKS mod STACK_INCREMENT = 0)| then we don't need to check the case that size overruns |MAX_COLORSTACKS|. */ colstacks = xreallocarray(colstacks, colstack_type, (unsigned) colstacks_size); } /*tex Claim new color stack. */ colstack_num = colstacks_used++; colstack = &colstacks[colstack_num]; /*tex Configure the new color stack. */ colstack->page_stack = NULL; colstack->form_stack = NULL; colstack->page_size = 0; colstack->page_used = 0; colstack->form_size = 0; colstack->form_used = 0; colstack->literal_mode = literal_mode; colstack->page_start = page_start; colstack->page_current = NULL; colstack->form_current = NULL; colstack->form_init = NULL; if (str) { colstack->page_current = xstrdup(str); colstack->form_current = xstrdup(str); colstack->form_init = xstrdup(str); } return colstack_num; } #define get_colstack(n) (&colstacks[n]) /*tex Put a string on top of the string pool and updates |pool_ptr|. */ static void put_cstring_on_str_pool(char *str) { int save_selector = selector; selector = new_string; if (str == NULL || *str == 0) { return; } tprint(str); selector = save_selector; } static int colorstackset(int colstack_no, str_number s) { colstack_type *colstack = get_colstack(colstack_no); if (global_shipping_mode == SHIPPING_PAGE) { xfree(colstack->page_current); colstack->page_current = makecstring(s); } else { xfree(colstack->form_current); colstack->form_current = makecstring(s); } return colstack->literal_mode; } int colorstackcurrent(int colstack_no) { colstack_type *colstack = get_colstack(colstack_no); if (global_shipping_mode == SHIPPING_PAGE) { put_cstring_on_str_pool(colstack->page_current); } else { put_cstring_on_str_pool(colstack->form_current); } return colstack->literal_mode; } static int colorstackpush(int colstack_no, str_number s) { colstack_type *colstack = get_colstack(colstack_no); char *str; if (global_shipping_mode == SHIPPING_PAGE) { if (colstack->page_used == colstack->page_size) { colstack->page_size += STACK_INCREMENT; xretalloc(colstack->page_stack, (unsigned) colstack->page_size, char *); } colstack->page_stack[colstack->page_used++] = colstack->page_current; str = makecstring(s); if (*str == 0) { colstack->page_current = NULL; } else { colstack->page_current = xstrdup(str); } free(str); } else { if (colstack->form_used == colstack->form_size) { colstack->form_size += STACK_INCREMENT; xretalloc(colstack->form_stack, (unsigned) colstack->form_size, char *); } colstack->form_stack[colstack->form_used++] = colstack->form_current; str = makecstring(s); if (*str == 0) { colstack->form_current = NULL; } else { colstack->form_current = xstrdup(str); } free(str); } return colstack->literal_mode; } int colorstackpop(int colstack_no) { colstack_type *colstack = get_colstack(colstack_no); if (global_shipping_mode == SHIPPING_PAGE) { if (colstack->page_used == 0) { formatted_warning("pdf backend","pop empty color page stack %u",(unsigned int) colstack_no); return colstack->literal_mode; } xfree(colstack->page_current); colstack->page_current = colstack->page_stack[--colstack->page_used]; put_cstring_on_str_pool(colstack->page_current); } else { if (colstack->form_used == 0) { formatted_warning("pdf backend","pop empty color form stack %u",(unsigned int) colstack_no); return colstack->literal_mode; } xfree(colstack->form_current); colstack->form_current = colstack->form_stack[--colstack->form_used]; put_cstring_on_str_pool(colstack->form_current); } return colstack->literal_mode; } void colorstackpagestart(void) { int i, j; colstack_type *colstack; if (global_shipping_mode == SHIPPING_PAGE) { return; } for (i = 0; i < colstacks_used; i++) { colstack = &colstacks[i]; for (j = 0; j < colstack->form_used; j++) { xfree(colstack->form_stack[j]); } colstack->form_used = 0; xfree(colstack->form_current); if (colstack->form_init == NULL) { colstack->form_current = NULL; } else { colstack->form_current = xstrdup(colstack->form_init); } } } int colorstackskippagestart(int colstack_no) { colstack_type *colstack = get_colstack(colstack_no); if (!colstack->page_start) { return 1; } if (colstack->page_current == NULL) { return 0; } if (strcmp(COLOR_DEFAULT, colstack->page_current) == 0) { return 2; } return 0; } void pdf_out_colorstack(PDF pdf, halfword p) { int old_setting; str_number s; int cmd = pdf_colorstack_cmd(p); int stack_no = pdf_colorstack_stack(p); int literal_mode = 0; if (stack_no >= colorstackused()) { formatted_warning("pdf backend","color stack %u is not initialized",(unsigned int) stack_no); return; } switch (cmd) { case colorstack_set: case colorstack_push: old_setting = selector; selector = new_string; show_token_list(token_link(pdf_colorstack_data(p)), null, -1); selector = old_setting; s = make_string(); if (cmd == colorstack_set) literal_mode = colorstackset(stack_no, s); else literal_mode = colorstackpush(stack_no, s); if (str_length(s) > 0) pdf_literal(pdf, s, literal_mode, false); flush_str(s); return; break; case colorstack_pop: literal_mode = colorstackpop(stack_no); break; case colorstack_current: literal_mode = colorstackcurrent(stack_no); break; default: break; } if (cur_length > 0) { s = make_string(); pdf_literal(pdf, s, literal_mode, false); flush_str(s); } } void pdf_out_colorstack_startpage(PDF pdf) { int start_status; int literal_mode; str_number s; int i = 0; int max = colorstackused(); while (i < max) { start_status = colorstackskippagestart(i); if (start_status == 0) { literal_mode = colorstackcurrent(i); if (cur_length > 0) { s = make_string(); pdf_literal(pdf, s, literal_mode, false); flush_str(s); } } i++; } }