[comp.text] Aligning with the right Margin

msb@sq.com (Mark Brader) (03/20/89)

To recap, the problem was to put a \(sq character aligned with the right
margin on the last line of a proof using troff.  Several solutions have
been posted to the net, but all of them are deficient in one way or another.

To really do this properly requires some knowledge of troff's built-in
read-only number registers.  The ones mentioned in this message are:

	\n(.i	current indent level from .in
	\n(.j	current adjustement mode from .ad/.na
	\n(.k	width of text in output line currently being filled
	\n(.l	current line length from .ll

The .i, .k, and .l registers are in basic units, so they usually get
a u suffix when they're being read back.

There are actually two somewhat separate problems.  One is making the \(sq
be right-justified, and the other is getting it on the same line as the
last line of the proof -- if it fits.

There are several ways to get a character right-justified if it's on a line
by itself, i.e., if you have just had a .br or equivalent.  These include:

	.ad r
	\(sq
	.br
	.ad b
and:
	.ti \n(.lu-\w'\(sq'u
	\(sq
and:
	\h'\n(.lu-\n(.iu-\w"\(sq"u'\(sq
and:
	\h'|\n(.lu-\n(.iu-\w"\(sq"u'\(sq
and:
	.ta \n(.lu-\n(.iuR
		\(sq
and:
	.lt \n(.lu
	.tl '''\(sq'
	.lt

Personally I would like the first one, being the only one that can get away
without referring to any built-in number registers -- except that it's not
really complete, because it should save the adjustment setting from \n(.j
first of all, and restore that state instead of doing .ad b at the end.

In the sixth version, by the way, note that the line length for .tl lines
is maintained separately (by .lt) from that for text lines, so a bare .tl
is inadequate.  And in the \h-based versions, note that the \(sq follows
immediately after the \h escape sequence, so that there isn't an interword
space in there to mess up the width calculations.


The next problem is combining the right-justified \(sq with the last
line of the proof.  You can't simply take the end of the last sentence
and tack onto it one of the \h-based or tab-based constructs shown above.
The reason that these don't work is that troff insists on knowing, before
it formats the paragraph, how large a motion is to be produced, but the
amount of motion you want depends on how the paragraph happens to be filled.

In the absence of traps you could do a .sp -1 and follow it with any of
the above constructs.  But normally there is at least a bottom-of-page trap,
so the .sp -1 method won't work if the end of the proof falls at the end
of the page.

If the bottom-of-page trap is set *relative to* the bottom of the page, and
there is no other trap nearby, then you can use the method I described in
my other article posted today.  This time it will look like this:

	.pl +1		(or better, buy sqtroff and use .vpt off)
	.mk ZZ
	.sp |\n(ZZu
	.ti \n(.lu-\w'\(sq'u
	\(sq
	.pl -1		(or .vpt on)


This last is perhaps the most elegant solution in some sense, but there is
another approach which does not require putting the \(sq on its own output
line at all.  This is:

	\h'\n(.lu-\n(.iu-\n(.ku-\w"  \(sq"u'\(sq

This works because the \n(.k is our window into the way troff is filling
the paragraph... we have taken one of the solutions given above, and
modified it by reducing the amount of motion by the amount of text already
going on the line.

There is a problem with this method too.  The value of \n(.k does not
include any allowance for the interword space after the text on the
line.  That's why we have to allow for it, with a space inside the
\w escape sequence inside the \h escape sequence.  But I actually gave
*2* spaces there.  Why?  Because I assumed that the last thing in the
proof would also end a sentence, and so, there will be a double interword
space there.  You don't care about the amount of interword space since
you're about to \h all the way to the right, but troff doesn't know that,
and it gives you a space whether you want it or not.  Or two spaces, if
it's at end-of-sentence.

Directly attaching the whole construction to the last text line, or
using \c to achieve the same effect, won't work because \n(.k is
updated only when a word is completed.

An alternative, perhaps better, is to modify the above escape sequence
by removing the double space:

	\h'\n(.lu-\n(.iu-\n(.ku-\w" \(sq"u'\(sq

--and then, if the preceding thing does look like an end-of-sentence to troff,
*concealing* this by appending a \& escape sequence or a space to the
last text line.  (\& is a non-printing zero-width pseudo-character.)


There is *still* a problem with *all* of these methods:
what if there is no room on the line for the \(sq?

My final suggestion is the following:

	.nr XX \n(.l-\n(.i-\n(.k-\w'  \(sq'u	
	.ie \n(XX>=0 \h'\n(XXu'\ \(sq
	.el \{.
	.ne 2
	.ti \n(.lu-\w'\(sq'u
	\(sq
	.\}

(Again, if the preceding thing looks like an end-of-sentence, you have to
conceal it from troff.)  What this does is to compute the motion that will
be needed to put a \(sq at the end of the line with a minimum of two spaces
before it, and if it fits, it does it; but if it doesn't fit, the final
line of the proof is forced out (and forced onto the next page if there
was room for only one line), and the \(sq is put on the following line.


Mark Brader			"'You wanted it to WORK?  That costs EXTRA!'
SoftQuad Inc., Toronto		 is probably the second-place security hole
utzoo!sq!msb, msb@sq.com	 after simple carelessness."	-- John Woods
416-963-8337, or from US, 800-387-2777