3. Page layout

Each page in a multiple-page HTML output has a navigation bar at its head and foot. The bar contains links to the first, previous, and next page, and the table-of-contents and index pages. If any of the first three pages doesn’t exist, or if it is the same as the page containing the navigation bar, the link for it is disabled (grayed). If the ToC or index page doesn’t exist, it is absent from the bar.

Page breaks

Multiple pages are created because the document source implicitly or explicilty used the page-break command .bp. Each call to .bp ends the current HTML page and start a new one. Thus .bp causes a page-break in both the HTML and the print outputs.

Generally, page-breaks require a different æsthetic in HTML than in print. You can use groff conditionals to express this difference:

    .if \n[.troff2page] .bp

causes a page-break only in the HTML, whereas

    .if !\n[.troff2page] .bp

causes a page-break only in print.

As can be seen, the number register \n[.troff2page] has a true value, i.e., a number greater than zero, only when Troff2page processes the document.

If may be convenient to define a .HBP macro that causes a page-break only for HTML:

    .de HBP
    .if \n[.troff2page] .bp

Note that Troff2page, like groff, does not automatically cause a page break at the sectioning commands. Use .bp or a macro like .HBP to explicitly to insert breaks as needed, or define your own sectioning commands or modify the existing ones to embed a page break.

Here’s a way to create pagebreaks in HTML before every top-level .SH, by redefining the underlying .@SH macro:

    .if \n[.troff2page] \{\
    .  rn @SH __@SH_orig
    .  de @SH
    .    nr __@SH_first_arg \\$1
    .    shift
    .    if '\\n[__@SH_first_arg]'1' .bp
    .    __@SH_orig \\n[__@SH_first_arg] \\$*
    .  .
    .  als SH @SH

Recognizing when Troff2page is running

We used the value stored in the number register \n[.troff2page] to determine which typesetter — Troff2page or groff — is operating on our text. A positive number is considered true. (Perhaps contrary to expectation, not just 0 but all negative numbers are false for groff.) Unset registers are assumed to contain 0.

In Troff2page, the positive number held in this register is in fact the version number of the Troff2page program processing the document. This is a number such as 20150320, which would be the version number of the Troff2page released on 2015 March 20. \n[.troff2page] could plausibly be used to distinguish not just Troff2page from groff, but also between various versions of Troff2page. Thus, one could, if the need were dire, write a document that translates differently with different versions of Troff2page. But don’t do this.

If you’d rather stick to known groff registers, the read-only paper-length register \n(.p is a candidate. groff sets it to 11 inches, i.e., the length of a US Letter size paper. Presumably it could vary depending on country of use, but it is never going to be non-positive. Troff2page, since it doesn’t deal in paper, sets \n(.p to 0.

groff has its own version registers \n(.x, \n(.y, and \n(.Y, which contain the major and minor version numbers and revision number respectively. Troff2page sets these too, splitting the value of \n[.troff2page] between year, month and day. Thus the Troff2page released on 2020 December 2 sets \n[.troff2page] to 20201202, \n(.x to 2020, and \n(.y to 12, and \n(.Y to 2. (These are all numbers (not strings), so there’s no 0-left-padding.)

HTML-style paragraphs

By default, Troff2page sets paragraphs exactly as groff does: the first lines of paragraphs are indented (unless explicitly disabled via an .LP), and the vertical distance between adjacent paragraphs is the same smallish amount that groff employs. These two values are given by the number registers PI and PD, which (for the -ms format) are 0.3n and 0.3v respectively.

If you wish to have the kind of block paragraphs customary in HTML, it is not enough to set PI to 0, for the prevailing value of PD is too small to comfortably tell adjacent paragraphs apart. You could set PD to a larger value, but as a convenience, Troff2page allows you to set PD to a negative value, which leads to whatever default paragraph separation that HTML uses. Note that setting PD to a negative value should be done only for HTML, as groff will happily overstrike text in order to respect a negative PD:

    .if \n[.troff2page] \{\
    .  nr PI 0n
    .  nr PD 0-1p

Scaling factors

The scaling factor n (en) used above is half the width of the letter ‘m’ in the default font size. v is the distance between two consecutive baselines. Scaling factors are generally, but not always, what you might think of as length units.

Other groff scaling factors that Troff2page recognizes are: i (inch = 1/12 ft), c (cm = 50/127 in., p (point = 1/72 in., P (pica = 12 pt), m (em = 2 ens), and M (1/100 em).


Internally, both groff and Troff2page store lengths in the scaling factor u, which is 1/1000 of a point, i.e., 1 millipoint (mpt). groff keeps all its lengths as integral multiples of millipoints, thereby avoiding floating-point arithmetic. Troff2page uses Lua’s FP, so fractions of a millipoint are allowed.

The largest length expressible in groff is 231 − 1 mpt, which is a shade over 0.47 miles. Troff2page is a bit more generous, allowing lengths of up to 6.7 × 10285 light years.

The scaling factor f is simply 216, so its coefficients can be used as fractions in a purely integer world. I.e., groff will let you write the coefficients as decimal rationals, but the product with f is always reduced to integer. Troff2page uses Lua’s FP, so its fractions can be finer grained.

Line length

Unlike groff -ms, which uses the line length given by the register LL, Troff2page by default does not restrict the text width on its HTML pages. Text will narrow or widen to fit the current browser window width. However, if you explicitly set LL (instead of relying on -ms’s default), Troff2page will respect that line length as an upper bound, regardless of how wide your browser gets.

If you prefer Troff2page’s default fit-to-width text-width, but one of the macro files you loaded sets LL, simply reset LL to 0 inside an .if \n[.troff2page] conditional. Use the same ploy to set your HTML LL to whatever max-width you want, without affecting the print output.

It is always OK to have the browser window less wide than LL (as is certainly unavoidable on cell phones): the text will correspondingly shrink to fit the window, avoiding the need for horizontal scrolling.

A good LL value for Troff2page should be a bit larger than -ms’s 6 inches, which is too narrow for a browser. The following sets it to 1/8000 of a mile, but it takes effect only for HTML:

    .if \n[.troff2page] .nr LL 7.92i


Troff2page supports a generalized version of the .IMG macro from groff’s www.tmac. It accepts all the usual image file formats. An example call:

    .IMG t2p.png

This produces: [t2p.png]

An optional first argument of -L aligns the image to the left; -R to the right; and -C, the default, centers it.

An optional final argument specifies the width of the image. The default is 1 inch. E.g.,

    .IMG -L t2p.png 2.718281828i

produces a left-justified image that’s e inches wide: [t2p.png]

.IMG relies on external programs convert (from ImageMagick); mkbitmap and potrace (both from the Potrace package); and Inkscape. mkbitmap and potrace are needed for PNG images; Inkscape for SVG images.