1ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 2ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik/* png-fix-itxt version 1.0.0 3ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * 49b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * Copyright 2015 Glenn Randers-Pehrson 59b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett * Last changed in libpng 1.6.18 [July 23, 2015] 6ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * 7ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * This code is released under the libpng license. 8ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * For conditions of distribution and use, see the disclaimer 9ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * and license in png.h 10ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * 1106f1087a94f1e48298e89d77ccc51a0ced871958Matt Sarett * Usage: 12ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * 13ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * png-fix-itxt.exe < bad.png > good.png 14ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * 15ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * Fixes a PNG file written with libpng-1.6.0 or 1.6.1 that has one or more 16ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * uncompressed iTXt chunks. Assumes that the actual length is greater 17ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * than or equal to the value in the length byte, and that the CRC is 18ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * correct for the actual length. This program hunts for the CRC and 19ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * adjusts the length byte accordingly. It is not an error to process a 20ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * PNG file that has no iTXt chunks or one that has valid iTXt chunks; 21ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * such files will simply be copied. 22ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * 23ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * Requires zlib (for crc32 and Z_NULL); build with 24ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * 25ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * gcc -O -o png-fix-itxt png-fix-itxt.c -lz 26ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * 27ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * If you need to handle iTXt chunks larger than 500000 kbytes you must 28ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * rebuild png-fix-itxt with a larger values of MAX_LENGTH (or a smaller value 29ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * if you know you will never encounter such huge iTXt chunks). 30ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik */ 31ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 32ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#include <stdio.h> 33ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#include <zlib.h> 34ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 35ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#define MAX_LENGTH 500000 36ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 379b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett/* Read one character (inchar), also return octet (c), break if EOF */ 389b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett#define GETBREAK inchar=getchar(); \ 399b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett c=(inchar & 0xffU);\ 409b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett if (inchar != c) break 41ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikint 42ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikmain(void) 43ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik{ 44ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik unsigned int i; 45ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik unsigned char buf[MAX_LENGTH]; 46ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik unsigned long crc; 47ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik unsigned char c; 48ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik int inchar; 49ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 50ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik/* Skip 8-byte signature */ 51ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik for (i=8; i; i--) 52ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik { 539b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett GETBREAK; 54ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik putchar(c); 55ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik } 56ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 579b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarettif (inchar == c) /* !EOF */ 58ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikfor (;;) 59ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik { 60ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik /* Read the length */ 61ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik unsigned long length; /* must be 32 bits! */ 629b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett GETBREAK; buf[0] = c; length = c; length <<= 8; 639b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett GETBREAK; buf[1] = c; length += c; length <<= 8; 649b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett GETBREAK; buf[2] = c; length += c; length <<= 8; 659b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett GETBREAK; buf[3] = c; length += c; 66ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 67ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik /* Read the chunkname */ 689b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett GETBREAK; buf[4] = c; 699b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett GETBREAK; buf[5] = c; 709b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett GETBREAK; buf[6] = c; 719b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett GETBREAK; buf[7] = c; 72ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 73ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 74ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik /* The iTXt chunk type expressed as integers is (105, 84, 88, 116) */ 75ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik if (buf[4] == 105 && buf[5] == 84 && buf[6] == 88 && buf[7] == 116) 76ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik { 77ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik if (length >= MAX_LENGTH-12) 78ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik break; /* To do: handle this more gracefully */ 79ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 80ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik /* Initialize the CRC */ 81ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik crc = crc32(0, Z_NULL, 0); 82ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 83ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik /* Copy the data bytes */ 84ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik for (i=8; i < length + 12; i++) 85ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik { 869b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett GETBREAK; buf[i] = c; 87ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik } 88ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 899b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett if (inchar != c) /* EOF */ 909b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett break; 919b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett 92ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik /* Calculate the CRC */ 93ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik crc = crc32(crc, buf+4, (uInt)length+4); 94ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 95ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik for (;;) 96ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik { 97ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik /* Check the CRC */ 989b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett if (((crc >> 24) & 0xffU) == buf[length+8] && 999b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett ((crc >> 16) & 0xffU) == buf[length+9] && 1009b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett ((crc >> 8) & 0xffU) == buf[length+10] && 1019b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett ((crc ) & 0xffU) == buf[length+11]) 102ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik break; 103ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 104ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik length++; 105ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 106ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik if (length >= MAX_LENGTH-12) 107ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik break; 108ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 1099b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett GETBREAK; 1109b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett buf[length+11] = c; 111ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 112ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik /* Update the CRC */ 113ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik crc = crc32(crc, buf+7+length, 1); 114ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik } 115ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 1169b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett if (inchar != c) /* EOF */ 1179b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett break; 1189b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett 119ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik /* Update length bytes */ 1209b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett buf[0] = (unsigned char)((length >> 24) & 0xffU); 1219b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett buf[1] = (unsigned char)((length >> 16) & 0xffU); 1229b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett buf[2] = (unsigned char)((length >> 8) & 0xffU); 1239b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett buf[3] = (unsigned char)((length ) & 0xffU); 124ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 125ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik /* Write the fixed iTXt chunk (length, name, data, crc) */ 126ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik for (i=0; i<length+12; i++) 127ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik putchar(buf[i]); 128ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik } 129ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 130ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik else 131ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik { 1329b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett if (inchar != c) /* EOF */ 1339b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett break; 1349b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett 135ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik /* Copy bytes that were already read (length and chunk name) */ 136ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik for (i=0; i<8; i++) 137ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik putchar(buf[i]); 138ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 139ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik /* Copy data bytes and CRC */ 140ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik for (i=8; i< length+12; i++) 141ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik { 1429b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett GETBREAK; 143ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik putchar(c); 144ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik } 145ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 1469b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett if (inchar != c) /* EOF */ 147ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik { 148ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik break; 149ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik } 150ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 151ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik /* The IEND chunk type expressed as integers is (73, 69, 78, 68) */ 152ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68) 153ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik break; 154ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik } 155ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 1569b1fe63dcc7ba076b9730b7bfa031cc0dbc25561Matt Sarett if (inchar != c) /* EOF */ 157ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik break; 158ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 159ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68) 160ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik break; 161ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik } 162ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik 163ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik return 0; 164ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik} 165