Init TIFFUtilities for mereging, splitting and rotating tiff pages

This commit is contained in:
Schmidor 2015-12-02 01:48:38 +01:00
parent 57402957ad
commit ad0d265f4f
6 changed files with 623 additions and 4 deletions

View File

@ -0,0 +1,415 @@
/*
* Copyright (c) 2013, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.contrib.tiff;
import com.twelvemonkeys.imageio.metadata.AbstractDirectory;
import com.twelvemonkeys.imageio.metadata.CompoundDirectory;
import com.twelvemonkeys.imageio.metadata.Directory;
import com.twelvemonkeys.imageio.metadata.Entry;
import com.twelvemonkeys.imageio.metadata.exif.EXIFReader;
import com.twelvemonkeys.imageio.metadata.exif.EXIFWriter;
import com.twelvemonkeys.imageio.metadata.exif.TIFF;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFBaseline;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFExtension;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriter;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFImageWriter.TIFFEntry;
import com.twelvemonkeys.lang.Validate;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* TIFFUtilities for manipulation TIFF Images and Metadata
*
* @author <a href="mailto:mail@schmidor.de">Oliver Schmidtmer</a>
* @author last modified by $Author$
* @version $Id$
*/
public class TIFFUtilities {
private TIFFUtilities() {
}
;
/**
* Merges all pages from the input TIFF files into one TIFF file at the
* output location.
*
* @param inputFiles
* @param outputFile
* @throws IOException
*/
public static void merge(List<File> inputFiles, File outputFile) throws IOException {
ImageOutputStream output = null;
try {
output = ImageIO.createImageOutputStream(outputFile);
for (File file : inputFiles) {
ImageInputStream input = null;
try {
input = ImageIO.createImageInputStream(file);
List<TIFFPage> pages = getPages(input);
writePages(output, pages);
}
finally {
if (input != null) {
input.close();
}
}
}
}
finally {
if (output != null) {
output.flush();
output.close();
}
}
}
/**
* Splits all pages from the input TIFF file to one file per page in the
* output directory.
*
* @param inputFile
* @param outputDirectory
* @return generated files
* @throws IOException
*/
public static List<File> split(File inputFile, File outputDirectory) throws IOException {
ImageInputStream input = null;
List<File> outputFiles = new ArrayList<>();
try {
input = ImageIO.createImageInputStream(inputFile);
List<TIFFPage> pages = getPages(input);
int pageNo = 1;
for (TIFFPage tiffPage : pages) {
ArrayList<TIFFPage> outputPages = new ArrayList<TIFFPage>(1);
ImageOutputStream outputStream = null;
try {
File outputFile = new File(outputDirectory, String.format("%04d", pageNo) + ".tif");
outputStream = ImageIO.createImageOutputStream(outputFile);
outputPages.clear();
outputPages.add(tiffPage);
writePages(outputStream, outputPages);
outputFiles.add(outputFile);
}
finally {
if (outputStream != null) {
outputStream.flush();
outputStream.close();
}
}
++pageNo;
}
}
finally {
if (input != null) {
input.close();
}
}
return outputFiles;
}
/**
* Rotates all pages of a TIFF file by changing TIFF.TAG_ORIENTATION.
* <p>
* NOTICE: TIFF.TAG_ORIENTATION is an advice how the image is meant do be
* displayed. Other metadata, such as width and height, relate to the image
* as how it is stored. The ImageIO TIFF plugin does not handle orientation.
* Use {@link TIFFUtilities#applyOrientation(BufferedImage, int)} for
* applying TIFF.TAG_ORIENTATION.
* </p>
*
* @param imageInput
* @param imageOutput
* @param degree Rotation amount, supports 90<EFBFBD>, 180<EFBFBD> and 270<EFBFBD>.
* @throws IOException
*/
public static void rotatePages(ImageInputStream imageInput, ImageOutputStream imageOutput, int degree)
throws IOException {
rotatePage(imageInput, imageOutput, degree, -1);
}
/**
* Rotates a page of a TIFF file by changing TIFF.TAG_ORIENTATION.
* <p>
* NOTICE: TIFF.TAG_ORIENTATION is an advice how the image is meant do be
* displayed. Other metadata, such as width and height, relate to the image
* as how it is stored. The ImageIO TIFF plugin does not handle orientation.
* Use {@link TIFFUtilities#applyOrientation(BufferedImage, int)} for
* applying TIFF.TAG_ORIENTATION.
* </p>
*
* @param imageInput
* @param imageOutput
* @param degree Rotation amount, supports 90<EFBFBD>, 180<EFBFBD> and 270<EFBFBD>.
* @param pageIndex page which should be rotated or -1 for all pages.
* @throws IOException
*/
public static void rotatePage(ImageInputStream imageInput, ImageOutputStream imageOutput, int degree, int pageIndex)
throws IOException {
ImageInputStream input = null;
try {
List<TIFFPage> pages = getPages(imageInput);
if (pageIndex != -1) {
pages.get(pageIndex).rotate(degree);
}
else {
for (TIFFPage tiffPage : pages) {
tiffPage.rotate(degree);
}
}
writePages(imageOutput, pages);
}
finally {
if (input != null) {
input.close();
}
}
}
public static List<TIFFPage> getPages(ImageInputStream imageInput) throws IOException {
ArrayList<TIFFPage> pages = new ArrayList<TIFFPage>();
CompoundDirectory IFDs = (CompoundDirectory) new EXIFReader().read(imageInput);
int pageCount = IFDs.directoryCount();
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
pages.add(new TIFFPage(IFDs.getDirectory(pageIndex), imageInput));
}
return pages;
}
public static void writePages(ImageOutputStream imageOutput, List<TIFFPage> pages) throws IOException {
EXIFWriter exif = new EXIFWriter();
long nextPagePos = imageOutput.getStreamPosition();
if (nextPagePos == 0) {
exif.writeTIFFHeader(imageOutput);
nextPagePos = imageOutput.getStreamPosition();
imageOutput.writeInt(0);
}
else {
// already has pages, so remember place of EOF to replace with
// IFD offset
nextPagePos -= 4;
}
for (TIFFPage tiffPage : pages) {
long ifdOffset = tiffPage.write(imageOutput, exif);
long tmp = imageOutput.getStreamPosition();
imageOutput.seek(nextPagePos);
imageOutput.writeInt((int) ifdOffset);
imageOutput.seek(tmp);
nextPagePos = tmp;
imageOutput.writeInt(0);
}
}
public static BufferedImage applyOrientation(BufferedImage input, int orientation) {
//TODO: Translations are currently off for non quadratic images
boolean flipExtends = false;
AffineTransform transform = AffineTransform.getTranslateInstance(input.getWidth() / 2, input.getHeight() / 2);
switch (orientation) {
case TIFFBaseline.ORIENTATION_TOPLEFT:
// normal
return input;
case TIFFExtension.ORIENTATION_TOPRIGHT:
// flipped vertically
transform.scale(-1, 1);
break;
case TIFFExtension.ORIENTATION_BOTRIGHT:
// rotated 180<EFBFBD>
transform.quadrantRotate(2);
break;
case TIFFExtension.ORIENTATION_BOTLEFT:
// flipped horizontally
transform.scale(1, -1);
break;
case TIFFExtension.ORIENTATION_LEFTTOP:
transform.scale(1, -1);
transform.quadrantRotate(1);
flipExtends = true;
break;
case TIFFExtension.ORIENTATION_RIGHTTOP:
// rotated 90<EFBFBD>
transform.quadrantRotate(1);
flipExtends = true;
break;
case TIFFExtension.ORIENTATION_RIGHTBOT:
transform.scale(-1, 1);
transform.quadrantRotate(1);
flipExtends = true;
break;
case TIFFExtension.ORIENTATION_LEFTBOT:
// rotated 270<EFBFBD>
transform.quadrantRotate(3);
flipExtends = true;
break;
}
int w = input.getWidth();
int h = input.getHeight();
if (flipExtends) {
w = input.getHeight();
h = input.getWidth();
}
BufferedImage output = new BufferedImage(w, h, input.getType());
transform.translate(-h / 2, -w / 2);
((Graphics2D) output.getGraphics()).drawImage(input, transform, null);
return output;
}
public static class TIFFPage {
private Directory IFD;
private ImageInputStream stream;
private TIFFPage(Directory IFD, ImageInputStream stream) {
this.IFD = IFD;
this.stream = stream;
}
private long write(ImageOutputStream outputStream, EXIFWriter exifWriter) throws IOException {
Entry stipOffsetsEntry = IFD.getEntryById(TIFF.TAG_STRIP_OFFSETS);
long[] offsets;
if (stipOffsetsEntry.valueCount() == 1) {
offsets = new long[] {(long) stipOffsetsEntry.getValue()};
}
else {
offsets = (long[]) stipOffsetsEntry.getValue();
}
Entry stipByteCountsEntry = IFD.getEntryById(TIFF.TAG_STRIP_BYTE_COUNTS);
long[] byteCounts;
if (stipOffsetsEntry.valueCount() == 1) {
byteCounts = new long[] {(long) stipByteCountsEntry.getValue()};
}
else {
byteCounts = (long[]) stipByteCountsEntry.getValue();
}
int[] newOffsets = new int[offsets.length];
for (int i = 0; i < offsets.length; i++) {
newOffsets[i] = (int) outputStream.getStreamPosition();
stream.seek(offsets[i]);
byte[] buffer = new byte[(int) byteCounts[i]];
stream.readFully(buffer);
outputStream.write(buffer);
}
ArrayList<Entry> newIFD = new ArrayList<Entry>();
Iterator<Entry> it = IFD.iterator();
while (it.hasNext()) {
newIFD.add(it.next());
}
newIFD.remove(stipOffsetsEntry);
newIFD.add(new TIFFEntry(TIFF.TAG_STRIP_OFFSETS, newOffsets));
return exifWriter.writeIFD(newIFD, outputStream);
}
/**
* Rotates the image by changing TIFF.TAG_ORIENTATION.
* <p>
* NOTICE: TIFF.TAG_ORIENTATION is an advice how the image is meant do
* be displayed. Other metadata, such as width and height, relate to the
* image as how it is stored. The ImageIO TIFF plugin does not handle
* orientation. Use
* {@link TIFFUtilities#applyOrientation(BufferedImage, int)} for
* applying TIFF.TAG_ORIENTATION.
* </p>
*
* @param degree Rotation amount, supports 90<EFBFBD>, 180<EFBFBD> and 270<EFBFBD>.
*/
public void rotate(int degree) {
Validate.isTrue(degree % 90 == 0 && degree > 0 && degree < 360,
"Only rotations by 90, 180 and 270 degree are supported");
ArrayList<Entry> newIDFData = new ArrayList<>();
Iterator<Entry> it = IFD.iterator();
while (it.hasNext()) {
newIDFData.add(it.next());
}
short orientation = TIFFBaseline.ORIENTATION_TOPLEFT;
Entry orientationEntry = IFD.getEntryById(TIFF.TAG_ORIENTATION);
if (orientationEntry != null) {
orientation = ((Number) orientationEntry.getValue()).shortValue();
newIDFData.remove(orientationEntry);
}
int steps = degree / 90;
for (int i = 0; i < steps; i++) {
switch (orientation) {
case TIFFBaseline.ORIENTATION_TOPLEFT:
orientation = TIFFExtension.ORIENTATION_RIGHTTOP;
break;
case TIFFExtension.ORIENTATION_TOPRIGHT:
orientation = TIFFExtension.ORIENTATION_RIGHTBOT;
break;
case TIFFExtension.ORIENTATION_BOTRIGHT:
orientation = TIFFExtension.ORIENTATION_LEFTBOT;
break;
case TIFFExtension.ORIENTATION_BOTLEFT:
orientation = TIFFExtension.ORIENTATION_LEFTTOP;
break;
case TIFFExtension.ORIENTATION_LEFTTOP:
orientation = TIFFExtension.ORIENTATION_TOPRIGHT;
break;
case TIFFExtension.ORIENTATION_RIGHTTOP:
orientation = TIFFExtension.ORIENTATION_BOTRIGHT;
break;
case TIFFExtension.ORIENTATION_RIGHTBOT:
orientation = TIFFExtension.ORIENTATION_BOTLEFT;
break;
case TIFFExtension.ORIENTATION_LEFTBOT:
orientation = TIFFBaseline.ORIENTATION_TOPLEFT;
break;
}
}
newIDFData.add(new TIFFImageWriter.TIFFEntry(TIFF.TAG_ORIENTATION, (short) orientation));
IFD = new AbstractDirectory(newIDFData) {
};
}
}
}

View File

@ -0,0 +1,204 @@
/*
* Copyright (c) 2013, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.contrib.tiff;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFExtension;
import com.twelvemonkeys.imageio.plugins.tiff.TIFFMedataFormat;
import com.twelvemonkeys.io.FileUtil;
import org.junit.Assert;
import org.junit.Test;
import org.w3c.dom.Node;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.xml.xpath.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.List;
/**
* TIFFUtilitiesTest
*
* @author <a href="mailto:mail@schmidor.de">Oliver Schmidtmer</a>
* @author last modified by $Author$
* @version $Id$
*/
public class TIFFUtilitiesTest {
@Test
public void testMerge() throws IOException {
// Files from ImageIO TIFF Plugin
InputStream stream1 = getClassLoaderResource("/tiff/ccitt/group3_1d.tif").openStream();
InputStream stream2 = getClassLoaderResource("/tiff/ccitt/group3_2d.tif").openStream();
InputStream stream3 = getClassLoaderResource("/tiff/ccitt/group4.tif").openStream();
File file1 = File.createTempFile("imageiotest", ".tif");
File file2 = File.createTempFile("imageiotest", ".tif");
File file3 = File.createTempFile("imageiotest", ".tif");
File output = File.createTempFile("imageiotest", ".tif");
byte[] data;
data = FileUtil.read(stream1);
FileUtil.write(file1, data);
stream1.close();
data = FileUtil.read(stream2);
FileUtil.write(file2, data);
stream2.close();
data = FileUtil.read(stream3);
FileUtil.write(file3, data);
stream3.close();
List<File> input = Arrays.asList(file1, file2, file3);
TIFFUtilities.merge(input, output);
ImageInputStream iis = ImageIO.createImageInputStream(output);
ImageReader reader = ImageIO.getImageReaders(iis).next();
reader.setInput(iis);
Assert.assertEquals(3, reader.getNumImages(true));
iis.close();
output.delete();
file1.delete();
file2.delete();
file3.delete();
}
@Test
public void testSplit() throws IOException {
InputStream inputStream = getClassLoaderResource("/contrib/tiff/multipage.tif").openStream();
File inputFile = File.createTempFile("imageiotest", "tif");
byte[] data = FileUtil.read(inputStream);
FileUtil.write(inputFile, data);
inputStream.close();
File outputDirectory = Files.createTempDirectory("imageio").toFile();
TIFFUtilities.split(inputFile, outputDirectory);
ImageReader reader = ImageIO.getImageReadersByFormatName("TIF").next();
File[] outputFiles = outputDirectory.listFiles();
Assert.assertEquals(3, outputFiles.length);
for (File outputFile : outputFiles) {
ImageInputStream iis = ImageIO.createImageInputStream(outputFile);
reader.setInput(iis);
Assert.assertEquals(1, reader.getNumImages(true));
iis.close();
outputFile.delete();
}
outputDirectory.delete();
inputFile.delete();
}
@Test
public void testRotate() throws IOException, XPathExpressionException {
ImageReader reader = ImageIO.getImageReadersByFormatName("TIF").next();
InputStream inputStream = getClassLoaderResource("/contrib/tiff/multipage.tif").openStream();
File inputFile = File.createTempFile("imageiotest", ".tif");
byte[] data = FileUtil.read(inputStream);
FileUtil.write(inputFile, data);
inputStream.close();
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression expression = xPath.compile("TIFFIFD/TIFFField[@number='274']/TIFFBytes/TIFFByte/@value");
// rotate all pages
ImageInputStream inputTest1 = ImageIO.createImageInputStream(inputFile);
File outputTest1 = File.createTempFile("imageiotest", ".tif");
ImageOutputStream iosTest1 = ImageIO.createImageOutputStream(outputTest1);
TIFFUtilities.rotatePages(inputTest1, iosTest1, 90);
iosTest1.close();
ImageInputStream checkTest1 = ImageIO.createImageInputStream(outputTest1);
reader.setInput(checkTest1);
for (int i = 0; i < 3; i++) {
Node metaData = reader.getImageMetadata(i)
.getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
Assert.assertEquals(orientation, TIFFExtension.ORIENTATION_RIGHTTOP);
}
checkTest1.close();
// rotate single page further
ImageInputStream inputTest2 = ImageIO.createImageInputStream(outputTest1);
File outputTest2 = File.createTempFile("imageiotest", ".tif");
ImageOutputStream iosTest2 = ImageIO.createImageOutputStream(outputTest2);
TIFFUtilities.rotatePage(inputTest2, iosTest2, 90, 1);
iosTest2.close();
ImageInputStream checkTest2 = ImageIO.createImageInputStream(outputTest2);
reader.setInput(checkTest2);
for (int i = 0; i < 3; i++) {
Node metaData = reader.getImageMetadata(i)
.getAsTree(TIFFMedataFormat.SUN_NATIVE_IMAGE_METADATA_FORMAT_NAME);
short orientation = ((Number) expression.evaluate(metaData, XPathConstants.NUMBER)).shortValue();
Assert.assertEquals(orientation, i == 1
? TIFFExtension.ORIENTATION_BOTRIGHT
: TIFFExtension.ORIENTATION_RIGHTTOP);
}
checkTest2.close();
}
@Test
public void testApplyOrientation() throws IOException {
InputStream inputStream = getClassLoaderResource("/contrib/tiff/multipage.tif").openStream();
File inputFile = File.createTempFile("imageiotest", "tif");
byte[] data = FileUtil.read(inputStream);
FileUtil.write(inputFile, data);
inputStream.close();
BufferedImage image = ImageIO.read(inputFile);
// rotate by 90<EFBFBD>
BufferedImage image90 = TIFFUtilities.applyOrientation(image, TIFFExtension.ORIENTATION_RIGHTTOP);
// rotate by 270<EFBFBD>
BufferedImage image360 = TIFFUtilities.applyOrientation(image90, TIFFExtension.ORIENTATION_LEFTBOT);
byte[] original = ((DataBufferByte) image.getData().getDataBuffer()).getData();
byte[] rotated = ((DataBufferByte) image360.getData().getDataBuffer()).getData();
Assert.assertArrayEquals(original, rotated);
}
protected URL getClassLoaderResource(final String pName) {
return getClass().getResource(pName);
}
}

Binary file not shown.

View File

@ -35,7 +35,7 @@ package com.twelvemonkeys.imageio.plugins.tiff;
* @author last modified by $Author: haraldk$
* @version $Id: TIFFBaseline.java,v 1.0 08.05.12 16:43 haraldk Exp$
*/
interface TIFFBaseline {
public interface TIFFBaseline {
int COMPRESSION_NONE = 1;
int COMPRESSION_CCITT_MODIFIED_HUFFMAN_RLE = 2;
int COMPRESSION_PACKBITS = 32773;

View File

@ -35,7 +35,7 @@ package com.twelvemonkeys.imageio.plugins.tiff;
* @author last modified by $Author: haraldk$
* @version $Id: TIFFExtension.java,v 1.0 08.05.12 16:45 haraldk Exp$
*/
interface TIFFExtension {
public interface TIFFExtension {
/** CCITT T.4/Group 3 Fax compression. */
int COMPRESSION_CCITT_T4 = 3;
/** CCITT T.6/Group 4 Fax compression. */

View File

@ -113,7 +113,7 @@ public final class TIFFImageWriter extends ImageWriterBase {
// TODO: Allow appending/partly overwrite of existing file...
}
static final class TIFFEntry extends AbstractEntry {
public static final class TIFFEntry extends AbstractEntry {
// TODO: Expose a merge of this and the EXIFEntry class...
private final short type;
@ -164,7 +164,7 @@ public final class TIFFImageWriter extends ImageWriterBase {
throw new UnsupportedOperationException(String.format("Method guessType not implemented for value of type %s", value.getClass()));
}
TIFFEntry(final int identifier, final Object value) {
public TIFFEntry(final int identifier, final Object value) {
this(identifier, guessType(value), value);
}