Hi Branden,
G. Branden Robinson wrote on Sun, Jul 26, 2026 at 04:40:39PM -0500:
> mandoc(1) advocates frequently boast of its speed relative to groff,
> and they have a point.
This is not particularly accurate. I only boast that mandoc(1) -T ascii
and -T utf8 rendering is significantly faster than groff(1)'s, where the
comparison makes sense because the output is intended (with few
exceptions) to be byte-by-byte identical.
> I can rig up a rough apples-to-apples comparison by
> having that tool produce a PDF of all of groff's man pages.
This makes no sense whatsoever. It is still comparing apples to oranges.
The -T ps and -T pdf formatters of groff(1) are real typesetters.
In mandoc(1), they are not. What mandoc(1) -T ps produces is
basically still terminal output in PostScript format, with a few very
minimal, very basic beautifications, but absolutely not real typesetting
in any sense. In mandoc(1), -T pdf output is functionally identical
to -T ps output, profiting from none of the distinctive features of
the PDF language.
You can certainly use "mandoc -T ps" or -T pdf if for some reason,
you find it subjectively more readable than terminal output, but
comparing the speed_of something that is massively defective in
many respects is not useful.
I always say that if you need real PostScript or PDF output, use groff(1),
not mandoc(1). mandoc -T ps was basically an experiment of Kristaps'
more than a decade ago, and has only received minimal maintenance
ever since. The source file of the formatter is called "term_ps.c"
for a reason: it is a thin layer over the terminal formatter.
A fairer comparison would be to compare "mandoc -T pdf" speed
to "groff -T ascii" speed (still not a useful comparison, but at
least slightly more fair).
Probably, the mandoc(1) manual should warn that -T ps and -T pdf
are not indended as serious typesetting and should warn against their
use for most purposes.
> 'Course, there are important caveats. Browsing through the output, I
> have some observations.
>
> * mandoc(1)'s output is not hyperlinked at all, neither internally nor
> to external URLs.
True. Isn't that feature specific to PDF and absent from PostScript?
The fundamental software architecture of mandoc(1) prevents using any
PDF features not provided by PostScript. That would need a
completely new formatting layer term_pdf.c, which does not exist.
> * Because there are no internal hyperlinks, mandoc(1) didn't need to
> perform multiple passes. That virtually guarantees a win on elapsed
> time to generate the document.
The fundamental architecture of mandoc(1) never does multiple
formatting passes. Instead, it builds an abstract syntax tree (AST)
of macros and requests, than transforms that AST with so-called
validation modules, which are independent of the output format. In
addition to regularizing the AST, the validators adds information
to the AST the any output module might need. While it would be
possible to add PDF-specific information to the AST at the validation
stage, nothing of the kind is done.
You might argue that mandoc(1)'s is a four-pass architecture in the
first place (first pass: the parsers; second and third passes: the
validators; fourth pass: the formatters) but that feels sophistic.
Groff similarly does multiple passes over the document, albeit
different ones (macro preprocessing, troff parsing, output
postprocessing), even in single-pass mode.
> * There's no navigation pane. Wanna navigate the document? Key in a
> page number and play Battleship to find the material you seek, or work
> that scroll bar, plebe.
Again, isn't that a PDF rather than PostScript feature?
> * In my opinion, mandoc(1)'s output is esthetically inferior,
I strongly agree, and it's no surprise because no work was ever done
to achieve good typography. The whole point was whether it is
feasible to create syntactically valid PostScript code from a
terminal formatter with limited effort, and the answer turned out
to be "yes, to a certain extent."
> with the possible exception of the base type size being larger
> and thus arguably gentler on the reader.
>
> * On the other hand, that decision swells the document to 675 pages.
>
> * We can _easily_ increase the base type size from 10p to 11p or 12p if
> we decide that's a good thing to do. I predict the impact on
> production time to be negligible. (Worth testing, though!)
I don't think it's worth wasting time on testing that.
For comparison, mandoc(1) does not even provide any way to change
the type size, neither via command line options, configuration
files, or in-document requests or macros. Real typography is simply
out of scope. The roff(7) manual distributed with mandoc(1) says,
for example:
ps [[+|-]size]
Change point size. Currently ignored.
\s'[+|-]number'
Change point size; ignored by mandoc(1). Alternative forms
\s[+|-]n, \s[+|-]'number', \s[[+|-]number], and \s[+|-][number]
are also parsed and ignored.
> * mandoc(1)'s bullet glyphs are ugly--they're an 'o' overstruck with a
> '+', to me, the glyphs seem to lack a precisely identical center.
Well, not only is mandoc(1) PostScript outpout based on terminal output,
but on -T ascii output specifically (and not on -T utf8). The only
fonts it can use are 96-glyph Times ASCII fonts. Again, not
intended for real typography.
> * Similar ugliness afflicts groff_mmse(7) (p. 78). Notice how ö is
> rendered.
>
> * Neutral apostrophes ' render incorrectly as ’ and copy-and-paste
> that way, too.
>
> * A hyphen-minus - _renders_ as a hyphen ‐, but copies and pastes
> correctly.
>
> * ± renders unhelpfully as "+-" (p. 144).
>
> * Between bad table layout (see below), refusal to format non-Basic
> Latin glyphs, and 1970s line-printer-style overstriking, the entire
> groff_char(7) document (pp. 187-222) is an utter disaster.
All that sounds related to being an ASCII rather than a UTF-8 output mode.
> * I see errors (missing space between words) in synopsis formatting;
> mandoc(1)'s support for the groff man `SY` and `YS` extensions may be
> lacking--or, since mandoc(1) hasn't had a release in a few years,
> those flaws might be fixed in its "HEAD".[A]
If that is reproducible in -T ascii mode, it's possibly a .SY formatting
bug in the man_term.c formatter. If not, i'd be somewhat surprised.
> * To my surprise, example displays are not set in a monospaced font.
Yes, i think there is even a TODO entry for that. It's a major task
though since right now, mandoc(1) -T ps only supports four fonts:
Times-Roman, Times-Bold, Times-Italic, Times-BoldItalic.
Again, never intended as real typography.
> People have ridden our rears about failing to do that when generating
> HTML,[B] and traditionally mdoc(7) mavens are proud to a fault of
> spattering their documents with Courier like cream pie filling at the
> end of a Keystone Kops short feature.
>
> * Table layout is poor. See, e.g., page 46.
I doubt that anyone has ever tested -T ps with tbl(7).
> + There's no separation between the columns.
>
> + The existence of the `BI` (bold-italic) font is ignored.
>
> + The width of text blocks is computed strongly suboptimally. (The
> "x" column modifier appears to be completely ignored.)
>
> + Even worse, line wrapping is permitted before the period at the end
> of a sentence. That's _really_ bad, QA-wise. mandoc(1) doesn't
> seem to mess this up when formatting regular text; why would it
> here? Ahh, it may be a parser issue. The line that gets
> incorrectly broken is a macro call inside a text block.
> I@T{
> Indent text by
> .BR \[rs]n[Si] .
> T}
> I'll bet that a macro argument boundary is being misinterpreted as
> a break opportunity. This might be due to misinterpretation or
> misimplementation of the `\c` escape sequence. Or not, since
> mandoc(1) is not a *roff, and the "macros" it recognizes are not
> implemented atop a substrate of formatter requests and escape
> sequences.
See the tbl(7) manual page distributed with mandoc(1):
COMPATIBILITY
The mandoc(1) implementation of tbl doesn't support mdoc(7) and man(7)
macros and eqn(7) equations inside tables.
There is also a TODO entry for that, but supporting macros inside tbl(7)
is among the hardest TODOs that exist.
> + In fact, macro calls within tbl(1) text blocks seem to be largely
> ignored. Changes of typeface that should occur, don't. See, e.g.,
> the table on page 47.
>
> + Table columns are misaligned when the headings are in bold but the
> rest of the table is not. See, e.g., the table on page 49.
>
> + The table spanning pp. 128-129 is so badly damaged that it's nearly
> incomprehensible.
>
> + mandoc(1) oversets not just the line but the page when employing its
> "semantic" glyph fallbacks. See p. 220.
>
> * Some of the widow/orphan management is cringe, but ours often can be
> too--this is one reason people want Knuth-Plass. On the other hand,
> groff's man pages (and other documents) often mitigate this with
> careful use of `ne` requests and I think mandoc(1) might be ignoring
> these.
Yes. See roff(7) in the mandoc(1) package:
ne [height]
Declare the need for the specified minimum vertical space before
the next trap or the bottom of the page. Currently ignored.
The reason for ignoring that request is that it is very hard to
implement in mandoc(1) because it constitutes a fundamental layering
violation from the mandoc perspective. What .ne does is essentially
inserting an AST node (page break - which, to make matters worse,
is something that mandoc never does in the first place: mandoc(1)
does not use the concept of "output pages" in any way whatsoever)
depending on the output format. In mandoc(1), the AST validators
cannot consider what the output mode might later be, at least not
without massive contortions.
> So mandoc(1) makes our documents look worse than they should.
> (Update: Maybe not. I checked a few cases; I might be seeing more
> widows and orphans simply because the type size mandoc(1) uses is
> larger, creating more _opportunities_ for widows and orphans.)
>
> * mandoc(1), unsurprisingly, doesn't typeset pic(1) diagrams (p. 117).
> Unfortunately because it also doesn't set a man(7) `EX`/`EE` display
> in a monospaced typeface, the fallback ASCII art is mostly
> unintelligible.
>
> * mandoc(1) also doesn't typeset eqn(1) equations (pp. 408-409). I
> cannot tell if eqn production is _supposed_ to be supported or not;
> the man page disclaims eqn(1) support for its "-T man" and "-T
> markdown" output forms, while claiming it for "-T html" but is silent
> on the matter for "-T pdf".
Again, i don't think anybody ever considered or tested what -T ps
might do with eqn(1), in much the same way as it was never considered
or tested for tbl(1).
> * Separation of man(7) paragraph tags from the paragraph body is too
> narrow; see, e.g., the bottom of p. 144. To be fair, groff man(7)
> used to have this problem, too.[C]
>
> * I observe that the mandoc(1)-generated PDF is 11 MB; ours is 1.6 MB.
> Bad old bloated GNU stuff once again, eh?[D]
Heh. Of course not. But that mandoc(1) produces unnecessarily large
PostScript output is simply due to it having a poor understanding of
the PostScript langauge in the first place, and doing everything in
an incredibly pedestrian way.
> I terminated my survey (except to peek ahead at eqn(1)) at page 223 of
> 675. I think I've seen enough to produce an informed opinion.
>
> I've attached the respective PDF documents. The different _ordering_ of
> the man page documents within is not mandoc(1)'s fault but merely an
> artifact of my laziness in employing a shell variable as shown above.
>
> I don't expect any of these observations to impact BSD/mdoc/mandoc(1)
> partisans' assessment of its superiority to groff in any way. It:
> * goes fast;
I do *not* claim that mandoc(1) is superior to groff(1).
What i claim is:
1. mandoc(1) output quality is about equal to groff(1) for -T ascii
and -T utf8, and mandoc is significantly faster in those modes.
2. mandoc(1) output quality is vastly superior to groff(1) for -T html,
to the point that groff cannot really be takes seriously in this
respect.
3. groff(1) output quality is vastly superior to mandoc(1) for -T ps
and -T pdf, to the point that mandoc cannot really be taken
seriously in this respect.
> * isn't copylefted;
> * isn't in C++.
While that is true, it is completely immaterial when it comes to real
typesetting: ability to do the job matters way more than any personal
preferance regarding programming languages or licenses.
> Whether mandoc(1) faithfully manifests the principle of the Unix
> philosophy holding that a tool should "do one thing and do it well",
> I leave open to consideration.
I doesn't really adhere to that philosophy on a technical level;
groff is closer to that philosophy technically, in so far as mandoc
is one monolithic blob whereas groff (the package) consists of multiple
programs that each do one thing well and can be piped together.
That technical failure of mandoc at the Unix philosophy is in part
intentional because one can argue that there is no such failure on
a user-oriented level. From the user perspective, the one thing
is "read a manual" and the user should not need to care that this
one task can be broken down into multiple subtasks of source file
searching by man(1), preprocessing, formatting, and postprocessing.
Admittedly, the mandoc(1) command line options are easier to get
right than groff(1)'s - most of the time, mandoc(1) needs none
whatsoever. Than again, the man-db + groff(1) combo is far from
bad in this respect - no user needs to construct pipes by hand, the
man(1) and groff(1) wrapper programs handle all that reasonably
well, albeit groff(1) (but not man(1)) occasionally requires a few
arcane command line options.
Also, mandoc(1) adheres to the Unix one-task philosophy in so far
as the one thing it does is "reading manuals" but not "general
purpose typography" - which is obviously a downside at the same
time as allowing some simplification. And even that aspect is not
followed strictly, in part due to historical reasons: Kristaps is
an adventurous child who loves to play, so he wondered about
PostScript and added the -T ps mode (which is arguably out of scope
for are pure manual reader, and has consequently be much neglected).
I am guilty of adding even more stuff that has little to do with
the one task "read manuals", for example -T markdown output mode,
simply because i could never come up with a reasonable idea where
else to put such stuff, given that the mandoc(1) programs
(intentionally!) lacks a modular design on the command line level -
of course, the source code organization is modular just fine.
Yours,
Ingo