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 29static int verbose = 1; 30static png_byte no_profile[] = "no profile"; 31 32static png_bytep 33extract(FILE *fp, png_uint_32 *proflen) 34{ 35 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); 36 png_infop info_ptr = NULL; 37 png_bytep result = NULL; 38 39 /* Initialize for error or no profile: */ 40 *proflen = 0; 41 42 if (png_ptr == NULL) 43 { 44 fprintf(stderr, "iccfrompng: version library mismatch?\n"); 45 return 0; 46 } 47 48 if (setjmp(png_jmpbuf(png_ptr))) 49 { 50 png_destroy_read_struct(&png_ptr, &info_ptr, NULL); 51 return 0; 52 } 53 54 png_init_io(png_ptr, fp); 55 56 info_ptr = png_create_info_struct(png_ptr); 57 if (info_ptr == NULL) 58 png_error(png_ptr, "OOM allocating info structure"); 59 60 png_read_info(png_ptr, info_ptr); 61 62 { 63 png_charp name; 64 int compression_type; 65 png_bytep profile; 66 67 if (png_get_iCCP(png_ptr, info_ptr, &name, &compression_type, &profile, 68 proflen) & PNG_INFO_iCCP) 69 { 70 result = malloc(*proflen); 71 if (result != NULL) 72 memcpy(result, profile, *proflen); 73 74 else 75 png_error(png_ptr, "OOM allocating profile buffer"); 76 } 77 78 else 79 result = no_profile; 80 } 81 82 png_destroy_read_struct(&png_ptr, &info_ptr, NULL); 83 return result; 84} 85 86static int 87extract_one_file(const char *filename) 88{ 89 int result = 0; 90 FILE *fp = fopen(filename, "rb"); 91 92 if (fp != NULL) 93 { 94 png_uint_32 proflen = 0; 95 png_bytep profile = extract(fp, &proflen); 96 97 if (profile != NULL && profile != no_profile) 98 { 99 size_t len; 100 char *output; 101 102 { 103 const char *ep = strrchr(filename, '.'); 104 105 if (ep != NULL) 106 len = ep-filename; 107 108 else 109 len = strlen(filename); 110 } 111 112 output = malloc(len + 5); 113 if (output != NULL) 114 { 115 FILE *of; 116 117 memcpy(output, filename, len); 118 strcpy(output+len, ".icc"); 119 120 of = fopen(output, "wb"); 121 if (of != NULL) 122 { 123 if (fwrite(profile, proflen, 1, of) == 1 && 124 fflush(of) == 0 && 125 fclose(of) == 0) 126 { 127 if (verbose) 128 printf("%s -> %s\n", filename, output); 129 /* Success return */ 130 result = 1; 131 } 132 133 else 134 { 135 fprintf(stderr, "%s: error writing profile\n", output); 136 if (remove(output)) 137 fprintf(stderr, "%s: could not remove file\n", output); 138 } 139 } 140 141 else 142 fprintf(stderr, "%s: failed to open output file\n", output); 143 144 free(output); 145 } 146 147 else 148 fprintf(stderr, "%s: OOM allocating string!\n", filename); 149 150 free(profile); 151 } 152 153 else if (verbose && profile == no_profile) 154 printf("%s has no profile\n", filename); 155 } 156 157 else 158 fprintf(stderr, "%s: could not open file\n", filename); 159 160 return result; 161} 162 163int 164main(int argc, char **argv) 165{ 166 int i; 167 int extracted = 0; 168 169 for (i=1; i<argc; ++i) 170 { 171 if (strcmp(argv[i], "-q") == 0) 172 verbose = 0; 173 174 else if (extract_one_file(argv[i])) 175 extracted = 1; 176 } 177 178 /* Exit code is true if any extract succeeds */ 179 return extracted == 0; 180} 181