The PNG Guide is an eBook based on Greg Roelofs' book, originally published by O'Reilly.


Possibly the most complete conversion program in existence, at least with respect to support for PNG features, is pnmtopng. In conjunction with its inverse, pngtopnm, and the rest of the NetPBM suite,[32] it is capable of handling basic conversions to and from virtually any image format. But pnmtopng really shines as a tool for adding and modifying PNG chunk information, including such things as text annotations, palette optimization, and support for adding or removing alpha (transparency) channels.

[32] NetPBM originated as the PBMplus package, last released in December 1991. Subsequent third-party contributions from the Internet were gathered together and released as NetPBM in 1993 and early 1994, containing some 200 utilities for converting and manipulating images. The package has lain dormant since then, aside from the occasional appearance of utilities to support new image formats like PNG, but further news on this front is expected in 1999.

Currently, the latest version of pnmtopng is 2.37.2, released in March 1999; it can be found on the PNG home site,, along with pointers to the libraries on which it depends.

Written and maintained by Alexander Lehmann and Willem van Schaik with contributions and fixes from others, pnmtopng is primarily a Unix-based tool, which unfortunately limits its usefulness to a minority of computer users. But other parts of the NetPBM suite have been ported to OS/2 and Windows, and it is likely that a future release of both pnmtopng and NetPBM will be more portable and may even include ready-to-go executables.

To begin explaining some of pnmtopng's features, it is first necessary to describe a little about the PBM format itself. If one wishes to be able to convert any of 100 possible image formats into any other, there are two options: write 10,000 individual converters to go directly from format A to format B for all possible pairs of A and B; or write only 200 converters, 100 to go from each of the image formats into some intermediate representation and another 100 to convert back from that intermediate format into the 100 target formats. Once the intermediate format exists, one need not stop at conversion programs; generic utilities to manipulate images suddenly become possible--for example, quantization, smoothing, cropping, contrast enhancement, and so on.

PBMplus/NetPBM is that intermediate format. It was originally designed by Jef Poskanzer and released as the PBMplus suite, with later ``interim'' packages released as NetPBM by Bill Davidsen. Since there has never been another PBMplus release, I will henceforth refer to the format as NetPBM, the name by which it is now most commonly known. The format is quite simple: three lines of text header--which may additionally include one or more comment lines--followed by the uncompressed image data. The image data may be stored as either text or binary values; the latter is more efficient and far more commonly used, but the existence of the text format means that one can actually create images or color palettes in an ordinary text editor. There are also three basic NetPBM image flavors: bilevel (or black and white), which is referred to as a portable bitmap or PBM file; grayscale, called a portable graymap or PGM; and truecolor (RGB), referred to as a portable pixmap or PPM file. Programs that can deal with more than one flavor usually have ``PNM'' in their names; this stands for portable anymap. There is currently no ``real'' PNM format; it is a virtual format and a convenient catchall name.

One notable feature missing from the NetPBM format is provision for alpha channels; this is a known limitation[33] with implications for converting between formats that support transparency, such as PNG, GIF, and TIFF. pnmtopng gets around this to some extent by the simple expedient of storing transparency information in a separate grayscale file. Before we get into that, let's look at some simpler cases.

[33] Alpha support is a major reason behind the expected NetPBM revisions in 1999.

pnmtopng is a command-line program, and, thanks to its Unix heritage, it is designed to operate as part of a multicommand pipeline. Unix pipes are a slick method of connecting the output of one program into the input of another; in principle there is no limit to how long such a chain can be, although in practice the amount of system resources that are available may constrain things. Here is a simple example that converts a GIF image into PNG:

giftopnm foo.gif | pnmtopng > foo.png

The file foo.gif is read by giftopnm (part of the NetPBM suite) and converted to NetPBM format, then piped into the input of pnmtopng, which converts the image to PNG format. Since there are no more programs to be run, pnmtopng's output is redirected into a file--in this case, foo.png.

Observant readers will recall that GIF images are always palette-based, yet I didn't say anything about palettes in describing the NetPBM format. In fact, NetPBM has no concept of palettes; giftopnm usually converts GIF images into PPM format (the RGB flavor). Fortunately, pnmtopng is smart enough to count the colors in an image and automatically write a palette-based PNG image if there are 256 or fewer colors. It will likewise detect if a color image is actually composed only of gray values; in that case, it will write either a grayscale PNG or a palette-based one, depending on which can be written with the fewest bits. This automatic checking comes at a cost, however: because it requires inspection of every pixel, it can be quite slow for large images. pnmtopng therefore includes a -force option to skip the checking. With this option, the previous example would result in a 24-bit truecolor PNG:

giftopnm foo.gif | pnmtopng -force > foo24.png

Here are examples for two other popular image formats, TIFF and JPEG:

tifftopnm foo.tiff | pnmtopng > foo-was-tiff.png
djpeg foo.jpg | pnmtopng > foo-was-jpeg.png

But these are all trivial conversions. Suppose I would like to convert an existing NetPBM image into an interlaced PNG, including gamma information, a timestamp, and some text--say, the author's name, the title of the image, its copyright, and perhaps the date on which the original photograph was taken. The first thing we need to do is create a small text file containing the text information. pnmtopng treats the first word on any line that does not begin with a blank (either a space or a tab character) as the keyword, with the actual text following. The text may stretch over several lines, and keywords with spaces in them must be quoted. Thus the following text file, containing four keywords and their corresponding values, would suffice:

Title           The Incredible and Rarely Seen Foo
Author          Greg Roelofs
Copyright       This image is hereby placed in the
                public domain by its author.
"Creation Time" 4 July 1976
    is the date on which this particular Foo was photographed.

Note that leading blanks (or ``white space''), including any between the keywords and subsequent text, will not be included in the PNG text chunks. But any newlines (or ``carriage returns,'' loosely speaking) will be included exactly as typed; thus, there will be one in the Copyright text chunk, right before the word ``public,'' and another in the Creation Time text chunk, immediately after ``1976.'' In addition, there is currently a bug in pnmtopng: when all of the text corresponding to a keyword appears on a line following the keyword--that is, the keyword is immediately followed by a carriage return--the program will sometimes crash. The problem will almost certainly be fixed by the time this book reaches print, but in the meantime, it can be avoided by adding a space after the keyword.

So assuming the text file were named comments.txt (and contains no keywords followed immediately by newlines), the following command would create the PNG image with the specified text and other information:

pnmtopng -interlace -gamma 0.65909 -text comments.txt \
  -time 1998-10-25 21:00:00 foo.ppm > foo.png

The first option is self-explanatory: the PNG image will be interlaced. For the -gamma option, we've used a value that corresponds to a typical Macintosh; we're imagining that the original image was scanned and tweaked on a Mac before being converted to PPM format (foo.ppm) on some other system. The -time option requires a little more explanation. First, note that it is distinct from the ``Creation Time'' text chunk we included; the -time option will write the special PNG tIME chunk, which represents the time the image was last modified. But the last modification time is clearly the time the image was converted into PNG format, so pnmtopng really should not require the user to specify the time information explicitly. This is particularly true, given that PNG's time chunk is supposed to be in Coordinated Universal Time, and most users are unlikely to know how to convert to that.[34] With luck, this oversight will also be corrected in the next release of the program.

[34] The example here corresponds to 1:00 p.m. in the US/Pacific time zone. But had the conversion taken place at 1:00 p.m. on the previous day, it would have been specified as 20:00:00 in Universal Time, thanks to the fact that daylight saving time had not yet ended.

Transparency is one of PNG's major strengths, so let's take a look at some of pnmtopng's options there. Suppose that we wish to vignette our treasured foo image--that is, we would like to apply an oval mask to it that gradually fades to complete transparency, in effect transforming our image from rectangular to rounded. This is easily accomplished by creating the oval mask as a grayscale (PGM) image, where white represents the regions that will be completely opaque (i.e., the main subject matter of the image) and black the outer, transparent regions. Then give the following command:

pnmtopng -alpha ovalmask.pgm foo.ppm > foo.png

This will ordinarily create a 32-bit RGBA image--in other words, truecolor with a full alpha channel. But if it happens that the combination of the original RGB image and the mask produces at most 256 RGBA combinations, pnmtopng is smart enough to detect that and write a palette-based image with transparency information instead. Moreover, it will automatically arrange the palette and transparency entries so that all of the completely opaque colors are at the end of the palette; the corresponding transparency entries may then be omitted, resulting in a smaller file.

In some cases, the transparency mask contains only fully opaque and fully transparent values, and it may happen (usually by design) that the parts of the underlying image that correspond to the transparent region are all one color, even though there may be thousands of colors in the opaque part. pnmtopng will again detect this, creating a palette-based image with just one transparency entry if possible; if there are too many colors, it will instead write a full grayscale or RGB image with a single color marked transparent. This results in a PNG file that's much more compact than one with a full alpha channel.

Transparent images intended for display only on web browsers will always have some sort of background specified as part of the web page, but for images that may be rendered by a standalone viewer, it is often desirable to include an explicit background color in the image. The -background option provides that capability; it accepts a color argument in almost any format allowed by MIT's X Window System, including English text (assuming the X color database file can be found). Thus, the following three commands are equivalent (the -alpha ovalmask.pgm option has been omitted for brevity):

pnmtopng -background rgbi:1.0/0.855/0.726 foo.ppm > foo.png
pnmtopng -background "peach puff"         foo.ppm > foo.png
pnmtopng -background "#ffdab9"            foo.ppm > foo.png

For most users, the second form is probably the most easily understood but the least precise. Making it precise requires the finely honed ability to find the X color-database file, which can be difficult when it exists and impossible when it doesn't[35] (it is also explicitly platform-dependent; that is, the same color name is allowed to have different RGB values on different machines). Therefore, the first form is likely to be the most useful. It specifies the RGB values of the background color as decimal fractions between 0.0 and 1.0. The values are separated by forward slashes (/) and prefixed by rgbi:. The third form is the old-style hexadecimal format that is favored by programmers but almost no one else. (It also happens to be the format used in the demo programs I present in Chapter 13, "Reading PNG Images" and Chapter 14, "Reading PNG Images Progressively" on reading PNG images. Oh, the embarrassment.) The hex value need not be placed in quotation marks on a command line, but within a shell script it should be quoted, or the hash character (#) will be treated as the beginning of a comment.

[35] For the record, it lives in /usr/openwin/lib/X11/rgb.txt on Sun systems, /usr/X11R6/lib/X11/rgb.txt on most Linux and FreeBSD systems, and /usr/lib/X11/rgb.txt on ``generic'' Unix/X11 systems.

pnmtopng also potentially supports the creation of 16-bit-per-sample images (that is, 16-bit grayscale, 32-bit gray+alpha, 48-bit RGB or 64-bit RGBA), but only with text (ASCII) NetPBM files, and only if the underlying NetPBM library supports 16-bit images, which is not the default behavior. The requirement to use ASCII format for the 16-bit NetPBM image files is a current limitation of the NetPBM suite. As with transparency and palettes, pnmtopng detects if 16-bit samples are really just scaled 8-bit samples; if so, it will automatically convert the image back to 8-bit samples unless the -force option is given. It can also be instructed to convert true 16-bit samples to 8-bit with the -downsample option.

Other supported features include chromaticity information, histograms, compressed text, explicit single-color transparency, physical pixel dimensions, and special compression options. Quantization of truecolor images to 256 or fewer colors is not supported by pnmtopng itself, but it is a straightforward part of the standard NetPBM package. For example, to quantize a 24-bit TIFF image to the 256 best colors, dither the result, and save it as a palette-based PNG, one can use:

tifftopnm foo.tiff | ppmquant -fs 256 | pnmtopng > foo.png

The -fs option to ppmquant instructs it to use Floyd-Steinberg dithering, which generally looks very nice but does require a fair amount of computation. The 256 parameter indicates the number of colors to be used in the final version; any value may be used (web-savvy designers might wish to use a smaller number of colors), but only values of 256 or less will result in a palette-based PNG image. What about images with an alpha channel? Unfortunately, those who wish to quantize 32-bit RGBA images down to a 256-entry ``RGBA palette'' are stuck for now. The ppmquant algorithm can easily be modified to support RGBA values in addition to ordinary RGB, but until NetPBM itself is updated, there is no way to pipe transparency information from one NetPBM utility into another.

For users of very large images, one other point is worth mentioning: pnmtopng currently reads the entire image into memory buffers before doing anything with it, which means that a 4000 × 4000 RGBA image would require 64 megabytes of real and/or virtual memory just for the uncompressed image itself. But all is not lost; in Chapter 15, "Writing PNG Images", I present a very simple-minded NetPBM-to-PNG converter, and one of its design goals was the ability to convert images on the fly, requiring only a very small memory footprint. (Of course, this only works if the PNG image is not interlaced.) The demo program also has a -time option that automatically records the current time in the proper format, as well as one or two other potentially handy features.

Last Update: 2010-Nov-26