% \iffalse meta-comment % %% File: l3backend-color.dtx % % Copyright (C) 2019,2020 The LaTeX3 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} % % \fi % % \title{^^A % The \textsf{l3backend-color} package\\ Backend color support^^A % } % % \author{^^A % The \LaTeX3 Project\thanks % {^^A % E-mail: % \href{mailto:latex-team@latex-project.org} % {latex-team@latex-project.org}^^A % }^^A % } % % \date{Released 2020-08-07} % % \maketitle % % \begin{documentation} % % \end{documentation} % % \begin{implementation} % % \section{\pkg{l3backend-color} Implementation} % % \begin{macrocode} %<*package> %<@@=color> % \end{macrocode} % % Color support is split into two parts: a \enquote{general} concept and % one directly linked to drawings (or rather the split between filling % and stroking). General color is relatively easy to handle: we have a color % stack available with all modern drivers, and can use that. % Whilst \texttt{(x)dvipdfmx} does have its own approach to color specials, % it is easier to use \texttt{dvips}-like ones for all cases except direct % PDF output. % % \subsection{\texttt{dvips}-style} % % \begin{macrocode} %<*dvisvgm|dvipdfmx|dvips|xdvipdfmx> % \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{macro} % { % \@@_backend_select_cmyk:n , % \@@_backend_select_gray:n , % \@@_backend_select_rgb:n , % \@@_backend_select:n % } % \begin{macro}{\@@_backend_reset:} % \begin{macro}{color.sc, color.fc} % 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 } \__kernel_backend_postscript:n { /color.fc~ { ~ } ~def } % \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{macro}{\@@_backend_select_separation:nn} % \begin{macrocode} \cs_new_protected:Npn \@@_backend_select_separation:nn #1#2 { \@@_backend_select:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \subsection{\texttt{pdfmode}} % % \begin{macrocode} %<*pdfmode> % \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{variable}{\l__kernel_color_stack_int} % \pdfTeX{} and \LuaTeX{} have multiple stacks available, and to track % which one is in use a variable is required. % \begin{macrocode} \int_new:N \l__kernel_color_stack_int % \end{macrocode} % \end{variable} % % \begin{macro} % { % \@@_backend_select_cmyk:n , % \@@_backend_select_gray:n , % \@@_backend_select_rgb:n % } % \begin{macro}{\@@_backend_reset:} % Simply dump the data, but allowing for \LuaTeX{}. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_select_cmyk:n #1 { \@@_backend_select:n { #1 ~ k ~ #1 ~ K } } \cs_new_protected:Npn \@@_backend_select_gray:n #1 { \@@_backend_select:n { #1 ~ g ~ #1 ~ G } } \cs_new_protected:Npn \@@_backend_select_rgb:n #1 { \@@_backend_select:n { #1 ~ rg ~ #1 ~ RG } } \cs_new_protected:Npx \@@_backend_select:n #1 { \cs_if_exist:NTF \tex_pdfextension:D { \tex_pdfextension:D colorstack } { \tex_pdfcolorstack:D } \exp_not:N \l__kernel_color_stack_int push {#1} \group_insert_after:N \exp_not:N \@@_backend_reset: } \cs_new_protected:Npx \@@_backend_reset: { \cs_if_exist:NTF \tex_pdfextension:D { \tex_pdfextension:D colorstack } { \tex_pdfcolorstack:D } \exp_not:N \l__kernel_color_stack_int pop \scan_stop: } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\@@_backend_select_separation:nn} % \begin{macrocode} \cs_new_protected:Npn \@@_backend_select_separation:nn #1#2 { \@@_backend_select:n { /#1 ~ cs ~ /#1 ~ CS ~ #2 ~ scn ~ #2 ~ SCN } } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \subsection{Fill and stroke color} % % Here, \texttt{(x)dvipdfmx} follows \texttt{pdfmode}, 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|pdfmode|xdvipdfmx> % \end{macrocode} % % \begin{macro} % { % \@@_backend_fill_cmyk:n , % \@@_backend_fill_gray:n , % \@@_backend_fill_rgb:n , % \@@_backend_stroke_cmyk:n , % \@@_backend_stroke_gray:n , % \@@_backend_stroke_rgb:n % } % Drawing (fill/stroke) color is handled in \texttt{(x)dvipdfmx} in the % same way as \texttt{pdfmode}. 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 { \__kernel_backend_literal_pdf:n { #1 ~ k } } \cs_new_protected:Npn \@@_backend_fill_gray:n #1 { \__kernel_backend_literal_pdf:n { #1 ~ g } } \cs_new_protected:Npn \@@_backend_fill_rgb:n #1 { \__kernel_backend_literal_pdf:n { #1 ~ rg } } \cs_new_protected:Npn \@@_backend_stroke_cmyk:n #1 { \__kernel_backend_literal_pdf:n { #1 ~ K } } \cs_new_protected:Npn \@@_backend_stroke_gray:n #1 { \__kernel_backend_literal_pdf:n { #1 ~ G } } \cs_new_protected:Npn \@@_backend_stroke_rgb:n #1 { \__kernel_backend_literal_pdf:n { #1 ~ RG } } % \end{macrocode} % \end{macro} % % \begin{macro} % { % \@@_backend_fill_separation:nn, % \@@_backend_stroke_separation: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 } } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \begin{macrocode} %<*dvips> % \end{macrocode} % % \begin{macro} % { % \@@_backend_fill_cmyk:n , % \@@_backend_fill_gray:n , % \@@_backend_fill_rgb:n , % \@@_backend_stroke_cmyk:n , % \@@_backend_stroke_gray:n , % \@@_backend_stroke_rgb:n % } % All questions of saving the non-stacked data. % \begin{macrocode} \cs_new_protected:Npn \@@_backend_fill_cmyk:n #1 { \__kernel_backend_postscript:n { /color.fc { #1 ~ setcmykcolor } def } } \cs_new_protected:Npn \@@_backend_fill_gray:n #1 { \__kernel_backend_postscript:n { /color.fc { #1 ~ setgray } def } } \cs_new_protected:Npn \@@_backend_fill_rgb:n #1 { \__kernel_backend_postscript:n { /color.fc { #1 ~ setrgbcolor } def } } \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 % } % \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 } } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \begin{macrocode} %<*dvisvgm> % \end{macrocode} % % \begin{macro} % { % \@@_backend_fill_cmyk:n , % \@@_backend_stroke_cmyk:n % } % \begin{macro}{\@@_backend_cmyk:nw} % \begin{macro} % { % \@@_backend_fill_gray:n , % \@@_backend_stroke_gray:n % } % \begin{macro}{\@@_backend_gray:nn, \@@_backend_gray_aux:n} % \begin{macro} % { % \@@_backend_fill_rgb:n , % \@@_backend_stroke_rgb:n % } % \begin{macro}{\@@_backend_rgb:nw} % \begin{macro}{\@@_backend:nnnn} % For drawings in SVG, we use scopes for all 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_fill_cmyk:n #1 { \@@_backend_cmyk:nw { fill } #1 \s_@@_stop } \cs_new_protected:Npn \@@_backend_stroke_cmyk:n #1 { \@@_backend_cmyk:nw { stroke } #1 \s_@@_stop } \cs_new_protected:Npn \@@_backend_cmyk:nw #1#2 ~ #3 ~ #4 ~ #5 \s_@@_stop { \use:x { \@@_backend:nnnn {#1} { \fp_eval:n { -100 * ( 1 - min ( 1 , #2 + #5 ) ) } } { \fp_eval:n { -100 * ( 1 - min ( 1 , #3 + #5 ) ) } } { \fp_eval:n { -100 * ( 1 - min ( 1 , #4 + #5 ) ) } } } } \cs_new_protected:Npn \@@_backend_fill_gray:n #1 { \@@_backend_grab:nn { fill } {#1} } \cs_new_protected:Npn \@@_backend_stroke_gray:n #1 { \@@_backend_grab:nn { stroke } {#1} } \cs_new_protected:Npn \@@_backend_gray:nn #1#2 { \use:x { \@@_backend_gray_aux:nn {#1} { \fp_eval:n { 100 * (#2) } } } } \cs_new_protected:Npn \@@_backend_gray_aux:nn #1#2 { \@@_backend:nnn {#1} {#2} {#2} {#2} } \cs_new_protected:Npn \@@_backend_fill_rgb:n #1 { \@@_backend_rgb:nw { fill } #1 \s_@@_stop } \cs_new_protected:Npn \@@_backend_stroke_rgb:n #1 { \@@_backend_rgb:nw { stroke } #1 \s_@@_stop } \cs_new_protected:Npn \@@_backend_rgb:nw #1#2 ~ #3 ~ #4\s_@@_stop { \use:x { \@@_backend:nnnn { fill } { \fp_eval:n { 100 * (#2) } } { \fp_eval:n { 100 * (#3) } } { \fp_eval:n { 100 * (#4) } } } } \cs_new_protected:Npx \@@_backend:nnnn #1#2#3#4 { \__kernel_backend_scope:n { #1 = " rgb ( #2 \c_percent_str , #3 \c_percent_str , #4 \c_percent_str ) " } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro} % { % \@@_backend_fill_separation:nn, % \@@_backend_stroke_separation: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 { } % \end{macrocode} % \end{macro} % % \begin{macrocode} % % \end{macrocode} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex