about summary refs log tree commit diff
path: root/converter/pbm
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2017-04-20 02:40:25 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2017-04-20 02:40:25 +0000
commita35cb3047edcfb17d6b45275a38a51cb38c81158 (patch)
treeb36b2e516a22844be449fe45b7ed06f0cc1e4292 /converter/pbm
parent7c605e807606003cfe752c195762467dfe6398bc (diff)
downloadnetpbm-mirror-a35cb3047edcfb17d6b45275a38a51cb38c81158.tar.gz
netpbm-mirror-a35cb3047edcfb17d6b45275a38a51cb38c81158.tar.xz
netpbm-mirror-a35cb3047edcfb17d6b45275a38a51cb38c81158.zip
New algorithm for pbmtog3, add -align8, -align16, don't use wordint anymore
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@2956 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/pbm')
-rw-r--r--converter/pbm/g3.h297
-rw-r--r--converter/pbm/g3topbm.c58
-rw-r--r--converter/pbm/pbmtog3.c722
3 files changed, 769 insertions, 308 deletions
diff --git a/converter/pbm/g3.h b/converter/pbm/g3.h
index e982f2da..42ba91c5 100644
--- a/converter/pbm/g3.h
+++ b/converter/pbm/g3.h
@@ -24,12 +24,30 @@
    There is also the newer G4.  
 */
 
-typedef struct g3TableEntry {
-    short int code;
-    short int length;
-} g3TableEntry;
+typedef struct G3TableEntry {
+    unsigned short int code;
+    unsigned short int length;
+} G3TableEntry;
 
-static struct g3TableEntry ttable[] = {
+
+struct BitString {
+    /* A string of bits, up to as many fit in 32 bits. */
+    uint32_t     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.
+        */
+    unsigned int bitCount;
+        /* The length of the bit string */
+
+    /* Example:  The bit string 010100 would be represented by
+       bitCount = 6, intBuffer = 20
+       (N.B. 20 = 00000000 00000000 00000000 00010100 in binary)
+    */
+};
+
+
+static struct G3TableEntry ttable[] = {
 /*    TERMWHITE           TERMBLACK   */
     { 0x35, 8 },    { 0x37, 10 },       /* white 0 , black 0 */
     { 0x07, 6 },    { 0x02,  3 },
@@ -143,4 +161,273 @@ static struct g3TableEntry ttable[] = {
 
 #define mtable ((ttable)+64*2)
 
+
+struct PrefabCode {
+    unsigned int leadBits;
+    unsigned int trailBits;
+    struct BitString activeBits;
+};
+
+
+struct PrefabCode const prefabCode[256] =
+{
+{ 0, 8, {   0x0000,   0}},   /* 000 */
+{ 7, 1, {   0x0000,   0}},   /* 001 */
+{ 6, 1, {   0x0002,   3}},   /* 002 */
+{ 6, 2, {   0x0000,   0}},   /* 003 */
+{ 5, 2, {   0x0002,   3}},   /* 004 */
+{ 5, 1, {   0x0087,   9}},   /* 005 */
+{ 5, 1, {   0x0003,   2}},   /* 006 */
+{ 5, 3, {   0x0000,   0}},   /* 007 */
+{ 4, 3, {   0x0002,   3}},   /* 008 */
+{ 4, 1, {   0x0027,   7}},   /* 009 */
+{ 4, 1, {   0x043a,  12}},   /* 010 */
+{ 4, 2, {   0x0087,   9}},   /* 011 */
+{ 4, 2, {   0x0003,   2}},   /* 012 */
+{ 4, 1, {   0x00c7,   8}},   /* 013 */
+{ 4, 1, {   0x0002,   2}},   /* 014 */
+{ 4, 4, {   0x0000,   0}},   /* 015 */
+{ 3, 4, {   0x0002,   3}},   /* 016 */
+{ 3, 1, {   0x0028,   7}},   /* 017 */
+{ 3, 1, {   0x013a,  10}},   /* 018 */
+{ 3, 2, {   0x0027,   7}},   /* 019 */
+{ 3, 2, {   0x043a,  12}},   /* 020 */
+{ 3, 1, {  0x10e87,  18}},   /* 021 */
+{ 3, 1, {   0x021f,  11}},   /* 022 */
+{ 3, 3, {   0x0087,   9}},   /* 023 */
+{ 3, 3, {   0x0003,   2}},   /* 024 */
+{ 3, 1, {   0x0037,   6}},   /* 025 */
+{ 3, 1, {   0x063a,  11}},   /* 026 */
+{ 3, 2, {   0x00c7,   8}},   /* 027 */
+{ 3, 2, {   0x0002,   2}},   /* 028 */
+{ 3, 1, {   0x0087,   8}},   /* 029 */
+{ 3, 1, {   0x0003,   3}},   /* 030 */
+{ 3, 5, {   0x0000,   0}},   /* 031 */
+{ 2, 5, {   0x0002,   3}},   /* 032 */
+{ 2, 1, {   0x002b,   7}},   /* 033 */
+{ 2, 1, {   0x0142,  10}},   /* 034 */
+{ 2, 2, {   0x0028,   7}},   /* 035 */
+{ 2, 2, {   0x013a,  10}},   /* 036 */
+{ 2, 1, {   0x4e87,  16}},   /* 037 */
+{ 2, 1, {   0x009f,   9}},   /* 038 */
+{ 2, 3, {   0x0027,   7}},   /* 039 */
+{ 2, 3, {   0x043a,  12}},   /* 040 */
+{ 2, 1, {   0x43a7,  16}},   /* 041 */
+{ 2, 1, {  0x8743a,  21}},   /* 042 */
+{ 2, 2, {  0x10e87,  18}},   /* 043 */
+{ 2, 2, {   0x021f,  11}},   /* 044 */
+{ 2, 1, {   0x87c7,  17}},   /* 045 */
+{ 2, 1, {   0x021e,  11}},   /* 046 */
+{ 2, 4, {   0x0087,   9}},   /* 047 */
+{ 2, 4, {   0x0003,   2}},   /* 048 */
+{ 2, 1, {   0x0038,   6}},   /* 049 */
+{ 2, 1, {   0x01ba,   9}},   /* 050 */
+{ 2, 2, {   0x0037,   6}},   /* 051 */
+{ 2, 2, {   0x063a,  11}},   /* 052 */
+{ 2, 1, {  0x18e87,  17}},   /* 053 */
+{ 2, 1, {   0x031f,  10}},   /* 054 */
+{ 2, 3, {   0x00c7,   8}},   /* 055 */
+{ 2, 3, {   0x0002,   2}},   /* 056 */
+{ 2, 1, {   0x0027,   6}},   /* 057 */
+{ 2, 1, {   0x043a,  11}},   /* 058 */
+{ 2, 2, {   0x0087,   8}},   /* 059 */
+{ 2, 2, {   0x0003,   3}},   /* 060 */
+{ 2, 1, {   0x00c7,   9}},   /* 061 */
+{ 2, 1, {   0x0003,   4}},   /* 062 */
+{ 2, 6, {   0x0000,   0}},   /* 063 */
+{ 1, 6, {   0x0002,   3}},   /* 064 */
+{ 1, 1, {   0x002c,   7}},   /* 065 */
+{ 1, 1, {   0x015a,  10}},   /* 066 */
+{ 1, 2, {   0x002b,   7}},   /* 067 */
+{ 1, 2, {   0x0142,  10}},   /* 068 */
+{ 1, 1, {   0x5087,  16}},   /* 069 */
+{ 1, 1, {   0x00a3,   9}},   /* 070 */
+{ 1, 3, {   0x0028,   7}},   /* 071 */
+{ 1, 3, {   0x013a,  10}},   /* 072 */
+{ 1, 1, {   0x13a7,  14}},   /* 073 */
+{ 1, 1, {  0x2743a,  19}},   /* 074 */
+{ 1, 2, {   0x4e87,  16}},   /* 075 */
+{ 1, 2, {   0x009f,   9}},   /* 076 */
+{ 1, 1, {   0x27c7,  15}},   /* 077 */
+{ 1, 1, {   0x009e,   9}},   /* 078 */
+{ 1, 4, {   0x0027,   7}},   /* 079 */
+{ 1, 4, {   0x043a,  12}},   /* 080 */
+{ 1, 1, {   0x43a8,  16}},   /* 081 */
+{ 1, 1, {  0x21d3a,  19}},   /* 082 */
+{ 1, 2, {   0x43a7,  16}},   /* 083 */
+{ 1, 2, {  0x8743a,  21}},   /* 084 */
+{ 1, 1, {0x21d0e87,  27}},   /* 085 */
+{ 1, 1, {  0x43a1f,  20}},   /* 086 */
+{ 1, 3, {  0x10e87,  18}},   /* 087 */
+{ 1, 3, {   0x021f,  11}},   /* 088 */
+{ 1, 1, {   0x21f7,  15}},   /* 089 */
+{ 1, 1, {  0x43e3a,  20}},   /* 090 */
+{ 1, 2, {   0x87c7,  17}},   /* 091 */
+{ 1, 2, {   0x021e,  11}},   /* 092 */
+{ 1, 1, {   0x8787,  17}},   /* 093 */
+{ 1, 1, {   0x043b,  12}},   /* 094 */
+{ 1, 5, {   0x0087,   9}},   /* 095 */
+{ 1, 5, {   0x0003,   2}},   /* 096 */
+{ 1, 1, {   0x003b,   6}},   /* 097 */
+{ 1, 1, {   0x01c2,   9}},   /* 098 */
+{ 1, 2, {   0x0038,   6}},   /* 099 */
+{ 1, 2, {   0x01ba,   9}},   /* 100 */
+{ 1, 1, {   0x6e87,  15}},   /* 101 */
+{ 1, 1, {   0x00df,   8}},   /* 102 */
+{ 1, 3, {   0x0037,   6}},   /* 103 */
+{ 1, 3, {   0x063a,  11}},   /* 104 */
+{ 1, 1, {   0x63a7,  15}},   /* 105 */
+{ 1, 1, {  0xc743a,  20}},   /* 106 */
+{ 1, 2, {  0x18e87,  17}},   /* 107 */
+{ 1, 2, {   0x031f,  10}},   /* 108 */
+{ 1, 1, {   0xc7c7,  16}},   /* 109 */
+{ 1, 1, {   0x031e,  10}},   /* 110 */
+{ 1, 4, {   0x00c7,   8}},   /* 111 */
+{ 1, 4, {   0x0002,   2}},   /* 112 */
+{ 1, 1, {   0x0028,   6}},   /* 113 */
+{ 1, 1, {   0x013a,   9}},   /* 114 */
+{ 1, 2, {   0x0027,   6}},   /* 115 */
+{ 1, 2, {   0x043a,  11}},   /* 116 */
+{ 1, 1, {  0x10e87,  17}},   /* 117 */
+{ 1, 1, {   0x021f,  10}},   /* 118 */
+{ 1, 3, {   0x0087,   8}},   /* 119 */
+{ 1, 3, {   0x0003,   3}},   /* 120 */
+{ 1, 1, {   0x0037,   7}},   /* 121 */
+{ 1, 1, {   0x063a,  12}},   /* 122 */
+{ 1, 2, {   0x00c7,   9}},   /* 123 */
+{ 1, 2, {   0x0003,   4}},   /* 124 */
+{ 1, 1, {   0x00c7,  10}},   /* 125 */
+{ 1, 1, {   0x0002,   4}},   /* 126 */
+{ 1, 7, {   0x0000,   0}},   /* 127 */
+{ 1, 7, {   0x0000,   0}},   /* 128 */
+{ 1, 1, {   0x000e,   4}},   /* 129 */
+{ 1, 1, {   0x0062,   7}},   /* 130 */
+{ 1, 2, {   0x000c,   4}},   /* 131 */
+{ 1, 2, {   0x005a,   7}},   /* 132 */
+{ 1, 1, {   0x1687,  13}},   /* 133 */
+{ 1, 1, {   0x002f,   6}},   /* 134 */
+{ 1, 3, {   0x000b,   4}},   /* 135 */
+{ 1, 3, {   0x0042,   7}},   /* 136 */
+{ 1, 1, {   0x0427,  11}},   /* 137 */
+{ 1, 1, {   0x843a,  16}},   /* 138 */
+{ 1, 2, {   0x1087,  13}},   /* 139 */
+{ 1, 2, {   0x0023,   6}},   /* 140 */
+{ 1, 1, {   0x08c7,  12}},   /* 141 */
+{ 1, 1, {   0x0022,   6}},   /* 142 */
+{ 1, 4, {   0x0008,   4}},   /* 143 */
+{ 1, 4, {   0x003a,   7}},   /* 144 */
+{ 1, 1, {   0x03a8,  11}},   /* 145 */
+{ 1, 1, {   0x1d3a,  14}},   /* 146 */
+{ 1, 2, {   0x03a7,  11}},   /* 147 */
+{ 1, 2, {   0x743a,  16}},   /* 148 */
+{ 1, 1, { 0x1d0e87,  22}},   /* 149 */
+{ 1, 1, {   0x3a1f,  15}},   /* 150 */
+{ 1, 3, {   0x0e87,  13}},   /* 151 */
+{ 1, 3, {   0x001f,   6}},   /* 152 */
+{ 1, 1, {   0x01f7,  10}},   /* 153 */
+{ 1, 1, {   0x3e3a,  15}},   /* 154 */
+{ 1, 2, {   0x07c7,  12}},   /* 155 */
+{ 1, 2, {   0x001e,   6}},   /* 156 */
+{ 1, 1, {   0x0787,  12}},   /* 157 */
+{ 1, 1, {   0x003b,   7}},   /* 158 */
+{ 1, 5, {   0x0007,   4}},   /* 159 */
+{ 1, 5, {   0x003a,   9}},   /* 160 */
+{ 1, 1, {   0x03ab,  13}},   /* 161 */
+{ 1, 1, {   0x1d42,  16}},   /* 162 */
+{ 1, 2, {   0x03a8,  13}},   /* 163 */
+{ 1, 2, {   0x1d3a,  16}},   /* 164 */
+{ 1, 1, {  0x74e87,  22}},   /* 165 */
+{ 1, 1, {   0x0e9f,  15}},   /* 166 */
+{ 1, 3, {   0x03a7,  13}},   /* 167 */
+{ 1, 3, {   0x743a,  18}},   /* 168 */
+{ 1, 1, {  0x743a7,  22}},   /* 169 */
+{ 1, 1, { 0xe8743a,  27}},   /* 170 */
+{ 1, 2, { 0x1d0e87,  24}},   /* 171 */
+{ 1, 2, {   0x3a1f,  17}},   /* 172 */
+{ 1, 1, {  0xe87c7,  23}},   /* 173 */
+{ 1, 1, {   0x3a1e,  17}},   /* 174 */
+{ 1, 4, {   0x0e87,  15}},   /* 175 */
+{ 1, 4, {   0x001f,   8}},   /* 176 */
+{ 1, 1, {   0x01f8,  12}},   /* 177 */
+{ 1, 1, {   0x0fba,  15}},   /* 178 */
+{ 1, 2, {   0x01f7,  12}},   /* 179 */
+{ 1, 2, {   0x3e3a,  17}},   /* 180 */
+{ 1, 1, {  0xf8e87,  23}},   /* 181 */
+{ 1, 1, {   0x1f1f,  16}},   /* 182 */
+{ 1, 3, {   0x07c7,  14}},   /* 183 */
+{ 1, 3, {   0x001e,   8}},   /* 184 */
+{ 1, 1, {   0x01e7,  12}},   /* 185 */
+{ 1, 1, {   0x3c3a,  17}},   /* 186 */
+{ 1, 2, {   0x0787,  14}},   /* 187 */
+{ 1, 2, {   0x003b,   9}},   /* 188 */
+{ 1, 1, {   0x0ec7,  15}},   /* 189 */
+{ 1, 1, {   0x0073,  10}},   /* 190 */
+{ 1, 6, {   0x0007,   6}},   /* 191 */
+{ 2, 6, {   0x0000,   0}},   /* 192 */
+{ 2, 1, {   0x000c,   4}},   /* 193 */
+{ 2, 1, {   0x005a,   7}},   /* 194 */
+{ 2, 2, {   0x000b,   4}},   /* 195 */
+{ 2, 2, {   0x0042,   7}},   /* 196 */
+{ 2, 1, {   0x1087,  13}},   /* 197 */
+{ 2, 1, {   0x0023,   6}},   /* 198 */
+{ 2, 3, {   0x0008,   4}},   /* 199 */
+{ 2, 3, {   0x003a,   7}},   /* 200 */
+{ 2, 1, {   0x03a7,  11}},   /* 201 */
+{ 2, 1, {   0x743a,  16}},   /* 202 */
+{ 2, 2, {   0x0e87,  13}},   /* 203 */
+{ 2, 2, {   0x001f,   6}},   /* 204 */
+{ 2, 1, {   0x07c7,  12}},   /* 205 */
+{ 2, 1, {   0x001e,   6}},   /* 206 */
+{ 2, 4, {   0x0007,   4}},   /* 207 */
+{ 2, 4, {   0x003a,   9}},   /* 208 */
+{ 2, 1, {   0x03a8,  13}},   /* 209 */
+{ 2, 1, {   0x1d3a,  16}},   /* 210 */
+{ 2, 2, {   0x03a7,  13}},   /* 211 */
+{ 2, 2, {   0x743a,  18}},   /* 212 */
+{ 2, 1, { 0x1d0e87,  24}},   /* 213 */
+{ 2, 1, {   0x3a1f,  17}},   /* 214 */
+{ 2, 3, {   0x0e87,  15}},   /* 215 */
+{ 2, 3, {   0x001f,   8}},   /* 216 */
+{ 2, 1, {   0x01f7,  12}},   /* 217 */
+{ 2, 1, {   0x3e3a,  17}},   /* 218 */
+{ 2, 2, {   0x07c7,  14}},   /* 219 */
+{ 2, 2, {   0x001e,   8}},   /* 220 */
+{ 2, 1, {   0x0787,  14}},   /* 221 */
+{ 2, 1, {   0x003b,   9}},   /* 222 */
+{ 2, 5, {   0x0007,   6}},   /* 223 */
+{ 3, 5, {   0x0000,   0}},   /* 224 */
+{ 3, 1, {   0x000b,   4}},   /* 225 */
+{ 3, 1, {   0x0042,   7}},   /* 226 */
+{ 3, 2, {   0x0008,   4}},   /* 227 */
+{ 3, 2, {   0x003a,   7}},   /* 228 */
+{ 3, 1, {   0x0e87,  13}},   /* 229 */
+{ 3, 1, {   0x001f,   6}},   /* 230 */
+{ 3, 3, {   0x0007,   4}},   /* 231 */
+{ 3, 3, {   0x003a,   9}},   /* 232 */
+{ 3, 1, {   0x03a7,  13}},   /* 233 */
+{ 3, 1, {   0x743a,  18}},   /* 234 */
+{ 3, 2, {   0x0e87,  15}},   /* 235 */
+{ 3, 2, {   0x001f,   8}},   /* 236 */
+{ 3, 1, {   0x07c7,  14}},   /* 237 */
+{ 3, 1, {   0x001e,   8}},   /* 238 */
+{ 3, 4, {   0x0007,   6}},   /* 239 */
+{ 4, 4, {   0x0000,   0}},   /* 240 */
+{ 4, 1, {   0x0008,   4}},   /* 241 */
+{ 4, 1, {   0x003a,   7}},   /* 242 */
+{ 4, 2, {   0x0007,   4}},   /* 243 */
+{ 4, 2, {   0x003a,   9}},   /* 244 */
+{ 4, 1, {   0x0e87,  15}},   /* 245 */
+{ 4, 1, {   0x001f,   8}},   /* 246 */
+{ 4, 3, {   0x0007,   6}},   /* 247 */
+{ 5, 3, {   0x0000,   0}},   /* 248 */
+{ 5, 1, {   0x0007,   4}},   /* 249 */
+{ 5, 1, {   0x003a,   9}},   /* 250 */
+{ 5, 2, {   0x0007,   6}},   /* 251 */
+{ 6, 2, {   0x0000,   0}},   /* 252 */
+{ 6, 1, {   0x0007,   6}},   /* 253 */
+{ 7, 1, {   0x0000,   0}},   /* 254 */
+{ 0, 8, {   0x0000,   0}},   /* 255 */
+  };
+
 #endif
+
diff --git a/converter/pbm/g3topbm.c b/converter/pbm/g3topbm.c
index 83479b91..f50339fd 100644
--- a/converter/pbm/g3topbm.c
+++ b/converter/pbm/g3topbm.c
@@ -44,11 +44,11 @@
 
 #define HASHSIZE 1021
 
-static g3TableEntry * whash[HASHSIZE];
-static g3TableEntry * bhash[HASHSIZE];
+static G3TableEntry * whash[HASHSIZE];
+static G3TableEntry * bhash[HASHSIZE];
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -63,8 +63,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** const argv,
-                 struct cmdlineInfo * const cmdlineP) {
+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
    was passed to us as the argv array.
@@ -98,7 +98,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char**)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (widthSpec && paper_sizeSpec)
@@ -136,7 +136,7 @@ parseCommandLine(int argc, char ** const argv,
 
 
 
-struct bitStream {
+struct BitStream {
 
     FILE * fileP;
     bool reversebits;
@@ -156,7 +156,7 @@ struct bitStream {
 
 
 static void
-readBit(struct bitStream * const bitStreamP,
+readBit(struct BitStream * const bitStreamP,
         unsigned int *     const bitP,
         const char **      const errorP) {
 /*----------------------------------------------------------------------------
@@ -189,7 +189,7 @@ readBit(struct bitStream * const bitStreamP,
 
 
 static void
-readBitAndDetectEol(struct bitStream * const bitStreamP,
+readBitAndDetectEol(struct BitStream * const bitStreamP,
                     unsigned int *     const bitP,
                     bool *             const eolP,
                     const char **      const errorP) {
@@ -217,7 +217,7 @@ readBitAndDetectEol(struct bitStream * const bitStreamP,
 
 
 static void
-initBitStream(struct bitStream * const bitStreamP,
+initBitStream(struct BitStream * const bitStreamP,
               FILE *             const fileP,
               bool               const reversebits) {
     
@@ -230,7 +230,7 @@ initBitStream(struct bitStream * const bitStreamP,
 
 
 static void
-skipToNextLine(struct bitStream * const bitStreamP) {
+skipToNextLine(struct BitStream * const bitStreamP) {
 
     bool eol;
     const char * error;
@@ -248,8 +248,8 @@ skipToNextLine(struct bitStream * const bitStreamP) {
 
 
 static void
-addtohash(g3TableEntry *     hash[], 
-          g3TableEntry       table[], 
+addtohash(G3TableEntry *     hash[], 
+          G3TableEntry       table[], 
           unsigned int const n, 
           int          const a, 
           int          const b) {
@@ -257,7 +257,7 @@ addtohash(g3TableEntry *     hash[],
     unsigned int i;
 
     for (i = 0; i < n; ++i) {
-        g3TableEntry * const teP = &table[i*2];
+        G3TableEntry * const teP = &table[i*2];
         unsigned int const pos =
             ((teP->length + a) * (teP->code + b)) % HASHSIZE;
         if (hash[pos])
@@ -268,15 +268,15 @@ addtohash(g3TableEntry *     hash[],
 
 
 
-static g3TableEntry *
-hashfind(g3TableEntry *       hash[], 
+static G3TableEntry *
+hashfind(G3TableEntry *       hash[], 
          int            const length, 
          int            const code, 
          int            const a, 
          int            const b) {
 
     unsigned int pos;
-    g3TableEntry * te;
+    G3TableEntry * te;
 
     pos = ((length + a) * (code + b)) % HASHSIZE;
     te = hash[pos];
@@ -286,8 +286,8 @@ hashfind(g3TableEntry *       hash[],
 
 
 static void
-buildHashes(g3TableEntry * (*whashP)[HASHSIZE],
-            g3TableEntry * (*bhashP)[HASHSIZE]) {
+buildHashes(G3TableEntry * (*whashP)[HASHSIZE],
+            G3TableEntry * (*bhashP)[HASHSIZE]) {
 
     unsigned int i;
 
@@ -315,7 +315,7 @@ makeRowWhite(unsigned char * const packedBitrow,
 
 
 
-static g3TableEntry *
+static G3TableEntry *
 g3code(unsigned int const curcode,
        unsigned int const curlen,
        bit          const color) {
@@ -326,7 +326,7 @@ g3code(unsigned int const curcode,
    Note that it is the _position_ in the table that determines the meaning
    of the code.  The contents of the table entry do not.
 -----------------------------------------------------------------------------*/
-    g3TableEntry * retval;
+    G3TableEntry * retval;
 
     switch (color) {
     case PBM_WHITE:
@@ -383,7 +383,7 @@ writeBlackBitSpan(unsigned char * const packedBitrow,
 enum g3tableId {TERMWHITE, TERMBLACK, MKUPWHITE, MKUPBLACK};
 
 static void
-processG3Code(const g3TableEntry * const teP,
+processG3Code(const G3TableEntry * const teP,
               unsigned char *      const packedBitrow,
               unsigned int *       const colP,
               bit *                const colorP,
@@ -452,7 +452,7 @@ formatBadCodeException(const char ** const exceptionP,
 
 
 static void
-readFaxRow(struct bitStream * const bitStreamP,
+readFaxRow(struct BitStream * const bitStreamP,
            unsigned char *    const packedBitrow,
            unsigned int *     const lineLengthP,
            const char **      const exceptionP,
@@ -526,7 +526,7 @@ readFaxRow(struct bitStream * const bitStreamP,
                     formatBadCodeException(exceptionP, col, curlen, curcode);
                     done = TRUE;
                 } else if (curcode != 0) {
-                    const g3TableEntry * const teP =
+                    const G3TableEntry * const teP =
                         g3code(curcode, curlen, currentColor);
                         /* Address of structure that describes the 
                            current G3 code.  Null means 'curcode' isn't
@@ -680,7 +680,7 @@ analyzeLineSize(lineSizeAnalyzer * const analyzerP,
 */
 
 static void
-readFax(struct bitStream * const bitStreamP,
+readFax(struct BitStream * const bitStreamP,
         bool               const stretch,
         unsigned int       const expectedLineSize,
         bool               const tolerateErrors,
@@ -747,16 +747,16 @@ readFax(struct bitStream * const bitStreamP,
 
 
 int
-main(int argc, char * argv[]) {
+main(int argc, const char * argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
-    struct bitStream bitStream;
+    struct BitStream bitStream;
     unsigned int rows, cols;
     unsigned char ** packedBits;
     int row;
 
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
diff --git a/converter/pbm/pbmtog3.c b/converter/pbm/pbmtog3.c
index f0fd1252..4f19ec2f 100644
--- a/converter/pbm/pbmtog3.c
+++ b/converter/pbm/pbmtog3.c
@@ -1,84 +1,59 @@
 /* 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 "pbm.h"
 
-#define TC_MC 64
+enum G3eol {EOL, ALIGN8, ALIGN16, NO_EOL, NO_RTC, NO_EOLRTC};
 
-static bool const pbmtorl = 
-#ifdef PBMTORL
-    TRUE;
-#else
-    FALSE;
-#endif
+enum OutMode {BINARY, DUMP};
 
+struct OutStream;
 
-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.
-        */
+typedef void PutbitsFn(struct OutStream * const outP,
+                       struct BitString   const newBits);
 
-    /* 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 */  
+    PutbitsFn * putbitsFn;
+    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 dump;
+    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 +62,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,24 +72,45 @@ 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,   "align8",           OPT_FLAG,  NULL, &align8,
+            0);
+    OPTENT3(0,   "align16",          OPT_FLAG,  NULL, &align16,
             0);
     OPTENT3(0,   "verbose",          OPT_FLAG,  NULL, &cmdlineP->verbose, 
             0);
+    OPTENT3(0,   "dump",             OPT_FLAG,  NULL, &cmdlineP->dump,
+            0);
 
     /* TODO
        Explicit fixed widths: -A4 -B4 -A3
     */
 
     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 (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)
@@ -129,27 +127,74 @@ 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");
+    }
 }
 
 
 
-static __inline__ void
-putbits(struct bitString const newBits) {
+static PutbitsFn putbitsDump;
+
+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.
+-----------------------------------------------------------------------------*/
+    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);
+}
+
+
+
+static PutbitsFn putbitsDump;
+
+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 +203,463 @@ 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;
+        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;
-
-        wordintToBytes(&outbytes, 
-                       (out.buffer.intBuffer << spaceLeft) 
-                       | (newBits.intBuffer >> nextBufBitCount));
-        if (out.reverseBits)
-            reversebuffer(outbytes, sizeof(outbytes));
+        outP->buffer.intBuffer = ( (outP->buffer.intBuffer << spaceLeft) 
+                                 | (newBits.intBuffer >> nextBufBitCount));
+        outP->buffer.bitCount  = fullBuffer;
+        flushBuffer(outP);
 
-        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;
+        outP->buffer.intBuffer = newBits.intBuffer & bitMask;
+        outP->buffer.bitCount = nextBufBitCount;
     }
 }
 
 
 
 static void 
-initOutStream(bool const reverseBits) {
-    out.buffer.intBuffer = 0;
-    out.buffer.bitCount  = 0;
-    out.reverseBits = reverseBits;
+initOutStream(struct OutStream * const outP,
+              bool               const reverseBits,
+              enum G3eol         const eolAlign,
+              bool               const wantDump) {
+
+    outP->buffer.intBuffer = 0;
+    outP->buffer.bitCount  = 0;
+    outP->reverseBits      = reverseBits;
+    outP->putbitsFn        = wantDump ? &putbitsDump : &putbitsBinary;
+    outP->fp               = wantDump ? stderr : 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 __inline__ void
-putcode(unsigned int const clr, 
-        unsigned int const ix) {
+static void
+putbits(struct OutStream * const outP,
+        struct BitString   const newBits) {
+
+    outP->putbitsFn(outP, newBits);
+}
+
+
+
+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.  
+       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(ttable[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 ttable, mtable entries in pbmtog3.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 = ttable[loIndex].length;
+    unsigned int const hiLength = mtable[hiIndex].length;
+
+    struct BitString combinedCode;
+
+    combinedCode.intBuffer = mtable[hiIndex].code << loLength |
+                             ttable[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 = mtable[2560/64];
+                              /* 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;
+
+    assert(colChars > 0);
 
-  Write inflection (=color change) points into array milepost[].  
-  It is easy to calculate run length from this.
+    for (charCnt = 0; charCnt < colChars && bitrow[charCnt] == 0; ++charCnt);
 
-  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.
+    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;
 
-  WWWWWWWBBWWWWWWW ... = 7,2,7, ...
-  BBBBBWBBBBBWBBBB ... = 0,5,1,5,1,4, ...
+        charCnt = colChars - 1;
 
-  Return the number of milepost elements written.
-  Note that max number of entries into milepost = cols+1 .
+        while (bitrow[charCnt--] == 0x00)
+            ;
+        lastChar = charCnt + 1;
+    }
 
-  The inflection points are calculated like this:
+    *firstNonWhiteCharP = firstChar;
+    *lastNonWhiteCharP  = lastChar;
+    *blankRowP          = blankRow;
+}
 
-   r1: 00000000000111111110011111000000
-   r2: c0000000000011111111001111100000 0->carry
-  xor: ?0000000000100000001010000100000
 
-  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 codes
+   for either ones 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 = prefabCode[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, cmdline.dump);
+    
+    puteol(&out);
 
     for (row = 0; row < rows; ++row) {
-        unsigned int nRun;  /* Number of runs in milepost[] */
-        unsigned int p;
-        unsigned int i;
-
         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;
 }
+
+
+