diff options
author | Norbert Preining <norbert@preining.info> | 2024-05-01 03:00:54 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2024-05-01 03:00:54 +0000 |
commit | 6ac3dbed4b71022bd6bb0cc25e8fc3df81198498 (patch) | |
tree | 74689e5368262d351f68fb5d1f0fbea88a3af701 /macros/latex/required/l3kernel/l3box.dtx | |
parent | 3bb814fe836cd59db56f2f5d07f69cb531ed25e0 (diff) |
CTAN sync 202405010300
Diffstat (limited to 'macros/latex/required/l3kernel/l3box.dtx')
-rw-r--r-- | macros/latex/required/l3kernel/l3box.dtx | 2530 |
1 files changed, 2530 insertions, 0 deletions
diff --git a/macros/latex/required/l3kernel/l3box.dtx b/macros/latex/required/l3kernel/l3box.dtx new file mode 100644 index 0000000000..2d4902950f --- /dev/null +++ b/macros/latex/required/l3kernel/l3box.dtx @@ -0,0 +1,2530 @@ +% \iffalse meta-comment +% +%% File: l3box.dtx +% +% Copyright (C) 2005-2024 The LaTeX 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 +% +% https://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 development version of the bundle can be found at +% +% https://github.com/latex3/latex3 +% +% for those people who are interested. +% +%<*driver> +\documentclass[full,kernel]{l3doc} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% \title{^^A +% The \pkg{l3box} module\\ Boxes^^A +% } +% +% \author{^^A +% The \LaTeX{} Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \date{Released 2024-04-11} +% +% \maketitle +% +% \begin{documentation} +% +% Box variables contain typeset material that can be inserted on the +% page or in other boxes. Their contents cannot be converted back to +% lists of tokens. +% There are three kinds of box operations: horizontal mode denoted +% with prefix |\hbox_|, vertical mode with prefix |\vbox_|, and the +% generic operations working in both modes with prefix |\box_|. +% For instance, a new box variable containing the words \enquote{Hello, +% world!} (in a horizontal box) can be obtained by the following code. +% \begin{verbatim} +% \box_new:N \l_hello_box +% \hbox_set:Nn \l_hello_box { Hello, ~ world! } +% \end{verbatim} +% The argument is typeset inside a \TeX{} group so that any variables +% assigned during the construction of this box restores its value +% afterwards. +% +% Box variables from \pkg{l3box} are compatible with those of \LaTeXe{} +% and plain \TeX{} and can be used interchangeably. The \pkg{l3box} +% commands to construct boxes, such as \cs{hbox:n} or \cs{hbox_set:Nn}, +% are \enquote{color-safe}, meaning that +% \begin{verbatim} +% \hbox:n { \color_select:n { blue } Hello, } ~ world! +% \end{verbatim} +% will result in \enquote{Hello,} taking the color blue, but +% \enquote{world!} remaining with the prevailing color outside the box. +% +% \section{Creating and initialising boxes} +% +% \begin{function}{\box_new:N, \box_new:c} +% \begin{syntax} +% \cs{box_new:N} \meta{box} +% \end{syntax} +% Creates a new \meta{box} or raises an error if the name is +% already taken. The declaration is global. The \meta{box} is +% initially void. +% \end{function} +% +% \begin{function}{\box_clear:N, \box_clear:c, \box_gclear:N, \box_gclear:c} +% \begin{syntax} +% \cs{box_clear:N} \meta{box} +% \end{syntax} +% Clears the content of the \meta{box} by setting the box equal to +% \cs{c_empty_box}. +% \end{function} +% +% \begin{function} +% {\box_clear_new:N, \box_clear_new:c, \box_gclear_new:N, \box_gclear_new:c} +% \begin{syntax} +% \cs{box_clear_new:N} \meta{box} +% \end{syntax} +% Ensures that the \meta{box} exists globally by applying +% \cs{box_new:N} if necessary, then applies +% \cs[index=box_clear:N]{box_(g)clear:N} to leave +% the \meta{box} empty. +% \end{function} +% +% \begin{function} +% { +% \box_set_eq:NN, \box_set_eq:cN, \box_set_eq:Nc, \box_set_eq:cc, +% \box_gset_eq:NN, \box_gset_eq:cN, \box_gset_eq:Nc, \box_gset_eq:cc +% } +% \begin{syntax} +% \cs{box_set_eq:NN} \meta{box_1} \meta{box_2} +% \end{syntax} +% Sets the content of \meta{box_1} equal to that of \meta{box_2}. +% \end{function} +% +% \begin{function}[EXP, pTF, added=2012-03-03] +% {\box_if_exist:N, \box_if_exist:c} +% \begin{syntax} +% \cs{box_if_exist_p:N} \meta{box} +% \cs{box_if_exist:NTF} \meta{box} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests whether the \meta{box} is currently defined. This does not +% check that the \meta{box} really is a box. +% \end{function} +% +% \section{Using boxes} +% +% \begin{function}{\box_use:N, \box_use:c} +% \begin{syntax} +% \cs{box_use:N} \meta{box} +% \end{syntax} +% Inserts the current content of the \meta{box} onto the current +% list for typesetting. An error is raised if the variable does +% not exist or if it is invalid. +% \begin{texnote} +% This is the \TeX{} primitive \tn{copy}. +% \end{texnote} +% \end{function} +% +% \begin{function}{\box_move_right:nn, \box_move_left:nn} +% \begin{syntax} +% \cs{box_move_right:nn} \Arg{dim expr} \Arg{box function} +% \end{syntax} +% This function operates in vertical mode, and inserts the +% material specified by the \meta{box function} +% such that its reference point is displaced horizontally by the given +% \meta{dim expr} from the reference point for typesetting, to the right +% or left as appropriate. The \meta{box function} should be +% a box operation such as \cs{box_use:N} |\<box>| or a \enquote{raw} +% box specification such as \cs{vbox:n} |{ xyz }|. +% \end{function} +% +% \begin{function}{\box_move_up:nn, \box_move_down:nn} +% \begin{syntax} +% \cs{box_move_up:nn} \Arg{dim expr} \Arg{box function} +% \end{syntax} +% This function operates in horizontal mode, and inserts the +% material specified by the \meta{box function} +% such that its reference point is displaced vertically by the given +% \meta{dim expr} from the reference point for typesetting, up +% or down as appropriate. The \meta{box function} should be +% a box operation such as \cs{box_use:N} |\<box>| or a \enquote{raw} +% box specification such as \cs{vbox:n} |{ xyz }|. +% \end{function} +% +% \section{Measuring and setting box dimensions} +% +% \begin{function}{\box_dp:N, \box_dp:c} +% \begin{syntax} +% \cs{box_dp:N} \meta{box} +% \end{syntax} +% Calculates the depth (below the baseline) of the \meta{box} +% in a form suitable for use in a \meta{dim expr}. +% \begin{texnote} +% This is the \TeX{} primitive \tn{dp}. +% \end{texnote} +% \end{function} +% +% \begin{function}{\box_ht:N, \box_ht:c} +% \begin{syntax} +% \cs{box_ht:N} \meta{box} +% \end{syntax} +% Calculates the height (above the baseline) of the \meta{box} +% in a form suitable for use in a \meta{dim expr}. +% \begin{texnote} +% This is the \TeX{} primitive \tn{ht}. +% \end{texnote} +% \end{function} +% +% \begin{function}{\box_wd:N, \box_wd:c} +% \begin{syntax} +% \cs{box_wd:N} \meta{box} +% \end{syntax} +% Calculates the width of the \meta{box} in a form +% suitable for use in a \meta{dim expr}. +% \begin{texnote} +% This is the \TeX{} primitive \tn{wd}. +% \end{texnote} +% \end{function} +% +% \begin{function}[added = 2021-05-05]{\box_ht_plus_dp:N, \box_ht_plus_dp:c} +% \begin{syntax} +% \cs{box_ht_plus_dp:N} \meta{box} +% \end{syntax} +% Calculates the total vertical size (height plus depth) of the \meta{box} +% in a form suitable for use in a \meta{dim expr}. +% \end{function} +% +% \begin{function}[updated = 2019-01-22] +% { +% \box_set_dp:Nn, \box_set_dp:cn, +% \box_gset_dp:Nn, \box_gset_dp:cn +% } +% \begin{syntax} +% \cs{box_set_dp:Nn} \meta{box} \Arg{dim expr} +% \end{syntax} +% Set the depth (below the baseline) of the \meta{box} to the value of +% the \Arg{dim expr}. +% \end{function} +% +% \begin{function}[updated = 2019-01-22] +% { +% \box_set_ht:Nn, \box_set_ht:cn, +% \box_gset_ht:Nn, \box_gset_ht:cn +% } +% \begin{syntax} +% \cs{box_set_ht:Nn} \meta{box} \Arg{dim expr} +% \end{syntax} +% Set the height (above the baseline) of the \meta{box} to the value of +% the \Arg{dim expr}. +% \end{function} +% +% \begin{function}[updated = 2019-01-22] +% { +% \box_set_wd:Nn, \box_set_wd:cn, +% \box_gset_wd:Nn, \box_gset_wd:cn +% } +% \begin{syntax} +% \cs{box_set_wd:Nn} \meta{box} \Arg{dim expr} +% \end{syntax} +% Set the width of the \meta{box} to the value of the +% \Arg{dim expr}. +% \end{function} +% +% \section{Box conditionals} +% +% \begin{function}[EXP,pTF]{\box_if_empty:N, \box_if_empty:c} +% \begin{syntax} +% \cs{box_if_empty_p:N} \meta{box} +% \cs{box_if_empty:NTF} \meta{box} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if \meta{box} is a empty (equal to \cs{c_empty_box}). +% \end{function} +% +% \begin{function}[EXP,pTF]{\box_if_horizontal:N, \box_if_horizontal:c} +% \begin{syntax} +% \cs{box_if_horizontal_p:N} \meta{box} +% \cs{box_if_horizontal:NTF} \meta{box} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if \meta{box} is a horizontal box. +% \end{function} +% +% \begin{function}[EXP,pTF]{\box_if_vertical:N, \box_if_vertical:c} +% \begin{syntax} +% \cs{box_if_vertical_p:N} \meta{box} +% \cs{box_if_vertical:NTF} \meta{box} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if \meta{box} is a vertical box. +% \end{function} +% +% \section{The last box inserted} +% +% \begin{function} +% { +% \box_set_to_last:N, \box_set_to_last:c, +% \box_gset_to_last:N, \box_gset_to_last:c +% } +% \begin{syntax} +% \cs{box_set_to_last:N} \meta{box} +% \end{syntax} +% Sets the \meta{box} equal to the last item (box) added to the current +% partial list, removing the item from the list at the same time. When +% applied to the main vertical list, the \meta{box} is always void as +% it is not possible to recover the last added item. +% \end{function} +% +% \section{Constant boxes} +% +% \begin{variable}[updated = 2012-11-04]{\c_empty_box} +% This is a permanently empty box, which is neither set as horizontal +% nor vertical. +% \begin{texnote} +% At the \TeX{} level this is a void box. +% \end{texnote} +% \end{variable} +% +% \section{Scratch boxes} +% +% \begin{variable}[updated = 2012-11-04]{\l_tmpa_box, \l_tmpb_box} +% Scratch boxes for local assignment. These are never used by +% the kernel code, and so are safe for use with any \LaTeX3-defined +% function. However, they may be overwritten by other non-kernel +% code and so should only be used for short-term storage. +% \end{variable} +% +% \begin{variable}{\g_tmpa_box, \g_tmpb_box} +% Scratch boxes for global assignment. These are never used by +% the kernel code, and so are safe for use with any \LaTeX3-defined +% function. However, they may be overwritten by other non-kernel +% code and so should only be used for short-term storage. +% \end{variable} +% +% \section{Viewing box contents} +% +% \begin{function}[updated = 2012-05-11]{\box_show:N, \box_show:c} +% \begin{syntax} +% \cs{box_show:N} \meta{box} +% \end{syntax} +% Shows full details of the content of the \meta{box} in the terminal. +% \end{function} +% +% \begin{function}[added = 2012-05-11]{\box_show:Nnn, \box_show:cnn} +% \begin{syntax} +% \cs{box_show:Nnn} \meta{box} \Arg{int expr_1} \Arg{int expr_2} +% \end{syntax} +% Display the contents of \meta{box} in the terminal, showing the first +% \meta{int expr_1} items of the box, and descending into \meta{int expr_2} +% group levels. +% \end{function} +% +% \begin{function}[added = 2012-05-11]{\box_log:N, \box_log:c} +% \begin{syntax} +% \cs{box_log:N} \meta{box} +% \end{syntax} +% Writes full details of the content of the \meta{box} to the log. +% \end{function} +% +% \begin{function}[added = 2012-05-11]{\box_log:Nnn, \box_log:cnn} +% \begin{syntax} +% \cs{box_log:Nnn} \meta{box} \Arg{int expr_1} \Arg{int expr_2} +% \end{syntax} +% Writes the contents of \meta{box} to the log, showing the first +% \meta{int expr_1} items of the box, and descending into \meta{int expr_2} +% group levels. +% \end{function} +% +% \section{Boxes and color} +% +% All \LaTeX{}3 boxes are \enquote{color safe}: a color set inside the box +% stops applying after the end of the box has occurred. +% +% \section{Horizontal mode boxes} +% +% \begin{function}[updated = 2017-04-05]{\hbox:n} +% \begin{syntax} +% \cs{hbox:n} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} into a horizontal box of natural +% width and then includes this box in the current list for typesetting. +% \end{function} +% +% \begin{function}[updated = 2017-04-05]{\hbox_to_wd:nn} +% \begin{syntax} +% \cs{hbox_to_wd:nn} \Arg{dim expr} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} into a horizontal box of width +% \meta{dim expr} and then includes this box in the current list for +% typesetting. +% \end{function} +% +% \begin{function}[updated = 2017-04-05]{\hbox_to_zero:n} +% \begin{syntax} +% \cs{hbox_to_zero:n} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} into a horizontal box of zero width +% and then includes this box in the current list for typesetting. +% \end{function} +% +% \begin{function}[updated = 2017-04-05] +% {\hbox_set:Nn, \hbox_set:cn, \hbox_gset:Nn, \hbox_gset:cn} +% \begin{syntax} +% \cs{hbox_set:Nn} \meta{box} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} at natural width and then stores the +% result inside the \meta{box}. +% \end{function} +% +% \begin{function}[updated = 2017-04-05] +% { +% \hbox_set_to_wd:Nnn, \hbox_set_to_wd:cnn, +% \hbox_gset_to_wd:Nnn, \hbox_gset_to_wd:cnn +% } +% \begin{syntax} +% \cs{hbox_set_to_wd:Nnn} \meta{box} \Arg{dim expr} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} to the width given by the \meta{dim expr} +% and then stores the result inside the \meta{box}. +% \end{function} +% +% \begin{function}[added = 2020-08-25]{\hbox_overlap_center:n} +% \begin{syntax} +% \cs{hbox_overlap_center:n} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} into a horizontal box of zero width +% such that material protrudes equally to both sides of the insertion point. +% \end{function} +% +% \begin{function}[updated = 2017-04-05]{\hbox_overlap_right:n} +% \begin{syntax} +% \cs{hbox_overlap_right:n} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} into a horizontal box of zero width +% such that material protrudes to the right of the insertion point. +% \end{function} +% +% \begin{function}[updated = 2017-04-05]{\hbox_overlap_left:n} +% \begin{syntax} +% \cs{hbox_overlap_left:n} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} into a horizontal box of zero width +% such that material protrudes to the left of the insertion point. +% \end{function} +% +% \begin{function}[updated = 2017-04-05] +% { +% \hbox_set:Nw, \hbox_set:cw, +% \hbox_set_end:, +% \hbox_gset:Nw, \hbox_gset:cw, +% \hbox_gset_end: +% } +% \begin{syntax} +% \cs{hbox_set:Nw} \meta{box} \meta{contents} \cs{hbox_set_end:} +% \end{syntax} +% Typesets the \meta{contents} at natural width and then stores the +% result inside the \meta{box}. In contrast +% to \cs{hbox_set:Nn} this function does not absorb the argument +% when finding the \meta{content}, and so can be used in circumstances +% where the \meta{content} may not be a simple argument. +% \end{function} +% +% \begin{function}[added = 2017-06-08] +% { +% \hbox_set_to_wd:Nnw, \hbox_set_to_wd:cnw, +% \hbox_gset_to_wd:Nnw, \hbox_gset_to_wd:cnw +% } +% \begin{syntax} +% \cs{hbox_set_to_wd:Nnw} \meta{box} \Arg{dim expr} \meta{contents} \cs{hbox_set_end:} +% \end{syntax} +% Typesets the \meta{contents} to the width given by the \meta{dim expr} +% and then stores the result inside the \meta{box}. In contrast +% to \cs{hbox_set_to_wd:Nnn} this function does not absorb the argument +% when finding the \meta{content}, and so can be used in circumstances +% where the \meta{content} may not be a simple argument +% \end{function} +% +% \begin{function}{\hbox_unpack:N, \hbox_unpack:c} +% \begin{syntax} +% \cs{hbox_unpack:N} \meta{box} +% \end{syntax} +% Unpacks the content of the horizontal \meta{box}, retaining any stretching +% or shrinking applied when the \meta{box} was set. +% \begin{texnote} +% This is the \TeX{} primitive \tn{unhcopy}. +% \end{texnote} +% \end{function} +% +% \section{Vertical mode boxes} +% +% Vertical boxes inherit their baseline from their contents. The +% standard case is that the baseline of the box is at the same position +% as that of the last item added to the box. This means that the box +% has no depth unless the last item added to it had depth. As a +% result most vertical boxes have a large height value and small or +% zero depth. The exception are |_top| boxes, where the reference point +% is that of the first item added. These tend to have a large depth and +% small height, although the latter is typically non-zero. +% +% \begin{function}[updated = 2017-04-05]{\vbox:n} +% \begin{syntax} +% \cs{vbox:n} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} into a vertical box of natural height +% and includes this box in the current list for typesetting. +% \end{function} +% +% \begin{function}[updated = 2017-04-05]{\vbox_top:n} +% \begin{syntax} +% \cs{vbox_top:n} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} into a vertical box of natural height +% and includes this box in the current list for typesetting. The +% baseline of the box is equal to that of the \emph{first} +% item added to the box. +% \end{function} +% +% \begin{function}[updated = 2017-04-05]{\vbox_to_ht:nn} +% \begin{syntax} +% \cs{vbox_to_ht:nn} \Arg{dim expr} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} into a vertical box of height +% \meta{dim expr} and then includes this box in the current list for +% typesetting. +% \end{function} +% +% \begin{function}[updated = 2017-04-05]{\vbox_to_zero:n} +% \begin{syntax} +% \cs{vbox_to_zero:n} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} into a vertical box of zero height +% and then includes this box in the current list for typesetting. +% \end{function} +% +% \begin{function}[updated = 2017-04-05] +% {\vbox_set:Nn, \vbox_set:cn, \vbox_gset:Nn, \vbox_gset:cn} +% \begin{syntax} +% \cs{vbox_set:Nn} \meta{box} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} at natural height and then stores the +% result inside the \meta{box}. +% \end{function} +% +% \begin{function}[updated = 2017-04-05] +% {\vbox_set_top:Nn, \vbox_set_top:cn, \vbox_gset_top:Nn, \vbox_gset_top:cn} +% \begin{syntax} +% \cs{vbox_set_top:Nn} \meta{box} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} at natural height and then stores the +% result inside the \meta{box}. The baseline of the box is equal +% to that of the \emph{first} item added to the box. +% \end{function} +% +% \begin{function}[updated = 2017-04-05] +% { +% \vbox_set_to_ht:Nnn, \vbox_set_to_ht:cnn, +% \vbox_gset_to_ht:Nnn, \vbox_gset_to_ht:cnn +% } +% \begin{syntax} +% \cs{vbox_set_to_ht:Nnn} \meta{box} \Arg{dim expr} \Arg{contents} +% \end{syntax} +% Typesets the \meta{contents} to the height given by the +% \meta{dim expr} and then stores the result inside the \meta{box}. +% \end{function} +% +% \begin{function}[updated = 2017-04-05] +% { +% \vbox_set:Nw, \vbox_set:cw, +% \vbox_set_end:, +% \vbox_gset:Nw, \vbox_gset:cw, +% \vbox_gset_end: +% } +% \begin{syntax} +% \cs{vbox_set:Nw} \meta{box} \meta{contents} \cs{vbox_set_end:} +% \end{syntax} +% Typesets the \meta{contents} at natural height and then stores the +% result inside the \meta{box}. In contrast +% to \cs{vbox_set:Nn} this function does not absorb the argument +% when finding the \meta{content}, and so can be used in circumstances +% where the \meta{content} may not be a simple argument. +% \end{function} +% +% \begin{function}[added = 2017-06-08] +% { +% \vbox_set_to_ht:Nnw, \vbox_set_to_ht:cnw, +% \vbox_gset_to_ht:Nnw, \vbox_gset_to_ht:cnw +% } +% \begin{syntax} +% \cs{vbox_set_to_ht:Nnw} \meta{box} \Arg{dim expr} \meta{contents} \cs{vbox_set_end:} +% \end{syntax} +% Typesets the \meta{contents} to the height given by the \meta{dim expr} +% and then stores the result inside the \meta{box}. In contrast +% to \cs{vbox_set_to_ht:Nnn} this function does not absorb the argument +% when finding the \meta{content}, and so can be used in circumstances +% where the \meta{content} may not be a simple argument +% \end{function} +% +% +% \begin{function}[updated = 2018-12-29] +% { +% \vbox_set_split_to_ht:NNn, \vbox_set_split_to_ht:cNn, +% \vbox_set_split_to_ht:Ncn, \vbox_set_split_to_ht:ccn, +% \vbox_gset_split_to_ht:NNn, \vbox_gset_split_to_ht:cNn, +% \vbox_gset_split_to_ht:Ncn, \vbox_gset_split_to_ht:ccn +% } +% \begin{syntax} +% \cs{vbox_set_split_to_ht:NNn} \meta{box_1} \meta{box_2} \Arg{dim expr} +% \end{syntax} +% Sets \meta{box_1} to contain material to the height given by the +% \meta{dim expr} by removing content from the top of \meta{box_2} +% (which must be a vertical box). +% \end{function} +% +% \begin{function}{\vbox_unpack:N, \vbox_unpack:c} +% \begin{syntax} +% \cs{vbox_unpack:N} \meta{box} +% \end{syntax} +% Unpacks the content of the vertical \meta{box}, retaining any stretching +% or shrinking applied when the \meta{box} was set. +% \begin{texnote} +% This is the \TeX{} primitive \tn{unvcopy}. +% \end{texnote} +% \end{function} +% +% \section{Using boxes efficiently} +% +% The functions above for using box contents work in exactly the same +% way as for any other \pkg{expl3} variable. However, for efficiency +% reasons, it is also useful to have functions which \emph{drop} box +% contents on use. When a box is dropped, the box becomes empty at the group +% level \emph{where the box was originally set} rather than necessarily +% \emph{at the current group level}. For example, with +% \begin{verbatim} +% \hbox_set:Nn \l_tmpa_box { A } +% \group_begin: +% \hbox_set:Nn \l_tmpa_box { B } +% \group_begin: +% \box_use_drop:N \l_tmpa_box +% \group_end: +% \box_show:N \l_tmpa_box +% \group_end: +% \box_show:N \l_tmpa_box +% \end{verbatim} +% the first use of \cs{box_show:N} will show an entirely cleared (void) box, and the +% second will show the letter |A| in the box. +% +% These functions should be preferred when the content of the box is no +% longer required after use. Note that due to the unusual scoping behaviour of +% \texttt{drop} functions they may be applied to both local and global boxes: +% the latter will naturally be set and thus cleared at a global level. +% +% \begin{function}{\box_use_drop:N, \box_use_drop:c} +% \begin{syntax} +% \cs{box_use_drop:N} \meta{box} +% \end{syntax} +% Inserts the current content of the \meta{box} onto the current +% list for typesetting then drops the box content. An error is raised if the +% variable does not exist or if it is invalid. This function may be applied to +% local or global boxes. +% \begin{texnote} +% This is the \TeX{} primitive \tn{box}. +% \end{texnote} +% \end{function} +% +% \begin{function}[added = 2019-01-17] +% { +% \box_set_eq_drop:NN, \box_set_eq_drop:cN, +% \box_set_eq_drop:Nc, \box_set_eq_drop:cc +% } +% \begin{syntax} +% \cs{box_set_eq_drop:NN} \meta{box_1} \meta{box_2} +% \end{syntax} +% Sets the content of \meta{box_1} equal to that of \meta{box_2}, then +% drops \meta{box_2}. +% \end{function} +% +% \begin{function}[added = 2019-01-17] +% { +% \box_gset_eq_drop:NN, \box_gset_eq_drop:cN, +% \box_gset_eq_drop:Nc, \box_gset_eq_drop:cc +% } +% \begin{syntax} +% \cs{box_gset_eq_drop:NN} \meta{box_1} \meta{box_2} +% \end{syntax} +% Sets the content of \meta{box_1} globally equal to that of \meta{box_2}, +% then drops \meta{box_2}. +% \end{function} +% +% \begin{function}[added = 2019-01-17] +% {\hbox_unpack_drop:N, \hbox_unpack_drop:c} +% \begin{syntax} +% \cs{hbox_unpack_drop:N} \meta{box} +% \end{syntax} +% Unpacks the content of the horizontal \meta{box}, retaining any stretching +% or shrinking applied when the \meta{box} was set. The original \meta{box} +% is then dropped. +% \begin{texnote} +% This is the \TeX{} primitive \tn{unhbox}. +% \end{texnote} +% \end{function} +% +% \begin{function}[added = 2019-01-17] +% {\vbox_unpack_drop:N, \vbox_unpack_drop:c} +% \begin{syntax} +% \cs{vbox_unpack_drop:N} \meta{box} +% \end{syntax} +% Unpacks the content of the vertical \meta{box}, retaining any stretching +% or shrinking applied when the \meta{box} was set. The original \meta{box} +% is then dropped. +% \begin{texnote} +% This is the \TeX{} primitive \tn{unvbox}. +% \end{texnote} +% \end{function} +% +% \section{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}[added = 2017-04-04, updated = 2019-01-22] +% { +% \box_autosize_to_wd_and_ht:Nnn, \box_autosize_to_wd_and_ht:cnn, +% \box_gautosize_to_wd_and_ht:Nnn, \box_gautosize_to_wd_and_ht:cnn, +% } +% \begin{syntax} +% \cs{box_autosize_to_wd_and_ht:Nnn} \meta{box} \Arg{x-size} \Arg{y-size} +% \end{syntax} +% Resizes the \meta{box} to fit within the given \meta{x-size} (horizontally) +% and \meta{y-size} (vertically); both of the sizes are dimension +% expressions. The \meta{y-size} is the height only: it does not include any +% depth. The updated \meta{box} is an |hbox|, irrespective of the nature +% of the \meta{box} before the resizing is applied. The final size of the +% \meta{box} is the smaller of \Arg{x-size} and \Arg{y-size}, +% \emph{i.e.}~the result fits within the dimensions specified. Negative +% sizes cause the material in the \meta{box} to be reversed in direction, +% but the reference point of the \meta{box} is unchanged. Thus a negative +% \meta{y-size} results in the \meta{box} having a depth dependent on the +% height of the original and \emph{vice versa}. +% \end{function} +% +% \begin{function}[added = 2017-04-04, updated = 2019-01-22] +% { +% \box_autosize_to_wd_and_ht_plus_dp:Nnn, +% \box_autosize_to_wd_and_ht_plus_dp:cnn, +% \box_gautosize_to_wd_and_ht_plus_dp:Nnn, +% \box_gautosize_to_wd_and_ht_plus_dp:cnn +% } +% \begin{syntax} +% \cs{box_autosize_to_wd_and_ht_plus_dp:Nnn} \meta{box} \Arg{x-size} \Arg{y-size} +% \end{syntax} +% Resizes the \meta{box} to fit within the given \meta{x-size} (horizontally) +% and \meta{y-size} (vertically); both of the sizes are dimension +% expressions. The \meta{y-size} is the total vertical size (height plus +% depth). The updated \meta{box} is an |hbox|, irrespective of the nature +% of the \meta{box} before the resizing is applied. The final size of the +% \meta{box} is the smaller of \Arg{x-size} and \Arg{y-size}, +% \emph{i.e.}~the result fits within the dimensions specified. Negative +% sizes cause the material in the \meta{box} to be reversed in direction, +% but the reference point of the \meta{box} is unchanged. Thus a negative +% \meta{y-size} results in the \meta{box} having a depth dependent on the +% height of the original and \emph{vice versa}. +% \end{function} +% +% \begin{function}[updated = 2019-01-22] +% { +% \box_resize_to_ht:Nn, \box_resize_to_ht:cn, +% \box_gresize_to_ht:Nn, \box_gresize_to_ht:cn +% } +% \begin{syntax} +% \cs{box_resize_to_ht:Nn} \meta{box} \Arg{y-size} +% \end{syntax} +% Resizes 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: it does not include any depth. The updated +% \meta{box} is an |hbox|, irrespective of the nature of the \meta{box} +% before the resizing is applied. A negative \meta{y-size} causes the +% material in the \meta{box} to be reversed in direction, but the reference +% point of the \meta{box} is unchanged. Thus a negative \meta{y-size} +% results in the \meta{box} having a depth dependent on the height of the +% original and \emph{vice versa}. +% \end{function} +% +% \begin{function}[updated = 2019-01-22] +% { +% \box_resize_to_ht_plus_dp:Nn, \box_resize_to_ht_plus_dp:cn, +% \box_gresize_to_ht_plus_dp:Nn, \box_gresize_to_ht_plus_dp:cn, +% } +% \begin{syntax} +% \cs{box_resize_to_ht_plus_dp:Nn} \meta{box} \Arg{y-size} +% \end{syntax} +% Resizes 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 total vertical size (height plus depth). The updated +% \meta{box} is an |hbox|, irrespective of the nature of the \meta{box} +% before the resizing is applied. A negative \meta{y-size} causes +% the material in the \meta{box} to be reversed in direction, but the +% reference point of the \meta{box} is unchanged. Thus a negative +% \meta{y-size} results in the \meta{box} having a depth dependent on the +% height of the original and \emph{vice versa}. +% \end{function} +% +% \begin{function}[updated = 2019-01-22] +% { +% \box_resize_to_wd:Nn, \box_resize_to_wd:cn, +% \box_gresize_to_wd:Nn, \box_gresize_to_wd:cn +% } +% \begin{syntax} +% \cs{box_resize_to_wd:Nn} \meta{box} \Arg{x-size} +% \end{syntax} +% Resizes 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} is an |hbox|, irrespective of the nature of the \meta{box} +% before the resizing is applied. A negative \meta{x-size} causes the +% material in the \meta{box} to be reversed in direction, but the reference +% point of the \meta{box} is unchanged. Thus a negative \meta{x-size} +% results in the \meta{box} having a depth dependent on the height of the +% original and \emph{vice versa}. +% \end{function} +% +% \begin{function}[added = 2014-07-03, updated = 2019-01-22] +% { +% \box_resize_to_wd_and_ht:Nnn, \box_resize_to_wd_and_ht:cnn, +% \box_gresize_to_wd_and_ht:Nnn, \box_gresize_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} +% Resizes 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 height only and does not include any depth. The updated +% \meta{box} is an |hbox|, irrespective of the nature of the \meta{box} +% before the resizing is applied. Negative sizes cause the material in +% the \meta{box} to be reversed in direction, but the reference point of the +% \meta{box} is unchanged. Thus a negative \meta{y-size} results in +% the \meta{box} having a depth dependent on the height of the original and +% \emph{vice versa}. +% \end{function} +% +% \begin{function}[added = 2017-04-06, updated = 2019-01-22] +% { +% \box_resize_to_wd_and_ht_plus_dp:Nnn, +% \box_resize_to_wd_and_ht_plus_dp:cnn, +% \box_gresize_to_wd_and_ht_plus_dp:Nnn, +% \box_gresize_to_wd_and_ht_plus_dp:cnn +% } +% \begin{syntax} +% \cs{box_resize_to_wd_and_ht_plus_dp:Nnn} \meta{box} \Arg{x-size} \Arg{y-size} +% \end{syntax} +% Resizes 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 total vertical size (height plus depth). The updated +% \meta{box} is an |hbox|, irrespective of the nature of the \meta{box} +% before the resizing is applied. Negative sizes cause the material in +% the \meta{box} to be reversed in direction, but the reference point of the +% \meta{box} is unchanged. Thus a negative \meta{y-size} results in +% the \meta{box} having a depth dependent on the height of the original and +% \emph{vice versa}. +% \end{function} +% +% \begin{function}[updated = 2019-01-22] +% { +% \box_rotate:Nn, \box_rotate:cn, +% \box_grotate:Nn, \box_grotate:cn +% } +% \begin{syntax} +% \cs{box_rotate:Nn} \meta{box} \Arg{angle} +% \end{syntax} +% Rotates the \meta{box} by \meta{angle} (a \meta{fp expr} in degrees) +% anti-clockwise about +% its reference point. The reference point of the updated box is moved +% horizontally such that it is at the left side of the smallest rectangle +% enclosing the rotated material. The updated \meta{box} is an |hbox|, +% irrespective of the nature of the \meta{box} before the rotation is applied. +% \end{function} +% +% \begin{function}[updated = 2019-01-22] +% { +% \box_scale:Nnn, \box_scale:cnn, +% \box_gscale:Nnn, \box_gscale: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 +% \meta{fp expr}). The updated \meta{box} is an |hbox|, irrespective +% of the nature of the \meta{box} before the scaling is applied. Negative +% scalings cause the material in the \meta{box} to be reversed in +% direction, but the reference point of the \meta{box} is unchanged. +% Thus a negative \meta{y-scale} results in the \meta{box} having a depth +% dependent on the height of the original and \emph{vice versa}. +% \end{function} +% +% \section{Viewing part of a box} +% +% \begin{function}[updated = 2023-04-14] +% { +% \box_set_clipped:N, \box_set_clipped:c, +% \box_gset_clipped:N, \box_gset_clipped:c +% } +% \begin{syntax} +% \cs{box_set_clipped: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} is an +% hbox, irrespective of the nature of the \meta{box} before the clipping is +% applied. Additional box levels are also generated by this operation. +% +% \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}[added = 2019-01-23] +% { +% \box_set_trim:Nnnnn, \box_set_trim:cnnnn, +% \box_gset_trim:Nnnnn, \box_gset_trim:cnnnn +% } +% \begin{syntax} +% \cs{box_set_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{dim exprs}. +% Material outside of the bounding box is still displayed in the output +% unless \cs{box_set_clipped:N} is subsequently applied. +% The updated \meta{box} is an +% hbox, irrespective of the nature of the \meta{box} before the trim +% operation is applied. +% Additional box levels are also generated by this operation. +% The behavior of the operation where the trims requested is +% greater than the size of the box is undefined. +% \end{function} +% +% \begin{function}[added = 2019-01-23] +% { +% \box_set_viewport:Nnnnn, \box_set_viewport:cnnnn, +% \box_gset_viewport:Nnnnn, \box_gset_viewport:cnnnn +% } +% \begin{syntax} +% \cs{box_set_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{dim exprs}. Material outside of the bounding box is +% still displayed in the output unless \cs{box_set_clipped:N} is +% subsequently applied. +% The updated \meta{box} is an +% hbox, irrespective of the nature of the \meta{box} before the viewport +% operation is applied. +% Additional box levels are also generated by this operation. +% \end{function} +% +% \section{Primitive box conditionals} +% +% \begin{function}[EXP]{\if_hbox:N} +% \begin{syntax} +% \cs{if_hbox:N} \meta{box} +% ~~\meta{true code} +% \cs{else:} +% ~~\meta{false code} +% \cs{fi:} +% \end{syntax} +% Tests is \meta{box} is a horizontal box. +% \begin{texnote} +% This is the \TeX{} primitive \tn{ifhbox}. +% \end{texnote} +% \end{function} +% +% \begin{function}[EXP]{\if_vbox:N} +% \begin{syntax} +% \cs{if_vbox:N} \meta{box} +% ~~\meta{true code} +% \cs{else:} +% ~~\meta{false code} +% \cs{fi:} +% \end{syntax} +% Tests is \meta{box} is a vertical box. +% \begin{texnote} +% This is the \TeX{} primitive \tn{ifvbox}. +% \end{texnote} +% \end{function} +% +% \begin{function}[EXP]{\if_box_empty:N} +% \begin{syntax} +% \cs{if_box_empty:N} \meta{box} +% ~~\meta{true code} +% \cs{else:} +% ~~\meta{false code} +% \cs{fi:} +% \end{syntax} +% Tests is \meta{box} is an empty (void) box. +% \begin{texnote} +% This is the \TeX{} primitive \tn{ifvoid}. +% \end{texnote} +% \end{function} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{l3box} implementation} +% +% \begin{macrocode} +%<*package> +% \end{macrocode} +% +% \begin{macrocode} +%<@@=box> +% \end{macrocode} +% +% \subsection{Support code} +% +% \begin{macro}{\@@_dim_eval:w} +% \begin{macro}{\@@_dim_eval:n} +% Evaluating a dimension expression expandably. The only +% difference with \cs{dim_eval:n} is the lack of \cs{dim_use:N}, to +% produce an internal dimension rather than expand it into characters. +% \begin{macrocode} +\cs_new_eq:NN \@@_dim_eval:w \tex_dimexpr:D +\cs_new:Npn \@@_dim_eval:n #1 + { \@@_dim_eval:w #1 \scan_stop: } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\__kernel_kern:n} +% We need kerns in a few places. At present, we don't have a module for +% this concept, so it goes in at first use: here. The idea is to avoid +% repeated use of the bare primitive. +% \begin{macrocode} +\cs_new_protected:Npn \__kernel_kern:n #1 + { \tex_kern:D \@@_dim_eval:n {#1} } +% \end{macrocode} +% \end{macro} +% +% \subsection{Creating and initialising boxes} +% +% \TestFiles{m3box001.lvt} +% +% \begin{macro}{\box_new:N, \box_new:c} +% Defining a new \meta{box} register: remember that box $255$ is not +% generally available. +% \begin{macrocode} +\cs_new_protected:Npn \box_new:N #1 + { + \__kernel_chk_if_free_cs:N #1 + \cs:w newbox \cs_end: #1 + } +\cs_generate_variant:Nn \box_new:N { c } +% \end{macrocode} +% +% \begin{macro}{\box_clear:N, \box_clear:c} +% \begin{macro}{\box_gclear:N, \box_gclear:c} +% \testfile* +% Clear a \meta{box} register. +% \begin{macrocode} +\cs_new_protected:Npn \box_clear:N #1 + { \box_set_eq:NN #1 \c_empty_box } +\cs_new_protected:Npn \box_gclear:N #1 + { \box_gset_eq:NN #1 \c_empty_box } +\cs_generate_variant:Nn \box_clear:N { c } +\cs_generate_variant:Nn \box_gclear:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\box_clear_new:N, \box_clear_new:c} +% \begin{macro}{\box_gclear_new:N, \box_gclear_new:c} +% \testfile* +% Clear or new. +% \begin{macrocode} +\cs_new_protected:Npn \box_clear_new:N #1 + { \box_if_exist:NTF #1 { \box_clear:N #1 } { \box_new:N #1 } } +\cs_new_protected:Npn \box_gclear_new:N #1 + { \box_if_exist:NTF #1 { \box_gclear:N #1 } { \box_new:N #1 } } +\cs_generate_variant:Nn \box_clear_new:N { c } +\cs_generate_variant:Nn \box_gclear_new:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% {\box_set_eq:NN, \box_set_eq:cN, \box_set_eq:Nc, \box_set_eq:cc} +% \testfile* +% \begin{macro} +% {\box_gset_eq:NN, \box_gset_eq:cN, \box_gset_eq:Nc, \box_gset_eq:cc} +% \testfile* +% Assigning the contents of a box to be another box. +% \begin{macrocode} +\cs_new_protected:Npn \box_set_eq:NN #1#2 + { \tex_setbox:D #1 \tex_copy:D #2 } +\cs_new_protected:Npn \box_gset_eq:NN #1#2 + { \tex_global:D \tex_setbox:D #1 \tex_copy:D #2 } +\cs_generate_variant:Nn \box_set_eq:NN { c , Nc , cc } +\cs_generate_variant:Nn \box_gset_eq:NN { c , Nc , cc } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \box_set_eq_drop:NN, \box_set_eq_drop:cN, +% \box_set_eq_drop:Nc, \box_set_eq_drop:cc +% } +% \begin{macro} +% { +% \box_gset_eq_drop:NN, \box_gset_eq_drop:cN, +% \box_gset_eq_drop:Nc, \box_gset_eq_drop:cc +% } +% Assigning the contents of a box to be another box, then drops the +% original box. +% \begin{macrocode} +\cs_new_protected:Npn \box_set_eq_drop:NN #1#2 + { \tex_setbox:D #1 \tex_box:D #2 } +\cs_new_protected:Npn \box_gset_eq_drop:NN #1#2 + { \tex_global:D \tex_setbox:D #1 \tex_box:D #2 } +\cs_generate_variant:Nn \box_set_eq_drop:NN { c , Nc , cc } +\cs_generate_variant:Nn \box_gset_eq_drop:NN { c , Nc , cc } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[pTF]{\box_if_exist:N, \box_if_exist:c} +% Copies of the \texttt{cs} functions defined in \pkg{l3basics}. +% \begin{macrocode} +\prg_new_eq_conditional:NNn \box_if_exist:N \cs_if_exist:N + { TF , T , F , p } +\prg_new_eq_conditional:NNn \box_if_exist:c \cs_if_exist:c + { TF , T , F , p } +% \end{macrocode} +% \end{macro} +% +% \subsection{Measuring and setting box dimensions} +% +% \begin{macro}{\box_ht:N, \box_ht:c} +% \begin{macro}{\box_dp:N, \box_dp:c} +% \begin{macro}{\box_wd:N, \box_wd:c} +% \testfile* +% Accessing the height, depth, and width of a \meta{box} register. +% \begin{macrocode} +\cs_new_eq:NN \box_ht:N \tex_ht:D +\cs_new_eq:NN \box_dp:N \tex_dp:D +\cs_new_eq:NN \box_wd:N \tex_wd:D +\cs_generate_variant:Nn \box_ht:N { c } +\cs_generate_variant:Nn \box_dp:N { c } +\cs_generate_variant:Nn \box_wd:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\box_ht_plus_dp:N} +% The \cs{box_ht:N} and \cs{box_dp:N} primitives do not expand but +% rather are suitable for use after \tn{the} or inside dimension +% expressions. Here we obtain the same behaviour by using +% \cs{@@_dim_eval:n} (basically \tn{dimexpr}) rather than +% \cs{dim_eval:n} (basically \tn{the} \tn{dimexpr}). +% \begin{macrocode} +\cs_new_protected:Npn \box_ht_plus_dp:N #1 + { \@@_dim_eval:n { \box_ht:N #1 + \box_dp:N #1 } } +\cs_generate_variant:Nn \box_ht_plus_dp:N { c } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\box_set_ht:Nn, \box_set_ht:cn, \box_gset_ht:Nn, \box_gset_ht:cn} +% \begin{macro}{\box_set_dp:Nn, \box_set_dp:cn, \box_gset_dp:Nn, \box_gset_dp:cn} +% \begin{macro}{\box_set_wd:Nn, \box_set_wd:cn, \box_gset_wd:Nn, \box_gset_wd:cn} +% Setting the size whilst respecting local scope requires copying; +% the same issue does not come up when working globally. +% When debugging, the dimension expression |#2| is surrounded by +% parentheses to catch early termination. +% \begin{macrocode} +\cs_new_protected:Npn \box_set_dp:Nn #1#2 + { + \tex_setbox:D #1 = \tex_copy:D #1 + \box_dp:N #1 \@@_dim_eval:n {#2} + } +\cs_generate_variant:Nn \box_set_dp:Nn { c } +\cs_new_protected:Npn \box_gset_dp:Nn #1#2 + { \box_dp:N #1 \@@_dim_eval:n {#2} } +\cs_generate_variant:Nn \box_gset_dp:Nn { c } +\cs_new_protected:Npn \box_set_ht:Nn #1#2 + { + \tex_setbox:D #1 = \tex_copy:D #1 + \box_ht:N #1 \@@_dim_eval:n {#2} + } +\cs_generate_variant:Nn \box_set_ht:Nn { c } +\cs_new_protected:Npn \box_gset_ht:Nn #1#2 + { \box_ht:N #1 \@@_dim_eval:n {#2} } +\cs_generate_variant:Nn \box_gset_ht:Nn { c } +\cs_new_protected:Npn \box_set_wd:Nn #1#2 + { + \tex_setbox:D #1 = \tex_copy:D #1 + \box_wd:N #1 \@@_dim_eval:n {#2} + } +\cs_generate_variant:Nn \box_set_wd:Nn { c } +\cs_new_protected:Npn \box_gset_wd:Nn #1#2 + { \box_wd:N #1 \@@_dim_eval:n {#2} } +\cs_generate_variant:Nn \box_gset_wd:Nn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Using boxes} +% +% \begin{macro}{\box_use_drop:N, \box_use_drop:c} +% \begin{macro}{\box_use:N, \box_use:c} +% Using a \meta{box}. These are just \TeX{} primitives with meaningful +% names. +% \begin{macrocode} +\cs_new_eq:NN \box_use_drop:N \tex_box:D +\cs_new_eq:NN \box_use:N \tex_copy:D +\cs_generate_variant:Nn \box_use_drop:N { c } +\cs_generate_variant:Nn \box_use:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\box_move_left:nn, \box_move_right:nn} +% \begin{macro}{\box_move_up:nn, \box_move_down:nn} +% \testfile* +% Move box material in different directions. +% When debugging, the dimension expression |#1| is surrounded by +% parentheses to catch early termination. +% \begin{macrocode} +\cs_new_protected:Npn \box_move_left:nn #1#2 + { \tex_moveleft:D \@@_dim_eval:n {#1} #2 } +\cs_new_protected:Npn \box_move_right:nn #1#2 + { \tex_moveright:D \@@_dim_eval:n {#1} #2 } +\cs_new_protected:Npn \box_move_up:nn #1#2 + { \tex_raise:D \@@_dim_eval:n {#1} #2 } +\cs_new_protected:Npn \box_move_down:nn #1#2 + { \tex_lower:D \@@_dim_eval:n {#1} #2 } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Box conditionals} +% +% \begin{macro}{\if_hbox:N} +% \begin{macro}{\if_vbox:N} +% \begin{macro}{\if_box_empty:N} +% \testfile* +% The primitives for testing if a \meta{box} is empty/void or which +% type of box it is. +% \begin{macrocode} +\cs_new_eq:NN \if_hbox:N \tex_ifhbox:D +\cs_new_eq:NN \if_vbox:N \tex_ifvbox:D +\cs_new_eq:NN \if_box_empty:N \tex_ifvoid:D +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[pTF]{\box_if_horizontal:N, \box_if_horizontal:c} +% \testfile* +% \begin{macro}[pTF]{\box_if_vertical:N, \box_if_vertical:c} +% \testfile* +% \begin{macrocode} +\prg_new_conditional:Npnn \box_if_horizontal:N #1 { p , T , F , TF } + { \if_hbox:N #1 \prg_return_true: \else: \prg_return_false: \fi: } +\prg_new_conditional:Npnn \box_if_vertical:N #1 { p , T , F , TF } + { \if_vbox:N #1 \prg_return_true: \else: \prg_return_false: \fi: } +\prg_generate_conditional_variant:Nnn \box_if_horizontal:N + { c } { p , T , F , TF } +\prg_generate_conditional_variant:Nnn \box_if_vertical:N + { c } { p , T , F , TF } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[pTF]{\box_if_empty:N, \box_if_empty:c} +% \testfile* +% Testing if a \meta{box} is empty/void. +% \begin{macrocode} +\prg_new_conditional:Npnn \box_if_empty:N #1 { p , T , F , TF } + { \if_box_empty:N #1 \prg_return_true: \else: \prg_return_false: \fi: } +\prg_generate_conditional_variant:Nnn \box_if_empty:N + { c } { p , T , F , TF } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{The last box inserted} +% +% \begin{macro}{\box_set_to_last:N, \box_set_to_last:c} +% \begin{macro}{\box_gset_to_last:N, \box_gset_to_last:c} +% \testfile* +% Set a box to the previous box. +% \begin{macrocode} +\cs_new_protected:Npn \box_set_to_last:N #1 + { \tex_setbox:D #1 \tex_lastbox:D } +\cs_new_protected:Npn \box_gset_to_last:N #1 + { \tex_global:D \tex_setbox:D #1 \tex_lastbox:D } +\cs_generate_variant:Nn \box_set_to_last:N { c } +\cs_generate_variant:Nn \box_gset_to_last:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Constant boxes} +% +% \begin{variable}{\c_empty_box} +% A box we never use. +% \begin{macrocode} +\box_new:N \c_empty_box +% \end{macrocode} +% \end{variable} +% +% \subsection{Scratch boxes} +% +% \begin{variable}{\l_tmpa_box, \l_tmpb_box, \g_tmpa_box, \g_tmpb_box} +% Scratch boxes. +% \begin{macrocode} +\box_new:N \l_tmpa_box +\box_new:N \l_tmpb_box +\box_new:N \g_tmpa_box +\box_new:N \g_tmpb_box +% \end{macrocode} +% \end{variable} +% +% \subsection{Viewing box contents} +% +% \TeX{}'s \tn{showbox} is not really that helpful in many cases, and +% it is also inconsistent with other \LaTeX3{} \texttt{show} functions as it +% does not actually shows material in the terminal. So we provide a richer +% set of functionality. +% +% \begin{macro}{\box_show:N, \box_show:c} +% \begin{macro}{\box_show:Nnn, \box_show:cnn} +% Essentially a wrapper around the internal function, but evaluating +% the breadth and depth arguments now outside the group. +% \begin{macrocode} +\cs_new_protected:Npn \box_show:N #1 + { \box_show:Nnn #1 \c_max_int \c_max_int } +\cs_generate_variant:Nn \box_show:N { c } +\cs_new_protected:Npn \box_show:Nnn #1#2#3 + { \@@_show:NNff 1 #1 { \int_eval:n {#2} } { \int_eval:n {#3} } } +\cs_generate_variant:Nn \box_show:Nnn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\box_log:N, \box_log:c} +% \begin{macro}{\box_log:Nnn, \box_log:cnn} +% \begin{macro}{\@@_log:nNnn} +% Getting \TeX{} to write to the log without interruption the run is done by +% altering the interaction mode. For that, the \eTeX{} extensions are needed. +% \begin{macrocode} +\cs_new_protected:Npn \box_log:N #1 + { \box_log:Nnn #1 \c_max_int \c_max_int } +\cs_generate_variant:Nn \box_log:N { c } +\cs_new_protected:Npn \box_log:Nnn + { \exp_args:No \@@_log:nNnn { \tex_the:D \tex_interactionmode:D } } +\cs_new_protected:Npn \@@_log:nNnn #1#2#3#4 + { + \int_gset:Nn \tex_interactionmode:D { 0 } + \@@_show:NNff 0 #2 { \int_eval:n {#3} } { \int_eval:n {#4} } + \int_gset:Nn \tex_interactionmode:D {#1} + } +\cs_generate_variant:Nn \box_log:Nnn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_show:NNnn, \@@_show:NNff} +% The internal auxiliary to actually do the output uses a group to deal +% with breadth and depth values. The \cs{use:n} here gives better output +% appearance. Setting \tn{tracingonline} and \tn{errorcontextlines} is +% used to control what appears in the terminal. +% \begin{macrocode} +\cs_new_protected:Npn \@@_show:NNnn #1#2#3#4 + { + \box_if_exist:NTF #2 + { + \group_begin: + \int_set:Nn \tex_showboxbreadth:D {#3} + \int_set:Nn \tex_showboxdepth:D {#4} + \int_set:Nn \tex_tracingonline:D {#1} + \int_set:Nn \tex_errorcontextlines:D { -1 } + \tex_showbox:D \use:n {#2} + \group_end: + } + { + \msg_error:nne { kernel } { variable-not-defined } + { \token_to_str:N #2 } + } + } +\cs_generate_variant:Nn \@@_show:NNnn { NNff } +% \end{macrocode} +% \end{macro} +% +% \subsection{Horizontal mode boxes} +% +% \begin{macro}{\hbox:n} +% \testfile{m3box002.lvt} +% Put a horizontal box directly into the input stream. +% \begin{macrocode} +\cs_new_protected:Npn \hbox:n #1 + { \tex_hbox:D \scan_stop: { \color_group_begin: #1 \color_group_end: } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hbox_set:Nn, \hbox_set:cn} +% \begin{macro}{\hbox_gset:Nn, \hbox_gset:cn} +% \testfile* +% \begin{macrocode} +\cs_new_protected:Npn \hbox_set:Nn #1#2 + { + \tex_setbox:D #1 \tex_hbox:D + { \color_group_begin: #2 \color_group_end: } + } +\cs_new_protected:Npn \hbox_gset:Nn #1#2 + { + \tex_global:D \tex_setbox:D #1 \tex_hbox:D + { \color_group_begin: #2 \color_group_end: } + } +\cs_generate_variant:Nn \hbox_set:Nn { c } +\cs_generate_variant:Nn \hbox_gset:Nn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hbox_set_to_wd:Nnn, \hbox_set_to_wd:cnn} +% \begin{macro}{\hbox_gset_to_wd:Nnn, \hbox_gset_to_wd:cnn} +% \testfile* +% Storing material in a horizontal box with a specified width. +% Again, put the dimension expression in parentheses when debugging. +% \begin{macrocode} +\cs_new_protected:Npn \hbox_set_to_wd:Nnn #1#2#3 + { + \tex_setbox:D #1 \tex_hbox:D to \@@_dim_eval:n {#2} + { \color_group_begin: #3 \color_group_end: } + } +\cs_new_protected:Npn \hbox_gset_to_wd:Nnn #1#2#3 + { + \tex_global:D \tex_setbox:D #1 \tex_hbox:D to \@@_dim_eval:n {#2} + { \color_group_begin: #3 \color_group_end: } + } +\cs_generate_variant:Nn \hbox_set_to_wd:Nnn { c } +\cs_generate_variant:Nn \hbox_gset_to_wd:Nnn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hbox_set:Nw, \hbox_set:cw} +% \begin{macro}{\hbox_gset:Nw, \hbox_gset:cw} +% \begin{macro}{\hbox_set_end:, \hbox_gset_end:} +% \testfile* +% Storing material in a horizontal box. This type is useful in +% environment definitions. +% \begin{macrocode} +\cs_new_protected:Npn \hbox_set:Nw #1 + { + \tex_setbox:D #1 \tex_hbox:D + \c_group_begin_token + \color_group_begin: + } +\cs_new_protected:Npn \hbox_gset:Nw #1 + { + \tex_global:D \tex_setbox:D #1 \tex_hbox:D + \c_group_begin_token + \color_group_begin: + } +\cs_generate_variant:Nn \hbox_set:Nw { c } +\cs_generate_variant:Nn \hbox_gset:Nw { c } +\cs_new_protected:Npn \hbox_set_end: + { + \color_group_end: + \c_group_end_token + } +\cs_new_eq:NN \hbox_gset_end: \hbox_set_end: +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hbox_set_to_wd:Nnw, \hbox_set_to_wd:cnw} +% \begin{macro}{\hbox_gset_to_wd:Nnw, \hbox_gset_to_wd:cnw} +% Combining the above ideas. +% \begin{macrocode} +\cs_new_protected:Npn \hbox_set_to_wd:Nnw #1#2 + { + \tex_setbox:D #1 \tex_hbox:D to \@@_dim_eval:n {#2} + \c_group_begin_token + \color_group_begin: + } +\cs_new_protected:Npn \hbox_gset_to_wd:Nnw #1#2 + { + \tex_global:D \tex_setbox:D #1 \tex_hbox:D to \@@_dim_eval:n {#2} + \c_group_begin_token + \color_group_begin: + } +\cs_generate_variant:Nn \hbox_set_to_wd:Nnw { c } +\cs_generate_variant:Nn \hbox_gset_to_wd:Nnw { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hbox_to_wd:nn} +% \begin{macro}{\hbox_to_zero:n} +% \testfile* +% Put a horizontal box directly into the input stream. +% \begin{macrocode} +\cs_new_protected:Npn \hbox_to_wd:nn #1#2 + { + \tex_hbox:D to \@@_dim_eval:n {#1} + { \color_group_begin: #2 \color_group_end: } + } +\cs_new_protected:Npn \hbox_to_zero:n #1 + { + \tex_hbox:D to \c_zero_dim + { \color_group_begin: #1 \color_group_end: } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\hbox_overlap_center:n, \hbox_overlap_left:n, \hbox_overlap_right:n} +% Put a zero-sized box with the contents pushed against one side (which +% makes it stick out on the other) directly into the input stream. +% \begin{macrocode} +\cs_new_protected:Npn \hbox_overlap_center:n #1 + { \hbox_to_zero:n { \tex_hss:D #1 \tex_hss:D } } +\cs_new_protected:Npn \hbox_overlap_left:n #1 + { \hbox_to_zero:n { \tex_hss:D #1 } } +\cs_new_protected:Npn \hbox_overlap_right:n #1 + { \hbox_to_zero:n { #1 \tex_hss:D } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\hbox_unpack:N, \hbox_unpack:c} +% \begin{macro}{\hbox_unpack_drop:N, \hbox_unpack_drop:c} +% \testfile* +% Unpacking a box and if requested also clear it. +% \begin{macrocode} +\cs_new_eq:NN \hbox_unpack:N \tex_unhcopy:D +\cs_new_eq:NN \hbox_unpack_drop:N \tex_unhbox:D +\cs_generate_variant:Nn \hbox_unpack:N { c } +\cs_generate_variant:Nn \hbox_unpack_drop:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Vertical mode boxes} +% +% \TeX{} ends these boxes directly with the internal \emph{end_graf} +% routine. This means that there is no \cs{par} at the end of vertical +% boxes unless we insert one. Thus all vertical boxes include a \cs{par} +% just before closing the color group. +% +% \begin{macro}{\vbox:n} +% \TestFiles{m3box003.lvt} +% \begin{macro}{\vbox_top:n} +% \TestFiles{m3box003.lvt} +% Put a vertical box directly into the input stream. +% \begin{macrocode} +\cs_new_protected:Npn \vbox:n #1 + { \tex_vbox:D { \color_group_begin: #1 \par \color_group_end: } } +\cs_new_protected:Npn \vbox_top:n #1 + { \tex_vtop:D { \color_group_begin: #1 \par \color_group_end: } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\vbox_to_ht:nn, \vbox_to_zero:n} +% \begin{macro}{\vbox_to_ht:nn, \vbox_to_zero:n} +% \testfile* +% Put a vertical box directly into the input stream. +% \begin{macrocode} +\cs_new_protected:Npn \vbox_to_ht:nn #1#2 + { + \tex_vbox:D to \@@_dim_eval:n {#1} + { \color_group_begin: #2 \par \color_group_end: } + } +\cs_new_protected:Npn \vbox_to_zero:n #1 + { + \tex_vbox:D to \c_zero_dim + { \color_group_begin: #1 \par \color_group_end: } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\vbox_set:Nn, \vbox_set:cn} +% \begin{macro}{\vbox_gset:Nn, \vbox_gset:cn} +% \testfile* +% Storing material in a vertical box with a natural height. +% \begin{macrocode} +\cs_new_protected:Npn \vbox_set:Nn #1#2 + { + \tex_setbox:D #1 \tex_vbox:D + { \color_group_begin: #2 \par \color_group_end: } + } +\cs_new_protected:Npn \vbox_gset:Nn #1#2 + { + \tex_global:D \tex_setbox:D #1 \tex_vbox:D + { \color_group_begin: #2 \par \color_group_end: } + } +\cs_generate_variant:Nn \vbox_set:Nn { c } +\cs_generate_variant:Nn \vbox_gset:Nn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\vbox_set_top:Nn, \vbox_set_top:cn} +% \begin{macro}{\vbox_gset_top:Nn, \vbox_gset_top:cn} +% \testfile* +% Storing material in a vertical box with a natural height and reference +% point at the baseline of the first object in the box. +% \begin{macrocode} +\cs_new_protected:Npn \vbox_set_top:Nn #1#2 + { + \tex_setbox:D #1 \tex_vtop:D + { \color_group_begin: #2 \par \color_group_end: } + } +\cs_new_protected:Npn \vbox_gset_top:Nn #1#2 + { + \tex_global:D \tex_setbox:D #1 \tex_vtop:D + { \color_group_begin: #2 \par \color_group_end: } + } +\cs_generate_variant:Nn \vbox_set_top:Nn { c } +\cs_generate_variant:Nn \vbox_gset_top:Nn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\vbox_set_to_ht:Nnn, \vbox_set_to_ht:cnn} +% \begin{macro}{\vbox_gset_to_ht:Nnn, \vbox_gset_to_ht:cnn} +% \testfile* +% Storing material in a vertical box with a specified height. +% \begin{macrocode} +\cs_new_protected:Npn \vbox_set_to_ht:Nnn #1#2#3 + { + \tex_setbox:D #1 \tex_vbox:D to \@@_dim_eval:n {#2} + { \color_group_begin: #3 \par \color_group_end: } + } +\cs_new_protected:Npn \vbox_gset_to_ht:Nnn #1#2#3 + { + \tex_global:D \tex_setbox:D #1 \tex_vbox:D to \@@_dim_eval:n {#2} + { \color_group_begin: #3 \par \color_group_end: } + } +\cs_generate_variant:Nn \vbox_set_to_ht:Nnn { c } +\cs_generate_variant:Nn \vbox_gset_to_ht:Nnn { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\vbox_set:Nw, \vbox_set:cw} +% \begin{macro}{\vbox_gset:Nw, \vbox_gset:cw} +% \begin{macro}{\vbox_set_end:, \vbox_gset_end:} +% \testfile* +% Storing material in a vertical box. This type is useful in +% environment definitions. +% \begin{macrocode} +\cs_new_protected:Npn \vbox_set:Nw #1 + { + \tex_setbox:D #1 \tex_vbox:D + \c_group_begin_token + \color_group_begin: + } +\cs_new_protected:Npn \vbox_gset:Nw #1 + { + \tex_global:D \tex_setbox:D #1 \tex_vbox:D + \c_group_begin_token + \color_group_begin: + } +\cs_generate_variant:Nn \vbox_set:Nw { c } +\cs_generate_variant:Nn \vbox_gset:Nw { c } +\cs_new_protected:Npn \vbox_set_end: + { + \par + \color_group_end: + \c_group_end_token + } +\cs_new_eq:NN \vbox_gset_end: \vbox_set_end: +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\vbox_set_to_ht:Nnw, \vbox_set_to_ht:cnw} +% \begin{macro}{\vbox_gset_to_ht:Nnw, \vbox_gset_to_ht:cnw} +% A combination of the above ideas. +% \begin{macrocode} +\cs_new_protected:Npn \vbox_set_to_ht:Nnw #1#2 + { + \tex_setbox:D #1 \tex_vbox:D to \@@_dim_eval:n {#2} + \c_group_begin_token + \color_group_begin: + } +\cs_new_protected:Npn \vbox_gset_to_ht:Nnw #1#2 + { + \tex_global:D \tex_setbox:D #1 \tex_vbox:D to \@@_dim_eval:n {#2} + \c_group_begin_token + \color_group_begin: + } +\cs_generate_variant:Nn \vbox_set_to_ht:Nnw { c } +\cs_generate_variant:Nn \vbox_gset_to_ht:Nnw { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\vbox_unpack:N, \vbox_unpack:c} +% \begin{macro}{\vbox_unpack_drop:N, \vbox_unpack_drop:c} +% \testfile* +% Unpacking a box and if requested also clear it. +% \begin{macrocode} +\cs_new_eq:NN \vbox_unpack:N \tex_unvcopy:D +\cs_new_eq:NN \vbox_unpack_drop:N \tex_unvbox:D +\cs_generate_variant:Nn \vbox_unpack:N { c } +\cs_generate_variant:Nn \vbox_unpack_drop:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \vbox_set_split_to_ht:NNn, \vbox_set_split_to_ht:cNn, +% \vbox_set_split_to_ht:Ncn, \vbox_set_split_to_ht:ccn, +% \vbox_gset_split_to_ht:NNn, \vbox_gset_split_to_ht:cNn, +% \vbox_gset_split_to_ht:Ncn, \vbox_gset_split_to_ht:ccn, +% } +% \testfile* +% Splitting a vertical box in two. +% \begin{macrocode} +\cs_new_protected:Npn \vbox_set_split_to_ht:NNn #1#2#3 + { \tex_setbox:D #1 \tex_vsplit:D #2 to \@@_dim_eval:n {#3} } +\cs_generate_variant:Nn \vbox_set_split_to_ht:NNn { c , Nc , cc } +\cs_new_protected:Npn \vbox_gset_split_to_ht:NNn #1#2#3 + { + \tex_global:D \tex_setbox:D #1 + \tex_vsplit:D #2 to \@@_dim_eval:n {#3} + } +\cs_generate_variant:Nn \vbox_gset_split_to_ht:NNn { c , Nc , cc } +% \end{macrocode} +% \end{macro} +% +% \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, \box_rotate:cn, +% \box_grotate:Nn, \box_grotate:cn +% } +% \begin{macro}{\@@_rotate:NnN} +% \begin{macro}{\@@_rotate:N} +% \begin{macro}{\@@_rotate_xdir:nnN, \@@_rotate_ydir:nnN} +% \begin{macro} +% { +% \@@_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 + { \@@_rotate:NnN #1 {#2} \hbox_set:Nn } +\cs_generate_variant:Nn \box_rotate:Nn { c } +\cs_new_protected:Npn \box_grotate:Nn #1#2 + { \@@_rotate:NnN #1 {#2} \hbox_gset:Nn } +\cs_generate_variant:Nn \box_grotate:Nn { c } +\cs_new_protected:Npn \@@_rotate:NnN #1#2#3 + { + #3 #1 + { + \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 + } + } +% \end{macrocode} +% The edges of the box are then recorded: the left edge is +% always 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:l3box: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:l3box: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 is 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 + { + \__kernel_kern:n { -\l_@@_left_new_dim } + \hbox:n + { + \@@_backend_rotate:Nn + \l_@@_internal_box + \l_@@_angle_fp + } + } +% \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_drop: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_xdir: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_ydir: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_ydir:nnN \l_@@_right_dim \l_@@_top_dim + \l_@@_top_new_dim + \@@_rotate_ydir:nnN \l_@@_left_dim \l_@@_bottom_dim + \l_@@_bottom_new_dim + \@@_rotate_xdir:nnN \l_@@_left_dim \l_@@_top_dim + \l_@@_left_new_dim + \@@_rotate_xdir:nnN \l_@@_right_dim \l_@@_bottom_dim + \l_@@_right_new_dim + } +\cs_new_protected:Npn \@@_rotate_quadrant_two: + { + \@@_rotate_ydir:nnN \l_@@_right_dim \l_@@_bottom_dim + \l_@@_top_new_dim + \@@_rotate_ydir:nnN \l_@@_left_dim \l_@@_top_dim + \l_@@_bottom_new_dim + \@@_rotate_xdir:nnN \l_@@_right_dim \l_@@_top_dim + \l_@@_left_new_dim + \@@_rotate_xdir:nnN \l_@@_left_dim \l_@@_bottom_dim + \l_@@_right_new_dim + } +\cs_new_protected:Npn \@@_rotate_quadrant_three: + { + \@@_rotate_ydir:nnN \l_@@_left_dim \l_@@_bottom_dim + \l_@@_top_new_dim + \@@_rotate_ydir:nnN \l_@@_right_dim \l_@@_top_dim + \l_@@_bottom_new_dim + \@@_rotate_xdir:nnN \l_@@_right_dim \l_@@_bottom_dim + \l_@@_left_new_dim + \@@_rotate_xdir:nnN \l_@@_left_dim \l_@@_top_dim + \l_@@_right_new_dim + } +\cs_new_protected:Npn \@@_rotate_quadrant_four: + { + \@@_rotate_ydir:nnN \l_@@_left_dim \l_@@_top_dim + \l_@@_top_new_dim + \@@_rotate_ydir:nnN \l_@@_right_dim \l_@@_bottom_dim + \l_@@_bottom_new_dim + \@@_rotate_xdir:nnN \l_@@_left_dim \l_@@_bottom_dim + \l_@@_left_new_dim + \@@_rotate_xdir:nnN \l_@@_right_dim \l_@@_top_dim + \l_@@_right_new_dim + } +% \end{macrocode} +% \end{macro} +% \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_to_wd_and_ht_plus_dp:Nnn, +% \box_resize_to_wd_and_ht_plus_dp:cnn, +% \box_gresize_to_wd_and_ht_plus_dp:Nnn, +% \box_gresize_to_wd_and_ht_plus_dp:cnn +% } +% \begin{macro}{\@@_resize_to_wd_and_ht_plus_dp:NnnN} +% \begin{macro}{\@@_resize_set_corners:N} +% \begin{macro}{\@@_resize:N} +% \begin{macro}{\@@_resize:NNN} +% Resizing a box starts by working out the various dimensions of the +% existing box. +% \begin{macrocode} +\cs_new_protected:Npn \box_resize_to_wd_and_ht_plus_dp:Nnn #1#2#3 + { + \@@_resize_to_wd_and_ht_plus_dp:NnnN #1 {#2} {#3} + \hbox_set:Nn + } +\cs_generate_variant:Nn \box_resize_to_wd_and_ht_plus_dp:Nnn { c } +\cs_new_protected:Npn \box_gresize_to_wd_and_ht_plus_dp:Nnn #1#2#3 + { + \@@_resize_to_wd_and_ht_plus_dp:NnnN #1 {#2} {#3} + \hbox_gset:Nn + } +\cs_generate_variant:Nn \box_gresize_to_wd_and_ht_plus_dp:Nnn { c } +\cs_new_protected:Npn \@@_resize_to_wd_and_ht_plus_dp:NnnN #1#2#3#4 + { + #4 #1 + { + \@@_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 + } + } +\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} +% \end{macro} +% +% \begin{macro} +% { +% \box_resize_to_ht:Nn, \box_resize_to_ht:cn, +% \box_gresize_to_ht:Nn, \box_gresize_to_ht:cn +% } +% \begin{macro}{\@@_resize_to_ht:NnN} +% \begin{macro} +% { +% \box_resize_to_ht_plus_dp:Nn, \box_resize_to_ht_plus_dp:cn, +% \box_gresize_to_ht_plus_dp:Nn, \box_gresize_to_ht_plus_dp:cn +% } +% \begin{macro}{\@@_resize_to_ht_plus_dp:NnN} +% \begin{macro} +% { +% \box_resize_to_wd:Nn, \box_resize_to_wd:cn, +% \box_gresize_to_wd:Nn, \box_gresize_to_wd:cn +% } +% \begin{macro}{\@@_resize_to_wd:NnN} +% \begin{macro} +% { +% \box_resize_to_wd_and_ht:Nnn, \box_resize_to_wd_and_ht:cnn, +% \box_gresize_to_wd_and_ht:Nnn, \box_gresize_to_wd_and_ht:cnn +% } +% \begin{macro}{\@@_resize_to_wd_ht:NnnN} +% 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 + { \@@_resize_to_ht:NnN #1 {#2} \hbox_set:Nn } +\cs_generate_variant:Nn \box_resize_to_ht:Nn { c } +\cs_new_protected:Npn \box_gresize_to_ht:Nn #1#2 + { \@@_resize_to_ht:NnN #1 {#2} \hbox_gset:Nn } +\cs_generate_variant:Nn \box_gresize_to_ht:Nn { c } +\cs_new_protected:Npn \@@_resize_to_ht:NnN #1#2#3 + { + #3 #1 + { + \@@_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 + } + } +\cs_new_protected:Npn \box_resize_to_ht_plus_dp:Nn #1#2 + { \@@_resize_to_ht_plus_dp:NnN #1 {#2} \hbox_set:Nn } +\cs_generate_variant:Nn \box_resize_to_ht_plus_dp:Nn { c } +\cs_new_protected:Npn \box_gresize_to_ht_plus_dp:Nn #1#2 + { \@@_resize_to_ht_plus_dp:NnN #1 {#2} \hbox_gset:Nn } +\cs_generate_variant:Nn \box_gresize_to_ht_plus_dp:Nn { c } +\cs_new_protected:Npn \@@_resize_to_ht_plus_dp:NnN #1#2#3 + { + #3 #1 + { + \@@_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 + } + } +\cs_new_protected:Npn \box_resize_to_wd:Nn #1#2 + { \@@_resize_to_wd:NnN #1 {#2} \hbox_set:Nn } +\cs_generate_variant:Nn \box_resize_to_wd:Nn { c } +\cs_new_protected:Npn \box_gresize_to_wd:Nn #1#2 + { \@@_resize_to_wd:NnN #1 {#2} \hbox_gset:Nn } +\cs_generate_variant:Nn \box_gresize_to_wd:Nn { c } +\cs_new_protected:Npn \@@_resize_to_wd:NnN #1#2#3 + { + #3 #1 + { + \@@_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 + } + } +\cs_new_protected:Npn \box_resize_to_wd_and_ht:Nnn #1#2#3 + { \@@_resize_to_wd_and_ht:NnnN #1 {#2} {#3} \hbox_set:Nn } +\cs_generate_variant:Nn \box_resize_to_wd_and_ht:Nnn { c } +\cs_new_protected:Npn \box_gresize_to_wd_and_ht:Nnn #1#2#3 + { \@@_resize_to_wd_and_ht:NnnN #1 {#2} {#3} \hbox_gset:Nn } +\cs_generate_variant:Nn \box_gresize_to_wd_and_ht:Nnn { c } +\cs_new_protected:Npn \@@_resize_to_wd_and_ht:NnnN #1#2#3#4 + { + #4 #1 + { + \@@_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 + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \box_scale:Nnn, \box_scale:cnn, +% \box_gscale:Nnn, \box_gscale:cnn +% } +% \begin{macro}{\@@_scale:NnnN} +% \begin{macro}{\@@_scale:N} +% 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 code here is split into two as this allows sharing +% with the auto-resizing functions. +% \begin{macrocode} +\cs_new_protected:Npn \box_scale:Nnn #1#2#3 + { \@@_scale:NnnN #1 {#2} {#3} \hbox_set:Nn } +\cs_generate_variant:Nn \box_scale:Nnn { c } +\cs_new_protected:Npn \box_gscale:Nnn #1#2#3 + { \@@_scale:NnnN #1 {#2} {#3} \hbox_gset:Nn } +\cs_generate_variant:Nn \box_gscale:Nnn { c } +\cs_new_protected:Npn \@@_scale:NnnN #1#2#3#4 + { + #4 #1 + { + \fp_set:Nn \l_@@_scale_x_fp {#2} + \fp_set:Nn \l_@@_scale_y_fp {#3} + \@@_scale:N #1 + } + } +\cs_new_protected:Npn \@@_scale: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 + \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 + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \box_autosize_to_wd_and_ht:Nnn , +% \box_autosize_to_wd_and_ht:cnn , +% \box_gautosize_to_wd_and_ht:Nnn , +% \box_gautosize_to_wd_and_ht:cnn , +% \box_autosize_to_wd_and_ht_plus_dp:Nnn , +% \box_autosize_to_wd_and_ht_plus_dp:cnn , +% \box_gautosize_to_wd_and_ht_plus_dp:Nnn , +% \box_gautosize_to_wd_and_ht_plus_dp:cnn +% } +% \begin{macro}{\@@_autosize:NnnnN} +% Although autosizing a box uses dimensions, it has more in common in +% implementation with scaling. As such, most of the real work here is +% done elsewhere. +% \begin{macrocode} +\cs_new_protected:Npn \box_autosize_to_wd_and_ht:Nnn #1#2#3 + { \@@_autosize:NnnnN #1 {#2} {#3} { \box_ht:N #1 } \hbox_set:Nn } +\cs_generate_variant:Nn \box_autosize_to_wd_and_ht:Nnn { c } +\cs_new_protected:Npn \box_gautosize_to_wd_and_ht:Nnn #1#2#3 + { \@@_autosize:NnnnN #1 {#2} {#3} { \box_ht:N #1 } \hbox_gset:Nn } +\cs_generate_variant:Nn \box_gautosize_to_wd_and_ht:Nnn { c } +\cs_new_protected:Npn \box_autosize_to_wd_and_ht_plus_dp:Nnn #1#2#3 + { + \@@_autosize:NnnnN #1 {#2} {#3} { \box_ht:N #1 + \box_dp:N #1 } + \hbox_set:Nn + } +\cs_generate_variant:Nn \box_autosize_to_wd_and_ht_plus_dp:Nnn { c } +\cs_new_protected:Npn \box_gautosize_to_wd_and_ht_plus_dp:Nnn #1#2#3 + { + \@@_autosize:NnnnN #1 {#2} {#3} { \box_ht:N #1 + \box_dp:N #1 } + \hbox_gset:Nn + } +\cs_generate_variant:Nn \box_gautosize_to_wd_and_ht_plus_dp:Nnn { c } +\cs_new_protected:Npn \@@_autosize:NnnnN #1#2#3#4#5 + { + #5 #1 + { + \fp_set:Nn \l_@@_scale_x_fp { ( \dim_to_fp:n {#2} ) / \box_wd:N #1 } + \fp_set:Nn \l_@@_scale_y_fp + { ( \dim_to_fp:n {#3} ) / ( \dim_to_fp:n {#4} ) } + \fp_compare:nNnTF \l_@@_scale_x_fp > \l_@@_scale_y_fp + { \fp_set_eq:NN \l_@@_scale_x_fp \l_@@_scale_y_fp } + { \fp_set_eq:NN \l_@@_scale_y_fp \l_@@_scale_x_fp } + \@@_scale:N #1 + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_resize_common:N} +% The main resize function places its input into a box which start +% off 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 + { + \@@_backend_scale:Nnn + #1 + \l_@@_scale_x_fp + \l_@@_scale_y_fp + } +% \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 } + { + \__kernel_kern:n { \l_@@_right_new_dim } + \box_use_drop:N \l_@@_internal_box + \tex_hss:D + } + } + { + \box_set_wd:Nn \l_@@_internal_box { \l_@@_right_new_dim } + \hbox:n + { + \__kernel_kern:n { 0pt } + \box_use_drop:N \l_@@_internal_box + \tex_hss:D + } + } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Viewing part of a box} +% +% \begin{macro}{\box_set_clipped:N, \box_set_clipped:c, \box_gset_clipped:N, \box_gset_clipped:c} +% A wrapper around the driver-dependent code. +% \begin{macrocode} +\cs_new_protected:Npn \box_set_clipped:N #1 + { \hbox_set:Nn #1 { \@@_backend_clip:N #1 } } +\cs_generate_variant:Nn \box_set_clipped:N { c } +\cs_new_protected:Npn \box_gset_clipped:N #1 + { \hbox_gset:Nn #1 { \@@_backend_clip:N #1 } } +\cs_generate_variant:Nn \box_gset_clipped:N { c } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \box_set_trim:Nnnnn, \box_set_trim:cnnnn, +% \box_gset_trim:Nnnnn, \box_gset_trim:cnnnn +% } +% \begin{macro}{\@@_set_trim:NnnnnN} +% 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_set_trim:Nnnnn #1#2#3#4#5 + { \@@_set_trim:NnnnnN #1 {#2} {#3} {#4} {#5} \box_set_eq:NN } +\cs_generate_variant:Nn \box_set_trim:Nnnnn { c } +\cs_new_protected:Npn \box_gset_trim:Nnnnn #1#2#3#4#5 + { \@@_set_trim:NnnnnN #1 {#2} {#3} {#4} {#5} \box_gset_eq:NN } +\cs_generate_variant:Nn \box_gset_trim:Nnnnn { c } +\cs_new_protected:Npn \@@_set_trim:NnnnnN #1#2#3#4#5#6 + { + \hbox_set:Nn \l_@@_internal_box + { + \__kernel_kern:n { -#2 } + \box_use:N #1 + \__kernel_kern:n { -#4 } + } +% \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_drop: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_drop: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_drop: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_drop:N \l_@@_internal_box } + } + \box_set_ht:Nn \l_@@_internal_box \c_zero_dim + } + #6 #1 \l_@@_internal_box + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \box_set_viewport:Nnnnn, \box_set_viewport:cnnnn, +% \box_gset_viewport:Nnnnn, \box_gset_viewport:cnnnn +% } +% \begin{macro}{\@@_viewport:NnnnnN} +% 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_set_viewport:Nnnnn #1#2#3#4#5 + { \@@_set_viewport:NnnnnN #1 {#2} {#3} {#4} {#5} \box_set_eq:NN } +\cs_generate_variant:Nn \box_set_viewport:Nnnnn { c } +\cs_new_protected:Npn \box_gset_viewport:Nnnnn #1#2#3#4#5 + { \@@_set_viewport:NnnnnN #1 {#2} {#3} {#4} {#5} \box_gset_eq:NN } +\cs_generate_variant:Nn \box_gset_viewport:Nnnnn { c } +\cs_new_protected:Npn \@@_set_viewport:NnnnnN #1#2#3#4#5#6 + { + \hbox_set:Nn \l_@@_internal_box + { + \__kernel_kern:n { -#2 } + \box_use:N #1 + \__kernel_kern:n { #4 - \box_wd:N #1 } + } + \dim_compare:nNnTF {#3} < \c_zero_dim + { + \hbox_set:Nn \l_@@_internal_box + { + \box_move_down:nn \c_zero_dim + { \box_use_drop: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_drop: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_drop: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_drop:N \l_@@_internal_box } + } + \box_set_ht:Nn \l_@@_internal_box \c_zero_dim + } + #6 #1 \l_@@_internal_box + } +% \end{macrocode} +% \end{macro} +% \end{macro} + +% +% \begin{macrocode} +%</package> +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex |