5.28.1.1 Page Location Traps

Request: .wh dist [name]

Call macro name when the vertical position dist on the page is reached or passed in the downward direction. The default scaling unit is ‘v’. Non-negative values for dist set the trap relative to the top of the page; negative values set the trap relative to the bottom of the page. An existing visible trap (see below) at dist is removed; this is wh’s sole function if name is missing.

A trap is sprung only if it is visible, meaning that its location is reachable on the page83 and it is not hidden by another trap at the same location already planted there.

An example of how a macro package might set headers and footers follows.

.de hd                \" page header
'  sp .5i
.  tl '\\*[Title]''\\*[Date]'
'  sp .3i
..
.
.de fo                \" page footer
'  sp 1v
.  tl ''%''
'  bp
..
.
.wh 0   hd            \" trap at top of the page
.wh -1i fo            \" trap one inch from bottom

A trap above the top or at or below the bottom of the page can be made visible by either moving it into the page area or increasing the page length so that the trap is on the page. Negative trap values always use the current page length; they are not converted to an absolute vertical position. We can use the ptr request to dump our page location traps to the standard error stream (see Debugging). Their positions are reported in basic units appropriate to the device; an nroff device example follows.

.pl 5i
.wh -1i xx
.ptr
    error→ xx      -240
.pl 100i
.ptr
    error→ xx      -240

It is possible to have more than one trap at the same location (although only one at a time can be visible); to achieve this, the traps must be defined at different locations, then moved to the same place with the ch request. In the following example, the many empty lines caused by the bp request are not shown in the output.

.de a
.  nop a
..
.de b
.  nop b
..
.de c
.  nop c
..
.
.wh 1i a
.wh 2i b
.wh 3i c
.bp
    ⇒ a b c
.ch b 1i
.ch c 1i
.bp
    ⇒ a
.ch a 0.5i
.bp
    ⇒ a b
Register: \n[.t]

The read-only register .t holds the distance to the next vertical position trap. If there are no traps between the current position and the bottom of the page, it contains the distance to the page bottom. Within a diversion, in the absence of a diversion trap, this distance is the largest representable integer in basic units—effectively infinite.

Request: .ch name [dist]

Change the location of a trap by moving macro name to new location dist, or by unplanting it altogether if dist is absent. The default scaling unit is ‘v’. Parameters to ch are specified in the opposite order from wh. If name is the earliest planted macro of multiple traps at the same location, (re)moving it from that location exposes the macro next least recently planted at the same place.84

Changing a trap’s location is useful for building up footnotes in a diversion to allow more space at the bottom of the page for them.

The same macro can be installed simultaneously at multiple locations; however, only the earliest-planted instance—that has not yet been deleted with wh—will be moved by ch. The following example (using an nroff device) illustrates this behavior.85 Blank lines have been elided from the output.

.de T
Trap sprung at \\n(nlu.
.br
..
.wh 1i T
.wh 2i T
foo
.sp 11i
.bp
.ch T 4i
bar
.sp 11i
.bp
.ch T 5i
baz
.sp 11i
.bp
.wh 5i
.ch T 6i
qux
.sp 11i
    ⇒ foo
    ⇒ Trap sprung at 240u.
    ⇒ Trap sprung at 480u.
    ⇒ bar
    ⇒ Trap sprung at 480u.
    ⇒ Trap sprung at 960u.
    ⇒ baz
    ⇒ Trap sprung at 480u.
    ⇒ Trap sprung at 1200u.
    ⇒ qux
    ⇒ Trap sprung at 1440u.
Register: \n[.ne]

The read-only register .ne contains the amount of space that was needed in the last ne request that caused a trap to be sprung; it is useful in conjunction with the .trunc register. See Page Control.

Since the .ne register is set only by traps, it doesn’t make sense to interpolate it outside of macros called by traps.

Register: \n[.trunc]

A read-only register containing the amount of vertical space truncated from an sp request by the most recently sprung vertical position trap, or, if the trap was sprung by an ne request, minus the amount of vertical motion produced by the ne request. In other words, at the point a trap is sprung, it represents the difference of what the vertical position would have been but for the trap, and what the vertical position actually is.

Since the .trunc register is set only by traps, it doesn’t make sense to interpolate it outside of macros called by traps.

Register: \n[.pe]

A read-only register containing 1 while a page is being ejected with the bp request (or by the end of input), and 0 otherwise.

In the following example, only the second call to x is caused by bp.

.de x
\&.pe=\\n[.pe]
.br
..
.wh 1v x
.wh 4v x
A line.
.br
Another line.
.br
    ⇒ A line.
       .pe=0
       Another line.

       .pe=1

An important fact to consider while designing macros is that diversions and traps do not interact normally. For example, if a trap calls a header macro (while outputting a diversion) that tries to change the font on the current page, the effect is not visible before the diversion has completely been printed (except for input protected with \! or \?) since the data in the diversion is already formatted. In most cases, this is not the expected behaviour.