[latex3-commits] [git/LaTeX3-latex3-latex3] color-multi-model: Add support for multiple color models (see #739) (cf071a5b5)
Joseph Wright
joseph.wright at morningstar2.co.uk
Tue Jun 9 13:25:30 CEST 2020
Repository : https://github.com/latex3/latex3
On branch : color-multi-model
Link : https://github.com/latex3/latex3/commit/cf071a5b50e15349aa671e7788d825b289fe8a26
>---------------------------------------------------------------
commit cf071a5b50e15349aa671e7788d825b289fe8a26
Author: Joseph Wright <joseph.wright at morningstar2.co.uk>
Date: Tue Jun 9 11:43:35 2020 +0100
Add support for multiple color models (see #739)
>---------------------------------------------------------------
cf071a5b50e15349aa671e7788d825b289fe8a26
l3experimental/CHANGELOG.md | 3 +
l3experimental/l3color/l3color.dtx | 154 +++++++++++++++++----
.../l3color/testfiles/m3color001.ptex.tlg | 3 +
l3experimental/l3color/testfiles/m3color001.tlg | 3 +
.../l3color/testfiles/m3color001.uptex.tlg | 3 +
.../l3color/testfiles/m3color001.xetex.tlg | 3 +
l3experimental/l3color/testfiles/m3color002.tlg | 2 +
l3experimental/l3color/testfiles/m3color003.lvt | 67 +++++++++
l3experimental/l3color/testfiles/m3color003.tlg | 72 ++++++++++
9 files changed, 282 insertions(+), 28 deletions(-)
diff --git a/l3experimental/CHANGELOG.md b/l3experimental/CHANGELOG.md
index 8cf1b5f05..b05c5cc4a 100644
--- a/l3experimental/CHANGELOG.md
+++ b/l3experimental/CHANGELOG.md
@@ -7,6 +7,9 @@ this project uses date-based 'snapshot' version identifiers.
## [Unreleased]
+### Added
+- Support for multiple color models
+
## [2020-06-03]
### Added
diff --git a/l3experimental/l3color/l3color.dtx b/l3experimental/l3color/l3color.dtx
index c7addda60..f83833d29 100644
--- a/l3experimental/l3color/l3color.dtx
+++ b/l3experimental/l3color/l3color.dtx
@@ -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,6 +237,31 @@
% and similar are not influenced by this setting.
% \end{variable}
%
+% \section{Multiple color models}
+%
+% 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.
+%
+% 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{Core color representation}
%
% To allow data to be handled internally, \pkg{l3color} uses a simple
@@ -368,7 +393,7 @@
%
% \subsection{Model conversion}
%
-% \begin{macro}{\@@_convert:nnN, \@@_convert:VVN}
+% \begin{macro}{\@@_convert:nnN, \@@_convert:nVN, \@@_convert:VVN}
% \begin{macro}{\@@_convert:nnnN}
% \begin{macro}[aux, EXP]
% {
@@ -386,7 +411,7 @@
% \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_generate_variant:Nn \@@_convert:nnN { nV , VV }
\cs_new_protected:Npn \@@_convert:nnnN #1#2#3#4
{
\str_if_eq:nnT {#1} { spot } % TO DO!!!
@@ -587,10 +612,15 @@
{ \l_@@_value_tl }
}
}
- \@@_convert:VVN
- \l_@@_next_model_tl
+ \prop_get:cVNF { l_@@_named_ #2 _prop }
\l_@@_model_tl
\l_@@_next_value_tl
+ {
+ \@@_convert:VVN
+ \l_@@_next_model_tl
+ \l_@@_model_tl
+ \l_@@_next_value_tl
+ }
}
\tl_set:Nx \l_@@_value_tl
{
@@ -825,8 +855,13 @@
%
% \begin{macro}{\color_select:n}
% \begin{macro}{\color_select:nn}
+% \begin{macro}{\@@_select_main:w, \@@_select_loop:w}
+% \begin{macro}{\@@_select:nnN}
+% \begin{macro}{\@@_select_swap:w}
% 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
{
@@ -835,32 +870,63 @@
}
\cs_new_protected:Npn \color_select:nn #1#2
{
- \@@_direct:nnN {#1} {#2} \l_@@_current_tl
+ \@@_select_main:w
+ #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:w
+ #1 / #2 / #3 \s_@@_mark #4 / #5 / #6 \s_@@_stop
{
- \cs_if_exist:cTF { @@_parse_model_ #1 :w }
+ \@@_select:nnN {#1} {#4} \l_@@_current_tl
+ \bool_lazy_or:nnF
+ { \tl_if_empty_p:N \l_color_fixed_model_tl }
+ { \str_if_eq_p:nV {#1} \l_color_fixed_model_tl }
+ { \@@_select_loop:w #2 / #3 \s_@@_mark #5 / #6 \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:w #1 / #2 \s_@@_mark #3 / #4 \s_@@_stop
+ {
+ \str_if_eq:nVTF {#1} \l_color_fixed_model_tl
+ { \@@_select:nnN {#1} {#3} \l_@@_current_tl }
{
- \tl_set:Nx #3
- { \use:c { @@_parse_model_ #1 :w } #2 , 0 , 0 , 0 , 0 \s_@@_stop }
- \@@_check_model:N #3
+ \tl_if_blank:nTF {#1}
+ {
+ \exp_after:wN \@@_select_swap:w
+ \l_@@_current_tl \s_@@_stop
+ }
+ { \@@_select_loop:w #2 \s_@@_mark #4 \s_@@_stop }
}
+ }
+\cs_new_protected:Npn \@@_select:nnN #1#2#3
+ {
+ \cs_if_exist:cTF { @@_parse_model_ #1 :w }
{
- \__kernel_msg_error:nnn { color } { unknown-model } {#1}
+ \tl_set:Nx #3
+ { \use:c { @@_parse_model_ #1 :w } #2 , 0 , 0 , 0 , 0 \s_@@_stop }
}
+ { \__kernel_msg_error:nnn { color } { unknown-model } {#1} }
+ }
+\cs_new_protected:Npn \@@_select_swap:w #1 ~ #2 \s_@@_stop
+ {
+ \@@_convert:nVN {#1} \l_color_fixed_model_tl \l_@@_value_tl
+ \tl_set:Nx #1
+ { \l_color_fixed_model_tl \c_space_tl \l_@@_value_tl }
}
% \end{macrocode}
% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
%
% \subsection{Defining named colors}
%
@@ -887,9 +953,14 @@
%
% \begin{macro}{\color_set:nn}
% \begin{macro}{\color_set:nnn}
+% \begin{macro}{\@@_set:nw}
+% \begin{macro}{\@@_set_loop:nw}
+% \begin{macro}[EXP]{\@@_gobble_model:w}
% \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.
+% 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.
% \begin{macrocode}
\cs_new_protected:Npn \color_set:nn #1#2
{
@@ -897,10 +968,29 @@
\@@_store:Nn \l_@@_named_tl {#1}
}
\cs_new_protected:Npn \color_set:nnn #1#2#3
+ { \@@_set:nw {#1} #2 / / \s_@@_mark #3 / / \s_@@_stop }
+\cs_new_protected:Npn \@@_set:nw
+ #1#2 / #3 / #4 \s_@@_mark #5 / #6 / #7 \s_@@_stop
{
- \@@_direct:nnN {#2} {#3} \l_@@_named_tl
+ \@@_select:nnN {#2} {#5} \l_@@_named_tl
\@@_store:Nn \l_@@_named_tl {#1}
+ \prop_clear_new:c { l_@@_named_ #1 _prop }
+ \prop_put:cnx { l_@@_named_ #1 _prop } {#2}
+ { \exp_after:wN \@@_gobble_model:w \l_@@_named_tl }
+ \@@_set_loop:nw {#1} #3 / #4 \s_@@_mark #6 / #7 \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
+ \prop_put:cnx { l_@@_named_ #1 _prop } {#2}
+ { \exp_after:wN \@@_gobble_model:w \l_@@_named_tl }
+ \@@_set_loop:nw {#1} #3 \s_@@_mark #5 \s_@@_stop
+ }
}
+\cs_new:Npn \@@_gobble_model:w #1 ~ { }
\cs_new_protected:Npn \color_set_eq:nn #1#2
{
\@@_if_defined:nTF {#2}
@@ -956,9 +1046,17 @@
\msg_show:nnxxxx { LaTeX / color } { show }
{#1}
{
- \@@_if_defined:nTF {#1}
+ \@@_if_defined:nT {#1}
{ \exp_last_unbraced:Nv \@@_show:w { l_@@_named_ #1 _tl } \s_@@_stop }
- { }
+ \@@_if_defined:nT {#1}
+ {
+ \int_compare:nNnT { \prop_count:c { l_@@_named_ #1 _prop } } > 1
+ {
+ \prop_map_function:cN
+ { l_@@_named_ #1 _prop }
+ \msg_show_item_unbraced:nn
+ }
+ }
}
{ }
{ }
diff --git a/l3experimental/l3color/testfiles/m3color001.ptex.tlg b/l3experimental/l3color/testfiles/m3color001.ptex.tlg
index 7b08c6f0b..e22f5abcf 100644
--- a/l3experimental/l3color/testfiles/m3color001.ptex.tlg
+++ b/l3experimental/l3color/testfiles/m3color001.ptex.tlg
@@ -5,18 +5,21 @@ Author: Joseph Wright
TEST 1: Naming colors by model
============================================================
Defining \l__color_named_foo1_tl on line ...
+Defining \l__color_named_foo1_prop on line ...
The color foo1 has the properties:
> model => gray
> value => 0.1.
<recently read> }
l. ... }
Defining \l__color_named_foo2_tl on line ...
+Defining \l__color_named_foo2_prop on line ...
The color foo2 has the properties:
> model => rgb
> value => 0.1, 0.2, 0.3.
<recently read> }
l. ... }
Defining \l__color_named_foo3_tl on line ...
+Defining \l__color_named_foo3_prop on line ...
The color foo3 has the properties:
> model => cmyk
> value => 0.1, 0.2, 0.3, 0.4.
diff --git a/l3experimental/l3color/testfiles/m3color001.tlg b/l3experimental/l3color/testfiles/m3color001.tlg
index f92931472..a8d072628 100644
--- a/l3experimental/l3color/testfiles/m3color001.tlg
+++ b/l3experimental/l3color/testfiles/m3color001.tlg
@@ -5,18 +5,21 @@ Author: Joseph Wright
TEST 1: Naming colors by model
============================================================
Defining \l__color_named_foo1_tl on line ...
+Defining \l__color_named_foo1_prop on line ...
The color foo1 has the properties:
> model => gray
> value => 0.1.
<recently read> }
l. ... }
Defining \l__color_named_foo2_tl on line ...
+Defining \l__color_named_foo2_prop on line ...
The color foo2 has the properties:
> model => rgb
> value => 0.1, 0.2, 0.3.
<recently read> }
l. ... }
Defining \l__color_named_foo3_tl on line ...
+Defining \l__color_named_foo3_prop on line ...
The color foo3 has the properties:
> model => cmyk
> value => 0.1, 0.2, 0.3, 0.4.
diff --git a/l3experimental/l3color/testfiles/m3color001.uptex.tlg b/l3experimental/l3color/testfiles/m3color001.uptex.tlg
index 7b08c6f0b..e22f5abcf 100644
--- a/l3experimental/l3color/testfiles/m3color001.uptex.tlg
+++ b/l3experimental/l3color/testfiles/m3color001.uptex.tlg
@@ -5,18 +5,21 @@ Author: Joseph Wright
TEST 1: Naming colors by model
============================================================
Defining \l__color_named_foo1_tl on line ...
+Defining \l__color_named_foo1_prop on line ...
The color foo1 has the properties:
> model => gray
> value => 0.1.
<recently read> }
l. ... }
Defining \l__color_named_foo2_tl on line ...
+Defining \l__color_named_foo2_prop on line ...
The color foo2 has the properties:
> model => rgb
> value => 0.1, 0.2, 0.3.
<recently read> }
l. ... }
Defining \l__color_named_foo3_tl on line ...
+Defining \l__color_named_foo3_prop on line ...
The color foo3 has the properties:
> model => cmyk
> value => 0.1, 0.2, 0.3, 0.4.
diff --git a/l3experimental/l3color/testfiles/m3color001.xetex.tlg b/l3experimental/l3color/testfiles/m3color001.xetex.tlg
index f309762d0..843ccb844 100644
--- a/l3experimental/l3color/testfiles/m3color001.xetex.tlg
+++ b/l3experimental/l3color/testfiles/m3color001.xetex.tlg
@@ -5,18 +5,21 @@ Author: Joseph Wright
TEST 1: Naming colors by model
============================================================
Defining \l__color_named_foo1_tl on line ...
+Defining \l__color_named_foo1_prop on line ...
The color foo1 has the properties:
> model => gray
> value => 0.1.
<recently read> }
l. ... }
Defining \l__color_named_foo2_tl on line ...
+Defining \l__color_named_foo2_prop on line ...
The color foo2 has the properties:
> model => rgb
> value => 0.1, 0.2, 0.3.
<recently read> }
l. ... }
Defining \l__color_named_foo3_tl on line ...
+Defining \l__color_named_foo3_prop on line ...
The color foo3 has the properties:
> model => cmyk
> value => 0.1, 0.2, 0.3, 0.4.
diff --git a/l3experimental/l3color/testfiles/m3color002.tlg b/l3experimental/l3color/testfiles/m3color002.tlg
index 46f012a5f..4f13867a5 100644
--- a/l3experimental/l3color/testfiles/m3color002.tlg
+++ b/l3experimental/l3color/testfiles/m3color002.tlg
@@ -5,6 +5,7 @@ Author: Joseph Wright
TEST 1: Hsb model
============================================================
Defining \l__color_named_foo1_tl on line ...
+Defining \l__color_named_foo1_prop on line ...
The color foo1 has the properties:
> model => rgb
> value => 0.7, 0.315, 0.28.
@@ -60,6 +61,7 @@ The color foo1 has the properties:
<recently read> }
l. ... }
Defining \l__color_named_foo2_tl on line ...
+Defining \l__color_named_foo2_prop on line ...
The color foo2 has the properties:
> model => rgb
> value => 0.07059, 0.20392, 0.68235.
diff --git a/l3experimental/l3color/testfiles/m3color003.lvt b/l3experimental/l3color/testfiles/m3color003.lvt
new file mode 100644
index 000000000..3acc06eb8
--- /dev/null
+++ b/l3experimental/l3color/testfiles/m3color003.lvt
@@ -0,0 +1,67 @@
+%
+% Copyright (C) 2020 The LaTeX3 Project
+%
+
+\documentclass{minimal}
+
+\input{regression-test}
+
+\RequirePackage[enable-debug]{expl3}
+\RequirePackage{l3color}
+\ExplSyntaxOn
+\debug_on:n { check-declarations , deprecation , log-functions }
+\ExplSyntaxOff
+
+\START
+
+\AUTHOR{Joseph Wright}
+
+\ExplSyntaxOn
+
+\OMIT
+ \cs_set_protected:Npn \test:n #1
+ {
+ \hbox_set:Nn \l_tmpa_box
+ {
+ \group_begin:
+ #1
+ Hello
+ \group_end:
+ }
+ \box_show:N \l_tmpa_box
+ }
+\TIMO
+
+\TEST { Multiple~models:~selecting }
+ {
+ \test:n { \color_select:nn { gray / rgb } { 0.1 / 0.1 , 0.1 , 0.2 } }
+ \group_begin:
+ \tl_set:Nn \l_color_fixed_model_tl { rgb }
+ \test:n { \color_select:nn { gray / rgb } { 0.1 / 0.1 , 0.1 , 0.2 } }
+ \group_end:
+ \test:n
+ {
+ \color_select:nn { rgb / cmyk }
+ { 0.1 , 0.2 , 0.3 / 0.1 , 0.2 , 0.3 , 0.4 }
+ }
+ \group_begin:
+ \tl_set:Nn \l_color_fixed_model_tl { cmyk }
+ \test:n
+ {
+ \color_select:nn { rgb / cmyk }
+ { 0.1 , 0.2 , 0.3 / 0.1 , 0.2 , 0.3 , 0.4 }
+ }
+ \group_end:
+ }
+
+\TEST { Multiple~models:~named }
+ {
+ \color_set:nnn { foo1 } { cmyk / rgb }
+ { 0.1 , 0.2 , 0.3 , 0.4 / 0.1 , 0.1 , 0.2 }
+ \color_set:nn { test } { cyan!50!foo1 }
+ \color_show:n { test }
+ \color_set:nn { test } { blue!50!foo1 }
+ \color_show:n { test }
+ }
+
+\END
diff --git a/l3experimental/l3color/testfiles/m3color003.tlg b/l3experimental/l3color/testfiles/m3color003.tlg
new file mode 100644
index 000000000..90d6cb107
--- /dev/null
+++ b/l3experimental/l3color/testfiles/m3color003.tlg
@@ -0,0 +1,72 @@
+This is a generated file for the LaTeX (2e + expl3) validation system.
+Don't change this file in any respect.
+Author: Joseph Wright
+============================================================
+TEST 1: Multiple models: selecting
+============================================================
+> \box...=
+\hbox(6.94444+0.0)x22.50005
+.\pdfcolorstack 0 push {0.1 g 0.1 G}
+.\OT1/cmr/m/n/10 H
+.\OT1/cmr/m/n/10 e
+.\OT1/cmr/m/n/10 l
+.\OT1/cmr/m/n/10 l
+.\OT1/cmr/m/n/10 o
+.\pdfcolorstack 0 pop
+! OK.
+<argument> \l_tmpa_box
+l. ... }
+> \box...=
+\hbox(6.94444+0.0)x22.50005
+.\pdfcolorstack 0 push {0.1 0.1 0.2 rg 0.1 0.1 0.2 RG}
+.\OT1/cmr/m/n/10 H
+.\OT1/cmr/m/n/10 e
+.\OT1/cmr/m/n/10 l
+.\OT1/cmr/m/n/10 l
+.\OT1/cmr/m/n/10 o
+.\pdfcolorstack 0 pop
+! OK.
+<argument> \l_tmpa_box
+l. ... }
+> \box...=
+\hbox(6.94444+0.0)x22.50005
+.\pdfcolorstack 0 push {0.1 0.2 0.3 rg 0.1 0.2 0.3 RG}
+.\OT1/cmr/m/n/10 H
+.\OT1/cmr/m/n/10 e
+.\OT1/cmr/m/n/10 l
+.\OT1/cmr/m/n/10 l
+.\OT1/cmr/m/n/10 o
+.\pdfcolorstack 0 pop
+! OK.
+<argument> \l_tmpa_box
+l. ... }
+> \box...=
+\hbox(6.94444+0.0)x22.50005
+.\pdfcolorstack 0 push {0.1 0.2 0.3 0.4 k 0.1 0.2 0.3 0.4 K}
+.\OT1/cmr/m/n/10 H
+.\OT1/cmr/m/n/10 e
+.\OT1/cmr/m/n/10 l
+.\OT1/cmr/m/n/10 l
+.\OT1/cmr/m/n/10 o
+.\pdfcolorstack 0 pop
+! OK.
+<argument> \l_tmpa_box
+l. ... }
+============================================================
+============================================================
+TEST 2: Multiple models: named
+============================================================
+Defining \l__color_named_foo1_tl on line ...
+Defining \l__color_named_foo1_prop on line ...
+Defining \l__color_named_test_tl on line ...
+The color test has the properties:
+> model => cmyk
+> value => 0.55, 0.1, 0.15, 0.2.
+<recently read> }
+l. ... }
+The color test has the properties:
+> model => rgb
+> value => 0.05, 0.05, 0.6.
+<recently read> }
+l. ... }
+============================================================
More information about the latex3-commits
mailing list.