[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.