*/
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <assert.h>
#include <libexif/exif-data.h>
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;
/* 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);
/* 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) {
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;
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;
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;