summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/l3experimental/l3color.dtx
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2020-07-18 03:01:08 +0000
committerNorbert Preining <norbert@preining.info>2020-07-18 03:01:08 +0000
commit63c4f21a5716ebbed11926d0bfbb26d656a766ea (patch)
treec02159e6ceb2453208664129ccda8ff306147328 /macros/latex/contrib/l3experimental/l3color.dtx
parent4ea983e3d411ade406c09b30ece9139457516b6f (diff)
CTAN sync 202007180301
Diffstat (limited to 'macros/latex/contrib/l3experimental/l3color.dtx')
-rw-r--r--macros/latex/contrib/l3experimental/l3color.dtx576
1 files changed, 461 insertions, 115 deletions
diff --git a/macros/latex/contrib/l3experimental/l3color.dtx b/macros/latex/contrib/l3experimental/l3color.dtx
index 8a87d390b4..91cb13e0b4 100644
--- a/macros/latex/contrib/l3experimental/l3color.dtx
+++ b/macros/latex/contrib/l3experimental/l3color.dtx
@@ -46,7 +46,7 @@
% }^^A
% }
%
-% \date{Released 2020-06-18}
+% \date{Released 2020-07-17}
%
% \maketitle
%
@@ -190,9 +190,9 @@
%
% \begin{function}{\color_set:nnn}
% \begin{syntax}
-% \cs{color_set:nnn} \Arg{name} \Arg{model} \Arg{value(s)}
+% \cs{color_set:nnn} \Arg{name} \Arg{model(s)} \Arg{value(s)}
% \end{syntax}
-% Stores the color specification equivalent to the \meta{model} and
+% Stores the color specification equivalent to the \meta{model(s)} and
% \meta{values} as the \meta{name}.
% \end{function}
%
@@ -225,9 +225,9 @@
%
% \begin{function}{\color_select:nn}
% \begin{syntax}
-% \cs{color_select:nn} \Arg{model} \Arg{value(s)}
+% \cs{color_select:nn} \Arg{model(s)} \Arg{value(s)}
% \end{syntax}
-% Activates the color specification equivalent to the \meta{model} and
+% Activates the color specification equivalent to the \meta{model(s)} and
% \meta{value(s)} for typeset material.
% \end{function}
%
@@ -237,23 +237,69 @@
% and similar are not influenced by this setting.
% \end{variable}
%
-% \section{Core color representation}
+% \section{Multiple color models}
%
-% To allow data to be handled internally, \pkg{l3color} uses a simple
-% representation of color, comprising two \meta{balanced text} entries, the first
-% the \meta{model} and the second the \meta{values} given
-% \emph{separated by spaces}.
-%
-% This core representation is produced when parsing color expressions.
+% When selecting or setting a color with an explicit model, it is possible
+% to give values for more than one model at one time. This is particularly
+% useful where automated conversion between models does not give the desired
+% outcome. To do this, the list of models and list of values are both subdivided
+% using |/| characters (as for the similar function in \pkg{xcolor}). For
+% example, to save a color with explicit |cmyk| and |rgb| values, one could
+% use
+% \begin{verbatim}
+% \color_set:nnn { foo } { cmyk / rgb }
+% { 0.1 , 0.2 , 0.3 , 0.4 / 0.1, 0.2 , 0.3 }
+% \end{verbatim}
+% The manually-specified conversion will be used in preference to automated
+% calculation whenever the model(s) listed are used: both in expressions and
+% when a fixed model is active.
%
-% \begin{function}{\color_parse:nN}
+% Similarly, the same syntax can be applied to directly selecting a color.
+% \begin{verbatim}
+% \color_select:nn { cmyk / rgb }
+% { 0.1 , 0.2 , 0.3 , 0.4 / 0.1, 0.2 , 0.3 }
+% \end{verbatim}
+% Again, this list is used when a fixed model is active: the first entry is used
+% unless there is a fixed model matching one of the other entries.
+%
+% \section{Exporting color specifications}
+%
+% The major use of color expressions is in setting typesetting output, but there
+% are other places in which some form of color information is required. These
+% may need data in a different format or using a different model to the internal
+% representation. Thus a set of functions are available to export colors in
+% different formats.
+%
+% Valid export targets are
+% \begin{itemize}
+% \item \texttt{backend} Two brace groups: the first containing the
+% model, the second containing space-separated values appropriate
+% for the model; this is the format required by backend functions
+% of \pkg{expl3}
+% \item \texttt{HTML} Uppercase two-digit hexadecimal values, expressing
+% a red-green-blue color; the digits are \emph{not} separated
+% \item \texttt{space-sep-cmyk} Space-separated cyan-magenta-yellow-black
+% values
+% \item \texttt{space-sep-rgb} Space-separated red-green-blue values
+% suitable for use as a PDF annotation color
+% \end{itemize}
+%
+% \begin{function}{\color_export:nnN}
% \begin{syntax}
-% \cs{color_parse:nN} \Arg{color expression} \Arg{tl}
+% \cs{color_export:nnN} \Arg{color expression} \Arg{format} \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).
+% Parses the \meta{color expression} as described earlier,
+% then converts to the \meta{format} specified and assigns the data to the
+% \meta{tl}.
+% \end{function}
+%
+% \begin{function}{\color_export:nnN}
+% \begin{syntax}
+% \cs{color_export:nnnN} \Arg{model} \Arg{value(s)} \Arg{format} \Arg{tl}
+% \end{syntax}
+% Expresses the combination of \meta{model} and \meta{value(s)} in an
+% internal representation, then converts to the \meta{format} specified and
+% assigns the data to the \meta{tl}.
% \end{function}
%
% \section{Spot colors}
@@ -275,7 +321,7 @@
%
% \begin{macrocode}
%<*package>
-\ProvidesExplPackage{l3color}{2020-06-18}{}
+\ProvidesExplPackage{l3color}{2020-07-17}{}
{L3 Experimental color support}
%</package>
% \end{macrocode}
@@ -297,8 +343,8 @@
% 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.
+% in a two-part structure: a \texttt{prop} for the colors themselves, and a
+% \texttt{tl} for the default model for each color.
%
% \subsection{Setup}
%
@@ -323,42 +369,37 @@
% \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_defined:n #1 { T, F, TF }
{
- \tl_if_exist:cTF { l_@@_named_ #1 _tl }
+ \prop_if_exist:cTF { l_@@_named_ #1 _prop }
\prg_return_true:
\prg_return_false:
}
% \end{macrocode}
% \end{macro}
%
+% \begin{macro}[EXP]{\@@_model:N, \@@_values:N}
+% Simple abstractions.
+% \begin{macrocode}
+\cs_new:Npn \@@_model:N #1 { \exp_after:wN \use_i:nn #1 }
+\cs_new:Npn \@@_values:N #1 { \exp_after:wN \use_ii:nn #1 }
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macro}{\@@_extract:nNN, \@@_extract:VNN}
-% \begin{macro}{\@@_extract:NNnn}
-% 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.
+% Recover the values for the standard model for a color.
% \begin{macrocode}
\cs_new_protected:Npn \@@_extract:nNN #1#2#3
{
- \exp_after:wN \exp_after:wN \exp_after:wN
- \@@_extract:NNnn
- \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:
+ \tl_set_eq:Nc #2 { l_@@_named_ #1 _tl }
+ \prop_get:cVN { l_@@_named_ #1 _prop } #2 #3
}
\cs_generate_variant:Nn \@@_extract:nNN { V }
-\cs_new_protected:Npn \@@_extract:NNnn #1#2#3#4
- {
- \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}{\@@_convert:nnnN, \@@_convert:nVnN, \@@_convert:nnVN}
% \begin{macro}[aux, EXP]
% {
% \@@_convert_gray_rgb:w
@@ -383,7 +424,7 @@
\tl_set:Nx #4
{ \use:c { @@_convert_ #1 _ #2 :w } #3 ~ 0 ~ 0 ~ 0 \s_@@_stop }
}
-\cs_generate_variant:Nn \@@_convert:nnnN { nnV }
+\cs_generate_variant:Nn \@@_convert:nnnN { nV , 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
@@ -442,12 +483,15 @@
% \end{macrocode}
% \end{variable}
%
-% \begin{macro}{\color_parse:nN}
% \begin{macro}{\@@_parse:nN}
+% \begin{macro}{\@@_parse_aux:nN}
+% \begin{macro}{\@@_parse_eq:Nn}
+% \begin{macro}{\@@_parse_eq:nNn}
% \begin{macro}{\@@_parse:Nw}
% \begin{macro}{\@@_parse_loop_init:Nnn}
% \begin{macro}{\@@_parse_loop:w}
% \begin{macro}{\@@_parse_loop:nn}
+% \begin{macro}{\@@_parse_gray:n, \@@_parse_std:n}
% \begin{macro}{\@@_parse_break:w}
% \begin{macro}{\@@_parse_end:}
% \begin{macro}[aux, EXP]{\@@_parse_mix:Nnnn, \@@_parse_mix:NVVn}
@@ -462,37 +506,73 @@
% 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
+\cs_new_protected:Npx \@@_parse:nN #1#2
{
%<*package>
- \@@_backend_pickup:N \l_@@_current_tl
- \tl_set_eq:cN { l_@@_named_ . _tl } \l_@@_current_tl
+ \exp_not:N \@@_backend_pickup:N \exp_not:N \l_@@_current_tl
+ \tl_set:Nx \exp_not:c { l_@@_named_ . _tl }
+ { \exp_not:N \@@_model:N \exp_not:N \l_@@_current_tl }
+ \prop_put:NVx \exp_not:c { l_@@_named_ . _prop }
+ \exp_not:c { l_@@_named_ . _tl }
+ { \exp_not:N \@@_values:N \exp_not:N \l_@@_current_tl }
%</package>
- \exp_args:Ne \@@_parse:nN { \tl_to_str:n {#1} } #2
+ \exp_not:N \exp_args:Ne \exp_not:N \@@_parse_aux:nN
+ { \exp_not:N \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
+\cs_new_protected:Npn \@@_parse_aux:nN #1#2
{
- \tl_if_exist:cTF { l_@@_named_ #1 _tl }
- { \tl_set_eq:Nc #2 { l_@@_named_ #1 _tl } }
+ \tl_if_exist:cTF { l_@@_named_ #1 _prop }
+ { \@@_parse_set_eq:Nn #2 {#1} }
{ \@@_parse:Nw #2#1 ! \s_@@_stop }
\@@_check_model:N #2
}
+\cs_new_protected:Npn \@@_parse_set_eq:Nn #1#2
+ {
+ \tl_if_empty:NTF \l_color_fixed_model_tl
+ { \exp_args:Nv \@@_parse_set_eq:nNn { l_@@_named_ #2 _tl } }
+ { \exp_args:NV \@@_parse_set_eq:nNn \l_color_fixed_model_tl }
+ #1 {#2}
+ }
+% \end{macrocode}
+% Here, we have to allow for the case where there is a fixed model:
+% that can't be swept up by generic conversion as we are dealing with a
+% named color.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_parse_set_eq:nNn #1#2#3
+ {
+ \prop_get:cnNTF
+ { l_@@_named_ #3 _prop } {#1}
+ \l_@@_value_tl
+ { \tl_set:Nx #2 { {#1} { \l_@@_value_tl } } }
+ {
+ \tl_set_eq:Nc \l_@@_model_tl { l_@@_named_ #3 _tl }
+ \prop_get:cVN { l_@@_named_ #3 _prop } \l_@@_model_tl
+ \l_@@_value_tl
+ \@@_convert:nnN
+ \l_@@_model_tl {#1} \l_@@_value_tl
+ \tl_set:Nx #2
+ {
+ {#1}
+ { \l_@@_value_tl }
+ }
+ }
+ }
\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_set_eq:Nn #1 {#2} }
{ \@@_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
+ \tl_set:Nn \l_@@_current_tl { { gray } { 0 } }
}
}
% \end{macrocode}
@@ -562,24 +642,9 @@
\@@_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
+ \str_if_eq:VnTF \l_@@_model_tl { gray }
+ { \@@_parse_gray:n {#2} }
+ { \@@_parse_std:n {#2} }
}
\tl_set:Nx \l_@@_value_tl
{
@@ -593,6 +658,31 @@
\@@_parse_break:w
}
}
+% \end{macrocode}
+% The \texttt{gray} model needs special handling: the models need to be
+% swapped: we do that using a dedicated function.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_parse_gray:n #1
+ {
+ \tl_set_eq:NN \l_@@_model_tl \l_@@_next_model_tl
+ \tl_set:Nn \l_@@_next_model_tl { gray }
+ \exp_args:NnV \@@_convert:nnN { gray } \l_@@_model_tl
+ \l_@@_value_tl
+ \prop_get:cVN { l_@@_named_ #1 _prop } \l_@@_model_tl
+ \l_@@_next_value_tl
+ }
+\cs_new_protected:Npn \@@_parse_std:n #1
+ {
+ \prop_get:cVNF { l_@@_named_ #1 _prop }
+ \l_@@_model_tl
+ \l_@@_next_value_tl
+ {
+ \@@_convert:VVN
+ \l_@@_next_model_tl
+ \l_@@_model_tl
+ \l_@@_next_value_tl
+ }
+ }
\cs_new_protected:Npn \@@_parse_break:w #1 \@@_parse_end: { }
\cs_new_protected:Npn \@@_parse_end: { }
% \end{macrocode}
@@ -640,6 +730,9 @@
% \end{macro}
% \end{macro}
% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
%
% \begin{macro}[EXP]
% {
@@ -814,52 +907,91 @@
% 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:
+\cs_new_protected:Npx \@@_select:
{
- \tl_set_eq:cN { l_@@_named_ . _tl } \l_@@_current_tl
- \@@_select:N \l_@@_current_tl
+ \tl_set:Nx \exp_not:c { l_@@_named_ . _tl }
+ { \exp_not:N \@@_model:N \exp_not:N \l_@@_current_tl }
+ \prop_clear:N \exp_not:c { l_@@_named_ . _prop }
+ \prop_put:NVx \exp_not:c { l_@@_named_ . _prop }
+ \exp_not:c { l_@@_named_ . _tl }
+ { \exp_not:N \@@_values:N \exp_not:N \l_@@_current_tl }
+ \@@_select:N \exp_not:N \l_@@_current_tl
}
% \end{macrocode}
% \end{macro}
%
% \begin{macro}{\color_select:n}
% \begin{macro}{\color_select:nn}
+% \begin{macro}{\@@_select_main:Nw, \@@_select_loop:Nw}
+% \begin{macro}{\@@_select:nnN}
+% \begin{macro}{\@@_select_swap:Nnn}
% Parse the input expressions then get the backend to actually activate
-% them.
+% them. The main complexity here is the need to check through multiple models.
+% That is done \enquote{locally} here as the approach is subtly different to
+% when different models are being stored.
% \begin{macrocode}
\cs_new_protected:Npn \color_select:n #1
{
- \color_parse:nN {#1} \l_@@_current_tl
+ \@@_parse:nN {#1} \l_@@_current_tl
\@@_select:
}
\cs_new_protected:Npn \color_select:nn #1#2
{
- \@@_direct:nnN {#1} {#2} \l_@@_current_tl
+ \@@_select_main:Nw \l_@@_current_tl
+ #1 / / \s_@@_mark #2 / / \s_@@_stop
\@@_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.
+% If the first color model is the fixed one, or if there is no fixed
+% model, we don't need most of the data: just set up and apply the backend
+% function.
% \begin{macrocode}
-\cs_new_protected:Npn \@@_direct:nnN #1#2#3
+\cs_new_protected:Npn \@@_select_main:Nw
+ #1 #2 / #3 / #4 \s_@@_mark #5 / #6 / #7 \s_@@_stop
+ {
+ \@@_select:nnN {#2} {#5} #1
+ \bool_lazy_or:nnF
+ { \tl_if_empty_p:N \l_color_fixed_model_tl }
+ { \str_if_eq_p:nV {#2} \l_color_fixed_model_tl }
+ { \@@_select_loop:Nw #1 #3 / #4 \s_@@_mark #6 / #7 \s_@@_stop }
+ }
+% \end{macrocode}
+% If a fixed model applies, we need to check each possible value in order.
+% If there is no hit at all, fall back on the generic formula-based
+% interchange.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_select_loop:Nw
+ #1 #2 / #3 \s_@@_mark #4 / #5 \s_@@_stop
+ {
+ \str_if_eq:nVTF {#2} \l_color_fixed_model_tl
+ { \@@_select:nnN {#2} {#4} #1 }
+ {
+ \tl_if_blank:nTF {#2}
+ { \exp_after:wN \@@_select_swap:Nnn \exp_after:wN #1 #1 }
+ { \@@_select_loop:Nw #1 #3 \s_@@_mark #5 \s_@@_stop }
+ }
+ }
+\cs_new_protected:Npn \@@_select: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}
}
+ { \__kernel_msg_error:nnn { color } { unknown-model } {#1} }
+ }
+\cs_new_protected:Npn \@@_select_swap:Nnn #1#2#3
+ {
+ \@@_convert:nVnN {#2} \l_color_fixed_model_tl {#3} \l_@@_value_tl
+ \tl_set:Nx #1
+ { { \l_color_fixed_model_tl } { \l_@@_value_tl } }
}
% \end{macrocode}
% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
%
% \subsection{Defining named colors}
%
@@ -870,44 +1002,113 @@
% \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
+% \begin{macro}{\color_set:nn}
+% \begin{macro}{\@@_set:nnn}
+% \begin{macro}{\@@_set:nn}
+% \begin{macro}{\@@_set:nnw}
+% \begin{macro}{\color_set:nnn}
+% \begin{macro}{\@@_set_loop:nw}
+% \begin{macro}{\color_set_eq:nn}
+% Defining named colors means working through the model list and saving
+% both the \enquote{main} color and any equivalents in other models. Even
+% if there is only one model, we store a |prop| as well as a |tl|, as there
+% could be grouping weirdness, etc. When setting using an expression,
+% we need to avoid any fixed model issues, which is done without a group as
+% in \pkg{l3keys}.
+% \begin{macrocode}
+\cs_new_protected:Npn \color_set:nn #1#2
+ {
+ \exp_args:NV \@@_set:nnn
+ \l_color_fixed_model_tl {#1} {#2}
+ }
+\cs_new_protected:Npn \@@_set:nnn #1#2#3
+ {
+ \tl_clear:N \l_color_fixed_model_tl
+ \@@_set:nn {#2} {#3}
+ \tl_set:Nn \l_color_fixed_model_tl {#1}
+ }
+\cs_new_protected:Npn \@@_set:nn #1#2
{
- \str_if_eq:nnF {#2} { . }
+ \str_if_eq:nnF {#1} { . }
{
- \tl_clear_new:c { l_@@_named_ #2 _tl }
- \tl_set_eq:cN { l_@@_named_ #2 _tl } #1
+ \@@_parse:nN {#2} \l_@@_named_tl
+ \tl_clear_new:c { l_@@_named_ #1 _tl }
+ \tl_set:cx { l_@@_named_ #1 _tl }
+ { \@@_model:N \l_@@_named_tl }
+ \prop_clear_new:c { l_@@_named_ #1 _prop }
+ \prop_put:cvx { l_@@_named_ #1 _prop } { l_@@_named_ #1 _tl }
+ { \@@_values:N \l_@@_named_tl }
+ \@@_set:nnw {#1} {#2} #2 ! \s_@@_stop
}
}
% \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.
+% When setting an expression-based color, there could be multiple model
+% data available for one or more of the input colors. Where that is true for
+% the \emph{first} named color in an expression, we re-parse the expression
+% when they are also parameter-based: only |cmyk|, |gray| and |rgb| make
+% any sense here. There is a bit of a performance hit but this should be
+% rare and taking place during set-up.
% \begin{macrocode}
-\cs_new_protected:Npn \color_set:nn #1#2
+\cs_new_protected:Npn \@@_set:nnw #1#2#3 ! #4 \s_@@_stop
{
- \color_parse:nN {#2} \l_@@_named_tl
- \@@_store:Nn \l_@@_named_tl {#1}
+ \clist_map_inline:nn { cmyk , gray , rgb }
+ {
+ \prop_get:cnNT { l_@@_named_ #3 _prop } {##1} \l_@@_internal_tl
+ {
+ \prop_if_in:cnF { l_@@_named_ #1 _prop } {##1}
+ {
+ \group_begin:
+ \tl_set:cn { l_@@_named_ #3 _tl } {##1}
+ \@@_parse:nN {#2} \l_@@_internal_tl
+ \exp_args:NNNV \group_end:
+ \tl_set:Nn \l_@@_internal_tl \l_@@_internal_tl
+ \prop_put:cxx { l_@@_named_ #1 _prop }
+ { \@@_model:N \l_@@_internal_tl }
+ { \@@_values:N \l_@@_internal_tl }
+ }
+ }
+ }
}
\cs_new_protected:Npn \color_set:nnn #1#2#3
{
- \@@_direct:nnN {#2} {#3} \l_@@_named_tl
- \@@_store:Nn \l_@@_named_tl {#1}
+ \str_if_eq:nnF {#1} { . }
+ {
+ \tl_clear_new:c { l_@@_named_ #1 _tl }
+ \prop_clear_new:c { l_@@_named_ #1 _prop }
+ \@@_set_loop:nw {#1} #2 / / \s_@@_mark #3 / / \s_@@_stop
+ }
+ }
+\cs_new_protected:Npn \@@_set_loop:nw
+ #1#2 / #3 \s_@@_mark #4 / #5 \s_@@_stop
+ {
+ \tl_if_blank:nF {#2}
+ {
+ \@@_select:nnN {#2} {#4} \l_@@_named_tl
+ \tl_set:Nx \l_@@_internal_tl { \@@_model:N \l_@@_named_tl }
+ \tl_if_empty:cT { l_@@_named_ #1 _tl }
+ { \tl_set_eq:cN { l_@@_named_ #1 _tl } \l_@@_internal_tl }
+ \prop_put:cVx { l_@@_named_ #1 _prop } \l_@@_internal_tl
+ { \@@_values:N \l_@@_named_tl }
+ \@@_set_loop:nw {#1} #3 \s_@@_mark #5 \s_@@_stop
+ }
}
\cs_new_protected:Npn \color_set_eq:nn #1#2
{
\@@_if_defined:nTF {#2}
{
\tl_clear_new:c { l_@@_named_ #1 _tl }
+ \prop_clear_new:c { l_@@_named_ #1 _prop }
\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 } }
+ {
+ \tl_set:cx { l_@@_named_ #1 _tl }
+ { \@@_model:N \l_@@_current_tl }
+ \prop_put:cvx { l_@@_named_ #1 _prop } { l_@@_named_ #1 _tl }
+ { \@@_values:N \l_@@_current_tl }
+ }
+ {
+ \tl_set_eq:cc { l_@@_named_ #1 _tl } { l_@@_named_ #2 _tl }
+ \prop_set_eq:cc { l_@@_named_ #1 _prop } { l_@@_named_ #2 _prop }
+ }
}
{
\__kernel_msg_error:nnn { color } { unknown-color } {#2}
@@ -920,6 +1121,7 @@
% \end{macro}
% \end{macro}
% \end{macro}
+% \end{macro}
%
% A small set of colors are always defined.
% \begin{macrocode}
@@ -933,19 +1135,154 @@
\color_set:nnn { blue } { rgb } { 0 , 0 , 1 }
% \end{macrocode}
%
-% \begin{variable}{\l_@@_named_._tl}
+% \begin{variable}{\l_@@_named_._prop, \l_@@_named_._tl}
% A special named color: this is always defined though not fixed in
% definition.
% \begin{macrocode}
+\prop_new:c { l_@@_named_._prop }
\tl_new:c { l_@@_named_._tl }
-\tl_set_eq:cN { l_@@_named_._tl } \l_@@_current_tl
+\tl_set:cx { l_@@_named_._tl } { \@@_model:N \l_@@_current_tl }
+% \end{macrocode}
+% \end{variable}
+%
+% \subsection{Exporting colors}
+%
+% \begin{macro}{\color_export:nnN}
+% \begin{macro}{\color_export:nnnN}
+% \begin{macro}{\@@_export:nN}
+% \begin{macro}{\@@_export:nnnN}
+% \begin{macrocode}
+\cs_new_protected:Npn \color_export:nnN #1#2#3
+ {
+ \group_begin:
+ \tl_if_exist:cT { c_@@_export_ #2 _tl }
+ { \tl_set_eq:Nc \l_color_fixed_model_tl { c_@@_export_ #2 _tl } }
+ \@@_parse:nN {#1} #3
+ \@@_export:nN {#2} #3
+ \exp_args:NNNV \group_end:
+ \tl_set:Nn #3 #3
+ }
+\cs_new_protected:Npn \color_export:nnnN #1#2#3#4
+ {
+ \@@_select_main:Nw #4
+ #1 / / \s_@@_mark #2 / / \s_@@_stop
+ \@@_export:nN {#3} #4
+ }
+\cs_new_protected:Npn \@@_export:nN #1#2
+ { \exp_after:wN \@@_export:nnnN #2 {#1} #2 }
+\cs_new:Npn \@@_export:nnnN #1#2#3#4
+ {
+ \cs_if_exist_use:cF { @@_export_format_ #3 :nnN }
+ {
+ \__kernel_msg_error:nnn { color } { unknown-export-format } {#3}
+ \use_none:nnn
+ }
+ {#1} {#2} #4
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_export_format_backend:nnN}
+% Simple.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_export_format_backend:nnN #1#2#3
+ { \tl_set:Nn #3 { {#1} {#2} } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_export:nnnNN}
+% A generic auxiliary for cases where only one model is appropriate.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_export:nnnNN #1#2#3#4#5
+ {
+ \str_if_eq:nnTF {#2} {#1}
+ { #5 #4 #3 \s_@@_stop }
+ {
+ \@@_convert:nnnN {#2} {#1} {#3} #4
+ \exp_after:wN #5 \exp_after:wN #4
+ #4 \s_@@_stop
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{variable}
+% {
+% \c_@@_export_HTML_tl ,
+% \c_@@_export_space-sep-cmyk_tl ,
+% \c_@@_export_space-sep-rgb_tl
+% }
+% \begin{macrocode}
+\tl_const:Nn \c_@@_export_HTML_tl { rgb }
+\tl_const:cn { c_@@_export_space-sep-cmyk_tl } { cmyk }
+\tl_const:cn { c_@@_export_space-sep-rgb_tl } { rgb }
% \end{macrocode}
% \end{variable}
%
+% \begin{macro}{\@@_export_format_space-sep-cmyk:nnN}
+% \begin{macro}{\@@_export_space-sep-cmyk:Nw}
+% \begin{macrocode}
+\cs_new_protected:cpx { @@_export_format_space-sep-cmyk:nnN } #1#2#3
+ {
+ \exp_not:N \@@_export:nnnNN { cmyk } {#1} {#2} #3
+ \exp_not:c { @@_export_space-sep-cmyk:Nw }
+ }
+\cs_new_protected:cpn { @@_export_space-sep-cmyk:Nw } #1#2 \s_@@_stop
+ { \tl_set:Nx #1 {#2} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \@@_export_format_HTML:nnN ,
+% \@@_export_format_space-sep-rgb:nnN
+% }
+% \begin{macro}
+% {
+% \@@_export_HTML:Nw ,
+% \@@_export_space-sep-rgb:Nw
+% }
+% \begin{macro}[EXP]{\@@_export_HTML:n}
+% \textsc{html} values must be given in |rgb|: we force conversion if
+% required, then do some simple maths.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_export_format_HTML:nnN #1#2#3
+ { \@@_export:nnnNN { rgb } {#1} {#2}#3 \@@_export_HTML:Nw }
+\cs_new_protected:cpx { @@_export_format_space-sep-rgb:nnN } #1#2#3
+ {
+ \exp_not:N \@@_export:nnnNN { rgb } {#1} {#2} #3
+ \exp_not:c { @@_export_space-sep-rgb:Nw }
+ }
+\cs_new_protected:Npn \@@_export_HTML:Nw #1#2 ~ #3 ~ #4 \s_@@_stop
+ {
+ \tl_set:Nx #1
+ {
+ \@@_export_HTML:n {#2}
+ \@@_export_HTML:n {#3}
+ \@@_export_HTML:n {#4}
+ }
+ }
+\cs_new:Npn \@@_export_HTML:n #1
+ {
+ \fp_compare:nNnTF {#1} = { 0 }
+ { 00 }
+ { \int_to_Hex:n { \fp_to_int:n { #1 * 255 } } }
+ }
+\cs_new_protected:cpn { @@_export_space-sep-rgb:Nw } #1#2 \s_@@_stop
+ { \tl_set:Nx #1 {#2} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
% \subsection{Diagnostics}
%
% \begin{macro}{\color_show:n}
-% \begin{macro}{\@@_show:nn}
+% \begin{macro}{\@@_show:n}
% Extract the information about a color and format for the user: the approach
% is similar to the keys module here.
% \begin{macrocode}
@@ -954,17 +1291,20 @@
\msg_show:nnxxxx { LaTeX / color } { show }
{#1}
{
- \@@_if_defined:nTF {#1}
- { \exp_last_unbraced:Nv \@@_show:nn { l_@@_named_ #1 _tl } }
- { }
+ \@@_if_defined:nT {#1}
+ {
+ \exp_args:Nv \@@_show:n { l_@@_named_ #1 _tl }
+ \prop_map_function:cN
+ { l_@@_named_ #1 _prop }
+ \msg_show_item_unbraced:nn
+ }
}
{ }
{ }
}
-\cs_new:Npn \@@_show:nn #1#2
+\cs_new:Npn \@@_show:n #1
{
\msg_show_item_unbraced:nn { model } {#1}
- \msg_show_item_unbraced:nn { value } {#2}
}
% \end{macrocode}
% \end{macro}
@@ -979,6 +1319,12 @@
LaTeX~has~been~asked~to~use~a~color~named~'#1',~
but~this~has~never~been~defined.
}
+\__kernel_msg_new:nnnn { color } { unknown-export-format }
+ { Unknown~export~format~'#1'. }
+ {
+ LaTeX~has~been~asked~to~export~a~color~in~format~'#1',~
+ but~this~has~never~been~defined.
+ }
\__kernel_msg_new:nnnn { color } { unknown-model }
{ Unknown~color~model~'#1'. }
{