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