% \iffalse meta-comment % %% File: mathcolor.dtx (C) Copyright 2021-2022 % Frank Mittelbach, LaTeX Team % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % https://www.latex-project.org/lppl.txt % % %%% From File: mathcolor.dtx % % \begin{macrocode} \def\mathcolorversion{v1.0c} \def\mathcolordate{2022/07/25} % \end{macrocode} %<*driver> \documentclass{l3doc} %\makeatletter \input{mathcolor.ltx} \makeatother \usepackage{color} % Fixing footnotes in functions and variables: this should be in l3doc! \newcommand\fixfootnote[2]{\footnotemark \AddToHookNext{env/#1/after}{\footnotetext{#2}}} \AddToHook{env/function/begin}{\def\footnote{\fixfootnote{function}}} \AddToHook{env/variable/begin}{\def\footnote{\fixfootnote{variable}}} \EnableCrossrefs \CodelineIndex \begin{document} \DocInput{mathcolor.dtx} \end{document} % % % \fi % % \title{Providing color in math\thanks{This file has version % \mathcolorversion\ dated \mathcolordate, \copyright\ \LaTeX\ % Project.}} % \author{Frank Mittelbach} % % \maketitle % % % \section{Introduction} % % It is possible to use the \cs{color} command in math formulas but % doing that has a number of restrictions and often leads to rather % unreadable input. For example, to color the summation sign in red % in the following formula % \[ % X = \color{red} \sum_{{\color{black} i=1}}^{{\color{black} n}} \color{black} x_i % \] % A total of four color commands and a number of seemingly % unnecessary extra braces in the sub and superscript are needed: %\begin{verbatim} % \[ X = \color{red} \sum % _{{\color{black} i=1}} % without {{ the superscript is misplaced % ^{{\color{black} n}} % without {{ the \sum is black % \color{black} % without the x_i is red % x_i \] %\end{verbatim} % While this is ugly but at least works, other things are simply % impossible, e.g., % \[ \left\{ \frac{1}{2} \mathcolor{red}{\right\}} \] % are not achievable at all because of the fact that \cs{left} and % \cs{right} form a group. Thus, a \cs{color} command immediately % before the \cs{right} has no effect and \verb=\}= remains black. % Putting the color change before the \cs{left} and then resetting % the color inside would work if you want to color both braces, but % the above combination or the attempt to color only the left brace % fails always. % % Using \cs{textcolor} in formulas appears to work on first glance, % but it produces spacing problems that are not easy to overcome % either, because it generates a single \cs{mathord} and no % longer reflects its input, thus % \verb/\[ a = b \textcolor{red}{\neq} c \]/ % produces % \[ a = b \textcolor{red}{\neq} c \] % This is a case one could mend by wrapping the % \cs{textcolor} and its arguments in a \cs{mathrel}, but even when % that is possible, the resulting formula source is difficult to % maintain and in other situations the solutions are even more % complicated or there is no solution at all. % % We therefore offer now a dedicated math coloring command named % \cs{mathcolor}. % % \begin{function}{\mathcolor} % \begin{syntax} % \cs{mathcolor} \oarg{model} \Arg{color-spec} \Arg{math material} % \end{syntax} % It has the same arguments as \cs{textcolor} but is intended for % use in formulas. The command does not generate a group and the % \meta{math material} retains its math atom states and it correctly % handles sub and superscripts that follow. % \end{function} % % The command can also be used to color a single opening or closing % symbol, e.g., the correct input to our earlier example is %\begin{verbatim} % \[ \left\{ \frac{1}{2} \mathcolor{red}{\right\}} \] %\end{verbatim} % which is how it was produced. % % If you attempt to color large operators that use explicit % \cs{limits}, \cs{nolimits}, or \cs{displaylimits}, then these % limit controls need to immediately follow the operator. However, % \cs{mathcolor} is written in a way that it is possible write %\begin{verbatim} % \[ \mathcolor{red}{\int}\limits_0^1 \textrm{ or } % \mathcolor{red}{\int\limits}_0^1 \] %\end{verbatim} % and achieve the same effect: % \[ \mathcolor{red}{\int}\limits_0^1 \textrm{ or } % \mathcolor{red}{\int\limits}_0^1 \] % % % \MaybeStop{\setlength\IndexMin{200pt} \PrintIndex } % % % \section{The Implementation} % % The code is called inside of the \pkg{color} or \pkg{xcolor} % packages, so \texttt{@} is already a letter, but the % coding here is done in the L3 programming layer, so we need to % activate that. But first we check if this file was loaded before, % e.g., when both \pkg{color} and \pkg{xcolor} are in the preamble % and in that case we stop immediately. % \begin{macrocode} %<*code> \ifcsname mathcolor\endcsname \endinput \fi % \end{macrocode} % % \begin{macrocode} \ExplSyntaxOn %<@@=mathcolor> % \end{macrocode} % % \begin{macro}{\g_@@_seq} % We need to keep our own stack of color commands, so we allocate a % sequence for that. % \begin{macrocode} \seq_new:N \g_@@_seq % \end{macrocode} % \end{macro} % % % \begin{macro}{\mathcolor} % The document-level command which expects a color (if unnamed then % the optional argument is the model) and colors the content of the % second argument (like \cs{textcolor}, but without spacing % problems in math). % \begin{macrocode} \DeclareDocumentCommand \mathcolor { o m m } { % \end{macrocode} % The \cs{mathcolor} is only supported in math mode because in text % mode it has problems scanning away a space after it, for example. % We therefore raise an error if it executes % anywhere else. The \LaTeXe{} error command is a % bit strangely named, because in the kernel it is only used for % math alphabets, but the message it gives is fine. % \changes{v1.0b}{2022/01/28}{Restrict command to math mode} % \begin{macrocode} \mode_if_math:F { \non@alpherr {\mathcolor\space} } % \end{macrocode} % First real action is to save the current color value on a stack % (needed if the command is nested or contains some further color % changes with \cs{color} inside). % \begin{macrocode} \seq_gpush:No \g_@@_seq \current@color % \end{macrocode} % Then we switch to the new color, but we do not want to reset the % color after the group (which is done by \cs{color} using % \cs{aftergroup}\cs{reset@color}). The best solution here would be % if the color packages would provide a command doing just the % color switching, but for now we simply undo that part by pushing % \cs{use_none:n} which gobbles the \cs{reset@color} added by % \cs{color} with \cs{aftergroup}. % \begin{macrocode} \group_insert_after:N \use_none:n % \end{macrocode} % Switching the color is also slightly suboptimal, because % depending on whether or not we have a \meta{model} argument, we % have to call \cs{color} with or without the optional argument. But % going low-level here is not an option as we need to support % different color packages and their internals are not identical. % \begin{macrocode} \IfValueTF {#1} { \color[#1]{#2} } { \color{#2} } % \end{macrocode} % Then comes the math material we want to see colored: % \begin{macrocode} #3 % \end{macrocode} % After that we need to reset the color ourselves (without a group % that does it for us), i.e., popping the saved color from our % stack, but there are some twists to that so we do this in a % separate command (which in fact needs to be called several times, % so inlining the code wouldn't be possible. % \begin{macrocode} \@@_scan_for_scripts:w } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_scan_for_scripts:w} % The complication when changing the color back is due to the fact % that the \cs{mathcolor} may be followed by \verb=^= or \verb=_= % or the hidden superscript \texttt{'} and its argument may end in a % \cs{mathop} in which case the sub and superscripts may be % attached as \cs{limits} instead of after the material. All cases % need separate treatment. % And we need to watch out for an upcoming \verb=&= and avoid % prematurely triggering the end of an alignment cell. % \changes{v1.0c}{2022/07/25}{Avoid ending an alignment cell % prematurely when hitting an \texttt{\&} during % scanning (gh/901)} % \begin{macrocode} \cs_new_protected:Npn \@@_scan_for_scripts:w { % \end{macrocode} % We need to look at what follows \cs{mathcolor}, and we need to do % that ignoring (and dropping) any spaces and \cs{relax} as \TeX{} % would do in normal math processing (for example before a subscript % token). We do this with expansion so that hidden sub or superscripts % in macros are still found as long as the macros are expandable. % \begin{macrocode} \peek_remove_filler:n { % \end{macrocode} % To avoid problems hitting on \verb=&= we start with a % \cs{group_align_safe_begin:}. That has to be ended with % \cs{group_align_safe_end:} when the % danger (aka scanning) is over, which, due to the branching below, happens at % four different points, i.e., when the \cs{mathcolor} is % \begin{enumerate} % \item % followed by a \enquote{normal} token; % \item % followed by a braced sub/superscript; % \item % followed by an unbraced sub/superscript; % \item % followed by one of the \cs{limits} primitives. % \end{enumerate} % In each case we have to end the align safe group and we mark the % points below in the code for easy reference. % \begin{macrocode} \group_align_safe_begin: % \end{macrocode} % We first parse for \cs{c_math_subscript_token} or % \cs{c_math_superscript_token}. % After \cs{peek_remove_filler:n} is done, it sets \cs{l_peek_token} % equal to the next non-filler token, so we can avoid unnecessary % work and just compare that. If either of the tokens is found, call % \cs{@@_handle_scripts:Nw}: % \begin{macrocode} \token_case_catcode:NnTF \l_peek_token { \c_math_subscript_token { } \c_math_superscript_token { } } { \@@_handle_scripts:Nw } % \end{macrocode} % Otherwise we check if it was any of the limit operation % primitives. If that is the case, e.g., if we have a situation % such as %\begin{verbatim} % \mathcolor{red}{\int}\limits_1 %\end{verbatim} % we have to move it directly after the \cs{int} to ensure there % is no color reset between the operator and the \cs{limits} command. % \begin{macrocode} { \token_case_meaning:NnTF \l_peek_token { \limits { \limits } \nolimits { \nolimits } \displaylimits { \displaylimits } } % \end{macrocode} % Once that is done, we have to get rid of the token we peeked at % and then restart scanning for sub or superscripts. Given that % \cs{@@_scan_for_scripts:w} expands while scanning the simplest % solution is to add \cs{use_none:n} in front of the peeked at % token. % % Here we end the align safe group and % \cs{@@_scan_for_scripts:w} will start a new one. % \begin{macrocode} { \group_align_safe_end: % case 4 \@@_scan_for_scripts:w \use_none:n } % \end{macrocode} % If it was not one of these we look for a \texttt{'} and if found remove it and % replace it by its expansion. The reason we have to do this (and % not rely on the earlier peeking to expand for us is the fact % that \texttt{'} is only ``math active'' and that doesn't expand % under \cs{expanded} or \cs{expandafter}. % \begin{macrocode} { \token_if_eq_meaning:NNTF \l_peek_token ' { \@@_handle_scripts:Nw ^ \c_group_begin_token \exp_after:wN \prim@s \use_none:n } % \end{macrocode} % If it is anything else we finish off which means we reset the % color (because we prevented that before to happen automatically % after the next group) and pop the color stack setting \cs{current@color}. % \begin{macrocode} { \group_align_safe_end: % case 1 \reset@color \seq_gpop:NN \g_@@_seq \current@color } } } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_handle_scripts:Nw} % The tricky part of handling sub and superscripts is that we have % to reset color to the one that is on the stack but reset it back % to what it was before to allow for cases like %\begin{verbatim} % \[ \mathcolor{red}{a+\sum}_{i=1}^n \] %\end{verbatim} % Here, \TeX{} constructs a \cs{vbox} stacking subscript, summation % sign, and superscript. So technically the superscript comes first % and the \cs{sum} that should get colored red is the middle. % \begin{macrocode} \cs_new_protected:Npn \@@_handle_scripts:Nw #1 { % \end{macrocode} % The argument is either \verb=^= or \verb=_=, so we execute it and % explicitly open two \verb={= groups. We need two because color % resets are always done after a group, so the first group is for % script material (in case it was just something like \verb=_i=) % and the second is needed for the color reset to keep it within % the super or subscript. If that reset would happen after it then % the color \cs{special} would interfere with \TeX{} math spacing. % \begin{macrocode} #1 \c_group_begin_token \c_group_begin_token % \end{macrocode} % Now it is time to change the color back to the one on the stack. % \begin{macrocode} \seq_get:NN \g_@@_seq \current@color \set@color % \end{macrocode} % \cs{set@color} adds \cs{aftergroup}\cs{reset@color}. We now a add % bit more so that the code executed after the current (inner) group % looks like this: %\begin{verbatim} % \reset@color } \@@_scan_for_scripts:w %\end{verbatim} % The \cs{@@_scan_for_scripts:w} then retakes control and initiates % parsing for another sub or superscript. % \begin{macrocode} \group_insert_after:N \c_group_end_token \group_insert_after:N \@@_scan_for_scripts:w % \end{macrocode} % Before we give control to \TeX{} to process the sub or % superscript some final adjustment is necessary: if the input was % \verb=^{...}= then we have one \verb={= too many, because we % already supplied the outer one already. In that case we drop % it. Otherwise we have an unbraced single token sub or superscript % which means we are missing a closing \verb=}= at the end and need % to account for that: this is done in false branch by % \cs{use_ii_i:nn}. % % After scanning for a brace all scanning is done, so here are the % other two points where we have to end the align safe group (in % the true and false case). % \begin{macrocode} \peek_remove_filler:n { \token_if_eq_meaning:NNTF \l_peek_token \c_group_begin_token { \group_align_safe_end: % case 2 \peek_catcode_remove:NT \c_group_begin_token { } } { \exp_after:wN \group_align_safe_end: % case 3 \use_ii_i:nn \c_group_end_token } } } % \end{macrocode} % \end{macro} % % \begin{macrocode} %<@@=> % \end{macrocode} % % \begin{macrocode} \ExplSyntaxOff % % \end{macrocode} % % \Finale % % %%%%%%%%%%%%%%%% \endinput %%%%%%%%%%%%%%%%