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
41int
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   {
53      GETBREAK;
54      putchar(c);
55   }
56
57if (inchar == c) /* !EOF */
58for (;;)
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