about summary refs log tree commit diff
path: root/converter/other/pnmtopalm
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/other/pnmtopalm
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/other/pnmtopalm')
-rw-r--r--converter/other/pnmtopalm/LICENSE16
-rw-r--r--converter/other/pnmtopalm/Makefile35
-rw-r--r--converter/other/pnmtopalm/README57
-rw-r--r--converter/other/pnmtopalm/gen_palm_colormap.c24
-rw-r--r--converter/other/pnmtopalm/palm.h66
-rw-r--r--converter/other/pnmtopalm/palmcolor8.map235
-rw-r--r--converter/other/pnmtopalm/palmcolormap.c277
-rw-r--r--converter/other/pnmtopalm/palmgray1.map4
-rw-r--r--converter/other/pnmtopalm/palmgray2.map6
-rw-r--r--converter/other/pnmtopalm/palmgray4.map8
-rw-r--r--converter/other/pnmtopalm/palmtopnm.c1131
-rw-r--r--converter/other/pnmtopalm/pnmtopalm.c1235
12 files changed, 3094 insertions, 0 deletions
diff --git a/converter/other/pnmtopalm/LICENSE b/converter/other/pnmtopalm/LICENSE
new file mode 100644
index 00000000..740b6080
--- /dev/null
+++ b/converter/other/pnmtopalm/LICENSE
@@ -0,0 +1,16 @@
+LICENSE FOR PNMTOPALM SUBPACKAGE OF NETPBM.  THIS LICENSE APPLIES TO ALL
+THE MATERIALS IN THE PNMTOPALM DIRECTORY OF THE NETPBM SOURCE PACKAGE.
+
+Copyright 1995-2001 by Ian Goldberg, George Caswell, and 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.
+
+
+(Bryan Henderson extracted this license memorandum from the comments in the
+original man page nroff source supplied to him with the rest of the Pnmtopalm
+package in January 2001 by Bill Janssen).
diff --git a/converter/other/pnmtopalm/Makefile b/converter/other/pnmtopalm/Makefile
new file mode 100644
index 00000000..2a76297e
--- /dev/null
+++ b/converter/other/pnmtopalm/Makefile
@@ -0,0 +1,35 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/other/pnmtopalm
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/Makefile.config
+
+BINARIES = palmtopnm pnmtopalm
+SCRIPTS =
+OBJECTS = $(BINARIES:%=%.o) palmcolormap.o
+MERGE_OBJECTS = $(BINARIES:%=%.o2) palmcolormap.o
+MERGEBINARIES = $(BINARIES)
+DATAFILES = palmcolor8.map palmgray1.map palmgray2.map palmgray4.map
+
+all: $(BINARIES)
+
+include $(SRCDIR)/Makefile.common
+
+LIBOPTS = $(shell $(LIBOPT) $(NETPBMLIB))
+
+$(BINARIES): %: %.o palmcolormap.o $(NETPBMLIB) $(LIBOPT)
+	$(LD) $(LDFLAGS) -o $@ $< palmcolormap.o $(LIBOPTS) \
+	  $(MATHLIB) $(LDLIBS) $(RPATH) $(LADD)
+
+gen_palm_colormap : $(SUBDIR)/gen_palm_colormap.c palmcolormap.o
+	$(CC) $(INCLUDES) $(CFLAGS) $(LDFLAGS) -o $@ $< palmcolormap.o \
+	  $(LIBOPTS) $(MATHLIB) $(LDLIBS) $(LADD)
+
+
+clean: cleanspecial
+.PHONY: cleanspecial
+cleanspecial:
+	rm -f gen_palm_colormap
diff --git a/converter/other/pnmtopalm/README b/converter/other/pnmtopalm/README
new file mode 100644
index 00000000..ebae9492
--- /dev/null
+++ b/converter/other/pnmtopalm/README
@@ -0,0 +1,57 @@
+This is version 1.0 of pnmpalm, netpbm converters for Palm pixmaps.
+
+It's derived from version 1.0 of ppmtoTbmp.  The original 'ppmtoTbmp'
+program has been renamed 'pnmtopalm', and has been rewritten to handle
+the various bitmaps that exist as of PalmOS 3.5, and to handle
+colormaps, a transparent color, and the various compression formats
+recognized by Palm OS.  The 'Tbmptopnm' program is now 'palmtopnm',
+and has been updated to handle the various newer bitmap formats as
+well.  Man pages for 'pnmtopalm' and 'palmtopnm' have been added.  See
+those man pages for more information.  Note that command-line switches
+have changed.
+
+As with Tbmptoppm, ppm images must be quantified properly before
+being passed to pnmtopalm.  Several colormap files are provided to
+use for this:
+ - palmgray1.map -- 1-bit grayscale (i.e. monochrome)
+ - palmgray2.map -- 2-bit grayscale
+ - palmgray4.map -- 4-bit grayscale
+ - palmcolor8.map -- 8-bit color, using the default color palette
+
+-- Bill Janssen  <bill@janssen.org>
+
+
+One source of information on Palm image formats is
+http://www.kawt.de/doc/palmimages.html.
+
+
+
+The original ppmtoTbmp README read as follows:
+
+This is version 1.0 of ppmtoTbmp, a ppm to Pilot bitmap converter.
+
+To compile it, you'll need the netpbm package.  Last I checked, it was
+available at "ftp://ftp.x.org/contrib/utilities/netpbm-1mar1994.p1.tar.gz".
+Change the Makefile to point to the directory that contains the netpbm
+header files, and make.
+
+Usage: ppmtoTbmp [-2bit] [file.ppm]
+
+If the ppm file is not specified, one is read from stdin.  The -2bit option
+produces a 2-bit bitmap instead of the normal 1-bit bitmap.  The ppm must
+have at most 4 colors in it (for 2-bit) or at most 2 colors (for 1-bit).
+Common invokations might be:
+
+xbmtopbm icon.xbm | ppmquant -fs -map q2.map | ppmtoTbmp > tAIB03e8.bin
+giftopnm image.gif | ppmquant -fs -map q4.map | ppmtoTbmp -2bit > Tbmp0bb8.bin
+
+(q2.map and q4.map are trivial 2 and 4 color maps, respectively, and are
+included in this distribution.)
+
+To include the resulting bitmap in a .prc file, just name the output
+tAIB03e8.bin (for an application icon) or Tbmpxxxx.bin (for a form bitmap,
+where xxxx is replaced by four hex digits giving the bitmap ID).
+
+I'll probably make Tbmptopnm later, and put it in the 1.1 release.
+
+   - Ian Goldberg <iang@cs.berkeley.edu>
diff --git a/converter/other/pnmtopalm/gen_palm_colormap.c b/converter/other/pnmtopalm/gen_palm_colormap.c
new file mode 100644
index 00000000..4b65e631
--- /dev/null
+++ b/converter/other/pnmtopalm/gen_palm_colormap.c
@@ -0,0 +1,24 @@
+/* gen_palm_colormap.c - generate a ppm file containing the default Palm colormap
+ *
+ * Bill Janssen  <bill@janssen.org>
+ */
+
+#include "pnm.h"
+
+#include "palm.h"
+
+int main( int argc, char **argv ) {
+
+  int i;
+  Color_s current;
+  Colormap default_map = palmcolor_build_default_8bit_colormap ();
+
+  printf("P3\n%d 1\n255\n", default_map->ncolors);
+  for (i = 0;  i < default_map->ncolors;  i++) {
+    current = default_map->color_entries[i];
+    printf ("%d %d %d\n", (current & 0xFF0000) >> 16, (current & 0xFF00) >> 8, (current & 0xFF));
+    /* printf ("%x:  %d %d %d\n", (current & 0xFF000000) >> 24, (current & 0xFF0000) >> 16, (current & 0xFF00) >> 8, (current & 0xFF)); */
+  };
+  return 0;
+}
+
diff --git a/converter/other/pnmtopalm/palm.h b/converter/other/pnmtopalm/palm.h
new file mode 100644
index 00000000..170c8cec
--- /dev/null
+++ b/converter/other/pnmtopalm/palm.h
@@ -0,0 +1,66 @@
+#ifndef PALM_H_INCLUDED
+#define PALM_H_INCLUDED
+
+#define PALM_IS_COMPRESSED_FLAG     0x8000
+#define PALM_HAS_COLORMAP_FLAG      0x4000
+#define PALM_HAS_TRANSPARENCY_FLAG  0x2000
+#define PALM_INDIRECT_BITMAP        0x1000  /* Palm says internal use only */
+#define PALM_FOR_SCREEN             0x0800  /* Palm says internal use only */
+#define PALM_DIRECT_COLOR_FLAG      0x0400
+#define PALM_INDIRECT_COLORMAP      0x0200  /* Palm says internal use only */ 
+#define PALM_NO_DITHER_FLAG         0x0100  /* rather mysterious */
+
+#define PALM_COMPRESSION_SCANLINE   0x00
+#define PALM_COMPRESSION_RLE        0x01
+#define PALM_COMPRESSION_PACKBITS   0x02
+#define PALM_COMPRESSION_END        0x03    /* Palm says internal use only */
+#define PALM_COMPRESSION_BEST       0x64    /* Palm says internal use only */
+#define PALM_COMPRESSION_NONE       0xFF    /* Palm says internal use only */
+
+#define PALM_DENSITY_LOW             72
+#define PALM_DENSITY_ONEANDAHALF    108
+#define PALM_DENSITY_DOUBLE         144
+#define PALM_DENSITY_TRIPLE         216
+#define PALM_DENSITY_QUADRUPLE      288
+
+#define PALM_FORMAT_INDEXED     0x00
+#define PALM_FORMAT_565         0x01
+#define PALM_FORMAT_565LE       0x02    /* Palm says internal use only */
+#define PALM_FORMAT_INDEXEDLE   0x03    /* Palm says internal use only */
+
+typedef unsigned long Color_s;
+
+typedef Color_s * Color;
+
+typedef struct {
+    unsigned int nentries;
+        /* number of allocated entries in 'color_entries' */
+    unsigned int ncolors;
+        /* number of colors actually in 'color_entries' -- entries are
+           filled from 0 consecutively, one color per entry.
+        */
+    Color_s * color_entries;  /* Array of colors */
+} Colormap_s;
+
+typedef Colormap_s * Colormap;
+
+int
+palmcolor_compare_indices(const void * const p1,
+                          const void * const p2);
+
+int
+palmcolor_compare_colors(const void * const p1,
+                         const void * const p2);
+
+Colormap
+palmcolor_build_custom_8bit_colormap(unsigned int const rows,
+                                     unsigned int const cols,
+                                     pixel **     const pixels);
+
+Colormap
+palmcolor_build_default_8bit_colormap(void);
+
+Colormap
+palmcolor_read_colormap (FILE * const ifP);
+
+#endif
diff --git a/converter/other/pnmtopalm/palmcolor8.map b/converter/other/pnmtopalm/palmcolor8.map
new file mode 100644
index 00000000..2e054616
--- /dev/null
+++ b/converter/other/pnmtopalm/palmcolor8.map
@@ -0,0 +1,235 @@
+P3
+232 1
+255
+0 0 0
+0 0 0
+0 0 51
+0 0 102
+0 0 153
+0 0 204
+0 0 255
+0 51 0
+0 51 51
+0 51 102
+0 51 153
+0 51 204
+0 51 255
+0 102 0
+0 102 51
+0 102 102
+0 102 153
+0 102 204
+0 102 255
+0 128 0
+0 128 128
+0 153 0
+0 153 51
+0 153 102
+0 153 153
+0 153 204
+0 153 255
+0 204 0
+0 204 51
+0 204 102
+0 204 153
+0 204 204
+0 204 255
+0 255 0
+0 255 51
+0 255 102
+0 255 153
+0 255 204
+0 255 255
+17 17 17
+34 34 34
+51 0 0
+51 0 51
+51 0 102
+51 0 153
+51 0 204
+51 0 255
+51 51 0
+51 51 51
+51 51 102
+51 51 153
+51 51 204
+51 51 255
+51 102 0
+51 102 51
+51 102 102
+51 102 153
+51 102 204
+51 102 255
+51 153 0
+51 153 51
+51 153 102
+51 153 153
+51 153 204
+51 153 255
+51 204 0
+51 204 51
+51 204 102
+51 204 153
+51 204 204
+51 204 255
+51 255 0
+51 255 51
+51 255 102
+51 255 153
+51 255 204
+51 255 255
+68 68 68
+85 85 85
+102 0 0
+102 0 51
+102 0 102
+102 0 153
+102 0 204
+102 0 255
+102 51 0
+102 51 51
+102 51 102
+102 51 153
+102 51 204
+102 51 255
+102 102 0
+102 102 51
+102 102 102
+102 102 153
+102 102 204
+102 102 255
+102 153 0
+102 153 51
+102 153 102
+102 153 153
+102 153 204
+102 153 255
+102 204 0
+102 204 51
+102 204 102
+102 204 153
+102 204 204
+102 204 255
+102 255 0
+102 255 51
+102 255 102
+102 255 153
+102 255 204
+102 255 255
+119 119 119
+128 0 0
+128 0 128
+136 136 136
+153 0 0
+153 0 51
+153 0 102
+153 0 153
+153 0 204
+153 0 255
+153 51 0
+153 51 51
+153 51 102
+153 51 153
+153 51 204
+153 51 255
+153 102 0
+153 102 51
+153 102 102
+153 102 153
+153 102 204
+153 102 255
+153 153 0
+153 153 51
+153 153 102
+153 153 153
+153 153 204
+153 153 255
+153 204 0
+153 204 51
+153 204 102
+153 204 153
+153 204 204
+153 204 255
+153 255 0
+153 255 51
+153 255 102
+153 255 153
+153 255 204
+153 255 255
+170 170 170
+187 187 187
+192 192 192
+204 0 0
+204 0 51
+204 0 102
+204 0 153
+204 0 204
+204 0 255
+204 51 0
+204 51 51
+204 51 102
+204 51 153
+204 51 204
+204 51 255
+204 102 0
+204 102 51
+204 102 102
+204 102 153
+204 102 204
+204 102 255
+204 153 0
+204 153 51
+204 153 102
+204 153 153
+204 153 204
+204 153 255
+204 204 0
+204 204 51
+204 204 102
+204 204 153
+204 204 204
+204 204 255
+204 255 0
+204 255 51
+204 255 102
+204 255 153
+204 255 204
+204 255 255
+221 221 221
+238 238 238
+255 0 0
+255 0 51
+255 0 102
+255 0 153
+255 0 204
+255 0 255
+255 51 0
+255 51 51
+255 51 102
+255 51 153
+255 51 204
+255 51 255
+255 102 0
+255 102 51
+255 102 102
+255 102 153
+255 102 204
+255 102 255
+255 153 0
+255 153 51
+255 153 102
+255 153 153
+255 153 204
+255 153 255
+255 204 0
+255 204 51
+255 204 102
+255 204 153
+255 204 204
+255 204 255
+255 255 0
+255 255 51
+255 255 102
+255 255 153
+255 255 204
+255 255 255
diff --git a/converter/other/pnmtopalm/palmcolormap.c b/converter/other/pnmtopalm/palmcolormap.c
new file mode 100644
index 00000000..a1a1cec1
--- /dev/null
+++ b/converter/other/pnmtopalm/palmcolormap.c
@@ -0,0 +1,277 @@
+/* See LICENSE file for licensing information.
+*/
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "pnm.h"
+
+#include "palm.h"
+
+int
+palmcolor_compare_indices(const void * const p1,
+                          const void * const p2) {
+
+    if ((*((Color) p1) & 0xFF000000) < (*((Color) p2) & 0xFF000000))
+        return -1;
+    else if ((*((Color) p1) & 0xFF000000) > (*((Color) p2) & 0xFF000000))
+        return 1;
+    else
+        return 0;
+}
+
+
+
+int
+palmcolor_compare_colors(const void * const p1,
+                         const void * const p2) {
+
+    unsigned long const val1 = *((const unsigned long *) p1) & 0xFFFFFF;
+    unsigned long const val2 = *((const unsigned long *) p2) & 0xFFFFFF;
+
+    if (val1 < val2)
+        return -1;
+    else if (val1 > val2)
+        return 1;
+    else
+        return 0;
+}
+
+/***********************************************************************
+ ***********************************************************************
+ ***********************************************************************
+ ******* colortables from pilrc-2.6/bitmap.c ***************************
+ ***********************************************************************
+ ***********************************************************************
+ ***********************************************************************/
+
+#if 0
+
+/*
+ * The 1bit-2 color system palette for Palm Computing Devices.
+ */
+static int PalmPalette1bpp[2][3] = 
+{
+  { 255, 255, 255}, {   0,   0,   0 }
+};
+
+/*
+ * The 2bit-4 color system palette for Palm Computing Devices.
+ */
+static int PalmPalette2bpp[4][3] = 
+{
+  { 255, 255, 255}, { 192, 192, 192}, { 128, 128, 128 }, {   0,   0,   0 }
+};
+
+/*
+ * The 4bit-16 color system palette for Palm Computing Devices.
+ */
+static int PalmPalette4bpp[16][3] = 
+{
+  { 255, 255, 255}, { 238, 238, 238 }, { 221, 221, 221 }, { 204, 204, 204 },
+  { 187, 187, 187}, { 170, 170, 170 }, { 153, 153, 153 }, { 136, 136, 136 },
+  { 119, 119, 119}, { 102, 102, 102 }, {  85,  85,  85 }, {  68,  68,  68 },
+  {  51,  51,  51}, {  34,  34,  34 }, {  17,  17,  17 }, {   0,   0,   0 }
+};
+
+/*
+ * The 4bit-16 color system palette for Palm Computing Devices.
+ */
+static int PalmPalette4bppColor[16][3] = 
+{
+  { 255, 255, 255}, { 128, 128, 128 }, { 128,   0,   0 }, { 128, 128,   0 },
+  {   0, 128,   0}, {   0, 128, 128 }, {   0,   0, 128 }, { 128,   0, 128 },
+  { 255,   0, 255}, { 192, 192, 192 }, { 255,   0,   0 }, { 255, 255,   0 },
+  {   0, 255,   0}, {   0, 255, 255 }, {   0,   0, 255 }, {   0,   0,   0 }
+};
+
+#endif  /* 0 */
+
+/*
+ * The 8bit-256 color system palette for Palm Computing Devices.
+ *
+ * NOTE:  only the first 231, plus the last one, are valid.
+ */
+static int PalmPalette8bpp[256][3] = 
+{
+  { 255, 255, 255 }, { 255, 204, 255 }, { 255, 153, 255 }, { 255, 102, 255 }, 
+  { 255,  51, 255 }, { 255,   0, 255 }, { 255, 255, 204 }, { 255, 204, 204 }, 
+  { 255, 153, 204 }, { 255, 102, 204 }, { 255,  51, 204 }, { 255,   0, 204 }, 
+  { 255, 255, 153 }, { 255, 204, 153 }, { 255, 153, 153 }, { 255, 102, 153 }, 
+  { 255,  51, 153 }, { 255,   0, 153 }, { 204, 255, 255 }, { 204, 204, 255 },
+  { 204, 153, 255 }, { 204, 102, 255 }, { 204,  51, 255 }, { 204,   0, 255 },
+  { 204, 255, 204 }, { 204, 204, 204 }, { 204, 153, 204 }, { 204, 102, 204 },
+  { 204,  51, 204 }, { 204,   0, 204 }, { 204, 255, 153 }, { 204, 204, 153 },
+  { 204, 153, 153 }, { 204, 102, 153 }, { 204,  51, 153 }, { 204,   0, 153 },
+  { 153, 255, 255 }, { 153, 204, 255 }, { 153, 153, 255 }, { 153, 102, 255 },
+  { 153,  51, 255 }, { 153,   0, 255 }, { 153, 255, 204 }, { 153, 204, 204 },
+  { 153, 153, 204 }, { 153, 102, 204 }, { 153,  51, 204 }, { 153,   0, 204 },
+  { 153, 255, 153 }, { 153, 204, 153 }, { 153, 153, 153 }, { 153, 102, 153 },
+  { 153,  51, 153 }, { 153,   0, 153 }, { 102, 255, 255 }, { 102, 204, 255 },
+  { 102, 153, 255 }, { 102, 102, 255 }, { 102,  51, 255 }, { 102,   0, 255 },
+  { 102, 255, 204 }, { 102, 204, 204 }, { 102, 153, 204 }, { 102, 102, 204 },
+  { 102,  51, 204 }, { 102,   0, 204 }, { 102, 255, 153 }, { 102, 204, 153 },
+  { 102, 153, 153 }, { 102, 102, 153 }, { 102,  51, 153 }, { 102,   0, 153 },
+  {  51, 255, 255 }, {  51, 204, 255 }, {  51, 153, 255 }, {  51, 102, 255 },
+  {  51,  51, 255 }, {  51,   0, 255 }, {  51, 255, 204 }, {  51, 204, 204 },
+  {  51, 153, 204 }, {  51, 102, 204 }, {  51,  51, 204 }, {  51,   0, 204 },
+  {  51, 255, 153 }, {  51, 204, 153 }, {  51, 153, 153 }, {  51, 102, 153 },
+  {  51,  51, 153 }, {  51,   0, 153 }, {   0, 255, 255 }, {   0, 204, 255 },
+  {   0, 153, 255 }, {   0, 102, 255 }, {   0,  51, 255 }, {   0,   0, 255 },
+  {   0, 255, 204 }, {   0, 204, 204 }, {   0, 153, 204 }, {   0, 102, 204 },
+  {   0,  51, 204 }, {   0,   0, 204 }, {   0, 255, 153 }, {   0, 204, 153 },
+  {   0, 153, 153 }, {   0, 102, 153 }, {   0,  51, 153 }, {   0,   0, 153 },
+  { 255, 255, 102 }, { 255, 204, 102 }, { 255, 153, 102 }, { 255, 102, 102 },
+  { 255,  51, 102 }, { 255,   0, 102 }, { 255, 255,  51 }, { 255, 204,  51 },
+  { 255, 153,  51 }, { 255, 102,  51 }, { 255,  51,  51 }, { 255,   0,  51 },
+  { 255, 255,   0 }, { 255, 204,   0 }, { 255, 153,   0 }, { 255, 102,   0 },
+  { 255,  51,   0 }, { 255,   0,   0 }, { 204, 255, 102 }, { 204, 204, 102 },
+  { 204, 153, 102 }, { 204, 102, 102 }, { 204,  51, 102 }, { 204,   0, 102 },
+  { 204, 255,  51 }, { 204, 204,  51 }, { 204, 153,  51 }, { 204, 102,  51 },
+  { 204,  51,  51 }, { 204,   0,  51 }, { 204, 255,   0 }, { 204, 204,   0 },
+  { 204, 153,   0 }, { 204, 102,   0 }, { 204,  51,   0 }, { 204,   0,   0 },
+  { 153, 255, 102 }, { 153, 204, 102 }, { 153, 153, 102 }, { 153, 102, 102 },
+  { 153,  51, 102 }, { 153,   0, 102 }, { 153, 255,  51 }, { 153, 204,  51 },
+  { 153, 153,  51 }, { 153, 102,  51 }, { 153,  51,  51 }, { 153,   0,  51 },
+  { 153, 255,   0 }, { 153, 204,   0 }, { 153, 153,   0 }, { 153, 102,   0 },
+  { 153,  51,   0 }, { 153,   0,   0 }, { 102, 255, 102 }, { 102, 204, 102 },
+  { 102, 153, 102 }, { 102, 102, 102 }, { 102,  51, 102 }, { 102,   0, 102 },
+  { 102, 255,  51 }, { 102, 204,  51 }, { 102, 153,  51 }, { 102, 102,  51 },
+  { 102,  51,  51 }, { 102,   0,  51 }, { 102, 255,   0 }, { 102, 204,   0 },
+  { 102, 153,   0 }, { 102, 102,   0 }, { 102,  51,   0 }, { 102,   0,   0 },
+  {  51, 255, 102 }, {  51, 204, 102 }, {  51, 153, 102 }, {  51, 102, 102 },
+  {  51,  51, 102 }, {  51,   0, 102 }, {  51, 255,  51 }, {  51, 204,  51 },
+  {  51, 153,  51 }, {  51, 102,  51 }, {  51,  51,  51 }, {  51,   0,  51 },
+  {  51, 255,   0 }, {  51, 204,   0 }, {  51, 153,   0 }, {  51, 102,   0 },
+  {  51,  51,   0 }, {  51,   0,   0 }, {   0, 255, 102 }, {   0, 204, 102 },
+  {   0, 153, 102 }, {   0, 102, 102 }, {   0,  51, 102 }, {   0,   0, 102 },
+  {   0, 255,  51 }, {   0, 204,  51 }, {   0, 153,  51 }, {   0, 102,  51 },
+  {   0,  51,  51 }, {   0,   0,  51 }, {   0, 255,   0 }, {   0, 204,   0 },
+  {   0, 153,   0 }, {   0, 102,   0 }, {   0,  51,   0 }, {  17,  17,  17 },
+  {  34,  34,  34 }, {  68,  68,  68 }, {  85,  85,  85 }, { 119, 119, 119 },
+  { 136, 136, 136 }, { 170, 170, 170 }, { 187, 187, 187 }, { 221, 221, 221 },
+  { 238, 238, 238 }, { 192, 192, 192 }, { 128,   0,   0 }, { 128,   0, 128 },
+  {   0, 128,   0 }, {   0, 128, 128 }, {   0,   0,   0 }, {   0,   0,   0 },
+  {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 },
+  {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 },
+  {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 },
+  {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 },
+  {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 },
+  {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 }, {   0,   0,   0 }
+};
+
+
+
+Colormap
+palmcolor_build_default_8bit_colormap(void) {
+
+    unsigned int i;
+
+    Colormap cm;
+
+    MALLOCVAR_NOFAIL(cm);
+    cm->nentries = 232;
+    MALLOCARRAY_NOFAIL(cm->color_entries, cm->nentries);
+
+    /* Fill in the colors */
+    for (i = 0; i < 231; ++i) {
+        cm->color_entries[i] = ((i << 24) |
+                                (PalmPalette8bpp[i][0] << 16) |
+                                (PalmPalette8bpp[i][1] << 8) |
+                                (PalmPalette8bpp[i][2]));
+    }
+    cm->color_entries[231] = 0xFF000000;
+    cm->ncolors = 232;
+
+    /* now sort the table */
+    qsort (cm->color_entries, cm->ncolors, sizeof(Color_s), 
+           palmcolor_compare_colors);
+    return cm;
+}
+
+
+
+Colormap
+palmcolor_build_custom_8bit_colormap(unsigned int const rows,
+                                     unsigned int const cols,
+                                     pixel **     const pixels) {
+    unsigned int row;
+    Colormap colormap;
+
+    MALLOCVAR_NOFAIL(colormap);
+    colormap->nentries = 256;
+    MALLOCARRAY_NOFAIL(colormap->color_entries, colormap->nentries);
+    colormap->ncolors = 0;  /* initial value */
+    
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            Color found;
+            Color_s temp;
+
+            temp = ((PPM_GETR(pixels[row][col]) << 16) |
+                    (PPM_GETG(pixels[row][col]) << 8) |
+                    PPM_GETB(pixels[row][col]));
+            found = (bsearch (&temp,
+                              colormap->color_entries, colormap->ncolors,
+                              sizeof(Color_s), palmcolor_compare_colors));
+            if (!found) {
+                if (colormap->ncolors >= colormap->nentries)
+                    pm_error("Too many colors for custom colormap "
+                             "(max 256).  "
+                             "Try using pnmquant to reduce the number "
+                             "of colors.");
+                else {
+                    /* add the new color, and re-sort */
+                    temp |= ((colormap->ncolors) << 24);
+                    colormap->color_entries[colormap->ncolors] = temp;
+                    colormap->ncolors += 1;
+                    qsort(colormap->color_entries, colormap->ncolors, 
+                          sizeof(Color_s), palmcolor_compare_colors);
+                }
+            }
+        }
+    }
+    return colormap;
+}
+
+
+
+Colormap
+palmcolor_read_colormap (FILE * const ifP) {
+
+    unsigned short ncolors;
+    Colormap retval;
+    int rc;
+    
+    rc = pm_readbigshort(ifP, (short *) &ncolors);
+    if (rc != 0)
+        retval = NULL;
+    else {
+        long colorentry;
+        Colormap colormap;
+        unsigned int i;
+        bool error;
+
+        MALLOCVAR_NOFAIL(colormap);
+        colormap->nentries = ncolors;
+        MALLOCARRAY_NOFAIL(colormap->color_entries, colormap->nentries);
+        
+        for (i = 0, error = FALSE;  i < ncolors && !error;  ++i) {
+            int rc;
+            rc = pm_readbiglong(ifP, &colorentry);
+            if (rc != 0)
+                error = TRUE;
+            else
+                colormap->color_entries[i] = (colorentry & 0xFFFFFFFF);
+        }
+        if (error) {
+            free (colormap->color_entries);
+            free (colormap);
+            retval = NULL;
+        } else {
+            colormap->ncolors = ncolors;
+            retval = colormap;
+        }
+    }
+    return retval;
+}
diff --git a/converter/other/pnmtopalm/palmgray1.map b/converter/other/pnmtopalm/palmgray1.map
new file mode 100644
index 00000000..96e20400
--- /dev/null
+++ b/converter/other/pnmtopalm/palmgray1.map
@@ -0,0 +1,4 @@
+P1
+# Black and white palette
+1 2
+0 1
diff --git a/converter/other/pnmtopalm/palmgray2.map b/converter/other/pnmtopalm/palmgray2.map
new file mode 100644
index 00000000..e8c95b01
--- /dev/null
+++ b/converter/other/pnmtopalm/palmgray2.map
@@ -0,0 +1,6 @@
+P2
+# 4 level grayscale palette
+2 2
+3
+0 1
+2 3
diff --git a/converter/other/pnmtopalm/palmgray4.map b/converter/other/pnmtopalm/palmgray4.map
new file mode 100644
index 00000000..bba2e6b8
--- /dev/null
+++ b/converter/other/pnmtopalm/palmgray4.map
@@ -0,0 +1,8 @@
+P2
+# 16 level grayscale palette
+4 4
+15
+ 0  1  2  3
+ 4  5  6  7
+ 8  9 10 11
+12 13 14 15
diff --git a/converter/other/pnmtopalm/palmtopnm.c b/converter/other/pnmtopalm/palmtopnm.c
new file mode 100644
index 00000000..9cd695e3
--- /dev/null
+++ b/converter/other/pnmtopalm/palmtopnm.c
@@ -0,0 +1,1131 @@
+/******************************************************************************
+                             palmtopnm
+*******************************************************************************
+  By Bryan Henderson, San Jose, California, June 2004.
+
+  Inspired by and using methods from Tbmptopnm by Ian Goldberg
+  <iang@cs.berkeley.edu>, and Bill Janssen <bill@janssen.org>.
+
+  Major fixes and new capability added by Paul Bolle <pebolle@tiscali.nl>
+  in late 2004 / early 2005.
+
+  Bryan's work is contributed to the public domain by its author.
+******************************************************************************/
+
+#include <string.h>
+#include <assert.h>
+
+#include "pnm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+#include "palm.h"
+
+
+
+enum palmCompressionType {
+    COMPRESSION_NONE, 
+    COMPRESSION_RLE, 
+    COMPRESSION_SCANLINE,
+    COMPRESSION_PACKBITS
+};
+
+struct palmHeader {
+    unsigned short cols;
+    unsigned short rows;
+    unsigned short bytesPerRow;
+    unsigned short flags;
+    bool           directColor;
+        /* The header indicates a direct color raster, either by flag
+           (the old way) or by pixel format (the new way)
+        */
+    bool           hasColormap;
+    bool           hasTransparency;
+    unsigned char  pixelSizeCode;
+    unsigned int   pixelSize;
+    unsigned char  version;
+    unsigned int   transparentIndex;
+    enum palmCompressionType compressionType;
+    /* version 3 encoding specific */
+    unsigned char  size;
+    unsigned char  pixelFormat;
+    unsigned short density;
+    unsigned long  transparentValue;
+};
+
+
+
+struct directPixelFormat {
+    unsigned int redbits;
+    unsigned int greenbits;
+    unsigned int bluebits;
+};
+
+
+
+struct directColorInfo {
+    struct directPixelFormat pixelFormat;
+    Color_s                  transparentColor;
+};
+
+
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFilespec;
+    unsigned int verbose;
+    unsigned int rendition;
+    unsigned int showhist;
+    unsigned int transparent;
+};
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo *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 renditionSpec;
+
+    unsigned int option_def_index;
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "verbose",     OPT_FLAG, NULL,
+            &cmdlineP->verbose,  0);
+    OPTENT3(0, "showhist",    OPT_FLAG, NULL,
+            &cmdlineP->showhist, 0);
+    OPTENT3(0, "transparent",    OPT_FLAG, NULL,
+            &cmdlineP->transparent, 0);
+    OPTENT3(0, "rendition",  OPT_UINT, &cmdlineP->rendition, 
+            &renditionSpec, 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 (renditionSpec) {
+        if (cmdlineP->rendition < 1)
+            pm_error("The -rendition value must be at least 1");
+    } else 
+        cmdlineP->rendition = 1;
+    
+    if (cmdlineP->transparent && cmdlineP->showhist)
+        pm_error("You can't specify -showhist with -transparent");
+
+    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 xelval *
+createGraymap(unsigned int const ncolors, 
+              xelval       const maxval) {
+    int i;
+    xelval *map;
+
+    MALLOCARRAY_NOFAIL(map, ncolors);
+    for (i = 0; i < ncolors; ++i) {
+        map[i] = maxval - (i * maxval) / (ncolors - 1);
+    }
+    return map;
+}
+
+
+
+static void 
+skipbytes(FILE *       const ifP, 
+          unsigned int const nbytes) {
+
+    unsigned char buf[256];
+    unsigned int n;
+    size_t bytesRead;
+
+    n = nbytes;  /* initial value */
+
+    while (n > 0) {
+        if (n > sizeof(buf)) {
+            bytesRead = fread(buf, sizeof(char), sizeof(buf), ifP);
+            if (bytesRead != sizeof(buf))
+               pm_error("Error reading Palm file.  Short read.");
+            n -= sizeof(buf);
+        } else {
+            bytesRead = fread(buf, sizeof(char), n, ifP);
+            if (bytesRead != n)
+               pm_error("Error reading Palm file.  Short read.");
+            n = 0;
+        }
+    }    
+}
+
+
+
+static void
+interpretCompression(unsigned char              const compressionValue,
+                     enum palmCompressionType * const compressionTypeP) {
+    
+    switch (compressionValue) {
+    case PALM_COMPRESSION_RLE:
+        *compressionTypeP = COMPRESSION_RLE;
+        break;
+    case PALM_COMPRESSION_SCANLINE:
+        *compressionTypeP = COMPRESSION_SCANLINE;
+        break;
+    case PALM_COMPRESSION_PACKBITS:
+        *compressionTypeP = COMPRESSION_PACKBITS;
+        break;
+    case PALM_COMPRESSION_NONE:
+        /* according to the spec this is not possible */
+        *compressionTypeP = COMPRESSION_NONE;
+        break;
+    default:
+        pm_error("The Palm image header has an unrecognized value for "
+                 "compression type: 0x%02x", (unsigned)compressionValue);
+    }
+}
+
+
+
+static void
+readRestOfHeaderVersion3(FILE *           const ifP,
+                         unsigned int     const pixelSize,
+                         unsigned char *  const sizeP,
+                         unsigned char *  const pixelFormatP,
+                         unsigned char *  const compressionTypeP,
+                         short *          const densityP,
+                         unsigned int *   const transparentIndexP,
+                         long *           const transparentValueP,
+                         long *           const nextBitmapOffsetP,
+                         short *          const nextDepthOffsetP) {
+
+    unsigned char unused;
+    
+    pm_readcharu(ifP, sizeP);
+    /* should be 0x18, but I can't see why we should really care */
+    if (*sizeP != 0x18)
+        pm_message("Strange value for Palm bitmap header size: %hu", *sizeP);
+
+    pm_readcharu(ifP, pixelFormatP);
+    if (*pixelFormatP != PALM_FORMAT_INDEXED &&
+        *pixelFormatP != PALM_FORMAT_565)
+        pm_error("Unrecognized pixelformat type: %u", *pixelFormatP);
+    
+    pm_readcharu(ifP, &unused);
+
+
+    pm_readcharu(ifP, compressionTypeP);
+
+    pm_readbigshort(ifP, densityP);
+    /* the specs imply that 0x00 is not valid */
+    if (*densityP != PALM_DENSITY_LOW &&
+        *densityP != PALM_DENSITY_ONEANDAHALF &&
+        *densityP != PALM_DENSITY_DOUBLE &&
+        *densityP != PALM_DENSITY_TRIPLE &&
+        *densityP != PALM_DENSITY_QUADRUPLE)
+        pm_error("Invalid value for -density: %d.", *densityP);
+ 
+    pm_readbiglong(ifP, transparentValueP);
+    if (pixelSize < 16)
+        *transparentIndexP = *transparentValueP;
+    else
+        *transparentIndexP = 0;
+ 
+    pm_readbiglong(ifP, nextBitmapOffsetP);
+    
+    /* version < 3 specific */
+    *nextDepthOffsetP = 0;
+}
+
+
+
+static void
+readRestOfHeaderOld(FILE *           const ifP,
+                    unsigned char *  const sizeP,
+                    unsigned char *  const pixelFormatP,
+                    unsigned char *  const compressionTypeP,
+                    short *          const densityP,
+                    unsigned int *   const transparentIndexP,
+                    long *           const transparentValueP,
+                    long *           const nextBitmapOffsetP,
+                    short *          const nextDepthOffsetP) {
+    
+    short pad;
+    unsigned char transparentIndex;
+    
+    pm_readbigshort(ifP, nextDepthOffsetP);
+    pm_readcharu(ifP, &transparentIndex);
+    *transparentIndexP = transparentIndex;
+ 
+    pm_readcharu(ifP,compressionTypeP);
+    
+    pm_readbigshort(ifP, &pad); /* reserved by Palm as of 8/9/00 */
+    
+    /* version 3 specific */
+    *sizeP = 0;
+    *pixelFormatP = 0;
+    *densityP = 0;
+    *transparentValueP = 0;
+    *nextBitmapOffsetP = 0;
+}
+
+
+
+static void
+interpretHeader(struct palmHeader * const palmHeaderP,
+                short               const cols,
+                short               const rows,
+                short               const bytesPerRow,
+                short               const flags,
+                unsigned char       const pixelSizeCode,
+                unsigned int        const pixelSize,
+                unsigned char       const version,
+                unsigned char       const size,
+                unsigned char       const pixelFormat,
+                short               const density,
+                long                const transparentValue,
+                unsigned int        const transparentIndex,
+                unsigned char       const compressionType) {
+    
+    palmHeaderP->cols = cols;
+    palmHeaderP->rows = rows;
+    palmHeaderP->bytesPerRow = bytesPerRow;
+    palmHeaderP->flags = flags;  /* Just for diagnostics */
+    palmHeaderP->hasColormap = !!(flags & PALM_HAS_COLORMAP_FLAG);
+    palmHeaderP->hasTransparency = !!(flags & PALM_HAS_TRANSPARENCY_FLAG);
+    palmHeaderP->pixelSizeCode = pixelSizeCode;
+    palmHeaderP->pixelSize = pixelSize;
+    palmHeaderP->version = version;
+    palmHeaderP->size = size;
+    palmHeaderP->pixelFormat = pixelFormat;
+    palmHeaderP->density = density;
+    palmHeaderP->transparentValue = transparentValue;
+    palmHeaderP->transparentIndex = transparentIndex;
+
+    if (palmHeaderP->version == 3 && (flags & PALM_DIRECT_COLOR_FLAG))
+        /* There's no directColorInfoType section in a version 3 Palm Bitmap */
+        pm_error("PALM_DIRECT_COLOR_FLAG is not valid for version 3 "
+                 "encoding type.");
+        
+    palmHeaderP->directColor = ((flags & PALM_DIRECT_COLOR_FLAG) || 
+                                palmHeaderP->pixelFormat == PALM_FORMAT_565);
+    
+    if (flags & PALM_IS_COMPRESSED_FLAG) 
+        interpretCompression(compressionType,
+                             &palmHeaderP->compressionType);
+    else
+        palmHeaderP->compressionType = COMPRESSION_NONE;
+}
+
+
+
+static void
+readHeader(FILE *              const ifP,
+           unsigned int        const requestedRendition,
+           struct palmHeader * const palmHeaderP) {
+/*----------------------------------------------------------------------------
+   Read the Palm Bitmap header from the file 'ifP'.  Read past all
+   renditions up to 'requestedRendition' and read the header of that
+   rendition.  Return the information contained in the header as *palmHeaderP.
+-----------------------------------------------------------------------------*/
+    bool gotHeader;
+    unsigned int currentRendition;
+
+    gotHeader = FALSE;
+    currentRendition = 1;
+    while (!gotHeader) {
+        short cols, rows, bytesPerRow, flags, nextDepthOffset, density;
+        unsigned char pixelSizeCode, version, compressionType, 
+            size, pixelFormat;
+        long transparentValue, nextBitmapOffset;
+        unsigned int pixelSize, transparentIndex;
+
+        pm_readbigshort(ifP, &cols);
+        pm_readbigshort(ifP, &rows);
+        pm_readbigshort(ifP, &bytesPerRow);
+        pm_readbigshort(ifP, &flags);
+ 
+        pm_readcharu(ifP, &pixelSizeCode);
+        pixelSize = pixelSizeCode == 0 ? 1 : pixelSizeCode;
+        if (pixelSizeCode != 0x00 &&
+            pixelSizeCode != 0x01 &&
+            pixelSizeCode != 0x02 &&
+            pixelSizeCode != 0x04 &&
+            pixelSizeCode != 0x08 &&
+            pixelSizeCode != 0x10 &&
+            pixelSizeCode != 0xFF)
+            pm_error("Invalid value for bits per pixel: %u.", pixelSizeCode);
+
+        if ((bytesPerRow * 8) < (cols * pixelSize))
+            pm_error("%u bytes per row is not valid with %u columns and %u "
+                     "bits per pixel.", bytesPerRow, cols, pixelSize);
+
+        pm_readcharu(ifP, &version);
+        if (version > 3) 
+            pm_error("Unknown encoding version type: %d", version);
+        else if (version == 3)
+            readRestOfHeaderVersion3(ifP, pixelSize,
+                                     &size, &pixelFormat, &compressionType,
+                                     &density, &transparentIndex, 
+                                     &transparentValue, &nextBitmapOffset,
+                                     &nextDepthOffset);
+        else
+            readRestOfHeaderOld(ifP,
+                                &size, &pixelFormat, &compressionType,
+                                &density, &transparentIndex,
+                                &transparentValue, &nextBitmapOffset,
+                                &nextDepthOffset);
+
+        if (currentRendition < requestedRendition) {
+             if (version < 3 && nextDepthOffset == 0 && pixelSizeCode != 0xFF) 
+                 pm_error("Not enough renditions in the input Palm Bitmap "
+                          "to extract the %dth", requestedRendition);
+             if (version == 3 && nextBitmapOffset == 0) 
+                 pm_error("Not enough renditions in the input Palm Bitmap "
+                          "to extract the %dth", requestedRendition);
+             /* nextDepthOffset is calculated in 4 byte words
+                from the beginning of this bitmap (so it equals its size) 
+             */
+             if (version < 3 && pixelSizeCode != 0xFF )
+                 skipbytes(ifP, (nextDepthOffset*4)-16);
+             else if (version == 3)
+                 /* FIXME rewrite skipbytes to accept longs? */
+                 skipbytes(ifP, (short) nextBitmapOffset-24); 
+             if (pixelSizeCode != 0xFF)
+                 ++currentRendition;
+        } else if (pixelSizeCode != 0xFF) {
+            gotHeader = TRUE;
+            
+            interpretHeader(palmHeaderP,
+                            cols, rows, bytesPerRow, flags, pixelSizeCode,
+                            pixelSize, version, size, pixelFormat, density,
+                            transparentValue, transparentIndex, 
+                            compressionType);
+        }
+    }
+}
+
+
+
+static const char *
+yesno(bool const arg) {
+
+    if (arg)
+        return "YES";
+    else
+        return "NO";
+}
+
+
+static void
+reportPalmHeader(struct palmHeader      const palmHeader,
+                 struct directColorInfo const directColorInfo) {
+
+    const char *ctype;
+
+    switch (palmHeader.compressionType) {
+    case COMPRESSION_RLE:
+        ctype = "rle (Palm OS 3.5)";
+        break;
+    case COMPRESSION_SCANLINE:
+        ctype = "scanline (Palm OS 2.0)";
+        break;
+    case COMPRESSION_PACKBITS:
+        ctype = "packbits (Palm OS 4.0)";
+        break;
+    case COMPRESSION_NONE:
+        ctype = "none";
+        break;
+    }
+    pm_message("Dimensions: %hu columns x %hu rows",
+               palmHeader.cols, palmHeader.rows);
+    pm_message("Row layout: %hu bytes per row, %hu bits per pixel",
+               palmHeader.bytesPerRow, palmHeader.pixelSize);
+    pm_message("Pixel Size code: %hu", palmHeader.pixelSizeCode);
+    pm_message("Flags: 0x%04hx", palmHeader.flags);
+    pm_message("  Direct Color: %s", yesno(palmHeader.directColor));
+    pm_message("  Colormap:     %s", yesno(palmHeader.hasColormap));
+    pm_message("  Transparency: %s", yesno(palmHeader.hasTransparency));
+    pm_message("Version %d", palmHeader.version);
+    if (palmHeader.hasTransparency) {
+        if (palmHeader.directColor) {
+            /* Copied from doTransparent(...) */
+            Color_s const color = directColorInfo.transparentColor;
+            pm_message("Transparent value: #%02x%02x%02x", 
+                       (unsigned int)((color >> 16) & 0xFF), 
+                       (unsigned int)((color >>  8) & 0xFF), 
+                       (unsigned int)((color >>  0) & 0xFF));
+        } else
+            pm_message("Transparent index: %u", palmHeader.transparentIndex);
+    }
+    pm_message("Compression type: %s", ctype);
+    if (palmHeader.version == 3)
+        pm_message("Density: %d", palmHeader.density);
+}
+
+
+
+static void
+determineOutputFormat(struct palmHeader const palmHeader,
+                      int *             const formatP,
+                      xelval *          const maxvalP) {
+
+    if (palmHeader.directColor) {
+        *formatP = PPM_TYPE;
+        *maxvalP = 255;
+    } else if (palmHeader.hasColormap) {
+        *formatP = PPM_TYPE;
+        *maxvalP = 255;
+    } else if (palmHeader.pixelSize == 1) {
+        *formatP = PBM_TYPE;
+        *maxvalP = 1;
+    } else if (palmHeader.pixelSize >= 8) {
+        *formatP = PPM_TYPE;
+        *maxvalP = pm_bitstomaxval(palmHeader.pixelSize);
+    } else {
+        *formatP = PGM_TYPE;
+        *maxvalP = pm_bitstomaxval(palmHeader.pixelSize);
+    }
+}
+
+
+
+static void
+readRgbFormat(FILE *                     const ifP,
+              struct directPixelFormat * const pixelFormatP) {
+
+    unsigned char r, g, b;
+
+    pm_readcharu(ifP, &r);
+    pm_readcharu(ifP, &g);
+    pm_readcharu(ifP, &b);
+    
+    if (r != 5 || g != 6 || b != 5)
+        pm_error("This image has a direct color pixel format of "
+                 "%u red, %u green, %u blue bits.  This program "
+                 "can handle only 5, 6, 5.", r, g, b);
+    else {
+        pixelFormatP->redbits   = r;
+        pixelFormatP->greenbits = g;
+        pixelFormatP->bluebits  = b;
+    }
+}
+
+
+
+static void
+readDirectTransparentColor(FILE *    const ifP,
+                           Color_s * const colorP) {
+
+    unsigned char r, g, b;
+
+    pm_readcharu(ifP, &r);
+    pm_readcharu(ifP, &g);
+    pm_readcharu(ifP, &b);
+
+    *colorP = (r << 16) | (g << 8) | (b << 0);
+}
+
+
+
+static void
+readDirectInfoType(FILE *                   const ifP,
+                   struct palmHeader        const palmHeader,
+                   struct directColorInfo * const directInfoTypeP) {
+/*----------------------------------------------------------------------------
+   Read the Palm Bitmap Direct Info Type section, if any.
+
+   The Direct Info Type section is a section of a pre-Version 3 direct
+   color Palm Bitmap that tells how to interpret the direct color
+   raster.
+
+   Return an undefined value as *directInfoTypeP if there is no such
+   section in this Palm Bitmap.
+-----------------------------------------------------------------------------*/
+    if ((palmHeader.directColor) && palmHeader.pixelSize != 16)
+        pm_error("The image is of the direct color type, but has %u "
+                 "bits per pixel.  The only kind of direct color images "
+                 "this program understands are 16 bit ones.", 
+                 palmHeader.pixelSize);
+
+    if (palmHeader.version == 3) {
+        /* All direct color info is in the header, because it'sversion
+           3 encoding.  No Direct Info Type section.  
+        */
+    } else {
+        if (palmHeader.directColor) {
+            unsigned char padding;
+
+            readRgbFormat(ifP, &directInfoTypeP->pixelFormat);
+
+            pm_readcharu(ifP, &padding);
+            pm_readcharu(ifP, &padding);
+
+            readDirectTransparentColor(ifP, 
+                                       &directInfoTypeP->transparentColor);
+        } else {
+            /* Not a direct color image; no Direct Info Type section. */
+        }
+    }
+}
+
+
+
+static void
+readColormap(FILE *            const ifP,
+             struct palmHeader const palmHeader,
+             Colormap *        const colormapP) {
+/*----------------------------------------------------------------------------
+   Read the colormap, if any from the Palm Bitmap.
+
+   If the image described by 'palmHeader' doesn't have a colormap,
+   return an undefined value as *colormapP.
+-----------------------------------------------------------------------------*/
+    if (palmHeader.hasColormap)
+        *colormapP = palmcolor_read_colormap(ifP);
+}
+
+
+
+static void
+getColorInfo(struct palmHeader        const palmHeader,
+             struct directColorInfo   const directInfoType,
+             Colormap                 const colormapFromImage,
+             Colormap *               const colormapP,
+             unsigned int *           const ncolorsP,
+             struct directColorInfo * const directColorInfoP) {
+/*----------------------------------------------------------------------------
+   Gather color encoding information from the various sources.
+
+   Note that 'directInfoType' and 'colormapFromImage' are meaningful only
+   with certain values of 'palmHeader'.
+
+   If it's a version 3 direct color, the pixel format must be "565".
+-----------------------------------------------------------------------------*/
+    if (palmHeader.version == 3 && palmHeader.directColor) {
+        *colormapP = NULL;
+
+        assert(palmHeader.pixelFormat == PALM_FORMAT_565);
+
+        directColorInfoP->pixelFormat.redbits   = 5;
+        directColorInfoP->pixelFormat.greenbits = 6;
+        directColorInfoP->pixelFormat.bluebits  = 5;
+        /* FIXME Just guessing here ... */
+        directColorInfoP->transparentColor = 
+            (((palmHeader.transparentValue >> 11) & 0x1F) << 16) |
+            (((palmHeader.transparentValue >>  5) & 0x3F) <<  8) |
+            (((palmHeader.transparentValue >>  0) & 0x1F) <<  0);
+    } else if (palmHeader.directColor) {
+        *colormapP = NULL;
+        *directColorInfoP = directInfoType;
+    } else if (palmHeader.hasColormap)
+        *colormapP = colormapFromImage;
+    else if (palmHeader.pixelSize >= 8) {
+        Colormap colormap;
+        colormap = palmcolor_build_default_8bit_colormap();
+        qsort(colormap->color_entries, colormap->ncolors, 
+              sizeof(Color_s), palmcolor_compare_indices);
+        *colormapP = colormap;
+    } else
+        *colormapP = NULL;
+
+    *ncolorsP = 1 << palmHeader.pixelSize;
+}
+
+
+
+static void
+doTransparent(FILE *                 const ofP,
+              bool                   const hasTransparency,
+              bool                   const directColor,
+              unsigned char          const transparentIndex,
+              unsigned char          const pixelSize,
+              Colormap               const colormap,
+              struct directColorInfo const directColorInfo) {
+/*----------------------------------------------------------------------------
+   Generate a PNM comment on *ofP telling what color in the raster is
+   supposed to be transparent.
+
+   Note that PNM itself doesn't have any way to represent transparency.
+   (But this program could be converted to a PAM program and use the
+   RGB_ALPHA and GRAYSCALE_ALPHA tuple types).
+-----------------------------------------------------------------------------*/
+    if (hasTransparency) {
+        if (colormap) {
+            Color_s const color = transparentIndex << 24;
+            Color const actualColor = (bsearch(&color,
+                                               colormap->color_entries, 
+                                               colormap->ncolors,
+                                               sizeof(color), 
+                                               palmcolor_compare_indices));
+            fprintf(ofP, "#%02x%02x%02x\n", 
+                   (unsigned int) ((*actualColor >> 16) & 0xFF),
+                   (unsigned int) ((*actualColor >>  8) & 0xFF), 
+                   (unsigned int) ((*actualColor >>  0) & 0xFF));
+        } else if (directColor) {
+            Color_s const color = directColorInfo.transparentColor;
+            fprintf(ofP, "#%02x%02x%02x\n", 
+                   (unsigned int)((color >> 16) & 0xFF), 
+                   (unsigned int)((color >>  8) & 0xFF), 
+                   (unsigned int)((color >>  0) & 0xFF));
+        } else {
+            unsigned int const maxval = pm_bitstomaxval(pixelSize);
+            unsigned int const grayval = 
+                ((maxval - transparentIndex) * 256) / maxval;
+            fprintf(ofP, "#%02x%02x%02x\n", grayval, grayval, grayval);
+        }
+    }
+}
+
+
+
+static void
+createHistogram(unsigned int    const ncolors,
+                unsigned int ** const seenP) {
+
+    unsigned int * seen;
+
+    MALLOCARRAY(seen, ncolors);
+    if (!seen)
+        pm_error("Can't allocate array for keeping track of "
+                 "how many pixels of each of %u colors are in the image.", 
+                 ncolors);
+
+    {    
+        /* Initialize the counter for each color to zero */
+        unsigned int i;
+        for (i = 0; i < ncolors; ++i)
+            seen[i] = 0;
+    }
+    *seenP = seen;
+}
+
+
+
+static void
+readScanlineRow(FILE *          const ifP,
+                unsigned char * const palmrow,
+                unsigned char * const lastrow,
+                unsigned int    const bytesPerRow,
+                bool            const firstRow) {
+
+    unsigned int j;
+
+    for (j = 0; j < bytesPerRow; j += 8) {
+        unsigned char diffmask;
+            /* A mask telling whether each of the 8 raster bytes indexed
+               j through j+7 is the same as in the previous row ('lastrow')
+               or is to be read from the file.  Bit 0 of the mask refers
+               to byte j, Bit 1 to byte j + 1, etc.
+            */
+        unsigned int byteCount;
+            /* How many bytes are covered by 'diffmask'.  Normally 8, but
+               at the end of the row, could be less.
+            */
+        unsigned int k;
+
+        pm_readcharu(ifP, &diffmask);
+        byteCount = MIN(bytesPerRow - j, 8);
+        
+        for (k = 0; k < byteCount; ++k) {
+            /* the first row cannot be compressed */
+            if (firstRow || ((diffmask & (1 << (7 - k))) != 0)) {
+                unsigned char inval;
+                pm_readcharu(ifP, &inval);
+                palmrow[j + k] = inval;
+            } else
+                palmrow[j + k] = lastrow[j + k];
+        }
+    }
+    memcpy(lastrow, palmrow, bytesPerRow);
+}
+
+
+
+static void
+readRleRow(FILE *          const ifP,
+           unsigned char * const palmrow,
+           unsigned int    const bytesPerRow) {
+
+    unsigned int j;
+
+    for (j = 0;  j < bytesPerRow; ) {
+        unsigned char incount;
+        unsigned char inval;
+
+        pm_readcharu(ifP, &incount);
+        if (incount == 0)
+            pm_error("Invalid (zero) count in RLE compression.");
+        if (j + incount > bytesPerRow)
+            pm_error("Bytes in RLE compressed row exceed bytes per row.  "
+                     "Bytes per row is %u.  A run length of %u bytes "
+                     "pushes the bytes in this row up to %u bytes (and then"
+                     "we gave up.", bytesPerRow, incount, j + incount);
+        pm_readcharu(ifP, &inval);
+        memset(palmrow + j, inval, incount);
+        j += incount;
+    }
+} 
+
+
+
+static void
+readPackBitsRow(FILE *          const ifP,
+                unsigned char * const palmrow,
+                unsigned int    const bytesPerRow) {
+
+    unsigned int j;
+
+    for (j = 0;  j < bytesPerRow; ) {
+        char incount;
+        pm_readchar(ifP, &incount);
+        if (incount < 0) { 
+            /* How do we handle incount == -128 ? */
+            unsigned int const runlength = -incount + 1;
+            unsigned char inval;
+            pm_readcharu(ifP, &inval);
+            memset(palmrow + j, inval, runlength);
+            j += runlength;
+        } else {
+            unsigned int const nonrunlength = incount + 1;
+            unsigned int k;
+            for (k = 0; k < nonrunlength; ++k) {
+                unsigned char inval;
+                pm_readcharu(ifP, &inval);
+                palmrow[j + k] = inval;
+            }
+            j += nonrunlength;
+        }
+        if (j > bytesPerRow) 
+            pm_error("Bytes in PackBits compressed row exceed bytes per row.");
+    }
+} 
+
+
+
+static void
+readUncompressedRow(FILE *          const ifP,
+                    unsigned char * const palmrow,
+                    unsigned int    const bytesPerRow) {
+
+    int bytesRead;
+    
+    bytesRead = fread(palmrow, 1, bytesPerRow, ifP);
+    if (bytesRead != bytesPerRow)
+        pm_error("Error reading Palm file.  Short read.");
+}
+
+
+
+static void
+readDecompressedRow(FILE *                   const ifP,
+                    unsigned char *          const palmrow,
+                    unsigned char *          const lastrow,
+                    enum palmCompressionType const compressionType,
+                    unsigned int             const bytesPerRow,
+                    bool                     const firstRow) {
+/*----------------------------------------------------------------------------
+   Read a row from Palm file 'ifP', in uncompressed form (i.e. decompress if
+   necessary).  Assume the row contains 'bytesPerRow' uncompressed bytes,
+   compressed according to 'compressionType'.  Return the data at 'palmrow'.
+
+   'firstRow' means decompress it as if it is the first row of the image
+   (some compression schemes transform the first row differently from the
+   rest, because each row depends on the row before it).
+
+   If 'compressionType' is COMPRESSION_SCANLINE, (which means
+   transformation of a row depends on the contents of the row before
+   it), then 'lastRow' is as input the uncompressed contents of the
+   previous row (undefined if 'firstRow' is true).  In that case, we
+   modify 'lastrow' to contain a copy of 'palmrow' (so Caller can
+   conveniently use it to read the next row).
+
+   If 'compressionType' is not COMPRESSION_SCANLINE, 'lastrow' is
+   undefined both as input and output.
+-----------------------------------------------------------------------------*/
+    switch (compressionType) {
+    case COMPRESSION_RLE:
+        readRleRow(ifP, palmrow, bytesPerRow);
+        break;
+    case COMPRESSION_SCANLINE:
+        readScanlineRow(ifP, palmrow, lastrow, bytesPerRow, firstRow);
+        break;
+    case COMPRESSION_PACKBITS:
+        readPackBitsRow(ifP, palmrow, bytesPerRow);
+        break;
+    case COMPRESSION_NONE:
+        readUncompressedRow(ifP, palmrow, bytesPerRow);
+        break;
+    }
+}
+
+
+
+static void
+convertRowToPnmDirect(const unsigned char * const palmrow,
+                      xel *                 const xelrow,
+                      unsigned int          const cols,
+                      xelval                const maxval,
+                      unsigned int *        const seen) {
+
+    /* There's a problem with this.  Take the Palm 16-bit
+       direct color.  That's 5 bits for the red, 6 for the
+       green, and 5 for the blue.  So what should the MAXVAL
+       be?  I decided to use 255 (8 bits) for everything,
+       since that's the theoretical max of the number of bits
+       in any one color, according to Palm.  So the Palm color
+       0xFFFF (white) would be red=0x1F, green=0x3F, and
+       blue=0x1F.  How do we promote those colors?  Simple
+       shift would give us R=248,G=252,B=248; which is
+       slightly green.  Hardly seems right.
+       
+       So I've perverted the math a bit.  Each color value is
+       multiplied by 255, then divided by either 31 (red or
+       blue) or 63 (green).  That's the right way to do it
+       anyway.  
+    */
+
+    const unsigned char *inbyte;
+    unsigned int j;
+    
+    for (inbyte = palmrow, j = 0;  j < cols;  ++j) {
+        unsigned int inval;
+        inval = *inbyte++ << 8;
+        inval |= *inbyte++;
+        
+        if (seen)
+            ++seen[inval];
+        
+        PPM_ASSIGN(xelrow[j], 
+                   (((inval >> 11) & 0x1F) * maxval) / 0x1F, 
+                   (((inval >>  5) & 0x3F) * maxval) / 0x3F, 
+                   (((inval >>  0) & 0x1F) * maxval) / 0x1F
+            );
+    }
+}
+
+
+
+static void
+convertRowToPnmNotDirect(const unsigned char * const palmrow,
+                         xel *                 const xelrow,
+                         unsigned int          const cols,
+                         Colormap              const colormap,
+                         xelval *              const graymap,
+                         unsigned int *        const seen,
+                         unsigned int          const pixelSize) {
+
+    unsigned int const mask = (1 << pixelSize) - 1;
+
+    const unsigned char *inbyte;
+    unsigned int inbit;
+    unsigned int j;
+    
+    inbit = 8 - pixelSize;
+    inbyte = palmrow;
+    for (j = 0; j < cols; ++j) {
+        short const color = ((*inbyte) & (mask << inbit)) >> inbit;
+        if (seen)
+            ++seen[color];
+        
+        if (colormap) {
+            Color_s const color2      = color << 24;
+            Color   const actualColor = (bsearch (&color2,
+                                                  colormap->color_entries, 
+                                                  colormap->ncolors,
+                                                  sizeof(color2), 
+                                                  palmcolor_compare_indices));
+            PPM_ASSIGN(xelrow[j], 
+                       (*actualColor >> 16) & 0xFF, 
+                       (*actualColor >>  8) & 0xFF, 
+                       (*actualColor >>  0) & 0xFF);
+        } else
+            PNM_ASSIGN1(xelrow[j], graymap[color]);
+        
+        if (!inbit) {
+            ++inbyte;
+            inbit = 8 - pixelSize;
+        } else
+            inbit -= pixelSize;
+    }
+}
+
+
+
+static void
+writePnm(FILE *            const ofP,
+         struct palmHeader const palmHeader,
+         FILE *            const ifP,
+         Colormap          const colormap,
+         xelval *          const graymap,
+         unsigned int      const nColors,
+         int               const format,
+         xelval            const maxval,
+         unsigned int **   const seenP) {
+
+    int const cols = palmHeader.cols;
+    int const rows = palmHeader.rows;
+
+    unsigned char * palmrow;
+    unsigned char * lastrow;
+    xel *           xelrow;
+    unsigned int *  seen;
+    unsigned int    row;
+    
+    pnm_writepnminit(ofP, cols, rows, maxval, format, 0);
+    xelrow = pnm_allocrow(cols);
+
+    /* Read the picture data, one row at a time */
+    MALLOCARRAY_NOFAIL(palmrow, palmHeader.bytesPerRow);
+    MALLOCARRAY_NOFAIL(lastrow, palmHeader.bytesPerRow); 
+    
+    if (seenP) {
+        createHistogram(nColors, &seen);
+        *seenP = seen;
+    } else
+        seen = NULL;
+
+    /* We should actually use compressedDataSizeNN for checking the sanity 
+       of the data we're reading ... 
+    */
+    if (palmHeader.compressionType != COMPRESSION_NONE) {
+        if (palmHeader.version < 3) {
+            short compressedDataSize16;
+            pm_readbigshort(ifP, &compressedDataSize16);
+        } else {
+            long compressedDataSize32;
+            pm_readbiglong(ifP, &compressedDataSize32);
+        }
+    }
+
+    for (row = 0; row < rows; ++row) {
+        readDecompressedRow(ifP, palmrow, lastrow, 
+                            palmHeader.compressionType, 
+                            palmHeader.bytesPerRow,
+                            row == 0);
+
+        if (palmHeader.directColor) {
+            assert(palmHeader.pixelSize == 16);
+            convertRowToPnmDirect(palmrow, xelrow, cols, maxval, seen);
+        } else
+            convertRowToPnmNotDirect(palmrow, xelrow, cols, colormap, graymap,
+                                     seen, palmHeader.pixelSize);
+
+        pnm_writepnmrow(ofP, xelrow, cols, maxval, format, 0);
+    }
+    free(lastrow);
+    free(palmrow);
+    pnm_freerow(xelrow);
+}
+
+
+
+static void
+showHistogram(unsigned int * const seen,
+              Colormap       const colormap,
+              const xelval * const graymap,
+              unsigned int   const ncolors) {
+
+    unsigned int colorIndex;
+    
+    for (colorIndex = 0;  colorIndex < ncolors; ++colorIndex) {
+        if (!colormap)
+            pm_message("%.3d -> %.3d:  %d", 
+                       colorIndex, graymap[colorIndex], seen[colorIndex]);
+        else {
+            Color_s const color = colorIndex << 24;
+            Color const actualColor = (bsearch(&color,
+                                               colormap->color_entries, 
+                                               colormap->ncolors,
+                                               sizeof(color), 
+                                               palmcolor_compare_indices));
+            if (actualColor)
+                pm_message("%.3d -> %ld,%ld,%ld:  %d", colorIndex,
+                           (*actualColor >> 16) & 0xFF,
+                           (*actualColor >> 8) & 0xFF,
+                           (*actualColor & 0xFF), seen[colorIndex]);
+        }
+    }
+}
+
+
+
+int
+main(int argc, char **argv) {
+
+    struct cmdlineInfo cmdline;
+
+    FILE* ifP;
+    struct palmHeader palmHeader;
+    struct directColorInfo directInfoType;
+    Colormap colormapFromImage;
+    Colormap colormap;
+    struct directColorInfo directColorInfo;
+    int format;
+    xelval maxval;
+    unsigned int nColors;
+
+    /* Parse default params */
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    readHeader(ifP, cmdline.rendition, &palmHeader);
+
+    readDirectInfoType(ifP, palmHeader, &directInfoType);
+   
+    readColormap(ifP, palmHeader, &colormapFromImage);
+    
+    determineOutputFormat(palmHeader, &format, &maxval);
+    
+    getColorInfo(palmHeader, directInfoType, colormapFromImage,
+                 &colormap, &nColors, &directColorInfo);
+
+    if (cmdline.verbose)
+        reportPalmHeader(palmHeader, directColorInfo);
+
+    if (cmdline.transparent)
+        doTransparent(stdout, 
+                      palmHeader.hasTransparency, palmHeader.directColor,
+                      palmHeader.transparentIndex, 
+                      palmHeader.pixelSize, colormap, directColorInfo);
+    else {
+        unsigned int * seen;
+        xelval * graymap;
+
+        graymap = createGraymap(nColors, maxval);
+
+        writePnm(stdout,
+                 palmHeader, ifP, colormap, graymap, nColors, format, maxval, 
+                 cmdline.showhist ? &seen : NULL);
+        
+        if (cmdline.showhist)
+            showHistogram(seen, colormap, graymap, nColors);
+        
+        free(graymap);
+    }
+    pm_close(ifP);
+
+    return 0;
+}
diff --git a/converter/other/pnmtopalm/pnmtopalm.c b/converter/other/pnmtopalm/pnmtopalm.c
new file mode 100644
index 00000000..3b9eec8f
--- /dev/null
+++ b/converter/other/pnmtopalm/pnmtopalm.c
@@ -0,0 +1,1235 @@
+/* pnmtopalm.c - read a PNM image and write a Palm Bitmap file
+ *
+ * Inspired by and using methods from ppmtoTbmp.c by Ian Goldberg
+ * <iang@cs.berkeley.edu>, which was based on ppmtopuzz.c by Jef
+ * Poskanzer, from the netpbm-1mar1994 package.
+ *
+ * Mods for multiple bits per pixel were added to ppmtoTbmp.c by
+ * George Caswell <tetsujin@sourceforge.net> and Bill Janssen
+ * <bill@janssen.org>.
+ *
+ * Major fixes and new capability added by Paul Bolle <pebolle@tiscali.nl>
+ * in late 2004 / early 2005.
+ *
+ * See LICENSE file for licensing information.
+ *
+ *  
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+
+#include "pnm.h"
+#include "palm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+enum compressionType {COMP_NONE, COMP_SCANLINE, COMP_RLE, COMP_PACKBITS};
+
+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;  /* Filespecs of input files */
+    char *transparent;          /* -transparent value.  Null if unspec */
+    unsigned int depth;         /* -depth value.  0 if unspec */
+    unsigned int maxdepth;      /* -maxdepth value.  0 if unspec */
+    enum compressionType compression;
+    unsigned int verbose;
+    unsigned int colormap;
+    unsigned int offset;        /* -offset specified */
+    unsigned int density;       /* screen density */
+    unsigned int withdummy;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optStruct3 opt;  /* set by OPTENT3 */
+    optEntry *option_def;
+    unsigned int option_def_index;
+
+    unsigned int transSpec, depthSpec, maxdepthSpec, densitySpec;
+    unsigned int scanline_compression, rle_compression, packbits_compression;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "transparent",      OPT_STRING, 
+            &cmdlineP->transparent, &transSpec, 0);
+    OPTENT3(0, "depth",            OPT_UINT, 
+            &cmdlineP->depth,       &depthSpec, 0);
+    OPTENT3(0, "maxdepth",         OPT_UINT, 
+            &cmdlineP->maxdepth,    &maxdepthSpec, 0);
+    OPTENT3(0, "scanline_compression", OPT_FLAG, 
+            NULL,                   &scanline_compression, 0);
+    OPTENT3(0, "rle_compression",  OPT_FLAG, 
+            NULL,                   &rle_compression, 0);
+    OPTENT3(0, "packbits_compression", OPT_FLAG, 
+            NULL,                   &packbits_compression, 0);
+    OPTENT3(0, "verbose",          OPT_FLAG, 
+            NULL,                   &cmdlineP->verbose, 0);
+    OPTENT3(0, "colormap",         OPT_FLAG, 
+            NULL,                   &cmdlineP->colormap, 0);
+    OPTENT3(0, "offset",           OPT_FLAG, 
+            NULL,                   &cmdlineP->offset, 0);
+    OPTENT3(0, "density",          OPT_UINT, 
+            &cmdlineP->density,     &densitySpec, 0);
+    OPTENT3(0, "withdummy",        OPT_FLAG, 
+            NULL,                   &cmdlineP->withdummy, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE; /* We have some 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 *cmdline_p and others. */
+
+    if (depthSpec) {
+        if (cmdlineP->depth != 1 && cmdlineP->depth != 2 
+            && cmdlineP->depth != 4 && cmdlineP->depth != 8
+            && cmdlineP->depth != 16)
+            pm_error("invalid value for -depth: %u.  Valid values are "
+                     "1, 2, 4, 8, and 16", cmdlineP->depth);
+    } else
+        cmdlineP->depth = 0;
+
+    if (maxdepthSpec) {
+        if (cmdlineP->maxdepth != 1 && cmdlineP->maxdepth != 2 
+            && cmdlineP->maxdepth != 4 && cmdlineP->maxdepth != 8
+            && cmdlineP->maxdepth != 16)
+            pm_error("invalid value for -maxdepth: %u.  Valid values are "
+                     "1, 2, 4, 8, and 16", cmdlineP->maxdepth);
+    } else
+        cmdlineP->maxdepth = 0;
+
+    if (depthSpec && maxdepthSpec && 
+        cmdlineP->depth > cmdlineP->maxdepth)
+        pm_error("-depth value (%u) is greater than -maxdepth (%u) value.",
+                 cmdlineP->depth, cmdlineP->maxdepth);
+
+    if (!transSpec)
+        cmdlineP->transparent = NULL;
+
+    if (densitySpec) {
+        if (cmdlineP->density != PALM_DENSITY_LOW &&
+            cmdlineP->density != PALM_DENSITY_ONEANDAHALF &&
+            cmdlineP->density != PALM_DENSITY_DOUBLE &&
+            cmdlineP->density != PALM_DENSITY_TRIPLE &&
+            cmdlineP->density != PALM_DENSITY_QUADRUPLE)
+            pm_error("Invalid value for -density: %d.  Valid values are "
+                     "%d, %d, %d, %d and %d.", cmdlineP->density, 
+                     PALM_DENSITY_LOW, PALM_DENSITY_ONEANDAHALF,
+                     PALM_DENSITY_DOUBLE, PALM_DENSITY_TRIPLE,
+                     PALM_DENSITY_QUADRUPLE);
+    } else
+        cmdlineP->density = PALM_DENSITY_LOW;
+
+    if (cmdlineP->density != PALM_DENSITY_LOW && cmdlineP->withdummy)
+            pm_error("You can't specify -withdummy with -density value %u.  "
+                     "It is valid only with low density (%u)",
+                     cmdlineP->density, PALM_DENSITY_LOW);
+
+    if (cmdlineP->withdummy && !cmdlineP->offset)
+        pm_error("-withdummy does not make sense without -offset");
+
+    if (scanline_compression + rle_compression + packbits_compression > 1)
+        pm_error("You may specify only one of -scanline_compression, "
+                 "-rle_compression, and -packbits_compression");
+    else {
+        if (scanline_compression)
+            cmdlineP->compression = COMP_SCANLINE;
+        else if (rle_compression)
+            cmdlineP->compression = COMP_RLE;
+        else if (packbits_compression)
+            cmdlineP->compression = COMP_PACKBITS;
+        else
+            cmdlineP->compression = COMP_NONE;
+    }
+        
+    if (argc-1 > 1)
+        pm_error("This program takes at most 1 argument: the file name.  "
+                 "You specified %d", argc-1);
+    else if (argc-1 > 0) 
+        cmdlineP->inputFilespec = argv[1];
+    else
+        cmdlineP->inputFilespec = "-";
+}
+
+
+
+static void
+determinePalmFormat(unsigned int   const cols, 
+                    unsigned int   const rows, 
+                    xelval         const maxval, 
+                    int            const format, 
+                    xel **         const xels,
+                    unsigned int   const specified_bpp,
+                    unsigned int   const max_bpp, 
+                    bool           const custom_colormap,
+                    bool           const verbose,
+                    unsigned int * const bppP, 
+                    bool *         const directColorP, 
+                    Colormap *     const colormapP) {
+
+    if (PNM_FORMAT_TYPE(format) == PBM_TYPE) {
+        if (custom_colormap)
+            pm_error("You specified -colormap with a black and white input "
+                     "image.  -colormap is valid only with color.");
+        if (specified_bpp)
+            *bppP = specified_bpp;
+        else
+            *bppP = 1;    /* no point in wasting bits */
+        *directColorP = FALSE;
+        *colormapP = NULL;
+        if (verbose)
+            pm_message("output is black and white");
+    } else if (PNM_FORMAT_TYPE(format) == PGM_TYPE) {
+        /* we can usually handle this one, but may not have enough
+           pixels.  So check... */
+        if (custom_colormap)
+            pm_error("You specified -colormap with a black and white input"
+                     "image.  -colormap is valid only with color.");
+        if (specified_bpp)
+            *bppP = specified_bpp;
+        else if (max_bpp && (maxval >= (1 << max_bpp)))
+            *bppP = max_bpp;
+        else if (maxval > 16)
+            *bppP = 4;
+        else {
+            /* scale to minimum number of bpp needed */
+            for (*bppP = 1;  (1 << *bppP) < maxval;  *bppP *= 2)
+                ;
+        }
+        if (*bppP > 4)
+            *bppP = 4;
+        if (verbose)
+            pm_message("output is grayscale %d bits-per-pixel", *bppP);
+        *directColorP = FALSE;
+        *colormapP = NULL;
+    } else if (PNM_FORMAT_TYPE(format) == PPM_TYPE) {
+
+        /* We assume that we only get a PPM if the image cannot be
+           represented as PBM or PGM.  There are two options here: either
+           8-bit with a colormap, either the standard one or a custom one,
+           or 16-bit direct color.  In the 8-bit case, if "custom_colormap"
+           is specified (not recommended by Palm) we will put in our own
+           colormap; otherwise we will assume that the colors have been
+           mapped to the default Palm colormap by appropriate use of
+           pnmquant.  We try for 8-bit color first, since it works on
+           more PalmOS devices. 
+        */
+        if ((specified_bpp == 16) || 
+            (specified_bpp == 0 && max_bpp == 16)) {
+            /* we do the 16-bit direct color */
+            *directColorP = TRUE;
+            *colormapP = NULL;
+            *bppP = 16;
+        } else if (!custom_colormap) {
+            /* standard indexed 8-bit color */
+            *colormapP = palmcolor_build_default_8bit_colormap();
+            *bppP = 8;
+            if (((specified_bpp != 0) && (specified_bpp != 8)) ||
+                ((max_bpp != 0) && (max_bpp < 8)))
+                pm_error("Must use depth of 8 for color Palm Bitmap without "
+                         "custom color table.");
+            *directColorP = FALSE;
+            if (verbose)
+                pm_message("Output is color with default colormap at 8 bpp");
+        } else {
+            /* indexed 8-bit color with a custom colormap */
+            *colormapP = 
+                palmcolor_build_custom_8bit_colormap(rows, cols, xels);
+            for (*bppP = 1; (1 << *bppP) < (*colormapP)->ncolors; *bppP *= 2);
+            if (specified_bpp != 0) {
+                if (specified_bpp >= *bppP)
+                    *bppP = specified_bpp;
+                else
+                    pm_error("Too many colors for specified depth.  "
+                             "Use pnmquant to reduce.");
+            } else if ((max_bpp != 0) && (max_bpp < *bppP)) {
+                pm_error("Too many colors for specified max depth.  "
+                         "Use pnmquant to reduce.");
+            }
+            *directColorP = FALSE;
+            if (verbose)
+                pm_message("Output is color with custom colormap "
+                           "with %d colors at %d bpp", 
+                           (*colormapP)->ncolors, *bppP);
+        }
+    } else {
+        pm_error("unknown format 0x%x on input file", (unsigned) format);
+    }
+}
+
+
+
+static const char * 
+formatName(int const format) {
+    
+    const char * retval;
+
+    switch(PNM_FORMAT_TYPE(format)) {
+    case PBM_TYPE: retval = "black and white"; break;
+    case PGM_TYPE: retval = "grayscale";       break;
+    case PPM_TYPE: retval = "color";           break;
+    default:       retval = "???";             break;
+    }
+    return retval;
+}
+
+        
+
+static void
+findTransparentColor(char *         const colorSpec, 
+                     pixval         const newMaxval,
+                     bool           const directColor, 
+                     pixval         const maxval, 
+                     Colormap       const colormap,
+                     xel *          const transcolorP, 
+                     unsigned int * const transindexP) {
+
+    *transcolorP = ppm_parsecolor(colorSpec, maxval);
+    if (!directColor) {
+        Color_s const temp_color = 
+            ((((PPM_GETR(*transcolorP)*newMaxval) / maxval) << 16) 
+             | (((PPM_GETG(*transcolorP)*newMaxval) / maxval) << 8)
+             | ((PPM_GETB(*transcolorP)*newMaxval) / maxval));
+        Color const found = 
+            (bsearch(&temp_color,
+                     colormap->color_entries, colormap->ncolors,
+                     sizeof(Color_s), palmcolor_compare_colors));
+        if (!found) {
+            pm_error("Specified transparent color %s not found "
+                     "in colormap.", colorSpec);
+        } else
+            *transindexP = (*found >> 24) & 0xFF;
+    }
+}
+
+
+
+static unsigned int
+bitmapVersion(unsigned int         const bpp,
+              bool                 const colormap,
+              bool                 const transparent,
+              enum compressionType const compression,
+              unsigned int         const density) {
+/*----------------------------------------------------------------------------
+   Return the version number of the oldest version that can represent
+   the specified attributes.
+-----------------------------------------------------------------------------*/
+    unsigned int version;
+    /* we need Version 1 if we use more than 1 bpp,
+       Version 2 if we use compression or transparency,
+       Version 3 if density is 108 or higher
+    */
+    if (density > PALM_DENSITY_LOW)
+        version = 3;
+    else if (transparent || compression != COMP_NONE)
+        version = 2;
+    else if (bpp > 1 || colormap)
+        version = 1;
+    else
+        version = 0;
+
+    return version;
+}
+
+
+
+static void
+writeCommonHeader(unsigned int         const cols,
+                  unsigned int         const rows,
+                  unsigned int         const rowbytes,
+                  enum compressionType const compression,
+                  bool                 const colormap,
+                  bool                 const transparent,
+                  bool                 const directColor,
+                  unsigned int         const bpp,
+                  unsigned int         const version) {
+/*----------------------------------------------------------------------------
+   Write the first 10 bytes of the Palm Bitmap header.
+   These are common to all encodings (versions 0, 1, 2 and 3).
+-----------------------------------------------------------------------------*/
+    unsigned short flags;
+
+    if (cols > USHRT_MAX)
+        pm_error("Too many columns for Palm Bitmap: %u", cols);
+    pm_writebigshort(stdout, cols);    /* width */
+    if (rows > USHRT_MAX)
+        pm_error("Too many columns for Palm Bitmap: %u", rows);
+    pm_writebigshort(stdout, rows);    /* height */
+    if (rowbytes > USHRT_MAX)
+        pm_error("Too many bytes per row for Palm Bitmap: %u", rowbytes);
+    pm_writebigshort(stdout, rowbytes);
+
+    flags = 0;  /* initial value */
+    if (compression != COMP_NONE)
+        flags |= PALM_IS_COMPRESSED_FLAG;
+    if (colormap)
+        flags |= PALM_HAS_COLORMAP_FLAG;
+    if (transparent)
+        flags |= PALM_HAS_TRANSPARENCY_FLAG;
+    if (directColor)
+        flags |= PALM_DIRECT_COLOR_FLAG;
+    pm_writebigshort(stdout, flags);
+    assert(bpp <= UCHAR_MAX);
+    fputc(bpp, stdout);
+
+    fputc(version, stdout);
+}
+
+
+
+static unsigned char 
+compressionFieldValue(enum compressionType const compression) {
+
+    unsigned char retval;
+
+    switch (compression) {
+    case COMP_SCANLINE:
+        retval = PALM_COMPRESSION_SCANLINE;
+        break;
+    case COMP_RLE:
+        retval = PALM_COMPRESSION_RLE;
+        break;
+    case COMP_PACKBITS:
+        retval = PALM_COMPRESSION_PACKBITS;
+        break;
+    case COMP_NONE:
+        retval = 0x00;  /* empty */
+        break;
+    }
+    return retval;
+}
+
+
+
+static void
+writeRemainingHeaderLow(unsigned int         const nextDepthOffset,
+                        unsigned int         const transindex,
+                        enum compressionType const compression,
+                        unsigned int         const bpp) {
+/*----------------------------------------------------------------------------
+   Write last 6 bytes of a low density Palm Bitmap header. 
+-----------------------------------------------------------------------------*/
+    if (nextDepthOffset > USHRT_MAX)
+        pm_error("Image too large for Palm Bitmap");
+
+    pm_writebigshort(stdout, nextDepthOffset);
+
+    if (bpp != 16) {
+        assert(transindex <= UCHAR_MAX);
+        fputc(transindex, stdout);    /* transparent index */
+    } else
+        fputc(0, stdout);    /* the DirectInfoType will hold this info */
+    
+    fputc(compressionFieldValue(compression), stdout);
+
+    pm_writebigshort(stdout, 0);  /* reserved by Palm */
+}
+
+
+
+static void
+writeRemainingHeaderHigh(unsigned int         const bpp,
+                         enum compressionType const compression,
+                         unsigned int         const density,
+                         xelval               const maxval,
+                         bool                 const transparent,
+                         xel                  const transcolor,
+                         unsigned int         const transindex,
+                         unsigned int         const nextBitmapOffset) {
+/*----------------------------------------------------------------------------
+   Write last 16 bytes of a high density Palm Bitmap header. 
+-----------------------------------------------------------------------------*/
+    if ((nextBitmapOffset >> 31) > 1)
+        pm_error("Image too large for Palm Bitmap.  nextBitmapOffset "
+            "value doesn't fit in 4 bytes");
+
+    fputc(0x18, stdout); /* size of this high density header */
+
+    if (bpp != 16)
+        fputc(PALM_FORMAT_INDEXED, stdout);
+    else
+        fputc(PALM_FORMAT_565, stdout);
+
+    fputc(0x00, stdout); /* unused */
+
+    fputc(compressionFieldValue(compression), stdout);
+
+    pm_writebigshort(stdout, density);
+
+    if (transparent) {
+        if (bpp == 16) {
+            /* Blind guess here */
+            fputc(0, stdout);
+            fputc((PPM_GETR(transcolor) * 255) / maxval, stdout);
+            fputc((PPM_GETG(transcolor) * 255) / maxval, stdout);
+            fputc((PPM_GETB(transcolor) * 255) / maxval, stdout);
+        } else {
+            assert(transindex <= UCHAR_MAX);
+            fputc(0, stdout);
+            fputc(0, stdout);
+            fputc(0, stdout);
+            fputc(transindex, stdout);   /* transparent index */
+        }
+    } else
+        pm_writebiglong(stdout, 0);
+
+    pm_writebiglong(stdout, nextBitmapOffset);
+}
+
+
+
+static void
+writeDummy() {
+/*----------------------------------------------------------------------------
+   Write a dummy Palm Bitmap header.  This is a 16 byte header, of
+   type version 1 and with (only) pixelSize set to 0xFF.
+
+   An old viewer will see this as invalid due to the pixelSize, and stop
+   reading the stream.  A new viewer will recognize this for what it is
+   (a dummy header designed to stop old viewers from reading further in
+   the stream) and continue reading the stream.  Presumably, what follows
+   in the stream is understandable by a new viewer, but would confuse an
+   old one.
+-----------------------------------------------------------------------------*/
+    pm_writebiglong(stdout, 0x00);
+    pm_writebiglong(stdout, 0x00);
+    fputc(0xFF, stdout);               /* pixelSize */
+    fputc(0x01, stdout);               /* version */
+    pm_writebigshort(stdout, 0x00); 
+    pm_writebiglong(stdout, 0x00);
+}
+
+
+
+static void
+writeColormap(bool         const explicitColormap,
+              Colormap     const colormap,
+              bool         const directColor,
+              unsigned int const bpp,
+              bool         const transparent,
+              xel          const transcolor,
+              xelval       const maxval,
+              unsigned int const version) {
+              
+    /* if there's a colormap, write it out */
+    if (explicitColormap) {
+        unsigned int row;
+        if (!colormap)
+            pm_error("Internal error: user specified -colormap, but we did "
+                     "not generate a colormap.");
+        qsort (colormap->color_entries, colormap->ncolors,
+               sizeof(Color_s), palmcolor_compare_indices);
+        pm_writebigshort( stdout, colormap->ncolors );
+        for (row = 0;  row < colormap->ncolors; ++row)
+            pm_writebiglong (stdout, colormap->color_entries[row]);
+    }
+
+    if (directColor && (version < 3)) {
+        /* write the DirectInfoType (8 bytes) */
+        if (bpp == 16) {
+            fputc(5, stdout);   /* # of bits of red */
+            fputc(6, stdout);   /* # of bits of green */    
+            fputc(5, stdout);   /* # of bits of blue */
+            fputc(0, stdout);   /* reserved by Palm */
+        } else
+            pm_error("Don't know how to create %d bit DirectColor bitmaps.", 
+                     bpp);
+        if (transparent) {
+            fputc(0, stdout);
+            fputc((PPM_GETR(transcolor) * 255) / maxval, stdout);
+            fputc((PPM_GETG(transcolor) * 255) / maxval, stdout);
+            fputc((PPM_GETB(transcolor) * 255) / maxval, stdout);
+        } else
+            pm_writebiglong(stdout, 0);     /* no transparent color */
+    }
+}
+
+
+
+static void
+computeRawRowDirectColor(const xel *     const xelrow,
+                         unsigned int    const cols,
+                         xelval          const maxval,
+                         unsigned char * const rowdata) {
+
+    unsigned int col;
+    unsigned char *outptr;
+    
+    for (col = 0, outptr = rowdata; col < cols; ++col) {
+        unsigned int const color = 
+            ((((PPM_GETR(xelrow[col])*31)/maxval) << 11) |
+             (((PPM_GETG(xelrow[col])*63)/maxval) << 5) |
+             ((PPM_GETB(xelrow[col])*31)/maxval));
+        *outptr++ = (color >> 8) & 0xFF;
+        *outptr++ = color & 0xFF;
+    }
+}
+
+
+
+static void
+computeRawRowNonDirect(const xel *     const xelrow,
+                       unsigned int    const cols,
+                       xelval          const maxval,
+                       unsigned int    const bpp,
+                       Colormap        const colormap,
+                       unsigned int    const newMaxval,
+                       unsigned char * const rowdata) {
+
+    unsigned int col;
+    unsigned char *outptr;
+    unsigned char outbyte;
+        /* Accumulated bits to be output */
+    unsigned char outbit;
+        /* The lowest bit number we want to access for this pixel */
+
+    outbyte = 0x00;
+    outptr = rowdata;
+
+    for (outbit = 8 - bpp, col = 0; col < cols; ++col) {
+        unsigned int color;
+        if (!colormap) {
+            /* we assume grayscale, and use simple scaling */
+            color = (PNM_GET1(xelrow[col]) * newMaxval)/maxval;
+            if (color > newMaxval)
+                pm_error("oops.  Bug in color re-calculation code.  "
+                         "color of %u.", color);
+            color = newMaxval - color; /* note grayscale maps are inverted */
+        } else {
+            Color_s const temp_color =
+                ((((PPM_GETR(xelrow[col])*newMaxval)/maxval)<<16) 
+                 | (((PPM_GETG(xelrow[col])*newMaxval)/maxval)<<8)
+                 | (((PPM_GETB(xelrow[col])*newMaxval)/maxval)));
+            Color const found = (bsearch (&temp_color,
+                                          colormap->color_entries, 
+                                          colormap->ncolors,
+                                          sizeof(Color_s), 
+                                          palmcolor_compare_colors));
+            if (!found) {
+                pm_error("Color %d:%d:%d not found in colormap.  "
+                         "Try using pnmquant to reduce the "
+                         "number of colors.",
+                         PPM_GETR(xelrow[col]), 
+                         PPM_GETG(xelrow[col]), 
+                         PPM_GETB(xelrow[col]));
+            }
+            color = (*found >> 24) & 0xFF;
+        }
+
+        if (color > newMaxval)
+            pm_error("oops.  Bug in color re-calculation code.  "
+                     "color of %u.", color);
+        outbyte |= (color << outbit);
+        if (outbit == 0) {
+            /* Bit buffer is full.  Flush to to rowdata. */
+            *outptr++ = outbyte;
+            outbyte = 0x00;
+            outbit = 8 - bpp;
+        } else
+            outbit -= bpp;
+    }
+    if ((cols % (8 / bpp)) != 0) {
+        /* Flush bits remaining in the bit buffer to rowdata */
+        *outptr++ = outbyte;
+    }
+}
+
+
+struct seqBuffer {
+/*----------------------------------------------------------------------------
+   A buffer to which one can write bytes sequentially.
+-----------------------------------------------------------------------------*/
+    char * buffer;
+    unsigned int allocatedSize;
+    unsigned int occupiedSize;
+};
+
+
+static void
+createBuffer(struct seqBuffer ** const bufferPP) {
+
+    struct seqBuffer * bufferP;
+
+    MALLOCVAR_NOFAIL(bufferP);
+
+    bufferP->allocatedSize = 4096;
+    MALLOCARRAY(bufferP->buffer, bufferP->allocatedSize);
+    if (bufferP == NULL)
+        pm_error("Unable to allocate %u bytes of buffer", 
+                 bufferP->allocatedSize);
+    bufferP->occupiedSize = 0;
+
+    *bufferPP = bufferP;
+}
+
+
+
+static void
+destroyBuffer(struct seqBuffer * const bufferP) {
+
+    free(bufferP->buffer);
+    free(bufferP);
+}
+
+
+
+static void
+addByteToBuffer(struct seqBuffer * const bufferP,
+                unsigned char      const newByte) {
+
+    assert(bufferP->allocatedSize >= bufferP->occupiedSize);
+
+    if (bufferP->allocatedSize == bufferP->occupiedSize) {
+        bufferP->allocatedSize *= 2;
+        REALLOCARRAY(bufferP->buffer, bufferP->allocatedSize);
+        if (bufferP->buffer == NULL)
+            pm_error("Couldn't (re)allocate %u bytes of memory "
+                     "for buffer.", bufferP->allocatedSize);
+    }
+    bufferP->buffer[bufferP->occupiedSize++] = newByte;
+}
+
+
+
+static unsigned int
+bufferLength(struct seqBuffer * const bufferP) {
+    return bufferP->occupiedSize;
+}
+
+
+
+static void
+writeOutBuffer(struct seqBuffer * const bufferP,
+               FILE *             const fileP) {
+
+    size_t bytesWritten;
+
+    bytesWritten = fwrite(bufferP->buffer, sizeof(char), 
+                          bufferP->occupiedSize, fileP);
+
+    if (bytesWritten != bufferP->occupiedSize)
+        pm_error("fwrite() failed to write out the buffer.");
+}
+
+
+
+static void
+copyRowToBuffer(const unsigned char * const rowdata,
+                unsigned int          const rowbytes,
+                struct seqBuffer *    const rasterBufferP) {
+
+    unsigned int pos;
+    for (pos = 0; pos < rowbytes; ++pos)
+        addByteToBuffer(rasterBufferP, rowdata[pos]);
+} 
+
+
+
+static void
+scanlineCompressAndBufferRow(const unsigned char * const rowdata,
+                             unsigned int          const rowbytes,
+                             struct seqBuffer *    const rasterBufferP,
+                             const unsigned char * const lastrow) {
+/*----------------------------------------------------------------------------
+   Take the raw Palm Bitmap row 'rowdata', which is 'rowbytes'
+   columns, and add the scanline-compressed representation of it to
+   the buffer with handle 'rasterBufferP'.
+
+   'lastrow' is the raw contents of the row immediately before the one
+   we're compressing -- i.e. we compress with respect to that row.  This
+   function does not work on the first row of an image.
+-----------------------------------------------------------------------------*/
+    unsigned int pos;
+
+    for (pos = 0;  pos < rowbytes;  pos += 8) {
+        unsigned int const limit = MIN(rowbytes - pos, 8);
+
+        unsigned char map;
+            /* mask indicating which of the next 8 pixels are
+               different from the previous row, and therefore present
+               in the file immediately following the map byte.  
+            */
+        unsigned char differentPixels[8];
+        unsigned char *outptr;
+        unsigned char outbit;
+            
+        for (outbit = 0, map = 0x00, outptr = differentPixels;
+             outbit < limit;  
+             ++outbit) {
+            if (!lastrow 
+                || (lastrow[pos + outbit] != rowdata[pos + outbit])) {
+                map |= (1 << (7 - outbit));
+                *outptr++ = rowdata[pos + outbit];
+            }
+        }
+
+        addByteToBuffer(rasterBufferP, map);
+        {
+            unsigned int j;
+            for (j = 0; j < (outptr - differentPixels); ++j)
+                addByteToBuffer(rasterBufferP, differentPixels[j]);
+        }
+    }
+}
+
+
+
+static void
+rleCompressAndBufferRow(const unsigned char * const rowdata,
+                        unsigned int          const rowbytes,
+                        struct seqBuffer *    const rasterBufferP) {
+/*----------------------------------------------------------------------------
+   Take the raw Palm Bitmap row 'rowdata', which is 'rowbytes' bytes,
+   and add the rle-compressed representation of it to the buffer with
+   handle 'rasterBufferP'.
+-----------------------------------------------------------------------------*/
+    unsigned int pos;
+
+    /* we output a count of the number of bytes a value is
+       repeated, followed by that byte value 
+    */
+    pos = 0;
+    while (pos < rowbytes) {
+        unsigned int repeatcount;
+        for (repeatcount = 1;  
+             repeatcount < (rowbytes - pos) && repeatcount  < 255;  
+             ++repeatcount)
+            if (rowdata[pos + repeatcount] != rowdata[pos])
+                break;
+
+        addByteToBuffer(rasterBufferP, repeatcount);
+        addByteToBuffer(rasterBufferP, rowdata[pos]);
+        pos += repeatcount;
+    }
+}
+
+
+
+static void
+computeNextPackbitsRun(const unsigned char * const rowdata,
+                       unsigned int          const rowbytes,
+                       unsigned int          const startPos,
+                       unsigned int *        const nextPosP,
+                       unsigned char *       const output,
+                       int *                 const countP) {
+
+    unsigned int pos;
+    int count;
+    
+    pos = startPos;
+    count = 0;
+    
+    if (rowdata[pos] == rowdata[pos + 1]) {
+        ++pos;
+        --count;
+        while ((count > -127) && (pos < (rowbytes - 1)) &&
+               (rowdata[pos] == rowdata[pos + 1])) {
+            ++pos;
+            --count;
+        }
+        ++pos;  /* push pos past end of this run */
+    } else {
+        output[count] = rowdata[pos];
+        ++pos;
+        while ((count < 127) && (pos < (rowbytes - 1)) && 
+               (rowdata[pos] != rowdata[pos + 1])) {
+            ++count;
+            output[count] = rowdata[pos];
+            ++pos;
+        }
+        /* trailing literal */
+        if ((count < 127) && (pos == (rowbytes - 1)) &&
+            (rowdata[pos - 1] != rowdata[pos])) {
+            ++count;
+            output[count] = rowdata[pos];
+            ++pos;
+        }
+    }
+    *nextPosP = pos;
+    *countP = count;
+}
+
+
+
+static void
+addPackbitsRunToBuffer(const unsigned char * const rowdata,
+                       unsigned int          const rowbytes,
+                       unsigned int          const pos,
+                       unsigned char *       const output,
+                       int                   const count,
+                       struct seqBuffer *    const rasterBufferP) {
+
+    addByteToBuffer(rasterBufferP, (unsigned char)(signed char)count);
+    if (count < 0) {
+        addByteToBuffer(rasterBufferP, rowdata[pos - 1]);
+    } else {
+        unsigned int j;
+        for (j = 0; j <= count; j++)
+            addByteToBuffer(rasterBufferP, output[j]);
+    }
+    
+    if (pos == (rowbytes - 1) && (rowdata[pos - 1] != rowdata[pos])) {
+        /* orphaned byte, treat as literal */
+        addByteToBuffer(rasterBufferP, 0);
+        addByteToBuffer(rasterBufferP, rowdata[pos]);
+    }
+}
+
+
+
+static void
+packbitsCompressAndBufferRow(const unsigned char * const rowdata,
+                             unsigned int          const rowbytes,
+                             struct seqBuffer *    const rasterBufferP) {
+/*----------------------------------------------------------------------------
+   Take the raw Palm Bitmap row 'rowdata', which is 'rowbytess' bytes, and
+   add the packbits-compressed representation of it to the buffer 
+   with handle 'rasterBufferP'.
+-----------------------------------------------------------------------------*/
+    unsigned int position;
+        /* byte position within the row */
+
+    position = 0;  /* Start at beginning of row */
+
+    while (position < rowbytes - 1) {
+        unsigned char output[128];
+        int count;
+
+        computeNextPackbitsRun(rowdata, rowbytes, position, 
+                               &position, output, &count);
+
+        addPackbitsRunToBuffer(rowdata, rowbytes, position, output, count,
+                               rasterBufferP);
+    }
+}
+
+
+
+static void
+bufferRowFromRawRowdata(const unsigned char *  const rowdata,
+                        unsigned int           const rowbytes,
+                        enum compressionType   const compression,
+                        const unsigned char *  const lastrow,
+                        struct seqBuffer *     const rasterBufferP) {
+/*----------------------------------------------------------------------------
+   Starting with a raw (uncompressed) Palm raster line, do the
+   compression identified by 'compression' and add the compressed row
+   to the buffer with handle 'rasterBufferP'.
+
+   If 'compression' indicates scanline compression, 'lastrow' is the
+   row immediately preceding this one in the image (and this function
+   doesn't work on the first row of an image).  Otherwise, 'lastrow'
+   is meaningless.
+-----------------------------------------------------------------------------*/
+    switch (compression) {
+    case COMP_NONE:
+        copyRowToBuffer(rowdata, rowbytes, rasterBufferP);
+        break;
+    case COMP_SCANLINE:
+        scanlineCompressAndBufferRow(rowdata, rowbytes, rasterBufferP, 
+                                     lastrow);
+        break;
+    case COMP_RLE:
+        rleCompressAndBufferRow(rowdata, rowbytes, rasterBufferP);
+        break;
+    case COMP_PACKBITS:
+        packbitsCompressAndBufferRow(rowdata, rowbytes, rasterBufferP);
+        break;
+    }
+}
+
+
+
+static void
+bufferRow(const xel *          const xelrow,
+          unsigned int         const cols,
+          xelval               const maxval,
+          unsigned int         const rowbytes,
+          unsigned int         const bpp,
+          unsigned int         const newMaxval,
+          enum compressionType const compression,
+          bool                 const directColor,
+          Colormap             const colormap,
+          unsigned char *      const rowdata,
+          unsigned char *      const lastrow,
+          struct seqBuffer *   const rasterBufferP) {
+/*----------------------------------------------------------------------------
+   Add a row of the Palm Bitmap raster to buffer 'rasterBufferP'.
+   
+   'xelrow' is the image contents of row.  It is 'cols' columns wide.
+
+   If 'compression' indicates scanline compression, 'lastrow' is the
+   row immediately preceding this one in the image (and this function
+   doesn't work on the first row of an image).  Otherwise, 'lastrow'
+   is meaningless.
+
+   'rowdata' is a work buffer 'rowbytes' in size.
+-----------------------------------------------------------------------------*/
+    if (directColor)
+        computeRawRowDirectColor(xelrow, cols, maxval, rowdata);
+    else 
+        computeRawRowNonDirect(xelrow, cols, maxval, bpp, colormap, newMaxval,
+                               rowdata);
+
+    bufferRowFromRawRowdata(rowdata, rowbytes, compression,
+                            lastrow, rasterBufferP);
+}
+
+
+
+static void 
+bufferRaster(xel **               const xels,
+             unsigned int         const cols,
+             unsigned int         const rows,
+             xelval               const maxval,
+             unsigned int         const rowbytes,
+             unsigned int         const bpp,
+             unsigned int         const newMaxval,
+             enum compressionType const compression,
+             bool                 const directColor,
+             Colormap             const colormap,
+             struct seqBuffer **  const rasterBufferPP) {
+    
+    unsigned char * rowdata;
+    unsigned char * lastrow;
+    unsigned int row;
+
+    createBuffer(rasterBufferPP);
+    
+    MALLOCARRAY_NOFAIL(rowdata, rowbytes);
+    MALLOCARRAY_NOFAIL(lastrow, rowbytes);
+
+    /* And write out the data. */
+    for (row = 0; row < rows; ++row) {
+        bufferRow(xels[row], cols, maxval, rowbytes, bpp, newMaxval,
+                  compression,
+                  directColor, colormap, rowdata, row > 0 ? lastrow : NULL,
+                  *rasterBufferPP);
+
+        if (compression == COMP_SCANLINE)
+            memcpy(lastrow, rowdata, rowbytes);
+    }
+    free(lastrow);
+    free(rowdata);
+}
+
+
+
+static void
+computeOffsetStuff(bool                 const offsetWanted,
+                   unsigned int         const version,
+                   bool                 const directColor,
+                   enum compressionType const compression,
+                   bool                 const colormap,
+                   unsigned int         const colormapColorCount,
+                   unsigned int         const sizePlusRasterSize,
+                   unsigned int *       const nextDepthOffsetP,
+                   unsigned int *       const nextBitmapOffsetP,
+                   unsigned int *       const padBytesRequiredP) {
+    
+    if (offsetWanted) {
+        /* Offset is measured in 4-byte words (double words in
+           Intel/Microsoft terminology).  Account for header,
+           colormap, and raster size and round up 
+        */
+        unsigned int const headerSize = ((version < 3) ? 16 : 24);
+        unsigned int const colormapSize =
+            (colormap ? (2 + colormapColorCount * 4) : 0);
+        if (version < 3) {
+            unsigned int const directSize = 
+                (directColor && version < 3) ? 8 : 0; 
+            if (compression != COMP_NONE && sizePlusRasterSize > USHRT_MAX)
+                pm_error("Oversized compressed bitmap: %u bytes",
+                         sizePlusRasterSize);
+            *padBytesRequiredP = 4 - (sizePlusRasterSize + headerSize + 
+                                      directSize + colormapSize) % 4;
+            *nextDepthOffsetP = 
+                (sizePlusRasterSize + headerSize + 
+                 directSize + colormapSize + *padBytesRequiredP) / 4;
+        } else {
+            if (compression != COMP_NONE && (sizePlusRasterSize >> 31) > 1)
+                pm_error("Oversized compressed bitmap: %u bytes",
+                         sizePlusRasterSize);
+            /* Does version 3 need padding? Probably won't hurt */
+            *padBytesRequiredP = 4 - (sizePlusRasterSize + headerSize +
+                                      colormapSize) % 4;
+            *nextBitmapOffsetP = sizePlusRasterSize + headerSize + 
+                colormapSize + *padBytesRequiredP;
+        }
+    } else {
+        *padBytesRequiredP = 0;
+        *nextDepthOffsetP = 0;
+        *nextBitmapOffsetP = 0;
+    }
+}
+
+
+
+static void
+writeRasterSize(unsigned int const sizePlusRasterSize,
+                unsigned int const version,
+                FILE *       const fileP) {
+/*----------------------------------------------------------------------------
+   Write to file 'fileP' a raster size field for a Palm Bitmap version
+   'version' header, indicating 'sizePlusRasterSize' bytes.
+-----------------------------------------------------------------------------*/
+    if (version < 3) 
+        pm_writebigshort(fileP, sizePlusRasterSize);
+    else
+        pm_writebiglong(fileP, sizePlusRasterSize);
+}
+
+
+
+static void
+writeBitmap(xel **               const xels,
+            unsigned int         const cols,
+            unsigned int         const rows,
+            xelval               const maxval,
+            unsigned int         const rowbytes,
+            unsigned int         const bpp,
+            unsigned int         const newMaxval,
+            enum compressionType const compression,
+            bool                 const transparent,
+            bool                 const directColor,
+            bool                 const offsetWanted,
+            bool                 const hasColormap,
+            Colormap             const colormap,
+            unsigned int         const transindex,
+            xel                  const transcolor,
+            unsigned int         const version,
+            unsigned int         const density,
+            bool                 const withdummy) {
+    
+    unsigned int sizePlusRasterSize;
+    unsigned int nextDepthOffset;
+    unsigned int nextBitmapOffset;
+        /* Offset from the beginning of the image we write to the beginning
+           of the next one, assuming user writes another one following this
+           one.
+           nextDepthOffset is used in encodings 1, 2 and is in 4 byte words 
+           nextBitmapOffset is used in encoding 3, is in 4 bytes 
+        */
+    unsigned int padBytesRequired;
+        /* Number of bytes of padding we need to put after the image in
+           order to align properly for User to add the next image to the
+           stream.
+        */
+    struct seqBuffer * rasterBufferP;
+
+    writeCommonHeader(cols, rows, rowbytes, compression, hasColormap, 
+                      transparent, directColor, bpp, version);
+    
+    bufferRaster(xels, cols, rows, maxval, rowbytes, bpp, newMaxval,
+                 compression, directColor, colormap, &rasterBufferP);
+
+    /* rasterSize itself takes 2 or 4 bytes */
+    if (version < 3)
+        sizePlusRasterSize = 2 + bufferLength(rasterBufferP);
+    else
+        sizePlusRasterSize = 4 + bufferLength(rasterBufferP);
+    
+    computeOffsetStuff(offsetWanted, version, directColor, compression,
+                       hasColormap, hasColormap ? colormap->ncolors : 0, 
+                       sizePlusRasterSize,
+                       &nextDepthOffset, &nextBitmapOffset,
+                       &padBytesRequired);
+
+    if (version < 3)
+        writeRemainingHeaderLow(nextDepthOffset, transindex, compression, bpp);
+    else
+        writeRemainingHeaderHigh(bpp, compression, density,
+                                 maxval, transparent, transcolor,
+                                 transindex, nextBitmapOffset);
+
+    writeColormap(hasColormap, colormap, directColor, bpp, 
+                  transparent, transcolor, maxval, version);
+
+    if (compression != COMP_NONE)
+        writeRasterSize(sizePlusRasterSize, version, stdout);
+
+    writeOutBuffer(rasterBufferP, stdout);
+
+    destroyBuffer(rasterBufferP);
+
+    {
+        unsigned int i;
+        for (i = 0; i < padBytesRequired; ++i)
+            fputc(0x00, stdout);
+    }
+
+    if (withdummy)
+        writeDummy();
+}        
+
+
+
+int 
+main( int argc, char **argv ) {
+    struct cmdline_info cmdline;
+    unsigned int version;
+    FILE* ifP;
+    xel** xels;
+    xel transcolor;
+    unsigned int transindex;
+    int rows, cols;
+    unsigned int rowbytes;
+    xelval maxval;
+    int format;
+    unsigned int bpp;
+    bool directColor;
+    unsigned int newMaxval;
+    Colormap colormap;
+    
+    /* Parse default params */
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    xels = pnm_readpnm(ifP, &cols, &rows, &maxval, &format);
+    pm_close(ifP);
+
+    if (cmdline.verbose)
+        pm_message("Input is %dx%d %s, maxval %d", 
+                   cols, rows, formatName(format), maxval);
+    
+    determinePalmFormat(cols, rows, maxval, format, xels, cmdline.depth,
+                        cmdline.maxdepth, cmdline.colormap, cmdline.verbose,
+                        &bpp, &directColor, &colormap);
+
+    newMaxval = (1 << bpp) - 1;
+
+    if (cmdline.transparent) 
+        findTransparentColor(cmdline.transparent, newMaxval, directColor,
+                             maxval, colormap, &transcolor, &transindex);
+    else 
+        transindex = 0;
+
+    rowbytes = ((cols + (16 / bpp -1)) / (16 / bpp)) * 2;    
+        /* bytes per row - always a word boundary */
+
+    version = bitmapVersion(bpp, cmdline.colormap, !!cmdline.transparent, 
+                            cmdline.compression, cmdline.density);
+
+    writeBitmap(xels, cols, rows, maxval,
+                rowbytes, bpp, newMaxval, cmdline.compression,
+                !!cmdline.transparent, directColor, cmdline.offset, 
+                cmdline.colormap, colormap, transindex, transcolor,
+                version, cmdline.density, cmdline.withdummy);
+    
+    return 0;
+}