1 /*- iccfrompng
2 *
3 * COPYRIGHT: Written by John Cunningham Bowler, 2011.
4 * To the extent possible under law, the author has waived all copyright and
5 * related or neighboring rights to this work. This work is published from:
6 * United States.
7 *
8 * Extract any icc profiles found in the given PNG files. This is a simple
9 * example of a program that extracts information from the header of a PNG file
10 * without processing the image. Notice that some header information may occur
11 * after the image data. Textual data and comments are an example; the approach
12 * in this file won't work reliably for such data because it only looks for the
13 * information in the section of the file that precedes the image data.
14 *
15 * Compile and link against libpng and zlib, plus anything else required on the
16 * system you use.
17 *
18 * To use supply a list of PNG files containing iCCP chunks, the chunks will be
19 * extracted to a similarly named file with the extension replaced by 'icc',
20 * which will be overwritten without warning.
21 */
22 #include <stdlib.h>
23 #include <setjmp.h>
24 #include <string.h>
25 #include <stdio.h>
26
27 #include <png.h>
28
29 #if defined(PNG_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) && \
30 defined (PNG_iCCP_SUPPORTED)
31
32
33 static int verbose = 1;
34 static png_byte no_profile[] = "no profile";
35
36 static png_bytep
extract(FILE * fp,png_uint_32 * proflen)37 extract(FILE *fp, png_uint_32 *proflen)
38 {
39 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
40 png_infop info_ptr = NULL;
41 png_bytep result = NULL;
42
43 /* Initialize for error or no profile: */
44 *proflen = 0;
45
46 if (png_ptr == NULL)
47 {
48 fprintf(stderr, "iccfrompng: version library mismatch?\n");
49 return 0;
50 }
51
52 if (setjmp(png_jmpbuf(png_ptr)))
53 {
54 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
55 return 0;
56 }
57
58 png_init_io(png_ptr, fp);
59
60 info_ptr = png_create_info_struct(png_ptr);
61 if (info_ptr == NULL)
62 png_error(png_ptr, "OOM allocating info structure");
63
64 png_read_info(png_ptr, info_ptr);
65
66 {
67 png_charp name;
68 int compression_type;
69 png_bytep profile;
70
71 if (png_get_iCCP(png_ptr, info_ptr, &name, &compression_type, &profile,
72 proflen) & PNG_INFO_iCCP)
73 {
74 result = malloc(*proflen);
75 if (result != NULL)
76 memcpy(result, profile, *proflen);
77
78 else
79 png_error(png_ptr, "OOM allocating profile buffer");
80 }
81
82 else
83 result = no_profile;
84 }
85
86 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
87 return result;
88 }
89
90 static int
extract_one_file(const char * filename)91 extract_one_file(const char *filename)
92 {
93 int result = 0;
94 FILE *fp = fopen(filename, "rb");
95
96 if (fp != NULL)
97 {
98 png_uint_32 proflen = 0;
99 png_bytep profile = extract(fp, &proflen);
100
101 if (profile != NULL && profile != no_profile)
102 {
103 size_t len;
104 char *output;
105
106 {
107 const char *ep = strrchr(filename, '.');
108
109 if (ep != NULL)
110 len = ep-filename;
111
112 else
113 len = strlen(filename);
114 }
115
116 output = malloc(len + 5);
117 if (output != NULL)
118 {
119 FILE *of;
120
121 memcpy(output, filename, len);
122 strcpy(output+len, ".icc");
123
124 of = fopen(output, "wb");
125 if (of != NULL)
126 {
127 if (fwrite(profile, proflen, 1, of) == 1 &&
128 fflush(of) == 0 &&
129 fclose(of) == 0)
130 {
131 if (verbose)
132 printf("%s -> %s\n", filename, output);
133 /* Success return */
134 result = 1;
135 }
136
137 else
138 {
139 fprintf(stderr, "%s: error writing profile\n", output);
140 if (remove(output))
141 fprintf(stderr, "%s: could not remove file\n", output);
142 }
143 }
144
145 else
146 fprintf(stderr, "%s: failed to open output file\n", output);
147
148 free(output);
149 }
150
151 else
152 fprintf(stderr, "%s: OOM allocating string!\n", filename);
153
154 free(profile);
155 }
156
157 else if (verbose && profile == no_profile)
158 printf("%s has no profile\n", filename);
159 }
160
161 else
162 fprintf(stderr, "%s: could not open file\n", filename);
163
164 return result;
165 }
166
167 int
main(int argc,char ** argv)168 main(int argc, char **argv)
169 {
170 int i;
171 int extracted = 0;
172
173 for (i=1; i<argc; ++i)
174 {
175 if (strcmp(argv[i], "-q") == 0)
176 verbose = 0;
177
178 else if (extract_one_file(argv[i]))
179 extracted = 1;
180 }
181
182 /* Exit code is true if any extract succeeds */
183 return extracted == 0;
184 }
185 #endif /* READ && STDIO && iCCP */
186