Fixed some memory leaks in the write-exif.c example program and
authorDan Fandrich <dan@coneharvesters.com>
Wed, 9 Dec 2009 06:17:19 +0000 (22:17 -0800)
committerDan Fandrich <dan@coneharvesters.com>
Wed, 9 Dec 2009 06:17:19 +0000 (22:17 -0800)
added some examples of allocating a new tag.

ChangeLog
contrib/examples/write-exif.c

index b1e5de6..59774e4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2009-12-08  Dan Fandrich <dan@coneharvesters.com>
+
+       * Fixed some memory leaks in the write-exif.c example program and
+         added some examples of allocating a new tag.
+
 2009-11-27  Dan Fandrich <dan@coneharvesters.com>
 
        * po/ja.po: Updated Japanese translation by Tadashi Jokagi
index 8c5739b..894cdb6 100644 (file)
@@ -13,6 +13,8 @@
  */
 
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <assert.h>
 #include <libexif/exif-data.h>
 
@@ -114,21 +116,83 @@ static const unsigned char exif_header[] = {
 static const unsigned int exif_header_len = sizeof(exif_header);
 
 /* byte order to use in the EXIF block */
-#define BYTE_ORDER EXIF_BYTE_ORDER_INTEL
+#define FILE_BYTE_ORDER EXIF_BYTE_ORDER_INTEL
+
+/* comment to write into the EXIF block */
+#define FILE_COMMENT "libexif demonstration image"
+
+/* special header required for EXIF_TAG_USER_COMMENT */
+#define ASCII_COMMENT "ASCII\0\0\0"
+
 
 /* Get an existing tag, or create one if it doesn't exist */
 static ExifEntry *init_tag(ExifData *exif, ExifIfd ifd, ExifTag tag)
 {
        ExifEntry *entry;
+       /* Return an existing tag if one exists */
        if (!((entry = exif_content_get_entry (exif->ifd[ifd], tag)))) {
+           /* Allocate a new entry */
            entry = exif_entry_new ();
            assert(entry != NULL); /* catch an out of memory condition */
+           entry->tag = tag; /* tag must be set before calling
+                                exif_content_add_entry */
+
+           /* Attach the ExifEntry to an IFD */
            exif_content_add_entry (exif->ifd[ifd], entry);
+
+           /* Allocate memory for the entry and fill with default data */
            exif_entry_initialize (entry, tag);
+
+           /* Ownership of the ExifEntry has now been passed to the IFD.
+            * One must be very careful in accessing a structure after
+            * unref'ing it; in this case, we know "entry" won't be freed
+            * because the reference count was bumped when it was added to
+            * the IFD.
+            */
+           exif_entry_unref(entry);
        }
        return entry;
 }
 
+/* Create a brand-new tag with a data field of the given length, in the
+ * given IFD. This is needed when exif_entry_initialize() isn't able to create
+ * this type of tag itself, or the default data length it creates isn't the
+ * correct length.
+ */
+static ExifEntry *create_tag(ExifData *exif, ExifIfd ifd, ExifTag tag, size_t len)
+{
+       void *buf;
+       ExifEntry *entry;
+       
+       /* Create a memory allocator to manage this ExifEntry */
+       ExifMem *mem = exif_mem_new_default();
+       assert(mem != NULL); /* catch an out of memory condition */
+
+       /* Create a new ExifEntry using our allocator */
+       entry = exif_entry_new_mem (mem);
+       assert(entry != NULL);
+
+       /* Allocate memory to use for holding the tag data */
+       buf = exif_mem_alloc(mem, len);
+       assert(buf != NULL);
+
+       /* Fill in the entry */
+       entry->data = buf;
+       entry->size = len;
+       entry->tag = tag;
+       entry->components = len;
+       entry->format = EXIF_FORMAT_UNDEFINED;
+
+       /* Attach the ExifEntry to an IFD */
+       exif_content_add_entry (exif->ifd[ifd], entry);
+
+       /* The ExifMem and ExifEntry are now owned elsewhere */
+       exif_mem_unref(mem);
+       exif_entry_unref(entry);
+
+       return entry;
+}
+
 int main(int argc, char **argv)
 {
        int rc = 1;
@@ -145,7 +209,7 @@ int main(int argc, char **argv)
        /* Set the image options */
        exif_data_set_option(exif, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
        exif_data_set_data_type(exif, EXIF_DATA_TYPE_COMPRESSED);
-       exif_data_set_byte_order(exif, BYTE_ORDER);
+       exif_data_set_byte_order(exif, FILE_BYTE_ORDER);
 
        /* Create the mandatory EXIF fields with default data */
        exif_data_fix(exif);
@@ -153,22 +217,41 @@ int main(int argc, char **argv)
        /* All these tags are created with default values by exif_data_fix() */
        /* Change the data to the correct values for this image. */
        entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION);
-       exif_set_long(entry->data, BYTE_ORDER, image_jpg_x);
+       exif_set_long(entry->data, FILE_BYTE_ORDER, image_jpg_x);
 
        entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION);
-       exif_set_long(entry->data, BYTE_ORDER, image_jpg_y);
+       exif_set_long(entry->data, FILE_BYTE_ORDER, image_jpg_y);
 
        entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_COLOR_SPACE);
-       exif_set_short(entry->data, BYTE_ORDER, 1);
+       exif_set_short(entry->data, FILE_BYTE_ORDER, 1);
 
-       /* A good example to give here would be to add a new tag that isn't
-        * automatically created by exif_data_fix() (such as
-        * EXIF_TAG_USER_COMMENT) which requires allocating memory in
-        * entry->data. This isn't so easy to do, unfortunately.
+       /* Create a EXIF_TAG_USER_COMMENT tag. This one must be handled
+        * differently because that tag isn't automatically created and
+        * allocated by exif_data_fix(), nor can it be created using
+        * exif_entry_initialize() so it must be explicitly allocated here.
         */
+       entry = create_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_USER_COMMENT, 
+                       sizeof(ASCII_COMMENT) + sizeof(FILE_COMMENT) - 2);
+       /* Write the special header needed for a comment tag */
+       memcpy(entry->data, ASCII_COMMENT, sizeof(ASCII_COMMENT)-1);
+       /* Write the actual comment text, without the trailing NUL character */
+       memcpy(entry->data+8, FILE_COMMENT, sizeof(FILE_COMMENT)-1);
+       /* create_tag() happens to set the format and components correctly for
+        * EXIF_TAG_USER_COMMENT, so there is nothing more to do. */
+
+       /* Create a EXIF_TAG_SUBJECT_AREA tag */
+       entry = create_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_SUBJECT_AREA,
+                          4 * exif_format_get_size(EXIF_FORMAT_SHORT));
+       entry->format = EXIF_FORMAT_SHORT;
+       entry->components = 4;
+       exif_set_short(entry->data, FILE_BYTE_ORDER, image_jpg_x / 2);
+       exif_set_short(entry->data+2, FILE_BYTE_ORDER, image_jpg_y / 2);
+       exif_set_short(entry->data+4, FILE_BYTE_ORDER, image_jpg_x);
+       exif_set_short(entry->data+6, FILE_BYTE_ORDER, image_jpg_y);
 
        /* Get a pointer to the EXIF data block we just created */
        exif_data_save_data(exif, &exif_data, &exif_data_len);
+       assert(exif_data != NULL);
 
        f = fopen(FILE_NAME, "wb");
        if (!f) {
@@ -181,7 +264,7 @@ int main(int argc, char **argv)
                fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
                goto errout;
        }
-       /* Write EXIF block length */
+       /* Write EXIF block length in big-endian order */
        if (fputc((exif_data_len+2) >> 8, f) < 0) {
                fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
                goto errout;
@@ -195,7 +278,7 @@ int main(int argc, char **argv)
                fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
                goto errout;
        }
-       /* Write JPEG image data */
+       /* Write JPEG image data, skipping the non-EXIF header */
        if (fwrite(image_jpg+image_data_offset, image_data_len, 1, f) != 1) {
                fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
                goto errout;
@@ -208,6 +291,10 @@ errout:
                fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
                rc = 1;
        }
+       /* The allocator we're using for ExifData is the standard one, so use
+        * it directly to free this pointer.
+        */
+       free(exif_data);
        exif_data_unref(exif);
 
        return rc;