about summary refs log tree commit diff
path: root/converter/pbm
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2006-08-19 03:12:28 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2006-08-19 03:12:28 +0000
commit1fd361a1ea06e44286c213ca1f814f49306fdc43 (patch)
tree64c8c96cf54d8718847339a403e5e67b922e8c3f /converter/pbm
downloadnetpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.gz
netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.tar.xz
netpbm-mirror-1fd361a1ea06e44286c213ca1f814f49306fdc43.zip
Create Subversion repository
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/pbm')
-rw-r--r--converter/pbm/Makefile78
-rw-r--r--converter/pbm/atktopbm.c362
-rw-r--r--converter/pbm/brushtopbm.c107
-rw-r--r--converter/pbm/cmuwm.h17
-rw-r--r--converter/pbm/cmuwmtopbm.c114
-rw-r--r--converter/pbm/ddbugtopbm.c173
-rw-r--r--converter/pbm/escp2topbm.c143
-rw-r--r--converter/pbm/g3.h146
-rw-r--r--converter/pbm/g3topbm.c739
-rw-r--r--converter/pbm/icontopbm.c159
-rw-r--r--converter/pbm/macp.h12
-rw-r--r--converter/pbm/macptopbm.c140
-rw-r--r--converter/pbm/mdaspec.txt239
-rw-r--r--converter/pbm/mdatopbm.c271
-rw-r--r--converter/pbm/mgr.h25
-rw-r--r--converter/pbm/mgrtopbm.c145
-rw-r--r--converter/pbm/mrftopbm.c210
-rw-r--r--converter/pbm/pbmto10x.c169
-rw-r--r--converter/pbm/pbmto4425.c179
-rw-r--r--converter/pbm/pbmtoascii.c165
-rw-r--r--converter/pbm/pbmtoatk.c188
-rw-r--r--converter/pbm/pbmtobbnbg.c152
-rw-r--r--converter/pbm/pbmtocmuwm.c117
-rw-r--r--converter/pbm/pbmtodjvurle.c140
-rw-r--r--converter/pbm/pbmtoepsi.c252
-rw-r--r--converter/pbm/pbmtoepson.c338
-rw-r--r--converter/pbm/pbmtoescp2.c186
-rw-r--r--converter/pbm/pbmtog3.c485
-rw-r--r--converter/pbm/pbmtog3.test23
-rw-r--r--converter/pbm/pbmtogem.c252
-rw-r--r--converter/pbm/pbmtogo.c309
-rw-r--r--converter/pbm/pbmtoibm23xx.c213
-rw-r--r--converter/pbm/pbmtoicon.c134
-rw-r--r--converter/pbm/pbmtolj.c564
-rw-r--r--converter/pbm/pbmtoln03.c260
-rw-r--r--converter/pbm/pbmtolps.c181
-rw-r--r--converter/pbm/pbmtomacp.c301
-rw-r--r--converter/pbm/pbmtomatrixorbital.c87
-rw-r--r--converter/pbm/pbmtomda.c201
-rw-r--r--converter/pbm/pbmtomgr.c120
-rw-r--r--converter/pbm/pbmtomrf.c338
-rw-r--r--converter/pbm/pbmtonokia.c225
-rw-r--r--converter/pbm/pbmtopi3.c123
-rw-r--r--converter/pbm/pbmtopk.c976
-rw-r--r--converter/pbm/pbmtoplot.c76
-rw-r--r--converter/pbm/pbmtoppa/CREDITS12
-rw-r--r--converter/pbm/pbmtoppa/INSTALL-MORE187
-rw-r--r--converter/pbm/pbmtoppa/LICENSE342
-rw-r--r--converter/pbm/pbmtoppa/Makefile25
-rw-r--r--converter/pbm/pbmtoppa/README.Netpbm30
-rw-r--r--converter/pbm/pbmtoppa/README.REDHAT29
-rw-r--r--converter/pbm/pbmtoppa/cutswath.c360
-rw-r--r--converter/pbm/pbmtoppa/cutswath.h4
-rw-r--r--converter/pbm/pbmtoppa/defaults.h65
-rw-r--r--converter/pbm/pbmtoppa/pbm.c135
-rw-r--r--converter/pbm/pbmtoppa/pbmtoppa.c449
-rw-r--r--converter/pbm/pbmtoppa/pbmtoppa.conf.hp100018
-rw-r--r--converter/pbm/pbmtoppa/pbmtoppa.conf.hp72018
-rw-r--r--converter/pbm/pbmtoppa/pbmtoppa.conf.hp82018
-rw-r--r--converter/pbm/pbmtoppa/ppa.c526
-rw-r--r--converter/pbm/pbmtoppa/ppa.h74
-rw-r--r--converter/pbm/pbmtoppa/ppapbm.h29
-rw-r--r--converter/pbm/pbmtopsg3.c374
-rw-r--r--converter/pbm/pbmtoptx.c101
-rw-r--r--converter/pbm/pbmtowbmp.c70
-rw-r--r--converter/pbm/pbmtox10bm.c120
-rw-r--r--converter/pbm/pbmtoxbm.c165
-rw-r--r--converter/pbm/pbmtoybm.c114
-rw-r--r--converter/pbm/pbmtozinc.c128
-rw-r--r--converter/pbm/pi3topbm.c112
-rw-r--r--converter/pbm/pktopbm.c442
-rw-r--r--converter/pbm/thinkjettopbm.c1792
-rw-r--r--converter/pbm/thinkjettopbm.l251
-rw-r--r--converter/pbm/wbmptopbm.c112
-rw-r--r--converter/pbm/xbmtopbm.c255
-rw-r--r--converter/pbm/ybmtopbm.c113
76 files changed, 16304 insertions, 0 deletions
diff --git a/converter/pbm/Makefile b/converter/pbm/Makefile
new file mode 100644
index 00000000..747f2a4d
--- /dev/null
+++ b/converter/pbm/Makefile
@@ -0,0 +1,78 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/pbm
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+PORTBINARIES =	atktopbm brushtopbm cmuwmtopbm ddbugtopbm g3topbm escp2topbm \
+		icontopbm macptopbm mdatopbm mgrtopbm mrftopbm \
+		pbmto10x pbmto4425 pbmtoascii pbmtoatk \
+		pbmtobbnbg pbmtocmuwm pbmtodjvurle \
+		pbmtoepsi pbmtoepson pbmtoescp2 \
+		pbmtog3 pbmtogem pbmtogo pbmtoibm23xx pbmtoicon pbmtolj \
+		pbmtoln03 pbmtolps \
+		pbmtomacp pbmtomatrixorbital pbmtomda pbmtomgr pbmtomrf \
+		pbmtonokia \
+		pbmtopi3 pbmtoplot pbmtopsg3 pbmtoptx pbmtowbmp \
+		pbmtox10bm pbmtoxbm pbmtoybm pbmtozinc \
+		pi3topbm pktopbm \
+		wbmptopbm xbmtopbm ybmtopbm	
+
+ifneq ($(LEX)x,x)
+  PORTBINARIES += thinkjettopbm
+endif
+
+#pbmpage uses sqrt(), which is sometimes in libc, not libm.  Is it ever
+#in libm?
+MATHBINARIES =	pbmtopk
+BINARIES =	$(PORTBINARIES) $(MATHBINARIES)
+SCRIPTS =
+
+OBJECTS = $(BINARIES:%=%.o)
+
+MERGEBINARIES = $(BINARIES)
+MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
+
+SUBDIRS=pbmtoppa
+
+.PHONY: all
+all: $(BINARIES) $(SUBDIRS:%=%/all)
+
+include $(SRCDIR)/Makefile.common
+
+ifneq ($(LEX)x,x)
+thinkjettopbm.c1:%.c1:%.l
+	$(LEX) -t $< >$@
+endif
+
+thinkjettopbm.c:%.c:%.c1 $(SRCDIR)/lib/util/lexheader
+# Different versions of Lex produce subtly different output, from the
+# same .l source file.  The .c1 file contains the raw output from Lex.
+# We now massage it so it will compile.  We must add some definitions
+# at the top (the lexheader file).  We must remove any yylex and
+# yywrap prototype, as our .l file already contains one.  The Lex
+# version "Software Generation Utilities (SGU) Solaris-ELF (4.0)"
+# puts declarations for yylex and yywrap, as external symbols,
+# into its output, causing a duplicate declaration error at compile time.
+#
+# Schwarb Manfred reports that it compiles OK, but with warnings, on
+# Solaris.  Solaris Lex has a -e option that eliminates the lex
+# warnings, but causes compiler warnings.  AIX and Flex don't have a
+# -e option.  -Bryan 2001.05.16.
+#
+# But Peter Weisz reported on 2002.12.11 that on Solaris, compile
+# failed due to a duplicate declaration of yylex and yywrap with Netpbm
+# 10.12, which does not remove those declarations as the current version
+# does.
+	cat $(SRCDIR)/lib/util/lexheader $< | \
+	  grep -v "^[[:space:]]*int yylex(void);" | \
+	  grep -v "^[[:space:]]*int yywrap(void);" \
+	  >$@
+
+clean: localclean
+.PHONY: localclean
+localclean:
+	-rm -f thinkjettopbm.c
diff --git a/converter/pbm/atktopbm.c b/converter/pbm/atktopbm.c
new file mode 100644
index 00000000..c4a81808
--- /dev/null
+++ b/converter/pbm/atktopbm.c
@@ -0,0 +1,362 @@
+/* atktopbm.c - convert Andrew Toolkit raster object to portable bitmap
+**
+** Copyright (C) 1991 by Bill Janssen
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "nstring.h"
+#include "pbm.h"
+#include "mallocvar.h"
+
+
+/* readatkraster
+**
+** Routine for reading rasters in .raster form.  (BE2 rasters version 2.)
+*/
+
+/* codes for data stream */
+#define WHITEZERO   'f'
+#define WHITETWENTY 'z'
+#define BLACKZERO   'F'
+#define BLACKTWENTY 'Z'
+#define OTHERZERO   0x1F
+
+#define WHITEBYTE   0x00
+#define BLACKBYTE   0xFF
+
+/* error codes (copied from $ANDREW/atk/basics/common/dataobj.ch) */
+/* return values from Read */
+#define dataobject_NOREADERROR  0
+#define dataobject_PREMATUREEOF 1
+#define dataobject_NOTBE2DATASTREAM 2 /* backward compatibility */
+#define dataobject_NOTATKDATASTREAM 2 /* preferred version */
+#define dataobject_MISSINGENDDATAMARKER 3
+#define dataobject_OBJECTCREATIONFAILED 4
+#define dataobject_BADFORMAT 5
+
+/* ReadRow(file, row, length) 
+** Reads from 'file' the encoding of bytes to fill in 'row'.  Row will be
+** truncated or padded (with WHITE) to exactly 'length' bytes.
+**
+** Returns the code that terminated the row.  This may be
+**      '|'     correct end of line
+**      '\0'    if the length was satisfied (before a terminator)
+**      EOF     if the file ended
+**      '\'  '{'    other recognized ends. 
+** The '|' is the expected end and pads the row with WHITE.
+** The '\' and '{' are error conditions and may indicate the
+** beginning of some other portion of the data stream.
+** If the terminator is '\' or '{', it is left at the front of the input.
+** '|' is gobbled up.
+*/
+
+/* macros to generate case entries for switch statement */
+#define case1(v) case v
+#define case4(v) case v: case (v)+1: case (v)+2: case(v)+3
+#define case6(v) case4(v): case ((v)+4): case ((v)+5)
+#define case8(v) case4(v): case4((v)+4)
+
+static long
+ReadRow(FILE * const file, unsigned char * const row, long const length) {
+/*----------------------------------------------------------------------------
+  'file' is where to get them from.
+  'row' is where to put bytes.
+  'length' is how many bytes in row must be filled.
+-----------------------------------------------------------------------------*/
+    /* Each input character is processed by the central loop.  There are 
+    ** some input codes which require two or three characters for
+    ** completion; these are handled by advancing the state machine.
+    ** Errors are not processed; instead the state machine is reset
+    ** to the Ready state whenever a character unacceptable to the
+    ** current state is read.
+    */
+    enum stateCode {
+        Ready,      /* any input code is allowed */
+        HexDigitPending,    /* have seen the first of a hex digit pair */
+        RepeatPending,  /* repeat code has been seen:
+                   must be followed by two hex digits */
+        RepeatAndDigit};    /* have seen repeat code and its first
+                   following digit */
+    enum stateCode InputState;  /* current state */
+    register int c;     /* the current input character */
+    register long repeatcount = 0;  /* current repeat value */
+    register long hexval;   /* current hex value */
+    long pendinghex = 0;    /* the first of a pair of hex characters */
+    int lengthRemaining;
+    unsigned char * cursor;
+    
+    /* We cannot exit when length becomes zero because we need to check 
+    ** to see if a row ending character follows.  Thus length is checked
+    ** only when we get a data generating byte.  If length then is
+    ** zero, we ungetc the byte.
+    */
+
+    lengthRemaining = length;
+    cursor = row;
+
+    InputState = Ready;
+    while ((c=getc(file)) != EOF) switch (c) {
+
+    case8(0x0):
+    case8(0x8):
+    case8(0x10):
+    case8(0x18):
+    case1(' '):
+        /* control characters and space are legal and ignored */
+        break;
+    case1(0x40):    /* '@' */
+    case1(0x5B):    /* '[' */
+    case4(0x5D):    /*  ']'  '^'  '_'  '`' */
+    case4(0x7D):    /* '}'  '~'  DEL  0x80 */
+    default:        /* all above 0x80 */
+        /* error code:  Ignored at present.  Reset InputState. */
+        InputState = Ready;
+        break;
+
+    case1(0x7B):    /* '{' */
+    case1(0x5C):    /* '\\' */
+        /* illegal end of line:  exit anyway */
+        ungetc(c, file);    /* retain terminator in stream */
+        /* DROP THROUGH */
+    case1(0x7C):    /* '|' */
+        /* legal end of row: may have to pad  */
+        while (lengthRemaining-- > 0)
+            *cursor++ = WHITEBYTE;
+        return c;
+    
+    case1(0x21):
+    case6(0x22):
+    case8(0x28):
+        /* punctuation characters: repeat byte given by two
+        ** succeeding hex chars
+        */
+        if (lengthRemaining <= 0) {
+            ungetc(c, file);
+            return('\0');
+        }
+        repeatcount = c - OTHERZERO;
+        InputState = RepeatPending;
+        break;
+
+    case8(0x30):
+    case8(0x38):
+        /* digit (or following punctuation)  -  hex digit */
+        hexval = c - 0x30;
+        goto hexdigit;
+    case6(0x41):
+        /* A ... F    -  hex digit */
+        hexval = c - (0x41 - 0xA);
+        goto hexdigit;
+    case6(0x61):
+        /* a ... f  - hex digit */
+        hexval = c - (0x61 - 0xA);
+        goto hexdigit;
+
+    case8(0x67):
+    case8(0x6F):
+    case4(0x77):
+        /* g ... z   -   multiple WHITE bytes */
+        if (lengthRemaining <= 0) {
+            ungetc(c, file);
+            return('\0');
+        }
+        repeatcount = c - WHITEZERO;
+        hexval = WHITEBYTE;
+        goto store;
+    case8(0x47):
+    case8(0x4F):
+    case4(0x57):
+        /* G ... Z   -   multiple BLACK bytes */
+        if (lengthRemaining <= 0) {
+            ungetc(c, file);
+            return('\0');
+        }
+        repeatcount = c - BLACKZERO;
+        hexval = BLACKBYTE;
+        goto store;
+
+hexdigit:
+        /* process a hex digit.  Use InputState to determine
+            what to do with it. */
+        if (lengthRemaining <= 0) {
+            ungetc(c, file);
+            return('\0');
+        }
+        switch(InputState) {
+        case Ready:
+            InputState = HexDigitPending;
+            pendinghex = hexval << 4;
+            break;
+        case HexDigitPending:
+            hexval |= pendinghex;
+            repeatcount = 1;
+            goto store;
+        case RepeatPending:
+            InputState = RepeatAndDigit;
+            pendinghex = hexval << 4;
+            break;
+        case RepeatAndDigit:
+            hexval |= pendinghex;
+            goto store;
+        }
+        break;
+
+store:
+        /* generate byte(s) into the output row 
+            Use repeatcount, depending on state.  */
+        if (lengthRemaining < repeatcount) 
+            /* reduce repeat count if it would exceed
+                available space */
+            repeatcount = lengthRemaining;
+        lengthRemaining -= repeatcount;  /* do this before repeatcount-- */
+        while (repeatcount-- > 0)
+                *cursor++ = hexval;
+        InputState = Ready;
+        break;
+
+    } /* end of while( - )switch( - ) */
+    return EOF;
+}
+
+
+
+#undef case1
+#undef case4
+#undef case6
+#undef case8
+
+
+
+static void
+ReadATKRaster(FILE * const file, 
+              int * const rwidth, 
+              int * const rheight, 
+              unsigned char ** const destaddrP) {
+
+    int row, rowlen;  /* count rows;  byte length of row */
+    int version;
+    char keyword[6];
+    int discardid;
+    int objectid;     /* id read for the incoming pixel image */
+    long tc;            /* temp */
+    int width, height;      /* dimensions of image */
+
+    if (fscanf(file, "\\begindata{raster,%d", &discardid) != 1
+                || getc(file) != '}' || getc(file) != '\n')
+      pm_error ("input file not Andrew raster object");
+
+    fscanf(file, " %d ", &version);
+    if (version < 2) 
+      pm_error ("version too old to parse");
+
+    {
+        unsigned int options;
+        long xscale, yscale;
+        long xoffset, yoffset, subwidth, subheight;
+        /* ignore all these features: */
+        fscanf(file, " %u %ld %ld %ld %ld %ld %ld",  
+               &options, &xscale, &yscale, &xoffset, 
+               &yoffset, &subwidth, &subheight);
+    }
+    /* scan to end of line in case this is actually something beyond V2 */
+    while (((tc=getc(file)) != '\n') && (tc != '\\') && (tc != EOF)) {}
+
+    /* read the keyword */
+    fscanf(file, " %5s", keyword);
+    if (!STREQ(keyword, "bits"))
+      pm_error ("keyword is not 'bits'!");
+
+    fscanf(file, " %d %d %d ", &objectid, &width, &height);
+
+    if (width < 1 || height < 1 || width > 1000000 || height > 1000000) 
+      pm_error ("bad width or height");
+
+    *rwidth = width;
+    *rheight = height;
+    rowlen = (width + 7) / 8;
+    MALLOCARRAY(*destaddrP, height * rowlen);
+    if (destaddrP == NULL)
+        pm_error("Unable to allocate %u bytes for the input image.",
+                 height * rowlen);
+    for (row = 0;   row < height;   row++)
+      {
+        long c;
+
+        c = ReadRow(file, *destaddrP + (row * rowlen), rowlen);
+        if (c != '|')
+          {
+        if (c == EOF)
+          pm_error ("premature EOF");
+        else
+          pm_error ("bad format");
+        break;
+          }
+      }
+    while (! feof(file) && getc(file) != '\\') {};  /* scan for \enddata */
+    if (fscanf(file, "enddata{raster,%d", &discardid) != 1
+        || getc(file) != '}' || getc(file) != '\n')
+      pm_error ("missing end-of-object marker");
+}
+
+
+
+int
+main(int argc, char **argv) {
+
+    FILE *ifp;
+    register bit *bitrow, *bP;
+    int rows, cols, row, col, charcount;
+    unsigned char *data, mask;
+
+
+    pbm_init ( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( "[raster obj]" );
+    
+    if ( argc == 2 )
+        ifp = pm_openr( argv[1] );
+    else
+        ifp = stdin;
+
+    ReadATKRaster( ifp, &cols, &rows, &data );
+
+    pm_close( ifp );
+
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    for ( row = 0; row < rows; ++row )
+    {
+        charcount = 0;
+        mask = 0x80;
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+        {
+            if ( charcount >= 8 )
+            {
+                ++data;
+                charcount = 0;
+                mask = 0x80;
+            }
+            *bP = ( *data & mask ) ? PBM_BLACK : PBM_WHITE;
+            ++charcount;
+            mask >>= 1;
+        }
+        ++data;
+        pbm_writepbmrow( stdout, bitrow, cols, 0 );
+    }
+
+    pm_close( stdout );
+    exit( 0 );
+}
+
diff --git a/converter/pbm/brushtopbm.c b/converter/pbm/brushtopbm.c
new file mode 100644
index 00000000..0cffaa4d
--- /dev/null
+++ b/converter/pbm/brushtopbm.c
@@ -0,0 +1,107 @@
+/* brushtopbm.c - read a doodle brush file and write a portable bitmap
+**
+** Copyright (C) 1988 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "pbm.h"
+
+static void getinit ARGS(( FILE* file, int* colsP, int* rowsP ));
+static bit getbit ARGS(( FILE* file ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, padright, row, col;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[brushfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    getinit( ifp, &cols, &rows );
+
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    /* Compute padding to round cols up to the next multiple of 16. */
+    padright = ( ( cols + 15 ) / 16 ) * 16 - cols;
+
+    for ( row = 0; row < rows; ++row )
+	{
+	/* Get data. */
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    *bP = getbit( ifp );
+	/* Discard line padding. */
+        for ( col = 0; col < padright; ++col )
+	    (void) getbit( ifp );
+	/* Write row. */
+	pbm_writepbmrow( stdout, bitrow, cols, 0 );
+	}
+
+    pm_close( ifp );
+    pm_close( stdout );
+    
+    exit( 0 );
+    }
+
+
+static int item, bitsperitem, bitshift;
+
+static void
+getinit( file, colsP, rowsP )
+    FILE* file;
+    int* colsP;
+    int* rowsP;
+    {
+    int i;
+
+    if ( getc( file ) != 1 )
+	pm_error( "bad magic number 1" );
+    if ( getc( file ) != 0 )
+	pm_error( "bad magic number 2" );
+    *colsP = getc( file ) << 8;
+    *colsP += getc( file );
+    *rowsP = getc( file ) << 8;
+    *rowsP += getc( file );
+    bitsperitem = 8;
+
+    /* Junk rest of header. */
+    for ( i = 0; i < 10; ++i )  /* 10 is just a guess at the header size */
+	(void) getc( file );
+    }
+
+static bit
+getbit( file )
+    FILE* file;
+    {
+    bit b;
+
+    if ( bitsperitem == 8 )
+	{
+	item = getc( file );
+	bitsperitem = 0;
+	bitshift = 7;
+	}
+    ++bitsperitem;
+    b = ( ( item >> bitshift) & 1 ) ? PBM_WHITE : PBM_BLACK;
+    --bitshift;
+    return b;
+    }
diff --git a/converter/pbm/cmuwm.h b/converter/pbm/cmuwm.h
new file mode 100644
index 00000000..e667f25e
--- /dev/null
+++ b/converter/pbm/cmuwm.h
@@ -0,0 +1,17 @@
+/* cmuwm.h - definitions for the CMU window manager format
+*/
+
+#ifndef CMUWM_H_INCLUDED
+#define CMUWM_H_INCLUDED
+
+struct cmuwm_header
+    {
+    long magic;
+    long width;
+    long height;
+    short depth;
+    };
+
+#define CMUWM_MAGIC 0xf10040bbL
+
+#endif
diff --git a/converter/pbm/cmuwmtopbm.c b/converter/pbm/cmuwmtopbm.c
new file mode 100644
index 00000000..dce1d449
--- /dev/null
+++ b/converter/pbm/cmuwmtopbm.c
@@ -0,0 +1,114 @@
+/* cmuwmtopbm.c - read a CMU window manager bitmap and produce a portable bitmap
+**
+** Copyright (C) 1989 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "pbm.h"
+#include "cmuwm.h"
+
+static void getinit ARGS(( FILE* file, int* colsP, int* rowsP, short* depthP, int* padrightP ));
+static bit getbit ARGS(( FILE* file ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, padright, row, col;
+    short depth;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[cmuwmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    getinit( ifp, &cols, &rows, &depth, &padright );
+    if ( depth != 1 )
+	pm_error(
+	    "CMU window manager file has depth of %d, must be 1",
+	    (int) depth );
+
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    for ( row = 0; row < rows; ++row )
+	{
+	/* Get data. */
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    *bP = getbit( ifp );
+	/* Discard line padding */
+        for ( col = 0; col < padright; ++col )
+	    (void) getbit( ifp );
+	pbm_writepbmrow( stdout, bitrow, cols, 0 );
+	}
+
+    pm_close( ifp );
+    pm_close( stdout );
+
+    exit( 0 );
+    }
+
+static int item, bitsperitem, bitshift;
+
+static void
+getinit( file, colsP, rowsP, depthP, padrightP )
+    FILE* file;
+    int* colsP;
+    int* rowsP;
+    short* depthP;
+    int* padrightP;
+    {
+    long l;
+
+    if ( pm_readbiglong( file, &l ) == -1 )
+	pm_error( "EOF / read error" );
+    if ( l != CMUWM_MAGIC )
+	pm_error( "bad magic number in CMU window manager file" );
+    if ( pm_readbiglong( file, &l ) == -1 )
+	pm_error( "EOF / read error" );
+    *colsP = (int) l;
+    if ( pm_readbiglong( file, &l ) == -1 )
+	pm_error( "EOF / read error" );
+    *rowsP = (int) l;
+    if ( pm_readbigshort( file, depthP ) == -1 )
+	pm_error( "EOF / read error" );
+    *padrightP = ( ( *colsP + 7 ) / 8 ) * 8 - *colsP;
+
+    bitsperitem = 0;
+    }
+
+static bit
+getbit( file )
+    FILE* file;
+    {
+    bit b;
+
+    if ( bitsperitem == 0 )
+	{
+	item = getc( file );
+	if ( item == EOF )
+	    pm_error( "EOF / read error" );
+	bitsperitem = 8;
+	bitshift = 7;
+	}
+    b = ( ( item >> bitshift) & 1 ) ? PBM_WHITE : PBM_BLACK;
+    --bitsperitem;
+    --bitshift;
+    return b;
+    }
diff --git a/converter/pbm/ddbugtopbm.c b/converter/pbm/ddbugtopbm.c
new file mode 100644
index 00000000..8b0a6d0e
--- /dev/null
+++ b/converter/pbm/ddbugtopbm.c
@@ -0,0 +1,173 @@
+/* ddbugtopbm - convert Diddle/DiddleBug sketch DB to PBM files.
+ * Copyright (c) 2002 Russell Marks. See COPYING for licence details.
+ *
+ * The decompression code (uncompress_sketch() is directly from
+ * DiddleBug itself, which like ddbugtopbm is distributed under the
+ * terms of the GNU GPL. As such, the following copyright applies to
+ * uncompress_sketch():
+ *
+ * Copyright (c) 1999,2000 Mitch Blevins <mblevin@debian.org>.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
+ *
+ * Adapted to Netpbm by Bryan Henderson 2003.08.09.  Bryan got his copy
+ * from ftp:ibiblio.com/pub/linux/apps/graphics/convert, dated 2002.08.21.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "pm_c_util.h"
+#include "pbm.h"
+#include "mallocvar.h"
+
+/* this is basically UncompressSketch() from DiddleBug 2.50's diddlebug.c */
+static void 
+uncompress_sketch(unsigned char * const cPtr,
+                  unsigned char * const uPtr,
+                  int             const size) {
+    int i, j;
+    unsigned char num_bytes;
+
+    j = 0;
+    for (i=0; i<size; ++i) {
+        if (cPtr[i]&0x80) {
+            num_bytes = cPtr[i]&0x3f;  /* ~(0x40|0x80) */
+            ++num_bytes;
+            if (cPtr[i]&0x40) {
+                /* Mixed */
+                memmove(uPtr+j, cPtr+i+1, num_bytes);
+                i += num_bytes;
+            } else {
+                /* Black */
+                memset(uPtr+j, 0xff, num_bytes);
+            }
+        } else {
+            /* White */
+            num_bytes = cPtr[i]&0x7f; /* ~0x80 */
+            ++num_bytes;
+            memset(uPtr+j, 0x00, num_bytes);
+        }
+        j += num_bytes;
+    }
+}
+
+
+
+static const char *
+make_noname(void) {
+    static char name[128];
+    static int num=0;
+    FILE *out;
+
+    out = NULL;
+
+    do {
+        num++;
+        if (out != NULL) 
+            fclose(out);
+        sprintf(name, "sketch-%04d.pbm", num);
+    } while (num<10000 && (out = fopen(name, "rb")) != NULL);
+
+    if (num>=10000)
+        pm_error("too many unnamed sketches!");
+
+    return(name);
+}
+
+
+
+int 
+main(int argc, char ** argv) {
+    FILE * const in=stdin;
+
+    static unsigned char buf[64*1024];
+    int *recoffsets;
+    int f;
+    int numrecs;
+    bool is_diddle;
+
+    pbm_init(&argc, argv);
+
+    if (argc-1 > 0)
+        pm_error("Program takes no arguments.");
+
+    /* main DB header */
+    fread(buf, 1, 64, in);
+
+    fread(buf, 1, 14, in);
+    if (memcmp(buf, "DIDL", 4) != 0 && memcmp(buf, "DIDB", 4) !=0 )
+        pm_error("not a Diddle or DiddleBug DB.");
+    is_diddle = (memcmp(buf, "DIDL", 4) == 0);
+    numrecs = buf[12]*256+buf[13];
+
+    MALLOCARRAY_NOFAIL(recoffsets, numrecs);
+
+    for (f = 0; f < numrecs; ++f) {
+        fread(buf, 1, 8, in);
+        recoffsets[f] = ((buf[0]<<24)|(buf[1]<<16)|(buf[2]<<8)|buf[3]);
+    }
+
+    for (f = 0; f < numrecs; ++f) {
+        static unsigned char obuf[64*1024];
+        char * nameptr;
+        char * ptr;
+        const char * outfilename;
+        FILE *out;
+
+        fseek(in, recoffsets[f], SEEK_SET);
+        fread(buf, 1, 16*1024, in);  /* XXX crappy! */
+
+        if (is_diddle) {
+            /* Diddle */
+            memcpy(obuf, buf, 160*160/8);
+            nameptr=(char*)(buf+(160*160/8));
+        } else {
+            /* DiddleBug */
+            int const sketchlen = buf[8]*256+buf[9];
+            uncompress_sketch(buf+26+80+1, obuf, sketchlen-80-1);
+            nameptr = (char*)(buf+26+sketchlen);
+        }
+
+        for (ptr = nameptr; *ptr; ++ptr) {
+            if (!isalnum(*ptr) && strchr("()-_+=[]:;,.<>?",*ptr) == NULL)
+                *ptr='_';
+            if (isupper(*ptr)) 
+                *ptr = tolower(*ptr);
+        }
+
+        if (*nameptr==0)
+            outfilename = make_noname();
+        else {
+            strcat(nameptr, ".pbm");
+            outfilename = nameptr;
+        }
+
+
+        pm_message("extracting sketch %2d as `%s'", f, outfilename);
+        if((out=fopen(outfilename,"wb"))==NULL)
+            pm_message("WARNING: couldn't open file '%s'.  Carrying on...", 
+                       outfilename);
+        else {
+            pbm_writepbminit(out, 160, 160, FALSE);
+            fwrite(obuf,1,160*160/8,out);
+            fclose(out);
+        }
+    }
+    return 0;
+}
diff --git a/converter/pbm/escp2topbm.c b/converter/pbm/escp2topbm.c
new file mode 100644
index 00000000..049ed23c
--- /dev/null
+++ b/converter/pbm/escp2topbm.c
@@ -0,0 +1,143 @@
+/* escp2topbm.c - read an Epson ESC/P2 printer file and
+**                 create a pbm file from the raster data,
+**                 ignoring all other data.
+**                 Can be regarded as a simple raster printer emulator
+**                 with a RLE run length decoder.
+**                 This program was made primarily for the test of pbmtoescp2
+**
+** Copyright (C) 2003 by Ulrich Walcher (u.walcher@gmx.de)
+**                       and Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <string.h>
+
+#include "pbm.h"
+#include "mallocvar.h"
+
+/* RLE decoder */
+static unsigned int 
+dec_epson_rle(unsigned        const int k, 
+              unsigned        const char * in, 
+              unsigned char * const out) {
+
+    unsigned int i;
+    unsigned int pos;
+    unsigned int dpos;
+
+    pos = 0;  /* initial value */
+    dpos = 0; /* initial value */
+
+    while (dpos < k) {
+        if (in[pos] < 128) {
+            for (i = 0; i < in[pos] + 1; ++i)
+                out[dpos+i] = in[pos + i + 1];     
+            /* copy through */
+            pos += i + 1;
+        } else {
+            for (i = 0; i < 257 - in[pos]; ++i)
+                out[dpos + i] = in[pos + 1];  
+            /* inflate this run */
+            pos += 2;
+        }
+        dpos += i;
+    }
+    return pos;        /* return number of treated input bytes */
+}
+
+
+
+int
+main(int    argc,
+     char * argv[]) {
+
+    unsigned int const size = 4096; /* arbitrary value */
+
+    FILE *ifP;
+    unsigned int i, len, pos, opos, width, height;
+    unsigned char *input, *output;
+    const char * fileName;
+
+    pbm_init(&argc, argv);
+
+    MALLOCARRAY(input, size);
+    MALLOCARRAY(output, size);
+    
+    if (input == NULL || output == NULL)
+        pm_error("Cannot allocate memory");
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments (%u).  Only argument is filename.",
+                 argc-1);
+
+    if (argc == 2)
+        fileName = argv[1];
+    else
+        fileName = "-";
+
+    ifP = pm_openr(fileName);
+
+    /* read the whole file */
+    len = 0;  /* initial value */
+    for (i = 0; !feof(ifP); ++i) {
+        size_t bytesRead;
+        REALLOCARRAY(input, (i+1) * size);
+        if (input == NULL)
+            pm_error("Cannot allocate memory");
+        bytesRead = fread(input + i * size, 1, size, ifP);
+        len += bytesRead;
+    }
+
+    /* filter out raster data */
+    height = 0;  /* initial value */
+    pos = 0;     /* initial value */
+    opos = 0;    /* initial value */
+
+    while (pos < len) {
+        /* only ESC sequences are regarded  */
+        if (input[pos] == '\x1b' && input[pos+1] == '.') {
+            unsigned int const k =
+                input[pos+5] * ((input[pos+7] * 256 + input[pos+6] + 7) / 8);
+            height += input[pos+5];
+            width = input[pos+7] * 256 + input[pos+6];
+            REALLOCARRAY(output, opos + k);
+            if (output == NULL)
+                pm_error("Cannot allocate memory");
+
+            switch (input[pos+2]) {
+            case 0:
+                /* copy the data block */
+                memcpy(output + opos, input + pos + 8, k);        
+                pos += k + 8;
+                opos += k;
+                break;
+            case 1: {
+                /* inflate the data block */
+                unsigned int l;
+                l = dec_epson_rle(k,input+pos+8,output+opos);  
+                pos += l + 8;
+                opos += k;
+            }
+                break;
+            default:
+                pm_error("unknown compression mode");
+                break;
+            }
+        }
+        else
+            ++pos;      /* skip bytes outside the ESCX sequence */
+    }
+
+    pbm_writepbminit(stdout, width, height, 0);
+    fwrite(output, opos, 1, stdout);
+    free(input); free(output);
+    fclose(stdout); fclose(ifP);
+
+    return 0;
+}
diff --git a/converter/pbm/g3.h b/converter/pbm/g3.h
new file mode 100644
index 00000000..e982f2da
--- /dev/null
+++ b/converter/pbm/g3.h
@@ -0,0 +1,146 @@
+/* G3 fax format declarations */
+
+#ifndef G3_H_INCLUDED
+#define G3_H_INCLUDED
+
+/* G3 is nearly universal as the format for fax transmissions in the
+   US.  Its full name is CCITT Group 3 (G3).  It is specified in
+   Recommendations T.4 and T.30 and in EIA Standards EIA-465 and
+   EIA-466.  It dates to 1993.
+
+   G3 faxes are 204 dots per inch (dpi) horizontally and 98 dpi (196
+   dpi optionally, in fine-detail mode) vertically.  Since G3 neither
+   assumes error free transmission nor retransmits when errors occur,
+   the encoding scheme used is differential only over small segments
+   never exceeding 2 lines at standard resolution or 4 lines for
+   fine-detail. (The incremental G3 encoding scheme is called
+   two-dimensional and the number of lines so encoded is specified by
+   a parameter called k.)
+
+   G3 specifies much more than the format of the bit stream, which is
+   the subject of this header file.  It also specifies layers
+   underneath the bit stream.
+
+   There is also the newer G4.  
+*/
+
+typedef struct g3TableEntry {
+    short int code;
+    short int length;
+} g3TableEntry;
+
+static struct g3TableEntry ttable[] = {
+/*    TERMWHITE           TERMBLACK   */
+    { 0x35, 8 },    { 0x37, 10 },       /* white 0 , black 0 */
+    { 0x07, 6 },    { 0x02,  3 },
+    { 0x07, 4 },    { 0x03,  2 },
+    { 0x08, 4 },    { 0x02,  2 },
+    { 0x0b, 4 },    { 0x03,  3 },
+    { 0x0c, 4 },    { 0x03,  4 },
+    { 0x0e, 4 },    { 0x02,  4 },
+    { 0x0f, 4 },    { 0x03,  5 },
+    { 0x13, 5 },    { 0x05,  6 },
+    { 0x14, 5 },    { 0x04,  6 },
+    { 0x07, 5 },    { 0x04,  7 },
+    { 0x08, 5 },    { 0x05,  7 },
+    { 0x08, 6 },    { 0x07,  7 },
+    { 0x03, 6 },    { 0x04,  8 },
+    { 0x34, 6 },    { 0x07,  8 },
+    { 0x35, 6 },    { 0x18,  9 },
+    { 0x2a, 6 },    { 0x17, 10 },
+    { 0x2b, 6 },    { 0x18, 10 },
+    { 0x27, 7 },    { 0x08, 10 },
+    { 0x0c, 7 },    { 0x67, 11 },
+    { 0x08, 7 },    { 0x68, 11 },
+    { 0x17, 7 },    { 0x6c, 11 },
+    { 0x03, 7 },    { 0x37, 11 },
+    { 0x04, 7 },    { 0x28, 11 },
+    { 0x28, 7 },    { 0x17, 11 },
+    { 0x2b, 7 },    { 0x18, 11 },
+    { 0x13, 7 },    { 0xca, 12 },
+    { 0x24, 7 },    { 0xcb, 12 },
+    { 0x18, 7 },    { 0xcc, 12 },
+    { 0x02, 8 },    { 0xcd, 12 },
+    { 0x03, 8 },    { 0x68, 12 },
+    { 0x1a, 8 },    { 0x69, 12 },
+    { 0x1b, 8 },    { 0x6a, 12 },
+    { 0x12, 8 },    { 0x6b, 12 },
+    { 0x13, 8 },    { 0xd2, 12 },
+    { 0x14, 8 },    { 0xd3, 12 },
+    { 0x15, 8 },    { 0xd4, 12 },
+    { 0x16, 8 },    { 0xd5, 12 },
+    { 0x17, 8 },    { 0xd6, 12 },
+    { 0x28, 8 },    { 0xd7, 12 },
+    { 0x29, 8 },    { 0x6c, 12 },
+    { 0x2a, 8 },    { 0x6d, 12 },
+    { 0x2b, 8 },    { 0xda, 12 },
+    { 0x2c, 8 },    { 0xdb, 12 },
+    { 0x2d, 8 },    { 0x54, 12 },
+    { 0x04, 8 },    { 0x55, 12 },
+    { 0x05, 8 },    { 0x56, 12 },
+    { 0x0a, 8 },    { 0x57, 12 },
+    { 0x0b, 8 },    { 0x64, 12 },
+    { 0x52, 8 },    { 0x65, 12 },
+    { 0x53, 8 },    { 0x52, 12 },
+    { 0x54, 8 },    { 0x53, 12 },
+    { 0x55, 8 },    { 0x24, 12 },
+    { 0x24, 8 },    { 0x37, 12 },
+    { 0x25, 8 },    { 0x38, 12 },
+    { 0x58, 8 },    { 0x27, 12 },
+    { 0x59, 8 },    { 0x28, 12 },
+    { 0x5a, 8 },    { 0x58, 12 },
+    { 0x5b, 8 },    { 0x59, 12 },
+    { 0x4a, 8 },    { 0x2b, 12 },
+    { 0x4b, 8 },    { 0x2c, 12 },
+    { 0x32, 8 },    { 0x5a, 12 },
+    { 0x33, 8 },    { 0x66, 12 },
+    { 0x34, 8 },    { 0x67, 12 },       /* white 63 , black 63 */
+
+/* mtable */    
+/*    MKUPWHITE           MKUPBLACK   */
+    { 0x00, 0 },    { 0x00,  0 },   /* dummy to simplify pointer math */
+    { 0x1b, 5 },    { 0x0f, 10 },   /* white 64 , black 64 */
+    { 0x12, 5 },    { 0xc8, 12 },
+    { 0x17, 6 },    { 0xc9, 12 },
+    { 0x37, 7 },    { 0x5b, 12 },
+    { 0x36, 8 },    { 0x33, 12 },
+    { 0x37, 8 },    { 0x34, 12 },
+    { 0x64, 8 },    { 0x35, 12 },
+    { 0x65, 8 },    { 0x6c, 13 },
+    { 0x68, 8 },    { 0x6d, 13 },
+    { 0x67, 8 },    { 0x4a, 13 },
+    { 0xcc, 9 },    { 0x4b, 13 },
+    { 0xcd, 9 },    { 0x4c, 13 },
+    { 0xd2, 9 },    { 0x4d, 13 },
+    { 0xd3, 9 },    { 0x72, 13 },
+    { 0xd4, 9 },    { 0x73, 13 },
+    { 0xd5, 9 },    { 0x74, 13 },
+    { 0xd6, 9 },    { 0x75, 13 },
+    { 0xd7, 9 },    { 0x76, 13 },
+    { 0xd8, 9 },    { 0x77, 13 },
+    { 0xd9, 9 },    { 0x52, 13 },
+    { 0xda, 9 },    { 0x53, 13 },
+    { 0xdb, 9 },    { 0x54, 13 },
+    { 0x98, 9 },    { 0x55, 13 },
+    { 0x99, 9 },    { 0x5a, 13 },
+    { 0x9a, 9 },    { 0x5b, 13 },
+    { 0x18, 6 },    { 0x64, 13 },
+    { 0x9b, 9 },    { 0x65, 13 },
+    { 0x08, 11 },   { 0x08, 11 },        /* extable len = 1792 */
+    { 0x0c, 11 },   { 0x0c, 11 },
+    { 0x0d, 11 },   { 0x0d, 11 },
+    { 0x12, 12 },   { 0x12, 12 },
+    { 0x13, 12 },   { 0x13, 12 },
+    { 0x14, 12 },   { 0x14, 12 },
+    { 0x15, 12 },   { 0x15, 12 },
+    { 0x16, 12 },   { 0x16, 12 },
+    { 0x17, 12 },   { 0x17, 12 },
+    { 0x1c, 12 },   { 0x1c, 12 },
+    { 0x1d, 12 },   { 0x1d, 12 },
+    { 0x1e, 12 },   { 0x1e, 12 },
+    { 0x1f, 12 },   { 0x1f, 12 },
+};
+
+#define mtable ((ttable)+64*2)
+
+#endif
diff --git a/converter/pbm/g3topbm.c b/converter/pbm/g3topbm.c
new file mode 100644
index 00000000..1eefee96
--- /dev/null
+++ b/converter/pbm/g3topbm.c
@@ -0,0 +1,739 @@
+/*===========================================================================
+                            g3topbm
+=============================================================================
+
+  This program reads a Group 3 FAX file and produces a PBM image.
+
+  Bryan Henderson wrote this on August 5, 2004 and contributed it to 
+  the public domain.
+
+  This program is designed to be a drop-in replacement for the program
+  of the same name that was distributed with Pbmplus and Netpbm since
+  1989, written by Paul Haeberli <paul@manray.sgi.com>.
+
+  Bryan used ideas on processing G3 data from Haeberli's code, but did
+  not use any of the code.
+
+  Others have modified the program since Bryan's initial work, each
+  contributing their work to the public domain.
+===========================================================================*/
+
+#include "pbm.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "mallocvar.h"
+#include "g3.h"
+#include "bitreverse.h"
+
+#define MAXCOLS 10800
+#define MAXROWS 14400   /* this allows up to two pages of image */
+
+#define WHASHA 3510
+#define WHASHB 1178
+
+#define BHASHA 293
+#define BHASHB 2695
+
+#define HASHSIZE 1021
+
+static g3TableEntry * whash[HASHSIZE];
+static g3TableEntry * bhash[HASHSIZE];
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFilespec;  /* Filespec of input file */
+    unsigned int reversebits;
+    unsigned int kludge;
+    unsigned int stretch;
+    unsigned int stop_error;
+    unsigned int expectedLineSize;
+};
+
+
+
+static void
+parseCommandLine(int argc, 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.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;  /* malloc'ed */
+        /* Instructions to OptParseOptions3 on how to parse our options.  */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int widthSpec, paper_sizeSpec;
+    const char * paperSize;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "reversebits",      OPT_FLAG,  NULL, &cmdlineP->reversebits,
+            0);
+    OPTENT3(0, "kludge",           OPT_FLAG,  NULL, &cmdlineP->kludge,
+            0);
+    OPTENT3(0, "stretch",          OPT_FLAG,  NULL, &cmdlineP->stretch, 
+            0);
+    OPTENT3(0, "stop_error",       OPT_FLAG,  NULL, &cmdlineP->stop_error, 
+            0);
+    OPTENT3(0, "width",            OPT_UINT,  &cmdlineP->expectedLineSize,
+            &widthSpec,                0);
+    OPTENT3(0, "paper_size",       OPT_STRING, &paperSize,
+            &paper_sizeSpec,           0);
+    
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (widthSpec && paper_sizeSpec)
+        pm_error("You can't specify both -width and -paper_size");
+
+    if (widthSpec) {
+        if (cmdlineP->expectedLineSize < 1)
+            pm_error("-width must be at least 1");
+    } else if (paper_sizeSpec) {
+        if (STRCASEEQ(paperSize, "A6"))
+            cmdlineP->expectedLineSize = 864;
+        else if (STRCASEEQ(paperSize, "A5"))
+            cmdlineP->expectedLineSize = 1216;
+        else if (STRCASEEQ(paperSize, "A4"))
+            cmdlineP->expectedLineSize = 1728;
+        else if (STRCASEEQ(paperSize, "B4"))
+            cmdlineP->expectedLineSize = 2048;
+        else if (STRCASEEQ(paperSize, "A3"))
+            cmdlineP->expectedLineSize = 2432;
+        else
+            pm_error("Unrecognized value for -paper_size '%s'.  "
+                     "We recognize only A3, A4, A5, A6, and B4.",
+                     paperSize);
+    } else
+        cmdlineP->expectedLineSize = 0;
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFilespec = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->inputFilespec = argv[1];
+}
+
+
+
+struct bitStream {
+
+    FILE * fileP;
+    bool reversebits;
+    int shdata;
+        /* 8-bit buffer for rawgetbit(). */
+    unsigned int shbit;
+        /* single bit mask for the bit of 'shdata' that is next in the stream.
+           zero when 'shdata' is empty.
+        */
+    unsigned int zeroBitCount;
+        /* Number of consecutive zero bits the stream has seen.  Note that
+           because an EOL mark ends in a one bit, this starts over for each
+           line.
+        */
+};
+
+
+
+static void
+readBit(struct bitStream * const bitStreamP,
+        unsigned int *     const bitP,
+        const char **      const errorP) {
+/*----------------------------------------------------------------------------
+   Return the next raw bit from the G3 input stream.
+
+   Do not call this outside of the bit stream object; Caller is responsible
+   for maintaining object state.
+-----------------------------------------------------------------------------*/
+    *errorP = NULL;  /* initial assumption */
+
+    if ((bitStreamP->shbit & 0xff) == 0) {
+        bitStreamP->shdata = getc(bitStreamP->fileP);
+        if (bitStreamP->shdata == EOF)
+            asprintfN(errorP, "EOF or error reading file");
+        else {
+            bitStreamP->shbit = 0x80;
+            if ( bitStreamP->reversebits )
+                bitStreamP->shdata = bitreverse[ bitStreamP->shdata ];
+            }
+    }
+
+    if (bitStreamP->shdata & bitStreamP->shbit)
+        *bitP = 1;
+    else
+        *bitP = 0;
+
+    bitStreamP->shbit >>= 1;
+}
+
+
+
+static void
+readBitAndDetectEol(struct bitStream * const bitStreamP,
+                    unsigned int *     const bitP,
+                    bool *             const eolP,
+                    const char **      const errorP) {
+/*----------------------------------------------------------------------------
+   Same as readBit(), but iff the bit read is the final bit of an EOL
+   mark, return *eolP == TRUE.
+-----------------------------------------------------------------------------*/
+    readBit(bitStreamP, bitP, errorP);
+    if (!*errorP) {
+        bool eol;
+
+        eol = FALSE;  /* initial assumption */
+        if (*bitP == 0)
+            ++bitStreamP->zeroBitCount;
+        else {
+            if (bitStreamP->zeroBitCount >= 11)
+                eol = TRUE;
+            bitStreamP->zeroBitCount = 0;
+        }
+        *eolP = eol;
+    }
+}
+
+
+static void
+initBitStream(struct bitStream * const bitStreamP,
+              FILE *             const fileP,
+              bool               const reversebits) {
+    
+    bitStreamP->fileP        = fileP;
+    bitStreamP->reversebits  = reversebits;
+    bitStreamP->shbit        = 0x00;
+    bitStreamP->zeroBitCount = 0;
+}
+
+
+
+static void
+skipToNextLine(struct bitStream * const bitStreamP) {
+
+    bool eol;
+    const char * error;
+
+    eol = FALSE;
+    error = NULL;
+    
+    while (!eol && !error) {
+        unsigned int bit;
+        
+        readBitAndDetectEol(bitStreamP, &bit, &eol, &error);
+    }
+}
+
+
+
+static void
+addtohash(g3TableEntry *     hash[], 
+          g3TableEntry       table[], 
+          unsigned int const n, 
+          int          const a, 
+          int          const b) {
+    
+    unsigned int i;
+
+    for (i = 0; i < n; ++i) {
+        g3TableEntry * const teP = &table[i*2];
+        unsigned int const pos =
+            ((teP->length + a) * (teP->code + b)) % HASHSIZE;
+        if (hash[pos])
+            pm_error("internal error: addtohash fatal hash collision");
+        hash[pos] = teP;
+    }
+}
+
+
+
+static g3TableEntry*
+hashfind(g3TableEntry *       hash[], 
+         int          const length, 
+         int          const code, 
+         int          const a, 
+         int          const b) {
+
+    unsigned int pos;
+    g3TableEntry * te;
+
+    pos = ((length + a) * (code + b)) % HASHSIZE;
+    te = hash[pos];
+    return ((te && te->length == length && te->code == code) ? te : 0);
+}
+
+
+
+static void
+buildHashes(g3TableEntry * (*whashP)[HASHSIZE],
+            g3TableEntry * (*bhashP)[HASHSIZE]) {
+
+    unsigned int i;
+
+    for (i = 0; i < HASHSIZE; ++i)
+        (*whashP)[i] = (*bhashP)[i] = NULL;
+
+    addtohash(*whashP, &ttable[0], 64, WHASHA, WHASHB);
+    addtohash(*whashP, &mtable[2], 40, WHASHA, WHASHB);
+
+    addtohash(*bhashP, &ttable[1], 64, BHASHA, BHASHB);
+    addtohash(*bhashP, &mtable[3], 40, BHASHA, BHASHB);
+
+}
+
+
+
+static void
+makeRowWhite(bit *        const bitrow,
+             unsigned int const cols) {
+
+    unsigned int col;
+    for (col = 0; col < MAXCOLS; ++col)
+        bitrow[col] = PBM_WHITE;
+}
+
+
+
+static g3TableEntry *
+g3code(unsigned int const curcode,
+       unsigned int const curlen,
+       bit          const color) {
+
+    g3TableEntry * retval;
+
+    switch (color) {
+    case PBM_WHITE:
+        if (curlen < 4)
+            retval = NULL;
+        else
+            retval = hashfind(whash, curlen, curcode, WHASHA, WHASHB);
+        break;
+    case PBM_BLACK:
+        if (curlen < 2)
+            retval = NULL;
+        else
+            retval = hashfind(bhash, curlen, curcode, BHASHA, BHASHB);
+        break;
+    default:
+        pm_error("INTERNAL ERROR: color is not black or white");
+    }
+    return retval;
+}
+
+
+
+enum g3tableId {TERMWHITE, TERMBLACK, MKUPWHITE, MKUPBLACK};
+
+
+
+static void
+processG3Code(g3TableEntry * const teP,
+              bit *          const bitrow,
+              unsigned int * const colP,
+              bit *          const colorP,
+              unsigned int * const countP) {
+              
+    enum g3tableId const teId =
+        (teP > mtable ? 2 : 0) + (teP - ttable) % 2;
+
+    unsigned int teCount;
+    
+    switch(teId) {
+    case TERMWHITE: teCount = (teP - ttable    ) / 2;      break;
+    case TERMBLACK: teCount = (teP - ttable - 1) / 2;      break;
+    case MKUPWHITE: teCount = (teP - mtable    ) / 2 * 64; break;
+    case MKUPBLACK: teCount = (teP - mtable - 1) / 2 * 64; break;
+    }
+
+    switch (teId) {
+    case TERMWHITE:
+    case TERMBLACK: {
+        unsigned int runLengthSoFar;
+        unsigned int col;
+        
+        col = *colP;
+        runLengthSoFar = MIN(*countP + teCount, MAXCOLS - col);
+
+        if (runLengthSoFar > 0) {
+            if (*colorP == PBM_WHITE) {
+                /* Row was initialized to white, so we just skip */
+                col += runLengthSoFar;
+            } else {
+                unsigned int i;
+                for (i = 0; i < runLengthSoFar; ++i)
+                    bitrow[col++] = PBM_BLACK;
+            }
+        }
+        *colorP = !*colorP;
+        *countP = 0;
+        *colP   = col;
+    } break;
+    case MKUPWHITE:
+    case MKUPBLACK:
+        *countP += teCount;
+        break;
+    default:
+        pm_error("Can't happen");
+    }
+}
+
+
+
+static void
+formatBadCodeException(const char ** const exceptionP,
+                       unsigned int  const col,
+                       unsigned int  const curlen,
+                       unsigned int  const curcode) {
+
+    asprintfN(exceptionP,
+        "bad code word at Column %u.  "
+        "No prefix of the %u bits 0x%x matches any recognized "
+        "code word and no code words longer than 12 bits are "
+        "defined.  ",
+        col, curlen, curcode);
+}
+
+
+
+static void
+readFaxRow(struct bitStream * const bitStreamP,
+           bit *              const bitrow,
+           unsigned int *     const lineLengthP,
+           const char **      const exceptionP,
+           const char **      const errorP) {
+/*----------------------------------------------------------------------------
+  Read one line of G3 fax from the bit stream *bitStreamP into 
+  bitrow[].  Return the length of the line, in pixels, as *lineLengthP.
+
+  If there's a problem with the line, return as much of it as we can,
+  advance the input stream past the next EOL mark, and put a text
+  description of the problem in newly malloc'ed storage at
+  *exceptionP.  If there's no problem, return *exceptionP = NULL.
+
+  We guarantee that we make progress through the input stream.
+
+  Iff there is an error, return a text description of it in newly
+  malloc'ed storage at *errorP and all other specified behavior 
+  (including return values) is unspecified.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+    unsigned int curlen;  
+        /* Number of bits we've read so far for the code we're currently 
+           reading
+        */
+    unsigned int curcode; 
+        /* What we've assembled so far of the code we're currently reading */
+    unsigned int count;
+        /* Number of consecutive pixels of the same color */
+    bit currentColor;
+        /* The color of the current run of pixels */
+    g3TableEntry * te;
+        /* Address of structure that describes the current G3 code */
+    bool done;
+
+    makeRowWhite(bitrow, MAXCOLS);  /* initialize row */
+
+    col = 0;
+    curlen = 0;
+    curcode = 0;
+    currentColor = PBM_WHITE;
+    count = 0;
+    *exceptionP = NULL;
+    *errorP = NULL;
+    done = FALSE;
+
+    while (!done) {
+        if (col >= MAXCOLS) {
+            asprintfN(exceptionP, "Line is too long for this program to "
+                      "handle -- longer than %u columns", MAXCOLS);
+            done = TRUE;
+        } else {
+            unsigned int bit;
+            bool eol;
+            const char * error;
+
+            readBitAndDetectEol(bitStreamP, &bit, &eol, &error);
+            if (error) {
+                if (col > 0)
+                    /* We got at least some of the row, so it's only an
+                       exception, not a fatal error.
+                    */
+                    *exceptionP = error;
+                else
+                    *errorP = error;
+                done = TRUE;
+            } else if (eol)
+                done = TRUE;
+            else {
+                curcode = (curcode << 1) | bit;
+                curlen++;
+            
+                if (curlen > 13) {
+                    formatBadCodeException(exceptionP, col, curlen, curcode);
+                    done = TRUE;
+                } else if (curcode != 0) {
+                    te = g3code(curcode, curlen, currentColor);
+                    
+                    if (te) {
+                        processG3Code(te, bitrow, &col, &currentColor, &count);
+                        
+                        curcode = 0;
+                        curlen = 0;
+                    }
+                }
+            }
+        }
+    }
+    if (*exceptionP)
+        skipToNextLine(bitStreamP);
+
+    *lineLengthP = col;
+}
+
+
+
+static void
+freeBits(bit **       const bits,
+         unsigned int const rows,
+         bool         const stretched) {
+
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        if (stretched && row % 2 == 1) {
+            /* This is just a pointer to the previous row; don't want to
+               free it twice.
+            */
+        } else 
+            pbm_freerow(bits[row]);
+    }
+    free(bits);
+}
+
+
+
+static void
+handleRowException(const char * const exception,
+                   const char * const error,
+                   unsigned int const row,
+                   bool         const tolerateErrors) {
+
+
+    if (exception) {
+        if (tolerateErrors)
+            pm_message("Problem reading Row %u.  Skipping rest of row.  %s",
+                       row, exception);
+        else
+            pm_error("Problem reading Row %u.  Aborting.  %s", row, exception);
+        strfree(exception);
+    }
+
+    if (error) {
+        if (tolerateErrors)
+            pm_message("Unable to read Row %u.  Skipping rest of image.  %s",
+                       row, error);
+        else
+            pm_error("Unable to read Row %u.  Aborting.  %s", row, error);
+        strfree(error);
+    }
+}
+
+
+
+typedef struct {
+    unsigned int expectedLineSize;
+        /* The size that lines are supposed to be.  Zero means we're happy
+           with any size.
+        */
+    unsigned int maxLineSize;
+        /* The maximum line size we have seen so far, or zero if we have
+           not seen any lines yet.
+        */
+    bool warned;
+        /* We have warned the user that he has a line length problem */
+    bool tolerateErrors;
+        /* Try to continue when we detect a line size error, as opposed to
+           aborting the program.
+        */
+} lineSizeAnalyzer;
+
+
+
+static void
+initializeLineSizeAnalyzer(lineSizeAnalyzer * const analyzerP,
+                           unsigned int       const expectedLineSize,
+                           bool               const tolerateErrors) {
+
+    analyzerP->expectedLineSize = expectedLineSize;
+    analyzerP->tolerateErrors   = tolerateErrors;
+
+    analyzerP->maxLineSize = 0;
+    analyzerP->warned      = FALSE;
+}
+
+
+
+static void
+analyzeLineSize(lineSizeAnalyzer * const analyzerP,
+                unsigned int       const thisLineSize) {
+
+    const char * error;
+
+    if (analyzerP->expectedLineSize &&
+        thisLineSize != analyzerP->expectedLineSize)
+        asprintfN(&error, "Image contains a line of %u pixels.  "
+                  "You specified lines should be %u pixels.",
+                  thisLineSize, analyzerP->expectedLineSize);
+    else {
+        if (analyzerP->maxLineSize && thisLineSize != analyzerP->maxLineSize)
+            asprintfN(&error, "There are at least two different "
+                      "line lengths in this image, "
+                      "%u pixels and %u pixels.  "
+                      "This is a violation of the G3 standard.  ",
+                      thisLineSize, analyzerP->maxLineSize);
+        else
+            error = NULL;
+    }
+
+    if (error) {
+        if (analyzerP->tolerateErrors) {
+            if (!analyzerP->warned) {
+                pm_message("Warning: %s.", error);
+                analyzerP->warned = TRUE;
+            }
+        } else
+            pm_error("%s", error);
+
+        strfree(error);
+    }
+    analyzerP->maxLineSize = MAX(thisLineSize, analyzerP->maxLineSize);
+}
+
+
+
+/* An empty line means EOF.  An ancient comment in the code said there
+   is supposed to 6 EOL marks in a row to indicate EOF, but the code
+   checked for 3 and considered 2 in row just to mean a zero length
+   line.  Starting in Netpbm 10.24 (August 2004), we assume there is
+   no valid reason to have an empty line and recognize EOF as any
+   empty line.  Alternatively, we could read off and ignore two empty
+   lines without a 3rd.  
+*/
+
+static void
+readFax(struct bitStream * const bitStreamP,
+        bool               const stretch,
+        unsigned int       const expectedLineSize,
+        bool               const tolerateErrors,
+        bit ***            const bitsP,
+        unsigned int *     const colsP,
+        unsigned int *     const rowsP) {
+
+    lineSizeAnalyzer lineSizeAnalyzer;
+    bit ** bits;
+    const char * error;
+    bool eof;
+    unsigned int row;
+    
+    MALLOCARRAY_NOFAIL(bits, MAXROWS);
+
+    initializeLineSizeAnalyzer(&lineSizeAnalyzer,
+                               expectedLineSize, tolerateErrors);
+
+    eof = FALSE;
+    error = NULL;
+    row = 0;
+
+    while (!eof && !error) {
+        unsigned int lineSize;
+
+        if (row >= MAXROWS)
+            asprintfN(&error, "Image is too tall.  This program can "
+                      "handle at most %u rows", MAXROWS);
+        else {
+            const char * exception;
+
+            bits[row] = pbm_allocrow(MAXCOLS);
+            readFaxRow(bitStreamP, bits[row], &lineSize, &exception, &error);
+
+            handleRowException(exception, error, row, tolerateErrors);
+
+            if (!error) {
+                if (lineSize == 0) {
+                    /* EOF.  See explanation above */
+                    eof = TRUE;
+                } else {
+                    analyzeLineSize(&lineSizeAnalyzer, lineSize);
+                    
+                    if (stretch) {
+                        ++row;
+                        if (row >= MAXROWS)
+                            asprintfN(&error, "Image is too tall.  This "
+                                      "program can handle at most %u rows "
+                                      "after stretching", MAXROWS);
+                        else
+                            bits[row] = bits[row-1];
+                    }
+                    ++row;
+                }
+            }
+        }
+    }
+    *rowsP  = row;
+    *colsP  = lineSizeAnalyzer.maxLineSize;
+    *bitsP  = bits;
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    struct bitStream bitStream;
+    unsigned int rows, cols;
+    bit ** bits;
+
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    initBitStream(&bitStream, ifP, cmdline.reversebits);
+
+    if (cmdline.kludge) {
+        /* Skip extra lines to get in sync. */
+        skipToNextLine(&bitStream);
+        skipToNextLine(&bitStream);
+        skipToNextLine(&bitStream);
+    }
+    skipToNextLine(&bitStream);
+
+    buildHashes(&whash, &bhash);
+
+    readFax(&bitStream, cmdline.stretch, cmdline.expectedLineSize,
+            !cmdline.stop_error, 
+            &bits, &cols, &rows);
+
+    pm_close(ifP);
+
+    pbm_writepbm(stdout, bits, cols, rows, 0);
+    pm_close(stdout);
+
+    freeBits(bits, rows, cmdline.stretch);
+
+    return 0;
+}
diff --git a/converter/pbm/icontopbm.c b/converter/pbm/icontopbm.c
new file mode 100644
index 00000000..d6dba8ae
--- /dev/null
+++ b/converter/pbm/icontopbm.c
@@ -0,0 +1,159 @@
+/* icontopbm.c - read a Sun icon file and produce a portable bitmap
+**
+** Copyright (C) 1988 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <string.h>
+
+#include "nstring.h"
+#include "pbm.h"
+
+/* size in bytes of a bitmap */
+#define BitmapSize(width, height) (((((width) + 15) >> 3) &~ 1) * (height))
+
+static void
+ReadIconFile(FILE *                const file, 
+             int *                 const widthP, 
+             int *                 const heightP, 
+             short unsigned int ** const dataP) {
+
+    char variable[80+1];
+    int ch;
+    int status, value, i, data_length, gotsome;
+
+    gotsome = 0;
+    *widthP = *heightP = -1;
+    for ( ; ; )
+    {
+        while ( ( ch = getc( file ) ) == ',' || ch == '\n' || ch == '\t' ||
+                ch == ' ' )
+            ;
+        for ( i = 0;
+              ch != '=' && ch != ',' && ch != '\n' && ch != '\t' && 
+                  ch != ' ' && (i < (sizeof(variable) - 1));
+              i++ )
+        {
+            variable[i] = ch;
+            if ((ch = getc( file )) == EOF)
+                pm_error( "invalid input file -- premature EOF" );
+        }
+        variable[i] = '\0';
+
+        if ( STREQ( variable, "*/" )&& gotsome )
+            break;
+
+        if ( fscanf( file, "%d", &value ) != 1 )
+            continue;
+
+        if ( STREQ( variable, "Width" ) )
+        {
+            *widthP = value;
+            gotsome = 1;
+        }
+        else if ( STREQ( variable, "Height" ) )
+        {
+            *heightP = value;
+            gotsome = 1;
+        }
+        else if ( STREQ( variable, "Depth" )  )
+        {
+            if ( value != 1 )
+                pm_error( "invalid depth" );
+            gotsome = 1;
+        }
+        else if ( STREQ( variable, "Format_version" ) )
+        {
+            if ( value != 1 )
+                pm_error( "invalid Format_version" );
+            gotsome = 1;
+        }
+        else if ( STREQ( variable, "Valid_bits_per_item" ) )
+        {
+            if ( value != 16 )
+                pm_error( "invalid Valid_bits_per_item" );
+            gotsome = 1;
+        }
+    }
+
+    if ( *widthP <= 0 )
+        pm_error( "invalid width (must be positive): %d", *widthP );
+    if ( *heightP <= 0 )
+        pm_error( "invalid height (must be positive): %d", *heightP );
+
+    data_length = BitmapSize( *widthP, *heightP );
+    *dataP = (short unsigned int *) malloc( data_length );
+    if ( *dataP == NULL )
+        pm_error( "out of memory" );
+    data_length /= sizeof( short );
+    
+    for ( i = 0 ; i < data_length; i++ )
+    {
+        if ( i == 0 )
+            status = fscanf( file, " 0x%4hx", *dataP );
+        else
+            status = fscanf( file, ", 0x%4hx", *dataP + i );
+        if ( status != 1 )
+            pm_error( "error 4 scanning bits item" );
+    }
+}
+
+
+
+int
+main(int  argc, char ** argv) {
+
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, row, col, shortcount, mask;
+    short unsigned int * data;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( "[iconfile]" );
+
+    if ( argc == 2 )
+        ifp = pm_openr( argv[1] );
+    else
+        ifp = stdin;
+
+    ReadIconFile( ifp, &cols, &rows, &data );
+
+    pm_close( ifp );
+
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    for ( row = 0; row < rows; row++ )
+    {
+        shortcount = 0;
+        mask = 0x8000;
+        for ( col = 0, bP = bitrow; col < cols; col++, bP++ )
+        {
+            if ( shortcount >= 16 )
+            {
+                data++;
+                shortcount = 0;
+                mask = 0x8000;
+            }
+            *bP = ( *data & mask ) ? PBM_BLACK : PBM_WHITE;
+            shortcount++;
+            mask = mask >> 1;
+        }
+        data++;
+        pbm_writepbmrow( stdout, bitrow, cols, 0 );
+    }
+
+    pm_close( stdout );
+    exit( 0 );
+}
+
diff --git a/converter/pbm/macp.h b/converter/pbm/macp.h
new file mode 100644
index 00000000..26a720a2
--- /dev/null
+++ b/converter/pbm/macp.h
@@ -0,0 +1,12 @@
+/* macp.h - header file for MacPaint files
+*/
+
+#ifndef MACP_H_INCLUDED
+#define MACP_H_INCLUDED
+
+#define	HEADER_LENGTH	512
+#define	MAX_LINES	720
+#define	BYTES_WIDE	72
+#define MAX_COLS	576	/* = BYTES_WIDE * 8 */
+
+#endif
diff --git a/converter/pbm/macptopbm.c b/converter/pbm/macptopbm.c
new file mode 100644
index 00000000..f4a341d3
--- /dev/null
+++ b/converter/pbm/macptopbm.c
@@ -0,0 +1,140 @@
+/* macptopbm.c - read a MacPaint file and produce a portable bitmap
+**
+** Copyright (C) 1988 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "pbm.h"
+#include "macp.h"
+
+static void ReadMacPaintFile ARGS(( FILE* file, int extraskip, int* scanLineP, unsigned char Pic[MAX_LINES][BYTES_WIDE] ));
+
+static unsigned char Pic[MAX_LINES][BYTES_WIDE];
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    int argn, extraskip, scanLine, rows, cols, row, bcol, i;
+    const char* usage = "[-extraskip N] [macpfile]";
+
+
+    pbm_init( &argc, argv );
+
+    argn = 1;
+    extraskip = 0;
+
+    /* Check for flags. */
+    if ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+	{
+	if ( pm_keymatch( argv[argn], "-extraskip", 2 ) )
+	    {
+	    argn++;
+	    if ( argn == argc || sscanf( argv[argn], "%d", &extraskip ) != 1 )
+		pm_usage( usage );
+	    }
+	else
+	    pm_usage( usage );
+	argn++;
+	}
+
+    if ( argn < argc )
+	{
+	ifp = pm_openr( argv[argn] );
+	argn++;
+	}
+    else
+	ifp = stdin;
+
+    if ( argn != argc )
+	pm_usage( usage );
+
+    ReadMacPaintFile( ifp, extraskip, &scanLine, Pic );
+
+    pm_close( ifp );
+
+    cols = BYTES_WIDE * 8;
+    rows = scanLine;
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    for ( row = 0; row < rows; row++ )
+	{
+	for ( bcol = 0; bcol < BYTES_WIDE; bcol++ )
+	    for ( i = 0; i < 8; i++ )
+		bitrow[bcol * 8 + i] =
+		    ( (Pic[row][bcol] >> (7 - i)) & 1 ) ? PBM_BLACK : PBM_WHITE;
+	pbm_writepbmrow( stdout, bitrow, cols, 0 );
+	}
+
+    pm_close( stdout );
+    exit( 0 );
+    }
+
+/*
+** Some of the following routine is:
+**
+**                Copyright 1987 by Patrick J. Naughton
+**                         All Rights Reserved
+** 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.
+*/
+
+static void
+ReadMacPaintFile( file, extraskip, scanLineP, Pic )
+    FILE* file;
+    int extraskip;
+    int* scanLineP;
+    unsigned char Pic[MAX_LINES][BYTES_WIDE];
+    {
+    unsigned int i, j, k;
+    unsigned char ch;
+
+    /* Skip over the header. */
+    for ( i = 0; i < extraskip; i++ )
+	getc( file );
+    for ( i = 0; i < HEADER_LENGTH; i++ )
+	getc( file );
+
+    *scanLineP = 0;
+    k = 0;
+
+    while ( *scanLineP < MAX_LINES )
+	{
+	ch = (unsigned char) getc( file );	/* Count byte */
+	i = (unsigned int) ch;
+	if ( ch < 0x80 )
+	    {	/* Unpack next (I+1) chars as is */
+	    for ( j = 0; j <= i; j++ )
+		if ( *scanLineP < MAX_LINES )
+		    {
+		    Pic[*scanLineP][k++] = (unsigned char) getc( file );
+		    if ( ! (k %= BYTES_WIDE) )
+			*scanLineP += 1;
+		    }
+	    }
+	else
+	    {	/* Repeat next char (2's comp I) times */
+	    ch = getc( file );
+	    for ( j = 0; j <= 256 - i; j++ )
+		if ( *scanLineP < MAX_LINES )
+		    {
+		    Pic[*scanLineP][k++] = (unsigned char) ch;
+		    if ( ! (k %= BYTES_WIDE) )
+			*scanLineP += 1;
+		    }
+	    }
+	}
+    }
diff --git a/converter/pbm/mdaspec.txt b/converter/pbm/mdaspec.txt
new file mode 100644
index 00000000..a93c0af7
--- /dev/null
+++ b/converter/pbm/mdaspec.txt
@@ -0,0 +1,239 @@
+
+         MicroDesign 3 page (.MDP) and area (.MDA) file specifications
+                                       
+   This publication and all information contained herein is Copyright ©
+   Creative Technology 1992. This information carries no guarantees of
+   accuracy or liabilities on the part of either Creative Technology or
+   myself.
+   
+   A hard copy of this document (with far better artwork) is available
+   from Creative Technology, 10 Park Street, Uttoxeter, Staffs ST14 7AG,
+   creative@net-shopper.co.uk.
+   
+   [Note: throughout this file hexadecimal numbers are represented as
+   #nn; 'one pixel' refers to one PCW screen pixel, ie. a MicroDesign
+   half-pixel; 'word' format indicates a 16-bit word stored with the LSBs
+   first]
+   
+The MDA (MicroDesign Area) file formats
+
+   There are two different MDA file formats. The earlier MicroDesign2
+   format uses a simple compression technique to reduce continuous areas
+   of white and black, while the more recent MicroDesign3 format uses a
+   more sophisticated technique which generally results in smaller disc
+   files.
+   
+   MicroDesign2, ProSCAN and Tweak (versions 1 and 2) can only load and
+   save the earlier format, but either format may be loaded or saved in
+   MicroDesign3. In MD3, the filetype is detected automatically on load,
+   but the user must choose whether to save in 'AREA2' or 'AREA3' format.
+   
+   The format is identified in byte 21 of the initial file 'stamp' record
+   - for a MicroDesign2 area this byte is "0" (#30), whereas for a
+   MicroDesign3 area it is "3" (#33).
+   
+   When loaded into memory and uncompressed an Area file can occupy up
+   the 720k of data, but its size on disc is indeterminate due to the
+   compression used. Because of the compression it is not possible to
+   perform 'random-access' reads or writes to the disc file - it must be
+   read sequentially in order correctly to decompress the data.
+   
+   The older MicroDesign2 Area file format is as follows:
+   
+Bytes 0..127: file 'stamp':
+    0 -   3 (#00 - #03)  .MDA            File Type  (4 bytes)
+    4 -  17 (#04 - #11)  MicroDesignPCW  Program Identifier (14 bytes)
+   18 -  22 (#12 - #16)  v1.00           File Version  (5 bytes)
+   23 -  24 (#17 - #18)  CR,LF           ie. 13,10 decimal (2 bytes)
+   25 -  31 (#19 - #1F)  xxxxxxx         User Serial No (ASCII) (7 bytes)
+   32 -  33 (#20 - #21)  CR,LF           ie. 13,10 decimal (2 bytes)
+   34 - 127 (#22 - #7F)                  fill with zeroes (#00) (94 bytes)
+
+Bytes 128..: file proper:
+  128 - 129 (#80 - #81)  Height in Lines (multiple of 4) (word)
+  130 - 131 (#82 - #83)  Width in Bytes (Pixels * 8) (word)
+  132 -     (#84 -    )  Bit-Image Data as follows...
+
+   Bytes read from left to right in lines, top line first.
+   
+   Each byte is standard 1-bit-per-pixel layout where MSB = LH pixel, LSB
+   = RH pixel, 1 = white ('on'), and 0 = black ('off'). Each #00 (all
+   black) or #FF (all white) byte is followed by a 'count' byte (ie. #00
+   #03 means 3 whole bytes width of solid black; #FF #A0 means 160 bytes
+   width of solid white). A value #00 for the count byte means 256. This
+   'count' can overrun into the next (several) lines.
+   
+   For example: (. represents black, # white)
+   
+    ....#### ##..##.. ####.... ........ ..###### ######## ########
+       0F       CC       F0      00,01     3F          FF,02
+
+    ####.... ........ ........ ........ ........ ........ ........
+       F0                            00,06
+
+   Because of this compression the file length is indeterminate, but
+   there must be HEIGHT * WIDTH bytes of actual image data by the time it
+   has been uncompressed.
+   
+   The newer MicroDesign3 Area file format is as follows:
+   
+Bytes 0..127: file 'stamp':
+    0 -   3 (#00 - #03)  .MDA            File Type  (4 bytes)
+    4 -  17 (#04 - #11)  MicroDesignPCW  Program Identifier (14 bytes)
+   18 -  22 (#12 - #16)  v1.30           File Version  (5 bytes)
+   23 -  24 (#17 - #18)  CR,LF           ie. 13,10 decimal (2 bytes)
+   25 -  31 (#19 - #1F)  xxxxxxx         User Serial No (ASCII) (7 bytes)
+   32 -  33 (#20 - #21)  CR,LF           ie. 13,10 decimal (2 bytes)
+   34 - 127 (#22 - #7F)                  fill with zeroes (#00) (94 bytes)
+
+Bytes 128..: file proper:
+  128 - 129 (#80 - #81)  Height in Lines (multiple of 4) (word)
+  130 - 131 (#82 - #83)  Width in Bytes (Pixels * 8) (word)
+  132 -     (#84 -    )  Bit-Image Data as follows...
+
+   Bytes read from left to right in lines, top line first.
+   
+   Each byte is standard 1-bit-per-pixel layout where MSB = LH pixel, LSB
+   = RH pixel, 1 = white ('on'), and 0 = black ('off').
+   
+   Each line of data is compressed according to one of three 'line
+   types'. The first byte of data for each line is the line type.
+   
+Line type byte:
+  #00: Line is ALL-SAME-BYTE type
+  #01: Line is DATA type
+  #02: Line is DIFFERENCE DATA type
+
+   The actual data for each line follows this type byte:
+   
+   ALL-SAME-BYTE type:
+          One more byte follows the initial #00 type byte - this is the
+          actual bit-image data with which to fill the whole line width.
+          
+          eg. Whole line of white = #00 #FF, whole line of black = #00
+          #00
+          
+   DATA type:
+          Following the initial #01 type byte, the data content of the
+          line is compressed as follows:
+          
+          Data is encoded in 'blocks', which are of *either* repeating
+          data bytes *or* non-repeating bytes. Each block starts with a
+          control byte, which determines whether the data which follows
+          it is *either* just one data byte to be repeated *or* a
+          sequence of dissimilar bytes.
+          
+          If the control byte N is negative (-1 to -127 in two's
+          complement - #FF for -1, #81 for -127), *one* data byte follows
+          which is to be repeated -N times. This means that there are to
+          be a total of (-N+1) occurrences of this data byte.
+          
+          If the control byte N is positive (0 to 127; #00 to #7F), it is
+          followed by (N+1) bytes of dissimilar data to load directly
+          into the line.
+          
+          For instance: 01 (line type), then:
+          
+....#### ##..##.. ####.... ........ ######## ######## ########
+          03,0F,CC,F0,00                       FE,FF
+
+#####... #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#. #.#..... ........
+  00,F8                 FD,AA                     01,A0,00
+
+          Note: the POSITIVE control bytes are 1 LESS than the number of
+          dissimilar bytes following them; NEGATIVE ones are (MINUS) 1
+          LESS than the number of occurrences of the byte following them.
+          
+   DIFFERENCE DATA type:
+          Following the initial #02 type byte, the difference between the
+          data content of this line and the content of the previous line
+          is stored: ie. this line is XORed with the previous line to
+          produce a 'difference' line, which is then compressed using the
+          same method as for a DATA type line.
+          
+          For instance, the following line stored as a 'difference' line
+          from the line above would be 02 (line type), then: (the second
+          pixel row below shows the results of XORing the first two
+          lines)
+          
+....#### ##..##.. ##..##.. ......## ######## ######## ########
+........ ........ ..####.. ......## ........ ........ ........
+      FF,00           01,3C,03                 FE,00
+
+######.. #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#. #.#.#.#. ........
+.....#.. ........ ........ ........ ........ ....#.#. ........
+ 00,04                  FD,00                     01,0A,00
+
+          Note: this DIFFERENCE encoding is used if it will result in
+          less bytes of data being stored in the file. The line type of
+          the previous line is irrelevant.
+          
+   Because of these various types of compression the file length on the
+   disc is indeterminate, but there must be HEIGHT * WIDTH bytes of
+   actual image data by the time it has been uncompressed.
+   
+The MDP (MicroDesign Page) file formats
+
+   MicroDesign3's Page (.MDP) files are identical to its Area (.MDA)
+   files except for the following differences:
+   
+Bytes 0..127: file 'stamp':
+    0 -   3 (#00 - #03)  .MDP            File Type  (4 bytes)
+   34       (#22)        nn: dpi         00 = 240dpi
+                                         01 = 360dpi
+                                         02 = 300dpi
+   35       (#23)        nn: format      00 = A5 portrait
+                                         01 = A5 landscape
+                                         02 = A4 portrait
+                                         03 = A4 landscape
+                                         04 = A5 portrait (hi-res)
+                                         05 = A5 landscape (hi-res)
+   36       (#24)        nn: page ram required in 16k blocks
+
+   In all other respects a Page (.MDP) file is identical to an Area
+   (.MDA) file (MD3 type).
+   
+The CUT image format
+
+   (Note: this information is in no way 'official' and results from my
+   own dabblings; it is separate from the above information on the MDA
+   format and is included here for convenience. No warranty expressed or
+   implied)
+   
+   CUT files have a format as follows:
+   
+    0 -   1 (#00 - #01)  Height code h1 (word) (see below)
+    2 -   3 (#02 - #03)  Width code w1 (word) (see below)
+    4 -     (#04 -    )  Bitmap data, row-by-row
+
+   The height in pixels h1 can be calculated from the height code h using
+   h = (h1+3)/2; the width in pixels is w = w1+2; the number of whole
+   bytes per row in the file is wb = INT((w+8)/8). Bitmap data in the
+   file is stored row-by-row, with a whole number of bytes per row; the
+   LSBs of the last byte that extend beyond the right edge of the image
+   should be discarded. As usual in bitmap data the MSB is towards the
+   left and the LSB towards the right.
+   
+   Note: the formula above for wb implies that if the image is a whole
+   multiple of 8 pixels wide, none of the bits from the last byte in the
+   row will be used. Strange, but there you go.
+   
+The GRF image format
+
+   (Same disclaimer applies; this is all from experiment)
+   
+   GRF files are substantially similar to CUTs (although slightly more
+   logical) and have a format as follows:
+   
+    0 -   1 (#00 - #01)  Width in pixels (word)
+    2 -   3 (#02 - #03)  Height in pixels (word)
+    4 -     (#04 -    )  Bitmap data, row-by-row
+
+   The number of bytes per row is simply the width in pixels divided by
+   8, rounded up; no catches here. As with CUTs unused rightmost bits
+   should be discarded.
+     _________________________________________________________________
+                                      
+   
+    Go to CP/M page or main page
+    Last updated 16 April 1997; Jacob Nevins
diff --git a/converter/pbm/mdatopbm.c b/converter/pbm/mdatopbm.c
new file mode 100644
index 00000000..d8e06572
--- /dev/null
+++ b/converter/pbm/mdatopbm.c
@@ -0,0 +1,271 @@
+
+/***************************************************************************
+
+    MDATOPBM: Convert Microdesign area to portable bitmap
+    Copyright (C) 1999 John Elliott <jce@seasip.demon.co.uk>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    See the file mdaspec.txt for a specification of the MDA format.
+******************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include "pbm.h"
+#include "mallocvar.h"
+
+/* Simple MDA -> portable bitmap converter */
+
+typedef unsigned char mdbyte;   /* Must be exactly one byte */
+
+static FILE *infile;            /* Input file */
+static mdbyte header[128];      /* MDA file header */
+static bit **data;          /* PBM image */
+static mdbyte *mdrow;           /* MDA row after decompression (MD3 only) */
+static int bInvert = 0;     /* Invert image? */
+static int bScale  = 0;     /* Scale image? */
+static int bAscii  = 0;     /* Ouput ASCII PBM? */
+static int nInRows, nInCols;        /* Height, width of input (rows x bytes) */
+static int nOutCols, nOutRows;      /* Height, width of output (rows x bytes) */
+
+static mdbyte 
+getbyte(void) {
+    /* Read a byte from the input stream, with error trapping */
+    int b;
+
+    b = fgetc(infile);
+
+    if (b == EOF) pm_error("Unexpected end of MDA file\n");
+    
+    return (mdbyte)b;
+}
+
+
+
+static void 
+render_byte(int *col, int *xp, int *yp, int b) {
+
+/* Convert a byte to 8 cells in the destination bitmap 
+ *
+ * *col = source column
+ * *xp  = destination column
+ * *yp  = destination row
+ *  b   = byte to draw
+ *
+ * Will update *col, *xp and *yp to point to the next bit of the row.
+ */
+
+    int mask = 0x80;
+    int n;
+    int y3 = *yp;
+
+    if (bScale) y3 *= 2;
+
+    if (y3 >= nOutRows) return;
+
+    for (n = 0; n < 8; ++n) {
+        if (bInvert) data[y3][*xp] = (b & mask) ? PBM_BLACK : PBM_WHITE;
+        else         data[y3][*xp] = (b & mask) ? PBM_WHITE : PBM_BLACK;
+        mask = mask >> 1;
+        if (bScale) data[y3+1][*xp] = data[y3][*xp];
+        ++(*xp);
+    }
+    ++(*col);       /* Next byte */
+    if ((*col) >= nInCols) {
+        /* Onto next line? */
+        *col = 0;
+        *xp = 0;
+        ++(*yp);
+    }
+}
+
+
+static void 
+md2_trans(void) {
+    /* Convert a MicroDesign 2 area to PBM */
+    /* MD2 has RLE encoding that may go over */
+
+    int x1, y1, col;    /* multiple lines. */
+    mdbyte b;
+    int c;
+
+    x1 = y1 = col = 0;
+
+    while (y1 < nInRows) {
+        b = getbyte();
+    
+        if (b == 0 || b == 0xFF) {
+            /* RLE sequence */
+            c = getbyte();
+            if (c == 0) c = 256;
+            while (c > 0) { 
+                render_byte(&col, &x1, &y1, b); 
+                --c; 
+            }
+        }
+        else 
+            render_byte(&col, &x1, &y1, b);    /* Not RLE */
+    }
+}
+
+
+
+static void 
+md3_trans(void) {
+    /* Convert MD3 file. MD3 are encoded as rows, and 
+       there are three types. 
+    */
+    int x1, y1, col;
+    mdbyte b;
+    int c, d, n;
+
+    for (y1 = 0; y1 < nInRows; ++y1) {
+        b = getbyte();   /* Row type */
+        switch(b)  {
+        case 0: /* All the same byte */
+            c = getbyte();
+            for (n = 0; n < nInCols; n++) 
+                mdrow[n] = c;
+            break;
+            
+        case 1:      /* Encoded data */
+        case 2: col = 0; /* Encoded as XOR with previous row */
+            while (col < nInCols) {
+                c = getbyte();
+                if (c >= 129) {
+                    /* RLE sequence */
+                    c = 257 - c;
+                    d = getbyte();
+                    for (n = 0; n < c; ++n) {
+                        if (b == 1) 
+                            mdrow[col++] = d;
+                        else 
+                            mdrow[col++] ^= d;
+                    }   
+                } else {
+                    /* not RLE sequence */
+                        ++c;
+                        for (n = 0; n < c; ++n) {
+                            d = getbyte();
+                            if (b == 1) 
+                                mdrow[col++] = d;
+                            else
+                                mdrow[col++] ^= d;
+                        }
+                } 
+            }
+        }
+        /* Row loaded. Convert it. */
+        x1 = 0; col = 0; 
+        for (n = 0; n < nInCols; ++n) {
+            d  = y1;
+            render_byte(&col, &x1, &d, mdrow[n]);
+        }
+    }
+}
+
+
+
+static void 
+usage(char *s) {        
+    printf("mdatopbm v1.00, Copyright (C) 1999 "
+           "John Elliott <jce@seasip.demon.co.uk>\n"
+           "This program is redistributable under the terms of "
+           "the GNU General Public\n"
+           "License, version 2 or later.\n\n"
+           "Usage: %s [ -a ] [ -d ] [ -i ] [ -- ] [ infile ]\n\n"
+           "-a: Output an ASCII pbm file\n"
+           "-d: Double height (to compensate for the PCW aspect ratio)\n"
+           "-i: Invert colors\n"
+           "--: No more options (use if filename begins with a dash)\n",
+           s);
+
+    exit(0);
+}
+
+
+
+int 
+main(int argc, char **argv) {
+    int n, optstop = 0;
+    char *fname = NULL;
+
+    pbm_init(&argc, argv);
+
+    /* Parse options */
+
+    for (n = 1; n < argc; ++n) {
+        if (argv[n][0] == '-' && !optstop) {   
+            if (argv[n][1] == 'a' || argv[n][1] == 'A') bAscii = 1;
+            if (argv[n][1] == 'd' || argv[n][1] == 'D') bScale = 1;
+            if (argv[n][1] == 'i' || argv[n][1] == 'I') bInvert = 1;
+            if (argv[n][1] == 'h' || argv[n][1] == 'H') usage(argv[0]);
+            if (argv[n][1] == '-' && argv[n][2] == 0 && !fname) {
+                /* "--" */
+                optstop = 1;
+            }
+            if (argv[n][1] == '-' && (argv[n][2] == 'h' || argv[n][2] == 'H'))
+                usage(argv[0]);
+        }
+        else if (argv[n][0] && !fname) {
+            /* Filename */
+            fname = argv[n];
+        }
+    }
+
+    if (fname) 
+        infile = pm_openr(fname);
+    else
+        infile = stdin;
+
+    /* Read MDA file header */
+
+    if (fread(header, 1, 128, infile) < 128)
+        pm_error("Not a .MDA file\n");
+
+    if (strncmp((char*) header, ".MDA", 4) && 
+        strncmp((char*) header, ".MDP", 4))
+        pm_error("Not a .MDA file\n");
+
+    {
+        short yy;
+        pm_readlittleshort(infile, &yy); nInRows = yy;
+        pm_readlittleshort(infile, &yy); nInCols = yy;
+    }
+    
+    nOutCols = 8 * nInCols;
+    nOutRows = nInRows;
+    if (bScale) 
+        nOutRows *= 2;
+
+    data = pbm_allocarray(nOutCols, nOutRows);
+    
+    MALLOCARRAY_NOFAIL(mdrow, nInCols);
+
+    if (header[21] == '0') 
+        md2_trans();
+    else
+        md3_trans();
+
+    pbm_writepbm(stdout, data, nInCols*8, nOutRows, bAscii);
+
+    if (infile != stdin) 
+        pm_close(infile);
+    fflush(stdout);
+    pbm_freearray(data, nOutRows);
+    free(mdrow);
+
+    return 0;
+}
diff --git a/converter/pbm/mgr.h b/converter/pbm/mgr.h
new file mode 100644
index 00000000..eeb39d4f
--- /dev/null
+++ b/converter/pbm/mgr.h
@@ -0,0 +1,25 @@
+/* mgr.h - the following defs are taken from the MGR header file lib/dump.h
+*/
+
+#ifndef MGR_H_INCLUDED
+#define MGR_H_INCLUDED
+
+struct old_b_header {
+   char magic[2];
+   char h_wide;
+   char l_wide;
+   char h_high;
+   char l_high;
+   };
+
+struct b_header {
+   char magic[2];
+   char h_wide;
+   char l_wide;
+   char h_high;
+   char l_high;
+   char depth;
+   char _reserved;
+   };
+
+#endif
diff --git a/converter/pbm/mgrtopbm.c b/converter/pbm/mgrtopbm.c
new file mode 100644
index 00000000..cea4be48
--- /dev/null
+++ b/converter/pbm/mgrtopbm.c
@@ -0,0 +1,145 @@
+/* mgrtopbm.c - read a MGR bitmap and produce a portable bitmap
+**
+** Copyright (C) 1989 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <string.h>
+#include <errno.h>
+
+#include "pbm.h"
+#include "mgr.h"
+
+
+static unsigned char item;
+static int bitsperitem, bitshift;
+
+static void
+getinit(FILE * const file, 
+        int *  const colsP, 
+        int *  const rowsP, 
+        int *  const depthP, 
+        int *  const padrightP,
+        int *  const bitsperitemP) {
+
+    struct b_header head;
+    int pad;
+
+    if (fread(&head, sizeof(struct old_b_header), 1, file ) != 1)
+        pm_error("Unable to read 1st byte of file.  "
+                 "fread() returns errno %d (%s)",
+                 errno, strerror(errno));
+    if (head.magic[0] == 'y' && head.magic[1] == 'z') { 
+        /* new style bitmap */
+        if (fread(&head.depth, 
+                  sizeof(head) - sizeof(struct old_b_header), 1, file) 
+             != 1 )
+            pm_error("Unable to read header after 1st byte.  "
+                     "fread() returns errno %d (%s)",
+                     errno, strerror(errno));
+        *depthP = (int) head.depth - ' ';
+        pad = 8;
+    } else if (head.magic[0] == 'x' && head.magic[1] == 'z') { 
+        /* old style bitmap with 32-bit padding */
+        *depthP = 1;
+        pad = 32;
+    } else if (head.magic[0] == 'z' && head.magic[1] == 'z') { 
+        /* old style bitmap with 16-bit padding */
+        *depthP = 1;
+        pad = 16;
+    } else if (head.magic[0] == 'z' && head.magic[1] == 'y') {
+        /* old style 8-bit pixmap with 16-bit padding */
+        *depthP = 8;
+        pad = 16;
+    } else {
+        pm_error("bad magic chars in MGR file: '%c%c'",
+                 head.magic[0], head.magic[1] );
+        pad = -1;  /* should never reach here */
+    }
+
+    if (head.h_wide < ' ' || head.l_wide < ' ')
+        pm_error("Invalid width field in MGR header");
+    if (head.h_high < ' ' || head.l_high < ' ')
+        pm_error("Invalid width field in MGR header");
+    
+    *colsP = (((int)head.h_wide - ' ') << 6) + ((int)head.l_wide - ' ');
+    *rowsP = (((int)head.h_high - ' ') << 6) + ((int) head.l_high - ' ');
+    *padrightP = ( ( *colsP + pad - 1 ) / pad ) * pad - *colsP;
+    
+    *bitsperitemP = 8;
+}
+
+
+
+static bit
+getbit( file )
+    FILE* file;
+    {
+    bit b;
+
+    if ( bitsperitem == 8 )
+	{
+	item = getc( file );
+	bitsperitem = 0;
+	bitshift = 7;
+	}
+    bitsperitem++;
+    b = ( ( item >> bitshift) & 1 ) ? PBM_BLACK : PBM_WHITE;
+    bitshift--;
+    return b;
+    }
+
+
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, depth, padright, row, col;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[mgrfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    getinit( ifp, &cols, &rows, &depth, &padright, &bitsperitem );
+    if ( depth != 1 )
+	pm_error( "MGR file has depth of %d, must be 1", depth );
+
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    for ( row = 0; row < rows; row++ )
+	{
+	/* Get data, bit-reversed within each byte. */
+        for ( col = 0, bP = bitrow; col < cols; col++, bP++ )
+	    *bP = getbit( ifp );
+	/* Discard line padding */
+        for ( col = 0; col < padright; col ++ )
+	    (void) getbit( ifp );
+	pbm_writepbmrow( stdout, bitrow, cols, 0 );
+	}
+
+    pm_close( ifp );
+    pm_close( stdout );
+
+    exit( 0 );
+    }
+
+
diff --git a/converter/pbm/mrftopbm.c b/converter/pbm/mrftopbm.c
new file mode 100644
index 00000000..696fe839
--- /dev/null
+++ b/converter/pbm/mrftopbm.c
@@ -0,0 +1,210 @@
+/* mrftopbm - convert mrf to pbm
+ * public domain by RJM
+ *
+ * Adapted to Netpbm by Bryan Henderson 2003.08.09.  Bryan got his copy from
+ * ftp://ibiblio.org/pub/linux/apps/convert, dated 1997.08.19.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "pm_c_util.h"
+#include "nstring.h"
+#include "pbm.h"
+
+
+static int bitbox;
+static int bitsleft;
+
+
+static void 
+bit_init(void) {
+    bitbox=0; 
+    bitsleft=0;
+}
+
+
+
+static int 
+bit_input(FILE * const in) {
+    if (bitsleft == 0)   {
+        bitbox = fgetc(in);
+        bitsleft = 8;
+    }
+    --bitsleft;
+    return((bitbox&(1<<bitsleft))?1:0);
+}
+
+
+
+static void 
+doSquare(FILE *          const in,
+          unsigned char * const image,
+          int             const ox,
+          int             const oy,
+          int             const w,
+          int             const size) {
+
+    if (size == 1 || bit_input(in)) { 
+        /* It's all black or all white.  Next bit says which. */
+
+        unsigned int const c = bit_input(in);
+
+        unsigned int y;
+
+        for (y = 0; y < size; ++y) {
+            unsigned int x;
+            for (x = 0; x < size; ++x)
+                image[(oy+y)*w+ox+x] = c;
+        }
+    } else {
+        /* not all one color, so recurse. */
+        doSquare(in, image, ox,      oy,     w, size >> 1);
+        doSquare(in, image, ox+size, oy,     w, size >> 1);
+        doSquare(in, image, ox,      oy+size,w, size >> 1);
+        doSquare(in, image, ox+size, oy+size,w, size >> 1);
+    }
+}
+
+
+
+static void
+writeOutput(FILE *                const ofP,
+            int                   const cols,
+            int                   const rows,
+            const unsigned char * const image) {
+            
+    /* w64 is units-of-64-bits width, h64 same for height */
+    unsigned int const w64 = (cols+63)/64;
+
+    bit * bitrow;
+    unsigned int row;
+
+    pbm_writepbminit(ofP, cols, rows, FALSE);
+
+    bitrow = pbm_allocrow(cols);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+     
+        for (col = 0; col < cols; ++col)
+            bitrow[col] = 
+                (image[row * (w64*64) + col] == 1) ? PBM_WHITE : PBM_BLACK;
+
+        pbm_writepbmrow(ofP, bitrow, cols, FALSE);
+    }
+    pbm_freerow(bitrow);
+}
+
+
+
+static void
+readMrfImage(FILE *           const ifP,
+             bool             const expandAll,
+             unsigned char ** const imageP,
+             unsigned int *   const colsP,
+             unsigned int *   const rowsP) {
+
+    static unsigned char buf[128];
+    unsigned int rows;
+    unsigned int cols;
+    unsigned int w64, h64;
+
+    unsigned char * image;
+
+    fread(buf, 1, 13, ifP);
+
+    if (memcmp(buf, "MRF1", 4) != 0)
+        pm_error("Input is not an mrf image.  "
+                 "We know this because it does not start with 'MRF1'.");
+
+    if (buf[12] != 0)
+        pm_error("can't handle file subtype %u", buf[12]);
+
+    cols = (buf[4] << 24) | (buf[5] << 16) | (buf[06] << 8) | buf[07] << 0;
+    rows = (buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11] << 0;
+
+    /* w64 is units-of-64-bits width, h64 same for height */
+    w64 = (cols+63)/64;
+    h64 = (rows+63)/64;
+    if (expandAll) {
+        *colsP = w64*64;
+        *rowsP = h64*64;
+    } else {
+        *colsP = cols;
+        *rowsP = rows;
+    }
+
+    if (UINT_MAX/w64/64/h64/64 == 0)
+        pm_error("Ridiculously large, unprocessable image: %u cols x %u rows",
+                 cols, rows);
+
+    image = calloc(w64*h64*64*64,1);
+    if (image == NULL)
+        pm_error("Unable to get memory for raster");
+                 
+    /* now recursively input squares. */
+
+    bit_init();
+
+    {
+        unsigned int row;
+        for (row = 0; row < h64; ++row) {
+            unsigned int col;
+            for (col = 0; col < w64; ++col)
+                doSquare(ifP, image, col*64, row*64, w64*64, 64);
+        }
+    }
+    *imageP = image;
+}
+
+
+
+int 
+main(int argc, char *argv[]) {
+
+    FILE *ifP;
+    FILE *ofP;
+    unsigned char *image;
+    bool expandAll;
+    unsigned int cols, rows;
+
+    pbm_init(&argc, argv);
+
+    expandAll = FALSE;  /* initial assumption */
+
+    if (argc-1 >= 1 && STREQ(argv[1], "-a")) {
+        expandAll = TRUE;
+        argc--,argv++;
+    }
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments: %d.  Only argument is input file",
+                 argc-1);
+
+    if (argc-1 == 1) 
+        ifP = pm_openr(argv[1]);
+    else
+        ifP = stdin;
+
+    ofP = stdout;
+
+    readMrfImage(ifP, expandAll, &image, &cols, &rows);
+    
+    pm_close(ifP);
+    
+    writeOutput(ofP, cols, rows, image);
+
+    free(image);
+
+    return 0;
+}
+
+
+
+
+
+
diff --git a/converter/pbm/pbmto10x.c b/converter/pbm/pbmto10x.c
new file mode 100644
index 00000000..f8a38b5d
--- /dev/null
+++ b/converter/pbm/pbmto10x.c
@@ -0,0 +1,169 @@
+/* pbmto10x.c - read a portable bitmap and produce a Gemini 10X printer file
+**
+** Copyright (C) 1990, 1994 by Ken Yap
+**
+** 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.
+**
+** Modified to shorten stripes and eliminate blank stripes. Dec 1994.
+*/
+
+#include "pbm.h"
+#include "mallocvar.h"
+
+#define LOW_RES_ROWS    8       /* printed per pass */
+#define HIGH_RES_ROWS   16      /* printed per pass */
+
+static int  highres = 0;
+static FILE *ifp;
+static int  rows, cols, format;
+
+
+
+static void
+outstripe(char * const stripe, 
+          char * const sP, 
+          int    const reschar) {
+
+    int ncols;
+
+    char * p;
+
+    p = sP;  /* initial value */
+
+    /* scan backwards, removing empty columns */
+    while (p != stripe) 
+        if (*--p != 0) {
+            ++p;
+            break;
+        }
+    ncols = p - stripe;
+    if (ncols > 0) {
+        printf("\033%c%c%c", reschar, ncols % 256, ncols / 256);
+        fwrite(stripe, sizeof(char), ncols, stdout);
+    }
+    putchar('\n');          /* flush buffer */
+}
+
+
+
+static void
+res_60x72(void) {
+    int i, item, npins, row, col;
+    bit *bitrows[LOW_RES_ROWS], *bP[LOW_RES_ROWS];
+    char *stripe, *sP;
+
+    MALLOCARRAY(stripe, cols);
+    if (stripe == NULL)
+        pm_error("Unable to allocate %u bytes for a stripe buffer.",
+                 cols * sizeof(stripe[0]));
+    for (i = 0; i < LOW_RES_ROWS; ++i)
+        bitrows[i] = pbm_allocrow(cols);
+    printf("\033A\010");        /* '\n' = 8/72 */
+    for (row = 0, sP = stripe; row < rows; row += LOW_RES_ROWS, sP = stripe) {
+        if (row + LOW_RES_ROWS <= rows)
+            npins = LOW_RES_ROWS;
+        else
+            npins = rows - row;
+        for (i = 0; i < npins; ++i)
+            pbm_readpbmrow(ifp, bP[i] = bitrows[i], cols, format);
+        for (col = 0; col < cols; ++col) {
+            item = 0;
+            for (i = 0; i < npins; ++i)
+                if (*(bP[i]++) == PBM_BLACK)
+                    item |= 1 << (7 - i);
+            *sP++ = item;
+        }
+        outstripe(stripe, sP, 'K');
+    }
+    printf("\033@");
+    free(stripe);
+}
+
+
+
+static void
+res_120x144(void) {
+    int i, pin, item, npins, row, col;
+    bit *bitrows[HIGH_RES_ROWS], *bP[HIGH_RES_ROWS];
+    char *stripe, *sP;
+
+    MALLOCARRAY(stripe, cols);
+    if (stripe == NULL)
+        pm_error("Unable to allocate %u bytes for a stripe buffer.",
+                 cols * sizeof(stripe[0]));
+    for (i = 0; i < HIGH_RES_ROWS; ++i)
+        bitrows[i] = pbm_allocrow(cols);
+    printf("\0333\001");            /* \n = 1/144" */
+    for (row = 0, sP = stripe; row < rows; row += HIGH_RES_ROWS, sP = stripe) {
+        if (row + HIGH_RES_ROWS <= rows)
+            npins = HIGH_RES_ROWS;
+        else
+            npins = rows - row;
+        for (i = 0; i < npins; ++i)
+            pbm_readpbmrow(ifp, bP[i] = bitrows[i], cols, format);
+        for (col = 0; col < cols; ++col) {
+            item = 0;
+            /* even rows */
+            for (pin = i = 0; i < npins; i += 2, ++pin)
+                if (*(bP[i]++) == PBM_BLACK)
+                    item |= 1 << (7 - pin);
+            *sP++ = item;
+        }
+        outstripe(stripe, sP, 'L');
+        sP = stripe;
+        for (col = 0; col < cols; ++col) {
+            item = 0;
+            /* odd rows */
+            for (i = 1, pin = 0; i < npins; i += 2, ++pin)
+                if (*(bP[i]++) == PBM_BLACK)
+                    item |= 1 << (7 - pin);
+            *sP++ = item;
+        }
+        outstripe(stripe, sP, 'L');
+        printf("\033J\016");        /* 14/144 down, \n did 1/144 */
+    }
+    printf("\033@");
+    free(stripe);
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    const char * fname;
+
+    pbm_init( &argc, argv );
+
+    if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'h') {
+        highres = 1;
+        --argc;
+        ++argv;
+    }
+    if (argc-1 > 1)
+        pm_error("Too many arguments.  Only argument is file name");
+    else if (argc-1 == 1)
+        fname = argv[1];
+    else
+        fname = "-";
+    
+    ifp = pm_openr(fname);
+
+    pbm_readpbminit(ifp, &cols, &rows, &format);
+
+    if (highres)
+        res_120x144();
+    else
+        res_60x72();
+
+    pm_close(ifp);
+    exit(0);
+}
+
+
+
diff --git a/converter/pbm/pbmto4425.c b/converter/pbm/pbmto4425.c
new file mode 100644
index 00000000..605b12d5
--- /dev/null
+++ b/converter/pbm/pbmto4425.c
@@ -0,0 +1,179 @@
+#include <string.h>
+
+#include "nstring.h"
+#include "pbm.h"
+
+static char bit_table[2][3] = {
+{1, 4, 0x10},
+{2, 8, 0x40}
+};
+
+static int vmap_width;
+static int vmap_height;
+
+static int xres;
+static int yres;
+
+static char *vmap;
+
+
+static void
+init_map()
+{
+    int x, y;
+
+
+    for(x = 0; x < vmap_width; ++x)
+    {
+        for(y = 0; y < vmap_height; ++y)
+        {
+            vmap[y*(vmap_width) + x] = 0x20;
+        }
+    }
+}
+
+
+
+static void
+set_vmap(x, y)
+    int x, y;
+{
+    int ix, iy;
+
+    ix = x/2;
+    iy = y/3;
+
+    vmap[iy*(vmap_width) + ix] |= bit_table[x % 2][y % 3];
+}
+
+
+
+static void
+fill_map(pbmfp)
+    FILE *pbmfp;
+{
+    bit **pbm_image;
+    int cols;
+    int rows;
+    int x;
+    int y;
+
+    pbm_image = pbm_readpbm(pbmfp, &cols, &rows);
+    for(y = 0; y < rows && y < yres; ++y)
+    {
+        for(x = 0; x < cols && x < xres; ++x)
+        {
+            if(pbm_image[y][x] == PBM_WHITE)
+            {
+                set_vmap(x, y);
+            }
+        }
+    }
+}
+
+
+static void
+print_map()
+{
+    int x, y;
+    int last_byte;
+
+#ifdef BUFFERED
+    char *iobuf;
+    iobuf = (char *)malloc(BUFSIZ);
+    if(iobuf == NULL)
+    {
+        pm_message( "Can't allocate space for I/O buffer.  "
+                    "Using unbuffered I/O...\n" );
+        setbuf(stdout, NULL);
+    }
+    else
+    {
+        setbuf(stdout, iobuf);
+    }
+#endif
+
+    fputs("\033[H\033[J", stdout);	/* clear screen */
+    fputs("\033[?3h", stdout);	/* 132 column mode */
+    fputs("\033)}\016", stdout);	/* mosaic mode */
+
+    for(y = 0; y < vmap_height; ++y)
+    {
+        for(last_byte = vmap_width - 1;
+            last_byte >= 0
+                && vmap[y * vmap_width + last_byte] == 0x20;
+            --last_byte)
+            ;
+
+        for(x = 0; x <= last_byte; ++x)
+        {
+            fputc(vmap[y*(vmap_width) + x], stdout);
+        }
+        fputc('\n', stdout);
+    }
+
+    fputs("\033(B\017", stdout);
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+    int argn;
+    const char *pbmfile;
+    FILE *pbmfp;
+    const char *usage="[pbmfile]";
+
+    pbm_init( &argc, argv );
+    for(argn = 1;
+        argn < argc && argv[argn][0] == '-' && strlen(argv[argn]) > 1;
+        ++argn)
+    {
+        pm_usage(usage);
+    }
+
+    if(argn >= argc)
+    {
+        pbmfile = "-";
+    }
+    else if(argc - argn != 1)
+    {
+        pm_usage(usage);
+    }
+    else
+    {
+        pbmfile = argv[argn];
+    }
+
+    if(STREQ(pbmfile, "-"))
+    {
+        pbmfp = stdin;
+    }
+    else
+    {
+        pbmfp = pm_openr( argv[argn] );
+    }
+
+    vmap_width = 132;
+    vmap_height = 23;
+
+    xres = vmap_width * 2;
+    yres = vmap_height * 3;
+
+    vmap = malloc(vmap_width * vmap_height * sizeof(char));
+    if(vmap == NULL)
+	{
+        pm_error( "Cannot allocate memory" );
+	}
+
+    init_map();
+    fill_map(pbmfp);
+    print_map();
+    /* If the program failed, it previously aborted with nonzero completion
+       code, via various function calls.
+    */
+    return 0; 
+}
+
+
+
diff --git a/converter/pbm/pbmtoascii.c b/converter/pbm/pbmtoascii.c
new file mode 100644
index 00000000..0472f809
--- /dev/null
+++ b/converter/pbm/pbmtoascii.c
@@ -0,0 +1,165 @@
+/* pbmtoascii.c - read a portable bitmap and produce ASCII graphics
+**
+** Copyright (C) 1988, 1992 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "pbm.h"
+
+#define SQQ '\''
+#define BSQ '\\'
+
+/* Bit-map for 1x2 mode:
+**     1
+**     2
+*/
+static char carr1x2[4] = {
+/*   0    1    2    3   */
+    ' ', '"', 'o', 'M' };
+
+/* Bit-map for 2x4 mode (hex):
+**     1  2
+**     4  8
+**     10 20
+**     40 80
+** The idea here is first to preserve geometry, then to show density.
+*/
+#define D08 'M'
+#define D07 'H'
+#define D06 '&'
+#define D05 '$'
+#define D04 '?'
+static char carr2x4[256] = {
+/*0  1    2   3    4   5    6   7    8   9    A   B    C   D    E   F  */
+' ',SQQ, '`','"', '-',SQQ, SQQ,SQQ, '-','`', '`','`', '-','^', '^','"',/*00-0F*/
+'.',':', ':',':', '|','|', '/',D04, '/','>', '/','>', '~','+', '/','*',/*10-1F*/
+'.',':', ':',':', BSQ,BSQ, '<','<', '|',BSQ, '|',D04, '~',BSQ, '+','*',/*20-2F*/
+'-',':', ':',':', '~',D04, '<','<', '~','>', D04,'>', '=','b', 'd','#',/*30-3F*/
+'.',':', ':',':', ':','!', '/',D04, ':',':', '/',D04, ':',D04, D04,'P',/*40-4F*/
+',','i', '/',D04, '|','|', '|','T', '/',D04, '/','7', 'r','}', '/','P',/*50-5F*/
+',',':', ';',D04, '>',D04, 'S','S', '/',')', '|','7', '>',D05, D05,D06,/*60-6F*/
+'v',D04, D04,D05, '+','}', D05,'F', '/',D05, '/',D06, 'p','D', D06,D07,/*70-7F*/
+'.',':', ':',':', ':',BSQ, ':',D04, ':',BSQ, '!',D04, ':',D04, D04,D05,/*80-8F*/
+BSQ,BSQ, ':',D04, BSQ,'|', '(',D05, '<','%', D04,'Z', '<',D05, D05,D06,/*90-9F*/
+',',BSQ, 'i',D04, BSQ,BSQ, D04,BSQ, '|','|', '|','T', D04,BSQ, '4','9',/*A0-AF*/
+'v',D04, D04,D05, BSQ,BSQ, D05,D06, '+',D05, '{',D06, 'q',D06, D06,D07,/*B0-BF*/
+'_',':', ':',D04, ':',D04, D04,D05, ':',D04, D04,D05, ':',D05, D05,D06,/*C0-CF*/
+BSQ,D04, D04,D05, D04,'L', D05,'[', '<','Z', '/','Z', 'c','k', D06,'R',/*D0-DF*/
+',',D04, D04,D05, '>',BSQ, 'S','S', D04,D05, 'J',']', '>',D06, '1','9',/*E0-EF*/
+'o','b', 'd',D06, 'b','b', D06,'6', 'd',D06, 'd',D07, '#',D07, D07,D08 /*F0-FF*/
+    };
+
+
+
+int
+main( argc, argv )
+int argc;
+char* argv[];
+    {
+    FILE* ifp;
+    int argn, gridx, gridy, rows, cols, format;
+    int ccols, lastcol, row, subrow, subcol;
+    register int col, b;
+    bit* bitrow;
+    register bit* bP;
+    int* sig;
+    register int* sP;
+    char* line;
+    register char* lP;
+    char* carr;
+    const char* usage = "[-1x2|-2x4] [pbmfile]";
+
+    pbm_init( &argc, argv );
+
+    /* Set up default parameters. */
+    argn = 1;
+    gridx = 1;
+    gridy = 2;
+    carr = carr1x2;
+
+    /* Check for flags. */
+    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+	{
+	if ( pm_keymatch( argv[argn], "-1x2", 2 ) )
+	    {
+	    gridx = 1;
+	    gridy = 2;
+	    carr = carr1x2;
+	    }
+	else if ( pm_keymatch( argv[argn], "-2x4", 2 ) )
+	    {
+	    gridx = 2;
+	    gridy = 4;
+	    carr = carr2x4;
+	    }
+	else
+	    pm_usage( usage );
+	++argn;
+	}
+
+    if ( argn < argc )
+	{
+	ifp = pm_openr( argv[argn] );
+	++argn;
+	}
+    else
+	ifp = stdin;
+    
+    if ( argn != argc )
+        pm_usage( usage );
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    ccols = ( cols + gridx - 1 ) / gridx;
+    bitrow = pbm_allocrow( cols );
+    sig = (int*) pm_allocrow( ccols, sizeof(int) );
+    line = (char*) pm_allocrow( ccols + 1, sizeof(char) );
+
+    for ( row = 0; row < rows; row += gridy )
+	{
+	/* Get a character-row's worth of sigs. */
+	for ( col = 0; col < ccols; ++col )
+	    sig[col] = 0;
+	b = 1;
+	for ( subrow = 0; subrow < gridy; ++subrow )
+	    {
+	    if ( row + subrow < rows )
+		{
+		pbm_readpbmrow( ifp, bitrow, cols, format );
+		for ( subcol = 0; subcol < gridx; ++subcol )
+		    {
+		    for ( col = subcol, bP = &(bitrow[subcol]), sP = sig;
+			  col < cols;
+			  col += gridx, bP += gridx, ++sP )
+			if ( *bP == PBM_BLACK )
+			    *sP |= b;
+		    b <<= 1;
+		    }
+		}
+	    }
+	/* Ok, now remove trailing blanks.  */
+	for ( lastcol = ccols - 1; lastcol >= 0; --lastcol )
+	    if ( carr[sig[lastcol]] != ' ' )
+		break;
+	/* Copy chars to an array and print. */
+	for ( col = 0, sP = sig, lP = line; col <= lastcol; ++col, ++sP, ++lP )
+	    *lP = carr[*sP];
+	*lP++ = '\0';
+	puts( line );
+	}
+
+    pm_close( ifp );
+    pbm_freerow( bitrow );
+    pm_freerow( (char*) sig );
+    pm_freerow( (char*) line );
+
+    /* If the program failed, it previously aborted with nonzero completion
+       code, via various function calls.
+    */
+    return 0;
+    }
diff --git a/converter/pbm/pbmtoatk.c b/converter/pbm/pbmtoatk.c
new file mode 100644
index 00000000..de7adf63
--- /dev/null
+++ b/converter/pbm/pbmtoatk.c
@@ -0,0 +1,188 @@
+/* pbmtoatk.c - convert portable bitmap to Andrew Toolkit raster object
+**
+** Copyright (C) 1991 by Bill Janssen.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "nstring.h"
+#include "pbm.h"
+
+#define DEFAULTSCALE (1<<16)
+#define RASTERVERSION 2
+
+
+static void
+write_atk_bytes(FILE *        const file, 
+                unsigned char const curbyte, 
+                unsigned int  const startcount) {
+
+    /* codes for data stream */
+    static unsigned char const whitezero   = 'f';
+    static unsigned char const whitetwenty = 'z';
+    static unsigned char const blackzero   = 'F';
+    static unsigned char const blacktwenty = 'Z';
+    static unsigned char const otherzero   = 0x1F;
+
+    #define WHITEBYTE 0x00
+    #define BLACKBYTE 0xFF
+
+    /* WriteRow table for conversion of a byte value to two character
+       hex representation 
+    */
+
+    static unsigned char hex[16] = {
+    '0', '1', '2', '3', '4', '5', '6', '7',
+    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+    };
+
+    unsigned int curcount;
+
+    curcount = startcount;  /* initial value */
+
+    switch (curbyte) {
+    case WHITEBYTE:
+        while (curcount > 20) {
+            fputc(whitetwenty, file);
+            curcount -= 20;
+        }
+        fputc(whitezero + curcount, file);
+        break;
+    case BLACKBYTE:
+        while (curcount > 20) {
+            fputc(blacktwenty, file);
+            curcount -= 20;
+        }
+        fputc(blackzero + curcount, file);
+        break;
+    default:
+        while (curcount > 16) {
+            fputc(otherzero + 16, file);
+            fputc(hex[curbyte / 16], file);
+            fputc(hex[curbyte & 15], file);
+            curcount -= 16;
+        }
+        if (curcount > 1)
+            fputc(otherzero + curcount, file);
+        else ;  /* the byte written will represent a single instance */
+        fputc(hex[curbyte / 16], file);
+        fputc(hex[curbyte & 15], file);
+    }
+}
+
+
+
+static void
+process_atk_byte(int *           const pcurcount, 
+                 unsigned char * const pcurbyte, 
+                 FILE *          const file, 
+                 unsigned char   const newbyte, 
+                 int             const eolflag) {
+
+    int curcount;
+    unsigned char curbyte;
+
+    curcount = *pcurcount;  /* initial value */
+    curbyte  = *pcurbyte;  /* initial value */
+    
+    if (curcount < 1) {
+        *pcurbyte = curbyte = newbyte;
+        *pcurcount = curcount = 1;
+    } else if (newbyte == curbyte) {
+        *pcurcount = (curcount += 1);
+    }
+
+    if (curcount > 0 && newbyte != curbyte) {
+        write_atk_bytes (file, curbyte, curcount);
+        *pcurcount = 1;
+        *pcurbyte = newbyte;
+    }
+
+    if (eolflag) {
+        write_atk_bytes (file, *pcurbyte, *pcurcount);
+        fprintf(file, " |\n");
+        *pcurcount = 0;
+        *pcurbyte = 0;
+    }
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    FILE *ifd;
+    bit *bitrow;
+    register bit *bP;
+    int rows, cols, format, row;
+    int col;
+    char name[100], *cp;
+    unsigned char curbyte, newbyte;
+    int curcount, gather;
+
+    pbm_init ( &argc, argv );
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments.  Only argument is file name");
+
+    else if (argc-1 == 1) {
+        ifd = pm_openr( argv[1] );
+        strcpy(name, argv[1]);
+        if (STREQ( name, "-"))
+            strcpy(name, "noname");
+        
+        if ((cp = strchr(name, '.')) != 0)
+            *cp = '\0';
+    } else {
+        ifd = stdin;
+        strcpy( name, "noname" );
+    }
+
+    pbm_readpbminit(ifd, &cols, &rows, &format);
+    bitrow = pbm_allocrow(cols);
+
+    printf ("\\begindata{raster,%d}\n", 1);
+    printf ("%d %d %d %d ", RASTERVERSION, 0, DEFAULTSCALE, DEFAULTSCALE);
+    printf ("%d %d %d %d\n", 0, 0, cols, rows); /* subraster */
+    printf ("bits %d %d %d\n", 1, cols, rows);
+
+    for (row = 0; row < rows; ++row) {
+        pbm_readpbmrow(ifd, bitrow, cols, format);
+        bP = bitrow;
+        gather = 0;
+        newbyte = 0;
+        curbyte = 0;
+        curcount = 0;
+        col = 0;
+        while (col < cols) {
+            if (gather > 7) {
+                process_atk_byte (&curcount, &curbyte, stdout, newbyte, FALSE);
+                gather = 0;
+                newbyte = 0;
+            }
+            newbyte = (newbyte << 1) | (*bP++);
+            gather += 1;
+            col += 1;
+        }
+
+        if (gather > 0) {
+            newbyte = (newbyte << (8 - gather));
+            process_atk_byte (&curcount, &curbyte, stdout, newbyte, TRUE);
+        }
+    }
+
+    pm_close( ifd );
+    
+    printf ("\\enddata{raster, %d}\n", 1);
+
+    return 0;
+}
diff --git a/converter/pbm/pbmtobbnbg.c b/converter/pbm/pbmtobbnbg.c
new file mode 100644
index 00000000..e97ef4f2
--- /dev/null
+++ b/converter/pbm/pbmtobbnbg.c
@@ -0,0 +1,152 @@
+/* pbmtobg.c - read a portable bitmap and produce BitGraph graphics
+**
+** Copyright 1989 by Mike Parker.
+**
+** 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.
+*/
+  
+/*
+** Changed to take advantage of negative Packed Pixed Data values and
+** supply ANSI-standard string terminator.  Paul Milazzo, 28 May 1990.
+*/
+
+#include "pbm.h"
+
+static void write16 ARGS(( unsigned int	));
+
+static int nco;
+
+int
+main(argc,argv)
+int argc;
+char **argv;
+{
+ int rows;
+ int cols;
+ int format;
+ bit *bitrow;
+ int row;
+ unsigned int sixteen;
+ int i;
+ unsigned int mask;
+ int op;
+ int x;
+ int y;
+
+
+ pbm_init( &argc, argv );
+
+ op = 3;
+ switch (argc)
+  { case 1:
+       break;
+    case 2:
+       op = atoi(argv[1]);
+       break;
+    case 3:
+       x = atoi(argv[1]);
+       y = atoi(argv[2]);
+       printf("\33:%d;%dm",x,y);
+       break;
+    case 4:
+       op = atoi(argv[1]);
+       x = atoi(argv[2]);
+       y = atoi(argv[3]);
+       printf("\33:%d;%dm",x,y);
+       break;
+  }
+ nco = 0;
+ pbm_readpbminit(stdin,&cols,&rows,&format);
+ printf("\33P:%d;%d;%ds\n",op,cols,rows);
+ bitrow = pbm_allocrow(cols);
+ for (row=0;row<rows;row++)
+  { pbm_readpbmrow(stdin,bitrow,cols,format);
+    sixteen = 0;
+    mask = 0x8000;
+    for (i=0;i<cols;i++)
+     { if (bitrow[i]==PBM_BLACK) sixteen |= mask;
+       mask >>= 1;
+       if (mask == 0)
+	{ mask = 0x8000;
+	  write16(sixteen);
+	  sixteen = 0;
+	}
+     }
+    if (mask != 0x8000)
+     { write16(sixteen);
+     }
+  }
+ puts("\033\\");
+ exit(0);
+}
+
+#ifdef POSITIVE_VALUES_ONLY
+static void
+write16(sixteen)
+unsigned int sixteen;
+{
+ if (nco > 75)
+  { putchar('\n');
+    nco = 0;
+  }
+ if (sixteen & 0xfc00)
+  { putchar(0100+(sixteen>>10));
+    nco ++;
+  }
+ if (sixteen & 0xfff0)
+  { putchar(0100+((sixteen>>4)&0x3f));
+    nco ++;
+  }
+ putchar(060+(sixteen&0xf));
+ nco ++;
+}
+#else
+/*
+ *  This version of "write16" uses negative Packed Pixel Data values to
+ *  represent numbers in the range 0x7fff--0xffff; negative values will
+ *  require fewer characters as they approach the upper end of that range.
+ */
+static void
+write16 (word)
+unsigned int	word;
+{
+    int		high;
+    int		mid;
+    int		low;
+    int		signChar;
+
+    if (nco > 75) {
+	putchar ('\n');
+	nco = 0;
+    }
+
+    if (word > 0x7fff) {
+	word = (unsigned int) (0x10000L - (long) word);
+	signChar = ' ';
+    }
+    else
+	signChar = '0';
+
+    high = (word >> 10) + '@';
+    mid	= ((word & 0x3f0) >> 4) + '@';
+    low	= (word & 0xf) + signChar;
+
+    if (high != '@') {
+	printf ("%c%c%c", high, mid, low);
+	nco += 3;
+    }
+    else if (mid != '@') {
+	printf ("%c%c", mid, low);
+	nco += 2;
+    }
+    else {
+	putchar (low);
+	nco++;
+    }
+}
+#endif
diff --git a/converter/pbm/pbmtocmuwm.c b/converter/pbm/pbmtocmuwm.c
new file mode 100644
index 00000000..64d7af40
--- /dev/null
+++ b/converter/pbm/pbmtocmuwm.c
@@ -0,0 +1,117 @@
+/* pbmtocmuwm.c - read a portable bitmap and produce a CMU window manager bitmap
+**
+** Copyright (C) 1989 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "pbm.h"
+#include "cmuwm.h"
+
+static void putinit ARGS(( int rows, int cols ));
+static void putbit ARGS(( bit b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, format, padright, row, col;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    bitrow = pbm_allocrow( cols );
+    
+    /* Round cols up to the nearest multiple of 8. */
+    padright = ( ( cols + 7 ) / 8 ) * 8 - cols;
+
+    putinit( rows, cols );
+    for ( row = 0; row < rows; row++ )
+	{
+	pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; col++, bP++ )
+	    putbit( *bP );
+	for ( col = 0; col < padright; col++ )
+	    putbit( 0 );
+        }
+
+    pm_close( ifp );
+
+    putrest( );
+
+    exit( 0 );
+    }
+
+static unsigned char item;
+static int bitsperitem, bitshift;
+
+static void
+putinit( rows, cols )
+    int rows, cols;
+    {
+    if ( pm_writebiglong( stdout, CMUWM_MAGIC ) == -1 )
+	pm_error( "write error" );
+    if ( pm_writebiglong( stdout, cols ) == -1 )
+	pm_error( "write error" );
+    if ( pm_writebiglong( stdout, rows ) == -1 )
+	pm_error( "write error" );
+    if ( pm_writebigshort( stdout, (short) 1 ) == -1 )
+	pm_error( "write error" );
+
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 7;
+    }
+
+#if __STDC__
+static void
+putbit( bit b )
+#else /*__STDC__*/
+static void
+putbit( b )
+    bit b;
+#endif /*__STDC__*/
+    {
+    if ( bitsperitem == 8 )
+	putitem( );
+    if ( b == PBM_WHITE )
+	item += 1 << bitshift;
+    bitsperitem++;
+    bitshift--;
+    }
+
+static void
+putrest( )
+    {
+    if ( bitsperitem > 0 )
+	putitem( );
+    }
+
+static void
+putitem( )
+    {
+    (void) putc( item, stdout );
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 7;
+    }
diff --git a/converter/pbm/pbmtodjvurle.c b/converter/pbm/pbmtodjvurle.c
new file mode 100644
index 00000000..dbe96f31
--- /dev/null
+++ b/converter/pbm/pbmtodjvurle.c
@@ -0,0 +1,140 @@
+/*
+   Convert a PBM image into the DjVu Bitonal RLE format
+   described in the csepdjvu(1) documentation
+  
+   Copyright (c) 2004 Scott Pakin <scott+pbm@pakin.org>
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+   
+   1. Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+   2. Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+   3. The name of the author may not be used to endorse or promote products
+      derived from this software without specific prior written permission.
+   
+   THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+   IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "pbm.h"
+
+
+/* Write a byte to a file and check for errors. */
+static void
+writebyte(FILE *        const ofP,
+          unsigned char const c) {
+
+    if (fputc (c, ofP) == EOF)
+        pm_error ("failed to write to the RLE file.  Errno=%d (%s)",
+                  errno, strerror(errno));
+}
+
+
+/* Write a run length to the RLE file. */
+static void 
+write_rle (FILE *rlefile, uint32n tally)
+{
+  do {
+    /* Output a single run. */
+    if (tally < 192) {
+      /* Single-byte runs */
+      writebyte (rlefile, tally);
+      tally >>= 8;
+    }
+    else {
+      /* Two-byte runs */
+      writebyte (rlefile, ((tally>>8)&0x3F) + 0xC0);
+      writebyte (rlefile, tally&0xFF);
+      tally >>= 14;
+    }
+
+    /* Very large runs need to be split into smaller runs.  We
+     * therefore need to toggle back to the same color we had for the
+     * previous smaller run. */
+    if (tally > 0)
+      writebyte (rlefile, 0);
+  }
+  while (tally > 0);
+}
+
+
+
+int 
+main (int argc, char *argv[])
+{
+  FILE * const rlefile = stdout;    /* Generated Bitonal RLE file */
+
+  FILE *pbmfile;             /* PBM file to convert */
+  int numcols, numrows;      /* Width and height in pixels of the PBM file */
+  int format;                /* Original image type before conversion to PBM */
+  bit *pbmrow;               /* One row of the PBM file */
+  uint32n pixeltally = 0;    /* Run length of the current color */
+  int row, col;              /* Row and column loop variables */
+  const char * pbmfilename;  /* Name of input file */
+
+  /* Parse the command line. */
+  pbm_init (&argc, argv);
+
+  if (argc-1 < 1)
+      pbmfilename = "-";
+  else if (argc-1 == 1)
+      pbmfilename = argv[1];
+  else
+      pm_error("Program takes at most 1 argument -- the input file name.  "
+               "You specified %d", argc-1);
+
+  pbmfile = pm_openr(pbmfilename);
+
+  /* Write an RLE header. */
+  pbm_readpbminit (pbmfile, &numcols, &numrows, &format);
+  fprintf (rlefile, "R4\n");
+  fprintf (rlefile, "%d %d\n", numcols, numrows);
+
+  /* Write the RLE data. */
+  pbmrow = pbm_allocrow (numcols);
+  for (row=0; row<numrows; row++) {
+    bit prevpixel;        /* Previous pixel seen */
+
+    pbm_readpbmrow (pbmfile, pbmrow, numcols, format);
+    prevpixel = PBM_WHITE;   /* Bitonal RLE rows always start with white */
+    pixeltally = 0;
+    for (col=0; col<numcols; col++) {
+      bit newpixel = pbmrow[col];      /* Current pixel color */
+
+      if (newpixel == prevpixel)
+        pixeltally++;
+      else {
+        write_rle (rlefile, pixeltally);
+        pixeltally = 1;
+        prevpixel = newpixel;
+      }
+    }
+    write_rle (rlefile, pixeltally);
+  }
+
+  /* Finish up cleanly. */
+  pbm_freerow (pbmrow);
+  if (rlefile != stdout)
+    pm_close (rlefile);
+  if (pbmfile != stdin)
+    pm_close (pbmfile);
+  exit (0);
+}
diff --git a/converter/pbm/pbmtoepsi.c b/converter/pbm/pbmtoepsi.c
new file mode 100644
index 00000000..fc8cee7d
--- /dev/null
+++ b/converter/pbm/pbmtoepsi.c
@@ -0,0 +1,252 @@
+/* pbmtoepsi.c
+**
+**    by Doug Crabill, based heavily on pbmtoascii
+**
+**    Converts a pbm file to an encapsulated PostScript style bitmap.
+**    Note that it does NOT covert the pbm file to PostScript, only to
+**    a bitmap to be added to a piece of PostScript generated elsewhere.
+**
+** Copyright (C) 1988 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "pbm.h"
+#include "shhopt.h"
+
+#if !defined(MAXINT)
+#define MAXINT (0x7fffffff)
+#endif
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilespec;  /* Filespecs of input files */
+
+    unsigned int dpiX;     /* horiz component of DPI option */
+    unsigned int dpiY;     /* vert component of DPI option */
+    unsigned int bbonly;
+
+    unsigned int verbose;
+};
+
+
+
+static void
+parse_dpi(char * const dpiOpt, 
+          unsigned int * const dpiXP, unsigned int * const dpiYP) {
+
+    char *dpistr2;
+    unsigned int dpiX, dpiY;
+
+    dpiX = strtol(dpiOpt, &dpistr2, 10);
+    if (dpistr2 == dpiOpt) 
+        pm_error("Invalid value for -dpi: '%s'.  Must be either number "
+                 "or NxN ", dpiOpt);
+    else {
+        if (*dpistr2 == '\0') {
+            *dpiXP = dpiX;
+            *dpiYP = dpiX;
+        } else if (*dpistr2 == 'x') {
+            char * dpistr3;
+
+            dpistr2++;  /* Move past 'x' */
+            dpiY = strtol(dpistr2, &dpistr3, 10);        
+            if (dpistr3 != dpistr2 && *dpistr3 == '\0') {
+                *dpiXP = dpiX;
+                *dpiYP = dpiY;
+            } else {
+                pm_error("Invalid value for -dpi: '%s'.  Must be either "
+                         "number or NxN", dpiOpt);
+            }
+        }
+    }
+}
+
+
+static void
+parseCommandLine(int argc, 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.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+        /* Instructions to OptParseOptions2 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    char * dpiOpt;
+    unsigned int dpiOptSpec;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "bbonly",     OPT_FLAG,   NULL, &cmdlineP->bbonly,        0);
+    OPTENT3(0, "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,       0);
+    OPTENT3(0, "dpi",        OPT_STRING, &dpiOpt,         &dpiOptSpec,   0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+    
+
+    if (dpiOptSpec)
+        parse_dpi(dpiOpt, &cmdlineP->dpiX, &cmdlineP->dpiY);
+    else
+        cmdlineP->dpiX = cmdlineP->dpiY = 72;
+    
+    if ((argc-1) > 1)
+        pm_error("Too many arguments (%d).  Only argument is input filespec",
+                 argc-1);
+    
+    if (argc-1 == 0)
+        cmdlineP->inputFilespec = "-";
+    else
+        cmdlineP->inputFilespec = argv[1];
+}
+
+
+
+static void
+findPrincipalImage(bit ** const bits, 
+                   int    const rows,
+                   int    const cols,
+                   int *  const topP,
+                   int *  const bottomP,
+                   int *  const leftP,
+                   int *  const rightP) {
+
+    int top, bottom, left, right;
+    int row;
+
+    /* Initial values */
+    top = MAXINT;
+    bottom = -MAXINT;
+    left = MAXINT;
+    right = -MAXINT;
+ 
+    for (row = 0; row < rows; row++) {
+        int col;
+        for (col = 0; col < cols; col++) {
+            if (bits[row][col] == PBM_BLACK) {
+                if (row < top) 
+                    top = row;
+                if (row > bottom) 
+                    bottom = row;
+                if (col < left) 
+                    left = col;
+                if (col > right) 
+                    right = col;
+            }
+        }
+    }
+    *topP = top;
+    *bottomP = bottom;
+    *leftP = left;
+    *rightP = right;
+}
+
+
+
+static void
+outputBoundingBox(int const top, int const bottom,
+                  int const left, int const right,
+                  int const rows,
+                  unsigned int const dpiX, unsigned int const dpiY) {
+
+    float const xScale = 72.0 / dpiX;
+    float const yScale = 72.0 / dpiY;
+
+    printf("%%%%BoundingBox: %d %d %d %d\n", 
+           ROUND(left*xScale),  ROUND((rows - bottom)*yScale), 
+           ROUND(right*xScale), ROUND((rows - top)*yScale));
+}
+
+
+
+static unsigned char
+eightPixels(bit ** const bits,
+            int    const row,
+            int    const col,
+            int    const cols) {
+/*----------------------------------------------------------------------------
+  Compute a byte that represents the 8 pixels starting at Column 'col' of
+  row 'row' of the raster 'bits'.  The most significant bit of the result
+  represents the leftmost pixel, with 1 meaning black.
+
+  The row is 'cols' columns wide, so fill on the right with white if there
+  are not eight pixels in the row starting with Column 'col'.
+-----------------------------------------------------------------------------*/
+    unsigned int bitPos;
+    unsigned char octet;
+
+    octet = 0;  /* initial value */
+
+    for (bitPos = 0; bitPos < 8; ++bitPos) {
+        octet <<= 1;
+        if (col + bitPos < cols)
+            if (bits[row][col + bitPos] == PBM_BLACK)
+                octet += 1;
+    }
+    return(octet);
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE *ifP;
+    bit **bits;
+    int rows, cols;
+    int top, bottom, left, right;
+        /* boundaries of principal part of image -- i.e. excluding white
+           borders
+        */
+
+    pbm_init( &argc, argv );
+    
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+    bits = pbm_readpbm( ifP, &cols, &rows );
+    pm_close(ifP);
+
+    findPrincipalImage(bits, rows, cols, &top, &bottom, &left, &right);
+
+    printf("%%!PS-Adobe-2.0 EPSF-1.2\n");
+
+    outputBoundingBox(top, bottom, left, right, rows, 
+                      cmdline.dpiX, cmdline.dpiY);
+
+    if (!cmdline.bbonly) {
+        int row;
+        printf("%%%%BeginPreview: %d %d 1 %d\n", 
+               right - left + 1, bottom - top + 1, bottom - top + 1);
+
+        for (row = top; row <= bottom; row++) {
+            int col;
+
+            printf("%% ");
+
+            for (col = left; col <= right; col += 8) 
+                printf("%02x", eightPixels(bits, row, col, cols));
+
+            printf("\n");
+        }
+        printf("%%%%EndImage\n");
+        printf("%%%%EndPreview\n");
+    }
+    exit(0);
+}
diff --git a/converter/pbm/pbmtoepson.c b/converter/pbm/pbmtoepson.c
new file mode 100644
index 00000000..d26734d4
--- /dev/null
+++ b/converter/pbm/pbmtoepson.c
@@ -0,0 +1,338 @@
+/* pbmtoeps.c - read a PBM image and produce Epson graphics
+**
+** Copyright (C) 1990 by John Tiller (tiller@galois.msfc.nasa.gov)
+**			 and Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#define _BSD_SOURCE    /* Make sure strcasecmp() is in string.h */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "shhopt.h"
+#include "mallocvar.h"
+
+#include "pbm.h"
+
+
+static char const esc = 033;
+
+enum epsonProtocol {ESCP9, ESCP};
+
+enum adjacence {ADJACENT_ANY, ADJACENT_YES, ADJACENT_NO};
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilespec;  /* '-' if stdin */
+    unsigned int dpi;  /* zero means "any" */
+    enum adjacence adjacence;
+    enum epsonProtocol protocol;
+};
+
+
+
+static void
+parseCommandLine(int                 argc, 
+                 char **             argv,
+                 struct cmdlineInfo *cmdlineP ) {
+/*----------------------------------------------------------------------------
+   Parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    char * protocol;
+    unsigned int adjacentSpec, nonadjacentSpec;
+    unsigned int dpiSpec, protocolSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "protocol",   OPT_UINT,   &protocol,
+            &protocolSpec,                    0);
+    OPTENT3(0, "dpi",        OPT_UINT,   &cmdlineP->dpi,
+            &dpiSpec,                    0);
+    OPTENT3(0, "adjacent",   OPT_FLAG,   NULL,
+            &adjacentSpec,                    0);
+    OPTENT3(0, "nonadjacent",   OPT_FLAG,   NULL,
+            &nonadjacentSpec,                    0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+
+    if (!dpiSpec)
+        cmdlineP->dpi = 0;
+    else {
+        if (cmdlineP->dpi == 0)
+            pm_error("-dpi must be positive");
+    }
+
+    if (!protocolSpec)
+        cmdlineP->protocol = ESCP9;
+    else {
+        if (strcasecmp(protocol, "escp9") == 0)
+            cmdlineP->protocol = ESCP9;
+        else if (strcasecmp(protocol, "escp") == 0)
+            cmdlineP->protocol = ESCP;
+        else if (strcasecmp(protocol, "escp2") == 0)
+            pm_error("This program cannot do ESC/P2.  Try Pbmtoescp2.");
+        else
+            pm_error("Unrecognized value '%s' for -protocol.  "
+                     "Only recognized values are 'escp9' and 'escp'",
+                     protocol);
+    }
+    
+    if (adjacentSpec && nonadjacentSpec)
+        pm_error("You can't specify both -adjacent and -nonadjacent");
+    else if (adjacentSpec)
+        cmdlineP->adjacence = ADJACENT_YES;
+    else if (nonadjacentSpec)
+        cmdlineP->adjacence = ADJACENT_NO;
+    else
+        cmdlineP->adjacence = ADJACENT_ANY;
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilespec = "-";
+    else {
+        cmdlineP->inputFilespec = argv[1];
+        if (argc-1 > 1)
+            pm_error("Too many arguments (%d).  The only non-option argument "
+                     "is the file name", argc-1);
+    }
+}
+
+
+
+static unsigned int
+lineWidth(const bit ** const stripeBits,
+          unsigned int const cols,
+          unsigned int const stripeRows) {
+/*----------------------------------------------------------------------------
+   Return the column number just past the rightmost column of the stripe
+   stripeBits[] that contains at least some black.
+
+   The stripe is 'cols' wide by 'stripeRows' high.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+    unsigned int endSoFar;
+    
+    endSoFar = 0;
+
+    for (col = 0; col < cols; ++ col) {
+        unsigned int stripeRow;  /* row number within stripe */
+
+        for (stripeRow = 0; stripeRow < stripeRows; ++stripeRow) {
+            if (stripeBits[stripeRow][col] == PBM_BLACK)
+                endSoFar = col+1;
+        }
+    }
+    return endSoFar;
+}
+
+
+
+static void
+printStripe(const bit ** const stripeBits,
+            unsigned int const cols,
+            unsigned int const stripeRows,
+            char         const m) {
+/*----------------------------------------------------------------------------
+   Print one stripe (a group of rows printed with one pass of the print
+   head.  The stripe is cols columns wide by stripeRows high.
+   stripeBits[row][col] is the pixel value for Row row, Column col within
+   the stripe.
+
+   'm' is the "m" parameter for the Select Bit Image command.  It controls
+   such things as the horizontal density.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+
+    /* Print header of Select Bit Image command */
+    printf("%c%c%c%c%c", esc, '*', m, cols % 256, cols / 256);
+    
+    /* Print the data part of the Select Bit Image command */
+    for (col = 0; col < cols; ++col) {
+        unsigned int stripeRow;
+        int val;
+        
+        val = 0;
+        for (stripeRow = 0; stripeRow < stripeRows; ++stripeRow) 
+            if (stripeBits[stripeRow][col] == PBM_BLACK)
+                val |= (1 << (8-1-stripeRow));
+        putchar(val);
+    }
+}
+
+
+
+static void
+computeM(enum epsonProtocol const protocol,
+         unsigned int       const dpi,
+         enum adjacence     const adjacence,
+         char *             const mP) {
+/*----------------------------------------------------------------------------
+   Compute the "m" parameter for the Select Bit Image command.
+-----------------------------------------------------------------------------*/
+    switch (dpi) {
+    case 0:
+        /* Special value meaning "any dpi you feel is appropriate" */
+        if (adjacence == ADJACENT_NO)
+            *mP = 2;
+        else {
+            switch (protocol) {
+            case ESCP9: *mP = 5; break;
+            case ESCP:  *mP = 6; break;
+            }
+        }
+        break;
+    case 60: 
+        if (adjacence == ADJACENT_NO)
+            pm_error("You can't print at %u dpi "
+                     "with adjacent dot printing", dpi);
+        *mP = 0;
+        break;
+    case 120:
+        *mP = adjacence == ADJACENT_NO ? 2 : 1;
+        break;
+    case 240:
+        if (adjacence == ADJACENT_YES)
+            pm_error("You can't print at %u dpi "
+                     "without adjacent dot printing", dpi);
+        *mP = 3;
+        break;
+    case 80:
+        if (adjacence == ADJACENT_NO)
+            pm_error("You can't print at %u dpi "
+                     "with adjacent dot printing", dpi);
+        *mP = 4;
+        break;
+    case 72:
+        if (protocol != ESCP9)
+            pm_error("%u dpi is possible only with the ESC/P 9-pin protocol", 
+                     dpi);
+        if (adjacence == ADJACENT_NO)
+            pm_error("You can't print at %u dpi "
+                     "with adjacent dot printing", dpi);
+        *mP = 5;
+        break;
+    case 90:
+        if (adjacence == ADJACENT_NO)
+            pm_error("You can't print at %u dpi "
+                     "with adjacent dot printing", dpi);
+        *mP = 6;
+        break;
+    case 144:
+        if (protocol != ESCP9)
+            pm_error("%u dpi is possible only with the ESC/P 9-pin protocol", 
+                     dpi);
+        if (adjacence == ADJACENT_NO)
+            pm_error("You can't print at %u dpi "
+                     "with adjacent dot printing", dpi);
+        *mP = 7;
+        break;
+    default:
+        pm_error("Invalid DPI value: %u.  This program knows only "
+                 "60, 72, 80, 90, 120, 144, and 240.", dpi);
+    }
+}
+
+
+
+static void
+convertToEpson(const bit **       const bits,
+               int                const cols,
+               int                const rows,
+               enum epsonProtocol const protocol,
+               unsigned int       const dpi,
+               enum adjacence     const adjacence) {
+    
+    unsigned int const rowsPerStripe = 8;
+    unsigned int const stripes = (rows + rowsPerStripe-1) / rowsPerStripe;
+
+    unsigned int stripe;
+    char m;
+    
+    computeM(protocol, dpi, adjacence, &m);
+
+    /* Change line spacing to 8/72 inches. */
+    printf("%c%c%c", esc, 'A', 8);
+
+    /* Write out the rows, one stripe at a time.  A stripe is 8 rows --
+       the amount written in one pass of the print head.  The bottommost
+       stripe can be fewer than 8 rows.
+    */
+
+    for (stripe = 0; stripe < stripes; ++stripe) {
+        const bit ** const stripeBits = &bits[stripe*rowsPerStripe];
+        unsigned int const stripeRows = 
+            MIN(rowsPerStripe, rows - stripe * rowsPerStripe);
+            /* Number of rows in this stripe (8 for all but bottom stripe) */
+        
+        unsigned int const endcol = lineWidth(stripeBits, cols, stripeRows);
+            /* Column where right margin (contiguous white area at right
+               end of stripe) begins.  Zero if entire stripe is white.
+            */
+
+        if (endcol > 0)
+            printStripe(stripeBits, endcol, stripeRows, m);
+
+        putchar('\n');
+    }
+    putchar('\f');
+
+    /* Restore normal line spacing. */
+    printf("%c%c", esc, '@');
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE* ifP;
+    const bit** bits;
+    int rows, cols;
+
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    bits = (const bit **)pbm_readpbm(ifP, &cols, &rows);
+
+    pm_close(ifP);
+
+    convertToEpson(bits, cols, rows, 
+                   cmdline.protocol, cmdline.dpi, cmdline.adjacence);
+
+    pbm_freearray(bits, rows);
+
+    return 0;
+}
diff --git a/converter/pbm/pbmtoescp2.c b/converter/pbm/pbmtoescp2.c
new file mode 100644
index 00000000..787f7027
--- /dev/null
+++ b/converter/pbm/pbmtoescp2.c
@@ -0,0 +1,186 @@
+/* pbmtoescp2.c - read a portable bitmap and produce Epson ESC/P2 raster
+**                 graphics output data for Epson Stylus printers
+**
+** Copyright (C) 2003 by Ulrich Walcher (u.walcher@gmx.de)
+**                       and Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+/* I used the Epson ESC/P Reference Manual (1997) in writing this. */
+
+#include <string.h>
+
+#include "pbm.h"
+#include "shhopt.h"
+
+static char const esc = 033;
+
+struct cmdlineInfo {
+    const char * inputFileName;
+    unsigned int resolution;
+    unsigned int compress;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo *cmdlineP) {
+
+    optStruct3 opt;
+    unsigned int option_def_index = 0;
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+
+    unsigned int compressSpec, resolutionSpec;
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;
+    opt.allowNegNum = FALSE;
+    OPTENT3(0, "compress",     OPT_UINT,    &cmdlineP->compress,    
+            &compressSpec, 0);
+    OPTENT3(0, "resolution",   OPT_UINT,    &cmdlineP->resolution,  
+            &resolutionSpec, 0);
+    
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    
+    if (argc-1 > 1)
+        pm_error("Too many arguments: %d.  "
+                 "Only argument is the filename", argc-1);
+
+    if (compressSpec) {
+        if (cmdlineP->compress != 0 && cmdlineP->compress != 1)
+            pm_error("Invalid -compress value: %u.  Only 0 and 1 are valid.",
+                     cmdlineP->compress);
+    } else
+        cmdlineP->compress = 1;
+
+    if (resolutionSpec) {
+        if (cmdlineP->resolution != 360 && cmdlineP->resolution != 180)
+            pm_error("Invalid -resolution value: %u.  "
+                     "Only 180 and 360 are valid.", cmdlineP->resolution);
+    } else
+        cmdlineP->resolution = 360;
+
+    if (argc-1 == 1)
+        cmdlineP->inputFileName = argv[1];
+    else
+        cmdlineP->inputFileName = "-";
+}
+
+
+
+static unsigned int
+enc_epson_rle(unsigned int          const l, 
+              const unsigned char * const src, 
+              unsigned char *       const dest) {
+/*----------------------------------------------------------------------------
+  compress l data bytes from src to dest and return the compressed
+  length
+-----------------------------------------------------------------------------*/
+    unsigned int i;      /* index */
+    unsigned int state;  /* run state */
+    unsigned int pos;    /* source position */
+    unsigned int dpos;   /* destination position */
+
+    pos = dpos = state  = 0;
+    while ( pos < l )
+    {
+        for (i=0; i<128 && pos+i<l; i++)
+            /* search for begin of a run, smallest useful run is 3
+               equal bytes 
+            */
+            if(src[pos+i]==src[pos+i+1] && src[pos+i]==src[pos+i+2])
+            {
+                state=1;                       /* set run state */
+                break;
+            }
+	if(i)
+	{
+        /* set counter byte for copy through */
+        dest[dpos] = i-1;       
+        /* copy data bytes before run begin or end cond. */
+        memcpy(dest+dpos+1,src+pos,i);    
+        pos+=i; dpos+=i+1;                 /* update positions */
+	}
+    if (state)
+    {
+        for (i=0; src[pos+i]==src[pos+i+1] && i<128 && pos+i<l; i++);
+        /* found the runlength i */
+        dest[dpos]   = 257-i;           /* set counter for byte repetition */
+        dest[dpos+1] = src[pos];        /* set byte to be repeated */
+        pos+=i; dpos+=2; state=0;       /* update positions, reset run state */
+        }
+    }
+    return dpos;
+}
+
+
+
+int
+main(int argc, char* argv[]) {
+
+    FILE* ifP;
+    int rows, cols;
+    int format;
+    unsigned int row, idx, len;
+    unsigned int h, v;
+    unsigned char *bytes, *cprbytes;
+    struct cmdlineInfo cmdline;
+    
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    bytes = malloc(24*pbm_packed_bytes(cols)+2);
+    cprbytes = malloc(2*24*pbm_packed_bytes(cols));
+    if (bytes == NULL || cprbytes == NULL)
+        pm_error("Cannot allocate memory");
+
+    h = v = 3600/cmdline.resolution;
+
+    /* Set raster graphic mode. */
+    printf("%c%c%c%c%c%c", esc, '(', 'G', 1, 0, 1);
+
+    /* Set line spacing in units of 1/360 inches. */
+    printf("%c%c%c", esc, '+', 24*h/10);
+
+    /* Write out raster stripes 24 rows high. */
+    for (row = 0; row < rows; row += 24) {
+        unsigned int const linesThisStripe = (rows-row<24) ? rows%24 : 24;
+        printf("%c%c%c%c%c%c%c%c", esc, '.', cmdline.compress, 
+               v, h, linesThisStripe, 
+               cols%256, cols/256);
+        /* Read pbm rows, each padded to full byte */
+        for (idx = 0; idx < 24 && row+idx < rows; ++idx)
+            pbm_readpbmrow_packed(ifP,bytes+idx*pbm_packed_bytes(cols),
+                                  cols,format);
+        /* Write raster data. */
+        if (cmdline.compress != 0) {
+            /* compressed */
+            len = enc_epson_rle(linesThisStripe * pbm_packed_bytes(cols), 
+                                bytes, cprbytes);
+            fwrite(cprbytes,len,1,stdout);
+        } else
+            /* uncompressed */
+            fwrite(bytes, pbm_packed_bytes(cols), linesThisStripe, stdout);    
+
+        if (rows-row >= 24) putchar('\n');
+    }
+    free(bytes); free(cprbytes);
+    pm_close(ifP);
+
+    /* Reset printer. */
+    printf("%c%c", esc, '@');
+
+    return 0;
+}
diff --git a/converter/pbm/pbmtog3.c b/converter/pbm/pbmtog3.c
new file mode 100644
index 00000000..10db3afe
--- /dev/null
+++ b/converter/pbm/pbmtog3.c
@@ -0,0 +1,485 @@
+/* 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.
+*/
+
+#include <assert.h>
+
+#include "pbm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "bitreverse.h"
+#include "wordaccess.h"
+#include "g3.h"
+
+#define TC_MC 64
+
+static bool const pbmtorl = 
+#ifdef PBMTORL
+    TRUE;
+#else
+    FALSE;
+#endif
+
+
+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.
+        */
+
+    /* 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 {
+    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;
+    unsigned int verbose;
+};
+
+
+
+static void
+parseCommandLine(int argc, 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.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions2 on how to parse our options.  */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0,   "reversebits",      OPT_FLAG,  NULL, &cmdlineP->reversebits,
+            0);
+    OPTENT3(0,   "nofixedwidth",     OPT_FLAG,  NULL, &cmdlineP->nofixedwidth,
+            0);
+    OPTENT3(0,   "verbose",          OPT_FLAG,  NULL, &cmdlineP->verbose, 
+            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 */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->inputFileName = argv[1];
+}
+
+
+
+static void
+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) {
+
+    struct bitString retval;
+    retval.intBuffer = bits;
+    retval.bitCount  = bitCount;
+
+    return retval;
+}
+
+    
+
+static __inline__ void
+putbits(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).
+
+   Flush the buffer to stdout as necessary to make room.
+
+   'newBits' must be shorter than a whole word.
+   
+   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;
+        /* Number of bits of unused space (at the high end) in buffer */
+
+    assert(newBits.bitCount < sizeof(out.buffer.intBuffer) * 8);
+    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 { 
+        /* 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;
+
+        wordintBytes outbytes;
+        size_t rc;
+
+        wordintToBytes(&outbytes, 
+                       (out.buffer.intBuffer << spaceLeft) 
+                       | (newBits.intBuffer >> nextBufBitCount));
+        if (out.reverseBits)
+            reversebuffer(outbytes, sizeof(outbytes));
+            
+        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(bool const reverseBits) {
+    out.buffer.intBuffer = 0;
+    out.buffer.bitCount  = 0;
+    out.reverseBits = reverseBits;
+}
+
+
+
+static __inline__ void
+putcode(unsigned int const clr, 
+        unsigned int const ix) {
+
+    /* Note that this requires ttable to be aligned white entry, black
+       entry, white, black, etc.  
+    */
+    putbits(makeBs(ttable[ix * 2 + clr].code, ttable[ix * 2 + clr].length));
+}
+
+
+
+static __inline__ void
+putcode2(int const clr,
+         int const ix) {
+/*----------------------------------------------------------------------------
+   Output Make-up code and Terminating code at once.
+
+   For run lengths above TC_MC threshold (usually 64).
+
+   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)
+
+   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));
+    }
+}
+
+
+
+static __inline__ void
+putspan_normal(bit          const color, 
+               unsigned int const len) {
+
+    if (len < TC_MC)
+        putcode(color, len);
+    else if (len < 2624)
+        putcode2(color, len);
+    else {  /* len >= 2624 : rare */
+        unsigned int remainingLen;
+
+        for (remainingLen = len;
+             remainingLen >= 2624;
+             remainingLen -= 2623) {
+
+            putcode2(color, 2560+63);
+            putcode(!color, 0);
+        }
+        if (remainingLen < TC_MC)
+            putcode(color, remainingLen);
+        else  /* TC_MC <= len < 2624 */
+            putcode2(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
+puteol(void) {
+
+    if (pbmtorl)
+        puts("EOL");
+    else {
+        struct bitString const eol = {12, 1};
+            
+        putbits(eol);
+    }
+}
+
+
+
+/*
+  PBM raw bitrow to inflection point array
+
+  Write inflection (=color change) points into array milepost[].  
+  It is easy to calculate run length from this.
+
+  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.
+
+  WWWWWWWBBWWWWWWW ... = 7,2,7, ...
+  BBBBBWBBBBBWBBBB ... = 0,5,1,5,1,4, ...
+
+  Return the number of milepost elements written.
+  Note that max number of entries into milepost = cols+1 .
+
+  The inflection points are calculated like this:
+
+   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);
+    }
+    {
+
+        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; 
+            } 
+        }
+        if (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;
+    }
+}
+
+
+
+int
+main(int    argc,
+     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.
+        */
+     
+    int rows;
+    int cols;
+    int readcols;
+    int outwidth;
+    int format;
+    int row;
+    unsigned int * milepost;
+
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+     
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+    if (cmdline.nofixedwidth)
+        readcols = outwidth = cols;
+    else {
+        readcols = MIN(cols, 1728);
+        outwidth = 1728;
+    }
+
+    MALLOCARRAY_NOFAIL(bitrow, pbm_packed_bytes(cols) + sizeof(wordint));
+
+    MALLOCARRAY_NOFAIL(milepost, readcols + 1);
+
+    initOutStream(cmdline.reversebits);
+    puteol();
+
+    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();
+    }
+
+    free(milepost);
+    {
+        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");
+    }
+    pm_close(ifP);
+
+    return 0;
+}
diff --git a/converter/pbm/pbmtog3.test b/converter/pbm/pbmtog3.test
new file mode 100644
index 00000000..9ca45079
--- /dev/null
+++ b/converter/pbm/pbmtog3.test
@@ -0,0 +1,23 @@
+echo Test 1.  Should print 3697098186 144
+./pbmtog3 ../../testgrid.pbm | cksum
+echo Test 2.  Should print 1248301383 122
+./pbmtog3 -nofixedwidth ../../testgrid.pbm | cksum
+echo Test 3.  Should print 686713716 144
+./pbmtog3 -reverse ../../testgrid.pbm | cksum
+echo Test 4.  Should print 215463240 122
+./pbmtog3 -nofixedwidth -reverse ../../testgrid.pbm | cksum
+echo Test 5.  Should print 28792587 47
+pbmmake -w 10 10 | ./pbmtog3 | cksum
+echo Test 6.  Should print 277456854 32
+pbmmake -w 10 10 | ./pbmtog3 -nofixedwidth | cksum
+echo Test 7.  Should print 28792587 47
+pbmmake -w 10000 10 | ./pbmtog3 | cksum
+echo Test 8.  Should print 871281767 162
+pbmmake -w 10000 10 | ./pbmtog3 -nofixedwidth | cksum
+echo Test 9.  Should print 3736247115 62
+pbmmake -b 10 10 | ./pbmtog3 | cksum
+echo Test 10.  Should print 2820255307 2191856
+pbmmake -g 1700 2286 | ./pbmtog3 | cksum
+echo Test 11.  Should print 4159089282 2226575
+pbmmake -g 1800 2286 | ./pbmtog3 | cksum
+echo Tests done.
diff --git a/converter/pbm/pbmtogem.c b/converter/pbm/pbmtogem.c
new file mode 100644
index 00000000..59f2b9cf
--- /dev/null
+++ b/converter/pbm/pbmtogem.c
@@ -0,0 +1,252 @@
+/* pbmtogem.c - read a portable bitmap and produce a GEM .img file
+**
+** Author: David Beckemeyer (bdt!david)
+**
+** Much of the code for this program was taken from other
+** pbmto* programs.  I just modified the code to produce
+** a .img header and generate .img "Bit Strings".
+**
+** Thanks to Diomidis D. Spinellis for the .img header format.
+**
+** Copyright (C) 1988 by David Beckemeyer (bdt!david) and Jef Poskanzer.
+**
+** Modified by Johann Haider to produce Atari ST compatible files
+**
+** 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.
+*/
+/*
+*  92/07/11 jh
+*
+*  Changes made to this program:
+*  changed header length to count words to conform with Atari ST documentation
+*  removed rounding of the imagewidth to the next word boundary
+*  removed arbitrary limit to imagewidth
+*  changed pattern length to 1 to simplify locating of compressable parts
+*	in real world images
+*  add solid run and pattern run compression
+*
+*  Deficiencies:
+*  Compression of repeated scanlines not added
+*  
+*	Johann Haider (jh@fortec.tuwien.ac.at)
+*
+* 94/01/31 Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de)
+* Changed to remove architecture dependencies
+* Added compression of repeated scanlines
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "pbm.h"
+
+#define SOLID_0 0
+#define SOLID_1 0xff
+#define MINRUN 4
+#define putsolid(v,c) putc((v&0x80)|c, stdout)
+#define putpattern(v,c) putc(0, stdout);putc(c, stdout);putc(v, stdout)
+
+static void putinit ARGS ((int rows, int cols));
+static void putbit ARGS(( bit b ));
+static void putitem ARGS(( void ));
+static void putrow ARGS(( void ));
+static void flushrow ARGS ((void));
+static void putstring ARGS((register unsigned char *p, register int n));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, format, row, col;
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+
+    bitrow = pbm_allocrow( cols );
+
+    putinit (rows, cols);
+    for ( row = 0; row < rows; ++row )
+	{
+#ifdef DEBUG
+	fprintf (stderr, "row %d\n", row);
+#endif
+	pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    putbit( *bP );
+        putrow( );
+        }
+    flushrow ();
+
+    pm_close( ifp );
+
+
+    exit( 0 );
+    }
+
+static short item;
+static int outcol, outmax;
+static short bitsperitem, bitshift;
+static short linerepeat;
+static unsigned char *outrow, *lastrow;
+
+static void
+putinit (rows, cols)
+     int rows, cols;
+{
+  if (pm_writebigshort (stdout, (short) 1) == -1 /* Image file version */
+      || pm_writebigshort (stdout, (short) 8) == -1 /* Header length */
+      || pm_writebigshort (stdout, (short) 1) == -1 /* Number of planes */
+      || pm_writebigshort (stdout, (short) 1) == -1 /* Pattern length */
+      || pm_writebigshort (stdout, (short) 372) == -1 /* Pixel width */
+      || pm_writebigshort (stdout, (short) 372) == -1 /* Pixel height */
+      || pm_writebigshort (stdout, (short) cols) == -1
+      || pm_writebigshort (stdout, (short) rows) == -1)
+    pm_error ("write error");
+  item = 0;
+  bitsperitem = 0;
+  bitshift = 7;
+  outcol = 0;
+  outmax = (cols + 7) / 8;
+  outrow = (unsigned char *) pm_allocrow (outmax, sizeof (unsigned char));
+  lastrow = (unsigned char *) pm_allocrow (outmax, sizeof (unsigned char));
+  linerepeat = -1;
+}
+
+#if __STDC__
+static void
+putbit( bit b )
+#else /*__STDC__*/
+static void
+putbit( b )
+    bit b;
+#endif /*__STDC__*/
+    {
+    if ( bitsperitem == 8 )
+	putitem( );
+    ++bitsperitem;
+    if ( b == PBM_BLACK )
+	item += 1 << bitshift;
+    --bitshift;
+    }
+
+static void
+putitem( )
+    {
+    outrow[outcol++] = item;
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 7;
+    }
+
+static void
+putstring (p, n)
+register unsigned char *p;
+register int n;
+{
+#ifdef DEBUG
+    fprintf (stderr, "Bitstring, length: %d, pos %d\n", n, outcol);
+#endif
+    (void) putc((char) 0x80, stdout);     /* a Bit string */
+    (void) putc(n, stdout);	/* count */
+    fwrite( p, n, 1, stdout );
+}
+
+static void
+putrow( )
+{
+  if (bitsperitem > 0)
+    putitem ();
+  outcol = 0;
+  if (linerepeat == -1 || linerepeat == 255
+      || memcmp (outrow, lastrow, outmax) != 0)
+    {
+      unsigned char *temp;
+      if (linerepeat != -1) /* Unless first line */
+	flushrow ();
+      /* Swap the pointers */
+      temp = outrow; outrow = lastrow; lastrow = temp;
+      linerepeat = 1;
+    }
+  else
+    /* Repeated line */
+    linerepeat++;
+}
+
+static void
+flushrow( )
+    {
+    register unsigned char *outp, *p, *q;
+    register int count;
+    int col = outmax;
+
+    if (linerepeat > 1)
+      {
+	/* Put out line repeat count */
+	fwrite ("\0\0\377", 3, 1, stdout);
+	putchar (linerepeat);
+      }
+    for (outp = p = lastrow; col > 0;)
+    {
+	    for (q = p, count=0; (count < col) && (*q == *p); q++,count++);
+	    if (count > MINRUN)
+	    {
+		if (p > outp)
+		{
+		    putstring (outp, p-outp);
+		    outp = p;
+		}
+		col -= count;
+		switch (*p)
+		{
+		case SOLID_0:
+#ifdef DEBUG
+/*			if (outcol > 0) */
+			fprintf (stderr, "Solid run 0, length: %d\n", count);
+#endif
+			putsolid (SOLID_0, count);
+			break;
+
+		case SOLID_1:
+#ifdef DEBUG
+			fprintf (stderr, "Solid run 1, length: %d, pos %d\n", count, outcol);
+#endif
+			putsolid (SOLID_1, count);
+			break;
+		default:
+#ifdef DEBUG
+			fprintf (stderr, "Pattern run, length: %d\n", count);
+#endif
+			putpattern (*p, count);
+			break;
+		}
+		outp = p = q;
+	    }
+	    else
+	    {
+		p++;
+		col--;
+	    }
+    }		
+    if (p > outp)
+         putstring (outp, p-outp);
+    if (ferror (stdout))
+      pm_error ("write error");
+}
+
diff --git a/converter/pbm/pbmtogo.c b/converter/pbm/pbmtogo.c
new file mode 100644
index 00000000..b7c12373
--- /dev/null
+++ b/converter/pbm/pbmtogo.c
@@ -0,0 +1,309 @@
+/* pbmtogo.c - read a PBM image and produce a GraphOn terminal raster file
+**	
+**	Rev 1.1 was based on pbmtolj.c
+**
+**	Bo Thide', Swedish Institute of Space Physics, bt@irfu.se
+**				   
+**
+** $Log:	pbmtogo.c,v $
+ * Revision 1.5  89/11/25  00:24:12  00:24:12  root (Bo Thide)
+ * Bug found: The byte after 64 repeated bytes sometimes lost. Fixed.
+ * 
+ * Revision 1.4  89/11/24  14:56:04  14:56:04  root (Bo Thide)
+ * Fixed the command line parsing since pbmtogo now always uses 2D
+ * compression.  Added a few comments to the source.
+ * 
+ * Revision 1.3  89/11/24  13:43:43  13:43:43  root (Bo Thide)
+ * Added capability for > 63 repeated bytes and > 62 repeated lines in
+ * the 2D compression scheme.
+ * 
+ * Revision 1.2  89/11/15  01:04:47  01:04:47  root (Bo Thide)
+ * First version that works reasonably well with GraphOn 2D runlength
+ * encoding/compression.
+ * 
+ * Revision 1.1  89/11/02  23:25:25  23:25:25  root (Bo Thide)
+ * Initial revision
+ * 
+**
+** Copyright (C) 1988, 1989 by Jef Poskanzer, Michael Haberler, and Bo Thide'.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "pbm.h"
+
+#define BUFSIZE 132	/* GraphOn has 132 byte/1056 bit wide raster lines */
+#define REPEAT_CURRENT_LINE_MASK	0x00 
+#define SKIP_AND_PLOT_MASK		0x40 
+#define REPEAT_PLOT_MASK		0x80 
+#define PLOT_ARBITRARY_DATA_MASK	0xc0 
+#define MAX_REPEAT 64
+
+static unsigned char *scanlineptr;		/* Pointer to current scan line byte */
+
+static void putinit ARGS(( void ));
+static void putbit ARGS(( bit b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+     int argc;
+     char* argv[];
+{
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int argn, rows, cols, format, rucols, padright, row, col;
+    int nbyte, bytesperrow, ecount, ucount, nout, i, linerepeat;
+    int	olditem;
+    unsigned char oldscanline[BUFSIZE];
+    unsigned char newscanline[BUFSIZE];
+    unsigned char diff[BUFSIZE];
+    unsigned char buffer[BUFSIZE];
+    unsigned char outbuffer[2*(BUFSIZE+1)];	/* Worst case.  Should malloc */
+    const char* usage = "[-c] [pbmfile]";
+
+
+    pbm_init( &argc, argv );
+
+    argn = 2;
+
+    /* Check for flags. */
+    if (argc > argn + 1)
+      pm_usage(usage);
+
+    if (argc == argn)
+      ifp = pm_openr( argv[argn-1] );
+    else
+      ifp = stdin;
+
+    pbm_readpbminit(ifp, &cols, &rows, &format);
+    bitrow = pbm_allocrow(cols);
+
+    /* Round cols up to the nearest multiple of 8. */
+    rucols = ( cols + 7 ) / 8;
+    bytesperrow = rucols;       /* GraphOn uses bytes */
+    rucols = rucols * 8;
+    padright = rucols - cols;
+
+    for (i = 0; i < BUFSIZE; ++i )
+      buffer[i] = 0;
+    putinit();
+
+    /* Start donwloading screen raster */
+    printf("\033P0;1;0;4;1;%d;%d;1!R1/", rows, rucols);
+
+    linerepeat = 63; /*  63 means "Start new picture" */
+    nout = 0;  /* To prevent compiler warning */
+    for (row = 0; row < rows; row++) {
+        /* Store scan line data in the new scan line vector */
+        scanlineptr = newscanline;
+        pbm_readpbmrow(ifp, bitrow, cols, format);
+        /* Transfer raster graphics */
+        for (col = 0, bP = bitrow; col < cols; col++, bP++)
+          putbit(*bP);
+        for (col = 0; col < padright; col++)
+          putbit(0);
+        
+        /* XOR data from the new scan line with data from old scan line */
+        for (i = 0; i < bytesperrow; i++)
+          diff[i] = oldscanline[i]^newscanline[i];
+        
+        /*
+         ** If the difference map is different from current internal buffer,
+         ** encode the difference and put it in the output buffer. 
+         ** Else, increase the counter for the current buffer by one.
+         */
+        
+        if ((memcmp(buffer, diff, bytesperrow) != 0) || (row == 0)) {
+            /*    
+             **Since the data in the buffer has changed, send the
+             **scan line repeat count to cause the old line(s) to
+             **be plotted on the screen, copy the new data into
+             **the internal buffer, and reset the counters.  
+             */
+              
+            putchar(linerepeat);
+            for (i = 0; i < bytesperrow; ++i)
+              buffer[i] = diff[i];
+            nbyte = 0;          /* Internal buffer byte counter */
+            nout = 0;           /* Output buffer byte counter */
+              
+            /* Run length encode the new internal buffr (= difference map) */
+            while (TRUE) {
+                ucount = 0;     /* Unique items counter */
+                do              /* Find unique patterns */
+                  {
+                      olditem = buffer[nbyte++];
+                      ucount++;
+                  } while ((olditem != buffer[nbyte])
+                           && (ucount < MIN(bytesperrow, MAX_REPEAT)));
+                  
+                if ((ucount != MAX_REPEAT) && (nbyte != bytesperrow)) {
+                    /* Back up to the last truly unique pattern */
+                    ucount--;
+                    nbyte--;
+                }
+
+                if (ucount > 0) { 
+                    /* Output the unique patterns */
+                    outbuffer[nout++] = 
+                      (ucount-1) | PLOT_ARBITRARY_DATA_MASK;
+                    for (i = nbyte-ucount; i < nbyte; i++)
+                      outbuffer[nout++] = buffer[i];
+                }
+
+                /*
+                 ** If we already are at the end of the current scan
+                 ** line, skip the rest of the encoding and start
+                 ** with a new scan line.  
+                 */
+
+                if (nbyte >= bytesperrow)
+                  goto nextrow;
+                  
+                ecount = 0;     /* Equal items counter */
+                do              /* Find equal patterns */
+                  {
+                      olditem = buffer[nbyte++];
+                      ecount++;
+                  } while ((olditem == buffer[nbyte])
+                           && (ecount < MIN(bytesperrow, MAX_REPEAT)));
+                  
+                if (ecount > 1) {
+                    /* More than 1 equal pattern */
+                    if (olditem == '\0') {
+                        /* White patterns */
+                        if (nbyte >= bytesperrow-1) {
+                            /* No more valid data ahead */
+                            outbuffer[nout++] = 
+                              (ecount-2) | SKIP_AND_PLOT_MASK;
+                            outbuffer[nout++] = buffer[nbyte-1];
+                        } 
+                        else { 
+                            /* More valid data ahead */
+                            outbuffer[nout++] = 
+                              (ecount-1) | SKIP_AND_PLOT_MASK;
+                            outbuffer[nout++] = buffer[nbyte++];
+                        } 
+                    }
+                    else { 
+                        /* Non-white patterns */
+                        outbuffer[nout++] = (ecount-1) | REPEAT_PLOT_MASK;
+                        outbuffer[nout++] = olditem;
+                    } /* if (olditem == '\0') */
+                } /* if (ecount > 1) */
+                else
+                  nbyte--;      /* No equal items found */
+                  
+                if (nbyte >= bytesperrow)
+                  goto nextrow;
+            } /* while (TRUE) */
+              
+          nextrow: printf("%d/", nout+1); /* Total bytes to xfer = nout+1 */
+            fflush(stdout);
+
+            /* Output the plot data */
+            write(1, outbuffer, nout);
+
+            /* Reset the counters */
+            linerepeat = 0;
+        } else {
+            linerepeat++;
+            if (linerepeat == 62) /* 62 lines max, then restart */
+              {
+                  putchar(linerepeat);
+                  printf("%d/", nout+1);
+                  fflush(stdout);
+                  write(1, outbuffer, nout);
+                  linerepeat = 0;
+              }
+        }
+        
+        /* Now we are ready for a new scan line */
+        for (i = 0; i < bytesperrow; ++i)
+          oldscanline[i] = newscanline[i];
+    }
+    putchar(linerepeat);        /* For the last line(s) to be plotted */
+    pm_close(ifp);
+    putrest();
+    exit(0);
+}
+
+
+
+static int item, bitsperitem, bitshift;
+
+static void
+putinit()
+{
+  /* Enter graphics window */
+  printf("\0331");
+
+  /* Erase graphics window */
+  printf("\033\014");
+
+  /* Set graphics window in raster mode */
+  printf("\033r");
+
+  /* Select standard Tek coding **/
+  printf("\033[=11l");
+
+  bitsperitem = 1;
+  item = 0;
+  bitshift = 7;
+}
+
+#if __STDC__
+static void
+putbit(bit b)
+#else /*__STDC__*/
+static void
+putbit(b)
+bit b;
+#endif /*__STDC__*/
+{
+  if (b == PBM_BLACK)
+    item += 1 << bitshift;
+  bitshift--;
+  if (bitsperitem == 8)
+  {
+    putitem();
+    bitshift = 7;
+  }
+  bitsperitem++;
+}
+
+static void
+putrest()
+{
+  if (bitsperitem > 1)
+      putitem();
+
+  /* end raster downloading */
+  printf("\033\134");
+
+  /* Exit raster mode */
+  printf("\033t");
+
+  /* Exit graphics window
+  printf("\0332"); */
+}
+
+static void
+putitem()
+  {
+  *scanlineptr++ = item;
+  bitsperitem = 0;
+  item = 0;
+  }
diff --git a/converter/pbm/pbmtoibm23xx.c b/converter/pbm/pbmtoibm23xx.c
new file mode 100644
index 00000000..973f7de0
--- /dev/null
+++ b/converter/pbm/pbmtoibm23xx.c
@@ -0,0 +1,213 @@
+/*
+ * pbmtoibm23xx -- print pbm file on IBM 23XX printers
+ * Copyright (C) 2004  Jorrit Fahlke <jorrit@jorrit.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/*
+ * This prgram is primarily based on the description of Brothers PPDS
+ * emulation (see
+ * http://www.brother.de/download/send_file.cfm?file_name=guide_ibmpro.pdf).
+ * However, there are some differences.  Their document states that
+ * ESC J does linefeed in terms of 1/216" -- my printer clearly does
+ * it in terms of 1/240".  Also, the quick and the slow mode for
+ * double density printing really makes a difference on my printer,
+ * the result of printing tiger.ps in quick double density mode was
+ * worse than printing it in single density mode.
+ *
+ * If anyone Knows of any better documentation of the language used by
+ * the IBM 23XX or PPDS in general, please send a mail to
+ * Jö Fahlke <jorrit@jorrit.de>.
+ *
+ * All the graphics modes of the printer differ only in the resolution
+ * in x they provide (and how quick they do their job).  They print a
+ * line of 8 pixels height and variable widths.  The bitlines within
+ * the line are 1/60" apart, so that is the resolution you can
+ * normally achieve in y.  But the printer is able to do line feeds in
+ * terms of 1/240", so the trick to print in higher resolutions is to
+ * print in several interleaved passes, and do a line feed of 1/240"
+ * or 1/120" inbetween.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pbm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+struct cmdlineInfo {
+    unsigned char graph_mode;
+    unsigned int passes;
+    unsigned int nFiles;
+    const char ** inputFile;
+};
+
+
+bool sent_xon; /* We have send x-on to enable the printer already */
+
+
+
+
+static void
+parseCommandLine(int argc, char ** const argv,
+                 struct cmdlineInfo * const cmdlineP) {
+
+    optStruct3 opt;
+    optEntry option_def[100];
+
+    unsigned int option_def_index = 0;
+    unsigned int xresSpec, yresSpec;
+    unsigned int xres, yres;
+    unsigned int slowMode;
+
+    OPTENT3(0, "xres", OPT_UINT, &xres, &xresSpec, 0);
+    OPTENT3(0, "yres", OPT_UINT, &yres, &yresSpec, 0);
+    OPTENT3(0, "slow", OPT_FLAG, NULL,  &slowMode, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = 0;
+    opt.allowNegNum = 0;
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!xresSpec)
+        pm_error("You must specify the -xres option");
+    if (!yresSpec)
+        pm_error("You must specify the -yres option");
+
+    switch (xres) {
+    case  60: cmdlineP->graph_mode = 'K';                  break;
+    case 120: cmdlineP->graph_mode = slowMode ? 'L' : 'Y'; break;
+    case 240: cmdlineP->graph_mode = 'Z';                  break;
+    default:
+        pm_error("Please specify 60, 120, or 240 for -xres");
+    }
+
+    if (yres != 60 && yres != 120 && yres != 240)
+        pm_error("Please specify 60, 120, or 240 for -yres");
+
+    cmdlineP->passes = yres / 60;
+
+    cmdlineP->nFiles = MAX(argc-1, 1);
+    MALLOCARRAY_NOFAIL(cmdlineP->inputFile, cmdlineP->nFiles);
+
+    if (argc-1 < 1)
+        cmdlineP->inputFile[0] = "-";
+    else {
+        unsigned int i;
+        for (i = 0; i < argc-1; ++i)
+            cmdlineP->inputFile[i] = argv[i+1];
+    }
+}
+
+
+
+/* Read all pbm images from a filehandle and print them */
+static void 
+process_handle(FILE *        const fh,
+               unsigned char const graph_mode,
+               unsigned int  const passes) {
+    int eof;
+
+    while(pbm_nextimage(fh, &eof), eof == 0) {
+        /* pbm header dats */
+        int cols, rows, format;
+        /* iteration variables */
+        unsigned int x, y;
+        unsigned int bitline; /* pixel line within a sigle printing line */
+        unsigned int pass;
+        /* here we build the to-be-printed data */
+        unsigned char *output;  /* for reading one row from the file */
+        bit *row;
+
+        /* Enable printer in case it is disabled, do it only once */
+        if(!sent_xon) {
+            putchar(0x11);
+            sent_xon = TRUE;
+        }
+
+        pbm_readpbminit(fh, &cols, &rows, &format);
+
+        output = malloc(sizeof(*output) * cols * passes);
+        if(output == NULL)
+            pm_error("Out of memory");
+        row = pbm_allocrow(cols);
+
+        for(y = 0; y < rows; y += 8 * passes) {
+            memset(output, 0, sizeof(*output) * cols * passes);
+            for(bitline = 0; bitline < 8; ++bitline)
+                for(pass = 0; pass < passes; ++pass)
+                    /* don't read beyond the end of the image if
+                       height is not a multiple of passes 
+                    */
+                    if(y + bitline * passes + pass < rows) {
+                        pbm_readpbmrow(fh, row, cols, format);
+                        for(x = 0; x < cols; ++x)
+                            if(row[x] == PBM_BLACK)
+                                output[cols * pass + x] |= 1 << (7 - bitline);
+                    }
+            for(pass = 0; pass < passes; ++pass){
+                /* write graphics data */
+                putchar(0x1b); putchar(graph_mode);
+                putchar(cols & 0xff); putchar((cols >> 8) & 0xff);
+                fwrite(output + pass * cols, sizeof(*output), cols, stdout);
+
+                /* Carriage return */
+                putchar('\r');
+
+                /* move one pixel down */
+                putchar(0x1b); putchar('J'); putchar(4 / passes);
+            }
+
+            /* move one line - passes pixel down */
+            putchar(0x1b); putchar('J'); putchar(24 - 4);
+        }
+        putchar(0x0c); /* Form-feed */
+
+        pbm_freerow(row);
+        free(output);
+    }
+}
+
+
+
+int 
+main(int argc,char **argv) {
+
+  struct cmdlineInfo cmdline;
+  unsigned int i;
+
+  pbm_init(&argc, argv);
+
+  parseCommandLine(argc, argv, &cmdline);
+
+  sent_xon = FALSE;
+
+  for (i = 0; i < cmdline.nFiles; ++i) {
+      FILE *ifP;
+      pm_message("opening '%s'", cmdline.inputFile[i]);
+      ifP = pm_openr(cmdline.inputFile[i]);
+      process_handle(ifP, cmdline.graph_mode, cmdline.passes);
+      pm_close(ifP);
+  }
+
+  free(cmdline.inputFile);
+
+  return 0;
+}
diff --git a/converter/pbm/pbmtoicon.c b/converter/pbm/pbmtoicon.c
new file mode 100644
index 00000000..0e21c202
--- /dev/null
+++ b/converter/pbm/pbmtoicon.c
@@ -0,0 +1,134 @@
+/* pbmtoicon.c - read a portable bitmap and produce a Sun icon file
+**
+** Copyright (C) 1988 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "pbm.h"
+
+static void putinit ARGS(( void ));
+static void putbit ARGS(( bit b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, format, pad, padleft, padright, row, col;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    bitrow = pbm_allocrow( cols );
+    
+    /* Round cols up to the nearest multiple of 16. */
+    pad = ( ( cols + 15 ) / 16 ) * 16 - cols;
+    padleft = pad / 2;
+    padright = pad - padleft;
+
+    printf( "/* Format_version=1, Width=%d, Height=%d", cols + pad, rows );
+    printf( ", Depth=1, Valid_bits_per_item=16\n */\n" );
+
+    putinit( );
+    for ( row = 0; row < rows; ++row )
+	{
+	pbm_readpbmrow( ifp, bitrow, cols, format );
+	for ( col = 0; col < padleft; ++col )
+	    putbit( 0 );
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    putbit( *bP );
+	for ( col = 0; col < padright; ++col )
+	    putbit( 0 );
+        }
+
+    pm_close( ifp );
+
+    putrest( );
+
+    exit( 0 );
+    }
+
+static int item, bitsperitem, bitshift, itemsperline, firstitem;
+
+static void
+putinit( )
+    {
+    itemsperline = 0;
+    bitsperitem = 0;
+    item = 0;
+    bitshift = 15;
+    firstitem = 1;
+    }
+
+#if __STDC__
+static void
+putbit( bit b )
+#else /*__STDC__*/
+static void
+putbit( b )
+bit b;
+#endif /*__STDC__*/
+    {
+    if ( bitsperitem == 16 )
+	putitem( );
+    ++bitsperitem;
+    if ( b == PBM_BLACK )
+	item += 1 << bitshift;
+    --bitshift;
+    }
+
+static void
+putrest( )
+    {
+    if ( bitsperitem > 0 )
+	putitem( );
+    putchar( '\n' );
+    }
+
+static void
+putitem( )
+    {
+    const char* hexits = "0123456789abcdef";
+
+    if ( firstitem )
+	firstitem = 0;
+    else
+	putchar( ',' );
+    if ( itemsperline == 8 )
+	{
+	putchar( '\n' );
+	itemsperline = 0;
+	}
+    if ( itemsperline == 0 )
+	putchar( '\t' );
+    putchar( '0' );
+    putchar( 'x' );
+    putchar( hexits[item >> 12] );
+    putchar( hexits[( item >> 8 ) & 15] );
+    putchar( hexits[( item >> 4 ) & 15] );
+    putchar( hexits[item & 15] );
+    ++itemsperline;
+    bitsperitem = 0;
+    item = 0;
+    bitshift = 15;
+    }
diff --git a/converter/pbm/pbmtolj.c b/converter/pbm/pbmtolj.c
new file mode 100644
index 00000000..e8373050
--- /dev/null
+++ b/converter/pbm/pbmtolj.c
@@ -0,0 +1,564 @@
+/* pbmtolj.c - read a portable bitmap and produce a LaserJet bitmap file
+**  
+**  based on pbmtops.c
+**
+**  Michael Haberler HP Vienna mah@hpuviea.uucp
+**                 mcvax!tuvie!mah
+**  misfeatures: 
+**      no positioning
+**
+**      Bug fix Dec 12, 1988 :
+**              lines in putbit() reshuffled 
+**              now runs OK on HP-UX 6.0 with X10R4 and HP Laserjet II
+**      Bo Thide', Swedish Institute of Space Physics, Uppsala <bt@irfu.se>
+**
+**  Flags added December, 1993:
+**      -noreset to suppress printer reset code
+**      -float to suppress positioning code (such as it is)
+**  Wim Lewis, Seattle <wiml@netcom.com>
+**
+** Copyright (C) 1988 by Jef Poskanzer and Michael Haberler.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "pbm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include <string.h>
+#include <assert.h>
+
+static char *rowBuffer, *prevRowBuffer, *packBuffer, *deltaBuffer;
+static int rowBufferSize, rowBufferIndex, prevRowBufferIndex;
+static int packBufferSize, packBufferIndex;
+static int deltaBufferSize, deltaBufferIndex;
+
+static int item, bitsperitem, bitshift;
+
+
+
+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 dpi;
+    unsigned int copies;     /* number of copies */
+    unsigned int floating;   /* suppress the ``ESC & l 0 E'' ? */
+    unsigned int noreset;
+    unsigned int pack;       /* use TIFF packbits compression */
+    unsigned int delta;      /* use row-delta compression */
+};
+
+
+static void
+parseCommandLine(int argc, char ** 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.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+    unsigned int dpiSpec, copiesSpec, compressSpec;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0,   "resolution",  OPT_UINT, &cmdlineP->dpi, 
+            &dpiSpec, 0);
+    OPTENT3(0,   "copies",      OPT_UINT, &cmdlineP->copies,
+            &copiesSpec, 0);
+    OPTENT3(0,   "float",       OPT_FLAG, NULL,
+            &cmdlineP->floating, 0);
+    OPTENT3(0,   "noreset",     OPT_FLAG, NULL,
+            &cmdlineP->noreset, 0);
+    OPTENT3(0,   "packbits",    OPT_FLAG, NULL,
+            &cmdlineP->pack, 0);
+    OPTENT3(0,   "delta",       OPT_FLAG, NULL,
+            &cmdlineP->delta, 0);
+    OPTENT3(0,   "compress",    OPT_FLAG, NULL,
+            &compressSpec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFilename = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->inputFilename = argv[1];
+
+    if (!dpiSpec)
+        cmdlineP->dpi = 75;
+    if (!copiesSpec)
+        cmdlineP->copies = 1;
+    if (compressSpec) {
+        cmdlineP->pack = 1;
+        cmdlineP->delta = 1;
+    }
+}
+
+
+
+static void
+allocateBuffers(unsigned int const cols) {
+
+    rowBufferSize = (cols + 7) / 8;
+    packBufferSize = rowBufferSize + (rowBufferSize + 127) / 128 + 1;
+    deltaBufferSize = rowBufferSize + rowBufferSize / 8 + 10;
+
+    MALLOCARRAY_NOFAIL(prevRowBuffer, rowBufferSize);
+    MALLOCARRAY_NOFAIL(rowBuffer, rowBufferSize);
+    MALLOCARRAY_NOFAIL(packBuffer, packBufferSize);
+    MALLOCARRAY_NOFAIL(deltaBuffer, deltaBufferSize);
+}
+
+
+
+static void
+freeBuffers(void) {
+
+    free(deltaBuffer);
+    free(packBuffer);
+    free(rowBuffer);
+    free(prevRowBuffer);
+}
+
+
+
+static void
+putinit(struct cmdlineInfo const cmdline) {
+    if (!cmdline.noreset) {
+        /* Printer reset. */
+        printf("\033E");
+    }
+
+    if (cmdline.copies > 1) {
+        /* number of copies */
+        printf("\033&l%dX", cmdline.copies);
+    }
+    if (!cmdline.floating) {
+        /* Ensure top margin is zero */
+        printf("\033&l0E");
+    }
+
+    /* Set raster graphics resolution */
+    printf("\033*t%dR", cmdline.dpi);
+
+    /* Start raster graphics, relative adressing */
+    printf("\033*r1A");
+
+    bitsperitem = 1;
+    item = 0;
+    bitshift = 7;
+}
+
+
+
+static void
+putitem(void) {
+    assert(rowBufferIndex < rowBufferSize);
+    rowBuffer[rowBufferIndex++] = item;
+    bitsperitem = 0;
+    item = 0;
+}
+
+
+
+static void
+putbit(bit const b) {
+
+    if (b == PBM_BLACK)
+        item += 1 << bitshift;
+
+    --bitshift;
+
+    if (bitsperitem == 8) {
+        putitem();
+        bitshift = 7;
+    }
+    ++bitsperitem;
+}
+
+
+
+static void
+putflush(void) {
+    if (bitsperitem > 1)
+        putitem();
+}
+
+
+
+static void
+putrest(bool const reset) {
+    /* end raster graphics */
+    printf("\033*rB");
+
+    if (reset) {
+        /* Printer reset. */
+        printf("\033E");
+    }
+}
+
+
+
+static void
+packbits(void) {
+    int ptr, litStart, runStart, thisByte, startByte, chew, spit;
+    packBufferIndex = 0;
+    ptr = 0;
+    while (ptr < rowBufferIndex) {
+        litStart = ptr;
+        runStart = ptr;
+        startByte = rowBuffer[ptr];
+        ++ptr;
+        while (ptr < rowBufferIndex) {
+            thisByte = rowBuffer[ptr];
+            if (thisByte != startByte) {
+                if (ptr - runStart > 3) {
+                    /* found literal after nontrivial run */
+                    break;
+                }
+                startByte = thisByte;
+                runStart = ptr;
+            }
+            ++ptr;
+        }
+        /*
+          We drop out here after having found a [possibly empty]
+          literal, followed by a [possibly degenerate] run of repeated
+          bytes.  Degenerate runs can occur at the end of the scan line...
+          there may be a "repeat" of 1 byte (which can't actually be 
+          represented as a repeat) so we simply fold it into the previous
+          literal.
+        */
+        if (runStart == rowBufferIndex - 1) {
+            runStart = rowBufferIndex;
+        }
+        /*
+          Spit out the leading literal if it isn't empty
+        */
+        chew = runStart - litStart;
+        while (chew > 0) {
+            spit = (chew > 127) ? 127 : chew;
+            packBuffer[packBufferIndex++] = (char) (spit - 1);
+            memcpy(packBuffer+packBufferIndex, rowBuffer+litStart, spit);
+            packBufferIndex += spit;
+            litStart += spit;
+            chew -= spit;
+        }
+        /*
+          Spit out the repeat, if it isn't empty
+        */
+        chew = ptr - runStart;
+        while (chew > 0) {
+            spit = (chew > 128) ? 128 : chew;
+            if (chew == spit + 1) {
+                spit--; /* don't leave a degenerate run at the end */
+            }
+            if (spit == 1) {
+                fprintf(stderr, "packbits created a degenerate run!\n");
+            }
+            packBuffer[packBufferIndex++] = (char) -(spit - 1);
+            packBuffer[packBufferIndex++] = startByte;
+            chew -= spit;
+        }
+    }
+}
+
+
+
+static void
+deltarow(void) {
+    int burstStart, burstEnd, burstCode, mustBurst, ptr, skip, skipped, code;
+    deltaBufferIndex = 0;
+    if (memcmp(rowBuffer, prevRowBuffer, rowBufferIndex) == 0) {
+        return; /* exact match, no deltas required */
+    }
+    ptr = 0;
+    skipped = 0;
+    burstStart = -1;
+    burstEnd = -1;
+    mustBurst = 0;
+    while (ptr < rowBufferIndex) {
+        skip = 0;
+        if (ptr == 0 || skipped == 30 || rowBuffer[ptr] != prevRowBuffer[ptr]
+            || (burstStart != -1 && ptr == rowBufferIndex - 1)) {
+            /* we want to output this byte... */
+            if (burstStart == -1) {
+                burstStart = ptr;
+            }
+            if (ptr - burstStart == 7 || ptr == rowBufferIndex - 1) {
+                /* we have to output it now... */
+                burstEnd = ptr;
+                mustBurst = 1;
+            }
+        } else {
+            /* duplicate byte, we can skip it */
+            if (burstStart != -1) {
+                burstEnd = ptr - 1;
+                mustBurst = 1;
+            }
+            skip = 1;
+        }
+        if (mustBurst) {
+            burstCode = burstEnd - burstStart; 
+                /* 0-7, means 1-8 bytes follow */
+            code = (burstCode << 5) | skipped;
+            deltaBuffer[deltaBufferIndex++] = (char) code;
+            memcpy(deltaBuffer+deltaBufferIndex, rowBuffer+burstStart, 
+                   burstCode + 1);
+            deltaBufferIndex += burstCode + 1;
+            burstStart = -1;
+            burstEnd = -1;
+            mustBurst = 0;
+            skipped = 0;
+        }
+        if (skip) {
+            ++skipped;
+        }
+        ++ptr;
+    }
+}
+
+
+
+static void
+findRightmostBlackCol(const bit *    const bitrow,
+                      unsigned int   const cols,
+                      bool *         const allWhiteP,
+                      unsigned int * const blackColP) {
+
+    int i;
+
+    for (i = cols - 1; i >= 0 && bitrow[i] == PBM_WHITE; --i);
+
+    if (i < 0)
+        *allWhiteP = TRUE;
+    else {
+        *allWhiteP = FALSE;
+        *blackColP = i;
+    }
+}
+
+
+
+static void
+convertRow(const bit *    const bitrow,
+           unsigned int   const cols,
+           bool           const pack,
+           bool           const delta,
+           bool *         const rowIsBlankP) {
+
+    unsigned int rightmostBlackCol;
+        
+    findRightmostBlackCol(bitrow, cols, rowIsBlankP, &rightmostBlackCol);
+
+    if (!*rowIsBlankP) {
+        unsigned int const nzcol = rightmostBlackCol + 1;
+            /* Number of columns excluding white right margin */
+        unsigned int const rucols = ((nzcol + 7) / 8) * 8;
+            /* 'nzcol' rounded up to nearest multiple of 8 */
+        
+        unsigned int col;
+
+        memset(rowBuffer, 0, rowBufferSize);
+
+        rowBufferIndex = 0;
+
+        /* Generate the unpacked data */
+
+        for (col = 0; col < nzcol; ++col)
+            putbit(bitrow[col]);
+
+        /* Pad out to a full byte with white */
+        for (col = nzcol; col < rucols; ++col)
+            putbit(0);
+
+        putflush();
+
+        /* Try optional compression algorithms */
+
+        if (pack)
+            packbits();
+        else
+            packBufferIndex = rowBufferIndex + 999;
+
+        if (delta) {
+            /* May need to temporarily bump the row buffer index up to
+               whatever the previous line's was - if this line is shorter 
+               than the previous would otherwise leave dangling cruft.
+            */
+            unsigned int const savedRowBufferIndex = rowBufferIndex;
+
+            if (rowBufferIndex < prevRowBufferIndex)
+                rowBufferIndex = prevRowBufferIndex;
+
+            deltarow();
+
+            rowBufferIndex = savedRowBufferIndex;
+        } else
+            deltaBufferIndex = packBufferIndex + 999;
+    }
+}
+
+
+    
+static void
+printBlankRows(unsigned int const count) {
+
+    if (count > 0) {
+        unsigned int x;
+        /* The code used to be this, but Charles Howes reports that
+           this escape sequence does not exist on his HP Laserjet IIP
+           plus, so we use the following less elegant code instead.
+           
+           printf("\033*b%dY", (*blankRowsP)); 
+        */
+        for (x = 0; x < count; ++x) 
+            printf("\033*b0W");
+        
+        memset(prevRowBuffer, 0, rowBufferSize);
+    }
+}
+
+
+
+static void
+establishMode(int const newMode) {
+
+    static int mode = -1;
+
+    if (mode != newMode) {
+        printf("\033*b%uM", newMode);
+        mode = newMode;
+    }
+}
+
+
+
+static void
+printRow(void) {
+
+    if (deltaBufferIndex < packBufferIndex &&
+        deltaBufferIndex < rowBufferIndex) {
+        assert(deltaBufferIndex <= deltaBufferSize);
+        /*
+          It's smallest when delta'ed
+        */
+        establishMode(3);
+
+        printf("\033*b%dW", deltaBufferIndex);
+        fwrite(deltaBuffer, 1, deltaBufferIndex, stdout);
+    } else if (rowBufferIndex <= packBufferIndex) {
+        assert (rowBufferIndex <= rowBufferSize);
+        /*
+          It didn't pack - send it unpacked
+        */
+        establishMode(0);
+
+        printf("\033*b%dW", rowBufferIndex);
+        fwrite(rowBuffer, 1, rowBufferIndex, stdout);
+    } else {
+        assert (packBufferIndex <= packBufferSize);
+        /*
+          It's smaller when packed
+        */
+        establishMode(2);
+
+        printf("\033*b%dW", packBufferIndex);
+        fwrite(packBuffer, 1, packBufferIndex, stdout);
+    }
+    memcpy(prevRowBuffer, rowBuffer, rowBufferSize);
+    prevRowBufferIndex = rowBufferIndex;
+}
+
+
+
+static void
+doPage(FILE *             const ifP, 
+       struct cmdlineInfo const cmdline) {
+
+    bit * bitrow;
+    int rows, cols, format, row;
+    unsigned int blankRows;
+    bool rowIsBlank;
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    bitrow = pbm_allocrow(cols);
+
+    allocateBuffers(cols);
+
+    putinit(cmdline);
+
+    blankRows = 0;
+    prevRowBufferIndex = 0;
+    memset(prevRowBuffer, 0, rowBufferSize);
+
+    for (row = 0; row < rows; ++row) {
+        pbm_readpbmrow(ifP, bitrow, cols, format);
+
+        convertRow(bitrow, cols, cmdline.pack, cmdline.delta,
+                   &rowIsBlank);
+
+        if (rowIsBlank)
+            ++blankRows;
+        else {
+            printBlankRows(blankRows);
+            blankRows = 0;
+            
+            printRow();
+        }
+    }    
+    printBlankRows(blankRows);
+    blankRows = 0;
+
+    putrest(!cmdline.noreset);
+
+    freeBuffers();
+    pbm_freerow(bitrow);
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    bool eof;
+
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilename);
+
+    eof = FALSE;
+    while (!eof) {
+        doPage(ifP, cmdline);
+        pbm_nextimage(ifP, &eof);
+    }
+
+    pm_close(ifP);
+
+    return 0;
+}
diff --git a/converter/pbm/pbmtoln03.c b/converter/pbm/pbmtoln03.c
new file mode 100644
index 00000000..07c8629f
--- /dev/null
+++ b/converter/pbm/pbmtoln03.c
@@ -0,0 +1,260 @@
+/*
+From tim@deakin.edu.au Fri May  7 00:18:57 1993
+From: Tim Cook <tim@deakin.edu.au>
+Date: Fri, 7 May 1993 15:18:34 -0500
+X-Mailer: Mail User's Shell (7.2.4 2/2/92)
+To: dyson@sunfish.Physics.UIowa.Edu
+Subject: Re: DEC LN03+ printer (not postscript) under SunOS (on SS10) ?
+Content-Length: 5893
+
+In a message dated 6 May,  9:32, dyson@sunfish.Physics.UIowa.Edu
+(Richard L. Dyson) wrote:
+> > Just in case anyone is interested, I have a pbmtoln03 utility I wrote
+> > when I was mucking about with an LN03+.  If you are interested in
+> > printing your bitmaps at 300x300dpi, ask me for the source.
+>
+> I would be interested.  We still only have LN03+ printers on our VMS
+> machines here...
+
+Ok, here goes.  Note that you will need the source from the pbmplus
+utilities, because my pbmtoln03 utility uses the library routines that
+are a part of pbmplus to read a PBM file (I linked it with libpbm.a).
+I have not tested this utility on VMS, but it looks like it should
+work.
+*/
+
+/* pbmtoln03.c -        Converts a PBM bitmap to DEC LN03 SIXEL bitmap
+ *
+ * SYNOPSIS
+ *      pbmtoln03 [pbm-file]
+ *
+ * OPTIONS
+ *      -l nn   Use "nn" as value for left margin (default 0).
+ *      -r nn   Use "nn" as value for right margin (default 2400).
+ *      -t nn   Use "nn" as value for top margin (default 0).
+ *      -b nn   Use "nn" as value for bottom margin (default 3400).
+ *      -f nn   Use "nn" as value for form length (default 3400).
+ *
+ * Tim Cook, 26 Feb 1992
+ * changed option parsing to PBM standards  - Ingo Wilken, 13 Oct 1993
+ */
+
+#include <stdio.h>
+#include "pbm.h"
+
+FILE *input ;
+
+#ifndef print
+#define print(s)        fputs (s, stdout)
+#define fprint(f, s)    fputs (s, f)
+#endif
+
+
+static void 
+output_sixel_record (unsigned char * record, int width) {
+
+   int i, j, k ;
+   unsigned char last_char ;
+   int start_repeat = 0 ;
+   int repeated ;
+   char repeated_str[16] ;
+   char *p ;
+
+   /* Do RLE */
+   last_char = record[0] ;
+   j = 0 ;
+
+   /* This will make the following loop complete */
+   record[width] = '\0' ;
+
+   for (i = 1 ; i <= width ; i++) {
+
+      repeated = i - start_repeat ;
+
+      if (record[i] != last_char || repeated >= 32766) {
+
+         /* Repeat has ended */
+
+         if (repeated > 3) {
+
+            /* Do an encoding */
+            record[j++] = '!' ;
+            sprintf (repeated_str, "%d", i - start_repeat) ;
+            for (p = repeated_str ; *p ; p++)
+               record[j++] = *p ;
+               record[j++] = last_char ; }
+
+         else {
+            for (k = 0 ; k < repeated ; k++)
+               record[j++] = last_char ; }
+
+         start_repeat = i ;
+         last_char = record[i] ; }
+      }
+
+   fwrite ((char *) record, j, 1, stdout) ;
+   putchar ('-') ;      /* DECGNL (graphics new-line) */
+   putchar ('\n') ;
+   }
+
+
+static void 
+convert (int width, int height, int format) {
+
+   int i ;
+   unsigned char *sixel ;               /* A row of sixels */
+   int sixel_row ;
+
+   bit *row = pbm_allocrow (width) ;
+
+   sixel = (unsigned char *) malloc (width + 2) ;
+
+   sixel_row = 0 ;
+   while (height--) {
+      pbm_readpbmrow (input, row, width, format) ;
+      switch (sixel_row) {
+         case 0 :
+           for (i = 0 ; i < width ; i++)
+              sixel[i] = row[i] ;
+           break ;
+         case 1 :
+           for (i = 0 ; i < width ; i++)
+              sixel[i] += row[i] << 1 ;
+           break ;
+         case 2 :
+           for (i = 0 ; i < width ; i++)
+              sixel[i] += row[i] << 2 ;
+           break ;
+         case 3 :
+           for (i = 0 ; i < width ; i++)
+              sixel[i] += row[i] << 3 ;
+           break ;
+         case 4 :
+           for (i = 0 ; i < width ; i++)
+              sixel[i] += row[i] << 4 ;
+           break ;
+         case 5 :
+           for (i = 0 ; i < width ; i++)
+              sixel[i] += (row[i] << 5) + 077 ;
+           output_sixel_record (sixel, width) ;
+           break ; }
+      if (sixel_row == 5)
+         sixel_row = 0 ;
+      else
+         sixel_row++ ;
+      }
+
+   if (sixel_row > 0) {
+      /* Incomplete sixel record needs to be output */
+      for (i = 0 ; i < width ; i++)
+         sixel[i] += 077 ;
+      output_sixel_record (sixel, width) ; }
+   }
+
+
+
+int
+main (int argc, char **argv) {
+   int argc_copy = argc ;
+   char **argv_copy = argv ;
+   int argn;
+   const char * const usage = "[-left <nn>] [-right <nn>] [-top <nn>] "
+       "[-bottom <nn>] [-formlength <nn>] [pbmfile]";
+
+   /* Options */
+   /* These defaults are for a DEC LN03 with A4 paper (2400x3400 pixels) */
+   const char *opt_left_margin = "0";
+   const char *opt_top_margin = opt_left_margin;
+   const char *opt_right_margin = "2400";
+   const char *opt_bottom_margin = "3400";
+   const char *opt_form_length = opt_bottom_margin;
+
+   int width, height, format ;
+
+   pbm_init (&argc_copy, argv_copy) ;
+
+   argn = 1;
+   while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) {
+      if( pm_keymatch(argv[argn], "-left", 2) ) {
+         if( ++argn >= argc )
+            pm_usage(usage);
+         opt_left_margin = argv[argn];
+      }
+      else
+      if( pm_keymatch(argv[argn], "-right", 2) ) {
+         if( ++argn >= argc )
+            pm_usage(usage);
+         opt_right_margin = argv[argn];
+      }
+      else
+      if( pm_keymatch(argv[argn], "-top", 2) ) {
+         if( ++argn >= argc )
+            pm_usage(usage);
+         opt_top_margin = argv[argn];
+      }
+      else
+      if( pm_keymatch(argv[argn], "-bottom", 2) ) {
+         if( ++argn >= argc )
+            pm_usage(usage);
+         opt_bottom_margin = argv[argn];
+      }
+      else
+      if( pm_keymatch(argv[argn], "-formlength", 2) ) {
+         if( ++argn >= argc )
+            pm_usage(usage);
+         opt_form_length = argv[argn];
+      }
+      else
+         pm_usage(usage);
+      ++argn;
+   }
+
+   if( argn < argc ) {
+      input = pm_openr( argv[argn] );
+      argn++;
+   }
+   else
+      input = stdin;
+
+   if( argn != argc )
+      pm_usage(usage);
+
+
+   /* Initialize pbm file */
+   pbm_readpbminit (input, &width, &height, &format) ;
+
+   if (format != PBM_FORMAT && format != RPBM_FORMAT)
+      pm_error ("input not in PBM format") ;
+
+/*
+ * In explanation of the sequence below:
+ *      <ESC>[!p        DECSTR  soft terminal reset
+ *      <ESC>[11h       PUM     select unit of measurement
+ *      <ESC>[7 I       SSU     select pixel as size unit
+ *      <ESC>[?52l      DECOPM  origin is corner of printable area
+ *      <ESC>[%s;%ss    DECSLRM left and right margins
+ *      <ESC>[%s;%sr    DECSTBM top and bottom margins
+ *      <ESC>[%st       DECSLPP form length
+ *      <ESC>P0;0;1q            select sixel graphics mode
+ *      "1;1            DECGRA  aspect ratio (1:1)
+ */
+
+   /* Initialize sixel file */
+   printf ("\033[!p\033[11h\033[7 I\033[?52l\033[%s;%ss\033"
+           "[%s;%sr\033[%st\033P0;0;1q\"1;1",
+      opt_left_margin, opt_right_margin, opt_top_margin, opt_bottom_margin,
+      opt_form_length);
+
+   /* Convert data */
+   convert (width, height, format) ;
+
+   /* Terminate sixel data */
+   print ("\033\\\n") ;
+
+   /* If the program failed, it previously aborted with nonzero completion
+      code, via various function calls.
+   */
+   return 0;
+}
+
+
diff --git a/converter/pbm/pbmtolps.c b/converter/pbm/pbmtolps.c
new file mode 100644
index 00000000..13a14e2b
--- /dev/null
+++ b/converter/pbm/pbmtolps.c
@@ -0,0 +1,181 @@
+/*
+ * pbmtolps -- convert a Portable BitMap into Postscript.  The
+ * output Postscript uses lines instead of the image operator to
+ * generate a (device dependent) picture which will be imaged
+ * much faster.
+ *
+ * The Postscript path length is constrained to be less that 1000
+ * points so that no limits are overrun on the Apple Laserwriter
+ * and (presumably) no other printers.
+ *
+ * To do:
+ *      make sure encapsulated format is correct
+ *      repitition of black-white strips
+ *      make it more device independent (is this possible?)
+ *
+ * Author:
+ *      George Phillips <phillips@cs.ubc.ca>
+ *      Department of Computer Science
+ *      University of British Columbia
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include "nstring.h"
+#include "pbm.h"
+
+
+static int prev_white = -1;
+static int prev_black = -1;
+static char cmd = '\0';
+static int pointcount = 2;
+
+#ifdef RUN
+static int run = 1;
+#endif
+
+static char 
+morepoints(char cmd, int howmany_pbmtolps) {
+    pointcount += 2;
+    if (pointcount > 1000) {
+        pointcount = 2;
+        cmd += 'm' - 'a';
+    }
+    return(cmd);
+}
+
+
+
+static void 
+addstrip(int const white, 
+         int const black) {
+
+    if (cmd) {
+#ifdef RUN
+        if (white == prev_white && black == prev_black)
+            run++;
+        else {
+            if (run == 1)
+#endif
+                printf("%d %d %c ", 
+                       prev_black, prev_white, morepoints(cmd, 2));
+#ifdef RUN
+            else
+                                /* of course, we need to give a new command */
+                printf("%d %d %d %c ",
+                       prev_white, prev_black, run,
+                       morepoints(cmd + 'f' - 'a', 2 * run));
+            run = 1;
+        }
+#endif
+    }
+
+    prev_white = white;
+    prev_black = black;
+    cmd = 'a';
+}
+
+
+
+static void 
+nextline(void) {
+    /* need to check run, should have an outcommand */
+    if (cmd)
+        printf("%d %d %c\n", prev_black, prev_white, morepoints('c', 3));
+    else
+        printf("%c\n", morepoints('b', 1));
+    cmd = '\0';
+}
+
+
+
+int
+main(int argc, char ** argv) {
+    FILE*   fp;
+    bit*    bits;
+    int             row;
+    int             col;
+    int         rows;
+    int             cols;
+    int             format;
+    int             white;
+    int             black;
+    const char*   name;
+    float   dpi = 300.0;
+    float   sc_rows;
+    float   sc_cols;
+    int             i;
+    const char*   const usage = "[ -dpi n ] [ pbmfile ]";
+
+
+	pbm_init(&argc, argv);
+
+    i = 1;
+    if (i < argc && STREQ(argv[i], "-dpi")) {
+        if (i == argc - 1)
+            pm_usage(usage);
+        sscanf(argv[i + 1], "%f", &dpi);
+        i += 2;
+    }
+
+    if (i < argc - 1)
+        pm_usage(usage);
+
+    if (i == argc) {
+        name = "noname";
+        fp = stdin;
+    } else {
+        name = argv[i];
+        fp = pm_openr(name);
+    }
+    pbm_readpbminit(fp, &cols, &rows, &format);
+    bits = pbm_allocrow(cols);
+
+    sc_rows = (float)rows / dpi * 72.0;
+    sc_cols = (float)cols / dpi * 72.0;
+
+    puts("%!PS-Adobe-2.0 EPSF-2.0");
+    puts("%%Creator: pbmtolps");
+    printf("%%%%Title: %s\n", name);
+    printf("%%%%BoundingBox: %d %d %d %d\n",
+           (int)(305.5 - sc_cols / 2.0),
+           (int)(395.5 - sc_rows / 2.0),
+           (int)(306.5 + sc_cols / 2.0),
+           (int)(396.5 + sc_rows / 2.0));
+    puts("%%EndComments");
+    puts("%%EndProlog");
+    puts("gsave");
+
+    printf("%f %f translate\n", 306.0 - sc_cols / 2.0, 396.0 + sc_rows / 2.0);
+    printf("72 %f div dup neg scale\n", dpi);
+    puts("/a { 0 rmoveto 0 rlineto } def");
+    puts("/b { 0 row 1 add dup /row exch def moveto } def");
+    puts("/c { a b } def");
+    puts("/m { currentpoint stroke newpath moveto a } def");
+    puts("/n { currentpoint stroke newpath moveto b } def");
+    puts("/o { currentpoint stroke newpath moveto c } def");
+    puts("/row 0 def");
+    puts("newpath 0 0 moveto");
+
+    for (row = 0; row < rows; row++) {
+        pbm_readpbmrow(fp, bits, cols, format);
+        /* output white-strip+black-strip sequences */
+        for (col = 0; col < cols; ) {
+            for (white = 0; col < cols && bits[col] == PBM_WHITE; col++)
+                white++;
+            for (black = 0; col < cols && bits[col] == PBM_BLACK; col++)
+                black++;
+
+            if (black != 0)
+                addstrip(white, black);
+        }
+        nextline();
+    }
+    puts("stroke grestore showpage");
+    puts("%%Trailer");
+
+    pm_close(fp);
+
+    exit(0);
+}
diff --git a/converter/pbm/pbmtomacp.c b/converter/pbm/pbmtomacp.c
new file mode 100644
index 00000000..82b55904
--- /dev/null
+++ b/converter/pbm/pbmtomacp.c
@@ -0,0 +1,301 @@
+/* pbmtomacp.c - read a portable bitmap and produce a MacPaint bitmap file
+**
+** Copyright (C) 1988 by Douwe vand der Schaaf.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <string.h>
+
+#include "pbm.h"
+#include "macp.h"
+
+#define TRUE		1
+#define FALSE		0
+#define EQUAL		1
+#define UNEQUAL		0
+
+static void fillbits ARGS(( bit **bits, bit **bitsr, int top, int left, int bottom, int right ));
+static void writemacp ARGS(( bit **bits ));
+static int packit ARGS(( bit *pb, bit *bits ));
+static void filltemp ARGS(( bit *dest, bit *src ));
+static void sendbytes ARGS(( bit *pb, register int npb ));
+static void header ARGS(( void ));
+
+static FILE *fdout;
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{ FILE *ifp;
+  register bit **bits, **bitsr;
+  int argn, rows, cols;
+  int left,bottom,right,top;
+  int lflg, rflg, tflg, bflg;
+  char name[100];
+  const char * const usage = "[-l left] [-r right] [-b bottom] [-t top] [pbmfile]";
+
+
+  pbm_init( &argc, argv );
+
+  argn = 1;
+  fdout = stdout;
+  lflg = rflg = tflg = bflg = 0;
+  left = right = top = bottom = 0;  /* To quiet compiler warning */
+
+  while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+  { switch ( argv[argn][1] )
+    { case 'l':
+      lflg++;
+      argn++;
+      left = atoi( argv[argn] );
+      break;
+
+      case 'r':
+      rflg++;
+      argn++;
+      right = atoi( argv[argn] );
+      break;
+
+      case 't':
+      tflg++;
+      argn++;
+      top = atoi( argv[argn] );
+      break;
+
+      case 'b':
+      bflg++;
+      argn++;
+      bottom = atoi( argv[argn] );
+      break;
+
+      case '?':
+      default:
+      pm_usage( usage );
+    }
+    ++argn;
+  }
+
+  if ( argn == argc )
+  { ifp = stdin;
+    strcpy( name, "noname" );
+  }
+  else
+  { ifp = pm_openr( argv[argn] );
+    strcpy( name, argv[argn] );
+    ++argn;
+  }
+
+  if ( argn != argc )
+    pm_usage( usage );
+
+  bitsr = pbm_readpbm( ifp, &cols, &rows );
+
+  pm_close( ifp );
+
+  bits = pbm_allocarray( MAX_COLS, MAX_LINES );
+
+  if( !lflg )
+    left = 0;
+
+  if( rflg )
+  { if( right - left >= MAX_COLS )
+      right = left + MAX_COLS - 1;
+  }
+  else
+    right = ( left + MAX_COLS > cols ) ? ( cols - 1 ) : ( left + MAX_COLS - 1 );
+
+  if( !tflg )
+    top = 0;
+
+  if( bflg )
+  { if( bottom - top >= MAX_LINES )
+      bottom = top + MAX_LINES - 1;
+  }
+  else
+    bottom = ( top + MAX_LINES > rows ) ?
+		   ( rows - 1 ) : ( top + MAX_LINES - 1 );
+  
+    if( right <= left || left < 0 || right - left + 1 > MAX_COLS )
+      pm_error("error in right (= %d) and/or left (=%d)",right,left );
+    if( bottom <= top || top < 0 || bottom - top + 1 > MAX_LINES )
+      pm_error("error in bottom (= %d) and/or top (=%d)",bottom,top );
+
+  fillbits( bits, bitsr, top, left, bottom, right );
+
+  writemacp( bits );
+
+  exit( 0 );
+
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* centreer het over te zenden plaatje in het MacPaint document
+ *
+ * Het plaatje wordt vanaf al of niet opgegeven (left, bottom)
+ * in een pbm bitmap van de juist macpaint afmetingen gezet,
+ * en eventueel afgekapt.
+ */
+static void
+fillbits( bits, bitsr, top, left, bottom, right )
+bit **bits, **bitsr;
+int top, left, bottom, right;
+{ register bit *bi, *bir;
+  register int i, j;
+  register int bottomr, leftr, topr, rightr;
+  int width, height;
+
+  width = right - left + 1;
+  leftr = (MAX_COLS - width) / 2;
+  rightr = leftr + width - 1;
+
+  height = bottom - top + 1;
+  topr = ( MAX_LINES - height ) / 2;
+  bottomr = topr + height - 1;
+
+  for( i = 0; i < topr; i++ )
+  { bi = bits[i];
+    for( j = 0; j < MAX_COLS; j++ )
+      *bi++ = 0;
+  }
+
+  for( i = topr; i <= bottomr; i++ )
+  { bi = bits[i];
+    { for( j = 0; j < leftr; j++ )
+	*bi++ = 0;
+      bir = bitsr[ i - topr + top ];
+      for( j = leftr; j <= rightr; j++ )
+	*bi++ = bir[j - leftr + left];
+      for( j = rightr + 1; j < MAX_COLS; j++ )
+	*bi++ = 0;
+  } }
+
+  for( i = bottomr + 1; i < MAX_LINES; i++ )
+  { bi = bits[i];
+    for( j = 0; j < MAX_COLS; j++ )
+      *bi++ = 0;
+  }
+} /* fillbits */
+      
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+static void
+writemacp( bits )
+bit **bits;
+{ register int i;
+  bit pb[MAX_COLS * 2];
+  int npb;
+
+  header();
+  for( i=0; i < MAX_LINES; i++ )
+  { npb = packit( pb, bits[i] );
+    sendbytes( pb, npb );
+  }
+} /* writemacp */
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/* pack regel van MacPaint doc in Apple's format
+ * return value = # of bytes in pb 
+ */
+static int
+packit( pb, bits )
+     bit *pb, *bits;
+{ register int charcount, npb, newcount, flg;
+  bit temp[72];
+  bit *count, *srcb, *destb, save;
+
+  srcb = bits; destb = temp;
+  filltemp( destb, srcb );
+  srcb = temp;
+  destb = pb;
+  npb = 0;
+  charcount = BYTES_WIDE;
+  flg = EQUAL;
+  while( charcount ) { 
+      save = *srcb++;
+      charcount--;
+      newcount = 1;
+      while( (*srcb == save) && charcount ) { 
+          srcb++;
+          newcount++;
+          charcount--;
+      }
+      if( newcount > 2 ) { 
+          count = destb++;
+          *count = 257 - newcount;
+          *destb++ = save;
+          npb += 2;
+          flg = EQUAL;
+      } else { 
+          if( flg == EQUAL ) { 
+              count = destb++;
+              *count = newcount - 1;
+              npb++;
+          } else
+            *count += newcount;
+          while( newcount-- ) { 
+              *destb++ = save;
+              npb++;
+          }
+          flg = UNEQUAL;
+      } 
+  }
+  return npb;
+} /* packit */
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+static void
+filltemp( dest, src )
+bit *dest, *src;
+{ register unsigned char ch, zero, acht;
+  register int i, j;
+
+  zero = '\0';
+  acht = 8;
+  i = BYTES_WIDE;
+  while( i-- )
+  { ch = zero; 
+    j = acht;
+    while( j-- )
+    { ch <<= 1;
+      if( *src++ )
+	ch++;
+    }
+    *dest++ = ch;
+  }
+} /* filltemp */
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+static void
+sendbytes( pb, npb )
+bit *pb;
+register int npb;
+{ register bit *b;
+
+  b = pb;
+  while( npb-- )
+    (void) putc( *b++, fdout );
+} /* sendbytes */
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+static void
+header()
+{ register int i;
+  register char ch;
+
+  /* header contains nothing ... */
+  ch = '\0';
+  for(i = 0; i < HEADER_LENGTH; i++ )
+    (void) putc( ch, fdout );
+} /* header */
diff --git a/converter/pbm/pbmtomatrixorbital.c b/converter/pbm/pbmtomatrixorbital.c
new file mode 100644
index 00000000..79347978
--- /dev/null
+++ b/converter/pbm/pbmtomatrixorbital.c
@@ -0,0 +1,87 @@
+#include "pbm.h"
+
+/* By Bryan Henderson, San Jose CA 2003.09.06.
+
+   Contributed to the public domain by its author.
+
+   This is a replacement of Joseph Sheedy's (hbarover2@yahoo.com) Perl
+   program of the same name, distributed in his Pbmtomatrixorbital
+   package.  This version uses Netpbm libraries and is fully
+   consistent with other Netpbm programs.
+*/
+
+
+static void
+generateMo(FILE * const ofP, 
+           bit ** const bits,
+           int    const cols,
+           int    const rows) {
+
+    unsigned int col;
+
+    fputc(cols, ofP);
+    fputc(rows, ofP);
+
+    for (col = 0; col < cols; ++col) {
+        unsigned int row;
+        unsigned int outbitpos;
+        unsigned char outchar;
+        
+        outbitpos = 0;  /* Start at 1st bit of 1st output byte */
+
+        for (row = 0; row < rows; ++row) {
+            if (outbitpos == 0)
+                /* We're starting a new byte; initialize it to zeroes */
+                outchar = 0;
+
+            outchar |= bits[row][col] << outbitpos;
+
+            if (outbitpos == 7) 
+                /* We filled up a byte.  Output it. */
+                fputc(outchar, ofP);
+
+            outbitpos = (outbitpos + 1) % 8;
+        }
+        if (outbitpos != 0)
+            /* Our last byte is partial, so must be output now. */
+            fputc(outchar, ofP);
+    }
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    FILE* ifp;
+    bit** bits;
+    int rows, cols;
+    const char * inputFilename;
+
+    pbm_init(&argc, argv);
+
+    if (argc-1 > 0)
+        pm_error("Too many arguments (%d).  The only valid argument is an "
+                 "input file name.", argc-1);
+    else if (argc-1 == 1) 
+        inputFilename = argv[1];
+    else
+        inputFilename = "-";
+
+    ifp = pm_openr(inputFilename);
+    
+    bits = pbm_readpbm(ifp, &cols, &rows);
+
+    if (rows > 255)
+        pm_error("Image is too high:  %d rows.  Max height: 255 rows", rows);
+    if (cols > 255)
+        pm_error("Image is too wide:  %d cols.  Max width: 255 cols", cols);
+
+    generateMo(stdout, bits, cols, rows);
+    
+    pm_close(ifp);
+
+    pbm_freearray(bits, rows);
+
+    exit(0);
+}
diff --git a/converter/pbm/pbmtomda.c b/converter/pbm/pbmtomda.c
new file mode 100644
index 00000000..3ad51499
--- /dev/null
+++ b/converter/pbm/pbmtomda.c
@@ -0,0 +1,201 @@
+
+/***************************************************************************
+
+    PBMTOMDA: Convert portable bitmap to Microdesign area
+    Copyright (C) 1999,2004 John Elliott <jce@seasip.demon.co.uk>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+******************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pbm.h"
+#include "mallocvar.h"
+
+/* I'm being somewhat conservative in the PBM -> MDA translation. I output 
+ * only the MD2 format and don't allow RLE over the ends of lines.
+ */
+
+typedef unsigned char mdbyte;
+
+static FILE *infile;
+static mdbyte header[128];
+static int bInvert = 0;
+static int bScale  = 0;
+
+/* Encode 8 pixels as a byte */
+
+static mdbyte 
+encode(bit ** const bits, int const row, int const col)
+{
+    int n;
+    int mask;
+    mdbyte b;
+
+    mask = 0x80;   /* initial value */
+    b = 0;  /* initial value */
+
+    for (n = 0; n < 8; n++) {
+        if (bits[row][col+n] == PBM_BLACK) b |= mask;
+        mask = mask >> 1;
+    }
+    return b;
+}
+
+/* Translate a pbm to MD2 format, one row at a time */
+
+static void 
+do_translation(bit ** const bits, 
+               int    const nOutCols, 
+               int    const nOutRows,
+               int    const nInRows)
+{
+    int row;
+    mdbyte *mdrow;  /* malloc'ed */
+
+    int const step = bScale ? 2 : 1;
+
+    MALLOCARRAY(mdrow, nOutCols);
+
+    if (mdrow == NULL)
+        pm_error("Not enough memory for conversion.");
+
+    for (row = 0; row < nOutRows; row+=step)
+    {
+        int col;
+        int x1;
+
+        /* Encode image into non-compressed bitmap */
+        for (col = 0; col < nOutCols; ++col) {
+            mdbyte b;
+
+            if (row < nInRows)
+                b = encode(bits, row, col*8);
+            else
+                b = 0xff;  /* All black */
+
+            mdrow[col] = bInvert ? b : ~b;
+        }
+
+        /* Encoded. Now RLE it */
+        for (col = 0; col < nOutCols; )
+        {
+            mdbyte const b = mdrow[col];
+
+            if (b != 0xFF && b != 0) /* Normal byte */
+            {
+                putchar(b);
+                ++col;
+            }
+            else    /* RLE a run of 0s or 0xFFs */
+            {
+                for (x1 = col; x1 < nOutCols; x1++)
+                {
+                    if (mdrow[x1] != b) break;
+                    if (x1 - col > 256) break;
+                }
+                x1 -= col;    /* x1 = no. of repeats */
+                if (x1 == 256) x1 = 0;
+                putchar(b);
+                putchar(x1);
+                col += x1;        
+            }   
+        }
+    }
+    free(mdrow);
+}
+
+
+static void usage(char *s)
+{        
+    printf("pbmtomda v1.01, Copyright (C) 1999,2004 John Elliott <jce@seasip.demon.co.uk>\n"
+         "This program is redistributable under the terms of the GNU General Public\n"
+                 "License, version 2 or later.\n\n"
+                 "Usage: %s [ -d ] [ -i ] [ -- ] [ infile ]\n\n"
+                 "-d: Halve height (to compensate for the PCW aspect ratio)\n"
+                 "-i: Invert colors\n"
+                 "--: No more options (use if filename begins with a dash)\n",
+        s);
+
+    exit(0);
+}
+
+int main(int argc, char **argv)
+{
+    int nOutRowsUnrounded;  /* Before rounding up to multiple of 4 */
+    int nOutCols, nOutRows;
+    int nInCols, nInRows;
+    bit **bits;
+    int rc;
+
+    int n, optstop = 0;
+    char *fname = NULL;
+
+    pbm_init(&argc, argv);
+
+    /* Output v2-format MDA images. Simulate MDA header...
+     * 2004-01-11: Hmm. Apparently some (but not all) MDA-reading 
+     * programs insist on the program identifier being exactly 
+     * 'MicroDesignPCW'. The spec does not make this clear. */
+    strcpy((char*) header, ".MDAMicroDesignPCWv1.00\r\npbm2mda\r\n");
+
+    for (n = 1; n < argc; n++)
+    {
+        if (argv[n][0] == '-' && !optstop)
+        {   
+            if (argv[n][1] == 'd' || argv[n][1] == 'D') bScale = 1;
+            if (argv[n][1] == 'i' || argv[n][1] == 'I') bInvert = 1;
+            if (argv[n][1] == 'h' || argv[n][1] == 'H') usage(argv[0]);
+            if (argv[n][1] == '-' && argv[n][2] == 0 && !fname)     /* "--" */
+            {
+                optstop = 1;
+            }
+            if (argv[n][1] == '-' && (argv[n][2] == 'h' || argv[n][2] == 'H')) usage(argv[0]);
+        }
+        else if (argv[n][0] && !fname)  /* Filename */
+        {
+            fname = argv[n];
+        }
+    }
+
+    if (fname) infile = pm_openr(fname);
+    else       infile = stdin;
+
+    bits = pbm_readpbm(infile, &nInCols, &nInRows);
+    
+    nOutRowsUnrounded = bScale ? nInRows/2 : nInRows;
+
+    nOutRows = ((nOutRowsUnrounded + 3) / 4) * 4;
+        /* MDA wants rows a multiple of 4 */   
+    nOutCols = nInCols / 8;
+
+    rc = fwrite(header, 1, 128, stdout);
+    if (rc < 128)
+        pm_error("Unable to write header to output file.  errno=%d (%s)",
+                 errno, strerror(errno));
+
+    pm_writelittleshort(stdout, nOutRows);
+    pm_writelittleshort(stdout, nOutCols);
+
+    do_translation(bits, nOutCols, nOutRows, nInRows);
+
+    pm_close(infile);
+    fflush(stdout);
+    pbm_freearray(bits, nInRows);
+    
+    return 0;
+}
diff --git a/converter/pbm/pbmtomgr.c b/converter/pbm/pbmtomgr.c
new file mode 100644
index 00000000..7a6e7fc1
--- /dev/null
+++ b/converter/pbm/pbmtomgr.c
@@ -0,0 +1,120 @@
+/* pbmtomgr.c - read a portable bitmap and produce a MGR bitmap
+**
+** Copyright (C) 1989 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "pbm.h"
+#include "mgr.h"
+
+static void putinit ARGS(( int rows, int cols ));
+static void putbit ARGS(( bit b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, format, padright, row, col;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    bitrow = pbm_allocrow( cols );
+    
+    /* Round cols up to the nearest multiple of 8. */
+    padright = ( ( cols + 7 ) / 8 ) * 8 - cols;
+
+    putinit( rows, cols );
+    for ( row = 0; row < rows; ++row )
+	{
+	pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    putbit( *bP );
+	for ( col = 0; col < padright; ++col )
+	    putbit( 0 );
+        }
+
+    pm_close( ifp );
+
+    putrest( );
+
+    exit( 0 );
+    }
+
+static unsigned char item;
+static int bitsperitem, bitshift;
+
+static void
+putinit( rows, cols )
+    int rows, cols;
+    {
+    struct b_header head;
+
+    head.magic[0] = 'y';
+    head.magic[1] = 'z';
+    head.h_wide = ( ( cols >> 6 ) & 0x3f ) + ' ';
+    head.l_wide = ( cols & 0x3f ) + ' ';
+    head.h_high = ( ( rows >> 6 ) & 0x3f ) + ' ';
+    head.l_high = ( rows & 0x3f ) + ' ';
+    head.depth = ( 1 & 0x3f ) + ' ';
+    head._reserved = ' ';
+    fwrite( &head, sizeof(head), 1, stdout );
+
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 7;
+    }
+
+#if __STDC__
+static void
+putbit( bit b )
+#else /*__STDC__*/
+static void
+putbit( b )
+    bit b;
+#endif /*__STDC__*/
+    {
+    if ( bitsperitem == 8 )
+	putitem( );
+    ++bitsperitem;
+    if ( b == PBM_BLACK )
+	item += 1 << bitshift;
+    --bitshift;
+    }
+
+static void
+putrest( )
+    {
+    if ( bitsperitem > 0 )
+	putitem( );
+    }
+
+static void
+putitem( )
+    {
+    fwrite( &item, sizeof(item), 1, stdout );
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 7;
+    }
diff --git a/converter/pbm/pbmtomrf.c b/converter/pbm/pbmtomrf.c
new file mode 100644
index 00000000..186e95f5
--- /dev/null
+++ b/converter/pbm/pbmtomrf.c
@@ -0,0 +1,338 @@
+/* pbmtomrf - convert pbm to mrf
+ * public domain by RJM
+ *
+ * Adapted to Netpbm by Bryan Henderson 2003.08.09.  Bryan got his copy from
+ * ftp://ibiblio.org/pub/linux/apps/convert, dated 1998.03.03.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "pm_c_util.h"
+#include "pbm.h"
+
+static int bitbox;
+static int bitsleft;
+
+static FILE *bit_out;
+
+
+static void 
+bit_init(FILE * const out) {
+    bitbox = 0; 
+    bitsleft = 8;
+    bit_out = out;
+}
+
+
+
+static void 
+bit_output(int const bit) {
+    --bitsleft;
+    bitbox |= (bit << bitsleft);
+    if (bitsleft == 0) {
+        fputc(bitbox, bit_out);
+        bitbox = 0;
+        bitsleft = 8;
+    }
+}
+
+
+
+static void 
+bit_flush(void) {
+    /* there are never 0 bits left outside of bit_output, but
+     * if 8 bits are left here there's nothing to flush, so
+     * only do it if bitsleft!=8.
+     */
+    if (bitsleft != 8) {
+        bitsleft = 1;
+        bit_output(0);    /* yes, really. This will always work. */
+    }
+}
+
+
+
+static void 
+doSquare(unsigned char * const image,
+         int             const ox,
+         int             const oy,
+         int             const w,
+         int             const size) {
+
+    unsigned int y;
+    unsigned int t;
+
+    /* check square to see if it's all black or all white. */
+
+    t = 0;
+    for (y = 0; y < size; ++y) {
+        unsigned int x;
+        for (x = 0; x < size; ++x)
+            t += image[(oy+y)*w + ox + x];
+    }        
+    /* if the total's 0, it's black. if it's size*size, it's white. */
+    if (t == 0 || t == size*size) {
+        if (size != 1)     /* (it's implicit when size is 1, of course) */
+            bit_output(1);  /* all same color */
+        bit_output(t?1:0);
+        return;
+    }
+    
+    /* otherwise, if our square is greater than 1x1, we need to recurse. */
+    if(size > 1) {
+        bit_output(0);    /* not all same */
+        doSquare(image, ox,      oy,      w, size>>1);
+        doSquare(image, ox+size, oy,      w, size>>1);
+        doSquare(image, ox,      oy+size, w, size>>1);
+        doSquare(image, ox+size, oy+size, w, size>>1);
+    }
+}
+    
+
+
+static void
+fiddleRightEdge(unsigned char * const image,
+                unsigned int    const w,
+                unsigned int    const h,
+                unsigned int    const pw,
+                bool *          const flippedP) {
+
+    unsigned int row;
+    unsigned int t;
+
+    for (row = t = 0; row < h; ++row)
+        t += image[row*pw + w - 1];
+
+    if (t*2 > h) {
+        unsigned int row;
+
+        *flippedP = TRUE;
+        for (row = 0; row < h; ++row) {
+            unsigned int col;
+            for (col = w; col < pw; ++col)
+                image[row*pw + col] = 1;
+        }
+    } else
+        *flippedP = FALSE;
+}
+
+
+
+static void
+fiddleBottomEdge(unsigned char * const image,
+                 unsigned int    const w,
+                 unsigned int    const h,
+                 unsigned int    const pw,
+                 unsigned int    const ph,
+                 bool *          const flippedP) {
+    
+    unsigned int col;
+    unsigned int t;
+
+    for (col = t = 0; col < w; ++col)
+        t += image[(h-1)*pw + col];
+
+    if (t*2 > w) {
+        unsigned int row;
+        *flippedP = TRUE;
+        for (row = h; row < ph; ++row) {
+            unsigned int col;
+            for (col = 0; col < w; ++col)
+                image[row*pw + col] = 1;
+        }
+    } else
+        *flippedP = FALSE;
+}
+
+
+
+
+static void
+fiddleBottomRightCorner(unsigned char * const image,
+                        unsigned int    const w,
+                        unsigned int    const h,
+                        unsigned int    const pw,
+                        unsigned int    const ph) {
+    unsigned int row;
+
+    for (row = h; row < ph; ++row) {
+        unsigned int col;
+        
+        for (col = w; col < pw; ++col)
+                    image[row*pw + col] = 1;
+    }
+}
+
+
+
+static void 
+fiddleEdges(unsigned char * const image,
+            int             const cols,
+            int             const rows) {
+/* the aim of this routine is play around with the edges which
+ * are compressed into the mrf but thrown away when it's decompressed,
+ * such that we get the best compression possible.
+ * If you don't see why this is a good idea, consider the simple case
+ * of a 1x1 white pixel. Placed on a black 64x64 this takes several bytes
+ * to compress. On a white 64x64, it takes two bits.
+ * (Clearly most cases will be more complicated, but you should get the
+ * basic idea from that.)
+ */
+
+    /* there are many possible approaches to this problem, and this one's
+         * certainly not the best, but at least it's quick and easy, and it's
+         * better than nothing. :-)
+         *
+         * So, all we do is flip the runoff area of an edge to white
+         * if more than half of the pixels on that edge are
+         * white. Then for the bottom-right runoff square (if there is
+         * one), we flip it if we flipped both edges.  
+         */
+        
+    /* w64 is units-of-64-bits width, h64 same for height */
+    unsigned int const w64 = (cols + 63) / 64;
+    unsigned int const h64 = (rows + 63) / 64;
+
+    int const pw=w64*64;
+    int const ph=h64*64;
+
+    bool flippedRight, flippedBottom;
+
+    if (cols % 64 != 0) 
+        fiddleRightEdge(image, cols, rows, pw, &flippedRight);
+    else
+        flippedRight = FALSE;
+
+    if (rows % 64 != 0) 
+        fiddleBottomEdge(image, cols, rows, pw, ph, &flippedBottom);
+    else
+        flippedBottom = FALSE;
+
+    if (flippedRight && flippedBottom) 
+        fiddleBottomRightCorner(image, cols, rows, pw, ph);
+}
+
+
+
+static void
+readPbmImage(FILE *           const ifP, 
+             unsigned char ** const imageP,
+             int *            const colsP,
+             int *            const rowsP) {
+    
+
+    /* w64 is units-of-64-bits width, h64 same for height */
+    unsigned int w64, h64;
+
+    unsigned char * image;
+    int cols, rows, format;
+    unsigned int row;
+    bit * bitrow;
+    
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    w64 = (cols + 63) / 64;
+    h64 = (rows + 63) / 64;
+
+    if (UINT_MAX/w64/64/h64/64 == 0)
+        pm_error("Ridiculously large, unprocessable image: %u cols x %u rows",
+                 cols, rows);
+
+    image = calloc(w64*h64*64*64,1);
+    if (image == NULL)
+        pm_error("Unable to get memory for raster");
+                 
+    /* get bytemap image rounded up into mod 64x64 squares */
+
+    bitrow = pbm_allocrow(cols);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        pbm_readpbmrow(ifP, bitrow, cols, format);
+
+        for (col =0; col < cols; ++col)
+            image[row*(w64*64) + col] = (bitrow[col] == PBM_WHITE ? 1 : 0);
+    }
+    pbm_freerow(bitrow);
+    *imageP = image;
+    *colsP = cols;
+    *rowsP = rows;
+}
+
+
+
+static void
+outputMrf(FILE *          const ofP, 
+          unsigned char * const image,
+          unsigned int    const cols,
+          unsigned int    const rows) {
+
+    unsigned int const w64 = (cols + 63) / 64;
+    unsigned int const h64 = (rows + 63) / 64;
+
+    unsigned int row;
+
+    fprintf(ofP, "MRF1");
+    fprintf(ofP, "%c%c%c%c", cols >> 24, cols >> 16, cols >> 8, cols >> 0);
+    fprintf(ofP, "%c%c%c%c", rows >> 24, rows >> 16, rows >> 8, rows >> 0);
+    fputc(0, ofP);   /* option byte, unused for now */
+    
+    /* now recursively check squares. */
+
+    bit_init(ofP);
+
+    for (row = 0; row < h64; ++row) {
+        unsigned int col;
+        for (col = 0; col < w64; ++col)
+            doSquare(image, col*64, row*64, w64*64, 64);
+    }
+    bit_flush();
+}
+
+
+
+int 
+main(int argc,char *argv[]) {
+
+    FILE * ifP;
+    FILE * ofP;
+    unsigned char *image;
+    int rows, cols;
+    
+    pbm_init(&argc, argv);
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments: %d.  Only argument is input file", 
+                 argc-1);
+
+    if (argc-1 == 1)
+        ifP = pm_openr(argv[1]);
+    else
+        ifP = stdin;
+
+    ofP = stdout;
+ 
+    readPbmImage(ifP, &image, &cols, &rows);
+
+    pm_close(ifP);
+
+    /* if necessary, alter the unused outside area to aid compression of
+     * edges of image.
+     */
+
+    fiddleEdges(image, cols, rows);
+
+    outputMrf(ofP, image, cols, rows);
+
+    free(image);
+
+    return 0;
+}
+
+
+
+
diff --git a/converter/pbm/pbmtonokia.c b/converter/pbm/pbmtonokia.c
new file mode 100644
index 00000000..66678b7b
--- /dev/null
+++ b/converter/pbm/pbmtonokia.c
@@ -0,0 +1,225 @@
+/* pbmtonokia.c - convert a portable bitmap to Nokia Smart Messaging
+   Formats (NOL, NGG, HEX)
+
+** Copyright (C)2001 OMS Open Media System GmbH, Tim Rühsen
+** <tim.ruehsen@openmediasystem.de>.
+**
+** 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.
+
+History
+  07.06.2001 Created
+  20.11.2001 Handle Picture Messages
+             new option -txt to embed text into Picture Messages
+             new option -net to specify operator network code for 
+                Nokia Operator Logos
+
+Notes:
+  - limited to rows <= 255 and columns <= 255
+  - limited to b/w graphics, not animated
+
+Testing:
+  Testing was done with SwissCom SMSC (Switzerland) and IC3S SMSC (Germany).
+  The data was send with EMI/UCP protocol over TCP/IP.
+
+  - 7.6.2001: tested with Nokia 3210: 72x14 Operator Logo
+  - 7.6.2001: tested with Nokia 6210: 72x14 Operator Logo and 
+              72x14 Group Graphic
+
+Todo:
+  - more testing
+  - sendsms compatibility ?
+  - are -fmt NOL and -fmt NGG working ok?  */
+
+#define _BSD_SOURCE    /* Make sure strcasecmp() is in string.h */
+#include <string.h>
+
+#include "nstring.h"
+#include "pbm.h"
+
+#define FMT_HEX_NOL   1
+#define FMT_HEX_NGG   2
+#define FMT_HEX_NPM   3
+#define FMT_NOL       4
+#define FMT_NGG       5
+
+static void 
+usage(char *myname)
+{
+    pm_message("Copyright (C)2001 OMS GmbH");
+    pm_message("Contact: Tim Ruehsen <tim.ruehsen@openmediasystem.de>\n");
+    pm_usage("[options] [pbmfile]\n"
+             "  Options:\n"
+             "    -fmt <HEX_NOL|HEX_NGG|HEX_NPM|NOL|NGG>  "
+             "Output format (default=HEX_NOL)\n"
+             "    -net <network code>                     "
+             "Network code for NOL operator logos\n"
+             "    -txt <text message>                     "
+             "Text for NMP picture messages\n");
+
+    exit(1);
+}
+
+int 
+main(int argc, char *argv[])
+{
+    FILE    *fp;
+    bit    **image;
+    unsigned int    c;
+    int    argpos, output=FMT_HEX_NOL, rows, cols, row, col, p, it, len;
+    char    header[32], *myname;
+    char    network_code[6+1];
+    char    *text=NULL;
+
+    if ((myname=strrchr(argv[0],'/'))!=NULL) myname++; else myname=argv[0];
+
+    pbm_init(&argc, argv);
+
+    strcpy(network_code, "62F210"); /* default is German D1 net */
+
+    for(argpos=1;argpos<argc;argpos++) {
+        if (argv[argpos][0]=='-') {
+            if (argv[argpos][1]=='-') {
+                if (argc>argpos+1 && ISDIGIT(argv[argpos+1][0]))
+                    {argpos++;break;}
+            } else if (STREQ(argv[argpos],"-fmt") && argc>argpos+1) {
+                ++argpos;
+                if (!strcasecmp(argv[argpos],"HEX_NOL")) output=FMT_HEX_NOL;
+                else if (!strcasecmp(argv[argpos],"HEX_NGG")) 
+                    output=FMT_HEX_NGG;
+                else if (!strcasecmp(argv[argpos],"HEX_NPM")) 
+                    output=FMT_HEX_NPM;
+                else if (!strcasecmp(argv[argpos],"NOL")) output=FMT_NOL;
+                else if (!strcasecmp(argv[argpos],"NGG")) output=FMT_NGG;
+                else usage(myname);
+            } else if (STREQ(argv[argpos],"-net") && argc>argpos+1) {
+                char * const network_code_arg=argv[++argpos];
+                unsigned int it;
+                len=strlen(network_code_arg);
+                if (len!=6) 
+                    pm_error("Network code must be 6 hex-digits long");
+                for (it=0;it<strlen(network_code_arg);it++) {
+                    if (!ISXDIGIT(network_code_arg[it])) 
+                        pm_error("Network code must contain hex-digits only");
+                    network_code[it]=TOUPPER(network_code_arg[it]);
+                }
+                network_code[it] = '\0';
+            } else if (STREQ(argv[argpos],"-txt") && argc>argpos+1) {
+                text=argv[++argpos];
+            }
+            else usage(myname);
+        } else break;
+    }
+
+    if (argpos==argc) {
+        image = pbm_readpbm(stdin, &cols, &rows);
+    } else {
+        fp=pm_openr(argv[argpos]);
+        image = pbm_readpbm(fp, &cols, &rows);
+        pm_close(fp);
+    }
+
+    memset(header,0,sizeof(header));
+
+    switch (output) {
+    case FMT_HEX_NOL:
+        /* header */
+        printf("06050415820000%s00%02X%02X01",network_code,cols,rows);
+
+        /* image */
+        for (row=0;row<rows;row++) {
+            for (p=c=col=0;col<cols;col++) {
+                if (image[row][col]==PBM_BLACK) c|=0x80>>p;
+                if (++p==8) {
+                    printf("%02X",c);
+                    p=c=0;
+                }
+            }
+            if (p) printf("%02X",c);
+        }
+        break;
+    case FMT_HEX_NGG:
+        /* header */
+        printf("0605041583000000%02X%02X01",cols,rows);
+
+        /* image */
+        for (row=0;row<rows;row++) {
+            for (p=c=col=0;col<cols;col++) {
+                if (image[row][col]==PBM_BLACK) c|=0x80>>p;
+                if (++p==8) {
+                    printf("%02X",c);
+                    p=c=0;
+                }
+            }
+            if (p) printf("%02X",c);
+        }
+        break;
+    case FMT_HEX_NPM:
+        /* header */
+        printf("060504158A0000");
+
+        /* text */
+        if (text!=NULL) {
+            printf("00%04X",(len=strlen(text)));
+            for (it=0;it<len;it++) printf("%02X",text[it]);
+        }
+
+        /* image */
+        printf("02%04X00%02X%02X01",(cols*rows)/8+4,cols,rows);
+        for (row=0;row<rows;row++) {
+            for (p=c=col=0;col<cols;col++) {
+                if (image[row][col]==PBM_BLACK) c|=0x80>>p;
+                if (++p==8) {
+                    printf("%02X",c);
+                    p=c=0;
+                }
+            }
+            if (p) printf("%02X",c);
+        }
+        break;
+    case FMT_NOL:
+        /* header - this is a hack */
+        header[1]=header[4]=header[5]=header[11]=header[13]=1;
+        header[3]=4;
+        header[7]=cols;
+        header[9]=rows;
+        header[15]=0x53;
+        fwrite(header,17,1,stdout);
+
+        /* image */
+        for (row=0;row<rows;row++) {
+            for (p=c=col=0;col<cols;col++) {
+                if (image[row][col]==PBM_BLACK) putchar('1');
+                else putchar('0');
+            }
+        }
+        break;
+    case FMT_NGG:
+        /* header - this is a hack */
+        header[1]=header[7]=header[9]=1;
+        header[3]=cols;
+        header[5]=rows;
+        header[11]=0x4a;
+        fwrite(header,13,1,stdout);
+
+        /* image */
+        for (row=0;row<rows;row++) {
+            for (p=c=col=0;col<cols;col++) {
+                if (image[row][col]==PBM_BLACK) putchar('1');
+                else putchar('0');
+            }
+        }
+        break;
+    default:
+        pm_error("Output format %d not implemented!\n"
+                 "Contact Tim Ruehsen <tim.ruehsen@openmediasystem.de>\n",
+                 output);
+        return 1;
+    }
+    return 0;
+}
+
diff --git a/converter/pbm/pbmtopi3.c b/converter/pbm/pbmtopi3.c
new file mode 100644
index 00000000..06023d7a
--- /dev/null
+++ b/converter/pbm/pbmtopi3.c
@@ -0,0 +1,123 @@
+/* pbmtopi3.c - read a portable bitmap and produce a Atari Degas .pi3 file
+**
+** Module created from other pbmplus tools by David Beckemeyer.
+**
+** Copyright (C) 1988 by David Beckemeyer and Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <stdio.h>
+#include "pbm.h"
+
+static void putinit ARGS(( void ));
+static void putbit ARGS(( bit b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, format, padright, row, col;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    if (cols > 640)
+	cols = 640;
+    if (rows > 400)
+	rows = 400;
+    bitrow = pbm_allocrow( cols );
+    
+    /* Compute padding to round cols up to 640 */
+    padright = 640 - cols;
+
+    putinit( );
+    for ( row = 0; row < rows; ++row )
+	{
+	pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    putbit( *bP );
+	for ( col = 0; col < padright; ++col )
+	    putbit( 0 );
+        }
+    while (row++ < 400)
+	for ( col = 0; col < 640; ++col)
+	    putbit( 0 );
+
+    pm_close( ifp );
+
+    putrest( );
+
+    exit( 0 );
+    }
+
+static char item;
+static short bitsperitem, bitshift;
+
+static void
+putinit( )
+    {
+    int i;
+    if (pm_writebigshort (stdout, (short) 2) == -1
+	|| pm_writebigshort (stdout, (short) 0x777) == -1)
+      pm_error ("write error");
+    for (i = 1; i < 16; i++)
+      if (pm_writebigshort (stdout, (short) 0) == -1)
+	pm_error ("write error");
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 7;
+    }
+
+#if __STDC__
+static void
+putbit( bit b )
+#else /*__STDC__*/
+static void
+putbit( b )
+    bit b;
+#endif /*__STDC__*/
+    {
+    if (bitsperitem == 8)
+	putitem( );
+    ++bitsperitem;
+    if ( b == PBM_BLACK )
+	item += 1 << bitshift;
+    --bitshift;
+    }
+
+static void
+putrest( )
+    {
+    if ( bitsperitem > 0 )
+	putitem( );
+    }
+
+static void
+putitem( )
+    {
+    putc (item, stdout);
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 7;
+    }
diff --git a/converter/pbm/pbmtopk.c b/converter/pbm/pbmtopk.c
new file mode 100644
index 00000000..b84818b1
--- /dev/null
+++ b/converter/pbm/pbmtopk.c
@@ -0,0 +1,976 @@
+/*
+  pbmtopk, adapted from "pxtopk.c by tomas rokicki" by AJCD 1/8/90
+  
+  compile with: cc -o pbmtopk pbmtopk.c -lm -lpbm
+*/
+
+#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <stdio.h>
+#include <math.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "pbm.h"
+#include "nstring.h"
+#include "mallocvar.h"
+
+#define MAXPKCHAR 256
+#define MAXOPTLINE 200
+#define MAXWIDTHTAB 256
+#define MAXHEIGHTTAB 16
+#define MAXDEPTHTAB 16
+#define MAXITALICTAB 64
+#define MAXPARAMS 30
+#define NAMELENGTH 80
+
+#define fixword(d) ((int)((double)(d)*1048576))
+#define unfixword(f) ((double)(f) / 1048576)
+#define fixrange(f) ((f) < 16777216 && (f) > -16777216)
+#define designunits(p) ((p)*72.27/(double)resolution/unfixword(designsize))
+
+/* character flags: in order of appearance in option files. */
+#define XOFFSET     1
+#define YOFFSET     2
+#define HORZESC     4
+#define VERTESC     8
+#define TFMWIDTH   16
+#define TFMHEIGHT  32
+#define TFMDEPTH   64
+#define TFMITALIC 128
+
+typedef int integer ;
+typedef char quarterword ;
+typedef char boolean ;
+typedef quarterword ASCIIcode ;
+typedef quarterword eightbits ;
+typedef unsigned char byte ;
+
+static integer resolution, designsize ;
+static char *filename[MAXPKCHAR] ;
+
+static integer xoffset[MAXPKCHAR] ;
+static integer yoffset[MAXPKCHAR] ;
+static integer horzesc[MAXPKCHAR] ;
+static integer vertesc[MAXPKCHAR] ;
+
+static byte tfmindex[MAXPKCHAR] ;
+static byte hgtindex[MAXPKCHAR] ;
+static byte depindex[MAXPKCHAR] ;
+static byte italindex[MAXPKCHAR] ;
+static byte charflags[MAXPKCHAR] ;
+
+static bit **bitmap ;
+static integer smallestch = MAXPKCHAR ;
+static integer largestch = -1;
+static integer emwidth  = 0;
+static integer checksum ;
+static const char *codingscheme = "GRAPHIC" ;
+static const char *familyname = "PBM" ;
+
+static integer widthtab[MAXWIDTHTAB] = {0}; /* TFM widths */
+static integer numwidth = 1;      /* number of entries in width table */
+static integer heighttab[MAXHEIGHTTAB]  = {0};
+static integer numheight = 1;
+static integer depthtab[MAXDEPTHTAB] = {0};
+static integer numdepth = 1;
+static integer italictab[MAXITALICTAB] = {0};
+static integer numitalic = 1;
+static integer parameters[MAXPARAMS] = {0};
+static integer numparam = 0;
+
+static ASCIIcode xord[128] ;
+static char xchr[256] = {
+    '?', '?', '?', '?', '?', '?', '?', '?',
+    '?', '?', '?', '?', '?', '?', '?', '?',
+    '?', '?', '?', '?', '?', '?', '?', '?',
+    '?', '?', '?', '?', '?', '?', '?', '?',
+    ' ', '!', '"', '#', '$', '%', '&', '\'',
+    '(', ')', '*', '+', ',', '-', '.', '/',
+    '0', '1', '2', '3', '4', '5', '6', '7',
+    '8', '9', ':', ';', '<', '=', '>', '?',
+    '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+    'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
+    '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+    'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+    'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+    'x', 'y', 'z', '{', '|', '}', '~', '?' };
+
+static FILE *tfmfile, *pkfile ;
+static char tfmname[NAMELENGTH+1], pkname[NAMELENGTH+1] ;
+static integer pbmtopk_pkloc = 0 ;
+static integer bitweight ;
+static integer outputbyte ;
+static integer car ;
+static integer hppp ;
+static integer width ;
+static integer height ;
+
+/* check sum algorithm (in Pascal):
+
+compute_checksum()
+    begin
+        c0:=bc; c1:=ec; c2:=bc; c3:=ec;
+        for c:=bc to ec do if char_wd[c]>0 then begin
+            temp_width:=memory[char_wd[c]];
+            if design_units<>unity then
+               temp_width:=round((temp_width/design_units)*1048576.0);
+            temp_width:=temp_width + (c+4)*@'20000000; 
+                {this should be positive}
+            c0:=(c0+c0+temp_width) mod 255;
+            c1:=(c1+c1+temp_width) mod 253;
+            c2:=(c2+c2+temp_width) mod 251;
+            c3:=(c3+c3+temp_width) mod 247;
+            end;
+        header_bytes[check_sum_loc]:=c0;
+        header_bytes[check_sum_loc+1]:=c1;
+        header_bytes[check_sum_loc+2]:=c2;
+        header_bytes[check_sum_loc+3]:=c3;
+        end
+*/
+
+#define add_tfmwidth(v) (add_tfmtable(widthtab, &numwidth, v, MAXWIDTHTAB,\
+                                      "TFM width"))
+#define add_tfmheight(v) (add_tfmtable(heighttab, &numheight, v, MAXHEIGHTTAB,\
+                                       "TFM height"))
+#define add_tfmdepth(v) (add_tfmtable(depthtab, &numdepth, v, MAXDEPTHTAB,\
+                                      "TFM depth"))
+#define add_tfmitalic(v) (add_tfmtable(italictab, &numitalic, v, MAXITALICTAB,\
+                                       "Italic correction"))
+
+
+static byte
+add_tfmtable(int *        const table, 
+             int *        const count, 
+             int          const value, 
+             int          const max_count, 
+             const char * const name) {
+    
+    integer i;
+    for (i = 0; i < *count; i++) /* search for value in tfm table */
+        if (table[i] == value) return (byte)i;
+    if (*count >= max_count)
+        pm_error("too many values in %s table", name) ;
+    if (!fixrange(value))
+        pm_error("%s %f for char %d out of range",
+                 name, unfixword(value), car);
+    table[*count] = value ;
+    return (*count)++ ;
+}
+
+
+
+/* add a suffix to a filename in an allocated space */
+static void 
+pbmtopk_add_suffix(char * const name, 
+                   const char * const suffix) {
+
+    char *slash = strrchr(name, '/');
+    char *dot = strrchr(name, '.');
+
+    if ((dot && slash ? dot < slash : !dot) && !STREQ(name, "-"))
+        strcat(name, suffix);
+}
+
+
+
+/* initialize the PK parameters */
+static void 
+initialize_pk(void) {
+    integer i ;
+    pm_message("This is PBMtoPK, version 2.4") ;
+    for (i = 127 ; i <= 255 ; i ++) xchr[i] = '?' ;
+    for (i = 0 ; i <= 127 ; i ++) xord[i] = 32 ;
+    for (i = 32 ; i < 127 ; i ++) xord[(int)xchr[i]] = i ;
+    for (i = 0; i < MAXPKCHAR; i++) {
+        filename[i] = NULL;
+        charflags[i] = 0;
+    }
+    designsize = fixword(1.0) ;
+}
+
+
+
+/* write a single byte to the PK file */
+static void 
+pbmtopk_pkbyte(integer const b_in) {
+    integer b;
+
+    b = b_in;  /* initial value */
+
+    if (b < 0) 
+        b += 256 ;
+    putc(b, pkfile) ;
+    pbmtopk_pkloc++ ;
+}
+
+
+
+/* write two bytes to the PK file */
+static void 
+pkhalfword(integer const a_in) {
+    integer a;
+
+    a = a_in;
+
+    if (a < 0) 
+        a += 65536 ;
+    pbmtopk_pkbyte(a >> 8) ;
+    pbmtopk_pkbyte(a & 255) ;
+}
+
+
+
+/* write three bytes to the PK file */
+static void 
+pkthreebytes(integer const a) {
+
+    pbmtopk_pkbyte((a>>16) & 255) ;
+    pbmtopk_pkbyte((a>>8) & 255) ;
+    pbmtopk_pkbyte(a & 255) ;
+}
+
+
+
+/* write four bytes to the PK file */
+static void 
+pkword(integer const a) {
+    pbmtopk_pkbyte((a>>24) & 255) ;
+    pbmtopk_pkbyte((a>>16) & 255) ;
+    pbmtopk_pkbyte((a>>8) & 255) ;
+    pbmtopk_pkbyte(a & 255) ;
+}
+
+
+
+/* write a nibble to the PK file */
+static void 
+pknyb(integer const a) {
+
+    if (bitweight == 16) {
+        outputbyte = (a<<4) ;
+        bitweight = 1 ;
+    } else {
+        pbmtopk_pkbyte(outputbyte + a) ;
+        bitweight = 16 ;
+    }
+}
+
+
+
+/* write preamble to PK file */
+static void 
+writepreamble(void) {
+    integer i ;
+    const char * const comment = "PBMtoPK 2.4 output" ;
+   
+    pbmtopk_pkbyte(247) ;                /* PRE command */
+    pbmtopk_pkbyte(89) ;             /* PK file type */
+    pbmtopk_pkbyte(strlen(comment)) ;            /* output comment */
+    for (i = 0 ; i < strlen(comment); i++) 
+        pbmtopk_pkbyte(xord[(int)comment[i]]) ;
+    pkword(designsize) ;             /* write designsize */
+    pkword(checksum) ;       /* write checksum; calculate if possible */
+    pkword(hppp) ;               /* write H pixels per point */
+    pkword(hppp) ;               /* write V pixels per point */
+}
+
+
+
+/* write postamble to PK file, padded to word length */
+static void 
+writepostamble(void) {
+    pbmtopk_pkbyte(245) ;                /* POST command */
+    while (pbmtopk_pkloc % 4)
+        pbmtopk_pkbyte(246) ;             /* pad with no-ops */
+    pm_message("%d bytes written to packed file.", pbmtopk_pkloc) ;
+}
+
+
+
+/* write a byte to the TFM file */
+static void 
+tfmbyte(integer const b_in) {
+    integer b;
+
+    b = b_in;
+
+    if (b < 0) b += 256 ;
+    putc(b, tfmfile) ;
+}
+
+
+
+/* write a half word to the TFM file */
+static void 
+tfmhalfword(integer const a_in) {
+    
+    integer a;
+
+    a = a_in;
+
+    if (a < 0) a += 65536 ;
+    tfmbyte(a >> 8) ;
+    tfmbyte(a & 255) ;
+}
+
+
+
+/* write a word to the TFM file */
+static void 
+tfmword(integer const a) {
+    tfmbyte((a>>24) & 255) ;
+    tfmbyte((a>>16) & 255) ;
+    tfmbyte((a>>8) & 255) ;
+    tfmbyte(a & 255) ;
+}
+
+
+
+/* write the whole TFM file for the font */
+static void 
+writetfmfile(void) {
+    integer totallength ;
+    integer headersize = 17;
+    integer i ;
+   
+    if (largestch - smallestch < 0) {
+        largestch = 0;
+        smallestch = 1;
+    }
+    if (numparam < 7) /* set default parameters */
+        switch (numparam) {
+        case 0: /* slant */
+            parameters[numparam++] = 0 ;
+        case 1: /* space */
+            parameters[numparam++] = fixword(designunits(emwidth/3.0));
+        case 2: /* space_stretch */
+            parameters[numparam++] = fixword(unfixword(parameters[1])/2.0) ;
+        case 3: /* space_shrink */
+            parameters[numparam++] = fixword(unfixword(parameters[1])/3.0) ;
+        case 4: /* x_height */
+            parameters[numparam++] = fixword(0.45);
+        case 5: /* quad */
+            parameters[numparam++] = fixword(designunits(emwidth)) ;
+        case 6: /* extra_space */
+            parameters[numparam++] = fixword(unfixword(parameters[1])/3.0) ;
+        }
+    totallength = 6 + headersize + (largestch+1-smallestch) +
+        numwidth + numheight + numdepth + numitalic + numparam ;
+    /* lengths */
+    tfmhalfword(totallength) ;           /* write file TFM length */
+    tfmhalfword(headersize) ;            /* write TFM header length */
+    tfmhalfword(smallestch) ;            /* write lowest char index */
+    tfmhalfword(largestch) ;         /* write highest char index */
+    tfmhalfword(numwidth) ;          /* write number of widths */
+    tfmhalfword(numheight) ;         /* write number of heights */
+    tfmhalfword(numdepth) ;          /* write number of depths */
+    tfmhalfword(numitalic) ;         /* write number of italcorrs */
+    tfmhalfword(0) ;             /* lig/kern table */
+    tfmhalfword(0) ;             /* kern table */
+    tfmhalfword(0) ;             /* extensible char table */
+    tfmhalfword(numparam) ;          /* number of fontdimens */
+    /* header */
+    tfmword(checksum) ;              /* write checksum */
+    tfmword(designsize) ;            /* write designsize */
+    if (strlen(codingscheme) > 39) 
+        tfmbyte(39) ; /* write coding scheme len */
+    else 
+        tfmbyte(strlen(codingscheme)) ;
+    for (i = 0; i < 39; i++)         /* write coding scheme */
+        if 
+            (*codingscheme) tfmbyte(xord[(int)(*codingscheme++)]) ;
+        else
+            tfmbyte(0) ;
+    if (strlen(familyname) > 19) 
+        tfmbyte(19) ;   /* write family length */
+    else 
+        tfmbyte(strlen(familyname)) ;
+    for (i = 0; i < 19; i++)         /* write family */
+        if (*familyname) 
+            tfmbyte(xord[(int)(*familyname++)]) ;
+        else 
+            tfmbyte(0) ;
+    /* char_info */
+    for (car = smallestch; car <= largestch; car++)
+        if (filename[car]) {          /* write character info */
+            tfmbyte(tfmindex[car]) ;
+            tfmbyte((hgtindex[car]<<4) + depindex[car]) ;
+            tfmbyte(italindex[car]<<2) ;
+            tfmbyte(0) ;
+        } else
+            tfmword(0) ;
+    /* width table */
+    for (i = 0; i < numwidth; i++) tfmword(widthtab[i]) ;
+    /* height table */
+    for (i = 0; i < numheight; i++) tfmword(heighttab[i]) ;
+    /* depth table */
+    for (i = 0; i < numdepth; i++) tfmword(depthtab[i]) ;
+    /* italic correction table */
+    for (i = 0; i < numitalic; i++) tfmword(italictab[i]) ;
+    /* no lig_kern, kern, or exten tables */
+    /* fontdimen table */
+    for (i = 0; i < numparam; i++)
+        if (i && !fixrange(parameters[i]))
+            pm_error("parameter %d out of range (-p)", i);
+        else
+            tfmword(parameters[i]) ;
+    pm_message("%d bytes written to tfm file.", totallength*4) ;
+}
+
+
+
+/* read a character from a PBM file */
+static void readcharacter(void) {
+    FILE *fp;
+   
+    fp = pm_openr(filename[car]);
+    bitmap = pbm_readpbm(fp, &width, &height) ;
+    pm_close(fp) ;
+   
+    if ((charflags[car] & HORZESC) == 0) horzesc[car] = width ;
+    if ((charflags[car] & VERTESC) == 0) vertesc[car] = 0;
+    if ((charflags[car] & XOFFSET) == 0) xoffset[car] = 0;
+    if ((charflags[car] & YOFFSET) == 0) yoffset[car] = height-1;
+    if ((charflags[car] & TFMWIDTH) == 0)
+        tfmindex[car] = add_tfmwidth(fixword(designunits(width)));
+    if ((charflags[car] & TFMHEIGHT) == 0)
+        hgtindex[car] = add_tfmheight(fixword(designunits(yoffset[car]+1)));
+    if ((charflags[car] & TFMDEPTH) == 0)
+        depindex[car] = 
+            add_tfmdepth(fixword(designunits(height-1-yoffset[car])));
+    if ((charflags[car] & TFMITALIC) == 0) italindex[car] = 0;
+   
+    if (car < smallestch) smallestch = car;
+    if (car > largestch) largestch = car;
+    if (width > emwidth) emwidth = width ;
+}
+
+
+
+/* test if two rows of the PBM are the same */
+static int 
+equal(const bit * const row1, 
+      const bit * const row2) {
+
+    integer i ;
+   
+    for (i = 0; i < width; i++)
+        if (row1[i] != row2[i]) 
+            return (0) ;
+
+    return(1) ;
+}
+
+
+
+static void 
+shipcharacter(void) {
+
+    integer compsize ;
+    integer i, j, k ;
+    bit *zerorow, *onesrow ;
+    integer *repeatptr, *bitcounts ;
+    integer count ;
+    integer test ;
+    integer curptr, rowptr ;
+    integer bitval ;
+    integer repeatflag ;
+    integer colptr ;
+    integer currepeat ;
+    integer dynf ;
+    integer deriv[14] ;
+    integer bcompsize ;
+    boolean firston ;
+    integer flagbyte ;
+    boolean state ;
+    boolean on ;
+    integer hbit ;
+    integer pbit ;
+    boolean ron, son ;
+    integer rcount, scount ;
+    integer ri, si ;
+    integer max2 ;
+    integer predpkloc ;
+    integer buff ;
+   
+    integer tfwid = widthtab[tfmindex[car]] ;
+    integer hesc = horzesc[car] ;
+    integer vesc = vertesc[car] ;
+    integer xoff = xoffset[car] ;
+    integer yoff = yoffset[car] ;
+   
+    MALLOCARRAY(repeatptr, height + 1);
+    MALLOCARRAY(bitcounts, height * width);
+    if (repeatptr == NULL || bitcounts == NULL)
+        pm_error("out of memory while allocating bit counts");
+    zerorow = pbm_allocrow(width) ;      /* initialize plain rows */
+    onesrow = pbm_allocrow(width) ;
+    for (i = 0 ; i < width ; i++) {
+        zerorow[i] = PBM_WHITE ;
+        onesrow[i] = PBM_BLACK ;
+    }
+    for (i=0; i < height; i = k) {       /* set repeat pointers */
+        k = i + 1;
+        if (!equal(bitmap[i], zerorow) && !equal(bitmap[i], onesrow)) {
+            while (k < height && equal(bitmap[i], bitmap[k]))
+                k++;
+            repeatptr[i] = k - i - 1;
+        } else {
+            repeatptr[i] = 0;
+        }
+    }
+    repeatptr[height] = 0 ;
+    colptr = width - 1 ;
+    repeatflag = currepeat = curptr = count = rowptr = 0 ;
+    test = PBM_WHITE ;
+    do {
+        colptr++ ;
+        if (colptr == width) {            /* end of row, get next row */
+            colptr = 0 ;
+            rowptr = currepeat ;
+            if (repeatptr[currepeat] > 0) {
+                repeatflag = repeatptr[currepeat] ;
+                currepeat += repeatflag ;
+                rowptr += repeatflag ;
+            }
+            currepeat++ ;
+        }
+        if (rowptr >= height) bitval = -1 ;
+        else bitval = bitmap[rowptr][colptr] ;
+        if (bitval == test) count++ ;     /* count repeated pixels */
+        else {                    /* end of pixel run */
+            bitcounts[curptr++] = count ;
+            if (curptr+3 >= height*width)
+                pm_error("out of memory while saving character counts");
+            count = 1 ;
+            test = bitval ;
+            if (repeatflag > 0) {
+                bitcounts[curptr++] = -repeatflag ;
+                repeatflag = 0 ;
+            }
+        }
+    } while (test != -1) ;
+    bitcounts[curptr] = 0 ;
+    bitcounts[curptr + 1] = 0 ;
+    for (i = 1 ; i <= 13 ; i ++) deriv[i] = 0 ;
+    i = firston = (bitcounts[0] == 0) ;
+    compsize = 0 ;
+    while (bitcounts[i] != 0) {          /* calculate dyn_f */
+        j = bitcounts[i] ;
+        if (j == -1) compsize++ ;
+        else {
+            if (j < 0) {
+                compsize++ ;
+                j = -j ;
+            }
+            if (j < 209) compsize += 2 ;
+            else {
+                k = j - 193 ;
+                while (k >= 16) {
+                    k >>= 4 ;
+                    compsize += 2 ;
+                }
+                compsize++ ;
+            }
+            if (j < 14) (deriv[j])-- ;
+            else if (j < 209) (deriv[(223 - j) / 15])++ ;
+            else {
+                k = 16 ;
+                while (((k<<4) < j + 3)) k <<= 4 ;
+                if (j - k <= 192)
+                    deriv[(207 - j + k) / 15] += 2 ;
+            }
+        }
+        i++ ;
+    }
+    bcompsize = compsize ;
+    dynf = 0 ;
+    for (i = 1 ; i <= 13 ; i ++) {
+        compsize += deriv[i] ;
+        if (compsize <= bcompsize) {
+            bcompsize = compsize ;
+            dynf = i ;
+        }
+    }
+    compsize = ((bcompsize + 1)>>1) ;
+    if ((compsize > ((height*width+7)>>3)) || (height*width == 0)) {
+        compsize = ((height*width+7)>>3) ;
+        dynf = 14 ;
+    }
+    flagbyte = (dynf<<4) ;
+    if (firston) flagbyte |= 8 ;
+    if (tfwid > 16777215 || tfwid < 0 || hesc < 0 || vesc != 0 ||
+        compsize > 196579 || width > 65535 || height > 65535 ||
+        xoff > 32767 || yoff > 32767 || xoff < -32768 || yoff < -32768) {
+        flagbyte |= 7 ;               /* long form preamble */
+        pbmtopk_pkbyte(flagbyte) ;
+        compsize += 28 ;
+        pkword(compsize) ;            /* char packet size */
+        pkword(car) ;             /* character number */
+        predpkloc = pbmtopk_pkloc + compsize ;
+        pkword(tfwid) ;               /* TFM width */
+        pkword(hesc<<16) ;            /* horiz escapement */
+        pkword(vesc<<16) ;            /* vert escapement */
+        pkword(width) ;               /* bounding box width */
+        pkword(height) ;              /* bounding box height */
+        pkword(xoff) ;                /* horiz offset */
+        pkword(yoff) ;                /* vert offset */
+    } else if (hesc > 255 || width > 255 || height > 255 ||
+               xoff > 127 || yoff > 127 || xoff < -128 ||
+               yoff < -128 || compsize > 1016) {
+        compsize += 13 ;              /* extended short preamble */
+        flagbyte += (compsize>>16) + 4 ;
+        pbmtopk_pkbyte(flagbyte) ;
+        pkhalfword(compsize & 65535) ;        /* char packet size */
+        pbmtopk_pkbyte(car) ;             /* character number */
+        predpkloc = pbmtopk_pkloc + compsize ;
+        pkthreebytes(tfwid) ;         /* TFM width */
+        pkhalfword(hesc) ;            /* horiz escapement */
+        pkhalfword(width) ;           /* bounding box width */
+        pkhalfword(height) ;          /* bounding box height */
+        pkhalfword(xoff) ;            /* horiz offset */
+        pkhalfword(yoff) ;            /* vert offset */
+    } else {
+        compsize += 8 ;               /* short form preamble */
+        flagbyte = flagbyte + (compsize>>8) ;
+        pbmtopk_pkbyte(flagbyte) ;
+        pbmtopk_pkbyte(compsize & 255) ;          /* char packet size */
+        pbmtopk_pkbyte(car) ;             /* character number */
+        predpkloc = pbmtopk_pkloc + compsize ;
+        pkthreebytes(tfwid) ;         /* TFM width */
+        pbmtopk_pkbyte(hesc) ;                /* horiz escapement */
+        pbmtopk_pkbyte(width) ;               /* bounding box width */
+        pbmtopk_pkbyte(height) ;              /* bounding box height */
+        pbmtopk_pkbyte(xoff) ;                /* horiz offset */
+        pbmtopk_pkbyte(yoff) ;                /* vert offset */
+    }
+    if (dynf != 14) {                /* write packed character */
+        bitweight = 16 ;
+        max2 = 208 - 15 * dynf ;
+        i = firston ;
+        while (bitcounts[i] != 0) {
+            j = bitcounts[i] ;
+            if (j == - 1) pknyb(15) ;
+            else {
+                if (j < 0) {
+                    pknyb(14) ;
+                    j = -j ;
+                }
+                if (j <= dynf) pknyb(j) ;
+                else if (j <= max2) {
+                    j -= dynf + 1 ;
+                    pknyb((j >> 4) + dynf + 1) ;
+                    pknyb((j & 15)) ;
+                } else {
+                    j -= max2 - 15 ;
+                    k = 16 ;
+                    while (k <= j) {
+                        k <<= 4 ;
+                        pknyb(0) ;
+                    }
+                    while (k > 1) {
+                        k >>= 4 ;
+                        pknyb(j / k) ;
+                        j = j % k ;
+                    }
+                }
+            }
+            i++ ;
+        }
+        if (bitweight != 16) pbmtopk_pkbyte(outputbyte) ;
+    } else {                 /* write bitmap character */
+        buff = 0 ;
+        pbit = 8 ;
+        i = firston ;
+        hbit = width ;
+        on = ! firston ;
+        state = 0 ;
+        count = repeatflag = 0 ;
+        while ((bitcounts[i] != 0) || state || (count > 0)) {
+            if (state) {
+                count = rcount ;
+                i = ri ;
+                on = ron ;
+                repeatflag-- ;
+            } else {
+                rcount = count ;
+                ri = i ;
+                ron = on ;
+            }
+            do {
+                if (count == 0) {
+                    if (bitcounts[i] < 0) {
+                        if (! state) repeatflag = -bitcounts[i] ;
+                        i++ ;
+                    }
+                    count = bitcounts[i] ;
+                    i++ ;
+                    on = !on ;
+                }
+                if ((count >= pbit) && (pbit < hbit)) {
+                    if (on) buff += (1 << pbit) - 1 ;
+                    pbmtopk_pkbyte(buff) ;
+                    buff = 0 ;
+                    hbit -= pbit ;
+                    count -= pbit ;
+                    pbit = 8 ;
+                } else if ((count < pbit) && (count < hbit)) {
+                    if (on) buff += (1 << pbit) - (1 << (pbit - count)) ;
+                    pbit -=  count ;
+                    hbit -= count ;
+                    count = 0 ;
+                } else {
+                    if (on) buff += (1 << pbit) - (1 << (pbit - hbit)) ;
+                    count -= hbit ;
+                    pbit -= hbit ;
+                    hbit = width ;
+                    if (pbit == 0) {
+                        pbmtopk_pkbyte(buff) ;
+                        buff = 0 ;
+                        pbit = 8 ;
+                    }
+                }
+            } while (hbit != width) ;
+            if (state && (repeatflag == 0)) {
+                count = scount ;
+                i = si ;
+                on = son ;
+                state = 0 ;
+            } else if (! state && (repeatflag > 0)) {
+                scount = count ;
+                si = i ;
+                son = on ;
+                state = 1 ;
+            }
+        }
+        if (pbit != 8) pbmtopk_pkbyte(buff) ;
+    }
+    if (predpkloc != pbmtopk_pkloc)
+        pm_error("bad predicted character length: character %d", car);
+    pbm_freerow(zerorow); 
+    pbm_freerow(onesrow); 
+    free((char *)repeatptr);
+    free((char *)bitcounts);
+}
+
+
+
+/* check that character is in valid range */
+static void 
+checkchar(void) {
+    if (car < 0 || car >= MAXPKCHAR)
+        pm_error("character must be in range 0 to %d", MAXPKCHAR-1) ;
+}
+
+
+
+/* read character information from an option file */
+static void 
+optionfile(const char * const name) {
+
+    FILE *fp ;
+    char buffer[MAXOPTLINE] ;
+   
+    fp = pm_openr(name);
+    while (!feof(fp)) {
+        char *here = buffer;
+      
+        if (fgets(buffer, MAXOPTLINE, fp) == NULL) break ;
+        while (ISSPACE(*here)) here++ ;
+        if (*here && *here == '=') {
+            if (sscanf(here+1, "%d", &car) != 1)
+                pm_error("bad option file line %s", buffer) ;
+        } else if (*here && *here != '%' && *here != '#') {
+            char str[NAMELENGTH] ;
+            integer i, n;
+     
+            checkchar() ;
+            if (sscanf(here, "%s%n", str, &n) != 1)
+                pm_error("bad option file line %s", buffer) ;
+            filename[car] = strdup(str);
+            if (filename[car] == NULL)
+                pm_error("out of memory allocating filename %s", str);
+            for (i = 1; i < 256; i<<=1) {
+                here += n;
+                if (sscanf(here, "%s%n", str, &n) != 1) break ;
+                if (strcmp(str, "*")) {
+                    charflags[car] |= i ;
+                    switch (i) {
+                    case XOFFSET:
+                        xoffset[car] = atoi(str) ;
+                        break ;
+                    case YOFFSET:
+                        yoffset[car] = atoi(str) ;
+                        break ;
+                    case HORZESC:
+                        horzesc[car] = atoi(str) ;
+                        break ;
+                    case VERTESC:
+                        vertesc[car] = atoi(str) ;
+                        break ;
+                    case TFMWIDTH:
+                        tfmindex[car] = add_tfmwidth(fixword(atof(str))) ;
+                        break ;
+                    case TFMHEIGHT:
+                        hgtindex[car] = add_tfmheight(fixword(atof(str))) ;
+                        break ;
+                    case TFMDEPTH:
+                        depindex[car] = add_tfmdepth(fixword(atof(str))) ;
+                        break ;
+                    case TFMITALIC:
+                        italindex[car] = add_tfmitalic(fixword(atof(str))) ;
+                        break ;
+                    }
+                }
+            }
+            car++ ;
+        }
+    }
+    pm_close(fp) ;
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+    integer i, hesc, vesc, xoff, yoff, tfwid, tfdep, tfhgt, tfital ;
+    byte flags ;
+    const char * const usage = "pkfile[.pk] tfmfile[.tfm] dpi "
+        "[-s designsize] [-p num param...]\n"
+        "[-C codingscheme ] [-F family] [-c num | <char>]...\n"
+        "<char> is:\n"
+        "[-W tfmwidth] [-H tfmheight] [-D tfmdepth] [-I ital_corr] "
+        "[-h horiz]\n"
+        "[-v vert] [-x xoffset] [-y yoffset] file\n"
+        "or:\n"
+        "-f optfile\n" ;
+
+    pbm_init(&argc, argv);
+    initialize_pk() ;
+   
+    if (--argc < 1) pm_usage(usage) ;
+    strcpy(pkname, *++argv) ;
+    pbmtopk_add_suffix(pkname, ".pk") ;
+   
+    if (--argc < 1) pm_usage(usage) ;
+    strcpy(tfmname, *++argv) ;
+    pbmtopk_add_suffix(tfmname, ".tfm") ;
+   
+    if (--argc < 1) pm_usage(usage) ;
+    resolution = atoi(*++argv) ;
+    if (resolution < 1 || resolution > 32767)
+        pm_error("unlikely resolution %d dpi", resolution);
+   
+    car = flags = hesc = vesc = xoff = yoff = tfwid = 0;
+    while (++argv, --argc) {
+        if (argv[0][0] == '-' && argv[0][1]) {
+            char c, *p;
+            c = argv[0][1] ;
+            if (argv[0][2]) p = *argv + 2 ;    /* set argument pointer */
+            else if (++argv, --argc) p = *argv ;
+            else pm_usage(usage) ;
+            switch (c) {
+            case 'C':
+                codingscheme = p;
+                break ;
+            case 'F':
+                familyname = p;
+                break ;
+            case 'c':
+                car = atoi(p) ;
+                break ;
+            case 's':
+                designsize = fixword(atof(p));
+                if (designsize < 1048576)
+                    pm_error("design size %f out of range", 
+                             unfixword(designsize));
+            case 'h':
+                hesc = atoi(p) ;
+                flags |= HORZESC ;
+                break ;
+            case 'v':
+                vesc = atoi(p) ;
+                flags |= VERTESC ;
+                break ;
+            case 'x':
+                xoff = atoi(p) ;
+                flags |= XOFFSET ;
+                break ;
+            case 'y':
+                yoff = atoi(p) ;
+                flags |= YOFFSET ;
+                break ;
+            case 'W':
+                tfwid = fixword(atof(p)) ;
+                flags |= TFMWIDTH ;
+                break ;
+            case 'H':
+                tfhgt = fixword(atof(p)) ;
+                flags |= TFMHEIGHT ;
+                break ;
+            case 'D':
+                tfdep = fixword(atof(p)) ;
+                flags |= TFMDEPTH ;
+                break ;
+            case 'I':
+                tfital = fixword(atof(p)) ;
+                flags |= TFMITALIC ;
+                break ;
+            case 'f':
+                optionfile(p) ;
+                break ;
+            case 'p':
+                numparam = atoi(p);
+                if (numparam < 1 || numparam > MAXPARAMS)
+                    pm_error("parameter count %d out of range", numparam);
+                for (i=0; i<numparam; i++)
+                    if (++argv,--argc)
+                        parameters[i] = fixword(atof(*argv)) ;
+                    else
+                        pm_error("not enough parameters (-p)");
+                break ;
+            default:
+                pm_usage(usage) ;
+            }
+        } else  {
+            checkchar() ;
+            if (flags & TFMWIDTH)
+                tfmindex[car] = add_tfmwidth(tfwid);
+            if (flags & TFMDEPTH)
+                depindex[car] = add_tfmdepth(tfdep);
+            if (flags & TFMHEIGHT)
+                hgtindex[car] = add_tfmheight(tfhgt);
+            if (flags & TFMITALIC)
+                italindex[car] = add_tfmitalic(tfital);
+            horzesc[car] = hesc ;
+            vertesc[car] = vesc ;
+            xoffset[car] = xoff ;
+            yoffset[car] = yoff ;
+            filename[car] = *argv ;
+            charflags[car] = flags ;
+            car++ ;
+            flags = 0;
+        }
+    }
+    hppp = ROUND((resolution<<16) / 72.27) ;
+    pkfile = pm_openw(pkname);
+    tfmfile = pm_openw(tfmname);
+    writepreamble() ;
+    for (car = 0 ; car < MAXPKCHAR ; car++)
+        if (filename[car]) {
+            readcharacter() ;
+            shipcharacter() ;
+        }
+    writepostamble() ;
+    writetfmfile() ;
+    pm_close(pkfile) ;
+    pm_close(tfmfile) ;
+
+    return 0;
+}
+
diff --git a/converter/pbm/pbmtoplot.c b/converter/pbm/pbmtoplot.c
new file mode 100644
index 00000000..8c9075b2
--- /dev/null
+++ b/converter/pbm/pbmtoplot.c
@@ -0,0 +1,76 @@
+/* pbmtoplot.c - read a portable bitmap and produce a UNIX-format plot file.
+**
+** Copyright (C) 1990 by Arthur David Olson.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <stdio.h>
+#include "pbm.h"
+
+static void puttwo ARGS((int i));
+static void
+puttwo( i )
+    int i;
+    {
+    (void) putchar(i);
+    (void) putchar(i >> 8);
+    }
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    register bit** bits;
+    register int row, col, scol;
+    int	rows, cols;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pbmfile]" );
+
+    ifp = (argc == 2) ? pm_openr( argv[1] ) : stdin;
+
+    bits = pbm_readpbm( ifp, &cols, &rows );
+
+    pm_close( ifp );
+
+    (void) putchar( 's' );
+    puttwo( 0 );
+    puttwo( 0 );
+    puttwo( rows - 1 );
+    puttwo( cols - 1 );
+    for ( row = 0; row < rows; ++row )
+	{
+	for ( col = 0; col < cols; ++col )
+	    {
+	    if ( bits[row][col] == PBM_WHITE )
+		continue;
+	    scol = col;
+	    while ( ++col < cols && bits[row][col] == PBM_BLACK )
+		; /* nothing */
+	    --col;
+	    if ( col == scol )
+		(void) putchar( 'p' );
+	    else
+		{
+		(void) putchar( 'l' );
+		puttwo( scol );
+		puttwo( rows - 1 - row );
+		}
+	    puttwo( col );
+	    puttwo( rows - 1 - row );
+	    }
+	}
+
+    exit( 0 );
+    }
diff --git a/converter/pbm/pbmtoppa/CREDITS b/converter/pbm/pbmtoppa/CREDITS
new file mode 100644
index 00000000..007d9a09
--- /dev/null
+++ b/converter/pbm/pbmtoppa/CREDITS
@@ -0,0 +1,12 @@
+CREDITS
+-------
+
+This project would not be where it is without the help of the following
+people:
+
+Ben Boule - first contacted me about the 720 series and helped with testing
+
+Jim Peterson - spent hours modifying the code for the 720 and adding lots of
+features, including all the configurability options.
+
+Kirk Reiten - helped with testing the 1000 series code
diff --git a/converter/pbm/pbmtoppa/INSTALL-MORE b/converter/pbm/pbmtoppa/INSTALL-MORE
new file mode 100644
index 00000000..2565c740
--- /dev/null
+++ b/converter/pbm/pbmtoppa/INSTALL-MORE
@@ -0,0 +1,187 @@
+Installation of ppa-0.8.5 with S.u.S.E. Linux
+(Special Installation with Hp820 and paper size A4)
+___________________________________________________
+
+
+0. Introduction
+
+This text describes how to use the package pbm2ppa written by Tim Norman
+with the S.u.S.E Linux System. This program allows the use of GDI (Winows
+only) printers with Linux. The program pbm2ppa is actually a converter
+between the two formats pbm (an output format from ghostscript) and the
+format understood by the HP printers 720, 820 and 1000. So anyway we have to
+use ghostscript to produce pbm (or faster: pbmraw) files. To print ascii
+files there is an extra step invoking enscript to convert the ascii to
+postscript files.  I rather constructed two new printer spoolers in printcap
+from scratch then using apsfilter: one for postscript files and another for
+ascii files. I welcome solutions in combination with the apsfilter script.
+The installation is quiet easy - after seven steps you should be ready to
+print postscript and ascii files, but it may take some time to adjust the
+constants properly, don't despair.
+
+0.1. Modifications
+
+This was modified on October 18, 1998 by Tim Norman to conform to the new
+A4 paper support in version 0.8.5.
+
+
+1. Installation of program package ppa-0.8.5
+
+Get the packate at http://www.rpi.edu/~normat/technical/ppa/ and compile it
+with
+
+# make 820
+
+or put in your printer number (720, 820 or 1000) (see also INSTALL and README
+file).
+
+
+2. To adjust the paper size to DIN A4, use the -s a4 option to pbm2ppa or
+change your pbm2ppa.conf file to read "papersize a4" (see step 5).
+
+
+3. You can now calibrate the printer with 
+
+For US size paper:
+# pbmtpg | pbm2ppa > /dev/lp1 ( as root )
+For A4 size paper:
+# pbmtpg -a4 | pbm2ppa -s a4 > /dev/lp1 ( as root )
+
+or you try first printing some sample files and calibrate in step 5.
+
+
+4. Now you can print (postscript) files with a shell script like this:
+
+Contents of print:
+
+cat $1 | gs -sDEVICE=pbmraw -q -dNOPAUSE -r600 -sOutputFile=- - | \
+pbm2ppa - - >/dev/lp1
+
+After changing the file modes (i.e. chmod 755 print) you are able to print
+a postscript file invoking the shell script print like:
+
+# print filename.ps
+
+
+To print ascii files just extend the script print with the use of enscript:
+
+Contents of printascii:
+
+enscript -2rj -p- $1 | \
+gs -sDEVICE=pbmraw -q -dNOPAUSE -r600 -sOutputFile=- - | \
+pbm2ppa - - >/dev/lp1
+
+
+Check the manpage for enscript to adjust the options to your flavour.
+Now you can also print ascii files with
+
+# printascii filename.ascii
+
+
+5. It may be possible that you have to recalibrate your printer (see 3.)
+
+Here follow the results from my calibration after printing ascii files with the
+shell script printascii (see 4.). The program pbm2ppa takes the arguments in the
+following order:
+
+1. shell arguments
+2. config file /etc/pbm2ppa.conf
+3. Compiled options from default.h
+
+So whenever you invoke pbm2ppa without arguments the program uses the options
+stored in the file /etc/pbm2ppa.conf, so I suggest to leave there a copy of
+this file.
+
+# Sample configuration file for the HP820 and DIN A4 paper size
+#
+# This file will be automatically read upon startup if it's placed in
+# /etc/pbm2ppa.conf
+#
+
+version  820
+
+papersize	a4
+
+xoff      0 # \ Adjust these for your printer.
+yoff    -600 # / (see CALIBRATE)
+
+# 1/4 inch margins all around (at 600 DPI)
+top       50
+bottom    50
+left      50
+right     50
+
+
+6. To integrate the converter into the Linux system we create two printer
+spooler in /etc/printcap. One to print postscript files and another to print
+plain ascii files.
+
+Contents of /etc/printcap:
+
+lp:\
+        :lp=/dev/lp1:\
+        :sd=/var/spool/lpd/lp:\
+        :lf=/var/spool/lpd/lp/log:\
+        :af=/var/spool/lpd/lp/acct:\
+        :if=/usr/local/bin/ps.if:\
+        :la:mx#0:\
+        :sh:sf:
+
+ascii:\
+        :lp=/dev/lp1:\
+        :sd=/var/spool/lpd/ascii:\
+        :lf=/var/spool/lpd/ascii/log:\
+        :af=/var/spool/lpd/ascii/acct:\
+        :if=/usr/local/bin/ascii.if:\
+        :la:mx#0:\
+        :sh:sf:
+
+
+Here follow some explanations (for more information consult the printcap
+manpage). We use the lp1 device, have two spool directories
+/var/spool/lpd/ascii and /var/spool/lpd/lp (better you create them now) a log
+file (lf) an accounting file (af), suppress form feeds (sf), suppress printing
+of burst page header (sh) and the maximum file size is unlimited (mx#0). To
+integrate the converter pbm2ppa into the system we use two input filters. Maybe
+you have a better solution in combination with apsfilter but until then try
+this way. Actually, the two input filter files are almost identical with the
+shell scripts print and printascii we created in step 4.
+
+File /usr/local/bin/ascii.if:
+
+#! /bin/sh
+enscript -2rj -p- | \
+gs -sDEVICE=pbmraw -q -dNOPAUSE -r600 -sOutputFile=- - | \
+/usr/local/bin/pbm2ppa - -
+
+
+File /usr/local/bin/ps.if:
+
+#! /bin/sh
+gs -sDEVICE=pbmraw -q -dNOPAUSE -r600 -sOutputFile=- - | \
+/usr/local/bin/pbm2ppa - -
+
+
+7. Place pbm2ppa in the directory /usr/local/bin. Now you are ready to print
+files with
+
+# lpr filename.ps
+
+and
+
+# lpr -P ascii filename.ascii
+
+like you are used to it.
+
+Enjoy
+
+
+19. May 1998
+
+Michael Buehlmann
+Badenerstrasse 285
+8003 Zuerich
+Switzerland
+
+mbuehlma@stud.ee.ethz.ch
+
diff --git a/converter/pbm/pbmtoppa/LICENSE b/converter/pbm/pbmtoppa/LICENSE
new file mode 100644
index 00000000..b8512125
--- /dev/null
+++ b/converter/pbm/pbmtoppa/LICENSE
@@ -0,0 +1,342 @@
+

+                    GNU GENERAL PUBLIC LICENSE

+                       Version 2, June 1991

+

+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.

+                          675 Mass Ave, Cambridge, MA 02139, USA

+ Everyone is permitted to copy and distribute verbatim copies

+ of this license document, but changing it is not allowed.

+

+                            Preamble

+

+  The licenses for most software are designed to take away your

+freedom to share and change it.  By contrast, the GNU General Public

+License is intended to guarantee your freedom to share and change free

+software--to make sure the software is free for all its users.  This

+General Public License applies to most of the Free Software

+Foundation's software and to any other program whose authors commit to

+using it.  (Some other Free Software Foundation software is covered by

+the GNU Library General Public License instead.)  You can apply it to

+your programs, too.

+

+  When we speak of free software, we are referring to freedom, not

+price.  Our General Public Licenses are designed to make sure that you

+have the freedom to distribute copies of free software (and charge for

+this service if you wish), that you receive source code or can get it

+if you want it, that you can change the software or use pieces of it

+in new free programs; and that you know you can do these things.

+

+  To protect your rights, we need to make restrictions that forbid

+anyone to deny you these rights or to ask you to surrender the rights.

+These restrictions translate to certain responsibilities for you if you

+distribute copies of the software, or if you modify it.

+

+  For example, if you distribute copies of such a program, whether

+gratis or for a fee, you must give the recipients all the rights that

+you have.  You must make sure that they, too, receive or can get the

+source code.  And you must show them these terms so they know their

+rights.

+

+  We protect your rights with two steps: (1) copyright the software, and

+(2) offer you this license which gives you legal permission to copy,

+distribute and/or modify the software.

+

+  Also, for each author's protection and ours, we want to make certain

+that everyone understands that there is no warranty for this free

+software.  If the software is modified by someone else and passed on, we

+want its recipients to know that what they have is not the original, so

+that any problems introduced by others will not reflect on the original

+authors' reputations.

+

+  Finally, any free program is threatened constantly by software

+patents.  We wish to avoid the danger that redistributors of a free

+program will individually obtain patent licenses, in effect making the

+program proprietary.  To prevent this, we have made it clear that any

+patent must be licensed for everyone's free use or not licensed at all.

+

+  The precise terms and conditions for copying, distribution and

+modification follow.

+

+                    GNU GENERAL PUBLIC LICENSE

+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

+

+  0. This License applies to any program or other work which contains

+a notice placed by the copyright holder saying it may be distributed

+under the terms of this General Public License.  The "Program", below,

+refers to any such program or work, and a "work based on the Program"

+means either the Program or any derivative work under copyright law:

+that is to say, a work containing the Program or a portion of it,

+either verbatim or with modifications and/or translated into another

+language.  (Hereinafter, translation is included without limitation in

+the term "modification".)  Each licensee is addressed as "you".

+

+Activities other than copying, distribution and modification are not

+covered by this License; they are outside its scope.  The act of

+running the Program is not restricted, and the output from the Program

+is covered only if its contents constitute a work based on the

+Program (independent of having been made by running the Program).

+Whether that is true depends on what the Program does.

+

+  1. You may copy and distribute verbatim copies of the Program's

+source code as you receive it, in any medium, provided that you

+conspicuously and appropriately publish on each copy an appropriate

+copyright notice and disclaimer of warranty; keep intact all the

+notices that refer to this License and to the absence of any warranty;

+and give any other recipients of the Program a copy of this License

+along with the Program.

+

+You may charge a fee for the physical act of transferring a copy, and

+you may at your option offer warranty protection in exchange for a fee.

+

+  2. You may modify your copy or copies of the Program or any portion

+of it, thus forming a work based on the Program, and copy and

+distribute such modifications or work under the terms of Section 1

+above, provided that you also meet all of these conditions:

+

+    a) You must cause the modified files to carry prominent notices

+    stating that you changed the files and the date of any change.

+

+    b) You must cause any work that you distribute or publish, that in

+    whole or in part contains or is derived from the Program or any

+    part thereof, to be licensed as a whole at no charge to all third

+    parties under the terms of this License.

+

+    c) If the modified program normally reads commands interactively

+    when run, you must cause it, when started running for such

+    interactive use in the most ordinary way, to print or display an

+    announcement including an appropriate copyright notice and a

+    notice that there is no warranty (or else, saying that you provide

+    a warranty) and that users may redistribute the program under

+    these conditions, and telling the user how to view a copy of this

+    License.  (Exception: if the Program itself is interactive but

+    does not normally print such an announcement, your work based on

+    the Program is not required to print an announcement.)

+

+These requirements apply to the modified work as a whole.  If

+identifiable sections of that work are not derived from the Program,

+and can be reasonably considered independent and separate works in

+themselves, then this License, and its terms, do not apply to those

+sections when you distribute them as separate works.  But when you

+distribute the same sections as part of a whole which is a work based

+on the Program, the distribution of the whole must be on the terms of

+this License, whose permissions for other licensees extend to the

+entire whole, and thus to each and every part regardless of who wrote it.

+

+Thus, it is not the intent of this section to claim rights or contest

+your rights to work written entirely by you; rather, the intent is to

+exercise the right to control the distribution of derivative or

+collective works based on the Program.

+

+In addition, mere aggregation of another work not based on the Program

+with the Program (or with a work based on the Program) on a volume of

+a storage or distribution medium does not bring the other work under

+the scope of this License.

+

+  3. You may copy and distribute the Program (or a work based on it,

+under Section 2) in object code or executable form under the terms of

+Sections 1 and 2 above provided that you also do one of the following:

+

+    a) Accompany it with the complete corresponding machine-readable

+    source code, which must be distributed under the terms of Sections

+    1 and 2 above on a medium customarily used for software interchange; or,

+

+    b) Accompany it with a written offer, valid for at least three

+    years, to give any third party, for a charge no more than your

+    cost of physically performing source distribution, a complete

+    machine-readable copy of the corresponding source code, to be

+    distributed under the terms of Sections 1 and 2 above on a medium

+    customarily used for software interchange; or,

+

+    c) Accompany it with the information you received as to the offer

+    to distribute corresponding source code.  (This alternative is

+    allowed only for noncommercial distribution and only if you

+    received the program in object code or executable form with such

+    an offer, in accord with Subsection b above.)

+

+The source code for a work means the preferred form of the work for

+making modifications to it.  For an executable work, complete source

+code means all the source code for all modules it contains, plus any

+associated interface definition files, plus the scripts used to

+control compilation and installation of the executable.  However, as a

+special exception, the source code distributed need not include

+anything that is normally distributed (in either source or binary

+form) with the major components (compiler, kernel, and so on) of the

+operating system on which the executable runs, unless that component

+itself accompanies the executable.

+

+If distribution of executable or object code is made by offering

+access to copy from a designated place, then offering equivalent

+access to copy the source code from the same place counts as

+distribution of the source code, even though third parties are not

+compelled to copy the source along with the object code.

+

+  4. You may not copy, modify, sublicense, or distribute the Program

+except as expressly provided under this License.  Any attempt

+otherwise to copy, modify, sublicense or distribute the Program is

+void, and will automatically terminate your rights under this License.

+However, parties who have received copies, or rights, from you under

+this License will not have their licenses terminated so long as such

+parties remain in full compliance.

+

+  5. You are not required to accept this License, since you have not

+signed it.  However, nothing else grants you permission to modify or

+distribute the Program or its derivative works.  These actions are

+prohibited by law if you do not accept this License.  Therefore, by

+modifying or distributing the Program (or any work based on the

+Program), you indicate your acceptance of this License to do so, and

+all its terms and conditions for copying, distributing or modifying

+the Program or works based on it.

+

+  6. Each time you redistribute the Program (or any work based on the

+Program), the recipient automatically receives a license from the

+original licensor to copy, distribute or modify the Program subject to

+these terms and conditions.  You may not impose any further

+restrictions on the recipients' exercise of the rights granted herein.

+You are not responsible for enforcing compliance by third parties to

+this License.

+

+  7. If, as a consequence of a court judgment or allegation of patent

+infringement or for any other reason (not limited to patent issues),

+conditions are imposed on you (whether by court order, agreement or

+otherwise) that contradict the conditions of this License, they do not

+excuse you from the conditions of this License.  If you cannot

+distribute so as to satisfy simultaneously your obligations under this

+License and any other pertinent obligations, then as a consequence you

+may not distribute the Program at all.  For example, if a patent

+license would not permit royalty-free redistribution of the Program by

+all those who receive copies directly or indirectly through you, then

+the only way you could satisfy both it and this License would be to

+refrain entirely from distribution of the Program.

+

+If any portion of this section is held invalid or unenforceable under

+any particular circumstance, the balance of the section is intended to

+apply and the section as a whole is intended to apply in other

+circumstances.

+

+It is not the purpose of this section to induce you to infringe any

+patents or other property right claims or to contest validity of any

+such claims; this section has the sole purpose of protecting the

+integrity of the free software distribution system, which is

+implemented by public license practices.  Many people have made

+generous contributions to the wide range of software distributed

+through that system in reliance on consistent application of that

+system; it is up to the author/donor to decide if he or she is willing

+to distribute software through any other system and a licensee cannot

+impose that choice.

+

+This section is intended to make thoroughly clear what is believed to

+be a consequence of the rest of this License.

+

+  8. If the distribution and/or use of the Program is restricted in

+certain countries either by patents or by copyrighted interfaces, the

+original copyright holder who places the Program under this License

+may add an explicit geographical distribution limitation excluding

+those countries, so that distribution is permitted only in or among

+countries not thus excluded.  In such case, this License incorporates

+the limitation as if written in the body of this License.

+

+  9. The Free Software Foundation may publish revised and/or new versions

+of the General Public License from time to time.  Such new versions will

+be similar in spirit to the present version, but may differ in detail to

+address new problems or concerns.

+

+Each version is given a distinguishing version number.  If the Program

+specifies a version number of this License which applies to it and "any

+later version", you have the option of following the terms and conditions

+either of that version or of any later version published by the Free

+Software Foundation.  If the Program does not specify a version number of

+this License, you may choose any version ever published by the Free Software

+Foundation.

+

+  10. If you wish to incorporate parts of the Program into other free

+programs whose distribution conditions are different, write to the author

+to ask for permission.  For software which is copyrighted by the Free

+Software Foundation, write to the Free Software Foundation; we sometimes

+make exceptions for this.  Our decision will be guided by the two goals

+of preserving the free status of all derivatives of our free software and

+of promoting the sharing and reuse of software generally.

+

+                            NO WARRANTY

+

+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY

+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN

+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES

+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED

+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF

+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS

+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE

+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,

+REPAIR OR CORRECTION.

+

+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING

+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR

+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,

+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING

+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED

+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY

+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER

+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE

+POSSIBILITY OF SUCH DAMAGES.

+

+                     END OF TERMS AND CONDITIONS

+

+        Appendix: How to Apply These Terms to Your New Programs

+

+  If you develop a new program, and you want it to be of the greatest

+possible use to the public, the best way to achieve this is to make it

+free software which everyone can redistribute and change under these terms.

+

+  To do so, attach the following notices to the program.  It is safest

+to attach them to the start of each source file to most effectively

+convey the exclusion of warranty; and each file should have at least

+the "copyright" line and a pointer to where the full notice is found.

+

+    <one line to give the program's name and a brief idea of what it does.>

+    Copyright (C) 19yy  <name of author>

+

+    This program is free software; you can redistribute it and/or modify

+    it under the terms of the GNU General Public License as published by

+    the Free Software Foundation; either version 2 of the License, or

+    (at your option) any later version.

+

+    This program is distributed in the hope that it will be useful,

+    but WITHOUT ANY WARRANTY; without even the implied warranty of

+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

+    GNU General Public License for more details.

+

+    You should have received a copy of the GNU General Public License

+    along with this program; if not, write to the Free Software

+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

+

+Also add information on how to contact you by electronic and paper mail.

+

+If the program is interactive, make it output a short notice like this

+when it starts in an interactive mode:

+

+    Gnomovision version 69, Copyright (C) 19yy name of author

+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.

+    This is free software, and you are welcome to redistribute it

+    under certain conditions; type `show c' for details.

+

+The hypothetical commands `show w' and `show c' should show the appropriate

+parts of the General Public License.  Of course, the commands you use may

+be called something other than `show w' and `show c'; they could even be

+mouse-clicks or menu items--whatever suits your program.

+

+You should also get your employer (if you work as a programmer) or your

+school, if any, to sign a "copyright disclaimer" for the program, if

+necessary.  Here is a sample; alter the names:

+

+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program

+  `Gnomovision' (which makes passes at compilers) written by James Hacker.

+

+  <signature of Ty Coon>, 1 April 1989

+  Ty Coon, President of Vice

+

+This General Public License does not permit incorporating your program into

+proprietary programs.  If your program is a subroutine library, you may

+consider it more useful to permit linking proprietary applications with the

+library.  If this is what you want to do, use the GNU Library General

+Public License instead of this License.

+

+

diff --git a/converter/pbm/pbmtoppa/Makefile b/converter/pbm/pbmtoppa/Makefile
new file mode 100644
index 00000000..c4be08b7
--- /dev/null
+++ b/converter/pbm/pbmtoppa/Makefile
@@ -0,0 +1,25 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/pbm/pbmtoppa
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+all: pbmtoppa
+
+BINARIES = pbmtoppa
+
+MERGEBINARIES = $(BINARIES)
+
+OBJECTS = pbmtoppa.o ppa.o pbm.o cutswath.o
+MERGE_OBJECTS = pbmtoppa.o2 ppa.o pbm.o cutswath.o
+
+include $(SRCDIR)/Makefile.common
+
+pbmtoppa: $(OBJECTS) $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o pbmtoppa $(OBJECTS) \
+	  -lm $(shell $(LIBOPT) $(NETPBMLIB)) $(LDLIBS) \
+	  $(RPATH) $(LADD)
+
diff --git a/converter/pbm/pbmtoppa/README.Netpbm b/converter/pbm/pbmtoppa/README.Netpbm
new file mode 100644
index 00000000..46c781e2
--- /dev/null
+++ b/converter/pbm/pbmtoppa/README.Netpbm
@@ -0,0 +1,30 @@
+Pbmtoppa was integrated into the Netpbm package in May 2000 by Bryan
+Henderson.  He took it from Tim Norton's pbm2ppa-0.8.6 package dated
+October 1998.
+
+Tim licenses the subject package to the public as described in the
+LICENSE file.
+
+The difference between what's in Netpbm and what was distributed by 
+Tim is:
+
+  - Tim called it 'pbm2ppa' Netpbm calls it 'pbmtoppa', to fit Netpbm
+    naming conventions.
+
+  - Tim's package included the program Pbmtpg, but the Netpbm directory
+    doesn't include that.  The program was integrated separately into
+    Netpbm as Pbmpage.
+
+  - Tim's package generated 3 different versions of Pbmtoppa, one for
+    each of the 720, 820 or 1000 printer models.  (They only differed
+    in their default parameters).  The netpbm version has only one
+    Pbmtoppa, and the (existing) -v option selects defaults for the 
+    specified printer model.
+
+  - Tim didn't have a man page.  His package had several other
+    documentation files, though, which are not in the Netpbm package
+    because the information is either specific to Tim's packaging or
+    the information in in the Netpbm man page.
+
+  - Jozsef Marak's extension to handle draft (300 dpi) printing is 
+    included.
diff --git a/converter/pbm/pbmtoppa/README.REDHAT b/converter/pbm/pbmtoppa/README.REDHAT
new file mode 100644
index 00000000..3586aea2
--- /dev/null
+++ b/converter/pbm/pbmtoppa/README.REDHAT
@@ -0,0 +1,29 @@
+RedHat users may find the following tip from Panayotis Vryonis <vrypan@hol.gr>
+helpful!
+
+Here is a tip to intergrate HP720C support in RedHat's printtool:
+
+Install pbm2ppa. Copy pbm2ppa to /usr/bin.
+Edit "printerdb" (in my system it is found in
+/usr/lib/rhs/rhs-printfilters )
+and append the following lines:
+----------------------Cut here
+-------------------------------------------
+StartEntry: DeskJet720C
+  GSDriver: pbm
+  Description: {HP DeskJet 720C}
+  About: { \
+           This driver supports the HP DeskJet 720C inkjet printer. \
+           It does does not support color printing. \
+           IMPORTANT! Insert \
+                "- | pbm2ppa -" \
+           in the "Extra GS Otions" field.\
+         }
+  Resolution: {600} {600} {}
+EndEntry
+--------------------------------------------------------------------------
+
+Now you can add an HP720C printer just like any other, using printtool.
+
+[Author's Note: The same should work for the 820 and 1000, but it hasn't
+been tested.  Also, use the pbmraw GSDriver if you have it; it's faster. ]
diff --git a/converter/pbm/pbmtoppa/cutswath.c b/converter/pbm/pbmtoppa/cutswath.c
new file mode 100644
index 00000000..0d44ce45
--- /dev/null
+++ b/converter/pbm/pbmtoppa/cutswath.c
@@ -0,0 +1,360 @@
+/* cutswath.c
+ * functions to cut a swath of a PBM file for PPA printers
+ * Copyright (c) 1998 Tim Norman.  See LICENSE for details.
+ * 3-15-98
+ *
+ * Mar 15, 1998  Jim Peterson  <jspeter@birch.ee.vt.edu>
+ *
+ *     Structured to accommodate both the HP820/1000, and HP720 series.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ppa.h"
+#include "ppapbm.h"
+#include "cutswath.h"
+
+extern int Width;
+extern int Height;
+extern int Pwidth;
+
+/* sweep_data->direction must be set already */
+/* Upon successful completion, sweep_data->image_data and
+   sweep_data->nozzle_data have been set to pointers which this routine
+   malloc()'d. */
+/* Upon successful completion, all members of *sweep_data have been set
+   except direction, vertical_pos, and next. */
+/* Returns: 0 if unsuccessful
+            1 if successful, but with non-printing result (end of page)
+            2 if successful, with printing result */
+int 
+cut_pbm_swath(pbm_stat* pbm,ppa_stat* prn,int maxlines,ppa_sweep_data* sweep_data)
+{
+  unsigned char *data, *ppa, *place, *maxplace;
+  int p_width, width8, p_width8;
+  int i, j, left, right, got_nonblank, numlines;
+  int horzpos, hp2;
+  int shift;
+  ppa_nozzle_data nozzles[2];
+
+  /* shift = 6 if DPI==300  */
+  /* shift = 12 if DPI==600 */ 
+  shift = ( prn->DPI == 300 ? 6:12 ) ;
+  
+  /* safeguard against the user freeing these */
+  sweep_data->image_data=NULL;
+  sweep_data->nozzle_data=NULL;
+
+  /* read the data from the input file */
+  width8 = (pbm->width + 7) / 8;
+ 
+/* 
+  fprintf(stderr,"cutswath(): width=%u\n",pbm->width);
+  fprintf(stderr,"cutswath(): height=%u\n",pbm->height);
+*/
+
+  if ((data=malloc(width8*maxlines)) == NULL)
+  {
+    fprintf(stderr,"cutswath(): could not malloc data storage\n");
+    return 0;
+  }
+
+  /* ignore lines that are above the upper margin */
+  while(pbm->current_line < prn->top_margin)
+    if(!pbm_readline(pbm,data))
+    {
+      fprintf(stderr,"cutswath(): A-could not read top margin\n");
+      free(data);
+      return 0;
+    }
+
+  /* eat all lines that are below the lower margin */
+  if(pbm->current_line >= Height - prn->bottom_margin)
+  {
+    while(pbm->current_line < pbm->height)
+      if(!pbm_readline(pbm,data))
+      {
+	fprintf(stderr,"cutswath(): could not clear bottom margin\n");
+	free(data);
+	return 0;
+      }
+    free(data);
+    return 1;
+  }
+
+  left = Pwidth-prn->right_margin/8;
+  right = prn->left_margin/8;
+
+  /* eat all beginning blank lines and then up to maxlines or lower margin */
+  got_nonblank=numlines=0;
+  while( (pbm->current_line < Height-prn->bottom_margin) &&
+	 (numlines < maxlines) )
+  {
+    if(!pbm_readline(pbm,data+width8*numlines))
+    {
+      fprintf(stderr,"cutswath(): B-could not read next line\n");
+      free(data);
+      return 0;
+    }
+    if(!got_nonblank)
+      for(j=prn->left_margin/8; j<left; j++)
+	if(data[j])
+	{
+	  left = j;
+	  got_nonblank=1;
+	  break;
+	}
+    if(got_nonblank)
+      {
+	int newleft = left, newright = right;
+
+	/* find left-most nonblank */
+	for (i = prn->left_margin/8; i < left; i++)
+	  if (data[width8*numlines+i])
+	    {
+	      newleft = i;
+	      break;
+	    }
+	/* find right-most nonblank */
+	for (i = Pwidth-prn->right_margin/8-1; i >= right; i--)
+	  if (data[width8*numlines+i])
+	    {
+	      newright = i;
+	      break;
+	    }
+	numlines++;
+
+	if (newright < newleft)
+	  {
+	    fprintf (stderr, "Ack! newleft=%d, newright=%d, left=%d, right=%d\n",
+		     newleft, newright, left, right);
+	    free (data);
+	    return 0;
+	  }
+
+	/* if the next line might push us over the buffer size, stop here! */
+	/* ignore this test for the 720 right now.  Will add better */
+	/* size-guessing for compressed data in the near future! */
+	if (numlines % 2 == 1 && prn->version != HP720)
+	  {
+	    int l = newleft, r = newright, w;
+	    
+	    l--;
+	    r+=2;
+	    l*=8;
+	    r*=8;
+	    w = r-l;
+	    w = (w+7)/8;
+	    
+	    if ((w+2*shift)*numlines > prn->bufsize)
+	      {
+		numlines--;
+		pbm_unreadline (pbm, data+width8*numlines);
+		break;
+	      }
+	    else
+	      {
+		left = newleft;
+		right = newright;
+	      }
+	  }
+	else
+	  {
+	    left = newleft;
+	    right = newright;
+	  }
+      }
+  }
+
+  if(!got_nonblank)
+  {
+    /* eat all lines that are below the lower margin */
+    if(pbm->current_line >= Height - prn->bottom_margin)
+    {
+      while(pbm->current_line < pbm->height)
+	if(!pbm_readline(pbm,data))
+	{
+	  fprintf(stderr,"cutswath(): could not clear bottom margin\n");
+	  free(data);
+	  return 0;
+	}
+      free(data);
+      return 1;
+    }
+    free(data);
+    return 0; /* error, since didn't get to lower margin, yet blank */
+  }
+
+  /* make sure numlines is even and >= 2 (b/c we have to pass the printer 
+     HALF of the number of pins used */
+  if (numlines == 1)
+    {
+      /* there's no way that we only have 1 line and not enough memory, so
+	 we're safe to increase numlines here.  Also, the bottom margin should
+	 be > 0 so we have some lines to read */
+      if(!pbm_readline(pbm,data+width8*numlines))
+	{
+	  fprintf(stderr,"cutswath(): C-could not read next line\n");
+	  free(data);
+	  return 0;
+	}
+      numlines++;
+    }
+  if (numlines % 2 == 1)
+    {
+      /* decrease instead of increasing so we don't max out the buffer */
+      numlines--;
+      pbm_unreadline (pbm, data+width8*numlines);
+    }
+
+  /* calculate vertical position */
+  sweep_data->vertical_pos = pbm->current_line;
+
+  /* change sweep params */
+  left--;
+  right+=2;
+  left *= 8;
+  right *= 8;
+
+  /* construct the sweep data */
+  p_width = right - left;
+  p_width8 = (p_width + 7) / 8;
+
+  if ((ppa = malloc ((p_width8+2*shift) * numlines)) == NULL)
+    {
+      fprintf(stderr,"cutswath(): could not malloc ppa storage\n");
+      free (data);
+      return 0;
+    }
+
+  place = ppa;
+
+  /* place 0's in the first 12 columns */
+  memset (place, 0, numlines/2 * shift);
+  place += numlines/2 * shift;
+
+
+  if(sweep_data->direction == right_to_left)  /* right-to-left */
+  {
+    for (i = p_width8+shift-1; i >= 0; i--)
+    {
+      if (i >= shift)
+      {
+	for (j = 0; j < numlines/2; j++)
+	  *place++ = data[j*2*width8 + i + left/8-shift];
+      }
+      else
+      {
+	memset (place, 0, numlines/2);
+	place += numlines/2;
+      }
+
+      if (i < p_width8)
+      {
+	for (j = 0; j < numlines/2; j++)
+	  *place++ = data[(j*2+1)*width8 + i + left/8];
+      }
+      else
+      {
+	memset (place, 0, numlines/2);
+	place += numlines/2;
+      }
+    }
+  }
+  else /* sweep_data->direction == left_to_right */
+  {
+    for (i = 0; i < p_width8+shift; i++)
+    {
+      if (i < p_width8)
+      {
+	for (j = 0; j < numlines/2; j++)
+	  *place++ = data[(j*2+1)*width8 + i + left/8];
+      }
+      else
+      {
+	memset (place, 0, numlines/2);
+	place += numlines/2;
+      }
+
+      if (i >= shift)
+      {
+	for (j = 0; j < numlines/2; j++)
+	  *place++ = data[j*2*width8 + i + left/8 - shift];
+      }
+      else
+      {
+	memset (place, 0, numlines/2);
+	place += numlines/2;
+      }
+    }
+  }
+
+  /* done with data */
+  free(data);
+
+  /* place 0's in the last 12 columns */
+  memset (place, 0, numlines/2 * shift);
+  place += numlines/2 * shift;
+  maxplace = place;
+
+  /* create sweep data */
+  sweep_data->image_data = ppa;
+  sweep_data->data_size = maxplace-ppa;
+  sweep_data->in_color = False;
+
+  /*
+  horzpos = left*600/prn->DPI + (sweep_data->direction==left_to_right ? 0*600/prn->DPI : 0);
+  */
+  horzpos = left * 600/prn->DPI;
+
+  hp2 = horzpos + ( p_width8 + 2*shift )*8 * 600/prn->DPI;
+  
+ 
+  sweep_data->left_margin = horzpos;
+  sweep_data->right_margin = hp2 + prn->marg_diff;
+
+  
+  for (i = 0; i < 2; i++)
+  {
+    nozzles[i].DPI = prn->DPI;
+        
+    nozzles[i].pins_used_d2 = numlines/2;
+    nozzles[i].unused_pins_p1 = 301-numlines;
+    nozzles[i].first_pin = 1;
+    if (i == 0)
+    {
+      nozzles[i].left_margin = horzpos + prn->marg_diff;
+      nozzles[i].right_margin = hp2 + prn->marg_diff;
+      if(sweep_data->direction == right_to_left)
+       /* 0 */
+	nozzles[i].nozzle_delay=prn->right_to_left_delay[0];
+      else
+       /* 6 */
+	nozzles[i].nozzle_delay=prn->left_to_right_delay[0];
+    }
+    else
+    {
+      nozzles[i].left_margin = horzpos;
+      nozzles[i].right_margin = hp2;
+      if(sweep_data->direction == right_to_left)
+       /* 2 */
+	nozzles[i].nozzle_delay=prn->right_to_left_delay[1];
+      else
+       /* 0 */
+	nozzles[i].nozzle_delay=prn->left_to_right_delay[1];
+
+    }
+  }
+
+  sweep_data->nozzle_data_size = 2;
+  sweep_data->nozzle_data = malloc(sizeof(nozzles));
+  if(sweep_data->nozzle_data == NULL)
+    return 0;
+  memcpy(sweep_data->nozzle_data,nozzles,sizeof(nozzles));
+
+  return 2;
+}
+
+
diff --git a/converter/pbm/pbmtoppa/cutswath.h b/converter/pbm/pbmtoppa/cutswath.h
new file mode 100644
index 00000000..430558de
--- /dev/null
+++ b/converter/pbm/pbmtoppa/cutswath.h
@@ -0,0 +1,4 @@
+int 
+cut_pbm_swath(pbm_stat* pbm,ppa_stat* prn,int maxlines,
+              ppa_sweep_data* sweep_data);
+
diff --git a/converter/pbm/pbmtoppa/defaults.h b/converter/pbm/pbmtoppa/defaults.h
new file mode 100644
index 00000000..481b9585
--- /dev/null
+++ b/converter/pbm/pbmtoppa/defaults.h
@@ -0,0 +1,65 @@
+/* defaults.h
+ * Default printer values.  Edit these and recompile if so desired.
+ * [Note: a /etc/pbm2ppa.conf file will override these]
+ */
+#ifndef _DEFAULTS_H
+#define _DEFAULTS_H
+
+/* Refer to CALIBRATION file about these settings */
+
+#define HP1000_PRINTER        ( HP1000 )
+
+#define HP1000_MARG_DIFF      (  0x62 )
+#define HP1000_BUFSIZE        ( 100*1024 )
+
+#define HP1000_X_OFFSET       (   100 )
+#define HP1000_Y_OFFSET       (  -650 )
+
+#define HP1000_TOP_MARGIN     (   150 )
+#define HP1000_LEFT_MARGIN    (   150 )
+#define HP1000_RIGHT_MARGIN   (   150 )
+#define HP1000_BOTTOM_MARGIN  (   150 )
+
+
+#define HP720_PRINTER        ( HP720 )
+
+#define HP720_MARG_DIFF      (     2 )
+#define HP720_BUFSIZE        ( 200*1024 )
+
+#define HP720_X_OFFSET       (   169 )
+#define HP720_Y_OFFSET       (  -569 )
+
+#define HP720_TOP_MARGIN     (   150 )
+#define HP720_LEFT_MARGIN    (   150 )
+#define HP720_RIGHT_MARGIN   (   150 )
+#define HP720_BOTTOM_MARGIN  (   150 )
+
+
+#define HP820_PRINTER        ( HP820 )
+
+#define HP820_MARG_DIFF      (  0x62 )
+#define HP820_BUFSIZE        ( 100*1024 )
+
+#define HP820_X_OFFSET       (    75 )
+#define HP820_Y_OFFSET       (  -500 )
+
+#define HP820_TOP_MARGIN     (    80 )
+#define HP820_LEFT_MARGIN    (    80 )
+#define HP820_RIGHT_MARGIN   (    80 )
+#define HP820_BOTTOM_MARGIN  (   150 )
+
+
+
+#define DEFAULT_PRINTER        HP1000_PRINTER
+
+#define DEFAULT_X_OFFSET       HP1000_X_OFFSET
+#define DEFAULT_Y_OFFSET       HP1000_Y_OFFSET
+
+#define DEFAULT_TOP_MARGIN     HP1000_TOP_MARGIN
+#define DEFAULT_LEFT_MARGIN    HP1000_LEFT_MARGIN
+#define DEFAULT_RIGHT_MARGIN   HP1000_RIGHT_MARGIN
+#define DEFAULT_BOTTOM_MARGIN  HP1000_BOTTOM_MARGIN
+
+#define DEFAULT_DPI ( 600 )
+
+#endif
diff --git a/converter/pbm/pbmtoppa/pbm.c b/converter/pbm/pbmtoppa/pbm.c
new file mode 100644
index 00000000..5c9798f2
--- /dev/null
+++ b/converter/pbm/pbmtoppa/pbm.c
@@ -0,0 +1,135 @@
+/* pbm.c
+ * code for reading the header of an ASCII PBM file
+ * Copyright (c) 1998 Tim Norman.  See LICENSE for details
+ * 2-25-98
+ *
+ * Mar 18, 1998  Jim Peterson  <jspeter@birch.ee.vt.edu>
+ *
+ *     Restructured to encapsulate more of the PBM handling.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ppapbm.h"
+
+int make_pbm_stat(pbm_stat* pbm,FILE* fptr)
+{
+  char line[1024];
+
+  pbm->fptr=fptr;
+  pbm->version=none;
+  pbm->current_line=0;
+  pbm->unread = 0;
+
+  if (fgets (line, 1024, fptr) == NULL)
+    return 0;
+  line[strlen(line)-1] = 0;
+
+  if(!strcmp(line,"P1")) pbm->version=P1;
+  if(!strcmp(line,"P4")) pbm->version=P4;
+  if(pbm->version == none)
+  {
+    fprintf(stderr,"pbm_readheader(): unknown PBM magic '%s'\n",line);
+    return 0;
+  }
+
+  do
+    if (fgets (line, 1024, fptr) == NULL)
+      return 0;
+  while (line[0] == '#');
+
+  if (2 != sscanf (line, "%d %d", &pbm->width, &pbm->height))
+    return 0;
+
+  return 1;
+}
+
+static int getbytes(FILE *fptr,int width,unsigned char* data)
+{
+  unsigned char mask,acc,*place;
+  int num;
+
+  if(!width) return 0;
+  for(mask=0x80, acc=0, num=0, place=data; num<width; )
+  {
+    switch(getc(fptr))
+    {
+    case EOF:      
+      return 0;
+    case '1':
+      acc|=mask;
+      /* fall through */
+    case '0':
+      mask>>=1;
+      num++;
+      if(!mask) /* if(num%8 == 0) */
+      {
+	*place++ = acc;
+	acc=0;
+	mask=0x80;
+      }
+    }
+  }
+  if(width%8)
+    *place=acc;
+  return 1;
+}
+
+/* Reads a single line into data which must be at least (pbm->width+7)/8
+   bytes of storage */
+int pbm_readline(pbm_stat* pbm,unsigned char* data)
+{
+  int tmp,tmp2;
+
+  if(pbm->current_line >= pbm->height) return 0;
+
+  if (pbm->unread)
+    {
+      memcpy (data, pbm->revdata, (pbm->width+7)/8);
+      pbm->current_line++;
+      pbm->unread = 0;
+      free (pbm->revdata);
+      return 1;
+    }
+
+  switch(pbm->version)
+  {
+  case P1:
+    if(getbytes(pbm->fptr,pbm->width,data))
+    {
+      pbm->current_line++;
+      return 1;
+    }
+    return 0;
+
+  case P4:
+    tmp=(pbm->width+7)/8;
+    tmp2=fread(data,1,tmp,pbm->fptr);
+    if(tmp2 == tmp)
+    {
+      pbm->current_line++;
+      return 1;
+    }
+    fprintf(stderr,"pbm_readline(): error reading line data (%d)\n",tmp2);
+    return 0;
+
+  default:
+    fprintf(stderr,"pbm_readline(): unknown PBM version\n");
+    return 0;
+  }
+}
+
+/* push a line back into the buffer; we read too much! */
+void pbm_unreadline (pbm_stat *pbm, void *data)
+{
+  /* can only store one line in the unread buffer */
+  if (pbm->unread)
+    return;
+
+  pbm->unread = 1;
+  pbm->revdata = malloc ((pbm->width+7)/8);
+  memcpy (pbm->revdata, data, (pbm->width+7)/8);
+  pbm->current_line--;
+}
diff --git a/converter/pbm/pbmtoppa/pbmtoppa.c b/converter/pbm/pbmtoppa/pbmtoppa.c
new file mode 100644
index 00000000..85a98529
--- /dev/null
+++ b/converter/pbm/pbmtoppa/pbmtoppa.c
@@ -0,0 +1,449 @@
+/* pbmtoppa.c
+ * program to print a 600 dpi PBM file on the HP DJ820Cse or the HP720 series.
+ * Copyright (c) 1998 Tim Norman.  See LICENSE for details
+ * 2-24-98
+ */
+
+#define _BSD_SOURCE
+    /* This makes sure strcasecmp() is in string.h */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "pbm.h"
+#include "ppa.h"
+#include "ppapbm.h"
+#include "cutswath.h"
+
+#include "defaults.h"
+
+/* Paper sizes in 600ths of an inch. */
+
+/* US is 8.5 in by 11 in */
+
+#define USWIDTH  (5100)
+#define USHEIGHT (6600)
+
+/* A4 is 210 mm by 297 mm == 8.27 in by 11.69 in */
+
+#define A4WIDTH  (4960)
+#define A4HEIGHT (7016)
+
+int Width;      /* width and height in 600ths of an inch */
+int Height;
+int Pwidth;     /* width in bytes */
+
+#define MAX_LINES 300
+
+ppa_stat printer;
+
+static int 
+print_pbm(FILE * const in) {
+
+    char line[1024];
+    pbm_stat pbm;
+    int done_page, numpages = 0;
+    ppa_sweep_data sweeps[2];
+    int current_sweep, previous_sweep;
+
+    ppa_init_job(&printer);
+
+    while(make_pbm_stat(&pbm, in)) {
+        if (pbm.width != Width || pbm.height != Height) 
+            pm_error("print_pbm(): Input image is not the size "
+                    "of a page for Page %d.  "
+                    "The input is %dW x %dH, "
+                     "while a page is %dW x %dH pixels.\n"
+                    "Page size is controlled by options and the configuration "
+                    "file.",
+                     numpages+1, pbm.width, pbm.height, Width, Height);
+
+        ppa_init_page(&printer);
+        ppa_load_page(&printer);
+
+        sweeps[0].direction = right_to_left;
+        sweeps[0].next=&sweeps[1];
+        sweeps[1].direction = left_to_right;
+        sweeps[1].next=&sweeps[0];
+
+        current_sweep=0;
+        previous_sweep=-1;
+
+        done_page=0;
+        while(!done_page) {
+            int rc;
+            rc = cut_pbm_swath(&pbm, &printer, MAX_LINES,
+                               &sweeps[current_sweep]);
+            switch(rc) {
+            case 0:
+                pm_error("print_pbm(): cut_pbm_swath() failed.");
+                break;
+            case 1:
+                done_page=1;
+                break;
+            case 2:
+                if (previous_sweep >= 0) {
+                    ppa_print_sweep(&printer, &sweeps[previous_sweep]);
+                    free(sweeps[previous_sweep].image_data);
+                    free(sweeps[previous_sweep].nozzle_data);
+                }
+                previous_sweep=current_sweep;
+                current_sweep= current_sweep==0 ? 1 : 0;
+                break;
+            default:
+                pm_error("print_pbm(): unknown return code from "
+                         "cut_pbm_swath()");
+            }
+        }
+        if (previous_sweep >= 0) {
+            sweeps[previous_sweep].next=NULL;
+            ppa_print_sweep(&printer,&sweeps[previous_sweep]);
+        }
+
+        free(sweeps[0].image_data);
+        free(sweeps[0].nozzle_data);
+        free(sweeps[1].image_data);
+        free(sweeps[1].nozzle_data);
+
+        ppa_eject_page(&printer);
+
+        /* eat any remaining whitespace */
+        if(pbm.version==P1)
+            fgets (line, 1024, in);
+
+        ++numpages;
+    }
+
+    if (numpages == 0)
+        pm_error("No pages printed!");
+
+    ppa_end_print(&printer);
+
+    fclose (pbm.fptr);
+    fclose (printer.fptr);
+
+    return 0;
+}
+
+
+
+static void 
+set_printer_specific_defaults()
+{
+    switch(printer.version)
+    {
+    case HP720:
+        printer.marg_diff     = HP720_MARG_DIFF;
+        printer.bufsize       = HP720_BUFSIZE;
+
+        printer.x_offset      = HP720_X_OFFSET;
+        printer.y_offset      = HP720_Y_OFFSET;
+        printer.top_margin    = HP720_TOP_MARGIN;
+        printer.left_margin   = HP720_LEFT_MARGIN;
+        printer.right_margin  = HP720_RIGHT_MARGIN;
+        printer.bottom_margin = HP720_BOTTOM_MARGIN;
+        break;
+    case HP820:
+        printer.marg_diff     = HP820_MARG_DIFF;
+        printer.bufsize       = HP820_BUFSIZE;
+
+        printer.x_offset      = HP820_X_OFFSET;
+        printer.y_offset      = HP820_Y_OFFSET;
+        printer.top_margin    = HP820_TOP_MARGIN;
+        printer.left_margin   = HP820_LEFT_MARGIN;
+        printer.right_margin  = HP820_RIGHT_MARGIN;
+        printer.bottom_margin = HP820_BOTTOM_MARGIN;
+        break;
+    case HP1000:
+        printer.marg_diff     = HP1000_MARG_DIFF;
+        printer.bufsize       = HP1000_BUFSIZE;
+
+        printer.x_offset      = HP1000_X_OFFSET;
+        printer.y_offset      = HP1000_Y_OFFSET;
+        printer.top_margin    = HP1000_TOP_MARGIN;
+        printer.left_margin   = HP1000_LEFT_MARGIN;
+        printer.right_margin  = HP1000_RIGHT_MARGIN;
+        printer.bottom_margin = HP1000_BOTTOM_MARGIN;
+        break;
+    default:
+        pm_error("set_printer_defaults(): unknown printer version");
+    }
+}
+
+
+
+static void 
+show_usage(const char* const prog)
+{
+    printf("usage: %s [ options ] [ <infile> [ <outfile> ] ]\n\n",prog);
+    printf("  Prints a pbm- or pbmraw-format <infile> to HP720/820/1000-format <outfile>.\n\n");
+    printf("    -v <version>    printer version (720, 820, or 1000)\n");
+    printf("    -x <xoff>       vertical offset adjustment in 1\"/600\n");
+    printf("    -y <yoff>       horizontal offset adjustment in 1\"/600\n");
+    printf("    -t <topmarg>    top margin in 1\"/600    (default: 150 = 0.25\")\n");
+    printf("    -l <leftmarg>   left margin in 1\"/600   (default: 150 = 0.25\")\n");
+    printf("    -r <rightmarg>  right margin in 1\"/600  (default: 150 = 0.25\")\n");
+    printf("    -b <botmarg>    bottom margin in 1\"/600 (default: 150 = 0.25\")\n");
+    printf("    -s <paper>      paper size (us, a4, default: us)\n");
+    printf("    -f <cfgfile>    read <cfgfile> as parameters\n\n");
+    printf("  The -x and -y options accumulate.  The -v option resets the horizontal and\n");
+    printf("  vertical adjustments to an internal default.  <infile> and <outfile> default\n");
+    printf("  to stdin and stdout.  '-' is a synonym for stdin and stdout.\n\n");
+    printf("  Configuration files specified with the '-f' parameter have the following\n  format:\n\n");
+    printf("    # Comment\n");
+    printf("    <key1> <value1>\n");
+    printf("    <key2> <value2>\n");
+    printf("    [etc.]\n\n");
+    printf("  Valid keys are 'version', 'xoffset', 'yoffset', 'topmargin', 'leftmargin',\n");
+    printf("  'rightmargin', 'bottommargin', 'papersize', or any non-null truncated\n");
+    printf("  version of these words.  Valid values are the same as with the corresponding\n");
+    printf("  command-line parameters.  Parameters in the configuration file act as though\n");
+    printf("  the corresponding parameters were substituted, in order, for the '-f'\n");
+    printf("  parameter which specified the file.\n\n");
+    printf("  The file /etc/pbmtoppa.conf, if it exists, is processed as a configuration\n");
+    printf("  file before any command-line parameters are processed.\n\n");
+}
+
+
+
+static void 
+parm_version(char* arg)
+{
+    if(!strcasecmp(arg,"hp720") || !strcmp(arg,"720"))
+        printer.version=HP720;
+    else if(!strcasecmp(arg,"hp820") || !strcmp(arg,"820"))
+        printer.version=HP820;
+    else if(!strcasecmp(arg,"hp1000") || !strcmp(arg,"1000"))
+        printer.version=HP1000;
+    else
+        pm_error("parm_version(): unknown printer version '%s'",arg);
+    set_printer_specific_defaults();
+}
+
+
+
+static void 
+parm_iversion(int arg)
+{
+    switch(arg)
+    {
+    case 720:
+        printer.version=HP720;
+        break;
+    case 820:
+        printer.version=HP820;
+        break;
+    case 1000:
+        printer.version=HP1000;
+        break;
+    default:
+        pm_error("parm_iversion(): unknown printer version '%d'", arg);
+    }
+    set_printer_specific_defaults();
+}
+
+
+
+static void 
+dump_config()
+{
+    printf("version:  ");
+    switch(printer.version)
+    {
+    case HP710:  printf("HP710\n");  break;
+    case HP720:  printf("HP720\n");  break;
+    case HP820:  printf("HP820\n");  break;
+    case HP1000: printf("HP1000\n"); break;
+    }
+    printf("x-offset: %d\ny-offset: %d\nmargins:\n top:    %d\n"
+           " left:   %d\n right:  %d\n bottom: %d\n",printer.x_offset,
+           printer.y_offset,printer.top_margin,printer.left_margin,
+           printer.right_margin,printer.bottom_margin);
+    exit(0);
+}
+
+
+
+static void 
+read_config_file(const char* const fname)
+{
+    FILE* cfgfile=fopen(fname,"r");
+    char line[1024],key[14],buf[10];
+    int len,value,lineno=1;
+
+    if(!cfgfile)
+        pm_error("read_config_file(): couldn't open file '%s'", fname);
+
+    while(fgets(line,1024,cfgfile))
+    {
+        if(strchr(line,'#'))
+            *strchr(line,'#')=0;
+        switch(sscanf(line,"%13s%9s",key,buf))
+        {
+        case 2:
+            value=atoi(buf);
+            len=strlen(key);
+            if(!strncmp(key,"version",len))
+                parm_iversion(value);
+            else if(!strncmp(key,"xoffset",len))
+                printer.x_offset=value;
+            else if(!strncmp(key,"yoffset",len))
+                printer.y_offset=value;
+            else if(!strncmp(key,"topmargin",len))
+                printer.top_margin=value;
+            else if(!strncmp(key,"leftmargin",len))
+                printer.left_margin=value;
+            else if(!strncmp(key,"rightmargin",len))
+                printer.right_margin=value;
+            else if(!strncmp(key,"bottommargin",len))
+                printer.bottom_margin=value;
+            else if(!strncmp(key,"papersize",len))
+            {
+                if(!strcmp(buf,"us"))
+                {
+                    Width = USWIDTH;
+                    Height = USHEIGHT;
+                }
+                else if(!strcmp(buf,"a4"))
+                {
+                    Width = A4WIDTH;
+                    Height = A4HEIGHT;
+                }
+                else
+                    pm_error("read_config_file(): unknown paper size %s", buf);
+            }
+            else if(!strcmp(key,"dump"))
+                dump_config();
+            else 
+                pm_error("read_config_file(): unrecognized parameter '%s' "
+                         "(line %d)", key, lineno);
+        case EOF:
+        case 0: 
+            break;
+        default:
+            pm_error("read_config_file(): error parsing config file "
+                     "(line %d)", lineno);
+        }
+        lineno++;
+    }
+
+    if(feof(cfgfile))
+    {
+        fclose(cfgfile);
+        return;
+    }
+
+    pm_error("read_config_file(): error parsing config file");
+}
+
+
+
+const char* const defaultcfgfile="/etc/pbmtoppa.conf";
+
+
+
+int 
+main(int argc, char *argv[]) {
+
+    int argn;
+    int got_in=0, got_out=0, do_continue=1;
+    FILE *in=stdin, *out=stdout;
+    struct stat tmpstat;
+
+    pbm_init(&argc, argv);
+
+    printer.version       = DEFAULT_PRINTER;
+    printer.x_offset      = DEFAULT_X_OFFSET;
+    printer.y_offset      = DEFAULT_Y_OFFSET;
+    printer.top_margin    = DEFAULT_TOP_MARGIN;
+    printer.left_margin   = DEFAULT_LEFT_MARGIN;
+    printer.right_margin  = DEFAULT_RIGHT_MARGIN;
+    printer.bottom_margin = DEFAULT_BOTTOM_MARGIN;
+    printer.DPI           = DEFAULT_DPI;
+    Width = USWIDTH;
+    Height = USHEIGHT;
+    set_printer_specific_defaults();
+
+    if(!stat(defaultcfgfile,&tmpstat))
+        read_config_file(defaultcfgfile);
+
+    for(argn=1; argn<argc; argn++)
+    {
+        if(!strcmp(argv[argn],"-h"))
+        {
+            show_usage(*argv);
+            return 0;
+        }
+        else if(!strcmp(argv[argn],"-d"))
+            dump_config();
+        else if(argn+1<argc)
+        {
+            do_continue=1;
+            if(!strcmp(argv[argn],"-v"))
+                parm_version(argv[++argn]);
+            else if(!strcmp(argv[argn],"-x"))
+                printer.x_offset+=atoi(argv[++argn]);
+            else if(!strcmp(argv[argn],"-y"))
+                printer.y_offset+=atoi(argv[++argn]);
+            else if(!strcmp(argv[argn],"-t"))
+                printer.top_margin=atoi(argv[++argn]);
+            else if(!strcmp(argv[argn],"-l"))
+                printer.left_margin=atoi(argv[++argn]);
+            else if(!strcmp(argv[argn],"-r"))
+                printer.right_margin=atoi(argv[++argn]);
+            else if(!strcmp(argv[argn],"-b"))
+                printer.bottom_margin=atoi(argv[++argn]);
+            else if(!strcmp(argv[argn],"-d"))
+                printer.DPI=atoi(argv[++argn]);
+            else if(!strcmp(argv[argn],"-s"))
+            {
+                argn++;
+                if(!strcmp(argv[argn],"us"))
+                {
+                    Width = USWIDTH;
+                    Height = USHEIGHT;
+                }
+                else if(!strcmp(argv[argn],"a4"))
+                {
+                    Width = A4WIDTH;
+                    Height = A4HEIGHT;
+                }
+                else
+                    pm_error("unknown paper size %s",argv[argn]);
+            }
+            else if(!strcmp(argv[argn],"-f"))
+                read_config_file(argv[++argn]);
+            else do_continue=0;
+            if(do_continue) continue;
+        }
+
+        if(!got_in)
+        {
+            if (strcmp (argv[argn], "-") == 0)
+                in = stdin;
+            else if ((in = fopen (argv[argn], "rb")) == NULL)
+                pm_error("main(): couldn't open file '%s'", 
+                         argv[argn]);
+            got_in=1;
+        }
+        else if(!got_out)
+        {
+            if (strcmp (argv[argn], "-") == 0)
+                out = stdout;
+            else if ((out = fopen (argv[argn], "wb")) == NULL)
+                pm_error("main(): couldn't open file '%s'", 
+                         argv[argn]);
+            got_out=1;
+        }
+        else
+            pm_error("main(): unrecognized parameter '%s'", argv[argn]);
+    }
+
+    Pwidth=(Width+7)/8;
+    printer.fptr=out;
+
+    return print_pbm (in);
+}
+
diff --git a/converter/pbm/pbmtoppa/pbmtoppa.conf.hp1000 b/converter/pbm/pbmtoppa/pbmtoppa.conf.hp1000
new file mode 100644
index 00000000..a3ba3260
--- /dev/null
+++ b/converter/pbm/pbmtoppa/pbmtoppa.conf.hp1000
@@ -0,0 +1,18 @@
+# Sample configuration file for the HP720
+#
+# This file will be automatically read upon startup if it's placed in
+# /etc/pbm2ppa.conf
+#
+
+version  1000
+
+papersize	us
+
+xoff     100 # \ Adjust these for your printer.
+yoff    -650 # / (see CALIBRATE)
+
+# 1/4 inch margins all around (at 600 DPI)
+top      150
+bottom   150
+left     150
+right    150
diff --git a/converter/pbm/pbmtoppa/pbmtoppa.conf.hp720 b/converter/pbm/pbmtoppa/pbmtoppa.conf.hp720
new file mode 100644
index 00000000..b8975564
--- /dev/null
+++ b/converter/pbm/pbmtoppa/pbmtoppa.conf.hp720
@@ -0,0 +1,18 @@
+# Sample configuration file for the HP720
+#
+# This file will be automatically read upon startup if it's placed in
+# /etc/pbm2ppa.conf
+#
+
+version  720
+
+papersize	us
+
+xoff     169 # \ Adjust these for your printer.
+yoff    -569 # / (see CALIBRATE)
+
+# 1/4 inch margins all around (at 600 DPI)
+top      150
+bottom   150
+left     150
+right    150
diff --git a/converter/pbm/pbmtoppa/pbmtoppa.conf.hp820 b/converter/pbm/pbmtoppa/pbmtoppa.conf.hp820
new file mode 100644
index 00000000..55a8cb7d
--- /dev/null
+++ b/converter/pbm/pbmtoppa/pbmtoppa.conf.hp820
@@ -0,0 +1,18 @@
+# Sample configuration file for the HP820
+#
+# This file will be automatically read upon startup if it's placed in
+# /etc/pbm2ppa.conf
+#
+
+version  820
+
+papersize	us
+
+xoff      75 # \ Adjust these for your printer.
+yoff    -500 # / (see CALIBRATE)
+
+# 1/4 inch margins all around (at 600 DPI)
+top       80
+bottom   150
+left      80
+right     80
diff --git a/converter/pbm/pbmtoppa/ppa.c b/converter/pbm/pbmtoppa/ppa.c
new file mode 100644
index 00000000..8363d927
--- /dev/null
+++ b/converter/pbm/pbmtoppa/ppa.c
@@ -0,0 +1,526 @@
+/* ppa.c
+ * functions to handle PPA communications
+ * Copyright (c) 1998 Tim Norman.  See LICENSE for details.
+ * 2-25-98
+ *
+ * Mar 3, 1998  Jim Peterson  <jspeter@birch.ee.vt.edu>
+ *
+ *     Restructured to accommodate both the HP820/1000, and HP720 series.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pm_config.h"
+  /* pm_config.h is necessary for __inline__ for those compilers that don't
+     define __inline__ themselves
+     */
+#include "ppa.h"
+
+/*
+  VLink packet structure:
+
+    Bytes  Description
+    --------------------------------------------
+      1    Packet-start marker (always '$')
+      1    Channel (0: image data, 1: commands/responses(*), 2: autostatus(*), 128: pacing(*))
+      2    Data length (N)
+      N    [remaining data (e.g., SCP or SCP2 packet when Channel=1)]
+
+  (*): responses, autostatus, and pacing are communicated from the printer to
+       the computer, and may be safely ignored.
+*/
+static void 
+vlink_put(FILE *fptr, int channel, int length, void *data)
+{
+  fputc ('$', fptr);
+  fputc (channel, fptr);
+  fputc (length>>8, fptr);     fputc (length&0xFF, fptr);
+  fwrite (data, length, 1, fptr);
+}
+
+/*
+  SCP packet structure:
+
+    Bytes  Description
+    --------------------------------------------
+      2    Command specifier
+      2    Command reference number
+      1    Priority
+      1    padding? (always zero)
+      2    Data length (N)
+      N    [remaining data]
+
+    ComSpec  ComRef     Command
+    -------------------------------
+       35       1     Initialize1
+      101       2     Initialize2
+       21       1     Initialize3
+       19       1     Handle Media
+       18       1     Print Sweep
+*/
+static void 
+scp_put(FILE *fptr, int comspec, int comref, int priority,
+	     int length, void *data)
+{
+  /* encapsulate the vlink_put call in here, to avoid a memcpy */
+  fputc ('$', fptr);
+  fputc (1, fptr);
+  fputc ((length+8)>>8, fptr);     fputc ((length+8)&0xFF, fptr);
+
+  fputc (comspec>>8, fptr);  fputc (comspec&0xFF, fptr);
+  fputc (comref>>8, fptr);   fputc (comref&0xFF, fptr);
+  fputc (priority, fptr);
+  fputc (0, fptr);
+  fputc (length>>8, fptr);   fputc (length&0xFF, fptr);
+
+  fwrite (data, length, 1, fptr);
+}
+
+
+/*
+  SCP2 packet structure:
+
+    Bytes  Description
+    --------------------------------------------
+      2    Command specifier
+      2    Packet length (N)
+      1    Priority
+      1    padding? (always zero)
+      2    Command reference number
+      4    Channel 0 buffer increment
+      4    version number? (always 00 02 00 00)
+     N-16  [remaining data]
+
+    ComSpec  ComRef     Command
+    -------------------------------
+     0x186      1     Initialize1
+     0x18f      2     Initialize2
+     0x183      1     Initialize3
+     0x181      1     Handle Media
+     0x180      1     Print Sweep
+*/
+static void 
+scp2_put(FILE *fptr,unsigned short comspec,unsigned short pkt_len_s16,
+	      unsigned char priority,unsigned short comref,unsigned data_len,
+	      void *data)
+{
+  /* encapsulate the vlink_put call in here, to avoid a memcpy */
+  fputc ('$', fptr);
+  fputc (1, fptr);
+  fputc ((pkt_len_s16+16)>>8, fptr);  fputc ((pkt_len_s16+16), fptr);
+
+  fputc (comspec>>8, fptr);  fputc (comspec, fptr);
+  fputc ((pkt_len_s16+16)>>8, fptr);  fputc ((pkt_len_s16+16), fptr);
+  fputc (priority, fptr);
+  fputc (0, fptr);
+  fputc (comref>>8, fptr);
+  fputc (comref, fptr);
+  fputc (data_len>>24, fptr);   fputc (data_len>>16, fptr);
+  fputc (data_len>>8, fptr);    fputc (data_len, fptr);
+  fputc (0, fptr);
+  fputc (2, fptr);
+  fputc (0, fptr);
+  fputc (0, fptr);
+
+  fwrite (data, pkt_len_s16, 1, fptr);
+}
+
+
+/*
+  SCP3 packet structure:
+
+    Bytes  Description
+    --------------------------------------------
+      2    Command specifier
+      2    Packet length (N)
+      1    Priority
+      1    padding? (always zero)
+      2    Command reference number
+      4    Channel 0 buffer increment
+      4    version number? (always 01 04 00 00)
+     N-16  [remaining data]
+
+    ComSpec  ComRef     Command
+    -------------------------------
+     0x186      1     Initialize1
+     0x18C     16     Initialize Printer name
+     0x1A1      1     Initialize4?
+     0x18f      2     Initialize2
+     0x183      1     Initialize3
+     0x181      1     Handle Media
+     0x180      1     Print Sweep
+*/
+static void 
+scp3_put(FILE *fptr,unsigned short comspec,unsigned short pkt_len_s16,
+	      unsigned char priority,unsigned short comref,unsigned data_len,
+	      void *data)
+{
+  /* encapsulate the vlink_put call in here, to avoid a memcpy */
+  fputc ('$', fptr);
+  fputc (1, fptr);
+  fputc ((pkt_len_s16+16)>>8, fptr);  fputc ((pkt_len_s16+16), fptr);
+
+  fputc (comspec>>8, fptr);  fputc (comspec, fptr);
+  fputc ((pkt_len_s16+16)>>8, fptr);  fputc ((pkt_len_s16+16), fptr);
+  fputc (priority, fptr);
+  fputc (0, fptr);
+  fputc (comref>>8, fptr);
+  fputc (comref, fptr);
+  fputc (data_len>>24, fptr);   fputc (data_len>>16, fptr);
+  fputc (data_len>>8, fptr);    fputc (data_len, fptr);
+  fputc (1, fptr);
+  fputc (4, fptr);
+  fputc (0, fptr);
+  fputc (0, fptr);
+
+  fwrite (data, pkt_len_s16, 1, fptr);
+}
+
+
+void ppa_init_job(ppa_stat* prn)
+{
+  unsigned char init1[8] = { 0x00, 0x00, 0x01, 0xf4, 0x01, 0x00, 0x00, 0x00 };
+  unsigned char init2[4] = { 0xde, 0xad, 0xbe, 0xef };
+  unsigned char init3[8] = { 0xde, 0xad, 0xbe, 0xef, 0x02, 0x00, 0x00, 0x00 };
+  unsigned char init4[60] = "!!TAZ            \x81*HP DeskJet 1000C Prin (Copy 2)*FILE!!\x00\x00\x00"; /* plus 0 terminator */
+  unsigned char init5[4] = { 0x01, 0x01, 0x00, 0x00 };
+
+  switch(prn->version)
+  {
+  case HP820:
+    scp_put (prn->fptr, 35, 1, 7, sizeof(init1), init1);
+    vlink_put (prn->fptr, 0, sizeof(init2), init2);
+    scp_put (prn->fptr, 101, 2, 7, sizeof(init3), init3);
+    break;
+  case HP720:
+    scp2_put (prn->fptr, 0x0186, sizeof(init1), 7, 1, 0, init1);
+    vlink_put(prn->fptr, 0, sizeof(init2), init2);
+    scp2_put (prn->fptr, 0x018f, sizeof(init3), 7, 2, 4, init3);
+    break;
+  case HP1000:
+    scp3_put (prn->fptr, 0x0186, sizeof(init1), 7, 16, 0, init1);
+    scp3_put (prn->fptr, 0x018C, sizeof(init4), 7, 1, 0, init4);
+    scp3_put (prn->fptr, 0x01A1, sizeof(init5), 7, 1, 0, init5);
+    vlink_put (prn->fptr, 0, sizeof(init2), init2);
+    scp3_put (prn->fptr, 0x018f, sizeof(init3), 7, 2, 4, init3);
+    break;
+  default:
+    fprintf(stderr,"ppa_init_job(): unknown printer verson\n");
+  }
+}
+
+void ppa_end_print(ppa_stat* prn)
+{
+  unsigned char pageA[4] = { 0x05, 0x01, 0x03, 0x84 };
+
+  if (prn->version == HP1000)
+    scp3_put (prn->fptr, 0x0181, sizeof(pageA), 7, 2, 0, pageA);
+}
+
+void ppa_init_page(ppa_stat* prn)
+{
+  unsigned char pageA[16] = {0x28, 0x2d, 0x00, 0x41, 0x29, 0x2e, 0x00, 0x42,
+			     0x29, 0x2e, 0x00, 0x42, 0x29, 0x2e, 0x00, 0x42 };
+  unsigned char pageB[16] = {0x28, 0x2d, 0x00, 0x41, 0x2d, 0x32, 0x00, 0x46,
+			     0x2d, 0x32, 0x00, 0x46, 0x2d, 0x32, 0x00, 0x46 };
+
+  switch(prn->version)
+  {
+  case HP820:
+    scp_put (prn->fptr, 21, 1, 5, sizeof(pageA), pageA);
+    break;
+  case HP720:
+    scp2_put (prn->fptr, 0x0183, sizeof(pageB), 5, 1, 0, pageB);
+    break;
+  case HP1000:
+    scp3_put (prn->fptr, 0x0183, sizeof(pageA), 5, 1, 0, pageA);
+    break;
+  default:
+    fprintf(stderr,"ppa_init_page(): unknown printer verson\n");
+  }
+}
+
+void ppa_load_page(ppa_stat* prn)
+{
+  unsigned char loadA[4] = {0x01, 0x01, 0x09, 0x60 };
+  unsigned char loadB[4] = {0x01, 0x01, 0x12, 0xc0 };
+  unsigned char loadC[4] = {0x01, 0x01, 0x07, 0x08 };
+
+  switch(prn->version)
+  {
+  case HP820:
+    scp_put (prn->fptr, 19, 1, 7, sizeof(loadA), loadA);
+    break;
+  case HP720:
+    scp2_put (prn->fptr, 0x0181, sizeof(loadB), 7, 1, 0, loadB);
+    break;
+  case HP1000:
+    scp3_put (prn->fptr, 0x0181, sizeof(loadC), 7, 1, 0, loadC);
+    break;
+  default:
+    fprintf(stderr,"ppa_load_page(): unknown printer verson\n");
+  }
+}
+
+void ppa_eject_page(ppa_stat* prn)
+{
+  unsigned char loadA[4] = {0x02, 0x01, 0x09, 0x60 };
+  unsigned char loadB[4] = {0x02, 0x01, 0x12, 0xc0 };
+  unsigned char loadC[4] = {0x02, 0x01, 0x07, 0x08 };
+
+  switch(prn->version)
+  {
+  case HP820:
+    scp_put (prn->fptr, 19, 1, 7, sizeof(loadA), loadA);
+    break;
+  case HP720:
+    scp2_put (prn->fptr, 0x0181, sizeof(loadB), 7, 1, 0, loadB);
+    break;
+  case HP1000:
+    scp3_put (prn->fptr, 0x0181, sizeof(loadC), 7, 1, 0, loadC);
+    break;
+  default:
+    fprintf(stderr,"ppa_eject_page(): unknown printer verson\n");
+  }
+}
+
+
+
+static int 
+compress(unsigned char *in, int num_lines_d2, int final_len, 
+             unsigned char *iout)
+{
+  unsigned char* out = iout;
+  int I,len=num_lines_d2;
+  for(I=0; I<final_len; I+=num_lines_d2, in+=num_lines_d2)
+  {
+    int i = 0;
+    while (i < len) {
+      /* Find the size of duplicate values */
+      int dup_len = 0;
+      while ((i + dup_len < len)
+	     && (in[i + dup_len] == in[i])) {
+	dup_len++;
+      }
+      /* See if we have enough zeros to be worth compressing. */
+      /* I figure one is enough. */
+      if ((dup_len >= 1) && (in[i] == 0)) {
+	/* Output run of zeros. */
+	while (dup_len >= 128) {
+	  /* Max is 128 */
+	  *out++ = 0x00;
+	  i += 128;
+	  dup_len -= 128;
+	}
+	if (dup_len >= 1)
+	{
+	  *out++ = dup_len;
+	  i += dup_len;
+	}
+	/* See if we have enough non-zeros to be worth compressing. */
+	/* Here two should be enough. */
+      }
+      else if (dup_len >= 2)
+      {
+	/* Output run of duplicates. */
+	while (dup_len >= 64) {
+	  /* Max is 64 */
+	  *out++ = 0x80;
+	  *out++ = in[i];
+	  i += 64;
+	  dup_len -= 64;
+	}
+	if (dup_len >= 2)
+	{
+	  *out++ = dup_len + 0x80;
+	  *out++ = in[i];
+	  i += dup_len;
+	}
+      }
+      else
+      {
+	/* Look for two zeros, or three duplicates to end literal run. */
+	/* Note this is one more than the number to start a run. */
+	int lit_len = -1;
+	int add_more = 1;
+	while (add_more) {
+	  lit_len++;
+	  if (i + lit_len == len) add_more = 0;
+	  /* Always add more if we are near the very end. */
+	  if (i + lit_len < len - 3) {
+	    char a = in[i + lit_len + 0];
+	    char b = in[i + lit_len + 1];
+	    char c = in[i + lit_len + 2];
+	    /* See if there are enough zeros */
+	    if ((a == b) && (b == 0)) add_more = 0;
+	    /* See if there are enough duplicates */
+	    if ((a == b) && (b == c)) add_more = 0;
+	  }
+	}
+	/* Output run of literals. */
+	while (lit_len >= 64) {
+	  /* Max is 64 */
+	  int j;
+	  *out++ = 0xc0;
+	  for (j = i; j < i + 64; j++) {
+	    *out++ = in[j];
+	  }
+	  i += 64;
+	  lit_len -= 64;
+	} 
+	if (lit_len) {
+	  int j;
+	  *out++ = lit_len + 0xc0;
+	  for (j = i; j < i + lit_len; j++) {
+	    *out++ = in[j];
+	  }
+	  i += lit_len;
+	}
+      }
+    }
+  }
+  return out-iout;
+}
+
+static void __inline__ place_2bytes(int x,unsigned char* y)
+{ y[0]=x>>8; y[1]=x; }
+static void __inline__ place_4bytes(int x,unsigned char* y)
+{ place_2bytes(x>>16,y); place_2bytes(x,y+2); }
+
+#define do_compress_data (1)
+void ppa_print_sweep(ppa_stat* prn,ppa_sweep_data* data)
+{
+  unsigned char* pc, *tpc;
+  unsigned i,datasize=data->data_size;
+  unsigned char sweep_packet[144];
+  int sweep_packet_size;
+  unsigned short HP720constants[] = { 0x8ca0, 0x4650, 0x12c0 };
+  unsigned short HP820constants[] = { 0x4650, 0x1c20, 0x0960 };
+  unsigned short HP1000constants[] = { 0x4650, 0x2328, 0x0708 };
+  unsigned short* constants;
+  int nozzle_data_size;
+  int MF; /* Multiplicative Factor -- quick hack */
+
+  pc=data->image_data;
+
+  if(do_compress_data)
+  {
+    if(!(pc=malloc((datasize/64+1)*65)))
+    {
+      fprintf(stderr,"ppa_print_sweep(): could not malloc storage for compressed data\n");
+      exit(-1);
+    }
+    datasize=compress(data->image_data,data->nozzle_data->pins_used_d2,datasize,pc);
+  }
+
+  /* send image data 16k at a time */
+  for(i=0, tpc=pc; i<datasize; tpc+=16384, i+=16384)
+    vlink_put(prn->fptr, 0, datasize-i > 16384 ? 16384 : datasize-i, tpc);
+
+  /* memory leak fix courtesy of John McKown */
+  if (do_compress_data)
+    free (pc);
+
+  /* construct sweep packet */
+  switch(prn->version)
+  {
+  case HP820:
+    constants=HP820constants;
+    MF=1;
+    break;
+  case HP720:
+    constants=HP720constants;
+    MF=2;
+    break;
+  case HP1000:
+    constants=HP1000constants;
+    MF=1;
+    break;
+  default:
+    fprintf(stderr,"ppa_print_sweep(): unknown printer verson\n");
+    return;
+  }
+
+  sweep_packet[0]=0;
+  sweep_packet[1]= do_compress_data;
+  sweep_packet[2]= data->direction==right_to_left ? 1 : 2;
+  sweep_packet[3]= data->in_color ? 14 : 1;
+
+  place_4bytes(datasize,sweep_packet+4);
+
+  memset(sweep_packet+8,0,8);
+
+  place_4bytes(MF*(data->vertical_pos+prn->y_offset),sweep_packet+16);
+  place_2bytes(constants[0],sweep_packet+20);
+  place_2bytes(MF*(data->left_margin+prn->x_offset),sweep_packet+22);
+  place_2bytes(MF*(data->right_margin+prn->x_offset),sweep_packet+24);
+  place_2bytes(constants[1],sweep_packet+26);
+  place_2bytes(constants[2],sweep_packet+28);
+  place_2bytes(0x100,sweep_packet+30);
+
+  if(data->next)
+  {
+    sweep_packet[32]= data->next->direction==right_to_left ? 1 : 2;
+    sweep_packet[33]= data->next->in_color ? 14 : 1;
+    place_4bytes(MF*(data->next->vertical_pos+prn->y_offset),sweep_packet+34);
+    place_2bytes(MF*(data->next->left_margin+prn->x_offset),sweep_packet+38);
+    place_2bytes(MF*(data->next->right_margin+prn->x_offset),sweep_packet+40);
+    place_2bytes(constants[1],sweep_packet+42);
+    place_2bytes(constants[2],sweep_packet+44);
+  }
+  else
+    memset(sweep_packet+32,0,14);
+  sweep_packet[46]=8;
+
+  nozzle_data_size=data->nozzle_data_size;
+  if(nozzle_data_size>6)
+  {
+    fprintf(stderr,"ppa_print_sweep(): truncating nozzle data from %d rows to 6 rows\n",nozzle_data_size);
+    nozzle_data_size=6;
+  }
+  sweep_packet[47]=nozzle_data_size;
+
+  for(i=0; i<nozzle_data_size; ++i)
+  {
+    unsigned char * const pc = sweep_packet + 48 + i*16;
+
+    place_2bytes(data->nozzle_data[i].DPI,pc);
+    place_2bytes(data->nozzle_data[i].pins_used_d2,pc+2);
+    place_2bytes(data->nozzle_data[i].unused_pins_p1,pc+4);
+    place_2bytes(data->nozzle_data[i].first_pin,pc+6);
+    place_2bytes(data->nozzle_data[i].pins_used_d2,pc+8);
+    place_2bytes(MF*(data->nozzle_data[i].left_margin+prn->x_offset),pc+10);
+    place_2bytes(MF*(data->nozzle_data[i].right_margin+prn->x_offset),pc+12);
+    pc[14]=data->nozzle_data[i].nozzle_delay;
+    pc[15]=0;
+  }
+
+  sweep_packet_size = data->in_color ? 144 : 80;
+
+  /* send sweep packet */
+  switch(prn->version)
+  {
+  case HP820:
+    scp_put (prn->fptr, 18, 1, 7, sweep_packet_size, sweep_packet);
+    break;
+  case HP720:
+    scp2_put (prn->fptr, 0x0180, sweep_packet_size, 7, 1, datasize, sweep_packet);
+    break;
+  case HP1000:
+    scp3_put (prn->fptr, 0x0180, sweep_packet_size, 7, 1, datasize, sweep_packet);
+    break;
+  default:
+    fprintf(stderr,"ppa_print_sweep(): unknown printer version\n");
+    return;
+  }
+}
+
+
+void ppa_print_sweeps(ppa_stat* prn,ppa_sweep_data* data)
+{
+  ppa_sweep_data* current_sweep;
+  for(current_sweep=data; current_sweep; current_sweep=current_sweep->next)
+    ppa_print_sweep(prn,current_sweep);
+}
diff --git a/converter/pbm/pbmtoppa/ppa.h b/converter/pbm/pbmtoppa/ppa.h
new file mode 100644
index 00000000..1c7e6f18
--- /dev/null
+++ b/converter/pbm/pbmtoppa/ppa.h
@@ -0,0 +1,74 @@
+/* ppa.h
+ * Copyright (c) 1998 Tim Norman.  See LICENSE for details.
+ * 2-25-98
+ *
+ * Mar 3, 1998  Jim Peterson  <jspeter@birch.ee.vt.edu>
+ *
+ *     Restructured to accommodate both the HP820/1000, and HP720 series.
+ */
+#ifndef _PPA_H
+#define _PPA_H
+#include <stdio.h>
+
+typedef struct
+{
+  FILE* fptr;
+  enum { HP820, HP1000, HP720, HP710 } version;
+  /* 300 , 600 dpi */
+  int DPI;
+  /* nozzle delay */
+  int right_to_left_delay[2];
+  int left_to_right_delay[2];
+  /* direction of printing */
+  enum { RIGHT_ONLY, LEFT_ONLY, BOTH_DIR } direction;
+  int x_offset;
+  int y_offset;
+  int marg_diff;
+  int top_margin;
+  int left_margin;
+  int right_margin;
+  int bottom_margin;
+  int bufsize;
+} ppa_stat;
+
+typedef struct
+{ 
+  int DPI;
+  int right;
+  int left;
+} printer_delay;
+
+typedef struct
+{
+  unsigned short DPI;
+  unsigned short pins_used_d2;
+  unsigned short unused_pins_p1;
+  unsigned short first_pin;
+  unsigned short left_margin;
+  unsigned short right_margin;
+  unsigned char nozzle_delay;
+} ppa_nozzle_data;
+
+typedef struct ppa_sweep_data_s
+{
+  unsigned char* image_data;
+  unsigned data_size;
+  enum { False=0, True=1 } in_color;
+  enum { right_to_left, left_to_right } direction;
+  int vertical_pos;
+  unsigned short left_margin;
+  unsigned short right_margin;
+  unsigned char nozzle_data_size;
+  ppa_nozzle_data* nozzle_data;
+  struct ppa_sweep_data_s* next; /* NULL indicates last print sweep */
+} ppa_sweep_data;
+
+void ppa_init_job(ppa_stat*);
+void ppa_init_page(ppa_stat*);
+void ppa_load_page(ppa_stat*);
+void ppa_eject_page(ppa_stat*);
+void ppa_end_print(ppa_stat*);
+void ppa_print_sweep(ppa_stat*,ppa_sweep_data*);  /* prints one sweep */
+void ppa_print_sweeps(ppa_stat*,ppa_sweep_data*); /* prints a linked-list of sweeps */
+
+#endif
diff --git a/converter/pbm/pbmtoppa/ppapbm.h b/converter/pbm/pbmtoppa/ppapbm.h
new file mode 100644
index 00000000..1ffc093b
--- /dev/null
+++ b/converter/pbm/pbmtoppa/ppapbm.h
@@ -0,0 +1,29 @@
+/* pbm.h
+ * Copyright (c) 1998 Tim Norman.  See LICENSE for details
+ * 2-25-98
+ *
+ * Mar 18, 1998  Jim Peterson  <jspeter@birch.ee.vt.edu>
+ *
+ *     Restructured to encapsulate more of the PBM handling.
+ */
+#ifndef _PBM_H
+#define _PBM_H
+
+#include <stdio.h>
+
+typedef struct
+{
+  FILE* fptr;
+  enum { none, P1, P4 } version;
+  int width, height;
+  int current_line;
+  void *revdata;
+  int unread;
+} pbm_stat;
+
+int make_pbm_stat(pbm_stat*,FILE*);
+int pbm_readline(pbm_stat*,unsigned char*); 
+  /* reads a single line into char* */
+void pbm_unreadline(pbm_stat*,void*); /* pushes a single line back */
+
+#endif
diff --git a/converter/pbm/pbmtopsg3.c b/converter/pbm/pbmtopsg3.c
new file mode 100644
index 00000000..68b265f0
--- /dev/null
+++ b/converter/pbm/pbmtopsg3.c
@@ -0,0 +1,374 @@
+/* pbmtopsg3
+
+   Reads a series of PBM images and writes a Postscript program
+   containing these images as individual pages with Fax-G3 
+   (CCITT-Fiter) compression. (Useful for combining scanned pages into
+   a comfortably printable document.)
+
+   Copyright (C) 2001 Kristof Koehler 
+       <kristof@fachschaft.physik.uni-karlsruhe.de>
+
+   Netpbm adaptation by Bryan Henderson June 2001.
+ 
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+ 
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "pbm.h"
+#include "shhopt.h"
+
+struct cmdline_info {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFilespec;  /* Filespec of input file */
+    float        dpi;            /* requested resolution, dpi */
+    char *       title;          /* -title option.  NULL for none */
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdline_info *cmdlineP) {
+
+    optStruct3 opt;
+    unsigned int option_def_index = 0;
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+
+    unsigned int dpiSpec, titleSpec;
+    float dpiOpt;
+    char * titleOpt;
+
+    OPTENT3(0, "dpi",      OPT_FLOAT,  &dpiOpt,   &dpiSpec,   0);
+    OPTENT3(0, "title",    OPT_STRING, &titleOpt, &titleSpec, 0);
+    
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;
+    opt.allowNegNum = FALSE;
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+
+    if (argc-1 == 0)
+        cmdlineP->inputFilespec = "-";
+    else {
+        cmdlineP->inputFilespec = argv[1];
+        if (argc-1 > 1)
+            pm_error("Too many arguments.  The only argument is the input "
+                     "file specification");
+    }
+
+    cmdlineP->dpi = dpiSpec ? dpiOpt : 72.0;
+    cmdlineP->title = titleSpec ? titleOpt : NULL;
+}
+
+    
+
+static void 
+write85 ( unsigned int bits, int *col )
+{
+	char buf[5] ;
+	if ( bits == 0 ) {
+		fputc ( 'z', stdout ) ;
+		*col += 1 ;
+	} else {
+		buf[4] = bits % 85 + '!' ;
+		bits /= 85 ;
+		buf[3] = bits % 85 + '!' ;
+		bits /= 85 ;
+		buf[2] = bits % 85 + '!' ;
+		bits /= 85 ;
+		buf[1] = bits % 85 + '!' ;
+		bits /= 85 ;
+		buf[0] = bits % 85 + '!' ;
+		fwrite ( buf, 1, 5, stdout ) ;
+		*col += 5 ;
+	}
+	if ( *col > 70 ) {
+		printf ( "\n" ) ;
+		*col = 0 ;
+	}
+}
+
+
+static void 
+writebits ( unsigned int *outbits, int *outbitsidx, int *col,
+            unsigned int bits, int n )
+{
+	int k, m ;
+	unsigned int usedbits ;
+	while ( n > 0 ) {
+		if ( *outbitsidx == 0 )
+			*outbits = 0 ;
+		k = 32 - *outbitsidx ;
+		m = n > k ? k : n ;
+		usedbits = (bits >> (n-m)) & ((1<<m)-1) ;
+		*outbits |= usedbits << (k-m) ;
+		*outbitsidx += m ;
+		n -= m ;
+		if ( *outbitsidx == 32 ) {
+			write85 ( *outbits, col ) ;
+			*outbitsidx = 0 ;
+		}
+	}
+}
+
+
+static void 
+flushbits ( unsigned int *outbits, int *outbitsidx, int *col )
+{
+	if ( *outbitsidx > 0 ) {
+		write85 ( *outbits, col ) ;
+		*outbitsidx = 0 ;
+	}
+}
+
+struct { unsigned int b, l ; } makeup[40][2] = {
+    { { 0x001b, 5 } /*         11011 */ , { 0x000f,10 } /*    0000001111 */  },
+    { { 0x0012, 5 } /*         10010 */ , { 0x00c8,12 } /*  000011001000 */  },
+    { { 0x0017, 6 } /*        010111 */ , { 0x00c9,12 } /*  000011001001 */  },
+    { { 0x0037, 7 } /*       0110111 */ , { 0x005b,12 } /*  000001011011 */  },
+    { { 0x0036, 8 } /*      00110110 */ , { 0x0033,12 } /*  000000110011 */  },
+    { { 0x0037, 8 } /*      00110111 */ , { 0x0034,12 } /*  000000110100 */  },
+    { { 0x0064, 8 } /*      01100100 */ , { 0x0035,12 } /*  000000110101 */  },
+    { { 0x0065, 8 } /*      01100101 */ , { 0x006c,13 } /* 0000001101100 */  },
+    { { 0x0068, 8 } /*      01101000 */ , { 0x006d,13 } /* 0000001101101 */  },
+    { { 0x0067, 8 } /*      01100111 */ , { 0x004a,13 } /* 0000001001010 */  },
+    { { 0x00cc, 9 } /*     011001100 */ , { 0x004b,13 } /* 0000001001011 */  },
+    { { 0x00cd, 9 } /*     011001101 */ , { 0x004c,13 } /* 0000001001100 */  },
+    { { 0x00d2, 9 } /*     011010010 */ , { 0x004d,13 } /* 0000001001101 */  },
+    { { 0x00d3, 9 } /*     011010011 */ , { 0x0072,13 } /* 0000001110010 */  },
+    { { 0x00d4, 9 } /*     011010100 */ , { 0x0073,13 } /* 0000001110011 */  },
+    { { 0x00d5, 9 } /*     011010101 */ , { 0x0074,13 } /* 0000001110100 */  },
+    { { 0x00d6, 9 } /*     011010110 */ , { 0x0075,13 } /* 0000001110101 */  },
+    { { 0x00d7, 9 } /*     011010111 */ , { 0x0076,13 } /* 0000001110110 */  },
+    { { 0x00d8, 9 } /*     011011000 */ , { 0x0077,13 } /* 0000001110111 */  },
+    { { 0x00d9, 9 } /*     011011001 */ , { 0x0052,13 } /* 0000001010010 */  },
+    { { 0x00da, 9 } /*     011011010 */ , { 0x0053,13 } /* 0000001010011 */  },
+    { { 0x00db, 9 } /*     011011011 */ , { 0x0054,13 } /* 0000001010100 */  },
+    { { 0x0098, 9 } /*     010011000 */ , { 0x0055,13 } /* 0000001010101 */  },
+    { { 0x0099, 9 } /*     010011001 */ , { 0x005a,13 } /* 0000001011010 */  },
+    { { 0x009a, 9 } /*     010011010 */ , { 0x005b,13 } /* 0000001011011 */  },
+    { { 0x0018, 6 } /*        011000 */ , { 0x0064,13 } /* 0000001100100 */  },
+    { { 0x009b, 9 } /*     010011011 */ , { 0x0065,13 } /* 0000001100101 */  },
+    { { 0x0008,11 } /*   00000001000 */ , { 0x0008,11 } /*   00000001000 */  },
+    { { 0x000c,11 } /*   00000001100 */ , { 0x000c,11 } /*   00000001100 */  },
+    { { 0x000d,11 } /*   00000001101 */ , { 0x000d,11 } /*   00000001101 */  },
+    { { 0x0012,12 } /*  000000010010 */ , { 0x0012,12 } /*  000000010010 */  },
+    { { 0x0013,12 } /*  000000010011 */ , { 0x0013,12 } /*  000000010011 */  },
+    { { 0x0014,12 } /*  000000010100 */ , { 0x0014,12 } /*  000000010100 */  },
+    { { 0x0015,12 } /*  000000010101 */ , { 0x0015,12 } /*  000000010101 */  },
+    { { 0x0016,12 } /*  000000010110 */ , { 0x0016,12 } /*  000000010110 */  },
+    { { 0x0017,12 } /*  000000010111 */ , { 0x0017,12 } /*  000000010111 */  },
+    { { 0x001c,12 } /*  000000011100 */ , { 0x001c,12 } /*  000000011100 */  },
+    { { 0x001d,12 } /*  000000011101 */ , { 0x001d,12 } /*  000000011101 */  },
+    { { 0x001e,12 } /*  000000011110 */ , { 0x001e,12 } /*  000000011110 */  },
+    { { 0x001f,12 } /*  000000011111 */ , { 0x001f,12 } /*  000000011111 */  }
+} ;
+
+struct { unsigned int b, l ; } term[64][2] = {
+    { { 0x0035, 8 } /*      00110101 */ , { 0x0037,10 } /*    0000110111 */  },
+    { { 0x0007, 6 } /*        000111 */ , { 0x0002, 3 } /*           010 */  },
+    { { 0x0007, 4 } /*          0111 */ , { 0x0003, 2 } /*            11 */  },
+    { { 0x0008, 4 } /*          1000 */ , { 0x0002, 2 } /*            10 */  },
+    { { 0x000b, 4 } /*          1011 */ , { 0x0003, 3 } /*           011 */  },
+    { { 0x000c, 4 } /*          1100 */ , { 0x0003, 4 } /*          0011 */  },
+    { { 0x000e, 4 } /*          1110 */ , { 0x0002, 4 } /*          0010 */  },
+    { { 0x000f, 4 } /*          1111 */ , { 0x0003, 5 } /*         00011 */  },
+    { { 0x0013, 5 } /*         10011 */ , { 0x0005, 6 } /*        000101 */  },
+    { { 0x0014, 5 } /*         10100 */ , { 0x0004, 6 } /*        000100 */  },
+    { { 0x0007, 5 } /*         00111 */ , { 0x0004, 7 } /*       0000100 */  },
+    { { 0x0008, 5 } /*         01000 */ , { 0x0005, 7 } /*       0000101 */  },
+    { { 0x0008, 6 } /*        001000 */ , { 0x0007, 7 } /*       0000111 */  },
+    { { 0x0003, 6 } /*        000011 */ , { 0x0004, 8 } /*      00000100 */  },
+    { { 0x0034, 6 } /*        110100 */ , { 0x0007, 8 } /*      00000111 */  },
+    { { 0x0035, 6 } /*        110101 */ , { 0x0018, 9 } /*     000011000 */  },
+    { { 0x002a, 6 } /*        101010 */ , { 0x0017,10 } /*    0000010111 */  },
+    { { 0x002b, 6 } /*        101011 */ , { 0x0018,10 } /*    0000011000 */  },
+    { { 0x0027, 7 } /*       0100111 */ , { 0x0008,10 } /*    0000001000 */  },
+    { { 0x000c, 7 } /*       0001100 */ , { 0x0067,11 } /*   00001100111 */  },
+    { { 0x0008, 7 } /*       0001000 */ , { 0x0068,11 } /*   00001101000 */  },
+    { { 0x0017, 7 } /*       0010111 */ , { 0x006c,11 } /*   00001101100 */  },
+    { { 0x0003, 7 } /*       0000011 */ , { 0x0037,11 } /*   00000110111 */  },
+    { { 0x0004, 7 } /*       0000100 */ , { 0x0028,11 } /*   00000101000 */  },
+    { { 0x0028, 7 } /*       0101000 */ , { 0x0017,11 } /*   00000010111 */  },
+    { { 0x002b, 7 } /*       0101011 */ , { 0x0018,11 } /*   00000011000 */  },
+    { { 0x0013, 7 } /*       0010011 */ , { 0x00ca,12 } /*  000011001010 */  },
+    { { 0x0024, 7 } /*       0100100 */ , { 0x00cb,12 } /*  000011001011 */  },
+    { { 0x0018, 7 } /*       0011000 */ , { 0x00cc,12 } /*  000011001100 */  },
+    { { 0x0002, 8 } /*      00000010 */ , { 0x00cd,12 } /*  000011001101 */  },
+    { { 0x0003, 8 } /*      00000011 */ , { 0x0068,12 } /*  000001101000 */  },
+    { { 0x001a, 8 } /*      00011010 */ , { 0x0069,12 } /*  000001101001 */  },
+    { { 0x001b, 8 } /*      00011011 */ , { 0x006a,12 } /*  000001101010 */  },
+    { { 0x0012, 8 } /*      00010010 */ , { 0x006b,12 } /*  000001101011 */  },
+    { { 0x0013, 8 } /*      00010011 */ , { 0x00d2,12 } /*  000011010010 */  },
+    { { 0x0014, 8 } /*      00010100 */ , { 0x00d3,12 } /*  000011010011 */  },
+    { { 0x0015, 8 } /*      00010101 */ , { 0x00d4,12 } /*  000011010100 */  },
+    { { 0x0016, 8 } /*      00010110 */ , { 0x00d5,12 } /*  000011010101 */  },
+    { { 0x0017, 8 } /*      00010111 */ , { 0x00d6,12 } /*  000011010110 */  },
+    { { 0x0028, 8 } /*      00101000 */ , { 0x00d7,12 } /*  000011010111 */  },
+    { { 0x0029, 8 } /*      00101001 */ , { 0x006c,12 } /*  000001101100 */  },
+    { { 0x002a, 8 } /*      00101010 */ , { 0x006d,12 } /*  000001101101 */  },
+    { { 0x002b, 8 } /*      00101011 */ , { 0x00da,12 } /*  000011011010 */  },
+    { { 0x002c, 8 } /*      00101100 */ , { 0x00db,12 } /*  000011011011 */  },
+    { { 0x002d, 8 } /*      00101101 */ , { 0x0054,12 } /*  000001010100 */  },
+    { { 0x0004, 8 } /*      00000100 */ , { 0x0055,12 } /*  000001010101 */  },
+    { { 0x0005, 8 } /*      00000101 */ , { 0x0056,12 } /*  000001010110 */  },
+    { { 0x000a, 8 } /*      00001010 */ , { 0x0057,12 } /*  000001010111 */  },
+    { { 0x000b, 8 } /*      00001011 */ , { 0x0064,12 } /*  000001100100 */  },
+    { { 0x0052, 8 } /*      01010010 */ , { 0x0065,12 } /*  000001100101 */  },
+    { { 0x0053, 8 } /*      01010011 */ , { 0x0052,12 } /*  000001010010 */  },
+    { { 0x0054, 8 } /*      01010100 */ , { 0x0053,12 } /*  000001010011 */  },
+    { { 0x0055, 8 } /*      01010101 */ , { 0x0024,12 } /*  000000100100 */  },
+    { { 0x0024, 8 } /*      00100100 */ , { 0x0037,12 } /*  000000110111 */  },
+    { { 0x0025, 8 } /*      00100101 */ , { 0x0038,12 } /*  000000111000 */  },
+    { { 0x0058, 8 } /*      01011000 */ , { 0x0027,12 } /*  000000100111 */  },
+    { { 0x0059, 8 } /*      01011001 */ , { 0x0028,12 } /*  000000101000 */  },
+    { { 0x005a, 8 } /*      01011010 */ , { 0x0058,12 } /*  000001011000 */  },
+    { { 0x005b, 8 } /*      01011011 */ , { 0x0059,12 } /*  000001011001 */  },
+    { { 0x004a, 8 } /*      01001010 */ , { 0x002b,12 } /*  000000101011 */  },
+    { { 0x004b, 8 } /*      01001011 */ , { 0x002c,12 } /*  000000101100 */  },
+    { { 0x0032, 8 } /*      00110010 */ , { 0x005a,12 } /*  000001011010 */  },
+    { { 0x0033, 8 } /*      00110011 */ , { 0x0066,12 } /*  000001100110 */  },
+    { { 0x0034, 8 } /*      00110100 */ , { 0x0067,12 } /*  000001100111 */  } 
+} ;
+
+
+static void
+writelength ( unsigned int *outbits, int *outbitsidx, int *col,
+              int bit, int length )
+{
+	while ( length >= 64 ) {
+		int m = length / 64 ;
+		if ( m > 40 )
+			m = 40 ;
+		writebits ( outbits, outbitsidx, col,
+			    makeup[m-1][bit].b, makeup[m-1][bit].l ) ;
+		length -= 64*m ;
+	}
+	writebits ( outbits, outbitsidx, col,
+		    term[length][bit].b, term[length][bit].l ) ;
+}
+
+
+
+static void
+doPage(FILE *       const ifP,
+       unsigned int const pageNum,
+       double       const dpi) {
+
+    int cols, rows, format;
+    bit * bitrow;
+    unsigned int row;
+    unsigned int outbits ;
+    int outbitsidx, col ;
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+        
+    bitrow = pbm_allocrow(cols);
+        
+    pm_message("[%u]\n", pageNum);
+
+    printf ("%%%%Page: %u %u\n", pageNum, pageNum);
+    printf ("%u %u 1 [ %f 0 0 %f 0 %u ]\n"
+            "{ currentfile /ASCII85Decode filter\n"
+            "  << /Columns %u /Rows %u /EndOfBlock false >> "
+                "/CCITTFaxDecode filter\n"
+            "  image } exec\n",
+            cols, rows, dpi/72.0, -dpi/72.0, rows, 
+            cols, rows) ;
+        
+    outbitsidx = col = 0 ;
+    for (row = 0 ; row < rows; ++row) {
+        int lastbit, cnt ;
+        unsigned int j;
+
+        pbm_readpbmrow(ifP, bitrow, cols, format);
+            
+        lastbit = cnt = 0 ;
+        for (j = 0; j < cols; ++j) {
+            if (bitrow[j] != lastbit) {
+                writelength(&outbits, &outbitsidx, &col, lastbit, cnt) ;
+                lastbit = 1 ^ lastbit ;
+                cnt = 0 ;
+            }
+            ++cnt;
+        }
+        writelength(&outbits, &outbitsidx, &col, lastbit, cnt);
+    }
+        
+    flushbits(&outbits, &outbitsidx, &col) ;
+    printf("~>\nshowpage\n") ;
+
+    pbm_freerow(bitrow);
+}
+
+
+
+static void 
+doPages(FILE *         const ifP,
+        unsigned int * const pagesP,
+        double         const dpi) {
+
+    bool eof;
+    unsigned int pagesDone;
+
+    eof = FALSE;
+    pagesDone = 0;
+
+    while (!eof) {
+        doPage(ifP, pagesDone + 1, dpi);
+        ++pagesDone;
+        pbm_nextimage(ifP, &eof);
+    }
+    *pagesP = pagesDone;
+}
+
+
+
+int
+main(int    argc,
+     char * argv[]) {
+
+    FILE *ifP;
+    unsigned int pages;
+    
+    struct cmdline_info cmdline;
+
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    printf ("%%!PS-Adobe-3.0\n");
+    if (cmdline.title)
+        printf("%%%%Title: %s\n", cmdline.title) ;
+    printf ("%%%%Creator: pbmtopsg3, Copyright (C) 2001 Kristof Koehler\n"
+            "%%%%Pages: (atend)\n"
+            "%%%%EndComments\n") ;
+    
+    doPages(ifP, &pages, cmdline.dpi);
+
+    printf ("%%%%Trailer\n"
+            "%%%%Pages: %u\n"
+            "%%%%EOF\n",
+            pages);
+
+    pm_close(ifP);
+    pm_close(stdout);
+    
+    return 0;
+}
diff --git a/converter/pbm/pbmtoptx.c b/converter/pbm/pbmtoptx.c
new file mode 100644
index 00000000..5031efcb
--- /dev/null
+++ b/converter/pbm/pbmtoptx.c
@@ -0,0 +1,101 @@
+/* pbmtoptx.c - read a portable bitmap and produce a Printronix printer file
+**
+** Copyright (C) 1988 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include "pbm.h"
+
+static void putinit ARGS(( void ));
+static void putbit ARGS(( bit b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+int argc;
+char *argv[];
+    {
+    FILE *ifp;
+    register bit *bitrow, *bP;
+    int rows, cols, format, row, col;
+    const char * const usage = "[pbmfile]";
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( usage );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    bitrow = pbm_allocrow( cols );
+
+    putinit( );
+    for ( row = 0; row < rows; row++ )
+	{
+	pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; col++, bP++ )
+	    putbit( *bP );
+	putrest( );
+	putchar( 5 );
+	putchar( '\n' );
+        }
+
+    pm_close( ifp );
+    
+    exit( 0 );
+    }
+
+static char item;
+static int bitsperitem, bitshift;
+
+static void
+putinit( )
+    {
+    bitsperitem = 0;
+    item = 64;
+    bitshift = 0;
+    }
+
+#if __STDC__
+static void
+putbit( bit b )
+#else /*__STDC__*/
+static void
+putbit( b )
+    bit b;
+#endif /*__STDC__*/
+    {
+    if ( bitsperitem == 6 )
+	putitem( );
+    if ( b == PBM_BLACK )
+	item += 1 << bitshift;
+    bitsperitem++;
+    bitshift++;
+    }
+
+static void
+putrest( )
+    {
+    if ( bitsperitem > 0 )
+	putitem( );
+    }
+
+static void
+putitem( )
+    {
+    putchar( item );
+    bitsperitem = 0;
+    item = 64;
+    bitshift = 0;
+    }
diff --git a/converter/pbm/pbmtowbmp.c b/converter/pbm/pbmtowbmp.c
new file mode 100644
index 00000000..2907fd68
--- /dev/null
+++ b/converter/pbm/pbmtowbmp.c
@@ -0,0 +1,70 @@
+/* pbmtowbmp.c - convert a portable bitmap to a Wireless Bitmap file
+
+   This is derived for Netpbm from the pbmwbmp package from 
+   <http://www.looplab.com/wap/tools> on 2000.06.06.
+   
+   The specifications for the wbmp format are part of the Wireless 
+   Application Environment specification at
+   <http://www.wapforum.org/what/technical.htm>.
+
+** Copyright (C) 1999 Terje Sannum <terje@looplab.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.
+*/
+
+#include "pbm.h"
+
+static void 
+outputint(int i) {
+  int c = 1;
+  while(i & 0x7f << 7*c) c++;
+  while(c > 1) putchar(0x80 | ((i >> 7*--c) & 0xff));
+  putchar(i & 0x7f);
+}
+
+
+
+int 
+main(int argc, char *argv[]) {
+  FILE *f;
+  bit **image;
+  int rows, cols, row, col;
+  int c, p;
+
+  pbm_init(&argc, argv);
+  if(argc > 2) {
+    fprintf(stderr, "Copyright (C) 1999 Terje Sannum <terje@looplab.com>\n");
+    pm_usage("[pbmfile]");
+  }
+
+  f = argc == 2 ? pm_openr(argv[1]) : stdin;
+  image = pbm_readpbm(f, &cols, &rows);
+  pm_close(f);
+
+  /* Header */
+  putchar(0); /* Type 0: B/W, no compression */
+  putchar(0); /* FixHeaderField */
+  /* Geometry */
+  outputint(cols);
+  outputint(rows);
+  /* Image data */
+  for(row = 0; row < rows; row++) {
+    p = c = 0;
+    for(col = 0; col < cols; col++) {
+      if(image[row][col] == PBM_WHITE) c = c | (1 << (7-p));
+      if(++p == 8) {
+	putchar(c);
+	p = c = 0;
+      }
+    }
+    if(p) putchar(c);
+  }
+  
+  return 0;
+}
+
diff --git a/converter/pbm/pbmtox10bm.c b/converter/pbm/pbmtox10bm.c
new file mode 100644
index 00000000..ef31fb9b
--- /dev/null
+++ b/converter/pbm/pbmtox10bm.c
@@ -0,0 +1,120 @@
+/* pbmtox10bm.c - read a portable bitmap and produce an X10 bitmap file
+**
+** Copyright (C) 1988 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <string.h>
+
+#include "nstring.h"
+#include "pbm.h"
+
+int
+main(int argc, char * argv[]) {
+
+    FILE* ifp;
+    bit* bitrow;
+    bit * bP;
+    int rows, cols, format, padright, row;
+    int col;
+    char name[100];
+    char* cp;
+    int itemsperline;
+    int bitsperitem;
+    int item;
+    int firstitem;
+    const char* const hexchar = "0123456789abcdef";
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+	{
+        ifp = pm_openr( argv[1] );
+        strcpy( name, argv[1] );
+        if (STREQ( name, "-" ))
+            strcpy( name, "noname" );
+
+        if ( ( cp = strchr( name, '.' ) ) != 0 )
+            *cp = '\0';
+	}
+    else
+	{
+        ifp = stdin;
+        strcpy( name, "noname" );
+	}
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    bitrow = pbm_allocrow( cols );
+
+    /* Compute padding to round cols up to the nearest multiple of 16. */
+    padright = ( ( cols + 15 ) / 16 ) * 16 - cols;
+
+    printf( "#define %s_width %d\n", name, cols );
+    printf( "#define %s_height %d\n", name, rows );
+    printf( "static short %s_bits[] = {\n", name );
+
+    itemsperline = 0;
+    bitsperitem = 0;
+    item = 0;
+    firstitem = 1;
+
+#define PUTITEM \
+    { \
+        if ( firstitem ) \
+	        firstitem = 0; \
+        else \
+	        putchar( ',' ); \
+    if ( itemsperline == 11 ) \
+	{ \
+	    putchar( '\n' ); \
+	    itemsperline = 0; \
+	} \
+    if ( itemsperline == 0 ) \
+	    putchar( ' ' ); \
+    ++itemsperline; \
+    putchar('0'); \
+    putchar('x'); \
+    putchar(hexchar[item >> 12]); \
+    putchar(hexchar[(item >> 8) & 15]); \
+    putchar(hexchar[(item >> 4) & 15]); \
+    putchar(hexchar[item & 15]); \
+    bitsperitem = 0; \
+    item = 0; \
+    }
+
+#define PUTBIT(b) \
+    { \
+    if ( bitsperitem == 16 ) \
+	    PUTITEM; \
+    if ( (b) == PBM_BLACK ) \
+	    item += 1 << bitsperitem; \
+    ++bitsperitem; \
+    }
+
+    for ( row = 0; row < rows; ++row )
+	{
+        pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+            PUTBIT(*bP);
+        for ( col = 0; col < padright; ++col )
+            PUTBIT(0);
+    }
+
+    pm_close( ifp );
+    
+    if ( bitsperitem > 0 )
+        PUTITEM;
+    printf( "};\n" );
+
+    return 0;
+}
diff --git a/converter/pbm/pbmtoxbm.c b/converter/pbm/pbmtoxbm.c
new file mode 100644
index 00000000..96830a0c
--- /dev/null
+++ b/converter/pbm/pbmtoxbm.c
@@ -0,0 +1,165 @@
+/* pbmtoxbm.c - read a portable bitmap and produce an X11 bitmap file
+**
+** Copyright (C) 1988 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <string.h>
+
+#include "pbm.h"
+#include "nstring.h"
+
+
+static void
+generateName(char const filenameArg[], const char ** const nameP) {
+/*----------------------------------------------------------------------------
+   Generate a name for the image to put in the bitmap file.  Derive it from
+   the filename argument filenameArg[] and return it as a null-terminated
+   string in newly malloc'ed space at *nameP.
+
+   We take the part of the name after the rightmost slash
+   (i.e. filename without the directory path part), stopping before
+   any period.  We convert any punctuation to underscores.
+
+   If the argument is "-", meaning standard input, we return the name
+   "noname".  Also, if the argument is null or ends in a slash, we
+   return "noname".
+-----------------------------------------------------------------------------*/
+    if (STREQ(filenameArg, "-"))
+        *nameP = strdup("noname");
+    else {
+        int nameIndex, argIndex;
+        /* indices into the input and output buffers */
+
+        /* Start just after the rightmost slash, or at beginning if no slash */
+        if (strrchr(filenameArg, '/') == 0) 
+            argIndex = 0;
+        else argIndex = strrchr(filenameArg, '/') - filenameArg + 1;
+
+        if (filenameArg[argIndex] == '\0') 
+            *nameP = strdup("noname");
+        else {
+            char * name;
+            nameIndex = 0;  /* Start at beginning of name buffer */
+
+            name = malloc(strlen(filenameArg));
+    
+            while (filenameArg[argIndex] != '\0' 
+                   && filenameArg[argIndex] != '.') {
+                const char filenameChar = filenameArg[argIndex++];
+                name[nameIndex++] = 
+                    ISALNUM(filenameChar) ? filenameChar : '_';
+            }
+            name[nameIndex] = '\0';
+            *nameP = name;
+        }
+    }
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+
+    FILE* ifp;
+    bit* bitrow;
+    int rows, cols, format;
+    int padright;
+    int row;
+    const char * inputFilename;
+    const char *name;
+    int itemsperline;
+    int bitsperitem;
+    int item;
+    int firstitem;
+    const char hexchar[] = "0123456789abcdef";
+
+    pbm_init(&argc, argv);
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments (%d).  The only valid argument is an "
+                 "input file name.", argc-1);
+    else if (argc-1 == 1) 
+        inputFilename = argv[1];
+    else
+        inputFilename = "-";
+
+    generateName(inputFilename, &name);
+    ifp = pm_openr(inputFilename);
+    
+    pbm_readpbminit(ifp, &cols, &rows, &format);
+    bitrow = pbm_allocrow(cols);
+    
+    /* Compute padding to round cols up to the nearest multiple of 8. */
+    padright = ((cols + 7)/8) * 8 - cols;
+
+    printf("#define %s_width %d\n", name, cols);
+    printf("#define %s_height %d\n", name, rows);
+    printf("static char %s_bits[] = {\n", name);
+
+    itemsperline = 0;
+    bitsperitem = 0;
+    item = 0;
+    firstitem = 1;
+
+#define PUTITEM \
+    { \
+    if ( firstitem ) \
+        firstitem = 0; \
+    else \
+        putchar( ',' ); \
+    if ( itemsperline == 15 ) \
+        { \
+        putchar( '\n' ); \
+        itemsperline = 0; \
+        } \
+    if ( itemsperline == 0 ) \
+        putchar( ' ' ); \
+    ++itemsperline; \
+    putchar('0'); \
+    putchar('x'); \
+    putchar(hexchar[item >> 4]); \
+    putchar(hexchar[item & 15]); \
+    bitsperitem = 0; \
+    item = 0; \
+    }
+
+#define PUTBIT(b) \
+    { \
+    if ( bitsperitem == 8 ) \
+        PUTITEM; \
+    if ( (b) == PBM_BLACK ) \
+        item += 1 << bitsperitem; \
+    ++bitsperitem; \
+    }
+
+    for (row = 0; row < rows; ++row) {
+        int col;
+        pbm_readpbmrow(ifp, bitrow, cols, format);
+        for (col = 0; col < cols; ++col)
+            PUTBIT(bitrow[col]);
+        for (col = 0; col < padright; ++col)
+            PUTBIT(0);
+    }
+
+    pm_close(ifp);
+
+    if (bitsperitem > 0)
+        PUTITEM;
+    printf("};\n");
+
+    pbm_freerow(bitrow);
+
+    strfree(name);
+
+    exit(0);
+}
diff --git a/converter/pbm/pbmtoybm.c b/converter/pbm/pbmtoybm.c
new file mode 100644
index 00000000..1d2be3d9
--- /dev/null
+++ b/converter/pbm/pbmtoybm.c
@@ -0,0 +1,114 @@
+/* pbmtoybm.c - read a pbm and write a file for Bennet Yee's 'xbm' and 'face'
+** programs.
+**
+** Written by Jamie Zawinski based on code (C) 1988 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <stdio.h>
+#include "pbm.h"
+
+#define YBM_MAGIC  ( ( '!' << 8 ) | '!' )
+
+static void putinit ARGS(( int cols, int rows ));
+static void putbit ARGS(( bit b ));
+static void putrest ARGS(( void ));
+static void putitem ARGS(( void ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, format, padright, row, col;
+
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[pbmfile]" );
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    bitrow = pbm_allocrow( cols );
+    
+    /* Compute padding to round cols up to the nearest multiple of 16. */
+    padright = ( ( cols + 15 ) / 16 ) * 16 - cols;
+
+    putinit( cols, rows );
+    for ( row = 0; row < rows; ++row )
+	{
+	pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    putbit( *bP );
+	for ( col = 0; col < padright; ++col )
+	    putbit( 0 );
+        }
+
+    if ( ifp != stdin )
+	fclose( ifp );
+
+    putrest( );
+
+    exit( 0 );
+    }
+
+static long item;
+static int bitsperitem, bitshift;
+
+static void
+putinit( cols, rows )
+    int cols, rows;
+    {
+    pm_writebigshort( stdout, YBM_MAGIC );
+    pm_writebigshort( stdout, cols );
+    pm_writebigshort( stdout, rows );
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 0;
+    }
+
+#if __STDC__
+static void
+putbit( bit b )
+#else /*__STDC__*/
+static void
+putbit( b )
+    bit b;
+#endif /*__STDC__*/
+    {
+    if ( bitsperitem == 16 )
+	putitem( );
+    ++bitsperitem;
+    if ( b == PBM_BLACK )
+	item += 1 << bitshift;
+    ++bitshift;
+    }
+
+static void
+putrest( )
+    {
+    if ( bitsperitem > 0 )
+	putitem( );
+    }
+
+static void
+putitem( )
+    {
+    pm_writebigshort( stdout, item );
+    item = 0;
+    bitsperitem = 0;
+    bitshift = 0;
+    }
diff --git a/converter/pbm/pbmtozinc.c b/converter/pbm/pbmtozinc.c
new file mode 100644
index 00000000..d39b71bc
--- /dev/null
+++ b/converter/pbm/pbmtozinc.c
@@ -0,0 +1,128 @@
+/* pbmtozinc.c - read a portable bitmap and produce an bitmap file
+**               in the format used by the Zinc Interface Library (v1.0)
+**               November 1990.
+**
+** Author: James Darrell McCauley
+**         Department of Agricultural Engineering
+**         Texas A&M University
+**         College Station, Texas 77843-2117 USA
+**
+** Copyright (C) 1988 by James Darrell McCauley (jdm5548@diamond.tamu.edu)
+**                    and Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "nstring.h"
+#include "pbm.h"
+
+int
+main(int argc, char * argv[]) {
+
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, format, padright, row;
+    register int col;
+    char name[100];
+    char* cp;
+    int itemsperline;
+    register int bitsperitem;
+    register int item;
+    int firstitem;
+    const char * const hexchar = "084c2a6e195d3b7f";
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+	{
+        ifp = pm_openr( argv[1] );
+        strcpy( name, argv[1] );
+        if ( STREQ( name, "-" ) )
+            strcpy( name, "noname" );
+
+        if ( ( cp = strchr( name, '.' ) ) != 0 )
+            *cp = '\0';
+	}
+    else
+	{
+        ifp = stdin;
+        strcpy( name, "noname" );
+	}
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+    bitrow = pbm_allocrow( cols );
+
+    /* Compute padding to round cols up to the nearest multiple of 16. */
+    padright = ( ( cols + 15 ) / 16 ) * 16 - cols;
+
+    printf( "USHORT %s[] = {\n",name);
+    printf( "  %d\n", cols );
+    printf( "  %d\n", rows );
+
+    itemsperline = 0;
+    bitsperitem = 0;
+    item = 0;
+    firstitem = 1;
+
+#define PUTITEM \
+    { \
+    if ( firstitem ) \
+	firstitem = 0; \
+    else \
+	putchar( ',' ); \
+    if ( itemsperline == 11 ) \
+	{ \
+	putchar( '\n' ); \
+	itemsperline = 0; \
+	} \
+    if ( itemsperline == 0 ) \
+	putchar( ' ' ); \
+    ++itemsperline; \
+    putchar('0'); \
+    putchar('x'); \
+    putchar(hexchar[item & 15]); \
+    putchar(hexchar[(item >> 4) & 15]); \
+    putchar(hexchar[(item >> 8) & 15]); \
+    putchar(hexchar[item >> 12]); \
+    bitsperitem = 0; \
+    item = 0; \
+    }
+
+#define PUTBIT(b) \
+    { \
+    if ( bitsperitem == 16 ) \
+	PUTITEM; \
+    if ( (b) == PBM_BLACK ) \
+	item += 1 << bitsperitem; \
+    ++bitsperitem; \
+    }
+
+    for ( row = 0; row < rows; ++row )
+	{
+        pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+            PUTBIT( *bP );
+        for ( col = 0; col < padright; ++col )
+            PUTBIT( 0 );
+    }
+
+    pm_close( ifp );
+    
+    if ( bitsperitem > 0 )
+        PUTITEM;
+    printf( "};\n" );
+
+    return 0;
+}
diff --git a/converter/pbm/pi3topbm.c b/converter/pbm/pi3topbm.c
new file mode 100644
index 00000000..8b3b21e3
--- /dev/null
+++ b/converter/pbm/pi3topbm.c
@@ -0,0 +1,112 @@
+/*
+ * Convert a ATARI Degas .pi3 file to a portable bitmap file.
+ *
+ * Author: David Beckemeyer
+ *
+ * This code was derived from the original gemtopbm program written
+ * by Diomidis D. Spinellis.
+ *
+ * (C) Copyright 1988 David Beckemeyer and Diomidis D. Spinellis.
+ *
+ * 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 file is provided AS IS with no warranties of any kind.  The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof.  In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ */
+
+#include <stdio.h>
+#include "pbm.h"
+
+int
+main(argc, argv)
+	int             argc;
+	char           *argv[];
+{
+	int             debug = 0;
+	FILE           *f;
+	int             x;
+	int             i, k;
+	int             c;
+	int		rows, cols;
+	bit		*bitrow;
+	short res;
+	int black, white;
+	const char * const usage = "[-debug] [pi3file]";
+	int argn = 1;
+
+	pbm_init( &argc, argv );
+
+	while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0')
+	  {
+	    if (pm_keymatch(argv[1], "-debug", 2))
+	      debug = 1;
+	    else
+	      pm_usage (usage);
+	    ++argn;
+	  }
+
+	if (argn == argc)
+	    f = stdin;
+	else
+	  {
+	    f = pm_openr (argv[argn]);
+	    ++argn;
+	  }
+
+	if (argn != argc)
+	  pm_usage (usage);
+
+	if (pm_readbigshort (f, &res) == -1)
+		pm_error ("EOF / read error");
+
+	if (debug)
+		pm_message ("resolution is %d", res);
+
+	/* only handles hi-rez 640x400 */
+	if (res != 2)
+		pm_error( "bad resolution" );
+
+	pm_readbigshort (f, &res);
+	if (res == 0)
+	  {
+	    black = PBM_WHITE;
+	    white = PBM_BLACK;
+	  }
+	else
+	  {
+	    black = PBM_BLACK;
+	    white = PBM_WHITE;
+	  }
+
+	for (i = 1; i < 16; i++)
+	  if (pm_readbigshort (f, &res) == -1)
+	    pm_error ("EOF / read error");
+
+	cols = 640;
+	rows = 400;
+	pbm_writepbminit( stdout, cols, rows, 0 );
+	bitrow = pbm_allocrow( cols );
+
+	for (i = 0; i < rows; ++i) {
+		x = 0;
+		while (x < cols) {
+			if ((c = getc(f)) == EOF)
+				pm_error( "end of file reached" );
+			for (k = 0x80; k; k >>= 1) {
+				bitrow[x] = (k & c) ? black : white;
+				++x;
+			}
+		}
+		pbm_writepbmrow( stdout, bitrow, cols, 0 );
+	}
+	pm_close( f );
+	pm_close( stdout );
+	exit(0);
+}
diff --git a/converter/pbm/pktopbm.c b/converter/pbm/pktopbm.c
new file mode 100644
index 00000000..32ed4fde
--- /dev/null
+++ b/converter/pbm/pktopbm.c
@@ -0,0 +1,442 @@
+/*
+  pktopbm, adapted from "pktopx in C by Tomas Rokicki" by AJCD 1/8/90
+  1998-09-22: jcn <janneke@gnu.org>
+     - lots of bugfixes:
+     * always read x/y offset bytes (3x)
+     * reset bmx, bmy to defaults for each char
+     * fix bitmap y placement of dynamically packed char
+     * skip char early if no output file allocated
+     - added debug output
+  
+  compile with: cc -lpbm -o pktopbm pktopbm.c
+  */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "nstring.h"
+#include "pbm.h"
+
+#define NAMELENGTH 80
+#define MAXROWWIDTH 3200
+#define MAXPKCHAR 256
+
+typedef int integer ;
+typedef unsigned char quarterword ;
+typedef char boolean ;
+typedef quarterword eightbits ;
+
+static FILE *pkfile ;
+static char pkname[NAMELENGTH+1] ;
+static integer pktopbm_pkloc = 0;
+static char *filename[MAXPKCHAR] ;
+static bit **bitmap = NULL ;
+static integer dynf ;
+static eightbits inputbyte ;
+static eightbits bitweight ;
+static integer repeatcount ;
+static integer flagbyte ;
+static integer debug=0;
+
+#define dprintf(s,d) if (debug) printf(s,d)
+#define dprintf0(s) if (debug) printf(s)
+
+/* add a suffix to a filename in an allocated space */
+static void
+pktopbm_add_suffix(char *       const name, 
+                   const char * const suffix) {
+
+    char * const slash = strrchr(name, '/');
+    char * const dot   = strrchr(name, '.');
+    
+    if ((dot && slash ? dot < slash : !dot) && !STREQ(name, "-"))
+        strcat(name, suffix);
+}
+
+
+
+/* get a byte from the PK file */
+static eightbits 
+pktopbm_pkbyte(void) {
+   pktopbm_pkloc++ ;
+   return(getc(pkfile)) ;
+}
+
+
+
+/* get a 16-bit half word from the PK file */
+static integer 
+get16(void) {
+   integer const a = pktopbm_pkbyte() ;
+   return((a<<8) + pktopbm_pkbyte()) ;
+}
+
+
+
+/* get a 32-bit word from the PK file */
+static integer get32(void) {
+    integer a;
+    a = get16() ;
+    if (a > 32767) a -= 65536 ;
+    return((a<<16) + get16()) ;
+}
+
+
+
+/* get a nibble from current input byte, or new byte if no current byte */
+static integer 
+getnyb(void) {
+    eightbits temp;
+    if (bitweight == 0) {
+        inputbyte = pktopbm_pkbyte() ;
+        bitweight = 16 ;
+    }
+    temp = inputbyte / bitweight ;
+    inputbyte -= temp * bitweight ;
+    bitweight >>= 4 ;
+    return(temp) ;
+}
+
+
+
+/* get a bit from the current input byte, or a new byte if no current byte */
+static bool
+getbit(void) {
+    bool temp ;
+    bitweight >>= 1 ;
+    if (bitweight == 0) {
+        inputbyte = pktopbm_pkbyte() ;
+        bitweight = 128 ;
+    }
+    temp = (inputbyte >= bitweight) ;
+    if (temp) inputbyte -= bitweight ;
+    return(temp) ;
+}
+
+
+
+/* unpack a dynamically packed number. dynf is dynamic packing threshold  */
+static integer 
+pkpackednum(void) {
+    integer i, j ;
+    i = getnyb() ;
+    if (i == 0) {           /* large run count, >= 3 nibbles */
+        do {
+            j = getnyb() ;          /* count extra nibbles */
+            i++ ;
+        } while (j == 0) ;
+        while (i > 0) {
+            j = (j<<4) + getnyb() ; /* add extra nibbles */
+            i-- ;
+        }
+        return (j - 15 +((13 - dynf)<<4) + dynf) ;
+    } else if (i <= dynf) return (i) ;  /* number > 0 and <= dynf */
+    else if (i < 14) return (((i - dynf - 1)<<4) + getnyb() + dynf + 1) ;
+    else {
+        if (i == 14) repeatcount = pkpackednum() ;  /* get repeat count */
+        else repeatcount = 1 ;      /* value 15 indicates repeat count 1 */
+        return(pkpackednum()) ;
+    }
+}
+
+
+
+/* skip specials in PK files, inserted by Metafont or some other program */
+static void
+skipspecials(void) {
+    integer i, j;
+    do {
+        flagbyte = pktopbm_pkbyte() ;
+        if (flagbyte >= 240)
+            switch(flagbyte) {
+            case 240:           /* specials of size 1-4 bytes */
+            case 241:
+            case 242:
+            case 243:
+                i = 0 ;
+                for (j = 240 ; j <= flagbyte ; ++j) 
+                    i = (i<<8) + pktopbm_pkbyte() ;
+                for (j = 1 ; j <= i ; ++j) 
+                    pktopbm_pkbyte() ;  /* ignore special */
+                break ;
+            case 244:           /* no-op, parameters to specials */
+                get32() ;
+            case 245:           /* start of postamble */
+            case 246:           /* no-op */
+                break ;
+            case 247:           /* pre-amble in wrong place */
+            case 248:
+            case 249:
+            case 250:
+            case 251:
+            case 252:
+            case 253:
+            case 254:
+            case 255:
+                pm_error("unexpected flag byte %d", flagbyte) ;
+            }
+    } while (!(flagbyte < 240 || flagbyte == 245)) ;
+}
+
+
+
+/* ignore character packet */
+static void
+ignorechar(integer const car, 
+           integer const endofpacket) {
+
+   while (pktopbm_pkloc != endofpacket) pktopbm_pkbyte() ;
+   if (car < 0 || car >= MAXPKCHAR)
+      pm_message("Character %d out of range", car) ;
+   skipspecials() ;
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+    integer x;
+    integer endofpacket ;
+    boolean turnon ;
+    integer i, j;
+    integer car ;
+    integer bmx=0, bmy=0;
+    integer set_bmx=0, set_bmy=0;
+    bit row[MAXROWWIDTH+1] ;
+    const char * const usage = 
+        "pkfile[.pk] [-d] [[-x width] [-y height] [-c num] pbmfile]...";
+   
+    pbm_init(&argc, argv);
+    for (i = 0 ; i < MAXPKCHAR ; i ++) filename[i] = NULL ;
+
+    pm_message("This is PKtoPBM, version 2.5") ;
+
+    if (--argc < 1) pm_usage(usage) ;
+
+    strcpy(pkname, *++argv) ;
+    pktopbm_add_suffix(pkname, ".pk") ;
+
+    car = 0 ;
+    /* urg: use getopt */
+    while (++argv, --argc) {
+        if (argv[0][0] == '-' && argv[0][1])
+            switch (argv[0][1]) {
+            case 'X':
+            case 'x':
+                if (argv[0][2]) bmx = atoi(*argv+2) ;
+                else if (++argv, --argc) set_bmx = atoi(*argv) ;
+                else pm_usage(usage) ;
+                continue ;
+            case 'Y':
+            case 'y':
+                if (argv[0][2]) bmy = atoi(*argv+2) ;
+                else if (++argv, --argc) set_bmy = atoi(*argv) ;
+                else pm_usage(usage) ;
+                continue ;
+            case 'C':
+            case 'c':
+                if (argv[0][2]) car = atoi(*argv+2) ;
+                else if (++argv, --argc) car = atoi(*argv) ;
+                else pm_usage(usage) ;
+                break ;
+            case 'd':
+                debug=1;
+                break ;
+            default:
+                pm_usage(usage) ;
+            } else if (car < 0 || car >= MAXPKCHAR) {
+                pm_error("character must be in range 0 to %d (-c)", 
+                         MAXPKCHAR-1) ;
+            } else filename[car++] = *argv ;
+    }
+
+    pkfile = pm_openr(pkname);
+    if (pktopbm_pkbyte() != 247)
+        pm_error("bad PK file (pre command missing)") ;
+    if (pktopbm_pkbyte() != 89)
+        pm_error("wrong version of packed file") ;
+    j = pktopbm_pkbyte() ;              /* get header comment size */
+    for (i = 1 ; i <= j ; i ++) pktopbm_pkbyte() ;  /* ignore header comment */
+    get32() ;                   /* ignore designsize */
+    get32() ;                   /* ignore checksum */
+    if (get32() != get32())         /* h & v pixels per point */
+        pm_message("Warning: aspect ratio not 1:1") ;
+    skipspecials() ;
+    while (flagbyte != 245) {           /* not at postamble */
+        integer cheight, cwidth ;
+        integer xoffs=0, yoffs=0;
+        FILE *ofp;
+
+        bmx=set_bmx;
+        bmy=set_bmy;
+        dynf = (flagbyte>>4) ;          /* get dynamic packing value */
+        flagbyte &= 15 ;
+        turnon = (flagbyte >= 8) ;      /* black or white initially? */
+        if (turnon) flagbyte &= 7 ;     /* long or short form */
+        if (flagbyte == 7) {            /* long form preamble */
+            integer packetlength = get32() ;    /* character packet length */
+            car = get32() ;         /* character number */
+            endofpacket = packetlength + pktopbm_pkloc;
+                /* calculate end of packet */
+            if ((car >= MAXPKCHAR) || !filename[car]) {
+                ignorechar(car, endofpacket);
+                continue;
+            }
+            dprintf0 ("flagbyte7\n");
+            dprintf ("car: %d\n", car);
+            get32() ;               /* ignore tfmwidth */
+            x=get32() ;             /* ignore horiz escapement */
+            x=get32() ;             /* ignore vert escapement */
+            dprintf ("horiz esc %d\n", x);
+            dprintf ("vert esc %d\n", x);
+            cwidth = get32() ;          /* bounding box width */
+            cheight = get32() ;         /* bounding box height */
+            dprintf ("cwidth %d\n", cwidth);
+            dprintf ("cheight %d\n", cheight);
+            if (cwidth < 0 || cheight < 0 || 
+                cwidth > 65535 || cheight > 65535) {
+                ignorechar(car, endofpacket);
+                continue;
+            }
+            xoffs= get32() ;              /* horiz offset */
+            yoffs= get32() ;              /* vert offset */
+            dprintf ("xoffs %d\n", xoffs);
+            dprintf ("yoffs %d\n", yoffs);
+        } else if (flagbyte > 3) {      /* extended short form */
+            integer packetlength = ((flagbyte - 4)<<16) + get16() ;
+            /* packet length */
+            car = pktopbm_pkbyte() ;            /* char number */
+            endofpacket = packetlength + pktopbm_pkloc ; 
+                /* calculate end of packet */
+            if ((car >= MAXPKCHAR) || !filename[car]) {
+                ignorechar(car, endofpacket);
+                continue;
+            }
+            dprintf0 ("flagbyte>3\n");
+            dprintf ("car: %d\n", car);
+            pktopbm_pkbyte() ;              /* ignore tfmwidth (3 bytes) */
+            get16() ;               /* ignore tfmwidth (3 bytes) */
+            get16() ;               /* ignore horiz escapement */
+            cwidth = get16() ;          /* bounding box width */
+            cheight = get16() ;         /* bounding box height */
+            dprintf ("cwidth %d\n", cwidth);
+            dprintf ("cheight %d\n", cheight);
+            xoffs=get16();                         /* horiz offset */
+            if (xoffs >= 32768)
+                xoffs-= 65536;
+            yoffs=get16();                         /* vert offset */
+            if (yoffs >= 32768)
+                yoffs-= 65536;
+            dprintf ("xoffs %d\n", xoffs);
+            dprintf ("yoffs %d\n", yoffs);
+        } else {                    /* short form preamble */
+            integer packetlength = (flagbyte<<8) + pktopbm_pkbyte() ;
+            /* packet length */
+            car = pktopbm_pkbyte() ;            /* char number */
+            endofpacket = packetlength + pktopbm_pkloc ;    
+                /* calculate end of packet */
+            if ((car >= MAXPKCHAR) || !filename[car]) {
+                ignorechar(car, endofpacket);
+                continue;
+            }
+            dprintf0 ("flagbyte<=3\n");
+            dprintf ("car: %d\n", car);
+            pktopbm_pkbyte() ;          /* ignore tfmwidth (3 bytes) */
+            get16() ;               /* ignore tfmwidth (3 bytes) */
+            x = pktopbm_pkbyte() ;  /* ignore horiz escapement */
+            dprintf ("horiz esc %d\n", x);
+            cwidth = pktopbm_pkbyte() ;            /* bounding box width */
+            cheight = pktopbm_pkbyte() ;           /* bounding box height */
+            dprintf ("cwidth %d\n", cwidth);
+            dprintf ("cheight %d\n", cheight);
+            xoffs=pktopbm_pkbyte ();               /* horiz offset */
+            if (xoffs >= 128)
+                xoffs-=256;
+            yoffs=pktopbm_pkbyte ();               /* vert offset */
+            if (yoffs >= 128)
+                yoffs-=256;
+            dprintf ("xoffs %d\n", xoffs);
+            dprintf ("yoffs %d\n", yoffs);
+        }
+        if (filename[car]) {
+            if (!bmx) bmx= cwidth;
+            if (!bmy) bmy= cheight;
+            bitmap = pbm_allocarray(bmx, bmy) ;
+            if (bitmap == NULL)
+                pm_error("out of memory allocating bitmap") ;
+        } else {
+            ignorechar(car, endofpacket);
+            continue;
+        }
+        bitweight = 0 ;
+        for (i = 0 ; i < bmy ; i ++)           /* make it blank */
+            for (j = 0 ; j < bmx ; j ++)
+                bitmap[i][j]= PBM_WHITE;
+        if (dynf == 14) {               /* bitmapped character */
+            dprintf ("bmy: %d\n ", bmy);
+            dprintf ("y: %d\n ", bmy-yoffs-1);
+            for (i = 0 ; i < cheight ; i ++) {
+                int yi= i+(bmy-yoffs-1);
+                for (j = 0 ; j < cwidth ; j ++) {
+                    int xj= j-xoffs;
+                    if (getbit() && 0<=xj && xj<bmx && 0<=yi && yi<bmy)
+                        bitmap[yi][xj] = PBM_BLACK ;
+                }
+            }
+        } else {                    /* dynamically packed char */
+            integer rowsleft = cheight ;
+            integer hbit = cwidth ;
+            integer rp = 0;
+            repeatcount = 0 ;
+            dprintf ("bmy: %d\n ", bmy);
+            dprintf ("y: %d\n", cheight-rowsleft+(bmy-2*yoffs-1));
+            while (rowsleft > 0) {
+                integer count = pkpackednum() ; /* get current color count */
+                while (count > 0) {
+                    if (count < hbit) {     /* doesn't extend past row */
+                        hbit -= count ;
+                        while (count--)
+                            row[rp++] = turnon ? PBM_BLACK : PBM_WHITE;
+                    } else {                /* reaches end of row */
+                        count -= hbit ;
+                        while (hbit--)
+                            row[rp++] = turnon ? PBM_BLACK : PBM_WHITE;
+                        for (i = 0; i <= repeatcount; i++) {  /* fill row */
+                            int yi= i+cheight-rowsleft-1;
+                            if (0<=yi && yi < bmy)
+                                for (j = 0; j < cwidth; j++) {
+                                    int xj= j-xoffs;
+                                    if (0<=xj && xj<bmx)
+                                        bitmap[yi][xj] = row[j] ;
+                                }
+                        }
+                        rowsleft -= repeatcount + 1;
+                        repeatcount = rp = 0 ;
+                        hbit = cwidth ;
+                    }
+                }
+                turnon = !turnon ;
+            }
+            if (rowsleft != 0 || hbit != cwidth)
+                pm_error("bad pk file (more bits than required)") ;
+        }
+        if (endofpacket != pktopbm_pkloc)
+            pm_error("bad pk file (bad packet length)") ;
+
+        ofp = pm_openw(filename[car]);
+        filename[car] = NULL;
+        pbm_writepbm(ofp, bitmap, bmx, bmy, 0) ;
+        pbm_freearray(bitmap, bmy) ;
+        pm_close(ofp) ;
+        skipspecials() ;
+    }
+    while (! feof(pkfile)) pktopbm_pkbyte() ;       /* skip trailing junk */
+    pm_close(pkfile);
+    for (car = 0; car < MAXPKCHAR; car++)
+        if (filename[car])
+            pm_message("Warning: No character in position %d (file %s).",
+                       car, filename[car]) ;
+    pm_message("%d bytes read from packed file.", pktopbm_pkloc-1) ;
+    return 0;
+}
diff --git a/converter/pbm/thinkjettopbm.c b/converter/pbm/thinkjettopbm.c
new file mode 100644
index 00000000..2f5f6fda
--- /dev/null
+++ b/converter/pbm/thinkjettopbm.c
@@ -0,0 +1,1792 @@
+/* This is the file 'lexheader'.  It contains extra stuff needed by
+   parsers generated by lex.
+
+   GNU Flex generates a parser that refers to the non-ansi C library 
+   subroutine fileno().  In order to let it compile without warnings,
+   we define _XOPEN_SOURCE to declare that fact.
+*/
+#define _XOPEN_SOURCE
+/* END OF lexheader */
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ * $Header: /home/daffy/u0/vern/flex/RCS/flex.skl,v 2.91 96/09/10 16:58:48 vern Exp $
+ */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+
+#include <stdio.h>
+
+
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+
+
+#ifdef __cplusplus
+
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Use prototypes in function declarations. */
+#define YY_USE_PROTOS
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif	/* __STDC__ */
+#endif	/* ! __cplusplus */
+
+#ifdef __TURBOC__
+ #pragma warn -rch
+ #pragma warn -use
+#include <io.h>
+#include <stdlib.h>
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#endif
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#define YY_BUF_SIZE 16384
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern int yyleng;
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* The funky do-while in the following #define is used to turn the definition
+ * int a single C statement (which needs a semi-colon terminator).  This
+ * avoids problems with code like:
+ *
+ * 	if ( condition_holds )
+ *		yyless( 5 );
+ *	else
+ *		do_something_else();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the yyless() call.
+ */
+
+/* Return all but the first 'n' matched characters back to the input stream. */
+
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+		*yy_cp = yy_hold_char; \
+		YY_RESTORE_YY_MORE_OFFSET \
+		yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, yytext_ptr )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+typedef unsigned int yy_size_t;
+
+
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+	};
+
+static YY_BUFFER_STATE yy_current_buffer = 0;
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+
+static int yy_n_chars;		/* number of characters read into yy_ch_buf */
+
+
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1;		/* whether we need to initialize */
+static int yy_start = 0;	/* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart YY_PROTO(( FILE *input_file ));
+
+void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+void yy_load_buffer_state YY_PROTO(( void ));
+YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
+void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
+void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer )
+
+YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size ));
+YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str ));
+YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len ));
+
+static void *yy_flex_alloc YY_PROTO(( yy_size_t ));
+static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! yy_current_buffer ) \
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	yy_current_buffer->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! yy_current_buffer ) \
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	yy_current_buffer->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)
+
+typedef unsigned char YY_CHAR;
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+typedef int yy_state_type;
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yy_fatal_error YY_PROTO(( yyconst char msg[] ));
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	yytext_ptr = yy_bp; \
+	yyleng = (int) (yy_cp - yy_bp); \
+	yy_hold_char = *yy_cp; \
+	*yy_cp = '\0'; \
+	yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 10
+#define YY_END_OF_BUFFER 11
+static yyconst short int yy_accept[34] =
+    {   0,
+        0,    0,    0,    0,    0,    0,   11,    9,    9,   10,
+        4,   10,    1,    0,    0,    0,    0,    0,    0,    0,
+        0,    0,    0,    0,    8,    0,    3,    5,    5,    7,
+        6,    2,    0
+    } ;
+
+static yyconst int yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    2,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    3,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    4,    1,    1,
+        1,    5,    1,    1,    1,    6,    1,    7,    7,    7,
+        7,    7,    7,    7,    7,    7,    7,    1,    1,    1,
+        1,    1,    1,    1,    8,    9,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,   10,    1,    1,    1,   11,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,   12,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,   13,    1,    1,
+        1,    1,    1,   14,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+static yyconst int yy_meta[15] =
+    {   0,
+        1,    2,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1
+    } ;
+
+static yyconst short int yy_base[38] =
+    {   0,
+       35,   34,    0,    5,    0,    0,   36,   39,    0,   39,
+       39,   30,   39,   21,    0,    1,   26,   25,    2,   24,
+       21,   15,    9,   11,   39,   12,   39,   39,   10,   39,
+       39,   39,   39,   23,   25,   27,    0
+    } ;
+
+static yyconst short int yy_def[38] =
+    {   0,
+       34,   34,   35,   35,   36,   36,   33,   33,   33,   33,
+       33,   33,   33,   33,   33,   33,   33,   33,   33,   33,
+       33,   37,   33,   33,   33,   33,   33,   33,   37,   33,
+       33,   33,    0,   33,   33,   33,   33
+    } ;
+
+static yyconst short int yy_nxt[54] =
+    {   0,
+       28,   11,   12,   14,   15,   11,   11,   12,   24,   25,
+       11,   18,   20,   19,   21,   23,   29,   24,   26,   30,
+       31,   29,   32,    8,    8,   10,   10,   13,   13,   27,
+       26,   23,   22,   17,   16,   33,    9,    9,    7,   33,
+       33,   33,   33,   33,   33,   33,   33,   33,   33,   33,
+       33,   33,   33
+    } ;
+
+static yyconst short int yy_chk[54] =
+    {   0,
+       37,    3,    3,    9,    9,    3,    4,    4,   19,   19,
+        4,   15,   16,   15,   16,   23,   29,   24,   26,   23,
+       24,   22,   26,   34,   34,   35,   35,   36,   36,   21,
+       20,   18,   17,   14,   12,    7,    2,    1,   33,   33,
+       33,   33,   33,   33,   33,   33,   33,   33,   33,   33,
+       33,   33,   33
+    } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "thinkjettopbm.l"
+#define INITIAL 0
+/*
+ * A user reported in January 2005 a problem building Thinkjettopbm
+ * in which the opening comment delimiter above for some reason did
+ * not make it into the Lex output in the Netpbm build of this.
+ * Needless to say, that would not compile.  This user was using
+ * 'lex -t' on Tru64.  We did not find it worthwhile to debug it.
+ *
+ * Simple FLEX scanner to convert HP ThinkJet graphics image
+ * to PBM format.
+ *
+ * Implements a small subset of ThinkJet commands.
+ *
+ * Copyright (C) 2001 by W. Eric Norum
+ *
+ * Department of Electrical Engineering
+ * University of Saskatchewan
+ * Saskatoon, Saskatchewan, CANADA
+ * eric.norum@usask.ca
+ *
+ *  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.
+ *
+ *  Modified 2001.04.05 by Bryan Henderson for inclusion in the Netpbm
+ *  package.  Now uses Netpbm libraries and, for consistency with other
+ *  Netpbm programs, does not have PGM output option.
+ */
+#line 34 "thinkjettopbm.l"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include "pbm.h"
+#include "shhopt.h"
+
+/* The following macro definitions tell Lex what sort of code to generate.
+   GNU Flex does #if XXX for some of these, as opposed to #ifdef XXX, which
+   means that they properly have to be set to zero instead of just left
+   undefined.  (Simply leaving them undefined typically works anyway, but it
+   is a problem if you use compiler options that say to fail when someone
+   uses a macro he failed to define).
+*/
+#define YY_NO_UNPUT
+#define YY_STACK_USED 0
+#define YY_ALWAYS_INTERACTIVE 0
+#define YY_NEVER_INTERACTIVE 0
+#define YY_MAIN 0
+    /* Don't include the main() function.  We have our own */
+
+static int yylex(void);
+static int yywrap(void);
+/* This works, but generates a warning 
+static void yyrestart(FILE*);
+*/
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilespec;  /* Filespec of input file */
+
+    unsigned int debug;
+};
+
+
+
+struct RowInfo {
+    int     length;    /* length, in bytes */
+    char    *bits;     /* Bitmap */
+};
+
+static int maxRowLength;
+static int rowCount;
+static int rowCapacity;
+static struct RowInfo *rows;
+
+static int column;
+
+int debugFlag;
+static void debug (const char *format, ...);
+
+#define RASTERMODE 1
+#define ROWMODE 2
+
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap YY_PROTO(( void ));
+#else
+extern int yywrap YY_PROTO(( void ));
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen YY_PROTO(( yyconst char * ));
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+#endif
+
+#if YY_STACK_USED
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+#ifndef YY_NO_PUSH_STATE
+static void yy_push_state YY_PROTO(( int new_state ));
+#endif
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state YY_PROTO(( void ));
+#endif
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state YY_PROTO(( void ));
+#endif
+
+#else
+#define YY_NO_PUSH_STATE 1
+#define YY_NO_POP_STATE 1
+#define YY_NO_TOP_STATE 1
+#endif
+
+#ifdef YY_MALLOC_DECL
+YY_MALLOC_DECL
+#else
+#if __STDC__
+#ifndef __cplusplus
+#include <stdlib.h>
+#endif
+#else
+/* Just try to get by without declaring the routines.  This will fail
+ * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)
+ * or sizeof(void*) != sizeof(int).
+ */
+#endif
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	if ( yy_current_buffer->yy_is_interactive ) \
+		{ \
+		int c = '*', n; \
+		for ( n = 0; n < max_size && \
+			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+			buf[n] = (char) c; \
+		if ( c == '\n' ) \
+			buf[n++] = (char) c; \
+		if ( c == EOF && ferror( yyin ) ) \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+		result = n; \
+		} \
+	else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \
+		  && ferror( yyin ) ) \
+		YY_FATAL_ERROR( "input in flex scanner failed" );
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL int yylex YY_PROTO(( void ))
+#endif
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+YY_DECL
+	{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+
+#line 95 "thinkjettopbm.l"
+
+
+
+	if ( yy_init )
+		{
+		yy_init = 0;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! yy_start )
+			yy_start = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! yy_current_buffer )
+			yy_current_buffer =
+				yy_create_buffer( yyin, YY_BUF_SIZE );
+
+		yy_load_buffer_state();
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = yy_c_buf_p;
+
+		/* Support of yytext. */
+		*yy_cp = yy_hold_char;
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = yy_start;
+yy_match:
+		do
+			{
+			register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+			if ( yy_accept[yy_current_state] )
+				{
+				yy_last_accepting_state = yy_current_state;
+				yy_last_accepting_cpos = yy_cp;
+				}
+			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+				{
+				yy_current_state = (int) yy_def[yy_current_state];
+				if ( yy_current_state >= 34 )
+					yy_c = yy_meta[(unsigned int) yy_c];
+				}
+			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+			++yy_cp;
+			}
+		while ( yy_base[yy_current_state] != 39 );
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+		if ( yy_act == 0 )
+			{ /* have to back up */
+			yy_cp = yy_last_accepting_cpos;
+			yy_current_state = yy_last_accepting_state;
+			yy_act = yy_accept[yy_current_state];
+			}
+
+		YY_DO_BEFORE_ACTION;
+
+
+do_action:	/* This label is used only to access EOF actions. */
+
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+			case 0: /* must back up */
+			/* undo the effects of YY_DO_BEFORE_ACTION */
+			*yy_cp = yy_hold_char;
+			yy_cp = yy_last_accepting_cpos;
+			yy_current_state = yy_last_accepting_state;
+			goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 97 "thinkjettopbm.l"
+{
+                        rows[rowCount].bits[column++] = yytext[0]; 
+                        if (column >= rows[rowCount].length) {
+                            rowCount++;
+                            debug ("Done %d-byte row %d.\n", column, rowCount);
+                            BEGIN (RASTERMODE);
+                        }
+                        }
+	YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 106 "thinkjettopbm.l"
+{
+                            int l;
+                            if (rowCount >= rowCapacity) {
+                                rowCapacity += 100;
+                                rows = realloc (rows, rowCapacity * sizeof *rows);
+                                if (rows == NULL)
+                                    pm_error ("Out of memory.");
+                            }
+                            l = atoi (yytext+3);
+                            rows[rowCount].length = l;
+                            rows[rowCount].bits = malloc (l);
+                            if (rows[rowCount].bits == NULL)
+                                pm_error ("Out of memory.");
+                            if (l > maxRowLength)
+                                maxRowLength = l;
+                            debug ("Start %d-byte row.\n", l);
+                            column = 0;
+                            BEGIN (ROWMODE);
+                            }
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 126 "thinkjettopbm.l"
+{
+                       debug ("Match <esc>*rB\n");
+                       BEGIN (0);
+                       }
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 131 "thinkjettopbm.l"
+{ pm_error ("Unexpected character (%#x) in raster mode.\n", yytext[0]); }
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 133 "thinkjettopbm.l"
+{ debug ("Match <esc>&l\n"); }
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 134 "thinkjettopbm.l"
+{ debug ("Match <esc>*r#S\n"); }
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 135 "thinkjettopbm.l"
+{ debug ("Match <esc>*r#w\n"); }
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 136 "thinkjettopbm.l"
+{
+                       debug ("Match <esc>*rA\n");
+                       BEGIN (RASTERMODE);
+                       }
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 141 "thinkjettopbm.l"
+{ /* Silently consume all other characters */ }
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 143 "thinkjettopbm.l"
+ECHO;
+	YY_BREAK
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(RASTERMODE):
+case YY_STATE_EOF(ROWMODE):
+	yyterminate();
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = yy_hold_char;
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * yylex().  If so, then we have to assure
+			 * consistency between yy_current_buffer and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			yy_n_chars = yy_current_buffer->yy_n_chars;
+			yy_current_buffer->yy_input_file = yyin;
+			yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state();
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+			yy_bp = yytext_ptr + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++yy_c_buf_p;
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = yy_c_buf_p;
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer() )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				yy_did_buffer_switch_on_eof = 0;
+
+				if ( yywrap() )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				yy_c_buf_p =
+					yytext_ptr + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state();
+
+				yy_cp = yy_c_buf_p;
+				yy_bp = yytext_ptr + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				yy_c_buf_p =
+				&yy_current_buffer->yy_ch_buf[yy_n_chars];
+
+				yy_current_state = yy_get_previous_state();
+
+				yy_cp = yy_c_buf_p;
+				yy_bp = yytext_ptr + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+	} /* end of yylex */
+
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+
+static int yy_get_next_buffer()
+	{
+	register char *dest = yy_current_buffer->yy_ch_buf;
+	register char *source = yytext_ptr;
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( yy_current_buffer->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		yy_current_buffer->yy_n_chars = yy_n_chars = 0;
+
+	else
+		{
+		int num_to_read =
+			yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+#ifdef YY_USES_REJECT
+			YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+#else
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = yy_current_buffer;
+
+			int yy_c_buf_p_offset =
+				(int) (yy_c_buf_p - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					yy_flex_realloc( (void *) b->yy_ch_buf,
+							 b->yy_buf_size + 2 );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = yy_current_buffer->yy_buf_size -
+						number_to_move - 1;
+#endif
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+			yy_n_chars, num_to_read );
+
+		yy_current_buffer->yy_n_chars = yy_n_chars;
+		}
+
+	if ( yy_n_chars == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			yyrestart( yyin );
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			yy_current_buffer->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	yy_n_chars += number_to_move;
+	yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+	yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+	yytext_ptr = &yy_current_buffer->yy_ch_buf[0];
+
+	return ret_val;
+	}
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+static yy_state_type yy_get_previous_state()
+	{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+
+	yy_current_state = yy_start;
+
+	for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+		{
+		register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 6);
+		if ( yy_accept[yy_current_state] )
+			{
+			yy_last_accepting_state = yy_current_state;
+			yy_last_accepting_cpos = yy_cp;
+			}
+		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+			{
+			yy_current_state = (int) yy_def[yy_current_state];
+			if ( yy_current_state >= 34 )
+				yy_c = yy_meta[(unsigned int) yy_c];
+			}
+		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+		}
+
+	return yy_current_state;
+	}
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+yy_state_type yy_current_state;
+#endif
+	{
+	register int yy_is_jam;
+	register char *yy_cp = yy_c_buf_p;
+
+	register YY_CHAR yy_c = 6;
+	if ( yy_accept[yy_current_state] )
+		{
+		yy_last_accepting_state = yy_current_state;
+		yy_last_accepting_cpos = yy_cp;
+		}
+	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+		{
+		yy_current_state = (int) yy_def[yy_current_state];
+		if ( yy_current_state >= 34 )
+			yy_c = yy_meta[(unsigned int) yy_c];
+		}
+	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+	yy_is_jam = (yy_current_state == 33);
+
+	return yy_is_jam ? 0 : yy_current_state;
+	}
+
+
+#ifndef YY_NO_UNPUT
+#ifdef YY_USE_PROTOS
+static void yyunput( int c, register char *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+int c;
+register char *yy_bp;
+#endif
+	{
+	register char *yy_cp = yy_c_buf_p;
+
+	/* undo effects of setting up yytext */
+	*yy_cp = yy_hold_char;
+
+	if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+		{ /* need to shift things up to make room */
+		/* +2 for EOB chars. */
+		register int number_to_move = yy_n_chars + 2;
+		register char *dest = &yy_current_buffer->yy_ch_buf[
+					yy_current_buffer->yy_buf_size + 2];
+		register char *source =
+				&yy_current_buffer->yy_ch_buf[number_to_move];
+
+		while ( source > yy_current_buffer->yy_ch_buf )
+			*--dest = *--source;
+
+		yy_cp += (int) (dest - source);
+		yy_bp += (int) (dest - source);
+		yy_current_buffer->yy_n_chars =
+			yy_n_chars = yy_current_buffer->yy_buf_size;
+
+		if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+			YY_FATAL_ERROR( "flex scanner push-back overflow" );
+		}
+
+	*--yy_cp = (char) c;
+
+
+	yytext_ptr = yy_bp;
+	yy_hold_char = *yy_cp;
+	yy_c_buf_p = yy_cp;
+	}
+#endif	/* ifndef YY_NO_UNPUT */
+
+
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#endif
+	{
+	int c;
+
+	*yy_c_buf_p = yy_hold_char;
+
+	if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+			/* This was really a NUL. */
+			*yy_c_buf_p = '\0';
+
+		else
+			{ /* need more input */
+			int offset = yy_c_buf_p - yytext_ptr;
+			++yy_c_buf_p;
+
+			switch ( yy_get_next_buffer() )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					yyrestart( yyin );
+
+					/* fall through */
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( yywrap() )
+						return EOF;
+
+					if ( ! yy_did_buffer_switch_on_eof )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput();
+#else
+					return input();
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					yy_c_buf_p = yytext_ptr + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) yy_c_buf_p;	/* cast for 8-bit char's */
+	*yy_c_buf_p = '\0';	/* preserve yytext */
+	yy_hold_char = *++yy_c_buf_p;
+
+
+	return c;
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yyrestart( FILE *input_file )
+#else
+void yyrestart( input_file )
+FILE *input_file;
+#endif
+	{
+	if ( ! yy_current_buffer )
+		yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+	yy_init_buffer( yy_current_buffer, input_file );
+	yy_load_buffer_state();
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+#else
+void yy_switch_to_buffer( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+	{
+	if ( yy_current_buffer == new_buffer )
+		return;
+
+	if ( yy_current_buffer )
+		{
+		/* Flush out information for old buffer. */
+		*yy_c_buf_p = yy_hold_char;
+		yy_current_buffer->yy_buf_pos = yy_c_buf_p;
+		yy_current_buffer->yy_n_chars = yy_n_chars;
+		}
+
+	yy_current_buffer = new_buffer;
+	yy_load_buffer_state();
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (yywrap()) processing, but the only time this flag
+	 * is looked at is after yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	yy_did_buffer_switch_on_eof = 1;
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_load_buffer_state( void )
+#else
+void yy_load_buffer_state()
+#endif
+	{
+	yy_n_chars = yy_current_buffer->yy_n_chars;
+	yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+	yyin = yy_current_buffer->yy_input_file;
+	yy_hold_char = *yy_c_buf_p;
+	}
+
+
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+#else
+YY_BUFFER_STATE yy_create_buffer( file, size )
+FILE *file;
+int size;
+#endif
+	{
+	YY_BUFFER_STATE b;
+
+	b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	yy_init_buffer( b, file );
+
+	return b;
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_delete_buffer( YY_BUFFER_STATE b )
+#else
+void yy_delete_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+	{
+	if ( ! b )
+		return;
+
+	if ( b == yy_current_buffer )
+		yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		yy_flex_free( (void *) b->yy_ch_buf );
+
+	yy_flex_free( (void *) b );
+	}
+
+
+#ifndef YY_ALWAYS_INTERACTIVE
+#ifndef YY_NEVER_INTERACTIVE
+extern int isatty YY_PROTO(( int ));
+#endif
+#endif
+
+#ifdef YY_USE_PROTOS
+void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
+#else
+void yy_init_buffer( b, file )
+YY_BUFFER_STATE b;
+FILE *file;
+#endif
+
+
+	{
+	yy_flush_buffer( b );
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+#if YY_ALWAYS_INTERACTIVE
+	b->yy_is_interactive = 1;
+#else
+#if YY_NEVER_INTERACTIVE
+	b->yy_is_interactive = 0;
+#else
+	b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+#endif
+#endif
+	}
+
+
+#ifdef YY_USE_PROTOS
+void yy_flush_buffer( YY_BUFFER_STATE b )
+#else
+void yy_flush_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+
+	{
+	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == yy_current_buffer )
+		yy_load_buffer_state();
+	}
+
+
+#ifndef YY_NO_SCAN_BUFFER
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )
+#else
+YY_BUFFER_STATE yy_scan_buffer( base, size )
+char *base;
+yy_size_t size;
+#endif
+	{
+	YY_BUFFER_STATE b;
+
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	yy_switch_to_buffer( b );
+
+	return b;
+	}
+#endif
+
+
+#ifndef YY_NO_SCAN_STRING
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str )
+#else
+YY_BUFFER_STATE yy_scan_string( yy_str )
+yyconst char *yy_str;
+#endif
+	{
+	int len;
+	for ( len = 0; yy_str[len]; ++len )
+		;
+
+	return yy_scan_bytes( yy_str, len );
+	}
+#endif
+
+
+#ifndef YY_NO_SCAN_BYTES
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len )
+#else
+YY_BUFFER_STATE yy_scan_bytes( bytes, len )
+yyconst char *bytes;
+int len;
+#endif
+	{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n;
+	int i;
+
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = len + 2;
+	buf = (char *) yy_flex_alloc( n );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+	for ( i = 0; i < len; ++i )
+		buf[i] = bytes[i];
+
+	buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = yy_scan_buffer( buf, n );
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+	}
+#endif
+
+
+#ifndef YY_NO_PUSH_STATE
+#ifdef YY_USE_PROTOS
+static void yy_push_state( int new_state )
+#else
+static void yy_push_state( new_state )
+int new_state;
+#endif
+	{
+	if ( yy_start_stack_ptr >= yy_start_stack_depth )
+		{
+		yy_size_t new_size;
+
+		yy_start_stack_depth += YY_START_STACK_INCR;
+		new_size = yy_start_stack_depth * sizeof( int );
+
+		if ( ! yy_start_stack )
+			yy_start_stack = (int *) yy_flex_alloc( new_size );
+
+		else
+			yy_start_stack = (int *) yy_flex_realloc(
+					(void *) yy_start_stack, new_size );
+
+		if ( ! yy_start_stack )
+			YY_FATAL_ERROR(
+			"out of memory expanding start-condition stack" );
+		}
+
+	yy_start_stack[yy_start_stack_ptr++] = YY_START;
+
+	BEGIN(new_state);
+	}
+#endif
+
+
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state()
+	{
+	if ( --yy_start_stack_ptr < 0 )
+		YY_FATAL_ERROR( "start-condition stack underflow" );
+
+	BEGIN(yy_start_stack[yy_start_stack_ptr]);
+	}
+#endif
+
+
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state()
+	{
+	return yy_start_stack[yy_start_stack_ptr - 1];
+	}
+#endif
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( yyconst char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+	{
+	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+	}
+
+
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+		yytext[yyleng] = yy_hold_char; \
+		yy_c_buf_p = yytext + n; \
+		yy_hold_char = *yy_c_buf_p; \
+		*yy_c_buf_p = '\0'; \
+		yyleng = n; \
+		} \
+	while ( 0 )
+
+
+/* Internal utility routines. */
+
+#ifndef yytext_ptr
+#ifdef YY_USE_PROTOS
+static void yy_flex_strncpy( char *s1, yyconst char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+yyconst char *s2;
+int n;
+#endif
+	{
+	register int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+	}
+#endif
+
+#ifdef YY_NEED_STRLEN
+#ifdef YY_USE_PROTOS
+static int yy_flex_strlen( yyconst char *s )
+#else
+static int yy_flex_strlen( s )
+yyconst char *s;
+#endif
+	{
+	register int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+	}
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( yy_size_t size )
+#else
+static void *yy_flex_alloc( size )
+yy_size_t size;
+#endif
+	{
+	return (void *) malloc( size );
+	}
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, yy_size_t size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+yy_size_t size;
+#endif
+	{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+	}
+
+#ifdef YY_USE_PROTOS
+static void yy_flex_free( void *ptr )
+#else
+static void yy_flex_free( ptr )
+void *ptr;
+#endif
+	{
+	free( ptr );
+	}
+
+#if YY_MAIN
+int main()
+	{
+	yylex();
+	return 0;
+	}
+#endif
+#line 143 "thinkjettopbm.l"
+
+
+static void
+parseCommandLine(int argc, 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.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0,   "debug",      OPT_FLAG, NULL, &cmdlineP->debug,    0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilespec = "-";
+    else if (argc-1 > 1)
+        pm_error("Too many parameters.  There is at most one: the input "
+                 "file specification.  You specified %d", argc-1);
+    else
+        cmdlineP->inputFilespec = argv[1];
+}
+
+
+
+/*
+ * Application entry point
+ */
+int
+main (int argc, char **argv)
+{
+    struct cmdlineInfo cmdline;
+
+    pbm_init( &argc, argv );
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    if (strlen(cmdline.inputFilespec) > 0) {
+        FILE * ifP;
+        ifP = freopen(cmdline.inputFilespec, "rb", stdin);
+        if (ifP == NULL)
+            pm_error("Unable to open file '%s' as stdin.  errno=%d (%s)", 
+                     cmdline.inputFilespec, errno, strerror(errno));
+    }
+    debugFlag = cmdline.debug;
+    yylex ();
+    return 0;
+}
+
+/*
+ * Finish at end of file
+ */
+static int 
+yywrap (void)
+{
+    int row;
+    unsigned char * packed_bitrow;
+
+    debug ("Got %d rows, %d columns\n", rowCount, maxRowLength);
+
+    /*
+     * Quite simple since ThinkJet bit arrangement matches PBM
+     */
+    pbm_writepbminit(stdout, maxRowLength*8, rowCount, 0);
+
+    packed_bitrow = malloc(maxRowLength);
+    if (packed_bitrow == NULL) pm_error("Out of memory");
+
+    for (row = 0 ; row < rowCount ; row++) {
+        int col;
+        for (col = 0 ; col < rows[row].length ; col++) 
+            packed_bitrow[col] = rows[row].bits[col];
+        for (        ; col < maxRowLength;      col++)
+            packed_bitrow[col] = 0;
+        pbm_writepbmrow_packed(stdout, packed_bitrow, maxRowLength*8, 0);
+    }
+    free(packed_bitrow);
+    return 1;
+}
+
+/*
+ * Print debugging message
+ */
+static void
+debug (const char *format, ...)
+{
+    va_list args;
+
+    if (debugFlag) {
+        fprintf (stderr, "thinkjettopbm: ");
+        va_start (args, format);
+        vfprintf (stderr, format, args);
+        va_end (args);
+    }
+}
+
diff --git a/converter/pbm/thinkjettopbm.l b/converter/pbm/thinkjettopbm.l
new file mode 100644
index 00000000..a66ae07e
--- /dev/null
+++ b/converter/pbm/thinkjettopbm.l
@@ -0,0 +1,251 @@
+%pointer
+/*
+ * A user reported in January 2005 a problem building Thinkjettopbm
+ * in which the opening comment delimiter above for some reason did
+ * not make it into the Lex output in the Netpbm build of this.
+ * Needless to say, that would not compile.  This user was using
+ * 'lex -t' on Tru64.  We did not find it worthwhile to debug it.
+ *
+ * Simple FLEX scanner to convert HP ThinkJet graphics image
+ * to PBM format.
+ *
+ * Implements a small subset of ThinkJet commands.
+ *
+ * Copyright (C) 2001 by W. Eric Norum
+ *
+ * Department of Electrical Engineering
+ * University of Saskatchewan
+ * Saskatoon, Saskatchewan, CANADA
+ * eric.norum@usask.ca
+ *
+ *  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.
+ *
+ *  Modified 2001.04.05 by Bryan Henderson for inclusion in the Netpbm
+ *  package.  Now uses Netpbm libraries and, for consistency with other
+ *  Netpbm programs, does not have PGM output option.
+ */
+
+%{
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include "pbm.h"
+#include "shhopt.h"
+
+/* The following macro definitions tell Lex what sort of code to generate.
+   GNU Flex does #if XXX for some of these, as opposed to #ifdef XXX, which
+   means that they properly have to be set to zero instead of just left
+   undefined.  (Simply leaving them undefined typically works anyway, but it
+   is a problem if you use compiler options that say to fail when someone
+   uses a macro he failed to define).
+*/
+#define YY_NO_UNPUT
+#define YY_STACK_USED 0
+#define YY_ALWAYS_INTERACTIVE 0
+#define YY_NEVER_INTERACTIVE 0
+#define YY_MAIN 0
+    /* Don't include the main() function.  We have our own */
+
+static int yylex(void);
+static int yywrap(void);
+/* This works, but generates a warning 
+static void yyrestart(FILE*);
+*/
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilespec;  /* Filespec of input file */
+
+    unsigned int debug;
+};
+
+
+
+struct RowInfo {
+    int     length;    /* length, in bytes */
+    char    *bits;     /* Bitmap */
+};
+
+static int maxRowLength;
+static int rowCount;
+static int rowCapacity;
+static struct RowInfo *rows;
+
+static int column;
+
+int debugFlag;
+static void debug (const char *format, ...);
+
+%}
+
+DIG             [0-9]
+
+%x RASTERMODE ROWMODE
+
+%%
+
+<ROWMODE>[\0-\377]      {
+                        rows[rowCount].bits[column++] = yytext[0]; 
+                        if (column >= rows[rowCount].length) {
+                            rowCount++;
+                            debug ("Done %d-byte row %d.\n", column, rowCount);
+                            BEGIN (RASTERMODE);
+                        }
+                        }
+
+<RASTERMODE>\033\*b{DIG}+W  {
+                            int l;
+                            if (rowCount >= rowCapacity) {
+                                rowCapacity += 100;
+                                rows = realloc (rows, rowCapacity * sizeof *rows);
+                                if (rows == NULL)
+                                    pm_error ("Out of memory.");
+                            }
+                            l = atoi (yytext+3);
+                            rows[rowCount].length = l;
+                            rows[rowCount].bits = malloc (l);
+                            if (rows[rowCount].bits == NULL)
+                                pm_error ("Out of memory.");
+                            if (l > maxRowLength)
+                                maxRowLength = l;
+                            debug ("Start %d-byte row.\n", l);
+                            column = 0;
+                            BEGIN (ROWMODE);
+                            }
+
+<RASTERMODE>\033\*rB   {
+                       debug ("Match <esc>*rB\n");
+                       BEGIN (0);
+                       }
+
+<RASTERMODE>[.\0\n]    { pm_error ("Unexpected character (%#x) in raster mode.\n", yytext[0]); }
+
+\033\&l{DIG}+.         { debug ("Match <esc>&l\n"); }
+\033\*r{DIG}+S         { debug ("Match <esc>*r#S\n"); }
+\033\*b{DIG}+W         { debug ("Match <esc>*r#w\n"); }
+\033\*rA               {
+                       debug ("Match <esc>*rA\n");
+                       BEGIN (RASTERMODE);
+                       }
+
+[\0-\377]               { /* Silently consume all other characters */ }
+
+%%
+
+static void
+parseCommandLine(int argc, 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.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0,   "debug",      OPT_FLAG, NULL, &cmdlineP->debug,    0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilespec = "-";
+    else if (argc-1 > 1)
+        pm_error("Too many parameters.  There is at most one: the input "
+                 "file specification.  You specified %d", argc-1);
+    else
+        cmdlineP->inputFilespec = argv[1];
+}
+
+
+
+/*
+ * Application entry point
+ */
+int
+main (int argc, char **argv)
+{
+    struct cmdlineInfo cmdline;
+
+    pbm_init( &argc, argv );
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    if (strlen(cmdline.inputFilespec) > 0) {
+        FILE * ifP;
+        ifP = freopen(cmdline.inputFilespec, "rb", stdin);
+        if (ifP == NULL)
+            pm_error("Unable to open file '%s' as stdin.  errno=%d (%s)", 
+                     cmdline.inputFilespec, errno, strerror(errno));
+    }
+    debugFlag = cmdline.debug;
+    yylex ();
+    return 0;
+}
+
+/*
+ * Finish at end of file
+ */
+static int 
+yywrap (void)
+{
+    int row;
+    unsigned char * packed_bitrow;
+
+    debug ("Got %d rows, %d columns\n", rowCount, maxRowLength);
+
+    /*
+     * Quite simple since ThinkJet bit arrangement matches PBM
+     */
+    pbm_writepbminit(stdout, maxRowLength*8, rowCount, 0);
+
+    packed_bitrow = malloc(maxRowLength);
+    if (packed_bitrow == NULL) pm_error("Out of memory");
+
+    for (row = 0 ; row < rowCount ; row++) {
+        int col;
+        for (col = 0 ; col < rows[row].length ; col++) 
+            packed_bitrow[col] = rows[row].bits[col];
+        for (        ; col < maxRowLength;      col++)
+            packed_bitrow[col] = 0;
+        pbm_writepbmrow_packed(stdout, packed_bitrow, maxRowLength*8, 0);
+    }
+    free(packed_bitrow);
+    return 1;
+}
+
+/*
+ * Print debugging message
+ */
+static void
+debug (const char *format, ...)
+{
+    va_list args;
+
+    if (debugFlag) {
+        fprintf (stderr, "thinkjettopbm: ");
+        va_start (args, format);
+        vfprintf (stderr, format, args);
+        va_end (args);
+    }
+}
+
diff --git a/converter/pbm/wbmptopbm.c b/converter/pbm/wbmptopbm.c
new file mode 100644
index 00000000..a3ce7ec3
--- /dev/null
+++ b/converter/pbm/wbmptopbm.c
@@ -0,0 +1,112 @@
+/* wbmptopbm.c - convert a wbmp file to a portable bitmap
+
+   This is derived for Netpbm from the pbmwbmp package from 
+   <http://www.looplab.com/wap/tools> on 2000.06.06.
+   
+   The specifications for the wbmp format are part of the Wireless 
+   Application Environment specification at
+   <http://www.wapforum.org/what/technical.htm>.
+
+** Copyright (C) 1999 Terje Sannum <terje@looplab.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.
+*/
+
+#include "pbm.h"
+
+static int 
+readc(FILE *f) {
+  int c = fgetc(f);
+  if(c == EOF) pm_error("EOF / read error");
+  return c;
+}
+
+
+
+static int 
+readint(FILE *f) {
+  int c=0, pos=0, sum=0;
+  do {
+    c = readc(f);
+    sum = (sum << 7*pos++) | (c & 0x7f);
+  } while(c & 0x80);
+  return sum;
+}
+
+
+
+static void 
+readheader(int h, FILE *f) {
+  int c,i;
+  switch(h & 0x60) {
+  case 0x00:
+    /* Type 00: read multi-byte bitfield */
+    do c=readc(f); while(c & 0x80);
+    break;
+  case 0x60:
+    /* Type 11: read name/value pair */
+    for(i=0; i < ((h & 0x70) >> 4) + (h & 0x0f); i++) c=readc(f);
+    break;
+  }
+}
+
+
+
+static bit **
+readwbmp(FILE *f, int *cols, int *rows) {
+  int i,j,k,row,c;
+  bit **image;
+  /* Type */
+  c = readint(f);
+  if(c != 0) pm_error("Unrecognized WBMP type");
+  /* Headers */
+  c = readc(f); /* FixHeaderField */
+  while(c & 0x80) { /* ExtHeaderFields */
+    c = readc(f);
+    readheader(c, f);
+  }
+  /* Geometry */
+  *cols = readint(f);
+  *rows = readint(f);
+  image = pbm_allocarray(*cols, *rows);
+  /* read image */
+  row = *cols/8;
+  if(*cols%8) row +=1;
+  for(i=0; i<*rows; i++) {
+    for(j=0; j<row; j++) {
+      c=readc(f);
+      for(k=0; k<8 && j*8+k<*cols; k++) {
+	image[i][j*8+k] = c & (0x80 >> k) ? PBM_WHITE : PBM_BLACK;
+      }
+    }
+  }
+  return image;
+}
+
+
+
+int 
+main(int argc, char *argv[]) {
+  FILE *f;
+  bit **image;
+  int rows, cols;
+
+  pbm_init(&argc, argv);
+  if(argc > 2) {
+    fprintf(stderr, "Copyright (C) 1999 Terje Sannum <terje@looplab.com>\n");
+    pm_usage("[wbmpfile]");
+  }
+
+  f = argc == 2 ? pm_openr(argv[1]) : stdin;
+  image = readwbmp(f, &cols, &rows);
+  pm_close(f);
+  pbm_writepbm(stdout, image, cols, rows, 0);
+  pm_close(stdout);
+  return 0;
+}
+
diff --git a/converter/pbm/xbmtopbm.c b/converter/pbm/xbmtopbm.c
new file mode 100644
index 00000000..7779a9b5
--- /dev/null
+++ b/converter/pbm/xbmtopbm.c
@@ -0,0 +1,255 @@
+/* xbmtopbm.c - read an X bitmap file and produce a portable bitmap
+**
+** Copyright (C) 1988 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <string.h>
+
+#include "nstring.h"
+#include "pbm.h"
+
+#define TRUE 1
+#define FALSE 0
+
+static void ReadBitmapFile ARGS(( FILE* stream, int* widthP, int* heightP, char** dataP ));
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    int rows, cols, row, col, charcount;
+    char* data;
+    char mask;
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( "[bitmapfile]" );
+    
+    if ( argc == 2 )
+        ifp = pm_openr( argv[1] );
+    else
+        ifp = stdin;
+
+    ReadBitmapFile( ifp, &cols, &rows, &data );
+
+    pm_close( ifp );
+
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    for ( row = 0; row < rows; ++row )
+        {
+        charcount = 0;
+        mask = 1;
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+            {
+            if ( charcount >= 8 )
+                {
+                ++data;
+                charcount = 0;
+                mask = 1;
+                }
+            *bP = ( *data & mask ) ? PBM_BLACK : PBM_WHITE;
+            ++charcount;
+            mask = mask << 1;
+            }
+        ++data;
+        pbm_writepbmrow( stdout, bitrow, cols, 0 );
+        }
+
+    pm_close( stdout );
+    exit( 0 );
+    }
+
+#define MAX_LINE 500
+
+static void
+ReadBitmapFile( stream, widthP, heightP, dataP )
+     FILE* stream;
+     int* widthP;
+     int* heightP;
+     char** dataP;
+{
+  char line[MAX_LINE], name_and_type[MAX_LINE];
+  char* ptr;
+  char* t;
+  int version10, raster_length, v;
+  register int bytes, bytes_per_line, padding;
+  register int c1, c2, value1, value2;
+  int hex_table[256];
+  int found_declaration;
+  /* In scanning through the bitmap file, we have found the first
+     line of the C declaration of the array (the "static char ..."
+     or whatever line)
+     */
+  int eof;
+  /* We've encountered end of file while searching file */
+
+  *widthP = *heightP = -1;
+
+  found_declaration = FALSE;    /* Haven't found it yet; haven't even looked*/
+  eof = FALSE;                  /* Haven't encountered end of file yet */
+
+  while (!found_declaration && !eof) {
+    if ( fgets( line, MAX_LINE, stream ) == NULL )
+      eof = TRUE;
+    else {
+      if ( strlen( line ) == MAX_LINE - 1 )
+        pm_error( "line too long" );
+
+      if ( sscanf( line, "#define %s %d", name_and_type, &v ) == 2 ) {
+        if ( ( t = strrchr( name_and_type, '_' ) ) == NULL )
+          t = name_and_type;
+        else
+          ++t;
+        if ( STREQ( "width", t ) )
+          *widthP = v;
+        else if ( STREQ( "height", t ) )
+          *heightP = v;
+        continue;
+      }
+        
+      if ( sscanf( line, "static short %s = {", name_and_type ) == 1 ) {
+        version10 = TRUE;
+        found_declaration = TRUE;
+      }
+      else if ( sscanf( line, "static char %s = {", name_and_type ) == 1 ) {
+        version10 = FALSE;
+        found_declaration = TRUE;
+      }
+      else if (sscanf(line, 
+                      "static unsigned char %s = {", name_and_type ) == 1 ) {
+        version10 = FALSE;
+        found_declaration = TRUE;
+      }
+    }
+  }
+ 
+  if (!found_declaration) 
+    pm_error("Unable to find a line in the file containing the start "
+             "of C array declaration (\"static char\" or whatever)");
+
+  if ( *widthP == -1 )
+    pm_error( "invalid width" );
+  if ( *heightP == -1 )
+    pm_error( "invalid height" );
+
+  padding = 0;
+  if ( ((*widthP % 16) >= 1) && ((*widthP % 16) <= 8) && version10 )
+    padding = 1;
+
+  bytes_per_line = (*widthP+7)/8 + padding;
+    
+  raster_length =  bytes_per_line * *heightP;
+  *dataP = (char*) malloc( raster_length );
+  if ( *dataP == (char*) 0 )
+    pm_error( "out of memory" );
+
+  /* Initialize hex_table. */
+  for ( c1 = 0; c1 < 256; ++c1 )
+    hex_table[c1] = 256;
+  hex_table['0'] = 0;
+  hex_table['1'] = 1;
+  hex_table['2'] = 2;
+  hex_table['3'] = 3;
+  hex_table['4'] = 4;
+  hex_table['5'] = 5;
+  hex_table['6'] = 6;
+  hex_table['7'] = 7;
+  hex_table['8'] = 8;
+  hex_table['9'] = 9;
+  hex_table['A'] = 10;
+  hex_table['B'] = 11;
+  hex_table['C'] = 12;
+  hex_table['D'] = 13;
+  hex_table['E'] = 14;
+  hex_table['F'] = 15;
+  hex_table['a'] = 10;
+  hex_table['b'] = 11;
+  hex_table['c'] = 12;
+  hex_table['d'] = 13;
+  hex_table['e'] = 14;
+  hex_table['f'] = 15;
+
+  if ( version10 )
+    for ( bytes = 0, ptr = *dataP; bytes < raster_length; bytes += 2 ) {
+      while ( ( c1 = getc( stream ) ) != 'x' )
+        if ( c1 == EOF )
+          pm_error( "EOF / read error" );
+      c1 = getc( stream );
+      c2 = getc( stream );
+      if ( c1 == EOF || c2 == EOF )
+        pm_error( "EOF / read error" );
+      value1 = ( hex_table[c1] << 4 ) + hex_table[c2];
+      if ( value1 >= 256 )
+        pm_error( "syntax error" );
+      c1 = getc( stream );
+      c2 = getc( stream );
+      if ( c1 == EOF || c2 == EOF )
+        pm_error( "EOF / read error" );
+      value2 = ( hex_table[c1] << 4 ) + hex_table[c2];
+      if ( value2 >= 256 )
+        pm_error( "syntax error" );
+      *ptr++ = value2;
+      if ( ( ! padding ) || ( ( bytes + 2 ) % bytes_per_line ) )
+        *ptr++ = value1;
+    }
+  else
+    for ( bytes = 0, ptr = *dataP; bytes < raster_length; ++bytes ) {
+      /*
+       ** Skip until digit is found.
+       */
+      for ( ; ; )
+        {
+          c1 = getc( stream );
+          if ( c1 == EOF )
+            pm_error( "EOF / read error" );
+          value1 = hex_table[c1];
+          if ( value1 != 256 )
+            break;
+        }
+      /*
+       ** Loop on digits.
+       */
+      for ( ; ; ) {
+        c2 = getc( stream );
+        if ( c2 == EOF )
+          pm_error( "EOF / read error" );
+        value2 = hex_table[c2];
+        if ( value2 != 256 ) {
+          value1 = (value1 << 4) | value2;
+          if ( value1 >= 256 )
+            pm_error( "syntax error" );
+        }
+        else if ( c2 == 'x' || c2 == 'X' )
+          if ( value1 == 0 )
+            continue;
+          else pm_error( "syntax error" );
+        else break;
+      }
+      *ptr++ = value1;
+    }
+}
+
+
+/*  CHANGE HISTORY:
+
+  99.09.08 bryanh    Recognize "static unsigned char" declaration.
+
+
+
+
+
+*/
diff --git a/converter/pbm/ybmtopbm.c b/converter/pbm/ybmtopbm.c
new file mode 100644
index 00000000..739e168a
--- /dev/null
+++ b/converter/pbm/ybmtopbm.c
@@ -0,0 +1,113 @@
+/* ybmtopbm.c - read a file from Bennet Yee's 'xbm' program and write a pbm.
+**
+** Written by Jamie Zawinski based on code (C) 1988 by Jef Poskanzer.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <stdio.h>
+#include "pbm.h"
+
+static void getinit ARGS(( FILE* file, short* colsP, short* rowsP, short* depthP, short* padrightP ));
+static bit getbit ARGS(( FILE* file ));
+
+#define YBM_MAGIC  ( ( '!' << 8 ) | '!' )
+
+int
+main( argc, argv )
+    int argc;
+    char* argv[];
+    {
+    FILE* ifp;
+    bit* bitrow;
+    register bit* bP;
+    short rows, cols, padright, row, col;
+    short depth;
+
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+	pm_usage( "[ybmfile]" );
+
+    if ( argc == 2 )
+	ifp = pm_openr( argv[1] );
+    else
+	ifp = stdin;
+
+    getinit( ifp, &cols, &rows, &depth, &padright );
+    if ( depth != 1 )
+	pm_error(
+	    "YBM file has depth of %d, must be 1",
+	    (int) depth );
+
+    pbm_writepbminit( stdout, cols, rows, 0 );
+    bitrow = pbm_allocrow( cols );
+
+    for ( row = 0; row < rows; ++row )
+	{
+	/* Get data. */
+        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
+	    *bP = getbit( ifp );
+	/* Discard line padding */
+        for ( col = 0; col < padright; ++col )
+	    (void) getbit( ifp );
+	pbm_writepbmrow( stdout, bitrow, cols, 0 );
+	}
+
+    pm_close( ifp );
+    pm_close( stdout );
+
+    exit( 0 );
+    }
+
+static int item;
+static int bitsperitem, bitshift;
+
+static void
+getinit( file, colsP, rowsP, depthP, padrightP )
+    FILE* file;
+    short* colsP;
+    short* rowsP;
+    short* depthP;
+    short* padrightP;
+    {
+    short magic;
+
+    if ( pm_readbigshort( file, &magic ) == -1 )
+	pm_error( "EOF / read error" );
+    if ( magic != YBM_MAGIC )
+	pm_error( "bad magic number in YBM file" );
+    if ( pm_readbigshort( file, colsP ) == -1 )
+	pm_error( "EOF / read error" );
+      if ( pm_readbigshort( file, rowsP ) == -1 )
+	pm_error( "EOF / read error" );
+
+    *depthP = 1;
+    *padrightP = ( ( *colsP + 15 ) / 16 ) * 16 - *colsP;
+    bitsperitem = 0;
+    }
+
+static bit
+getbit( file )
+    FILE* file;
+    {
+    bit b;
+
+    if ( bitsperitem == 0 )
+	{
+	item = getc(file) | getc(file)<<8;
+	if ( item == EOF )
+	    pm_error( "EOF / read error" );
+	bitsperitem = 16;
+	bitshift = 0;
+	}
+    b = ( ( item >> bitshift) & 1 ) ? PBM_BLACK : PBM_WHITE;
+    --bitsperitem;
+    ++bitshift;
+    return b;
+    }