Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ThumbnailWrite method with storing the JpegSegments offset #353

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Source/com/drew/imaging/jpeg/JpegMetadataReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public static void processJpegSegmentData(Metadata metadata, Iterable<JpegSegmen
// Pass the appropriate byte arrays to each reader.
for (JpegSegmentMetadataReader reader : readers) {
for (JpegSegmentType segmentType : reader.getSegmentTypes()) {
reader.readJpegSegments(segmentData.getSegments(segmentType), metadata, segmentType);
reader.readJpegSegments(segmentData.getSegmentsInfo(segmentType), metadata, segmentType);
}
}
}
Expand Down
67 changes: 55 additions & 12 deletions Source/com/drew/imaging/jpeg/JpegSegmentData.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ 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<Byte, List<byte[]>> _segmentDataMap = new HashMap<Byte, List<byte[]>>(10);
private final HashMap<Byte, List<JpegSegmentInfo>> _segmentDataMap = new HashMap<Byte, List<JpegSegmentInfo>>(10);

/**
* Adds segment bytes to the collection.
Expand All @@ -54,7 +54,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));
}

/**
Expand Down Expand Up @@ -125,10 +137,10 @@ public byte[] getSegment(@NotNull JpegSegmentType segmentType, int occurrence)
@Nullable
public byte[] getSegment(byte segmentType, int occurrence)
{
final List<byte[]> segmentList = getSegmentList(segmentType);
final List<JpegSegmentInfo> segmentList = getSegmentList(segmentType);

return segmentList != null && segmentList.size() > occurrence
? segmentList.get(occurrence)
? segmentList.get(occurrence).bytes
: null;
}

Expand All @@ -144,6 +156,18 @@ public Iterable<byte[]> 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<JpegSegmentInfo> 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.
*
Expand All @@ -153,24 +177,43 @@ public Iterable<byte[]> getSegments(@NotNull JpegSegmentType segmentType)
@NotNull
public Iterable<byte[]> getSegments(byte segmentType)
{
final List<byte[]> segmentList = getSegmentList(segmentType);
return segmentList == null ? new ArrayList<byte[]>() : segmentList;
final List<JpegSegmentInfo> segmentList = getSegmentList(segmentType);
List<byte[]> segmentDataList = new ArrayList<byte[]>();
if (segmentList != null) {
for (JpegSegmentInfo info : segmentList) {
segmentDataList.add(info.bytes);
}
}
return segmentList == null ? new ArrayList<byte[]>() : 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<JpegSegmentInfo> getSegmentsInfo(byte segmentType)
{
final List<JpegSegmentInfo> segmentList = getSegmentList(segmentType);
return segmentList == null ? new ArrayList<JpegSegmentInfo>() : segmentList;
}

@Nullable
private List<byte[]> getSegmentList(byte segmentType)
private List<JpegSegmentInfo> getSegmentList(byte segmentType)
{
return _segmentDataMap.get(segmentType);
}

@NotNull
private List<byte[]> getOrCreateSegmentList(byte segmentType)
private List<JpegSegmentInfo> getOrCreateSegmentList(byte segmentType)
{
List<byte[]> segmentList;
List<JpegSegmentInfo> segmentList;
if (_segmentDataMap.containsKey(segmentType)) {
segmentList = _segmentDataMap.get(segmentType);
} else {
segmentList = new ArrayList<byte[]>();
segmentList = new ArrayList<JpegSegmentInfo>();
_segmentDataMap.put(segmentType, segmentList);
}
return segmentList;
Expand All @@ -195,7 +238,7 @@ public int getSegmentCount(@NotNull JpegSegmentType segmentType)
*/
public int getSegmentCount(byte segmentType)
{
final List<byte[]> segmentList = getSegmentList(segmentType);
final List<JpegSegmentInfo> segmentList = getSegmentList(segmentType);
return segmentList == null ? 0 : segmentList.size();
}

Expand All @@ -222,7 +265,7 @@ public void removeSegmentOccurrence(@NotNull JpegSegmentType segmentType, int oc
@SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
public void removeSegmentOccurrence(byte segmentType, int occurrence)
{
final List<byte[]> segmentList = _segmentDataMap.get(segmentType);
final List<JpegSegmentInfo> segmentList = _segmentDataMap.get(segmentType);
segmentList.remove(occurrence);
}

Expand Down
24 changes: 24 additions & 0 deletions Source/com/drew/imaging/jpeg/JpegSegmentInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte[]> segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType);
void readJpegSegments(@NotNull final Iterable<JpegSegmentInfo> segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType);
}
3 changes: 2 additions & 1 deletion Source/com/drew/imaging/jpeg/JpegSegmentReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
9 changes: 9 additions & 0 deletions Source/com/drew/imaging/tiff/TiffReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions Source/com/drew/lang/RandomAccessReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@
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);

Expand Down
13 changes: 13 additions & 0 deletions Source/com/drew/metadata/Directory.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ public abstract class Directory
@Nullable
private Directory _parent;

private long fileDataOffset = 0;

// ABSTRACT METHODS

/**
Expand All @@ -90,6 +92,17 @@ 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.
*/
Expand Down
9 changes: 5 additions & 4 deletions Source/com/drew/metadata/adobe/AdobeJpegReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -49,11 +50,11 @@ public Iterable<JpegSegmentType> getSegmentTypes()
return Collections.singletonList(JpegSegmentType.APPE);
}

public void readJpegSegments(@NotNull Iterable<byte[]> segments, @NotNull Metadata metadata, @NotNull JpegSegmentType segmentType)
public void readJpegSegments(@NotNull Iterable<JpegSegmentInfo> 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);
}
}

Expand Down
9 changes: 5 additions & 4 deletions Source/com/drew/metadata/exif/ExifReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -53,15 +54,15 @@ public Iterable<JpegSegmentType> getSegmentTypes()
return Collections.singletonList(JpegSegmentType.APP1);
}

public void readJpegSegments(@NotNull final Iterable<byte[]> segments, @NotNull final Metadata metadata, @NotNull final JpegSegmentType segmentType)
public void readJpegSegments(@NotNull final Iterable<JpegSegmentInfo> 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());
}
}

Expand Down
39 changes: 39 additions & 0 deletions Source/com/drew/metadata/exif/ExifThumbnailDirectory.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -77,4 +79,41 @@ protected HashMap<Integer, String> 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.");
}
}
Loading