| |
| /* 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; |
| } |