4

I'm trying to use libexif to write exif data into a jpeg file. I'm pretty much following the example provided with it. I can understand all the Exif data blocks being written and everything, but how do I load the jpeg data like they suggested? (using libjpeg). I couldn't find a function that would directly let me import the jpeg as char. My code works fine if I include a dummy jpg with it like shown below.

/*
 * write-exif.c
 *
 * Placed into the public domain by Daniel Fandrich
 *
 * Create a new EXIF data block and write it into a JPEG image file.
 *
 * The JPEG image data used in this example is fixed and is guaranteed not
 * to contain an EXIF tag block already, so it is easy to precompute where
 * in the file the EXIF data should be. In real life, a library like
 * libjpeg (included with the exif command-line tool source code) would
 * be used to write to an existing JPEG file.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <libexif/exif-data.h>

/* this file will be unilaterally overwritten */
#define FILE_NAME "write-exif.jpg"

/* raw JPEG image data */
static const unsigned char image_jpg[] = {
  0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
  0x01, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xff, 0xdb, 0x00, 0x43,
  0x00, 0x14, 0x0e, 0x0f, 0x12, 0x0f, 0x0d, 0x14, 0x12, 0x10, 0x12, 0x17,
  0x15, 0x14, 0x18, 0x1e, 0x32, 0x21, 0x1e, 0x1c, 0x1c, 0x1e, 0x3d, 0x2c,
  0x2e, 0x24, 0x32, 0x49, 0x40, 0x4c, 0x4b, 0x47, 0x40, 0x46, 0x45, 0x50,
  0x5a, 0x73, 0x62, 0x50, 0x55, 0x6d, 0x56, 0x45, 0x46, 0x64, 0x88, 0x65,
  0x6d, 0x77, 0x7b, 0x81, 0x82, 0x81, 0x4e, 0x60, 0x8d, 0x97, 0x8c, 0x7d,
  0x96, 0x73, 0x7e, 0x81, 0x7c, 0xff, 0xc0, 0x00, 0x0b, 0x08, 0x00, 0x40,
  0x00, 0x40, 0x01, 0x01, 0x11, 0x00, 0xff, 0xc4, 0x00, 0x1b, 0x00, 0x00,
  0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x04, 0x03, 0x07, 0x02, 0x01, 0xff,
  0xc4, 0x00, 0x2f, 0x10, 0x00, 0x01, 0x03, 0x03, 0x02, 0x05, 0x03, 0x03,
  0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x11,
  0x00, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x81, 0x61, 0x71,
  0x91, 0x13, 0x32, 0xa1, 0x14, 0x22, 0xc1, 0x15, 0x23, 0x52, 0xd1, 0xf0,
  0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xb3, 0xa2,
  0x8a, 0x28, 0xa2, 0x8a, 0x28, 0xa2, 0x97, 0x64, 0x72, 0xd6, 0x58, 0xd4,
  0x8f, 0xd5, 0x3d, 0xca, 0xa5, 0x7d, 0xa8, 0x4e, 0xaa, 0x3e, 0xb0, 0x3a,
  0x7a, 0x9d, 0x2b, 0x2d, 0x87, 0x13, 0xe3, 0xaf, 0x9f, 0x0c, 0xb6, 0xb5,
  0xb6, 0xe2, 0xb4, 0x48, 0x71, 0x30, 0x14, 0x7b, 0x02, 0x09, 0x14, 0xee,
  0x94, 0x64, 0x38, 0x87, 0x1f, 0x8d, 0x5f, 0xd3, 0x7d, 0xe2, 0xa7, 0x46,
  0xe8, 0x6c, 0x49, 0x1e, 0xfd, 0x07, 0xb1, 0x33, 0x5f, 0xb8, 0xdc, 0xf5,
  0x96, 0x51, 0x7c, 0x96, 0xee, 0x14, 0xba, 0x04, 0xf2, 0x2c, 0x42, 0xa3,
  0xd3, 0x70, 0x7c, 0x1a, 0x6d, 0x45, 0x2c, 0xcd, 0xe5, 0x11, 0x89, 0xb1,
  0x5b, 0xc4, 0x02, 0xe1, 0xd1, 0xb4, 0xcf, 0xdc, 0xa3, 0xfc, 0x0d, 0xcd,
  0x41, 0xde, 0xe3, 0xb2, 0x2e, 0xda, 0x1c, 0xbd, 0xe2, 0x4a, 0x90, 0xe9,
  0x04, 0x95, 0x2b, 0xf7, 0x41, 0xd0, 0x18, 0xe8, 0x36, 0x03, 0xc6, 0x91,
  0x4a, 0x81, 0x20, 0x82, 0x24, 0x10, 0x66, 0x45, 0x5a, 0xdd, 0xf1, 0x32,
  0xff, 0x00, 0xa2, 0x5a, 0x8b, 0x62, 0x57, 0x7d, 0x70, 0x80, 0x93, 0x1a,
  0x94, 0x10, 0x79, 0x4a, 
};

/* length of data in image_jpg */
static const unsigned int image_jpg_len = sizeof(image_jpg);

/* dimensions of image */
static const unsigned int image_jpg_x = 64;
static const unsigned int image_jpg_y = 64;

/* start of JPEG image data section */
static const unsigned int image_data_offset = 20;
#define image_data_len (image_jpg_len - image_data_offset)

/* raw EXIF header data */
static const unsigned char exif_header[] = {
  0xff, 0xd8, 0xff, 0xe1
};
/* length of data in exif_header */
static const unsigned int exif_header_len = sizeof(exif_header);

/* byte order to use in the EXIF block */
#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;
    FILE *f;
    unsigned char *exif_data;
    unsigned int exif_data_len;
    ExifEntry *entry;
    ExifData *exif = exif_data_new();
    if (!exif) {
        fprintf(stderr, "Out of memory\n");
        return 2;
    }

    /* 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, 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, FILE_BYTE_ORDER, image_jpg_x);

    entry = init_tag(exif, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION);
    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, FILE_BYTE_ORDER, 1);

    /* 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 creating file %s\n", FILE_NAME);
        exif_data_unref(exif);
        return rc;
    }
    /* Write EXIF header */
    if (fwrite(exif_header, exif_header_len, 1, f) != 1) {
        fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
        goto errout;
    }
    /* 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;
    }
    if (fputc((exif_data_len+2) & 0xff, f) < 0) {
        fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
        goto errout;
    }
    /* Write EXIF data block */
    if (fwrite(exif_data, exif_data_len, 1, f) != 1) {
        fprintf(stderr, "Error writing to file %s\n", FILE_NAME);
        goto errout;
    }
    /* 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;
    }
    printf("Wrote file %s\n", FILE_NAME);
    rc = 0;

errout:
    if (fclose(f)) {
        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;
}
tsf144
  • 817
  • 1
  • 8
  • 14

1 Answers1

-1

You can just use the fopen function to load the specific jpg. and then write the exif data into it.

Nicholas
  • 11
  • 3
  • The EXIF data must be written to a specific location in the file but your answer doesn't explain this in any detail. – Nathan Osman Sep 30 '17 at 05:24
  • The libexif has related APIs to put the EXIF data to specific location, you just need to fill in the EXIF information. If you want know the lay out of the EXIF file, you can reference the EXIF doc. – Nicholas Oct 12 '17 at 02:34
  • Libexif has no API to save exif data to an existing image. – Remember Monica Dec 05 '21 at 03:10