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