In a funny bit of coincidence I read a few different pages discussing Groff in the last week or two. While my previous experience has been limited to coercing man pages into shape I thought I might take the time to dig a little deeper and see how it compares to LaTeX (or what little LaTeX I have used).
Both LaTeX and Groff belong to a family of programs for typesetting documents, usually for documents like reports and books; they seem analogous to HTML. The written documents include formatting directives for laying out pages in a way that WYSIWYG editors do not.
Somewhat depressingly, my most-read document prepared in LaTeX is undoubtedly my resume. Keeping it up to date has been a bit of a pain because I don't really know much about the templates (styles?) that I use. Using premade styles probably impedes my actually learning much because it isn't clear where TeX ends LaTeX begins and the macros specific to the style in use extends things. Because the resume class/template/style that I am using is intended to be broadly reusable it contains a huge amount of stuff. It is about 800 lines long and I couldn't really tell you what is in it or even how to use it. If memory serves me, I think I cribbed from an example distributed with miktex and kept tweaking until I felt things were good enough.
Perhaps unsurprisingly, Groff seems to have similar issues. Typical
use seems to require macro packages,
either ms
, mm
, or mom
. To be
totally honest, the macros that are distributed with these packages
are pretty daunting if you come to them cold. As an example, this is
the macro definition for a bulleted list:
.de @IP
.if \\n[.$]>1 .nr \\n[.ev]:ai (n;\\$2)
.par*start \\n[\\n[.ev]:ai] 0
.if !'\\$1'' \{\
. \" Divert the label so as to freeze any spaces.
. di par*label
. par*push-tag-env
\&\\$1
. par*pop-tag-env
. di
. chop par*label
. ti -\\n[\\n[.ev]:ai]u
. ie \\n[dl]+1n<=\\n[\\n[.ev]:ai] \{\
. DEVTAG-COL 1
\\*[par*label]\h'|\\n[\\n[.ev]:ai]u'\c
. DEVTAG-COL 2
. \}
. el \{\
. DEVTAG-COL 1
\\*[par*label]
. DEVTAG-COL-NEXT 2
. br
. \}
. rm par*label
.\}
..
I think these standard Groff macros carry the same burden of having
to be both portable and widley reusable that LaTeX styles do. I have
found the documentation for Groff better though when using a
standard macro package like ms
(available via man
7 groff_ms
).
More importantly to me though was how easy I found it to write my own macros after doing the drudge work of laying out a page in a way that I liked. I just identified bits of repetition and then copy-pasted in my own macro definition to substitute. I won't claim to be a good Groff macro author but I think it doesn't matter so much in this context. Someone could conceivably duplicate my setup by copy-pasting the macros but why would they bother? Writing Groff like this seems like an appropriate instance of specialization that I'm happy to maintain myself. For instance, "building" the document is handled by a Makefile:
resume.ps: resume.ms resume.tmac
groff -rHY=0 -k -ms resume.ms > resume.ps
resume.pdf: resume.ps
ps2pdf resume.ps resume.pdf
.PHONY: clean
clean:
rm *.pdf *.ps
For my resume the body of the Groff document ends up looking like this:
.so resume.tmac
.nameinfo "Nolan Prescott" "nprescott.com \(en mail@nprescott.com"
.B "Experience"
.sp -1
.job \
"Senior Software Engineer" \
"HackEDU" \
"Dec. 2021 - Present" \
"Best in class interactive cybersecurity training to companies ranging from startups to the Fortune 5"
.bullet "Customer-facing platform of development sandboxes built on Kubernetes using Python, Go, and AWS"
.job \
"Software Engineer" \
"Nuix" \
"Jul. 2020 - Dec. 2021" \
"Innovative electronic discovery software that empowers organizations to quickly find the truth from any data"
.bullet "Large-scale data ingestion for electronic discovery and information retrieval using C# and T-SQL"
.bullet "Parsing, normalization, and analysis of a wide variety of digital formats for investigation by legal professionals"
.job \
"Software Engineer" \
"RoBotany" \
"Sep. 2019 - Mar. 2020" \
"A modern agriculture company leveraging automation and software analytics to improve efficiency and crop output"
.bullet "Built software controls for the hardware systems in the company's first production-scale facility"
.bullet "Integrated Modbus controls alongside a stream-oriented industrial internet of things architecture using TypeScript, NodeJS, and Redis"
.job \
"Software Engineer" \
"Seegrid" \
"Oct. 2016 - Sep. 2019" \
"Vision-guided, autonomous vehicles for materials handling in manufacturing and distribution facilities around the world"
.bullet "Development of an inference engine to enable building customized applications through business-centric rule definitions"
.bullet "Autonomous robot fleet coordination, including traffic management and task delegation"
.job \
"Engineer" \
"PROS Inc." \
"Feb. 2013 - Aug. 2015" \
"A revenue management and price optimization company developing innovative pricing science and data analytics"
.bullet "Developed a predictive analytics service to identify sales opportunities using machine learning"
.B "Education"
.sp -1
.nudge
Bachelor of Science, Texas Tech University
.br
Lubbock, Texas
In the above
the .nameinfo
, .job
, .bullet
,
and .nudge
macros were all written by me. I find it
satisfying that the bulk of the document is concerned with the
content rather than the layout. There are a few oddities, like the
spacing around the "Education" and "Experience" labels. I have not
worked out the optimum way of creating the sort of layout I like,
which is a bit like a two-column layout but with uneven widths and
varying amounts of text. I found judicious use of indents worked
just fine and while authoring the document it mostly doesn't matter
because the inner working gets wrapped up in
the .job
and .bullet
macros.
Here are the macro definitions I ended up writing:
.de nudge
.in 1.25i
..
.de bullet
.RS
.RS
.RS
.RS
.IP \(bu 2
.sp -1
\\$1
.sp -5p
.RE
.RE
.RE
.RE
.LP
..
.de rdate
.rj
.sp -1
\\$1
.sp 0.5
..
.de job
.nudge
.B "\\$1"\c
, \\$2
.rdate "\\$3"
.sp 0.5m
\\$4
.sp 0.25m
..
.de nameinfo
.ps 24
.ce 3
\\$1
.sp -8p
.ps
.sp 0.75m
\D'l 6i 0 '
.sp 1.5m
\\$2
.sp 1m
..
I am pleasantly surprised to find I was able to recreate my LaTeX resume in Groff with a minimum of work. The LaTex version is seen here on the left, the new Groff version on the right:
Based on how quickly I was able to put together something based only
on a reference document and the manual I can see reaching for Groff
in the future if I find myself writing a more formal document than
something like HTML will allow. This is a nice change of pace
compared to LaTeX, where I own Lamport's LaTeX, A Document
Preparation System and still don't really feel like I
understand it. I feel pretty confident that I can take my 45 lines
of macros and get back up to speed on any system that supports
the ms
macro package (which I think dates back to the
1970s). Changes are easy and there is very little to understand
deeply. I like it!
As far as I know, Groff supports more output targets than LaTeX so something like this is reasonably easy to achieve:
───────────────────────Nolan─Prescott────────────────────────
nprescott.com – mail@nprescott.com
Experience Senior Software Engineer, HackEDU Dec. 2021 ‐ Present
Best in class interactive cybersecurity training to companies ranging from
startups to the Fortune 5
• Customer‐facing platform of development sandboxes built on
Kubernetes using Python, Go, and AWS
Software Engineer, Nuix Jul. 2020 ‐ Dec. 2021
Innovative electronic discovery software that empowers organizations to
quickly find the truth from any data
• Large‐scale data ingestion for electronic discovery and information
retrieval using C# and T‐SQL
• Parsing, normalization, and analysis of a wide variety of digital
formats for investigation by legal professionals
Software Engineer, RoBotany Sep. 2019 ‐ Mar. 2020
A modern agriculture company leveraging automation and software analytics to
improve efficiency and crop output
• Built software controls for the hardware systems in the company’s
first production‐scale facility
• Integrated Modbus controls alongside a stream‐oriented industrial
internet of things architecture using TypeScript, NodeJS, and Redis
Software Engineer, Seegrid Oct. 2016 ‐ Sep. 2019
Vision‐guided, autonomous vehicles for materials handling in manufacturing and
distribution facilities around the world
• Development of an inference engine to enable building customized
applications through business‐centric rule definitions
• Autonomous robot fleet coordination, including traffic management
and task delegation
Engineer, PROS Inc. Feb. 2013 ‐ Aug. 2015
A revenue management and price optimization company developing innovative
pricing science and data analytics
• Developed a predictive analytics service to identify sales
opportunities using machine learning
Education Bachelor of Science, Texas Tech University
Lubbock, Texas
I'm sure I'll forget these unless I make a note:
pdftoppm in.pdf out.png -png
montage -mode concatenate -tile 2x {1,2}.png compare.png
groff -rHY=0