diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx | 2828 |
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 |