% \iffalse meta-comment % %% File: l3candidates.dtx Copyright(C) 2012-2015 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> \documentclass[full]{l3doc} % %<*driver|package> \GetIdInfo$Id: l3candidates.dtx 5544 2015-03-01 18:30:00Z joseph $ {L3 Experimental additions to l3kernel} % %<*driver> \begin{document} \DocInput{\jobname.dtx} \end{document} % % \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} % % \section{Important notice} % % This module provides a space in which functions can be added to % \pkg{l3kernel} (\pkg{expl3}) while still being experimental. % \begin{quote} % \bfseries % As such, the % functions here may not remain in their current form, or indeed at all, % in \pkg{l3kernel} in the future. % \end{quote} % 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. % % \medskip % % Thus, if you intend to use any of these functions from the candidate module in a public package % offered to others for productive use (e.g., being placed on CTAN) please consider the following points carefully: % \begin{itemize} % \item Be prepared that your public packages might require updating when such functions % are being finalized. % \item Consider informing us that you use a particular function in your public package, e.g., by % discussing this on the \texttt{LaTeX-L} % mailing list. This way it becomes easier to coordinate any updates necessary without a issues % for the users of your package. % \item Discussing and understanding use cases for a particular addition or concept also helps to % ensure that we provide the right interfaces in the final version so please give us feedback % if you consider a certain candidate function useful (or not). % \end{itemize} % We only add functions in this space if we consider them being serious candidates for a final inclusion % into the kernel. However, real use sometimes leads to better ideas, so functions from this module are % \textbf{not necessarily stable} and we may have to adjust them! % % \section{Additions to \pkg{l3basics}} % % \begin{function}[added = 2014-08-22]{\cs_log:N, \cs_log:c} % \begin{syntax} % \cs{cs_log:N} \meta{control sequence} % \end{syntax} % Writes the definition of the \meta{control sequence} in the log % file. See also \cs{cs_show:N} which displays the result in the % terminal. % \end{function} % % \begin{function}{\__kernel_register_log:N, \__kernel_register_log:c} % \begin{syntax} % \cs{__kernel_register_log:N} \meta{register} % \end{syntax} % Used to write the contents of a \TeX{} register to the log file in a % form similar to \cs{__kernel_register_show:N}. % \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. % Thus negative $y$-sizes will result in a box a depth dependent on the % height of the original box a height dependent on the depth. % 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. % Thus negative $y$-sizes will result in a box with depth dependent on the % height of the original box and height dependent on the depth of the original. % The resizing applies within the current \TeX{} group level. % \end{function} % % \begin{function} % {\box_resize_to_ht:Nn, \box_resize_to_ht:cn} % \begin{syntax} % \cs{box_resize_to_ht: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 height only, not including 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. % Thus negative $y$-sizes will result in a box with depth dependent on the % height of the original box and height dependent on the depth of the original. % 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. % Thus negative $y$-sizes will result in a box a depth dependent on the % height of the original box a height dependent on the depth. % The resizing applies within the current \TeX{} group level. % \end{function} % % \begin{function}[added = 2014-07-03] % {\box_resize_to_wd_and_ht:Nnn, \box_resize_to_wd_and_ht:cnn} % \begin{syntax} % \cs{box_resize_to_wd_and_ht:Nnn} \meta{box} \Arg{x-size} \Arg{y-size} % \end{syntax} % Resize the \meta{box} to a \emph{height} of % \meta{x-size} horizontally and \meta{y-size} % vertically (both of the sizes are dimension expressions). % The \meta{y-size} is the \emph{height} of the box, ignoring any depth. % 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. % \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. % Thus negative $y$-scales will result in a box a depth dependent on the % height of the original box a height dependent on the depth. % The resizing 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 placed 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 trim % operation is applied. The adjustment applies within the current \TeX{} % group level. The behavior of the operation where the trims requested is % greater than the size of the box is undefined. % \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}[added = 2014-08-22]{\clist_log:N, \clist_log:c} % \begin{syntax} % \cs{clist_log:N} \meta{comma list} % \end{syntax} % Writes the entries in the \meta{comma list} in the log file. See % also \cs{clist_show:N} which displays the result in the terminal. % \end{function} % % \begin{function}[added = 2014-08-22]{\clist_log:n} % \begin{syntax} % \cs{clist_log:n} \Arg{tokens} % \end{syntax} % Writes the entries in the comma list in the log file. See also % \cs{clist_show:n} which displays the result in the terminal. % \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. % \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} % % \begin{function}[added = 2014-08-22] % {\coffin_log_structure:N, \coffin_log_structure:c} % \begin{syntax} % \cs{coffin_log_structure:N} \meta{coffin} % \end{syntax} % This function writes the structural information about the % \meta{coffin} in the log file. The width, height and depth of the % typeset material are given, along with the location of all of the % poles of the coffin. See also \cs{coffin_show_structure:N} which % displays the result in the terminal. % \end{function} % % \section{Additions to \pkg{l3file}} % % \begin{function}[TF, added = 2014-07-02]{\file_if_exist_input:n} % \begin{syntax} % \cs{file_if_exist_input:n} \Arg{file name} % \cs{file_if_exist_input:nTF} \Arg{file name} \Arg{true code} \Arg{false code} % \end{syntax} % Searches for \meta{file name} using the current \TeX{} search % path and the additional paths controlled by % \cs{file_path_include:n}). If found, inserts the \meta{true code} then % reads in the file as additional \LaTeX{} source as described for % \cs{file_input:n}. Note that \cs{file_if_exist_input:n} does not raise % an error if the file is not found, in contrast to \cs{file_input:n}. % \end{function} % % \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|. % Note that \TeX{} removes trailing space and tab characters % (character codes 32 and 9) from every line upon input. \TeX{} also % ignores any trailing new-line marker from the file it reads. % \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{stream}. 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|. % Note that \TeX{} removes trailing space and tab characters % (character codes 32 and 9) from every line upon input. \TeX{} also % ignores any trailing new-line marker from the file it reads. % \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 { } } % { % % 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} % % \begin{function}[added = 2014-08-22] % {\ior_log_streams:, \iow_log_streams:} % \begin{syntax} % \cs{ior_log_streams:} % \cs{iow_log_streams:} % \end{syntax} % Writes in the log file a list of the file names associated with each % open stream: intended for tracking down problems. % \end{function} % % \section{Additions to \pkg{l3fp}} % % \begin{function}[added = 2014-08-22]{\fp_log:N, \fp_log:c, \fp_log:n} % \begin{syntax} % \cs{fp_log:N} \meta{fp~var} % \cs{fp_log:n} \Arg{floating point expression} % \end{syntax} % Evaluates the \meta{floating point expression} and writes the % result in the log file. % \end{function} % % \section{Additions to \pkg{l3int}} % % \begin{function}[added = 2014-08-22]{\int_log:N, \int_log:c} % \begin{syntax} % \cs{int_log:N} \meta{integer} % \end{syntax} % Writes the value of the \meta{integer} in the log file. % \end{function} % % \begin{function}[added = 2014-08-22]{\int_log:n} % \begin{syntax} % \cs{int_log:n} \Arg{integer expression} % \end{syntax} % Writes the result of evaluating the \meta{integer expression} % in the log file. % \end{function} % % \section{Additions to \pkg{l3keys}} % % \begin{function}[added = 2014-08-22]{\keys_log:nn} % \begin{syntax} % \cs{keys_log:nn} \Arg{module} \Arg{key} % \end{syntax} % Writes in the log file the function which is used to actually % implement a \meta{key} for a \meta{module}. % \end{function} % % \section{Additions to \pkg{l3msg}} % % \begin{function}[added = 2014-08-22]{\__msg_log:nnn} % \begin{syntax} % \cs{__msg_log:nnn} \Arg{module} \Arg{message} \Arg{arg one} % \end{syntax} % Writes the \meta{message} from \meta{module} in the log file without % formatting. Used in messages which print complex variable contents % completely. % \end{function} % % \begin{function}[added = 2014-08-22]{\__msg_log_variable:Nnn} % \begin{syntax} % \cs{__msg_log_variable:Nnn} \meta{variable} \Arg{type} \Arg{formatted content} % \end{syntax} % Writes the \meta{formatted content} of the \meta{variable} of % \meta{type} in the log file. The \meta{formatted content} will be % processed as the first argument in a call to \cs{iow_wrap:nnnN}, % hence |\\|, \verb*|\ | and other formatting sequences can be used. % Once expanded and processed, the \meta{formatted content} must % either be empty or contain |>|; everything until the first |>| will % be removed. % \end{function} % % \begin{function}[added = 2014-08-22]{\__msg_log_wrap:n} % \begin{syntax} % \cs{__msg_log_wrap:n} \Arg{formatted text} % \end{syntax} % Writes the \meta{formatted text} in the log file. After expansion, % unless it is empty, the \meta{formatted text} must contain |>|, % and the part of \meta{formatted text} before the first |>| is % removed. Failure to do so causes low-level \TeX{} errors. % \end{function} % % \begin{function}[added = 2014-08-22] % {\__msg_log_value:n, \__msg_log_value:x} % \begin{syntax} % \cs{__msg_log_value:n} \Arg{tokens} % \end{syntax} % Writes \verb*|> |\meta{tokens}|.| in the log file. % \end{function} % % \section{Additions to \pkg{l3prg}} % % \begin{function}[added = 2014-08-22]{\bool_log:N, \bool_log:c} % \begin{syntax} % \cs{bool_log:N} \meta{boolean} % \end{syntax} % Writes the logical truth of the \meta{boolean} in the log file. % \end{function} % % \begin{function}[added = 2014-08-22]{\bool_log:n} % \begin{syntax} % \cs{bool_log:n} \Arg{boolean expression} % \end{syntax} % Writes the logical truth of the \meta{boolean expression} in the log % file. % \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_item:Nn} is faster. % \end{function} % % \begin{function}[added = 2014-08-12]{\prop_log:N, \prop_log:c} % \begin{syntax} % \cs{prop_log:N} \meta{property list} % \end{syntax} % Writes the entries in the \meta{property list} in the log file. % \end{function} % % \section{Additions to \pkg{l3seq}} % % \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_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}[added = 2014-08-12]{\seq_log:N, \seq_log:c} % \begin{syntax} % \cs{seq_log:N} \meta{sequence} % \end{syntax} % Writes the entries in the \meta{sequence} in the log file. % \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} % % \begin{function}[added = 2014-08-22]{\dim_log:N, \dim_log:c} % \begin{syntax} % \cs{dim_log:N} \meta{dimension} % \end{syntax} % Writes the value of the \meta{dimension} in the log file. % \end{function} % % \begin{function}[added = 2014-08-22]{\dim_log:n} % \begin{syntax} % \cs{dim_log:n} \Arg{dimension expression} % \end{syntax} % Writes the result of evaluating the \meta{dimension expression} % in the log file. % \end{function} % % \begin{function}[added = 2014-08-22]{\skip_log:N, \skip_log:c} % \begin{syntax} % \cs{skip_log:N} \meta{skip} % \end{syntax} % Writes the value of the \meta{skip} in the log file. % \end{function} % % \begin{function}[added = 2014-08-22]{\skip_log:n} % \begin{syntax} % \cs{skip_log:n} \Arg{skip expression} % \end{syntax} % Writes the result of evaluating the \meta{skip expression} % in the log file. % \end{function} % % \begin{function}[added = 2014-08-22]{\muskip_log:N, \muskip_log:c} % \begin{syntax} % \cs{muskip_log:N} \meta{muskip} % \end{syntax} % Writes the value of the \meta{muskip} in the log file. % \end{function} % % \begin{function}[added = 2014-08-22]{\muskip_log:n} % \begin{syntax} % \cs{muskip_log:n} \Arg{muskip expression} % \end{syntax} % Writes the result of evaluating the \meta{muskip expression} % in the log file. % \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, added = 2014-06-30, updated = 2015-02-18]^^A % { % \tl_lower_case:n, \tl_upper_case:n, \tl_mixed_case:n, % \tl_lower_case:nn, \tl_upper_case:nn, \tl_mixed_case:nn % } % \begin{syntax} % \cs{tl_upper_case:n} \Arg{tokens} % \cs{tl_upper_case:nn} \Arg{language} \Arg{tokens} % \end{syntax} % These functions are intended to be applied to input which may be % regarded broadly as \enquote{text}. They traverse the \meta{tokens} and % change the case of characters as discussed below. The character code of % the characters replaced may be arbitrary: the replacement characters will % have standard document-level category codes ($11$ for letters, $12$ for % letter-like characters which can also be case-changed). Begin-group and % end-group characters in the \meta{tokens} are normalized and become |{| % and |}|, respectively. % % Importantly, notice that these functions are intended for working with % user text for typesetting. For case changing programmatic data see the % \pkg{l3str} module and discussion there of \cs{str_lower_case:n}, % \cs{str_upper_case:n} and \cs{str_fold_case:n}. % \end{function} % % The functions perform expansion on the input in most cases. In particular, % input in the form of token lists or expandable functions will be expanded % \emph{unless} it falls within one of the special handling classes described % below. This expansion approach means that in general the result of case % changing will match the \enquote{natural} outcome expected from a % \enquote{functional} approach to case modification. For example % \begin{verbatim} % \tl_set:Nn \l_tmpa_tl { hello } % \tl_upper_case:n { \l_tmpa_tl \c_space_tl world } % \end{verbatim} % will produce % \begin{verbatim} % HELLO WORLD % \end{verbatim} % The expansion approach taken means that in package mode any \LaTeXe{} % \enquote{robust} commands which may appear in the input should be converted % to engine-protected versions using for example the \tn{robustify} command % from the \pkg{etoolbox} package. % % \begin{variable}{\l_tl_case_change_math_tl} % Case changing will not take place within math mode material so for example % \begin{verbatim} % \tl_upper_case:n { Some~text~$y = mx + c$~with~{Braces} } % \end{verbatim} % will become % \begin{verbatim} % SOME TEXT $y = mx + c$ WITH {BRACES} % \end{verbatim} % Material inside math mode is left entirely unchanged: in particular, no % expansion is undertaken. % % Detection of math mode is controlled by the list of tokens in % \cs{l_tl_case_change_math_tl}, which should be in open--close pairs. In % package mode the standard settings is % \begin{verbatim} % $ $ \( \) % \end{verbatim} % \end{variable} % % \begin{variable}{\l_tl_case_change_exclude_tl} % Case changing can be prevented by using any command on the list % \cs{l_tl_case_change_exclude_tl}. Each entry should be a function % to be followed by one argument: the latter will be preserved as-is % with no expansion. Thus for example following % \begin{verbatim} % \tl_put_right:Nn \l_tl_case_change_exclude_tl { \NoChangeCase } % \end{verbatim} % the input % \begin{verbatim} % \tl_upper_case:n % { Some~text~$y = mx + c$~with~\NoChangeCase {Protection} } % \end{verbatim} % will result in % \begin{verbatim} % SOME TEXT $y = mx + c$ WITH \NoChangeCase {Protection} % \end{verbatim} % Notice that the case changing mapping preserves the inclusion of % the escape functions: it is left to other code to provide suitable % definitions (typically equivalent to \cs{use:n}). In particular, the % result of case changing is returned protected by \cs{exp_not:n}. % % When used with \LaTeXe{} the commands |\cite|, |\ensuremath|, |\label| % and |\ref| are automatically included in the list for exclusion from % case changing. % \end{variable} % % In package mode, the case change system will also % convert text stored using the \LaTeXe{} \enquote{LICR} approach. This % will upper/lower case tokens as implemented for the font encodings % \texttt{T1}, \texttt{T2}, \texttt{T5} and \texttt{LGR} (see the behaviour of % the \LaTeXe{} command \tn{MakeUppercase}). Note that these commands will % automatically be protected from expansion. % % \enquote{Mixed} case conversion may be regarded informally as converting the % first character of the \meta{tokens} to upper case and the rest to lower % case. However, the process is more complex than this as there are some % situations where a single lower case character maps to a special form, for % example \texttt{ij} in Dutch which becomes \texttt{IJ}. As such, % \cs{tl_mixed_case:n(n)} implement a more sophisticated mapping which accounts % for this and for modifying accents on the first letter. Spaces at the start % of the \meta{tokens} are ignored when finding the first \enquote{letter} for % conversion. % \begin{verbatim} % \tl_mixed_case:n { hello~WORLD } % => "Hello world" % \tl_mixed_case:n { ~hello~WORLD } % => " Hello world" % \tl_mixed_case:n { {hello}~WORLD } % => "{Hello} world" % \end{verbatim} % When finding the first \enquote{letter} for this process, any content in % math mode or covered by \cs{l_tl_case_change_exclude_tl} is ignored. % % (Note that the Unicode Consortium describe this as \enquote{title case}, but % that in English title case applies on a word-by-word basis. The % \enquote{mixed} case implemented here is a lower level concept needed for % both \enquote{title} and \enquote{sentence} casing of text.) % % \begin{variable}{\l_tl_mixed_case_ignore_tl} % The list of characters to ignore when searching for the first % \enquote{letter} in mixed-casing is determined by % \cs{l_tl_mixed_change_ignore_tl}. This has the standard setting % \begin{verbatim} % ( [ { ` - % \end{verbatim} % where comparisons are made on a character basis. % \end{variable} % % As is generally true for \pkg{expl3}, these functions are designed to % work with Unicode input only. As such, when used with \pdfTeX{} % \emph{only} the characters \texttt{a}--\texttt{zA}--\texttt{Z} are % modified. When used with \XeTeX{} or \LuaTeX{} a full range of Unicode % transformations are enabled. Specifically, the standard mappings here % follow those defined by the \href{http://www.unicode.org}^^A % {Unicode Consortium} in \texttt{UnicodeData.txt} and % \texttt{SpecialCasing.txt}. Note that in some cases, \pdfTeX{} can % interpret the input to a case change but not generate the correct output % (for example in the mapping i to I-dot in Turkish): in these cases the % input is left unchanged. % % Context-sensitive mappings are enabled: language-dependent cases are % discussed below. All context detection works before any expansion of % following tokens and thus any control sequences are taken as the end of % the current \enquote{word}/character context. % % \begin{variable}{\l_tl_change_case_after_final_sigma_tl} % The \enquote{final sigma} rule for Greek letters is enabled and active for % all inputs. It is implemented here in a modified form which takes account % of the requirements of the likely real use cases, performance and % expandability. A capital sigma will map to a final-sigma if it is followed % by a space, the end of the input or one of the characters listed in % \cs{l_tl_change_case_after_final_sigma_tl}, which has standard setting % \begin{verbatim} % ) ] } . : ; , ! ? ' " % \end{verbatim} % with comparisons are made on a character basis. % \end{variable} % % Language-sensitive conversions are enabled using the \meta{language} % argument, and follow Unicode Consortium guidelines. Currently, the % languages recognised for special handling are as follows. % \begin{itemize} % \item Azeri and Turkish (\texttt{az} and \texttt{tr}). % The case pairs I/i-dotless and I-dot/i are activated for these % languages. The combining dot mark is removed when lower % casing I-dot and introduced when upper casing i-dotless. % \item Lithuanian (\texttt{lt}). % The lower case letters i and j should retain a dot above when the % accents grave, acute or tilde are present. This is implemented for % lower casing of the relevant upper case letters both when input as % single Unicode codepoints and when using combining accents. The % combining dot is removed when upper casing in these cases. Note that % \emph{only} the accents used in Lithuanian are covered: the behaviour % of other accents are not modified. % \item Dutch (\texttt{nl}). % Capitalisation of \texttt{ij} at the beginning of mixed cased % input produces \texttt{IJ} rather than \texttt{Ij}. The output % retains two separate letters, thus this transformation \emph{is} % available using \pdfTeX{}. % \end{itemize} % % Creating additional context-sensitive mappings requires knowledge % of the underlying mapping implementation used here. The team are happy % to add these to the kernel where they are well-documented % (\emph{e.g.}~in Unicode Consortium or relevant government publications). % % \begin{function}[added = 2014-06-25]^^A % { % \tl_set_from_file:Nnn, \tl_set_from_file:cnn, % \tl_gset_from_file:Nnn, \tl_gset_from_file:cnn % } % \begin{syntax} % \cs{tl_set_from_file:Nnn} \meta{tl} \Arg{setup} \Arg{filename} % \end{syntax} % Defines \meta{tl} to the contents of \meta{filename}. % Category codes may need to be set appropriately via the \meta{setup} % argument. % \end{function} % % \begin{function}[added = 2014-06-25]^^A % { % \tl_set_from_file_x:Nnn, \tl_set_from_file_x:cnn, % \tl_gset_from_file_x:Nnn, \tl_gset_from_file_x:cnn % } % \begin{syntax} % \cs{tl_set_from_file_x:Nnn} \meta{tl} \Arg{setup} \Arg{filename} % \end{syntax} % Defines \meta{tl} to the contents of \meta{filename}, expanding % the contents of the file as it is read. Category codes and other % definitions may need to be set appropriately via the \meta{setup} % argument. % \end{function} % % \begin{function}[added = 2014-08-22]{\tl_log:N, \tl_log:c} % \begin{syntax} % \cs{tl_log:N} \meta{tl~var} % \end{syntax} % Writes the content of the \meta{tl~var} in the log file. See also % \cs{tl_show:N} which displays the result in the terminal. % \end{function} % % \begin{function}[added = 2014-08-22]{\tl_log:n} % \begin{syntax} % \cs{tl_log:n} \meta{token list} % \end{syntax} % Writes the \meta{token list} in the log file. See also % \cs{tl_show:n} which displays the result in the terminal. % \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, updated = 2012-12-20]{\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$), or an outer token (never used in \LaTeX3) % 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} % % \subsection{Additions to \pkg{l3basics}} % % \begin{macrocode} %<@@=cs> % \end{macrocode} % % \begin{macro}{\cs_log:N, \cs_log:c} % The same as \cs{cs_show:N} and \cs{cs_show:c}, but using % \cs{__msg_log_wrap:n} instead of \cs{__msg_show_variable:n}. % \begin{macrocode} \group_begin: \tex_lccode:D `? = `: \scan_stop: \tex_catcode:D `? = 12 \scan_stop: \tex_lowercase:D { \group_end: \cs_new_protected:Npn \cs_log:N #1 { \__msg_log_wrap:n { > ~ \token_to_str:N #1 = \exp_after:wN \@@_show:www \cs_meaning:N #1 \use_none:nn ? \prg_do_nothing: } } } \cs_new_protected_nopar:Npn \cs_log:c { \group_begin: \exp_args:NNc \group_end: \cs_log:N } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\__kernel_register_log:N, \__kernel_register_log:c} % Check that the variable exists, then write its name and value to the % log file. % \begin{macrocode} \cs_new_protected:Npn \__kernel_register_log:N #1 { \cs_if_exist:NTF #1 { \__msg_log_value:x { \token_to_str:N #1 = \tex_the:D #1 } } { \__msg_kernel_error:nnx { kernel } { variable-not-defined } { \token_to_str:N #1 } } } \cs_generate_variant:Nn \__kernel_register_log:N { c } % \end{macrocode} % \end{macro} % % \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 { sind ( \l_@@_angle_fp ) } \fp_set:Nn \l_@@_cos_fp { cosd ( \l_@@_angle_fp ) } \@@_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:l3candidates: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:l3candidates: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_set_corners:N} % \begin{macro}[aux]{\@@_resize:N} % \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: \@@_resize_set_corners:N #1 % \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 rest of the work. % \begin{macrocode} \@@_resize:N #1 \group_end: } } \cs_generate_variant:Nn \box_resize:Nnn { c } \cs_new_protected:Npn \@@_resize_set_corners: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} % 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. 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:N #1 { \@@_resize:NNN \l_@@_right_new_dim \l_@@_scale_x_fp \l_@@_right_dim \@@_resize:NNN \l_@@_bottom_new_dim \l_@@_scale_y_fp \l_@@_bottom_dim \@@_resize:NNN \l_@@_top_new_dim \l_@@_scale_y_fp \l_@@_top_dim \@@_resize_common:N #1 } \cs_new_protected:Npn \@@_resize:NNN #1#2#3 { \dim_set:Nn #1 { \fp_to_dim:n { \fp_abs:n { #2 } * \dim_to_fp:n { #3 } } } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\box_resize_to_ht:Nn, \box_resize_to_ht:cn} % \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} % \begin{macro}{\box_resize_to_wd_and_ht:Nnn, \box_resize_to_wd_and_ht:cnn} % 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:Nn #1#2 { \hbox_set:Nn #1 { \group_begin: \@@_resize_set_corners:N #1 \fp_set:Nn \l_@@_scale_y_fp { \dim_to_fp:n {#2} / \dim_to_fp:n { \l_@@_top_dim } } \fp_set_eq:NN \l_@@_scale_x_fp \l_@@_scale_y_fp \@@_resize:N #1 \group_end: } } \cs_generate_variant:Nn \box_resize_to_ht:Nn { c } \cs_new_protected:Npn \box_resize_to_ht_plus_dp:Nn #1#2 { \hbox_set:Nn #1 { \group_begin: \@@_resize_set_corners:N #1 \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:N #1 \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: \@@_resize_set_corners:N #1 \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:N #1 \group_end: } } \cs_generate_variant:Nn \box_resize_to_wd:Nn { c } \cs_new_protected:Npn \box_resize_to_wd_and_ht:Nnn #1#2#3 { \hbox_set:Nn #1 { \group_begin: \@@_resize_set_corners:N #1 \fp_set:Nn \l_@@_scale_x_fp { \dim_to_fp:n {#2} / \dim_to_fp:n { \l_@@_right_dim } } \fp_set:Nn \l_@@_scale_y_fp { \dim_to_fp:n {#3} / \dim_to_fp:n { \l_@@_top_dim } } \@@_resize:N #1 \group_end: } } \cs_generate_variant:Nn \box_resize_to_wd_and_ht:Nnn { c } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\box_scale:Nnn, \box_scale:cnn} % 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 too many \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 \dim_set:Nn \l_@@_top_new_dim { \fp_abs:n { \l_@@_scale_y_fp } \l_@@_top_dim } \dim_set:Nn \l_@@_bottom_new_dim { \fp_abs:n { \l_@@_scale_y_fp } \l_@@_bottom_dim } \dim_set:Nn \l_@@_right_new_dim { \fp_abs:n { \l_@@_scale_x_fp } \l_@@_right_dim } \@@_resize_common:N #1 \group_end: } } \cs_generate_variant:Nn \box_scale:Nnn { c } % \end{macrocode} % \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} \fp_compare:nNnTF \l_@@_scale_y_fp > \c_zero_fp { \box_set_ht:Nn \l_@@_internal_box { \l_@@_top_new_dim } \box_set_dp:Nn \l_@@_internal_box { -\l_@@_bottom_new_dim } } { \box_set_dp:Nn \l_@@_internal_box { \l_@@_top_new_dim } \box_set_ht: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 } \hbox:n { \tex_kern:D \c_zero_dim \box_use:N \l_@@_internal_box \tex_hss:D } } } % \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: kern the % appropriate parts off each side. % \begin{macrocode} \cs_new_protected:Npn \box_trim:Nnnnn #1#2#3#4#5 { \hbox_set:Nn \l_@@_internal_box { \tex_kern:D -\__dim_eval:w #2 \__dim_eval_end: \box_use:N #1 \tex_kern:D -\__dim_eval:w #4 \__dim_eval_end: } % \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. First, the bottom % edge. If there is enough depth, simply set the depth, or if not move % down so the result is zero depth. \cs{box_move_down:nn} is used in both % cases so the resulting box always contains a \tn{lower} primitive. % The internal box is used here as it allows safe use of \cs{box_set_dp:Nn}. % \begin{macrocode} \dim_compare:nNnTF { \box_dp:N #1 } > {#3} { \hbox_set:Nn \l_@@_internal_box { \box_move_down:nn \c_zero_dim { \box_use:N \l_@@_internal_box } } \box_set_dp:Nn \l_@@_internal_box { \box_dp:N #1 - (#3) } } { \hbox_set:Nn \l_@@_internal_box { \box_move_down:nn { #3 - \box_dp:N #1 } { \box_use:N \l_@@_internal_box } } \box_set_dp:Nn \l_@@_internal_box \c_zero_dim } % \end{macrocode} % Same thing, this time from the top of the box. % \begin{macrocode} \dim_compare:nNnTF { \box_ht:N \l_@@_internal_box } > {#5} { \hbox_set:Nn \l_@@_internal_box { \box_move_up:nn \c_zero_dim { \box_use:N \l_@@_internal_box } } \box_set_ht:Nn \l_@@_internal_box { \box_ht:N \l_@@_internal_box - (#5) } } { \hbox_set:Nn \l_@@_internal_box { \box_move_up:nn { #5 - \box_ht:N \l_@@_internal_box } { \box_use:N \l_@@_internal_box } } \box_set_ht:Nn \l_@@_internal_box \c_zero_dim } \box_set_eq:NN #1 \l_@@_internal_box } \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 the trim operation, but with absolute % dimensions. As a result, there are some things to watch out for in the % vertical direction. % \begin{macrocode} \cs_new_protected:Npn \box_viewport:Nnnnn #1#2#3#4#5 { \hbox_set:Nn \l_@@_internal_box { \tex_kern:D -\__dim_eval:w #2 \__dim_eval_end: \box_use:N #1 \tex_kern:D \__dim_eval:w #4 - \box_wd:N #1 \__dim_eval_end: } \dim_compare:nNnTF {#3} < \c_zero_dim { \hbox_set:Nn \l_@@_internal_box { \box_move_down:nn \c_zero_dim { \box_use:N \l_@@_internal_box } } \box_set_dp:Nn \l_@@_internal_box { -\dim_eval:n {#3} } } { \hbox_set:Nn \l_@@_internal_box { \box_move_down:nn {#3} { \box_use:N \l_@@_internal_box } } \box_set_dp:Nn \l_@@_internal_box \c_zero_dim } \dim_compare:nNnTF {#5} > \c_zero_dim { \hbox_set:Nn \l_@@_internal_box { \box_move_up:nn \c_zero_dim { \box_use:N \l_@@_internal_box } } \box_set_ht:Nn \l_@@_internal_box { #5 \dim_compare:nNnT {#3} > \c_zero_dim { - (#3) } } } { \hbox_set:Nn \l_@@_internal_box { \box_move_up:nn { -\dim_eval:n {#5} } { \box_use:N \l_@@_internal_box } } \box_set_ht:Nn \l_@@_internal_box \c_zero_dim } \box_set_eq:NN #1 \l_@@_internal_box } \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_log:N, \clist_log:c, \clist_log:n} % Same as \cs{clist_show:N} but using \cs{__msg_log_variable:Nnn}. % \begin{macrocode} \cs_new_protected:Npn \clist_log:N #1 { \__msg_log_variable:Nnn #1 { clist } { \clist_map_function:NN #1 \__msg_show_item:n } } \cs_new_protected:Npn \clist_log:n #1 { \clist_set:Nn \l_@@_internal_clist {#1} \clist_log:N \l_@@_internal_clist } \cs_generate_variant:Nn \clist_log:N { c } % \end{macrocode} % \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 sine and cosine of the angle in degrees are % computed. 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 { sind ( #2 ) } \fp_set:Nn \l_@@_cos_fp { cosd ( #2 ) } % \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. The % internal box is used here to allow for the next step. % \begin{macrocode} \hbox_set:Nn \l_@@_internal_box { \tex_kern:D \__dim_eval:w \l_@@_bounding_shift_dim - \l_@@_left_corner_dim \__dim_eval_end: \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. As this operation requires setting box dimensions and these % transcend grouping, the safe way to do this is to use the internal box % and to reset the result into the target box. % \begin{macrocode} \box_set_ht:Nn \l_@@_internal_box { \l_@@_top_corner_dim - \l_@@_bottom_corner_dim } \box_set_dp:Nn \l_@@_internal_box { 0 pt } \box_set_wd:Nn \l_@@_internal_box { \l_@@_right_corner_dim - \l_@@_left_corner_dim } \hbox_set:Nn #1 { \box_use:N \l_@@_internal_box } % \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:Nn \l_@@_left_corner_dim { \dim_min:nn { \l_@@_left_corner_dim } {#1} } \dim_set:Nn \l_@@_right_corner_dim { \dim_max:nn { \l_@@_right_corner_dim } {#1} } \dim_set:Nn \l_@@_bottom_corner_dim { \dim_min:nn { \l_@@_bottom_corner_dim } {#2} } \dim_set:Nn \l_@@_top_corner_dim { \dim_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:Nn \l_@@_bounding_shift_dim { \dim_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 { \fp_set:Nn \l_@@_scale_x_fp { \dim_to_fp:n {#2} / \dim_to_fp:n { \coffin_wd:N #1 } } \fp_set:Nn \l_@@_scale_y_fp { \dim_to_fp:n {#3} / \dim_to_fp:n { \coffin_ht:N #1 + \coffin_dp:N #1 } } \box_resize:Nnn #1 {#2} {#3} \@@_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{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 { \fp_set:Nn \l_@@_scale_x_fp {#2} \fp_set:Nn \l_@@_scale_y_fp {#3} \box_scale:Nnn #1 { \l_@@_scale_x_fp } { \l_@@_scale_y_fp } \dim_set:Nn \l_@@_internal_dim { \coffin_ht:N #1 + \coffin_dp:N #1 } \dim_set:Nn \l_@@_scaled_total_height_dim { \fp_abs:n { \l_@@_scale_y_fp } \l_@@_internal_dim } \dim_set:Nn \l_@@_scaled_width_dim { -\fp_abs:n { \l_@@_scale_x_fp } \coffin_wd:N #1 } \@@_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{Coffin diagnostics} % % \begin{macro}{\coffin_log_structure:N, \coffin_log_structure:c} % Same as \cs{coffin_show_structure:N}, but using % \cs{__msg_log_variable:Nnn}. % \begin{macrocode} \cs_new_protected:Npn \coffin_log_structure:N #1 { \@@_if_exist:NT #1 { \__msg_log_variable:Nnn #1 { coffins } { \prop_map_function:cN { l_@@_poles_ \__int_value:w #1 _prop } \__msg_show_item_unbraced:nn } } } \cs_generate_variant:Nn \coffin_log_structure:N { c } % \end{macrocode} % \end{macro} % % \subsection{Additions to \pkg{l3file}} % % \begin{macrocode} %<@@=file> % \end{macrocode} % % \begin{macro}[TF]{\file_if_exist_input:n} % Input of a file with a test for existence cannot be done the usual % way as the tokens to insert are in an odd place. % \begin{macrocode} \cs_new_protected:Npn \file_if_exist_input:n #1 { \file_if_exist:nT {#1} { \@@_input:V \l_@@_internal_name_tl } } \cs_new_protected:Npn \file_if_exist_input:nT #1#2 { \file_if_exist:nT {#1} { #2 \@@_input:V \l_@@_internal_name_tl } } \cs_new_protected:Npn \file_if_exist_input:nF #1 { \file_if_exist:nTF {#1} { \@@_input:V \l_@@_internal_name_tl } } \cs_new_protected:Npn \file_if_exist_input:nTF #1#2 { \file_if_exist:nTF {#1} { #2 \@@_input:V \l_@@_internal_name_tl } } % \end{macrocode} % \end{macro} % % \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} % % \begin{macro}{\ior_log_streams:} % \begin{macro}[aux]{\@@_log_streams:Nn} % Same as \cs{ior_list_streams:}, but with \cs{__msg_log:nnn} and % \cs{__msg_log_wrap:n}. % \begin{macrocode} \cs_new_protected_nopar:Npn \ior_log_streams: { \@@_log_streams:Nn \g_@@_streams_prop { input } } \cs_new_protected:Npn \@@_log_streams:Nn #1#2 { \__msg_log:nnn { LaTeX / kernel } { \prop_if_empty:NTF #1 { show-no-stream } { show-open-streams } } {#2} \__msg_log_wrap:n { \prop_map_function:NN #1 \__msg_show_item_unbraced:nn } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macrocode} %<@@=iow> % \end{macrocode} % % \begin{macro}{\iow_log_streams:} % \begin{macro}[aux]{\@@_log_streams:Nn} % Same as \cs{iow_list_streams:}, but with \cs{__msg_log:nnn} and % \cs{__msg_log_wrap:n}. % \begin{macrocode} \cs_new_protected_nopar:Npn \iow_log_streams: { \@@_log_streams:Nn \g_@@_streams_prop { output } } \cs_new_protected:Npn \@@_log_streams:Nn #1#2 { \__msg_log:nnn { LaTeX / kernel } { \prop_if_empty:NTF #1 { show-no-stream } { show-open-streams } } {#2} \__msg_log_wrap:n { \prop_map_function:NN #1 \__msg_show_item_unbraced:nn } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsection{Additions to \pkg{l3fp}} % % \begin{macrocode} %<@@=fp> % \end{macrocode} % % \begin{macro}{\fp_log:N, \fp_log:c, \fp_log:n} % Same as \cs{fp_show:N} but using \cs{__msg_log_value:x} since we % know that the result is short. % \begin{macrocode} \cs_new_protected:Npn \fp_log:N #1 { \fp_if_exist:NTF #1 { \__msg_log_value:x { \token_to_str:N #1 = \fp_to_tl:N #1 } } { \__msg_kernel_error:nnx { kernel } { variable-not-defined } { \token_to_str:N #1 } } } \cs_new_protected:Npn \fp_log:n #1 { \__msg_log_value:x { \fp_to_tl:n {#1} } } \cs_generate_variant:Nn \fp_log:N { c } % \end{macrocode} % \end{macro} % % \subsection{Additions to \pkg{l3int}} % % \begin{macro}{\int_log:N, \int_log:c} % Simple copies. % \begin{macrocode} \cs_new_eq:NN \int_log:N \__kernel_register_log:N \cs_new_eq:NN \int_log:c \__kernel_register_log:c % \end{macrocode} % \end{macro} % % \begin{macro}{\int_log:n} % Use \cs{__msg_log_value:x}. % \begin{macrocode} \cs_new_protected:Npn \int_log:n #1 { \__msg_log_value:x { \int_eval:n {#1} } } % \end{macrocode} % \end{macro} % % \subsection{Additions to \pkg{l3keys}} % % \begin{macrocode} %<@@=keys> % \end{macrocode} % % \begin{macro}{\keys_log:nn} % See \cs{keys_show:nn} but using \cs{cs_log:c} instead of % \cs{show:c}. % \begin{macrocode} \cs_new_protected:Npn \keys_log:nn #1#2 { \cs_log:c { \c_@@_code_root_tl #1 / \tl_to_str:n {#2} } } % \end{macrocode} % \end{macro} % % \subsection{Additions to \pkg{l3msg}} % % \begin{macrocode} %<@@=msg> % \end{macrocode} % % \begin{macro}[int]{\@@_log:nnn} % Print the text of a message to the terminal without formatting: % short cuts around \cs{iow_wrap:nnnN}. % \begin{macrocode} \cs_new_protected:Npn \@@_log:nnn #1#2#3 { \iow_wrap:nnnN { \use:c { \c_@@_text_prefix_tl #1 / #2 } {#3} { } { } { } } { } { } \iow_log:n } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_log_variable:Nnn, \@@_log_wrap:n} % \begin{macro}[aux]{\@@_log:n} % This is a direct analogue of \cs{@@_show_variable:Nnn}, but writing % to the log file instead of the terminal. It is important to pass to % \cs{iow_wrap:nnnN} the whole text that will be written in the log, % including the trailing period, as it would otherwise not be % formatted correctly. Note that we detect the case where nothing % would be shown, and add |>~| at the start: this is safe unless lines % are less than $3$~characters long. % \begin{macrocode} \cs_new_protected:Npn \@@_log_variable:Nnn #1#2#3 { \cs_if_exist:NTF #1 { \@@_log:nnn { LaTeX / kernel } { show- #2 } {#1} \@@_log_wrap:n {#3} } { \@@_kernel_error:nnx { kernel } { variable-not-defined } { \token_to_str:N #1 } } } \cs_new_protected:Npn \@@_log_wrap:n #1 { \iow_wrap:nnnN { #1 . } { } { } \@@_log:n } \cs_new_protected:Npn \@@_log:n #1 { \iow_log:x { \tl_if_single:nT {#1} { > ~ } #1 } } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[int]{\@@_log_value:n, \@@_log_value:x} % Write the tokens |#1| to the log file without line wrapping but with % a formatting similar to what is done by the commands which show % material to the terminal. % \begin{macrocode} \cs_new_protected:Npn \@@_log_value:n #1 { \iow_log:n { >~ #1 . } } \cs_generate_variant:Nn \@@_log_value:n { x } % \end{macrocode} % \end{macro} % % \subsection{Additions to \pkg{l3prg}} % % \begin{macrocode} %<@@=bool> % \end{macrocode} % % \begin{macro}{\bool_log:N, \bool_log:c, \bool_log:n} % \begin{macro}[aux]{\@@_to_word:n} % Writes in the log file the truth value of the boolean, as % \texttt{true} or \texttt{false}. We use \cs{__msg_log_value:x}. % \begin{macrocode} \cs_new_protected:Npn \bool_log:N #1 { \bool_if_exist:NTF #1 { \__msg_log_value:x { \token_to_str:N #1 = \@@_to_word:n {#1} } } { \__msg_kernel_error:nnx { kernel } { variable-not-defined } { \token_to_str:N #1 } } } \cs_new_protected:Npn \bool_log:n #1 { \__msg_log_value:x { \@@_to_word:n {#1} } } \cs_new:Npn \@@_to_word:n #1 { \bool_if:nTF {#1} { true } { false } } \cs_generate_variant:Nn \bool_log:N { c } % \end{macrocode} % \end{macro} % \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:nwwn} % The mapping is very similar to \cs{prop_map_function:NN}. It 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 without interfering with \cs{prop_map_break:}. % Argument |#2| of \cs{@@_map_tokens:nwwn} is \cs{s_@@} the first % time, and is otherwise empty. % \begin{macrocode} \cs_new:Npn \prop_map_tokens:Nn #1#2 { \exp_last_unbraced:Nno \@@_map_tokens:nwwn {#2} #1 \@@_pair:wn \q_recursion_tail \s_@@ { } \__prg_break_point:Nn \prop_map_break: { } } \cs_new:Npn \@@_map_tokens:nwwn #1#2 \@@_pair:wn #3 \s_@@ #4 { \if_meaning:w \q_recursion_tail #3 \exp_after:wN \prop_map_break: \fi: \use:n {#1} {#3} {#4} \@@_map_tokens:nwwn {#1} } \cs_generate_variant:Nn \prop_map_tokens:Nn { c } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\prop_log:N, \prop_log:c} % Same as \cs{prop_show:N} but using \cs{__msg_log_variable:Nnn}. % \begin{macrocode} \cs_new_protected:Npn \prop_log:N #1 { \__msg_log_variable:Nnn #1 { prop } { \prop_map_function:NN #1 \__msg_show_item:nn } } \cs_generate_variant:Nn \prop_log:N { c } % \end{macrocode} % \end{macro} % % \subsection{Additions to \pkg{l3seq}} % % \begin{macrocode} %<@@=seq> % \end{macrocode} % % \begin{macro} % { % \seq_mapthread_function:NNN, \seq_mapthread_function:NcN, % \seq_mapthread_function:cNN, \seq_mapthread_function:ccN % } % \begin{macro}[aux] % { % \@@_mapthread_function:wNN, \@@_mapthread_function:wNw, % \@@_mapthread_function:Nnnwnn % } % The idea is to first expand both 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 tokens of |#2| and |#5|, % which for items in the sequences will both be \cs{s_@@} % \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:wNN #2 \q_stop #1 #3 } \cs_new:Npn \@@_mapthread_function:wNN \s_@@ #1 \q_stop #2#3 { \exp_after:wN \@@_mapthread_function:wNw #2 \q_stop #3 #1 { ? \__prg_break: } { } \__prg_break_point: } \cs_new:Npn \@@_mapthread_function:wNw \s_@@ #1 \q_stop #2 { \@@_mapthread_function:Nnnwnn #2 #1 { ? \__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} % % \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}{\seq_log:N, \seq_log:c} % Same as \cs{seq_show:N} but using \cs{__msg_show_variable:Nnn}. % \begin{macrocode} \cs_new_protected:Npn \seq_log:N #1 { \__msg_log_variable:Nnn #1 { seq } { \seq_map_function:NN #1 \__msg_show_item:n } } \cs_generate_variant:Nn \seq_log:N { c } % \end{macrocode} % \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} % % \begin{macro}{\dim_log:N, \dim_log:c, \dim_log:n} % Diagnostics. % \begin{macrocode} \cs_new_eq:NN \dim_log:N \__kernel_register_log:N \cs_new_eq:NN \dim_log:c \__kernel_register_log:c \cs_new_protected:Npn \dim_log:n #1 { \__msg_log_value:x { \dim_eval:n {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\skip_log:N, \skip_log:c, \skip_log:n} % Diagnostics. % \begin{macrocode} \cs_new_eq:NN \skip_log:N \__kernel_register_log:N \cs_new_eq:NN \skip_log:c \__kernel_register_log:c \cs_new_protected:Npn \skip_log:n #1 { \__msg_log_value:x { \skip_eval:n {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\muskip_log:N, \muskip_log:c, \muskip_log:n} % Diagnostics. % \begin{macrocode} \cs_new_eq:NN \muskip_log:N \__kernel_register_log:N \cs_new_eq:NN \muskip_log:c \__kernel_register_log:c \cs_new_protected:Npn \muskip_log:n #1 { \__msg_log_value:x { \muskip_eval:n {#1} } } % \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 % emptiness. For the next case, an empty token list is not a single % token. Finally, we have a non-empty token list starting with a % space or a brace group. Applying \texttt{f}-expansion yields an % empty result if and only if the token list is a single space. % \begin{macrocode} \prg_new_conditional:Npnn \tl_if_single_token:n #1 { p , T , F , TF } { \tl_if_head_is_N_type:nTF {#1} { \@@_if_empty_return:o { \use_none:n #1 } } { \tl_if_empty:nTF {#1} { \prg_return_false: } { \@@_if_empty_return:o { \tex_romannumeral:D -`0 #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 count 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 correspondence 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 correspondence 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:nnF #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}^^A % { % \tl_set_from_file:Nnn, \tl_set_from_file:cnn, % \tl_gset_from_file:Nnn, \tl_gset_from_file:cnn % } % \begin{macro}[aux]{\@@_set_from_file:NNnn} % \begin{macro}[aux]{\@@_from_file_do:w} % The approach here is similar to that for doing a rescan, and so the same % internals can be reused. Thus the plan is to insert a pair of tokens of % the same charcode but different catcodes after the file has been read. % This plus \cs{exp_not:N} allows the primitive to be used to carry out % a set operation. % \begin{macrocode} \cs_new_protected_nopar:Npn \tl_set_from_file:Nnn { \@@_set_from_file:NNnn \tl_set:Nn } \cs_new_protected_nopar:Npn \tl_gset_from_file:Nnn { \@@_set_from_file:NNnn \tl_gset:Nn } \cs_generate_variant:Nn \tl_set_from_file:Nnn { c } \cs_generate_variant:Nn \tl_gset_from_file:Nnn { c } \cs_new_protected:Npn \@@_set_from_file:NNnn #1#2#3#4 { \__file_if_exist:nT {#4} { \group_begin: \exp_args:No \etex_everyeof:D { \c_@@_rescan_marker_tl \exp_not:N } #3 \scan_stop: \exp_after:wN \@@_from_file_do:w \exp_after:wN \prg_do_nothing: \tex_input:D \l__file_internal_name_tl \scan_stop: \exp_args:NNNo \group_end: #1 #2 \l_@@_internal_a_tl } } \exp_args:Nno \use:nn { \cs_set_protected:Npn \@@_from_file_do:w #1 } { \c_@@_rescan_marker_tl } { \tl_set:No \l_@@_internal_a_tl {#1} } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}^^A % { % \tl_set_from_file_x:Nnn, \tl_set_from_file_x:cnn, % \tl_gset_from_file_x:Nnn, \tl_gset_from_file_x:cnn % } % \begin{macro}[aux]{\@@_set_from_file_x:NNnn} % When reading a file and allowing expansion of the content, the set up % only needs to prevent \TeX{} complaining about the end of the file. That % is done simply, with a group then used to trap the definition needed. % Once the business is done using some scratch space, the tokens can be % transferred to the real target. % \begin{macrocode} \cs_new_protected_nopar:Npn \tl_set_from_file_x:Nnn { \@@_set_from_file_x:NNnn \tl_set:Nn } \cs_new_protected_nopar:Npn \tl_gset_from_file_x:Nnn { \@@_set_from_file_x:NNnn \tl_gset:Nn } \cs_generate_variant:Nn \tl_set_from_file_x:Nnn { c } \cs_generate_variant:Nn \tl_gset_from_file_x:Nnn { c } \cs_new_protected:Npn \@@_set_from_file_x:NNnn #1#2#3#4 { \__file_if_exist:nT {#4} { \group_begin: \etex_everyeof:D { \exp_not:N } #3 \scan_stop: \tl_set:Nx \l_@@_internal_a_tl { \tex_input:D \l__file_internal_name_tl \c_space_token } \exp_args:NNNo \group_end: #1 #2 \l_@@_internal_a_tl } } % \end{macrocode} % \end{macro} % \end{macro} % % \subsubsection{Unicode case changing} % % The mechanisms needed for case changing are somewhat involved, particularly % to allow for all of the special cases. These functions also require the % appropriate data extracted from the Unicode documentation (either manually % or automatically), which is covered by \pkg{l3unicode-data}. % % \begin{macro}[EXP]{\tl_if_head_eq_catcode:oNTF} % Extra variants. % \begin{macrocode} \cs_generate_variant:Nn \tl_if_head_eq_catcode:nNTF { o } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP]{\tl_lower_case:n, \tl_upper_case:n, \tl_mixed_case:n} % \begin{macro}[EXP]{\tl_lower_case:nn, \tl_upper_case:nn, \tl_mixed_case:nn} % The user level functions here are all wrappers around the internal % functions for case changing. Note that \cs{tl_mixed_case:nn} could be % done without an internal, but this way the logic is slightly clearer as % everything essentially follows the same path. % \begin{macrocode} \cs_new_nopar:Npn \tl_lower_case:n { \@@_change_case:nnn { lower } { } } \cs_new_nopar:Npn \tl_upper_case:n { \@@_change_case:nnn { upper } { } } \cs_new_nopar:Npn \tl_mixed_case:n { \@@_mixed_case:nn { } } \cs_new_nopar:Npn \tl_lower_case:nn { \@@_change_case:nnn { lower } } \cs_new_nopar:Npn \tl_upper_case:nn { \@@_change_case:nnn { upper } } \cs_new_nopar:Npn \tl_mixed_case:nn { \@@_mixed_case:nn } % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}[aux, EXP]{\@@_change_case:nnn} % \begin{macro}[aux, EXP]{\@@_change_case_aux:nnn} % \begin{macro}[aux, EXP]{\@@_change_case_loop:wnn} % \begin{macro}[aux, EXP] % { % \@@_change_case_output:nwn , % \@@_change_case_output:Vwn , % \@@_change_case_output:own , % \@@_change_case_output:fwn , % } % \begin{macro}[aux, EXP]{\@@_change_case_end:wn} % \begin{macro}[aux, EXP]{\@@_change_case_group:nwnn} % \begin{macro}[aux, EXP]{\@@_change_case_space:wnn} % \begin{macro}[aux, EXP]{\@@_change_case_N_type:Nwnn} % \begin{macro}[aux, EXP]{\@@_change_case_N_type:NNNnnn} % \begin{macro}[aux, EXP]{\@@_change_case_math:NNNnnn} % \begin{macro}[aux, EXP]{\@@_change_case_math_loop:wNNnn} % \begin{macro}[aux, EXP]{\@@_change_case_math:NwNNnn} % \begin{macro}[aux, EXP]{\@@_change_case_math_group:nwNNnn} % \begin{macro}[aux, EXP]{\@@_change_case_math_space:wNNnn} % \begin{macro}[aux, EXP]{\@@_change_case_N_type:Nnnn} % \begin{macro}[aux, EXP]{\@@_change_case_char:Nnn} % \begin{macro}[aux, EXP]{\@@_change_case_char:Nn} % \begin{macro}[aux, EXP]{\@@_change_case_char:NNNNNNNNn} % \begin{macro}[aux, EXP]{\@@_change_case_cs:Nnnn} % \begin{macro}[aux, EXP]{\@@_change_case_cs:nNnnn} % \begin{macro}[aux, EXP]{\@@_change_case_cs_three:NNNw} % \begin{macro}[aux, EXP]{\@@_change_case_cs_four:NNNNw} % \begin{macro}[aux, EXP]{\@@_change_case_cs_cyr:NnNNNNw} % \begin{macro}[aux, EXP]{\@@_change_case_cs_type:Nnnnn} % \begin{macro}[aux, EXP]{\@@_change_case_cs_type:nnn} % \begin{macro}[aux, EXP]{\@@_change_case_cs:N} % \begin{macro}[aux, EXP]{\@@_change_case_cs:NN} % \begin{macro}[aux, EXP]{\@@_change_case_cs:NNn} % \begin{macro}[aux, EXP]{\@@_change_case_cs_expand:Nnw} % \begin{macro}[aux, EXP]{\@@_change_case_cs_expand:NN} % \begin{macro}[aux, EXP]{\@@_change_case_cs_expand:NNnw} % The mechanism for the core conversion of case is based on the idea that % we can use a loop to grab the entire token list plus a quark: the latter is % used as an end marker and to avoid any brace stripping. Depending on the % nature of the first item in the grabbed argument, it can either processed % as a single token, treated as a group or treated as a space. These % different cases all work by re-reading |#1| in the appropriate way, hence % the repetition of |#1 \q_recursion_stop|. % \begin{macrocode} \cs_new:Npn \@@_change_case:nnn #1#2#3 { \etex_unexpanded:D \exp_after:wN { \tex_romannumeral:D \@@_change_case_aux:nnn {#1} {#2} {#3} } } \cs_new:Npn \@@_change_case_aux:nnn #1#2#3 { \group_align_safe_begin: \@@_change_case_loop:wnn #3 \q_recursion_tail \q_recursion_stop {#1} {#2} \@@_change_case_result:n { } } \cs_new:Npn \@@_change_case_loop:wnn #1 \q_recursion_stop { \tl_if_head_is_N_type:nTF {#1} { \@@_change_case_N_type:Nwnn } { \tl_if_head_is_group:nTF {#1} { \@@_change_case_group:nwnn } { \@@_change_case_space:wnn } } #1 \q_recursion_stop } % \end{macrocode} % Earlier versions of the code where only \texttt{x}-type expandable rather % than \texttt{f}-type: this causes issues with nesting and so the slight % performance hit is taken for a better outcome in usability terms. Setting % up for \texttt{f}-type expandability has two requirements: a marker % token after the main loop (see above) and a mechanism to \enquote{load} % and finalise the result. That is handled in the code below, which includes % the necessary material to end the \tn{romannumeral} expansion. % \begin{macrocode} \cs_new:Npn \@@_change_case_output:nwn #1#2 \@@_change_case_result:n #3 { #2 \@@_change_case_result:n { #3 #1 } } \cs_generate_variant:Nn \@@_change_case_output:nwn { V , o , f } \cs_new:Npn \@@_change_case_end:wn #1 \@@_change_case_result:n #2 { \group_align_safe_end: \c_zero #2 } % \end{macrocode} % Handling for the cases where the current argument is a brace group or % a space is relatively easy. For the brace case, the routine works % recursively, using the expandability of the mechanism to ensure that the % result is finalised before storage. For the space case it is simply a % question of removing the space in the input and storing it in the output. % In both cases, and indeed for the \texttt{N}-type grabber, after removing % the current item from the input \cs{@@_change_case_loop:wnn} is inserted % in front of the remaining tokens. % \begin{macrocode} \cs_new:Npn \@@_change_case_group:nwnn #1#2 \q_recursion_stop #3#4 { \@@_change_case_output:own { \exp_after:wN { \tex_romannumeral:D \@@_change_case_aux:nnn {#3} {#4} {#1} } } \@@_change_case_loop:wnn #2 \q_recursion_stop {#3} {#4} } \exp_last_unbraced:NNo \cs_new:Npn \@@_change_case_space:wnn \c_space_tl { \@@_change_case_output:nwn { ~ } \@@_change_case_loop:wnn } % \end{macrocode} % For \texttt{N}-type arguments there are several stages to the approach. % First, a simply check for the end-of-input marker, which if found triggers % the final clean up and output step. Assuming that is not the case, the % first check is for math-mode escaping: this test can encompass control % sequences or other \texttt{N}-type tokens so is handled up front. % \begin{macrocode} \cs_new:Npn \@@_change_case_N_type:Nwnn #1#2 \q_recursion_stop { \quark_if_recursion_tail_stop_do:Nn #1 { \@@_change_case_end:wn } \exp_after:wN \@@_change_case_N_type:NNNnnn \exp_after:wN #1 \l_tl_case_change_math_tl \q_recursion_tail ? \q_recursion_stop {#2} } % \end{macrocode} % Looking for math mode escape first requires a loop over the possible % token pairs to see if the current input (|#1|) matches an open-math case % (|#2|). If if does then this test loop is ended and a new input-gathering % one is begun. The latter simply transfers material from the input to the % output without any expansion, testing each \texttt{N}-type token to see % if it matches the close-math case required. If that is the situation then % the \enquote{math loop} stops and resumes the main loop: as that might % be either the standard case-changing one or the mixed-case alternative, % it is not hard-coded into the math loop but is rather passed as argument % |#3| to \cs{@@_change_case_math:NNNnnn}. If no close-math token is found % then the final clean-up will be forced (\emph{i.e.}~there is no assumption % of \enquote{well-behaved} code in terms of math mode). % \begin{macrocode} \cs_new:Npn \@@_change_case_N_type:NNNnnn #1#2#3 { \quark_if_recursion_tail_stop_do:Nn #2 { \@@_change_case_N_type:Nnnn #1 } \token_if_eq_meaning:NNTF #1 #2 { \use_i_delimit_by_q_recursion_stop:nw { \@@_change_case_math:NNNnnn #1 #3 \@@_change_case_loop:wnn } } { \@@_change_case_N_type:NNNnnn #1 } } \cs_new:Npn \@@_change_case_math:NNNnnn #1#2#3#4 { \@@_change_case_output:nwn {#1} \@@_change_case_math_loop:wNNnn #4 \q_recursion_stop #2 #3 } \cs_new:Npn \@@_change_case_math_loop:wNNnn #1 \q_recursion_stop { \tl_if_head_is_N_type:nTF {#1} { \@@_change_case_math:NwNNnn } { \tl_if_head_is_group:nTF {#1} { \@@_change_case_math_group:nwNNnn } { \@@_change_case_math_space:wNNnn } } #1 \q_recursion_stop } \cs_new:Npn \@@_change_case_math:NwNNnn #1#2 \q_recursion_stop #3#4 { \token_if_eq_meaning:NNTF \q_recursion_tail #1 { \@@_change_case_end:wn } { \@@_change_case_output:nwn {#1} \token_if_eq_meaning:NNTF #1 #3 { #4 #2 \q_recursion_stop } { \@@_change_case_math_loop:wNNnn #2 \q_recursion_stop #3#4 } } } \cs_new:Npn \@@_change_case_math_group:nwNNnn #1#2 \q_recursion_stop { \@@_change_case_output:nwn { {#1} } \@@_change_case_math_loop:wNNnn #2 \q_recursion_stop } \exp_last_unbraced:NNo \cs_new:Npn \@@_change_case_math_space:wNNnn \c_space_tl { \@@_change_case_output:nwn { ~ } \@@_change_case_math_loop:wNNnn } % \end{macrocode} % Once potential math-mode cases are filtered out the next stage is to % test if the token grabbed is a control sequence: they cannot be used in % the lookup table and also may require expansion. At this stage the loop % code starting \cs{@@_change_case_loop:wnn} is inserted: all subsequent % steps in the code which need a look-ahead are coded to rely on this and % thus have \texttt{w}-type arguments if they may do a look-ahead. % \begin{macrocode} \cs_new:Npn \@@_change_case_N_type:Nnnn #1#2#3#4 { \token_if_cs:NTF #1 %<*initex> { \@@_change_case_cs:N #1 } % %<*package> { \@@_change_case_cs:Nnnn #1 {#3} { } { \@@_change_case_cs:N #1 } } % { \@@_change_case_char:Nnn #1 {#3} {#4} } \@@_change_case_loop:wnn #2 \q_recursion_stop {#3} {#4} } % \end{macrocode} % For character tokens there are a couple of potential special cases to % handle then the core idea of the loop: a lookup table. The latter uses % the character code to spilt what would otherwise be a very long list into % $100$ manageable blocks (this is a balance between hash table usage and % performance). Notice that the special case code may do a look-ahead so % requires a final \texttt{w}-type argument whereas the core lookup table % does not and also guarantees an output so \texttt{f}-type expansion may % be used to obtain the case-changed result. % \begin{macrocode} \cs_new:Npn \@@_change_case_char:Nnn #1#2#3 { \cs_if_exist_use:cF { @@_change_case_ #2 _ #3 :Nnw } { \use_ii:nn } #1 { \use:c { @@_change_case_ #2 _ sigma:Nnw } #1 { \@@_change_case_char:Nn #1 {#2} } } } \cs_new:Npn \@@_change_case_char:Nn #1#2 { \@@_change_case_output:fwn { \str_case:nvF #1 { c__unicode_ #2 _exceptions_tl } { \exp_after:wN \@@_change_case_char:NNNNNNNNn \int_use:N \__int_eval:w 1000000 + `#1 \__int_eval_end: #1 {#2} } } } \cs_new:Npn \@@_change_case_char:NNNNNNNNn #1#2#3#4#5#6#7#8#9 { \str_case:nvF #8 { c__unicode_ #9 _ #6 _X_ #7 _tl } { \exp_stop_f: #8 } } % \end{macrocode} % If a control sequence has been given as the argument and it is not on % the list of those with an argument to examine, the other possibility % is that it is a character represented as a command such as |\aa|. To % deal with that there is a need again to balance performance against % name use. For \LaTeXe{} the list of possible special cases is quite % long (there are around $100$ for Cyrillic alone) so a single long % \cs{str_case:nvF} is inefficient. On the other hand, using one % tl per special case would use a lot of names. The balance here is % to split up the special cases by type and for Cyrillic to further % subdivide. This works as there are various cases: % \begin{itemize} % \item Short (one or two letter) names for special Latin characters % \item Cyrillic names, all starting |cyr| % \item Greek names, all starting |text| % \item Greek accents, all starting |acc| and only applicable % for upper casing % \item Other cases (\enquote{bits and pieces}) % \end{itemize} % Thus a split can be carried out by converting the control sequence to % a string then examining the first four characters. For Cyrillic, the % fourth character can be used for a second split based on the character % code. % % Note that as this is dependent on \LaTeXe{}, in format mode the code % goes straight to the final phase of handling control sequences. % \begin{macrocode} %<*package> \cs_new:Npn \@@_change_case_cs:Nnnn #1#2 { \exp_args:Nf \@@_change_case_cs:nNnnn { \cs_to_str:N #1 } #1 {#2} } \cs_new:Npn \@@_change_case_cs:nNnnn #1#2#3 { \tl_if_head_eq_catcode:oNTF { \use_none:nnn #1 a a a a } a { \@@_change_case_cs_type:Nnnnn #2 { latin } {#3} } { \str_if_eq_x:nnTF { \@@_change_case_cs_three:NNNw #1 \q_nil } { \str_if_eq:nnTF {#3} { lower } { CYR } { cyr } } { \@@_change_case_cs_cyr:NnNNNNw #2 {#3} #1 \q_stop } { \str_if_eq_x:nnTF { \@@_change_case_cs_three:NNNw #1 \q_nil } { acc } { \@@_change_case_cs_type:Nnnnn #2 { acc } {#3} } { \str_if_eq_x:nnTF { \@@_change_case_cs_four:NNNNw #1 \q_nil } { text } { \@@_change_case_cs_type:Nnnnn #2 { greek } {#3} } { \@@_change_case_cs_type:Nnnnn #2 { misc } {#3} } } } } } \cs_new:Npn \@@_change_case_cs_three:NNNw #1#2#3#4 \q_nil { #1#2#3 } \cs_new:Npn \@@_change_case_cs_four:NNNNw #1#2#3#4#5 \q_nil { #1#2#3#4 } \cs_new:Npn \@@_change_case_cs_cyr:NnNNNNw #1#2#3#4#5#6#7 \q_stop { \@@_change_case_cs_type:Nnnnn #1 { cyrillic } { #2 _ \int_to_roman:n { 1 + \int_div_truncate:nn { `#6 - \str_if_eq:nnTF {#2} { lower } { `A } { `a } } { 7 } } } } \cs_new:Npn \@@_change_case_cs_type:Nnnnn #1#2#3 { \exp_args:Nf \@@_change_case_cs_type:nnn { \str_case:nvF #1 { c_@@_change_case_ #2 _ #3 _ tl } { \exp_stop_f: } } } \cs_new:Npn \@@_change_case_cs_type:nnn #1#2#3 { \tl_if_blank:nTF {#1} {#3} { \@@_change_case_output:nwn {#1} #2 } } % % \end{macrocode} % To deal with a control sequence there is first a need to test if it is % on the list which indicate that case changing should be skipped. That's % done using a loop as for the other special cases. If a hit is found then % the argument is grabbed: that comes \emph{after} the loop function which % is therefore rearranged. % \begin{macrocode} \cs_new:Npn \@@_change_case_cs:N #1 { \exp_after:wN \@@_change_case_cs:NN \exp_after:wN #1 \l_tl_case_change_exclude_tl \q_recursion_tail \q_recursion_stop } \cs_new:Npn \@@_change_case_cs:NN #1#2 { \quark_if_recursion_tail_stop_do:Nn #2 { \@@_change_case_cs_expand:Nnw #1 { \@@_change_case_output:nwn {#1} } } \str_if_eq:nnTF {#1} {#2} { \use_i_delimit_by_q_recursion_stop:nw { \@@_change_case_cs:NNn #1 } } { \@@_change_case_cs:NN #1 } } \cs_new:Npn \@@_change_case_cs:NNn #1#2#3 { \@@_change_case_output:nwn { #1 {#3} } #2 } % \end{macrocode} % When a control sequence is not on the exclude list the other test if % to see if it is expandable. Once again, if there is a hit then the loop % function is grabbed as part of the clean-up and reinserted before the % now expanded material. % \begin{macrocode} \cs_new:Npn \@@_change_case_cs_expand:Nnw #1#2 { \bool_if:nTF { \token_if_expandable_p:N #1 && ! \token_if_protected_macro_p:N #1 && ! \token_if_protected_long_macro_p:N #1 } { \@@_change_case_cs_expand:NN #1 } { #2 } } \cs_new:Npn \@@_change_case_cs_expand:NN #1#2 { \exp_after:wN #2 #1 } \cs_new:Npn \@@_change_case_cs_expand:NNnw #1#2#3 { \@@_change_case_cs_expand:Nnw #2 {#3} #1 } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[aux, EXP]{\@@_change_case_lower_sigma:Nnw} % \begin{macro}[aux, EXP]{\@@_change_case_lower_sigma:w} % \begin{macro}[aux, EXP]{\@@_change_case_lower_sigma:Nw} % \begin{macro}[aux, EXP]{\@@_change_case_lower_sigma_loop:NN} % \begin{macro}[aux, EXP]{\@@_change_case_upper_sigma:Nnw} % If the current char is an upper case sigma, the a check is made on the next % item in the input. If it is \texttt{N}-type and not a control sequence % then there is a look-ahead phase. % \begin{macrocode} \cs_new:Npn \@@_change_case_lower_sigma:Nnw #1#2#3#4 \q_recursion_stop { \int_compare:nNnTF { `#1 } = { "03A3 } { \@@_change_case_output:fwn { \@@_change_case_lower_sigma:w #4 \q_recursion_stop } } {#2} #3 #4 \q_recursion_stop } \cs_new:Npn \@@_change_case_lower_sigma:w #1 \q_recursion_stop { \tl_if_head_is_N_type:nTF {#1} { \@@_change_case_lower_sigma:Nw #1 \q_recursion_stop } { \c__unicode_final_sigma_tl } } \cs_new:Npn \@@_change_case_lower_sigma:Nw #1#2 \q_recursion_stop { \token_if_cs:NTF #1 { \c__unicode_final_sigma_tl } { \exp_after:wN \@@_change_case_lower_sigma_loop:NN \exp_after:wN #1 \l_tl_case_change_after_final_sigma_tl \q_recursion_tail \q_recursion_stop } } % \end{macrocode} % Assuming the next token is not a control sequence, a loop is used to test % if the next char is something that can be interpreted as the end of a word. % Rather than use all of the Unicode data for this, the simplifying % assumption is made that in real text the end of a word will be indicated by % a small number of chars. % \begin{macrocode} \cs_new:Npn \@@_change_case_lower_sigma_loop:NN #1#2 { \quark_if_recursion_tail_stop_do:Nn #2 { \c__unicode_std_sigma_tl } \int_compare:nNnT { `#1 } = { `#2 } { \use_i_delimit_by_q_recursion_stop:nw { \c__unicode_final_sigma_tl } } \@@_change_case_lower_sigma_loop:NN #1 } % \end{macrocode} % Simply skip to the final step for upper casing. % \begin{macrocode} \cs_new_eq:NN \@@_change_case_upper_sigma:Nnw \use_ii:nn % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[aux, EXP]{\@@_change_case_lower_tr:Nnw} % \begin{macro}[aux, EXP]{\@@_change_case_lower_tr_auxi:Nw} % \begin{macro}[aux, EXP]{\@@_change_case_lower_tr_auxii:Nw} % \begin{macro}[aux, EXP]{\@@_change_case_upper_tr:Nnw} % \begin{macro}[aux, EXP]{\@@_change_case_lower_az:Nnw} % \begin{macro}[aux, EXP]{\@@_change_case_upper_az:Nnw} % The Turkic languages need special treatment for dotted-i and dotless-i. % The lower casing rule can be expressed in terms of searching first for % either a dotless-I or a dotted-I. In the latter case the mapping is % easy, but in the former there is a second stage search. % \begin{macrocode} \cs_new:Npn \@@_change_case_lower_tr:Nnw #1#2 { \int_compare:nNnTF { `#1 } = { "0049 } { \@@_change_case_lower_tr_auxi:Nw } { \int_compare:nNnTF { `#1 } = { "0130 } { \@@_change_case_output:nwn { i } } {#2} } } % \end{macrocode} % After a dotless-I there may be a dot-above character. If there is then % a dotted-i should be produced, otherwise output a dotless-i. When the % combination is found both the dotless-I and the dot-above char have to % be removed from the input, which is done by the \cs{use_i:nn} % (it grabs \cs{@@_change_case_loop:wn} and the dot-above char and % discards the latter). % \begin{macrocode} \cs_new:Npn \@@_change_case_lower_tr_auxi:Nw #1#2 \q_recursion_stop { \tl_if_head_is_N_type:nTF {#2} { \@@_change_case_lower_tr_auxii:Nw #2 \q_recursion_stop } { \@@_change_case_output:Vwn \c__unicode_dotless_i_tl } #1 #2 \q_recursion_stop } \cs_new:Npn \@@_change_case_lower_tr_auxii:Nw #1#2 \q_recursion_stop { \bool_if:nTF { \token_if_cs_p:N #1 || ! ( \int_compare_p:nNn { `#1 } = { "0307 } ) } { \@@_change_case_output:Vwn \c__unicode_dotless_i_tl } { \@@_change_case_output:nwn { i } \use_i:nn } } % \end{macrocode} % Upper casing is easier: just one exception with no context. % \begin{macrocode} \cs_new:Npn \@@_change_case_upper_tr:Nnw #1#2 { \int_compare:nNnTF { `#1 } = { "0069 } { \@@_change_case_output:Vwn \c__unicode_dotted_I_tl } {#2} } % \end{macrocode} % Straight copies. % \begin{macrocode} \cs_new_eq:NN \@@_change_case_lower_az:Nnw \@@_change_case_lower_tr:Nnw \cs_new_eq:NN \@@_change_case_upper_az:Nnw \@@_change_case_upper_tr:Nnw % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[aux, EXP]{\@@_change_case_lower_lt:Nnw} % \begin{macro}[aux, EXP]{\@@_change_case_lower_lt:nNnw} % \begin{macro}[aux, EXP]{\@@_change_case_lower_lt:nnw} % \begin{macro}[aux, EXP]{\@@_change_case_lower_lt:Nw} % \begin{macro}[aux, EXP]{\@@_change_case_lower_lt:NNw} % \begin{macro}[aux, EXP]{\@@_change_case_upper_lt:Nnw} % \begin{macro}[aux, EXP]{\@@_change_case_upper_lt:nnw} % \begin{macro}[aux, EXP]{\@@_change_case_upper_lt:Nw} % \begin{macro}[aux, EXP]{\@@_change_case_upper_lt:NNw} % For Lithuanian, the issue to be dealt with is dots over lower case % letters: these should be present if there is another accent. That means % that there is some work to do when lower casing I and J. The first step % is a simple match attempt: \cs{c_@@_accents_lt_tl} contains % accented upper case letters which should gain a dot-above char in their % lower case form. This is done using \texttt{f}-type expansion so only one % pass is needed to find if it works or not. If there was no hit, the second % stage is to check for I, J and I-ogonek, and if the current char is a % match to look for a following accent. % \begin{macrocode} \cs_new:Npn \@@_change_case_lower_lt:Nnw #1 { \exp_args:Nf \@@_change_case_lower_lt:nNnw { \str_case:nVF #1 \c__unicode_accents_lt_tl \exp_stop_f: } #1 } \cs_new:Npn \@@_change_case_lower_lt:nNnw #1#2 { \tl_if_blank:nTF {#1} { \exp_args:Nf \@@_change_case_lower_lt:nnw { \int_case:nnF {`#2} { { "0049 } i { "004A } j { "012E } \c__unicode_i_ogonek_tl } \exp_stop_f: } } { \@@_change_case_output:nwn {#1} \use_none:n } } \cs_new:Npn \@@_change_case_lower_lt:nnw #1#2 { \tl_if_blank:nTF {#1} {#2} { \@@_change_case_output:nwn {#1} \@@_change_case_lower_lt:Nw } } % \end{macrocode} % Grab the next char and see if it is one of the accents used in Lithuanian: % if it is, add the dot-above char into the output. % \begin{macrocode} \cs_new:Npn \@@_change_case_lower_lt:Nw #1#2 \q_recursion_stop { \tl_if_head_is_N_type:nT {#2} { \@@_change_case_lower_lt:NNw } #1 #2 \q_recursion_stop } \cs_new:Npn \@@_change_case_lower_lt:NNw #1#2#3 \q_recursion_stop { \bool_if:nT { ! \token_if_cs_p:N #2 && ( \int_compare_p:nNn { `#2 } = { "0300 } || \int_compare_p:nNn { `#2 } = { "0301 } || \int_compare_p:nNn { `#2 } = { "0303 } ) } { \@@_change_case_output:Vwn \c__unicode_dot_above_tl } #1 #2#3 \q_recursion_stop } % \end{macrocode} % For upper casing, the test required is for a dot-above char after an I, % J or I-ogonek. First a test for the appropriate letter, and if found a % look-ahead and potentially one token dropped. % \begin{macrocode} \cs_new:Npn \@@_change_case_upper_lt:Nnw #1 { \exp_args:Nf \@@_change_case_upper_lt:nnw { \int_case:nnF {`#1} { { "0069 } I { "006A } J { "012F } \c__unicode_I_ogonek_tl } \exp_stop_f: } } \cs_new:Npn \@@_change_case_upper_lt:nnw #1#2 { \tl_if_blank:nTF {#1} {#2} { \@@_change_case_output:nwn {#1} \@@_change_case_upper_lt:Nw } } \cs_new:Npn \@@_change_case_upper_lt:Nw #1#2 \q_recursion_stop { \tl_if_head_is_N_type:nT {#2} { \@@_change_case_upper_lt:NNw } #1 #2 \q_recursion_stop } \cs_new:Npn \@@_change_case_upper_lt:NNw #1#2#3 \q_recursion_stop { \bool_if:nTF { ! \token_if_cs_p:N #2 && \int_compare_p:nNn { `#2 } = { "0307 } } { #1 } { #1 #2 } #3 \q_recursion_stop } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[aux, EXP]{\@@_mixed_case:nn} % \begin{macro}[aux, EXP]{\@@_mixed_case_aux:nn} % \begin{macro}[aux, EXP]{\@@_mixed_case_loop:wn} % \begin{macro}[aux, EXP]{\@@_mixed_case_group:nwn} % \begin{macro}[aux, EXP]{\@@_mixed_case_space:wn} % \begin{macro}[aux, EXP]{\@@_mixed_case_N_type:Nwn} % \begin{macro}[aux, EXP]{\@@_mixed_case_N_type:NNNnn} % \begin{macro}[aux, EXP]{\@@_mixed_case_N_type:Nnn} % \begin{macro}[aux, EXP]{\@@_mixed_case_char:Nn} % \begin{macro}[aux, EXP]{\@@_mixed_case_skip:N} % \begin{macro}[aux, EXP]{\@@_mixed_case_skip:NN} % \begin{macro}[aux, EXP]{\@@_mixed_case_skip_tidy:Nwn} % \begin{macro}[aux, EXP]{\@@_mixed_case_char:nN} % Mixed (title) casing requires some custom handling of the case changing % of the first letter in the input followed by a switch to the normal % lower casing routine. That could be covered by passing a set of functions % to generic routines, but at the cost of making the process rather opaque. % Instead, the approach taken here is to use a dedicated set of functions % which keep the different loop requirements clearly separate. % % The main loop looks for the first \enquote{real} char in the input % (skipping any pre-letter chars). Once one is found, it is case changed to % upper case but first checking that there is not an entry in the exceptions % list. Note that simply grabbing the first token in the input is no good % here: it can't handle pre-letter tokens or any special treatment of the % first letter found (\emph{e.g.}~words starting with \texttt{i} in % Turkish). Spaces at the start of the input are passed through without % counting as being the \enquote{start} of the first word, while a brace % group is assumed to be contain the first char with everything after the % brace therefore lower cased. % \begin{macrocode} \cs_new:Npn \@@_mixed_case:nn #1#2 { \etex_unexpanded:D \exp_after:wN { \tex_romannumeral:D \@@_mixed_case_aux:nn {#1} {#2} } } \cs_new:Npn \@@_mixed_case_aux:nn #1#2 { \group_align_safe_begin: \@@_mixed_case_loop:wn #2 \q_recursion_tail \q_recursion_stop {#1} \@@_change_case_result:n { } } \cs_new:Npn \@@_mixed_case_loop:wn #1 \q_recursion_stop { \tl_if_head_is_N_type:nTF {#1} { \@@_mixed_case_N_type:Nwn } { \tl_if_head_is_group:nTF {#1} { \@@_mixed_case_group:nwn } { \@@_mixed_case_space:wn } } #1 \q_recursion_stop } \cs_new:Npn \@@_mixed_case_group:nwn #1#2 \q_recursion_stop #3 { \@@_change_case_output:own { \exp_after:wN { \tex_romannumeral:D \@@_mixed_case_aux:nn {#3} {#1} } } \@@_change_case_loop:wnn #2 \q_recursion_stop { lower } {#3} } \exp_last_unbraced:NNo \cs_new:Npn \@@_mixed_case_space:wn \c_space_tl { \@@_change_case_output:nwn { ~ } \@@_mixed_case_loop:wn } \cs_new:Npn \@@_mixed_case_N_type:Nwn #1#2 \q_recursion_stop { \quark_if_recursion_tail_stop_do:Nn #1 { \@@_change_case_end:wn } \exp_after:wN \@@_mixed_case_N_type:NNNnn \exp_after:wN #1 \l_tl_case_change_math_tl \q_recursion_tail ? \q_recursion_stop {#2} } \cs_new:Npn \@@_mixed_case_N_type:NNNnn #1#2#3 { \quark_if_recursion_tail_stop_do:Nn #2 { \@@_mixed_case_N_type:Nnn #1 } \token_if_eq_meaning:NNTF #1 #2 { \use_i_delimit_by_q_recursion_stop:nw { \@@_change_case_math:NNNnnn #1 #3 \@@_mixed_case_loop:wn } } { \@@_mixed_case_N_type:NNNnn #1 } } % \end{macrocode} % The business end of the loop is here: there is first a need to deal % with any control sequence cases before looking for characters to skip. % \begin{macrocode} \cs_new:Npn \@@_mixed_case_N_type:Nnn #1#2#3 { \token_if_cs:NTF #1 %<*initex> { \@@_change_case_cs:N #1 \@@_mixed_case_loop:wn #2 \q_recursion_stop {#3} } % %<*package> { \@@_change_case_cs:Nnnn #1 { upper } { \@@_change_case_loop:wnn #2 \q_recursion_stop { lower } {#3} } { \@@_change_case_cs:N #1 \@@_mixed_case_loop:wn #2 \q_recursion_stop {#3} } } % { \@@_mixed_case_char:Nn #1 {#3} \@@_change_case_loop:wnn #2 \q_recursion_stop { lower } {#3} } } % \end{macrocode} % As detailed above, handling a mixed case char means first looking for % exceptions then treating as an upper cased letter, but with a list of % tokens to skip over too. % \begin{macrocode} \cs_new:Npn \@@_mixed_case_char:Nn #1#2 { \cs_if_exist_use:cF { @@_change_case_mixed_ #2 :Nnw } { \cs_if_exist_use:cF { @@_change_case_upper_ #2 :Nnw } { \use_ii:nn } } #1 { \@@_mixed_case_skip:N #1 } } \cs_new:Npn \@@_mixed_case_skip:N #1 { \exp_after:wN \@@_mixed_case_skip:NN \exp_after:wN #1 \l_tl_mixed_case_ignore_tl \q_recursion_tail \q_recursion_stop } \cs_new:Npn \@@_mixed_case_skip:NN #1#2 { \quark_if_recursion_tail_stop_do:nn {#2} { \exp_args:Nf \@@_mixed_case_char:nN { \str_case:nVF #1 \c__unicode_mixed_exceptions_tl \exp_stop_f: } #1 } \int_compare:nNnT { `#1 } = { `#2 } { \use_i_delimit_by_q_recursion_stop:nw { \@@_change_case_output:nwn {#1} \@@_mixed_case_skip_tidy:Nwn } } \@@_mixed_case_skip:NN #1 } \cs_new:Npn \@@_mixed_case_skip_tidy:Nwn #1#2 \q_recursion_stop #3 { \@@_mixed_case_loop:wn #2 \q_recursion_stop } \cs_new:Npn \@@_mixed_case_char:nN #1#2 { \tl_if_blank:nTF {#1} { \@@_change_case_char:Nn #2 { upper } } { \@@_change_case_output:nwn {#1} } } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}[aux, EXP]{\@@_change_case_mixed_nl:Nnw} % \begin{macro}[aux, EXP]{\@@_change_case_mixed_nl:Nw} % \begin{macro}[aux, EXP]{\@@_change_case_mixed_nl:NNw} % For Dutch, there is a single look-ahead test for \texttt{ij} when % title casing. If the appropriate letters are found, produce \texttt{IJ} % and gobble the \texttt{j}/\texttt{J}. % \begin{macrocode} \cs_new:Npn \@@_change_case_mixed_nl:Nnw #1 { \bool_if:nTF { \int_compare_p:nNn { `#1 } = { `i } || \int_compare_p:nNn { `#1 } = { `I } } { \@@_change_case_output:nwn { I } \@@_change_case_mixed_nl:Nw } } \cs_new:Npn \@@_change_case_mixed_nl:Nw #1#2 \q_recursion_stop { \tl_if_head_is_N_type:nT {#2} { \@@_change_case_mixed_nl:NNw } #1 #2 \q_recursion_stop } \cs_new:Npn \@@_change_case_mixed_nl:NNw #1#2#3 \q_recursion_stop { \bool_if:nTF { ! ( \token_if_cs_p:N #2 ) && ( \int_compare_p:nNn { `#2 } = { `j } || \int_compare_p:nNn { `#2 } = { `J } ) } { \@@_change_case_output:nwn { J } #1 } { #1 #2 } #3 \q_recursion_stop } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{variable}{\l_tl_case_change_math_tl} % The list of token pairs which are treated as math mode and so % not case changed. % \begin{macrocode} \tl_new:N \l_tl_case_change_math_tl %<*package> \tl_set:Nn \l_tl_case_change_math_tl { $ $ \( \) } % % \end{macrocode} % \end{variable} % % \begin{variable}{\l_tl_case_change_exclude_tl} % The list of commands for which an argument is not case changed. % \begin{macrocode} \tl_new:N \l_tl_case_change_exclude_tl %<*package> \tl_set:Nn \l_tl_case_change_exclude_tl { \cite \ensuremath \label \ref } % % \end{macrocode} % \end{variable} % % \begin{variable}{\l_tl_case_change_after_final_sigma_tl} % Characters coming after a final sigma. % \begin{macrocode} \tl_new:N \l_tl_case_change_after_final_sigma_tl \tl_set:Nx \l_tl_case_change_after_final_sigma_tl { % ( ) % [ ] % \{ \cs_to_str:N \} . : ; , ! ? ' " } % \end{macrocode} % \end{variable} % % \begin{variable}{\l_tl_mixed_case_ignore_tl} % Characters to skip over when finding the first letter in a word to be % mixed cased. % \begin{macrocode} \tl_new:N \l_tl_mixed_case_ignore_tl \tl_set:Nx \l_tl_mixed_case_ignore_tl { ( % ) [ % ] \cs_to_str:N \{ % \} ` - } % \end{macrocode} % \end{variable} % % \begin{variable} % { % \c_@@_change_case_latin_upper_tl , % \c_@@_change_case_latin_lower_tl , % \c_@@_change_case_cyrillic_upper_i_tl , % \c_@@_change_case_cyrillic_upper_ii_tl , % \c_@@_change_case_cyrillic_upper_iii_tl , % \c_@@_change_case_cyrillic_upper_iv_tl , % \c_@@_change_case_cyrillic_lower_i_tl , % \c_@@_change_case_cyrillic_lower_ii_tl , % \c_@@_change_case_cyrillic_lower_iii_tl , % \c_@@_change_case_cyrillic_lower_iv_tl , % \c_@@_change_case_greek_upper_tl , % \c_@@_change_case_greek_lower_tl , % \c_@@_change_case_acc_upper_tl , % \c_@@_change_case_acc_upper_tl , % \c_@@_change_case_misc_upper_tl , % \c_@@_change_case_misc_lower_tl % } % The data for case changing control sequences representing characters % is stored such that further expansion is inhibited. This is required % as many of the \LaTeXe{} commands are fragile and so will fail inside % an \texttt{x}-type expansion without this precaution. As such, there is % a bit of set up to deal with the various requirements here: upper and % lower case mappings are not identical so special end cases are needed % to get everything set up correctly with minimal repetition. % \begin{macrocode} %<*package> \group_begin: \cs_set_protected:Npn \@@_change_case_setup:nnnn #1#2#3#4 { \tl_const:cx { c_@@_change_case_ #1 _upper #2 _tl } { \@@_change_case_map:NN #3 \q_recursion_tail ? \q_recursion_stop } \tl_const:cx { c_@@_change_case_ #1 _lower #2 _tl } { \@@_change_case_map:NNN #4 #3 \q_recursion_tail ? \q_recursion_stop } } \cs_set:Npn \@@_change_case_map:NN #1#2 { \quark_if_recursion_tail_stop:N #1 \exp_not:N #1 \exp_not:n { { \exp_stop_f: #2 } } \@@_change_case_map:NN } \cs_set:Npn \@@_change_case_map:NNN #1#2#3 { \str_if_eq:nnT {#1} {#2} { \use_none_delimit_by_q_recursion_stop:w } \exp_not:N #3 \exp_not:n { { \exp_stop_f: #2 } } \@@_change_case_map:NNN #1 } \@@_change_case_setup:nnnn { latin } { } { \aa \AA \ae \AE \dh \DH \dj \DJ \l \L \ng \NG \o \O \oe \OE \ss \SS \th \TH \i I \j J } { \i } \@@_change_case_setup:nnnn { cyrillic } { _i } { \cyra \CYRA \cyrabhch \CYRABHCH \cyrabhchdsc \CYRABHCHDSC \cyrabhdze \CYRABHDZE \cyrabhha \CYRABHHA \cyrae \CYRAE \cyrb \CYRB \cyrbyus \CYRBYUS \cyrc \CYRC \cyrch \CYRCH \cyrchldsc \CYRCHLDSC \cyrchrdsc \CYRCHRDSC \cyrchvcrs \CYRCHVCRS \cyrd \CYRD \cyrdelta \CYRDELTA \cyrdje \CYRDJE \cyrdze \CYRDZE \cyrdzhe \CYRDZHE \cyre \CYRE \cyreps \CYREPS \cyrerev \CYREREV \cyrery \CYRERY \cyrf \CYRF \cyrfita \CYRFITA \cyrg \CYRG \cyrgdsc \CYRGDSC \cyrgdschcrs \CYRGDSCHCRS \cyrghcrs \CYRGHCRS \cyrghk \CYRGHK \cyrgup \CYRGUP } { \q_recursion_tail } \@@_change_case_setup:nnnn { cyrillic } { _ii } { \cyrh \CYRH \cyrhdsc \CYRHDSC \cyrhhcrs \CYRHHCRS \cyrhhk \CYRHHK \cyrhrdsn \CYRHRDSN \cyri \CYRI \cyrie \CYRIE \cyrii \CYRII \cyrishrt \CYRISHRT \cyrishrtdsc \CYRISHRTDSC \cyrizh \CYRIZH \cyrje \CYRJE \cyrk \CYRK \cyrkbeak \CYRKBEAK \cyrkdsc \CYRKDSC \cyrkhcrs \CYRKHCRS \cyrkhk \CYRKHK \cyrkvcrs \CYRKVCRS \cyrl \CYRL \cyrldsc \CYRLDSC \cyrlhk \CYRLHK \cyrlje \CYRLJE \cyrm \CYRM \cyrmdsc \CYRMDSC \cyrmhk \CYRMHK \cyrn \CYRN \cyrndsc \CYRNDSC \cyrng \CYRNG \cyrnhk \CYRNHK \cyrnje \CYRNJE \cyrnlhk \CYRNLHK } { \q_recursion_tail } \@@_change_case_setup:nnnn { cyrillic } { _iii } { \cyro \CYRO \cyrotld \CYROTLD \cyrp \CYRP \cyrphk \CYRPHK \cyrq \CYRQ \cyrr \CYRR \cyrrdsc \CYRRDSC \cyrrhk \CYRRHK \cyrrtick \CYRRTICK \cyrs \CYRS \cyrsacrs \CYRSACRS \cyrschwa \CYRSCHWA \cyrsdsc \CYRSDSC \cyrsemisftsn \CYRSEMISFTSN \cyrsftsn \CYRSFTSN \cyrsh \CYRSH \cyrshch \CYRSHCH \cyrshha \CYRSHHA \cyrt \CYRT \cyrtdsc \CYRTDSC \cyrtetse \CYRTETSE \cyrtshe \CYRTSHE \cyru \CYRU \cyrushrt \CYRUSHRT } { \q_recursion_tail } \@@_change_case_setup:nnnn { cyrillic } { _iv } { \cyrv \CYRV \cyrw \CYRW \cyry \CYRY \cyrya \CYRYA \cyryat \CYRYAT \cyryhcrs \CYRYHCRS \cyryi \CYRYI \cyryo \CYRYO \cyryu \CYRYU \cyrz \CYRZ \cyrzdsc \CYRZDSC \cyrzh \CYRZH \cyrzhdsc \CYRZHDSC } { \q_recursion_tail } \@@_change_case_setup:nnnn { greek } { } { \textalpha \textAlpha \textbeta \textBeta \textchi \textChi \textdelta \textDelta \textdigamma \textDigamma \texteta \textEta \textepsilon \textEpsilon \textgamma \textGamma \textiota \textIota \textkappa \textKappa \textlambda \textLambda \textmu \textMu \textnu \textNu \textomega \textOmega \textomicron \textOmicron \textphi \textPhi \textpi \textPi \textpsi \textPsi \textqoppa \textQoppa \textrho \textRho \textsampi \textSampi \textautosigma \textSigma \textstigma \textStigma \texttheta \textTheta \texttau \textTau \textupsilon \textUpsilon \textxi \textXi \textzeta \textZeta \textsigma \textSigma \textvarsigma \textSigma \textvarstigma \textStigma } { \textsigma } \tl_const:Nn \c_@@_change_case_acc_upper_tl { \accdasia { \exp_stop_f: \LGR@accdropped } \accdasiaoxia { \exp_stop_f: \LGR@hiatus } \accdasiavaria { \exp_stop_f: \LGR@accdropped } \accdasiaperispomeni { \exp_stop_f: \LGR@accdropped } \accpsili { \exp_stop_f: \LGR@hiatus } \accpsilioxia { \exp_stop_f: \LGR@hiatus } \accpsilivaria { \exp_stop_f: \LGR@hiatus } \accpsiliperispomeni { \exp_stop_f: \LGR@accdropped } \acctonos { \exp_stop_f: \LGR@hiatus } \accvaria { \exp_stop_f: \LGR@accdropped } \accdialytikatonos { \exp_stop_f: \LGR@accDialytika } \accdialytikavaria { \exp_stop_f: \LGR@accDialytika } \accdialytikaperispomeni { \exp_stop_f: \LGR@accDialytika } \accperispomeni { \exp_stop_f: \LGR@accdropped } } \tl_const:Nn \c_@@_change_case_acc_lower_tl { } \tl_const:Nn \c_@@_change_case_misc_upper_tl { \ypogegrammeni { \exp_stop_f: \prosgegrammeni } \abreve { \exp_stop_f: \Abreve } \acircumflex { \exp_stop_f: \Acircumflex } \ecircumflex { \exp_stop_f: \Ecircumflex } \ocircumflex { \exp_stop_f: \Ocircumflex } \ohorn { \exp_stop_f: \Ohorn } \uhorn { \exp_stop_f: \Uhorn } } \tl_const:Nn \c_@@_change_case_misc_lower_tl { \prosgegrammeni { \exp_stop_f: \ypogegrammeni } \Abreve { \exp_stop_f: \abreve } \Acircumflex { \exp_stop_f: \acircumflex } \Ecircumflex { \exp_stop_f: \ecircumflex } \Ocircumflex { \exp_stop_f: \ocircumflex } \Ohorn { \exp_stop_f: \ohorn } \Uhorn { \exp_stop_f: \uhorn } \ABREVE { \exp_stop_f: \abreve } \ACIRCUMFLEX { \exp_stop_f: \acircumflex } \ECIRCUMFLEX { \exp_stop_f: \ecircumflex } \OCIRCUMFLEX { \exp_stop_f: \ocircumflex } \OHORN { \exp_stop_f: \ohorn } \UHORN { \exp_stop_f: \uhorn } } \group_end: % % \end{macrocode} % \end{variable} % % \begin{macro}{\tl_log:N, \tl_log:c} % Showing token list variables in the log file is done after checking % that the variable is defined. % \begin{macrocode} \cs_new_protected:Npn \tl_log:N #1 { \tl_if_exist:NTF #1 { \cs_log:N #1 } { \__msg_kernel_error:nnx { kernel } { variable-not-defined } { \token_to_str:N #1 } } } \cs_generate_variant:Nn \tl_log:N { c } % \end{macrocode} % \end{macro} % % \begin{macro}{\tl_log:n} % The same as \cs{tl_show:n} but using \cs{__msg_log_wrap:n}. % \begin{macrocode} \cs_new_protected:Npn \tl_log:n #1 { \__msg_log_wrap:n { > ~ \tl_to_str:n {#1} } } % \end{macrocode} % \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}[aux] % {\@@_execute_branches_N_type:, \@@_N_type:w, \@@_N_type_aux:nnw} % All tokens are \texttt{N}-type tokens, except in four cases: % begin-group tokens, end-group tokens, space tokens with character % code~$32$, and outer tokens. Since \cs{l_peek_token} might be % outer, we cannot use the convenient \cs{bool_if:nTF} function, and % must resort to the old trick of using \tn{ifodd} to expand a set of % tests. The \texttt{false} branch of this test is taken if the token % is one of the first three kinds of non-\texttt{N}-type tokens % (explicit or implicit), thus we call \cs{@@_false:w}. In the % \texttt{true} branch, we must detect outer tokens, without impacting % performance too much for non-outer tokens. The first filter is to % search for \texttt{outer} in the \tn{meaning} of \cs{l_peek_token}. % If that is absent, \cs{use_none_delimit_by_q_stop:w} cleans up, and % we call \cs{@@_true:w}. Otherwise, the token can be a non-outer % macro or a primitive mark whose parameter or replacement text % contains \texttt{outer}, it can be the primitive \tn{outer}, or it % can be an outer token. Macros and marks would have \texttt{ma} in % the part before the first occurrence of \texttt{outer}; the meaning % of \tn{outer} has nothing after \texttt{outer}, contrarily to outer % macros; and that covers all cases, calling \cs{@@_true:w} or % \cs{@@_false:w} as appropriate. Here, there is no \meta{search % token}, so we feed a dummy \cs{scan_stop:} to the % \cs{@@_token_generic:NNTF} function. % \begin{macrocode} \group_begin: \char_set_catcode_other:N \O \char_set_catcode_other:N \U \char_set_catcode_other:N \T \char_set_catcode_other:N \E \char_set_catcode_other:N \R \tl_to_lowercase:n { \cs_new_protected_nopar:Npn \@@_execute_branches_N_type: { \if_int_odd:w \if_catcode:w \exp_not:N \l_peek_token { \c_two \fi: \if_catcode:w \exp_not:N \l_peek_token } \c_two \fi: \if_meaning:w \l_peek_token \c_space_token \c_two \fi: \c_one \exp_after:wN \@@_N_type:w \token_to_meaning:N \l_peek_token \q_mark \@@_N_type_aux:nnw OUTER \q_mark \use_none_delimit_by_q_stop:w \q_stop \exp_after:wN \@@_true:w \else: \exp_after:wN \@@_false:w \fi: } \cs_new_protected:Npn \@@_N_type:w #1 OUTER #2 \q_mark #3 { #3 {#1} {#2} } } \group_end: \cs_new_protected:Npn \@@_N_type_aux:nnw #1 #2 #3 \fi: { \fi: \tl_if_in:noTF {#1} { \tl_to_str:n {ma} } { \@@_true:w } { \tl_if_empty:nTF {#2} { \@@_true:w } { \@@_false: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} % % \subsection{Deprecated candidates} % % \begin{macro} % { % \fp_set_from_dim:Nn, \fp_set_from_dim:cn, % \fp_gset_from_dim:Nn, \fp_gset_from_dim:cn % } % Deprecated 2014-07-17. % \begin{macrocode} \cs_new_protected:Npn \fp_set_from_dim:Nn #1#2 { \fp_set:Nn #1 { \dim_to_fp:n {#2} } } \cs_new_protected:Npn \fp_gset_from_dim:Nn #1#2 { \fp_gset:Nn #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} % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintIndex