[latex3-commits] [git/LaTeX3-latex3-latex3] master: Initial support for DeviceN color spaces (5685656b1)

Joseph Wright joseph.wright at morningstar2.co.uk
Wed Sep 16 11:34:04 CEST 2020


Repository : https://github.com/latex3/latex3
On branch  : master
Link       : https://github.com/latex3/latex3/commit/5685656b11b08da6eadd73fc02cf68dc5c2063a4

>---------------------------------------------------------------

commit 5685656b11b08da6eadd73fc02cf68dc5c2063a4
Author: Joseph Wright <joseph.wright at morningstar2.co.uk>
Date:   Wed Sep 16 10:34:04 2020 +0100

    Initial support for DeviceN color spaces
    
    At the moment no mixing - tints are a must, so are
    to follow shortly.


>---------------------------------------------------------------

5685656b11b08da6eadd73fc02cf68dc5c2063a4
 l3backend/l3backend-color.dtx           | 124 ++++++++--
 l3experimental/CHANGELOG.md             |   3 +
 l3experimental/l3color/l3color.dtx      | 391 +++++++++++++++++++++++++++++++-
 l3kernel/testfiles/m3expl001.luatex.tlg |   6 +
 l3kernel/testfiles/m3expl001.ptex.tlg   |   4 +
 l3kernel/testfiles/m3expl001.tlg        |   6 +
 l3kernel/testfiles/m3expl001.uptex.tlg  |   4 +
 l3kernel/testfiles/m3expl001.xetex.tlg  |   6 +
 l3kernel/testfiles/m3expl003.luatex.tlg |   6 +
 l3kernel/testfiles/m3expl003.ptex.tlg   |   4 +
 l3kernel/testfiles/m3expl003.tlg        |   6 +
 l3kernel/testfiles/m3expl003.uptex.tlg  |   4 +
 l3kernel/testfiles/m3expl003.xetex.tlg  |   6 +
 13 files changed, 550 insertions(+), 20 deletions(-)

diff --git a/l3backend/l3backend-color.dtx b/l3backend/l3backend-color.dtx
index a9a86daeb..4424c1d46 100644
--- a/l3backend/l3backend-color.dtx
+++ b/l3backend/l3backend-color.dtx
@@ -308,10 +308,11 @@
 %<*dvips>
 %    \end{macrocode}
 %
-% \begin{macro}{\@@_backend_select_separation:nn}
+% \begin{macro}{\@@_backend_select_separation:nn, \@@_backend_select_devicen:nn}
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_backend_select_separation:nn #1#2
   { \@@_backend_select:n { separation ~ #1 ~ #2 } }
+\cs_new_eq:NN \@@_backend_select_devicen:nn \@@_backend_select_separation:nn
 %    \end{macrocode}
 % \end{macro}
 %
@@ -362,7 +363,7 @@
       {
         !
         TeXDict ~ begin ~
-        /color \int_use:N \g_@@_separation_int
+        /color \int_use:N \g_@@_model_int
           {
             [ ~
               /Separation ~ ( \str_convert_pdfname:n {#1} ) ~
@@ -541,6 +542,30 @@
 % \end{macro}
 % \end{macro}
 %
+% \begin{macro}{\@@_backend_devicen_init:nnn}
+%   Trivial as almost all of the work occurs in the shared code.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_backend_devicen_init:nnn #1#2#3
+  {
+    \__kernel_backend_literal:e
+      {
+        !
+        TeXDict ~ begin ~
+        /color \int_use:N \g_@@_model_int
+          {
+            [ ~
+              /DeviceN ~
+              [ ~ #1 ~ ] ~
+              #2 ~
+              { ~ #3 ~ } ~
+            ] ~ setcolorspace
+          } ~ def ~
+        end
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
 %    \begin{macrocode}
 %</dvips>
 %    \end{macrocode}
@@ -549,10 +574,11 @@
 %<*dvisvgm>
 %    \end{macrocode}
 %
-% \begin{macro}{\@@_backend_select_separation:nn}
+% \begin{macro}{\@@_backend_select_separation:nn, \@@_backend_select_devicen:nn}
 %   No support at present.
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_backend_select_separation:nn #1#2 { }
+\cs_new_protected:Npn \@@_backend_select_devicen:nn #1#2 { }
 %    \end{macrocode}
 % \end{macro}
 %
@@ -573,7 +599,7 @@
 %<*dvipdfmx|luatex|pdftex|xetex>
 %    \end{macrocode}
 %
-% \begin{macro}{\@@_backend_select_separation:nn}
+% \begin{macro}{\@@_backend_select_separation:nn, \@@_backend_select_devicen:nn}
 % \begin{macro}{\@@_backend_select:n}
 %   Different syntaxes here as the stacks are accessed very differently.
 %    \begin{macrocode}
@@ -591,6 +617,7 @@
     \group_insert_after:N \@@_backend_reset:
   }
 %</dvipdfmx|xetex>
+\cs_new_eq:NN \@@_backend_select_devicen:nn \@@_backend_select_separation:nn
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
@@ -617,7 +644,7 @@
       }
     \@@_backend_separation_init:n
       {
-        /Separation
+        /Separation ~
         / \str_convert_pdfname:n {#1} ~ #2 ~
         \pdf_object_last:
       }
@@ -626,7 +653,7 @@
       {
         \pdfcoredict_gput:nnn
           { Page / Resources / ColorSpace }
-          { color \int_use:N \g_@@_separation_int }
+          { color \int_use:N \g_@@_model_int }
           { \pdf_object_last: }
       }
 %</luatex|pdftex>
@@ -638,7 +665,7 @@
 %<*dvipdfmx|xetex>
     \__kernel_backend_literal:x
       {
-        pdf:obj ~ @color \int_use:N \g_@@_separation_int \c_space_tl
+        pdf:obj ~ @color \int_use:N \g_@@_model_int \c_space_tl
           [#1]
       }
 %</dvipdfmx|xetex>
@@ -682,6 +709,65 @@
 % \end{macro}
 % \end{macro}
 %
+% \begin{macro}{\@@_backend_devicen_init:nnn}
+% \begin{macro}[EXP]{\@@_backend_devicen_init:w}
+% \begin{macro}{\@@_backend_devicen_init:n}
+%   Similar to the Separations case, but with an arbitrary function for
+%   the alternative space work.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_backend_devicen_init:nnn #1#2#3
+  {
+    \pdf_object_now:nx { stream }
+      {
+        {
+          /FunctionType ~ 4 ~
+          /Domain ~
+            [ ~
+              \prg_replicate:nn
+                { 0 \@@_backend_devicen_init:w #1 ~ \s_@@_stop }
+                { 0 ~ 1 ~ } ~
+            ] ~
+          /Range ~
+            [ ~
+              \str_case:nn {#2}
+                {
+                  { /DeviceCMYK } { 0 ~ 1 ~ 0 ~ 1 ~ 0 ~ 1 ~ 0 ~ 1 }
+                  { /DeviceGray } { 0 ~ 1 }
+                  { /DeviceRGB }  { 0 ~ 1 ~ 0 ~ 1 ~ 0 ~ 1 }
+                } ~
+            ]
+        }
+        {#3}
+     }
+    \@@_backend_separation_init:n
+      {
+        /DeviceN ~
+        [ ~ #1 ~ ] ~
+        #2 ~
+        \pdf_object_last:
+      }
+%<*luatex|pdftex>
+    \use:x
+      {
+        \pdfcoredict_gput:nnn
+          { Page / Resources / ColorSpace }
+          { color \int_use:N \g_@@_model_int }
+          { \pdf_object_last: }
+      }
+%</luatex|pdftex>
+  }
+\cs_new:Npn \@@_backend_devicen_init:w #1 ~ #2 \s_@@_stop
+  {
+    + 1
+    \tl_if_blank:nF {#2}
+      { \@@_backend_devicen_init:w #2 \s_@@_stop }
+  }
+\cs_new_eq:NN \@@_backend_devicen_init:n \@@_backend_separation_init:n
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
 %    \begin{macrocode}
 %</dvipdfmx|luatex|pdftex|xetex>
 %    \end{macrocode}
@@ -730,13 +816,17 @@
 % \begin{macro}
 %   {
 %     \@@_backend_fill_separation:nn,
-%     \@@_backend_stroke_separation:nn
+%     \@@_backend_stroke_separation:nn,
+%     \@@_backend_fill_devicen:nn,
+%     \@@_backend_stroke_devicen:nn
 %   }
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_backend_fill_separation:nn #1#2
   { \__kernel_backend_literal_pdf:n { /#1 ~ cs ~ #2 ~ scn } }
 \cs_new_protected:Npn \@@_backend_stroke_separation:nn #1#2
   { \__kernel_backend_literal_pdf:n { /#1 ~ CS ~ #2 ~ SCN } }
+\cs_new_eq:NN \@@_backend_fill_devicen:nn \@@_backend_fill_separation:nn
+\cs_new_eq:NN \@@_backend_stroke_devicen:nn \@@_backend_stroke_separation:nn
 %    \end{macrocode}
 % \end{macro}
 %
@@ -777,13 +867,17 @@
 % \begin{macro}
 %   {
 %     \@@_backend_fill_separation:nn,
-%     \@@_backend_stroke_separation:nn
+%     \@@_backend_stroke_separation:nn,
+%     \@@_backend_fill_devicen:nn,
+%     \@@_backend_stroke_devicen:nn
 %   }
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_backend_fill_separation:nn #1#2
   { \__kernel_backend_postscript:n { /color.fc { #1 } def } }
 \cs_new_protected:Npn \@@_backend_stroke_separation:nn #1#2
   { \__kernel_backend_postscript:n { /color.sc { #1 } def } }
+\cs_new_eq:NN \@@_backend_fill_devicen:nn \@@_backend_fill_separation:nn
+\cs_new_eq:NN \@@_backend_stroke_devicen:nn \@@_backend_stroke_separation:nn
 %    \end{macrocode}
 % \end{macro}
 %
@@ -892,14 +986,16 @@
 % \begin{macro}
 %   {
 %     \@@_backend_fill_separation:nn,
-%     \@@_backend_stroke_separation:nn
+%     \@@_backend_stroke_separation:nn,
+%     \@@_backend_fill_devicen:nn,
+%     \@@_backend_stroke_devicen:nn
 %   }
 %   At present, these are no-ops.
 %    \begin{macrocode}
-\cs_new_protected:Npn \@@_backend_fill_separation:nn #1#2
-  { }
-\cs_new_protected:Npn \@@_backend_stroke_separation:nn #1#2
-  { }
+\cs_new_protected:Npn \@@_backend_fill_separation:nn #1#2 { }
+\cs_new_protected:Npn \@@_backend_stroke_separation:nn #1#2 { }
+\cs_new_eq:NN \@@_backend_fill_devicen:nn \@@_backend_fill_separation:nn
+\cs_new_eq:NN \@@_backend_stroke_devicen:nn \@@_backend_stroke_separation:nn
 %    \end{macrocode}
 % \end{macro}
 %
diff --git a/l3experimental/CHANGELOG.md b/l3experimental/CHANGELOG.md
index 362059210..edefc1c56 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 DeviceN color spaces
+
 ## [2020-09-11]
 
 ### Fixed
diff --git a/l3experimental/l3color/l3color.dtx b/l3experimental/l3color/l3color.dtx
index f644bf5fd..e30d2a83b 100644
--- a/l3experimental/l3color/l3color.dtx
+++ b/l3experimental/l3color/l3color.dtx
@@ -362,6 +362,7 @@
 %   Creates a new \meta{model} which is derived from the color model \meta{family}.
 %   The latter should be one of
 %   \begin{itemize}
+%     \item \texttt{DeviceN}
 %     \item \texttt{Separation}
 %   \end{itemize}
 %   (The \meta{family} may be given in mixed case as-in the PDF reference:
@@ -398,6 +399,13 @@
 % not the case, \pkg{l3color} will fallback to using black as the colorant in
 % any mixing.
 %
+% For a \texttt{DeviceN} space, there is one \emph{compulsory} key.
+% \begin{itemize}
+%   \item \texttt{names} The names of the components of the \texttt{DeviceN}
+%   space. Each should be either the \meta{name} of a \texttt{Separation} model,
+%   a process color name (\texttt{cyan}, etc.) or the special name \texttt{none}.
+% \end{itemize}
+%
 % \end{documentation}
 %
 % \begin{implementation}
@@ -439,8 +447,9 @@
 %
 % \subsection{Setup}
 %
-% \begin{variable}{\l_@@_internal_tl}
+% \begin{variable}{\l_@@_internal_int, \l_@@_internal_tl}
 %    \begin{macrocode}
+\int_new:N \l_@@_internal_int
 \tl_new:N \l_@@_internal_tl
 %    \end{macrocode}
 % \end{variable}
@@ -1517,10 +1526,25 @@
 %    \end{macrocode}
 % \end{variable}
 %
-% \begin{variable}{\g_@@_separation_int}
-%   A tracker for the total number of separations.
+% \begin{variable}{\g_@@_model_int}
+%   A tracker for the total number of new models.
+%    \begin{macrocode}
+\int_new:N \g_@@_model_int
+%    \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_colorants_prop}
+%   Mapping from names to colorants.
 %    \begin{macrocode}
-\int_new:N \g_@@_separation_int
+\prop_new:N \g_@@_colorants_prop
+\prop_gput:Nnn \g_@@_colorants_prop { black }   { Black }
+\prop_gput:Nnn \g_@@_colorants_prop { blue }    { Blue }
+\prop_gput:Nnn \g_@@_colorants_prop { cyan }    { Cyan }
+\prop_gput:Nnn \g_@@_colorants_prop { green }   { Green }
+\prop_gput:Nnn \g_@@_colorants_prop { magenta } { Magenta }
+\prop_gput:Nnn \g_@@_colorants_prop { none }    { None }
+\prop_gput:Nnn \g_@@_colorants_prop { red }     { Red }
+\prop_gput:Nnn \g_@@_colorants_prop { yellow }  { Yellow }
 %    \end{macrocode}
 % \end{variable}
 %
@@ -1553,6 +1577,31 @@
 %    \end{macrocode}
 % \end{variable}
 %
+% \begin{variable}{\g_@@_alternative_model_prop}
+%   For tracking the alternative model set up for separations, etc.
+%    \begin{macrocode}
+\prop_new:N \g_@@_alternative_model_prop
+\clist_map_inline:nn { cyan , magenta , yellow , black }
+  { \prop_gput:Nnn \g_@@_alternative_model_prop {#1} { cmyk } }
+\clist_map_inline:nn { red , green , blue }
+  { \prop_gput:Nnn \g_@@_alternative_model_prop {#1} { rgb } }
+%    \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\g_@@_alternative_values_prop}
+%   Same for the values: a bit more involved.
+%    \begin{macrocode}
+\prop_new:N \g_@@_alternative_values_prop
+\prop_gput:Nnn \g_@@_alternative_values_prop { cyan }    {  1 , 0 , 0 , 0 }
+\prop_gput:Nnn \g_@@_alternative_values_prop { magenta } {  0 , 1 , 0 , 0 }
+\prop_gput:Nnn \g_@@_alternative_values_prop { yellow }  {  0 , 0 , 1 , 0 }
+\prop_gput:Nnn \g_@@_alternative_values_prop { black }   {  0 , 0 , 0 , 1 }
+\prop_gput:Nnn \g_@@_alternative_values_prop { red }   {  1 , 0 , 0 }
+\prop_gput:Nnn \g_@@_alternative_values_prop { green } {  0 , 1 , 0 }
+\prop_gput:Nnn \g_@@_alternative_values_prop { blue }  {  0 , 0 , 1 }
+%    \end{macrocode}
+% \end{variable}
+%
 % \begin{macro}{\color_model_new:nnn, \@@_model_new:nnn}
 %   Set up a new model: in general this has to be handled by a family-dependent
 %   function. To avoid some \enquote{interesting} questions with casing, we
@@ -1665,7 +1714,7 @@
 \cs_new_protected:Npn \@@_model_separation:w
   #1 , #2 , #3 , #4 , #5 \s_@@_stop #6#7#8
   {
-    \int_gincr:N \g_@@_separation_int
+    \int_gincr:N \g_@@_model_int
     \tl_const:cn { c_@@_fallback_ #6 _tl } { 1 }
     \cs_new_eq:cN { @@_parse_mix_ #6 :nw } \@@_parse_mix_gray:nw
     \cs_new:cpn { @@_parse_model_ #6 :w } ##1 , ##2 \s_@@_stop
@@ -1675,11 +1724,14 @@
         \cs_new_protected:cpx { @@_backend_ ##1 _ #6 :n } ####1
           {
             \exp_not:c { @@_backend_ ##1 _ separation:nn }
-              { color \int_use:N \g_@@_separation_int } {####1}
+              { color \int_use:N \g_@@_model_int } {####1}
           }
       }
     \use:c { @@_model_separation_ #8 :nnnnnn }
       {#6} {#7} {#1} {#2} {#3} {#4}
+    \prop_gput:Nnn \g_@@_alternative_model_prop {#6} {#8}
+    \prop_gput:Nnx \g_@@_colorants_prop {#6}
+      { \str_convert_pdfname:n {#7} }
     \cs_new_protected:cpx { @@_model_ #6 _white: }
       {
         \prop_put:Nnn \exp_not:N \l_@@_named_white_prop {#6} { 0 }
@@ -1699,6 +1751,7 @@
       }
     \@@_model_convert:nnn {#1} { cmyk } { rgb }
     \@@_model_convert:nnn {#1} { cmyk } { gray }
+    \prop_gput:Nnn \g_@@_alternative_values_prop {#1} { #3 , #4 , #5 , #6 }
     \@@_backend_separation_init:nnnnn {#2} { /DeviceCMYK } { }
       { 0 ~ 0 ~ 0 ~ 0 } { #3 ~ #4 ~ #5 ~ #6 }
   }
@@ -1712,6 +1765,7 @@
       }
     \@@_model_convert:nnn {#1} { rgb } { cmyk }
     \@@_model_convert:nnn {#1} { rgb } { gray }
+    \prop_gput:Nnn \g_@@_alternative_values_prop {#1} { #3 , #4 , #5 }
     \@@_backend_separation_init:nnnnn {#2} { /DeviceRGB } { }
       { 0 ~ 0 ~ 0 } { #3 ~ #4 ~ #5 }
   }
@@ -1732,6 +1786,7 @@
         \fp_eval:n {##1 * #3} ~
         \fp_eval:n {##1 * #3}
       }
+    \prop_gput:Nnn \g_@@_alternative_values_prop {#1} {#3}
     \@@_backend_separation_init:nnnnn {#2} { /DeviceGray } { } { 0 } {#3}
   }
 %    \end{macrocode}
@@ -1797,6 +1852,308 @@
 % \end{macro}
 % \end{macro}
 %
+% \begin{macro}{\@@_model_devicen:n}
+% \begin{macro}{\@@_model_devicen:nn}
+% \begin{macro}{\@@_model_devicen:nnn}
+% \begin{macro}{\@@_model_devicen:nnnn}
+% \begin{macro}
+%   {
+%     \@@_model_devicen_parse_1:nn ,
+%     \@@_model_devicen_parse_2:nn ,
+%     \@@_model_devicen_parse_3:nn ,
+%     \@@_model_devicen_parse_4:nn ,
+%     \@@_model_devicen_parse_generic:nn
+%  }
+%  \begin{macro}[EXP]{\@@_model_devicen_parse:nw}
+% \begin{macro}{\@@_model_devicen_init:nnn}
+% \begin{macro}{\@@_model_devicen_init:nnnn}
+% \begin{macro}{\@@_model_devicen_tranform:w}
+% \begin{macro}
+%   {
+%     \@@_model_devicen_tranform_1:nnnnn ,
+%     \@@_model_devicen_tranform_3:nnnnn ,
+%     \@@_model_devicen_tranform_4:nnnnn ,
+%   }
+% \begin{macro}{\@@_model_devicen_tranform:nnn}
+% \begin{macro}[EXP]{\@@_model_devicen_colorant:n}
+%   We require a list of component names here: one might call them colorants,
+%   but it's convenient to use \TeX{} names instead so we slightly adjust the
+%   terminology.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_model_devicen:n #1
+  {
+    \prop_get:NnNTF \l_@@_internal_prop { names }
+      \l_@@_internal_tl
+      {
+        \exp_args:NV \@@_model_devicen:nn
+          \l_@@_internal_tl {#1}
+      }
+      {
+        \__kernel_msg_error:nnn { color }
+          { DeviceN-requires-names } {#1}
+      }
+  }
+%    \end{macrocode}
+%   All valid models will have an alternative listed, either hard-coded for
+%   the core device ones, or dynamically added for Separations, etc.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_model_devicen:nn #1#2
+  {
+    \tl_clear:N \l_@@_model_tl
+    \clist_map_inline:nn {#1}
+      {
+        \prop_get:NnNTF \g_@@_alternative_model_prop {##1}
+          \l_@@_internal_tl
+          {
+            \tl_if_empty:NTF \l_@@_model_tl
+              { \tl_set_eq:NN \l_@@_model_tl \l_@@_internal_tl }
+              {
+                \tl_if_eq:NNF \l_@@_model_tl \l_@@_internal_tl
+                  {
+                    \__kernel_msg_error:nnn { color }
+                      { DeviceN-inconsistent-alternative }
+                      {#1}
+                    \clist_map_break:n { \use_none:nnnnn }
+                  }
+               }
+          }
+          {
+            \__kernel_msg_error:nnn { color }
+              { DeviceN-no-alternative }
+              {#2}
+          }
+      }
+    \exp_args:NV \@@_model_devicen:nnn \l_@@_model_tl {#1} {#2}
+  }
+%    \end{macrocode}
+%   We now complete the data we require by first finding out how many
+%   colorants there are, then moving on to begin constructing the function
+%   required to map to the alternative color space.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_model_devicen:nnn #1#2#3
+  {
+    \exp_args:Nx \@@_model_devicen:nnnn
+      { \clist_count:n {#2} } {#1} {#2} {#3}
+  }
+%    \end{macrocode}
+%   At this stage, we have checked everything is in place, so we can set up
+%   the \TeX{} and backend data structures. As for separations, it's not really
+%   possible in general to have a fallback, so we simply provide
+%   \enquote{black} for each element.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_model_devicen:nnnn #1#2#3#4
+  {
+    \int_gincr:N \g_@@_model_int
+    \tl_const:cx { c_@@_fallback_ #4 _tl }
+      { \prg_replicate:nn {#1} { 1 ~ } }
+    \cs_if_exist_use:cF { @@_model_devicen_parse_ #1 :nn }
+      { \@@_model_devicen_parse_generic:nn }
+        {#4} {#1}
+    \clist_map_inline:nn { fill , stroke , select }
+      {
+        \cs_new_protected:cpx { @@_backend_ ##1 _ #4 :n } ####1
+          {
+            \exp_not:c { @@_backend_ ##1 _ devicen:nn }
+              { color \int_use:N \g_@@_model_int } {####1}
+          }
+      }
+    \@@_model_devicen_init:nnn {#1} {#2} {#3}
+  }
+%    \end{macrocode}
+%   For short lists of DeviceN colors, wee can use hand-tuned parsing. This
+%   lines up with other models, where we allow for up to four components. For
+%   larger spaces,. rather than limit artificially, we use a somewhat slow
+%   approach based on open-ended commas-lists.
+%    \begin{macrocode}
+\cs_new_protected:cpn { @@_model_devicen_parse_1:nn } #1#2
+  {
+    \cs_new:cpn { @@_parse_model_ #1 :w  } ##1 , ##2 \s_@@_stop
+      { {#1} { \@@_parse_number:n {##1} } }
+  }
+\cs_new_protected:cpn { @@_model_devicen_parse_2:nn } #1#2
+  {
+    \cs_new:cpn { @@_parse_model_ #1 :w  } ##1 , ##2 , ##3 \s_@@_stop
+      { {#1} { \@@_parse_number:n {##1} ~ \@@_parse_number:n {##2} } }
+  }
+\cs_new_protected:cpn { @@_model_devicen_parse_3:nn } #1#2
+  {
+    \cs_new:cpn { @@_parse_model_ #1 :w  } ##1 , ##2 , ##3 , ##4 \s_@@_stop
+      {
+        {#1}
+        {
+          \@@_parse_number:n {##1} ~
+          \@@_parse_number:n {##2} ~
+          \@@_parse_number:n {##3}
+        }
+      }
+  }
+\cs_new_protected:cpn { @@_model_devicen_parse_4:nn } #1#2
+  {
+    \cs_new:cpn { @@_parse_model_ #1 :w  }
+      ##1 , ##2 , ##3 , ##4 , ##5 \s_@@_stop
+      {
+        {#1}
+        {
+          \@@_parse_number:n {##1} ~
+          \@@_parse_number:n {##2} ~
+          \@@_parse_number:n {##3} ~
+          \@@_parse_number:n {##4}
+        }
+      }
+  }
+\cs_new_protected:Npn \@@_model_devicen_parse_generic:nn #1#2
+  {
+    \cs_new:cpn { @@_parse_model_ #1 :w  } ##1 , ##2 \s_@@_stop
+      {
+        {#1}
+        { \@@_model_devicen_parse:nw {#2} ##2 , \q_nil , \s_@@_stop }
+      }
+  }
+\cs_new:Npn \@@_model_devicen_parse:nw #1#2 , #3 \s_@@_stop
+  {
+    \int_compare:nNnT {#1} > 0
+      {
+        \quark_if_nil:nTF {#2}
+          { \prg_replicate:nn {#1} { 0 ~ } }
+          {
+            \@@_parse_number:n {#2}
+            \int_compare:nNnT {#1} > 1 { ~ }
+            \exp_args:Nf \@@_model_devicen_parse:nw
+              { \int_eval:n { #1 - 1 } } #3 \s_@@_stop
+          }
+      }
+  }
+%    \end{macrocode}
+%   To construct the tint transformation, we have to use PostScript. The
+%   aim is to have the final tint for each device colorant as
+%   \[
+%     1 - \prod_{n} (1 - X_{n} D_{X_{n}})
+%   \]
+%   where $X$ is a DeviceN colorant and $D$ is the amount of device colorant
+%   that the DeviceN colorant maps to. At the start of the process, the
+%   PostScript stack will contain the $X_{n}$ values, whilst we have the
+%   $D$ values on a per-DeviceN colorant basis. The more convenient approach
+%   for us is therefore to take each DeviceN colorant in turn and find the
+%   value $1 - X_{n} D_{X_{n}}$, multiplying as we go, and finalise with the
+%   subtraction. That contrasts to \pkg{colorspace}: it splits the process
+%   up by process color, which works better when you have a fixed list
+%   of colorants. (\pkg{colorspace} only supports up to $4$ DeviceN colors,
+%   and only \texttt{cmyk} as the alternative space.) To set this up,
+%   we first need to know the number of values in the target color space:
+%   this is easily handled as there are a very small range of possibles.
+%   Once we have that information, it's relatively easy to build the required
+%   PostScript using some generic code.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_model_devicen_init:nnn #1#2#3
+  {
+    \exp_args:Ne \@@_model_devicen_init:nnnn
+      {
+        \str_case:nn {#2}
+          {
+            { cmyk } { 4 }
+            { gray } { 1 }
+            { rgb }  { 3 }
+          }
+      }
+      {#1} {#2} {#3}
+  }
+%    \end{macrocode}
+%   As we always need to split the alternative values into parts, we use a
+%   shared auxiliary and only use a minimal difference between code paths.
+%   Construction of the tint transformation is as far as possible done using
+%   loops, which means there are some inefficiencies for device colors in
+%   the \texttt{DeviceN} space: we roll the stack one-at-a-time even if there
+%   is a potential shortcut. However, that way there is nothing to special-case.
+%   Once this is sorted, we can write the tint transform object, which will
+%   remain as the last object until we sort out the final step: the colorant
+%   list.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_model_devicen_init:nnnn #1#2#3#4
+  {
+    \tl_set:Nx \l_@@_internal_tl
+      { \prg_replicate:nn {#1} { 1.0 ~ }   }
+    \int_zero:N \l_@@_internal_int
+    \clist_map_inline:nn {#4}
+      {
+        \int_incr:N \l_@@_internal_int
+        \prop_get:NnN \g_@@_alternative_values_prop {##1}
+          \l_@@_value_tl
+        \exp_after:wN \@@_model_devicen_transform:w
+          \l_@@_value_tl , 0 , 0 , 0 \s_@@_stop {#1} {#2}
+      }
+    \tl_put_right:Nx \l_@@_internal_tl
+      {
+        \prg_replicate:nn {#1}
+          { neg ~ 1.0 ~ add ~ #1 ~ -1 ~ roll ~ }
+        \int_eval:n { #2 + 4 } ~ 4 ~ roll
+        \prg_replicate:nn {#2} { ~ pop }
+      }
+    \use:x
+      {
+        \@@_backend_devicen_init:nnn
+          {
+            \clist_map_function:nN {#4}
+              \@@_model_devicen_colorant:n
+          }
+          {
+            \str_case:nn {#3}
+              {
+                { cmyk } { /DeviceCMYK }
+                { gray } { /DeviceGray }
+                { rgb }  { /DeviceRGB }
+              }
+          }
+          { \exp_not:V \l_@@_internal_tl }
+      }
+  }
+\cs_new_protected:Npn \@@_model_devicen_transform:w
+  #1 , #2 , #3 , #4 , #5 \s_@@_stop #6#7
+  {
+    \use:c { @@_model_devicen_transform_ #6 :nnnnn }
+      {#1} {#2} {#3} {#4} {#7}
+  }
+\cs_new_protected:cpn { @@_model_devicen_transform_1:nnnnn } #1#2#3#4#5
+  { \@@_model_devicen_transform:nnn {#5} { 1 } {#1} }
+\cs_new_protected:cpn { @@_model_devicen_transform_3:nnnnn } #1#2#3#4#5
+  {
+    \clist_map_inline { #1 , #2 , #3 }
+      { \@@_model_devicen_transform:nnn {#5} { 3 } {##1} }
+  }
+\cs_new_protected:cpn { @@_model_devicen_transform_4:nnnnn } #1#2#3#4#5
+  {
+    \clist_map_inline:nn { #1 , #2 , #3 , #4 }
+      { \@@_model_devicen_transform:nnn {#5} { 4 } {##1} }
+  }
+\cs_new_protected:Npn \@@_model_devicen_transform:nnn #1#2#3
+  {
+    \tl_put_right:Nx \l_@@_internal_tl
+      {
+        \fp_compare:nNnF {#3} = \c_zero_fp
+          {
+            \int_eval:n { #1 - \l_@@_internal_int + #2 } ~ index ~
+              #3 ~ neg ~ mul ~ 1.0 ~ add ~ mul ~
+          }
+        #2 ~ -1 ~ roll ~
+      }
+  }
+\cs_new:Npn \@@_model_devicen_colorant:n #1
+  {
+    / \prop_item:Nn \g_@@_colorants_prop {#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}
+% \end{macro}
+%
 % \subsection{Diagnostics}
 %
 % \begin{macro}{\color_show:n}
@@ -1847,6 +2204,28 @@
     LaTeX~has~been~asked~to~convert~a~color~from~model~'#1'~
     to~model'#2',~but~there~is~no~method~available~to~do~that.
   }
+\__kernel_msg_new:nnnn { color } { DeviceN-inconsistent-alternative }
+  { DeviceN~color~spaces~require~a~single~alternative~space. }
+  {
+    LaTeX~has~been~asked~to~create~a~DeviceN~color~space~'#1',~
+    but~the~constituent~colors~do~not~have~a~common~alternative~
+    color.
+  }
+\__kernel_msg_new:nnnn { color } { DeviceN-no-alternative }
+  { DeviceN~color~spaces~require~an~alternative~space. }
+  {
+    LaTeX~has~been~asked~to~create~a~DeviceN~color~space~'#1',~
+    but~the~constituent~colors~do~not~all~have~a~device-based~alternative.
+  }
+\__kernel_msg_new:nnnn { color } { DeviceN-requires-names }
+  { DeviceN~color~space~'#1'~require~a~list~of~names. }
+  {
+    LaTeX~has~been~asked~to~create~a~DeviceN~color~space,~
+    but~no~\\ \\
+    \iow_indent:n { names~=~<names> }
+    \\ \\
+    key~was~given~with~the~correct~information.
+  }
 \__kernel_msg_new:nnnn { color } { model-already-defined }
   { Color~model~'#1'~already~defined. }
   {
diff --git a/l3kernel/testfiles/m3expl001.luatex.tlg b/l3kernel/testfiles/m3expl001.luatex.tlg
index 136d88fad..8b9a9ad24 100644
--- a/l3kernel/testfiles/m3expl001.luatex.tlg
+++ b/l3kernel/testfiles/m3expl001.luatex.tlg
@@ -36,9 +36,13 @@ Defining \__color_backend_select_rgb:n on line ...
 Defining \__color_backend_select:n on line ...
 Defining \__color_backend_reset: on line ...
 Defining \__color_backend_select_separation:nn on line ...
+Defining \__color_backend_select_devicen:nn on line ...
 Defining \__color_backend_separation_init:nnnnn on line ...
 Defining \__color_backend_separation_init:n on line ...
 Defining \__color_backend_separation_init_CIELAB:nnn on line ...
+Defining \__color_backend_devicen_init:nnn on line ...
+Defining \__color_backend_devicen_init:w on line ...
+Defining \__color_backend_devicen_init:n on line ...
 Defining \__color_backend_fill_cmyk:n on line ...
 Defining \__color_backend_fill_gray:n on line ...
 Defining \__color_backend_fill_rgb:n on line ...
@@ -47,6 +51,8 @@ Defining \__color_backend_stroke_gray:n on line ...
 Defining \__color_backend_stroke_rgb:n on line ...
 Defining \__color_backend_fill_separation:nn on line ...
 Defining \__color_backend_stroke_separation:nn on line ...
+Defining \__color_backend_fill_devicen:nn on line ...
+Defining \__color_backend_stroke_devicen:nn on line ...
 Defining \__box_backend_clip:N on line ...
 Defining \__box_backend_rotate:Nn on line ...
 Defining \__box_backend_rotate_aux:Nn on line ...
diff --git a/l3kernel/testfiles/m3expl001.ptex.tlg b/l3kernel/testfiles/m3expl001.ptex.tlg
index ba9930c14..4d3d1ed8d 100644
--- a/l3kernel/testfiles/m3expl001.ptex.tlg
+++ b/l3kernel/testfiles/m3expl001.ptex.tlg
@@ -35,6 +35,7 @@ Defining \__color_backend_select_rgb:n on line ...
 Defining \__color_backend_select:n on line ...
 Defining \__color_backend_reset: on line ...
 Defining \__color_backend_select_separation:nn on line ...
+Defining \__color_backend_select_devicen:nn on line ...
 Defining \__color_backend_separation_init:nnnnn on line ...
 Defining \exp_args:Nnxx on line ...
 Defining \__color_backend_separation_init:nxxnn on line ...
@@ -51,6 +52,7 @@ Defining \__color_backend_separation_init:w on line ...
 Defining \__color_backend_separation_init:n on line ...
 Defining \__color_backend_separation_init:nw on line ...
 Defining \__color_backend_separation_init_CIELAB:nnn on line ...
+Defining \__color_backend_devicen_init:nnn on line ...
 Defining \__color_backend_fill_cmyk:n on line ...
 Defining \__color_backend_fill_gray:n on line ...
 Defining \__color_backend_fill_rgb:n on line ...
@@ -59,6 +61,8 @@ Defining \__color_backend_stroke_gray:n on line ...
 Defining \__color_backend_stroke_rgb:n on line ...
 Defining \__color_backend_fill_separation:nn on line ...
 Defining \__color_backend_stroke_separation:nn on line ...
+Defining \__color_backend_fill_devicen:nn on line ...
+Defining \__color_backend_stroke_devicen:nn on line ...
 Defining \__box_backend_clip:N on line ...
 Defining \__box_backend_rotate:Nn on line ...
 Defining \__box_backend_rotate_aux:Nn on line ...
diff --git a/l3kernel/testfiles/m3expl001.tlg b/l3kernel/testfiles/m3expl001.tlg
index c2329c9e8..1dc04ecb1 100644
--- a/l3kernel/testfiles/m3expl001.tlg
+++ b/l3kernel/testfiles/m3expl001.tlg
@@ -36,9 +36,13 @@ Defining \__color_backend_select_rgb:n on line ...
 Defining \__color_backend_select:n on line ...
 Defining \__color_backend_reset: on line ...
 Defining \__color_backend_select_separation:nn on line ...
+Defining \__color_backend_select_devicen:nn on line ...
 Defining \__color_backend_separation_init:nnnnn on line ...
 Defining \__color_backend_separation_init:n on line ...
 Defining \__color_backend_separation_init_CIELAB:nnn on line ...
+Defining \__color_backend_devicen_init:nnn on line ...
+Defining \__color_backend_devicen_init:w on line ...
+Defining \__color_backend_devicen_init:n on line ...
 Defining \__color_backend_fill_cmyk:n on line ...
 Defining \__color_backend_fill_gray:n on line ...
 Defining \__color_backend_fill_rgb:n on line ...
@@ -47,6 +51,8 @@ Defining \__color_backend_stroke_gray:n on line ...
 Defining \__color_backend_stroke_rgb:n on line ...
 Defining \__color_backend_fill_separation:nn on line ...
 Defining \__color_backend_stroke_separation:nn on line ...
+Defining \__color_backend_fill_devicen:nn on line ...
+Defining \__color_backend_stroke_devicen:nn on line ...
 Defining \__box_backend_clip:N on line ...
 Defining \__box_backend_rotate:Nn on line ...
 Defining \__box_backend_rotate_aux:Nn on line ...
diff --git a/l3kernel/testfiles/m3expl001.uptex.tlg b/l3kernel/testfiles/m3expl001.uptex.tlg
index ba9930c14..4d3d1ed8d 100644
--- a/l3kernel/testfiles/m3expl001.uptex.tlg
+++ b/l3kernel/testfiles/m3expl001.uptex.tlg
@@ -35,6 +35,7 @@ Defining \__color_backend_select_rgb:n on line ...
 Defining \__color_backend_select:n on line ...
 Defining \__color_backend_reset: on line ...
 Defining \__color_backend_select_separation:nn on line ...
+Defining \__color_backend_select_devicen:nn on line ...
 Defining \__color_backend_separation_init:nnnnn on line ...
 Defining \exp_args:Nnxx on line ...
 Defining \__color_backend_separation_init:nxxnn on line ...
@@ -51,6 +52,7 @@ Defining \__color_backend_separation_init:w on line ...
 Defining \__color_backend_separation_init:n on line ...
 Defining \__color_backend_separation_init:nw on line ...
 Defining \__color_backend_separation_init_CIELAB:nnn on line ...
+Defining \__color_backend_devicen_init:nnn on line ...
 Defining \__color_backend_fill_cmyk:n on line ...
 Defining \__color_backend_fill_gray:n on line ...
 Defining \__color_backend_fill_rgb:n on line ...
@@ -59,6 +61,8 @@ Defining \__color_backend_stroke_gray:n on line ...
 Defining \__color_backend_stroke_rgb:n on line ...
 Defining \__color_backend_fill_separation:nn on line ...
 Defining \__color_backend_stroke_separation:nn on line ...
+Defining \__color_backend_fill_devicen:nn on line ...
+Defining \__color_backend_stroke_devicen:nn on line ...
 Defining \__box_backend_clip:N on line ...
 Defining \__box_backend_rotate:Nn on line ...
 Defining \__box_backend_rotate_aux:Nn on line ...
diff --git a/l3kernel/testfiles/m3expl001.xetex.tlg b/l3kernel/testfiles/m3expl001.xetex.tlg
index 72681207d..13b2e496a 100644
--- a/l3kernel/testfiles/m3expl001.xetex.tlg
+++ b/l3kernel/testfiles/m3expl001.xetex.tlg
@@ -32,9 +32,13 @@ Defining \__color_backend_reset: on line ...
 Defining \__color_backend_pickup:N on line ...
 Defining \__color_backend_select_separation:nn on line ...
 Defining \__color_backend_select:n on line ...
+Defining \__color_backend_select_devicen:nn on line ...
 Defining \__color_backend_separation_init:nnnnn on line ...
 Defining \__color_backend_separation_init:n on line ...
 Defining \__color_backend_separation_init_CIELAB:nnn on line ...
+Defining \__color_backend_devicen_init:nnn on line ...
+Defining \__color_backend_devicen_init:w on line ...
+Defining \__color_backend_devicen_init:n on line ...
 Defining \__color_backend_fill_cmyk:n on line ...
 Defining \__color_backend_fill_gray:n on line ...
 Defining \__color_backend_fill_rgb:n on line ...
@@ -43,6 +47,8 @@ Defining \__color_backend_stroke_gray:n on line ...
 Defining \__color_backend_stroke_rgb:n on line ...
 Defining \__color_backend_fill_separation:nn on line ...
 Defining \__color_backend_stroke_separation:nn on line ...
+Defining \__color_backend_fill_devicen:nn on line ...
+Defining \__color_backend_stroke_devicen:nn on line ...
 Defining \__box_backend_clip:N on line ...
 Defining \__box_backend_rotate:Nn on line ...
 Defining \__box_backend_rotate_aux:Nn on line ...
diff --git a/l3kernel/testfiles/m3expl003.luatex.tlg b/l3kernel/testfiles/m3expl003.luatex.tlg
index 136d88fad..8b9a9ad24 100644
--- a/l3kernel/testfiles/m3expl003.luatex.tlg
+++ b/l3kernel/testfiles/m3expl003.luatex.tlg
@@ -36,9 +36,13 @@ Defining \__color_backend_select_rgb:n on line ...
 Defining \__color_backend_select:n on line ...
 Defining \__color_backend_reset: on line ...
 Defining \__color_backend_select_separation:nn on line ...
+Defining \__color_backend_select_devicen:nn on line ...
 Defining \__color_backend_separation_init:nnnnn on line ...
 Defining \__color_backend_separation_init:n on line ...
 Defining \__color_backend_separation_init_CIELAB:nnn on line ...
+Defining \__color_backend_devicen_init:nnn on line ...
+Defining \__color_backend_devicen_init:w on line ...
+Defining \__color_backend_devicen_init:n on line ...
 Defining \__color_backend_fill_cmyk:n on line ...
 Defining \__color_backend_fill_gray:n on line ...
 Defining \__color_backend_fill_rgb:n on line ...
@@ -47,6 +51,8 @@ Defining \__color_backend_stroke_gray:n on line ...
 Defining \__color_backend_stroke_rgb:n on line ...
 Defining \__color_backend_fill_separation:nn on line ...
 Defining \__color_backend_stroke_separation:nn on line ...
+Defining \__color_backend_fill_devicen:nn on line ...
+Defining \__color_backend_stroke_devicen:nn on line ...
 Defining \__box_backend_clip:N on line ...
 Defining \__box_backend_rotate:Nn on line ...
 Defining \__box_backend_rotate_aux:Nn on line ...
diff --git a/l3kernel/testfiles/m3expl003.ptex.tlg b/l3kernel/testfiles/m3expl003.ptex.tlg
index ba9930c14..4d3d1ed8d 100644
--- a/l3kernel/testfiles/m3expl003.ptex.tlg
+++ b/l3kernel/testfiles/m3expl003.ptex.tlg
@@ -35,6 +35,7 @@ Defining \__color_backend_select_rgb:n on line ...
 Defining \__color_backend_select:n on line ...
 Defining \__color_backend_reset: on line ...
 Defining \__color_backend_select_separation:nn on line ...
+Defining \__color_backend_select_devicen:nn on line ...
 Defining \__color_backend_separation_init:nnnnn on line ...
 Defining \exp_args:Nnxx on line ...
 Defining \__color_backend_separation_init:nxxnn on line ...
@@ -51,6 +52,7 @@ Defining \__color_backend_separation_init:w on line ...
 Defining \__color_backend_separation_init:n on line ...
 Defining \__color_backend_separation_init:nw on line ...
 Defining \__color_backend_separation_init_CIELAB:nnn on line ...
+Defining \__color_backend_devicen_init:nnn on line ...
 Defining \__color_backend_fill_cmyk:n on line ...
 Defining \__color_backend_fill_gray:n on line ...
 Defining \__color_backend_fill_rgb:n on line ...
@@ -59,6 +61,8 @@ Defining \__color_backend_stroke_gray:n on line ...
 Defining \__color_backend_stroke_rgb:n on line ...
 Defining \__color_backend_fill_separation:nn on line ...
 Defining \__color_backend_stroke_separation:nn on line ...
+Defining \__color_backend_fill_devicen:nn on line ...
+Defining \__color_backend_stroke_devicen:nn on line ...
 Defining \__box_backend_clip:N on line ...
 Defining \__box_backend_rotate:Nn on line ...
 Defining \__box_backend_rotate_aux:Nn on line ...
diff --git a/l3kernel/testfiles/m3expl003.tlg b/l3kernel/testfiles/m3expl003.tlg
index c2329c9e8..1dc04ecb1 100644
--- a/l3kernel/testfiles/m3expl003.tlg
+++ b/l3kernel/testfiles/m3expl003.tlg
@@ -36,9 +36,13 @@ Defining \__color_backend_select_rgb:n on line ...
 Defining \__color_backend_select:n on line ...
 Defining \__color_backend_reset: on line ...
 Defining \__color_backend_select_separation:nn on line ...
+Defining \__color_backend_select_devicen:nn on line ...
 Defining \__color_backend_separation_init:nnnnn on line ...
 Defining \__color_backend_separation_init:n on line ...
 Defining \__color_backend_separation_init_CIELAB:nnn on line ...
+Defining \__color_backend_devicen_init:nnn on line ...
+Defining \__color_backend_devicen_init:w on line ...
+Defining \__color_backend_devicen_init:n on line ...
 Defining \__color_backend_fill_cmyk:n on line ...
 Defining \__color_backend_fill_gray:n on line ...
 Defining \__color_backend_fill_rgb:n on line ...
@@ -47,6 +51,8 @@ Defining \__color_backend_stroke_gray:n on line ...
 Defining \__color_backend_stroke_rgb:n on line ...
 Defining \__color_backend_fill_separation:nn on line ...
 Defining \__color_backend_stroke_separation:nn on line ...
+Defining \__color_backend_fill_devicen:nn on line ...
+Defining \__color_backend_stroke_devicen:nn on line ...
 Defining \__box_backend_clip:N on line ...
 Defining \__box_backend_rotate:Nn on line ...
 Defining \__box_backend_rotate_aux:Nn on line ...
diff --git a/l3kernel/testfiles/m3expl003.uptex.tlg b/l3kernel/testfiles/m3expl003.uptex.tlg
index ba9930c14..4d3d1ed8d 100644
--- a/l3kernel/testfiles/m3expl003.uptex.tlg
+++ b/l3kernel/testfiles/m3expl003.uptex.tlg
@@ -35,6 +35,7 @@ Defining \__color_backend_select_rgb:n on line ...
 Defining \__color_backend_select:n on line ...
 Defining \__color_backend_reset: on line ...
 Defining \__color_backend_select_separation:nn on line ...
+Defining \__color_backend_select_devicen:nn on line ...
 Defining \__color_backend_separation_init:nnnnn on line ...
 Defining \exp_args:Nnxx on line ...
 Defining \__color_backend_separation_init:nxxnn on line ...
@@ -51,6 +52,7 @@ Defining \__color_backend_separation_init:w on line ...
 Defining \__color_backend_separation_init:n on line ...
 Defining \__color_backend_separation_init:nw on line ...
 Defining \__color_backend_separation_init_CIELAB:nnn on line ...
+Defining \__color_backend_devicen_init:nnn on line ...
 Defining \__color_backend_fill_cmyk:n on line ...
 Defining \__color_backend_fill_gray:n on line ...
 Defining \__color_backend_fill_rgb:n on line ...
@@ -59,6 +61,8 @@ Defining \__color_backend_stroke_gray:n on line ...
 Defining \__color_backend_stroke_rgb:n on line ...
 Defining \__color_backend_fill_separation:nn on line ...
 Defining \__color_backend_stroke_separation:nn on line ...
+Defining \__color_backend_fill_devicen:nn on line ...
+Defining \__color_backend_stroke_devicen:nn on line ...
 Defining \__box_backend_clip:N on line ...
 Defining \__box_backend_rotate:Nn on line ...
 Defining \__box_backend_rotate_aux:Nn on line ...
diff --git a/l3kernel/testfiles/m3expl003.xetex.tlg b/l3kernel/testfiles/m3expl003.xetex.tlg
index 72681207d..13b2e496a 100644
--- a/l3kernel/testfiles/m3expl003.xetex.tlg
+++ b/l3kernel/testfiles/m3expl003.xetex.tlg
@@ -32,9 +32,13 @@ Defining \__color_backend_reset: on line ...
 Defining \__color_backend_pickup:N on line ...
 Defining \__color_backend_select_separation:nn on line ...
 Defining \__color_backend_select:n on line ...
+Defining \__color_backend_select_devicen:nn on line ...
 Defining \__color_backend_separation_init:nnnnn on line ...
 Defining \__color_backend_separation_init:n on line ...
 Defining \__color_backend_separation_init_CIELAB:nnn on line ...
+Defining \__color_backend_devicen_init:nnn on line ...
+Defining \__color_backend_devicen_init:w on line ...
+Defining \__color_backend_devicen_init:n on line ...
 Defining \__color_backend_fill_cmyk:n on line ...
 Defining \__color_backend_fill_gray:n on line ...
 Defining \__color_backend_fill_rgb:n on line ...
@@ -43,6 +47,8 @@ Defining \__color_backend_stroke_gray:n on line ...
 Defining \__color_backend_stroke_rgb:n on line ...
 Defining \__color_backend_fill_separation:nn on line ...
 Defining \__color_backend_stroke_separation:nn on line ...
+Defining \__color_backend_fill_devicen:nn on line ...
+Defining \__color_backend_stroke_devicen:nn on line ...
 Defining \__box_backend_clip:N on line ...
 Defining \__box_backend_rotate:Nn on line ...
 Defining \__box_backend_rotate_aux:Nn on line ...





More information about the latex3-commits mailing list.