fukasawa e60969
/*- iccfrompng
fukasawa e60969
 *
fukasawa e60969
 * COPYRIGHT: Written by John Cunningham Bowler, 2011.
fukasawa e60969
 * To the extent possible under law, the author has waived all copyright and
fukasawa e60969
 * related or neighboring rights to this work.  This work is published from:
fukasawa e60969
 * United States.
fukasawa e60969
 *
fukasawa e60969
 * Extract any icc profiles found in the given PNG files.  This is a simple
fukasawa e60969
 * example of a program that extracts information from the header of a PNG file
fukasawa e60969
 * without processing the image.  Notice that some header information may occur
fukasawa e60969
 * after the image data. Textual data and comments are an example; the approach
fukasawa e60969
 * in this file won't work reliably for such data because it only looks for the
fukasawa e60969
 * information in the section of the file that preceeds the image data.
fukasawa e60969
 *
fukasawa e60969
 * Compile and link against libpng and zlib, plus anything else required on the
fukasawa e60969
 * system you use.
fukasawa e60969
 *
fukasawa e60969
 * To use supply a list of PNG files containing iCCP chunks, the chunks will be
fukasawa e60969
 * extracted to a similarly named file with the extension replaced by 'icc',
fukasawa e60969
 * which will be overwritten without warning.
fukasawa e60969
 */
fukasawa e60969
#include <stdlib.h></stdlib.h>
fukasawa e60969
#include <setjmp.h></setjmp.h>
fukasawa e60969
#include <string.h></string.h>
fukasawa e60969
#include <stdio.h></stdio.h>
fukasawa e60969
fukasawa e60969
#include <png.h></png.h>
fukasawa e60969
fukasawa e60969
#if defined(PNG_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) && \
fukasawa e60969
    defined (PNG_iCCP_SUPPORTED)
fukasawa e60969
fukasawa e60969
fukasawa e60969
static int verbose = 1;
fukasawa e60969
static png_byte no_profile[] = "no profile";
fukasawa e60969
fukasawa e60969
static png_bytep
fukasawa e60969
extract(FILE *fp, png_uint_32 *proflen)
fukasawa e60969
{
fukasawa e60969
   png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
fukasawa e60969
   png_infop info_ptr = NULL;
fukasawa e60969
   png_bytep result = NULL;
fukasawa e60969
fukasawa e60969
   /* Initialize for error or no profile: */
fukasawa e60969
   *proflen = 0;
fukasawa e60969
fukasawa e60969
   if (png_ptr == NULL)
fukasawa e60969
   {
fukasawa e60969
      fprintf(stderr, "iccfrompng: version library mismatch?\n");
fukasawa e60969
      return 0;
fukasawa e60969
   }
fukasawa e60969
fukasawa e60969
   if (setjmp(png_jmpbuf(png_ptr)))
fukasawa e60969
   {
fukasawa e60969
      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fukasawa e60969
      return 0;
fukasawa e60969
   }
fukasawa e60969
fukasawa e60969
   png_init_io(png_ptr, fp);
fukasawa e60969
fukasawa e60969
   info_ptr = png_create_info_struct(png_ptr);
fukasawa e60969
   if (info_ptr == NULL)
fukasawa e60969
      png_error(png_ptr, "OOM allocating info structure");
fukasawa e60969
fukasawa e60969
   png_read_info(png_ptr, info_ptr);
fukasawa e60969
fukasawa e60969
   {
fukasawa e60969
      png_charp name;
fukasawa e60969
      int compression_type;
fukasawa e60969
      png_bytep profile;
fukasawa e60969
fukasawa e60969
      if (png_get_iCCP(png_ptr, info_ptr, &name, &compression_type, &profile,
fukasawa e60969
         proflen) & PNG_INFO_iCCP)
fukasawa e60969
      {
fukasawa e60969
         result = malloc(*proflen);
fukasawa e60969
         if (result != NULL)
fukasawa e60969
            memcpy(result, profile, *proflen);
fukasawa e60969
fukasawa e60969
         else
fukasawa e60969
            png_error(png_ptr, "OOM allocating profile buffer");
fukasawa e60969
      }
fukasawa e60969
fukasawa e60969
      else
fukasawa e60969
	result = no_profile;
fukasawa e60969
   }
fukasawa e60969
fukasawa e60969
   png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
fukasawa e60969
   return result;
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
static int
fukasawa e60969
extract_one_file(const char *filename)
fukasawa e60969
{
fukasawa e60969
   int result = 0;
fukasawa e60969
   FILE *fp = fopen(filename, "rb");
fukasawa e60969
fukasawa e60969
   if (fp != NULL)
fukasawa e60969
   {
fukasawa e60969
      png_uint_32 proflen = 0;
fukasawa e60969
      png_bytep profile = extract(fp, &proflen);
fukasawa e60969
fukasawa e60969
      if (profile != NULL && profile != no_profile)
fukasawa e60969
      {
fukasawa e60969
         size_t len;
fukasawa e60969
         char *output;
fukasawa e60969
fukasawa e60969
         {
fukasawa e60969
            const char *ep = strrchr(filename, '.');
fukasawa e60969
fukasawa e60969
            if (ep != NULL)
fukasawa e60969
               len = ep-filename;
fukasawa e60969
fukasawa e60969
            else
fukasawa e60969
               len = strlen(filename);
fukasawa e60969
         }
fukasawa e60969
fukasawa e60969
         output = malloc(len + 5);
fukasawa e60969
         if (output != NULL)
fukasawa e60969
         {
fukasawa e60969
            FILE *of;
fukasawa e60969
fukasawa e60969
            memcpy(output, filename, len);
fukasawa e60969
            strcpy(output+len, ".icc");
fukasawa e60969
fukasawa e60969
            of = fopen(output, "wb");
fukasawa e60969
            if (of != NULL)
fukasawa e60969
            {
fukasawa e60969
               if (fwrite(profile, proflen, 1, of) == 1 &&
fukasawa e60969
                  fflush(of) == 0 &&
fukasawa e60969
                  fclose(of) == 0)
fukasawa e60969
               {
fukasawa e60969
                  if (verbose)
fukasawa e60969
                     printf("%s -> %s\n", filename, output);
fukasawa e60969
                  /* Success return */
fukasawa e60969
                  result = 1;
fukasawa e60969
               }
fukasawa e60969
fukasawa e60969
               else
fukasawa e60969
               {
fukasawa e60969
                  fprintf(stderr, "%s: error writing profile\n", output);
fukasawa e60969
                  if (remove(output))
fukasawa e60969
                     fprintf(stderr, "%s: could not remove file\n", output);
fukasawa e60969
               }
fukasawa e60969
            }
fukasawa e60969
fukasawa e60969
            else
fukasawa e60969
               fprintf(stderr, "%s: failed to open output file\n", output);
fukasawa e60969
fukasawa e60969
            free(output);
fukasawa e60969
         }
fukasawa e60969
fukasawa e60969
         else
fukasawa e60969
            fprintf(stderr, "%s: OOM allocating string!\n", filename);
fukasawa e60969
fukasawa e60969
         free(profile);
fukasawa e60969
      }
fukasawa e60969
fukasawa e60969
      else if (verbose && profile == no_profile)
fukasawa e60969
	printf("%s has no profile\n", filename);
fukasawa e60969
   }
fukasawa e60969
fukasawa e60969
   else
fukasawa e60969
      fprintf(stderr, "%s: could not open file\n", filename);
fukasawa e60969
fukasawa e60969
   return result;
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
int
fukasawa e60969
main(int argc, char **argv)
fukasawa e60969
{
fukasawa e60969
   int i;
fukasawa e60969
   int extracted = 0;
fukasawa e60969
fukasawa e60969
   for (i=1; i
fukasawa e60969
   {
fukasawa e60969
      if (strcmp(argv[i], "-q") == 0)
fukasawa e60969
         verbose = 0;
fukasawa e60969
fukasawa e60969
      else if (extract_one_file(argv[i]))
fukasawa e60969
         extracted = 1;
fukasawa e60969
   }
fukasawa e60969
fukasawa e60969
   /* Exit code is true if any extract succeeds */
fukasawa e60969
   return extracted == 0;
fukasawa e60969
}
fukasawa e60969
#endif /* READ && STDIO && iCCP */