1ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
2ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik/* png-fix-itxt version 1.0.0
3ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *
4ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * Copyright 2013 Glenn Randers-Pehrson
5ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * Last changed in libpng 1.6.3 [July 18, 2013]
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 *
11ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * 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
37ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#define GETBREAK ((unsigned char)(inchar=getchar())); if (inchar == EOF) break
38ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
39ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikint
40ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikmain(void)
41ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik{
42ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned int i;
43ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned char buf[MAX_LENGTH];
44ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned long crc;
45ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned char c;
46ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   int inchar;
47ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
48ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik/* Skip 8-byte signature */
49ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   for (i=8; i; i--)
50ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   {
51ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      c=GETBREAK;
52ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      putchar(c);
53ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   }
54ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
55ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikif (inchar != EOF)
56ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikfor (;;)
57ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik {
58ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   /* Read the length */
59ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned long length; /* must be 32 bits! */
60ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   c=GETBREAK; buf[0] = c; length  = c; length <<= 8;
61ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   c=GETBREAK; buf[1] = c; length += c; length <<= 8;
62ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   c=GETBREAK; buf[2] = c; length += c; length <<= 8;
63ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   c=GETBREAK; buf[3] = c; length += c;
64ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
65ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   /* Read the chunkname */
66ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   c=GETBREAK; buf[4] = c;
67ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   c=GETBREAK; buf[5] = c;
68ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   c=GETBREAK; buf[6] = c;
69ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   c=GETBREAK; buf[7] = c;
70ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
71ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
72ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   /* The iTXt chunk type expressed as integers is (105, 84, 88, 116) */
73ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   if (buf[4] == 105 && buf[5] == 84 && buf[6] == 88 && buf[7] == 116)
74ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   {
75ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      if (length >= MAX_LENGTH-12)
76ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         break;  /* To do: handle this more gracefully */
77ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
78ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Initialize the CRC */
79ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      crc = crc32(0, Z_NULL, 0);
80ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
81ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Copy the data bytes */
82ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (i=8; i < length + 12; i++)
83ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
84ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         c=GETBREAK; buf[i] = c;
85ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
86ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
87ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Calculate the CRC */
88ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      crc = crc32(crc, buf+4, (uInt)length+4);
89ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
90ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (;;)
91ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
92ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik        /* Check the CRC */
93ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik        if (((crc >> 24) & 0xff) == buf[length+8] &&
94ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            ((crc >> 16) & 0xff) == buf[length+9] &&
95ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            ((crc >>  8) & 0xff) == buf[length+10] &&
96ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            ((crc      ) & 0xff) == buf[length+11])
97ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik           break;
98ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
99ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik        length++;
100ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
101ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik        if (length >= MAX_LENGTH-12)
102ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik           break;
103ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
104ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik        c=GETBREAK;
105ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik        buf[length+11]=c;
106ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
107ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik        /* Update the CRC */
108ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik        crc = crc32(crc, buf+7+length, 1);
109ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
110ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
111ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Update length bytes */
112ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      buf[0] = (unsigned char)((length << 24) & 0xff);
113ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      buf[1] = (unsigned char)((length << 16) & 0xff);
114ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      buf[2] = (unsigned char)((length <<  8) & 0xff);
115ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      buf[3] = (unsigned char)((length      ) & 0xff);
116ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
117ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Write the fixed iTXt chunk (length, name, data, crc) */
118ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (i=0; i<length+12; i++)
119ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         putchar(buf[i]);
120ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   }
121ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
122ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   else
123ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   {
124ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Copy bytes that were already read (length and chunk name) */
125ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (i=0; i<8; i++)
126ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         putchar(buf[i]);
127ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
128ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Copy data bytes and CRC */
129ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (i=8; i< length+12; i++)
130ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
131ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         c=GETBREAK;
132ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         putchar(c);
133ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
134ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
135ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      if (inchar == EOF)
136ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
137ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         break;
138ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
139ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
140ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   /* The IEND chunk type expressed as integers is (73, 69, 78, 68) */
141ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68)
142ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         break;
143ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   }
144ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
145ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   if (inchar == EOF)
146ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      break;
147ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
148ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   if (buf[4] == 73 && buf[5] == 69 && buf[6] == 78 && buf[7] == 68)
149ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik     break;
150ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik }
151ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
152ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik return 0;
153ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik}
154