leaflogo

Iowa Dave

Living and Learning

New Fonts for Old Groff

Introduction

Groff is a professional-grade typesetting program that often comes pre-installed with Unix-like operating systems such as Linux. It can produce publication-quality PDF documents from marked-up text, including math formulas and well-formatted tables.

I describe my initial experience with groff at a sibling web site, The Code Diarist. Here is a link to the article: To the Dawn of Unix... and Beyond!

Recently I wanted groff to use DejaVu Serif, a font that it did not already recognize. Making the new font available proved to be quite a learning process. I am glad to say that it became a quick and easy thing to do after study and some one-time preparation.

The result was a script that installs a new font for groff with a single command. This article shares what I learned. The script is provided at the end.

Many Linux distributions pre-install at least some parts of groff. For purposes of this article, you might need to obtain the full installation available in your distribution’s package repositories.

Ubuntu and the Raspberry Pi OS are based on Debian Linux, which uses the apt package manager. The command line instruction for installing the full groff package with apt is:

sudo apt install groff

Other Linux distributions may implement a different package manager. Apple Macs run a Unix-like operating system for which groff may also be available by means of a third-party package manager. I leave it to the reader to search the internet for those solutions.

Full installation usually brings with it a selection of font families specially prepared for groff in four styles: Roman (normal), Italic, Bold, and Bold Italic. In 2025, a Raspberry Pi OS package for groff, installed on a Model 5 Pi, included the following families:

That collection of fonts dates back to the mid-1980s when Apple and Adobe worked together to bring laser printers to the desktop publishing market. The Ubuntu 24.04 LTS distribution includes all of those also, plus the more recently issued URW Base35 set of fonts. Such a range of choices is likely to serve many purposes very well. Why add another? Why not? Maybe you merely want to, and you can.

groff can use just about any other font you might find for your computer. The Google Fonts collection comes to mind. For now, keep in mind that the font you choose to add may need to undergo some special preparation. Official documentation is rather sparse and murky on this subject, alas.

This article describes a procedure that works for me. I based it on a few, helpful online tutorials combined with some tricks of my own. The steps are described individually, in sequence. A script is provided at the end of the article which combines the steps into a single command.

Prepare storage for added fonts

We shall need to save our new font files somewhere. The apt package manager installs the prepackaged fonts into system folders that deny write privileges to ordinary users. It is possible, but needlessly tedious to add new fonts into the system folders. A bit of one-time work can give an easier way.

A tutorial I found online suggested creating a storage location in my user account’s home folder instead. I used the cd and the mkdir commands to establish the following, normally hidden storage path:

/home/user-name/.local/share/groff/site-font/

Replace user-name with your actual account name. Now, groff needs to be informed of this location.

Remain calm! Super-geeky content ahead. Be patient. Go slow. Get through it. The benefits will repay the effort.

Tell groff where to find added fonts

The way to do this is to assign the path’s name to a certain, particular environment variable, called GROFF_FONT_PATH.

This step assumes that groff will run in the bash shell. Locate the file named .bashrc in your user account’s home directory. Open this file in your favorite text editor. Scroll to the bottom and add the following on a new line there:

export GROFF_FONT_PATH='/home/user-name/.local/share/groff/site-font/'

Of course, replace the user-name bit with your actual user name. Save the file. In future, the variable will receive the desired value automatically when a new bash shell is opened.

Test this change to be sure. Close any open shell sessions then open a new shell. Type this command:

echo $GROFF_FONT_PATH

You will know you are on the right track when the path to the font storage location prints out.

One, final step remains to organize the font storage location.

I added the following two directories into that site-font folder: devps and devpdf, leaving both of those folders empty for the moment.

To be clear, I actually prepared two, distinct font storage locations. Their full path names are:

Fonts and other information that groff needs to know about the fonts will be put into each of the two folders during the steps that follow. Fonts in the devps folder are configured for producing PostScript documents. Those in the devpdf folder are configured differently for PDF output.

Make a working folder

I found it convenient to create a folder in which to store copies of font files temporarily during the conversion process. I named it fontshop and put it here:

/home/user-name/.local/share/groff/fontshop/

Within that folder I created links to two, informational files that need to be accessed during each font preparation procedure. These files are stored elsewhere, deep inside the system files for the groff installation. The links give them nice, short nicknames that are easier to use.

ln -s /usr/share/groff/1.22.4/font/devps/text.enc textenc

ln -s /usr/share/groff/1.22.4/font/devps/generate/textmap textmap

The two files provide character encoding and mapping information that groff needs.

Be aware that the 1.22.4 part of those paths is the groff version number. It might be different on your system, so please use yours if it is different.

Font Preparation Tools

The fontforge utility derives suitable font files for groff from TrueType font files. Install it with your system's package manager. Raspberry Pi OS does it this way:

sudo apt install fontforge

Verify also that the afmtodit utility is present on your system. It usually gets installed as part of the default package when groff is installed.

The foregoing steps complete the preparation of your system. They need to be performed just once. The steps which follow need to be repeated for each new font being added to groff.

Obtain the font files

Perhaps you want to enable groff to use a font already installed on your system. In that case, I recommend placing a copy of the font file in the fontshop folder, described above. Work on the copy, not the original.

I desired to add the DejaVu font family to my groff because I like the look of its Serif face. My system did not have all of the DejaVu files I wanted. Instead, my first step was to download the font files. I found them at the following url: https://sourceforge.net/projects/dejavu/. I used The Raspberry Pi’s Archiver utility to extract the files into a folder.

The actual files were in a subfolder named ttf. The files had the .ttf suffix, indicating TrueType fonts. I found 22 of them! This article covers only the four serif fonts that align with groff’s four, traditional styles:

I copied the files into my fontshop folder and went to work.

Adapt font for groff

Open a shell (a command-line window) and navigate to the fontshop folder. All of the steps which follow are performed in that location. If you have completed the preparatory steps described above then the directory contains font files and the links named textenc and textmap.

The first two steps direct the fontforge utility to adapt the chosen font for groff. It will produce three, new files for that font. The example illustrates for the normal DejaVu Serif font. Enter the two commands that follow, one at a time.

fontforge -lang ff -c "Open(\"DejaVuSerif.ttf\"); Generate(\"DejaVuSerif.pfa\");"

fontforge -lang ff -c "Open(\"DejaVuSerif.ttf\"); Generate(\"DejaVuSerif.t42\");"

The directory will now contain four files for DejaVu Serif:

groff also needs a font description file. In the next step, the afmtodit utility combines the .afm file with the links to the text encoding and the text map files to produce it. I wish that groff’s official documentation would describe this more completely. Thanks are due to a few kind souls who published tutorials online.

afmtodit -e textenc DejaVuSerif.afm textmap DVR

The directory now contains a font description file named DVR. What on Earth does DVR signify? It establishes a code name for the font family and tells groff which one of the four font styles this one is.

Identify the font family and style

groff takes the last letter (or two) of the font descriptor file name to signify the font style. An R indicates the Regular, or Roman font style. Other style signifiers recognized by groff include I for italic, B for bold and BI for bold italic.

The beginning part of the font descriptor file name encodes the font family name for groff. In the example, DV is a two-letter code that I chose to mean DejaVu Serif. I could have chosen other letters; these two simply made sense to me.

At the end of this article I will demonstrate combining the DV family code with the other font style codes described above.

This two-letter naming scheme for font family looks — and is, actually — old-fashioned; yet it works. Its main appeal is to stay consistent with the way other, pre-installed font families are named.

Modern versions of groff do not limit us to two-letter font names. I must defer discussion of longer font family names to a future article.

There are two things to inspect inside the font description file. Open it in a text editor. For example, the first few lines of my DVR file appear below:

# This file has been generated with GNU afmtodit (groff) version 1.22.4
#
#   FullName DejaVu Serif
#   Version 2.37
#   FamilyName DejaVu Serif

name DVR
internalname DejaVuSerif
spacewidth 317
encoding textenc
ligatures fi fl 0

kernpairs
- :Y -109
			

Make a note of the internal name. In this case, it is DejaVuSerif. This name will be used in a subsequent step.

Also, look to see that the ligatures line is present, just before the list of kernpairs begins. Sometimes that information does not come through, in which case you may wish to type it into the file as it appears here. Save any changes you make to the file before moving on to the next step.

Move font files into storage

Transfer the font files to the storage locations specified in the GROFF_FONT_PATH, introduced earlier in this article. Command-line instructions are shown here, similar to the way they will be used in the script provided at the end of the article.

cp DVR /home/user-name/.local/share/groff/site-font/devps/
cp DejaVuSerif.t42 /home/user-name/.local/share/groff/site-font/devps/
cp DVR /home/user-name/.local/share/groff/site-font/devpdf/
cp DejaVuSerif.pfa /home/user-name/.local/share/groff/site-font/devpdf/
			

Be sure to replace the user-name part of those instructions with your own, actual user account name.

Tie everything together

The final step is to relate the font file names to their respective internal names as found in the font description files. This information goes into a file named download in each of the two font storage locations. To do this manually, navigate to each folder and edit the download file in a text editor. If it does not already exist, create it.

Start by navigating into the folder for PostScript output: /home/user-name/.local/share/groff/site-font/devps. The download file will contain one line of specially-formatted text for each font in that folder. For PostScript-related fonts, the format of the line is as follows:

internalName<TAB>fontFilePath.t42

The internalName should be written as you found it when you examined the font description file. The font file path will be only the file name because this procedure chooses to store the font files in the same folder as the download file. Note that the <TAB> bit in that line represents a single, actual tab character. When you type it, be sure your text editor enters an actual tab and not some space characters. The following example is for the regular DejaVu Serif font.

DejaVuSerif<TAB>DejaVuSerif.t42

Move into the folder for PDF output, /home/user-name/.local.share/groff/site-font/devpdf/, and repeat the procedure above. Except notice that the format of a text line in the download file is different. It has three fields rather than two: Foundry <TAB> internalName <TAB> fontFilePath. In this procedure the Foundry field is left blank. This means that the lines here must begin with a <TAB> character.

<TAB>internalName<TAB>fontFilePath.pfa

For example:

<TAB>DejaVuSerif<TAB>DejaVuSerif.pfa

Automate it with a script

The procedure described above uses instructions designed to be typed onto the command line in a Linux shell, specifically the bash shell. The same instructions can be listed together in a shell script, which can then be run on the command line as a kind of program. As programs, shell scripts can accept parameters. This capability enables a user to specify the font on the command line, as will be shown.

The following lists a script named font4groff that performs all of the above steps for a .ttf type of font. Call it with the name of the font file and the name chosen by the user for the groff font descritption file. Examples of usage will be provided below. At the time of writing I have not yet tested it with other types of font files.

#!/usr/bin/bash
#
# usage: ./font4groff FontName-Style.ttf GroffName
# where:
# 		FontName-Style.ttf should be in the local directory
# 		GroffName is a one- or two-letter font family identifier,
# 		e.g. DV for DejaVu, as chosen by the user,
# 		combined with a style identifier: R I B or BI
# 		meaning respectively, Regular Italic Bold or BoldItalic
#
# example: ./font4groff DejaVuSerif-Bold.ttf DVB
#
# arguments:
# $1 = FontName-Style.ttf	example DejaVuSerif-Bold.ttf
# $2 = DVB		DV identifies the groff font family,
# 			    B is the style, indicating Bold
 
# generate fontforge output specifiers
pfaname=$(echo $1 | sed 's/\(^[[:alnum:]-]*\)\..*$/\1.pfa/')
t42name=$(echo $1 | sed 's/\(^[[:alnum:]-]*\)\..*$/\1.t42/')

# generate afmtodit input name
afmname=$(echo $1 | sed 's/\(^[[:alnum:]-]*\)\..*$/\1.afm/')

# generate 'internal name' for the device download files
internalName=$(echo $1 | sed 's/\(^[[:alnum:]-]*\)\..*$/\1/')

# make .pfa and .afm files for the font
fontforge -lang=ff -c "Open(\"$1\"); Generate(\"$pfaname\");"
# make .t42 file for the font
fontforge -lang=ff -c "Open(\"$1\"); Generate(\"$t42name\");"

# produce the groff font description file
afmtodit -e textenc $afmname textmap $2

# move the resulting files to their proper locations
cp $2 /home/user-name/.local/share/groff/site-font/devps
mv $2 /home/user-name/.local/share/groff/site-font/devpdf
mv $t42name /home/user-name/.local/share/groff/site-font/devps
mv $pfaname /home/user-name/.local/share/groff/site-font/devpdf

# append the font into the download files in devps and devpdf
printf "\n%s\t%s" $internalName $t42name >> \
	/home/user-name/.local/share/groff/site-font/devps/download
printf "\n\t%s\t%s" $internalName $pfaname >> \
	/home/user-name/.local/share/groff/site-font/devpdf/download

# delete the .afm file
rm *.afm
			

The script should be saved in the fontshop folder and run from there when the command-line shell is open in that folder. Make it executable with this command-line instruction:

chmod +x font4groff

As a general rule, one needs to prepend ./ before the name of an executable script being run in its own directory, as shown below. I used this script to install the other three font style files for DejaVu Serif. For each font, it needed only a single instruction.

./font4groff DejaVuSerif-Bold.ttf DVB
./font4groff DejaVuSerif-Italic.ttf DVI
./font4groff DejaVuSerif-BoldItalic.ttf DVBI
			

Disclaimer

Please treat this work product kindly. You will probably have to change the instructions or the script to suit your personal setup before the steps will go right for you. Presented here is just something I made that works for me on my Debian Linux-based devices (Ubuntu laptop, Raspberry Pi 5). The file pathways spelled out in the script are ones I custom-created for the purpose on my devices. Everything written here in this article has been intended solely for my own benefit as a future reminder of how I did it. I sincerely hope that something shown here might help a reader with their own groff experience. However, as the Olde Professor would say, I leave adaptation to their own purposes as an exercise for the reader.