blob: c7654c11363c45093d92c65b7a4f8065de6e689a [file] [log] [blame]
Chris Craikca2bf812013-07-29 15:28:30 -07001
2/* png-fix-itxt version 1.0.0
3 *
Matt Sarett9ea75692016-01-08 13:00:42 -05004 * Copyright 2015 Glenn Randers-Pehrson
5 * Last changed in libpng 1.6.18 [July 23, 2015]
Chris Craikca2bf812013-07-29 15:28:30 -07006 *
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 *
Matt Sarett9ea75692016-01-08 13:00:42 -050011 * Usage:
Chris Craikca2bf812013-07-29 15:28:30 -070012 *
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
Matt Sarett9ea75692016-01-08 13:00:42 -050037/* 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
Chris Craikca2bf812013-07-29 15:28:30 -070041int
42main(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 {
Matt Sarett9ea75692016-01-08 13:00:42 -050053 GETBREAK;
Chris Craikca2bf812013-07-29 15:28:30 -070054 putchar(c);
55 }
56
Matt Sarett9ea75692016-01-08 13:00:42 -050057if (inchar == c) /* !EOF */
Chris Craikca2bf812013-07-29 15:28:30 -070058for (;;)
59 {
60 /* Read the length */
61 unsigned long length; /* must be 32 bits! */
Matt Sarett9ea75692016-01-08 13:00:42 -050062 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;
Chris Craikca2bf812013-07-29 15:28:30 -070066
67 /* Read the chunkname */
Matt Sarett9ea75692016-01-08 13:00:42 -050068 GETBREAK; buf[4] = c;
69 GETBREAK; buf[5] = c;
70 GETBREAK; buf[6] = c;
71 GETBREAK; buf[7] = c;
Chris Craikca2bf812013-07-29 15:28:30 -070072
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 {
Matt Sarett9ea75692016-01-08 13:00:42 -050086 GETBREAK; buf[i] = c;
Chris Craikca2bf812013-07-29 15:28:30 -070087 }
88
Matt Sarett9ea75692016-01-08 13:00:42 -050089 if (inchar != c) /* EOF */
90 break;
91
Chris Craikca2bf812013-07-29 15:28:30 -070092 /* Calculate the CRC */
93 crc = crc32(crc, buf+4, (uInt)length+4);
94
95 for (;;)
96 {
97 /* Check the CRC */
Matt Sarett9ea75692016-01-08 13:00:42 -050098 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])
Chris Craikca2bf812013-07-29 15:28:30 -0700102 break;
103
104 length++;
105
106 if (length >= MAX_LENGTH-12)
107 break;
108
Matt Sarett9ea75692016-01-08 13:00:42 -0500109 GETBREAK;
110 buf[length+11] = c;
Chris Craikca2bf812013-07-29 15:28:30 -0700111
112 /* Update the CRC */
113 crc = crc32(crc, buf+7+length, 1);
114 }
115
Matt Sarett9ea75692016-01-08 13:00:42 -0500116 if (inchar != c) /* EOF */
117 break;
118
Chris Craikca2bf812013-07-29 15:28:30 -0700119 /* Update length bytes */
Matt Sarett9ea75692016-01-08 13:00:42 -0500120 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);
Chris Craikca2bf812013-07-29 15:28:30 -0700124
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 {
Matt Sarett9ea75692016-01-08 13:00:42 -0500132 if (inchar != c) /* EOF */
133 break;
134
Chris Craikca2bf812013-07-29 15:28:30 -0700135 /* 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 {
Matt Sarett9ea75692016-01-08 13:00:42 -0500142 GETBREAK;
Chris Craikca2bf812013-07-29 15:28:30 -0700143 putchar(c);
144 }
145
Matt Sarett9ea75692016-01-08 13:00:42 -0500146 if (inchar != c) /* EOF */
Chris Craikca2bf812013-07-29 15:28:30 -0700147 {
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
Matt Sarett9ea75692016-01-08 13:00:42 -0500156 if (inchar != c) /* EOF */
Chris Craikca2bf812013-07-29 15:28:30 -0700157 break;
158
159 if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68)
160 break;
161 }
162
163 return 0;
164}