% \iffalse meta-comment
%
%% File: l3backend-color.dtx
%
% Copyright (C) 2019-2021 The LaTeX Project
%
% It may be distributed and/or modified under the conditions of the
% LaTeX Project Public License (LPPL), either version 1.3c of this
% license or (at your option) any later version.  The latest version
% of this license is in the file
%
%    https://www.latex-project.org/lppl.txt
%
% This file is part of the "l3backend bundle" (The Work in LPPL)
% and all files in that bundle must be distributed together.
%
% -----------------------------------------------------------------------
%
% The development version of the bundle can be found at
%
%    https://github.com/latex3/latex3
%
% for those people who are interested.
%
%<*driver>
\documentclass[full,kernel]{l3doc}
\begin{document}
  \DocInput{\jobname.dtx}
\end{document}
%</driver>
% \fi
%
% \title{^^A
%   The \textsf{l3backend-color} package\\ Backend color support^^A
% }
%
% \author{^^A
%  The \LaTeX{} Project\thanks
%    {^^A
%      E-mail:
%        \href{mailto:latex-team@latex-project.org}
%          {latex-team@latex-project.org}^^A
%    }^^A
% }
%
% \date{Released 2020-01-29}
%
% \maketitle
%
% \begin{documentation}
%
% \end{documentation}
%
% \begin{implementation}
%
% \section{\pkg{l3backend-color} Implementation}
%
%    \begin{macrocode}
%<*package>
%<@@=color>
%    \end{macrocode}
%
% Color support is split into parts: collecting data from \LaTeXe{}, the color stack, general color, separations, and color for
% drawings. We have different approaches in each backend, and have some choices
% to make about \texttt{dvipdfmx}/\XeTeX{} in particular. Whilst it is in some ways
% convenient to use the same approach in multiple backends, the fact that
% \texttt{dvipdfmx}/\XeTeX{} is PDF-based means it (largely) sticks closer to
% direct PDF output.
%
% \subsection{Collecting information from \LaTeXe{}}
%
% \subsubsection{\texttt{dvips}-style}
%
%    \begin{macrocode}
%<*dvisvgm|dvipdfmx|dvips|xetex>
%    \end{macrocode}
%
% \begin{macro}{\@@_backend_pickup:N}
% \begin{macro}{\@@_backend_pickup:w}
%   Allow for \LaTeXe{} color. Here, the possible input values are limited:
%   \texttt{dvips}-style colors can mainly be taken as-is with the exception
%   spot ones (here we need a model and a tint). The \texttt{x}-type expansion
%   is there to cover the case where \pkg{xcolor} is in use.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_pickup:N #1 { }
\cs_if_exist:cT { ver@color.sty }
  {
    \cs_set_protected:Npn \@@_backend_pickup:N #1
      {
        \exp_args:NV \tl_if_head_is_space:nTF \current@color
          {
            \tl_set:Nx #1
               {
                 { \exp_after:wN \use:n \current@color }
                 { 1 }
               }
          }
          {
            \exp_last_unbraced:Nx \@@_backend_pickup:w
              { \current@color } \s_@@_stop #1
          }
      }
    \cs_new_protected:Npn \@@_backend_pickup:w #1 ~ #2 \s_@@_stop #3
      { \tl_set:Nn #3 { {#1} {#2} } }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
%</dvisvgm|dvipdfmx|dvips|xetex>
%    \end{macrocode}
%
% \subsubsection{\LuaTeX{} and \pdfTeX{}}
%
%    \begin{macrocode}
%<*luatex|pdftex>
%    \end{macrocode}
%
% \begin{macro}{\@@_backend_pickup:N}
% \begin{macro}{\@@_backend_pickup:w}
%   The current color in driver-dependent format: pick up the package-mode
%   data if available. We end up converting back and forward in this route as
%   we store our color data in \texttt{dvips} format.
%   The \tn{current@color} needs to be \texttt{x}-expanded before
%   \cs{@@_backend_pickup:w} breaks it apart, because for instance
%   \pkg{xcolor} sets it to be instructions to generate a color
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_pickup:N #1 { }
\cs_if_exist:cT { ver@color.sty }
  {
    \cs_set_protected:Npn \@@_backend_pickup:N #1
      {
        \exp_last_unbraced:Nx \@@_backend_pickup:w
          { \current@color } ~ 0 ~ 0 ~ 0 \s_@@_stop #1
      }
    \cs_new_protected:Npn \@@_backend_pickup:w
      #1 ~ #2 ~ #3 ~ #4 ~ #5 ~ #6 \s_@@_stop #7
      {
        \str_if_eq:nnTF {#2} { g }
          { \tl_set:Nn #7 { { gray } {#1} } }
          {
            \str_if_eq:nnTF {#4} { rg }
              { \tl_set:Nn #7 { { rgb } { #1 ~ #2 ~ #3 } } }
              {
                 \str_if_eq:nnTF {#5} { k }
                   { \tl_set:Nn #7 { { cmyk } { #1 ~ #2 ~ #3 ~ #4 } } }
                   {
                     \str_if_eq:nnTF {#2} { cs }
                       {
                         \tl_set:Nx #7 { { \use:n #1 } { #5 } }
                       }
                       {
                         \tl_set:Nn #7 { { gray } { 0 } }
                       }
                   }
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
%</luatex|pdftex>
%    \end{macrocode}
%
% \subsection{The color stack}
%
% For PDF-based engines, we have a color stack available inside the specials.
% This is used for concepts beyond color itself: it is needed to manage th graphics
% state generally. The exact form depends on the engine, and for
% \texttt{dvipdfmx}/\XeTeX{} the backend version.
%
% \subsubsection{Common code}
%
%    \begin{macrocode}
%<*dvipdfmx|luatex|pdftex|xetex>
%    \end{macrocode}
%
% \begin{variable}{\l_@@_backend_stack_int}
%   \pdfTeX{}, \LuaTeX{} and recent \texttt{(x)dvipdfmx} have multiple stacks
%   available, and to track which one is in use a variable is required.
%    \begin{macrocode}
\int_new:N \l_@@_backend_stack_int
%    \end{macrocode}
% \end{variable}
%
%    \begin{macrocode}
%</dvipdfmx|luatex|pdftex|xetex>
%    \end{macrocode}
%
% \subsubsection{\texttt{dvipdfmx}/\XeTeX{}}
%
%    \begin{macrocode}
%<*dvipdfmx|xetex>
%    \end{macrocode}
%
% \begin{macro}{\__kernel_color_backend_stack_init:Nnn}
% \begin{variable}
%   {\g_@@_backend_stack_int, \c_@@_backend_main_stack_int}
%   In \texttt{(x)dvipdfmx}, the base color stack is not set up, so we have to
%   force that, as well as providing a mechanism more generally.
%    \begin{macrocode}
\int_compare:nNnTF \c__kernel_sys_dvipdfmx_version_int < { 20201111 }
  { \cs_new_protected:Npn \__kernel_color_backend_stack_init:Nnn #1#2#3 { } }
  {
    \int_new:N \g_@@_backend_stack_int
    \cs_new_protected:Npx \__kernel_color_backend_stack_init:Nnn #1#2#3
      {
        \int_gincr:N \exp_not:N \g_@@_backend_stack_int
        \int_const:Nn #1 { \exp_not:N \g_@@_backend_stack_int }
        \cs_if_exist:NTF \AtBeginDvi
          { \exp_not:N \AtBeginDvi }
          { \exp_not:N \use:n }
          {
            \__kernel_backend_literal:x
              {
                pdfcolorstackinit ~
                \exp_not:N \int_use:N \exp_not:N \g_@@_backend_stack_int
                \c_space_tl
                \exp_not:N \tl_if_blank:nF {#2} { #2 ~ }
                (#3)
              }
          }
      }
    \cs_if_exist:cTF { main@pdfcolorstack }
      {
        \int_set:Nn \l_@@_backend_stack_int
          { \int_use:c { main@pdfcolorstack } }
      }
      {
        \__kernel_color_backend_stack_init:Nnn \c_@@_backend_main_stack_int
          { page ~ direct } { 0 ~ g ~ 0 ~ G }
        \int_set_eq:NN \l_@@_backend_stack_int
          \c_@@_backend_main_stack_int
      }
  }
%    \end{macrocode}
% \end{variable}
% \end{macro}
%
% \begin{macro}{\@@_backend_stack_push:nn, \@@_backend_stack_push:nx}
% \begin{macro}{\@@_backend_stack_pop:n}
%   Simple enough but needs a version check.
%    \begin{macrocode}
\int_compare:nNnF \c__kernel_sys_dvipdfmx_version_int < { 20201111 }
  {
    \cs_new_protected:Npn \@@_backend_stack_push:nn #1#2
      {
        \__kernel_backend_literal:x
          {
            pdfcolorstack ~
            \int_eval:n {#1} ~
            push ~ (#2)
          }
      }
    \cs_generate_variant:Nn \@@_backend_stack_push:nn { nx }
    \cs_new_protected:Npn \@@_backend_stack_pop:n #1
      {
        \__kernel_backend_literal:x
          {
            pdfcolorstack ~
            \int_eval:n {#1} ~
            pop
          }
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
%</dvipdfmx|xetex>
%    \end{macrocode}
%
% \subsubsection{\LuaTeX and \pdfTeX{}}
%
%    \begin{macrocode}
%<*luatex|pdftex>
%    \end{macrocode}
%
% \begin{macro}{\__kernel_color_backend_stack_init:Nnn}
%    \begin{macrocode}
\cs_new_protected:Npn \__kernel_color_backend_stack_init:Nnn #1#2#3
  {
    \int_const:Nn #1
      {
%<*luatex>
        \tex_pdffeedback:D colorstackinit ~
%</luatex>
%<*pdftex>
        \tex_pdfcolorstackinit:D
%</pdftex>
        \tl_if_blank:nF {#2} { #2 ~ }
        {#3}
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_backend_stack_push:nn, \@@_backend_stack_push:nx}
% \begin{macro}{\@@_backend_stack_pop:n}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_stack_push:nn #1#2
  {
%<*luatex>
    \tex_pdfextension:D colorstack ~
%</luatex>
%<*pdftex>
    \tex_pdfcolorstack:D
%</pdftex>
      \int_eval:n {#1} ~ push ~ {#2}
  }
\cs_generate_variant:Nn \@@_backend_stack_push:nn { nx }
\cs_new_protected:Npn \@@_backend_stack_pop:n #1
  {
%<*luatex>
    \tex_pdfextension:D colorstack ~
%</luatex>
%<*pdftex>
    \tex_pdfcolorstack:D
%</pdftex>
      \int_eval:n {#1} ~ pop \scan_stop:
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
%</luatex|pdftex>
%    \end{macrocode}
%
% \subsection{General color}
%
% \subsubsection{\texttt{dvips}-style}
%
%    \begin{macrocode}
%<*dvips|dvisvgm>
%    \end{macrocode}
%
% \begin{macro}
%   {
%     \@@_backend_select_cmyk:n  ,
%     \@@_backend_select_gray:n  ,
%     \@@_backend_select_rgb:n   ,
%     \@@_backend_select:n
%   }
% \begin{macro}{\@@_backend_reset:}
% \begin{macro}{color.sc}
%    Push the data to the stack. In the case of \texttt{dvips} also saves the
%    drawing color in raw PostScript.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_select_cmyk:n #1
  { \@@_backend_select:n { cmyk ~ #1 } }
\cs_new_protected:Npn \@@_backend_select_gray:n #1
  { \@@_backend_select:n { gray ~ #1 } }
\cs_new_protected:Npn \@@_backend_select_rgb:n #1
  { \@@_backend_select:n { rgb ~ #1 } }
\cs_new_protected:Npn \@@_backend_select:n #1
  {
    \__kernel_backend_literal:n { color~push~ #1 }
%<*dvips>
    \__kernel_backend_postscript:n { /color.sc ~ { } ~ def }
%</dvips>
    \group_insert_after:N \@@_backend_reset:
  }
\cs_new_protected:Npn \@@_backend_reset:
  { \__kernel_backend_literal:n { color~pop } }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
%</dvips|dvisvgm>
%    \end{macrocode}
%
% \subsubsection{\LuaTeX{} and \pdfTeX{}}
%
%    \begin{macrocode}
%<*dvipdfmx|luatex|pdftex|xetex>
%    \end{macrocode}
%
% \begin{variable}{\l_@@_backend_fill_tl, \l_@@_backend_stroke_tl}
%    \begin{macrocode}
\tl_new:N \l_@@_backend_fill_tl
\tl_new:N \l_@@_backend_stroke_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}
%   {
%     \@@_backend_select_cmyk:n ,
%     \@@_backend_select_gray:n ,
%     \@@_backend_select_rgb:n
%   }
% \begin{macro}{\@@_backend_select:nn}
% \begin{macro}{\@@_backend_reset:}
%   Store the values then pass to the stack.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_select_cmyk:n #1
  { \@@_backend_select:nn { #1 ~ k } { #1 ~ K } }
\cs_new_protected:Npn \@@_backend_select_gray:n #1
  { \@@_backend_select:nn { #1 ~ g } { #1 ~ G } }
\cs_new_protected:Npn \@@_backend_select_rgb:n #1
  { \@@_backend_select:nn { #1 ~ rg } { #1 ~ RG } }
\cs_new_protected:Npn \@@_backend_select:nn #1#2
  {
    \tl_set:Nn \l_@@_backend_fill_tl {#1}
    \tl_set:Nn \l_@@_backend_stroke_tl {#2}
    \@@_backend_stack_push:nn \l_@@_backend_stack_int { #1 ~ #2 }
    \group_insert_after:N \@@_backend_reset:
  }
\cs_new_protected:Npn \@@_backend_reset:
  { \@@_backend_stack_pop:n \l_@@_backend_stack_int }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
%</dvipdfmx|luatex|pdftex|xetex>
%    \end{macrocode}
%
% \subsubsection{\texttt{dvipmdfx}/\XeTeX{}}
%
%    \begin{macrocode}
%<*dvipdfmx|xetex>
%    \end{macrocode}
%
% These backends have the most possible approaches: it recognises both
% \texttt{dvips}-based color specials and it's own format, plus one can
% include PDF statements directly. Recent releases also have a color stack
% approach similar to \pdfTeX{}. Of the stack methods, the dedicated
% the most versatile is the latter as it can cover all of the use cases
% we have. Thus it is used in preference to the \texttt{dvips}-style interface
% or the \enquote{native} color specials (which have only one stack).
%
% \begin{macro}
%   {
%     \@@_backend_select_cmyk:n  ,
%     \@@_backend_select_gray:n  ,
%     \@@_backend_select_rgb:n
%   }
% \begin{macro}{\@@_backend_reset:}
%    Push the data to the stack.
%    \begin{macrocode}
\int_compare:nNnT \c__kernel_sys_dvipdfmx_version_int < { 20201111 }
  {
    \cs_gset_protected:Npn \@@_backend_select_cmyk:n #1
      {
        \__kernel_backend_literal:n { pdf: bc ~ [#1] }
        \group_insert_after:N \@@_backend_reset:
      }
    \cs_gset_eq:NN \@@_backend_select_gray:n \@@_backend_select_cmyk:n
    \cs_gset_eq:NN \@@_backend_select_rgb:n \@@_backend_select_cmyk:n
    \cs_gset_protected:Npn \@@_backend_reset:
      { \__kernel_backend_literal:n { pdf: ec } }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
%</dvipdfmx|xetex>
%    \end{macrocode}
%
% \subsection{Separations}
%
% Here, life gets interesting and we need essentially one approach per
% backend.
%
%    \begin{macrocode}
%<*dvips>
%    \end{macrocode}
%
% \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}
%
% \begin{macro}
%   {
%     \@@_backend_separation_init:nnnnn,
%     \@@_backend_separation_init:nxxnn,
%     \@@_backend_separation_init_aux:nnnnn
%   }
% \begin{macro}[EXP]
%   {
%     \@@_backend_separation_init_/DeviceCMYK:nnn ,
%     \@@_backend_separation_init_/DeviceGray:nnn ,
%     \@@_backend_separation_init_/DeviceRGB:nnn
%   }
% \begin{macro}[EXP]{\@@_backend_separation_init_Device:Nn}
% \begin{macro}[EXP]{\@@_backend_separation_init:nnn}
% \begin{macro}[EXP]{\@@_backend_separation_init_count:n}
% \begin{macro}[EXP]{\@@_backend_separation_init_count:w}
% \begin{macro}[EXP]{\@@_backend_separation_init:nnnn}
% \begin{macro}[EXP]{\@@_backend_separation_init:w}
% \begin{macro}[EXP]{\@@_backend_separation_init:n}
% \begin{macro}[EXP]{\@@_backend_separation_init:nw}
% \begin{macro}{\@@_backend_separation_init_CIELAB:nnn}
%   Initialising here means creating a small header set up plus massaging
%   some data. This comes about as we have to deal with PDF-focussed data,
%   which makes most sense \enquote{higher-up}. The approach is based on
%   ideas from \url{https://tex.stackexchange.com/q/560093} plus using
%   the PostScript manual for other aspects.
%    \begin{macrocode}
\cs_new_protected:Npx \@@_backend_separation_init:nnnnn #1#2#3#4#5
  {
    \bool_if:NT \g__kernel_backend_header_bool
      {
        \cs_if_exist:NTF \AtBeginDvi
          { \exp_not:N \AtBeginDvi }
          { \use:n }
            {
              \exp_not:N \@@_backend_separation_init_aux:nnnnn
                {#1} {#2} {#3} {#4} {#5}
            }
      }
  }
\cs_generate_variant:Nn \@@_backend_separation_init:nnnnn { nxx }
\cs_new_protected:Npn \@@_backend_separation_init_aux:nnnnn #1#2#3#4#5
  {
    \__kernel_backend_literal:e
      {
        !
        TeXDict ~ begin ~
        /color \int_use:N \g_@@_model_int
          {
            [ ~
              /Separation ~ ( \str_convert_pdfname:n {#1} ) ~
              [ ~ #2 ~ ] ~
                {
                  \cs_if_exist_use:cF { @@_backend_separation_init_ #2 :nnn }
                    { \@@_backend_separation_init:nnn }
                      {#3} {#4} {#5}
                }
            ] ~ setcolorspace
          } ~ def ~
        end
      }
  }
\cs_new:cpn { @@_backend_separation_init_ /DeviceCMYK :nnn } #1#2#3
  { \@@_backend_separation_init_Device:Nn 4 {#3} }
\cs_new:cpn { @@_backend_separation_init_ /DeviceGray :nnn } #1#2#3
  { \@@_backend_separation_init_Device:Nn 1 {#3} }
\cs_new:cpn { @@_backend_separation_init_ /DeviceRGB :nnn } #1#2#3
  { \@@_backend_separation_init_Device:Nn 2 {#3} }
\cs_new:Npn \@@_backend_separation_init_Device:Nn #1#2
  {
    #2 ~
    \prg_replicate:nn {#1}
      { #1 ~ index ~ mul ~ #1 ~ 1 ~ roll ~ }
    \int_eval:n { #1 + 1 } ~ -1 ~ roll ~ pop
  }
%    \end{macrocode}
%   For the generic case, we cannot use |/FunctionType 2| unfortunately, so
%   we have to code that idea up in PostScript. Here, we will therefore assume
%   that a range is \emph{always} given. First, we count values in each argument:
%   at the backend level, we can assume there are always well-behaved with
%   spaces present.
%    \begin{macrocode}
\cs_new:Npn \@@_backend_separation_init:nnn #1#2#3
  {
   \exp_args:Ne \@@_backend_separation_init:nnnn
     { \@@_backend_separation_init_count:n {#2} }
     {#1} {#2} {#3}
  }
\cs_new:Npn \@@_backend_separation_init_count:n #1
  { \int_eval:n { 0 \@@_backend_separation_init_count:w #1 ~ \s_@@_stop } }
\cs_new:Npn \@@_backend_separation_init_count:w #1 ~ #2 \s_@@_stop
  {
    +1
    \tl_if_blank:nF {#2}
      { \@@_backend_separation_init_count:w #2 \s_@@_stop }
  }
%    \end{macrocode}
%   Now we implement the algorithm. In the terms in the PostScript manual,
%   we have $\mathbf{N} = 1$ and $\mathbf{Domain} = [0~1]$, with
%   $\mathbf{Range}$ as |#2|, $\mathbf{C0}$ as |#3| and $\mathbf{C1}$
%   as |#4|, with the number of output components in |#1|. So all we have
%   to do is implement $y_{i} = \mathbf{C0}_{i} + x(\mathbf{C1}_{i} -
%   \mathbf{C0}_{i})$ with lots of stack manipulation, then check the
%   ranges. That's done by adding everything to the stack first, then using
%   the fact we know all of the offsets. As manipulating the stack is tricky,
%   we start by re-formatting the $\mathbf{C0}$ and $\mathbf{C1}$ arrays to
%   be interleaved, and add a \texttt{0} to each pair: this is used
%   to keep the stack of constant length while we are doing the first pass of
%   mathematics. We then working through that list, calculating from the
%   last to the first value before tidying up by removing all of the input
%   values. We do that by first copying all of the final $y$ values to the
%   end of the stack, then rolling everything so we can pop the now-unneeded
%   material.
%    \begin{macrocode}
\cs_new:Npn \@@_backend_separation_init:nnnn #1#2#3#4
  {
    \@@_backend_separation_init:w #3 ~ \s_@@_stop #4 ~ \s_@@_stop
    \prg_replicate:nn {#1}
      {
        pop ~ 1 ~ index ~ neg ~ 1 ~ index ~ add ~
        \int_eval:n { 3 * #1 } ~ index ~ mul ~
        2 ~ index ~ add ~
        \int_eval:n { 3 * #1 } ~ #1 ~ roll ~
      }
    \int_step_function:nnnN {#1} { -1 } { 1 }
      \@@_backend_separation_init:n
    \int_eval:n { 4 * #1 + 1 } ~ #1 ~ roll ~
    \prg_replicate:nn { 3 * #1 + 1 } { pop ~ }
    \tl_if_blank:nF {#2}
      { \@@_backend_separation_init:nw {#1} #2 ~ \s_@@_stop }
  }
\cs_new:Npn \@@_backend_separation_init:w
  #1 ~ #2 \s_@@_stop #3 ~ #4 \s_@@_stop
  {
    #1 ~ #3 ~ 0 ~
    \tl_if_blank:nF {#2}
      { \@@_backend_separation_init:w #2 \s_@@_stop #4 \s_@@_stop }
  }
\cs_new:Npn \@@_backend_separation_init:n #1
  { \int_eval:n { #1 * 2 } ~ index ~ }
%    \end{macrocode}
%   Finally, we deal with the range limit if required. This is handled
%   by splitting the range into pairs. It's then just a question of doing
%   the comparisons, this time dropping everything except the desired
%   result.
%    \begin{macrocode}
\cs_new:Npn \@@_backend_separation_init:nw #1#2 ~ #3 ~ #4 \s_@@_stop
  {
     #2 ~ #3 ~
     2 ~ index ~ 2 ~ index ~ lt ~
       { ~ pop ~ exch ~ pop ~ } ~
       { ~
         2 ~ index ~ 1 ~ index ~ gt ~
           { ~ exch ~ pop ~ exch ~ pop ~ } ~
           { ~ pop ~ pop ~ } ~
         ifelse ~
       }
    ifelse ~
    #1 ~ 1 ~ roll ~
    \tl_if_blank:nF {#4}
      { \@@_backend_separation_init:nw {#1} #4 \s_@@_stop }
  }
%    \end{macrocode}
%  CIELAB support uses the detail from the PostScript reference, page 227;
%  other than that block of PostScript, this is the same as for PDF-based
%  routes.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_separation_init_CIELAB:nnn #1#2#3
  {
    \@@_backend_separation_init:nxxnn
      {#2}
      {
        /CIEBasedABC ~
            << ~
              /RangeABC ~ [ ~ \c_@@_model_range_CIELAB_tl \c_space_tl ] ~
              /DecodeABC ~
                [ ~
                  { ~ 16 ~ add ~ 116 ~ div ~ } ~ bind ~
                  { ~ 500 ~ div ~ } ~ bind ~
                  { ~ 200 ~ div ~ } ~ bind ~
                ] ~
              /MatrixABC ~ [ ~ 1 ~ 1 ~ 1 ~ 1 ~ 0 ~ 0 ~ 0 ~ 0 ~ -1 ~ ] ~
              /DecodeLMN ~
                [ ~
                  { ~
                    dup ~ 6 ~ 29 ~ div ~ ge ~
                      { ~ dup ~ dup ~ mul ~ mul ~ ~ } ~
                      { ~ 4 ~ 29 ~ div ~ sub ~ 108 ~ 841 ~ div ~ mul ~ } ~
                    ifelse ~
                    0.9505 ~ mul ~
                  } ~ bind ~
                  { ~
                    dup ~ 6 ~ 29 ~ div ~ ge ~
                      { ~ dup ~ dup ~ mul ~ mul ~ } ~
                      { ~ 4 ~ 29 ~ div ~ sub ~ 108 ~ 841 ~ div ~ mul ~ } ~
                    ifelse ~
                  } ~ bind ~
                  { ~
                    dup ~ 6 ~ 29 ~ div ~ ge ~
                      { ~ dup ~ dup ~ mul ~ mul ~ } ~
                      { ~ 4 ~ 29 ~ div ~ sub ~ 108 ~ 841 ~ div ~ mul ~ } ~
                    ifelse ~
                    1.0890 ~ mul ~
                  } ~ bind
                ] ~
              /WhitePoint ~
                [ ~ \tl_use:c { c_@@_model_whitepoint_CIELAB_ #1 _tl } ~ ] ~
            >>
      }
      { \c_@@_model_range_CIELAB_tl }
      { 100 ~ 0 ~ 0 }
      {#3}
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_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}
%
%    \begin{macrocode}
%<*dvisvgm>
%    \end{macrocode}
%
% \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}
%
% \begin{macro}
%   {\@@_backend_separation_init:nnnnn, \@@_backend_separation_init_CIELAB:nnn}
%   No support at present.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_separation_init:nnnnn #1#2#3#4#5 { }
\cs_new_protected:Npn \@@_backend_separation_init_CIELAB:nnnnnn #1#2#3 { }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%</dvisvgm>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*dvipdfmx|luatex|pdftex|xetex>
%    \end{macrocode}
%
% \begin{macro}{\@@_backend_select_separation:nn, \@@_backend_select_devicen:nn}
% \begin{macro}{\@@_backend_select:n}
%   Although \texttt{(x)dvipdfmx} has a built-in approach to color spaces, that
%   can't be used with the generic color stacks. So we take an approach in which
%   we share the same code as for \pdfTeX{}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_select_separation:nn #1#2
  { \@@_backend_select:nn { /#1 ~ cs ~ #2 ~ scn  } { /#1 ~ CS ~ #2 ~ SCN } }
\cs_new_eq:NN \@@_backend_select_devicen:nn \@@_backend_select_separation:nn
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\@@_backend_separation_init:nnnnn}
% \begin{macro}{\@@_backend_separation_init:n}
% \begin{macro}{\@@_backend_separation_init_CIELAB:nnn}
%   Initialising the PDF structures needs two parts: creating an object
%   containing the \enquote{real} name of the Separation, then adding a reference
%   to that to each page. We use a separate object for the tint transformation
%   following the model in the PDF reference.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_separation_init:nnnnn #1#2#3#4#5
  {
    \pdf_object_now:nx { dict }
      {
        /FunctionType ~ 2
        /Domain ~ [0 ~ 1]
        \tl_if_blank:nF {#3} { /Range ~ [#3] }
        /C0 ~ [#4] ~
        /C1 ~ [#5] /N ~ 1
      }
    \@@_backend_separation_init:n
      {
        /Separation ~
        / \str_convert_pdfname:n {#1} ~ #2 ~
        \pdf_object_last:
      }
    \use:x
      {
        \pdfmanagement_add:nnn
          { Page / Resources / ColorSpace }
          { color \int_use:N \g_@@_model_int }
          { \pdf_object_last: }
      }
  }
\cs_if_exist:NF \pdf_object_now:nn
  { \cs_gset_protected:Npn \@@_backend_separation_init:nnnnn #1#2#3#4#5 { } }
\cs_new_protected:Npn \@@_backend_separation_init:n #1
  {
    \pdf_object_now:nx { array } {#1}
  }
%    \end{macrocode}
%   For CIELAB colors, we need one object per document for the illuminant,
%   plus initialisation of the color space referencing that object.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_separation_init_CIELAB:nnn #1#2#3
  {
    \pdf_object_if_exist:nF { @@_illuminant_CIELAB_ #1 }
      {
        \pdf_object_new:nn { @@_illuminant_CIELAB_ #1 } { array }
        \pdf_object_write:nx { @@_illuminant_CIELAB_ #1 }
          {
            /Lab ~
            <<
             /WhitePoint ~
               [ \tl_use:c { c_@@_model_whitepoint_CIELAB_ #1 _tl } ]
             /Range ~ [ \c_@@_model_range_CIELAB_tl ]
            >>
          }
      }
    \@@_backend_separation_init:nnnnn
      {#2}
      { \pdf_object_ref:n { @@_illuminant_CIELAB_ #1 } }
      { \c_@@_model_range_CIELAB_tl }
      { 100 ~ 0 ~ 0 }
      {#3}
  }
\cs_if_exist:NF \pdf_object_now:nn
  {
    \cs_gset_protected:Npn \@@_backend_separation_init_CIELAB:nnn #1#2#3
      { }
  }
%    \end{macrocode}
% \end{macro}
% \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:
      }
    \use:x
      {
        \pdfmanagement_add:nnn
          { Page / Resources / ColorSpace }
          { color \int_use:N \g_@@_model_int }
          { \pdf_object_last: }
      }
  }
\cs_if_exist:NF \pdf_object_now:nn
  { \cs_gset_protected:Npn \@@_backend_devicen_init:nnn #1#2#3 { } }
\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}
%
%    \begin{macrocode}
%<*dvipdfmx|xetex>
%    \end{macrocode}
%
% \begin{macro}{\@@_backend_select_separation:nn, \@@_backend_select_devicen:nn}
%   For older \texttt{(x)dvipdfmx}, we \emph{could} support separations using a
%   dedicated mechanism, but it was not added that long before the color
%   stacks. So instead of having two complex paths, just disable here.
%    \begin{macrocode}
\int_compare:nNnT \c__kernel_sys_dvipdfmx_version_int < { 20201111 }
  {
    \cs_gset_protected:Npn \@@_backend_select_separation:nn #1#2 { }
    \cs_gset_eq:NN \@@_backend_select_devicen:nn
      \@@_backend_select_separation:nn
  }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
%</dvipdfmx|xetex>
%    \end{macrocode}
%
% \subsection{Fill and stroke color}
%
% Here, \texttt{dvipdfmx}/\XeTeX{} follows \LuaTeX{} and \pdfTeX{},
% while for \texttt{dvips}
% we have to manage fill and stroke color ourselves. We also handle
% \texttt{dvisvgm} independently, as there we can create SVG directly.
%
%    \begin{macrocode}
%<*dvipdfmx|luatex|pdftex|xetex>
%    \end{macrocode}
%
% \begin{macro}
%   {
%     \@@_backend_fill_cmyk:n   ,
%     \@@_backend_fill_gray:n   ,
%     \@@_backend_fill_rgb:n    ,
%     \@@_backend_fill:n        ,
%     \@@_backend_stroke_cmyk:n ,
%     \@@_backend_stroke_gray:n ,
%     \@@_backend_stroke_rgb:n  ,
%     \@@_backend_stroke:n
%   }
%   Drawing (fill/stroke) color is handled in \texttt{dvipdfmx}/\XeTeX{} in the
%   same way as \LuaTeX{}/\pdfTeX{}. We use the same approach as earlier, except the
%   color stack is not involved so the generic direct PDF operation is used.
%   There is no worry about the nature of strokes: everything is handled
%   automatically.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_fill_cmyk:n #1
  { \@@_backend_fill:n { #1 ~ k } }
\cs_new_protected:Npn \@@_backend_fill_gray:n #1
  { \@@_backend_fill:n { #1 ~ g } }
\cs_new_protected:Npn \@@_backend_fill_rgb:n #1
  { \@@_backend_fill:n { #1 ~ rg } }
\cs_new_protected:Npn \@@_backend_fill:n #1
  {
    \tl_set:Nn \l_@@_backend_fill_tl {#1}
    \@@_backend_stack_push:nn \l_@@_backend_stack_int
      { #1 ~ \l_@@_backend_stroke_tl }
    \group_insert_after:N \@@_backend_reset:
  }
\cs_new_protected:Npn \@@_backend_stroke_cmyk:n #1
  { \@@_backend_stroke:n { #1 ~ K } }
\cs_new_protected:Npn \@@_backend_stroke_gray:n #1
  { \@@_backend_stroke:n { #1 ~ G } }
\cs_new_protected:Npn \@@_backend_stroke_rgb:n #1
  { \@@_backend_stroke:n { #1 ~ RG } }
\cs_new_protected:Npn \@@_backend_stroke:n #1
  {
    \tl_set:Nn \l_@@_backend_stroke_tl {#1}
    \@@_backend_stack_push:nn \l_@@_backend_stack_int
      { \l_@@_backend_fill_tl \c_space_tl #1 }
    \group_insert_after:N \@@_backend_reset:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}
%   {
%     \@@_backend_fill_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
  { \@@_backend_fill:n { /#1 ~ cs ~ #2 ~ scn } }
\cs_new_protected:Npn \@@_backend_stroke_separation:nn #1#2
  { \@@_backend_stroke: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}
%
%    \begin{macrocode}
%</dvipdfmx|luatex|pdftex|xetex>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*dvipdfmx|xetex>
%    \end{macrocode}
%
% \begin{macro}
%   {
%     \@@_backend_fill_cmyk:n   ,
%     \@@_backend_fill_gray:n   ,
%     \@@_backend_fill_rgb:n
%   }
% \begin{macro}{\@@_backend_reset:}
% \begin{macro}{\@@_backend_stroke:n}
% \begin{macro}{\@@_backend_fill_separation:nn, \@@_backend_stroke_separation:nn}
%   Deal with older \texttt{(x)dvipdfmx}.
%    \begin{macrocode}
\int_compare:nNnT \c__kernel_sys_dvipdfmx_version_int < { 20201111 }
  {
    \cs_gset_protected:Npn \@@_backend_fill_cmyk:n #1
      {
        \__kernel_backend_literal:n { pdf: bc ~ [#1] }
        \group_insert_after:N \@@_backend_reset:
      }
    \cs_gset_eq:NN \@@_backend_fill_gray:n \@@_backend_fill_cmyk:n
    \cs_gset_eq:NN \@@_backend_fill_rgb:n \@@_backend_fill_cmyk:n
    \cs_gset_protected:Npn \@@_backend_reset:
      { \__kernel_backend_literal:n { pdf: ec } }
    \cs_gset_protected:Npn \@@_backend_stroke:n #1
      { \__kernel_backend_literal:n {#1} }
    \cs_gset_protected:Npn \@@_backend_fill_separation:nn #1#2 { }
    \cs_gset_eq:NN \@@_backend_fill_devicen:nn
      \@@_backend_fill_separation:nn
    \cs_gset_eq:NN \@@_backend_stroke_separation:nn
      \@@_backend_fill_separation:nn
    \cs_gset_eq:NN \@@_backend_stroke_devicen:nn
      \@@_backend_stroke_separation:nn
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
%    \begin{macrocode}
%</dvipdfmx|xetex>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*dvips>
%    \end{macrocode}
%
% \begin{macro}
%   {
%     \@@_backend_fill_cmyk:n   ,
%     \@@_backend_fill_gray:n   ,
%     \@@_backend_fill_rgb:n    ,
%     \@@_backend_fill:n    ,
%     \@@_backend_stroke_cmyk:n ,
%     \@@_backend_stroke_gray:n ,
%     \@@_backend_stroke_rgb:n
%   }
%   Fill color here is the same as general color \emph{except} we skip the
%   stroke part.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_fill_cmyk:n #1
  { \@@_backend_fill:n { cmyk ~ #1 } }
\cs_new_protected:Npn \@@_backend_fill_gray:n #1
  { \@@_backend_fill:n { gray ~ #1 } }
\cs_new_protected:Npn \@@_backend_fill_rgb:n #1
  { \@@_backend_fill:n { rgb ~ #1 } }
\cs_new_protected:Npn \@@_backend_fill:n #1
  {
    \__kernel_backend_literal:n { color~push~ #1 }
    \group_insert_after:N \@@_backend_reset:
  }
\cs_new_protected:Npn \@@_backend_stroke_cmyk:n #1
  { \__kernel_backend_postscript:n { /color.sc { #1 ~ setcmykcolor } def } }
\cs_new_protected:Npn \@@_backend_stroke_gray:n #1
  { \__kernel_backend_postscript:n { /color.sc { #1 ~ setgray } def } }
\cs_new_protected:Npn \@@_backend_stroke_rgb:n #1
  { \__kernel_backend_postscript:n { /color.sc { #1 ~ setrgbcolor } def } }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}
%   {
%     \@@_backend_fill_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
  { \@@_backend_fill:n { separation ~ #1 ~ #2 } }
\cs_new_protected:Npn \@@_backend_stroke_separation:nn #1#2
  { \__kernel_backend_postscript:n { /color.sc { separation ~ #1 ~ #2 } 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}
%
%    \begin{macrocode}
%</dvips>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*dvisvgm>
%    \end{macrocode}
%
% \begin{macro}
%   {
%     \@@_backend_fill_cmyk:n   ,
%     \@@_backend_fill_gray:n   ,
%     \@@_backend_fill_rgb:n    ,
%     \@@_backend_fill:n
%   }
%   Fill color here is the same as general color \emph{except} we skip the
%   stroke part.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_fill_cmyk:n #1
  { \@@_backend_fill:n { cmyk ~ #1 } }
\cs_new_protected:Npn \@@_backend_fill_gray:n #1
  { \@@_backend_fill:n { gray ~ #1 } }
\cs_new_protected:Npn \@@_backend_fill_rgb:n #1
  { \@@_backend_fill:n { rgb ~ #1 } }
\cs_new_protected:Npn \@@_backend_fill:n #1
  {
    \__kernel_backend_literal:n { color~push~ #1 }
    \group_insert_after:N \@@_backend_reset:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_backend_stroke_cmyk:n}
% \begin{macro}{\@@_backend_stroke_cmyk:w}
% \begin{macro}{\@@_backend_stroke_gray:n, \@@_backend_stroke_gray_aux:n}
% \begin{macro}{\@@_backend_stroke_rgb:n}
% \begin{macro}{\@@_backend_stroke_rgb:w}
% \begin{macro}{\@@_backend:nnn}
%   For drawings in SVG, we use scopes for all stroke colors. That
%   requires using \texttt{RGB} values, which luckily are easy to
%   convert here (|cmyk| to |RGB| is a fixed function).
%    \begin{macrocode}
\cs_new_protected:Npn \@@_backend_stroke_cmyk:n #1
  { \@@_backend_cmyk:w #1 \s_@@_stop }
\cs_new_protected:Npn \@@_backend_stroke_cmyk:w
  #1 ~ #2 ~ #3 ~ #4 \s_@@_stop
  {
    \use:x
      {
        \@@_backend:nnn
          { \fp_eval:n { -100 * ( 1 - min ( 1 , #1 + #4 ) ) } }
          { \fp_eval:n { -100 * ( 1 - min ( 1 , #2 + #4 ) ) } }
          { \fp_eval:n { -100 * ( 1 - min ( 1 , #3 + #4 ) ) } }
      }
  }
\cs_new_protected:Npn \@@_backend_stroke_gray:n #1
  {
    \use:x
      {
        \@@_backend_stroke_gray_aux:n
          { \fp_eval:n { 100 * (#1) } }
      }
  }
\cs_new_protected:Npn \@@_backend_stroke_gray_aux:n #1
  { \@@_backend:nnn {#1} {#1} {#1} }
\cs_new_protected:Npn \@@_backend_stroke_rgb:n #1
  { \@@_backend_rgb:w #1 \s_@@_stop }
\cs_new_protected:Npn \@@_backend_stroke_rgb:w
  #1 ~ #2 ~ #3 \s_@@_stop
  {
    \use:x
      {
        \@@_backend:nnn
          { \fp_eval:n { 100 * (#1) } }
          { \fp_eval:n { 100 * (#2) } }
          { \fp_eval:n { 100 * (#3) } }
      }
  }
\cs_new_protected:Npx \@@_backend:nnn #1#2#3
  {
    \__kernel_backend_scope:n
      {
        stroke =
         "
           rgb
             (
               #1 \c_percent_str ,
               #2 \c_percent_str ,
               #3 \c_percent_str
             )
         "
      }
  }
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}
%   {
%     \@@_backend_fill_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_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}
%
%    \begin{macrocode}
%</dvisvgm>
%    \end{macrocode}
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \end{implementation}
%
% \PrintIndex
