[pdftex] Color stack, first try

Heiko Oberdiek oberdiek at uni-freiburg.de
Sun Feb 16 01:57:10 CET 2003


ColorStack

The following test file shows a problem with colors:

%%% cut %%% test.tex %%% cut %%%
\ifx\pdfcompresslevel\undefined
\else
  \pdfcompresslevel=0
\fi
\documentclass{article}
\usepackage{color}
%\usepackage{pdfcolmk}% needed for pdfTeX without color stack
\begin{document}
First line is black.\\
\textcolor{red}{%
  The second line is red and\newpage
  continued on the next page. This line should be red.}\\
And this line is black again.
\end{document}
%%% cut %%% test.tex %%% cut %%%

This file runs fine with latex/dvips, because dvips has
a better color support by the use of a "color stack"
(see file "TeX/texk/dvipsk/color.c").

Package pdfcolmk tries to solve this problem. It remembers
the last set color with the help of a mark register and
sets it at the begin of the next page. So the eTeX extensions
are recommended because of an additional and separate mark
register. The main disadvantage is that it has to insert
some code in the output routine. Not very elegant and portable.

Thus I tried to extend pdfTeX and implements a color stack.
The implementation is intended as basis for discussion and
further experimenting.

Short explanation:

The stack handling is done in "colorstack.c". The stack
will grow if necessary. It is a simple char array, the
entries are zero terminated C-strings. Array entries
contains the offsets of the strings. "cur_entry" contains
the number of the current entry, "stack_used" records
the used space, so "stack[stack_used]" points to the
next free space.
  The first color on the stack is a global default color,
initially black. There are four main functions:

* colorset:
  This sets the global default color. The stack should be empty.
  Main purpose: color setting by use of \color outside of groups
  would fill the stack without effectiveness. (See package
  dvipscol.sty for a longer description.)

* colorpush:
  This pushes a color on the stack and pdfTeX sets the color.

* colorpop:
  This function removes the topmost color and pdfTeX sets the
  previous color.

* colorpeek:
  The topmost color is returned. This function is used at
  the begin of the page. (Optimization: black is the default
  of PDF, so it can be suppressed.)

File "pdftex.ch" implements the pdfTeX primitive interface.
Because color was set by \pdfliteral, this command and its
node type are also used for the color stack methods:

* \pdfliteral colorset{<color setting>}
* \pdfliteral colorpush{<color setting>}
* \pdfliteral colorpop
* \pdfliteral colorpeek

Internally the variable pdf_literal_direct is used to distinguish
between the different forms of \pdfliteral:
  0: "non-direct"
  1: direct
  2: colorset
  3: colorpush
  4: colorpop
  5: colorpeek
Only 0-3 use pdf_literal_data. Possible (cosmetic)
improvement: names instead of numbers, better name
for pdf_literal_direct or use of other command,
variable for color stuff.

\pdfliteral knows the distinction between "direct" and
setting the CTM before. For color commands the position
is not relevant. So method "direct" would be right.
Unhappily there is a bug, see my posting "LiteralDirectFirstSpace".
Thus the patches below using the fix of this posting.
But with "pdftex.ch.nodirect.diff" pdftex.ch can be changed
to use "non-direct" for color setting and disabling the bug fix.

For use with the color package, the file color.cfg
enables support of the color stack commands for
version "1.10b". It is for illustration, how the
color commands of pdftex.def can be redefined.

Files for color stack:
* colorstack.c: new file
* Makefile.in.diff: adds colorstack
* pdftex.defines.diff: adds functions of colorstack.c for use in pdftex.ch
* ptexlib.h.diff: colorstack.c functions added
* pdftex.ch.diff: support of colorstack
* pdftex.ch.nodirect.diff: can be applied after pdftex.ch.diff to
  change the method of color setting to \pdfliteral without "direct".
* color.cfg: example for redefinition of package color/pdftex.def commands.

Yours sincerely
  Heiko <oberdiek at uni-freiburg.de>


%%% cut %%% colorstack.c %%% cut %%%
/*
  First try for color stack, 2002/02/16 Heiko Oberdiek.
*/

#include "ptexlib.h"
#include <kpathsea/c-auto.h>
#include <kpathsea/c-memstr.h>

#define INIT_STACK_SIZE MAX_CSTRING_LEN * 2
#define INIT_ENTRY_COUNT 100

static char *stack = NULL;
static int stack_used = 0;
static int stack_size = INIT_STACK_SIZE;

static int *entries = NULL;
static int max_entries = INIT_ENTRY_COUNT;
static int cur_entry = 0;

static char color_default[] = "0 g 0 G";

void initstack(void) {
    if (stack == NULL) {
        stack = xtalloc(stack_size, char);
        entries = (int*)xtalloc(max_entries, int);
        cur_entry = 0;
        entries[0] = 0;
        strcpy(stack, color_default);
        stack_used = sizeof(color_default);
    }
}

void colorset(strnumber s) {
    char *color_string = makecstring(s);
    int color_string_size = strlen(color_string) + 1;

    initstack();
    if (cur_entry != 0) {
        if (cur_entry == 1) {
            pdftex_warn("Global color setting, 1 entry deleted"
                        " from color stack");
        }
        else {
            pdftex_warn("Global color setting, %d entries deleted"
                        " from color stack", cur_entry);
        }
    }
    cur_entry = 0;
    memcpy(stack, color_string, color_string_size);
    stack_used = color_string_size;
}

void colorpush(strnumber s) {
    char *color_string = makecstring(s);
    int color_string_size = strlen(color_string) + 1;

    initstack();
    if (stack_size - stack_used < color_string_size) {
        stack_size *= 2;
        stack = xretalloc(stack, stack_size, char);
    }
    cur_entry++;
    if (cur_entry >= max_entries) {
        max_entries *= 2;
        entries = (int*)xretalloc((char*)entries, max_entries, int);
    }
    entries[cur_entry] = stack_used;
    memcpy(stack + entries[cur_entry], color_string, color_string_size);
    stack_used += color_string_size;
}

strnumber colorpeek(boolean pagebegin) {
    initstack();
    if (cur_entry == 0 && pagebegin && !strcmp(stack, "0 g 0 G")) {
        return getnullstr();
    }
    return maketexstring(stack + entries[cur_entry]);
}

strnumber colorpop(void) {
    initstack();
    if (cur_entry > 0) {
        stack_used = entries[cur_entry];
        cur_entry--;
    }
    else {
        pdftex_warn("colorpop ignored because of empty color stack");
    }
    return colorpeek(false);
}
%%% cut %%% colorstack.c %%% cut %%%


%%% cut %%% Makefile.in.diff %%% cut %%%
*** Makefile.in.org	Sun Feb 16 00:35:44 2003
--- Makefile.in	Sun Feb 16 00:36:51 2003
***************
*** 28,36 ****
  @LIBPNGCPPFLAGS@ @ZLIBCPPFLAGS@ -DpdfTeX
  
  
! OBJS = epdf.o mapfile.o papersiz.o utils.o config.o vfpacket.o pkin.o \
! writefont.o writet1.o writet3.o writezip.o writeenc.o writettf.o \
! writejpg.o writepng.o writeimg.o pdftoepdf.o
  
  all: libpdf.a
  
--- 28,36 ----
  @LIBPNGCPPFLAGS@ @ZLIBCPPFLAGS@ -DpdfTeX
  
  
! OBJS = epdf.o mapfile.o papersiz.o utils.o colorstack.o config.o \
! vfpacket.o pkin.o writefont.o writet1.o writet3.o writezip.o \
! writeenc.o writettf.o writejpg.o writepng.o writeimg.o pdftoepdf.o
  
  all: libpdf.a
  
%%% cut %%% Makefile.in.diff %%% cut %%%


%%% cut %%% pdftex.defines.diff %%% cut %%%
*** pdftex.defines.org	Sun Feb 16 01:51:01 2003
--- pdftex.defines	Sun Feb 16 01:52:50 2003
***************
*** 32,37 ****
--- 32,43 ----
  @define procedure pdfmarkchar();
  @define procedure writepdf();
  
+ { functions from colorstack.c }
+ @define procedure colorset();
+ @define procedure colorpush();
+ @define function colorpeek();
+ @define function colorpop;
+ 
  { functions from config.c }
  @define function cfgpar();
  @define function iscfgtruedimen();
%%% cut %%% pdftex.defines.diff %%% cut %%%


%%% cut %%% ptexlib.h.diff %%% cut %%%
*** ptexlib.h.org	Sun Feb 16 00:36:07 2003
--- ptexlib.h	Sun Feb 16 00:37:12 2003
***************
*** 109,114 ****
--- 109,120 ----
  
  /* pdftexlib function prototypes */
  
+ /* colorstack.c */
+ extern void colorset(strnumber);
+ extern void colorpush(strnumber);
+ extern strnumber colorpeek(boolean);
+ extern strnumber colorpop(void);
+ 
  /* config.c */
  extern integer cfgpar(integer);
  extern boolean iscfgtruedimen(integer);
%%% cut %%% ptexlib.h.diff %%% cut %%%


%%% cut %%% pdftex.ch.diff %%% cut %%%
*** pdftex.ch.org	Sun Feb 16 00:41:36 2003
--- pdftex.ch	Sun Feb 16 00:41:14 2003
***************
*** 1306,1311 ****
--- 1306,1313 ----
          pdf_set_origin;
      end
      else begin
+         pdf_first_space_corr:=0;
+         {Does the previous line fix the bug "LiteralDirectFirstSpace"?}
          pdf_end_string;
          pdf_print_nl;
      end;
***************
*** 1788,1793 ****
--- 1790,1797 ----
  @d pdf_literal_data(#)     == link(#+1) {data}
  @d pdf_literal_direct(#)   == info(#+1) {write data directly to the page
                                contents without resetting text matrix}
+   {0: nothing, 1: |direct|, 2: |colorset|, 3: |colorpush|,
+    4: |colorpop|, 5: |colorpeek|}
  
  @# {data struture for \.{\\pdfobj} and \.{\\pdfrefobj}}
  @d pdf_refobj_node_size    == 2 {size of whatsit node representing the raw object}
***************
*** 3059,3072 ****
  var old_setting:0..max_selector; {holds print |selector|}
      s: str_number;
  begin
!     old_setting:=selector; selector:=new_string;
!     show_token_list(link(pdf_literal_data(p)),null,pool_size-pool_ptr);
!     selector:=old_setting;
!     s := make_string;
!     if pdf_literal_direct(p) = 1 then
!         literal(s, false, false, false)
      else
!         literal(s, true, false, false);
      flush_str(s);
  end;
  
--- 3063,3086 ----
  var old_setting:0..max_selector; {holds print |selector|}
      s: str_number;
  begin
!     if pdf_literal_direct(p) <= 3 then begin
!         old_setting:=selector; selector:=new_string;
!         show_token_list(link(pdf_literal_data(p)),null,pool_size-pool_ptr);
!         selector:=old_setting;
!         s:=make_string;
!     end;
!     case pdf_literal_direct(p) of
!     4: s:=colorpop;
!     5: s:=colorpeek(false);
!     end;
!     if pdf_literal_direct(p) = 0 then
!         literal(s, true, false, false)
      else
!         literal(s, false, false, false);
!     case pdf_literal_direct(p) of
!     2: colorset(s);
!     3: colorpush(s);
!     end;
      flush_str(s);
  end;
  
***************
*** 3562,3567 ****
--- 3576,3584 ----
  @ @<Start stream of page/form contents@>=
  pdf_begin_stream;
  if shipping_page then begin
+     s:=colorpeek(true);
+     pdf_print_ln(s);
+     flush_str(s);
      @<Adjust tranformation matrix for the magnification ratio@>;
  end
  
***************
*** 6387,6396 ****
      new_whatsit(pdf_literal_node, write_node_size);
      if scan_keyword("direct") then
          pdf_literal_direct(tail) := 1
      else
          pdf_literal_direct(tail) := 0;
!     scan_pdf_ext_toks;
!     pdf_literal_data(tail) := def_ref;
  end
  
  @ The \.{\\pdfobj} primitive is to create a ``raw'' object in PDF
--- 6404,6423 ----
      new_whatsit(pdf_literal_node, write_node_size);
      if scan_keyword("direct") then
          pdf_literal_direct(tail) := 1
+     else if scan_keyword("colorset") then
+         pdf_literal_direct(tail) := 2
+     else if scan_keyword("colorpush") then
+         pdf_literal_direct(tail) := 3
+     else if scan_keyword("colorpop") then
+         pdf_literal_direct(tail) := 4
+     else if scan_keyword("colorpeek") then
+         pdf_literal_direct(tail) := 5
      else
          pdf_literal_direct(tail) := 0;
!     if pdf_literal_direct(tail) <= 3 then begin
!         scan_pdf_ext_toks;
!         pdf_literal_data(tail) := def_ref;
!     end;
  end
  
  @ The \.{\\pdfobj} primitive is to create a ``raw'' object in PDF
***************
*** 7439,7447 ****
  @y
  pdf_literal_node: begin
      print_esc("pdfliteral");
!     if pdf_literal_direct(p) > 0 then
!         print(" direct");
!     print_mark(pdf_literal_data(p));
  end;
  pdf_refobj_node: begin
      print_esc("pdfrefobj");
--- 7466,7480 ----
  @y
  pdf_literal_node: begin
      print_esc("pdfliteral");
!     case pdf_literal_direct(p) of
!     1: print(" direct");
!     2: print(" colorset");
!     3: print(" colorpush");
!     4: print(" colorpop");
!     5: print(" colorpeek");
!     end;
!     if pdf_literal_direct(p) <= 3 then
!         print_mark(pdf_literal_data(p));
  end;
  pdf_refobj_node: begin
      print_esc("pdfrefobj");
***************
*** 7601,7607 ****
  @y
  pdf_literal_node: begin
      r := get_node(write_node_size);
!     add_token_ref(pdf_literal_data(p));
      words := write_node_size;
  end;
  pdf_refobj_node: begin
--- 7634,7641 ----
  @y
  pdf_literal_node: begin
      r := get_node(write_node_size);
!     if pdf_literal_direct(p) <= 3 then
!         add_token_ref(pdf_literal_data(p));
      words := write_node_size;
  end;
  pdf_refobj_node: begin
***************
*** 7667,7673 ****
  othercases confusion("ext3")
  @y
  pdf_literal_node: begin
!     delete_token_ref(pdf_literal_data(p));
      free_node(p, write_node_size);
  end;
  pdf_refobj_node:
--- 7701,7708 ----
  othercases confusion("ext3")
  @y
  pdf_literal_node: begin
!     if pdf_literal_direct(p) <= 3 then
!         delete_token_ref(pdf_literal_data(p));
      free_node(p, write_node_size);
  end;
  pdf_refobj_node:
%%% cut %%% pdftex.ch.diff %%% cut %%%


%%% cut %%% pdftex.ch.nodirect.diff %%% cut %%%
*** pdftex.ch.direct	Sun Feb 16 00:47:18 2003
--- pdftex.ch	Sun Feb 16 00:47:05 2003
***************
*** 1306,1313 ****
          pdf_set_origin;
      end
      else begin
-         pdf_first_space_corr:=0;
-         {Does the previous line fix the bug "LiteralDirectFirstSpace"?}
          pdf_end_string;
          pdf_print_nl;
      end;
--- 1306,1311 ----
***************
*** 3073,3082 ****
      4: s:=colorpop;
      5: s:=colorpeek(false);
      end;
!     if pdf_literal_direct(p) = 0 then
!         literal(s, true, false, false)
      else
!         literal(s, false, false, false);
      case pdf_literal_direct(p) of
      2: colorset(s);
      3: colorpush(s);
--- 3071,3080 ----
      4: s:=colorpop;
      5: s:=colorpeek(false);
      end;
!     if pdf_literal_direct(p) = 1 then
!         literal(s, false, false, false)
      else
!         literal(s, true, false, false);
      case pdf_literal_direct(p) of
      2: colorset(s);
      3: colorpush(s);
%%% cut %%% pdftex.ch.nodirect.diff %%% cut %%%


%%% cut %%% color.cfg %%% cut %%%
\ProvidesFile{color.cfg}%
  [2001/08/31 v1.1-2003/02/15 color configuration of teTeX/TeXLive]
\immediate\write16{%
  Patch for pdfTeX-1.10b test version with color stack%
}

% Select an appropriate default driver
\begingroup
  \chardef\x=0 %
  % check pdfTeX
  \@ifundefined{pdfoutput}{}{%
    \ifcase\pdfoutput
    \else
      \chardef\x=1 %
    \fi
  }%
  % check VTeX
  \@ifundefined{OpMode}{}{%
    \chardef\x=2 %
  }%
\expandafter\endgroup
\ifcase\x
  % default case
  \ExecuteOptions{dvips}%
\or
  % pdfTeX is running in pdf mode
  \ExecuteOptions{pdftex}%
  % Patch for color stack for test version based on 1.10b
  \ifnum\pdftexversion=110
    \if\pdftexrevision b%
      \begingroup\expandafter\expandafter\expandafter\endgroup
      \expandafter\ifx\csname AtEndOfPackage\endcsname\relax
      \else
        \AtEndOfPackage{%
          \begingroup\expandafter\expandafter\expandafter\endgroup
          \expandafter\ifx\csname currentgrouplevel\endcsname\relax
            \def\set at color{%
              \pdfliteral colorpush{\current at color}\relax
              \aftergroup\reset at color
            }%
          \else
            \def\set at color{%
              \ifcase\currentgrouplevel
                \pdfliteral colorset{\current at color}%
              \else
                \pdfliteral colorpush{\current at color}\relax
                \aftergroup\reset at color
              \fi
            }%
          \fi
          \def\reset at color{\pdfliteral colorpop\relax}%
          \immediate\write16{%
            * Color commands redefined to support test version %
            of pdfTeX%
          }%
          \immediate\write16{%
            \space\space with color stack, %
            trial version 2002/02/16 (HO)%
          }%
        }%
      \fi
    \fi
  \fi
\else
  % VTeX is running
  \ExecuteOptions{vtex}%
\fi
\endinput
%%% cut %%% color.cfg %%% cut %%%

-- 


More information about the pdftex mailing list