-
Notifications
You must be signed in to change notification settings - Fork 186
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: YavizGuldalf <[email protected]> Co-authored-by: Hasti Mohebali Zadeh <[email protected]> Co-authored-by: chenyi <[email protected]> Co-authored-by: regusta <[email protected]>
- Loading branch information
1 parent
ff13fa4
commit 0ab16f7
Showing
2 changed files
with
358 additions
and
0 deletions.
There are no files selected for viewing
173 changes: 173 additions & 0 deletions
173
src/main/java/org/apache/commons/imaging/formats/jpeg/exif/ExifOrientationRewriter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.apache.commons.imaging.formats.jpeg.exif; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import java.io.File; | ||
import java.io.FileOutputStream; | ||
import java.io.IOException; | ||
import java.nio.ByteOrder; | ||
import java.util.ArrayList; | ||
|
||
import org.apache.commons.imaging.ImageReadException; | ||
import org.apache.commons.imaging.ImageWriteException; | ||
import org.apache.commons.imaging.Imaging; | ||
import org.apache.commons.imaging.common.bytesource.ByteSource; | ||
import org.apache.commons.imaging.common.bytesource.ByteSourceArray; | ||
import org.apache.commons.imaging.common.bytesource.ByteSourceFile; | ||
import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; | ||
import org.apache.commons.imaging.formats.tiff.TiffContents; | ||
import org.apache.commons.imaging.formats.tiff.TiffHeader; | ||
import org.apache.commons.imaging.formats.tiff.TiffImageMetadata; | ||
import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants; | ||
import org.apache.commons.imaging.formats.tiff.write.TiffOutputDirectory; | ||
import org.apache.commons.imaging.formats.tiff.write.TiffOutputField; | ||
import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet; | ||
|
||
public class ExifOrientationRewriter { | ||
|
||
public enum Orientation { | ||
HORIZONTAL((short)1), | ||
MIRROR_HORIZONTAL((short)2), | ||
ROTATE_180((short)3), | ||
MIRROR_VERTICAL((short)4), | ||
MIRROR_HORIZONTAL_AND_ROTATE_270((short)5), | ||
ROTATE_90((short)6), | ||
MIRROR_HORIZONTAL_AND_ROTATE_90((short)7), | ||
ROTATE_270((short)8); | ||
|
||
private short val; | ||
|
||
Orientation(short orVal) { | ||
this.val = orVal; | ||
} | ||
|
||
public short getVal() { | ||
return val; | ||
} | ||
} | ||
|
||
private ByteSource fileSrc; | ||
|
||
public ExifOrientationRewriter(File imageFile) { | ||
fileSrc = new ByteSourceFile(imageFile); | ||
} | ||
public ExifOrientationRewriter(byte[] byteArray) { | ||
fileSrc = new ByteSourceArray(byteArray); | ||
} | ||
public ExifOrientationRewriter(ByteSource byteSource) { | ||
fileSrc = byteSource; | ||
} | ||
|
||
/*** | ||
* Get the orientation (enum) of the current image | ||
* Returns horizontal by default | ||
* @return Orientation enum | ||
* @throws IOException | ||
* @throws ImageReadException | ||
* @throws ImageWriteException | ||
*/ | ||
public Orientation getExifOrientation() throws IOException, ImageReadException, ImageWriteException { | ||
|
||
final JpegImageMetadata metadata = (JpegImageMetadata) Imaging.getMetadata(this.fileSrc.getAll()); | ||
|
||
if (metadata == null) { | ||
return Orientation.HORIZONTAL; | ||
} | ||
|
||
final TiffImageMetadata exifMetadata = metadata.getExif(); | ||
|
||
if (exifMetadata == null) { | ||
return Orientation.HORIZONTAL; | ||
} | ||
|
||
final TiffOutputSet outputSet = exifMetadata.getOutputSet(); | ||
|
||
TiffOutputDirectory tod = outputSet.getRootDirectory(); | ||
if (tod == null) { | ||
return Orientation.HORIZONTAL; | ||
} | ||
|
||
TiffOutputField tof = tod.findField(TiffTagConstants.TIFF_TAG_ORIENTATION); | ||
if (tof == null) { | ||
return Orientation.HORIZONTAL; | ||
} | ||
|
||
short imageOrientationVal = (short) exifMetadata.getFieldValue(TiffTagConstants.TIFF_TAG_ORIENTATION); | ||
|
||
for (Orientation orientation : Orientation.values()) { | ||
if(orientation.getVal() == imageOrientationVal) { | ||
return orientation; | ||
} | ||
} | ||
|
||
return Orientation.HORIZONTAL; | ||
} | ||
|
||
/** | ||
* A method that sets a new value to the orientation field in the EXIF metadata of a JPEG file. | ||
* @param orientation the value as a enum of the direction to set as the new EXIF orientation | ||
* | ||
*/ | ||
public void setExifOrientation(Orientation orientation) throws ImageWriteException, IOException, ImageReadException { | ||
|
||
JpegImageMetadata metadata = (JpegImageMetadata) Imaging.getMetadata(this.fileSrc.getAll()); | ||
|
||
if (metadata == null) { | ||
metadata = new JpegImageMetadata(null, new TiffImageMetadata(new TiffContents(new TiffHeader(ByteOrder.BIG_ENDIAN, 0, 0), new ArrayList<>(), new ArrayList<>()))); | ||
} | ||
|
||
TiffImageMetadata exifMetadata = metadata.getExif(); | ||
|
||
if (exifMetadata == null) { | ||
exifMetadata = new TiffImageMetadata(new TiffContents(new TiffHeader(ByteOrder.BIG_ENDIAN, 0, 0), new ArrayList<>(), new ArrayList<>())); | ||
} | ||
|
||
final TiffOutputSet outputSet = exifMetadata.getOutputSet(); | ||
|
||
TiffOutputDirectory tod = outputSet.getOrCreateRootDirectory(); | ||
tod.removeField(TiffTagConstants.TIFF_TAG_ORIENTATION); | ||
tod.add(TiffTagConstants.TIFF_TAG_ORIENTATION, orientation.getVal()); | ||
|
||
final ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||
new ExifRewriter().updateExifMetadataLossy(this.fileSrc, baos, outputSet); | ||
|
||
this.fileSrc = new ByteSourceArray(baos.toByteArray()); | ||
} | ||
|
||
/** | ||
* @return the ByteSource of the current file | ||
*/ | ||
public ByteSource getOutput() { | ||
return fileSrc; | ||
} | ||
|
||
/** | ||
* Writes Bytesource to file with given path | ||
* @param path String of the path in which the file is written | ||
* @throws IOException | ||
*/ | ||
public void getOutput(String path) | ||
throws IOException { | ||
final File tempFile = new File(path); | ||
try (FileOutputStream outputStream = new FileOutputStream(tempFile)) { | ||
outputStream.write(fileSrc.getAll()); | ||
} | ||
} | ||
|
||
} |
185 changes: 185 additions & 0 deletions
185
src/test/java/org/apache/commons/imaging/formats/jpeg/exif/ExifOrientationRewriterTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
|
||
package org.apache.commons.imaging.formats.jpeg.exif; | ||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import java.io.File; | ||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.util.List; | ||
|
||
import org.apache.commons.imaging.Imaging; | ||
import org.apache.commons.imaging.ImageReadException; | ||
import org.apache.commons.imaging.ImageWriteException; | ||
import org.apache.commons.imaging.common.bytesource.ByteSource; | ||
import org.apache.commons.imaging.common.bytesource.ByteSourceFile; | ||
import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; | ||
import org.apache.commons.imaging.formats.jpeg.exif.ExifOrientationRewriter.Orientation; | ||
import org.apache.commons.imaging.formats.tiff.TiffImageMetadata; | ||
import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants; | ||
import org.apache.commons.imaging.formats.tiff.write.TiffOutputSet; | ||
import org.apache.commons.io.FileUtils; | ||
import org.junit.jupiter.api.Test; | ||
|
||
public class ExifOrientationRewriterTest extends ExifBaseTest { | ||
|
||
@Test | ||
public void originalOrientationMatchesRewriterGet() | ||
throws ImageReadException, IOException, ImageWriteException { | ||
|
||
final List<File> images = getImagesWithExifData(); | ||
for (final File imageFile : images) { | ||
final JpegImageMetadata originalMetadata = (JpegImageMetadata) Imaging.getMetadata(imageFile); | ||
assertNotNull(originalMetadata); | ||
|
||
final TiffImageMetadata originalExifMetadata = originalMetadata.getExif(); | ||
assertNotNull(originalExifMetadata); | ||
|
||
short originalOrt = (short) originalExifMetadata.getFieldValue(TiffTagConstants.TIFF_TAG_ORIENTATION); | ||
|
||
ExifOrientationRewriter rewriter = new ExifOrientationRewriter(imageFile); | ||
short ort = rewriter.getExifOrientation().getVal(); | ||
|
||
assertEquals(ort, originalOrt); | ||
} | ||
|
||
} | ||
|
||
@Test | ||
public void rewrittenOrientationMatchesRewriterGet() | ||
throws ImageReadException, IOException, ImageWriteException { | ||
|
||
final List<File> images = getImagesWithExifData(); | ||
for (final File imageFile : images) { | ||
final ByteSource byteSource = new ByteSourceFile(imageFile); | ||
|
||
final JpegImageMetadata originalMetadata = (JpegImageMetadata) Imaging.getMetadata(imageFile); | ||
assertNotNull(originalMetadata); | ||
|
||
final TiffImageMetadata originalExifMetadata = originalMetadata.getExif(); | ||
assertNotNull(originalExifMetadata); | ||
|
||
final TiffOutputSet outputSet = originalExifMetadata.getOutputSet(); | ||
|
||
outputSet.getOrCreateRootDirectory().removeField(TiffTagConstants.TIFF_TAG_ORIENTATION); | ||
outputSet.getOrCreateRootDirectory().add(TiffTagConstants.TIFF_TAG_ORIENTATION, (short) TiffTagConstants.ORIENTATION_VALUE_ROTATE_270_CW); | ||
|
||
final ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||
|
||
new ExifRewriter().updateExifMetadataLossy(byteSource.getAll(), baos, | ||
outputSet); | ||
|
||
final byte[] bytes = baos.toByteArray(); | ||
final File tempFile = Files.createTempFile("inserted" + "_", ".jpg").toFile(); | ||
|
||
FileUtils.writeByteArrayToFile(tempFile, bytes); | ||
|
||
final ExifOrientationRewriter rewriter = new ExifOrientationRewriter(tempFile); | ||
short ort = rewriter.getExifOrientation().getVal(); | ||
|
||
assertEquals(ort, (short) TiffTagConstants.ORIENTATION_VALUE_ROTATE_270_CW); | ||
} | ||
|
||
} | ||
|
||
@Test | ||
public void getWithNullExifReturnsHorizontal() | ||
throws ImageReadException, IOException, ImageWriteException { | ||
|
||
final List<File> images = getImagesWithExifData(); | ||
for (final File imageFile : images) { | ||
final ByteSource byteSource = new ByteSourceFile(imageFile); | ||
final JpegImageMetadata originalMetadata = (JpegImageMetadata) Imaging.getMetadata(imageFile); | ||
assertNotNull(originalMetadata); | ||
|
||
final TiffImageMetadata originalExifMetadata = originalMetadata.getExif(); | ||
assertNotNull(originalExifMetadata); | ||
|
||
final ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||
new ExifRewriter().removeExifMetadata(byteSource, baos); | ||
final byte[] bytes = baos.toByteArray(); | ||
final File tempFile = Files.createTempFile("removed", ".jpg").toFile(); | ||
FileUtils.writeByteArrayToFile(tempFile, bytes); | ||
|
||
assertFalse(hasExifData(tempFile)); | ||
|
||
final ExifOrientationRewriter rewriter = new ExifOrientationRewriter(tempFile); | ||
short ort = rewriter.getExifOrientation().getVal(); | ||
|
||
assertEquals(ort, Orientation.HORIZONTAL.getVal()); | ||
} | ||
|
||
} | ||
|
||
@Test | ||
void testSetExifOrientation() | ||
throws IOException, ImageReadException, ImageWriteException { | ||
|
||
final List<File> images = getImagesWithExifData(); | ||
for (final File imageFile : images) { | ||
ExifOrientationRewriter eor = new ExifOrientationRewriter(imageFile); | ||
ExifOrientationRewriter.Orientation eo = ExifOrientationRewriter.Orientation.ROTATE_180; | ||
eor.setExifOrientation(eo); | ||
|
||
ByteSource bs = eor.getOutput(); | ||
final JpegImageMetadata newMetadata = (JpegImageMetadata) Imaging.getMetadata(bs.getAll()); | ||
final TiffImageMetadata newExifMetadata = newMetadata.getExif(); | ||
|
||
assertNotNull(newExifMetadata, "The new EXIF metadata is null"); | ||
assertEquals(newExifMetadata.getFieldValue(TiffTagConstants.TIFF_TAG_ORIENTATION), Short.valueOf(eo.getVal()), "The orientation field in the EXIF metadata is not set correctly"); | ||
} | ||
} | ||
|
||
@Test | ||
public void setWithNullExifDoesNotThrow() | ||
throws ImageReadException, IOException, ImageWriteException { | ||
|
||
final List<File> images = getImagesWithExifData(); | ||
for (final File imageFile : images) { | ||
final ByteSource byteSource = new ByteSourceFile(imageFile); | ||
final JpegImageMetadata originalMetadata = (JpegImageMetadata) Imaging.getMetadata(imageFile); | ||
assertNotNull(originalMetadata); | ||
|
||
final TiffImageMetadata originalExifMetadata = originalMetadata.getExif(); | ||
assertNotNull(originalExifMetadata); | ||
|
||
final ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||
new ExifRewriter().removeExifMetadata(byteSource, baos); | ||
final byte[] bytes = baos.toByteArray(); | ||
final File tempFile = Files.createTempFile("removed", ".jpg").toFile(); | ||
FileUtils.writeByteArrayToFile(tempFile, bytes); | ||
|
||
assertFalse(hasExifData(tempFile)); | ||
|
||
final ExifOrientationRewriter rewriter = new ExifOrientationRewriter(tempFile); | ||
assertDoesNotThrow(() -> rewriter.setExifOrientation(Orientation.ROTATE_180)); | ||
|
||
ByteSource bs = rewriter.getOutput(); | ||
final JpegImageMetadata newMetadata = (JpegImageMetadata) Imaging.getMetadata(bs.getAll()); | ||
final TiffImageMetadata newExifMetadata = newMetadata.getExif(); | ||
|
||
assertNotNull(newExifMetadata, "The new EXIF metadata is null"); | ||
assertEquals(newExifMetadata.getFieldValue(TiffTagConstants.TIFF_TAG_ORIENTATION), Orientation.ROTATE_180.getVal(), "The orientation field in the EXIF metadata is not set correctly"); | ||
} | ||
} | ||
} |