/* png-fix-itxt version 1.0.0 * * Copyright 2015 Glenn Randers-Pehrson * Last changed in libpng 1.6.18 [July 23, 2015] * * 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 /* Read one character (inchar), also return octet (c), break if EOF */ #define GETBREAK inchar=getchar(); \ c=(inchar & 0xffU);\ if (inchar != c) 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--) { GETBREAK; putchar(c); } if (inchar == c) /* !EOF */ for (;;) { /* Read the length */ unsigned long length; /* must be 32 bits! */ GETBREAK; buf[0] = c; length = c; length <<= 8; GETBREAK; buf[1] = c; length += c; length <<= 8; GETBREAK; buf[2] = c; length += c; length <<= 8; GETBREAK; buf[3] = c; length += c; /* Read the chunkname */ GETBREAK; buf[4] = c; GETBREAK; buf[5] = c; GETBREAK; buf[6] = 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++) { GETBREAK; buf[i] = c; } if (inchar != c) /* EOF */ break; /* Calculate the CRC */ crc = crc32(crc, buf+4, (uInt)length+4); for (;;) { /* Check the CRC */ if (((crc >> 24) & 0xffU) == buf[length+8] && ((crc >> 16) & 0xffU) == buf[length+9] && ((crc >> 8) & 0xffU) == buf[length+10] && ((crc ) & 0xffU) == buf[length+11]) break; length++; if (length >= MAX_LENGTH-12) break; GETBREAK; buf[length+11] = c; /* Update the CRC */ crc = crc32(crc, buf+7+length, 1); } if (inchar != c) /* EOF */ break; /* Update length bytes */ buf[0] = (unsigned char)((length >> 24) & 0xffU); buf[1] = (unsigned char)((length >> 16) & 0xffU); buf[2] = (unsigned char)((length >> 8) & 0xffU); buf[3] = (unsigned char)((length ) & 0xffU); /* Write the fixed iTXt chunk (length, name, data, crc) */ for (i=0; i<length+12; i++) putchar(buf[i]); } else { if (inchar != c) /* EOF */ break; /* 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++) { GETBREAK; putchar(c); } if (inchar != c) /* 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 != c) /* EOF */ break; if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68) break; } return 0; }