about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2009-09-27 22:25:15 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2009-09-27 22:25:15 +0000
commit2a536013ae18d7f7b684d302256f0bf1c44a3692 (patch)
treea4a4b62adead04c4df2d9646d4f13080699d8253
parent0745c2e7833b1a9290330bd3bbaa9b376d8bdef6 (diff)
downloadnetpbm-mirror-2a536013ae18d7f7b684d302256f0bf1c44a3692.tar.gz
netpbm-mirror-2a536013ae18d7f7b684d302256f0bf1c44a3692.tar.xz
netpbm-mirror-2a536013ae18d7f7b684d302256f0bf1c44a3692.zip
Rebase Advanced series to trunk: Release 10.48.00
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@998 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r--GNUmakefile5
-rw-r--r--analyzer/pamsumm.c2
-rwxr-xr-xbuildtools/configure.pl143
-rw-r--r--common.mk34
-rw-r--r--converter/other/Makefile18
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_stream.c2
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_image.h2
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_types.h2
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig228
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/jp2_cod.c2
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h2
-rw-r--r--converter/other/pamtotiff.c74
-rw-r--r--converter/other/pngtopam.c705
-rw-r--r--converter/other/pngtopnm.c1234
-rw-r--r--converter/other/pngx.c95
-rw-r--r--converter/other/pngx.h29
-rw-r--r--converter/other/pnmtopng.c608
-rw-r--r--converter/other/tifftopnm.c88
-rw-r--r--converter/pbm/Makefile5
-rw-r--r--converter/pbm/cistopbm.c180
-rw-r--r--converter/pbm/pbmtocis.c170
-rw-r--r--doc/HISTORY48
-rw-r--r--editor/pamscale.c6
-rw-r--r--lib/Makefile2
-rw-r--r--lib/libpm.c99
-rw-r--r--lib/libsystem_dummy.c2
-rw-r--r--lib/pm.h15
-rw-r--r--lib/util/Makefile2
-rw-r--r--other/ppmsvgalib.c12
-rw-r--r--urt/rle_addhist.c2
-rw-r--r--urt/rle_getrow.c4
-rw-r--r--urt/rle_open_f.c4
-rw-r--r--urt/rle_putcom.c4
-rw-r--r--urt/scanargs.c4
-rw-r--r--version.mk4
35 files changed, 1562 insertions, 2274 deletions
diff --git a/GNUmakefile b/GNUmakefile
index 4b01cec2..780ec870 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -150,6 +150,11 @@ ifeq ($(HAVE_INT64),Y)
 else
 	echo "#define HAVE_INT64 0" >>$@
 endif	
+ifeq ($(DONT_HAVE_PROCESS_MGMT),Y)
+	echo "#define HAVE_FORK 0" >>$@
+else
+	echo "#define HAVE_FORK 1" >>$@
+endif
 	echo '/* pm_config.h.in FOLLOWS ... */' >>$@
 	cat $(SRCDIR)/pm_config.in.h >>$@
 	$(ENDIANGEN) >>$@
diff --git a/analyzer/pamsumm.c b/analyzer/pamsumm.c
index c9b8a7bf..ffb4a033 100644
--- a/analyzer/pamsumm.c
+++ b/analyzer/pamsumm.c
@@ -73,7 +73,7 @@ parseCommandLine(int argc, char ** const argv,
     } else if (maxSpec) {
         cmdlineP->function = FN_MAX;
     } else 
-        pm_error("You must specify one of -sum, -min, or -max");
+        pm_error("You must specify one of -sum, -min, -max, or -mean");
         
     if (argc-1 > 1)
         pm_error("Too many arguments (%d).  File spec is the only argument.",
diff --git a/buildtools/configure.pl b/buildtools/configure.pl
index 29887642..e82c229c 100755
--- a/buildtools/configure.pl
+++ b/buildtools/configure.pl
@@ -93,6 +93,29 @@ sub prompt($$) {
 
 
 
+sub promptYesNo($) {
+    my ($default) = $@;
+
+    my $retval;
+
+    while (!defined($retval)) {
+        my $response = prompt("(y)es or (n)o", $default);
+        
+        if (uc($response) =~ m{ ^ (Y|YES) $ }x)  {
+            $retval = $TRUE;
+        } elsif (uc($response) =~ m{ ^ (N|NO) $ }x)  {
+            $retval = $FALSE;
+        } else {
+            print("'$response' isn't one of the choices.  \n" .
+                  "You must choose 'yes' or 'no' (or 'y' or 'n').\n");
+        }
+    }
+
+    return $retval;
+}
+
+
+
 sub tmpdir() {
 # This is our approximation of File::Spec->tmpdir(), which became part of
 # basic Perl some time after Perl 5.005_03.
@@ -322,20 +345,8 @@ sub askAboutCygwin() {
         $default = "n";
     }
     
-    my $retval;
+    my $retval = promptYesNo($default);
 
-    while (!defined($retval)) {
-        my $response = prompt("(y)es or (n)o", $default);
-    
-        if (uc($response) =~ /^(Y|YES)$/)  {
-            $retval = $TRUE;
-        } elsif (uc($response) =~ /^(N|NO)$/)  {
-            $retval = $FALSE;
-        } else {
-            print("'$response' isn't one of the choices.  \n" .
-                  "You must choose 'yes' or 'no' (or 'y' or 'n').\n");
-        }
-    }
     return $retval;
 }
 
@@ -353,20 +364,7 @@ sub askAboutDjgpp() {
         $default = "n";
     }
     
-    my $retval;
-
-    while (!defined($retval)) {
-        my $response = prompt("(y)es or (n)o", $default);
-    
-        if (uc($response) =~ /^(Y|YES)$/)  {
-            $retval = $TRUE;
-        } elsif (uc($response) =~ /^(N|NO)$/)  {
-            $retval = $FALSE;
-        } else {
-            print("'$response' isn't one of the choices.  \n" .
-                  "You must choose 'yes' or 'no' (or 'y' or 'n').\n");
-        }
-    }
+    my $retval = promptYesNo($default);
 }
 
 
@@ -485,6 +483,49 @@ sub getPlatform() {
 
 
 
+sub getGccChoiceFromUser($) {
+    my ($platform) = @_;
+
+    my $retval;
+
+    print("GNU compiler or native operating system compiler (cc)?\n");
+    print("\n");
+
+    my $default;
+
+    if ($platform eq "SOLARIS" || $platform eq "SCO" ) {
+        $default = "gcc";
+    } else {
+        $default = "cc";
+    }
+
+    while (!defined($retval)) {
+        my $response = prompt("gcc or cc", $default);
+
+        if ($response eq "gcc") {
+            $retval = "gcc";
+        } elsif ($response eq "cc") {
+            $retval = "cc";
+        } else {
+            print("'$response' isn't one of the choices.  \n" .
+                  "You must choose 'gcc' or 'cc'.\n");
+        }
+    }
+    if ($retval eq 'gcc' && !commandExists('gcc')) {
+        print("WARNING: You selected the GNU compiler, " .
+              "but do not appear to have a program " .
+              "named 'gcc' in your PATH.  This may " .
+              "cause trouble later.  You may need to " .
+              "set the CC environment variable or CC " .
+              "makefile variable or install 'gcc'\n");
+    }
+    print("\n");
+
+    return $retval;
+}
+
+
+
 sub getCompiler($$$) {
     my ($platform, $subplatform, $compilerR) = @_;
 #-----------------------------------------------------------------------------
@@ -529,45 +570,23 @@ sub getCompiler($$$) {
                             "DARWIN"  => 1,
                             );
 
-    if ($gccUsualPlatform{$platform}) {
-        $$compilerR = "gcc";
-    } elsif ($platform eq "WINDOWS" && $subplatform eq "cygwin") {
-        $$compilerR = "gcc";
-    } elsif ($gccOptionalPlatform{$platform}) {
-        print("GNU compiler or native operating system compiler (cc)?\n");
-        print("\n");
-
-        my $default;
+    if (commandExists('x86_64-w64-mingw32-gcc')) {
+        printf("Do you want to use the Mingw-w64 Cross-Compiler?\n");
 
-        if ($platform eq "SOLARIS" || $platform eq "SCO" ) {
-            $default = "gcc";
-        } else {
-            $default = "cc";
+        if (promptYesNo('y') == "y") {
+            $$compilerR = 'x86_64-w64-mingw32-gcc';
         }
-
-        my $response = prompt("gcc or cc", $default);
-
-        if ($response eq "gcc") {
+    }
+    if (!defined($$compilerR)) {
+        if ($gccUsualPlatform{$platform}) {
             $$compilerR = "gcc";
-        } elsif ($response eq "cc") {
-            $$compilerR = "cc";
+        } elsif ($platform eq "WINDOWS" && $subplatform eq "cygwin") {
+            $$compilerR = "gcc";
+        } elsif ($gccOptionalPlatform{$platform}) {
+            $$compilerR = getGccChoiceFromUser($platform);
         } else {
-            print("'$response' isn't one of the choices.  \n" .
-                  "You must choose 'gcc' or 'cc'.\n");
-            exit 12;
-        }
-
-        if ($$compilerR eq 'gcc' && !commandExists('gcc')) {
-            print("WARNING: You selected the GNU compiler, " .
-                  "but do not appear to have a program " .
-                  "named 'gcc' in your PATH.  This may " .
-                  "cause trouble later.  You may need to " .
-                  "set the CC environment variable or CC " .
-                  "makefile variable or install 'gcc'\n");
+            $$compilerR = 'cc';
         }
-        print("\n");
-    } else {
-        $$compilerR = 'cc';
     }
 }
 
@@ -1135,6 +1154,8 @@ sub getLinuxsvgaLibrary($@) {
             $default = '/usr/lib/svgalib/libvga.so';
         } elsif (system('ldconfig -p | grep libvga &>/dev/null') == 0) {
             $default = 'libvga.so';
+        } elsif (-f('/usr/lib/libvga.a')) {
+            $default = '/usr/lib/libvga.a';
         } else {
             $default = 'none';
         }
diff --git a/common.mk b/common.mk
index 9e13a7c8..ea7e5807 100644
--- a/common.mk
+++ b/common.mk
@@ -76,7 +76,15 @@ include $(SRCDIR)/version.mk
 # fully made.
 .DELETE_ON_ERROR:
 
-NETPBM_INCLUDES := -Iimportinc -I$(SRCDIR)/$(SUBDIR)
+# -I importinc/netpbm is a backward compatibility thing.  Really, the source
+# file should refer to e.g. "netpbm/pam.h" but for historical reasons, most
+# refer to "pam.h" and we'll probably never have the energy to convert them
+# all.  The reason the file exists as importinc/netpbm/pam.h rather than just
+# importinc/pam.h (as it did originally) is that it lives on a user's system
+# as <netpbm/pam.h>, and therefore all _exported_ header files do say
+# "<netpbm/pam.h>.
+
+NETPBM_INCLUDES := -Iimportinc -Iimportinc/netpbm -I$(SRCDIR)/$(SUBDIR)
 
 # -I. is needed when builddir != srcdir
 INCLUDES = -I. $(COMP_INCLUDES) $(NETPBM_INCLUDES) $(EXTERN_INCLUDES)
@@ -133,33 +141,27 @@ IMPORTINC_HEADERS := \
   $(IMPORTINC_LIB_HEADERS) \
   $(IMPORTINC_LIB_UTIL_HEADERS)
 
-IMPORTINC_ROOT_FILES := $(IMPORTINC_ROOT_HEADERS:%=importinc/%)
-IMPORTINC_LIB_FILES := $(IMPORTINC_LIB_HEADERS:%=importinc/%)
-IMPORTINC_LIB_UTIL_FILES := $(IMPORTINC_LIB_UTIL_HEADERS:%=importinc/%)
+IMPORTINC_ROOT_FILES := $(IMPORTINC_ROOT_HEADERS:%=importinc/netpbm/%)
+IMPORTINC_LIB_FILES := $(IMPORTINC_LIB_HEADERS:%=importinc/netpbm/%)
+IMPORTINC_LIB_UTIL_FILES := $(IMPORTINC_LIB_UTIL_HEADERS:%=importinc/netpbm/%)
 
 importinc: \
-  importinc/netpbm \
   $(IMPORTINC_ROOT_FILES) \
   $(IMPORTINC_LIB_FILES) \
   $(IMPORTINC_LIB_UTIL_FILES) \
 
-importinc/netpbm:
-	mkdir -p importinc
-	rm -f $@
-	$(SYMLINK) . $@
-
-$(IMPORTINC_ROOT_FILES):importinc/%:$(BUILDDIR)/%
-	mkdir -p importinc
+$(IMPORTINC_ROOT_FILES):importinc/netpbm/%:$(BUILDDIR)/%
+	mkdir -p importinc/netpbm
 	rm -f $@
 	$(SYMLINK) $< $@
 
-$(IMPORTINC_LIB_FILES):importinc/%:$(SRCDIR)/lib/%
-	mkdir -p importinc
+$(IMPORTINC_LIB_FILES):importinc/netpbm/%:$(SRCDIR)/lib/%
+	mkdir -p importinc/netpbm
 	rm -f $@
 	$(SYMLINK) $< $@
 
-$(IMPORTINC_LIB_UTIL_FILES):importinc/%:$(SRCDIR)/lib/util/%
-	mkdir -p importinc
+$(IMPORTINC_LIB_UTIL_FILES):importinc/netpbm/%:$(SRCDIR)/lib/util/%
+	mkdir -p importinc/netpbm
 	rm -f $@
 	$(SYMLINK) $< $@
 
diff --git a/converter/other/Makefile b/converter/other/Makefile
index 5231bffa..83676aaa 100644
--- a/converter/other/Makefile
+++ b/converter/other/Makefile
@@ -96,7 +96,7 @@ endif
 BINARIES = $(PORTBINARIES) pnmtorast rasttopnm
 
 ifeq ($(HAVE_PNGLIB),Y)
-  BINARIES += pnmtopng pngtopnm pngtopam pamrgbatopng
+  BINARIES += pnmtopng pngtopam pamrgbatopng
 endif
 ifneq ($(JPEGLIB),NONE)
   BINARIES += jpegtopnm pnmtojpeg
@@ -120,6 +120,7 @@ MERGEBINARIES = $(BINARIES)
 EXTRA_OBJECTS = exif.o rast.o bmepsoe.o
 ifeq ($(HAVE_PNGLIB),Y)
   EXTRA_OBJECTS += pngtxt.o
+  EXTRA_OBJECTS += pngx.o
 endif
 ifneq ($(JPEGLIB),NONE)
   EXTRA_OBJECTS += jpegdatasource.o
@@ -158,13 +159,13 @@ else
   PNGLIB_LIBOPTS = $(shell libpng-config --ldflags)
 endif
 
-pngtopnm pngtopam: %: %.o $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $@.o \
+pngtopam: %: %.o pngx.o $(NETPBMLIB) $(LIBOPT)
+	$(LD) -o $@ $@.o pngx.o \
 	  $(shell $(LIBOPT) $(NETPBMLIB)) \
 	  $(PNGLIB_LIBOPTS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
 
-pnmtopng: %: %.o pngtxt.o $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $@.o pngtxt.o \
+pnmtopng: %: %.o pngx.o pngtxt.o $(NETPBMLIB) $(LIBOPT)
+	$(LD) -o $@ $@.o pngx.o pngtxt.o \
 	  $(shell $(LIBOPT) $(NETPBMLIB)) \
 	  $(PNGLIB_LIBOPTS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
 
@@ -244,4 +245,9 @@ ifneq ($(TIFF_PREREQ_MISSING),Y)
 # In October 2005, pamtotiff replaced pnmtotiff
 	cd $(PKGDIR)/bin ; \
 	$(SYMLINK) pamtotiff$(EXE) pnmtotiff
-endif
\ No newline at end of file
+endif
+ifeq ($(HAVE_PNGLIB),Y)
+# In September 2009, pngtopam replaced pngtopnm
+	cd $(PKGDIR)/bin ; \
+	$(SYMLINK) pngtopam$(EXE) pngtopnm
+endif
diff --git a/converter/other/jpeg2000/libjasper/base/jas_stream.c b/converter/other/jpeg2000/libjasper/base/jas_stream.c
index 4c84e6c2..19dc7c11 100644
--- a/converter/other/jpeg2000/libjasper/base/jas_stream.c
+++ b/converter/other/jpeg2000/libjasper/base/jas_stream.c
@@ -130,7 +130,7 @@
 #include <io.h>
 #endif
 
-#include "pm.h"
+#include "netpbm/pm.h"
 
 #include "jasper/jas_types.h"
 #include "jasper/jas_stream.h"
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h
index d6456f7c..558f4368 100644
--- a/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h
@@ -123,7 +123,7 @@
 * Includes.
 \******************************************************************************/
 
-#include "pm_c_util.h"
+#include "netpbm/pm_c_util.h"
 #include <jasper/jas_stream.h>
 #include <jasper/jas_seq.h>
 
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h
index 4d3a4988..fbcb2ffb 100644
--- a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h
@@ -4,7 +4,7 @@
    In Netpbm, we do that with pm_config.h, and the original Jasper
    method doesn't work.
 */
-#include "pm_config.h"
+#include "netpbm/pm_config.h"
 
 
 /* The below macro is intended to be used for type casts.  By using this
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig
deleted file mode 100644
index 10c1152d..00000000
--- a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (c) 1999-2000 Image Power, Inc. and the University of
- *   British Columbia.
- * Copyright (c) 2001-2002 Michael David Adams.
- * All rights reserved.
- */
-
-/* __START_OF_JASPER_LICENSE__
- * 
- * JasPer Software License
- * 
- * IMAGE POWER JPEG-2000 PUBLIC LICENSE
- * ************************************
- * 
- * GRANT:
- * 
- * Permission is hereby granted, free of charge, to any person (the "User")
- * obtaining a copy of this software and associated documentation, to deal
- * in the JasPer Software without restriction, including without limitation
- * the right to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the JasPer Software (in source and binary forms),
- * and to permit persons to whom the JasPer Software is furnished to do so,
- * provided further that the License Conditions below are met.
- * 
- * License Conditions
- * ******************
- * 
- * A.  Redistributions of source code must retain the above copyright notice,
- * and this list of conditions, and the following disclaimer.
- * 
- * B.  Redistributions in binary form must reproduce the above copyright
- * notice, and this list of conditions, and the following disclaimer in
- * the documentation and/or other materials provided with the distribution.
- * 
- * C.  Neither the name of Image Power, Inc. nor any other contributor
- * (including, but not limited to, the University of British Columbia and
- * Michael David Adams) may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * 
- * D.  User agrees that it shall not commence any action against Image Power,
- * Inc., the University of British Columbia, Michael David Adams, or any
- * other contributors (collectively "Licensors") for infringement of any
- * intellectual property rights ("IPR") held by the User in respect of any
- * technology that User owns or has a right to license or sublicense and
- * which is an element required in order to claim compliance with ISO/IEC
- * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
- * rights worldwide arising under statutory or common law, and whether
- * or not perfected, including, without limitation, all (i) patents and
- * patent applications owned or licensable by User; (ii) rights associated
- * with works of authorship including copyrights, copyright applications,
- * copyright registrations, mask work rights, mask work applications,
- * mask work registrations; (iii) rights relating to the protection of
- * trade secrets and confidential information; (iv) any right analogous
- * to those set forth in subsections (i), (ii), or (iii) and any other
- * proprietary rights relating to intangible property (other than trademark,
- * trade dress, or service mark rights); and (v) divisions, continuations,
- * renewals, reissues and extensions of the foregoing (as and to the extent
- * applicable) now existing, hereafter filed, issued or acquired.
- * 
- * E.  If User commences an infringement action against any Licensor(s) then
- * such Licensor(s) shall have the right to terminate User's license and
- * all sublicenses that have been granted hereunder by User to other parties.
- * 
- * F.  This software is for use only in hardware or software products that
- * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
- * or right to this Software is granted for products that do not comply
- * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
- * from the ISO.
- * 
- * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
- * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
- * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
- * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
- * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
- * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
- * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
- * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
- * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
- * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
- * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
- * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
- * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
- * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
- * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
- * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
- * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
- * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
- * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
- * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
- * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
- * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
- * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
- * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
- * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
- * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
- * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
- * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
- * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
- * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
- * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
- * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
- * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
- * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
- * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
- * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
- * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
- * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
- * NOTICE SPECIFIED IN THIS SECTION.
- * 
- * __END_OF_JASPER_LICENSE__
- */
-
-/*
- * Primitive Types
- *
- * $Id$
- */
-
-#ifndef JAS_TYPES_H
-#define JAS_TYPES_H
-
-#if defined(HAVE_STDLIB_H)
-#include <stdlib.h>
-#endif
-#if defined(HAVE_STDDEF_H)
-#include <stddef.h>
-#endif
-#if defined(HAVE_SYS_TYPES_H)
-#include <sys/types.h>
-#endif
-
-#if defined(HAVE_STDBOOL_H)
-/*
- * The C language implementation does correctly provide the standard header
- * file "stdbool.h".
- */
-#include <stdbool.h>
-#else
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * The C language implementation does not provide the standard header file
- * "stdbool.h" as required by ISO/IEC 9899:1999.  Try to compensate for this
- * braindamage below.
- */
-#if !defined(bool)
-#define	bool	int
-#endif
-#if !defined(true)
-#define true	1
-#endif
-#if !defined(false)
-#define	false	0
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-/* pm_config.h defines the FAST integer types if possible (typically by
-   including <inttypes.h>.  If not, the following try to take care
-   of it.
-*/
-/**********/
-#if !defined(INT_FAST8_MAX)
-typedef signed char int_fast8_t;
-#define INT_FAST8_MIN	(-127)
-#define INT_FAST8_MAX	128
-#endif
-/**********/
-#if !defined(UINT_FAST8_MAX)
-typedef unsigned char uint_fast8_t;
-#define UINT_FAST8_MAX	255
-#endif
-/**********/
-#if !defined(INT_FAST16_MAX)
-typedef short int_fast16_t;
-#define INT_FAST16_MIN	SHRT_MIN
-#define INT_FAST16_MAX	SHRT_MAX
-#endif
-/**********/
-#if !defined(UINT_FAST16_MAX)
-typedef unsigned short uint_fast16_t;
-#define UINT_FAST16_MAX	USHRT_MAX
-#endif
-/**********/
-#if !defined(INT_FAST32_MAX)
-typedef int int_fast32_t;
-#define INT_FAST32_MIN	INT_MIN
-#define INT_FAST32_MAX	INT_MAX
-#endif
-/**********/
-#if !defined(UINT_FAST32_MAX)
-typedef unsigned int uint_fast32_t;
-#define UINT_FAST32_MAX	UINT_MAX
-#endif
-/**********/
-#if !defined(INT_FAST64_MAX)
-typedef int int_fast64_t;
-#define INT_FAST64_MIN	LLONG_MIN
-#define INT_FAST64_MAX	LLONG_MAX
-#endif
-/**********/
-#if !defined(UINT_FAST64_MAX)
-typedef unsigned int uint_fast64_t;
-#define UINT_FAST64_MAX	ULLONG_MAX
-#endif
-/**********/
-#endif
-
-/* The below macro is intended to be used for type casts.  By using this
-  macro, type casts can be easily located in the source code with
-  tools like "grep". */
-#define	JAS_CAST(t, e) \
-	((t) (e))
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c b/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c
index 4769c408..cb70fd24 100644
--- a/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c
+++ b/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c
@@ -123,7 +123,7 @@
 #include <assert.h>
 #include <stdlib.h>
 
-#include "pm_c_util.h"
+#include "netpbm/pm_c_util.h"
 
 #include "jasper/jas_stream.h"
 #include "jasper/jas_malloc.h"
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h b/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h
index 914f8e8a..5f99e021 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h
@@ -123,7 +123,7 @@
 * Includes.
 \*****************************************************************************/
 
-#include "pm_c_util.h"
+#include "netpbm/pm_c_util.h"
 #include "jasper/jas_types.h"
 
 /*****************************************************************************\
diff --git a/converter/other/pamtotiff.c b/converter/other/pamtotiff.c
index 9fac4b47..59261a99 100644
--- a/converter/other/pamtotiff.c
+++ b/converter/other/pamtotiff.c
@@ -339,34 +339,6 @@ parseCommandLine(int                        argc,
 
 
 static void
-putSample(sample           const s,
-          sample           const maxval,
-          sample           const tiff_maxval,
-          unsigned int     const bitspersample,
-          unsigned char ** const tPP) {
-
-    xelval s2;
-
-    s2 = s;
-    if (maxval != tiff_maxval)
-        s2 = s * tiff_maxval / maxval;
-    if (bitspersample > 8) {
-        *((unsigned short *)(*tPP)) = s2;
-        (*tPP) += sizeof(short);
-    } else
-        *(*tPP)++ = s2 & 0xff;
-}
-
-
-
-
-/* Note: PUTSAMPLE doesn't work if bitspersample is 1-4. */
-
-#define PUTSAMPLE putSample(s, maxval, tiff_maxval, bitspersample, &tP);
-
-
-
-static void
 fillRowOfSubBytePixels(struct pam *    const pamP,
                        const tuple *   const tuplerow,
                        unsigned char * const buf,
@@ -443,6 +415,36 @@ fillRowOfSubBytePixels(struct pam *    const pamP,
 
 
 static void
+putSample(sample           const s,
+          sample           const maxval,
+          sample           const tiffMaxval,
+          unsigned int     const bitspersample,
+          bool             const minIsWhite,
+          unsigned char ** const tPP) {
+
+    /* Until release 10.48 (September 2009), we ignored the min-is-white
+       photometric (i.e. treated it like min-is-black).  Nobody has ever
+       complained, but it seems clear to me that that was wrong, so I
+       changed it.  We have always respected it for sub-byte samples,
+       and have always respected it going the other direction, in
+       Tifftopnm.
+       - Bryan.
+    */
+
+    xelval const s2 = maxval == tiffMaxval ? s : s * tiffMaxval / maxval;
+
+    xelval const s3 = minIsWhite ? tiffMaxval - s2 : s2;
+
+    if (bitspersample > 8) {
+        *((unsigned short *)(*tPP)) = s3;
+        (*tPP) += sizeof(short);
+    } else
+        *(*tPP)++ = s3 & 0xff;
+}
+
+
+
+static void
 fillRowOfWholeBytePixels(struct pam *    const pamP,
                          tuple *         const tuplerow,
                          unsigned char * const buf,
@@ -450,6 +452,8 @@ fillRowOfWholeBytePixels(struct pam *    const pamP,
                          unsigned short  const tiffMaxval,
                          unsigned int    const bitsPerSample) {
 
+    bool const minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
+
     unsigned int col;
     unsigned char * tP;
     unsigned int planes;
@@ -466,7 +470,7 @@ fillRowOfWholeBytePixels(struct pam *    const pamP,
         unsigned int plane;
         for (plane = 0; plane < planes; ++plane) {
             putSample(tuplerow[col][plane], pamP->maxval,
-                      tiffMaxval, bitsPerSample, &tP);
+                      tiffMaxval, bitsPerSample, minIsWhite, &tP);
             /* Advances tP */
         }
     }
@@ -495,15 +499,19 @@ writeScanLines(struct pam *   const pamP,
        it's 'buf' parameter, but here it is: Its format depends on the
        bits per pixel of the TIFF image.  If it's 16, 'buf' is an
        array of short (16 bit) integers, one per raster column.  If
-       it's 8, 'buf' is an array of characters (8 bit integers), one
-       per image column.  If it's less than 8, it's an array of characters,
-       each of which represents 1-8 raster columns, packed
+       it's 8, 'buf' is an array of 8 bit unsigned integers, one
+       per pixel sample.  If it's less than 8, it's an array of bytes,
+       each of which represents 1-8 pixel samples, packed
        into it in the order specified by the TIFF image's fill order,
        with don't-care bits on the right such that each byte contains only
-       whole pixels.
+       whole samples.
 
        In all cases, the array elements are in order left to right going
        from low array indices to high array indices.
+
+       The samples form pixel values according to the pixel format indicated
+       by the TIFF photometric.  E.g. if it is MINISWHITE, then a pixel is
+       one sample and a value of 0 for that sample means white.
     */
     MALLOCARRAY(buf, bytesperrow);
 
diff --git a/converter/other/pngtopam.c b/converter/other/pngtopam.c
index 534439d2..e88338c4 100644
--- a/converter/other/pngtopam.c
+++ b/converter/other/pngtopam.c
@@ -22,25 +22,13 @@
 #include <math.h>
 #include <float.h>
 #include <png.h>    /* includes zlib.h and setjmp.h */
-#define VERSION "2.37.4 (5 December 1999) +netpbm"
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
 #include "pam.h"
-
-/* A hack until we can remove direct access to png_info from the program */
-#if PNG_LIBPNG_VER >= 10400
-#define trans_values trans_color
-#define TRANS_ALPHA trans_alpha
-#else
-#define TRANS_ALPHA trans
-#endif
-
-typedef struct _jmpbuf_wrapper {
-  jmp_buf jmpbuf;
-} jmpbuf_wrapper;
+#include "pngx.h"
 
 enum alpha_handling {ALPHA_NONE, ALPHA_ONLY, ALPHA_MIX, ALPHA_IN};
 
@@ -73,7 +61,6 @@ typedef struct {
 
 static png_uint_16 maxval;
 static bool verbose;
-static jmpbuf_wrapper pngtopnm_jmpbuf_struct;
 
 
 static void
@@ -162,18 +149,138 @@ parseCommandLine(int                  argc,
 
 
 
+static void
+verifyFileIsPng(FILE *   const ifP,
+                size_t * const consumedByteCtP) {
+
+    unsigned char buffer[4];
+    size_t bytesRead;
+
+    bytesRead = fread(buffer, 1, sizeof(buffer), ifP);
+    if (bytesRead != sizeof(buffer))
+        pm_error("input file is empty or too short");
+
+    if (png_sig_cmp(buffer, (png_size_t) 0, (png_size_t) sizeof(buffer)) != 0)
+        pm_error("input file is not a PNG file "
+                 "(does not have the PNG signature in its first 4 bytes)");
+    else
+        *consumedByteCtP = bytesRead;
+}
+
+
+
+static unsigned int
+computePngLineSize(struct pngx * const pngxP) {
+
+    unsigned int const bytesPerSample =
+        pngxP->info_ptr->bit_depth == 16 ? 2 : 1;
+
+    unsigned int samplesPerPixel;
+
+    switch (pngxP->info_ptr->color_type) {
+    case PNG_COLOR_TYPE_GRAY_ALPHA: samplesPerPixel = 2; break;
+    case PNG_COLOR_TYPE_RGB:        samplesPerPixel = 3; break;
+    case PNG_COLOR_TYPE_RGB_ALPHA:  samplesPerPixel = 4; break;
+    default:                        samplesPerPixel = 1;
+    }
+
+    if (UINT_MAX / bytesPerSample / samplesPerPixel < pngxP->info_ptr->width)
+        pm_error("Width %u of PNG is uncomputably large",
+                 (unsigned int)pngxP->info_ptr->width);
+       
+    return pngxP->info_ptr->width * bytesPerSample * samplesPerPixel;
+}
+
+
+
+static void
+allocPngRaster(struct pngx * const pngxP,
+               png_byte ***  const pngImageP) {
+
+    unsigned int const lineSize = computePngLineSize(pngxP);
+
+    png_byte ** pngImage;
+    unsigned int row;
+
+    MALLOCARRAY(pngImage, pngxP->info_ptr->height);
+
+    if (pngImage == NULL)
+        pm_error("couldn't allocate space for %u PNG raster rows",
+                 (unsigned int)pngxP->info_ptr->height);
+
+    for (row = 0; row < pngxP->info_ptr->height; ++row) {
+        MALLOCARRAY(pngImage[row], lineSize);
+        if (pngImage[row] == NULL)
+            pm_error("couldn't allocate space for %uth row of PNG raster",
+                     row);
+    }
+    *pngImageP = pngImage;
+}
+
+
+
+static void
+freePngRaster(png_byte **   const pngRaster,
+              struct pngx * const pngxP) {
+
+    unsigned int row;
+
+    for (row = 0; row < pngxP->info_ptr->height; ++row)
+        free(pngRaster[row]);
+
+    free(pngRaster);
+}
+
+
+
+static void
+readPng(struct pngx * const pngxP,
+        FILE *        const ifP,
+        png_byte ***  const pngRasterP) {
+
+    size_t sigByteCt;
+    png_byte ** pngRaster;
+            
+    verifyFileIsPng(ifP, &sigByteCt);
+
+    /* Declare that we already read the signature bytes */
+    png_set_sig_bytes(pngxP->png_ptr, (int)sigByteCt);
+
+    png_init_io(pngxP->png_ptr, ifP);
+
+    png_read_info(pngxP->png_ptr, pngxP->info_ptr);
+
+    allocPngRaster(pngxP, &pngRaster);
+
+    if (pngxP->info_ptr->bit_depth < 8)
+        png_set_packing(pngxP->png_ptr);
+
+    png_read_image(pngxP->png_ptr, pngRaster);
+
+    png_read_end(pngxP->png_ptr, pngxP->info_ptr);
+
+    /* Note that some of info_ptr is not defined until png_read_end() 
+       completes.  That's because it comes from chunks that are at the
+       end of the stream.
+    */
+
+    *pngRasterP = pngRaster;
+}
+
+
+
 static png_uint_16
-get_png_val(const png_byte ** const pp,
-            int               const bit_depth) {
+getPngVal(const png_byte ** const pp,
+          int               const bitDepth) {
 
     png_uint_16 c;
     
-    if (bit_depth == 16)
-        c = (*((*pp)++)) << 8;
+    if (bitDepth == 16)
+        c = *(*pp)++ << 8;
     else
         c = 0;
 
-    c |= (*((*pp)++));
+    c |= *(*pp)++;
     
     return c;
 }
@@ -251,8 +358,8 @@ setTuple(const struct pam *  const pamP,
 
 
 static png_uint_16
-gamma_correct(png_uint_16 const v,
-              float       const g) {
+gammaCorrect(png_uint_16 const v,
+             float       const g) {
 
     if (g != -1.0)
         return (png_uint_16) ROUNDU(pow((double) v / maxval, (1.0 / g)) *
@@ -263,84 +370,80 @@ gamma_correct(png_uint_16 const v,
 
 
 
-static int iscolor (png_color c)
-{
-  return c.red != c.green || c.green != c.blue;
+static bool
+isColor(png_color const c) {
+
+    return c.red != c.green || c.green != c.blue;
 }
 
-static void save_text (png_info *info_ptr, FILE *tfp)
-{
-  int i, j, k;
-
-  for (i = 0 ; i < info_ptr->num_text ; i++) {
-    j = 0;
-    while (info_ptr->text[i].key[j] != '\0' && info_ptr->text[i].key[j] != ' ')
-      j++;    
-    if (info_ptr->text[i].key[j] != ' ') {
-      fprintf (tfp, "%s", info_ptr->text[i].key);
-      for (j = strlen (info_ptr->text[i].key) ; j < 15 ; j++)
-        putc (' ', tfp);
-    } else {
-      fprintf (tfp, "\"%s\"", info_ptr->text[i].key);
-      for (j = strlen (info_ptr->text[i].key) ; j < 13 ; j++)
-        putc (' ', tfp);
-    }
-    putc (' ', tfp); /* at least one space between key and text */
+
+
+static void
+saveText(struct pngx * const pngxP,
+         FILE *        const tfP) {
+
+    png_info * const info_ptr = pngxP->info_ptr;
+
+    unsigned int i;
+
+    for (i = 0 ; i < info_ptr->num_text; ++i) {
+        unsigned int j;
+        j = 0;
+
+        while (info_ptr->text[i].key[j] != '\0' &&
+               info_ptr->text[i].key[j] != ' ')
+            ++j;    
+
+        if (info_ptr->text[i].key[j] != ' ') {
+            fprintf(tfP, "%s", info_ptr->text[i].key);
+            for (j = strlen (info_ptr->text[i].key); j < 15; ++j)
+                putc(' ', tfP);
+        } else {
+            fprintf(tfP, "\"%s\"", info_ptr->text[i].key);
+            for (j = strlen (info_ptr->text[i].key); j < 13; ++j)
+                putc(' ', tfP);
+        }
+        putc(' ', tfP); /* at least one space between key and text */
     
-    for (j = 0 ; j < info_ptr->text[i].text_length ; j++) {
-      putc (info_ptr->text[i].text[j], tfp);
-      if (info_ptr->text[i].text[j] == '\n')
-        for (k = 0 ; k < 16 ; k++)
-          putc ((int)' ', tfp);
+        for (j = 0; j < info_ptr->text[i].text_length; ++j) {
+            putc(info_ptr->text[i].text[j], tfP);
+            if (info_ptr->text[i].text[j] == '\n') {
+                unsigned int k;
+                for (k = 0; k < 16; ++k)
+                    putc(' ', tfP);
+            }
+        }
+        putc('\n', tfP);
     }
-    putc ((int)'\n', tfp);
-  }
 }
 
-static void show_time (png_info *info_ptr)
-{
+
+
+static void
+showTime(struct pngx * const pngxP) {
+
     static const char * const month[] = {
         "", "January", "February", "March", "April", "May", "June",
         "July", "August", "September", "October", "November", "December"
     };
 
-  if (info_ptr->valid & PNG_INFO_tIME) {
-    pm_message ("modification time: %02d %s %d %02d:%02d:%02d",
-                info_ptr->mod_time.day, month[info_ptr->mod_time.month],
-                info_ptr->mod_time.year, info_ptr->mod_time.hour,
-                info_ptr->mod_time.minute, info_ptr->mod_time.second);
-  }
-}
-
-static void pngtopnm_error_handler (png_structp png_ptr, png_const_charp msg)
-{
-  jmpbuf_wrapper  *jmpbuf_ptr;
-
-  /* this function, aside from the extra step of retrieving the "error
-   * pointer" (below) and the fact that it exists within the application
-   * rather than within libpng, is essentially identical to libpng's
-   * default error handler.  The second point is critical:  since both
-   * setjmp() and longjmp() are called from the same code, they are
-   * guaranteed to have compatible notions of how big a jmp_buf is,
-   * regardless of whether _BSD_SOURCE or anything else has (or has not)
-   * been defined. */
-
-  pm_message("fatal libpng error: %s", msg);
-
-  jmpbuf_ptr = png_get_error_ptr(png_ptr);
-  if (jmpbuf_ptr == NULL) {
-      /* we are completely hosed now */
-      pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating.");
-  }
-
-  longjmp(jmpbuf_ptr->jmpbuf, 1);
+    if (pngxP->info_ptr->valid & PNG_INFO_tIME) {
+        pm_message("modification time: %02d %s %d %02d:%02d:%02d",
+                   pngxP->info_ptr->mod_time.day,
+                   month[pngxP->info_ptr->mod_time.month],
+                   pngxP->info_ptr->mod_time.year,
+                   pngxP->info_ptr->mod_time.hour,
+                   pngxP->info_ptr->mod_time.minute,
+                   pngxP->info_ptr->mod_time.second);
+    }
 }
 
 
 
 static void
-dump_png_info(png_info *info_ptr) {
+dumpPngInfo(struct pngx * const pngxP) {
 
+    png_info * const info_ptr = pngxP->info_ptr;
     const char *type_string;
     const char *filter_string;
 
@@ -457,73 +560,27 @@ dump_png_info(png_info *info_ptr) {
 
 
 
-static unsigned int
-computePngLineSize(png_info * const pngInfoP) {
-
-    unsigned int const bytesPerSample = pngInfoP->bit_depth == 16 ? 2 : 1;
-
-    unsigned int samplesPerPixel;
-
-    switch (pngInfoP->color_type) {
-    case PNG_COLOR_TYPE_GRAY_ALPHA: samplesPerPixel = 2; break;
-    case PNG_COLOR_TYPE_RGB:        samplesPerPixel = 3; break;
-    case PNG_COLOR_TYPE_RGB_ALPHA:  samplesPerPixel = 4; break;
-    default:                        samplesPerPixel = 1;
-    }
-
-    if (UINT_MAX / bytesPerSample / samplesPerPixel < pngInfoP->width)
-        pm_error("Width %u of PNG is uncomputably large",
-                 (unsigned int)pngInfoP->width);
-       
-    return pngInfoP->width * bytesPerSample * samplesPerPixel;
-}
-
-
-
-static void
-allocPngRaster(png_info *   const pngInfoP,
-               png_byte *** const pngImageP) {
-
-    unsigned int const lineSize = computePngLineSize(pngInfoP);
-
-    png_byte ** pngImage;
-    unsigned int row;
-
-    MALLOCARRAY(pngImage, pngInfoP->height);
-
-    if (pngImage == NULL)
-        pm_error("couldn't allocate space for %u PNG raster rows",
-                 (unsigned int)pngInfoP->height);
-
-    for (row = 0; row < pngInfoP->height; ++row) {
-        MALLOCARRAY(pngImage[row], lineSize);
-        if (pngImage[row] == NULL)
-            pm_error("couldn't allocate space for %uth row of PNG raster",
-                     row);
-    }
-    *pngImageP = pngImage;
-}
-
-
-
-static void
-freePngRaster(png_byte ** const pngRaster,
-              png_info *  const pngInfoP) {
+static const png_color_16 *
+transColor(struct pngx * const pngxP) {
 
-    unsigned int row;
+    png_bytep trans;
+    int numTrans;
+    png_color_16 * transColorP;
 
-    for (row = 0; row < pngInfoP->height; ++row)
-        free(pngRaster[row]);
+    assert(pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS));
+    
+    png_get_tRNS(pngxP->png_ptr, pngxP->info_ptr,
+                 &trans, &numTrans, &transColorP);
 
-    free(pngRaster);
+    return transColorP;
 }
 
 
 
 static bool
-isTransparentColor(pngcolor   const color,
-                   png_info * const pngInfoP,
-                   double     const totalgamma) {
+isTransparentColor(pngcolor      const color,
+                   struct pngx * const pngxP,
+                   double        const totalgamma) {
 /*----------------------------------------------------------------------------
    Return TRUE iff pixels of color 'color' are supposed to be transparent
    everywhere they occur.  Assume it's an RGB image.
@@ -532,8 +589,8 @@ isTransparentColor(pngcolor   const color,
 -----------------------------------------------------------------------------*/
     bool retval;
 
-    if (pngInfoP->valid & PNG_INFO_tRNS) {
-        const png_color_16 * const transColorP = &pngInfoP->trans_values;
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) {
+        const png_color_16 * const transColorP = transColor(pngxP);
 
         /* It seems odd that libpng lets you get gamma-corrected pixel
            values, but not gamma-corrected transparency or background
@@ -549,15 +606,15 @@ isTransparentColor(pngcolor   const color,
            pixels, and just do it ourselves.
         */
     
-        switch (pngInfoP->color_type) {
+        switch (pngxP->info_ptr->color_type) {
         case PNG_COLOR_TYPE_GRAY:
-            retval = color.r == gamma_correct(transColorP->gray, totalgamma);
+            retval = color.r == gammaCorrect(transColorP->gray, totalgamma);
             break;
         default:
             retval = 
-                color.r == gamma_correct(transColorP->red,   totalgamma) &&
-                color.g == gamma_correct(transColorP->green, totalgamma) &&
-                color.b == gamma_correct(transColorP->blue,  totalgamma);
+                color.r == gammaCorrect(transColorP->red,   totalgamma) &&
+                color.g == gammaCorrect(transColorP->green, totalgamma) &&
+                color.b == gammaCorrect(transColorP->blue,  totalgamma);
         }
     } else 
         retval = FALSE;
@@ -567,37 +624,17 @@ isTransparentColor(pngcolor   const color,
 
 
 
-#define SIG_CHECK_SIZE 4
-
 static void
-read_sig_buf(FILE * const ifP) {
-
-    unsigned char sig_buf[SIG_CHECK_SIZE];
-    size_t bytesRead;
-
-    bytesRead = fread(sig_buf, 1, SIG_CHECK_SIZE, ifP);
-    if (bytesRead != SIG_CHECK_SIZE)
-        pm_error ("input file is empty or too short");
-
-    if (png_sig_cmp(sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE)
-        != 0)
-        pm_error ("input file is not a PNG file");
-}
-
-
-
-static void
-setupGammaCorrection(png_struct * const png_ptr,
-                     png_info *   const info_ptr,
-                     float        const displaygamma,
-                     float *      const totalgammaP) {
+setupGammaCorrection(struct pngx * const pngxP,
+                     float         const displaygamma,
+                     float *       const totalgammaP) {
 
     if (displaygamma == -1.0)
         *totalgammaP = -1.0;
     else {
         float imageGamma;
-        if (info_ptr->valid & PNG_INFO_gAMA)
-            imageGamma = info_ptr->gamma;
+        if (pngxP->info_ptr->valid & PNG_INFO_gAMA)
+            imageGamma = pngxP->info_ptr->gamma;
         else {
             if (verbose)
                 pm_message("PNG doesn't specify image gamma.  Assuming 1.0");
@@ -611,12 +648,12 @@ setupGammaCorrection(png_struct * const png_ptr,
                            "display gamma %4.2f.  No conversion.",
                            imageGamma, displaygamma);
         } else {
-            png_set_gamma(png_ptr, displaygamma, imageGamma);
+            png_set_gamma(pngxP->png_ptr, displaygamma, imageGamma);
             *totalgammaP = imageGamma * displaygamma;
             /* in case of gamma-corrections, sBIT's as in the
                PNG-file are not valid anymore 
             */
-            info_ptr->valid &= ~PNG_INFO_sBIT;
+            pngxP->info_ptr->valid &= ~PNG_INFO_sBIT;
             if (verbose)
                 pm_message("image gamma is %4.2f, "
                            "converted for display gamma of %4.2f",
@@ -628,20 +665,25 @@ setupGammaCorrection(png_struct * const png_ptr,
 
 
 static bool
-paletteHasPartialTransparency(png_info * const info_ptr) {
+paletteHasPartialTransparency(struct pngx * const pngxP) {
 
     bool retval;
 
-    if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
-        if (info_ptr->valid & PNG_INFO_tRNS) {
+    if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+        if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) {
             bool foundGray;
             unsigned int i;
+            png_bytep trans;
+            int numTrans;
+            png_color_16 * transColorP;
             
+            png_get_tRNS(pngxP->png_ptr, pngxP->info_ptr,
+                         &trans, &numTrans, &transColorP);
+
             for (i = 0, foundGray = FALSE;
-                 i < info_ptr->num_trans && !foundGray;
+                 i < numTrans && !foundGray;
                  ++i) {
-                if (info_ptr->TRANS_ALPHA[i] != 0 &&
-                    info_ptr->TRANS_ALPHA[i] != maxval) {
+                if (trans[i] != 0 && trans[i] != maxval) {
                     foundGray = TRUE;
                 }
             }
@@ -657,9 +699,11 @@ paletteHasPartialTransparency(png_info * const info_ptr) {
 
 
 static void
-getComponentSbitFg(png_info * const pngInfoP,
-                   png_byte * const fgSbitP,
-                   bool *     const notUniformP) {
+getComponentSbitFg(struct pngx * const pngxP,
+                   png_byte *    const fgSbitP,
+                   bool *        const notUniformP) {
+
+    png_info * const pngInfoP = pngxP->info_ptr;
 
     if (pngInfoP->color_type == PNG_COLOR_TYPE_RGB ||
         pngInfoP->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
@@ -680,7 +724,7 @@ getComponentSbitFg(png_info * const pngInfoP,
 
 
 static void
-getComponentSbit(png_info *          const pngInfoP,
+getComponentSbit(struct pngx *       const pngxP,
                  enum alpha_handling const alphaHandling,
                  png_byte *          const componentSbitP,
                  bool *              const notUniformP) {
@@ -692,26 +736,26 @@ getComponentSbit(png_info *          const pngInfoP,
            the alpha Sbit
         */
         *notUniformP = false;
-        *componentSbitP = pngInfoP->sig_bit.alpha;
+        *componentSbitP = pngxP->info_ptr->sig_bit.alpha;
         break;
     case ALPHA_NONE:
     case ALPHA_MIX:
         /* We aren't going to produce an alpha channel, so we care only
            about the uniformity of the foreground channels.
         */
-        getComponentSbitFg(pngInfoP, componentSbitP, notUniformP);
+        getComponentSbitFg(pngxP, componentSbitP, notUniformP);
         break;
     case ALPHA_IN: {
         /* We care about both the foreground and the alpha */
         bool fgNotUniform;
         png_byte fgSbit;
         
-        getComponentSbitFg(pngInfoP, &fgSbit, &fgNotUniform);
+        getComponentSbitFg(pngxP, &fgSbit, &fgNotUniform);
 
         if (fgNotUniform)
             *notUniformP = true;
         else {
-            if (fgSbit == pngInfoP->sig_bit.alpha) {
+            if (fgSbit == pngxP->info_ptr->sig_bit.alpha) {
                 *notUniformP    = false;
                 *componentSbitP = fgSbit;
             } else
@@ -724,8 +768,8 @@ getComponentSbit(png_info *          const pngInfoP,
                  
 
 static void
-shiftPalette(png_info *   const pngInfoP,
-             unsigned int const shift) {
+shiftPalette(struct pngx * const pngxP,
+             unsigned int  const shift) {
 /*----------------------------------------------------------------------------
    Shift every component of every color in the PNG palette right by
    'shift' bits because sBIT chunk says only those are significant.
@@ -738,10 +782,10 @@ shiftPalette(png_info *   const pngInfoP,
     else {
         unsigned int i;
         
-        for (i = 0; i < pngInfoP->num_palette; ++i) {
-            pngInfoP->palette[i].red   >>= (8 - shift);
-            pngInfoP->palette[i].green >>= (8 - shift);
-            pngInfoP->palette[i].blue  >>= (8 - shift);
+        for (i = 0; i < pngxP->info_ptr->num_palette; ++i) {
+            pngxP->info_ptr->palette[i].red   >>= (8 - shift);
+            pngxP->info_ptr->palette[i].green >>= (8 - shift);
+            pngxP->info_ptr->palette[i].blue  >>= (8 - shift);
         }
     }
 }
@@ -749,12 +793,11 @@ shiftPalette(png_info *   const pngInfoP,
 
 
 static void
-computeMaxvalFromSbit(png_struct *        const pngP,
-                      png_info *          const pngInfoP,
+computeMaxvalFromSbit(struct pngx *       const pngxP,
                       enum alpha_handling const alphaHandling,
                       png_uint_16 *       const maxvalP,
                       bool *              const succeededP,
-                      int *               const errorlevelP) {
+                      int *               const errorLevelP) {
 
     /* sBIT handling is very tricky. If we are extracting only the
        image, we can use the sBIT info for grayscale and color images,
@@ -775,37 +818,38 @@ computeMaxvalFromSbit(png_struct *        const pngP,
            Meaningless if they aren't all the same (i.e. 'notUniform')
         */
 
-    getComponentSbit(pngInfoP, alphaHandling, &componentSigBit, &notUniform);
+    getComponentSbit(pngxP, alphaHandling, &componentSigBit, &notUniform);
 
     if (notUniform) {
         pm_message("This program cannot handle "
                    "different bit depths for color channels");
-        pm_message("writing file with %u bit resolution", pngInfoP->bit_depth);
+        pm_message("writing file with %u bit resolution",
+                   pngxP->info_ptr->bit_depth);
         *succeededP = false;
-        *errorlevelP = PNMTOPNG_WARNING_LEVEL;
+        *errorLevelP = PNMTOPNG_WARNING_LEVEL;
     } else if (componentSigBit > 15) {
         pm_message("Invalid PNG: says %u significant bits for a component; "
                    "max possible is 16.  Ignoring sBIT chunk.",
                    componentSigBit);
         *succeededP = false;
-        *errorlevelP = PNMTOPNG_WARNING_LEVEL;
+        *errorLevelP = PNMTOPNG_WARNING_LEVEL;
     } else {
         if (alphaHandling == ALPHA_MIX &&
-            (pngInfoP->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
-             pngInfoP->color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
-             paletteHasPartialTransparency(pngInfoP)))
+            (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+             pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
+             paletteHasPartialTransparency(pngxP)))
             *succeededP = false;
         else {
-            if (componentSigBit < pngInfoP->bit_depth) {
+            if (componentSigBit < pngxP->info_ptr->bit_depth) {
                 pm_message("Image has fewer significant bits, "
                            "writing file with %u bits", componentSigBit);
                 *maxvalP = (1l << componentSigBit) - 1;
                 *succeededP = true;
 
-                if (pngInfoP->color_type == PNG_COLOR_TYPE_PALETTE)
-                    shiftPalette(pngInfoP, componentSigBit);
+                if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+                    shiftPalette(pngxP, componentSigBit);
                 else
-                    png_set_shift(pngP, &pngInfoP->sig_bit);
+                    png_set_shift(pngxP->png_ptr, &pngxP->info_ptr->sig_bit);
             } else
                 *succeededP = false;
         }
@@ -815,25 +859,26 @@ computeMaxvalFromSbit(png_struct *        const pngP,
 
 
 static void
-setupSignificantBits(png_struct *        const pngP,
-                     png_info *          const pngInfoP,
+setupSignificantBits(struct pngx *       const pngxP,
                      enum alpha_handling const alphaHandling,
                      png_uint_16 *       const maxvalP,
-                     int *               const errorlevelP) {
+                     int *               const errorLevelP) {
 /*----------------------------------------------------------------------------
   Figure out what maxval would best express the information in the PNG
-  described by *pngP and *pngInfoP, with 'alpha' telling which
-  information in the PNG we care about (image or alpha mask).
+  described by *pngxP, with 'alpha' telling which information in the PNG we
+  care about (image or alpha mask).
 
   Return the result as *maxvalP.
 
-  Also set up *pngP for the corresponding significant bits.
+  Also set up *pngxP for the corresponding significant bits.
 -----------------------------------------------------------------------------*/
+    png_info * const pngInfoP = pngxP->info_ptr;
+
     bool gotItFromSbit;
     
     if (pngInfoP->valid & PNG_INFO_sBIT)
-        computeMaxvalFromSbit(pngP, pngInfoP, alphaHandling,
-                              maxvalP, &gotItFromSbit, errorlevelP);
+        computeMaxvalFromSbit(pngxP, alphaHandling,
+                              maxvalP, &gotItFromSbit, errorLevelP);
     else
         gotItFromSbit = false;
 
@@ -846,7 +891,7 @@ setupSignificantBits(png_struct *        const pngP,
                        is plenty
                     */
                     *maxvalP = 1;
-                else if (paletteHasPartialTransparency(pngInfoP))
+                else if (paletteHasPartialTransparency(pngxP))
                     /* Use same maxval as PNG transparency palette for
                        simplicity
                     */
@@ -866,22 +911,22 @@ setupSignificantBits(png_struct *        const pngP,
 
 
 static bool
-imageHasColor(png_info * const info_ptr) {
+imageHasColor(struct pngx * const pngxP) {
 
     bool retval;
 
-    if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
-        info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+    if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
+        pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
 
         retval = FALSE;
-    else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+    else if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
         bool foundColor;
         unsigned int i;
             
         for (i = 0, foundColor = FALSE;
-             i < info_ptr->num_palette && !foundColor;
+             i < pngxP->info_ptr->num_palette && !foundColor;
              ++i) {
-            if (iscolor(info_ptr->palette[i]))
+            if (isColor(pngxP->info_ptr->palette[i]))
                 foundColor = TRUE;
         }
         retval = foundColor;
@@ -894,7 +939,7 @@ imageHasColor(png_info * const info_ptr) {
 
 
 static void
-determineOutputType(png_info *          const pngInfoP,
+determineOutputType(struct pngx *       const pngxP,
                     enum alpha_handling const alphaHandling,
                     pngcolor            const bgColor,
                     xelval              const maxval,
@@ -909,7 +954,7 @@ determineOutputType(png_info *          const pngInfoP,
     } else {            
         /* The output is a normal Netpbm image */
         bool const outputIsColor =
-            imageHasColor(pngInfoP) || !isGrayscale(bgColor);
+            imageHasColor(pngxP) || !isGrayscale(bgColor);
 
         if (alphaHandling == ALPHA_IN) {
             *formatP = PAM_FORMAT;
@@ -935,11 +980,11 @@ determineOutputType(png_info *          const pngInfoP,
 
 
 static void
-getBackgroundColor(png_info *   const info_ptr,
-                   const char * const requestedColor,
-                   float        const totalgamma,
-                   xelval       const maxval,
-                   pngcolor *   const bgColorP) {
+getBackgroundColor(struct pngx * const pngxP,
+                   const char *  const requestedColor,
+                   float         const totalgamma,
+                   xelval        const maxval,
+                   pngcolor *    const bgColorP) {
 /*----------------------------------------------------------------------------
    Figure out what the background color should be.  If the user requested
    a particular color ('requestedColor' not null), that's the one.
@@ -957,31 +1002,31 @@ getBackgroundColor(png_info *   const info_ptr,
         bgColorP->g = PPM_GETG(backcolor);
         bgColorP->b = PPM_GETB(backcolor);
 
-    } else if (info_ptr->valid & PNG_INFO_bKGD) {
+    } else if (pngxP->info_ptr->valid & PNG_INFO_bKGD) {
         /* didn't manage to get libpng to work (bugs?) concerning background
            processing, therefore we do our own.
         */
-        switch (info_ptr->color_type) {
+        switch (pngxP->info_ptr->color_type) {
         case PNG_COLOR_TYPE_GRAY:
         case PNG_COLOR_TYPE_GRAY_ALPHA:
             bgColorP->r = bgColorP->g = bgColorP->b = 
-                gamma_correct(info_ptr->background.gray, totalgamma);
+                gammaCorrect(pngxP->info_ptr->background.gray, totalgamma);
             break;
         case PNG_COLOR_TYPE_PALETTE: {
             png_color const rawBgcolor = 
-                info_ptr->palette[info_ptr->background.index];
-            bgColorP->r = gamma_correct(rawBgcolor.red, totalgamma);
-            bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
-            bgColorP->b = gamma_correct(rawBgcolor.blue, totalgamma);
+                pngxP->info_ptr->palette[pngxP->info_ptr->background.index];
+            bgColorP->r = gammaCorrect(rawBgcolor.red, totalgamma);
+            bgColorP->g = gammaCorrect(rawBgcolor.green, totalgamma);
+            bgColorP->b = gammaCorrect(rawBgcolor.blue, totalgamma);
         }
         break;
         case PNG_COLOR_TYPE_RGB:
         case PNG_COLOR_TYPE_RGB_ALPHA: {
-            png_color_16 const rawBgcolor = info_ptr->background;
+            png_color_16 const rawBgcolor = pngxP->info_ptr->background;
             
-            bgColorP->r = gamma_correct(rawBgcolor.red,   totalgamma);
-            bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
-            bgColorP->b = gamma_correct(rawBgcolor.blue,  totalgamma);
+            bgColorP->r = gammaCorrect(rawBgcolor.red,   totalgamma);
+            bgColorP->g = gammaCorrect(rawBgcolor.green, totalgamma);
+            bgColorP->b = gammaCorrect(rawBgcolor.blue,  totalgamma);
         }
         break;
         }
@@ -992,14 +1037,62 @@ getBackgroundColor(png_info *   const info_ptr,
 
 
 
-#define GET_PNG_VAL(p) get_png_val(&(p), pngInfoP->bit_depth)
+static void
+warnNonsquarePixels(struct pngx * const pngxP,
+                    int *         const errorLevelP) {
+
+    if (pngxP->info_ptr->valid & PNG_INFO_pHYs) {
+        float const r =
+            (float)pngxP->info_ptr->x_pixels_per_unit /
+            pngxP->info_ptr->y_pixels_per_unit;
+
+        if (r != 1.0) {
+            pm_message ("warning - non-square pixels; "
+                        "to fix do a 'pamscale -%cscale %g'",
+                        r < 1.0 ? 'x' : 'y',
+                        r < 1.0 ? 1.0 / r : r );
+            *errorLevelP = PNMTOPNG_WARNING_LEVEL;
+        }
+    }
+}
+
+
+
+static png_uint_16
+paletteAlpha(struct pngx * const pngxP,
+             png_uint_16   const index,
+             sample        const maxval) {
+
+    png_uint_16 retval;
+
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) {
+        png_bytep      trans;
+        int            numTrans;
+        png_color_16 * transColorP;
+
+        png_get_tRNS(pngxP->png_ptr, pngxP->info_ptr,
+                     &trans, &numTrans, &transColorP);
+
+        if (index < numTrans)
+            retval = trans[index];
+        else
+            retval = maxval;
+    } else
+        retval = maxval;
+
+    return retval;
+}
+
+
+
+#define GET_PNG_VAL(p) getPngVal(&(p), pngxP->info_ptr->bit_depth)
 
 
 
 static void
 makeTupleRow(const struct pam *  const pamP,
              const tuple *       const tuplerow,
-             png_info *          const pngInfoP,
+             struct pngx *       const pngxP,
              const png_byte *    const pngRasterRow,
              pngcolor            const bgColor,
              enum alpha_handling const alphaHandling,
@@ -1009,13 +1102,13 @@ makeTupleRow(const struct pam *  const pamP,
     unsigned int col;
 
     pngPixelP = &pngRasterRow[0];  /* initial value */
-    for (col = 0; col < pngInfoP->width; ++col) {
-        switch (pngInfoP->color_type) {
+    for (col = 0; col < pngxP->info_ptr->width; ++col) {
+        switch (pngxP->info_ptr->color_type) {
         case PNG_COLOR_TYPE_GRAY: {
             pngcolor fgColor;
             fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP);
             setTuple(pamP, tuplerow[col], fgColor, bgColor, alphaHandling,
-                   isTransparentColor(fgColor, pngInfoP, totalgamma) ?
+                   isTransparentColor(fgColor, pngxP, totalgamma) ?
                    0 : maxval);
         }
         break;
@@ -1033,7 +1126,7 @@ makeTupleRow(const struct pam *  const pamP,
 
         case PNG_COLOR_TYPE_PALETTE: {
             png_uint_16 const index        = GET_PNG_VAL(pngPixelP);
-            png_color   const paletteColor = pngInfoP->palette[index];
+            png_color   const paletteColor = pngxP->info_ptr->palette[index];
 
             pngcolor fgColor;
 
@@ -1042,9 +1135,7 @@ makeTupleRow(const struct pam *  const pamP,
             fgColor.b = paletteColor.blue;
 
             setTuple(pamP, tuplerow[col], fgColor, bgColor, alphaHandling,
-                     (pngInfoP->valid & PNG_INFO_tRNS) &&
-                     index < pngInfoP->num_trans ?
-                     pngInfoP->TRANS_ALPHA[index] : maxval);
+                     paletteAlpha(pngxP, index, maxval));
         }
         break;
                 
@@ -1055,7 +1146,7 @@ makeTupleRow(const struct pam *  const pamP,
             fgColor.g = GET_PNG_VAL(pngPixelP);
             fgColor.b = GET_PNG_VAL(pngPixelP);
             setTuple(pamP, tuplerow[col], fgColor, bgColor, alphaHandling,
-                     isTransparentColor(fgColor, pngInfoP, totalgamma) ?
+                     isTransparentColor(fgColor, pngxP, totalgamma) ?
                      0 : maxval);
         }
         break;
@@ -1074,7 +1165,8 @@ makeTupleRow(const struct pam *  const pamP,
         break;
 
         default:
-            pm_error("unknown PNG color type: %d", pngInfoP->color_type);
+            pm_error("unknown PNG color type: %d",
+                     pngxP->info_ptr->color_type);
         }
     }
 }
@@ -1108,15 +1200,15 @@ reportOutputFormat(const struct pam * const pamP) {
 
 static void
 writeNetpbm(struct pam *        const pamP,
-            png_info *          const pngInfoP,
+            struct pngx *       const pngxP,
             png_byte **         const pngRaster,
             pngcolor            const bgColor,
             enum alpha_handling const alphaHandling,
             double              const totalgamma) {
 /*----------------------------------------------------------------------------
    Write a Netpbm image of either the image or the alpha mask, according to
-   'alphaHandling' that is in the PNG image described by 'pngInfoP' and
-   pngRaster.
+   'alphaHandling' that is in the PNG image described by *pngxP and
+   pngRaster[][].
 
    *pamP describes the required output image and is consistent with
    *pngInfoP.
@@ -1134,8 +1226,8 @@ writeNetpbm(struct pam *        const pamP,
 
     tuplerow = pnm_allocpamrow(pamP);
 
-    for (row = 0; row < pngInfoP->height; ++row) {
-        makeTupleRow(pamP, tuplerow, pngInfoP, pngRaster[row], bgColor,
+    for (row = 0; row < pngxP->info_ptr->height; ++row) {
+        makeTupleRow(pamP, tuplerow, pngxP, pngRaster[row], bgColor,
                      alphaHandling, totalgamma);
 
         pnm_writepamrow(pamP, tuplerow);
@@ -1146,97 +1238,62 @@ writeNetpbm(struct pam *        const pamP,
 
 
 static void 
-convertpng(FILE *             const ifp, 
-           FILE *             const tfp, 
+convertpng(FILE *             const ifP, 
+           FILE *             const tfP, 
            struct cmdlineInfo const cmdline,
-           int *              const errorlevelP) {
+           int *              const errorLevelP) {
 
-    png_struct * png_ptr;
-    png_info * info_ptr;
-    png_byte ** png_image;
+    png_byte ** pngRaster;
     pngcolor bgColor;
     float totalgamma;
     struct pam pam;
+    jmp_buf jmpbuf;
+    struct pngx * pngxP;
 
-    *errorlevelP = 0;
-
-    read_sig_buf(ifp);
-
-    png_ptr = png_create_read_struct(
-        PNG_LIBPNG_VER_STRING,
-        &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
-    if (png_ptr == NULL)
-        pm_error("cannot allocate main libpng structure (png_ptr)");
+    *errorLevelP = 0;
 
-    info_ptr = png_create_info_struct (png_ptr);
-    if (info_ptr == NULL)
-        pm_error("cannot allocate LIBPNG structures");
-
-    if (setjmp(pngtopnm_jmpbuf_struct.jmpbuf))
+    if (setjmp(jmpbuf))
         pm_error ("setjmp returns error condition");
 
-    png_init_io (png_ptr, ifp);
-    png_set_sig_bytes (png_ptr, SIG_CHECK_SIZE);
-    png_read_info (png_ptr, info_ptr);
-
-    allocPngRaster(info_ptr, &png_image);
-
-    if (info_ptr->bit_depth < 8)
-        png_set_packing (png_ptr);
-
-    setupGammaCorrection(png_ptr, info_ptr, cmdline.gamma, &totalgamma);
-
-    setupSignificantBits(png_ptr, info_ptr, cmdline.alpha,
-                         &maxval, errorlevelP);
-
-    getBackgroundColor(info_ptr, cmdline.background, totalgamma, maxval,
-                       &bgColor);
+    pngx_create(&pngxP, PNGX_READ, &jmpbuf);
 
-    png_read_image(png_ptr, png_image);
-    png_read_end(png_ptr, info_ptr);
+    readPng(pngxP, ifP, &pngRaster);
 
     if (verbose)
-        /* Note that some of info_ptr is not defined until png_read_end() 
-           completes.  That's because it comes from chunks that are at the
-           end of the stream.
-        */
-        dump_png_info(info_ptr);
+        dumpPngInfo(pngxP);
 
     if (cmdline.time)
-        show_time(info_ptr);
-    if (tfp)
-        save_text(info_ptr, tfp);
+        showTime(pngxP);
+    if (tfP)
+        saveText(pngxP, tfP);
 
-    if (info_ptr->valid & PNG_INFO_pHYs) {
-        float const r =
-            (float)info_ptr->x_pixels_per_unit / info_ptr->y_pixels_per_unit;
-        if (r != 1.0) {
-            pm_message ("warning - non-square pixels; "
-                        "to fix do a 'pamscale -%cscale %g'",
-                        r < 1.0 ? 'x' : 'y',
-                        r < 1.0 ? 1.0 / r : r );
-            *errorlevelP = PNMTOPNG_WARNING_LEVEL;
-        }
-    }
+    warnNonsquarePixels(pngxP, errorLevelP);
+
+    setupGammaCorrection(pngxP, cmdline.gamma, &totalgamma);
+
+    setupSignificantBits(pngxP, cmdline.alpha, &maxval, errorLevelP);
 
+    getBackgroundColor(pngxP, cmdline.background, totalgamma, maxval,
+                         &bgColor);
+  
     pam.size        = sizeof(pam);
     pam.len         = PAM_STRUCT_SIZE(maxval);
     pam.file        = stdout;
     pam.plainformat = 0;
-    pam.height      = info_ptr->height;
-    pam.width       = info_ptr->width;
+    pam.height      = pngxP->info_ptr->height;
+    pam.width       = pngxP->info_ptr->width;
     pam.maxval      = maxval;
 
-    determineOutputType(info_ptr, cmdline.alpha, bgColor, maxval,
+    determineOutputType(pngxP, cmdline.alpha, bgColor, maxval,
                         &pam.format, &pam.depth, pam.tuple_type);
 
-    writeNetpbm(&pam, info_ptr, png_image, bgColor, cmdline.alpha, totalgamma);
+    writeNetpbm(&pam, pngxP, pngRaster, bgColor, cmdline.alpha, totalgamma);
 
     fflush(stdout);
 
-    freePngRaster(png_image, info_ptr);
+    freePngRaster(pngRaster, pngxP);
 
-    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+    pngx_destroy(pngxP);
 }
 
 
@@ -1247,7 +1304,7 @@ main(int argc, const char *argv[]) {
     struct cmdlineInfo cmdline;
     FILE * ifP;
     FILE * tfP;
-    int errorlevel;
+    int errorLevel;
 
     pm_proginit(&argc, argv);
 
@@ -1262,7 +1319,7 @@ main(int argc, const char *argv[]) {
     else
         tfP = NULL;
 
-    convertpng(ifP, tfP, cmdline, &errorlevel);
+    convertpng(ifP, tfP, cmdline, &errorLevel);
 
     if (tfP)
         pm_close(tfP);
@@ -1270,5 +1327,5 @@ main(int argc, const char *argv[]) {
     pm_close(ifP);
     pm_close(stdout);
 
-    return errorlevel;
+    return errorLevel;
 }
diff --git a/converter/other/pngtopnm.c b/converter/other/pngtopnm.c
deleted file mode 100644
index ffb98ef7..00000000
--- a/converter/other/pngtopnm.c
+++ /dev/null
@@ -1,1234 +0,0 @@
-/*
-** pngtopnm.c -
-** read a Portable Network Graphics file and produce a PNM.
-**
-** Copyright (C) 1995,1998 by Alexander Lehmann <alex@hal.rhein-main.de>
-**                        and Willem van Schaik <willem@schaik.com>
-**
-** Permission to use, copy, modify, and distribute this software and its
-** documentation for any purpose and without fee is hereby granted, provided
-** that the above copyright notice appear in all copies and that both that
-** copyright notice and this permission notice appear in supporting
-** documentation.  This software is provided "as is" without express or
-** implied warranty.
-**
-** modeled after giftopnm by David Koblas and
-** with lots of bits pasted from libpng.txt by Guy Eric Schalnat
-*/
-
-#ifndef PNMTOPNG_WARNING_LEVEL
-#  define PNMTOPNG_WARNING_LEVEL 0   /* use 0 for backward compatibility, */
-#endif                               /*  2 for warnings (1 == error) */
-
-#include <assert.h>
-#include <math.h>
-#include <float.h>
-#include <png.h>    /* includes zlib.h and setjmp.h */
-#define VERSION "2.37.4 (5 December 1999) +netpbm"
-
-#include "pm_c_util.h"
-#include "mallocvar.h"
-#include "nstring.h"
-#include "shhopt.h"
-#include "pnm.h"
-
-enum alpha_handling {ALPHA_NONE, ALPHA_ONLY, ALPHA_MIX};
-
-struct cmdlineInfo {
-    /* All the information the user supplied in the command line,
-       in a form easy for the program to use.
-    */
-    const char *inputFilespec;  /* '-' if stdin */
-    unsigned int verbose;
-    enum alpha_handling alpha;
-    const char * background;
-    float gamma;  /* -1.0 means unspecified */
-    const char * text;
-    unsigned int time;
-};
-
-
-typedef struct {
-/*----------------------------------------------------------------------------
-   A color in a format compatible with the PNG library.
-
-   Note that the PNG library declares types png_color and png_color_16
-   which are similar.
------------------------------------------------------------------------------*/
-    png_uint_16 r;
-    png_uint_16 g;
-    png_uint_16 b;
-} pngcolor;
-
-
-static png_uint_16 maxval;
-static bool verbose;
-
-
-static void
-parseCommandLine(int                  argc, 
-                 const char **        argv,
-                 struct cmdlineInfo * cmdlineP ) {
-/*----------------------------------------------------------------------------
-   Parse program command line described in Unix standard form by argc
-   and argv.  Return the information in the options as *cmdlineP.  
-
-   If command line is internally inconsistent (invalid options, etc.),
-   issue error message to stderr and abort program.
-
-   Note that the strings we return are stored in the storage that
-   was passed to us as the argv array.  We also trash *argv.
------------------------------------------------------------------------------*/
-    optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
-         */
-    optStruct3 opt;
-
-    unsigned int option_def_index;
-
-    unsigned int alphaSpec, mixSpec, backgroundSpec, gammaSpec, textSpec;
-
-    MALLOCARRAY(option_def, 100);
-
-    option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0, "verbose",     OPT_FLAG,   NULL,                  
-            &cmdlineP->verbose,       0);
-    OPTENT3(0, "alpha",       OPT_FLAG,   NULL,                  
-            &alphaSpec,               0);
-    OPTENT3(0, "mix",         OPT_FLAG,   NULL,                  
-            &mixSpec,                 0);
-    OPTENT3(0, "background",  OPT_STRING, &cmdlineP->background,
-            &backgroundSpec,          0);
-    OPTENT3(0, "gamma",       OPT_FLOAT,  &cmdlineP->gamma,
-            &gammaSpec,               0);
-    OPTENT3(0, "text",        OPT_STRING, &cmdlineP->text,
-            &textSpec,                0);
-    OPTENT3(0, "time",        OPT_FLAG,   NULL,                  
-            &cmdlineP->time,          0);
-
-    opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
-
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
-        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
-
-
-    if (alphaSpec && mixSpec)
-        pm_error("You cannot specify both -alpha and -mix");
-    else if (alphaSpec)
-        cmdlineP->alpha = ALPHA_ONLY;
-    else if (mixSpec)
-        cmdlineP->alpha = ALPHA_MIX;
-    else
-        cmdlineP->alpha = ALPHA_NONE;
-
-    if (backgroundSpec && !mixSpec)
-        pm_error("-background is useless without -mix");
-
-    if (!backgroundSpec)
-        cmdlineP->background = NULL;
-
-    if (!gammaSpec)
-        cmdlineP->gamma = -1.0;
-
-    if (!textSpec)
-        cmdlineP->text = NULL;
-
-    if (argc-1 < 1)
-        cmdlineP->inputFilespec = "-";
-    else if (argc-1 == 1)
-        cmdlineP->inputFilespec = argv[1];
-    else
-        pm_error("Program takes at most one argument: input file name.  "
-            "you specified %d", argc-1);
-}
-
-
-
-static void
-pngtopnmErrorHandler(png_structp     const png_ptr,
-                     png_const_charp const msg) {
-
-    jmp_buf * jmpbufP;
-
-    /* this function, aside from the extra step of retrieving the "error
-       pointer" (below) and the fact that it exists within the application
-       rather than within libpng, is essentially identical to libpng's
-       default error handler.  The second point is critical:  since both
-       setjmp() and longjmp() are called from the same code, they are
-       guaranteed to have compatible notions of how big a jmp_buf is,
-       regardless of whether _BSD_SOURCE or anything else has (or has not)
-       been defined.
-    */
-
-    pm_message("fatal libpng error: %s", msg);
-
-    jmpbufP = png_get_error_ptr(png_ptr);
-
-    if (!jmpbufP) {
-        /* we are completely hosed now */
-        pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating.");
-    }
-
-    longjmp(*jmpbufP, 1);
-}
-
-
-
-struct pngx {
-    png_structp png_ptr;
-    png_infop info_ptr;
-};
-
-
-
-static void
-pngx_createRead(struct pngx ** const pngxPP,
-                jmp_buf *      const jmpbufP) {
-
-    struct pngx * pngxP;
-
-    MALLOCVAR(pngxP);
-
-    if (!pngxP)
-        pm_error("Failed to allocate memory for PNG object");
-    else {
-        pngxP->png_ptr = png_create_read_struct(
-            PNG_LIBPNG_VER_STRING,
-            jmpbufP, pngtopnmErrorHandler, NULL);
-
-        if (!pngxP->png_ptr)
-            pm_error("cannot allocate main libpng structure (png_ptr)");
-        else {
-            pngxP->info_ptr = png_create_info_struct(pngxP->png_ptr);
-
-            if (!pngxP->info_ptr)
-                pm_error("cannot allocate libpng info structure (info_ptr)");
-            else
-                *pngxPP = pngxP;
-        }
-    }
-}
-
-
-
-static void
-pngx_destroy(struct pngx * const pngxP) {
-
-    png_destroy_read_struct(&pngxP->png_ptr, &pngxP->info_ptr, NULL);
-
-    free(pngxP);
-}
-
-
-
-static bool
-pngx_chunkIsPresent(struct pngx * const pngxP,
-                    uint32_t      const chunkType) {
-
-    return png_get_valid(pngxP->png_ptr, pngxP->info_ptr, chunkType);
-}
-
-
-
-static void
-verifyFileIsPng(FILE *   const ifP,
-                size_t * const consumedByteCtP) {
-
-    unsigned char buffer[4];
-    size_t bytesRead;
-
-    bytesRead = fread(buffer, 1, sizeof(buffer), ifP);
-    if (bytesRead != sizeof(buffer))
-        pm_error("input file is empty or too short");
-
-    if (png_sig_cmp(buffer, (png_size_t) 0, (png_size_t) sizeof(buffer)) != 0)
-        pm_error("input file is not a PNG file "
-                 "(does not have the PNG signature in its first 4 bytes)");
-    else
-        *consumedByteCtP = bytesRead;
-}
-
-
-
-static unsigned int
-computePngLineSize(struct pngx * const pngxP) {
-
-    unsigned int const bytesPerSample =
-        pngxP->info_ptr->bit_depth == 16 ? 2 : 1;
-
-    unsigned int samplesPerPixel;
-
-    switch (pngxP->info_ptr->color_type) {
-    case PNG_COLOR_TYPE_GRAY_ALPHA: samplesPerPixel = 2; break;
-    case PNG_COLOR_TYPE_RGB:        samplesPerPixel = 3; break;
-    case PNG_COLOR_TYPE_RGB_ALPHA:  samplesPerPixel = 4; break;
-    default:                        samplesPerPixel = 1;
-    }
-
-    if (UINT_MAX / bytesPerSample / samplesPerPixel < pngxP->info_ptr->width)
-        pm_error("Width %u of PNG is uncomputably large",
-                 (unsigned int)pngxP->info_ptr->width);
-       
-    return pngxP->info_ptr->width * bytesPerSample * samplesPerPixel;
-}
-
-
-
-static void
-allocPngRaster(struct pngx * const pngxP,
-               png_byte ***  const pngImageP) {
-
-    unsigned int const lineSize = computePngLineSize(pngxP);
-
-    png_byte ** pngImage;
-    unsigned int row;
-
-    MALLOCARRAY(pngImage, pngxP->info_ptr->height);
-
-    if (pngImage == NULL)
-        pm_error("couldn't allocate space for %u PNG raster rows",
-                 (unsigned int)pngxP->info_ptr->height);
-
-    for (row = 0; row < pngxP->info_ptr->height; ++row) {
-        MALLOCARRAY(pngImage[row], lineSize);
-        if (pngImage[row] == NULL)
-            pm_error("couldn't allocate space for %uth row of PNG raster",
-                     row);
-    }
-    *pngImageP = pngImage;
-}
-
-
-
-static void
-freePngRaster(png_byte **   const pngRaster,
-              struct pngx * const pngxP) {
-
-    unsigned int row;
-
-    for (row = 0; row < pngxP->info_ptr->height; ++row)
-        free(pngRaster[row]);
-
-    free(pngRaster);
-}
-
-
-
-static void
-readPng(struct pngx * const pngxP,
-        FILE *        const ifP,
-        png_byte ***  const pngRasterP) {
-
-    size_t sigByteCt;
-    png_byte ** pngRaster;
-            
-    verifyFileIsPng(ifP, &sigByteCt);
-
-    /* Declare that we already read the signature bytes */
-    png_set_sig_bytes(pngxP->png_ptr, (int)sigByteCt);
-
-    png_init_io(pngxP->png_ptr, ifP);
-
-    png_read_info(pngxP->png_ptr, pngxP->info_ptr);
-
-    allocPngRaster(pngxP, &pngRaster);
-
-    if (pngxP->info_ptr->bit_depth < 8)
-        png_set_packing(pngxP->png_ptr);
-
-    png_read_image(pngxP->png_ptr, pngRaster);
-
-    png_read_end(pngxP->png_ptr, pngxP->info_ptr);
-
-    /* Note that some of info_ptr is not defined until png_read_end() 
-       completes.  That's because it comes from chunks that are at the
-       end of the stream.
-    */
-
-    *pngRasterP = pngRaster;
-}
-
-
-
-static png_uint_16
-get_png_val(const png_byte ** const pp,
-            int               const bit_depth) {
-
-    png_uint_16 c;
-    
-    if (bit_depth == 16)
-        c = (*((*pp)++)) << 8;
-    else
-        c = 0;
-
-    c |= (*((*pp)++));
-    
-    return c;
-}
-
-
-
-static bool
-isGrayscale(pngcolor const color) {
-
-    return color.r == color.g && color.r == color.b;
-}
-
-
-
-static void 
-setXel(xel *               const xelP, 
-       pngcolor            const foreground,
-       pngcolor            const background,
-       enum alpha_handling const alpha_handling,
-       png_uint_16         const alpha) {
-
-    if (alpha_handling == ALPHA_ONLY) {
-        PNM_ASSIGN1(*xelP, alpha);
-    } else {
-        if ((alpha_handling == ALPHA_MIX) && (alpha != maxval)) {
-            double const opacity      = (double)alpha / maxval;
-            double const transparency = 1.0 - opacity;
-
-            pngcolor mix;
-
-            mix.r = foreground.r * opacity + background.r * transparency + 0.5;
-            mix.g = foreground.g * opacity + background.g * transparency + 0.5;
-            mix.b = foreground.b * opacity + background.b * transparency + 0.5;
-            PPM_ASSIGN(*xelP, mix.r, mix.g, mix.b);
-        } else
-            PPM_ASSIGN(*xelP, foreground.r, foreground.g, foreground.b);
-    }
-}
-
-
-
-static png_uint_16
-gamma_correct(png_uint_16 const v,
-              float       const g) {
-
-    if (g != -1.0)
-        return (png_uint_16) ROUNDU(pow((double) v / maxval, (1.0 / g)) *
-                                    maxval);
-    else
-        return v;
-}
-
-
-
-static bool
-iscolor(png_color const c) {
-
-    return c.red != c.green || c.green != c.blue;
-}
-
-
-
-static void
-saveText(struct pngx * const pngxP,
-         FILE *        const tfP) {
-
-    png_info * const info_ptr = pngxP->info_ptr;
-
-    unsigned int i;
-
-    for (i = 0 ; i < info_ptr->num_text; ++i) {
-        unsigned int j;
-        j = 0;
-
-        while (info_ptr->text[i].key[j] != '\0' &&
-               info_ptr->text[i].key[j] != ' ')
-            ++j;    
-
-        if (info_ptr->text[i].key[j] != ' ') {
-            fprintf(tfP, "%s", info_ptr->text[i].key);
-            for (j = strlen (info_ptr->text[i].key); j < 15; ++j)
-                putc(' ', tfP);
-        } else {
-            fprintf(tfP, "\"%s\"", info_ptr->text[i].key);
-            for (j = strlen (info_ptr->text[i].key); j < 13; ++j)
-                putc(' ', tfP);
-        }
-        putc(' ', tfP); /* at least one space between key and text */
-    
-        for (j = 0; j < info_ptr->text[i].text_length; ++j) {
-            putc(info_ptr->text[i].text[j], tfP);
-            if (info_ptr->text[i].text[j] == '\n') {
-                unsigned int k;
-                for (k = 0; k < 16; ++k)
-                    putc(' ', tfP);
-            }
-        }
-        putc('\n', tfP);
-    }
-}
-
-
-
-static void
-showTime(struct pngx * const pngxP) {
-
-    static const char * const month[] = {
-        "", "January", "February", "March", "April", "May", "June",
-        "July", "August", "September", "October", "November", "December"
-    };
-
-    if (pngxP->info_ptr->valid & PNG_INFO_tIME) {
-        pm_message("modification time: %02d %s %d %02d:%02d:%02d",
-                   pngxP->info_ptr->mod_time.day,
-                   month[pngxP->info_ptr->mod_time.month],
-                   pngxP->info_ptr->mod_time.year,
-                   pngxP->info_ptr->mod_time.hour,
-                   pngxP->info_ptr->mod_time.minute,
-                   pngxP->info_ptr->mod_time.second);
-    }
-}
-
-
-
-static void
-dumpPngInfo(struct pngx * const pngxP) {
-
-    png_info * const info_ptr = pngxP->info_ptr;
-    const char *type_string;
-    const char *filter_string;
-
-    switch (info_ptr->color_type) {
-      case PNG_COLOR_TYPE_GRAY:
-        type_string = "gray";
-        break;
-
-      case PNG_COLOR_TYPE_GRAY_ALPHA:
-        type_string = "gray+alpha";
-        break;
-
-      case PNG_COLOR_TYPE_PALETTE:
-        type_string = "palette";
-        break;
-
-      case PNG_COLOR_TYPE_RGB:
-        type_string = "truecolor";
-        break;
-
-      case PNG_COLOR_TYPE_RGB_ALPHA:
-        type_string = "truecolor+alpha";
-        break;
-    }
-
-    switch (info_ptr->filter_type) {
-    case PNG_FILTER_TYPE_BASE:
-        asprintfN(&filter_string, "base filter");
-        break;
-    default:
-        asprintfN(&filter_string, "unknown filter type %d", 
-                  info_ptr->filter_type);
-    }
-
-    pm_message("reading a %ldw x %ldh image, %d bit%s",
-               info_ptr->width, info_ptr->height,
-               info_ptr->bit_depth, info_ptr->bit_depth > 1 ? "s" : "");
-    pm_message("%s, %s, %s",
-               type_string,
-               info_ptr->interlace_type ? 
-               "Adam7 interlaced" : "not interlaced",
-               filter_string);
-    pm_message("background {index, gray, red, green, blue} = "
-               "{%d, %d, %d, %d, %d}",
-               info_ptr->background.index,
-               info_ptr->background.gray,
-               info_ptr->background.red,
-               info_ptr->background.green,
-               info_ptr->background.blue);
-
-    strfree(filter_string);
-
-    if (info_ptr->valid & PNG_INFO_tRNS)
-        pm_message("tRNS chunk (transparency): %u entries",
-                   info_ptr->num_trans);
-    else
-        pm_message("tRNS chunk (transparency): not present");
-
-    if (info_ptr->valid & PNG_INFO_gAMA)
-        pm_message("gAMA chunk (image gamma): gamma = %4.2f", info_ptr->gamma);
-    else
-        pm_message("gAMA chunk (image gamma): not present");
-
-    if (info_ptr->valid & PNG_INFO_sBIT)
-        pm_message("sBIT chunk: present");
-    else
-        pm_message("sBIT chunk: not present");
-
-    if (info_ptr->valid & PNG_INFO_cHRM)
-        pm_message("cHRM chunk: present");
-    else
-        pm_message("cHRM chunk: not present");
-
-    if (info_ptr->valid & PNG_INFO_PLTE)
-        pm_message("PLTE chunk: %d entries", info_ptr->num_palette);
-    else
-        pm_message("PLTE chunk: not present");
-
-    if (info_ptr->valid & PNG_INFO_bKGD)
-        pm_message("bKGD chunk: present");
-    else
-        pm_message("bKGD chunk: not present");
-
-    if (info_ptr->valid & PNG_INFO_PLTE)
-        pm_message("hIST chunk: present");
-    else
-        pm_message("hIST chunk: not present");
-
-    if (info_ptr->valid & PNG_INFO_pHYs)
-        pm_message("pHYs chunk: present");
-    else
-        pm_message("pHYs chunk: not present");
-
-    if (info_ptr->valid & PNG_INFO_oFFs)
-        pm_message("oFFs chunk: present");
-    else
-        pm_message("oFFs chunk: not present");
-
-    if (info_ptr->valid & PNG_INFO_tIME)
-        pm_message("tIME chunk: present");
-    else
-        pm_message("tIME chunk: not present");
-
-    if (info_ptr->valid & PNG_INFO_pCAL)
-        pm_message("pCAL chunk: present");
-    else
-        pm_message("pCAL chunk: not present");
-
-    if (info_ptr->valid & PNG_INFO_sRGB)
-        pm_message("sRGB chunk: present");
-    else
-        pm_message("sRGB chunk: not present");
-}
-
-
-
-static const png_color_16 *
-transColor(struct pngx * const pngxP) {
-
-    png_bytep trans;
-    int numTrans;
-    png_color_16 * transColor;
-
-    assert(pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS));
-    
-    png_get_tRNS(pngxP->png_ptr, pngxP->info_ptr,
-                 &trans, &numTrans, &transColor);
-
-    return transColor;
-}
-
-
-
-static bool
-isTransparentColor(pngcolor      const color,
-                   struct pngx * const pngxP,
-                   double        const totalgamma) {
-/*----------------------------------------------------------------------------
-   Return TRUE iff pixels of color 'color' are supposed to be transparent
-   everywhere they occur.  Assume it's an RGB image.
-
-   'color' has been gamma-corrected.
------------------------------------------------------------------------------*/
-    bool retval;
-
-    if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) {
-        const png_color_16 * const transColorP = transColor(pngxP);
-
-        /* It seems odd that libpng lets you get gamma-corrected pixel
-           values, but not gamma-corrected transparency or background
-           values.  But as that is the case, we have to gamma-correct
-           the transparency values.
-
-           Note that because we compare the gamma-corrected values and
-           there may be many-to-one mapping of uncorrected to corrected
-           values, more pixels may be transparent than what the user
-           intended.
-
-           We could fix this by not letting libpng gamma-correct the
-           pixels, and just do it ourselves.
-        */
-    
-        switch (pngxP->info_ptr->color_type) {
-        case PNG_COLOR_TYPE_GRAY:
-            retval = color.r == gamma_correct(transColorP->gray, totalgamma);
-            break;
-        default:
-            retval = 
-                color.r == gamma_correct(transColorP->red,   totalgamma) &&
-                color.g == gamma_correct(transColorP->green, totalgamma) &&
-                color.b == gamma_correct(transColorP->blue,  totalgamma);
-        }
-    } else 
-        retval = FALSE;
-
-    return retval;
-}
-
-
-
-static void
-setupGammaCorrection(struct pngx * const pngxP,
-                     float         const displaygamma,
-                     float *       const totalgammaP) {
-
-    if (displaygamma == -1.0)
-        *totalgammaP = -1.0;
-    else {
-        float imageGamma;
-        if (pngxP->info_ptr->valid & PNG_INFO_gAMA)
-            imageGamma = pngxP->info_ptr->gamma;
-        else {
-            if (verbose)
-                pm_message("PNG doesn't specify image gamma.  Assuming 1.0");
-            imageGamma = 1.0;
-        }
-
-        if (fabs(displaygamma * imageGamma - 1.0) < .01) {
-            *totalgammaP = -1.0;
-            if (verbose)
-                pm_message("image gamma %4.2f matches "
-                           "display gamma %4.2f.  No conversion.",
-                           imageGamma, displaygamma);
-        } else {
-            png_set_gamma(pngxP->png_ptr, displaygamma, imageGamma);
-            *totalgammaP = imageGamma * displaygamma;
-            /* in case of gamma-corrections, sBIT's as in the
-               PNG-file are not valid anymore 
-            */
-            pngxP->info_ptr->valid &= ~PNG_INFO_sBIT;
-            if (verbose)
-                pm_message("image gamma is %4.2f, "
-                           "converted for display gamma of %4.2f",
-                           imageGamma, displaygamma);
-        }
-    }
-}
-
-
-
-static bool
-paletteHasPartialTransparency(png_info * const info_ptr) {
-
-    bool retval;
-
-    if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
-        if (info_ptr->valid & PNG_INFO_tRNS) {
-            bool foundGray;
-            unsigned int i;
-            
-            for (i = 0, foundGray = FALSE;
-                 i < info_ptr->num_trans && !foundGray;
-                 ++i) {
-                if (info_ptr->trans[i] != 0 &&
-                    info_ptr->trans[i] != maxval) {
-                    foundGray = TRUE;
-                }
-            }
-            retval = foundGray;
-        } else
-            retval = FALSE;
-    } else
-        retval = FALSE;
-
-    return retval;
-}
-
-
-
-static void
-setupSignificantBits(struct pngx *       const pngxP,
-                     enum alpha_handling const alpha,
-                     png_uint_16 *       const maxvalP,
-                     int *               const errorLevelP) {
-/*----------------------------------------------------------------------------
-  Figure out what maxval would best express the information in the PNG
-  described by *pngxP, with 'alpha' telling which information in the PNG we
-  care about (image or alpha mask).
-
-  Return the result as *maxvalP.
------------------------------------------------------------------------------*/
-    png_info * const info_ptr = pngxP->info_ptr;
-
-    /* Initial assumption of maxval */
-    if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
-        if (alpha == ALPHA_ONLY) {
-            if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
-                info_ptr->color_type == PNG_COLOR_TYPE_RGB)
-                /* The alpha mask will be all opaque, so maxval 1 is plenty */
-                *maxvalP = 1;
-            else if (paletteHasPartialTransparency(info_ptr))
-                /* Use same maxval as PNG transparency palette for simplicity*/
-                *maxvalP = 255;
-            else
-                /* A common case, so we conserve bits */
-                *maxvalP = 1;
-        } else
-            /* Use same maxval as PNG palette for simplicity */
-            *maxvalP = 255;
-    } else {
-        *maxvalP = (1l << info_ptr->bit_depth) - 1;
-    }
-
-    /* sBIT handling is very tricky. If we are extracting only the
-       image, we can use the sBIT info for grayscale and color images,
-       if the three values agree. If we extract the transparency/alpha
-       mask, sBIT is irrelevant for trans and valid for alpha. If we
-       mix both, the multiplication may result in values that require
-       the normal bit depth, so we will use the sBIT info only for
-       transparency, if we know that only solid and fully transparent
-       is used 
-    */
-    
-    if (info_ptr->valid & PNG_INFO_sBIT) {
-        switch (alpha) {
-        case ALPHA_MIX:
-            if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
-                info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
-                break;
-            if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
-                (info_ptr->valid & PNG_INFO_tRNS)) {
-
-                bool trans_mix;
-                unsigned int i;
-                trans_mix = TRUE;
-                for (i = 0; i < info_ptr->num_trans; ++i)
-                    if (info_ptr->trans[i] != 0 && info_ptr->trans[i] != 255) {
-                        trans_mix = FALSE;
-                        break;
-                    }
-                if (!trans_mix)
-                    break;
-            }
-
-            /* else fall though to normal case */
-
-        case ALPHA_NONE:
-            if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
-                 info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
-                 info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
-                (info_ptr->sig_bit.red != info_ptr->sig_bit.green ||
-                 info_ptr->sig_bit.red != info_ptr->sig_bit.blue) &&
-                alpha == ALPHA_NONE) {
-                pm_message("This program cannot handle "
-                           "different bit depths for color channels");
-                pm_message("writing file with %d bit resolution",
-                           info_ptr->bit_depth);
-                *errorLevelP = PNMTOPNG_WARNING_LEVEL;
-            } else {
-                if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) &&
-                    (info_ptr->sig_bit.red < 255)) {
-                    unsigned int i;
-                    for (i = 0; i < info_ptr->num_palette; ++i) {
-                        info_ptr->palette[i].red   >>=
-                            (8 - info_ptr->sig_bit.red);
-                        info_ptr->palette[i].green >>=
-                            (8 - info_ptr->sig_bit.green);
-                        info_ptr->palette[i].blue  >>=
-                            (8 - info_ptr->sig_bit.blue);
-                    }
-                    *maxvalP = (1l << info_ptr->sig_bit.red) - 1;
-                    if (verbose)
-                        pm_message ("image has fewer significant bits, "
-                                    "writing file with %d bits per channel", 
-                                    info_ptr->sig_bit.red);
-                } else
-                    if ((info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
-                         info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
-                        (info_ptr->sig_bit.red < info_ptr->bit_depth)) {
-                        png_set_shift(pngxP->png_ptr, &(info_ptr->sig_bit));
-                        *maxvalP = (1l << info_ptr->sig_bit.red) - 1;
-                        if (verbose)
-                            pm_message("image has fewer significant bits, "
-                                       "writing file with %d "
-                                       "bits per channel", 
-                                       info_ptr->sig_bit.red);
-                    } else 
-                        if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
-                             info_ptr->color_type ==
-                                 PNG_COLOR_TYPE_GRAY_ALPHA) &&
-                            (info_ptr->sig_bit.gray < info_ptr->bit_depth)) {
-                            png_set_shift(pngxP->png_ptr, &info_ptr->sig_bit);
-                            *maxvalP = (1l << info_ptr->sig_bit.gray) - 1;
-                            if (verbose)
-                                pm_message("image has fewer significant bits, "
-                                           "writing file with %d bits",
-                                           info_ptr->sig_bit.gray);
-                        }
-            }
-            break;
-
-        case ALPHA_ONLY:
-            if ((info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
-                 info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) && 
-                (info_ptr->sig_bit.gray < info_ptr->bit_depth)) {
-                png_set_shift(pngxP->png_ptr, &info_ptr->sig_bit);
-                if (verbose)
-                    pm_message ("image has fewer significant bits, "
-                                "writing file with %d bits", 
-                                info_ptr->sig_bit.alpha);
-                *maxvalP = (1l << info_ptr->sig_bit.alpha) - 1;
-            }
-            break;
-
-        }
-    }
-}
-
-
-
-static bool
-imageHasColor(struct pngx * const pngxP) {
-
-    bool retval;
-
-    if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
-        pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
-
-        retval = FALSE;
-    else if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
-        bool foundColor;
-        unsigned int i;
-            
-        for (i = 0, foundColor = FALSE;
-             i < pngxP->info_ptr->num_palette && !foundColor;
-             ++i) {
-            if (iscolor(pngxP->info_ptr->palette[i]))
-                foundColor = TRUE;
-        }
-        retval = foundColor;
-    } else
-        retval = TRUE;
-
-    return retval;
-}
-
-
-
-static void
-determineOutputType(struct pngx *       const pngxP,
-                    enum alpha_handling const alphaHandling,
-                    pngcolor            const bgColor,
-                    xelval              const maxval,
-                    int *               const pnmTypeP) {
-
-    if (alphaHandling != ALPHA_ONLY &&
-        (imageHasColor(pngxP) || !isGrayscale(bgColor)))
-        *pnmTypeP = PPM_TYPE;
-    else {
-        if (maxval > 1)
-            *pnmTypeP = PGM_TYPE;
-        else
-            *pnmTypeP = PBM_TYPE;
-    }
-}
-
-
-
-static void
-getBackgroundColor(struct pngx * const pngxP,
-                   const char *  const requestedColor,
-                   float         const totalgamma,
-                   xelval        const maxval,
-                   pngcolor *    const bgColorP) {
-/*----------------------------------------------------------------------------
-   Figure out what the background color should be.  If the user requested
-   a particular color ('requestedColor' not null), that's the one.
-   Otherwise, if the PNG specifies a background color, that's the one.
-   And otherwise, it's white.
------------------------------------------------------------------------------*/
-    if (requestedColor) {
-        /* Background was specified from the command-line; we always
-           use that.  I chose to do no gamma-correction in this case;
-           which is a bit arbitrary.  
-        */
-        pixel const backcolor = ppm_parsecolor(requestedColor, maxval);
-
-        bgColorP->r = PPM_GETR(backcolor);
-        bgColorP->g = PPM_GETG(backcolor);
-        bgColorP->b = PPM_GETB(backcolor);
-
-    } else if (pngxP->info_ptr->valid & PNG_INFO_bKGD) {
-        /* didn't manage to get libpng to work (bugs?) concerning background
-           processing, therefore we do our own.
-        */
-        switch (pngxP->info_ptr->color_type) {
-        case PNG_COLOR_TYPE_GRAY:
-        case PNG_COLOR_TYPE_GRAY_ALPHA:
-            bgColorP->r = bgColorP->g = bgColorP->b = 
-                gamma_correct(pngxP->info_ptr->background.gray, totalgamma);
-            break;
-        case PNG_COLOR_TYPE_PALETTE: {
-            png_color const rawBgcolor = 
-                pngxP->info_ptr->palette[pngxP->info_ptr->background.index];
-            bgColorP->r = gamma_correct(rawBgcolor.red, totalgamma);
-            bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
-            bgColorP->b = gamma_correct(rawBgcolor.blue, totalgamma);
-        }
-        break;
-        case PNG_COLOR_TYPE_RGB:
-        case PNG_COLOR_TYPE_RGB_ALPHA: {
-            png_color_16 const rawBgcolor = pngxP->info_ptr->background;
-            
-            bgColorP->r = gamma_correct(rawBgcolor.red,   totalgamma);
-            bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
-            bgColorP->b = gamma_correct(rawBgcolor.blue,  totalgamma);
-        }
-        break;
-        }
-    } else 
-        /* when no background given, we use white [from version 2.37] */
-        bgColorP->r = bgColorP->g = bgColorP->b = maxval;
-}
-
-
-
-static void
-warnNonsquarePixels(struct pngx * const pngxP,
-                    int *         const errorLevelP) {
-
-    if (pngxP->info_ptr->valid & PNG_INFO_pHYs) {
-        float const r =
-            (float)pngxP->info_ptr->x_pixels_per_unit /
-            pngxP->info_ptr->y_pixels_per_unit;
-
-        if (r != 1.0) {
-            pm_message ("warning - non-square pixels; "
-                        "to fix do a 'pamscale -%cscale %g'",
-                        r < 1.0 ? 'x' : 'y',
-                        r < 1.0 ? 1.0 / r : r );
-            *errorLevelP = PNMTOPNG_WARNING_LEVEL;
-        }
-    }
-}
-
-
-
-#define GET_PNG_VAL(p) get_png_val(&(p), pngxP->info_ptr->bit_depth)
-
-
-
-static void
-makeXelRow(xel *               const xelrow,
-           xelval              const maxval,
-           int                 const pnmType,
-           struct pngx *       const pngxP,
-           const png_byte *    const pngRasterRow,
-           pngcolor            const bgColor,
-           enum alpha_handling const alphaHandling,
-           double              const totalgamma) {
-
-    const png_byte * pngPixelP;
-    unsigned int col;
-
-    pngPixelP = &pngRasterRow[0];  /* initial value */
-    for (col = 0; col < pngxP->info_ptr->width; ++col) {
-        switch (pngxP->info_ptr->color_type) {
-        case PNG_COLOR_TYPE_GRAY: {
-            pngcolor fgColor;
-            fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP);
-            setXel(&xelrow[col], fgColor, bgColor, alphaHandling,
-                   isTransparentColor(fgColor, pngxP, totalgamma) ?
-                   0 : maxval);
-        }
-        break;
-
-        case PNG_COLOR_TYPE_GRAY_ALPHA: {
-            pngcolor fgColor;
-            png_uint_16 alpha;
-
-            fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP);
-            alpha = GET_PNG_VAL(pngPixelP);
-            setXel(&xelrow[col], fgColor, bgColor, alphaHandling, alpha);
-        }
-        break;
-
-        case PNG_COLOR_TYPE_PALETTE: {
-            png_uint_16 const index        = GET_PNG_VAL(pngPixelP);
-            png_color   const paletteColor = pngxP->info_ptr->palette[index];
-
-            pngcolor fgColor;
-
-            fgColor.r = paletteColor.red;
-            fgColor.g = paletteColor.green;
-            fgColor.b = paletteColor.blue;
-
-            setXel(&xelrow[col], fgColor, bgColor, alphaHandling,
-                   (pngxP->info_ptr->valid & PNG_INFO_tRNS) &&
-                   index < pngxP->info_ptr->num_trans ?
-                   pngxP->info_ptr->trans[index] : maxval);
-        }
-        break;
-                
-        case PNG_COLOR_TYPE_RGB: {
-            pngcolor fgColor;
-
-            fgColor.r = GET_PNG_VAL(pngPixelP);
-            fgColor.g = GET_PNG_VAL(pngPixelP);
-            fgColor.b = GET_PNG_VAL(pngPixelP);
-            setXel(&xelrow[col], fgColor, bgColor, alphaHandling,
-                   isTransparentColor(fgColor, pngxP, totalgamma) ?
-                   0 : maxval);
-        }
-        break;
-
-        case PNG_COLOR_TYPE_RGB_ALPHA: {
-            pngcolor fgColor;
-            png_uint_16 alpha;
-
-            fgColor.r = GET_PNG_VAL(pngPixelP);
-            fgColor.g = GET_PNG_VAL(pngPixelP);
-            fgColor.b = GET_PNG_VAL(pngPixelP);
-            alpha     = GET_PNG_VAL(pngPixelP);
-            setXel(&xelrow[col], fgColor, bgColor, alphaHandling, alpha);
-        }
-        break;
-
-        default:
-            pm_error("unknown PNG color type: %d",
-                     pngxP->info_ptr->color_type);
-        }
-    }
-}
-
-
-
-static void
-writePnm(FILE *              const ofP,
-         xelval              const maxval,
-         int                 const pnmType,
-         struct pngx *       const pngxP,
-         png_byte **         const pngRaster,
-         pngcolor            const bgColor,
-         enum alpha_handling const alphaHandling,
-         double              const totalgamma) {
-/*----------------------------------------------------------------------------
-   Write a PNM of either the image or the alpha mask, according to
-   'alphaHandling' that is in the PNG image described by *pngxP and
-   pngRaster[][].
-
-   'pnmType' and 'maxval' are of the output image.
-
-   Use background color 'bgColor' in the output if the PNG is such that a
-   background color is needed.
------------------------------------------------------------------------------*/
-    int const plainFalse = 0;
-
-    xel * xelrow;
-    unsigned int row;
-
-    if (verbose)
-        pm_message("writing a %s file (maxval=%u)",
-                   pnmType == PBM_TYPE ? "PBM" :
-                   pnmType == PGM_TYPE ? "PGM" :
-                   pnmType == PPM_TYPE ? "PPM" :
-                   "UNKNOWN!", 
-                   maxval);
-    
-    xelrow = pnm_allocrow(pngxP->info_ptr->width);
-
-    pnm_writepnminit(stdout,
-                     pngxP->info_ptr->width, pngxP->info_ptr->height, maxval,
-                     pnmType, plainFalse);
-
-    for (row = 0; row < pngxP->info_ptr->height; ++row) {
-        makeXelRow(xelrow, maxval, pnmType, pngxP, pngRaster[row], bgColor,
-                   alphaHandling, totalgamma);
-
-        pnm_writepnmrow(ofP, xelrow, pngxP->info_ptr->width, maxval,
-                        pnmType, plainFalse);
-    }
-    pnm_freerow (xelrow);
-}
-
-
-
-static void 
-convertpng(FILE *             const ifP, 
-           FILE *             const tfP, 
-           struct cmdlineInfo const cmdline,
-           int *              const errorLevelP) {
-
-    png_byte ** pngRaster;
-    int pnmType;
-    pngcolor bgColor;
-    float totalgamma;
-    jmp_buf jmpbuf;
-    struct pngx * pngxP;
-
-    *errorLevelP = 0;
-
-    if (setjmp(jmpbuf))
-        pm_error ("setjmp returns error condition");
-
-    pngx_createRead(&pngxP, &jmpbuf);
-
-    readPng(pngxP, ifP, &pngRaster);
-
-    if (verbose)
-        dumpPngInfo(pngxP);
-
-    if (cmdline.time)
-        showTime(pngxP);
-    if (tfP)
-        saveText(pngxP, tfP);
-
-    warnNonsquarePixels(pngxP, errorLevelP);
-
-    setupGammaCorrection(pngxP, cmdline.gamma, &totalgamma);
-
-    setupSignificantBits(pngxP, cmdline.alpha, &maxval, errorLevelP);
-
-    getBackgroundColor(pngxP, cmdline.background, totalgamma, maxval,
-                       &bgColor);
-
-    determineOutputType(pngxP, cmdline.alpha, bgColor, maxval, &pnmType);
-
-    writePnm(stdout, maxval, pnmType, pngxP, pngRaster, bgColor, 
-             cmdline.alpha, totalgamma);
-
-    fflush(stdout);
-
-    freePngRaster(pngRaster, pngxP);
-
-    pngx_destroy(pngxP);
-}
-
-
-
-int 
-main(int argc, const char *argv[]) {
-
-    struct cmdlineInfo cmdline;
-    FILE * ifP;
-    FILE * tfP;
-    int errorLevel;
-
-    pm_proginit(&argc, argv);
-
-    parseCommandLine(argc, argv, &cmdline);
-
-    verbose = cmdline.verbose;
-
-    ifP = pm_openr(cmdline.inputFilespec);
-
-    if (cmdline.text)
-        tfP = pm_openw(cmdline.text);
-    else
-        tfP = NULL;
-
-    convertpng(ifP, tfP, cmdline, &errorLevel);
-
-    if (tfP)
-        pm_close(tfP);
-
-    pm_close(ifP);
-    pm_close(stdout);
-
-    return errorLevel;
-}
diff --git a/converter/other/pngx.c b/converter/other/pngx.c
new file mode 100644
index 00000000..6d79bcfe
--- /dev/null
+++ b/converter/other/pngx.c
@@ -0,0 +1,95 @@
+#include <png.h>
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "pm.h"
+#include "pngx.h"
+
+
+static void
+errorHandler(png_structp     const png_ptr,
+             png_const_charp const msg) {
+
+    jmp_buf * jmpbufP;
+
+    /* this function, aside from the extra step of retrieving the "error
+       pointer" (below) and the fact that it exists within the application
+       rather than within libpng, is essentially identical to libpng's
+       default error handler.  The second point is critical:  since both
+       setjmp() and longjmp() are called from the same code, they are
+       guaranteed to have compatible notions of how big a jmp_buf is,
+       regardless of whether _BSD_SOURCE or anything else has (or has not)
+       been defined.
+    */
+
+    pm_message("fatal libpng error: %s", msg);
+
+    jmpbufP = png_get_error_ptr(png_ptr);
+
+    if (!jmpbufP) {
+        /* we are completely hosed now */
+        pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating.");
+    }
+
+    longjmp(*jmpbufP, 1);
+}
+
+
+
+void
+pngx_create(struct pngx ** const pngxPP,
+            pngx_rw        const rw,
+            jmp_buf *      const jmpbufP) {
+
+    struct pngx * pngxP;
+
+    MALLOCVAR(pngxP);
+
+    if (!pngxP)
+        pm_error("Failed to allocate memory for PNG object");
+    else {
+        switch(rw) {
+        case PNGX_READ:
+            pngxP->png_ptr = png_create_write_struct(
+                PNG_LIBPNG_VER_STRING,
+                jmpbufP, errorHandler, NULL);
+            break;
+        case PNGX_WRITE:
+            pngxP->png_ptr = png_create_write_struct(
+                PNG_LIBPNG_VER_STRING,
+                jmpbufP, errorHandler, NULL);
+            break;
+        }
+        if (!pngxP->png_ptr)
+            pm_error("cannot allocate main libpng structure (png_ptr)");
+        else {
+            pngxP->info_ptr = png_create_info_struct(pngxP->png_ptr);
+
+            if (!pngxP->info_ptr)
+                pm_error("cannot allocate libpng info structure (info_ptr)");
+            else
+                *pngxPP = pngxP;
+        }
+    }
+}
+
+
+
+void
+pngx_destroy(struct pngx * const pngxP) {
+
+    png_destroy_write_struct(&pngxP->png_ptr, &pngxP->info_ptr);
+
+    free(pngxP);
+}
+
+
+
+bool
+pngx_chunkIsPresent(struct pngx * const pngxP,
+                    uint32_t      const chunkType) {
+
+    return png_get_valid(pngxP->png_ptr, pngxP->info_ptr, chunkType);
+}
+
+
+
diff --git a/converter/other/pngx.h b/converter/other/pngx.h
new file mode 100644
index 00000000..81487ca1
--- /dev/null
+++ b/converter/other/pngx.h
@@ -0,0 +1,29 @@
+#ifndef PNGX_H_INCLUDED
+#define PNGX_H_INCLUDED
+
+/* pngx is designed to be an extension of the PNG library to make using
+   the PNG library easier and cleaner.
+*/
+
+struct pngx {
+    png_structp png_ptr;
+    png_infop info_ptr;
+};
+
+typedef enum {PNGX_READ, PNGX_WRITE} pngx_rw;
+
+
+void
+pngx_create(struct pngx ** const pngxPP,
+            pngx_rw        const rw,
+            jmp_buf *      const jmpbufP);
+
+void
+pngx_destroy(struct pngx * const pngxP);
+
+
+bool
+pngx_chunkIsPresent(struct pngx * const pngxP,
+                    uint32_t      const chunkType);
+
+#endif
diff --git a/converter/other/pnmtopng.c b/converter/other/pnmtopng.c
index 5107c33a..6cef96ad 100644
--- a/converter/other/pnmtopng.c
+++ b/converter/other/pnmtopng.c
@@ -1,6 +1,5 @@
 /*
-** pnmtopng.c -
-** read a portable anymap and produce a Portable Network Graphics file
+** read a PNM image and produce a Portable Network Graphics file
 **
 ** derived from pnmtorast.c (c) 1990,1991 by Jef Poskanzer and some
 ** parts derived from ppmtogif.c by Marcel Wijkstra <wijkstra@fwi.uva.nl>
@@ -63,21 +62,13 @@
 
 #include "pm_c_util.h"
 #include "pnm.h"
+#include "pngx.h"
 #include "pngtxt.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "nstring.h"
 #include "version.h"
 
-/* A hack until we can remove direct access to png_info from the program */
-#if PNG_LIBPNG_VER >= 10400
-#define trans_values trans_color
-#define TRANS_ALPHA trans_alpha
-#else
-#define TRANS_ALPHA trans
-#endif
-
-
 struct zlibCompression {
     /* These are parameters that describe a form of zlib compression.
        Values have the same meaning as the similarly named arguments to
@@ -634,36 +625,6 @@ lookupColorAlpha(coloralphahash_table const caht,
 
 
 
-static void
-pnmtopng_error_handler(png_structp     const png_ptr,
-                       png_const_charp const msg) {
-
-  jmpbuf_wrapper  *jmpbuf_ptr;
-
-  /* this function, aside from the extra step of retrieving the "error
-   * pointer" (below) and the fact that it exists within the application
-   * rather than within libpng, is essentially identical to libpng's
-   * default error handler.  The second point is critical:  since both
-   * setjmp() and longjmp() are called from the same code, they are
-   * guaranteed to have compatible notions of how big a jmp_buf is,
-   * regardless of whether _BSD_SOURCE or anything else has (or has not)
-   * been defined. */
-
-  fprintf(stderr, "pnmtopng:  fatal libpng error: %s\n", msg);
-  fflush(stderr);
-
-  jmpbuf_ptr = png_get_error_ptr(png_ptr);
-  if (jmpbuf_ptr == NULL) {         /* we are completely hosed now */
-    fprintf(stderr,
-      "pnmtopng:  EXTREMELY fatal error: jmpbuf unrecoverable; terminating.\n");
-    fflush(stderr);
-    exit(99);
-  }
-
-  longjmp(jmpbuf_ptr->jmpbuf, 1);
-}
-
-
 /* The following variables belong to getChv() and freeChv() */
 static bool getChv_computed = FALSE;
 static colorhist_vector getChv_chv;
@@ -1031,6 +992,123 @@ analyzeAlpha(FILE *     const ifp,
 
 
 static void
+determineTransparency(struct cmdlineInfo const cmdline,
+                      FILE *             const ifP,
+                      pm_filepos         const rasterPos,
+                      unsigned int       const cols,
+                      unsigned int       const rows,
+                      xelval             const maxval,
+                      int                const format,
+                      FILE *             const afP,
+                      bool *             const alphaP,
+                      int *              const transparentP,
+                      pixel *            const transColorP,
+                      bool *             const transExactP,
+                      gray ***           const alphaMaskP,
+                      gray *             const alphaMaxvalP) {
+/*----------------------------------------------------------------------------
+   Determine the various aspects of transparency we need to generate the
+   PNG.
+
+   Note that there are two kinds of transparency: pixel-by-pixel
+   transparency/translucency with an alpha mask and all pixels of a certain
+   color being transparent.  Both these exist both in input from the user and
+   as representations in the PNG -- i.e. user may supply an alpha mask,
+   or identify a transparent color and the PNG may contain an alpha mask
+   or identify a transparent color.
+
+   We return as *transparentP:
+   
+     -1 PNG is not to have single-color transparency
+      1 PNG is to have single-color transparency as directed by user
+      2 PNG is to have single-color transparency that effects an alpha
+            mask that the user supplied.
+
+   In the cases where there is to be single-color transparency, *transColorP
+   is that color.
+-----------------------------------------------------------------------------*/
+    if (cmdline.alpha) {
+        pixel alphaTranscolor;
+        bool alphaCanBeTransparencyIndex;
+        bool allOpaque;
+        int alphaCols, alphaRows;
+        gray alphaMaxval;
+        gray ** alphaMask;
+
+        if (verbose)
+            pm_message("reading alpha-channel image...");
+        alphaMask = pgm_readpgm(afP, &alphaCols, &alphaRows, &alphaMaxval);
+
+        if (alphaCols != cols || alphaRows != rows) {
+            pm_error("dimensions for image and alpha mask do not agree");
+        }
+        analyzeAlpha(ifP, rasterPos, cols, rows, maxval, format, 
+                     alphaMask, alphaMaxval, &allOpaque,
+                     &alphaCanBeTransparencyIndex, &alphaTranscolor);
+
+        if (alphaCanBeTransparencyIndex && !cmdline.force) {
+            if (verbose)
+                pm_message("converting alpha mask to transparency index");
+            *alphaP       = FALSE;
+            *transparentP = 2;
+            *transColorP  = alphaTranscolor;
+        } else if (allOpaque) {
+            *alphaP       = FALSE;
+            *transparentP = -1;
+        } else {
+            *alphaP       = TRUE;
+            *transparentP = -1;
+        }
+        *alphaMaxvalP = alphaMaxval;
+        *alphaMaskP   = alphaMask;
+    } else {
+        /* Though there's no alpha_mask, we still need an alpha_maxval for
+           use with trans[], which can have stuff in it if the user specified
+           a transparent color.
+        */
+        *alphaP       = FALSE;
+        *alphaMaxvalP = 255;
+
+        if (cmdline.transparent) {
+            const char * transstring2;  
+            /* The -transparent value, but with possible leading '=' removed */
+            if (cmdline.transparent[0] == '=') {
+                *transExactP = TRUE;
+                transstring2 = &cmdline.transparent[1];
+            } else {
+                *transExactP = FALSE;
+                transstring2 = cmdline.transparent;
+            }  
+            /* We do this funny PPM_DEPTH thing instead of just passing 'maxval'
+               to ppm_parsecolor() because ppm_parsecolor() does a cheap maxval
+               scaling, and this is more precise.
+            */
+            PPM_DEPTH(*transColorP, 
+                      ppm_parsecolor(transstring2, PNM_OVERALLMAXVAL),
+                      PNM_OVERALLMAXVAL, maxval);
+
+            *transparentP = 1;
+        } else
+            *transparentP = -1;
+    }
+}
+
+
+
+static void
+determineBackground(struct cmdlineInfo const cmdline,
+                    xelval             const maxval,
+                    xel *              const backColorP) {
+
+  if (cmdline.background) 
+      PPM_DEPTH(*backColorP,
+                ppm_parsecolor(cmdline.background, PNM_OVERALLMAXVAL), 
+                PNM_OVERALLMAXVAL, maxval);;
+}
+
+
+
+static void
 findRedundantBits(FILE *         const ifp, 
                   int            const rasterPos, 
                   int            const cols,
@@ -2148,8 +2226,7 @@ makePngLine(png_byte *           const line,
 
 
 static void
-writeRaster(png_struct *         const png_ptr,
-            png_info *           const info_ptr,
+writeRaster(struct pngx *        const pngxP,
             FILE *               const ifP,
             pm_filepos           const rasterPos,
             unsigned int         const cols,
@@ -2182,7 +2259,7 @@ writeRaster(png_struct *         const png_ptr,
     if (line == NULL)
         pm_error("out of memory allocating PNG row buffer");
 
-    for (pass = 0; pass < png_set_interlace_handling(png_ptr); ++pass) {
+    for (pass = 0; pass < png_set_interlace_handling(pngxP->png_ptr); ++pass) {
         unsigned int row;
         pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
         for (row = 0; row < rows; ++row) {
@@ -2192,9 +2269,9 @@ writeRaster(png_struct *         const png_ptr,
             
             makePngLine(line, xelrow, cols, maxval,
                         alpha, alpha ? alpha_mask[row] : NULL,
-                        cht, caht, info_ptr, png_maxval, depth);
+                        cht, caht, pngxP->info_ptr, png_maxval, depth);
 
-            png_write_row(png_ptr, line);
+            png_write_row(pngxP->png_ptr, line);
         }
     }
     pnm_freerow(xelrow);
@@ -2203,6 +2280,77 @@ writeRaster(png_struct *         const png_ptr,
 
 
 static void
+doHistChunk(bool         const histRequested,
+            pixel        const palettePnm[],
+            FILE *       const ifP,
+            pm_filepos   const rasterPos,
+            unsigned int const cols,
+            unsigned int const rows,
+            xelval       const maxval,
+            int          const format,
+            png_info *   const info_ptr,
+            bool         const verbose) {
+
+    if (histRequested) {
+        colorhist_vector chv;
+        unsigned int colorCt;
+        colorhash_table cht;
+        
+        getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, 
+               &chv, &colorCt);
+
+        cht = ppm_colorhisttocolorhash(chv, colorCt);
+                
+        { 
+            png_uint_16 * histogram;  /* malloc'ed */
+        
+            MALLOCARRAY(histogram, MAXCOLORS);
+
+            if (!histogram)
+                pm_error("Failed to allocate memory for %u-color histogram",
+                         MAXCOLORS);
+            else {
+                unsigned int i;
+                for (i = 0 ; i < MAXCOLORS; ++i) {
+                    int const chvIndex = ppm_lookupcolor(cht, &palettePnm[i]);
+                    if (chvIndex == -1)
+                        histogram[i] = 0;
+                    else
+                        histogram[i] = chv[chvIndex].value;
+                }
+            
+                info_ptr->valid |= PNG_INFO_hIST;
+                info_ptr->hist = histogram;
+                if (verbose)
+                    pm_message("histogram created in PNG stream");
+            }
+        }
+        ppm_freecolorhash(cht);
+    }
+}
+
+
+
+static void
+setColorType(struct pngx * const pngxP,
+             bool          const colorMapped,
+             int           const pnmType,
+             bool          const alpha) {
+
+    if (colorMapped)
+        pngxP->info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
+    else if (pnmType == PPM_TYPE)
+        pngxP->info_ptr->color_type = PNG_COLOR_TYPE_RGB;
+    else
+        pngxP->info_ptr->color_type = PNG_COLOR_TYPE_GRAY;
+
+    if (alpha && pngxP->info_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+        pngxP->info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
+}
+
+
+
+static void
 doGamaChunk(struct cmdlineInfo const cmdline,
             png_info *         const info_ptr) {
             
@@ -2268,6 +2416,96 @@ doTimeChunk(struct cmdlineInfo const cmdline,
 
 
 static void
+reportTrans(struct pngx * const pngxP) {
+
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) {
+        png_bytep trans;
+        int numTrans;
+        png_color_16 * transColorP;
+
+        png_get_tRNS(pngxP->png_ptr, pngxP->info_ptr,
+                     &trans, &numTrans, &transColorP);
+
+        pm_message("Transparent color {gray, red, green, blue} = "
+                   "{%d, %d, %d, %d}",
+                   transColorP->gray,
+                   transColorP->red,
+                   transColorP->green,
+                   transColorP->blue);
+    } else
+        pm_message("No transparent color");
+}
+
+
+static void
+doTrnsChunk(struct pngx * const pngxP,
+            png_byte      const transPalette[],
+            unsigned int  const transPaletteSize,
+            int           const transparent,
+            pixel         const transColor,
+            xelval        const maxval,
+            xelval        const pngMaxval) {
+
+    switch (pngxP->info_ptr->color_type) {
+    case PNG_COLOR_TYPE_PALETTE:
+        if (transPaletteSize > 0) {
+            png_set_tRNS(pngxP->png_ptr, pngxP->info_ptr,
+                         (png_byte *)transPalette,
+                         transPaletteSize /* omit opaque values */,
+                         0);
+        }
+        break;
+    case PNG_COLOR_TYPE_GRAY:
+    case PNG_COLOR_TYPE_RGB:
+        if (transparent > 0) {
+            png_color_16 pngTransColor = 
+                xelToPngColor_16(transColor, maxval, pngMaxval);
+            png_set_tRNS(pngxP->png_ptr, pngxP->info_ptr,
+                         NULL, 0, &pngTransColor);
+        }
+        break;
+    default:
+        /* This is PNG_COLOR_MASK_ALPHA.  Transparency will be handled
+           by the alpha channel, not a transparency color.
+        */
+    {}
+    }
+    if (verbose)
+        reportTrans(pngxP);
+}
+
+
+
+static void
+doBkgdChunk(bool         const bkgdRequested,
+            png_info *   const info_ptr,
+            unsigned int const backgroundIndex,
+            pixel        const backColor,
+            xelval       const maxval,
+            xelval       const pngMaxval,
+            bool         const verbose) {
+    
+    if (bkgdRequested) {
+        info_ptr->valid |= PNG_INFO_bKGD;
+        if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+            info_ptr->background.index = backgroundIndex;
+        } else {
+            info_ptr->background = 
+                xelToPngColor_16(backColor, maxval, pngMaxval);
+            if (verbose)
+                pm_message("Writing bKGD chunk with background color "
+                           " {gray, red, green, blue} = {%d, %d, %d, %d}",
+                           info_ptr->background.gray, 
+                           info_ptr->background.red, 
+                           info_ptr->background.green, 
+                           info_ptr->background.blue ); 
+        }
+    }
+}
+
+
+
+static void
 doSbitChunk(png_info * const pngInfoP,
             xelval     const pngMaxval,
             xelval     const maxval,
@@ -2322,6 +2560,7 @@ doSbitChunk(png_info * const pngInfoP,
 static void 
 convertpnm(struct cmdlineInfo const cmdline,
            FILE *             const ifp,
+           FILE *             const ofp,
            FILE *             const afp,
            FILE *             const pfp,
            FILE *             const tfp,
@@ -2357,8 +2596,8 @@ convertpnm(struct cmdlineInfo const cmdline,
       /* The background color, with maxval equal to that of the input
          image.
       */
-  png_struct *png_ptr;
-  png_info *info_ptr;
+  jmp_buf jmpbuf;
+  struct pngx * pngxP;
 
   bool colorMapped;
   pixel palette_pnm[MAXCOLORS];
@@ -2381,10 +2620,7 @@ convertpnm(struct cmdlineInfo const cmdline,
   unsigned int background_index;
       /* Index into palette[] of the background color. */
 
-  png_uint_16 histogram[MAXCOLORS];
   gray alpha_maxval;
-  int alpha_rows;
-  int alpha_cols;
   const char * noColormapReason;
       /* The reason that we shouldn't make a colormapped PNG, or NULL if
          we should.  malloc'ed null-terminated string.
@@ -2406,38 +2642,21 @@ convertpnm(struct cmdlineInfo const cmdline,
       /* The row of the input image currently being processed */
 
   int pnm_type;
-  xelval maxmaxval;
   gray ** alpha_mask;
 
-  /* these guys are initialized to quiet compiler warnings: */
-  maxmaxval = 255;
-  alpha_mask = NULL;
+  /* We initialize these guys to quiet compiler warnings: */
   depth = 0;
-  errorlevel = 0;
 
-  png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
-    &pnmtopng_jmpbuf_struct, pnmtopng_error_handler, NULL);
-  if (png_ptr == NULL) {
-    pm_closer (ifp);
-    pm_error ("cannot allocate main libpng structure (png_ptr)");
-  }
+  errorlevel = 0;
 
-  info_ptr = png_create_info_struct (png_ptr);
-  if (info_ptr == NULL) {
-    png_destroy_write_struct (&png_ptr, (png_infopp)NULL);
-    pm_closer (ifp);
-    pm_error ("cannot allocate libpng info structure (info_ptr)");
-  }
+  if (setjmp(jmpbuf))
+      pm_error ("setjmp returns error condition");
 
-  if (setjmp (pnmtopng_jmpbuf_struct.jmpbuf)) {
-    png_destroy_write_struct (&png_ptr, &info_ptr);
-    pm_closer (ifp);
-    pm_error ("setjmp returns error condition (1)");
-  }
+  pngx_create(&pngxP, PNGX_WRITE, &jmpbuf);
 
-  pnm_readpnminit (ifp, &cols, &rows, &maxval, &format);
+  pnm_readpnminit(ifp, &cols, &rows, &maxval, &format);
   pm_tell2(ifp, &rasterPos, sizeof(rasterPos));
-  pnm_type = PNM_FORMAT_TYPE (format);
+  pnm_type = PNM_FORMAT_TYPE(format);
 
   xelrow = pnm_allocrow(cols);
 
@@ -2450,71 +2669,12 @@ convertpnm(struct cmdlineInfo const cmdline,
       pm_message ("reading a PPM file (maxval=%d)", maxval);
   }
 
-  if (pnm_type == PGM_TYPE)
-    maxmaxval = PGM_OVERALLMAXVAL;
-  else if (pnm_type == PPM_TYPE)
-    maxmaxval = PPM_OVERALLMAXVAL;
-
-  if (cmdline.transparent) {
-      const char * transstring2;  
-          /* The -transparent value, but with possible leading '=' removed */
-      if (cmdline.transparent[0] == '=') {
-          transexact = 1;
-          transstring2 = &cmdline.transparent[1];
-      } else {
-          transexact = 0;
-          transstring2 = cmdline.transparent;
-      }  
-      /* We do this funny PPM_DEPTH thing instead of just passing 'maxval'
-         to ppm_parsecolor() because ppm_parsecolor() does a cheap maxval
-         scaling, and this is more precise.
-      */
-      PPM_DEPTH(transcolor, ppm_parsecolor(transstring2, maxmaxval),
-                maxmaxval, maxval);
-  }
-  if (cmdline.alpha) {
-    pixel alpha_transcolor;
-    bool alpha_can_be_transparency_index;
-    bool all_opaque;
+  determineTransparency(cmdline, ifp, rasterPos, cols, rows, maxval, format,
+                        afp,
+                        &alpha, &transparent, &transcolor, &transexact,
+                        &alpha_mask, &alpha_maxval);
 
-    if (verbose)
-      pm_message ("reading alpha-channel image...");
-    alpha_mask = pgm_readpgm (afp, &alpha_cols, &alpha_rows, &alpha_maxval);
-
-    if (alpha_cols != cols || alpha_rows != rows) {
-      png_destroy_write_struct (&png_ptr, &info_ptr);
-      pm_closer (ifp);
-      pm_error ("dimensions for image and alpha mask do not agree");
-    }
-    analyzeAlpha(ifp, rasterPos, cols, rows, maxval, format, 
-                 alpha_mask, alpha_maxval, &all_opaque,
-                 &alpha_can_be_transparency_index, &alpha_transcolor);
-
-    if (alpha_can_be_transparency_index && !cmdline.force) {
-      if (verbose)
-        pm_message ("converting alpha mask to transparency index");
-      alpha = FALSE;
-      transparent = 2;
-      transcolor = alpha_transcolor;
-    } else if (all_opaque) {
-        alpha = FALSE;
-        transparent = -1;
-    } else {
-      alpha = TRUE;
-      transparent = -1;
-    }
-  } else {
-      /* Though there's no alpha_mask, we still need an alpha_maxval for
-         use with trans[], which can have stuff in it if the user specified
-         a transparent color.
-      */
-      alpha = FALSE;
-      alpha_maxval = 255;
-      transparent = cmdline.transparent ? 1 : -1;
-  }
-  if (cmdline.background) 
-      PPM_DEPTH(backcolor, ppm_parsecolor(cmdline.background, maxmaxval), 
-                maxmaxval, maxval);;
+  determineBackground(cmdline, maxval, &backcolor);
 
   /* first of all, check if we have a grayscale image written as PPM */
 
@@ -2540,8 +2700,6 @@ convertpnm(struct cmdlineInfo const cmdline,
   /* handle `odd' maxvalues */
 
   if (maxval > 65535 && !cmdline.downscale) {
-      png_destroy_write_struct(&png_ptr, &info_ptr);
-      pm_closer(ifp);
       pm_error("can only handle files up to 16-bit "
                "(use -downscale to override");
   }
@@ -2589,174 +2747,82 @@ convertpnm(struct cmdlineInfo const cmdline,
   png_maxval = pm_bitstomaxval(depth);
 
   if (setjmp (pnmtopng_jmpbuf_struct.jmpbuf)) {
-    png_destroy_write_struct (&png_ptr, &info_ptr);
-    pm_closer (ifp);
     pm_error ("setjmp returns error condition (2)");
   }
 
-  png_init_io (png_ptr, stdout);
-  info_ptr->width = cols;
-  info_ptr->height = rows;
-  info_ptr->bit_depth = depth;
+  pngxP->info_ptr->width = cols;
+  pngxP->info_ptr->height = rows;
+  pngxP->info_ptr->bit_depth = depth;
 
-  if (colorMapped)
-    info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
-  else if (pnm_type == PPM_TYPE)
-    info_ptr->color_type = PNG_COLOR_TYPE_RGB;
-  else
-    info_ptr->color_type = PNG_COLOR_TYPE_GRAY;
+  setColorType(pngxP, colorMapped, pnm_type, alpha);
 
-  if (alpha && info_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
-    info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
+  pngxP->info_ptr->interlace_type = cmdline.interlace;
 
-  info_ptr->interlace_type = cmdline.interlace;
+  doGamaChunk(cmdline, pngxP->info_ptr);
 
-  doGamaChunk(cmdline, info_ptr);
+  doChrmChunk(cmdline, pngxP->info_ptr);
 
-  doChrmChunk(cmdline, info_ptr);
+  doPhysChunk(cmdline, pngxP->info_ptr);
 
-  doPhysChunk(cmdline, info_ptr);
+  if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
 
-  if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
-
-    /* creating PNG palette  (PLTE and tRNS chunks) */
+    /* creating PNG palette (Not counting the transparency palette) */
 
     createPngPalette(palette_pnm, palette_size, maxval,
                      trans_pnm, trans_size, alpha_maxval, 
                      palette, trans);
-    info_ptr->valid |= PNG_INFO_PLTE;
-    info_ptr->palette = palette;
-    info_ptr->num_palette = palette_size;
-    if (trans_size > 0) {
-        info_ptr->valid |= PNG_INFO_tRNS;
-        info_ptr->TRANS_ALPHA = trans;
-        info_ptr->num_trans = trans_size;   /* omit opaque values */
-    }
-    /* creating hIST chunk */
-    if (cmdline.hist) {
-        colorhist_vector chv;
-        unsigned int colors;
-        colorhash_table cht;
-        
-        getChv(ifp, rasterPos, cols, rows, maxval, format, MAXCOLORS, 
-               &chv, &colors);
+    pngxP->info_ptr->valid |= PNG_INFO_PLTE;
+    pngxP->info_ptr->palette = palette;
+    pngxP->info_ptr->num_palette = palette_size;
 
-        cht = ppm_colorhisttocolorhash (chv, colors);
-                
-        { 
-            unsigned int i;
-            for (i = 0 ; i < MAXCOLORS; ++i) {
-                int const chvIndex = ppm_lookupcolor(cht, &palette_pnm[i]);
-                if (chvIndex == -1)
-                    histogram[i] = 0;
-                else
-                    histogram[i] = chv[chvIndex].value;
-            }
-        }
-
-        ppm_freecolorhash(cht);
-
-        info_ptr->valid |= PNG_INFO_hIST;
-        info_ptr->hist = histogram;
-        if (verbose)
-            pm_message("histogram created");
-    }
-  } else { /* color_type != PNG_COLOR_TYPE_PALETTE */
-    if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
-        info_ptr->color_type == PNG_COLOR_TYPE_RGB) {
-        if (transparent > 0) {
-            info_ptr->valid |= PNG_INFO_tRNS;
-            info_ptr->trans_values = 
-                xelToPngColor_16(transcolor, maxval, png_maxval);
-        }
-    } else {
-        /* This is PNG_COLOR_MASK_ALPHA.  Transparency will be handled
-           by the alpha channel, not a transparency color.
-        */
-    }
-    if (verbose) {
-        if (info_ptr->valid && PNG_INFO_tRNS) 
-            pm_message("Transparent color {gray, red, green, blue} = "
-                       "{%d, %d, %d, %d}",
-                       info_ptr->trans_values.gray,
-                       info_ptr->trans_values.red,
-                       info_ptr->trans_values.green,
-                       info_ptr->trans_values.blue);
-        else
-            pm_message("No transparent color");
-    }
+    doHistChunk(cmdline.hist, palette_pnm, ifp, rasterPos,
+                cols, rows, maxval, format,
+                pngxP->info_ptr, cmdline.verbose);
   }
 
-  /* bKGD chunk */
-  if (cmdline.background) {
-      info_ptr->valid |= PNG_INFO_bKGD;
-      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
-          info_ptr->background.index = background_index;
-      } else {
-          info_ptr->background = 
-              xelToPngColor_16(backcolor, maxval, png_maxval);
-          if (verbose)
-              pm_message("Writing bKGD chunk with background color "
-                         " {gray, red, green, blue} = {%d, %d, %d, %d}",
-                         info_ptr->background.gray, 
-                         info_ptr->background.red, 
-                         info_ptr->background.green, 
-                         info_ptr->background.blue ); 
-      }
-  }
+  doTrnsChunk(pngxP, trans, trans_size,
+              transparent, transcolor, maxval, png_maxval);
+
+  doBkgdChunk(!!cmdline.background, pngxP->info_ptr,
+              background_index, backcolor,
+              maxval, png_maxval, cmdline.verbose);
 
-  doSbitChunk(info_ptr, png_maxval, maxval, alpha, alpha_maxval);
+  doSbitChunk(pngxP->info_ptr, png_maxval, maxval, alpha, alpha_maxval);
 
   /* tEXT and zTXT chunks */
   if (cmdline.text || cmdline.ztxt)
-      pnmpng_read_text(info_ptr, tfp, !!cmdline.ztxt, cmdline.verbose);
+      pnmpng_read_text(pngxP->info_ptr, tfp, !!cmdline.ztxt, cmdline.verbose);
 
-  doTimeChunk(cmdline, info_ptr);
+  doTimeChunk(cmdline, pngxP->info_ptr);
 
   if (cmdline.filterSet != 0)
-      png_set_filter(png_ptr, 0, cmdline.filterSet);
+      png_set_filter(pngxP->png_ptr, 0, cmdline.filterSet);
 
-  setZlibCompression(png_ptr, cmdline.zlibCompression);
+  setZlibCompression(pngxP->png_ptr, cmdline.zlibCompression);
+
+  png_init_io(pngxP->png_ptr, ofp);
 
   /* write the png-info struct */
-  png_write_info(png_ptr, info_ptr);
+  png_write_info(pngxP->png_ptr, pngxP->info_ptr);
 
   if (cmdline.text || cmdline.ztxt)
       /* prevent from being written twice with png_write_end */
-      info_ptr->num_text = 0;
+      pngxP->info_ptr->num_text = 0;
 
   if (cmdline.modtime)
       /* prevent from being written twice with png_write_end */
-      info_ptr->valid &= ~PNG_INFO_tIME;
+      pngxP->info_ptr->valid &= ~PNG_INFO_tIME;
 
   /* let libpng take care of, e.g., bit-depth conversions */
-  png_set_packing (png_ptr);
+  png_set_packing(pngxP->png_ptr);
 
-  writeRaster(png_ptr, info_ptr, ifp, rasterPos, cols, rows, maxval, format,
+  writeRaster(pngxP, ifp, rasterPos,
+              cols, rows, maxval, format,
               png_maxval, depth, alpha, alpha_mask, cht, caht);
 
-  png_write_end (png_ptr, info_ptr);
-
+  png_write_end(pngxP->png_ptr, pngxP->info_ptr);
 
-#if 0
-  /* The following code may be intended to solve some segfault problem
-     that arises with png_destroy_write_struct().  The latter is the
-     method recommended in the libpng documentation and this program 
-     will not compile under Cygwin because the Windows DLL for libpng
-     does not contain png_write_destroy() at all.  Since the author's
-     comment below does not make it clear what the segfault issue is,
-     we cannot consider it.  -Bryan 00.09.15
-*/
-
-  png_write_destroy (png_ptr);
-  /* flush first because free(png_ptr) can segfault due to jmpbuf problems
-     in png_write_destroy */
-  fflush (stdout);
-  free (png_ptr);
-  free (info_ptr);
-#else
-  png_destroy_write_struct(&png_ptr, &info_ptr);
-#endif
+  pngx_destroy(pngxP);
 
   pnm_freerow(xelrow);
 
@@ -2837,7 +2903,7 @@ main(int argc, char *argv[]) {
     else
         tfP = NULL;
 
-    convertpnm(cmdline, ifP, afP, pfP, tfP, &errorlevel);
+    convertpnm(cmdline, ifP, stdout, afP, pfP, tfP, &errorlevel);
     
     if (afP)
         pm_close(afP);
diff --git a/converter/other/tifftopnm.c b/converter/other/tifftopnm.c
index 6665c7fd..1cf869fb 100644
--- a/converter/other/tifftopnm.c
+++ b/converter/other/tifftopnm.c
@@ -52,7 +52,6 @@
 #include <assert.h>
 #include <string.h>
 #include <stdio.h>
-#include <sys/wait.h>
 
 #include "pm_c_util.h"
 #include "shhopt.h"
@@ -381,8 +380,8 @@ readscanline(TIFF *         const tif,
                   "TIFFReadScanline() failed.",
                   row, plane);
     else if (bps == 8) {
-        int sample;
-        for (sample = 0; sample < cols*spp; sample++) 
+        unsigned int sample;
+        for (sample = 0; sample < cols * spp; ++sample) 
             samplebuf[sample] = scanbuf[sample];
     } else if (bps < 8) {
         /* Note that in this format, samples do not span bytes.  Rather,
@@ -390,12 +389,12 @@ readscanline(TIFF *         const tif,
            At least that's how I infer the format from reading pnmtotiff.c
            -Bryan 00.11.18
            */
-        int sample;
-        int bitsleft;
+        unsigned int sample;
+        unsigned int bitsleft;
         unsigned char * inP;
 
-        for (sample = 0, bitsleft=8, inP=scanbuf; 
-             sample < cols*spp; 
+        for (sample = 0, bitsleft = 8, inP = scanbuf; 
+             sample < cols * spp; 
              ++sample) {
             if (bitsleft == 0) {
                 ++inP; 
@@ -412,6 +411,7 @@ readscanline(TIFF *         const tif,
                 pm_error("Internal error: invalid value for fillorder: %u", 
                          fillorder);
             }
+            assert(bitsleft >= bps);
             bitsleft -= bps; 
             if (bitsleft < bps)
                 /* Don't count dregs at end of byte */
@@ -433,10 +433,10 @@ readscanline(TIFF *         const tif,
         for (sample = 0; sample < cols*spp; ++sample)
             samplebuf[sample] = scanbuf16[sample];
     } else if (bps == 32) {
-        uint32 * const scanbuf32 = (uint32 *) scanbuf;
+        const uint32 * const scanbuf32 = (const uint32 *) scanbuf;
         unsigned int sample;
         
-        for (sample = 0; sample < cols*spp; ++sample)
+        for (sample = 0; sample < cols * spp; ++sample)
             samplebuf[sample] = scanbuf32[sample];
     } else 
         pm_error("Internal error: invalid bits per sample passed to "
@@ -739,38 +739,34 @@ spawnWithInputPipe(const char *  const shellCmd,
         asprintfN(errorP, "Failed to create pipe for process input.  "
                   "Errno=%d (%s)", errno, strerror(errno));
     else {
-        int rc;
-
-        rc = fork();
-
-        if (rc < 0) {
-            asprintfN(errorP, "Failed to fork a process.  errno=%d (%s)",
-                      errno, strerror(errno));
-        } else if (rc == 0) {
-            /* This is the child */
-            int rc;
-            close(fd[PIPE_WRITE]);
-            close(STDIN_FILENO);
-            dup2(fd[PIPE_READ], STDIN_FILENO);
+        int iAmParent;
+        pid_t childPid;
 
-            rc = system(shellCmd);
+        pm_fork(&iAmParent, &childPid, errorP);
 
-            exit(rc);
-        } else {
-            /* Parent */
-            pid_t const childPid = rc;
+        if (!*errorP) {
+            if (iAmParent) {
+                close(fd[PIPE_READ]);
 
-            close(fd[PIPE_READ]);
+                *pidP   = childPid;
+                *pipePP = fdopen(fd[PIPE_WRITE], "w");
+                
+                if (*pipePP == NULL)
+                    asprintfN(errorP,"Unable to create stream from pipe.  "
+                              "fdopen() fails with errno=%d (%s)",
+                              errno, strerror(errno));
+                else
+                    *errorP = NULL;
+            } else {
+                int rc;
+                close(fd[PIPE_WRITE]);
+                close(STDIN_FILENO);
+                dup2(fd[PIPE_READ], STDIN_FILENO);
 
-            *pidP   = childPid;
-            *pipePP = fdopen(fd[PIPE_WRITE], "w");
+                rc = system(shellCmd);
 
-            if (*pipePP == NULL)
-                asprintfN(errorP,"Unable to create stream from pipe.  "
-                          "fdopen() fails with errno=%d (%s)",
-                          errno, strerror(errno));
-            else
-                *errorP = NULL;
+                exit(rc);
+            }
         }
     }
 }
@@ -787,7 +783,7 @@ createFlipProcess(FILE *         const outFileP,
    Create a process that runs the program Pamflip and writes its output
    to *imageoutFileP.
 
-   The process takes it input from a pipe that we create.  We return as
+   The process takes its input from a pipe that we create.  We return as
    *inPipePP a file stream connected to the other end of that pipe.
 
    I.e. Caller will write a Netpbm file stream to **inPipePP and a flipped
@@ -973,12 +969,12 @@ pnmOut_init(FILE *         const imageoutFileP,
    data, pnmOut get 'cols' x 'rows' data, but its output file may be
    'rows x cols'.
 
-   Because the pnmOut object must be set up to receive flipped or not
-   flipped input, we have *flipOkP and *noflipOkP outputs that tell
-   Caller whether he has to flip or not.  In the unique case that
-   the TIFF matrix is already oriented the way the output PNM file needs
-   to be, flipping is idempotent, so both *flipOkP and *noflipOkP are
-   true.
+   Because we must set up the pnmOut object either to receive flipped or not
+   flipped input, we have *flipOkP and *noflipOkP outputs that tell Caller
+   whether he has to flip or not.  Note that Caller also influences which way
+   we set up pnmOut, with his 'flipIfNeeded' argument.  In the unique case
+   that the TIFF matrix is already oriented the way the output PNM file needs
+   to be, flipping is idempotent, so both *flipOkP and *noflipOkP are true.
 -----------------------------------------------------------------------------*/
     pnmOutP->imageoutFileP = imageoutFileP;
     pnmOutP->alphaFileP    = alphaFileP;
@@ -1040,14 +1036,12 @@ pnmOut_term(pnmOut * const pnmOutP,
                        "waiting for Pamflip to terminate");
 
         if (pnmOutP->imagePipeP) {
-            int status;
             fclose(pnmOutP->imagePipeP);
-            waitpid(pnmOutP->imageFlipPid, &status, 0);
+            pm_waitpidSimple(pnmOutP->imageFlipPid);
         }
         if (pnmOutP->alphaPipeP) {
-            int status;
             fclose(pnmOutP->alphaPipeP);
-            waitpid(pnmOutP->alphaFlipPid, &status, 0);
+            pm_waitpidSimple(pnmOutP->alphaFlipPid);
         }
     } else {
         if (pnmOutP->imageoutFileP)
diff --git a/converter/pbm/Makefile b/converter/pbm/Makefile
index c859b105..0461eedc 100644
--- a/converter/pbm/Makefile
+++ b/converter/pbm/Makefile
@@ -7,10 +7,11 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 include $(BUILDDIR)/config.mk
 
-PORTBINARIES =	atktopbm brushtopbm cmuwmtopbm ddbugtopbm g3topbm escp2topbm \
+PORTBINARIES =	atktopbm brushtopbm cistopbm cmuwmtopbm \
+		ddbugtopbm g3topbm escp2topbm \
 		icontopbm macptopbm mdatopbm mgrtopbm mrftopbm \
 		pbmto10x pbmto4425 pbmtoascii pbmtoatk \
-		pbmtobbnbg pbmtocmuwm pbmtodjvurle \
+		pbmtobbnbg pbmtocis pbmtocmuwm pbmtodjvurle \
 		pbmtoepsi pbmtoepson pbmtoescp2 \
 		pbmtog3 pbmtogem pbmtogo pbmtoibm23xx pbmtoicon pbmtolj \
 		pbmtoln03 pbmtolps \
diff --git a/converter/pbm/cistopbm.c b/converter/pbm/cistopbm.c
new file mode 100644
index 00000000..591e2aa5
--- /dev/null
+++ b/converter/pbm/cistopbm.c
@@ -0,0 +1,180 @@
+/*
+ *  cistopbm: Convert images in the CompuServe RLE format to PBM
+ *  Copyright (C) 2009  John Elliott
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pbm.h"
+
+
+static void syntax(const char *prog)
+{
+        pm_usage(" { options } { input }\n\n"
+                 "Input file should be in CompuServe RLE format.\n"
+                 "Output file will be in PBM format.\n"
+                 "Options:\n"
+                 "-i, --inverse: Reverse black and white.\n"
+                 "--:            End of options\n\n"
+"cistopbm v1.01, Copyright 2009 John Elliott <jce@seasip.demon.co.uk>\n"
+"This program is redistributable under the terms of the GNU General Public\n"
+"License, version 2 or later.\n"
+                 );
+}
+
+int main(int argc, const char **argv)
+{
+    FILE *ifP;
+    int c[3];
+    int inoptions = 1;
+    int n, x, y;
+    int bw = PBM_BLACK;     /* Default colouring is white on black */
+    const char *inpname = NULL;
+    int height, width;
+    bit **bits;
+
+    pm_proginit(&argc, argv);
+
+    for (n = 1; n < argc; n++)
+    {
+        if (!strcmp(argv[n], "--"))
+        {
+            inoptions = 0;
+            continue;
+        }
+        if (inoptions)
+        {
+            if (pm_keymatch(argv[n], "-h", 2) ||
+                pm_keymatch(argv[n], "-H", 2) ||
+                pm_keymatch(argv[n], "--help", 6))
+            {
+                syntax(argv[0]);
+                return EXIT_SUCCESS;
+            }
+            if (pm_keymatch(argv[n], "-i", 2) ||
+                pm_keymatch(argv[n], "-I", 2) ||
+                pm_keymatch(argv[n], "--inverse", 9))
+            {
+                bw ^= (PBM_WHITE ^ PBM_BLACK);
+                continue;
+            }
+            if (argv[n][0] == '-' && argv[n][1] != 0)
+            {
+                pm_message("Unknown option: %s", argv[n]);
+                syntax(argv[0]);
+                return EXIT_FAILURE;
+            }
+        }
+
+        if (inpname == NULL) inpname = argv[n];
+        else { syntax(argv[0]); return EXIT_FAILURE; }
+    }
+    if (inpname == NULL) inpname = "-";
+    ifP  = pm_openr(inpname);
+
+    /* There may be junk before the magic number. If so, skip it. */
+    x = 0;
+    c[0] = c[1] = c[2] = EOF;
+
+    /* Read until the array c[] holds the magic number. */
+    do
+    {
+        c[0] = c[1];
+        c[1] = c[2];
+        c[2] = fgetc(ifP);
+
+        /* If character read was EOF, end of file was reached and magic number
+         * not found.
+         */
+        if (c[2] == EOF)
+        {
+            pm_error("Input file is not in CompuServe RLE format");
+        }
+        ++x;
+    } while (c[0] != 0x1B || c[1] != 0x47);
+
+    /* x = number of bytes read. Should be 3 if signature is at the start */
+    if (x > 3)
+    {
+        pm_message("Warning: %d bytes of junk skipped before image",
+                   x - 3);
+    }
+    /* Parse the resolution */
+    switch(c[2])
+    {
+    case 0x48:      height = 192; width = 256; break;
+    case 0x4D:      height =  96; width = 128; break;
+    default:        pm_error("Unknown resolution 0x%02x", c[2]);
+        break;
+    }
+    /* Convert the data */
+    bits = pbm_allocarray(width, height);
+    x = y = 0;
+    do
+    {
+        c[0] = fgetc(ifP);
+
+        /* Stop if we hit EOF or Escape */
+        if (c[0] == EOF)  break;        /* EOF */
+        if (c[0] == 0x1B) break;        /* End of graphics */
+        /* Other non-printing characters are ignored; some files contain a
+         * BEL
+         */
+        if (c[0] < 0x20)  continue;
+
+        /* Each character gives the number of pixels to draw in the appropriate
+         * colour.
+         */
+        for (n = 0x20; n < c[0]; n++)
+        {
+            if (x < width && y < height) bits[y][x] = bw;
+            x++;
+            /* Wrap at end of line */
+            if (x >= width)
+            {
+                x = 0;
+                y++;
+            }
+        }
+        /* And toggle colours */
+        bw ^= (PBM_WHITE ^ PBM_BLACK);
+    }
+    while (1);
+
+    /* See if the end-graphics signature (ESC G N) is present. */
+    c[1] = EOF;
+    if (c[0] == 0x1B)
+    {
+        c[1] = fgetc(ifP);
+        c[2] = fgetc(ifP);
+    }
+    if (c[0] != 0x1B || c[1] != 0x47 || c[2] != 0x4E)
+    {
+        pm_message("Warning: End-graphics signature not found");
+    }
+    /* See if we decoded the right number of pixels */
+    if (x != 0 || y != height)
+    {
+        pm_message("Warning: %d pixels found, should be %d",
+                   y * width + x, width * height);
+    }
+    pbm_writepbm(stdout, bits, width, height, 0);
+    pm_close(ifP);
+    return 0;       
+}
diff --git a/converter/pbm/pbmtocis.c b/converter/pbm/pbmtocis.c
new file mode 100644
index 00000000..9bb42c56
--- /dev/null
+++ b/converter/pbm/pbmtocis.c
@@ -0,0 +1,170 @@
+/*
+ *  cistopbm: Convert images in the CompuServe RLE format to PBM
+ *  Copyright (C) 2009  John Elliott
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pbm.h"
+
+/* The maximum length of a run. Limit it to 0x5E bytes so that it is always
+ * represented by a printable character 0x20-0x7E */
+#define MAXRUNLENGTH 0x5E
+
+static void syntax(const char *prog)
+{
+    pm_usage(" { options } { input } }\n\n"
+         "Input file should be in PBM format.\n"
+         "Output will be in CompuServe RLE format.\n"
+         "Options:\n"
+         "-i, --inverse: Reverse black and white.\n"
+         "-w, --whitebg: White background.\n"
+         "--:            End of options\n\n"
+"pbmtocis v1.00, Copyright 2009 John Elliott <jce@seasip.demon.co.uk>\n"
+"This program is redistributable under the terms of the GNU General Public\n"
+"License, version 2 or later.\n"
+         );
+}
+
+int main(int argc, const char **argv)
+{
+    FILE *ofP = stdout;
+    FILE *ifP;
+    int inoptions = 1;
+    int n, x, y;
+    int bg = PBM_BLACK; /* Default colouring is white on black */
+    int inverse = 0;
+    int cell, last, run;
+    const char *inpname = NULL;
+
+    int outh, outw;
+    int height, width;
+    bit **bits;
+
+    pm_proginit(&argc, argv);
+
+    for (n = 1; n < argc; n++)
+    {
+        if (!strcmp(argv[n], "--"))
+        {
+            inoptions = 0;
+            continue;
+        }
+        if (inoptions)
+        {
+            if (pm_keymatch(argv[n], "-h", 2) ||
+                pm_keymatch(argv[n], "-H", 2) ||
+                pm_keymatch(argv[n], "--help", 6))
+            {
+                syntax(argv[0]);
+                return EXIT_SUCCESS;
+            }
+            if (pm_keymatch(argv[n], "-i", 2) ||
+                pm_keymatch(argv[n], "-I", 2) ||
+                pm_keymatch(argv[n], "--inverse", 9))
+            {
+                inverse = 1;
+                continue;
+            }
+            if (pm_keymatch(argv[n], "-w", 2) ||
+                pm_keymatch(argv[n], "-W", 2) ||
+                pm_keymatch(argv[n], "--whitebg", 9))
+            {
+                bg = PBM_WHITE; 
+                continue;
+            }
+            if (argv[n][0] == '-' && argv[n][1] != 0)
+            {
+                pm_message("Unknown option: %s", argv[n]);
+                syntax(argv[0]);
+                return EXIT_FAILURE;
+            }
+        }
+
+        if (inpname == NULL) inpname = argv[n];
+        else { syntax(argv[0]); return EXIT_FAILURE; }
+    }
+    if (inpname == NULL) inpname = "-";
+    ifP  = pm_openr(inpname);
+
+    /* Load the PBM */
+    bits = pbm_readpbm(ifP, &width, &height);
+
+    if      (width <= 128 && height <= 96)  { outw = 128; outh = 96; }
+    else if (width <= 256 && height <= 192) { outw = 256; outh = 192; }
+    else
+    {
+        outw = 256;
+        outh = 192;
+        pm_message("Warning: Input file is larger than 256x192. "
+                "It will be cropped.");
+    }
+    /* Write the magic number */
+    fputc(0x1B, ofP);
+    fputc(0x47, ofP);
+    fputc((outw == 128) ? 0x4D : 0x48, ofP);
+
+    /* And now start encoding */
+    y = x = 0;
+    last = PBM_BLACK;
+    run = 0;
+    while (y < outh)
+    {
+        if (x < width && y < height)    
+        {
+            cell = bits[y][x];
+            if (inverse) cell ^= (PBM_BLACK ^ PBM_WHITE);
+        }
+        else cell = bg;
+
+        if (cell == last)   /* Cell is part of current run */
+        {
+            ++run;
+            if (run > MAXRUNLENGTH)
+            {       
+                fputc(0x20 + MAXRUNLENGTH, ofP);
+                fputc(0x20, ofP);
+                run -= MAXRUNLENGTH;
+            }   
+        }
+        else    /* change */
+        {
+            fputc(run + 0x20, ofP);
+            last = last ^ (PBM_BLACK ^ PBM_WHITE);
+            run = 1;
+        }
+        ++x;
+        if (x >= outw) { x = 0; ++y; }
+    }
+    if (last == bg) /* Last cell written was background. Write foreground */
+    {
+        fputc(run + 0x20, ofP);
+    }
+    else if (run)   /* Write background and foreground */
+    {
+        fputc(run + 0x20, ofP);
+        fputc(0x20, ofP);
+    }
+    /* Write the end-graphics signature */
+    fputc(0x1B, ofP);
+    fputc(0x47, ofP);
+    fputc(0x4E, ofP);
+    pm_close(ifP);
+    return 0;   
+}
diff --git a/doc/HISTORY b/doc/HISTORY
index 41d36953..b4ba92d2 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -4,39 +4,53 @@ Netpbm.
 CHANGE HISTORY 
 --------------
 
-09.09.18 BJH  Release 10.47.04
+09.09.27 BJH  Release 10.48.00
 
-              pambayer: fix unconditional crash/failure when you aren't using
-              Standard Input.
+              ppmsvgalib: Wait to initialize Svgalib to prevent it from
+              interfering with error messages early code might issue, and
+              leaving the console in an undesirable state if the failures
+              cause the program to abort early.
 
-              Build: use AR from config.mk instead of "ar" to build static
-              libraries: libnetpbm, librle, libjasper.
+              tifftopnm: wait for Pamflip processes to terminate before
+              exiting.
 
-              Build: compiles with libpng 1.4 beta.
+              Remove pngtopnm from the package.  Pngtopnm is now an alias
+              for Pngtopam.
 
-              Build: don't use "uint".
+              pngtopam, pnmtopng: Compiles with libpng 1.4 beta.
 
-              Package: fix installation of pnmplain on Windows (.exe suffix).
-
-09.09.03 BJH  Release 10.47.03
+              pamtotiff: do miniswhite properly with 8 and 16 bit samples.
 
-              Build: fix failure to recognize static library in omitting
-              -R from link.
+              pamsumm: fix syntax error message.
 
-09.08.17 BJH  Release 10.47.02
+              pambayer: fix unconditional crash/failure when you aren't using
+              Standard Input.
 
-              tifftopnm: wait for Pamflip processes to terminate before
-              exiting.
+              Add pbmtocis, cistopbm.  Thanks John Elliott
+              <jce@seasip.demon.co.uk>.
 
-              Build: Compiles with libpng 1.4 beta.
+              Build: tifftopnm builds on systems without fork().
 
               Build: work with JPEG library Version 7.
 
-09.07.23 BJH  Release 10.47.01
+              Build: Configure recognizes libvga.a installed without
+              libvga.so and offers that as default.
+
+              Build: Configure recognizes the Mingw-64 compiler.
 
               Build: fix failure of a merge build on a system that doesn't
               have the PNG library.
 
+              Build: fix failure to recognize static library in omitting
+              -R from link.
+
+              Build: use AR from config.mk instead of "ar" to build static
+              libraries: libnetpbm, librle, libjasper.
+
+              Build: don't use "uint".
+
+              Package: fix installation of pnmplain on Windows (.exe suffix).
+
 09.06.27 BJH  Release 10.47.00
 
               Add pamsistoaglyph.  Thanks Scott Pakin.
diff --git a/editor/pamscale.c b/editor/pamscale.c
index 69861e0e..35c76b17 100644
--- a/editor/pamscale.c
+++ b/editor/pamscale.c
@@ -610,11 +610,11 @@ parseCommandLine(int argc,
     OPTENT3(0, "xysize",    OPT_FLAG,    NULL,       &xyfit,               0);
     OPTENT3(0, "xyfit",     OPT_FLAG,    NULL,       &xyfit,               0);
     OPTENT3(0, "xyfill",    OPT_FLAG,    NULL,       &xyfill,              0);
-    OPTENT3(0, "verbose",   OPT_FLAG,    NULL,       &cmdlineP->verbose,  0);
+    OPTENT3(0, "verbose",   OPT_FLAG,    NULL,       &cmdlineP->verbose,   0);
     OPTENT3(0, "filter",    OPT_STRING,  &filterOpt, &filterSpec,          0);
     OPTENT3(0, "window",    OPT_STRING,  &window,    &windowSpec,          0);
-    OPTENT3(0, "nomix",     OPT_FLAG,    NULL,       &cmdlineP->nomix,    0);
-    OPTENT3(0, "linear",    OPT_FLAG,    NULL,       &cmdlineP->linear,   0);
+    OPTENT3(0, "nomix",     OPT_FLAG,    NULL,       &cmdlineP->nomix,     0);
+    OPTENT3(0, "linear",    OPT_FLAG,    NULL,       &cmdlineP->linear,    0);
   
     /* Set the defaults. -1 = unspecified */
 
diff --git a/lib/Makefile b/lib/Makefile
index 67fac5b2..5632b8f0 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -67,8 +67,6 @@ BINARIES =
 OMIT_LIBRARY_RULE = 1
 include $(SRCDIR)/common.mk
 
-INCLUDES = -I$(SRCDIR)/$(SUBDIR) -I. -Iimportinc
-
 # The following must go after common.mk because $(LIBNETPBM) may 
 # contain a reference to $(NETPBM_MAJOR_RELEASE).
 .PHONY: libnetpbm
diff --git a/lib/libpm.c b/lib/libpm.c
index 1089da3c..099d4bf7 100644
--- a/lib/libpm.c
+++ b/lib/libpm.c
@@ -10,6 +10,9 @@
 
 #define _XOPEN_SOURCE 500    /* Make sure ftello, fseeko are defined */
 
+#include "netpbm/pm_config.h"
+
+#include <assert.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <stdarg.h>
@@ -18,15 +21,19 @@
 #include <setjmp.h>
 #include <time.h>
 #include <limits.h>
+#if HAVE_FORK
+#include <sys/wait.h>
+#endif
+#include <sys/types.h>
 
-#include "pm_c_util.h"
-#include "mallocvar.h"
-#include "version.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/version.h"
+#include "netpbm/nstring.h"
+#include "netpbm/shhopt.h"
 #include "compile.h"
-#include "nstring.h"
-#include "shhopt.h"
 
-#include "pm.h"
+#include "netpbm/pm.h"
 
 /* The following are set by pm_init(), then used by subsequent calls to other
    pm_xxx() functions.
@@ -95,6 +102,86 @@ pm_longjmp(void) {
 
 
 void
+pm_fork(int *         const iAmParentP,
+        pid_t *       const childPidP,
+        const char ** const errorP) {
+/*----------------------------------------------------------------------------
+   Same as POSIX fork, except with a nicer interface and works
+   (fails cleanly) on systems that don't have POSIX fork().
+-----------------------------------------------------------------------------*/
+#if HAVE_FORK
+    int rc;
+
+    rc = fork();
+
+    if (rc < 0) {
+        asprintfN(errorP, "Failed to fork a process.  errno=%d (%s)",
+                  errno, strerror(errno));
+    } else {
+        *errorP = NULL;
+
+        if (rc == 0) {
+            *iAmParentP = FALSE;
+        } else {
+            *iAmParentP = TRUE;
+            *childPidP = rc;
+        }
+    }
+#else
+    asprintfN(errorP, "Cannot fork a process, because this system does "
+              "not have POSIX fork()");
+#endif
+}
+
+
+
+void
+pm_waitpid(pid_t         const pid,
+           int *         const statusP,
+           int           const options,
+           pid_t *       const exitedPidP,
+           const char ** const errorP) {
+
+#if HAVE_FORK
+    pid_t rc;
+    rc = waitpid(pid, statusP, options);
+    if (rc == (pid_t)-1) {
+        asprintfN(errorP, "Failed to wait for process exit.  "
+                  "waitpid() errno = %d (%s)",
+                  errno, strerror(errno));
+    } else {
+        *exitedPidP = rc;
+        *errorP = NULL;
+    }
+#else
+    pm_error("INTERNAL ERROR: Attempt to wait for a process we created on "
+             "a system on which we can't create processes");
+#endif
+}
+
+
+
+void
+pm_waitpidSimple(pid_t const pid) {
+
+    int status;
+    pid_t exitedPid;
+    const char * error;
+
+    pm_waitpid(pid, &status, 0, &exitedPid, &error);
+
+    if (error) {
+        pm_errormsg("%s", error);
+        strfree(error);
+        pm_longjmp();
+    } else {
+        assert(exitedPid != 0);
+    }
+}
+
+
+
+void
 pm_setusererrormsgfn(pm_usererrormsgfn * fn) {
 
     userErrorMsgFn = fn;
diff --git a/lib/libsystem_dummy.c b/lib/libsystem_dummy.c
index 2787ce67..c89b4d5d 100644
--- a/lib/libsystem_dummy.c
+++ b/lib/libsystem_dummy.c
@@ -4,7 +4,7 @@
   This is a dummy version of libsystem.c, for use on systems that don't
   have the kind of process control that libsystem.c needs.
 
-  With this module, program will build cleanly, but if a program actually
+  With this module, programs will build cleanly, but if a program actually
   calls pm_system(), it will die with an error message saying that
   the facility is not available.
 =============================================================================*/
diff --git a/lib/pm.h b/lib/pm.h
index 4ba20aa5..55ea4f29 100644
--- a/lib/pm.h
+++ b/lib/pm.h
@@ -164,6 +164,21 @@ pm_setjmpbufsave(jmp_buf *  const jmpbufP,
 void
 pm_longjmp(void);
 
+void
+pm_fork(int *         const iAmParentP,
+        pid_t *       const childPidP,
+        const char ** const errorP);
+
+void
+pm_waitpid(pid_t         const pid,
+           int *         const statusP,
+           int           const options,
+           pid_t *       const exitedPidP,
+           const char ** const errorP);
+
+
+void
+pm_waitpidSimple(pid_t const pid);
 
 typedef void pm_usermessagefn(const char * msg);
 
diff --git a/lib/util/Makefile b/lib/util/Makefile
index 8b3fa1c1..f5f0e04c 100644
--- a/lib/util/Makefile
+++ b/lib/util/Makefile
@@ -17,8 +17,6 @@ all: $(UTILOBJECTS)
 
 include $(SRCDIR)/common.mk
 
-INCLUDES = -I$(SRCDIR)/$(SUBDIR) -I. -Iimportinc
-
 $(UTILOBJECTS):%.o:%.c importinc
 	$(CC) -c $(INCLUDES) -DNDEBUG $(CPPFLAGS) $(CFLAGS) $(CFLAGS_SHLIB) \
 	  $(CFLAGS_PERSONAL) $(CADD) -o $@ $<
diff --git a/other/ppmsvgalib.c b/other/ppmsvgalib.c
index 5bcabdc1..8598cef2 100644
--- a/other/ppmsvgalib.c
+++ b/other/ppmsvgalib.c
@@ -253,10 +253,6 @@ main(int argc, char *argv[]) {
 
     ppm_init(&argc, argv);
 
-    rc = vga_init();         /* Initialize. */
-    if (rc < 0)
-        pm_error("Svgalib unable to allocate a virtual console.");
-
     parseCommandLine(argc, argv, &cmdline);
 
     ifP = pm_openr(cmdline.inputFilespec);
@@ -269,6 +265,14 @@ main(int argc, char *argv[]) {
                   &checkResult);
     }
 
+    /* We wait to initialize Svgalib to prevent it from interfering with
+       error messages the code above might issue, and leaving the console
+       in an undesirable state if the above code aborts the program.
+    */
+    rc = vga_init();         /* Initialize. */
+    if (rc < 0)
+        pm_error("Svgalib unable to allocate a virtual console.");
+
     if (vga_hasmode(cmdline.mode))
         display(ifP, cols, rows, maxval, format, 
                 cmdline.mode, cmdline.verbose);
diff --git a/urt/rle_addhist.c b/urt/rle_addhist.c
index 04e26206..0df9642d 100644
--- a/urt/rle_addhist.c
+++ b/urt/rle_addhist.c
@@ -37,7 +37,7 @@
 #include <sys/time.h>
 #endif
 
-#include "mallocvar.h"
+#include "netpbm/mallocvar.h"
 
 /*****************************************************************
  * TAG( rle_addhist )
diff --git a/urt/rle_getrow.c b/urt/rle_getrow.c
index fadf5441..bd7d1c8e 100644
--- a/urt/rle_getrow.c
+++ b/urt/rle_getrow.c
@@ -33,8 +33,8 @@
 #include <string.h>
 #include <stdio.h>
 
-#include "pm.h"
-#include "mallocvar.h"
+#include "netpbm/pm.h"
+#include "netpbm/mallocvar.h"
 
 #include "rle.h"
 #include "rle_code.h"
diff --git a/urt/rle_open_f.c b/urt/rle_open_f.c
index 07dbea01..7234afe9 100644
--- a/urt/rle_open_f.c
+++ b/urt/rle_open_f.c
@@ -15,8 +15,8 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-#include "pm_c_util.h"
-#include "nstring.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/nstring.h"
 #include "rle.h"
 
 
diff --git a/urt/rle_putcom.c b/urt/rle_putcom.c
index b1215661..ab2eb208 100644
--- a/urt/rle_putcom.c
+++ b/urt/rle_putcom.c
@@ -27,8 +27,8 @@
 
 #include <stdio.h>
 
-#include "mallocvar.h"
-#include "pm.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/pm.h"
 #include "rle.h"
 
 /*****************************************************************
diff --git a/urt/scanargs.c b/urt/scanargs.c
index b91f3e37..e1f15047 100644
--- a/urt/scanargs.c
+++ b/urt/scanargs.c
@@ -49,8 +49,8 @@
 #include <stdarg.h>
 #endif
 
-#include "pm_c_util.h"
-#include "nstring.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/nstring.h"
 
 /* 
  * An explicit assumption is made in this code that all pointers look
diff --git a/version.mk b/version.mk
index f1196705..d01fdb60 100644
--- a/version.mk
+++ b/version.mk
@@ -1,4 +1,4 @@
 NETPBM_MAJOR_RELEASE = 10
-NETPBM_MINOR_RELEASE = 47
-NETPBM_POINT_RELEASE = 4
+NETPBM_MINOR_RELEASE = 48
+NETPBM_POINT_RELEASE = 0