about summary refs log tree commit diff
path: root/converter/pbm/pbmtog3.c
diff options
context:
space:
mode:
Diffstat (limited to 'converter/pbm/pbmtog3.c')
-rw-r--r--converter/pbm/pbmtog3.c729
1 files changed, 448 insertions, 281 deletions
diff --git a/converter/pbm/pbmtog3.c b/converter/pbm/pbmtog3.c
index f0fd1252..f034b466 100644
--- a/converter/pbm/pbmtog3.c
+++ b/converter/pbm/pbmtog3.c
@@ -1,84 +1,54 @@
 /* pbmtog3.c - read a PBM image and produce a Group 3 FAX file
-**
-** Copyright (C) 1989 by Paul Haeberli <paul@manray.sgi.com>.
-**
-** 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.
-*/
 
-/*
-   For specifications for Group 3 (G3) fax MH coding see ITU-T T.4
-   This program generates only MH.  It is coded with future expansion for
-   MR and MMR in mind.
+   For specifications for Group 3 (G3) fax MH coding see ITU-T T.4:
+   Standardization of Group 3 facsimile terminals for document transmission
+   https://www.itu.int/rec/T-REC-T.4/en
+
+   This program generates only MH.
 */
 
+
 #include <assert.h>
 
 #include "pm_c_util.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "bitreverse.h"
-#include "wordaccess.h"
-#include "wordintclz.h"
+#include "intcode.h"
 #include "g3.h"
+#include "g3ttable.h"
+#include "g3prefab.h"
 #include "pbm.h"
 
-#define TC_MC 64
-
-static bool const pbmtorl = 
-#ifdef PBMTORL
-    TRUE;
-#else
-    FALSE;
-#endif
-
+enum G3eol {EOL, ALIGN8, ALIGN16, NO_EOL, NO_RTC, NO_EOLRTC};
 
-struct bitString {
-    /* A string of bits, up to as many fit in a word. */
-    unsigned int bitCount;
-        /* The length of the bit string */
-    wordint intBuffer;
-        /* The bits are in the 'bitCount' least significant bit positions 
-           of this number.  The rest of the bits of this number are always 
-           zero.
-        */
+struct OutStream;
 
-    /* Example:  The bit string 010100, on a machine with a 32 bit word,
-       would be represented by bitCount = 6, intBuffer = 20
-       (N.B. 20 = 00000000 00000000 00000000 00010100 in binary)
-    */
+struct OutStream {
+    FILE * fp;
+    struct BitString buffer;
+    bool reverseBits;    /* Reverse bit order */
+    enum G3eol eolAlign; /* Omit EOL and/or RTC; align EOL to 8/16 bits */
+    void * data;         /* Reserved for future expansion */
 };
 
 
 
-struct outStream {
-    struct bitString buffer;
-
-    bool reverseBits;
-};
-
-/* This is a global variable for speed. */
-static struct outStream out;
-
-
 struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
     const char * inputFileName;
     unsigned int reversebits;
-    unsigned int nofixedwidth;
+    enum G3eol   align;
+    unsigned int desiredWidth;
     unsigned int verbose;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** const argv,
+parseCommandLine(int argc, const char ** const argv,
                  struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
@@ -87,6 +57,8 @@ parseCommandLine(int argc, char ** const argv,
     optEntry * option_def;
         /* Instructions to OptParseOptions2 on how to parse our options.  */
     optStruct3 opt;
+    unsigned int nofixedwidth;
+    unsigned int align8, align16;
 
     unsigned int option_def_index;
 
@@ -95,9 +67,13 @@ parseCommandLine(int argc, char ** const argv,
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0,   "reversebits",      OPT_FLAG,  NULL, &cmdlineP->reversebits,
             0);
-    OPTENT3(0,   "nofixedwidth",     OPT_FLAG,  NULL, &cmdlineP->nofixedwidth,
+    OPTENT3(0,   "nofixedwidth",     OPT_FLAG,  NULL, &nofixedwidth,
             0);
-    OPTENT3(0,   "verbose",          OPT_FLAG,  NULL, &cmdlineP->verbose, 
+    OPTENT3(0,   "align8",           OPT_FLAG,  NULL, &align8,
+            0);
+    OPTENT3(0,   "align16",          OPT_FLAG,  NULL, &align16,
+            0);
+    OPTENT3(0,   "verbose",          OPT_FLAG,  NULL, &cmdlineP->verbose,
             0);
 
     /* TODO
@@ -105,15 +81,30 @@ parseCommandLine(int argc, char ** const argv,
     */
 
     opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = TRUE;  /* We may have parms that are negative numbers */
+    opt.short_allowed = false;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = true;  /* We may have parms that are negative numbers */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     free(option_def);
 
-    if (argc-1 == 0) 
+    if (align8) {
+        if (align16)
+            pm_error("You can't specify both -align8 and -align16");
+        else
+            cmdlineP->align = ALIGN8;
+    } else if (align16)
+        cmdlineP->align = ALIGN16;
+    else
+        cmdlineP->align = EOL;
+
+    if (nofixedwidth)
+        cmdlineP->desiredWidth = 0;
+    else
+        cmdlineP->desiredWidth = 1728;
+
+    if (argc-1 == 0)
         cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
@@ -125,31 +116,79 @@ parseCommandLine(int argc, char ** const argv,
 
 
 static void
-reversebuffer(unsigned char * const p, 
+reversebuffer(unsigned char * const p,
               unsigned int    const n) {
 
     unsigned int i;
+
     for (i = 0; i < n; ++i)
         p[i] = bitreverse[p[i]];
 }
 
 
 
-static struct bitString
-makeBs(wordint      const bits, 
-       unsigned int const bitCount) {
+static void
+flushBuffer(struct OutStream * const outP) {
+/*----------------------------------------------------------------------------
+  Flush the contents of the bit buffer
+-----------------------------------------------------------------------------*/
+    struct BitString const buffer = outP->buffer;
 
-    struct bitString retval;
-    retval.intBuffer = bits;
-    retval.bitCount  = bitCount;
+    assert (buffer.bitCount <= 32);
 
-    return retval;
+    if (buffer.bitCount > 0) {
+        unsigned int const fullBuffer = sizeof(buffer.intBuffer) * 8;
+        unsigned int const bytesToWrite = (buffer.bitCount+7)/8;
+        bigend32 outbytes;
+        size_t rc;
+
+        outbytes = pm_bigendFromUint32(
+                   buffer.intBuffer << (fullBuffer - buffer.bitCount));
+        if (outP->reverseBits)
+    	reversebuffer((unsigned char *)&outbytes, bytesToWrite);
+        rc = fwrite((unsigned char *)&outbytes, 1, bytesToWrite, outP->fp);
+        if (rc != bytesToWrite)
+            pm_error("Output error");
+    }
 }
 
 
+#if 1==0
+static void
+putbitsDump(struct OutStream * const outP,
+            struct BitString   const newBits) {
+/*----------------------------------------------------------------------------
+  Print the content of the bit put request, in human readable text form
+  For debugging.  Also good for studying how the coding scheme works.
+
+  By default the compiler ignores this function.
+  To turn on, remove the "#if" - "#endif" lines enclosing the function and
+  edit the output function name in putbits().
+-----------------------------------------------------------------------------*/
+    unsigned int const bitCount = newBits.bitCount;
+
+    unsigned int i;
+    char charBuff[128];
+
+    assert (bitCount >= 0 && bitCount < 32);
+    assert (sizeof(newBits.intBuffer) + 2 < 128);
+
+    for (i = 0; i < bitCount; ++i) {
+        unsigned int const n = bitCount - i - 1;
+        charBuff[i] = ((newBits.intBuffer >> n) & 0x01) + '0';
+    }
+
+    charBuff[bitCount]   = '\n';
+    charBuff[bitCount+1] = '\0';
+    fwrite(charBuff, 1, bitCount+1, outP->fp);
+}
+#endif
+
+
 
-static __inline__ void
-putbits(struct bitString const newBits) {
+static void
+putbitsBinary(struct OutStream * const outP,
+              struct BitString   const newBits) {
 /*----------------------------------------------------------------------------
    Push the bits 'newBits' onto the right end of output buffer
    out.buffer (moving the bits already in the buffer left).
@@ -158,334 +197,462 @@ putbits(struct bitString const newBits) {
 
    'newBits' must be shorter than a whole word.
 
-   N.B. the definition of struct bitString requires upper bits to be zero.
+   N.B. the definition of struct BitString requires upper bits to be zero.
 -----------------------------------------------------------------------------*/
-    unsigned int const spaceLeft = 
-        sizeof(out.buffer.intBuffer)*8 - out.buffer.bitCount;
+    unsigned int const fullBuffer = sizeof(outP->buffer.intBuffer) * 8;
+    unsigned int const spaceLeft = fullBuffer - outP->buffer.bitCount;
         /* Number of bits of unused space (at the high end) in buffer */
 
-    assert(newBits.bitCount < sizeof(out.buffer.intBuffer) * 8);
+    assert(newBits.bitCount < fullBuffer);
     assert(newBits.intBuffer >> newBits.bitCount == 0);
 
     if (spaceLeft > newBits.bitCount) {
         /* New bits fit with bits to spare */
-        out.buffer.intBuffer = 
-            out.buffer.intBuffer << newBits.bitCount | newBits.intBuffer;
-        out.buffer.bitCount += newBits.bitCount;
-    } else { 
+        outP->buffer.intBuffer =
+            outP->buffer.intBuffer << newBits.bitCount | newBits.intBuffer;
+        outP->buffer.bitCount += newBits.bitCount;
+    } else {
         /* New bits fill buffer.  We'll have to flush the buffer to stdout
            and put the rest of the bits in the new buffer.
         */
         unsigned int const nextBufBitCount = newBits.bitCount - spaceLeft;
+        unsigned int const bitMask = ((1<<nextBufBitCount) - 1);
 
-        wordintBytes outbytes;
-        size_t rc;
+        outP->buffer.intBuffer = ( (outP->buffer.intBuffer << spaceLeft)
+                                 | (newBits.intBuffer >> nextBufBitCount));
+        outP->buffer.bitCount  = fullBuffer;
+        flushBuffer(outP);
 
-        wordintToBytes(&outbytes, 
-                       (out.buffer.intBuffer << spaceLeft) 
-                       | (newBits.intBuffer >> nextBufBitCount));
-        if (out.reverseBits)
-            reversebuffer(outbytes, sizeof(outbytes));
+        outP->buffer.intBuffer = newBits.intBuffer & bitMask;
+        outP->buffer.bitCount = nextBufBitCount;
+    }
+}
 
-        rc = fwrite(outbytes, 1, sizeof(outbytes), stdout);
-        if (rc != sizeof(outbytes))
-            pm_error("Output error.  Unable to fwrite() to stdout");
 
-        out.buffer.intBuffer = newBits.intBuffer & ((1<<nextBufBitCount) - 1); 
-        out.buffer.bitCount = nextBufBitCount;
-    }
+
+static void
+initOutStream(struct OutStream * const outP,
+              bool               const reverseBits,
+              enum G3eol         const eolAlign) {
+
+    outP->buffer.intBuffer = 0;
+    outP->buffer.bitCount  = 0;
+    outP->reverseBits      = reverseBits;
+    outP->fp               = stdout;
+    outP->eolAlign         = eolAlign;
+}
+
+
+
+static struct BitString
+tableEntryToBitString(G3TableEntry const tableEntry) {
+
+    struct BitString retval;
+
+    retval.intBuffer = tableEntry.code;
+    retval.bitCount  = tableEntry.length;
+
+    return retval;
 }
 
 
 
-static void 
-initOutStream(bool const reverseBits) {
-    out.buffer.intBuffer = 0;
-    out.buffer.bitCount  = 0;
-    out.reverseBits = reverseBits;
+static void
+putbits(struct OutStream * const outP,
+        struct BitString   const newBits) {
+
+    putbitsBinary(outP, newBits);
+    /* Change to putbitsDump() for human readable output */
 }
 
 
 
-static __inline__ void
-putcode(unsigned int const clr, 
-        unsigned int const ix) {
+static void
+putcodeShort(struct OutStream * const outP,
+             bit                const color,
+             unsigned int       const runLength) {
 
-    /* Note that this requires ttable to be aligned white entry, black
-       entry, white, black, etc.  
+    /* Note that this requires g3ttable_table to be aligned white entry, black
+       entry, white, black, etc.
     */
-    putbits(makeBs(ttable[ix * 2 + clr].code, ttable[ix * 2 + clr].length));
+    unsigned int index = runLength * 2 + color;
+    putbits(outP, tableEntryToBitString(g3ttable_table[index]));
 }
 
 
 
-static __inline__ void
-putcode2(int const clr,
-         int const ix) {
+static void
+putcodeLong(struct OutStream * const outP,
+            bit                const color,
+            unsigned int       const runLength) {
 /*----------------------------------------------------------------------------
    Output Make-up code and Terminating code at once.
 
-   For run lengths above TC_MC threshold (usually 64).
+   For run lengths which require both: length 64 and above
 
    The codes are combined here to avoid calculations in putbits()
-   wordint is usually wide enough, with 32 or 64 bits.
-   Provisions are made for 16 bit wordint (for debugging).
 
    Terminating code is max 12 bits, Make-up code is max 13 bits.
-   (See ttable, mtable entries in pbmtog3.h)
+   (See g3ttable_table, g3ttable_mtable entries in g3ttable.h)
 
    Also reduces object code size when putcode is compiled inline.
 -----------------------------------------------------------------------------*/
-    unsigned int const loIndex = ix % 64 * 2 + clr;
-    unsigned int const hiIndex = ix / 64 * 2 + clr;
-
-    if (sizeof(wordint) * 8 > 24) {
-        unsigned int const l1 = ttable[loIndex].length;
-
-        putbits(
-            makeBs(mtable[hiIndex].code << l1 | ttable[loIndex].code,
-                   mtable[hiIndex].length + l1)
-            );
-    } else { /* typically 16 bit wordint used for debugging */
-        putbits(makeBs(mtable[hiIndex].code, mtable[hiIndex].length));
-        putbits(makeBs(ttable[loIndex].code, ttable[loIndex].length));
-    }
+    unsigned int const loIndex = runLength % 64 * 2 + color;
+    unsigned int const hiIndex = runLength / 64 * 2 + color;
+    unsigned int const loLength = g3ttable_table[loIndex].length;
+    unsigned int const hiLength = g3ttable_mtable[hiIndex].length;
+
+    struct BitString combinedCode;
+
+    combinedCode.intBuffer = g3ttable_mtable[hiIndex].code << loLength |
+                             g3ttable_table[loIndex].code;
+    combinedCode.bitCount  = hiLength + loLength;
+
+    putbits(outP, combinedCode);
 }
 
 
 
-static __inline__ void
-putspan_normal(bit          const color, 
-               unsigned int const len) {
+static  void
+putcodeExtra(struct OutStream * const outP,
+             int                const color,
+             int                const runLength) {
+/*----------------------------------------------------------------------------
+   Lengths over 2560.  This is rare.
+   According to the standard, the mark-up code for 2560 can be issued as
+   many times as necessary without terminal codes.
+   --------------------------------------------------------------------------*/
+    G3TableEntry const markUp2560 = g3ttable_mtable[2560/64*2];
+                              /* Same code for black and white */
 
-    if (len < TC_MC)
-        putcode(color, len);
-    else if (len < 2624)
-        putcode2(color, len);
-    else {  /* len >= 2624 : rare */
-        unsigned int remainingLen;
+    unsigned int remainingLen;
 
-        for (remainingLen = len;
-             remainingLen >= 2624;
-             remainingLen -= 2623) {
+    for (remainingLen = runLength; remainingLen > 2560; remainingLen -= 2560)
+      putbits(outP, tableEntryToBitString(markUp2560));
+    /* after the above: 0 < remainingLen <= 2560 */
 
-            putcode2(color, 2560+63);
-            putcode(!color, 0);
-        }
-        if (remainingLen < TC_MC)
-            putcode(color, remainingLen);
-        else  /* TC_MC <= len < 2624 */
-            putcode2(color, remainingLen);
-    }
+    if (remainingLen >= 64)
+        putcodeLong(outP, color, remainingLen);
+    else
+        putcodeShort(outP, color, remainingLen);
 }
 
 
 
-static __inline__ void
-putspan(bit          const color, 
-        unsigned int const len) {
-/*----------------------------------------------------------------------------
-   Put a span of 'len' pixels of color 'color' in the output.
------------------------------------------------------------------------------*/
-    if (pbmtorl) {
-        if (len > 0) 
-            printf("%c %d\n", color == PBM_WHITE ? 'W' : 'B', len);
-    } else 
-        putspan_normal(color, len);
+static void
+putspan(struct OutStream * const outP,
+        bit                const color,
+        unsigned int       const runLength) {
+
+    if (runLength < 64)
+        putcodeShort(outP, color, runLength);
+    else if (runLength < 2560)
+        putcodeLong (outP, color, runLength);
+    else  /* runLength > 2560 : rare */
+        putcodeExtra(outP, color, runLength);
 }
 
 
 
 static void
-puteol(void) {
+puteol(struct OutStream * const outP) {
+
+    switch (outP->eolAlign) {
+    case EOL: {
+        struct BitString const eol = { 1, 12 };
+
+        putbits(outP, eol);
+    } break;
+    case ALIGN8:  case ALIGN16: {
+        unsigned int const bitCount = outP->buffer.bitCount;
+        unsigned int const fillbits =
+            (outP->eolAlign == ALIGN8) ? (44 - bitCount) % 8
+            : (52 - bitCount) % 16;
+
+        struct BitString eol;
+
+        eol.bitCount = 12 + fillbits;     eol.intBuffer = 1;
+        putbits(outP, eol);
+    } break;
+    case NO_EOL: case NO_EOLRTC:
+        break;
+    case NO_RTC:
+        pm_error("INTERNAL ERROR: no-RTC EOL treatment not implemented");
+        break;
+    }
+}
 
-    if (pbmtorl)
-        puts("EOL");
-    else {
-        struct bitString const eol = {12, 1};
 
-        putbits(eol);
+
+static void
+putrtc(struct OutStream * const outP) {
+
+    switch (outP->eolAlign) {
+    case NO_RTC: case NO_EOLRTC:
+        break;
+    default:
+        puteol(outP);    puteol(outP);    puteol(outP);
+        puteol(outP);    puteol(outP);    puteol(outP);
     }
 }
 
 
 
-/*
-  PBM raw bitrow to inflection point array
+static void
+readOffSideMargins(unsigned char * const bitrow,
+                   unsigned int    const colChars,
+                   unsigned int  * const firstNonWhiteCharP,
+                   unsigned int  * const lastNonWhiteCharP,
+                   bool          * const blankRowP) {
+/*----------------------------------------------------------------------------
+  Determine the white margins on the left and right side of a row.
+  This is an enhancement: convertRowToG3() works without this.
+-----------------------------------------------------------------------------*/
+    unsigned int charCnt;
+    unsigned int firstChar;
+    unsigned int lastChar;
+    bool         blankRow;
 
-  Write inflection (=color change) points into array milepost[].  
-  It is easy to calculate run length from this.
+    assert(colChars > 0);
 
-  In milepost, a white-to-black (black-to-white) inflection point
-  always has an even (odd) index.  A line starting with black is
-  indicated by bitrow[0] == 0.
+    for (charCnt = 0; charCnt < colChars && bitrow[charCnt] == 0; ++charCnt);
 
-  WWWWWWWBBWWWWWWW ... = 7,2,7, ...
-  BBBBBWBBBBBWBBBB ... = 0,5,1,5,1,4, ...
+    if (charCnt >= colChars) {
+        /* Reached end of bitrow with no black pixels encountered */
+        firstChar = lastChar = 0;
+        blankRow  = true;
+    } else {
+        /* There is at least one black pixel in the row */
+        firstChar = charCnt;
+        blankRow = false;
 
-  Return the number of milepost elements written.
-  Note that max number of entries into milepost = cols+1 .
+        charCnt = colChars - 1;
 
-  The inflection points are calculated like this:
+        while (bitrow[charCnt--] == 0x00)
+            ;
+        lastChar = charCnt + 1;
+    }
 
-   r1: 00000000000111111110011111000000
-   r2: c0000000000011111111001111100000 0->carry
-  xor: ?0000000000100000001010000100000
+    *firstNonWhiteCharP = firstChar;
+    *lastNonWhiteCharP  = lastChar;
+    *blankRowP          = blankRow;
+}
 
-  The 1 bits in the xor above are the inflection points.
-*/
 
-static __inline__ void
-convertRowToRunLengths(unsigned char * const bitrow, 
-                       int             const cols, 
-                       unsigned int *  const milepost,
-                       unsigned int *  const lengthP) {
-
-    unsigned int   const bitsPerWord  = sizeof(wordint) * 8;
-    wordint      * const bitrowByWord = (wordint *) bitrow;
-    int            const wordCount    = (cols + bitsPerWord - 1)/bitsPerWord; 
-        /* Number of full and partial words in the row */
-
-
-    if (cols % bitsPerWord != 0) {
-        /* Clean final word in row.  For loop simplicity */
-        wordint r1;
-        r1 = bytesToWordint((unsigned char *)&bitrowByWord[wordCount - 1]);
-        r1 >>= bitsPerWord - cols % bitsPerWord;
-        r1 <<= bitsPerWord - cols % bitsPerWord;
-        wordintToBytes((wordintBytes *)&bitrowByWord[wordCount - 1], r1);
+
+static void
+setBlockBitsInFinalChar(unsigned char * const finalByteP,
+                        unsigned int    const cols) {
+/*----------------------------------------------------------------------------
+   If the char in the row is fractional, set it up so that the don't care
+   bits are the opposite color of the last valid pixel.
+----------------------------------------------------------------------------*/
+    unsigned char const finalByte  = *finalByteP;
+    unsigned int const silentBitCnt = 8 - cols % 8;
+    bit const rowEndColor = (finalByte >> silentBitCnt) & 0x01;
+
+    if (rowEndColor == PBM_WHITE) {
+        unsigned char const blackMask = (0x01 << silentBitCnt) - 1;
+
+        *finalByteP = finalByte | blackMask;
     }
-    {
-
-        wordint carry;
-        wordint r1, r2;
-        unsigned int n;
-        int i,c,k;
-
-        for (i = carry = n = 0; i < wordCount; ++i) {
-            r1 = r2 = bytesToWordint((unsigned char *)&bitrowByWord[i]);
-            r2 = r1 ^ (carry << (bitsPerWord-1) | r2 >> 1);
-            carry = r1 & 0x1;  k = 0;
-            while (r2 != 0) {
-                /* wordintClz(r2) reports most significant "1" bit of r2
-                   counting from MSB = position 0.
-                */
-                c = wordintClz(r2);
-                milepost[n++] = i * bitsPerWord + k + c;
-                r2 <<= c++; r2 <<= 1;  k += c; 
-            } 
+    /* No adjustment required if the row ends with a black pixel.
+       pbm_cleanrowend_packed() takes care of this.
+    */
+}
+
+
+
+static void
+trimFinalChar(struct OutStream * const outP,
+              bit                const color,
+              int                const carryLength,
+              int                const existingCols,
+              int                const desiredWidth) {
+/*---------------------------------------------------------------------------
+   If the carry value from the last char in the row represents a valid
+   sequence, output it.
+
+   (1) If input row width is not a whole multiple of 8 and -nofixwidth
+       was specified, the final carry value represents inactive bits
+       at the row end.  Emit no code.  See setBlockBitsInFinalChar().
+
+   (2) If there is white margin on the right side, the final carry value
+       is valid.  We add to it the margin width.  Right-side margin may
+       be added in main() to a narrow input image, detected in the
+       input row by readOffSideMargins() or both.  The same treatment
+       applies regardless of the nature of the right-side margin.
+----------------------------------------------------------------------------*/
+    if (existingCols == desiredWidth) {
+        if (existingCols % 8 == 0)
+            putspan(outP, color, carryLength);  /* Code up to byte boundary */
+        /* Emit nothing if existingCols is not a whole multiple of 8 */
+    } else if (existingCols < desiredWidth) {
+        if (color == 0) {       /* Last bit sequence in final char: white */
+            unsigned int const totalLength =
+                carryLength + (desiredWidth - existingCols);
+            putspan(outP, 0, totalLength);
+        } else {                 /* Black */
+            unsigned int const padLength = desiredWidth - existingCols;
+            putspan(outP, 1, carryLength);
+            putspan(outP, 0, padLength);
         }
-        if (n == 0 || milepost[n - 1] != cols) 
-            milepost[n++] = cols;
-        *lengthP = n;
     }
 }
 
 
 
 static void
-padToDesiredWidth(unsigned int * const milepost,
-                  unsigned int * const nRunP,
-                  int            const existingCols,
-                  int            const desiredCols) {
-
-    if (existingCols < desiredCols) {
-        /* adjustment for narrow input in fixed width mode
-           nRun % 2 == 1 (0) means last (=rightmost) pixel is white (black)
-           if white, extend the last span to outwidth
-           if black, fill with a white span len (outwidth - readcols)
-        */
-        if (*nRunP % 2 == 0)
-            ++*nRunP;
-        milepost[*nRunP - 1] = desiredCols;
+convertRowToG3(struct OutStream * const outP,
+               unsigned char    * const bitrow,
+               unsigned int       const existingCols,
+               unsigned int       const desiredWidth) {
+/*----------------------------------------------------------------------------
+   Table based Huffman coding
+
+   Normally Huffman code encoders count sequences of ones and zeros
+   and convert them to binary codes as they terminate.  This program
+   recognizes chains of pixels and converts them directly, reading
+   prefabricated code chains from an indexed table.
+
+   For example the 8-bit sequence 01100110 translates to
+   Huffman code: 000111 11 0111 11 000111.
+
+   In reality things are more complicated.  The leftmost 0 (MSB) may be
+   part of a longer sequence starting in the adjacent byte or perhaps
+   spanning several bytes.  Likewise for the rightmost 0.
+
+   So we first remove the sequence on the left side and compare its
+   color with the leftmost pixel of the adjacent byte and emit either
+   one code for a single sequence if they agree or two if they disagree.
+   Next the composite code for the central part (in the above example
+   110011 -> 11 0111 11) is emitted.  Finally we save the length and
+   color of the sequence on the right end as carry-over for the next
+   byte cycle.  Some 8-bit input sequences (00000000, 01111111,
+   00111111, etc.) have no central part: these are special cases.
+---------------------------------------------------------------------------*/
+    unsigned int const colChars = pbm_packed_bytes(existingCols);
+
+    unsigned int charCnt;
+    unsigned int firstActiveChar;
+    unsigned int lastActiveChar;
+    bool         blankRow;
+    bit          borderColor;
+
+    borderColor = PBM_WHITE; /* initial value */
+
+    if (existingCols == desiredWidth && (desiredWidth % 8) > 0)
+        setBlockBitsInFinalChar(&bitrow[colChars-1], desiredWidth);
+
+    readOffSideMargins(bitrow, colChars,
+                       &firstActiveChar, &lastActiveChar, &blankRow);
+
+    if (blankRow)
+        putspan(outP, PBM_WHITE, desiredWidth);
+    else {
+        unsigned int carryLength;
+
+        for (charCnt = firstActiveChar, carryLength = firstActiveChar * 8;
+             charCnt <=lastActiveChar;
+             ++charCnt) {
+
+            unsigned char const byte = bitrow[charCnt];
+            bit const rColor = !borderColor;
+
+            if (byte == borderColor * 0xFF) {
+                carryLength += 8;
+            } else if (byte == (unsigned char) ~(borderColor * 0xFF)) {
+                putspan(outP, borderColor, carryLength);
+                carryLength = 8;
+                borderColor = rColor;
+            } else {
+                struct PrefabCode const code = g3prefab_code[byte];
+                unsigned int const activeLength =
+                    8 - code.leadBits - code.trailBits;
+
+                if (borderColor == (byte >> 7)) {
+                    putspan(outP, borderColor, carryLength + code.leadBits);
+                } else {
+                    putspan(outP, borderColor, carryLength);
+                    putcodeShort(outP, rColor, code.leadBits);
+                }
+                if (activeLength > 0)
+                    putbits(outP, code.activeBits);
+
+                borderColor = byte & 0x01;
+                carryLength = code.trailBits;
+            }
+        }
+        trimFinalChar(outP, borderColor, carryLength,
+                      (lastActiveChar + 1) * 8, desiredWidth);
     }
+    puteol(outP);
 }
 
 
 
 int
-main(int    argc,
-     char * argv[]) {
+main(int          argc,
+     const char * argv[]) {
 
     struct CmdlineInfo cmdline;
     FILE * ifP;
     unsigned char * bitrow;
        /* This is the bits of the current row, as read from the input and
-           modified various ways at various points in the program.  It has
-           a word of zero padding on the high (right) end for the convenience
-           of code that accesses this buffer in word-size bites.
+          modified various ways at various points in the program.  It has
+          a word of zero padding on the high (right) end for the convenience
+          of code that accesses this buffer in word-size bites.
         */
 
     int rows;
     int cols;
-    int readcols;
-    int outwidth;
     int format;
-    int row;
-    unsigned int * milepost;
+    unsigned int existingCols;
+    unsigned int desiredWidth;
+    unsigned int row;
+    struct OutStream out;
 
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
     ifP = pm_openr(cmdline.inputFileName);
 
     pbm_readpbminit(ifP, &cols, &rows, &format);
-    if (cmdline.nofixedwidth)
-        readcols = outwidth = cols;
+
+    if (cmdline.desiredWidth == 0)
+        desiredWidth = existingCols = cols;
     else {
-        readcols = MIN(cols, 1728);
-        outwidth = 1728;
+        if (cmdline.desiredWidth < cols)
+            existingCols = desiredWidth = cmdline.desiredWidth;
+        else {
+            existingCols = pbm_packed_bytes(cols) * 8;
+            desiredWidth = cmdline.desiredWidth;
+        }
     }
 
-    MALLOCARRAY_NOFAIL(bitrow, pbm_packed_bytes(cols) + sizeof(wordint));
+    MALLOCARRAY(bitrow, pbm_packed_bytes(cols) + sizeof(uint32_t));
 
-    MALLOCARRAY_NOFAIL(milepost, readcols + 2);
+    if (!bitrow)
+        pm_error("Failed to allocate a row buffer for %u columns", cols);
 
-    initOutStream(cmdline.reversebits);
-    puteol();
+    initOutStream(&out, cmdline.reversebits, cmdline.align);
 
-    for (row = 0; row < rows; ++row) {
-        unsigned int nRun;  /* Number of runs in milepost[] */
-        unsigned int p;
-        unsigned int i;
+    puteol(&out);
 
+    for (row = 0; row < rows; ++row) {
         pbm_readpbmrow_packed(ifP, bitrow, cols, format);
-
-        convertRowToRunLengths(bitrow, readcols, milepost, &nRun);
-
-        padToDesiredWidth(milepost, &nRun, readcols, outwidth);
-
-        for (i = p = 0; i < nRun; p = milepost[i++])
-            putspan(i%2 == 0 ? PBM_WHITE : PBM_BLACK, milepost[i] - p);
-        /* TODO 2-dimensional coding MR, MMR */
-        puteol();
+        pbm_cleanrowend_packed(bitrow, cols);
+        convertRowToG3(&out, bitrow, existingCols, desiredWidth);
     }
 
-    free(milepost);
     pbm_freerow_packed(bitrow);
-
-    {
-        unsigned int i;  
-        for( i = 0; i < 6; ++i)
-            puteol();
-    }
-    if (out.buffer.bitCount > 0) {
-        /* flush final partial buffer */
-        unsigned int const bytesToWrite = (out.buffer.bitCount+7)/8;
-
-        unsigned char outbytes[sizeof(wordint)];
-        size_t rc;
-        wordintToBytes(&outbytes, 
-                       out.buffer.intBuffer << (sizeof(out.buffer.intBuffer)*8 
-                                                - out.buffer.bitCount));
-        if (out.reverseBits)
-            reversebuffer(outbytes, bytesToWrite);
-        rc = fwrite(outbytes, 1, bytesToWrite, stdout);
-        if (rc != bytesToWrite)
-            pm_error("Output error");
-    }
+    putrtc(&out);
+    flushBuffer(&out);
     pm_close(ifP);
 
     return 0;
 }
+
+
+