From ac1b9531115433927aba90c507a5aea848c92685 Mon Sep 17 00:00:00 2001 From: Sergii Date: Wed, 18 Jul 2018 10:03:38 -0700 Subject: [PATCH 1/2] ThumbnailWrite method with storing the JpegSegments offset --- .../drew/imaging/jpeg/JpegMetadataReader.java | 2 +- .../drew/imaging/jpeg/JpegSegmentData.java | 70 +++++++++++++++---- .../drew/imaging/jpeg/JpegSegmentInfo.java | 20 ++++++ .../jpeg/JpegSegmentMetadataReader.java | 2 +- .../drew/imaging/jpeg/JpegSegmentReader.java | 3 +- Source/com/drew/imaging/tiff/TiffReader.java | 9 +++ Source/com/drew/lang/RandomAccessReader.java | 10 +++ Source/com/drew/metadata/Directory.java | 11 +++ .../drew/metadata/adobe/AdobeJpegReader.java | 9 +-- Source/com/drew/metadata/exif/ExifReader.java | 9 +-- .../metadata/exif/ExifThumbnailDirectory.java | 37 ++++++++++ Source/com/drew/metadata/icc/IccReader.java | 15 ++-- Source/com/drew/metadata/iptc/IptcReader.java | 9 +-- Source/com/drew/metadata/jfif/JfifReader.java | 9 +-- Source/com/drew/metadata/jfxx/JfxxReader.java | 9 +-- .../drew/metadata/jpeg/JpegCommentReader.java | 7 +- .../com/drew/metadata/jpeg/JpegDhtReader.java | 7 +- .../com/drew/metadata/jpeg/JpegDnlReader.java | 7 +- Source/com/drew/metadata/jpeg/JpegReader.java | 7 +- .../drew/metadata/photoshop/DuckyReader.java | 9 +-- .../metadata/photoshop/PhotoshopReader.java | 11 +-- .../metadata/tiff/DirectoryTiffHandler.java | 5 ++ Source/com/drew/metadata/xmp/XmpReader.java | 21 +++--- .../drew/metadata/exif/ExifReaderTest.java | 5 +- .../com/drew/metadata/icc/IccReaderTest.java | 5 +- .../com/drew/metadata/xmp/XmpReaderTest.java | 5 +- 26 files changed, 232 insertions(+), 81 deletions(-) create mode 100644 Source/com/drew/imaging/jpeg/JpegSegmentInfo.java diff --git a/Source/com/drew/imaging/jpeg/JpegMetadataReader.java b/Source/com/drew/imaging/jpeg/JpegMetadataReader.java index e90b2ba66..121ad2ed3 100644 --- a/Source/com/drew/imaging/jpeg/JpegMetadataReader.java +++ b/Source/com/drew/imaging/jpeg/JpegMetadataReader.java @@ -131,7 +131,7 @@ public static void processJpegSegmentData(Metadata metadata, Iterable> _segmentDataMap = new HashMap>(10); + private final HashMap> _segmentDataMap = new HashMap>(10); /** * Adds segment bytes to the collection. @@ -54,7 +53,19 @@ public class JpegSegmentData @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"}) public void addSegment(byte segmentType, @NotNull byte[] segmentBytes) { - getOrCreateSegmentList(segmentType).add(segmentBytes); + addSegment(segmentType, 0, segmentBytes); + } + + /** + * Adds segment bytes to the collection. + * + * @param segmentType the type of the segment being added + * @param segmentBytes the byte array holding bytes for the segment being added + */ + @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"}) + public void addSegment(byte segmentType, long fileOffset, @NotNull byte[] segmentBytes) + { + getOrCreateSegmentList(segmentType).add(new JpegSegmentInfo(segmentBytes, fileOffset)); } /** @@ -125,10 +136,10 @@ public byte[] getSegment(@NotNull JpegSegmentType segmentType, int occurrence) @Nullable public byte[] getSegment(byte segmentType, int occurrence) { - final List segmentList = getSegmentList(segmentType); + final List segmentList = getSegmentList(segmentType); return segmentList != null && segmentList.size() > occurrence - ? segmentList.get(occurrence) + ? segmentList.get(occurrence).bytes : null; } @@ -144,6 +155,18 @@ public Iterable getSegments(@NotNull JpegSegmentType segmentType) return getSegments(segmentType.byteValue); } + /** + * Returns all instances of a given JPEG segment. If no instances exist, an empty sequence is returned. + * + * @param segmentType a number which identifies the type of JPEG segment being queried + * @return zero or more byte arrays, each holding the data of a JPEG segment + */ + @NotNull + public Iterable getSegmentsInfo(@NotNull JpegSegmentType segmentType) + { + return getSegmentsInfo(segmentType.byteValue); + } + /** * Returns all instances of a given JPEG segment. If no instances exist, an empty sequence is returned. * @@ -153,24 +176,43 @@ public Iterable getSegments(@NotNull JpegSegmentType segmentType) @NotNull public Iterable getSegments(byte segmentType) { - final List segmentList = getSegmentList(segmentType); - return segmentList == null ? new ArrayList() : segmentList; + final List segmentList = getSegmentList(segmentType); + List segmentDataList = new ArrayList(); + if (segmentList != null) { + for (JpegSegmentInfo info : segmentList) { + segmentDataList.add(info.bytes); + } + } + return segmentList == null ? new ArrayList() : segmentDataList; + } + + /** + * Returns all instances of a given JPEG segment. If no instances exist, an empty sequence is returned. + * + * @param segmentType a number which identifies the type of JPEG segment being queried + * @return zero or more byte arrays, each holding the bytes of a JPEG segment + */ + @NotNull + public Iterable getSegmentsInfo(byte segmentType) + { + final List segmentList = getSegmentList(segmentType); + return segmentList == null ? new ArrayList() : segmentList; } @Nullable - private List getSegmentList(byte segmentType) + private List getSegmentList(byte segmentType) { return _segmentDataMap.get(segmentType); } @NotNull - private List getOrCreateSegmentList(byte segmentType) + private List getOrCreateSegmentList(byte segmentType) { - List segmentList; + List segmentList; if (_segmentDataMap.containsKey(segmentType)) { segmentList = _segmentDataMap.get(segmentType); } else { - segmentList = new ArrayList(); + segmentList = new ArrayList(); _segmentDataMap.put(segmentType, segmentList); } return segmentList; @@ -195,7 +237,7 @@ public int getSegmentCount(@NotNull JpegSegmentType segmentType) */ public int getSegmentCount(byte segmentType) { - final List segmentList = getSegmentList(segmentType); + final List segmentList = getSegmentList(segmentType); return segmentList == null ? 0 : segmentList.size(); } @@ -222,7 +264,7 @@ public void removeSegmentOccurrence(@NotNull JpegSegmentType segmentType, int oc @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"}) public void removeSegmentOccurrence(byte segmentType, int occurrence) { - final List segmentList = _segmentDataMap.get(segmentType); + final List segmentList = _segmentDataMap.get(segmentType); segmentList.remove(occurrence); } diff --git a/Source/com/drew/imaging/jpeg/JpegSegmentInfo.java b/Source/com/drew/imaging/jpeg/JpegSegmentInfo.java new file mode 100644 index 000000000..61a8349b0 --- /dev/null +++ b/Source/com/drew/imaging/jpeg/JpegSegmentInfo.java @@ -0,0 +1,20 @@ +package com.drew.imaging.jpeg; + +public class JpegSegmentInfo { + public byte[] bytes; + public long fileOffset; + + public JpegSegmentInfo() { + + } + + public JpegSegmentInfo(byte[] bytes) { + this.bytes = bytes; + this.fileOffset = 0; + } + + public JpegSegmentInfo(byte[] bytes, long fileOffset) { + this.bytes = bytes; + this.fileOffset = fileOffset; + } +} diff --git a/Source/com/drew/imaging/jpeg/JpegSegmentMetadataReader.java b/Source/com/drew/imaging/jpeg/JpegSegmentMetadataReader.java index e1d2aef34..4222df1dd 100644 --- a/Source/com/drew/imaging/jpeg/JpegSegmentMetadataReader.java +++ b/Source/com/drew/imaging/jpeg/JpegSegmentMetadataReader.java @@ -22,5 +22,5 @@ public interface JpegSegmentMetadataReader * @param metadata The {@link Metadata} object into which extracted values should be merged. * @param segmentType The {@link JpegSegmentType} being read. */ - void readJpegSegments(@NotNull final Iterable segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType); + void readJpegSegments(@NotNull final Iterable segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType); } diff --git a/Source/com/drew/imaging/jpeg/JpegSegmentReader.java b/Source/com/drew/imaging/jpeg/JpegSegmentReader.java index 55e6bd01b..089da42ac 100644 --- a/Source/com/drew/imaging/jpeg/JpegSegmentReader.java +++ b/Source/com/drew/imaging/jpeg/JpegSegmentReader.java @@ -148,9 +148,10 @@ public static JpegSegmentData readSegments(@NotNull final SequentialReader reade // Check whether we are interested in this segment if (segmentTypeBytes == null || segmentTypeBytes.contains(segmentType)) { + long fileOffset = reader.getPosition(); byte[] segmentBytes = reader.getBytes(segmentLength); assert (segmentLength == segmentBytes.length); - segmentData.addSegment(segmentType, segmentBytes); + segmentData.addSegment(segmentType, fileOffset, segmentBytes); } else { // Skip this segment if (!reader.trySkip(segmentLength)) { diff --git a/Source/com/drew/imaging/tiff/TiffReader.java b/Source/com/drew/imaging/tiff/TiffReader.java index dd4aefe50..6900ae068 100644 --- a/Source/com/drew/imaging/tiff/TiffReader.java +++ b/Source/com/drew/imaging/tiff/TiffReader.java @@ -23,6 +23,8 @@ import com.drew.lang.RandomAccessReader; import com.drew.lang.Rational; import com.drew.lang.annotations.NotNull; +import com.drew.metadata.Directory; +import com.drew.metadata.tiff.DirectoryTiffHandler; import java.io.IOException; import java.util.HashSet; @@ -64,6 +66,13 @@ public void processTiff(@NotNull final RandomAccessReader reader, final int tiffMarker = reader.getUInt16(2 + tiffHeaderOffset); handler.setTiffMarker(tiffMarker); + if (handler instanceof DirectoryTiffHandler) { + Directory currentDirectory = ((DirectoryTiffHandler) handler).getCurrentDirectory(); + if (currentDirectory != null) { + currentDirectory.setFileDataOffset(reader.getOriginOffset()); + } + } + int firstIfdOffset = reader.getInt32(4 + tiffHeaderOffset) + tiffHeaderOffset; // David Ekholm sent a digital camera image that has this problem diff --git a/Source/com/drew/lang/RandomAccessReader.java b/Source/com/drew/lang/RandomAccessReader.java index 7e5987a42..5864c1160 100644 --- a/Source/com/drew/lang/RandomAccessReader.java +++ b/Source/com/drew/lang/RandomAccessReader.java @@ -46,6 +46,16 @@ public abstract class RandomAccessReader { private boolean _isMotorolaByteOrder = true; + private long _originOffset = 0; + + public RandomAccessReader originOffset(long _originOffset) { + this._originOffset = _originOffset; + return this; + } + + public long getOriginOffset() { + return _originOffset; + } public abstract int toUnshiftedOffset(int localOffset); diff --git a/Source/com/drew/metadata/Directory.java b/Source/com/drew/metadata/Directory.java index 6736c5649..944fe7fa3 100644 --- a/Source/com/drew/metadata/Directory.java +++ b/Source/com/drew/metadata/Directory.java @@ -67,6 +67,8 @@ public abstract class Directory @Nullable private Directory _parent; + private long fileDataOffset = 0; + // ABSTRACT METHODS /** @@ -90,6 +92,15 @@ protected Directory() // VARIOUS METHODS + public long getFileDataOffset() { + return fileDataOffset + + (_parent == null ? 0 : _parent.getFileDataOffset()); + } + + public void setFileDataOffset(long fileDataOffset) { + this.fileDataOffset = fileDataOffset; + } + /** * Gets a value indicating whether the directory is empty, meaning it contains no errors and no tag values. */ diff --git a/Source/com/drew/metadata/adobe/AdobeJpegReader.java b/Source/com/drew/metadata/adobe/AdobeJpegReader.java index d5d910150..2d33837ba 100644 --- a/Source/com/drew/metadata/adobe/AdobeJpegReader.java +++ b/Source/com/drew/metadata/adobe/AdobeJpegReader.java @@ -21,6 +21,7 @@ package com.drew.metadata.adobe; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentMetadataReader; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.SequentialByteArrayReader; @@ -49,11 +50,11 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APPE); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) { - for (byte[] bytes : segments) { - if (bytes.length == 12 && PREAMBLE.equalsIgnoreCase(new String(bytes, 0, PREAMBLE.length()))) - extract(new SequentialByteArrayReader(bytes), metadata); + for (JpegSegmentInfo info : segments) { + if (info.bytes.length == 12 && PREAMBLE.equalsIgnoreCase(new String(info.bytes, 0, PREAMBLE.length()))) + extract(new SequentialByteArrayReader(info.bytes), metadata); } } diff --git a/Source/com/drew/metadata/exif/ExifReader.java b/Source/com/drew/metadata/exif/ExifReader.java index ffc77c67c..7fb972046 100644 --- a/Source/com/drew/metadata/exif/ExifReader.java +++ b/Source/com/drew/metadata/exif/ExifReader.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.exif; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentMetadataReader; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.imaging.tiff.TiffProcessingException; @@ -53,15 +54,15 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APP1); } - public void readJpegSegments(@NotNull final Iterable segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType) + public void readJpegSegments(@NotNull final Iterable segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType) { assert(segmentType == JpegSegmentType.APP1); - for (byte[] segmentBytes : segments) { + for (JpegSegmentInfo info: segments) { // Filter any segments containing unexpected preambles - if (segmentBytes.length < JPEG_SEGMENT_PREAMBLE.length() || !new String(segmentBytes, 0, JPEG_SEGMENT_PREAMBLE.length()).equals(JPEG_SEGMENT_PREAMBLE)) + if (info.bytes.length < JPEG_SEGMENT_PREAMBLE.length() || !new String(info.bytes, 0, JPEG_SEGMENT_PREAMBLE.length()).equals(JPEG_SEGMENT_PREAMBLE)) continue; - extract(new ByteArrayReader(segmentBytes), metadata, JPEG_SEGMENT_PREAMBLE.length()); + extract(new ByteArrayReader(info.bytes).originOffset(info.fileOffset), metadata, JPEG_SEGMENT_PREAMBLE.length()); } } diff --git a/Source/com/drew/metadata/exif/ExifThumbnailDirectory.java b/Source/com/drew/metadata/exif/ExifThumbnailDirectory.java index 56122e3ca..1bc003002 100644 --- a/Source/com/drew/metadata/exif/ExifThumbnailDirectory.java +++ b/Source/com/drew/metadata/exif/ExifThumbnailDirectory.java @@ -22,7 +22,9 @@ package com.drew.metadata.exif; import com.drew.lang.annotations.NotNull; +import com.drew.metadata.MetadataException; +import java.io.*; import java.util.HashMap; /** @@ -77,4 +79,39 @@ protected HashMap getTagNameMap() { return _tagNameMap; } + + public boolean writeThumbnail(File imageFile, File thumbTargetFile) throws MetadataException, IOException { + // after the extraction process, if we have the correct tags, we may be able to store thumbnail information + if (containsTag(ExifThumbnailDirectory.TAG_COMPRESSION)) { + Integer offset = getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_OFFSET); + Integer length = getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_LENGTH); + if (offset != null && length != null) { + FileInputStream input = new FileInputStream(imageFile); + try { + FileOutputStream output = new FileOutputStream(thumbTargetFile); + try { + long tiffHeaderOffset = getFileDataOffset(); + tiffHeaderOffset += ExifReader.JPEG_SEGMENT_PREAMBLE.length(); + byte[] buffer = new byte[length]; + if (input.skip(tiffHeaderOffset + offset) < 0) { + throw new MetadataException("Thumbnail offset is beyond the end of file"); + } + if (input.read(buffer, 0, length) < length){ + throw new MetadataException("Thumbnail content is beyond the end of file"); + } + output.write(buffer); + output.flush(); + return true; + } finally { + output.flush(); + output.close(); + } + } finally { + input.close(); + } + } + return false; + } + throw new MetadataException("No thumbnail data exists."); + } } diff --git a/Source/com/drew/metadata/icc/IccReader.java b/Source/com/drew/metadata/icc/IccReader.java index 090c9b482..9ba94fe1c 100644 --- a/Source/com/drew/metadata/icc/IccReader.java +++ b/Source/com/drew/metadata/icc/IccReader.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.icc; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentMetadataReader; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.ByteArrayReader; @@ -57,7 +58,7 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APP2); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) { final int preambleLength = JPEG_SEGMENT_PREAMBLE.length(); @@ -65,22 +66,22 @@ public void readJpegSegments(@NotNull Iterable segments, @NotNull Metada // We concat them together in this buffer for later processing. byte[] buffer = null; - for (byte[] segmentBytes : segments) { + for (JpegSegmentInfo info : segments) { // Skip any segments that do not contain the required preamble - if (segmentBytes.length < preambleLength || !JPEG_SEGMENT_PREAMBLE.equalsIgnoreCase(new String(segmentBytes, 0, preambleLength))) + if (info.bytes.length < preambleLength || !JPEG_SEGMENT_PREAMBLE.equalsIgnoreCase(new String(info.bytes, 0, preambleLength))) continue; // NOTE we ignore three bytes here -- are they useful for anything? // Grow the buffer if (buffer == null) { - buffer = new byte[segmentBytes.length - 14]; + buffer = new byte[info.bytes.length - 14]; // skip the first 14 bytes - System.arraycopy(segmentBytes, 14, buffer, 0, segmentBytes.length - 14); + System.arraycopy(info.bytes, 14, buffer, 0, info.bytes.length - 14); } else { - byte[] newBuffer = new byte[buffer.length + segmentBytes.length - 14]; + byte[] newBuffer = new byte[buffer.length + info.bytes.length - 14]; System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); - System.arraycopy(segmentBytes, 14, newBuffer, buffer.length, segmentBytes.length - 14); + System.arraycopy(info.bytes, 14, newBuffer, buffer.length, info.bytes.length - 14); buffer = newBuffer; } } diff --git a/Source/com/drew/metadata/iptc/IptcReader.java b/Source/com/drew/metadata/iptc/IptcReader.java index 7a21c0c2a..ee0d3d880 100644 --- a/Source/com/drew/metadata/iptc/IptcReader.java +++ b/Source/com/drew/metadata/iptc/IptcReader.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.iptc; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentMetadataReader; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.SequentialByteArrayReader; @@ -65,12 +66,12 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APPD); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) { - for (byte[] segmentBytes : segments) { + for (JpegSegmentInfo info : segments) { // Ensure data starts with the IPTC marker byte - if (segmentBytes.length != 0 && segmentBytes[0] == IptcMarkerByte) { - extract(new SequentialByteArrayReader(segmentBytes), metadata, segmentBytes.length); + if (info.bytes.length != 0 && info.bytes[0] == IptcMarkerByte) { + extract(new SequentialByteArrayReader(info.bytes), metadata, info.bytes.length); } } } diff --git a/Source/com/drew/metadata/jfif/JfifReader.java b/Source/com/drew/metadata/jfif/JfifReader.java index 55db301a1..1ca5cefa1 100644 --- a/Source/com/drew/metadata/jfif/JfifReader.java +++ b/Source/com/drew/metadata/jfif/JfifReader.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.jfif; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentMetadataReader; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.ByteArrayReader; @@ -51,12 +52,12 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APP0); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) { - for (byte[] segmentBytes : segments) { + for (JpegSegmentInfo info : segments) { // Skip segments not starting with the required header - if (segmentBytes.length >= PREAMBLE.length() && PREAMBLE.equals(new String(segmentBytes, 0, PREAMBLE.length()))) - extract(new ByteArrayReader(segmentBytes), metadata); + if (info.bytes.length >= PREAMBLE.length() && PREAMBLE.equals(new String(info.bytes, 0, PREAMBLE.length()))) + extract(new ByteArrayReader(info.bytes), metadata); } } diff --git a/Source/com/drew/metadata/jfxx/JfxxReader.java b/Source/com/drew/metadata/jfxx/JfxxReader.java index ca68ad9c7..e3f1097b0 100644 --- a/Source/com/drew/metadata/jfxx/JfxxReader.java +++ b/Source/com/drew/metadata/jfxx/JfxxReader.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.jfxx; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentMetadataReader; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.ByteArrayReader; @@ -51,12 +52,12 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APP0); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) { - for (byte[] segmentBytes : segments) { + for (JpegSegmentInfo info : segments) { // Skip segments not starting with the required header - if (segmentBytes.length >= PREAMBLE.length() && PREAMBLE.equals(new String(segmentBytes, 0, PREAMBLE.length()))) - extract(new ByteArrayReader(segmentBytes), metadata); + if (info.bytes.length >= PREAMBLE.length() && PREAMBLE.equals(new String(info.bytes, 0, PREAMBLE.length()))) + extract(new ByteArrayReader(info.bytes), metadata); } } diff --git a/Source/com/drew/metadata/jpeg/JpegCommentReader.java b/Source/com/drew/metadata/jpeg/JpegCommentReader.java index 8170ba5f2..f20e2aa91 100644 --- a/Source/com/drew/metadata/jpeg/JpegCommentReader.java +++ b/Source/com/drew/metadata/jpeg/JpegCommentReader.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.jpeg; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentMetadataReader; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.annotations.NotNull; @@ -42,14 +43,14 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.COM); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) { - for (byte[] segmentBytes : segments) { + for (JpegSegmentInfo info: segments) { JpegCommentDirectory directory = new JpegCommentDirectory(); metadata.addDirectory(directory); // The entire contents of the directory are the comment - directory.setStringValue(JpegCommentDirectory.TAG_COMMENT, new StringValue(segmentBytes, null)); + directory.setStringValue(JpegCommentDirectory.TAG_COMMENT, new StringValue(info.bytes, null)); } } } diff --git a/Source/com/drew/metadata/jpeg/JpegDhtReader.java b/Source/com/drew/metadata/jpeg/JpegDhtReader.java index f8f769151..288897c97 100644 --- a/Source/com/drew/metadata/jpeg/JpegDhtReader.java +++ b/Source/com/drew/metadata/jpeg/JpegDhtReader.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.jpeg; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentMetadataReader; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.SequentialByteArrayReader; @@ -44,10 +45,10 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.DHT); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) { - for (byte[] segmentBytes : segments) { - extract(new SequentialByteArrayReader(segmentBytes), metadata); + for (JpegSegmentInfo info : segments) { + extract(new SequentialByteArrayReader(info.bytes), metadata); } } diff --git a/Source/com/drew/metadata/jpeg/JpegDnlReader.java b/Source/com/drew/metadata/jpeg/JpegDnlReader.java index 6fc348747..601554776 100644 --- a/Source/com/drew/metadata/jpeg/JpegDnlReader.java +++ b/Source/com/drew/metadata/jpeg/JpegDnlReader.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.jpeg; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentMetadataReader; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.SequentialByteArrayReader; @@ -44,10 +45,10 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.DNL); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) { - for (byte[] segmentBytes : segments) { - extract(segmentBytes, metadata, segmentType); + for (JpegSegmentInfo info : segments) { + extract(info.bytes, metadata, segmentType); } } diff --git a/Source/com/drew/metadata/jpeg/JpegReader.java b/Source/com/drew/metadata/jpeg/JpegReader.java index 9e6bf6619..4a94bd287 100644 --- a/Source/com/drew/metadata/jpeg/JpegReader.java +++ b/Source/com/drew/metadata/jpeg/JpegReader.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.jpeg; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentMetadataReader; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.SequentialByteArrayReader; @@ -62,10 +63,10 @@ public Iterable getSegmentTypes() ); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) { - for (byte[] segmentBytes : segments) { - extract(segmentBytes, metadata, segmentType); + for (JpegSegmentInfo info : segments) { + extract(info.bytes, metadata, segmentType); } } diff --git a/Source/com/drew/metadata/photoshop/DuckyReader.java b/Source/com/drew/metadata/photoshop/DuckyReader.java index f740acaf7..9e90d0412 100644 --- a/Source/com/drew/metadata/photoshop/DuckyReader.java +++ b/Source/com/drew/metadata/photoshop/DuckyReader.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.photoshop; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentMetadataReader; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.Charsets; @@ -48,17 +49,17 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APPC); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) { final int preambleLength = JPEG_SEGMENT_PREAMBLE.length(); - for (byte[] segmentBytes : segments) { + for (JpegSegmentInfo info : segments) { // Ensure data starts with the necessary preamble - if (segmentBytes.length < preambleLength || !JPEG_SEGMENT_PREAMBLE.equals(new String(segmentBytes, 0, preambleLength))) + if (info.bytes.length < preambleLength || !JPEG_SEGMENT_PREAMBLE.equals(new String(info.bytes, 0, preambleLength))) continue; extract( - new SequentialByteArrayReader(segmentBytes, preambleLength), + new SequentialByteArrayReader(info.bytes, preambleLength), metadata); } } diff --git a/Source/com/drew/metadata/photoshop/PhotoshopReader.java b/Source/com/drew/metadata/photoshop/PhotoshopReader.java index dd06b8638..e3bd218dc 100644 --- a/Source/com/drew/metadata/photoshop/PhotoshopReader.java +++ b/Source/com/drew/metadata/photoshop/PhotoshopReader.java @@ -21,6 +21,7 @@ package com.drew.metadata.photoshop; import com.drew.imaging.ImageProcessingException; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentMetadataReader; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.ByteArrayReader; @@ -56,18 +57,18 @@ public Iterable getSegmentTypes() return Collections.singletonList(JpegSegmentType.APPD); } - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) { final int preambleLength = JPEG_SEGMENT_PREAMBLE.length(); - for (byte[] segmentBytes : segments) { + for (JpegSegmentInfo info : segments) { // Ensure data starts with the necessary preamble - if (segmentBytes.length < preambleLength + 1 || !JPEG_SEGMENT_PREAMBLE.equals(new String(segmentBytes, 0, preambleLength))) + if (info.bytes.length < preambleLength + 1 || !JPEG_SEGMENT_PREAMBLE.equals(new String(info.bytes, 0, preambleLength))) continue; extract( - new SequentialByteArrayReader(segmentBytes, preambleLength + 1), - segmentBytes.length - preambleLength - 1, + new SequentialByteArrayReader(info.bytes, preambleLength + 1), + info.bytes.length - preambleLength - 1, metadata); } } diff --git a/Source/com/drew/metadata/tiff/DirectoryTiffHandler.java b/Source/com/drew/metadata/tiff/DirectoryTiffHandler.java index 9a06e95d4..c418d0107 100644 --- a/Source/com/drew/metadata/tiff/DirectoryTiffHandler.java +++ b/Source/com/drew/metadata/tiff/DirectoryTiffHandler.java @@ -95,6 +95,11 @@ public void error(@NotNull String message) getCurrentOrErrorDirectory().addError(message); } + @NotNull + public Directory getCurrentDirectory() { + return _currentDirectory; + } + @NotNull private Directory getCurrentOrErrorDirectory() { diff --git a/Source/com/drew/metadata/xmp/XmpReader.java b/Source/com/drew/metadata/xmp/XmpReader.java index bfcb2bb94..44c66170e 100644 --- a/Source/com/drew/metadata/xmp/XmpReader.java +++ b/Source/com/drew/metadata/xmp/XmpReader.java @@ -26,6 +26,7 @@ import com.adobe.xmp.XMPMetaFactory; import com.adobe.xmp.impl.ByteBuffer; import com.adobe.xmp.properties.XMPPropertyInfo; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentMetadataReader; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.SequentialByteArrayReader; @@ -85,24 +86,24 @@ public Iterable getSegmentTypes() * @param metadata The {@link Metadata} object into which extracted values should be merged. * @param segmentType The {@link JpegSegmentType} being read. */ - public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) + public void readJpegSegments(@NotNull Iterable segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType) { final int preambleLength = XMP_JPEG_PREAMBLE.length(); final int extensionPreambleLength = XMP_EXTENSION_JPEG_PREAMBLE.length(); String extendedXMPGUID = null; byte[] extendedXMPBuffer = null; - for (byte[] segmentBytes : segments) { + for (JpegSegmentInfo info : segments) { // XMP in a JPEG file has an identifying preamble which is not valid XML - if (segmentBytes.length >= preambleLength) { + if (info.bytes.length >= preambleLength) { // NOTE we expect the full preamble here, but some images (such as that reported on GitHub #102) // start with "XMP\0://ns.adobe.com/xap/1.0/" which appears to be an error but is easily recovered // from. In such cases, the actual XMP data begins at the same offset. - if (XMP_JPEG_PREAMBLE.equalsIgnoreCase(new String(segmentBytes, 0, preambleLength)) || - "XMP".equalsIgnoreCase(new String(segmentBytes, 0, 3))) { + if (XMP_JPEG_PREAMBLE.equalsIgnoreCase(new String(info.bytes, 0, preambleLength)) || + "XMP".equalsIgnoreCase(new String(info.bytes, 0, 3))) { - byte[] xmlBytes = new byte[segmentBytes.length - preambleLength]; - System.arraycopy(segmentBytes, preambleLength, xmlBytes, 0, xmlBytes.length); + byte[] xmlBytes = new byte[info.bytes.length - preambleLength]; + System.arraycopy(info.bytes, preambleLength, xmlBytes, 0, xmlBytes.length); extract(xmlBytes, metadata); // Check in the Standard XMP if there should be a Extended XMP part in other chunks. extendedXMPGUID = getExtendedXMPGUID(metadata); @@ -112,10 +113,10 @@ public void readJpegSegments(@NotNull Iterable segments, @NotNull Metada // If we know that there's Extended XMP chunks, look for them. if (extendedXMPGUID != null && - segmentBytes.length >= extensionPreambleLength && - XMP_EXTENSION_JPEG_PREAMBLE.equalsIgnoreCase(new String(segmentBytes, 0, extensionPreambleLength))) { + info.bytes.length >= extensionPreambleLength && + XMP_EXTENSION_JPEG_PREAMBLE.equalsIgnoreCase(new String(info.bytes, 0, extensionPreambleLength))) { - extendedXMPBuffer = processExtendedXMPChunk(metadata, segmentBytes, extendedXMPGUID, extendedXMPBuffer); + extendedXMPBuffer = processExtendedXMPChunk(metadata, info.bytes, extendedXMPGUID, extendedXMPBuffer); } } diff --git a/Tests/com/drew/metadata/exif/ExifReaderTest.java b/Tests/com/drew/metadata/exif/ExifReaderTest.java index 1bf4cebfd..0464fa1c2 100644 --- a/Tests/com/drew/metadata/exif/ExifReaderTest.java +++ b/Tests/com/drew/metadata/exif/ExifReaderTest.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.exif; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.ByteArrayReader; import com.drew.lang.Rational; @@ -87,8 +88,8 @@ public void testReadJpegSegmentWithNoExifData() throws Exception { byte[] badExifData = new byte[]{ 1,2,3,4,5,6,7,8,9,10 }; Metadata metadata = new Metadata(); - ArrayList segments = new ArrayList(); - segments.add(badExifData); + ArrayList segments = new ArrayList(); + segments.add(new JpegSegmentInfo(badExifData)); new ExifReader().readJpegSegments(segments, metadata, JpegSegmentType.APP1); assertEquals(0, metadata.getDirectoryCount()); assertFalse(metadata.hasErrors()); diff --git a/Tests/com/drew/metadata/icc/IccReaderTest.java b/Tests/com/drew/metadata/icc/IccReaderTest.java index 04dd6bcc6..d4106f402 100644 --- a/Tests/com/drew/metadata/icc/IccReaderTest.java +++ b/Tests/com/drew/metadata/icc/IccReaderTest.java @@ -21,6 +21,7 @@ package com.drew.metadata.icc; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.lang.ByteArrayReader; import com.drew.metadata.Metadata; @@ -62,7 +63,7 @@ public void testReadJpegSegments_InvalidData() throws Exception byte[] app2Bytes = FileUtil.readBytes("Tests/Data/iccDataInvalid1.jpg.app2"); Metadata metadata = new Metadata(); - new IccReader().readJpegSegments(Arrays.asList(app2Bytes), metadata, JpegSegmentType.APP2); + new IccReader().readJpegSegments(Arrays.asList(new JpegSegmentInfo(app2Bytes)), metadata, JpegSegmentType.APP2); IccDirectory directory = metadata.getFirstDirectoryOfType(IccDirectory.class); @@ -76,7 +77,7 @@ public void testExtract_ProfileDateTime() throws Exception byte[] app2Bytes = FileUtil.readBytes("Tests/Data/withExifAndIptc.jpg.app2"); Metadata metadata = new Metadata(); - new IccReader().readJpegSegments(Arrays.asList(app2Bytes), metadata, JpegSegmentType.APP2); + new IccReader().readJpegSegments(Arrays.asList(new JpegSegmentInfo(app2Bytes)), metadata, JpegSegmentType.APP2); IccDirectory directory = metadata.getFirstDirectoryOfType(IccDirectory.class); diff --git a/Tests/com/drew/metadata/xmp/XmpReaderTest.java b/Tests/com/drew/metadata/xmp/XmpReaderTest.java index f83d31636..7416d8ddd 100644 --- a/Tests/com/drew/metadata/xmp/XmpReaderTest.java +++ b/Tests/com/drew/metadata/xmp/XmpReaderTest.java @@ -20,6 +20,7 @@ */ package com.drew.metadata.xmp; +import com.drew.imaging.jpeg.JpegSegmentInfo; import com.drew.imaging.jpeg.JpegSegmentType; import com.drew.metadata.Metadata; import com.drew.tools.FileUtil; @@ -41,8 +42,8 @@ public class XmpReaderTest public void setUp() throws Exception { Metadata metadata = new Metadata(); - List jpegSegments = new ArrayList(); - jpegSegments.add(FileUtil.readBytes("Tests/Data/withXmpAndIptc.jpg.app1.1")); + List jpegSegments = new ArrayList(); + jpegSegments.add(new JpegSegmentInfo(FileUtil.readBytes("Tests/Data/withXmpAndIptc.jpg.app1.1"))); new XmpReader().readJpegSegments(jpegSegments, metadata, JpegSegmentType.APP1); Collection xmpDirectories = metadata.getDirectoriesOfType(XmpDirectory.class); From 745175760c846803f1e2d42d6821a368fd76f9a9 Mon Sep 17 00:00:00 2001 From: Sergii Date: Wed, 18 Jul 2018 12:51:05 -0700 Subject: [PATCH 2/2] ThumbnailWrite method with storing the JpegSegments offset. formatting --- Source/com/drew/imaging/jpeg/JpegSegmentData.java | 3 ++- Source/com/drew/imaging/jpeg/JpegSegmentInfo.java | 12 ++++++++---- Source/com/drew/lang/RandomAccessReader.java | 6 ++++-- Source/com/drew/metadata/Directory.java | 6 ++++-- .../drew/metadata/exif/ExifThumbnailDirectory.java | 6 ++++-- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Source/com/drew/imaging/jpeg/JpegSegmentData.java b/Source/com/drew/imaging/jpeg/JpegSegmentData.java index 7abb2809d..4b2b3cc8f 100644 --- a/Source/com/drew/imaging/jpeg/JpegSegmentData.java +++ b/Source/com/drew/imaging/jpeg/JpegSegmentData.java @@ -39,7 +39,8 @@ * * @author Drew Noakes https://drewnoakes.com */ -public class JpegSegmentData { +public class JpegSegmentData +{ // TODO key this on JpegSegmentType rather than Byte, and hopefully lose much of the use of 'byte' with this class @NotNull private final HashMap> _segmentDataMap = new HashMap>(10); diff --git a/Source/com/drew/imaging/jpeg/JpegSegmentInfo.java b/Source/com/drew/imaging/jpeg/JpegSegmentInfo.java index 61a8349b0..d8a625cf0 100644 --- a/Source/com/drew/imaging/jpeg/JpegSegmentInfo.java +++ b/Source/com/drew/imaging/jpeg/JpegSegmentInfo.java @@ -1,19 +1,23 @@ package com.drew.imaging.jpeg; -public class JpegSegmentInfo { +public class JpegSegmentInfo +{ public byte[] bytes; public long fileOffset; - public JpegSegmentInfo() { + public JpegSegmentInfo() + { } - public JpegSegmentInfo(byte[] bytes) { + public JpegSegmentInfo(byte[] bytes) + { this.bytes = bytes; this.fileOffset = 0; } - public JpegSegmentInfo(byte[] bytes, long fileOffset) { + public JpegSegmentInfo(byte[] bytes, long fileOffset) + { this.bytes = bytes; this.fileOffset = fileOffset; } diff --git a/Source/com/drew/lang/RandomAccessReader.java b/Source/com/drew/lang/RandomAccessReader.java index 5864c1160..6a8b4e19f 100644 --- a/Source/com/drew/lang/RandomAccessReader.java +++ b/Source/com/drew/lang/RandomAccessReader.java @@ -48,12 +48,14 @@ public abstract class RandomAccessReader private boolean _isMotorolaByteOrder = true; private long _originOffset = 0; - public RandomAccessReader originOffset(long _originOffset) { + public RandomAccessReader originOffset(long _originOffset) + { this._originOffset = _originOffset; return this; } - public long getOriginOffset() { + public long getOriginOffset() + { return _originOffset; } diff --git a/Source/com/drew/metadata/Directory.java b/Source/com/drew/metadata/Directory.java index 944fe7fa3..c7ea809c8 100644 --- a/Source/com/drew/metadata/Directory.java +++ b/Source/com/drew/metadata/Directory.java @@ -92,12 +92,14 @@ protected Directory() // VARIOUS METHODS - public long getFileDataOffset() { + public long getFileDataOffset() + { return fileDataOffset + (_parent == null ? 0 : _parent.getFileDataOffset()); } - public void setFileDataOffset(long fileDataOffset) { + public void setFileDataOffset(long fileDataOffset) + { this.fileDataOffset = fileDataOffset; } diff --git a/Source/com/drew/metadata/exif/ExifThumbnailDirectory.java b/Source/com/drew/metadata/exif/ExifThumbnailDirectory.java index 1bc003002..908201128 100644 --- a/Source/com/drew/metadata/exif/ExifThumbnailDirectory.java +++ b/Source/com/drew/metadata/exif/ExifThumbnailDirectory.java @@ -80,9 +80,11 @@ protected HashMap getTagNameMap() return _tagNameMap; } - public boolean writeThumbnail(File imageFile, File thumbTargetFile) throws MetadataException, IOException { + public boolean writeThumbnail(File imageFile, File thumbTargetFile) throws MetadataException, IOException + { // after the extraction process, if we have the correct tags, we may be able to store thumbnail information - if (containsTag(ExifThumbnailDirectory.TAG_COMPRESSION)) { + if (containsTag(ExifThumbnailDirectory.TAG_COMPRESSION)) + { Integer offset = getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_OFFSET); Integer length = getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_LENGTH); if (offset != null && length != null) {