% \iffalse meta-comment
%
%% File: l3color.dtx
%
% Copyright (C) 2017-2020 The LaTeX3 Project
%
% 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
%
%    http://www.latex-project.org/lppl.txt
%
% This file is part of the "l3experimental bundle" (The Work in LPPL)
% and all files in that bundle must be distributed together.
%
% -----------------------------------------------------------------------
%
% The development version of the bundle can be found at
%
%    https://github.com/latex3/latex3
%
% for those people who are interested.
%
%<*driver|package>
\RequirePackage{expl3}
%</driver|package>
%<*driver>
\documentclass[full]{l3doc}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{^^A
%   The \textsf{l3color} package\\ Experimental color support^^A
% }
%
% \author{^^A
%  The \LaTeX3 Project\thanks
%    {^^A
%      E-mail:
%        \href{mailto:latex-team@latex-project.org}
%          {latex-team@latex-project.org}^^A
%    }^^A
% }
%
% \date{Released 2020-06-03}
%
% \maketitle
%
% \begin{documentation}
%
% \section{Color models}
%
% A color \emph{model} is a way to represent sets of colors. Different models
% are particularly suitable for different output methods, \emph{e.g.}~screen
% or print. Parameter-based models can describe a very large number of unique
% colors, and have a varying number of \emph{axes} which define a color
% space. In contrast, various proprietary models are available which define
% \emph{spot} colors.
%
% Core models are used to pass color information to output; these are
% \enquote{native} to \pkg{l3color}. Core models use real numbers in the range
% $[0,1]$ to represent values. The core models supported here are
% \begin{itemize}
%   \item \texttt{gray} Grayscale color, with a single axis running from
%     $0$ (fully black) to $1$ (fully white)
%   \item \texttt{rgb} Red-green-blue color, with three axes, one for each of
%     the components
%   \item \texttt{cmyk} Cyan-magenta-yellow-black color, with four axes, one for
%     each of the components
%   \item \texttt{spot} Spot color, with one value, the name of the color
%     (see \url{https://helpx.adobe.com/indesign/using/spot-process-colors.html}
%     for details of the use of spot colors in print)
% \end{itemize}
% There are also interface models: these are convenient for users but have
% to be manipulated before storing/passing to the backend. Interface models
% are primarily integer-based: see below for more detail. The supported
% interface models are
% \begin{itemize}
%   \item \texttt{Hsb} Hue-saturation-brightness color, with three axes, integer
%     in the range $[0,360]$ for hue, real values in the range $[0,1]$ for
%     saturation and brightness
%   \item \texttt{HSB} Hue-saturation-brightness color, with three axes, integer
%     in the range $[0,360]$ for hue, integer values in the range $[0,255]$ for
%     saturation and brightness
%   \item \texttt{HTML} HTML format representation of RGB color given as a
%     single six-digit hexadecimal number
%   \item \texttt{RGB} Red-green-blue color, with three axes, one for each of
%     the components, values as integers from $0$ to $255$
% \end{itemize}
% All interface models are internally stored as |rgb|.
% 
% Additional models may be created to allow mixing of spot colors
% with each other or with those from other models. See
% Section~\ref{l3color:sec:spot} for more detail of spot color support.
%
% When color is selected by model, the \meta{values} given are specified as
% a comma-separated list. The length of the list will therefore be determined
% by the detail of the model involved.
%
% Color models (and interconversion) are complex, and more details are given
% in the manual to the \LaTeXe{} \pkg{xcolor} package and in the
% \emph{PostScript Language Reference Manual}, published by Addison--Wesley.
%
% \section{Color expressions}
%
% In addition to allowing specification of color by model and values,
% \pkg{l3color} also supports color expressions. These are created
% by combining one or more color names, with the amount of each specified
% as a percentage. The latter is given between |!| symbols in the expression.
% Thus for example
% \begin{verbatim}
%   red!50!green
% \end{verbatim}
% is a mixture of $50\,\%$ red and $50\,\%$ green. A trailing percentage is
% interpreted as implicitly followed by |white|, and so 
% \begin{verbatim}
%   red!25
% \end{verbatim}
% specifies $25\,\%$ red mixed with $75\,\%$ white.
%
% Where the models for the mixed colors are different, the model of the first
% color is used. Thus
% \begin{verbatim}
%   red!50!cyan
% \end{verbatim}
% will result in a color specification using the |rgb| model, made up of
% $50\,\%$ red and  $50\,\%$ of cyan \emph{expressed in \texttt{rgb}}. As color
% model interconversion is not exact.
%
% The one exception to the above is where the first model in an expression is
% |gray|. In this case, the order of mixing is \enquote{swapped} internally, so
% that for example
% \begin{verbatim}
%   black!50!red
% \end{verbatim}
% has the same result as
% \begin{verbatim}
%   red!50!black
% \end{verbatim}
% (the predefined colors |black| and |white| use the |gray| model).
%
% Where more than two colors are mixed in an expression, evaluation takes place
% in a stepwise fashion. Thus in
% \begin{verbatim}
%   cyan!50!magenta!10!yellow
% \end{verbatim}
% the sub-expression
% \begin{verbatim}
%   cyan!50!magenta
% \end{verbatim}
% is first evaluated to give an intermediate color specification, before
% the second step
% \begin{verbatim}
%   <intermediate>!10!yellow
% \end{verbatim}
% where |<intermediate>| represents this transitory calculated value.
%
% Within a color expression, |.| may be used to represent the color active
% for typesetting (the current color). This allows for example
% \begin{verbatim}
%   .!50
% \end{verbatim}
% to mean a mixture of $50\,\%$ of current color with white.
%
% (Color expressions supported here are a subset of those provided by
% the \LaTeXe{} \pkg{xcolor} package. At present, only such features as are
% clearly useful have been added here.)
%
% \section{Named colors}
%
% Color names are stored in a single namespace, which makes them accessible
% as part of color expressions. Whilst they are not reserved in a technical
% sense, the names |black|, |white|, |red|, |green|, |blue|, |cyan|, |magenta|
% and |yellow| have special meaning and should not be redefined. Color names
% should be made up of letters, numbers and spaces only: other characters are
% reserved for use in color expressions. In particular, |.| represents the
% current color at the start of a color expression.
%
% \begin{function}{\color_set:nn}
%   \begin{syntax}
%     \cs{color_set:nn} \Arg{name} \Arg{color expression}
%   \end{syntax}
%   Evaluates the \meta{color expression} and stores the resulting
%   color specification as the \meta{name}.
% \end{function}
%
% \begin{function}{\color_set:nnn}
%   \begin{syntax}
%     \cs{color_set:nnn} \Arg{name} \Arg{model} \Arg{value(s)}
%   \end{syntax}
%   Stores the color specification equivalent to the \meta{model} and
%   \meta{values} as the \meta{name}.
% \end{function}
%
% \begin{function}{\color_set_eq:nn}
%   \begin{syntax}
%     \cs{color_set_eq:nn} \Arg{name1} \Arg{name2}
%   \end{syntax}
%   Copies the color specification in \meta{name2} to \meta{name1}. The
%   special name |.| may be used to represent the current color, allowing
%   it to be saved to a name.
% \end{function}
%
% \begin{function}{\color_show:n}
%   \begin{syntax}
%     \cs{color_show:n} \Arg{name}
%   \end{syntax}
%   Displays the color specification stored in the \meta{name} on the
%   terminal.
% \end{function}
%
% \section{Selecting colors}
%
% \begin{function}{\color_select:n}
%   \begin{syntax}
%     \cs{color_select:n} \Arg{color expression}
%   \end{syntax}
%   Parses the \meta{color expression} and then activates the resulting
%   color specification for typeset material.
% \end{function}
%
% \begin{function}{\color_select:nn}
%   \begin{syntax}
%     \cs{color_select:nn} \Arg{model} \Arg{value(s)}
%   \end{syntax}
%   Activates the color specification equivalent to the \meta{model} and
%   \meta{value(s)} for typeset material.
% \end{function}
%
% \begin{variable}{\l_color_fixed_model_tl}
%   When this is set to a non-empty value, colors will be converted to
%   the specified model when they are selected. Note that included images
%   and similar are not influenced by this setting.
% \end{variable}
%
% \section{Core color representation}
%
% To allow data to be handled internally, \pkg{l3color} uses a simple
% representation of color, based on that used by the \pkg{dvips} program.
% This is a token list made up of the model name followed by one or more
% data entries, each separated by a \emph{space}. The valid forms are thus
% \begin{itemize}
%   \item \texttt{gray \meta{gray}} Grayscale color with the \meta{gray}
%     value running from $0$ (fully black) to $1$ (fully white)
%   \item \texttt{cmyk \meta{cyan} \meta{magenta} \meta{yellow} \meta{black}},
%     each of which falls in the range $[0,1]$
%   \item \texttt{rgb \meta{red} \meta{green} \meta{blue}},
%     each of which falls in the range $[0,1]$
%   \item \texttt{spot \meta{name} \meta{tint}} A pre-defined spot color,
%     where the \meta{name} should be a pre-defined string color name and the
%     \meta{tint} should be in the range $[0,1]$.
% \end{itemize}
%
% This core representation is produced when parsing color expressions.
% 
% \begin{function}{\color_parse:nN}
%   \begin{syntax}
%     \cs{color_parse:nN} \Arg{color expression} \Arg{tl}
%   \end{syntax}
%   Parses the \meta{color expression} as described above, and sets the
%   \meta{tl} to the equivalent \meta{core color representation}
%   (used at the backend level and based on \texttt{dvips} color
%   representation).
% \end{function}
%
% \section{Spot colors}
% \label{l3color:sec:spot}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3color} Implementation}
%
%    \begin{macrocode}
%<*initex|package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<@@=color>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*package>
\ProvidesExplPackage{l3color}{2020-06-03}{}
  {L3 Experimental color support}
%</package>
%    \end{macrocode}
%
% \subsection{Predefined color names}
%
% The ability to predefine colors with a name is a key part of this module and
% means there has to be a method for storing the results. At first sight, it
% seems natural to follow the usual \pkg{expl3} model and create a
% \texttt{color} variable type for the process. That would then allow both
% local and global colors, constant colors and the like. However, these names
% need to be accessible in some form at the user level, for selection of colors
% either simply by name or as part of a more complex expression. This does not
% require that the full name is exposed but does require that they can be
% looked up in a predictable way. As such, it is more useful to expose just the
% color names as part of the interface, with the result that only local color
% names can be created. (This is also seen for example in key creation in
% \pkg{l3keys}.) As a result, color names are declarative (no \texttt{new}
% functions).
%
% Since there is no need to manipulate colors \emph{en masse}, each is stored
% in a separate token list variable, rather than the alternative of using a
% single property list for all names.
%
% \subsection{Setup}
%
% \begin{variable}{\l_@@_internal_tl}
%    \begin{macrocode}
\tl_new:N \l_@@_internal_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\s_@@_mark}
%   Internal scan marks. \cs{s_@@_stop} is already defined in \pkg{l3color-base}.
%    \begin{macrocode}
\scan_new:N \s_@@_mark
%    \end{macrocode}
% \end{variable}
%
% \subsection{Utility functions}
%
% \begin{macro}[int, TF, EXP]{\@@_if_defined:n}
%   A simple wrapper to avoid needing to have the lookup repeated in too many
%   places.
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_defined:n #1 { T, F, TF }
  {
    \tl_if_exist:cTF { l_@@_named_ #1 _tl }
      \prg_return_true:
      \prg_return_false:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_extract:nNN, \@@_extract:VNN}
% \begin{macro}{\@@_extract:NNw}
%   Split the model and color from a named color, and store the two. No test
%   for the existence of the color: that is assumed to be the case (this
%   is internal only). Somewhat \enquote{old-fashioned} but should be quite
%   fast.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_extract:nNN #1#2#3
  {
    \exp_after:wN \exp_after:wN \exp_after:wN
      \@@_extract:NNw
      \exp_after:wN \exp_after:wN \exp_after:wN #2
      \exp_after:wN \exp_after:wN \exp_after:wN #3
        \cs:w l_@@_named_ #1 _tl \cs_end: \s_@@_stop
  }
\cs_generate_variant:Nn \@@_extract:nNN { V }
\cs_new_protected:Npn \@@_extract:NNw #1#2 #3 ~ #4 \s_@@_stop
  {
    \tl_set:Nn #1 {#3}
    \tl_set:Nn #2 {#4}
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsection{Model conversion}
%
% \begin{macro}{\@@_convert:nnN, \@@_convert:VVN}
% \begin{macro}{\@@_convert:nnnN}
% \begin{macro}[aux, EXP]
%   {
%     \@@_convert_gray_rgb:w
%     \@@_convert_gray_cmyk:w
%     \@@_convert_cmyk_gray:w
%     \@@_convert_cmyk_rgb:w
%     \@@_convert_rgb_gray:w
%     \@@_convert_rgb_cmyk:w
%   }
%  \begin{macro}[aux, EXP]{\@@_convert_rgb_cmyk:nnnn}
%    Model conversion is carried out using standard formulae, as described in
%    the manual for \pkg{xcolor} (see also the \emph{PostScript Language
%    Reference Manual}).
%    \begin{macrocode}
\cs_new_protected:Npn \@@_convert:nnN #1#2#3
  { \@@_convert:nnVN {#1} {#2} #3 #3 }
\cs_generate_variant:Nn \@@_convert:nnN { VV }
\cs_new_protected:Npn \@@_convert:nnnN #1#2#3#4
  {
    \str_if_eq:nnT {#1} { spot } % TO DO!!!
      { }
    \tl_set:Nx #4
      { \use:c { @@_convert_ #1 _ #2 :w } #3 ~ 0 ~ 0 ~ 0 \s_@@_stop }
  }
\cs_generate_variant:Nn \@@_convert:nnnN { nnV }
\cs_new:Npn \@@_convert_gray_rgb:w #1 ~ #2 \s_@@_stop
  { #1 ~ #1 ~ #1 }
\cs_new:Npn \@@_convert_gray_cmyk:w #1 ~ #2 \s_@@_stop
  { 0 ~ 0 ~ 0 ~ \fp_eval:n { 1 - #1 } }
%    \end{macrocode}
%   These rather odd values are based on \textsc{ntsc} television: the set are
%   used for the |cmyk| conversion.
%    \begin{macrocode}
\cs_new:Npn \@@_convert_rgb_gray:w #1 ~ #2 ~ #3 ~ #4 \s_@@_stop
  { \fp_eval:n { 0.3 * #1 + 0.59 * #2 + 0.11 * #3 } }
%    \end{macrocode}
%   The conversion from |rgb| to |cmyk| is the most complex: a two-step
%   procedure which requires \emph{black generation} and \emph{undercolor
%   removal} functions. The PostScript reference describes them as
%   device-dependent, but following \pkg{xcolor} we assume they are linear.
%   Moreover, as the likelihood of anyone using a non-unitary matrix here is
%   tiny, we simplify and treat those two concepts as no-ops.
%    \begin{macrocode}
\cs_new:Npn \@@_convert_rgb_cmyk:w #1 ~ #2 ~ #3 ~ #4 \s_@@_stop
  {
    \exp_args:Nf \@@_convert_rgb_cmyk:nnnn
      { \fp_eval:n { min ( 1 - #1 , 1 - #2 , 1 - #3 ) } } {#1} {#2} {#3}
  }
\cs_new:Npn \@@_convert_rgb_cmyk:nnnn #1#2#3#4
  {
    \fp_eval:n { min ( 1 , max ( 0 , 1 - #2 - #1 ) ) } \c_space_tl
    \fp_eval:n { min ( 1 , max ( 0 , 1 - #3 - #1 ) ) } \c_space_tl
    \fp_eval:n { min ( 1 , max ( 0 , 1 - #4 - #1 ) ) } \c_space_tl
    #1
  }
\cs_new:Npn \@@_convert_cmyk_gray:w #1 ~ #2 ~ #3 ~ #4 ~ #5 \s_@@_stop
  { \fp_eval:n { 1 - min ( 1 , 0.3 * #1 + 0.59 * #2 + 0.11 * #3 + #4 ) } }
\cs_new:Npn \@@_convert_cmyk_rgb:w #1 ~ #2 ~ #3 ~ #4 ~ #5 \s_@@_stop
  {
    \fp_eval:n { 1 - min ( 1 , #1 + #4 ) } \c_space_tl
    \fp_eval:n { 1 - min ( 1 , #2 + #4 ) } \c_space_tl
    \fp_eval:n { 1 - min ( 1 , #3 + #4 ) }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \subsection{Color expressions}
%
% \begin{variable}
%   {\l_@@_model_tl, \l_@@_value_tl, \l_@@_next_model_tl, \l_@@_next_value_tl}
%   Working space to store the color data whilst doing calculations: keeping
%   it on the stack is attractive but gets tricky (return is non-trivial).
%    \begin{macrocode}
\tl_new:N \l_@@_model_tl
\tl_new:N \l_@@_value_tl
\tl_new:N \l_@@_next_model_tl
\tl_new:N \l_@@_next_value_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\color_parse:nN}
% \begin{macro}{\@@_parse:nN}
% \begin{macro}{\@@_parse:Nw}
% \begin{macro}{\@@_parse_loop_init:Nnn}
% \begin{macro}{\@@_parse_loop:w}
% \begin{macro}{\@@_parse_loop:nn}
% \begin{macro}{\@@_parse_break:w}
% \begin{macro}{\@@_parse_end:}
% \begin{macro}[aux, EXP]{\@@_parse_mix:Nnnn, \@@_parse_mix:NVVn}
% \begin{macro}[aux, EXP]{\@@_parse_mix:nNnn}
% \begin{macro}[aux, EXP]
%   {
%     \@@_parse_mix_gray:nw ,
%     \@@_parse_mix_rgb:nw  ,
%     \@@_parse_mix_cmyk:nw
%   }
%   The main function for parsing color expressions removes actives but
%   otherwise expands, then starts working through the expression itself.
%   At the end, we apply the payload.
%    \begin{macrocode}
\cs_new_protected:Npn \color_parse:nN #1#2
  {
%<*package>
    \@@_backend_pickup:N \l_@@_current_tl
    \tl_set_eq:cN { l_@@_named_ . _tl } \l_@@_current_tl
%</package>
    \exp_args:Ne \@@_parse:nN { \tl_to_str:n {#1} } #2
  }
%    \end{macrocode}
%   Before going to all of the effort of parsing an expression, these two
%   precursor functions look for a pre-defined name, either on its own or
%   with a trailing |!| (which is the same thing).
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse:nN #1#2
  {
    \tl_if_exist:cTF { l_@@_named_ #1 _tl }
      { \tl_set_eq:Nc #2 { l_@@_named_ #1 _tl } }
      { \@@_parse:Nw #2#1 ! \s_@@_stop }
    \@@_check_model:N #2
  }
\cs_new_protected:Npn \@@_parse:Nw #1#2 ! #3 \s_@@_stop
  {
    \@@_if_defined:nTF {#2}
      {
        \tl_if_blank:nTF {#3}
          { \tl_set_eq:Nc #1 { l_@@_named_ #2 _tl } }
          { \@@_parse_loop_init:Nnn #1 {#2} {#3} }
      }
      {
        \__kernel_msg_error:nnn { color } { unknown-color } {#2}
        \tl_set_eq:NN \l_@@_current_tl \l_@@_named_black_tl
      }
  }
%    \end{macrocode}
%   Once we establish that a full parse is needed, the next job is to get the
%   detail of the first color. That will determine the model we use for the
%   calculation: splitting here makes checking that a bit easier.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_loop_init:Nnn #1#2#3
  {
    \group_begin:
      \@@_extract:nNN {#2} \l_@@_model_tl \l_@@_value_tl
      \@@_parse_loop:w #3 ! ! ! ! \s_@@_stop
      \tl_set:Nx \l_@@_internal_tl
        { \l_@@_model_tl \c_space_tl \l_@@_value_tl }
    \exp_args:NNNV \group_end:
    \tl_set:Nn #1 \l_@@_internal_tl
  }
%    \end{macrocode}
%   This is the loop proper: there can be an open-ended set of colors to parse,
%   separated by |!| tokens. There are a few cases to look out for. At the end
%   of the expression and with we find a mix of $100$ then we simply skip the
%   next color entirely (we can't stop the loop as there might be a further
%   valid color to mix in). On the other hand, if we get a mix of $0$ then
%   drop everything so far and start again. There is also a trailing
%   |white| to \enquote{read in} if the final explicit data is a mix.
%   Those conditions are separate from actually looping, which is therefore
%   sorted out by checking if we have further data to process: in contrast
%   to \pkg{xcolor}, we don't allow |!!| so the test can be simplified.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_loop:w #1 ! #2 ! #3 ! #4 ! #5 \s_@@_stop
  {
    \bool_lazy_or:nnF
      { \tl_if_blank_p:n {#1} }
      { \int_compare_p:nNn {#1} = { 100 } }
      {
        \int_compare:nNnTF {#1} = { 0 }
          {
            \tl_if_blank:nTF {#2}
              { \@@_extract:nNN { white } }
              { \@@_extract:nNN {#2} }
                \l_@@_model_tl \l_@@_value_tl
          }
          {
            \use:x
              {
                \@@_parse_loop:nn {#1}
                  { \tl_if_blank:nTF {#2} { white } {#2} }
              }
          }
      }
    \tl_if_blank:nF {#3}
      { \@@_parse_loop:w #3 ! #4 ! #5 \s_@@_stop }
    \@@_parse_end:
  }
%    \end{macrocode}
%   The \enquote{payload} of calculation in the loop first. If the model for
%   the upcoming color is different from that of the existing (partial) color,
%   convert the model. For |gray| the two are flipped round so that the outcome
%   is something with \enquote{real} color. We are then in a position to do the
%   actual calculation itself. The two auxiliaries here give us a way to break
%   the loop should an invalid name be found.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_loop:nn #1#2
  {
    \@@_if_defined:nTF {#2}
      {
        \@@_extract:nNN {#2} \l_@@_next_model_tl \l_@@_next_value_tl
        \tl_if_eq:NNF \l_@@_model_tl \l_@@_next_model_tl
          {
            \str_if_eq:VnT \l_@@_model_tl { gray }
              {
                \use:x
                  {
                    \tl_set:Nn \exp_not:N \l_@@_model_tl
                      { \l_@@_next_model_tl }
                    \tl_set:Nn \exp_not:N \l_@@_value_tl
                      { \l_@@_next_value_tl }
                    \tl_set:Nn \exp_not:N \l_next_@@_model_tl
                      { \l_@@_model_tl }
                    \tl_set:Nn \exp_not:N \l_next_@@_value_tl
                      { \l_@@_value_tl }
                  }
              }
            \@@_convert:VVN
              \l_@@_next_model_tl
              \l_@@_model_tl
              \l_@@_next_value_tl
          }
        \tl_set:Nx \l_@@_value_tl
          {
            \@@_parse_mix:NVVn
              \l_@@_model_tl \l_@@_value_tl \l_@@_next_value_tl {#1}
          }
      }
      {
        \__kernel_msg_error:nnn { color } { unknown-color } {#2}
        \@@_extract:nNN { black } \l_@@_model_tl \l_@@_value_tl
        \@@_parse_break:w
      }
  }
\cs_new_protected:Npn \@@_parse_break:w #1 \@@_parse_end: { }
\cs_new_protected:Npn \@@_parse_end: { }
%    \end{macrocode}
%   Do the vector arithmetic: mainly a question of shuffling input, along
%   with one pre-calculation to keep down the use of division.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_mix:Nnnn #1#2#3#4
  {
    \exp_args:Nf \@@_parse_mix:nNnn
      { \fp_eval:n { #4 / 100 } }
      #1 {#2} {#3}
  }
\cs_generate_variant:Nn \@@_parse_mix:Nnnn { NVV }
\cs_new:Npn \@@_parse_mix:nNnn #1#2#3#4
  {
    \use:c { @@_parse_mix_ #2 :nw } {#1}
      #3 \s_@@_mark #4 \s_@@_stop
  }
\cs_new:Npn \@@_parse_mix_gray:nw #1#2 \s_@@_mark #3 \s_@@_stop
  { \fp_eval:n { #2 * #1 + #3 * ( 1 - #1 ) } }
\cs_new:Npn \@@_parse_mix_rgb:nw
  #1#2 ~ #3 ~ #4 \s_@@_mark #5 ~ #6 ~ #7 \s_@@_stop
  {
    \fp_eval:n { #2 * #1 + #5 * ( 1 - #1 ) } \c_space_tl
    \fp_eval:n { #3 * #1 + #6 * ( 1 - #1 ) } \c_space_tl
    \fp_eval:n { #4 * #1 + #7 * ( 1 - #1 ) }
  }
\cs_new:Npn \@@_parse_mix_cmyk:nw
  #1#2 ~ #3 ~ #4 ~ #5 \s_@@_mark #6 ~ #7 ~ #8 ~ #9 \s_@@_stop
  {
    \fp_eval:n { #2 * #1 + #6 * ( 1 - #1 ) } \c_space_tl
    \fp_eval:n { #3 * #1 + #7 * ( 1 - #1 ) } \c_space_tl
    \fp_eval:n { #4 * #1 + #8 * ( 1 - #1 ) } \c_space_tl
    \fp_eval:n { #5 * #1 + #9 * ( 1 - #1 ) }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]
%   {
%     \@@_parse_model_gray:w, \@@_parse_model_rgb:w,
%     \@@_parse_model_cmyk:w, \@@_parse_model_spot:w
%   }
% \begin{macro}[EXP]{\@@_parse_number:n}
% \begin{macro}[EXP]{\@@_parse_number:w}
% \begin{macro}[EXP]{\@@_parse_spot_aux:w}
%   Turn the input into internal form, also tidying up the number quickly.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_model_gray:w #1 , #2 \s_@@_stop
  { gray ~ \@@_parse_number:n {#1} }
\cs_new:Npn \@@_parse_model_rgb:w #1 , #2 , #3 , #4 \s_@@_stop
  {
    rgb ~
    \@@_parse_number:n {#1} ~
    \@@_parse_number:n {#2} ~
    \@@_parse_number:n {#3}
  }
\cs_new:Npn \@@_parse_model_cmyk:w #1 , #2 , #3 , #4 , #5 \s_@@_stop
  {
    cmyk ~
    \@@_parse_number:n {#1} ~
    \@@_parse_number:n {#2} ~
    \@@_parse_number:n {#3} ~
    \@@_parse_number:n {#4}
  }
\cs_new:Npn \@@_parse_model_spot:w #1 , #2 \s_@@_stop
  {
    spot ~
    \@@_parse_spot_aux:w #1 ! 100 ! \s_@@_stop
  }
\cs_new:Npn \@@_parse_spot_aux:w #1 ! #2 ! #3 \s_@@_stop
  { #1 ~ \fp_eval:n { #2 / 100 } }
\cs_new:Npn \@@_parse_number:n #1
  {  \@@_parse_number:w #1 . 0 . \s_@@_stop }
\cs_new:Npn \@@_parse_number:w #1 . #2 . #3 \s_@@_stop
  { \tl_if_blank:nTF {#1} { 0 } {#1} . #2 }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}[EXP]
%   {
%     \@@_parse_model_Hsb:w, \@@_parse_model_HSB:w,
%     \@@_parse_model_HTML:w, \@@_parse_model_RGB:w
%   }
%    \begin{macrocode}
\cs_new:Npn \@@_parse_model_Hsb:w #1 , #2 , #3 , #4 \s_@@_stop
  {
    \exp_args:Ne \@@_parse_model_hsb:nnn { \fp_eval:n { #1 / 360 } }
      {#2} {#3}
  }
%    \end{macrocode}
%   The conversion here is non-trivial but is described at length
%   in the \pkg{xcolor} manual. For ease, we calculate the integer
%   and fractional parts of the hue first, then use them to work out the
%   possible values for $r$, $g$ and $b$ before putting them in the correct
%   places.
%    \begin{macrocode}
\cs_new:Npn \@@_parse_model_hsb:nnn #1#2#3
  {
    rgb ~
    \exp_args:Ne \@@_parse_model_hsb_aux:nnn
      { \fp_eval:n { 6 * #1 } } {#2} {#3}
  }
\cs_new:Npn \@@_parse_model_hsb_aux:nnn #1#2#3
  {
    \exp_args:Nee \@@_parse_model_hsb_aux:nnnn
      { \fp_eval:n { floor(#1) } } { \fp_eval:n { #1 - floor(#1) } }
      {#2} {#3}
  }
\cs_new:Npn \@@_parse_model_hsb_aux:nnnn #1#2#3#4
  {
    \use:e
      {
        \exp_not:N \@@_parse_model_hsb_aux:nnnnn
         { \@@_parse_number:n {#4} }
         { \fp_eval:n { round(#4 * (1 - #3) ,5) } }
         { \fp_eval:n { round(#4 * ( 1 - #3 * #2 ) ,5) } }
         { \fp_eval:n { round(#4 * ( 1 - #3 * (1 - #2) ) ,5) } }
         {#1}
      }
  }
\cs_new:Npn \@@_parse_model_hsb_aux:nnnnn #1#2#3#4#5
  { \use:c { @@_parse_model_hsb_ #5 :nnnn } {#1} {#2} {#3} {#4} }
\cs_new:cpn { @@_parse_model_hsb_0:nnnn } #1#2#3#4 { #1 ~ #4 ~ #2 }
\cs_new:cpn { @@_parse_model_hsb_1:nnnn } #1#2#3#4 { #3 ~ #1 ~ #2 }
\cs_new:cpn { @@_parse_model_hsb_2:nnnn } #1#2#3#4 { #2 ~ #1 ~ #4 }
\cs_new:cpn { @@_parse_model_hsb_3:nnnn } #1#2#3#4 { #2 ~ #3 ~ #1 }
\cs_new:cpn { @@_parse_model_hsb_4:nnnn } #1#2#3#4 { #4 ~ #2 ~ #1 }
\cs_new:cpn { @@_parse_model_hsb_5:nnnn } #1#2#3#4 { #1 ~ #2 ~ #3 }
\cs_new:cpn { @@_parse_model_hsb_6:nnnn } #1#2#3#4 { #1 ~ #2 ~ #2 }
\cs_new:Npn \@@_parse_model_HSB:w #1 , #2 , #3 , #4 \s_@@_stop
  {
    \exp_args:Neee \@@_parse_model_hsb:nnn
      { \fp_eval:n {#1 / 360} }
      { \fp_eval:n {#2 / 255} }
      { \fp_eval:n {#3 / 255} }
  }
\cs_new:Npn \@@_parse_model_HTML:w #1 , #2 \s_@@_stop
  { \@@_parse_model_HTML_aux:w #1 0 0 0 0 0 0 \s_@@_stop }
\cs_new:Npn \@@_parse_model_HTML_aux:w #1#2#3#4#5#6#7 \s_@@_stop
  {
    rgb ~
    \fp_eval:n { round(\int_from_hex:n {#1#2} / 255,5) } ~
    \fp_eval:n { round(\int_from_hex:n {#3#4} / 255,5) } ~
    \fp_eval:n { round(\int_from_hex:n {#5#6} / 255,5) }
  }
\cs_new:Npn \@@_parse_model_RGB:w #1 , #2 , #3 , #4 \s_@@_stop
  {
    rgb ~
    \fp_eval:n { round(#1 / 255,5) } ~
    \fp_eval:n { round(#2 / 255,5) } ~
    \fp_eval:n { round(#3 / 255,5) }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Selecting colors (and color models)}
%
% \begin{variable}{\l_color_fixed_model_tl}
%   For selecting a single fixed model.
%    \begin{macrocode}
\tl_new:N \l_color_fixed_model_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_check_model:N}
% \begin{macro}{\@@_check_model:w}
%   Check that the model in use is the one required.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_check_model:N #1
  {
    \tl_if_empty:NF \l_color_fixed_model_tl
      {
        \exp_after:wN \@@_check_model:w #1 \s_@@_stop
        \tl_if_eq:NNF \l_@@_model_tl \l_color_fixed_model_tl
          {
            \@@_convert:VVN \l_@@_model_tl \l_color_fixed_model_tl
              \l_@@_value_tl
          }
        \tl_set:Nx #1
          { \l_color_fixed_model_tl \c_space_tl \l_@@_value_tl }
      }
  }
\cs_new_protected:Npn \@@_check_model:w #1 ~ #2 \s_@@_stop
  {
    \tl_set:Nn \l_@@_model_tl {#1}
    \tl_set:Nn \l_@@_value_tl {#2}
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_select:}
%   A backend-neutral location for \enquote{last minute} manipulations before
%   handing off to the backend code.  We set the special |.| syntax here: this
%   will therefore always be available. The finalisation is separate from the
%   main function so it can also be applied to \emph{e.g.}~page color.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_select:
  {
    \tl_set_eq:cN { l_@@_named_ . _tl } \l_@@_current_tl
    \@@_select:V \l_@@_current_tl
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\color_select:n}
% \begin{macro}{\color_select:nn}
%   Parse the input expressions then get the backend to actually activate
%   them.
%    \begin{macrocode}
\cs_new_protected:Npn \color_select:n #1
  {
    \color_parse:nN {#1} \l_@@_current_tl
    \@@_select:
  }
\cs_new_protected:Npn \color_select:nn #1#2
  {
    \@@_direct:nnN {#1} {#2} \l_@@_current_tl
    \@@_select:
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \subsection{Direct model use}
%
% \begin{macro}{\@@_direct:nnN}
%   Directly set a color based on a model/value combination.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_direct:nnN #1#2#3
  {
    \cs_if_exist:cTF { @@_parse_model_ #1 :w }
      {
        \tl_set:Nx #3
          { \use:c {  @@_parse_model_ #1 :w } #2 , 0 , 0 , 0 , 0 \s_@@_stop }
        \@@_check_model:N #3
      }
      {
        \__kernel_msg_error:nnn { color } { unknown-model } {#1}
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \subsection{Defining named colors}
%
% \begin{variable}{\l_@@_named_tl}
%   Space to store the detail of the named color.
%    \begin{macrocode}
\tl_new:N \l_@@_named_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_store:Nn}
%   Store the named color unless it has an invalid name.
%     \begin{macrocode}
\cs_new_protected:Npn \@@_store:Nn #1#2
  {
    \str_if_eq:nnF {#2} { . }
      {
        \tl_clear_new:c { l_@@_named_ #2 _tl }
        \tl_set_eq:cN { l_@@_named_ #2 _tl } #1
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\color_set:nn}
% \begin{macro}{\color_set:nnn}
% \begin{macro}{\color_set_eq:nn}
%   Defining named colors has to include a step to force creation of the
%   underlying token list to avoid errors when checking is enabled.
%    \begin{macrocode}
\cs_new_protected:Npn \color_set:nn #1#2
  {
    \color_parse:nN {#2} \l_@@_named_tl
    \@@_store:Nn \l_@@_named_tl {#1}
  }
\cs_new_protected:Npn \color_set:nnn #1#2#3
  {
    \@@_direct:nnN {#2} {#3} \l_@@_named_tl
    \@@_store:Nn \l_@@_named_tl {#1}
  }
\cs_new_protected:Npn \color_set_eq:nn #1#2
  {
    \@@_if_defined:nTF {#2}
      {
        \tl_clear_new:c { l_@@_named_ #1 _tl }
        \str_if_eq:nnTF {#2} { . }
          { \tl_set_eq:cN { l_@@_named_ #1 _tl } \l_@@_current_tl }
          { \tl_set_eq:cc { l_@@_named_ #1 _tl } { l_@@_named_ #2 _tl } }
      }
      {
        \__kernel_msg_error:nnn { color } { unknown-color } {#2}
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% A small set of colors are always defined.
% \begin{macrocode}
\color_set:nnn { black } { gray } { 0 }
\color_set:nnn { white } { gray } { 1 }
\color_set:nnn { cyan }    { cmyk } { 1 , 0 , 0 , 0 }
\color_set:nnn { magenta } { cmyk } { 0 , 1 , 0 , 0 }
\color_set:nnn { yellow }  { cmyk } { 0 , 0 , 1 , 0 }
\color_set:nnn { red }   { rgb } { 1 , 0 , 0 }
\color_set:nnn { green } { rgb } { 0 , 1 , 0 }
\color_set:nnn { blue }  { rgb } { 0 , 0 , 1 }
% \end{macrocode}
%
% \begin{variable}{\l_@@_named_._tl}
%   A special named color: this is always defined though not fixed in
%   definition.
%    \begin{macrocode}
\tl_new:c { l_@@_named_._tl }
\tl_set_eq:cN { l_@@_named_._tl } \l_@@_current_tl
%    \end{macrocode}
% \end{variable}
%
% \subsection{Diagnostics}
%
% \begin{macro}{\color_show:n}
% \begin{macro}{\@@_show:w}
% \begin{macro}{\@@_show_gray:w, \@@_show_rgb:w, \@@_show_cmyk:w}
%   Extract the information about a color and format for the user: the approach
%   is similar to the keys module here.
%    \begin{macrocode}
\cs_new_protected:Npn \color_show:n #1
  {
    \msg_show:nnxxxx { LaTeX / color } { show }
      {#1}
      {
        \@@_if_defined:nTF {#1}
          { \exp_last_unbraced:Nv \@@_show:w { l_@@_named_ #1 _tl } \s_@@_stop }
          { }
      }
      { }
      { }
  }
\cs_new:Npn \@@_show:w #1 ~ #2 \s_@@_stop
  {
    \msg_show_item_unbraced:nn { model } {#1}
    \exp_args:Nnf \msg_show_item_unbraced:nn { value }
      { \use:c { @@_show_ #1 :w } #2 \s_@@_stop }
  }
\cs_new:Npn \@@_show_gray:w #1 \s_@@_stop { #1 }
\cs_new:Npn \@@_show_rgb:w #1 ~ #2 ~ #3 \s_@@_stop { #1 ,~ #2 ,~ #3 }
\cs_new:Npn \@@_show_cmyk:w #1 ~ #2 ~ #3 ~ #4 \s_@@_stop { #1 ,~ #2 ,~ #3 ,~ #4 }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \subsection{Messages}
%
% \begin{macrocode}
\__kernel_msg_new:nnnn { color } { unknown-color }
  { Unknown~color~'#1'. }
  {
    LaTeX~has~been~asked~to~use~a~color~named~'#1',~
    but~this~has~never~been~defined.
  }
\__kernel_msg_new:nnnn { color } { unknown-model }
  { Unknown~color~model~'#1'. }
  {
    LaTeX~has~been~asked~to~use~a~color~model~called~'#1',~
    but~this~model~is~not~set~up.
  }
% \end{macrocode}
%
% \begin{macrocode}
\__kernel_msg_new:nnn { color } { show }
  {
    The~color~#1~
    \tl_if_empty:nTF {#2}
      { is~undefined. }
      { has~the~properties: #2 }
  }
% \end{macrocode}
%
%    \begin{macrocode}
%</initex|package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex
