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 preceeds 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