+/* exif-loader.c
+ *
+ * Copyright (c) 2002 Lutz Mueller <lutz@users.sourceforge.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ */
+
#include <config.h>
-#include "exif-loader.h"
+#include <libexif/exif-loader.h>
+#include <libexif/exif-utils.h>
+#include <libexif/i18n.h>
+
+#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
-#include <libjpeg/jpeg-marker.h>
+#undef JPEG_MARKER_DHT
+#define JPEG_MARKER_DHT 0xc4
+#undef JPEG_MARKER_SOI
+#define JPEG_MARKER_SOI 0xd8
+#undef JPEG_MARKER_DQT
+#define JPEG_MARKER_DQT 0xdb
+#undef JPEG_MARKER_APP0
+#define JPEG_MARKER_APP0 0xe0
+#undef JPEG_MARKER_APP1
+#define JPEG_MARKER_APP1 0xe1
+#undef JPEG_MARKER_APP2
+#define JPEG_MARKER_APP2 0xe2
+#undef JPEG_MARKER_APP13
+#define JPEG_MARKER_APP13 0xed
+#undef JPEG_MARKER_COM
+#define JPEG_MARKER_COM 0xfe
typedef enum {
EL_READ = 0,
- EL_READ_SIZE_HIGH_BYTE,
- EL_READ_SIZE_LOW_BYTE,
+ EL_READ_SIZE_BYTE_24,
+ EL_READ_SIZE_BYTE_16,
+ EL_READ_SIZE_BYTE_08,
+ EL_READ_SIZE_BYTE_00,
EL_SKIP_BYTES,
EL_EXIF_FOUND,
- EL_FAILED
} ExifLoaderState;
+typedef enum {
+ EL_DATA_FORMAT_UNKNOWN,
+ EL_DATA_FORMAT_EXIF,
+ EL_DATA_FORMAT_JPEG,
+ EL_DATA_FORMAT_FUJI_RAW
+} ExifLoaderDataFormat;
+
+/*! \internal */
struct _ExifLoader {
ExifLoaderState state;
+ ExifLoaderDataFormat data_format;
+
+ /*! Small buffer used for detection of format */
+ unsigned char b[12];
+
+ /*! Number of bytes in the small buffer \c b */
+ unsigned char b_len;
- int size;
- int last_marker;
+ unsigned int size;
unsigned char *buf;
- int bytes_read;
+ unsigned int bytes_read;
unsigned int ref_count;
+
+ ExifLog *log;
+ ExifMem *mem;
};
-#undef MIN
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+/*! Magic number for EXIF header */
+static const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
+
+static void *
+exif_loader_alloc (ExifLoader *l, unsigned int i)
+{
+ void *d;
+
+ if (!l || !i)
+ return NULL;
+
+ d = exif_mem_alloc (l->mem, i);
+ if (d)
+ return d;
+
+ EXIF_LOG_NO_MEMORY (l->log, "ExifLog", i);
+ return NULL;
+}
+
+void
+exif_loader_write_file (ExifLoader *l, const char *path)
+{
+ FILE *f;
+ int size;
+ unsigned char data[1024];
+
+ if (!l || !path)
+ return;
+
+ f = fopen (path, "rb");
+ if (!f) {
+ exif_log (l->log, EXIF_LOG_CODE_NONE, "ExifLoader",
+ _("The file '%s' could not be opened."), path);
+ return;
+ }
+ while (1) {
+ size = fread (data, 1, sizeof (data), f);
+ if (size <= 0)
+ break;
+ if (!exif_loader_write (l, data, size))
+ break;
+ }
+ fclose (f);
+}
+
+static unsigned int
+exif_loader_copy (ExifLoader *eld, unsigned char *buf, unsigned int len)
+{
+ if (!eld || (len && !buf) || (eld->bytes_read >= eld->size))
+ return 0;
+
+ /* If needed, allocate the buffer. */
+ if (!eld->buf)
+ eld->buf = exif_loader_alloc (eld, eld->size);
+ if (!eld->buf)
+ return 0;
+
+ /* Copy memory */
+ len = MIN (len, eld->size - eld->bytes_read);
+ memcpy (eld->buf + eld->bytes_read, buf, len);
+ eld->bytes_read += len;
+
+ return (eld->bytes_read >= eld->size) ? 0 : 1;
+}
-/* This function imitates code from libexif, written by Lutz
- * Müller. See libexif/exif-data.c:exif_data_new_from_file. Here, it
- * can cope with a sequence of data chunks.
- */
unsigned char
exif_loader_write (ExifLoader *eld, unsigned char *buf, unsigned int len)
{
- int i;
- int len_remain;
+ unsigned int i;
+
+ if (!eld || (len && !buf))
+ return 0;
+
+ switch (eld->state) {
+ case EL_EXIF_FOUND:
+ return exif_loader_copy (eld, buf, len);
+ case EL_SKIP_BYTES:
+ if (eld->size > len) {
+ eld->size -= len;
+ return 1;
+ }
+ len -= eld->size;
+ buf += eld->size;
+ eld->size = 0;
+ eld->b_len = 0;
+ switch (eld->data_format) {
+ case EL_DATA_FORMAT_FUJI_RAW:
+ eld->state = EL_READ_SIZE_BYTE_24;
+ break;
+ default:
+ eld->state = EL_READ;
+ break;
+ }
+ break;
+
+ case EL_READ:
+ default:
+ break;
+ }
+
+ if (!len)
+ return 1;
+ exif_log (eld->log, EXIF_LOG_CODE_DEBUG, "ExifLoader",
+ "Scanning %i byte(s) of data...", len);
+
+ /*
+ * First fill the small buffer. Only continue if the buffer
+ * is filled. Note that EXIF data contains at least 12 bytes.
+ */
+ i = MIN (len, sizeof (eld->b) - eld->b_len);
+ if (i) {
+ memcpy (&eld->b[eld->b_len], buf, i);
+ eld->b_len += i;
+ if (eld->b_len < sizeof (eld->b))
+ return 1;
+ buf += i;
+ len -= i;
+ }
- if (!eld) return 0;
- if (eld->state == EL_FAILED) return 0;
- if (eld->size && eld->bytes_read == eld->size) return 0;
+ switch (eld->data_format) {
+ case EL_DATA_FORMAT_UNKNOWN:
- for (i = 0; (i < len) && (eld->state != EL_EXIF_FOUND) &&
- (eld->state != EL_FAILED); i++) {
+ /* Check the small buffer against known formats. */
+ if (!memcmp (eld->b, "FUJIFILM", 8)) {
+ /* Skip to byte 84. There is another offset there. */
+ eld->data_format = EL_DATA_FORMAT_FUJI_RAW;
+ eld->size = 84;
+ eld->state = EL_SKIP_BYTES;
+ eld->size = 84;
+
+ } else if (!memcmp (eld->b + 2, ExifHeader, sizeof (ExifHeader))) {
+
+ /* Read the size (2 bytes). */
+ eld->data_format = EL_DATA_FORMAT_EXIF;
+ eld->state = EL_READ_SIZE_BYTE_08;
+ }
+ default:
+ break;
+ }
+
+ for (i = 0; i < sizeof (eld->b); i++)
switch (eld->state) {
+ case EL_EXIF_FOUND:
+ if (!exif_loader_copy (eld, eld->b + i,
+ sizeof (eld->b) - i))
+ return 0;
+ return exif_loader_copy (eld, buf, len);
case EL_SKIP_BYTES:
eld->size--;
- if (eld->size == 0) {
+ if (!eld->size)
eld->state = EL_READ;
- }
- break;
-
- case EL_READ_SIZE_HIGH_BYTE:
- eld->size = buf [i] << 8;
- eld->state = EL_READ_SIZE_LOW_BYTE;
break;
-
- case EL_READ_SIZE_LOW_BYTE:
- eld->size |= buf [i];
- switch (eld->last_marker) {
- case JPEG_MARKER_APP0:
+ case EL_READ_SIZE_BYTE_24:
+ eld->size |= eld->b[i] << 24;
+ eld->state = EL_READ_SIZE_BYTE_16;
+ break;
+ case EL_READ_SIZE_BYTE_16:
+ eld->size |= eld->b[i] << 16;
+ eld->state = EL_READ_SIZE_BYTE_08;
+ break;
+ case EL_READ_SIZE_BYTE_08:
+ eld->size |= eld->b[i] << 8;
+ eld->state = EL_READ_SIZE_BYTE_00;
+ break;
+ case EL_READ_SIZE_BYTE_00:
+ eld->size |= eld->b[i] << 0;
+ switch (eld->data_format) {
+ case EL_DATA_FORMAT_JPEG:
eld->state = EL_SKIP_BYTES;
+ eld->size -= 2;
break;
-
- case JPEG_MARKER_APP1:
- eld->state = EL_EXIF_FOUND;
+ case EL_DATA_FORMAT_FUJI_RAW:
+ eld->data_format = EL_DATA_FORMAT_EXIF;
+ eld->state = EL_SKIP_BYTES;
+ eld->size -= 86;
break;
-
- case 0:
- /*
- * Assume that we are reading EXIF data.
- * This should probably be verified by reading
- * some bytes ahead.
- */
+ case EL_DATA_FORMAT_EXIF:
eld->state = EL_EXIF_FOUND;
break;
default:
- return 0;
+ break;
}
-
- eld->last_marker = 0;
break;
default:
- if (buf[i] != 0xff) {
- if (buf [i] == JPEG_MARKER_APP0 ||
- buf [i] == JPEG_MARKER_APP1) {
- eld->state = EL_READ_SIZE_HIGH_BYTE;
- eld->last_marker = buf [i];
-
- } else if (buf [i] == JPEG_MARKER_SOI) {
- /* Nothing */
+ switch (eld->b[i]) {
+ case JPEG_MARKER_APP1:
+ if (!memcmp (eld->b + i + 3, ExifHeader, MIN((ssize_t)(sizeof(ExifHeader)), MAX(0, ((ssize_t)(sizeof(eld->b))) - ((ssize_t)i) - 3)))) {
+ eld->data_format = EL_DATA_FORMAT_EXIF;
} else {
- /* Assume that we are reading EXIF
- * data. This should probably be
- * verified by reading some bytes
- * ahead.
- */
- eld->last_marker = JPEG_MARKER_APP1;
- eld->state = EL_READ_SIZE_HIGH_BYTE;
- i--;
+ eld->data_format = EL_DATA_FORMAT_JPEG; /* Probably JFIF - keep searching for APP1 EXIF*/
}
+ eld->size = 0;
+ eld->state = EL_READ_SIZE_BYTE_08;
+ break;
+ case JPEG_MARKER_DHT:
+ case JPEG_MARKER_DQT:
+ case JPEG_MARKER_APP0:
+ case JPEG_MARKER_APP2:
+ case JPEG_MARKER_APP13:
+ case JPEG_MARKER_COM:
+ eld->data_format = EL_DATA_FORMAT_JPEG;
+ eld->size = 0;
+ eld->state = EL_READ_SIZE_BYTE_08;
+ break;
+ case 0xff:
+ case JPEG_MARKER_SOI:
+ break;
+ default:
+ exif_log (eld->log,
+ EXIF_LOG_CODE_CORRUPT_DATA,
+ "ExifLoader", _("The data supplied "
+ "does not seem to contain "
+ "EXIF data."));
+ exif_loader_reset (eld);
+ return 0;
}
}
- }
-
- len_remain = len - i;
- if (!len_remain) return 1;
- if (eld->state == EL_EXIF_FOUND && len_remain > 0) {
- if (eld->buf == NULL) {
- eld->buf = malloc (sizeof (unsigned char) * eld->size);
- eld->bytes_read = 0;
- }
-
- if (eld->bytes_read < eld->size) {
- int cp_len;
+ /*
+ * If we reach this point, the buffer has not been big enough
+ * to read all data we need. Fill it with new data.
+ */
+ eld->b_len = 0;
+ return exif_loader_write (eld, buf, len);
+}
- /* the number of bytes we need to copy */
- cp_len = MIN (eld->size - eld->bytes_read, len_remain);
-
- if ((cp_len + eld->bytes_read) > eld->size) return 1;
+ExifLoader *
+exif_loader_new (void)
+{
+ ExifMem *mem = exif_mem_new_default ();
+ ExifLoader *l = exif_loader_new_mem (mem);
- /* Copy memory */
- memcpy (eld->buf + eld->bytes_read, &buf[i], cp_len);
- eld->bytes_read += cp_len;
- }
- }
+ exif_mem_unref (mem);
- return 1;
+ return l;
}
ExifLoader *
-exif_loader_new (void)
+exif_loader_new_mem (ExifMem *mem)
{
- ExifLoader *loader = malloc (sizeof (ExifLoader));
+ ExifLoader *loader;
- memset (loader, 0, sizeof (ExifLoader));
+ if (!mem)
+ return NULL;
+ loader = exif_mem_alloc (mem, sizeof (ExifLoader));
+ if (!loader)
+ return NULL;
+ loader->ref_count = 1;
+
+ loader->mem = mem;
+ exif_mem_ref (mem);
+
return loader;
}
void
exif_loader_ref (ExifLoader *loader)
{
- if (loader) loader->ref_count++;
+ if (loader)
+ loader->ref_count++;
}
+static void
+exif_loader_free (ExifLoader *loader)
+{
+ ExifMem *mem;
+
+ if (!loader)
+ return;
+
+ mem = loader->mem;
+ exif_loader_reset (loader);
+ exif_log_unref (loader->log);
+ exif_mem_free (mem, loader);
+ exif_mem_unref (mem);
+}
+
void
exif_loader_unref (ExifLoader *loader)
{
- if (!loader) return;
- if (!--loader->ref_count) {
- exif_loader_reset (loader);
- free (loader);
- }
+ if (!loader)
+ return;
+ if (!--loader->ref_count)
+ exif_loader_free (loader);
}
void
exif_loader_reset (ExifLoader *loader)
{
- if (!loader) return;
- free (loader->buf); loader->buf = NULL;
+ if (!loader)
+ return;
+ exif_mem_free (loader->mem, loader->buf); loader->buf = NULL;
loader->size = 0;
loader->bytes_read = 0;
- loader->last_marker = 0;
loader->state = 0;
+ loader->b_len = 0;
+ loader->data_format = EL_DATA_FORMAT_UNKNOWN;
}
ExifData *
exif_loader_get_data (ExifLoader *loader)
{
- return exif_data_new_from_data (loader->buf, loader->bytes_read);
+ ExifData *ed;
+
+ if (!loader || (loader->data_format == EL_DATA_FORMAT_UNKNOWN) ||
+ !loader->bytes_read)
+ return NULL;
+
+ ed = exif_data_new_mem (loader->mem);
+ exif_data_log (ed, loader->log);
+ exif_data_load_data (ed, loader->buf, loader->bytes_read);
+
+ return ed;
+}
+
+void
+exif_loader_get_buf (ExifLoader *loader, const unsigned char **buf,
+ unsigned int *buf_size)
+{
+ const unsigned char* b = NULL;
+ unsigned int s = 0;
+
+ if (loader) {
+ if (loader->data_format == EL_DATA_FORMAT_UNKNOWN) {
+ exif_log (loader->log, EXIF_LOG_CODE_DEBUG, "ExifLoader",
+ "Loader format unknown");
+ } else {
+ b = loader->buf;
+ s = loader->bytes_read;
+ }
+ }
+
+ if (buf)
+ *buf = b;
+ if (buf_size)
+ *buf_size = s;
+}
+
+void
+exif_loader_log (ExifLoader *loader, ExifLog *log)
+{
+ if (!loader)
+ return;
+ exif_log_unref (loader->log);
+ loader->log = log;
+ exif_log_ref (log);
}