Build and Check a Netpbm PAM Image by Hand
You will create a two-pixel RGB image in Netpbm's PAM format, inspect its header and raster, and convert it to PPM without changing the source file. Allow about fifteen minutes. You need a shell, the netpbm package, and a text editor or a way to create a file. The examples use Netpbm 11.5.2 from package netpbm 2:11.05.02-1.1build1.
The route
Jump straight to the step you need, or tick off Done means at the end.
PAM means Portable Arbitrary Map. It is a general container for a rectangular grid of tuples. In an image, a tuple is normally one pixel and its samples are channels such as red, green and blue. The format does not give every tuple type a universal meaning, so the header and the agreement between the tools producing and consuming the file both matter.
1. Check the installed tools
Start by confirming the commands used in this guide:
$ command -v pamfile pamtopnm
/usr/bin/pamfile
/usr/bin/pamtopnm
$ pamfile --version
pamfile: Using libnetpbm from Netpbm Version: Netpbm 11.5.2
pamfile reports the structure it can read. pamtopnm converts PAM to the older portable anymap family, which includes PPM. These commands only read the input in the examples. No elevated privilege is required. If either command is absent, install the distribution's Netpbm package before continuing, rather than copying a binary from an unrelated system.
Checkpoint: if pamfile --version reports a different release, keep that fact with any test result. The format specification is older than the installed library, and implementation limits or diagnostics can differ between releases.
2. Create a minimal RGB PAM file
Use a temporary directory for the test so that an existing image cannot be overwritten. The command below writes a new file named rgb.pam containing two pixels: red followed by green.
work=$(mktemp -d /tmp/pam-example.XXXXXX)
printf 'P7\nWIDTH 2\nHEIGHT 1\nDEPTH 3\nMAXVAL 255\nTUPLTYPE RGB\nENDHDR\n\377\000\000\000\377\000' > "$work/rgb.pam"
This uses printf because the raster is binary. The header is ASCII text and ends at ENDHDR followed by a newline. The six bytes after it are two tuples, each with three samples: 255, 0, 0 for red and 0, 255, 0 for green. The file is deliberately small enough to reason about byte by byte.
Do not open the file in a text editor and save it. A text editor can replace or discard binary bytes. If you need to recreate it, rerun the command in the temporary directory.
3. Read the header with pamfile
Ask Netpbm to identify the file:
$ pamfile "$work/rgb.pam"
/tmp/pam-example.XXXXXX/rgb.pam: PAM, 2 by 1 by 3 maxval 255
Tuple type: RGB
Your temporary directory name will differ. The useful values are width 2, height 1, depth 3, and maxval 255. Width counts columns and height counts rows. Depth is the number of samples in each tuple, not the number of bytes in a tuple. With a maxval of 255, each sample needs one byte; a larger allowed maxval can require two bytes.
A PAM header must contain exactly one each of WIDTH, HEIGHT, DEPTH, MAXVAL and ENDHDR. TUPLTYPE is optional and describes meaning by convention. Header lines are newline-delimited ASCII, and a line beginning with # is a comment. Keep header tokens and values separate with whitespace.
4. Convert it and verify the result
Convert to PPM in a new file, then inspect the result:
pamtopnm "$work/rgb.pam" > "$work/rgb.ppm"
pnmfile "$work/rgb.ppm"
$ pnmfile /tmp/pam-example.XXXXXX/rgb.ppm
/tmp/pam-example.XXXXXX/rgb.ppm: PPM raw, 2 by 1 maxval 255
The PPM header and raster are a different representation of the same two-pixel image. The conversion is useful when a program understands PPM but not PAM. It does not prove that an arbitrary TUPLTYPE has visual RGB meaning: the official RGB convention is depth 3 with samples ordered red, green, blue. For a custom tuple type, document the sample meanings alongside the file.
For a quick visual or byte-level check, use a Netpbm reader rather than relying on the filename:
pamfile "$work/rgb.pam"
pnmfile "$work/rgb.ppm"
wc -c "$work/rgb.pam" "$work/rgb.ppm"
Do not assume a .pam suffix makes a file valid. The conventional suffix is .pam, but the format is identified by the P7 magic number and a valid header. Conversely, a valid PAM stream may arrive through standard input or have another filename.
5. Keep the raster rules straight
Samples are unsigned integers from zero through MAXVAL. They are stored in pure binary form, most significant byte first, using the minimum number of bytes needed for that maxval. Tuples appear left to right within each row, and rows appear top to bottom. There is no delimiter between samples, tuples or rows, and no padding before or after the raster.
For visual subformats, BLACKANDWHITE uses depth 1 and maxval 1, with zero meaning black in PAM. GRAYSCALE uses depth 1. RGB uses depth 3, with red, green and blue as its planes. Adding _ALPHA adds an opacity plane at the highest numbered position, so RGB_ALPHA uses depth 4. The alpha sample is opacity as a fraction of maxval, not a gamma-adjusted colour sample.
Depth is still the value a decoder should use to walk the raster. Do not infer depth solely from TUPLTYPE. A tuple type can imply what a producer intended, but it does not change the binary encoding or replace validation of the header.
6. Remove the temporary test safely
Once verification is complete, remove only the directory created by this guide:
rm -rf -- "$work"
This is the one destructive command here. Check that $work still names the temporary directory before running it, and do not substitute a broad path such as your home directory. If you need the sample for later testing, keep it instead and remove it when you have finished.
Done means
pamfileidentified a PAM image with the intended width, height, depth and maxval.- The header's required lines appeared once, followed immediately by the binary raster.
pamtopnmproduced a PPM file, andpnmfileverified its dimensions and maxval.- You treated
TUPLTYPEas a meaning convention, while usingDEPTHto decode samples. - The temporary directory was removed only after the checks passed.