/*
* 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.sanselan.formats.tiff;
//import java.awt.Dimension;
//import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.sanselan.FormatCompliance;
import org.apache.sanselan.ImageFormat;
import org.apache.sanselan.ImageInfo;
import org.apache.sanselan.ImageParser;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.formats.tiff.constants.TiffConstants;
public class TiffImageParser extends ImageParser implements TiffConstants
{
public TiffImageParser()
{
// setDebug(true);
}
public String getName()
{
return "Tiff-Custom";
}
public String getDefaultExtension()
{
return DEFAULT_EXTENSION;
}
private static final String DEFAULT_EXTENSION = ".tif";
private static final String ACCEPTED_EXTENSIONS[] = { ".tif", ".tiff", };
protected String[] getAcceptedExtensions()
{
return ACCEPTED_EXTENSIONS;
}
protected ImageFormat[] getAcceptedTypes()
{
return new ImageFormat[] { ImageFormat.IMAGE_FORMAT_TIFF, //
};
}
public byte[] getICCProfileBytes(ByteSource byteSource, Map params)
throws ImageReadException, IOException
{
FormatCompliance formatCompliance = FormatCompliance.getDefault();
TiffContents contents = new TiffReader(isStrict(params))
.readFirstDirectory(byteSource, params, false, formatCompliance);
TiffDirectory directory = (TiffDirectory) contents.directories.get(0);
TiffField field = directory.findField(EXIF_TAG_ICC_PROFILE);
if (null == field)
return null;
return field.oversizeValue;
}
public int[] getImageSize(ByteSource byteSource, Map params)
throws ImageReadException, IOException
{
FormatCompliance formatCompliance = FormatCompliance.getDefault();
TiffContents contents = new TiffReader(isStrict(params))
.readFirstDirectory(byteSource, params, false, formatCompliance);
TiffDirectory directory = (TiffDirectory) contents.directories.get(0);
int width = directory.findField(TIFF_TAG_IMAGE_WIDTH).getIntValue();
int height = directory.findField(TIFF_TAG_IMAGE_LENGTH).getIntValue();
return new int[]{width, height};
}
public byte[] embedICCProfile(byte image[], byte profile[])
{
return null;
}
public boolean embedICCProfile(File src, File dst, byte profile[])
{
return false;
}
public IImageMetadata getMetadata(ByteSource byteSource, Map params)
throws ImageReadException, IOException
{
FormatCompliance formatCompliance = FormatCompliance.getDefault();
TiffContents contents = new TiffReader(isStrict(params)).readContents(
byteSource, params, formatCompliance);
ArrayList directories = contents.directories;
TiffImageMetadata result = new TiffImageMetadata(contents);
for (int i = 0; i < directories.size(); i++)
{
TiffDirectory dir = (TiffDirectory) directories.get(i);
TiffImageMetadata.Directory metadataDirectory = new TiffImageMetadata.Directory(
dir);
ArrayList entries = dir.getDirectoryEntrys();
for (int j = 0; j < entries.size(); j++)
{
TiffField entry = (TiffField) entries.get(j);
metadataDirectory.add(entry);
}
result.add(metadataDirectory);
}
return result;
}
public ImageInfo getImageInfo(ByteSource byteSource, Map params)
throws ImageReadException, IOException
{
FormatCompliance formatCompliance = FormatCompliance.getDefault();
TiffContents contents = new TiffReader(isStrict(params))
.readDirectories(byteSource, false, formatCompliance);
TiffDirectory directory = (TiffDirectory) contents.directories.get(0);
TiffField widthField = directory.findField(TIFF_TAG_IMAGE_WIDTH, true);
TiffField heightField = directory
.findField(TIFF_TAG_IMAGE_LENGTH, true);
if ((widthField == null) || (heightField == null))
throw new ImageReadException("TIFF image missing size info.");
int height = heightField.getIntValue();
int width = widthField.getIntValue();
// -------------------
TiffField resolutionUnitField = directory
.findField(TIFF_TAG_RESOLUTION_UNIT);
int resolutionUnit = 2; // Inch
if ((resolutionUnitField != null)
&& (resolutionUnitField.getValue() != null))
resolutionUnit = resolutionUnitField.getIntValue();
double unitsPerInch = -1;
switch (resolutionUnit)
{
case 1:
break;
case 2: // Inch
unitsPerInch = 1.0;
break;
case 3: // Meter
unitsPerInch = 0.0254;
break;
default:
break;
}
TiffField xResolutionField = directory.findField(TIFF_TAG_XRESOLUTION);
TiffField yResolutionField = directory.findField(TIFF_TAG_YRESOLUTION);
int physicalWidthDpi = -1;
float physicalWidthInch = -1;
int physicalHeightDpi = -1;
float physicalHeightInch = -1;
if (unitsPerInch > 0)
{
if ((xResolutionField != null)
&& (xResolutionField.getValue() != null))
{
double XResolutionPixelsPerUnit = xResolutionField
.getDoubleValue();
physicalWidthDpi = (int) (XResolutionPixelsPerUnit / unitsPerInch);
physicalWidthInch = (float) (width / (XResolutionPixelsPerUnit * unitsPerInch));
}
if ((yResolutionField != null)
&& (yResolutionField.getValue() != null))
{
double YResolutionPixelsPerUnit = yResolutionField
.getDoubleValue();
physicalHeightDpi = (int) (YResolutionPixelsPerUnit / unitsPerInch);
physicalHeightInch = (float) (height / (YResolutionPixelsPerUnit * unitsPerInch));
}
}
// -------------------
TiffField bitsPerSampleField = directory
.findField(TIFF_TAG_BITS_PER_SAMPLE);
int bitsPerSample = -1;
if ((bitsPerSampleField != null)
&& (bitsPerSampleField.getValue() != null))
bitsPerSample = bitsPerSampleField.getIntValueOrArraySum();
int bitsPerPixel = bitsPerSample; // assume grayscale;
// dunno if this handles colormapped images correctly.
// -------------------
ArrayList comments = new ArrayList();
ArrayList entries = directory.entries;
for (int i = 0; i < entries.size(); i++)
{
TiffField field = (TiffField) entries.get(i);
String comment = field.toString();
comments.add(comment);
}
ImageFormat format = ImageFormat.IMAGE_FORMAT_TIFF;
String formatName = "TIFF Tag-based Image File Format";
String mimeType = "image/tiff";
int numberOfImages = contents.directories.size();
// not accurate ... only reflects first
boolean isProgressive = false;
// is TIFF ever interlaced/progressive?
String formatDetails = "Tiff v." + contents.header.tiffVersion;
boolean isTransparent = false; // TODO: wrong
boolean usesPalette = false;
TiffField colorMapField = directory.findField(TIFF_TAG_COLOR_MAP);
if (colorMapField != null)
usesPalette = true;
int colorType = ImageInfo.COLOR_TYPE_RGB;
int compression = directory.findField(TIFF_TAG_COMPRESSION)
.getIntValue();
String compressionAlgorithm;
switch (compression)
{
case TIFF_COMPRESSION_UNCOMPRESSED_1:
compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_NONE;
break;
case TIFF_COMPRESSION_CCITT_1D:
compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_CCITT_1D;
break;
case TIFF_COMPRESSION_CCITT_GROUP_3:
compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_CCITT_GROUP_3;
break;
case TIFF_COMPRESSION_CCITT_GROUP_4:
compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_CCITT_GROUP_4;
break;
case TIFF_COMPRESSION_LZW:
compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_LZW;
break;
case TIFF_COMPRESSION_JPEG:
compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_JPEG;
break;
case TIFF_COMPRESSION_UNCOMPRESSED_2:
compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_NONE;
break;
case TIFF_COMPRESSION_PACKBITS:
compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_PACKBITS;
break;
default:
compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_UNKNOWN;
break;
}
ImageInfo result = new ImageInfo(formatDetails, bitsPerPixel, comments,
format, formatName, height, mimeType, numberOfImages,
physicalHeightDpi, physicalHeightInch, physicalWidthDpi,
physicalWidthInch, width, isProgressive, isTransparent,
usesPalette, colorType, compressionAlgorithm);
return result;
}
public String getXmpXml(ByteSource byteSource, Map params)
throws ImageReadException, IOException
{
FormatCompliance formatCompliance = FormatCompliance.getDefault();
TiffContents contents = new TiffReader(isStrict(params))
.readDirectories(byteSource, false, formatCompliance);
TiffDirectory directory = (TiffDirectory) contents.directories.get(0);
TiffField xmpField = directory.findField(TIFF_TAG_XMP, false);
if (xmpField == null)
return null;
byte bytes[] = xmpField.getByteArrayValue();
try
{
// segment data is UTF-8 encoded xml.
String xml = new String(bytes, "utf-8");
return xml;
} catch (UnsupportedEncodingException e)
{
throw new ImageReadException("Invalid JPEG XMP Segment.");
}
}
public boolean dumpImageFile(PrintWriter pw, ByteSource byteSource)
throws ImageReadException, IOException
{
try
{
pw.println("tiff.dumpImageFile");
{
ImageInfo imageData = getImageInfo(byteSource);
if (imageData == null)
return false;
imageData.toString(pw, "");
}
pw.println("");
// try
{
FormatCompliance formatCompliance = FormatCompliance
.getDefault();
Map params = null;
TiffContents contents = new TiffReader(true).readContents(
byteSource, params, formatCompliance);
ArrayList directories = contents.directories;
if (directories == null)
return false;
for (int d = 0; d < directories.size(); d++)
{
TiffDirectory directory = (TiffDirectory) directories
.get(d);
ArrayList entries = directory.entries;
if (entries == null)
return false;
// Debug.debug("directory offset", directory.offset);
for (int i = 0; i < entries.size(); i++)
{
TiffField field = (TiffField) entries.get(i);
field.dump(pw, d + "");
}
}
pw.println("");
}
// catch (Exception e)
// {
// Debug.debug(e);
// pw.println("");
// return false;
// }
return true;
} finally
{
pw.println("");
}
}
public FormatCompliance getFormatCompliance(ByteSource byteSource)
throws ImageReadException, IOException
{
FormatCompliance formatCompliance = FormatCompliance.getDefault();
Map params = null;
new TiffReader(isStrict(params)).readContents(byteSource, params,
formatCompliance);
return formatCompliance;
}
public List collectRawImageData(ByteSource byteSource, Map params)
throws ImageReadException, IOException
{
FormatCompliance formatCompliance = FormatCompliance.getDefault();
TiffContents contents = new TiffReader(isStrict(params))
.readDirectories(byteSource, true, formatCompliance);
List result = new ArrayList();
for (int i = 0; i < contents.directories.size(); i++)
{
TiffDirectory directory = (TiffDirectory) contents.directories
.get(i);
List dataElements = directory.getTiffRawImageDataElements();
for (int j = 0; j < dataElements.size(); j++)
{
TiffDirectory.ImageDataElement element = (TiffDirectory.ImageDataElement) dataElements
.get(j);
byte bytes[] = byteSource.getBlock(element.offset,
element.length);
result.add(bytes);
}
}
return result;
}
// public BufferedImage getBufferedImage(ByteSource byteSource, Map params)
// throws ImageReadException, IOException
// {
// FormatCompliance formatCompliance = FormatCompliance.getDefault();
// TiffContents contents = new TiffReader(isStrict(params))
// .readFirstDirectory(byteSource, params, true, formatCompliance);
// TiffDirectory directory = (TiffDirectory) contents.directories.get(0);
// BufferedImage result = directory.getTiffImage(params);
// if (null == result)
// throw new ImageReadException("TIFF does not contain an image.");
// return result;
// }
// protected BufferedImage getBufferedImage(TiffDirectory directory, Map params)
// throws ImageReadException, IOException
// {
// ArrayList entries = directory.entries;
//
// if (entries == null)
// throw new ImageReadException("TIFF missing entries");
//
// int photometricInterpretation = directory.findField(
// TIFF_TAG_PHOTOMETRIC_INTERPRETATION, true).getIntValue();
// int compression = directory.findField(TIFF_TAG_COMPRESSION, true)
// .getIntValue();
// int width = directory.findField(TIFF_TAG_IMAGE_WIDTH, true)
// .getIntValue();
// int height = directory.findField(TIFF_TAG_IMAGE_LENGTH, true)
// .getIntValue();
// int samplesPerPixel = directory.findField(TIFF_TAG_SAMPLES_PER_PIXEL,
// true).getIntValue();
// int bitsPerSample[] = directory.findField(TIFF_TAG_BITS_PER_SAMPLE,
// true).getIntArrayValue();
// // TODO: why are we using bits per sample twice? because one is a sum.
// int bitsPerPixel = directory.findField(TIFF_TAG_BITS_PER_SAMPLE, true)
// .getIntValueOrArraySum();
//
// // int bitsPerPixel = getTagAsValueOrArraySum(entries,
// // TIFF_TAG_BITS_PER_SAMPLE);
//
// int predictor = -1;
// {
// // dumpOptionalNumberTag(entries, TIFF_TAG_FILL_ORDER);
// // dumpOptionalNumberTag(entries, TIFF_TAG_FREE_BYTE_COUNTS);
// // dumpOptionalNumberTag(entries, TIFF_TAG_FREE_OFFSETS);
// // dumpOptionalNumberTag(entries, TIFF_TAG_ORIENTATION);
// // dumpOptionalNumberTag(entries, TIFF_TAG_PLANAR_CONFIGURATION);
// TiffField predictorField = directory.findField(TIFF_TAG_PREDICTOR);
// if (null != predictorField)
// predictor = predictorField.getIntValueOrArraySum();
// }
//
// if (samplesPerPixel != bitsPerSample.length)
// throw new ImageReadException("Tiff: samplesPerPixel ("
// + samplesPerPixel + ")!=fBitsPerSample.length ("
// + bitsPerSample.length + ")");
//
// boolean hasAlpha = false;
// BufferedImage result = getBufferedImageFactory(params)
// .getColorBufferedImage(width, height, hasAlpha);
//
// PhotometricInterpreter photometricInterpreter = getPhotometricInterpreter(
// directory, photometricInterpretation, bitsPerPixel,
// bitsPerSample, predictor, samplesPerPixel, width, height);
//
// TiffImageData imageData = directory.getTiffImageData();
//
// DataReader dataReader = imageData.getDataReader(entries,
// photometricInterpreter, bitsPerPixel, bitsPerSample, predictor,
// samplesPerPixel, width, height, compression);
//
// dataReader.readImageData(result);
//
// photometricInterpreter.dumpstats();
//
// return result;
// }
// private PhotometricInterpreter getPhotometricInterpreter(
// TiffDirectory directory, int photometricInterpretation,
// int bitsPerPixel, int bitsPerSample[], int predictor,
// int samplesPerPixel, int width, int height) throws IOException,
// ImageReadException
// {
// switch (photometricInterpretation)
// {
// case 0:
// case 1:
// boolean invert = photometricInterpretation == 0;
//
// return new PhotometricInterpreterBiLevel(bitsPerPixel,
// samplesPerPixel, bitsPerSample, predictor, width, height,
// invert);
// case 3: // Palette
// {
// int colorMap[] = directory.findField(TIFF_TAG_COLOR_MAP, true)
// .getIntArrayValue();
//
// int expected_colormap_size = 3 * (1 << bitsPerPixel);
//
// if (colorMap.length != expected_colormap_size)
// throw new ImageReadException("Tiff: fColorMap.length ("
// + colorMap.length + ")!=expected_colormap_size ("
// + expected_colormap_size + ")");
//
// return new PhotometricInterpreterPalette(samplesPerPixel,
// bitsPerSample, predictor, width, height, colorMap);
// }
// case 2: // RGB
// return new PhotometricInterpreterRGB(samplesPerPixel,
// bitsPerSample, predictor, width, height);
// case 5: // CMYK
// return new PhotometricInterpreterCMYK(samplesPerPixel,
// bitsPerSample, predictor, width, height);
// case 6: //
// {
// double yCbCrCoefficients[] = directory.findField(
// TIFF_TAG_YCBCR_COEFFICIENTS, true).getDoubleArrayValue();
//
// int yCbCrPositioning[] = directory.findField(
// TIFF_TAG_YCBCR_POSITIONING, true).getIntArrayValue();
// int yCbCrSubSampling[] = directory.findField(
// TIFF_TAG_YCBCR_SUB_SAMPLING, true).getIntArrayValue();
//
// double referenceBlackWhite[] = directory.findField(
// TIFF_TAG_REFERENCE_BLACK_WHITE, true).getDoubleArrayValue();
//
// return new PhotometricInterpreterYCbCr(yCbCrCoefficients,
// yCbCrPositioning, yCbCrSubSampling, referenceBlackWhite,
// samplesPerPixel, bitsPerSample, predictor, width, height);
// }
//
// case 8:
// return new PhotometricInterpreterCIELAB(samplesPerPixel,
// bitsPerSample, predictor, width, height);
//
// case 32844:
// case 32845: {
// boolean yonly = (photometricInterpretation == 32844);
// return new PhotometricInterpreterLogLUV(samplesPerPixel,
// bitsPerSample, predictor, width, height, yonly);
// }
//
// default:
// throw new ImageReadException(
// "TIFF: Unknown fPhotometricInterpretation: "
// + photometricInterpretation);
// }
// }
// public void writeImage(BufferedImage src, OutputStream os, Map params)
// throws ImageWriteException, IOException
// {
// new TiffImageWriterLossy().writeImage(src, os, params);
// }
}