about summary refs log tree commit diff
path: root/generator
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2018-03-25 17:14:57 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2018-03-25 17:14:57 +0000
commitcd86a2c8d798c98b2f5d7656971fc4553b4ed172 (patch)
tree6218a52f6e09f0cb0ff15c0a0b130da920328e8e /generator
parente488b82f0f446576138084a1bd57b7b4406e8db0 (diff)
downloadnetpbm-mirror-cd86a2c8d798c98b2f5d7656971fc4553b4ed172.tar.gz
netpbm-mirror-cd86a2c8d798c98b2f5d7656971fc4553b4ed172.tar.xz
netpbm-mirror-cd86a2c8d798c98b2f5d7656971fc4553b4ed172.zip
Copy Development as new Advanced
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@3186 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'generator')
-rw-r--r--generator/pbmtext.c658
-rw-r--r--generator/ppmpat.c97
-rwxr-xr-xgenerator/ppmrainbow4
3 files changed, 491 insertions, 268 deletions
diff --git a/generator/pbmtext.c b/generator/pbmtext.c
index e25c6bbe..0fe8ad6a 100644
--- a/generator/pbmtext.c
+++ b/generator/pbmtext.c
@@ -19,6 +19,8 @@
 #include <limits.h>
 #include <assert.h>
 #include <setjmp.h>
+#include <locale.h>
+#include <wchar.h>
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
@@ -34,21 +36,64 @@ struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char * text;    /* text from command line or NULL if none */
+    const PM_WCHAR * text; /* text from command line or NULL if none */
     const char * font;    /* -font option value or NULL if none */
     const char * builtin; /* -builtin option value or NULL if none */
     float space;          /* -space option value or default */
     int lspace;           /* -lspace option value or default */
     unsigned int width;   /* -width option value or zero */
+    unsigned int wchar;   /* -wchar option specified  */
     unsigned int nomargins;  /* -nomargins option specified  */
-    unsigned int dryrun;  /* -dry-run option specified */ 
-    unsigned int verbose; /* -verbose option specified */
+    unsigned int dryrun;     /* -dry-run option specified */
+    unsigned int textdump;   /* -text-dump option specified */
+    unsigned int verbose;    /* -verbose option specified */
         /* undocumented option */
-    unsigned int dumpsheet; /* font data sheet in PBM format for -font */   
+    unsigned int dumpsheet; /* font data sheet in PBM format for -font */
 };
 
 
 
+static const PM_WCHAR *
+textFmCmdLine(int argc, const char ** argv) {
+
+    char * text;
+    PM_WCHAR * wtext;
+    unsigned int i;
+    unsigned int totaltextsize;
+
+    MALLOCARRAY(text, MAXLINECHARS+1);
+
+    if (!text)
+        pm_error("Unable to allocate memory for a buffer of up to %u "
+                 "characters of text", MAXLINECHARS);
+
+    text[0] = '\0';
+
+    for (i = 1, totaltextsize = 1; i < argc; ++i) {
+        if (i > 1) {
+            strcat(text, " ");
+        }
+        totaltextsize += strlen(argv[i]) + 1;
+        if (totaltextsize > MAXLINECHARS)
+            pm_error("input text too long");
+        strcat(text, argv[i]);
+    }
+    MALLOCARRAY(wtext, totaltextsize * sizeof(PM_WCHAR));
+
+    if (!wtext)
+        pm_error("Unable to allocate memory for a buffer of up to %u "
+                 "wide characters of text", totaltextsize);
+
+    for (i = 0; i < totaltextsize; ++i)
+        wtext[i] = (PM_WCHAR) text[i];
+
+    free(text);
+
+    return wtext;
+}
+
+
+
 static void
 parseCommandLine(int argc, const char ** argv,
                  struct CmdlineInfo * const cmdlineP) {
@@ -72,8 +117,10 @@ parseCommandLine(int argc, const char ** argv,
     OPTENT3(0, "lspace",     OPT_INT,    &cmdlineP->lspace,  NULL,   0);
     OPTENT3(0, "width",      OPT_UINT,   &cmdlineP->width,   NULL,   0);
     OPTENT3(0, "nomargins",  OPT_FLAG,   NULL, &cmdlineP->nomargins, 0);
+    OPTENT3(0, "wchar",      OPT_FLAG,   NULL, &cmdlineP->wchar,     0);
     OPTENT3(0, "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,   0);
     OPTENT3(0, "dry-run",    OPT_FLAG,   NULL, &cmdlineP->dryrun,    0);
+    OPTENT3(0, "text-dump",  OPT_FLAG,   NULL, &cmdlineP->textdump,  0);
     OPTENT3(0, "dump-sheet", OPT_FLAG,   NULL, &cmdlineP->dumpsheet, 0);
 
     /* Set the defaults */
@@ -90,7 +137,7 @@ parseCommandLine(int argc, const char ** argv,
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
     /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (cmdlineP->width > 0 && cmdlineP->nomargins == TRUE) {
+    if (cmdlineP->width > 0 && cmdlineP->nomargins) {
         pm_message("-nomargins has no effect when -width is specified");
         cmdlineP->nomargins = FALSE;
     } else if (cmdlineP->width > INT_MAX-10)
@@ -106,33 +153,25 @@ parseCommandLine(int argc, const char ** argv,
     else if (cmdlineP->lspace < -pbm_maxfontheight())
         pm_error("negative -lspace value too large");
 
+    if (cmdlineP->textdump) {
+        if (cmdlineP->dryrun)
+            pm_error("You cannot specify both -dry-run and -text-dump");
+        else if (cmdlineP->dumpsheet)
+            pm_error("You cannot specify both -dump-sheet and -text-dump");
+    }
+
+    if (cmdlineP->dryrun && cmdlineP->dumpsheet)
+        pm_error("You cannot specify both -dry-run and -dump-sheet");
+
     if (argc-1 == 0)
         cmdlineP->text = NULL;
-    else {
-        char *text;
-        int i;
-        int totaltextsize;
-
-        totaltextsize = 1;  /* initial value */
-
-        MALLOCARRAY(text, MAXLINECHARS+1);        
-
-        if (!text)
-            pm_error("Unable to allocate memory for a buffer of up to %u "
-                     "characters of text", MAXLINECHARS);
-
-        text[0] = '\0';
-        
-        for (i = 1; i < argc; ++i) {
-            if (i > 1) {
-                strcat(text, " ");
-            } 
-            totaltextsize += strlen(argv[i]) + 1;
-            if (totaltextsize > MAXLINECHARS)
-                pm_error("input text too long");
-            strcat(text, argv[i]);
-        }
-        cmdlineP->text = text;
+    else {  /* Text to render is part of command line */
+        if (cmdlineP->wchar)
+            pm_error("-wchar is not valid when text is from command line");
+
+        cmdlineP->text = textFmCmdLine(argc, argv);
+
+
     }
     free(option_def);
 }
@@ -140,7 +179,7 @@ parseCommandLine(int argc, const char ** argv,
 
 
 static void
-reportFont(struct font * const fontP) {
+reportFont(struct font2 * const fontP) {
 
     unsigned int n;
     unsigned int c;
@@ -150,9 +189,10 @@ reportFont(struct font * const fontP) {
                fontP->maxwidth, fontP->maxheight);
     pm_message("  Additional vert white space: %d pixels", fontP->y);
 
-    for (c = 0, n = 0; c < ARRAY_SIZE(fontP->glyph); ++c)
+    for (c = 0, n = 0; c <= fontP->maxglyph; ++c) {
         if (fontP->glyph[c])
             ++n;
+    }
 
     pm_message("  # characters: %u", n);
 }
@@ -162,7 +202,7 @@ reportFont(struct font * const fontP) {
 static struct font *
 fontFromFile(const char * const fileName) {
 
-    struct font * retval;
+    struct font  * retval;
 
     jmp_buf jmpbuf;
     int rc;
@@ -190,31 +230,72 @@ fontFromFile(const char * const fileName) {
 
 
 
+static struct font2 *
+font2FromFile(const char * const fileName) {
+
+    struct font2 * font2P;
+
+    jmp_buf jmpbuf;
+    int rc;
+
+    rc = setjmp(jmpbuf);
+
+    if (rc == 0) {
+        /* This is the normal program flow */
+        pm_setjmpbuf(&jmpbuf);
+
+        font2P = pbm_loadbdffont2(fileName, PM_FONT2_MAXGLYPH);
+
+        pm_setjmpbuf(NULL);
+    } else {
+        /* This is the second pass, after pbm_loadbdffont2 does a longjmp
+           because it fails.
+        */
+        pm_setjmpbuf(NULL);
+
+        pm_error("Failed to load font from file '%s'", fileName);
+    }
+
+    return font2P;
+}
+
+
+
 static void
 computeFont(struct CmdlineInfo const cmdline,
-            struct font **     const fontPP) {
+            struct font2 **    const fontPP) {
 
-    struct font * fontP;
+    struct font2 * font2P;
 
-    if (cmdline.font)
-        fontP = fontFromFile(cmdline.font);
+    if (cmdline.wchar && cmdline.font)
+        font2P = font2FromFile(cmdline.font);
     else {
-        if (cmdline.builtin)
-            fontP = pbm_defaultfont(cmdline.builtin);
-        else
-            fontP = pbm_defaultfont("bdf");
+        struct font  * fontP;
+
+        if (cmdline.font)
+            fontP = fontFromFile(cmdline.font);
+        else {
+            if (cmdline.builtin)
+                fontP = pbm_defaultfont(cmdline.builtin);
+            else
+                fontP = pbm_defaultfont("bdf");
+        }
+        font2P = pbm_expandbdffont(fontP);
     }
 
     if (cmdline.verbose)
-        reportFont(fontP);
+        reportFont(font2P);
 
-    *fontPP = fontP;
+    *fontPP = font2P;
 }
 
 
 
 struct Text {
-    char **      textArray;  /* malloc'ed */
+    PM_WCHAR **     textArray;  /* malloc'ed */
+        /* This is strictly characters that are in user's font - no control
+           characters, no undefined code points.
+        */
     unsigned int allocatedLineCount;
     unsigned int lineCount;
 };
@@ -248,7 +329,7 @@ freeTextArray(struct Text const text) {
     unsigned int line;
 
     for (line = 0; line < text.allocatedLineCount; ++line)
-        free((char **)text.textArray[line]);
+        free((PM_WCHAR **)text.textArray[line]);
 
     free(text.textArray);
 }
@@ -261,10 +342,10 @@ enum FixMode {SILENT, /* convert silently */
 
 
 static void
-fixControlChars(const char *  const input,
-                struct font * const fontP,
-                const char ** const outputP,
-                enum FixMode  const fixMode) {
+fixControlChars(const PM_WCHAR  * const input,
+                struct font2    * const fontP,
+                const PM_WCHAR ** const outputP,
+                enum FixMode      const fixMode) {
 /*----------------------------------------------------------------------------
    Return a translation of input[] that can be rendered as glyphs in
    the font 'fontP'.  Return it as newly malloced *outputP.
@@ -273,7 +354,7 @@ fixControlChars(const char *  const input,
 
    Remove any trailing newline.  (But leave intermediate ones as line
    delimiters).
-   
+
    Depending on value of fixMode, turn anything that isn't a code point
    in the font to a single space (which isn't guaranteed to be in the
    font either, of course).
@@ -289,10 +370,10 @@ fixControlChars(const char *  const input,
     unsigned int const tabSize = 8;
 
     unsigned int inCursor, outCursor;
-    char * output;      /* Output buffer.  Malloced */
+    PM_WCHAR * output;      /* Output buffer.  Malloced */
     size_t outputSize;  /* Currently allocated size of 'output' */
 
-    outputSize = strlen(input) + 1 + tabSize;
+    outputSize = wcslen(input) + 1 + tabSize;
         /* Leave room for one worst case tab expansion and NUL terminator */
     MALLOCARRAY(output, outputSize);
 
@@ -300,7 +381,8 @@ fixControlChars(const char *  const input,
         pm_error("Couldn't allocate %u bytes for a line of text.",
                  (unsigned)outputSize);
 
-    for (inCursor = 0, outCursor = 0; input[inCursor] != '\0'; ++inCursor) {
+    for (inCursor = 0, outCursor = 0; input[inCursor] != L'\0'; ++inCursor) {
+        PM_WCHAR const currentChar = input[inCursor];
         if (outCursor + 1 + tabSize > outputSize) {
             outputSize = outCursor + 1 + 4 * tabSize;
             REALLOCARRAY(output, outputSize);
@@ -308,29 +390,34 @@ fixControlChars(const char *  const input,
                 pm_error("Couldn't allocate %u bytes for a line of text.",
                          (unsigned)outputSize);
         }
-        if (input[inCursor] == '\n' && input[inCursor+1] == '\0') {
+        if (currentChar == L'\n' && input[inCursor+1] == L'\0') {
             /* This is a terminating newline.  We don't do those. */
-        } else if (input[inCursor] == '\t') { 
+        } else if (currentChar == L'\t') {
             /* Expand this tab into the right number of spaces. */
             unsigned int const nextTabStop =
                 (outCursor + tabSize) / tabSize * tabSize;
 
-        if (fontP->glyph[(unsigned char)' '] == NULL)
-            pm_error("space character not defined in font");
+            if (fontP->glyph[L' '] == NULL)
+                pm_error("space character not defined in font");
 
             while (outCursor < nextTabStop)
-                output[outCursor++] = ' ';
-        } else if (!fontP->glyph[(unsigned char)input[inCursor]]) {
+                output[outCursor++] = L' ';
+        } else if (currentChar > fontP->maxglyph ||
+                   !fontP->glyph[currentChar]) {
+        if (currentChar > PM_FONT2_MAXGLYPH)
+            pm_message("code point %X is beyond what this program "
+                       "can handle.  Max=%X",
+                       (unsigned int)currentChar, PM_FONT2_MAXGLYPH);
             /* Turn this unknown char into a single space. */
-            if (fontP->glyph[(unsigned char) ' '] == NULL)
+            if (fontP->glyph[L' '] == NULL)
                 pm_error("space character not defined in font");
             else if (fixMode == QUIT)
-                pm_error("character %d not defined in font",
-                         (unsigned int )input[inCursor] );
+                pm_error("code point %X not defined in font",
+                         (unsigned int) currentChar );
             else {
                 if (fixMode == WARN)
-                    pm_message("converting character %d to space",
-                               (unsigned int) input[inCursor] );
+                    pm_message("converting code point %X to space",
+                               (unsigned int) currentChar );
                 output[outCursor++] = ' ';
             }
         } else
@@ -338,7 +425,7 @@ fixControlChars(const char *  const input,
 
         assert(outCursor <= outputSize);
     }
-    output[outCursor++] = '\0';
+    output[outCursor++] = L'\0';
 
     assert(outCursor <= outputSize);
 
@@ -348,12 +435,12 @@ fixControlChars(const char *  const input,
 
 
 static void
-clearBackground(bit ** const bits, 
-                int    const cols, 
+clearBackground(bit ** const bits,
+                int    const cols,
                 int    const rows) {
 
     unsigned int row;
-    
+
     for (row = 0; row < rows; ++row) {
         unsigned int colChar;
         for (colChar = 0; colChar < pbm_packed_bytes(cols); ++colChar)
@@ -365,8 +452,8 @@ clearBackground(bit ** const bits,
 
 static void
 getEdges(double               const currentPosition,
-         char                 const currentChar,
-         const struct glyph * const glyphP, 
+         PM_WCHAR             const currentChar,
+         const struct glyph * const glyphP,
          int                  const currLeftEdge,
          double               const currRightEdge,
          int                * const newLeftEdgeP,
@@ -374,9 +461,9 @@ getEdges(double               const currentPosition,
 
     int leftEdge;
     double rightEdge;
-  
+
     if (glyphP == NULL)
-        pm_error("Unrenderable char: %c", currentChar);
+        pm_error("Unrenderable char: %04X", (unsigned int) currentChar);
     else {
         leftEdge  =  (int) MIN(currentPosition + glyphP->x, currLeftEdge);
         rightEdge =  MAX(currentPosition + glyphP->x + glyphP->width,
@@ -390,7 +477,7 @@ getEdges(double               const currentPosition,
 
 static void
 advancePosition(double               const currentPosition,
-                char                 const currentChar,
+                PM_WCHAR             const currentChar,
                 const struct glyph * const glyphP,
                 float                const space,
                 double               const accumulatedSpace,
@@ -408,14 +495,14 @@ advancePosition(double               const currentPosition,
     int const fullPixels = (int) (accumulatedSpace + space);
         /* round toward 0 */
     int const advance    = (int) glyphP->xadd + fullPixels;
-    
+
     if (advance < 0) {
         if (space < 0)
-            pm_error("Negative -space value too large"); 
+            pm_error("Negative -space value too large");
         else
             pm_error("Abnormal horizontal advance value %d "
-                     "for char '%c' 0x%x.",
-                     glyphP->xadd, currentChar, (unsigned int) currentChar);
+                     "for code point 0x%lx.",
+                     glyphP->xadd, (unsigned long int) currentChar);
     }
     else if (currentPosition + advance > INT_MAX)
         pm_error("Image is too wide");
@@ -429,11 +516,11 @@ advancePosition(double               const currentPosition,
 
 
 static void
-getLineDimensions(char                const line[],
-                  const struct font * const fontP, 
-                  float               const intercharacterSpace,
-                  double *            const rightEdgeP,
-                  int    *            const leftEdgeP) {
+getLineDimensions(PM_WCHAR             const line[],
+                  const struct font2 * const fontP,
+                  float                const intercharacterSpace,
+                  double *             const rightEdgeP,
+                  int    *             const leftEdgeP) {
 /*----------------------------------------------------------------------------
    Determine the left edge and right edge in pixels of the line of text
    line[] in the font *fontP, and return them as *leftEdgeP and *rightEdgeP.
@@ -451,7 +538,7 @@ getLineDimensions(char                const line[],
    This often happens with fixed-width font in which the white areas on the
    sides are not trimmed.
 -----------------------------------------------------------------------------*/
-    int cursor;  /* cursor into the line of text */
+    unsigned int cursor;  /* cursor into the line of text */
     double currentPosition;
         /* sum of xadd values and intercharacter space so far in line.  this
            is never negative.
@@ -471,11 +558,11 @@ getLineDimensions(char                const line[],
 
     leftEdge  = INT_MAX;  /* initial value */
     rightEdge = INT_MIN;  /* initial value */
-    
-    for (cursor = 0; line[cursor] != '\0'; ++cursor) {
-        char const currentChar = line[cursor];
-        struct glyph * const glyphP = 
-            fontP->glyph[(unsigned char) currentChar];
+
+    for (cursor = 0; line[cursor] != L'\0'; ++cursor) {
+        PM_WCHAR          const currentChar = line[cursor];
+        unsigned long int const glyphIndex  = (unsigned long int) currentChar;
+        struct glyph *    const glyphP      = fontP->glyph[glyphIndex];
 
         getEdges(currentPosition, currentChar, glyphP, leftEdge, rightEdge,
                  &leftEdge, &rightEdge);
@@ -485,7 +572,7 @@ getLineDimensions(char                const line[],
                         &currentPosition, &accumulatedIcs);
     }
 
-    if (line[0] == '\0') {     /* Empty line */
+    if (line[0] == L'\0') {     /* Empty line */
         leftEdge  = 0;
         rightEdge = 0.0;
     }
@@ -497,12 +584,12 @@ getLineDimensions(char                const line[],
 
 
 static void
-getCharsWithinWidth(char                const line[],
-                    const struct font * const fontP, 
-                    float               const intercharacter_space,
-                    unsigned int        const targetWidth,
-                    unsigned int      * const charCountP,
-                    int               * const leftEdgeP) {
+getCharsWithinWidth(PM_WCHAR             const line[],
+                    const struct font2 * const fontP,
+                    float                const intercharacter_space,
+                    unsigned int         const targetWidth,
+                    unsigned int       * const charCountP,
+                    int                * const leftEdgeP) {
 /*----------------------------------------------------------------------------
    Determine how many characters of text line[] fit into an image of target
    width targetWidth.
@@ -510,7 +597,7 @@ getCharsWithinWidth(char                const line[],
    *leftEdgeP will be negative if the leftmost character in the line has a
    "backup" distance and zero if it does not.
 -----------------------------------------------------------------------------*/
-    if (line[0] == '\0') {
+    if (line[0] == L'\0') {
         /* Empty line */
         *leftEdgeP = 0;
         *charCountP = 0;
@@ -527,13 +614,14 @@ getCharsWithinWidth(char                const line[],
 
         leftEdge     = INT_MAX;  /* initial value */
         rightEdge    = INT_MIN;  /* initial value */
-    
+
         for (cursor = 0, currentWidth = 0;
-             currentWidth <= targetWidth && line[cursor] != '\0';
+             currentWidth <= targetWidth && line[cursor] != L'\0';
              ++cursor) {
-            char const currentChar = line[cursor];
-            struct glyph * const glyphP = 
-                fontP->glyph[(unsigned char) currentChar];
+            PM_WCHAR const currentChar = line[cursor];
+            unsigned long int const glyphIndex =
+              (unsigned long int) currentChar;
+            struct glyph * const glyphP = fontP->glyph[glyphIndex];
 
             getEdges(currentPosition, currentChar, glyphP, leftEdge, rightEdge,
                      &leftEdge, &rightEdge);
@@ -562,7 +650,7 @@ getCharsWithinWidth(char                const line[],
 
 static void
 insertCharacter(const struct glyph * const glyphP,
-                int                  const toprow, 
+                int                  const toprow,
                 int                  const leftcol,
                 unsigned int         const cols,
                 unsigned int         const rows,
@@ -593,21 +681,21 @@ insertCharacter(const struct glyph * const glyphP,
             }
         }
     }
-}    
+}
 
 
 
 static void
-insertCharacters(bit **        const bits, 
-                 struct Text   const lp,
-                 struct font * const fontP, 
-                 int           const topmargin, 
-                 int           const leftmargin,
-                 float         const intercharacter_space,
-                 unsigned int  const cols,
-                 unsigned int  const rows,
-                 int           const lspace,
-                 bool          const fixedAdvance) {
+insertCharacters(bit **         const bits,
+                 struct Text    const lp,
+                 struct font2 * const fontP,
+                 int            const topmargin,
+                 int            const leftmargin,
+                 float          const intercharacter_space,
+                 unsigned int   const cols,
+                 unsigned int   const rows,
+                 int            const lspace,
+                 bool           const fixedAdvance) {
 /*----------------------------------------------------------------------------
    Render the text 'lp' into the image 'bits' using font *fontP and
    putting 'intercharacter_space' pixels between characters and
@@ -623,23 +711,24 @@ insertCharacters(bit **        const bits,
             /* accumulated intercharacter space so far in the line we
                are building.  Because the intercharacter space might
                not be an integer, we accumulate it here and realize
-               full pixels whenever we have more than one pixel. 
+               full pixels whenever we have more than one pixel.
             */
 
         row = topmargin + line * (fontP->maxheight + lspace);
         leftcol = leftmargin;
         accumulatedIcs = 0.0;  /* initial value */
-    
+
         for (cursor = 0; lp.textArray[line][cursor] != '\0'; ++cursor) {
-            char const currentChar = lp.textArray[line][cursor];
-            unsigned int const glyphIndex = (unsigned char) currentChar;
+            PM_WCHAR const currentChar = lp.textArray[line][cursor];
+            unsigned long int const glyphIndex =
+                (unsigned long int)currentChar;
             struct glyph * const glyphP = fontP->glyph[glyphIndex];
             int const toprow =
                 row + fontP->maxheight + fontP->y - glyphP->height - glyphP->y;
                 /* row number in image of top row in glyph */
 
             assert(glyphP != NULL);
-            
+
             insertCharacter(glyphP, toprow, leftcol, cols, rows, bits);
 
         if (fixedAdvance)
@@ -656,12 +745,12 @@ insertCharacters(bit **        const bits,
 
 static void
 flowText(struct Text    const inputText,
-         int            const targetWidth, 
-         struct font  * const fontP, 
+         int            const targetWidth,
+         struct font2 * const fontP,
          float          const intercharacterSpace,
          struct Text  * const outputTextP,
          unsigned int * const maxleftbP) {
-    
+
     unsigned int outputLineNum;
     unsigned int incursor;   /* cursor into the line we are reading */
     unsigned int const maxLineCount = 50; /* max output lines */
@@ -672,11 +761,11 @@ flowText(struct Text    const inputText,
     allocTextArray(outputTextP, maxLineCount, 0);
 
     for (incursor = 0, outputLineNum = 0;
-         inputText.textArray[0][incursor] != '\0'; ) {
+         inputText.textArray[0][incursor] != L'\0'; ) {
 
         unsigned int outcursor;
 
-        getCharsWithinWidth(&inputText.textArray[0][incursor], fontP, 
+        getCharsWithinWidth(&inputText.textArray[0][incursor], fontP,
                             intercharacterSpace, targetWidth,
                             &charCount, &leftEdge);
 
@@ -688,11 +777,11 @@ flowText(struct Text    const inputText,
 
         ++outputTextP->allocatedLineCount;
 
-        for (outcursor = 0; outcursor < charCount; ++outcursor, ++incursor) 
-            outputTextP->textArray[outputLineNum][outcursor] = 
+        for (outcursor = 0; outcursor < charCount; ++outcursor, ++incursor)
+            outputTextP->textArray[outputLineNum][outcursor] =
                 inputText.textArray[0][incursor];
 
-        outputTextP->textArray[outputLineNum][charCount] = '\0'; 
+        outputTextP->textArray[outputLineNum][charCount] = L'\0';
         ++outputLineNum;
         if (outputLineNum >= maxLineCount)
             pm_error("-width too small.  too many output lines");
@@ -706,9 +795,9 @@ flowText(struct Text    const inputText,
 
 
 static void
-truncateText(struct Text    const inputText, 
-             unsigned int   const targetWidth, 
-             struct font  * const fontP, 
+truncateText(struct Text    const inputText,
+             unsigned int   const targetWidth,
+             struct font2 * const fontP,
              float          const intercharacterSpace,
              unsigned int * const maxleftbP) {
 
@@ -717,18 +806,18 @@ truncateText(struct Text    const inputText,
     int leftExtreme = 0;
 
     for (lineNum = 0; lineNum < inputText.lineCount; ++lineNum) {
-        char * const currentLine = inputText.textArray[lineNum];
+        PM_WCHAR * const currentLine = inputText.textArray[lineNum];
 
         unsigned int charCount;
 
-        getCharsWithinWidth(currentLine, fontP, 
+        getCharsWithinWidth(currentLine, fontP,
                             intercharacterSpace, targetWidth,
                             &charCount, &leftEdge);
 
-        if (currentLine[charCount] != '\0') {
+        if (currentLine[charCount] != L'\0') {
             pm_message("truncating line %u from %u to %u characters",
-                       lineNum, (unsigned) strlen(currentLine), charCount); 
-            currentLine[charCount] = '\0';
+                       lineNum, (unsigned) wcslen(currentLine), charCount);
+            currentLine[charCount] = L'\0';
         }
 
         leftExtreme = MIN(leftEdge, leftExtreme);
@@ -739,11 +828,77 @@ truncateText(struct Text    const inputText,
 
 
 static void
-getText(char          const cmdlineText[], 
-        struct font * const fontP,
-        struct Text * const inputTextP,
-        enum FixMode  const fixMode) {
+fgetNarrowWideString(PM_WCHAR *    const widestring,
+                     unsigned int  const size,
+                     FILE *        const ifP,
+                     const char ** const errorP) {
+/*----------------------------------------------------------------------------
+  Return the next line from file *ifP, up to 'size' characters, as
+  *widestring.
+
+  Return error if we can't read the file, or file is at EOF.
+-----------------------------------------------------------------------------*/
+    int wideCode;
+        /* Width orientation for *ifP: positive means wide, negative means
+           byte, zero means undecided.
+        */
+
+    assert(widestring);
+    assert(size > 0);
+
+    wideCode = fwide(ifP, 0);
+    if (wideCode > 0) {
+        /* *ifP is wide-oriented */
+        wchar_t * rc;
+        rc = fgetws(widestring, size, ifP);
+        if (rc == NULL)
+            pm_asprintf(errorP,
+                        "fgetws() of max %u bytes failed or end of stream",
+                        size);
+        else
+            *errorP = NULL;
+    } else {
+        char * bufNarrow;
+        char * rc;
+
+        MALLOCARRAY_NOFAIL(bufNarrow, MAXLINECHARS+1);
+
+        rc = fgets(bufNarrow, size, ifP);
+        if (rc == NULL)
+            pm_asprintf(errorP, "EOF or error reading file");
+        else {
+            size_t cnt;
+
+            for (cnt = 0; cnt < size && bufNarrow[cnt] != '\0'; ++cnt)
+                widestring[cnt] = (PM_WCHAR)(unsigned char) bufNarrow[cnt];
 
+            widestring[cnt] = L'\0';
+            *errorP = NULL;
+        }
+        free(bufNarrow);
+    }
+}
+
+
+
+
+static void
+getText(PM_WCHAR       const cmdlineText[],
+        struct font2 * const fontP,
+        struct Text  * const inputTextP,
+        enum FixMode   const fixMode) {
+/*----------------------------------------------------------------------------
+   Get as *inputTextP the text to format, given that the text on the
+   command line (one word per command line argument, separated by spaces),
+   is 'cmdlineText'.
+
+   If 'cmdlineText' is null, that means to get the text from Standard Input.
+   Otherwise, 'cmdlineText' is that text.
+
+   But we return text as only renderable characters - characters in *fontP -
+   with control characters interpreted or otherwise fixed, according to
+   'fixMode'.
+-----------------------------------------------------------------------------*/
     struct Text inputText;
 
     if (cmdlineText) {
@@ -751,7 +906,7 @@ getText(char          const cmdlineText[],
         inputText.allocatedLineCount = 1;
         inputText.lineCount = 1;
         fixControlChars(cmdlineText, fontP,
-                        (const char**)&inputText.textArray[0], fixMode);
+                        (const PM_WCHAR**)&inputText.textArray[0], fixMode);
     } else {
         /* Read text from stdin. */
 
@@ -759,9 +914,10 @@ getText(char          const cmdlineText[],
             /* Maximum number of lines for which we presently have space in
                the text array
             */
-        char * buf;
-        char ** textArray;
+        PM_WCHAR *   buf;
+        PM_WCHAR **  textArray;
         unsigned int lineCount;
+        bool         eof;
 
         MALLOCARRAY(buf, MAXLINECHARS+1);
 
@@ -771,27 +927,38 @@ getText(char          const cmdlineText[],
 
         maxlines = 50;  /* initial value */
         MALLOCARRAY(textArray, maxlines);
-        
+
         if (!textArray)
             pm_error("Unable to allocate memory for a buffer for up to %u "
                      "lines of text", maxlines);
 
-        lineCount = 0;  /* initial value */
-        while (fgets(buf, MAXLINECHARS, stdin) != NULL) {
-            if (strlen(buf) + 1 >= MAXLINECHARS)
-                pm_error("A line of input text is longer than %u characters."
-                         "Cannot process", (unsigned int) MAXLINECHARS-1);
-            if (lineCount >= maxlines) {
-                maxlines *= 2;
-                REALLOCARRAY(textArray, maxlines);
-                if (textArray == NULL)
+        for (lineCount = 0, eof = false; !eof; ) {
+            const char * error;
+            fgetNarrowWideString(buf, MAXLINECHARS, stdin, &error);
+            if (error) {
+                /* We're lazy, so we treat any error as EOF */
+                pm_strfree(error);
+                eof = true;
+            } else {
+                if (wcslen(buf) + 1 >= MAXLINECHARS)
+                    pm_error(
+                        "Line %u (starting at zero) of input text "
+                        "is longer than %u characters."
+                        "Cannot process",
+                        lineCount, (unsigned int) MAXLINECHARS-1);
+                if (lineCount >= maxlines) {
+                    maxlines *= 2;
+                    REALLOCARRAY(textArray, maxlines);
+                    if (textArray == NULL)
+                        pm_error("out of memory");
+                }
+                fixControlChars(buf, fontP,
+                                (const PM_WCHAR **)&textArray[lineCount],
+                                fixMode);
+                if (textArray[lineCount] == NULL)
                     pm_error("out of memory");
+                ++lineCount;
             }
-            fixControlChars(buf, fontP,
-                            (const char **)&textArray[lineCount], fixMode);
-            if (textArray[lineCount] == NULL)
-                pm_error("out of memory");
-            ++lineCount;
         }
         inputText.textArray = textArray;
         inputText.lineCount = lineCount;
@@ -805,10 +972,10 @@ getText(char          const cmdlineText[],
 static void
 computeMargins(struct CmdlineInfo const cmdline,
                struct Text        const inputText,
-               struct font *      const fontP,
+               struct font2 *     const fontP,
                unsigned int *     const vmarginP,
                unsigned int *     const hmarginP) {
-       
+
     if (cmdline.nomargins) {
         *vmarginP = 0;
         *hmarginP = 0;
@@ -823,12 +990,12 @@ computeMargins(struct CmdlineInfo const cmdline,
     }
 }
 
-    
+
 
 static void
 formatText(struct CmdlineInfo const cmdline,
            struct Text        const inputText,
-           struct font *      const fontP,
+           struct font2 *     const fontP,
            unsigned int       const hmargin,
            struct Text *      const formattedTextP,
            unsigned int *     const maxleftb0P) {
@@ -858,20 +1025,20 @@ formatText(struct CmdlineInfo const cmdline,
 
 
 static void
-computeImageHeight(struct Text         const formattedText, 
-                   const struct font * const fontP,
-                   int                 const interlineSpace,
-                   unsigned int        const vmargin,
-                   unsigned int      * const rowsP) {
+computeImageHeight(struct Text          const formattedText,
+                   const struct font2 * const fontP,
+                   int                  const interlineSpace,
+                   unsigned int         const vmargin,
+                   unsigned int       * const rowsP) {
 
     if (interlineSpace < 0 && fontP->maxheight < -interlineSpace)
         pm_error("-lspace value (%d) negative and exceeds font height.",
-                 interlineSpace);     
+                 interlineSpace);
     else {
-        double const rowsD = 2 * (double) vmargin + 
-            (double) formattedText.lineCount * fontP->maxheight + 
+        double const rowsD = 2 * (double) vmargin +
+            (double) formattedText.lineCount * fontP->maxheight +
             (double) (formattedText.lineCount-1) * interlineSpace;
-        
+
         if (rowsD > INT_MAX-10)
             pm_error("Image height too large.");
         else
@@ -882,21 +1049,21 @@ computeImageHeight(struct Text         const formattedText,
 
 
 static void
-computeImageWidth(struct Text         const formattedText, 
-                  const struct font * const fontP,
-                  float               const intercharacterSpace,
-                  unsigned int        const hmargin,
-                  unsigned int *      const colsP,
-                  unsigned int *      const maxleftbP) {
+computeImageWidth(struct Text          const formattedText,
+                  const struct font2 * const fontP,
+                  float                const intercharacterSpace,
+                  unsigned int         const hmargin,
+                  unsigned int *       const colsP,
+                  unsigned int *       const maxleftbP) {
 
     if (intercharacterSpace < 0 && fontP->maxwidth < -intercharacterSpace)
         pm_error("negative -space value %.2f exceeds font width",
-                 intercharacterSpace);     
+                 intercharacterSpace);
     else {
         /* Find the widest line, and the one that backs up the most past
            the nominal start of the line.
         */
-    
+
         unsigned int lineNum;
         double rightExtreme;
         int leftExtreme;
@@ -908,7 +1075,7 @@ computeImageWidth(struct Text         const formattedText,
         for (lineNum = 0; lineNum < formattedText.lineCount;  ++lineNum) {
             double rightEdge;
             int leftEdge;
-            
+
             getLineDimensions(formattedText.textArray[lineNum], fontP,
                               intercharacterSpace,
                               &rightEdge, &leftEdge);
@@ -923,7 +1090,7 @@ computeImageWidth(struct Text         const formattedText,
             pm_error("Image width too large.");
         else
             *colsP = (unsigned int) colsD;
-    
+
         *maxleftbP = (unsigned int) - leftExtreme;
     }
 }
@@ -931,17 +1098,17 @@ computeImageWidth(struct Text         const formattedText,
 
 
 static void
-renderText(unsigned int  const cols,
-           unsigned int  const rows,
-           struct font * const fontP,
-           unsigned int  const hmargin,
-           unsigned int  const vmargin,
-           struct Text   const formattedText,
-           unsigned int  const maxleftb,
-           float         const space,
-           int           const lspace,
-           bool          const fixedAdvance,
-           FILE *        const ofP) {
+renderText(unsigned int   const cols,
+           unsigned int   const rows,
+           struct font2 * const fontP,
+           unsigned int   const hmargin,
+           unsigned int   const vmargin,
+           struct Text    const formattedText,
+           unsigned int   const maxleftb,
+           float          const space,
+           int            const lspace,
+           bool           const fixedAdvance,
+           FILE *         const ofP) {
 
     bit ** const bits = pbm_allocarray(pbm_packed_bytes(cols), rows);
 
@@ -949,7 +1116,7 @@ renderText(unsigned int  const cols,
     clearBackground(bits, cols, rows);
 
     /* Put the text in  */
-    insertCharacters(bits, formattedText, fontP, vmargin, hmargin + maxleftb, 
+    insertCharacters(bits, formattedText, fontP, vmargin, hmargin + maxleftb,
                      space, cols, rows, lspace, fixedAdvance);
 
     {
@@ -966,47 +1133,48 @@ renderText(unsigned int  const cols,
 
 
 
-static char const * sheetTextArray[] = { 
-"M \",/^_[`jpqy| M",
-"                ",
-"/  !\"#$%&'()*+ /",
-"< ,-./01234567 <",
-"> 89:;<=>?@ABC >",
-"@ DEFGHIJKLMNO @",
-"_ PQRSTUVWXYZ[ _",
-"{ \\]^_`abcdefg {",
-"} hijklmnopqrs }",
-"~ tuvwxyz{|}~  ~",
-"                ",
-"M \",/^_[`jpqy| M" };
+static PM_WCHAR const * sheetTextArray[] = {
+L"M \",/^_[`jpqy| M",
+L"                ",
+L"/  !\"#$%&'()*+ /",
+L"< ,-./01234567 <",
+L"> 89:;<=>?@ABC >",
+L"@ DEFGHIJKLMNO @",
+L"_ PQRSTUVWXYZ[ _",
+L"{ \\]^_`abcdefg {",
+L"} hijklmnopqrs }",
+L"~ tuvwxyz{|}~  ~",
+L"                ",
+L"M \",/^_[`jpqy| M" };
 
 
 
 static void
-validateText(const char ** const textArray,
-             struct font * const fontP) {
+validateText(const PM_WCHAR ** const textArray,
+             struct font2    * const fontP) {
 /*----------------------------------------------------------------------------
    Abort the program if there are characters in 'textArray' which cannot be
    rendered in font *fontP.
 -----------------------------------------------------------------------------*/
-    const char * output;
+    const PM_WCHAR * output;
     unsigned int textRow;
 
     for (textRow = 0; textRow < 12; ++textRow)
         fixControlChars(textArray[textRow], fontP, &output, QUIT);
 
-    pm_strfree(output);
+    free((PM_WCHAR *)output);
 }
 
 
 
 static void
-renderSheet(struct font * const fontP,
-            FILE *        const ofP) {
+renderSheet(struct font2 * const fontP,
+            FILE *         const ofP) {
 
     int const cols  = fontP->maxwidth  * 16;
     int const rows  = fontP->maxheight * 12;
-    struct Text const sheetText = { (char ** const) sheetTextArray, 12, 12};
+    struct Text const sheetText =
+        { (PM_WCHAR ** const) sheetTextArray, 12, 12};
 
     validateText(sheetTextArray, fontP);
 
@@ -1020,15 +1188,49 @@ static void
 dryrunOutput(unsigned int const cols,
              unsigned int const rows,
              FILE *       const ofP) {
- 
-    fprintf(ofP, "%u %u\n", cols, rows); 
+
+    fprintf(ofP, "%u %u\n", cols, rows);
+}
+
+
+
+static void
+textDumpOutput(struct Text   const lp,
+               FILE *        const ofP) {
+/*----------------------------------------------------------------------------
+   Output the text 'lp' as characters.  (Do not render.)
+
+   Note that the output stream is wide-oriented; it cannot be mixed with
+   narrow-oriented output.  The libnetpbm library functions are
+   narrow-oriented.  Thus, when this output is specified, it must not be mixed
+   with any output from the library; it should be the sole output.
+-----------------------------------------------------------------------------*/
+    int rc;
+
+    rc = fwide(ofP, 1);
+    if (rc != 1) {
+        /* This occurs when narrow-oriented output to ofP happens before we
+           get here.
+        */
+        pm_error("Failed to set output stream to wide "
+                 "(fwide() returned %d.  Maybe the output file "
+                 "was written in narrow mode before this program was invoked?",
+                 rc);
+    } else {
+        unsigned int line;  /* Line number in input text */
+
+        for (line = 0; line < lp.lineCount; ++line) {
+            fputws(lp.textArray[line], ofP);
+            fputwc(L'\n', ofP);
+        }
+    }
 }
 
 
 
 static void
 pbmtext(struct CmdlineInfo const cmdline,
-        struct font *      const fontP,
+        struct font2 *     const fontP,
         FILE *             const ofP) {
 
     unsigned int rows, cols;
@@ -1051,7 +1253,7 @@ pbmtext(struct CmdlineInfo const cmdline,
 
     if (formattedText.lineCount == 0)
         pm_error("No input text");
-    
+
     computeImageHeight(formattedText, fontP, cmdline.lspace, vmargin, &rows);
 
     computeImageWidth(formattedText, fontP, cmdline.space,
@@ -1078,7 +1280,9 @@ pbmtext(struct CmdlineInfo const cmdline,
 
     if (cmdline.dryrun)
         dryrunOutput(cols, rows, ofP);
-    else 
+    else if (cmdline.textdump)
+        textDumpOutput(formattedText, ofP);
+    else
         renderText(cols, rows, fontP, hmargin, vmargin, formattedText,
                    maxleftb, cmdline.space, cmdline.lspace, FALSE, ofP);
 
@@ -1091,12 +1295,26 @@ int
 main(int argc, const char *argv[]) {
 
     struct CmdlineInfo cmdline;
-    struct font * fontP;
+    struct font2 * fontP;
 
     pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
-    
+
+    if (cmdline.wchar) {
+        char * newLocale;
+        newLocale = setlocale(LC_ALL, "");
+        if (!newLocale)
+            pm_error("Failed to set locale (LC_ALL) from environemt");
+
+        /* Orient standard input stream to wide */
+        fwide(stdin,  1);
+    } else
+        fwide(stdin, -1);
+
+    if (cmdline.verbose)
+        pm_message("LC_CTYPE is set to '%s'", setlocale(LC_CTYPE, NULL) );
+
     computeFont(cmdline, &fontP);
 
     if (cmdline.dumpsheet)
@@ -1104,6 +1322,10 @@ main(int argc, const char *argv[]) {
     else
         pbmtext(cmdline, fontP, stdout);
 
+    /* Note that *fontP is unfreeable.  See pbm_loadbdffont2,
+       pbm_expandbdffont
+    */
+
     pm_close(stdout);
 
     return 0;
diff --git a/generator/ppmpat.c b/generator/ppmpat.c
index 6d9cb1ca..908c200f 100644
--- a/generator/ppmpat.c
+++ b/generator/ppmpat.c
@@ -75,7 +75,7 @@ validateColorCount(Pattern      const basePattern,
                    unsigned int const colorCount) {
 
     if (colorCount == 0)
-        pm_error("-color: no colors specified"); 
+        pm_error("-color: no colors specified");
 
     switch (basePattern) {
     case PAT_GINGHAM2:
@@ -84,7 +84,7 @@ validateColorCount(Pattern      const basePattern,
         if (colorCount != 2)
             pm_error("Wrong number of colors: %u. "
                      "2 colors are required for the specified pattern.",
-                     colorCount); 
+                     colorCount);
         break;
     case PAT_GINGHAM3:
     case PAT_MADRAS:
@@ -93,14 +93,14 @@ validateColorCount(Pattern      const basePattern,
         if (colorCount != 3)
             pm_error("Wrong number of colors: %u. "
                      "3 colors are required for the specified pattern.",
-                     colorCount); 
+                     colorCount);
         break;
     case PAT_POLES:
         if (colorCount < 2)
-            pm_error("Too few colors: %u. " 
+            pm_error("Too few colors: %u. "
                      "At least 2 colors are required "
                      "for the specified pattern.",
-                     colorCount); 
+                     colorCount);
         break;
     case PAT_SQUIG:
     case PAT_CAMO:
@@ -109,7 +109,7 @@ validateColorCount(Pattern      const basePattern,
             pm_error("Wrong number of colors: %u. "
                      "At least 3 colors are required "
                      "for the specified pattern.",
-                     colorCount); 
+                     colorCount);
         break;
 
     case PAT_SPIRO2:
@@ -132,7 +132,7 @@ parseColorOpt(const char ** const colorText,
     "-color=rgb:ff/ff/ff,rgb:00/00/00,rgb:80/80/ff"
 
     Input:
-      Color name/value string-list: colorText[] 
+      Color name/value string-list: colorText[]
 
     Output values:
       Color array: colorTableP->color[]
@@ -154,7 +154,7 @@ parseColorOpt(const char ** const colorText,
     for (i = 0; i < colorCount; ++i)
         inColor[i] = ppm_parsecolor(colorText[i], PPM_MAXMAXVAL);
 
-    validateColorCount(basePattern, colorCount); 
+    validateColorCount(basePattern, colorCount);
 
     colorTableP->count = colorCount;
     colorTableP->index = 0;  /* initial value */
@@ -309,7 +309,7 @@ parseCommandLine(int argc, const char ** argv,
 
 static void
 freeCmdline(struct CmdlineInfo const cmdline) {
-    
+
     if (cmdline.colorSpec)
         free(cmdline.colorTable.color);
 }
@@ -330,7 +330,7 @@ validateComputableDimensions(unsigned int const cols,
       PPMD functions use signed integers for pixel positions
       (because they allow you to specify points off the canvas).
     */
-      
+
     if (cols > INT_MAX/4 || rows > INT_MAX/4 || rows > INT_MAX/cols)
         pm_error("Width and/or height are way too large: %u x %u",
                  cols, rows);
@@ -348,7 +348,7 @@ randomColor(pixval const maxval) {
                rand() % (maxval + 1),
                rand() % (maxval + 1)
         );
-    
+
     return p;
 }
 
@@ -403,12 +403,12 @@ averageTwoColors(pixel const p1,
 static ppmd_drawproc average_drawproc;
 
 static void
-average_drawproc(pixel **     const pixels, 
-                 int          const cols, 
-                 int          const rows, 
-                 pixval       const maxval, 
-                 int          const col, 
-                 int          const row, 
+average_drawproc(pixel **     const pixels,
+                 int          const cols,
+                 int          const rows,
+                 pixval       const maxval,
+                 int          const col,
+                 int          const row,
                  const void * const clientdata) {
 
     if (col >= 0 && col < cols && row >= 0 && row < rows)
@@ -466,7 +466,7 @@ randomAnticamoColor(pixval const maxval) {
     case 3:
         PPM_ASSIGN(p, rand() % v2, rand() % v1 + v3, rand() % v2);
         break;
-        
+
     case 4:
     case 5:
         PPM_ASSIGN(p, rand() % v2, rand() % v2, rand() % v1 + v3);
@@ -490,7 +490,7 @@ randomAnticamoColor(pixval const maxval) {
         PPM_ASSIGN(p, rand() % v1 + v3, rand() % v1 + v3, rand() % v2);
         break;
     }
-    
+
     return p;
 }
 
@@ -519,7 +519,7 @@ randomCamoColor(pixval const maxval) {
         /* dark green */
         PPM_ASSIGN(p, rand() % v2, rand() % v2 + 3 * v1, rand() % v2);
         break;
-    
+
     case 6:
     case 7:
         /* brown */
@@ -577,10 +577,11 @@ camoFill(pixel **         const pixels,
          struct fillobj * const fh,
          ColorTable     * const colorTableP,
          bool             const antiflag) {
-         
+
     pixel color;
 
     if (colorTableP->count > 0) {
+        assert(colorTableP->index < colorTableP->count);
         color = colorTableP->color[colorTableP->index];
         nextColorBg(colorTableP);
     } else if (antiflag)
@@ -589,7 +590,7 @@ camoFill(pixel **         const pixels,
         color = randomCamoColor(maxval);
 
     ppmd_fill(pixels, cols, rows, maxval, fh, PPMD_NULLDRAWPROC, &color);
-}        
+}
 
 
 
@@ -620,9 +621,9 @@ computeXsYs(int *        const xs,
     double const b = rnduni() * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) +
         MIN_ELLIPSE_FACTOR;
     double const theta = rnduni() * 2.0 * M_PI;
-    
+
     unsigned int p;
-        
+
     for (p = 0; p < pointCt; ++p) {
         double const c = rnduni() * (MAX_POINT_FACTOR - MIN_POINT_FACTOR) +
             MIN_POINT_FACTOR;
@@ -673,9 +674,9 @@ camo(pixel **     const pixels,
         ppmd_polyspline(
             pixels, cols, rows, maxval, x0, y0, pointCt, xs, ys, x0, y0,
             ppmd_fill_drawproc, fh);
-        
+
         camoFill(pixels, cols, rows, maxval, fh, colorTableP, antiflag);
-        
+
         ppmd_fill_destroy(fh);
     }
 }
@@ -840,7 +841,7 @@ madras(pixel **     const pixels,
         pixels, cols, rows, maxval, 9 * cols2 + 3 * cols3 + cols6a + cols6b, 0,
         cols2, rows, PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
-        pixels, cols, rows, maxval, 10 * cols2 + 3 * cols3 + cols6a + cols6b, 
+        pixels, cols, rows, maxval, 10 * cols2 + 3 * cols3 + cols6a + cols6b,
         0, cols3, rows, PPMD_NULLDRAWPROC, &fore1color);
 
     /* Woof. */
@@ -890,7 +891,7 @@ madras(pixel **     const pixels,
         pixels, cols, rows, maxval, 0, 9 * rows2 + 3 * rows3 + rows6a + rows6b,
         cols, rows2, average_drawproc, &backcolor);
     ppmd_filledrectangle(
-        pixels, cols, rows, maxval, 0, 
+        pixels, cols, rows, maxval, 0,
         10 * rows2 + 3 * rows3 + rows6a + rows6b,
         cols, rows3, average_drawproc, &fore2color);
 }
@@ -1059,7 +1060,7 @@ placeAndColorPolesRandomly(int *        const xs,
                            unsigned int const cols,
                            unsigned int const rows,
                            pixval       const maxval,
-                           ColorTable * const colorTableP, 
+                           ColorTable * const colorTableP,
                            unsigned int const poleCt) {
 
     unsigned int i;
@@ -1085,8 +1086,8 @@ assignInterpolatedColor(pixel * const resultP,
                         double  const dist1,
                         pixel   const color2,
                         double  const dist2) {
-    
-    if (dist1 == 0) 
+
+    if (dist1 == 0)
         /* pixel is a pole */
         *resultP = color1;
     else {
@@ -1095,7 +1096,7 @@ assignInterpolatedColor(pixel * const resultP,
         pixval const r = (PPM_GETR(color1)*dist2 + PPM_GETR(color2)*dist1)/sum;
         pixval const g = (PPM_GETG(color1)*dist2 + PPM_GETG(color2)*dist1)/sum;
         pixval const b = (PPM_GETB(color1)*dist2 + PPM_GETB(color2)*dist1)/sum;
-        
+
         PPM_ASSIGN(*resultP, r, g, b);
     }
 }
@@ -1110,7 +1111,7 @@ poles(pixel **     const pixels,
       pixval       const maxval) {
 
     unsigned int const poleCt = MAX(2, MIN(MAXPOLES, cols * rows / 30000));
-    
+
     int xs[MAXPOLES], ys[MAXPOLES];
     pixel colors[MAXPOLES];
     unsigned int row;
@@ -1176,7 +1177,7 @@ validateSquigAspect(unsigned int const cols,
     if (cols / rows >= 25 || rows / cols >= 25)
         pm_error("Image too narrow.  Aspect ratio: %u/%u=%f "
                  "is outside accepted range: 0.04 - 25.0",
-                 cols, rows, (float)cols/rows ); 
+                 cols, rows, (float)cols/rows );
 
 }
 
@@ -1194,10 +1195,10 @@ vectorSum(ppmd_point const a,
 static ppmd_drawprocp sqMeasureCircleDrawproc;
 
 static void
-sqMeasureCircleDrawproc(pixel**      const pixels, 
-                        unsigned int const cols, 
-                        unsigned int const rows, 
-                        pixval       const maxval, 
+sqMeasureCircleDrawproc(pixel**      const pixels,
+                        unsigned int const cols,
+                        unsigned int const rows,
+                        pixval       const maxval,
                         ppmd_point   const p,
                         const void * const clientdata) {
 
@@ -1213,10 +1214,10 @@ sqMeasureCircleDrawproc(pixel**      const pixels,
 static ppmd_drawprocp sqRainbowCircleDrawproc;
 
 static void
-sqRainbowCircleDrawproc(pixel **     const pixels, 
-                        unsigned int const cols, 
-                        unsigned int const rows, 
-                        pixval       const maxval, 
+sqRainbowCircleDrawproc(pixel **     const pixels,
+                        unsigned int const cols,
+                        unsigned int const rows,
+                        pixval       const maxval,
                         ppmd_point   const p,
                         const void * const clientdata) {
 
@@ -1254,7 +1255,7 @@ chooseSqPoleColors(ColorTable * const colorTableP,
         *color3P = randomBrightColor(maxval);
     }
 }
-    
+
 
 
 static void
@@ -1321,7 +1322,7 @@ clearBackgroundSquig(pixel **     const pixels,
     if (colorTableP->count > 0) {
         color = colorTableP->color[0];
         colorTableP->index = 1;
-    } else 
+    } else
         PPM_ASSIGN(color, 0, 0, 0);
 
     ppmd_filledrectangle(
@@ -1340,7 +1341,7 @@ chooseWrapAroundPoint(unsigned int const cols,
                       ppmd_point * const p1P,
                       ppmd_point * const p2P,
                       ppmd_point * const p3P) {
-                      
+
     switch (rand() % 4) {
     case 0:
         p1P->x = rand() % cols;
@@ -1428,7 +1429,7 @@ squig(pixel **     const pixels,
     int i;
 
     validateSquigAspect(cols, rows);
-    
+
     clearBackgroundSquig(pixels, cols, rows, colorTableP, maxval);
 
     /* Draw the squigs. */
@@ -1491,11 +1492,11 @@ main(int argc, const char ** argv) {
     pixel ** pixels;
 
     pm_proginit(&argc, argv);
-    
+
     parseCommandLine(argc, argv, &cmdline);
 
     validateComputableDimensions(cmdline.width, cmdline.height);
-    
+
     srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
 
     pixels = ppm_allocarray(cmdline.width, cmdline.height);
diff --git a/generator/ppmrainbow b/generator/ppmrainbow
index a4f90a09..e8a329ff 100755
--- a/generator/ppmrainbow
+++ b/generator/ppmrainbow
@@ -49,7 +49,7 @@ sub doVersionHack($) {
 sub fatal($) {
     my ($msg) = @_;
 
-    print(STDERR "$msg\n");
+    print(STDERR "ppmrainbow: $msg\n");
     exit(1);
 }
 
@@ -115,7 +115,7 @@ while (@colorlist >= 2) {
     my $rc = system("$verboseCommand pgmramp -lr $w $Thgt | " .
                     "pgmtoppm \"$colorlist[0]-$colorlist[1]\" >$outfile");
     if ($rc != 0) {
-        fatal("pgmramp|pgmtoppm failed.");
+        fatal("pgmramp|pgmtoppm pipe failed.");
     }
     $widthRemaining -= $w;
     $n++;