View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.imaging.formats.tiff.write;
18  
19  import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.DEFAULT_TIFF_BYTE_ORDER;
20  
21  import java.nio.ByteOrder;
22  import java.util.ArrayList;
23  import java.util.Iterator;
24  import java.util.List;
25  
26  import org.apache.commons.imaging.ImagingException;
27  import org.apache.commons.imaging.common.RationalNumber;
28  import org.apache.commons.imaging.formats.tiff.constants.GpsTagConstants;
29  import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryConstants;
30  import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo;
31  import org.apache.commons.imaging.internal.Debug;
32  
33  public final class TiffOutputSet implements Iterable<TiffOutputDirectory> {
34  
35      private static final String NEWLINE = System.lineSeparator();
36      public final ByteOrder byteOrder;
37      private final List<TiffOutputDirectory> directories = new ArrayList<>();
38  
39      public TiffOutputSet() {
40          this(DEFAULT_TIFF_BYTE_ORDER);
41      }
42  
43      public TiffOutputSet(final ByteOrder byteOrder) {
44          this.byteOrder = byteOrder;
45      }
46  
47      public void addDirectory(final TiffOutputDirectory directory) throws ImagingException {
48          if (null != findDirectory(directory.getType())) {
49              throw new ImagingException("Output set already contains a directory of that type.");
50          }
51          directories.add(directory);
52      }
53  
54      public TiffOutputDirectory addExifDirectory() throws ImagingException {
55          final TiffOutputDirectory result = new TiffOutputDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_EXIF, byteOrder);
56          addDirectory(result);
57          return result;
58      }
59  
60      public TiffOutputDirectory addGpsDirectory() throws ImagingException {
61          final TiffOutputDirectory result = new TiffOutputDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_GPS, byteOrder);
62          addDirectory(result);
63          return result;
64      }
65  
66      public TiffOutputDirectory addInteroperabilityDirectory() throws ImagingException {
67          getOrCreateExifDirectory();
68  
69          final TiffOutputDirectory result = new TiffOutputDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_INTEROPERABILITY, byteOrder);
70          addDirectory(result);
71          return result;
72      }
73  
74      public TiffOutputDirectory addRootDirectory() throws ImagingException {
75          final TiffOutputDirectory result = new TiffOutputDirectory(TiffDirectoryConstants.DIRECTORY_TYPE_ROOT, byteOrder);
76          addDirectory(result);
77          return result;
78      }
79  
80      public void dump() {
81          Debug.debug(this.toString());
82      }
83  
84      public TiffOutputDirectory findDirectory(final int directoryType) {
85          for (final TiffOutputDirectory directory : directories) {
86              if (directory.getType() == directoryType) {
87                  return directory;
88              }
89          }
90          return null;
91      }
92  
93      public TiffOutputField findField(final int tag) {
94          for (final TiffOutputDirectory directory : directories) {
95              final TiffOutputField field = directory.findField(tag);
96              if (null != field) {
97                  return field;
98              }
99          }
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 }