/* exif-loader.c * * Copyright (c) 2002 Lutz Mueller * * 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #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; struct _ExifLoader { ExifLoaderState state; ExifLoaderDataFormat data_format; /* Small buffer used for detection of format */ unsigned char b[12]; unsigned char b_len; unsigned int size; unsigned char *buf; unsigned int bytes_read; unsigned int ref_count; ExifLog *log; ExifMem *mem; }; 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; } #undef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) 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; 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_log (ExifLoader *loader, ExifLog *log) { if (!loader) return; exif_log_unref (loader->log); loader->log = log; exif_log_ref (log); }