summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx649
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~