summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx2828
1 files changed, 2828 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx b/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx
new file mode 100644
index 00000000000..8afac7cda1f
--- /dev/null
+++ b/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx
@@ -0,0 +1,2828 @@
+% \iffalse meta-comment
+%
+%% File: l3candidates.dtx Copyright(C) 2012 The LaTeX3 Project
+%%
+%% It may be distributed and/or modified under the conditions of the
+%% LaTeX Project Public License (LPPL), either version 1.3c of this
+%% license or (at your option) any later version. The latest version
+%% of this license is in the file
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% This file is part of the "l3kernel bundle" (The Work in LPPL)
+%% and all files in that bundle must be distributed together.
+%%
+%% The released version of this bundle is available from CTAN.
+%%
+%% -----------------------------------------------------------------------
+%%
+%% The development version of the bundle can be found at
+%%
+%% http://www.latex-project.org/svnroot/experimental/trunk/
+%%
+%% for those people who are interested.
+%%
+%%%%%%%%%%%
+%% NOTE: %%
+%%%%%%%%%%%
+%%
+%% Snapshots taken from the repository represent work in progress and may
+%% not work or may contain conflicting material! We therefore ask
+%% people _not_ to put them into distributions, archives, etc. without
+%% prior consultation with the LaTeX Project Team.
+%%
+%% -----------------------------------------------------------------------
+%%
+%
+%<*driver|package>
+\RequirePackage{l3names}
+\GetIdInfo$Id: l3candidates.dtx 3633 2012-05-12 20:11:32Z joseph $
+ {L3 Experimental additions to l3kernel}
+%</driver|package>
+%<*driver>
+\documentclass[full]{l3doc}
+\begin{document}
+ \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+% The \textsf{l3candidates} package\\ Experimental additions to
+% \pkg{l3kernel}^^A
+% \thanks{This file describes v\ExplFileVersion,
+% last revised \ExplFileDate.}^^A
+% }
+%
+% \author{^^A
+% The \LaTeX3 Project\thanks
+% {^^A
+% E-mail:
+% \href{mailto:latex-team@latex-project.org}
+% {latex-team@latex-project.org}^^A
+% }^^A
+% }
+%
+% \date{Released \ExplFileDate}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% This module provides a space in which functions can be added to
+% \pkg{l3kernel} (\pkg{expl3}) while still being experimental. As such, the
+% functions here may not remain in their current form, or indeed at all,
+% in \pkg{l3kernel} in the future. In contrast to the material in
+% \pkg{l3experimental}, the functions here are all \emph{small} additions to
+% the kernel. We encourage programmers to test them out and report back on
+% the \texttt{LaTeX-L} mailing list.
+%
+% \section{Additions to \pkg{l3basics}}
+%
+% \begin{function}[EXP,TF]{\cs_if_exist_use:N, \cs_if_exist_use:c}
+% \begin{syntax}
+% \cs{cs_if_exist_use:NTF} \meta{control sequence} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% If the \meta{control sequence} exists, leave it in the input stream,
+% followed by the \meta{true code} (unbraced). Otherwise, leave the
+% \meta{false} code in the input stream. For example,
+% \begin{verbatim}
+% \cs_set:Npn \mypkg_use_character:N #1
+% { \cs_if_exist_use:cF { mypkg_#1:n } { \mypkg_default:N #1 } }
+% \end{verbatim}
+% calls the function |\mypkg_#1:n| if it exists, and falls back to
+% a default action otherwise. This could also be done (more slowly)
+% using \cs{str_case_x:nnn}.
+% \begin{texnote}
+% The \texttt{c} variants do not introduce the \meta{control sequence}
+% in the hash table if it is not there.
+% \end{texnote}
+% \end{function}
+%
+% \section{Additions to \pkg{l3box}}
+%
+% \subsection{Affine transformations}
+%
+% Affine transformations are changes which (informally) preserve straight
+% lines. Simple translations are affine transformations, but are better handled
+% in \TeX{} by doing the translation first, then inserting an unmodified box.
+% On the other hand, rotation and resizing of boxed material can best be
+% handled by modifying boxes. These transformations are described here.
+%
+% \begin{function}{\box_resize:Nnn, \box_resize:cnn}
+% \begin{syntax}
+% \cs{box_resize:Nnn} \meta{box} \Arg{x-size} \Arg{y-size}
+% \end{syntax}
+% Resize the \meta{box} to \meta{x-size} horizontally and \meta{y-size}
+% vertically (both of the sizes are dimension expressions).
+% The \meta{y-size} is the vertical size (height plus depth) of
+% the box. The updated \meta{box} will be an hbox, irrespective of the nature
+% of the \meta{box} before the resizing is applied. Negative sizes will
+% cause the material in the \meta{box} to be reversed in direction, but the
+% reference point of the \meta{box} will be unchanged. The resizing applies
+% within the current \TeX{} group level.
+% \end{function}
+%
+% \begin{function}
+% {\box_resize_to_ht_plus_dp:Nn, \box_resize_to_ht_plus_dp:cn}
+% \begin{syntax}
+% \cs{box_resize_to_ht_plus_dp:Nn} \meta{box} \Arg{y-size}
+% \end{syntax}
+% Resize the \meta{box} to \meta{y-size} vertically, scaling the horizontal
+% size by the same amount (\meta{y-size} is a dimension expression).
+% The \meta{y-size} is the vertical size (height plus depth) of
+% the box.
+% The updated \meta{box} will be an hbox, irrespective of the nature
+% of the \meta{box} before the resizing is applied. A negative size will
+% cause the material in the \meta{box} to be reversed in direction, but the
+% reference point of the \meta{box} will be unchanged. The resizing applies
+% within the current \TeX{} group level.
+% \end{function}
+%
+% \begin{function}{\box_resize_to_wd:Nn, \box_resize_to_wd:cn}
+% \begin{syntax}
+% \cs{box_resize_to_wd:Nn} \meta{box} \Arg{x-size}
+% \end{syntax}
+% Resize the \meta{box} to \meta{x-size} horizontally, scaling the vertical
+% size by the same amount (\meta{x-size} is a dimension expression).
+% The updated \meta{box} will be an hbox, irrespective of the nature
+% of the \meta{box} before the resizing is applied. A negative size will
+% cause the material in the \meta{box} to be reversed in direction, but the
+% reference point of the \meta{box} will be unchanged. The resizing applies
+% within the current \TeX{} group level.
+% \end{function}
+%
+% \begin{function}{\box_rotate:Nn, \box_rotate:cn}
+% \begin{syntax}
+% \cs{box_rotate:Nn} \meta{box} \Arg{angle}
+% \end{syntax}
+% Rotates the \meta{box} by \meta{angle} (in degrees) anti-clockwise about
+% its reference point. The reference point of the updated box will be moved
+% horizontally such that it is at the left side of the smallest rectangle
+% enclosing the rotated material.
+% The updated \meta{box} will be an hbox, irrespective of the nature
+% of the \meta{box} before the rotation is applied. The rotation applies
+% within the current \TeX{} group level.
+% \end{function}
+%
+% \begin{function}{\box_scale:Nnn, \box_scale:cnn}
+% \begin{syntax}
+% \cs{box_scale:Nnn} \meta{box} \Arg{x-scale} \Arg{y-scale}
+% \end{syntax}
+% Scales the \meta{box} by factors \meta{x-scale} and \meta{y-scale} in
+% the horizontal and vertical directions, respectively (both scales are
+% integer expressions). The updated \meta{box} will be an hbox, irrespective
+% of the nature of the \meta{box} before the scaling is applied. Negative
+% scalings will cause the material in the \meta{box} to be reversed in
+% direction, but the reference point of the \meta{box} will be unchanged.
+% The scaling applies within the current \TeX{} group level.
+% \end{function}
+%
+% \subsection{Viewing part of a box}
+%
+% \begin{function}{\box_clip:N, \box_clip:c}
+% \begin{syntax}
+% \cs{box_clip:N} \meta{box}
+% \end{syntax}
+% Clips the \meta{box} in the output so that only material inside the
+% bounding box is displayed in the output. The updated \meta{box} will be an
+% hbox, irrespective of the nature of the \meta{box} before the clipping is
+% applied. The clipping applies within the current \TeX{} group level.
+%
+% \textbf{These functions require the \LaTeX3 native drivers: they will
+% not work with the \LaTeXe{} \pkg{graphics} drivers!}
+%
+% \begin{texnote}
+% Clipping is implemented by the driver, and as such the full content of
+% the box is places in the output file. Thus clipping does not remove
+% any information from the raw output, and hidden material can therefore
+% be viewed by direct examination of the file.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}{\box_trim:Nnnnn, \box_trim:cnnnn}
+% \begin{syntax}
+% \cs{box_trim:Nnnnn} \meta{box} \Arg{left} \Arg{bottom} \Arg{right} \Arg{top}
+% \end{syntax}
+% Adjusts the bounding box of the \meta{box} \meta{left} is removed from
+% the left-hand edge of the bounding box, \meta{right} from the right-hand
+% edge and so fourth. All adjustments are \meta{dimension expressions}.
+% Material output of the bounding box will still be displayed in the output
+% unless \cs{box_clip:N} is subsequently applied.
+% The updated \meta{box} will be an
+% hbox, irrespective of the nature of the \meta{box} before the viewport
+% operation is applied. The adjustment applies within the current \TeX{}
+% group level.
+% \end{function}
+%
+% \begin{function}{\box_viewport:Nnnnn, \box_viewport:cnnnn}
+% \begin{syntax}
+% \cs{box_viewport:Nnnnn} \meta{box} \Arg{llx} \Arg{lly} \Arg{urx} \Arg{ury}
+% \end{syntax}
+% Adjusts the bounding box of the \meta{box} such that it has lower-left
+% co-ordinates (\meta{llx}, \meta{lly}) and upper-right co-ordinates
+% (\meta{urx}, \meta{ury}). All four co-ordinate positions are
+% \meta{dimension expressions}. Material output of the bounding box will
+% still be displayed in the output unless \cs{box_clip:N} is
+% subsequently applied.
+% The updated \meta{box} will be an
+% hbox, irrespective of the nature of the \meta{box} before the viewport
+% operation is applied. The adjustment applies within the current \TeX{}
+% group level.
+% \end{function}
+%
+% \subsection{Internal variables}
+%
+% \begin{variable}{\l__box_angle_fp}
+% The angle through which a box is rotated by \cs{box_rotate:Nn}, given in
+% degrees counter-clockwise. This value is required by the underlying
+% driver code in \pkg{l3driver} to carry out the driver-dependent part
+% of box rotation.
+% \end{variable}
+%
+% \begin{variable}{\l__box_cos_fp, \l__box_sin_fp}
+% The sine and cosine of the angle through which a box is rotated by
+% \cs{box_rotate:Nn}: the values refer to the angle counter-clockwise. These
+% values are required by the underlying driver code in \pkg{l3driver} to
+% carry out the driver-dependent part of box rotation.
+% \end{variable}
+%
+% \begin{variable}{\l__box_scale_x_fp, \l__box_scale_y_fp}
+% The scaling factors by which a box is scaled by \cs{box_scale:Nnn}
+% or \cs{box_resize:Nnn}. These values are required by the underlying
+% driver code in \pkg{l3driver} to carry out the driver-dependent part
+% of box rotation.
+% \end{variable}
+%
+% \begin{variable}{\l__box_internal_box}
+% Box used for affine transformations, which is used to contain rotated
+% material when applying \cs{box_rotate:Nn}. This box must be correctly
+% constructed for the driver-dependent code in \pkg{l3driver} to function
+% correctly.
+% \end{variable}
+%
+% \section{Additions to \pkg{l3clist}}
+%
+% \begin{function}{\clist_item:Nn, \clist_item:cn, \clist_item:nn}
+% \begin{syntax}
+% \cs{clist_item:Nn} \meta{comma list} \Arg{integer expression}
+% \end{syntax}
+% Indexing items in the \meta{comma list} from~$1$ at the top (left), this
+% function will evaluate the \meta{integer expression} and leave the
+% appropriate item from the comma list in the input stream. If the
+% \meta{integer expression} is negative, indexing occurs from the
+% bottom (right) of the comma list. When the \meta{integer expression}
+% is larger than the number of items in the \meta{comma list} (as
+% calculated by \cs{clist_count:N}) then the function will expand to
+% nothing.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{item}
+% will not expand further when appearing in an \texttt{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}
+% {
+% \clist_set_from_seq:NN, \clist_set_from_seq:cN,
+% \clist_set_from_seq:Nc, \clist_set_from_seq:cc,
+% \clist_gset_from_seq:NN, \clist_gset_from_seq:cN,
+% \clist_gset_from_seq:Nc, \clist_gset_from_seq:cc
+% }
+% \begin{syntax}
+% \cs{clist_set_from_seq:NN} \meta{comma list} \meta{sequence}
+% \end{syntax}
+% Sets the \meta{comma list} to be equal to the content of the
+% \meta{sequence}.
+% Items which contain either spaces or commas are surrounded by braces.
+% \end{function}
+%
+% \begin{function}
+% {
+% \clist_const:Nn, \clist_const:Nx,
+% \clist_const:cn, \clist_const:cx
+% }
+% \begin{syntax}
+% \cs{clist_const:Nn} \meta{clist~var} \Arg{comma list}
+% \end{syntax}
+% Creates a new constant \meta{clist~var} or raises an error
+% if the name is already taken. The value of the
+% \meta{clist~var} will be set globally to the
+% \meta{comma list}.
+% \end{function}
+%
+% \begin{function}[EXP, pTF]{\clist_if_empty:n}
+% \begin{syntax}
+% \cs{clist_if_empty_p:n} \Arg{comma list}
+% \cs{clist_if_empty:nTF} \Arg{comma list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the \meta{comma list} is empty (containing no items).
+% The rules for space trimming are as for other \texttt{n}-type
+% comma-list functions, hence the comma list |{~,~,,~}| (without
+% outer braces) is empty, while |{~,{},}| (without outer braces)
+% contains one element, which happens to be empty: the comma-list
+% is not empty.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2012-06-26]{\clist_use:Nnnn}
+% \begin{syntax}
+% \cs{clist_use:Nnnn} \meta{clist~var} \Arg{separator~between~two} \Arg{separator~between~more~than~two} \Arg{separator~between~final~two}
+% \end{syntax}
+% Places the contents of the \meta{clist~var} in the input stream,
+% with the appropriate \meta{separator} between the items. Namely, if
+% the comma list has more than $2$ items, the \meta{separator between
+% more than two} is placed between each pair of items except the
+% last, for which the \meta{separator between final two} is used. If
+% the comma list has $2$ items, then they are placed in the input
+% stream separated by the \meta{separator between two}. If the comma
+% list has $1$ item, it is placed in the input stream, and a comma
+% list with no items produces no output. An error will be raised if
+% the variable does not exist or if it is invalid.
+%
+% For example,
+% \begin{verbatim}
+% \clist_set:Nn \l_tmpa_clist { a , b , , c , {de} , f }
+% \clist_use:Nnnn \l_tmpa_clist { ~and~ } { ,~ } { ,~and~ }
+% \end{verbatim}
+% will insert \enquote{\texttt{a, b, c, de, and f}} in the input
+% stream. The first separator argument is not used in this case
+% because the comma list has more than $2$ items.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{items}
+% will not expand further when appearing in an \texttt{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \section{Additions to \pkg{l3coffins}}
+%
+% \begin{function}{\coffin_resize:Nnn, \coffin_resize:cnn}
+% \begin{syntax}
+% \cs{coffin_resize:Nnn} \meta{coffin} \Arg{width} \Arg{total-height}
+% \end{syntax}
+% Resized the \meta{coffin} to \meta{width} and \meta{total-height},
+% both of which should be given as dimension expressions. These may
+% include the terms \cs{TotalHeight}, \cs{Height}, \cs{Depth} and
+% \cs{Width}, which will evaluate to the appropriate dimensions of
+% the \meta{coffin}.
+% \end{function}
+%
+% \begin{function}{\coffin_rotate:Nn, \coffin_rotate:cn}
+% \begin{syntax}
+% \cs{coffin_rotate:Nn} \meta{coffin} \Arg{angle}
+% \end{syntax}
+% Rotates the \meta{coffin} by the given \meta{angle} (given in
+% degrees counter-clockwise). This process will rotate both the
+% coffin content and poles. Multiple rotations will not result in
+% the bounding box of the coffin growing unnecessarily.
+% \end{function}
+%
+% \begin{function}{\coffin_scale:Nnn, \coffin_scale:cnn}
+% \begin{syntax}
+% \cs{coffin_scale:Nnn} \meta{coffin} \Arg{x-scale} \Arg{y-scale}
+% \end{syntax}
+% Scales the \meta{coffin} by a factors \meta{x-scale} and
+% \meta{y-scale} in the horizontal and vertical directions,
+% respectively. The two scale factors should be given as real numbers.
+% \end{function}
+%
+% \section{Additions to \pkg{l3file}}
+%
+% \begin{function}[added = 2012-02-11]{\ior_map_inline:Nn}
+% \begin{syntax}
+% \cs{ior_map_inline:Nn} \meta{stream} \Arg{inline function}
+% \end{syntax}
+% Applies the \meta{inline function} to \meta{lines} obtained by
+% reading one or more lines (until an equal number of left and right
+% braces are found) from the \meta{stream}. The \meta{inline function}
+% should consist of code which will receive the \meta{line} as |#1|.
+% \end{function}
+%
+% \begin{function}[added = 2012-02-11]{\ior_str_map_inline:Nn}
+% \begin{syntax}
+% \cs{ior_str_map_inline:Nn} \Arg{stream} \Arg{inline function}
+% \end{syntax}
+% Applies the \meta{inline function} to every \meta{line}
+% in the \meta{sream}. The material is read from the \meta{stream}
+% as a series of tokens with category code $12$ (other), with the
+% exception of space characters which are given category code $10$
+% (space). The \meta{inline function} should consist of code which
+% will receive the \meta{line} as |#1|.
+% \end{function}
+%
+% \begin{function}[added = 2012-06-29]{\ior_map_break:}
+% \begin{syntax}
+% \cs{ior_map_break:}
+% \end{syntax}
+% Used to terminate a \cs{ior_map_\ldots} function before all
+% lines from the \meta{stream} have been processed. This will
+% normally take place within a conditional statement, for example
+% \begin{verbatim}
+% \ior_map_inline:Nn \l_my_ior
+% {
+% \str_if_eq:nnTF { #1 } { bingo }
+% { \ior_map_break: }
+% {
+% % Do something useful
+% }
+% }
+% \end{verbatim}
+% Use outside of a \cs{ior_map_\ldots} scenario will lead to low
+% level \TeX{} errors.
+% \begin{texnote}
+% When the mapping is broken, additional tokens may be inserted by the
+% internal macro \cs{__prg_break_point:Nn} before further items are taken
+% from the input stream. This will depend on the design of the mapping
+% function.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2012-06-29]{\ior_map_break:n}
+% \begin{syntax}
+% \cs{ior_map_break:n} \Arg{tokens}
+% \end{syntax}
+% Used to terminate a \cs{ior_map_\ldots} function before all
+% lines in the \meta{stream} have been processed, inserting
+% the \meta{tokens} after the mapping has ended. This will
+% normally take place within a conditional statement, for example
+% \begin{verbatim}
+% \ior_map_inline:Nn \l_my_ior
+% {
+% \str_if_eq:nnTF { #1 } { bingo }
+% { \ior_map_break:n { <tokens> } }
+% {
+% % Do something useful
+% }
+% }
+% \end{verbatim}
+% Use outside of a \cs{ior_map_\ldots} scenario will lead to low
+% level \TeX{} errors.
+% \begin{texnote}
+% When the mapping is broken, additional tokens may be inserted by the
+% internal macro \cs{__prg_break_point:Nn} before the \meta{tokens} are
+% inserted into the input stream.
+% This will depend on the design of the mapping function.
+% \end{texnote}
+% \end{function}
+%
+% \section{Additions to \pkg{l3fp}}
+%
+% \begin{function}
+% {
+% \fp_set_from_dim:Nn, \fp_set_from_dim:cn,
+% \fp_gset_from_dim:Nn, \fp_gset_from_dim:cn
+% }
+% \begin{syntax}
+% \cs{fp_set_from_dim:Nn} \meta{floating point variable} \Arg{dimexpr}
+% \end{syntax}
+% Sets the \meta{floating point variable} to the distance represented
+% by the \meta{dimension expression} in the units points. This means
+% that distances given in other units are first converted to points
+% before being assigned to the \meta{floating point variable}.
+% \end{function}
+%
+% \section{Additions to \pkg{l3prop}}
+%
+% \begin{function}[rEXP]
+% {\prop_map_tokens:Nn, \prop_map_tokens:cn}
+% \begin{syntax}
+% \cs{prop_map_tokens:Nn} \meta{property list} \Arg{code}
+% \end{syntax}
+% Analogue of \cs{prop_map_function:NN} which maps several tokens
+% instead of a single function. The \meta{code} receives each
+% key--value pair in the \meta{property list} as two trailing brace
+% groups. For instance,
+% \begin{verbatim}
+% \prop_map_tokens:Nn \l_my_prop { \str_if_eq:nnT { mykey } }
+% \end{verbatim}
+% will expand to the value corresponding to \texttt{mykey}: for each
+% pair in \cs{l_my_prop} the function \cs{str_if_eq:nnT} receives
+% \texttt{mykey}, the \meta{key} and the \meta{value} as its three
+% arguments. For that specific task, \cs{prop_get:Nn} is faster.
+% \end{function}
+%
+% \begin{function}[EXP]{\prop_get:Nn, \prop_get:cn}
+% \begin{syntax}
+% \cs{prop_get:Nn} \meta{property list} \Arg{key}
+% \end{syntax}
+% Expands to the \meta{value} corresponding to the \meta{key} in
+% the \meta{property list}. If the \meta{key} is missing, this has
+% an empty expansion.
+% \begin{texnote}
+% This function is slower than the non-expandable analogue
+% \cs{prop_get:NnN}.
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{value}
+% will not expand further when appearing in an \texttt{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \section{Additions to \pkg{l3seq}}
+%
+% \begin{function}[EXP]{\seq_item:Nn, \seq_item:cn}
+% \begin{syntax}
+% \cs{seq_item:Nn} \meta{sequence} \Arg{integer expression}
+% \end{syntax}
+% Indexing items in the \meta{sequence} from~$1$ at the top (left), this
+% function will evaluate the \meta{integer expression} and leave the
+% appropriate item from the sequence in the input stream. If the
+% \meta{integer expression} is negative, indexing occurs from the
+% bottom (right) of the sequence. When the \meta{integer expression}
+% is larger than the number of items in the \meta{sequence} (as
+% calculated by \cs{seq_count:N}) then the function will expand to
+% nothing.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{item}
+% will not expand further when appearing in an \texttt{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[rEXP]
+% {
+% \seq_mapthread_function:NNN, \seq_mapthread_function:NcN,
+% \seq_mapthread_function:cNN, \seq_mapthread_function:ccN
+% }
+% \begin{syntax}
+% \cs{seq_mapthread_function:NNN} \meta{seq_1} \meta{seq_2} \meta{function}
+% \end{syntax}
+% Applies \meta{function} to every pair of items
+% \meta{seq_1-item}--\meta{seq_2-item} from the two sequences, returning
+% items from both sequences from left to right. The \meta{function} will
+% receive two \texttt{n}-type arguments for each iteration. The mapping
+% will terminate when
+% the end of either sequence is reached (\emph{i.e.}~whichever sequence has
+% fewer items determines how many iterations
+% occur).
+% \end{function}
+%
+% \begin{function}
+% {
+% \seq_set_from_clist:NN, \seq_set_from_clist:cN,
+% \seq_set_from_clist:Nc, \seq_set_from_clist:cc,
+% \seq_set_from_clist:Nn, \seq_set_from_clist:cn,
+% \seq_gset_from_clist:NN, \seq_gset_from_clist:cN,
+% \seq_gset_from_clist:Nc, \seq_gset_from_clist:cc,
+% \seq_gset_from_clist:Nn, \seq_gset_from_clist:cn
+% }
+% \begin{syntax}
+% \cs{seq_set_from_clist:NN} \meta{sequence} \meta{comma-list}
+% \end{syntax}
+% Sets the \meta{sequence} within the current \TeX{} group to be equal
+% to the content of the \meta{comma-list}.
+% \end{function}
+%
+% \begin{function}{\seq_reverse:N, \seq_greverse:N}
+% \begin{syntax}
+% \cs{seq_reverse:N} \meta{sequence}
+% \end{syntax}
+% Reverses the order of items in the \meta{sequence}, and
+% assigns the result to \meta{sequence}, locally or globally
+% according to the variant chosen.
+% \end{function}
+%
+% \begin{function}{\seq_set_filter:NNn, \seq_gset_filter:NNn}
+% \begin{syntax}
+% \cs{seq_set_filter:NNn} \meta{sequence_1} \meta{sequence_2} \Arg{inline boolexpr}
+% \end{syntax}
+% Evaluates the \meta{inline boolexpr} for every \meta{item} stored
+% within the \meta{sequence_2}. The \meta{inline boolexpr} will
+% receive the \meta{item} as |#1|. The sequence of all \meta{items}
+% for which the \meta{inline boolexpr} evaluated to \texttt{true}
+% is assigned to \meta{sequence_1}.
+% \begin{texnote}
+% Contrarily to other mapping functions, \cs{seq_map_break:} cannot
+% be used in this function, and will lead to low-level \TeX{} errors.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[added = 2011-12-22]
+% {\seq_set_map:NNn, \seq_gset_map:NNn}
+% \begin{syntax}
+% \cs{seq_set_map:NNn} \meta{sequence_1} \meta{sequence_2} \Arg{inline function}
+% \end{syntax}
+% Applies \meta{inline function} to every \meta{item} stored
+% within the \meta{sequence_2}. The \meta{inline function} should
+% consist of code which will receive the \meta{item} as |#1|.
+% The sequence resulting from \texttt{x}-expanding
+% \meta{inline function} applied to each \meta{item}
+% is assigned to \meta{sequence_1}. As such, the code
+% in \meta{inline function} should be expandable.
+% \begin{texnote}
+% Contrarily to other mapping functions, \cs{seq_map_break:} cannot
+% be used in this function, and will lead to low-level \TeX{} errors.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP, added = 2012-06-26]{\seq_use:Nnnn}
+% \begin{syntax}
+% \cs{seq_use:Nnnn} \meta{seq~var} \Arg{separator~between~two} \Arg{separator~between~more~than~two} \Arg{separator~between~final~two}
+% \end{syntax}
+% Places the contents of the \meta{seq~var} in the input stream, with
+% the appropriate \meta{separator} between the items. Namely, if the
+% sequence has more than $2$ items, the \meta{separator between more
+% than two} is placed between each pair of items except the last,
+% for which the \meta{separator between final two} is used. If the
+% sequence has $2$ items, then they are placed in the input stream
+% separated by the \meta{separator between two}. If the sequence has
+% $1$ item, it is placed in the input stream, and an empty sequence
+% produces no output. An error will be raised if the variable does
+% not exist or if it is invalid.
+%
+% For example,
+% \begin{verbatim}
+% \seq_set_split:Nnn \l_tmpa_seq { | } { a | b | c | {de} | f }
+% \seq_use:Nnnn \l_tmpa_seq { ~and~ } { ,~ } { ,~and~ }
+% \end{verbatim}
+% will insert \enquote{\texttt{a, b, c, de, and f}} in the input
+% stream. The first separator argument is not used in this case
+% because the sequence has more than $2$ items.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{items}
+% will not expand further when appearing in an \texttt{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \section{Additions to \pkg{l3skip}}
+%
+% \begin{function}{\skip_split_finite_else_action:nnNN}
+% \begin{syntax}
+% \cs{skip_split_finite_else_action:nnNN} \Arg{skipexpr} \Arg{action}
+% ~~\meta{dimen_1} \meta{dimen_2}
+% \end{syntax}
+% Checks if the \meta{skipexpr} contains finite glue. If it does then it
+% assigns
+% \meta{dimen_1} the stretch component and \meta{dimen_2} the shrink
+% component. If
+% it contains infinite glue set \meta{dimen_1} and \meta{dimen_2} to $0$\,pt
+% and place |#2| into the input stream: this is usually an error or
+% warning message of some sort.
+% \end{function}
+%
+% \section{Additions to \pkg{l3tl}}
+%
+% \begin{function}[EXP,pTF]{\tl_if_single_token:n}
+% \begin{syntax}
+% \cs{tl_if_single_token_p:n} \Arg{token list}
+% \cs{tl_if_single_token:nTF} \Arg{token list} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the token list consists of exactly one token, \emph{i.e.}~is
+% either a single space character or a single \enquote{normal} token.
+% Token groups (|{|\ldots|}|) are not single tokens.
+% \end{function}
+%
+% \begin{function}[EXP]{\tl_reverse_tokens:n}
+% \begin{syntax}
+% \cs{tl_reverse_tokens:n} \Arg{tokens}
+% \end{syntax}
+% This function, which works directly on \TeX{} tokens, reverses
+% the order of the \meta{tokens}: the first will be the last and
+% the last will become first. Spaces are preserved. The reversal
+% also operates within brace groups, but the braces themselves
+% are not exchanged, as this would lead to an unbalanced token
+% list. For instance, \cs{tl_reverse_tokens:n} |{a~{b()}}|
+% leaves |{)(b}~a| in the input stream. This function requires
+% two steps of expansion.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the token
+% list will not expand further when appearing in an \texttt{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\tl_count_tokens:n}
+% \begin{syntax}
+% \cs{tl_count_tokens:n} \Arg{tokens}
+% \end{syntax}
+% Counts the number of \TeX{} tokens in the \meta{tokens} and leaves
+% this information in the input stream. Every token, including spaces and
+% braces, contributes one to the total; thus for instance, the token count of
+% |a~{bc}| is $6$.
+% This function requires three expansions,
+% giving an \meta{integer denotation}.
+% \end{function}
+%
+% \begin{function}[EXP]{\tl_expandable_uppercase:n,\tl_expandable_lowercase:n}
+% \begin{syntax}
+% \cs{tl_expandable_uppercase:n} \Arg{tokens}
+% \cs{tl_expandable_lowercase:n} \Arg{tokens}
+% \end{syntax}
+% The \cs{tl_expandable_uppercase:n} function works through all of
+% the \meta{tokens}, replacing characters in the range |a|--|z|
+% (with arbitrary category code) by the corresponding letter
+% in the range |A|--|Z|, with category code $11$ (letter). Similarly,
+% \cs{tl_expandable_lowercase:n} replaces characters in the range
+% |A|--|Z| by letters in the range |a|--|z|, and leaves other tokens
+% unchanged. This function requires two steps of expansion.
+% \begin{texnote}
+% Begin-group and end-group characters are normalized and become
+% |{| and |}|, respectively.
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the token
+% list will not expand further when appearing in an \texttt{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \begin{function}[EXP]{\tl_item:nn, \tl_item:Nn, \tl_item:cn}
+% \begin{syntax}
+% \cs{tl_item:nn} \Arg{token list} \Arg{integer expression}
+% \end{syntax}
+% Indexing items in the \meta{token list} from~$1$ on the left, this
+% function will evaluate the \meta{integer expression} and leave the
+% appropriate item from the \meta{token list} in the input stream.
+% If the \meta{integer expression} is negative, indexing occurs from
+% the right of the token list, starting at $-1$ for the right-most item.
+% If the index is out of bounds, then thr function expands to nothing.
+% \begin{texnote}
+% The result is returned within the \tn{unexpanded}
+% primitive (\cs{exp_not:n}), which means that the \meta{item}
+% will not expand further when appearing in an \texttt{x}-type
+% argument expansion.
+% \end{texnote}
+% \end{function}
+%
+% \section{Additions to \pkg{l3tokens}}
+%
+% \begin{function}{\char_set_active:Npn, \char_set_active:Npx}
+% \begin{syntax}
+% \cs{char_set_active:Npn} \meta{char} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Makes \meta{char} an active character to expand to \meta{code} as
+% replacement text.
+% Within the \meta{code}, the \meta{parameters} (|#1|, |#2|,
+% \emph{etc.}) will be replaced by those absorbed. The \meta{char} is
+% made active within the current \TeX{} group level, and the definition
+% is also local.
+% \end{function}
+%
+% \begin{function}{\char_gset_active:Npn, \char_gset_active:Npx}
+% \begin{syntax}
+% \cs{char_gset_active:Npn} \meta{char} \meta{parameters} \Arg{code}
+% \end{syntax}
+% Makes \meta{char} an active character to expand to \meta{code} as
+% replacement text.
+% Within the \meta{code}, the \meta{parameters} (|#1|, |#2|,
+% \emph{etc.}) will be replaced by those absorbed. The \meta{char} is
+% made active within the current \TeX{} group level, but the definition
+% is global. This function is therefore suited to cases where an active
+% character definition should be applied only in some context (where the
+% \meta{char} is again made active).
+% \end{function}
+%
+% \begin{function}{\char_set_active_eq:NN}
+% \begin{syntax}
+% \cs{char_set_active_eq:NN} \meta{char} \meta{function}
+% \end{syntax}
+% Makes \meta{char} an active character equivalent in meaning to the
+% \meta{function} (which may itself be an active character). The \meta{char}
+% is made active within the current \TeX{} group level, and the definition
+% is also local.
+% \end{function}
+%
+% \begin{function}{\char_gset_active_eq:NN}
+% \begin{syntax}
+% \cs{char_gset_active_eq:NN} \meta{char} \meta{function}
+% \end{syntax}
+% Makes \meta{char} an active character equivalent in meaning to the
+% \meta{function} (which may itself be an active character). The \meta{char}
+% is made active within the current \TeX{} group level, but the definition
+% is global. This function is therefore suited to cases where an active
+% character definition should be applied only in some context (where the
+% \meta{char} is again made active).
+% \end{function}
+%
+% \begin{function}[TF]{\peek_N_type:}
+% \begin{syntax}
+% \cs{peek_N_type:TF} \Arg{true code} \Arg{false code}
+% \end{syntax}
+% Tests if the next \meta{token} in the input stream can be safely
+% grabbed as an \texttt{N}-type argument. The test will be \meta{false}
+% if the next \meta{token} is either an explicit or implicit
+% begin-group or end-group token (with any character code), or
+% an explicit or implicit space character (with character code $32$
+% and category code $10$), and \meta{true} in all other cases.
+% Note that a \meta{true} result ensures that the next \meta{token} is
+% a valid \texttt{N}-type argument. However, if the next \meta{token}
+% is for instance \cs{c_space_token}, the test will take the
+% \meta{false} branch, even though the next \meta{token} is in fact
+% a valid \texttt{N}-type argument. The \meta{token} will be left
+% in the input stream after the \meta{true code} or \meta{false code}
+% (as appropriate to the result of the test).
+% \end{function}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3candidates} Implementation}
+%
+% \begin{macrocode}
+%<*initex|package>
+% \end{macrocode}
+%
+% \begin{macrocode}
+%<*package>
+\ProvidesExplPackage
+ {\ExplFileName}{\ExplFileDate}{\ExplFileVersion}{\ExplFileDescription}
+\__expl_package_check:
+%</package>
+% \end{macrocode}
+%
+% \subsection{Additions to \pkg{l3box}}
+%
+% \begin{macrocode}
+%<@@=box>
+% \end{macrocode}
+%
+% \subsection{Affine transformations}
+%
+% \begin{variable}{\l_@@_angle_fp}
+% When rotating boxes, the angle itself may be needed by the
+% engine-dependent code. This is done using the \pkg{fp} module so
+% that the value is tidied up properly.
+% \begin{macrocode}
+\fp_new:N \l_@@_angle_fp
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_cos_fp, \l_@@_sin_fp}
+% These are used to hold the calculated sine and cosine values while
+% carrying out a rotation.
+% \begin{macrocode}
+\fp_new:N \l_@@_cos_fp
+\fp_new:N \l_@@_sin_fp
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {\l_@@_top_dim, \l_@@_bottom_dim, \l_@@_left_dim, \l_@@_right_dim}
+% These are the positions of the four edges of a box before
+% manipulation.
+% \begin{macrocode}
+\dim_new:N \l_@@_top_dim
+\dim_new:N \l_@@_bottom_dim
+\dim_new:N \l_@@_left_dim
+\dim_new:N \l_@@_right_dim
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \l_@@_top_new_dim, \l_@@_bottom_new_dim ,
+% \l_@@_left_new_dim, \l_@@_right_new_dim
+% }
+% These are the positions of the four edges of a box after
+% manipulation.
+% \begin{macrocode}
+\dim_new:N \l_@@_top_new_dim
+\dim_new:N \l_@@_bottom_new_dim
+\dim_new:N \l_@@_left_new_dim
+\dim_new:N \l_@@_right_new_dim
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_internal_box}
+% Scratch space, but also needed by some parts of the driver.
+% \begin{macrocode}
+\box_new:N \l_@@_internal_box
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\box_rotate:Nn}
+% \begin{macro}[aux]{\@@_rotate:N}
+% \begin{macro}[aux]{\@@_rotate_x:nnN, \@@_rotate_y:nnN}
+% \begin{macro}[aux]
+% {
+% \@@_rotate_quadrant_one:, \@@_rotate_quadrant_two:,
+% \@@_rotate_quadrant_three:, \@@_rotate_quadrant_four:
+% }
+% Rotation of a box starts with working out the relevant sine and
+% cosine. The actual rotation is in an auxiliary to keep the flow slightly
+% clearer
+% \begin{macrocode}
+\cs_new_protected:Npn \box_rotate:Nn #1#2
+ {
+ \hbox_set:Nn #1
+ {
+ \group_begin:
+ \fp_set:Nn \l_@@_angle_fp {#2}
+ \fp_set:Nn \l_@@_sin_fp { sin ( \l_@@_angle_fp * deg ) }
+ \fp_set:Nn \l_@@_cos_fp { cos ( \l_@@_angle_fp * deg ) }
+ \@@_rotate:N #1
+ \group_end:
+ }
+ }
+% \end{macrocode}
+% The edges of the box are then recorded: the left edge will
+% always be at zero. Rotation of the four edges then takes place: this is
+% most efficiently done on a quadrant by quadrant basis.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_rotate:N #1
+ {
+ \dim_set:Nn \l_@@_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l_@@_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l_@@_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l_@@_left_dim
+% \end{macrocode}
+% The next step is to work out the $x$ and $y$ coordinates of vertices of
+% the rotated box in relation to its original coordinates. The box can be
+% visualized with vertices $B$, $C$, $D$ and $E$ is illustrated
+% (Figure~\ref{fig:rotation}). The vertex $O$ is the reference point on the
+% baseline, and in this implementation is also the centre of rotation.
+% \begin{figure}
+% \centering
+% \setlength{\unitlength}{3pt}^^A
+% \begin{picture}(34,36)(12,44)
+% \thicklines
+% \put(20,52){\dashbox{1}(20,21){}}
+% \put(20,80){\line(0,-1){36}}
+% \put(12,58){\line(1, 0){34}}
+% \put(41,59){A}
+% \put(40,74){B}
+% \put(21,74){C}
+% \put(21,49){D}
+% \put(40,49){E}
+% \put(21,59){O}
+% \end{picture}
+% \caption{Co-ordinates of a box prior to rotation.}
+% \label{fig:rotation}
+% \end{figure}
+% The formulae are, for a point $P$ and angle $\alpha$:
+% \[
+% \begin{array}{l}
+% P'_x = P_x - O_x \\
+% P'_y = P_y - O_y \\
+% P''_x = ( P'_x \cos(\alpha)) - ( P'_y \sin(\alpha) ) \\
+% P''_y = ( P'_x \sin(\alpha)) + ( P'_y \cos(\alpha) ) \\
+% P'''_x = P''_x + O_x + L_x \\
+% P'''_y = P''_y + O_y
+% \end{array}
+% \]
+% The \enquote{extra} horizontal translation $L_x$ at the end is calculated
+% so that the leftmost point of the resulting box has $x$-coordinate $0$.
+% This is desirable as \TeX{} boxes must have the reference point at
+% the left edge of the box. (As $O$ is always $(0,0)$, this part of the
+% calculation is omitted here.)
+% \begin{macrocode}
+ \fp_compare:nNnTF \l_@@_sin_fp > \c_zero_fp
+ {
+ \fp_compare:nNnTF \l_@@_cos_fp > \c_zero_fp
+ { \@@_rotate_quadrant_one: }
+ { \@@_rotate_quadrant_two: }
+ }
+ {
+ \fp_compare:nNnTF \l_@@_cos_fp < \c_zero_fp
+ { \@@_rotate_quadrant_three: }
+ { \@@_rotate_quadrant_four: }
+ }
+% \end{macrocode}
+% The position of the box edges are now known, but the box at this
+% stage be misplaced relative to the current \TeX{} reference point. So the
+% content of the box is moved such that the reference point of the
+% rotated box will be in the same place as the original.
+% \begin{macrocode}
+ \hbox_set:Nn \l_@@_internal_box { \box_use:N #1 }
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \tex_kern:D -\l_@@_left_new_dim
+ \hbox:n
+ {
+ \__driver_box_rotate_begin:
+ \box_use:N \l_@@_internal_box
+ \__driver_box_rotate_end:
+ }
+ }
+% \end{macrocode}
+% Tidy up the size of the box so that the material is actually inside
+% the bounding box. The result can then be used to reset the original
+% box.
+% \begin{macrocode}
+ \box_set_ht:Nn \l_@@_internal_box { \l_@@_top_new_dim }
+ \box_set_dp:Nn \l_@@_internal_box { -\l_@@_bottom_new_dim }
+ \box_set_wd:Nn \l_@@_internal_box
+ { \l_@@_right_new_dim - \l_@@_left_new_dim }
+ \box_use:N \l_@@_internal_box
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% These functions take a general point $(|#1|, |#2|)$ and rotate its
+% location about the origin, using the previously-set sine and cosine
+% values. Each function gives only one component of the location of the
+% updated point. This is because for rotation of a box each step needs
+% only one value, and so performance is gained by avoiding working
+% out both $x'$ and $y'$ at the same time. Contrast this with
+% the equivalent function in the \pkg{l3coffins} module, where both parts
+% are needed.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_rotate_x:nnN #1#2#3
+ {
+ \dim_set:Nn #3
+ {
+ \fp_to_dim:n
+ {
+ \l_@@_cos_fp * \dim_to_fp:n {#1}
+ - ( \l_@@_sin_fp * \dim_to_fp:n {#2} )
+ }
+ }
+ }
+\cs_new_protected:Npn \@@_rotate_y:nnN #1#2#3
+ {
+ \dim_set:Nn #3
+ {
+ \fp_to_dim:n
+ {
+ \l_@@_sin_fp * \dim_to_fp:n {#1}
+ + \l_@@_cos_fp * \dim_to_fp:n {#2}
+ }
+ }
+ }
+% \end{macrocode}
+% Rotation of the edges is done using a different formula for each
+% quadrant. In every case, the top and bottom edges only need the
+% resulting $y$-values, whereas the left and right edges need the
+% $x$-values. Each case is a question of picking out which corner
+% ends up at with the maximum top, bottom, left and right value. Doing
+% this by hand means a lot less calculating and avoids lots of
+% comparisons.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_rotate_quadrant_one:
+ {
+ \@@_rotate_y:nnN \l_@@_right_dim \l_@@_top_dim
+ \l_@@_top_new_dim
+ \@@_rotate_y:nnN \l_@@_left_dim \l_@@_bottom_dim
+ \l_@@_bottom_new_dim
+ \@@_rotate_x:nnN \l_@@_left_dim \l_@@_top_dim
+ \l_@@_left_new_dim
+ \@@_rotate_x:nnN \l_@@_right_dim \l_@@_bottom_dim
+ \l_@@_right_new_dim
+ }
+\cs_new_protected:Npn \@@_rotate_quadrant_two:
+ {
+ \@@_rotate_y:nnN \l_@@_right_dim \l_@@_bottom_dim
+ \l_@@_top_new_dim
+ \@@_rotate_y:nnN \l_@@_left_dim \l_@@_top_dim
+ \l_@@_bottom_new_dim
+ \@@_rotate_x:nnN \l_@@_right_dim \l_@@_top_dim
+ \l_@@_left_new_dim
+ \@@_rotate_x:nnN \l_@@_left_dim \l_@@_bottom_dim
+ \l_@@_right_new_dim
+ }
+\cs_new_protected:Npn \@@_rotate_quadrant_three:
+ {
+ \@@_rotate_y:nnN \l_@@_left_dim \l_@@_bottom_dim
+ \l_@@_top_new_dim
+ \@@_rotate_y:nnN \l_@@_right_dim \l_@@_top_dim
+ \l_@@_bottom_new_dim
+ \@@_rotate_x:nnN \l_@@_right_dim \l_@@_bottom_dim
+ \l_@@_left_new_dim
+ \@@_rotate_x:nnN \l_@@_left_dim \l_@@_top_dim
+ \l_@@_right_new_dim
+ }
+\cs_new_protected:Npn \@@_rotate_quadrant_four:
+ {
+ \@@_rotate_y:nnN \l_@@_left_dim \l_@@_top_dim
+ \l_@@_top_new_dim
+ \@@_rotate_y:nnN \l_@@_right_dim \l_@@_bottom_dim
+ \l_@@_bottom_new_dim
+ \@@_rotate_x:nnN \l_@@_left_dim \l_@@_bottom_dim
+ \l_@@_left_new_dim
+ \@@_rotate_x:nnN \l_@@_right_dim \l_@@_top_dim
+ \l_@@_right_new_dim
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_@@_scale_x_fp, \l_@@_scale_y_fp}
+% Scaling is potentially-different in the two axes.
+% \begin{macrocode}
+\fp_new:N \l_@@_scale_x_fp
+\fp_new:N \l_@@_scale_y_fp
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\box_resize:Nnn, \box_resize:cnn}
+% \begin{macro}[aux]{\@@_resize:Nnn}
+% Resizing a box starts by working out the various dimensions of the
+% existing box.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_resize:Nnn #1#2#3
+ {
+ \hbox_set:Nn #1
+ {
+ \group_begin:
+ \dim_set:Nn \l_@@_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l_@@_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l_@@_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l_@@_left_dim
+% \end{macrocode}
+% The $x$-scaling and resulting box size is easy enough to work
+% out: the dimension is that given as |#2|, and the scale is simply the
+% new width divided by the old one.
+% \begin{macrocode}
+ \fp_set:Nn \l_@@_scale_x_fp
+ { \dim_to_fp:n {#2} / ( \dim_to_fp:n \l_@@_right_dim ) }
+% \end{macrocode}
+% The $y$-scaling needs both the height and the depth of the current box.
+% \begin{macrocode}
+ \fp_set:Nn \l_@@_scale_y_fp
+ {
+ \dim_to_fp:n {#3} /
+ ( \dim_to_fp:n { \l_@@_top_dim - \l_@@_bottom_dim } )
+ }
+% \end{macrocode}
+% Hand off to the auxiliary which does the work.
+% \begin{macrocode}
+ \@@_resize:Nnn #1 {#2} {#3}
+ \group_end:
+ }
+ }
+\cs_generate_variant:Nn \box_resize:Nnn { c }
+% \end{macrocode}
+% With at least one real scaling to do, the next phase is to find the new
+% edge co-ordinates. In the $x$~direction this is relatively easy: just
+% scale the right edge. This is done using the absolute value of the
+% scale so that the new edge is in the correct place. In the $y$~direction,
+% both dimensions have to be scaled, and this again needs the absolute
+% scale value. Once that is all done, the common resize/rescale code can
+% be employed.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_resize:Nnn #1#2#3
+ {
+ \dim_compare:nNnTF {#2} > \c_zero_dim
+ { \dim_set:Nn \l_@@_right_new_dim {#2} }
+ { \dim_set:Nn \l_@@_right_new_dim { \c_zero_dim - ( #2 ) } }
+ \dim_compare:nNnTF {#3} > \c_zero_dim
+ {
+ \dim_set:Nn \l_@@_top_new_dim
+ { \fp_use:N \l_@@_scale_y_fp \l_@@_top_dim }
+ \dim_set:Nn \l_@@_bottom_new_dim
+ { \fp_use:N \l_@@_scale_y_fp \l_@@_bottom_dim }
+ }
+ {
+ \dim_set:Nn \l_@@_top_new_dim
+ { - \fp_use:N \l_@@_scale_y_fp \l_@@_top_dim }
+ \dim_set:Nn \l_@@_bottom_new_dim
+ { - \fp_use:N \l_@@_scale_y_fp \l_@@_bottom_dim }
+ }
+ \@@_resize_common:N #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\box_resize_to_ht_plus_dp:Nn, \box_resize_to_ht_plus_dp:cn}
+% \begin{macro}{\box_resize_to_wd:Nn, \box_resize_to_wd:cn}
+% Scaling to a total height or to a width is a simplified version of the main
+% resizing operation, with the scale simply copied between the two parts. The
+% internal auxiliary is called using the scaling value twice, as the sign for
+% both parts is needed (as this allows the same internal code to be used as
+% for the general case).
+% \begin{macrocode}
+\cs_new_protected:Npn \box_resize_to_ht_plus_dp:Nn #1#2
+ {
+ \hbox_set:Nn #1
+ {
+ \group_begin:
+ \dim_set:Nn \l_@@_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l_@@_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l_@@_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l_@@_left_dim
+ \fp_set:Nn \l_@@_scale_y_fp
+ {
+ \dim_to_fp:n {#2} /
+ ( \dim_to_fp:n { \l_@@_top_dim - \l_@@_bottom_dim } )
+ }
+ \fp_set_eq:NN \l_@@_scale_x_fp \l_@@_scale_y_fp
+ \@@_resize:Nnn #1 {#2} {#2}
+ \group_end:
+ }
+ }
+\cs_generate_variant:Nn \box_resize_to_ht_plus_dp:Nn { c }
+\cs_new_protected:Npn \box_resize_to_wd:Nn #1#2
+ {
+ \hbox_set:Nn #1
+ {
+ \group_begin:
+ \dim_set:Nn \l_@@_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l_@@_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l_@@_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l_@@_left_dim
+ \fp_set:Nn \l_@@_scale_x_fp
+ { \dim_to_fp:n {#2} / ( \dim_to_fp:n \l_@@_right_dim ) }
+ \fp_set_eq:NN \l_@@_scale_y_fp \l_@@_scale_x_fp
+ \@@_resize:Nnn #1 {#2} {#2}
+ \group_end:
+ }
+ }
+\cs_generate_variant:Nn \box_resize_to_wd:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\box_scale:Nnn, \box_scale:cnn}
+% \begin{macro}[aux]{\@@_scale:Nnn}
+% When scaling a box, setting the scaling itself is easy enough. The
+% new dimensions are also relatively easy to find, allowing only for
+% the need to keep them positive in all cases. Once that is done then
+% after a check for the trivial scaling a hand-off can be made to the
+% common code. The dimension scaling operations are carried out using
+% the \TeX{} mechanism as it avoids needing to use \texttt{fp}
+% operations.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_scale:Nnn #1#2#3
+ {
+ \hbox_set:Nn #1
+ {
+ \group_begin:
+ \fp_set:Nn \l_@@_scale_x_fp {#2}
+ \fp_set:Nn \l_@@_scale_y_fp {#3}
+ \dim_set:Nn \l_@@_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l_@@_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l_@@_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l_@@_left_dim
+ \@@_scale:Nnn #1 {#2} {#3}
+ \group_end:
+ }
+ }
+\cs_generate_variant:Nn \box_scale:Nnn { c }
+\cs_new_protected:Npn \@@_scale:Nnn #1#2#3
+ {
+ \fp_compare:nNnTF \l_@@_scale_y_fp > \c_zero_fp
+ {
+ \dim_set:Nn \l_@@_top_new_dim { #3 \l_@@_top_dim }
+ \dim_set:Nn \l_@@_bottom_new_dim { #3 \l_@@_bottom_dim }
+ }
+ {
+ \dim_set:Nn \l_@@_top_new_dim { -#3 \l_@@_bottom_dim }
+ \dim_set:Nn \l_@@_bottom_new_dim { -#3 \l_@@_top_dim }
+ }
+ \fp_compare:nNnTF \l_@@_scale_x_fp > \c_zero_fp
+ { \l_@@_right_new_dim #2 \l_@@_right_dim }
+ { \l_@@_right_new_dim -#2 \l_@@_right_dim }
+ \@@_resize_common:N #1
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[aux]{\@@_resize_common:N}
+% The main resize function places in input into a box which will start
+% of with zero width, and includes the handles for engine rescaling.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_resize_common:N #1
+ {
+ \hbox_set:Nn \l_@@_internal_box
+ {
+ \__driver_box_scale_begin:
+ \hbox_overlap_right:n { \box_use:N #1 }
+ \__driver_box_scale_end:
+ }
+% \end{macrocode}
+% The new height and depth can be applied directly.
+% \begin{macrocode}
+ \box_set_ht:Nn \l_@@_internal_box { \l_@@_top_new_dim }
+ \box_set_dp:Nn \l_@@_internal_box { \l_@@_bottom_new_dim }
+% \end{macrocode}
+% Things are not quite as obvious for the width, as the reference point
+% needs to remain unchanged. For positive scaling factors resizing the
+% box is all that is needed. However, for case of a negative scaling
+% the material must be shifted such that the reference point ends up in
+% the right place.
+% \begin{macrocode}
+ \fp_compare:nNnTF \l_@@_scale_x_fp < \c_zero_fp
+ {
+ \hbox_to_wd:nn { \l_@@_right_new_dim }
+ {
+ \tex_kern:D \l_@@_right_new_dim
+ \box_use:N \l_@@_internal_box
+ \tex_hss:D
+ }
+ }
+ {
+ \box_set_wd:Nn \l_@@_internal_box { \l_@@_right_new_dim }
+ \box_use:N \l_@@_internal_box
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Viewing part of a box}
+%
+% \begin{macro}{\box_clip:N, \box_clip:c}
+% A wrapper around the driver-dependent code.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_clip:N #1
+ { \hbox_set:Nn #1 { \__driver_box_use_clip:N #1 } }
+\cs_generate_variant:Nn \box_clip:N { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\box_trim:Nnnnn, \box_trim:cnnnn}
+% Trimming from the left- and right-hand edges of the box is easy. The total
+% width is set to remove from the right, and a skip will shift the material
+% to remove from the left.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_trim:Nnnnn #1#2#3#4#5
+ {
+ \box_set_wd:Nn #1 { \box_wd:N #1 - (#4) - (#2) }
+ \hbox_set:Nn #1
+ {
+ \skip_horizontal:n { - \dim_eval:n {#2} }
+ \box_use:N #1
+ }
+% \end{macrocode}
+% For the height and depth, there is a need to watch the baseline is
+% respected. Material always has to stay on the correct side, so trimming
+% has to check that there is enough material to trim.
+% \begin{macrocode}
+ \dim_compare:nNnTF { \box_dp:N #1 } > {#3}
+ { \box_set_dp:Nn #1 { \box_dp:N #1 - (#3) } }
+ {
+ \hbox_set:Nn #1
+ { \box_move_down:nn { #3 - \box_dp:N #1 } { \box_use:N #1 } }
+ \box_set_dp:Nn #1 \c_zero_dim
+ }
+ \dim_compare:nNnTF { \box_ht:N #1 } > {#5}
+ { \box_set_ht:Nn #1 { \box_ht:N #1 - (#5) } }
+ {
+ \hbox_set:Nn #1
+ { \box_move_up:nn { #5 - \box_ht:N #1 } { \box_use:N #1 } }
+ \box_set_ht:Nn #1 \c_zero_dim
+ }
+ }
+\cs_generate_variant:Nn \box_trim:Nnnnn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\box_viewport:Nnnnn, \box_viewport:cnnnn}
+% The same general logic as for clipping, but with absolute dimensions.
+% Thus again width is easy and height is harder.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_viewport:Nnnnn #1#2#3#4#5
+ {
+ \box_set_wd:Nn #1 { (#4) - (#2) }
+ \hbox_set:Nn #1
+ {
+ \skip_horizontal:n { - \dim_eval:n {#2} }
+ \box_use:N #1
+ }
+ \dim_compare:nNnTF {#3} > \c_zero_dim
+ {
+ \hbox_set:Nn #1 { \box_move_down:nn {#3} { \box_use:N #1 } }
+ \box_set_dp:Nn #1 \c_zero_dim
+ }
+ { \box_set_dp:Nn #1 { - \dim_eval:n {#3} } }
+ \dim_compare:nNnTF {#5} > \c_zero_dim
+ { \box_set_ht:Nn #1 {#5} }
+ {
+ \hbox_set:Nn #1
+ { \box_move_up:nn { -\dim_eval:n {#5} } { \box_use:N #1 } }
+ \box_set_ht:Nn #1 \c_zero_dim
+ }
+ }
+\cs_generate_variant:Nn \box_viewport:Nnnnn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3clist}}
+%
+% \begin{macrocode}
+%<@@=clist>
+% \end{macrocode}
+%
+% \begin{macro}{\clist_item:Nn, \clist_item:cn}
+% \begin{macro}[aux]{\@@_item:nnNn}
+% \begin{macro}[aux]{\@@_item_N_loop:nw}
+% To avoid needing to test the end of the list at each step,
+% we first compute the \meta{length} of the list. If the item number
+% is~$0$, less than $-\meta{length}$, or more than $\meta{length}$,
+% the result is empty. If it is negative, but not less than $-\meta{length}$,
+% add $\meta{length}+1$ to the item number before performing the loop.
+% The loop itself is very simple, return the item if the counter
+% reached~$1$, otherwise, decrease the counter and repeat.
+% \begin{macrocode}
+\cs_new:Npn \clist_item:Nn #1#2
+ {
+ \exp_args:Nfo \@@_item:nnNn
+ { \clist_count:N #1 }
+ #1
+ \@@_item_N_loop:nw
+ {#2}
+ }
+\cs_new:Npn \@@_item:nnNn #1#2#3#4
+ {
+ \int_compare:nNnTF {#4} < \c_zero
+ {
+ \int_compare:nNnTF {#4} < { - #1 }
+ { \use_none_delimit_by_q_stop:w }
+ { \exp_args:Nf #3 { \int_eval:n { #4 + \c_one + #1 } } }
+ }
+ {
+ \int_compare:nNnTF {#4} > {#1}
+ { \use_none_delimit_by_q_stop:w }
+ { #3 {#4} }
+ }
+ { } , #2 , \q_stop
+ }
+\cs_new:Npn \@@_item_N_loop:nw #1 #2,
+ {
+ \int_compare:nNnTF {#1} = \c_zero
+ { \use_i_delimit_by_q_stop:nw { \exp_not:n {#2} } }
+ { \exp_args:Nf \@@_item_N_loop:nw { \int_eval:n { #1 - 1 } } }
+ }
+\cs_generate_variant:Nn \clist_item:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\clist_item:nn}
+% \begin{macro}[aux]{
+% \@@_item_n:nw,
+% \@@_item_n_loop:nw,
+% \@@_item_n_end:n,
+% \@@_item_n_strip:w}
+% This starts in the same way as \cs{clist_item:Nn} by counting the items
+% of the comma list. The final item should be space-trimmed before being
+% brace-stripped, hence we insert a couple of odd-looking
+% \cs{prg_do_nothing:} to avoid losing braces. Blank items are ignored.
+% \begin{macrocode}
+\cs_new:Npn \clist_item:nn #1#2
+ {
+ \exp_args:Nf \@@_item:nnNn
+ { \clist_count:n {#1} }
+ {#1}
+ \@@_item_n:nw
+ {#2}
+ }
+\cs_new:Npn \@@_item_n:nw #1
+ { \@@_item_n_loop:nw {#1} \prg_do_nothing: }
+\cs_new:Npn \@@_item_n_loop:nw #1 #2,
+ {
+ \exp_args:No \tl_if_blank:nTF {#2}
+ { \@@_item_n_loop:nw {#1} \prg_do_nothing: }
+ {
+ \int_compare:nNnTF {#1} = \c_zero
+ { \exp_args:No \@@_item_n_end:n {#2} }
+ {
+ \exp_args:Nf \@@_item_n_loop:nw
+ { \int_eval:n { #1 - 1 } }
+ \prg_do_nothing:
+ }
+ }
+ }
+\cs_new:Npn \@@_item_n_end:n #1 #2 \q_stop
+ {
+ \__tl_trim_spaces:nn { \q_mark #1 }
+ { \exp_last_unbraced:No \@@_item_n_strip:w } ,
+ }
+\cs_new:Npn \@@_item_n_strip:w #1 , { \exp_not:n {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \clist_set_from_seq:NN, \clist_set_from_seq:cN,
+% \clist_set_from_seq:Nc, \clist_set_from_seq:cc
+% }
+% \UnitTested
+% \begin{macro}
+% {
+% \clist_gset_from_seq:NN, \clist_gset_from_seq:cN,
+% \clist_gset_from_seq:Nc, \clist_gset_from_seq:cc
+% }
+% \UnitTested
+% \begin{macro}[aux]{\@@_set_from_seq:NNNN}
+% \begin{macro}[aux]{\@@_wrap_item:n}
+% \begin{macro}[aux]{\@@_set_from_seq:w}
+% Setting a comma list from a comma-separated list is done using a simple
+% mapping. We wrap most items with \cs{exp_not:n}, and a comma. Items which
+% contain a comma or a space are surrounded by an extra set of braces. The
+% first comma must be removed, except in the case of an empty comma-list.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_set_from_seq:NN
+ { \@@_set_from_seq:NNNN \clist_clear:N \tl_set:Nx }
+\cs_new_protected:Npn \clist_gset_from_seq:NN
+ { \@@_set_from_seq:NNNN \clist_gclear:N \tl_gset:Nx }
+\cs_new_protected:Npn \@@_set_from_seq:NNNN #1#2#3#4
+ {
+ \seq_if_empty:NTF #4
+ { #1 #3 }
+ {
+ #2 #3
+ {
+ \exp_last_unbraced:Nf \use_none:n
+ { \seq_map_function:NN #4 \@@_wrap_item:n }
+ }
+ }
+ }
+\cs_new:Npn \@@_wrap_item:n #1
+ {
+ ,
+ \tl_if_empty:oTF { \@@_set_from_seq:w #1 ~ , #1 ~ }
+ { \exp_not:n {#1} }
+ { \exp_not:n { {#1} } }
+ }
+\cs_new:Npn \@@_set_from_seq:w #1 , #2 ~ { }
+\cs_generate_variant:Nn \clist_set_from_seq:NN { Nc }
+\cs_generate_variant:Nn \clist_set_from_seq:NN { c , cc }
+\cs_generate_variant:Nn \clist_gset_from_seq:NN { Nc }
+\cs_generate_variant:Nn \clist_gset_from_seq:NN { c , cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \clist_const:Nn, \clist_const:cn,
+% \clist_const:Nx, \clist_const:cx
+% }
+% Creating and initializing a constant comma list is done in a way
+% similar to \cs{clist_set:Nn} and \cs{clist_gset:Nn}, being careful
+% to strip spaces.
+% \begin{macrocode}
+\cs_new_protected:Npn \clist_const:Nn #1#2
+ { \tl_const:Nx #1 { \@@_trim_spaces:n {#2} } }
+\cs_generate_variant:Nn \clist_const:Nn { c , Nx , cx }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP, pTF]{\clist_if_empty:n}
+% \begin{macro}[aux, EXP]{\@@_if_empty_n:w}
+% \begin{macro}[aux, EXP]{\@@_if_empty_n:wNw}
+% As usual, we insert a token (here |?|) before grabbing
+% any argument: this avoids losing braces. The argument
+% of \cs{tl_if_empty:oTF} is empty if |#1| is |?| followed
+% by blank spaces (besides, this particular variant of
+% the emptyness test is optimized). If the item of the
+% comma list is blank, grab the next one. As soon as one
+% item is non-blank, exit: the second auxiliary will grab
+% \cs{prg_return_false:} as |#2|, unless every item in
+% the comma list was blank and the loop actually got broken
+% by the trailing |\q_mark \prg_return_false:| item.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \clist_if_empty:n #1 { p , T , F , TF }
+ {
+ \@@_if_empty_n:w ? #1
+ , \q_mark \prg_return_false:
+ , \q_mark \prg_return_true:
+ \q_stop
+ }
+\cs_new:Npn \@@_if_empty_n:w #1 ,
+ {
+ \tl_if_empty:oTF { \use_none:nn #1 ? }
+ { \@@_if_empty_n:w ? }
+ { \@@_if_empty_n:wNw }
+ }
+\cs_new:Npn \@@_if_empty_n:wNw #1 \q_mark #2#3 \q_stop {#2}
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\clist_use:Nnnn}
+% \begin{macro}[EXP, aux]
+% {\@@_use:wwn, \@@_use_ii:nwwwwnwn, \@@_use_iii:nwwn}
+% First check that the variable exists. Then count the items in the
+% comma list. If it has none, output nothing. If it has one item,
+% output that item, brace stripped (note that space-trimming has
+% already been done when the comma list was assigned). If it has two,
+% place the \meta{separator~between~two} in the middle.
+%
+% Otherwise, \cs{@@_use_ii:nwwwwnwn} takes the following arguments; 1:
+% a \meta{separator}, 2, 3, 4: three items from the comma list (or
+% quarks), 5: the rest of the comma list, 6: a \meta{continuation}
+% function (\texttt{use_ii} or \texttt{use_iii} with its
+% \meta{separator} argument), 7: junk, and 8: the temporary result,
+% which is built in a brace group following \cs{q_stop}. The
+% \meta{separator} and the first of the three items are placed in the
+% result, then we use the \meta{continuation}, placing the remaining
+% two items after it. When we begin this loop, the three items really
+% belong to the comma list, the first \cs{q_mark} is taken as a
+% delimiter to the \texttt{use_ii} function, and the continuation is
+% \texttt{use_ii} itself. When we reach the last two items of the
+% original token list, \cs{q_mark} is taken as a third item, and now
+% the seconf \cs{q_mark} serves as a delimiter to \texttt{use_ii},
+% switching to the other \meta{continuation}, \texttt{use_iii}, which
+% uses the \meta{separator between final two}.
+% \begin{macrocode}
+\cs_new:Npn \clist_use:Nnnn #1#2#3#4
+ {
+ \clist_if_exist:NTF #1
+ {
+ \int_case:nnn { \clist_count:N #1 }
+ {
+ { 0 } { }
+ { 1 } { \exp_after:wN \@@_use:wwn #1 , , { } }
+ { 2 } { \exp_after:wN \@@_use:wwn #1 , {#2} }
+ }
+ {
+ \exp_after:wN \@@_use_ii:nwwwwnwn
+ \exp_after:wN { \exp_after:wN } #1 ,
+ \q_mark , { \@@_use_ii:nwwwwnwn {#3} }
+ \q_mark , { \@@_use_iii:nwwn {#4} }
+ \q_stop { }
+ }
+ }
+ { \__msg_kernel_expandable_error:nnn { kernel } { bad-variable } {#1} }
+ }
+\cs_new:Npn \@@_use:wwn #1 , #2 , #3 { \exp_not:n { #1 #3 #2 } }
+\cs_new:Npn \@@_use_ii:nwwwwnwn
+ #1#2 , #3 , #4 , #5 \q_mark , #6#7 \q_stop #8
+ { #6 {#3} , {#4} , #5 \q_mark , {#6} #7 \q_stop { #8 #1 #2 } }
+\cs_new:Npn \@@_use_iii:nwwn #1#2 , #3 \q_stop #4
+ { \exp_not:n { #4 #1 #2 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3coffins}}
+%
+% \begin{macrocode}
+%<@@=coffin>
+% \end{macrocode}
+%
+% \subsection{Rotating coffins}
+%
+% \begin{variable}{\l_@@_sin_fp}
+% \begin{variable}{\l_@@_cos_fp}
+% Used for rotations to get the sine and cosine values.
+% \begin{macrocode}
+\fp_new:N \l_@@_sin_fp
+\fp_new:N \l_@@_cos_fp
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_bounding_prop}
+% A property list for the bounding box of a coffin. This is only needed
+% during the rotation, so there is just the one.
+% \begin{macrocode}
+\prop_new:N \l_@@_bounding_prop
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_bounding_shift_dim}
+% The shift of the bounding box of a coffin from the real content.
+% \begin{macrocode}
+\dim_new:N \l_@@_bounding_shift_dim
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_left_corner_dim}
+% \begin{variable}{\l_@@_right_corner_dim}
+% \begin{variable}{\l_@@_bottom_corner_dim}
+% \begin{variable}{\l_@@_top_corner_dim}
+% These are used to hold maxima for the various corner values: these
+% thus define the minimum size of the bounding box after rotation.
+% \begin{macrocode}
+\dim_new:N \l_@@_left_corner_dim
+\dim_new:N \l_@@_right_corner_dim
+\dim_new:N \l_@@_bottom_corner_dim
+\dim_new:N \l_@@_top_corner_dim
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+% \end{variable}
+% \end{variable}
+%
+% \begin{macro}{\coffin_rotate:Nn, \coffin_rotate:cn}
+% Rotating a coffin requires several steps which can be conveniently
+% run together. The first step is to convert the angle given in degrees
+% to one in radians. This is then used to set \cs{l_@@_sin_fp} and
+% \cs{l_@@_cos_fp}, which are carried through unchanged for the rest
+% of the procedure.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_rotate:Nn #1#2
+ {
+ \fp_set:Nn \l_@@_sin_fp { sin ( ( #2 ) * deg ) }
+ \fp_set:Nn \l_@@_cos_fp { cos ( ( #2 ) * deg ) }
+% \end{macrocode}
+% The corners and poles of the coffin can now be rotated around the
+% origin. This is best achieved using mapping functions.
+% \begin{macrocode}
+ \prop_map_inline:cn { l_@@_corners_ \__int_value:w #1 _prop }
+ { \@@_rotate_corner:Nnnn #1 {##1} ##2 }
+ \prop_map_inline:cn { l_@@_poles_ \__int_value:w #1 _prop }
+ { \@@_rotate_pole:Nnnnnn #1 {##1} ##2 }
+% \end{macrocode}
+% The bounding box of the coffin needs to be rotated, and to do this
+% the corners have to be found first. They are then rotated in the same
+% way as the corners of the coffin material itself.
+% \begin{macrocode}
+ \@@_set_bounding:N #1
+ \prop_map_inline:Nn \l_@@_bounding_prop
+ { \@@_rotate_bounding:nnn {##1} ##2 }
+% \end{macrocode}
+% At this stage, there needs to be a calculation to find where the
+% corners of the content and the box itself will end up.
+% \begin{macrocode}
+ \@@_find_corner_maxima:N #1
+ \@@_find_bounding_shift:
+ \box_rotate:Nn #1 {#2}
+% \end{macrocode}
+% The correction of the box position itself takes place here. The idea
+% is that the bounding box for a coffin is tight up to the content, and
+% has the reference point at the bottom-left. The $x$-direction is
+% handled by moving the content by the difference in the positions of
+% the bounding box and the content left edge. The $y$-direction is
+% dealt with by moving the box down by any depth it has acquired.
+% \begin{macrocode}
+ \hbox_set:Nn #1
+ {
+ \tex_kern:D \l_@@_bounding_shift_dim
+ \tex_kern:D -\l_@@_left_corner_dim
+ \box_move_down:nn { \l_@@_bottom_corner_dim }
+ { \box_use:N #1 }
+ }
+% \end{macrocode}
+% If there have been any previous rotations then the size of the
+% bounding box will be bigger than the contents. This can be corrected
+% easily by setting the size of the box to the height and width of the
+% content.
+% \begin{macrocode}
+ \box_set_ht:Nn #1
+ { \l_@@_top_corner_dim - \l_@@_bottom_corner_dim }
+ \box_set_dp:Nn #1 { 0 pt }
+ \box_set_wd:Nn #1
+ { \l_@@_right_corner_dim - \l_@@_left_corner_dim }
+% \end{macrocode}
+% The final task is to move the poles and corners such that they are
+% back in alignment with the box reference point.
+% \begin{macrocode}
+ \prop_map_inline:cn { l_@@_corners_ \__int_value:w #1 _prop }
+ { \@@_shift_corner:Nnnn #1 {##1} ##2 }
+ \prop_map_inline:cn { l_@@_poles_ \__int_value:w #1 _prop }
+ { \@@_shift_pole:Nnnnnn #1 {##1} ##2 }
+ }
+\cs_generate_variant:Nn \coffin_rotate:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_set_bounding:N}
+% The bounding box corners for a coffin are easy enough to find: this
+% is the same code as for the corners of the material itself, but
+% using a dedicated property list.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_set_bounding:N #1
+ {
+ \prop_put:Nnx \l_@@_bounding_prop { tl }
+ { { 0 pt } { \dim_use:N \box_ht:N #1 } }
+ \prop_put:Nnx \l_@@_bounding_prop { tr }
+ { { \dim_use:N \box_wd:N #1 } { \dim_use:N \box_ht:N #1 } }
+ \dim_set:Nn \l_@@_internal_dim { - \box_dp:N #1 }
+ \prop_put:Nnx \l_@@_bounding_prop { bl }
+ { { 0 pt } { \dim_use:N \l_@@_internal_dim } }
+ \prop_put:Nnx \l_@@_bounding_prop { br }
+ { { \dim_use:N \box_wd:N #1 } { \dim_use:N \l_@@_internal_dim } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_rotate_bounding:nnn}
+% \begin{macro}{\@@_rotate_corner:Nnnn}
+% Rotating the position of the corner of the coffin is just a case
+% of treating this as a vector from the reference point. The same
+% treatment is used for the corners of the material itself and the
+% bounding box.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_rotate_bounding:nnn #1#2#3
+ {
+ \@@_rotate_vector:nnNN {#2} {#3} \l_@@_x_dim \l_@@_y_dim
+ \prop_put:Nnx \l_@@_bounding_prop {#1}
+ { { \dim_use:N \l_@@_x_dim } { \dim_use:N \l_@@_y_dim } }
+ }
+\cs_new_protected:Npn \@@_rotate_corner:Nnnn #1#2#3#4
+ {
+ \@@_rotate_vector:nnNN {#3} {#4} \l_@@_x_dim \l_@@_y_dim
+ \prop_put:cnx { l_@@_corners_ \__int_value:w #1 _prop } {#2}
+ { { \dim_use:N \l_@@_x_dim } { \dim_use:N \l_@@_y_dim } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_rotate_pole:Nnnnnn}
+% Rotating a single pole simply means shifting the co-ordinate of
+% the pole and its direction. The rotation here is about the bottom-left
+% corner of the coffin.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_rotate_pole:Nnnnnn #1#2#3#4#5#6
+ {
+ \@@_rotate_vector:nnNN {#3} {#4} \l_@@_x_dim \l_@@_y_dim
+ \@@_rotate_vector:nnNN {#5} {#6}
+ \l_@@_x_prime_dim \l_@@_y_prime_dim
+ \@@_set_pole:Nnx #1 {#2}
+ {
+ { \dim_use:N \l_@@_x_dim } { \dim_use:N \l_@@_y_dim }
+ { \dim_use:N \l_@@_x_prime_dim }
+ { \dim_use:N \l_@@_y_prime_dim }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_rotate_vector:nnNN}
+% A rotation function, which needs only an input vector (as dimensions)
+% and an output space. The values \cs{l_@@_cos_fp} and
+% \cs{l_@@_sin_fp} should previously have been set up correctly.
+% Working this way means that the floating point work is kept to a
+% minimum: for any given rotation the sin and cosine values do no
+% change, after all.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_rotate_vector:nnNN #1#2#3#4
+ {
+ \dim_set:Nn #3
+ {
+ \fp_to_dim:n
+ {
+ \dim_to_fp:n {#1} * \l_@@_cos_fp
+ - ( \dim_to_fp:n {#2} * \l_@@_sin_fp )
+ }
+ }
+ \dim_set:Nn #4
+ {
+ \fp_to_dim:n
+ {
+ \dim_to_fp:n {#1} * \l_@@_sin_fp
+ + ( \dim_to_fp:n {#2} * \l_@@_cos_fp )
+ }
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_find_corner_maxima:N}
+% \begin{macro}[aux]{\@@_find_corner_maxima_aux:nn}
+% The idea here is to find the extremities of the content of the
+% coffin. This is done by looking for the smallest values for the bottom
+% and left corners, and the largest values for the top and right
+% corners. The values start at the maximum dimensions so that the
+% case where all are positive or all are negative works out correctly.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_find_corner_maxima:N #1
+ {
+ \dim_set:Nn \l_@@_top_corner_dim { -\c_max_dim }
+ \dim_set:Nn \l_@@_right_corner_dim { -\c_max_dim }
+ \dim_set:Nn \l_@@_bottom_corner_dim { \c_max_dim }
+ \dim_set:Nn \l_@@_left_corner_dim { \c_max_dim }
+ \prop_map_inline:cn { l_@@_corners_ \__int_value:w #1 _prop }
+ { \@@_find_corner_maxima_aux:nn ##2 }
+ }
+\cs_new_protected:Npn \@@_find_corner_maxima_aux:nn #1#2
+ {
+ \dim_set_min:Nn \l_@@_left_corner_dim {#1}
+ \dim_set_max:Nn \l_@@_right_corner_dim {#1}
+ \dim_set_min:Nn \l_@@_bottom_corner_dim {#2}
+ \dim_set_max:Nn \l_@@_top_corner_dim {#2}
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_find_bounding_shift:}
+% \begin{macro}[aux]{\@@_find_bounding_shift_aux:nn}
+% The approach to finding the shift for the bounding box is similar to
+% that for the corners. However, there is only one value needed here and
+% a fixed input property list, so things are a bit clearer.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_find_bounding_shift:
+ {
+ \dim_set:Nn \l_@@_bounding_shift_dim { \c_max_dim }
+ \prop_map_inline:Nn \l_@@_bounding_prop
+ { \@@_find_bounding_shift_aux:nn ##2 }
+ }
+\cs_new_protected:Npn \@@_find_bounding_shift_aux:nn #1#2
+ { \dim_set_min:Nn \l_@@_bounding_shift_dim {#1} }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_shift_corner:Nnnn}
+% \begin{macro}{\@@_shift_pole:Nnnnnn}
+% Shifting the corners and poles of a coffin means subtracting the
+% appropriate values from the $x$- and $y$-components. For
+% the poles, this means that the direction vector is unchanged.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_shift_corner:Nnnn #1#2#3#4
+ {
+ \prop_put:cnx { l_@@_corners_ \__int_value:w #1 _ prop } {#2}
+ {
+ { \dim_eval:n { #3 - \l_@@_left_corner_dim } }
+ { \dim_eval:n { #4 - \l_@@_bottom_corner_dim } }
+ }
+ }
+\cs_new_protected:Npn \@@_shift_pole:Nnnnnn #1#2#3#4#5#6
+ {
+ \prop_put:cnx { l_@@_poles_ \__int_value:w #1 _ prop } {#2}
+ {
+ { \dim_eval:n { #3 - \l_@@_left_corner_dim } }
+ { \dim_eval:n { #4 - \l_@@_bottom_corner_dim } }
+ {#5} {#6}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Resizing coffins}
+%
+% \begin{variable}{\l_@@_scale_x_fp}
+% \begin{variable}{\l_@@_scale_y_fp}
+% Storage for the scaling factors in $x$ and $y$, respectively.
+% \begin{macrocode}
+\fp_new:N \l_@@_scale_x_fp
+\fp_new:N \l_@@_scale_y_fp
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{variable}{\l_@@_scaled_total_height_dim}
+% \begin{variable}{\l_@@_scaled_width_dim}
+% When scaling, the values given have to be turned into absolute values.
+% \begin{macrocode}
+\dim_new:N \l_@@_scaled_total_height_dim
+\dim_new:N \l_@@_scaled_width_dim
+% \end{macrocode}
+% \end{variable}
+% \end{variable}
+%
+% \begin{macro}{\coffin_resize:Nnn, \coffin_resize:cnn}
+% Resizing a coffin begins by setting up the user-friendly names for
+% the dimensions of the coffin box. The new sizes are then turned into
+% scale factor. This is the same operation as takes place for the
+% underlying box, but that operation is grouped and so the same
+% calculation is done here.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_resize:Nnn #1#2#3
+ {
+ \@@_set_user_dimensions:N #1
+ \box_resize:Nnn #1 {#2} {#3}
+ \fp_set:Nn \l_@@_scale_x_fp
+ { \dim_to_fp:n {#2} / \dim_to_fp:n \Width }
+ \fp_set:Nn \l_@@_scale_y_fp
+ { \dim_to_fp:n {#3} / \dim_to_fp:n \TotalHeight }
+ \@@_resize_common:Nnn #1 {#2} {#3}
+ }
+\cs_generate_variant:Nn \coffin_resize:Nnn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_resize_common:Nnn}
+% The poles and corners of the coffin are scaled to the appropriate
+% places before actually resizing the underlying box.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_resize_common:Nnn #1#2#3
+ {
+ \prop_map_inline:cn { l_@@_corners_ \__int_value:w #1 _prop }
+ { \@@_scale_corner:Nnnn #1 {##1} ##2 }
+ \prop_map_inline:cn { l_@@_poles_ \__int_value:w #1 _prop }
+ { \@@_scale_pole:Nnnnnn #1 {##1} ##2 }
+% \end{macrocode}
+% Negative $x$-scaling values will place the poles in the wrong
+% location: this is corrected here.
+% \begin{macrocode}
+ \fp_compare:nNnT \l_@@_scale_x_fp < \c_zero_fp
+ {
+ \prop_map_inline:cn { l_@@_corners_ \__int_value:w #1 _prop }
+ { \@@_x_shift_corner:Nnnn #1 {##1} ##2 }
+ \prop_map_inline:cn { l_@@_poles_ \__int_value:w #1 _prop }
+ { \@@_x_shift_pole:Nnnnnn #1 {##1} ##2 }
+ }
+ \@@_end_user_dimensions:
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\coffin_scale:Nnn, \coffin_scale:cnn}
+% For scaling, the opposite calculation is done to find the new
+% dimensions for the coffin. Only the total height is needed, as this
+% is the shift required for corners and poles. The scaling is done
+% the \TeX{} way as this works properly with floating point values
+% without needing to use the \texttt{fp} module.
+% \begin{macrocode}
+\cs_new_protected:Npn \coffin_scale:Nnn #1#2#3
+ {
+ \box_scale:Nnn #1 {#2} {#3}
+ \@@_set_user_dimensions:N #1
+ \fp_set:Nn \l_@@_scale_x_fp {#2}
+ \fp_set:Nn \l_@@_scale_y_fp {#3}
+ \fp_compare:nNnTF \l_@@_scale_y_fp > \c_zero_fp
+ { \l_@@_scaled_total_height_dim #3 \TotalHeight }
+ { \l_@@_scaled_total_height_dim -#3 \TotalHeight }
+ \fp_compare:nNnTF \l_@@_scale_x_fp > \c_zero_fp
+ { \l_@@_scaled_width_dim -#2 \Width }
+ { \l_@@_scaled_width_dim #2 \Width }
+ \@@_resize_common:Nnn #1
+ { \l_@@_scaled_width_dim } { \l_@@_scaled_total_height_dim }
+ }
+\cs_generate_variant:Nn \coffin_scale:Nnn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_scale_vector:nnNN}
+% This functions scales a vector from the origin using the pre-set scale
+% factors in $x$ and $y$. This is a much less complex operation
+% than rotation, and as a result the code is a lot clearer.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_scale_vector:nnNN #1#2#3#4
+ {
+ \dim_set:Nn #3
+ { \fp_to_dim:n { \dim_to_fp:n {#1} * \l_@@_scale_x_fp } }
+ \dim_set:Nn #4
+ { \fp_to_dim:n { \dim_to_fp:n {#2} * \l_@@_scale_y_fp } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\@@_scale_corner:Nnnn}
+% \begin{macro}{\@@_scale_pole:Nnnnnn}
+% Scaling both corners and poles is a simple calculation using the
+% preceding vector scaling.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_scale_corner:Nnnn #1#2#3#4
+ {
+ \@@_scale_vector:nnNN {#3} {#4} \l_@@_x_dim \l_@@_y_dim
+ \prop_put:cnx { l_@@_corners_ \__int_value:w #1 _prop } {#2}
+ { { \dim_use:N \l_@@_x_dim } { \dim_use:N \l_@@_y_dim } }
+ }
+\cs_new_protected:Npn \@@_scale_pole:Nnnnnn #1#2#3#4#5#6
+ {
+ \@@_scale_vector:nnNN {#3} {#4} \l_@@_x_dim \l_@@_y_dim
+ \@@_set_pole:Nnx #1 {#2}
+ {
+ { \dim_use:N \l_@@_x_dim } { \dim_use:N \l_@@_y_dim }
+ {#5} {#6}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\@@_x_shift_corner:Nnnn}
+% \begin{macro}{\@@_x_shift_pole:Nnnnnn}
+% These functions correct for the $x$ displacement that takes
+% place with a negative horizontal scaling.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_x_shift_corner:Nnnn #1#2#3#4
+ {
+ \prop_put:cnx { l_@@_corners_ \__int_value:w #1 _prop } {#2}
+ {
+ { \dim_eval:n { #3 + \box_wd:N #1 } } {#4}
+ }
+ }
+\cs_new_protected:Npn \@@_x_shift_pole:Nnnnnn #1#2#3#4#5#6
+ {
+ \prop_put:cnx { l_@@_poles_ \__int_value:w #1 _prop } {#2}
+ {
+ { \dim_eval:n #3 + \box_wd:N #1 } {#4}
+ {#5} {#6}
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3file}}
+%
+% \begin{macrocode}
+%<@@=ior>
+% \end{macrocode}
+%
+% \begin{macro}[EXP]{\ior_map_break:, \ior_map_break:n}
+% Usual map breaking functions. Those are not yet in \pkg{l3kernel}
+% proper since the mapping below is the first of its kind.
+% \begin{macrocode}
+\cs_new_nopar:Npn \ior_map_break:
+ { \__prg_map_break:Nn \ior_map_break: { } }
+\cs_new_nopar:Npn \ior_map_break:n
+ { \__prg_map_break:Nn \ior_map_break: }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\ior_map_inline:Nn, \ior_str_map_inline:Nn}
+% \begin{macro}[aux]{\@@_map_inline:NNn}
+% \begin{macro}[aux]{\@@_map_inline:NNNn}
+% \begin{macro}[aux]{\@@_map_inline_loop:NNN}
+% \begin{variable}{\l_@@_internal_tl}
+% Mapping to an input stream can be done on either a token or a string
+% basis, hence the set up. Within that, there is a check to avoid reading
+% past the end of a file, hence the two applications of \cs{ior_if_eof:N}.
+% This mapping cannot be nested as the stream has only one \enquote{current
+% line}.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \ior_map_inline:Nn
+ { \@@_map_inline:NNn \ior_get:NN }
+\cs_new_protected_nopar:Npn \ior_str_map_inline:Nn
+ { \@@_map_inline:NNn \ior_get_str:NN }
+\cs_new_protected_nopar:Npn \@@_map_inline:NNn
+ {
+ \int_gincr:N \g__prg_map_int
+ \exp_args:Nc \@@_map_inline:NNNn
+ { __prg_map_ \int_use:N \g__prg_map_int :n }
+ }
+\cs_new_protected:Npn \@@_map_inline:NNNn #1#2#3#4
+ {
+ \cs_set:Npn #1 ##1 {#4}
+ \ior_if_eof:NF #3 { \@@_map_inline_loop:NNN #1#2#3 }
+ \__prg_break_point:Nn \ior_map_break:
+ { \int_gdecr:N \g__prg_map_int }
+ }
+\cs_new_protected:Npn \@@_map_inline_loop:NNN #1#2#3
+ {
+ #2 #3 \l_@@_internal_tl
+ \ior_if_eof:NF #3
+ {
+ \exp_args:No #1 \l_@@_internal_tl
+ \@@_map_inline_loop:NNN #1#2#3
+ }
+ }
+\tl_new:N \l_@@_internal_tl
+% \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3fp}}
+%
+% \begin{macrocode}
+%<@@=fp>
+% \end{macrocode}
+%
+% \begin{macro}
+% {
+% \fp_set_from_dim:Nn, \fp_set_from_dim:cn,
+% \fp_gset_from_dim:Nn, \fp_gset_from_dim:cn
+% }
+% Use the appropriate function from \pkg{l3fp-convert}.
+% \begin{macrocode}
+\cs_new_protected:Npn \fp_set_from_dim:Nn #1#2
+ { \tl_set:Nx #1 { \dim_to_fp:n {#2} } }
+\cs_new_protected:Npn \fp_gset_from_dim:Nn #1#2
+ { \tl_gset:Nx #1 { \dim_to_fp:n {#2} } }
+\cs_generate_variant:Nn \fp_set_from_dim:Nn { c }
+\cs_generate_variant:Nn \fp_gset_from_dim:Nn { c }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3prop}}
+%
+% \begin{macrocode}
+%<@@=prop>
+% \end{macrocode}
+%
+% \begin{macro}[rEXP]{\prop_map_tokens:Nn, \prop_map_tokens:cn}
+% \begin{macro}[aux]{\@@_map_tokens:nwn}
+% The mapping grabs one key--value pair at a time, and stops when
+% reaching the marker key \cs{q_recursion_tail}, which
+% cannot appear in normal keys since those are strings. The odd
+% construction |\use:n {#1}| allows |#1| to contain any token.
+% \begin{macrocode}
+\cs_new:Npn \prop_map_tokens:Nn #1#2
+ {
+ \exp_last_unbraced:Nno \@@_map_tokens:nwn {#2} #1
+ \q_recursion_tail \q__prop { }
+ \__prg_break_point:Nn \prop_map_break: { }
+ }
+\cs_new:Npn \@@_map_tokens:nwn #1 \q__prop #2 \q__prop #3
+ {
+ \__quark_if_recursion_tail_break:NN #2 \prop_map_break:
+ \use:n {#1} {#2} {#3}
+ \@@_map_tokens:nwn {#1}
+ }
+\cs_generate_variant:Nn \prop_map_tokens:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\prop_get:Nn, \prop_get:cn}
+% \begin{macro}[aux]{\@@_get_Nn:nwn}
+% Getting the value corresponding to a key in a property list in an
+% expandable fashion is a simple instance of mapping some tokens.
+% Map the function \cs{prop_get:nnn} which takes as its three
+% arguments the \meta{key} that we are looking for, the current
+% \meta{key} and the current \meta{value}. If the \meta{keys} match,
+% the \meta{value} is returned. If none of the keys match, this expands
+% to nothing.
+% \begin{macrocode}
+\cs_new:Npn \prop_get:Nn #1#2
+ {
+ \exp_last_unbraced:Noo \@@_get_Nn:nwn
+ { \tl_to_str:n {#2} } #1
+ \tl_to_str:n {#2} \q__prop { }
+ \__prg_break_point:
+ }
+\cs_new:Npn \@@_get_Nn:nwn #1 \q__prop #2 \q__prop #3
+ {
+ \str_if_eq_x:nnTF {#1} {#2}
+ { \__prg_break:n { \exp_not:n {#3} } }
+ { \@@_get_Nn:nwn {#1} }
+ }
+\cs_generate_variant:Nn \prop_get:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3seq}}
+%
+% \begin{macrocode}
+%<@@=seq>
+% \end{macrocode}
+%
+% \begin{macro}{\seq_item:Nn, \seq_item:cn}
+% \begin{macro}[aux]{\@@_item:nnn}
+% The idea here is to find the offset of the item from the left, then use
+% a loop to grab the correct item. If the resulting offset is too large,
+% then the stop code |{ ? \__prg_break: } { }| will be used by the auxiliary,
+% terminating the loop and returning nothing at all.
+% \begin{macrocode}
+\cs_new:Npn \seq_item:Nn #1#2
+ {
+ \exp_last_unbraced:Nfo \@@_item:nnn
+ {
+ \int_eval:n
+ {
+ \int_compare:nNnT {#2} < \c_zero
+ { \seq_count:N #1 + \c_one + }
+ #2
+ }
+ }
+ #1
+ { ? \__prg_break: }
+ { }
+ \__prg_break_point:
+ }
+\cs_new:Npn \@@_item:nnn #1#2#3
+ {
+ \use_none:n #2
+ \int_compare:nNnTF {#1} = \c_one
+ { \__prg_break:n { \exp_not:n {#3} } }
+ { \exp_args:Nf \@@_item:nnn { \int_eval:n { #1 - 1 } } }
+ }
+\cs_generate_variant:Nn \seq_item:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \seq_mapthread_function:NNN, \seq_mapthread_function:NcN,
+% \seq_mapthread_function:cNN, \seq_mapthread_function:ccN
+% }
+% \begin{macro}[aux]{\@@_mapthread_function:NN}
+% \begin{macro}[aux]{\@@_mapthread_function:Nnnwnn}
+% The idea here is to first expand both of the sequences, adding the usual
+% |{ ? \__prg_break: } { }| to the end of each one. This is most conveniently
+% done in two steps using an auxiliary function. The mapping then throws
+% away the first token of |#2| and |#5|, which for items in the sequences
+% will both be \cs{@@_item:n}. The function to be mapped will then be
+% applied to the two entries. When the code hits the end of one of the
+% sequences, the break material will stop the entire loop and tidy up. This
+% avoids needing to find the count of the two sequences, or worrying about
+% which is longer.
+% \begin{macrocode}
+\cs_new:Npn \seq_mapthread_function:NNN #1#2#3
+ {
+ \exp_after:wN \@@_mapthread_function:NN
+ \exp_after:wN #3
+ \exp_after:wN #1
+ #2
+ { ? \__prg_break: } { }
+ \__prg_break_point:
+ }
+\cs_new:Npn \@@_mapthread_function:NN #1#2
+ {
+ \exp_after:wN \@@_mapthread_function:Nnnwnn
+ \exp_after:wN #1
+ #2
+ { ? \__prg_break: } { }
+ \q_stop
+ }
+\cs_new:Npn \@@_mapthread_function:Nnnwnn #1#2#3#4 \q_stop #5#6
+ {
+ \use_none:n #2
+ \use_none:n #5
+ #1 {#3} {#6}
+ \@@_mapthread_function:Nnnwnn #1 #4 \q_stop
+ }
+\cs_generate_variant:Nn \seq_mapthread_function:NNN { Nc }
+\cs_generate_variant:Nn \seq_mapthread_function:NNN { c , cc }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {
+% \seq_set_from_clist:NN, \seq_set_from_clist:cN,
+% \seq_set_from_clist:Nc, \seq_set_from_clist:cc,
+% \seq_set_from_clist:Nn, \seq_set_from_clist:cn
+% }
+% \begin{macro}
+% {
+% \seq_gset_from_clist:NN, \seq_gset_from_clist:cN,
+% \seq_gset_from_clist:Nc, \seq_gset_from_clist:cc,
+% \seq_gset_from_clist:Nn, \seq_gset_from_clist:cn
+% }
+% Setting a sequence from a comma-separated list is done using a simple
+% mapping.
+% \begin{macrocode}
+\cs_new_protected:Npn \seq_set_from_clist:NN #1#2
+ {
+ \tl_set:Nx #1
+ { \clist_map_function:NN #2 \@@_wrap_item:n }
+ }
+\cs_new_protected:Npn \seq_set_from_clist:Nn #1#2
+ {
+ \tl_set:Nx #1
+ { \clist_map_function:nN {#2} \@@_wrap_item:n }
+ }
+\cs_new_protected:Npn \seq_gset_from_clist:NN #1#2
+ {
+ \tl_gset:Nx #1
+ { \clist_map_function:NN #2 \@@_wrap_item:n }
+ }
+\cs_new_protected:Npn \seq_gset_from_clist:Nn #1#2
+ {
+ \tl_gset:Nx #1
+ { \clist_map_function:nN {#2} \@@_wrap_item:n }
+ }
+\cs_generate_variant:Nn \seq_set_from_clist:NN { Nc }
+\cs_generate_variant:Nn \seq_set_from_clist:NN { c , cc }
+\cs_generate_variant:Nn \seq_set_from_clist:Nn { c }
+\cs_generate_variant:Nn \seq_gset_from_clist:NN { Nc }
+\cs_generate_variant:Nn \seq_gset_from_clist:NN { c , cc }
+\cs_generate_variant:Nn \seq_gset_from_clist:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+% {\seq_reverse:N, \seq_reverse:c, \seq_greverse:N, \seq_greverse:c}
+% \begin{macro}[aux]{\@@_tmp:w}
+% \begin{macro}[aux]{\@@_reverse:NN}
+% \begin{macro}[aux]{\@@_reverse_item:nwn}
+% Previously, \cs{seq_reverse:N} was coded by collecting the items
+% in reverse order after an \cs{exp_stop_f:} marker.
+% \begin{verbatim}
+% \cs_new_protected:Npn \seq_reverse:N #1
+% {
+% \cs_set_eq:NN \@@_item:n \@@_reverse_item:nw
+% \tl_set:Nf #2 { #2 \exp_stop_f: }
+% }
+% \cs_new:Npn \@@_reverse_item:nw #1 #2 \exp_stop_f:
+% {
+% #2 \exp_stop_f:
+% \@@_item:n {#1}
+% }
+% \end{verbatim}
+% At first, this seems optimal, since we can forget about each item
+% as soon as it is placed after \cs{exp_stop_f:}. Unfortunately,
+% \TeX{}'s usual tail recursion does not take place in this case:
+% since the following \cs{@@_reverse_item:nw} only reads
+% tokens until \cs{exp_stop_f:}, and never reads the
+% |\@@_item:n {#1}| left by the previous call, \TeX{} cannot
+% remove that previous call from the stack, and in particular
+% must retain the various macro parameters in memory, until the
+% end of the replacement text is reached. The stack is thus
+% only flushed after all the \cs{@@_reverse_item:nw} are
+% expanded. Keeping track of the arguments of all those calls
+% uses up a memory quadratic in the length of the sequence.
+% \TeX{} can then not cope with more than a few thousand items.
+%
+% Instead, we collect the items in the argument
+% of \cs{exp_not:n}. The previous calls are cleanly removed
+% from the stack, and the memory consumption becomes linear.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_tmp:w { }
+\cs_new_protected_nopar:Npn \seq_reverse:N
+ { \@@_reverse:NN \tl_set:Nx }
+\cs_new_protected_nopar:Npn \seq_greverse:N
+ { \@@_reverse:NN \tl_gset:Nx }
+\cs_new_protected:Npn \@@_reverse:NN #1 #2
+ {
+ \cs_set_eq:NN \@@_tmp:w \@@_item:n
+ \cs_set_eq:NN \@@_item:n \@@_reverse_item:nwn
+ #1 #2 { #2 \exp_not:n { } }
+ \cs_set_eq:NN \@@_item:n \@@_tmp:w
+ }
+\cs_new:Npn \@@_reverse_item:nwn #1 #2 \exp_not:n #3
+ {
+ #2
+ \exp_not:n { \@@_item:n {#1} #3 }
+ }
+\cs_generate_variant:Nn \seq_reverse:N { c }
+\cs_generate_variant:Nn \seq_greverse:N { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_set_filter:NNn, \seq_gset_filter:NNn}
+% \begin{macro}[aux]{\@@_set_filter:NNNn}
+% Similar to \cs{seq_map_inline:Nn}, without a
+% \cs{__prg_break_point:} because the user's code
+% is performed within the evaluation of a boolean expression,
+% and skipping out of that would break horribly.
+% The \cs{@@_wrap_item:n} function inserts the relevant
+% \cs{@@_item:n} without expansion in the input stream,
+% hence in the \texttt{x}-expanding assignment.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \seq_set_filter:NNn
+ { \@@_set_filter:NNNn \tl_set:Nx }
+\cs_new_protected_nopar:Npn \seq_gset_filter:NNn
+ { \@@_set_filter:NNNn \tl_gset:Nx }
+\cs_new_protected:Npn \@@_set_filter:NNNn #1#2#3#4
+ {
+ \@@_push_item_def:n { \bool_if:nT {#4} { \@@_wrap_item:n {##1} } }
+ #1 #2 { #3 }
+ \@@_pop_item_def:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\seq_set_map:NNn,\seq_gset_map:NNn}
+% \begin{macro}[aux]{\@@_set_map:NNNn}
+% Very similar to \cs{seq_set_filter:NNn}. We could actually
+% merge the two within a single function, but it would have weird
+% semantics.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \seq_set_map:NNn
+ { \@@_set_map:NNNn \tl_set:Nx }
+\cs_new_protected_nopar:Npn \seq_gset_map:NNn
+ { \@@_set_map:NNNn \tl_gset:Nx }
+\cs_new_protected:Npn \@@_set_map:NNNn #1#2#3#4
+ {
+ \@@_push_item_def:n { \exp_not:N \@@_item:n {#4} }
+ #1 #2 { #3 }
+ \@@_pop_item_def:
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\seq_use:Nnnn}
+% \begin{macro}[EXP, aux]
+% {\@@_use:NnNnn, \@@_use_ii:nwwwwnwn, \@@_use_iii:nwwn}
+% See \cs{clist_use:Nnnn} for a general explanation. The main
+% difference is that we use \cs{@@_item:n} as a delimiter rather than
+% commas. We also need to add \cs{@@_item:n} at various places.
+% \begin{macrocode}
+\cs_new:Npn \seq_use:Nnnn #1#2#3#4
+ {
+ \seq_if_exist:NTF #1
+ {
+ \int_case:nnn { \seq_count:N #1 }
+ {
+ { 0 } { }
+ { 1 } { \exp_after:wN \@@_use:NnNnn #1 \@@_item:n { } { } }
+ { 2 } { \exp_after:wN \@@_use:NnNnn #1 {#2} }
+ }
+ {
+ \exp_after:wN \@@_use_ii:nwwwwnwn
+ \exp_after:wN { \exp_after:wN } #1 \@@_item:n
+ \q_mark { \@@_use_ii:nwwwwnwn {#3} }
+ \q_mark { \@@_use_iii:nwwn {#4} }
+ \q_stop { }
+ }
+ }
+ { \__msg_kernel_expandable_error:nnn { kernel } { bad-variable } {#1} }
+ }
+\cs_new:Npn \@@_use:NnNnn \@@_item:n #1 \@@_item:n #2#3
+ { \exp_not:n { #1 #3 #2 } }
+\cs_new:Npn \@@_use_ii:nwwwwnwn
+ #1 \@@_item:n #2 \@@_item:n #3 \@@_item:n #4#5
+ \q_mark #6#7 \q_stop #8
+ {
+ #6 \@@_item:n {#3} \@@_item:n {#4} #5
+ \q_mark {#6} #7 \q_stop { #8 #1 #2 }
+ }
+\cs_new:Npn \@@_use_iii:nwwn #1 \@@_item:n #2 #3 \q_stop #4
+ { \exp_not:n { #4 #1 #2 } }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3skip}}
+%
+% \begin{macrocode}
+%<@@=skip>
+% \end{macrocode}
+%
+% \begin{macro}{\skip_split_finite_else_action:nnNN}
+% This macro is useful when performing error checking in certain
+% circumstances. If the \meta{skip} register holds finite glue it sets
+% |#3| and |#4| to the stretch and shrink component, resp. If it holds
+% infinite glue set |#3| and |#4| to zero and issue the special action
+% |#2| which is probably an error message.
+% Assignments are local.
+% \begin{macrocode}
+\cs_new:Npn \skip_split_finite_else_action:nnNN #1#2#3#4
+ {
+ \skip_if_finite:nTF {#1}
+ {
+ #3 = \etex_gluestretch:D #1 \scan_stop:
+ #4 = \etex_glueshrink:D #1 \scan_stop:
+ }
+ {
+ #3 = \c_zero_skip
+ #4 = \c_zero_skip
+ #2
+ }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3tl}}
+%
+% \begin{macrocode}
+%<@@=tl>
+% \end{macrocode}
+%
+% \begin{macro}[EXP,pTF]{\tl_if_single_token:n}
+% There are four cases: empty token list, token list starting with
+% a normal token, with a brace group, or with a space token.
+% If the token list starts with a normal token, remove it
+% and check for emptyness. Otherwise, compare with a single
+% space, only case where we have a single token.
+% \begin{macrocode}
+\prg_new_conditional:Npnn \tl_if_single_token:n #1 { p , T , F , TF }
+ {
+ \tl_if_head_is_N_type:nTF {#1}
+ { \__str_if_eq_x_return:nn { \exp_not:o { \use_none:n #1 } } { } }
+ { \__str_if_eq_x_return:nn { \exp_not:n {#1} } { ~ } }
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\tl_reverse_tokens:n}
+% \begin{macro}[EXP,aux]{\@@_reverse_group:nn}
+% The same as \cs{tl_reverse:n} but with recursion within brace groups.
+% \begin{macrocode}
+\cs_new:Npn \tl_reverse_tokens:n #1
+ {
+ \etex_unexpanded:D \exp_after:wN
+ {
+ \tex_romannumeral:D
+ \@@_act:NNNnn
+ \@@_reverse_normal:nN
+ \@@_reverse_group:nn
+ \@@_reverse_space:n
+ { }
+ {#1}
+ }
+ }
+\cs_new:Npn \@@_reverse_group:nn #1
+ {
+ \@@_act_group_recurse:Nnn
+ \@@_act_reverse_output:n
+ { \tl_reverse_tokens:n }
+ }
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}[EXP,aux]{\@@_act_group_recurse:Nnn}
+% In many applications of \cs{@@_act:NNNnn}, we need to recursively
+% apply some transformation within brace groups, then output. In this
+% code, |#1| is the output function, |#2| is the transformation,
+% which should expand in two steps, and |#3| is the group.
+% \begin{macrocode}
+\cs_new:Npn \@@_act_group_recurse:Nnn #1#2#3
+ {
+ \exp_args:Nf #1
+ { \exp_after:wN \exp_after:wN \exp_after:wN { #2 {#3} } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\tl_count_tokens:n}
+% \begin{macro}[EXP,aux]{\@@_act_count_normal:nN,
+% \@@_act_count_group:nn,\@@_act_count_space:n}
+% The token coung is computed through an \cs{int_eval:n} construction.
+% Each \texttt{1+} is output to the \emph{left}, into the integer
+% expression, and the sum is ended by the \cs{c_zero} inserted by
+% \cs{@@_act_end:wn}. Somewhat a hack.
+% \begin{macrocode}
+\cs_new:Npn \tl_count_tokens:n #1
+ {
+ \int_eval:n
+ {
+ \@@_act:NNNnn
+ \@@_act_count_normal:nN
+ \@@_act_count_group:nn
+ \@@_act_count_space:n
+ { }
+ {#1}
+ }
+ }
+\cs_new:Npn \@@_act_count_normal:nN #1 #2 { 1 + }
+\cs_new:Npn \@@_act_count_space:n #1 { 1 + }
+\cs_new:Npn \@@_act_count_group:nn #1 #2
+ { 2 + \tl_count_tokens:n {#2} + }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\c_@@_act_uppercase_tl, \c_@@_act_lowercase_tl}
+% These constants contain the correspondance between lowercase
+% and uppercase letters, in the form |aAbBcC...| and |AaBbCc...|
+% respectively.
+% \begin{macrocode}
+\tl_const:Nn \c_@@_act_uppercase_tl
+ {
+ aA bB cC dD eE fF gG hH iI jJ kK lL mM
+ nN oO pP qQ rR sS tT uU vV wW xX yY zZ
+ }
+\tl_const:Nn \c_@@_act_lowercase_tl
+ {
+ Aa Bb Cc Dd Ee Ff Gg Hh Ii Jj Kk Ll Mm
+ Nn Oo Pp Qq Rr Ss Tt Uu Vv Ww Xx Yy Zz
+ }
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}[EXP]{\tl_expandable_uppercase:n,\tl_expandable_lowercase:n}
+% \begin{macro}[EXP,aux]{\@@_act_case_normal:nN,
+% \@@_act_case_group:nn,\@@_act_case_space:n}
+% The only difference between uppercasing and lowercasing is
+% the table of correspondance that is used. As for other
+% token list actions, we feed \cs{@@_act:NNNnn} three
+% functions, and this time, we use the \meta{parameters}
+% argument to carry which case-changing we are applying.
+% A space is simply output. A normal token is compared
+% to each letter in the alphabet using \cs{str_if_eq:nn}
+% tests, and converted if necessary to upper/lowercase,
+% before being output. For a group, we must perform the
+% conversion within the group (the \cs{exp_after:wN} trigger
+% \tn{romannumeral}, which expands fully to give the
+% converted group), then output.
+% \begin{macrocode}
+\cs_new:Npn \tl_expandable_uppercase:n #1
+ {
+ \etex_unexpanded:D \exp_after:wN
+ {
+ \tex_romannumeral:D
+ \@@_act_case_aux:nn { \c_@@_act_uppercase_tl } {#1}
+ }
+ }
+\cs_new:Npn \tl_expandable_lowercase:n #1
+ {
+ \etex_unexpanded:D \exp_after:wN
+ {
+ \tex_romannumeral:D
+ \@@_act_case_aux:nn { \c_@@_act_lowercase_tl } {#1}
+ }
+ }
+\cs_new:Npn \@@_act_case_aux:nn
+ {
+ \@@_act:NNNnn
+ \@@_act_case_normal:nN
+ \@@_act_case_group:nn
+ \@@_act_case_space:n
+ }
+\cs_new:Npn \@@_act_case_space:n #1 { \@@_act_output:n {~} }
+\cs_new:Npn \@@_act_case_normal:nN #1 #2
+ {
+ \exp_args:Nf \@@_act_output:n
+ {
+ \exp_args:NNo \str_case:nnn #2 {#1}
+ { \exp_stop_f: #2 }
+ }
+ }
+\cs_new:Npn \@@_act_case_group:nn #1 #2
+ {
+ \exp_after:wN \@@_act_output:n \exp_after:wN
+ { \exp_after:wN { \tex_romannumeral:D \@@_act_case_aux:nn {#1} {#2} } }
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\tl_item:nn, \tl_item:Nn, \tl_item:cn}
+% \begin{macro}[aux]{\@@_item:nn}
+% The idea here is to find the offset of the item from the left, then use
+% a loop to grab the correct item. If the resulting offset is too large,
+% then \cs{quark_if_recursion_tail_stop:n} terminates the loop, and returns
+% nothing at all.
+% \begin{macrocode}
+\cs_new:Npn \tl_item:nn #1#2
+ {
+ \exp_args:Nf \@@_item:nn
+ {
+ \int_eval:n
+ {
+ \int_compare:nNnT {#2} < \c_zero
+ { \tl_count:n {#1} + \c_one + }
+ #2
+ }
+ }
+ #1
+ \q_recursion_tail
+ \__prg_break_point:
+ }
+\cs_new:Npn \@@_item:nn #1#2
+ {
+ \__quark_if_recursion_tail_break:nN {#2} \__prg_break:
+ \int_compare:nNnTF {#1} = \c_one
+ { \__prg_break:n { \exp_not:n {#2} } }
+ { \exp_args:Nf \@@_item:nn { \int_eval:n { #1 - 1 } } }
+ }
+\cs_new_nopar:Npn \tl_item:Nn { \exp_args:No \tl_item:nn }
+\cs_generate_variant:Nn \tl_item:Nn { c }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Additions to \pkg{l3tokens}}
+%
+% \begin{macrocode}
+%<@@=char>
+% \end{macrocode}
+%
+% \begin{macro}{\char_set_active:Npn,\char_set_active:Npx}
+% \begin{macro}{\char_gset_active:Npn,\char_gset_active:Npx}
+% \begin{macro}{\char_set_active_eq:NN,\char_gset_active_eq:NN}
+% \begin{macrocode}
+\group_begin:
+ \char_set_catcode_active:N \^^@
+ \cs_set:Npn \char_tmp:NN #1#2
+ {
+ \cs_new:Npn #1 ##1
+ {
+ \char_set_catcode_active:n { `##1 }
+ \group_begin:
+ \char_set_lccode:nn { `\^^@ } { `##1 }
+ \tl_to_lowercase:n { \group_end: #2 ^^@ }
+ }
+ }
+ \char_tmp:NN \char_set_active:Npn \cs_set:Npn
+ \char_tmp:NN \char_set_active:Npx \cs_set:Npx
+ \char_tmp:NN \char_gset_active:Npn \cs_gset:Npn
+ \char_tmp:NN \char_gset_active:Npx \cs_gset:Npx
+ \char_tmp:NN \char_set_active_eq:NN \cs_set_eq:NN
+ \char_tmp:NN \char_gset_active_eq:NN \cs_gset_eq:NN
+\group_end:
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%<@@=peek>
+% \end{macrocode}
+%
+% \begin{macro}[TF]{\peek_N_type:}
+% \begin{macro}{\@@_execute_branches_N_type:}
+% The next token is normal if it is neither a begin-group token,
+% nor an end-group token, nor a charcode-$32$ space token.
+% Note that implicit begin-group tokens, end-group tokens, and
+% spaces are also recognized as non-\texttt{N}-type.
+% Here, there is no \meta{search token}, so we feed a dummy
+% \cs{scan_stop:} to the \cs{peek_token_generic::NN} functions.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \@@_execute_branches_N_type:
+ {
+ \bool_if:nTF
+ {
+ \token_if_eq_catcode_p:NN \l_peek_token \c_group_begin_token ||
+ \token_if_eq_catcode_p:NN \l_peek_token \c_group_end_token ||
+ \token_if_eq_meaning_p:NN \l_peek_token \c_space_token
+ }
+ { \@@_false:w }
+ { \@@_true:w }
+ }
+\cs_new_protected_nopar:Npn \peek_N_type:TF
+ { \@@_token_generic:NNTF \@@_execute_branches_N_type: \scan_stop: }
+\cs_new_protected_nopar:Npn \peek_N_type:T
+ { \@@_token_generic:NNT \@@_execute_branches_N_type: \scan_stop: }
+\cs_new_protected_nopar:Npn \peek_N_type:F
+ { \@@_token_generic:NNF \@@_execute_branches_N_type: \scan_stop: }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macrocode}
+%</initex|package>
+% \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex