diff --git a/src/main/java/edu/illinois/library/cantaloupe/image/Metadata.java b/src/main/java/edu/illinois/library/cantaloupe/image/Metadata.java index 8a2d26819..4c5809bce 100644 --- a/src/main/java/edu/illinois/library/cantaloupe/image/Metadata.java +++ b/src/main/java/edu/illinois/library/cantaloupe/image/Metadata.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import edu.illinois.library.cantaloupe.image.exif.Directory; +import edu.illinois.library.cantaloupe.image.exif.Field; import edu.illinois.library.cantaloupe.image.exif.Tag; import edu.illinois.library.cantaloupe.image.iptc.DataSet; import edu.illinois.library.cantaloupe.image.xmp.MapReader; @@ -120,10 +121,7 @@ public Orientation getOrientation() { if (orientation == null) { orientation = Orientation.ROTATE_0; } - } catch (IllegalArgumentException e) { - LOGGER.info("readOrientation(): {}", e.getMessage()); - orientation = Orientation.ROTATE_0; - } catch (RiotException e) { + } catch (IllegalArgumentException | RiotException e) { LOGGER.info("readOrientation(): {}", e.getMessage()); orientation = Orientation.ROTATE_0; } @@ -132,9 +130,29 @@ public Orientation getOrientation() { } private void readOrientationFromEXIF() { + Field field = exif.getField(Tag.ORIENTATION); Object value = exif.getValue(Tag.ORIENTATION); - if (value != null) { - orientation = Orientation.forEXIFOrientation((int) value); + if (field != null && value != null) { + switch (field.getDataType()) { + case LONG: + case SLONG: + case SHORT: + case SSHORT: + // According to spec the orientation must be an unsigned short (16 bit) + // However, we have seen exif data in the wild with LONG and SLONG types + // Thus to be lenient we accept either and convert to int (github issue #548) + // It could also happen that 'value' is in fact a Java Integer even if the + // exif data type is LONG or SLONG, so we need to check for that as well. + if (value instanceof Long) { + orientation = Orientation.forEXIFOrientation(Math.toIntExact((long) value)); + } else if (value instanceof Integer) { + orientation = Orientation.forEXIFOrientation((int) value); + } + break; + default: + LOGGER.warn("readOrientationFromEXIF(): Unsupported Orientation data type: {}", + field.getDataType()); + } } } diff --git a/src/main/java/edu/illinois/library/cantaloupe/image/exif/Directory.java b/src/main/java/edu/illinois/library/cantaloupe/image/exif/Directory.java index c421ae259..193397d44 100644 --- a/src/main/java/edu/illinois/library/cantaloupe/image/exif/Directory.java +++ b/src/main/java/edu/illinois/library/cantaloupe/image/exif/Directory.java @@ -170,6 +170,14 @@ public Object getValue(Tag tag) { .orElse(null); } + public Field getField(Tag tag) { + return fields.keySet() + .stream() + .filter(f -> f.getTag().equals(tag)) + .findFirst() + .orElse(null); + } + @Override public int hashCode() { final Map codes = new HashMap<>(); diff --git a/src/test/java/edu/illinois/library/cantaloupe/image/MetadataTest.java b/src/test/java/edu/illinois/library/cantaloupe/image/MetadataTest.java index de3d914f6..325a5d5f4 100644 --- a/src/test/java/edu/illinois/library/cantaloupe/image/MetadataTest.java +++ b/src/test/java/edu/illinois/library/cantaloupe/image/MetadataTest.java @@ -17,12 +17,15 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.nio.file.FileVisitOption; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; @@ -232,6 +235,20 @@ void testGetOrientationWithOnlyIllegalEXIFOrientation() throws Exception { } } + @Test + void testGetOrientationWithOnlyLONGEXIFOrientation() throws Exception { + // This image has exif Orientation stored as SLONG, causing a failure (github issue #548) + Path fixture = TestUtil.getImage("jpg-exif-long-orientation.jpg"); + ImageReader reader = new ImageReaderFactory() + .newImageReader(Format.get("jpg"), fixture); + try { + Metadata metadata = reader.getMetadata(0); + assertEquals(Orientation.ROTATE_0, metadata.getOrientation()); + } finally { + reader.dispose(); + } + } + @Test void testGetOrientationWithOnlyXMPOrientation() throws Exception { Path fixture = TestUtil.getImage("jpg-xmp-orientation-90.jpg"); diff --git a/src/test/java/edu/illinois/library/cantaloupe/image/exif/DirectoryTest.java b/src/test/java/edu/illinois/library/cantaloupe/image/exif/DirectoryTest.java index 9624a6c8a..be13f18ff 100644 --- a/src/test/java/edu/illinois/library/cantaloupe/image/exif/DirectoryTest.java +++ b/src/test/java/edu/illinois/library/cantaloupe/image/exif/DirectoryTest.java @@ -366,6 +366,14 @@ void testGetValueWithoutMatchingValue() { assertNull(ifd.getValue(Tag.MAKE)); } + @Test + void testGetField() { + final Directory ifd = new Directory(TagSet.BASELINE_TIFF); + ifd.put(Tag.MAKE, DataType.ASCII, "Cats"); + Field field = ifd.getField(Tag.MAKE); + assertEquals(DataType.ASCII, field.getDataType()); + } + @Test void testHashCodeWithEqualInstances() { final Directory subIFD1 = new Directory(TagSet.EXIF); diff --git a/src/test/resources/images/jpg-exif-long-orientation.jpg b/src/test/resources/images/jpg-exif-long-orientation.jpg new file mode 100755 index 000000000..8ad6fb145 Binary files /dev/null and b/src/test/resources/images/jpg-exif-long-orientation.jpg differ