Re: EXIF support in PNG

John Bowler <[email protected]>
Newsgroups gmane.comp.graphics.png.general
Message-ID <CAP7U39-Pds8vP17CsSH43h0tzRj2NUjsFqe-X+a06+fu8=YT_w@mail.gmail.com>
Ok, so Glenn and Cosmin are saying "it's easy", but Cosmin's easy code
doesn't work and Glenn is apparently saying "write a complete
program"; something which I don't regard as easy.

THE CHALLENGE: PROVE IT

libexif is widely available and certainly my EXIF library of choice,
given EXIF data in memory it requires one function call.  libpng 1.2
is still widely used.  Given libexif and a PNG loaded as
{png_struct,png_info} please write the C code (it's simple, right,
only three function calls!) to return an (ExifData*) loaded with the
EXIF data from the PNG, assuming it is formated the way Glenn has
specified (compression method byte then deflate data).

To make this easy I've written the code to find the unknown chunk;
that's all that is required (apart from the one libexif call) with
*MY* proposal.  In other words once you have the png_unknown_structp
from libpng ZERO extra work is required.

The code is attached.  It compiles without warnings using:

gcc -DHEADER_LOC='"path-to-1.2/png.h"' -Wall -Wextra -c exif-example.c

All Glenn and/or Cosmin need to do is fill in the "simple" bit that
handles the compressed version.  It isn't even necessary to build
libpng 1.2 to compile it.

The attached file is well commented, but for those who don't care to
read comments the complete implementation including *ALL* error
handling and *ALL* security checks is:

#include <stdlib.h>
#include <libexif/exif-data.h>
#include <libpng12/png.h>

ExifData *exif_data_new_from_libpng(png_structp png_ptr,
    png_infop info_ptr) {
    png_unknown_chunkp entries = NULL;
    png_uint_32 count =
        png_get_unknown_chunks(png_ptr, info_ptr, &entries);

    if (count > 0 && entries != NULL) {
        png_uint_32 i;

        for (i=0; i<count; ++i) {
            static png_byte name[4] = { 101, 88, 73, 102 };
            if (memcmp(entries[i].name, name, 4) == 0 /* EXIF */ &&
                entries[i].size <= (unsigned int)-1)
                return exif_data_new_from_data(entries[i].data,
                        entries[i].size);
            }
    }

    /* Here if there is no EXIF data or it is too big for libexif: */
    return NULL;
}

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most 
engaging tech sites, SlashDot.org! http://sdm.link/slashdot

_______________________________________________
png-mng-misc mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/png-mng-misc
exif-example.c (text/x-csrc, 2.9 KB)
/* To compile this code if libpng 1.2 is not installed:
 *
 * gcc -DHEADER_LOC='"path-to-1.2/png.h"' -Wall -Wextra -c exif-example.c
 *
 * I.e. 'HEADER_LOC' must be defined to the appropriate string for a #include
 * directive; see the default below.  libpng 1.2 does not actually have to be
 * build, all that is needed is the png.h header file.
 */
#include <libexif/exif-data.h>

/* The following function definitions are contained in the above file:
 */
/*! Allocate a new #ExifData and load EXIF data from a JPEG file.
 * Uses an #ExifLoader internally to do the loading.
 *
 * \param[in] path filename including path
 * \return allocated #ExifData, or NULL on error
 */
ExifData *exif_data_new_from_file (const char *path);

/*! Allocate a new #ExifData and load EXIF data from a memory buffer.
 *
 * \param[in] data pointer to raw JPEG or EXIF data
 * \param[in] size number of bytes of data at data
 * \return allocated #ExifData, or NULL on error
 */
ExifData *exif_data_new_from_data (const unsigned char *data,
				   unsigned int size);

#ifndef HEADER_LOC
#   define HEADER_LOC <libpng12/png.h>
#endif
#include HEADER_LOC

/* The following definitions contained in the above file: */
/* png_unknown_chunk is a structure to hold queued chunks for which there is
 * no specific support.  The idea is that we can use this to queue
 * up private chunks for output even though the library doesn't actually
 * know about their semantics.
 */
#ifndef PNG_H
#define PNG_CHUNK_NAME_LENGTH 5
typedef struct png_unknown_chunk_t
{
    png_byte name[PNG_CHUNK_NAME_LENGTH];
    png_byte *data;
    png_size_t size;

    /* libpng-using applications should NOT directly modify this byte. */
    png_byte location; /* mode of operation at read time */
}
png_unknown_chunk;
typedef png_unknown_chunk FAR * png_unknown_chunkp;
typedef png_unknown_chunk FAR * FAR * png_unknown_chunkpp;
#endif /* commented out */
/* ... */
extern PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp
   png_ptr, png_infop info_ptr, png_unknown_chunkpp entries));

/* THE CHALLENGE:
 *
 * Given existing code that uses libpng12 to load a PNG into memory as a
 * png_structp and png_infop implement a function to use libexif to produce an
 * ExifData containing EXIF data from the PNG (if it is there.)
 *
 * John Bowler's solution:
 */
#include <stdlib.h>

ExifData *exif_data_new_from_libpng(png_structp png_ptr, png_infop info_ptr) {
    png_unknown_chunkp entries = NULL;
    png_uint_32 count = png_get_unknown_chunks(png_ptr, info_ptr, &entries);

    if (count > 0 && entries != NULL) {
        png_uint_32 i;

        for (i=0; i<count; ++i) {
            static png_byte name[4] = { 101, 88, 73, 102 };
            if (memcmp(entries[i].name, name, 4) == 0 /* EXIF */ &&
                entries[i].size <= (unsigned int)-1)
                return exif_data_new_from_data(entries[i].data,
                        entries[i].size);
            }
    }

    /* Here if there is no EXIF data: */
    return NULL;
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.