435 lines
9.1 KiB
C
435 lines
9.1 KiB
C
|
/* 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 <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>
|
||
|
|
||
|
#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_BYTE_24,
|
||
|
EL_READ_SIZE_BYTE_16,
|
||
|
EL_READ_SIZE_BYTE_08,
|
||
|
EL_READ_SIZE_BYTE_00,
|
||
|
EL_SKIP_BYTES,
|
||
|
EL_EXIF_FOUND,
|
||
|
} 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;
|
||
|
|
||
|
unsigned int size;
|
||
|
unsigned char *buf;
|
||
|
unsigned int bytes_read;
|
||
|
|
||
|
unsigned int ref_count;
|
||
|
|
||
|
ExifLog *log;
|
||
|
ExifMem *mem;
|
||
|
};
|
||
|
|
||
|
/*! 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)
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
unsigned char
|
||
|
exif_loader_write (ExifLoader *eld, unsigned char *buf, unsigned int len)
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
switch (eld->data_format) {
|
||
|
case EL_DATA_FORMAT_UNKNOWN:
|
||
|
|
||
|
/* 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)
|
||
|
eld->state = EL_READ;
|
||
|
break;
|
||
|
|
||
|
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 EL_DATA_FORMAT_FUJI_RAW:
|
||
|
eld->data_format = EL_DATA_FORMAT_EXIF;
|
||
|
eld->state = EL_SKIP_BYTES;
|
||
|
eld->size -= 86;
|
||
|
break;
|
||
|
case EL_DATA_FORMAT_EXIF:
|
||
|
eld->state = EL_EXIF_FOUND;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
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 {
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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);
|
||
|
}
|
||
|
|
||
|
ExifLoader *
|
||
|
exif_loader_new (void)
|
||
|
{
|
||
|
ExifMem *mem = exif_mem_new_default ();
|
||
|
ExifLoader *l = exif_loader_new_mem (mem);
|
||
|
|
||
|
exif_mem_unref (mem);
|
||
|
|
||
|
return l;
|
||
|
}
|
||
|
|
||
|
ExifLoader *
|
||
|
exif_loader_new_mem (ExifMem *mem)
|
||
|
{
|
||
|
ExifLoader *loader;
|
||
|
|
||
|
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++;
|
||
|
}
|
||
|
|
||
|
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_free (loader);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
exif_loader_reset (ExifLoader *loader)
|
||
|
{
|
||
|
if (!loader)
|
||
|
return;
|
||
|
exif_mem_free (loader->mem, loader->buf); loader->buf = NULL;
|
||
|
loader->size = 0;
|
||
|
loader->bytes_read = 0;
|
||
|
loader->state = 0;
|
||
|
loader->b_len = 0;
|
||
|
loader->data_format = EL_DATA_FORMAT_UNKNOWN;
|
||
|
}
|
||
|
|
||
|
ExifData *
|
||
|
exif_loader_get_data (ExifLoader *loader)
|
||
|
{
|
||
|
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 || (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);
|
||
|
}
|