Draw Reproducible Diagrams in roff with pic
By the end of this guide, you will have a small roff document containing a box, an arrow and a label, and you will be able to render it as terminal output with GNU pic and groff. The examples use GNU groff 1.23.0, supplied here by the groff-base package. Allow about 15 minutes if you are new to roff. No elevated privileges are needed.
The route
Jump straight to the step you need, or tick off Done means at the end.
What pic does
pic is a preprocessor. It copies ordinary input to standard output, but interprets a picture between .PS and .PE, .PF or .PY. The result is drawing language understood by troff, or TeX when you select TeX mode. In normal use, groff invokes pic for you with its -p option; calling pic directly is useful when you want to inspect or pipeline its output.
The gpic command is the GNU name for the same installed implementation on this system. The two commands report GNU pic (groff) version 1.23.0, and their installed manual pages are identical here.
1. Check the local version
Run this as your ordinary user:
$ pic --version
GNU pic (groff) version 1.23.0
Also check the available option summary if you are working on another machine:
$ pic --help
usage: pic [-CnSU] [file ...]
usage: pic -t [-cCSUz] [file ...]
usage: pic {-v | --version}
usage: pic --help
Do not assume that a different groff release has exactly the same extensions. This guide describes the GNU implementation documented by the local pic(1) and gpic(1) pages.
2. Write the smallest useful picture
Create a temporary input file in a directory you control. The file has roff requests outside the picture and pic objects inside it:
$ mkdir -p /tmp/pic-example
$ cat > /tmp/pic-example/diagram.roff <<'EOF'
.PS
box "input"
arrow
box "output"
.PE
EOF
The first box is placed at the current drawing position. arrow then joins the next object, so the second box follows it. The quoted strings become labels. The here-document is only creating disposable input under /tmp; it does not install anything or change system configuration.
3. Render it through groff
Use groff's -p option to run pic, -m pic to load simple centring macros, and the utf8 terminal device for a readable result:
$ groff -Tutf8 -mpic -p /tmp/pic-example/diagram.roff
┌─────────┐ ┌──────────┐
│ input │ ───> │ output │
└─────────┘ └──────────┘
The exact box width and terminal glyphs depend on the device and font, so treat the layout above as representative. A successful command prints the rendered picture and exits with status zero.
Checkpoint
If you see the literal .PS and box lines, pic was not run. Check that the command contains -p, and that the picture delimiters are spelled exactly as shown.
4. Inspect pic output when debugging
Run pic directly when you need to see what it passes to troff:
$ pic /tmp/pic-example/diagram.roff | sed -n '1,12p'
.do if !dPS .ds PS
.do if !dPE .ds PE
.do if !dPF .ds PF
.do if !dPY .ds PY
...
This output is intermediate roff, not a finished page. It is useful for confirming that a picture was recognised. For TeX-compatible output, add -t:
$ pic -t /tmp/pic-example/diagram.roff | sed -n '1,8p'
\expandafter\ifx\csname graph\endcsname\relax
\csname newbox\expandafter\endcsname\csname graph\endcsname
...
TeX mode requires a driver that supports tpic version 2 specials. The generated picture is placed in a TeX box named graph by default, and the document must print that box itself. Do not send this output to a terminal and expect a diagram.
5. Keep untrusted input in safer mode
GNU pic recognises a sh command that can pass text to a shell. Safer mode, selected by -S, is enabled by default and ignores these commands. This is the correct default for roff files obtained from elsewhere:
$ printf '.PS\nsh "printf BAD >&2"\nbox\n.PE\n' | pic -S
$
The command produces no BAD output because the shell request is ignored. Never use -U merely to make a third-party document render. Unsafe mode interprets shell commands with the permissions of the user running pic. If you deliberately need a trusted build step that uses sh, review the complete input first, run it as an unprivileged account, and keep its output directory separate from important files.
Warning
-U is a security boundary, not a compatibility switch. The following harmless test shows the distinction, but do not replace the command text with input you have not audited:
$ printf '.PS\nsh "printf PIC_TEST >&2"\nbox\n.PE\n' | pic -U
PIC_TEST
Useful boundaries and common traps
-Sand-Uare opposites for shell commands. Safer mode is the default; unsafe mode permits execution.-tselects TeX output. It is not needed for ordinary troff or terminal rendering.-Cmakes pic recognise.PS,.PE,.PFand.PYeven when followed by a character other than a space or newline. Use it only when an existing document needs that compatibility.-navoids GNU troff drawing extensions. Use it when a downstream postprocessor cannot accept those extensions; it also changes how dots are drawn in troff mode..PFand.PYleave the drawing position at the top of the picture, while.PEleaves it at the bottom. GNU pic supports.PYas a workaround for a macro-name collision with the mm package.
If the picture is not centred, check the surrounding macro setup. The pic program expects suitable definitions for the PS, PE and, where used, PF or PY macros. The groff -mpic option supplies simple definitions that centre each picture.
Done means
pic --versionidentifies the installed GNU groff version.- Your input has one complete picture between recognised delimiters.
groff -Tutf8 -mpic -prenders the diagram and exits successfully.- You know whether you need troff output or TeX output from
-t. - Untrusted documents are processed with the default safer mode, and you have not enabled
-Uwithout auditing every shell-capable command.