jpeginfo: decode some TIFF/Exif values
This commit is contained in:
parent
06779c6bdd
commit
15f57a079e
241
tools/jpeginfo.c
241
tools/jpeginfo.c
@ -309,7 +309,7 @@ tiffer_next_entry(struct tiffer *self, struct tiffer_entry *entry)
|
|||||||
entry->p = self->p;
|
entry->p = self->p;
|
||||||
self->p += sizeof offset;
|
self->p += sizeof offset;
|
||||||
} else if (tiffer_u32(self, &offset)) {
|
} else if (tiffer_u32(self, &offset)) {
|
||||||
entry->p = self->p + offset;
|
entry->p = self->begin + offset;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -326,111 +326,111 @@ tiffer_next_entry(struct tiffer *self, struct tiffer_entry *entry)
|
|||||||
|
|
||||||
// --- TIFF/Exif/MPF/* tags ----------------------------------------------------
|
// --- TIFF/Exif/MPF/* tags ----------------------------------------------------
|
||||||
|
|
||||||
|
struct tiff_value {
|
||||||
|
const char *name;
|
||||||
|
uint16_t value;
|
||||||
|
};
|
||||||
|
|
||||||
struct tiff_entry {
|
struct tiff_entry {
|
||||||
const char *name;
|
const char *name;
|
||||||
uint16_t tag;
|
uint16_t tag;
|
||||||
|
struct tiff_value *values;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct tiff_entry tiff_entries[] = {
|
static struct tiff_entry tiff_entries[] = {
|
||||||
{"NewSubfileType", 254},
|
{"NewSubfileType", 254, NULL},
|
||||||
{"SubfileType", 255},
|
{"SubfileType", 255, NULL},
|
||||||
{"ImageWidth", 256},
|
{"ImageWidth", 256, NULL},
|
||||||
{"ImageLength", 257},
|
{"ImageLength", 257, NULL},
|
||||||
{"BitsPerSample", 258},
|
{"BitsPerSample", 258, NULL},
|
||||||
{"Compression", 259},
|
{"Compression", 259, (struct tiff_value[]) {
|
||||||
{"PhotometricInterpretation", 262},
|
{"Uncompressed", 1},
|
||||||
{"Threshholding", 263},
|
{"CCITT 1D", 2},
|
||||||
{"CellWidth", 264},
|
{"Group 3 Fax", 3},
|
||||||
{"CellLength", 265},
|
{"Group 4 Fax", 4},
|
||||||
{"FillOrder", 266},
|
{"LZW", 5},
|
||||||
{"DocumentName", 269},
|
{"JPEG", 6},
|
||||||
{"ImageDescription", 270},
|
{"PackBits", 32773},
|
||||||
{"Make", 271},
|
{}
|
||||||
{"Model", 272},
|
}},
|
||||||
{"StripOffsets", 273},
|
{"PhotometricInterpretation", 262, (struct tiff_value[]) {
|
||||||
{"Orientation", 274},
|
{"WhiteIsZero", 0},
|
||||||
{"SamplesPerPixel", 277},
|
{"BlackIsZero", 1},
|
||||||
{"RowsPerStrip", 278},
|
{"RGB", 2},
|
||||||
{"StripByteCounts", 279},
|
{"RGB Palette", 3},
|
||||||
{"MinSampleValue", 280},
|
{"Transparency mask", 4},
|
||||||
{"MaxSampleValue", 281},
|
{"CMYK", 5},
|
||||||
{"XResolution", 282},
|
{"YCbCr", 6},
|
||||||
{"YResolution", 283},
|
{"CIELab", 8},
|
||||||
{"PlanarConfiguration", 284},
|
{}
|
||||||
{"PageName", 285},
|
}},
|
||||||
{"XPosition", 286},
|
{"Threshholding", 263, NULL},
|
||||||
{"YPosition", 287},
|
{"CellWidth", 264, NULL},
|
||||||
{"FreeOffsets", 288},
|
{"CellLength", 265, NULL},
|
||||||
{"FreeByteCounts", 289},
|
{"FillOrder", 266, NULL},
|
||||||
{"GrayResponseUnit", 290},
|
{"DocumentName", 269, NULL},
|
||||||
{"GrayResponseCurve", 291},
|
{"ImageDescription", 270, NULL},
|
||||||
{"T4Options", 292},
|
{"Make", 271, NULL},
|
||||||
{"T6Options", 293},
|
{"Model", 272, NULL},
|
||||||
{"ResolutionUnit", 296},
|
{"StripOffsets", 273, NULL},
|
||||||
{"PageNumber", 297},
|
{"Orientation", 274, NULL},
|
||||||
{"TransferFunction", 301},
|
{"SamplesPerPixel", 277, NULL},
|
||||||
{"Software", 305},
|
{"RowsPerStrip", 278, NULL},
|
||||||
{"DateTime", 306},
|
{"StripByteCounts", 279, NULL},
|
||||||
{"Artist", 315},
|
{"MinSampleValue", 280, NULL},
|
||||||
{"HostComputer", 316},
|
{"MaxSampleValue", 281, NULL},
|
||||||
{"Predictor", 317},
|
{"XResolution", 282, NULL},
|
||||||
{"WhitePoint", 318},
|
{"YResolution", 283, NULL},
|
||||||
{"PrimaryChromaticities", 319},
|
{"PlanarConfiguration", 284, NULL},
|
||||||
{"ColorMap", 320},
|
{"PageName", 285, NULL},
|
||||||
{"HalftoneHints", 321},
|
{"XPosition", 286, NULL},
|
||||||
{"TileWidth", 322},
|
{"YPosition", 287, NULL},
|
||||||
{"TileLength", 323},
|
{"FreeOffsets", 288, NULL},
|
||||||
{"TileOffsets", 324},
|
{"FreeByteCounts", 289, NULL},
|
||||||
{"TileByteCounts", 325},
|
{"GrayResponseUnit", 290, NULL},
|
||||||
{"InkSet", 332},
|
{"GrayResponseCurve", 291, NULL},
|
||||||
{"InkNames", 333},
|
{"T4Options", 292, NULL},
|
||||||
{"NumberOfInks", 334},
|
{"T6Options", 293, NULL},
|
||||||
{"DotRange", 336},
|
{"ResolutionUnit", 296, NULL},
|
||||||
{"TargetPrinter", 337},
|
{"PageNumber", 297, NULL},
|
||||||
{"ExtraSamples", 338},
|
{"TransferFunction", 301, NULL},
|
||||||
{"SampleFormat", 339},
|
{"Software", 305, NULL},
|
||||||
{"SMinSampleValue", 340},
|
{"DateTime", 306, NULL},
|
||||||
{"SMaxSampleValue", 341},
|
{"Artist", 315, NULL},
|
||||||
{"TransferRange", 342},
|
{"HostComputer", 316, NULL},
|
||||||
{"JPEGProc", 512},
|
{"Predictor", 317, NULL},
|
||||||
{"JPEGInterchangeFormat", 513},
|
{"WhitePoint", 318, NULL},
|
||||||
{"JPEGInterchangeFormatLngth", 514},
|
{"PrimaryChromaticities", 319, NULL},
|
||||||
{"JPEGRestartInterval", 515},
|
{"ColorMap", 320, NULL},
|
||||||
{"JPEGLosslessPredictors", 517},
|
{"HalftoneHints", 321, NULL},
|
||||||
{"JPEGPointTransforms", 518},
|
{"TileWidth", 322, NULL},
|
||||||
{"JPEGQTables", 519},
|
{"TileLength", 323, NULL},
|
||||||
{"JPEGDCTables", 520},
|
{"TileOffsets", 324, NULL},
|
||||||
{"JPEGACTables", 521},
|
{"TileByteCounts", 325, NULL},
|
||||||
{"YCbCrCoefficients", 529},
|
{"InkSet", 332, NULL},
|
||||||
{"YCbCrSubSampling", 530},
|
{"InkNames", 333, NULL},
|
||||||
{"YCbCrPositioning", 531},
|
{"NumberOfInks", 334, NULL},
|
||||||
{"ReferenceBlackWhite", 532},
|
{"DotRange", 336, NULL},
|
||||||
{"Copyright", 33432},
|
{"TargetPrinter", 337, NULL},
|
||||||
{}
|
{"ExtraSamples", 338, NULL},
|
||||||
};
|
{"SampleFormat", 339, NULL},
|
||||||
|
{"SMinSampleValue", 340, NULL},
|
||||||
// Compression
|
{"SMaxSampleValue", 341, NULL},
|
||||||
static struct tiff_entry tiff_compression_values[] = {
|
{"TransferRange", 342, NULL},
|
||||||
{"Uncompressed", 1},
|
{"JPEGProc", 512, NULL},
|
||||||
{"CCITT 1D", 2},
|
{"JPEGInterchangeFormat", 513, NULL},
|
||||||
{"Group 3 Fax", 3},
|
{"JPEGInterchangeFormatLngth", 514, NULL},
|
||||||
{"Group 4 Fax", 4},
|
{"JPEGRestartInterval", 515, NULL},
|
||||||
{"LZW", 5},
|
{"JPEGLosslessPredictors", 517, NULL},
|
||||||
{"JPEG", 6},
|
{"JPEGPointTransforms", 518, NULL},
|
||||||
{"PackBits", 32773},
|
{"JPEGQTables", 519, NULL},
|
||||||
{}
|
{"JPEGDCTables", 520, NULL},
|
||||||
};
|
{"JPEGACTables", 521, NULL},
|
||||||
|
{"YCbCrCoefficients", 529, NULL},
|
||||||
// PhotometricInterpretation
|
{"YCbCrSubSampling", 530, NULL},
|
||||||
static struct tiff_entry tiff_photometric_interpretation_values[] = {
|
{"YCbCrPositioning", 531, NULL},
|
||||||
{"WhiteIsZero", 0},
|
{"ReferenceBlackWhite", 532, NULL},
|
||||||
{"BlackIsZero", 1},
|
{"Copyright", 33432, NULL},
|
||||||
{"RGB", 2},
|
|
||||||
{"RGB Palette", 3},
|
|
||||||
{"Transparency mask", 4},
|
|
||||||
{"CMYK", 5},
|
|
||||||
{"YCbCr", 6},
|
|
||||||
{"CIELab", 8},
|
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -465,15 +465,36 @@ add_error(jv o, const char *message)
|
|||||||
|
|
||||||
// --- Exif --------------------------------------------------------------------
|
// --- Exif --------------------------------------------------------------------
|
||||||
|
|
||||||
// TODO(p): Decode more and better.
|
|
||||||
static jv
|
static jv
|
||||||
process_exif_entry(jv o, const struct tiffer_entry *entry)
|
process_exif_entry(jv o, struct tiffer *T, const struct tiffer_entry *entry)
|
||||||
{
|
{
|
||||||
for (const struct tiff_entry *p = tiff_entries; p->name; p++) {
|
jv value = jv_true();
|
||||||
if (p->tag == entry->tag)
|
|
||||||
return add_to_subarray(o, "TIFF", jv_string(p->name));
|
// TODO(p): Decode much more, and also descend into sub-IFD trees.
|
||||||
|
bool numeric = false;
|
||||||
|
double real = 0;
|
||||||
|
if (!entry->remaining_count) {
|
||||||
|
value = jv_null();
|
||||||
|
} else if (entry->type == ASCII) {
|
||||||
|
value = jv_string_sized((const char *) entry->p,
|
||||||
|
entry->remaining_count - 1);
|
||||||
|
} else if ((numeric = tiffer_real(T, entry, &real))) {
|
||||||
|
value = jv_number(real);
|
||||||
}
|
}
|
||||||
return add_to_subarray(o, "TIFF", jv_number(entry->tag));
|
|
||||||
|
for (const struct tiff_entry *p = tiff_entries; p->name; p++) {
|
||||||
|
if (p->tag != entry->tag)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (numeric && p->values) {
|
||||||
|
for (const struct tiff_value *q = p->values; q->name; q++) {
|
||||||
|
if (q->value == real)
|
||||||
|
return jv_set(o, jv_string(p->name), jv_string(q->name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jv_set(o, jv_string(p->name), value);
|
||||||
|
}
|
||||||
|
return jv_set(o, jv_string_fmt("%u", entry->tag), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static jv
|
static jv
|
||||||
@ -483,12 +504,12 @@ parse_exif(jv o, const uint8_t *p, size_t len)
|
|||||||
if (!tiffer_init(&T, p, len))
|
if (!tiffer_init(&T, p, len))
|
||||||
return add_warning(o, "invalid Exif");
|
return add_warning(o, "invalid Exif");
|
||||||
|
|
||||||
// TODO(p): Turn this into an array of objects indexed by tag name.
|
|
||||||
struct tiffer_entry entry;
|
struct tiffer_entry entry;
|
||||||
while (tiffer_next_ifd(&T)) {
|
while (tiffer_next_ifd(&T)) {
|
||||||
while (tiffer_next_entry(&T, &entry)) {
|
jv ifd = jv_object();
|
||||||
o = process_exif_entry(o, &entry);
|
while (tiffer_next_entry(&T, &entry))
|
||||||
}
|
ifd = process_exif_entry(ifd, &T, &entry);
|
||||||
|
o = add_to_subarray(o, "TIFF", ifd);
|
||||||
}
|
}
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user