[libpng17] Port recent zlib windowBits handling from libpng-1.6.3beta06
diff --git a/ANNOUNCE b/ANNOUNCE
index 3f02f3e..3e24450 100644
--- a/ANNOUNCE
+++ b/ANNOUNCE
@@ -285,6 +285,23 @@
Added a call to png_set_packing()
Initialize dimension values so if sscanf fails at least we have known
invalid values.
+ Calculate our own zlib windowBits when decoding rather than trusting the
+ CMF bytes in the PNG datastream.
+ Added an option to force maximum window size for inflating, which was
+ the behavior of libpng15 and earlier.
+ Added png-fix-itxt and png-fix-too-far-back to the built programs and
+ removed warnings from the source code and timepng that are revealed as
+ a result.
+ Detect wrong libpng versions linked to png-fix-too-far-back, which currently
+ only works with libpng versions that can be made to reliably fail when
+ the deflate data contains an out-of-window reference. This means only
+ 1.6 and later.
+ Attempt to detect configuration issues with png-fix-too-far-back, which
+ requires both the correct libpng and the correct zlib to function
+ correctly.
+ Check ZLIB_VERNUM for mismatches, enclose #error in quotes
+ Added information in the documentation about problems with and fixes for
+ the bad CRC and bad iTXt chunk situations.
Send comments/corrections/commendations to png-mng-implement at lists.sf.net
(subscription required; visit
diff --git a/CHANGES b/CHANGES
index 80cc136..daac046 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4573,6 +4573,23 @@
Added a call to png_set_packing()
Initialize dimension values so if sscanf fails at least we have known
invalid values.
+ Calculate our own zlib windowBits when decoding rather than trusting the
+ CMF bytes in the PNG datastream.
+ Added an option to force maximum window size for inflating, which was
+ the behavior of libpng15 and earlier.
+ Added png-fix-itxt and png-fix-too-far-back to the built programs and
+ removed warnings from the source code and timepng that are revealed as
+ a result.
+ Detect wrong libpng versions linked to png-fix-too-far-back, which currently
+ only works with libpng versions that can be made to reliably fail when
+ the deflate data contains an out-of-window reference. This means only
+ 1.6 and later.
+ Attempt to detect configuration issues with png-fix-too-far-back, which
+ requires both the correct libpng and the correct zlib to function
+ correctly.
+ Check ZLIB_VERNUM for mismatches, enclose #error in quotes
+ Added information in the documentation about problems with and fixes for
+ the bad CRC and bad iTXt chunk situations.
Send comments/corrections/commendations to png-mng-implement at lists.sf.net
(subscription required; visit
diff --git a/LICENSE b/LICENSE
index 5969dac..9514e7f 100644
--- a/LICENSE
+++ b/LICENSE
@@ -10,7 +10,7 @@
This code is released under the libpng license.
-libpng versions 1.2.6, August 15, 2004, through 1.7.0beta13, April 30, 2013, are
+libpng versions 1.2.6, August 15, 2004, through 1.7.0beta13, May 10, 2013, are
Copyright (c) 2004, 2006-2013 Glenn Randers-Pehrson, and are
distributed according to the same disclaimer and license as libpng-1.2.5
with the following individual added to the list of Contributing Authors
@@ -108,4 +108,4 @@
Glenn Randers-Pehrson
glennrp at users.sourceforge.net
-April 30, 2013
+May 10, 2013
diff --git a/Makefile.am b/Makefile.am
index 006cb93..c51ee4d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,6 +9,9 @@
# test programs - run on make check, make distcheck
check_PROGRAMS= pngtest pngunknown pngstest pngvalid
+# Utilities - installed
+bin_PROGRAMS= png-fix-too-far-back png-fix-itxt
+
pngtest_SOURCES = pngtest.c
pngtest_LDADD = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@.la
@@ -21,6 +24,11 @@
pngunknown_SOURCES = contrib/libtests/pngunknown.c
pngunknown_LDADD = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@.la
+png_fix_too_far_back_SOURCES = contrib/tools/png-fix-too-far-back.c
+png_fix_too_far_back_LDADD = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@.la
+
+png_fix_itxt_SOURCES = contrib/tools/png-fix-itxt.c
+
# Generally these are single line shell scripts to run a test with a particular
# set of parameters:
TESTS =\
@@ -182,14 +190,15 @@
$(srcdir)/scripts/pnglibconf.h.prebuilt:
@echo "Attempting to build $@" >&2
@echo "This is a machine generated file, but if you want to make" >&2
- @echo "a new one simply make 'scripts/pnglibconf.out' and copy that" >&2
+ @echo "a new one simply make 'scripts/pnglibconf.out', copy that" >&2
+ @echo "AND set PNG_ZLIB_VERNUM to 0 (you MUST do this)" >&2
@exit 1
# The following is necessary to ensure that the local pnglibconf.h is used, not
# an installed one (this can happen immediately after on a clean system if
# 'make test' is the first thing the user does.)
-contrib/libtests/pngstest.o contrib/libtests/pngvalid.o pngtest.o: pnglibconf.h
-contrib/libtests/pngunknown.o: pnglibconf.h
+pngstest.o pngvalid.o pngtest.o pngunknown.o timepng.o: pnglibconf.h
+png-fix-too-far-back.o png-fix-itxt.o: pnglibconf.h
# We must use -DPNG_NO_USE_READ_MACROS here even when the library may actually
# be built with PNG_USE_READ_MACROS; this prevents the read macros from
diff --git a/README b/README
index c5f2133..19d755a 100644
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-README for libpng version 1.7.0beta13 - April 30, 2013 (shared library 17.0)
+README for libpng version 1.7.0beta13 - May 10, 2013 (shared library 17.0)
See the note about version numbers near the top of png.h
See INSTALL for instructions on how to install libpng.
diff --git a/contrib/tools/png-fix-itxt.c b/contrib/tools/png-fix-itxt.c
new file mode 100644
index 0000000..7cbd996
--- /dev/null
+++ b/contrib/tools/png-fix-itxt.c
@@ -0,0 +1,153 @@
+
+/* png-fix-itxt version 1.0.0
+ *
+ * Copyright 2013 Glenn Randers-Pehrson
+ * Last changed in libpng 1.6.3 [(PENDING RELEASE)]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Usage:
+ *
+ * png-fix-itxt.exe < bad.png > good.png
+ *
+ * Fixes a PNG file written with libpng-1.6.0 or 1.6.1 that has one or more
+ * uncompressed iTXt chunks. Assumes that the actual length is greater
+ * than or equal to the value in the length byte, and that the CRC is
+ * correct for the actual length. This program hunts for the CRC and
+ * adjusts the length byte accordingly. It is not an error to process a
+ * PNG file that has no iTXt chunks or one that has valid iTXt chunks;
+ * such files will simply be copied.
+ *
+ * Requires zlib (for crc32 and Z_NULL); build with
+ *
+ * gcc -O -o png-fix-itxt png-fix-itxt.c -lz
+ *
+ * If you need to handle iTXt chunks larger than 500000 kbytes you must
+ * rebuild png-fix-itxt with a larger values of MAX_LENGTH (or a smaller value
+ * if you know you will never encounter such huge iTXt chunks).
+ */
+
+#include <stdio.h>
+#include <zlib.h>
+
+#define MAX_LENGTH 500000
+
+#define GETBREAK ((unsigned char)(inchar=getchar())); if (inchar == EOF) break
+
+int
+main(void)
+{
+ unsigned int i;
+ unsigned char buf[MAX_LENGTH];
+ unsigned long crc;
+ unsigned char c;
+ int inchar;
+
+/* Skip 8-byte signature */
+ for (i=8; i; i--)
+ {
+ c=GETBREAK;
+ putchar(c);
+ }
+
+if (inchar != EOF)
+for (;;)
+ {
+ /* Read the length */
+ unsigned long length; /* must be 32 bits! */
+ c=GETBREAK; buf[0] = c; length = c; length <<= 8;
+ c=GETBREAK; buf[1] = c; length += c; length <<= 8;
+ c=GETBREAK; buf[2] = c; length += c; length <<= 8;
+ c=GETBREAK; buf[3] = c; length += c;
+
+ /* Read the chunkname */
+ c=GETBREAK; buf[4] = c;
+ c=GETBREAK; buf[5] = c;
+ c=GETBREAK; buf[6] = c;
+ c=GETBREAK; buf[7] = c;
+
+
+ /* The iTXt chunk type expressed as integers is (105, 84, 88, 116) */
+ if (buf[4] == 105 && buf[5] == 84 && buf[6] == 88 && buf[7] == 116)
+ {
+ if (length >= MAX_LENGTH-12)
+ break; /* To do: handle this more gracefully */
+
+ /* Initialize the CRC */
+ crc = crc32(0, Z_NULL, 0);
+
+ /* Copy the data bytes */
+ for (i=8; i < length + 12; i++)
+ {
+ c=GETBREAK; buf[i] = c;
+ }
+
+ /* Calculate the CRC */
+ crc = crc32(crc, buf+4, (uInt)length+4);
+
+ for (;;)
+ {
+ /* Check the CRC */
+ if (((crc >> 24) & 0xff) == buf[length+8] &&
+ ((crc >> 16) & 0xff) == buf[length+9] &&
+ ((crc >> 8) & 0xff) == buf[length+10] &&
+ ((crc ) & 0xff) == buf[length+11])
+ break;
+
+ length++;
+
+ if (length >= MAX_LENGTH-12)
+ break;
+
+ c=GETBREAK;
+ buf[length+11]=c;
+
+ /* Update the CRC */
+ crc = crc32(crc, buf+7+length, 1);
+ }
+
+ /* Update length bytes */
+ buf[0] = (length << 24) & 0xff;
+ buf[1] = (length << 16) & 0xff;
+ buf[2] = (length << 8) & 0xff;
+ buf[3] = (length ) & 0xff;
+
+ /* Write the fixed iTXt chunk (length, name, data, crc) */
+ for (i=0; i<length+12; i++)
+ putchar(buf[i]);
+ }
+
+ else
+ {
+ /* Copy bytes that were already read (length and chunk name) */
+ for (i=0; i<8; i++)
+ putchar(buf[i]);
+
+ /* Copy data bytes and CRC */
+ for (i=8; i< length+12; i++)
+ {
+ c=GETBREAK;
+ putchar(c);
+ }
+
+ if (inchar == EOF)
+ {
+ break;
+ }
+
+ /* The IEND chunk type expressed as integers is (73, 69, 78, 68) */
+ if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68)
+ break;
+ }
+
+ if (inchar == EOF)
+ break;
+
+ if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68)
+ break;
+ }
+
+ return 0;
+}
diff --git a/contrib/tools/png-fix-too-far-back.c b/contrib/tools/png-fix-too-far-back.c
new file mode 100644
index 0000000..829253a
--- /dev/null
+++ b/contrib/tools/png-fix-too-far-back.c
@@ -0,0 +1,1263 @@
+/* png-fix-too-far-back.c
+ *
+ * Copyright (c) 2013 John Cunningham Bowler
+ *
+ * Last changed in libpng 1.6.3 [(PENDING RELEASE)]
+ *
+ * This code is released under the libpng license.
+ * For conditions of distribution and use, see the disclaimer
+ * and license in png.h
+ *
+ * Tool to check and fix the zlib inflate 'too far back' problem, see the usage
+ * message for more information.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#define PROGRAM_NAME "png-fix-too-far-back"
+
+/* Define the following to use this program against your installed libpng,
+ * rather than the one being built here:
+ */
+#ifdef PNG_FREESTANDING_TESTS
+# include <png.h>
+#else
+# include "../../png.h"
+#endif
+
+#if PNG_LIBPNG_VER < 10603 /* 1.6.3 */
+# error "png-fix-too-far-back will not work with libpng prior to 1.6.3"
+#endif
+
+#ifdef PNG_READ_SUPPORTED
+#include <zlib.h>
+
+#ifndef PNG_MAXIMUM_INFLATE_WINDOW
+# error "png-fix-too-far-back not supported in this libpng version"
+#endif
+
+#if PNG_ZLIB_VERNUM >= 0x1240
+
+/* Copied from pngpriv.h */
+#ifdef __cplusplus
+# define png_voidcast(type, value) static_cast<type>(value)
+# define png_constcast(type, value) const_cast<type>(value)
+# define png_aligncast(type, value) \
+ static_cast<type>(static_cast<void*>(value))
+# define png_aligncastconst(type, value) \
+ static_cast<type>(static_cast<const void*>(value))
+#else
+# define png_voidcast(type, value) (value)
+# define png_constcast(type, value) ((type)(value))
+# define png_aligncast(type, value) ((void*)(value))
+# define png_aligncastconst(type, value) ((const void*)(value))
+#endif /* __cplusplus */
+
+static int idat_error = 0;
+static int verbose = 0;
+static int errors = 0;
+static int warnings = 0;
+#ifdef PNG_MAXIMUM_INFLATE_WINDOW
+ static int set_option = 0;
+#endif
+static const char *name = "stdin";
+static uLong crc_IDAT_head; /* CRC32 of "IDAT" */
+static uLong crc_IEND;
+static z_stream z_idat;
+
+/* Control structure for the temporary file */
+typedef struct
+{
+ size_t image_size;
+ off_t file_size;
+ fpos_t header_pos;
+ fpos_t crc_pos;
+ uLong crc_tail; /* CRC of bytes after header */
+ png_uint_32 len_tail; /* Count thereof */
+ png_byte header[2];
+
+ /* Image info */
+ png_uint_32 width;
+ png_uint_32 height;
+ png_byte bit_depth;
+ png_byte color_type;
+ png_byte compression_method;
+ png_byte filter_method;
+ png_byte interlace_method;
+} IDAT_info;
+
+static png_uint_32
+mult(png_uint_32 n, png_uint_32 m)
+{
+ if ((n + (m-1)) / m > 0xffffffff/m)
+ {
+ fprintf(stderr, "%s: overflow (%lu, %u)\n", name, (unsigned long)n, m);
+ exit(2);
+ }
+
+ return n * m;
+}
+
+static size_t
+image_size(const IDAT_info *info)
+{
+ unsigned int pd = info->bit_depth;
+ size_t cb;
+
+ switch (info->color_type)
+ {
+ case 0: case 3:
+ break;
+
+ case 2: /* rgb */
+ pd *= 3;
+ break;
+
+ case 4: /* ga */
+ pd *= 2;
+ break;
+
+ case 6: /* rgba */
+ pd *= 4;
+ break;
+
+ default:
+ fprintf(stderr, "%s: invalid color type (%d)\n", name,
+ info->color_type);
+ exit(2);
+ }
+
+ switch (info->interlace_method)
+ {
+ case PNG_INTERLACE_ADAM7:
+ /* Interlacing makes the image larger because of the replication of
+ * both the filter byte and the padding to a byte boundary.
+ */
+ {
+ int pass;
+
+ for (cb=0, pass=0; pass<=6; ++pass)
+ {
+ png_uint_32 pw = PNG_PASS_COLS(info->width, pass);
+
+ if (pw > 0)
+ cb += mult(((mult(pd, pw)+7) >> 3)+1,
+ PNG_PASS_ROWS(info->height, pass));
+ }
+ }
+ break;
+
+ case PNG_INTERLACE_NONE:
+ cb = mult(info->height, 1+((mult(info->width, pd) + 7) >> 3));
+ break;
+
+ default:
+ fprintf(stderr, "%s: invalid interlace type %d\n", name,
+ info->interlace_method);
+ exit(2);
+ }
+
+ return cb;
+}
+
+static int
+image_windowBits(const IDAT_info *info)
+{
+ size_t cb = image_size(info);
+
+ if (cb > 16384) return 15;
+ if (cb > 8192) return 14;
+ if (cb > 4096) return 13;
+ if (cb > 2048) return 12;
+ if (cb > 1024) return 11;
+ if (cb > 512) return 10;
+ if (cb > 256) return 9;
+ return 8;
+}
+
+static void
+error_handler(png_structp png_ptr, png_const_charp message)
+{
+ if (strcmp(message, "IDAT: invalid distance too far back") == 0)
+ idat_error = 1;
+
+ else if (errors || verbose)
+ fprintf(stderr, "%s: %s\n", name, message);
+
+ png_longjmp(png_ptr, 1);
+}
+
+static void
+warning_handler(png_structp png_ptr, png_const_charp message)
+{
+ if (warnings || verbose)
+ fprintf(stderr, "%s: %s\n", name, message);
+
+ (void)png_ptr;
+}
+
+static int
+read_png(FILE *fp)
+{
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,
+ error_handler, warning_handler);
+ png_infop info_ptr = NULL;
+ png_bytep row = NULL, display = NULL;
+
+ if (png_ptr == NULL)
+ return 0;
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ if (row != NULL) free(row);
+ if (display != NULL) free(display);
+ return 0;
+ }
+
+# ifdef PNG_MAXIMUM_INFLATE_WINDOW
+ png_set_option(png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, set_option != 0);
+# endif
+
+ png_init_io(png_ptr, fp);
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL)
+ png_error(png_ptr, "OOM allocating info structure");
+
+ png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0);
+
+ png_read_info(png_ptr, info_ptr);
+
+ /* Limit the decompression buffer size to 1 - this ensures that overlong
+ * length codes are always detected.
+ */
+ png_set_compression_buffer_size(png_ptr, 1);
+
+ {
+ png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+ row = png_voidcast(png_byte*, malloc(rowbytes));
+ display = png_voidcast(png_byte*, malloc(rowbytes));
+
+ if (row == NULL || display == NULL)
+ png_error(png_ptr, "OOM allocating row buffers");
+
+ {
+ png_uint_32 height = png_get_image_height(png_ptr, info_ptr);
+ int passes = png_set_interlace_handling(png_ptr);
+ int pass;
+
+ png_start_read_image(png_ptr);
+
+ for (pass = 0; pass < passes; ++pass)
+ {
+ png_uint_32 y = height;
+
+ /* NOTE: this trashes the row each time; interlace handling won't
+ * work, but this avoids memory thrashing for speed testing.
+ */
+ while (y-- > 0)
+ png_read_row(png_ptr, row, display);
+ }
+ }
+ }
+
+ /* Make sure to read to the end of the file: */
+ png_read_end(png_ptr, info_ptr);
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ free(row);
+ free(display);
+ return 1;
+}
+
+/* Chunk tags (copied from pngpriv.h) */
+#define PNG_32b(b,s) ((png_uint_32)(b) << (s))
+#define PNG_CHUNK(b1,b2,b3,b4) \
+ (PNG_32b(b1,24) | PNG_32b(b2,16) | PNG_32b(b3,8) | PNG_32b(b4,0))
+
+#if PNG_LIBPNG_VER < 10700 /* These were moved to png.h in 1.7.0 */
+#define png_IHDR PNG_CHUNK( 73, 72, 68, 82)
+#define png_IDAT PNG_CHUNK( 73, 68, 65, 84)
+#define png_IEND PNG_CHUNK( 73, 69, 78, 68)
+#define png_PLTE PNG_CHUNK( 80, 76, 84, 69)
+#define png_bKGD PNG_CHUNK( 98, 75, 71, 68)
+#define png_cHRM PNG_CHUNK( 99, 72, 82, 77)
+#define png_gAMA PNG_CHUNK(103, 65, 77, 65)
+#define png_hIST PNG_CHUNK(104, 73, 83, 84)
+#define png_iCCP PNG_CHUNK(105, 67, 67, 80)
+#define png_iTXt PNG_CHUNK(105, 84, 88, 116)
+#define png_oFFs PNG_CHUNK(111, 70, 70, 115)
+#define png_pCAL PNG_CHUNK(112, 67, 65, 76)
+#define png_sCAL PNG_CHUNK(115, 67, 65, 76)
+#define png_pHYs PNG_CHUNK(112, 72, 89, 115)
+#define png_sBIT PNG_CHUNK(115, 66, 73, 84)
+#define png_sPLT PNG_CHUNK(115, 80, 76, 84)
+#define png_sRGB PNG_CHUNK(115, 82, 71, 66)
+#define png_sTER PNG_CHUNK(115, 84, 69, 82)
+#define png_tEXt PNG_CHUNK(116, 69, 88, 116)
+#define png_tIME PNG_CHUNK(116, 73, 77, 69)
+#define png_tRNS PNG_CHUNK(116, 82, 78, 83)
+#define png_zTXt PNG_CHUNK(122, 84, 88, 116)
+#endif
+
+static void
+rx(FILE *fp, png_bytep buf, off_t cb)
+{
+ if (fread(buf,cb,1,fp) != 1) {
+ fprintf(stderr, "%s: failed to read %lu bytes\n", name,
+ (unsigned long)cb);
+ exit(2);
+ }
+}
+
+static png_uint_32
+r32(FILE *fp)
+{
+ png_byte buf[4];
+ rx(fp, buf, 4);
+ return ((((((png_uint_32)buf[0] << 8)+buf[1]) << 8)+buf[2]) << 8) + buf[3];
+}
+
+static void
+wx(FILE *fp, png_const_bytep buf, off_t cb)
+{
+ if (fwrite(buf,cb,1,fp) != 1) {
+ fprintf(stderr, "%s: failed to write %lu bytes\n", name,
+ (unsigned long)cb);
+ exit(3);
+ }
+}
+
+static void
+w32(FILE *fp, png_uint_32 val)
+{
+ png_byte buf[4];
+ buf[0] = (png_byte)(val >> 24);
+ buf[1] = (png_byte)(val >> 16);
+ buf[2] = (png_byte)(val >> 8);
+ buf[3] = (png_byte)(val);
+ wx(fp, buf, 4);
+}
+
+static void
+wcrc(FILE *fp, uLong crc)
+{
+ /* Safe cast because a CRC is 32 bits */
+ w32(fp, (png_uint_32)crc);
+}
+
+static void
+copy(FILE *fp, FILE *fpIn, off_t cb)
+{
+ png_byte buffer[1024];
+
+ while (cb >= 1024)
+ {
+ rx(fpIn, buffer, 1024);
+ wx(fp, buffer, 1024);
+ cb -= 1024;
+ }
+
+ if (cb > 0)
+ {
+ rx(fpIn, buffer, cb);
+ wx(fp, buffer, cb);
+ }
+}
+
+static void
+skip_bytes(FILE *fpIn, png_uint_32 cb)
+{
+ png_byte buffer[1024];
+
+ while (cb >= 1024)
+ {
+ rx(fpIn, buffer, 1024);
+ cb -= 1024;
+ }
+
+ if (cb > 0)
+ rx(fpIn, buffer, cb);
+}
+
+static void
+safe_getpos(FILE *fp, fpos_t *pos)
+{
+ if (fgetpos(fp, pos))
+ {
+ perror("tmpfile");
+ fprintf(stderr, "%s: tmpfile fgetpos failed\n", name);
+ exit(3);
+ }
+}
+
+static void
+safe_setpos(FILE *fp, fpos_t *pos)
+{
+ if (fflush(fp))
+ {
+ perror("tmpfile");
+ fprintf(stderr, "%s: tmpfile fflush failed\n", name);
+ exit(3);
+ }
+
+ if (fsetpos(fp, pos))
+ {
+ perror("tmpfile");
+ fprintf(stderr, "%s: tmpfile fsetpos failed\n", name);
+ exit(3);
+ }
+}
+
+static void
+idat_update(FILE *fp, IDAT_info *info)
+{
+ uLong crc;
+
+ safe_setpos(fp, &info->header_pos);
+ wx(fp, info->header, 2);
+
+ crc = crc32(crc_IDAT_head, info->header, 2);
+ crc = crc32_combine(crc, info->crc_tail, info->len_tail);
+
+ safe_setpos(fp, &info->crc_pos);
+ wcrc(fp, crc);
+}
+
+static void
+set_bits(const char *file, FILE *fp, IDAT_info *info, int bits)
+{
+ int byte1 = (info->header[0] & 0xf) + ((bits-8) << 4);
+ int byte2 = info->header[1] & 0xe0;
+
+ /* The checksum calculation: */
+ byte2 += 0x1f - ((byte1 << 8) + byte2) % 0x1f;
+
+ info->header[0] = (png_byte)byte1;
+ info->header[1] = (png_byte)byte2;
+
+ if (verbose)
+ fprintf(stderr, "%s: trying windowBits %d (Z_CMF = 0x%x)\n", file, bits,
+ byte1);
+
+ idat_update(fp, info);
+}
+
+static void
+ptagchar(png_uint_32 ch)
+{
+ ch &= 0xff;
+ if (isprint(ch))
+ putc(ch, stderr);
+
+ else
+ fprintf(stderr, "[%02x]", ch);
+}
+
+static void
+ptag(png_uint_32 tag)
+{
+ if (tag != 0)
+ {
+ ptag(tag >> 8);
+ ptagchar(tag);
+ }
+}
+
+static int
+fix_one(FILE *fp, FILE *fpIn, IDAT_info *info, png_uint_32 max_IDAT, int strip)
+{
+ int state = 0;
+ /* 0: at beginning, before first IDAT
+ * 1: read first CMF header byte
+ * 2: read second byte, in first IDAT
+ * 3: after first IDAT
+ * +4: saw deflate stream end.
+ */
+ int truncated_idat = 0; /* Count of spurious IDAT bytes */
+ uLong crc_idat = 0; /* Running CRC of current IDAT */
+ png_uint_32 len_IDAT = 0; /* Length of current IDAT */
+ fpos_t pos_IDAT_length; /* fpos_t of length field in current IDAT */
+
+ /* The signature: */
+ {
+ png_byte buf[8];
+ rx(fpIn, buf, 8);
+ wx(fp, buf, 8);
+ }
+
+ info->file_size = 45; /* signature + IHDR + IEND */
+
+ for (;;) /* Chunk for loop */
+ {
+ png_uint_32 len = r32(fpIn);
+ png_uint_32 tag = r32(fpIn);
+
+ if (tag == png_IHDR)
+ {
+ /* Need width, height, color type, bit depth and interlace for the
+ * file.
+ */
+ info->width = r32(fpIn);
+ info->height = r32(fpIn);
+ rx(fpIn, &info->bit_depth, 1);
+ rx(fpIn, &info->color_type, 1);
+ rx(fpIn, &info->compression_method, 1);
+ rx(fpIn, &info->filter_method, 1);
+ rx(fpIn, &info->interlace_method, 1);
+
+ /* And write the information. */
+ w32(fp, len);
+ w32(fp, tag);
+ w32(fp, info->width);
+ w32(fp, info->height);
+ wx(fp, &info->bit_depth, 1);
+ wx(fp, &info->color_type, 1);
+ wx(fp, &info->compression_method, 1);
+ wx(fp, &info->filter_method, 1);
+ wx(fp, &info->interlace_method, 1);
+
+ /* Copy the CRC */
+ copy(fp, fpIn, 4);
+ }
+
+ else if (tag == png_IEND)
+ {
+ /* Ok, write an IEND chunk and finish. */
+ w32(fp, 0);
+ w32(fp, png_IEND);
+ wcrc(fp, crc_IEND);
+ break;
+ }
+
+ else if (tag == png_IDAT && len > 0)
+ {
+ /* Write the chunk header now if it hasn't been written yet */
+ if (len_IDAT == 0)
+ {
+ /* The length is set at the end: */
+ safe_getpos(fp, &pos_IDAT_length);
+ w32(fp, max_IDAT); /* length, not yet written */
+ w32(fp, png_IDAT);
+
+ if (state == 0) /* Start of first IDAT */
+ {
+ safe_getpos(fp, &info->header_pos);
+ /* This will become info->crc_tail: */
+ crc_idat = crc32(0L, Z_NULL, 0);
+ }
+
+ else
+ crc_idat = crc_IDAT_head;
+ }
+
+ /* Do the zlib 2-byte header, it gets written out but not added
+ * to the CRC (yet):
+ */
+ while (len > 0 && state < 2)
+ {
+ rx(fpIn, info->header + state, 1);
+ wx(fp, info->header + state, 1);
+ ++len_IDAT;
+ --len;
+
+ if (state++ == 1)
+ {
+ /* The zlib stream is used to validate the compressed IDAT
+ * data in the most relaxed way possible.
+ */
+ png_byte bdummy;
+ int ret;
+
+ z_idat.next_in = info->header;
+ z_idat.avail_in = 2;
+ z_idat.next_out = &bdummy; /* Else Z_STREAM_ERROR! */
+ z_idat.avail_out = 0;
+
+ ret = inflate(&z_idat, Z_NO_FLUSH);
+ if (ret != Z_OK || z_idat.avail_in != 0)
+ {
+ fprintf(stderr,
+ "%s: unexpected/invalid inflate result %d \"%s\"\n",
+ name, ret, z_idat.msg);
+ return 1;
+ }
+ }
+ } /* while in zlib header */
+
+ /* Process further bytes in the IDAT chunk */
+ while (len > 0 && state < 4)
+ {
+ png_byte b;
+
+ rx(fpIn, &b, 1);
+ --len;
+
+ /* Do this 1 byte at a time to guarantee
+ * detecting errors (in particular zlib can skip the
+ * 'too-far-back' error if the output buffer is bigger than
+ * the window size.)
+ */
+ z_idat.next_in = &b;
+ z_idat.avail_in = 1;
+
+ do
+ {
+ int ret;
+ png_byte bout;
+
+ z_idat.next_out = &bout;
+ z_idat.avail_out = 1;
+
+ ret = inflate(&z_idat, Z_SYNC_FLUSH);
+
+ if (z_idat.avail_out == 0)
+ ++info->image_size;
+
+ switch (ret)
+ {
+ case Z_OK:
+ /* Just keep going */
+ break;
+
+ case Z_BUF_ERROR:
+ if (z_idat.avail_in > 0)
+ {
+ fprintf(stderr,
+ "%s: unexpected buffer error \"%s\"\n",
+ name, z_idat.msg);
+ return 1;
+ }
+ goto end_loop;
+
+ case Z_STREAM_END:
+ /* End of stream */
+ state |= 4;
+ goto end_loop;
+
+ default:
+ fprintf(stderr, "%s: bad zlib stream %d, \"%s\"\n",
+ name, ret, z_idat.msg);
+ return 1;
+ }
+ } while (z_idat.avail_in > 0 || z_idat.avail_out == 0);
+
+ /* The byte need not be consumed, if, for example, there is a
+ * spurious byte after the end of the zlib data.
+ */
+ end_loop:
+ if (z_idat.avail_in == 0)
+ {
+ /* Write it and update the length information and running
+ * CRC.
+ */
+ wx(fp, &b, 1);
+ crc_idat = crc32(crc_idat, &b, 1);
+ ++len_IDAT;
+ }
+
+ else
+ ++truncated_idat;
+
+ if (len_IDAT >= max_IDAT || state >= 4)
+ {
+ /* Either the IDAT chunk is full or we've seen the end of
+ * the deflate stream, or both. Flush the chunk and handle
+ * the details of the first chunk.
+ */
+ fpos_t save;
+
+ if ((state & 3) < 3) /* First IDAT */
+ {
+ safe_getpos(fp, &info->crc_pos);
+ info->crc_tail = crc_idat;
+ info->len_tail = len_IDAT-2;
+ }
+
+ /* This is not the correct value for the first IDAT! */
+ wcrc(fp, crc_idat);
+ state |= 3;
+
+ /* Update the length if it is not max_IDAT: */
+ if (len_IDAT != max_IDAT)
+ {
+ safe_getpos(fp, &save);
+ safe_setpos(fp, &pos_IDAT_length);
+ w32(fp, len_IDAT);
+ safe_setpos(fp, &save);
+ }
+
+ /* Add this IDAT to the file size: */
+ info->file_size += 12 + len_IDAT;
+ }
+ } /* while len > 0 && state < 4 */
+
+ /* The above loop only exits on 0 bytes left or end of stream. If
+ * the stream ended with bytes left, discard them:
+ */
+ if (len > 0)
+ {
+ truncated_idat += len;
+ /* Skip those bytes and the CRC */
+ skip_bytes(fpIn, len+4);
+ }
+
+ else
+ skip_bytes(fpIn, 4); /* The CRC */
+ } /* IDAT and len > 0 */
+
+ else
+ {
+ int skip = 0;
+
+ if (tag == png_IDAT)
+ skip = 1;
+
+ else if (state == 0)
+ {
+ /* Chunk before IDAT */
+ if (!skip) switch (strip)
+ {
+ case 0: /* Don't strip */
+ break;
+
+ case 1: /* Keep gAMA, sRGB */
+ if (tag == png_gAMA || tag == png_sRGB)
+ break;
+ /* Fall through */
+
+ default: /* Keep only IHDR, PLTE, tRNS */
+ if (tag == png_IHDR || tag == png_PLTE || tag == png_tRNS)
+ break;
+
+ skip = 1;
+ break;
+ }
+ }
+
+ else if (state >= 4)
+ {
+ /* Keep nothing after IDAT if stripping: */
+ skip = strip;
+ }
+
+ else
+ {
+ /* This is either an unterminated deflate stream or a spurious
+ * non-IDAT chunk in the list of IDAT chunks. Both are fatal
+ * errors.
+ */
+ fprintf(stderr, "%s: tag '", name);
+ ptag(tag);
+ fprintf(stderr, "' after unterminated IDAT\n");
+ break;
+ }
+
+ /* Skip or send? */
+ if (skip)
+ {
+ if (tag != png_IDAT && (tag & 0x20000000) == 0)
+ {
+ fprintf(stderr, "%s: unknown critical chunk '", name);
+ ptag(tag);
+ fprintf(stderr, "'\n");
+ return 1;
+ }
+
+ /* Skip this tag */
+ if (fseek(fpIn, len+4, SEEK_CUR))
+ {
+ perror(name);
+ fprintf(stderr, "%s: seek failed\n", name);
+ return 1;
+ }
+ }
+
+ else /* Keep this tag */
+ {
+ w32(fp, len);
+ w32(fp, tag);
+ copy(fp, fpIn, len+4);
+ info->file_size += 12+len;
+ }
+ } /* Not IDAT or len == 0 */
+ } /* Chunk for loop */
+
+ /* Break out of the loop on error or end */
+ if (state >= 4)
+ {
+ if (truncated_idat)
+ fprintf(stderr, "%s: removed %d bytes from end of IDAT\n", name,
+ truncated_idat);
+
+ return 0; /* success */
+ }
+
+ /* This is somewhat generic but it works: */
+ fprintf(stderr, "%s: unterminated/truncated PNG (%d)\n", name, state);
+
+ return 1;
+}
+
+static FILE *fpIn;
+
+static int
+fix_file(FILE *fp, const char *file, png_uint_32 max_IDAT, int inplace,
+ int strip, int optimize, const char *output)
+{
+ IDAT_info info;
+ int imageBits, oldBits, bestBits, lowBits, newBits, ok_read;
+
+ memset(&info, 0, sizeof info);
+
+ name = file;
+ idat_error = 0;
+
+ /* fpIn is closed by the caller if necessary */
+ fpIn = fopen(file, "rb");
+ if (fpIn == NULL)
+ {
+ perror(file);
+ fprintf(stderr, "%s: open failed\n", file);
+ return 1;
+ }
+
+ /* With no arguments just check this file */
+ if (optimize == 0 && strip == 0 && inplace == 0 && output == NULL)
+ return !read_png(fpIn);
+
+ /* Otherwise, maybe, fix it */
+ if (fix_one(fp, fpIn, &info, max_IDAT, strip))
+ return 1;
+
+ /* oldBits may be invalid, imageBits is always OK, newBits always records the
+ * actual window bits of the temporary file (fp).
+ */
+ bestBits = imageBits = image_windowBits(&info);
+ newBits = oldBits = 8+(info.header[0] >> 4);
+ ok_read = 0; /* records a successful read */
+
+ /* Find the optimal (lowest) newBits */
+ if (optimize)
+ for (lowBits=8; lowBits < bestBits;)
+ {
+ /* This will always set 'newBits' to a value lower than 'bestBits' because
+ * 'lowBits' is less than 'bestBits':
+ */
+ newBits = (bestBits + lowBits) >> 1;
+
+ set_bits(file, fp, &info, newBits);
+
+ rewind(fp);
+ idat_error = 0;
+
+ if (!read_png(fp))
+ {
+ /* If idat_error is *not* set this is some other problem */
+ if (!idat_error)
+ return 1;
+
+ /* This is the hypothetical case where the IDAT has too much data *and*
+ * the window size is wrong. In fact this should never happen because
+ * of the way libpng handles a deflate stream that produces extra data.
+ */
+ if (newBits >= imageBits)
+ {
+ fprintf(stderr, "%s: imageBits(%d) too low (%d)\n", file, imageBits,
+ newBits);
+ return 1;
+ }
+
+ if (lowBits <= newBits)
+ lowBits = newBits+1;
+ }
+
+ else
+ {
+ bestBits = newBits;
+ ok_read = 1;
+ }
+ }
+
+ else if (bestBits > oldBits)
+ {
+ /* See if the original value is ok */
+ rewind(fp);
+ idat_error = 0;
+
+ if (read_png(fp))
+ {
+ ok_read = 1;
+ bestBits = oldBits;
+ }
+
+ else if (!idat_error)
+ return 1;
+
+ /* Otherwise there is an IDAT error and no optimization is being done, so
+ * just use imageBits (which is already set in bestBits).
+ */
+ }
+
+ if (newBits != bestBits)
+ {
+ /* Update the header to the required value */
+ newBits = bestBits;
+
+ set_bits(file, fp, &info, newBits);
+ }
+
+ if (!ok_read)
+ {
+ /* bestBits has not been checked */
+ idat_error = 0;
+ rewind(fp);
+ ok_read = read_png(fp);
+
+ if (idat_error)
+ {
+ /* This should never happen */
+ fprintf(stderr, "%s: imageBits(%d) too low [%d]\n", file, imageBits,
+ newBits);
+ return 1;
+ }
+
+ /* This means that the PNG has some other error */
+ if (!ok_read)
+ return 1;
+ }
+
+ /* Have a valid newBits */
+ if (optimize)
+ printf("%2d %2d %2d %s %s %d %s\n", newBits, oldBits, imageBits,
+ newBits < imageBits ? "<" : "=",
+ newBits < oldBits ? "reduce " :
+ (newBits > oldBits ? "INCREASE" : "ok "),
+ newBits - oldBits, name);
+
+# ifdef PNG_MAXIMUM_INFLATE_WINDOW
+ /* Because setting libpng to use the maximum window bits breaks the
+ * read_png test above.
+ */
+ if (set_option)
+ return 0;
+# endif
+
+ if (output != NULL || (inplace && (bestBits != oldBits || strip)))
+ {
+ FILE *fpOut;
+
+ if (output != NULL)
+ fpOut = fopen(output, "wb");
+
+ else
+ {
+ fpOut = freopen(file, "wb", fpIn);
+ fpIn = NULL;
+ }
+
+ if (fpOut == NULL)
+ {
+ perror(output);
+ fprintf(stderr, "%s: %s: open failed\n", file, output);
+ exit(3);
+ }
+
+ rewind(fp);
+ copy(fpOut, fp, info.file_size);
+
+ if (fflush(fpOut) || ferror(fpOut) || fclose(fpOut))
+ {
+ perror(output != NULL ? output : file);
+ fprintf(stderr, "%s: %s: close failed\n", file, output);
+ if (output != NULL)
+ remove(output);
+ exit(3);
+ }
+ }
+
+ return 0;
+}
+
+static void
+usage(const char *prog, int rc)
+{
+ /* ANSI C-90 limits strings to 509 characters, so use a string array: */
+ size_t i;
+ static const char *usage_string[] = {
+" Tests, optimizes and optionally fixes the zlib header in PNG files.\n",
+" Optionally, when fixing, strips ancilliary chunks from the file.\n",
+"\n",
+"OPTIONS\n",
+" OPERATION\n",
+" By default files are just checked for readability.\n",
+" --optimize (-o):\n",
+" Find the smallest deflate window size for the file, also outputs\n",
+" a summary of the result for each file.\n",
+" --strip (-s):\n",
+" Remove chunks except for IHDR, PLTE, IEND, tRNS, gAMA, sRGB. If\n",
+" given twice remove gAMA and sRGB as well.\n",
+" --max=<number>:\n",
+" Use IDAT chunks sized <number>. If not given the the IDAT\n",
+" chunks will be the maximum size permitted; 2^31-1 bytes.\n",
+" MESSAGES\n",
+" By default the program is silent.\n",
+" --errors (-e):\n",
+" Output errors from libpng (except too-far-back).\n",
+" --warnings (-w):\n",
+" Output warnings from libpng.\n",
+" --verbose (-v):\n",
+" Describe program progress (refer to the source).\n",
+" OUTPUT\n",
+" By default nothing is written.\n",
+" --out=<file>:\n",
+" Write the optimized/corrected version of the next PNG to\n",
+" <file>. This overrides the following two options\n",
+" --suffix=<suffix>:\n",
+" Set --out=<name><suffix> for all following files, unless\n",
+" overridden on a per-file basis by explicit --out. If no\n",
+" --suffix= value is given behaves as --inplace.\n",
+" --inplace (-i):\n",
+" Modify the file in place. THIS IS DANGEROUS - please keep a\n",
+" backup of the file because a program interrupt or bug will\n",
+" result in a corrupted file.\n",
+#ifdef PNG_MAXIMUM_INFLATE_WINDOW
+" INTERNAL OPTIONS\n",
+" --test:\n",
+" Test the PNG_MAXIMUM_INFLATE_WINDOW option. Setting this\n",
+" disables output as this would produce a broken file.\n",
+#endif
+"\n",
+"EXIT CODES\n",
+" 0: Success, all files pass the test, any output written ok.\n",
+" 1: At least one file had a read error, all files checked.\n",
+" 2: A file had an unrecoverable error (integer overflow, bad format),\n",
+" the program exited immediately, without processing further files.\n",
+" 3: An IO or out of memory error, or a file could not be opened.h\n",
+"\n",
+"DESCRIPTION\n",
+" " PROGRAM_NAME " checks each PNG file on the command line\n",
+" for errors. By default it is silent and just returns an exit code\n",
+" (as above). Options allow the zlib error:\n",
+"\n",
+" \"invalid distance too far back\"\n",
+"\n",
+" to be fixed during the read and therefore test the file for other\n",
+" errors that may prevent reading.\n",
+"\n",
+" Setting one of the \"OUTPUT\" options causes the possibly modified\n",
+" file to be written to a new file or, with --inplace, to the existing\n",
+" file.\n",
+"\n",
+" IMPORTANT: --inplace will overwrite the original file, if you use it\n",
+" be sure to keep a backup of the original file, this program can fail\n",
+" during the write and has been known to have serious bugs! A failure\n",
+" during write will certainly damage the original file.\n",
+"\n",
+" Notice that some PNG files with the zlib header problem can still be\n",
+" read by libpng under some circumstances. This program will still\n",
+" detect and, if requested, correct the error.\n",
+"\n",
+" The output produced with --optimize is as follows:\n",
+"\n",
+" opt-bits curr-bits image-bits opt-flag opt-type change file\n",
+"\n",
+" opt-bits: The minimum window bits (8-15) that works, if the file\n",
+" is written this is the value that will be stored.\n",
+" curr-bits: The value currently stored in the file.\n",
+" image-bits: The window bits value corresponding to the size of the\n",
+" uncompressed PNG image data. When --optimize is not\n",
+" given but --strip is, this value will be used if lower\n",
+" than the current value.\n",
+" opt-flag: < if the optimized bit value is less than that implied by\n",
+" the PNG image size (opt-bits < image-bits)\n",
+" = if optimization is not possible (opt-bits = image-bits)\n",
+" opt-type: reduce if opts-bits < curr-bits\n",
+" ok if opt-bits = curr-bits (no change required)\n",
+" INCREASE if opt-bits > curr-bits (the file has the bug)\n",
+" change: opt-bits - curr-bits, so negative if optimization is\n",
+" possible, 0 if no change is required, positive if the\n",
+" bug is present.\n",
+" file: The file name.\n",
+};
+
+ fprintf(stderr, "Usage: %s {[options] png-file}\n", prog);
+
+ for (i=0; i < (sizeof usage_string)/(sizeof usage_string[0]); ++i)
+ fputs(usage_string[i], stderr);
+
+ exit(rc);
+}
+
+int
+main(int argc, const char **argv)
+{
+ int err, strip = 0, optimize = 0, inplace = 0, done = 0;
+ png_uint_32 max_IDAT = 0x7fffffff;
+ FILE *fp;
+ const char *outfile = NULL;
+ const char *suffix = NULL;
+ const char *prog = *argv;
+ static const png_byte idat_bytes[4] = { 73, 68, 65, 84 };
+ static const png_byte iend_bytes[4] = { 73, 69, 78, 68 };
+
+ /* Initialize this first, could be stored as a constant: */
+ crc_IEND = crc_IDAT_head = crc32(0L, Z_NULL, 0);
+ crc_IDAT_head = crc32(crc_IDAT_head, idat_bytes, 4);
+ crc_IEND = crc32(crc_IEND, iend_bytes, 4);
+
+ z_idat.next_in = Z_NULL;
+ z_idat.avail_in = 0;
+ z_idat.zalloc = Z_NULL;
+ z_idat.zfree = Z_NULL;
+ z_idat.opaque = Z_NULL;
+
+ err = inflateInit(&z_idat);
+ if (err != Z_OK)
+ {
+ fprintf(stderr, "inflateInit failed %d \"%s\"\n", err, z_idat.msg);
+ inflateEnd(&z_idat);
+ return 3;
+ }
+
+ fp = tmpfile();
+ if (fp == NULL)
+ {
+ perror("tmpfile");
+ fprintf(stderr, "could not open a temporary file\n");
+ return 3;
+ }
+
+ err = 0;
+ while (--argc > 0)
+ {
+ ++argv;
+
+ if (strcmp(*argv, "--inplace") == 0 || strcmp(*argv, "-i") == 0)
+ ++inplace;
+
+ else if (strncmp(*argv, "--max=", 6) == 0)
+ max_IDAT = (png_uint_32)atol(6+*argv);
+
+ else if (strcmp(*argv, "--optimize") == 0 || strcmp(*argv, "-o") == 0)
+ ++optimize;
+
+ else if (strncmp(*argv, "--out=", 6) == 0)
+ outfile = 6+*argv;
+
+ else if (strncmp(*argv, "--suffix=", 9) == 0)
+ suffix = 9+*argv;
+
+ else if (strcmp(*argv, "--strip") == 0 || strcmp(*argv, "-s") == 0)
+ ++strip;
+
+ else if (strcmp(*argv, "--errors") == 0 || strcmp(*argv, "-e") == 0)
+ ++errors;
+
+ else if (strcmp(*argv, "--warnings") == 0 || strcmp(*argv, "-w") == 0)
+ ++warnings;
+
+ else if (strcmp(*argv, "--verbose") == 0 || strcmp(*argv, "-v") == 0)
+ ++verbose;
+
+# ifdef PNG_MAXIMUM_INFLATE_WINDOW
+ else if (strcmp(*argv, "--test") == 0)
+ ++set_option;
+# endif
+
+ else if ((*argv)[0] == '-')
+ usage(prog, 3);
+
+ else
+ {
+ int ret, overwrite;
+
+ if (outfile != NULL)
+ overwrite = 0;
+
+ else if (suffix != NULL)
+ {
+ if (*suffix == 0)
+ overwrite = 1;
+
+ else
+ {
+ static char temp_name[FILENAME_MAX];
+ size_t filelen = strlen(*argv);
+ size_t suffixlen = strlen(suffix);
+
+ if (filelen + suffixlen >= FILENAME_MAX)
+ {
+ fprintf(stderr, "%s: output file name too long: %s%s\n", prog,
+ *argv, suffix);
+ exit(3);
+ }
+
+ memcpy(temp_name, *argv, filelen);
+ memcpy(temp_name+filelen, suffix, suffixlen);
+ temp_name[filelen+suffixlen] = 0;
+
+ outfile = temp_name;
+ overwrite = 0;
+ }
+ }
+
+ else
+ overwrite = inplace;
+
+ err +=
+ fix_file(fp, *argv, max_IDAT, overwrite, strip, optimize, outfile);
+
+ if (fpIn != NULL)
+ {
+ fclose(fpIn);
+ fpIn = NULL;
+ }
+
+ z_idat.next_in = z_idat.next_out = Z_NULL;
+ z_idat.avail_in = z_idat.avail_out = 0;
+ ret = inflateReset(&z_idat);
+ if (ret != Z_OK)
+ {
+ fprintf(stderr, "inflateReset failed %d \"%s\"\n", ret, z_idat.msg);
+ inflateEnd(&z_idat);
+ return 3;
+ }
+
+ rewind(fp);
+ outfile = NULL;
+ ++done;
+ }
+ }
+
+ inflateEnd(&z_idat);
+
+ if (!done)
+ usage(prog, 0);
+
+ return err != 0;
+}
+
+#else /* PNG_ZLIB_VERNUM < 0x1240 */
+int
+main(void)
+{
+ fprintf(stderr,
+ "png-fix-too-far-back needs libpng with a zlib >=1.2.4 (not 0x%x)\n",
+ PNG_ZLIB_VERNUM);
+ return 77;
+}
+#endif /* PNG_ZLIB_VERNUM */
+
+#else /* No read support */
+
+int
+main(void)
+{
+ fprintf(stderr, "png-fix-too-far-back does not work without read support\n");
+ return 77;
+}
+#endif /* PNG_READ_SUPPORTED */
diff --git a/libpng-manual.txt b/libpng-manual.txt
index e289432..611f0b5 100644
--- a/libpng-manual.txt
+++ b/libpng-manual.txt
@@ -1,6 +1,6 @@
libpng-manual.txt - A description on how to use and modify libpng
- libpng version 1.7.0beta13 - April 30, 2013
+ libpng version 1.7.0beta13 - May 10, 2013
Updated and distributed by Glenn Randers-Pehrson
<glennrp at users.sourceforge.net>
Copyright (c) 1998-2013 Glenn Randers-Pehrson
@@ -11,7 +11,7 @@
Based on:
- libpng versions 0.97, January 1998, through 1.7.0beta13 - April 30, 2013
+ libpng versions 0.97, January 1998, through 1.7.0beta13 - May 10, 2013
Updated and distributed by Glenn Randers-Pehrson
Copyright (c) 1998-2013 Glenn Randers-Pehrson
@@ -5021,6 +5021,21 @@
libpng16 and later of the GIT repository. They continue to be included
in the tarball releases, however.
+Libpng-1.6.0 and later use the CMF bytes at the beginning of the IDAT stream
+to set the size of the sliding window for reading instead of using the default
+32-kbyte sliding window size. It was discovered that there are hundreds of PNG
+files in the wild that have incorrect CMF bytes that cause libpng to now issue
+a "too far back" error and reject the file. Libpng-1.6.3 provides a way to
+revert to the libpng-1.5.x behavior (ignoring the CMF bytes and using a
+32-kbyte sliding window), and provides a tool
+(contrib/tools/png-fix-too-far-back) for optimizing the CMF bytes
+correctly.
+
+Libpng-1.6.0 and libpng-1.6.1 wrote uncompressed iTXt chunks with the wrong
+length, which resulted in PNG files that cannot be read beyond the bad iTXt
+chunk. This error was fixed in libpng-1.6.3, and a tool (called
+contrib/tools/png-fix-itxt) has been added to the libpng distribution.
+
XIII. Changes to Libpng from version 1.6.x to 1.7.x
Some functions that were deprecated in libpng-1.6.0 were removed:
@@ -5215,7 +5230,7 @@
XVII. Y2K Compliance in libpng
-April 30, 2013
+May 10, 2013
Since the PNG Development group is an ad-hoc body, we can't make
an official declaration.
diff --git a/libpng.3 b/libpng.3
index 5a73d0c..b710075 100644
--- a/libpng.3
+++ b/libpng.3
@@ -1,4 +1,4 @@
-.TH LIBPNG 3 "April 30, 2013"
+.TH LIBPNG 3 "May 10, 2013"
.SH NAME
libpng \- Portable Network Graphics (PNG) Reference Library 1.7.0beta13
.SH SYNOPSIS
@@ -494,7 +494,7 @@
.SH LIBPNG.TXT
libpng-manual.txt - A description on how to use and modify libpng
- libpng version 1.7.0beta13 - April 30, 2013
+ libpng version 1.7.0beta13 - May 10, 2013
Updated and distributed by Glenn Randers-Pehrson
<glennrp at users.sourceforge.net>
Copyright (c) 1998-2013 Glenn Randers-Pehrson
@@ -505,7 +505,7 @@
Based on:
- libpng versions 0.97, January 1998, through 1.7.0beta13 - April 30, 2013
+ libpng versions 0.97, January 1998, through 1.7.0beta13 - May 10, 2013
Updated and distributed by Glenn Randers-Pehrson
Copyright (c) 1998-2013 Glenn Randers-Pehrson
@@ -5516,6 +5516,21 @@
libpng16 and later of the GIT repository. They continue to be included
in the tarball releases, however.
+Libpng-1.6.0 and later use the CMF bytes at the beginning of the IDAT stream
+to set the size of the sliding window for reading instead of using the default
+32-kbyte sliding window size. It was discovered that there are hundreds of PNG
+files in the wild that have incorrect CMF bytes that cause libpng to now issue
+a "too far back" error and reject the file. Libpng-1.6.3 provides a way to
+revert to the libpng-1.5.x behavior (ignoring the CMF bytes and using a
+32-kbyte sliding window), and provides a tool
+(contrib/tools/png-fix-too-far-back) for optimizing the CMF bytes
+correctly.
+
+Libpng-1.6.0 and libpng-1.6.1 wrote uncompressed iTXt chunks with the wrong
+length, which resulted in PNG files that cannot be read beyond the bad iTXt
+chunk. This error was fixed in libpng-1.6.3, and a tool (called
+contrib/tools/png-fix-itxt) has been added to the libpng distribution.
+
.SH XIII. Changes to Libpng from version 1.6.x to 1.7.x
Some functions that were deprecated in libpng-1.6.0 were removed:
@@ -5710,7 +5725,7 @@
.SH XVII. Y2K Compliance in libpng
-April 30, 2013
+May 10, 2013
Since the PNG Development group is an ad-hoc body, we can't make
an official declaration.
@@ -5980,7 +5995,7 @@
Thanks to Frank J. T. Wojcik for helping with the documentation.
-Libpng version 1.7.0beta13 - April 30, 2013:
+Libpng version 1.7.0beta13 - May 10, 2013:
Initially created in 1995 by Guy Eric Schalnat, then of Group 42, Inc.
Currently maintained by Glenn Randers-Pehrson (glennrp at users.sourceforge.net).
@@ -6003,7 +6018,7 @@
This code is released under the libpng license.
-libpng versions 1.2.6, August 15, 2004, through 1.7.0beta13, April 30, 2013, are
+libpng versions 1.2.6, August 15, 2004, through 1.7.0beta13, May 10, 2013, are
Copyright (c) 2004,2006-2007 Glenn Randers-Pehrson, and are
distributed according to the same disclaimer and license as libpng-1.2.5
with the following individual added to the list of Contributing Authors
@@ -6102,7 +6117,7 @@
Glenn Randers-Pehrson
glennrp at users.sourceforge.net
-April 30, 2013
+May 10, 2013
.\" end of man page
diff --git a/libpngpf.3 b/libpngpf.3
index d385221..ff565ed 100644
--- a/libpngpf.3
+++ b/libpngpf.3
@@ -1,4 +1,4 @@
-.TH LIBPNGPF 3 "April 30, 2013"
+.TH LIBPNGPF 3 "May 10, 2013"
.SH NAME
libpng \- Portable Network Graphics (PNG) Reference Library 1.7.0beta13
(private functions)
diff --git a/png.5 b/png.5
index 6b466fb..dea1235 100644
--- a/png.5
+++ b/png.5
@@ -1,4 +1,4 @@
-.TH PNG 5 "April 30, 2013"
+.TH PNG 5 "May 10, 2013"
.SH NAME
png \- Portable Network Graphics (PNG) format
.SH DESCRIPTION
diff --git a/png.c b/png.c
index 39ae975..8eb6c2b 100644
--- a/png.c
+++ b/png.c
@@ -691,13 +691,13 @@
#else
# ifdef __STDC__
return PNG_STRING_NEWLINE \
- "libpng version 1.7.0beta13 - April 30, 2013" PNG_STRING_NEWLINE \
+ "libpng version 1.7.0beta13 - May 10, 2013" PNG_STRING_NEWLINE \
"Copyright (c) 1998-2013 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \
"Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \
"Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \
PNG_STRING_NEWLINE;
# else
- return "libpng version 1.7.0beta13 - April 30, 2013\
+ return "libpng version 1.7.0beta13 - May 10, 2013\
Copyright (c) 1998-2013 Glenn Randers-Pehrson\
Copyright (c) 1996-1997 Andreas Dilger\
Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.";
diff --git a/png.h b/png.h
index 35e7fe5..63f8dec 100644
--- a/png.h
+++ b/png.h
@@ -1,7 +1,7 @@
/* png.h - header file for PNG reference library
*
- * libpng version 1.7.0beta13 - April 30, 2013
+ * libpng version 1.7.0beta13 - May 10, 2013
* Copyright (c) 1998-2013 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@@ -11,7 +11,7 @@
* Authors and maintainers:
* libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat
* libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger
- * libpng versions 0.97, January 1998, through 1.7.0beta13 - April 30, 2013: Glenn
+ * libpng versions 0.97, January 1998, through 1.7.0beta13 - May 10, 2013: Glenn
* See also "Contributing Authors", below.
*
* Note about libpng version numbers:
@@ -200,7 +200,7 @@
*
* This code is released under the libpng license.
*
- * libpng versions 1.2.6, August 15, 2004, through 1.7.0beta13, April 30, 2013, are
+ * libpng versions 1.2.6, August 15, 2004, through 1.7.0beta13, May 10, 2013, are
* Copyright (c) 2004, 2006-2013 Glenn Randers-Pehrson, and are
* distributed according to the same disclaimer and license as libpng-1.2.5
* with the following individual added to the list of Contributing Authors:
@@ -312,7 +312,7 @@
* Y2K compliance in libpng:
* =========================
*
- * April 30, 2013
+ * May 10, 2013
*
* Since the PNG Development group is an ad-hoc body, we can't make
* an official declaration.
@@ -380,7 +380,7 @@
/* Version information for png.h - this should match the version in png.c */
#define PNG_LIBPNG_VER_STRING "1.7.0beta13"
#define PNG_HEADER_VERSION_STRING \
- " libpng version 1.7.0beta13 - April 30, 2013\n"
+ " libpng version 1.7.0beta13 - May 10, 2013\n"
#define PNG_LIBPNG_VER_SONUM 17
#define PNG_LIBPNG_VER_DLLNUM 17
@@ -2916,7 +2916,8 @@
#ifdef PNG_ARM_NEON_API_SUPPORTED
# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */
#endif
-#define PNG_OPTION_NEXT 2 /* Next option - numbers must be even */
+#define PNG_MAXIMUM_INFLATE_WINDOW 2 /* SOFTWARE: force maximum window */
+#define PNG_OPTION_NEXT 4 /* Next option - numbers must be even */
/* Return values: NOTE: there are four values and 'off' is *not* zero */
#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */
diff --git a/pngconf.h b/pngconf.h
index a75e0d5..a0164b7 100644
--- a/pngconf.h
+++ b/pngconf.h
@@ -1,7 +1,7 @@
/* pngconf.h - machine configurable file for libpng
*
- * libpng version 1.7.0beta13 - April 30, 2013
+ * libpng version 1.7.0beta13 - May 10, 2013
*
* Copyright (c) 1998-2013 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
diff --git a/pngpriv.h b/pngpriv.h
index 3e137b5..b6725d2 100644
--- a/pngpriv.h
+++ b/pngpriv.h
@@ -629,6 +629,24 @@
#include "pngstruct.h"
#include "pnginfo.h"
+/* Validate the include paths - the include path used to generate pnglibconf.h
+ * must match that used in the build, or we must be using pnglibconf.h.prebuilt:
+ */
+#if PNG_ZLIB_VERNUM != 0 && PNG_ZLIB_VERNUM != ZLIB_VERNUM
+# error ZLIB_VERNUM != PNG_ZLIB_VERNUM \
+ "-I (include path) error: see the notes in pngpriv.h"
+ /* This means that when pnglibconf.h was built the copy of zlib.h that it
+ * used is not the same as the one being used here. Because the build of
+ * libpng makes decisions to use inflateInit2 and inflateReset2 based on the
+ * zlib version number and because this affects handling of certain broken
+ * PNG files the -I directives must match.
+ *
+ * The most likely explanation is that you passed a -I in CFLAGS, this will
+ * not work; all the preprocessor directories and in particular all the -I
+ * directives must be in CPPFLAGS.
+ */
+#endif
+
/* This is used for 16 bit gamma tables -- only the top level pointers are
* const; this could be changed:
*/
diff --git a/pngrutil.c b/pngrutil.c
index b82b4ea..29154e1 100644
--- a/pngrutil.c
+++ b/pngrutil.c
@@ -311,7 +311,7 @@
* chunk apparently owns the stream. Prior to release it does a png_error.
*/
static int
-png_inflate_claim(png_structrp png_ptr, png_uint_32 owner, int window_bits)
+png_inflate_claim(png_structrp png_ptr, png_uint_32 owner)
{
if (png_ptr->zowner != 0)
{
@@ -346,6 +346,22 @@
*/
{
int ret; /* zlib return code */
+# if PNG_ZLIB_VERNUM >= 0x1240
+
+# if defined(PNG_SET_OPTION_SUPPORTED) && \
+ defined(PNG_MAXIMUM_INFLATE_WINDOW)
+ int window_bits;
+
+ if (((png_ptr->options >> PNG_MAXIMUM_INFLATE_WINDOW) & 3) ==
+ PNG_OPTION_ON)
+ window_bits = 15;
+
+ else
+ window_bits = 0;
+# else
+# define window_bits 0
+# endif
+# endif
/* Set this for safety, just in case the previous owner left pointers to
* memory allocations.
@@ -357,8 +373,7 @@
if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED)
{
-# if ZLIB_VERNUM < 0x1240
- PNG_UNUSED(window_bits)
+# if PNG_ZLIB_VERNUM < 0x1240
ret = inflateReset(&png_ptr->zstream);
# else
ret = inflateReset2(&png_ptr->zstream, window_bits);
@@ -367,7 +382,7 @@
else
{
-# if ZLIB_VERNUM < 0x1240
+# if PNG_ZLIB_VERNUM < 0x1240
ret = inflateInit(&png_ptr->zstream);
# else
ret = inflateInit2(&png_ptr->zstream, window_bits);
@@ -385,6 +400,10 @@
return ret;
}
+
+# ifdef window_bits
+# undef window_bits
+# endif
}
#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED
@@ -557,14 +576,8 @@
if (limit < *newlength)
*newlength = limit;
- /* Now try to claim the stream; the 'warn' setting causes zlib to be told
- * to use the maximum window size during inflate; this hides errors in the
- * deflate header window bits value which is used if '0' is passed. In
- * fact this only has an effect with zlib versions 1.2.4 and later - see
- * the comments in png_inflate_claim above.
- */
- ret = png_inflate_claim(png_ptr, png_ptr->chunk_name,
- png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN ? 15 : 0);
+ /* Now try to claim the stream. */
+ ret = png_inflate_claim(png_ptr, png_ptr->chunk_name);
if (ret == Z_OK)
{
@@ -1334,8 +1347,7 @@
{
read_length -= keyword_length+2;
- if (png_inflate_claim(png_ptr, png_iCCP,
- png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN ? 15 : 0) == Z_OK)
+ if (png_inflate_claim(png_ptr, png_iCCP) == Z_OK)
{
Byte profile_header[132];
Byte local_buffer[PNG_INFLATE_BUF_SIZE];
@@ -4412,7 +4424,7 @@
* IDAT stream has a bogus deflate header window_bits value, but this should
* not be happening any longer!)
*/
- if (png_inflate_claim(png_ptr, png_IDAT, 0) != Z_OK)
+ if (png_inflate_claim(png_ptr, png_IDAT) != Z_OK)
png_error(png_ptr, png_ptr->zstream.msg);
png_ptr->flags |= PNG_FLAG_ROW_INIT;
diff --git a/projects/vstudio/readme.txt b/projects/vstudio/readme.txt
index 2cab710..28050f3 100644
--- a/projects/vstudio/readme.txt
+++ b/projects/vstudio/readme.txt
@@ -1,7 +1,7 @@
VisualStudio instructions
-libpng version 1.7.0beta13 - April 30, 2013
+libpng version 1.7.0beta13 - May 10, 2013
Copyright (c) 1998-2010 Glenn Randers-Pehrson
diff --git a/projects/vstudio/zlib.props b/projects/vstudio/zlib.props
index 27d0082..c26c20c 100644
--- a/projects/vstudio/zlib.props
+++ b/projects/vstudio/zlib.props
@@ -2,7 +2,7 @@
<!--
* zlib.props - location of zlib source
*
- * libpng version 1.7.0beta13 - April 30, 2013
+ * libpng version 1.7.0beta13 - May 10, 2013
*
* Copyright (c) 1998-2011 Glenn Randers-Pehrson
*
diff --git a/scripts/README.txt b/scripts/README.txt
index d0e359a..3701ead 100644
--- a/scripts/README.txt
+++ b/scripts/README.txt
@@ -1,5 +1,5 @@
-Makefiles for libpng version 1.7.0beta13 - April 30, 2013
+Makefiles for libpng version 1.7.0beta13 - May 10, 2013
pnglibconf.h.prebuilt => Stores configuration settings
makefile.linux => Linux/ELF makefile
diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa
index cd3b3c6..77cc7ab 100644
--- a/scripts/pnglibconf.dfa
+++ b/scripts/pnglibconf.dfa
@@ -238,6 +238,7 @@
# ARM_NEON: the optimization itself
# ARM_NEON_API: allow the optimization to be switched on with png_set_hardware
# ARM_NEON_CHECK: compile a run-time check to see if Neon extensions are
+setting ZLIB_VERNUM default @ZLIB_VERNUM
# supported, this is poorly supported and deprectated - use the
# png_set_hardware API.
option ARM_NEON disabled,
@@ -425,7 +426,7 @@
# to libpng 1.6; the new interfaces in 1.6 will take several years to become
# popular.
-option READ enables READ_INTERLACING
+option READ enables READ_INTERLACING SET_OPTION
# Disabling READ_16BIT does not disable reading 16-bit PNG files, but it
# forces them to be chopped down to 8-bit, and disables any 16-bit
diff --git a/scripts/pnglibconf.h.prebuilt b/scripts/pnglibconf.h.prebuilt
index 88865d3..7c39e3c 100644
--- a/scripts/pnglibconf.h.prebuilt
+++ b/scripts/pnglibconf.h.prebuilt
@@ -2,7 +2,7 @@
/* pnglibconf.h - library build configuration */
-/* Libpng version 1.7.0beta13 - April 30, 2013 */
+/* Libpng version 1.7.0beta13 - May 10, 2013 */
/* Copyright (c) 1998-2013 Glenn Randers-Pehrson */
@@ -103,7 +103,7 @@
#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_SEQUENTIAL_READ_SUPPORTED
#define PNG_SETJMP_SUPPORTED
-/*#undef PNG_SET_OPTION_SUPPORTED*/
+#define PNG_SET_OPTION_SUPPORTED
#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
#define PNG_SET_USER_LIMITS_SUPPORTED
#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED
@@ -200,6 +200,7 @@
#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1)
#define PNG_TEXT_Z_DEFAULT_STRATEGY 0
#define PNG_WEIGHT_SHIFT 8
+#define PNG_ZLIB_VERNUM 0 /* unknown */
#define PNG_ZBUF_SIZE 8192
#define PNG_ZLIB_HEADER <zlib.h>
#define PNG_Z_DEFAULT_COMPRESSION (-1)