n-t-roff / heirloom-doctools

The Heirloom Documentation Tools: troff, nroff, and related utilities
http://n-t-roff.github.io/heirloom/doctools.html
Other
127 stars 23 forks source link

.fzoom value not applied to word space #86

Open schickele opened 5 years ago

schickele commented 5 years ago

Hello,

I've discovered Heirloom troff recently and I'm really loving it so far. While trying to use different fonts together, I've noticed that the .fzoom value is not applied to the word space in the output:

.do xflag 3
.spacewidth
.ds test Test Test Test Test Test Test Test Test |
.nf
.nm 1
.ft HB
\*[test] \fC.fzoom 1.0 (10pt)\fP
.ps 7.5
\*[test] \fC.ps 7.5 / .fzoom 1.0 \fP
.ps 10
.fzoom HB 0.75
.ft HB
\*[test] \fC.ps 10 / .fzoom .75\fP
.ps 10
.fzoom HB 0.75
.fspacewidth HB 208.5
\*[test] \fC.ps 10 / .fzoom .75 / .fspacewidth HB 208.5\fP
.fspacewidth HB
\*[test] \fC.ps 10 / .fzoom .75 / .fspacewidth HB\fP

fzoom_spacewidth_on

Line 2 in the output is what you want, line 3 is what you get instead. The word spaces are not shrunk correctly. There is a work-around: setting the word space manually with .fspacewidth (line 4). Invoking .fspacewidth with only one argument, as suggested in the manual on p. 23 (rev. Jan. 2019), does not solve the problem (line 5).

Without .spacewidth in the preamble, troff does not use the space width of the font, as far as I can tell:

fzoom_spacewidth_off

Not sure if this is a real issue -- perhaps I'm just missing something.

Greetings from Leimen, Germany!

Paul

reffort commented 5 years ago

Hi Paul,

This characteristic of .fzoom doesn't seem to be documented anywhere, but I am sure the behavior is intentional. In running text the space sizes need to match, and doing that is straightforward if it isn't necessary to compensate for the zoom factor.

Consider a case where the base font is Times, with Helvetica, acronym caps, fake small caps, and a typewriter font used for inline code:

.fzoom H 0.85\"  match x-height for Helvetica
.fzoom A 0.9\"   acronym cap height (roman font)
.fzoom SC 0.82\" fake small caps (roman font)
.fzoom T 0.925\" match x-height for typewriter font

Times has a font space width of 250 (1/4 em). Regardless of the .fzoom setting, all the space widths can be made to match using:

.fspacewidth R\" 250
.fspacewidth H 250
.fspacewidth A 250
.fspacewidth SC 250
.fspacewidth T 250

But if the space width were to change with the zoom factor, it would be necessary to set the space widths to:

.fspacewidth H 294\"  250/0.85
.fspacewidth A 278\"  250/0.9
.fspacewidth SC 305\" 250/0.82
.fspacewidth T 270\"  250/0.925

in order to get actual spacewidths of 250.

On the second item, .spacewidth does need to occur before the fonts are mounted. This doesn't seem to be documented, either. (Alternately, use .fspacewidth F on a per-font basis).

If there is agreement, I could add those two things to the users' manual, since I'm already working with it.

schickele commented 5 years ago

Hi reffort,

Thanks a lot for your reply. You've added some certainty to my suspicions. Yes, more documentation on the topic would be great.

I can understand that, in running text, the spaces on a given line need to be equal, even with different fonts. However, if you use your auxiliary fonts in separate paragraphs or in the headings (other typical user case scenarios I would assume), ignoring the zoom correction or their internal space width entirely makes less sense. Hence my question.

More on the practical side, is there a way to set up the space width of your additional fonts globally without having to look into their metrics? I can define a little macro for each font with an individual point size:

.do xflag 3
.spacewidth
.\" AF: Additional font with custom size
.de AF
.ps 8.5
.fzoom HB 1.0
.ft HB
..
.\" NF: Regular font in normal size
.de NF
.ps 10
.ft R
..
.ds test Test Test Test Test Test Test Test Test |
.nf
.nm 1
\*[test] \fC(.ft R)\fP
.ft HB
\*[test] \fC.ft HB\fP
.ps 10
.fzoom HB .85
\*[test] \fC.ps 10 / .fzoom HB .85\fP
.AF
\*[test] \fC.AF\fP
.NF
\*[test] \fC.NF\fP

screenshot_20190227_154448

but I would rather just use the .ft and the inline \f requests.

Sorry if this becomes more of a beginner how-to.

reffort commented 5 years ago

You can write a macro to set the fzoom and calculate the spacewidths at the same time. One approach would be to set any number of fonts to the same zoom factor and scale the font file's spacewidth accordingly. The following example would set the font spacewidth and zoom factor for the Helvetica and Courier font families:

.setfzsw 0.85 H HI HB HX
.setfzsw 1.07 CW CI CB CX

You could then construct a sentence like this:

\f[R]The quick brown fox\f[H] jumps \f[HB]over \fPthe\f[CW] lazy dog's\f[R] sphinx of quartz.

and the x-heights would match, and the space sizes would be scaled from each font file's space width.

The macro would look something like:

.de setfzsw
.   lnr fpsav \\n[.f]
.   lnrf pssav \\n[.s]
.   lnr sssav \\n[.ss]
.   lnrf fz \\$1
.   shift
.   ss 12
.   ps 1
.   while \\n(.$ \{\
.       ft \\$1
.       fspacewidth \\$1
.       lnr fsw \w' '
.       lnrf zsw \\n[fsw]*\\n[fz]
.       fspacewidth \\$1 \\n[zsw]
.       fzoom \\$1 \\n[fz]
.       tm \\$1 fsw=\\n[fsw] ; fz=\\n[fz] ; fzsw=\\n[zsw]
.       shift
.   \}
.   ft \\n[fpsav]
.   ps \\n[pssav]
.   ss \\n[sssav]
..

(The macro assumes the use of the default high-resolution device driver. This macro could also be executed in an environment and the save and restore steps eliminated.)

A test file might be:

.do xflag 3
.
.\" insert fzsw here
.
.de showspacewidth
.   tm -------
.   while \\n(.$ \{\
.       ft \\$1
.       lnr sw \s[1]\w' '\s0
.       lnrf fz \\n[.fzoom]
.       tmc \\$1 spacewidth = \\n[sw] ; 
.       tm fzoom = \\n[.fzoom]
.       shift
.   \}
..
.fspacewidth R
.
.\" set the zoom and spacewidths
.setfzsw 0.85 H HI HB HX
.setfzsw 1.07 CW CI CB CX
.
.\" show the results
.showspacewidth R I B
.showspacewidth H HI HB HX
.showspacewidth CW CI CB CX

Run it with:

troff testfile.tr > /dev/null

You should get a series of terminal messages like this:

H fsw=278 ; fz=0.85 ; fzsw=236.3
HI fsw=278 ; fz=0.85 ; fzsw=236.3
HB fsw=278 ; fz=0.85 ; fzsw=236.3
HX fsw=278 ; fz=0.85 ; fzsw=236.3
CW fsw=600 ; fz=1.07 ; fzsw=642
CI fsw=600 ; fz=1.07 ; fzsw=642
CB fsw=600 ; fz=1.07 ; fzsw=642
CX fsw=600 ; fz=1.07 ; fzsw=642
-------
R spacewidth = 250 ; fzoom = 0
I spacewidth = 333 ; fzoom = 0
B spacewidth = 333 ; fzoom = 0
-------
H spacewidth = 236 ; fzoom = 0.85
HI spacewidth = 236 ; fzoom = 0.85
HB spacewidth = 236 ; fzoom = 0.85
HX spacewidth = 236 ; fzoom = 0.85
-------
CW spacewidth = 642 ; fzoom = 1.07
CI spacewidth = 642 ; fzoom = 1.07
CB spacewidth = 642 ; fzoom = 1.07
CX spacewidth = 642 ; fzoom = 1.07

The values listed for each font in the first group are: the space width defined in the font file, the requested zoom factor, and the adjusted space width. The other groups list the actual space width (in milli-ems) and actual fzoom value that have been set.

There is an undocumented curve ball: When the value of the .fzoom read-only number register is zero, that means .fzoom has not been set or the zoom is turned off; setting .fzoom 0 turns off the zoom. The documentation states that this number is always positive, so one might reasonably expect a value of 1.0 as the normal value instead of 0.

There is also another bug to add to the list: The floating point if request .if f does not work, so if you modify the macro and want to test \n[.fzoom] for zero, you will have to multiply it (by 1000, say) and test it as an integer.

is there a way to set up the space width of your additional fonts globally without having to look into their metrics?

I'm not sure I fully understand the question, but the .setfzsw macro does something like that.

If you are asking about determining the space width without reading the font's .afm file in a text editor, the first three lines after the .while request in the .setfzsw macro will do that.

If you are asking about just setting several space widths at the same time, I use a macro that is similar to the way .setfzsw works, but is much simpler because it only sets the space widths:

.setspacewidth 236 R I B SC T ...

If you are asking about how to determine the zoom factors, I do not know of a way to get consistently accurate numbers for the x-height without looking at the font in a font editor. Troff does evaluate vertical sizes as a side effect of the \w function, but I have not had good enough results across the different file types (.pfb, .ttf, .otf). Type 1 fonts seem to produce the best results. The measurement of cap height usually works well enough to calculate the size of drop caps (with a few tweaks here and there).

Alhadis commented 5 years ago

I do not know of a way to get consistently accurate numbers for the x-height without looking at the font in a font editor.

Isn't Heirloom Troff using Adobe Font Metrics? If so, the AFM format defines a field for declaring the font's XHeight:

CapHeight 739
XHeight 554
Descender -185
Ascender 739
StartCharMetrics 316
C 32 ; WX 280 ; N space ; B 0 0 0 0 ;
C 33 ; WX 280 ; N exclam ; B 72 0 205 739 ;
schickele commented 5 years ago

@reffort: Thank you very much for this very thorough answer. I didn't except that much. Your macro works like a charm:

.do xflag 3
.hylang la_VA
.fp 1 R GaramondURW-Reg-hinted ttf
.fp 3 B TheSansOfficeLF-Bold ttf
.fp 0 C PTM55F ttf
.\" <fzsw macro by reffort here>
.fspacewidth R
.ds test Test Test Test Test Test Test Test Test |
.nf
.nm 1
\*[test] \fC(.ft R)\fP
.ft B
.ps 10
.fzoom B .84
\*[test] \fC.ps 10 / .fzoom B .84\fP
.setfzsw .84 B C
.ft B
\*[test] \fC with fzsw macro\fR
.nm
.SH
Sample text
.LP
Lorem ipsum, quia \fBdolor\fP sit, amet, consectetur, adipisci velit, sed quia
non numquam eius modi tempora incidunt, \fBut labore et dolore magnam aliquam
quaerat voluptatem.\fP Ut enim ad minima veniam, quis nostrum exercitationem
ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur?

screenshot_20190301_222959 (with troff -ms)

I especially like the way you can define the zoom factor for multiple fonts at once.

You're right, my question should have been: "How can I have access to the space width of a font in troff without having to use an external tool?" Your script does pretty much exactly that, in a very neat way. But as you pointed out yourself, this becomes a bit irrelevant with your fzsw macro.

As far as the x-height and the actual size correction go, I can use other tools I'm familiar with to determine them, so no big deal there. I'll try to investigate that \w command more though.

I hope that you didn't spend too much time on this. If for some reason, you're planning to rewrite the font handling paper of Gunnar, this macro should definitively be in it.

@Alhadis: Yes, all the metrics of the default fonts are stored in AFM files under /usr/local/ucblib/doctools/font/devps/.

reffort commented 5 years ago

@alhadis - Yes, the metrics for Type 1 fonts come from the .afm files, so grepping the .afm file would work for any Type 1 font. The .afm metrics can also be applied to metrics-compatible fonts, like Arial (.ttf) and the TeX Gyre .otf series. Troff does return usable vertical size numbers for Type 1 fonts. Unfortunately, the numbers it returns for .ttf and .otf fonts are not generally reliable. (Getting reliable vertical metrics is on my list of font-handling fixes.)

@schickele - Good to hear it does the job. Most of the time spent was due to getting an unexpected zero from the \n[.fzoom] register (and having to figure out it was an actual undocumented feature), the .if f statement not working, and fixing typos in the descriptive text.

I think it could worth while to add a new request that zooms the space width along with the character size, called .fzoomsw or something like that. The application that pops into mind is for multi-line code samples, where it is important to keep the monospaced characteristic but the font's overall size needs to be scaled. Other software does it this way, too: InDesign, for instance, scales the space width when adjusting the horizontal scaling.

schickele commented 5 years ago

I would welcome such an addition, even though your solution seems perfect right now. It would not change anything to the existent troff, just add a practical little tool to make font setups smoother. In fact, I stumbled upon this space width behavior while trying to mount a monospaced font, when I saw that the word space wasn't aligned to the characters.