10

I'd like to have a command \globalred that makes all subsequent text red, even if the command is issued inside a group. For example, the output of the TeX file

\documentclass{article}
\begin{document}
a {\globalred b} c
\end{document}

should be  desired output, with a red c. I do have some very hacky solution, but it only works by redefining the \set@color and \reset@color commands from pdftex.def to what they where up to version 0.03t (or by using an old TeX installation before 2007):

\documentclass{article}
\usepackage[pdftex]{xcolor}
\makeatletter
\def\set@color  {\pdfliteral{\current@color}%
                 \aftergroup\reset@color}
\def\reset@color{\pdfliteral{\current@color}}
\def\globalred  {\xdef\current@color{\xcolor@ {}{1 0 0 rg 1 0 0 RG}{rgb}{1,0,0}}%
                 \set@color}
\makeatother
\begin{document}
a {\globalred b} c
\end{document}

This leads me to the following questions:

  • Is there an "official" way to achieve what I want?
  • Why doesn't \set@color from more recent pdftex.def versions act globally anymore, although I globally redefine \current@color with \xdef? (Is this a design choice or just an unforeseen side effect of the new implementation of \set@color?)

Let me shortly explain how I came up with this: in my hacky code above, I'm roughly emulating what beamer does to produce transparent text. beamer aims to do this in a way that transcedes groups (see the user guide, pages 24 and 79), but it appears that it doesn't work anymore; at least for me, the example on page 79 gives strange output:

strange output of itemize with pauses

The same problem (and hacky solution) came up in this question my mine.

3
  • @egreg: Oops - thank you! (I think you found a typo in the beamer user guide, page 24 :-)) Commented Sep 20, 2012 at 12:22
  • @HendrikVogt It's "transcend", actually. :). It's from Latin "trans" plus "scandere" (to climb), not "trans" plus "cedere" (to move). Compare "descend" and "procede".
    – egreg
    Commented Sep 20, 2012 at 12:48
  • @egreg: Thanks, yeah, now I got it. I just copied the word from the beamer user guide without thinking about it :-) Commented Sep 20, 2012 at 12:53

2 Answers 2

14

There is no official way to do it in LaTeX. Color changes are bound to groups. That the "global trick" works with older versions of pdftex.def is due to the shortcomings of the color implmentation there. Older versions of pdfTeX do not provide a color stack and have therefore serious trouble with page breaks.

Dvips provides a color stack where the current color is put on the stack in the driver, the color is changed and after the group the color of the stack is restored, \current@color is not used during restoring the color value. The same now happens with pdftex.def and pdfTeX, if they are not too old. Also pdfTeX improves the color stack of dvips by supporting multiple color stacks. For example, an additional color stack can be used for footnotes (package pdfcol) to support color changes in footnotes that are broken across pages. See packages pdfcolparrallel or pdfcolparcolumns for other examples. Furthermore other parameters than color can be managed this way, see example package pdftransparent for transparency or pdfrender.

\documentclass{article}
\usepackage[pdftex]{xcolor}
\makeatletter
\def\set@color{%
  \pdfliteral{\current@color}%
  \aftergroup\reset@color
}
\def\reset@color{%
  \pdfliteral{\current@color}%
}
% colorfix stuff for old implementation of color for pdfTeX
\RequirePackage{ltxcmds}
\AtBeginDocument{%
  \ltx@ifundefined{@ldc@l@r}{%
    \let\@ldc@l@r\color
    \def\color{%
      \ltx@IfUndefined{if@inlabel}{}{%
        \csname if@inlabel\expandafter\endcsname
        \expandafter\leavevmode\csname fi\endcsname
      }%
      \@ldc@l@r
    }%
  }{}%
  \ltx@ifundefined{@lduseb@x}{%
    \ltx@IfUndefined{usebox}{}{%
      \let\@lduseb@x\usebox
      \def\usebox#1{\@lduseb@x{#1}\reset@color}%
    }%
  }{}%
}

\newcommand*{\globalcolor}{%
  \let\saved@set@color\set@color
  \def\set@color{%
    \saved@set@color
    \global\let\current@color\current@color
    \let\set@color\saved@set@color
  }%
  \color
}
\makeatother

\begin{document}
a \textcolor{blue}{b \globalcolor{red}c }d
\end{document}

Example

\pdfcolorstack could be disabled, then pdftex.def would automatically switch to the old behaviour. However this would also break other stuff not related to color (e.g. pdfrender, pdftransparent, …).

And this \globalcolor will not work for other drivers like dvips. Therefore I do not recommend this method.

3
  • Tanks a lot for your answer; I think I'll need some time to digest it. It's a shame that there's no official way! I hope this doesn't mean that there's no good way to fix the beamer problem I mention at the end of my question. Commented Sep 16, 2012 at 10:56
  • If I could upvote more often I would do it. Commented Sep 16, 2012 at 11:13
  • May I ask you to have a look at what I did in my answer? Maybe that version doesn't break all the stuff you mention in your answer? (I do see that using \current@color in my redefinition of \reset@color contravenes the general idea of how \reset@color should work.) Thank you! Commented Sep 18, 2012 at 11:46
6

OK, I came up with another solution that doesn't work by disabling the pdf color stack but by manipulating it. The idea is to use the set option of the \pdfcolorstack primitive. I hope this has less side effects than the crude idea from the question. (One downside is that my redefinition of \reset@color causes problems at page breaks; see Heiko Oberdieck's comment.)

Edit: Now the solution works with pdflatex and with latex -> dvips. I'm not too happy with the code for the dvips driver, but it appears that the driver doesn't complain if color pop is used for an empty color stack.

output with c, d and f in red

\documentclass{article}
\usepackage{xcolor}
\makeatletter
\let\rs@c\reset@color
\def\dvips@driver{dvips.def}
\def\pdftex@driver{pdftex.def}
\ifx\Gin@driver\pdftex@driver
  \def\reset@color{%
    \rs@c
    \pdfcolorstack\@pdfcolorstack set{\current@color}%
  }
\fi
\ifx\Gin@driver\dvips@driver
  \def\reset@color{%
    \rs@c
    \rs@c
    \set@color
  }
\fi
\newcommand*{\globalcolor}{%
  \let\saved@set@color\set@color
  \def\set@color{%
    \saved@set@color
    \let\set@color\saved@set@color
    \global\let\current@color\current@color
  }%
  \color
}
\makeatother
\begin{document}
\textcolor{green}{%
  a \textcolor{blue}{b \globalcolor{red} c }d \textcolor{black} e
}f
\end{document}
2
  • You get the old problems with colors at page breaks of the old implementation without color stacks. Try \textcolor{blue}{blue\newpage ?color?\color{red}red\newpage}. Then ?color? is not blue but red. Commented Sep 18, 2012 at 12:08
  • Ah, OK, thank you - that's what I almost expected. But it's no problem for me since I want to use this as a fix for my beamer problem, where there are no automatic page breaks. (I added a word of caution to my answer.) Commented Sep 18, 2012 at 13:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .