Home / Alt manpages / pic(1)

  • pic(1)
  • User command
  • linux

Draw Reproducible Diagrams in roff with pic

Hand-drawn ASCII diagrams rot the moment someone edits the text around them, but pic redraws them from a text description every time. By the end you will have a small roff document with a box, an arrow and a label, rendered 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.

What pic actually does

  • It is a preprocessor. pic copies ordinary input straight to standard output, but interprets anything between .PS and .PE, .PF or .PY as a picture.
  • It hands off drawing language. The result is drawing language understood by troff, or by TeX when you select TeX mode.
  • groff calls it for you. In normal use, groff invokes pic with its -p option; running pic directly is for when you want to inspect or pipeline its output yourself.
  • gpic is the same thing. On this system it is the GNU name for the same installed implementation. Both 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.
  • Quoted strings become labels.

The here-document only creates 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

  • -S and -U are opposites for shell commands. Safer mode is the default; unsafe mode permits execution.
  • -t selects TeX output. It is not needed for ordinary troff or terminal rendering.
  • -C widens delimiter matching. It makes pic recognise .PS, .PE, .PF and .PY even when followed by a character other than a space or newline. Use it only when an existing document needs that compatibility.
  • -n avoids GNU troff drawing extensions. Use it when a downstream postprocessor cannot accept those extensions; it also changes how dots are drawn in troff mode.
  • .PF and .PY leave the position at the top of the picture, while .PE leaves it at the bottom. GNU pic supports .PY as 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

  • Version confirmed: pic --version identifies the installed GNU groff version.
  • Picture complete: your input has one complete picture between recognised delimiters.
  • Render succeeds: groff -Tutf8 -mpic -p renders the diagram and exits successfully.
  • Output mode understood: you know whether you need troff output or TeX output from -t.
  • Safer mode kept on: untrusted documents are processed with the default safer mode, and you have not enabled -U without auditing every shell-capable command.