diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx | 649 |
1 files changed, 340 insertions, 309 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx b/Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx index 8b5aa7d53c1..242bc86790c 100644 --- a/Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx +++ b/Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx @@ -1,6 +1,6 @@ % \iffalse meta-comment % -%% File: l3drivers.dtx Copyright(C) 2011-2017 The LaTeX3 Project +%% File: l3drivers.dtx Copyright(C) 2011-2018 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 @@ -41,7 +41,7 @@ % }^^A % } % -% \date{Released 2018/02/21} +% \date{Released 2018/03/05} % % \maketitle % @@ -129,7 +129,7 @@ % \end{syntax} % Sets the color to the CMYK values specified, all of which are % fp denotations in the range $0$ and $1$. For drawing colors, see -% \cs{driver_draw_color_cmyk:nnnn}, \emph{etc.} +% \cs{driver_draw_stroke_cmyk:nnnn}, \emph{etc.} % \end{function} % % \begin{function}[added = 2018-02-20]{\driver_color_gray:n} @@ -138,7 +138,7 @@ % \end{syntax} % Sets the color to the grayscale value specified, which is % fp denotations in the range $0$ and $1$. For drawing colors, see -% \cs{driver_draw_color_gray:n}, \emph{etc.} +% \cs{driver_draw_stroke_gray:n}, \emph{etc.} % \end{function} % % \begin{function}[added = 2018-02-20]{\driver_color_rgb:nnn} @@ -147,20 +147,7 @@ % \end{syntax} % Sets the color to the RGB values specified, all of which are % fp denotations in the range $0$ and $1$. For drawing colors, see -% \cs{driver_draw_color_rgb:nnn}, \emph{etc.} -% \end{function} -% -% \begin{function}[added = 2018-02-20]{\driver_color_reset:} -% \begin{syntax} -% \cs{driver_color_reset:} -% \end{syntax} -% Resets the active color to that before a call to -% \cs{driver_color_\meta{model}:n(nnn)}. This function should be inserted -% after the \TeX{} group setting a color, \emph{i.e.}~normally -% \begin{verbatim} -% % Use of \driver_color_rgb:nnn, etc. -% \group_insert_after:N \driver_color_reset: -% \end{verbatim} +% \cs{driver_draw_stroke_rgb:nnn}, \emph{etc.} % \end{function} % % \begin{function}[added = 2018-02-20]{\driver_color_pickup:N} @@ -404,58 +391,46 @@ % % \begin{function} % { -% \driver_draw_color_cmyk:nnnn , -% \driver_draw_fill_cmyk:nnnn , -% \driver_draw_stroke_cmyk:nnnn +% \driver_draw_color_fill_cmyk:nnnn , +% \driver_draw_color_stroke_cmyk:nnnn % } % \begin{syntax} -% \cs{driver_draw_color_cmyk:nnnn} \Arg{cyan} \Arg{magenta} \Arg{yellow} +% \cs{driver_draw_color_fill_cmyk:nnnn} \Arg{cyan} \Arg{magenta} \Arg{yellow} % \Arg{black} % \end{syntax} % Sets the color for drawing to the CMYK values specified, all of which are -% fp denotations in the range $0$ and $1$. The -% \texttt{fill} and \texttt{stroke} versions set only the color for those -% operations. Note that the general setting is more efficient with some -% drivers so should in most cases be preferred. +% fp denotations in the range $0$ and $1$. % \end{function} % % \begin{function} % { -% \driver_draw_color_gray:n , -% \driver_draw_fill_gray:n , -% \driver_draw_stroke_gray:n +% \driver_draw_color_fill_gray:n , +% \driver_draw_color_stroke_gray:n % } % \begin{syntax} -% \cs{driver_draw_color_gray:n} \Arg{gray} +% \cs{driver_draw_color_fill_gray:n} \Arg{gray} % \end{syntax} % Sets the color for drawing to the grayscale value specified, which is -% fp denotations in the range $0$ and $1$. The -% \texttt{fill} and \texttt{stroke} versions set only the color for those -% operations. Note that the general setting is more efficient with some -% drivers so should in most cases be preferred. +% fp denotations in the range $0$ and $1$. % \end{function} % % \begin{function} % { -% \driver_draw_color_rgb:nnn , -% \driver_draw_fill_rgb:nnn , -% \driver_draw_stroke_rgb:nnn +% \driver_draw_color_fill_rgb:nnn , +% \driver_draw_color_stroke_rgb:nnn % } % \begin{syntax} -% \cs{driver_draw_color_rgb:nnn} \Arg{red} \Arg{green} \Arg{blue} +% \cs{driver_draw_color_fill_rgb:nnn} \Arg{red} \Arg{green} \Arg{blue} % \end{syntax} % Sets the color for drawing to the RGB values specified, all of which are -% fp denotations in the range $0$ and $1$. The -% \texttt{fill} and \texttt{stroke} versions set only the color for those -% operations. Note that the general setting is more efficient with some -% drivers so should in most cases be preferred. +% fp denotations in the range $0$ and $1$. % \end{function} % % \subsection{Inserting \TeX{} material} % -% \begin{function}{\driver_draw_hbox:Nnnnnnn} +% \begin{function}{\driver_draw_box_use:Nnnnn} % \begin{syntax} -% \cs{driver_draw_hbox:Nnnnnnn} \meta{box} +% \cs{driver_draw_box:Nnnnnnn} \meta{box} % \Arg{a} \Arg{b} \Arg{c} \Arg{d} \Arg{x} \Arg{y} % \end{syntax} % Inserts the \meta{box} as an hbox with the box reference point placed @@ -469,14 +444,13 @@ % % \subsection{Coordinate system transformations} % -% \begin{function}{\driver_draw_transformcm:nnnnnn} +% \begin{function}{\driver_draw_cm:nnnn} % \begin{syntax} -% \cs{driver_draw_transformcm:nnnnnn} \Arg{a} \Arg{b} \Arg{c} \Arg{d} -% \Arg{x} \Arg{y} +% \cs{driver_draw_cm:nnnn} \Arg{a} \Arg{b} \Arg{c} \Arg{d} % \end{syntax} -% Applies the transformation matrix $[a b c d]$ and offset vector -% ($x$, $y$) to the current graphic state. This affects any subsequent -% items in the same scope but not those already given. +% Applies the transformation matrix $[a b c d]$ to the current graphic state. +% This affects any subsequent items in the same scope but not those already +% given. % \end{function} % % \end{documentation} @@ -537,9 +511,13 @@ % % \subsection{Color support} % +% 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. As such the color code is collected here in two blocks. +% PDF output. % % \subsubsection{\texttt{dvips}-style} % @@ -586,8 +564,9 @@ % \begin{macro}{\driver_color_rgb:nnn} % \begin{macro}{\driver_color_spot:nn} % \begin{macro}{\@@_color_select:n} -% \begin{macro}{\driver_color_reset:} -% Simply dump the data. +% \begin{macro}{\@@_color_reset:} +% Push the data to the stack. In the case of \texttt{dvips} also reset the +% drawing fill color in raw PostScript. % \begin{macrocode} \cs_new_protected:Npn \driver_color_cmyk:nnnn #1#2#3#4 { \@@_color_select:n { cmyk~ #1 ~ #2 ~ #3 ~ #4 } } @@ -596,10 +575,16 @@ \cs_new_protected:Npn \driver_color_rgb:nnn #1#2#3 { \@@_color_select:n { rgb~ #1 ~ #2 ~ #3 } } \cs_new_protected:Npn \driver_color_spot:nn #1#2 - { \@@_color_select:n { color~push~\c_space_tl #1 } } + { \@@_color_select:n { \c_space_tl #1 } } \cs_new_protected:Npn \@@_color_select:n #1 - { \tex_special:D { color~push~ #1 } } -\cs_new_protected:Npn \driver_color_reset: + { + \tex_special:D { color~push~ #1 } +%<*dvips> + \tex_special:D { ps::/l3fc~{ }~def } +%</dvips> + \group_insert_after:N \@@_color_reset: + } +\cs_new_protected:Npn \@@_color_reset: { \tex_special:D { color~pop } } % \end{macrocode} % \end{macro} @@ -683,7 +668,7 @@ % \begin{macro}{\driver_color_rgb:nnn} % \begin{macro}{\driver_color_spot:nn} % \begin{macro}{\@@_color_select:n} -% \begin{macro}{\driver_color_reset:} +% \begin{macro}{\@@_color_reset:} % Simply dump the data, but allowing for \LuaTeX{}. % \begin{macrocode} \cs_new_protected:Npn \driver_color_cmyk:nnnn #1#2#3#4 @@ -700,8 +685,9 @@ { \luatex_pdfextension:D colorstack } { \pdftex_pdfcolorstack:D } \exp_not:N \l_@@_color_stack_int push {#1} + \group_insert_after:N \exp_not:N \@@_color_reset: } -\cs_new_protected:Npx \driver_color_reset: +\cs_new_protected:Npx \@@_color_reset: { \cs_if_exist:NTF \luatex_pdfextension:D { \luatex_pdfextension:D colorstack } @@ -877,7 +863,7 @@ % here). % \begin{macrocode} \cs_new_protected:Npn \@@_draw_literal:n #1 - { \tex_special:D { ps:: ~ #1 } } + { \tex_special:D { ps:: #1 } } \cs_generate_variant:Nn \@@_draw_literal:n { x } % \end{macrocode} % \end{macro} @@ -892,16 +878,17 @@ % \begin{macrocode} \cs_new_protected:Npn \driver_draw_begin: { - \tex_special:D { ps::[begin] } - \tex_special:D { ps::~save } - \tex_special:D { ps::~/l3x~currentpoint~/l3y~exch~def~def } - \tex_special:D { ps::~@beginspecial } + \@@_draw_literal:n { [begin] } + \@@_draw_literal:n { save } + \@@_draw_literal:n { /l3x~currentpoint~/l3y~exch~def~def } + \@@_draw_literal:n { @beginspecial } + \@@_draw_literal:n { /l3fc~{ }~def } } \cs_new_protected:Npn \driver_draw_end: { - \tex_special:D { ps::~@endspecial } - \tex_special:D { ps::~restore } - \tex_special:D { ps::[end] } + \@@_draw_literal:n { @endspecial } + \@@_draw_literal:n { restore } + \@@_draw_literal:n { [end] } } % \end{macrocode} % \end{macro} @@ -987,21 +974,17 @@ % \begin{variable}{\g_@@_draw_clip_bool} % Unlike PDF, PostScript doesn't track separate colors for strokes and other % elements. It is also desirable to have the |clip| keyword after a stroke or -% fill. To achieve those outcomes, there is some work to do. For color, if a -% stroke or fill color is defined it is used for the relevant operation, with -% a graphic scope inserted as required. That does mean that once such a color -% is set all further uses inside the same scope have to use scoping: see also -% the color set up functions. For clipping, the required ordering is achieved -% using a \TeX{} switch. All of the operations end with a new path instruction -% as they do not terminate (again in contrast to PDF). +% fill. To achieve those outcomes, there is some work to do. For color, the +% stoke color is simple but the fill one has to be inserted by hand. For +% clipping, the required ordering is achieved using a \TeX{} switch. All of +% the operations end with a new path instruction as they do not terminate +% (again in contrast to PDF). % \begin{macrocode} \cs_new_protected:Npn \driver_draw_closepath: { \@@_draw_literal:n { closepath } } \cs_new_protected:Npn \driver_draw_stroke: { - \@@_draw_literal:n { currentdict~/l3sc~known~{gsave~l3sc}~if } - \@@_draw_literal:n { stroke } - \@@_draw_literal:n { currentdict~/l3sc~known~{grestore}~if } + \@@_draw_literal:n { stroke } \bool_if:NT \g_@@_draw_clip_bool { \@@_draw_literal:x @@ -1020,13 +1003,14 @@ } \cs_new_protected:Npn \driver_draw_fill: { - \@@_draw_literal:n { currentdict~/l3fc~known~{gsave~l3fc}~if } + \@@_draw_literal:n { gsave } + \@@_draw_literal:n { l3fc } \@@_draw_literal:x { \bool_if:NT \g_@@_draw_eor_bool { eo } fill } - \@@_draw_literal:n { currentdict~/l3fc~known~{grestore}~if } + \@@_draw_literal:n { grestore } \bool_if:NT \g_@@_draw_clip_bool { \@@_draw_literal:x @@ -1040,16 +1024,15 @@ } \cs_new_protected:Npn \driver_draw_fillstroke: { - \@@_draw_literal:n { currentdict~/l3fc~known~{gsave~l3fc}~if } + \@@_draw_literal:n { gsave } + \@@_draw_literal:n { l3fc } \@@_draw_literal:x { \bool_if:NT \g_@@_draw_eor_bool { eo } fill } - \@@_draw_literal:n { currentdict~/l3fc~known~{grestore}~if } - \@@_draw_literal:n { currentdict~/l3sc~known~{gsave~l3sc}~if } + \@@_draw_literal:n { grestore } \@@_draw_literal:n { stroke } - \@@_draw_literal:n { currentdict~/l3sc~known~{grestore}~if } \bool_if:NT \g_@@_draw_clip_bool { \@@_draw_literal:x @@ -1131,73 +1114,46 @@ % \end{macro} % \end{macro} % -% \begin{macro}{\_@@_draw_color_reset:} +% % \begin{macro} % { -% \driver_draw_color_cmyk:nnnn , -% \driver_draw_fill_cmyk:nnnn , -% \driver_draw_stroke_cmyk:nnnn +% \driver_draw_color_fill_cmyk:nnnn , +% \driver_draw_color_stroke_cmyk:nnnn % } % \begin{macro} % { -% \driver_draw_color_gray:n , -% \driver_draw_fill_gray:n , -% \driver_draw_stroke_gray:n +% \driver_draw_color_fill_gray:n , +% \driver_draw_color_stroke_gray:n % } % \begin{macro} % { -% \driver_draw_color_rgb:nnn , -% \driver_draw_fill_rgb:nnn , -% \driver_draw_stroke_rgb:nnn +% \driver_draw_color_fill_rgb:nnn , +% \driver_draw_color_stroke_rgb:nnn % } -% To allow color to be defined for strokes and fills separately and to -% respect scoping, the data needs to be stored at the PostScript level. -% We cannot undefine (local) fill/stroke colors once set up but we can -% set them blank to improve performance slightly. +% \begin{macro}{\@@_draw_color_fill:n, \@@_draw_color_stroke:n} +% For \texttt{dvips}, we can use the standard color stack to deal with +% stroke color, but for fills have to switch to raw PostScript. This is +% thus not handled by the stack, but the context is very restricted. See +% also how fills are implemented. % \begin{macrocode} -\cs_new_protected:Npn \@@_draw_color_reset: - { - \@@_draw_literal:n { currentdic~/l3fc~known~{ /l3fc~ { } ~def }~if } - \@@_draw_literal:n { currentdic~/l3sc~known~{ /l3sc~ { } ~def }~if } - } -\cs_new_protected:Npn \driver_draw_color_cmyk:nnnn #1#2#3#4 - { - \@@_draw_literal:n { #1 ~ #2 ~ #3 ~ #4 ~ setcmykcolor } - \@@_draw_color_reset: - } -\cs_new_protected:Npn \driver_draw_fill_cmyk:nnnn #1#2#3#4 - { - \@@_draw_literal:n - { /l3fc ~ { #1 ~ #2 ~ #3 ~ #4 ~ setcmykcolor ~ } ~ def } - } -\cs_new_protected:Npn \driver_draw_stroke_cmyk:nnnn #1#2#3#4 +\cs_new_protected:Npn \driver_draw_color_fill_cmyk:nnnn #1#2#3#4 + { \@@_draw_fill:n { #1 ~ #2 ~ #3 ~ #4 ~ setcmykcolor } } +\cs_new_protected:Npn \driver_draw_color_stroke_cmyk:nnnn #1#2#3#4 + { \@@_draw_stroke:n { cmyk ~ #1 ~ #2 ~ #3 ~ #4 } } +\cs_new_protected:Npn \driver_draw_color_fill_gray:n #1 + { \@@_draw_fill:n { #1 ~ setgray } } +\cs_new_protected:Npn \driver_draw_color_stroke_gray:n #1 + { \@@_draw_stroke:n { gray ~ #1 } } +\cs_new_protected:Npn \driver_draw_color_fill_rgb:nnn #1#2#3 + { \@@_draw_fill:n { #1 ~ #2 ~ #3 ~ setrgbcolor } } +\cs_new_protected:Npn \driver_draw_color_stroke_rgb:nnn #1#2#3 + { \@@_draw_stroke:n { rgb ~ #1 ~ #2 ~ #3 } } +\cs_new_protected:Npn \@@_draw_color_fill:n #1 + { \@@_draw_literal:n { /l3fc ~ { #1 } ~ def } } +\cs_new_protected:Npn \@@_draw_color_stroke:n #1 { - \@@_draw_literal:n - { /l3sc ~ { #1 ~ #2 ~ #3 ~ #4 ~ setcmykcolor ~ } ~ def } - } -\cs_new_protected:Npn \driver_draw_color_gray:n #1 - { - \@@_draw_literal:n { #1 ~ setgray } - \@@_draw_color_reset: - } -\cs_new_protected:Npn \driver_draw_fill_gray:n #1 - { \@@_draw_literal:n { /l3fc ~ { #1 ~ setgray } ~ def } } -\cs_new_protected:Npn \driver_draw_stroke_gray:n #1 - { \@@_draw_literal:n { /l3sc ~ { #1 ~ setgray } ~ def } } -\cs_new_protected:Npn \driver_draw_color_rgb:nnn #1#2#3 - { - \@@_draw_literal:n { #1 ~ #2 ~ #3 ~ setrgbcolor } - \@@_draw_color_reset: - } -\cs_new_protected:Npn \driver_draw_fill_rgb:nnn #1#2#3 - { - \@@_draw_literal:n - { /l3fc ~ { #1 ~ #2 ~ #3 ~ setrgbcolor } ~ def } - } -\cs_new_protected:Npn \driver_draw_stroke_rgb:nnn #1#2#3 - { - \@@_draw_literal:n - { /l3sc ~ { #1 ~ #2 ~ #3 ~ setrgbcolor } ~ def } + \tex_special:D { color~push~#1 } + \group_insert_after:N \@@_color_reset: } % \end{macrocode} % \end{macro} @@ -1205,26 +1161,19 @@ % \end{macro} % \end{macro} % -% \begin{macro}{\driver_draw_transformcm:nnnnnn} -% The first four arguments here are floats (the affine matrix), the last -% two are a displacement vector. Once again, force evaluation to allow for -% caching. +% \begin{macro}{\driver_draw_cm:nnnn} +% In \texttt{dvips}, keeping the transformations in line with the engine +% is unfortunately not possible for scaling and rotations: even if we +% decompose the matrix into those operations, there is still no driver +% tracking (\emph{cf.}~\texttt{(x)dvipdfmx}). Thus we take the shortest +% path available and simply dump the matrix as given. % \begin{macrocode} -\cs_new_protected:Npn \driver_draw_transformcm:nnnnnn #1#2#3#4#5#6 - { - \@@_draw_literal:x - { - [ - #1 ~ #2 ~ #3 ~ #4 ~ - \dim_to_decimal_in_bp:n {#5} ~ \dim_to_decimal_in_bp:n {#6} ~ - ] ~ - concat - } - } +\cs_new_protected:Npn \driver_draw_cm:nnnn #1#2#3#4 + { \@@_draw_literal:n { [#1 ~ #2 ~ #3 ~ #4 ~ 0 ~ 0 ] ~ concat } } % \end{macrocode} % \end{macro} % -% \begin{macro}{\driver_draw_hbox:Nnnnnnn} +% \begin{macro}{\driver_draw_box_use:Nnnnn} % Inside a picture |@beginspecial|/|@endspecial| are active, which is % normally a good thing but means that the position and scaling would be off % if the box was inserted directly. Instead, we need to reverse the effect of @@ -1234,19 +1183,16 @@ % drawing origin so has to be done purely in driver code not using \TeX{} % offsets. % \begin{macrocode} -\cs_new_protected:Npn \driver_draw_hbox:Nnnnnnn #1#2#3#4#5#6#7 +\cs_new_protected:Npn \driver_draw_box_use:Nnnnn #1#2#3#4#5 { \@@_scope_begin: - \tex_special:D { ps::[end] } - \driver_draw_transformcm:nnnnnn {#2} {#3} {#4} {#5} {#6} {#7} - \tex_special:D { ps::~72~Resolution~div~72~VResolution~div~neg~scale } - \tex_special:D { ps::~magscale~{1~DVImag~div~dup~scale}~if } - \tex_special:D { ps::~l3x~neg~l3y~neg~translate } - \box_set_wd:Nn #1 { 0pt } - \box_set_ht:Nn #1 { 0pt } - \box_set_dp:Nn #1 { 0pt } - \box_use:N #1 - \tex_special:D { ps::[begin] } + \@@_draw_literal:n { [end] } + \driver_draw_cm:nnnn {#2} {#3} {#4} {#5} + \@@_draw_literal:n { 72~Resolution~div~72~VResolution~div~neg~scale } + \@@_draw_literal:n { magscale~{1~DVImag~div~dup~scale}~if } + \@@_draw_literal:n { l3x~neg~l3y~neg~translate } + \hbox_overlap_right:n { \box_use:N #1 } + \@@_draw_literal:n { [begin] } \@@_scope_end: } % \end{macrocode} @@ -2053,75 +1999,200 @@ % % \begin{macro} % { -% \driver_draw_color_cmyk:nnnn , -% \driver_draw_fill_cmyk:nnnn , -% \driver_draw_stroke_cmyk:nnnn +% \driver_draw_color_fill_cmyk:nnnn , +% \driver_draw_color_stroke_cmyk:nnnn % } % \begin{macro} % { -% \driver_draw_color_gray:n , -% \driver_draw_fill_gray:n , -% \driver_draw_stroke_gray:n +% \driver_draw_color_fill_gray:n , +% \driver_draw_color_stroke_gray:n % } % \begin{macro} % { -% \driver_draw_color_rgb:nnn , -% \driver_draw_fill_rgb:nnn , -% \driver_draw_stroke_rgb:nnn +% \driver_draw_color_fill_rgb:nnn , +% \driver_draw_color_stroke_rgb:nnn % } -% Yet more fast conversion, all using the FPU to allow for expressions -% in numerical input. +% For the stroke color, all engines here can use the color stack to handle +% the setting. However, that is not the case for fill color: the stack in +% \texttt{(x)dvipdfmx} only covers one type of color. So we have to use +% different approaches for the two sets of engines. +% \begin{macrocode} +\cs_new_protected:Npn \driver_draw_color_fill_cmyk:nnnn #1#2#3#4 + { \@@_color_fill_select:n { #1 ~ #2 ~ #3 ~ #4 ~ k } } +\cs_new_protected:Npn \driver_draw_color_stroke_cmyk:nnnn #1#2#3#4 + { \@@_color_select:n { #1 ~ #2 ~ #3 ~ #4 ~ K } } +\cs_new_protected:Npn \driver_draw_color_fill_gray:n #1 + { \@@_color_fill_select:n { #1 ~ g } } +\cs_new_protected:Npn \driver_draw_color_stroke_gray:n #1 + { \@@_color_select:n { #1 ~ G } } +\cs_new_protected:Npn \driver_draw_color_fill_rgb:nnn #1#2#3 + { \@@_color_fill_select:n { #1 ~ #2 ~ #3 ~ rg } } +\cs_new_protected:Npn \driver_draw_color_stroke_rgb:nnn #1#2#3 + { \@@_color_select:n { #1 ~ #2 ~ #3 ~ RG } } +%<*pdfmode> +\cs_new_eq:NN \@@_color_fill_select:n \@@_color_select:n +%</pdfmode> +%<*dvipdfmx|xdvipdfmx> +\cs_new_eq:NN \@@_color_fill_select:n \@@_draw_literal:n +%</dvipdfmx|xdvipdfmx> +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\driver_draw_cm:nnnn} +% \begin{macro}{\@@_draw_cm:nnnn} +% Another split here between \texttt{pdfmode} and \texttt{(x)dvipdfmx}. +% In the former, we have a direct method to maintain alignment: the driver +% can use a matrix itself. For \texttt{(x)dvipdfmx}, we can to decompose the +% matrix into rotations and a scaling, then use those operations as they +% are handled by the driver. (There is driver support for matrix operations in +% \texttt{(x)dvipdfmx}, but as a matched pair so not suitable for the +% \enquote{stand alone} transformation set up here.) % \begin{macrocode} -\cs_new_protected:Npn \driver_draw_color_cmyk:nnnn #1#2#3#4 +\cs_new_protected:Npn \driver_draw_cm:nnnn #1#2#3#4 { - \@@_draw_literal:n - { #1 ~ #2 ~ #3 ~ #4 ~ k ~ #1 ~ #2 ~ #3 ~ #4 ~ K } - } -\cs_new_protected:Npn \driver_draw_fill_cmyk:nnnn #1#2#3#4 - { - \@@_draw_literal:n { #1 ~ #2 ~ #3 ~ #4 ~ k } +%<*pdfmode> + \@@_matrix:n { #1 ~ #2 ~ #3 ~ #4 } +%</pdfmode> +%<*dvipdfmx|xdvipdfmx> + \@@_draw_cm_decompose:nnnnN {#1} {#2} {#3} {#4} + \@@_draw_cm:nnnn +%</dvipdfmx|xdvipdfmx> } -\cs_new_protected:Npn \driver_draw_stroke_cmyk:nnnn #1#2#3#4 - { \@@_draw_literal:n { #1 ~ #2 ~ #3 ~ #4 ~ K } } -\cs_new_protected:Npn \driver_draw_color_gray:n #1 - { \@@_draw_literal:n { #1 ~ g ~ #1 ~ G } } -\cs_new_protected:Npn \driver_draw_fill_gray:n #1 - { \@@_draw_literal:n { #1 ~ g } } -\cs_new_protected:Npn \driver_draw_stroke_gray:n #1 - { \@@_draw_literal:n { #1 ~ G } } -\cs_new_protected:Npn \driver_draw_color_rgb:nnn #1#2#3 +%<*dvipdfmx|xdvipdfmx> +\cs_new_protected:Npn \@@_draw_cm:nnnn #1#2#3#4 { - \@@_draw_literal:n - { #1 ~ #2 ~ #3 ~ rg ~ #1 ~ #2 ~ #3 ~ RG } + \tex_special:D + { + x:rotate~ + \fp_compare:nNnTF {#1} = \c_zero_fp + { 0 } + { \fp_eval:n { round ( -#1 , 5 ) } } + } + \tex_special:D + { + x:scale~ + \fp_eval:n { round ( #2 , 5 ) } ~ + \fp_eval:n { round ( #3 , 5 ) } + } + \tex_special:D + { + x:rotate~ + \fp_compare:nNnTF {#4} = \c_zero_fp + { 0 } + { \fp_eval:n { round ( -#4 , 5 ) } } + } } -\cs_new_protected:Npn \driver_draw_fill_rgb:nnn #1#2#3 - { \@@_draw_literal:n { #1 ~ #2 ~ #3 ~ rg } } -\cs_new_protected:Npn \driver_draw_stroke_rgb:nnn #1#2#3 - { \@@_draw_literal:n { #1 ~ #2 ~ #3 ~ RG } } +%</dvipdfmx|xdvipdfmx> % \end{macrocode} % \end{macro} % \end{macro} -% \end{macro} % -% \begin{macro}{\driver_draw_transformcm:nnnnnn} -% The first four arguments here are floats (the affine matrix), the last -% two are a displacement vector. Once again, force evaluation to allow for -% caching. +% \begin{macro}{\@@_draw_cm_decompose:nnnnN} +% \begin{macro} +% { +% \@@_draw_cm_decompose_auxi:nnnnN, +% \@@_draw_cm_decompose_auxii:nnnnN, +% \@@_draw_cm_decompose_auxiii:nnnnN, +% } +% Internally, transformations for drawing are tracked as a matrix. Not all +% engines provide a way of dealing with this: if we use a raw matrix, the +% engine looses track of positions (for example for hyperlinks), and this is +% not desirable. They do, however, allow us to track rotations and scalings. +% Luckily, we can decompose any (two-dimensional) matrix into two rotations +% and a single scaling: +% \[ +% \begin{bmatrix} +% A & B \\ C & D +% \end{bmatrix} +% = +% \begin{bmatrix} +% \cos\beta & \sin\beta \\ -\sin\beta & \cos\beta +% \end{bmatrix} +% \begin{bmatrix} +% w_{1} & 0 \\ 0 & w_{2} +% \end{bmatrix} +% \begin{bmatrix} +% \cos\gamma & \sin\gamma \\ -\sin\gamma & \cos\gamma +% \end{bmatrix} +% \] +% The parent matrix can be converted to +% \[ +% \begin{bmatrix} +% A & B \\ C & D +% \end{bmatrix} +% = +% \begin{bmatrix} +% E & H \\-H & E +% \end{bmatrix} +% + +% \begin{bmatrix} +% F & G \\ G & -F +% \end{bmatrix} +% \] +% From these, we can find that +% \begin{align*} +% \frac{w_{1} + w_{2}}{2} &= \sqrt{E^{2} + H^{2}} \\ +% \frac{w_{1} - w_{2}}{2} &= \sqrt{F^{2} + G^{2}} \\ +% \gamma - \beta &= \tan^{-1}(G/F) \\ +% \gamma + \beta &= \tan^{-1}(H/E) +% \end{align*} +% at which point we just have to do various pieces of re-arrangement to +% get all of the values. (See J.~Blinn, \emph{IEEE Comput.\ Graph.\ Appl.}, +% 1996, \textbf{16}, 82--88.) There is one wrinkle: the PostScript (and PDF) +% way of specifying a transformation matrix exchanges where one would +% normally expect $B$ and $C$ to be. % \begin{macrocode} -\cs_new_protected:Npn \driver_draw_transformcm:nnnnnn #1#2#3#4#5#6 +%<*dvipdfmx|xdvipdfmx> +\cs_new_protected:Npn \@@_draw_cm_decompose:nnnnN #1#2#3#4#5 { - \@@_draw_literal:x + \use:x { - #1 ~ #2 ~ #3 ~ #4 ~ - \dim_to_decimal_in_bp:n {#5} ~ \dim_to_decimal_in_bp:n {#6} ~ - cm + \@@_draw_cm_decompose_auxi:nnnnN + { \fp_eval:n { (#1 + #4) / 2 } } + { \fp_eval:n { (#1 - #4) / 2 } } + { \fp_eval:n { (#3 + #2) / 2 } } + { \fp_eval:n { (#3 - #2) / 2 } } + } + #5 + } +\cs_new_protected:Npn \@@_draw_cm_decompose_auxi:nnnnN #1#2#3#4#5 + { + \use:x + { + \@@_draw_cm_decompose_auxii:nnnnN + { \fp_eval:n { 2 * sqrt ( #1 * #1 + #4 * #4 ) } } + { \fp_eval:n { 2 * sqrt ( #2 * #2 + #3 * #3 ) } } + { \fp_eval:n { atand ( #3 , #2 ) } } + { \fp_eval:n { atand ( #4 , #1 ) } } } + #5 + } +\cs_new_protected:Npn \@@_draw_cm_decompose_auxii:nnnnN #1#2#3#4#5 + { + \use:x + { + \@@_draw_cm_decompose_auxiii:nnnnN + { \fp_eval:n { ( #4 - #3 ) / 2 } } + { \fp_eval:n { ( #1 + #2 ) / 2 } } + { \fp_eval:n { ( #1 - #2 ) / 2 } } + { \fp_eval:n { ( #4 + #3 ) / 2 } } + } + #5 + } +\cs_new_protected:Npn \@@_draw_cm_decompose_auxiii:nnnnN #1#2#3#4#5 + { + \fp_compare:nNnTF { abs( #2 ) } > { abs ( #3 ) } + { #5 {#1} {#2} {#3} {#4} } + { #5 {#1} {#3} {#2} {#4} } } +%</dvipdfmx|xdvipdfmx> % \end{macrocode} % \end{macro} +% \end{macro} % -% \begin{macro}{\driver_draw_hbox:Nnnnnnn} -% \begin{variable}{\l_@@_tmp_box} +% \begin{macro}{\driver_draw_box_use:Nnnnn} % Inserting a \TeX{} box transformed to the requested position and using % the current matrix is done using a mixture of \TeX{} and low-level % manipulation. The offset can be handled by \TeX{}, so only any rotation/^^A @@ -2129,25 +2200,22 @@ % operation can never be cached, the scope is set directly not using the % \texttt{draw} version. % \begin{macrocode} -\cs_new_protected:Npn \driver_draw_hbox:Nnnnnnn #1#2#3#4#5#6#7 +\cs_new_protected:Npn \driver_draw_box_use:Nnnnn #1#2#3#4#5 { - \hbox_set:Nn \l_@@_tmp_box - { - \tex_kern:D \__dim_eval:n {#6} - \@@_scope_begin: - \driver_draw_transformcm:nnnnnn {#2} {#3} {#4} {#5} - { 0pt } { 0pt } - \box_move_up:nn {#7} { \box_use:N #1 } - \@@_scope_end: - } - \box_set_wd:Nn \l_@@_tmp_box { 0pt } - \box_set_ht:Nn \l_@@_tmp_box { 0pt } - \box_set_dp:Nn \l_@@_tmp_box { 0pt } - \box_use:N \l_@@_tmp_box + \@@_scope_begin: +%<*pdfmode> + \driver_draw_cm:nnnn {#2} {#3} {#4} {#5} +%</pdfmode> +%<*dvipdfmx|xdvipdfmx> + \tex_special:D { pdf:btrans~matrix~ #2 ~ #3 ~ #4 ~ #5 ~ 0 ~ 0 } +%</dvipdfmx|xdvipdfmx> + \hbox_overlap_right:n { \box_use:N #1 } +%<*dvipdfmx|xdvipdfmx> + \tex_special:D { pdf:etrans } +%</dvipdfmx|xdvipdfmx> + \@@_scope_end: } -\box_new:N \l_@@_tmp_box % \end{macrocode} -% \end{variable} % \end{macro} % % \begin{macrocode} @@ -2651,122 +2719,85 @@ % % \begin{macro} % { -% \driver_draw_color_cmyk:nnnn , -% \driver_draw_fill_cmyk:nnnn , -% \driver_draw_stroke_cmyk:nnnn +% \driver_draw_color_fill_cmyk:nnnn , +% \driver_draw_color_stroke_cmyk:nnnn % } % \begin{macro} % { -% \driver_draw_color_gray:n , -% \driver_draw_fill_gray:n , -% \driver_draw_stroke_gray:n +% \driver_draw_color_fill_gray:n , +% \driver_draw_color_stroke_gray:n % } % \begin{macro} % { -% \driver_draw_color_rgb:nnn , -% \driver_draw_fill_rgb:nnn , -% \driver_draw_stroke_rgb:nnn +% \driver_draw_color_fill_rgb:nnn , +% \driver_draw_color_stroke_rgb:nnn % } -% SVG only works with RGB colors, so there is some conversion to -% do. The values also need to be given as percentages, which means a -% little more maths. +% \begin{macro}{\@@_draw_color_fill:nnn} +% SVG fill color has to be covered outside of the stack, as for +% \texttt{dvips}. Here, we are only allowed RGB colors so there is some +% conversion to do. % \begin{macrocode} -\cs_new_protected:Npn \@@_draw_color_cmyk_aux:NNnnnnn #1#2#3#4#5#6 +\cs_new_protected:Npn \driver_draw_color_stroke_cmyk:nnnn #1#2#3#4 { \use:x { - \@@_draw_color_rgb_auxii:nnn - { \fp_eval:n { -100 * ( (#3) * ( 1 - (#6) ) - 1 ) } } - { \fp_eval:n { -100 * ( (#4) * ( 1 - (#6) ) + #6 - 1 ) } } - { \fp_eval:n { -100 * ( (#5) * ( 1 - (#6) ) + #6 - 1 ) } } + \@@_draw_color_fill:nnn + { \fp_eval:n { -100 * ( (#1) * ( 1 - (#4) ) - 1 ) } } + { \fp_eval:n { -100 * ( (#2) * ( 1 - (#4) ) + #4 - 1 ) } } + { \fp_eval:n { -100 * ( (#3) * ( 1 - (#4) ) + #4 - 1 ) } } } - #1 #2 } -\cs_new_protected:Npn \driver_draw_color_cmyk:nnnn - { \@@_draw_color_cmyk_aux:NNnnnnn \c_true_bool \c_true_bool } -\cs_new_protected:Npn \driver_draw_fill_cmyk:nnnn - { \@@_draw_color_cmyk_aux:NNnnnnn \c_false_bool \c_true_bool } -\cs_new_protected:Npn \driver_draw_stroke_cmyk:nnnn - { \@@_draw_color_cmyk_aux:NNnnnnn \c_true_bool \c_false_bool } -\cs_new_protected:Npn \@@_draw_color_gray_aux:NNn #1#2#3 +\cs_new_eq:NN \driver_draw_color_stroke_cmyk:nnnn \driver_color_cmyk:nnnn +\cs_new_protected:Npn \driver_draw_color_gray:n #1 { \use:x { - \@@_draw_color_gray_aux:nNN - { \fp_eval:n { 100 * (#3)} } + \@@_draw_color_gray_aux:n + { \fp_eval:n { 100 * (#3) } } } - #1 #2 - } -\cs_new_protected:Npn \@@_draw_color_gray_aux:nNN #1 - { \@@_draw_color_rgb_auxii:nnnNN {#1} {#1} {#1} } -\cs_generate_variant:Nn \@@_draw_color_gray_aux:nNN { x } -\cs_new_protected:Npn \driver_draw_color_gray:n - { \@@_draw_color_gray_aux:NNn \c_true_bool \c_true_bool } -\cs_new_protected:Npn \driver_draw_fill_gray:n - { \@@_draw_color_gray_aux:NNn \c_false_bool \c_true_bool } -\cs_new_protected:Npn \driver_draw_stroke_gray:n - { \@@_draw_color_gray_aux:NNn \c_true_bool \c_false_bool } -\cs_new_protected:Npn \@@_draw_color_rgb_auxi:NNnnn #1#2#3#4#5 + } +\cs_new_protected:Npn \@@_draw_color_gray_aux:n #1 + { \@@_draw_color_fill:nnn {#1} {#1} {#1} } +\cs_new_eq:NN \driver_draw_color_stroke_gray:n \driver_color_gray:n +\cs_new_protected:Npn \driver_draw_color_rgb:nnn #1#2#3 { \use:x { - \@@_draw_color_rgb_auxii:nnnNN + \@@_draw_color_fill:nnn + { \fp_eval:n { 100 * (#1) } } + { \fp_eval:n { 100 * (#2) } } { \fp_eval:n { 100 * (#3) } } - { \fp_eval:n { 100 * (#4) } } - { \fp_eval:n { 100 * (#5) } } } - #1 #2 } -\cs_new_protected:Npn \@@_draw_color_rgb_auxii:nnnNN #1#2#3#4#5 +\cs_new_protected:Npn \@@_draw_color_fill:nnn #1#2#3 { \@@_draw_scope:x { - \bool_if:NT #4 - { - fill = - " - rgb - ( - #1 \c_percent_str , - #2 \c_percent_str , - #3 \c_percent_str - ) - " - \bool_if:NT #5 { ~ } - } - \bool_if:NT #5 - { - stroke = - " - rgb - ( - #1 \c_percent_str , - #2 \c_percent_str , - #3 \c_percent_str - ) - " - } + fill = + " + rgb + ( + #1 \c_percent_str , + #2 \c_percent_str , + #3 \c_percent_str + ) + " } } -\cs_new_protected:Npn \driver_draw_color_rgb:nnn - { \@@_draw_color_rgb_auxi:NNnnn \c_true_bool \c_true_bool } -\cs_new_protected:Npn \driver_draw_fill_rgb:nnn - { \@@_draw_color_rgb_auxi:NNnnn \c_false_bool \c_true_bool } -\cs_new_protected:Npn \driver_draw_stroke_rgb:nnn - { \@@_draw_color_rgb_auxi:NNnnn \c_true_bool \c_false_bool } +\cs_new_eq:NN \driver_draw_color_stroke_rgb:nnn \driver_color_rgb:nnn % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} +% \end{macro} % -% \begin{macro}{\driver_draw_transformcm:nnnnnn} -% The first four arguments here are floats (the affine matrix), the last -% two are a displacement vector. Once again, force evaluation to allow for -% caching. +% \begin{macro}{\driver_draw_cm:nnnn} +% The four arguments here are floats (the affine matrix), the last +% two are a displacement vector. % \begin{macrocode} -\cs_new_protected:Npn \driver_draw_transformcm:nnnnnn #1#2#3#4#5#6 +\cs_new_protected:Npn \driver_draw_cm:nnnn #1#2#3#4 { - \@@_draw_scope:x + \@@_draw_scope:n { transform = " @@ -2774,7 +2805,7 @@ ( \fp_eval:n {#1} , \fp_eval:n {#2} , \fp_eval:n {#3} , \fp_eval:n {#4} , - \dim_to_decimal:n {#5} , \dim_to_decimal:n {#6} + 0pt , 0pt ) " } @@ -2790,7 +2821,7 @@ \cs_new_protected:Npn \driver_draw_hbox:Nnnnnnn #1#2#3#4#5#6#7 { \@@_scope_begin: - \driver_draw_transformcm:nnnnnn {#2} {#3} {#4} {#5} {#6} {#7} + \driver_draw_cm:nnnn {#2} {#3} {#4} {#5} \@@_literal:n { < g~ |