📄 exif.c
字号:
} } else { spacer = ""; } g_string_append_printf(string, "%s%02x", spacer, ((char *)data)[i]); } if (i >= UNDEFINED_TEXT_BYTE_COUNT) g_string_append_printf(string, " (%d bytes)", ne); return string;}gchar *exif_text_list_find_value(ExifTextList *list, guint value){ gchar *result = NULL; gint i; i = 0; while (!result && list[i].value >= 0) { if (value == list[i].value) result = g_strdup(_(list[i].description)); i++; } if (!result) result = g_strdup_printf("%d (%s)", value, _("unknown")); return result;}/* *------------------------------------------------------------------- * byte size utils *------------------------------------------------------------------- */guint16 exif_byte_get_int16(unsigned char *f, ExifByteOrder bo){ if (bo == EXIF_BYTE_ORDER_INTEL) return GUINT16_FROM_LE(*(guint16*)f); else return GUINT16_FROM_BE(*(guint16*)f);}guint32 exif_byte_get_int32(unsigned char *f, ExifByteOrder bo){ if (bo == EXIF_BYTE_ORDER_INTEL) return GUINT32_FROM_LE(*(guint32*)f); else return GUINT32_FROM_BE(*(guint32*)f);}guint16 exif_byte_swab_int16(guint16 n, ExifByteOrder bo){#if G_BYTE_ORDER == G_LITTLE_ENDIAN if (bo == EXIF_BYTE_ORDER_MOTOROLA)#else if (bo == EXIF_BYTE_ORDER_INTEL)#endif return GUINT16_SWAP_LE_BE(n); else return n;}guint32 exif_byte_swab_int32(guint32 n, ExifByteOrder bo){#if G_BYTE_ORDER == G_LITTLE_ENDIAN if (bo == EXIF_BYTE_ORDER_MOTOROLA)#else if (bo == EXIF_BYTE_ORDER_INTEL)#endif return GUINT32_SWAP_LE_BE(n); else return n;}/* *------------------------------------------------------------------- * IFD utils *------------------------------------------------------------------- */static const ExifMarker *exif_marker_from_tag(guint16 tag, const ExifMarker *list){ gint i = 0; if (!list) return NULL; while (list[i].tag != 0 && list[i].tag != tag) { i++; } return (list[i].tag == 0 ? NULL : &list[i]);}static void rational_from_data(ExifRational *r, void *src, ExifByteOrder bo){ r->num = exif_byte_get_int32(src, bo); r->den = exif_byte_get_int32(src + sizeof(guint32), bo);}/* src_format and item->format must be compatible * and not overrun src or item->data. */void exif_item_copy_data(ExifItem *item, void *src, guint len, ExifFormatType src_format, ExifByteOrder bo){ gint bs; gint ne; gpointer dest; gint i; bs = ExifFormatList[item->format].size; ne = item->elements; dest = item->data; if (!dest || ExifFormatList[src_format].size * ne > len) { printf("exif tag %s data size mismatch\n", exif_item_get_tag_name(item)); return; } switch (item->format) { case EXIF_FORMAT_UNKNOWN: break; case EXIF_FORMAT_BYTE_UNSIGNED: case EXIF_FORMAT_BYTE: case EXIF_FORMAT_UNDEFINED: memcpy(dest, src, len); break; case EXIF_FORMAT_STRING: memcpy(dest, src, len); /* string is NULL terminated, make sure this is true */ if (((char *)dest)[len - 1] != '\0') ((char *)dest)[len - 1] = '\0'; break; case EXIF_FORMAT_SHORT_UNSIGNED: case EXIF_FORMAT_SHORT: for (i = 0; i < ne; i++) { ((guint16 *)dest)[i] = exif_byte_get_int16(src + i * bs, bo); } break; case EXIF_FORMAT_LONG_UNSIGNED: case EXIF_FORMAT_LONG: if (src_format == EXIF_FORMAT_SHORT_UNSIGNED || src_format == EXIF_FORMAT_SHORT) { /* a short fits into a long, so allow it */ gint ss; ss = ExifFormatList[src_format].size; for (i = 0; i < ne; i++) { ((gint32 *)dest)[i] = (gint32)exif_byte_get_int16(src + i * ss, bo); } } else { for (i = 0; i < ne; i++) { ((gint32 *)dest)[i] = exif_byte_get_int32(src + i * bs, bo); } } break; case EXIF_FORMAT_RATIONAL_UNSIGNED: case EXIF_FORMAT_RATIONAL: for (i = 0; i < ne; i++) { rational_from_data(&((ExifRational *)dest)[i], src + i * bs, bo); } break; case EXIF_FORMAT_FLOAT: for (i = 0; i < ne; i++) { ((float *)dest)[i] = exif_byte_get_int32(src + i * bs, bo); } break; case EXIF_FORMAT_DOUBLE: for (i = 0; i < ne; i++) { ExifRational r; rational_from_data(&r, src + i * bs, bo); if (r.den) ((double *)dest)[i] = (double)r.num / r.den; } break; }}static gint exif_parse_IFD_entry(ExifData *exif, unsigned char *tiff, guint offset, guint size, ExifByteOrder bo, gint level, const ExifMarker *list){ guint tag; guint format; guint count; guint data_val; guint data_offset; guint data_length; const ExifMarker *marker; ExifItem *item; tag = exif_byte_get_int16(tiff + offset + EXIF_TIFD_OFFSET_TAG, bo); format = exif_byte_get_int16(tiff + offset + EXIF_TIFD_OFFSET_FORMAT, bo); count = exif_byte_get_int32(tiff + offset + EXIF_TIFD_OFFSET_COUNT, bo); data_val = exif_byte_get_int32(tiff + offset + EXIF_TIFD_OFFSET_DATA, bo); /* Check tag type. If it does not match, either the format is wrong, * either it is a unknown tag; so it is not really an error. */ marker = exif_marker_from_tag(tag, list); if (!marker) { if (format >= EXIF_FORMAT_COUNT) { printf("warning: exif tag 0x%4x has invalid format %d\n", tag, format); return 0; } /* allow non recognized tags to be displayed */ marker = &ExifUnknownMarkersList[format]; } if (marker->format != format) { /* Some cameras got mixed up signed/unsigned_rational * eg KODAK DC4800 on object_distance tag * * FIXME: what exactly is this test trying to do? * ok, so this test is to allow the case of swapped signed/unsigned mismatch to leak through? */ if (!(marker->format == EXIF_FORMAT_RATIONAL_UNSIGNED && format == EXIF_FORMAT_RATIONAL) && !(marker->format == EXIF_FORMAT_RATIONAL && format == EXIF_FORMAT_RATIONAL_UNSIGNED) && /* short fits into a long so allow this mismatch * as well (some tags allowed to be unsigned short _or_ unsigned long) */ !(marker->format == EXIF_FORMAT_LONG_UNSIGNED && format == EXIF_FORMAT_SHORT_UNSIGNED) ) { if (format < EXIF_FORMAT_COUNT) { printf("warning: exif tag %s format mismatch, found %s exif spec requests %s\n", marker->key, ExifFormatList[format].short_name, ExifFormatList[marker->format].short_name); } else { printf("warning: exif tag %s format mismatch, found unknown id %d exif spec requests %d (%s)\n", marker->key, format, marker->format, ExifFormatList[marker->format].short_name); } return 0; } } /* Where is the data, is it available? */ if (marker->components > 0 && marker->components != count) { printf("warning: exif tag %s has %d elements, exif spec requests %d\n", marker->key, count, marker->components); } data_length = ExifFormatList[marker->format].size * count; if (data_length > 4) { data_offset = data_val; if (size < data_offset + data_length) { printf("warning: exif tag %s data will overrun end of file, ignored.\n", marker->key); return -1; } } else { data_offset = offset + EXIF_TIFD_OFFSET_DATA; } item = exif_item_new(marker->format, tag, count, marker); exif_item_copy_data(item, tiff + data_offset, data_length, format, bo); exif->items = g_list_prepend(exif->items, item); if (list == ExifKnownMarkersList) { switch (item->tag) { case TAG_EXIFOFFSET: exif_parse_IFD_table(exif, tiff, data_val, size, bo, level + 1, list); break; case TAG_EXIFMAKERNOTE: format_exif_makernote_parse(exif, tiff, data_val, size, bo); break; } } return 0;}gint exif_parse_IFD_table(ExifData *exif, unsigned char *tiff, guint offset, guint size, ExifByteOrder bo, gint level, const ExifMarker *list){ guint count; guint i; /* limit damage from infinite loops */ if (level > EXIF_TIFF_MAX_LEVELS) return -1; /* We should be able to read number of entries in IFD0) */ if (size < offset + 2) return -1; count = exif_byte_get_int16(tiff + offset, bo); offset += 2; /* Entries and next IFD offset must be readable */ if (size < offset + count * EXIF_TIFD_SIZE + 4) return -1; for (i = 0; i < count; i++) { exif_parse_IFD_entry(exif, tiff, offset + i * EXIF_TIFD_SIZE, size, bo, level, list); } return 0;}/* *------------------------------------------------------------------- * file formats *------------------------------------------------------------------- */gint exif_tiff_directory_offset(unsigned char *data, const guint len, guint *offset, ExifByteOrder *bo){ if (len < 8) return FALSE; if (memcmp(data, "II", 2) == 0) { *bo = EXIF_BYTE_ORDER_INTEL; } else if (memcmp(data, "MM", 2) == 0) { *bo = EXIF_BYTE_ORDER_MOTOROLA; } else { return FALSE; } if (exif_byte_get_int16(data + 2, *bo) != 0x002A) { return FALSE; } *offset = exif_byte_get_int32(data + 4, *bo); return (*offset < len);}gint exif_tiff_parse(ExifData *exif, unsigned char *tiff, guint size, ExifMarker *list){ ExifByteOrder bo; guint offset; if (!exif_tiff_directory_offset(tiff, size, &offset, &bo)) return -1; return exif_parse_IFD_table(exif, tiff, offset, size, bo, 0, list);}/* *------------------------------------------------------------------- * jpeg marker utils *------------------------------------------------------------------- */#define MARKER_UNKNOWN 0x00#define MARKER_SOI 0xD8#define MARKER_APP1 0xE1static gint jpeg_get_marker_size(unsigned char *data){ /* Size is always in Motorola byte order */ return exif_byte_get_int16(data + 2, EXIF_BYTE_ORDER_MOTOROLA);}static gint jpeg_goto_next_marker(unsigned char **data, gint *size, gint *marker){ gint marker_size = 2; *marker = MARKER_UNKNOWN; /* It is safe to access the marker and its size since we have checked * the SOI and this function guaranties the whole next marker is * available */ if (*(*data + 1) != MARKER_SOI) { marker_size += jpeg_get_marker_size(*data); } *size -= marker_size; /* size should be at least 4, so we can read the marker and its size * and check data are actually available */ if (*size < 4) return -1; /* Jump to the next marker and be sure it begins with 0xFF */ *data += marker_size; if (**data != 0xFF) return -1; if (jpeg_get_marker_size(*data) + 2 > *size) return -1; *marker = *(*data + 1); return 0;}static gint exif_parse_JPEG(ExifData *exif, unsigned char *data, guint size, ExifMarker *list){ guint marker; guint marker_size; if (size < 4 || *data != 0xFF || *(data + 1) != MARKER_SOI) { return -2; } do { if (jpeg_goto_next_marker(&data, &size, &marker) == -1) { break; } } while (marker != MARKER_APP1); if (marker != MARKER_APP1) { return -2; } marker_size = jpeg_get_marker_size(data) - 2; if (marker_size < 6 || memcmp(data + 4, "Exif\x00\x00", 6) != 0) { return -2; } return exif_tiff_parse(exif, data + 10, marker_size - 6, list);}/* *------------------------------------------------------------------- * misc *------------------------------------------------------------------- */static gint map_file(const gchar *path, void **mapping, int *size){ int fd; struct stat fs; if ((fd = open(path, O_RDONLY)) == -1) { perror(path); return -1; } if (fstat(fd, &fs) == -1) { perror(path); close(fd); return -1; } *size = fs.st_size; if ((*mapping = mmap(0, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { perror(path); close(fd); return -1; } close(fd); return 0;}static gint unmap_file(void *mapping, int size){ if (munmap(mapping, size) == -1) { perror("munmap"); return -1; } return 0;}void exif_free(ExifData *exif){ GList *work; if (!exif) return; work = exif->items; while (work) { ExifItem *item = work->data; work = work->next; exif_item_free(item); } g_list_free(exif->items); g_free(exif);}ExifData *exif_read(const gchar *path){ ExifData *exif; void *f; int size, res; gchar *pathl; if (!path) return NULL; pathl = path_from_utf8(path); if (map_file(pathl, &f, &size) == -1) { g_free(pathl); return NULL; } g_free(pathl); exif = g_new0(ExifData, 1); exif->items = NULL; if ((res = exif_parse_JPEG(exif, (unsigned char *)f, size, ExifKnownMarkersList)) == -2) { res = exif_tiff_parse(exif, (unsigned char *)f, size, ExifKnownMarkersList); } if (res != 0) { guint32 offset = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -