about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--GNUmakefile4
-rw-r--r--buildtools/debian/README8
-rw-r--r--buildtools/manpage.mk9
-rw-r--r--converter/other/ipdb.c4
-rw-r--r--converter/other/pamtopdbimg.c152
-rw-r--r--converter/other/pdbimgtopam.c33
-rw-r--r--converter/other/pnmtopalm/palm.h30
-rw-r--r--converter/other/pnmtopalm/palmcolormap.c134
-rw-r--r--converter/other/pnmtopalm/palmcolormap.h25
-rw-r--r--converter/other/pnmtopalm/palmtopnm.c219
-rw-r--r--converter/other/pnmtopalm/pnmtopalm.c567
-rw-r--r--doc/HISTORY51
-rwxr-xr-xeditor/ppmshadow44
-rw-r--r--generator/pamgradient.c1
-rw-r--r--generator/pbmtext.c71
-rw-r--r--generator/pgmmake.c70
-rw-r--r--lib/libpamcolor.c129
-rw-r--r--lib/libpbmfont.c74
-rw-r--r--lib/libpm.c2
-rw-r--r--lib/libppmcolor.c9
-rw-r--r--lib/util/runlength.c67
-rw-r--r--lib/util/runlength.h3
-rw-r--r--pm_config.in.h2
-rw-r--r--test/Test-Order4
-rw-r--r--test/g3-roundtrip.ok3
-rwxr-xr-xtest/g3-roundtrip.test25
-rw-r--r--test/palm-roundtrip.ok10
-rwxr-xr-xtest/palm-roundtrip.test32
-rw-r--r--test/palm-roundtrip2.ok5
-rwxr-xr-xtest/palm-roundtrip2.test31
-rw-r--r--test/pamtopdbimg.ok20
-rwxr-xr-xtest/pamtopdbimg.test86
-rw-r--r--test/pdb-roundtrip.ok18
-rwxr-xr-xtest/pdb-roundtrip.test55
-rw-r--r--test/pnmquantall.ok6
-rwxr-xr-xtest/pnmquantall.test20
-rw-r--r--test/ppmbrighten.ok6
-rw-r--r--version.mk4
38 files changed, 1325 insertions, 708 deletions
diff --git a/GNUmakefile b/GNUmakefile
index 6f9eb41c..e5914648 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -446,6 +446,7 @@ deb:
 CHECK_VARS = \
 	BUILDDIR=$(BUILDDIR) \
 	RGBDEF=$(RGBDEF) \
+	PALMMAPDIR=$(PALMMAPDIR) \
 	BUILD_FIASCO=$(BUILD_FIASCO) \
 	JASPERLIB="$(JASPERLIB)" \
 	JBIGLIB="$(JBIGLIB)" \
@@ -512,6 +513,7 @@ colon :=:
 check-tree : PBM_TEST_PATH := $(subst $(space),$(colon),$(strip $(RBINDIRS)))
 check-tree : PBM_LIBRARY_PATH ?= $(BUILDDIR)/lib
 check-tree : RGBDEF ?= $(SRCDIR)/lib/rgb.txt
+check-tree : PALMMAPDIR ?= $(SRCDIR)/converter/other/pnmtopalm
 
 
 # Create RESULTDIR.
@@ -544,6 +546,7 @@ check-tree: $(TESTRANDOM) resultdir-backup
 check-package : PBM_TEST_PATH := $(PKGDIR)/bin
 check-package : PBM_LIBRARY_PATH := $(PKGDIR)/lib
 check-package : RGBDEF ?= $(PKGDIR)/misc/rgb.txt
+check-package : PALMMAPDIR ?= $(PKGDIR)/misc/
 check: check-package
 
 check-package: $(TESTRANDOM) resultdir-backup
@@ -556,6 +559,7 @@ check-package: $(TESTRANDOM) resultdir-backup
 
 
 # Check after install
+check-install: PALMMAPDIR ?= ""
 check-install: $(TESTRANDOM) resultdir-backup
 	cd $(RESULTDIR); \
 	  $(CHECK_VARS) \
diff --git a/buildtools/debian/README b/buildtools/debian/README
index 2cf921ce..7cefb249 100644
--- a/buildtools/debian/README
+++ b/buildtools/debian/README
@@ -54,8 +54,8 @@ indicate you don't have them, and the build will simply omit some parts.
 For example, if you don't install libx11-dev, the Netpbm build process
 will not build the 'pamx' program.
 
-  libjpeg62-dev
-  libpng12-0-dev
+  libjpeg-dev
+  libpng-dev
   libtiff5-dev
   libx11-dev
   libxml2-dev
@@ -79,9 +79,9 @@ The following Debian packages are the known prerequisites for running Netpbm
 (and the package created by 'mkdeb' knows this).
 
     libc6
-    libjpeg8
+    libjpeg62 or libjpeg8
     libpng12-0
-    libsvga1
+    libsvga1 (available only on older systems)
     libtiff5
     libx11-6
     zlib1g
diff --git a/buildtools/manpage.mk b/buildtools/manpage.mk
index b0f7668c..ef1a1039 100644
--- a/buildtools/manpage.mk
+++ b/buildtools/manpage.mk
@@ -15,7 +15,7 @@ else
   MAKEFILE_DIR := $(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))
 endif
 
-# Python script makeman should be in the same directory.
+# Program 'makeman' should be in the same directory.
 MAKEMAN ?= $(MAKEFILE_DIR)makeman
 
 # Install location of manpages.
@@ -33,10 +33,9 @@ else
   endif
 endif
 
-# In the past the following default value was used.
-# It works if you've done a SVN checkout for netpbm and userguide in the
-# same directory, and you are working in a subdirectory of netpbm, say
-# ./buildtools .
+# In the past, the following default value was used.  It works if you've done
+# a Subversion checkout for source code and userguide in the same directory,
+# and you are working in a subdirectory of netpbm, say ./buildtools .
 # USERGUIDE = ../../userguide
 
 # The files that don't get converted to manual pages.
diff --git a/converter/other/ipdb.c b/converter/other/ipdb.c
index 882bf187..5e4dc82e 100644
--- a/converter/other/ipdb.c
+++ b/converter/other/ipdb.c
@@ -283,8 +283,8 @@ ipdb_image_alloc(const char * const name,
             if (w != 0 && h != 0) {
                 MALLOCARRAY(imgP->data, w * h);
 
-                if (imgP->data) {
-                    memset(imgP->data, 0, sizeof(*(imgP->data)) * w * h);
+                if (imgP->data != NULL) {
+                  memset(imgP->data, 0, sizeof(*(imgP->data)) * w * h);
                 } else
                     failed = true;
             }
diff --git a/converter/other/pamtopdbimg.c b/converter/other/pamtopdbimg.c
index 6454292e..ce2f7659 100644
--- a/converter/other/pamtopdbimg.c
+++ b/converter/other/pamtopdbimg.c
@@ -38,6 +38,7 @@
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
+#include "runlength.h"
 #include "pam.h"
 
 #include "ipdb.h"
@@ -128,6 +129,8 @@ parseCommandLine(int argc, const char ** argv,
         cmdlineP->inputFileName = argv[1];
     else
         pm_error("Program takes at most one argument:  input file name");
+
+    free(option_def);
 }
 
 
@@ -238,91 +241,6 @@ textWrite(TEXT * const textP,
 
 
 
-typedef struct {
-    unsigned int match;
-    uint8_t      buf[128];
-    int          mode;
-    size_t       len;
-    size_t       used;
-    uint8_t *    p;
-} RLE;
-#define MODE_MATCH  0
-#define MODE_LIT    1
-#define MODE_NONE   2
-
-#define reset(r) {                              \
-        (r)->match = 0xffff;                    \
-        (r)->mode  = MODE_NONE;                 \
-        (r)->len   = 0;                         \
-    }
-
-
-
-static void
-putMatch(RLE *  const rleP,
-          size_t const n) {
-
-    *rleP->p++ = 0x80 + n - 1;
-    *rleP->p++ = rleP->match;
-    rleP->used += 2;
-    reset(rleP);
-}
-
-
-
-static void
-putLit(RLE *  const rleP,
-       size_t const n) {
-
-    *rleP->p++ = n - 1;
-    rleP->p = (uint8_t *)memcpy(rleP->p, rleP->buf, n) + n;
-    rleP->used += n + 1;
-    reset(rleP);
-}
-
-
-
-static size_t
-compress(const uint8_t * const inData,
-         size_t          const n_in,
-         uint8_t *       const out) {
-
-    static void (*put[])(RLE *, size_t) = {putMatch, putLit};
-    RLE rle;
-    size_t  i;
-    const uint8_t * p;
-
-    MEMSZERO(&rle);
-    rle.p = out;
-    reset(&rle);
-
-    for (i = 0, p = &inData[0]; i < n_in; ++i, ++p) {
-        if (*p == rle.match) {
-            if (rle.mode == MODE_LIT && rle.len > 1) {
-                putLit(&rle, rle.len - 1);
-                ++rle.len;
-                rle.match = *p;
-            }
-            rle.mode = MODE_MATCH;
-            ++rle.len;
-        } else {
-            if (rle.mode == MODE_MATCH)
-                putMatch(&rle, rle.len);
-            rle.mode         = MODE_LIT;
-            rle.match        = *p;
-            rle.buf[rle.len++] = *p;
-        }
-        if (rle.len == 128)
-            put[rle.mode](&rle, rle.len);
-    }
-    if (rle.len != 0)
-        put[rle.mode](&rle, rle.len);
-
-    return rle.used;
-}
-
-
-
 static void
 compressIfRequired(IPDB *     const pdbP,
                    int        const comp,
@@ -334,38 +252,29 @@ compressIfRequired(IPDB *     const pdbP,
         *compressedSizeP = ipdb_img_size(pdbP->i);
     } else {
         int const uncompressedSz = ipdb_img_size(pdbP->i);
-        
-        /* Allocate for the worst case. */
-        size_t const allocSz = (3 * uncompressedSz + 2)/2;
 
-        uint8_t * data;
-            
-        data = pdbP->i->data;
+        unsigned char * outbuf;
+        size_t          compressedSz;
 
-        MALLOCARRAY(data, allocSz);
-            
-        if (data == NULL)
-            pm_error("Could not get %lu bytes of memory to decompress",
-                     (unsigned long)allocSz);
-        else {
-            size_t compressedSz;
-            compressedSz = compress(pdbP->i->data, uncompressedSz, data);
-            if (comp == IPDB_COMPMAYBE && compressedSz >= uncompressedSz) {
-                /* Return the uncompressed data */
-                free(data);
-                *compressedDataP = pdbP->i->data;
-                *compressedSizeP = uncompressedSz;
-            } else {
-                pdbP->i->compressed = TRUE;
-                if (pdbP->i->type == IMG_GRAY16)
-                    pdbP->i->version = 9;
-                else
-                    pdbP->i->version = 1;
-                if (pdbP->t != NULL)
-                    pdbP->t->r->offset -= uncompressedSz - compressedSz;
-                *compressedDataP = data;
-                *compressedSizeP = compressedSz;
-            }
+        pm_rlenc_allocoutbuf(&outbuf, uncompressedSz, PM_RLE_PALMPDB);
+
+        pm_rlenc_compressbyte(pdbP->i->data, outbuf, PM_RLE_PALMPDB,
+                              uncompressedSz, &compressedSz);
+        if (comp == IPDB_COMPMAYBE && compressedSz >= uncompressedSz) {
+            /* Return the uncompressed data */
+            free(outbuf);
+            *compressedDataP = pdbP->i->data;
+            *compressedSizeP = uncompressedSz;
+        } else {
+            pdbP->i->compressed = TRUE;
+            if (pdbP->i->type == IMG_GRAY16)
+                pdbP->i->version = 9;
+            else
+                pdbP->i->version = 1;
+            if (pdbP->t != NULL)
+                pdbP->t->r->offset -= uncompressedSz - compressedSz;
+            *compressedDataP = outbuf;
+            *compressedSizeP = compressedSz;
         }
     }
 }
@@ -742,6 +651,14 @@ readtxt(IPDB *       const pdbP,
         pm_error("stat of '%s' failed, errno = %d (%s)",
                  noteFileName, errno, strerror(errno));
 
+    /* The maximum size of a memory block that a Palm can allocate is 64K.
+       Abort with error if specified note file is any larger.
+    */
+
+    if (st.st_size + 1 >= 65535)
+        pm_error("Note file is too large: %lu bytes",
+                  (unsigned long) st.st_size);
+
     fP = pm_openr(noteFileName);
 
     MALLOCARRAY(fileContent, st.st_size + 1);
@@ -758,6 +675,8 @@ readtxt(IPDB *       const pdbP,
 
     pm_close(fP);
 
+    fileContent[st.st_size] = 0x00;  /* add terminating NUL char */
+
     /* Chop of trailing newlines */
     for (n = strlen(fileContent) - 1; n >= 0 && fileContent[n] == '\n'; --n)
         fileContent[n] = '\0';
@@ -787,6 +706,9 @@ main(int argc, const char **argv) {
     case MAYBE:        comp = IPDB_COMPMAYBE;  break;
     }
 
+    if (strlen(cmdline.title) > 31)
+        pm_error("Title too long.  Max length is 31 characters.");
+
     pdbP = ipdb_alloc(cmdline.title);
 
     if (pdbP == NULL)
diff --git a/converter/other/pdbimgtopam.c b/converter/other/pdbimgtopam.c
index 3cb4129a..67044109 100644
--- a/converter/other/pdbimgtopam.c
+++ b/converter/other/pdbimgtopam.c
@@ -97,6 +97,8 @@ parseCommandLine(int argc, const char ** argv,
         cmdlineP->inputFileName = argv[1];
     else
         pm_error("Program takes at most one argument:  input file name");
+
+    free(option_def);
 }
 
 
@@ -211,7 +213,8 @@ readCompressed(IMAGE *    const imgP,
 
     if (end_offset == UNKNOWN_OFFSET) {
         /*
-         * Read until EOF. Some of them have an extra zero byte
+         * Read sufficient amount for worst-case compressed image,
+         * or until EOF.  Some of them have an extra zero byte
          * dangling off the end.  I originally thought this was
          * an empty note record (even though there was no record
          * header for it); however, the release notes for Image
@@ -221,12 +224,20 @@ readCompressed(IMAGE *    const imgP,
          * this extra byte and ignore it by paying attention to
          * the image dimensions.
          */
-        MALLOCARRAY(buffer, ipdb_img_size(imgP));
+       size_t const maxCompressedSizeWithBloat = ipdb_img_size(imgP) * 2;
+         /*
+          * Provide a buffer large enough for the worst case.
+          * See note in lib/util/runlength.c .
+          * We do not use pm_rlenc_allocoutbuf() because there is no
+          * guarantee that the encoder that produced the image was
+          * efficient.
+          */
+       MALLOCARRAY(buffer, maxCompressedSizeWithBloat);
 
         if (buffer == NULL)
             retval = ENOMEM;
         else {
-            dataSize = fread(buffer, 1, ipdb_img_size(imgP), fP);
+            dataSize = fread(buffer, 1,  maxCompressedSizeWithBloat, fP);
             if (dataSize <= 0)
                 retval = EIO;
             else
@@ -307,6 +318,8 @@ imageReadData(FILE *   const fileP,
               IMAGE *  const imgP,
               uint32_t const end_offset) {
 
+    size_t const imageSize = ipdb_img_size(imgP);  
+
     int retval;
     size_t dataSize;
     uint8_t * buffer;
@@ -318,14 +331,24 @@ imageReadData(FILE *   const fileP,
          * Compressed data can cross row boundaries so we decompress
          * the data here to avoid messiness in the row access functions.
          */
-        if (dataSize != ipdb_img_size(imgP)) {
-            decompress(buffer, dataSize, ipdb_img_size(imgP), &imgP->data);
+        if (dataSize < imageSize || imgP->version == 1) {
+            if (imgP->version == 0)
+                pm_message("Image header says raster data is uncompressed.  "
+                           "Encountered only %u instead of the "
+                           "required %u bytes.  Assuming compressed mode.",
+                           (unsigned)dataSize, (unsigned)imageSize);
+            decompress(buffer, dataSize, imageSize, &imgP->data);
             if (imgP->data == NULL)
                 retval = ENOMEM;
             else
                 imgP->compressed = true;
             free(buffer);
         } else {
+            if (dataSize > imageSize)
+                pm_message("Image header says raster data is uncompressed. "
+                           "Encountered %u instead of the required %u bytes. "
+                           "Assuming uncompressed mode.",
+                           (unsigned)dataSize, (unsigned)imageSize);
             imgP->compressed = false;
             imgP->data       = buffer;
             /* Storage at 'buffer' now belongs to *imgP */
diff --git a/converter/other/pnmtopalm/palm.h b/converter/other/pnmtopalm/palm.h
index 718a66cf..0edf9a28 100644
--- a/converter/other/pnmtopalm/palm.h
+++ b/converter/other/pnmtopalm/palm.h
@@ -28,9 +28,18 @@
 #define PALM_FORMAT_565LE       0x02    /* Palm says internal use only */
 #define PALM_FORMAT_INDEXEDLE   0x03    /* Palm says internal use only */
 
-typedef unsigned long Color_s;
+typedef unsigned long ColormapEntry;
+    /* A entry in a Colormap.  It is an encoding of 4 bytes as the integer
+       that those 4 bytes would represent in pure binary:
 
-typedef Color_s * Color;
+          MSB 0: the color index
+              1: red intensity
+              2: green intensity
+          LSB 3: blue intensity
+
+       The intensities are on a scale with a certain maxval (that must be
+       specified to interpret a ColormapEntry).
+    */
 
 typedef struct {
     unsigned int nentries;
@@ -39,24 +48,11 @@ typedef struct {
         /* number of colors actually in 'color_entries' -- entries are
            filled from 0 consecutively, one color per entry.
         */
-    Color_s * color_entries;  /* Array of colors */
-} Colormap_s;
-
-typedef Colormap_s * Colormap;
+    ColormapEntry * color_entries;  /* Array of colors */
+} Colormap;
 
 qsort_comparison_fn palmcolor_compare_indices;
 
 qsort_comparison_fn palmcolor_compare_colors;
 
-Colormap
-palmcolor_build_custom_8bit_colormap(unsigned int const rows,
-                                     unsigned int const cols,
-                                     pixel **     const pixels);
-
-Colormap
-palmcolor_build_default_8bit_colormap(void);
-
-Colormap
-palmcolor_read_colormap (FILE * const ifP);
-
 #endif
diff --git a/converter/other/pnmtopalm/palmcolormap.c b/converter/other/pnmtopalm/palmcolormap.c
index 0f47558c..1341ca2b 100644
--- a/converter/other/pnmtopalm/palmcolormap.c
+++ b/converter/other/pnmtopalm/palmcolormap.c
@@ -4,18 +4,46 @@
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "pnm.h"
-
 #include "palm.h"
 
+#include "palmcolormap.h"
+
+
+
+static pixval
+scaleSample(pixval const arg,
+            pixval const oldMaxval,
+            pixval const newMaxval) {
+
+    return (arg * newMaxval + oldMaxval/2) / oldMaxval;
+}
+
+
+
+ColormapEntry
+palmcolor_mapEntryColorFmPixel(pixel  const color,
+                               pixval const maxval,
+                               pixval const newMaxval) {
+
+
+    return
+        0
+        | (scaleSample(PPM_GETR(color), maxval, newMaxval) << 16) 
+        | (scaleSample(PPM_GETG(color), maxval, newMaxval) <<  8)
+        | (scaleSample(PPM_GETB(color), maxval, newMaxval) <<  0);
+}
+
+
+
 int
 palmcolor_compare_indices(const void * const p1,
                           const void * const p2) {
 /*----------------------------------------------------------------------------
    This is a 'qsort' collation function.
 -----------------------------------------------------------------------------*/
-    if ((*((Color) p1) & 0xFF000000) < (*((Color) p2) & 0xFF000000))
+    if ((*((ColormapEntry *) p1) & 0xFF000000) < (*((ColormapEntry *) p2) & 0xFF000000))
         return -1;
-    else if ((*((Color) p1) & 0xFF000000) > (*((Color) p2) & 0xFF000000))
+    else if ((*((ColormapEntry *) p1) & 0xFF000000) > (*((ColormapEntry *) p2) & 0xFF000000))
         return 1;
     else
         return 0;
@@ -165,86 +193,88 @@ static int PalmPalette8bpp[256][3] =
 
 
 
-Colormap
+Colormap *
 palmcolor_build_default_8bit_colormap(void) {
 
     unsigned int i;
 
-    Colormap cm;
+    Colormap * cmP;
 
-    MALLOCVAR_NOFAIL(cm);
-    cm->nentries = 232;
-    MALLOCARRAY_NOFAIL(cm->color_entries, cm->nentries);
+    MALLOCVAR_NOFAIL(cmP);
+    cmP->nentries = 232;
+    MALLOCARRAY_NOFAIL(cmP->color_entries, cmP->nentries);
 
     /* Fill in the colors */
     for (i = 0; i < 231; ++i) {
-        cm->color_entries[i] = ((i << 24) |
+        cmP->color_entries[i] = ((i << 24) |
                                 (PalmPalette8bpp[i][0] << 16) |
                                 (PalmPalette8bpp[i][1] << 8) |
                                 (PalmPalette8bpp[i][2]));
     }
-    cm->color_entries[231] = 0xFF000000;
-    cm->ncolors = 232;
+    cmP->color_entries[231] = 0xFF000000;
+    cmP->ncolors = 232;
 
     /* now sort the table */
-    qsort (cm->color_entries, cm->ncolors, sizeof(Color_s), 
-           palmcolor_compare_colors);
-    return cm;
+    qsort(cmP->color_entries, cmP->ncolors, sizeof(ColormapEntry), 
+          palmcolor_compare_colors);
+    return cmP;
 }
 
 
 
-Colormap
-palmcolor_build_custom_8bit_colormap(unsigned int const rows,
+Colormap *
+palmcolor_build_custom_8bit_colormap(pixel **     const pixels,
+                                     unsigned int const rows,
                                      unsigned int const cols,
-                                     pixel **     const pixels) {
+                                     pixval       const maxval) {
+
     unsigned int row;
-    Colormap colormap;
+    Colormap * colormapP;
 
-    MALLOCVAR_NOFAIL(colormap);
-    colormap->nentries = 256;
-    MALLOCARRAY_NOFAIL(colormap->color_entries, colormap->nentries);
-    colormap->ncolors = 0;  /* initial value */
+    MALLOCVAR_NOFAIL(colormapP);
+    colormapP->nentries = 256;
+    MALLOCARRAY_NOFAIL(colormapP->color_entries, colormapP->nentries);
+    colormapP->ncolors = 0;  /* initial value */
     
     for (row = 0; row < rows; ++row) {
         unsigned int col;
         for (col = 0; col < cols; ++col) {
-            Color found;
-            Color_s temp;
-
-            temp = ((PPM_GETR(pixels[row][col]) << 16) |
-                    (PPM_GETG(pixels[row][col]) << 8) |
-                    PPM_GETB(pixels[row][col]));
-            found = (bsearch (&temp,
-                              colormap->color_entries, colormap->ncolors,
-                              sizeof(Color_s), palmcolor_compare_colors));
-            if (!found) {
-                if (colormap->ncolors >= colormap->nentries)
+            ColormapEntry * foundEntryP;
+            ColormapEntry const searchTarget =
+                palmcolor_mapEntryColorFmPixel(pixels[row][col], maxval, 255);
+
+            foundEntryP =
+                bsearch(&searchTarget,
+                        colormapP->color_entries, colormapP->ncolors,
+                        sizeof(ColormapEntry), palmcolor_compare_colors);
+            if (!foundEntryP) {
+                if (colormapP->ncolors >= colormapP->nentries)
                     pm_error("Too many colors for custom colormap "
-                             "(max 256).  "
+                             "(max %u).  "
                              "Try using pnmquant to reduce the number "
-                             "of colors.");
+                             "of colors.", colormapP->nentries);
                 else {
                     /* add the new color, and re-sort */
-                    temp |= ((colormap->ncolors) << 24);
-                    colormap->color_entries[colormap->ncolors] = temp;
-                    colormap->ncolors += 1;
-                    qsort(colormap->color_entries, colormap->ncolors, 
-                          sizeof(Color_s), palmcolor_compare_colors);
+                    unsigned int const colorIndex = colormapP->ncolors++;
+                    ColormapEntry const newEntry =
+                        searchTarget | (colorIndex << 24);
+                    colormapP->color_entries[colorIndex] = newEntry;
+                    qsort(colormapP->color_entries, colormapP->ncolors, 
+                          sizeof(ColormapEntry), palmcolor_compare_colors);
                 }
             }
         }
     }
-    return colormap;
+    return colormapP;
 }
 
 
 
-Colormap
+Colormap *
 palmcolor_read_colormap (FILE * const ifP) {
 
     unsigned short ncolors;
-    Colormap retval;
+    Colormap * retval;
     int rc;
     
     rc = pm_readbigshort(ifP, (short *) &ncolors);
@@ -252,13 +282,13 @@ palmcolor_read_colormap (FILE * const ifP) {
         retval = NULL;
     else {
         long colorentry;
-        Colormap colormap;
+        Colormap * colormapP;
         unsigned int i;
         bool error;
 
-        MALLOCVAR_NOFAIL(colormap);
-        colormap->nentries = ncolors;
-        MALLOCARRAY_NOFAIL(colormap->color_entries, colormap->nentries);
+        MALLOCVAR_NOFAIL(colormapP);
+        colormapP->nentries = ncolors;
+        MALLOCARRAY_NOFAIL(colormapP->color_entries, colormapP->nentries);
         
         for (i = 0, error = FALSE;  i < ncolors && !error;  ++i) {
             int rc;
@@ -266,15 +296,15 @@ palmcolor_read_colormap (FILE * const ifP) {
             if (rc != 0)
                 error = TRUE;
             else
-                colormap->color_entries[i] = (colorentry & 0xFFFFFFFF);
+                colormapP->color_entries[i] = (colorentry & 0xFFFFFFFF);
         }
         if (error) {
-            free (colormap->color_entries);
-            free (colormap);
+            free (colormapP->color_entries);
+            free (colormapP);
             retval = NULL;
         } else {
-            colormap->ncolors = ncolors;
-            retval = colormap;
+            colormapP->ncolors = ncolors;
+            retval = colormapP;
         }
     }
     return retval;
diff --git a/converter/other/pnmtopalm/palmcolormap.h b/converter/other/pnmtopalm/palmcolormap.h
new file mode 100644
index 00000000..cbdb2031
--- /dev/null
+++ b/converter/other/pnmtopalm/palmcolormap.h
@@ -0,0 +1,25 @@
+#ifndef PALMCOLORMAP_H_INCLUDED
+#define PALMCOLORMAP_H_INCLUDED
+
+#include <stdio.h>
+#include "ppm.h"
+#include "palm.h"
+
+ColormapEntry
+palmcolor_mapEntryColorFmPixel(pixel    const color,
+                               pixval const maxval,
+                               pixval const newMaxval);
+
+Colormap *
+palmcolor_build_custom_8bit_colormap(pixel **     const pixels,
+                                     unsigned int const rows,
+                                     unsigned int const cols,
+                                     pixval       const maxval);
+
+Colormap *
+palmcolor_build_default_8bit_colormap(void);
+
+Colormap *
+palmcolor_read_colormap (FILE * const ifP);
+
+#endif
diff --git a/converter/other/pnmtopalm/palmtopnm.c b/converter/other/pnmtopalm/palmtopnm.c
index 0f76207d..b58c99bc 100644
--- a/converter/other/pnmtopalm/palmtopnm.c
+++ b/converter/other/pnmtopalm/palmtopnm.c
@@ -21,17 +21,18 @@
 #include "mallocvar.h"
 
 #include "palm.h"
+#include "palmcolormap.h"
 
 
 
-enum palmCompressionType {
+enum PalmCompressionType {
     COMPRESSION_NONE, 
     COMPRESSION_RLE, 
     COMPRESSION_SCANLINE,
     COMPRESSION_PACKBITS
 };
 
-struct palmHeader {
+struct PalmHeader {
     unsigned short cols;
     unsigned short rows;
     unsigned short bytesPerRow;
@@ -46,7 +47,7 @@ struct palmHeader {
     unsigned int   pixelSize;
     unsigned char  version;
     unsigned int   transparentIndex;
-    enum palmCompressionType compressionType;
+    enum PalmCompressionType compressionType;
     /* version 3 encoding specific */
     unsigned char  size;
     unsigned char  pixelFormat;
@@ -56,7 +57,7 @@ struct palmHeader {
 
 
 
-struct directPixelFormat {
+struct DirectPixelFormat {
     unsigned int redbits;
     unsigned int greenbits;
     unsigned int bluebits;
@@ -64,15 +65,15 @@ struct directPixelFormat {
 
 
 
-struct directColorInfo {
-    struct directPixelFormat pixelFormat;
-    Color_s                  transparentColor;
+struct DirectColorInfo {
+    struct DirectPixelFormat pixelFormat;
+    ColormapEntry            transparentColor;
 };
 
 
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -85,13 +86,13 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo *cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc( 100*sizeof( optEntry ) );
+    optEntry * option_def;
         /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
@@ -100,6 +101,8 @@ parseCommandLine(int argc, char ** argv,
 
     unsigned int option_def_index;
 
+    MALLOCARRAY_NOFAIL(option_def, 100);
+    
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0, "verbose",     OPT_FLAG, NULL,
             &cmdlineP->verbose,  0);
@@ -114,7 +117,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
@@ -135,6 +138,7 @@ parseCommandLine(int argc, char ** argv,
             pm_error("Too many arguments (%d).  The only non-option "
                      "argument is the file name", argc-1);
     }
+    free(option_def);
 }
 
 
@@ -183,7 +187,7 @@ skipbytes(FILE *       const ifP,
 
 static void
 interpretCompression(unsigned char              const compressionValue,
-                     enum palmCompressionType * const compressionTypeP) {
+                     enum PalmCompressionType * const compressionTypeP) {
     
     switch (compressionValue) {
     case PALM_COMPRESSION_RLE:
@@ -292,7 +296,7 @@ readRestOfHeaderOld(FILE *           const ifP,
 
 
 static void
-interpretHeader(struct palmHeader * const palmHeaderP,
+interpretHeader(struct PalmHeader * const palmHeaderP,
                 short               const cols,
                 short               const rows,
                 short               const bytesPerRow,
@@ -345,7 +349,7 @@ interpretHeader(struct palmHeader * const palmHeaderP,
 static void
 readHeader(FILE *              const ifP,
            unsigned int        const requestedRendition,
-           struct palmHeader * const palmHeaderP) {
+           struct PalmHeader * const palmHeaderP) {
 /*----------------------------------------------------------------------------
    Read the Palm Bitmap header from the file 'ifP'.  Read past all
    renditions up to 'requestedRendition' and read the header of that
@@ -441,8 +445,8 @@ yesno(bool const arg) {
 
 
 static void
-reportPalmHeader(struct palmHeader      const palmHeader,
-                 struct directColorInfo const directColorInfo) {
+reportPalmHeader(struct PalmHeader      const palmHeader,
+                 struct DirectColorInfo const directColorInfo) {
 
     const char *ctype;
 
@@ -473,7 +477,7 @@ reportPalmHeader(struct palmHeader      const palmHeader,
     if (palmHeader.hasTransparency) {
         if (palmHeader.directColor) {
             /* Copied from doTransparent(...) */
-            Color_s const color = directColorInfo.transparentColor;
+            ColormapEntry const color = directColorInfo.transparentColor;
             pm_message("Transparent value: #%02x%02x%02x", 
                        (unsigned int)((color >> 16) & 0xFF), 
                        (unsigned int)((color >>  8) & 0xFF), 
@@ -489,7 +493,7 @@ reportPalmHeader(struct palmHeader      const palmHeader,
 
 
 static void
-determineOutputFormat(struct palmHeader const palmHeader,
+determineOutputFormat(struct PalmHeader const palmHeader,
                       int *             const formatP,
                       xelval *          const maxvalP) {
 
@@ -515,7 +519,7 @@ determineOutputFormat(struct palmHeader const palmHeader,
 
 static void
 readRgbFormat(FILE *                     const ifP,
-              struct directPixelFormat * const pixelFormatP) {
+              struct DirectPixelFormat * const pixelFormatP) {
 
     unsigned char r, g, b;
 
@@ -537,8 +541,8 @@ readRgbFormat(FILE *                     const ifP,
 
 
 static void
-readDirectTransparentColor(FILE *    const ifP,
-                           Color_s * const colorP) {
+readDirectTransparentColor(FILE *          const ifP,
+                           ColormapEntry * const colorP) {
 
     unsigned char r, g, b;
 
@@ -553,8 +557,8 @@ readDirectTransparentColor(FILE *    const ifP,
 
 static void
 readDirectInfoType(FILE *                   const ifP,
-                   struct palmHeader        const palmHeader,
-                   struct directColorInfo * const directInfoTypeP) {
+                   struct PalmHeader        const palmHeader,
+                   struct DirectColorInfo * const directInfoTypeP) {
 /*----------------------------------------------------------------------------
    Read the Palm Bitmap Direct Info Type section, if any.
 
@@ -596,8 +600,8 @@ readDirectInfoType(FILE *                   const ifP,
 
 static void
 readColormap(FILE *            const ifP,
-             struct palmHeader const palmHeader,
-             Colormap *        const colormapP) {
+             struct PalmHeader const palmHeader,
+             Colormap **       const colormapPP) {
 /*----------------------------------------------------------------------------
    Read the colormap, if any from the Palm Bitmap.
 
@@ -605,18 +609,18 @@ readColormap(FILE *            const ifP,
    return an undefined value as *colormapP.
 -----------------------------------------------------------------------------*/
     if (palmHeader.hasColormap)
-        *colormapP = palmcolor_read_colormap(ifP);
+        *colormapPP = palmcolor_read_colormap(ifP);
 }
 
 
 
 static void
-getColorInfo(struct palmHeader        const palmHeader,
-             struct directColorInfo   const directInfoType,
-             Colormap                 const colormapFromImage,
-             Colormap *               const colormapP,
+getColorInfo(struct PalmHeader        const palmHeader,
+             struct DirectColorInfo   const directInfoType,
+             Colormap *               const colormapFromImageP,
+             Colormap **              const colormapPP,
              unsigned int *           const ncolorsP,
-             struct directColorInfo * const directColorInfoP) {
+             struct DirectColorInfo * const directColorInfoP) {
 /*----------------------------------------------------------------------------
    Gather color encoding information from the various sources.
 
@@ -626,7 +630,7 @@ getColorInfo(struct palmHeader        const palmHeader,
    If it's a version 3 direct color, the pixel format must be "565".
 -----------------------------------------------------------------------------*/
     if (palmHeader.version == 3 && palmHeader.directColor) {
-        *colormapP = NULL;
+        *colormapPP = NULL;
 
         assert(palmHeader.pixelFormat == PALM_FORMAT_565);
 
@@ -646,18 +650,18 @@ getColorInfo(struct palmHeader        const palmHeader,
             ((((palmHeader.transparentValue >>  0) & 0x1F) * 255 / 0x1F)
              <<  0);
     } else if (palmHeader.directColor) {
-        *colormapP = NULL;
+        *colormapPP = NULL;
         *directColorInfoP = directInfoType;
     } else if (palmHeader.hasColormap)
-        *colormapP = colormapFromImage;
+        *colormapPP = colormapFromImageP;
     else if (palmHeader.pixelSize >= 8) {
-        Colormap colormap;
-        colormap = palmcolor_build_default_8bit_colormap();
-        qsort(colormap->color_entries, colormap->ncolors, 
-              sizeof(Color_s), palmcolor_compare_indices);
-        *colormapP = colormap;
+        Colormap * const colormapP =
+            palmcolor_build_default_8bit_colormap();
+        qsort(colormapP->color_entries, colormapP->ncolors, 
+              sizeof(ColormapEntry), palmcolor_compare_indices);
+        *colormapPP = colormapP;
     } else
-        *colormapP = NULL;
+        *colormapPP = NULL;
 
     *ncolorsP = 1 << palmHeader.pixelSize;
 }
@@ -670,8 +674,8 @@ doTransparent(FILE *                 const ofP,
               bool                   const directColor,
               unsigned char          const transparentIndex,
               unsigned char          const pixelSize,
-              Colormap               const colormap,
-              struct directColorInfo const directColorInfo) {
+              Colormap *             const colormapP,
+              struct DirectColorInfo const directColorInfo) {
 /*----------------------------------------------------------------------------
    Generate a PNM comment on *ofP telling what color in the raster is
    supposed to be transparent.
@@ -681,24 +685,25 @@ doTransparent(FILE *                 const ofP,
    RGB_ALPHA and GRAYSCALE_ALPHA tuple types).
 -----------------------------------------------------------------------------*/
     if (hasTransparency) {
-        if (colormap) {
-            Color_s const color = transparentIndex << 24;
-            Color const actualColor = (bsearch(&color,
-                                               colormap->color_entries, 
-                                               colormap->ncolors,
-                                               sizeof(color), 
-                                               palmcolor_compare_indices));
-            if (!actualColor)
+        if (colormapP) {
+            ColormapEntry const searchTarget = transparentIndex << 24;
+            ColormapEntry * const foundEntryP =
+                bsearch(&searchTarget,
+                        colormapP->color_entries, 
+                        colormapP->ncolors,
+                        sizeof(searchTarget), 
+                        palmcolor_compare_indices);
+            if (!foundEntryP)
                 pm_error("Invalid input; transparent index %u "
                          "is not among the %u colors in the image's colormap",
-                         transparentIndex, colormap->ncolors);
+                         transparentIndex, colormapP->ncolors);
 
             fprintf(ofP, "#%02x%02x%02x\n", 
-                   (unsigned int) ((*actualColor >> 16) & 0xFF),
-                   (unsigned int) ((*actualColor >>  8) & 0xFF), 
-                   (unsigned int) ((*actualColor >>  0) & 0xFF));
+                   (unsigned int) ((*foundEntryP >> 16) & 0xFF),
+                   (unsigned int) ((*foundEntryP >>  8) & 0xFF), 
+                   (unsigned int) ((*foundEntryP >>  0) & 0xFF));
         } else if (directColor) {
-            Color_s const color = directColorInfo.transparentColor;
+            ColormapEntry const color = directColorInfo.transparentColor;
             fprintf(ofP, "#%02x%02x%02x\n", 
                    (unsigned int)((color >> 16) & 0xFF), 
                    (unsigned int)((color >>  8) & 0xFF), 
@@ -907,7 +912,7 @@ static void
 readDecompressedRow(FILE *                   const ifP,
                     unsigned char *          const palmrow,
                     unsigned char *          const lastrow,
-                    enum palmCompressionType const compressionType,
+                    enum PalmCompressionType const compressionType,
                     unsigned int             const bytesPerRow,
                     unsigned int             const pixelSize,
                     bool                     const firstRow) {
@@ -1000,46 +1005,50 @@ static void
 convertRowToPnmNotDirect(const unsigned char * const palmrow,
                          xel *                 const xelrow,
                          unsigned int          const cols,
-                         Colormap              const colormap,
+                         Colormap *            const colormapP,
                          xelval *              const graymap,
                          unsigned int *        const seen,
                          unsigned int          const pixelSize) {
 
     unsigned int const mask = (1 << pixelSize) - 1;
 
-    const unsigned char *inbyte;
+    const unsigned char * inbyteP;
     unsigned int inbit;
     unsigned int j;
+
+    assert(pixelSize <= 8);
     
     inbit = 8 - pixelSize;
-    inbyte = palmrow;
+    inbyteP = &palmrow[0];
     for (j = 0; j < cols; ++j) {
-        short const color = ((*inbyte) & (mask << inbit)) >> inbit;
+        short const color = (*inbyteP & (mask << inbit)) >> inbit;
         if (seen)
             ++seen[color];
         
-        if (colormap) {
-            Color_s const color2      = color << 24;
-            Color   const actualColor = (bsearch (&color2,
-                                                  colormap->color_entries, 
-                                                  colormap->ncolors,
-                                                  sizeof(color2), 
-                                                  palmcolor_compare_indices));
-            if (!actualColor)
+        if (colormapP) {
+            ColormapEntry const searchTarget = color << 24;
+            ColormapEntry * const foundEntryP =
+                bsearch(&searchTarget,
+                        colormapP->color_entries, 
+                        colormapP->ncolors,
+                        sizeof(searchTarget), 
+                        palmcolor_compare_indices);
+
+            if (!foundEntryP)
                 pm_error("Invalid input.  A color index in column %u "
                          "is %u, which is not among the %u colors "
                          "in the colormap",
-                         j, color, colormap->ncolors);
+                         j, color, colormapP->ncolors);
 
             PPM_ASSIGN(xelrow[j], 
-                       (*actualColor >> 16) & 0xFF, 
-                       (*actualColor >>  8) & 0xFF, 
-                       (*actualColor >>  0) & 0xFF);
+                       (*foundEntryP >> 16) & 0xFF, 
+                       (*foundEntryP >>  8) & 0xFF, 
+                       (*foundEntryP >>  0) & 0xFF);
         } else
             PNM_ASSIGN1(xelrow[j], graymap[color]);
         
         if (!inbit) {
-            ++inbyte;
+            ++inbyteP;
             inbit = 8 - pixelSize;
         } else
             inbit -= pixelSize;
@@ -1050,9 +1059,9 @@ convertRowToPnmNotDirect(const unsigned char * const palmrow,
 
 static void
 writePnm(FILE *            const ofP,
-         struct palmHeader const palmHeader,
+         struct PalmHeader const palmHeader,
          FILE *            const ifP,
-         Colormap          const colormap,
+         Colormap *        const colormapP,
          xelval *          const graymap,
          unsigned int      const nColors,
          int               const format,
@@ -1105,7 +1114,7 @@ writePnm(FILE *            const ofP,
             assert(palmHeader.pixelSize == 16);
             convertRowToPnmDirect(palmrow, xelrow, cols, maxval, seen);
         } else
-            convertRowToPnmNotDirect(palmrow, xelrow, cols, colormap, graymap,
+            convertRowToPnmNotDirect(palmrow, xelrow, cols, colormapP, graymap,
                                      seen, palmHeader.pixelSize);
 
         pnm_writepnmrow(ofP, xelrow, cols, maxval, format, 0);
@@ -1119,28 +1128,29 @@ writePnm(FILE *            const ofP,
 
 static void
 showHistogram(unsigned int * const seen,
-              Colormap       const colormap,
+              Colormap *     const colormapP,
               const xelval * const graymap,
               unsigned int   const ncolors) {
 
     unsigned int colorIndex;
     
     for (colorIndex = 0;  colorIndex < ncolors; ++colorIndex) {
-        if (!colormap)
+        if (!colormapP)
             pm_message("%.3d -> %.3d:  %d", 
                        colorIndex, graymap[colorIndex], seen[colorIndex]);
         else {
-            Color_s const color = colorIndex << 24;
-            Color const actualColor = (bsearch(&color,
-                                               colormap->color_entries, 
-                                               colormap->ncolors,
-                                               sizeof(color), 
-                                               palmcolor_compare_indices));
-            if (actualColor)
+            ColormapEntry const searchTarget = colorIndex << 24;
+            ColormapEntry * const foundEntryP =
+                bsearch(&searchTarget,
+                        colormapP->color_entries, 
+                        colormapP->ncolors,
+                        sizeof(searchTarget), 
+                        palmcolor_compare_indices);
+            if (foundEntryP)
                 pm_message("%.3d -> %ld,%ld,%ld:  %d", colorIndex,
-                           (*actualColor >> 16) & 0xFF,
-                           (*actualColor >> 8) & 0xFF,
-                           (*actualColor & 0xFF), seen[colorIndex]);
+                           (*foundEntryP >> 16) & 0xFF,
+                           (*foundEntryP >> 8) & 0xFF,
+                           (*foundEntryP & 0xFF), seen[colorIndex]);
         }
     }
 }
@@ -1148,22 +1158,21 @@ showHistogram(unsigned int * const seen,
 
 
 int
-main(int argc, char **argv) {
+main(int argc, const char **argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
 
-    FILE* ifP;
-    struct palmHeader palmHeader;
-    struct directColorInfo directInfoType;
-    Colormap colormapFromImage;
-    Colormap colormap;
-    struct directColorInfo directColorInfo;
+    FILE * ifP;
+    struct PalmHeader palmHeader;
+    struct DirectColorInfo directInfoType;
+    Colormap * colormapFromImageP;
+    Colormap * colormapP;
+    struct DirectColorInfo directColorInfo;
     int format;
     xelval maxval;
     unsigned int nColors;
 
-    /* Parse default params */
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -1173,12 +1182,12 @@ main(int argc, char **argv) {
 
     readDirectInfoType(ifP, palmHeader, &directInfoType);
    
-    readColormap(ifP, palmHeader, &colormapFromImage);
+    readColormap(ifP, palmHeader, &colormapFromImageP);
     
     determineOutputFormat(palmHeader, &format, &maxval);
     
-    getColorInfo(palmHeader, directInfoType, colormapFromImage,
-                 &colormap, &nColors, &directColorInfo);
+    getColorInfo(palmHeader, directInfoType, colormapFromImageP,
+                 &colormapP, &nColors, &directColorInfo);
 
     if (cmdline.verbose)
         reportPalmHeader(palmHeader, directColorInfo);
@@ -1187,7 +1196,7 @@ main(int argc, char **argv) {
         doTransparent(stdout, 
                       palmHeader.hasTransparency, palmHeader.directColor,
                       palmHeader.transparentIndex, 
-                      palmHeader.pixelSize, colormap, directColorInfo);
+                      palmHeader.pixelSize, colormapP, directColorInfo);
     else {
         unsigned int * seen;
         xelval * graymap;
@@ -1195,11 +1204,11 @@ main(int argc, char **argv) {
         graymap = createGraymap(nColors, maxval);
 
         writePnm(stdout,
-                 palmHeader, ifP, colormap, graymap, nColors, format, maxval, 
+                 palmHeader, ifP, colormapP, graymap, nColors, format, maxval, 
                  cmdline.showhist ? &seen : NULL);
         
         if (cmdline.showhist)
-            showHistogram(seen, colormap, graymap, nColors);
+            showHistogram(seen, colormapP, graymap, nColors);
         
         free(graymap);
     }
diff --git a/converter/other/pnmtopalm/pnmtopalm.c b/converter/other/pnmtopalm/pnmtopalm.c
index a7d1fd46..25c8af2e 100644
--- a/converter/other/pnmtopalm/pnmtopalm.c
+++ b/converter/other/pnmtopalm/pnmtopalm.c
@@ -29,33 +29,37 @@
 
 #include "pm_c_util.h"
 #include "pnm.h"
-#include "palm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "runlength.h"
 
-enum compressionType {COMP_NONE, COMP_SCANLINE, COMP_RLE, COMP_PACKBITS};
+#include "palm.h"
+#include "palmcolormap.h"
+
+enum CompressionType {COMP_NONE, COMP_SCANLINE, COMP_RLE, COMP_PACKBITS};
 
-struct cmdline_info {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
     const char * inputFilespec;  /* Filespecs of input files */
-    const char * transparent;    /* -transparent value.  Null if unspec */
-    unsigned int depth;         /* -depth value.  0 if unspec */
-    unsigned int maxdepth;      /* -maxdepth value.  0 if unspec */
-    enum compressionType compression;
+    const char * transparent;
+    unsigned int depthSpec;
+    unsigned int depth;
+    unsigned int maxdepthSpec;
+    unsigned int maxdepth;
+    enum CompressionType compression;
     unsigned int verbose;
     unsigned int colormap;
-    unsigned int offset;        /* -offset specified */
-    unsigned int density;       /* screen density */
+    unsigned int offset;
+    unsigned int density;
     unsigned int withdummy;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
+parseCommandLine(int argc, const char ** argv, struct CmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
@@ -64,7 +68,7 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
     optEntry *option_def;
     unsigned int option_def_index;
 
-    unsigned int transSpec, depthSpec, maxdepthSpec, densitySpec;
+    unsigned int transSpec, densitySpec;
     unsigned int scanline_compression, rle_compression, packbits_compression;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
@@ -73,9 +77,9 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
     OPTENT3(0, "transparent",      OPT_STRING, 
             &cmdlineP->transparent, &transSpec, 0);
     OPTENT3(0, "depth",            OPT_UINT, 
-            &cmdlineP->depth,       &depthSpec, 0);
+            &cmdlineP->depth,       &cmdlineP->depthSpec, 0);
     OPTENT3(0, "maxdepth",         OPT_UINT, 
-            &cmdlineP->maxdepth,    &maxdepthSpec, 0);
+            &cmdlineP->maxdepth,    &cmdlineP->maxdepthSpec, 0);
     OPTENT3(0, "scanline_compression", OPT_FLAG, 
             NULL,                   &scanline_compression, 0);
     OPTENT3(0, "rle_compression",  OPT_FLAG, 
@@ -97,28 +101,26 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
     opt.short_allowed = FALSE; /* We have some short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
-    if (depthSpec) {
+    if (cmdlineP->depthSpec) {
         if (cmdlineP->depth != 1 && cmdlineP->depth != 2 
             && cmdlineP->depth != 4 && cmdlineP->depth != 8
             && cmdlineP->depth != 16)
             pm_error("invalid value for -depth: %u.  Valid values are "
                      "1, 2, 4, 8, and 16", cmdlineP->depth);
-    } else
-        cmdlineP->depth = 0;
+    }
 
-    if (maxdepthSpec) {
+    if (cmdlineP->maxdepthSpec) {
         if (cmdlineP->maxdepth != 1 && cmdlineP->maxdepth != 2 
             && cmdlineP->maxdepth != 4 && cmdlineP->maxdepth != 8
             && cmdlineP->maxdepth != 16)
             pm_error("invalid value for -maxdepth: %u.  Valid values are "
                      "1, 2, 4, 8, and 16", cmdlineP->maxdepth);
-    } else
-        cmdlineP->maxdepth = 0;
+    }
 
-    if (depthSpec && maxdepthSpec && 
+    if (cmdlineP->depthSpec && cmdlineP->maxdepthSpec && 
         cmdlineP->depth > cmdlineP->maxdepth)
         pm_error("-depth value (%u) is greater than -maxdepth (%u) value.",
                  cmdlineP->depth, cmdlineP->maxdepth);
@@ -132,8 +134,8 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
             cmdlineP->density != PALM_DENSITY_DOUBLE &&
             cmdlineP->density != PALM_DENSITY_TRIPLE &&
             cmdlineP->density != PALM_DENSITY_QUADRUPLE)
-            pm_error("Invalid value for -density: %d.  Valid values are "
-                     "%d, %d, %d, %d and %d.", cmdlineP->density, 
+            pm_error("Invalid value for -density: %u.  Valid values are "
+                     "%u, %u, %u, %u and %u.", cmdlineP->density, 
                      PALM_DENSITY_LOW, PALM_DENSITY_ONEANDAHALF,
                      PALM_DENSITY_DOUBLE, PALM_DENSITY_TRIPLE,
                      PALM_DENSITY_QUADRUPLE);
@@ -164,7 +166,7 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
         
     if (argc-1 > 1)
         pm_error("This program takes at most 1 argument: the file name.  "
-                 "You specified %d", argc-1);
+                 "You specified %u", argc-1);
     else if (argc-1 > 0) 
         cmdlineP->inputFilespec = argv[1];
     else
@@ -173,108 +175,241 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
 
 
 
+static xelval
+scaleSample(pixval const arg,
+            pixval const oldMaxval,
+            pixval const newMaxval) {
+
+    return (arg * newMaxval + oldMaxval/2) / oldMaxval;
+}
+
+
+
+static void
+determinePalmFormatPgm(xelval               const maxval, 
+                       bool                 const bppSpecified,
+                       unsigned int         const bpp,
+                       bool                 const maxBppSpecified,
+                       unsigned int         const maxBpp, 
+                       bool                 const wantCustomColormap,
+                       enum CompressionType const compression,
+                       bool                 const verbose,
+                       unsigned int *       const bppP) {
+        
+    /* We can usually handle this one, but may not have enough pixels.  So
+       check.
+    */
+
+    if (wantCustomColormap)
+        pm_error("You specified -colormap with a black and white input"
+                 "image.  -colormap is valid only with color.");
+    if (bppSpecified)
+        *bppP = bpp;
+    else if (maxBppSpecified && (maxval >= (1 << maxBpp)))
+        *bppP = maxBpp;
+    else if (compression != COMP_NONE && maxval > 255)
+        *bppP = 8;
+    else if (maxval > 16)
+        *bppP = 4;
+    else {
+        /* scale to minimum number of bpp needed */
+        unsigned int bpp;
+        for (bpp = 1;  (1 << bpp) < maxval;  bpp *= 2)
+            ;
+        *bppP = bpp;
+    }
+    if (verbose)
+        pm_message("output is grayscale %u bits-per-pixel", *bppP);
+}
+
+
+
+static void
+validateImageAgainstStandardColormap(const Colormap * const colormapP,
+                                     xel **           const xels,
+                                     unsigned int     const cols,
+                                     unsigned int     const rows,
+                                     xelval           const maxval) {
+/*----------------------------------------------------------------------------
+   Abort program if the image xels[][] (which is 'cols' x 'rows') contains a
+   color not in the colormap *colormapP, giving an error message assuming the
+   user chose the standard Palm colormap.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        for (col = 0; col < cols; ++col) {
+            ColormapEntry const searchTarget = 
+                palmcolor_mapEntryColorFmPixel(xels[row][col], maxval, 255);
+
+            ColormapEntry * const foundEntryP = 
+                (bsearch(&searchTarget,
+                         colormapP->color_entries, colormapP->ncolors,
+                         sizeof(ColormapEntry), palmcolor_compare_colors));
+            if (!foundEntryP)
+                pm_error(
+                    "A color in the input image is not in the standard Palm "
+                    "8-bit color palette.  Either adjust the colors in the "
+                    "input with 'pnmremap' and the 'palmcolor8.map' file "
+                    "(see manual) or specify -colormap or -depth=16");
+        }
+    }
+}
+
+
+
+static void
+determinePalmFormatPpm(unsigned int         const cols, 
+                       unsigned int         const rows, 
+                       xelval               const maxval, 
+                       xel **               const xels,
+                       bool                 const bppSpecified,
+                       unsigned int         const bpp,
+                       bool                 const maxBppSpecified,
+                       unsigned int         const maxBpp, 
+                       bool                 const wantCustomColormap,
+                       enum CompressionType const compression,
+                       bool                 const verbose,
+                       unsigned int *       const bppP, 
+                       bool *               const directColorP, 
+                       Colormap **          const colormapPP) {
+            
+    /* We don't attempt to identify PPM files that are actually
+       monochrome.  So there are two options here: either 8-bit with a
+       colormap, either the standard one or a custom one, or 16-bit direct
+       color.  In the colormap case, if 'wantCustomColormap' is true (not
+       recommended by Palm) we will put in our own colormap that has the
+       colors of the input image; otherwise we will select the default
+       Palm colormap and will fail if the input image has any colors that
+       are not in that map (user should use Pnmremap and the
+       palmcolor8.map file that comes with Netpbm to avoid this).  We try
+       for colormapped first, since it works on more PalmOS devices.
+    */
+    if ((bppSpecified && bpp == 16) || 
+        (!bppSpecified && maxBppSpecified && maxBpp == 16)) {
+        /* we do the 16-bit direct color */
+        *directColorP = TRUE;
+        *colormapPP = NULL;
+        *bppP = 16;
+    } else if (!wantCustomColormap) {
+        /* colormapped with the standard colormap */
+        Colormap * colormapP;
+            
+        if ((bppSpecified && bpp != 8) || (maxBppSpecified && maxBpp < 8))
+            pm_error("Must use depth of 8 for color Palm Bitmap without "
+                     "custom color table.");
+        colormapP = palmcolor_build_default_8bit_colormap();
+        validateImageAgainstStandardColormap(colormapP,
+                                             xels, cols, rows, maxval);
+
+        *colormapPP = colormapP;
+        *bppP = 8;
+        *directColorP = FALSE;
+        if (verbose)
+            pm_message("Output is color with default colormap at 8 bpp");
+    } else {
+        /* colormapped with a custom colormap */
+        *colormapPP = 
+            palmcolor_build_custom_8bit_colormap(xels, rows, cols, maxval);
+        for (*bppP = 1; (1 << *bppP) < (*colormapPP)->ncolors; *bppP *= 2);
+        if (bppSpecified) {
+            if (bpp >= *bppP)
+                *bppP = bpp;
+            else
+                pm_error("Too many colors for specified depth.  "
+                         "Specified depth is %u bits; would need %u to "
+                         "represent the %u colors in the image.  "
+                         "Use pnmquant to reduce.",
+                         maxBpp, *bppP, (*colormapPP)->ncolors);
+        } else if (maxBppSpecified && maxBpp < *bppP) {
+            pm_error("Too many colors for specified max depth.  "
+                     "Specified maximum is %u bits; would need %u to "
+                     "represent the %u colors in the image.  "
+                     "Use pnmquant to reduce.",
+                     maxBpp, *bppP, (*colormapPP)->ncolors);
+        } else if (compression != COMP_NONE && *bppP > 8) {
+            pm_error("Too many colors for a compressed image.  "
+                     "Maximum is 256; the image has %u",
+                     (*colormapPP)->ncolors);
+        }
+        *directColorP = FALSE;
+        if (verbose)
+            pm_message("Output is color with custom colormap "
+                       "with %u colors at %u bpp", 
+                       (*colormapPP)->ncolors, *bppP);
+    }
+}
+
+
+
 static void
-determinePalmFormat(unsigned int   const cols, 
-                    unsigned int   const rows, 
-                    xelval         const maxval, 
-                    int            const format, 
-                    xel **         const xels,
-                    unsigned int   const specified_bpp,
-                    unsigned int   const max_bpp, 
-                    bool           const custom_colormap,
-                    bool           const verbose,
-                    unsigned int * const bppP, 
-                    bool *         const directColorP, 
-                    Colormap *     const colormapP) {
+determinePalmFormat(unsigned int         const cols, 
+                    unsigned int         const rows, 
+                    xelval               const maxval, 
+                    int                  const format, 
+                    xel **               const xels,
+                    bool                 const bppSpecified,
+                    unsigned int         const bpp,
+                    bool                 const maxBppSpecified,
+                    unsigned int         const maxBpp, 
+                    bool                 const wantCustomColormap,
+                    enum CompressionType const compression,
+                    bool                 const verbose,
+                    unsigned int *       const bppP, 
+                    bool *               const directColorP, 
+                    Colormap **          const colormapPP) {
+/*----------------------------------------------------------------------------
+   Determine what kind of Palm output file to make.
 
+   Also compute the colormap, if there is to be one.  This could be either one
+   we make up, that needs to go into the image, or a standard one.
+-----------------------------------------------------------------------------*/
+    if (compression != COMP_NONE) {
+        if (bppSpecified && bpp > 8)
+            pm_error("You requested %u bits per pixel and compression.  "
+                     "This program does not know how to generate a "
+                     "compressed image with more than 8 bits per pixel",
+                     bpp);
+        if (maxBppSpecified && maxBpp > 8)
+            pm_error("You requested %u max bits per pixel and compression.  "
+                     "This program does not know how to generate a "
+                     "compressed image with more than 8 bits per pixel",
+                     maxBpp);
+    }
     if (PNM_FORMAT_TYPE(format) == PBM_TYPE) {
-        if (custom_colormap)
+        if (wantCustomColormap)
             pm_error("You specified -colormap with a black and white input "
                      "image.  -colormap is valid only with color.");
-        if (specified_bpp)
-            *bppP = specified_bpp;
+        if (bppSpecified)
+            *bppP = bpp;
         else
             *bppP = 1;    /* no point in wasting bits */
         *directColorP = FALSE;
-        *colormapP = NULL;
+        *colormapPP = NULL;
         if (verbose)
             pm_message("output is black and white");
     } else if (PNM_FORMAT_TYPE(format) == PGM_TYPE) {
-        /* we can usually handle this one, but may not have enough
-           pixels.  So check... */
-        if (custom_colormap)
-            pm_error("You specified -colormap with a black and white input"
-                     "image.  -colormap is valid only with color.");
-        if (specified_bpp)
-            *bppP = specified_bpp;
-        else if (max_bpp && (maxval >= (1 << max_bpp)))
-            *bppP = max_bpp;
-        else if (maxval > 16)
-            *bppP = 4;
-        else {
-            /* scale to minimum number of bpp needed */
-            for (*bppP = 1;  (1 << *bppP) < maxval;  *bppP *= 2)
-                ;
-        }
-        if (*bppP > 4)
-            *bppP = 4;
-        if (verbose)
-            pm_message("output is grayscale %d bits-per-pixel", *bppP);
+        determinePalmFormatPgm(maxval,
+                               bppSpecified, bpp, maxBppSpecified, maxBpp,
+                               wantCustomColormap, compression,
+                               verbose,
+                               bppP);
+
         *directColorP = FALSE;
-        *colormapP = NULL;
+        *colormapPP = NULL;
     } else if (PNM_FORMAT_TYPE(format) == PPM_TYPE) {
-
-        /* We assume that we only get a PPM if the image cannot be
-           represented as PBM or PGM.  There are two options here: either
-           8-bit with a colormap, either the standard one or a custom one,
-           or 16-bit direct color.  In the 8-bit case, if "custom_colormap"
-           is specified (not recommended by Palm) we will put in our own
-           colormap; otherwise we will assume that the colors have been
-           mapped to the default Palm colormap by appropriate use of
-           pnmquant.  We try for 8-bit color first, since it works on
-           more PalmOS devices. 
-        */
-        if ((specified_bpp == 16) || 
-            (specified_bpp == 0 && max_bpp == 16)) {
-            /* we do the 16-bit direct color */
-            *directColorP = TRUE;
-            *colormapP = NULL;
-            *bppP = 16;
-        } else if (!custom_colormap) {
-            /* standard indexed 8-bit color */
-            *colormapP = palmcolor_build_default_8bit_colormap();
-            *bppP = 8;
-            if (((specified_bpp != 0) && (specified_bpp != 8)) ||
-                ((max_bpp != 0) && (max_bpp < 8)))
-                pm_error("Must use depth of 8 for color Palm Bitmap without "
-                         "custom color table.");
-            *directColorP = FALSE;
-            if (verbose)
-                pm_message("Output is color with default colormap at 8 bpp");
-        } else {
-            /* indexed 8-bit color with a custom colormap */
-            *colormapP = 
-                palmcolor_build_custom_8bit_colormap(rows, cols, xels);
-            for (*bppP = 1; (1 << *bppP) < (*colormapP)->ncolors; *bppP *= 2);
-            if (specified_bpp != 0) {
-                if (specified_bpp >= *bppP)
-                    *bppP = specified_bpp;
-                else
-                    pm_error("Too many colors for specified depth.  "
-                             "Use pnmquant to reduce.");
-            } else if ((max_bpp != 0) && (max_bpp < *bppP)) {
-                pm_error("Too many colors for specified max depth.  "
-                         "Use pnmquant to reduce.");
-            }
-            *directColorP = FALSE;
-            if (verbose)
-                pm_message("Output is color with custom colormap "
-                           "with %d colors at %d bpp", 
-                           (*colormapP)->ncolors, *bppP);
-        }
+        determinePalmFormatPpm(cols, rows, maxval, xels, bppSpecified, bpp,
+                               maxBppSpecified, maxBpp,
+                               wantCustomColormap, compression, verbose,
+                               bppP, directColorP, colormapPP);
     } else {
         pm_error("unknown format 0x%x on input file", (unsigned) format);
     }
+
+    if (compression != COMP_NONE)
+        assert(*bppP <= 8);
 }
 
 
@@ -300,25 +435,23 @@ findTransparentColor(const char *   const colorSpec,
                      pixval         const newMaxval,
                      bool           const directColor, 
                      pixval         const maxval, 
-                     Colormap       const colormap,
+                     Colormap *     const colormapP,
                      xel *          const transcolorP, 
                      unsigned int * const transindexP) {
 
     *transcolorP = ppm_parsecolor(colorSpec, maxval);
     if (!directColor) {
-        Color_s const temp_color = 
-            ((((PPM_GETR(*transcolorP)*newMaxval) / maxval) << 16) 
-             | (((PPM_GETG(*transcolorP)*newMaxval) / maxval) << 8)
-             | ((PPM_GETB(*transcolorP)*newMaxval) / maxval));
-        Color const found = 
-            (bsearch(&temp_color,
-                     colormap->color_entries, colormap->ncolors,
-                     sizeof(Color_s), palmcolor_compare_colors));
-        if (!found) {
+        ColormapEntry const searchTarget = 
+            palmcolor_mapEntryColorFmPixel(*transcolorP, maxval, newMaxval);
+        ColormapEntry * const foundEntryP = 
+            (bsearch(&searchTarget,
+                     colormapP->color_entries, colormapP->ncolors,
+                     sizeof(ColormapEntry), palmcolor_compare_colors));
+        if (!foundEntryP) {
             pm_error("Specified transparent color %s not found "
                      "in colormap.", colorSpec);
         } else
-            *transindexP = (*found >> 24) & 0xFF;
+            *transindexP = (*foundEntryP >> 24) & 0xFF;
     }
 }
 
@@ -326,9 +459,9 @@ findTransparentColor(const char *   const colorSpec,
 
 static unsigned int
 bitmapVersion(unsigned int         const bpp,
-              bool                 const colormap,
+              bool                 const colormapped,
               bool                 const transparent,
-              enum compressionType const compression,
+              enum CompressionType const compression,
               unsigned int         const density) {
 /*----------------------------------------------------------------------------
    Return the version number of the oldest version that can represent
@@ -343,7 +476,7 @@ bitmapVersion(unsigned int         const bpp,
         version = 3;
     else if (transparent || compression != COMP_NONE)
         version = 2;
-    else if (bpp > 1 || colormap)
+    else if (bpp > 1 || colormapped)
         version = 1;
     else
         version = 0;
@@ -357,8 +490,8 @@ static void
 writeCommonHeader(unsigned int         const cols,
                   unsigned int         const rows,
                   unsigned int         const rowbytes,
-                  enum compressionType const compression,
-                  bool                 const colormap,
+                  enum CompressionType const compression,
+                  bool                 const colormapped,
                   bool                 const transparent,
                   bool                 const directColor,
                   unsigned int         const bpp,
@@ -382,7 +515,7 @@ writeCommonHeader(unsigned int         const cols,
     flags = 0;  /* initial value */
     if (compression != COMP_NONE)
         flags |= PALM_IS_COMPRESSED_FLAG;
-    if (colormap)
+    if (colormapped)
         flags |= PALM_HAS_COLORMAP_FLAG;
     if (transparent)
         flags |= PALM_HAS_TRANSPARENCY_FLAG;
@@ -398,7 +531,7 @@ writeCommonHeader(unsigned int         const cols,
 
 
 static unsigned char 
-compressionFieldValue(enum compressionType const compression) {
+compressionFieldValue(enum CompressionType const compression) {
 
     unsigned char retval;
 
@@ -424,7 +557,7 @@ compressionFieldValue(enum compressionType const compression) {
 static void
 writeRemainingHeaderLow(unsigned int         const nextDepthOffset,
                         unsigned int         const transindex,
-                        enum compressionType const compression,
+                        enum CompressionType const compression,
                         unsigned int         const bpp) {
 /*----------------------------------------------------------------------------
    Write last 6 bytes of a low density Palm Bitmap header. 
@@ -449,7 +582,7 @@ writeRemainingHeaderLow(unsigned int         const nextDepthOffset,
 
 static void
 writeRemainingHeaderHigh(unsigned int         const bpp,
-                         enum compressionType const compression,
+                         enum CompressionType const compression,
                          unsigned int         const density,
                          xelval               const maxval,
                          bool                 const transparent,
@@ -480,9 +613,9 @@ writeRemainingHeaderHigh(unsigned int         const bpp,
         if (bpp == 16) {
             /* Blind guess here */
             fputc(0, stdout);
-            fputc((PPM_GETR(transcolor) * 255) / maxval, stdout);
-            fputc((PPM_GETG(transcolor) * 255) / maxval, stdout);
-            fputc((PPM_GETB(transcolor) * 255) / maxval, stdout);
+            fputc(scaleSample(PPM_GETR(transcolor), maxval, 255), stdout);
+            fputc(scaleSample(PPM_GETG(transcolor), maxval, 255), stdout);
+            fputc(scaleSample(PPM_GETB(transcolor), maxval, 255), stdout);
         } else {
             assert(transindex <= UCHAR_MAX);
             fputc(0, stdout);
@@ -523,7 +656,7 @@ writeDummy() {
 
 static void
 writeColormap(bool         const explicitColormap,
-              Colormap     const colormap,
+              Colormap *   const colormapP,
               bool         const directColor,
               unsigned int const bpp,
               bool         const transparent,
@@ -534,14 +667,14 @@ writeColormap(bool         const explicitColormap,
     /* if there's a colormap, write it out */
     if (explicitColormap) {
         unsigned int row;
-        if (!colormap)
+        if (!colormapP)
             pm_error("Internal error: user specified -colormap, but we did "
                      "not generate a colormap.");
-        qsort (colormap->color_entries, colormap->ncolors,
-               sizeof(Color_s), palmcolor_compare_indices);
-        pm_writebigshort( stdout, colormap->ncolors );
-        for (row = 0;  row < colormap->ncolors; ++row)
-            pm_writebiglong (stdout, colormap->color_entries[row]);
+        qsort(colormapP->color_entries, colormapP->ncolors,
+              sizeof(ColormapEntry), palmcolor_compare_indices);
+        pm_writebigshort( stdout, colormapP->ncolors );
+        for (row = 0;  row < colormapP->ncolors; ++row)
+            pm_writebiglong (stdout, colormapP->color_entries[row]);
     }
 
     if (directColor && (version < 3)) {
@@ -552,13 +685,13 @@ writeColormap(bool         const explicitColormap,
             fputc(5, stdout);   /* # of bits of blue */
             fputc(0, stdout);   /* reserved by Palm */
         } else
-            pm_error("Don't know how to create %d bit DirectColor bitmaps.", 
+            pm_error("Don't know how to create %u bit DirectColor bitmaps.", 
                      bpp);
         if (transparent) {
             fputc(0, stdout);
-            fputc((PPM_GETR(transcolor) * 255) / maxval, stdout);
-            fputc((PPM_GETG(transcolor) * 255) / maxval, stdout);
-            fputc((PPM_GETB(transcolor) * 255) / maxval, stdout);
+            fputc(scaleSample(PPM_GETR(transcolor) , maxval, 255), stdout);
+            fputc(scaleSample(PPM_GETG(transcolor) , maxval, 255), stdout);
+            fputc(scaleSample(PPM_GETB(transcolor) , maxval, 255), stdout);
         } else
             pm_writebiglong(stdout, 0);     /* no transparent color */
     }
@@ -571,17 +704,29 @@ computeRawRowDirectColor(const xel *     const xelrow,
                          unsigned int    const cols,
                          xelval          const maxval,
                          unsigned char * const rowdata) {
+/*----------------------------------------------------------------------------
+  Compute a row of Palm data in raw (uncompressed) form for an image that
+  uses direct color (really, true color: each pixel contains RGB intensities
+  as distinct R, G, and B numbers).
 
+  In this format, each pixel is 16 bits: 5 red, 6 green, 5 blue.
+
+  'xelrow' is the image contents of row.  It is 'cols' columns wide and
+  samples are based on maxval 'maxval'.
+
+  Put the output data at 'rowdata'. 
+-----------------------------------------------------------------------------*/
     unsigned int col;
-    unsigned char *outptr;
+    unsigned char * outCursor;
     
-    for (col = 0, outptr = rowdata; col < cols; ++col) {
+    for (col = 0, outCursor = &rowdata[0]; col < cols; ++col) {
         unsigned int const color = 
-            ((((PPM_GETR(xelrow[col])*31)/maxval) << 11) |
-             (((PPM_GETG(xelrow[col])*63)/maxval) << 5) |
-             ((PPM_GETB(xelrow[col])*31)/maxval));
-        *outptr++ = (color >> 8) & 0xFF;
-        *outptr++ = color & 0xFF;
+            (scaleSample(PPM_GETR(xelrow[col]), maxval, 31) << 11) |
+            (scaleSample(PPM_GETG(xelrow[col]), maxval, 63) <<  5) |
+            (scaleSample(PPM_GETB(xelrow[col]), maxval, 31) <<  0);
+
+        *outCursor++ = (color >> 8) & 0xFF;
+        *outCursor++ = color & 0xFF;
     }
 }
 
@@ -592,23 +737,40 @@ computeRawRowNonDirect(const xel *     const xelrow,
                        unsigned int    const cols,
                        xelval          const maxval,
                        unsigned int    const bpp,
-                       Colormap        const colormap,
+                       Colormap *      const colormapP,
                        unsigned int    const newMaxval,
                        unsigned char * const rowdata) {
+/*----------------------------------------------------------------------------
+  Compute a row of Palm data in raw (uncompressed) form for an image that
+  does not have a raster whose elements are explicit R, G, and B
+  intensities.
 
+  If 'colormapP' is non-null, the pixel is an index into that colormap.
+  'newMaxval' is meaningless.
+
+  If 'colormapP' is null, the pixel is a grayscale intensity, on a scale with
+  maximum value 'newMaxval'.  (N.B. this is really direct color, but for some
+  reason it's historically lumped in with the paletted formats).
+
+  'xelrow' is the image contents of row.  It is 'cols' columns wide and
+  samples are based on maxval 'maxval'.
+
+  Put the output data at 'rowdata', using 'bpp' bits per pixel.
+-----------------------------------------------------------------------------*/
     unsigned int col;
-    unsigned char *outptr;
+    unsigned char * outCursor;
+        /* Points to next slot in 'rowdata' we will fill */
     unsigned char outbyte;
         /* Accumulated bits to be output */
     unsigned char outbit;
         /* The lowest bit number we want to access for this pixel */
 
-    outbyte = 0x00;
-    outptr = rowdata;
+    outbyte = 0x00;  /* initial value */
+    outCursor = &rowdata[0];  /* Start at the beginning of the row */
 
     for (outbit = 8 - bpp, col = 0; col < cols; ++col) {
         unsigned int color;
-        if (!colormap) {
+        if (!colormapP) {
             /* we assume grayscale, and use simple scaling */
             color = (PNM_GET1(xelrow[col]) * newMaxval)/maxval;
             if (color > newMaxval)
@@ -616,24 +778,23 @@ computeRawRowNonDirect(const xel *     const xelrow,
                          "color of %u.", color);
             color = newMaxval - color; /* note grayscale maps are inverted */
         } else {
-            Color_s const temp_color =
-                ((((PPM_GETR(xelrow[col])*newMaxval)/maxval)<<16) 
-                 | (((PPM_GETG(xelrow[col])*newMaxval)/maxval)<<8)
-                 | (((PPM_GETB(xelrow[col])*newMaxval)/maxval)));
-            Color const found = (bsearch (&temp_color,
-                                          colormap->color_entries, 
-                                          colormap->ncolors,
-                                          sizeof(Color_s), 
-                                          palmcolor_compare_colors));
-            if (!found) {
-                pm_error("Color %d:%d:%d not found in colormap.  "
-                         "Try using pnmquant to reduce the "
-                         "number of colors.",
+            ColormapEntry const searchTarget =
+                palmcolor_mapEntryColorFmPixel(xelrow[col], maxval, 255);
+            ColormapEntry * const foundEntryP =
+                bsearch(&searchTarget,
+                        colormapP->color_entries, 
+                        colormapP->ncolors,
+                        sizeof(ColormapEntry), 
+                        palmcolor_compare_colors);
+            if (!foundEntryP) {
+                pm_error("INERNAL ERROR: "
+                         "Color (%u,%u,%u) not found in colormap, "
+                         "though it was supposedly there before",
                          PPM_GETR(xelrow[col]), 
                          PPM_GETG(xelrow[col]), 
                          PPM_GETB(xelrow[col]));
             }
-            color = (*found >> 24) & 0xFF;
+            color = (*foundEntryP >> 24) & 0xFF;
         }
 
         if (color > newMaxval)
@@ -642,7 +803,7 @@ computeRawRowNonDirect(const xel *     const xelrow,
         outbyte |= (color << outbit);
         if (outbit == 0) {
             /* Bit buffer is full.  Flush to to rowdata. */
-            *outptr++ = outbyte;
+            *outCursor++ = outbyte;
             outbyte = 0x00;
             outbit = 8 - bpp;
         } else
@@ -650,7 +811,7 @@ computeRawRowNonDirect(const xel *     const xelrow,
     }
     if ((cols % (8 / bpp)) != 0) {
         /* Flush bits remaining in the bit buffer to rowdata */
-        *outptr++ = outbyte;
+        *outCursor++ = outbyte;
     }
 }
 
@@ -830,7 +991,7 @@ rleCompressAndBufferRow(const unsigned char * const rowdata,
         unsigned int repeatcount;
         for (repeatcount = 1;  
              repeatcount < (rowbytes - pos) && repeatcount  < 255;  
-             ++repeatcount)
+             ++repeatcount) 
             if (rowdata[pos + repeatcount] != rowdata[pos])
                 break;
 
@@ -870,7 +1031,7 @@ packbitsCompressAndBufferRow(const unsigned char * const rowdata,
 static void
 bufferRowFromRawRowdata(const unsigned char *  const rowdata,
                         unsigned int           const rowbytes,
-                        enum compressionType   const compression,
+                        enum CompressionType   const compression,
                         const unsigned char *  const lastrow,
                         struct seqBuffer *     const rasterBufferP) {
 /*----------------------------------------------------------------------------
@@ -909,16 +1070,17 @@ bufferRow(const xel *          const xelrow,
           unsigned int         const rowbytes,
           unsigned int         const bpp,
           unsigned int         const newMaxval,
-          enum compressionType const compression,
+          enum CompressionType const compression,
           bool                 const directColor,
-          Colormap             const colormap,
+          Colormap *           const colormapP,
           unsigned char *      const rowdata,
           unsigned char *      const lastrow,
           struct seqBuffer *   const rasterBufferP) {
 /*----------------------------------------------------------------------------
    Add a row of the Palm Bitmap raster to buffer 'rasterBufferP'.
    
-   'xelrow' is the image contents of row.  It is 'cols' columns wide.
+   'xelrow' is the image contents of row.  It is 'cols' columns wide and
+   samples are based on maxval 'maxval'.
 
    If 'compression' indicates scanline compression, 'lastrow' is the
    row immediately preceding this one in the image (and this function
@@ -930,7 +1092,7 @@ bufferRow(const xel *          const xelrow,
     if (directColor)
         computeRawRowDirectColor(xelrow, cols, maxval, rowdata);
     else 
-        computeRawRowNonDirect(xelrow, cols, maxval, bpp, colormap, newMaxval,
+        computeRawRowNonDirect(xelrow, cols, maxval, bpp, colormapP, newMaxval,
                                rowdata);
 
     bufferRowFromRawRowdata(rowdata, rowbytes, compression,
@@ -947,9 +1109,9 @@ bufferRaster(xel **               const xels,
              unsigned int         const rowbytes,
              unsigned int         const bpp,
              unsigned int         const newMaxval,
-             enum compressionType const compression,
+             enum CompressionType const compression,
              bool                 const directColor,
-             Colormap             const colormap,
+             Colormap *           const colormapP,
              struct seqBuffer **  const rasterBufferPP) {
     
     unsigned char * rowdata;
@@ -971,7 +1133,7 @@ bufferRaster(xel **               const xels,
     for (row = 0; row < rows; ++row) {
         bufferRow(xels[row], cols, maxval, rowbytes, bpp, newMaxval,
                   compression,
-                  directColor, colormap, rowdata, row > 0 ? lastrow : NULL,
+                  directColor, colormapP, rowdata, row > 0 ? lastrow : NULL,
                   *rasterBufferPP);
 
         if (compression == COMP_SCANLINE)
@@ -988,8 +1150,8 @@ static void
 computeOffsetStuff(bool                 const offsetWanted,
                    unsigned int         const version,
                    bool                 const directColor,
-                   enum compressionType const compression,
-                   bool                 const colormap,
+                   enum CompressionType const compression,
+                   bool                 const colormapped,
                    unsigned int         const colormapColorCount,
                    unsigned int         const sizePlusRasterSize,
                    unsigned int *       const nextDepthOffsetP,
@@ -1003,7 +1165,7 @@ computeOffsetStuff(bool                 const offsetWanted,
         */
         unsigned int const headerSize = ((version < 3) ? 16 : 24);
         unsigned int const colormapSize =
-            (colormap ? (2 + colormapColorCount * 4) : 0);
+            (colormapped ? (2 + colormapColorCount * 4) : 0);
         if (version < 3) {
             unsigned int const directSize = 
                 (directColor && version < 3) ? 8 : 0; 
@@ -1058,12 +1220,12 @@ writeBitmap(xel **               const xels,
             unsigned int         const rowbytes,
             unsigned int         const bpp,
             unsigned int         const newMaxval,
-            enum compressionType const compression,
+            enum CompressionType const compression,
             bool                 const transparent,
             bool                 const directColor,
             bool                 const offsetWanted,
-            bool                 const hasColormap,
-            Colormap             const colormap,
+            bool                 const colormapped,
+            Colormap *           const colormapP,
             unsigned int         const transindex,
             xel                  const transcolor,
             unsigned int         const version,
@@ -1086,11 +1248,11 @@ writeBitmap(xel **               const xels,
         */
     struct seqBuffer * rasterBufferP;
 
-    writeCommonHeader(cols, rows, rowbytes, compression, hasColormap, 
+    writeCommonHeader(cols, rows, rowbytes, compression, colormapped, 
                       transparent, directColor, bpp, version);
     
     bufferRaster(xels, cols, rows, maxval, rowbytes, bpp, newMaxval,
-                 compression, directColor, colormap, &rasterBufferP);
+                 compression, directColor, colormapP, &rasterBufferP);
 
     /* rasterSize itself takes 2 or 4 bytes */
     if (version < 3)
@@ -1099,7 +1261,7 @@ writeBitmap(xel **               const xels,
         sizePlusRasterSize = 4 + bufferLength(rasterBufferP);
     
     computeOffsetStuff(offsetWanted, version, directColor, compression,
-                       hasColormap, hasColormap ? colormap->ncolors : 0, 
+                       colormapped, colormapped ? colormapP->ncolors : 0, 
                        sizePlusRasterSize,
                        &nextDepthOffset, &nextBitmapOffset,
                        &padBytesRequired);
@@ -1111,7 +1273,7 @@ writeBitmap(xel **               const xels,
                                  maxval, transparent, transcolor,
                                  transindex, nextBitmapOffset);
 
-    writeColormap(hasColormap, colormap, directColor, bpp, 
+    writeColormap(colormapped, colormapP, directColor, bpp, 
                   transparent, transcolor, maxval, version);
 
     if (compression != COMP_NONE)
@@ -1134,8 +1296,8 @@ writeBitmap(xel **               const xels,
 
 
 int 
-main( int argc, char **argv ) {
-    struct cmdline_info cmdline;
+main( int argc, const char **argv ) {
+    struct CmdlineInfo cmdline;
     unsigned int version;
     FILE* ifP;
     xel** xels;
@@ -1148,10 +1310,9 @@ main( int argc, char **argv ) {
     unsigned int bpp;
     bool directColor;
     unsigned int newMaxval;
-    Colormap colormap;
+    Colormap * colormapP;
     
-    /* Parse default params */
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -1161,18 +1322,20 @@ main( int argc, char **argv ) {
     pm_close(ifP);
 
     if (cmdline.verbose)
-        pm_message("Input is %dx%d %s, maxval %d", 
+        pm_message("Input is %ux%u %s, maxval %u", 
                    cols, rows, formatName(format), maxval);
     
-    determinePalmFormat(cols, rows, maxval, format, xels, cmdline.depth,
-                        cmdline.maxdepth, cmdline.colormap, cmdline.verbose,
-                        &bpp, &directColor, &colormap);
+    determinePalmFormat(cols, rows, maxval, format, xels,
+                        cmdline.depthSpec, cmdline.depth,
+                        cmdline.maxdepthSpec, cmdline.maxdepth,
+                        cmdline.colormap, cmdline.compression, cmdline.verbose,
+                        &bpp, &directColor, &colormapP);
 
     newMaxval = (1 << bpp) - 1;
 
     if (cmdline.transparent) 
         findTransparentColor(cmdline.transparent, newMaxval, directColor,
-                             maxval, colormap, &transcolor, &transindex);
+                             maxval, colormapP, &transcolor, &transindex);
     else 
         transindex = 0;
 
@@ -1185,7 +1348,7 @@ main( int argc, char **argv ) {
     writeBitmap(xels, cols, rows, maxval,
                 rowbytes, bpp, newMaxval, cmdline.compression,
                 !!cmdline.transparent, directColor, cmdline.offset, 
-                cmdline.colormap, colormap, transindex, transcolor,
+                cmdline.colormap, colormapP, transindex, transcolor,
                 version, cmdline.density, cmdline.withdummy);
     
     return 0;
diff --git a/doc/HISTORY b/doc/HISTORY
index 703d8479..d54b89be 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -4,49 +4,62 @@ Netpbm.
 CHANGE HISTORY 
 --------------
 
-17.09.19 BJH  Release 10.79.07
+17.09.30 BJH  Release 10.80.00
+
+              pnmtopalm: Refuse to create a compressed image with more than 8
+              bits per pixel.
+
+              pbmtext, libnetpbm font facilities: allow glyphs in font files
+              that have no bitmap data; just used for their advance value to
+              code a space.
+
+              pbmtext: Improve error message when there is a problem reading
+              the font file.
+
+              libnetpbm font facilities: consider font invalid if a glyph is
+              more than 65536 pixels high or wide.
+
+              ppmshadow: handle images with a black background and low
+              contrast images (i.e. little difference between foreground and
+              background).
 
               ppmbrighten: fix bug: red pixels change hue.  Introduced in
               after Netpbm 10.11 (October 2002) and before Netpbm 10.18
               (September 2003).
 
-17.09.13 BJH  Release 10.79.06
-
               palmtopnm: fix crash if invalid input contains color index that
               is not in the palette.  Always broken (palmtopnm was new in
               Netpbm 9.10 (October 2001)).
 
-17.09.10 BJH  Release 10.79.05
-
               pnmtopalm: fix incorrect output with certain input files and
               -packbits_compression.  Always broken.  -packbits_compression
               was new in Netpbm 10.27 (March 2005).
 
-              pamtopdbimg: Fix incorrect output. Always broken (pamtopdbimg
-              was new in Netpbm 10.52.00 (October 2010)).
+              pnmtopalm: Correct error message recommending running
+              pnmquant when the real solution is to run pnmremap to modify the
+              image to Palm standard colors.
 
-17.08.11 BJH  Release 10.79.04
+              pnmtopalm: Fix bug causing "color not in colormap" failure with
+              -colormap and maxval other than 255.
 
               libnetpbm: font facilities: fix invalid memory reference with
               certain font files.
 
-17.08.02 BJH  Release 10.79.03
-
-              pbmtoibm23xx: change license terms to GPL 2 or later.
+              libnetpbm: ppm_readcolordict: Improve error message when there is
+              a problem reading the color dictionary.  Affects ppmhist.
 
-              Makeman: make it work with Python 3.6 II.
+              pgmmake: Fix bug: treats non-numeric gray-level argument as zero.
+              Always broken (Pgmmake was new in Netpbm 10.32, February 2006).
 
-17.07.08 BJH  Release 10.79.02
+              pdbimgtopam, pamtopdbimg: fix various cases of incorrect output,
+              some always present (programs were new in Netpbm 10.52.00
+              (October 2010)).
 
               libnetpbm: pnm_parsecolorn(), pnm_parsecolor(): fix parsing of
               rgb: color specifications: yields value slightly too dim.
-              Affects many programs.  Broken in Netpbm 10.79.00 about a week
-              ago.
+              Affects many programs.  Broken in Netpbm 10.79 (June 2017).
 
-17.07.03 BJH  Release 10.79.01
-
-              Fix 'format-security' GNU compiler warning.  Introduced in
-              Netpbm 10.79.00 a few days ago.
+              Makeman: make it work with Python 3.6 II.
 
 17.06.30 BJH  Release 10.79.00
 
diff --git a/editor/ppmshadow b/editor/ppmshadow
index a46341d7..ae6b1b0f 100755
--- a/editor/ppmshadow
+++ b/editor/ppmshadow
@@ -73,7 +73,7 @@ sub doVersionHack($) {
 sub imageDimensions($) {
     my ($fileName) = @_;
 #-----------------------------------------------------------------------------
-#  Return the dimensions of the Netpbm image in the named file
+#  Return the dimensions of the Netpbm image in the file named $fileName.
 #-----------------------------------------------------------------------------
     my ($width, $height, $depth);
     my $pamfileOutput = `pamfile $fileName`;
@@ -86,6 +86,29 @@ sub imageDimensions($) {
     return ($width, $height, $depth);
 }    
 
+sub backgroundColor($) {
+    my ($fileName) = @_;
+#-----------------------------------------------------------------------------
+#  Return the color of the backround of the image in the file named $fileName.
+#-----------------------------------------------------------------------------
+    # We call the color of the top left pixel the background color.
+
+    my $ppmhistOut = qx{pamcut 0 0 1 1 $fileName | ppmhist -noheader -float};
+
+    my ($ired, $igrn, $iblu, $lum, $count);
+
+    if ($ppmhistOut =~
+        m{\s*([01].\d+)\s*([01].\d+)\s*([01].\d+)\s*([01].\d+)\s*(\d+)}) {
+        ($ired, $igrn, $iblu, $lum, $count) = ($1, $2, $3, $4, $5);
+    } else {
+        die("Unrecognized format of output from 'ppmhist' shell command");
+    }
+    my $irgb = sprintf("rgbi:%f/%f/%f", $ired, $igrn, $iblu);
+
+    return $irgb;
+}    
+
+
 
 sub makeConvolutionKernel($$) {
     my ($convkernelfile, $ckern) = @_;
@@ -211,19 +234,22 @@ system("ppmtoppm");
 my ($sourceImageWidth, $sourceImageHeight, $sourceImageDepth) =
     imageDimensions($infile);
 
-#   Create an all-background-color image (same size as original image)
+my $bgColorIrgb = backgroundColor($infile);
+
+# Create an all-background-color image (same size as original image),
+# named $backgroundfile. 
 
 my $backgroundfile = "$ourtmp/background.ppm";
-system("pamcut -left=0 -top=0 -width=1 -height=1 $infile | " .
-       "pamscale -xsize=$sourceImageWidth " .
-       "-ysize=$sourceImageHeight >$backgroundfile");
+system("ppmmake $bgColorIrgb $sourceImageWidth $sourceImageHeight " .
+    "-maxval $sourceImageDepth " .
+    ">$backgroundfile");
 
-#   Create mask file for background, named $bgmaskfile.  It is a PBM, white
-#   wherever there is background image in the input.
+# Create mask file for background, named $bgmaskfile.  It is a PBM, white
+# wherever there is background image in the input.
 
 my $bgmaskfile = "$ourtmp/bgmask.pbm";
-system("pamarith -difference $infile $backgroundfile | pnminvert | ppmtopgm " .
-       "| pgmtopbm -thresh -value 1.0 >$bgmaskfile");
+system("ppmchange -remainder=black $bgColorIrgb white $infile | " .
+       "ppmtopgm | pgmtopbm -threshold -value=0.5 >$bgmaskfile"); 
 
 my $ckern = $convolve <= 11 ? $convolve : 11;
 
diff --git a/generator/pamgradient.c b/generator/pamgradient.c
index 57e78288..95e4d8c4 100644
--- a/generator/pamgradient.c
+++ b/generator/pamgradient.c
@@ -77,6 +77,7 @@ parseCommandLine(int argc, const char **argv,
             pm_error("height argument must be a positive number.  You "
                      "specified '%s'", argv[6]);
     }
+    free(option_def);
 }
 
 
diff --git a/generator/pbmtext.c b/generator/pbmtext.c
index 357f0429..e25c6bbe 100644
--- a/generator/pbmtext.c
+++ b/generator/pbmtext.c
@@ -18,11 +18,13 @@
 #include <math.h>
 #include <limits.h>
 #include <assert.h>
+#include <setjmp.h>
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
+#include "pm.h"
 #include "pbm.h"
 #include "pbmfont.h"
 
@@ -157,6 +159,37 @@ reportFont(struct font * const fontP) {
 
 
 
+static struct font *
+fontFromFile(const char * const fileName) {
+
+    struct font * retval;
+
+    jmp_buf jmpbuf;
+    int rc;
+
+    rc = setjmp(jmpbuf);
+
+    if (rc == 0) {
+        /* This is the normal program flow */
+        pm_setjmpbuf(&jmpbuf);
+
+        retval = pbm_loadfont(fileName);
+
+        pm_setjmpbuf(NULL);
+    } else {
+        /* This is the second pass, after pbm_loadfont does a longjmp
+           because it fails.
+        */
+        pm_setjmpbuf(NULL);
+
+        pm_error("Failed to load font from file '%s'", fileName);
+    }
+
+    return retval;
+}
+
+
+
 static void
 computeFont(struct CmdlineInfo const cmdline,
             struct font **     const fontPP) {
@@ -164,7 +197,7 @@ computeFont(struct CmdlineInfo const cmdline,
     struct font * fontP;
 
     if (cmdline.font)
-        fontP = pbm_loadfont(cmdline.font);
+        fontP = fontFromFile(cmdline.font);
     else {
         if (cmdline.builtin)
             fontP = pbm_defaultfont(cmdline.builtin);
@@ -538,22 +571,26 @@ insertCharacter(const struct glyph * const glyphP,
    Insert one character (whose glyph is 'glyph') into the image bits[].
    Its top left corner shall be row 'toprow', column 'leftcol'.
 -----------------------------------------------------------------------------*/
-    unsigned int glyph_y;  /* Y position within the glyph */
-
-    if (leftcol + glyphP->x < 0 ||
-        leftcol + glyphP->x + glyphP->width > cols ||
-        toprow < 0 ||
-        toprow + glyphP->height >rows )
-        pm_error("internal error.  Rendering out of bounds");
-
-    for (glyph_y = 0; glyph_y < glyphP->height; ++glyph_y) {
-        unsigned int glyph_x;  /* position within the glyph */
-
-        for (glyph_x = 0; glyph_x < glyphP->width; ++glyph_x) {
-            if (glyphP->bmap[glyph_y * glyphP->width + glyph_x]) {
-                unsigned int const col = leftcol + glyphP->x + glyph_x;
-                bits[toprow+glyph_y][col/8] |= PBM_BLACK << (7-col%8);
-        }
+    if (glyphP->width == 0 && glyphP->height == 0) {
+        /* No bitmap data.  Some BDF files code space this way */
+    } else {
+        unsigned int glyph_y;  /* Y position within the glyph */
+
+        if (leftcol + glyphP->x < 0 ||
+            leftcol + glyphP->x + glyphP->width > cols ||
+            toprow < 0 ||
+            toprow + glyphP->height >rows )
+            pm_error("internal error.  Rendering out of bounds");
+
+        for (glyph_y = 0; glyph_y < glyphP->height; ++glyph_y) {
+            unsigned int glyph_x;  /* position within the glyph */
+
+            for (glyph_x = 0; glyph_x < glyphP->width; ++glyph_x) {
+                if (glyphP->bmap[glyph_y * glyphP->width + glyph_x]) {
+                    unsigned int const col = leftcol + glyphP->x + glyph_x;
+                    bits[toprow+glyph_y][col/8] |= PBM_BLACK << (7-col%8);
+                }
+            }
         }
     }
 }    
diff --git a/generator/pgmmake.c b/generator/pgmmake.c
index f8f8b09c..3843e316 100644
--- a/generator/pgmmake.c
+++ b/generator/pgmmake.c
@@ -1,13 +1,18 @@
+#include <stdlib.h>
+#include <string.h>
+
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "pgm.h"
 
-struct cmdlineInfo {
+
+
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    gray grayLevel;
+    double grayLevel;
     unsigned int cols;
     unsigned int rows;
     gray maxval;
@@ -15,9 +20,37 @@ struct cmdlineInfo {
 
 
 
+static double
+grayLevelFromArg(const char * const arg) {
+
+    double retval;
+
+    if (strlen(arg) < 1)
+        pm_error("Gray level argument is a null string");
+    else {
+        char * endPtr;
+
+        retval = strtod(arg, &endPtr);
+
+        if (*endPtr != '\0')
+            pm_error("Gray level argument '%s' is not a floating point number",
+                     arg);
+
+        if (retval < 0.0)
+            pm_error("You can't have a negative gray level (%f)", retval);
+        if (retval > 1.0)
+            pm_error("Gray level must be in the range [0.0, 1.0].  "
+                     "You specified %f", retval);
+
+    }
+    return retval;
+}
+
+
+
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
   Convert program invocation arguments (argc,argv) into a format the 
   program can use easily, struct cmdlineInfo.  Validate arguments along
@@ -34,7 +67,7 @@ parseCommandLine(int argc, char ** argv,
     unsigned int maxvalSpec;
     unsigned int option_def_index;
 
-    MALLOCARRAY(option_def, 100);
+    MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0,   "maxval",    OPT_UINT, &cmdlineP->maxval, &maxvalSpec,    0);
@@ -43,11 +76,9 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = false;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = false;  /* We have no parms that are negative numbers */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    free (option_def);
-
     if (!maxvalSpec)
         cmdlineP->maxval = PGM_MAXMAXVAL;
     else {
@@ -65,37 +96,36 @@ parseCommandLine(int argc, char ** argv,
         pm_error("Only 3 arguments allowed: gray level, width, height.  "
                  "You specified %d", argc-1);
     else {
-        double const grayLevel = atof(argv[1]);
-        if (grayLevel < 0.0)
-            pm_error("You can't have a negative gray level (%f)", grayLevel);
-        if (grayLevel > 1.0)
-            pm_error("Gray level must be in the range [0.0, 1.0].  "
-                     "You specified %f", grayLevel);
-        cmdlineP->grayLevel = ROUNDU(grayLevel * cmdlineP->maxval);
+        cmdlineP->grayLevel = grayLevelFromArg(argv[1]);
         cmdlineP->cols = pm_parse_width(argv[2]);
         cmdlineP->rows = pm_parse_height(argv[3]);
     }
+    free(option_def);
 }
 
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char ** const argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     gray * grayrow;
     unsigned int col, row;
+    gray grayLevel;
 
-    pgm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
+    grayLevel = ROUNDU(cmdline.grayLevel * cmdline.maxval);
+
     pgm_writepgminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0);
+
     grayrow = pgm_allocrow(cmdline.cols);
 
     /* All rows are identical.  Fill once. */
     for (col = 0; col < cmdline.cols; ++col)
-        grayrow[col] = cmdline.grayLevel;
+        grayrow[col] = grayLevel;
 
     for (row = 0; row < cmdline.rows; ++row)
         pgm_writepgmrow(stdout, grayrow, cmdline.cols, cmdline.maxval, 0);
@@ -105,3 +135,5 @@ main(int argc, char *argv[]) {
 
     return 0;
 }
+
+
diff --git a/lib/libpamcolor.c b/lib/libpamcolor.c
index 336dcd6b..c0a57dee 100644
--- a/lib/libpamcolor.c
+++ b/lib/libpamcolor.c
@@ -31,30 +31,30 @@
 
 
 
-static void
-computeHexTable(int * const hexit) {
-
-    unsigned int i;
-
-    for (i = 0; i < 256; ++i)
-        hexit[i] = -1;
-
-    hexit['0'] = 0;
-    hexit['1'] = 1;
-    hexit['2'] = 2;
-    hexit['3'] = 3;
-    hexit['4'] = 4;
-    hexit['5'] = 5;
-    hexit['6'] = 6;
-    hexit['7'] = 7;
-    hexit['8'] = 8;
-    hexit['9'] = 9;
-    hexit['a'] = hexit['A'] = 10;
-    hexit['b'] = hexit['B'] = 11;
-    hexit['c'] = hexit['C'] = 12;
-    hexit['d'] = hexit['D'] = 13;
-    hexit['e'] = hexit['E'] = 14;
-    hexit['f'] = hexit['F'] = 15;
+static unsigned int
+hexDigitValue(char const digit) {
+
+    switch (digit) {
+    case '0': return 0;
+    case '1': return 1;
+    case '2': return 2;
+    case '3': return 3;
+    case '4': return 4;
+    case '5': return 5;
+    case '6': return 6;
+    case '7': return 7;
+    case '8': return 8;
+    case '9': return 9;
+    case 'a': case 'A': return 10;
+    case 'b': case 'B': return 11;
+    case 'c': case 'C': return 12;
+    case 'd': case 'D': return 13;
+    case 'e': case 'E': return 14;
+    case 'f': case 'F': return 15;
+    default:
+        pm_error("Invalid hex digit '%c'", digit);
+        return 0;  /* Defeat compiler warning */
+    }
 }
 
 
@@ -62,7 +62,6 @@ computeHexTable(int * const hexit) {
 static void
 parseHexDigits(const char *   const string,
                char           const delim,
-               const int *    const hexit,
                samplen *      const nP,
                unsigned int * const digitCtP) {
 
@@ -76,11 +75,7 @@ parseHexDigits(const char *   const string,
         if (digit == '\0')
             pm_error("rgb: color spec '%s' ends prematurely", string);
         else {
-            int const hexval = hexit[(unsigned int)digit];
-            if (hexval == -1)
-                pm_error("Invalid hex digit in rgb: color spec: 0x%02x",
-                         digit);
-            n = n * 16 + hexval;
+            n = n * 16 + hexDigitValue(digit);
             range *= 16;
             ++digitCt;
         }
@@ -107,26 +102,22 @@ parseNewHexX11(char   const colorname[],
    Assume colorname[] starts with "rgb:", but otherwise it might be
    gibberish.
 -----------------------------------------------------------------------------*/
-    int hexit[256];
-
     const char * cp;
     unsigned int digitCt;
 
-    computeHexTable(hexit);
-
     cp = &colorname[4];
 
-    parseHexDigits(cp, '/', hexit, &color[PAM_RED_PLANE], &digitCt);
+    parseHexDigits(cp, '/', &color[PAM_RED_PLANE], &digitCt);
 
     cp += digitCt;
     ++cp;  /* Skip the slash */
 
-    parseHexDigits(cp, '/', hexit, &color[PAM_GRN_PLANE], &digitCt);
+    parseHexDigits(cp, '/', &color[PAM_GRN_PLANE], &digitCt);
 
     cp += digitCt;
     ++cp;  /* Skip the slash */
 
-    parseHexDigits(cp, '\0', hexit, &color[PAM_BLU_PLANE], &digitCt);
+    parseHexDigits(cp, '\0', &color[PAM_BLU_PLANE], &digitCt);
 }
 
 
@@ -170,71 +161,67 @@ parseOldX11(const char * const colorname,
    Return as *colorP the color specified by the old X11 style color
    specififier colorname[] (e.g. #554055).
 -----------------------------------------------------------------------------*/
-    int hexit[256];
-    
-    computeHexTable(hexit);
-
     if (!pm_strishex(&colorname[1]))
         pm_error("Non-hexadecimal characters in #-type color specification");
 
     switch (strlen(colorname) - 1 /* (Number of hex digits) */) {
     case 3:
-        color[PAM_RED_PLANE] = (samplen)hexit[(unsigned int)colorname[1]]/15;
-        color[PAM_GRN_PLANE] = (samplen)hexit[(unsigned int)colorname[2]]/15;
-        color[PAM_BLU_PLANE] = (samplen)hexit[(unsigned int)colorname[3]]/15;
+        color[PAM_RED_PLANE] = (samplen)hexDigitValue(colorname[1])/15;
+        color[PAM_GRN_PLANE] = (samplen)hexDigitValue(colorname[2])/15;
+        color[PAM_BLU_PLANE] = (samplen)hexDigitValue(colorname[3])/15;
         break;
 
     case 6:
         color[PAM_RED_PLANE] =
-            ((samplen)(hexit[(unsigned int)colorname[1]] << 4) +
-             (samplen)(hexit[(unsigned int)colorname[2]] << 0))
+            ((samplen)(hexDigitValue(colorname[1]) << 4) +
+             (samplen)(hexDigitValue(colorname[2]) << 0))
              / 255;
         color[PAM_GRN_PLANE] =
-            ((samplen)(hexit[(unsigned int)colorname[3]] << 4) +
-             (samplen)(hexit[(unsigned int)colorname[4]] << 0))
+            ((samplen)(hexDigitValue(colorname[3]) << 4) +
+             (samplen)(hexDigitValue(colorname[4]) << 0))
              / 255;
         color[PAM_BLU_PLANE] =
-            ((samplen)(hexit[(unsigned int)colorname[5]] << 4) + 
-             (samplen)(hexit[(unsigned int)colorname[6]] << 0))
+            ((samplen)(hexDigitValue(colorname[5]) << 4) + 
+             (samplen)(hexDigitValue(colorname[6]) << 0))
              / 255;
         break;
 
     case 9:
         color[PAM_RED_PLANE] =
-            ((samplen)(hexit[(unsigned int)colorname[1]] << 8) +
-             (samplen)(hexit[(unsigned int)colorname[2]] << 4) +
-             (samplen)(hexit[(unsigned int)colorname[3]] << 0))
+            ((samplen)(hexDigitValue(colorname[1]) << 8) +
+             (samplen)(hexDigitValue(colorname[2]) << 4) +
+             (samplen)(hexDigitValue(colorname[3]) << 0))
             / 4095;
         color[PAM_GRN_PLANE] =
-            ((samplen)(hexit[(unsigned int)colorname[4]] << 8) + 
-             (samplen)(hexit[(unsigned int)colorname[5]] << 4) +
-             (samplen)(hexit[(unsigned int)colorname[6]] << 0))
+            ((samplen)(hexDigitValue(colorname[4]) << 8) + 
+             (samplen)(hexDigitValue(colorname[5]) << 4) +
+             (samplen)(hexDigitValue(colorname[6]) << 0))
             / 4095;
         color[PAM_BLU_PLANE] =
-            ((samplen)(hexit[(unsigned int)colorname[7]] << 8) + 
-             (samplen)(hexit[(unsigned int)colorname[8]] << 4) +
-             (samplen)(hexit[(unsigned int)colorname[9]] << 0))
+            ((samplen)(hexDigitValue(colorname[7]) << 8) + 
+             (samplen)(hexDigitValue(colorname[8]) << 4) +
+             (samplen)(hexDigitValue(colorname[9]) << 0))
             / 4095;
         break;
 
     case 12:
         color[PAM_RED_PLANE] =
-            ((samplen)(hexit[(unsigned int)colorname[1]] << 12) + 
-             (samplen)(hexit[(unsigned int)colorname[2]] <<  8) +
-             (samplen)(hexit[(unsigned int)colorname[3]] <<  4) + 
-             (samplen)(hexit[(unsigned int)colorname[4]] <<  0))
+            ((samplen)(hexDigitValue(colorname[1]) << 12) + 
+             (samplen)(hexDigitValue(colorname[2]) <<  8) +
+             (samplen)(hexDigitValue(colorname[3]) <<  4) + 
+             (samplen)(hexDigitValue(colorname[4]) <<  0))
             / 65535;
         color[PAM_GRN_PLANE] =
-            ((samplen)(hexit[(unsigned int)colorname[5]] << 12) + 
-             (samplen)(hexit[(unsigned int)colorname[6]] <<  8) +
-             (samplen)(hexit[(unsigned int)colorname[7]] <<  4) +
-             (samplen)(hexit[(unsigned int)colorname[8]] <<  0))
+            ((samplen)(hexDigitValue(colorname[5]) << 12) + 
+             (samplen)(hexDigitValue(colorname[6]) <<  8) +
+             (samplen)(hexDigitValue(colorname[7]) <<  4) +
+             (samplen)(hexDigitValue(colorname[8]) <<  0))
             / 65535;
         color[PAM_BLU_PLANE] =
-            ((samplen)(hexit[(unsigned int)colorname[9]] << 12) + 
-             (samplen)(hexit[(unsigned int)colorname[10]] << 8) +
-             (samplen)(hexit[(unsigned int)colorname[11]] << 4) +
-             (samplen)(hexit[(unsigned int)colorname[12]] << 0))
+            ((samplen)(hexDigitValue(colorname[ 9]) << 12) + 
+             (samplen)(hexDigitValue(colorname[10]) << 8) +
+             (samplen)(hexDigitValue(colorname[11]) << 4) +
+             (samplen)(hexDigitValue(colorname[12]) << 0))
             / 65535;
         break;
 
diff --git a/lib/libpbmfont.c b/lib/libpbmfont.c
index 77724127..8e854aa6 100644
--- a/lib/libpbmfont.c
+++ b/lib/libpbmfont.c
@@ -307,10 +307,13 @@ pbm_loadfont(const char * const filename) {
 
     FILE * fileP;
     struct font * fontP;
-    char line[256];
+    char line[10] = "\0\0\0\0\0\0\0\0\0\0";
+        /* Initialize to suppress Valgrind error which is reported when file
+           is empty or very small.
+        */
 
     fileP = pm_openr(filename);
-    fgets(line, 256, fileP);
+    fgets(line, 10, fileP);
     pm_close(fileP);
 
     if (line[0] == PBM_MAGIC1 && 
@@ -339,8 +342,23 @@ pbm_loadpbmfont(const char * const filename) {
     int fcols, frows;
 
     ifP = pm_openr(filename);
+
     font = pbm_readpbm(ifP, &fcols, &frows);
+
+    if ((fcols - 1) / 16 >= pbm_maxfontwidth() ||
+       (frows - 1) / 12 >= pbm_maxfontheight())
+        pm_error("Absurdly large PBM font file: %s", filename);
+    else if (fcols < 31 || frows < 23) {
+        /* Need at least one pixel per character, and this is too small to
+           have that.
+        */
+        pm_error("PBM font file '%s' too small to be a font file: %u x %u.  "
+                 "Minimum sensible size is 31 x 23",
+                 filename, fcols, frows);
+    }
+
     pm_close(ifP);
+
     return pbm_dissectfont((const bit **)font, frows, fcols);
 }
 
@@ -406,10 +424,23 @@ pbm_dumpfont(struct font * const fontP,
 }
 
 
+/*----------------------------------------------------------------------------
+  Routines for loading a BDF font file
+-----------------------------------------------------------------------------*/
 
-/* Routines for loading a BDF font file */
 
-#define  MAXBDFLINE 1024 
+/* The following are not recognized in individual glyph data; library
+   routines do a pm_error if they see one:
+
+   Vertical writing systems: DWIDTH1, SWIDTH1, VVECTOR, METRICSET,
+   CONTENTVERSION.
+
+   The following is not recognized and is thus ignored at the global level:
+   DWIDTH
+*/
+
+
+#define MAXBDFLINE 1024 
 
 /* Official Adobe document says max length of string is 65535 characters.
    However the value 1024 is sufficient for practical uses.
@@ -428,7 +459,7 @@ typedef struct {
 
            It also functions as a work area for readline_read().
         */
-    const char * arg[32];
+    const char * arg[6];
         /* These are the words; each entry is a pointer into line[] (above) */
 } Readline;
 
@@ -448,15 +479,17 @@ readline_init(Readline * const readlineP,
 static void
 tokenize(char *         const s,
          const char **  const words,
-         unsigned int   const maxWordCt) {
+         unsigned int   const wordsSz) {
 /*----------------------------------------------------------------------------
-   Chop up 's' into words by changing space characters to NUL.  Return
-   as 'words' pointer to the beginning of those words in 's'.
+   Chop up 's' into words by changing space characters to NUL.  Return as
+   'words' an array of pointers to the beginnings of those words in 's'.
+   Terminate the words[] list with a null pointer.
 
-   If there are more than 'maxWordCt' words in 's', ignore the excess on
-   the right.
+   'wordsSz' is the number of elements of space in 'words'.  If there are more
+   words in 's' than will fit in that space (including the terminating null
+   pointer), ignore the excess on the right.
 -----------------------------------------------------------------------------*/
-    unsigned int n;
+    unsigned int n;  /* Number of words in words[] so far */
     char * p;
 
     p = &s[0];
@@ -467,12 +500,13 @@ tokenize(char *         const s,
             *p++ = '\0';
         else {
             words[n++] = p;
-            if (n >= maxWordCt-1)
+            if (n >= wordsSz - 1)
                 break;
             while (*p && !ISSPACE(*p))
                 ++p;
         }
     }
+    assert(n <= wordsSz - 1);
     words[n] = NULL;
 }
 
@@ -634,6 +668,9 @@ createBmap(unsigned int  const glyphWidth,
                  "BDF font file.");
     
     if (streq(readlineP->arg[0], "ATTRIBUTES")) {
+        /* ATTRIBUTES is defined in Glyph Bitmap Distribution Format (BDF)
+           Specification Version 2.1, but not in Version 2.2. 
+        */
         bool eof;
         readline_read(readlineP, &eof);
         if (eof)
@@ -754,7 +791,7 @@ readEncoding(Readline *     const readlineP,
 static void
 validateFontLimits(const struct font * const fontP) {
 
-    assert( pbm_maxfontheight() > 0 && pbm_maxfontwidth() > 0 );
+    assert(pbm_maxfontheight() > 0 && pbm_maxfontwidth() > 0);
 
     if (fontP->maxwidth  <= 0 ||
         fontP->maxheight <= 0 ||
@@ -765,7 +802,7 @@ validateFontLimits(const struct font * const fontP) {
         fontP->x > fontP->maxwidth  ||
         fontP->y > fontP->maxheight ||
         fontP->x + fontP->maxwidth  > pbm_maxfontwidth() || 
-         fontP->y + fontP->maxheight > pbm_maxfontheight()
+        fontP->y + fontP->maxheight > pbm_maxfontheight()
         ) {
 
         pm_error("Global font metric(s) out of bounds.\n"); 
@@ -779,8 +816,13 @@ validateGlyphLimits(const struct font  * const fontP,
                     const struct glyph * const glyphP,
                     const char *         const charName) {
 
-    if (glyphP->width  == 0 ||
-        glyphP->height == 0 ||
+    /* Some BDF files code space with zero width and height,
+       no bitmap data and just the xadd value.
+       We allow zero width and height, iff both are zero.
+    */
+
+    if (((glyphP->width == 0 || glyphP->height == 0) &&
+         !(glyphP->width == 0 && glyphP->height == 0)) ||
         glyphP->width  > fontP->maxwidth  ||
         glyphP->height > fontP->maxheight ||
         glyphP->x < fontP->x ||
diff --git a/lib/libpm.c b/lib/libpm.c
index dbfcac12..f9aa1aef 100644
--- a/lib/libpm.c
+++ b/lib/libpm.c
@@ -68,7 +68,7 @@ static pm_usererrormsgfn * userErrorMsgFn = NULL;
     */
 
 static pm_usermessagefn * userMessageFn = NULL;
-    /* A function to call to issue an error message.
+    /* A function to call to issue a non-error message.
 
        NULL means use the library default: print to Standard Error
     */
diff --git a/lib/libppmcolor.c b/lib/libppmcolor.c
index 9a1ee5c1..3378e076 100644
--- a/lib/libppmcolor.c
+++ b/lib/libppmcolor.c
@@ -539,15 +539,14 @@ openColornameFile(const char *  const fileName,
 
     if (setjmp(jmpbuf) != 0) {
         pm_asprintf(errorP, "Failed to open color name file");
-        pm_setjmpbuf(origJmpbufP);
-        pm_longjmp();
     } else {
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
         *filePP = pm_openColornameFile(fileName, mustOpen);
 
         *errorP = NULL;  /* Would have longjmped if there were a problem */
-
-        pm_setjmpbuf(origJmpbufP);
     }
+    pm_setjmpbuf(origJmpbufP);
 }
 
 
@@ -733,7 +732,7 @@ ppm_readcolordict(const char *      const fileName,
     if (error) {
         pm_errormsg("%s", error);
         pm_strfree(error);
-        ppm_freecolorhash(cht);
+        pm_longjmp();
     } else {
         if (chtP)
             *chtP = cht;
diff --git a/lib/util/runlength.c b/lib/util/runlength.c
index e5c60db0..a4bb9057 100644
--- a/lib/util/runlength.c
+++ b/lib/util/runlength.c
@@ -12,8 +12,8 @@
   documentation.  This software is provided "as is" without express or implied
   warranty.
 
-  Functions pm_rlenc_byte() and pm_rlenc_word() are based on algorithm
-  originally by Robert A. Knop (rknop@mop.caltech.edu).
+  Functions pm_rlenc_compressbyte() and pm_rlenc_compressword() are based
+  on algorithm originally by Robert A. Knop (rknop@mop.caltech.edu).
 
   Those who wish to incorporate the code herein into their own programs are
   strongly discouraged from removing the comments within borders consisting
@@ -41,12 +41,12 @@
   code.  This redundancy bloated the Netpbm package and made maintenance
   difficult.  This maintenance difficulty surfaced as an issue when tests with
   valgrind revealed multiple memory-related problems in the above programs.
-  Indeed, just determining which programs do this compression by this method
+  Indeed, just determining which programs do compression by this method
   was not simple because of differences in terminology.  "Packbits" is often
   called "run length encoding."  In ppmtoilbm the name is "byterun1."
 
-  Today, all Netpbm programs that do Packbits compression use the facilities
-  in this file for it.
+  Today, all Netpbm programs that do Packbits compression with the exception
+  of pbmtoppa and pbmtogo use the facilities   in this file for it.  
 =============================================================================*/
 
 #include <string.h>
@@ -68,11 +68,11 @@ static const char * const errorUndefinedMode =
 
    In this simple run-length encoding scheme, compressed and uncompressed
    strings follow a single index or "flag" byte N.  There are several
-   variations, differing in the format of the flag byte, maximum string length
-   and element size (byte or word).
+   variations, differing in the format of the flag byte, maximum string
+   length and element size (byte or word).
    
-   In the most widely used version, Packbits, the meaning of the flag byte N
-   is defined as follows:
+   In the most widely used version, Packbits, the meaning of the flag byte
+   N is defined as follows:
 
     0-127 means the next N+1 bytes are uncompressed.
     129-255 means the next byte is to be repeated 257-N times.
@@ -99,7 +99,7 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf,
   Always encode 3-byte repeat sequences.
 
   Encode 2-byte repeat sequences only when they are at the start of the block.
-  This ensures that the output is never unnecessary bloated.
+  This ensures that the output is never unnecessarily bloated.
 
   Original algorithm by Robert A. Knop (rknop@mop.caltech.edu)
 -----------------------------------------------------------------------------*/
@@ -107,8 +107,17 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf,
 
     size_t inCurs, outCurs;
 
-    if (mode != PM_RLE_PACKBITS)
-        pm_error(errorUndefinedMode, mode);
+    int packBase;
+    int packSign;
+ 
+    switch (mode) {
+    case PM_RLE_PACKBITS:
+        packBase = 257 ; packSign = -1; break;
+    case PM_RLE_PALMPDB:
+        packBase = 127 ; packSign = +1; break;
+    default:
+         pm_error(errorUndefinedMode, mode);
+    }
 
     for (inCurs = 0, outCurs = 0; inCurs < inSize; ) {
         if ((inCurs < inSize - 1) && (inbuf[inCurs] == inbuf[inCurs+1])) {
@@ -121,8 +130,7 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf,
                      count < maxRun; 
                  ++inCurs, ++count)
                 ;
-
-            outbuf[outCurs++] = (unsigned char)(257 - count);
+            outbuf[outCurs++] = (unsigned char) (packBase + packSign * count);
             outbuf[outCurs++] = inbuf[hold];
         } else {
             /*Do a non-run*/
@@ -258,6 +266,8 @@ pm_rlenc_compressword(const uint16_t   * const inbuf,
    number of flag bytes.  The worst case happens when there are no repeat
    sequences in the input.
 
+   The encoder in this file is an efficient one.
+
    The key to an efficient encoder is correct handling of short, inefficient
    sequences.  The following algorithm (not actually used in this file)
    describes the idea.
@@ -288,6 +298,16 @@ pm_rlenc_compressword(const uint16_t   * const inbuf,
 
      - Whenever savings are larger than the added overhead, encode the run
        as a compressed block.
+
+   -----------------------------------------------------------------------
+
+   MS-DOS/Windows BMP format note
+
+   The compression method for BMP is a variant of packbits.
+   We could support the 8-bit version with some modifications to functions
+   in this file.  Determining the worst-case output size of an efficient
+   coder is rather complicated because uncompressed blocks may not be less
+   than three bytes long and are indicated by two-byte flags.
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
 
 
@@ -318,15 +338,22 @@ pm_rlenc_maxbytes(size_t          const inSize,  /* number of elements */
 
     switch (mode) {
     case PM_RLE_PACKBITS:
-        blockSize = 128; flagSize = 1; itemSize = 1; miscSize = 0; break;
+    case PM_RLE_PALMPDB:
+        blockSize = 128; flagSize = 1; itemSize = 1; miscSize = 0;
+        break;
     case PM_RLE_SGI8:
-        blockSize = 127; flagSize = 1; itemSize = 1; miscSize = 0; break;
-    case PM_RLE_GRAPHON:  case PM_RLE_PPA:
-        blockSize =  64; flagSize = 1; itemSize = 1; miscSize = 0; break;
+        blockSize = 127; flagSize = 1; itemSize = 1; miscSize = 0;
+        break;
+    case PM_RLE_GRAPHON:
+    case PM_RLE_PPA:
+        blockSize =  64; flagSize = 1; itemSize = 1; miscSize = 0;
+        break;
     case PM_RLE_SGI16:
-        blockSize = 127; flagSize = 2; itemSize = 2; miscSize = 2; break;
+        blockSize = 127; flagSize = 2; itemSize = 2; miscSize = 2;
+        break;
     case PM_RLE_PALM16:
-        blockSize = 128; flagSize = 1; itemSize = 2; miscSize = 0; break;
+        blockSize = 128; flagSize = 1; itemSize = 2; miscSize = 0;
+        break;
     default:
         pm_error(errorUndefinedMode, mode);
     }
diff --git a/lib/util/runlength.h b/lib/util/runlength.h
index 4857ae61..de921650 100644
--- a/lib/util/runlength.h
+++ b/lib/util/runlength.h
@@ -18,7 +18,8 @@ enum pm_RleMode { PM_RLE_PACKBITS,          /* most common mode */
                   PM_RLE_PPA,               /* reserved */
                   PM_RLE_SGI8,              /* reserved */
                   PM_RLE_SGI16,
-                  PM_RLE_PALM16
+                  PM_RLE_PALM16,
+                  PM_RLE_PALMPDB  
                 };
 
 size_t
diff --git a/pm_config.in.h b/pm_config.in.h
index d156127e..2b92c617 100644
--- a/pm_config.in.h
+++ b/pm_config.in.h
@@ -123,7 +123,7 @@
 #endif
 
 /* WIN32 is a macro that some older compilers predefine (compilers aren't
-   supposed to because it doesn't start with an underscore, hence the change.
+   supposed to because it doesn't start with an underscore, hence the change).
    Many build systems (project files, etc.) set WIN32 explicitly for
    backward compatibility.  Netpbm doesn't use it.
 */
diff --git a/test/Test-Order b/test/Test-Order
index d67c7f29..9b280d12 100644
--- a/test/Test-Order
+++ b/test/Test-Order
@@ -82,6 +82,7 @@ symmetry.test
 
 # Format converter tests
 
+pamtopdbimg.test
 pbmtog3.test
 411toppm.test
 eyuvtoppm.test
@@ -132,7 +133,10 @@ macp-roundtrip.test
 mda-roundtrip.test
 mgr-roundtrip.test
 mrf-roundtrip.test
+palm-roundtrip.test
+palm-roundtrip2.test
 pcx-roundtrip.test
+pdb-roundtrip.test
 pfm-roundtrip.test
 pi3-roundtrip.test
 pict-roundtrip.test
diff --git a/test/g3-roundtrip.ok b/test/g3-roundtrip.ok
index 4521d575..853d06d2 100644
--- a/test/g3-roundtrip.ok
+++ b/test/g3-roundtrip.ok
@@ -8,3 +8,6 @@
 0
 0
 0
+1777627284 265
+2985378006 3135
+3651878552 3135
diff --git a/test/g3-roundtrip.test b/test/g3-roundtrip.test
index baa6ac31..10174733 100755
--- a/test/g3-roundtrip.test
+++ b/test/g3-roundtrip.test
@@ -19,12 +19,15 @@ pbmtog3 testgrid.pbm | \
 g3topbm  | pnmcrop -white -right -bottom | \
  cmp -s - testgrid.pbm ; echo $?
 
-awk 'BEGIN { print "P4";         # header
-             print "8 256";
-             for (i=0;i<256;++i) # raster
-                  printf("%c",i) }' > ${complete256_pbm}
+# works with gawk and mawk
+# produce all possible 8-bit patterns
 
-pbmtog3 -nofixedwidth  ${complete256_pbm} |  g3topbm -width=8 | tee /tmp/z1 | \
+LC_ALL=C awk 'BEGIN { print "P4";         # header
+                      print "8 256";
+                      for (i=0;i<256;++i) # raster
+                           printf("%c",i) }' > ${complete256_pbm}
+
+pbmtog3 -nofixedwidth  ${complete256_pbm} |  g3topbm -width=8 | \
  cmp -s - ${complete256_pbm} ; echo $?
 
 pbmtog3 -reverse -nofixedwidth ${complete256_pbm} | \
@@ -39,16 +42,20 @@ pbmtog3 -align16 ${complete256_pbm} | \
 g3topbm -width=1728 | pnmcrop -white -right | \
  cmp -s - ${complete256_pbm} ; echo $?
 
-pbmmake -w 5000 5 | tee ${widew_pbm} | pbmtog3 -nofixedwidth | \
-g3topbm | \
+pbmmake -w 5000 5 > ${widew_pbm}
+pbmtog3 -nofixedwidth ${widew_pbm} | g3topbm | \
  cmp -s - ${widew_pbm} ; echo $?
 
 pbmtog3 -nofixedwidth ${widew_pbm} | \
 g3topbm -width=5000 | \
  cmp -s - ${widew_pbm} ; echo $?
 
-pbmmake -b 5000 5 | tee ${wideb_pbm} | pbmtog3 -nofixedwidth | \
-g3topbm | \
+pbmmake -b 5000 5 > ${wideb_pbm}
+pbmtog3 -nofixedwidth ${wideb_pbm} | g3topbm | \
  cmp -s - ${wideb_pbm} ; echo $?
 
+cat ${complete256_pbm} | cksum
+cat ${wideb_pbm} | cksum
+cat ${widew_pbm} | cksum
+
 rm ${complete256_pbm} ${wideb_pbm} ${widew_pbm}
diff --git a/test/palm-roundtrip.ok b/test/palm-roundtrip.ok
new file mode 100644
index 00000000..6c9114a4
--- /dev/null
+++ b/test/palm-roundtrip.ok
@@ -0,0 +1,10 @@
+584219238 236
+584219238 236
+584219238 236
+584219238 236
+584219238 236
+600373046 101484
+600373046 101484
+600373046 101484
+600373046 101484
+600373046 101484
diff --git a/test/palm-roundtrip.test b/test/palm-roundtrip.test
new file mode 100755
index 00000000..5570e968
--- /dev/null
+++ b/test/palm-roundtrip.test
@@ -0,0 +1,32 @@
+#! /bin/bash
+# This script tests: pnmtopalm palmtopnm
+# Also requires: pamdepth pnmquant
+
+tmpdir=${tmpdir:-/tmp}
+test4bit_pgm=${tmpdir}/test4bit.pgm
+test256color_ppm=${tmpdir}/test256color.ppm
+
+# Test 1. Should print 584219238 236  5 times
+
+pamdepth 15 testgrid.pbm | tee ${test4bit_pgm} | cksum
+
+for flags in "" \
+             "-scanline_compression" \
+             "-rle_compression" \
+             "-packbits_compression"
+  do pnmtopalm $flags ${test4bit_pgm} | palmtopnm | cksum; done
+
+rm ${test4bit_pgm}
+
+
+# Test 2. Should print 600373046 101484 5 times
+
+pnmquant 256 testimg.ppm | tee ${test256color_ppm} | cksum
+
+for flags in "" \
+             "-scanline_compression" \
+             "-rle_compression" \
+             "-packbits_compression" 
+  do pnmtopalm -colormap $flags ${test256color_ppm} | palmtopnm | cksum; done
+
+rm ${test256color_ppm}
diff --git a/test/palm-roundtrip2.ok b/test/palm-roundtrip2.ok
new file mode 100644
index 00000000..0060d7b6
--- /dev/null
+++ b/test/palm-roundtrip2.ok
@@ -0,0 +1,5 @@
+438301914 101484
+438301914 101484
+438301914 101484
+438301914 101484
+438301914 101484
diff --git a/test/palm-roundtrip2.test b/test/palm-roundtrip2.test
new file mode 100755
index 00000000..4c94df8c
--- /dev/null
+++ b/test/palm-roundtrip2.test
@@ -0,0 +1,31 @@
+#! /bin/bash
+# This script tests: pnmtopalm palmtopnm
+# Also requires: pnmremap
+
+tmpdir=${tmpdir:-/tmp}
+test256color_ppm=${tmpdir}/test256color.ppm
+
+if [ ${CHECK_TYPE} = install ]
+  then mapfile=$(netpbm-config --datadir)/palmcolor8.map
+  else mapfile=${PALMMAPDIR}/palmcolor8.map
+fi
+
+if [ ! -f ${mapfile} ]
+  then
+  echo "Cannot find palmcolor8.map.  Skipping." 1>&2
+  exit 80;
+fi
+
+
+# Test. Should print 438301914 101484
+# 5 times
+
+pnmremap -mapfile=${mapfile} testimg.ppm | tee ${test256color_ppm} | cksum
+
+for flags in "" \
+             "-scanline_compression" \
+             "-rle_compression" \
+             "-packbits_compression" 
+  do pnmtopalm -colormap $flags ${test256color_ppm} | palmtopnm | cksum; done
+
+rm ${test256color_ppm}
diff --git a/test/pamtopdbimg.ok b/test/pamtopdbimg.ok
new file mode 100644
index 00000000..57258940
--- /dev/null
+++ b/test/pamtopdbimg.ok
@@ -0,0 +1,20 @@
+high compression
+2
+1
+poor compression
+1
+2
+long titles
+268
+0
+268
+0
+0
+1
+large notefile
+3344
+0
+68885
+0
+0
+1
diff --git a/test/pamtopdbimg.test b/test/pamtopdbimg.test
new file mode 100755
index 00000000..267f6bc8
--- /dev/null
+++ b/test/pamtopdbimg.test
@@ -0,0 +1,86 @@
+#! /bin/bash
+# This script tests: pamtopdbimg
+# Also requires: pgmmake pgmnoise
+
+tmpdir=${tmpdir:-/tmp}
+mono_pgm=${tmpdir}/graymono.pgm
+noise_pgm=${tmpdir}/graynoise.pgm
+text65533=${tmpdir}/ascii65533.txt
+text65534=${tmpdir}/ascii65534.txt
+text65597=${tmpdir}/ascii65597.txt
+
+# The PDB image format has a time stamp.  This causes pamtopdbimg
+# to produce slightly different output files with each invocation.
+
+# Test 1. High compression
+# -maybecompressed should produce a compressed file
+# Should print:
+#  2
+#  1
+echo high compression
+pgmmake 0.5 -maxval=15 160 160 > ${mono_pgm}
+for flag in "-compressed" "-maybecompressed" "-uncompressed"
+   do
+   pamtopdbimg $flag ${mono_pgm} | wc -c
+   done  | uniq -c | awk '{print $1}'
+rm ${mono_pgm}
+
+
+# Test 2. Poor compression
+#  -maybecompressed should produce a uncompressed file
+# Should print:
+#  1
+#  2
+echo poor compression
+pgmnoise -maxval=15 -randomseed=0 160 160 > ${noise_pgm}
+for flag in "-compressed" "-maybecompressed" "-uncompressed"
+   do
+   pamtopdbimg $flag ${noise_pgm} | wc -c
+   done  | uniq -c | awk '{print $1}'
+rm ${noise_pgm}
+
+
+# Test 3. long titles
+# Should succeed twice and fail once, producing:
+# 268
+# 0
+# 268
+# 0
+# 1
+# 0
+echo long titles 
+for flag in "" "-title=0123456789012345678901234567890" \
+               "-title=01234567890123456789012345678901"
+   do
+   pamtopdbimg $flag testgrid.pbm | wc -c ; echo ${PIPESTATUS[0]}
+   done
+
+
+# Test 4. large notefile
+# Should succeed twice and fail once, producing:
+# 3344
+# 0
+# 68886
+# 0
+# 1
+# 0
+echo large notefile 
+awk 'BEGIN { ABC="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+             abc="abcdefghijklmnopqrstuvwxyz";
+             digits="0123456789"; string ="";
+
+             while ( length(string) < 65597 ) 
+                string = string ABC abc digits;
+
+             print string }' > ${text65597}
+
+head -c 65533 ${text65597} > ${text65533}
+head -c 65534 ${text65597} > ${text65534}
+pamtopdbimg -uncompressed testgrid.pbm | wc -c
+  echo ${PIPESTATUS[0]}
+pamtopdbimg -uncompressed -notefile=${text65533} testgrid.pbm | wc -c
+  echo ${PIPESTATUS[0]}
+pamtopdbimg -uncompressed -notefile=${text65534} testgrid.pbm | wc -c
+  echo ${PIPESTATUS[0]}
+
+rm ${text65533} ${text65534} ${text65597}
\ No newline at end of file
diff --git a/test/pdb-roundtrip.ok b/test/pdb-roundtrip.ok
new file mode 100644
index 00000000..cb920669
--- /dev/null
+++ b/test/pdb-roundtrip.ok
@@ -0,0 +1,18 @@
+pbm grid
+2224198737 25671
+2224198737 25671
+2224198737 25671
+pbm tiled
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+pgm ellipse
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+pgm ellipse -4depth
+0 0 0
+0 0 0
+0 0 0
diff --git a/test/pdb-roundtrip.test b/test/pdb-roundtrip.test
new file mode 100755
index 00000000..a6de93d2
--- /dev/null
+++ b/test/pdb-roundtrip.test
@@ -0,0 +1,55 @@
+#! /bin/bash
+# This script tests: pamtopdbimg pdbimgtopam
+# Also requires: pnmtile pgmramp pamtopnm
+
+tmpdir=${tmpdir:-/tmp}
+tiled_pbm=${tmpdir}/tiled.pbm
+ellipse_pgm=${tmpdir}/ellipse.pgm
+
+
+# Test 1. Should produce 2224198737 25671
+# 3 times 
+echo pbm grid
+for flag in "-compressed" "-maybecompressed" "-uncompressed"
+   do
+   pamtopdbimg ${flag} testgrid.pbm | pdbimgtopam | cksum
+   done
+
+
+# Test 2. Should produce 0 0 0
+# 4 times 
+echo pbm tiled
+pnmtile 160 160 testgrid.pbm > ${tiled_pbm}
+for flag in "" "-compressed" "-maybecompressed" "-uncompressed"
+  do
+  pamtopdbimg ${flag} ${tiled_pbm} | pdbimgtopam | pamtopnm | \
+    cmp -s - ${tiled_pbm}
+  echo ${PIPESTATUS[0]} ${PIPESTATUS[1]} $?
+  done
+rm ${tiled_pbm}
+
+
+# Test 3. Should produce 0 0 0
+# 4 times
+echo pgm ellipse
+pgmramp -ellipse 160 160 -maxval=3 > ${ellipse_pgm}
+for flag in "" "-compressed" "-maybecompressed" "-uncompressed"
+    do
+    pamtopdbimg ${flag} ${ellipse_pgm} | pdbimgtopam | \
+      pamtopnm | pamdepth 3 | cmp -s - ${ellipse_pgm}
+    echo ${PIPESTATUS[0]} ${PIPESTATUS[1]} $?
+    done
+rm ${ellipse_pgm}
+
+
+# Test 3. Should produce 0 0 0
+# 3 times 
+echo pgm ellipse -4depth
+pgmramp -ellipse 160 160 -maxval=15 > ${ellipse_pgm}
+for flag in "-compressed" "-maybecompressed" "-uncompressed"
+    do
+    pamtopdbimg -4depth ${flag} ${ellipse_pgm} | pdbimgtopam | \
+      pamtopnm | pamdepth 15 | cmp -s - ${ellipse_pgm}
+    echo ${PIPESTATUS[0]} ${PIPESTATUS[1]} $?
+    done
+rm ${ellipse_pgm}
\ No newline at end of file
diff --git a/test/pnmquantall.ok b/test/pnmquantall.ok
index bd5bbc05..172eb0aa 100644
--- a/test/pnmquantall.ok
+++ b/test/pnmquantall.ok
@@ -1,5 +1,5 @@
 got color map
-2373957371 33838 testimg.red
-3892560659 33838 testimg.grn
-1383839923 33838 testimg.blu
+2373957371 33838
+3892560659 33838
+1383839923 33838
 1
diff --git a/test/pnmquantall.test b/test/pnmquantall.test
index 7d9ddf82..cd01bb8d 100755
--- a/test/pnmquantall.test
+++ b/test/pnmquantall.test
@@ -2,16 +2,26 @@
 # This script tests: pnmquantall
 # Also requires: ppmtorgb3 pgmhist
 
-ppmtorgb3 testimg.ppm
+tmpdir=${tmpdir:-/tmp}
+test_ppm=${tmpdir}/testimg.ppm
 
-pnmquantall 20 testimg.red testimg.grn testimg.blu
+cp testimg.ppm ${tmpdir} &&
+ppmtorgb3 ${test_ppm}
 
-for i in testimg.red testimg.grn testimg.blu
+test_red=${tmpdir}/testimg.red
+test_grn=${tmpdir}/testimg.grn
+test_blu=${tmpdir}/testimg.blu
+
+pnmquantall 20 ${test_red} ${test_grn} ${test_blu}
+
+for i in ${test_red} ${test_grn} ${test_blu}
 do
-cksum $i
+cat $i | cksum
 done
 
 # Should print 1
 
-pnmcat testimg.red testimg.grn testimg.blu -tb | \
+pnmcat ${test_red} ${test_grn} ${test_blu} -tb | \
     pgmhist -m | awk '$2>0 {s++}; END { print (s<=20) }'
+
+rm ${test_red} ${test_grn} ${test_blu} ${test_ppm}
\ No newline at end of file
diff --git a/test/ppmbrighten.ok b/test/ppmbrighten.ok
index 65feb812..0ce9f722 100644
--- a/test/ppmbrighten.ok
+++ b/test/ppmbrighten.ok
@@ -1,3 +1,3 @@
-2737989845 101484
-1059247992 101484
-32344911 101484
+2634278866 101484
+2791274519 101484
+2464564658 101484
diff --git a/version.mk b/version.mk
index 169bc3a5..04bdfba1 100644
--- a/version.mk
+++ b/version.mk
@@ -1,3 +1,3 @@
 NETPBM_MAJOR_RELEASE = 10
-NETPBM_MINOR_RELEASE = 79
-NETPBM_POINT_RELEASE = 7
+NETPBM_MINOR_RELEASE = 80
+NETPBM_POINT_RELEASE = 0