Add support for saving attributes for DNG files
Also add support for IMAGE_TYPE_UNKNOWN files that do not have an identifiable file extension, but follows the TIFF specification. Also fixes issue where certain thumbnail-only tags were not being replaced after primary/preview/thumbnail image data was swapped. Bug: 193010129, Bug: 193010735 Test: atest ExifInterfaceTest Change-Id: I098cf1a0730d0cb0f1d6010a21b3ec7ada8dc6b7
This commit is contained in:
@@ -82,7 +82,7 @@ import java.util.zip.CRC32;
|
||||
* Supported for reading: JPEG, PNG, WebP, HEIF, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW, RAF,
|
||||
* AVIF.
|
||||
* <p>
|
||||
* Supported for writing: JPEG, PNG, WebP.
|
||||
* Supported for writing: JPEG, PNG, WebP, DNG.
|
||||
* <p>
|
||||
* Note: JPEG and HEIF files may contain XMP data either inside the Exif data chunk or outside of
|
||||
* it. This class will search both locations for XMP data, but if XMP data exist both inside and
|
||||
@@ -1294,6 +1294,7 @@ public class ExifInterface {
|
||||
new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
|
||||
new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
|
||||
new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
|
||||
new ExifTag(TAG_XMP, 700, IFD_FORMAT_BYTE),
|
||||
new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
|
||||
new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
|
||||
new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
|
||||
@@ -1361,12 +1362,6 @@ public class ExifInterface {
|
||||
new ExifTag(TAG_ORF_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_BYTE)
|
||||
};
|
||||
|
||||
// Tags for indicating the thumbnail offset and length
|
||||
private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG =
|
||||
new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG);
|
||||
private static final ExifTag JPEG_INTERCHANGE_FORMAT_LENGTH_TAG =
|
||||
new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG);
|
||||
|
||||
// Mappings from tag number to tag name and each item represents one IFD tag group.
|
||||
private static final HashMap[] sExifTagMapsForReading = new HashMap[EXIF_TAGS.length];
|
||||
// Mappings from tag name to tag number and each item represents one IFD tag group.
|
||||
@@ -2078,7 +2073,7 @@ public class ExifInterface {
|
||||
* {@link #setAttribute(String,String)} to set all attributes to write and
|
||||
* make a single call rather than multiple calls for each attribute.
|
||||
* <p>
|
||||
* This method is supported for JPEG, PNG and WebP files.
|
||||
* This method is supported for JPEG, PNG, WebP, and DNG files.
|
||||
* <p class="note">
|
||||
* Note: after calling this method, any attempts to obtain range information
|
||||
* from {@link #getAttributeRange(String)} or {@link #getThumbnailRange()}
|
||||
@@ -2091,13 +2086,17 @@ public class ExifInterface {
|
||||
*/
|
||||
public void saveAttributes() throws IOException {
|
||||
if (!isSupportedFormatForSavingAttributes()) {
|
||||
throw new IOException("ExifInterface only supports saving attributes on JPEG, PNG, "
|
||||
+ "or WebP formats.");
|
||||
throw new IOException("ExifInterface only supports saving attributes for JPEG, PNG, "
|
||||
+ "WebP, and DNG formats.");
|
||||
}
|
||||
if (mIsInputStream || (mSeekableFileDescriptor == null && mFilename == null)) {
|
||||
throw new IOException(
|
||||
"ExifInterface does not support saving attributes for the current input.");
|
||||
}
|
||||
if (mHasThumbnail && mHasThumbnailStrips && !mAreThumbnailStripsConsecutive) {
|
||||
throw new IOException("ExifInterface does not support saving attributes when the image "
|
||||
+ "file has non-consecutive thumbnail strips");
|
||||
}
|
||||
|
||||
// Remember the fact that we've changed the file on disk from what was
|
||||
// originally parsed, meaning we can't answer range questions
|
||||
@@ -2146,6 +2145,10 @@ public class ExifInterface {
|
||||
savePngAttributes(bufferedIn, bufferedOut);
|
||||
} else if (mMimeType == IMAGE_TYPE_WEBP) {
|
||||
saveWebpAttributes(bufferedIn, bufferedOut);
|
||||
} else if (mMimeType == IMAGE_TYPE_DNG || mMimeType == IMAGE_TYPE_UNKNOWN) {
|
||||
ByteOrderedDataOutputStream dataOutputStream =
|
||||
new ByteOrderedDataOutputStream(bufferedOut, ByteOrder.BIG_ENDIAN);
|
||||
writeExifSegment(dataOutputStream);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -2285,9 +2288,9 @@ public class ExifInterface {
|
||||
}
|
||||
|
||||
ExifAttribute imageLengthAttribute =
|
||||
(ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_LENGTH);
|
||||
(ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_THUMBNAIL_IMAGE_LENGTH);
|
||||
ExifAttribute imageWidthAttribute =
|
||||
(ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_IMAGE_WIDTH);
|
||||
(ExifAttribute) mAttributes[IFD_TYPE_THUMBNAIL].get(TAG_THUMBNAIL_IMAGE_WIDTH);
|
||||
if (imageLengthAttribute != null && imageWidthAttribute != null) {
|
||||
int imageLength = imageLengthAttribute.getIntValue(mExifByteOrder);
|
||||
int imageWidth = imageWidthAttribute.getIntValue(mExifByteOrder);
|
||||
@@ -2559,7 +2562,12 @@ public class ExifInterface {
|
||||
mIsInputStream = false;
|
||||
try {
|
||||
in = new FileInputStream(filename);
|
||||
ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(in.getFD());
|
||||
ParcelFileDescriptor modernFd;
|
||||
try {
|
||||
modernFd = FileUtils.convertToModernFd(in.getFD());
|
||||
} catch (IOException e) {
|
||||
modernFd = null;
|
||||
}
|
||||
if (modernFd != null) {
|
||||
closeQuietly(in);
|
||||
in = new FileInputStream(modernFd.getFileDescriptor());
|
||||
@@ -2927,10 +2935,12 @@ public class ExifInterface {
|
||||
if (in.skipBytes(1) != 1) {
|
||||
throw new IOException("Invalid SOFx");
|
||||
}
|
||||
mAttributes[imageType].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong(
|
||||
in.readUnsignedShort(), mExifByteOrder));
|
||||
mAttributes[imageType].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong(
|
||||
in.readUnsignedShort(), mExifByteOrder));
|
||||
mAttributes[imageType].put(imageType != IFD_TYPE_THUMBNAIL
|
||||
? TAG_IMAGE_LENGTH : TAG_THUMBNAIL_IMAGE_LENGTH,
|
||||
ExifAttribute.createULong(in.readUnsignedShort(), mExifByteOrder));
|
||||
mAttributes[imageType].put(imageType != IFD_TYPE_THUMBNAIL
|
||||
? TAG_IMAGE_WIDTH : TAG_THUMBNAIL_IMAGE_WIDTH,
|
||||
ExifAttribute.createULong(in.readUnsignedShort(), mExifByteOrder));
|
||||
length -= 5;
|
||||
break;
|
||||
}
|
||||
@@ -4501,6 +4511,17 @@ public class ExifInterface {
|
||||
if (!isThumbnail(mAttributes[IFD_TYPE_THUMBNAIL])) {
|
||||
Log.d(TAG, "No image meets the size requirements of a thumbnail image.");
|
||||
}
|
||||
|
||||
// TAG_THUMBNAIL_* tags should be replaced with TAG_* equivalents and vice versa if needed.
|
||||
replaceInvalidTags(IFD_TYPE_PRIMARY, TAG_THUMBNAIL_ORIENTATION, TAG_ORIENTATION);
|
||||
replaceInvalidTags(IFD_TYPE_PRIMARY, TAG_THUMBNAIL_IMAGE_LENGTH, TAG_IMAGE_LENGTH);
|
||||
replaceInvalidTags(IFD_TYPE_PRIMARY, TAG_THUMBNAIL_IMAGE_WIDTH, TAG_IMAGE_WIDTH);
|
||||
replaceInvalidTags(IFD_TYPE_PREVIEW, TAG_THUMBNAIL_ORIENTATION, TAG_ORIENTATION);
|
||||
replaceInvalidTags(IFD_TYPE_PREVIEW, TAG_THUMBNAIL_IMAGE_LENGTH, TAG_IMAGE_LENGTH);
|
||||
replaceInvalidTags(IFD_TYPE_PREVIEW, TAG_THUMBNAIL_IMAGE_WIDTH, TAG_IMAGE_WIDTH);
|
||||
replaceInvalidTags(IFD_TYPE_THUMBNAIL, TAG_ORIENTATION, TAG_THUMBNAIL_ORIENTATION);
|
||||
replaceInvalidTags(IFD_TYPE_THUMBNAIL, TAG_IMAGE_LENGTH, TAG_THUMBNAIL_IMAGE_LENGTH);
|
||||
replaceInvalidTags(IFD_TYPE_THUMBNAIL, TAG_IMAGE_WIDTH, TAG_THUMBNAIL_IMAGE_WIDTH);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4581,8 +4602,15 @@ public class ExifInterface {
|
||||
removeAttribute(tag.name);
|
||||
}
|
||||
// Remove old thumbnail data
|
||||
removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
|
||||
removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
|
||||
if (mHasThumbnail) {
|
||||
if (mHasThumbnailStrips) {
|
||||
removeAttribute(TAG_STRIP_OFFSETS);
|
||||
removeAttribute(TAG_STRIP_BYTE_COUNTS);
|
||||
} else {
|
||||
removeAttribute(TAG_JPEG_INTERCHANGE_FORMAT);
|
||||
removeAttribute(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove null value tags.
|
||||
for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) {
|
||||
@@ -4609,10 +4637,17 @@ public class ExifInterface {
|
||||
ExifAttribute.createULong(0, mExifByteOrder));
|
||||
}
|
||||
if (mHasThumbnail) {
|
||||
mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
|
||||
ExifAttribute.createULong(0, mExifByteOrder));
|
||||
mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
|
||||
ExifAttribute.createULong(mThumbnailLength, mExifByteOrder));
|
||||
if (mHasThumbnailStrips) {
|
||||
mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_STRIP_OFFSETS,
|
||||
ExifAttribute.createUShort(0, mExifByteOrder));
|
||||
mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_STRIP_BYTE_COUNTS,
|
||||
ExifAttribute.createUShort(mThumbnailLength, mExifByteOrder));
|
||||
} else {
|
||||
mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT,
|
||||
ExifAttribute.createULong(0, mExifByteOrder));
|
||||
mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
|
||||
ExifAttribute.createULong(mThumbnailLength, mExifByteOrder));
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
|
||||
@@ -4641,8 +4676,13 @@ public class ExifInterface {
|
||||
}
|
||||
if (mHasThumbnail) {
|
||||
int thumbnailOffset = position;
|
||||
mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
|
||||
ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
|
||||
if (mHasThumbnailStrips) {
|
||||
mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_STRIP_OFFSETS,
|
||||
ExifAttribute.createUShort(thumbnailOffset, mExifByteOrder));
|
||||
} else {
|
||||
mAttributes[IFD_TYPE_THUMBNAIL].put(TAG_JPEG_INTERCHANGE_FORMAT,
|
||||
ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
|
||||
}
|
||||
// Need to add mExifOffset, which is the offset to the EXIF data segment
|
||||
mThumbnailOffset = thumbnailOffset + mExifOffset;
|
||||
position += mThumbnailLength;
|
||||
@@ -5206,9 +5246,20 @@ public class ExifInterface {
|
||||
}
|
||||
}
|
||||
|
||||
private void replaceInvalidTags(@IfdType int ifdType, String invalidTag, String validTag) {
|
||||
if (!mAttributes[ifdType].isEmpty()) {
|
||||
if (mAttributes[ifdType].get(invalidTag) != null) {
|
||||
mAttributes[ifdType].put(validTag,
|
||||
mAttributes[ifdType].get(invalidTag));
|
||||
mAttributes[ifdType].remove(invalidTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSupportedFormatForSavingAttributes() {
|
||||
if (mIsSupportedFile && (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_PNG
|
||||
|| mMimeType == IMAGE_TYPE_WEBP)) {
|
||||
|| mMimeType == IMAGE_TYPE_WEBP || mMimeType == IMAGE_TYPE_DNG
|
||||
|| mMimeType == IMAGE_TYPE_UNKNOWN)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user