groff: Page Location Traps

 
 5.28.1.1 Page Location Traps
 ............................
 
 A "page location trap" is a vertical position trap that applies to the
 page; that is, to undiverted output.  Many can be present; manage them
 with the 'wh' and 'ch' requests.
 
  -- Request: .wh dist [name]
      Plant macro NAME as page location trap at DIST.  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.  It is not possible to plant a
      trap less than one basic unit from the page bottom: a DIST of '-0'
      Location Traps-Footnote-1::) 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
      Traps-Footnote-2::) and it is not hidden by another trap at the
      same location already planted there.
 
      A macro package might set headers and footers as follows; this
      example configures vertical margins of one inch to the body text,
      and one half-inch to the titles.  Observe the use of the no-break
      control character with 'sp' request to position our text baselines,
      and the page number character '%' used with the 'tl' request.
 
           .\" hdfo.roff
           .de hd                  \" page header
           '  sp .5i
           '  tl '\\*(Ti''\\*(Da'  \" title and date strings
           '  sp .5i
           ..
           .de fo                  \" page footer
           '  sp .5i
           .  tl ''%''
           .  bp
           ..
           .wh 0   hd             \" trap at top of the page
           .wh -1i fo             \" trap 1 inch from bottom
 
      To use these traps, copy the above (or load it from a file with the
      'so' or 'mso' requests), then set up the strings it uses.
 
           .so hdfo.roff
           .ds Ti Final Report\"
           .ds Da 21 May 2023\"
           .ti
           On 5 August of last year,
           this committee tasked me with the investigation of the
           CFIT (controlled flight into terrain) incident of
           .\" ...and so on...
 
      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 (⇒
      Debugging).  Their positions are reported in basic units; 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.(3)  (⇒Page Location Traps-Footnote-3)
 
      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.  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.  ⇒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]
      This Boolean-valued, read-only register interpolates 1 while a page
      is being ejected, and 0 otherwise.
 
      In the following example, we plant the same trap at the top and the
      bottom of the page.  We also make the trap report its name and the
      vertical drawing position.
 
           .de T
           .tm \\$0: page \\n%, nl=\\n[nl] .pe=\\n[.pe]
           ..
           .ll 46n
           .wh 0 T
           .wh -1v T
           Those who can make you believe absurdities can make you
           commit atrocities. \[em] Voltaire
               error-> T: page 1, nl=0 .pe=0
               error-> T: page 1, nl=2600 .pe=1
               => Those who can make you believe absurdities can
               => make you commit atrocities. -- Voltaire
 
    When designing macros, keep in mind that diversions and traps do
 normally interact.  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.