about summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2019-06-28 23:45:11 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2019-06-28 23:45:11 +0000
commitcdf6e0151411d887fef61245cb303ef190b29335 (patch)
tree678c2212e125e66e0a868773e2b4ec460794da4e /lib
parentde1311e820dc892f1a3c5c9ae70dbc56868030d8 (diff)
downloadnetpbm-mirror-cdf6e0151411d887fef61245cb303ef190b29335.tar.gz
netpbm-mirror-cdf6e0151411d887fef61245cb303ef190b29335.tar.xz
netpbm-mirror-cdf6e0151411d887fef61245cb303ef190b29335.zip
Promote Advanced to Stable
git-svn-id: http://svn.code.sf.net/p/netpbm/code/stable@3641 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile21
-rw-r--r--lib/colorname.c113
-rw-r--r--lib/colorname.h14
-rw-r--r--lib/libpam.c191
-rw-r--r--lib/libpam.h6
-rw-r--r--lib/libpamcolor.c643
-rw-r--r--lib/libpamn.c215
-rw-r--r--lib/libpbm2.c24
-rw-r--r--lib/libpbm3.c100
-rw-r--r--lib/libpbmfont.c1603
-rw-r--r--lib/libpbmfont0.c335
-rw-r--r--lib/libpbmfont1.c359
-rw-r--r--lib/libpbmfont2.c1051
-rw-r--r--lib/libpbmfontdump.c96
-rw-r--r--lib/libpgm.h6
-rw-r--r--lib/libpm.c115
-rw-r--r--lib/libpnm3.c293
-rw-r--r--lib/libppm1.c108
-rw-r--r--lib/libppmcolor.c486
-rw-r--r--lib/libppmd.c7
-rw-r--r--lib/libppmfuzzy.c2
-rw-r--r--lib/libsystem.c276
-rw-r--r--lib/pam.h165
-rw-r--r--lib/path.c157
-rw-r--r--lib/pbmfont.h288
-rw-r--r--lib/pbmfontdata.h7
-rw-r--r--lib/pbmfontdata0.c9
-rw-r--r--lib/pbmfontdata1.c269
-rw-r--r--lib/pbmfontdata2.c463
-rw-r--r--lib/pgm.h38
-rw-r--r--lib/pm.h71
-rw-r--r--lib/pm_gamma.h24
-rw-r--r--lib/pm_system.h47
-rw-r--r--lib/pmfileio.c187
-rw-r--r--lib/pnm.h53
-rw-r--r--lib/ppm.h93
-rw-r--r--lib/ppmdraw.h65
-rw-r--r--lib/util/mallocvar.c8
-rw-r--r--lib/util/mallocvar.h18
-rw-r--r--lib/util/nstring.c105
-rw-r--r--lib/util/nstring.h33
-rw-r--r--lib/util/pm_c_util.h13
-rw-r--r--lib/util/runlength.c103
-rw-r--r--lib/util/runlength.h3
-rw-r--r--lib/util/shhopt.c58
-rw-r--r--lib/util/shhopt.h66
-rw-r--r--lib/util/wordaccess.h75
-rw-r--r--lib/util/wordaccess_64_le.h28
-rw-r--r--lib/util/wordaccess_be_aligned.h35
-rw-r--r--lib/util/wordaccess_be_unaligned.h20
-rw-r--r--lib/util/wordaccess_generic.h29
-rw-r--r--lib/util/wordintclz.h93
52 files changed, 5433 insertions, 3254 deletions
diff --git a/lib/Makefile b/lib/Makefile
index 1e607ee5..bc758df4 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -27,7 +27,9 @@ endif
 
 LIBOBJECTS = libpm.o pmfileio.o fileio.o colorname.o \
 	libpamd.o \
-	libpbm1.o libpbm2.o libpbm3.o libpbmfont.o \
+	libpbm1.o libpbm2.o libpbm3.o \
+	libpbmfont0.o libpbmfont1.o libpbmfont2.o \
+	pbmfontdata0.o pbmfontdata1.o pbmfontdata2.o \
 	libpgm1.o libpgm2.o \
 	libppm1.o libppm2.o libppmcmap.o libppmcolor.o libppmfuzzy.o \
 	libppmd.o ppmdfont.o standardppmdfont.o path.o \
@@ -55,7 +57,7 @@ MANUALS3 = libnetpbm
 MANUALS5 = pbm pgm ppm pnm pam
 
 INTERFACE_HEADERS = colorname.h \
-	pam.h pamdraw.h pammap.h pbm.h pbmfont.h \
+	pam.h pamdraw.h pammap.h pbm.h pbmfont.h pbmfontdata.h \
 	pgm.h pm.h pm_gamma.h pm_system.h pnm.h \
 	ppm.h ppmcmap.h ppmdfont.h ppmdraw.h ppmfloyd.h \
 	util/mallocvar.h util/runlength.h util/shhopt.h \
@@ -219,7 +221,7 @@ libpm.o: compile.h
 .PHONY: install.lib
 ifeq ($(NETPBMLIBTYPE),unixshared)
 # install a Unix-style shared library
-install.lib: $(PKGDIR)/lib $(PKGDIR)/link
+install.lib: $(PKGDIR)/lib $(PKGDIR)/sharedlink
 	cd $(PKGDIR)/lib ; rm -f libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ).*
 	$(INSTALL) -c -m $(INSTALL_PERM_LIBD) \
 	  libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ).$(MIN)  $(PKGDIR)/lib/
@@ -261,26 +263,27 @@ $(INTERFACE_HEADERS:%=%_installhdr): $(PKGDIR)/include/netpbm
 	  $(SRCDIR)/lib/$(@:%_installhdr=%) $(PKGDIR)/include/netpbm/
 
 .PHONY: install.staticlib
-install.staticlib: $(PKGDIR)/link
+install.staticlib: $(PKGDIR)/staticlink
 	$(INSTALL) -c -m $(INSTALL_PERM_LIBS) libnetpbm.$(STATICLIBSUFFIX) \
-	  $(PKGDIR)/link
+	  $(PKGDIR)/staticlink
 
 # Install a shared library stub -- the ".so" file used at link time to
 # prepare a program for dynamically linking a library at run time 
 .PHONY: install.sharedlibstub
-install.sharedlibstub: $(PKGDIR)/link
+install.sharedlibstub: $(PKGDIR)/sharedlink
 ifeq ($(NETPBMLIBTYPE),unixshared)
 # install the link-time (.so) links to the runtime libraries
-	cd $(PKGDIR)/link ; \
+	cd $(PKGDIR)/sharedlink ; \
           rm -f libnetpbm.$(NETPBMLIBSUFFIX); \
           $(SYMLINK) ../lib/libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ) \
             libnetpbm.$(NETPBMLIBSUFFIX)
 endif
 ifeq ($(NETPBMLIBTYPE),dll)
-	$(INSTALL) -c -m $(INSTALL_PERM_LIBS) libnetpbm.dll.a $(PKGDIR)/link
+	$(INSTALL) -c -m $(INSTALL_PERM_LIBS) libnetpbm.dll.a \
+	  $(PKGDIR)/sharedlink
 endif
 ifeq ($(NETPBMLIBTYPE),dylib)
-	cd $(PKGDIR)/link/ ; \
+	cd $(PKGDIR)/sharedlink/ ; \
           rm -f libnetpbm.dylib; \
 	$(SYMLINK) ../lib/libnetpbm.$(MAJ).$(MIN).dylib libnetpbm.dylib
 endif
diff --git a/lib/colorname.c b/lib/colorname.c
index 83cf5d1a..fe580cb9 100644
--- a/lib/colorname.c
+++ b/lib/colorname.c
@@ -12,6 +12,7 @@
 ** implied warranty.
 */
 
+#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 */
 
@@ -20,8 +21,10 @@
 #include <string.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <math.h>
 
 #include "netpbm/nstring.h"
+#include "netpbm/mallocvar.h"
 
 #include "colorname.h"
 
@@ -29,7 +32,7 @@ static int lineNo;
 
 
 
-void 
+void
 pm_canonstr(char * const arg) {
 /*----------------------------------------------------------------------------
    Modify string 'arg' to canonical form: lower case, no white space.
@@ -90,7 +93,7 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
    environment variable whose name is RGB_ENV (e.g. "RGBDEF").  Except
    if that environment variable is not set, it is the first file found,
    if any, in the search path RGB_DB_PATH.
-   
+
    'must_open' is a logical: we must get the file open or die.  If
    'must_open' is true and we can't open the file (e.g. it doesn't
    exist), exit the program with an error message.  If 'must_open' is
@@ -108,7 +111,7 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
                          "named %s, per the %s environment variable.  "
                          "errno = %d (%s)",
                          rgbdef, RGBENV, errno, strerror(errno));
-        } else {            
+        } else {
             /* The environment variable isn't set, so try the hardcoded
                default color name dictionary locations.
             */
@@ -119,7 +122,7 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
                          "path '%s' "
                          "and Environment variable %s not set.  Set %s to "
                          "the pathname of your rgb.txt file or don't use "
-                         "color names.", 
+                         "color names.",
                          RGB_DB_PATH, RGBENV, RGBENV);
             }
         }
@@ -128,7 +131,7 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
         if (f == NULL && must_open)
             pm_error("Can't open the color names dictionary file '%s'.  "
                      "errno = %d (%s)", fileName, errno, strerror(errno));
-        
+
     }
     lineNo = 0;
     return(f);
@@ -151,7 +154,7 @@ pm_colorget(FILE * const f) {
     bool eof;
     struct colorfile_entry retval;
     char * rc;
-    
+
     gotOne = FALSE;  /* initial value */
     eof = FALSE;
     while (!gotOne && !eof) {
@@ -162,15 +165,15 @@ pm_colorget(FILE * const f) {
         else {
             if (buf[0] != '#' && buf[0] != '\n' && buf[0] != '!' &&
                 buf[0] != '\0') {
-                if (sscanf(buf, "%ld %ld %ld %[^\n]", 
-                           &retval.r, &retval.g, &retval.b, colorname) 
+                if (sscanf(buf, "%ld %ld %ld %[^\n]",
+                           &retval.r, &retval.g, &retval.b, colorname)
                     == 4 )
                     gotOne = TRUE;
                 else {
                     if (buf[strlen(buf)-1] == '\n')
                         buf[strlen(buf)-1] = '\0';
                     pm_message("can't parse color names dictionary Line %d:  "
-                               "'%s'", 
+                               "'%s'",
                                lineNo, buf);
                 }
             }
@@ -186,65 +189,83 @@ pm_colorget(FILE * const f) {
 
 
 void
-pm_parse_dictionary_name(char    const colorname[], 
-                         pixval  const maxval,
-                         int     const closeOk,
-                         pixel * const colorP) {
+pm_parse_dictionary_namen(char   const colorname[],
+                          tuplen const color) {
 
-    FILE* f;
+    FILE * fP;
     bool gotit;
     bool colorfileExhausted;
-    struct colorfile_entry colorfile_entry;
+    struct colorfile_entry colorfileEntry;
     char * canoncolor;
-    pixval r,g,b;
 
-    f = pm_openColornameFile(NULL, TRUE);  /* exits if error */
-    canoncolor = pm_strdup(colorname);
+    fP = pm_openColornameFile(NULL, TRUE);  /* exits if error */
+    canoncolor = strdup(colorname);
 
     if (!canoncolor)
         pm_error("Failed to allocate memory for %u-byte color name",
                  (unsigned)strlen(colorname));
 
     pm_canonstr(canoncolor);
-    gotit = FALSE;
-    colorfileExhausted = FALSE;
-    while (!gotit && !colorfileExhausted) {
-        colorfile_entry = pm_colorget(f);
-        if (colorfile_entry.colorname) {
-            pm_canonstr(colorfile_entry.colorname);
-            if (strcmp( canoncolor, colorfile_entry.colorname) == 0)
+
+    for(gotit = FALSE, colorfileExhausted = FALSE;
+        !gotit && !colorfileExhausted; ) {
+
+        colorfileEntry = pm_colorget(fP);
+        if (colorfileEntry.colorname) {
+            pm_canonstr(colorfileEntry.colorname);
+            if (streq(canoncolor, colorfileEntry.colorname))
                 gotit = TRUE;
         } else
             colorfileExhausted = TRUE;
     }
-    fclose(f);
-    
+    fclose(fP);
+
     if (!gotit)
         pm_error("unknown color '%s'", colorname);
-    
-    /* Rescale from [0..255] if necessary. */
-    if (maxval != 255) {
-        r = colorfile_entry.r * maxval / 255;
-        g = colorfile_entry.g * maxval / 255;
-        b = colorfile_entry.b * maxval / 255;
-
-        if (!closeOk) {
-            if (r * 255 / maxval != colorfile_entry.r ||
-                g * 255 / maxval != colorfile_entry.g ||
-                b * 255 / maxval != colorfile_entry.b)
+
+    color[PAM_RED_PLANE] = (samplen)colorfileEntry.r / PAM_COLORFILE_MAXVAL;
+    color[PAM_GRN_PLANE] = (samplen)colorfileEntry.g / PAM_COLORFILE_MAXVAL;
+    color[PAM_BLU_PLANE] = (samplen)colorfileEntry.b / PAM_COLORFILE_MAXVAL;
+
+    free(canoncolor);
+}
+
+
+
+void
+pm_parse_dictionary_name(char    const colorname[],
+                         pixval  const maxval,
+                         int     const closeOk,
+                         pixel * const colorP) {
+
+    double const epsilon = 1.0/65536.0;
+
+    tuplen color;
+    pixval r, g, b;
+
+    MALLOCARRAY_NOFAIL(color, 3);
+
+    pm_parse_dictionary_namen(colorname, color);
+
+    r = ppm_unnormalize(color[PAM_RED_PLANE], maxval);
+    g = ppm_unnormalize(color[PAM_GRN_PLANE], maxval);
+    b = ppm_unnormalize(color[PAM_BLU_PLANE], maxval);
+
+    if (!closeOk) {
+        if (maxval != PAM_COLORFILE_MAXVAL) {
+            if (fabs((double)r / maxval - color[PAM_RED_PLANE]) > epsilon ||
+                fabs((double)g / maxval - color[PAM_GRN_PLANE]) > epsilon ||
+                fabs((double)b / maxval - color[PAM_BLU_PLANE]) > epsilon) {
                 pm_message("WARNING: color '%s' cannot be represented "
                            "exactly with a maxval of %u.  "
                            "Approximating as (%u,%u,%u).  "
-                           "The color dictionary uses maxval 255, so that "
-                           "maxval will always work.",
-                           colorname, maxval, r, g, b);
+                           "(The color dictionary uses maxval %u, so that "
+                           "maxval will always work).",
+                           colorname, maxval, r, g, b,
+                           PAM_COLORFILE_MAXVAL);
+            }
         }
-    } else {
-        r = colorfile_entry.r;
-        g = colorfile_entry.g;
-        b = colorfile_entry.b;
     }
-    free(canoncolor);
 
     PPM_ASSIGN(*colorP, r, g, b);
 }
diff --git a/lib/colorname.h b/lib/colorname.h
index 74583144..492df951 100644
--- a/lib/colorname.h
+++ b/lib/colorname.h
@@ -4,6 +4,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <netpbm/ppm.h>
+#include <netpbm/pam.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -15,14 +16,19 @@ extern "C" {
 enum colornameFormat {PAM_COLORNAME_ENGLISH = 0,
                       PAM_COLORNAME_HEXOK   = 1};
 
+#define PAM_COLORFILE_MAXVAL 255
+
 struct colorfile_entry {
     long r, g, b;
+        /* Red, green, and blue components of color based on maxval
+           PAM_COLORFILE_MAXVAL
+        */
     char * colorname;
 };
 
 
 
-void 
+void
 pm_canonstr(char * const str);
 
 FILE *
@@ -32,7 +38,11 @@ struct colorfile_entry
 pm_colorget(FILE * const f);
 
 void
-pm_parse_dictionary_name(const char       colorname[], 
+pm_parse_dictionary_namen(char   const colorname[],
+                          tuplen const color);
+
+void
+pm_parse_dictionary_name(const char       colorname[],
                          pixval     const maxval,
                          int        const closeOk,
                          pixel *    const colorP);
diff --git a/lib/libpam.c b/lib/libpam.c
index cc6368e1..a8f140b3 100644
--- a/lib/libpam.c
+++ b/lib/libpam.c
@@ -12,7 +12,8 @@
    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 */
 
@@ -107,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)
@@ -129,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;
@@ -138,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;
 
@@ -172,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);
 }
 
@@ -185,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);
@@ -194,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
@@ -204,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;
 }
 
@@ -225,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;
@@ -260,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
@@ -270,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);
 }
 
@@ -306,7 +307,7 @@ pnm_freerowimage(unsigned char * const rowimage) {
 
 
 
-void 
+void
 pnm_scaletuplerow(const struct pam * const pamP,
                   tuple *            const destRow,
                   tuple *            const sourceRow,
@@ -320,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 {
@@ -352,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));
@@ -382,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) {
 
@@ -393,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);
 }
 
@@ -401,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;
     }
 }
@@ -420,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;
@@ -434,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++;
@@ -451,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';
     }
 }
@@ -493,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)
@@ -582,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);
 }
@@ -643,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];
@@ -664,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);
         }
     }
@@ -680,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",
@@ -696,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) {
 /*----------------------------------------------------------------------------
@@ -742,7 +743,7 @@ pnm_readpaminitrestaspnm(FILE * const fileP,
 }
 
 
-                
+
 unsigned int
 pnm_bytespersample(sample const maxval) {
 /*----------------------------------------------------------------------------
@@ -870,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;
@@ -894,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: {
@@ -925,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. */
@@ -958,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')
@@ -985,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
@@ -996,7 +997,7 @@ writeComments(const struct pam * const pamP) {
    facilities to pam.
 */
 
-void 
+void
 pnm_writepaminit(struct pam * const pamP) {
 
     const char * tupleType;
@@ -1013,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', "
@@ -1044,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. */
@@ -1064,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;
 
@@ -1081,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;
 
@@ -1092,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;
 
@@ -1125,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:
@@ -1157,7 +1158,7 @@ pnm_checkpam(const struct pam *   const pamP,
 
 
 
-void 
+void
 pnm_maketuplergb(const struct pam * const pamP,
                  tuple              const tuple) {
 
@@ -1171,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) {
 
@@ -1202,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;
@@ -1216,7 +1215,7 @@ pnm_makearrayrgb(const struct pam * const pamP,
 
 
 
-void 
+void
 pnm_makerowrgba(const struct pam * const pamP,
                 tuple *            const tuplerow) {
 /*----------------------------------------------------------------------------
@@ -1237,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 {
@@ -1246,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;
 
@@ -1263,7 +1262,7 @@ pnm_makerowrgba(const struct pam * const pamP,
 
 
 
-void 
+void
 pnm_addopacityrow(const struct pam * const pamP,
                   tuple *            const tuplerow) {
 /*----------------------------------------------------------------------------
@@ -1284,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 {
@@ -1296,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;
         }
@@ -1387,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/libpam.h b/lib/libpam.h
index 9f8a34d0..2d4f29d7 100644
--- a/lib/libpam.h
+++ b/lib/libpam.h
@@ -7,9 +7,9 @@
 #include "pgm.h"
 
 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);
 
diff --git a/lib/libpamcolor.c b/lib/libpamcolor.c
index f3ca9a86..e1a24c66 100644
--- a/lib/libpamcolor.c
+++ b/lib/libpamcolor.c
@@ -12,27 +12,349 @@
    offset stuff.
 */
 #define _FILE_OFFSET_BITS 64
-#define _LARGE_FILES  
-
-#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
-#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+#define _LARGE_FILES
 
 #include <string.h>
 #include <limits.h>
+#include <math.h>
 
 #include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+#include "netpbm/colorname.h"
+
+#include "netpbm/pam.h"
+#include "netpbm/ppm.h"
+
+
+
+static unsigned int
+hexDigitValue(char const digit) {
+
+    switch (digit) {
+    case '0': return 0;
+    case '1': return 1;
+    case '2': return 2;
+    case '3': return 3;
+    case '4': return 4;
+    case '5': return 5;
+    case '6': return 6;
+    case '7': return 7;
+    case '8': return 8;
+    case '9': return 9;
+    case 'a': case 'A': return 10;
+    case 'b': case 'B': return 11;
+    case 'c': case 'C': return 12;
+    case 'd': case 'D': return 13;
+    case 'e': case 'E': return 14;
+    case 'f': case 'F': return 15;
+    default:
+        pm_error("Invalid hex digit '%c'", digit);
+        return 0;  /* Defeat compiler warning */
+    }
+}
+
+
+
+static void
+parseHexDigits(const char *   const string,
+               char           const delim,
+               samplen *      const nP,
+               unsigned int * const digitCtP) {
+
+    unsigned int digitCt;
+    unsigned long n;
+    unsigned long range;
+        /* 16 for one hex digit, 256 for two hex digits, etc. */
+
+    for (digitCt = 0, n = 0, range = 1; string[digitCt] != delim; ) {
+        char const digit = string[digitCt];
+        if (digit == '\0')
+            pm_error("rgb: color spec '%s' ends prematurely", string);
+        else {
+            n = n * 16 + hexDigitValue(digit);
+            range *= 16;
+            ++digitCt;
+        }
+    }
+    if (range <= 1)
+        pm_error("No digits where hexadecimal number expected in rgb: "
+                 "color spec '%s'", string);
+
+    *nP = (samplen) n / (range-1);
+    *digitCtP = digitCt;
+}
+
 
-#include "pam.h"
-#include "ppm.h"
+
+static void
+parseNewHexX11(char   const colorname[],
+               tuplen const color) {
+/*----------------------------------------------------------------------------
+   Determine what color colorname[] specifies in the new style hex
+   color specification format (e.g. rgb:55/40/55).
+
+   Return that color as *colorP.
+
+   Assume colorname[] starts with "rgb:", but otherwise it might be
+   gibberish.
+-----------------------------------------------------------------------------*/
+    const char * cp;
+    unsigned int digitCt;
+
+    cp = &colorname[4];
+
+    parseHexDigits(cp, '/', &color[PAM_RED_PLANE], &digitCt);
+
+    cp += digitCt;
+    ++cp;  /* Skip the slash */
+
+    parseHexDigits(cp, '/', &color[PAM_GRN_PLANE], &digitCt);
+
+    cp += digitCt;
+    ++cp;  /* Skip the slash */
+
+    parseHexDigits(cp, '\0', &color[PAM_BLU_PLANE], &digitCt);
+}
+
+
+
+static bool
+isNormal(samplen const arg) {
+
+    return arg >= 0.0 && arg <= 1.0;
+}
+
+
+
+static void
+parseNewDecX11(const char * const colorname,
+               tuplen       const color) {
+
+    int rc;
+
+    rc = sscanf(colorname, "rgbi:%f/%f/%f",
+                &color[PAM_RED_PLANE],
+                &color[PAM_GRN_PLANE],
+                &color[PAM_BLU_PLANE]);
+
+    if (rc != 3)
+        pm_error("invalid color specifier '%s'", colorname);
+
+    if (!(isNormal(color[PAM_RED_PLANE]) &&
+          isNormal(color[PAM_GRN_PLANE]) &&
+          isNormal(color[PAM_BLU_PLANE]))) {
+        pm_error("invalid color specifier '%s' - "
+                 "values must be between 0.0 and 1.0", colorname);
+    }
+}
+
+
+
+static void
+parseInteger(const char * const colorname,
+             tuplen       const color) {
+
+    unsigned int maxval;
+    unsigned int r, g, b;
+    int rc;
+
+    rc = sscanf(colorname, "rgb-%u:%u/%u/%u", &maxval, &r, &g, &b);
+
+    if (rc != 4)
+        pm_error("invalid color specifier '%s'.  "
+                 "If it starts with \"rgb-\", then it must have the format "
+                 "rgb-<MAXVAL>:<RED>:<GRN>:<BLU>, "
+                 "where <MAXVAL>, <RED>, <GRN>, and <BLU> are "
+                 "unsigned integers",
+                 colorname);
+
+    if (maxval < 1 || maxval > PNM_OVERALLMAXVAL)
+        pm_error("Maxval in color specification '%s' is %u, "
+                 "which is invalid because it is not between "
+                 "1 and %u, inclusive",
+                 colorname, maxval, PNM_OVERALLMAXVAL);
+
+    if (r > maxval)
+        pm_error("Red value in color specification '%s' is %u, "
+                 "whcih is invalid because the specified maxval is %u",
+                 colorname, r, maxval);
+    if (g > maxval)
+        pm_error("Green value in color specification '%s' is %u, "
+                 "whcih is invalid because the specified maxval is %u",
+                 colorname, g, maxval);
+    if (b > maxval)
+        pm_error("Blue value in color specification '%s' is %u, "
+                 "whcih is invalid because the specified maxval is %u",
+                 colorname, b, maxval);
+
+    color[PAM_RED_PLANE] = (float)r/maxval;
+    color[PAM_GRN_PLANE] = (float)g/maxval;
+    color[PAM_BLU_PLANE] = (float)b/maxval;
+}
+
+
+
+static void
+parseOldX11(const char * const colorname,
+            tuplen       const color) {
+/*----------------------------------------------------------------------------
+   Return as *colorP the color specified by the old X11 style color
+   specififier colorname[] (e.g. #554055).
+-----------------------------------------------------------------------------*/
+    if (!pm_strishex(&colorname[1]))
+        pm_error("Non-hexadecimal characters in #-type color specification");
+
+    switch (strlen(colorname) - 1 /* (Number of hex digits) */) {
+    case 3:
+        color[PAM_RED_PLANE] = (samplen)hexDigitValue(colorname[1])/15;
+        color[PAM_GRN_PLANE] = (samplen)hexDigitValue(colorname[2])/15;
+        color[PAM_BLU_PLANE] = (samplen)hexDigitValue(colorname[3])/15;
+        break;
+
+    case 6:
+        color[PAM_RED_PLANE] =
+            ((samplen)(hexDigitValue(colorname[1]) << 4) +
+             (samplen)(hexDigitValue(colorname[2]) << 0))
+             / 255;
+        color[PAM_GRN_PLANE] =
+            ((samplen)(hexDigitValue(colorname[3]) << 4) +
+             (samplen)(hexDigitValue(colorname[4]) << 0))
+             / 255;
+        color[PAM_BLU_PLANE] =
+            ((samplen)(hexDigitValue(colorname[5]) << 4) +
+             (samplen)(hexDigitValue(colorname[6]) << 0))
+             / 255;
+        break;
+
+    case 9:
+        color[PAM_RED_PLANE] =
+            ((samplen)(hexDigitValue(colorname[1]) << 8) +
+             (samplen)(hexDigitValue(colorname[2]) << 4) +
+             (samplen)(hexDigitValue(colorname[3]) << 0))
+            / 4095;
+        color[PAM_GRN_PLANE] =
+            ((samplen)(hexDigitValue(colorname[4]) << 8) +
+             (samplen)(hexDigitValue(colorname[5]) << 4) +
+             (samplen)(hexDigitValue(colorname[6]) << 0))
+            / 4095;
+        color[PAM_BLU_PLANE] =
+            ((samplen)(hexDigitValue(colorname[7]) << 8) +
+             (samplen)(hexDigitValue(colorname[8]) << 4) +
+             (samplen)(hexDigitValue(colorname[9]) << 0))
+            / 4095;
+        break;
+
+    case 12:
+        color[PAM_RED_PLANE] =
+            ((samplen)(hexDigitValue(colorname[1]) << 12) +
+             (samplen)(hexDigitValue(colorname[2]) <<  8) +
+             (samplen)(hexDigitValue(colorname[3]) <<  4) +
+             (samplen)(hexDigitValue(colorname[4]) <<  0))
+            / 65535;
+        color[PAM_GRN_PLANE] =
+            ((samplen)(hexDigitValue(colorname[5]) << 12) +
+             (samplen)(hexDigitValue(colorname[6]) <<  8) +
+             (samplen)(hexDigitValue(colorname[7]) <<  4) +
+             (samplen)(hexDigitValue(colorname[8]) <<  0))
+            / 65535;
+        color[PAM_BLU_PLANE] =
+            ((samplen)(hexDigitValue(colorname[ 9]) << 12) +
+             (samplen)(hexDigitValue(colorname[10]) << 8) +
+             (samplen)(hexDigitValue(colorname[11]) << 4) +
+             (samplen)(hexDigitValue(colorname[12]) << 0))
+            / 65535;
+        break;
+
+    default:
+        pm_error("invalid color specifier '%s'", colorname);
+    }
+}
+
+
+
+static void
+parseOldX11Dec(const char* const colorname,
+               tuplen      const color) {
+
+    int rc;
+
+    rc = sscanf(colorname, "%f,%f,%f",
+                &color[PAM_RED_PLANE],
+                &color[PAM_GRN_PLANE],
+                &color[PAM_BLU_PLANE]);
+
+    if (rc != 3)
+        pm_error("invalid color specifier '%s'", colorname);
+
+    if (!(isNormal(color[PAM_RED_PLANE]) &&
+          isNormal(color[PAM_GRN_PLANE]) &&
+          isNormal(color[PAM_BLU_PLANE]))) {
+        pm_error("invalid color specifier '%s' - "
+                 "values must be between 0.0 and 1.0", colorname);
+    }
+}
+
+
+
+tuplen
+pnm_parsecolorn(const char * const colorname) {
+
+    tuplen retval;
+
+    MALLOCARRAY_NOFAIL(retval, 3);
+
+    if (strneq(colorname, "rgb:", 4))
+        /* It's a new-X11-style hexadecimal rgb specifier. */
+        parseNewHexX11(colorname, retval);
+    else if (strneq(colorname, "rgbi:", 5))
+        /* It's a new-X11-style decimal/float rgb specifier. */
+        parseNewDecX11(colorname, retval);
+    else if (strneq(colorname, "rgb-", 4))
+        /* It's a Netpbm-native decimal integer rgb specifier */
+        parseInteger(colorname, retval);
+    else if (colorname[0] == '#')
+        /* It's an old-X11-style hexadecimal rgb specifier. */
+        parseOldX11(colorname, retval);
+    else if ((colorname[0] >= '0' && colorname[0] <= '9') ||
+             colorname[0] == '.')
+        /* It's an old-style decimal/float rgb specifier. */
+        parseOldX11Dec(colorname, retval);
+    else
+        /* Must be a name from the X-style rgb file. */
+        pm_parse_dictionary_namen(colorname, retval);
+
+    return retval;
+}
+
+
+
+static void
+warnIfNotExact(const char * const colorname,
+               tuple        const rounded,
+               tuplen       const exact,
+               sample       const maxval,
+               unsigned int const plane) {
+
+    float const epsilon = 1.0/65536.0;
+
+    if (fabs((float)(rounded[plane] / maxval) - exact[plane]) > epsilon) {
+        pm_message("WARNING: Component %u of color '%s' is %f, "
+                   "which cannot be represented precisely with maxval %lu.  "
+                   "Approximating as %lu.",
+                   plane, colorname, exact[plane], maxval, rounded[plane]);
+    }
+}
 
 
 
 tuple
-pnm_parsecolor(const char * const colorname,
-               sample       const maxval) {
+pnm_parsecolor2(const char * const colorname,
+                sample       const maxval,
+                int          const closeOk) {
 
     tuple retval;
-    pixel color;
+    tuplen color;
     struct pam pam;
 
     pam.len = PAM_STRUCT_SIZE(bytes_per_sample);
@@ -42,17 +364,32 @@ pnm_parsecolor(const char * const colorname,
 
     retval = pnm_allocpamtuple(&pam);
 
-    color = ppm_parsecolor(colorname, maxval);
+    color = pnm_parsecolorn(colorname);
 
-    retval[PAM_RED_PLANE] = PPM_GETR(color);
-    retval[PAM_GRN_PLANE] = PPM_GETG(color);
-    retval[PAM_BLU_PLANE] = PPM_GETB(color);
+    pnm_unnormalizetuple(&pam, color, retval);
+
+    if (!closeOk) {
+        warnIfNotExact(colorname, retval, color, maxval, PAM_RED_PLANE);
+        warnIfNotExact(colorname, retval, color, maxval, PAM_GRN_PLANE);
+        warnIfNotExact(colorname, retval, color, maxval, PAM_BLU_PLANE);
+    }
+
+    free(color);
 
     return retval;
 }
 
 
 
+tuple
+pnm_parsecolor(const char * const colorname,
+               sample       const maxval) {
+
+    return pnm_parsecolor2(colorname, maxval, true);
+}
+
+
+
 const char *
 pnm_colorname(struct pam * const pamP,
               tuple        const color,
@@ -64,7 +401,7 @@ pnm_colorname(struct pam * const pamP,
 
     if (pamP->depth < 3)
         PPM_ASSIGN(colorp, color[0], color[0], color[0]);
-    else 
+    else
         PPM_ASSIGN(colorp,
                    color[PAM_RED_PLANE],
                    color[PAM_GRN_PLANE],
@@ -72,8 +409,8 @@ pnm_colorname(struct pam * const pamP,
 
     colorname = ppm_colorname(&colorp, pamP->maxval, hexok);
 
-    retval = strdup(colorname);
-    if (retval == NULL)
+    retval = pm_strdup(colorname);
+    if (retval == pm_strsol)
         pm_error("Couldn't get memory for color name string");
 
     return retval;
@@ -81,21 +418,279 @@ pnm_colorname(struct pam * const pamP,
 
 
 
+static tuple
+scaledRgb(struct pam * const pamP,
+          tuple        const color,
+          sample       const maxval) {
+
+    tuple scaledColor;
+
+    struct pam pam;
+
+    pam.size             = sizeof(pam);
+    pam.len              = PAM_STRUCT_SIZE(allocation_depth);
+    pam.maxval           = pamP->maxval;
+    pam.depth            = pamP->depth;
+    pam.allocation_depth = 3;
+
+    scaledColor = pnm_allocpamtuple(&pam);
+
+    pnm_scaletuple(&pam, scaledColor, color, maxval);
+
+    pnm_maketuplergb(&pam, scaledColor);
+
+    return scaledColor;
+}
+
+
+
+const char *
+pnm_colorspec_rgb_integer(struct pam * const pamP,
+                          tuple        const color,
+                          sample       const maxval) {
+
+    const char * retval;
+
+    tuple scaledColor = scaledRgb(pamP, color, maxval);
+
+    pm_asprintf(&retval, "rgb-%lu:%lu/%lu/%lu",
+                maxval,
+                scaledColor[PAM_RED_PLANE],
+                scaledColor[PAM_GRN_PLANE],
+                scaledColor[PAM_BLU_PLANE]
+        );
+
+    pnm_freepamtuple(scaledColor);
+
+    return retval;
+}
+
+
+
+const char *
+pnm_colorspec_rgb_norm(struct pam * const pamP,
+                       tuple        const color,
+                       unsigned int const digitCt) {
+
+    const char * retval;
+
+    tuple rgbColor;
+
+    tuplen normColor;
+
+    struct pam rgbPam;
+
+    rgbPam.size             = sizeof(rgbPam);
+    rgbPam.len              = PAM_STRUCT_SIZE(allocation_depth);
+    rgbPam.maxval           = pamP->maxval;
+    rgbPam.depth            = pamP->depth;
+    rgbPam.allocation_depth = 3;
+
+    rgbColor = pnm_allocpamtuple(&rgbPam);
+
+    pnm_assigntuple(&rgbPam, rgbColor, color);  /* initial value */
+
+    pnm_maketuplergb(&rgbPam, rgbColor);
+
+    normColor = pnm_allocpamtuplen(&rgbPam);
+
+    rgbPam.depth = 3;
+
+    pnm_normalizetuple(&rgbPam, rgbColor, normColor);
+
+    {
+        const char * format;
+        pm_asprintf(&format, "rgbi:%%.%uf/%%.%uf/%%.%uf",
+                    digitCt, digitCt, digitCt);
+
+        pm_asprintf(&retval, format,
+                    normColor[PAM_RED_PLANE],
+                    normColor[PAM_GRN_PLANE],
+                    normColor[PAM_BLU_PLANE]
+            );
+        pm_strfree(format);
+    }
+
+    pnm_freepamtuplen(normColor);
+    pnm_freepamtuple(rgbColor);
+
+    return retval;
+}
+
+
+
+const char *
+pnm_colorspec_rgb_x11(struct pam * const pamP,
+                      tuple        const color,
+                      unsigned int const hexDigitCt) {
+
+    const char * retval;
+
+    sample maxval;
+    const char * format;
+
+    switch(hexDigitCt) {
+    case 1:
+        maxval =    15;
+        format = "rgb:%01x:%01x:%01x";
+        break;
+    case 2:
+        maxval =   255;
+        format = "rgb:%02x:%02x:%02x";
+        break;
+    case 3:
+        maxval =  4095;
+        format = "rgb:%03x:%03x:%03x";
+        break;
+    case 4:
+        maxval = 65535;
+        format = "rgb:%04x:%04x:%04x";
+        break;
+    default:
+        pm_error("Invalid number of hex digits "
+                 "for X11 color specification: %u.  "
+                 "Must be 1, 2, 3, or 4", hexDigitCt);
+    }
+
+    {
+        tuple const scaledColor = scaledRgb(pamP, color, maxval);
+
+        pm_asprintf(&retval, format,
+                    scaledColor[PAM_RED_PLANE],
+                    scaledColor[PAM_GRN_PLANE],
+                    scaledColor[PAM_BLU_PLANE]
+            );
+
+        pnm_freepamtuple(scaledColor);
+    }
+    return retval;
+}
+
+
+
+const char *
+pnm_colorspec_dict(struct pam * const pamP,
+                   tuple        const color) {
+/*----------------------------------------------------------------------------
+   Return the name from the color dictionary of color 'color'.
+
+   Return it in newly allocated pm_strdrup storage.
+
+   If the color is not in the dictionary, or the dictionary doesn't even
+   exist (file not found in any of the possible places), return NULL.
+
+   The color dictionary uses maxval 255, so we match to that precision.
+   E.g. if a component of 'color' is 1000 out of maxval 65535 (which would be
+   3.9 out of maxval 255), we consider it a match to a component value of 4
+   in the color dictionary.
+-----------------------------------------------------------------------------*/
+    tuple scaledColor = scaledRgb(pamP, color, PAM_COLORFILE_MAXVAL);
+
+    FILE * dictFileP;
+    const char * colorname;
+
+    dictFileP = pm_openColornameFile(NULL, false);
+
+    if (dictFileP) {
+        bool eof;
+        for (colorname = NULL, eof = false; !colorname && !eof; ) {
+            struct colorfile_entry const ce = pm_colorget(dictFileP);
+
+            if (ce.colorname)  {
+                if (scaledColor[PAM_RED_PLANE] == (sample)ce.r &&
+                    scaledColor[PAM_GRN_PLANE] == (sample)ce.g &&
+                    scaledColor[PAM_BLU_PLANE] == (sample)ce.b) {
+                    colorname = pm_strdup(ce.colorname);
+                }
+            } else
+                eof = TRUE;
+        }
+
+        fclose(dictFileP);
+    } else
+        colorname = NULL;
+
+    pnm_freepamtuple(scaledColor);
+
+    return colorname;
+}
+
+
+
+const char *
+pnm_colorspec_dict_close(struct pam * const pamP,
+                         tuple        const color) {
+/*----------------------------------------------------------------------------
+   Return the name from the color dictionary of the color closst to 'color'.
+
+   Return it in newly allocated pm_strdrup storage.
+
+   If the color dictionary is empty, or the dictionary doesn't even exist
+   (file not found in any of the possible places), return a null string.
+   This is the only case in which we would return a null string, as the
+   color dictionary cannot define a null string color name.
+-----------------------------------------------------------------------------*/
+    tuple scaledColor = scaledRgb(pamP, color, PAM_COLORFILE_MAXVAL);
+
+    FILE * dictFileP;
+    static char colorname[200];
+
+    dictFileP = pm_openColornameFile(NULL, false);
+
+    if (dictFileP) {
+        unsigned int bestDiff;
+        bool eof;
+
+        for (bestDiff = 32767, eof = FALSE; !eof && bestDiff > 0; ) {
+            struct colorfile_entry const ce = pm_colorget(dictFileP);
+
+            if (ce.colorname)  {
+                unsigned int const thisDiff =
+                    abs((int)scaledColor[PAM_RED_PLANE] - (int)ce.r) +
+                    abs((int)scaledColor[PAM_GRN_PLANE] - (int)ce.g) +
+                    abs((int)scaledColor[PAM_BLU_PLANE] - (int)ce.b);
+
+                if (thisDiff < bestDiff) {
+                    bestDiff = thisDiff;
+                    STRSCPY(colorname, ce.colorname);
+                }
+            } else
+                eof = TRUE;
+        }
+
+        fclose(dictFileP);
+
+        if (bestDiff == 32767) {
+            /* Color file contain no entries, so we can't even pick a close
+               one
+            */
+            STRSCPY(colorname, "");
+        }
+    } else
+        STRSCPY(colorname, "");
+
+    pnm_freepamtuple(scaledColor);
+
+    return pm_strdup(colorname);
+}
+
+
+
 double pnm_lumin_factor[3] = {PPM_LUMINR, PPM_LUMING, PPM_LUMINB};
 
 void
-pnm_YCbCrtuple(tuple    const tuple, 
-               double * const YP, 
-               double * const CbP, 
+pnm_YCbCrtuple(tuple    const tuple,
+               double * const YP,
+               double * const CbP,
                double * const CrP) {
 /*----------------------------------------------------------------------------
-   Assuming that the tuple 'tuple' is of tupletype RGB, return the 
+   Assuming that the tuple 'tuple' is of tupletype RGB, return the
    Y/Cb/Cr representation of the color represented by the tuple.
 -----------------------------------------------------------------------------*/
     int const red = (int)tuple[PAM_RED_PLANE];
     int const grn = (int)tuple[PAM_GRN_PLANE];
     int const blu = (int)tuple[PAM_BLU_PLANE];
-    
+
     *YP  = (+ PPM_LUMINR * red + PPM_LUMING * grn + PPM_LUMINB * blu);
     *CbP = (- 0.16874 * red - 0.33126 * grn + 0.50000 * blu);
     *CrP = (+ 0.50000 * red - 0.41869 * grn - 0.08131 * blu);
@@ -103,11 +698,11 @@ pnm_YCbCrtuple(tuple    const tuple,
 
 
 
-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) {
 
@@ -128,7 +723,7 @@ pnm_YCbCr_to_rgbtuple(const struct pam * const pamP,
         } else if (rgb[plane] < 0.0) {
             overflow = TRUE;
             tuple[plane] = 0;
-        } else 
+        } else
             tuple[plane] = (sample)rgb[plane];
     }
     if (overflowP)
diff --git a/lib/libpamn.c b/lib/libpamn.c
index 26dbbfe3..ae28283a 100644
--- a/lib/libpamn.c
+++ b/lib/libpamn.c
@@ -23,6 +23,43 @@
 
 
 
+static unsigned int
+allocationDepth(const struct pam * const pamP) {
+
+    unsigned int retval;
+
+    if (pamP->len >= PAM_STRUCT_SIZE(allocation_depth)) {
+        if (pamP->allocation_depth == 0)
+            retval = pamP->depth;
+        else {
+            if (pamP->depth > pamP->allocation_depth)
+                pm_error("'allocationDepth' (%u) is smaller than 'depth' (%u)",
+                         pamP->allocation_depth, pamP->depth);
+            retval = pamP->allocation_depth;
+        }
+    } else
+        retval = pamP->depth;
+    return retval;
+}
+
+
+
+tuplen
+pnm_allocpamtuplen(const struct pam * const pamP) {
+
+    tuplen retval;
+
+    retval = malloc(allocationDepth(pamP) * sizeof(retval[0]));
+
+    if (retval == NULL)
+        pm_error("Out of memory allocating %u-plane normalized tuple",
+                 allocationDepth(pamP));
+
+    return retval;
+}
+
+
+
 static void
 allocpamrown(const struct pam * const pamP,
              tuplen **          const tuplerownP,
@@ -32,7 +69,8 @@ allocpamrown(const struct pam * const pamP,
    overflow will not occur in our calculations.  NOTE: pnm_readpaminit()
    ensures this assumption is valid.
 -----------------------------------------------------------------------------*/
-    int const bytes_per_tuple = pamP->depth * sizeof(samplen);
+    unsigned int const bytes_per_tuple =
+        allocationDepth(pamP) * sizeof(samplen);
 
     tuplen * tuplerown;
     const char * error;
@@ -47,15 +85,16 @@ allocpamrown(const struct pam * const pamP,
         pm_asprintf(&error, "Out of memory allocating space for a tuple row of"
                     "%u tuples by %u samples per tuple "
                     "by %u bytes per sample.",
-                    pamP->width, pamP->depth, (unsigned)sizeof(samplen));
+                    pamP->width, allocationDepth(pamP),
+                    (unsigned)sizeof(samplen));
     else {
         /* Now we initialize the pointers to the individual tuples to make this
            a regulation C two dimensional array.
         */
-        
+
         unsigned char * p;
         unsigned int i;
-        
+
         p = (unsigned char*) (tuplerown + pamP->width);
             /* location of Tuple 0 */
         for (i = 0; i < pamP->width; ++i) {
@@ -93,7 +132,7 @@ pnm_allocpamrown(const struct pam * const pamP) {
 
 
 static void
-readpbmrow(const struct pam * const pamP, 
+readpbmrow(const struct pam * const pamP,
            tuplen *           const tuplenrow) {
 
     bit * bitrow;
@@ -101,7 +140,7 @@ readpbmrow(const struct pam * const pamP,
     jmp_buf * origJmpbufP;
 
     bitrow = pbm_allocrow(pamP->width);
-    
+
     if (setjmp(jmpbuf) != 0) {
         pbm_freerow(bitrow);
         pm_setjmpbuf(origJmpbufP);
@@ -123,15 +162,15 @@ readpbmrow(const struct pam * const pamP,
 
 
 static void
-readpamrow(const struct pam * const pamP, 
+readpamrow(const struct pam * const pamP,
            tuplen *           const tuplenrow) {
 
     jmp_buf jmpbuf;
     jmp_buf * origJmpbufP;
     tuple * tuplerow;
-    
+
     tuplerow = pnm_allocpamrow(pamP);
-    
+
     if (setjmp(jmpbuf) != 0) {
         pnm_freepamrow(tuplerow);
         pm_setjmpbuf(origJmpbufP);
@@ -159,13 +198,13 @@ readpamrow(const struct pam * const pamP,
 
 
 
-void 
-pnm_readpamrown(const struct pam * const pamP, 
+void
+pnm_readpamrown(const struct pam * const pamP,
                 tuplen *           const tuplenrow) {
 
-    /* For speed, we don't check any of the inputs for consistency 
+    /* For speed, we don't check any of the inputs for consistency
        here (unless it's necessary to avoid crashing).  Any consistency
-       checking should have been done by a prior call to 
+       checking should have been done by a prior call to
        pnm_writepaminit().
     */
     assert(pamP->maxval != 0);
@@ -186,7 +225,7 @@ pnm_readpamrown(const struct pam * const pamP,
 
 
 static void
-writepbmrow(const struct pam * const pamP, 
+writepbmrow(const struct pam * const pamP,
             const tuplen *     const tuplenrow) {
 
     jmp_buf jmpbuf;
@@ -206,26 +245,26 @@ writepbmrow(const struct pam * const pamP,
 
         for (col = 0; col < pamP->width; ++col)
             bitrow[col] = tuplenrow[col][0] < 0.5 ? PBM_BLACK : PBM_WHITE;
-        pbm_writepbmrow(pamP->file, bitrow, pamP->width, 
+        pbm_writepbmrow(pamP->file, bitrow, pamP->width,
                         pamP->format == PBM_FORMAT);
 
         pm_setjmpbuf(origJmpbufP);
     }
     pbm_freerow(bitrow);
-} 
+}
 
 
 
 static void
-writepamrow(const struct pam * const pamP, 
+writepamrow(const struct pam * const pamP,
             const tuplen *     const tuplenrow) {
 
     jmp_buf jmpbuf;
     jmp_buf * origJmpbufP;
     tuple * tuplerow;
-    
+
     tuplerow = pnm_allocpamrow(pamP);
-    
+
     if (setjmp(jmpbuf) != 0) {
         pnm_freepamrow(tuplerow);
         pm_setjmpbuf(origJmpbufP);
@@ -240,7 +279,7 @@ writepamrow(const struct pam * const pamP,
             for (plane = 0; plane < pamP->depth; ++plane)
                 tuplerow[col][plane] = (sample)
                     (tuplenrow[col][plane] * pamP->maxval + 0.5);
-        }    
+        }
         pnm_writepamrow(pamP, tuplerow);
 
         pm_setjmpbuf(origJmpbufP);
@@ -250,13 +289,13 @@ writepamrow(const struct pam * const pamP,
 
 
 
-void 
-pnm_writepamrown(const struct pam * const pamP, 
+void
+pnm_writepamrown(const struct pam * const pamP,
                  const tuplen *     const tuplenrow) {
 
-    /* For speed, we don't check any of the inputs for consistency 
+    /* For speed, we don't check any of the inputs for consistency
        here (unless it's necessary to avoid crashing).  Any consistency
-       checking should have been done by a prior call to 
+       checking should have been done by a prior call to
        pnm_writepaminit().
     */
     assert(pamP->maxval != 0);
@@ -274,16 +313,16 @@ pnm_writepamrown(const struct pam * const pamP,
 
 tuplen **
 pnm_allocpamarrayn(const struct pam * const pamP) {
-    
+
     tuplen ** tuplenarray;
     const char * error;
 
     /* If the speed of this is ever an issue, it might be sped up a little
        by allocating one large chunk.
     */
-    
+
     MALLOCARRAY(tuplenarray, pamP->height);
-    if (tuplenarray == NULL) 
+    if (tuplenarray == NULL)
         pm_asprintf(&error,
                     "Out of memory allocating the row pointer section of "
                     "a %u row array", pamP->height);
@@ -317,7 +356,7 @@ pnm_allocpamarrayn(const struct pam * const pamP) {
 
 
 void
-pnm_freepamarrayn(tuplen **          const tuplenarray, 
+pnm_freepamarrayn(tuplen **          const tuplenarray,
                   const struct pam * const pamP) {
 
     int row;
@@ -329,9 +368,9 @@ pnm_freepamarrayn(tuplen **          const tuplenarray,
 
 
 
-tuplen** 
-pnm_readpamn(FILE *       const file, 
-             struct pam * const pamP, 
+tuplen**
+pnm_readpamn(FILE *       const file,
+             struct pam * const pamP,
              int          const size) {
 
     tuplen **tuplenarray;
@@ -339,9 +378,9 @@ pnm_readpamn(FILE *       const file,
     jmp_buf * origJmpbufP;
 
     pnm_readpaminit(file, pamP, size);
-    
+
     tuplenarray = pnm_allocpamarrayn(pamP);
-    
+
     if (setjmp(jmpbuf) != 0) {
         pnm_freepamarrayn(tuplenarray, pamP);
         pm_setjmpbuf(origJmpbufP);
@@ -351,7 +390,7 @@ pnm_readpamn(FILE *       const file,
 
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
-        for (row = 0; row < pamP->height; ++row) 
+        for (row = 0; row < pamP->height; ++row)
             pnm_readpamrown(pamP, tuplenarray[row]);
 
         pm_setjmpbuf(origJmpbufP);
@@ -361,20 +400,33 @@ pnm_readpamn(FILE *       const file,
 
 
 
-void 
-pnm_writepamn(struct pam * const pamP, 
+void
+pnm_writepamn(struct pam * const pamP,
               tuplen **    const tuplenarray) {
 
     unsigned int row;
 
     pnm_writepaminit(pamP);
-    
-    for (row = 0; row < pamP->height; ++row) 
+
+    for (row = 0; row < pamP->height; ++row)
         pnm_writepamrown(pamP, tuplenarray[row]);
 }
 
 
 
+samplen
+pnm_normalized_sample(struct pam * const pamP,
+                      sample       const sample) {
+    return (samplen)sample/pamP->maxval;
+}
+
+sample
+pnm_unnormalized_sample(struct pam * const pamP,
+                        samplen      const sampleVal) {
+    double const epsilon = 1e-6;
+    return (sample)((sampleVal + epsilon) * pamP->maxval + 0.5);
+}
+
 void
 pnm_normalizetuple(struct pam * const pamP,
                    tuple        const tuple,
@@ -382,8 +434,8 @@ pnm_normalizetuple(struct pam * const pamP,
 
     unsigned int plane;
 
-    for (plane = 0; plane < pamP->depth; ++plane) 
-        tuplen[plane] = (samplen)tuple[plane] / pamP->maxval;
+    for (plane = 0; plane < pamP->depth; ++plane)
+        tuplen[plane] = pnm_normalized_sample(pamP, tuple[plane]);
 }
 
 
@@ -395,8 +447,8 @@ pnm_unnormalizetuple(struct pam * const pamP,
 
     unsigned int plane;
 
-    for (plane = 0; plane < pamP->depth; ++plane) 
-        tuple[plane] = tuplen[plane] * pamP->maxval + 0.5;
+    for (plane = 0; plane < pamP->depth; ++plane)
+        tuple[plane] = pnm_unnormalized_sample(pamP, tuplen[plane]);
 }
 
 
@@ -412,7 +464,7 @@ pnm_normalizeRow(struct pam *             const pamP,
            once here so we can multiply many times later.
         */
     unsigned int plane;
-    
+
     for (plane = 0; plane < pamP->depth; ++plane) {
         if (transform && transform[plane]) {
             unsigned int col;
@@ -437,14 +489,14 @@ reversemap(samplen          const samplen,
 /*----------------------------------------------------------------------------
    Find the integer sample value that maps to the normalized samplen value
    'samplen' through the map 'transformMap'.  We interpret the map as
-   mapping the value N+1 to all the values transformMap[N] through 
+   mapping the value N+1 to all the values transformMap[N] through
    transformMap[N+1], and we expect transformMap[N+1] to be greater than
    transformMap[N] for all N.
 -----------------------------------------------------------------------------*/
     /* Do a binary search, since the values are in sorted (increasing)
        order
     */
-    
+
     sample low, high;
 
     low = 0; high = maxval;  /* Consider whole range to start */
@@ -471,18 +523,18 @@ pnm_unnormalizeRow(struct pam *             const pamP,
                    tuple *                  const tuplerow) {
 
     unsigned int plane;
-    
+
     for (plane = 0; plane < pamP->depth; ++plane) {
         if (transform && transform[plane]) {
             unsigned int col;
             for (col = 0; col < pamP->width; ++col)
-                tuplerow[col][plane] = 
-                    reversemap(tuplenrow[col][plane], 
+                tuplerow[col][plane] =
+                    reversemap(tuplenrow[col][plane],
                                transform[plane], pamP->maxval);
         } else {
             unsigned int col;
             for (col = 0; col < pamP->width; ++col)
-                tuplerow[col][plane] = 
+                tuplerow[col][plane] =
                     tuplenrow[col][plane] * pamP->maxval + 0.5;
         }
     }
@@ -500,13 +552,13 @@ gammaCommon(struct pam *  const pamP,
     unsigned int plane;
     unsigned int opacityPlane;
     int haveOpacity;
-    
+
     pnm_getopacity(pamP, &haveOpacity, &opacityPlane);
 
     for (plane = 0; plane < pamP->depth; ++plane) {
         if (haveOpacity && plane == opacityPlane) {
             /* It's an opacity (alpha) plane, which means there is
-               no gamma adjustment in it.  
+               no gamma adjustment in it.
             */
         } else {
             unsigned int col;
@@ -550,7 +602,7 @@ applyopacityCommon(enum applyUnapply const applyUnapply,
 -----------------------------------------------------------------------------*/
     unsigned int opacityPlane;
     int haveOpacity;
-    
+
     pnm_getopacity(pamP, &haveOpacity, &opacityPlane);
 
     if (haveOpacity) {
@@ -605,6 +657,63 @@ pnm_unapplyopacityrown(struct pam * const pamP,
 
 
 
+void
+pnm_maketuplergbn(const struct pam * const pamP,
+                  tuplen             const tuple) {
+
+    if (allocationDepth(pamP) < 3)
+        pm_error("allocation depth %u passed to pnm_maketuplergb().  "
+                 "Must be at least 3.", allocationDepth(pamP));
+
+    if (pamP->depth < 3)
+        tuple[2] = tuple[1] = tuple[0];
+}
+
+
+
+void
+pnm_makerowrgbn(const struct pam * const pamP,
+                tuplen *           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));
+
+        for (col = 0; col < pamP->width; ++col) {
+            tuplen const thisTuple = tuplerow[col];
+            thisTuple[2] = thisTuple[1] = thisTuple[0];
+        }
+    }
+}
+
+
+
+void
+pnm_makearrayrgbn(const struct pam * const pamP,
+                  tuplen **          const tuples) {
+
+    if (pamP->depth < 3) {
+        unsigned int row;
+        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) {
+            tuplen * const tuplerow = tuples[row];
+            unsigned int col;
+            for (col = 0; col < pamP->width; ++col) {
+                tuplen const thisTuple = tuplerow[col];
+                thisTuple[2] = thisTuple[1] = thisTuple[0];
+            }
+        }
+    }
+}
+
+
+
 static void
 fillInMap(pnm_transformMap const ungammaTransformMap,
           sample           const maxval,
@@ -657,7 +766,7 @@ createUngammaMapOffset(const struct pam * const pamP,
                     retval[plane] = NULL;
                 else
                     retval[plane] = ungammaTransformMap;
-            }            
+            }
             fillInMap(ungammaTransformMap, pamP->maxval, offset);
         } else {
             free(retval);
diff --git a/lib/libpbm2.c b/lib/libpbm2.c
index f199c51a..a35004f9 100644
--- a/lib/libpbm2.c
+++ b/lib/libpbm2.c
@@ -18,7 +18,7 @@
 #include "fileio.h"
 #include "pam.h"
 
-static bit 
+static bit
 getbit (FILE * const file) {
     char ch;
 
@@ -28,7 +28,7 @@ getbit (FILE * const file) {
 
     if ( ch != '0' && ch != '1' )
         pm_error( "junk in file where bits should be" );
-    
+
     return ( ch == '1' ) ? 1 : 0;
 }
 
@@ -157,9 +157,9 @@ pbm_readpbmrow( FILE * const file,
 
 
 void
-pbm_readpbmrow_packed(FILE *          const fileP, 
+pbm_readpbmrow_packed(FILE *          const fileP,
                       unsigned char * const packedBits,
-                      int             const cols, 
+                      int             const cols,
                       int             const format) {
 
     switch(format) {
@@ -182,20 +182,20 @@ pbm_readpbmrow_packed(FILE *          const fileP,
     case RPBM_FORMAT: {
         unsigned int bytesReadCt;
         bytesReadCt = fread(packedBits, 1, pbm_packed_bytes(cols), fileP);
-             
+
         if (bytesReadCt < pbm_packed_bytes(cols)) {
-            if (feof(fileP)) 
-                if (bytesReadCt == 0) 
+            if (feof(fileP))
+                if (bytesReadCt == 0)
                     pm_error("Attempt to read a raw PBM image row, but "
                              "no more rows left in file.");
                 else
                     pm_error("EOF in the middle of a raw PBM row.");
-            else 
+            else
                 pm_error("I/O error reading raw PBM row");
         }
     }
     break;
-    
+
     default:
         pm_error("Internal error in pbm_readpbmrow_packed.");
     }
@@ -205,7 +205,7 @@ pbm_readpbmrow_packed(FILE *          const fileP,
 
 void
 pbm_readpbmrow_bitoffset(FILE *          const ifP,
-                         unsigned char * const packedBits, 
+                         unsigned char * const packedBits,
                          int             const cols,
                          int             const format,
                          unsigned int    const offset) {
@@ -237,7 +237,7 @@ pbm_readpbmrow_bitoffset(FILE *          const ifP,
         /* Target slot doesn't start on byte boundary; right-shift. */
         unsigned char carryover;
         unsigned int i;
-  
+
         carryover = (origHead >> lsh) << lsh;
 
         for (i = 0; i <= last; ++i) {
@@ -246,7 +246,7 @@ pbm_readpbmrow_bitoffset(FILE *          const ifP,
             carryover = t;
         }
     }
-  
+
     if ((cols + rsh) % 8 > 0) {
         /* Adjust rightmost char */
         unsigned int  const trs = (cols + rsh) % 8;
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
deleted file mode 100644
index d3551e78..00000000
--- a/lib/libpbmfont.c
+++ /dev/null
@@ -1,1603 +0,0 @@
-/* libpbm5.c - pbm utility library part 5
-**
-** Font routines.
-**
-** BDF font code Copyright 1993 by George Phillips.
-**
-** Copyright (C) 1991 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.
-*/
-
-#include <assert.h>
-#include <string.h>
-#include <ctype.h>
-
-#include "netpbm/pm_c_util.h"
-#include "netpbm/mallocvar.h"
-#include "netpbm/nstring.h"
-
-#include "pbmfont.h"
-#include "pbm.h"
-
-static unsigned int const firstCodePoint = 32;
-    /* This is the code point of the first character in a pbmfont font.
-       In ASCII, it is a space.
-    */
-
-static unsigned int const nCharsInFont = 96;
-    /* The number of characters in a pbmfont font.  A pbmfont font defines
-       characters at position 32 (ASCII space) through 127, so that's 96.
-    */
-
-/* The default font, packed in hex so this source file doesn't get huge.
-** You can replace this with your own font using pbm_dumpfont().
-*/
-#define DEFAULTFONT_ROWS 155
-#define DEFAULTFONT_COLS 112
-static unsigned long defaultfont_bits[DEFAULTFONT_ROWS][(DEFAULTFONT_COLS+31)/32] = {
-    {0x00000000L,0x20000c00L,0x10000000L,0x00000000L},
-    {0xc600a000L,0x42000810L,0x00000002L,0x00000063L},
-    {0x6c00a000L,0x45000810L,0x00000002L,0x00000036L},
-    {0x6c00a000L,0x88800808L,0xf2e1dee2L,0x00000036L},
-    {0x54000000L,0x80000800L,0x11122442L,0x0000002aL},
-    {0x54000001L,0x00000800L,0x11122442L,0x0000002aL},
-    {0x54000001L,0x00000800L,0x11122282L,0x0000002aL},
-    {0x44000102L,0x00000800L,0x11122382L,0x00000022L},
-    {0xee000102L,0x00000800L,0x11e1e102L,0x00000077L},
-    {0x00000204L,0x00000800L,0x11002102L,0x00000000L},
-    {0x00000000L,0x00000c00L,0x11002102L,0x00000000L},
-    {0x00000000L,0x003f8000L,0xe3807600L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x02000080L,0x00040000L,0x00120000L,0x00000001L},
-    {0x04000082L,0x828e1838L,0x20210100L,0x00000002L},
-    {0x04000082L,0x82912448L,0x20210100L,0x00000002L},
-    {0x08000082L,0x8fd01940L,0x404087c2L,0x00000004L},
-    {0x08000080L,0x050c0622L,0x00408102L,0x00000004L},
-    {0x10000080L,0x05061874L,0x0040828fL,0x00008008L},
-    {0x10000080L,0x1f912688L,0x00408002L,0x00000008L},
-    {0x20000000L,0x0a11098cL,0x00408002L,0x00000010L},
-    {0x20000080L,0x0a0e0672L,0x00210000L,0x00000010L},
-    {0x40000000L,0x00040000L,0x00210000L,0x00000020L},
-    {0x00000000L,0x00000000L,0x00120000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x004e0838L,0x7023e1cfL,0x00008000L},
-    {0x00000000L,0x00913844L,0x88620208L,0x00008000L},
-    {0x08000000L,0x00910844L,0x08a20401L,0x00000004L},
-    {0x10000000L,0x01110844L,0x08a20401L,0x00000008L},
-    {0x20000000L,0x01110808L,0x3123c781L,0x00000010L},
-    {0x400003e0L,0x02110810L,0x0a202441L,0x00000020L},
-    {0x20000000L,0x02110820L,0x0bf02442L,0x00000010L},
-    {0x10008000L,0x04110844L,0x88242442L,0x00000008L},
-    {0x08008002L,0x040e3e7cL,0x7073c382L,0x00000004L},
-    {0x00010000L,0x08000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x0000e1c0L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00011220L,0x00000000L,0x70e38f87L,0x00000000L},
-    {0x20011220L,0x00020020L,0x89108448L,0x00008010L},
-    {0x10011220L,0x00040010L,0x09314448L,0x00008008L},
-    {0x0800e221L,0x02083e08L,0x11514788L,0x00000004L},
-    {0x040111e0L,0x00100004L,0x2153e448L,0x00000002L},
-    {0x08011020L,0x00083e08L,0x213a2448L,0x00008004L},
-    {0x10011040L,0x02040010L,0x01022448L,0x00008008L},
-    {0x2000e381L,0x02020020L,0x20e77f87L,0x00000010L},
-    {0x00000000L,0x04000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x3803e7efL,0xc73bbe3dL,0xdb863ce7L,0x0000001cL},
-    {0x44011224L,0x48910808L,0x91036648L,0x00008022L},
-    {0x4c011285L,0x48910808L,0xa1036648L,0x00008026L},
-    {0x54011387L,0x081f0808L,0xc102a548L,0x0000802aL},
-    {0x54011285L,0x09910808L,0xe102a548L,0x0000802aL},
-    {0x4e011204L,0x08910848L,0x9112a4c8L,0x00008027L},
-    {0x40011224L,0x08910848L,0x891224c8L,0x00008020L},
-    {0x3803e7efL,0x073bbe31L,0xcff77e47L,0x0000001cL},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000003L,0x00000000L},
-    {0x0003e1cfL,0x87bff7efL,0xdfbf77c2L,0x00000000L},
-    {0x00013224L,0x48a4a244L,0x89122442L,0x00000000L},
-    {0x00011224L,0x4824a244L,0xa8a14482L,0x00000000L},
-    {0x00013227L,0x8e04226cL,0xa8414102L,0x00000000L},
-    {0x0001e224L,0x83842228L,0xa8a08102L,0x00000000L},
-    {0x00010224L,0x40842228L,0xd8a08242L,0x00000000L},
-    {0x00010224L,0x48843638L,0x51108442L,0x00000000L},
-    {0x0003c1ceL,0x6f1f1c10L,0x53b9c7c2L,0x00000000L},
-    {0x00000060L,0x00000000L,0x00000002L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000003L,0x00000000L},
-    {0xfe000000L,0x00000000L,0x00000000L,0x0000007fL},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00010180L,0x000000c0L,0x003001c0L,0x00000000L},
-    {0x08008081L,0x00040040L,0x00100200L,0x00000004L},
-    {0x10008082L,0x80040040L,0x00100200L,0x00000008L},
-    {0x10004084L,0x40023c78L,0x70f1c7c7L,0x00004008L},
-    {0x10004080L,0x00000244L,0x89122208L,0x00008008L},
-    {0x20002080L,0x00001e44L,0x8113e208L,0x00008010L},
-    {0x10002080L,0x00002244L,0x81120208L,0x00008008L},
-    {0x10001080L,0x00002244L,0x89122208L,0x00008008L},
-    {0x10001080L,0x00001db8L,0x70e9c787L,0x00008008L},
-    {0x10000880L,0x00000000L,0x00000000L,0x00008008L},
-    {0x08000180L,0x00000000L,0x00000000L,0x00008004L},
-    {0x00000000L,0x1fc00000L,0x00000007L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00030080L,0x981c0000L,0x00000000L,0x00000000L},
-    {0x20010000L,0x08040000L,0x00000000L,0x00000010L},
-    {0x10010000L,0x08040000L,0x00000000L,0x00000008L},
-    {0x10016387L,0x898474b8L,0x72e1d5c7L,0x00000008L},
-    {0x10019080L,0x8a042a64L,0x89122208L,0x00008008L},
-    {0x08011080L,0x8c042a44L,0x89122207L,0x00000004L},
-    {0x10011080L,0x8a042a44L,0x89122200L,0x00008008L},
-    {0x10011080L,0x89042a44L,0x89122208L,0x00008008L},
-    {0x1003bbe0L,0x98dfebe6L,0x71e1e787L,0x00000008L},
-    {0x10000000L,0x80000000L,0x01002000L,0x00000008L},
-    {0x20000000L,0x80000000L,0x01002000L,0x00000010L},
-    {0x00000007L,0x00000000L,0x03807000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00008000L,0x00000000L,0x10410000L,0x00000000L},
-    {0x00008000L,0x00000000L,0x20408000L,0x00000000L},
-    {0x0001f66eL,0xfdfbf77cL,0x20408000L,0x00000000L},
-    {0x24008224L,0x488a2248L,0x20408240L,0x00000012L},
-    {0x54008224L,0x4a842210L,0x40404540L,0x0000002aL},
-    {0x48008222L,0x8a8a1420L,0x20408480L,0x00000024L},
-    {0x00008a23L,0x85111c44L,0x20408000L,0x00000000L},
-    {0x000071d1L,0x0531887cL,0x20408000L,0x00000000L},
-    {0x00000000L,0x00000800L,0x20408000L,0x00000000L},
-    {0x00000000L,0x00000800L,0x10410000L,0x00000000L},
-    {0x00000000L,0x00003000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x20000c00L,0x10000000L,0x00000000L},
-    {0xc600a000L,0x42000810L,0x00000002L,0x00000063L},
-    {0x6c00a000L,0x45000810L,0x00000002L,0x00000036L},
-    {0x6c00a000L,0x88800808L,0xf2e1dee2L,0x00000036L},
-    {0x54000000L,0x80000800L,0x11122442L,0x0000002aL},
-    {0x54000001L,0x00000800L,0x11122442L,0x0000002aL},
-    {0x54000001L,0x00000800L,0x11122282L,0x0000002aL},
-    {0x44000102L,0x00000800L,0x11122382L,0x00000022L},
-    {0xee000102L,0x00000800L,0x11e1e102L,0x00000077L},
-    {0x00000204L,0x00000800L,0x11002102L,0x00000000L},
-    {0x00000000L,0x00000c00L,0x11002102L,0x00000000L},
-    {0x00000000L,0x003f8000L,0xe3807600L,0x00000000L}
-    };
-
-/* A default BDF font */
-/* Not as nicely compacted as the PBM font, oh well. */
-
-static struct glyph _g[190] = {
- { 1, 1, 0, 0, 3, "\0" },
- { 1, 9, 1, 0, 3, "\1\1\1\1\1\1\1\0\1" },
- { 3, 3, 1, 6, 5, "\1\0\1\1\0\1\1\0\1" },
- { 5, 8, 0, 0, 6, "\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\1\0\1\0\0\1\0\1\0" },
- { 5, 11, 0, -1, 6, "\0\0\1\0\0\0\1\1\1\0\1\0\1\0\1\1\0\1\0\0\0\1\1\0\0\0\0\1\1\0\0\0\1\0\1\0\0\1\0\1\1\0\1\0\1\0\1\1\1\0\0\0\1\0\0" },
- { 8, 9, 0, 0, 9, "\0\1\1\0\0\0\1\1\1\0\0\1\1\1\1\0\1\0\0\1\0\1\0\0\0\1\1\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\1\0\1\1\0\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\0" },
- { 9, 9, 0, 0, 10, "\0\0\0\1\1\0\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\0\1\1\0\1\1\1\0\1\1\1\1\0\0\1\0\1\1\0\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\0\0\1\1\1\0\1\0\1\1\1\1\0\1\1\0" },
- { 2, 3, 1, 6, 4, "\1\1\0\1\1\0" },
- { 3, 12, 1, -3, 5, "\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1" },
- { 3, 12, 0, -3, 5, "\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0" },
- { 5, 5, 0, 4, 6, "\0\0\1\0\0\1\0\1\0\1\0\1\1\1\0\1\0\1\0\1\0\0\1\0\0" },
- { 5, 5, 1, 1, 7, "\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0" },
- { 2, 3, 0, -2, 3, "\0\1\0\1\1\0" },
- { 5, 1, 1, 3, 8, "\1\1\1\1\1" },
- { 1, 1, 1, 0, 3, "\1" },
- { 3, 9, 0, 0, 3, "\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0" },
- { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\1\0\1\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\1\0\1\1\0\1\1\1\0" },
- { 4, 9, 0, 0, 6, "\0\0\1\0\0\1\1\0\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\1\1" },
- { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\1\1\1\1\1" },
- { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\1\1\1\0\0\0\0\0\1\0\0\0\0\1\1\0\0\0\1\0\1\1\1\0" },
- { 5, 9, 0, 0, 6, "\0\0\0\1\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\1\1\1\1\1\0\0\0\1\0\0\0\0\1\0" },
- { 5, 9, 0, 0, 6, "\0\0\1\1\1\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\1\1\0\0\1\1\0\1\1\1\0" },
- { 5, 9, 0, 0, 6, "\0\0\0\1\1\0\1\1\0\0\0\1\0\0\0\1\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\0" },
- { 5, 9, 0, 0, 6, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\0\1\0\0\0" },
- { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\0\1\1\1\0" },
- { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\1\0\0\0\1\0\0\0\1\1\0\1\1\0\0\0" },
- { 1, 6, 1, 0, 3, "\1\0\0\0\0\1" },
- { 2, 8, 0, -2, 3, "\0\1\0\0\0\0\0\0\0\0\0\1\0\1\1\0" },
- { 6, 5, 0, 1, 8, "\0\0\0\0\1\1\0\0\1\1\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1" },
- { 5, 3, 1, 2, 7, "\1\1\1\1\1\0\0\0\0\0\1\1\1\1\1" },
- { 6, 5, 1, 1, 8, "\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\1\1\0\0\1\1\0\0\0\0" },
- { 4, 9, 0, 0, 5, "\0\1\1\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\1\0\0" },
- { 10, 11, 1, -2, 11, "\0\0\0\0\1\1\1\1\0\0\0\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\1\1\0\1\0\1\1\0\0\1\0\0\1\0\0\1\1\0\1\0\0\0\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\1\0\1\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0" },
- { 9, 9, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
- { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\1\1\1\1\1\1\0" },
- { 7, 9, 0, 0, 8, "\0\0\1\1\1\0\1\0\1\1\0\0\1\1\0\1\0\0\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\0\1\1\0\0\1\1\0\0\1\1\1\1\0" },
- { 8, 9, 0, 0, 9, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\1\1\1\1\1\1\0\0" },
- { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
- { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0" },
- { 8, 9, 0, 0, 9, "\0\0\1\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\0\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 9, 0, 0, 9, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\1\1\1\0\0\1\1\1" },
- { 3, 9, 0, 0, 4, "\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 4, 9, 0, 0, 4, "\0\1\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\1\0\1\0\1\1\0\0" },
- { 8, 9, 0, 0, 8, "\1\1\1\0\1\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\1\1\1\0\0\1\1\1" },
- { 6, 9, 0, 0, 7, "\1\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\1\1\1\1\1\1\1" },
- { 11, 9, 0, 0, 11, "\1\1\0\0\0\0\0\0\0\1\1\0\1\1\0\0\0\0\0\1\1\0\0\1\1\0\0\0\0\0\1\1\0\0\1\0\1\0\0\0\1\0\1\0\0\1\0\1\0\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\1\1\1\0\0\1\0\0\1\1\1" },
- { 9, 9, 0, 0, 9, "\1\1\0\0\0\0\1\1\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\0\0\0\1\0" },
- { 8, 9, 0, 0, 9, "\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 7, 9, 0, 0, 7, "\1\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0" },
- { 8, 11, 0, -2, 9, "\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\0\0\1\1" },
- { 8, 9, 0, 0, 8, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\0\1\1\1\1\1\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\1\1\1\0\0\0\1\1" },
- { 6, 9, 0, 0, 7, "\0\1\1\1\0\1\1\0\0\0\1\1\1\0\0\0\0\1\0\1\1\0\0\0\0\0\1\1\1\0\0\0\0\0\1\1\1\0\0\0\0\1\1\1\0\0\1\1\1\0\1\1\1\0" },
- { 7, 9, 0, 0, 7, "\1\1\1\1\1\1\1\1\0\0\1\0\0\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0" },
- { 8, 9, 0, 0, 8, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0" },
- { 12, 9, 0, 0, 12, "\1\1\1\0\1\1\1\0\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\1\0\0\0\0\0\1\0\1\0\1\0\1\0\0\0\0\0\1\1\0\0\1\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0" },
- { 8, 9, 0, 0, 8, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\1\0\1\1\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\0\1\0\1\1\1\0\0\1\1\1" },
- { 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\0\0\0" },
- { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\1\1\1\1" },
- { 3, 12, 1, -3, 5, "\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1" },
- { 3, 9, 0, 0, 3, "\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1" },
- { 3, 12, 0, -3, 5, "\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1" },
- { 5, 5, 0, 4, 6, "\0\0\1\0\0\0\1\0\1\0\0\1\0\1\0\1\0\0\0\1\1\0\0\0\1" },
- { 6, 1, 0, -3, 6, "\1\1\1\1\1\1" },
- { 2, 3, 1, 6, 4, "\0\1\1\0\1\1" },
- { 5, 6, 1, 0, 6, "\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 5, 9, 0, 0, 6, "\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0" },
- { 4, 6, 1, 0, 5, "\0\1\1\0\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\1\0\1\1\0" },
- { 5, 9, 1, 0, 6, "\0\0\1\1\0\0\0\0\1\0\0\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 5, 6, 1, 0, 6, "\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
- { 3, 9, 0, 0, 3, "\0\0\1\0\1\0\0\1\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0" },
- { 5, 9, 1, -3, 6, "\0\1\1\1\1\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\0\1\0\0\0\0\1\1\1\0\1\0\0\0\1\1\0\0\0\1\0\1\1\1\0" },
- { 6, 9, 0, 0, 6, "\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" },
- { 3, 9, 0, 0, 3, "\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 2, 12, 0, -3, 3, "\0\1\0\0\0\0\1\1\0\1\0\1\0\1\0\1\0\1\0\1\0\1\1\0" },
- { 6, 9, 0, 0, 6, "\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\0\0\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\1" },
- { 3, 9, 0, 0, 3, "\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 9, 6, 0, 0, 9, "\1\0\1\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1\0\1\1" },
- { 6, 6, 0, 0, 6, "\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" },
- { 4, 6, 1, 0, 6, "\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 5, 9, 0, -3, 6, "\1\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\0" },
- { 5, 9, 1, -3, 6, "\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\1\1\1" },
- { 4, 6, 0, 0, 4, "\1\0\1\1\0\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\1\1\1\0" },
- { 4, 6, 1, 0, 6, "\0\1\1\1\1\0\0\1\1\1\0\0\0\0\1\1\1\0\0\1\1\1\1\0" },
- { 4, 7, 0, 0, 4, "\0\1\0\0\1\1\1\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\1" },
- { 6, 6, 0, 0, 6, "\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
- { 6, 6, 0, 0, 6, "\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0" },
- { 9, 6, 0, 0, 9, "\1\1\1\0\1\1\0\1\1\0\1\0\0\1\0\0\1\0\0\1\1\0\1\0\1\1\0\0\0\1\0\1\0\1\0\0\0\0\1\1\0\1\0\0\0\0\0\1\0\0\1\0\0\0" },
- { 5, 6, 1, 0, 6, "\1\1\0\1\1\0\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\1\0\1\1\0\1\1" },
- { 6, 9, 0, -3, 6, "\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" },
- { 4, 6, 1, 0, 6, "\1\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\1" },
- { 4, 12, 1, -3, 6, "\0\0\1\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\1" },
- { 1, 9, 1, 0, 3, "\1\1\1\1\1\1\1\1\1" },
- { 4, 12, 0, -3, 6, "\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\1\1\0\0" },
- { 6, 2, 0, 3, 7, "\0\1\1\0\0\1\1\0\0\1\1\0" },
- { 1, 9, 1, -3, 4, "\1\0\1\1\1\1\1\1\1" },
- { 5, 8, 1, -1, 6, "\0\0\0\0\1\0\1\1\1\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\1\0\0\1\0\1\1\1\0\1\0\0\0\0" },
- { 5, 9, 0, 0, 6, "\0\0\1\1\0\0\1\0\0\1\0\1\0\0\0\0\1\0\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\1\1\1\0\1\1" },
- { 6, 7, 1, 1, 7, "\1\0\0\0\0\1\0\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\0\1\0\0\0\0\1" },
- { 5, 9, 0, 0, 6, "\1\0\0\0\1\1\0\0\0\1\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\1\1\0" },
- { 1, 9, 1, 0, 3, "\1\1\1\0\0\1\1\1\1" },
- { 4, 12, 1, -3, 6, "\0\1\1\1\1\0\0\1\1\1\0\0\0\1\1\0\1\0\1\1\1\0\0\1\1\0\0\1\1\1\0\1\0\1\1\0\0\0\1\1\1\0\0\1\1\1\1\0" },
- { 3, 1, 0, 7, 3, "\1\0\1" },
- { 9, 9, 1, 0, 11, "\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\1\1\0\1\0\1\0\1\0\0\1\0\0\1\1\0\1\0\0\0\0\0\1\1\0\1\0\0\1\0\0\1\0\1\0\1\1\1\0\1\0\0\1\1\0\0\0\1\1\0\0\0\0\1\1\1\0\0\0" },
- { 3, 6, 1, 3, 5, "\1\1\0\0\0\1\1\1\1\1\0\1\0\0\0\1\1\1" },
- { 5, 5, 1, 0, 7, "\0\0\1\0\1\0\1\0\1\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1" },
- { 6, 4, 1, 1, 8, "\1\1\1\1\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1" },
- { 4, 1, 1, 3, 6, "\1\1\1\1" },
- { 9, 9, 1, 0, 11, "\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\1\1\0\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\1\1\0\0\1\1\0\0\1\0\1\0\0\1\1\1\0\1\0\1\0\1\0\0\1\1\0\0\0\1\1\0\0\0\1\1\1\1\0\0\0" },
- { 4, 1, 0, 7, 4, "\1\1\1\1" },
- { 4, 4, 0, 5, 5, "\0\1\1\0\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 5, 7, 1, 0, 7, "\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\1\1" },
- { 4, 5, 0, 4, 4, "\0\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1" },
- { 3, 5, 0, 4, 4, "\1\1\1\0\0\1\0\1\0\0\0\1\1\1\0" },
- { 2, 2, 1, 7, 4, "\0\1\1\0" },
- { 6, 9, 0, -3, 6, "\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\1\0\0\0\0\0\1\0\0\0\0\0\1\1\0\0\0" },
- { 6, 12, 0, -3, 7, "\0\1\1\1\1\1\1\1\1\0\1\0\1\1\1\0\1\0\1\1\1\0\1\0\1\1\1\0\1\0\0\1\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0" },
- { 1, 1, 1, 3, 3, "\1" },
- { 3, 3, 0, -3, 3, "\0\1\0\0\0\1\1\1\1" },
- { 3, 5, 0, 4, 4, "\0\1\0\1\1\0\0\1\0\0\1\0\1\1\1" },
- { 3, 6, 1, 3, 5, "\0\1\0\1\0\1\1\0\1\0\1\0\0\0\0\1\1\1" },
- { 5, 5, 0, 0, 7, "\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\1\0\1\0\1\0\1\0\0" },
- { 9, 9, 0, 0, 9, "\0\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\1\0\1\0\0\0\1\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0" },
- { 9, 9, 0, 0, 9, "\0\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\1\0\1\1\0\0\0\0\1\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\1\1\1" },
- { 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\1\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\1\0\1\0\0\0\1\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0" },
- { 4, 9, 0, -3, 5, "\0\0\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\1\1\0" },
- { 9, 12, 0, 0, 9, "\0\0\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
- { 9, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
- { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
- { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
- { 9, 11, 0, 0, 9, "\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
- { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
- { 10, 9, 0, 0, 11, "\0\0\1\1\1\1\1\1\1\1\0\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\1\1\1\1\0\0\1\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\0\0\1\1\1\1\1\1" },
- { 7, 12, 0, -3, 8, "\0\0\1\1\1\0\1\0\1\1\0\0\1\1\0\1\0\0\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\0\1\1\0\0\1\1\0\0\1\1\1\1\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\1\1\0\0" },
- { 7, 12, 0, 0, 8, "\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
- { 7, 12, 0, 0, 8, "\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
- { 7, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
- { 7, 11, 0, 0, 8, "\0\0\1\0\1\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
- { 3, 12, 0, 0, 4, "\1\0\0\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 3, 12, 0, 0, 4, "\0\0\1\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 3, 12, 0, 0, 4, "\0\1\0\1\0\1\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 3, 11, 0, 0, 4, "\1\0\1\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 8, 9, 0, 0, 9, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\1\1\1\1\1\1\0\0" },
- { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\1\1\0\0\0\0\1\1\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\0\0\0\1\0" },
- { 8, 12, 0, 0, 9, "\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 12, 0, 0, 9, "\0\0\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 12, 0, 0, 9, "\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 11, 0, 0, 9, "\0\0\1\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 5, 5, 1, 1, 7, "\1\0\0\0\1\0\1\0\1\0\0\0\1\0\0\0\1\0\1\0\1\0\0\0\1" },
- { 9, 10, 0, 0, 9, "\0\0\0\0\0\0\0\0\1\0\0\1\1\1\1\0\1\0\0\1\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\1\0\0\1\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\0\1\1\0\0\1\0\1\1\1\1\0\0\0" },
- { 8, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 12, 0, 0, 8, "\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 11, 0, 0, 8, "\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 9, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\0\0\0" },
- { 7, 9, 0, 0, 7, "\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0" },
- { 6, 9, 0, 0, 6, "\0\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\0\1\1\1\0\1\1\0" },
- { 5, 9, 1, 0, 6, "\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 5, 9, 1, 0, 6, "\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 5, 9, 1, 0, 6, "\0\1\0\1\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 5, 8, 1, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 8, 6, 1, 0, 9, "\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\1\1\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\0\1\1\0\1\1\1\0" },
- { 4, 9, 1, -3, 5, "\0\1\1\0\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\1\0\1\1\0\0\1\0\0\0\0\1\0\1\1\1\0" },
- { 5, 9, 1, 0, 6, "\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
- { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
- { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
- { 5, 8, 1, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
- { 3, 9, 0, 0, 3, "\1\0\0\0\1\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 3, 9, 0, 0, 3, "\0\1\0\1\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 3, 9, 0, 0, 3, "\0\1\0\1\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 3, 8, 0, 0, 3, "\1\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 4, 9, 1, 0, 6, "\0\1\0\0\0\1\1\1\1\0\1\0\0\1\1\1\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 6, 9, 0, 0, 6, "\0\0\1\0\1\0\0\1\0\1\0\0\0\0\0\0\0\0\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" },
- { 4, 9, 1, 0, 6, "\0\1\0\0\0\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 4, 9, 1, 0, 6, "\0\0\1\0\0\1\0\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 4, 9, 1, 0, 6, "\0\0\1\0\0\1\0\1\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 4, 9, 1, 0, 6, "\0\1\0\1\1\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 4, 8, 1, 0, 6, "\1\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 5, 5, 1, 1, 7, "\0\0\1\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\1\0\0" },
- { 6, 7, 0, -1, 6, "\0\0\1\1\0\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\0\0\1\0\0\0\0\0" },
- { 6, 9, 0, 0, 6, "\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
- { 6, 9, 0, 0, 6, "\0\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
- { 6, 9, 0, 0, 6, "\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
- { 6, 8, 0, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
- { 6, 12, 0, -3, 6, "\0\0\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" },
- { 5, 12, 0, -3, 6, "\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\0" },
- { 6, 11, 0, -3, 6, "\0\1\0\0\1\0\0\0\0\0\0\0\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" }
-};
-
-static struct font default_bdffont = { 14, 15, -1, -3, {
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- _g + 0,
- _g + 1,
- _g + 2,
- _g + 3,
- _g + 4,
- _g + 5,
- _g + 6,
- _g + 7,
- _g + 8,
- _g + 9,
- _g + 10,
- _g + 11,
- _g + 12,
- _g + 13,
- _g + 14,
- _g + 15,
- _g + 16,
- _g + 17,
- _g + 18,
- _g + 19,
- _g + 20,
- _g + 21,
- _g + 22,
- _g + 23,
- _g + 24,
- _g + 25,
- _g + 26,
- _g + 27,
- _g + 28,
- _g + 29,
- _g + 30,
- _g + 31,
- _g + 32,
- _g + 33,
- _g + 34,
- _g + 35,
- _g + 36,
- _g + 37,
- _g + 38,
- _g + 39,
- _g + 40,
- _g + 41,
- _g + 42,
- _g + 43,
- _g + 44,
- _g + 45,
- _g + 46,
- _g + 47,
- _g + 48,
- _g + 49,
- _g + 50,
- _g + 51,
- _g + 52,
- _g + 53,
- _g + 54,
- _g + 55,
- _g + 56,
- _g + 57,
- _g + 58,
- _g + 59,
- _g + 60,
- _g + 61,
- _g + 62,
- _g + 63,
- _g + 64,
- _g + 65,
- _g + 66,
- _g + 67,
- _g + 68,
- _g + 69,
- _g + 70,
- _g + 71,
- _g + 72,
- _g + 73,
- _g + 74,
- _g + 75,
- _g + 76,
- _g + 77,
- _g + 78,
- _g + 79,
- _g + 80,
- _g + 81,
- _g + 82,
- _g + 83,
- _g + 84,
- _g + 85,
- _g + 86,
- _g + 87,
- _g + 88,
- _g + 89,
- _g + 90,
- _g + 91,
- _g + 92,
- _g + 93,
- _g + 94,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- _g + 95,
- _g + 96,
- _g + 97,
- _g + 98,
- _g + 99,
- _g + 100,
- _g + 101,
- _g + 102,
- _g + 103,
- _g + 104,
- _g + 105,
- _g + 106,
- _g + 107,
- _g + 108,
- _g + 109,
- _g + 110,
- _g + 111,
- _g + 112,
- _g + 113,
- _g + 114,
- _g + 115,
- _g + 116,
- _g + 117,
- _g + 118,
- _g + 119,
- _g + 120,
- _g + 121,
- _g + 122,
- _g + 123,
- _g + 124,
- _g + 125,
- _g + 126,
- _g + 127,
- _g + 128,
- _g + 129,
- _g + 130,
- _g + 131,
- _g + 132,
- _g + 133,
- _g + 134,
- _g + 135,
- _g + 136,
- _g + 137,
- _g + 138,
- _g + 139,
- _g + 140,
- _g + 141,
- _g + 142,
- _g + 143,
- _g + 144,
- _g + 145,
- _g + 146,
- _g + 147,
- _g + 148,
- _g + 149,
- _g + 150,
- _g + 151,
- _g + 152,
- _g + 153,
- _g + 154,
- _g + 155,
- _g + 156,
- _g + 157,
- _g + 158,
- _g + 159,
- _g + 160,
- _g + 161,
- _g + 162,
- _g + 163,
- _g + 164,
- _g + 165,
- _g + 166,
- _g + 167,
- _g + 168,
- _g + 169,
- _g + 170,
- _g + 171,
- _g + 172,
- _g + 173,
- _g + 174,
- _g + 175,
- _g + 176,
- _g + 177,
- _g + 178,
- _g + 179,
- _g + 180,
- _g + 181,
- _g + 182,
- _g + 183,
- _g + 184,
- _g + 185,
- _g + 186,
- _g + 187,
- _g + 188,
- _g + 189
- }
-};
-
-
-
-struct font *
-pbm_defaultfont(const char * const name) {
-/*----------------------------------------------------------------------------
-   Generate the built-in font with name 'name'.
------------------------------------------------------------------------------*/
-    struct font * retval;
-
-    if (strcmp(name, "bdf") == 0)
-        retval = &default_bdffont;
-    else {
-        bit** defaultfont;
-        unsigned int row;
-
-        if (strcmp(name, "fixed") != 0)
-            pm_error( "built-in font name unknown, try 'bdf' or 'fixed'");
-
-        defaultfont = pbm_allocarray(DEFAULTFONT_COLS, DEFAULTFONT_ROWS);
-        for (row =  0; row < DEFAULTFONT_ROWS; ++row) {
-            unsigned int col;
-            for (col = 0; col < DEFAULTFONT_COLS; col += 32) {
-                int scol;
-                unsigned long l;
-
-                l = defaultfont_bits[row][col / 32]; /* initial value */
-
-                for (scol = MIN( col + 32, DEFAULTFONT_COLS) - 1;
-                     scol >= (int)col; 
-                     --scol) {
-                    if (l & 0x01)
-                        defaultfont[row][scol] = 1;
-                    else
-                        defaultfont[row][scol] = 0;
-                    l >>= 1;
-                }
-            }
-        }
-        retval = 
-            pbm_dissectfont((const bit **)defaultfont,
-                            DEFAULTFONT_ROWS, DEFAULTFONT_COLS);
-    }
-    return retval;
-}
-
-
-
-static void
-findFirstBlankRow(const bit **   const font,
-                  unsigned int   const fcols,
-                  unsigned int   const frows,
-                  unsigned int * const browP) {
-
-    unsigned int row;
-    bool foundBlank;
-
-    for (row = 0, foundBlank = false; row < frows / 6 && !foundBlank; ++row) {
-        unsigned int col;
-        bit col0Value = font[row][0];
-        bool rowIsBlank;
-        rowIsBlank = true;  /* initial assumption */
-        for (col = 1; col < fcols; ++col)
-            if (font[row][col] != col0Value)
-                rowIsBlank = false;
-
-        if (rowIsBlank) {
-            foundBlank = true;
-            *browP = row;
-        }
-    }
-
-    if (!foundBlank)
-        pm_error("couldn't find blank pixel row in font");
-}
-
-
-
-static void
-findFirstBlankCol(const bit **   const font,
-                  unsigned int   const fcols,
-                  unsigned int   const frows,
-                  unsigned int * const bcolP) {
-
-    unsigned int col;
-    bool foundBlank;
-
-    for (col = 0, foundBlank = false; col < fcols / 6 && !foundBlank; ++col) {
-        unsigned int row;
-        bit row0Value = font[0][col];
-        bool colIsBlank;
-        colIsBlank = true;  /* initial assumption */
-        for (row = 1; row < frows; ++row)
-            if (font[row][col] != row0Value)
-                colIsBlank = false;
-
-        if (colIsBlank) {
-            foundBlank = true;
-            *bcolP = col;
-        }
-    }
-
-    if (!foundBlank)
-        pm_error("couldn't find blank pixel column in font");
-}
-
-
-
-static void
-computeCharacterSize(const bit **   const font,
-                     unsigned int   const fcols,
-                     unsigned int   const frows,
-                     unsigned int * const cellWidthP,
-                     unsigned int * const cellHeightP,
-                     unsigned int * const charWidthP,
-                     unsigned int * const charHeightP) {
-
-    unsigned int firstBlankRow;
-    unsigned int firstBlankCol;
-    unsigned int heightLast11Rows;
-
-    findFirstBlankRow(font, fcols, frows, &firstBlankRow);
-
-    findFirstBlankCol(font, fcols, frows, &firstBlankCol);
-
-    heightLast11Rows = frows - firstBlankRow;
-
-    if (heightLast11Rows % 11 != 0)
-        pm_error("The rows of characters in the font do not appear to "
-                 "be all the same height.  The last 11 rows are %u pixel "
-                 "rows high (from pixel row %u up to %u), "
-                 "which is not a multiple of 11.",
-                 heightLast11Rows, firstBlankRow, frows);
-    else {
-        unsigned int widthLast15Cols;
-
-        *cellHeightP = heightLast11Rows / 11;
-
-        widthLast15Cols = fcols - firstBlankCol;
-
-        if (widthLast15Cols % 15 != 0)
-            pm_error("The columns of characters in the font do not appear to "
-                     "be all the same width.  "
-                     "The last 15 columns are %u pixel "
-                     "columns wide (from pixel col %u up to %u), "
-                     "which is not a multiple of 15.",
-                     widthLast15Cols, firstBlankCol, fcols);
-        else {
-            *cellWidthP = widthLast15Cols / 15;
-
-            *charWidthP = firstBlankCol;
-            *charHeightP = firstBlankRow;
-        }
-    }
-}
-
-
-
-struct font*
-pbm_dissectfont(const bit ** const font,
-                unsigned int const frows,
-                unsigned int const fcols) {
-    /*
-       This routine expects a font bitmap representing the following text:
-      
-       (0,0)
-          M ",/^_[`jpqy| M
-      
-          /  !"#$%&'()*+ /
-          < ,-./01234567 <
-          > 89:;<=>?@ABC >
-          @ DEFGHIJKLMNO @
-          _ PQRSTUVWXYZ[ _
-          { \]^_`abcdefg {
-          } hijklmnopqrs }
-          ~ tuvwxyz{|}~  ~
-      
-          M ",/^_[`jpqy| M
-      
-       The bitmap must be cropped exactly to the edges.
-      
-       The characters in the border you see are irrelevant except for
-       character size compuations.  The 12 x 8 array in the center is
-       the font.  The top left character there belongs to code point
-       0, and the code points increase in standard reading order, so
-       the bottom right character is code point 127.  You can't define
-       code points < 32 or > 127 with this font format.
-
-       The characters in the top and bottom border rows must include a
-       character with the lowest reach of any in the font (e.g. "y",
-       "_") and one with the highest reach (e.g. '"').  The characters
-       in the left and right border columns must include characters
-       with the rightmost and leftmost reach of any in the font
-       (e.g. "M" for both).
-
-       The border must be separated from the font by one blank text
-       row or text column.
-      
-       The dissection works by finding the first blank row and column;
-       i.e the lower right corner of the "M" in the upper left corner
-       of the matrix.  That gives the height and width of the
-       maximum-sized character, which is not too useful.  But the
-       distance from there to the opposite side is an integral
-       multiple of the cell size, and that's what we need.  Then it's
-       just a matter of filling in all the coordinates.  */
-    
-    unsigned int cellWidth, cellHeight;
-        /* Dimensions in pixels of each cell of the font -- that
-           includes the glyph and the white space above and to the
-           right of it.  Each cell is a tile of the font image.  The
-           top character row and left character row don't count --
-           those cells are smaller because they are missing the white
-           space.
-        */
-    unsigned int charWidth, charHeight;
-        /* Maximum dimensions of glyph itself, inside its cell */
-
-    int row, col, ch, r, c, i;
-    struct font * fn;
-    struct glyph * glyph;
-    char* bmap;
-
-    computeCharacterSize(font, fcols, frows,
-                         &cellWidth, &cellHeight, &charWidth, &charHeight);
-
-    /* Now convert to a general font */
-
-    MALLOCVAR(fn);
-    if (fn == NULL)
-        pm_error("out of memory allocating font structure");
-
-    fn->maxwidth  = charWidth;
-    fn->maxheight = charHeight;
-    fn->x = fn->y = 0;
-
-    fn->oldfont = font;
-    fn->frows = frows;
-    fn->fcols = fcols;
-    
-    /* Initialize all character positions to "undefined."  Those that
-       are defined in the font will be filled in below.
-    */
-    for (i = 0; i < 256; i++)
-        fn->glyph[i] = NULL;
-
-    MALLOCARRAY(glyph, nCharsInFont);
-    if ( glyph == NULL )
-        pm_error( "out of memory allocating glyphs" );
-    
-    bmap = (char*) malloc( fn->maxwidth * fn->maxheight * nCharsInFont );
-    if ( bmap == (char*) 0)
-        pm_error( "out of memory allocating glyph data" );
-
-    /* Now fill in the 0,0 coords. */
-    row = cellHeight * 2;
-    col = cellWidth * 2;
-    for (i = 0; i < firstCodePoint; ++i)
-        fn->glyph[i] = NULL;
-
-    for ( ch = 0; ch < nCharsInFont; ++ch ) {
-        glyph[ch].width = fn->maxwidth;
-        glyph[ch].height = fn->maxheight;
-        glyph[ch].x = glyph[ch].y = 0;
-        glyph[ch].xadd = cellWidth;
-
-        for ( r = 0; r < glyph[ch].height; ++r )
-            for ( c = 0; c < glyph[ch].width; ++c )
-                bmap[r * glyph[ch].width + c] = font[row + r][col + c];
-    
-        glyph[ch].bmap = bmap;
-        bmap += glyph[ch].width * glyph[ch].height;
-
-        fn->glyph[firstCodePoint + ch] = &glyph[ch];
-
-        col += cellWidth;
-        if ( col >= cellWidth * 14 ) {
-            col = cellWidth * 2;
-            row += cellHeight;
-        }
-    }
-    for (i = firstCodePoint + nCharsInFont; i < 256; ++i)
-        fn->glyph[i] = NULL;
-    
-    return fn;
-}
-
-
-
-struct font*
-pbm_loadfont(const char * const filename)
-{
-    FILE* fp;
-    struct font* fn;
-    char line[256];
-
-    fp = pm_openr( filename );
-    fgets(line, 256, fp);
-    pm_close( fp );
-
-    if (line[0] == PBM_MAGIC1 && 
-        (line[1] == PBM_MAGIC2 || line[1] == RPBM_MAGIC2)) {
-        return pbm_loadpbmfont( filename );
-    } else if (!strncmp(line, "STARTFONT", 9)) {
-        if (!(fn = pbm_loadbdffont( filename )))
-          pm_error( "could not load BDF font file" );
-        return fn;
-    } else {
-      pm_error( "font file not in a recognized format ");
-      return NULL;  /* should never reach here */
-  }
-}
-
-
-
-struct font *
-pbm_loadpbmfont(const char * const filename) {
-
-    FILE * ifP;
-    bit ** font;
-    int fcols, frows;
-
-    ifP = pm_openr(filename);
-    font = pbm_readpbm(ifP, &fcols, &frows);
-    pm_close(ifP);
-    return pbm_dissectfont((const bit **)font, frows, fcols);
-}
-
-
-
-void
-pbm_dumpfont( fn )
-    struct font* fn;
-{
-    /* Dump out font as C source code. */
-    int row, col, scol, lperrow;
-    unsigned long l;
-
-    if (fn->oldfont) {
-    printf( "#define DEFAULTFONT_ROWS %d\n", fn->frows );
-    printf( "#define DEFAULTFONT_COLS %d\n", fn->fcols );
-    printf( "static unsigned long defaultfont_bits[DEFAULTFONT_ROWS][(DEFAULTFONT_COLS+31)/32] = {\n" );
-    for ( row = 0; row < fn->frows; ++row )
-        {
-        lperrow = 0;
-        for ( col = 0; col < fn->fcols; col += 32 )
-        {
-        if ( lperrow == 0 )
-            printf( "    {" );
-        else if ( lperrow % 6 == 0 )
-            {
-            printf( ",\n     " );
-            lperrow = 0;
-            }
-        else
-            printf( "," );
-        l = 0;
-        for ( scol = col; scol < MIN( col + 32, fn->fcols ); ++scol )
-            {
-            l <<= 1;
-            if ( fn->oldfont[row][scol] )
-            l |= 1;
-            }
-        printf( "0x%08lxL", l );
-        ++lperrow;
-        }
-        printf( "}%s\n", row == fn->frows - 1 ? "" : "," );
-        }
-    printf( "    };\n" );
-    }
-    else {
-    struct glyph* glyph;
-    int i, j, ng;
-
-    ng = 0;
-    for (i = 0; i < 256; i++)
-        if (fn->glyph[i])
-            ng++;
-
-    printf("static struct glyph _g[%d] = {\n", ng);
-    for (i = 0; i < 256; i++) {
-        if (!(glyph = fn->glyph[i]))
-            continue;
-
-        printf(" { %d, %d, %d, %d, %d, \"", glyph->width, glyph->height,
-            glyph->x, glyph->y, glyph->xadd);
-
-        for (j = 0; j < glyph->width * glyph->height; j++)
-            if (glyph->bmap[j])
-                printf("\\1");
-            else
-                printf("\\0");
-        
-        ng--;
-        printf("\" }%s\n", ng ? "," : "");
-    }
-    printf("};\n");
-
-    printf("static struct font default_bdffont = { %d, %d, %d, %d, {\n",
-        fn->maxwidth, fn->maxheight, fn->x, fn->y);
-
-    for (i = 0; i < 256; i++) {
-        if (fn->glyph[i])
-            printf(" _g + %d", ng++);
-        else
-            printf(" 0");
-        
-        if (i != 255) printf(",");
-        printf("\n");
-    }
-
-    printf(" }\n};\n");
-    exit(0);
-
-    }
-
-}
-
-
-/* Routines for loading a BDF font file */
-
-
-typedef struct {
-/*----------------------------------------------------------------------------
-   This is an object for reading lines of a font file.  It reads and tokenizes
-   them into words.
------------------------------------------------------------------------------*/
-    FILE * ifP;
-
-    char line[1024];
-        /* This is the storage space for the words of the line.  The
-           words go in here, one after another, separated by NULs.
-
-           It also functions as a work area for readline_read().
-        */
-    const char * arg[32];
-        /* These are the words; each entry is a pointer into line[] (above) */
-} readline;
-
-
-
-static void
-readline_init(readline * const readlineP,
-              FILE *     const ifP) {
-
-    readlineP->ifP = ifP;
-
-    readlineP->arg[0] = NULL;
-}
-
-
-
-static void
-tokenize(char *         const s,
-         const char **  const words,
-         unsigned int   const maxWordCt) {
-/*----------------------------------------------------------------------------
-   Chop up 's' into words by changing space characters to NUL.  Return
-   as 'words' pointer to the beginning of those words in 's'.
-
-   If there are more than 'maxWordCt' words in 's', ignore the excess on
-   the right.
------------------------------------------------------------------------------*/
-    unsigned int n;
-    char * p;
-
-    p = &s[0];
-    n = 0;
-
-    while (*p) {
-        if (ISSPACE(*p))
-            *p++ = '\0';
-        else {
-            words[n++] = p;
-            if (n >= maxWordCt-1)
-                break;
-            while (*p && !ISSPACE(*p))
-                ++p;
-        }
-    }
-    words[n] = NULL;
-}
-
-
-
-static void
-readline_read(readline * const readlineP,
-              bool *     const eofP) {
-/*----------------------------------------------------------------------------
-   Read a nonblank line from the file.  Make its contents available
-   as readlineP->arg[].
-
-   Return *eofP == true iff there is no nonblank line before EOF or we
-   are unable to read the file.
------------------------------------------------------------------------------*/
-    bool gotLine;
-    bool error;
-
-    for (gotLine = false, error = false; !gotLine && !error; ) {
-        char * rc;
-
-        rc = fgets(readlineP->line, 1024, readlineP->ifP);
-        if (rc == NULL)
-            error = true;
-        else {
-            tokenize(readlineP->line,
-                     readlineP->arg, ARRAY_SIZE(readlineP->arg));
-            if (readlineP->arg[0] != NULL)
-                gotLine = true;
-        }
-    }
-    *eofP = error;
-}
-
-
-
-static void
-parseBitmapRow(const char *    const hex,
-               unsigned int    const glyphWidth,
-               unsigned char * const bmap,
-               unsigned int    const origBmapIndex,
-               unsigned int *  const newBmapIndexP,
-               const char **   const errorP) {
-/*----------------------------------------------------------------------------
-   Parse one row of the bitmap for a glyph, from the hexadecimal string
-   for that row in the font file, 'hex'.  The glyph is 'glyphWidth'
-   pixels wide.
-
-   We place our result in 'bmap' at *bmapIndexP and advanced *bmapIndexP.
------------------------------------------------------------------------------*/
-    unsigned int bmapIndex;
-    int i;  /* dot counter */
-    const char * p;
-
-    bmapIndex = origBmapIndex;
-
-    for (i = glyphWidth, p = &hex[0], *errorP = NULL;
-         i > 0 && !*errorP;
-         i -= 4) {
-
-        if (*p == '\0')
-            pm_asprintf(errorP, "Not enough hexadecimal digits for glyph "
-                        "of width %u in '%s'",
-                        glyphWidth, hex);
-        else {
-            char const hdig = *p++;
-            unsigned int hdigValue;
-
-            if (hdig >= '0' && hdig <= '9')
-                hdigValue = hdig - '0';
-            else if (hdig >= 'a' && hdig <= 'f')
-                hdigValue = 10 + (hdig - 'a');
-            else if (hdig >= 'A' && hdig <= 'F')
-                hdigValue = 10 + (hdig - 'A');
-            else 
-                pm_asprintf(errorP,
-                            "Invalid hex digit x%02x (%c) in bitmap data '%s'",
-                            (unsigned int)(unsigned char)hdig, 
-                            isprint(hdig) ? hdig : '.',
-                            hex);
-
-            if (!*errorP) {
-                if (i > 0)
-                    bmap[bmapIndex++] = hdigValue & 0x8 ? 1 : 0;
-                if (i > 1)
-                    bmap[bmapIndex++] = hdigValue & 0x4 ? 1 : 0;
-                if (i > 2)
-                    bmap[bmapIndex++] = hdigValue & 0x2 ? 1 : 0;
-                if (i > 3)
-                    bmap[bmapIndex++] = hdigValue & 0x1 ? 1 : 0;
-            }
-        }
-    }
-    *newBmapIndexP = bmapIndex;
-}
-
-
-
-static void
-readBitmap(readline *      const readlineP,
-           unsigned int    const glyphWidth,
-           unsigned int    const glyphHeight,
-           const char *    const charName,
-           unsigned char * const bmap) {
-
-    int n;
-    unsigned int bmapIndex;
-
-    bmapIndex = 0;
-           
-    for (n = glyphHeight; n > 0; --n) {
-        bool eof;
-        const char * error;
-
-        readline_read(readlineP, &eof);
-
-        if (eof)
-            pm_error("End of file in bitmap for character '%s' in BDF "
-                     "font file.", charName);
-
-        if (!readlineP->arg[0])
-            pm_error("A line that is supposed to contain bitmap data, "
-                     "in hexadecimal, for character '%s' is empty", charName);
-
-        parseBitmapRow(readlineP->arg[0], glyphWidth, bmap, bmapIndex,
-                       &bmapIndex, &error);
-
-        if (error) {
-            pm_error("Error in line %d of bitmap for character '%s': %s",
-                     n, charName, error);
-            pm_strfree(error);
-        }
-    }
-}
-
-
-
-static void
-createBmap(unsigned int  const glyphWidth,
-           unsigned int  const glyphHeight,
-           readline *    const readlineP,
-           const char *  const charName,
-           const char ** const bmapP) {
-
-    unsigned char * bmap;
-    bool eof;
-    
-    if (glyphWidth > 0 && UINT_MAX / glyphWidth < glyphHeight)
-        pm_error("Ridiculously large glyph");
-
-    MALLOCARRAY(bmap, glyphWidth * glyphHeight);
-
-    if (!bmap)
-        pm_error("no memory for font glyph byte map");
-
-    readline_read(readlineP, &eof);
-    if (eof)
-        pm_error("End of file encountered reading font glyph byte map from "
-                 "BDF font file.");
-    
-    if (streq(readlineP->arg[0], "ATTRIBUTES")) {
-        bool eof;
-        readline_read(readlineP, &eof);
-        if (eof)
-            pm_error("End of file encountered after ATTRIBUTES in BDF "
-                     "font file.");
-    }                
-    if (!streq(readlineP->arg[0], "BITMAP"))
-        pm_error("'%s' found where BITMAP expected in definition of "
-                 "character '%s' in BDF font file.",
-                 readlineP->arg[0], charName);
-
-    assert(streq(readlineP->arg[0], "BITMAP"));
-
-    readBitmap(readlineP, glyphWidth, glyphHeight, charName, bmap);
-
-    *bmapP = (char *)bmap;
-}
-
-
-
-static void
-readExpectedStatement(readline *    const readlineP,
-                      const char *  const expected) {
-/*----------------------------------------------------------------------------
-  Have the readline object *readlineP read the next line from the file, but
-  expect it to be a line of type 'expected' (i.e. the verb token at the
-  beginning of the line is that, e.g. "STARTFONT").  If it isn't, fail the
-  program.
------------------------------------------------------------------------------*/
-    bool eof;
-
-    readline_read(readlineP, &eof);
-
-    if (eof)
-        pm_error("EOF in BDF font file where '%s' expected", expected);
-    else if (!streq(readlineP->arg[0], expected))
-        pm_error("Statement of type '%s' where '%s' expected in BDF font file",
-                 readlineP->arg[0], expected);
-}
-
-
-
-static void
-skipCharacter(readline * const readlineP) {
-/*----------------------------------------------------------------------------
-  In the BDF font file being read by readline object *readlineP, skip through
-  the end of the character we are presently in.
------------------------------------------------------------------------------*/
-    bool endChar;
-                        
-    endChar = FALSE;
-                        
-    while (!endChar) {
-        bool eof;
-        readline_read(readlineP, &eof);
-        if (eof)
-            pm_error("End of file in the middle of a character (before "
-                     "ENDCHAR) in BDF font file.");
-        endChar = streq(readlineP->arg[0], "ENDCHAR");
-    }                        
-}
-
-
-
-static void
-interpEncoding(const char **  const arg,
-               unsigned int * const codepointP,
-               bool *         const badCodepointP) {
-/*----------------------------------------------------------------------------
-   With arg[] being the ENCODING statement from the font, return as
-   *codepointP the codepoint that it indicates (code point is the character
-   code, e.g. in ASCII, 48 is '0').
-
-   But if the statement doesn't give an acceptable codepoint return
-   *badCodepointP == TRUE.
------------------------------------------------------------------------------*/
-
-    bool gotCodepoint;
-    bool badCodepoint;
-    unsigned int codepoint;
-
-    if (!arg[1])
-        pm_error("Invalid ENCODING statement - no arguments");
-    if (atoi(arg[1]) >= 0) {
-        codepoint = atoi(arg[1]);
-        gotCodepoint = true;
-    } else {
-        if (arg[2]) {
-            codepoint = atoi(arg[2]);
-            gotCodepoint = true;
-        } else
-            gotCodepoint = false;
-    }
-    if (gotCodepoint) {
-        if (codepoint > 255)
-            badCodepoint = true;
-        else
-            badCodepoint = false;
-    } else
-        badCodepoint = true;
-
-    *badCodepointP = badCodepoint;
-    *codepointP    = codepoint;
-}
-
-
-
-static void
-readEncoding(readline *     const readlineP,
-             unsigned int * const codepointP,
-             bool *         const badCodepointP) {
-
-    readExpectedStatement(readlineP, "ENCODING");
-    
-    interpEncoding(readlineP->arg, codepointP, badCodepointP);
-}
-
-
-
-static void
-processChars(readline *    const readlineP,
-             struct font * const fontP) {
-/*----------------------------------------------------------------------------
-   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
-   contents to *fontP.
------------------------------------------------------------------------------*/
-    unsigned int nCharacters;
-    unsigned int nCharsDone;
-
-    if (!readlineP->arg[1])
-        pm_error("Invalid CHARS line - no arguments");
-
-    nCharacters = atoi(readlineP->arg[1]);
-
-    nCharsDone = 0;
-
-    while (nCharsDone < nCharacters) {
-        bool eof;
-
-        readline_read(readlineP, &eof);
-        if (eof)
-            pm_error("End of file after CHARS reading BDF font file");
-
-        if (streq(readlineP->arg[0], "COMMENT")) {
-            /* ignore */
-        } else if (!streq(readlineP->arg[0], "STARTCHAR"))
-            pm_error("no STARTCHAR after CHARS in BDF font file");
-        else if (!readlineP->arg[1])
-            pm_error("Invalid STARTCHAR - no arguments");
-        else {
-            const char * const charName = readlineP->arg[1];
-
-            struct glyph * glyphP;
-            unsigned int codepoint;
-            bool badCodepoint;
-
-            assert(streq(readlineP->arg[0], "STARTCHAR"));
-
-            MALLOCVAR(glyphP);
-
-            if (glyphP == NULL)
-                pm_error("no memory for font glyph for '%s' character",
-                         charName);
-
-            readEncoding(readlineP, &codepoint, &badCodepoint);
-
-            if (badCodepoint)
-                skipCharacter(readlineP);
-            else {
-                readExpectedStatement(readlineP, "SWIDTH");
-                    
-                readExpectedStatement(readlineP, "DWIDTH");
-                if (!readlineP->arg[1])
-                    pm_error("Invalid DWIDTH statement - no arguments");
-                glyphP->xadd = atoi(readlineP->arg[1]);
-
-                readExpectedStatement(readlineP, "BBX");
-                if (!readlineP->arg[1])
-                    pm_error("Invalid BBX statement - no arguments");
-                glyphP->width  = atoi(readlineP->arg[1]);
-                if (!readlineP->arg[2])
-                    pm_error("Invalid BBX statement - only 1 argument");
-                glyphP->height = atoi(readlineP->arg[2]);
-                if (!readlineP->arg[3])
-                    pm_error("Invalid BBX statement - only 2 arguments");
-                glyphP->x      = atoi(readlineP->arg[3]);
-                if (!readlineP->arg[4])
-                    pm_error("Invalid BBX statement - only 3 arguments");
-                glyphP->y      = atoi(readlineP->arg[4]);
-
-                createBmap(glyphP->width, glyphP->height, readlineP, charName,
-                           &glyphP->bmap);
-                
-
-                readExpectedStatement(readlineP, "ENDCHAR");
-
-                assert(codepoint < 256); /* Ensured by readEncoding() */
-
-                fontP->glyph[codepoint] = glyphP;
-            }
-            ++nCharsDone;
-        }
-    }
-}
-
-
-
-static void
-processBdfFontLine(readline *    const readlineP,
-                   struct font * const fontP,
-                   bool *        const endOfFontP) {
-/*----------------------------------------------------------------------------
-   Process a nonblank line just read from a BDF font file.
-
-   This processing may involve reading more lines.
------------------------------------------------------------------------------*/
-    *endOfFontP = FALSE;  /* initial assumption */
-
-    assert(readlineP->arg[0] != NULL);  /* Entry condition */
-
-    if (streq(readlineP->arg[0], "COMMENT")) {
-        /* ignore */
-    } else if (streq(readlineP->arg[0], "SIZE")) {
-        /* ignore */
-    } else if (streq(readlineP->arg[0], "STARTPROPERTIES")) {
-        /* Read off the properties and ignore them all */
-        unsigned int propCount;
-        unsigned int i;
-
-        if (!readlineP->arg[1])
-            pm_error("Invalid STARTPROPERTIES statement - no arguments");
-        propCount = atoi(readlineP->arg[1]);
-
-        for (i = 0; i < propCount; ++i) {
-            bool eof;
-            readline_read(readlineP, &eof);
-            if (eof)
-                pm_error("End of file after STARTPROPERTIES in BDF font file");
-        }
-    } else if (streq(readlineP->arg[0], "FONTBOUNDINGBOX")) {
-        if (!readlineP->arg[1])
-            pm_error("Invalid FONTBOUNDINGBOX  statement - no arguments");
-        fontP->maxwidth  = atoi(readlineP->arg[1]);
-        if (!readlineP->arg[2])
-            pm_error("Invalid FONTBOUNDINGBOX  statement - only 1 argument");
-        fontP->maxheight = atoi(readlineP->arg[2]);
-        if (!readlineP->arg[3])
-            pm_error("Invalid FONTBOUNDINGBOX  statement - only 2 arguments");
-        fontP->x         = atoi(readlineP->arg[3]);
-        if (!readlineP->arg[4])
-            pm_error("Invalid FONTBOUNDINGBOX  statement - only 3 arguments");
-        fontP->y         = atoi(readlineP->arg[4]);
-    } else if (streq(readlineP->arg[0], "ENDFONT")) {
-        *endOfFontP = true;
-    } else if (streq(readlineP->arg[0], "CHARS")) {
-        processChars(readlineP, fontP);
-    } else {
-        /* ignore */
-    }
-}
-
-
-
-struct font *
-pbm_loadbdffont(const char * const name) {
-
-    FILE * ifP;
-    readline readline;
-    struct font * fontP;
-    bool endOfFont;
-
-    ifP = fopen(name, "rb");
-    if (!ifP)
-        pm_error("Unable to open BDF font file name '%s'.  errno=%d (%s)",
-                 name, errno, strerror(errno));
-
-    readline_init(&readline, ifP);
-
-    MALLOCVAR(fontP);
-    if (fontP == NULL)
-        pm_error("no memory for font");
-
-    fontP->oldfont = 0;
-    { 
-        /* 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;
-    }
-    fontP->x = fontP->y = 0;
-
-    readExpectedStatement(&readline, "STARTFONT");
-
-    endOfFont = FALSE;
-
-    while (!endOfFont) {
-        bool eof;
-        readline_read(&readline, &eof);
-        if (eof)
-            pm_error("End of file before ENDFONT statement in BDF font file");
-
-        processBdfFontLine(&readline, fontP, &endOfFont);
-    }
-    return fontP;
-}
diff --git a/lib/libpbmfont0.c b/lib/libpbmfont0.c
new file mode 100644
index 00000000..503c7ee7
--- /dev/null
+++ b/lib/libpbmfont0.c
@@ -0,0 +1,335 @@
+/*
+**
+** Font routines.
+**
+** Wide character stuff written by Akira Urushibata in 2018 and contributed
+** to the public domain.
+**
+** Also copyright (C) 1991 by Jef Poskanzer and licensed to the public as
+** follows.
+**
+** 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.
+*/
+
+#include <assert.h>
+#include <string.h>
+
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+
+#include "pbm.h"
+#include "pbmfont.h"
+#include "pbmfontdata.h"
+
+
+struct font *
+pbm_defaultfont(const char * const name) {
+/*----------------------------------------------------------------------------
+   Generate the built-in font with name 'name'.
+-----------------------------------------------------------------------------*/
+    struct font * retval;
+
+    if (streq(name, "bdf"))
+        retval = &pbm_defaultBdffont;
+    else if (streq(name, "fixed"))
+        retval = &pbm_defaultFixedfont;
+    else
+        pm_error( "built-in font name unknown, try 'bdf' or 'fixed'");
+
+    return retval;
+}
+
+
+
+struct font2 *
+pbm_defaultfont2(const char * const requestedFontName) {
+
+    struct font2 * font2P;
+    struct font2 * retval = NULL; /* initial value */
+    unsigned int i;
+
+    for (i = 0; retval == NULL; ++i) {
+        const char * longName;
+        const char * shortName;
+        font2P = (struct font2 * ) pbm_builtinFonts[i];
+        if (font2P == NULL)
+            break;
+
+        longName = font2P->name;
+        shortName = &longName[strlen("builtin ")];
+
+        if (streq(shortName, requestedFontName))
+             retval = font2P;
+    }
+
+    if (retval == NULL)
+        pm_error("No builtin font named %s", requestedFontName);
+
+    return retval;
+}
+
+
+
+static void
+selectFontType(const    char * const filename,
+               PM_WCHAR        const maxmaxglyph,
+               unsigned int    const isWide,
+               struct font  ** const fontPP,
+               struct font2 ** const font2PP) {
+
+    FILE * fileP;
+    struct font  * fontP  = NULL; /* initial value */
+    struct font2 * font2P = NULL; /* initial value */
+    char line[10] = "\0\0\0\0\0\0\0\0\0\0";
+        /* Initialize to suppress Valgrind error which is reported when file
+           is empty or very small.
+        */
+
+    fileP = pm_openr(filename);
+    fgets(line, 10, fileP);
+    pm_close(fileP);
+
+    if (line[0] == PBM_MAGIC1 &&
+        (line[1] == PBM_MAGIC2 || line[1] == RPBM_MAGIC2)) {
+        if (isWide == TRUE)
+            font2P = pbm_loadpbmfont2(filename);
+        else
+            fontP  = pbm_loadpbmfont(filename);
+        if (fontP == NULL && font2P == NULL)
+            pm_error("could not load PBM font file");
+
+    } else if (!strncmp(line, "STARTFONT", 9)) {
+        if (isWide == TRUE)
+            font2P = pbm_loadbdffont2(filename, maxmaxglyph);
+        else
+            fontP = pbm_loadbdffont(filename);
+        if (fontP == NULL && font2P == NULL)
+            pm_error("could not load BDF font file");
+
+    } else {
+        pm_error("font file not in a recognized format.  Does not start "
+                 "with the signature of a PBM file or BDF font file");
+        assert(false);
+        fontP = NULL;  /* defeat compiler warning */
+    }
+
+    if (isWide)
+        *font2PP = font2P;
+    else
+        *fontPP = fontP;
+}
+
+
+
+struct font *
+pbm_loadfont(const    char * const filename) {
+
+    struct font  * fontP;
+    struct font2 * font2P;
+
+    selectFontType(filename, PM_FONT_MAXGLYPH, FALSE, &fontP, &font2P);
+    return fontP;
+}
+
+
+
+struct font2 *
+pbm_loadfont2(const    char * const filename,
+              PM_WCHAR        const maxmaxglyph) {
+
+    struct font  * fontP;
+    struct font2 * font2P;
+
+    selectFontType(filename, maxmaxglyph, TRUE, &fontP, &font2P);
+    return font2P;
+}
+
+
+
+void
+pbm_createbdffont2_base(struct font2 ** const font2PP,
+                        PM_WCHAR        const maxmaxglyph) {
+
+    struct font2 * font2P;
+
+    MALLOCVAR(font2P);
+    if (font2P == NULL)
+        pm_error("no memory for font");
+
+    MALLOCARRAY(font2P->glyph, maxmaxglyph + 1);
+    if (font2P->glyph == NULL)
+        pm_error("no memory for font glyphs");
+
+    /* Initialize */
+    font2P->size = sizeof (struct font2);
+    font2P->len  = PBM_FONT2_STRUCT_SIZE(charset_string);
+
+    /*  Caller should overwrite following fields as necessary */
+    font2P->oldfont = NULL;
+    font2P->fcols = font2P->frows = 0;
+    font2P->selector = NULL;
+    font2P->default_char = 0;
+    font2P->default_char_defined = FALSE;
+    font2P->total_chars = font2P->chars = 0;
+    font2P->name = NULL;
+    font2P->charset = ENCODING_UNKNOWN;
+    font2P->charset_string = NULL;
+
+    *font2PP = font2P;
+}
+
+
+
+static void
+destroyGlyphData(struct glyph ** const glyph,
+                 PM_WCHAR        const maxglyph) {
+/*----------------------------------------------------------------------------
+  Free glyph objects and bitmap objects.
+
+  This does not work when an object is "shared" through multiple pointers
+  referencing an identical address and thus pointing to a common glyph
+  or bitmap object.
+-----------------------------------------------------------------------------*/
+
+    PM_WCHAR i;
+
+    for(i = 0; i <= maxglyph; ++i) {
+        if (glyph[i]!=NULL) {
+            free((void *) (glyph[i]->bmap));
+            free(glyph[i]);
+      }
+    }
+}
+
+
+void
+pbm_destroybdffont2_base(struct font2 * const font2P) {
+/*----------------------------------------------------------------------------
+  Free font2 structure, but not the glyph data
+---------------------------------------------------------------------------- */
+
+    free(font2P->selector);
+
+    pm_strfree(font2P->name);
+    pm_strfree(font2P->charset_string);
+    free(font2P->glyph);
+
+    if (font2P->oldfont !=NULL)
+       pbm_freearray(font2P->oldfont, font2P->frows);
+
+    free((void *)font2P);
+
+}
+
+
+
+void
+pbm_destroybdffont2(struct font2 * const font2P) {
+/*----------------------------------------------------------------------------
+  Free font2 structure and glyph data
+
+  Examines the 'load_fn' field to check whether the object is fixed data.
+  Do nothing if 'load_fn' is 'FIXED_DATA'.
+---------------------------------------------------------------------------- */
+
+    if (font2P->load_fn != FIXED_DATA) {
+        destroyGlyphData(font2P->glyph, font2P->maxglyph);
+        pbm_destroybdffont2_base(font2P);
+    }
+}
+
+
+
+void
+pbm_destroybdffont(struct font * const fontP) {
+/*----------------------------------------------------------------------------
+  Free font structure and glyph data.
+
+  For freeing a structure created by pbm_loadbdffont() or pbm_loadpbmfont().
+---------------------------------------------------------------------------- */
+
+    destroyGlyphData(fontP->glyph, PM_FONT_MAXGLYPH);
+
+    if (fontP->oldfont !=NULL)
+       pbm_freearray(fontP->oldfont, fontP->frows);
+
+    free(fontP);
+}
+
+
+
+struct font2 *
+pbm_expandbdffont(const struct font * const fontP) {
+/*----------------------------------------------------------------------------
+  Convert a traditional 'font' structure into an expanded 'font2' structure.
+
+  After calling this function *fontP may be freed, but not the individual
+  glyph data: fontP->glyph[0...255] .
+
+  Using this function on static data is not recommended.  Rather add
+  the extra fields to make a font2 structure.  See file pbmfontdata1.c
+  for an example.
+
+  The returned object is in new malloc'ed storage, in many pieces.
+
+  Destroy with pbm_destroybdffont2() if *fontP is read from a file.
+
+  Destroy with pbm_destroybdffont2_base() if *fontP is static data
+  and you desire to defy the above-stated recommendation.
+
+  The general function for conversion in the opposite direction
+  'font2' => 'font' is font2ToFont() in libpbmfont2.c .  It is currently
+  declared as static.
+ ---------------------------------------------------------------------------*/
+    PM_WCHAR maxglyph, codePoint;
+    unsigned int nCharacters;
+    struct font2 * font2P;
+
+    pbm_createbdffont2_base(&font2P, PM_FONT_MAXGLYPH);
+
+    font2P->maxwidth  = fontP->maxwidth;
+    font2P->maxheight = fontP->maxheight;
+
+    font2P->x = fontP->x;
+    font2P->y = fontP->y;
+
+    /* Hunt for max non-NULL entry in glyph table */
+    for (codePoint = PM_FONT_MAXGLYPH;
+         fontP->glyph[codePoint] == NULL && codePoint > 0; --codePoint)
+        ;
+
+    maxglyph = font2P->maxglyph = codePoint;
+    assert (0 <= maxglyph && maxglyph <= PM_FONT_MAXGLYPH);
+
+    if (maxglyph == 0 && fontP->glyph[0] == NULL)
+        pm_error("no glyphs loaded");
+
+    REALLOCARRAY(font2P->glyph, font2P->maxglyph + 1);
+
+    for (codePoint = 0; codePoint <= maxglyph; ++codePoint) {
+        font2P->glyph[codePoint] = fontP->glyph[codePoint];
+
+        if (font2P->glyph[codePoint] != NULL)
+           ++nCharacters;
+    }
+
+    font2P->oldfont = fontP->oldfont;
+    font2P->fcols = fontP->fcols;
+    font2P->frows = fontP->frows;
+
+    font2P->bit_format = PBM_FORMAT;
+    font2P->total_chars = font2P->chars = nCharacters;
+    font2P->load_fn = CONVERTED_TYPE1_FONT;
+    /* Caller should be overwrite the above to a more descriptive
+       value */
+    return font2P;
+}
+
+
+
diff --git a/lib/libpbmfont1.c b/lib/libpbmfont1.c
new file mode 100644
index 00000000..2d269da7
--- /dev/null
+++ b/lib/libpbmfont1.c
@@ -0,0 +1,359 @@
+/*
+**
+** Routines for loading a PBM sheet font file
+**
+** Copyright (C) 1991 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.
+*/
+
+#include <assert.h>
+#include <string.h>
+
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+
+#include "pbm.h"
+#include "pbmfont.h"
+
+
+/*----------------------------------------------------------------------------
+
+  The routines in this file reads a font bitmap representing
+  the following text:
+
+  (0,0)
+     M ",/^_[`jpqy| M
+
+     /  !"#$%&'()*+ /
+     < ,-./01234567 <
+     > 89:;<=>?@ABC >
+     @ DEFGHIJKLMNO @
+     _ PQRSTUVWXYZ[ _
+     { \]^_`abcdefg {
+     } hijklmnopqrs }
+     ~ tuvwxyz{|}~  ~
+
+     M ",/^_[`jpqy| M
+
+  The bitmap must be cropped exactly to the edges.
+
+  The characters in the border you see are irrelevant except for
+  character size compuations.  The 12 x 8 array in the center is
+  the font.  The top left character there belongs to code point
+  0, and the code points increase in standard reading order, so
+  the bottom right character is code point 127.  You can't define
+  code points < 32 or > 127 with this font format.
+
+  The characters in the top and bottom border rows must include a
+  character with the lowest reach of any in the font (e.g. "y",
+  "_") and one with the highest reach (e.g. '"').  The characters
+  in the left and right border columns must include characters
+  with the rightmost and leftmost reach of any in the font
+  (e.g. "M" for both).
+
+  The border must be separated from the font by one blank text
+  row or text column.
+-----------------------------------------------------------------------------*/
+
+
+static unsigned int const firstCodePoint = 32;
+    /* This is the code point of the first character in a pbmfont font.
+       In ASCII, it is a space.
+    */
+
+static unsigned int const nCharsInFont = 96;
+    /* The number of characters in a pbmfont font.  A pbmfont font defines
+       characters at position 32 (ASCII space) through 127, so that's 96.
+    */
+
+
+static void
+findFirstBlankRow(const bit **   const font,
+                  unsigned int   const fcols,
+                  unsigned int   const frows,
+                  unsigned int * const browP) {
+
+    unsigned int row;
+    bool foundBlank;
+
+    for (row = 0, foundBlank = false; row < frows / 6 && !foundBlank; ++row) {
+        unsigned int col;
+        bit col0Value = font[row][0];
+        bool rowIsBlank;
+        rowIsBlank = true;  /* initial assumption */
+        for (col = 1; col < fcols; ++col)
+            if (font[row][col] != col0Value)
+                rowIsBlank = false;
+
+        if (rowIsBlank) {
+            foundBlank = true;
+            *browP = row;
+        }
+    }
+
+    if (!foundBlank)
+        pm_error("couldn't find blank pixel row in font");
+}
+
+
+
+static void
+findFirstBlankCol(const bit **   const font,
+                  unsigned int   const fcols,
+                  unsigned int   const frows,
+                  unsigned int * const bcolP) {
+
+    unsigned int col;
+    bool foundBlank;
+
+    for (col = 0, foundBlank = false; col < fcols / 6 && !foundBlank; ++col) {
+        unsigned int row;
+        bit row0Value = font[0][col];
+        bool colIsBlank;
+        colIsBlank = true;  /* initial assumption */
+        for (row = 1; row < frows; ++row)
+            if (font[row][col] != row0Value)
+                colIsBlank = false;
+
+        if (colIsBlank) {
+            foundBlank = true;
+            *bcolP = col;
+        }
+    }
+
+    if (!foundBlank)
+        pm_error("couldn't find blank pixel column in font");
+}
+
+
+
+static void
+computeCharacterSize(const bit **   const font,
+                     unsigned int   const fcols,
+                     unsigned int   const frows,
+                     unsigned int * const cellWidthP,
+                     unsigned int * const cellHeightP,
+                     unsigned int * const charWidthP,
+                     unsigned int * const charHeightP) {
+
+    unsigned int firstBlankRow;
+    unsigned int firstBlankCol;
+    unsigned int heightLast11Rows;
+
+    findFirstBlankRow(font, fcols, frows, &firstBlankRow);
+
+    findFirstBlankCol(font, fcols, frows, &firstBlankCol);
+
+    heightLast11Rows = frows - firstBlankRow;
+
+    if (heightLast11Rows % 11 != 0)
+        pm_error("The rows of characters in the font do not appear to "
+                 "be all the same height.  The last 11 rows are %u pixel "
+                 "rows high (from pixel row %u up to %u), "
+                 "which is not a multiple of 11.",
+                 heightLast11Rows, firstBlankRow, frows);
+    else {
+        unsigned int widthLast15Cols;
+
+        *cellHeightP = heightLast11Rows / 11;
+
+        widthLast15Cols = fcols - firstBlankCol;
+
+        if (widthLast15Cols % 15 != 0)
+            pm_error("The columns of characters in the font do not appear to "
+                     "be all the same width.  "
+                     "The last 15 columns are %u pixel "
+                     "columns wide (from pixel col %u up to %u), "
+                     "which is not a multiple of 15.",
+                     widthLast15Cols, firstBlankCol, fcols);
+        else {
+            *cellWidthP = widthLast15Cols / 15;
+
+            *charWidthP = firstBlankCol;
+            *charHeightP = firstBlankRow;
+        }
+    }
+}
+
+
+
+struct font*
+pbm_dissectfont(const bit ** const fontsheet,
+                unsigned int const frows,
+                unsigned int const fcols) {
+/*----------------------------------------------------------------------------
+  Dissect PBM sheet font data, create a font structre,
+  load bitmap data into it.
+
+  Return value is a pointer to the newly created font structure
+
+  The input bitmap data is in memory, in one byte per pixel format.
+
+  The dissection works by finding the first blank row and column;
+  i.e the lower right corner of the "M" in the upper left corner
+  of the matrix.  That gives the height and width of the
+  maximum-sized character, which is not too useful.  But the
+  distance from there to the opposite side is an integral
+  multiple of the cell size, and that's what we need.  Then it's
+  just a matter of filling in all the coordinates.
+
+  Struct font has fields 'oldfont', 'fcols', 'frows' for backward
+  compability.  If there is any need to load data stored in this format
+  feed the above three, in order, as arguments to this function:
+
+    pbm_dissectfont(oldfont, fcols, frows);
+ ----------------------------------------------------------------------------*/
+
+    unsigned int cellWidth, cellHeight;
+        /* Dimensions in pixels of each cell of the font -- that
+           includes the glyph and the white space above and to the
+           right of it.  Each cell is a tile of the font image.  The
+           top character row and left character row don't count --
+           those cells are smaller because they are missing the white
+           space.
+        */
+    unsigned int charWidth, charHeight;
+        /* Maximum dimensions of glyph itself, inside its cell */
+
+    int row, col, ch, r, c, i;
+    struct font * fn;
+
+    computeCharacterSize(fontsheet, fcols, frows,
+                         &cellWidth, &cellHeight, &charWidth, &charHeight);
+
+    /* Now convert to a general font */
+
+    MALLOCVAR(fn);
+    if (fn == NULL)
+        pm_error("out of memory allocating font structure");
+
+    fn->maxwidth  = charWidth;
+    fn->maxheight = charHeight;
+    fn->x = fn->y = 0;
+
+    fn->oldfont = fontsheet;
+    fn->frows = frows;
+    fn->fcols = fcols;
+
+    /* Now fill in the 0,0 coords. */
+    row = cellHeight * 2;
+    col = cellWidth  * 2;
+
+    /* Load individual glyphs */
+    for ( ch = 0; ch < nCharsInFont; ++ch ) {
+        /* Allocate memory separately for each glyph.
+           pbm_loadbdffont2() does this in exactly the same manner.
+         */
+        struct glyph * const glyph =
+             (struct glyph *) malloc (sizeof (struct glyph));
+        char * const bmap = (char*) malloc(fn->maxwidth * fn->maxheight);
+
+        if ( bmap == NULL || glyph == NULL )
+            pm_error( "out of memory allocating glyph data" );
+
+        glyph->width  = fn->maxwidth;
+        glyph->height = fn->maxheight;
+        glyph->x = glyph->y = 0;
+        glyph->xadd = cellWidth;
+
+        for ( r = 0; r < glyph->height; ++r )
+            for ( c = 0; c < glyph->width; ++c )
+                bmap[r * glyph->width + c] = fontsheet[row + r][col + c];
+
+        glyph->bmap = bmap;
+        fn->glyph[firstCodePoint + ch] = glyph;
+
+        col += cellWidth;
+        if ( col >= cellWidth * 14 ) {
+            col = cellWidth * 2;
+            row += cellHeight;
+        }
+    }
+
+    /* Initialize all remaining character positions to "undefined." */
+    for (i = 0; i < firstCodePoint; ++i)
+        fn->glyph[i] = NULL;
+
+    for (i = firstCodePoint + nCharsInFont; i <= PM_FONT_MAXGLYPH; ++i)
+        fn->glyph[i] = NULL;
+
+    return fn;
+}
+
+
+
+
+struct font *
+pbm_loadpbmfont(const char * const filename) {
+/*----------------------------------------------------------------------------
+  Read PBM font sheet data from file 'filename'.
+  Load data into font structure.
+
+  When done with object, free with pbm_destroybdffont().
+-----------------------------------------------------------------------------*/
+
+    FILE * ifP;
+    bit ** fontsheet;
+    int fcols, frows;
+    struct font * retval;
+
+    ifP = pm_openr(filename);
+
+    fontsheet = pbm_readpbm(ifP, &fcols, &frows);
+
+    if ((fcols - 1) / 16 >= pbm_maxfontwidth() ||
+        (frows - 1) / 12 >= pbm_maxfontheight())
+        pm_error("Absurdly large PBM font file: %s", filename);
+    else if (fcols < 31 || frows < 23) {
+        /* Need at least one pixel per character, and this is too small to
+           have that.
+        */
+        pm_error("PBM font file '%s' too small to be a font file: %u x %u.  "
+                 "Minimum sensible size is 31 x 23",
+                 filename, fcols, frows);
+    }
+
+    pm_close(ifP);
+
+    retval = pbm_dissectfont((const bit **)fontsheet, frows, fcols);
+    return (retval);
+
+}
+
+
+
+struct font2 *
+pbm_loadpbmfont2(const char * const filename) {
+/*----------------------------------------------------------------------------
+  Like pbm_loadpbmfont, but return a pointer to struct font2.
+
+  When done with object, free with pbm_destroybdffont2().
+-----------------------------------------------------------------------------*/
+
+    const struct font * const pbmfont = pbm_loadpbmfont(filename);
+    struct font2 * const retval  = pbm_expandbdffont(pbmfont);
+
+    free ((void *)pbmfont);
+
+    /* Overwrite some fields */
+
+    retval->load_fn = LOAD_PBMSHEET;
+    retval->default_char = (PM_WCHAR) ' ';
+    retval->default_char_defined = TRUE;
+    retval->name = pm_strdup("(PBM sheet font has no name)");
+    retval->charset = ISO646_1991_IRV;
+    retval->charset_string = pm_strdup("ASCII");
+    retval->total_chars = retval->chars = nCharsInFont;
+
+    return(retval);
+}
+
+
+
diff --git a/lib/libpbmfont2.c b/lib/libpbmfont2.c
new file mode 100644
index 00000000..b354e91b
--- /dev/null
+++ b/lib/libpbmfont2.c
@@ -0,0 +1,1051 @@
+/*
+**
+** Font routines.
+**
+** Wide character stuff written by Akira Urushibata in 2018 and contributed
+** to the public domain.
+**
+** BDF font code by George Phillips, copyright 1993
+**
+** 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.
+**
+** BDF font specs available from:
+** https://partners.adobe.com/public/developer/en/font/5005.BDF_Spec.pdf
+** Glyph Bitmap Distribution Format (BDF) Specification
+** Version 2.2
+** 22 March 1993
+** Adobe Developer Support
+*/
+
+#include <assert.h>
+#include <string.h>
+
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+
+#include "pbmfont.h"
+#include "pbm.h"
+
+/*----------------------------------------------------------------------------
+  Routines for loading a BDF font file
+-----------------------------------------------------------------------------*/
+
+/* The following are not recognized in individual glyph data; library
+   routines do a pm_error if they see one:
+
+   Vertical writing systems: DWIDTH1, SWIDTH1, VVECTOR, METRICSET,
+   CONTENTVERSION.
+
+   The following is not recognized and is thus ignored at the global level:
+   DWIDTH
+*/
+
+
+#define MAXBDFLINE 1024
+
+/* Official Adobe document says max length of string is 65535 characters.
+   However the value 1024 is sufficient for practical uses.
+*/
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   This is an object for reading lines of a font file.  It reads and tokenizes
+   them into words.
+-----------------------------------------------------------------------------*/
+    FILE * ifP;
+
+    char line[MAXBDFLINE+1];
+        /* This is the storage space for the words of the line.  The
+           words go in here, one after another, separated by NULs.
+
+           It also functions as a work area for readline_read().
+        */
+    const char * arg[7];
+        /* These are the words; each entry is a pointer into line[] (above) */
+
+    unsigned int wordCt;
+} Readline;
+
+
+
+static void
+readline_init(Readline * const readlineP,
+              FILE *     const ifP) {
+
+    readlineP->ifP = ifP;
+
+    readlineP->arg[0] = NULL;
+    readlineP->wordCt = 0;
+}
+
+
+
+static void
+tokenize(char *         const s,
+         const char **  const words,
+         unsigned int   const wordsSz,
+         unsigned int * const wordCtP) {
+/*----------------------------------------------------------------------------
+   Chop up 's' into words by changing space characters to NUL.  Return as
+   'words' an array of pointers to the beginnings of those words in 's'.
+   Terminate the words[] list with a null pointer.
+
+   'wordsSz' is the number of elements of space in 'words'.  If there are more
+   words in 's' than will fit in that space (including the terminating null
+   pointer), ignore the excess on the right.
+
+   '*wordCtP' is the number elements actually found.
+-----------------------------------------------------------------------------*/
+    unsigned int n;  /* Number of words in words[] so far */
+    char * p;
+
+    p = &s[0];
+    n = 0;
+
+    while (*p) {
+        if (!ISGRAPH(*p)) {
+            if(!ISSPACE(*p)) {
+              /* Control chars excluding 09 - 0d (space), 80-ff */
+            pm_message("Warning: non-ASCII character '%x' in "
+                       "BDF font file", *p);
+            }
+            *p++ = '\0';
+        }
+        else {
+            words[n++] = p;
+            if (n >= wordsSz - 1)
+                break;
+            while (*p && ISGRAPH(*p))
+                ++p;
+        }
+    }
+    assert(n <= wordsSz - 1);
+    words[n] = NULL;
+    *wordCtP = n;
+}
+
+
+
+static void
+readline_read(Readline * const readlineP,
+              bool *     const eofP) {
+/*----------------------------------------------------------------------------
+   Read a nonblank line from the file.  Make its contents available
+   as readlineP->arg[].
+
+   Return *eofP == true iff there is no nonblank line before EOF or we
+   are unable to read the file.
+-----------------------------------------------------------------------------*/
+    bool gotLine;
+    bool error;
+
+    for (gotLine = false, error = false; !gotLine && !error; ) {
+        char * rc;
+
+        rc = fgets(readlineP->line, MAXBDFLINE+1, readlineP->ifP);
+        if (rc == NULL)
+            error = true;
+        else {
+            tokenize(readlineP->line, readlineP->arg,
+                     ARRAY_SIZE(readlineP->arg), &readlineP->wordCt);
+            if (readlineP->arg[0] != NULL)
+                gotLine = true;
+        }
+    }
+    *eofP = error;
+}
+
+
+
+static void
+parseBitmapRow(const char *    const hex,
+               unsigned int    const glyphWidth,
+               unsigned char * const bmap,
+               unsigned int    const origBmapIndex,
+               unsigned int *  const newBmapIndexP,
+               const char **   const errorP) {
+/*----------------------------------------------------------------------------
+   Parse one row of the bitmap for a glyph, from the hexadecimal string
+   for that row in the font file, 'hex'.  The glyph is 'glyphWidth'
+   pixels wide.
+
+   We place our result in 'bmap' at *bmapIndexP and advanced *bmapIndexP.
+-----------------------------------------------------------------------------*/
+    unsigned int bmapIndex;
+    int i;  /* dot counter */
+    const char * p;
+
+    bmapIndex = origBmapIndex;
+
+    for (i = glyphWidth, p = &hex[0], *errorP = NULL;
+         i > 0 && !*errorP;
+         i -= 4) {
+
+        if (*p == '\0')
+            pm_asprintf(errorP, "Not enough hexadecimal digits for glyph "
+                        "of width %u in '%s'",
+                        glyphWidth, hex);
+        else {
+            char const hdig = *p++;
+            unsigned int hdigValue;
+
+            if (hdig >= '0' && hdig <= '9')
+                hdigValue = hdig - '0';
+            else if (hdig >= 'a' && hdig <= 'f')
+                hdigValue = 10 + (hdig - 'a');
+            else if (hdig >= 'A' && hdig <= 'F')
+                hdigValue = 10 + (hdig - 'A');
+            else
+                pm_asprintf(errorP,
+                            "Invalid hex digit x%02x (%c) in bitmap data '%s'",
+                            (unsigned int)(unsigned char)hdig,
+                            isprint(hdig) ? hdig : '.',
+                            hex);
+
+            if (!*errorP) {
+                if (i > 0)
+                    bmap[bmapIndex++] = hdigValue & 0x8 ? 1 : 0;
+                if (i > 1)
+                    bmap[bmapIndex++] = hdigValue & 0x4 ? 1 : 0;
+                if (i > 2)
+                    bmap[bmapIndex++] = hdigValue & 0x2 ? 1 : 0;
+                if (i > 3)
+                    bmap[bmapIndex++] = hdigValue & 0x1 ? 1 : 0;
+            }
+        }
+    }
+    *newBmapIndexP = bmapIndex;
+}
+
+
+
+static void
+readBitmap(Readline *      const readlineP,
+           unsigned int    const glyphWidth,
+           unsigned int    const glyphHeight,
+           const char *    const charName,
+           unsigned char * const bmap) {
+
+    int n;
+    unsigned int bmapIndex;
+
+    bmapIndex = 0;
+
+    for (n = glyphHeight; n > 0; --n) {
+        bool eof;
+        const char * error;
+
+        readline_read(readlineP, &eof);
+
+        if (eof)
+            pm_error("End of file in bitmap for character '%s' in BDF "
+                     "font file.", charName);
+
+        if (!readlineP->arg[0])
+            pm_error("A line that is supposed to contain bitmap data, "
+                     "in hexadecimal, for character '%s' is empty", charName);
+
+        parseBitmapRow(readlineP->arg[0], glyphWidth, bmap, bmapIndex,
+                       &bmapIndex, &error);
+
+        if (error) {
+            pm_error("Error in line %d of bitmap for character '%s': %s",
+                     n, charName, error);
+            pm_strfree(error);
+        }
+    }
+}
+
+
+
+static void
+createBmap(unsigned int  const glyphWidth,
+           unsigned int  const glyphHeight,
+           Readline *    const readlineP,
+           const char *  const charName,
+           const char ** const bmapP) {
+
+    unsigned char * bmap;
+    bool eof;
+
+    if (glyphWidth > 0 && UINT_MAX / glyphWidth < glyphHeight)
+        pm_error("Ridiculously large glyph");
+
+    MALLOCARRAY(bmap, glyphWidth * glyphHeight);
+
+    if (!bmap)
+        pm_error("no memory for font glyph byte map");
+
+    readline_read(readlineP, &eof);
+    if (eof)
+        pm_error("End of file encountered reading font glyph byte map from "
+                 "BDF font file.");
+
+    if (streq(readlineP->arg[0], "ATTRIBUTES")) {
+        /* ATTRIBUTES is defined in Glyph Bitmap Distribution Format (BDF)
+           Specification Version 2.1, but not in Version 2.2.
+        */
+        bool eof;
+        readline_read(readlineP, &eof);
+        if (eof)
+            pm_error("End of file encountered after ATTRIBUTES in BDF "
+                     "font file.");
+    }
+    if (!streq(readlineP->arg[0], "BITMAP"))
+        pm_error("'%s' found where BITMAP expected in definition of "
+                 "character '%s' in BDF font file.",
+                 readlineP->arg[0], charName);
+
+    assert(streq(readlineP->arg[0], "BITMAP"));
+
+    readBitmap(readlineP, glyphWidth, glyphHeight, charName, bmap);
+
+    *bmapP = (char *)bmap;
+}
+
+
+
+static void
+validateWordCount(Readline *    const readlineP,
+                  unsigned int  const nWords) {
+
+    if( readlineP->wordCt != nWords )
+        pm_error("Wrong number of arguments in '%s' line in BDF font file",
+                 readlineP->arg[0]);
+
+    /* We assume that the first word in line 'arg[0]' is a valid string */
+
+}
+
+
+static void
+readExpectedStatement(Readline *    const readlineP,
+                      const char *  const expected,
+                      unsigned int  const nWords) {
+/*----------------------------------------------------------------------------
+  Have the readline object *readlineP read the next line from the file, but
+  expect it to be a line of type 'expected' (i.e. the verb token at the
+  beginning of the line is that, e.g. "STARTFONT").  Check for the number
+  of words: 'nWords'.  If either condition is not met, fail the program.
+-----------------------------------------------------------------------------*/
+
+    bool eof;
+
+    readline_read(readlineP, &eof);
+
+    if (eof)
+        pm_error("EOF in BDF font file where '%s' expected", expected);
+    else if (!streq(readlineP->arg[0], expected))
+        pm_error("Statement of type '%s' where '%s' expected in BDF font file",
+                 readlineP->arg[0], expected);
+
+    validateWordCount(readlineP, nWords);
+
+}
+
+
+
+static void
+skipCharacter(Readline * const readlineP) {
+/*----------------------------------------------------------------------------
+  In the BDF font file being read by readline object *readlineP, skip through
+  the end of the character we are presently in.
+-----------------------------------------------------------------------------*/
+    bool endChar;
+
+    endChar = FALSE;
+
+    while (!endChar) {
+        bool eof;
+        readline_read(readlineP, &eof);
+        if (eof)
+            pm_error("End of file in the middle of a character (before "
+                     "ENDCHAR) in BDF font file.");
+        endChar = streq(readlineP->arg[0], "ENDCHAR");
+    }
+}
+
+
+
+static int
+wordToInt(const char * const word) {
+
+    unsigned int absValue;
+
+    int retval;
+
+    const char * error;
+    const int sign = (word[0] == '-') ? -1 : +1;
+    const char * const absString = (sign == -1) ? &word[1] : word;
+    /* No leading spaces allowed in 'word' */
+
+    if (!ISDIGIT(absString[0]))
+      error = "Non-digit character encountered";
+
+    else {
+        pm_string_to_uint(absString, &absValue, &error);
+        if (error == NULL && absValue > INT_MAX)
+            error = "Out of range";
+    }
+
+    if (error != NULL)
+        pm_error ("Error reading numerical argument in "
+                  "BDF font file: %s %s %s", error, word, absString);
+
+    retval = sign * absValue;
+    assert (INT_MIN < retval && retval < INT_MAX);
+
+    return retval;
+}
+
+
+
+static void
+interpEncoding(const char **  const arg,
+               unsigned int * const codepointP,
+               bool *         const badCodepointP,
+               PM_WCHAR       const maxmaxglyph) {
+/*----------------------------------------------------------------------------
+   With arg[] being the ENCODING statement from the font, return as
+   *codepointP the codepoint that it indicates (code point is the character
+   code, e.g. in ASCII, 48 is '0').
+
+   But if the statement doesn't give an acceptable codepoint return
+   *badCodepointP == TRUE.
+
+   'maxmaxglyph' is the maximum codepoint in the font.
+-----------------------------------------------------------------------------*/
+    bool gotCodepoint;
+    bool badCodepoint;
+    unsigned int codepoint;
+
+    if (wordToInt(arg[1]) >= 0) {
+        codepoint = wordToInt(arg[1]);
+        gotCodepoint = true;
+    } else {
+      if (wordToInt(arg[1]) == -1 && arg[2] != NULL) {
+            codepoint = wordToInt(arg[2]);
+            gotCodepoint = true;
+        } else
+            gotCodepoint = false;
+    }
+    if (gotCodepoint) {
+        if (codepoint > maxmaxglyph)
+            badCodepoint = true;
+        else
+            badCodepoint = false;
+    } else
+        badCodepoint = true;
+
+    *badCodepointP = badCodepoint;
+    *codepointP    = codepoint;
+}
+
+
+
+static void
+readEncoding(Readline *     const readlineP,
+             unsigned int * const codepointP,
+             bool *         const badCodepointP,
+             PM_WCHAR       const maxmaxglyph) {
+
+    bool eof;
+    const char * expected = "ENCODING";
+
+    readline_read(readlineP, &eof);
+
+    if (eof)
+        pm_error("EOF in BDF font file where '%s' expected", expected);
+    else if (!streq(readlineP->arg[0], expected))
+        pm_error("Statement of type '%s' where '%s' expected in BDF font file",
+                 readlineP->arg[0], expected);
+    else if(readlineP->wordCt != 2 &&  readlineP->wordCt != 3)
+        pm_error("Wrong number of arguments in '%s' line in BDF font file",
+                 readlineP->arg[0]);
+
+    interpEncoding(readlineP->arg, codepointP, badCodepointP, maxmaxglyph);
+}
+
+
+
+static void
+validateFontLimits(const struct font2 * const font2P) {
+
+    assert(pbm_maxfontheight() > 0 && pbm_maxfontwidth() > 0);
+
+    if (font2P->maxwidth  <= 0 ||
+        font2P->maxheight <= 0 ||
+        font2P->maxwidth  > pbm_maxfontwidth()  ||
+        font2P->maxheight > pbm_maxfontheight() ||
+        -font2P->x + 1 > font2P->maxwidth ||
+        -font2P->y + 1 > font2P->maxheight ||
+        font2P->x > font2P->maxwidth  ||
+        font2P->y > font2P->maxheight ||
+        font2P->x + font2P->maxwidth  > pbm_maxfontwidth() ||
+        font2P->y + font2P->maxheight > pbm_maxfontheight()
+        ) {
+
+        pm_error("Global font metric(s) out of bounds.");
+    }
+
+    if (font2P->maxglyph > PM_FONT2_MAXGLYPH)
+        pm_error("Internal error.  Glyph table too large: %u glyphs; "
+                 "Maximum possible in Netpbm is %u",
+                 (unsigned int) font2P->maxglyph, PM_FONT2_MAXGLYPH);
+}
+
+
+
+static void
+validateGlyphLimits(const struct font2 * const font2P,
+                    const struct glyph * const glyphP,
+                    const char *         const charName) {
+
+    /* Some BDF files code space with zero width and height,
+       no bitmap data and just the xadd value.
+       We allow zero width and height, iff both are zero.
+
+       Some BDF files have individual glyphs with a BBX value which
+       exceeds the global maximum stated by FONTBOUNDINGBOX.
+       Abort with error when this is encountered.
+       It seems some programs including emacs and bdftopcf tolerate
+       this violation.
+    */
+
+    if (((glyphP->width == 0 || glyphP->height == 0) &&
+         !(glyphP->width == 0 && glyphP->height == 0)) ||
+        glyphP->width  > font2P->maxwidth  ||
+        glyphP->height > font2P->maxheight ||
+        glyphP->x < font2P->x ||
+        glyphP->y < font2P->y ||
+        glyphP->x + (int) glyphP->width  > font2P->x + font2P->maxwidth  ||
+        glyphP->y + (int) glyphP->height > font2P->y + font2P->maxheight ||
+        glyphP->xadd > pbm_maxfontwidth() ||
+        glyphP->xadd + MAX(glyphP->x,0) + (int) glyphP->width >
+        pbm_maxfontwidth()
+        ) {
+
+        pm_error("Font metric(s) for char '%s' out of bounds.\n", charName);
+    }
+}
+
+
+
+static void
+processChars(Readline *     const readlineP,
+             struct font2 * const font2P) {
+/*----------------------------------------------------------------------------
+   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
+   contents to *font2P.
+-----------------------------------------------------------------------------*/
+    unsigned int const nCharacters = wordToInt(readlineP->arg[1]);
+
+    unsigned int nCharsDone;
+    unsigned int nCharsValid;
+
+    for (nCharsDone = 0, nCharsValid = 0;
+         nCharsDone < nCharacters; ) {
+
+        bool eof;
+
+        readline_read(readlineP, &eof);
+        if (eof)
+            pm_error("End of file after CHARS reading BDF font file");
+
+        if (streq(readlineP->arg[0], "COMMENT")) {
+            /* ignore */
+        } else if (!streq(readlineP->arg[0], "STARTCHAR"))
+            pm_error("%s detected where \'STARTCHAR\' expected "
+                     "in BDF font file", readlineP->arg[0] );
+        else {
+            const char * charName;
+
+            struct glyph * glyphP;
+            unsigned int codepoint;
+            bool badCodepoint;
+
+            if (readlineP->wordCt < 2)
+                pm_error("Wrong number of arguments in STARTCHAR line "
+                         "in BDF font file");
+            /* Character name may contain spaces: there may be more than
+               three words in the line.
+             */
+            charName = pm_strdup(readlineP->arg[1]);
+
+            assert(streq(readlineP->arg[0], "STARTCHAR"));
+
+            MALLOCVAR(glyphP);
+
+            if (glyphP == NULL)
+                pm_error("no memory for font glyph for '%s' character",
+                         charName);
+
+            readEncoding(readlineP, &codepoint, &badCodepoint,
+                         font2P->maxmaxglyph);
+
+            if (badCodepoint)
+                skipCharacter(readlineP);
+            else {
+                if (codepoint < font2P->maxglyph) {
+                    if (font2P->glyph[codepoint] != NULL)
+                        pm_error("Multiple definition of code point %u "
+                                 "in BDF font file", (unsigned int) codepoint);
+                    else
+                        pm_message("Reverse order detected in BDF file. "
+                                   "Code point %u defined after %u",
+                                    (unsigned int) codepoint,
+                                    (unsigned int) font2P->maxglyph);
+                } else {
+                    /* Initialize all characters in the gap to nonexistent */
+                    unsigned int i;
+                    unsigned int const oldMaxglyph = font2P->maxglyph;
+                    unsigned int const newMaxglyph = codepoint;
+
+                    for (i = oldMaxglyph + 1; i < newMaxglyph; ++i)
+                        font2P->glyph[i] = NULL;
+
+                    font2P->maxglyph = newMaxglyph;
+                    }
+
+                readExpectedStatement(readlineP, "SWIDTH", 3);
+
+                readExpectedStatement(readlineP, "DWIDTH", 3);
+                glyphP->xadd = wordToInt(readlineP->arg[1]);
+
+                readExpectedStatement(readlineP, "BBX", 5);
+                glyphP->width  = wordToInt(readlineP->arg[1]);
+                glyphP->height = wordToInt(readlineP->arg[2]);
+                glyphP->x      = wordToInt(readlineP->arg[3]);
+                glyphP->y      = wordToInt(readlineP->arg[4]);
+
+                validateGlyphLimits(font2P, glyphP, charName);
+
+                createBmap(glyphP->width, glyphP->height, readlineP, charName,
+                           &glyphP->bmap);
+
+                readExpectedStatement(readlineP, "ENDCHAR", 1);
+
+                assert(codepoint <= font2P->maxmaxglyph);
+                /* Ensured by readEncoding() */
+
+                font2P->glyph[codepoint] = glyphP;
+                pm_strfree(charName);
+
+                ++nCharsValid;
+            }
+            ++nCharsDone;
+        }
+    }
+    font2P->chars = nCharsValid;
+    font2P->total_chars = nCharacters;
+}
+
+
+
+static void
+processBdfFontNameLine(Readline     * const readlineP,
+                       struct font2 * const font2P) {
+
+    if (font2P->name)  /* We've already processed a FONT line */
+        pm_error("Multiple FONT lines in BDF font file");
+    else {
+        char * buffer;
+
+        MALLOCARRAY(buffer, MAXBDFLINE+1);
+
+        if (!buffer)
+            pm_error("Failed to get memory for %u-character font name buffer",
+                     MAXBDFLINE+1);
+
+        if (readlineP->wordCt == 1)
+            strcpy(buffer, "(no name)");
+
+        else {
+            unsigned int tokenCt;
+
+            buffer[0] ='\0';
+
+            for (tokenCt=1;
+                 tokenCt < ARRAY_SIZE(readlineP->arg) &&
+                     readlineP->arg[tokenCt] != NULL; ++tokenCt) {
+                strcat(buffer, " ");
+                strcat(buffer, readlineP->arg[tokenCt]);
+            }
+        }
+        font2P->name = buffer;
+    }
+}
+
+
+
+static void
+loadCharsetString(const char *  const registry,
+                  const char *  const encoding,
+                  const char ** const charsetStringP) {
+
+    char * dest;
+    unsigned int inCt, outCt;
+
+    dest = malloc(strlen(registry) + strlen(encoding) + 1);
+
+    if (!dest)
+        pm_error("no memory to load CHARSET_REGISTRY and CHARSET_ENCODING "
+               "from BDF file");
+
+    for (inCt = outCt = 0; inCt < strlen(registry); ++inCt) {
+        char const c = registry[inCt];
+        if (isgraph(c) && c != '"')
+            dest[outCt++] = c;
+    }
+    dest[outCt++] = '-';
+
+    for (inCt = 0; inCt < strlen(encoding); ++inCt) {
+        char const c = encoding[inCt];
+        if (isgraph(c) && c != '"')
+            dest[outCt++] = c;
+    }
+
+    dest[outCt] = '\0';
+    *charsetStringP = dest;
+}
+
+
+
+
+static unsigned int const maxTokenLen = 60;
+
+
+
+static void
+doCharsetRegistry(Readline *    const readlineP,
+                  bool *        const gotRegistryP,
+                  const char ** const registryP) {
+
+    if (*gotRegistryP)
+        pm_error("Multiple CHARSET_REGISTRY lines in BDF font file");
+    else if (readlineP->arg[2] != NULL)
+        pm_message("CHARSET_REGISTRY in BDF font file is not "
+                   "a single word.  Ignoring extra element(s) %s ...",
+                   readlineP->arg[2]);
+    else if (strlen(readlineP->arg[1]) > maxTokenLen)
+        pm_message("CHARSET_REGISTRY in BDF font file is too long. "
+                   "Truncating");
+
+    *registryP = pm_strdup(readlineP->arg[1]);
+    *gotRegistryP = true;
+}
+
+
+
+static void
+doCharsetEncoding(Readline *    const readlineP,
+                  bool *        const gotEncodingP,
+                  const char ** const encodingP) {
+
+    if (*gotEncodingP)
+        pm_error("Multiple CHARSET_ENCODING lines in BDF font file");
+    else if (readlineP->arg[2] != NULL)
+        pm_message("CHARSET_ENCODING in BDF font file is not "
+                   "a single word.  Ignoring extra element(s) %s ...",
+                   readlineP->arg[2]);
+    else if (strlen(readlineP->arg[1]) > maxTokenLen)
+        pm_message("CHARSET_ENCODING in BDF font file is too long. "
+                   "Truncating");
+
+    *encodingP = pm_strdup(readlineP->arg[1]);
+    *gotEncodingP = true;
+}
+
+
+
+static void
+doDefaultChar(Readline * const readlineP,
+              bool *     const gotDefaultCharP,
+              PM_WCHAR * const defaultCharP) {
+
+    if (*gotDefaultCharP)
+        pm_error("Multiple DEFAULT_CHAR lines in BDF font file");
+    else if (readlineP->arg[1] == NULL)
+        pm_error("Malformed DEFAULT_CHAR line in BDF font file");
+    else {
+        *defaultCharP = (PM_WCHAR) wordToInt(readlineP->arg[1]);
+        *gotDefaultCharP = true;
+    }
+}
+
+
+
+static void
+processBdfPropertyLine(Readline     * const readlineP,
+                       struct font2 * const font2P) {
+
+    bool gotRegistry;
+    const char * registry;
+    bool gotEncoding;
+    const char * encoding;
+    bool gotDefaultChar;
+    PM_WCHAR defaultChar;
+    unsigned int propCt;
+    unsigned int commentCt;
+    unsigned int propTotal;
+
+    validateWordCount(readlineP, 2);   /* STARTPROPERTIES n */
+
+    propTotal = wordToInt(readlineP->arg[1]);
+
+    gotRegistry    = false;  /* initial value */
+    gotEncoding    = false;  /* initial value */
+    gotDefaultChar = false;  /* initial value */
+
+    propCt    = 0;  /* initial value */
+    commentCt = 0;  /* initial value */
+
+    do {
+        bool eof;
+
+        readline_read(readlineP, &eof);
+        if (eof)
+            pm_error("End of file after STARTPROPERTIES in BDF font file");
+        else if (streq(readlineP->arg[0], "CHARSET_REGISTRY") &&
+                 readlineP->arg[1] != NULL) {
+            doCharsetRegistry(readlineP, &gotRegistry, &registry);
+        } else if (streq(readlineP->arg[0], "CHARSET_ENCODING") &&
+                   readlineP->arg[1] != NULL) {
+            doCharsetEncoding(readlineP, &gotEncoding, &encoding);
+        } else if (streq(readlineP->arg[0], "DEFAULT_CHAR")) {
+            doDefaultChar(readlineP, &gotDefaultChar, &defaultChar);
+        } else if (streq(readlineP->arg[0], "COMMENT")) {
+            ++commentCt;
+        }
+        ++propCt;
+
+    } while (!streq(readlineP->arg[0], "ENDPROPERTIES"));
+
+    --propCt; /* Subtract one for ENDPROPERTIES line */
+
+    if (propCt != propTotal && propCt - commentCt != propTotal)
+      /* Some BDF files have COMMENTs in the property section and leave
+         them out of the count.
+         Others just give a wrong count.
+       */
+        pm_message ("Note: wrong number of property lines in BDF font file. "
+                    "STARTPROPERTIES line says %u, actual count: %u. "
+                    "Proceeding.",
+                    propTotal, propCt);
+
+
+    if (gotRegistry && gotEncoding)
+        loadCharsetString(registry, encoding, &font2P->charset_string);
+    else if (gotRegistry != gotEncoding) {
+        pm_message ("CHARSET_%s absent or incomplete in BDF font file. "
+                    "Ignoring CHARSET_%s.",
+                    gotEncoding ? "REGISTRY" : "ENCODING",
+                    gotEncoding ? "ENCODING" : "REGISTRY");
+    }
+    if (gotRegistry)
+        pm_strfree(registry);
+    if (gotEncoding)
+        pm_strfree(encoding);
+
+    if (gotDefaultChar) {
+        font2P->default_char         = defaultChar;
+        font2P->default_char_defined = true;
+    }
+
+}
+
+
+static void
+processBdfFontLine(Readline     * const readlineP,
+                   struct font2 * const font2P,
+                   bool         * const endOfFontP) {
+/*----------------------------------------------------------------------------
+   Process a nonblank line just read from a BDF font file.
+
+   This processing may involve reading more lines.
+-----------------------------------------------------------------------------*/
+    *endOfFontP = FALSE;  /* initial assumption */
+
+    assert(readlineP->arg[0] != NULL);  /* Entry condition */
+
+    if (streq(readlineP->arg[0], "FONT")) {
+        processBdfFontNameLine(readlineP, font2P);
+    } else if (streq(readlineP->arg[0], "COMMENT")) {
+        /* ignore */
+    } else if (streq(readlineP->arg[0], "SIZE")) {
+        /* ignore */
+    } else if (streq(readlineP->arg[0], "STARTPROPERTIES")) {
+      if (font2P->maxwidth == 0)
+      pm_error("Encountered STARTROPERTIES before FONTBOUNDINGBOX "
+               "in BDF font file");
+      else
+        processBdfPropertyLine(readlineP, font2P);
+    } else if (streq(readlineP->arg[0], "FONTBOUNDINGBOX")) {
+        validateWordCount(readlineP,5);
+
+        font2P->maxwidth  = wordToInt(readlineP->arg[1]);
+        font2P->maxheight = wordToInt(readlineP->arg[2]);
+        font2P->x = wordToInt(readlineP->arg[3]);
+        font2P->y = wordToInt(readlineP->arg[4]);
+        validateFontLimits(font2P);
+    } else if (streq(readlineP->arg[0], "ENDFONT")) {
+        *endOfFontP = true;
+    } else if (streq(readlineP->arg[0], "CHARS")) {
+      if (font2P->maxwidth == 0)
+      pm_error("Encountered CHARS before FONTBOUNDINGBOX "
+                   "in BDF font file");
+      else {
+        validateWordCount(readlineP, 2);  /* CHARS n */
+        processChars(readlineP, font2P);
+      }
+    } else {
+        /* ignore */
+    }
+
+}
+
+
+
+struct font2 *
+pbm_loadbdffont2(const char * const filename,
+                 PM_WCHAR     const maxmaxglyph) {
+/*----------------------------------------------------------------------------
+   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.
+
+   Codepoints up to maxmaxglyph inclusive are valid in the file.
+
+   The returned object is in new malloc'ed storage, in many pieces.
+   When done with, destroy with pbm_destroybdffont2().
+-----------------------------------------------------------------------------*/
+
+    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)",
+                 filename, errno, strerror(errno));
+
+    readline_init(&readline, ifP);
+
+    pbm_createbdffont2_base(&font2P, maxmaxglyph);
+
+    font2P->maxglyph = 0;
+        /* Initial value.  Increases as new characters are loaded */
+    font2P->glyph[0] = NULL;
+        /* Initial value.  Overwrite later if codepoint 0 is defined. */
+
+    font2P->maxmaxglyph = maxmaxglyph;
+
+    /* Initialize some values - to be overwritten if actual values are
+       stated in BDF file */
+    font2P->maxwidth = font2P->maxheight = font2P->x = font2P->y = 0;
+    font2P->name = font2P->charset_string = NULL;
+    font2P->chars = font2P->total_chars = 0;
+    font2P->default_char = 0;
+    font2P->default_char_defined = FALSE;
+
+    readExpectedStatement(&readline, "STARTFONT", 2);
+
+    endOfFont = FALSE;
+
+    while (!endOfFont) {
+        bool eof;
+        readline_read(&readline, &eof);
+        if (eof)
+            pm_error("End of file before ENDFONT statement in BDF font file");
+
+        processBdfFontLine(&readline, font2P, &endOfFont);
+    }
+    fclose(ifP);
+
+    if(font2P->chars == 0)
+        pm_error("No glyphs found in BDF font file "
+                 "in codepoint range 0 - %u", (unsigned int) maxmaxglyph);
+
+    REALLOCARRAY(font2P->glyph, font2P->maxglyph + 1);
+
+    font2P->bit_format = PBM_FORMAT;
+    font2P->load_fn = LOAD_BDFFILE;
+    font2P->charset = ENCODING_UNKNOWN;
+    font2P->oldfont = NULL;  /* Legacy field */
+    font2P->fcols = font2P->frows = 0;  /* Legacy fields */
+
+    return font2P;
+}
+
+
+static struct font *
+font2ToFont(const struct font2 * const font2P) {
+            struct font  * fontP;
+            unsigned int   codePoint;
+
+    MALLOCVAR(fontP);
+    if (fontP == NULL)
+        pm_error("no memory for font");
+
+    fontP->maxwidth  = font2P->maxwidth;
+    fontP->maxheight = font2P->maxheight;
+
+    fontP->x = font2P->x;
+    fontP->y = font2P->y;
+
+    for (codePoint = 0; codePoint <= font2P->maxglyph; ++codePoint)
+        fontP->glyph[codePoint] = font2P->glyph[codePoint];
+
+    /* font2P->maxglyph is typically 255 (PM_FONT_MAXGLYPH) or larger.
+       But in some rare cases it is smaller.
+       If an ASCII-only font is read, it will be 126 or 127.
+
+       Set remaining codepoints up to PM_FONT_MAXGLYPH, if any, to NULL
+    */
+
+    for ( ; codePoint <= PM_FONT_MAXGLYPH; ++codePoint)
+        fontP->glyph[codePoint] = NULL;
+
+    /* Give values to legacy fields */
+    fontP->oldfont = font2P->oldfont;
+    fontP->fcols = font2P->fcols;
+    fontP->frows = font2P->frows;
+
+    return fontP;
+}
+
+
+
+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.
+   Destroy with pbm_destroybdffont().
+-----------------------------------------------------------------------------*/
+    struct font  * fontP;
+    struct font2 * const font2P = pbm_loadbdffont2(filename, PM_FONT_MAXGLYPH);
+
+    fontP = font2ToFont(font2P);
+
+    /* Free the base structure which was created by pbm_loadbdffont2() */
+    pbm_destroybdffont2_base(font2P);
+
+    return fontP;
+}
+
+
+
diff --git a/lib/libpbmfontdump.c b/lib/libpbmfontdump.c
new file mode 100644
index 00000000..f0c950f7
--- /dev/null
+++ b/lib/libpbmfontdump.c
@@ -0,0 +1,96 @@
+/*
+**
+** Font routines.
+**
+** BDF font code Copyright 1993 by George Phillips.
+**
+** Copyright (C) 1991 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.
+**
+** BDF font specs available from:
+** https://partners.adobe.com/public/developer/en/font/5005.BDF_Spec.pdf
+** Glyph Bitmap Distribution Format (BDF) Specification
+** Version 2.2
+** 22 March 1993
+** Adobe Developer Support
+*/
+
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+
+#include "pbmfont.h"
+#include "pbm.h"
+
+
+void
+pbm_dumpfont(struct font * const fontP,
+             FILE *        const ofP) {
+/*----------------------------------------------------------------------------
+  Dump out font as C source code.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    unsigned int ng;
+
+    if (fontP->oldfont)
+        pm_message("Netpbm no longer has the capability to generate "
+                   "a font in long hexadecimal data format");
+
+    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 < PM_FONT_MAXGLYPH + 1; ++i) {
+        struct glyph * const glyphP = fontP->glyph[i];
+        if (glyphP) {
+            unsigned int j;
+            printf(" { %d, %d, %d, %d, %d, \"", glyphP->width, glyphP->height,
+                   glyphP->x, glyphP->y, glyphP->xadd);
+
+            for (j = 0; j < glyphP->width * glyphP->height; ++j) {
+                if (glyphP->bmap[j])
+                    printf("\\1");
+                else
+                    printf("\\0");
+            }
+            --ng;
+            printf("\" }%s\n", ng ? "," : "");
+        }
+    }
+    printf("};\n");
+
+    printf("struct font XXX_font = { %d, %d, %d, %d, {\n",
+           fontP->maxwidth, fontP->maxheight, fontP->x, fontP->y);
+
+    {
+        unsigned int i;
+
+        for (i = 0; i < PM_FONT_MAXGLYPH + 1; ++i) {
+            if (fontP->glyph[i])
+                printf(" _g + %d", ng++);
+            else
+                printf(" NULL");
+
+            if (i != PM_FONT_MAXGLYPH) printf(",");
+            printf("\n");
+        }
+    }
+
+    printf(" }\n};\n");
+}
+
+
+
diff --git a/lib/libpgm.h b/lib/libpgm.h
index 7523faaf..eb292c80 100644
--- a/lib/libpgm.h
+++ b/lib/libpgm.h
@@ -7,9 +7,9 @@
 #include "pgm.h"
 
 void
-pgm_readpgminitrest(FILE * const file, 
-                    int *  const colsP, 
-                    int *  const rowsP, 
+pgm_readpgminitrest(FILE * const file,
+                    int *  const colsP,
+                    int *  const rowsP,
                     gray * const maxvalP);
 
 #endif
diff --git a/lib/libpm.c b/lib/libpm.c
index 4374bbe3..47a2f498 100644
--- a/lib/libpm.c
+++ b/lib/libpm.c
@@ -8,7 +8,7 @@
   Netpbm library subroutines.
 **************************************************************************/
 
-#define _BSD_SOURCE          /* Make sure strdup is in string.h */
+#define _DEFAULT_SOURCE      /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500    /* Make sure ftello, fseeko are defined */
 
 #include "netpbm/pm_config.h"
@@ -44,7 +44,7 @@ const char * pm_progname;
 int pm_plain_output;
     /* Boolean: programs should produce output in plain format */
 
-static bool pm_showmessages;  
+static bool pm_showmessages;
     /* Programs should display informational messages (because the user didn't
        specify the --quiet option).
     */
@@ -67,7 +67,7 @@ static pm_usererrormsgfn * userErrorMsgFn = NULL;
     */
 
 static pm_usermessagefn * userMessageFn = NULL;
-    /* A function to call to issue an error message.
+    /* A function to call to issue a non-error message.
 
        NULL means use the library default: print to Standard Error
     */
@@ -248,7 +248,7 @@ pm_errormsg(const char format[], ...) {
     va_start(args, format);
 
     pm_vasprintf(&msg, format, args);
-    
+
     errormsg(msg);
 
     pm_strfree(msg);
@@ -266,7 +266,7 @@ pm_error(const char format[], ...) {
     va_start(args, format);
 
     pm_vasprintf(&msg, format, args);
-    
+
     errormsg(msg);
 
     pm_strfree(msg);
@@ -355,7 +355,7 @@ pm_allocarray(int const cols,
 
 
 void
-pm_freearray(char ** const rowIndex, 
+pm_freearray(char ** const rowIndex,
              int     const rows) {
 
     void * const rowIndexVoid = rowIndex;
@@ -368,8 +368,8 @@ pm_freearray(char ** const rowIndex,
 /* Case-insensitive keyword matcher. */
 
 int
-pm_keymatch(const char *       const strarg, 
-            const char * const keywordarg, 
+pm_keymatch(const char *       const strarg,
+            const char * const keywordarg,
             int          const minchars) {
     int len;
     const char * keyword;
@@ -438,7 +438,8 @@ pm_maxvaltobits(int const maxval) {
         return 16;
     else
         pm_error( "maxval of %d is too large!", maxval );
-        return -1;  /* Should never come here */
+
+    assert(false);
 }
 
 int
@@ -448,7 +449,7 @@ pm_bitstomaxval(int const bits) {
 
 
 unsigned int PURE_FN_ATTR
-pm_lcm(unsigned int const x, 
+pm_lcm(unsigned int const x,
        unsigned int const y,
        unsigned int const z,
        unsigned int const limit) {
@@ -471,7 +472,7 @@ pm_lcm(unsigned int const x,
            candidate <= limit)
         candidate += biggest;
 
-    if (candidate > limit) 
+    if (candidate > limit)
         candidate = limit;
 
     return candidate;
@@ -498,7 +499,7 @@ pm_init(const char * const progname,
 #ifdef HAVE_SETMODE
     /* Set the stdin and stdout mode to binary.  This means nothing on Unix,
        but matters on Windows.
-       
+
        Note that stdin and stdout aren't necessarily image files.  In
        particular, stdout is sometimes text for human consumption,
        typically printed on the terminal.  Binary mode isn't really
@@ -506,7 +507,7 @@ pm_init(const char * const progname,
        any knowledge of how stdin and stdout are being used because it is
        easy.  But we do make an exception for the case that we know the
        file is a terminal, to get a little closer to doing the right
-       thing.  
+       thing.
     */
     if (!isatty(0)) setmode(0,O_BINARY);  /* Standard Input */
     if (!isatty(1)) setmode(1,O_BINARY);  /* Standard Output */
@@ -516,13 +517,51 @@ pm_init(const char * const progname,
 
 
 
+static const char *
+dtMsg(time_t const dateTime) {
+/*----------------------------------------------------------------------------
+   Text for the version message to indicate datetime 'dateTime'.
+-----------------------------------------------------------------------------*/
+    struct tm * const brokenTimeP = localtime(&dateTime);
+
+    char buffer[100];
+
+    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", brokenTimeP);
+
+    return pm_strdup(buffer);
+}
+
+
+
 static void
 showVersion(void) {
-    pm_message( "Using libnetpbm from Netpbm Version: %s", NETPBM_VERSION );
-#if defined(COMPILE_TIME) && defined(COMPILED_BY)
-    pm_message( "Compiled %s by user \"%s\"",
-                COMPILE_TIME, COMPILED_BY );
+
+    pm_message("Using libnetpbm from Netpbm Version: %s", NETPBM_VERSION);
+
+    /* SOURCE_DATETIME is defined when the user wants a reproducible build,
+       so wants the source code modification datetime instead of the build
+       datetime in the object code.
+    */
+#if defined(SOURCE_DATETIME)
+    {
+        const char * const sourceDtMsg = dtMsg(SOURCE_DATETIME);
+        pm_message("Built from source dated %s", sourceDtMsg);
+        pm_strfree(sourceDtMsg);
+    }
+#else
+  #if defined(BUILD_DATETIME)
+    {
+        const char * const buildDtMsg = dtMsg(BUILD_DATETIME);
+        pm_message("Built at %s", buildDtMsg);
+        pm_strfree(buildDtMsg);
+    }
+  #endif
 #endif
+
+#if defined(COMPILED_BY)
+    pm_message("Built by %s", COMPILED_BY);
+#endif
+
 #ifdef BSD
     pm_message( "BSD defined" );
 #endif /*BSD*/
@@ -540,7 +579,7 @@ showVersion(void) {
         pm_message( "RGB_ENV='%s'", RGBENV );
         rgbdef = getenv(RGBENV);
         if( rgbdef )
-            pm_message( "RGBENV= '%s' (env vbl set to '%s')", 
+            pm_message( "RGBENV= '%s' (env vbl set to '%s')",
                         RGBENV, rgbdef );
         else
             pm_message( "RGBENV= '%s' (env vbl is unset)", RGBENV);
@@ -553,7 +592,7 @@ static void
 showNetpbmHelp(const char progname[]) {
 /*----------------------------------------------------------------------------
   Tell the user where to get help for this program, assuming it is a Netpbm
-  program (a program that comes with the Netpbm package, as opposed to a 
+  program (a program that comes with the Netpbm package, as opposed to a
   program that just uses the Netpbm libraries).
 
   Tell him to go to the URL listed in the Netpbm configuration file.
@@ -571,9 +610,9 @@ showNetpbmHelp(const char progname[]) {
 
     if (getenv("NETPBM_CONF"))
         netpbmConfigFileName = getenv("NETPBM_CONF");
-    else 
+    else
         netpbmConfigFileName = "/etc/netpbm";
-    
+
     netpbmConfigFile = fopen(netpbmConfigFileName, "r");
     if (netpbmConfigFile == NULL) {
         pm_message("Unable to open Netpbm configuration file '%s'.  "
@@ -627,14 +666,14 @@ parseCommonOptions(int *         const argcP,
 
     for (inCursor = 1, outCursor = 1; inCursor < *argcP; ++inCursor) {
             if (strcaseeq(argv[inCursor], "-quiet") ||
-                strcaseeq(argv[inCursor], "--quiet")) 
+                strcaseeq(argv[inCursor], "--quiet"))
                 *showMessagesP = false;
             else if (strcaseeq(argv[inCursor], "-version") ||
-                     strcaseeq(argv[inCursor], "--version")) 
+                     strcaseeq(argv[inCursor], "--version"))
                 *showVersionP = true;
             else if (strcaseeq(argv[inCursor], "-help") ||
                      strcaseeq(argv[inCursor], "--help") ||
-                     strcaseeq(argv[inCursor], "-?")) 
+                     strcaseeq(argv[inCursor], "-?"))
                 *showHelpP = true;
             else if (strcaseeq(argv[inCursor], "-plain") ||
                      strcaseeq(argv[inCursor], "--plain"))
@@ -689,7 +728,7 @@ pm_proginit(int *         const argcP,
         exit(0);
     } else if (justShowHelp) {
         pm_error("Use 'man %s' for help.", progname);
-        /* If we can figure out a way to distinguish Netpbm programs from 
+        /* If we can figure out a way to distinguish Netpbm programs from
            other programs using the Netpbm libraries, we can do better here.
         */
         if (0)
@@ -703,7 +742,7 @@ pm_proginit(int *         const argcP,
 void
 pm_setMessage(int   const newState,
               int * const oldStateP) {
-    
+
     if (oldStateP)
         *oldStateP = pm_showmessages;
 
@@ -724,7 +763,7 @@ static void
 extractAfterLastSlash(const char * const fullPath,
                       char *       const retval,
                       size_t       const retvalSize) {
-    
+
     char * slashPos;
 
     /* Chop any directories off the left end */
@@ -737,10 +776,17 @@ extractAfterLastSlash(const char * const fullPath,
         strncpy(retval, slashPos +1, retvalSize);
         retval[retvalSize-1] = '\0';
     }
+}
+
 
-    /* Chop any .exe off the right end */
-    if (strlen(retval) >= 4 && strcmp(retval+strlen(retval)-4, ".exe") == 0)
-        retval[strlen(retval)-4] = 0;
+
+static void
+chopOffExe(char * const arg) {
+/*----------------------------------------------------------------------------
+  Chop any .exe off the right end of 'arg'.
+-----------------------------------------------------------------------------*/
+    if (strlen(arg) >= 4 && strcmp(arg+strlen(arg)-4, ".exe") == 0)
+        arg[strlen(arg)-4] = 0;
 }
 
 
@@ -748,13 +794,13 @@ extractAfterLastSlash(const char * const fullPath,
 char *
 pm_arg0toprogname(const char arg0[]) {
 /*----------------------------------------------------------------------------
-   Given a value for argv[0] (a command name or file name passed to a 
+   Given a value for argv[0] (a command name or file name passed to a
    program in the standard C calling sequence), return the name of the
    Netpbm program to which it refers.
 
    In the most ordinary case, this is simply the argument itself.
 
-   But if the argument contains a slash, it is the part of the argument 
+   But if the argument contains a slash, it is the part of the argument
    after the last slash, and if there is a .exe on it (as there is for
    DJGPP), that is removed.
 
@@ -775,6 +821,7 @@ pm_arg0toprogname(const char arg0[]) {
 #else
     static char retval[MAX_RETVAL_SIZE+1];
     extractAfterLastSlash(arg0, retval, sizeof(retval));
+    chopOffExe(retval);
 #endif
 
     return retval;
@@ -801,7 +848,7 @@ pm_parse_width(const char * const arg) {
     unsigned int width;
     const char * error;
 
-    pm_interpret_uint(arg, &width, &error);
+    pm_string_to_uint(arg, &width, &error);
 
     if (error) {
         pm_error("'%s' is invalid as an image width.  %s", arg, error);
@@ -826,7 +873,7 @@ pm_parse_height(const char * const arg) {
     unsigned int height;
     const char * error;
 
-    pm_interpret_uint(arg, &height, &error);
+    pm_string_to_uint(arg, &height, &error);
 
     if (error) {
         pm_error("'%s' is invalid as an image height.  %s", arg, error);
diff --git a/lib/libpnm3.c b/lib/libpnm3.c
index 0426ebcb..3970c734 100644
--- a/lib/libpnm3.c
+++ b/lib/libpnm3.c
@@ -10,6 +10,9 @@
 ** implied warranty.
 */
 
+#include <stdbool.h>
+#include <assert.h>
+
 #include "pnm.h"
 #include "ppm.h"
 #include "pgm.h"
@@ -199,22 +202,22 @@ pnm_blackxel(xelval const maxval,
     default:
         pm_error("Invalid format %d passed to pnm_blackxel()", format);
     }
-    
+
     return retval;
 }
 
 
 
 void
-pnm_invertxel(xel*   const xP, 
-              xelval const maxval, 
+pnm_invertxel(xel*   const xP,
+              xelval const maxval,
               int    const format) {
 
     switch (PNM_FORMAT_TYPE(format)) {
     case PPM_TYPE:
-        PPM_ASSIGN(*xP, 
+        PPM_ASSIGN(*xP,
                    maxval - PPM_GETR(*xP),
-                   maxval - PPM_GETG(*xP), 
+                   maxval - PPM_GETG(*xP),
                    maxval - PPM_GETB(*xP));
         break;
 
@@ -234,145 +237,199 @@ pnm_invertxel(xel*   const xP,
 
 
 void
-pnm_promoteformat( xel** xels, int cols, int rows, xelval maxval, int format, xelval newmaxval, int newformat )
-    {
-    int row;
+pnm_promoteformat(xel ** const xels,
+                  int    const cols,
+                  int    const rows,
+                  xelval const maxval,
+                  int    const format,
+                  xelval const newmaxval,
+                  int    const newformat) {
 
-    for ( row = 0; row < rows; ++row )
-    pnm_promoteformatrow(
-        xels[row], cols, maxval, format, newmaxval, newformat );
-    }
+    unsigned int row;
 
-void
-pnm_promoteformatrow( xel* xelrow, int cols, xelval maxval, int format, xelval newmaxval, int newformat )
-    {
-    register int col;
-    register xel* xP;
-
-    if ( ( PNM_FORMAT_TYPE(format) == PPM_TYPE &&
-       ( PNM_FORMAT_TYPE(newformat) == PGM_TYPE ||
-         PNM_FORMAT_TYPE(newformat) == PBM_TYPE ) ) ||
-     ( PNM_FORMAT_TYPE(format) == PGM_TYPE &&
-       PNM_FORMAT_TYPE(newformat) == PBM_TYPE ) )
-    pm_error( "pnm_promoteformatrow: can't promote downwards!" );
-
-    /* Are we promoting to the same type? */
-    if ( PNM_FORMAT_TYPE(format) == PNM_FORMAT_TYPE(newformat) )
-    {
-    if ( PNM_FORMAT_TYPE(format) == PBM_TYPE )
-        return;
-    if ( newmaxval < maxval )
-        pm_error(
-       "pnm_promoteformatrow: can't decrease maxval - try using pnmdepth" );
-    if ( newmaxval == maxval )
-        return;
-    /* Increase maxval. */
-    switch ( PNM_FORMAT_TYPE(format) )
-        {
-        case PGM_TYPE:
-        for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-        PNM_ASSIGN1(
-            *xP, (int) PNM_GET1(*xP) * newmaxval / maxval );
-        break;
+    for (row = 0; row < rows; ++row)
+        pnm_promoteformatrow(
+            xels[row], cols, maxval, format, newmaxval, newformat);
+}
 
-        case PPM_TYPE:
-        for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-        PPM_DEPTH( *xP, *xP, maxval, newmaxval );
-        break;
 
-        default:
-        pm_error( "Invalid old format passed to pnm_promoteformatrow()" );
-        }
-    return;
-    }
 
-    /* We must be promoting to a higher type. */
-    switch ( PNM_FORMAT_TYPE(format) )
-    {
-    case PBM_TYPE:
-    switch ( PNM_FORMAT_TYPE(newformat) )
-        {
-        case PGM_TYPE:
-        for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-        if ( PNM_GET1(*xP) == 0 )
-            PNM_ASSIGN1( *xP, 0 );
-        else
-            PNM_ASSIGN1( *xP, newmaxval );
-        break;
-
-        case PPM_TYPE:
-        for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-        if ( PNM_GET1(*xP) == 0 )
-            PPM_ASSIGN( *xP, 0, 0, 0 );
-        else
-            PPM_ASSIGN( *xP, newmaxval, newmaxval, newmaxval );
-        break;
-
-        default:
-        pm_error( "Invalid new format passed to pnm_promoteformatrow()" );
+void
+pnm_promoteformatrow(xel *  const xelrow,
+                     int    const cols,
+                     xelval const maxval,
+                     int    const format,
+                     xelval const newmaxval,
+                     int    const newformat) {
+
+    if ((PNM_FORMAT_TYPE(format) == PPM_TYPE &&
+         (PNM_FORMAT_TYPE(newformat) == PGM_TYPE ||
+          PNM_FORMAT_TYPE(newformat) == PBM_TYPE)) ||
+        (PNM_FORMAT_TYPE(format) == PGM_TYPE &&
+         PNM_FORMAT_TYPE(newformat) == PBM_TYPE)) {
+
+        pm_error( "pnm_promoteformatrow: can't promote downwards!" );
+    } else if (PNM_FORMAT_TYPE(format) == PNM_FORMAT_TYPE(newformat)) {
+        /* We're promoting to the same type - but not necessarily maxval */
+        if (PNM_FORMAT_TYPE(format) == PBM_TYPE) {
+            /* PBM doesn't have maxval, so this is idempotent */
+        } else if (newmaxval < maxval)
+            pm_error("pnm_promoteformatrow: can't decrease maxval - "
+                     "try using pamdepth");
+        else if (newmaxval == maxval) {
+            /* Same type, same maxval => idempotent function */
+        } else {
+            /* Increase maxval. */
+            switch (PNM_FORMAT_TYPE(format)) {
+            case PGM_TYPE: {
+                unsigned int col;
+                for (col = 0; col < cols; ++col)
+                    PNM_ASSIGN1(xelrow[col],
+                                PNM_GET1(xelrow[col]) * newmaxval / maxval);
+            } break;
+
+            case PPM_TYPE: {
+                unsigned int col;
+                for (col = 0; col < cols; ++col)
+                    PPM_DEPTH(xelrow[col], xelrow[col], maxval, newmaxval);
+            } break;
+
+            default:
+                pm_error("Invalid old format passed to "
+                         "pnm_promoteformatrow()");
+            }
         }
-    break;
+    } else {
+        /* Promote to a higher type. */
+        switch (PNM_FORMAT_TYPE(format)) {
+        case PBM_TYPE:
+            switch (PNM_FORMAT_TYPE(newformat)) {
+            case PGM_TYPE: {
+                unsigned int col;
+                for (col = 0; col < cols; ++col) {
+                    if (PNM_GET1(xelrow[col]) == 0)
+                        PNM_ASSIGN1(xelrow[col], 0);
+                    else
+                        PNM_ASSIGN1(xelrow[col], newmaxval);
+                }
+            } break;
+
+            case PPM_TYPE: {
+                unsigned int col;
+                for (col = 0; col < cols; ++col) {
+                    if (PNM_GET1(xelrow[col]) == 0)
+                        PPM_ASSIGN(xelrow[col], 0, 0, 0);
+                    else
+                        PPM_ASSIGN(xelrow[col],
+                                   newmaxval, newmaxval, newmaxval );
+                }
+            } break;
+
+            default:
+                pm_error("Invalid new format passed to "
+                         "pnm_promoteformatrow()");
+            }
+            break;
 
-    case PGM_TYPE:
-    switch ( PNM_FORMAT_TYPE(newformat) )
-        {
-        case PPM_TYPE:
-        if ( newmaxval < maxval )
-        pm_error(
-       "pnm_promoteformatrow: can't decrease maxval - try using pnmdepth" );
-        if ( newmaxval == maxval )
-        {
-        for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-            PPM_ASSIGN(
-            *xP, PNM_GET1(*xP), PNM_GET1(*xP), PNM_GET1(*xP) );
-        }
-        else
-        { /* Increase maxval. */
-        for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-            PPM_ASSIGN(
-            *xP, (int) PNM_GET1(*xP) * newmaxval / maxval,
-            (int) PNM_GET1(*xP) * newmaxval / maxval,
-            (int) PNM_GET1(*xP) * newmaxval / maxval );
-        }
-        break;
+        case PGM_TYPE:
+            switch (PNM_FORMAT_TYPE(newformat)) {
+            case PPM_TYPE:
+                if (newmaxval < maxval)
+                    pm_error("pnm_promoteformatrow: can't decrease maxval - "
+                             "try using pamdepth");
+                else if (newmaxval == maxval) {
+                    unsigned int col;
+                    for (col = 0; col < cols; ++col) {
+                        PPM_ASSIGN(xelrow[col],
+                                   PNM_GET1(xelrow[col]),
+                                   PNM_GET1(xelrow[col]),
+                                   PNM_GET1(xelrow[col]));
+                    }
+                } else {
+                    /* Increase maxval. */
+                    unsigned int col;
+                    for (col = 0; col < cols; ++col) {
+                        PPM_ASSIGN(xelrow[col],
+                                   PNM_GET1(xelrow[col]) * newmaxval / maxval,
+                                   PNM_GET1(xelrow[col]) * newmaxval / maxval,
+                                   PNM_GET1(xelrow[col]) * newmaxval / maxval);
+                    }
+                }
+                break;
+
+            default:
+                pm_error("Invalid new format passed to "
+                         "pnm_promoteformatrow()");
+            }
+            break;
 
         default:
-        pm_error( "Invalid new format passed to pnm_promoteformatrow()" );
+            pm_error("Invalid old format passed to pnm_promoteformatrow()");
         }
-    break;
-
-    default:
-        pm_error( "Invalid old format passed to pnm_promoteformatrow()" );
-    }
     }
+}
+
 
 
 pixel
-pnm_xeltopixel(xel const inputxel,
+pnm_xeltopixel(xel const inputXel,
                int const format) {
-    
-    pixel outputpixel;
+
+    pixel outputPixel;
 
     switch (PNM_FORMAT_TYPE(format)) {
     case PPM_TYPE:
-        PPM_ASSIGN(outputpixel,
-                   PPM_GETR(inputxel),
-                   PPM_GETG(inputxel),
-                   PPM_GETB(inputxel));
+        PPM_ASSIGN(outputPixel,
+                   PNM_GETR(inputXel),
+                   PNM_GETG(inputXel),
+                   PNM_GETB(inputXel));
         break;
     case PGM_TYPE:
     case PBM_TYPE:
-        PPM_ASSIGN(outputpixel,
-                   PNM_GET1(inputxel),
-                   PNM_GET1(inputxel),
-                   PNM_GET1(inputxel));
+        PPM_ASSIGN(outputPixel,
+                   PNM_GET1(inputXel),
+                   PNM_GET1(inputXel),
+                   PNM_GET1(inputXel));
         break;
     default:
         pm_error("Invalid format code %d passed to pnm_xeltopixel()",
                  format);
     }
 
-    return outputpixel;
+    return outputPixel;
+}
+
+
+
+xel
+pnm_pixeltoxel(pixel const inputPixel) {
+
+    return inputPixel;
+}
+
+
+
+xel
+pnm_graytoxel(gray const inputGray) {
+
+    xel outputXel;
+
+    PNM_ASSIGN1(outputXel, inputGray);
+
+    return outputXel;
+}
+
+
+xel
+pnm_bittoxel(bit    const inputBit,
+             xelval const maxval) {
+
+    switch (inputBit) {
+    case PBM_BLACK: return pnm_blackxel(maxval, PBM_TYPE); break;
+    case PBM_WHITE: return pnm_whitexel(maxval, PBM_TYPE); break;
+    default:
+        assert(false);
+    }
 }
 
 
@@ -413,6 +470,6 @@ pnm_parsecolorxel(const char * const colorName,
         pm_error("Invalid format code %d passed to pnm_parsecolorxel()",
                  format);
     }
-    
+
     return retval;
 }
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/libppmcolor.c b/lib/libppmcolor.c
index aee8fd83..c0a88dc2 100644
--- a/lib/libppmcolor.c
+++ b/lib/libppmcolor.c
@@ -9,9 +9,6 @@
 ** implied warranty.
 */
 
-#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
-#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
-
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
@@ -22,376 +19,31 @@
 #include "netpbm/nstring.h"
 #include "ppm.h"
 #include "colorname.h"
+#include "pam.h"
 
 
-static void
-computeHexTable(int hexit[]) {
-
-    unsigned int i;
-
-    for ( i = 0; i < 256; ++i )
-        hexit[i] = -1;
-
-    hexit['0'] = 0;
-    hexit['1'] = 1;
-    hexit['2'] = 2;
-    hexit['3'] = 3;
-    hexit['4'] = 4;
-    hexit['5'] = 5;
-    hexit['6'] = 6;
-    hexit['7'] = 7;
-    hexit['8'] = 8;
-    hexit['9'] = 9;
-    hexit['a'] = hexit['A'] = 10;
-    hexit['b'] = hexit['B'] = 11;
-    hexit['c'] = hexit['C'] = 12;
-    hexit['d'] = hexit['D'] = 13;
-    hexit['e'] = hexit['E'] = 14;
-    hexit['f'] = hexit['F'] = 15;
-}
-
+pixel
+ppm_parsecolor2(const char * const colorname,
+                pixval       const maxval,
+                int          const closeOk) {
 
+    tuple const color = pnm_parsecolor2(colorname, maxval, closeOk);
 
-static long
-invRgbnorm(pixval       const rgb,
-           pixval       const maxval,
-           unsigned int const hexDigits) {
-/*----------------------------------------------------------------------------
-  This is the inverse of 'rgbnorm', below.
------------------------------------------------------------------------------*/
-    long retval;
-
-    switch (hexDigits) {
-    case 1:
-        retval = (long)((double) rgb * 15 / maxval + 0.5);
-        break;
-    case 2:
-        retval = (long) ((double) rgb * 255 / maxval + 0.5);
-        break;
-    case 3:
-        retval = (long) ((double) rgb * 4095 / maxval + 0.5);
-        break;
-    case 4:
-        retval = (long) ((double) rgb * 65535UL / maxval + 0.5);
-        break;
-    default:
-        pm_message("Internal error in invRgbnorm()");
-        abort();
-    }
-    return retval;
-}
-
+    pixel retval;
 
+    PPM_PUTR(retval, color[PAM_RED_PLANE]);
+    PPM_PUTG(retval, color[PAM_GRN_PLANE]);
+    PPM_PUTB(retval, color[PAM_BLU_PLANE]);
 
-static pixval
-rgbnorm(long         const rgb, 
-        pixval       const maxval, 
-        unsigned int const hexDigitCount, 
-        bool         const closeOk,
-        const char * const colorname) {
-/*----------------------------------------------------------------------------
-   Normalize the color (r, g, or b) value 'rgb', which was specified
-   with 'hexDigitCount' digits, to a maxval of 'maxval'.  If the
-   number of digits isn't valid, issue an error message and identify
-   the complete color color specification in error as 'colorname'.
-
-   For example, if the user says "0ff" and the maxval is 100,
-   then rgb is 0xff, n is 3, and our result is 
-   0xff / (16**3-1) * 100 = 6.
------------------------------------------------------------------------------*/
-    pixval retval;
-
-    switch (hexDigitCount) {
-    case 0:
-        pm_error("A hexadecimal color specifier in color '%s' is "
-                 "an empty string", colorname);
-        break;
-    case 1:
-        retval = (pixval)((double) rgb * maxval / 15 + 0.5);
-        break;
-    case 2:
-        retval = (pixval) ((double) rgb * maxval / 255 + 0.5);
-        break;
-    case 3:
-        retval = (pixval) ((double) rgb * maxval / 4095 + 0.5);
-        break;
-    case 4:
-        retval = (pixval) ((double) rgb * maxval / 65535L + 0.5);
-        break;
-    default:
-        pm_error("color specifier '%s' has too many digits", colorname);
-    }
+    free(color);
 
-    if (!closeOk) {
-        long const newrgb = invRgbnorm(retval, maxval, hexDigitCount);
-        if (newrgb != rgb)
-            pm_message("WARNING: Component 0x%lx of color '%s' "
-                       "cannot be represented precisely with maxval %u.  "
-                       "Approximating as %u.",
-                       rgb, colorname, maxval, retval);
-    }
     return retval;
 }
 
 
 
-static void
-parseHexDigits(const char *   const string,
-               char           const delim,
-               int            const hexit[],
-               pixval *       const nP,
-               unsigned int * const digitCountP) {
-
-    unsigned int digitCount;
-    pixval n;
-    
-    digitCount = 0;  /* initial value */
-    n = 0;           /* initial value */
-    while (string[digitCount] != delim) {
-        char const digit = string[digitCount];
-        if (digit == '\0')
-            pm_error("rgb: color spec ends prematurely");
-        else {
-            int const hexval = hexit[(unsigned int)digit];
-            if (hexval == -1)
-                pm_error("Invalid hex digit in rgb: color spec: 0x%02x",
-                         digit);
-            n = n * 16 + hexval;
-            ++digitCount;
-        }
-    }
-    *nP = n;
-    *digitCountP = digitCount;
-}
-
-
-
-static void
-parseNewHexX11(char       const colorname[], 
-               pixval     const maxval,
-               bool       const closeOk,
-               pixel *    const colorP) {
-/*----------------------------------------------------------------------------
-   Determine what color colorname[] specifies in the new style hex
-   color specification format (e.g. rgb:55/40/55).
-
-   Return that color as *colorP.
-
-   Assume colorname[] starts with "rgb:", but otherwise it might be
-   gibberish.
------------------------------------------------------------------------------*/
-    int hexit[256];
-
-    const char * cp;
-    pixval n;
-    unsigned int digitCount;
-    pixval rNorm, gNorm, bNorm;
-
-    computeHexTable(hexit);
-
-    cp = &colorname[4];
-
-    parseHexDigits(cp, '/', hexit, &n, &digitCount);
-
-    rNorm = rgbnorm(n, maxval, digitCount, closeOk, colorname);
-
-    cp += digitCount;
-    ++cp;  /* Skip the slash */
-
-    parseHexDigits(cp, '/', hexit, &n, &digitCount);
-
-    gNorm = rgbnorm(n, maxval, digitCount, closeOk, colorname);
-
-    cp += digitCount;
-    ++cp;  /* Skip the slash */
-
-    parseHexDigits(cp, '\0', hexit, &n, &digitCount);
-
-    bNorm = rgbnorm(n, maxval, digitCount, closeOk, colorname);
-
-    PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm);
-}
-
-
-
-static void
-parseNewDecX11(char       const colorname[], 
-               pixval     const maxval,
-               bool       const closeOk,
-               pixel *    const colorP) {
-
-    float const epsilon = 1.0/65536.0;
-    float fr, fg, fb;
-    pixval rNorm, gNorm, bNorm;
-
-    if (sscanf( colorname, "rgbi:%f/%f/%f", &fr, &fg, &fb) != 3)
-        pm_error("invalid color specifier '%s'", colorname);
-    if (fr < 0.0 || fr > 1.0 || fg < 0.0 || fg > 1.0 
-        || fb < 0.0 || fb > 1.0)
-        pm_error("invalid color specifier '%s' - "
-                 "values must be between 0.0 and 1.0", colorname );
-
-    rNorm = fr * maxval + 0.5;
-    gNorm = fg * maxval + 0.5;
-    bNorm = fb * maxval + 0.5;
-
-    if (!closeOk) {
-        if (fabs((double)rNorm/maxval - fr) > epsilon ||
-            fabs((double)gNorm/maxval - fg) > epsilon ||
-            fabs((double)bNorm/maxval - fb) > epsilon)
-            pm_message("WARNING: Color '%s' cannot be represented "
-                       "precisely with maxval %u.  "
-                       "Approximating as (%u,%u,%u).",
-                       colorname, maxval, rNorm, gNorm, bNorm);
-    }
-    PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm);
-}
-
-
-
-static void
-parseOldX11(char       const colorname[], 
-            pixval     const maxval,
-            bool       const closeOk,
-            pixel *    const colorP) {
-/*----------------------------------------------------------------------------
-   Return as *colorP the color specified by the old X11 style color
-   specififier colorname[] (e.g. #554055).
------------------------------------------------------------------------------*/
-    int hexit[256];
-    long r,g,b;
-    pixval rNorm, gNorm, bNorm;
-    
-    computeHexTable(hexit);
-
-    if (!pm_strishex(&colorname[1]))
-        pm_error("Non-hexadecimal characters in #-type color specification");
-
-    switch (strlen(colorname) - 1 /* (Number of hex digits) */) {
-    case 3:
-        r = hexit[(int)colorname[1]];
-        g = hexit[(int)colorname[2]];
-        b = hexit[(int)colorname[3]];
-        rNorm = rgbnorm(r, maxval, 1, closeOk, colorname);
-        gNorm = rgbnorm(g, maxval, 1, closeOk, colorname);
-        bNorm = rgbnorm(b, maxval, 1, closeOk, colorname);
-        break;
-
-    case 6:
-        r = (hexit[(int)colorname[1]] << 4 ) + hexit[(int)colorname[2]];
-        g = (hexit[(int)colorname[3]] << 4 ) + hexit[(int)colorname[4]];
-        b = (hexit[(int)colorname[5]] << 4 ) + hexit[(int)colorname[6]];
-        rNorm = rgbnorm(r, maxval, 2, closeOk, colorname);
-        gNorm = rgbnorm(g, maxval, 2, closeOk, colorname);
-        bNorm = rgbnorm(b, maxval, 2, closeOk, colorname);
-        break;
-
-    case 9:
-        r = (hexit[(int)colorname[1]] << 8) +
-            (hexit[(int)colorname[2]] << 4) +
-            (hexit[(int)colorname[3]] << 0);
-        g = (hexit[(int)colorname[4]] << 8) + 
-            (hexit[(int)colorname[5]] << 4) +
-            (hexit[(int)colorname[6]] << 0);
-        b = (hexit[(int)colorname[7]] << 8) + 
-            (hexit[(int)colorname[8]] << 4) +
-            (hexit[(int)colorname[9]] << 0);
-        rNorm = rgbnorm(r, maxval, 3, closeOk, colorname);
-        gNorm = rgbnorm(g, maxval, 3, closeOk, colorname);
-        bNorm = rgbnorm(b, maxval, 3, closeOk, colorname);
-        break;
-
-    case 12:
-        r = (hexit[(int)colorname[1]] << 12) + 
-            (hexit[(int)colorname[2]] <<  8) +
-            (hexit[(int)colorname[3]] <<  4) + hexit[(int)colorname[4]];
-        g = (hexit[(int)colorname[5]] << 12) + 
-            (hexit[(int)colorname[6]] <<  8) +
-            (hexit[(int)colorname[7]] <<  4) + hexit[(int)colorname[8]];
-        b = (hexit[(int)colorname[9]] << 12) + 
-            (hexit[(int)colorname[10]] << 8) +
-            (hexit[(int)colorname[11]] << 4) + hexit[(int)colorname[12]];
-        rNorm = rgbnorm(r, maxval, 4, closeOk, colorname);
-        gNorm = rgbnorm(g, maxval, 4, closeOk, colorname);
-        bNorm = rgbnorm(b, maxval, 4, closeOk, colorname);
-        break;
-
-    default:
-        pm_error("invalid color specifier '%s'", colorname);
-    }
-    PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm);
-}
-
-
-
-
-static void
-parseOldX11Dec(const char       colorname[], 
-               pixval     const maxval,
-               bool       const closeOk,
-               pixel *    const colorP) {
-
-    float const epsilon = 1.0/65536.0;
-
-    float fr, fg, fb;
-    pixval rNorm, gNorm, bNorm;
-
-    if (sscanf(colorname, "%f,%f,%f", &fr, &fg, &fb) != 3)
-        pm_error("invalid color specifier '%s'", colorname);
-    if (fr < 0.0 || fr > 1.0 || fg < 0.0 || fg > 1.0 
-        || fb < 0.0 || fb > 1.0)
-        pm_error("invalid color specifier '%s' - "
-                 "values must be between 0.0 and 1.0", colorname );
-
-    rNorm = fr * maxval + 0.5;
-    gNorm = fg * maxval + 0.5;
-    bNorm = fb * maxval + 0.5;
-
-    if (!closeOk) {
-        if (fabs((float)rNorm/maxval - fr) > epsilon ||
-            fabs((float)gNorm/maxval - fg) > epsilon ||
-            fabs((float)bNorm/maxval - fb) > epsilon)
-            pm_message("WARNING: Color '%s' cannot be represented "
-                       "precisely with maxval %u.  "
-                       "Approximating as (%u,%u,%u).",
-                       colorname, maxval, rNorm, gNorm, bNorm);
-    }
-    PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm);
-}
-
-
-
-pixel
-ppm_parsecolor2(const char * const colorname,
-                pixval       const maxval,
-                int          const closeOk) {
-
-    pixel color;
-    
-    if (strncmp(colorname, "rgb:", 4) == 0)
-        /* It's a new-X11-style hexadecimal rgb specifier. */
-        parseNewHexX11(colorname, maxval, closeOk, &color);
-    else if (strncmp(colorname, "rgbi:", 5) == 0)
-        /* It's a new-X11-style decimal/float rgb specifier. */
-        parseNewDecX11(colorname, maxval, closeOk, &color);
-    else if (colorname[0] == '#')
-        /* It's an old-X11-style hexadecimal rgb specifier. */
-        parseOldX11(colorname, maxval, closeOk, &color);
-    else if ((colorname[0] >= '0' && colorname[0] <= '9') ||
-             colorname[0] == '.')
-        /* It's an old-style decimal/float rgb specifier. */
-        parseOldX11Dec(colorname, maxval, closeOk, &color);
-    else 
-        /* Must be a name from the X-style rgb file. */
-        pm_parse_dictionary_name(colorname, maxval, closeOk, &color);
-    
-    return color;
-}
-
-
-
 pixel
-ppm_parsecolor(const char * const colorname, 
+ppm_parsecolor(const char * const colorname,
                pixval       const maxval) {
 
     return ppm_parsecolor2(colorname, maxval, TRUE);
@@ -400,13 +52,14 @@ ppm_parsecolor(const char * const colorname,
 
 
 char *
-ppm_colorname(const pixel * const colorP, 
-              pixval        const maxval, 
+ppm_colorname(const pixel * const colorP,
+              pixval        const maxval,
               int           const hexok)   {
 
     int r, g, b;
     FILE * f;
     static char colorname[200];
+        /* Null string means no suitable name so far */
 
     if (maxval == 255) {
         r = PPM_GETR(*colorP);
@@ -419,33 +72,57 @@ ppm_colorname(const pixel * const colorP,
     }
 
     f = pm_openColornameFile(NULL, !hexok);
-    if (f != NULL) {
-        int best_diff, this_diff;
+
+    if (!f)
+        STRSCPY(colorname, "");
+    else {
+        int bestDiff;
         bool eof;
 
-        best_diff = 32767;
-        eof = FALSE;
-        while (!eof && best_diff > 0 ) {
+        for (bestDiff = 32767, eof = FALSE;
+             !eof && bestDiff > 0; ) {
             struct colorfile_entry const ce = pm_colorget(f);
             if (ce.colorname)  {
-                this_diff = abs(r - ce.r) + abs(g - ce.g) + abs(b - ce.b);
-                if (this_diff < best_diff) {
-                    best_diff = this_diff;
-                    strcpy(colorname, ce.colorname);
+                int const thisDiff =
+                    abs(r - (int)ce.r) +
+                    abs(g - (int)ce.g) +
+                    abs(b - (int)ce.b);
+
+                if (thisDiff < bestDiff) {
+                    bestDiff = thisDiff;
+                    STRSCPY(colorname, ce.colorname);
                 }
             } else
                 eof = TRUE;
         }
         fclose(f);
-        if (best_diff != 32767 && (best_diff == 0 || ! hexok))
-            return colorname;
+
+        if (bestDiff == 32767) {
+            /* Color file contain no entries, so we can't even pick a close
+               one
+            */
+            STRSCPY(colorname, "");
+        } else if (bestDiff > 0 && hexok) {
+            /* We didn't find an exact match and user is willing to accept
+               hex, so we don't have to use an approximate match.
+            */
+            STRSCPY(colorname, "");
+        }
     }
 
-    /* Color lookup failed, but caller is willing to take an X11-style
-       hex specifier, so return that.
-    */
-    sprintf(colorname, "#%02x%02x%02x", r, g, b);
-    return colorname;}
+    if (streq(colorname, "")) {
+        if (hexok) {
+            /* Color lookup failed, but caller is willing to take an X11-style
+               hex specifier, so return that.
+            */
+            sprintf(colorname, "#%02x%02x%02x", r, g, b);
+        } else {
+            pm_error("Couldn't find any name colors at all");
+        }
+    }
+
+    return colorname;
+}
 
 
 
@@ -508,14 +185,14 @@ processColorfileEntry(struct colorfile_entry const ce,
             /* The color is already in the hash, which means we saw it
                earlier in the file.  We prefer the first name that the
                file gives for each color, so we just ignore the
-               current entry.  
+               current entry.
             */
             *errorP = NULL;
         } else {
             ppm_addtocolorhash(cht, &color, *colornameIndexP);
-            colornames[*colornameIndexP] = strdup(ce.colorname);
+            colornames[*colornameIndexP] = pm_strdup(ce.colorname);
             colors[*colornameIndexP] = color;
-            if (colornames[*colornameIndexP] == NULL)
+            if (colornames[*colornameIndexP] == pm_strsol)
                 pm_asprintf(errorP, "Unable to allocate space for color name");
             else {
                 *errorP = NULL;
@@ -538,15 +215,14 @@ openColornameFile(const char *  const fileName,
 
     if (setjmp(jmpbuf) != 0) {
         pm_asprintf(errorP, "Failed to open color name file");
-        pm_setjmpbuf(origJmpbufP);
-        pm_longjmp();
     } else {
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
         *filePP = pm_openColornameFile(fileName, mustOpen);
 
         *errorP = NULL;  /* Would have longjmped if there were a problem */
-
-        pm_setjmpbuf(origJmpbufP);
     }
+    pm_setjmpbuf(origJmpbufP);
 }
 
 
@@ -576,15 +252,15 @@ readOpenColorFile(FILE *          const colorFileP,
 
     while (!done && !*errorP) {
         struct colorfile_entry const ce = pm_colorget(colorFileP);
-        
-        if (!ce.colorname)  
+
+        if (!ce.colorname)
             done = TRUE;
-        else 
+        else
             processColorfileEntry(ce, cht, colornames, colors,
                                   &nColorsDone, errorP);
     }
     *nColorsP = nColorsDone;
-    
+
     if (*errorP) {
         unsigned int colorIndex;
 
@@ -630,13 +306,13 @@ readColorFile(const char *    const fileName,
         } else {
             readOpenColorFile(colorFileP, nColorsP, colornames, colors, cht,
                               errorP);
-            
+
             fclose(colorFileP);
         }
     }
 }
 
-    
+
 
 static void
 readcolordict(const char *      const fileName,
@@ -657,14 +333,14 @@ readcolordict(const char *      const fileName,
         pixel * colors;
 
         MALLOCARRAY(colors, MAXCOLORNAMES);
-        
+
         if (colors == NULL)
             pm_asprintf(errorP, "Unable to allocate space for color table.");
         else {
             colorhash_table cht;
 
             cht = allocColorHash();
-            
+
             if (cht == NULL)
                 pm_asprintf(errorP, "Unable to allocate space for color hash");
             else {
@@ -732,7 +408,7 @@ ppm_readcolordict(const char *      const fileName,
     if (error) {
         pm_errormsg("%s", error);
         pm_strfree(error);
-        ppm_freecolorhash(cht);
+        pm_longjmp();
     } else {
         if (chtP)
             *chtP = cht;
@@ -754,9 +430,9 @@ ppm_readcolordict(const char *      const fileName,
 
 
 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) {
 
     ppm_readcolordict(fileName, mustOpen, NULL, colornamesP, NULL, chtP);
@@ -778,7 +454,7 @@ ppm_freecolornames(const char ** const colornames) {
 
 
 
-static unsigned int 
+static unsigned int
 nonnegative(unsigned int const arg) {
 
     if ((int)(arg) < 0)
@@ -790,8 +466,8 @@ nonnegative(unsigned int const arg) {
 
 
 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) {
 /*----------------------------------------------------------------------------
    Return the color that has luminance 'y', blue chrominance 'cb', and
@@ -805,12 +481,12 @@ ppm_color_from_ycbcr(unsigned int const y,
 -----------------------------------------------------------------------------*/
     pixel retval;
 
-    PPM_ASSIGN(retval, 
+    PPM_ASSIGN(retval,
                y + 1.4022 * cr,
                nonnegative(y - 0.7145 * cr - 0.3456 * cb),
                y + 1.7710 * cb
         );
-    
+
     return retval;
 }
 
@@ -876,10 +552,10 @@ ppm_color_from_hsv(struct hsv const hsv,
             pm_error("Invalid H value passed to color_from_HSV: %f", hsv.h);
         }
     }
-    PPM_ASSIGN(retval, 
-               ROUNDU(R * maxval),
-               ROUNDU(G * maxval),
-               ROUNDU(B * maxval));
+    PPM_ASSIGN(retval,
+               ppm_unnormalize(R, maxval),
+               ppm_unnormalize(G, maxval),
+               ppm_unnormalize(B, maxval));
 
     return retval;
 }
diff --git a/lib/libppmd.c b/lib/libppmd.c
index 262679ec..a94ff107 100644
--- a/lib/libppmd.c
+++ b/lib/libppmd.c
@@ -850,7 +850,7 @@ ppmd_spline4p(pixel **       const pixels,
 /*----------------------------------------------------------------------------
    Draw a cubic spline from 'endPt0' to 'endPt1', using 'ctlPt0' and
    'ctlPt1' as control points in the classic way: a line through
-   'endPt0' and 'ctlPt0' is tangent to the curve at 'entPt0' and the
+   'endPt0' and 'ctlPt0' is tangent to the curve at 'endPt0' and the
    length of that line controls "enthusiasm," whatever that is.
    Same for 'endPt1' and 'ctlPt1'.
 -----------------------------------------------------------------------------*/
@@ -981,7 +981,7 @@ typedef struct fillobj {
 
     /* The only reason we have a struct fillState separate from
        struct fillobj is that the drawproc interface is defined to
-       have drawing not modify the fillobj, i.e. it passed
+       have drawing not modify the fillobj, i.e. it passes
        const fillobj * to the drawing program.
     */
     struct fillState * stateP;
@@ -1116,14 +1116,13 @@ continueSegment(struct fillState * const stateP,
 
 
 
-
 /* ppmd_fill_drawprocp() is a drawproc that turns an outline drawing function
    into a filled shape function.  This is a somewhat off-label application of
    a drawproc:  A drawproc is intended just to draw a point.  So e.g. you
    might draw a circle with a fat brush by calling ppmd_circle with a drawproc
    that draws a point as a 10-pixel disk.
 
-   But ppmd_fill_drawproc() just draws a point the trivial way: as one pixel.
+   But ppmd_fill_drawprocp() just draws a point the trivial way: as one pixel.
    However, it tracks every point that is drawn in a form that a subsequent
    ppmd_fill() call can use to to fill in the shape drawn, assuming it turns
    out to be a closed shape.
diff --git a/lib/libppmfuzzy.c b/lib/libppmfuzzy.c
index f1573c68..902d684d 100644
--- a/lib/libppmfuzzy.c
+++ b/lib/libppmfuzzy.c
@@ -338,7 +338,7 @@ matchBk(pixel     const color,
              );
 
     (*bkMatchP)[BKCOLOR_BROWN]  =
-	fzOr(
+        fzOr(
              fzAnd(fzOr(hueAround015, hueAround360),
                    fzAnd(fzNot(satVeryLow), fzOr(valLow, valMedium))),
              fzAnd(hueAround015, satLow)
diff --git a/lib/libsystem.c b/lib/libsystem.c
index fd3c52ec..bf2416a4 100644
--- a/lib/libsystem.c
+++ b/lib/libsystem.c
@@ -1,18 +1,23 @@
 /*=============================================================================
                                  pm_system
 ===============================================================================
-   This is the library subroutine pm_system().  It is just like Standard C
-   Library system(), except that you can supply routines for it to run to
-   generate the Standard Input for the executed shell command and to accept
-   the Standard Output from it.  system(), by contrast, always sets up the
-   current Standard Input and Standard Output as the Standard Input and
-   Standard Output of the shell command.
+   This is the pm_system() family of subroutines.
+
+   pm_system() is just like Standard C Library system(), except that you can
+   supply routines for it to run to generate the Standard Input for the
+   executed shell command and to accept the Standard Output from it.
+   system(), by contrast, always sets up the current Standard Input and
+   Standard Output as the Standard Input and Standard Output of the shell
+   command.
+
+   pm_system_lp() and pm_system_vp() are similar, but exec an OS-level program
+   (i.e. exec a program) rather than run a shell command.
 
    By Bryan Henderson, San Jose CA  2002.12.14.
 
    Contributed to the public domain.
 =============================================================================*/
-#define _XOPEN_SOURCE
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE  /* Make SIGWINCH defined on OpenBSD */
 
 #include <stdarg.h>
@@ -26,6 +31,7 @@
 
 #include "netpbm/pm_c_util.h"
 #include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
 #include "pm.h"
 #include "pm_system.h"
 
@@ -164,7 +170,7 @@ spawnProcessor(const char *  const progName,
 /*----------------------------------------------------------------------------
    Create a process to run program 'progName' with arguments
    argArray[] (terminated by NULL element).  Pass file descriptor
-   'stdinFd' to the shell as Standard Input.
+   'stdinFd' to the process as Standard Input.
 
    if 'stdoutFdP' is NULL, have that process write its Standard Output to
    the current process' Standard Output.
@@ -313,26 +319,44 @@ signalName(unsigned int const signalClass) {
 
 
 
-static void
-cleanupProcessorProcess(pid_t const processorPid) {
+const char *
+pm_termStatusDesc(int const termStatusArg) {
+/*----------------------------------------------------------------------------
+   English description of  process termination status 'termStatus'.
+-----------------------------------------------------------------------------*/
+    const char * retval;
+
+    /* WIFEXITED, etc. do not work with a constant argument in older GNU C
+       library.  Compilation fails with "attempt to assign read-only
+       location".  This is because The GNU C library has some magic to allow
+       for a BSD 'union wait' (instead of int) argument to WIFEXITED.  The
+       magic involves defining a variable with 'typeof' the argument and
+       assigning to that variable.
+       
+       To work around this, we make sure the argument is not constant.
+    */
 
-    int terminationStatus;
-    waitpid(processorPid, &terminationStatus, 0);
+    int termStatus = termStatusArg;
 
-    if (WIFEXITED(terminationStatus)) {
-        int const exitStatus = WEXITSTATUS(terminationStatus);
+    if (WIFEXITED(termStatus)) {
+        int const exitStatus = WEXITSTATUS(termStatus);
 
-        if (exitStatus != 0)
-            pm_message("Shell process exited with abnormal exit status %u.  ",
-                       exitStatus);
-    } else if (WIFSIGNALED(terminationStatus)) {
-        pm_message("Shell process was killed by a Class %u (%s) signal.",
-                   WTERMSIG(terminationStatus),
-                   signalName(WTERMSIG(terminationStatus)));
+        if (exitStatus == 0)
+            pm_asprintf(&retval, "Process exited normally");
+        else
+            pm_asprintf(&retval,
+                        "Process exited with abnormal exit status %u.  ",
+                        exitStatus);
+    } else if (WIFSIGNALED(termStatus)) {
+        pm_asprintf(&retval, "Process was killed by a Class %u (%s) signal.",
+                    WTERMSIG(termStatus),
+                    signalName(WTERMSIG(termStatus)));
     } else {
-        pm_message("Shell process died, but its termination status "
-                   "0x%x  doesn't make sense", terminationStatus);
+        pm_asprintf(&retval, "Process died, but its termination status "
+                    "0x%x  doesn't make sense", termStatus);
     }
+
+    return retval;
 }
 
 
@@ -347,7 +371,7 @@ cleanupFeederProcess(pid_t const feederPid) {
         if (WTERMSIG(status) == SIGPIPE)
             pm_message("WARNING: "
                        "Standard Input feeder process was terminated by a "
-                       "SIGPIPE signal because the shell command closed its "
+                       "SIGPIPE signal because the program closed its "
                        "Standard Input before the Standard Input feeder was "
                        "through feeding it.");
         else
@@ -369,12 +393,13 @@ cleanupFeederProcess(pid_t const feederPid) {
 
 
 void
-pm_system_vp(const char *    const progName,
-             const char **   const argArray,
-             void stdinFeeder(int, void *),
-             void *          const feederParm,
-             void stdoutAccepter(int, void *),
-             void *          const accepterParm) {
+pm_system2_vp(const char *    const progName,
+              const char **   const argArray,
+              void stdinFeeder(int, void *),
+              void *          const feederParm,
+              void stdoutAccepter(int, void *),
+              void *          const accepterParm,
+              int *           const termStatusP) {
 /*----------------------------------------------------------------------------
    Run a program in a child process.  Feed its Standard Input with a
    pipe, which is fed by the routine 'stdinFeeder' with parameter
@@ -387,6 +412,9 @@ pm_system_vp(const char *    const progName,
 
    Run the program 'progName' with arguments argArray[] (terminated by NULL
    element).  That includes arg0.
+
+   Return as *termStatusP the termination status of the processor process
+   (the one running the program named 'progName').
 -----------------------------------------------------------------------------*/
     /* If 'stdinFeeder' is non-NULL, we create a child process to run
        'stdinFeeder' and create a pipe from that process as the
@@ -416,6 +444,7 @@ pm_system_vp(const char *    const progName,
         */
     pid_t feederPid;
     pid_t processorPid;
+    int termStatus;
 
     if (stdinFeeder) {
         createPipeFeeder(stdinFeeder, feederParm, &progStdinFd, &feederPid);
@@ -452,10 +481,118 @@ pm_system_vp(const char *    const progName,
         close(progStdinFd);
     }
 
-    cleanupProcessorProcess(processorPid);
+    waitpid(processorPid, &termStatus, 0);
 
     if (feederPid) 
         cleanupFeederProcess(feederPid);
+
+    *termStatusP = termStatus;
+}
+
+
+
+void
+pm_system2_lp(const char *    const progName,
+              void stdinFeeder(int, void *),
+              void *          const feederParm,
+              void stdoutAccepter(int, void *),
+              void *          const accepterParm,
+              int *           const termStatusP,
+              ...) {
+/*----------------------------------------------------------------------------
+  Same as pm_system_vp() except with arguments as variable arguments
+  instead of an array.
+
+  N.B. the first variable argument is the program's arg 0; the last
+  variable argument must be NULL.
+-----------------------------------------------------------------------------*/
+    va_list args;
+    bool endOfArgs;
+    const char ** argArray;
+    unsigned int n;
+
+    va_start(args, termStatusP);
+
+    endOfArgs = FALSE;
+    argArray = NULL;
+
+    for (endOfArgs = FALSE, argArray = NULL, n = 0;
+         !endOfArgs;
+        ) {
+        const char * const arg = va_arg(args, const char *);
+        
+        REALLOCARRAY(argArray, n+1);
+
+        argArray[n++] = arg;
+
+        if (!arg)
+            endOfArgs = TRUE;
+    }
+
+    va_end(args);
+
+    pm_system2_vp(progName, argArray,
+                  stdinFeeder, feederParm, stdoutAccepter, accepterParm,
+                  termStatusP);
+
+    free(argArray);
+}
+
+
+
+void
+pm_system2(void stdinFeeder(int, void *),
+           void *          const feederParm,
+           void stdoutAccepter(int, void *),
+           void *          const accepterParm,
+           const char *    const shellCommand,
+           int *           const termStatusP) {
+/*----------------------------------------------------------------------------
+   Run a shell and have it run command 'shellCommand'.  Feed its
+   Standard Input with a pipe, which is fed by the routine
+   'stdinFeeder' with parameter 'feederParm'.  Process its Standard
+   Output with the routine 'stdoutAccepter' with parameter 'accepterParm'.
+
+   But if 'stdinFeeder' is NULL, just feed the shell our own Standard
+   Input.  And if 'stdoutFeeder' is NULL, just send its Standard Output
+   to our own Standard Output.
+
+   Return as *termStatusP the termination status of the processor process
+   (the one running the program named 'progName').
+-----------------------------------------------------------------------------*/
+    pm_system2_lp("/bin/sh", 
+                  stdinFeeder, feederParm, stdoutAccepter, accepterParm,
+                  termStatusP,
+                  "sh", "-c", shellCommand, NULL);
+}
+
+
+
+void
+pm_system_vp(const char *    const progName,
+             const char **   const argArray,
+             void stdinFeeder(int, void *),
+             void *          const feederParm,
+             void stdoutAccepter(int, void *),
+             void *          const accepterParm) {
+/*----------------------------------------------------------------------------
+   Same as pm_system2_vp(), except instead of returning the termination
+   status, we just issue a message (pm_message) describing it.
+-----------------------------------------------------------------------------*/
+    int termStatus;
+
+    pm_system2_vp(progName, argArray,
+                  stdinFeeder, feederParm,
+                  stdoutAccepter, accepterParm,
+                  &termStatus);
+
+    if (termStatus != 0) {
+        const char * const msg = pm_termStatusDesc(termStatus);
+
+        pm_message("%s", msg);
+
+        pm_strfree(msg);
+    }
 }
 
 
@@ -468,8 +605,11 @@ pm_system_lp(const char *    const progName,
              void *          const accepterParm,
              ...) {
 /*----------------------------------------------------------------------------
-  same as pm_system_vp() except with arguments as variable arguments
+  Same as pm_system_vp() except with arguments as variable arguments
   instead of an array.
+
+  N.B. the first variable argument is the program's arg 0; the last
+  variable argument must be NULL.
 -----------------------------------------------------------------------------*/
     va_list args;
     bool endOfArgs;
@@ -511,19 +651,62 @@ pm_system(void stdinFeeder(int, void *),
           void *          const accepterParm,
           const char *    const shellCommand) {
 /*----------------------------------------------------------------------------
-   Run a shell and have it run command 'shellCommand'.  Feed its
-   Standard Input with a pipe, which is fed by the routine
-   'stdinFeeder' with parameter 'feederParm'.  Process its Standard
-   Output with the routine 'stdoutAccepter' with parameter 'accepterParm'.
-
-   But if 'stdinFeeder' is NULL, just feed the shell our own Standard
-   Input.  And if 'stdoutFeeder' is NULL, just send its Standard Output
-   to our own Standard Output.
+   Same as pm_system2(), except instead of returning the termination status,
+   we just issue a message (pm_message) describing it.
 -----------------------------------------------------------------------------*/
+    int termStatus;
+
+    pm_system2(stdinFeeder, feederParm, stdoutAccepter, accepterParm,
+               shellCommand,
+               &termStatus);
+
+    if (termStatus != 0) {
+        const char * const msg = pm_termStatusDesc(termStatus);
+
+        pm_message("%s", msg);
+
+        pm_strfree(msg);
+    }
+}
+
+
+
+void
+pm_feed_null(int    const pipeToFeedFd,
+             void * const feederParm) {
+
+}
 
-    pm_system_lp("/bin/sh", 
-                 stdinFeeder, feederParm, stdoutAccepter, accepterParm,
-                 "sh", "-c", shellCommand, NULL);
+
+
+void
+pm_accept_null(int    const pipetosuckFd,
+               void * const accepterParm ) {
+
+    size_t const bufferSize = 4096;
+
+    unsigned char * buffer;
+
+    MALLOCARRAY(buffer, bufferSize);
+
+    if (buffer) {
+        bool eof;
+
+        for (eof = false; !eof; ) {
+            ssize_t rc;
+
+            rc = read(pipetosuckFd, buffer, bufferSize);
+
+            if (rc < 0) {
+                /* No way to report the problem; just say we're done */
+                eof = true;
+            } else if (rc == 0)
+                /* eof */
+                eof = true;
+        }
+        free(buffer);
+    }
+    close(pipetosuckFd);
 }
 
 
@@ -532,7 +715,7 @@ void
 pm_feed_from_memory(int    const pipeToFeedFd,
                     void * const feederParm) {
 
-    struct bufferDesc * const inputBufferP = feederParm;
+    pm_bufferDesc * const inputBufferP = feederParm;
     
     FILE * const outFileP = fdopen(pipeToFeedFd, "w");
     
@@ -556,7 +739,7 @@ void
 pm_accept_to_memory(int             const pipetosuckFd,
                     void *          const accepterParm ) {
 
-    struct bufferDesc * const outputBufferP = accepterParm;
+    pm_bufferDesc * const outputBufferP = accepterParm;
     
     FILE * const inFileP = fdopen(pipetosuckFd, "r");
 
@@ -570,3 +753,6 @@ pm_accept_to_memory(int             const pipetosuckFd,
     if (outputBufferP->bytesTransferredP)
         *(outputBufferP->bytesTransferredP) = bytesTransferred;
 }
+
+
+
diff --git a/lib/pam.h b/lib/pam.h
index c58e36a2..0055858b 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);
 
@@ -401,6 +401,11 @@ typedef float samplen;
 typedef samplen *tuplen;
     /* Same as 'tuple', except using normalized samples. */
 
+tuplen
+pnm_allocpamtuplen(const struct pam * const pamP);
+
+#define pnm_freepamtuplen(tuplen) pm_freerow((char*) tuplen)
+
 tuplen *
 pnm_allocpamrown(const struct pam * const pamP);
 
@@ -409,30 +414,37 @@ 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);
 
+samplen
+pnm_normalized_sample(struct pam * const pamP,
+                      sample       const sample);
+
+sample
+pnm_unnormalized_sample(struct pam * const pamP,
+                        samplen      const sampleVal);
 
 void
 pnm_normalizetuple(struct pam * const pamP,
@@ -477,6 +489,18 @@ void
 pnm_unapplyopacityrown(struct pam * const pamP,
                        tuplen *     const tuplenrow);
 
+void
+pnm_maketuplergbn(const struct pam * const pamP,
+                  tuplen             const tuple);
+
+void
+pnm_makerowrgbn(const struct pam * const pamP,
+                tuplen *           const tuplerow);
+
+void
+pnm_makearrayrgbn(const struct pam * const pamP,
+                  tuplen **          const tuples);
+
 pnm_transformMap *
 pnm_creategammatransform(const struct pam * const pamP);
 
@@ -490,26 +514,57 @@ pnm_createungammatransform(const struct pam * const pamP);
 #define pnm_freeungammatransform pnm_freegammatransform;
 
 tuple
+pnm_parsecolor2(const char * const colorname,
+                sample       const maxval,
+                int          const closeOk);
+
+tuple
 pnm_parsecolor(const char * const colorname,
                sample       const maxval);
 
+tuplen
+pnm_parsecolorn(const char * const colorname);
+
 const char *
 pnm_colorname(struct pam * const pamP,
               tuple        const color,
               int          const hexok);
 
-extern double 
+const char *
+pnm_colorspec_rgb_integer(struct pam * const pamP,
+                          tuple        const color,
+                          sample       const maxval);
+
+const char *
+pnm_colorspec_rgb_norm(struct pam * const pamP,
+                       tuple        const color,
+                       unsigned int const digitCt);
+
+const char *
+pnm_colorspec_rgb_x11(struct pam * const pamP,
+                      tuple        const color,
+                      unsigned int const hexDigitCt);
+
+const char *
+pnm_colorspec_dict(struct pam * const pamP,
+                   tuple        const color);
+
+const char *
+pnm_colorspec_dict_close(struct pam * const pamP,
+                         tuple        const color);
+
+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/path.c b/lib/path.c
index 10ae92d2..8d53eb9e 100644
--- a/lib/path.c
+++ b/lib/path.c
@@ -58,13 +58,156 @@
 /* NOTE NOTE NOTE
 
    In all the path logic below, we call the direction of increasing row
-   number "up" because we think of the raster as standard Cartesian
+   number "up" because we think of the raster as a standard Cartesian
    plane.  So visualize the image as upside down in the first quadrant
    of the Cartesian plane -- the real top left corner of the image is at
    the origin.
 */
 
 
+ppmd_pathleg
+ppmd_makeLineLeg(ppmd_point const point) {
+
+    ppmd_pathleg retval;
+
+    retval. type = PPMD_PATHLEG_LINE;
+
+    retval.u.linelegparms.end = point;
+
+    return retval;
+}
+
+
+
+ppmd_pathbuilder *
+ppmd_pathbuilder_create() {
+
+    ppmd_pathbuilder * retval;
+
+    MALLOCVAR(retval);
+
+    if (!retval)
+        pm_error("Failed to allocate memory "
+                 "for a ppmd_pathuilder structure");
+
+    retval->path.version = 0;
+    retval->path.legCount = 0;
+    retval->path.legSize = sizeof(ppmd_pathleg);
+    retval->path.legs = NULL;
+
+    retval->begIsSet = false;
+    retval->legsAreAutoAllocated = true;
+    retval->legsAllocSize = 0;
+
+    return retval;
+}
+
+
+
+void
+ppmd_pathbuilder_destroy(ppmd_pathbuilder * const pathBuilderP) {
+
+    if (pathBuilderP->legsAreAutoAllocated) {
+        if (pathBuilderP->path.legs)
+            free(pathBuilderP->path.legs);
+    }
+    free(pathBuilderP);
+}
+
+
+
+void
+ppmd_pathbuilder_setLegArray(ppmd_pathbuilder * const pathBuilderP,
+                             ppmd_pathleg *     const legs,
+                             unsigned int       const legCount) {
+
+    if (pathBuilderP->path.legs)
+        pm_error("Legs array is already set up");
+
+    if (legCount < 1)
+        pm_error("Leg array size must be at least one leg in size");
+
+    if (legs == NULL)
+        pm_error("Leg array pointer is null");
+
+    pathBuilderP->legsAreAutoAllocated = false;
+    
+    pathBuilderP->legsAllocSize = legCount;
+
+    pathBuilderP->path.legs = legs;
+}
+
+
+
+void
+ppmd_pathbuilder_preallocLegArray(ppmd_pathbuilder * const pathBuilderP,
+                                  unsigned int       const legCount) {
+
+    if (pathBuilderP->path.legs)
+        pm_error("Legs array is already set up");
+
+    if (legCount < 1)
+        pm_error("Leg array size must be at least one leg in size");
+
+    MALLOCARRAY(pathBuilderP->path.legs, legCount);
+
+    if (!pathBuilderP->path.legs)
+        pm_error("Unable to allocate memory for %u legs", legCount);
+
+    pathBuilderP->legsAllocSize = legCount;
+}
+
+
+
+void
+ppmd_pathbuilder_setBegPoint(ppmd_pathbuilder * const pathBuilderP,
+                             ppmd_point         const begPoint) {
+
+    pathBuilderP->path.begPoint = begPoint;
+    
+    pathBuilderP->begIsSet = true;
+}
+
+
+
+void
+ppmd_pathbuilder_addLineLeg(ppmd_pathbuilder * const pathBuilderP,
+                            ppmd_pathleg       const leg) {
+
+    if (!pathBuilderP->begIsSet)
+        pm_error("Attempt to add a leg to a path when the "
+                 "beginning point of the path has not been set");
+
+    if (pathBuilderP->path.legCount + 1 > pathBuilderP->legsAllocSize) {
+        if (pathBuilderP->legsAreAutoAllocated) {
+            pathBuilderP->legsAllocSize =
+                MAX(16, pathBuilderP->legsAllocSize * 2);
+
+            REALLOCARRAY(pathBuilderP->path.legs, 
+                         pathBuilderP->legsAllocSize);
+
+            if (pathBuilderP->path.legs == NULL)
+                pm_error("Unable to allocate memory for %u legs", 
+                         pathBuilderP->legsAllocSize);
+        } else
+            pm_error("Out of space in user-supplied legs array "
+                     "(has space for %u legs)", pathBuilderP->legsAllocSize);
+    }
+
+    assert(pathBuilderP->path.legCount + 1 <= pathBuilderP->legsAllocSize);
+
+    pathBuilderP->path.legs[pathBuilderP->path.legCount++] = leg;
+}
+
+
+
+const ppmd_path *
+ppmd_pathbuilder_pathP(ppmd_pathbuilder * const pathBuilderP) {
+
+    return &pathBuilderP->path;
+}
+
+
 
 static bool
 pointEqual(ppmd_point const comparator,
@@ -411,12 +554,12 @@ fillLeg(ppmd_point  const begPoint,
 
 
 void
-ppmd_fill_path(pixel **      const pixels, 
-               int           const cols, 
-               int           const rows, 
-               pixval        const maxval,
-               ppmd_path *   const pathP,
-               pixel         const color) {
+ppmd_fill_path(pixel **          const pixels, 
+               int               const cols, 
+               int               const rows, 
+               pixval            const maxval,
+               const ppmd_path * const pathP,
+               pixel             const color) {
 /*----------------------------------------------------------------------------
    Draw a path which defines a closed figure (or multiple closed figures)
    and fill it in.
diff --git a/lib/pbmfont.h b/lib/pbmfont.h
index bedf57b9..c8b3934b 100644
--- a/lib/pbmfont.h
+++ b/lib/pbmfont.h
@@ -10,6 +10,70 @@ extern "C" {
 } /* to fake out automatic code indenters */
 #endif
 
+
+/* Maximum dimensions for fonts */
+
+#define  pbm_maxfontwidth()  65535
+#define  pbm_maxfontheight() 65535
+    /* These limits are not in the official Adobe BDF definition, but
+       should never be a problem for practical purposes, considering that
+       a 65536 x 65536 glyph occupies 4G pixels.
+
+       Note that the maximum line length allowed in a BDF file imposes
+       another restriction.
+    */
+
+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.
+    */
+
+enum pbmFontLoad { FIXED_DATA           = 0,
+                   LOAD_PBMSHEET        = 1,
+                   LOAD_BDFFILE         = 2,
+                   CONVERTED_TYPE1_FONT = 9 };
+
+static const char * const pbmFontOrigin[10] =
+                 {"Fixed data",                                   /* 0 */
+                  "Loaded from PBM sheet by libnetpbm",           /* 1 */
+                  "Loaded from BDF file by libnetpbm",            /* 2 */
+                  NULL, NULL, NULL, NULL, NULL, NULL,
+                  "Expanded from type 1 font structure by libnetpbm"}; /* 9 */
+
+enum pbmFontEncoding { ENCODING_UNKNOWN = 0,
+                       ISO646_1991_IRV = 1,   /* ASCII */
+                       ISO_8859_1 = 1000, ISO_8859_2, ISO_8859_3, ISO_8859_4,
+                       ISO_8859_5,   ISO_8859_6,   ISO_8859_7,   ISO_8859_8,
+                       ISO_8859_9,   ISO_8859_10,  ISO_8859_11,  ISO_8859_12,
+                       ISO_8859_13,  ISO_8859_14,  ISO_8859_15,  ISO_8859_16,
+                       ISO_10646 = 2000 };
+
+/* For future use */
+
+/* In addition to the above, the following CHARSET_REGISTRY-CHARSET_ENCODING
+   values have been observed in actual BDF files:
+
+  ADOBE-FONTSPECIFIC, DEC-DECTECH, GOST19768.74-1, IS13194-DEVANAGARI,
+  JISX0201.1976-0, KOI8-C, KOI8-R, MISC-FONTSPECIFIC,
+  MULEARABIC-0, MULEARABIC-1, MULEARABIC-2, MULEIPA-1, MULELAO-1,
+  OMRON_UDC_ZH-0, TIS620.2529-0, TIS620.2529-1, VISCII1-1, VISCII1.1-1,
+  XTIS-0
+ */
+
+
 struct glyph {
     /* A glyph consists of white borders and the "central glyph" which
        can be anything, but normally does not have white borders because
@@ -41,38 +105,244 @@ struct glyph {
            the top half and white on the bottom, this is an array of
            800 bytes, with the first 400 having value 0x01 and the
            last 400 having value 0x00.
+
+           Do not share bmap objects among glyphs if using
+           pbm_destroybdffont() or pbm_destroybdffont2() to free
+           the font/font2 structure.
         */
 };
 
+
 struct font {
     /* This describes a combination of font and character set.  Given
        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;
-        /* ?? Not used by Pbmtext */
+        /* 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[256];
-        /* glyph[i] is the glyph for code point i */
+        /* glyph[i] is the glyph for code point i.
+           Glyph objects must be unique for pbm_destroybdffont() to work.
+        */
     const bit ** oldfont;
         /* for compatibility with old pbmtext routines */
         /* oldfont is NULL if the font is BDF derived */
     int fcols, frows;
 };
 
-struct font* pbm_defaultfont(const char* const which);
-struct font*
+
+struct font2 {
+    /* Font structure for expanded character set.
+       Code points in the range 0...maxmaxglyph are loaded.
+       Loaded code point is in the range 0..maxglyph .
+     */
+
+    /* 'size' and 'len' are necessary in order to provide forward and
+       backward compatibility between library functions and calling programs
+       as this structure grows.  See struct pam in pam.h.
+     */
+    unsigned int size;
+        /* The storage size of this entire structure, in bytes */
+
+    unsigned int len;
+        /* The length, in bytes, of the information in this structure.
+           The information starts in the first byte and is contiguous.
+           This cannot be greater than 'size'
+        */
+
+    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
+
+           Glyph objects must be unique for pbm_destroybdffont2() to work.
+           For example space and non-break-space are often identical at the
+           image data level; they must be loaded into separate memory
+           locations if using pbm_destroybdffont2().
+         */
+
+    PM_WCHAR maxglyph;
+        /* max code point for glyphs, including vacant slots max value of
+           above i
+        */
+
+    void * selector;
+        /* Reserved
+
+           Bit array or structure indicating which code points to load.
+
+           When NULL, all available code points up to maxmaxglyph, inclusive
+           are loaded.
+       */
+
+    PM_WCHAR maxmaxglyph;
+        /* Code points above this value are not loaded, even if they occur
+           in the BDF font file
+        */
+
+    const bit ** oldfont;
+        /* For compatibility with old pbmtext routines.
+           Valid only when data is in the form of a PBM sheet
+        */
+
+    unsigned int fcols, frows;
+        /* For compatibility with old pbmtext routines.
+           Valid only when oldfont is non-NULL
+        */
+
+    unsigned int bit_format;
+        /* PBM_FORMAT:   glyph data: 1 byte per pixel (like P1, but not ASCII)
+           RPBM_FORMAT:  glyph data: 1 bit per pixel
+           Currently only PBM_FORMAT is possible
+        */
+
+    unsigned int total_chars;
+        /* Number of glyphs defined in font file, as stated in the CHARS line
+           of the BDF file PBM sheet font.  Always 96
+        */
+
+    unsigned int chars;
+        /* Number of glyphs actually loaded into structure
+
+           Maximum: total_chars
+
+           Less than total_chars when a subset of the file is loaded
+           PBM sheet font: always 96 */
+
+    enum pbmFontLoad load_fn;
+        /* Description of the function that created the structure and loaded
+           the glyph data
+
+           Used to choose a string to show in verbose messages.
+
+           FIXED_DATA (==0) means memory for this structure was not
+           dynamically allocated by a function; all data is hardcoded in
+           source code and resides in static data.  See file pbmfontdata1.c
+        */
+
+    PM_WCHAR default_char;
+        /* Code index of what to show when there is no glyph for a requested
+           code Available in many BDF fonts between STARPROPERTIES -
+           ENDPROPERTIES.
+
+           Set to value read from BDF font file.
+
+           Common values are 0, 32, 8481, 32382, 33, 159, 255.
+        */
+
+    unsigned int default_char_defined;
+        /* boolean
+           TRUE: above field is valid; DEFAULT_CHAR is defined in font file.
+           FALSE: font file has no DEFAULT_CHAR field.
+        */
+
+    const char * name;
+        /* Name of the font.  Available in BDF fonts.
+           NULL means no name.
+        */
+
+    enum pbmFontEncoding charset;
+        /* Reserved for future use.
+           Set by analyzing following charset_string.
+        */
+
+    const char * charset_string;
+        /* Charset registry and encoding.
+           Available in most BDF fonts between STARPROPERTIES - ENDPROPERTIES.
+           NULL means no name.
+        */
+};
+
+
+/* PBM_FONT2_STRUCT_SIZE(x) tells you how big a struct font2 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.
+*/
+
+/* Some compilers are really vigilant and recognize it as an error
+   to cast a 64 bit address to a 32 bit type.  Hence the roundabout
+   casting.  See PAM_MEMBER_OFFSET in pam.h .
+*/
+
+
+#define PBM_FONT2_MEMBER_OFFSET(mbrname) \
+  ((size_t)(unsigned long)(char*)&((struct font2 *)0)->mbrname)
+#define PBM_FONT2_MEMBER_SIZE(mbrname) \
+  sizeof(((struct font2 *)0)->mbrname)
+#define PBM_FONT2_STRUCT_SIZE(mbrname) \
+  (PBM_FONT2_MEMBER_OFFSET(mbrname) + PBM_FONT2_MEMBER_SIZE(mbrname))
+
+
+struct font *
+pbm_defaultfont(const char* const which);
+
+struct font2 *
+pbm_defaultfont2(const char* const which);
+
+struct font *
 pbm_dissectfont(const bit ** const font,
                 unsigned int const frows,
                 unsigned int const fcols);
-struct font* pbm_loadfont(const char * const filename);
-struct font* pbm_loadpbmfont(const char * const filename);
-struct font* pbm_loadbdffont(const char * const filename);
-void pbm_dumpfont(struct font * const fnP);
+
+struct font *
+pbm_loadfont(const char * const filename);
+
+struct font2 *
+pbm_loadfont2(const    char * const filename,
+              PM_WCHAR        const maxmaxglyph);
+
+struct font *
+pbm_loadpbmfont(const char * const filename);
+
+struct font2 *
+pbm_loadpbmfont2(const char * const filename);
+
+struct font *
+pbm_loadbdffont(const char * const filename);
+
+struct font2 *
+pbm_loadbdffont2(const char * const filename,
+                 PM_WCHAR     const maxmaxglyph);
+
+struct font2 *
+pbm_loadbdffont2_select(const char * const filename,
+                        PM_WCHAR     const maxmaxglyph,
+                        const void * const selector);
+
+void
+pbm_createbdffont2_base(struct font2 ** const font2P,
+                        PM_WCHAR        const maxmaxglyph);
+
+void
+pbm_destroybdffont(struct font * const fontP);
+
+void
+pbm_destroybdffont2_base(struct font2 * const font2P);
+
+void
+pbm_destroybdffont2(struct font2 * const font2P);
+
+struct font2 *
+pbm_expandbdffont(const struct font * const font);
+
+void
+pbm_dumpfont(struct font * const fontP,
+             FILE *        const ofP);
 
 #ifdef __cplusplus
 }
diff --git a/lib/pbmfontdata.h b/lib/pbmfontdata.h
new file mode 100644
index 00000000..7ac63abc
--- /dev/null
+++ b/lib/pbmfontdata.h
@@ -0,0 +1,7 @@
+extern struct font pbm_defaultFixedfont;
+extern struct font pbm_defaultBdffont;
+
+extern struct font2 const pbm_defaultFixedfont2;
+extern struct font2 const pbm_defaultBdffont2;
+
+extern struct font2 const * pbm_builtinFonts[];
diff --git a/lib/pbmfontdata0.c b/lib/pbmfontdata0.c
new file mode 100644
index 00000000..dfafc317
--- /dev/null
+++ b/lib/pbmfontdata0.c
@@ -0,0 +1,9 @@
+#include "pbm.h"
+#include "pbmfont.h"
+#include "pbmfontdata.h"
+
+struct font2 const * pbm_builtinFonts[] = {
+    &pbm_defaultFixedfont2,
+    &pbm_defaultBdffont2,
+    NULL,
+};
diff --git a/lib/pbmfontdata1.c b/lib/pbmfontdata1.c
new file mode 100644
index 00000000..ab6ce28d
--- /dev/null
+++ b/lib/pbmfontdata1.c
@@ -0,0 +1,269 @@
+#include "pbmfont.h"
+#include "pbmfontdata.h"
+
+/* Default fixed-width font
+   All glyphs fit into a 7 x 12 rectangular cell.
+   Raster data is provided completely.
+
+   This kind of font, commonly used on early bitmap displays, is
+   sometimes called "cell-char".
+
+
+   Note: In the current version default_fixedfont is fixed data, which
+   means it is always accessible, without any preparatory action.
+   However, the storage format may change in future versions.
+
+   To ensure future compatibility call pbm_defaultfont() before accessing
+   default_fixedfont.
+
+   The Netpbm development tool 'genfontc' generates C source code like this
+   from a libnetpbm font file or builtin font.
+*/
+
+static struct glyph glFxd[96] = {
+/*  32 character   */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  33 character ! */
+{7,12,0,0,7,"\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  34 character " */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  35 character # */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\1\1\1\1\1\1\0\0\1\0\1\0\0\0\0\1\0\1\0\0\1\1\1\1\1\1\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  36 character $ */
+{7,12,0,0,7,"\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  37 character % */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\0\0\1\0\0\0\0\1\1\0\0\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  38 character & */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\1\0\1\1\1\0\1\0\1\0\0\0\1\0\0\1\0\0\0\1\1\0\0\1\1\1\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  39 character ' */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  40 character ( */
+{7,12,0,0,7,"\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0" },
+/*  41 character ) */
+{7,12,0,0,7,"\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  42 character * */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  43 character + */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  44 character , */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  45 character - */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  46 character . */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  47 character / */
+{7,12,0,0,7,"\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  48 character 0 */
+{7,12,0,0,7,"\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  49 character 1 */
+{7,12,0,0,7,"\0\0\0\1\0\0\0\0\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  50 character 2 */
+{7,12,0,0,7,"\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  51 character 3 */
+{7,12,0,0,7,"\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  52 character 4 */
+{7,12,0,0,7,"\0\0\0\0\1\0\0\0\0\0\1\1\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\1\1\1\1\1\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  53 character 5 */
+{7,12,0,0,7,"\0\1\1\1\1\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  54 character 6 */
+{7,12,0,0,7,"\0\0\0\1\1\1\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  55 character 7 */
+{7,12,0,0,7,"\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  56 character 8 */
+{7,12,0,0,7,"\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  57 character 9 */
+{7,12,0,0,7,"\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  58 character : */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  59 character ; */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  60 character < */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  61 character = */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  62 character > */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  63 character ? */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  64 character @ */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\1\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\0\1\1\1\0\1\0\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  65 character A */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  66 character B */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  67 character C */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  68 character D */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  69 character E */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\1\0\0\0\0\1\1\1\0\0\0\0\1\0\1\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\0\1\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  70 character F */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\1\0\1\0\0\1\1\1\0\0\0\0\1\0\1\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  71 character G */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  72 character H */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  73 character I */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  74 character J */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  75 character K */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\1\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\0\1\1\1\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\1\1\1\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  76 character L */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  77 character M */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\0\0\0\1\1\0\1\1\0\1\1\0\0\1\1\0\1\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\0\0\1\0\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  78 character N */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\0\0\1\1\1\0\1\1\0\0\1\0\0\1\1\0\0\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\0\1\1\0\0\1\0\0\1\1\0\1\1\1\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  79 character O */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  80 character P */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\1\0\0\1\1\0\0\1\0\0\0\1\0\0\1\0\0\1\1\0\0\1\1\1\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  81 character Q */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  82 character R */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\1\1\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  83 character S */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\1\0\0\1\0\0\0\1\0\0\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  84 character T */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\1\1\1\1\1\0\0\1\0\0\1\1\0\0\1\0\0\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  85 character U */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\1\0\1\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  86 character V */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\1\0\1\1\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  87 character W */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\1\0\1\1\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  88 character X */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\1\0\0\0\1\0\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  89 character Y */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  90 character Z */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  91 character [ */
+{7,12,0,0,7,"\0\0\0\1\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\0\0\0" },
+/*  92 character \ */
+{7,12,0,0,7,"\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  93 character ] */
+{7,12,0,0,7,"\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\0\0\0\0\0\0\0\0\0\0" },
+/*  94 character ^ */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  95 character _ */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1" },
+/*  96 character ` */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  97 character a */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\0\0\0\0\1\0\0\0\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  98 character b */
+{7,12,0,0,7,"\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  99 character c */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 100 character d */
+{7,12,0,0,7,"\0\0\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 101 character e */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 102 character f */
+{7,12,0,0,7,"\0\0\0\1\1\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 103 character g */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\1\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\1\1\0\0" },
+/* 104 character h */
+{7,12,0,0,7,"\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\1\1\0\0\0\1\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 105 character i */
+{7,12,0,0,7,"\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 106 character j */
+{7,12,0,0,7,"\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\1\1\0\0\0" },
+/* 107 character k */
+{7,12,0,0,7,"\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\0\0\1\1\0\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 108 character l */
+{7,12,0,0,7,"\0\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 109 character m */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\1\0\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\1\1\0\1\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 110 character n */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 111 character o */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 112 character p */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\1\1\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0" },
+/* 113 character q */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\1\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1" },
+/* 114 character r */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\1\1\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 115 character s */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 116 character t */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 117 character u */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 118 character v */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\1\0\0\0\0\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 119 character w */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 120 character x */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\0\1\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\1\0\0\0\1\0\1\1\0\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 121 character y */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\1\0\0\0\0\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" },
+/* 122 character z */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\0\0\1\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 123 character { */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0" },
+/* 124 character | */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0" },
+/* 125 character } */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0" },
+/* 126 character ~ */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\1\0\0\1\0\1\0\1\0\0\1\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 127 character (Control character, retained for backward compatibility) */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }
+};
+
+
+
+struct font pbm_defaultFixedfont = { 7, 12, 0, 0, {
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+glFxd+ 0, glFxd+ 1, glFxd+ 2, glFxd+ 3, glFxd+ 4, glFxd+ 5,
+glFxd+ 6, glFxd+ 7, glFxd+ 8, glFxd+ 9, glFxd+10, glFxd+11,
+glFxd+12, glFxd+13, glFxd+14, glFxd+15, glFxd+16, glFxd+17,
+glFxd+18, glFxd+19, glFxd+20, glFxd+21, glFxd+22, glFxd+23,
+glFxd+24, glFxd+25, glFxd+26, glFxd+27, glFxd+28, glFxd+29,
+glFxd+30, glFxd+31, glFxd+32, glFxd+33, glFxd+34, glFxd+35,
+glFxd+36, glFxd+37, glFxd+38, glFxd+39, glFxd+40, glFxd+41,
+glFxd+42, glFxd+43, glFxd+44, glFxd+45, glFxd+46, glFxd+47,
+glFxd+48, glFxd+49, glFxd+50, glFxd+51, glFxd+52, glFxd+53,
+glFxd+54, glFxd+55, glFxd+56, glFxd+57, glFxd+58, glFxd+59,
+glFxd+60, glFxd+61, glFxd+62, glFxd+63, glFxd+64, glFxd+65,
+glFxd+66, glFxd+67, glFxd+68, glFxd+69, glFxd+70, glFxd+71,
+glFxd+72, glFxd+73, glFxd+74, glFxd+75, glFxd+76, glFxd+77,
+glFxd+78, glFxd+79, glFxd+80, glFxd+81, glFxd+82, glFxd+83,
+glFxd+84, glFxd+85, glFxd+86, glFxd+87, glFxd+88, glFxd+89,
+glFxd+90, glFxd+91, glFxd+92, glFxd+93, glFxd+94,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, NULL, 0, 0
+};
+
+struct font2 const pbm_defaultFixedfont2 = {
+  sizeof(pbm_defaultFixedfont2),         /* len */
+  PBM_FONT2_STRUCT_SIZE(charset_string), /* size */
+  7, 12, 0, 0,                    /* maxwidth, maxheight, x, y */
+  pbm_defaultFixedfont.glyph,     /* glyph table */
+  255, NULL, 255,                 /* maxglyph, selector, maxmaxglyph */
+  NULL, 0, 0,                     /* oldfont, fcols, frows */
+  PBM_FORMAT,                     /* bit_format */
+  96, 96,                         /* total_chars, chars */
+  FIXED_DATA,                     /* load_fn */
+  32, 1,                          /* default_char, default_char_defined */
+  (char *) "builtin fixed",       /* name */
+  ISO646_1991_IRV, (char *)"ASCII"  /* charset, charset_string */
+};
+
+
+
diff --git a/lib/pbmfontdata2.c b/lib/pbmfontdata2.c
new file mode 100644
index 00000000..11dd84e6
--- /dev/null
+++ b/lib/pbmfontdata2.c
@@ -0,0 +1,463 @@
+#include "pbmfont.h"
+#include "pbmfontdata.h"
+
+/* Default proportional font.
+   BDF-style advance value, bounding box dimensions and point of origin.
+
+
+   Note: In the current version default_bdffont is fixed data, which
+   means it is always accessible, without any preparatory action.
+   However, the storage format may change in future versions.
+
+   To ensure future compatibility call pbm_defaultfont() before accessing
+   default_bdffont.
+
+   The Netpbm development tool 'genfontc' generates C source code like this
+   from a libnetpbm font file or builtin font.
+*/
+
+static struct glyph glBdf[191] = {
+/*  32 character    */
+{ 1, 1, 0, 0, 3, "\0" },
+/*  33 character !  */
+{ 1, 9, 1, 0, 3, "\1\1\1\1\1\1\1\0\1" },
+/*  34 character "  */
+{ 3, 3, 1, 6, 5, "\1\0\1\1\0\1\1\0\1" },
+/*  35 character #  */
+{ 5, 8, 0, 0, 6, "\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\1\0\1\0\0\1\0\1\0" },
+/*  36 character $  */
+{ 5, 11, 0, -1, 6, "\0\0\1\0\0\0\1\1\1\0\1\0\1\0\1\1\0\1\0\0\0\1\1\0\0\0\0\1\1\0\0\0\1\0\1\0\0\1\0\1\1\0\1\0\1\0\1\1\1\0\0\0\1\0\0" },
+/*  37 character %  */
+{ 8, 9, 0, 0, 9, "\0\1\1\0\0\0\1\1\1\0\0\1\1\1\1\0\1\0\0\1\0\1\0\0\0\1\1\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\1\0\1\1\0\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\0" },
+/*  38 character &  */
+{ 9, 9, 0, 0, 10, "\0\0\0\1\1\0\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\0\1\1\0\1\1\1\0\1\1\1\1\0\0\1\0\1\1\0\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\0\0\1\1\1\0\1\0\1\1\1\1\0\1\1\0" },
+/*  39 character '  */
+{ 2, 3, 1, 6, 4, "\1\1\0\1\1\0" },
+/*  40 character (  */
+{ 3, 12, 1, -3, 5, "\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1" },
+/*  41 character )  */
+{ 3, 12, 0, -3, 5, "\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0" },
+/*  42 character *  */
+{ 5, 5, 0, 4, 6, "\0\0\1\0\0\1\0\1\0\1\0\1\1\1\0\1\0\1\0\1\0\0\1\0\0" },
+/*  43 character +  */
+{ 5, 5, 1, 1, 7, "\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0" },
+/*  44 character ,  */
+{ 2, 3, 0, -2, 3, "\0\1\0\1\1\0" },
+/*  45 character -  */
+{ 5, 1, 1, 3, 8, "\1\1\1\1\1" },
+/*  46 character .  */
+{ 1, 1, 1, 0, 3, "\1" },
+/*  47 character /  */
+{ 3, 9, 0, 0, 3, "\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0" },
+/*  48 character 0  */
+{ 5, 9, 0, 0, 6, "\0\1\1\1\0\1\1\0\1\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\1\0\1\1\0\1\1\1\0" },
+/*  49 character 1  */
+{ 4, 9, 0, 0, 6, "\0\0\1\0\0\1\1\0\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\1\1" },
+/*  50 character 2  */
+{ 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\1\1\1\1\1" },
+/*  51 character 3  */
+{ 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\1\1\1\0\0\0\0\0\1\0\0\0\0\1\1\0\0\0\1\0\1\1\1\0" },
+/*  52 character 4  */
+{ 5, 9, 0, 0, 6, "\0\0\0\1\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\1\1\1\1\1\0\0\0\1\0\0\0\0\1\0" },
+/*  53 character 5  */
+{ 5, 9, 0, 0, 6, "\0\0\1\1\1\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\1\1\0\0\1\1\0\1\1\1\0" },
+/*  54 character 6  */
+{ 5, 9, 0, 0, 6, "\0\0\0\1\1\0\1\1\0\0\0\1\0\0\0\1\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\0" },
+/*  55 character 7  */
+{ 5, 9, 0, 0, 6, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\0\1\0\0\0" },
+/*  56 character 8  */
+{ 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\0\1\1\1\0" },
+/*  57 character 9  */
+{ 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\1\0\0\0\1\0\0\0\1\1\0\1\1\0\0\0" },
+/*  58 character :  */
+{ 1, 6, 1, 0, 3, "\1\0\0\0\0\1" },
+/*  59 character ;  */
+{ 2, 8, 0, -2, 3, "\0\1\0\0\0\0\0\0\0\0\0\1\0\1\1\0" },
+/*  60 character <  */
+{ 6, 5, 0, 1, 8, "\0\0\0\0\1\1\0\0\1\1\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1" },
+/*  61 character =  */
+{ 5, 3, 1, 2, 7, "\1\1\1\1\1\0\0\0\0\0\1\1\1\1\1" },
+/*  62 character >  */
+{ 6, 5, 1, 1, 8, "\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\1\1\0\0\1\1\0\0\0\0" },
+/*  63 character ?  */
+{ 4, 9, 0, 0, 5, "\0\1\1\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\1\0\0" },
+/*  64 character @  */
+{ 10, 11, 1, -2, 11, "\0\0\0\0\1\1\1\1\0\0\0\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\1\1\0\1\0\1\1\0\0\1\0\0\1\0\0\1\1\0\1\0\0\0\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\1\0\1\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0" },
+/*  65 character A  */
+{ 9, 9, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
+/*  66 character B  */
+{ 7, 9, 0, 0, 8, "\1\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\1\1\1\1\1\1\0" },
+/*  67 character C  */
+{ 7, 9, 0, 0, 8, "\0\0\1\1\1\0\1\0\1\1\0\0\1\1\0\1\0\0\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\0\1\1\0\0\1\1\0\0\1\1\1\1\0" },
+/*  68 character D  */
+{ 8, 9, 0, 0, 9, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\1\1\1\1\1\1\0\0" },
+/*  69 character E  */
+{ 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
+/*  70 character F  */
+{ 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0" },
+/*  71 character G  */
+{ 8, 9, 0, 0, 9, "\0\0\1\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\0\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/*  72 character H  */
+{ 8, 9, 0, 0, 9, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\1\1\1\0\0\1\1\1" },
+/*  73 character I  */
+{ 3, 9, 0, 0, 4, "\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/*  74 character J  */
+{ 4, 9, 0, 0, 4, "\0\1\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\1\0\1\0\1\1\0\0" },
+/*  75 character K  */
+{ 8, 9, 0, 0, 8, "\1\1\1\0\1\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\1\1\1\0\0\1\1\1" },
+/*  76 character L  */
+{ 6, 9, 0, 0, 7, "\1\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\1\1\1\1\1\1\1" },
+/*  77 character M  */
+{ 11, 9, 0, 0, 11, "\1\1\0\0\0\0\0\0\0\1\1\0\1\1\0\0\0\0\0\1\1\0\0\1\1\0\0\0\0\0\1\1\0\0\1\0\1\0\0\0\1\0\1\0\0\1\0\1\0\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\1\1\1\0\0\1\0\0\1\1\1" },
+/*  78 character N  */
+{ 9, 9, 0, 0, 9, "\1\1\0\0\0\0\1\1\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\0\0\0\1\0" },
+/*  79 character O  */
+{ 8, 9, 0, 0, 9, "\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/*  80 character P  */
+{ 7, 9, 0, 0, 7, "\1\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0" },
+/*  81 character Q  */
+{ 8, 11, 0, -2, 9, "\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\0\0\1\1" },
+/*  82 character R  */
+{ 8, 9, 0, 0, 8, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\0\1\1\1\1\1\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\1\1\1\0\0\0\1\1" },
+/*  83 character S  */
+{ 6, 9, 0, 0, 7, "\0\1\1\1\0\1\1\0\0\0\1\1\1\0\0\0\0\1\0\1\1\0\0\0\0\0\1\1\1\0\0\0\0\0\1\1\1\0\0\0\0\1\1\1\0\0\1\1\1\0\1\1\1\0" },
+/*  84 character T  */
+{ 7, 9, 0, 0, 7, "\1\1\1\1\1\1\1\1\0\0\1\0\0\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0" },
+/*  85 character U  */
+{ 8, 9, 0, 0, 8, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/*  86 character V  */
+{ 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0" },
+/*  87 character W  */
+{ 12, 9, 0, 0, 12, "\1\1\1\0\1\1\1\0\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\1\0\0\0\0\0\1\0\1\0\1\0\1\0\0\0\0\0\1\1\0\0\1\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0" },
+/*  88 character X  */
+{ 8, 9, 0, 0, 8, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\1\0\1\1\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\0\1\0\1\1\1\0\0\1\1\1" },
+/*  89 character Y  */
+{ 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\0\0\0" },
+/*  90 character Z  */
+{ 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\1\1\1\1" },
+/*  91 character [  */
+{ 3, 12, 1, -3, 5, "\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1" },
+/*  92 character \  */
+{ 3, 9, 0, 0, 3, "\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1" },
+/*  93 character ]  */
+{ 3, 12, 0, -3, 5, "\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1" },
+/*  94 character ^  */
+{ 5, 5, 0, 4, 6, "\0\0\1\0\0\0\1\0\1\0\0\1\0\1\0\1\0\0\0\1\1\0\0\0\1" },
+/*  95 character _  */
+{ 6, 1, 0, -3, 6, "\1\1\1\1\1\1" },
+/*  96 character `  */
+{ 2, 3, 1, 6, 4, "\0\1\1\0\1\1" },
+/*  97 character a  */
+{ 5, 6, 1, 0, 6, "\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/*  98 character b  */
+{ 5, 9, 0, 0, 6, "\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0" },
+/*  99 character c  */
+{ 4, 6, 1, 0, 5, "\0\1\1\0\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\1\0\1\1\0" },
+/* 100 character d  */
+{ 5, 9, 1, 0, 6, "\0\0\1\1\0\0\0\0\1\0\0\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/* 101 character e  */
+{ 5, 6, 1, 0, 6, "\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
+/* 102 character f  */
+{ 3, 9, 0, 0, 3, "\0\0\1\0\1\0\0\1\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0" },
+/* 103 character g  */
+{ 5, 9, 1, -3, 6, "\0\1\1\1\1\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\0\1\0\0\0\0\1\1\1\0\1\0\0\0\1\1\0\0\0\1\0\1\1\1\0" },
+/* 104 character h  */
+{ 6, 9, 0, 0, 6, "\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" },
+/* 105 character i  */
+{ 3, 9, 0, 0, 3, "\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 106 character j  */
+{ 2, 12, 0, -3, 3, "\0\1\0\0\0\0\1\1\0\1\0\1\0\1\0\1\0\1\0\1\0\1\1\0" },
+/* 107 character k  */
+{ 6, 9, 0, 0, 6, "\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\0\0\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\1" },
+/* 108 character l  */
+{ 3, 9, 0, 0, 3, "\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 109 character m  */
+{ 9, 6, 0, 0, 9, "\1\0\1\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1\0\1\1" },
+/* 110 character n  */
+{ 6, 6, 0, 0, 6, "\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" },
+/* 111 character o  */
+{ 4, 6, 1, 0, 6, "\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 112 character p  */
+{ 5, 9, 0, -3, 6, "\1\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\0" },
+/* 113 character q  */
+{ 5, 9, 1, -3, 6, "\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\1\1\1" },
+/* 114 character r  */
+{ 4, 6, 0, 0, 4, "\1\0\1\1\0\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\1\1\1\0" },
+/* 115 character s  */
+{ 4, 6, 1, 0, 6, "\0\1\1\1\1\0\0\1\1\1\0\0\0\0\1\1\1\0\0\1\1\1\1\0" },
+/* 116 character t  */
+{ 4, 7, 0, 0, 4, "\0\1\0\0\1\1\1\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\1" },
+/* 117 character u  */
+{ 6, 6, 0, 0, 6, "\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
+/* 118 character v  */
+{ 6, 6, 0, 0, 6, "\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0" },
+/* 119 character w  */
+{ 9, 6, 0, 0, 9, "\1\1\1\0\1\1\0\1\1\0\1\0\0\1\0\0\1\0\0\1\1\0\1\0\1\1\0\0\0\1\0\1\0\1\0\0\0\0\1\1\0\1\0\0\0\0\0\1\0\0\1\0\0\0" },
+/* 120 character x  */
+{ 5, 6, 1, 0, 6, "\1\1\0\1\1\0\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\1\0\1\1\0\1\1" },
+/* 121 character y  */
+{ 6, 9, 0, -3, 6, "\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" },
+/* 122 character z  */
+{ 4, 6, 1, 0, 6, "\1\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\1" },
+/* 123 character {  */
+{ 4, 12, 1, -3, 6, "\0\0\1\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\1" },
+/* 124 character |  */
+{ 1, 9, 1, 0, 3, "\1\1\1\1\1\1\1\1\1" },
+/* 125 character }  */
+{ 4, 12, 0, -3, 6, "\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\1\1\0\0" },
+/* 126 character ~  */
+{ 6, 2, 0, 3, 7, "\0\1\1\0\0\1\1\0\0\1\1\0" },
+/* 160 */
+{ 1, 1, 0, 0, 3, "\0" },
+/* 161 */
+{ 1, 9, 1, -3, 4, "\1\0\1\1\1\1\1\1\1" },
+/* 162 */
+{ 5, 8, 1, -1, 6, "\0\0\0\0\1\0\1\1\1\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\1\0\0\1\0\1\1\1\0\1\0\0\0\0" },
+/* 163 */
+{ 5, 9, 0, 0, 6, "\0\0\1\1\0\0\1\0\0\1\0\1\0\0\0\0\1\0\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\1\1\1\0\1\1" },
+/* 164 */
+{ 6, 7, 1, 1, 7, "\1\0\0\0\0\1\0\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\0\1\0\0\0\0\1" },
+/* 165 */
+{ 5, 9, 0, 0, 6, "\1\0\0\0\1\1\0\0\0\1\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\1\1\0" },
+/* 166 */
+{ 1, 9, 1, 0, 3, "\1\1\1\0\0\1\1\1\1" },
+/* 167 */
+{ 4, 12, 1, -3, 6, "\0\1\1\1\1\0\0\1\1\1\0\0\0\1\1\0\1\0\1\1\1\0\0\1\1\0\0\1\1\1\0\1\0\1\1\0\0\0\1\1\1\0\0\1\1\1\1\0" },
+/* 168 */
+{ 3, 1, 0, 7, 3, "\1\0\1" },
+/* 169 */
+{ 9, 9, 1, 0, 11, "\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\1\1\0\1\0\1\0\1\0\0\1\0\0\1\1\0\1\0\0\0\0\0\1\1\0\1\0\0\1\0\0\1\0\1\0\1\1\1\0\1\0\0\1\1\0\0\0\1\1\0\0\0\0\1\1\1\0\0\0" },
+/* 170 */
+{ 3, 6, 1, 3, 5, "\1\1\0\0\0\1\1\1\1\1\0\1\0\0\0\1\1\1" },
+/* 171 */
+{ 5, 5, 1, 0, 7, "\0\0\1\0\1\0\1\0\1\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1" },
+/* 172 */
+{ 6, 4, 1, 1, 8, "\1\1\1\1\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1" },
+/* 173 */
+{ 4, 1, 1, 3, 6, "\1\1\1\1" },
+/* 174 */
+{ 9, 9, 1, 0, 11, "\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\1\1\0\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\1\1\0\0\1\1\0\0\1\0\1\0\0\1\1\1\0\1\0\1\0\1\0\0\1\1\0\0\0\1\1\0\0\0\1\1\1\1\0\0\0" },
+/* 175 */
+{ 4, 1, 0, 7, 4, "\1\1\1\1" },
+/* 176 */
+{ 4, 4, 0, 5, 5, "\0\1\1\0\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 177 */
+{ 5, 7, 1, 0, 7, "\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\1\1" },
+/* 178 */
+{ 4, 5, 0, 4, 4, "\0\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1" },
+/* 179 */
+{ 3, 5, 0, 4, 4, "\1\1\1\0\0\1\0\1\0\0\0\1\1\1\0" },
+/* 180 */
+{ 2, 2, 1, 7, 4, "\0\1\1\0" },
+/* 181 */
+{ 6, 9, 0, -3, 6, "\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\1\0\0\0\0\0\1\0\0\0\0\0\1\1\0\0\0" },
+/* 182 */
+{ 6, 12, 0, -3, 7, "\0\1\1\1\1\1\1\1\1\0\1\0\1\1\1\0\1\0\1\1\1\0\1\0\1\1\1\0\1\0\0\1\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0" },
+/* 183 */
+{ 1, 1, 1, 3, 3, "\1" },
+/* 184 */
+{ 3, 3, 0, -3, 3, "\0\1\0\0\0\1\1\1\1" },
+/* 185 */
+{ 3, 5, 0, 4, 4, "\0\1\0\1\1\0\0\1\0\0\1\0\1\1\1" },
+/* 186 */
+{ 3, 6, 1, 3, 5, "\0\1\0\1\0\1\1\0\1\0\1\0\0\0\0\1\1\1" },
+/* 187 */
+{ 5, 5, 0, 0, 7, "\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\1\0\1\0\1\0\1\0\0" },
+/* 188 */
+{ 9, 9, 0, 0, 9, "\0\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\1\0\1\0\0\0\1\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0" },
+/* 189 */
+{ 9, 9, 0, 0, 9, "\0\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\1\0\1\1\0\0\0\0\1\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\1\1\1" },
+/* 190 */
+{ 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\1\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\1\0\1\0\0\0\1\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0" },
+/* 191 */
+{ 4, 9, 0, -3, 5, "\0\0\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\1\1\0" },
+/* 192 */
+{ 9, 12, 0, 0, 9, "\0\0\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
+/* 193 */
+{ 9, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
+/* 194 */
+{ 9, 12, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
+/* 195 */
+{ 9, 12, 0, 0, 9, "\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
+/* 196 */
+{ 9, 11, 0, 0, 9, "\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
+/* 197 */
+{ 9, 12, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
+/* 198 */
+{ 10, 9, 0, 0, 11, "\0\0\1\1\1\1\1\1\1\1\0\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\1\1\1\1\0\0\1\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\0\0\1\1\1\1\1\1" },
+/* 199 */
+{ 7, 12, 0, -3, 8, "\0\0\1\1\1\0\1\0\1\1\0\0\1\1\0\1\0\0\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\0\1\1\0\0\1\1\0\0\1\1\1\1\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\1\1\0\0" },
+/* 200 */
+{ 7, 12, 0, 0, 8, "\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
+/* 201 */
+{ 7, 12, 0, 0, 8, "\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
+/* 202 */
+{ 7, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
+/* 203 */
+{ 7, 11, 0, 0, 8, "\0\0\1\0\1\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
+/* 204 */
+{ 3, 12, 0, 0, 4, "\1\0\0\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 205 */
+{ 3, 12, 0, 0, 4, "\0\0\1\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 206 */
+{ 3, 12, 0, 0, 4, "\0\1\0\1\0\1\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 207 */
+{ 3, 11, 0, 0, 4, "\1\0\1\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 208 */
+{ 8, 9, 0, 0, 9, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\1\1\1\1\1\1\0\0" },
+/* 209 */
+{ 9, 12, 0, 0, 9, "\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\1\1\0\0\0\0\1\1\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\0\0\0\1\0" },
+/* 210 */
+{ 8, 12, 0, 0, 9, "\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 211 */
+{ 8, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 212 */
+{ 8, 12, 0, 0, 9, "\0\0\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 213 */
+{ 8, 12, 0, 0, 9, "\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 214 */
+{ 8, 11, 0, 0, 9, "\0\0\1\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 215 */
+{ 5, 5, 1, 1, 7, "\1\0\0\0\1\0\1\0\1\0\0\0\1\0\0\0\1\0\1\0\1\0\0\0\1" },
+/* 216 */
+{ 9, 10, 0, 0, 9, "\0\0\0\0\0\0\0\0\1\0\0\1\1\1\1\0\1\0\0\1\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\1\0\0\1\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\0\1\1\0\0\1\0\1\1\1\1\0\0\0" },
+/* 217 */
+{ 8, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 218 */
+{ 8, 12, 0, 0, 8, "\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 219 */
+{ 8, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 220 */
+{ 8, 11, 0, 0, 8, "\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 221 */
+{ 9, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\0\0\0" },
+/* 222 */
+{ 7, 9, 0, 0, 7, "\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0" },
+/* 223 */
+{ 6, 9, 0, 0, 6, "\0\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\0\1\1\1\0\1\1\0" },
+/* 224 */
+{ 5, 9, 1, 0, 6, "\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/* 225 */
+{ 5, 9, 1, 0, 6, "\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/* 226 */
+{ 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/* 227 */
+{ 5, 9, 1, 0, 6, "\0\1\0\1\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/* 228 */
+{ 5, 8, 1, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/* 229 */
+{ 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/* 230 */
+{ 8, 6, 1, 0, 9, "\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\1\1\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\0\1\1\0\1\1\1\0" },
+/* 231 */
+{ 4, 9, 1, -3, 5, "\0\1\1\0\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\1\0\1\1\0\0\1\0\0\0\0\1\0\1\1\1\0" },
+/* 232 */
+{ 5, 9, 1, 0, 6, "\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
+/* 233 */
+{ 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
+/* 234 */
+{ 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
+/* 235 */
+{ 5, 8, 1, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
+/* 236 */
+{ 3, 9, 0, 0, 3, "\1\0\0\0\1\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 237 */
+{ 3, 9, 0, 0, 3, "\0\1\0\1\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 238 */
+{ 3, 9, 0, 0, 3, "\0\1\0\1\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 239 */
+{ 3, 8, 0, 0, 3, "\1\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 240 */
+{ 4, 9, 1, 0, 6, "\0\1\0\0\0\1\1\1\1\0\1\0\0\1\1\1\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 241 */
+{ 6, 9, 0, 0, 6, "\0\0\1\0\1\0\0\1\0\1\0\0\0\0\0\0\0\0\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" },
+/* 242 */
+{ 4, 9, 1, 0, 6, "\0\1\0\0\0\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 243 */
+{ 4, 9, 1, 0, 6, "\0\0\1\0\0\1\0\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 244 */
+{ 4, 9, 1, 0, 6, "\0\0\1\0\0\1\0\1\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 245 */
+{ 4, 9, 1, 0, 6, "\0\1\0\1\1\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 246 */
+{ 4, 8, 1, 0, 6, "\1\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 247 */
+{ 5, 5, 1, 1, 7, "\0\0\1\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\1\0\0" },
+/* 248 */
+{ 6, 7, 0, -1, 6, "\0\0\1\1\0\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\0\0\1\0\0\0\0\0" },
+/* 249 */
+{ 6, 9, 0, 0, 6, "\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
+/* 250 */
+{ 6, 9, 0, 0, 6, "\0\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
+/* 251 */
+{ 6, 9, 0, 0, 6, "\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
+/* 252 */
+{ 6, 8, 0, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
+/* 253 */
+{ 6, 12, 0, -3, 6, "\0\0\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" },
+/* 254 */
+{ 5, 12, 0, -3, 6, "\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\0" },
+/* 255 */
+{ 6, 11, 0, -3, 6, "\0\1\0\0\1\0\0\0\0\0\0\0\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" }
+
+};
+
+struct font pbm_defaultBdffont = { 14, 15, -1, -3, {
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+glBdf+ 0, glBdf+ 1, glBdf+ 2, glBdf+ 3, glBdf+ 4, glBdf+ 5,
+glBdf+ 6, glBdf+ 7, glBdf+ 8, glBdf+ 9, glBdf+10, glBdf+11,
+glBdf+12, glBdf+13, glBdf+14, glBdf+15, glBdf+16, glBdf+17,
+glBdf+18, glBdf+19, glBdf+20, glBdf+21, glBdf+22, glBdf+23,
+glBdf+24, glBdf+25, glBdf+26, glBdf+27, glBdf+28, glBdf+29,
+glBdf+30, glBdf+31, glBdf+32, glBdf+33, glBdf+34, glBdf+35,
+glBdf+36, glBdf+37, glBdf+38, glBdf+39, glBdf+40, glBdf+41,
+glBdf+42, glBdf+43, glBdf+44, glBdf+45, glBdf+46, glBdf+47,
+glBdf+48, glBdf+49, glBdf+50, glBdf+51, glBdf+52, glBdf+53,
+glBdf+54, glBdf+55, glBdf+56, glBdf+57, glBdf+58, glBdf+59,
+glBdf+60, glBdf+61, glBdf+62, glBdf+63, glBdf+64, glBdf+65,
+glBdf+66, glBdf+67, glBdf+68, glBdf+69, glBdf+70, glBdf+71,
+glBdf+72, glBdf+73, glBdf+74, glBdf+75, glBdf+76, glBdf+77,
+glBdf+78, glBdf+79, glBdf+80, glBdf+81, glBdf+82, glBdf+83,
+glBdf+84, glBdf+85, glBdf+86, glBdf+87, glBdf+88, glBdf+89,
+glBdf+90, glBdf+91, glBdf+92, glBdf+93, glBdf+94,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+glBdf+95,  glBdf+96,  glBdf+97,  glBdf+98,  glBdf+99,  glBdf+100,
+glBdf+101, glBdf+102, glBdf+103, glBdf+104, glBdf+105, glBdf+106,
+glBdf+107, glBdf+108, glBdf+109, glBdf+110, glBdf+111, glBdf+112,
+glBdf+113, glBdf+114, glBdf+115, glBdf+116, glBdf+117, glBdf+118,
+glBdf+119, glBdf+120, glBdf+121, glBdf+122, glBdf+123, glBdf+124,
+glBdf+125, glBdf+126, glBdf+127, glBdf+128, glBdf+129, glBdf+130,
+glBdf+131, glBdf+132, glBdf+133, glBdf+134, glBdf+135, glBdf+136,
+glBdf+137, glBdf+138, glBdf+139, glBdf+140, glBdf+141, glBdf+142,
+glBdf+143, glBdf+144, glBdf+145, glBdf+146, glBdf+147, glBdf+148,
+glBdf+149, glBdf+150, glBdf+151, glBdf+152, glBdf+153, glBdf+154,
+glBdf+155, glBdf+156, glBdf+157, glBdf+158, glBdf+159, glBdf+160,
+glBdf+161, glBdf+162, glBdf+163, glBdf+164, glBdf+165, glBdf+166,
+glBdf+167, glBdf+168, glBdf+169, glBdf+170, glBdf+171, glBdf+172,
+glBdf+173, glBdf+174, glBdf+175, glBdf+176, glBdf+177, glBdf+178,
+glBdf+179, glBdf+180, glBdf+181, glBdf+182, glBdf+183, glBdf+184,
+glBdf+185, glBdf+186, glBdf+187, glBdf+188, glBdf+189, glBdf+190 },
+NULL, 0, 0
+};
+
+struct font2 const pbm_defaultBdffont2 = {
+  sizeof(pbm_defaultFixedfont2),         /* len */
+  PBM_FONT2_STRUCT_SIZE(charset_string), /* size */
+  14, 15, -1, -3,                 /* maxwidth, maxheight, x, y */
+  pbm_defaultBdffont.glyph,       /* glyph table */
+  255, NULL, 255,                 /* maxglyph, selector, maxmaxglyph */
+  NULL, 0, 0,                     /* oldfont, fcols, frows */
+  PBM_FORMAT,                     /* bit_format */
+  190, 190,                       /* total_chars, chars */
+  FIXED_DATA,                     /* load_fn */
+  32, 1,                          /* default_char, default_char_defined */
+  (char *) "builtin bdf",         /* name */
+  ISO_8859_1, (char *)"ISO8859-1" /* charset, charset_string */
+};
+
+
+
diff --git a/lib/pgm.h b/lib/pgm.h
index 2de8d531..d4655239 100644
--- a/lib/pgm.h
+++ b/lib/pgm.h
@@ -24,7 +24,7 @@ typedef unsigned int gray;
    (because then old Netpbm programs can process them, and they're
    only half as big).
 
-   So we keep PGM_MAXMAXVAL = 255, even though it's kind of a misnomer.  
+   So we keep PGM_MAXMAXVAL = 255, even though it's kind of a misnomer.
 
    Note that one could always write a file with maxval > PGM_MAXMAXVAL and
    it would just go into plain (text) format instead of raw (binary) format.
@@ -42,6 +42,8 @@ typedef unsigned int gray;
 #define PGM_OVERALLMAXVAL 65535
 #define PGM_MAXMAXVAL 255
 
+#define pgm_unnormalize(value, maxval) \
+  ((gray)((value + 1e-6) * (maxval) + 0.5))
 
 /* Magic constants. */
 
@@ -62,7 +64,7 @@ typedef unsigned int gray;
 
 /* Declarations of routines. */
 
-void 
+void
 pgm_init(int *   const argcP,
          char ** const argv);
 
@@ -78,35 +80,35 @@ pgm_allocrow(unsigned int const cols);
 gray **
 pgm_readpgm(FILE * const file,
             int *  const colsP,
-            int *  const rowsP, 
+            int *  const rowsP,
             gray * const maxvalP);
 
 void
 pgm_readpgminit(FILE * const file,
-                int *  const colsP, 
+                int *  const colsP,
                 int *  const rowsP,
                 gray * const maxvalP,
                 int *  const formatP);
 
 void
 pgm_readpgmrow(FILE * const file,
-               gray * const grayrow, 
+               gray * const grayrow,
                int    const cols,
                gray   const maxval,
                int    const format);
 
 void
-pgm_writepgminit(FILE * const fileP, 
-                 int    const cols, 
-                 int    const rows, 
-                 gray   const maxval, 
+pgm_writepgminit(FILE * const fileP,
+                 int    const cols,
+                 int    const rows,
+                 gray   const maxval,
                  int    const forceplain);
 
 void
-pgm_writepgmrow(FILE *       const fileP, 
-                const gray * const grayrow, 
-                int          const cols, 
-                gray         const maxval, 
+pgm_writepgmrow(FILE *       const fileP,
+                const gray * const grayrow,
+                int          const cols,
+                gray         const maxval,
                 int          const forceplain);
 
 void
@@ -121,11 +123,11 @@ void
 pgm_nextimage(FILE * const file, int * const eofP);
 
 void
-pgm_check(FILE *               const file, 
-          enum pm_check_type   const check_type, 
-          int                  const format, 
-          int                  const cols, 
-          int                  const rows, 
+pgm_check(FILE *               const file,
+          enum pm_check_type   const check_type,
+          int                  const format,
+          int                  const cols,
+          int                  const rows,
           gray                 const maxval,
           enum pm_check_code * const retval_p);
 
diff --git a/lib/pm.h b/lib/pm.h
index 47cbfe83..3fc92fb4 100644
--- a/lib/pm.h
+++ b/lib/pm.h
@@ -110,10 +110,10 @@ extern int pm_plain_output;
     */
 extern const char * pm_progname;
 
-void 
+void
 pm_init(const char * const progname, unsigned int const flags);
 
-void 
+void
 pm_proginit(int * const argcP, const char * argv[]);
 
 void
@@ -122,7 +122,7 @@ pm_setMessage(int const newState, int * const oldStateP);
 int
 pm_getMessage(void);
 
-FILE * 
+FILE *
 pm_tmpfile(void);
 
 int
@@ -141,22 +141,22 @@ pm_nextimage(FILE * const file, int * const eofP);
 
 /* Variable-sized arrays definitions. */
 
-char** 
+char**
 pm_allocarray (int const cols, int const rows, int const size );
 
-void * 
+void *
 pm_allocrow(unsigned int const cols,
             unsigned int const size);
 
-void 
+void
 pm_freearray (char** const its, int const rows);
 
-void 
+void
 pm_freerow(void * const row);
 
 
 /* Obsolete -- use shhopt instead */
-int 
+int
 pm_keymatch(const char * const str,
             const char * const keyword,
             int          const minchars);
@@ -169,7 +169,7 @@ int PURE_FN_ATTR
 pm_bitstomaxval(int const bits);
 
 unsigned int PURE_FN_ATTR
-pm_lcm (unsigned int const x, 
+pm_lcm (unsigned int const x,
         unsigned int const y,
         unsigned int const z,
         unsigned int const limit);
@@ -211,37 +211,37 @@ void
 pm_setusererrormsgfn(pm_usererrormsgfn * fn);
 
 void PM_GNU_PRINTF_ATTR(1,2)
-pm_message (const char format[], ...);     
+pm_message (const char format[], ...);
 
 void PM_GNU_PRINTF_ATTR(1,2)
 pm_errormsg(const char format[], ...);
 
 void PM_GNU_PRINTF_ATTR(1,2)
-pm_error (const char reason[], ...);       
+pm_error (const char reason[], ...);
 
 int
 pm_have_float_format(void);
 
 /* Obsolete - use shhopt and user's manual instead */
-void 
-pm_usage (const char usage[]);             
+void
+pm_usage (const char usage[]);
 
-FILE* 
+FILE*
 pm_openr (const char* const name);
-         
-FILE*    
+
+FILE*
 pm_openw (const char* const name);
-         
+
 FILE *
 pm_openr_seekable(const char name[]);
 
-void     
+void
 pm_close (FILE* const f);
 
-void 
+void
 pm_closer (FILE* const f);
-          
-void      
+
+void
 pm_closew (FILE* const f);
 
 
@@ -267,11 +267,11 @@ pm_writecharu(FILE *        const ofP,
 }
 
 int
-pm_readbigshort(FILE *  const ifP, 
+pm_readbigshort(FILE *  const ifP,
                 short * const sP);
 
 static __inline__ int
-pm_readbigshortu(FILE*            const ifP, 
+pm_readbigshortu(FILE*            const ifP,
                  unsigned short * const sP) {
     return pm_readbigshort(ifP, (short *) sP);
 }
@@ -287,7 +287,7 @@ pm_writebigshortu(FILE *          const ofP,
 }
 
 int
-pm_readbiglong(FILE * const ifP, 
+pm_readbiglong(FILE * const ifP,
                long * const lP);
 
 static __inline__ int
@@ -297,7 +297,7 @@ pm_readbiglongu(FILE *          const ifP,
 }
 
 int
-pm_readbiglong2(FILE * const ifP, 
+pm_readbiglong2(FILE * const ifP,
                 int32_t * const lP);
 
 static __inline__ int
@@ -366,13 +366,20 @@ pm_writelittlelongu(FILE *        const ofP,
     return pm_writelittlelong(ofP, (long) l);
 }
 
-int 
+int
 pm_readmagicnumber(FILE * const ifP);
 
-char* 
-pm_read_unknown_size(FILE * const ifP, 
+char*
+pm_read_unknown_size(FILE * const ifP,
                      long * const buf);
 
+void
+pm_getline(FILE *   const ifP,
+           char **  const bufferP,
+           size_t * const bufferSzP,
+           int *    const eofP,
+           size_t * const lineLenP);
+
 short
 pm_bs_short(short const s);
 
@@ -383,12 +390,12 @@ unsigned int
 pm_tell(FILE * const fileP);
 
 void
-pm_tell2(FILE *       const fileP, 
+pm_tell2(FILE *       const fileP,
          void *       const fileposP,
          unsigned int const fileposSize);
 
 void
-pm_seek2(FILE *             const fileP, 
+pm_seek2(FILE *             const fileP,
          const pm_filepos * const fileposP,
          unsigned int       const fileposSize);
 
@@ -408,8 +415,8 @@ enum pm_check_type {
 };
 
 void
-pm_check(FILE *               const file, 
-         enum pm_check_type   const check_type, 
+pm_check(FILE *               const file,
+         enum pm_check_type   const check_type,
          pm_filepos           const need_raster_size,
          enum pm_check_code * const retval_p);
 
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/pm_system.h b/lib/pm_system.h
index a7560f48..dfcd5b3f 100644
--- a/lib/pm_system.h
+++ b/lib/pm_system.h
@@ -10,6 +10,32 @@ extern "C" {
 
 
 void
+pm_system2_vp(const char *    const progName,
+              const char **   const argArray,
+              void stdinFeeder(int, void *),
+              void *          const feederParm,
+              void stdoutAccepter(int, void *),
+              void *          const accepterParm,
+              int *           const termStatusP);
+
+void
+pm_system2_lp(const char *    const progName,
+              void stdinFeeder(int, void *),
+              void *          const feederParm,
+              void stdoutAccepter(int, void *),
+              void *          const accepterParm,
+              int *           const termStatusP,
+              ...);
+
+void
+pm_system2(void                  stdinFeeder(int, void *),
+           void *          const feederParm,
+           void                  stdoutAccepter(int, void *),
+           void *          const accepterParm,
+           const char *    const shellCommand,
+           int *           const termStatusP);
+
+void
 pm_system_vp(const char *    const progName,
              const char **   const argArray,
              void stdinFeeder(int, void *),
@@ -32,6 +58,21 @@ pm_system(void                  stdinFeeder(int, void *),
           void *          const accepterParm,
           const char *    const shellCommand);
 
+const char *
+pm_termStatusDesc(int const termStatus);
+
+
+/* The following are Standard Input feeders and Standard Output accepters
+   for pm_system() etc.
+*/
+void
+pm_feed_null(int    const pipeToFeedFd,
+             void * const feederParm);
+
+void
+pm_accept_null(int    const pipetosuckFd,
+               void * const accepterParm);
+
 struct bufferDesc {
     /* This is just a parameter for the routines below */
     unsigned int    size;
@@ -40,9 +81,11 @@ struct bufferDesc {
 };
 
 
-/* The following are a Standard Input feeder and a Standard Output accepter
-   for pm_system().  
+/* The struct name "bufferDesc", without the "pm" namespace, is an unfortunate
+   historical accident.
 */
+typedef struct bufferDesc pm_bufferDesc;
+
 void
 pm_feed_from_memory(int    const pipeToFeedFd,
                     void * const feederParm);
diff --git a/lib/pmfileio.c b/lib/pmfileio.c
index 8176ae6a..33f89110 100644
--- a/lib/pmfileio.c
+++ b/lib/pmfileio.c
@@ -5,6 +5,7 @@
   These are external functions, unlike 'fileio.c', but are not
   particular to any Netpbm format.
 **************************************************************************/
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _SVID_SOURCE
     /* Make sure P_tmpdir is defined in GNU libc 2.0.7 (_XOPEN_SOURCE 500
        does it in other libc's).  pm_config.h defines TMPDIR as P_tmpdir
@@ -13,10 +14,10 @@
 #define _BSD_SOURCE    /* Make sure strdup is defined */
 #define _XOPEN_SOURCE 500    /* Make sure ftello, fseeko, strdup are defined */
 #define _LARGEFILE_SOURCE 1  /* Make sure ftello, fseeko are defined */
-#define _LARGEFILE64_SOURCE 1 
+#define _LARGEFILE64_SOURCE 1
 #define _FILE_OFFSET_BITS 64
     /* This means ftello() is really ftello64() and returns a 64 bit file
-       position.  Unless the C library doesn't have ftello64(), in which 
+       position.  Unless the C library doesn't have ftello64(), in which
        case ftello() is still just ftello().
 
        Likewise for all the other C library file functions.
@@ -24,7 +25,7 @@
        And off_t and fpos_t are 64 bit types instead of 32.  Consequently,
        pm_filepos_t might be 64 bits instead of 32.
     */
-#define _LARGE_FILES  
+#define _LARGE_FILES
     /* This does for AIX what _FILE_OFFSET_BITS=64 does for GNU */
 #define _LARGE_FILE_API
     /* This makes the the x64() functions available on AIX */
@@ -59,9 +60,9 @@ pm_openr(const char * const name) {
         f = stdin;
     else {
         f = fopen(name, "rb");
-        if (f == NULL) 
+        if (f == NULL)
             pm_error("Unable to open file '%s' for reading.  "
-                     "fopen() returns errno %d (%s)", 
+                     "fopen() returns errno %d (%s)",
                      name, errno, strerror(errno));
     }
     return f;
@@ -77,9 +78,9 @@ pm_openw(const char * const name) {
         f = stdout;
     else {
         f = fopen(name, "wb");
-        if (f == NULL) 
+        if (f == NULL)
             pm_error("Unable to open file '%s' for writing.  "
-                     "fopen() returns errno %d (%s)", 
+                     "fopen() returns errno %d (%s)",
                      name, errno, strerror(errno));
     }
     return f;
@@ -117,7 +118,7 @@ tmpDir(void) {
 static int
 tempFileOpenFlags(void) {
 /*----------------------------------------------------------------------------
-  Open flags (argument to open()) suitable for a new temporary file  
+  Open flags (argument to open()) suitable for a new temporary file
 -----------------------------------------------------------------------------*/
     int retval;
 
@@ -181,11 +182,11 @@ mkstempx(char * const filenameBuffer) {
             } else {
                 if (errno == EEXIST) {
                     /* We'll just have to keep trying */
-                } else 
+                } else
                     error = TRUE;
             }
         }
-    }    
+    }
     if (gotFile)
         retval = fd;
     else
@@ -215,7 +216,7 @@ makeTmpfileWithTemplate(const char *  const filenameTemplate,
                         int *         const fdP,
                         const char ** const filenameP,
                         const char ** const errorP) {
-    
+
     char * filenameBuffer;  /* malloc'ed */
 
     filenameBuffer = strdup(filenameTemplate);
@@ -225,9 +226,9 @@ makeTmpfileWithTemplate(const char *  const filenameTemplate,
                     "file name");
     else {
         int rc;
-        
+
         rc = mkstemp2(filenameBuffer);
-        
+
         if (rc < 0)
             pm_asprintf(errorP,
                         "Unable to create temporary file according to name "
@@ -260,8 +261,8 @@ pm_make_tmpfile_fd(int *         const fdP,
         dirseparator = "";
     else
         dirseparator = "/";
-    
-    pm_asprintf(&filenameTemplate, "%s%s%s%s", 
+
+    pm_asprintf(&filenameTemplate, "%s%s%s%s",
                 tmpdir, dirseparator, pm_progname, "_XXXXXX");
 
     if (filenameTemplate == pm_strsol)
@@ -290,7 +291,7 @@ pm_make_tmpfile(FILE **       const filePP,
     pm_make_tmpfile_fd(&fd, filenameP);
 
     *filePP = fdopen(fd, "w+b");
-    
+
     if (*filePP == NULL) {
         close(fd);
         unlink(*filenameP);
@@ -304,7 +305,7 @@ pm_make_tmpfile(FILE **       const filePP,
 
 
 
-bool const canUnlinkOpen = 
+bool const canUnlinkOpen =
 #if CAN_UNLINK_OPEN
     1
 #else
@@ -393,7 +394,7 @@ scheduleUnlinkAtExit(const char * const fileName,
    code too badly for Unix.
 -----------------------------------------------------------------------------*/
     static bool unlinkListEstablished = false;
-    
+
     if (!unlinkListEstablished) {
         atexit(unlinkTempFiles);
         unlinkListP = NULL;
@@ -417,7 +418,7 @@ arrangeUnlink(const char * const fileName,
 
 
 
-FILE * 
+FILE *
 pm_tmpfile(void) {
 
     FILE * fileP;
@@ -472,7 +473,7 @@ pm_openr_seekable(const char name[]) {
 
     original_file = pm_openr((char *) name);
 
-    /* I would use fseek() to determine if the file is seekable and 
+    /* I would use fseek() to determine if the file is seekable and
        be a little more general than checking the type of file, but I
        don't have reliable information on how to do that.  I have seen
        streams be partially seekable -- you can, for example seek to
@@ -487,16 +488,16 @@ pm_openr_seekable(const char name[]) {
     stat_rc = fstat(fileno(original_file), &statbuf);
     if (stat_rc == 0 && S_ISREG(statbuf.st_mode))
         seekable = TRUE;
-    else 
+    else
         seekable = FALSE;
 
     if (seekable) {
         seekable_file = original_file;
     } else {
         seekable_file = pm_tmpfile();
-        
+
         /* Copy the input into the temporary seekable file */
-        while (!feof(original_file) && !ferror(original_file) 
+        while (!feof(original_file) && !ferror(original_file)
                && !ferror(seekable_file)) {
             char buffer[4096];
             int bytes_read;
@@ -536,7 +537,7 @@ pm_close(FILE * const f) {
 
 
 
-/* The pnmtopng package uses pm_closer() and pm_closew() instead of 
+/* The pnmtopng package uses pm_closer() and pm_closew() instead of
    pm_close(), apparently because the 1999 Pbmplus package has them.
    I don't know what the difference is supposed to be.
 */
@@ -600,7 +601,7 @@ getcNofail(FILE * const ifP) {
 void
 pm_readchar(FILE * const ifP,
             char * const cP) {
-    
+
     *cP = (char)getcNofail(ifP);
 }
 
@@ -616,7 +617,7 @@ pm_writechar(FILE * const ofP,
 
 
 int
-pm_readbigshort(FILE *  const ifP, 
+pm_readbigshort(FILE *  const ifP,
                 short * const sP) {
 
     unsigned short s;
@@ -632,7 +633,7 @@ pm_readbigshort(FILE *  const ifP,
 
 
 int
-pm_writebigshort(FILE * const ofP, 
+pm_writebigshort(FILE * const ofP,
                  short  const s) {
 
     putc((s >> 8) & 0xff, ofP);
@@ -644,7 +645,7 @@ pm_writebigshort(FILE * const ofP,
 
 
 int
-pm_readbiglong(FILE * const ifP, 
+pm_readbiglong(FILE * const ifP,
                long * const lP) {
 
     unsigned long l;
@@ -679,7 +680,7 @@ pm_readbiglong2(FILE *    const ifP,
 
 
 int
-pm_writebiglong(FILE * const ofP, 
+pm_writebiglong(FILE * const ofP,
                 long   const l) {
 
     putc((l >> 24) & 0xff, ofP);
@@ -693,7 +694,7 @@ pm_writebiglong(FILE * const ofP,
 
 
 int
-pm_readlittleshort(FILE *  const ifP, 
+pm_readlittleshort(FILE *  const ifP,
                    short * const sP) {
     unsigned short s;
 
@@ -708,7 +709,7 @@ pm_readlittleshort(FILE *  const ifP,
 
 
 int
-pm_writelittleshort(FILE * const ofP, 
+pm_writelittleshort(FILE * const ofP,
                     short  const s) {
 
     putc((s >> 0) & 0xff, ofP);
@@ -720,7 +721,7 @@ pm_writelittleshort(FILE * const ofP,
 
 
 int
-pm_readlittlelong(FILE * const ifP, 
+pm_readlittlelong(FILE * const ifP,
                   long * const lP) {
     unsigned long l;
 
@@ -754,7 +755,7 @@ pm_readlittlelong2(FILE *    const ifP,
 
 
 int
-pm_writelittlelong(FILE * const ofP, 
+pm_writelittlelong(FILE * const ofP,
                    long   const l) {
 
     putc((l >>  0) & 0xff, ofP);
@@ -767,17 +768,24 @@ pm_writelittlelong(FILE * const ofP,
 
 
 
-int 
+int
 pm_readmagicnumber(FILE * const ifP) {
 
     int ich1, ich2;
 
     ich1 = getc(ifP);
+
+    if (ich1 == EOF)
+        pm_error("Error reading first byte of what is expected to be "
+                 "a Netpbm magic number.  "
+                 "Most often, this means your input file is empty");
+
     ich2 = getc(ifP);
-    if (ich1 == EOF || ich2 == EOF)
-        pm_error( "Error reading magic number from Netpbm image stream.  "
-                  "Most often, this "
-                  "means your input file is empty." );
+
+    if (ich2 == EOF)
+        pm_error("Error reading second byte of what is expected to be "
+                 "a Netpbm magic number (the first byte was successfully "
+                 "read as 0x%02x)", ich1);
 
     return ich1 * 256 + ich2;
 }
@@ -797,7 +805,7 @@ pm_readmagicnumber(FILE * const ifP) {
                                   than this. */
 
 char *
-pm_read_unknown_size(FILE * const file, 
+pm_read_unknown_size(FILE * const file,
                      long * const nread) {
     long nalloc;
     char * buf;
@@ -823,7 +831,7 @@ pm_read_unknown_size(FILE * const file,
         val = getc(file);
         if (val == EOF)
             eof = TRUE;
-        else 
+        else
             buf[(*nread)++] = val;
     }
     return buf;
@@ -831,6 +839,73 @@ pm_read_unknown_size(FILE * const file,
 
 
 
+void
+pm_getline(FILE *   const ifP,
+           char **  const bufferP,
+           size_t * const bufferSzP,
+           int *    const eofP,
+           size_t * const lineLenP) {
+/*----------------------------------------------------------------------------
+   This is like POSIX 'getline'.
+
+   But we don't include the newline in the returned line.
+-----------------------------------------------------------------------------*/
+    char * buffer;
+    size_t bufferSz;
+    bool gotLine;
+    bool eof;
+    size_t nReadSoFar;
+
+    buffer   = *bufferP;    /* initial value */
+    bufferSz = *bufferSzP;  /* initial value */
+
+    for (nReadSoFar = 0, gotLine = false, eof = false;
+         !gotLine && !eof; ) {
+
+        int rc;
+
+        rc = fgetc(ifP);
+
+        if (rc == EOF) {
+            if (ferror(ifP))
+                pm_error("Error reading input file.  fgets() failed with "
+                         "errno %d (%s)", errno, strerror(errno));
+
+            if (nReadSoFar == 0) {
+                /* Didn't get anything before EOF, so return EOF */
+                eof = true;
+            } else {
+                /* End of file ends the line */
+                gotLine = true;
+            }
+        } else {
+            char const c = (char)rc;
+
+            if (c == '\n') {
+                /* Newline ends the line, and is not part of it */
+                gotLine = true;
+            } else {
+                if (nReadSoFar + 2 > bufferSz) {
+                    /* + 2 = 1 for 'c', one for terminating NUL */
+                    bufferSz += 128;
+                    REALLOCARRAY(buffer, bufferSz);
+                }
+                buffer[nReadSoFar++] = c;
+            }
+        }
+    }
+
+    if (gotLine)
+        buffer[nReadSoFar] = '\0';
+
+    *eofP      = eof;
+    *bufferP   = buffer;
+    *bufferSzP = bufferSz;
+    *lineLenP  = nReadSoFar;
+}
+
+
+
 union cheat {
     uint32_t l;
     short s;
@@ -871,7 +946,7 @@ pm_bs_long(long const l) {
 
 
 void
-pm_tell2(FILE *       const fileP, 
+pm_tell2(FILE *       const fileP,
          void *       const fileposP,
          unsigned int const fileposSize) {
 /*----------------------------------------------------------------------------
@@ -903,7 +978,7 @@ pm_tell2(FILE *       const fileP,
         }
     } else
         pm_error("File position size passed to pm_tell() is invalid: %u.  "
-                 "Valid sizes are %u and %u", 
+                 "Valid sizes are %u and %u",
                  fileposSize, (unsigned int)sizeof(pm_filepos),
                  (unsigned int) sizeof(long));
 }
@@ -912,7 +987,7 @@ pm_tell2(FILE *       const fileP,
 
 unsigned int
 pm_tell(FILE * const fileP) {
-    
+
     long filepos;
 
     pm_tell2(fileP, &filepos, sizeof(filepos));
@@ -923,14 +998,14 @@ pm_tell(FILE * const fileP) {
 
 
 void
-pm_seek2(FILE *             const fileP, 
+pm_seek2(FILE *             const fileP,
          const pm_filepos * const fileposP,
          unsigned int       const fileposSize) {
 /*----------------------------------------------------------------------------
    Position file *fileP to position *fileposP.  Abort if error, including
    if *fileP isn't a seekable file.
 -----------------------------------------------------------------------------*/
-    if (fileposSize == sizeof(pm_filepos)) 
+    if (fileposSize == sizeof(pm_filepos))
         /* Note: FSEEKO() is either fseeko() or fseek(), depending on the
            capabilities of the underlying C library.  It is defined in
            pm_config.h.  fseeko(), in turn, may be either fseek() or
@@ -942,7 +1017,7 @@ pm_seek2(FILE *             const fileP,
         fseek(fileP, fileposLong, SEEK_SET);
     } else
         pm_error("File position size passed to pm_seek() is invalid: %u.  "
-                 "Valid sizes are %u and %u", 
+                 "Valid sizes are %u and %u",
                  fileposSize, (unsigned int)sizeof(pm_filepos),
                  (unsigned int) sizeof(long));
 }
@@ -969,7 +1044,7 @@ pm_nextimage(FILE * const file, int * const eofP) {
    Position the file 'file' to the next image in the stream, assuming it is
    now positioned just after the current image.  I.e. read off any white
    space at the end of the current image's raster.  Note that the raw formats
-   don't permit such white space, but this routine tolerates it anyway, 
+   don't permit such white space, but this routine tolerates it anyway,
    because the plain formats do permit white space after the raster.
 
    Iff there is no next image, return *eofP == TRUE.
@@ -994,7 +1069,7 @@ pm_nextimage(FILE * const file, int * const eofP) {
         int c;
         c = getc(file);
         if (c == EOF) {
-            if (feof(file)) 
+            if (feof(file))
                 eof = TRUE;
             else
                 pm_error("File error on getc() to position to image");
@@ -1005,10 +1080,10 @@ pm_nextimage(FILE * const file, int * const eofP) {
                 nonWhitespaceFound = TRUE;
 
                 /* Have to put the non-whitespace character back in
-                   the stream -- it's part of the next image.  
+                   the stream -- it's part of the next image.
                 */
                 rc = ungetc(c, file);
-                if (rc == EOF) 
+                if (rc == EOF)
                     pm_error("File error doing ungetc() "
                              "to position to image.");
             }
@@ -1020,8 +1095,8 @@ pm_nextimage(FILE * const file, int * const eofP) {
 
 
 void
-pm_check(FILE *               const file, 
-         enum pm_check_type   const check_type, 
+pm_check(FILE *               const file,
+         enum pm_check_type   const check_type,
          pm_filepos           const need_raster_size,
          enum pm_check_code * const retval_p) {
 /*----------------------------------------------------------------------------
@@ -1039,9 +1114,9 @@ pm_check(FILE *               const file,
     curpos = FTELLO(file);
     if (curpos >= 0) {
         /* This type of file has a current position */
-            
+
         rc = fstat(fileno(file), &statbuf);
-        if (rc != 0) 
+        if (rc != 0)
             pm_error("fstat() failed to get size of file, though ftello() "
                      "successfully identified\n"
                      "the current position.  Errno=%s (%d)",
@@ -1051,12 +1126,12 @@ pm_check(FILE *               const file,
             if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE;
         } else {
             pm_filepos const have_raster_size = statbuf.st_size - curpos;
-            
+
             if (have_raster_size < need_raster_size)
                 pm_error("File has invalid format.  The raster should "
                          "contain %u bytes, but\n"
                          "the file ends after only %u bytes.",
-                         (unsigned int) need_raster_size, 
+                         (unsigned int) need_raster_size,
                          (unsigned int) have_raster_size);
             else if (have_raster_size > need_raster_size) {
                 if (retval_p) *retval_p = PM_CHECK_TOO_LONG;
diff --git a/lib/pnm.h b/lib/pnm.h
index 3b490552..0625cb5c 100644
--- a/lib/pnm.h
+++ b/lib/pnm.h
@@ -5,6 +5,8 @@
 #define _PNM_H_
 
 #include <netpbm/pm.h>
+#include <netpbm/pbm.h>
+#include <netpbm/pgm.h>
 #include <netpbm/ppm.h>
 
 #ifdef __cplusplus
@@ -19,6 +21,7 @@ typedef pixel xel;
 typedef pixval xelval;
 #define PNM_OVERALLMAXVAL PPM_OVERALLMAXVAL
 #define PNM_MAXMAXVAL PPM_MAXMAXVAL
+#define pnm_unnormalize ppm_unnormalize
 #define PNM_GET1(x) PPM_GETB(x)
 #define PNM_GETR(x) PPM_GETR(x)
 #define PNM_GETG(x) PPM_GETG(x)
@@ -72,7 +75,7 @@ pnm_readpnm(FILE *   const fileP,
 
 void
 pnm_check(FILE *               const fileP,
-          enum pm_check_type   const check_type, 
+          enum pm_check_type   const check_type,
           int                  const format,
           int                  const cols,
           int                  const rows,
@@ -81,19 +84,19 @@ pnm_check(FILE *               const fileP,
 
 
 void
-pnm_writepnminit(FILE * const fileP, 
-                 int    const cols, 
-                 int    const rows, 
-                 xelval const maxval, 
-                 int    const format, 
+pnm_writepnminit(FILE * const fileP,
+                 int    const cols,
+                 int    const rows,
+                 xelval const maxval,
+                 int    const format,
                  int    const forceplain);
 
 void
-pnm_writepnmrow(FILE *      const fileP, 
-                const xel * const xelrow, 
-                int         const cols, 
-                xelval      const maxval, 
-                int         const format, 
+pnm_writepnmrow(FILE *      const fileP,
+                const xel * const xelrow,
+                int         const cols,
+                xelval      const maxval,
+                int         const format,
                 int         const forceplain);
 
 void
@@ -105,28 +108,28 @@ pnm_writepnm(FILE * const fileP,
              int    const format,
              int    const forceplain);
 
-xel 
+xel
 pnm_backgroundxel(xel** xels, int cols, int rows, xelval maxval, int format);
 
-xel 
+xel
 pnm_backgroundxelrow(xel* xelrow, int cols, xelval maxval, int format);
 
-xel 
+xel
 pnm_whitexel(xelval maxval, int format);
 
-xel 
+xel
 pnm_blackxel(xelval maxval, int format);
 
-void 
+void
 pnm_invertxel(xel *  const x,
               xelval const maxval,
               int    const format);
 
-void 
-pnm_promoteformat(xel** xels, int cols, int rows, xelval maxval, int format, 
+void
+pnm_promoteformat(xel** xels, int cols, int rows, xelval maxval, int format,
                   xelval newmaxval, int newformat);
-void 
-pnm_promoteformatrow(xel* xelrow, int cols, xelval maxval, int format, 
+void
+pnm_promoteformatrow(xel* xelrow, int cols, xelval maxval, int format,
                      xelval newmaxval, int newformat);
 
 pixel
@@ -134,6 +137,16 @@ pnm_xeltopixel(xel const inputxel,
                int const format);
 
 xel
+pnm_pixeltoxel(pixel const inputPixel);
+
+xel
+pnm_graytoxel(gray const inputGray);
+
+xel
+pnm_bittoxel(bit    const inputBit,
+             xelval const maxval);
+
+xel
 pnm_parsecolorxel(const char * const colorName,
                   xelval       const maxval,
                   int          const format);
diff --git a/lib/ppm.h b/lib/ppm.h
index 82241b70..9fc90bb3 100644
--- a/lib/ppm.h
+++ b/lib/ppm.h
@@ -23,6 +23,9 @@ typedef gray pixval;
 
 #define PPM_OVERALLMAXVAL PGM_OVERALLMAXVAL
 #define PPM_MAXMAXVAL PGM_MAXMAXVAL
+
+#define ppm_unnormalize pgm_unnormalize
+
 typedef struct {
     pixval r, g, b;
 } pixel;
@@ -90,61 +93,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 +157,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 +170,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 +214,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 +223,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/ppmdraw.h b/lib/ppmdraw.h
index df22b44d..5fd4148c 100644
--- a/lib/ppmdraw.h
+++ b/lib/ppmdraw.h
@@ -9,6 +9,7 @@
 */
 
 #include <netpbm/pm_config.h>
+#include <netpbm/ppm.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -59,6 +60,9 @@ typedef struct {
     } u;
 } ppmd_pathleg;
 
+ppmd_pathleg
+ppmd_makeLineLeg(ppmd_point const point);
+
 typedef struct {
 /*----------------------------------------------------------------------------
    A closed path
@@ -75,9 +79,58 @@ typedef struct {
            as the definition of ppmd_pathleg changes.
         */
     ppmd_pathleg * legs;
+        /* An array of the legs of the path, in order, starting at 'begPoint'.
+        */
 } ppmd_path;
 
+typedef struct {
+
+    ppmd_path path;
+        /* The path we are building (or have built).
+           Null for path.legs means we don't have a leg array yet.
+        */
+
+    bool begIsSet;
+        /* User has set path.begPoint.  If this is false, path.begPoint is
+           meaningless.
+        */
+    
+    unsigned int legsAllocSize;
+        /* How many legs of space is allocated in the leg array path.legs */
+
+    bool legsAreAutoAllocated;
+        /* The array 'legs' is allocated or reallocated automatically by
+           ppmd_path_addlineline(), as opposed to being supplied by the
+           user as part of initializing this structure, never to be altered.
+        */
+    
+} ppmd_pathbuilder;
+
+ppmd_pathbuilder *
+ppmd_pathbuilder_create(void);
+
+void
+ppmd_pathbuilder_destroy(ppmd_pathbuilder * const pathBuilderP);
+
+void
+ppmd_pathbuilder_setLegArray(ppmd_pathbuilder * const pathBuilderP,
+                             ppmd_pathleg *     const legs,
+                             unsigned int       const legCount);
+
+void
+ppmd_pathbuilder_preallocLegArray(ppmd_pathbuilder * const pathBuilderP,
+                                  unsigned int       const legCount);
+
+void
+ppmd_pathbuilder_setBegPoint(ppmd_pathbuilder * const pathBuilderP,
+                             ppmd_point         const begPoint);
+
+void
+ppmd_pathbuilder_addLineLeg(ppmd_pathbuilder * const pathBuilderP,
+                            ppmd_pathleg       const leg);
 
+const ppmd_path *
+ppmd_pathbuilder_pathP(ppmd_pathbuilder * const pathBuilderP);
 
 typedef void ppmd_drawprocp(pixel **, unsigned int, unsigned int,
                             pixval, ppmd_point, const void *);
@@ -272,12 +325,12 @@ ppmd_filledrectangle(pixel **      const pixels,
 
 
 void
-ppmd_fill_path(pixel **      const pixels, 
-               int           const cols, 
-               int           const rows, 
-               pixval        const maxval,
-               ppmd_path *   const pathP,
-               pixel         const color);
+ppmd_fill_path(pixel **          const pixels, 
+               int               const cols, 
+               int               const rows, 
+               pixval            const maxval,
+               const ppmd_path * const pathP,
+               pixel             const color);
     /* Fills in a closed path.  Not much different from ppmd_fill(),
        but with a different interface.
     */
diff --git a/lib/util/mallocvar.c b/lib/util/mallocvar.c
index 339d5d61..f75bd94d 100644
--- a/lib/util/mallocvar.c
+++ b/lib/util/mallocvar.c
@@ -30,7 +30,7 @@ allocarrayNoHeap(void **      const rowIndex,
         void * rowSpace;
 
         mallocProduct(&rowSpace, cols, elementSize);
-        
+
         if (rowSpace == NULL) {
             unsigned int row;
 
@@ -123,7 +123,7 @@ pm_mallocarray2(void **      const resultP,
 
             if (rowheap) {
                 unsigned int row;
-                
+
                 for (row = 0; row < rows; ++row)
                     rowIndex[row] = &(rowheap[row * cols * elementSize]);
             }
@@ -133,7 +133,7 @@ pm_mallocarray2(void **      const resultP,
                format.
             */
             rowIndex[rows+1] = NULL;   /* Declare it fragmented format */
-            
+
             allocarrayNoHeap(rowIndex, rows, cols, elementSize, &failed);
         }
         rowIndex[rows+0] = NULL;   /* mark end of rows */
@@ -151,7 +151,7 @@ array2RowCount(void ** const rowIndex) {
 /*----------------------------------------------------------------------------
    Return the number of rows in the 2-dimensional array.
 -----------------------------------------------------------------------------*/
-    /* The end of the rows is marked by a null pointer where a row 
+    /* The end of the rows is marked by a null pointer where a row
        pointer otherwise would be.
     */
 
diff --git a/lib/util/mallocvar.h b/lib/util/mallocvar.h
index e92e3fe4..00ba6484 100644
--- a/lib/util/mallocvar.h
+++ b/lib/util/mallocvar.h
@@ -1,5 +1,5 @@
 /* These are some dynamic memory allocation facilities.  They are essentially
-   an extension to C, as they do allocations with a cognizance of C 
+   an extension to C, as they do allocations with a cognizance of C
    variables.  You can use them to make C read more like a high level
    language.
 
@@ -23,7 +23,7 @@ extern "C" {
 #endif
 
 static __inline__ void
-mallocProduct(void **      const resultP, 
+mallocProduct(void **      const resultP,
               unsigned int const factor1,
               unsigned int const factor2) {
 /*----------------------------------------------------------------------------
@@ -44,10 +44,10 @@ mallocProduct(void **      const resultP,
     if (factor1 == 0 || factor2 == 0)
         *resultP = malloc(1);
     else {
-        if (UINT_MAX / factor2 < factor1) 
+        if (UINT_MAX / factor2 < factor1)
             *resultP = NULL;
-        else 
-            *resultP = malloc(factor1 * factor2); 
+        else
+            *resultP = malloc(factor1 * factor2);
     }
 }
 
@@ -61,11 +61,11 @@ reallocProduct(void **      const blockP,
     void * const oldBlockP = *blockP;
 
     void * newBlockP;
-    
-    if (UINT_MAX / factor2 < factor1) 
+
+    if (UINT_MAX / factor2 < factor1)
         newBlockP = NULL;
-    else 
-        newBlockP = realloc(oldBlockP, factor1 * factor2); 
+    else
+        newBlockP = realloc(oldBlockP, factor1 * factor2);
 
     if (newBlockP)
         *blockP = newBlockP;
diff --git a/lib/util/nstring.c b/lib/util/nstring.c
index 711cfca9..7ef9fcfb 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.
 
@@ -113,6 +113,7 @@
  */
 
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in string.h */
 #define _GNU_SOURCE
@@ -194,6 +195,19 @@ static char credits[] = "\n\
 @(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
 
 
+/* MacOS X before 10.7, for one, does not have strnlen */
+size_t
+pm_strnlen(const char * const s,
+           size_t       const maxlen) {
+
+    unsigned int i;
+
+    for (i = 0; i < maxlen && s[i]; ++i) {}
+
+    return i;
+}
+
+
 
 void
 pm_vsnprintf(char *       const str,
@@ -201,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;
 
@@ -226,7 +240,6 @@ pm_vsnprintf(char *       const str,
             }
             p += n; str_l += n;
         } else {
-            const char *starting_p;
             size_t min_field_width;
             size_t precision = 0;
             bool precision_specified;
@@ -268,7 +281,6 @@ pm_vsnprintf(char *       const str,
             str_arg = credits;
                 /* just to make compiler happy (defined but not used) */
             str_arg = NULL;
-            starting_p = p;
             ++p;  /* skip '%' */
 
             /* parse flags */
@@ -405,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)
@@ -457,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';
@@ -590,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) {
@@ -604,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? */
@@ -756,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);
@@ -795,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);
@@ -810,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);
@@ -831,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);
 
@@ -859,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;
@@ -869,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;
@@ -902,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.
@@ -995,39 +1007,50 @@ pm_strishex(const char * const subject) {
 
 
 void
-pm_interpret_uint(const char *   const string,
-                  unsigned int * const valueP,
+pm_string_to_uint(const char *   const string,
+                  unsigned int * const uintP,
                   const char **  const errorP) {
 
-    if (string[0] == '\0')
-        pm_asprintf(errorP, "Null string.");
+    if (strlen(string) == 0)
+        pm_asprintf(errorP, "Value is a null string");
     else {
-        /* strtoul() does a bizarre thing where if the number is out
+        char * tailptr;
+        long longValue;
+
+        /* We can't use 'strtoul'.  Contrary to expectations, though as
+           designed, it returns junk if there is a minus sign.
+        */
+
+        /* strtol() does a bizarre thing where if the number is out
            of range, it returns a clamped value but tells you about it
            by setting errno = ERANGE.  If it is not out of range,
-           strtoul() leaves errno alone.
+           strtol() leaves errno alone.
         */
-        char * tail;
-        unsigned long ulongValue;
-        
         errno = 0;  /* So we can tell if strtoul() overflowed */
 
-        ulongValue = strtoul(string, &tail, 10);
-
-        if (tail[0] != '\0')
-            pm_asprintf(errorP, "Non-digit stuff in string: %s", tail);
-        else if (errno == ERANGE)
-            pm_asprintf(errorP, "Number too large");
-        else if (ulongValue > UINT_MAX)
-            pm_asprintf(errorP, "Number too large");
-        else if (string[0] == '-')
-            pm_asprintf(errorP, "Negative number");
-            /* Sleazy code; string may have leading spaces. */
+        longValue = strtol(string, &tailptr, 10);
+
+        if (*tailptr != '\0')
+            pm_asprintf(errorP, "Non-numeric crap in string: '%s'", tailptr);
         else {
-            *valueP = ulongValue;
-            *errorP = NULL;
+             if (errno == ERANGE)
+                 pm_asprintf(errorP, "Number is too large for computation");
+             else {
+                 if (longValue < 0)
+                     pm_asprintf(errorP, "Number is negative");
+                 else {
+                     if ((unsigned int)longValue != longValue)
+                         pm_asprintf(errorP,
+                                     "Number is too large for computation");
+                     else {
+                         *uintP = (unsigned int)longValue;
+                         *errorP = NULL;
+                     }
+                 }
+             }
         }
     }
 }
 
 
+
diff --git a/lib/util/nstring.h b/lib/util/nstring.h
index 7238a76e..5159277c 100644
--- a/lib/util/nstring.h
+++ b/lib/util/nstring.h
@@ -3,6 +3,7 @@
 
 #include <stdarg.h>
 #include <string.h>
+#include <strings.h>  /* For strncasecmp */
 #include <ctype.h>
 
 #include "pm_c_util.h"
@@ -59,15 +60,21 @@ static __inline__ int
 memeq(const void * const comparand,
       const void * const comparator,
       size_t       const size) {
-    
+
     return memcmp(comparand, comparator, size) == 0;
 }
 
-/* The Standard C Library may not declare strcasecmp() if the including
-   source file doesn't request BSD functions, with _BSD_SOURCE.  So
-   we don't define functions that use strcasecmp() in that case.
+/* The Standard C Library may not declare strcasecmp() if the including source
+   file doesn't request BSD functions, with _BSD_SOURCE or SUSv2 function,
+   with _XOPEN_SOURCE >= 500.  So we don't define functions that use
+   strcasecmp() in that case.
+
+   (Actually, _XOPEN_SOURCE 500 is stronger than you need for strcasecmp -
+   _XOPEN_SOURCE_EXTENDED, which asks for XPG 4, would do, whereas
+   _XOPEN_SOURCE 500 asks for XPG 5, but for simplicity, we don't use
+   _XOPEN_SOURCE_EXTENDED in Netpbm.
 */
-#ifdef _BSD_SOURCE
+#if defined(_BSD_SOURCE) || (defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE - 0) >= 500)
 static __inline__ int
 strcaseeq(const char * const comparand,
           const char * const comparator) {
@@ -85,7 +92,7 @@ strncaseeq(const char * const comparand,
 #endif
 
 
-/* The standard C library routines isdigit(), for some weird 
+/* The standard C library routines isdigit(), for some weird
    historical reason, does not take a character (type 'char') as its
    argument.  Instead it takes an integer.  When the integer is a whole
    number, it represents a character in the obvious way using the local
@@ -125,7 +132,7 @@ strncaseeq(const char * const comparand,
    Netpbm must include them in its own libraries, and because some
    standard C libraries have some of them, Netpbm must use different
    names for them.
-   
+
    The GNU C library has all of them.  All but the oldest standard C libraries
    have snprintf().
 
@@ -149,6 +156,10 @@ strncaseeq(const char * const comparand,
 
 extern const char * const pm_strsol;
 
+size_t
+pm_strnlen(const char * const s,
+           size_t       const maxlen);
+
 int
 pm_snprintf(char *       const dest,
             size_t       const str_m,
@@ -178,7 +189,7 @@ pm_vasprintf(const char ** const resultP,
 bool
 pm_vasprintf_knows_float(void);
 
-void 
+void
 pm_strfree(const char * const string);
 
 const char *
@@ -198,9 +209,9 @@ bool
 pm_strishex(const char * const subject);
 
 void
-pm_interpret_uint(const char *   const string,
-               unsigned int * const valueP,
-               const char **  const errorP);
+pm_string_to_uint(const char *   const string,
+                  unsigned int * const uintP,
+                  const char **  const errorP);
 
 #ifdef __cplusplus
 }
diff --git a/lib/util/pm_c_util.h b/lib/util/pm_c_util.h
index 01a07657..a093adb6 100644
--- a/lib/util/pm_c_util.h
+++ b/lib/util/pm_c_util.h
@@ -20,6 +20,13 @@
 #define ROUND(X) (((X) >= 0) ? (int)((X)+0.5) : (int)((X)-0.5))
 #undef ROUNDU
 #define ROUNDU(X) ((unsigned int)((X)+0.5))
+    /* Note that imprecision in floating point arithmetic can make an exact
+       half fractional part round down instead of up.  What should be
+       1000.5 might actually be 1000.49999999999.
+
+       Use 'pnm_unnormalized_sample' instead of ROUNDU(samplen*maxval) to get
+       consistent rounding up when you are unnormalizing sample values.
+    */
 
 /* ROUNDUP rounds up to a specified multiple.  E.g. ROUNDUP(22, 8) == 24 */
 
@@ -53,9 +60,11 @@
 #else
   /* The test for __STDC__ is paranoid.  It is there just in case some
      nonstandard compiler defines __STDC_VERSION__ in an arbitrary manner.
+
+     We know GCC 2.95.3 has stdbool; not sure about earlier GCC 2.
   */
-  #if ( defined(__GNUC__) && (__GNUC__ >= 3) ) || \
-      ( defined(__STDC__) && (__STDC__ ==1) && \
+  #if ( defined(__GNUC__) && (__GNUC__ >= 2) ) || \
+      ( defined(__STDC__) && (__STDC__ == 1) && \
         defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) ) 
     #include <stdbool.h>
   #else
diff --git a/lib/util/runlength.c b/lib/util/runlength.c
index e5c60db0..62e3bf0c 100644
--- a/lib/util/runlength.c
+++ b/lib/util/runlength.c
@@ -12,8 +12,8 @@
   documentation.  This software is provided "as is" without express or implied
   warranty.
 
-  Functions pm_rlenc_byte() and pm_rlenc_word() are based on algorithm
-  originally by Robert A. Knop (rknop@mop.caltech.edu).
+  Functions pm_rlenc_compressbyte() and pm_rlenc_compressword() are based
+  on algorithm originally by Robert A. Knop (rknop@mop.caltech.edu).
 
   Those who wish to incorporate the code herein into their own programs are
   strongly discouraged from removing the comments within borders consisting
@@ -22,9 +22,9 @@
 
 ===============================================================================
 
-  Packbits is a simple yet efficient simple run-length compression method.  It
-  has a provision for uncompressed sequences which allows efficient encoding
-  of noisy input.
+  Packbits is a simple yet efficient simple run-length compression method.
+  It has a provision for uncompressed sequences which allows efficient
+  encoding of noisy input.
 
   A survey of netpbm source code in 2015 found Packbits encoding in the
   following Netpbm programs: pbmtoescp2, pbmtomacp, pnmtopalm, pnmtopclxl,
@@ -34,19 +34,20 @@
   images that use Packbits compression, but does so via code in the TIFF
   library (not part of Netpbm).
 
-  Variants of Packbits are employed in pnmtosgi,  pamtopdbimg,  pbmtoppa
-  and  pbmtogo.
+  Variants of Packbits are employed in pnmtosgi, pamtopdbimg, pbmtoppa
+  pbmtogo and pamtotga.
 
   All the above programs formerly did the same compression through different
   code.  This redundancy bloated the Netpbm package and made maintenance
   difficult.  This maintenance difficulty surfaced as an issue when tests with
   valgrind revealed multiple memory-related problems in the above programs.
-  Indeed, just determining which programs do this compression by this method
+  Indeed, just determining which programs do compression by this method
   was not simple because of differences in terminology.  "Packbits" is often
   called "run length encoding."  In ppmtoilbm the name is "byterun1."
 
-  Today, all Netpbm programs that do Packbits compression use the facilities
-  in this file for it.
+  Today, all Netpbm programs that do Packbits compression with the exception
+  of pamtotiff, pbmtoppa, pbmtogo and pamtotga use the facilities in this
+  file for it.  
 =============================================================================*/
 
 #include <string.h>
@@ -68,11 +69,11 @@ static const char * const errorUndefinedMode =
 
    In this simple run-length encoding scheme, compressed and uncompressed
    strings follow a single index or "flag" byte N.  There are several
-   variations, differing in the format of the flag byte, maximum string length
-   and element size (byte or word).
+   variations, differing in the format of the flag byte, maximum string
+   length and element size (byte or word).
    
-   In the most widely used version, Packbits, the meaning of the flag byte N
-   is defined as follows:
+   In the most widely used version, Packbits, the meaning of the flag byte
+   N is defined as follows:
 
     0-127 means the next N+1 bytes are uncompressed.
     129-255 means the next byte is to be repeated 257-N times.
@@ -99,7 +100,7 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf,
   Always encode 3-byte repeat sequences.
 
   Encode 2-byte repeat sequences only when they are at the start of the block.
-  This ensures that the output is never unnecessary bloated.
+  This ensures that the output is never unnecessarily bloated.
 
   Original algorithm by Robert A. Knop (rknop@mop.caltech.edu)
 -----------------------------------------------------------------------------*/
@@ -107,8 +108,17 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf,
 
     size_t inCurs, outCurs;
 
-    if (mode != PM_RLE_PACKBITS)
-        pm_error(errorUndefinedMode, mode);
+    int packBase;
+    int packSign;
+ 
+    switch (mode) {
+    case PM_RLE_PACKBITS:
+        packBase = 257 ; packSign = -1; break;
+    case PM_RLE_PALMPDB:
+        packBase = 127 ; packSign = +1; break;
+    default:
+         pm_error(errorUndefinedMode, mode);
+    }
 
     for (inCurs = 0, outCurs = 0; inCurs < inSize; ) {
         if ((inCurs < inSize - 1) && (inbuf[inCurs] == inbuf[inCurs+1])) {
@@ -121,8 +131,7 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf,
                      count < maxRun; 
                  ++inCurs, ++count)
                 ;
-
-            outbuf[outCurs++] = (unsigned char)(257 - count);
+            outbuf[outCurs++] = (unsigned char) (packBase + packSign * count);
             outbuf[outCurs++] = inbuf[hold];
         } else {
             /*Do a non-run*/
@@ -176,11 +185,11 @@ pm_rlenc_compressword(const uint16_t   * const inbuf,
                       size_t             const inSize,
                       size_t           * const outputSizeP) {
 /*---------------------------------------------------------------------------
-   Similar to pm_rlenc_byte(), but this works with two-byte elements.  The
-   difference between SGI16 and PALM16 is the size of the flag.  SGI16 : 16
-   bits; PALM16 : 8 bits
+   Similar to pm_rlenc_compressbyte(), but this works with two-byte elements.
+   The difference between SGI16 and PALM16 is the size of the flag.
+   SGI16 : 16 bits; PALM16 : 8 bits
 
-   'inSize' is a number of words,but *outputSizeP is a number of bytes.
+   'inSize' is a number of words, but *outputSizeP is a number of bytes.
 -----------------------------------------------------------------------------*/
     size_t inCurs, outCurs;
     size_t flagSz;
@@ -258,6 +267,8 @@ pm_rlenc_compressword(const uint16_t   * const inbuf,
    number of flag bytes.  The worst case happens when there are no repeat
    sequences in the input.
 
+   The encoder in this file is an efficient one.
+
    The key to an efficient encoder is correct handling of short, inefficient
    sequences.  The following algorithm (not actually used in this file)
    describes the idea.
@@ -267,9 +278,9 @@ pm_rlenc_compressword(const uint16_t   * const inbuf,
    different from its neighbors.
 
    A compressed block is one that encodes a sequence of identical values
-   (which could be a run or just part of a run) as a value and a count.
-   count.  An uncompressed block is one that encodes a sequence of any values
-   by listing the values individually.
+   (which could be a run or just part of a very long run) as a value and
+   a count.  An uncompressed block is one that encodes a sequence of any
+   values by listing the values individually.
 
    Start by encoding the entire input as uncompressed blocks.  Seek runs that
    can be encoded with compressed blocks, but only if doing so doesn't make
@@ -288,6 +299,16 @@ pm_rlenc_compressword(const uint16_t   * const inbuf,
 
      - Whenever savings are larger than the added overhead, encode the run
        as a compressed block.
+
+   -----------------------------------------------------------------------
+
+   MS-DOS/Windows BMP format note
+
+   The compression method for BMP is a variant of packbits.
+   We could support the 8-bit version with some modifications to functions
+   in this file.  Determining the worst-case output size of an efficient
+   coder is rather complicated because uncompressed blocks may not be less
+   than three bytes long and are indicated by two-byte flags.
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
 
 
@@ -310,23 +331,31 @@ pm_rlenc_maxbytes(size_t          const inSize,  /* number of elements */
    /* The upper limit could be raised above INT_MAX, but no program needs that
       today.
    */
-    size_t blockSize;
-    size_t flagSize;
-    size_t itemSize;
-    size_t miscSize;
-    size_t overhead;
+    size_t blockSize;  /* Number of items in data block */
+    size_t flagSize;   /* Size of flag, in bytes */
+    size_t itemSize;   /* Size of item, in bytes */
+    size_t miscSize;   /* Size of other elements such as term code, in bytes */
+    size_t overhead;   /* Worst-case overhead, in bytes */
+    /* return value:      Worst-case output size, in bytes */ 
 
     switch (mode) {
     case PM_RLE_PACKBITS:
-        blockSize = 128; flagSize = 1; itemSize = 1; miscSize = 0; break;
+    case PM_RLE_PALMPDB:
+        blockSize = 128; flagSize = 1; itemSize = 1; miscSize = 0;
+        break;
     case PM_RLE_SGI8:
-        blockSize = 127; flagSize = 1; itemSize = 1; miscSize = 0; break;
-    case PM_RLE_GRAPHON:  case PM_RLE_PPA:
-        blockSize =  64; flagSize = 1; itemSize = 1; miscSize = 0; break;
+        blockSize = 127; flagSize = 1; itemSize = 1; miscSize = 0;
+        break;
+    case PM_RLE_GRAPHON:
+    case PM_RLE_PPA:
+        blockSize =  64; flagSize = 1; itemSize = 1; miscSize = 0;
+        break;
     case PM_RLE_SGI16:
-        blockSize = 127; flagSize = 2; itemSize = 2; miscSize = 2; break;
+        blockSize = 127; flagSize = 2; itemSize = 2; miscSize = 2;
+        break;
     case PM_RLE_PALM16:
-        blockSize = 128; flagSize = 1; itemSize = 2; miscSize = 0; break;
+        blockSize = 128; flagSize = 1; itemSize = 2; miscSize = 0;
+        break;
     default:
         pm_error(errorUndefinedMode, mode);
     }
diff --git a/lib/util/runlength.h b/lib/util/runlength.h
index 4857ae61..de921650 100644
--- a/lib/util/runlength.h
+++ b/lib/util/runlength.h
@@ -18,7 +18,8 @@ enum pm_RleMode { PM_RLE_PACKBITS,          /* most common mode */
                   PM_RLE_PPA,               /* reserved */
                   PM_RLE_SGI8,              /* reserved */
                   PM_RLE_SGI16,
-                  PM_RLE_PALM16
+                  PM_RLE_PALM16,
+                  PM_RLE_PALMPDB  
                 };
 
 size_t
diff --git a/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/lib/util/shhopt.h b/lib/util/shhopt.h
index 9a446290..d9304f9f 100644
--- a/lib/util/shhopt.h
+++ b/lib/util/shhopt.h
@@ -4,7 +4,7 @@ HERE IS AN EXAMPLE OF THE USE OF SHHOPT:
 
 
 #include <shhopt.h>
-int 
+int
 main ( int argc, char **argv ) {
 
     /* initial values here are just to demonstrate what gets set and
@@ -18,19 +18,21 @@ main ( int argc, char **argv ) {
     int height=7;
     int verbose_flag=7;
     int debug_flag=7;
+    char ** methodlist;
     struct optNameValue * optlist;
-    
+
     optStruct3 opt;
     unsigned int option_def_index = 0;
     optEntry * option_def;
     MALLOCARRAY(option_def, 100);
 
-    OPTENT3(0,   "help",     OPT_FLAG,     &help_flag,    &help_spec,   0);
-    OPTENT3(0,   "height",   OPT_INT,      &height,       &height_spec, 0);
-    OPTENT3('n', "name",     OPT_STRING,   &name,         &name_spec,   0);
-    OPTENT3('v', "verbose",  OPT_FLAG,     &verbose_flag, NULL,         0);
-    OPTENT3('g', "debug",    OPT_FLAG,     &debug_flag,   NULL,         0);
-    OPTENT3(0,   "options",  OPT_NAMELIST, &optlist,      NULL,         0);
+    OPTENT3(0,   "help",     OPT_FLAG,       &help_flag,    &help_spec,   0);
+    OPTENT3(0,   "height",   OPT_INT,        &height,       &height_spec, 0);
+    OPTENT3('n', "name",     OPT_STRING,     &name,         &name_spec,   0);
+    OPTENT3('v', "verbose",  OPT_FLAG,       &verbose_flag, NULL,         0);
+    OPTENT3('g', "debug",    OPT_FLAG,       &debug_flag,   NULL,         0);
+    OPTENT3(0,   "methods",  OPT_STRINGLIST, &methodlist,   NULL,         0);
+    OPTENT3(0,   "options",  OPT_NAMELIST,   &optlist,      NULL,         0);
 
     opt.opt_table = option_def;
     opt.short_allowed = 1;
@@ -38,7 +40,7 @@ main ( int argc, char **argv ) {
 
 
     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
-    
+
 
     printf("argc=%d\n", argc);
     printf("help_flag=%d\n", help_flag);
@@ -50,13 +52,24 @@ main ( int argc, char **argv ) {
     printf("verbose_flag=%d\n", verbose_flag);
     printf("debug_flag=%d\n", verbose_flag);
 
+    if (methodlist) {
+        unsigned int i;
+        printf("methods: ");
+        while (methodlist[i]) {
+            printf("'%s', ", methodlist[i]);
+            ++i;
+        }
+        free(methodlist);
+    } else
+        printf("No -options\n");
+
     if (optlist) {
         unsigned int i;
         while (optlist[i].name) {
             printf("option '%s' = '%s'\n", optlist[i].name, optlist[i].value);
             ++i;
         }
-        optDestroyNameValueList(optlist);
+        pm_optDestroyNameValueList(optlist);
     } else
         printf("No -options\n");
 }
@@ -116,7 +129,7 @@ typedef struct {
                             * or pointer to function if type == OPT_FUNC. */
     int        flags;      /* modifier flags. */
 } optStruct;
-    
+
 typedef struct {
     /* This structure describes a single program option in a form for
      use by the pm_optParseOptions3() function.
@@ -124,10 +137,10 @@ typedef struct {
     char       shortName;  /* short option name. */
     const char *longName;  /* long option name, not including '--' or '-' */
     optArgType type;       /* option type. */
-    void       *arg;       
+    void       *arg;
         /* pointer to variable in which to return option's argument (or TRUE
-           if it's a flag option), or pointer to function if 
-           type == OPT_FUNC.  If the option is specified multiple times, only 
+           if it's a flag option), or pointer to function if
+           type == OPT_FUNC.  If the option is specified multiple times, only
            the rightmost one affects this return value.
         */
     unsigned int *specified;
@@ -136,7 +149,7 @@ typedef struct {
         */
     int        flags;      /* modifier flags. */
 } optEntry;
-    
+
 
 typedef struct {
     /* This structure describes the options of a program in a form for
@@ -146,20 +159,20 @@ typedef struct {
         /* The syntax may include short (i.e. one-character) options.
            These options may be stacked within a single token (e.g.
            -abc = -a -b -c).  If this value is not true, the short option
-           member of the option table entry is meaningless and long 
+           member of the option table entry is meaningless and long
            options may have either one or two dashes.
            */
     unsigned char allowNegNum;  /* boolean */
         /* Anything that starts with - and then a digit is a numeric
-           parameter, not an option 
+           parameter, not an option
            */
     optStruct *opt_table;
 } optStruct2;
 
 typedef struct {
     /* Same as optStruct2, but for pm_optParseOptions3() */
-    unsigned char short_allowed;  
-    unsigned char allowNegNum;    
+    unsigned char short_allowed;
+    unsigned char allowNegNum;
     optEntry *opt_table;
 } optStruct3;
 
@@ -173,9 +186,9 @@ typedef struct {
        optStruct *option_def = malloc(100*sizeof(optStruct));
        OPTENTRY('h', "help",     OPT_FLAG, &help_flag, 0);
        OPTENTRY(0,   "alphaout", OPT_STRING, &alpha_filename, 0);
-*/   
+*/
 
-/* If you name your variables option_def and option_def_index like in the 
+/* If you name your variables option_def and option_def_index like in the
    example above, everything's easy.  If you want to use OPTENTRY with other
    variables, define macros OPTION_DEF and OPTION_DEF_INDEX before calling
    OPTENTRY.
@@ -217,7 +230,10 @@ typedef struct {
     OPTENTRY(shortvalue, longvalue, typevalue, outputvalue, flagvalue) \
     }
 
-#define OPTENTINIT OPTION_DEF[0].type = OPT_END
+#define OPTENTINIT \
+    do {OPTION_DEF_INDEX=0; \
+        OPTION_DEF[OPTION_DEF_INDEX].type = OPT_END; \
+    } while (0)
 
 
 struct optNameValue {
@@ -226,7 +242,7 @@ struct optNameValue {
 };
 
 
-        
+
 void
 pm_optSetFatalFunc(void (*f)(const char *, ...));
 
@@ -234,10 +250,10 @@ void
 pm_optParseOptions(int *argc, char *argv[],
                    optStruct opt[], int allowNegNum);
 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);
 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);
 
 void
diff --git a/lib/util/wordaccess.h b/lib/util/wordaccess.h
deleted file mode 100644
index df0eaf12..00000000
--- a/lib/util/wordaccess.h
+++ /dev/null
@@ -1,75 +0,0 @@
-#ifndef WORDACCESS_H_INCLUDED
-#define WORDACCESS_H_INCLUDED
-
-/* These are facilities for accessing data in C programs in ways that
-   exploit the way the machine defines words in order to squeeze out
-   speed and CPU efficiency.
-
-   In particular, routines in this file exploit the endianness of the
-   machine and use explicit machine instructions to access C
-   variables.
-
-   A word is the amount of data that fits in a register; the amount of
-   data that a single machine instruction can process.  For example,
-   on IA32, a word is 32 bits because a single load or store
-   instruction moves that many bits and a single add instruction
-   operates on that many bits.
-
-
-   These facilities revolve around two data types:  wordInt and
-   wordIntBytes.
-
-   wordint is an unsigned integer with precision (size) of one word.
-   It is just the number -- nothing is implied about how it is
-   represented in memory.
-
-   wordintBytes is an array of bytes that represent a word-sized
-   unsigned integer.  x[0] is the high order 8 digits of the binary
-   coding of the integer, x[1] the next highest 8 digits, etc.
-   Note that it has big-endian form, regardless of what endianness the
-   underlying machine uses.
-
-   The actual size of word differs by machine.  Usually it is 32 or 64
-   bits.  Logically it can be as small as one byte.  Fixed bit sequences
-   in each program impose a lower limit of word width.  For example, the
-   longest bit sequence in pbmtog3 has 13 bits, so an 8-bit word won't
-   work with that.
-
-   We also assume that a char is 8 bits.
-
-   HAVE_GCC_BITCOUNT and HAVE_GCC_BSWAP are set in pm_config.h
-
-   BITS_PER_LONG is the number of bits in long int.
-*/
-
-#include "pm_config.h"
-
-#if defined(WORDACCESS_GENERIC)
-  /* User wants this, regardless of whether machine can do better */
-  #include "wordaccess_generic.h"
-#elif BYTE_ORDER == BIG_ENDIAN
-  #if UNALIGNED_OK
-     #include "wordaccess_be_unaligned.h"
-  #else
-    /* Sparc */
-    #include "wordaccess_be_aligned.h"
-  #endif
-#elif HAVE_GCC_BITCOUNT
-  #if (BITS_PER_LONG == 64)
-    /* AMD Athlon 64, Intel x86_64, Intel Itanium, etc. */
-    #include "wordaccess_64_le.h"
-  #elif (BITS_PER_LONG == 32)
-    /* Intel x86_32 (80386, 80486, Pentium), etc. */
-      #include "wordaccess_generic.h"
-  #else
-     /* Extremely rare case.
-        If long is neither 32 nor 64 bits, (say, 128) it comes here.
-     */
-     #include "wordaccess_generic.h"
-  #endif
-#else
-  /* Non GCC or GCC prior to v.3.4; little-endian  */
-  #include "wordaccess_generic.h"
-#endif
-
-#endif
diff --git a/lib/util/wordaccess_64_le.h b/lib/util/wordaccess_64_le.h
deleted file mode 100644
index 4d148ad2..00000000
--- a/lib/util/wordaccess_64_le.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*=============================================================================
-  This file is the part of wordaccess.h for use under these
-  conditions:
-
-  * GCC (>=3.4)  (__builtin_clz appears in GCC 3.4)
-  * Little-Endian machines (IA64, X86-64, AMD64)
-  * 64 bit long
- 
-=============================================================================*/
-
-#include "intcode.h"
-
-typedef uint64_t wordint;
-typedef unsigned char wordintBytes[sizeof(wordint)];
-
-
-static __inline__ wordint
-bytesToWordint(wordintBytes const bytes) {
-    return (wordint) pm_uintFromBigend64(*(bigend64*)bytes);
-}
-
-
-
-static __inline__ void
-wordintToBytes(wordintBytes * const bytesP,
-               wordint        const wordInt) {
-     *(bigend64*)bytesP = pm_bigendFromUint64(wordInt);
-}
diff --git a/lib/util/wordaccess_be_aligned.h b/lib/util/wordaccess_be_aligned.h
deleted file mode 100644
index f3bbb841..00000000
--- a/lib/util/wordaccess_be_aligned.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*=============================================================================
-  This file is the part of wordaccess.h for use under with big-endian
-  machines that require word accesses to be word-aligned.
-*===========================================================================*/
-
-typedef unsigned long int wordint;
-typedef unsigned char wordintBytes[sizeof(wordint)];
-
-static __inline__ wordint
-bytesToWordint(wordintBytes bytes) {
-    uint16_t const hi = *((uint16_t *) (bytes + 0));
-    uint16_t const mh = *((uint16_t *) (bytes + 2));
-    uint16_t const ml = *((uint16_t *) (bytes + 4));
-    uint16_t const lo = *((uint16_t *) (bytes + 6));
-    return
-        (((wordint) hi) << 48) |
-        (((wordint) mh) << 32) |
-        (((wordint) ml) << 24) |
-        (((wordint) lo) <<  0);
-}
-
-
-
-static __inline__ void
-wordintToBytes(wordintBytes * const bytesP,
-               wordint        const wordInt) {
-    uint16_t const hi = ((wordInt >> 48) & 0xFF);
-    uint16_t const mh = ((wordInt >> 32) & 0xFF);
-    uint16_t const ml = ((wordInt >> 24) & 0xFF);
-    uint16_t const lo = ((wordInt >>  0) & 0xFF);
-    *(uint16_t *)(bytesP + 0) = hi;
-    *(uint16_t *)(bytesP + 2) = mh;
-    *(uint16_t *)(bytesP + 4) = ml;
-    *(uint16_t *)(bytesP + 6) = lo;
-}
diff --git a/lib/util/wordaccess_be_unaligned.h b/lib/util/wordaccess_be_unaligned.h
deleted file mode 100644
index 95b68ac7..00000000
--- a/lib/util/wordaccess_be_unaligned.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*=============================================================================
-  This file is the part of wordaccess.h for use on a big-endian machine
-  that does not require word accesses to be word-aligned.
-*===========================================================================*/
-
-typedef unsigned long int wordint;
-typedef unsigned char wordintBytes[sizeof(wordint)];
-
-static __inline__ wordint
-bytesToWordint(wordintBytes bytes) {
-    return *((wordint *)bytes);
-}
-
-
-
-static __inline__ void
-wordintToBytes(wordintBytes * const bytesP,
-               wordint        const wordInt) {
-    *(wordint *)bytesP = wordInt;
-}
diff --git a/lib/util/wordaccess_generic.h b/lib/util/wordaccess_generic.h
deleted file mode 100644
index 6e0a20ef..00000000
--- a/lib/util/wordaccess_generic.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*=============================================================================
-
-  This file is the part of wordaccess.h for use under any of these
-  conditions:
-
-  * Compiler other than GCC
-  * GCC before version 3.4
-  * Requested by the user with WORDACCESS_GENERIC
-=============================================================================*/
-
-#include "intcode.h"
-
-typedef uint32_t wordint;
-typedef unsigned char wordintBytes[sizeof(wordint)];
-
-    
-static __inline__ wordint
-bytesToWordint(wordintBytes  const bytes) {
-
-    return (wordint)  pm_uintFromBigend32( * (bigend32*) bytes);
-}
-
-
-static __inline__ void
-wordintToBytes(wordintBytes * const bytesP,
-               wordint    const wordInt) {
-
-    * (bigend32*) bytesP = pm_bigendFromUint32((uint32_t)wordInt);
-}
diff --git a/lib/util/wordintclz.h b/lib/util/wordintclz.h
deleted file mode 100644
index 32e6ade8..00000000
--- a/lib/util/wordintclz.h
+++ /dev/null
@@ -1,93 +0,0 @@
-#ifndef WORDINTCLZ_H_INCLUDED
-#define WORDINTCLZ_H_INCLUDED
-
-#if (!defined(WORDACCESS_GENERIC) && HAVE_GCC_BITCOUNT )
-/* 
-   Compiler is GCC and has __builtin_clz()
-   wordint is long 
- 
-   __builtin_clz is available on GCC 3.4 and above
-     
-   Note that the clz scheme does not work and requires adjustment
-   if long type does not make use of all bits for data storage.
-  
-   This is unlikely.  According to GNU MP (http://www.swox.com/gmp/),
-   in rare cases such as Cray, there are smaller data types that take up
-   the same space as long, but leave the higher bits silent.
-   Currently, there are no known such cases for data type long.
- */
-
-static __inline__ unsigned int
-wordintClz(wordint const x){
-
-    assert(sizeof(unsigned long int) == sizeof(wordint));
-
-    if (x == 0)
-        return sizeof(wordint) * 8;
-    else
-        return (__builtin_clzl( (unsigned long int) x ));
-}
-  
-#else
-
-/* wordint is uint32_t: exactly 32 bits wide */
- 
-static unsigned char const clz8[256]= {
-    8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
-    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
-    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 
-};
-
-
-
-static __inline__ unsigned int
-clz16(wordint const x) {
-
-    if (x >> 8 != 0)
-        return clz8[x >> 8];
-    else
-        return clz8[x] + 8;
-}
-
-
-
-static __inline__  unsigned int
-clz32(wordint const x) {
-
-    if (x >> 16 != 0)
-        return clz16(x >> 16);
-    else
-        return clz16(x) + 16;
-}
-
-
-
-static __inline__  unsigned int
-wordintClz(wordint const x) {
-
-    assert(sizeof(wordint) == 4);
-
-    return clz32(x);
-}
-
-/* Another way to calculate leading zeros:
-   x == 0 ? 32 : 31 - floor(log(x)/log(2))
-   (Beware: insufficient precision will cause errors)
-*/
-
-#endif
-
-#endif