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.dtx1160
1 files changed, 1106 insertions, 54 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx b/Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx
index cfbcb9bdce3..fc30245ded9 100644
--- a/Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx
+++ b/Master/texmf-dist/source/latex/l3kernel/l3drivers.dtx
@@ -38,7 +38,7 @@
\documentclass[full]{l3doc}
%</driver>
%<*driver|package>
-\GetIdInfo$Id: l3drivers.dtx 6511 2016-05-17 20:20:23Z joseph $
+\GetIdInfo$Id: l3drivers.dtx 6644 2016-08-13 20:22:11Z bruno $
{L3 Experimental drivers}
%</driver|package>
%<*driver>
@@ -167,8 +167,6 @@
% but still require an appreciation of the concepts of PostScript/PDF/SVG
% graphic creation.
%
-% At present \emph{not all drivers are covered} by the following.
-%
% \begin{function}
% { \__driver_draw_begin:, \__driver_draw_end:}
% \begin{syntax}
@@ -195,7 +193,8 @@
% state and concepts such as color or linewidth are localised to a scope.
% This function pair must never be used if an partial path is under
% construction: such paths must be entirely contained at one unbroken
-% scope level.
+% scope level. Note that scopes do not form \TeX{} groups and may not
+% be aligned with them.
% \end{function}
%
% \subsection{Path construction}
@@ -231,6 +230,16 @@
% without appearing itself.
% \end{function}
%
+% \begin{function}{\__driver_draw_rectangle:nnnn}
+% \begin{syntax}
+% \cs{__driver_draw_rectangle:nnnn} \Arg{x} \Arg{y} \Arg{width} \Arg{height}
+% \end{syntax}
+% Adds rectangular path from (\meta{x_1}, \meta{y_1}) of \meta{height}
+% and \meta{width}; any active transformation matrix will apply. Note that
+% nothing is drawn until a fill or stroke operation is applied, and that the
+% path may be discarded or used as a clip without appearing itself.
+% \end{function}
+%
% \begin{function}{\__driver_draw_closepath:}
% \begin{syntax}
% \cs{__driver_draw_closepath:}
@@ -273,9 +282,18 @@
% influenced by the setting for fill color (or the current color if no
% specific stroke color is set). The path may also be used for clipping.
% For paths which are self-intersecting or comprising multiple parts, the
-% determination of which areas are inside the path is made using the nonzero
-% winding rule number unless \cs{__driver_draw_eor_bool} is \texttt{true},
-% in which case the even-odd rule is used.
+% determination of which areas are inside the path is made using the non-zero
+% winding number rule unless the even-odd rule is active.
+% \end{function}
+%
+% \begin{function}{\__driver_draw_nonzero_rule:, \__driver_draw_evenodd_rule:}
+% \begin{syntax}
+% \cs{__driver_draw_nonzero_rule:}
+% \end{syntax}
+% Active either the non-zero winding number or the even-odd rule,
+% respectively, for determining what is inside a fill or clip area.
+% For technical reasons, these command are not influenced by scoping
+% and apply on an ongoing basis.
% \end{function}
%
% \begin{function}{\__driver_draw_clip:}
@@ -286,7 +304,9 @@
% Indicates that the current path should be used for clipping, such that
% any subsequent material outside of the path (but within the current
% scope) will not be shown. This command should be given once a path is
-% complete but before it is stroked or filled (if appropriate).
+% complete but before it is stroked or filled (if appropriate). This
+% command is \emph{not} affected by scoping: it applies to exactly one
+% path as shown.
% \end{function}
%
% \begin{function}{\__driver_draw_discardpath:}
@@ -321,7 +341,7 @@
% $1$\,pt on, $2$\,pt off, $3$\,pt on, and so on. An odd number of entries
% means that the last is repeated, for example \texttt{3pt} is equal to
% \texttt{3pt, 3pt}. An empty pattern yields a solid line.
-%
+%
% The \meta{phase} specifies an offset at the start of the cycle. For
% example, with a pattern \texttt{3pt} a phase of \texttt{1pt} will mean
% that the output is $2$\,pt on, $3$\,pt off, $3$\,pt on, $3$\,pt on,
@@ -361,6 +381,85 @@
% PDF and PostScript manuals.
% \end{function}
%
+% \subsection{Color}
+%
+% \begin{function}
+% {
+% \__driver_draw_color_cmyk:nnnn ,
+% \__driver_draw_color_cmyk_fill:nnnn ,
+% \__driver_draw_color_cmyk_stroke:nnnn
+% }
+% \begin{syntax}
+% \cs{__driver_draw_color_cmyk:nnnn} \Arg{cyan} \Arg{magneta} \Arg{yellow}
+% \Arg{black}
+% \end{syntax}
+% Sets the color for drawing to the CMYK values specified, all of which are
+% fp expressions which should evaluate to between $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.
+% \end{function}
+%
+% \begin{function}
+% {
+% \__driver_draw_color_gray:n ,
+% \__driver_draw_color_gray_fill:n ,
+% \__driver_draw_color_gray_stroke:n
+% }
+% \begin{syntax}
+% \cs{__driver_draw_color_gray:n} \Arg{gray}
+% \end{syntax}
+% Sets the color for drawing to the grayscale value specified, which is
+% fp expressions which should evaluate to between $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.
+% \end{function}
+%
+% \begin{function}
+% {
+% \__driver_draw_color_rgb:nnn ,
+% \__driver_draw_color_rgb_fill:nnn ,
+% \__driver_draw_color_rgb_stroke:nnn
+% }
+% \begin{syntax}
+% \cs{__driver_draw_color_gray:n} \Arg{red} \Arg{green} \Arg{blue}
+% \end{syntax}
+% Sets the color for drawing to the RGB values specified, all of which are
+% fp expressions which should evaluate to between $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.
+% \end{function}
+%
+% \subsection{Inserting \TeX{} material}
+%
+% \begin{function}{\__driver_draw_hbox:nnnnnn}
+% \begin{syntax}
+% \cs{__driver_draw_hbox: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
+% at ($x$, $y$). The transformation matrix $[a b c d]$ will be applied
+% to the box, allowing it to be in synchronisation with any scaling, rotation
+% or skewing applying more generally. Note that \TeX{} material should not
+% be inserted directly into a drawing as it will not be in the correct
+% location. Also note that as for other drawing elements the box here will
+% have no size from a \TeX{} perspective.
+% \end{function}
+%
+% \subsection{Coordinate system transformations}
+%
+% \begin{function}{\__driver_draw_transformcm:nnnnnn}
+% \begin{syntax}
+% \cs{__driver_draw_transformcm:nnnnnn} \Arg{a} \Arg{b} \Arg{c} \Arg{d}
+% \Arg{x} \Arg{y}
+% \end{syntax}
+% Applies the transformation matrix $[a b c d]$ and offset vector
+% ($x$, $y$) to the current graphic state. This will affect any subsequent
+% items in the same scope but not those already given.
+% \end{function}
+%
% \end{documentation}
%
% \begin{implementation}
@@ -440,13 +539,13 @@
% \begin{macro}[int]{\@@_scope_begin:, \@@_scope_end:}
% Higher-level interfaces for saving and restoring the graphic state.
% \begin{macrocode}
-\cs_new_protected_nopar:Npx \@@_scope_begin:
+\cs_new_protected:Npx \@@_scope_begin:
{
\cs_if_exist:NTF \luatex_pdfextension:D
{ \luatex_pdfextension:D save \scan_stop: }
{ \pdftex_pdfsave:D }
}
-\cs_new_protected_nopar:Npx \@@_scope_end:
+\cs_new_protected:Npx \@@_scope_end:
{
\cs_if_exist:NTF \luatex_pdfextension:D
{ \luatex_pdfextension:D restore \scan_stop: }
@@ -502,7 +601,7 @@
% There is a dedicated primitive/primitive interface for setting colors.
% As with scoping, this approach is not suitable for cached operations.
% \begin{macrocode}
-\cs_new_protected_nopar:Npx \@@_color_ensure_current:
+\cs_new_protected:Npx \@@_color_ensure_current:
{
\cs_if_exist:NTF \luatex_pdfextension:D
{ \luatex_pdfextension:D colorstack }
@@ -511,7 +610,7 @@
{ \exp_not:N \l_@@_current_color_tl }
\group_insert_after:N \exp_not:N \@@_color_reset:
}
-\cs_new_protected_nopar:Npx \@@_color_reset:
+\cs_new_protected:Npx \@@_color_reset:
{
\cs_if_exist:NTF \luatex_pdfextension:D
{ \luatex_pdfextension:D colorstack }
@@ -552,9 +651,9 @@
% \begin{macro}[int]{\@@_scope_begin:, \@@_scope_end:}
% Scoping is done using direct PDF operations here.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_scope_begin:
+\cs_new_protected:Npn \@@_scope_begin:
{ \@@_literal:n { q } }
-\cs_new_protected_nopar:Npn \@@_scope_end:
+\cs_new_protected:Npn \@@_scope_end:
{ \@@_literal:n { Q } }
% \end{macrocode}
% \end{macro}
@@ -591,12 +690,12 @@
% \begin{macro}[aux]{\@@_color_reset:}
% Directly set the color using the specials with optimisation support.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_color_ensure_current:
+\cs_new_protected:Npn \@@_color_ensure_current:
{
\tex_special:D { pdf:bcolor~\l_@@_current_color_tl }
\group_insert_after:N \@@_color_reset:
}
-\cs_new_protected_nopar:Npn \@@_color_reset:
+\cs_new_protected:Npn \@@_color_reset:
{ \tex_special:D { pdf:ecolor } }
% \end{macrocode}
% \end{macro}
@@ -624,12 +723,12 @@
{
\@ifpackageloaded { color }
{
- \cs_set_protected_nopar:Npn \@@_color_ensure_current:
+ \cs_set_protected:Npn \@@_color_ensure_current:
{
\tex_special:D { color~push~\l_@@_current_color_tl }
\group_insert_after:N \@@_color_reset:
}
- \cs_set_protected_nopar:Npn \@@_color_reset:
+ \cs_set_protected:Npn \@@_color_reset:
{ \tex_special:D { color~pop } }
}
{ }
@@ -753,9 +852,9 @@
% \begin{macro}[int]{\@@_draw_begin:, \@@_draw_end:}
% No special requirements here, so simply set up a drawing scope.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_draw_begin:
+\cs_new_protected:Npn \@@_draw_begin:
{ \@@_draw_scope_begin: }
-\cs_new_protected_nopar:Npn \@@_draw_end:
+\cs_new_protected:Npn \@@_draw_end:
{ \@@_draw_scope_end: }
% \end{macrocode}
% \end{macro}
@@ -764,15 +863,16 @@
% In contrast to a general scope, a drawing scope is always done using
% the PDF operators so is the same for all relevant drivers.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_draw_scope_begin:
+\cs_new_protected:Npn \@@_draw_scope_begin:
{ \@@_draw_literal:n { q } }
-\cs_new_protected_nopar:Npn \@@_draw_scope_end:
+\cs_new_protected:Npn \@@_draw_scope_end:
{ \@@_draw_literal:n { Q } }
% \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{\@@_draw_moveto:nn, \@@_draw_lineto:nn}
% \begin{macro}[int]{\@@_draw_curveto:nnnnnn}
+% \begin{macro}[int]{\@@_draw_rectangle:nnnn}
% Path creation operations all resolve directly to PDF primitive steps, with
% only the need to convert to \texttt{bp}. Notice that \texttt{x}-type
% expansion is included here to ensure that any variable values are
@@ -798,16 +898,32 @@
c
}
}
+\cs_new_protected:Npn \@@_draw_rectangle:nnnn #1#2#3#4
+ {
+ \@@_draw_literal:x
+ {
+ \dim_to_decimal_in_bp:n {#1} ~ \dim_to_decimal_in_bp:n {#2} ~
+ \dim_to_decimal_in_bp:n {#3} ~ \dim_to_decimal_in_bp:n {#4} ~
+ re
+ }
+ }
% \end{macrocode}
% \end{macro}
% \end{macro}
+% \end{macro}
%
-% \begin{variable}[int]{\@@_draw_eor_bool}
+% \begin{macro}[int]{\@@_draw_evenodd_rule:, \@@_draw_nonzero_rule:}
+% \begin{variable}[aux]{\g_@@_draw_eor_bool}
% The even-odd rule here can be implemented as a simply switch.
% \begin{macrocode}
-\bool_new:N \@@_draw_eor_bool
+\cs_new_protected:Npn \@@_draw_evenodd_rule:
+ { \bool_gset_true:N \g_@@_draw_eor_bool }
+\cs_new_protected:Npn \@@_draw_nonzero_rule:
+ { \bool_gset_false:N \g_@@_draw_eor_bool }
+\bool_new:N \g_@@_draw_eor_bool
% \end{macrocode}
% \end{variable}
+% \end{macro}
%
% \begin{macro}[int]
% {
@@ -822,28 +938,28 @@
% Converting paths to output is again a case of mapping directly to
% PDF operations.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_draw_closepath:
+\cs_new_protected:Npn \@@_draw_closepath:
{ \@@_draw_literal:n { h } }
-\cs_new_protected_nopar:Npn \@@_draw_stroke:
+\cs_new_protected:Npn \@@_draw_stroke:
{ \@@_draw_literal:n { S } }
-\cs_new_protected_nopar:Npn \@@_draw_closestroke:
+\cs_new_protected:Npn \@@_draw_closestroke:
{ \@@_draw_literal:n { s } }
-\cs_new_protected_nopar:Npn \@@_draw_fill:
+\cs_new_protected:Npn \@@_draw_fill:
{
\@@_draw_literal:x
- { f \bool_if:NT \@@_draw_eor_bool * }
+ { f \bool_if:NT \g_@@_draw_eor_bool * }
}
-\cs_new_protected_nopar:Npn \@@_draw_fillstroke:
+\cs_new_protected:Npn \@@_draw_fillstroke:
{
\@@_draw_literal:x
- { B \bool_if:NT \@@_draw_eor_bool * }
+ { B \bool_if:NT \g_@@_draw_eor_bool * }
}
-\cs_new_protected_nopar:Npn \@@_draw_clip:
+\cs_new_protected:Npn \@@_draw_clip:
{
\@@_draw_literal:x
- { W \bool_if:NT \@@_draw_eor_bool * }
+ { W \bool_if:NT \g_@@_draw_eor_bool * }
}
-\cs_new_protected_nopar:Npn \@@_draw_discardpath:
+\cs_new_protected:Npn \@@_draw_discardpath:
{ \@@_draw_literal:n { n } }
% \end{macrocode}
% \end{macro}
@@ -852,7 +968,7 @@
% \begin{macro}[aux]{\@@_draw_dash:n}
% \begin{macro}[int]{\@@_draw_linewidth:n}
% \begin{macro}[int]{\@@_draw_miterlimit:n}
-% \begin{macro}[int]^^A
+% \begin{macro}[int]
% {
% \@@_draw_cap_butt:, \@@_draw_cap_round:, \@@_draw_cap_rectangle:,
% \@@_draw_join_miter:, \@@_draw_join_round:, \@@_draw_join_bevel:
@@ -879,17 +995,17 @@
}
\cs_new_protected:Npn \@@_draw_miterlimit:n #1
{ \@@_draw_literal:x { \fp_eval:n {#1} ~ M } }
-\cs_new_protected_nopar:Npn \@@_draw_cap_butt:
+\cs_new_protected:Npn \@@_draw_cap_butt:
{ \@@_draw_literal:n { 0 ~ J } }
-\cs_new_protected_nopar:Npn \@@_draw_cap_round:
+\cs_new_protected:Npn \@@_draw_cap_round:
{ \@@_draw_literal:n { 1 ~ J } }
-\cs_new_protected_nopar:Npn \@@_draw_cap_rectangle:
+\cs_new_protected:Npn \@@_draw_cap_rectangle:
{ \@@_draw_literal:n { 2 ~ J } }
-\cs_new_protected_nopar:Npn \@@_draw_join_miter:
+\cs_new_protected:Npn \@@_draw_join_miter:
{ \@@_draw_literal:n { 0 ~ j } }
-\cs_new_protected_nopar:Npn \@@_draw_join_round:
+\cs_new_protected:Npn \@@_draw_join_round:
{ \@@_draw_literal:n { 1 ~ j } }
-\cs_new_protected_nopar:Npn \@@_draw_join_bevel:
+\cs_new_protected:Npn \@@_draw_join_bevel:
{ \@@_draw_literal:n { 2 ~ j } }
% \end{macrocode}
% \end{macro}
@@ -899,14 +1015,45 @@
% \end{macro}
%
% \begin{macro}[int]
-% {\@@_draw_color_cmyk_fill:nnnn, \@@_draw_color_cmyk_stroke:nnnn}
+% {
+% \@@_draw_color_cmyk:nnnn ,
+% \@@_draw_color_cmyk_fill:nnnn ,
+% \@@_draw_color_cmyk_stroke:nnnn
+% }
+% \begin{macro}[aux]{\@@_draw_color_cmyk_aux:nnnn}
% \begin{macro}[int]
-% {\@@_draw_color_gray_fill:n, \@@_draw_color_gray_stroke:n}
+% {
+% \@@_draw_color_gray:n ,
+% \@@_draw_color_gray_fill:n ,
+% \@@_draw_color_gray_stroke:n
+% }
+% \begin{macro}[aux]{\@@_draw_color_gray_aux:n}
% \begin{macro}[int]
-% {\@@_draw_color_rgb_fill:nnn, \@@_draw_color_rgb_stroke:nnn}
+% {
+% \@@_draw_color_rgb:nnn ,
+% \@@_draw_color_rgb_fill:nnn ,
+% \@@_draw_color_rgb_stroke:nnn
+% }
+% \begin{macro}[aux]{\@@_draw_color_rgb_aux:nnn}
% Yet more fast conversion, all using the FPU to allow for expressions
% in numerical input.
% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_color_cmyk:nnnn #1#2#3#4
+ {
+ \use:x
+ {
+ \@@_draw_color_cmyk_aux:nnnn
+ { \fp_eval:n {#1} }
+ { \fp_eval:n {#2} }
+ { \fp_eval:n {#3} }
+ { \fp_eval:n {#4} }
+ }
+ }
+\cs_new_protected:Npn \@@_draw_color_cmyk_aux:nnnn #1#2#3#4
+ {
+ \@@_draw_literal:n
+ { #1 ~ #2 ~ #3 ~ #4 ~ k ~ #1 ~ #2 ~ #3 ~ #4 ~ K }
+ }
\cs_new_protected:Npn \@@_draw_color_cmyk_fill:nnnn #1#2#3#4
{
\@@_draw_literal:x
@@ -925,10 +1072,34 @@
K
}
}
+\cs_new_protected:Npn \@@_draw_color_gray:n #1
+ {
+ \use:x
+ { \@@_draw_color_gray_aux:n { \fp_eval:n {#1} } }
+ }
+\cs_new_protected:Npn \@@_draw_color_gray_aux:n #1
+ {
+ \@@_draw_literal:n { #1 ~ g ~ #1 ~ G }
+ }
\cs_new_protected:Npn \@@_draw_color_gray_fill:n #1
{ \@@_draw_literal:x { \fp_eval:n {#1} ~ g } }
\cs_new_protected:Npn \@@_draw_color_gray_stroke:n #1
{ \@@_draw_literal:x { \fp_eval:n {#1} ~ G } }
+\cs_new_protected:Npn \@@_draw_color_rgb:nnn #1#2#3
+ {
+ \use:x
+ {
+ \@@_draw_color_rgb_aux:nnn
+ { \fp_eval:n {#1} }
+ { \fp_eval:n {#2} }
+ { \fp_eval:n {#3} }
+ }
+ }
+\cs_new_protected:Npn \@@_draw_color_rgb_aux:nnn #1#2#3
+ {
+ \@@_draw_literal:n
+ { #1 ~ #2 ~ #3 ~ rg ~ #1 ~ #2 ~ #3 ~ RG }
+ }
\cs_new_protected:Npn \@@_draw_color_rgb_fill:nnn #1#2#3
{
\@@_draw_literal:x
@@ -943,6 +1114,9 @@
% \end{macro}
% \end{macro}
% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
%
% \begin{macro}[int]{\@@_draw_transformcm:nnnnnn}
% The first four arguments here are floats (the affine matrix), the last
@@ -1027,11 +1201,12 @@
%
% \begin{macro}{\@@_scope_begin:, \@@_scope_end:}
% Scope saving/restoring is done directly with no need to worry about the
-% transformation matrix.
+% transformation matrix. General scoping is only for the graphics stack so
+% the lower-cost |gsave|/|grestore| pair are used.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_scope_begin:
+\cs_new_protected:Npn \@@_scope_begin:
{ \tex_special:D { ps:gsave } }
-\cs_new_protected_nopar:Npn \@@_scope_end:
+\cs_new_protected:Npn \@@_scope_end:
{ \tex_special:D { ps:grestore } }
% \end{macrocode}
% \end{macro}
@@ -1145,17 +1320,442 @@
% \begin{macro}[aux]{\@@_color_reset:}
% Directly set the color using the specials: no optimisation here.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_color_ensure_current:
+\cs_new_protected:Npn \@@_color_ensure_current:
{
\tex_special:D { color~push~\l_@@_current_color_tl }
\group_insert_after:N \@@_color_reset:
}
-\cs_new_protected_nopar:Npn \@@_color_reset:
+\cs_new_protected:Npn \@@_color_reset:
{ \tex_special:D { color~pop } }
% \end{macrocode}
% \end{macro}
% \end{macro}
%
+% \subsection{Drawing}
+%
+% \begin{macro}[aux]{\@@_draw_literal:n, \@@_draw_literal:x}
+% Literals with no positioning (using |ps:| each one is positioned but
+% cut of from everything else, so no good for the stepwise approach needed
+% here).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_literal:n #1
+ { \tex_special:D { ps:: ~ #1 } }
+\cs_generate_variant:Nn \@@_draw_literal:n { x }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_draw_begin:, \@@_draw_end:}
+% The |ps::[begin]| special here deals with positioning but allows us to
+% continue on to a matching |ps::[end]|: contrast with |ps:|, which positions
+% but where we can't split material between separate calls. The
+% |@beginspecial|/|@endspecial| pair are from |special.pro| and correct the
+% scale and $y$-axis direction. The reference point at the start of the box
+% is saved (as |l3x|/|l3y|) as it is needed when inserting various items.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_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 }
+ }
+\cs_new_protected:Npn \@@_draw_end:
+ {
+ \tex_special:D { ps::~@endspecial }
+ \tex_special:D { ps::~restore }
+ \tex_special:D { ps::[end] }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_draw_scope_begin:, \@@_draw_scope_end:}
+% Scope here may need to contain saved definitions, so the entire memory
+% rather than just the graphic state has to be sent to the stack.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_scope_begin:
+ { \@@_draw_literal:n { save } }
+\cs_new_protected:Npn \@@_draw_scope_end:
+ { \@@_draw_literal:n { restore } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_draw_moveto:nn, \@@_draw_lineto:nn}
+% \begin{macro}[int]{\@@_draw_rectangle:nnnn}
+% \begin{macro}[int]{\@@_draw_curveto:nnnnnn}
+% Path creation operations mainly resolve directly to PostScript primitive
+% steps, with only the need to convert to \texttt{bp}. Notice that
+% \texttt{x}-type expansion is included here to ensure that any variable
+% values are forced to literals before any possible caching. There is
+% no native rectangular path command (without also clipping, filling or
+% stroking), so that task is done using a small amount of PostScript.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_moveto:nn #1#2
+ {
+ \@@_draw_literal:x
+ { \dim_to_decimal_in_bp:n {#1} ~ \dim_to_decimal_in_bp:n {#2} ~ moveto }
+ }
+\cs_new_protected:Npn \@@_draw_lineto:nn #1#2
+ {
+ \@@_draw_literal:x
+ { \dim_to_decimal_in_bp:n {#1} ~ \dim_to_decimal_in_bp:n {#2} ~ lineto }
+ }
+\cs_new_protected:Npn \@@_draw_rectangle:nnnn #1#2#3#4
+ {
+ \@@_draw_literal:x
+ {
+ \dim_to_decimal_in_bp:n {#4} ~ \dim_to_decimal_in_bp:n {#3} ~
+ \dim_to_decimal_in_bp:n {#1} ~ \dim_to_decimal_in_bp:n {#2} ~
+ moveto~dup~0~rlineto~exch~0~exch~rlineto~neg~0~rlineto~clospath
+ }
+ }
+\cs_new_protected:Npn \@@_draw_curveto:nnnnnn #1#2#3#4#5#6
+ {
+ \@@_draw_literal:x
+ {
+ \dim_to_decimal_in_bp:n {#1} ~ \dim_to_decimal_in_bp:n {#2} ~
+ \dim_to_decimal_in_bp:n {#3} ~ \dim_to_decimal_in_bp:n {#4} ~
+ \dim_to_decimal_in_bp:n {#5} ~ \dim_to_decimal_in_bp:n {#6} ~
+ curveto
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_draw_evenodd_rule:, \@@_draw_nonzero_rule:}
+% \begin{variable}[aux]{\g_@@_draw_eor_bool}
+% The even-odd rule here can be implemented as a simply switch.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_evenodd_rule:
+ { \bool_gset_true:N \g_@@_draw_eor_bool }
+\cs_new_protected:Npn \@@_draw_nonzero_rule:
+ { \bool_gset_false:N \g_@@_draw_eor_bool }
+\bool_new:N \g_@@_draw_eor_bool
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \begin{macro}[int]
+% {
+% \@@_draw_closepath: ,
+% \@@_draw_stroke: ,
+% \@@_draw_closestroke: ,
+% \@@_draw_fill: ,
+% \@@_draw_fillstroke: ,
+% \@@_draw_clip: ,
+% \@@_draw_discardpath:
+% }
+% \begin{variable}[aux]{\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).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_closepath:
+ { \@@_draw_literal:n { closepath } }
+\cs_new_protected:Npn \@@_draw_stroke:
+ {
+ \@@_draw_literal:n { currentdict~/l3sc~known~{gsave~l3sc}~if }
+ \@@_draw_literal:n { stroke }
+ \@@_draw_literal:n { currentdict~/l3sc~known~{grestore}~if }
+ \bool_if:NT \g_@@_draw_clip_bool
+ {
+ \@@_draw_literal:x
+ {
+ \bool_if:NT \g_@@_draw_eor_bool { eo }
+ clip
+ }
+ }
+ \@@_draw_literal:n { newpath }
+ \bool_gset_false:N \g_@@_draw_clip_bool
+ }
+\cs_new_protected:Npn \@@_draw_closestroke:
+ {
+ \@@_draw_closepath:
+ \@@_draw_stroke:
+ }
+\cs_new_protected:Npn \@@_draw_fill:
+ {
+ \@@_draw_literal:n { currentdict~/l3fc~known~{gsave~l3fc}~if }
+ \@@_draw_literal:x
+ {
+ \bool_if:NT \g_@@_draw_eor_bool { eo }
+ fill
+ }
+ \@@_draw_literal:n { currentdict~/l3fc~known~{grestore}~if }
+ \bool_if:NT \g_@@_draw_clip_bool
+ {
+ \@@_draw_literal:x
+ {
+ \bool_if:NT \g_@@_draw_eor_bool { eo }
+ clip
+ }
+ }
+ \@@_draw_literal:n { newpath }
+ \bool_gset_false:N \g_@@_draw_clip_bool
+ }
+\cs_new_protected:Npn \@@_draw_fillstroke:
+ {
+ \@@_draw_literal:n { currentdict~/l3fc~known~{gsave~l3fc}~if }
+ \@@_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 { stroke }
+ \@@_draw_literal:n { currentdict~/l3sc~known~{grestore}~if }
+ \bool_if:NT \g_@@_draw_clip_bool
+ {
+ \@@_draw_literal:x
+ {
+ \bool_if:NT \g_@@_draw_eor_bool { eo }
+ clip
+ }
+ }
+ \@@_draw_literal:n { newpath }
+ \bool_gset_false:N \g_@@_draw_clip_bool
+ }
+\cs_new_protected:Npn \@@_draw_clip:
+ { \bool_gset_true:N \g_@@_draw_clip_bool }
+\bool_new:N \g_@@_draw_clip_bool
+\cs_new_protected:Npn \@@_draw_discardpath:
+ {
+ \bool_if:NT \g_@@_draw_clip_bool
+ {
+ \@@_draw_literal:x
+ {
+ \bool_if:NT \g_@@_draw_eor_bool { eo }
+ clip
+ }
+ }
+ \@@_draw_literal:n { newpath }
+ \bool_gset_false:N \g_@@_draw_clip_bool
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_draw_dash:nn}
+% \begin{macro}[aux]{\@@_draw_dash:n}
+% \begin{macro}[int]{\@@_draw_linewidth:n}
+% \begin{macro}[int]{\@@_draw_miterlimit:n}
+% \begin{macro}[int]
+% {
+% \@@_draw_cap_butt:, \@@_draw_cap_round:, \@@_draw_cap_rectangle:,
+% \@@_draw_join_miter:, \@@_draw_join_round:, \@@_draw_join_bevel:
+% }
+% Converting paths to output is again a case of mapping directly to
+% PostScript operations.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_dash:nn #1#2
+ {
+ \@@_draw_literal:x
+ {
+ [ ~
+ \clist_map_function:nN {#1} \@@_draw_dash:n
+ ] ~
+ \dim_to_decimal_in_bp:n {#2} ~ setdash
+ }
+ }
+\cs_new:Npn \@@_draw_dash:n #1
+ { \dim_to_decimal_in_bp:n {#1} ~ }
+\cs_new_protected:Npn \@@_draw_linewidth:n #1
+ {
+ \@@_draw_literal:x
+ { \dim_to_decimal_in_bp:n {#1} ~ setlinewidth }
+ }
+\cs_new_protected:Npn \@@_draw_miterlimit:n #1
+ { \@@_draw_literal:x { \fp_eval:n {#1} ~ setmiterlimit } }
+\cs_new_protected:Npn \@@_draw_cap_butt:
+ { \@@_draw_literal:n { 0 ~ setlinecap } }
+\cs_new_protected:Npn \@@_draw_cap_round:
+ { \@@_draw_literal:n { 1 ~ setlinecap } }
+\cs_new_protected:Npn \@@_draw_cap_rectangle:
+ { \@@_draw_literal:n { 2 ~ setlinecap } }
+\cs_new_protected:Npn \@@_draw_join_miter:
+ { \@@_draw_literal:n { 0 ~ setlinejoin } }
+\cs_new_protected:Npn \@@_draw_join_round:
+ { \@@_draw_literal:n { 1 ~ setlinejoin } }
+\cs_new_protected:Npn \@@_draw_join_bevel:
+ { \@@_draw_literal:n { 2 ~ setlinejoin } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[aux]{\_@@_draw_color_reset:}
+% \begin{macro}[int]
+% {
+% \@@_draw_color_cmyk:nnnn ,
+% \@@_draw_color_cmyk_fill:nnnn ,
+% \@@_draw_color_cmyk_stroke:nnnn
+% }
+% \begin{macro}[int]
+% {
+% \@@_draw_color_gray:n ,
+% \@@_draw_color_gray_fill:n ,
+% \@@_draw_color_gray_stroke:n
+% }
+% \begin{macro}[int]
+% {
+% \@@_draw_color_rgb:nnn ,
+% \@@_draw_color_rgb_fill:nnn ,
+% \@@_draw_color_rgb_stroke: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{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 \@@_draw_color_cmyk:nnnn #1#2#3#4
+ {
+ \@@_draw_literal:x
+ {
+ \fp_eval:n {#1} ~ \fp_eval:n {#2} ~
+ \fp_eval:n {#3} ~ \fp_eval:n {#4} ~
+ setcmykcolor ~
+ }
+ \@@_draw_color_reset:
+ }
+\cs_new_protected:Npn \@@_draw_color_cmyk_fill:nnnn #1#2#3#4
+ {
+ \@@_draw_literal:x
+ {
+ /l3fc ~
+ {
+ \fp_eval:n {#1} ~ \fp_eval:n {#2} ~
+ \fp_eval:n {#3} ~ \fp_eval:n {#4} ~
+ setcmykcolor
+ } ~
+ def
+ }
+ }
+\cs_new_protected:Npn \@@_draw_color_cmyk_stroke:nnnn #1#2#3#4
+ {
+ \__driver_draw_literal:x
+ {
+ /l3sc ~
+ {
+ \fp_eval:n {#1} ~ \fp_eval:n {#2} ~
+ \fp_eval:n {#3} ~ \fp_eval:n {#4} ~
+ setcmykcolor
+ } ~
+ def
+ }
+ }
+\cs_new_protected:Npn \@@_draw_color_gray:n #1
+ {
+ \@@_draw_literal:x { fp_eval:n {#1} ~ setgray }
+ \@@_draw_color_reset:
+ }
+\cs_new_protected:Npn \@@_draw_color_gray_fill:n #1
+ { \@@_draw_literal:x { /l3fc ~ { \fp_eval:n {#1} ~ setgray } ~ def } }
+\cs_new_protected:Npn \@@_draw_color_gray_stroke:n #1
+ { \@@_draw_literal:x { /l3sc ~ { \fp_eval:n {#1} ~ setgray } ~ def } }
+\cs_new_protected:Npn \@@_draw_color_rgb:nnn #1#2#3
+ {
+ \@@_draw_literal:x
+ {
+ \fp_eval:n {#1} ~ \fp_eval:n {#2} ~ \fp_eval:n {#3} ~
+ setrgbcolor
+ }
+ \@@_draw_color_reset:
+ }
+\cs_new_protected:Npn \@@_draw_color_rgb_fill:nnn #1#2#3
+ {
+ \@@_draw_literal:x
+ {
+ /l3fc ~
+ {
+ \fp_eval:n {#1} ~ \fp_eval:n {#2} ~ \fp_eval:n {#3} ~
+ setrgbcolor
+ } ~
+ def
+ }
+ }
+\cs_new_protected:Npn \@@_draw_color_rgb_stroke:nnn #1#2#3
+ {
+ \@@_draw_literal:x
+ {
+ /l3sc ~
+ {
+ \fp_eval:n {#1} ~ \fp_eval:n {#2} ~ \fp_eval:n {#3} ~
+ setrgbcolor
+ } ~
+ def
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_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{macrocode}
+\cs_new_protected:Npn \@@_draw_transformcm:nnnnnn #1#2#3#4#5#6
+ {
+ \@@_draw_literal:x
+ {
+ [
+ \fp_eval:n {#1} ~ \fp_eval:n {#2} ~
+ \fp_eval:n {#3} ~ \fp_eval:n {#4} ~
+ \dim_to_decimal_in_bp:n {#5} ~ \dim_to_decimal_in_bp:n {#6} ~
+ ] ~
+ concat
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_draw_hbox:Nnnnnnn}
+% Inside a picture |@beginspecial|/|@endspecial| are active, which is
+% normally a good thing but means that the position and scaling will be off
+% if the box is inserted directly. Instead, we need to reverse the effect of
+% the (normally desirable) shift/scaling within the box. That requires
+% knowing where the reference point for the drawing is: saved as |l3x|/|l3y|
+% at the start of the picture. Transformation here is relative to the
+% drawing origin so has to be done purely in driver code not using \TeX{}
+% offsets.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_hbox:Nnnnnnn #1#2#3#4#5#6#7
+ {
+ \@@_scope_begin:
+ \tex_special:D { ps::[end] }
+ \@@_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 }
+ \group_begin:
+ \box_set_wd:Nn #1 { 0pt }
+ \box_set_ht:Nn #1 { 0pt }
+ \box_set_dp:Nn #1 { 0pt }
+ \box_use:N #1
+ \group_end:
+ \tex_special:D { ps::[begin] }
+ \@@_scope_end:
+ }
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macrocode}
%</dvips>
% \end{macrocode}
@@ -1184,9 +1784,9 @@
% A scope in SVG terms is slightly different to the other drivers as
% operations have to be \enquote{tied} to these not simply inside them.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_scope_begin:
+\cs_new_protected:Npn \@@_scope_begin:
{ \@@_literal:n { <g> } }
-\cs_new_protected_nopar:Npn \@@_scope_end:
+\cs_new_protected:Npn \@@_scope_end:
{ \@@_literal:n { </g> } }
% \end{macrocode}
% \end{macro}
@@ -1352,17 +1952,469 @@
% \begin{macro}[aux]{\@@_color_reset:}
% Directly set the color: same as \texttt{dvips}.
% \begin{macrocode}
-\cs_new_protected_nopar:Npn \@@_color_ensure_current:
+\cs_new_protected:Npn \@@_color_ensure_current:
{
\tex_special:D { color~push~\l_@@_current_color_tl }
\group_insert_after:N \@@_color_reset:
}
-\cs_new_protected_nopar:Npn \@@_color_reset:
+\cs_new_protected:Npn \@@_color_reset:
{ \tex_special:D { color~pop } }
% \end{macrocode}
% \end{macro}
% \end{macro}
%
+% \subsection{Drawing}
+%
+% \begin{macro}[aux]{\@@_draw_literal:n, \@@_draw_literal:x}
+% The same as the more general literal call.
+% \begin{macrocode}
+\cs_new_eq:NN \@@_draw_literal:n \@@_literal:n
+\cs_generate_variant:Nn \@@_draw_literal:n { x }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_draw_begin:, \@@_draw_end:}
+% A drawing needs to be set up such that the co-ordinate system is
+% translated. That is done inside a scope, which as described below
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_begin:
+ {
+ \@@_draw_scope_begin:
+ \@@_draw_scope:n { transform="translate({?x},{?y})~scale(1,-1)" }
+ }
+\cs_new_protected:Npn \@@_draw_end:
+ { \@@_draw_scope_end: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_draw_scope_begin:, \@@_draw_scope_end:}
+% \begin{macro}[aux]{\@@_draw_scope:n, \@@_draw_scope:x}
+% \begin{variable}[aux]{\g_@@_draw_scope_int, \l_@@_draw_scope_int}
+% Several settings that with other drivers are \enquote{stand alone} have
+% to be given as part of a scope in SVG. As a result, there is a need to
+% provide a mechanism to automatically close these extra scopes. That is
+% done using a dedicated function and a pair of tracking variables. Within
+% each graphics scope we use a global variable to do the work, with a group
+% used to save the value between scopes. The result is that no direct action
+% is needed when creating a scope.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_scope_begin:
+ {
+ \int_set_eq:NN
+ \l_@@_draw_scope_int
+ \g_@@_draw_scope_int
+ \group_begin:
+ \int_gset:Nn \g_@@_draw_scope_int { 0 }
+ }
+\cs_new_protected:Npn \@@_draw_scope_end:
+ {
+ \prg_replicate:nn
+ { \g_@@_draw_scope_int }
+ { \@@_draw_literal:n { </g> } }
+ \group_end:
+ \int_gset_eq:NN
+ \g_@@_draw_scope_int
+ \l_@@_draw_scope_int
+ }
+\cs_new_protected:Npn \@@_draw_scope:n #1
+ {
+ \@@_draw_literal:n { <g~ #1 > }
+ \int_gincr:N \g_@@_draw_scope_int
+ }
+\cs_generate_variant:Nn \@@_draw_scope:n { x }
+\int_new:N \g_@@_draw_scope_int
+\int_new:N \l_@@_draw_scope_int
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_draw_moveto:nn, \@@_draw_lineto:nn}
+% \begin{macro}[int]{\@@_draw_rectangle:nnnn}
+% \begin{macro}[int]{\@@_draw_curveto:nnnnnn}
+% \begin{macro}[aux]{\@@_draw_add_to_path:n}
+% \begin{variable}[aux]{\g_@@_draw_path_tl}
+% Once again, some work is needed to get path constructs correct. Rather
+% then write the values as they are given, the entire path needs to be
+% collected up before being output in one go. For that we use a dedicated
+% storage routine, which will add spaces as required. Since paths should
+% be fully expanded there is no need to worry about the internal
+% \texttt{x}-type expansion.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_moveto:nn #1#2
+ {
+ \@@_draw_add_to_path:n
+ { M ~ \dim_to_decimal:n {#1} ~ \dim_to_decimal:n {#2} }
+ }
+\cs_new_protected:Npn \@@_draw_lineto:nn #1#2
+ {
+ \@@_draw_add_to_path:n
+ { L ~ \dim_to_decimal:n {#1} ~ \dim_to_decimal:n {#2} }
+ }
+\cs_new_protected:Npn \@@_draw_rectangle:nnnn #1#2#3#4
+ {
+ \@@_draw_add_to_path:n
+ {
+ M ~ \dim_to_decimal:n {#1} ~ \dim_to_decimal:n {#2}
+ h ~ \dim_to_decimal:n {#3} ~
+ v ~ \dim_to_decimal:n {#4} ~
+ h ~ \dim_to_decimal:n { -#3 } ~
+ Z
+ }
+ }
+\cs_new_protected:Npn \@@_draw_curveto:nnnnnn #1#2#3#4#5#6
+ {
+ \@@_draw_add_to_path:n
+ {
+ C ~
+ \dim_to_decimal:n {#1} ~ \dim_to_decimal:n {#2} ~
+ \dim_to_decimal:n {#3} ~ \dim_to_decimal:n {#4} ~
+ \dim_to_decimal:n {#5} ~ \dim_to_decimal:n {#6}
+ }
+ }
+\cs_new_protected:Npn \@@_draw_add_to_path:n #1
+ {
+ \tl_gset:Nx \g_@@_draw_path_tl
+ {
+ \g_@@_draw_path_tl
+ \tl_if_empty:NF \g_@@_draw_path_tl { \c_space_tl }
+ #1
+ }
+ }
+\tl_new:N \g_@@_draw_path_tl
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_draw_evenodd_rule:, \@@_draw_nonzero_rule:}
+% The fill rules here have to be handled as scopes.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_evenodd_rule:
+ { \@@_draw_scope:n { fill-rule="evenodd" } }
+\cs_new_protected:Npn \@@_draw_nonzero_rule:
+ { \@@_draw_scope:n { fill-rule="nonzero" } }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[aux]{\@@_draw_path:n}
+% \begin{macro}[int]
+% {
+% \@@_draw_closepath: ,
+% \@@_draw_stroke: ,
+% \@@_draw_closestroke: ,
+% \@@_draw_fill: ,
+% \@@_draw_fillstroke: ,
+% \@@_draw_clip: ,
+% \@@_draw_discardpath:
+% }
+% \begin{variable}[aux]{\g_@@_draw_clip_bool}
+% \begin{variable}[aux]{\g_@@_draw_path_int}
+% Setting fill and stroke effects and doing clipping all has to be done using
+% scopes. This means setting up the various requirements in a shared
+% auxiliary which deals with the bits and pieces. Clipping paths are reused
+% for path drawing: not essential but avoids constructing them twice.
+% Discarding a path needs a separate function as it's not quite the same.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_closepath:
+ { \@@_draw_add_to_path:n { Z } }
+\cs_new_protected:Npn \@@_draw_path:n #1
+ {
+ \bool_if:NTF \g_@@_draw_clip_bool
+ {
+ \int_gincr:N \g_@@_clip_path_int
+ \@@_draw_literal:x
+ {
+ < clipPath~id = " l3cp \int_use:N \g_@@_clip_path_int " >
+ { ?nl }
+ <path~d=" \g_@@_draw_path_tl "/> { ?nl }
+ < /clipPath > { ? nl }
+ <
+ use~xlink:href =
+ "\c_hash_str l3path \int_use:N \g_@@_path_int " ~
+ #1
+ />
+ }
+ \@@_draw_scope:x
+ {
+ clip-path =
+ "url( \c_hash_str l3cp \int_use:N \g_@@_clip_path_int)"
+ }
+ }
+ {
+ \@@_draw_literal:x
+ { <path ~ d=" \g_@@_draw_path_tl " ~ #1 /> }
+ }
+ \tl_gclear:N \g_@@_draw_path_tl
+ \bool_gset_false:N \g_@@_draw_clip_bool
+ }
+\int_new:N \g_@@_path_int
+\cs_new_protected:Npn \@@_draw_stroke:
+ { \@@_draw_path:n { style="fill:none" } }
+\cs_new_protected:Npn \@@_draw_closestroke:
+ {
+ \@@_draw_closepath:
+ \@@_draw_stroke:
+ }
+\cs_new_protected:Npn \@@_draw_fill:
+ { \@@_draw_path:n { style="stroke:none" } }
+\cs_new_protected:Npn \@@_draw_fillstroke:
+ { \@@_draw_path:n { } }
+\cs_new_protected:Npn \@@_draw_clip:
+ { \bool_gset_true:N \g_@@_draw_clip_bool }
+\bool_new:N \g_@@_draw_clip_bool
+\cs_new_protected:Npn \@@_draw_discardpath:
+ {
+ \bool_if:NT \g_@@_draw_clip_bool
+ {
+ \int_gincr:N \g_@@_clip_path_int
+ \@@_draw_literal:x
+ {
+ < clipPath~id = " l3cp \int_use:N \g_@@_clip_path_int " >
+ { ?nl }
+ <path~d=" \g_@@_draw_path_tl "/> { ?nl }
+ < /clipPath >
+ }
+ \@@_draw_scope:x
+ {
+ clip-path =
+ "url( \c_hash_str l3cp \int_use:N \g_@@_clip_path_int)"
+ }
+ }
+ \tl_gclear:N \g_@@_draw_path_tl
+ \bool_gset_false:N \g_@@_draw_clip_bool
+ }
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_draw_dash:nn}
+% \begin{macro}[aux]{\@@_draw_dash:n}
+% \begin{macro}[aux]{\@@_draw_dash_aux:nn}
+% \begin{macro}[int]{\@@_draw_linewidth:n}
+% \begin{macro}[int]{\@@_draw_miterlimit:n}
+% \begin{macro}[int]
+% {
+% \@@_draw_cap_butt:, \@@_draw_cap_round:, \@@_draw_cap_rectangle:,
+% \@@_draw_join_miter:, \@@_draw_join_round:, \@@_draw_join_bevel:
+% }
+% All of these ideas are properties of scopes in SVG. The only slight
+% complexity is converting the dash array properly (doing any required
+% maths).
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_dash:nn #1#2
+ {
+ \use:x
+ {
+ \@@_draw_dash_aux:nn
+ { \clist_map_function:nn {#1} \@@_draw_dash:n }
+ { \dim_to_decimal:n {#2} }
+ }
+ }
+\cs_new:Npn \@@_draw_dash:n #1
+ { , \dim_to_decimal_in_bp:n {#1} }
+\cs_new_protected:Npn \@@_draw_dash_aux:nn #1#2
+ {
+ \@@_draw_scope:x
+ {
+ stroke-dasharray =
+ "
+ \tl_if_empty:oTF { \use_none:n #1 }
+ { none }
+ { \use_none:n #1 }
+ " ~
+ stroke-offset=" #2 "
+ }
+ }
+\cs_new_protected:Npn \@@_draw_linewidth:n #1
+ { \@@_draw_scope:x { stroke-width=" \dim_to_decimal:n {#1} " } }
+\cs_new_protected:Npn \@@_draw_miterlimit:n #1
+ { \@@_draw_scope:x { stroke-miterlimit=" \fp_eval:n {#1} " } }
+\cs_new_protected:Npn \@@_draw_cap_butt:
+ { \@@_draw_scope:n { stroke-linecap="butt" } }
+\cs_new_protected:Npn \@@_draw_cap_round:
+ { \@@_draw_scope:n { stroke-linecap="round" } }
+\cs_new_protected:Npn \@@_draw_cap_rectangle:
+ { \@@_draw_scope:n { stroke-linecap="square" } }
+\cs_new_protected:Npn \@@_draw_join_miter:
+ { \@@_draw_scope:n { stroke-linejoin="miter" } }
+\cs_new_protected:Npn \@@_draw_join_round:
+ { \@@_draw_scope:n { stroke-linejoin="round" } }
+\cs_new_protected:Npn \@@_draw_join_bevel:
+ { \@@_draw_scope:n { stroke-linejoin="bevel" } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[int]
+% {
+% \@@_draw_color_cmyk:nnnn ,
+% \@@_draw_color_cmyk_fill:nnnn ,
+% \@@_draw_color_cmyk_stroke:nnnn
+% }
+% \begin{macro}[int]
+% {
+% \@@_draw_color_gray:n ,
+% \@@_draw_color_gray_fill:n ,
+% \@@_draw_color_gray_stroke:n
+% }
+% \begin{macro}[int]
+% {
+% \@@_draw_color_rgb:nnn ,
+% \@@_draw_color_rgb_fill:nnn ,
+% \@@_draw_color_rgb_stroke: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{macrocode}
+\cs_new_protected:Npn \@@_draw_color_cmyk_aux:NNnnnnn #1#2#3#4#5#6
+ {
+ \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 ) } }
+ }
+ #1 #2
+ }
+\cs_new_protected:Npn \@@_draw_color_cmyk:nnnn
+ { \@@_draw_color_cmyk_aux:NNnnnnn \c_true_bool \c_true_bool }
+\cs_new_protected:Npn \@@_draw_color_cmyk_fill:nnnn
+ { \@@_draw_color_cmyk_aux:NNnnnnn \c_false_bool \c_true_bool }
+\cs_new_protected:Npn \@@_draw_color_cmyk_stroke:nnnn
+ { \@@_draw_color_cmyk_aux:NNnnnnn \c_true_bool \c_false_bool }
+\cs_new_protected:Npn \@@_draw_color_gray_aux:NNn #1#2#3
+ {
+ \use:x
+ {
+ \@@_draw_color_gray_aux:nNN
+ { \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 \@@_draw_color_gray:n
+ { \@@_draw_color_gray_aux:NNn \c_true_bool \c_true_bool }
+\cs_new_protected:Npn \@@_draw_color_gray_fill:n
+ { \@@_draw_color_gray_aux:NNn \c_false_bool \c_true_bool }
+\cs_new_protected:Npn \@@_draw_color_gray_stroke: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
+ {
+ \use:x
+ {
+ \@@_draw_color_rgb_auxii:nnnNN
+ { \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
+ {
+ \@@_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
+ )
+ "
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_draw_color_rgb:nnn
+ { \@@_draw_color_rgb_auxi:NNnnn \c_true_bool \c_true_bool }
+\cs_new_protected:Npn \@@_draw_color_rgb_fill:nnn
+ { \@@_draw_color_rgb_auxi:NNnnn \c_false_bool \c_true_bool }
+\cs_new_protected:Npn \@@_draw_color_rgb_stroke:nnn
+ { \@@_draw_color_rgb_auxi:NNnnn \c_true_bool \c_false_bool }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_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{macrocode}
+\cs_new_protected:Npn \@@_draw_transformcm:nnnnnn #1#2#3#4#5#6
+ {
+ \@@_draw_scope:x
+ {
+ transform =
+ "
+ matrix
+ (
+ \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}
+ )
+ "
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[int]{\@@_draw_hbox:Nnnnnnn}
+% No special savings can be made here: simply displace the box inside
+% a scope. As there is nothing to re-box, just make the box passed of
+% zero size.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_draw_hbox:Nnnnnnn #1#2#3#4#5#6#7
+ {
+ \@@_scope_begin:
+ \@@_draw_transformcm:nnnnnn {#2} {#3} {#4} {#5} {#6} {#7}
+ \@@_literal:n
+ {
+ < g~
+ stroke="none"~
+ transform="scale(-1,1)~translate({?x},{?y})~scale(-1,-1)"
+ >
+ }
+ \group_begin:
+ \box_set_wd:Nn #1 { 0pt }
+ \box_set_ht:Nn #1 { 0pt }
+ \box_set_dp:Nn #1 { 0pt }
+ \box_use:N #1
+ \group_end:
+ \@@_literal:n { </g> }
+ \@@_scope_end:
+ }
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macrocode}
%</dvisvgm>
% \end{macrocode}