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.)
graphics
andcolor
packages may be used in plain tex. ctan.org/pkg/miniltx