about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2018-03-25 17:14:57 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2018-03-25 17:14:57 +0000
commitcd86a2c8d798c98b2f5d7656971fc4553b4ed172 (patch)
tree6218a52f6e09f0cb0ff15c0a0b130da920328e8e
parente488b82f0f446576138084a1bd57b7b4406e8db0 (diff)
downloadnetpbm-mirror-cd86a2c8d798c98b2f5d7656971fc4553b4ed172.tar.gz
netpbm-mirror-cd86a2c8d798c98b2f5d7656971fc4553b4ed172.tar.xz
netpbm-mirror-cd86a2c8d798c98b2f5d7656971fc4553b4ed172.zip
Copy Development as new Advanced
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@3186 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r--analyzer/pnmpsnr.c135
-rw-r--r--analyzer/ppmhist.c226
-rw-r--r--common.mk8
-rw-r--r--converter/other/giftopnm.c260
-rw-r--r--converter/other/pamtogif.c627
-rw-r--r--converter/other/pngtopam.c98
-rw-r--r--converter/pbm/g3topbm.c62
-rw-r--r--converter/pbm/pbmtolj.c41
-rw-r--r--converter/ppm/ppmtompeg/HISTORY2
-rw-r--r--doc/HISTORY45
-rw-r--r--editor/pnmconvol.c130
-rwxr-xr-xeditor/pnmquant20
-rw-r--r--editor/pnmremap.c250
-rw-r--r--generator/pbmtext.c658
-rw-r--r--generator/ppmpat.c97
-rwxr-xr-xgenerator/ppmrainbow4
-rw-r--r--lib/libpam.c190
-rw-r--r--lib/libpbm3.c100
-rw-r--r--lib/libpbmfont.c211
-rw-r--r--lib/libppm1.c108
-rw-r--r--lib/pam.h110
-rw-r--r--lib/pbmfont.h53
-rw-r--r--lib/pm_gamma.h24
-rw-r--r--lib/ppm.h90
-rw-r--r--lib/util/nstring.c36
-rw-r--r--lib/util/shhopt.c58
-rwxr-xr-xtest/all-in-place.test2
-rw-r--r--test/gif-roundtrip.ok13
-rwxr-xr-xtest/gif-roundtrip.test105
-rw-r--r--test/jpeg-roundtrip.ok6
-rwxr-xr-xtest/jpeg-roundtrip.test23
-rw-r--r--test/pnmpsnr.ok3
-rwxr-xr-xtest/pnmpsnr.test3
-rw-r--r--test/ppmhist.ok14
-rwxr-xr-xtest/ppmhist.test21
-rw-r--r--test/tiffcmyk-roundtrip.ok10
-rwxr-xr-xtest/tiffcmyk-roundtrip.test33
-rw-r--r--version.mk4
38 files changed, 2383 insertions, 1497 deletions
diff --git a/analyzer/pnmpsnr.c b/analyzer/pnmpsnr.c
index 1ddefac2..2363e8c3 100644
--- a/analyzer/pnmpsnr.c
+++ b/analyzer/pnmpsnr.c
@@ -21,6 +21,33 @@
 
 
 
+struct TargetSet {
+    unsigned int targetSpec;
+    float        target;
+    unsigned int target1Spec;
+    float        target1;
+    unsigned int target2Spec;
+    float        target2;
+    unsigned int target3Spec;
+    float        target3;
+};
+
+
+
+static bool
+targetSet_compTargetSpec(struct TargetSet const targetSet) {
+/*----------------------------------------------------------------------------
+   The target set specifies individual color component targets
+   (some may be "don't care", though).
+-----------------------------------------------------------------------------*/
+    return
+        targetSet.target1Spec ||
+        targetSet.target2Spec ||
+        targetSet.target3Spec;
+}
+
+
+
 struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
@@ -31,11 +58,36 @@ struct CmdlineInfo {
     unsigned int machine;
     unsigned int maxSpec;
     float        max;
+    bool         targetMode;
+    struct TargetSet target;
 };
 
 
 
 static void
+interpretTargetSet(struct TargetSet const targetSet,
+                   bool *           const targetModeP) {
+
+    if (targetSet.targetSpec && targetSet.target <= 0.0)
+        pm_error("Nonpositive -target does not make sense");
+
+    if (targetSet.target1Spec && targetSet.target1 <= 0.0)
+        pm_error("Nonpositive -target1 does not make sense");
+
+    if (targetSet.target2Spec && targetSet.target2 <= 0.0)
+        pm_error("Nonpositive -target2 does not make sense");
+
+    if (targetSet.target3Spec && targetSet.target3 <= 0.0)
+        pm_error("Nonpositive -target3 does not make sense");
+
+    *targetModeP =
+        targetSet.targetSpec || targetSet.target1Spec ||
+        targetSet.target2Spec || targetSet.target3Spec;
+}
+
+
+
+static void
 parseCommandLine(int argc, const char ** argv,
                  struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
@@ -50,23 +102,31 @@ parseCommandLine(int argc, const char ** argv,
     unsigned int option_def_index;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
-    
+
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0,   "rgb",      OPT_FLAG,  NULL,
             &cmdlineP->rgb,       0);
     OPTENT3(0,   "machine",  OPT_FLAG,  NULL,
             &cmdlineP->machine,   0);
-    OPTENT3(0,   "max",      OPT_FLOAT, &cmdlineP->max,  
+    OPTENT3(0,   "max",      OPT_FLOAT, &cmdlineP->max,
             &cmdlineP->maxSpec,   0);
+    OPTENT3(0,   "target",   OPT_FLOAT, &cmdlineP->target.target,
+            &cmdlineP->target.targetSpec,   0);
+    OPTENT3(0,   "target1",  OPT_FLOAT, &cmdlineP->target.target1,
+            &cmdlineP->target.target1Spec,   0);
+    OPTENT3(0,   "target2",  OPT_FLOAT, &cmdlineP->target.target2,
+            &cmdlineP->target.target2Spec,   0);
+    OPTENT3(0,   "target3",  OPT_FLOAT, &cmdlineP->target.target3,
+            &cmdlineP->target.target3Spec,   0);
 
     opt.opt_table     = option_def;
     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, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others */
 
-    if (argc-1 < 2) 
+    if (argc-1 < 2)
         pm_error("Takes two arguments:  names of the two files to compare");
     else {
         cmdlineP->inputFile1Name = argv[1];
@@ -78,6 +138,11 @@ parseCommandLine(int argc, const char ** argv,
     }
 
     free(option_def);
+
+    interpretTargetSet(cmdlineP->target, &cmdlineP->targetMode);
+
+    if (cmdlineP->targetMode && cmdlineP->maxSpec)
+        pm_error("-max is meaningless with -targetX");
 }
 
 
@@ -221,10 +286,10 @@ sqDiffYCbCr(tuple    const tuple1,
     struct SqDiff retval;
 
     double y1, y2, cb1, cb2, cr1, cr2;
-    
+
     pnm_YCbCrtuple(tuple1, &y1, &cb1, &cr1);
     pnm_YCbCrtuple(tuple2, &y2, &cb2, &cr2);
-    
+
     retval.sqDiff[Y_INDEX]  = square(y1  - y2);
     retval.sqDiff[CB_INDEX] = square(cb1 - cb2);
     retval.sqDiff[CR_INDEX] = square(cr1 - cr2);
@@ -316,12 +381,12 @@ sumSqDiffFromRaster(struct pam * const pam1P,
 
     tuplerow1 = pnm_allocpamrow(pam1P);
     tuplerow2 = pnm_allocpamrow(pam2P);
-    
+
     sumSqDiff = zeroSqDiff();
 
     for (row = 0; row < pam1P->height; ++row) {
         unsigned int col;
-        
+
         pnm_readpamrow(pam1P, tuplerow1);
         pnm_readpamrow(pam2P, tuplerow2);
 
@@ -385,7 +450,7 @@ psnrFromSumSqDiff(struct SqDiff const sumSqDiff,
        to the actual mean square difference, which is also the ratio of
        the maximum possible sum square difference to the actual sum square
        difference.
-   
+
        Note that in the important special case that the images are
        identical, the sum square differences are identically 0.0.
        No precision error; no rounding error.
@@ -418,6 +483,49 @@ psnrIsFinite(double const psnr) {
 
 
 static void
+reportTarget(struct Psnr      const psnr,
+             ColorSpace       const colorSpace,
+             struct TargetSet const target) {
+
+    bool hitsTarget;
+
+    if (colorSpace.componentCt == 1) {
+        if (!target.targetSpec)
+            pm_error("Image is monochrome and you specified "
+                     "-target1, -target2, or -target3 but not -target");
+
+        hitsTarget = psnr.psnr[0] >= target.target;
+    } else {
+        float compTarget[3];
+
+        unsigned int i;
+
+        assert(colorSpace.componentCt == 3);
+
+        if (targetSet_compTargetSpec(target)) {
+            compTarget[0] = target.target1Spec ? target.target1 : -1;
+            compTarget[1] = target.target2Spec ? target.target2 : -1;
+            compTarget[2] = target.target3Spec ? target.target3 : -1;
+        } else {
+            assert(target.targetSpec);
+            compTarget[0] = target.target;
+            compTarget[1] = target.target;
+            compTarget[2] = target.target;
+        }
+        for (i = 0, hitsTarget = true;
+             i < colorSpace.componentCt && hitsTarget;
+             ++i) {
+
+            if (psnr.psnr[i] < compTarget[i])
+                hitsTarget = false;
+        }
+    }
+    fprintf(stdout, "%s\n", hitsTarget ? "match" : "nomatch");
+}
+
+
+
+static void
 reportPsnrHuman(struct Psnr   const psnr,
                 ColorSpace    const colorSpace,
                 const char *  const fileName1,
@@ -470,7 +578,7 @@ main (int argc, const char **argv) {
     FILE * if2P;
     struct pam pam1, pam2;
     ColorSpace colorSpace;
-    
+
     struct CmdlineInfo cmdline;
 
     pm_proginit(&argc, argv);
@@ -508,12 +616,16 @@ main (int argc, const char **argv) {
             psnrFromSumSqDiff(
                 sumSqDiff, maxSumSqDiff, colorSpace.componentCt);
 
-        if (cmdline.machine)
+        if (cmdline.targetMode)
+            reportTarget(psnr, colorSpace, cmdline.target);
+        else if (cmdline.machine)
             reportPsnrMachine(psnr, colorSpace.componentCt,
                               cmdline.maxSpec, cmdline.max);
         else
             reportPsnrHuman(psnr, colorSpace,
                             cmdline.inputFile1Name, cmdline.inputFile2Name);
+
+
     }
     pm_close(if2P);
     pm_close(if1P);
@@ -522,3 +634,4 @@ main (int argc, const char **argv) {
 }
 
 
+
diff --git a/analyzer/ppmhist.c b/analyzer/ppmhist.c
index 9b526606..299ab6ca 100644
--- a/analyzer/ppmhist.c
+++ b/analyzer/ppmhist.c
@@ -22,7 +22,7 @@ enum sort {SORT_BY_FREQUENCY, SORT_BY_RGB};
 
 enum ColorFmt {FMT_DECIMAL, FMT_HEX, FMT_FLOAT, FMT_PPMPLAIN};
 
-struct cmdline_info {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -38,7 +38,7 @@ struct cmdline_info {
 
 static void
 parseCommandLine(int argc, const char ** argv,
-                 struct cmdline_info * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
@@ -230,21 +230,21 @@ separateInvalidItems(colorhist_vector const chv,
                      unsigned int  *  const validColorCtP) {
 /*----------------------------------------------------------------------------
   Move invalid color entries from chv to chvInvalid.
-  Count how many color entries are valid. 
+  Count how many color entries are valid.
 -----------------------------------------------------------------------------*/
     unsigned int i;
     unsigned int validCt;
     unsigned int invalidCt;
- 
+
     for (i = 0, validCt = 0, invalidCt = 0; i < colorCt; ++i) {
         if (!colorIsValid(chv[i].color, maxval))
             chvInvalid[invalidCt++] = chv[i];
         else
             chv[validCt++] = chv[i];
-    } 
+    }
     *validColorCtP = validCt;
 }
-  
+
 
 
 static void
@@ -261,7 +261,7 @@ sortHistogramForensic(enum sort        const sortFn,
 
     {
         int (*compare_function)(const void *, const void *);
-    
+
         switch (sortFn) {
         case SORT_BY_FREQUENCY: compare_function = countcompare; break;
         case SORT_BY_RGB:       compare_function = rgbcompare;   break;
@@ -269,7 +269,7 @@ sortHistogramForensic(enum sort        const sortFn,
 
         qsort((void*) chv, validColorCt,
               sizeof(struct colorhist_item), compare_function);
-        
+
         qsort((void*) chvInvalid, colorCt - validColorCt,
               sizeof(struct colorhist_item), compare_function);
     }
@@ -295,20 +295,104 @@ sortHistogramNormal(enum sort        const sortFn,
 }
 
 
+typedef struct {
+    unsigned int nTotal;
+        /* Number of colors; sum of all the following */
+    unsigned int nBlack;
+        /* 1 if black is present; 0 otherwise */
+    unsigned int nWhite;
+        /* 1 if white is present; 0 otherwise */
+    unsigned int nGray;
+        /* Number of gray shades, not including black and white */
+    unsigned int nColor;
+        /* number of colors other than black, white, and gray */
+
+} ColorSummary;
+
+
+
+static ColorSummary
+colorSummary(colorhist_vector const chv,
+             unsigned int     const colorCt,
+             pixval           const maxval) {
+
+    ColorSummary retval;
+
+    unsigned int i;
+
+    retval.nTotal = colorCt;
+
+    for (i = 0,
+             retval.nBlack = 0,
+             retval.nWhite = 0,
+             retval.nGray = 0,
+             retval.nColor = 0;
+         i < colorCt;
+         ++i) {
+
+        pixel const color = chv[i].color;
+        pixval const r = PPM_GETR(color);
+        pixval const g = PPM_GETG(color);
+        pixval const b = PPM_GETB(color);
+
+        if (r == 0 && g == 0 && b == 0)
+            ++retval.nBlack;
+        else if (r == maxval && g == maxval && b == maxval)
+            ++retval.nWhite;
+        else if (r == g && r ==b)
+            ++retval.nGray;
+        else
+            ++retval.nColor;
+    }
+    assert(retval.nBlack + retval.nWhite + retval.nGray + retval.nColor ==
+           retval.nTotal);
+
+    return retval;
+}
+
+
+static void
+printColorSummary(ColorSummary const colorSummary,
+                  const char * const prefix) {
+
+    printf("%sSummary: %u colors: %u black, %u white, %u gray, %u color\n",
+           prefix,
+           colorSummary.nTotal,
+           colorSummary.nBlack,
+           colorSummary.nWhite,
+           colorSummary.nGray,
+           colorSummary.nColor);
+
+    printf("\n");
+}
+
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   A map of color name to color.
+
+   The actual information is outside of this structure; we just point to it.
+-----------------------------------------------------------------------------*/
+    unsigned int  n;
+
+    pixel *       color;
+
+    const char ** name;
+} ColorDict;
+
+
 
 static const char *
-colornameLabel(pixel        const color,
-               pixval       const maxval,
-               unsigned int const nDictColor,
-               pixel        const dictColors[],
-               const char * const dictColornames[]) {
+colornameLabel(pixel     const color,
+               pixval    const maxval,
+               ColorDict const colorDict) {
 /*----------------------------------------------------------------------------
    Return the name of the color 'color' or the closest color in the
    dictionary to it.  If the name returned is not the exact color,
    prefix it with "*".  Otherwise, prefix it with " ".
 
-   'nDictColor', dictColors[], and dictColorNames[] are the color
-   dictionary.
+   'colorDict' is the color dictionary.
 
    Return the name in static storage within this subroutine.
 -----------------------------------------------------------------------------*/
@@ -322,16 +406,16 @@ colornameLabel(pixel        const color,
 
     PPM_DEPTH(color255, color, maxval, 255);
 
-    colorIndex = ppm_findclosestcolor(dictColors, nDictColor, &color255);
+    colorIndex = ppm_findclosestcolor(colorDict.color, colorDict.n, &color255);
 
-    assert(colorIndex >= 0 && colorIndex < nDictColor);
+    assert(colorIndex >= 0 && colorIndex < colorDict.n);
 
-    if (PPM_EQUAL(dictColors[colorIndex], color))
+    if (PPM_EQUAL(colorDict.color[colorIndex], color))
         STRSCPY(retval, " ");
     else
         STRSCPY(retval, "*");
 
-    STRSCAT(retval, dictColornames[colorIndex]);
+    STRSCAT(retval, colorDict.name[colorIndex]);
 
     return retval;
 }
@@ -343,13 +427,22 @@ printColors(colorhist_vector const chv,
             int              const colorCt,
             pixval           const maxval,
             enum ColorFmt    const colorFmt,
-            unsigned int     const nKnown,
-            pixel            const knownColors[],
-            const char *     const colornames[]) {
+            bool             const withColorName,
+            ColorDict        const colorDict) {
+/*----------------------------------------------------------------------------
+   Print to Standard Output the list of colors, one per line in 'chv',
+   of which there are 'colorCt'.
+
+   Print the color in format 'colorFmt'.
 
-    int i;
+   If 'withColorName' is true, we add the name of each color to the line.
+   'oclorDict' is a list of known names of colors. If the color is not in the
+   list, we add the name of the color closest to it whose name we know,
+   prefixed by "*".
+-----------------------------------------------------------------------------*/
+    unsigned int i;
 
-    for (i = 0; i < colorCt; i++) {
+    for (i = 0; i < colorCt; ++i) {
         pixval       const r          = PPM_GETR(chv[i].color);
         pixval       const g          = PPM_GETG(chv[i].color);
         pixval       const b          = PPM_GETB(chv[i].color);
@@ -360,9 +453,8 @@ printColors(colorhist_vector const chv,
 
         const char * colornameValue;
 
-        if (colornames)
-            colornameValue = colornameLabel(chv[i].color, maxval,
-                                            nKnown, knownColors, colornames);
+        if (withColorName)
+            colornameValue = colornameLabel(chv[i].color, maxval, colorDict);
         else
             colornameValue = "";
 
@@ -393,6 +485,47 @@ printColors(colorhist_vector const chv,
 
 
 static void
+printHistogram(colorhist_vector const chv,
+               unsigned int     const colorCt,
+               pixval           const maxval,
+               enum ColorFmt    const colorFmt,
+               bool             const wantHeader,
+               bool             const wantColorName) {
+
+    ColorDict colorDict;
+
+    if (colorFmt == FMT_PPMPLAIN)
+        printf("P3\n# color map\n%d 1\n%d\n", colorCt, maxval);
+
+    if (wantHeader) {
+        const char * commentDelim = colorFmt == FMT_PPMPLAIN ? "#" : " ";
+
+        printColorSummary(colorSummary(chv, colorCt, maxval), commentDelim);
+
+        printf("%s  r     g     b   \t lum \t count  %s\n",
+               commentDelim, wantColorName ? "name" : "");
+        printf("%s----- ----- ----- \t-----\t------- %s\n",
+               commentDelim, wantColorName ? "----" : "");
+    }
+    if (wantColorName) {
+        bool const mustOpenTrue = TRUE;
+        ppm_readcolordict(NULL, mustOpenTrue,
+                          &colorDict.n, &colorDict.name, &colorDict.color,
+                          NULL);
+    }
+
+    printColors(chv, colorCt, maxval,
+                colorFmt, wantColorName, colorDict);
+
+    if (wantColorName) {
+        free(colorDict.color);
+        free(colorDict.name);
+    }
+}
+
+
+
+static void
 summarizeInvalidPixels(unsigned long int const validPixelCt,
                        unsigned long int const invalidPixelCt,
                        pixval            const maxval) {
@@ -430,7 +563,7 @@ printInvalidSamples(colorhist_vector const chv,
     unsigned long int invalidPixelCt;
 
     for (i = 0, validPixelCt = 0; i < validColorCt; ++i)
-        validPixelCt += chv[i].value; 
+        validPixelCt += chv[i].value;
 
     for (i = 0, invalidPixelCt = 0; i < invalidColorCt; ++i) {
         pixval       const r     = PPM_GETR(chvInvalid[i].color);
@@ -438,7 +571,7 @@ printInvalidSamples(colorhist_vector const chv,
         pixval       const b     = PPM_GETB(chvInvalid[i].color);
         unsigned int const count = chvInvalid[i].value;
 
-        invalidPixelCt += chvInvalid[i].value; 
+        invalidPixelCt += chvInvalid[i].value;
 
         switch(colorFmt) {
         case FMT_FLOAT:
@@ -470,7 +603,7 @@ printInvalidSamples(colorhist_vector const chv,
 int
 main(int argc, const char *argv[]) {
 
-    struct cmdline_info cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     colorhist_vector chv;
     colorhist_vector chvInvalid;
@@ -479,9 +612,6 @@ main(int argc, const char *argv[]) {
     pixval mmaxval;
     int format;
     int colorCt;
-    unsigned int dictColorCt;
-    const char ** dictColornames;
-    pixel * dictColors;
     unsigned int validColorCt;
 
     pm_proginit(&argc, argv);
@@ -512,38 +642,13 @@ main(int argc, const char *argv[]) {
         validColorCt = colorCt;
     }
 
-    /* And print the histogram. */
-    if (cmdline.colorFmt == FMT_PPMPLAIN)
-        printf("P3\n# color map\n%d 1\n%d\n", colorCt, maxval);
-
-    if (!cmdline.noheader) {
-        const char commentDelim = cmdline.colorFmt == FMT_PPMPLAIN ? '#' : ' ';
-        printf("%c  r     g     b   \t lum \t count  %s\n",
-               commentDelim, cmdline.colorname ? "name" : "");
-        printf("%c----- ----- ----- \t-----\t------- %s\n",
-               commentDelim, cmdline.colorname ? "----" : "");
-    }
-    if (cmdline.colorname) {
-        bool const mustOpenTrue = TRUE;
-        ppm_readcolordict(NULL, mustOpenTrue,
-                          &dictColorCt, &dictColornames, &dictColors, NULL);
-    } else {
-        dictColors = NULL;
-        dictColornames = NULL;
-    }
-
-    printColors(chv, validColorCt, maxval,
-                cmdline.colorFmt, dictColorCt, dictColors, dictColornames);
+    printHistogram(chv, validColorCt, maxval,
+                   cmdline.colorFmt, !cmdline.noheader, cmdline.colorname);
 
     if (colorCt > validColorCt)
         printInvalidSamples(chv, chvInvalid, colorCt, validColorCt,
                             maxval, cmdline.colorFmt);
 
-    if (dictColors)
-        free(dictColors);
-    if (dictColornames)
-        free(dictColornames);
-
     ppm_freecolorhist(chv);
 
     if (chvInvalid)
@@ -553,4 +658,3 @@ main(int argc, const char *argv[]) {
 }
 
 
-
diff --git a/common.mk b/common.mk
index e24d8ff3..97810cd4 100644
--- a/common.mk
+++ b/common.mk
@@ -272,24 +272,24 @@ $(OBJECTS): %.o: %.c importinc
 LIBOPT = $(BUILDDIR)/buildtools/libopt
 
 ifneq ($(OMIT_BUILDTOOL_RULE),1)
-$(LIBOPT) $(TYPEGEN): $(BUILDDIR)/buildtools
+$(LIBOPT) $(TYPEGEN): $(BUILDDIR)/buildtools FORCE
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/buildtools/Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 endif
 
 ifneq ($(OMIT_LIBRARY_RULE),1)
-$(NETPBMLIB): $(BUILDDIR)/lib
+$(NETPBMLIB): $(BUILDDIR)/lib FORCE
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/lib/Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 endif
 
 ifneq ($(OMIT_URT_RULE),1)
-$(BUNDLED_URTLIB): $(BUILDDIR)/urt
+$(BUNDLED_URTLIB): $(BUILDDIR)/urt FORCE
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/urt/Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 endif
 
-$(BUILDDIR)/icon/netpbm.o: $(BUILDDIR)/icon
+$(BUILDDIR)/icon/netpbm.o: $(BUILDDIR)/icon FORCE
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/icon/Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 
diff --git a/converter/other/giftopnm.c b/converter/other/giftopnm.c
index f0946c9e..e2d5923d 100644
--- a/converter/other/giftopnm.c
+++ b/converter/other/giftopnm.c
@@ -9,7 +9,7 @@
 /* +-------------------------------------------------------------------+ */
 
 /* There is a copy of the GIF89 specification, as defined by its inventor,
-   Compuserve, in 1990 at: 
+   Compuserve, in 1990 at:
    http://www.w3.org/Graphics/GIF/spec-gif89a.txt
 
    This covers the high level format, but does not cover how the "data"
@@ -116,7 +116,7 @@ parseCommandLine(int argc, const char ** argv,
     optEntry * option_def;
         /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
-    
+
     optStruct3 opt;
 
     unsigned int alphaSpec, imageSpec;
@@ -125,9 +125,9 @@ parseCommandLine(int argc, const 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, 
+    OPTENT3(0, "verbose",     OPT_FLAG, NULL,
             &cmdlineP->verbose,         0);
     OPTENT3(0, "comments",    OPT_FLAG, NULL,
             &cmdlineP->comments,        0);
@@ -137,7 +137,7 @@ parseCommandLine(int argc, const char ** argv,
             &cmdlineP->repair,          0);
     OPTENT3(0, "image",       OPT_STRING, &image,
             &imageSpec,                 0);
-    OPTENT3(0, "alphaout",    OPT_STRING, &cmdlineP->alphaFileName, 
+    OPTENT3(0, "alphaout",    OPT_STRING, &cmdlineP->alphaFileName,
             &alphaSpec,                 0);
 
     opt.opt_table = option_def;
@@ -153,7 +153,7 @@ parseCommandLine(int argc, const char ** argv,
         cmdlineP->imageNum = 0;
         cmdlineP->allImages = false;
     } else {
-        if (strcaseeq(image, "all")) { 
+        if (strcaseeq(image, "all")) {
             cmdlineP->allImages = true;
         } else {
             char * tailptr;
@@ -174,8 +174,8 @@ parseCommandLine(int argc, const char ** argv,
             cmdlineP->imageNum = (unsigned int) imageNum - 1;
         }
     }
-    
-    if (argc-1 == 0) 
+
+    if (argc-1 == 0)
         cmdlineP->inputFilespec = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
@@ -183,7 +183,7 @@ parseCommandLine(int argc, const char ** argv,
     else
         cmdlineP->inputFilespec = argv[1];
 
-    if (!alphaSpec) 
+    if (!alphaSpec)
         cmdlineP->alphaFileName = NULL;
 }
 
@@ -255,13 +255,13 @@ struct GifScreen {
         /* Aspect ratio of each pixel, times 64, minus 15.  (i.e. 1 => 1:4).
            But Zero means 1:1.
         */
-    bool     hasGray;  
+    bool     hasGray;
         /* Boolean: global colormap has at least one gray color
-           (not counting black and white) 
+           (not counting black and white)
         */
     bool     hasColor;
         /* Boolean: global colormap has at least one non-gray,
-           non-black, non-white color 
+           non-black, non-white color
         */
 };
 
@@ -269,7 +269,7 @@ struct Gif89 {
     bool         haveTransColor;
         /* The GIF specifies a transparent background color */
     unsigned int transparentIndex;
-        /* The color index of the color which is the transparent 
+        /* The color index of the color which is the transparent
            background color.
 
            Meaningful only when 'haveTransColor' is true
@@ -288,7 +288,7 @@ initGif89(struct Gif89 * const gif89P) {
     gif89P->haveDelayTime  = false;
     gif89P->haveInputFlag  = false;
     gif89P->haveDisposal   = false;
-}       
+}
 
 
 static bool verbose;
@@ -347,8 +347,8 @@ static bool zeroDataBlock = false;
     */
 
 static void
-getDataBlock(FILE *          const ifP, 
-             unsigned char * const buf, 
+getDataBlock(FILE *          const ifP,
+             unsigned char * const buf,
              bool *          const eofP,
              unsigned int *  const lengthP,
              const char **   const errorP) {
@@ -372,7 +372,7 @@ getDataBlock(FILE *          const ifP,
 
     unsigned char count;
     const char * error;
-    
+
     readFile(ifP, &count, sizeof(count), &error);
 
     if (error) {
@@ -395,7 +395,7 @@ getDataBlock(FILE *          const ifP,
             const char * error;
 
             zeroDataBlock = false;
-            readFile(ifP, buf, count, &error); 
+            readFile(ifP, buf, count, &error);
 
             if (error) {
                 pm_asprintf(errorP,
@@ -446,7 +446,7 @@ doPlainTextExtension(FILE * const ifP) {
 #if 0
     /* incomplete code fragment, attempt to handle Plain Text Extension */
     GetDataBlock(ifP, (unsigned char*) buf, &eof, &length);
-    
+
     lpos   = LM_to_uint(buf[0], buf[1]);
     tpos   = LM_to_uint(buf[2], buf[3]);
     width  = LM_to_uint(buf[4], buf[5]);
@@ -467,20 +467,20 @@ doCommentExtension(FILE * const ifP) {
 /*----------------------------------------------------------------------------
    Read the rest of a comment extension from the input file 'ifP' and handle
    it.
-   
+
    We ought to deal with the possibility that the comment is not text.  I.e.
    it could have nonprintable characters or embedded nulls.  I don't know if
    the GIF spec requires regular text or not.
 -----------------------------------------------------------------------------*/
     char buf[256];
-    unsigned int blocklen;  
+    unsigned int blocklen;
     bool done;
 
     done = false;
     while (!done) {
         bool eof;
         const char * error;
-        getDataBlock(ifP, (unsigned char*) buf, &eof, &blocklen, &error); 
+        getDataBlock(ifP, (unsigned char*) buf, &eof, &blocklen, &error);
         if (error)
             pm_error("Error reading a data block in a comment extension.  %s",
                      error);
@@ -506,7 +506,7 @@ LM_to_uint(unsigned char const a,
 
 
 
-static void 
+static void
 doGraphicControlExtension(FILE *         const ifP,
                           struct Gif89 * const gif89P) {
 
@@ -521,7 +521,7 @@ doGraphicControlExtension(FILE *         const ifP,
     if (eof)
         pm_error("EOF/error encountered reading "
                  "1st DataBlock of Graphic Control Extension.");
-    else if (length < 4) 
+    else if (length < 4)
         pm_error("graphic control extension 1st DataBlock too short.  "
                  "It must be at least 4 bytes; it is %d bytes.",
                  length);
@@ -548,7 +548,7 @@ doExtension(FILE *         const ifP,
             struct Gif89 * const gif89P) {
 
     const char * str;
-    
+
     switch (label) {
     case 0x01:              /* Plain Text Extension */
         str = "Plain Text";
@@ -582,7 +582,7 @@ doExtension(FILE *         const ifP,
 
 struct GetCodeState {
     unsigned char buf[280];
-        /* This is the buffer through which we read the data from the 
+        /* This is the buffer through which we read the data from the
            stream.  We must buffer it because we have to read whole data
            blocks at a time, but our client wants one code at a time.
            The buffer typically contains the contents of one data block
@@ -605,7 +605,7 @@ struct GetCodeState {
 
 
 static void
-getAnotherBlock(FILE *                const ifP, 
+getAnotherBlock(FILE *                const ifP,
                 struct GetCodeState * const gsP,
                 const char **         const errorP) {
 
@@ -623,7 +623,7 @@ getAnotherBlock(FILE *                const ifP,
 
     gsP->curbit -= (gsP->bufCount-2)*8;
     gsP->bufCount = 2;
-        
+
     /* Add the next block to the buffer */
     getDataBlock(ifP, &gsP->buf[gsP->bufCount], &eof, &count, errorP);
     if (*errorP)
@@ -640,7 +640,7 @@ getAnotherBlock(FILE *                const ifP,
             assumedCount = count;
 
         gsP->streamExhausted = (assumedCount == 0);
-        
+
         gsP->bufCount += assumedCount;
     }
 }
@@ -651,7 +651,7 @@ static struct GetCodeState getCodeState;
 
 static void
 getCode_init(struct GetCodeState * const getCodeStateP) {
-    
+
     /* Fake a previous data block */
     getCodeStateP->buf[0] = 0;
     getCodeStateP->buf[1] = 0;
@@ -675,7 +675,7 @@ bitsOfLeBuffer(const unsigned char * const buf,
    first byte of buf[].
 
    We return the string as an integer such that its pure binary encoding with
-   the bits numbered Intel-style is the string.  E.g. the string 0,1,1 
+   the bits numbered Intel-style is the string.  E.g. the string 0,1,1
    yields six.
 -----------------------------------------------------------------------------*/
     uint32_t codeBlock;
@@ -694,8 +694,8 @@ bitsOfLeBuffer(const unsigned char * const buf,
             (buf[start/8+0] <<  0) |
             (buf[start/8+1] <<  8) |
             (buf[start/8+2] << 16);
-            
-    return (unsigned int) 
+
+    return (unsigned int)
         (codeBlock >> (start % 8)) & ((1 << len) - 1);
 }
 
@@ -703,7 +703,7 @@ bitsOfLeBuffer(const unsigned char * const buf,
 
 static void
 getCode_get(struct GetCodeState * const gsP,
-            FILE *                const ifP, 
+            FILE *                const ifP,
             int                   const codeSize,
             bool *                const eofP,
             unsigned int *        const codeP,
@@ -727,7 +727,7 @@ getCode_get(struct GetCodeState * const gsP,
     *errorP = NULL;
 
     while (gsP->curbit + codeSize > gsP->bufCount * 8 &&
-           !gsP->streamExhausted && !*errorP) 
+           !gsP->streamExhausted && !*errorP)
         /* Not enough left in buffer to satisfy request.  Get the next
            data block into the buffer.
 
@@ -775,7 +775,7 @@ struct Stack {
 
 
 
-static void 
+static void
 initStack(struct Stack * const stackP, unsigned int const size) {
 
     MALLOCARRAY(stackP->stack, size);
@@ -810,7 +810,7 @@ popStack(struct Stack * const stackP) {
 
     if (stackP->sp <= stackP->stack)
         pm_error("stack underflow");
-    
+
     return *(--stackP->sp);
 }
 
@@ -822,7 +822,7 @@ termStack(struct Stack * const stackP) {
     stackP->stack = NULL;
 }
 
-    
+
 
 /*----------------------------------------------------------------------------
    Some notes on LZW.
@@ -839,28 +839,35 @@ termStack(struct Stack * const stackP) {
 
    The true data elements are dataWidth bits wide, so the maximum
    value of a true data element is 2**dataWidth-1.  We call that
-   max_dataVal.  The first byte in the stream tells you what dataWidth
+   maxDataVal.  The first byte in the stream tells you what dataWidth
    is.
 
-   LZW codes 0 - max_dataVal are direct codes.  Each one represents
+   LZW codes 0 - maxDataVal are direct codes.  Each one represents
    the true data element whose value is that of the LZW code itself.
    No decompression is required.
 
-   max_dataVal + 1 and up are compression codes.  They encode
+   maxDataVal + 1 and up are compression codes.  They encode
    true data elements:
 
-   max_dataVal + 1 is the clear code.
-         
-   max_dataVal + 2 is the end code.
+   maxDataVal + 1 is the clear code.
+
+   maxDataVal + 2 is the end code.
 
-   max_dataVal + 3 and up are string codes.  Each string code 
+   maxDataVal + 3 and up are string codes.  Each string code
    represents a string of true data elements.  The translation from a
    string code to the string of true data elements varies as the stream
    progresses.  In the beginning and after every clear code, the
    translation table is empty, so no string codes are valid.  As the
-   stream progresses, the table gets filled and more string codes 
+   stream progresses, the table gets filled and more string codes
    become valid.
 
+   At the beginning of the stream, string codes are represented by
+   dataWidth + 1 bits.  When enough codes have been defined to use up that
+   space, they start being represented by dataWidth + 2 bits, and so on.
+
+   What we call 'dataWidth', others call the "minimum code size," which is a
+   misnomer, because the minimum code size in a stream must be at least one
+   more than 'dataWidth', to accomodate the clear and end codes.
 -----------------------------------------------------------------------------*/
 
 static int const maxLzwCodeCt = (1<<MAX_LZW_BITS);
@@ -890,7 +897,7 @@ typedef struct {
            the stream.
         */
     unsigned int maxCodeCt;
-        /* The maximum number of LZW codes that can be represented with the 
+        /* The maximum number of LZW codes that can be represented with the
            current code size.  (1 << codeSize)
         */
     unsigned int nextTableSlot;
@@ -903,6 +910,9 @@ typedef struct {
     /* The following are constant for the life of the decompressor */
     FILE * ifP;
     unsigned int initCodeSize;
+        /* The code size, in bits, at the start of the stream and immediately
+           after a Clear code.
+        */
     unsigned int cmapSize;
     unsigned int maxDataVal;
     unsigned int clearCode;
@@ -910,11 +920,11 @@ typedef struct {
     bool haveTransColor;
     unsigned int transparentIndex;
         /* meaningful only when 'haveTransColor' is true */
-    bool tolerateBadInput; 
+    bool tolerateBadInput;
         /* We are to tolerate bad input data as best we can, rather than
            just declaring an error and bailing out.
         */
-    CodeTableEntry table[(1 << MAX_LZW_BITS)];   /* LZW code table */  
+    CodeTableEntry table[(1 << MAX_LZW_BITS)];   /* LZW code table */
         /* This contains the strings of expansions of LZW string codes, in
            linked list form.  table[N] gives the first data element for the
            string with string code N and the LZW code for the rest of the
@@ -928,7 +938,7 @@ typedef struct {
 static void
 resetDecompressor(Decompressor * const decompP) {
 
-    decompP->codeSize      = decompP->initCodeSize+1;
+    decompP->codeSize      = decompP->initCodeSize;
     decompP->maxCodeCt     = 1 << decompP->codeSize;
     decompP->nextTableSlot = decompP->maxDataVal + 3;
     decompP->fresh         = true;
@@ -962,23 +972,21 @@ validateTransparentIndex(unsigned int const transparentIndex,
 
 
 static void
-lzwInit(Decompressor * const decompP, 
+lzwInit(Decompressor * const decompP,
         FILE *         const ifP,
-        int            const initCodeSize,
+        int            const dataWidth,
         unsigned int   const cmapSize,
         bool           const haveTransColor,
         unsigned int   const transparentIndex,
         bool           const tolerateBadInput) {
 
-    unsigned int const maxDataVal = (1 << initCodeSize) - 1;
-    
+    unsigned int const maxDataVal = (1 << dataWidth) - 1;
+
     if (verbose)
-        pm_message("Image says the initial compression code size is "
-                   "%d bits", 
-                   initCodeSize);
-    
+        pm_message("Image says the data width is %u bits", dataWidth);
+
     decompP->ifP              = ifP;
-    decompP->initCodeSize     = initCodeSize;
+    decompP->initCodeSize     = dataWidth + 1;
     decompP->cmapSize         = cmapSize;
     decompP->tolerateBadInput = tolerateBadInput;
     decompP->maxDataVal       = maxDataVal;
@@ -988,9 +996,9 @@ lzwInit(Decompressor * const decompP,
     if (verbose)
         pm_message("Initial code size is %u bits; clear code = 0x%03x, "
                    "end code = 0x%03x",
-                   decompP->initCodeSize, 
+                   decompP->initCodeSize,
                    decompP->clearCode, decompP->endCode);
-    
+
     /* The entries in the translation table for true data codes are
        constant throughout the image.  For PBM output we make an
        adjustment later.  Once set entries never change.
@@ -1012,12 +1020,12 @@ lzwInit(Decompressor * const decompP,
     resetDecompressor(decompP);
 
     getCode_init(&getCodeState);
-    
+
     decompP->fresh = true;
-    
+
     initStack(&decompP->stack, maxLzwCodeCt);
 
-    assert(decompP->initCodeSize < sizeof(decompP->maxDataVal) * 8);
+    assert(decompP->initCodeSize < sizeof(decompP->maxDataVal) * 8 + 1);
 }
 
 
@@ -1087,7 +1095,7 @@ addLzwStringCode(Decompressor * const decompP) {
                Future codes in the stream will have codes one bit longer.
                But there's an exception if we're already at the LZW
                maximum, in which case the codes will simply continue
-               the same size.
+               the same size and no new ones will be defined.
             */
             if (decompP->codeSize < MAX_LZW_BITS) {
                 ++decompP->codeSize;
@@ -1134,7 +1142,7 @@ expandCodeOntoStack(Decompressor * const decompP,
             pm_asprintf(&gifError, "Invalid color code %u. "
                         "Valid color values are 0 - %u",
                         incode, decompP->cmapSize - 1);
-    } else if (incode < decompP->nextTableSlot)  
+    } else if (incode < decompP->nextTableSlot)
         /* LZW string, defined */
         code = incode;
     else if (incode == decompP->nextTableSlot) {
@@ -1146,7 +1154,7 @@ expandCodeOntoStack(Decompressor * const decompP,
         else {
             if (wantLzwCodes && verbose)
                 pm_message ("LZW code valid, but not in decoder table");
-            
+
             pushStack(&decompP->stack, decompP->firstcode);
             code = decompP->prevcode;
         }
@@ -1208,7 +1216,7 @@ lzwReadByteFresh(struct GetCodeState * const getCodeStateP,
             if (!zeroDataBlock)
                 readThroughEod(decompP->ifP);
             *endOfImageP = true;
-        } else if (code >= decompP->cmapSize) { 
+        } else if (code >= decompP->cmapSize) {
             pm_asprintf(errorP, "Error in GIF image: invalid color code %u. "
                         "Valid color values are: 0 - %u",
                         code, decompP->cmapSize-1);
@@ -1307,7 +1315,7 @@ bumpRowInterlace(unsigned int   const rows,
        MULT4PLUS2: Rows 2, 6, 10, 14, etc.
        MULT2PLUS1: Rows 1, 3, 5, 7, 9, etc.
     */
-    
+
     switch (*passP) {
     case MULT8PLUS0:
         *rowP += 8;
@@ -1352,7 +1360,7 @@ bumpRowInterlace(unsigned int   const rows,
 static void
 renderRow(unsigned char *    const cmapIndexRow,
           unsigned int       const cols,
-          GifColorMap        const cmap, 
+          GifColorMap        const cmap,
           bool               const haveTransColor,
           unsigned int       const transparentIndex,
           FILE *             const imageOutfile,
@@ -1370,7 +1378,7 @@ renderRow(unsigned char *    const cmapIndexRow,
   'haveTransColor' is false, render all white (i.e. the row is
   opaque).  'alphabits' is otherwise just a one-row buffer for us to use
   in rendering the alpha row.
-  
+
   imageOutfile is NULL if user wants only the alpha file.
 ----------------------------------------------------------------------------*/
     if (alphabits) {
@@ -1387,7 +1395,7 @@ renderRow(unsigned char *    const cmapIndexRow,
     if (imageOutfile) {
         if (useFastPbmRender && format == PBM_FORMAT && !haveTransColor) {
 
-            bit * const bitrow = cmapIndexRow; 
+            bit * const bitrow = cmapIndexRow;
 
             pbm_writepbmrow(imageOutfile, bitrow, cols, false);
         } else {
@@ -1441,7 +1449,7 @@ pnmFormat(bool const hasGray,
 -----------------------------------------------------------------------------*/
     int format;
     const char * formatName;
-           
+
     if (hasColor) {
         format = PPM_FORMAT;
         formatName = "PPM";
@@ -1452,9 +1460,9 @@ pnmFormat(bool const hasGray,
         format = PBM_FORMAT;
         formatName = "PBM";
     }
-    if (verbose) 
+    if (verbose)
         pm_message("writing a %s file", formatName);
- 
+
     return format;
 }
 
@@ -1481,7 +1489,7 @@ makePnmRow(Decompressor * const decompP,
 
         if (fillingWithZero)
             colorIndex = 0;
-        else { 
+        else {
             const char *  readError;
             unsigned char readColorIndex;
             bool          endOfImage;
@@ -1513,7 +1521,7 @@ static void
 convertRaster(Decompressor * const decompP,
               unsigned int   const cols,
               unsigned int   const rows,
-              GifColorMap    const cmap, 
+              GifColorMap    const cmap,
               bool           const interlace,
               FILE *         const imageOutFileP,
               FILE *         const alphaFileP,
@@ -1551,7 +1559,7 @@ convertRaster(Decompressor * const decompP,
     if (alphaFileP)
         pbm_writepbminit(alphaFileP, cols, rows, false);
 
-    xelrow = pnm_allocrow(cols);  
+    xelrow = pnm_allocrow(cols);
     if (!xelrow)
         pm_error("couldn't alloc space for image" );
 
@@ -1595,10 +1603,10 @@ convertRaster(Decompressor * const decompP,
                       decompP->haveTransColor, decompP->transparentIndex,
                       imageOutFileP, format, xelrow, alphaFileP, alphabits);
     }
-    /* All rows decompressed (and rendered and output if non-interlaced) */  
+    /* All rows decompressed (and rendered and output if non-interlaced) */
     if (interlace) {
         unsigned int row;
-        for (row = 0; row < rows; ++row) 
+        for (row = 0; row < rows; ++row)
             renderRow(cmapIndexArray[row], cols, cmap,
                       decompP->haveTransColor, decompP->transparentIndex,
                       imageOutFileP, format, xelrow, alphaFileP, alphabits);
@@ -1646,13 +1654,13 @@ skipExtraneousData(Decompressor * const decompP) {
 
 static void
 issueTransparencyMessage(bool         const haveTransColor,
-                         unsigned int const transparentIndex, 
+                         unsigned int const transparentIndex,
                          GifColorMap  const cmap) {
 /*----------------------------------------------------------------------------
    If user wants verbose output, tell him whether there is a transparent
    background color ('haveTransColor') and if so what it is
    ('transparentIndex').
-   
+
    Some GIFs put transparentIndex outside the color map.  Allow this only
    with "-repair", checked in lzwInit().  Here we issue a warning and report
    the substitute color.
@@ -1687,10 +1695,10 @@ issueTransparencyMessage(bool         const haveTransColor,
 
 
 static void
-readImageData(FILE *       const ifP, 
+readImageData(FILE *       const ifP,
               unsigned int const cols,
               unsigned int const rows,
-              GifColorMap  const cmap, 
+              GifColorMap  const cmap,
               bool         const interlace,
               bool         const haveTransColor,
               unsigned int const transparentIndex,
@@ -1700,22 +1708,25 @@ readImageData(FILE *       const ifP,
               bool         const hasColor,
               bool         const tolerateBadInput) {
 
-    unsigned char lzwMinCodeSize;      
+    unsigned char lzwDataWidth;
     Decompressor decomp;
     const char * error;
 
-    readFile(ifP, &lzwMinCodeSize, sizeof(lzwMinCodeSize), &error);
+    readFile(ifP, &lzwDataWidth, sizeof(lzwDataWidth), &error);
     if (error)
         pm_error("Can't read GIF stream "
                  "right after an image separator; no "
                  "image data follows.  %s", error);
 
-    if (lzwMinCodeSize > MAX_LZW_BITS)
-        pm_error("Invalid minimum code size value in image data: %u.  "
-                 "Maximum allowable code size in GIF is %u", 
-                 lzwMinCodeSize, MAX_LZW_BITS);
+    if (lzwDataWidth+1 > MAX_LZW_BITS)
+        pm_error("Invalid data width (bits for a true data item) "
+                 "in image data: %u.  "
+                 "Maximum allowable code size in GIF is %u, "
+                 "and a code has to be wide enough to accomodate both "
+                 "all possible data values and two control codes",
+                 lzwDataWidth, MAX_LZW_BITS);
 
-    lzwInit(&decomp, ifP, lzwMinCodeSize, cmap.size,
+    lzwInit(&decomp, ifP, lzwDataWidth, cmap.size,
             haveTransColor, transparentIndex, tolerateBadInput);
 
     issueTransparencyMessage(haveTransColor, transparentIndex, cmap);
@@ -1776,24 +1787,24 @@ readGifHeader(FILE *             const gifFileP,
     readFile(gifFileP, buf, 6, &error);
     if (error)
         pm_error("Error reading magic number.  %s", error);
-    
+
     if (!strneq((char *)buf, "GIF", 3))
         pm_error("File does not contain a GIF stream.  It does not start "
                  "with 'GIF'.");
-    
+
     strncpy(version, (char *)buf + 3, 3);
     version[3] = '\0';
-    
+
     if (verbose)
         pm_message("GIF format version is '%s'", version);
-    
+
     if ((!streq(version, "87a")) && (!streq(version, "89a")))
         pm_error("Bad version number, not '87a' or '89a'" );
-    
+
     readFile(gifFileP, buf, 7, &error);
     if (error)
         pm_error("Failed to read screen descriptor.  %s", error);
-    
+
     gifScreenP->width           = LM_to_uint(buf[0],buf[1]);
     gifScreenP->height          = LM_to_uint(buf[2],buf[3]);
     cmapSize                    = 1 << ((buf[4] & 0x07) + 1);
@@ -1804,25 +1815,25 @@ readGifHeader(FILE *             const gifFileP,
     if (verbose) {
         pm_message("GIF Width = %u GIF Height = %u "
                    "Pixel aspect ratio = %u (%f:1)",
-                   gifScreenP->width, gifScreenP->height, 
-                   gifScreenP->aspectRatio, 
-                   gifScreenP->aspectRatio == 0 ? 
+                   gifScreenP->width, gifScreenP->height,
+                   gifScreenP->aspectRatio,
+                   gifScreenP->aspectRatio == 0 ?
                    1 : (gifScreenP->aspectRatio + 15) / 64.0);
         pm_message("Global color count = %u   Color Resolution = %u",
                    cmapSize, gifScreenP->colorResolution);
-    }           
+    }
     if (buf[4] & GLOBALCOLORMAP) {
         gifScreenP->hasGlobalColorMap = true;
         readColorMap(gifFileP, cmapSize, &gifScreenP->colorMap,
                      &gifScreenP->hasGray, &gifScreenP->hasColor);
         if (verbose) {
-            pm_message("Global color map %s grays, %s colors", 
+            pm_message("Global color map %s grays, %s colors",
                        gifScreenP->hasGray ? "contains" : "doesn't contain",
                        gifScreenP->hasColor ? "contains" : "doesn't contain");
         }
     } else
         gifScreenP->hasGlobalColorMap = false;
-    
+
     if (gifScreenP->aspectRatio != 0 && gifScreenP->aspectRatio != 49)
         warnUserNotSquare(gifScreenP->aspectRatio);
 
@@ -1832,7 +1843,7 @@ readGifHeader(FILE *             const gifFileP,
 
 
 static void
-readExtensions(FILE*          const ifP, 
+readExtensions(FILE*          const ifP,
                struct Gif89 * const gif89P,
                bool *         const eodP,
                const char **  const errorP) {
@@ -1841,7 +1852,7 @@ readExtensions(FILE*          const ifP,
    positioned.  Read up through the image separator that begins the
    next image or GIF stream terminator.
 
-   If we encounter EOD (end of GIF stream) before we find an image 
+   If we encounter EOD (end of GIF stream) before we find an image
    separator, we return *eodP == true.  Else *eodP == false.
 
    If we hit end of file before an EOD marker, we fail.
@@ -1883,9 +1894,9 @@ readExtensions(FILE*          const ifP,
                 } else {
                     doExtension(ifP, functionCode, gif89P);
                 }
-            } else if (c == ',') 
+            } else if (c == ',')
                 imageStart = true;
-            else 
+            else
                 pm_message("Encountered invalid character 0x%02x while "
                            "seeking extension block, ignoring", (int)c);
         }
@@ -1901,7 +1912,7 @@ struct GifImageHeader {
 -----------------------------------------------------------------------------*/
     bool hasLocalColormap;
         /* The image has its own color map.  Its size is 'localColorMapSize' */
-        /* (If an image does not have its own color map, the image uses the 
+        /* (If an image does not have its own color map, the image uses the
            global color map for the GIF stream)
         */
     unsigned int localColorMapSize;
@@ -1930,7 +1941,7 @@ reportImageHeader(struct GifImageHeader const imageHeader) {
     if (imageHeader.lpos > 0 || imageHeader.tpos > 0)
         pm_message("  Image left position: %u top position: %u",
                    imageHeader.lpos, imageHeader.tpos);
-    
+
     if (imageHeader.hasLocalColormap)
         pm_message("  Uses local colormap of %u colors",
                    imageHeader.localColorMapSize);
@@ -1976,7 +1987,7 @@ validateWithinGlobalScreen(struct GifImageHeader const imageHeader,
                            struct GifScreen      const gifScreen) {
 
     unsigned long int const rpos = imageHeader.lpos + imageHeader.cols;
-    unsigned long int const bpos = imageHeader.tpos + imageHeader.rows; 
+    unsigned long int const bpos = imageHeader.tpos + imageHeader.rows;
 
     if (rpos > gifScreen.width)
         pm_error("Image right end (%lu) is outside global screen: %u x %u",
@@ -2005,10 +2016,10 @@ skipImageData(FILE * const ifP) {
 
 
 static void
-convertImage(FILE *           const ifP, 
-             bool             const skipIt, 
-             FILE *           const imageoutFileP, 
-             FILE *           const alphafileP, 
+convertImage(FILE *           const ifP,
+             bool             const skipIt,
+             FILE *           const imageoutFileP,
+             FILE *           const alphafileP,
              struct GifScreen const gifScreen,
              struct Gif89     const gif89,
              bool             const tolerateBadInput) {
@@ -2029,7 +2040,7 @@ convertImage(FILE *           const ifP,
     validateWithinGlobalScreen(imageHeader, gifScreen);
 
     if (imageHeader.hasLocalColormap) {
-        readColorMap(ifP, imageHeader.localColorMapSize, &localColorMap, 
+        readColorMap(ifP, imageHeader.localColorMapSize, &localColorMap,
                      &hasGray, &hasColor);
         currentColorMapP = &localColorMap;
     } else if (gifScreen.hasGlobalColorMap) {
@@ -2077,11 +2088,11 @@ disposeOfReadExtensionsError(const char * const error,
 
 
 static void
-convertImages(FILE *       const ifP, 
+convertImages(FILE *       const ifP,
               bool         const allImages,
-              unsigned int const requestedImageSeq, 
+              unsigned int const requestedImageSeq,
               bool         const drainStream,
-              FILE *       const imageOutFileP, 
+              FILE *       const imageOutFileP,
               FILE *       const alphaFileP,
               bool         const tolerateBadInput) {
 /*----------------------------------------------------------------------------
@@ -2089,9 +2100,9 @@ convertImages(FILE *       const ifP,
    it as PNM images to file 'imageOutFileP'.  If the images have transparency
    and 'alphafile' is non-NULL, write PGM alpha masks to file 'alphaFileP'.
 
-   'allImages' means Caller wants all the images in the stream.  
+   'allImages' means Caller wants all the images in the stream.
 
-   'requestedImageSeq' is meaningful only when 'allImages' is false.  It 
+   'requestedImageSeq' is meaningful only when 'allImages' is false.  It
    is the sequence number of the one image Caller wants from the stream,
    with the first image being 0.
 
@@ -2138,7 +2149,7 @@ convertImages(FILE *       const ifP,
             if (verbose)
                 pm_message("Reading Image Sequence %u", imageSeq);
 
-            convertImage(ifP, !allImages && (imageSeq != requestedImageSeq), 
+            convertImage(ifP, !allImages && (imageSeq != requestedImageSeq),
                          imageOutFileP, alphaFileP, gifScreen, gif89,
                          tolerateBadInput);
         }
@@ -2160,7 +2171,7 @@ main(int argc, const char **argv) {
     parseCommandLine(argc, argv, &cmdline);
     verbose = cmdline.verbose;
     showComment = cmdline.comments;
-   
+
     ifP = pm_openr(cmdline.inputFilespec);
 
     if (cmdline.alphaFileName == NULL)
@@ -2173,12 +2184,12 @@ main(int argc, const char **argv) {
     else
         imageOutFileP = stdout;
 
-    convertImages(ifP, cmdline.allImages, cmdline.imageNum, 
+    convertImages(ifP, cmdline.allImages, cmdline.imageNum,
                   !cmdline.quitearly, imageOutFileP, alphaFileP,
                   cmdline.repair);
 
     pm_close(ifP);
-    if (imageOutFileP != NULL) 
+    if (imageOutFileP != NULL)
         pm_close(imageOutFileP);
     if (alphaFileP != NULL)
         pm_close(alphaFileP);
@@ -2187,4 +2198,3 @@ main(int argc, const char **argv) {
 }
 
 
-
diff --git a/converter/other/pamtogif.c b/converter/other/pamtogif.c
index aabf7fc2..8d432da1 100644
--- a/converter/other/pamtogif.c
+++ b/converter/other/pamtogif.c
@@ -8,6 +8,7 @@
 
 #include <assert.h>
 #include <string.h>
+#include <stdbool.h>
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
@@ -23,7 +24,7 @@ static unsigned int const gifMaxval = 255;
 static bool verbose;
 
 
-typedef int stringCode;
+typedef unsigned int StringCode;
     /* A code to be place in the GIF raster.  It represents
        a string of one or more pixels.  You interpret this in the context
        of a current code size.  The lower half of the values representable
@@ -34,15 +35,11 @@ typedef int stringCode;
        strings.  The mapping between value and the sequence of pixels
        changes throughout the image.
 
-       A variable of this type sometimes has the value -1 instead of
-       a string code because of cheesy programming.
-
-       Ergo, this data structure must be signed and at least BITS bits
-       wide plus sign bit.
+       Ergo, this data structure must be at least BITS bits wide.
     */
 
 
-struct cmap {
+struct Cmap {
     /* This is the information for the GIF colormap (aka palette). */
 
     struct pam pam;
@@ -67,7 +64,7 @@ struct cmap {
         /* A hash table to translate color to GIF colormap index. */
 };
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -79,6 +76,7 @@ struct cmdlineInfo {
     const char *transparent;    /* -transparent option value.  NULL if none. */
     const char *comment;        /* -comment option value; NULL if none */
     unsigned int nolzw;         /* -nolzw option */
+    unsigned int noclear;       /* -noclear option */
     float aspect;               /* -aspect option value (the ratio).  */
     unsigned int verbose;
 };
@@ -100,7 +98,7 @@ pamAlphaPlane(struct pam * const pamP) {
         alphaPlane = 2;
     else
         alphaPlane = 0;
-    
+
     if (alphaPlane >= pamP->depth)
         pm_error("Tuple type is '%s', but depth (%u) is less than %u",
                  pamP->tuple_type, pamP->depth, alphaPlane + 1);
@@ -112,7 +110,7 @@ pamAlphaPlane(struct pam * const pamP) {
 
 static void
 parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Parse the program arguments (given by argc and argv) into a form
    the program can deal with more easily -- a cmdline_info structure.
@@ -131,30 +129,32 @@ parseCommandLine(int argc, char ** argv,
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "interlace",   OPT_FLAG,   
+    OPTENT3(0,   "interlace",   OPT_FLAG,
             NULL,                       &cmdlineP->interlace, 0);
-    OPTENT3(0,   "sort",        OPT_FLAG,   
+    OPTENT3(0,   "sort",        OPT_FLAG,
             NULL,                       &cmdlineP->sort, 0);
-    OPTENT3(0,   "nolzw",       OPT_FLAG,   
+    OPTENT3(0,   "nolzw",       OPT_FLAG,
             NULL,                       &cmdlineP->nolzw, 0);
-    OPTENT3(0,   "mapfile",     OPT_STRING, 
+    OPTENT3(0,   "noclear",     OPT_FLAG,
+            NULL,                       &cmdlineP->noclear, 0);
+    OPTENT3(0,   "mapfile",     OPT_STRING,
             &cmdlineP->mapfile,        NULL, 0);
-    OPTENT3(0,   "transparent", OPT_STRING, 
+    OPTENT3(0,   "transparent", OPT_STRING,
             &cmdlineP->transparent,    NULL, 0);
-    OPTENT3(0,   "comment",     OPT_STRING, 
+    OPTENT3(0,   "comment",     OPT_STRING,
             &cmdlineP->comment,        NULL, 0);
-    OPTENT3(0,   "alphacolor",  OPT_STRING, 
+    OPTENT3(0,   "alphacolor",  OPT_STRING,
             &cmdlineP->alphacolor,     NULL, 0);
-    OPTENT3(0,   "aspect",      OPT_FLOAT, 
+    OPTENT3(0,   "aspect",      OPT_FLOAT,
             &cmdlineP->aspect,         &aspectSpec, 0);
-    OPTENT3(0,   "verbose",     OPT_FLAG, 
+    OPTENT3(0,   "verbose",     OPT_FLAG,
             NULL,                      &cmdlineP->verbose, 0);
-    
+
     /* Set the defaults */
     cmdlineP->mapfile = NULL;
     cmdlineP->transparent = NULL;  /* no transparency */
     cmdlineP->comment = NULL;      /* no comment */
-    cmdlineP->alphacolor = "rgb:0/0/0";      
+    cmdlineP->alphacolor = "rgb:0/0/0";
         /* We could say "black" here, but then we depend on the color names
            database existing.
         */
@@ -166,15 +166,15 @@ parseCommandLine(int argc, char ** argv,
     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (argc-1 == 0) 
+    if (argc-1 == 0)
         cmdlineP->input_filespec = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
                  "specified %d", argc-1);
     else
         cmdlineP->input_filespec = argv[1];
-        
-    if (aspectSpec) { 
+
+    if (aspectSpec) {
         if (cmdlineP->aspect < 0.25  || cmdlineP->aspect > 4.21875)
             pm_error("Invalid -aspect value: %f.  "
                      "GIF allows only the range 0.25-4.0 .",
@@ -205,10 +205,10 @@ Putword(int const w, FILE * const fp) {
 static int
 closestColor(tuple         const color,
              struct pam *  const pamP,
-             struct cmap * const cmapP) {
+             struct Cmap * const cmapP) {
 /*----------------------------------------------------------------------------
    Return the colormap index of the color in the colormap *cmapP
-   that is closest to the color 'color', whose format is specified by 
+   that is closest to the color 'color', whose format is specified by
    *pamP.
 
    Also add 'color' to the colormap hash, with the colormap index we
@@ -217,7 +217,7 @@ closestColor(tuple         const color,
 -----------------------------------------------------------------------------*/
     unsigned int const nComp = pamP->depth >= 3 ? 3 : 1;
         /* Number of color components (not alpha) in 'color' */
-    
+
     unsigned int i;
     unsigned int imin, dmin;
     int fits;
@@ -234,8 +234,8 @@ closestColor(tuple         const color,
 
         if (distance < dmin) {
             dmin = distance;
-            imin = i; 
-        } 
+            imin = i;
+        }
     }
     pnm_addtotuplehash(pamP, cmapP->tuplehash, color, imin, &fits);
 
@@ -268,16 +268,16 @@ typedef struct {
         /* A bitbucket for rows we read in order to advance the file
            position.
         */
-} rowReader;
+} RowReader;
 
 
 
-static rowReader *
+static RowReader *
 rowReader_create(struct pam * const pamP,
                  pm_filepos   const rasterPos,
                  bool         const interlace) {
 
-    rowReader * rdrP;
+    RowReader * rdrP;
 
     MALLOCVAR_NOFAIL(rdrP);
 
@@ -298,7 +298,7 @@ rowReader_create(struct pam * const pamP,
 
 
 static void
-rowReader_destroy(rowReader * const rdrP) {
+rowReader_destroy(RowReader * const rdrP) {
 
     pnm_freepamrow(rdrP->discardBuffer);
     free(rdrP);
@@ -307,7 +307,7 @@ rowReader_destroy(rowReader * const rdrP) {
 
 
 static void
-rowReaderSkipRows(rowReader *  const rdrP,
+rowReaderSkipRows(RowReader *  const rdrP,
                   unsigned int const rowCount,
                   bool *       const eofP) {
 /*----------------------------------------------------------------------------
@@ -338,7 +338,7 @@ rowReaderSkipRows(rowReader *  const rdrP,
 
 
 static void
-rowReaderGotoNextInterlaceRow(rowReader * const rdrP) {
+rowReaderGotoNextInterlaceRow(RowReader * const rdrP) {
 /*----------------------------------------------------------------------------
   Position reader to the next row in the interlace pattern, assuming it
   is now positioned immediately after the current row.
@@ -351,7 +351,7 @@ rowReaderGotoNextInterlaceRow(rowReader * const rdrP) {
        MULT4PLUS2: Rows 2, 6, 10, 14, etc.
        MULT2PLUS1: Rows 1, 3, 5, 7, 9, etc.
     */
-    
+
     switch (rdrP->pass) {
     case MULT8PLUS0:
         rowReaderSkipRows(rdrP, 7, &endOfPass);
@@ -399,7 +399,7 @@ rowReaderGotoNextInterlaceRow(rowReader * const rdrP) {
 
 
 static void
-rowReaderGotoNextStraightRow(rowReader * const rdrP) {
+rowReaderGotoNextStraightRow(RowReader * const rdrP) {
 /*----------------------------------------------------------------------------
   Position reader to the next row in a straight, non-interlace
   pattern, assuming the file is now positioned immediately after the
@@ -415,7 +415,7 @@ rowReaderGotoNextStraightRow(rowReader * const rdrP) {
 
 
 static void
-rowReader_read(rowReader * const rdrP,
+rowReader_read(RowReader * const rdrP,
                tuple *     const tuplerow) {
 
     if (rdrP->eof)
@@ -424,7 +424,7 @@ rowReader_read(rowReader * const rdrP,
 
     pnm_readpamrow(&rdrP->pam, tuplerow);
     ++rdrP->nextRow;
-    
+
     if (rdrP->interlace)
         rowReaderGotoNextInterlaceRow(rdrP);
     else
@@ -437,8 +437,8 @@ static unsigned int
 gifPixel(struct pam *   const pamP,
          tuple          const tuple,
          unsigned int   const alphaPlane,
-         sample         const alphaThreshold, 
-         struct cmap *  const cmapP) {
+         sample         const alphaThreshold,
+         struct Cmap *  const cmapP) {
 /*----------------------------------------------------------------------------
    Return as *colorIndexP the colormap index of the tuple 'tuple',
    whose format is described by *pamP, using colormap *cmapP.
@@ -456,7 +456,7 @@ gifPixel(struct pam *   const pamP,
 
         pnm_lookuptuple(pamP, cmapP->tuplehash, tuple,
                         &found, &colorIndex);
-        
+
         if (!found)
             colorIndex = closestColor(tuple, pamP, cmapP);
     }
@@ -493,13 +493,13 @@ writeCommentExtension(FILE * const ofP,
     unsigned int const maxSegmentSize = 255;
 
     const char * segment;
-    
+
     fputc('!',  ofP);   /* Identifies an extension */
     fputc(0xfe, ofP);   /* Identifies a comment */
 
     /* Write it out in segments no longer than 255 characters */
-    for (segment = &comment[0]; 
-         segment < comment + strlen(comment); 
+    for (segment = &comment[0];
+         segment < comment + strlen(comment);
          segment += maxSegmentSize) {
 
         unsigned int const lengthThisSegment =
@@ -546,20 +546,30 @@ writeCommentExtension(FILE * const ofP,
  */
 
 
-static stringCode const maxCodeLimitLzw = (stringCode)1 << BITS;
-       /* One beyond the largest string code that can exist in GIF */ 
+static StringCode const maxCodeLimitLzw = (StringCode)1 << BITS;
+       /* One beyond the largest string code that can exist in GIF */
        /* Used only in assertions  */
 
 
-struct hashTableEntry {
-    stringCode fcode;   /* -1 means unused slot */
-    unsigned int ent;
-};    
+struct HashTableEntry {
+    /* This is an entry in the string table, which is a hash table.  It says
+       that the string code 'combinedString' represents the string which is
+       the single pixel 'additionalPixel' appended to 'baseString', where
+       'baseString' may represent a multi-pixel string.
+    */
+    bool present;
+        /* There is an entry here.  Following members are meaningless if
+           not.
+        */
+    StringCode baseString;
+    StringCode additionalPixel;
+    StringCode combinedString;
+};
 
 
 
 /***************************************************************************
-*                          BYTE OUTPUTTER                 
+*                          BYTE OUTPUTTER
 ***************************************************************************/
 
 typedef struct {
@@ -568,14 +578,14 @@ typedef struct {
         /* Number of bytes so far in the current data block */
     unsigned char buffer[256];
         /* The current data block, under construction */
-} byteBuffer;
+} ByteBuffer;
 
 
 
-static byteBuffer *
+static ByteBuffer *
 byteBuffer_create(FILE * const fileP) {
 
-    byteBuffer * byteBufferP;
+    ByteBuffer * byteBufferP;
 
     MALLOCVAR_NOFAIL(byteBufferP);
 
@@ -588,7 +598,7 @@ byteBuffer_create(FILE * const fileP) {
 
 
 static void
-byteBuffer_destroy(byteBuffer * const byteBufferP) {
+byteBuffer_destroy(ByteBuffer * const byteBufferP) {
 
     free(byteBufferP);
 }
@@ -596,9 +606,9 @@ byteBuffer_destroy(byteBuffer * const byteBufferP) {
 
 
 static void
-byteBuffer_flush(byteBuffer * const byteBufferP) {
+byteBuffer_flush(ByteBuffer * const byteBufferP) {
 /*----------------------------------------------------------------------------
-   Write the current data block to the output file, then reset the current 
+   Write the current data block to the output file, then reset the current
    data block to empty.
 -----------------------------------------------------------------------------*/
     if (byteBufferP->count > 0 ) {
@@ -613,10 +623,10 @@ byteBuffer_flush(byteBuffer * const byteBufferP) {
 
 
 static void
-byteBuffer_flushFile(byteBuffer * const byteBufferP) {
-    
+byteBuffer_flushFile(ByteBuffer * const byteBufferP) {
+
     fflush(byteBufferP->fileP);
-    
+
     if (ferror(byteBufferP->fileP))
         pm_error("error writing output file");
 }
@@ -624,7 +634,7 @@ byteBuffer_flushFile(byteBuffer * const byteBufferP) {
 
 
 static void
-byteBuffer_out(byteBuffer *  const byteBufferP,
+byteBuffer_out(ByteBuffer *  const byteBufferP,
                unsigned char const c) {
 /*----------------------------------------------------------------------------
   Add a byte to the end of the current data block, and if it is now 255
@@ -638,39 +648,40 @@ byteBuffer_out(byteBuffer *  const byteBufferP,
 
 
 /***************************************************************************
-*                          GIF CODE OUTPUTTER                 
+*                          GIF CODE OUTPUTTER
 ***************************************************************************/
 
 typedef struct {
-    byteBuffer * byteBufferP;
+    ByteBuffer * byteBufferP;
     unsigned int initBits;
     unsigned int nBits;
         /* Number of bits to put in output for each code */
-    stringCode maxCode;                  /* maximum code, given n_bits */
-    stringCode maxCodeLimit;
+    StringCode maxCode;                  /* maximum code, given n_bits */
+    StringCode maxCodeLimit;
         /* LZW: One beyond the largest string code that can exist in GIF.
            Uncompressed: a ceiling to prevent code size from ratcheting up.
            In either case, output code never reaches this value.
-        */  
+        */
     unsigned long curAccum;
-    int curBits;
-    unsigned int codeCount;
-        /* Number of codes that have been output to this buffer (doesn't
-           matter if they have gone out the other side yet or not) since
-           the last flush (or ever, if no last flush).  The main use of this
-           is debugging -- when something fails, you can see in a debugger
-           where in the image it was, then set a trap for there.
+    unsigned int curBits;
+    unsigned int stringCount;
+        /* Number of strings that have been output to this buffer (by writing
+           a string code) since the last flush (or ever, if no last flush).
+           Note that this counts only strings that go into the buffer; it
+           doesn't matter if they have gone out the other side yet.  The main
+           use of this is debugging -- when something fails, you can see in a
+           debugger where in the image it was, then set a trap for there.
         */
-} codeBuffer;
+} CodeBuffer;
 
 
 
-static codeBuffer *
+static CodeBuffer *
 codeBuffer_create(FILE *       const ofP,
                   unsigned int const initBits,
                   bool         const lzw) {
 
-    codeBuffer * codeBufferP;
+    CodeBuffer * codeBufferP;
 
     MALLOCVAR_NOFAIL(codeBufferP);
 
@@ -678,11 +689,11 @@ codeBuffer_create(FILE *       const ofP,
     codeBufferP->nBits       = codeBufferP->initBits;
     codeBufferP->maxCode     = (1 << codeBufferP->nBits) - 1;
     codeBufferP->maxCodeLimit = lzw ?
-        (stringCode)1 << BITS : (stringCode) (1 << codeBufferP->nBits) - 1; 
+        (StringCode)1 << BITS : (StringCode) (1 << codeBufferP->nBits) - 1;
     codeBufferP->byteBufferP = byteBuffer_create(ofP);
     codeBufferP->curAccum    = 0;
     codeBufferP->curBits     = 0;
-    codeBufferP->codeCount   = 0;
+    codeBufferP->stringCount = 0;
 
     return codeBufferP;
 }
@@ -690,7 +701,7 @@ codeBuffer_create(FILE *       const ofP,
 
 
 static void
-codeBuffer_destroy(codeBuffer * const codeBufferP) {
+codeBuffer_destroy(CodeBuffer * const codeBufferP) {
 
     byteBuffer_destroy(codeBufferP->byteBufferP);
 
@@ -700,7 +711,7 @@ codeBuffer_destroy(codeBuffer * const codeBufferP) {
 
 
 static void
-codeBuffer_resetCodeSize(codeBuffer * const codeBufferP) {
+codeBuffer_resetCodeSize(CodeBuffer * const codeBufferP) {
 
     codeBufferP->nBits = codeBufferP->initBits;
 
@@ -712,7 +723,7 @@ codeBuffer_resetCodeSize(codeBuffer * const codeBufferP) {
 
 
 static void
-codeBuffer_increaseCodeSize(codeBuffer * const codeBufferP) {
+codeBuffer_increaseCodeSize(CodeBuffer * const codeBufferP) {
 
     ++codeBufferP->nBits;
 
@@ -721,18 +732,16 @@ codeBuffer_increaseCodeSize(codeBuffer * const codeBufferP) {
     codeBufferP->maxCode = (1 << codeBufferP->nBits) - 1;
 }
 
+
+
 static void
-codeBuffer_output(codeBuffer * const codeBufferP,
-                  stringCode   const code) {
+codeBuffer_output(CodeBuffer * const codeBufferP,
+                  StringCode   const code) {
 /*----------------------------------------------------------------------------
    Output one GIF code to the file, through the code buffer.
 
    The code is represented as N bits in the file -- the lower
    N bits of 'code'.  N is a the current code size of *codeBufferP.
-   
-   Id 'code' is the maximum possible code for the current code size
-   for *codeBufferP, increase that code size (unless it's already 
-   maxed out).
 -----------------------------------------------------------------------------*/
     assert (code <= codeBufferP->maxCode);
 
@@ -752,13 +761,13 @@ codeBuffer_output(codeBuffer * const codeBufferP,
         codeBufferP->curBits -= 8;
     }
 
-    ++codeBufferP->codeCount;
+    ++codeBufferP->stringCount;
 }
 
 
 
 static void
-codeBuffer_flush(codeBuffer * const codeBufferP) {
+codeBuffer_flush(CodeBuffer * const codeBufferP) {
 
     /* Output the possible partial byte in the buffer */
 
@@ -768,19 +777,19 @@ codeBuffer_flush(codeBuffer * const codeBufferP) {
         codeBufferP->curBits = 0;
     }
     byteBuffer_flush(codeBufferP->byteBufferP);
-    
+
     byteBuffer_flushFile(codeBufferP->byteBufferP);
 
     if (verbose)
         pm_message("%u strings of pixels written to file",
-                   codeBufferP->codeCount);
-    codeBufferP->codeCount = 0;
+                   codeBufferP->stringCount);
+    codeBufferP->stringCount = 0;
 }
 
 
 
 typedef struct {
-    codeBuffer * codeBufferP;
+    CodeBuffer * codeBufferP;
         /* The place to which we write our string codes.
 
            Constant.
@@ -791,12 +800,16 @@ typedef struct {
            proper data, but always using one code per pixel, and therefore
            not effecting any compression and not using the LZW patent.
         */
+    bool noclear;
+        /* Never put a clear code in the output.  Ergo don't recompute the
+           string table from current input.  When the string table fills up,
+           continue using that table for the rest of the image.
+        */
     unsigned int hsize;
         /* The number of slots in the hash table.  This variable to
            enhance overall performance by reducing memory use when
-           encoding smaller gifs. 
+           encoding smaller gifs.
          */
-        
     unsigned int hshift;
         /* This is how many bits we shift left a string code in forming the
            primary hash of the concatenation of that string with another.
@@ -810,37 +823,32 @@ typedef struct {
        represents a string of pixels that is defined by the preceding
        stream.
     */
-    stringCode clearCode;
+    StringCode clearCode;
         /* The code in an LZW stream that means to clear the string
            dictionary and start fresh.
 
            Constant.
         */
-    stringCode eofCode;
+    StringCode eofCode;
         /* The code in an LZW stream that means there's no more coming
 
            Constant.
         */
-    stringCode initCodeLimit;
+    StringCode initCodeLimit;
         /* The value of 'codeLimit' at the start of a block.
 
            Constant.
         */
-
-    stringCode codeLimit;
+    StringCode codeLimit;
         /* One beyond the maximum code possible with the current code
            size.
         */
-
-    struct hashTableEntry * hashTable;
-    stringCode nextUnusedCode;
-        /* Numerically next code available to assign to a a multi-pixel
-           string.  Note that codes for multi-pixel strings are in the
-           upper half of the range of codes, always greater than
-           'clearCode'.
+    struct HashTableEntry * hashTable;
+    StringCode nextCodeToDefine;
+        /* The next string code the GIF protocol will define.  It will do this
+           the next time we emit a string code.
         */
-
-    stringCode stringSoFar;
+    StringCode stringSoFar;
         /* The code for the string we have built so far.  This code indicates
            one or more pixels that we have encoded but not yet output
            because we're hoping to match an even longer string.
@@ -853,16 +861,20 @@ typedef struct {
         /* We are in the middle of building a string; 'stringSoFar' describes
            the pixels in it so far.  The only time this is false is at the
            very beginning of the stream.
- 
-           Ignored in the non-lzw case. 
+
+           Ignored in the non-lzw case.
+        */
+    bool reportedNoclear;
+        /* We have reported to Standard Error that the string table filled up
+           and we elected not to clear it.
         */
-} lzwCompressor;
+} LzwCompressor;
 
 
 
 
 static unsigned int
-nSignificantBits( unsigned int const arg ){
+nSignificantBits(unsigned int const arg){
 
 #if HAVE_GCC_BITCOUNT
 
@@ -880,37 +892,39 @@ nSignificantBits( unsigned int const arg ){
 
 
 
-static lzwCompressor *
+static LzwCompressor *
 lzw_create(FILE *       const ofP,
            unsigned int const initBits,
            bool         const lzw,
+           bool         const noclear,
            unsigned int const pixelCount) {
 
     unsigned int const hsizeTable[] = {257, 521, 1031, 2053, 4099, 5003};
     /* If the image has 4096 or fewer pixels we use prime numbers slightly
        above powers of two between 8 and 12.  In this case the hash table
        never fills up; clear code is never emitted.
-    
+
        Above that we use a table with 4096 slots plus 20% extra.
        When this is not enough the clear code is emitted.
        Because of the extra 20% the table itself never fills up.
-       
+
        lzw.hsize and lzw.hshift stay constant through the image.
 
        Variable hsize is a performance enhancement based on the fact that
        the encoder never needs more codes than the number of pixels in
        the image.  Typically, the ratio of pixels to codes is around
        10:1 to 20:1.
-   
+
        Logic works with fixed values lzw.hsize=5003 and t=13.
     */
 
-    lzwCompressor * lzwP;
-       
+    LzwCompressor * lzwP;
+
     MALLOCVAR_NOFAIL(lzwP);
 
     /* Constants */
-    lzwP->lzw = lzw;
+    lzwP->lzw     = lzw;
+    lzwP->noclear = noclear;
 
     lzwP->clearCode     = 1 << (initBits - 1);
     lzwP->eofCode       = lzwP->clearCode + 1;
@@ -920,18 +934,18 @@ lzw_create(FILE *       const ofP,
         unsigned int const t =
             MIN(13, MAX(8, nSignificantBits(pixelCount +lzwP->eofCode - 2)));
             /* Index into hsizeTable */
-    
+
         lzwP->hsize = hsizeTable[t-8];
 
         lzwP->hshift = (t == 13 ? 12 : t) - nSignificantBits(MAXCMAPSIZE-1);
 
         MALLOCARRAY(lzwP->hashTable, lzwP->hsize);
-        
+
         if (lzwP->hashTable == NULL)
             pm_error("Couldn't get memory for %u-entry hash table.",
                      lzwP->hsize);
     } else {
-        /* No LZW compression.  We don't need a stringcode hash table */  
+        /* No LZW compression.  We don't need a stringcode hash table */
         lzwP->hashTable = NULL;
         lzwP->hsize     = 0;
     }
@@ -940,13 +954,15 @@ lzw_create(FILE *       const ofP,
 
     lzwP->codeBufferP = codeBuffer_create(ofP, initBits, lzw);
 
+    lzwP->reportedNoclear = false;
+
     return lzwP;
 }
 
 
 
 static void
-lzw_destroy(lzwCompressor * const lzwP) {
+lzw_destroy(LzwCompressor * const lzwP) {
 
     codeBuffer_destroy(lzwP->codeBufferP);
 
@@ -958,24 +974,38 @@ lzw_destroy(lzwCompressor * const lzwP) {
 
 
 static void
-lzwHashClear(lzwCompressor * const lzwP) {
+lzwHashClear(LzwCompressor * const lzwP) {
 
     /* Empty the code table */
 
     unsigned int i;
 
     for (i = 0; i < lzwP->hsize; ++i)
-        lzwP->hashTable[i].fcode = -1;
+        lzwP->hashTable[i].present = false;
+
+    lzwP->nextCodeToDefine = lzwP->clearCode + 2;
+}
+
+
 
-    lzwP->nextUnusedCode = lzwP->clearCode + 2;
+static void
+lzw_reportNoclear(LzwCompressor * const lzwP) {
+
+    if (verbose && !lzwP->reportedNoclear) {
+        pm_message("String table filled up.  Not starting a new one "
+                   "because of noclear mode");
+        lzwP->reportedNoclear = true;
+    }
 }
 
 
 
 static void
-lzw_clearBlock(lzwCompressor * const lzwP) {
+lzw_clearBlock(LzwCompressor * const lzwP) {
 /*----------------------------------------------------------------------------
-  
+  Insert a string table clear in the stream.  Clear our table and set it up to
+  start building again, and emit the code to tell the decoder we're doing it
+  so he can do the same.
 -----------------------------------------------------------------------------*/
     lzwHashClear(lzwP);
 
@@ -989,8 +1019,8 @@ lzw_clearBlock(lzwCompressor * const lzwP) {
 
 
 static void
-lzwAdjustCodeSize(lzwCompressor * const lzwP,
-                  stringCode      const newCode) {
+lzwAdjustCodeSize(LzwCompressor * const lzwP,
+                  StringCode      const newCode) {
 /*----------------------------------------------------------------------------
    Assuming we just defined code 'newCode', increase the code size as
    required so that this code fits.
@@ -1011,64 +1041,67 @@ lzwAdjustCodeSize(lzwCompressor * const lzwP,
 
 
 static void
-lzwOutputCurrentString(lzwCompressor * const lzwP) {
+lzwOutputCurrentString(LzwCompressor * const lzwP) {
 /*----------------------------------------------------------------------------
    Put a code for the currently built-up string in the output stream.
 
-   Doing this causes a new string code to be defined (code is
-   lzwP->nextUnusedCode), so Caller must add that to the hash.  If
-   that code's size is beyond the overall limit, we reset the hash
-   (which means future codes will start back at the minimum size) and
-   put a clear code in the stream to tell the decompressor to do the
-   same.  So Caller must add it to the hash _before_ calling us.
-
-   Note that in the non-compressing case, the overall limit is small
-   enough to prevent us from ever defining string codes; we'll always
-   reset the hash.
-
-   There's an odd case that always screws up any attempt to make this
-   code cleaner: At the end of the LZW stream, you have to output the
-   code for the final string even though you don't have a following
-   pixel that would make a longer string.  So there's nothing to add
-   to the hash table and no point in allocating a new string code.
-   But the decompressor doesn't know that we're done, so he allocates
-   the next string code and may therefore increase his code length.
-   If we don't do the same, we will write our one last code -- the EOF
-   code -- in a code length smaller than what the decompressor is
-   expecting, and he will have a premature end of stream.
-
-   So this subroutine does run for that final code flush and does some
-   of the motions of defining a new string code, but this subroutine
-   can't update the hash because in that particular case, there's
-   nothing to add.
+   Doing this causes the protocol to define a new string code, defined as the
+   string we put plus the first pixel of the next string we put.  (It almost
+   seems to violate causality, especially since the next string we put can
+   legally be the very string code that gets defined here, but it actually
+   works).
+
+   The code that gets defined is lzwP->nextCodeToDefine.  Caller is
+   responsible for figuring out the value for the code (i.e. what we just said
+   above) so it can use the code in the future.
+
+   BUT: if the string table has reached its maximum size, issue a clear code
+   instead to cause the protocol to forget all the defined string coes and
+   start its table over (so Caller must start its own table over too).
+   EXCEPT: if we're running in no-clear mode; then we skip the clear code (so
+   the protocol maintains all the string code definitions and caller will have
+   to do so as well).
+
+   Note that in the non-compressing case, the overall limit is small enough to
+   prevent us from ever defining string codes; we'll always issue the clear
+   code.
+
+   Note that there is a case where Caller can just ignore the fact that we
+   cause the protocol to define a new string code: where the string we're
+   outputting is the last one in the stream.  In that case, the new string
+   code we define is irrelevant; it will never be used.
 -----------------------------------------------------------------------------*/
     codeBuffer_output(lzwP->codeBufferP, lzwP->stringSoFar);
-    if (lzwP->nextUnusedCode < lzwP->codeBufferP->maxCodeLimit) {
-        /* Allocate the code for the extended string, which Caller
-           should have already put in the hash so he can use it in the
-           future.  Decompressor knows when it sees the code output
-           above to define a string on its end too, using the same
-           string code we do.
-        */
-        stringCode const newCode = lzwP->nextUnusedCode++;
 
-        /* This code may be too big to fit in the current code size, in
-           which case we have to increase the code size (and decompressor
-           will do the same).
+    if (lzwP->nextCodeToDefine < lzwP->codeBufferP->maxCodeLimit) {
+        /* Record that the protocol defined a new string code, to wit
+           the numerically next one, when we did the output to the stream
+           above, and adjust the code size if this code is too wide to
+           fit in the current size.
         */
+        StringCode const newCode = lzwP->nextCodeToDefine++;
+
         lzwAdjustCodeSize(lzwP, newCode);
     } else {
-        /* Forget all the strings so far; start building again; tell
-           decompressor to do the same.
-        */
-        lzw_clearBlock(lzwP);
+        if (lzwP->noclear)
+            lzw_reportNoclear(lzwP);
+        else {
+            /* Forget all the strings so far; start building again; tell
+               decompressor to do the same.
+            */
+            lzw_clearBlock(lzwP);
+
+            if (verbose)
+                pm_message("String table filled up.  "
+                           "Clearing and starting over");
+        }
     }
 }
 
 
 
 static void
-lzw_flush(lzwCompressor * const lzwP) {
+lzw_flush(LzwCompressor * const lzwP) {
 
     if (lzwP->lzw)
         lzwOutputCurrentString(lzwP);
@@ -1082,8 +1115,8 @@ lzw_flush(lzwCompressor * const lzwP) {
 
 
 static unsigned int
-primaryHash(stringCode   const baseString,
-            stringCode   const additionalPixel,
+primaryHash(StringCode   const baseString,
+            StringCode   const additionalPixel,
             unsigned int const hshift) {
 
     unsigned int hash;
@@ -1092,71 +1125,76 @@ primaryHash(stringCode   const baseString,
     assert(additionalPixel < MAXCMAPSIZE);
 
     hash = (additionalPixel << hshift) ^ baseString;
-    
+
     return hash;
 }
 
-    
+
 
 static void
-lookupInHash(lzwCompressor *  const lzwP,
+lookupInHash(LzwCompressor *  const lzwP,
              unsigned int     const gifPixel,
-             stringCode       const fcode,
              bool *           const foundP,
-             unsigned short * const codeP,
+             StringCode *     const codeP,
              unsigned int *   const hashP) {
 
-    int disp;
+    unsigned int disp;
         /* secondary hash stride (after G. Knott) */
-    int i;
+    unsigned int hash;
         /* Index into hash table */
 
-    i = primaryHash(lzwP->stringSoFar, gifPixel, lzwP->hshift);
-    disp = (i == 0) ? 1 : lzwP->hsize - i;
-
-    while (lzwP->hashTable[i].fcode != fcode &&
-           lzwP->hashTable[i].fcode >= 0) {
-        i -= disp;
-        if (i < 0)
-            i += lzwP->hsize;
+    hash = primaryHash(lzwP->stringSoFar, gifPixel, lzwP->hshift);
+    assert(hash < lzwP->hsize);
+    disp = (hash == 0) ? 1 : lzwP->hsize - hash;
+
+    while (lzwP->hashTable[hash].present &&
+           (lzwP->hashTable[hash].baseString != lzwP->stringSoFar ||
+            lzwP->hashTable[hash].additionalPixel != gifPixel)) {
+        if (hash < disp)
+            hash += lzwP->hsize;
+        assert(hash >= disp);
+        hash -= disp;
+        assert(hash < lzwP->hsize);
     }
 
-    if (lzwP->hashTable[i].fcode == fcode) {
+    if (lzwP->hashTable[hash].present) {
         /* Found fcode in hash table */
-        *foundP = TRUE;
-        *codeP = lzwP->hashTable[i].ent;
+        *foundP = true;
+        *codeP  = lzwP->hashTable[hash].combinedString;
     } else {
         /* Found where it _should_ be (but it's not) with primary hash */
-        *foundP = FALSE;
-        *hashP = i;
+        *foundP = false;
+        *hashP  = hash;
     }
 }
 
 
 
 static void
-lzw_encodePixel(lzwCompressor * const lzwP,
+lzw_encodePixel(LzwCompressor * const lzwP,
                 unsigned int    const gifPixel) {
 
-    bool found;
-    unsigned short code;
-    unsigned int hash;
-        /* Index into hash table where the value should go */
-    
     assert(gifPixel < 256);
 
     if (!lzwP->buildingString) {
         /* Start a new string with just this pixel */
         lzwP->stringSoFar = gifPixel;
-        lzwP->buildingString = TRUE;
+        lzwP->buildingString = true;
     } else {
-        stringCode const fcode =
-            ((stringCode) gifPixel << BITS) + lzwP->stringSoFar;
-            /* The encoding of the string we've already recognized plus the
-               instant pixel, to be looked up in the hash of known strings.
+        bool found;
+            /* There's a code for the current string in the string table */
+        StringCode code;
+            /* Existing code for the current string in the string table, if
+               any
+            */
+        unsigned int hash;
+            /* Index into hash table where the entry for the new string code
+               should go; meaningless if we don't need a new string code
+               (because there's already one in the hash table for the
+               current string)
             */
-    
-        lookupInHash(lzwP, gifPixel, fcode, &found, &code, &hash);
+
+        lookupInHash(lzwP, gifPixel, &found, &code, &hash);
 
         if (found)
             /* With this new pixel, it is still a known string; 'code' is
@@ -1164,14 +1202,22 @@ lzw_encodePixel(lzwCompressor * const lzwP,
             */
             lzwP->stringSoFar = code;
         else {
-            /* It's no longer a known string.  Output the code for the
-               known prefix of the string, thus defining a new string
-               code for possible later use.  Warning:
-               lzwOutputCurrentString() does more than you think. 
+            /* We've found the longest prefix of the rest of the image for
+               which we have a string code defined.  Output the code for that
+               prefix, thus defining a new string code in the protocol for
+               possible later use.  The new code is defined as this string
+               plus the first pixel of the next string.
+
+               But if there aren't any unused string codes left, outputting
+               our string doesn't define any new string code.
             */
 
-            lzwP->hashTable[hash].ent   = lzwP->nextUnusedCode;
-            lzwP->hashTable[hash].fcode = fcode;
+            if (lzwP->nextCodeToDefine < lzwP->codeBufferP->maxCodeLimit) {
+                lzwP->hashTable[hash].present         = true;
+                lzwP->hashTable[hash].baseString      = lzwP->stringSoFar;
+                lzwP->hashTable[hash].additionalPixel = gifPixel;
+                lzwP->hashTable[hash].combinedString  = lzwP->nextCodeToDefine;
+            }
 
             lzwOutputCurrentString(lzwP);
 
@@ -1184,8 +1230,6 @@ lzw_encodePixel(lzwCompressor * const lzwP,
 
 
 /*
- * compress stdin to stdout
- *
  * Algorithm:  use open addressing double hashing (no chaining) on the
  * prefix code / next character combination.  We do a variant of Knuth's
  * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
@@ -1200,23 +1244,25 @@ lzw_encodePixel(lzwCompressor * const lzwP,
  */
 
 static void
-writePixelUncompressed(lzwCompressor * const lzwP,
+writePixelUncompressed(LzwCompressor * const lzwP,
                        unsigned int    const gifPixel) {
-                      
+
     lzwP->stringSoFar = gifPixel;
     lzwOutputCurrentString(lzwP);
 
-}    
+}
+
 
 static void
 writeRaster(struct pam *  const pamP,
-            rowReader *   const rowReaderP,
+            RowReader *   const rowReaderP,
             unsigned int  const alphaPlane,
             unsigned int  const alphaThreshold,
-            struct cmap * const cmapP, 
+            struct Cmap * const cmapP,
             unsigned int  const initBits,
             FILE *        const ofP,
-            bool          const lzw) {
+            bool          const lzw,
+            bool          const noclear) {
 /*----------------------------------------------------------------------------
    Write the raster to file 'ofP'.
 
@@ -1228,8 +1274,12 @@ writeRaster(struct pam *  const pamP,
 
    Write the raster using LZW compression, or uncompressed depending
    on 'lzw'.
+
+   If 'noclear', don't use any GIF clear codes in the output; i.e. don't
+   recompute the string table from current input.  Once the string table gets
+   to maximum size, just keep using that table for the rest of the image.
 -----------------------------------------------------------------------------*/
-    lzwCompressor * lzwP;
+    LzwCompressor * lzwP;
     tuple * tuplerow;
     unsigned int nRowsDone;
         /* Number of rows we have read so far from the the input (the
@@ -1237,15 +1287,15 @@ writeRaster(struct pam *  const pamP,
            in case of interlace, this is not the same thing as the row
            number of the current row.
         */
-    
-    lzwP = lzw_create(ofP, initBits, lzw, pamP->height * pamP->width);
+
+    lzwP = lzw_create(ofP, initBits, lzw, noclear, pamP->height * pamP->width);
 
     tuplerow = pnm_allocpamrow(pamP);
 
     lzw_clearBlock(lzwP);
 
     nRowsDone = 0;
-    
+
     while (nRowsDone < pamP->height) {
         unsigned int col;
 
@@ -1255,14 +1305,14 @@ writeRaster(struct pam *  const pamP,
             unsigned int const colorIndex =
                 gifPixel(pamP, tuplerow[col], alphaPlane, alphaThreshold,
                          cmapP);
-            
+
                 /* The value for the pixel in the GIF image.  I.e. the colormap
                    index.
                 */
             if (lzw)
                 lzw_encodePixel(lzwP, colorIndex);
             else
-                writePixelUncompressed(lzwP, colorIndex);    
+                writePixelUncompressed(lzwP, colorIndex);
         }
         ++nRowsDone;
     }
@@ -1272,7 +1322,7 @@ writeRaster(struct pam *  const pamP,
     lzw_flush(lzwP);
 
     pnm_freepamrow(tuplerow);
-    
+
     lzw_destroy(lzwP);
 }
 
@@ -1280,7 +1330,7 @@ writeRaster(struct pam *  const pamP,
 
 static void
 writeGlobalColorMap(FILE *              const ofP,
-                    const struct cmap * const cmapP,
+                    const struct Cmap * const cmapP,
                     unsigned int        const bitsPerPixel) {
 /*----------------------------------------------------------------------------
   Write out the Global Color Map
@@ -1326,16 +1376,16 @@ writeGlobalColorMap(FILE *              const ofP,
     }
     pnm_freepamtuple(tupleRgb255);
 }
-        
+
 
 
 static void
 writeGifHeader(FILE *              const ofP,
                unsigned int        const width,
-               unsigned int        const height, 
-               unsigned int        const background, 
+               unsigned int        const height,
+               unsigned int        const background,
                unsigned int        const bitsPerPixel,
-               const struct cmap * const cmapP,
+               const struct Cmap * const cmapP,
                char                const comment[],
                float               const aspect) {
 
@@ -1371,12 +1421,12 @@ writeGifHeader(FILE *              const ofP,
 
     {
         int const aspectValue = aspect == 1.0 ? 0 : ROUND(aspect * 64) - 15;
-        assert(0 <= aspectValue && aspectValue <= 255); 
+        assert(0 <= aspectValue && aspectValue <= 255);
         fputc(aspectValue, ofP);
     }
     writeGlobalColorMap(ofP, cmapP, bitsPerPixel);
 
-    if (cmapP->haveTransparent) 
+    if (cmapP->haveTransparent)
         writeTransparentColorIndexExtension(ofP, cmapP->transparent);
 
     if (comment)
@@ -1430,15 +1480,16 @@ reportImageInfo(bool         const interlace,
 
 static void
 gifEncode(struct pam *  const pamP,
-          FILE *        const ofP, 
+          FILE *        const ofP,
           pm_filepos    const rasterPos,
           bool          const gInterlace,
-          int           const background, 
+          int           const background,
           unsigned int  const bitsPerPixel,
-          struct cmap * const cmapP,
+          struct Cmap * const cmapP,
           char          const comment[],
           float         const aspect,
-          bool          const lzw) {
+          bool          const lzw,
+          bool          const noclear) {
 
     unsigned int const leftOffset = 0;
     unsigned int const topOffset  = 0;
@@ -1453,14 +1504,14 @@ gifEncode(struct pam *  const pamP,
 
     unsigned int const alphaPlane = pamAlphaPlane(pamP);
 
-    rowReader * rowReaderP;
+    RowReader * rowReaderP;
 
     reportImageInfo(gInterlace, background, bitsPerPixel);
 
     if (pamP->width > 65535)
         pm_error("Image width %u too large for GIF format.  (Max 65535)",
                  pamP->width);
-     
+
     if (pamP->height > 65535)
         pm_error("Image height %u too large for GIF format.  (Max 65535)",
                  pamP->height);
@@ -1479,7 +1530,7 @@ gifEncode(struct pam *  const pamP,
     /* Write the actual raster */
 
     writeRaster(pamP, rowReaderP, alphaPlane, alphaThreshold,
-                cmapP, initCodeSize + 1, ofP, lzw);
+                cmapP, initCodeSize + 1, ofP, lzw, noclear);
 
     rowReader_destroy(rowReaderP);
 
@@ -1493,7 +1544,7 @@ gifEncode(struct pam *  const pamP,
 
 
 static void
-reportTransparent(struct cmap * const cmapP) {
+reportTransparent(struct Cmap * const cmapP) {
 
     if (verbose) {
         if (cmapP->haveTransparent) {
@@ -1511,10 +1562,10 @@ reportTransparent(struct cmap * const cmapP) {
 
 
 static void
-computeTransparent(char          const colorarg[], 
+computeTransparent(char          const colorarg[],
                    bool          const usingFakeTrans,
                    unsigned int  const fakeTransparent,
-                   struct cmap * const cmapP) {
+                   struct Cmap * const cmapP) {
 /*----------------------------------------------------------------------------
    Figure out the color index (index into the colormap) of the color
    that is to be transparent in the GIF.
@@ -1546,7 +1597,7 @@ computeTransparent(char          const colorarg[],
         tuple transcolor;
         int found;
         int colorindex;
-        
+
         if (colorarg[0] == '=') {
             colorspec = &colorarg[1];
             exact = TRUE;
@@ -1558,7 +1609,7 @@ computeTransparent(char          const colorarg[],
         transcolor = pnm_parsecolor(colorspec, cmapP->pam.maxval);
         pnm_lookuptuple(&cmapP->pam, cmapP->tuplehash, transcolor, &found,
                         &colorindex);
-        
+
         if (found) {
             cmapP->haveTransparent = TRUE;
             cmapP->transparent = colorindex;
@@ -1602,7 +1653,7 @@ sortCompareColor(const void * const entry1P,
     struct tupleint * const * const tupleint1PP = entry1P;
     struct tupleint * const * const tupleint2PP = entry2P;
 
-    return (sortOrderColor((*tupleint1PP)->tuple) 
+    return (sortOrderColor((*tupleint1PP)->tuple)
             - sortOrderColor((*tupleint2PP)->tuple));
 }
 
@@ -1640,15 +1691,15 @@ sortTupletable(struct pam * const mapPamP,
     if (mapPamP->depth < 3)
         qsort(tuplefreq, colors, sizeof(tuplefreq[0]), sortCompareGray);
     else
-        qsort(tuplefreq, colors, sizeof(tuplefreq[0]), sortCompareColor); 
+        qsort(tuplefreq, colors, sizeof(tuplefreq[0]), sortCompareColor);
 
 }
 
 
 
 static void
-addToColormap(struct cmap *  const cmapP, 
-              const char *   const colorspec, 
+addToColormap(struct Cmap *  const cmapP,
+              const char *   const colorspec,
               unsigned int * const newIndexP) {
 /*----------------------------------------------------------------------------
   Add a new entry to the colormap.  Make the color that specified by
@@ -1685,7 +1736,7 @@ addToColormap(struct cmap *  const cmapP,
 static void
 colormapFromFile(char               const filespec[],
                  unsigned int       const maxcolors,
-                 tupletable *       const tupletableP, 
+                 tupletable *       const tupletableP,
                  struct pam *       const mapPamP,
                  unsigned int *     const colorCountP) {
 /*----------------------------------------------------------------------------
@@ -1703,13 +1754,13 @@ colormapFromFile(char               const filespec[],
     pm_close(mapfileP);
 
     pm_message("computing other colormap ...");
-    
-    *tupletableP = 
+
+    *tupletableP =
         pnm_computetuplefreqtable(mapPamP, colors, maxcolors, &colorCount);
 
     *colorCountP = colorCount;
 
-    pnm_freepamarray(colors, mapPamP); 
+    pnm_freepamarray(colors, mapPamP);
 }
 
 
@@ -1717,7 +1768,7 @@ colormapFromFile(char               const filespec[],
 static void
 readAndValidateColormapFromFile(char           const filename[],
                                 unsigned int   const maxcolors,
-                                tupletable *   const tuplefreqP, 
+                                tupletable *   const tuplefreqP,
                                 struct pam *   const mapPamP,
                                 unsigned int * const colorCountP,
                                 unsigned int   const nInputComp,
@@ -1753,7 +1804,7 @@ computeColormapBw(struct pam *   const pamP,
    $ pbmmake -w 600 400 | pamtogif -sort > canvas.gif
 -----------------------------------------------------------------------------*/
     tupletable const colormap = pnm_alloctupletable(pamP, 2);
-    
+
     *mapPamP = *pamP;
     mapPamP->depth = 1;
 
@@ -1761,12 +1812,12 @@ computeColormapBw(struct pam *   const pamP,
     colormap[0]->tuple[0] = PAM_BLACK;
     colormap[1]->value = 1;
     colormap[1]->tuple[0] = PAM_BW_WHITE;
-    
+
     *tuplefreqP  = colormap;
     *colorCountP = 2;
 }
-  
-    
+
+
 
 static void
 computeColormapFromInput(struct pam *   const pamP,
@@ -1775,7 +1826,7 @@ computeColormapFromInput(struct pam *   const pamP,
                          struct pam *   const mapPamP,
                          unsigned int * const colorCountP,
                          tupletable *   const tuplefreqP) {
-    
+
     tupletable tuplefreq;
 
     pm_message("computing colormap...");
@@ -1842,9 +1893,9 @@ computeLibnetpbmColormap(struct pam *   const pamP,
              pamP->height * pamP->width > 1)
         computeColormapBw(pamP, mapPamP, &colorCount, &tuplefreq);
     else
-        computeColormapFromInput(pamP, maxcolors, nInputComp, 
+        computeColormapFromInput(pamP, maxcolors, nInputComp,
                                  mapPamP, &colorCount, &tuplefreq);
-    
+
     if (tuplefreq == NULL)
         pm_error("too many colors - try doing a 'pnmquant %u'", maxcolors);
 
@@ -1870,10 +1921,10 @@ computeLibnetpbmColormap(struct pam *   const pamP,
 
 
 static void
-destroyCmap(struct cmap * const cmapP) {
+destroyCmap(struct Cmap * const cmapP) {
 
     unsigned int colorIndex;
-    
+
     for (colorIndex = 0; colorIndex < cmapP->cmapSize; ++colorIndex)
         pnm_freepamtuple(cmapP->color[colorIndex]);
 
@@ -1884,41 +1935,41 @@ destroyCmap(struct cmap * const cmapP) {
 
 int
 main(int argc, char *argv[]) {
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     struct pam pam;
     unsigned int bitsPerPixel;
     pm_filepos rasterPos;
 
-    struct cmap cmap;
+    struct Cmap cmap;
         /* The colormap, with all its accessories */
     unsigned int fakeTransparent;
         /* colormap index of the fake transparency color we're using to
            implement the alpha mask.  Undefined if we're not doing an alpha
            mask.
         */
-    
+
     pnm_init(&argc, argv);
-    
+
     parseCommandLine(argc, argv, &cmdline);
-    
+
     verbose = cmdline.verbose;
-    
+
     ifP = pm_openr_seekable(cmdline.input_filespec);
-    
+
     pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
-    
+
     pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
-    
-    computeLibnetpbmColormap(&pam, !!pamAlphaPlane(&pam), cmdline.mapfile, 
+
+    computeLibnetpbmColormap(&pam, !!pamAlphaPlane(&pam), cmdline.mapfile,
                              cmap.color, &cmap.tuplehash,
                              &cmap.pam, &cmap.cmapSize, cmdline.sort);
-    
+
     assert(cmap.pam.maxval == pam.maxval);
 
     if (pamAlphaPlane(&pam)) {
-        /* Add a fake entry to the end of the colormap for transparency.  
-           Make its color black. 
+        /* Add a fake entry to the end of the colormap for transparency.
+           Make its color black.
         */
         addToColormap(&cmap, cmdline.alphacolor, &fakeTransparent);
     }
@@ -1931,13 +1982,13 @@ main(int argc, char *argv[]) {
     /* All set, let's do it. */
     gifEncode(&pam, stdout, rasterPos,
               cmdline.interlace, 0, bitsPerPixel, &cmap, cmdline.comment,
-              cmdline.aspect, !cmdline.nolzw);
-    
+              cmdline.aspect, !cmdline.nolzw, cmdline.noclear);
+
     destroyCmap(&cmap);
 
     pm_close(ifP);
     pm_close(stdout);
-    
+
     return 0;
 }
 
@@ -1955,21 +2006,21 @@ main(int argc, char *argv[]) {
   JPEG Group's djpeg on 2001.09.29.  In 2006.12 the output subroutines
   were rewritten; now no uncompressed output subroutines are derived from
   the Independent JPEG Group's source code.
-  
+
   2007.01  Changed sort routine to qsort.  (afu)
   2007.03  Implemented variable hash table size, PBM color table
            shortcut and "-aspect" command line option.   (afu)
 
- 
+
   Copyright (C) 1989 by Jef Poskanzer.
- 
+
   Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted, provided
   that the above copyright notice appear in all copies and that both that
   copyright notice and this permission notice appear in supporting
   documentation.  This software is provided "as is" without express or
   implied warranty.
- 
+
   The Graphics Interchange Format(c) is the Copyright property of
   CompuServe Incorporated.  GIF(sm) is a Service Mark property of
   CompuServe Incorporated.
diff --git a/converter/other/pngtopam.c b/converter/other/pngtopam.c
index 59b29f5f..01690520 100644
--- a/converter/other/pngtopam.c
+++ b/converter/other/pngtopam.c
@@ -64,12 +64,12 @@ static bool verbose;
 
 
 static void
-parseCommandLine(int                  argc, 
+parseCommandLine(int                  argc,
                  const char **        argv,
                  struct CmdlineInfo * cmdlineP ) {
 /*----------------------------------------------------------------------------
    Parse program command line described in Unix standard form by argc
-   and argv.  Return the information in the options as *cmdlineP.  
+   and argv.  Return the information in the options as *cmdlineP.
 
    If command line is internally inconsistent (invalid options, etc.),
    issue error message to stderr and abort program.
@@ -90,13 +90,13 @@ parseCommandLine(int                  argc,
     MALLOCARRAY(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0, "verbose",     OPT_FLAG,   NULL,                  
+    OPTENT3(0, "verbose",     OPT_FLAG,   NULL,
             &cmdlineP->verbose,       0);
-    OPTENT3(0, "alpha",       OPT_FLAG,   NULL,                  
+    OPTENT3(0, "alpha",       OPT_FLAG,   NULL,
             &alphaSpec,               0);
-    OPTENT3(0, "alphapam",    OPT_FLAG,   NULL,                  
+    OPTENT3(0, "alphapam",    OPT_FLAG,   NULL,
             &alphapamSpec,            0);
-    OPTENT3(0, "mix",         OPT_FLAG,   NULL,                  
+    OPTENT3(0, "mix",         OPT_FLAG,   NULL,
             &mixSpec,                 0);
     OPTENT3(0, "background",  OPT_STRING, &cmdlineP->background,
             &backgroundSpec,          0);
@@ -104,9 +104,9 @@ parseCommandLine(int                  argc,
             &cmdlineP->gammaSpec,     0);
     OPTENT3(0, "text",        OPT_STRING, &cmdlineP->text,
             &textSpec,                0);
-    OPTENT3(0, "time",        OPT_FLAG,   NULL,                  
+    OPTENT3(0, "time",        OPT_FLAG,   NULL,
             &cmdlineP->time,          0);
-    OPTENT3(0, "byrow",       OPT_FLAG,   NULL,                  
+    OPTENT3(0, "byrow",       OPT_FLAG,   NULL,
             &cmdlineP->byrow,         0);
 
     opt.opt_table = option_def;
@@ -248,7 +248,7 @@ computePngLineSize(struct pngx * const pngxP) {
     if (UINT_MAX / bytesPerSample / samplesPerPixel < pngx_imageWidth(pngxP))
         pm_error("Width %u of PNG is uncomputably large",
                  pngx_imageWidth(pngxP));
-       
+
     return pngx_imageWidth(pngxP) * bytesPerSample * samplesPerPixel;
 }
 
@@ -370,7 +370,7 @@ reader_createRowByRow(struct pngx * const pngxP,
 
     readerP->pngRaster = NULL;
 
-    MALLOCARRAY(readerP->rowBuf, computePngLineSize(pngxP)); 
+    MALLOCARRAY(readerP->rowBuf, computePngLineSize(pngxP));
 
     if (!readerP->rowBuf)
         pm_error("Could not allocate %u bytes for a PNG row buffer",
@@ -393,7 +393,7 @@ reader_destroy(Reader * const readerP) {
 
     if (readerP->pngRaster)
         freePngRaster(readerP->pngRaster, readerP->pngxP);
-   
+
     if (readerP->rowBuf)
         free(readerP->rowBuf);
 
@@ -434,14 +434,14 @@ getPngVal(const png_byte ** const pp,
           int               const bitDepth) {
 
     png_uint_16 c;
-    
+
     if (bitDepth == 16)
         c = *(*pp)++ << 8;
     else
         c = 0;
 
     c |= *(*pp)++;
-    
+
     return c;
 }
 
@@ -498,7 +498,7 @@ setTuple(const struct pam *  const pamP,
             tuple[PAM_GRN_PLANE] = foreground.g;
             tuple[PAM_BLU_PLANE] = foreground.b;
             tuple[PAM_TRN_PLANE] = alpha;
-        }    
+        }
     } else {
         assert(alphaHandling == ALPHA_MIX);
 
@@ -540,7 +540,7 @@ saveText(struct pngx * const pngxP,
 
         while (text.line[i].key[j] != '\0' &&
                text.line[i].key[j] != ' ')
-            ++j;    
+            ++j;
 
         if (text.line[i].key[j] != ' ') {
             fprintf(tfP, "%s", text.line[i].key);
@@ -552,7 +552,7 @@ saveText(struct pngx * const pngxP,
                 putc(' ', tfP);
         }
         putc(' ', tfP); /* at least one space between key and text */
-    
+
         for (j = 0; j < text.line[i].text_length; ++j) {
             putc(text.line[i].text[j], tfP);
             if (text.line[i].text[j] == '\n') {
@@ -607,11 +607,11 @@ dumpTypeAndFilter(struct pngx * const pngxP) {
     case PNG_COLOR_TYPE_GRAY:
         typeString = "gray";
         break;
-        
+
     case PNG_COLOR_TYPE_GRAY_ALPHA:
         typeString = "gray+alpha";
         break;
-        
+
     case PNG_COLOR_TYPE_PALETTE:
         typeString = "palette";
         break;
@@ -630,13 +630,13 @@ dumpTypeAndFilter(struct pngx * const pngxP) {
         pm_asprintf(&filterString, "base filter");
         break;
     default:
-        pm_asprintf(&filterString, "unknown filter type %d", 
+        pm_asprintf(&filterString, "unknown filter type %d",
                     pngx_filterType(pngxP));
     }
 
     pm_message("%s, %s, %s",
                typeString,
-               pngx_interlaceType(pngxP) ? 
+               pngx_interlaceType(pngxP) ?
                "Adam7 interlaced" : "not interlaced",
                filterString);
 
@@ -679,7 +679,7 @@ dumpPngInfo(struct pngx * const pngxP) {
                    pngx_gama(pngxP));
     else
         pm_message("gAMA chunk (image gamma): not present");
-    
+
     if (pngx_chunkIsPresent(pngxP, PNG_INFO_sBIT))
         pm_message("sBIT chunk: present");
     else
@@ -739,7 +739,7 @@ transColor(struct pngx * const pngxP) {
     struct pngx_trns const trans = pngx_trns(pngxP);
 
     assert(pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS));
-    
+
     return trans.transColor;
 }
 
@@ -775,7 +775,7 @@ isTransparentColor(pngcolor        const color,
            We could fix this by not letting libpng gamma-correct the
            pixels, and just do it ourselves.
         */
-    
+
         switch (pngx_colorType(pngxP)) {
         case PNG_COLOR_TYPE_GRAY:
             retval = color.r == gammaCorrect(transColor16.gray, gamma,
@@ -788,7 +788,7 @@ isTransparentColor(pngcolor        const color,
                                                      pngxP->maxval));
         }
         }
-    } else 
+    } else
         retval = FALSE;
 
     return retval;
@@ -810,7 +810,7 @@ setupGammaCorrection(struct pngx *     const pngxP,
    use *gammaCorrectionP to do it).
 
    'screenGammaIsKnown' means we know what the screen gamma is, and it is
-   'screenGamma'.  If we don't know what the screen gamma is, gamma 
+   'screenGamma'.  If we don't know what the screen gamma is, gamma
    correction is not possible, so we set up for no gamma correction.
 
    The gamma correction we ordain is a combination of the image gamma,
@@ -845,7 +845,7 @@ setupGammaCorrection(struct pngx *     const pngxP,
             gammaCorrectionP->needCorrection = true;
             gammaCorrectionP->gamma = imageGamma * screenGamma;
             /* In case of gamma-corrections, sBIT's as in the
-               PNG-file are not valid anymore 
+               PNG-file are not valid anymore
             */
             pngx_removeChunk(pngxP, PNG_INFO_sBIT);
             if (verbose)
@@ -870,7 +870,7 @@ paletteHasPartialTransparency(struct pngx * const pngxP) {
 
             bool foundGray;
             unsigned int i;
-            
+
             for (i = 0, foundGray = FALSE;
                  i < trans.numTrans && !foundGray;
                  ++i) {
@@ -945,7 +945,7 @@ getComponentSbit(struct pngx *       const pngxP,
         /* We care about both the foreground and the alpha */
         bool fgNotUniform;
         png_byte fgSbit;
-        
+
         getComponentSbitFg(pngxP, &fgSbit, &fgNotUniform);
 
         if (fgNotUniform)
@@ -961,7 +961,7 @@ getComponentSbit(struct pngx *       const pngxP,
     }
 }
 
-                 
+
 
 static void
 shiftPalette(struct pngx * const pngxP,
@@ -977,9 +977,9 @@ shiftPalette(struct pngx * const pngxP,
                  shift);
     else {
         struct pngx_plte const palette = pngx_plte(pngxP);
-        
+
         unsigned int i;
-        
+
         for (i = 0; i < palette.size; ++i) {
             palette.palette[i].red   >>= (8 - shift);
             palette.palette[i].green >>= (8 - shift);
@@ -1004,7 +1004,7 @@ computeMaxvalFromSbit(struct pngx *       const pngxP,
        mix both, the multiplication may result in values that require
        the normal bit depth, so we will use the sBIT info only for
        transparency, if we know that only solid and fully transparent
-       is used 
+       is used
     */
 
     bool notUniform;
@@ -1043,7 +1043,7 @@ computeMaxvalFromSbit(struct pngx *       const pngxP,
                            "writing file with %u bits", componentSigBit);
                 *maxvalP = (1l << componentSigBit) - 1;
                 *succeededP = true;
-                
+
                 if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_PALETTE)
                     shiftPalette(pngxP, componentSigBit);
                 else
@@ -1070,7 +1070,7 @@ setupSignificantBits(struct pngx *       const pngxP,
   Also set up *pngxP for the corresponding significant bits.
 -----------------------------------------------------------------------------*/
     bool gotItFromSbit;
-    
+
     if (pngx_chunkIsPresent(pngxP, PNG_INFO_sBIT))
         computeMaxvalFromSbit(pngxP, alphaHandling,
                               &pngxP->maxval, &gotItFromSbit, errorLevelP);
@@ -1119,7 +1119,7 @@ imageHasColor(struct pngx * const pngxP) {
 
         bool foundColor;
         unsigned int i;
-            
+
         for (i = 0, foundColor = FALSE;
              i < palette.size && !foundColor;
              ++i) {
@@ -1148,7 +1148,7 @@ determineOutputType(struct pngx *       const pngxP,
         /* The output is a old style pseudo-PNM transparency image */
         *depthP = 1;
         *formatP = maxval > 1 ? PGM_FORMAT : PBM_FORMAT;
-    } else {            
+    } else {
         /* The output is a normal Netpbm image */
         bool const outputIsColor =
             imageHasColor(pngxP) || !isGrayscale(bgColor);
@@ -1191,7 +1191,7 @@ getBackgroundColor(struct pngx *   const pngxP,
     if (requestedColor) {
         /* Background was specified from the command-line; we always
            use that.  I chose to do no gamma-correction in this case;
-           which is a bit arbitrary.  
+           which is a bit arbitrary.
         */
         pixel const backcolor = ppm_parsecolor(requestedColor, maxval);
 
@@ -1207,12 +1207,12 @@ getBackgroundColor(struct pngx *   const pngxP,
         switch (pngx_colorType(pngxP)) {
         case PNG_COLOR_TYPE_GRAY:
         case PNG_COLOR_TYPE_GRAY_ALPHA:
-            bgColorP->r = bgColorP->g = bgColorP->b = 
+            bgColorP->r = bgColorP->g = bgColorP->b =
                 gammaCorrect(background.gray, gamma, pngxP->maxval);
             break;
         case PNG_COLOR_TYPE_PALETTE: {
             struct pngx_plte const palette = pngx_plte(pngxP);
-            png_color const rawBgcolor = 
+            png_color const rawBgcolor =
                 palette.palette[background.index];
             *bgColorP = gammaCorrectColor(pngcolorFromByte(rawBgcolor),
                                           gamma, pngxP->maxval);
@@ -1221,13 +1221,13 @@ getBackgroundColor(struct pngx *   const pngxP,
         case PNG_COLOR_TYPE_RGB:
         case PNG_COLOR_TYPE_RGB_ALPHA: {
             png_color_16 const rawBgcolor = background;
-            
+
             *bgColorP = gammaCorrectColor(pngcolorFrom16(rawBgcolor),
                                           gamma, pngxP->maxval);
         }
         break;
         }
-    } else 
+    } else
         /* when no background given, we use white [from version 2.37] */
         bgColorP->r = bgColorP->g = bgColorP->b = maxval;
 }
@@ -1346,7 +1346,7 @@ makeTupleRow(const struct pam *  const pamP,
                      pngxP, paletteAlpha(pngxP, index, pngxP->maxval));
         }
         break;
-                
+
         case PNG_COLOR_TYPE_RGB: {
             pngcolor fgColor;
 
@@ -1404,7 +1404,7 @@ reportOutputFormat(const struct pam * const pamP) {
         assert(false); /* Every possible value handled above */
     }
 }
-    
+
 
 
 static void
@@ -1450,9 +1450,9 @@ writeNetpbm(struct pam *        const pamP,
 
 
 
-static void 
-convertpng(FILE *             const ifP, 
-           FILE *             const tfP, 
+static void
+convertpng(FILE *             const ifP,
+           FILE *             const tfP,
            struct CmdlineInfo const cmdline,
            int *              const errorLevelP) {
 
@@ -1488,7 +1488,7 @@ convertpng(FILE *             const ifP,
 
     getBackgroundColor(pngxP, cmdline.background, gamma, pngxP->maxval,
                        &bgColor);
-  
+
     pam.size        = sizeof(pam);
     pam.len         = PAM_STRUCT_SIZE(tuple_type);
     pam.file        = stdout;
@@ -1500,7 +1500,7 @@ convertpng(FILE *             const ifP,
     determineOutputType(pngxP, cmdline.alpha, bgColor, pngxP->maxval,
                         &pam.format, &pam.depth, pam.tuple_type);
 
-    rasterReaderP = cmdline.byrow ? 
+    rasterReaderP = cmdline.byrow ?
         reader_createRowByRow(pngxP, ifP) : reader_createAllAtOnce(pngxP, ifP);
 
     writeNetpbm(&pam, pngxP, rasterReaderP, bgColor,
@@ -1517,7 +1517,7 @@ convertpng(FILE *             const ifP,
 
 
 
-int 
+int
 main(int argc, const char *argv[]) {
 
     struct CmdlineInfo cmdline;
diff --git a/converter/pbm/g3topbm.c b/converter/pbm/g3topbm.c
index 2a4d84e5..1ffa3893 100644
--- a/converter/pbm/g3topbm.c
+++ b/converter/pbm/g3topbm.c
@@ -4,7 +4,7 @@
 
   This program reads a Group 3 FAX file and produces a PBM image.
 
-  Bryan Henderson wrote this on August 5, 2004 and contributed it to 
+  Bryan Henderson wrote this on August 5, 2004 and contributed it to
   the public domain.
 
   This program is designed to be a drop-in replacement for the program
@@ -111,15 +111,15 @@ parseCommandLine(int argc, const char ** const argv,
             0);
     OPTENT3(0, "kludge",           OPT_FLAG,  NULL, &cmdlineP->kludge,
             0);
-    OPTENT3(0, "stretch",          OPT_FLAG,  NULL, &cmdlineP->stretch, 
+    OPTENT3(0, "stretch",          OPT_FLAG,  NULL, &cmdlineP->stretch,
             0);
-    OPTENT3(0, "stop_error",       OPT_FLAG,  NULL, &cmdlineP->stop_error, 
+    OPTENT3(0, "stop_error",       OPT_FLAG,  NULL, &cmdlineP->stop_error,
             0);
     OPTENT3(0, "width",            OPT_UINT,  &cmdlineP->expectedLineSize,
             &widthSpec,                0);
     OPTENT3(0, "paper_size",       OPT_STRING, &paperSize,
             &paper_sizeSpec,           0);
-    
+
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
@@ -151,7 +151,7 @@ parseCommandLine(int argc, const char ** const argv,
     } else
         cmdlineP->expectedLineSize = 0;
 
-    if (argc-1 == 0) 
+    if (argc-1 == 0)
         cmdlineP->inputFilespec = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
@@ -246,7 +246,7 @@ static void
 initBitStream(struct BitStream * const bitStreamP,
               FILE *             const fileP,
               bool               const reversebits) {
-    
+
     bitStreamP->fileP        = fileP;
     bitStreamP->reversebits  = reversebits;
     bitStreamP->shbit        = 0x00;
@@ -263,10 +263,10 @@ skipToNextLine(struct BitStream * const bitStreamP) {
 
     eol = FALSE;
     error = NULL;
-    
+
     while (!eol && !error) {
         unsigned int bit;
-        
+
         readBitAndDetectEol(bitStreamP, &bit, &eol, &error);
     }
 }
@@ -274,12 +274,12 @@ skipToNextLine(struct BitStream * const bitStreamP) {
 
 
 static void
-addtohash(G3TableEntry *     hash[], 
-          G3TableEntry       table[], 
-          unsigned int const n, 
-          int          const a, 
+addtohash(G3TableEntry *     hash[],
+          G3TableEntry       table[],
+          unsigned int const n,
+          int          const a,
           int          const b) {
-    
+
     unsigned int i;
 
     for (i = 0; i < n; ++i) {
@@ -295,10 +295,10 @@ addtohash(G3TableEntry *     hash[],
 
 
 static G3TableEntry *
-hashfind(G3TableEntry *       hash[], 
-         int            const length, 
-         int            const code, 
-         int            const a, 
+hashfind(G3TableEntry *       hash[],
+         int            const length,
+         int            const code,
+         int            const a,
          int            const b) {
 
     unsigned int pos;
@@ -423,7 +423,7 @@ processG3Code(const G3TableEntry * const teP,
         (teP > mtable ? 2 : 0) + (teP - ttable) % 2;
 
     unsigned int teCount;
-    
+
     switch(teId) {
     case TERMWHITE: teCount = (teP - ttable    ) / 2;      break;
     case TERMBLACK: teCount = (teP - ttable - 1) / 2;      break;
@@ -436,7 +436,7 @@ processG3Code(const G3TableEntry * const teP,
     case TERMBLACK: {
         unsigned int totalRunLength;
         unsigned int col;
-        
+
         col = *colP;
         totalRunLength = MIN(*countP + teCount, MAXCOLS - col);
 
@@ -484,7 +484,7 @@ readFaxRow(struct BitStream * const bitStreamP,
            const char **      const exceptionP,
            const char **      const errorP) {
 /*----------------------------------------------------------------------------
-  Read one line of G3 fax from the bit stream *bitStreamP into 
+  Read one line of G3 fax from the bit stream *bitStreamP into
   packedBitrow[].  Return the length of the line, in pixels, as *lineLengthP.
 
   If there's a problem with the line, return as much of it as we can,
@@ -495,17 +495,17 @@ readFaxRow(struct BitStream * const bitStreamP,
   We guarantee that we make progress through the input stream.
 
   Iff there is an error, return a text description of it in newly
-  malloc'ed storage at *errorP and all other specified behavior 
+  malloc'ed storage at *errorP and all other specified behavior
   (including return values) is unspecified.
 -----------------------------------------------------------------------------*/
     unsigned int col;
-    unsigned int curlen;  
-        /* Number of bits we've read so far for the code we're currently 
+    unsigned int curlen;
+        /* Number of bits we've read so far for the code we're currently
            reading
         */
     unsigned int fillbits;
         /* Number of consecutive 0 bits.  Can precede EOL codes */
-    unsigned int curcode; 
+    unsigned int curcode;
         /* What we've assembled so far of the code we're currently reading */
     unsigned int count;
         /* Number of consecutive pixels of the same color */
@@ -562,14 +562,14 @@ readFaxRow(struct BitStream * const bitStreamP,
                 } else if (curcode != 0) {
                     const G3TableEntry * const teP =
                         g3code(curcode, curlen, currentColor);
-                        /* Address of structure that describes the 
+                        /* Address of structure that describes the
                            current G3 code.  Null means 'curcode' isn't
                            a G3 code yet (probably just the beginning of one)
                         */
                     if (teP) {
                         processG3Code(teP, packedBitrow,
                                       &col, &currentColor, &count);
-                        
+
                         curcode = 0;
                         curlen = 0;
                     }
@@ -597,7 +597,7 @@ freeBits(unsigned char ** const packedBits,
             /* This is just a pointer to the previous row; don't want to
                free it twice.
             */
-        } else 
+        } else
             pbm_freerow_packed(packedBits[row]);
     }
     free(packedBits);
@@ -710,7 +710,7 @@ analyzeLineSize(lineSizeAnalyzer * const analyzerP,
    line.  Starting in Netpbm 10.24 (August 2004), we assume there is
    no valid reason to have an empty line and recognize EOF as any
    empty line.  Alternatively, we could read off and ignore two empty
-   lines without a 3rd.  
+   lines without a 3rd.
 */
 
 static void
@@ -727,7 +727,7 @@ readFax(struct BitStream * const bitStreamP,
     const char * error;
     bool eof;
     unsigned int row;
-    
+
     MALLOCARRAY_NOFAIL(packedBits, MAXROWS);
 
     initializeLineSizeAnalyzer(&lineSizeAnalyzer,
@@ -758,7 +758,7 @@ readFax(struct BitStream * const bitStreamP,
                     eof = TRUE;
                 } else {
                     analyzeLineSize(&lineSizeAnalyzer, lineSize);
-                    
+
                     if (stretch) {
                         ++row;
                         if (row >= MAXROWS)
@@ -808,7 +808,7 @@ main(int argc, const char * argv[]) {
     buildHashes(&whash, &bhash);
 
     readFax(&bitStream, cmdline.stretch, cmdline.expectedLineSize,
-            !cmdline.stop_error, 
+            !cmdline.stop_error,
             &packedBits, &cols, &rows);
 
     pm_close(ifP);
diff --git a/converter/pbm/pbmtolj.c b/converter/pbm/pbmtolj.c
index 0cceb4fe..3cd76703 100644
--- a/converter/pbm/pbmtolj.c
+++ b/converter/pbm/pbmtolj.c
@@ -1,14 +1,14 @@
 /* pbmtolj.c - read a portable bitmap and produce a LaserJet bitmap file
-**  
+**
 **  based on pbmtops.c
 **
 **  Michael Haberler HP Vienna mah@hpuviea.uucp
 **                 mcvax!tuvie!mah
-**  misfeatures: 
+**  misfeatures:
 **      no positioning
 **
 **      Bug fix Dec 12, 1988 :
-**              lines in putbit() reshuffled 
+**              lines in putbit() reshuffled
 **              now runs OK on HP-UX 6.0 with X10R4 and HP Laserjet II
 **      Bo Thide', Swedish Institute of Space Physics, Uppsala <bt@irfu.se>
 **
@@ -75,7 +75,7 @@ parseCommandLine(int argc, char ** argv,
     MALLOCARRAY(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3(0,   "resolution",  OPT_UINT, &cmdlineP->dpi, 
+    OPTENT3(0,   "resolution",  OPT_UINT, &cmdlineP->dpi,
             &dpiSpec, 0);
     OPTENT3(0,   "copies",      OPT_UINT, &cmdlineP->copies,
             &copiesSpec, 0);
@@ -97,7 +97,7 @@ parseCommandLine(int argc, char ** argv,
     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (argc-1 == 0) 
+    if (argc-1 == 0)
         cmdlineP->inputFilename = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
@@ -246,7 +246,7 @@ packbits(void) {
           We drop out here after having found a [possibly empty]
           literal, followed by a [possibly degenerate] run of repeated
           bytes.  Degenerate runs can occur at the end of the scan line...
-          there may be a "repeat" of 1 byte (which can't actually be 
+          there may be a "repeat" of 1 byte (which can't actually be
           represented as a repeat) so we simply fold it into the previous
           literal.
         */
@@ -320,11 +320,11 @@ deltarow(void) {
             skip = 1;
         }
         if (mustBurst) {
-            burstCode = burstEnd - burstStart; 
+            burstCode = burstEnd - burstStart;
                 /* 0-7, means 1-8 bytes follow */
             code = (burstCode << 5) | skipped;
             deltaBuffer[deltaBufferIndex++] = (char) code;
-            memcpy(deltaBuffer+deltaBufferIndex, rowBuffer+burstStart, 
+            memcpy(deltaBuffer+deltaBufferIndex, rowBuffer+burstStart,
                    burstCode + 1);
             deltaBufferIndex += burstCode + 1;
             burstStart = -1;
@@ -369,7 +369,7 @@ convertRow(const bit *    const bitrow,
            bool *         const rowIsBlankP) {
 
     unsigned int rightmostBlackCol;
-        
+
     findRightmostBlackCol(bitrow, cols, rowIsBlankP, &rightmostBlackCol);
 
     if (!*rowIsBlankP) {
@@ -377,7 +377,7 @@ convertRow(const bit *    const bitrow,
             /* Number of columns excluding white right margin */
         unsigned int const rucols = ((nzcol + 7) / 8) * 8;
             /* 'nzcol' rounded up to nearest multiple of 8 */
-        
+
         unsigned int col;
 
         memset(rowBuffer, 0, rowBufferSize);
@@ -404,7 +404,7 @@ convertRow(const bit *    const bitrow,
 
         if (delta) {
             /* May need to temporarily bump the row buffer index up to
-               whatever the previous line's was - if this line is shorter 
+               whatever the previous line's was - if this line is shorter
                than the previous would otherwise leave dangling cruft.
             */
             unsigned int const savedRowBufferIndex = rowBufferIndex;
@@ -421,7 +421,7 @@ convertRow(const bit *    const bitrow,
 }
 
 
-    
+
 static void
 printBlankRows(unsigned int const count) {
 
@@ -430,12 +430,12 @@ printBlankRows(unsigned int const count) {
         /* The code used to be this, but Charles Howes reports that
            this escape sequence does not exist on his HP Laserjet IIP
            plus, so we use the following less elegant code instead.
-           
-           printf("\033*b%dY", (*blankRowsP)); 
+
+           printf("\033*b%dY", (*blankRowsP));
         */
-        for (x = 0; x < count; ++x) 
+        for (x = 0; x < count; ++x)
             printf("\033*b0W");
-        
+
         memset(prevRowBuffer, 0, rowBufferSize);
     }
 }
@@ -494,7 +494,7 @@ printRow(void) {
 
 
 static void
-doPage(FILE *             const ifP, 
+doPage(FILE *             const ifP,
        struct cmdlineInfo const cmdline) {
 
     bit * bitrow;
@@ -525,10 +525,10 @@ doPage(FILE *             const ifP,
         else {
             printBlankRows(blankRows);
             blankRows = 0;
-            
+
             printRow();
         }
-    }    
+    }
     printBlankRows(blankRows);
     blankRows = 0;
 
@@ -563,3 +563,6 @@ main(int argc, char * argv[]) {
 
     return 0;
 }
+
+
+
diff --git a/converter/ppm/ppmtompeg/HISTORY b/converter/ppm/ppmtompeg/HISTORY
index c9f4932a..e6fb86d3 100644
--- a/converter/ppm/ppmtompeg/HISTORY
+++ b/converter/ppm/ppmtompeg/HISTORY
@@ -1,4 +1,4 @@
-The entire ppmtojpeg directory was adapted by Bryan from the package
+The entire ppmtompeg directory was adapted by Bryan from the package
 mpeg_encode-1.5b-src (subdirectory mpeg_encode) on March 30, 1999.  The 
 program was called mpeg_encode in that package.  It was dated August 16,
 1995 and came from ftp://mm-ftp.cs.berkeley.edu/pub/multimedia/mpeg/
diff --git a/doc/HISTORY b/doc/HISTORY
index ceeb277b..a569e725 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -4,24 +4,46 @@ Netpbm.
 CHANGE HISTORY 
 --------------
 
-18.02.17 BJH  Release 10.81.03
+18.03.25 BJH  Release 10.82.00
 
-              g3topbm: Fix bug - produces invalid empty PBM image if input
-              image is empty.
+              pbmtext: Add -wchar.
 
-              mrftopbm: Fix bug - wrong error messages or output when input
-              invalidly short.
+              pbmtext: Add -text-dump option.
 
-18.01.20 BJH  Release 10.81.02
+              ppmhist: Add color summary to top of output, (except with
+              -noheader).
 
-              Build: fix compile failure on systems without strnlen (such
-              as MacOS X before 10.7).  Broken in 10.81.00 (December 2017).
+              pnmremap: Add -randomseed.
+
+              pnmquant: Add -norandom, -randomseed.
+
+              pamtogif: Add -noclear option.
+
+              giftopnm: Check "data width" value from GIF image properly:
+              can't be bigger than 11, because the minimum code size is one
+              more than the data width and the maximum code size is 12.  (Note
+              that GIF spec prohibits anything more than 8).
+
+              pnmpsnr: Add -targetX options.
 
-18.01.05 BJH  Release 10.81.01
+              ppmrainbow: Add "ppmrainbow: " to error messages, like other
+              programs.
+
+              ppmrainbow: improve error message.
+
+              g3topbm: Fix bug - produces invalid empty PBM image if input
+              image is empty.
 
               ppmpat: Fix bug - crash or junk output with -camo or -anticamo
               and no -color.  Introduced in Netpbm 10.78 (March 2017).
 
+              mrftopbm: Fix bug - wrong error messages or output when input
+              invalidly short.  Always broken (mrftopbm was new in Netpbm
+              10.18 (September 2003).
+
+              Build: fix compile failure on systems without strnlen (such
+              as MacOS X before 10.7).  Broken in 10.81 (December 2017).
+
 17.12.30 BJH  Release 10.81.00
 
               sldtoppm: -lib and -dir don't work - always says slide not
@@ -1881,8 +1903,9 @@ CHANGE HISTORY
               (produces grayscale output).  Broken between Netpbm 10.19 and
               10.26, inclusive.
 
-              pbmtomrf, mrftopbm: fix crashes, incorrect output in all
-              cases.  Broken forever.
+              pbmtomrf, mrftopbm: fix crashes, incorrect output in all cases.
+              Always broken (mrftopbm was new in Netpbm 10.18 (September
+              2003).
 
               pnm_alloctupletable, pnm_tuplehashtotable,
               pnm_computetuplefreqtable3: fix crash when out of memory,
diff --git a/editor/pnmconvol.c b/editor/pnmconvol.c
index d1feb0a3..98ec3a6b 100644
--- a/editor/pnmconvol.c
+++ b/editor/pnmconvol.c
@@ -169,7 +169,7 @@ getMatrixOptDimensions(const char *   const matrixOptString,
                 if (colCt != *widthP)
                 pm_error("-matrix option value contains rows of different "
                          "widths: %u and %u", *widthP, colCt);
-            }            
+            }
             pm_strfree(rowString);
             cursor = next;
 
@@ -211,7 +211,7 @@ parseMatrixRow(const char * const matrixOptRowString,
                 char * trailingJunk;
                 weight[col] = strtod(colString, &trailingJunk);
 
-                if (*trailingJunk != '\0') 
+                if (*trailingJunk != '\0')
                     pm_error("The Column %u element of the row '%s' in the "
                              "-matrix value is not a valid floating point "
                              "number", col, matrixOptRowString);
@@ -235,7 +235,7 @@ parseMatrixOptWithDimensions(const char * const matrixOptString,
                              unsigned int const width,
                              unsigned int const height,
                              float **     const weight) {
-    
+
     unsigned int row;
     const char * cursor;
 
@@ -262,7 +262,7 @@ parseMatrixOptWithDimensions(const char * const matrixOptString,
             }
         }
     }
-}    
+}
 
 
 
@@ -311,7 +311,7 @@ parseCommandLine(int argc, char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
-   and argv.  Return the information in the options as *cmdlineP.  
+   and argv.  Return the information in the options as *cmdlineP.
 
    If command line is internally inconsistent (invalid options, etc.),
    issue error message to stderr and abort program.
@@ -336,9 +336,9 @@ parseCommandLine(int argc, char ** argv,
             &cmdlineP->matrixSpec,     0)
     OPTENT3(0, "matrixfile",   OPT_STRINGLIST, &cmdlineP->matrixfile,
             &matrixfileSpec,           0)
-    OPTENT3(0, "nooffset",     OPT_FLAG,   NULL,                  
+    OPTENT3(0, "nooffset",     OPT_FLAG,   NULL,
             &cmdlineP->nooffset,       0);
-    OPTENT3(0, "normalize",    OPT_FLAG,   NULL,                  
+    OPTENT3(0, "normalize",    OPT_FLAG,   NULL,
             &cmdlineP->normalize,      0);
     OPTENT3(0, "bias",         OPT_UINT,   &cmdlineP->bias,
             &biasSpec,                 0);
@@ -382,7 +382,7 @@ parseCommandLine(int argc, char ** argv,
                      "argument is the input file name");
     } else {
         /* It's an old style invocation we accept for backward compatibility */
-        
+
         if (argc-1 < 1)
             pm_error("You must specify either -matrix or -matrixfile "
                      "at least one argument which names an old-style PGM "
@@ -394,7 +394,7 @@ parseCommandLine(int argc, char ** argv,
                 cmdlineP->inputFileName = argv[2];
             else
                 cmdlineP->inputFileName = "-";
-            
+
             if (argc-1 > 2)
                 pm_error("Too many arguments.  Only acceptable arguments are: "
                          "convolution matrix file name and input file name");
@@ -416,11 +416,11 @@ struct ConvKernel {
     float ** weight[3];
         /* weight[PLANE][ROW][COL] is the weight to give to Plane PLANE
            of the pixel at row ROW, column COL within the convolution window.
-           
+
            One means full weight.
 
            It can have magnitude greater than or less than one.  It can be
-           positive or negative.  
+           positive or negative.
         */
     unsigned int bias;
         /* The amount to be added to the linear combination of sample values.
@@ -441,7 +441,7 @@ warnBadKernel(struct ConvKernel * const convKernelP) {
 
     for (plane = 0; plane < convKernelP->planes; ++plane)
         sum[plane] = 0.0; /* initial value */
-    
+
     for (row = 0; row < convKernelP->rows; ++row) {
         unsigned int col;
         for (col = 0; col < convKernelP->cols; ++col) {
@@ -462,9 +462,9 @@ warnBadKernel(struct ConvKernel * const convKernelP) {
             if (sum[plane] < 0.0)
                 negative = true;
         }
-    
+
         if (biased) {
-            pm_message("WARNING - this convolution matrix is biased.  " 
+            pm_message("WARNING - this convolution matrix is biased.  "
                        "red, green, and blue average weights: %f, %f, %f "
                        "(unbiased would be 1).",
                        sum[PAM_RED_PLANE],
@@ -488,7 +488,7 @@ warnBadKernel(struct ConvKernel * const convKernelP) {
 
 static void
 convKernelCreatePnm(struct pam *         const cpamP,
-                    tuple * const *      const ctuples, 
+                    tuple * const *      const ctuples,
                     unsigned int         const depth,
                     bool                 const offsetPnm,
                     struct ConvKernel ** const convKernelPP) {
@@ -526,7 +526,7 @@ convKernelCreatePnm(struct pam *         const cpamP,
         unsigned int row;
 
         MALLOCARRAY_NOFAIL(convKernelP->weight[plane], cpamP->height);
-    
+
         for (row = 0; row < cpamP->height; ++row) {
             unsigned int col;
 
@@ -578,9 +578,9 @@ normalizeKernelPlane(struct ConvKernel * const convKernelP,
 
     for (row = 0, sum = 0.0; row < convKernelP->rows; ++row) {
         unsigned int col;
-            
+
         for (col = 0; col < convKernelP->cols; ++col) {
-                
+
             sum += convKernelP->weight[plane][row][col];
         }
     }
@@ -592,7 +592,7 @@ normalizeKernelPlane(struct ConvKernel * const convKernelP,
 
         for (row = 0; row < convKernelP->rows; ++row) {
             unsigned int col;
-                
+
             for (col = 0; col < convKernelP->cols; ++col)
                 convKernelP->weight[plane][row][col] *= scaler;
         }
@@ -641,7 +641,7 @@ getKernelPnm(const char *         const fileName,
     /* Read in the convolution matrix. */
     ctuples = pnm_readpam(cifP, &cpam, PAM_STRUCT_SIZE(tuple_type));
     pm_close(cifP);
-    
+
     validateKernelDimensions(cpam.width, cpam.height);
 
     convKernelCreatePnm(&cpam, ctuples, depth, offset, convKernelPP);
@@ -657,7 +657,7 @@ convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
 /*----------------------------------------------------------------------------
    Create a convolution kernel as described by a -matrix command line
    option.
-   
+
    The option value is 'matrixOpt'.
 -----------------------------------------------------------------------------*/
     struct ConvKernel * convKernelP;
@@ -678,7 +678,7 @@ convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
 
             MALLOCARRAY_NOFAIL(convKernelP->weight[plane][row],
                                matrixOpt.width);
-    
+
             for (col = 0; col < matrixOpt.width; ++col)
                 convKernelP->weight[plane][row][col] =
                     matrixOpt.weight[row][col];
@@ -728,7 +728,7 @@ parsePlaneFileLine(const char *   const line,
         else {
             char * trailingJunk;
             weight[colCt] = strtod(token, &trailingJunk);
-            if (*trailingJunk != '\0') 
+            if (*trailingJunk != '\0')
                 pm_error("The Column %u element of the row '%s' in the "
                          "-matrix value is not a valid floating point "
                          "number", colCt, line);
@@ -744,7 +744,7 @@ parsePlaneFileLine(const char *   const line,
 
 
 static void
-readPlaneFile(FILE *         const ifP, 
+readPlaneFile(FILE *         const ifP,
               float ***      const weightP,
               unsigned int * const widthP,
               unsigned int * const heightP) {
@@ -783,7 +783,7 @@ readPlaneFile(FILE *         const ifP,
                 eof = true;
             else {
                 REALLOCARRAY(weight, rowCt + 1);
-            
+
                 if (weight == NULL)
                     pm_error("Unable to allocate memory for "
                              "convolution matrix");
@@ -799,7 +799,7 @@ readPlaneFile(FILE *         const ifP,
                             pm_error("Multiple row widths in the convolution "
                                      "matrix file: %u columns and %u columns.",
                                      width, thisWidth);
-                    }                    
+                    }
                     ++rowCt;
                 }
                 pm_strfree(line);
@@ -818,7 +818,7 @@ readPlaneFile(FILE *         const ifP,
 static void
 copyWeight(float **       const srcWeight,
            unsigned int   const width,
-           unsigned int   const height, 
+           unsigned int   const height,
            float ***      const dstWeightP) {
 /*----------------------------------------------------------------------------
    Make a copy, in dynamically allocated memory, of the weight matrix
@@ -832,7 +832,7 @@ copyWeight(float **       const srcWeight,
 
     if (dstWeight == NULL)
         pm_error("Could not allocate memory for convolution matrix");
-   
+
     for (row = 0; row < height; ++row) {
         unsigned int col;
 
@@ -973,7 +973,7 @@ validateEnoughImageToConvolve(const struct pam *        const inpamP,
         pm_error("Image is too short (%u rows) to convolve with this "
                  "%u-row convolution kernel.",
                  inpamP->height, convKernelP->rows);
-    
+
     if (inpamP->width < convKernelP->cols + 1)
         pm_error("Image is too narrow (%u columns) to convolve with this "
                  "%u-column convolution kernel.",
@@ -994,7 +994,7 @@ allocRowbuf(struct pam * const pamP,
         pm_error("Failed to allocate %u-row buffer", height);
     else {
         unsigned int row;
-    
+
         for (row = 0; row < height; ++row)
             rowbuf[row] = pnm_allocpamrow(pamP);
     }
@@ -1043,7 +1043,7 @@ readAndScaleRows(struct pam *              const inpamP,
                  unsigned int              const outputDepth) {
 /*----------------------------------------------------------------------------
   Read in 'count' rows into rowbuf[].
-  
+
   Scale the contents to maxval 'outputMaxval' and expand to depth
   'outputDepth'.
 -----------------------------------------------------------------------------*/
@@ -1173,7 +1173,7 @@ convolveGeneralRowPlane(struct pam *              const pamP,
     unsigned int const ccolso2 = convKernelP->cols / 2;
 
     unsigned int col;
-    
+
     for (col = 0; col < pamP->width; ++col) {
         if (col < ccolso2 || col >= pamP->width - ccolso2)
             /* The unconvolved left or right edge */
@@ -1283,7 +1283,7 @@ allocSum(unsigned int const depth,
 
         for (plane = 0; plane < depth; ++plane) {
             MALLOCARRAY(sum[plane], size);
-            
+
             if (!sum[plane])
                 pm_error("Could not allocate memory for %u sums", size);
         }
@@ -1330,7 +1330,7 @@ computeInitialColumnSums(struct pam *              const pamP,
                  row < convKernelP->rows;
                  ++row)
                 convColumnSum[plane][col] += window[row][col][plane];
-        }            
+        }
     }
 }
 
@@ -1358,7 +1358,7 @@ convolveRowWithColumnSumsMean(const struct ConvKernel * const convKernelP,
   be convolved because the convolution window runs off the edge).
 -----------------------------------------------------------------------------*/
     unsigned int plane;
-    
+
     for (plane = 0; plane < pamP->depth; ++plane) {
         unsigned int const crowso2 = convKernelP->rows / 2;
         unsigned int const ccolso2 = convKernelP->cols / 2;
@@ -1385,7 +1385,7 @@ convolveRowWithColumnSumsMean(const struct ConvKernel * const convKernelP,
                 } else {
                     /* Column numbers to subtract or add to isum */
                     unsigned int const subcol = col - ccolso2 - 1;
-                    unsigned int const addcol = col + ccolso2;  
+                    unsigned int const addcol = col + ccolso2;
 
                     gisum -= convColumnSum[plane][subcol];
                     gisum += convColumnSum[plane][addcol];
@@ -1429,7 +1429,7 @@ convolveRowWithColumnSumsVertical(
 
     for (plane = 0; plane < pamP->depth; ++plane) {
         unsigned int col;
-    
+
         for (col = 0; col < pamP->width; ++col) {
             if (col < ccolso2 || col >= pamP->width - ccolso2) {
                 /* The unconvolved left or right edge */
@@ -1513,12 +1513,12 @@ convolveMeanRowPlane(struct pam *              const pamP,
             } else {
                 /* Column numbers to subtract or add to isum */
                 unsigned int const subcol = col - ccolso2 - 1;
-                unsigned int const addcol = col + ccolso2;  
-                
+                unsigned int const addcol = col + ccolso2;
+
                 convColumnSum[addcol] = convColumnSum[addcol]
                     - window[subrow][addcol][plane]
                     + window[addrow][addcol][plane];
-                
+
                 gisum = gisum - convColumnSum[subcol] + convColumnSum[addcol];
             }
             outputrow[col][plane] =
@@ -1674,7 +1674,7 @@ allocRowSum(unsigned int const depth,
 
         for (plane = 0; plane < depth; ++plane) {
             MALLOCARRAY(sum[plane], height);
-            
+
             if (!sum[plane])
                 pm_error("Could not allocate memory for %u rows of sums",
                          height);
@@ -1683,7 +1683,7 @@ allocRowSum(unsigned int const depth,
 
                 for (row = 0; row < height; ++row) {
                     MALLOCARRAY(sum[plane][row], width);
-                    
+
                     if (!sum[plane][row])
                         pm_error("Could not allocate memory "
                                  "for a row of sums");
@@ -1747,7 +1747,7 @@ convolveHorizontalRowPlane0(struct pam *              const outpamP,
                    starts at the left edge of the image.
                 */
                 unsigned int const leftcol = 0;
-            
+
                 unsigned int crow;
 
                 for (crow = 0, matrixSum = 0.0;
@@ -1756,7 +1756,7 @@ convolveHorizontalRowPlane0(struct pam *              const outpamP,
                     tuple * const tuplesInWindow = &window[crow][leftcol];
 
                     unsigned int ccol;
-                
+
                     sumWindow[crow][col] = 0;
                     for (ccol = 0; ccol < convKernelP->cols; ++ccol)
                         sumWindow[crow][col] += tuplesInWindow[ccol][plane];
@@ -1769,7 +1769,7 @@ convolveHorizontalRowPlane0(struct pam *              const outpamP,
                 unsigned int const addcol  = col + ccolso2;
 
                 unsigned int crow;
-                
+
                 for (crow = 0, matrixSum = 0.0;
                      crow < convKernelP->rows;
                      ++crow) {
@@ -1798,7 +1798,7 @@ setupCircMap2(tuple **     const rowbuf,
               unsigned int const windowHeight) {
 
     unsigned int const toprow = windowTopRow % windowHeight;
-    
+
     unsigned int crow;
     unsigned int i;
 
@@ -1874,7 +1874,7 @@ convolveHorizontalRowPlane(struct pam *              const pamP,
             }
         } else {
             unsigned int const subcol  = col - ccolso2 - 1;
-            unsigned int const addcol  = col + ccolso2;  
+            unsigned int const addcol  = col + ccolso2;
 
             unsigned int crow;
 
@@ -1947,7 +1947,7 @@ convolveHorizontal(struct pam *              const inpamP,
 
         for (crow = 0; crow < convKernelP->rows; ++crow)
             sumCircMap[crow] = convRowSum[plane][crow];
- 
+
         convolveHorizontalRowPlane0(outpamP, circMap, convKernelP, plane,
                                     outputrow, sumCircMap);
     }
@@ -1969,13 +1969,13 @@ convolveHorizontal(struct pam *              const inpamP,
 
             readAndScaleRow(inpamP, rowbuf[windowBotRow % windowHeight],
                             outpamP->maxval, outpamP->depth);
-            
+
             setupCircMap2(rowbuf, convRowSum[plane], circMap, sumCircMap,
                           windowTopRow, windowHeight);
 
             convolveHorizontalRowPlane(outpamP, circMap, convKernelP, plane,
                                        outputrow, sumCircMap);
-            
+
             pnm_writepamrow(outpamP, outputrow);
         }
     }
@@ -2005,7 +2005,7 @@ convolveVerticalRowPlane(struct pam *              const pamP,
         */
     unsigned int const addrow = 1 + (convKernelP->rows - 1);
         /* Bottom row of convolution window: What we add to running sum */
-    
+
     unsigned int col;
 
     for (col = 0; col < pamP->width; ++col) {
@@ -2304,7 +2304,7 @@ main(int argc, char * argv[]) {
  original) in January 1995.
 
  Reduce run time by general optimizations and handling special cases of
- convolution matrices.  Program automatically determines if convolution 
+ convolution matrices.  Program automatically determines if convolution
  matrix is one of the types it can make use of so no extra command line
  arguments are necessary.
 
@@ -2322,7 +2322,7 @@ main(int argc, char * argv[]) {
  -------------------------------------------
  Created separate functions as code was getting too large to put keep both
  PGM and PPM cases in same function and also because SWITCH statement in
- inner loop can take progressively more time the larger the size of the 
+ inner loop can take progressively more time the larger the size of the
  convolution matrix.  GCC is affected this way.
 
  Removed use of MOD (%) operator from innermost loop by modifying manner in
@@ -2331,50 +2331,50 @@ main(int argc, char * argv[]) {
  This is from the file pnmconvol.README, dated August 1995, extracted in
  April 2000, which was in the March 1994 Netpbm release:
 
- ----------------------------------------------------------------------------- 
+ -----------------------------------------------------------------------------
  This is a faster version of the pnmconvol.c program that comes with netpbm.
  There are no changes to the command line arguments, so this program can be
  dropped in without affecting the way you currently run it.  An updated man
  page is also included.
- 
+
  My original intention was to improve the running time of applying a
  neighborhood averaging convolution matrix to an image by using a different
  algorithm, but I also improved the run time of performing the general
  convolution by optimizing that code.  The general convolution runs in 1/4 to
  1/2 of the original time and neighborhood averaging runs in near constant
  time for the convolution masks I tested (3x3, 5x5, 7x7, 9x9).
- 
+
  Sample times for two computers are below.  Times are in seconds as reported
  by /bin/time for a 512x512 pgm image.
- 
+
  Matrix                  IBM RS6000      SUN IPC
  Size & Type                220
- 
+
  3x3
  original pnmconvol         6.3            18.4
  new general case           3.1             6.0
  new average case           1.8             2.6
- 
+
  5x5
  original pnmconvol        11.9            44.4
  new general case           5.6            11.9
  new average case           1.8             2.6
- 
+
  7x7
  original pnmconvol        20.3            82.9
  new general case           9.4            20.7
  new average case           1.8             2.6
- 
+
  9x9
  original pnmconvol        30.9           132.4
  new general case          14.4            31.8
  new average case           1.8             2.6
- 
- 
+
+
  Send all questions/comments/bugs to me at burns@chem.psu.edu.
- 
+
  - Mike
- 
+
  ----------------------------------------------------------------------------
  Mike Burns                                              System Administrator
  burns@chem.psu.edu                                   Department of Chemistry
diff --git a/editor/pnmquant b/editor/pnmquant
index 35a75e96..0bb328d2 100755
--- a/editor/pnmquant
+++ b/editor/pnmquant
@@ -96,6 +96,8 @@ sub parseCommandLine(@) {
                                   "spreadbrightness",
                                   "spreadluminosity",
                                   "floyd|fs!",
+                                  "norandom",
+                                  "randomseed=i",
                                   "quiet",
                                   "plain");
 
@@ -248,9 +250,10 @@ sub makeColormap($$$$$) {
 
 
 
-sub remap($$$$) {
+sub remap($$$$$$) {
 
-    my ($mapfileSpec, $opt_floyd, $opt_plain, $opt_quiet) = @_;
+    my ($mapfileSpec, $opt_floyd, $opt_norandom, $opt_randomseed,
+        $opt_plain, $opt_quiet) = @_;
 
     # Remap the image on Standard Input to Standard Output, using the colors
     # from the colormap file named $mapfileSpec.
@@ -261,6 +264,17 @@ sub remap($$$$) {
     if ($opt_floyd) {
         push(@options, "-floyd");
     }
+    if ($opt_norandom) {
+        push(@options, "-norandom");
+    }
+    if (defined($opt_randomseed)) {
+        if ($opt_randomseed < 0) {
+             print(STDERR "-randomseed value must not be negative.  " .
+                   "You specified $opt_randomseed\n");
+             exit(10);
+        }
+        push(@options, "-randomseed=$opt_randomseed");
+    }
     if ($opt_plain) {
         push(@options, "-plain");
     }
@@ -311,6 +325,8 @@ open(STDOUT, ">&OLDOUT");
 
 remap($mapfileSpec, 
       $cmdlineR->{floyd}, 
+      $cmdlineR->{norandom}, 
+      $cmdlineR->{randomseed}, 
       $cmdlineR->{plain},
       $cmdlineR->{quiet});
 
diff --git a/editor/pnmremap.c b/editor/pnmremap.c
index ed758aa3..59b1e84b 100644
--- a/editor/pnmremap.c
+++ b/editor/pnmremap.c
@@ -41,6 +41,17 @@ enum MissingMethod {
     MISSING_CLOSE
 };
 
+enum InitRandom {
+    RANDOM_NONE,
+    RANDOM_WITHSEED,
+    RANDOM_NOSEED
+};
+
+struct Random {
+    enum InitRandom init;
+    unsigned int seed;
+};
+
 struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
@@ -48,9 +59,9 @@ struct CmdlineInfo {
     const char * inputFilespec;  /* Filespec of input file */
     const char * mapFilespec;    /* Filespec of colormap file */
     unsigned int floyd;   /* Boolean: -floyd/-fs option */
-    unsigned int norandom;
+    struct Random random;
     enum MissingMethod missingMethod;
-    char * missingcolor;      
+    char * missingcolor;
         /* -missingcolor value.  Null if not specified */
     unsigned int verbose;
 };
@@ -62,7 +73,7 @@ parseCommandLine (int argc, const char ** argv,
                   struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
-   and argv.  Return the information in the options as *cmdlineP.  
+   and argv.  Return the information in the options as *cmdlineP.
 
    If command line is internally inconsistent (invalid options, etc.),
    issue error message to stderr and abort program.
@@ -78,28 +89,30 @@ parseCommandLine (int argc, const char ** argv,
     unsigned int option_def_index;
 
     unsigned int nofloyd, firstisdefault;
-    unsigned int missingSpec, mapfileSpec;
+    unsigned int missingSpec, mapfileSpec, norandomSpec, randomseedSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
-    
+
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "floyd",          OPT_FLAG,   
+    OPTENT3(0,   "floyd",          OPT_FLAG,
             NULL,                       &cmdlineP->floyd,    0);
-    OPTENT3(0,   "fs",             OPT_FLAG,   
+    OPTENT3(0,   "fs",             OPT_FLAG,
             NULL,                       &cmdlineP->floyd,    0);
-    OPTENT3(0,   "nofloyd",        OPT_FLAG,   
+    OPTENT3(0,   "nofloyd",        OPT_FLAG,
             NULL,                       &nofloyd,            0);
-    OPTENT3(0,   "nofs",           OPT_FLAG,   
+    OPTENT3(0,   "nofs",           OPT_FLAG,
             NULL,                       &nofloyd,            0);
-    OPTENT3(0,   "norandom",       OPT_FLAG,   
-            NULL,                       &cmdlineP->norandom, 0);
-    OPTENT3(0,   "firstisdefault", OPT_FLAG,   
+    OPTENT3(0,   "norandom",       OPT_FLAG,
+            NULL,                       &norandomSpec,       0);
+    OPTENT3(0,   "randomseed",     OPT_UINT,
+            &cmdlineP->random.seed,     &randomseedSpec,     0);
+    OPTENT3(0,   "firstisdefault", OPT_FLAG,
             NULL,                       &firstisdefault,     0);
-    OPTENT3(0,   "mapfile",        OPT_STRING, 
+    OPTENT3(0,   "mapfile",        OPT_STRING,
             &cmdlineP->mapFilespec,    &mapfileSpec,         0);
-    OPTENT3(0,   "missingcolor",   OPT_STRING, 
+    OPTENT3(0,   "missingcolor",   OPT_STRING,
             &cmdlineP->missingcolor,   &missingSpec,         0);
-    OPTENT3(0, "verbose",          OPT_FLAG,   NULL,                  
+    OPTENT3(0, "verbose",          OPT_FLAG,   NULL,
             &cmdlineP->verbose,                              0);
 
     opt.opt_table = option_def;
@@ -107,13 +120,32 @@ parseCommandLine (int argc, const char ** argv,
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
     cmdlineP->missingcolor = NULL;  /* default value */
-    
+
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (cmdlineP->floyd && nofloyd)
         pm_error("You cannot specify both -floyd and -nofloyd options.");
 
+    if (cmdlineP->floyd) {
+        if (norandomSpec) {
+            if (randomseedSpec)
+                pm_error("You cannot specify both -norandom and -randomseed.");
+            else
+                cmdlineP->random.init = RANDOM_NONE;
+        } else {
+            if (randomseedSpec)
+                cmdlineP->random.init = RANDOM_WITHSEED;
+            else
+                cmdlineP->random.init = RANDOM_NOSEED;
+        }
+    } else {
+        if (norandomSpec)
+            pm_message("-floyd not specified.  -norandom has no effect.");
+        if (randomseedSpec)
+            pm_message("-floyd not specified.  Ignoring -randomseed value.");
+    }
+
     if (firstisdefault && missingSpec)
         pm_error("You cannot specify both -missing and -firstisdefault.");
 
@@ -176,7 +208,7 @@ grayscaleToDepth3(tuple const tuple) {
 static void
 adjustDepthTuple(tuple           const tuple,
                  depthAdjustment const adjustment) {
-    
+
     switch (adjustment) {
     case ADJUST_NONE:
         break;
@@ -194,7 +226,7 @@ adjustDepthTuple(tuple           const tuple,
 static void
 inverseAdjustDepthTuple(tuple           const tuple,
                         depthAdjustment const adjustment) {
-    
+
     switch (adjustment) {
     case ADJUST_NONE:
         break;
@@ -249,7 +281,7 @@ selectDepthAdjustment(const struct pam * const pamP,
 
    The only depth changes we know how to do are:
 
-     - from tuple type RGB, depth 3 to depth 1 
+     - from tuple type RGB, depth 3 to depth 1
 
        We change it to grayscale or black and white.
 
@@ -296,7 +328,7 @@ selectDepthAdjustment(const struct pam * const pamP,
                      "that I know how to convert to the map depth.  "
                      "I can convert RGB, GRAYSCALE, and BLACKANDWHITE.  "
                      "The input image is '%.*s'.",
-                     newDepth, pamP->depth, 
+                     newDepth, pamP->depth,
                      (int)sizeof(pamP->tuple_type), pamP->tuple_type);
         }
     }
@@ -305,8 +337,8 @@ selectDepthAdjustment(const struct pam * const pamP,
 
 
 static void
-computeColorMapFromMap(struct pam *   const mappamP, 
-                       tuple **       const maptuples, 
+computeColorMapFromMap(struct pam *   const mappamP,
+                       tuple **       const maptuples,
                        tupletable *   const colormapP,
                        unsigned int * const newcolorsP) {
 /*----------------------------------------------------------------------------
@@ -317,12 +349,12 @@ computeColorMapFromMap(struct pam *   const mappamP,
 
    Return the number of colors in the returned colormap as *newcolorsP.
 -----------------------------------------------------------------------------*/
-    unsigned int colors; 
+    unsigned int colors;
 
     if (mappamP->width == 0 || mappamP->height == 0)
         pm_error("colormap file contains no pixels");
 
-    *colormapP = 
+    *colormapP =
         pnm_computetuplefreqtable(mappamP, maptuples, MAXCOLORS, &colors);
     if (*colormapP == NULL)
         pm_error("too many colors in colormap!");
@@ -334,7 +366,7 @@ computeColorMapFromMap(struct pam *   const mappamP,
 
 #define FS_SCALE 1024
 
-struct fserr {
+struct Fserr {
     unsigned int width;
         /* Width of the image being dithered */
     long ** thiserr;
@@ -359,20 +391,26 @@ struct fserr {
 
 
 static void
-randomizeError(long **      const err,
-               unsigned int const width,
-               unsigned int const depth) {
+randomizeError(long **       const err,
+               unsigned int  const width,
+               unsigned int  const depth,
+               struct Random const random) {
 /*----------------------------------------------------------------------------
    Set a random error in the range [-1 .. 1] (normalized via FS_SCALE)
    in the error array err[][].
 -----------------------------------------------------------------------------*/
+    unsigned int const seed = (random.init == RANDOM_WITHSEED) ?
+        random.seed : pm_randseed();
+
     unsigned int col;
 
-    srand(pm_randseed());
+    assert(random.init != RANDOM_NONE);
+
+    srand(seed);
 
     for (col = 0; col < width; ++col) {
         unsigned int plane;
-        for (plane = 0; plane < depth; ++plane) 
+        for (plane = 0; plane < depth; ++plane)
             err[plane][col] = rand() % (FS_SCALE * 2) - FS_SCALE;
     }
 }
@@ -390,7 +428,7 @@ zeroError(long **      const err,
 
     for (col = 0; col < width; ++col) {
         unsigned int plane;
-        for (plane = 0; plane < depth; ++plane) 
+        for (plane = 0; plane < depth; ++plane)
             err[plane][col] = 0;
     }
 }
@@ -398,7 +436,7 @@ zeroError(long **      const err,
 
 
 static void
-fserrSetForward(struct fserr * const fserrP) {
+fserr_setForward(struct Fserr * const fserrP) {
 
     fserrP->fsForward = TRUE;
     fserrP->begCol = 0;
@@ -409,7 +447,7 @@ fserrSetForward(struct fserr * const fserrP) {
 
 
 static void
-fserrSetBackward(struct fserr * const fserrP) {
+fserr_setBackward(struct Fserr * const fserrP) {
 
     fserrP->fsForward = FALSE;
     fserrP->begCol = fserrP->width - 1;
@@ -420,9 +458,9 @@ fserrSetBackward(struct fserr * const fserrP) {
 
 
 static void
-initFserr(struct pam *   const pamP,
-          struct fserr * const fserrP,
-          bool           const initRandom) {
+fserr_init(struct pam *   const pamP,
+           struct Fserr * const fserrP,
+           struct Random  const random) {
 /*----------------------------------------------------------------------------
    Initialize the Floyd-Steinberg error vectors
 -----------------------------------------------------------------------------*/
@@ -440,7 +478,7 @@ initFserr(struct pam *   const pamP,
     if (fserrP->nexterr == NULL)
         pm_error("Out of memory allocating Floyd-Steinberg structures "
                  "for depth %u", pamP->depth);
-    
+
     for (plane = 0; plane < pamP->depth; ++plane) {
         MALLOCARRAY(fserrP->thiserr[plane], fserrSize);
         if (fserrP->thiserr[plane] == NULL)
@@ -452,24 +490,24 @@ initFserr(struct pam *   const pamP,
                      "for Plane %u, size %u", plane, fserrSize);
     }
 
-    if (initRandom)
-        randomizeError(fserrP->thiserr, fserrSize, pamP->depth);
+    if (random.init != RANDOM_NONE)
+        randomizeError(fserrP->thiserr, fserrSize, pamP->depth, random);
     else
         zeroError(fserrP->thiserr, fserrSize, pamP->depth);
 
-    fserrSetForward(fserrP);
+    fserr_setForward(fserrP);
 }
 
 
 
 static void
-floydInitRow(struct pam * const pamP, struct fserr * const fserrP) {
+floydInitRow(struct pam * const pamP, struct Fserr * const fserrP) {
 
     int col;
-    
+
     for (col = 0; col < pamP->width + 2; ++col) {
         unsigned int plane;
-        for (plane = 0; plane < pamP->depth; ++plane) 
+        for (plane = 0; plane < pamP->depth; ++plane)
             fserrP->nexterr[plane][col] = 0;
     }
 }
@@ -477,10 +515,10 @@ floydInitRow(struct pam * const pamP, struct fserr * const fserrP) {
 
 
 static void
-floydAdjustColor(struct pam *   const pamP, 
-                 tuple          const intuple, 
-                 tuple          const outtuple, 
-                 struct fserr * const fserrP, 
+floydAdjustColor(struct pam *   const pamP,
+                 tuple          const intuple,
+                 tuple          const outtuple,
+                 struct Fserr * const fserrP,
                  int            const col) {
 /*----------------------------------------------------------------------------
   Use Floyd-Steinberg errors to adjust actual color.
@@ -497,10 +535,10 @@ floydAdjustColor(struct pam *   const pamP,
 
 
 static void
-floydPropagateErr(struct pam *   const pamP, 
-                  struct fserr * const fserrP, 
-                  int            const col, 
-                  tuple          const oldtuple, 
+floydPropagateErr(struct pam *   const pamP,
+                  struct Fserr * const fserrP,
+                  int            const col,
+                  tuple          const oldtuple,
                   tuple          const newtuple) {
 /*----------------------------------------------------------------------------
   Propagate Floyd-Steinberg error terms.
@@ -515,7 +553,7 @@ floydPropagateErr(struct pam *   const pamP,
         long const newSample = newtuple[plane];
         long const oldSample = oldtuple[plane];
         long const err = (oldSample - newSample) * FS_SCALE;
-            
+
         if (fserrP->fsForward) {
             fserrP->thiserr[plane][col + 2] += ( err * 7 ) / 16;
             fserrP->nexterr[plane][col    ] += ( err * 3 ) / 16;
@@ -533,7 +571,7 @@ floydPropagateErr(struct pam *   const pamP,
 
 
 static void
-floydSwitchDir(struct pam * const pamP, struct fserr * const fserrP) {
+floydSwitchDir(struct pam * const pamP, struct Fserr * const fserrP) {
 
     unsigned int plane;
 
@@ -544,9 +582,9 @@ floydSwitchDir(struct pam * const pamP, struct fserr * const fserrP) {
     }
 
     if (fserrP->fsForward)
-        fserrSetBackward(fserrP);
+        fserr_setBackward(fserrP);
     else
-        fserrSetForward(fserrP);
+        fserr_setForward(fserrP);
 }
 
 
@@ -567,7 +605,7 @@ struct colormapFinder {
         /* The value by which our intermediate distance calculations
            have to be divided to make sure we don't overflow our
            unsigned int data structure.
-           
+
            To the extent 'distanceDivider' is greater than 1, closest
            color results will be approximate -- there could
            conceivably be a closer one that we miss.
@@ -590,13 +628,13 @@ createColormapFinder(struct pam *             const pamP,
     colormapFinderP->colors = colors;
 
     {
-        unsigned int const maxHandleableSqrDiff = 
+        unsigned int const maxHandleableSqrDiff =
             (unsigned int)UINT_MAX / pamP->depth;
-        
+
         if (SQR(pamP->maxval) > maxHandleableSqrDiff)
             colormapFinderP->distanceDivider = (unsigned int)
                 (SQR(pamP->maxval) / maxHandleableSqrDiff + 0.1 + 1.0);
-                /* The 0.1 is a fudge factor to keep us out of rounding 
+                /* The 0.1 is a fudge factor to keep us out of rounding
                    trouble.  The 1.0 effects a round-up.
                 */
         else
@@ -664,8 +702,8 @@ searchColormapClose(struct pam *            const pamP,
         newdist = 0;
 
         for (plane=0; plane < pamP->depth; ++plane) {
-            newdist += 
-                SQR(tuple[plane] - colorFinderP->colormap[i]->tuple[plane]) 
+            newdist +=
+                SQR(tuple[plane] - colorFinderP->colormap[i]->tuple[plane])
                 / colorFinderP->distanceDivider;
         }
         if (newdist < dist) {
@@ -695,15 +733,15 @@ searchColormapExact(struct pam *            const pamP,
 -----------------------------------------------------------------------------*/
     unsigned int i;
     bool found;
-    
+
     found = FALSE;  /* initial value */
     for (i = 0; i < colorFinderP->colors && !found; ++i) {
         unsigned int plane;
         found = TRUE;  /* initial assumption */
-        for (plane=0; plane < pamP->depth; ++plane) 
-            if (tuple[plane] != colorFinderP->colormap[i]->tuple[plane]) 
+        for (plane=0; plane < pamP->depth; ++plane)
+            if (tuple[plane] != colorFinderP->colormap[i]->tuple[plane])
                 found = FALSE;
-        if (found) 
+        if (found)
             *colormapIndexP = i;
     }
     *foundP = found;
@@ -712,11 +750,11 @@ searchColormapExact(struct pam *            const pamP,
 
 
 static void
-lookupThroughHash(struct pam *            const pamP, 
-                  tuple                   const tuple, 
+lookupThroughHash(struct pam *            const pamP,
+                  tuple                   const tuple,
                   bool                    const needExactMatch,
                   struct colormapFinder * const colorFinderP,
-                  tuplehash               const colorhash,       
+                  tuplehash               const colorhash,
                   int *                   const colormapIndexP,
                   bool *                  const usehashP) {
 /*----------------------------------------------------------------------------
@@ -748,11 +786,11 @@ lookupThroughHash(struct pam *            const pamP,
                                 colormapIndexP, &found);
             if (!found)
                 *colormapIndexP = -1;
-        } else 
+        } else
             searchColormapClose(pamP, tuple, colorFinderP, colormapIndexP);
         if (*usehashP) {
             int fits;
-            pnm_addtotuplehash(pamP, colorhash, tuple, *colormapIndexP, 
+            pnm_addtotuplehash(pamP, colorhash, tuple, *colormapIndexP,
                                &fits);
             if (!fits) {
                 pm_message("out of memory adding to hash table; "
@@ -771,7 +809,7 @@ mapTuple(struct pam *            const pamP,
          tuple                   const defaultColor,
          tupletable              const colormap,
          struct colormapFinder * const colorFinderP,
-         tuplehash               const colorhash, 
+         tuplehash               const colorhash,
          bool *                  const usehashP,
          tuple                   const outTuple,
          bool *                  const missingP) {
@@ -781,7 +819,7 @@ mapTuple(struct pam *            const pamP,
            there is no usable color in the color map.
         */
 
-    lookupThroughHash(pamP, inTuple, !!defaultColor, colorFinderP, 
+    lookupThroughHash(pamP, inTuple, !!defaultColor, colorFinderP,
                       colorhash, &colormapIndex, usehashP);
 
     if (colormapIndex == -1) {
@@ -800,12 +838,12 @@ mapTuple(struct pam *            const pamP,
 
 static void
 convertRowStraight(struct pam *            const inpamP,
-                   struct pam *            const outpamP, 
+                   struct pam *            const outpamP,
                    tuple                         inrow[],
                    depthAdjustment         const depthAdjustment,
                    tupletable              const colormap,
                    struct colormapFinder * const colorFinderP,
-                   tuplehash               const colorhash, 
+                   tuplehash               const colorhash,
                    bool *                  const usehashP,
                    tuple                   const defaultColor,
                    tuple                         outrow[],
@@ -822,7 +860,7 @@ convertRowStraight(struct pam *            const inpamP,
 -----------------------------------------------------------------------------*/
     unsigned int col;
     unsigned int missingCount;
-    
+
     /* The following modify tuplerow, to make it consistent with
      *outpamP instead of *inpamP.
      */
@@ -833,7 +871,7 @@ convertRowStraight(struct pam *            const inpamP,
     adjustDepthRow(outrow, outpamP->width, depthAdjustment);
 
     missingCount = 0;  /* initial value */
-    
+
     for (col = 0; col < outpamP->width; ++col) {
         bool missing;
         mapTuple(outpamP, outrow[col], defaultColor,
@@ -856,10 +894,10 @@ convertRowDither(struct pam *            const inpamP,
                  depthAdjustment         const depthAdjustment,
                  tupletable              const colormap,
                  struct colormapFinder * const colorFinderP,
-                 tuplehash               const colorhash, 
+                 tuplehash               const colorhash,
                  bool *                  const usehashP,
                  tuple                   const defaultColor,
-                 struct fserr *          const fserrP,
+                 struct Fserr *          const fserrP,
                  tuple                         outrow[],
                  unsigned int *          const missingCountP) {
 /*----------------------------------------------------------------------------
@@ -885,7 +923,7 @@ convertRowDither(struct pam *            const inpamP,
     floydInitRow(inpamP, fserrP);
 
     missingCount = 0;  /* initial value */
-    
+
     for (col = fserrP->begCol; col != fserrP->endCol; col += fserrP->step) {
         bool missing;
 
@@ -929,11 +967,11 @@ convertRow(struct pam *            const inpamP,
            depthAdjustment               depthAdjustment,
            tupletable              const colormap,
            struct colormapFinder * const colorFinderP,
-           tuplehash               const colorhash, 
+           tuplehash               const colorhash,
            bool *                  const usehashP,
-           bool                    const floyd, 
+           bool                    const floyd,
            tuple                   const defaultColor,
-           struct fserr *          const fserrP,
+           struct Fserr *          const fserrP,
            tuple                         outrow[],
            unsigned int *          const missingCountP) {
 /*----------------------------------------------------------------------------
@@ -959,7 +997,7 @@ convertRow(struct pam *            const inpamP,
                          depthAdjustment, colormap, colorFinderP, colorhash,
                          usehashP, defaultColor,
                          fserrP, outrow, missingCountP);
-    else 
+    else
         convertRowStraight(inpamP, outpamP, inrow,
                            depthAdjustment, colormap, colorFinderP, colorhash,
                            usehashP, defaultColor,
@@ -969,14 +1007,14 @@ convertRow(struct pam *            const inpamP,
 
 
 static void
-copyRaster(struct pam *       const inpamP, 
-           struct pam *       const outpamP,
-           tupletable         const colormap, 
-           unsigned int       const colormapSize,
-           bool               const floyd, 
-           bool               const randomize,
-           tuple              const defaultColor, 
-           unsigned int *     const missingCountP) {
+copyRaster(struct pam *   const inpamP,
+           struct pam *   const outpamP,
+           tupletable     const colormap,
+           unsigned int   const colormapSize,
+           bool           const floyd,
+           struct Random  const random,
+           tuple          const defaultColor,
+           unsigned int * const missingCountP) {
 
     tuplehash const colorhash = pnm_createtuplehash();
 
@@ -992,7 +1030,7 @@ copyRaster(struct pam *       const inpamP,
     depthAdjustment depthAdjustment;
     struct colormapFinder * colorFinderP;
     bool usehash;
-    struct fserr fserr;
+    struct Fserr fserr;
     int row;
 
     workpam = *outpamP;
@@ -1017,7 +1055,7 @@ copyRaster(struct pam *       const inpamP,
     createColormapFinder(outpamP, colormap, colormapSize, &colorFinderP);
 
     if (floyd)
-        initFserr(inpamP, &fserr, randomize);
+        fserr_init(inpamP, &fserr, random);
 
     *missingCountP = 0;  /* initial value */
 
@@ -1030,9 +1068,9 @@ copyRaster(struct pam *       const inpamP,
                    depthAdjustment, colormap, colorFinderP, colorhash,
                    &usehash, floyd, defaultColor,
                    &fserr,  outrow, &missingCount);
-        
+
         *missingCountP += missingCount;
-        
+
         pnm_writepamrow(outpamP, outrow);
     }
     destroyColormapFinder(colorFinderP);
@@ -1046,10 +1084,10 @@ copyRaster(struct pam *       const inpamP,
 static void
 remap(FILE *             const ifP,
       const struct pam * const outpamCommonP,
-      tupletable         const colormap, 
+      tupletable         const colormap,
       unsigned int       const colormapSize,
       bool               const floyd,
-      bool               const randomize,
+      struct Random      const random,
       tuple              const defaultColor,
       bool               const verbose) {
 /*----------------------------------------------------------------------------
@@ -1075,7 +1113,7 @@ remap(FILE *             const ifP,
             */
 
         pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth));
-    
+
         outpam = *outpamCommonP;
         outpam.width  = inpam.width;
         outpam.height = inpam.height;
@@ -1086,13 +1124,13 @@ remap(FILE *             const ifP,
            convert the input to the output depth.
         */
         pnm_setminallocationdepth(&inpam, outpam.depth);
-    
+
         copyRaster(&inpam, &outpam, colormap, colormapSize, floyd,
-                   randomize, defaultColor, &missingCount);
-        
+                   random, defaultColor, &missingCount);
+
         if (verbose)
             pm_message("%u pixels not matched in color map", missingCount);
-        
+
         pnm_nextimage(ifP, &eof);
     }
 }
@@ -1130,7 +1168,7 @@ processMapFile(const char *   const mapFileName,
 
     pnm_freepamarray(maptuples, &mappam);
 
-    *outpamCommonP = mappam; 
+    *outpamCommonP = mappam;
     outpamCommonP->file = stdout;
 }
 
@@ -1142,7 +1180,7 @@ getSpecifiedMissingColor(struct pam * const pamP,
                          tuple *      const specColorP) {
 
     tuple specColor;
-                             
+
     specColor = pnm_allocpamtuple(pamP);
 
     if (colorName) {
@@ -1213,8 +1251,8 @@ main(int argc, const char * argv[] ) {
         break;
     }
 
-    remap(ifP, &outpamCommon, colormap, colormapSize, 
-          cmdline.floyd, !cmdline.norandom, defaultColor,
+    remap(ifP, &outpamCommon, colormap, colormapSize,
+          cmdline.floyd, cmdline.random, defaultColor,
           cmdline.verbose);
 
     pnm_freepamtuple(firstColor);
diff --git a/generator/pbmtext.c b/generator/pbmtext.c
index e25c6bbe..0fe8ad6a 100644
--- a/generator/pbmtext.c
+++ b/generator/pbmtext.c
@@ -19,6 +19,8 @@
 #include <limits.h>
 #include <assert.h>
 #include <setjmp.h>
+#include <locale.h>
+#include <wchar.h>
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
@@ -34,21 +36,64 @@ struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char * text;    /* text from command line or NULL if none */
+    const PM_WCHAR * text; /* text from command line or NULL if none */
     const char * font;    /* -font option value or NULL if none */
     const char * builtin; /* -builtin option value or NULL if none */
     float space;          /* -space option value or default */
     int lspace;           /* -lspace option value or default */
     unsigned int width;   /* -width option value or zero */
+    unsigned int wchar;   /* -wchar option specified  */
     unsigned int nomargins;  /* -nomargins option specified  */
-    unsigned int dryrun;  /* -dry-run option specified */ 
-    unsigned int verbose; /* -verbose option specified */
+    unsigned int dryrun;     /* -dry-run option specified */
+    unsigned int textdump;   /* -text-dump option specified */
+    unsigned int verbose;    /* -verbose option specified */
         /* undocumented option */
-    unsigned int dumpsheet; /* font data sheet in PBM format for -font */   
+    unsigned int dumpsheet; /* font data sheet in PBM format for -font */
 };
 
 
 
+static const PM_WCHAR *
+textFmCmdLine(int argc, const char ** argv) {
+
+    char * text;
+    PM_WCHAR * wtext;
+    unsigned int i;
+    unsigned int totaltextsize;
+
+    MALLOCARRAY(text, MAXLINECHARS+1);
+
+    if (!text)
+        pm_error("Unable to allocate memory for a buffer of up to %u "
+                 "characters of text", MAXLINECHARS);
+
+    text[0] = '\0';
+
+    for (i = 1, totaltextsize = 1; i < argc; ++i) {
+        if (i > 1) {
+            strcat(text, " ");
+        }
+        totaltextsize += strlen(argv[i]) + 1;
+        if (totaltextsize > MAXLINECHARS)
+            pm_error("input text too long");
+        strcat(text, argv[i]);
+    }
+    MALLOCARRAY(wtext, totaltextsize * sizeof(PM_WCHAR));
+
+    if (!wtext)
+        pm_error("Unable to allocate memory for a buffer of up to %u "
+                 "wide characters of text", totaltextsize);
+
+    for (i = 0; i < totaltextsize; ++i)
+        wtext[i] = (PM_WCHAR) text[i];
+
+    free(text);
+
+    return wtext;
+}
+
+
+
 static void
 parseCommandLine(int argc, const char ** argv,
                  struct CmdlineInfo * const cmdlineP) {
@@ -72,8 +117,10 @@ parseCommandLine(int argc, const char ** argv,
     OPTENT3(0, "lspace",     OPT_INT,    &cmdlineP->lspace,  NULL,   0);
     OPTENT3(0, "width",      OPT_UINT,   &cmdlineP->width,   NULL,   0);
     OPTENT3(0, "nomargins",  OPT_FLAG,   NULL, &cmdlineP->nomargins, 0);
+    OPTENT3(0, "wchar",      OPT_FLAG,   NULL, &cmdlineP->wchar,     0);
     OPTENT3(0, "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,   0);
     OPTENT3(0, "dry-run",    OPT_FLAG,   NULL, &cmdlineP->dryrun,    0);
+    OPTENT3(0, "text-dump",  OPT_FLAG,   NULL, &cmdlineP->textdump,  0);
     OPTENT3(0, "dump-sheet", OPT_FLAG,   NULL, &cmdlineP->dumpsheet, 0);
 
     /* Set the defaults */
@@ -90,7 +137,7 @@ parseCommandLine(int argc, const char ** argv,
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
     /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (cmdlineP->width > 0 && cmdlineP->nomargins == TRUE) {
+    if (cmdlineP->width > 0 && cmdlineP->nomargins) {
         pm_message("-nomargins has no effect when -width is specified");
         cmdlineP->nomargins = FALSE;
     } else if (cmdlineP->width > INT_MAX-10)
@@ -106,33 +153,25 @@ parseCommandLine(int argc, const char ** argv,
     else if (cmdlineP->lspace < -pbm_maxfontheight())
         pm_error("negative -lspace value too large");
 
+    if (cmdlineP->textdump) {
+        if (cmdlineP->dryrun)
+            pm_error("You cannot specify both -dry-run and -text-dump");
+        else if (cmdlineP->dumpsheet)
+            pm_error("You cannot specify both -dump-sheet and -text-dump");
+    }
+
+    if (cmdlineP->dryrun && cmdlineP->dumpsheet)
+        pm_error("You cannot specify both -dry-run and -dump-sheet");
+
     if (argc-1 == 0)
         cmdlineP->text = NULL;
-    else {
-        char *text;
-        int i;
-        int totaltextsize;
-
-        totaltextsize = 1;  /* initial value */
-
-        MALLOCARRAY(text, MAXLINECHARS+1);        
-
-        if (!text)
-            pm_error("Unable to allocate memory for a buffer of up to %u "
-                     "characters of text", MAXLINECHARS);
-
-        text[0] = '\0';
-        
-        for (i = 1; i < argc; ++i) {
-            if (i > 1) {
-                strcat(text, " ");
-            } 
-            totaltextsize += strlen(argv[i]) + 1;
-            if (totaltextsize > MAXLINECHARS)
-                pm_error("input text too long");
-            strcat(text, argv[i]);
-        }
-        cmdlineP->text = text;
+    else {  /* Text to render is part of command line */
+        if (cmdlineP->wchar)
+            pm_error("-wchar is not valid when text is from command line");
+
+        cmdlineP->text = textFmCmdLine(argc, argv);
+
+
     }
     free(option_def);
 }
@@ -140,7 +179,7 @@ parseCommandLine(int argc, const char ** argv,
 
 
 static void
-reportFont(struct font * const fontP) {
+reportFont(struct font2 * const fontP) {
 
     unsigned int n;
     unsigned int c;
@@ -150,9 +189,10 @@ reportFont(struct font * const fontP) {
                fontP->maxwidth, fontP->maxheight);
     pm_message("  Additional vert white space: %d pixels", fontP->y);
 
-    for (c = 0, n = 0; c < ARRAY_SIZE(fontP->glyph); ++c)
+    for (c = 0, n = 0; c <= fontP->maxglyph; ++c) {
         if (fontP->glyph[c])
             ++n;
+    }
 
     pm_message("  # characters: %u", n);
 }
@@ -162,7 +202,7 @@ reportFont(struct font * const fontP) {
 static struct font *
 fontFromFile(const char * const fileName) {
 
-    struct font * retval;
+    struct font  * retval;
 
     jmp_buf jmpbuf;
     int rc;
@@ -190,31 +230,72 @@ fontFromFile(const char * const fileName) {
 
 
 
+static struct font2 *
+font2FromFile(const char * const fileName) {
+
+    struct font2 * font2P;
+
+    jmp_buf jmpbuf;
+    int rc;
+
+    rc = setjmp(jmpbuf);
+
+    if (rc == 0) {
+        /* This is the normal program flow */
+        pm_setjmpbuf(&jmpbuf);
+
+        font2P = pbm_loadbdffont2(fileName, PM_FONT2_MAXGLYPH);
+
+        pm_setjmpbuf(NULL);
+    } else {
+        /* This is the second pass, after pbm_loadbdffont2 does a longjmp
+           because it fails.
+        */
+        pm_setjmpbuf(NULL);
+
+        pm_error("Failed to load font from file '%s'", fileName);
+    }
+
+    return font2P;
+}
+
+
+
 static void
 computeFont(struct CmdlineInfo const cmdline,
-            struct font **     const fontPP) {
+            struct font2 **    const fontPP) {
 
-    struct font * fontP;
+    struct font2 * font2P;
 
-    if (cmdline.font)
-        fontP = fontFromFile(cmdline.font);
+    if (cmdline.wchar && cmdline.font)
+        font2P = font2FromFile(cmdline.font);
     else {
-        if (cmdline.builtin)
-            fontP = pbm_defaultfont(cmdline.builtin);
-        else
-            fontP = pbm_defaultfont("bdf");
+        struct font  * fontP;
+
+        if (cmdline.font)
+            fontP = fontFromFile(cmdline.font);
+        else {
+            if (cmdline.builtin)
+                fontP = pbm_defaultfont(cmdline.builtin);
+            else
+                fontP = pbm_defaultfont("bdf");
+        }
+        font2P = pbm_expandbdffont(fontP);
     }
 
     if (cmdline.verbose)
-        reportFont(fontP);
+        reportFont(font2P);
 
-    *fontPP = fontP;
+    *fontPP = font2P;
 }
 
 
 
 struct Text {
-    char **      textArray;  /* malloc'ed */
+    PM_WCHAR **     textArray;  /* malloc'ed */
+        /* This is strictly characters that are in user's font - no control
+           characters, no undefined code points.
+        */
     unsigned int allocatedLineCount;
     unsigned int lineCount;
 };
@@ -248,7 +329,7 @@ freeTextArray(struct Text const text) {
     unsigned int line;
 
     for (line = 0; line < text.allocatedLineCount; ++line)
-        free((char **)text.textArray[line]);
+        free((PM_WCHAR **)text.textArray[line]);
 
     free(text.textArray);
 }
@@ -261,10 +342,10 @@ enum FixMode {SILENT, /* convert silently */
 
 
 static void
-fixControlChars(const char *  const input,
-                struct font * const fontP,
-                const char ** const outputP,
-                enum FixMode  const fixMode) {
+fixControlChars(const PM_WCHAR  * const input,
+                struct font2    * const fontP,
+                const PM_WCHAR ** const outputP,
+                enum FixMode      const fixMode) {
 /*----------------------------------------------------------------------------
    Return a translation of input[] that can be rendered as glyphs in
    the font 'fontP'.  Return it as newly malloced *outputP.
@@ -273,7 +354,7 @@ fixControlChars(const char *  const input,
 
    Remove any trailing newline.  (But leave intermediate ones as line
    delimiters).
-   
+
    Depending on value of fixMode, turn anything that isn't a code point
    in the font to a single space (which isn't guaranteed to be in the
    font either, of course).
@@ -289,10 +370,10 @@ fixControlChars(const char *  const input,
     unsigned int const tabSize = 8;
 
     unsigned int inCursor, outCursor;
-    char * output;      /* Output buffer.  Malloced */
+    PM_WCHAR * output;      /* Output buffer.  Malloced */
     size_t outputSize;  /* Currently allocated size of 'output' */
 
-    outputSize = strlen(input) + 1 + tabSize;
+    outputSize = wcslen(input) + 1 + tabSize;
         /* Leave room for one worst case tab expansion and NUL terminator */
     MALLOCARRAY(output, outputSize);
 
@@ -300,7 +381,8 @@ fixControlChars(const char *  const input,
         pm_error("Couldn't allocate %u bytes for a line of text.",
                  (unsigned)outputSize);
 
-    for (inCursor = 0, outCursor = 0; input[inCursor] != '\0'; ++inCursor) {
+    for (inCursor = 0, outCursor = 0; input[inCursor] != L'\0'; ++inCursor) {
+        PM_WCHAR const currentChar = input[inCursor];
         if (outCursor + 1 + tabSize > outputSize) {
             outputSize = outCursor + 1 + 4 * tabSize;
             REALLOCARRAY(output, outputSize);
@@ -308,29 +390,34 @@ fixControlChars(const char *  const input,
                 pm_error("Couldn't allocate %u bytes for a line of text.",
                          (unsigned)outputSize);
         }
-        if (input[inCursor] == '\n' && input[inCursor+1] == '\0') {
+        if (currentChar == L'\n' && input[inCursor+1] == L'\0') {
             /* This is a terminating newline.  We don't do those. */
-        } else if (input[inCursor] == '\t') { 
+        } else if (currentChar == L'\t') {
             /* Expand this tab into the right number of spaces. */
             unsigned int const nextTabStop =
                 (outCursor + tabSize) / tabSize * tabSize;
 
-        if (fontP->glyph[(unsigned char)' '] == NULL)
-            pm_error("space character not defined in font");
+            if (fontP->glyph[L' '] == NULL)
+                pm_error("space character not defined in font");
 
             while (outCursor < nextTabStop)
-                output[outCursor++] = ' ';
-        } else if (!fontP->glyph[(unsigned char)input[inCursor]]) {
+                output[outCursor++] = L' ';
+        } else if (currentChar > fontP->maxglyph ||
+                   !fontP->glyph[currentChar]) {
+        if (currentChar > PM_FONT2_MAXGLYPH)
+            pm_message("code point %X is beyond what this program "
+                       "can handle.  Max=%X",
+                       (unsigned int)currentChar, PM_FONT2_MAXGLYPH);
             /* Turn this unknown char into a single space. */
-            if (fontP->glyph[(unsigned char) ' '] == NULL)
+            if (fontP->glyph[L' '] == NULL)
                 pm_error("space character not defined in font");
             else if (fixMode == QUIT)
-                pm_error("character %d not defined in font",
-                         (unsigned int )input[inCursor] );
+                pm_error("code point %X not defined in font",
+                         (unsigned int) currentChar );
             else {
                 if (fixMode == WARN)
-                    pm_message("converting character %d to space",
-                               (unsigned int) input[inCursor] );
+                    pm_message("converting code point %X to space",
+                               (unsigned int) currentChar );
                 output[outCursor++] = ' ';
             }
         } else
@@ -338,7 +425,7 @@ fixControlChars(const char *  const input,
 
         assert(outCursor <= outputSize);
     }
-    output[outCursor++] = '\0';
+    output[outCursor++] = L'\0';
 
     assert(outCursor <= outputSize);
 
@@ -348,12 +435,12 @@ fixControlChars(const char *  const input,
 
 
 static void
-clearBackground(bit ** const bits, 
-                int    const cols, 
+clearBackground(bit ** const bits,
+                int    const cols,
                 int    const rows) {
 
     unsigned int row;
-    
+
     for (row = 0; row < rows; ++row) {
         unsigned int colChar;
         for (colChar = 0; colChar < pbm_packed_bytes(cols); ++colChar)
@@ -365,8 +452,8 @@ clearBackground(bit ** const bits,
 
 static void
 getEdges(double               const currentPosition,
-         char                 const currentChar,
-         const struct glyph * const glyphP, 
+         PM_WCHAR             const currentChar,
+         const struct glyph * const glyphP,
          int                  const currLeftEdge,
          double               const currRightEdge,
          int                * const newLeftEdgeP,
@@ -374,9 +461,9 @@ getEdges(double               const currentPosition,
 
     int leftEdge;
     double rightEdge;
-  
+
     if (glyphP == NULL)
-        pm_error("Unrenderable char: %c", currentChar);
+        pm_error("Unrenderable char: %04X", (unsigned int) currentChar);
     else {
         leftEdge  =  (int) MIN(currentPosition + glyphP->x, currLeftEdge);
         rightEdge =  MAX(currentPosition + glyphP->x + glyphP->width,
@@ -390,7 +477,7 @@ getEdges(double               const currentPosition,
 
 static void
 advancePosition(double               const currentPosition,
-                char                 const currentChar,
+                PM_WCHAR             const currentChar,
                 const struct glyph * const glyphP,
                 float                const space,
                 double               const accumulatedSpace,
@@ -408,14 +495,14 @@ advancePosition(double               const currentPosition,
     int const fullPixels = (int) (accumulatedSpace + space);
         /* round toward 0 */
     int const advance    = (int) glyphP->xadd + fullPixels;
-    
+
     if (advance < 0) {
         if (space < 0)
-            pm_error("Negative -space value too large"); 
+            pm_error("Negative -space value too large");
         else
             pm_error("Abnormal horizontal advance value %d "
-                     "for char '%c' 0x%x.",
-                     glyphP->xadd, currentChar, (unsigned int) currentChar);
+                     "for code point 0x%lx.",
+                     glyphP->xadd, (unsigned long int) currentChar);
     }
     else if (currentPosition + advance > INT_MAX)
         pm_error("Image is too wide");
@@ -429,11 +516,11 @@ advancePosition(double               const currentPosition,
 
 
 static void
-getLineDimensions(char                const line[],
-                  const struct font * const fontP, 
-                  float               const intercharacterSpace,
-                  double *            const rightEdgeP,
-                  int    *            const leftEdgeP) {
+getLineDimensions(PM_WCHAR             const line[],
+                  const struct font2 * const fontP,
+                  float                const intercharacterSpace,
+                  double *             const rightEdgeP,
+                  int    *             const leftEdgeP) {
 /*----------------------------------------------------------------------------
    Determine the left edge and right edge in pixels of the line of text
    line[] in the font *fontP, and return them as *leftEdgeP and *rightEdgeP.
@@ -451,7 +538,7 @@ getLineDimensions(char                const line[],
    This often happens with fixed-width font in which the white areas on the
    sides are not trimmed.
 -----------------------------------------------------------------------------*/
-    int cursor;  /* cursor into the line of text */
+    unsigned int cursor;  /* cursor into the line of text */
     double currentPosition;
         /* sum of xadd values and intercharacter space so far in line.  this
            is never negative.
@@ -471,11 +558,11 @@ getLineDimensions(char                const line[],
 
     leftEdge  = INT_MAX;  /* initial value */
     rightEdge = INT_MIN;  /* initial value */
-    
-    for (cursor = 0; line[cursor] != '\0'; ++cursor) {
-        char const currentChar = line[cursor];
-        struct glyph * const glyphP = 
-            fontP->glyph[(unsigned char) currentChar];
+
+    for (cursor = 0; line[cursor] != L'\0'; ++cursor) {
+        PM_WCHAR          const currentChar = line[cursor];
+        unsigned long int const glyphIndex  = (unsigned long int) currentChar;
+        struct glyph *    const glyphP      = fontP->glyph[glyphIndex];
 
         getEdges(currentPosition, currentChar, glyphP, leftEdge, rightEdge,
                  &leftEdge, &rightEdge);
@@ -485,7 +572,7 @@ getLineDimensions(char                const line[],
                         &currentPosition, &accumulatedIcs);
     }
 
-    if (line[0] == '\0') {     /* Empty line */
+    if (line[0] == L'\0') {     /* Empty line */
         leftEdge  = 0;
         rightEdge = 0.0;
     }
@@ -497,12 +584,12 @@ getLineDimensions(char                const line[],
 
 
 static void
-getCharsWithinWidth(char                const line[],
-                    const struct font * const fontP, 
-                    float               const intercharacter_space,
-                    unsigned int        const targetWidth,
-                    unsigned int      * const charCountP,
-                    int               * const leftEdgeP) {
+getCharsWithinWidth(PM_WCHAR             const line[],
+                    const struct font2 * const fontP,
+                    float                const intercharacter_space,
+                    unsigned int         const targetWidth,
+                    unsigned int       * const charCountP,
+                    int                * const leftEdgeP) {
 /*----------------------------------------------------------------------------
    Determine how many characters of text line[] fit into an image of target
    width targetWidth.
@@ -510,7 +597,7 @@ getCharsWithinWidth(char                const line[],
    *leftEdgeP will be negative if the leftmost character in the line has a
    "backup" distance and zero if it does not.
 -----------------------------------------------------------------------------*/
-    if (line[0] == '\0') {
+    if (line[0] == L'\0') {
         /* Empty line */
         *leftEdgeP = 0;
         *charCountP = 0;
@@ -527,13 +614,14 @@ getCharsWithinWidth(char                const line[],
 
         leftEdge     = INT_MAX;  /* initial value */
         rightEdge    = INT_MIN;  /* initial value */
-    
+
         for (cursor = 0, currentWidth = 0;
-             currentWidth <= targetWidth && line[cursor] != '\0';
+             currentWidth <= targetWidth && line[cursor] != L'\0';
              ++cursor) {
-            char const currentChar = line[cursor];
-            struct glyph * const glyphP = 
-                fontP->glyph[(unsigned char) currentChar];
+            PM_WCHAR const currentChar = line[cursor];
+            unsigned long int const glyphIndex =
+              (unsigned long int) currentChar;
+            struct glyph * const glyphP = fontP->glyph[glyphIndex];
 
             getEdges(currentPosition, currentChar, glyphP, leftEdge, rightEdge,
                      &leftEdge, &rightEdge);
@@ -562,7 +650,7 @@ getCharsWithinWidth(char                const line[],
 
 static void
 insertCharacter(const struct glyph * const glyphP,
-                int                  const toprow, 
+                int                  const toprow,
                 int                  const leftcol,
                 unsigned int         const cols,
                 unsigned int         const rows,
@@ -593,21 +681,21 @@ insertCharacter(const struct glyph * const glyphP,
             }
         }
     }
-}    
+}
 
 
 
 static void
-insertCharacters(bit **        const bits, 
-                 struct Text   const lp,
-                 struct font * const fontP, 
-                 int           const topmargin, 
-                 int           const leftmargin,
-                 float         const intercharacter_space,
-                 unsigned int  const cols,
-                 unsigned int  const rows,
-                 int           const lspace,
-                 bool          const fixedAdvance) {
+insertCharacters(bit **         const bits,
+                 struct Text    const lp,
+                 struct font2 * const fontP,
+                 int            const topmargin,
+                 int            const leftmargin,
+                 float          const intercharacter_space,
+                 unsigned int   const cols,
+                 unsigned int   const rows,
+                 int            const lspace,
+                 bool           const fixedAdvance) {
 /*----------------------------------------------------------------------------
    Render the text 'lp' into the image 'bits' using font *fontP and
    putting 'intercharacter_space' pixels between characters and
@@ -623,23 +711,24 @@ insertCharacters(bit **        const bits,
             /* accumulated intercharacter space so far in the line we
                are building.  Because the intercharacter space might
                not be an integer, we accumulate it here and realize
-               full pixels whenever we have more than one pixel. 
+               full pixels whenever we have more than one pixel.
             */
 
         row = topmargin + line * (fontP->maxheight + lspace);
         leftcol = leftmargin;
         accumulatedIcs = 0.0;  /* initial value */
-    
+
         for (cursor = 0; lp.textArray[line][cursor] != '\0'; ++cursor) {
-            char const currentChar = lp.textArray[line][cursor];
-            unsigned int const glyphIndex = (unsigned char) currentChar;
+            PM_WCHAR const currentChar = lp.textArray[line][cursor];
+            unsigned long int const glyphIndex =
+                (unsigned long int)currentChar;
             struct glyph * const glyphP = fontP->glyph[glyphIndex];
             int const toprow =
                 row + fontP->maxheight + fontP->y - glyphP->height - glyphP->y;
                 /* row number in image of top row in glyph */
 
             assert(glyphP != NULL);
-            
+
             insertCharacter(glyphP, toprow, leftcol, cols, rows, bits);
 
         if (fixedAdvance)
@@ -656,12 +745,12 @@ insertCharacters(bit **        const bits,
 
 static void
 flowText(struct Text    const inputText,
-         int            const targetWidth, 
-         struct font  * const fontP, 
+         int            const targetWidth,
+         struct font2 * const fontP,
          float          const intercharacterSpace,
          struct Text  * const outputTextP,
          unsigned int * const maxleftbP) {
-    
+
     unsigned int outputLineNum;
     unsigned int incursor;   /* cursor into the line we are reading */
     unsigned int const maxLineCount = 50; /* max output lines */
@@ -672,11 +761,11 @@ flowText(struct Text    const inputText,
     allocTextArray(outputTextP, maxLineCount, 0);
 
     for (incursor = 0, outputLineNum = 0;
-         inputText.textArray[0][incursor] != '\0'; ) {
+         inputText.textArray[0][incursor] != L'\0'; ) {
 
         unsigned int outcursor;
 
-        getCharsWithinWidth(&inputText.textArray[0][incursor], fontP, 
+        getCharsWithinWidth(&inputText.textArray[0][incursor], fontP,
                             intercharacterSpace, targetWidth,
                             &charCount, &leftEdge);
 
@@ -688,11 +777,11 @@ flowText(struct Text    const inputText,
 
         ++outputTextP->allocatedLineCount;
 
-        for (outcursor = 0; outcursor < charCount; ++outcursor, ++incursor) 
-            outputTextP->textArray[outputLineNum][outcursor] = 
+        for (outcursor = 0; outcursor < charCount; ++outcursor, ++incursor)
+            outputTextP->textArray[outputLineNum][outcursor] =
                 inputText.textArray[0][incursor];
 
-        outputTextP->textArray[outputLineNum][charCount] = '\0'; 
+        outputTextP->textArray[outputLineNum][charCount] = L'\0';
         ++outputLineNum;
         if (outputLineNum >= maxLineCount)
             pm_error("-width too small.  too many output lines");
@@ -706,9 +795,9 @@ flowText(struct Text    const inputText,
 
 
 static void
-truncateText(struct Text    const inputText, 
-             unsigned int   const targetWidth, 
-             struct font  * const fontP, 
+truncateText(struct Text    const inputText,
+             unsigned int   const targetWidth,
+             struct font2 * const fontP,
              float          const intercharacterSpace,
              unsigned int * const maxleftbP) {
 
@@ -717,18 +806,18 @@ truncateText(struct Text    const inputText,
     int leftExtreme = 0;
 
     for (lineNum = 0; lineNum < inputText.lineCount; ++lineNum) {
-        char * const currentLine = inputText.textArray[lineNum];
+        PM_WCHAR * const currentLine = inputText.textArray[lineNum];
 
         unsigned int charCount;
 
-        getCharsWithinWidth(currentLine, fontP, 
+        getCharsWithinWidth(currentLine, fontP,
                             intercharacterSpace, targetWidth,
                             &charCount, &leftEdge);
 
-        if (currentLine[charCount] != '\0') {
+        if (currentLine[charCount] != L'\0') {
             pm_message("truncating line %u from %u to %u characters",
-                       lineNum, (unsigned) strlen(currentLine), charCount); 
-            currentLine[charCount] = '\0';
+                       lineNum, (unsigned) wcslen(currentLine), charCount);
+            currentLine[charCount] = L'\0';
         }
 
         leftExtreme = MIN(leftEdge, leftExtreme);
@@ -739,11 +828,77 @@ truncateText(struct Text    const inputText,
 
 
 static void
-getText(char          const cmdlineText[], 
-        struct font * const fontP,
-        struct Text * const inputTextP,
-        enum FixMode  const fixMode) {
+fgetNarrowWideString(PM_WCHAR *    const widestring,
+                     unsigned int  const size,
+                     FILE *        const ifP,
+                     const char ** const errorP) {
+/*----------------------------------------------------------------------------
+  Return the next line from file *ifP, up to 'size' characters, as
+  *widestring.
+
+  Return error if we can't read the file, or file is at EOF.
+-----------------------------------------------------------------------------*/
+    int wideCode;
+        /* Width orientation for *ifP: positive means wide, negative means
+           byte, zero means undecided.
+        */
+
+    assert(widestring);
+    assert(size > 0);
+
+    wideCode = fwide(ifP, 0);
+    if (wideCode > 0) {
+        /* *ifP is wide-oriented */
+        wchar_t * rc;
+        rc = fgetws(widestring, size, ifP);
+        if (rc == NULL)
+            pm_asprintf(errorP,
+                        "fgetws() of max %u bytes failed or end of stream",
+                        size);
+        else
+            *errorP = NULL;
+    } else {
+        char * bufNarrow;
+        char * rc;
+
+        MALLOCARRAY_NOFAIL(bufNarrow, MAXLINECHARS+1);
+
+        rc = fgets(bufNarrow, size, ifP);
+        if (rc == NULL)
+            pm_asprintf(errorP, "EOF or error reading file");
+        else {
+            size_t cnt;
+
+            for (cnt = 0; cnt < size && bufNarrow[cnt] != '\0'; ++cnt)
+                widestring[cnt] = (PM_WCHAR)(unsigned char) bufNarrow[cnt];
 
+            widestring[cnt] = L'\0';
+            *errorP = NULL;
+        }
+        free(bufNarrow);
+    }
+}
+
+
+
+
+static void
+getText(PM_WCHAR       const cmdlineText[],
+        struct font2 * const fontP,
+        struct Text  * const inputTextP,
+        enum FixMode   const fixMode) {
+/*----------------------------------------------------------------------------
+   Get as *inputTextP the text to format, given that the text on the
+   command line (one word per command line argument, separated by spaces),
+   is 'cmdlineText'.
+
+   If 'cmdlineText' is null, that means to get the text from Standard Input.
+   Otherwise, 'cmdlineText' is that text.
+
+   But we return text as only renderable characters - characters in *fontP -
+   with control characters interpreted or otherwise fixed, according to
+   'fixMode'.
+-----------------------------------------------------------------------------*/
     struct Text inputText;
 
     if (cmdlineText) {
@@ -751,7 +906,7 @@ getText(char          const cmdlineText[],
         inputText.allocatedLineCount = 1;
         inputText.lineCount = 1;
         fixControlChars(cmdlineText, fontP,
-                        (const char**)&inputText.textArray[0], fixMode);
+                        (const PM_WCHAR**)&inputText.textArray[0], fixMode);
     } else {
         /* Read text from stdin. */
 
@@ -759,9 +914,10 @@ getText(char          const cmdlineText[],
             /* Maximum number of lines for which we presently have space in
                the text array
             */
-        char * buf;
-        char ** textArray;
+        PM_WCHAR *   buf;
+        PM_WCHAR **  textArray;
         unsigned int lineCount;
+        bool         eof;
 
         MALLOCARRAY(buf, MAXLINECHARS+1);
 
@@ -771,27 +927,38 @@ getText(char          const cmdlineText[],
 
         maxlines = 50;  /* initial value */
         MALLOCARRAY(textArray, maxlines);
-        
+
         if (!textArray)
             pm_error("Unable to allocate memory for a buffer for up to %u "
                      "lines of text", maxlines);
 
-        lineCount = 0;  /* initial value */
-        while (fgets(buf, MAXLINECHARS, stdin) != NULL) {
-            if (strlen(buf) + 1 >= MAXLINECHARS)
-                pm_error("A line of input text is longer than %u characters."
-                         "Cannot process", (unsigned int) MAXLINECHARS-1);
-            if (lineCount >= maxlines) {
-                maxlines *= 2;
-                REALLOCARRAY(textArray, maxlines);
-                if (textArray == NULL)
+        for (lineCount = 0, eof = false; !eof; ) {
+            const char * error;
+            fgetNarrowWideString(buf, MAXLINECHARS, stdin, &error);
+            if (error) {
+                /* We're lazy, so we treat any error as EOF */
+                pm_strfree(error);
+                eof = true;
+            } else {
+                if (wcslen(buf) + 1 >= MAXLINECHARS)
+                    pm_error(
+                        "Line %u (starting at zero) of input text "
+                        "is longer than %u characters."
+                        "Cannot process",
+                        lineCount, (unsigned int) MAXLINECHARS-1);
+                if (lineCount >= maxlines) {
+                    maxlines *= 2;
+                    REALLOCARRAY(textArray, maxlines);
+                    if (textArray == NULL)
+                        pm_error("out of memory");
+                }
+                fixControlChars(buf, fontP,
+                                (const PM_WCHAR **)&textArray[lineCount],
+                                fixMode);
+                if (textArray[lineCount] == NULL)
                     pm_error("out of memory");
+                ++lineCount;
             }
-            fixControlChars(buf, fontP,
-                            (const char **)&textArray[lineCount], fixMode);
-            if (textArray[lineCount] == NULL)
-                pm_error("out of memory");
-            ++lineCount;
         }
         inputText.textArray = textArray;
         inputText.lineCount = lineCount;
@@ -805,10 +972,10 @@ getText(char          const cmdlineText[],
 static void
 computeMargins(struct CmdlineInfo const cmdline,
                struct Text        const inputText,
-               struct font *      const fontP,
+               struct font2 *     const fontP,
                unsigned int *     const vmarginP,
                unsigned int *     const hmarginP) {
-       
+
     if (cmdline.nomargins) {
         *vmarginP = 0;
         *hmarginP = 0;
@@ -823,12 +990,12 @@ computeMargins(struct CmdlineInfo const cmdline,
     }
 }
 
-    
+
 
 static void
 formatText(struct CmdlineInfo const cmdline,
            struct Text        const inputText,
-           struct font *      const fontP,
+           struct font2 *     const fontP,
            unsigned int       const hmargin,
            struct Text *      const formattedTextP,
            unsigned int *     const maxleftb0P) {
@@ -858,20 +1025,20 @@ formatText(struct CmdlineInfo const cmdline,
 
 
 static void
-computeImageHeight(struct Text         const formattedText, 
-                   const struct font * const fontP,
-                   int                 const interlineSpace,
-                   unsigned int        const vmargin,
-                   unsigned int      * const rowsP) {
+computeImageHeight(struct Text          const formattedText,
+                   const struct font2 * const fontP,
+                   int                  const interlineSpace,
+                   unsigned int         const vmargin,
+                   unsigned int       * const rowsP) {
 
     if (interlineSpace < 0 && fontP->maxheight < -interlineSpace)
         pm_error("-lspace value (%d) negative and exceeds font height.",
-                 interlineSpace);     
+                 interlineSpace);
     else {
-        double const rowsD = 2 * (double) vmargin + 
-            (double) formattedText.lineCount * fontP->maxheight + 
+        double const rowsD = 2 * (double) vmargin +
+            (double) formattedText.lineCount * fontP->maxheight +
             (double) (formattedText.lineCount-1) * interlineSpace;
-        
+
         if (rowsD > INT_MAX-10)
             pm_error("Image height too large.");
         else
@@ -882,21 +1049,21 @@ computeImageHeight(struct Text         const formattedText,
 
 
 static void
-computeImageWidth(struct Text         const formattedText, 
-                  const struct font * const fontP,
-                  float               const intercharacterSpace,
-                  unsigned int        const hmargin,
-                  unsigned int *      const colsP,
-                  unsigned int *      const maxleftbP) {
+computeImageWidth(struct Text          const formattedText,
+                  const struct font2 * const fontP,
+                  float                const intercharacterSpace,
+                  unsigned int         const hmargin,
+                  unsigned int *       const colsP,
+                  unsigned int *       const maxleftbP) {
 
     if (intercharacterSpace < 0 && fontP->maxwidth < -intercharacterSpace)
         pm_error("negative -space value %.2f exceeds font width",
-                 intercharacterSpace);     
+                 intercharacterSpace);
     else {
         /* Find the widest line, and the one that backs up the most past
            the nominal start of the line.
         */
-    
+
         unsigned int lineNum;
         double rightExtreme;
         int leftExtreme;
@@ -908,7 +1075,7 @@ computeImageWidth(struct Text         const formattedText,
         for (lineNum = 0; lineNum < formattedText.lineCount;  ++lineNum) {
             double rightEdge;
             int leftEdge;
-            
+
             getLineDimensions(formattedText.textArray[lineNum], fontP,
                               intercharacterSpace,
                               &rightEdge, &leftEdge);
@@ -923,7 +1090,7 @@ computeImageWidth(struct Text         const formattedText,
             pm_error("Image width too large.");
         else
             *colsP = (unsigned int) colsD;
-    
+
         *maxleftbP = (unsigned int) - leftExtreme;
     }
 }
@@ -931,17 +1098,17 @@ computeImageWidth(struct Text         const formattedText,
 
 
 static void
-renderText(unsigned int  const cols,
-           unsigned int  const rows,
-           struct font * const fontP,
-           unsigned int  const hmargin,
-           unsigned int  const vmargin,
-           struct Text   const formattedText,
-           unsigned int  const maxleftb,
-           float         const space,
-           int           const lspace,
-           bool          const fixedAdvance,
-           FILE *        const ofP) {
+renderText(unsigned int   const cols,
+           unsigned int   const rows,
+           struct font2 * const fontP,
+           unsigned int   const hmargin,
+           unsigned int   const vmargin,
+           struct Text    const formattedText,
+           unsigned int   const maxleftb,
+           float          const space,
+           int            const lspace,
+           bool           const fixedAdvance,
+           FILE *         const ofP) {
 
     bit ** const bits = pbm_allocarray(pbm_packed_bytes(cols), rows);
 
@@ -949,7 +1116,7 @@ renderText(unsigned int  const cols,
     clearBackground(bits, cols, rows);
 
     /* Put the text in  */
-    insertCharacters(bits, formattedText, fontP, vmargin, hmargin + maxleftb, 
+    insertCharacters(bits, formattedText, fontP, vmargin, hmargin + maxleftb,
                      space, cols, rows, lspace, fixedAdvance);
 
     {
@@ -966,47 +1133,48 @@ renderText(unsigned int  const cols,
 
 
 
-static char const * sheetTextArray[] = { 
-"M \",/^_[`jpqy| M",
-"                ",
-"/  !\"#$%&'()*+ /",
-"< ,-./01234567 <",
-"> 89:;<=>?@ABC >",
-"@ DEFGHIJKLMNO @",
-"_ PQRSTUVWXYZ[ _",
-"{ \\]^_`abcdefg {",
-"} hijklmnopqrs }",
-"~ tuvwxyz{|}~  ~",
-"                ",
-"M \",/^_[`jpqy| M" };
+static PM_WCHAR const * sheetTextArray[] = {
+L"M \",/^_[`jpqy| M",
+L"                ",
+L"/  !\"#$%&'()*+ /",
+L"< ,-./01234567 <",
+L"> 89:;<=>?@ABC >",
+L"@ DEFGHIJKLMNO @",
+L"_ PQRSTUVWXYZ[ _",
+L"{ \\]^_`abcdefg {",
+L"} hijklmnopqrs }",
+L"~ tuvwxyz{|}~  ~",
+L"                ",
+L"M \",/^_[`jpqy| M" };
 
 
 
 static void
-validateText(const char ** const textArray,
-             struct font * const fontP) {
+validateText(const PM_WCHAR ** const textArray,
+             struct font2    * const fontP) {
 /*----------------------------------------------------------------------------
    Abort the program if there are characters in 'textArray' which cannot be
    rendered in font *fontP.
 -----------------------------------------------------------------------------*/
-    const char * output;
+    const PM_WCHAR * output;
     unsigned int textRow;
 
     for (textRow = 0; textRow < 12; ++textRow)
         fixControlChars(textArray[textRow], fontP, &output, QUIT);
 
-    pm_strfree(output);
+    free((PM_WCHAR *)output);
 }
 
 
 
 static void
-renderSheet(struct font * const fontP,
-            FILE *        const ofP) {
+renderSheet(struct font2 * const fontP,
+            FILE *         const ofP) {
 
     int const cols  = fontP->maxwidth  * 16;
     int const rows  = fontP->maxheight * 12;
-    struct Text const sheetText = { (char ** const) sheetTextArray, 12, 12};
+    struct Text const sheetText =
+        { (PM_WCHAR ** const) sheetTextArray, 12, 12};
 
     validateText(sheetTextArray, fontP);
 
@@ -1020,15 +1188,49 @@ static void
 dryrunOutput(unsigned int const cols,
              unsigned int const rows,
              FILE *       const ofP) {
- 
-    fprintf(ofP, "%u %u\n", cols, rows); 
+
+    fprintf(ofP, "%u %u\n", cols, rows);
+}
+
+
+
+static void
+textDumpOutput(struct Text   const lp,
+               FILE *        const ofP) {
+/*----------------------------------------------------------------------------
+   Output the text 'lp' as characters.  (Do not render.)
+
+   Note that the output stream is wide-oriented; it cannot be mixed with
+   narrow-oriented output.  The libnetpbm library functions are
+   narrow-oriented.  Thus, when this output is specified, it must not be mixed
+   with any output from the library; it should be the sole output.
+-----------------------------------------------------------------------------*/
+    int rc;
+
+    rc = fwide(ofP, 1);
+    if (rc != 1) {
+        /* This occurs when narrow-oriented output to ofP happens before we
+           get here.
+        */
+        pm_error("Failed to set output stream to wide "
+                 "(fwide() returned %d.  Maybe the output file "
+                 "was written in narrow mode before this program was invoked?",
+                 rc);
+    } else {
+        unsigned int line;  /* Line number in input text */
+
+        for (line = 0; line < lp.lineCount; ++line) {
+            fputws(lp.textArray[line], ofP);
+            fputwc(L'\n', ofP);
+        }
+    }
 }
 
 
 
 static void
 pbmtext(struct CmdlineInfo const cmdline,
-        struct font *      const fontP,
+        struct font2 *     const fontP,
         FILE *             const ofP) {
 
     unsigned int rows, cols;
@@ -1051,7 +1253,7 @@ pbmtext(struct CmdlineInfo const cmdline,
 
     if (formattedText.lineCount == 0)
         pm_error("No input text");
-    
+
     computeImageHeight(formattedText, fontP, cmdline.lspace, vmargin, &rows);
 
     computeImageWidth(formattedText, fontP, cmdline.space,
@@ -1078,7 +1280,9 @@ pbmtext(struct CmdlineInfo const cmdline,
 
     if (cmdline.dryrun)
         dryrunOutput(cols, rows, ofP);
-    else 
+    else if (cmdline.textdump)
+        textDumpOutput(formattedText, ofP);
+    else
         renderText(cols, rows, fontP, hmargin, vmargin, formattedText,
                    maxleftb, cmdline.space, cmdline.lspace, FALSE, ofP);
 
@@ -1091,12 +1295,26 @@ int
 main(int argc, const char *argv[]) {
 
     struct CmdlineInfo cmdline;
-    struct font * fontP;
+    struct font2 * fontP;
 
     pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
-    
+
+    if (cmdline.wchar) {
+        char * newLocale;
+        newLocale = setlocale(LC_ALL, "");
+        if (!newLocale)
+            pm_error("Failed to set locale (LC_ALL) from environemt");
+
+        /* Orient standard input stream to wide */
+        fwide(stdin,  1);
+    } else
+        fwide(stdin, -1);
+
+    if (cmdline.verbose)
+        pm_message("LC_CTYPE is set to '%s'", setlocale(LC_CTYPE, NULL) );
+
     computeFont(cmdline, &fontP);
 
     if (cmdline.dumpsheet)
@@ -1104,6 +1322,10 @@ main(int argc, const char *argv[]) {
     else
         pbmtext(cmdline, fontP, stdout);
 
+    /* Note that *fontP is unfreeable.  See pbm_loadbdffont2,
+       pbm_expandbdffont
+    */
+
     pm_close(stdout);
 
     return 0;
diff --git a/generator/ppmpat.c b/generator/ppmpat.c
index 6d9cb1ca..908c200f 100644
--- a/generator/ppmpat.c
+++ b/generator/ppmpat.c
@@ -75,7 +75,7 @@ validateColorCount(Pattern      const basePattern,
                    unsigned int const colorCount) {
 
     if (colorCount == 0)
-        pm_error("-color: no colors specified"); 
+        pm_error("-color: no colors specified");
 
     switch (basePattern) {
     case PAT_GINGHAM2:
@@ -84,7 +84,7 @@ validateColorCount(Pattern      const basePattern,
         if (colorCount != 2)
             pm_error("Wrong number of colors: %u. "
                      "2 colors are required for the specified pattern.",
-                     colorCount); 
+                     colorCount);
         break;
     case PAT_GINGHAM3:
     case PAT_MADRAS:
@@ -93,14 +93,14 @@ validateColorCount(Pattern      const basePattern,
         if (colorCount != 3)
             pm_error("Wrong number of colors: %u. "
                      "3 colors are required for the specified pattern.",
-                     colorCount); 
+                     colorCount);
         break;
     case PAT_POLES:
         if (colorCount < 2)
-            pm_error("Too few colors: %u. " 
+            pm_error("Too few colors: %u. "
                      "At least 2 colors are required "
                      "for the specified pattern.",
-                     colorCount); 
+                     colorCount);
         break;
     case PAT_SQUIG:
     case PAT_CAMO:
@@ -109,7 +109,7 @@ validateColorCount(Pattern      const basePattern,
             pm_error("Wrong number of colors: %u. "
                      "At least 3 colors are required "
                      "for the specified pattern.",
-                     colorCount); 
+                     colorCount);
         break;
 
     case PAT_SPIRO2:
@@ -132,7 +132,7 @@ parseColorOpt(const char ** const colorText,
     "-color=rgb:ff/ff/ff,rgb:00/00/00,rgb:80/80/ff"
 
     Input:
-      Color name/value string-list: colorText[] 
+      Color name/value string-list: colorText[]
 
     Output values:
       Color array: colorTableP->color[]
@@ -154,7 +154,7 @@ parseColorOpt(const char ** const colorText,
     for (i = 0; i < colorCount; ++i)
         inColor[i] = ppm_parsecolor(colorText[i], PPM_MAXMAXVAL);
 
-    validateColorCount(basePattern, colorCount); 
+    validateColorCount(basePattern, colorCount);
 
     colorTableP->count = colorCount;
     colorTableP->index = 0;  /* initial value */
@@ -309,7 +309,7 @@ parseCommandLine(int argc, const char ** argv,
 
 static void
 freeCmdline(struct CmdlineInfo const cmdline) {
-    
+
     if (cmdline.colorSpec)
         free(cmdline.colorTable.color);
 }
@@ -330,7 +330,7 @@ validateComputableDimensions(unsigned int const cols,
       PPMD functions use signed integers for pixel positions
       (because they allow you to specify points off the canvas).
     */
-      
+
     if (cols > INT_MAX/4 || rows > INT_MAX/4 || rows > INT_MAX/cols)
         pm_error("Width and/or height are way too large: %u x %u",
                  cols, rows);
@@ -348,7 +348,7 @@ randomColor(pixval const maxval) {
                rand() % (maxval + 1),
                rand() % (maxval + 1)
         );
-    
+
     return p;
 }
 
@@ -403,12 +403,12 @@ averageTwoColors(pixel const p1,
 static ppmd_drawproc average_drawproc;
 
 static void
-average_drawproc(pixel **     const pixels, 
-                 int          const cols, 
-                 int          const rows, 
-                 pixval       const maxval, 
-                 int          const col, 
-                 int          const row, 
+average_drawproc(pixel **     const pixels,
+                 int          const cols,
+                 int          const rows,
+                 pixval       const maxval,
+                 int          const col,
+                 int          const row,
                  const void * const clientdata) {
 
     if (col >= 0 && col < cols && row >= 0 && row < rows)
@@ -466,7 +466,7 @@ randomAnticamoColor(pixval const maxval) {
     case 3:
         PPM_ASSIGN(p, rand() % v2, rand() % v1 + v3, rand() % v2);
         break;
-        
+
     case 4:
     case 5:
         PPM_ASSIGN(p, rand() % v2, rand() % v2, rand() % v1 + v3);
@@ -490,7 +490,7 @@ randomAnticamoColor(pixval const maxval) {
         PPM_ASSIGN(p, rand() % v1 + v3, rand() % v1 + v3, rand() % v2);
         break;
     }
-    
+
     return p;
 }
 
@@ -519,7 +519,7 @@ randomCamoColor(pixval const maxval) {
         /* dark green */
         PPM_ASSIGN(p, rand() % v2, rand() % v2 + 3 * v1, rand() % v2);
         break;
-    
+
     case 6:
     case 7:
         /* brown */
@@ -577,10 +577,11 @@ camoFill(pixel **         const pixels,
          struct fillobj * const fh,
          ColorTable     * const colorTableP,
          bool             const antiflag) {
-         
+
     pixel color;
 
     if (colorTableP->count > 0) {
+        assert(colorTableP->index < colorTableP->count);
         color = colorTableP->color[colorTableP->index];
         nextColorBg(colorTableP);
     } else if (antiflag)
@@ -589,7 +590,7 @@ camoFill(pixel **         const pixels,
         color = randomCamoColor(maxval);
 
     ppmd_fill(pixels, cols, rows, maxval, fh, PPMD_NULLDRAWPROC, &color);
-}        
+}
 
 
 
@@ -620,9 +621,9 @@ computeXsYs(int *        const xs,
     double const b = rnduni() * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) +
         MIN_ELLIPSE_FACTOR;
     double const theta = rnduni() * 2.0 * M_PI;
-    
+
     unsigned int p;
-        
+
     for (p = 0; p < pointCt; ++p) {
         double const c = rnduni() * (MAX_POINT_FACTOR - MIN_POINT_FACTOR) +
             MIN_POINT_FACTOR;
@@ -673,9 +674,9 @@ camo(pixel **     const pixels,
         ppmd_polyspline(
             pixels, cols, rows, maxval, x0, y0, pointCt, xs, ys, x0, y0,
             ppmd_fill_drawproc, fh);
-        
+
         camoFill(pixels, cols, rows, maxval, fh, colorTableP, antiflag);
-        
+
         ppmd_fill_destroy(fh);
     }
 }
@@ -840,7 +841,7 @@ madras(pixel **     const pixels,
         pixels, cols, rows, maxval, 9 * cols2 + 3 * cols3 + cols6a + cols6b, 0,
         cols2, rows, PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
-        pixels, cols, rows, maxval, 10 * cols2 + 3 * cols3 + cols6a + cols6b, 
+        pixels, cols, rows, maxval, 10 * cols2 + 3 * cols3 + cols6a + cols6b,
         0, cols3, rows, PPMD_NULLDRAWPROC, &fore1color);
 
     /* Woof. */
@@ -890,7 +891,7 @@ madras(pixel **     const pixels,
         pixels, cols, rows, maxval, 0, 9 * rows2 + 3 * rows3 + rows6a + rows6b,
         cols, rows2, average_drawproc, &backcolor);
     ppmd_filledrectangle(
-        pixels, cols, rows, maxval, 0, 
+        pixels, cols, rows, maxval, 0,
         10 * rows2 + 3 * rows3 + rows6a + rows6b,
         cols, rows3, average_drawproc, &fore2color);
 }
@@ -1059,7 +1060,7 @@ placeAndColorPolesRandomly(int *        const xs,
                            unsigned int const cols,
                            unsigned int const rows,
                            pixval       const maxval,
-                           ColorTable * const colorTableP, 
+                           ColorTable * const colorTableP,
                            unsigned int const poleCt) {
 
     unsigned int i;
@@ -1085,8 +1086,8 @@ assignInterpolatedColor(pixel * const resultP,
                         double  const dist1,
                         pixel   const color2,
                         double  const dist2) {
-    
-    if (dist1 == 0) 
+
+    if (dist1 == 0)
         /* pixel is a pole */
         *resultP = color1;
     else {
@@ -1095,7 +1096,7 @@ assignInterpolatedColor(pixel * const resultP,
         pixval const r = (PPM_GETR(color1)*dist2 + PPM_GETR(color2)*dist1)/sum;
         pixval const g = (PPM_GETG(color1)*dist2 + PPM_GETG(color2)*dist1)/sum;
         pixval const b = (PPM_GETB(color1)*dist2 + PPM_GETB(color2)*dist1)/sum;
-        
+
         PPM_ASSIGN(*resultP, r, g, b);
     }
 }
@@ -1110,7 +1111,7 @@ poles(pixel **     const pixels,
       pixval       const maxval) {
 
     unsigned int const poleCt = MAX(2, MIN(MAXPOLES, cols * rows / 30000));
-    
+
     int xs[MAXPOLES], ys[MAXPOLES];
     pixel colors[MAXPOLES];
     unsigned int row;
@@ -1176,7 +1177,7 @@ validateSquigAspect(unsigned int const cols,
     if (cols / rows >= 25 || rows / cols >= 25)
         pm_error("Image too narrow.  Aspect ratio: %u/%u=%f "
                  "is outside accepted range: 0.04 - 25.0",
-                 cols, rows, (float)cols/rows ); 
+                 cols, rows, (float)cols/rows );
 
 }
 
@@ -1194,10 +1195,10 @@ vectorSum(ppmd_point const a,
 static ppmd_drawprocp sqMeasureCircleDrawproc;
 
 static void
-sqMeasureCircleDrawproc(pixel**      const pixels, 
-                        unsigned int const cols, 
-                        unsigned int const rows, 
-                        pixval       const maxval, 
+sqMeasureCircleDrawproc(pixel**      const pixels,
+                        unsigned int const cols,
+                        unsigned int const rows,
+                        pixval       const maxval,
                         ppmd_point   const p,
                         const void * const clientdata) {
 
@@ -1213,10 +1214,10 @@ sqMeasureCircleDrawproc(pixel**      const pixels,
 static ppmd_drawprocp sqRainbowCircleDrawproc;
 
 static void
-sqRainbowCircleDrawproc(pixel **     const pixels, 
-                        unsigned int const cols, 
-                        unsigned int const rows, 
-                        pixval       const maxval, 
+sqRainbowCircleDrawproc(pixel **     const pixels,
+                        unsigned int const cols,
+                        unsigned int const rows,
+                        pixval       const maxval,
                         ppmd_point   const p,
                         const void * const clientdata) {
 
@@ -1254,7 +1255,7 @@ chooseSqPoleColors(ColorTable * const colorTableP,
         *color3P = randomBrightColor(maxval);
     }
 }
-    
+
 
 
 static void
@@ -1321,7 +1322,7 @@ clearBackgroundSquig(pixel **     const pixels,
     if (colorTableP->count > 0) {
         color = colorTableP->color[0];
         colorTableP->index = 1;
-    } else 
+    } else
         PPM_ASSIGN(color, 0, 0, 0);
 
     ppmd_filledrectangle(
@@ -1340,7 +1341,7 @@ chooseWrapAroundPoint(unsigned int const cols,
                       ppmd_point * const p1P,
                       ppmd_point * const p2P,
                       ppmd_point * const p3P) {
-                      
+
     switch (rand() % 4) {
     case 0:
         p1P->x = rand() % cols;
@@ -1428,7 +1429,7 @@ squig(pixel **     const pixels,
     int i;
 
     validateSquigAspect(cols, rows);
-    
+
     clearBackgroundSquig(pixels, cols, rows, colorTableP, maxval);
 
     /* Draw the squigs. */
@@ -1491,11 +1492,11 @@ main(int argc, const char ** argv) {
     pixel ** pixels;
 
     pm_proginit(&argc, argv);
-    
+
     parseCommandLine(argc, argv, &cmdline);
 
     validateComputableDimensions(cmdline.width, cmdline.height);
-    
+
     srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
 
     pixels = ppm_allocarray(cmdline.width, cmdline.height);
diff --git a/generator/ppmrainbow b/generator/ppmrainbow
index a4f90a09..e8a329ff 100755
--- a/generator/ppmrainbow
+++ b/generator/ppmrainbow
@@ -49,7 +49,7 @@ sub doVersionHack($) {
 sub fatal($) {
     my ($msg) = @_;
 
-    print(STDERR "$msg\n");
+    print(STDERR "ppmrainbow: $msg\n");
     exit(1);
 }
 
@@ -115,7 +115,7 @@ while (@colorlist >= 2) {
     my $rc = system("$verboseCommand pgmramp -lr $w $Thgt | " .
                     "pgmtoppm \"$colorlist[0]-$colorlist[1]\" >$outfile");
     if ($rc != 0) {
-        fatal("pgmramp|pgmtoppm failed.");
+        fatal("pgmramp|pgmtoppm pipe failed.");
     }
     $widthRemaining -= $w;
     $n++;
diff --git a/lib/libpam.c b/lib/libpam.c
index fa1be8f4..a8f140b3 100644
--- a/lib/libpam.c
+++ b/lib/libpam.c
@@ -12,7 +12,7 @@
    offset stuff.
 */
 #define _FILE_OFFSET_BITS 64
-#define _LARGE_FILES  
+#define _LARGE_FILES
 #define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
@@ -108,7 +108,7 @@ validateComputableSize(struct pam * const pamP) {
                  INT_MAX - depth * sizeof(tuple *))
             pm_error("image width and depth (%u, %u) too large "
                      "to be processed.", pamP->width, depth);
-        
+
         if (depth > INT_MAX - 2)
             pm_error("image depth (%u) too large to be processed", depth);
         if (pamP->width > INT_MAX - 2)
@@ -130,7 +130,7 @@ pnm_allocpamtuple(const struct pam * const pamP) {
     retval = malloc(allocationDepth(pamP) * sizeof(retval[0]));
 
     if (retval == NULL)
-        pm_error("Out of memory allocating %u-plane tuple", 
+        pm_error("Out of memory allocating %u-plane tuple",
                  allocationDepth(pamP));
 
     return retval;
@@ -139,15 +139,15 @@ pnm_allocpamtuple(const struct pam * const pamP) {
 
 
 int
-pnm_tupleequal(const struct pam * const pamP, 
-               tuple              const comparand, 
+pnm_tupleequal(const struct pam * const pamP,
+               tuple              const comparand,
                tuple              const comparator) {
 
     unsigned int plane;
     bool equal;
 
     equal = TRUE;  /* initial value */
-    for (plane = 0; plane < pamP->depth; ++plane) 
+    for (plane = 0; plane < pamP->depth; ++plane)
         if (comparand[plane] != comparator[plane])
             equal = FALSE;
 
@@ -173,11 +173,11 @@ pnm_assigntuple(const struct pam * const pamP,
 static void
 scaleTuple(const struct pam * const pamP,
            tuple              const dest,
-           tuple              const source, 
+           tuple              const source,
            sample             const newmaxval) {
 
     unsigned int plane;
-    for (plane = 0; plane < pamP->depth; ++plane) 
+    for (plane = 0; plane < pamP->depth; ++plane)
         dest[plane] = pnm_scalesample(source[plane], pamP->maxval, newmaxval);
 }
 
@@ -186,7 +186,7 @@ scaleTuple(const struct pam * const pamP,
 void
 pnm_scaletuple(const struct pam * const pamP,
                tuple              const dest,
-               tuple              const source, 
+               tuple              const source,
                sample             const newmaxval) {
 
     scaleTuple(pamP, dest, source, newmaxval);
@@ -195,7 +195,7 @@ pnm_scaletuple(const struct pam * const pamP,
 
 
 void
-pnm_createBlackTuple(const struct pam * const pamP, 
+pnm_createBlackTuple(const struct pam * const pamP,
                      tuple *            const blackTupleP) {
 /*----------------------------------------------------------------------------
    Create a "black" tuple.  By that we mean a tuple all of whose elements
@@ -205,7 +205,7 @@ pnm_createBlackTuple(const struct pam * const pamP,
 
     *blackTupleP = pnm_allocpamtuple(pamP);
 
-    for (i = 0; i < pamP->depth; ++i) 
+    for (i = 0; i < pamP->depth; ++i)
         (*blackTupleP)[i] = 0;
 }
 
@@ -226,14 +226,14 @@ allocPamRow(const struct pam * const pamP) {
     tuple * tuplerow;
 
     tuplerow = malloc(pamP->width * (sizeof(tuple *) + bytesPerTuple));
-                      
+
     if (tuplerow != NULL) {
         /* Now we initialize the pointers to the individual tuples
-           to make this a regulation C two dimensional array.  
+           to make this a regulation C two dimensional array.
         */
         char * p;
         unsigned int col;
-        
+
         p = (char*) (tuplerow + pamP->width);  /* location of Tuple 0 */
         for (col = 0; col < pamP->width; ++col) {
                 tuplerow[col] = (tuple) p;
@@ -261,7 +261,7 @@ pnm_allocpamrow(const struct pam * const pamP) {
 
 
 
-static unsigned int 
+static unsigned int
 rowimagesize(const struct pam * const pamP) {
 
     /* If repeatedly calculating this turns out to be a significant
@@ -271,7 +271,7 @@ rowimagesize(const struct pam * const pamP) {
 
     if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE)
         return pbm_packed_bytes(pamP->width);
-    else 
+    else
         return (pamP->width * pamP->bytes_per_sample * pamP->depth);
 }
 
@@ -307,7 +307,7 @@ pnm_freerowimage(unsigned char * const rowimage) {
 
 
 
-void 
+void
 pnm_scaletuplerow(const struct pam * const pamP,
                   tuple *            const destRow,
                   tuple *            const sourceRow,
@@ -321,26 +321,26 @@ pnm_scaletuplerow(const struct pam * const pamP,
             for (col = 0; col < pamP->width; ++col)
                 pnm_assigntuple(pamP, destRow[col], sourceRow[col]);
         }
-    } else {        
+    } else {
         unsigned int col;
-        for (col = 0; col < pamP->width; ++col) 
+        for (col = 0; col < pamP->width; ++col)
             scaleTuple(pamP, destRow[col], sourceRow[col], newMaxval);
     }
 }
 
- 
+
 
 tuple **
 pnm_allocpamarray(const struct pam * const pamP) {
-    
+
     tuple **tuplearray;
 
     /* If the speed of this is ever an issue, it might be sped up a little
        by allocating one large chunk.
     */
-    
+
     MALLOCARRAY(tuplearray, pamP->height);
-    if (tuplearray == NULL) 
+    if (tuplearray == NULL)
         pm_error("Out of memory allocating the row pointer section of "
                  "a %u row array", pamP->height);
     else {
@@ -353,14 +353,14 @@ pnm_allocpamarray(const struct pam * const pamP) {
             if (tuplearray[row] == NULL) {
                 unsigned int freerow;
                 outOfMemory = TRUE;
-                
+
                 for (freerow = 0; freerow < row; ++freerow)
                     pnm_freepamrow(tuplearray[row]);
             }
         }
         if (outOfMemory) {
             free(tuplearray);
-            
+
             pm_error("Out of memory allocating the %u rows %u columns wide by "
                      "%u planes deep", pamP->height, pamP->width,
                      allocationDepth(pamP));
@@ -383,7 +383,7 @@ pnm_freepamarray(tuple ** const tuplearray, const struct pam * const pamP) {
 
 
 
-void 
+void
 pnm_setminallocationdepth(struct pam * const pamP,
                           unsigned int const allocationDepth) {
 
@@ -394,7 +394,7 @@ pnm_setminallocationdepth(struct pam * const pamP,
                  pamP->len, (unsigned)PAM_STRUCT_SIZE(allocation_depth));
 
     pamP->allocation_depth = MAX(allocationDepth, pamP->depth);
-        
+
     validateComputableSize(pamP);
 }
 
@@ -402,13 +402,13 @@ pnm_setminallocationdepth(struct pam * const pamP,
 
 void
 pnm_setpamrow(const struct pam * const pamP,
-              tuple *            const tuplerow, 
+              tuple *            const tuplerow,
               sample             const value) {
 
     int col;
     for (col = 0; col < pamP->width; ++col) {
         int plane;
-        for (plane = 0; plane < pamP->depth; ++plane) 
+        for (plane = 0; plane < pamP->depth; ++plane)
             tuplerow[col][plane] = value;
     }
 }
@@ -421,9 +421,9 @@ pnm_setpamrow(const struct pam * const pamP,
 
 static void
 parseHeaderLine(const char buffer[],
-                char label[MAX_LABEL_LENGTH+1], 
+                char label[MAX_LABEL_LENGTH+1],
                 char value[MAX_VALUE_LENGTH+1]) {
-    
+
     int buffer_curs;
 
     buffer_curs = 0;
@@ -435,12 +435,12 @@ parseHeaderLine(const char buffer[],
         int label_curs;
         label_curs = 0;
         while (!ISSPACE(buffer[buffer_curs]) && buffer[buffer_curs] != '\0') {
-            if (label_curs < MAX_LABEL_LENGTH) 
+            if (label_curs < MAX_LABEL_LENGTH)
                 label[label_curs++] = buffer[buffer_curs];
             buffer_curs++;
         }
         label[label_curs] = '\0';  /* null terminate it */
-    }    
+    }
 
     /* Skip white space between label and value */
     while (ISSPACE(buffer[buffer_curs])) buffer_curs++;
@@ -452,7 +452,7 @@ parseHeaderLine(const char buffer[],
         /* Remove trailing white space from value[] */
         int value_curs;
         value_curs = strlen(value)-1;
-        while (value_curs >= 0 && ISSPACE(value[value_curs])) 
+        while (value_curs >= 0 && ISSPACE(value[value_curs]))
             value[value_curs--] = '\0';
     }
 }
@@ -494,10 +494,10 @@ parseHeaderUint(const char *   const valueString,
         if (errno != 0)
             pm_error("Too-large value for %s in "
                      "PAM file header: '%s'", name, valueString);
-        else if (*endptr != '\0') 
+        else if (*endptr != '\0')
             pm_error("Non-numeric value for %s in "
                      "PAM file header: '%s'", name, valueString);
-        else if (valueNum < 0) 
+        else if (valueNum < 0)
             pm_error("Negative value for %s in "
                      "PAM file header: '%s'", name, valueString);
         else if ((unsigned int)valueNum != valueNum)
@@ -583,7 +583,7 @@ processHeaderLine(char                const buffer[],
             }
             pamP->tuple_type[sizeof(pamP->tuple_type)-1] = '\0';
         }
-    } else 
+    } else
         pm_error("Unrecognized header line type: '%s'.  "
                  "Possible missing ENDHDR line?", label);
 }
@@ -644,13 +644,13 @@ readpaminitrest(struct pam * const pamP) {
 
     comments = strdup("");
 
-    { 
+    {
         int c;
-        /* Read off rest of 1st line -- probably just the newline after the 
-           magic number 
+        /* Read off rest of 1st line -- probably just the newline after the
+           magic number
         */
         while ((c = getc(pamP->file)) != -1 && c != '\n');
-    }    
+    }
 
     while (!headerSeen.endhdr) {
         char buffer[256];
@@ -665,7 +665,7 @@ readpaminitrest(struct pam * const pamP) {
                 appendComment(&comments, buffer);
             else if (pm_stripeq(buffer, ""));
                 /* Ignore it; it's a blank line */
-            else 
+            else
                 processHeaderLine(buffer, pamP, &headerSeen);
         }
     }
@@ -681,13 +681,13 @@ readpaminitrest(struct pam * const pamP) {
     if (!headerSeen.maxval)
         pm_error("No MAXVAL header line in PAM header");
 
-    if (pamP->height == 0) 
+    if (pamP->height == 0)
         pm_error("HEIGHT value is zero in PAM header");
-    if (pamP->width == 0) 
+    if (pamP->width == 0)
         pm_error("WIDTH value is zero in PAM header");
-    if (pamP->depth == 0) 
+    if (pamP->depth == 0)
         pm_error("DEPTH value is zero in PAM header");
-    if (pamP->maxval == 0) 
+    if (pamP->maxval == 0)
         pm_error("MAXVAL value is zero in PAM header");
     if (pamP->maxval > PAM_OVERALL_MAXVAL)
         pm_error("MAXVAL value (%lu) in PAM header is greater than %u",
@@ -697,9 +697,9 @@ readpaminitrest(struct pam * const pamP) {
 
 
 void
-pnm_readpaminitrestaspnm(FILE * const fileP, 
-                         int *  const colsP, 
-                         int *  const rowsP, 
+pnm_readpaminitrestaspnm(FILE * const fileP,
+                         int *  const colsP,
+                         int *  const rowsP,
                          gray * const maxvalP,
                          int *  const formatP) {
 /*----------------------------------------------------------------------------
@@ -743,7 +743,7 @@ pnm_readpaminitrestaspnm(FILE * const fileP,
 }
 
 
-                
+
 unsigned int
 pnm_bytespersample(sample const maxval) {
 /*----------------------------------------------------------------------------
@@ -871,17 +871,17 @@ interpretTupleType(struct pam * const pamP) {
 
 
 
-void 
-pnm_readpaminit(FILE *       const file, 
-                struct pam * const pamP, 
+void
+pnm_readpaminit(FILE *       const file,
+                struct pam * const pamP,
                 int          const size) {
 
-    if (size < PAM_STRUCT_SIZE(tuple_type)) 
+    if (size < PAM_STRUCT_SIZE(tuple_type))
         pm_error("pam object passed to pnm_readpaminit() is too small.  "
                  "It must be large "
                  "enough to hold at least up to the "
                  "'tuple_type' member, but according "
-                 "to the 'size' argument, it is only %d bytes long.", 
+                 "to the 'size' argument, it is only %d bytes long.",
                  size);
 
     pamP->size = size;
@@ -895,7 +895,7 @@ pnm_readpaminit(FILE *       const file,
     pamP->format = pm_readmagicnumber(file);
 
     switch (PAM_FORMAT_TYPE(pamP->format)) {
-    case PAM_TYPE: 
+    case PAM_TYPE:
         readpaminitrest(pamP);
         break;
     case PPM_TYPE: {
@@ -926,12 +926,12 @@ pnm_readpaminit(FILE *       const file,
         if (pamCommentP(pamP))
             *pamP->comment_p = strdup("");
         break;
-        
+
     default:
         pm_error("bad magic number 0x%x - not a PAM, PPM, PGM, or PBM file",
                  pamP->format);
     }
-    
+
     pamP->bytes_per_sample = pnm_bytespersample(pamP->maxval);
     pamP->plainformat = FALSE;
         /* See below for complex explanation of why this is FALSE. */
@@ -959,7 +959,7 @@ writeComments(const struct pam * const pamP) {
         for (p = &comment[0], startOfLine = TRUE; *p; ++p) {
             if (startOfLine)
                 fputc('#', pamP->file);
-                
+
             fputc(*p, pamP->file);
 
             if (*p == '\n')
@@ -986,7 +986,7 @@ writeComments(const struct pam * const pamP) {
    pam structure is what determines whether it is raw or plain.  So we
    set it false here so that it is false in the copied output pam
    structure.
-   
+
    Before 10.32, we set 'plainformat' according to the
    plainness of the input image, and thought it was a good
    idea for a program that reads a plain format input image to
@@ -997,7 +997,7 @@ writeComments(const struct pam * const pamP) {
    facilities to pam.
 */
 
-void 
+void
 pnm_writepaminit(struct pam * const pamP) {
 
     const char * tupleType;
@@ -1014,7 +1014,7 @@ pnm_writepaminit(struct pam * const pamP) {
                  "It must be large "
                  "enough to hold at least up through the "
                  "'bytes_per_sample' member, but according "
-                 "to its 'size' member, it is only %u bytes long.", 
+                 "to its 'size' member, it is only %u bytes long.",
                  pamP->size);
     if (pamP->len < PAM_STRUCT_SIZE(maxval))
         pm_error("pam object must contain members at least through 'maxval', "
@@ -1045,7 +1045,7 @@ pnm_writepaminit(struct pam * const pamP) {
     interpretTupleType(pamP);
 
     pamP->len = MIN(pamP->size, PAM_STRUCT_SIZE(opacity_plane));
-    
+
     switch (PAM_FORMAT_TYPE(pamP->format)) {
     case PAM_TYPE:
         /* See explanation below of why we ignore 'pm_plain_output' here. */
@@ -1065,13 +1065,13 @@ pnm_writepaminit(struct pam * const pamP) {
         */
         if (pamP->depth != 3)
             pm_error("pnm_writepaminit() got PPM format, but depth = %d "
-                     "instead of 3, as required for PPM.", 
+                     "instead of 3, as required for PPM.",
                      pamP->depth);
-        if (pamP->maxval > PPM_OVERALLMAXVAL) 
+        if (pamP->maxval > PPM_OVERALLMAXVAL)
             pm_error("pnm_writepaminit() got PPM format, but maxval = %ld, "
-                     "which exceeds the maximum allowed for PPM: %d", 
+                     "which exceeds the maximum allowed for PPM: %d",
                      pamP->maxval, PPM_OVERALLMAXVAL);
-        ppm_writeppminit(pamP->file, pamP->width, pamP->height, 
+        ppm_writeppminit(pamP->file, pamP->width, pamP->height,
                          (pixval) pamP->maxval, pamP->plainformat);
         break;
 
@@ -1082,9 +1082,9 @@ pnm_writepaminit(struct pam * const pamP) {
                      pamP->depth);
         if (pamP->maxval > PGM_OVERALLMAXVAL)
             pm_error("pnm_writepaminit() got PGM format, but maxval = %ld, "
-                     "which exceeds the maximum allowed for PGM: %d", 
+                     "which exceeds the maximum allowed for PGM: %d",
                      pamP->maxval, PGM_OVERALLMAXVAL);
-        pgm_writepgminit(pamP->file, pamP->width, pamP->height, 
+        pgm_writepgminit(pamP->file, pamP->width, pamP->height,
                          (gray) pamP->maxval, pamP->plainformat);
         break;
 
@@ -1093,10 +1093,10 @@ pnm_writepaminit(struct pam * const pamP) {
             pm_error("pnm_writepaminit() got PBM format, but depth = %d "
                      "instead of 1, as required for PBM.",
                      pamP->depth);
-        if (pamP->maxval != 1) 
+        if (pamP->maxval != 1)
             pm_error("pnm_writepaminit() got PBM format, but maxval = %ld "
                      "instead of 1, as required for PBM.", pamP->maxval);
-        pbm_writepbminit(pamP->file, pamP->width, pamP->height, 
+        pbm_writepbminit(pamP->file, pamP->width, pamP->height,
                          pamP->plainformat);
         break;
 
@@ -1126,29 +1126,29 @@ pnm_writepaminit(struct pam * const pamP) {
 
 
 void
-pnm_checkpam(const struct pam *   const pamP, 
-             enum pm_check_type   const checkType, 
+pnm_checkpam(const struct pam *   const pamP,
+             enum pm_check_type   const checkType,
              enum pm_check_code * const retvalP) {
 
     if (checkType != PM_CHECK_BASIC) {
         if (retvalP) *retvalP = PM_CHECK_UNKNOWN_TYPE;
     } else switch (PAM_FORMAT_TYPE(pamP->format)) {
     case PAM_TYPE: {
-        pm_filepos const need_raster_size = 
+        pm_filepos const need_raster_size =
             pamP->width * pamP->height * pamP->depth * pamP->bytes_per_sample;
         pm_check(pamP->file, checkType, need_raster_size, retvalP);
     }
         break;
     case PPM_TYPE:
-        pgm_check(pamP->file, checkType, pamP->format, 
+        pgm_check(pamP->file, checkType, pamP->format,
                   pamP->width, pamP->height, pamP->maxval, retvalP);
         break;
     case PGM_TYPE:
-        pgm_check(pamP->file, checkType, pamP->format, 
+        pgm_check(pamP->file, checkType, pamP->format,
                   pamP->width, pamP->height, pamP->maxval, retvalP);
         break;
     case PBM_TYPE:
-        pbm_check(pamP->file, checkType, pamP->format, 
+        pbm_check(pamP->file, checkType, pamP->format,
                   pamP->width, pamP->height, retvalP);
         break;
     default:
@@ -1158,7 +1158,7 @@ pnm_checkpam(const struct pam *   const pamP,
 
 
 
-void 
+void
 pnm_maketuplergb(const struct pam * const pamP,
                  tuple              const tuple) {
 
@@ -1172,29 +1172,27 @@ pnm_maketuplergb(const struct pam * const pamP,
 
 
 
-void 
+void
 pnm_makerowrgb(const struct pam * const pamP,
                tuple *            const tuplerow) {
-    
+
     if (pamP->depth < 3) {
         unsigned int col;
 
         if (allocationDepth(pamP) < 3)
             pm_error("allocation depth %u passed to pnm_makerowrgb().  "
                      "Must be at least 3.", allocationDepth(pamP));
-        
-        if (strncmp(pamP->tuple_type, "RGB", 3) != 0) {
-            for (col = 0; col < pamP->width; ++col) {
-                tuple const thisTuple = tuplerow[col];
-                thisTuple[2] = thisTuple[1] = thisTuple[0];
-            }
+
+        for (col = 0; col < pamP->width; ++col) {
+            tuple const thisTuple = tuplerow[col];
+            thisTuple[2] = thisTuple[1] = thisTuple[0];
         }
     }
 }
 
 
 
-void 
+void
 pnm_makearrayrgb(const struct pam * const pamP,
                  tuple **           const tuples) {
 
@@ -1203,7 +1201,7 @@ pnm_makearrayrgb(const struct pam * const pamP,
         if (allocationDepth(pamP) < 3)
             pm_error("allocation depth %u passed to pnm_makearrayrgb().  "
                      "Must be at least 3.", allocationDepth(pamP));
-        
+
         for (row = 0; row < pamP->height; ++row) {
             tuple * const tuplerow = tuples[row];
             unsigned int col;
@@ -1217,7 +1215,7 @@ pnm_makearrayrgb(const struct pam * const pamP,
 
 
 
-void 
+void
 pnm_makerowrgba(const struct pam * const pamP,
                 tuple *            const tuplerow) {
 /*----------------------------------------------------------------------------
@@ -1238,7 +1236,7 @@ pnm_makerowrgba(const struct pam * const pamP,
     } else {
         if (!pamP->visual)
             pm_error("Non-visual tuples given to pnm_addopacityrow()");
-        
+
         if (pamP->color_depth >= 3 && pamP->have_opacity) {
             /* It's already in RGBA format.  Leave it alone. */
         } else {
@@ -1247,10 +1245,10 @@ pnm_makerowrgba(const struct pam * const pamP,
             if (allocationDepth(pamP) < 4)
                 pm_error("allocation depth %u passed to pnm_makerowrgba().  "
                          "Must be at least 4.", allocationDepth(pamP));
-        
+
             for (col = 0; col < pamP->width; ++col) {
                 tuple const thisTuple = tuplerow[col];
-                thisTuple[PAM_TRN_PLANE] = 
+                thisTuple[PAM_TRN_PLANE] =
                     pamP->have_opacity ? thisTuple[pamP->opacity_plane] :
                     pamP->maxval;
 
@@ -1264,7 +1262,7 @@ pnm_makerowrgba(const struct pam * const pamP,
 
 
 
-void 
+void
 pnm_addopacityrow(const struct pam * const pamP,
                   tuple *            const tuplerow) {
 /*----------------------------------------------------------------------------
@@ -1285,7 +1283,7 @@ pnm_addopacityrow(const struct pam * const pamP,
     } else {
         if (!pamP->visual)
             pm_error("Non-visual tuples given to pnm_addopacityrow()");
-        
+
         if (pamP->have_opacity) {
             /* It already has opacity.  Leave it alone. */
         } else {
@@ -1297,7 +1295,7 @@ pnm_addopacityrow(const struct pam * const pamP,
                 pm_error("allocation depth %u passed to pnm_addopacityrow().  "
                          "Must be at least %u.",
                          allocationDepth(pamP), opacityPlane + 1);
-        
+
             for (col = 0; col < pamP->width; ++col)
                 tuplerow[col][opacityPlane] = pamP->maxval;
         }
@@ -1388,7 +1386,7 @@ pnm_backgroundtuple(struct pam *  const pamP,
 
 
 /*=============================================================================
-   pm_system() Standard Input feeder and Standard Output accepter functions.   
+   pm_system() Standard Input feeder and Standard Output accepter functions.
 =============================================================================*/
 
 void
diff --git a/lib/libpbm3.c b/lib/libpbm3.c
index c8389824..0144abe2 100644
--- a/lib/libpbm3.c
+++ b/lib/libpbm3.c
@@ -40,9 +40,9 @@
 
 
 void
-pbm_writepbminit(FILE * const fileP, 
-                 int    const cols, 
-                 int    const rows, 
+pbm_writepbminit(FILE * const fileP,
+                 int    const cols,
+                 int    const rows,
                  int    const forceplain) {
 
     if (!forceplain && !pm_plain_output) {
@@ -55,14 +55,20 @@ pbm_writepbminit(FILE * const fileP,
 
 static void
 writePackedRawRow(FILE *                const fileP,
-                  const unsigned char * const packed_bits,
-                  int                   const cols) {
+                  const unsigned char * const packedBits,
+                  unsigned int          const cols) {
 
-    int bytesWritten;
-    bytesWritten = fwrite(packed_bits, 1, pbm_packed_bytes(cols), fileP);
-    if (bytesWritten < pbm_packed_bytes(cols)) 
-        pm_error("I/O error writing packed row to raw PBM file.");
-} 
+    unsigned int const packedByteCt = pbm_packed_bytes(cols);
+
+    size_t writtenByteCt;
+
+    writtenByteCt = fwrite(packedBits, 1, packedByteCt, fileP);
+    if (writtenByteCt < packedByteCt)
+        pm_error("I/O error writing packed row to raw PBM file.  "
+                 "(Attempted fwrite() of %u packed bytes; "
+                 "only %u got written)",
+                 packedByteCt, (unsigned)writtenByteCt);
+}
 
 
 
@@ -83,21 +89,21 @@ packBitsWithSse2(  FILE *          const fileP,
 -----------------------------------------------------------------------------*/
     /*
       We use 2 SSE registers.
-    
+
       The key machine instructions are:
-        
+
       PCMPGTB128  Packed CoMPare Greater Than Byte
-    
+
         Compares 16 bytes in parallel
         Result is x00 if greater than, xFF if not for each byte
 
-    
-      PMOVMSKB128 Packed MOVe MaSK Byte 
-    
+
+      PMOVMSKB128 Packed MOVe MaSK Byte
+
         Result is 16 bits, the MSBs of 16 bytes
-        x00 xFF x00 xFF xFF xFF x00 x00 xFF xFF xFF xFF x00 x00 x00 x00 
+        x00 xFF x00 xFF xFF xFF x00 x00 xFF xFF xFF xFF x00 x00 x00 x00
         --> 0101110011110000B = 0x5CF0
-        
+
         The result is actually a 64 bit int, but the higher bits are
         always 0.
 
@@ -133,7 +139,7 @@ packBitsWithSse2(  FILE *          const fileP,
             v16qi const compare = (v16qi)
                 _mm_cmpgt_epi8((__m128i)bit128.v16, (__m128i) zero128);
             uint16_t const blackMask = _mm_movemask_epi8 ((__m128i)compare);
-            
+
             *(uint16_t *) & packedBits[col/8] = blackMask;
         }
     }
@@ -142,10 +148,10 @@ packBitsWithSse2(  FILE *          const fileP,
         unsigned int i, j;
 
         bit128.v16 = bit128.v16 ^ bit128.v16;
-    
-        for (i = 0, j = col ; j < cols; ++i, ++j) 
+
+        for (i = 0, j = col ; j < cols; ++i, ++j)
             bit128.byte[ (i&8) + 7-(i&7) ] = bitrow[j];
-      
+
         {
             v16qi const compare = (v16qi)
                 _mm_cmpgt_epi8((__m128i)bit128.v16, (__m128i) zero128);
@@ -211,22 +217,22 @@ packPartialBytes(const bit *     const bitrow,
                  unsigned int    const cols,
                  unsigned int    const nextCol,
                  unsigned char * const packedBits) {
-              
+
     /* routine for partial byte at the end of packedBits[]
        Prior to addition of the above enhancement,
        this method was used for the entire process
-    */                   
-    
+    */
+
     unsigned int col;
     int bitshift;
     unsigned char item;
-    
+
     bitshift = 7;  /* initial value */
     item = 0;      /* initial value */
     for (col = nextCol; col < cols; ++col, --bitshift)
         if (bitrow[col] != 0)
             item |= 1 << bitshift;
-    
+
     packedBits[col/8] = item;
 }
 
@@ -252,7 +258,7 @@ writePbmRowRaw(FILE *      const fileP,
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
         switch (PACKBITS_SSE) {
-        case 2: 
+        case 2:
             packBitsWithSse2(fileP, bitrow, packedBits, cols);
             break;
         default: {
@@ -273,9 +279,9 @@ writePbmRowRaw(FILE *      const fileP,
 
 static void
 writePbmRowPlain(FILE *      const fileP,
-                 const bit * const bitrow, 
+                 const bit * const bitrow,
                  int         const cols) {
-    
+
     int col, charcount;
 
     charcount = 0;
@@ -293,9 +299,9 @@ writePbmRowPlain(FILE *      const fileP,
 
 
 void
-pbm_writepbmrow(FILE *       const fileP, 
-                const bit *  const bitrow, 
-                int          const cols, 
+pbm_writepbmrow(FILE *       const fileP,
+                const bit *  const bitrow,
+                int          const cols,
                 int          const forceplain) {
 
     if (!forceplain && !pm_plain_output)
@@ -307,9 +313,9 @@ pbm_writepbmrow(FILE *       const fileP,
 
 
 void
-pbm_writepbmrow_packed(FILE *                const fileP, 
+pbm_writepbmrow_packed(FILE *                const fileP,
                        const unsigned char * const packedBits,
-                       int                   const cols, 
+                       int                   const cols,
                        int                   const forceplain) {
 
     if (!forceplain && !pm_plain_output)
@@ -327,11 +333,11 @@ pbm_writepbmrow_packed(FILE *                const fileP,
             pm_longjmp();
         } else {
             unsigned int col;
-            
+
             pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
-            for (col = 0; col < cols; ++col) 
-                bitrow[col] = 
+            for (col = 0; col < cols; ++col)
+                bitrow[col] =
                     packedBits[col/8] & (0x80 >> (col%8)) ?
                     PBM_BLACK : PBM_WHITE;
 
@@ -395,13 +401,13 @@ pbm_writepbmrow_bitoffset(FILE *          const fileP,
     bool const carryover = (csh == 0 || rsh + csh > 8);
         /* TRUE:  Input comes from colByteCnt bytes and one extra byte.
            FALSE: Input comes from colByteCnt bytes.  For example:
-           TRUE:  xxxxxxii iiiiiiii iiiiiiii iiixxxxx  cols=21, offset=6 
+           TRUE:  xxxxxxii iiiiiiii iiiiiiii iiixxxxx  cols=21, offset=6
            FALSE: xiiiiiii iiiiiiii iiiiiixx ________  cols=21, offset=1
 
            We treat these differently for in the FALSE case the byte after
            last (indicated by ________) may not exist.
         */
-       
+
     if (rsh > 0) {
         unsigned int const shiftBytes =  carryover ? colByteCnt : colByteCnt-1;
 
@@ -412,26 +418,26 @@ pbm_writepbmrow_bitoffset(FILE *          const fileP,
         if (!carryover)
             window[last] = window[last] << rsh;
     }
-      
+
     if (csh > 0)
         window[last] = leftBits(window[last], csh);
-          
+
     pbm_writepbmrow_packed(fileP, window, cols, 0);
 }
 
 
 
 void
-pbm_writepbm(FILE * const fileP, 
-             bit ** const bits, 
-             int    const cols, 
-             int    const rows, 
+pbm_writepbm(FILE * const fileP,
+             bit ** const bits,
+             int    const cols,
+             int    const rows,
              int    const forceplain) {
 
     int row;
 
     pbm_writepbminit(fileP, cols, rows, forceplain);
-    
+
     for (row = 0; row < rows; ++row)
         pbm_writepbmrow(fileP, bits[row], cols, forceplain);
 }
diff --git a/lib/libpbmfont.c b/lib/libpbmfont.c
index 8e854aa6..8f308eda 100644
--- a/lib/libpbmfont.c
+++ b/lib/libpbmfont.c
@@ -256,7 +256,7 @@ pbm_dissectfont(const bit ** const font,
     /* Initialize all character positions to "undefined."  Those that
        are defined in the font will be filled in below.
     */
-    for (i = 0; i < 256; i++)
+    for (i = 0; i < PM_FONT_MAXGLYPH + 1; i++)
         fn->glyph[i] = NULL;
 
     MALLOCARRAY(glyph, nCharsInFont);
@@ -294,7 +294,7 @@ pbm_dissectfont(const bit ** const font,
             row += cellHeight;
         }
     }
-    for (i = firstCodePoint + nCharsInFont; i < 256; ++i)
+    for (i = firstCodePoint + nCharsInFont; i < PM_FONT_MAXGLYPH + 1; ++i)
         fn->glyph[i] = NULL;
     
     return fn;
@@ -377,14 +377,14 @@ pbm_dumpfont(struct font * const fontP,
         pm_message("Netpbm no longer has the capability to generate "
                    "a font in long hexadecimal data format");
 
-    for (i = 0, ng = 0; i < 256; ++i) {
+    for (i = 0, ng = 0; i < PM_FONT_MAXGLYPH +1; ++i) {
         if (fontP->glyph[i])
             ++ng;
     }
 
     printf("static struct glyph _g[%d] = {\n", ng);
 
-    for (i = 0; i < 256; ++i) {
+    for (i = 0; i < PM_FONT_MAXGLYPH + 1; ++i) {
         struct glyph * const glyphP = fontP->glyph[i];
         if (glyphP) {
             unsigned int j;
@@ -409,13 +409,13 @@ pbm_dumpfont(struct font * const fontP,
     {
         unsigned int i;
 
-        for (i = 0; i < 256; ++i) {
+        for (i = 0; i < PM_FONT_MAXGLYPH + 1; ++i) {
             if (fontP->glyph[i])
                 printf(" _g + %d", ng++);
             else
                 printf(" NULL");
         
-            if (i != 255) printf(",");
+            if (i != PM_FONT_MAXGLYPH) printf(",");
             printf("\n");
         }
     }
@@ -738,7 +738,8 @@ skipCharacter(Readline * const readlineP) {
 static void
 interpEncoding(const char **  const arg,
                unsigned int * const codepointP,
-               bool *         const badCodepointP) {
+               bool *         const badCodepointP,
+               PM_WCHAR       const maxglyph) {
 /*----------------------------------------------------------------------------
    With arg[] being the ENCODING statement from the font, return as
    *codepointP the codepoint that it indicates (code point is the character
@@ -746,8 +747,9 @@ interpEncoding(const char **  const arg,
 
    But if the statement doesn't give an acceptable codepoint return
    *badCodepointP == TRUE.
------------------------------------------------------------------------------*/
 
+   'maxglyph' is the maximum codepoint in the font.
+-----------------------------------------------------------------------------*/
     bool gotCodepoint;
     bool badCodepoint;
     unsigned int codepoint;
@@ -763,7 +765,7 @@ interpEncoding(const char **  const arg,
             gotCodepoint = false;
     }
     if (gotCodepoint) {
-        if (codepoint > 255)
+        if (codepoint > maxglyph)
             badCodepoint = true;
         else
             badCodepoint = false;
@@ -779,17 +781,18 @@ interpEncoding(const char **  const arg,
 static void
 readEncoding(Readline *     const readlineP,
              unsigned int * const codepointP,
-             bool *         const badCodepointP) {
+             bool *         const badCodepointP,
+             PM_WCHAR       const maxglyph) {
 
     readExpectedStatement(readlineP, "ENCODING");
     
-    interpEncoding(readlineP->arg, codepointP, badCodepointP);
+    interpEncoding(readlineP->arg, codepointP, badCodepointP, maxglyph);
 }
 
 
 
 static void
-validateFontLimits(const struct font * const fontP) {
+validateFontLimits(const struct font2 * const fontP) {
 
     assert(pbm_maxfontheight() > 0 && pbm_maxfontwidth() > 0);
 
@@ -797,22 +800,27 @@ validateFontLimits(const struct font * const fontP) {
         fontP->maxheight <= 0 ||
         fontP->maxwidth  > pbm_maxfontwidth()  ||
         fontP->maxheight > pbm_maxfontheight() ||
-        fontP->x < - fontP->maxwidth  +1 ||
-        fontP->y < - fontP->maxheight +1 ||
+        -fontP->x + 1 > fontP->maxwidth ||
+        -fontP->y + 1 > fontP->maxheight ||
         fontP->x > fontP->maxwidth  ||
         fontP->y > fontP->maxheight ||
         fontP->x + fontP->maxwidth  > pbm_maxfontwidth() || 
         fontP->y + fontP->maxheight > pbm_maxfontheight()
         ) {
 
-        pm_error("Global font metric(s) out of bounds.\n"); 
+        pm_error("Global font metric(s) out of bounds."); 
     }
+
+    if (fontP->maxglyph > PM_FONT2_MAXGLYPH)
+        pm_error("Internal error.  Glyph table too large: %u glyphs; "
+                 "Maximum possible in Netpbm is %u",
+                 fontP->maxglyph, PM_FONT2_MAXGLYPH);
 }
 
 
 
 static void
-validateGlyphLimits(const struct font  * const fontP,
+validateGlyphLimits(const struct font2 * const fontP,
                     const struct glyph * const glyphP,
                     const char *         const charName) {
 
@@ -842,7 +850,8 @@ validateGlyphLimits(const struct font  * const fontP,
 
 static void
 processChars(Readline *     const readlineP,
-             struct font  * const fontP) {
+             struct font2 * const fontP,
+             PM_WCHAR       const maxglyph ) {
 /*----------------------------------------------------------------------------
    Process the CHARS block in a BDF font file, assuming the file is positioned
    just after the CHARS line.  Read the rest of the block and apply its
@@ -880,7 +889,7 @@ processChars(Readline *     const readlineP,
                 pm_error("no memory for font glyph for '%s' character",
                          charName);
 
-            readEncoding(readlineP, &codepoint, &badCodepoint);
+            readEncoding(readlineP, &codepoint, &badCodepoint, maxglyph);
 
             if (badCodepoint)
                 skipCharacter(readlineP);
@@ -907,7 +916,7 @@ processChars(Readline *     const readlineP,
 
                 readExpectedStatement(readlineP, "ENDCHAR");
 
-                assert(codepoint < 256); /* Ensured by readEncoding() */
+                assert(codepoint <= maxglyph); /* Ensured by readEncoding() */
 
                 fontP->glyph[codepoint] = glyphP;
                 pm_strfree(charName);
@@ -921,8 +930,9 @@ processChars(Readline *     const readlineP,
 
 static void
 processBdfFontLine(Readline     * const readlineP,
-                   struct font  * const fontP,
-                   bool         * const endOfFontP) {
+                   struct font2 * const fontP,
+                   bool         * const endOfFontP,
+                   PM_WCHAR       const maxglyph) {
 /*----------------------------------------------------------------------------
    Process a nonblank line just read from a BDF font file.
 
@@ -964,7 +974,7 @@ processBdfFontLine(Readline     * const readlineP,
       pm_error("Encountered CHARS before FONTBOUNDINGBOX " 
                    "in BDF font file");
       else
-        processChars(readlineP, fontP);
+        processChars(readlineP, fontP, maxglyph);
     } else {
         /* ignore */
     }
@@ -972,36 +982,57 @@ processBdfFontLine(Readline     * const readlineP,
 
 
 
-struct font *
-pbm_loadbdffont(const char * const name) {
+struct font2 *
+pbm_loadbdffont2(const char * const filename,
+                 PM_WCHAR     const maxglyph) {
+/*----------------------------------------------------------------------------
+   Read a BDF font file "filename" as a 'font2' structure.  A 'font2'
+   structure is more expressive than a 'font' structure, most notably in that
+   it can handle wide code points and many more glyphs.
 
-    FILE * ifP;
-    Readline readline;
-    struct font * fontP;
-    bool endOfFont;
+   Codepoints up to maxglyph inclusive are valid in the file.
 
-    ifP = fopen(name, "rb");
+   The returned object is in new malloc'ed storage, in many pieces, and
+   cannot be destroyed.
+-----------------------------------------------------------------------------*/
+    /* For our return object to be destroyable, we need to supply a destroy
+       function, and it needs to return glyph and raster memory, and
+       struct font needs to manage glyph memory separately from mapping
+       code points to glyphs.
+    */
+    FILE *         ifP;
+    Readline       readline;
+    struct font2 * font2P;
+    bool           endOfFont;
+
+    ifP = fopen(filename, "rb");
     if (!ifP)
         pm_error("Unable to open BDF font file name '%s'.  errno=%d (%s)",
-                 name, errno, strerror(errno));
+                 filename, errno, strerror(errno));
 
     readline_init(&readline, ifP);
 
-    MALLOCVAR(fontP);
-    if (fontP == NULL)
+    MALLOCVAR(font2P);
+    if (font2P == NULL)
         pm_error("no memory for font");
 
-    fontP->oldfont = 0;
-    { 
+    MALLOCARRAY(font2P->glyph, maxglyph + 1);
+    if (font2P->glyph == NULL)
+        pm_error("no memory for font glyphs");
+
+    font2P->maxglyph = maxglyph;
+
+    font2P->oldfont = NULL;
+    {
         /* Initialize all characters to nonexistent; we will fill the ones we
            find in the bdf file later.
         */
-        unsigned int i;
-        for (i = 0; i < 256; ++i) 
-            fontP->glyph[i] = NULL;
+        PM_WCHAR i;
+        for (i = 0; i <= maxglyph; ++i)
+            font2P->glyph[i] = NULL;
     }
 
-    fontP->maxwidth = fontP->maxheight = fontP->x = fontP->y = 0;
+    font2P->maxwidth = font2P->maxheight = font2P->x = font2P->y = 0;
 
     readExpectedStatement(&readline, "STARTFONT");
 
@@ -1013,10 +1044,112 @@ pbm_loadbdffont(const char * const name) {
         if (eof)
             pm_error("End of file before ENDFONT statement in BDF font file");
 
-        processBdfFontLine(&readline, fontP, &endOfFont);
+        processBdfFontLine(&readline, font2P, &endOfFont, maxglyph);
     }
+    return font2P;
+}
+
+
+
+struct font *
+pbm_loadbdffont(const char * const filename) {
+/*----------------------------------------------------------------------------
+   Read a BDF font file "filename" into a traditional font structure.
+
+   Codepoints up to 255 (PM_FONT_MAXGLYPH) are valid.
+
+   Can handle ASCII, ISO-8859-1, ISO-8859-2, ISO-8859-15, etc.
+
+   The returned object is in new malloc'ed storage, in many pieces, and
+   cannot be destroyed.
+-----------------------------------------------------------------------------*/
+    /* For our return object to deep copy the glyphs and fonts from
+       the struct font2be destroyable, we need to supply a destroy
+       function, and it needs to return glyph and raster memory, and
+       struct font needs to manage glyph memory separately from mapping
+       code points to glyphs.
+    */
+    struct font  * fontP;
+    struct font2 * font2P;
+    unsigned int   codePoint;
+
+    MALLOCVAR(fontP);
+    if (fontP == NULL)
+        pm_error("no memory for font");
+
+    font2P = pbm_loadbdffont2(filename, PM_FONT_MAXGLYPH);
+
+    fontP->maxwidth  = font2P->maxwidth;
+    fontP->maxheight = font2P->maxheight;
+
+    fontP->x = font2P->x;
+    fontP->y = font2P->y;
+
+    for (codePoint = 0; codePoint < PM_FONT_MAXGLYPH + 1; ++codePoint)
+        fontP->glyph[codePoint] = font2P->glyph[codePoint];
+
+    fontP->oldfont = NULL;
+
+    fontP->fcols = 0;
+    fontP->frows = 0;
+
+    /* Note that *fontP2 is unfreeable.  See pbm_loadbdffont2.  And even if it
+       were, we hooked *fontP into it above, so that would have to turn into a
+       deep copy before we could free *fontP2.
+    */
+
     return fontP;
 }
 
 
 
+struct font2 *
+pbm_expandbdffont(const struct font * const fontP) {
+/*----------------------------------------------------------------------------
+  Convert a traditional font structure into an expanded font2 structure.
+
+  This function depends upon the fact that *fontP, like any struct font,
+  cannot be destroyed.  The returned object refers to memory that belongs
+  to *fontP.
+
+  The returned object is in new malloc'ed storage, in many pieces, and
+  cannot be destroyed.
+-----------------------------------------------------------------------------*/
+    /* If we ever make struct font destroyable, this function needs to
+       copy the glyphs and rasters, and struct font and struct font2 need
+       to manage glyph memory separately from mapping code points to the
+       glyphs.
+    */
+    PM_WCHAR const maxglyph = PM_FONT_MAXGLYPH;
+
+    struct font2 * font2P;
+    unsigned int   codePoint;
+
+    MALLOCVAR(font2P);
+    if (font2P == NULL)
+        pm_error("no memory for font");
+
+    MALLOCARRAY(font2P->glyph, maxglyph + 1);
+    if (font2P->glyph == NULL)
+        pm_error("no memory for font glyphs");
+
+    font2P->maxwidth  = fontP->maxwidth;
+    font2P->maxheight = fontP->maxheight;
+
+    font2P->x = fontP->x;
+    font2P->y = fontP->y;
+
+    font2P->maxglyph = maxglyph;
+
+    for (codePoint = 0; codePoint < maxglyph + 1; ++codePoint)
+        font2P->glyph[codePoint] = fontP->glyph[codePoint];
+
+    font2P->oldfont = fontP->oldfont;
+
+    font2P->fcols = fontP->fcols;
+    font2P->frows = fontP->frows;
+
+    return font2P;
+}
+
+
diff --git a/lib/libppm1.c b/lib/libppm1.c
index c1eb152c..ccc8adb5 100644
--- a/lib/libppm1.c
+++ b/lib/libppm1.c
@@ -14,7 +14,7 @@
    offset stuff.
 */
 #define _FILE_OFFSET_BITS 64
-#define _LARGE_FILES  
+#define _LARGE_FILES
 
 #include <string.h>
 #include <stdio.h>
@@ -56,7 +56,7 @@ ppm_init(int * const argcP, char ** const argv) {
 
 
 void
-ppm_nextimage(FILE * const fileP, 
+ppm_nextimage(FILE * const fileP,
               int *  const eofP) {
     pm_nextimage(fileP, eofP);
 }
@@ -64,9 +64,9 @@ ppm_nextimage(FILE * const fileP,
 
 
 void
-ppm_readppminitrest(FILE *   const fileP, 
-                    int *    const colsP, 
-                    int *    const rowsP, 
+ppm_readppminitrest(FILE *   const fileP,
+                    int *    const colsP,
+                    int *    const rowsP,
                     pixval * const maxvalP) {
     unsigned int maxval;
 
@@ -79,7 +79,7 @@ ppm_readppminitrest(FILE *   const fileP,
     if (maxval > PPM_OVERALLMAXVAL)
         pm_error("maxval of input image (%u) is too large.  "
                  "The maximum allowed by the PPM format is %u.",
-                 maxval, PPM_OVERALLMAXVAL); 
+                 maxval, PPM_OVERALLMAXVAL);
     if (maxval == 0)
         pm_error("maxval of input image is zero.");
 
@@ -114,10 +114,10 @@ validateComputableSize(unsigned int const cols,
 
 
 void
-ppm_readppminit(FILE *   const fileP, 
-                int *    const colsP, 
-                int *    const rowsP, 
-                pixval * const maxvalP, 
+ppm_readppminit(FILE *   const fileP,
+                int *    const colsP,
+                int *    const rowsP,
+                pixval * const maxvalP,
                 int *    const formatP) {
 
     int realFormat;
@@ -156,10 +156,10 @@ ppm_readppminit(FILE *   const fileP,
 
 
 static void
-readPpmRow(FILE *       const fileP, 
-           pixel *      const pixelrow, 
-           unsigned int const cols, 
-           pixval       const maxval, 
+readPpmRow(FILE *       const fileP,
+           pixel *      const pixelrow,
+           unsigned int const cols,
+           pixval       const maxval,
            int          const format) {
 
     unsigned int col;
@@ -168,7 +168,7 @@ readPpmRow(FILE *       const fileP,
         pixval const r = pm_getuint(fileP);
         pixval const g = pm_getuint(fileP);
         pixval const b = pm_getuint(fileP);
-        
+
         if (r > maxval)
             pm_error("Red sample value %u is greater than maxval (%u)",
                      r, maxval);
@@ -178,7 +178,7 @@ readPpmRow(FILE *       const fileP,
         if (b > maxval)
             pm_error("Blue sample value %u is greater than maxval (%u)",
                      b, maxval);
-        
+
         PPM_ASSIGN(pixelrow[col], r, g, b);
     }
 }
@@ -194,7 +194,7 @@ interpRasterRowRaw(const unsigned char * const rowBuffer,
     unsigned int bufferCursor;
 
     bufferCursor = 0;  /* start at beginning of rowBuffer[] */
-        
+
     if (bytesPerSample == 1) {
         unsigned int col;
         for (col = 0; col < cols; ++col) {
@@ -208,16 +208,16 @@ interpRasterRowRaw(const unsigned char * const rowBuffer,
         unsigned int col;
         for (col = 0; col < cols; ++col) {
             pixval r, g, b;
-                    
+
             r = rowBuffer[bufferCursor++] << 8;
             r |= rowBuffer[bufferCursor++];
-                    
+
             g = rowBuffer[bufferCursor++] << 8;
             g |= rowBuffer[bufferCursor++];
-                    
+
             b = rowBuffer[bufferCursor++] << 8;
             b |= rowBuffer[bufferCursor++];
-                    
+
             PPM_ASSIGN(pixelrow[col], r, g, b);
         }
     }
@@ -231,7 +231,7 @@ validateRppmRow(pixel *       const pixelrow,
                 pixval        const maxval,
                 const char ** const errorP) {
 /*----------------------------------------------------------------------------
-  Check for sample values above maxval in input.  
+  Check for sample values above maxval in input.
 
   Note: a program that wants to deal with invalid sample values itself can
   simply make sure it uses a sufficiently high maxval on the read function
@@ -272,20 +272,20 @@ validateRppmRow(pixel *       const pixelrow,
 
 
 static void
-readRppmRow(FILE *       const fileP, 
-            pixel *      const pixelrow, 
-            unsigned int const cols, 
-            pixval       const maxval, 
+readRppmRow(FILE *       const fileP,
+            pixel *      const pixelrow,
+            unsigned int const cols,
+            pixval       const maxval,
             int          const format) {
 
     unsigned int const bytesPerSample = maxval < 256 ? 1 : 2;
     unsigned int const bytesPerRow    = cols * 3 * bytesPerSample;
-        
+
     unsigned char * rowBuffer;
     const char * error;
-    
+
     MALLOCARRAY(rowBuffer, bytesPerRow);
-        
+
     if (rowBuffer == NULL)
         pm_asprintf(&error, "Unable to allocate memory for row buffer "
                     "for %u columns", cols);
@@ -293,7 +293,7 @@ readRppmRow(FILE *       const fileP,
         ssize_t rc;
 
         rc = fread(rowBuffer, 1, bytesPerRow, fileP);
-    
+
         if (feof(fileP))
             pm_asprintf(&error, "Unexpected EOF reading row of PPM image.");
         else if (ferror(fileP))
@@ -323,10 +323,10 @@ readRppmRow(FILE *       const fileP,
 
 
 static void
-readPgmRow(FILE *       const fileP, 
-           pixel *      const pixelrow, 
-           unsigned int const cols, 
-           pixval       const maxval, 
+readPgmRow(FILE *       const fileP,
+           pixel *      const pixelrow,
+           unsigned int const cols,
+           pixval       const maxval,
            int          const format) {
 
     jmp_buf jmpbuf;
@@ -341,7 +341,7 @@ readPgmRow(FILE *       const fileP,
         pm_longjmp();
     } else {
         unsigned int col;
-    
+
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
         pgm_readpgmrow(fileP, grayrow, cols, maxval, format);
@@ -358,10 +358,10 @@ readPgmRow(FILE *       const fileP,
 
 
 static void
-readPbmRow(FILE *       const fileP, 
-           pixel *      const pixelrow, 
-           unsigned int const cols, 
-           pixval       const maxval, 
+readPbmRow(FILE *       const fileP,
+           pixel *      const pixelrow,
+           unsigned int const cols,
+           pixval       const maxval,
            int          const format) {
 
     jmp_buf jmpbuf;
@@ -395,10 +395,10 @@ readPbmRow(FILE *       const fileP,
 
 
 void
-ppm_readppmrow(FILE *  const fileP, 
-               pixel * const pixelrow, 
-               int     const cols, 
-               pixval  const maxval, 
+ppm_readppmrow(FILE *  const fileP,
+               pixel * const pixelrow,
+               int     const cols,
+               pixval  const maxval,
                int     const format) {
 
     switch (format) {
@@ -432,9 +432,9 @@ ppm_readppmrow(FILE *  const fileP,
 
 
 pixel**
-ppm_readppm(FILE *   const fileP, 
-            int *    const colsP, 
-            int *    const rowsP, 
+ppm_readppm(FILE *   const fileP,
+            int *    const colsP,
+            int *    const rowsP,
             pixval * const maxvalP) {
 
     jmp_buf jmpbuf;
@@ -472,11 +472,11 @@ ppm_readppm(FILE *   const fileP,
 
 
 void
-ppm_check(FILE *               const fileP, 
-          enum pm_check_type   const checkType, 
-          int                  const format, 
-          int                  const cols, 
-          int                  const rows, 
+ppm_check(FILE *               const fileP,
+          enum pm_check_type   const checkType,
+          int                  const format,
+          int                  const cols,
+          int                  const rows,
           pixval               const maxval,
           enum pm_check_code * const retvalP) {
 
@@ -484,7 +484,7 @@ ppm_check(FILE *               const fileP,
         pm_error("Invalid number of rows passed to ppm_check(): %d", rows);
     if (cols < 0)
         pm_error("Invalid number of columns passed to ppm_check(): %d", cols);
-    
+
     if (checkType != PM_CHECK_BASIC) {
         if (retvalP)
             *retvalP = PM_CHECK_UNKNOWN_TYPE;
@@ -495,10 +495,10 @@ ppm_check(FILE *               const fileP,
     } else if (format != RPPM_FORMAT) {
         if (retvalP)
             *retvalP = PM_CHECK_UNCHECKABLE;
-    } else {        
+    } else {
         pm_filepos const bytesPerRow    = cols * 3 * (maxval > 255 ? 2 : 1);
         pm_filepos const needRasterSize = rows * bytesPerRow;
-        
+
         pm_check(fileP, checkType, needRasterSize, retvalP);
     }
 }
diff --git a/lib/pam.h b/lib/pam.h
index 1cf7b855..d8d0e5c7 100644
--- a/lib/pam.h
+++ b/lib/pam.h
@@ -29,8 +29,8 @@ struct pam {
     /* This structure describes an open PAM image file.  It consists
        entirely of information that belongs in the header of a PAM image
        and filesystem information.  It does not contain any state
-       information about the processing of that image.  
-       
+       information about the processing of that image.
+
        This is not considered to be an opaque object.  The user of Netbpm
        libraries is free to access and set any of these fields whenever
        appropriate.  The structure exists to make coding of function calls
@@ -41,11 +41,11 @@ struct pam {
        backward compatibility between library functions and calling programs
        as this structure grows.
        */
-    unsigned int size;   
+    unsigned int size;
         /* The storage size of this entire structure, in bytes */
-    unsigned int len;    
+    unsigned int len;
         /* The length, in bytes, of the information in this structure.
-           The information starts in the first byte and is contiguous.  
+           The information starts in the first byte and is contiguous.
            This cannot be greater than 'size'
 
            Use PAM_STRUCT_SIZE() to compute or interpret a value for this.
@@ -76,12 +76,12 @@ struct pam {
            have plain and raw variations.
         */
     int height;  /* Height of image in rows */
-    int width;   
+    int width;
         /* Width of image in number of columns (tuples per row) */
-    unsigned int depth;   
+    unsigned int depth;
         /* Depth of image (number of samples in each tuple). */
     sample maxval;  /* Maximum defined value for a sample */
-    unsigned int bytes_per_sample;  
+    unsigned int bytes_per_sample;
         /* Number of bytes used to represent each sample in the image file.
            Note that this is strictly a function of 'maxval'.  It is in a
            a separate member for computational speed.
@@ -100,7 +100,7 @@ struct pam {
 
            The purpose of this is to make it possible for a program to
            change the type of a tuple to one with more or fewer
-           planes.  
+           planes.
 
            0 means the allocation depth is the same as the image depth.
         */
@@ -141,7 +141,7 @@ struct pam {
 #define PAM_HAVE_ALLOCATION_DEPTH 1
 #define PAM_HAVE_COMMENT_P 1
 
-/* PAM_STRUCT_SIZE(x) tells you how big a struct pam is up through the 
+/* PAM_STRUCT_SIZE(x) tells you how big a struct pam is up through the
    member named x.  This is useful in conjunction with the 'len' value
    to determine which fields are present in the structure.
 */
@@ -172,7 +172,7 @@ struct pam {
     /* These are values of samples in a PAM image that represents a black
        and white bitmap image.  They are the values of black and white,
        respectively.  For example, if you use pnm_readpamrow() to read a
-       row from a PBM file, the black pixels get returned as 
+       row from a PBM file, the black pixels get returned as
        PAM_PBM_BLACK.
     */
 
@@ -192,7 +192,7 @@ struct pam {
 #define PAM_GRAY_TRN_PLANE 1
     /* For a "GRAYSCALE" tuple type, this is the transparency plane */
 
-typedef sample *tuple;  
+typedef sample *tuple;
     /* A tuple in a PAM.  This is an array such that tuple[i-1] is the
        ith sample (element) in the tuple.  It's dimension is the depth
        of the image (see pam.depth above).
@@ -203,7 +203,7 @@ typedef sample *tuple;
 /* Note: xv uses the same "P7" signature for its thumbnail images (it
    started using it years before PAM and unbeknownst to the designer
    of PAM).  But these images are still easily distinguishable from
-   PAMs 
+   PAMs
 */
 #define PAM_MAGIC1 'P'
 #define PAM_MAGIC2 '7'
@@ -221,9 +221,9 @@ struct pamtuples {
 
 
 typedef float * pnm_transformMap;
-    /* This is an array of transform maps.  transform[N] is the 
+    /* This is an array of transform maps.  transform[N] is the
        array that is the map for Plane N.
-   
+
        Transform maps define a transformation between PAM sample value
        to normalized libnetpbm "samplen" value, i.e. what you get back
        from pnm_readpamrown() or pass to pnm_writepamrown().
@@ -244,7 +244,7 @@ typedef float * pnm_transformMap;
        obvious table lookup.  The samplen -> sample transformation is
        more complicated -- if the samplen value is between map[N]
        and map[N+1], then the sample value is N.  And only transforms
-       where map[N+1] > map[N] are allowed.  
+       where map[N+1] > map[N] are allowed.
     */
 
 /* Declarations of library functions. */
@@ -257,8 +257,8 @@ unsigned int
 pnm_bytespersample(sample const maxval);
 
 int
-pnm_tupleequal(const struct pam * const pamP, 
-               tuple              const comparand, 
+pnm_tupleequal(const struct pam * const pamP,
+               tuple              const comparand,
                tuple              const comparator);
 
 void
@@ -267,14 +267,14 @@ pnm_assigntuple(const struct pam * const pamP,
                 tuple              const source);
 
 static __inline__ sample
-pnm_scalesample(sample const source, 
-                sample const oldmaxval, 
+pnm_scalesample(sample const source,
+                sample const oldmaxval,
                 sample const newmaxval) {
 
     if (oldmaxval == newmaxval)
         /* Fast path for common case */
         return source;
-    else 
+    else
         return (source * newmaxval + (oldmaxval/2)) / oldmaxval;
 }
 
@@ -283,24 +283,24 @@ pnm_scalesample(sample const source,
 void
 pnm_scaletuple(const struct pam * const pamP,
                tuple              const dest,
-               tuple              const source, 
+               tuple              const source,
                sample             const newmaxval);
 
-void 
+void
 pnm_scaletuplerow(const struct pam * const pamP,
                   tuple *            const destRow,
                   tuple *            const sourceRow,
                   sample             const newMaxval);
 
-void 
+void
 pnm_maketuplergb(const struct pam * const pamP,
                  tuple              const tuple);
 
-void 
+void
 pnm_makerowrgb(const struct pam * const pamP,
                tuple *            const tuplerow);
 
-void 
+void
 pnm_makearrayrgb(const struct pam * const pamP,
                  tuple **           const tuples);
 
@@ -336,13 +336,13 @@ pnm_allocpamarray(const struct pam * const pamP);
 void
 pnm_freepamarray(tuple ** const tuplearray, const struct pam * const pamP);
 
-void 
+void
 pnm_setminallocationdepth(struct pam * const pamP,
                           unsigned int const allocationDepth);
 
 void
-pnm_setpamrow(const struct pam * const pam, 
-              tuple *            const tuplerow, 
+pnm_setpamrow(const struct pam * const pam,
+              tuple *            const tuplerow,
               sample             const value);
 
 unsigned char *
@@ -351,20 +351,20 @@ pnm_allocrowimage(const struct pam * const pamP);
 void
 pnm_freerowimage(unsigned char * const rowimage);
 
-void 
-pnm_readpaminit(FILE *       const file, 
-                struct pam * const pamP, 
+void
+pnm_readpaminit(FILE *       const file,
+                struct pam * const pamP,
                 int          const size);
 
-void 
+void
 pnm_readpamrow(const struct pam * const pamP, tuple* const tuplerow);
 
-tuple ** 
-pnm_readpam(FILE *       const file, 
-            struct pam * const pamP, 
+tuple **
+pnm_readpam(FILE *       const file,
+            struct pam * const pamP,
             int          const size);
 
-void 
+void
 pnm_writepaminit(struct pam * const pamP);
 
 void
@@ -373,19 +373,19 @@ pnm_formatpamrow(const struct pam * const pamP,
                  unsigned char *    const outbuf,
                  unsigned int *     const rowSizeP);
 
-void 
+void
 pnm_writepamrow(const struct pam * const pamP, const tuple * const tuplerow);
 
 void
-pnm_writepamrowmult(const struct pam * const pamP, 
+pnm_writepamrowmult(const struct pam * const pamP,
                     const tuple *      const tuplerow,
                     unsigned int       const rptcnt);
 
-void 
+void
 pnm_writepam(struct pam * const pamP, tuple ** const tuplearray);
 
 void
-pnm_checkpam(const struct pam *   const pamP, 
+pnm_checkpam(const struct pam *   const pamP,
              enum pm_check_type   const checkType,
              enum pm_check_code * const retvalP);
 
@@ -409,28 +409,28 @@ pnm_allocpamrown(const struct pam * const pamP);
 tuplen *
 pnm_allocpamrown(const struct pam * const pamP);
 
-void 
-pnm_readpamrown(const struct pam * const pamP, 
+void
+pnm_readpamrown(const struct pam * const pamP,
                 tuplen *           const tuplenrow);
 
-void 
-pnm_writepamrown(const struct pam * const pamP, 
+void
+pnm_writepamrown(const struct pam * const pamP,
                  const tuplen *     const tuplenrow);
 
 tuplen **
 pnm_allocpamarrayn(const struct pam * const pamP);
 
 void
-pnm_freepamarrayn(tuplen **          const tuplenarray, 
+pnm_freepamarrayn(tuplen **          const tuplenarray,
                   const struct pam * const pamP);
 
-tuplen** 
-pnm_readpamn(FILE *       const file, 
-             struct pam * const pamP, 
+tuplen**
+pnm_readpamn(FILE *       const file,
+             struct pam * const pamP,
              int          const size);
 
-void 
-pnm_writepamn(struct pam * const pamP, 
+void
+pnm_writepamn(struct pam * const pamP,
               tuplen **    const tuplenarray);
 
 
@@ -501,18 +501,18 @@ pnm_colorname(struct pam * const pamP,
               tuple        const color,
               int          const hexok);
 
-extern double 
+extern double
 pnm_lumin_factor[3];
 
 void
-pnm_YCbCrtuple(const tuple tuple, 
+pnm_YCbCrtuple(const tuple tuple,
                double * const YP, double * const CbP, double * const CrP);
 
-void 
+void
 pnm_YCbCr_to_rgbtuple(const struct pam * const pamP,
                       tuple              const tuple,
                       double             const Y,
-                      double             const Cb, 
+                      double             const Cb,
                       double             const Cr,
                       int *              const overflowP);
 
diff --git a/lib/pbmfont.h b/lib/pbmfont.h
index 5111a075..ad5d3acf 100644
--- a/lib/pbmfont.h
+++ b/lib/pbmfont.h
@@ -20,6 +20,23 @@ extern "C" {
        a 65536 x 65536 glyph occupies 4G pixels. 
     */
 
+typedef wchar_t PM_WCHAR;
+    /* Precaution to make adjustments, if necessary, for systems with
+       unique wchar_t.
+    */
+
+#define PM_FONT_MAXGLYPH 255
+
+#define PM_FONT2_MAXGLYPH 65535
+    /* Upper limit of codepoint value.
+
+       This is large enough to handle Unicode Plane 0 (Basic Multilingual
+       Plane: BMP) which defines the great majority of characters used in
+       modern languages.
+
+       This can be set to a higher value at some cost to efficiency.
+       As of Unicode v. 11.0.0 planes up to 16 are defined.
+    */
 
 struct glyph {
     /* A glyph consists of white borders and the "central glyph" which
@@ -60,7 +77,7 @@ struct font {
        an code point in the range 0..255, this structure describes the
        glyph for that character.
     */
-    int maxwidth, maxheight;
+    unsigned int maxwidth, maxheight;
     int x;
         /* The minimum value of glyph.font.  The left edge of the glyph
            in the glyph set which advances furthest to the left. */
@@ -76,6 +93,33 @@ struct font {
     int fcols, frows;
 };
 
+
+struct font2 {
+    /* Font structure for expanded character set.  Code point is in the
+       range 0..maxglyph .
+     */
+    int maxwidth, maxheight;
+
+    int x;
+        /* The minimum value of glyph.font.  The left edge of the glyph
+           in the glyph set which advances furthest to the left. */
+    int y;
+        /* Amount of white space that should be added between lines of
+           this font.  Can be negative.
+        */
+    struct glyph ** glyph;
+        /* glyph[i] is the glyph for code point i */
+
+    PM_WCHAR maxglyph;
+        /* max code point for glyphs, including vacant slots */
+
+    const bit ** oldfont;
+        /* for compatibility with old pbmtext routines */
+        /* oldfont is NULL if the font is BDF derived */
+
+    unsigned int fcols, frows;
+};
+
 struct font *
 pbm_defaultfont(const char* const which);
 
@@ -93,6 +137,13 @@ pbm_loadpbmfont(const char * const filename);
 struct font *
 pbm_loadbdffont(const char * const filename);
 
+struct font2 *
+pbm_loadbdffont2(const char * const filename,
+                 PM_WCHAR     const maxglyph);
+
+struct font2 *
+pbm_expandbdffont(const struct font * const font);
+
 void
 pbm_dumpfont(struct font * const fontP,
              FILE *        const ofP);
diff --git a/lib/pm_gamma.h b/lib/pm_gamma.h
index 6630e05c..00d551fc 100644
--- a/lib/pm_gamma.h
+++ b/lib/pm_gamma.h
@@ -15,18 +15,28 @@ extern "C" {
 static __inline__ float
 pm_gamma709(float const intensity) {
 
-    /* Here are parameters of the gamma transfer function
-       for the Netpbm formats.  This is CIE Rec 709.
-       
-       This transfer function is linear for sample values 0 .. .018 
+    /* Here are parameters of the gamma transfer function for the Netpbm
+       formats.  This is ITU-R Recommendation BT.709, FKA CIE Rec 709.  It is
+       also ITU-R Recommendation BT.601, FKA CCIR 601.
+
+       This transfer function is linear for sample values 0 .. .018
        and an exponential for larger sample values.
        The exponential is slightly stretched and translated, though,
        unlike the popular pure exponential gamma transfer function.
+
+       The standard actually defines the linear expansion as 4.500, which
+       means there is a discontinuity at linear intensity .018.  We instead
+       use ~4.514 to make a continuous function.  This may have been simply
+       a mistake when this code was written or based on an actual benefit
+       to having a continuous function -- The history is not clear.
+
+       Note that the discrepancy is below the precision of a maxval 255
+       image.
     */
     float const gamma = 2.2;
     float const oneOverGamma = 1.0 / gamma;
     float const linearCutoff = 0.018;
-    float const linearExpansion = 
+    float const linearExpansion =
         (1.099 * pow(linearCutoff, oneOverGamma) - 0.099) / linearCutoff;
 
     float brightness;
@@ -49,9 +59,9 @@ pm_ungamma709(float const brightness) {
     float const gamma = 2.2;
     float const oneOverGamma = 1.0 / gamma;
     float const linearCutoff = 0.018;
-    float const linearExpansion = 
+    float const linearExpansion =
         (1.099 * pow(linearCutoff, oneOverGamma) - 0.099) / linearCutoff;
-    
+
     float intensity;
 
     if (brightness < linearCutoff * linearExpansion)
diff --git a/lib/ppm.h b/lib/ppm.h
index 82241b70..7c483b59 100644
--- a/lib/ppm.h
+++ b/lib/ppm.h
@@ -90,61 +90,61 @@ ppm_allocrow(unsigned int const cols);
 #define ppm_freerow(pixelrow) pm_freerow(pixelrow);
 
 pixel**
-ppm_readppm(FILE *   const fileP, 
-            int *    const colsP, 
-            int *    const rowsP, 
+ppm_readppm(FILE *   const fileP,
+            int *    const colsP,
+            int *    const rowsP,
             pixval * const maxvalP);
 
 void
-ppm_readppminit(FILE *   const fileP, 
-                int *    const colsP, 
-                int *    const rowsP, 
-                pixval * const maxvalP, 
+ppm_readppminit(FILE *   const fileP,
+                int *    const colsP,
+                int *    const rowsP,
+                pixval * const maxvalP,
                 int *    const formatP);
 
 void
-ppm_readppmrow(FILE*  const fileP, 
-               pixel* const pixelrow, 
-               int    const cols, 
-               pixval const maxval, 
+ppm_readppmrow(FILE*  const fileP,
+               pixel* const pixelrow,
+               int    const cols,
+               pixval const maxval,
                int    const format);
 
 void
-ppm_writeppm(FILE *  const fileP, 
-             pixel** const pixels, 
-             int     const cols, 
-             int     const rows, 
-             pixval  const maxval, 
+ppm_writeppm(FILE *  const fileP,
+             pixel** const pixels,
+             int     const cols,
+             int     const rows,
+             pixval  const maxval,
              int     const forceplain);
 
 void
-ppm_writeppminit(FILE*  const fileP, 
-                 int    const cols, 
-                 int    const rows, 
-                 pixval const maxval, 
+ppm_writeppminit(FILE*  const fileP,
+                 int    const cols,
+                 int    const rows,
+                 pixval const maxval,
                  int    const forceplain);
 
 void
-ppm_writeppmrow(FILE *        const fileP, 
-                const pixel * const pixelrow, 
-                int           const cols, 
-                pixval        const maxval, 
+ppm_writeppmrow(FILE *        const fileP,
+                const pixel * const pixelrow,
+                int           const cols,
+                pixval        const maxval,
                 int           const forceplain);
 
 void
-ppm_check(FILE *               const fileP, 
-          enum pm_check_type   const check_type, 
-          int                  const format, 
-          int                  const cols, 
-          int                  const rows, 
+ppm_check(FILE *               const fileP,
+          enum pm_check_type   const check_type,
+          int                  const format,
+          int                  const cols,
+          int                  const rows,
           pixval               const maxval,
           enum pm_check_code * const retval_p);
 
 void
-ppm_nextimage(FILE * const fileP, 
+ppm_nextimage(FILE * const fileP,
               int *  const eofP);
 
-pixel 
+pixel
 ppm_parsecolor(const char * const colorname,
                pixval       const maxval);
 
@@ -154,8 +154,8 @@ ppm_parsecolor2(const char * const colorname,
                 int          const closeOk);
 
 char*
-ppm_colorname(const pixel* const colorP, 
-              pixval       const maxval, 
+ppm_colorname(const pixel* const colorP,
+              pixval       const maxval,
               int          const hexok);
 
 void
@@ -167,9 +167,9 @@ ppm_readcolordict(const char *      const fileName,
                   colorhash_table * const chtP);
 
 void
-ppm_readcolornamefile(const char *      const fileName, 
+ppm_readcolornamefile(const char *      const fileName,
                       int               const mustOpen,
-                      colorhash_table * const chtP, 
+                      colorhash_table * const chtP,
                       const char ***    const colornamesP);
 
 void
@@ -211,9 +211,7 @@ PPM_DISTANCE(pixel const p1,
    combination of intensities, whereas luma is a linear combination of
    gamma-adjusted intensities, as you would find in a Netpbm image.
 
-   These are from ITU-R BT.601.5.  That means they probably aren't technically
-   right for use with PPM images, because the PPM spec says ITU-R BT.709.
-   The two are similar, though.
+   These are from ITU-R BT.601.5.
 */
 #define PPM_LUMINR (0.2989)
 #define PPM_LUMING (0.5866)
@@ -222,16 +220,20 @@ PPM_DISTANCE(pixel const p1,
 #define PPM_LUMIN(p) ( PPM_LUMINR * PPM_GETR(p) \
                        + PPM_LUMING * PPM_GETG(p) \
                        + PPM_LUMINB * PPM_GETB(p) )
-#define PPM_CHROM_B(p) ( -0.16874 * PPM_GETR(p) \
-                         - 0.33126 * PPM_GETG(p) \
+
+/* The coefficients in the following formulae are functions of
+   PPM_LUMIN{R,G,B} and nothing else.
+*/
+#define PPM_CHROM_B(p) ( -0.168736 * PPM_GETR(p) \
+                         - 0.331264 * PPM_GETG(p) \
                          + 0.5 * PPM_GETB(p) )
 #define PPM_CHROM_R(p) ( 0.5 * PPM_GETR(p) \
-                         - 0.41869 * PPM_GETG(p) \
-                         - 0.08131 * PPM_GETB(p) )
+                         - 0.418688 * PPM_GETG(p) \
+                         - 0.081312 * PPM_GETB(p) )
 
 pixel
-ppm_color_from_ycbcr(unsigned int const y, 
-                     int          const cb, 
+ppm_color_from_ycbcr(unsigned int const y,
+                     int          const cb,
                      int          const cr);
 
 /* Hue/Saturation/Value calculations */
diff --git a/lib/util/nstring.c b/lib/util/nstring.c
index 9b132ee2..8cff4d56 100644
--- a/lib/util/nstring.c
+++ b/lib/util/nstring.c
@@ -3,7 +3,7 @@
  *
 
    THIS MODULE WAS ADAPTED FOR NETPBM BY BRYAN HENDERSON ON 2002.03.24.
-   Bryan got the base from 
+   Bryan got the base from
    http://www.ijs.si/software/snprintf/snprintf-2.2.tar.gz, but made
    a lot of changes and additions.
 
@@ -215,7 +215,7 @@ pm_vsnprintf(char *       const str,
              const char * const fmt,
              va_list            ap,
              size_t *     const sizeP) {
-    
+
     size_t str_l = 0;
     const char *p = fmt;
 
@@ -417,7 +417,7 @@ pm_vsnprintf(char *       const str,
                         str_arg_l = 0;
                     else if (!precision_specified)
                         /* truncate string if necessary as requested by
-                           precision 
+                           precision
                         */
                         str_arg_l = strlen(str_arg);
                     else if (precision == 0)
@@ -469,7 +469,7 @@ pm_vsnprintf(char *       const str,
                       undefined. (Actually %hp converts only 16-bits
                       of address and %llp treats address as 64-bit
                       data which is incompatible with (void *)
-                      argument on a 32-bit system). 
+                      argument on a 32-bit system).
                     */
 
                     length_modifier = '\0';
@@ -602,7 +602,7 @@ pm_vsnprintf(char *       const str,
                         && !(zero_padding_insertion_ind < str_arg_l
                              && tmp[zero_padding_insertion_ind] == '0')) {
                         /* assure leading zero for alternate-form
-                           octal numbers 
+                           octal numbers
                         */
                         if (!precision_specified ||
                             precision < num_of_digits+1) {
@@ -616,7 +616,7 @@ pm_vsnprintf(char *       const str,
                         }
                     }
                     /* zero padding to specified precision? */
-                    if (num_of_digits < precision) 
+                    if (num_of_digits < precision)
                         number_of_zeros_to_pad = precision - num_of_digits;
                 }
                 /* zero padding to specified minimal field width? */
@@ -768,7 +768,7 @@ pm_snprintf(char *       const dest,
     va_list ap;
 
     va_start(ap, fmt);
-    
+
     pm_vsnprintf(dest, str_m, fmt, ap, &size);
 
     va_end(ap);
@@ -807,12 +807,12 @@ pm_strdup(const char * const arg) {
 
 void PM_GNU_PRINTF_ATTR(2,3)
 pm_asprintf(const char ** const resultP,
-            const char *  const fmt, 
+            const char *  const fmt,
             ...) {
 
     const char * result;
     va_list varargs;
-    
+
 #if HAVE_VASPRINTF
     int rc;
     va_start(varargs, fmt);
@@ -822,7 +822,7 @@ pm_asprintf(const char ** const resultP,
         result = pm_strsol;
 #else
     size_t dryRunLen;
-    
+
     va_start(varargs, fmt);
 
     pm_vsnprintf(NULL, 0, fmt, varargs, &dryRunLen);
@@ -843,7 +843,7 @@ pm_asprintf(const char ** const resultP,
             va_start(varargs, fmt);
 
             pm_vsnprintf(buffer, allocSize, fmt, varargs, &realLen);
-                
+
             assert(realLen == dryRunLen);
             va_end(varargs);
 
@@ -871,7 +871,7 @@ pm_strfree(const char * const string) {
 
 const char *
 pm_strsep(char ** const stringP, const char * const delim) {
-    const char * retval;   
+    const char * retval;
 
     if (stringP == NULL || *stringP == NULL)
         retval = NULL;
@@ -881,16 +881,16 @@ pm_strsep(char ** const stringP, const char * const delim) {
         retval = *stringP;
 
         for (p = *stringP; *p && strchr(delim, *p) == NULL; ++p);
- 
+
         if (*p) {
-            /* We hit a delimiter, not end-of-string.  So null out the 
+            /* We hit a delimiter, not end-of-string.  So null out the
                delimiter and advance user's pointer to the next token
             */
             *p++ = '\0';
             *stringP = p;
         } else {
-            /* We ran out of string.  So the end-of-string delimiter is 
-               already there, and we set the user's pointer to NULL to 
+            /* We ran out of string.  So the end-of-string delimiter is
+               already there, and we set the user's pointer to NULL to
                indicate there are no more tokens.
             */
             *stringP = NULL;
@@ -914,7 +914,7 @@ pm_stripeq(const char * const comparand,
     const char * px;
     const char * qx;
     bool equal;
-  
+
     /* Make p and q point to the first non-blank character in each string.
        If there are no non-blank characters, make them point to the terminating
        NUL.
@@ -1021,7 +1021,7 @@ pm_interpret_uint(const char *   const string,
         */
         char * tail;
         unsigned long ulongValue;
-        
+
         errno = 0;  /* So we can tell if strtoul() overflowed */
 
         ulongValue = strtoul(string, &tail, 10);
diff --git a/lib/util/shhopt.c b/lib/util/shhopt.c
index ccf2d1eb..ab489fef 100644
--- a/lib/util/shhopt.c
+++ b/lib/util/shhopt.c
@@ -168,7 +168,7 @@ optString(const optEntry opte, int lng)
 }
 
 
-    
+
 static optEntry
 optStructToEntry(const optStruct opt) {
 /*----------------------------------------------------------------------------
@@ -202,7 +202,7 @@ optStructTblToEntryTbl(const optStruct optStructTable[]) {
     int i;
 
     optEntry *optEntryTable;  /* malloc'ed array */
-    
+
     /* Count the entries in optStructTable[] */
     for (i = 0; optStructTable[i].type != OPT_END && i < 500; i++);
     count = i+1;
@@ -210,7 +210,7 @@ optStructTblToEntryTbl(const optStruct optStructTable[]) {
     optEntryTable = (optEntry *) malloc(count * sizeof(optEntry));
     if (optEntryTable) {
         int i;
-        for (i = 0; i < count; i++) 
+        for (i = 0; i < count; i++)
             optEntryTable[i] = optStructToEntry(optStructTable[i]);
     }
     return(optEntryTable);
@@ -284,7 +284,7 @@ getToken(const char *  const tokenStart,
    doesn't necessarily advance.
 -----------------------------------------------------------------------------*/
     const char * error;
-    
+
     pm_gettoken(tokenStart, delimiter, tokenP, nextP, &error);
 
     if (error)
@@ -383,7 +383,7 @@ parseStringList(const char *   const listText,
  |
  |  FUNCTION      Perform the action of an option.
  |
- |  INPUT         opt     element in array of defined options that 
+ |  INPUT         opt     element in array of defined options that
  |                        applies to this option
  |                arg     argument to option, if it applies.
  |                lng     was the option given as a long option?
@@ -411,7 +411,7 @@ optExecute(optEntry  const opt, char *arg, int lng)
     case OPT_LONG: {
         long tmp;
         char *e;
-	  
+
         if (arg == NULL)
             optFatal("internal error: optExecute() called with NULL argument "
                      "'%s'", optString(opt, lng));
@@ -429,12 +429,12 @@ optExecute(optEntry  const opt, char *arg, int lng)
                 *((long *) opt.arg) = tmp;
         }
     } break;
-	
+
     case OPT_UINT:
     case OPT_ULONG: {
         unsigned long tmp;
         char * tailPtr;
-        
+
         if (arg == NULL)
             optFatal("internal error: optExecute() called with NULL argument "
                      "'%s'", optString(opt, lng));
@@ -460,7 +460,7 @@ optExecute(optEntry  const opt, char *arg, int lng)
     case OPT_FLOAT: {
         float tmp;
         char *e;
-	  
+
         if (arg == NULL)
             optFatal("internal error: optExecute() called with NULL argument "
                      "'%s'", optString(opt, lng));
@@ -597,7 +597,7 @@ pm_optParseOptions(int *argc, char *argv[], optStruct opt[], int allowNegNum)
             arg = NULL;
             if ((p = strchr(argv[ai], '=')) != NULL)
                 arg = p + 1;
-	    
+
             /* does this option take an argument? */
             optarg = -1;
             if (optNeedsArgument(opt_table[mi])) {
@@ -666,7 +666,7 @@ pm_optParseOptions(int *argc, char *argv[], optStruct opt[], int allowNegNum)
 
 static void
 parse_short_option_token(char *argv[], const int argc, const int ai,
-                         const optEntry opt_table[], 
+                         const optEntry opt_table[],
                          int * const tokens_consumed_p) {
 /*----------------------------------------------------------------------------
    Parse a cluster of short options, e.g. -walne .
@@ -681,7 +681,7 @@ parse_short_option_token(char *argv[], const int argc, const int ai,
     char *arg;
     int mi;   /* index into option table */
     unsigned char processed_arg;  /* boolean */
-        /* We processed an argument to one of the one-character options. 
+        /* We processed an argument to one of the one-character options.
            This necessarily means there are no more options in this token
            to process.
            */
@@ -707,7 +707,7 @@ parse_short_option_token(char *argv[], const int argc, const int ai,
             (*tokens_consumed_p)++;
 		    }
 		    processed_arg = 1;
-		} else 
+		} else
             arg = NULL;
 		/* perform the action of this option. */
 		optExecute(opt_table[mi], arg, 0);
@@ -761,7 +761,7 @@ parse_long_option(char *   const argv[],
                   int      const argc,
                   int      const ai,
                   int      const namepos,
-                  optEntry const opt_table[], 
+                  optEntry const opt_table[],
                   int *    const tokens_consumed_p) {
 /*----------------------------------------------------------------------------
    Parse a long option, e.g. -verbose or --verbose.
@@ -788,11 +788,11 @@ parse_long_option(char *   const argv[],
         fatalUnrecognizedLongOption(argv[ai], opt_table);
 
     /* possibly locate the argument to this option. */
-    { 
+    {
         char *p;
         if ((p = strchr(argv[ai], '=')) != NULL)
             equals_arg = p + 1;
-        else 
+        else
             equals_arg = NULL;
     }
     /* does this option take an argument? */
@@ -812,7 +812,7 @@ parse_long_option(char *   const argv[],
             optFatal("option `%s' doesn't allow an argument, but you "
                      "have specified it in the form name=value",
                      optString(opt_table[mi], 1));
-        else 
+        else
             arg = NULL;
     }
     /* perform the action of this option. */
@@ -849,7 +849,7 @@ parse_long_option(char *   const argv[],
  |
  |                This differs from pm_optParseOptions in that it accepts
  |                long options with just one hyphen and doesn't accept
- |                any short options.  It also has accommodations for 
+ |                any short options.  It also has accommodations for
  |                future expansion.
  |
  |                Options and arguments used are removed from the argv-
@@ -858,11 +858,11 @@ parse_long_option(char *   const argv[],
  |                Any error leads to program abortion.
  */
 void
-pm_optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt, 
+pm_optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt,
                  const unsigned long flags)
 /*----------------------------------------------------------------------------
    This does the same thing as pm_optParseOptions3(), except that there is no
-   "specified" return value.  
+   "specified" return value.
 
    This function exists for backward compatibility.
 -----------------------------------------------------------------------------*/
@@ -877,7 +877,7 @@ pm_optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt,
     if (opt3.opt_table == NULL)
         optFatal("Memory allocation failed (trying to allocate space for "
                  "new-format option table)");
-    
+
     pm_optParseOptions3(argc_p, argv, opt3, sizeof(opt3), flags);
 
     free(opt3.opt_table);
@@ -914,13 +914,13 @@ zero_specified(optEntry opt_table[]) {
  |                        Size of "opt" (since the caller may be older
  |                        than this function, it may be using a structure
  |                        with fewer fields than exist today.  We use this
- |                        parameter to handle those older callers). 
+ |                        parameter to handle those older callers).
  |                flags   Result is undefined if not zero.
  |                        For future expansion.
  |
  |  OUTPUT        argc    new argument count.
  |                argv    array with arguments removed.
- |                
+ |
  |                Areas pointed to by pointers in 'opt' get updated with
  |                option values and counts.
  |
@@ -934,7 +934,7 @@ zero_specified(optEntry opt_table[]) {
  |
  |                This differs from pm_optParseOptions in that it accepts
  |                long options with just one hyphen and doesn't accept
- |                any short options.  It also has accommodations for 
+ |                any short options.  It also has accommodations for
  |                future expansion.
  |
  |                Options and arguments used are removed from the argv-
@@ -943,7 +943,7 @@ zero_specified(optEntry opt_table[]) {
  |                Any error leads to program abortion.
  */
 void
-pm_optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt, 
+pm_optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt,
                  const unsigned int optStructSize, const unsigned long flags)
 {
     int  ai;        /* argv index. */
@@ -958,10 +958,10 @@ pm_optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt,
      */
     no_more_options = 0;  /* initial value */
     for (ai = 0; ai < *argc_p; ) {
-        if (no_more_options) 
+        if (no_more_options)
             /* Can't be an option -- there aren't any more */
             ai++;
-        else if (argv[ai][0] != '-') 
+        else if (argv[ai][0] != '-')
             /* Can't be an option -- doesn't start with a dash */
             ai++;
         else {
@@ -980,7 +980,7 @@ pm_optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt,
                     /* The entire thing is "--".  That means no more options */
                     tokens_consumed = 1;
                     no_more_options = 1;
-                } else 
+                } else
                     /* It's an option that starts with "--" */
                     parse_long_option(argv, *argc_p, ai, 2,
                                       opt.opt_table, &tokens_consumed);
@@ -994,7 +994,7 @@ pm_optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt,
                     parse_long_option(argv, *argc_p, ai, 1,
                                       opt.opt_table, &tokens_consumed);
                 }
-            
+
             }
             /* remove option and any argument from the argv-array. */
             {
diff --git a/test/all-in-place.test b/test/all-in-place.test
index 52d38bcf..a52739ed 100755
--- a/test/all-in-place.test
+++ b/test/all-in-place.test
@@ -406,7 +406,7 @@ for i in $ordinary_testprogs
     testExitStatus $i 0 ${exitStatus}
 
     if [ ${CHECK_TYPE} != "install" ]
-      then grepSwitch=$((${grepSwitch} * ${exitStatus})) ;
+      then grepSwitch=$((${grepSwitch} * ${exitStatus}==0 ? 0 : 1)) ;
     fi
   done
 
diff --git a/test/gif-roundtrip.ok b/test/gif-roundtrip.ok
index 552bf90c..1704ba03 100644
--- a/test/gif-roundtrip.ok
+++ b/test/gif-roundtrip.ok
@@ -3,9 +3,20 @@
 1571496937 33838
 1571496937 33838
 1571496937 33838
+1571496937 33838
 2425386270 41
 2425386270 41
 2425386270 41
 2425386270 41
 2425386270 41
-P1 4 1 0101 
\ No newline at end of file
+P1 4 1 0101 
+4030 0 , 4030 0
+4031 0 , 4031 0
+4097 0 , 4097 0
+238 0 , 238 0
+239 0 , 239 0
+240 0 , 240 0
+241 0 , 241 0
+255 0 , 255 0
+256 0 , 256 0
+257 0 , 257 0
diff --git a/test/gif-roundtrip.test b/test/gif-roundtrip.test
index a6d0ec4c..b49cc6d2 100755
--- a/test/gif-roundtrip.test
+++ b/test/gif-roundtrip.test
@@ -1,7 +1,6 @@
 #! /bin/bash
 # This script tests: giftopnm pamtogif
-# Also requires: ppmtorgb3 rgb3toppm pbmmake pnminvert
-
+# Also requires: ppmtorgb3 rgb3toppm pbmmake pnminvert pamcut pamdepth
 
 tmpdir=${tmpdir:-/tmp}
 
@@ -33,12 +32,13 @@ rm ${test_ppm} ${test_grn} ${test_blu} ${out_red} ${out_grn}
 
 # Test 2. Should produce 1571496937 33838
 # which is the result of cksum testimg.red
-# four times
+# five times
 
 test_gif=${tmpdir}/testimg.gif
 
 pamtogif ${test_red} | giftopnm | cksum
 pamtogif -interlace ${test_red} | giftopnm | cksum
+pamtogif -noclear ${test_red} | giftopnm | cksum
 pamtogif -sort ${test_red} | tee ${test_gif} | \
   giftopnm | cksum
 echo "junk" >> ${test_gif} && \
@@ -48,6 +48,7 @@ rm  ${test_gif} ${test_red}
 
 
 # Test 3. Should produce 2425386270 41 five times.
+# testgrid.pbm is too small for -noclear to take effect 
 
 pamtogif testgrid.pbm | giftopnm | cksum
 pamtogif -nolzw testgrid.pbm | giftopnm | cksum
@@ -56,11 +57,107 @@ pamtogif -alpha=testgrid.pbm testgrid.pbm | giftopnm | cksum
 pamtogif -transparent=white testgrid.pbm | giftopnm -alpha=- | \
   pnminvert | cksum
 
+
 # Test 4.
 # In this gif file the code length changes after the last image data.
 # Image data: 3 bits, end code 4 bits.
 # Should produce P1 4 1 0 1 0 1
 
 pbmmake -g 4 1 | \
-  pamtogif | giftopnm -plain | \
+  pamtogif -verbose | giftopnm -plain | \
   tr '\n' ' '
+echo ""
+
+
+# Test 5.
+# Test whether saturated code tables are handled correctly.
+# Test input images which most use up the string code table or
+# come close to doing so.
+
+# Should produce:
+# 4030 0 , 4030 0
+# 4031 0 , 4031 0
+# 4097 0 , 4097 0
+# 238 0 , 238 0
+# 239 0 , 239 0
+# 240 0 , 240 0
+# 241 0 , 241 0
+# 255 0 , 255 0
+# 256 0 , 256 0
+# 257 0 , 257 0
+
+test4097_pgm=${tmpdir}/testimg4097.pgm
+test_pgm=${tmpdir}/testimg.pgm
+
+# The following awk script produces a PGM file with no repeated sequences.
+# Here is a smaller example with the same property:
+# P2
+# 8 8
+# 7
+# 0 0 1 1 0 2 2 0
+# 3 3 0 4 4 0 5 5
+# 0 6 6 0 7 7 1 2
+# 1 3 1 4 1 5 1 6
+# 1 7 2 3 2 4 2 5
+# 2 6 2 7 3 4 3 5
+# 3 6 3 7 4 5 4 6
+# 4 7 5 6 5 7 6 7
+
+maxval=63
+awk -v maxval=${maxval} 'BEGIN \
+{ print "P2"
+         print "1 "(maxval+1) * (maxval+1) +1
+         print maxval
+         print 0 
+         for (j=i+1; j<=maxval; ++j)
+	    {print 0; print j; print j }
+         for (i=1; i<=maxval; ++i)
+             for (j=i+1; j<=maxval; ++j)
+                 {print i; print j }
+         print 0
+}' > ${test4097_pgm}
+
+for size in 4030 4031 4097
+  do
+  pamcut -height ${size} ${test4097_pgm} > ${test_pgm} &&
+  pamtogif -verbose ${test_pgm} | giftopnm | pamdepth ${maxval} | \
+    cmp - ${test_pgm}
+  # pamdepth ${maxval} is necessary because
+  # giftopnm output is maxval 255
+  echo -n ${size} $? ", "
+  pamtogif -nolzw ${test_pgm} | giftopnm | pamdepth ${maxval} | \
+    cmp - ${test_pgm}
+  echo ${size} $?
+  rm ${test_pgm}
+  done 
+
+rm ${test4097_pgm}
+test257_pgm=${tmpdir}/testimg257.pgm
+
+maxval=15
+awk -v maxval=${maxval} 'BEGIN \
+{ print "P2"
+         print "1 "(maxval+1) * (maxval+1) +1
+         print maxval
+         print 0 
+         for (j=i+1; j<=maxval; ++j)
+	    {print 0; print j; print j }
+         for (i=1; i<=maxval; ++i)
+             for (j=i+1; j<=maxval; ++j)
+                 {print i; print j }
+         print 0
+}' >  ${test257_pgm}
+
+for size in 238 239 240 241 255 256 257
+  do
+  pamcut -height=${size} ${test257_pgm} > ${test_pgm} &&
+  pamtogif -verbose ${test_pgm} | giftopnm | pamdepth ${maxval} | \
+    cmp - ${test_pgm}
+  echo -n ${size} $? ", "
+  pamtogif -nolzw -verbose ${test_pgm} | giftopnm | pamdepth ${maxval} | \
+    cmp - ${test_pgm}
+  echo ${size} $?
+  rm ${test_pgm}
+  done 
+
+rm ${test257_pgm}
diff --git a/test/jpeg-roundtrip.ok b/test/jpeg-roundtrip.ok
index 9a93f216..5551f78e 100644
--- a/test/jpeg-roundtrip.ok
+++ b/test/jpeg-roundtrip.ok
@@ -1,3 +1,3 @@
-  3 1000.00 1000.00 1000.00
-  3 1000.00 1000.00 1000.00
-  3 1000.00 1000.00 1000.00
+match
+match
+match
diff --git a/test/jpeg-roundtrip.test b/test/jpeg-roundtrip.test
index 23df6341..7cc0fc20 100755
--- a/test/jpeg-roundtrip.test
+++ b/test/jpeg-roundtrip.test
@@ -4,29 +4,16 @@
 
 # TODO: threshold has been determined without much thought.
 # Observed pnmpsnr output: 56.20 58.26 49.38
+# On another system:       54.73 49.41 44.52
 # A small margin has been added to the above numbers.
 
-# Should print 3 1 1 1 three times
+# Should print "match" three times
 
 pnmtojpeg testimg.ppm | jpegtopnm | \
-  pnmpsnr -machine - testimg.ppm |\
-  awk '{printf("%3d %.2f %.2f %.2f\n", NF,
-                $1>55.0 ? 1000.00 : $1,
-                $2>57.0 ? 1000.00 : $2,
-                $3>48.0 ? 1000.00 : $3) }'
-
+  pnmpsnr -target1=54 -target2=49 -target3=44 - testimg.ppm
 
 pnmtojpeg testimg.ppm -opt | jpegtopnm | \
-  pnmpsnr -machine - testimg.ppm |\
-  awk '{printf("%3d %.2f %.2f %.2f\n", NF,
-                $1>55.0 ? 1000.00 : $1,
-                $2>57.0 ? 1000.00 : $2,
-                $3>48.0 ? 1000.00 : $3) }'
-
+  pnmpsnr -target1=54 -target2=49 -target3=44 - testimg.ppm
 
 pnmtojpeg testimg.ppm -progressive | jpegtopnm | \
-  pnmpsnr -machine - testimg.ppm |\
-  awk '{printf("%3d %.2f %.2f %.2f\n", NF,
-                $1>55.0 ? 1000.00 : $1,
-                $2>57.0 ? 1000.00 : $2,
-                $3>48.0 ? 1000.00 : $3) }'
+  pnmpsnr -target1=54 -target2=49 -target3=44 - testimg.ppm
diff --git a/test/pnmpsnr.ok b/test/pnmpsnr.ok
index 4c7eccde..3469f836 100644
--- a/test/pnmpsnr.ok
+++ b/test/pnmpsnr.ok
@@ -1,4 +1,7 @@
 0.00
 inf
 1000.00
+match
 300.00 300.00 300.00
+match
+match
diff --git a/test/pnmpsnr.test b/test/pnmpsnr.test
index c7591983..f24c08aa 100755
--- a/test/pnmpsnr.test
+++ b/test/pnmpsnr.test
@@ -15,6 +15,9 @@ pbmmake -b 10 10 > ${b_pbm}
 pnmpsnr  ${w_pbm}  ${b_pbm} -machine
 pnmpsnr  ${w_pbm}  ${w_pbm} -machine
 pnmpsnr  ${w_pbm}  ${w_pbm} -machine -max=1000
+pnmpsnr  ${w_pbm}  ${w_pbm} -target=1000
 pnmpsnr  testimg.ppm  testimg.ppm -machine -max=300
+pnmpsnr  testimg.ppm  testimg.ppm -target=1000
+pnmpsnr  testimg.ppm  testimg.ppm -target1=1000 -target2=1000 -target3=1000
 
 rm ${b_pbm} ${w_pbm}
diff --git a/test/ppmhist.ok b/test/ppmhist.ok
index d7ecf07e..f2ba637b 100644
--- a/test/ppmhist.ok
+++ b/test/ppmhist.ok
@@ -1,5 +1,3 @@
-   r     g     b   	 lum 	 count  
- ----- ----- ----- 	-----	------- 
      0     0     0	    0	      2 
      1     1     1	    1	      2 
      2     2     2	    2	      2 
@@ -8,4 +6,14 @@
      5     5     5	    5	      2 
      6     6     6	    6	      2 
      8     8     8	    8	      2 
-3438989921 711087
+3081591280 60957
+ Summary: 1 colors: 1 black, 0 white, 0 gray, 0 color
+ Summary: 1 colors: 0 black, 1 white, 0 gray, 0 color
+ Summary: 2 colors: 1 black, 1 white, 0 gray, 0 color
+ Summary: 16 colors: 1 black, 1 white, 14 gray, 0 color
+ Summary: 8 colors: 1 black, 1 white, 6 gray, 0 color
+ Summary: 8 colors: 1 black, 1 white, 0 gray, 6 color
+ Summary: 27 colors: 1 black, 1 white, 1 gray, 24 color
+ Summary: 6 colors: 0 black, 0 white, 0 gray, 6 color
+ Summary: 6 colors: 1 black, 0 white, 0 gray, 5 color
+ Summary: 6 colors: 1 black, 1 white, 1 gray, 3 color
diff --git a/test/ppmhist.test b/test/ppmhist.test
index 97fbd79d..27d31562 100755
--- a/test/ppmhist.test
+++ b/test/ppmhist.test
@@ -1,7 +1,22 @@
 #! /bin/bash
 # This script tests: ppmhist
-# Also requires: pgmramp
+# Also requires: pgmramp pamtopnm pbmmake pamseq ppmpat
 
+pgmramp -maxval=8 -lr 8 2 | ppmhist -sort=rgb -noheader
+ppmhist -map -sort=rgb -noheader testimg.ppm | pamtopnm | cksum
 
-pgmramp -maxval=8 -lr 8 2 | ppmhist -sort=rgb
-ppmhist -map -sort=rgb testimg.ppm | cksum
+# Test summary header
+
+pbmmake -b 2 1 | ppmhist   | head -n1
+pbmmake -w 2 1 | ppmhist   | head -n1
+pbmmake -g 2 1 | ppmhist   | head -n1
+pgmramp -lr 16 1 | ppmhist | head -n1
+pgmramp -lr -maxval=7 16 1 | ppmhist | head -n1
+pamseq  3 1 | ppmhist | head -n1
+pamseq  3 2 | ppmhist | head -n1
+ppmpat -madras --color=rgb:32/0d/b7,rgb:31/58/a3,rgb:e9/5e/d4 25 25 | \
+  ppmhist | head -n1
+ppmpat -madras --color=rgb:00/00/00,rgb:31/58/a3,rgb:e9/5e/d4 25 25 | \
+  ppmhist | head -n1
+ppmpat -madras --color=rgb:00/00/00,rgb:31/58/a3,rgb:ff/ff/ff 25 25 | \
+  ppmhist | head -n1
\ No newline at end of file
diff --git a/test/tiffcmyk-roundtrip.ok b/test/tiffcmyk-roundtrip.ok
index 5f4963cd..05c0b188 100644
--- a/test/tiffcmyk-roundtrip.ok
+++ b/test/tiffcmyk-roundtrip.ok
@@ -1,5 +1,5 @@
-  3 1000.00 1000.00 1000.00
-  3 1000.00 1000.00 1000.00
-  3 1000.00 1000.00 1000.00
-  3 1000.00 1000.00 1000.00
-  3 1000.00 1000.00 1000.00
+match
+match
+match
+match
+match
diff --git a/test/tiffcmyk-roundtrip.test b/test/tiffcmyk-roundtrip.test
index 99e02c48..133c81b7 100755
--- a/test/tiffcmyk-roundtrip.test
+++ b/test/tiffcmyk-roundtrip.test
@@ -15,47 +15,28 @@ output_tiff=${tmpdir}/output.tiff
 
 pnmtotiffcmyk testimg.ppm > ${output_tiff} && \
   tifftopnm -headerdump -byrow ${output_tiff} | \
-  pnmpsnr -machine - testimg.ppm | \
-  awk '{printf("%3d %.2f %.2f %.2f\n", NF,
-                $1>45.0 ? 1000.00 : $1,
-                $2>59.5 ? 1000.00 : $2,
-                $3>56.5 ? 1000.00 : $3) }'
+  pnmpsnr -target1=45.0 -target2=59.5 -target3=56.5 - testimg.ppm
 
 
 # Note that "-rowsperstrip=1" does not work
 pnmtotiffcmyk -rowsperstrip 1 -lsb2msb testimg.ppm > ${output_tiff} && \
   tifftopnm -respectfillorder -byrow  ${output_tiff} | \
-  pnmpsnr -machine - testimg.ppm | \
-  awk '{printf("%3d %.2f %.2f %.2f\n", NF,
-                $1>45.0 ? 1000.00 : $1,
-                $2>59.5 ? 1000.00 : $2,
-                $3>56.5 ? 1000.00 : $3) }'
+  pnmpsnr -target1=45.0 -target2=59.5 -target3=56.5 - testimg.ppm
 
 
 pnmtotiffcmyk -packbits testimg.ppm > ${output_tiff} && \
   tifftopnm -byrow ${output_tiff} | \
-  pnmpsnr -machine - testimg.ppm | \
-  awk '{printf("%3d %.2f %.2f %.2f\n", NF,
-                $1>45.0 ? 1000.00 : $1,
-                $2>59.5 ? 1000.00 : $2,
-                $3>56.5 ? 1000.00 : $3) }'
+  pnmpsnr -target1=45.0 -target2=59.5 -target3=56.5 - testimg.ppm
 
 
 pnmtotiffcmyk -lzw testimg.ppm > ${output_tiff} && \
   tifftopnm -byrow ${output_tiff} | \
-    pnmpsnr -machine - testimg.ppm | \
-  awk '{printf("%3d %.2f %.2f %.2f\n", NF,
-                $1>45.0 ? 1000.00 : $1,
-                $2>59.5 ? 1000.00 : $2,
-                $3>56.5 ? 1000.00 : $3) }'
+  pnmpsnr -target1=45.0 -target2=59.5 -target3=56.5 - testimg.ppm
 
 
 pnmtotiffcmyk -lzw -predictor 2 testimg.ppm > ${output_tiff} && \
   tifftopnm -respectfillorder -byrow ${output_tiff} | \
-  pnmpsnr -machine - testimg.ppm | \
-  awk '{printf("%3d %.2f %.2f %.2f\n", NF,
-                $1>45.0 ? 1000.00 : $1,
-                $2>59.5 ? 1000.00 : $2,
-                $3>56.5 ? 1000.00 : $3) }'
+  pnmpsnr -target1=45.0 -target2=59.5 -target=56.5 - testimg.ppm
 
-rm ${output_tiff}
\ No newline at end of file
+
+rm ${output_tiff}
diff --git a/version.mk b/version.mk
index 49e6b878..f53df329 100644
--- a/version.mk
+++ b/version.mk
@@ -1,3 +1,3 @@
 NETPBM_MAJOR_RELEASE = 10
-NETPBM_MINOR_RELEASE = 81
-NETPBM_POINT_RELEASE = 3
+NETPBM_MINOR_RELEASE = 82
+NETPBM_POINT_RELEASE = 0