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
main(void)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