summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/l3kernel/l3box.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/source/latex/l3kernel/l3box.dtx')
-rw-r--r--Master/texmf-dist/source/latex/l3kernel/l3box.dtx666
1 files changed, 663 insertions, 3 deletions
diff --git a/Master/texmf-dist/source/latex/l3kernel/l3box.dtx b/Master/texmf-dist/source/latex/l3kernel/l3box.dtx
index 8e5070145b4..d839bee4342 100644
--- a/Master/texmf-dist/source/latex/l3kernel/l3box.dtx
+++ b/Master/texmf-dist/source/latex/l3kernel/l3box.dtx
@@ -35,7 +35,7 @@
%
%<*driver|package>
\RequirePackage{l3names}
-\GetIdInfo$Id: l3box.dtx 2665 2011-08-25 22:15:27Z joseph $
+\GetIdInfo$Id: l3box.dtx 2711 2011-09-02 12:15:53Z joseph $
{L3 Experimental boxes}
%</driver|package>
%<*driver>
@@ -193,7 +193,7 @@
% such that its reference point is displaced horizontally by the given
% \meta{dimexpr} from the reference point for typesetting, to the right
% or left as appropriate. The \meta{box function} should be
-% a box opeeration such as |\box_use:N \<box>| or a \enquote{raw}
+% a box operation such as |\box_use:N \<box>| or a \enquote{raw}
% box specification such as |\vbox:n { xyz }|.
% \end{function}
%
@@ -206,7 +206,7 @@
% such that its reference point is displaced vertical by the given
% \meta{dimexpr} from the reference point for typesetting, up
% or down as appropriate. The \meta{box function} should be
-% a box opeeration such as |\box_use:N \<box>| or a \enquote{raw}
+% a box operation such as |\box_use:N \<box>| or a \enquote{raw}
% box specification such as |\vbox:n { xyz }|.
% \end{function}
%
@@ -273,6 +273,92 @@
% Set the width of the \meta{box} to the value of the
% \Arg{dimension expression}. This is a global assignment.
% \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}{\box_resize:Nnn, \box_resize:cnn}
+% \begin{syntax}
+% \cs{box_resize:Nnn} \meta{box} \Arg{x-size} \Arg{y-size}
+% \end{syntax}
+% Resize the \meta{box} to \meta{x-size} horizontally and \meta{y-size}
+% vertically (both of the sizes are dimension expressions).
+% The \meta{y-size} is the vertical size (height plus depth) of
+% the box. The updated \meta{box} will be an hbox, irrespective of the nature
+% of the \meta{box} before the resizing is applied. Negative sizes will
+% cause the material in the \meta{box} to be reversed in direction, but the
+% reference point of the \meta{box} will be unchanged. The resizing applies
+% within the current \TeX{} group level.
+%
+% \textbf{This function is experimental}
+% \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:Nnn} \meta{box} \Arg{y-size}
+% \end{syntax}
+% Resize the \meta{box} to \meta{y-size} vertically, scaling the horizontal
+% size by the same amount (\meta{y-size} is a dimension expression).
+% The \meta{y-size} is the vertical size (height plus depth) of
+% the box.
+% The updated \meta{box} will be an hbox, irrespective of the nature
+% of the \meta{box} before the resizing is applied. A negative size will
+% cause the material in the \meta{box} to be reversed in direction, but the
+% reference point of the \meta{box} will be unchanged. The resizing applies
+% within the current \TeX{} group level.
+%
+% \textbf{This function is experimental}
+% \end{function}
+%
+% \begin{function}{\box_resize_to_wd:Nn, \box_resize_to_wd:cn}
+% \begin{syntax}
+% \cs{box_resize_to_wd:Nnn} \meta{box} \Arg{x-size}
+% \end{syntax}
+% Resize the \meta{box} to \meta{x-size} horizontally, scaling the vertical
+% size by the same amount (\meta{x-size} is a dimension expression).
+% The updated \meta{box} will be an hbox, irrespective of the nature
+% of the \meta{box} before the resizing is applied. A negative size will
+% cause the material in the \meta{box} to be reversed in direction, but the
+% reference point of the \meta{box} will be unchanged. The resizing applies
+% within the current \TeX{} group level.
+%
+% \textbf{This function is experimental}
+% \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.
+%
+% \textbf{This function is experimental}
+% \end{function}
+%
+% \begin{function}{\box_scale:Nnn, \box_scale:cnn}
+% \begin{syntax}
+% \cs{box_scale:Nnn} \meta{box} \Arg{x-scale} \Arg{y-scale}
+% \end{syntax}
+% Scales the \meta{box} by factors \meta{x-scale} and \meta{y-scale} in
+% the horizontal and vertical directions, respectively (both scales are
+% integer expressions). The updated \meta{box} will be an hbox, irrespective
+% of the nature of the \meta{box} before the scaling is applied. Negative
+% scalings will cause the material in the \meta{box} to be reversed in
+% direction, but the reference point of the \meta{box} will be unchanged.
+% The scaling applies within the current \TeX{} group level.
+%
+% \textbf{This function is experimental}
+% \end{function}
%
% \section{Box conditionals}
%
@@ -1210,6 +1296,580 @@
{ \tex_setbox:D #1 \tex_vsplit:D #2 to \dim_eval:w #3 \dim_eval_end: }
% \end{macrocode}
% \end{macro}
+%
+% \subsection{Affine transformations}
+%
+% \begin{variable}{\l_box_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_box_angle_fp
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_box_cos_fp, \l_box_sin_fp}
+% These are used to hold the calculated sine and cosine values while
+% carrying out a rotation.
+% \begin{macrocode}
+\fp_new:N \l_box_cos_fp
+\fp_new:N \l_box_sin_fp
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {\l_box_top_dim, \l_box_bottom_dim, \l_box_left_dim, \l_box_right_dim}
+% These are the positions of the four edges of a box before
+% manipulation.
+% \begin{macrocode}
+\dim_new:N \l_box_top_dim
+\dim_new:N \l_box_bottom_dim
+\dim_new:N \l_box_left_dim
+\dim_new:N \l_box_right_dim
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}
+% {
+% \l_box_top_new_dim, \l_box_bottom_new_dim ,
+% \l_box_left_new_dim, \l_box_right_new_dim
+% }
+% These are the positions of the four edges of a box after
+% manipulation.
+% \begin{macrocode}
+\dim_new:N \l_box_top_new_dim
+\dim_new:N \l_box_bottom_new_dim
+\dim_new:N \l_box_left_new_dim
+\dim_new:N \l_box_right_new_dim
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_box_tmp_box, \l_box_tmp_fp}
+% Scratch space.
+% \begin{macrocode}
+\box_new:N \l_box_tmp_box
+\fp_new:N \l_box_tmp_fp
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{variable}{\l_box_x_fp, \l_box_y_fp, \l_box_x_new_fp, \l_box_y_new_fp}
+% Used as the input and output values for a point when manipulation the
+% location.
+% \begin{macrocode}
+\fp_new:N \l_box_x_fp
+\fp_new:N \l_box_y_fp
+\fp_new:N \l_box_x_new_fp
+\fp_new:N \l_box_y_new_fp
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\box_rotate:Nn}
+% \begin{macro}[aux]{\box_rotate_aux:N}
+% \begin{macro}[aux]{\box_rotate_set_sin_cos:}
+% \begin{macro}[aux]{\box_rotate_x:nnN, \box_rotate_y:nnN}
+% \begin{macro}[aux]
+% {
+% \box_rotate_quadrant_one:, \box_rotate_quadrant_two:,
+% \box_rotate_quadrant_three:, \box_rotate_quadrant_four:
+% }
+% Rotation of a box starts with working out the relevant sine and
+% cosine. There is then a check to avoid doing any real work for the
+% trivial rotation.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \box_rotate:Nn #1#2
+ {
+ \hbox_set:Nn #1
+ {
+ \group_begin:
+ \fp_set:Nn \l_box_angle_fp {#2}
+ \box_rotate_set_sin_cos:
+ \fp_compare:NNNTF \l_box_sin_fp = \c_zero_fp
+ {
+ \fp_compare:NNNTF \l_box_cos_fp = \c_one_fp
+ { \box_use:N #1 }
+ { \box_rotate_aux:N #1 }
+ }
+ { \box_rotate_aux: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_nopar:Npn \box_rotate_aux:N #1
+ {
+ \dim_set:Nn \l_box_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l_box_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l_box_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l_box_left_dim
+% \end{macrocode}
+% The next step is to work out the $x$ and $y$ coordinates of vertices of
+% the rotated box in relation to its original coordinates. The box can be
+% visualized with vertices $B$, $C$, $D$ and $E$ is illustrated
+% (Figure~\ref{fig:rotation}). The vertex $O$ is the reference point on the
+% baseline, and in this implementation is also the centre of rotation.
+% \begin{figure}
+% \centering
+% \setlength{\unitlength}{3pt}^^A
+% \begin{picture}(34,36)(12,44)
+% \thicklines
+% \put(20,52){\dashbox{1}(20,21){}}
+% \put(20,80){\line(0,-1){36}}
+% \put(12,58){\line(1, 0){34}}
+% \put(41,59){A}
+% \put(40,74){B}
+% \put(21,74){C}
+% \put(21,49){D}
+% \put(40,49){E}
+% \put(21,59){O}
+% \end{picture}
+% \caption{Co-ordinates of a box prior to rotation.}
+% \label{fig:rotation}
+% \end{figure}
+% The formulae are, for a point $P$ and angle $\alpha$:
+% \[
+% \begin{array}{l}
+% P'_x = P_x - O_x \\
+% P'_y = P_y - O_y \\
+% P''_x = ( P'_x \cos(\alpha)) - ( P'_y \sin(\alpha) ) \\
+% P''_y = ( P'_x \sin(\alpha)) + ( P'_y \cos(\alpha) ) \\
+% P'''_x = P''_x + O_x + L_x \\
+% P'''_y = P''_y + O_y
+% \end{array}
+% \]
+% The \enquote{extra} horizontal translation $L_x$ at the end is calculated
+% so that the leftmost point of the resulting box has $x$-coordinate $0$.
+% This is desirable as \TeX{} boxes must have the reference point at
+% the left edge of the box. (As $O$ is always $(0,0)$, this part of the
+% calculation is omitted here.)
+% \begin{macrocode}
+ \fp_compare:NNNTF \l_box_sin_fp > \c_zero_fp
+ {
+ \fp_compare:NNNTF \l_box_cos_fp > \c_zero_fp
+ { \box_rotate_quadrant_one: }
+ { \box_rotate_quadrant_two: }
+ }
+ {
+ \fp_compare:NNNTF \l_box_cos_fp < \c_zero_fp
+ { \box_rotate_quadrant_three: }
+ { \box_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_box_tmp_box { \box_use:N #1 }
+ \hbox_set:Nn \l_box_tmp_box
+ {
+ \tex_kern:D -\l_box_left_new_dim
+ \hbox:n
+ {
+ \driver_box_rotate_begin:
+ \box_use:N \l_box_tmp_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_box_tmp_box { \l_box_top_new_dim }
+ \box_set_dp:Nn \l_box_tmp_box { -\l_box_bottom_new_dim }
+ \box_set_wd:Nn \l_box_tmp_box
+ { \l_box_right_new_dim - \l_box_left_new_dim }
+ \box_use:N \l_box_tmp_box
+ }
+% \end{macrocode}
+% When loaded on top of \LaTeXe{} the \cs{rotatebox} function can be
+% used. There is just a slight adjustment in the syntax.
+% \begin{macrocode}
+%<*package>
+\cs_set_protected_nopar:Npn \box_rotate:Nn #1#2
+ { \hbox_set:Nn #1 { \rotatebox {#2} { \box_use:N #1 } } }
+%</package>
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% A simple conversion from degrees to radians followed by calculation
+% of the sine and cosine.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \box_rotate_set_sin_cos:
+ {
+ \fp_set_eq:NN \l_box_tmp_fp \l_box_angle_fp
+ \fp_div:Nn \l_box_tmp_fp { 180 }
+ \fp_mul:Nn \l_box_tmp_fp { \c_pi_fp }
+ \fp_sin:Nn \l_box_sin_fp { \l_box_tmp_fp }
+ \fp_cos:Nn \l_box_cos_fp { \l_box_tmp_fp }
+ }
+% \end{macrocode}
+% 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_nopar:Npn \box_rotate_x:nnN #1#2#3
+ {
+ \fp_set_from_dim:Nn \l_box_x_fp {#1}
+ \fp_set_from_dim:Nn \l_box_y_fp {#2}
+ \fp_set_eq:NN \l_box_x_new_fp \l_box_x_fp
+ \fp_set_eq:NN \l_box_tmp_fp \l_box_y_fp
+ \fp_mul:Nn \l_box_x_new_fp { \l_box_cos_fp }
+ \fp_mul:Nn \l_box_tmp_fp { \l_box_sin_fp }
+ \fp_sub:Nn \l_box_x_new_fp { \l_box_tmp_fp }
+ \dim_set:Nn #3 { \fp_to_dim:N \l_box_x_new_fp }
+ }
+\cs_new_protected_nopar:Npn \box_rotate_y:nnN #1#2#3
+ {
+ \fp_set_from_dim:Nn \l_box_x_fp {#1}
+ \fp_set_from_dim:Nn \l_box_y_fp {#2}
+ \fp_set_eq:NN \l_box_y_new_fp \l_box_y_fp
+ \fp_set_eq:NN \l_box_tmp_fp \l_box_x_fp
+ \fp_mul:Nn \l_box_y_new_fp { \l_box_cos_fp }
+ \fp_mul:Nn \l_box_tmp_fp { \l_box_sin_fp }
+ \fp_add:Nn \l_box_y_new_fp { \l_box_tmp_fp }
+ \dim_set:Nn #3 { \fp_to_dim:N \l_box_y_new_fp }
+}
+% \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_nopar:Npn \box_rotate_quadrant_one:
+ {
+ \box_rotate_y:nnN \l_box_right_dim \l_box_top_dim
+ \l_box_top_new_dim
+ \box_rotate_y:nnN \l_box_left_dim \l_box_bottom_dim
+ \l_box_bottom_new_dim
+ \box_rotate_x:nnN \l_box_left_dim \l_box_top_dim
+ \l_box_left_new_dim
+ \box_rotate_x:nnN \l_box_right_dim \l_box_bottom_dim
+ \l_box_right_new_dim
+ }
+\cs_new_protected_nopar:Npn \box_rotate_quadrant_two:
+ {
+ \box_rotate_y:nnN \l_box_right_dim \l_box_bottom_dim
+ \l_box_top_new_dim
+ \box_rotate_y:nnN \l_box_left_dim \l_box_top_dim
+ \l_box_bottom_new_dim
+ \box_rotate_x:nnN \l_box_right_dim \l_box_top_dim
+ \l_box_left_new_dim
+ \box_rotate_x:nnN \l_box_left_dim \l_box_bottom_dim
+ \l_box_right_new_dim
+ }
+\cs_new_protected_nopar:Npn \box_rotate_quadrant_three:
+ {
+ \box_rotate_y:nnN \l_box_left_dim \l_box_bottom_dim
+ \l_box_top_new_dim
+ \box_rotate_y:nnN \l_box_right_dim \l_box_top_dim
+ \l_box_bottom_new_dim
+ \box_rotate_x:nnN \l_box_right_dim \l_box_bottom_dim
+ \l_box_left_new_dim
+ \box_rotate_x:nnN \l_box_left_dim \l_box_top_dim
+ \l_box_right_new_dim
+ }
+\cs_new_protected_nopar:Npn \box_rotate_quadrant_four:
+ {
+ \box_rotate_y:nnN \l_box_left_dim \l_box_top_dim
+ \l_box_top_new_dim
+ \box_rotate_y:nnN \l_box_right_dim \l_box_bottom_dim
+ \l_box_bottom_new_dim
+ \box_rotate_x:nnN \l_box_left_dim \l_box_bottom_dim
+ \l_box_left_new_dim
+ \box_rotate_x:nnN \l_box_right_dim \l_box_top_dim
+ \l_box_right_new_dim
+ }
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{variable}{\l_box_scale_x_fp, \l_box_scale_y_fp}
+% Scaling is potentially-different in the two axes.
+% \begin{macrocode}
+\fp_new:N \l_box_scale_x_fp
+\fp_new:N \l_box_scale_y_fp
+% \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\box_resize:Nnn, \box_resize:cnn}
+% \begin{macro}[aux]{\box_resize_aux:Nnn}
+% Resizing a box starts by working out the various dimensions of the
+% existing box.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_resize:Nnn #1#2#3
+ {
+ \hbox_set:Nn #1
+ {
+ \group_begin:
+ \dim_set:Nn \l_box_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l_box_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l_box_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l_box_left_dim
+% \end{macrocode}
+% The $x$-scaling and resulting box size is easy enough to work
+% out: the dimension is that given as |#2|, and the scale is simply the
+% new width divided by the old one.
+% \begin{macrocode}
+ \fp_set_from_dim:Nn \l_box_scale_x_fp {#2}
+ \fp_set_from_dim:Nn \l_box_tmp_fp { \l_box_right_dim }
+ \fp_div:Nn \l_box_scale_x_fp { \l_box_tmp_fp }
+% \end{macrocode}
+% The $y$-scaling needs both the height and the depth of the current box.
+% \begin{macrocode}
+ \fp_set_from_dim:Nn \l_box_scale_y_fp {#3}
+ \fp_set_from_dim:Nn \l_box_tmp_fp
+ { \l_box_top_dim - \l_box_bottom_dim }
+ \fp_div:Nn \l_box_scale_y_fp { \l_box_tmp_fp }
+% \end{macrocode}
+% At this stage, check for trivial scaling. If both scalings are unity, then
+% the code does nothing. Otherwise, pass on to the auxiliary function to
+% find the new dimensions.
+% \begin{macrocode}
+ \fp_compare:NNNTF \l_box_scale_x_fp = \c_one_fp
+ {
+ \fp_compare:NNNTF \l_box_scale_y_fp = \c_one_fp
+ { \box_use:N #1 }
+ { \box_resize_aux:Nnn #1 {#2} {#3} }
+ }
+ { \box_resize_aux:Nnn #1 {#2} {#3} }
+ \group_end:
+ }
+ }
+\cs_generate_variant:Nn \box_resize:Nnn { c }
+% \end{macrocode}
+% With at least one real scaling to do, the next phase is to find the new
+% edge co-ordinates. In the $x$~direction this is relatively easy: just
+% scale the right edge. This is done using the absolute value of the
+% scale so that the new edge is in the correct place. In the $y$~direction,
+% both dimensions have to be scaled, and this again needs the absolute
+% scale value. Once that is all done, the common resize/rescale code can
+% be employed.
+% \begin{macrocode}
+\cs_new_protected:Npn \box_resize_aux:Nnn #1#2#3
+ {
+ \dim_compare:nNnTF {#2} > \c_zero_dim
+ { \dim_set:Nn \l_box_right_new_dim {#2} }
+ { \dim_set:Nn \l_box_right_new_dim { \c_zero_dim - ( #2 ) } }
+ \dim_compare:nNnTF {#3} > \c_zero_dim
+ {
+ \dim_set:Nn \l_box_top_new_dim
+ { \fp_use:N \l_box_scale_y_fp \l_box_top_dim }
+ \dim_set:Nn \l_box_bottom_new_dim
+ { \fp_use:N \l_box_scale_y_fp \l_box_bottom_dim }
+ }
+ {
+ \dim_set:Nn \l_box_top_new_dim
+ { - \fp_use:N \l_box_scale_y_fp \l_box_top_dim }
+ \dim_set:Nn \l_box_bottom_new_dim
+ { - \fp_use:N \l_box_scale_y_fp \l_box_bottom_dim }
+ }
+ \box_resize_common:N #1
+ }
+% \end{macrocode}
+% When loaded on top of \LaTeXe{} the \cs{resizebox} function can be
+% used. There is just a slight adjustment in the syntax.
+% \begin{macrocode}
+%<*package>
+\cs_set_protected_nopar:Npn \box_resize:Nnn #1#2#3
+ {
+ \hbox_set:Nn #1
+ {
+ \resizebox *
+ { \etex_dimexpr:D #2 \scan_stop: }
+ { \etex_dimexpr:D #3 \scan_stop: }
+ { \box_use:N #1 }
+ }
+ }
+%</package>
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\box_resize_to_ht_plus_dp:Nn, \box_resize_to_ht_plus_dp:cn}
+% \begin{macro}{\box_resize_to_wd:Nn, \box_resize_to_wd:cn}
+% Scaling to a total height or to a width is a simplified version of the main
+% resizing operation, with the scale simply copied between the two parts. The
+% internal auxiliary is called using the scaling value twice, as the sign for
+% both parts is needed (as this allows the same internal code to be used as
+% for the general case).
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \box_resize_to_ht_plus_dp:Nn #1#2
+ {
+ \hbox_set:Nn #1
+ {
+ \group_begin:
+ \dim_set:Nn \l_box_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l_box_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l_box_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l_box_left_dim
+ \fp_set_from_dim:Nn \l_box_scale_y_fp {#2}
+ \fp_set_from_dim:Nn \l_box_tmp_fp
+ { \l_box_top_dim - \l_box_bottom_dim }
+ \fp_div:Nn \l_box_scale_y_fp { \l_box_tmp_fp }
+ \fp_set_eq:NN \l_box_scale_x_fp \l_box_scale_y_fp
+ \fp_compare:NNNTF \l_box_scale_y_fp = \c_one_fp
+ { \box_use:N #1 }
+ { \box_resize_aux:Nnn #1 {#2} {#2} }
+ \group_end:
+ }
+ }
+\cs_generate_variant:Nn \box_resize_to_ht_plus_dp:Nn { c }
+\cs_new_protected_nopar:Npn \box_resize_to_wd:Nn #1#2
+ {
+ \hbox_set:Nn #1
+ {
+ \group_begin:
+ \dim_set:Nn \l_box_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l_box_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l_box_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l_box_left_dim
+ \fp_set_from_dim:Nn \l_box_scale_x_fp {#2}
+ \fp_set_from_dim:Nn \l_box_tmp_fp { \l_box_right_dim }
+ \fp_div:Nn \l_box_scale_x_fp { \l_box_tmp_fp }
+ \fp_set_eq:NN \l_box_scale_y_fp \l_box_scale_x_fp
+ \fp_compare:NNNTF \l_box_scale_x_fp = \c_one_fp
+ { \box_use:N #1 }
+ { \box_resize_aux:Nnn #1 {#2} {#2} }
+ \group_end:
+ }
+ }
+\cs_generate_variant:Nn \box_resize_to_wd:Nn { c }
+% \end{macrocode}
+% Again, in package mode the scaling can be handled by \cs{resizebox}.
+% \begin{macrocode}
+%<*package>
+\cs_set_protected_nopar:Npn \box_resize_to_ht_plus_dp:Nn #1#2
+ {
+ \hbox_set:Nn #1
+ {
+ \resizebox * { ! } { \etex_dimexpr:D #2 \scan_stop: } { \box_use:N #1 }
+ }
+ }
+\cs_set_protected_nopar:Npn \box_resize_to_wd:Nn #1#2
+ {
+ \hbox_set:Nn #1
+ {
+ \resizebox * { \etex_dimexpr:D #2 \scan_stop: } { ! } { \box_use:N #1 }
+ }
+ }
+%</package>
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\box_scale:Nnn, \box_scale:cnn}
+% \begin{macro}[aux]{\box_scale_aux:Nnn}
+% When scaling a box, setting the scaling itself is easy enough. The
+% new dimensions are also relatively easy to find, allowing only for
+% the need to keep them positive in all cases. Once that is done then
+% after a check for the trivial scaling a hand-off can be made to the
+% common code. The dimension scaling operations are carried out using
+% the \TeX{} mechanism as it avoids needing to use \texttt{fp}
+% operations.
+% \begin{macrocode}
+\cs_new_protected_nopar:Npn \box_scale:Nnn #1#2#3
+ {
+ \hbox_set:Nn #1
+ {
+ \group_begin:
+ \fp_set:Nn \l_box_scale_x_fp {#2}
+ \fp_set:Nn \l_box_scale_y_fp {#3}
+ \dim_set:Nn \l_box_top_dim { \box_ht:N #1 }
+ \dim_set:Nn \l_box_bottom_dim { -\box_dp:N #1 }
+ \dim_set:Nn \l_box_right_dim { \box_wd:N #1 }
+ \dim_zero:N \l_box_left_dim
+ \fp_compare:NNNTF \l_box_scale_x_fp = \c_one_fp
+ {
+ \fp_compare:NNNTF \l_box_scale_y_fp = \c_one_fp
+ { \box_use:N #1 }
+ { \box_scale_aux:Nnn #1 {#2} {#3} }
+ }
+ { \box_scale_aux:Nnn #1 {#2} {#3} }
+ \group_end:
+ }
+ }
+\cs_generate_variant:Nn \box_scale:Nnn { c }
+\cs_new_protected_nopar:Npn \box_scale_aux:Nnn #1#2#3
+ {
+ \fp_compare:NNNTF \l_box_scale_y_fp > \c_zero_fp
+ {
+ \dim_set:Nn \l_box_top_new_dim { #3 \l_box_top_dim }
+ \dim_set:Nn \l_box_bottom_new_dim { #3 \l_box_bottom_dim }
+ }
+ {
+ \dim_set:Nn \l_box_top_new_dim { -#3 \l_box_bottom_dim }
+ \dim_set:Nn \l_box_bottom_new_dim { -#3 \l_box_top_dim }
+ }
+ \fp_compare:NNNTF \l_box_scale_x_fp > \c_zero_fp
+ { \l_box_right_new_dim #2 \l_box_right_dim }
+ { \l_box_right_new_dim -#2 \l_box_right_dim }
+ \box_resize_common:N #1
+ }
+% \end{macrocode}
+% When loaded on top of \LaTeXe{} the \cs{scalebox} function can be
+% used. There is just a slight adjustment in the syntax.
+% \begin{macrocode}
+%<*package>
+\cs_set_protected_nopar:Npn \box_scale:Nnn #1#2#3
+ { \hbox_set:Nn #1 { \scalebox {#2} [#3] { \box_use:N #1 } } }
+%</package>
+% \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[int]{\box_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_nopar:Npn \box_resize_common:N #1
+ {
+ \hbox_set:Nn \l_box_tmp_box
+ {
+ \driver_box_scale_begin:
+ \hbox_overlap_right:n { \box_use:N #1 }
+ \driver_box_scale_end:
+ }
+% \end{macrocode}
+% The new height and depth can be applied directly.
+% \begin{macrocode}
+ \box_set_ht:Nn \l_box_tmp_box { \l_box_top_new_dim }
+ \box_set_dp:Nn \l_box_tmp_box { \l_box_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_box_scale_x_fp < \c_zero_fp
+ {
+ \hbox_to_wd:nn { \l_box_right_new_dim }
+ {
+ \tex_kern:D \l_box_right_new_dim
+ \box_use:N \l_box_tmp_box
+ \tex_hss:D
+ }
+ }
+ {
+ \box_set_wd:Nn \l_box_tmp_box { \l_box_right_new_dim }
+ \box_use:N \l_box_tmp_box
+ }
+ }
+% \end{macrocode}
+%\end{macro}
%
% \begin{macrocode}
%</initex|package>