Add Clear Text Labels to PPM Images with ppmlabel
ppmlabel stamps text onto a PPM image at a chosen position, size, colour and angle, all without touching the input file. This guide adds a single label, chains several in one pass, sets a background and rotation, and reads labels from a file. Allow about ten minutes if you already have a PPM file, or fifteen for the small test image below.
The route
Jump straight to the step you need, or tick off Done means at the end.
You need a shell, the ppmlabel command and a writable output directory. The examples use Netpbm 11.5.2, installed here as Debian package version 2:11.05.02-1.1build1. This tool reads a PPM image and writes a PPM image; it does not edit the input in place, and it does not need sudo.
1. Check the command and your input image
Confirm which executable will run, then inspect the image before changing anything:
$ command -v ppmlabel
/usr/bin/ppmlabel
$ pamfile INPUT.ppm
INPUT.ppm: PPM raw, 160 by 90 maxval 255
Replace INPUT.ppm with your own path; the dimensions and maximum value will vary. pamfile is a useful checkpoint because it confirms you are feeding the program an actual PPM file, not a PNG with a merely similar filename.
Checkpoint
Keep the original input untouched and choose a different output path, such as OUTPUT.ppm. If you plan to replace an existing file, make a backup first:
$ cp -- INPUT.ppm INPUT.ppm.bak
That copy is your recovery path if the new image is misplaced or wrong. Shell redirection can truncate an existing destination before ppmlabel even reports an error, so never point > at your only copy.
2. Add one label at a chosen position
Run the command below with a separate output file:
$ ppmlabel \
-x 10 -y 25 \
-size 12 \
-colour black \
-text 'Inspection copy' \
INPUT.ppm > OUTPUT.ppm
$ pamfile OUTPUT.ppm
OUTPUT.ppm: PPM raw, 160 by 90 maxval 255
-xsets the column where the text is left justified.-ysets the baseline row, not the top edge of the letters, so descenders can extend below it.-sizeis the height in pixels of the tallest characters above the baseline.-colouraccepts a Netpbm colour name.-textdraws the supplied string, quoted as one shell argument if it contains spaces.
Single quotes are convenient for ordinary labels; use double quotes when you need shell expansion, and remember that expansion happens before ppmlabel ever sees the text.
3. Add several labels without repeating coordinates
Options are commands applied left to right. A setting affects the text that follows it, and each -text advances the next label down by 1.75 times the current text size:
$ ppmlabel \
-x 10 -y 25 -size 12 -colour black \
-text 'Sample image' \
-text 'Captured 2026-09-26' \
INPUT.ppm > OUTPUT.ppm
That is useful for a short caption block, but the automatic spacing is not a layout engine: long text can run beyond the image, and rotated text can overlap later lines. If exact placement matters, set -x and -y again before each label.
Checkpoint
Inspect the output dimensions and open it with an image viewer that understands PPM. A successful pamfile result proves the format is readable, but it cannot tell you whether a label actually sits inside the visible image.
4. Choose a background and rotate the baseline
By default the label is drawn straight over the existing pixels. Make that explicit with -background transparent, or request a coloured rectangle behind the text that follows:
$ ppmlabel \
-x 10 -y 25 -size 12 \
-colour white -background navy \
-text 'Night shift' \
INPUT.ppm > OUTPUT.ppm
The background rectangle encloses each text item that comes after it. With multiple lines, the manual warns that the space between lines may not be filled correctly, especially when the text is rotated. Use a transparent background when preserving the underlying image matters, or check the coloured version visually before distributing it.
Set a counterclockwise baseline angle for later text with -angle:
$ ppmlabel \
-x 30 -y 70 -size 14 -colour red \
-angle -30 -text 'Draft' \
INPUT.ppm > OUTPUT.ppm
Angles are integral degrees, measured counterclockwise from the image row axis. Keep the starting coordinates away from the edges, because rotation changes the space the label actually occupies.
5. Read labels from a file or standard input
-file reads lines from a named file and draws them on successive lines, which keeps a long caption out of the shell command itself:
$ ppmlabel -x 10 -y 25 -size 12 -colour black \
-file labels.txt INPUT.ppm > OUTPUT.ppm
Warning
Each line is treated as text, and the font is limited to 7-bit ASCII. Accented letters and other 8-bit characters are not supported by this tool, so convert or simplify the text first; quoting will not make non-ASCII characters render correctly.
If you omit the input filename, ppmlabel reads the PPM image from standard input instead, and the output is still standard output:
$ cat INPUT.ppm | ppmlabel -x 10 -y 25 -text 'From stdin' > OUTPUT.ppm
$ pamfile OUTPUT.ppm
OUTPUT.ppm: PPM raw, 160 by 90 maxval 255
For a script, prefer a direct redirection such as ppmlabel ... < INPUT.ppm > OUTPUT.ppm: it states both streams clearly and avoids an unnecessary process.
6. Diagnose the usual failures
A missing input file produces a non-zero status and an error on standard error. Check the status immediately:
$ ppmlabel -text 'Test' missing.ppm > OUTPUT.ppm
ppmlabel: Unable to open file 'missing.ppm' for reading. ...
$ printf 'exit status: %s\n' "$?"
exit status: 1
The exact diagnostic includes the system error and can vary. A status of 1 means the command failed; it is not a reason to trust whatever was partially written.
- Text clipped? Move the baseline inward, reduce
-size, shorten the label or use a smaller angle. - Colour rejected? Use a recognised Netpbm colour name such as
black,white,redornavy. - Never trust a viewer as proof. A viewer displaying the result is not evidence that the label is positioned well; check the actual coordinates.
For more complex overlays, the manpage points to pbmtext or pbmtextps combined with pamcomp, and to ppmdraw for general drawing. Reach for those when you need a designed text layer, fonts outside ppmlabel's fixed font, or precise composition.
Done means
- Input confirmed. The source is a readable PPM image.
- Output kept separate. The labelled result was written to a different path from the original.
- Baseline understood. You can explain that
-ymarks a baseline, not a top edge. - Order respected. Multiple labels used left-to-right option order.
- Background and rotation tested when those features actually mattered.
- Backup kept. You verified the output with
pamfileand backed up before replacing any original.