1 2 /* png-fix-itxt version 1.0.0 3 * 4 * Copyright 2015 Glenn Randers-Pehrson 5 * Last changed in libpng 1.6.18 [July 23, 2015] 6 * 7 * This code is released under the libpng license. 8 * For conditions of distribution and use, see the disclaimer 9 * and license in png.h 10 * 11 * Usage: 12 * 13 * png-fix-itxt.exe < bad.png > good.png 14 * 15 * Fixes a PNG file written with libpng-1.6.0 or 1.6.1 that has one or more 16 * uncompressed iTXt chunks. Assumes that the actual length is greater 17 * than or equal to the value in the length byte, and that the CRC is 18 * correct for the actual length. This program hunts for the CRC and 19 * adjusts the length byte accordingly. It is not an error to process a 20 * PNG file that has no iTXt chunks or one that has valid iTXt chunks; 21 * such files will simply be copied. 22 * 23 * Requires zlib (for crc32 and Z_NULL); build with 24 * 25 * gcc -O -o png-fix-itxt png-fix-itxt.c -lz 26 * 27 * If you need to handle iTXt chunks larger than 500000 kbytes you must 28 * rebuild png-fix-itxt with a larger values of MAX_LENGTH (or a smaller value 29 * if you know you will never encounter such huge iTXt chunks). 30 */ 31 32 #include <stdio.h> 33 #include <zlib.h> 34 35 #define MAX_LENGTH 500000 36 37 /* Read one character (inchar), also return octet (c), break if EOF */ 38 #define GETBREAK inchar=getchar(); \ 39 c=(inchar & 0xffU);\ 40 if (inchar != c) break 41 int 42 main(void) 43 { 44 unsigned int i; 45 unsigned char buf[MAX_LENGTH]; 46 unsigned long crc; 47 unsigned char c; 48 int inchar; 49 50 /* Skip 8-byte signature */ 51 for (i=8; i; i--) 52 { 53 GETBREAK; 54 putchar(c); 55 } 56 57 if (inchar == c) /* !EOF */ 58 for (;;) 59 { 60 /* Read the length */ 61 unsigned long length; /* must be 32 bits! */ 62 GETBREAK; buf[0] = c; length = c; length <<= 8; 63 GETBREAK; buf[1] = c; length += c; length <<= 8; 64 GETBREAK; buf[2] = c; length += c; length <<= 8; 65 GETBREAK; buf[3] = c; length += c; 66 67 /* Read the chunkname */ 68 GETBREAK; buf[4] = c; 69 GETBREAK; buf[5] = c; 70 GETBREAK; buf[6] = c; 71 GETBREAK; buf[7] = c; 72 73 74 /* The iTXt chunk type expressed as integers is (105, 84, 88, 116) */ 75 if (buf[4] == 105 && buf[5] == 84 && buf[6] == 88 && buf[7] == 116) 76 { 77 if (length >= MAX_LENGTH-12) 78 break; /* To do: handle this more gracefully */ 79 80 /* Initialize the CRC */ 81 crc = crc32(0, Z_NULL, 0); 82 83 /* Copy the data bytes */ 84 for (i=8; i < length + 12; i++) 85 { 86 GETBREAK; buf[i] = c; 87 } 88 89 if (inchar != c) /* EOF */ 90 break; 91 92 /* Calculate the CRC */ 93 crc = crc32(crc, buf+4, (uInt)length+4); 94 95 for (;;) 96 { 97 /* Check the CRC */ 98 if (((crc >> 24) & 0xffU) == buf[length+8] && 99 ((crc >> 16) & 0xffU) == buf[length+9] && 100 ((crc >> 8) & 0xffU) == buf[length+10] && 101 ((crc ) & 0xffU) == buf[length+11]) 102 break; 103 104 length++; 105 106 if (length >= MAX_LENGTH-12) 107 break; 108 109 GETBREAK; 110 buf[length+11] = c; 111 112 /* Update the CRC */ 113 crc = crc32(crc, buf+7+length, 1); 114 } 115 116 if (inchar != c) /* EOF */ 117 break; 118 119 /* Update length bytes */ 120 buf[0] = (unsigned char)((length >> 24) & 0xffU); 121 buf[1] = (unsigned char)((length >> 16) & 0xffU); 122 buf[2] = (unsigned char)((length >> 8) & 0xffU); 123 buf[3] = (unsigned char)((length ) & 0xffU); 124 125 /* Write the fixed iTXt chunk (length, name, data, crc) */ 126 for (i=0; i<length+12; i++) 127 putchar(buf[i]); 128 } 129 130 else 131 { 132 if (inchar != c) /* EOF */ 133 break; 134 135 /* Copy bytes that were already read (length and chunk name) */ 136 for (i=0; i<8; i++) 137 putchar(buf[i]); 138 139 /* Copy data bytes and CRC */ 140 for (i=8; i< length+12; i++) 141 { 142 GETBREAK; 143 putchar(c); 144 } 145 146 if (inchar != c) /* EOF */ 147 { 148 break; 149 } 150 151 /* The IEND chunk type expressed as integers is (73, 69, 78, 68) */ 152 if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68) 153 break; 154 } 155 156 if (inchar != c) /* EOF */ 157 break; 158 159 if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68) 160 break; 161 } 162 163 return 0; 164 } 165