001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.imaging.formats.tiff.write; 018 019import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.DEFAULT_TIFF_BYTE_ORDER; 020 021import java.nio.ByteOrder; 022import java.util.ArrayList; 023import java.util.List; 024 025import org.apache.commons.imaging.ImageWriteException; 026import org.apache.commons.imaging.common.RationalNumber; 027import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants; 028import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryConstants; 029import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo; 030import org.apache.commons.imaging.internal.Debug; 031 032public final class TiffOutputSet { 033 public final ByteOrder byteOrder; 034 private final List<TiffOutputDirectory> directories = new ArrayList<>(); 035 private static final String NEWLINE = System.getProperty("line.separator"); 036 037 public TiffOutputSet() { 038 this(DEFAULT_TIFF_BYTE_ORDER); 039 } 040 041 public TiffOutputSet(final ByteOrder byteOrder) { 042 this.byteOrder = byteOrder; 043 } 044 045 protected List<TiffOutputItem> getOutputItems( 046 final TiffOutputSummary outputSummary) throws ImageWriteException { 047 final List<TiffOutputItem> result = new ArrayList<>(); 048 049 for (final TiffOutputDirectory directory : directories) { 050 result.addAll(directory.getOutputItems(outputSummary)); 051 } 052 053 return result; 054 } 055 056 public void addDirectory(final TiffOutputDirectory directory) 057 throws ImageWriteException { 058 if (null != findDirectory(directory.type)) { 059 throw new ImageWriteException( 060 "Output set already contains a directory of that type."); 061 } 062 directories.add(directory); 063 } 064 065 public List<TiffOutputDirectory> getDirectories() { 066 return new ArrayList<>(directories); 067 } 068 069 public TiffOutputDirectory getRootDirectory() { 070 return findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_ROOT); 071 } 072 073 public TiffOutputDirectory getExifDirectory() { 074 return findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_EXIF); 075 } 076 077 public TiffOutputDirectory getOrCreateRootDirectory() 078 throws ImageWriteException { 079 final TiffOutputDirectory result = findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_ROOT); 080 if (null != result) { 081 return result; 082 } 083 return addRootDirectory(); 084 } 085 086 public TiffOutputDirectory getOrCreateExifDirectory() 087 throws ImageWriteException { 088 // EXIF directory requires root directory. 089 getOrCreateRootDirectory(); 090 091 final TiffOutputDirectory result = findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_EXIF); 092 if (null != result) { 093 return result; 094 } 095 return addExifDirectory(); 096 } 097 098 public TiffOutputDirectory getOrCreateGPSDirectory() 099 throws ImageWriteException { 100 // GPS directory requires EXIF directory 101 getOrCreateExifDirectory(); 102 103 final TiffOutputDirectory result = findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_GPS); 104 if (null != result) { 105 return result; 106 } 107 return addGPSDirectory(); 108 } 109 110 public TiffOutputDirectory getGPSDirectory() { 111 return findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_GPS); 112 } 113 114 public TiffOutputDirectory getInteroperabilityDirectory() { 115 return findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_INTEROPERABILITY); 116 } 117 118 public TiffOutputDirectory findDirectory(final int directoryType) { 119 for (final TiffOutputDirectory directory : directories) { 120 if (directory.type == directoryType) { 121 return directory; 122 } 123 } 124 return null; 125 } 126 127 /** 128 * A convenience method to update GPS values in EXIF metadata. 129 * 130 * @param longitude 131 * Longitude in degrees E, negative values are W. 132 * @param latitude 133 * latitude in degrees N, negative values are S. 134 * @throws ImageWriteException if it fails to write the new data to the GPS directory 135 */ 136 public void setGPSInDegrees(double longitude, double latitude) 137 throws ImageWriteException { 138 final TiffOutputDirectory gpsDirectory = getOrCreateGPSDirectory(); 139 140 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_VERSION_ID); 141 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_VERSION_ID, GpsTagConstants.gpsVersion()); 142 143 final String longitudeRef = longitude < 0 ? "W" : "E"; 144 longitude = Math.abs(longitude); 145 final String latitudeRef = latitude < 0 ? "S" : "N"; 146 latitude = Math.abs(latitude); 147 148 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF); 149 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF, 150 longitudeRef); 151 152 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF); 153 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF, 154 latitudeRef); 155 156 { 157 double value = longitude; 158 final double longitudeDegrees = (long) value; 159 value %= 1; 160 value *= 60.0; 161 final double longitudeMinutes = (long) value; 162 value %= 1; 163 value *= 60.0; 164 final double longitudeSeconds = value; 165 166 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_LONGITUDE); 167 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_LONGITUDE, 168 RationalNumber.valueOf(longitudeDegrees), 169 RationalNumber.valueOf(longitudeMinutes), 170 RationalNumber.valueOf(longitudeSeconds)); 171 } 172 173 { 174 double value = latitude; 175 final double latitudeDegrees = (long) value; 176 value %= 1; 177 value *= 60.0; 178 final double latitudeMinutes = (long) value; 179 value %= 1; 180 value *= 60.0; 181 final double latitudeSeconds = value; 182 183 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_LATITUDE); 184 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_LATITUDE, 185 RationalNumber.valueOf(latitudeDegrees), 186 RationalNumber.valueOf(latitudeMinutes), 187 RationalNumber.valueOf(latitudeSeconds)); 188 } 189 190 } 191 192 public void removeField(final TagInfo tagInfo) { 193 removeField(tagInfo.tag); 194 } 195 196 public void removeField(final int tag) { 197 for (final TiffOutputDirectory directory : directories) { 198 directory.removeField(tag); 199 } 200 } 201 202 public TiffOutputField findField(final TagInfo tagInfo) { 203 return findField(tagInfo.tag); 204 } 205 206 public TiffOutputField findField(final int tag) { 207 for (final TiffOutputDirectory directory : directories) { 208 final TiffOutputField field = directory.findField(tag); 209 if (null != field) { 210 return field; 211 } 212 } 213 return null; 214 } 215 216 public TiffOutputDirectory addRootDirectory() throws ImageWriteException { 217 final TiffOutputDirectory result = new TiffOutputDirectory( 218 TiffDirectoryConstants.DIRECTORY_TYPE_ROOT, byteOrder); 219 addDirectory(result); 220 return result; 221 } 222 223 public TiffOutputDirectory addExifDirectory() throws ImageWriteException { 224 final TiffOutputDirectory result = new TiffOutputDirectory( 225 TiffDirectoryConstants.DIRECTORY_TYPE_EXIF, byteOrder); 226 addDirectory(result); 227 return result; 228 } 229 230 public TiffOutputDirectory addGPSDirectory() throws ImageWriteException { 231 final TiffOutputDirectory result = new TiffOutputDirectory( 232 TiffDirectoryConstants.DIRECTORY_TYPE_GPS, byteOrder); 233 addDirectory(result); 234 return result; 235 } 236 237 public TiffOutputDirectory addInteroperabilityDirectory() 238 throws ImageWriteException { 239 getOrCreateExifDirectory(); 240 241 final TiffOutputDirectory result = new TiffOutputDirectory( 242 TiffDirectoryConstants.DIRECTORY_TYPE_INTEROPERABILITY, byteOrder); 243 addDirectory(result); 244 return result; 245 } 246 247 @Override 248 public String toString() { 249 return toString(null); 250 } 251 252 public String toString(String prefix) { 253 if (prefix == null) { 254 prefix = ""; 255 } 256 257 final StringBuilder result = new StringBuilder(39); 258 259 result.append(prefix); 260 result.append("TiffOutputSet {"); 261 result.append(NEWLINE); 262 263 result.append(prefix); 264 result.append("byteOrder: "); 265 result.append(byteOrder); 266 result.append(NEWLINE); 267 268 for (int i = 0; i < directories.size(); i++) { 269 final TiffOutputDirectory directory = directories.get(i); 270 result.append(String.format("%s\tdirectory %d: %s (%d)%n", 271 prefix, i, directory.description(), directory.type)); 272 273 final List<TiffOutputField> fields = directory.getFields(); 274 for (final TiffOutputField field : fields) { 275 result.append(prefix); 276 result.append("\t\tfield ").append(i).append(": ").append(field.tagInfo); 277 result.append(NEWLINE); 278 } 279 } 280 result.append(prefix); 281 282 result.append('}'); 283 result.append(NEWLINE); 284 285 return result.toString(); 286 } 287 288 public void dump() { 289 Debug.debug(this.toString()); 290 } 291 292}