Aligning Characters and Rules including a proposal to change

, together with Computer Modern, does not guarantee aligning characters and rules correctly. Essentially, there are two situations to be handled by .

The first can be studied by typeing $ $ and watching what happens: At position a typeset a radical sign of width b in a which translates into a push/pop pair; then move right b to position a + b, there typeset next character or rule.

Now, the three values a, b, and a + b may well be such that round(a) + round(b) ≠ round(a + b). To go into this, let round(x) = x + .5 − δ_{x} such that the values to be rounded are assumed to be real valued pixel measures, the result is an integer, and 0 ≤ δ_{x} < 1:

round(a + b) = a + b + .5 − δ_{a+b}
round(a) + round(b) = a + b + 1 − δ_{a} − δ_{b}

whose difference is

Δ = δ_{a} + δ_{b} − .5 − δ_{a+b}.

Δ tells us something about where thought to set the first pixel of the rule relative to a position computed as round(b) in the radical sign. As a glance on adjust fit and big sqrt will tell us, the right margin of the radical sign depends, indeed, on round(charwd) if shrink fit = 0. We see that, dependent on how b rounds, Δ may be (−1 or 0), or (0 or +1), so we actually have two pixels variation to take care of. The situation is still worsened by shrink fit being 1 or 2 which effect is not taken care of at the radical sign. At the moustaches it is.

A similar phenomenon may happen vertically when the radical as well as the radicand box are raised by the same amount, when, by a font space switch due to a font switch, the second raise is rounded differently. Then you may see perhaps a horn at the top of the radical sign.

There is another aligning problem of the kind char kern −3μ char where the place of joining may seem thin. This also is due to pixel rounding and shrink fit difficulties. One easy solution may be to kern somewhat more backwards.

The second situation can be characterized by a framed box. At a typeset a rule of height b; move up by b − c to position a − b + c and typeset there a rule of height c. Of course we expect the edges at position a + b to align but …

Take the one line file

printing 1. Kiste. \underline{\overline{}}

It will, when displayed at 80dpi, show two horns. explains that (we give here coordinates in pixels) a = 39.3870, b = 14.1691, c = .4428 such that a − b + c = 25.6607. So round(a) = 39, ceiling(b) = 15, ceiling(c) = 1 yet round(a − b + c) = 26 where we would expect 25. There is no difficulty to construct a horizontal example as well.

We will express ceiling(x) = x + δ_{x}, conditions as above, and find

round(a − b + c) − ceiling(c) = a − b + .5 − δ_{a−b+c} − δ_{c}
round(a) − ceiling(b) = a − b + .5 − δ_{a} − δ_{b}

whose difference is

Δ = +δ_{a} + δ_{b} − δ_{c} − δ_{a−b+c}

so again we may have to cope with the choice between −1, 0 and +1 pixels deviation.

So this is the situation. There is a choice of possible remedies each of which will soften conditions somewhat. First one is: live with it, the effects are noticably at low resolution only, after all. But then, there is a thing like professional pride, and isn’t TEX supposed to typeset the most beautiful documents, after all?

Second, which has been done successfully at our installation, is to fix up Computer Modern such as to better handle at least horizontal character aligning. To this end we compensate for shrink fit and compute the right margin of the character in question based on floor(charwd) instead of round(charwd). Setting floor(x) = x − δ′_{x}, the horizontal alignment formulas turn out

round(a + b) = a + b + .5 − δ_{a+b}
round(a) + floor(b) = a + b + .5 − δ_{a} − δ′_{b}

whose difference this time is

Δ = δ_{a} + δ′_{b} − δ_{a+b}.

so now we must cope with 1 pixel variance only, and this we can do, changing cmbase:

a new subroutine … def right align adjust fit(expr left adjustment) = % for characters that must align at the right

l := − hround(left adjustment ∗ hppp) − letter fit;

interim xoffset := −l;

charwd := charwd + letter fit^{#} + left adjustment;

r := l + floor(charwd ∗ hppp) − shrink fit; % this is the gist of it w := r; enddef;

and a change … def big sqrt =

right align adjust fit(0);

pickup rule.nib;

x_{1} = good.x(^{4}/_{9}w + shrink fit); % still tentative since some of them connect left x_{2} = good.x(w + shrink fit); % this is the essential news

… etc … enddef;

and changing similarly the radical sign pieces in . Here dot “2” is exactly at floor(charwd) if rule thickness is an even number of pixels, or exactly at floor(charwd) + ^{1}/_{2} if rule thickness is odd. Both cases will align perfectly with any choice of Δ.

Vertical misalignment of roots, however, will not be covered by above font manipulation. There is another way of approach, namely to set a = 0 in above formulas. Square roots will come out right vertically, too, and, as a surprise, if one manages to set rule thickness exactly n + ^{1}/_{2} with n ≥ 0 integer, even boxes come out right.

But it would mean a change in . One would replace the h, v, hh, vv values by pairs denoting ‘base’ and ‘offset’, computing the effective value base + offset 1. at actual typesetting time, and 2. when pushing. At push time, push base and offset, set base = base + offset and offset = 0 for all the values mentioned. That this approach can be done, I showed by my program where I used h and hh that way, and a direction indicator.

It is clear that maxdrift may be > 2 in deeply nested formulas, this is the price to be paid, yet on the other hand, some more consistency may be gained (æsthetically).

Lastly, and not well studied, one might think of a matching algorithm for aligning rules, such as to keep record of the rule edge coordinates encountered on a page, and keep record as well of the pixel coordinates used (we need all the four per rule). Then, if a new rule comes along sharing two adjacent of the four coordinates, use the stored pixel roundings instead.