| /* |
| * 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.tiff; |
| |
| import java.awt.image.BufferedImage; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Hashtable; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.commons.imaging.ImageReadException; |
| import org.apache.commons.imaging.ImageWriteException; |
| import org.apache.commons.imaging.common.ImageMetadata; |
| import org.apache.commons.imaging.common.RationalNumber; |
| import org.apache.commons.imaging.formats.tiff.constants.AllTagConstants; |
| import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants; |
| import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryConstants; |
| import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryType; |
| import org.apache.commons.imaging.formats.tiff.fieldtypes.FieldType; |
| import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo; |
| import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoAscii; |
| import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoByte; |
| import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoDouble; |
| import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoFloat; |
| import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoGpsText; |
| import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoLong; |
| import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoRational; |
| import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSByte; |
| import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSLong; |
| import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSRational; |
| import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoSShort; |
| import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoShort; |
| import org.apache.commons.imaging.formats.tiff.taginfos.TagInfoXpString; |
| 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 TiffImageMetadata extends ImageMetadata |
| implements |
| TiffDirectoryConstants |
| { |
| public final TiffContents contents; |
| private static final Map<Object, Integer> tagCounts = countTags(AllTagConstants.ALL_TAGS); |
| |
| public TiffImageMetadata(final TiffContents contents) |
| { |
| this.contents = contents; |
| } |
| |
| private static final Map<Object, Integer> countTags(List<TagInfo> tags) |
| { |
| Map<Object, Integer> map = new Hashtable<Object, Integer>(); |
| |
| for (int i = 0; i < tags.size(); i++) |
| { |
| TagInfo tag = tags.get(i); |
| |
| Integer count = map.get(tag.tag); |
| if (count == null) |
| map.put(tag.tag, 1); |
| else |
| map.put(tag.tag, count + 1); |
| } |
| |
| return map; |
| } |
| |
| public static class Directory extends ImageMetadata implements ImageMetadata.IImageMetadataItem |
| { |
| // private BufferedImage thumbnail = null; |
| |
| public final int type; |
| |
| private final TiffDirectory directory; |
| private final int byteOrder; |
| |
| public Directory(int byteOrder, final TiffDirectory directory) |
| { |
| this.type = directory.type; |
| this.directory = directory; |
| this.byteOrder = byteOrder; |
| } |
| |
| public void add(TiffField entry) |
| { |
| add(new TiffImageMetadata.Item(entry)); |
| } |
| |
| public BufferedImage getThumbnail() throws ImageReadException, |
| IOException |
| { |
| return directory.getTiffImage(byteOrder); |
| } |
| |
| public TiffImageData getTiffImageData() |
| { |
| return directory.getTiffImageData(); |
| } |
| |
| public TiffField findField(TagInfo tagInfo) throws ImageReadException |
| { |
| return directory.findField(tagInfo); |
| } |
| |
| public List<TiffField> getAllFields() |
| { |
| return directory.getDirectoryEntrys(); |
| } |
| |
| public JpegImageData getJpegImageData() |
| { |
| return directory.getJpegImageData(); |
| } |
| |
| @Override |
| public String toString(String prefix) |
| { |
| return (prefix != null ? prefix : "") + directory.description() |
| + ": " // |
| + (getTiffImageData() != null ? " (tiffImageData)" : "") // |
| + (getJpegImageData() != null ? " (jpegImageData)" : "") // |
| + "\n" + super.toString(prefix) + "\n"; |
| } |
| |
| public TiffOutputDirectory getOutputDirectory(int byteOrder) |
| throws ImageWriteException |
| { |
| try |
| { |
| TiffOutputDirectory dstDir = new TiffOutputDirectory(type, byteOrder); |
| |
| List<? extends IImageMetadataItem> entries = getItems(); |
| for (int i = 0; i < entries.size(); i++) |
| { |
| TiffImageMetadata.Item item = (TiffImageMetadata.Item) entries |
| .get(i); |
| TiffField srcField = item.getTiffField(); |
| |
| if (null != dstDir.findField(srcField.tag)) |
| { |
| // ignore duplicate tags in a directory. |
| continue; |
| } |
| else if (srcField.tagInfo.isOffset()) |
| { |
| // ignore offset fields. |
| continue; |
| } |
| |
| TagInfo tagInfo = srcField.tagInfo; |
| FieldType fieldType = srcField.fieldType; |
| int count = srcField.length; |
| // byte bytes[] = srcField.fieldType.getRawBytes(srcField); |
| |
| // Debug.debug("tagInfo", tagInfo); |
| |
| Object value = srcField.getValue(); |
| |
| // Debug.debug("value", Debug.getType(value)); |
| |
| byte bytes[] = tagInfo.encodeValue(fieldType, value, |
| byteOrder); |
| |
| // if (tagInfo.isUnknown()) |
| // Debug.debug( |
| // "\t" + "unknown tag(0x" |
| // + Integer.toHexString(srcField.tag) |
| // + ") bytes", bytes); |
| |
| TiffOutputField dstField = new TiffOutputField( |
| srcField.tag, tagInfo, fieldType, count, bytes); |
| dstField.setSortHint(srcField.getSortHint()); |
| dstDir.add(dstField); |
| } |
| |
| dstDir.setTiffImageData(getTiffImageData()); |
| dstDir.setJpegImageData(getJpegImageData()); |
| |
| return dstDir; |
| } |
| catch (ImageReadException e) |
| { |
| throw new ImageWriteException(e.getMessage(), e); |
| } |
| } |
| |
| } |
| |
| public List<? extends IImageMetadataItem> getDirectories() |
| { |
| return super.getItems(); |
| } |
| |
| @Override |
| public List<? extends IImageMetadataItem> getItems() |
| { |
| List<IImageMetadataItem> result = new ArrayList<IImageMetadataItem>(); |
| |
| List<? extends IImageMetadataItem> items = super.getItems(); |
| for (int i = 0; i < items.size(); i++) |
| { |
| Directory dir = (Directory) items.get(i); |
| result.addAll(dir.getItems()); |
| } |
| |
| return result; |
| } |
| |
| public static class Item extends ImageMetadata.Item |
| { |
| private final TiffField entry; |
| |
| public Item(TiffField entry) |
| { |
| // super(entry.getTagName() + " (" + entry.getFieldTypeName() + ")", |
| super(entry.getTagName(), entry.getValueDescription()); |
| this.entry = entry; |
| } |
| |
| public TiffField getTiffField() |
| { |
| return entry; |
| } |
| |
| } |
| |
| public TiffOutputSet getOutputSet() throws ImageWriteException |
| { |
| int byteOrder = contents.header.byteOrder; |
| TiffOutputSet result = new TiffOutputSet(byteOrder); |
| |
| List<? extends IImageMetadataItem> srcDirs = getDirectories(); |
| for (int i = 0; i < srcDirs.size(); i++) |
| { |
| TiffImageMetadata.Directory srcDir = (TiffImageMetadata.Directory) srcDirs |
| .get(i); |
| |
| if (null != result.findDirectory(srcDir.type)) |
| { |
| // Certain cameras right directories more than once. |
| // This is a bug. |
| // Ignore second directory of a given type. |
| continue; |
| } |
| |
| TiffOutputDirectory outputDirectory = srcDir |
| .getOutputDirectory(byteOrder); |
| result.addDirectory(outputDirectory); |
| } |
| |
| return result; |
| } |
| |
| public TiffField findField(TagInfo tagInfo) throws ImageReadException |
| { |
| return findField(tagInfo, false); |
| } |
| |
| public TiffField findField(TagInfo tagInfo, boolean exactDirectoryMatch) throws ImageReadException |
| { |
| // Please keep this method in sync with TiffField's getTag() |
| Integer tagCount = tagCounts.get(tagInfo.tag); |
| int tagsMatching = tagCount == null ? 0 : tagCount.intValue(); |
| |
| List<? extends IImageMetadataItem> directories = getDirectories(); |
| if (exactDirectoryMatch || tagInfo.directoryType != TiffDirectoryType.EXIF_DIRECTORY_UNKNOWN) |
| { |
| for (int i = 0; i < directories.size(); i++) |
| { |
| Directory directory = (Directory) directories.get(i); |
| if (directory.type == tagInfo.directoryType.directoryType) { |
| TiffField field = directory.findField(tagInfo); |
| if (field != null) { |
| return field; |
| } |
| } |
| } |
| if (exactDirectoryMatch || tagsMatching > 1) { |
| return null; |
| } |
| for (int i = 0; i < directories.size(); i++) |
| { |
| Directory directory = (Directory) directories.get(i); |
| if (tagInfo.directoryType.isImageDirectory() && |
| directory.type >= 0) { |
| TiffField field = directory.findField(tagInfo); |
| if (field != null) { |
| return field; |
| } |
| } else if (!tagInfo.directoryType.isImageDirectory() && |
| directory.type < 0) { |
| TiffField field = directory.findField(tagInfo); |
| if (field != null) { |
| return field; |
| } |
| } |
| } |
| } |
| |
| for (int i = 0; i < directories.size(); i++) |
| { |
| Directory directory = (Directory) directories.get(i); |
| TiffField field = directory.findField(tagInfo); |
| if (field != null) { |
| return field; |
| } |
| } |
| |
| return null; |
| } |
| |
| public Object getFieldValue(TagInfo tag) throws ImageReadException { |
| TiffField field = findField(tag); |
| if (field == null) { |
| return null; |
| } |
| return field.getValue(); |
| } |
| |
| public byte[] getFieldValue(TagInfoByte tag) throws ImageReadException { |
| TiffField field = findField(tag); |
| if (field == null) { |
| return null; |
| } |
| if (!tag.dataTypes.contains(field.fieldType)) { |
| return null; |
| } |
| return field.fieldType.getRawBytes(field); |
| } |
| |
| public String[] getFieldValue(TagInfoAscii tag) throws ImageReadException { |
| TiffField field = findField(tag); |
| if (field == null) { |
| return null; |
| } |
| if (!tag.dataTypes.contains(field.fieldType)) { |
| return null; |
| } |
| byte[] bytes = field.fieldType.getRawBytes(field); |
| return tag.getValue(field.byteOrder, bytes); |
| } |
| |
| public short[] getFieldValue(TagInfoShort tag) throws ImageReadException { |
| TiffField field = findField(tag); |
| if (field == null) { |
| return null; |
| } |
| if (!tag.dataTypes.contains(field.fieldType)) { |
| return null; |
| } |
| byte[] bytes = field.fieldType.getRawBytes(field); |
| return tag.getValue(field.byteOrder, bytes); |
| } |
| |
| public int[] getFieldValue(TagInfoLong tag) throws ImageReadException { |
| TiffField field = findField(tag); |
| if (field == null) { |
| return null; |
| } |
| if (!tag.dataTypes.contains(field.fieldType)) { |
| return null; |
| } |
| byte[] bytes = field.fieldType.getRawBytes(field); |
| return tag.getValue(field.byteOrder, bytes); |
| } |
| |
| public RationalNumber[] getFieldValue(TagInfoRational tag) throws ImageReadException { |
| TiffField field = findField(tag); |
| if (field == null) { |
| return null; |
| } |
| if (!tag.dataTypes.contains(field.fieldType)) { |
| return null; |
| } |
| byte[] bytes = field.fieldType.getRawBytes(field); |
| return tag.getValue(field.byteOrder, bytes); |
| } |
| |
| public byte[] getFieldValue(TagInfoSByte tag) throws ImageReadException { |
| TiffField field = findField(tag); |
| if (field == null) { |
| return null; |
| } |
| if (!tag.dataTypes.contains(field.fieldType)) { |
| return null; |
| } |
| return field.fieldType.getRawBytes(field); |
| } |
| |
| public short[] getFieldValue(TagInfoSShort tag) throws ImageReadException { |
| TiffField field = findField(tag); |
| if (field == null) { |
| return null; |
| } |
| if (!tag.dataTypes.contains(field.fieldType)) { |
| return null; |
| } |
| byte[] bytes = field.fieldType.getRawBytes(field); |
| return tag.getValue(field.byteOrder, bytes); |
| } |
| |
| public int[] getFieldValue(TagInfoSLong tag) throws ImageReadException { |
| TiffField field = findField(tag); |
| if (field == null) { |
| return null; |
| } |
| if (!tag.dataTypes.contains(field.fieldType)) { |
| return null; |
| } |
| byte[] bytes = field.fieldType.getRawBytes(field); |
| return tag.getValue(field.byteOrder, bytes); |
| } |
| |
| public RationalNumber[] getFieldValue(TagInfoSRational tag) throws ImageReadException { |
| TiffField field = findField(tag); |
| if (field == null) { |
| return null; |
| } |
| if (!tag.dataTypes.contains(field.fieldType)) { |
| return null; |
| } |
| byte[] bytes = field.fieldType.getRawBytes(field); |
| return tag.getValue(field.byteOrder, bytes); |
| } |
| |
| public float[] getFieldValue(TagInfoFloat tag) throws ImageReadException { |
| TiffField field = findField(tag); |
| if (field == null) { |
| return null; |
| } |
| if (!tag.dataTypes.contains(field.fieldType)) { |
| return null; |
| } |
| byte[] bytes = field.fieldType.getRawBytes(field); |
| return tag.getValue(field.byteOrder, bytes); |
| } |
| |
| public double[] getFieldValue(TagInfoDouble tag) throws ImageReadException { |
| TiffField field = findField(tag); |
| if (field == null) { |
| return null; |
| } |
| if (!tag.dataTypes.contains(field.fieldType)) { |
| return null; |
| } |
| byte[] bytes = field.fieldType.getRawBytes(field); |
| return tag.getValue(field.byteOrder, bytes); |
| } |
| |
| public String getFieldValue(TagInfoGpsText tag) throws ImageReadException { |
| TiffField field = findField(tag); |
| if (field == null) { |
| return null; |
| } |
| return tag.getValue(field); |
| } |
| |
| public String getFieldValue(TagInfoXpString tag) throws ImageReadException { |
| TiffField field = findField(tag); |
| if (field == null) { |
| return null; |
| } |
| return tag.getValue(field); |
| } |
| |
| public TiffDirectory findDirectory(int directoryType) |
| { |
| List<? extends IImageMetadataItem> directories = getDirectories(); |
| for (int i = 0; i < directories.size(); i++) |
| { |
| Directory directory = (Directory) directories.get(i); |
| if (directory.type == directoryType) |
| return directory.directory; |
| } |
| return null; |
| } |
| |
| public List<TiffField> getAllFields() |
| { |
| List<TiffField> result = new ArrayList<TiffField>(); |
| List<? extends IImageMetadataItem> directories = getDirectories(); |
| for (int i = 0; i < directories.size(); i++) |
| { |
| Directory directory = (Directory) directories.get(i); |
| result.addAll(directory.getAllFields()); |
| } |
| return result; |
| } |
| |
| public GPSInfo getGPS() throws ImageReadException |
| { |
| TiffDirectory gpsDirectory = findDirectory(DIRECTORY_TYPE_GPS); |
| if (null == gpsDirectory) |
| return null; |
| |
| // more specific example of how to access GPS values. |
| TiffField latitudeRefField = gpsDirectory |
| .findField(GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF); |
| TiffField latitudeField = gpsDirectory |
| .findField(GpsTagConstants.GPS_TAG_GPS_LATITUDE); |
| TiffField longitudeRefField = gpsDirectory |
| .findField(GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF); |
| TiffField longitudeField = gpsDirectory |
| .findField(GpsTagConstants.GPS_TAG_GPS_LONGITUDE); |
| |
| if (latitudeRefField == null || latitudeField == null |
| || longitudeRefField == null || longitudeField == null) |
| return null; |
| |
| // all of these values are strings. |
| String latitudeRef = latitudeRefField.getStringValue(); |
| RationalNumber latitude[] = (RationalNumber[]) latitudeField.getValue(); |
| String longitudeRef = longitudeRefField.getStringValue(); |
| RationalNumber longitude[] = (RationalNumber[]) longitudeField |
| .getValue(); |
| |
| if (latitude.length != 3 || longitude.length != 3) |
| throw new ImageReadException( |
| "Expected three values for latitude and longitude."); |
| |
| RationalNumber latitudeDegrees = latitude[0]; |
| RationalNumber latitudeMinutes = latitude[1]; |
| RationalNumber latitudeSeconds = latitude[2]; |
| |
| RationalNumber longitudeDegrees = longitude[0]; |
| RationalNumber longitudeMinutes = longitude[1]; |
| RationalNumber longitudeSeconds = longitude[2]; |
| |
| return new GPSInfo(latitudeRef, longitudeRef, latitudeDegrees, |
| latitudeMinutes, latitudeSeconds, longitudeDegrees, |
| longitudeMinutes, longitudeSeconds); |
| } |
| |
| public static class GPSInfo |
| { |
| public final String latitudeRef; |
| public final String longitudeRef; |
| |
| public final RationalNumber latitudeDegrees; |
| public final RationalNumber latitudeMinutes; |
| public final RationalNumber latitudeSeconds; |
| public final RationalNumber longitudeDegrees; |
| public final RationalNumber longitudeMinutes; |
| public final RationalNumber longitudeSeconds; |
| |
| public GPSInfo(final String latitudeRef, final String longitudeRef, |
| final RationalNumber latitudeDegrees, |
| final RationalNumber latitudeMinutes, |
| final RationalNumber latitudeSeconds, |
| final RationalNumber longitudeDegrees, |
| final RationalNumber longitudeMinutes, |
| final RationalNumber longitudeSeconds) |
| { |
| this.latitudeRef = latitudeRef; |
| this.longitudeRef = longitudeRef; |
| this.latitudeDegrees = latitudeDegrees; |
| this.latitudeMinutes = latitudeMinutes; |
| this.latitudeSeconds = latitudeSeconds; |
| this.longitudeDegrees = longitudeDegrees; |
| this.longitudeMinutes = longitudeMinutes; |
| this.longitudeSeconds = longitudeSeconds; |
| } |
| |
| @Override |
| public String toString() |
| { |
| // This will format the gps info like so: |
| // |
| // latitude: 8 degrees, 40 minutes, 42.2 seconds S |
| // longitude: 115 degrees, 26 minutes, 21.8 seconds E |
| |
| StringBuffer result = new StringBuffer(); |
| result.append("[GPS. "); |
| result.append("Latitude: " + latitudeDegrees.toDisplayString() |
| + " degrees, " + latitudeMinutes.toDisplayString() |
| + " minutes, " + latitudeSeconds.toDisplayString() |
| + " seconds " + latitudeRef); |
| result.append(", Longitude: " + longitudeDegrees.toDisplayString() |
| + " degrees, " + longitudeMinutes.toDisplayString() |
| + " minutes, " + longitudeSeconds.toDisplayString() |
| + " seconds " + longitudeRef); |
| result.append("]"); |
| |
| return result.toString(); |
| } |
| |
| public double getLongitudeAsDegreesEast() throws ImageReadException |
| { |
| double result = longitudeDegrees.doubleValue() |
| + (longitudeMinutes.doubleValue() / 60.0) |
| + (longitudeSeconds.doubleValue() / 3600.0); |
| |
| if (longitudeRef.trim().equalsIgnoreCase("e")) |
| return result; |
| else if (longitudeRef.trim().equalsIgnoreCase("w")) |
| return -result; |
| else |
| throw new ImageReadException("Unknown longitude ref: \"" |
| + longitudeRef + "\""); |
| } |
| |
| public double getLatitudeAsDegreesNorth() throws ImageReadException |
| { |
| double result = latitudeDegrees.doubleValue() |
| + (latitudeMinutes.doubleValue() / 60.0) |
| + (latitudeSeconds.doubleValue() / 3600.0); |
| |
| if (latitudeRef.trim().equalsIgnoreCase("n")) |
| return result; |
| else if (latitudeRef.trim().equalsIgnoreCase("s")) |
| return -result; |
| else |
| throw new ImageReadException("Unknown latitude ref: \"" |
| + latitudeRef + "\""); |
| } |
| |
| } |
| |
| } |