[Tex/LaTex] Coloured text in plain tex

colorplain-tex

Recently I became intrigued by plain TeX because it gives even more flexibility than regular LaTeX.

In my document I wish to use grayscales of text. In LaTeX, producing this result is achieved through the color package. texdoc color gives great documentation on the graphics bundle by David Carlisle, but extensive documentation on the implementation is missing.

Alternatively I could try working my way through xcolor which is substantially more complex when it comes to its implementation, but behold, a complete chapter on the implementation is missing in that package too.

I've found some workarounds on the comp.text.tex forums (http://compgroups.net/comp.text.tex/problem-using-color-and-graphicx-in-plain-tex/1918061), which is of course very easy and (if you will) convenient, but these solutions conflict at its heart with the primary reason as outlined in Reasons to use plain TeX, namely that the plain TeX format is not affected by the sands of time. Using a LaTeX package in a plain TeX document could possibly be argued to touch on a gray area, e.g. "the color package is unlikely to change in the near future". However, from my vantage point, the purpose of this question could stretch even further, giving an outline of implementation of an interlocking between a graphical driver (which I assume is at play in the color package and related packages) and TeX.

So I would like to ask about the implementation of the color package in plain TeX, or an alternative implementation with similar functionality.

Best Answer

As the question is focussed on learning how these things may be done just using the primitives (I'd agree with David's answer that loading the color package in plain is an easier route).

What I'll do here is implement much the same approach as is taken by the color package, with appropriate tests for classical TeX (dvips or dvipdfm(x) drivers), pdfTeX/LuaTeX in PDF mode and XeTeX. As there is a bit going on, I'll intersperse the code with comments.

First, set up a conditional to test for direct PDF output

\newif\ifpdfmode

\begingroup\expandafter\expandafter\expandafter\endgroup
\expandafter\ifx\csname pdfoutput\endcsname\relax
\else
  \ifnum\pdfoutput>0 %
    \expandafter\expandafter\expandafter\pdfmodetrue
  \fi
\fi

Define the current colour as black, using an \edef so that once defined there are no conditionals about (the same idea applies to the rest of the code)

\edef\currentcolor{%
  \ifpdfmode
    0 g 0 G%
  \else
    gray 0%
  \fi
}

Set up a pre-defined colour: I've just done one (red) as a demo:

\edef\colorred{%
  \ifpdfmode
    1 0 0 rg 1 0 0 RG%
  \else
    rgb 1 0 0%
  \fi
}

For direct PDF output, there may be a colour stack available (since pdfTeX 1.40.0). A one-off test will tell us this: if there is no stack, just restore the colour manually. See the pdfTeX manual for the details here.

\begingroup\expandafter\expandafter\expandafter\endgroup
\expandafter\ifx\csname pdfcolorstack\endcsname\relax
  \ifpdfmode
    \def\pdfcolorstackpush{\pdfliteral{\currentcolor}}%
    \let\pdfcolorstackpop\pdfcolorstackpush
  \fi
\else
  \chardef\colorstack=0 %
  \def\pdfcolorstackpush{%
    \pdfcolorstack\colorstackcnt push{\currentcolor}%
  }%
  \def\pdfcolorstackpop{%
    \pdfcolorstack\colorstackcnt pop\relax%
  }%
\fi

The main macro to set colour starts with a test: if the argument is the name of a pre-defined colour use that, otherwise assume a hard-coded engine-specific value. (A more sophisticated approach is to convert the colour to the correct format: as that is not asked for in the question I'll leave as an exercise). Once the colour is set up, insert the appropriate special (noting the \edef will again mean at point of use there are no conditionals):

\edef\color#1{%
  \begingroup\noexpand\expandafter\noexpand\expandafter\noexpand\expandafter\endgroup
  \noexpand\expandafter\noexpand\ifx\noexpand\csname color#1\noexpand\endcsname\relax
    \def\noexpand\currentcolor{#1}%
  \noexpand\else
    \noexpand\expandafter\let\noexpand\expandafter\noexpand\currentcolor
      \noexpand\csname color#1\noexpand\endcsname
  \noexpand\fi
  \ifpdfmode
    \noexpand\pdfcolorstackpush
  \else
    \special{color push \noexpand\currentcolor}%
  \fi
  \aftergroup\noexpand\resetcolor
}

Following the color approach, a reset macro is also created using the appropriate special.

\edef\resetcolor{%
  \ifpdfmode
    \noexpand\pdfcolorstackpop
  \else
    \special{color pop}%
  \fi
}

The demo itself. The implementation above relies on a level of grouping inside boxes, which in LaTeX would be done by the \savebox 'wrapper' for \hbox (and so on). In plain that's not the case, so a colour-safe box needs a group. This could of course be put inside an appropriate set of wrapper macros:

\newbox\mybox
\setbox\mybox=\hbox{\begingroup\color{red}Red text\endgroup}
Surrounding text \box\mybox \space and more of it.
\bye

(Note: I've constructed the above in much the same way as I'd do using DocStrip for creating separate files. As DocStrip is not involved, this costs of some conditional/edef work.)

Related Question