2

I am trying to measure the width of an \hskip. I have tried something like

\ExplSyntaxOn
a
\skip_horizontal:N 10pt plus 1fill
\box_set_to_last:N \l_tmpa_box
b\par

\the\box_wd:N \l_tmpa_box \ExplSyntaxOff

But \l_tmpa_box appears to have zero width. I thought that an \hskip becomes a box once it's typeset, but maybe I am misunderstanding something.

In any case, I know how to use \tikzmark to measure the width with two compilation passes. However, I am wondering if it can be done directly in one pass.

2 Answers2

2

The function \box_set_to_last:N is essentially the TeX primitive \lastbox and a call such as

\box_set_to_last:N \l_tmpa_box

is the same as

\setbox\l_tmpa_box=\lastbox

Here's what the TeXbook says about it:

The bottom line of the table above refers to \lastbox, a primitive operation that hasn't been mentioned before. If the last item on the current horizontal list or vertical list is an hbox or vbox, it is removed from the list and it becomes the \lastbox; otherwise \lastbox is void. This operation is allowed in internal vertical mode, horizontal mode, and restricted horizontal mode, but you cannot use it to take a box from the current page in vertical mode. In math modes, \lastbox is always void. At the beginning of a paragraph, ‘{\setbox0=\lastbox}’ removes the indentation box.

So your attempt fails, because the last item in the current horizontal list is a skip.

However, also

a\setbox0=\lastbox\hskip 10pt plus 1fill

will result in \box0 to have zero width; more precisely, it will be void, because the last item isn't an hbox or a vbox, but a character.

You cannot detect the final amount of a skip when a paragraph is being absorbed, because stretching or shrinking happen at a later stage when the paragraph is split into lines.

egreg
  • 1,121,712
0

If you know in advance what the right hand side of the line will contain then you can do as follows.

The tabto package defines a command \tabto that jumps to a specific position in a line, and a length \CurrentLineWidth that contains the line width at the point of calling the command. Furthermore, \tabto stores the position where it was last called in a length \TabPrevPos. Therefore, as explained in https://tex.stackexchange.com/a/541683/89417 (answer by the package author), you can 'jump' to the current width (which causes no change in the position), then do the \hskip, then calculate the width of the skip by subtracting the width of the right hand side and the first position from the linewidth. This calculation needs only a single compilation pass.

MWE:

\documentclass{article}
\usepackage{showframe}
\usepackage{calc}
\usepackage{tabto}

\newlength{\hskiplen} \newlength{\firstpos} \newlength{\secondpos} \newsavebox{\rhs} \begin{document} \sbox{\rhs}{xyz} abc\hskip10pt plus 1fill xyz

abc\tabto{\CurrentLineWidth}\hskip10pt plus 1fill\setlength{\firstpos}{\TabPrevPos}\setlength{\secondpos}{\linewidth-\wd\rhs-\TabPrevPos}\usebox{\rhs}

first position: \the\firstpos

second position: \the\secondpos

\setlength{\hskiplen}{\secondpos-\firstpos}length of hskip: \the\hskiplen

width of rhs: \the\wd\rhs \end{document}

Result:

enter image description here

Note that this essentially circumvents the original problem, because you are not measuring the hskip itself, but all other elements in the line. It is difficult to actually measure the box created by \hskip with fill, because the width is dependent on the rest of the line so the box is not yet available after the actual command (I think...). You could try if LuaLaTeX can provide more information on the complete list of boxes, see https://www.overleaf.com/learn/latex/Articles/Pandora%E2%80%99s_%5Chbox%3A_Using_LuaTeX_to_Lift_the_Lid_of_TeX_Boxes for some pointers.

Marijn
  • 37,699