You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
4.1 KiB
164 lines
4.1 KiB
|
|
/* 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; |
|
}
|
|
|