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 super(); 043 this.byteOrder = byteOrder; 044 } 045 046 protected List<TiffOutputItem> getOutputItems( 047 final TiffOutputSummary outputSummary) throws ImageWriteException { 048 final List<TiffOutputItem> result = new ArrayList<>(); 049 050 for (final TiffOutputDirectory directory : directories) { 051 result.addAll(directory.getOutputItems(outputSummary)); 052 } 053 054 return result; 055 } 056 057 public void addDirectory(final TiffOutputDirectory directory) 058 throws ImageWriteException { 059 if (null != findDirectory(directory.type)) { 060 throw new ImageWriteException( 061 "Output set already contains a directory of that type."); 062 } 063 directories.add(directory); 064 } 065 066 public List<TiffOutputDirectory> getDirectories() { 067 return new ArrayList<>(directories); 068 } 069 070 public TiffOutputDirectory getRootDirectory() { 071 return findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_ROOT); 072 } 073 074 public TiffOutputDirectory getExifDirectory() { 075 return findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_EXIF); 076 } 077 078 public TiffOutputDirectory getOrCreateRootDirectory() 079 throws ImageWriteException { 080 final TiffOutputDirectory result = findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_ROOT); 081 if (null != result) { 082 return result; 083 } 084 return addRootDirectory(); 085 } 086 087 public TiffOutputDirectory getOrCreateExifDirectory() 088 throws ImageWriteException { 089 // EXIF directory requires root directory. 090 getOrCreateRootDirectory(); 091 092 final TiffOutputDirectory result = findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_EXIF); 093 if (null != result) { 094 return result; 095 } 096 return addExifDirectory(); 097 } 098 099 public TiffOutputDirectory getOrCreateGPSDirectory() 100 throws ImageWriteException { 101 // GPS directory requires EXIF directory 102 getOrCreateExifDirectory(); 103 104 final TiffOutputDirectory result = findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_GPS); 105 if (null != result) { 106 return result; 107 } 108 return addGPSDirectory(); 109 } 110 111 public TiffOutputDirectory getGPSDirectory() { 112 return findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_GPS); 113 } 114 115 public TiffOutputDirectory getInteroperabilityDirectory() { 116 return findDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_INTEROPERABILITY); 117 } 118 119 public TiffOutputDirectory findDirectory(final int directoryType) { 120 for (final TiffOutputDirectory directory : directories) { 121 if (directory.type == directoryType) { 122 return directory; 123 } 124 } 125 return null; 126 } 127 128 /** 129 * A convenience method to update GPS values in EXIF metadata. 130 * 131 * @param longitude 132 * Longitude in degrees E, negative values are W. 133 * @param latitude 134 * latitude in degrees N, negative values are S. 135 * @throws ImageWriteException if it fails to write the new data to the GPS directory 136 */ 137 public void setGPSInDegrees(double longitude, double latitude) 138 throws ImageWriteException { 139 final TiffOutputDirectory gpsDirectory = getOrCreateGPSDirectory(); 140 141 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_VERSION_ID); 142 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_VERSION_ID, GpsTagConstants.gpsVersion()); 143 144 final String longitudeRef = longitude < 0 ? "W" : "E"; 145 longitude = Math.abs(longitude); 146 final String latitudeRef = latitude < 0 ? "S" : "N"; 147 latitude = Math.abs(latitude); 148 149 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF); 150 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_LONGITUDE_REF, 151 longitudeRef); 152 153 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF); 154 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_LATITUDE_REF, 155 latitudeRef); 156 157 { 158 double value = longitude; 159 final double longitudeDegrees = (long) value; 160 value %= 1; 161 value *= 60.0; 162 final double longitudeMinutes = (long) value; 163 value %= 1; 164 value *= 60.0; 165 final double longitudeSeconds = value; 166 167 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_LONGITUDE); 168 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_LONGITUDE, 169 RationalNumber.valueOf(longitudeDegrees), 170 RationalNumber.valueOf(longitudeMinutes), 171 RationalNumber.valueOf(longitudeSeconds)); 172 } 173 174 { 175 double value = latitude; 176 final double latitudeDegrees = (long) value; 177 value %= 1; 178 value *= 60.0; 179 final double latitudeMinutes = (long) value; 180 value %= 1; 181 value *= 60.0; 182 final double latitudeSeconds = value; 183 184 gpsDirectory.removeField(GpsTagConstants.GPS_TAG_GPS_LATITUDE); 185 gpsDirectory.add(GpsTagConstants.GPS_TAG_GPS_LATITUDE, 186 RationalNumber.valueOf(latitudeDegrees), 187 RationalNumber.valueOf(latitudeMinutes), 188 RationalNumber.valueOf(latitudeSeconds)); 189 } 190 191 } 192 193 public void removeField(final TagInfo tagInfo) { 194 removeField(tagInfo.tag); 195 } 196 197 public void removeField(final int tag) { 198 for (final TiffOutputDirectory directory : directories) { 199 directory.removeField(tag); 200 } 201 } 202 203 public TiffOutputField findField(final TagInfo tagInfo) { 204 return findField(tagInfo.tag); 205 } 206 207 public TiffOutputField findField(final int tag) { 208 for (final TiffOutputDirectory directory : directories) { 209 final TiffOutputField field = directory.findField(tag); 210 if (null != field) { 211 return field; 212 } 213 } 214 return null; 215 } 216 217 public TiffOutputDirectory addRootDirectory() throws ImageWriteException { 218 final TiffOutputDirectory result = new TiffOutputDirectory( 219 TiffDirectoryConstants.DIRECTORY_TYPE_ROOT, byteOrder); 220 addDirectory(result); 221 return result; 222 } 223 224 public TiffOutputDirectory addExifDirectory() throws ImageWriteException { 225 final TiffOutputDirectory result = new TiffOutputDirectory( 226 TiffDirectoryConstants.DIRECTORY_TYPE_EXIF, byteOrder); 227 addDirectory(result); 228 return result; 229 } 230 231 public TiffOutputDirectory addGPSDirectory() throws ImageWriteException { 232 final TiffOutputDirectory result = new TiffOutputDirectory( 233 TiffDirectoryConstants.DIRECTORY_TYPE_GPS, byteOrder); 234 addDirectory(result); 235 return result; 236 } 237 238 public TiffOutputDirectory addInteroperabilityDirectory() 239 throws ImageWriteException { 240 getOrCreateExifDirectory(); 241 242 final TiffOutputDirectory result = new TiffOutputDirectory( 243 TiffDirectoryConstants.DIRECTORY_TYPE_INTEROPERABILITY, byteOrder); 244 addDirectory(result); 245 return result; 246 } 247 248 @Override 249 public String toString() { 250 return toString(null); 251 } 252 253 public String toString(String prefix) { 254 if (prefix == null) { 255 prefix = ""; 256 } 257 258 final StringBuilder result = new StringBuilder(39); 259 260 result.append(prefix); 261 result.append("TiffOutputSet {"); 262 result.append(NEWLINE); 263 264 result.append(prefix); 265 result.append("byteOrder: "); 266 result.append(byteOrder); 267 result.append(NEWLINE); 268 269 for (int i = 0; i < directories.size(); i++) { 270 final TiffOutputDirectory directory = directories.get(i); 271 result.append(String.format("%s\tdirectory %d: %s (%d)%n", 272 prefix, i, directory.description(), directory.type)); 273 274 final List<TiffOutputField> fields = directory.getFields(); 275 for (final TiffOutputField field : fields) { 276 result.append(prefix); 277 result.append("\t\tfield ").append(i).append(": ").append(field.tagInfo); 278 result.append(NEWLINE); 279 } 280 } 281 result.append(prefix); 282 283 result.append('}'); 284 result.append(NEWLINE); 285 286 return result.toString(); 287 } 288 289 public void dump() { 290 Debug.debug(this.toString()); 291 } 292 293}