Follow-up Comment #4, bug #68671 (group groff): On Thursday, 3 September 2026 04:07:01 British Summer Time you wrote: > Follow-up Comment #1, bug #68671 (group groff): > > Hi Deri, > > [comment #0 original submission:] > >> The issue is that groff has 2 ways of setting the line width:- >> >> Mode 1 (the original - used by tbl if you include linesize(n)) is to make >> lines proportional to the current font size. > > This is a dusty corner for me, but as I understand it, that is supposed to > be what a negative line thickness means. This quantity is not tracked in > the formatter _except_ as a drawing command parameter.
Not quite. Tbl's linesize outputs a '\s[nn]...draw...\s0' each time it wants
to draw a line. The 'nn' is controlled by 'linesize'. So it is assuming the
output driver is in mode 1 (width controlled by font size). You can't have a
negative font size. You can have a negative \D'-1' line thickness, which has
the special meaning for the output driver to now use mode 1.
This is code from grops:-
void ps_printer::set_line_thickness_and_color(const environment *env)
{
if (line_thickness < 0) {
if (output_draw_point_size != env->size) {
// we ought to check for overflow here
int lw = ((font::res / (72 * font::sizescale))
* linewidth
* env->size)
/ 1000;
out.put_fix_number(lw)
.put_symbol("LW");
output_draw_point_size = env->size;
output_line_thickness = -1;
}
}
else {
if (output_line_thickness != line_thickness) {
out.put_fix_number(line_thickness)
.put_symbol("LW");
output_line_thickness = line_thickness;
output_draw_point_size = -1;
}
}
line_thickness: Set by 'Dt' (Default -1)
output_line_thickness: Current mode 2 thickness
env->size: Set by 'snnnn'
output_draw_pointsize: Current mode 1 thickness
In each mode, the others 'output_' var is set to -1, so that if mode changes a
new postscript LW command is certain.
> _groff_(7):
>
> Drawing commands
> ...
> Circles, ellipses, and polygons can be drawn stroked or filled.
> These are independent properties; if you want a filled, stroked
> figure, you must draw the same figure twice using each drawing
> command. A filled figure is always smaller than an outlined one
> because the former is drawn only within its defined area, whereas
> strokes have a line thickness (set with \D't').
> ...
> \D't n'
> Set stroke thickness of geometric objects to to n basic
> units. A zero n selects the minimum supported thickness. A
> negative n selects a thickness proportional to the type
> size; this is the default.
> ...
>
>
> (I see a doubled word typo. Fixing in my working copy.)
>
>> Mode 2 (a groff extension) is to use the drawing command \D't n'
>> (millipoints).
>>
>> grops starts up in mode 1 (lines are proportional to current font size)
>> the
>> first time you used \D't nnn' it switched to mode 2 and font size changes
>> do not affect line width. The only way to switch back to mode 1 is to
>> issue \D't -1'.
>
> I don't think GNU _troff_ has any notion of "mode 1" or "mode 2".
It is to save bloody typing "proportional to current type size" (M1) or "set
by \D't'" (M2), all over the place. I hope you don't mind.
troff knows values set by \D't' and \s but it does not care what the output
drivers do with them, although current horizontal position is tracked but I
don't think it takes into account width of stroked vertical lines.
+verbatim+
printf "\D't 0'\n.sp 1i\nHullo\\\v'-.5c'\D'l 0 1c'\\\v'-.5c'World\n\D't
6000'\n.sp 2\nHullo\\\v'-.5c'\D'l 0 1c'\\\v'-.5c'World\n"|test-groff -Tpdf|
okular -
This shows the linewidth is not taken into account by troff when tracking
horizontal position.
> I think what you are observing is the product of a few interacting
> phenomena:
What I am observing, is a difference in which mode is the default start up
mode grops v. gropdf. Nothing at all to do with troff!
> 1. The formatter doesn't even _have_ a "line thickness" property; this is a
> property of some (not all) output drivers, exposed only via the 't' drawing
> command.
> [https://cgit.git.savannah.gnu.org/cgit/groff.git/tree/src/roff/troff/input.
> cpp?h=1.24.1#n10406 You can see here that GNU _troff_ has no special logic
> to interpret a 't' drawing
> command]--[https://cgit.git.savannah.gnu.org/cgit/groff.git/tree/src/roff/tr
> off/input.cpp?h=1.24.1#n10523 the formatter blindly passes it to the output
> device, the same as any extension.]
> And `t` **is** in fact an extension;
> CSTR #54 makes no mention of it (screenshot attached).
Which is why I wrote (above) "Mode 2 (a groff extension)".
> I perceive a possible documentation gap here; I suspect that the man page of
> every output driver that implements the `t` drawing extension command
> should document its semantics, and those semantics should be as described
> above for internal consistency.
If the current output drivers which stroke lines all do the same thing the
current documentationb is adequate.
> 2. GNU _tbl_ does not bother to configure a line thickness unless the user
> specifies the `linesize()` region option.
>
> _tbl_(1):
>
> linesize(n) Draw lines or rules (e.g., from box) with a
> thickness of n points. The default is the current
> type size when the region begins. This option has
> no effect on terminal devices.
>
>
> There may be a problem here. The default isn't in fact the **formatter**'s
> default (what is described here), but the formatter's **current state**.
> Even more precisely, it's the **output driver**'s current state, which, if
> it supports the `t` drawing extension command at all, we expect it to
> absorb and interpret as we document.
>
> As evidence, here's a simple boxed table as prepared by GNU _tbl_, with all
> the drawing commands filtered:
>
>
> $ printf '.sp 1i\n.TS\nbox;\nL.\nfoo\n.TE\n' | tbl | grep D
> \h'|\n[3cd0]u'\D'l |\n[3cd1]u 0'\c
> \v'.25m'\h'\n[3cd1]u'\s[\n[3lps]]\D'l 0 |\n[3rt0]u-1v'\s0
> \v'.25m'\h'\n[3cd0]u'\s[\n[3lps]]\D'l 0 |\n[3rt0]u-1v'\s0
> \h'|\n[3cd0]u'\D'l |\n[3cd1]u 0'\c
>
>
> Not a `\D't'` in sight.
Yes, it is assuming the output driver is in Mode 1 (notice the \s[\n[31ps]]/
\s0 pairs around the \D'l' commands). From this I would expect \n[31ps] is
calculated from 'linesize'.
> If the user throws down a `\D't 24'` right before a `.TS`, and does not use
> the `linesize()` region option, any rules in the ensuing table gonna be
> thicc.
Partially correct, I think you mean \D't 2400' (millipoints), and,
particularly, 'linesize' is completely ignored if you use it.
>
> I tentatively conclude that GNU _tbl_'s implementation is correct and the
> documentation wrong. But we could change tbl to save the configured line
> thickness when entering the table region, explicitly set it (to '-1' if not
> user-specified), and then restore the saved line thickness when exiting the
> table region.
The documentation for tbl could be improved by saying that linesize only works
if you have not used \D't' anywhere before the table and mention that issueing
\D't -1' before the table will restore linesize working.
> 3. Judging by your file #58940 LineThk-examples2.pdf exhibit, it appears to
> me that the vinculum in an _eqn_ fraction is thicker when the equation is
> set inside a _pic_ box than when the equation is not thus wrapped. That
> seems inconsistent, and potentially a bug (or a "NEWS"-worthy feature
> change if we alter it).
Is the "vinculum" the fraction line? If so they are the same width in both
formulas. What is "odd" is the tail of the first arrow is thicker than the
second arrow, that is, until you realise that the font size at the start of
the picture (10.95: PICTURE) is different to the fontsize used in the equation
(10: H(ω)) so the proportional line thickness would change.
> 4. GNU _pic_ steps on the line thickness, explicitly setting it to '-1'.
> Like GNU _tbl_, it performs no save/restore dance.
>
It lookks like (something) already does that:-
printf '.PS\ncircle "dig here";\n.PE' | pic | test-groff -Z
x T ps
x res 72000 1 1
x init
p1
DFd
V12000
H108000
n12000 0
md
V12000
H72000
s10000
Dt -1000 0 <----
h1000
n12000 0
V30000
H72000
Dc 36000
n12000 0
x font 5 TR
f5
V32200
H73755
tdig
wh2500
there
n12000 0
x trailer
V792000
x stop
> $ printf '.PS\ncircle "dig here";\n.PE' | pic | grep D
> \D't -1.000p'\h'1.000p'
> \h'0.000i'\v'0.250i'\D'c 0.500i'
>
>
> These 4 items are all potentially future tickets. I welcome views and other
> feedback on them.
>
>> gropdf starts up in mode 2.
>>
>> The attached patch fixes gropdf to behave like grops.
>
> I think this is a sound change.
>
>> The attached pdf shows the result of tests with postscript and pdf side by
>> side after the patch applied.
>
> Looks good to me!
>
>> As we are currently in code freeze it will be applied after 1.24.0.
>
> The 1.25 RC process has been hard on our code freeze, and a code freeze
> doesn't categorically prohibit bug fixes.
>
> Since this defect (a) has low transitive fallout (a _gropdf_ bug can't
> create many problems elsewhere, unlike a formatter or preprocessor change);
> (b) is a long-standing problem affecting several _groff_ releases; and (c)
> has had real users complaining about it vs. being detected only by internal
> auditing, I think it's well worth committing to the master branch before
> 1.25 final. Preferably before 1.25.0.rc3, which I **hope** to tag this
> weekend, so that we can get it into folks' hot sweaty hands and increase
> our odds of catching any undesired side effects. (Hard to imagine,
> though--at least none that aren't already in play due to the
> ill-definedness outlined by the 4 potential new tickets I listed.)
>
> But if you want to be more conservative than that with _gropdf_, I'll
> understand.
>
> Want to go for it?
>
Yes please.
Cheers
Deri
_______________________________________________________
Reply to this item at:
<https://savannah.gnu.org/bugs/?68671>
_______________________________________________
Message sent via Savannah
https://savannah.gnu.org/
signature.asc
Description: PGP signature
