Changeset 6127 in josm for trunk/src/com/drew
- Timestamp:
- 2013-08-09T18:05:11+02:00 (10 years ago)
- Location:
- trunk/src/com/drew
- Files:
-
- 24 added
- 7 deleted
- 53 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/com/drew/imaging/PhotographicConversions.java
r4231 r6127 1 1 /* 2 * This is public domain software - that is, you can do whatever you want 3 * with it, and include it software that is licensed under the GNU or the 4 * BSD license, or whatever other licence you choose, including proprietary 5 * closed source licenses. I do ask that you leave this header in tact. 2 * Copyright 2002-2012 Drew Noakes 6 3 * 7 * If you make modifications to this code that you think would benefit the 8 * wider community, please send me a copy and I'll post it on my site. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 9 7 * 10 * If you make use of this code, I'd appreciate hearing about it. 11 * drew@drewnoakes.com 12 * Latest version of this software kept at 13 * http://drewnoakes.com/ 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 14 20 */ 15 21 package com.drew.imaging; … … 17 23 /** 18 24 * Contains helper methods that perform photographic conversions. 25 * 26 * @author Drew Noakes http://drewnoakes.com 19 27 */ 20 public class PhotographicConversions28 public final class PhotographicConversions 21 29 { 22 30 public final static double ROOT_TWO = Math.sqrt(2); 23 31 24 private PhotographicConversions() 25 {} 32 private PhotographicConversions() throws Exception 33 { 34 throw new Exception("Not intended for instantiation."); 35 } 26 36 27 37 /** 28 38 * Converts an aperture value to its corresponding F-stop number. 39 * 29 40 * @param aperture the aperture value to convert 30 41 * @return the F-stop number of the specified aperture … … 32 43 public static double apertureToFStop(double aperture) 33 44 { 34 double fStop = Math.pow(ROOT_TWO, aperture); 35 return fStop; 45 return Math.pow(ROOT_TWO, aperture); 36 46 37 // Puzzle?! 38 // jhead uses a different calculation as far as i can tell... this confuses me... 47 // NOTE jhead uses a different calculation as far as i can tell... this confuses me... 39 48 // fStop = (float)Math.exp(aperture * Math.log(2) * 0.5)); 40 49 } … … 42 51 /** 43 52 * Converts a shutter speed to an exposure time. 53 * 44 54 * @param shutterSpeed the shutter speed to convert 45 55 * @return the exposure time of the specified shutter speed … … 47 57 public static double shutterSpeedToExposureTime(double shutterSpeed) 48 58 { 49 return (float) (1 / Math.exp(shutterSpeed * Math.log(2)));59 return (float) (1 / Math.exp(shutterSpeed * Math.log(2))); 50 60 } 51 61 } -
trunk/src/com/drew/imaging/jpeg/JpegMetadataReader.java
r4231 r6127 1 1 /* 2 * This is public domain software - that is, you can do whatever you want 3 * with it, and include it software that is licensed under the GNU or the 4 * BSD license, or whatever other licence you choose, including proprietary 5 * closed source licenses. I do ask that you leave this header in tact. 2 * Copyright 2002-2012 Drew Noakes 6 3 * 7 * If you make modifications to this code that you think would benefit the 8 * wider community, please send me a copy and I'll post it on my site. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 9 7 * 10 * If you make use of this code, I'd appreciate hearing about it. 11 * drew@drewnoakes.com 12 * Latest version of this software kept at 13 * http://drewnoakes.com/ 8 * http://www.apache.org/licenses/LICENSE-2.0 14 9 * 15 * Created by dnoakes on 12-Nov-2002 18:51:36 using IntelliJ IDEA. 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 16 20 */ 17 21 package com.drew.imaging.jpeg; 18 22 19 import com.drew.metadata.Directory; 23 import com.drew.lang.ByteArrayReader; 24 import com.drew.lang.annotations.NotNull; 20 25 import com.drew.metadata.Metadata; 21 import com.drew.metadata.MetadataException;22 import com.drew.metadata.Tag;23 import com.drew.metadata.exif.ExifDirectory;24 26 import com.drew.metadata.exif.ExifReader; 25 27 import com.drew.metadata.iptc.IptcReader; 26 28 import com.drew.metadata.jpeg.JpegCommentReader; 29 import com.drew.metadata.jpeg.JpegDirectory; 27 30 import com.drew.metadata.jpeg.JpegReader; 28 31 … … 30 33 import java.io.IOException; 31 34 import java.io.InputStream; 32 import java.util.Iterator;33 35 34 36 /** 37 * Obtains all available metadata from Jpeg formatted files. 35 38 * 39 * @author Drew Noakes http://drewnoakes.com 36 40 */ 37 41 public class JpegMetadataReader 38 42 { 43 // TODO investigate supporting javax.imageio 39 44 // public static Metadata readMetadata(IIOMetadata metadata) throws JpegProcessingException {} 40 45 // public static Metadata readMetadata(ImageInputStream in) throws JpegProcessingException{} … … 42 47 // public static Metadata readMetadata(ImageReader reader) throws JpegProcessingException{} 43 48 44 public static Metadata readMetadata(InputStream in) throws JpegProcessingException 49 @NotNull 50 public static Metadata readMetadata(@NotNull InputStream inputStream) throws JpegProcessingException 45 51 { 46 JpegSegmentReader segmentReader = new JpegSegmentReader(in); 47 return extractMetadataFromJpegSegmentReader(segmentReader); 52 return readMetadata(inputStream, true); 48 53 } 49 54 50 public static Metadata readMetadata(File file) throws JpegProcessingException 55 @NotNull 56 public static Metadata readMetadata(@NotNull InputStream inputStream, final boolean waitForBytes) throws JpegProcessingException 57 { 58 JpegSegmentReader segmentReader = new JpegSegmentReader(inputStream, waitForBytes); 59 return extractMetadataFromJpegSegmentReader(segmentReader.getSegmentData()); 60 } 61 62 @NotNull 63 public static Metadata readMetadata(@NotNull File file) throws JpegProcessingException, IOException 51 64 { 52 65 JpegSegmentReader segmentReader = new JpegSegmentReader(file); 53 return extractMetadataFromJpegSegmentReader(segmentReader );66 return extractMetadataFromJpegSegmentReader(segmentReader.getSegmentData()); 54 67 } 55 68 56 public static Metadata extractMetadataFromJpegSegmentReader(JpegSegmentReader segmentReader) 69 @NotNull 70 public static Metadata extractMetadataFromJpegSegmentReader(@NotNull JpegSegmentData segmentReader) 57 71 { 58 72 final Metadata metadata = new Metadata(); 59 try { 60 byte[] exifSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_APP1); 61 new ExifReader(exifSegment).extract(metadata); 62 } catch (JpegProcessingException e) { 63 // in the interests of catching as much data as possible, continue 64 // TODO lodge error message within exif directory? 73 74 // Loop through looking for all SOFn segments. When we find one, we know what type of compression 75 // was used for the JPEG, and we can process the JPEG metadata in the segment too. 76 for (byte i = 0; i < 16; i++) { 77 // There are no SOF4 or SOF12 segments, so don't bother 78 if (i == 4 || i == 12) 79 continue; 80 // Should never have more than one SOFn for a given 'n'. 81 byte[] jpegSegment = segmentReader.getSegment((byte)(JpegSegmentReader.SEGMENT_SOF0 + i)); 82 if (jpegSegment == null) 83 continue; 84 JpegDirectory directory = metadata.getOrCreateDirectory(JpegDirectory.class); 85 directory.setInt(JpegDirectory.TAG_JPEG_COMPRESSION_TYPE, i); 86 new JpegReader().extract(new ByteArrayReader(jpegSegment), metadata); 87 break; 65 88 } 66 89 67 try { 68 byte[] iptcSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_APPD); 69 new IptcReader(iptcSegment).extract(metadata); 70 } catch (JpegProcessingException e) { 71 // TODO lodge error message within iptc directory? 90 // There should never be more than one COM segment. 91 byte[] comSegment = segmentReader.getSegment(JpegSegmentReader.SEGMENT_COM); 92 if (comSegment != null) 93 new JpegCommentReader().extract(new ByteArrayReader(comSegment), metadata); 94 95 // Loop through all APP1 segments, checking the leading bytes to identify the format of each. 96 for (byte[] app1Segment : segmentReader.getSegments(JpegSegmentReader.SEGMENT_APP1)) { 97 if (app1Segment.length > 3 && "EXIF".equalsIgnoreCase(new String(app1Segment, 0, 4))) 98 new ExifReader().extract(new ByteArrayReader(app1Segment), metadata); 99 100 //if (app1Segment.length > 27 && "http://ns.adobe.com/xap/1.0/".equalsIgnoreCase(new String(app1Segment, 0, 28))) 101 // new XmpReader().extract(new ByteArrayReader(app1Segment), metadata); 72 102 } 73 103 74 try { 75 byte[] jpegSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_SOF0); 76 new JpegReader(jpegSegment).extract(metadata); 77 } catch (JpegProcessingException e) { 78 // TODO lodge error message within jpeg directory? 79 } 80 81 try { 82 byte[] jpegCommentSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_COM); 83 new JpegCommentReader(jpegCommentSegment).extract(metadata); 84 } catch (JpegProcessingException e) { 85 // TODO lodge error message within jpegcomment directory? 86 } 104 // Loop through all APPD segments, checking the leading bytes to identify the format of each. 105 for (byte[] appdSegment : segmentReader.getSegments(JpegSegmentReader.SEGMENT_APPD)) { 106 if (appdSegment.length > 12 && "Photoshop 3.0".compareTo(new String(appdSegment, 0, 13))==0) { 107 //new PhotoshopReader().extract(new ByteArrayReader(appdSegment), metadata); 108 } else { 109 // TODO might be able to check for a leading 0x1c02 for IPTC data... 110 new IptcReader().extract(new ByteArrayReader(appdSegment), metadata); 111 } 112 } 87 113 88 114 return metadata; 89 115 } 90 116 91 private JpegMetadataReader() 117 private JpegMetadataReader() throws Exception 92 118 { 93 } 94 95 public static void main(String[] args) throws MetadataException, IOException 96 { 97 Metadata metadata = null; 98 try { 99 metadata = JpegMetadataReader.readMetadata(new File(args[0])); 100 } catch (Exception e) { 101 e.printStackTrace(System.err); 102 System.exit(1); 103 } 104 105 // iterate over the exif data and print to System.out 106 Iterator directories = metadata.getDirectoryIterator(); 107 while (directories.hasNext()) { 108 Directory directory = (Directory)directories.next(); 109 Iterator tags = directory.getTagIterator(); 110 while (tags.hasNext()) { 111 Tag tag = (Tag)tags.next(); 112 try { 113 System.out.println("[" + directory.getName() + "] " + tag.getTagName() + " = " + tag.getDescription()); 114 } catch (MetadataException e) { 115 System.err.println(e.getMessage()); 116 System.err.println(tag.getDirectoryName() + " " + tag.getTagName() + " (error)"); 117 } 118 } 119 if (directory.hasErrors()) { 120 Iterator errors = directory.getErrors(); 121 while (errors.hasNext()) { 122 System.out.println("ERROR: " + errors.next()); 123 } 124 } 125 } 126 127 if (args.length>1 && args[1].trim().equals("/thumb")) 128 { 129 ExifDirectory directory = (ExifDirectory)metadata.getDirectory(ExifDirectory.class); 130 if (directory.containsThumbnail()) 131 { 132 System.out.println("Writing thumbnail..."); 133 directory.writeThumbnail(args[0].trim() + ".thumb.jpg"); 134 } 135 else 136 { 137 System.out.println("No thumbnail data exists in this image"); 138 } 139 } 119 throw new Exception("Not intended for instantiation"); 140 120 } 141 121 } 122 -
trunk/src/com/drew/imaging/jpeg/JpegProcessingException.java
r4231 r6127 1 1 /* 2 * JpegProcessingException.java2 * Copyright 2002-2012 Drew Noakes 3 3 * 4 * This class is public domain software - that is, you can do whatever you want 5 * with it, and include it software that is licensed under the GNU or the 6 * BSD license, or whatever other licence you choose, including proprietary 7 * closed source licenses. I do ask that you leave this header in tact. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 8 7 * 9 * If you make modifications to this code that you think would benefit the 10 * wider community, please send me a copy and I'll post it on my site. 8 * http://www.apache.org/licenses/LICENSE-2.0 11 9 * 12 * If you make use of this code, I'd appreciate hearing about it. 13 * drew@drewnoakes.com 14 * Latest version of this software kept at 15 * http://drewnoakes.com/ 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 16 15 * 17 * Created by dnoakes on 04-Nov-2002 19:31:29 using IntelliJ IDEA. 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 18 20 */ 19 21 package com.drew.imaging.jpeg; 20 22 21 import com.drew.lang.CompoundException; 23 import com.drew.imaging.ImageProcessingException; 24 import com.drew.lang.annotations.Nullable; 22 25 23 26 /** 24 * An exception class thrown upon unexpected and fatal conditions while processing 25 * a Jpeg file.26 * @author 27 * An exception class thrown upon unexpected and fatal conditions while processing a Jpeg file. 28 * 29 * @author Drew Noakes http://drewnoakes.com 27 30 */ 28 public class JpegProcessingException extends CompoundException31 public class JpegProcessingException extends ImageProcessingException 29 32 { 30 public JpegProcessingException(String message) 33 private static final long serialVersionUID = -7870179776125450158L; 34 35 public JpegProcessingException(@Nullable String message) 31 36 { 32 37 super(message); 33 38 } 34 39 35 public JpegProcessingException( String message,Throwable cause)40 public JpegProcessingException(@Nullable String message, @Nullable Throwable cause) 36 41 { 37 42 super(message, cause); 38 43 } 39 44 40 public JpegProcessingException( Throwable cause)45 public JpegProcessingException(@Nullable Throwable cause) 41 46 { 42 47 super(cause); -
trunk/src/com/drew/imaging/jpeg/JpegSegmentData.java
r4231 r6127 1 1 /* 2 * This is public domain software - that is, you can do whatever you want 3 * with it, and include it software that is licensed under the GNU or the 4 * BSD license, or whatever other licence you choose, including proprietary 5 * closed source licenses. I do ask that you leave this header in tact. 6 * 7 * If you make modifications to this code that you think would benefit the 8 * wider community, please send me a copy and I'll post it on my site. 9 * 10 * If you make use of this code, I'd appreciate hearing about it. 11 * drew@drewnoakes.com 12 * Latest version of this software kept at 13 * http://drewnoakes.com/ 2 * Copyright 2002-2012 Drew Noakes 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 14 20 */ 15 21 package com.drew.imaging.jpeg; 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 16 25 17 26 import java.io.*; … … 22 31 /** 23 32 * Holds a collection of Jpeg data segments. This need not necessarily be all segments 24 * within the Jpeg. For example, it may be convenient to port aboutonly the non-image33 * within the Jpeg. For example, it may be convenient to store only the non-image 25 34 * segments when analysing (or serializing) metadata. 35 * <p/> 36 * Segments are keyed via their segment marker (a byte). Where multiple segments use the 37 * same segment marker, they will all be stored and available. 38 * 39 * @author Drew Noakes http://drewnoakes.com 26 40 */ 27 41 public class JpegSegmentData implements Serializable 28 42 { 29 static final long serialVersionUID = 7110175216435025451L;43 private static final long serialVersionUID = 7110175216435025451L; 30 44 31 45 /** A map of byte[], keyed by the segment marker */ 32 private final HashMap _segmentDataMap; 33 34 public JpegSegmentData() 35 { 36 _segmentDataMap = new HashMap(10); 37 } 38 39 public void addSegment(byte segmentMarker, byte[] segmentBytes) 40 { 41 List segmentList = getOrCreateSegmentList(segmentMarker); 46 @NotNull 47 private final HashMap<Byte, List<byte[]>> _segmentDataMap = new HashMap<Byte, List<byte[]>>(10); 48 49 /** 50 * Adds segment bytes to the collection. 51 * @param segmentMarker 52 * @param segmentBytes 53 */ 54 @SuppressWarnings({ "MismatchedQueryAndUpdateOfCollection" }) 55 public void addSegment(byte segmentMarker, @NotNull byte[] segmentBytes) 56 { 57 final List<byte[]> segmentList = getOrCreateSegmentList(segmentMarker); 42 58 segmentList.add(segmentBytes); 43 59 } 44 60 61 /** 62 * Gets the first Jpeg segment data for the specified marker. 63 * @param segmentMarker the byte identifier for the desired segment 64 * @return a byte[] containing segment data or null if no data exists for that segment 65 */ 66 @Nullable 45 67 public byte[] getSegment(byte segmentMarker) 46 68 { … … 48 70 } 49 71 72 /** 73 * Gets segment data for a specific occurrence and marker. Use this method when more than one occurrence 74 * of segment data for a given marker exists. 75 * @param segmentMarker identifies the required segment 76 * @param occurrence the zero-based index of the occurrence 77 * @return the segment data as a byte[], or null if no segment exists for the marker & occurrence 78 */ 79 @Nullable 50 80 public byte[] getSegment(byte segmentMarker, int occurrence) 51 81 { 52 final List segmentList = getSegmentList(segmentMarker);82 final List<byte[]> segmentList = getSegmentList(segmentMarker); 53 83 54 84 if (segmentList==null || segmentList.size()<=occurrence) 55 85 return null; 56 86 else 57 return (byte[]) segmentList.get(occurrence); 58 } 59 87 return segmentList.get(occurrence); 88 } 89 90 /** 91 * Returns all instances of a given Jpeg segment. If no instances exist, an empty sequence is returned. 92 * 93 * @param segmentMarker a number which identifies the type of Jpeg segment being queried 94 * @return zero or more byte arrays, each holding the data of a Jpeg segment 95 */ 96 @NotNull 97 public Iterable<byte[]> getSegments(byte segmentMarker) 98 { 99 final List<byte[]> segmentList = getSegmentList(segmentMarker); 100 return segmentList==null ? new ArrayList<byte[]>() : segmentList; 101 } 102 103 @Nullable 104 public List<byte[]> getSegmentList(byte segmentMarker) 105 { 106 return _segmentDataMap.get(Byte.valueOf(segmentMarker)); 107 } 108 109 @NotNull 110 private List<byte[]> getOrCreateSegmentList(byte segmentMarker) 111 { 112 List<byte[]> segmentList; 113 if (_segmentDataMap.containsKey(segmentMarker)) { 114 segmentList = _segmentDataMap.get(segmentMarker); 115 } else { 116 segmentList = new ArrayList<byte[]>(); 117 _segmentDataMap.put(segmentMarker, segmentList); 118 } 119 return segmentList; 120 } 121 122 /** 123 * Returns the count of segment data byte arrays stored for a given segment marker. 124 * @param segmentMarker identifies the required segment 125 * @return the segment count (zero if no segments exist). 126 */ 60 127 public int getSegmentCount(byte segmentMarker) 61 128 { 62 final List segmentList = getSegmentList(segmentMarker); 63 if (segmentList==null) 64 return 0; 65 else 66 return segmentList.size(); 67 } 68 129 final List<byte[]> segmentList = getSegmentList(segmentMarker); 130 return segmentList == null ? 0 : segmentList.size(); 131 } 132 133 /** 134 * Removes a specified instance of a segment's data from the collection. Use this method when more than one 135 * occurrence of segment data for a given marker exists. 136 * @param segmentMarker identifies the required segment 137 * @param occurrence the zero-based index of the segment occurrence to remove. 138 */ 139 @SuppressWarnings({ "MismatchedQueryAndUpdateOfCollection" }) 69 140 public void removeSegmentOccurrence(byte segmentMarker, int occurrence) 70 141 { 71 final List segmentList = (List)_segmentDataMap.get(new Byte(segmentMarker));142 final List<byte[]> segmentList = _segmentDataMap.get(Byte.valueOf(segmentMarker)); 72 143 segmentList.remove(occurrence); 73 144 } 74 145 146 /** 147 * Removes all segments from the collection having the specified marker. 148 * @param segmentMarker identifies the required segment 149 */ 75 150 public void removeSegment(byte segmentMarker) 76 151 { 77 _segmentDataMap.remove(new Byte(segmentMarker)); 78 } 79 80 private List getSegmentList(byte segmentMarker) 81 { 82 return (List)_segmentDataMap.get(new Byte(segmentMarker)); 83 } 84 85 private List getOrCreateSegmentList(byte segmentMarker) 86 { 87 List segmentList; 88 Byte key = new Byte(segmentMarker); 89 if (_segmentDataMap.containsKey(key)) { 90 segmentList = (List)_segmentDataMap.get(key); 91 } else { 92 segmentList = new ArrayList(); 93 _segmentDataMap.put(key, segmentList); 94 } 95 return segmentList; 96 } 97 152 _segmentDataMap.remove(Byte.valueOf(segmentMarker)); 153 } 154 155 /** 156 * Determines whether data is present for a given segment marker. 157 * @param segmentMarker identifies the required segment 158 * @return true if data exists, otherwise false 159 */ 98 160 public boolean containsSegment(byte segmentMarker) 99 161 { 100 return _segmentDataMap.containsKey(new Byte(segmentMarker)); 101 } 102 103 public static void ToFile(File file, JpegSegmentData segmentData) throws IOException 104 { 105 ObjectOutputStream outputStream = null; 162 return _segmentDataMap.containsKey(Byte.valueOf(segmentMarker)); 163 } 164 165 /** 166 * Serialises the contents of a JpegSegmentData to a file. 167 * @param file to file to write from 168 * @param segmentData the data to write 169 * @throws IOException if problems occur while writing 170 */ 171 public static void toFile(@NotNull File file, @NotNull JpegSegmentData segmentData) throws IOException 172 { 173 FileOutputStream fileOutputStream = null; 106 174 try 107 175 { 108 outputStream = new ObjectOutputStream(new FileOutputStream(file));109 outputStream.writeObject(segmentData);176 fileOutputStream = new FileOutputStream(file); 177 new ObjectOutputStream(fileOutputStream).writeObject(segmentData); 110 178 } 111 179 finally 112 180 { 113 if (outputStream!=null) 114 outputStream.close(); 115 } 116 } 117 118 public static JpegSegmentData FromFile(File file) throws IOException, ClassNotFoundException 181 if (fileOutputStream!=null) 182 fileOutputStream.close(); 183 } 184 } 185 186 /** 187 * Deserialises the contents of a JpegSegmentData from a file. 188 * @param file the file to read from 189 * @return the JpegSegmentData as read 190 * @throws IOException if problems occur while reading 191 * @throws ClassNotFoundException if problems occur while deserialising 192 */ 193 @NotNull 194 public static JpegSegmentData fromFile(@NotNull File file) throws IOException, ClassNotFoundException 119 195 { 120 196 ObjectInputStream inputStream = null; -
trunk/src/com/drew/imaging/jpeg/JpegSegmentReader.java
r4231 r6127 1 1 /* 2 * JpegSegmentReader.java3 * 4 * This class written by Drew Noakes, in accordance with the Jpeg specification.5 * 6 * This is public domain software - that is, you can do whatever you want7 * with it, and include it software that is licensed under the GNU or the8 * BSD license, or whatever other licence you choose, including proprietary9 * closed source licenses. I do ask that you leave this header in tact.10 * 11 * If you make modifications to this code that you think would benefit the12 * wider community, please send me a copy and I'll post it on my site.13 * 14 * If you make use of this code, I'd appreciate hearing about it.15 * drew@drewnoakes.com16 * Latest version of this software kept at17 * http://drewnoakes.com/18 * 19 * Created by dnoakes on 04-Nov-2002 00:54:00 using IntelliJ IDEA2 * Copyright 2002-2012 Drew Noakes 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 20 20 */ 21 21 package com.drew.imaging.jpeg; 22 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 25 23 26 import java.io.*; 24 27 25 28 /** 26 29 * Performs read functions of Jpeg files, returning specific file segments. 27 * TODO add a findAvailableSegments() method28 * TODO add more segment identifiers29 * TODO add a getSegmentDescription() method, returning for example 'App1 application data segment, commonly containing Exif data'30 30 * @author Drew Noakes http://drewnoakes.com 31 31 */ 32 32 public class JpegSegmentReader 33 33 { 34 // Jpeg data can be sourced from either a file, byte[] or InputStream 35 36 /** Jpeg file */ 37 private final File _file; 38 /** Jpeg data as byte array */ 39 private final byte[] _data; 40 /** Jpeg data as an InputStream */ 41 private final InputStream _stream; 42 43 private JpegSegmentData _segmentData; 44 45 /** 46 * Private, because this segment crashes my algorithm, and searching for 47 * it doesn't work (yet). 34 // TODO add a findAvailableSegments() method 35 // TODO add more segment identifiers 36 // TODO add a getSegmentDescription() method, returning for example 'App1 application data segment, commonly containing Exif data' 37 38 @NotNull 39 private final JpegSegmentData _segmentData; 40 41 /** 42 * Private, because this segment crashes my algorithm, and searching for it doesn't work (yet). 48 43 */ 49 44 private static final byte SEGMENT_SOS = (byte)0xDA; … … 54 49 private static final byte MARKER_EOI = (byte)0xD9; 55 50 56 /** APP0 Jpeg segment identifier -- J fif data. */51 /** APP0 Jpeg segment identifier -- JFIF data (also JFXX apparently). */ 57 52 public static final byte SEGMENT_APP0 = (byte)0xE0; 58 /** APP1 Jpeg segment identifier -- where Exif data is kept. */53 /** APP1 Jpeg segment identifier -- where Exif data is kept. XMP data is also kept in here, though usually in a second instance. */ 59 54 public static final byte SEGMENT_APP1 = (byte)0xE1; 60 55 /** APP2 Jpeg segment identifier. */ … … 74 69 /** APP9 Jpeg segment identifier. */ 75 70 public static final byte SEGMENT_APP9 = (byte)0xE9; 76 /** APPA Jpeg segment identifier -- can hold Unicode comments. */71 /** APPA (App10) Jpeg segment identifier -- can hold Unicode comments. */ 77 72 public static final byte SEGMENT_APPA = (byte)0xEA; 78 /** APPB Jpeg segment identifier. */73 /** APPB (App11) Jpeg segment identifier. */ 79 74 public static final byte SEGMENT_APPB = (byte)0xEB; 80 /** APPC Jpeg segment identifier. */75 /** APPC (App12) Jpeg segment identifier. */ 81 76 public static final byte SEGMENT_APPC = (byte)0xEC; 82 /** APPD Jpeg segment identifier -- IPTC data in here. */77 /** APPD (App13) Jpeg segment identifier -- IPTC data in here. */ 83 78 public static final byte SEGMENT_APPD = (byte)0xED; 84 /** APPE Jpeg segment identifier. */79 /** APPE (App14) Jpeg segment identifier. */ 85 80 public static final byte SEGMENT_APPE = (byte)0xEE; 86 /** APPF Jpeg segment identifier. */81 /** APPF (App15) Jpeg segment identifier. */ 87 82 public static final byte SEGMENT_APPF = (byte)0xEF; 88 83 /** Start Of Image segment identifier. */ … … 101 96 * @param file the Jpeg file to read segments from 102 97 */ 103 public JpegSegmentReader(File file) throws JpegProcessingException 104 { 105 _file = file; 106 _data = null; 107 _stream = null; 108 109 readSegments(); 98 @SuppressWarnings({ "ConstantConditions" }) 99 public JpegSegmentReader(@NotNull File file) throws JpegProcessingException, IOException 100 { 101 if (file==null) 102 throw new NullPointerException(); 103 104 InputStream inputStream = null; 105 try { 106 inputStream = new FileInputStream(file); 107 _segmentData = readSegments(new BufferedInputStream(inputStream), false); 108 } finally { 109 if (inputStream != null) 110 inputStream.close(); 111 } 110 112 } 111 113 … … 114 116 * @param fileContents the byte array containing Jpeg data 115 117 */ 116 public JpegSegmentReader(byte[] fileContents) throws JpegProcessingException117 {118 _file = null;119 _data = fileContents;120 _stream = null;121 122 readSegments();123 }124 125 public JpegSegmentReader(InputStream in) throws JpegProcessingException 126 {127 _stream = in;128 _file = null;129 _data = null;130 131 readSegments();132 }133 134 public JpegSegmentReader(JpegSegmentData segmentData)135 { 136 _file = null;137 _data = null;138 _stream = null;139 140 _segmentData = segmentData;118 @SuppressWarnings({ "ConstantConditions" }) 119 public JpegSegmentReader(@NotNull byte[] fileContents) throws JpegProcessingException 120 { 121 if (fileContents==null) 122 throw new NullPointerException(); 123 124 BufferedInputStream stream = new BufferedInputStream(new ByteArrayInputStream(fileContents)); 125 _segmentData = readSegments(stream, false); 126 } 127 128 /** 129 * Creates a JpegSegmentReader for an InputStream. 130 * @param inputStream the InputStream containing Jpeg data 131 */ 132 @SuppressWarnings({ "ConstantConditions" }) 133 public JpegSegmentReader(@NotNull InputStream inputStream, boolean waitForBytes) throws JpegProcessingException 134 { 135 if (inputStream==null) 136 throw new NullPointerException(); 137 138 BufferedInputStream bufferedInputStream = inputStream instanceof BufferedInputStream 139 ? (BufferedInputStream)inputStream 140 : new BufferedInputStream(inputStream); 141 142 _segmentData = readSegments(bufferedInputStream, waitForBytes); 141 143 } 142 144 … … 146 148 * @param segmentMarker the byte identifier for the desired segment 147 149 * @return the byte array if found, else null 148 * @throws JpegProcessingException for any problems processing the Jpeg data, 149 * including inner IOExceptions 150 */ 151 public byte[] readSegment(byte segmentMarker) throws JpegProcessingException 150 */ 151 @Nullable 152 public byte[] readSegment(byte segmentMarker) 152 153 { 153 154 return readSegment(segmentMarker, 0); … … 155 156 156 157 /** 157 * Reads the first instance of a given Jpeg segment, returning the contents as158 * a byte array.158 * Reads the Nth instance of a given Jpeg segment, returning the contents as a byte array. 159 * 159 160 * @param segmentMarker the byte identifier for the desired segment 160 161 * @param occurrence the occurrence of the specified segment within the jpeg file 161 162 * @return the byte array if found, else null 162 163 */ 164 @Nullable 163 165 public byte[] readSegment(byte segmentMarker, int occurrence) 164 166 { … … 166 168 } 167 169 170 /** 171 * Returns all instances of a given Jpeg segment. If no instances exist, an empty sequence is returned. 172 * 173 * @param segmentMarker a number which identifies the type of Jpeg segment being queried 174 * @return zero or more byte arrays, each holding the data of a Jpeg segment 175 */ 176 @NotNull 177 public Iterable<byte[]> readSegments(byte segmentMarker) 178 { 179 return _segmentData.getSegments(segmentMarker); 180 } 181 182 /** 183 * Returns the number of segments having the specified JPEG segment marker. 184 * @param segmentMarker the JPEG segment identifying marker. 185 * @return the count of matching segments. 186 */ 168 187 public final int getSegmentCount(byte segmentMarker) 169 188 { … … 171 190 } 172 191 192 /** 193 * Returns the JpegSegmentData object used by this reader. 194 * @return the JpegSegmentData object. 195 */ 196 @NotNull 173 197 public final JpegSegmentData getSegmentData() 174 198 { … … 176 200 } 177 201 178 private void readSegments() throws JpegProcessingException179 {180 _segmentData = new JpegSegmentData();181 182 BufferedInputStream inStream = getJpegInputStream(); 202 @NotNull 203 private JpegSegmentData readSegments(@NotNull final BufferedInputStream jpegInputStream, boolean waitForBytes) throws JpegProcessingException 204 { 205 JpegSegmentData segmentData = new JpegSegmentData(); 206 183 207 try { 184 208 int offset = 0; 185 209 // first two bytes should be jpeg magic number 186 if (!isValidJpegHeaderBytes(inStream)) { 210 byte[] headerBytes = new byte[2]; 211 if (jpegInputStream.read(headerBytes, 0, 2)!=2) 187 212 throw new JpegProcessingException("not a jpeg file"); 188 } 213 final boolean hasValidHeader = (headerBytes[0] & 0xFF) == 0xFF && (headerBytes[1] & 0xFF) == 0xD8; 214 if (!hasValidHeader) 215 throw new JpegProcessingException("not a jpeg file"); 216 189 217 offset += 2; 190 218 do { 219 // need four bytes from stream for segment header before continuing 220 if (!checkForBytesOnStream(jpegInputStream, 4, waitForBytes)) 221 throw new JpegProcessingException("stream ended before segment header could be read"); 222 191 223 // next byte is 0xFF 192 byte segmentIdentifier = (byte)( inStream.read() & 0xFF);224 byte segmentIdentifier = (byte)(jpegInputStream.read() & 0xFF); 193 225 if ((segmentIdentifier & 0xFF) != 0xFF) { 194 226 throw new JpegProcessingException("expected jpeg segment start identifier 0xFF at offset " + offset + ", not 0x" + Integer.toHexString(segmentIdentifier & 0xFF)); … … 196 228 offset++; 197 229 // next byte is <segment-marker> 198 byte thisSegmentMarker = (byte)( inStream.read() & 0xFF);230 byte thisSegmentMarker = (byte)(jpegInputStream.read() & 0xFF); 199 231 offset++; 200 232 // next 2-bytes are <segment-size>: [high-byte] [low-byte] 201 233 byte[] segmentLengthBytes = new byte[2]; 202 inStream.read(segmentLengthBytes, 0, 2); 234 if (jpegInputStream.read(segmentLengthBytes, 0, 2) != 2) 235 throw new JpegProcessingException("Jpeg data ended unexpectedly."); 203 236 offset += 2; 204 237 int segmentLength = ((segmentLengthBytes[0] << 8) & 0xFF00) | (segmentLengthBytes[1] & 0xFF); 205 238 // segment length includes size bytes, so subtract two 206 239 segmentLength -= 2; 207 if ( segmentLength > inStream.available())240 if (!checkForBytesOnStream(jpegInputStream, segmentLength, waitForBytes)) 208 241 throw new JpegProcessingException("segment size would extend beyond file stream length"); 209 elseif (segmentLength < 0)242 if (segmentLength < 0) 210 243 throw new JpegProcessingException("segment size would be less than zero"); 211 244 byte[] segmentBytes = new byte[segmentLength]; 212 inStream.read(segmentBytes, 0, segmentLength); 245 if (jpegInputStream.read(segmentBytes, 0, segmentLength) != segmentLength) 246 throw new JpegProcessingException("Jpeg data ended unexpectedly."); 213 247 offset += segmentLength; 214 248 if ((thisSegmentMarker & 0xFF) == (SEGMENT_SOS & 0xFF)) { … … 216 250 // have to search for the two bytes: 0xFF 0xD9 (EOI). 217 251 // It comes last so simply return at this point 218 return ;252 return segmentData; 219 253 } else if ((thisSegmentMarker & 0xFF) == (MARKER_EOI & 0xFF)) { 220 254 // the 'End-Of-Image' segment -- this should never be found in this fashion 221 return ;255 return segmentData; 222 256 } else { 223 _segmentData.addSegment(thisSegmentMarker, segmentBytes);257 segmentData.addSegment(thisSegmentMarker, segmentBytes); 224 258 } 225 // didn't find the one we're looking for, loop through to the next segment226 259 } while (true); 227 260 } catch (IOException ioe) { 228 //throw new JpegProcessingException("IOException processing Jpeg file", ioe);229 261 throw new JpegProcessingException("IOException processing Jpeg file: " + ioe.getMessage(), ioe); 230 262 } finally { 231 263 try { 232 if ( inStream != null) {233 inStream.close();264 if (jpegInputStream != null) { 265 jpegInputStream.close(); 234 266 } 235 267 } catch (IOException ioe) { 236 //throw new JpegProcessingException("IOException processing Jpeg file", ioe);237 268 throw new JpegProcessingException("IOException processing Jpeg file: " + ioe.getMessage(), ioe); 238 269 } … … 240 271 } 241 272 242 /** 243 * Private helper method to create a BufferedInputStream of Jpeg data from whichever 244 * data source was specified upon construction of this instance. 245 * @return a a BufferedInputStream of Jpeg data 246 * @throws JpegProcessingException for any problems obtaining the stream 247 */ 248 private BufferedInputStream getJpegInputStream() throws JpegProcessingException 249 { 250 if (_stream!=null) { 251 if (_stream instanceof BufferedInputStream) { 252 return (BufferedInputStream) _stream; 253 } else { 254 return new BufferedInputStream(_stream); 273 private boolean checkForBytesOnStream(@NotNull BufferedInputStream stream, int bytesNeeded, boolean waitForBytes) throws IOException 274 { 275 // NOTE waiting is essential for network streams where data can be delayed, but it is not necessary for byte[] or filesystems 276 277 if (!waitForBytes) 278 return bytesNeeded <= stream.available(); 279 280 int count = 40; // * 100ms = approx 4 seconds 281 while (count > 0) { 282 if (bytesNeeded <= stream.available()) 283 return true; 284 try { 285 Thread.sleep(100); 286 } catch (InterruptedException e) { 287 // continue 255 288 } 289 count--; 256 290 } 257 InputStream inputStream; 258 if (_data == null) { 259 try { 260 inputStream = new FileInputStream(_file); 261 } catch (FileNotFoundException e) { 262 throw new JpegProcessingException("Jpeg file does not exist", e); 263 } 264 } else { 265 inputStream = new ByteArrayInputStream(_data); 266 } 267 return new BufferedInputStream(inputStream); 268 } 269 270 /** 271 * Helper method that validates the Jpeg file's magic number. 272 * @param fileStream the InputStream to read bytes from, which must be positioned 273 * at its start (i.e. no bytes read yet) 274 * @return true if the magic number is Jpeg (0xFFD8) 275 * @throws IOException for any problem in reading the file 276 */ 277 private boolean isValidJpegHeaderBytes(InputStream fileStream) throws IOException 278 { 279 byte[] header = new byte[2]; 280 fileStream.read(header, 0, 2); 281 return (header[0] & 0xFF) == 0xFF && (header[1] & 0xFF) == 0xD8; 291 return false; 282 292 } 283 293 } -
trunk/src/com/drew/lang/CompoundException.java
r4231 r6127 1 1 /* 2 * This is public domain software - that is, you can do whatever you want 3 * with it, and include it software that is licensed under the GNU or the 4 * BSD license, or whatever other licence you choose, including proprietary 5 * closed source licenses. I do ask that you leave this header in tact. 2 * Copyright 2002-2012 Drew Noakes 6 3 * 7 * If you make modifications to this code that you think would benefit the 8 * wider community, please send me a copy and I'll post it on my site. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 9 7 * 10 * If you make use of this code, I'd appreciate hearing about it. 11 * drew@drewnoakes.com 12 * Latest version of this software kept at 13 * http://drewnoakes.com/ 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 14 20 */ 15 21 package com.drew.lang; 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 16 25 17 26 import java.io.PrintStream; … … 22 31 * unavailable in previous versions. This class allows support 23 32 * of these previous JDK versions. 33 * 34 * @author Drew Noakes http://drewnoakes.com 24 35 */ 25 36 public class CompoundException extends Exception 26 37 { 27 private final Throwable _innnerException;38 private static final long serialVersionUID = -9207883813472069925L; 28 39 29 public CompoundException(String msg) 40 @Nullable 41 private final Throwable _innerException; 42 43 public CompoundException(@Nullable String msg) 30 44 { 31 45 this(msg, null); 32 46 } 33 47 34 public CompoundException( Throwable exception)48 public CompoundException(@Nullable Throwable exception) 35 49 { 36 50 this(null, exception); 37 51 } 38 52 39 public CompoundException( String msg,Throwable innerException)53 public CompoundException(@Nullable String msg, @Nullable Throwable innerException) 40 54 { 41 55 super(msg); 42 _inn nerException = innerException;56 _innerException = innerException; 43 57 } 44 58 59 @Nullable 45 60 public Throwable getInnerException() 46 61 { 47 return _inn nerException;62 return _innerException; 48 63 } 49 64 65 @NotNull 50 66 public String toString() 51 67 { 52 StringBu ffer sbuffer = new StringBuffer();53 s buffer.append(super.toString());54 if (_inn nerException != null) {55 s buffer.append("\n");56 s buffer.append("--- inner exception ---");57 s buffer.append("\n");58 s buffer.append(_innnerException.toString());68 StringBuilder string = new StringBuilder(); 69 string.append(super.toString()); 70 if (_innerException != null) { 71 string.append("\n"); 72 string.append("--- inner exception ---"); 73 string.append("\n"); 74 string.append(_innerException.toString()); 59 75 } 60 return s buffer.toString();76 return string.toString(); 61 77 } 62 78 63 public void printStackTrace( PrintStream s)79 public void printStackTrace(@NotNull PrintStream s) 64 80 { 65 81 super.printStackTrace(s); 66 if (_inn nerException != null) {82 if (_innerException != null) { 67 83 s.println("--- inner exception ---"); 68 _inn nerException.printStackTrace(s);84 _innerException.printStackTrace(s); 69 85 } 70 86 } 71 87 72 public void printStackTrace( PrintWriter s)88 public void printStackTrace(@NotNull PrintWriter s) 73 89 { 74 90 super.printStackTrace(s); 75 if (_inn nerException != null) {91 if (_innerException != null) { 76 92 s.println("--- inner exception ---"); 77 _inn nerException.printStackTrace(s);93 _innerException.printStackTrace(s); 78 94 } 79 95 } … … 82 98 { 83 99 super.printStackTrace(); 84 if (_inn nerException != null) {100 if (_innerException != null) { 85 101 System.err.println("--- inner exception ---"); 86 _inn nerException.printStackTrace();102 _innerException.printStackTrace(); 87 103 } 88 104 } -
trunk/src/com/drew/lang/NullOutputStream.java
r4231 r6127 1 /** 2 * This is public domain software - that is, you can do whatever you want 3 * with it, and include it software that is licensed under the GNU or the 4 * BSD license, or whatever other licence you choose, including proprietary 5 * closed source licenses. I do ask that you leave this header in tact. 1 /* 2 * Copyright 2002-2012 Drew Noakes 6 3 * 7 * If you make modifications to this code that you think would benefit the 8 * wider community, please send me a copy and I'll post it on my site. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 9 7 * 10 * If you make use of this code, I'd appreciate hearing about it. 11 * drew@drewnoakes.com 12 * Latest version of this software kept at 13 * http://drewnoakes.com/ 8 * http://www.apache.org/licenses/LICENSE-2.0 14 9 * 15 * Created by dnoakes on Dec 15, 2002 3:30:59 PM using IntelliJ IDEA. 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 16 20 */ 17 21 package com.drew.lang; … … 20 24 import java.io.OutputStream; 21 25 26 /** 27 * An implementation of OutputSteam that ignores write requests by doing nothing. This class may be useful in tests. 28 * 29 * @author Drew Noakes http://drewnoakes.com 30 */ 22 31 public class NullOutputStream extends OutputStream 23 32 { -
trunk/src/com/drew/lang/Rational.java
r4231 r6127 1 1 /* 2 * Rational.java 3 * 4 * This class is public domain software - that is, you can do whatever you want 5 * with it, and include it software that is licensed under the GNU or the 6 * BSD license, or whatever other licence you choose, including proprietary 7 * closed source licenses. Similarly, I release this Java version under the 8 * same license, though I do ask that you leave this header in tact. 9 * 10 * If you make modifications to this code that you think would benefit the 11 * wider community, please send me a copy and I'll post it on my site. 12 * 13 * If you make use of this code, I'd appreciate hearing about it. 14 * drew.noakes@drewnoakes.com 15 * Latest version of this software kept at 16 * http://drewnoakes.com/ 17 * 18 * Created on 6 May 2002, 18:06 19 * Updated 26 Aug 2002 by Drew 20 * - Added toSimpleString() method, which returns a simplified and hopefully more 21 * readable version of the Rational. i.e. 2/10 -> 1/5, and 10/2 -> 5 22 * Modified 29 Oct 2002 (v1.2) 23 * - Improved toSimpleString() to factor more complex rational numbers into 24 * a simpler form 25 * i.e. 26 * 10/15 -> 2/3 27 * - toSimpleString() now accepts a boolean flag, 'allowDecimals' which will 28 * display the rational number in decimal form if it fits within 5 digits 29 * i.e. 30 * 3/4 -> 0.75 when allowDecimal == true 2 * Copyright 2002-2012 Drew Noakes 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 31 20 */ 32 21 33 22 package com.drew.lang; 23 24 import com.drew.lang.annotations.NotNull; 25 import com.drew.lang.annotations.Nullable; 34 26 35 27 import java.io.Serializable; … … 38 30 * Immutable class for holding a rational number without loss of precision. Provides 39 31 * a familiar representation via toString() in form <code>numerator/denominator</code>. 40 * <p>41 * @author 32 * 33 * @author Drew Noakes http://drewnoakes.com 42 34 */ 43 35 public class Rational extends java.lang.Number implements Serializable 44 36 { 45 /** 46 * Holds the numerator. 47 */ 48 private final int numerator; 49 50 /** 51 * Holds the denominator. 52 */ 53 private final int denominator; 54 55 private int maxSimplificationCalculations = 1000; 37 private static final long serialVersionUID = 510688928138848770L; 38 39 /** Holds the numerator. */ 40 private final long _numerator; 41 42 /** Holds the denominator. */ 43 private final long _denominator; 56 44 57 45 /** … … 60 48 * with them! 61 49 */ 62 public Rational( int numerator, intdenominator)63 { 64 this.numerator = numerator;65 this.denominator = denominator;50 public Rational(long numerator, long denominator) 51 { 52 _numerator = numerator; 53 _denominator = denominator; 66 54 } 67 55 … … 70 58 * This may involve rounding. 71 59 * 72 * @return 73 * 60 * @return the numeric value represented by this object after conversion 61 * to type <code>double</code>. 74 62 */ 75 63 public double doubleValue() 76 64 { 77 return (double) numerator / (double)denominator;65 return (double) _numerator / (double) _denominator; 78 66 } 79 67 … … 82 70 * This may involve rounding. 83 71 * 84 * @return 85 * 72 * @return the numeric value represented by this object after conversion 73 * to type <code>float</code>. 86 74 */ 87 75 public float floatValue() 88 76 { 89 return (float) numerator / (float)denominator;77 return (float) _numerator / (float) _denominator; 90 78 } 91 79 … … 95 83 * casts the result of <code>doubleValue()</code> to <code>byte</code>. 96 84 * 97 * @return 98 * 85 * @return the numeric value represented by this object after conversion 86 * to type <code>byte</code>. 99 87 */ 100 88 public final byte byteValue() 101 89 { 102 return (byte) doubleValue();90 return (byte) doubleValue(); 103 91 } 104 92 … … 108 96 * casts the result of <code>doubleValue()</code> to <code>int</code>. 109 97 * 110 * @return 111 * 98 * @return the numeric value represented by this object after conversion 99 * to type <code>int</code>. 112 100 */ 113 101 public final int intValue() 114 102 { 115 return (int) doubleValue();103 return (int) doubleValue(); 116 104 } 117 105 … … 121 109 * casts the result of <code>doubleValue()</code> to <code>long</code>. 122 110 * 123 * @return 124 * 111 * @return the numeric value represented by this object after conversion 112 * to type <code>long</code>. 125 113 */ 126 114 public final long longValue() 127 115 { 128 return (long) doubleValue();116 return (long) doubleValue(); 129 117 } 130 118 … … 134 122 * casts the result of <code>doubleValue()</code> to <code>short</code>. 135 123 * 136 * @return 137 * 124 * @return the numeric value represented by this object after conversion 125 * to type <code>short</code>. 138 126 */ 139 127 public final short shortValue() 140 128 { 141 return (short)doubleValue(); 142 } 143 144 145 /** 146 * Returns the denominator. 147 */ 148 public final int getDenominator() 149 { 150 return this.denominator; 151 } 152 153 /** 154 * Returns the numerator. 155 */ 156 public final int getNumerator() 157 { 158 return this.numerator; 159 } 160 161 /** 162 * Returns the reciprocal value of this obejct as a new Rational. 129 return (short) doubleValue(); 130 } 131 132 133 /** Returns the denominator. */ 134 public final long getDenominator() 135 { 136 return this._denominator; 137 } 138 139 /** Returns the numerator. */ 140 public final long getNumerator() 141 { 142 return this._numerator; 143 } 144 145 /** 146 * Returns the reciprocal value of this object as a new Rational. 147 * 163 148 * @return the reciprocal in a new object 164 149 */ 150 @NotNull 165 151 public Rational getReciprocal() 166 152 { 167 return new Rational(this.denominator, this.numerator); 168 } 169 170 /** 171 * Checks if this rational number is an Integer, either positive or negative. 172 */ 153 return new Rational(this._denominator, this._numerator); 154 } 155 156 /** Checks if this rational number is an Integer, either positive or negative. */ 173 157 public boolean isInteger() 174 158 { 175 if (denominator == 1 || 176 (denominator != 0 && (numerator % denominator == 0)) || 177 (denominator == 0 && numerator == 0) 178 ) { 179 return true; 180 } else { 181 return false; 182 } 159 return _denominator == 1 || 160 (_denominator != 0 && (_numerator % _denominator == 0)) || 161 (_denominator == 0 && _numerator == 0); 183 162 } 184 163 185 164 /** 186 165 * Returns a string representation of the object of form <code>numerator/denominator</code>. 187 * @return a string representation of the object. 188 */ 166 * 167 * @return a string representation of the object. 168 */ 169 @NotNull 189 170 public String toString() 190 171 { 191 return numerator + "/" + denominator; 192 } 193 194 /** 195 * Returns the simplest represenation of this Rational's value possible. 196 */ 172 return _numerator + "/" + _denominator; 173 } 174 175 /** Returns the simplest representation of this Rational's value possible. */ 176 @NotNull 197 177 public String toSimpleString(boolean allowDecimal) 198 178 { 199 if ( denominator == 0 &&numerator != 0) {179 if (_denominator == 0 && _numerator != 0) { 200 180 return toString(); 201 181 } else if (isInteger()) { 202 182 return Integer.toString(intValue()); 203 } else if ( numerator != 1 && denominator %numerator == 0) {183 } else if (_numerator != 1 && _denominator % _numerator == 0) { 204 184 // common factor between denominator and numerator 205 int newDenominator = denominator /numerator;185 long newDenominator = _denominator / _numerator; 206 186 return new Rational(1, newDenominator).toSimpleString(allowDecimal); 207 187 } else { … … 220 200 * Decides whether a brute-force simplification calculation should be avoided 221 201 * by comparing the maximum number of possible calculations with some threshold. 202 * 222 203 * @return true if the simplification should be performed, otherwise false 223 204 */ 224 205 private boolean tooComplexForSimplification() 225 206 { 226 double maxPossibleCalculations = (((double)(Math.min(denominator, numerator) - 1) / 5d) + 2); 207 double maxPossibleCalculations = (((double) (Math.min(_denominator, _numerator) - 1) / 5d) + 2); 208 final int maxSimplificationCalculations = 1000; 227 209 return maxPossibleCalculations > maxSimplificationCalculations; 228 210 } … … 231 213 * Compares two <code>Rational</code> instances, returning true if they are mathematically 232 214 * equivalent. 215 * 233 216 * @param obj the Rational to compare this instance to. 234 217 * @return true if instances are mathematically equivalent, otherwise false. Will also 235 218 * return false if <code>obj</code> is not an instance of <code>Rational</code>. 236 219 */ 237 public boolean equals(Object obj) 238 { 239 if (!(obj instanceof Rational)) { 220 @Override 221 public boolean equals(@Nullable Object obj) 222 { 223 if (obj==null || !(obj instanceof Rational)) 240 224 return false; 241 } 242 Rational that = (Rational)obj; 225 Rational that = (Rational) obj; 243 226 return this.doubleValue() == that.doubleValue(); 227 } 228 229 @Override 230 public int hashCode() 231 { 232 return (23 * (int)_denominator) + (int)_numerator; 244 233 } 245 234 … … 252 241 * To reduce a rational, need to see if both numerator and denominator are divisible 253 242 * by a common factor. Using the prime number series in ascending order guarantees 254 * the minimu nnumber of checks required.</p>243 * the minimum number of checks required.</p> 255 244 * <p> 256 245 * However, generating the prime number series seems to be a hefty task. Perhaps … … 265 254 * -- * ------------------------------------ + 2 266 255 * 10 2 267 * 256 * <p/> 268 257 * Math.min(denominator, numerator) - 1 269 258 * = ------------------------------------ + 2 270 259 * 5 271 260 * </pre></code> 272 * @return a simplified instance, or if the Rational could not be simpliffied, 261 * 262 * @return a simplified instance, or if the Rational could not be simplified, 273 263 * returns itself (unchanged) 274 264 */ 265 @NotNull 275 266 public Rational getSimplifiedInstance() 276 267 { … … 278 269 return this; 279 270 } 280 for (int factor = 2; factor <= Math.min( denominator,numerator); factor++) {271 for (int factor = 2; factor <= Math.min(_denominator, _numerator); factor++) { 281 272 if ((factor % 2 == 0 && factor > 2) || (factor % 5 == 0 && factor > 5)) { 282 273 continue; 283 274 } 284 if ( denominator % factor == 0 &&numerator % factor == 0) {275 if (_denominator % factor == 0 && _numerator % factor == 0) { 285 276 // found a common factor 286 return new Rational( numerator / factor,denominator / factor);277 return new Rational(_numerator / factor, _denominator / factor); 287 278 } 288 279 } -
trunk/src/com/drew/metadata/DefaultTagDescriptor.java
r4231 r6127 1 1 /* 2 * This is public domain software - that is, you can do whatever you want 3 * with it, and include it software that is licensed under the GNU or the 4 * BSD license, or whatever other licence you choose, including proprietary 5 * closed source licenses. I do ask that you leave this header in tact. 2 * Copyright 2002-2012 Drew Noakes 6 3 * 7 * If you make modifications to this code that you think would benefit the 8 * wider community, please send me a copy and I'll post it on my site. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 9 7 * 10 * If you make use of this code, I'd appreciate hearing about it. 11 * drew@drewnoakes.com 12 * Latest version of this software kept at 13 * http://drewnoakes.com/ 8 * http://www.apache.org/licenses/LICENSE-2.0 14 9 * 15 * Created by dnoakes on 22-Nov-2002 16:45:19 using IntelliJ IDEA. 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 16 20 */ 17 21 package com.drew.metadata; 18 22 23 import com.drew.lang.annotations.NotNull; 24 19 25 /** 26 * A default implementation of the abstract TagDescriptor. As this class is not coded with awareness of any metadata 27 * tags, it simply reports tag names using the format 'Unknown tag 0x00' (with the corresponding tag number in hex) 28 * and gives descriptions using the default string representation of the value. 20 29 * 30 * @author Drew Noakes http://drewnoakes.com 21 31 */ 22 public class DefaultTagDescriptor extends TagDescriptor 32 public class DefaultTagDescriptor extends TagDescriptor<Directory> 23 33 { 24 public DefaultTagDescriptor( Directory directory)34 public DefaultTagDescriptor(@NotNull Directory directory) 25 35 { 26 36 super(directory); 27 37 } 28 38 39 /** 40 * Gets a best-effort tag name using the format 'Unknown tag 0x00' (with the corresponding tag type in hex). 41 * @param tagType the tag type identifier. 42 * @return a string representation of the tag name. 43 */ 44 @NotNull 29 45 public String getTagName(int tagType) 30 46 { … … 33 49 return "Unknown tag 0x" + hex; 34 50 } 35 36 public String getDescription(int tagType)37 {38 return _directory.getString(tagType);39 }40 51 } -
trunk/src/com/drew/metadata/Directory.java
r4231 r6127 1 1 /* 2 * This is public domain software - that is, you can do whatever you want 3 * with it, and include it software that is licensed under the GNU or the 4 * BSD license, or whatever other licence you choose, including proprietary 5 * closed source licenses. I do ask that you leave this header in tact. 2 * Copyright 2002-2012 Drew Noakes 6 3 * 7 * If you make modifications to this code that you think would benefit the 8 * wider community, please send me a copy and I'll post it on my site. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 9 7 * 10 * If you make use of this code, I'd appreciate hearing about it. 11 * drew@drewnoakes.com 12 * Latest version of this software kept at 13 * http://drewnoakes.com/ 8 * http://www.apache.org/licenses/LICENSE-2.0 14 9 * 15 * Created by dnoakes on 25-Nov-2002 20:30:39 using IntelliJ IDEA. 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 16 20 */ 17 21 package com.drew.metadata; 18 22 19 23 import com.drew.lang.Rational; 20 21 import java.io.Serializable; 24 import com.drew.lang.annotations.NotNull; 25 import com.drew.lang.annotations.Nullable; 26 import com.drew.lang.annotations.SuppressWarnings; 27 28 import java.io.UnsupportedEncodingException; 22 29 import java.lang.reflect.Array; 23 30 import java.text.DateFormat; 24 import java.util.ArrayList; 25 import java.util.HashMap; 26 import java.util.Iterator; 27 import java.util.List; 31 import java.text.ParseException; 32 import java.text.SimpleDateFormat; 33 import java.util.*; 28 34 29 35 /** 30 * Base class for all Metadata directory types with supporting methods for setting and 31 * getting tag values. 36 * Abstract base class for all directory implementations, having methods for getting and setting tag values of various 37 * data types. 38 * 39 * @author Drew Noakes http://drewnoakes.com 32 40 */ 33 public abstract class Directory implements Serializable41 public abstract class Directory 34 42 { 35 /** 36 * Map of values hashed by type identifiers. 37 */ 38 protected final HashMap _tagMap; 39 40 /** 41 * The descriptor used to interperet tag values. 42 */ 43 protected TagDescriptor _descriptor; 43 // TODO get Array methods need to return cloned data, to maintain this directory's integrity 44 45 /** Map of values hashed by type identifiers. */ 46 @NotNull 47 protected final Map<Integer, Object> _tagMap = new HashMap<Integer, Object>(); 44 48 45 49 /** … … 48 52 * defined tags. 49 53 */ 50 protected final List _definedTagList; 51 52 private List _errorList; 54 @NotNull 55 protected final Collection<Tag> _definedTagList = new ArrayList<Tag>(); 56 57 @NotNull 58 private final Collection<String> _errorList = new ArrayList<String>(4); 59 60 /** The descriptor used to interpret tag values. */ 61 protected TagDescriptor _descriptor; 53 62 54 63 // ABSTRACT METHODS … … 56 65 /** 57 66 * Provides the name of the directory, for display purposes. E.g. <code>Exif</code> 67 * 58 68 * @return the name of the directory 59 69 */ 70 @NotNull 60 71 public abstract String getName(); 61 72 62 73 /** 63 74 * Provides the map of tag names, hashed by tag type identifier. 75 * 64 76 * @return the map of tag names 65 77 */ 66 protected abstract HashMap getTagNameMap(); 67 68 // CONSTRUCTORS 69 70 /** 71 * Creates a new Directory. 72 */ 73 public Directory() 74 { 75 _tagMap = new HashMap(); 76 _definedTagList = new ArrayList(); 77 } 78 @NotNull 79 protected abstract HashMap<Integer, String> getTagNameMap(); 80 81 protected Directory() 82 {} 78 83 79 84 // VARIOUS METHODS … … 81 86 /** 82 87 * Indicates whether the specified tag type has been set. 88 * 83 89 * @param tagType the tag type to check for 84 90 * @return true if a value exists for the specified tag type, false if not 85 91 */ 92 @java.lang.SuppressWarnings({ "UnnecessaryBoxing" }) 86 93 public boolean containsTag(int tagType) 87 94 { 88 return _tagMap.containsKey( new Integer(tagType));95 return _tagMap.containsKey(Integer.valueOf(tagType)); 89 96 } 90 97 91 98 /** 92 99 * Returns an Iterator of Tag instances that have been set in this Directory. 100 * 93 101 * @return an Iterator of Tag instances 94 102 */ 95 public Iterator getTagIterator() 96 { 97 return _definedTagList.iterator(); 103 @NotNull 104 public Collection<Tag> getTags() 105 { 106 return _definedTagList; 98 107 } 99 108 100 109 /** 101 110 * Returns the number of tags set in this Directory. 111 * 102 112 * @return the number of tags set in this Directory 103 113 */ … … 108 118 109 119 /** 110 * Sets the descriptor used to interperet tag values. 111 * @param descriptor the descriptor used to interperet tag values 112 */ 113 public void setDescriptor(TagDescriptor descriptor) 114 { 115 if (descriptor==null) { 120 * Sets the descriptor used to interpret tag values. 121 * 122 * @param descriptor the descriptor used to interpret tag values 123 */ 124 @java.lang.SuppressWarnings({ "ConstantConditions" }) 125 public void setDescriptor(@NotNull TagDescriptor descriptor) 126 { 127 if (descriptor == null) 116 128 throw new NullPointerException("cannot set a null descriptor"); 117 }118 129 _descriptor = descriptor; 119 130 } 120 131 121 public void addError(String message) 122 { 123 if (_errorList==null) { 124 _errorList = new ArrayList(); 125 } 132 /** 133 * Registers an error message with this directory. 134 * 135 * @param message an error message. 136 */ 137 public void addError(@NotNull String message) 138 { 126 139 _errorList.add(message); 127 140 } 128 141 142 /** 143 * Gets a value indicating whether this directory has any error messages. 144 * 145 * @return true if the directory contains errors, otherwise false 146 */ 129 147 public boolean hasErrors() 130 148 { 131 return (_errorList!=null && _errorList.size()>0); 132 } 133 134 public Iterator getErrors() 135 { 136 return _errorList.iterator(); 137 } 138 149 return _errorList.size() > 0; 150 } 151 152 /** 153 * Used to iterate over any error messages contained in this directory. 154 * 155 * @return an iterable collection of error message strings. 156 */ 157 @NotNull 158 public Iterable<String> getErrors() 159 { 160 return _errorList; 161 } 162 163 /** Returns the count of error messages in this directory. */ 139 164 public int getErrorCount() 140 165 { … … 145 170 146 171 /** 147 * Sets an int value for the specified tag. 172 * Sets an <code>int</code> value for the specified tag. 173 * 148 174 * @param tagType the tag's value as an int 149 * @param value the value for the specified tag as an int175 * @param value the value for the specified tag as an int 150 176 */ 151 177 public void setInt(int tagType, int value) 152 178 { 153 setObject(tagType, new Integer(value)); 154 } 155 156 /** 157 * Sets a double value for the specified tag. 179 setObject(tagType, value); 180 } 181 182 /** 183 * Sets an <code>int[]</code> (array) for the specified tag. 184 * 185 * @param tagType the tag identifier 186 * @param ints the int array to store 187 */ 188 public void setIntArray(int tagType, @NotNull int[] ints) 189 { 190 setObjectArray(tagType, ints); 191 } 192 193 /** 194 * Sets a <code>float</code> value for the specified tag. 195 * 158 196 * @param tagType the tag's value as an int 159 * @param value the value for the specified tag as a double 197 * @param value the value for the specified tag as a float 198 */ 199 public void setFloat(int tagType, float value) 200 { 201 setObject(tagType, value); 202 } 203 204 /** 205 * Sets a <code>float[]</code> (array) for the specified tag. 206 * 207 * @param tagType the tag identifier 208 * @param floats the float array to store 209 */ 210 public void setFloatArray(int tagType, @NotNull float[] floats) 211 { 212 setObjectArray(tagType, floats); 213 } 214 215 /** 216 * Sets a <code>double</code> value for the specified tag. 217 * 218 * @param tagType the tag's value as an int 219 * @param value the value for the specified tag as a double 160 220 */ 161 221 public void setDouble(int tagType, double value) 162 222 { 163 setObject(tagType, new Double(value)); 164 } 165 166 /** 167 * Sets a float value for the specified tag. 223 setObject(tagType, value); 224 } 225 226 /** 227 * Sets a <code>double[]</code> (array) for the specified tag. 228 * 229 * @param tagType the tag identifier 230 * @param doubles the double array to store 231 */ 232 public void setDoubleArray(int tagType, @NotNull double[] doubles) 233 { 234 setObjectArray(tagType, doubles); 235 } 236 237 /** 238 * Sets a <code>String</code> value for the specified tag. 239 * 168 240 * @param tagType the tag's value as an int 169 * @param value the value for the specified tag as a float 170 */ 171 public void setFloat(int tagType, float value) 172 { 173 setObject(tagType, new Float(value)); 174 } 175 176 /** 177 * Sets an int value for the specified tag. 178 * @param tagType the tag's value as an int 179 * @param value the value for the specified tag as a String 180 */ 181 public void setString(int tagType, String value) 182 { 241 * @param value the value for the specified tag as a String 242 */ 243 @java.lang.SuppressWarnings({ "ConstantConditions" }) 244 public void setString(int tagType, @NotNull String value) 245 { 246 if (value == null) 247 throw new NullPointerException("cannot set a null String"); 183 248 setObject(tagType, value); 184 249 } 185 250 186 251 /** 187 * Sets an int value for the specified tag. 188 * @param tagType the tag's value as an int 189 * @param value the value for the specified tag as a boolean 190 */ 191 public void setBoolean(int tagType, boolean value) 192 { 193 setObject(tagType, new Boolean(value)); 194 } 195 196 /** 197 * Sets a long value for the specified tag. 198 * @param tagType the tag's value as an int 199 * @param value the value for the specified tag as a long 200 */ 201 public void setLong(int tagType, long value) 202 { 203 setObject(tagType, new Long(value)); 204 } 205 206 /** 207 * Sets a java.util.Date value for the specified tag. 208 * @param tagType the tag's value as an int 209 * @param value the value for the specified tag as a java.util.Date 210 */ 211 public void setDate(int tagType, java.util.Date value) 212 { 213 setObject(tagType, value); 214 } 215 216 /** 217 * Sets a Rational value for the specified tag. 218 * @param tagType the tag's value as an int 219 * @param rational rational number 220 */ 221 public void setRational(int tagType, Rational rational) 222 { 223 setObject(tagType, rational); 224 } 225 226 /** 227 * Sets a Rational array for the specified tag. 228 * @param tagType the tag identifier 229 * @param rationals the Rational array to store 230 */ 231 public void setRationalArray(int tagType, Rational[] rationals) 232 { 233 setObjectArray(tagType, rationals); 234 } 235 236 /** 237 * Sets an int array for the specified tag. 238 * @param tagType the tag identifier 239 * @param ints the int array to store 240 */ 241 public void setIntArray(int tagType, int[] ints) 242 { 243 setObjectArray(tagType, ints); 244 } 245 246 /** 247 * Sets a byte array for the specified tag. 248 * @param tagType the tag identifier 249 * @param bytes the byte array to store 250 */ 251 public void setByteArray(int tagType, byte[] bytes) 252 { 253 setObjectArray(tagType, bytes); 254 } 255 256 /** 257 * Sets a String array for the specified tag. 252 * Sets a <code>String[]</code> (array) for the specified tag. 253 * 258 254 * @param tagType the tag identifier 259 255 * @param strings the String array to store 260 256 */ 261 public void setStringArray(int tagType, String[] strings)257 public void setStringArray(int tagType, @NotNull String[] strings) 262 258 { 263 259 setObjectArray(tagType, strings); … … 265 261 266 262 /** 267 * Private helper method, containing common functionality for all 'add'268 * methods.263 * Sets a <code>boolean</code> value for the specified tag. 264 * 269 265 * @param tagType the tag's value as an int 270 * @param value the value for the specified tag 266 * @param value the value for the specified tag as a boolean 267 */ 268 public void setBoolean(int tagType, boolean value) 269 { 270 setObject(tagType, value); 271 } 272 273 /** 274 * Sets a <code>long</code> value for the specified tag. 275 * 276 * @param tagType the tag's value as an int 277 * @param value the value for the specified tag as a long 278 */ 279 public void setLong(int tagType, long value) 280 { 281 setObject(tagType, value); 282 } 283 284 /** 285 * Sets a <code>java.util.Date</code> value for the specified tag. 286 * 287 * @param tagType the tag's value as an int 288 * @param value the value for the specified tag as a java.util.Date 289 */ 290 public void setDate(int tagType, @NotNull java.util.Date value) 291 { 292 setObject(tagType, value); 293 } 294 295 /** 296 * Sets a <code>Rational</code> value for the specified tag. 297 * 298 * @param tagType the tag's value as an int 299 * @param rational rational number 300 */ 301 public void setRational(int tagType, @NotNull Rational rational) 302 { 303 setObject(tagType, rational); 304 } 305 306 /** 307 * Sets a <code>Rational[]</code> (array) for the specified tag. 308 * 309 * @param tagType the tag identifier 310 * @param rationals the Rational array to store 311 */ 312 public void setRationalArray(int tagType, @NotNull Rational[] rationals) 313 { 314 setObjectArray(tagType, rationals); 315 } 316 317 /** 318 * Sets a <code>byte[]</code> (array) for the specified tag. 319 * 320 * @param tagType the tag identifier 321 * @param bytes the byte array to store 322 */ 323 public void setByteArray(int tagType, @NotNull byte[] bytes) 324 { 325 setObjectArray(tagType, bytes); 326 } 327 328 /** 329 * Sets a <code>Object</code> for the specified tag. 330 * 331 * @param tagType the tag's value as an int 332 * @param value the value for the specified tag 271 333 * @throws NullPointerException if value is <code>null</code> 272 334 */ 273 public void setObject(int tagType, Object value) 274 { 275 if (value==null) { 335 @java.lang.SuppressWarnings( { "ConstantConditions", "UnnecessaryBoxing" }) 336 public void setObject(int tagType, @NotNull Object value) 337 { 338 if (value == null) 276 339 throw new NullPointerException("cannot set a null object"); 277 } 278 279 Integer key = new Integer(tagType); 280 if (!_tagMap.containsKey(key)) { 340 341 if (!_tagMap.containsKey(Integer.valueOf(tagType))) { 281 342 _definedTagList.add(new Tag(tagType, this)); 282 343 } 283 _tagMap.put(key, value); 284 } 285 286 /** 287 * Private helper method, containing common functionality for all 'add...Array' 288 * methods. 344 // else { 345 // final Object oldValue = _tagMap.get(tagType); 346 // if (!oldValue.equals(value)) 347 // addError(String.format("Overwritten tag 0x%s (%s). Old=%s, New=%s", Integer.toHexString(tagType), getTagName(tagType), oldValue, value)); 348 // } 349 _tagMap.put(tagType, value); 350 } 351 352 /** 353 * Sets an array <code>Object</code> for the specified tag. 354 * 289 355 * @param tagType the tag's value as an int 290 * @param array the array of values for the specified tag291 */ 292 public void setObjectArray(int tagType, Object array)356 * @param array the array of values for the specified tag 357 */ 358 public void setObjectArray(int tagType, @NotNull Object array) 293 359 { 294 360 // for now, we don't do anything special -- this method might be a candidate for removal once the dust settles … … 299 365 300 366 /** 301 * Returns the specified tag's value as an int, if possible. 367 * Returns the specified tag's value as an int, if possible. Every attempt to represent the tag's value as an int 368 * is taken. Here is a list of the action taken depending upon the tag's original type: 369 * <ul> 370 * <li> int - Return unchanged. 371 * <li> Number - Return an int value (real numbers are truncated). 372 * <li> Rational - Truncate any fractional part and returns remaining int. 373 * <li> String - Attempt to parse string as an int. If this fails, convert the char[] to an int (using shifts and OR). 374 * <li> Rational[] - Return int value of first item in array. 375 * <li> byte[] - Return int value of first item in array. 376 * <li> int[] - Return int value of first item in array. 377 * </ul> 378 * 379 * @throws MetadataException if no value exists for tagType or if it cannot be converted to an int. 302 380 */ 303 381 public int getInt(int tagType) throws MetadataException 304 382 { 305 Object o = getObject(tagType); 306 if (o==null) { 307 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first"); 308 } else if (o instanceof String) { 383 Integer integer = getInteger(tagType); 384 if (integer!=null) 385 return integer; 386 387 Object o = getObject(tagType); 388 if (o == null) 389 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first"); 390 throw new MetadataException("Tag '" + tagType + "' cannot be converted to int. It is of type '" + o.getClass() + "'."); 391 } 392 393 /** 394 * Returns the specified tag's value as an Integer, if possible. Every attempt to represent the tag's value as an 395 * Integer is taken. Here is a list of the action taken depending upon the tag's original type: 396 * <ul> 397 * <li> int - Return unchanged 398 * <li> Number - Return an int value (real numbers are truncated) 399 * <li> Rational - Truncate any fractional part and returns remaining int 400 * <li> String - Attempt to parse string as an int. If this fails, convert the char[] to an int (using shifts and OR) 401 * <li> Rational[] - Return int value of first item in array if length > 0 402 * <li> byte[] - Return int value of first item in array if length > 0 403 * <li> int[] - Return int value of first item in array if length > 0 404 * </ul> 405 * 406 * If the value is not found or cannot be converted to int, <code>null</code> is returned. 407 */ 408 @Nullable 409 public Integer getInteger(int tagType) 410 { 411 Object o = getObject(tagType); 412 413 if (o == null) 414 return null; 415 416 if (o instanceof String) { 309 417 try { 310 418 return Integer.parseInt((String)o); … … 314 422 byte[] bytes = s.getBytes(); 315 423 long val = 0; 316 for ( int i = 0; i < bytes.length; i++) {424 for (byte aByte : bytes) { 317 425 val = val << 8; 318 val += bytes[i];426 val += (aByte & 0xff); 319 427 } 320 428 return (int)val; … … 324 432 } else if (o instanceof Rational[]) { 325 433 Rational[] rationals = (Rational[])o; 326 if (rationals.length ==1)434 if (rationals.length == 1) 327 435 return rationals[0].intValue(); 328 436 } else if (o instanceof byte[]) { 329 437 byte[] bytes = (byte[])o; 330 if (bytes.length ==1)331 return bytes[0];438 if (bytes.length == 1) 439 return (int)bytes[0]; 332 440 } else if (o instanceof int[]) { 333 441 int[] ints = (int[])o; 334 if (ints.length ==1)442 if (ints.length == 1) 335 443 return ints[0]; 336 444 } 337 throw new MetadataException("Tag '" + tagType + "' cannot be cast to int. It is of type '" + o.getClass() + "'."); 338 } 339 340 // TODO get Array methods need to return cloned data, to maintain this directory's integrity 445 return null; 446 } 341 447 342 448 /** 343 449 * Gets the specified tag's value as a String array, if possible. Only supported 344 450 * where the tag is set as String[], String, int[], byte[] or Rational[]. 451 * 345 452 * @param tagType the tag identifier 346 * @return the tag's value as an array of Strings 347 * @throws MetadataException if the tag has not been set or cannot be represented 348 * as a String[] 349 */ 350 public String[] getStringArray(int tagType) throws MetadataException 351 { 352 Object o = getObject(tagType); 353 if (o==null) { 354 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first"); 355 } else if (o instanceof String[]) { 453 * @return the tag's value as an array of Strings. If the value is unset or cannot be converted, <code>null</code> is returned. 454 */ 455 @Nullable 456 public String[] getStringArray(int tagType) 457 { 458 Object o = getObject(tagType); 459 if (o == null) 460 return null; 461 if (o instanceof String[]) 356 462 return (String[])o; 357 } else if (o instanceof String) { 358 String[] strings = {(String)o}; 359 return strings; 360 } else if (o instanceof int[]) { 463 if (o instanceof String) 464 return new String[] { (String)o }; 465 if (o instanceof int[]) { 361 466 int[] ints = (int[])o; 362 467 String[] strings = new String[ints.length]; 363 for (int i = 0; i <strings.length; i++) {468 for (int i = 0; i < strings.length; i++) 364 469 strings[i] = Integer.toString(ints[i]); 365 }366 470 return strings; 367 471 } else if (o instanceof byte[]) { 368 472 byte[] bytes = (byte[])o; 369 473 String[] strings = new String[bytes.length]; 370 for (int i = 0; i <strings.length; i++) {474 for (int i = 0; i < strings.length; i++) 371 475 strings[i] = Byte.toString(bytes[i]); 372 }373 476 return strings; 374 477 } else if (o instanceof Rational[]) { 375 478 Rational[] rationals = (Rational[])o; 376 479 String[] strings = new String[rationals.length]; 377 for (int i = 0; i <strings.length; i++) {480 for (int i = 0; i < strings.length; i++) 378 481 strings[i] = rationals[i].toSimpleString(false); 379 }380 482 return strings; 381 483 } 382 throw new MetadataException("Tag '" + tagType + "' cannot be cast to an String array. It is of type '" + o.getClass() + "'.");484 return null; 383 485 } 384 486 385 487 /** 386 488 * Gets the specified tag's value as an int array, if possible. Only supported 387 * where the tag is set as String, int[], byte[] or Rational[]. 489 * where the tag is set as String, Integer, int[], byte[] or Rational[]. 490 * 388 491 * @param tagType the tag identifier 389 492 * @return the tag's value as an int array 390 * @throws MetadataException if the tag has not been set, or cannot be converted to 391 * an int array 392 */ 393 public int[] getIntArray(int tagType) throws MetadataException 394 { 395 Object o = getObject(tagType); 396 if (o==null) { 397 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first"); 398 } else if (o instanceof Rational[]) { 493 */ 494 @Nullable 495 public int[] getIntArray(int tagType) 496 { 497 Object o = getObject(tagType); 498 if (o == null) 499 return null; 500 if (o instanceof Rational[]) { 399 501 Rational[] rationals = (Rational[])o; 400 502 int[] ints = new int[rationals.length]; 401 for (int i = 0; i <ints.length; i++) {503 for (int i = 0; i < ints.length; i++) { 402 504 ints[i] = rationals[i].intValue(); 403 505 } 404 506 return ints; 405 } else if (o instanceof int[]) { 507 } 508 if (o instanceof int[]) 406 509 return (int[])o; 407 } elseif (o instanceof byte[]) {510 if (o instanceof byte[]) { 408 511 byte[] bytes = (byte[])o; 409 512 int[] ints = new int[bytes.length]; 410 for (int i = 0; i <bytes.length; i++) {513 for (int i = 0; i < bytes.length; i++) { 411 514 byte b = bytes[i]; 412 515 ints[i] = b; 413 516 } 414 517 return ints; 415 } else if (o instanceof String) { 416 String str = (String)o; 518 } 519 if (o instanceof CharSequence) { 520 CharSequence str = (CharSequence)o; 417 521 int[] ints = new int[str.length()]; 418 for (int i = 0; i <str.length(); i++) {522 for (int i = 0; i < str.length(); i++) { 419 523 ints[i] = str.charAt(i); 420 524 } 421 525 return ints; 422 526 } 423 throw new MetadataException("Tag '" + tagType + "' cannot be cast to an int array. It is of type '" + o.getClass() + "'."); 527 if (o instanceof Integer) 528 return new int[] { (Integer)o }; 529 530 return null; 424 531 } 425 532 426 533 /** 427 534 * Gets the specified tag's value as an byte array, if possible. Only supported 428 * where the tag is set as String, int[], byte[] or Rational[]. 535 * where the tag is set as String, Integer, int[], byte[] or Rational[]. 536 * 429 537 * @param tagType the tag identifier 430 538 * @return the tag's value as a byte array 431 * @throws MetadataException if the tag has not been set, or cannot be converted to 432 * a byte array 433 */ 434 public byte[] getByteArray(int tagType) throws MetadataException 435 { 436 Object o = getObject(tagType); 437 if (o==null) { 438 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first"); 539 */ 540 @Nullable 541 public byte[] getByteArray(int tagType) 542 { 543 Object o = getObject(tagType); 544 if (o == null) { 545 return null; 439 546 } else if (o instanceof Rational[]) { 440 547 Rational[] rationals = (Rational[])o; 441 548 byte[] bytes = new byte[rationals.length]; 442 for (int i = 0; i <bytes.length; i++) {549 for (int i = 0; i < bytes.length; i++) { 443 550 bytes[i] = rationals[i].byteValue(); 444 551 } … … 449 556 int[] ints = (int[])o; 450 557 byte[] bytes = new byte[ints.length]; 451 for (int i = 0; i <ints.length; i++) {558 for (int i = 0; i < ints.length; i++) { 452 559 bytes[i] = (byte)ints[i]; 453 560 } 454 561 return bytes; 455 } else if (o instanceof String) {456 String str = (String)o;562 } else if (o instanceof CharSequence) { 563 CharSequence str = (CharSequence)o; 457 564 byte[] bytes = new byte[str.length()]; 458 for (int i = 0; i <str.length(); i++) {565 for (int i = 0; i < str.length(); i++) { 459 566 bytes[i] = (byte)str.charAt(i); 460 567 } 461 568 return bytes; 462 569 } 463 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a byte array. It is of type '" + o.getClass() + "'."); 464 } 465 466 /** 467 * Returns the specified tag's value as a double, if possible. 468 */ 570 if (o instanceof Integer) 571 return new byte[] { ((Integer)o).byteValue() }; 572 573 return null; 574 } 575 576 /** Returns the specified tag's value as a double, if possible. */ 469 577 public double getDouble(int tagType) throws MetadataException 470 578 { 471 Object o = getObject(tagType); 472 if (o==null) { 473 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first"); 474 } else if (o instanceof String) { 579 Double value = getDoubleObject(tagType); 580 if (value!=null) 581 return value; 582 Object o = getObject(tagType); 583 if (o == null) 584 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first"); 585 throw new MetadataException("Tag '" + tagType + "' cannot be converted to a double. It is of type '" + o.getClass() + "'."); 586 } 587 /** Returns the specified tag's value as a Double. If the tag is not set or cannot be converted, <code>null</code> is returned. */ 588 @Nullable 589 public Double getDoubleObject(int tagType) 590 { 591 Object o = getObject(tagType); 592 if (o == null) 593 return null; 594 if (o instanceof String) { 475 595 try { 476 596 return Double.parseDouble((String)o); 477 597 } catch (NumberFormatException nfe) { 478 throw new MetadataException("unable to parse string " + o + " as a double", nfe); 479 } 480 } else if (o instanceof Number) { 598 return null; 599 } 600 } 601 if (o instanceof Number) 481 602 return ((Number)o).doubleValue(); 482 } 483 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a double. It is of type '" + o.getClass() + "'."); 484 } 485 486 /** 487 * Returns the specified tag's value as a float, if possible. 488 */ 603 604 return null; 605 } 606 607 /** Returns the specified tag's value as a float, if possible. */ 489 608 public float getFloat(int tagType) throws MetadataException 490 609 { 491 Object o = getObject(tagType); 492 if (o==null) { 493 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first"); 494 } else if (o instanceof String) { 610 Float value = getFloatObject(tagType); 611 if (value!=null) 612 return value; 613 Object o = getObject(tagType); 614 if (o == null) 615 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first"); 616 throw new MetadataException("Tag '" + tagType + "' cannot be converted to a float. It is of type '" + o.getClass() + "'."); 617 } 618 619 /** Returns the specified tag's value as a float. If the tag is not set or cannot be converted, <code>null</code> is returned. */ 620 @Nullable 621 public Float getFloatObject(int tagType) 622 { 623 Object o = getObject(tagType); 624 if (o == null) 625 return null; 626 if (o instanceof String) { 495 627 try { 496 628 return Float.parseFloat((String)o); 497 629 } catch (NumberFormatException nfe) { 498 throw new MetadataException("unable to parse string " + o + " as a float", nfe); 499 } 500 } else if (o instanceof Number) { 630 return null; 631 } 632 } 633 if (o instanceof Number) 501 634 return ((Number)o).floatValue(); 502 } 503 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a float. It is of type '" + o.getClass() + "'."); 504 } 505 506 /** 507 * Returns the specified tag's value as a long, if possible. 508 */ 635 return null; 636 } 637 638 /** Returns the specified tag's value as a long, if possible. */ 509 639 public long getLong(int tagType) throws MetadataException 510 640 { 511 Object o = getObject(tagType); 512 if (o==null) { 513 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first"); 514 } else if (o instanceof String) { 641 Long value = getLongObject(tagType); 642 if (value!=null) 643 return value; 644 Object o = getObject(tagType); 645 if (o == null) 646 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first"); 647 throw new MetadataException("Tag '" + tagType + "' cannot be converted to a long. It is of type '" + o.getClass() + "'."); 648 } 649 650 /** Returns the specified tag's value as a long. If the tag is not set or cannot be converted, <code>null</code> is returned. */ 651 @Nullable 652 public Long getLongObject(int tagType) 653 { 654 Object o = getObject(tagType); 655 if (o == null) 656 return null; 657 if (o instanceof String) { 515 658 try { 516 659 return Long.parseLong((String)o); 517 660 } catch (NumberFormatException nfe) { 518 throw new MetadataException("unable to parse string " + o + " as a long", nfe); 519 } 520 } else if (o instanceof Number) { 661 return null; 662 } 663 } 664 if (o instanceof Number) 521 665 return ((Number)o).longValue(); 522 } 523 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a long. It is of type '" + o.getClass() + "'."); 524 } 525 526 /** 527 * Returns the specified tag's value as a boolean, if possible. 528 */ 666 return null; 667 } 668 669 /** Returns the specified tag's value as a boolean, if possible. */ 529 670 public boolean getBoolean(int tagType) throws MetadataException 530 671 { 531 Object o = getObject(tagType); 532 if (o==null) { 533 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first"); 534 } else if (o instanceof Boolean) { 535 return ((Boolean)o).booleanValue(); 536 } else if (o instanceof String) { 672 Boolean value = getBooleanObject(tagType); 673 if (value!=null) 674 return value; 675 Object o = getObject(tagType); 676 if (o == null) 677 throw new MetadataException("Tag '" + getTagName(tagType) + "' has not been set -- check using containsTag() first"); 678 throw new MetadataException("Tag '" + tagType + "' cannot be converted to a boolean. It is of type '" + o.getClass() + "'."); 679 } 680 681 /** Returns the specified tag's value as a boolean. If the tag is not set or cannot be converted, <code>null</code> is returned. */ 682 @Nullable 683 @SuppressWarnings(value = "NP_BOOLEAN_RETURN_NULL", justification = "keep API interface consistent") 684 public Boolean getBooleanObject(int tagType) 685 { 686 Object o = getObject(tagType); 687 if (o == null) 688 return null; 689 if (o instanceof Boolean) 690 return (Boolean)o; 691 if (o instanceof String) { 537 692 try { 538 693 return Boolean.getBoolean((String)o); 539 694 } catch (NumberFormatException nfe) { 540 throw new MetadataException("unable to parse string " + o + " as a boolean", nfe); 541 } 542 } else if (o instanceof Number) { 543 return (((Number)o).doubleValue()!=0); 544 } 545 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a boolean. It is of type '" + o.getClass() + "'."); 546 } 547 548 /** 549 * Returns the specified tag's value as a java.util.Date, if possible. 550 */ 551 public java.util.Date getDate(int tagType) throws MetadataException 552 { 553 Object o = getObject(tagType); 554 if (o==null) { 555 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first"); 556 } else if (o instanceof java.util.Date) { 695 return null; 696 } 697 } 698 if (o instanceof Number) 699 return (((Number)o).doubleValue() != 0); 700 return null; 701 } 702 703 /** 704 * Returns the specified tag's value as a java.util.Date. If the value is unset or cannot be converted, <code>null</code> is returned. 705 * <p/> 706 * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in 707 * the current {@link TimeZone}. If the {@link TimeZone} is known, call the overload that accepts one as an argument. 708 */ 709 @Nullable 710 public java.util.Date getDate(int tagType) 711 { 712 return getDate(tagType, null); 713 } 714 715 /** 716 * Returns the specified tag's value as a java.util.Date. If the value is unset or cannot be converted, <code>null</code> is returned. 717 * <p/> 718 * If the underlying value is a {@link String}, then attempts will be made to parse the string as though it is in 719 * the {@link TimeZone} represented by the {@code timeZone} parameter (if it is non-null). Note that this parameter 720 * is only considered if the underlying value is a string and parsing occurs, otherwise it has no effect. 721 */ 722 @Nullable 723 public java.util.Date getDate(int tagType, @Nullable TimeZone timeZone) 724 { 725 Object o = getObject(tagType); 726 727 if (o == null) 728 return null; 729 730 if (o instanceof java.util.Date) 557 731 return (java.util.Date)o; 558 } else if (o instanceof String) { 559 // add new dateformat strings to make this method even smarter560 // so far, this seems to cover all knowndate strings561 // (for example, AM and PM strings are not supported...)732 733 if (o instanceof String) { 734 // This seems to cover all known Exif date strings 735 // Note that " : : : : " is a valid date string according to the Exif spec (which means 'unknown date'): http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/datetimeoriginal.html 562 736 String datePatterns[] = { 563 "yyyy:MM:dd HH:mm:ss", 564 "yyyy:MM:dd HH:mm", 565 "yyyy-MM-dd HH:mm:ss", 566 "yyyy-MM-dd HH:mm"}; 737 "yyyy:MM:dd HH:mm:ss", 738 "yyyy:MM:dd HH:mm", 739 "yyyy-MM-dd HH:mm:ss", 740 "yyyy-MM-dd HH:mm", 741 "yyyy.MM.dd HH:mm:ss", 742 "yyyy.MM.dd HH:mm" }; 567 743 String dateString = (String)o; 568 for ( int i = 0; i<datePatterns.length; i++) {744 for (String datePattern : datePatterns) { 569 745 try { 570 DateFormat parser = new java.text.SimpleDateFormat(datePatterns[i]); 746 DateFormat parser = new SimpleDateFormat(datePattern); 747 if (timeZone != null) 748 parser.setTimeZone(timeZone); 571 749 return parser.parse(dateString); 572 } catch ( java.text.ParseException ex) {750 } catch (ParseException ex) { 573 751 // simply try the next pattern 574 752 } 575 753 } 576 754 } 577 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a java.util.Date. It is of type '" + o.getClass() + "'."); 578 } 579 580 /** 581 * Returns the specified tag's value as a Rational, if possible. 582 */ 583 public Rational getRational(int tagType) throws MetadataException 584 { 585 Object o = getObject(tagType); 586 if (o==null) { 587 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first"); 588 } else if (o instanceof Rational) { 755 return null; 756 } 757 758 /** Returns the specified tag's value as a Rational. If the value is unset or cannot be converted, <code>null</code> is returned. */ 759 @Nullable 760 public Rational getRational(int tagType) 761 { 762 Object o = getObject(tagType); 763 764 if (o == null) 765 return null; 766 767 if (o instanceof Rational) 589 768 return (Rational)o; 590 } 591 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a Rational. It is of type '" + o.getClass() + "'."); 592 } 593 594 public Rational[] getRationalArray(int tagType) throws MetadataException 595 { 596 Object o = getObject(tagType); 597 if (o==null) { 598 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first"); 599 } else if (o instanceof Rational[]) { 769 if (o instanceof Integer) 770 return new Rational((Integer)o, 1); 771 if (o instanceof Long) 772 return new Rational((Long)o, 1); 773 774 // NOTE not doing conversions for real number types 775 776 return null; 777 } 778 779 /** Returns the specified tag's value as an array of Rational. If the value is unset or cannot be converted, <code>null</code> is returned. */ 780 @Nullable 781 public Rational[] getRationalArray(int tagType) 782 { 783 Object o = getObject(tagType); 784 if (o == null) 785 return null; 786 787 if (o instanceof Rational[]) 600 788 return (Rational[])o; 601 } 602 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a Rational array. It is of type '" + o.getClass() + "'.");789 790 return null; 603 791 } 604 792 … … 606 794 * Returns the specified tag's value as a String. This value is the 'raw' value. A more presentable decoding 607 795 * of this value may be obtained from the corresponding Descriptor. 608 * @return the String reprensentation of the tag's value, or 796 * 797 * @return the String representation of the tag's value, or 609 798 * <code>null</code> if the tag hasn't been defined. 610 799 */ 800 @Nullable 611 801 public String getString(int tagType) 612 802 { 613 803 Object o = getObject(tagType); 614 if (o ==null)804 if (o == null) 615 805 return null; 616 806 … … 618 808 return ((Rational)o).toSimpleString(true); 619 809 620 if (o.getClass().isArray()) 621 { 810 if (o.getClass().isArray()) { 622 811 // handle arrays of objects and primitives 623 812 int arrayLength = Array.getLength(o); 624 // determine if this is an array of objects i.e. [Lcom.drew.blah 625 boolean isObjectArray = o.getClass().toString().startsWith("class [L"); 626 StringBuffer sbuffer = new StringBuffer(); 627 for (int i = 0; i<arrayLength; i++) 628 { 629 if (i!=0) 630 sbuffer.append(' '); 813 final Class<?> componentType = o.getClass().getComponentType(); 814 boolean isObjectArray = Object.class.isAssignableFrom(componentType); 815 boolean isFloatArray = componentType.getName().equals("float"); 816 boolean isDoubleArray = componentType.getName().equals("double"); 817 boolean isIntArray = componentType.getName().equals("int"); 818 boolean isLongArray = componentType.getName().equals("long"); 819 boolean isByteArray = componentType.getName().equals("byte"); 820 StringBuilder string = new StringBuilder(); 821 for (int i = 0; i < arrayLength; i++) { 822 if (i != 0) 823 string.append(' '); 631 824 if (isObjectArray) 632 sbuffer.append(Array.get(o, i).toString()); 825 string.append(Array.get(o, i).toString()); 826 else if (isIntArray) 827 string.append(Array.getInt(o, i)); 828 else if (isLongArray) 829 string.append(Array.getLong(o, i)); 830 else if (isFloatArray) 831 string.append(Array.getFloat(o, i)); 832 else if (isDoubleArray) 833 string.append(Array.getDouble(o, i)); 834 else if (isByteArray) 835 string.append(Array.getByte(o, i)); 633 836 else 634 sbuffer.append(Array.getInt(o, i)); 635 } 636 return sbuffer.toString(); 637 } 638 837 addError("Unexpected array component type: " + componentType.getName()); 838 } 839 return string.toString(); 840 } 841 842 // Note that several cameras leave trailing spaces (Olympus, Nikon) but this library is intended to show 843 // the actual data within the file. It is not inconceivable that whitespace may be significant here, so we 844 // do not trim. Also, if support is added for writing data back to files, this may cause issues. 845 // We leave trimming to the presentation layer. 639 846 return o.toString(); 640 847 } 641 848 849 @Nullable 850 public String getString(int tagType, String charset) 851 { 852 byte[] bytes = getByteArray(tagType); 853 if (bytes==null) 854 return null; 855 try { 856 return new String(bytes, charset); 857 } catch (UnsupportedEncodingException e) { 858 return null; 859 } 860 } 861 642 862 /** 643 863 * Returns the object hashed for the particular tag type specified, if available. 864 * 644 865 * @param tagType the tag type identifier 645 * @return the tag's value as an Object if available, else null 646 */ 866 * @return the tag's value as an Object if available, else <code>null</code> 867 */ 868 @java.lang.SuppressWarnings({ "UnnecessaryBoxing" }) 869 @Nullable 647 870 public Object getObject(int tagType) 648 871 { 649 return _tagMap.get( new Integer(tagType));872 return _tagMap.get(Integer.valueOf(tagType)); 650 873 } 651 874 … … 654 877 /** 655 878 * Returns the name of a specified tag as a String. 879 * 656 880 * @param tagType the tag type identifier 657 881 * @return the tag's name as a String 658 882 */ 883 @NotNull 659 884 public String getTagName(int tagType) 660 885 { 661 Integer key = new Integer(tagType); 662 HashMap nameMap = getTagNameMap(); 663 if (!nameMap.containsKey(key)) { 886 HashMap<Integer, String> nameMap = getTagNameMap(); 887 if (!nameMap.containsKey(tagType)) { 664 888 String hex = Integer.toHexString(tagType); 665 while (hex.length() <4) {889 while (hex.length() < 4) { 666 890 hex = "0" + hex; 667 891 } 668 892 return "Unknown tag (0x" + hex + ")"; 669 893 } 670 return (String)nameMap.get(key);894 return nameMap.get(tagType); 671 895 } 672 896 … … 674 898 * Provides a description of a tag's value using the descriptor set by 675 899 * <code>setDescriptor(Descriptor)</code>. 900 * 676 901 * @param tagType the tag type identifier 677 902 * @return the tag value's description as a String 678 * @throws MetadataException if a descriptor hasn't been set, or if an error 679 * occurs during calculation of the description within the Descriptor 680 */ 681 public String getDescription(int tagType) throws MetadataException 682 { 683 if (_descriptor==null) { 684 throw new MetadataException("a descriptor must be set using setDescriptor(...) before descriptions can be provided"); 685 } 686 903 */ 904 @Nullable 905 public String getDescription(int tagType) 906 { 907 assert(_descriptor != null); 687 908 return _descriptor.getDescription(tagType); 688 909 } -
trunk/src/com/drew/metadata/Metadata.java
r4231 r6127 1 1 /* 2 * Metadata.java2 * Copyright 2002-2012 Drew Noakes 3 3 * 4 * This class is public domain software - that is, you can do whatever you want 5 * with it, and include it software that is licensed under the GNU or the 6 * BSD license, or whatever other licence you choose, including proprietary 7 * closed source licenses. Similarly, I release this Java version under the 8 * same license, though I do ask that you leave this header in tact. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 9 7 * 10 * If you make modifications to this code that you think would benefit the 11 * wider community, please send me a copy and I'll post it on my site. 8 * http://www.apache.org/licenses/LICENSE-2.0 12 9 * 13 * If you make use of this code, I'd appreciate hearing about it. 14 * drew.noakes@drewnoakes.com 15 * Latest version of this software kept at 16 * http://drewnoakes.com/ 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 17 15 * 18 * Created on 28 April 2002, 17:40 19 * Modified 04 Aug 2002 20 * - Adjusted javadoc 21 * - Added 22 * Modified 29 Oct 2002 (v1.2) 23 * - Stored IFD directories in separate tag-spaces 24 * - iterator() now returns an Iterator over a list of TagValue objects 25 * - More get*Description() methods to detail GPS tags, among others 26 * - Put spaces between words of tag name for presentation reasons (they had no 27 * significance in compound form) 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 28 20 */ 29 21 package com.drew.metadata; 30 22 31 import java.io.Serializable; 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 25 32 26 import java.util.ArrayList; 27 import java.util.Collection; 33 28 import java.util.HashMap; 34 import java.util. Iterator;29 import java.util.Map; 35 30 36 31 /** 37 * Result from an exif extraction operation, containing all tags, their 38 * values and support for retrieving them. 39 * @author Drew Noakes http://drewnoakes.com 32 * A top-level object to hold the various types of metadata (Exif/IPTC/etc) related to one entity (such as a file 33 * or stream). 34 * <p/> 35 * Metadata objects may contain zero or more directories. Each directory may contain zero or more tags with 36 * corresponding values. 37 * 38 * @author Drew Noakes http://drewnoakes.com 40 39 */ 41 public final class Metadata implements Serializable40 public final class Metadata 42 41 { 43 /** 44 * 45 */ 46 private final HashMap directoryMap; 47 42 @NotNull 43 private final Map<Class<? extends Directory>,Directory> _directoryByClass = new HashMap<Class<? extends Directory>, Directory>(); 44 48 45 /** 49 46 * List of Directory objects set against this object. Keeping a list handy makes 50 47 * creation of an Iterator and counting tags simple. 51 48 */ 52 private final ArrayList directoryList; 49 @NotNull 50 private final Collection<Directory> _directoryList = new ArrayList<Directory>(); 53 51 54 52 /** 55 * Creates a new instance of Metadata. Package private. 53 * Returns an objects for iterating over Directory objects in the order in which they were added. 54 * 55 * @return an iterable collection of directories 56 56 */ 57 public Metadata() 57 @NotNull 58 public Iterable<Directory> getDirectories() 58 59 { 59 directoryMap = new HashMap(); 60 directoryList = new ArrayList(); 61 } 62 63 64 // OTHER METHODS 65 66 /** 67 * Creates an Iterator over the tag types set against this image, preserving the order 68 * in which they were set. Should the same tag have been set more than once, it's first 69 * position is maintained, even though the final value is used. 70 * @return an Iterator of tag types set for this image 71 */ 72 public Iterator getDirectoryIterator() 73 { 74 return directoryList.iterator(); 60 return _directoryList; 75 61 } 76 62 77 63 /** 78 64 * Returns a count of unique directories in this metadata collection. 65 * 79 66 * @return the number of unique directory types set for this metadata collection 80 67 */ 81 68 public int getDirectoryCount() 82 69 { 83 return directoryList.size();70 return _directoryList.size(); 84 71 } 85 72 … … 88 75 * such a directory, it is returned. Otherwise a new instance of this directory will be created and stored within 89 76 * this Metadata object. 77 * 90 78 * @param type the type of the Directory implementation required. 91 79 * @return a directory of the specified type. 92 80 */ 93 public Directory getDirectory(Class type) 81 @NotNull 82 @SuppressWarnings("unchecked") 83 public <T extends Directory> T getOrCreateDirectory(@NotNull Class<T> type) 94 84 { 95 if (!Directory.class.isAssignableFrom(type)) {96 throw new RuntimeException("Class type passed to getDirectory must be an implementation of com.drew.metadata.Directory");97 } 85 // We suppress the warning here as the code asserts a map signature of Class<T>,T. 86 // So after get(Class<T>) it is for sure the result is from type T. 87 98 88 // check if we've already issued this type of directory 99 if ( directoryMap.containsKey(type)) {100 return ( Directory)directoryMap.get(type);101 } 102 Objectdirectory;89 if (_directoryByClass.containsKey(type)) 90 return (T)_directoryByClass.get(type); 91 92 T directory; 103 93 try { 104 94 directory = type.newInstance(); … … 106 96 throw new RuntimeException("Cannot instantiate provided Directory type: " + type.toString()); 107 97 } 108 // store the directory in case it's requested later 109 directoryMap.put(type, directory); 110 directoryList.add(directory); 111 return (Directory)directory; 98 // store the directory 99 _directoryByClass.put(type, directory); 100 _directoryList.add(directory); 101 102 return directory; 103 } 104 105 /** 106 * If this <code>Metadata</code> object contains a <code>Directory</code> of the specified type, it is returned. 107 * Otherwise <code>null</code> is returned. 108 * 109 * @param type the Directory type 110 * @param <T> the Directory type 111 * @return a Directory of type T if it exists in this Metadata object, otherwise <code>null</code>. 112 */ 113 @Nullable 114 @SuppressWarnings("unchecked") 115 public <T extends Directory> T getDirectory(@NotNull Class<T> type) 116 { 117 // We suppress the warning here as the code asserts a map signature of Class<T>,T. 118 // So after get(Class<T>) it is for sure the result is from type T. 119 120 return (T)_directoryByClass.get(type); 112 121 } 113 122 114 123 /** 115 124 * Indicates whether a given directory type has been created in this metadata 116 * repository. Directories are created by calling getDirectory(Class). 125 * repository. Directories are created by calling <code>getOrCreateDirectory(Class)</code>. 126 * 117 127 * @param type the Directory type 118 128 * @return true if the metadata directory has been created 119 129 */ 120 public boolean containsDirectory(Class type)130 public boolean containsDirectory(Class<? extends Directory> type) 121 131 { 122 return directoryMap.containsKey(type); 132 return _directoryByClass.containsKey(type); 133 } 134 135 /** 136 * Indicates whether any errors were reported during the reading of metadata values. 137 * This value will be true if Directory.hasErrors() is true for one of the contained Directory objects. 138 * 139 * @return whether one of the contained directories has an error 140 */ 141 public boolean hasErrors() 142 { 143 for (Directory directory : _directoryList) { 144 if (directory.hasErrors()) 145 return true; 146 } 147 return false; 123 148 } 124 149 } -
trunk/src/com/drew/metadata/MetadataException.java
r4231 r6127 1 1 /* 2 * This is public domain software - that is, you can do whatever you want 3 * with it, and include it software that is licensed under the GNU or the 4 * BSD license, or whatever other licence you choose, including proprietary 5 * closed source licenses. I do ask that you leave this header in tact. 2 * Copyright 2002-2012 Drew Noakes 6 3 * 7 * If you make modifications to this code that you think would benefit the 8 * wider community, please send me a copy and I'll post it on my site. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 9 7 * 10 * If you make use of this code, I'd appreciate hearing about it. 11 * drew@drewnoakes.com 12 * Latest version of this software kept at 13 * http://drewnoakes.com/ 8 * http://www.apache.org/licenses/LICENSE-2.0 14 9 * 15 * Created by dnoakes on 13-Nov-2002 18:10:23 using IntelliJ IDEA. 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 16 20 */ 17 21 package com.drew.metadata; 18 22 19 23 import com.drew.lang.CompoundException; 24 import com.drew.lang.annotations.Nullable; 20 25 21 26 /** 27 * Base class for all metadata specific exceptions. 22 28 * 29 * @author Drew Noakes http://drewnoakes.com 23 30 */ 24 31 public class MetadataException extends CompoundException 25 32 { 26 public MetadataException(String msg) 33 private static final long serialVersionUID = 8612756143363919682L; 34 35 public MetadataException(@Nullable String msg) 27 36 { 28 37 super(msg); 29 38 } 30 39 31 public MetadataException( Throwable exception)40 public MetadataException(@Nullable Throwable exception) 32 41 { 33 42 super(exception); 34 43 } 35 44 36 public MetadataException( String msg,Throwable innerException)45 public MetadataException(@Nullable String msg, @Nullable Throwable innerException) 37 46 { 38 47 super(msg, innerException); -
trunk/src/com/drew/metadata/MetadataReader.java
r4231 r6127 1 1 /* 2 * This is public domain software - that is, you can do whatever you want 3 * with it, and include it software that is licensed under the GNU or the 4 * BSD license, or whatever other licence you choose, including proprietary 5 * closed source licenses. I do ask that you leave this header in tact. 2 * Copyright 2002-2012 Drew Noakes 6 3 * 7 * If you make modifications to this code that you think would benefit the 8 * wider community, please send me a copy and I'll post it on my site. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 9 7 * 10 * If you make use of this code, I'd appreciate hearing about it. 11 * drew@drewnoakes.com 12 * Latest version of this software kept at 13 * http://drewnoakes.com/ 8 * http://www.apache.org/licenses/LICENSE-2.0 14 9 * 15 * Created by dnoakes on 26-Nov-2002 11:21:43 using IntelliJ IDEA. 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 16 20 */ 17 21 package com.drew.metadata; 18 22 23 import com.drew.lang.BufferReader; 24 import com.drew.lang.annotations.NotNull; 25 19 26 /** 27 * Interface through which all classes responsible for decoding a particular type of metadata may be called. 28 * Note that the data source is not specified on this interface. Instead it is suggested that implementations 29 * take their data within a constructor. Constructors might be overloaded to allow for different sources, such as 30 * files, streams and byte arrays. As such, instances of implementations of this interface would be single-use and 31 * not thread-safe. 20 32 * 33 * @author Drew Noakes http://drewnoakes.com 21 34 */ 22 35 public interface MetadataReader 23 36 { 24 public Metadata extract(); 25 26 public Metadata extract(Metadata metadata); 37 /** 38 * Extract metadata from the source and merge it into an existing Metadata object. 39 * 40 * @param reader The reader from which the metadata should be extracted. 41 * @param metadata The Metadata object into which extracted values should be merged. 42 */ 43 public void extract(@NotNull final BufferReader reader, @NotNull final Metadata metadata); 27 44 } -
trunk/src/com/drew/metadata/Tag.java
r4231 r6127 1 1 /* 2 * This is public domain software - that is, you can do whatever you want 3 * with it, and include it software that is licensed under the GNU or the 4 * BSD license, or whatever other licence you choose, including proprietary 5 * closed source licenses. I do ask that you leave this header in tact. 2 * Copyright 2002-2012 Drew Noakes 6 3 * 7 * If you make modifications to this code that you think would benefit the 8 * wider community, please send me a copy and I'll post it on my site. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 9 7 * 10 * If you make use of this code, I'd appreciate hearing about it. 11 * drew@drewnoakes.com 12 * Latest version of this software kept at 13 * http://drewnoakes.com/ 8 * http://www.apache.org/licenses/LICENSE-2.0 14 9 * 15 * Created by dnoakes on 26-Nov-2002 18:29:12 using IntelliJ IDEA. 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 16 20 */ 17 21 package com.drew.metadata; 18 22 19 import java.io.Serializable; 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 20 25 21 26 /** 27 * Models a particular tag within a directory and provides methods for obtaining its value. Note that a Tag instance is 28 * specific to a particular metadata extraction and cannot be reused. 22 29 * 30 * @author Drew Noakes http://drewnoakes.com 23 31 */ 24 public class Tag implements Serializable32 public class Tag 25 33 { 26 34 private final int _tagType; 35 @NotNull 27 36 private final Directory _directory; 28 37 29 public Tag(int tagType, Directory directory)38 public Tag(int tagType, @NotNull Directory directory) 30 39 { 31 40 _tagType = tagType; … … 35 44 /** 36 45 * Gets the tag type as an int 46 * 37 47 * @return the tag type as an int 38 48 */ … … 45 55 * Gets the tag type in hex notation as a String with padded leading 46 56 * zeroes if necessary (i.e. <code>0x100E</code>). 57 * 47 58 * @return the tag type as a string in hexadecimal notation 48 59 */ 60 @NotNull 49 61 public String getTagTypeHex() 50 62 { … … 57 69 * Get a description of the tag's value, considering enumerated values 58 70 * and units. 71 * 59 72 * @return a description of the tag's value 60 73 */ 61 public String getDescription() throws MetadataException 74 @Nullable 75 public String getDescription() 62 76 { 63 77 return _directory.getDescription(_tagType); … … 67 81 * Get the name of the tag, such as <code>Aperture</code>, or 68 82 * <code>InteropVersion</code>. 83 * 69 84 * @return the tag's name 70 85 */ 86 @NotNull 71 87 public String getTagName() 72 88 { … … 77 93 * Get the name of the directory in which the tag exists, such as 78 94 * <code>Exif</code>, <code>GPS</code> or <code>Interoperability</code>. 95 * 79 96 * @return name of the directory in which this tag exists 80 97 */ 98 @NotNull 81 99 public String getDirectoryName() 82 100 { … … 85 103 86 104 /** 87 * A basic representation of the tag's type and value in format:88 * <code>FNumber - F2.8</code>.105 * A basic representation of the tag's type and value. EG: <code>[FNumber] F2.8</code>. 106 * 89 107 * @return the tag's type and value 90 108 */ 109 @NotNull 91 110 public String toString() 92 111 { 93 String description; 94 try { 95 description = getDescription(); 96 } catch (MetadataException e) { 112 String description = getDescription(); 113 if (description==null) 97 114 description = _directory.getString(getTagType()) + " (unable to formulate description)"; 98 }99 115 return "[" + _directory.getName() + "] " + getTagName() + " - " + description; 100 116 } -
trunk/src/com/drew/metadata/TagDescriptor.java
r4231 r6127 1 1 /* 2 * This is public domain software - that is, you can do whatever you want 3 * with it, and include it software that is licensed under the GNU or the 4 * BSD license, or whatever other licence you choose, including proprietary 5 * closed source licenses. I do ask that you leave this header in tact. 2 * Copyright 2002-2012 Drew Noakes 6 3 * 7 * If you make modifications to this code that you think would benefit the 8 * wider community, please send me a copy and I'll post it on my site. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 9 7 * 10 * If you make use of this code, I'd appreciate hearing about it. 11 * drew@drewnoakes.com 12 * Latest version of this software kept at 13 * http://drewnoakes.com/ 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 14 20 */ 15 21 package com.drew.metadata; 16 22 17 import java.io.Serializable; 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 25 26 import java.lang.reflect.Array; 18 27 19 28 /** 20 29 * Abstract base class for all tag descriptor classes. Implementations are responsible for 21 * providing the human-readable string represen ation of tag values stored in a directory.30 * providing the human-readable string representation of tag values stored in a directory. 22 31 * The directory is provided to the tag descriptor via its constructor. 32 * 33 * @author Drew Noakes http://drewnoakes.com 23 34 */ 24 public abstract class TagDescriptor implements Serializable35 public abstract class TagDescriptor<T extends Directory> 25 36 { 26 protected final Directory _directory; 37 @NotNull 38 protected final T _directory; 27 39 28 public TagDescriptor( Directorydirectory)40 public TagDescriptor(@NotNull T directory) 29 41 { 30 42 _directory = directory; … … 34 46 * Returns a descriptive value of the the specified tag for this image. 35 47 * Where possible, known values will be substituted here in place of the raw 36 * tokens actually kept in the Exif segment. If no substitution is 37 * available, the value provided by getString(int) will be returned. 38 * <p> 39 * This and getString(int) are the only 'get' methods that won't throw an 40 * exception. 48 * tokens actually kept in the metadata segment. If no substitution is 49 * available, the value provided by <code>getString(tagType)</code> will be returned. 50 * 41 51 * @param tagType the tag to find a description for 42 52 * @return a description of the image's value for the specified tag, or 43 53 * <code>null</code> if the tag hasn't been defined. 44 54 */ 45 public abstract String getDescription(int tagType) throws MetadataException; 55 @Nullable 56 public String getDescription(int tagType) 57 { 58 Object object = _directory.getObject(tagType); 59 60 if (object==null) 61 return null; 62 63 // special presentation for long arrays 64 if (object.getClass().isArray()) { 65 final int length = Array.getLength(object); 66 if (length > 16) { 67 final String componentTypeName = object.getClass().getComponentType().getName(); 68 return String.format("[%d %s%s]", length, componentTypeName, length==1 ? "" : "s"); 69 } 70 } 71 72 // no special handling required, so use default conversion to a string 73 return _directory.getString(tagType); 74 } 75 76 /** 77 * Takes a series of 4 bytes from the specified offset, and converts these to a 78 * well-known version number, where possible. 79 * <p/> 80 * Two different formats are processed: 81 * <ul> 82 * <li>[30 32 31 30] -> 2.10</li> 83 * <li>[0 1 0 0] -> 1.00</li> 84 * </ul> 85 * @param components the four version values 86 * @param majorDigits the number of components to be 87 * @return the version as a string of form "2.10" or null if the argument cannot be converted 88 */ 89 @Nullable 90 public static String convertBytesToVersionString(@Nullable int[] components, final int majorDigits) 91 { 92 if (components==null) 93 return null; 94 StringBuilder version = new StringBuilder(); 95 for (int i = 0; i < 4 && i < components.length; i++) { 96 if (i == majorDigits) 97 version.append('.'); 98 char c = (char)components[i]; 99 if (c < '0') 100 c += '0'; 101 if (i == 0 && c=='0') 102 continue; 103 version.append(c); 104 } 105 return version.toString(); 106 } 46 107 } -
trunk/src/com/drew/metadata/exif/CanonMakernoteDescriptor.java
r4231 r6127 1 1 /* 2 * This is public domain software - that is, you can do whatever you want 3 * with it, and include it software that is licensed under the GNU or the 4 * BSD license, or whatever other licence you choose, including proprietary 5 * closed source licenses. I do ask that you leave this header in tact. 2 * Copyright 2002-2012 Drew Noakes 6 3 * 7 * If you make modifications to this code that you think would benefit the 8 * wider community, please send me a copy and I'll post it on my site. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 9 7 * 10 * If you make use of this code, I'd appreciate hearing about it. 11 * drew@drewnoakes.com 12 * Latest version of this software kept at 13 * http://drewnoakes.com/ 8 * http://www.apache.org/licenses/LICENSE-2.0 14 9 * 15 * Created by dnoakes on 27-Nov-2002 10:12:05 using IntelliJ IDEA. 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 16 20 */ 17 21 package com.drew.metadata.exif; 18 22 19 import com.drew. metadata.Directory;20 import com.drew. metadata.MetadataException;23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 21 25 import com.drew.metadata.TagDescriptor; 22 26 23 27 /** 28 * Provides human-readable string representations of tag values stored in a <code>CanonMakernoteDirectory</code>. 24 29 * 30 * @author Drew Noakes http://drewnoakes.com 25 31 */ 26 public class CanonMakernoteDescriptor extends TagDescriptor 32 public class CanonMakernoteDescriptor extends TagDescriptor<CanonMakernoteDirectory> 27 33 { 28 public CanonMakernoteDescriptor( Directory directory)34 public CanonMakernoteDescriptor(@NotNull CanonMakernoteDirectory directory) 29 35 { 30 36 super(directory); 31 37 } 32 38 33 public String getDescription(int tagType) throws MetadataException 39 @Nullable 40 public String getDescription(int tagType) 34 41 { 35 42 switch (tagType) { 36 case CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_ACTIVITY: 43 case CanonMakernoteDirectory.TAG_CANON_SERIAL_NUMBER: 44 return getSerialNumberDescription(); 45 case CanonMakernoteDirectory.CameraSettings.TAG_FLASH_ACTIVITY: 37 46 return getFlashActivityDescription(); 38 case CanonMakernoteDirectory. TAG_CANON_STATE1_FOCUS_TYPE:47 case CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_TYPE: 39 48 return getFocusTypeDescription(); 40 case CanonMakernoteDirectory. TAG_CANON_STATE1_DIGITAL_ZOOM:49 case CanonMakernoteDirectory.CameraSettings.TAG_DIGITAL_ZOOM: 41 50 return getDigitalZoomDescription(); 42 case CanonMakernoteDirectory. TAG_CANON_STATE1_QUALITY:51 case CanonMakernoteDirectory.CameraSettings.TAG_QUALITY: 43 52 return getQualityDescription(); 44 case CanonMakernoteDirectory. TAG_CANON_STATE1_MACRO_MODE:53 case CanonMakernoteDirectory.CameraSettings.TAG_MACRO_MODE: 45 54 return getMacroModeDescription(); 46 case CanonMakernoteDirectory. TAG_CANON_STATE1_SELF_TIMER_DELAY:55 case CanonMakernoteDirectory.CameraSettings.TAG_SELF_TIMER_DELAY: 47 56 return getSelfTimerDelayDescription(); 48 case CanonMakernoteDirectory. TAG_CANON_STATE1_FLASH_MODE:57 case CanonMakernoteDirectory.CameraSettings.TAG_FLASH_MODE: 49 58 return getFlashModeDescription(); 50 case CanonMakernoteDirectory. TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE:59 case CanonMakernoteDirectory.CameraSettings.TAG_CONTINUOUS_DRIVE_MODE: 51 60 return getContinuousDriveModeDescription(); 52 case CanonMakernoteDirectory. TAG_CANON_STATE1_FOCUS_MODE_1:61 case CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_MODE_1: 53 62 return getFocusMode1Description(); 54 case CanonMakernoteDirectory. TAG_CANON_STATE1_IMAGE_SIZE:63 case CanonMakernoteDirectory.CameraSettings.TAG_IMAGE_SIZE: 55 64 return getImageSizeDescription(); 56 case CanonMakernoteDirectory. TAG_CANON_STATE1_EASY_SHOOTING_MODE:65 case CanonMakernoteDirectory.CameraSettings.TAG_EASY_SHOOTING_MODE: 57 66 return getEasyShootingModeDescription(); 58 case CanonMakernoteDirectory. TAG_CANON_STATE1_CONTRAST:67 case CanonMakernoteDirectory.CameraSettings.TAG_CONTRAST: 59 68 return getContrastDescription(); 60 case CanonMakernoteDirectory. TAG_CANON_STATE1_SATURATION:69 case CanonMakernoteDirectory.CameraSettings.TAG_SATURATION: 61 70 return getSaturationDescription(); 62 case CanonMakernoteDirectory. TAG_CANON_STATE1_SHARPNESS:71 case CanonMakernoteDirectory.CameraSettings.TAG_SHARPNESS: 63 72 return getSharpnessDescription(); 64 case CanonMakernoteDirectory. TAG_CANON_STATE1_ISO:73 case CanonMakernoteDirectory.CameraSettings.TAG_ISO: 65 74 return getIsoDescription(); 66 case CanonMakernoteDirectory. TAG_CANON_STATE1_METERING_MODE:75 case CanonMakernoteDirectory.CameraSettings.TAG_METERING_MODE: 67 76 return getMeteringModeDescription(); 68 case CanonMakernoteDirectory. TAG_CANON_STATE1_AF_POINT_SELECTED:77 case CanonMakernoteDirectory.CameraSettings.TAG_AF_POINT_SELECTED: 69 78 return getAfPointSelectedDescription(); 70 case CanonMakernoteDirectory. TAG_CANON_STATE1_EXPOSURE_MODE:79 case CanonMakernoteDirectory.CameraSettings.TAG_EXPOSURE_MODE: 71 80 return getExposureModeDescription(); 72 case CanonMakernoteDirectory. TAG_CANON_STATE1_LONG_FOCAL_LENGTH:81 case CanonMakernoteDirectory.CameraSettings.TAG_LONG_FOCAL_LENGTH: 73 82 return getLongFocalLengthDescription(); 74 case CanonMakernoteDirectory. TAG_CANON_STATE1_SHORT_FOCAL_LENGTH:83 case CanonMakernoteDirectory.CameraSettings.TAG_SHORT_FOCAL_LENGTH: 75 84 return getShortFocalLengthDescription(); 76 case CanonMakernoteDirectory. TAG_CANON_STATE1_FOCAL_UNITS_PER_MM:85 case CanonMakernoteDirectory.CameraSettings.TAG_FOCAL_UNITS_PER_MM: 77 86 return getFocalUnitsPerMillimetreDescription(); 78 case CanonMakernoteDirectory. TAG_CANON_STATE1_FLASH_DETAILS:87 case CanonMakernoteDirectory.CameraSettings.TAG_FLASH_DETAILS: 79 88 return getFlashDetailsDescription(); 80 case CanonMakernoteDirectory. TAG_CANON_STATE1_FOCUS_MODE_2:89 case CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_MODE_2: 81 90 return getFocusMode2Description(); 82 case CanonMakernoteDirectory. TAG_CANON_STATE2_WHITE_BALANCE:91 case CanonMakernoteDirectory.FocalLength.TAG_WHITE_BALANCE: 83 92 return getWhiteBalanceDescription(); 84 case CanonMakernoteDirectory. TAG_CANON_STATE2_AF_POINT_USED:93 case CanonMakernoteDirectory.FocalLength.TAG_AF_POINT_USED: 85 94 return getAfPointUsedDescription(); 86 case CanonMakernoteDirectory. TAG_CANON_STATE2_FLASH_BIAS:95 case CanonMakernoteDirectory.FocalLength.TAG_FLASH_BIAS: 87 96 return getFlashBiasDescription(); 88 case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION: 89 return getLongExposureNoiseReductionDescription(); 90 case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS: 91 return getShutterAutoExposureLockButtonDescription(); 92 case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP: 93 return getMirrorLockupDescription(); 94 case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL: 95 return getTvAndAvExposureLevelDescription(); 96 case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT: 97 return getAutoFocusAssistLightDescription(); 98 case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE: 99 return getShutterSpeedInAvModeDescription(); 100 case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING: 101 return getAutoExposureBrackettingSequenceAndAutoCancellationDescription(); 102 case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC: 103 return getShutterCurtainSyncDescription(); 104 case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP: 105 return getLensAutoFocusStopButtonDescription(); 106 case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION: 107 return getFillFlashReductionDescription(); 108 case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN: 109 return getMenuButtonReturnPositionDescription(); 110 case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION: 111 return getSetButtonFunctionWhenShootingDescription(); 112 case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING: 113 return getSensorCleaningDescription(); 114 default: 115 return _directory.getString(tagType); 116 } 117 } 118 119 public String getLongExposureNoiseReductionDescription() throws MetadataException 120 { 121 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION)) return null; 122 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION); 97 98 // It turns out that these values are dependent upon the camera model and therefore the below code was 99 // incorrect for some Canon models. This needs to be revisited. 100 101 // case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION: 102 // return getLongExposureNoiseReductionDescription(); 103 // case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS: 104 // return getShutterAutoExposureLockButtonDescription(); 105 // case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP: 106 // return getMirrorLockupDescription(); 107 // case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL: 108 // return getTvAndAvExposureLevelDescription(); 109 // case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT: 110 // return getAutoFocusAssistLightDescription(); 111 // case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE: 112 // return getShutterSpeedInAvModeDescription(); 113 // case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING: 114 // return getAutoExposureBrackettingSequenceAndAutoCancellationDescription(); 115 // case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC: 116 // return getShutterCurtainSyncDescription(); 117 // case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP: 118 // return getLensAutoFocusStopButtonDescription(); 119 // case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION: 120 // return getFillFlashReductionDescription(); 121 // case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN: 122 // return getMenuButtonReturnPositionDescription(); 123 // case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION: 124 // return getSetButtonFunctionWhenShootingDescription(); 125 // case CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING: 126 // return getSensorCleaningDescription(); 127 default: 128 return super.getDescription(tagType); 129 } 130 } 131 132 @Nullable 133 public String getSerialNumberDescription() 134 { 135 Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_SERIAL_NUMBER); 136 if (value==null) 137 return null; 138 return String.format("%04X%05d", (value >> 8) & 0xFF, value & 0xFF); 139 } 140 141 /* 142 @Nullable 143 public String getLongExposureNoiseReductionDescription() 144 { 145 Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION); 146 if (value==null) 147 return null; 123 148 switch (value) { 124 149 case 0: return "Off"; … … 127 152 } 128 153 } 129 public String getShutterAutoExposureLockButtonDescription() throws MetadataException 130 { 131 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS)) return null; 132 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS); 154 155 @Nullable 156 public String getShutterAutoExposureLockButtonDescription() 157 { 158 Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS); 159 if (value==null) 160 return null; 133 161 switch (value) { 134 162 case 0: return "AF/AE lock"; … … 139 167 } 140 168 } 141 public String getMirrorLockupDescription() throws MetadataException 142 { 143 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP)) return null; 144 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP); 169 170 @Nullable 171 public String getMirrorLockupDescription() 172 { 173 Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP); 174 if (value==null) 175 return null; 145 176 switch (value) { 146 177 case 0: return "Disabled"; … … 149 180 } 150 181 } 151 public String getTvAndAvExposureLevelDescription() throws MetadataException 152 { 153 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL)) return null; 154 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL); 182 183 @Nullable 184 public String getTvAndAvExposureLevelDescription() 185 { 186 Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL); 187 if (value==null) 188 return null; 155 189 switch (value) { 156 190 case 0: return "1/2 stop"; … … 159 193 } 160 194 } 161 public String getAutoFocusAssistLightDescription() throws MetadataException 162 { 163 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT)) return null; 164 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT); 195 196 @Nullable 197 public String getAutoFocusAssistLightDescription() 198 { 199 Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT); 200 if (value==null) 201 return null; 165 202 switch (value) { 166 203 case 0: return "On (Auto)"; … … 169 206 } 170 207 } 171 public String getShutterSpeedInAvModeDescription() throws MetadataException 172 { 173 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE)) return null; 174 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE); 208 209 @Nullable 210 public String getShutterSpeedInAvModeDescription() 211 { 212 Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE); 213 if (value==null) 214 return null; 175 215 switch (value) { 176 216 case 0: return "Automatic"; … … 179 219 } 180 220 } 181 public String getAutoExposureBrackettingSequenceAndAutoCancellationDescription() throws MetadataException 182 { 183 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING)) return null; 184 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING); 221 222 @Nullable 223 public String getAutoExposureBrackettingSequenceAndAutoCancellationDescription() 224 { 225 Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING); 226 if (value==null) 227 return null; 185 228 switch (value) { 186 229 case 0: return "0,-,+ / Enabled"; … … 191 234 } 192 235 } 193 public String getShutterCurtainSyncDescription() throws MetadataException 194 { 195 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC)) return null; 196 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC); 236 237 @Nullable 238 public String getShutterCurtainSyncDescription() 239 { 240 Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC); 241 if (value==null) 242 return null; 197 243 switch (value) { 198 244 case 0: return "1st Curtain Sync"; … … 201 247 } 202 248 } 203 public String getLensAutoFocusStopButtonDescription() throws MetadataException 204 { 205 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP)) return null; 206 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP); 249 250 @Nullable 251 public String getLensAutoFocusStopButtonDescription() 252 { 253 Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_AF_STOP); 254 if (value==null) 255 return null; 207 256 switch (value) { 208 257 case 0: return "AF stop"; … … 212 261 } 213 262 } 214 public String getFillFlashReductionDescription() throws MetadataException 215 { 216 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION)) return null; 217 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION); 263 264 @Nullable 265 public String getFillFlashReductionDescription() 266 { 267 Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION); 268 if (value==null) 269 return null; 218 270 switch (value) { 219 271 case 0: return "Enabled"; … … 222 274 } 223 275 } 224 public String getMenuButtonReturnPositionDescription() throws MetadataException 225 { 226 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN)) return null; 227 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN); 276 277 @Nullable 278 public String getMenuButtonReturnPositionDescription() 279 { 280 Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN); 281 if (value==null) 282 return null; 228 283 switch (value) { 229 284 case 0: return "Top"; … … 233 288 } 234 289 } 235 public String getSetButtonFunctionWhenShootingDescription() throws MetadataException 236 { 237 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION)) return null; 238 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION); 290 291 @Nullable 292 public String getSetButtonFunctionWhenShootingDescription() 293 { 294 Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION); 295 if (value==null) 296 return null; 239 297 switch (value) { 240 298 case 0: return "Not Assigned"; … … 245 303 } 246 304 } 247 public String getSensorCleaningDescription() throws MetadataException 248 { 249 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING)) return null; 250 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING); 305 306 @Nullable 307 public String getSensorCleaningDescription() 308 { 309 Integer value = _directory.getInteger(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING); 310 if (value==null) 311 return null; 251 312 switch (value) { 252 313 case 0: return "Disabled"; … … 255 316 } 256 317 } 257 258 public String getFlashBiasDescription() throws MetadataException 259 { 260 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE2_FLASH_BIAS)) return null; 261 262 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE2_FLASH_BIAS); 318 */ 319 320 @Nullable 321 public String getFlashBiasDescription() 322 { 323 Integer value = _directory.getInteger(CanonMakernoteDirectory.FocalLength.TAG_FLASH_BIAS); 324 325 if (value==null) 326 return null; 263 327 264 328 boolean isNegative = false; … … 278 342 } 279 343 280 public String getAfPointUsedDescription() throws MetadataException 281 { 282 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE2_AF_POINT_USED)) return null; 283 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE2_AF_POINT_USED); 344 @Nullable 345 public String getAfPointUsedDescription() 346 { 347 Integer value = _directory.getInteger(CanonMakernoteDirectory.FocalLength.TAG_AF_POINT_USED); 348 if (value==null) 349 return null; 284 350 if ((value & 0x7) == 0) { 285 351 return "Right"; … … 293 359 } 294 360 295 public String getWhiteBalanceDescription() throws MetadataException 296 { 297 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE2_WHITE_BALANCE)) return null; 298 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE2_WHITE_BALANCE); 361 @Nullable 362 public String getWhiteBalanceDescription() 363 { 364 Integer value = _directory.getInteger(CanonMakernoteDirectory.FocalLength.TAG_WHITE_BALANCE); 365 if (value==null) 366 return null; 299 367 switch (value) { 300 368 case 0: … … 307 375 return "Tungsten"; 308 376 case 4: 309 return "Flo urescent";377 return "Florescent"; 310 378 case 5: 311 379 return "Flash"; … … 317 385 } 318 386 319 public String getFocusMode2Description() throws MetadataException 320 { 321 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_2)) return null; 322 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_2); 387 @Nullable 388 public String getFocusMode2Description() 389 { 390 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_MODE_2); 391 if (value==null) 392 return null; 323 393 switch (value) { 324 394 case 0: … … 331 401 } 332 402 333 public String getFlashDetailsDescription() throws MetadataException 334 { 335 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_DETAILS)) return null; 336 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_DETAILS); 337 if (((value << 14) & 1) > 0) { 403 @Nullable 404 public String getFlashDetailsDescription() 405 { 406 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FLASH_DETAILS); 407 if (value==null) 408 return null; 409 if (((value >> 14) & 1) > 0) { 338 410 return "External E-TTL"; 339 411 } 340 if (((value <<13) & 1) > 0) {412 if (((value >> 13) & 1) > 0) { 341 413 return "Internal flash"; 342 414 } 343 if (((value <<11) & 1) > 0) {415 if (((value >> 11) & 1) > 0) { 344 416 return "FP sync used"; 345 417 } 346 if (((value <<4) & 1) > 0) {418 if (((value >> 4) & 1) > 0) { 347 419 return "FP sync enabled"; 348 420 } … … 350 422 } 351 423 352 public String getFocalUnitsPerMillimetreDescription() throws MetadataException 353 { 354 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCAL_UNITS_PER_MM)) return ""; 355 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCAL_UNITS_PER_MM); 424 @Nullable 425 public String getFocalUnitsPerMillimetreDescription() 426 { 427 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCAL_UNITS_PER_MM); 428 if (value==null) 429 return null; 356 430 if (value != 0) { 357 431 return Integer.toString(value); … … 361 435 } 362 436 363 public String getShortFocalLengthDescription() throws MetadataException 364 { 365 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SHORT_FOCAL_LENGTH)) return null; 366 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SHORT_FOCAL_LENGTH); 437 @Nullable 438 public String getShortFocalLengthDescription() 439 { 440 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SHORT_FOCAL_LENGTH); 441 if (value==null) 442 return null; 367 443 String units = getFocalUnitsPerMillimetreDescription(); 368 444 return Integer.toString(value) + " " + units; 369 445 } 370 446 371 public String getLongFocalLengthDescription() throws MetadataException 372 { 373 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_LONG_FOCAL_LENGTH)) return null; 374 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_LONG_FOCAL_LENGTH); 447 @Nullable 448 public String getLongFocalLengthDescription() 449 { 450 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_LONG_FOCAL_LENGTH); 451 if (value==null) 452 return null; 375 453 String units = getFocalUnitsPerMillimetreDescription(); 376 454 return Integer.toString(value) + " " + units; 377 455 } 378 456 379 public String getExposureModeDescription() throws MetadataException 380 { 381 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_EXPOSURE_MODE)) return null; 382 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_EXPOSURE_MODE); 457 @Nullable 458 public String getExposureModeDescription() 459 { 460 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_EXPOSURE_MODE); 461 if (value==null) 462 return null; 383 463 switch (value) { 384 464 case 0: … … 399 479 } 400 480 401 public String getAfPointSelectedDescription() throws MetadataException 402 { 403 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_AF_POINT_SELECTED)) return null; 404 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_AF_POINT_SELECTED); 481 @Nullable 482 public String getAfPointSelectedDescription() 483 { 484 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_AF_POINT_SELECTED); 485 if (value==null) 486 return null; 405 487 switch (value) { 406 488 case 0x3000: … … 419 501 } 420 502 421 public String getMeteringModeDescription() throws MetadataException 422 { 423 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_METERING_MODE)) return null; 424 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_METERING_MODE); 503 @Nullable 504 public String getMeteringModeDescription() 505 { 506 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_METERING_MODE); 507 if (value==null) 508 return null; 425 509 switch (value) { 426 510 case 3: … … 435 519 } 436 520 437 public String getIsoDescription() throws MetadataException 438 { 439 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_ISO)) return null; 440 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_ISO); 521 @Nullable 522 public String getIsoDescription() 523 { 524 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_ISO); 525 if (value==null) 526 return null; 527 528 // Canon PowerShot S3 is special 529 int canonMask = 0x4000; 530 if ((value & canonMask) > 0) 531 return "" + (value & ~canonMask); 532 441 533 switch (value) { 442 534 case 0: … … 457 549 } 458 550 459 public String getSharpnessDescription() throws MetadataException 460 { 461 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SHARPNESS)) return null; 462 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SHARPNESS); 551 @Nullable 552 public String getSharpnessDescription() 553 { 554 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SHARPNESS); 555 if (value==null) 556 return null; 463 557 switch (value) { 464 558 case 0xFFFF: … … 473 567 } 474 568 475 public String getSaturationDescription() throws MetadataException 476 { 477 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SATURATION)) return null; 478 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SATURATION); 569 @Nullable 570 public String getSaturationDescription() 571 { 572 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SATURATION); 573 if (value==null) 574 return null; 479 575 switch (value) { 480 576 case 0xFFFF: … … 489 585 } 490 586 491 public String getContrastDescription() throws MetadataException 492 { 493 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTRAST)) return null; 494 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTRAST); 587 @Nullable 588 public String getContrastDescription() 589 { 590 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_CONTRAST); 591 if (value==null) 592 return null; 495 593 switch (value) { 496 594 case 0xFFFF: … … 505 603 } 506 604 507 public String getEasyShootingModeDescription() throws MetadataException 508 { 509 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_EASY_SHOOTING_MODE)) return null; 510 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_EASY_SHOOTING_MODE); 605 @Nullable 606 public String getEasyShootingModeDescription() 607 { 608 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_EASY_SHOOTING_MODE); 609 if (value==null) 610 return null; 511 611 switch (value) { 512 612 case 0: … … 539 639 } 540 640 541 public String getImageSizeDescription() throws MetadataException 542 { 543 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_IMAGE_SIZE)) return null; 544 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_IMAGE_SIZE); 641 @Nullable 642 public String getImageSizeDescription() 643 { 644 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_IMAGE_SIZE); 645 if (value==null) 646 return null; 545 647 switch (value) { 546 648 case 0: … … 555 657 } 556 658 557 public String getFocusMode1Description() throws MetadataException 558 { 559 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_1)) return null; 560 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_MODE_1); 659 @Nullable 660 public String getFocusMode1Description() 661 { 662 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_MODE_1); 663 if (value==null) 664 return null; 561 665 switch (value) { 562 666 case 0: … … 580 684 } 581 685 582 public String getContinuousDriveModeDescription() throws MetadataException583 {584 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE)) return null;585 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE);586 switch (value) {587 case 0:588 if (_directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SELF_TIMER_DELAY) == 0) {589 return "Single shot";590 } else {591 return "Single shot with self-timer";592 }686 @Nullable 687 public String getContinuousDriveModeDescription() 688 { 689 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_CONTINUOUS_DRIVE_MODE); 690 if (value==null) 691 return null; 692 switch (value) { 693 case 0: 694 final Integer delay = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SELF_TIMER_DELAY); 695 if (delay!=null) 696 return delay == 0 ? "Single shot" : "Single shot with self-timer"; 593 697 case 1: 594 698 return "Continuous"; 595 default: 596 return "Unknown (" + value + ")"; 597 } 598 } 599 600 public String getFlashModeDescription() throws MetadataException 601 { 602 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_MODE)) return null; 603 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_MODE); 699 } 700 return "Unknown (" + value + ")"; 701 } 702 703 @Nullable 704 public String getFlashModeDescription() 705 { 706 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FLASH_MODE); 707 if (value==null) 708 return null; 604 709 switch (value) { 605 710 case 0: … … 619 724 case 16: 620 725 // note: this value not set on Canon D30 621 return "Extenal flash"; 622 default: 623 return "Unknown (" + value + ")"; 624 } 625 } 626 627 public String getSelfTimerDelayDescription() throws MetadataException 628 { 629 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SELF_TIMER_DELAY)) return null; 630 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SELF_TIMER_DELAY); 726 return "External flash"; 727 default: 728 return "Unknown (" + value + ")"; 729 } 730 } 731 732 @Nullable 733 public String getSelfTimerDelayDescription() 734 { 735 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SELF_TIMER_DELAY); 736 if (value==null) 737 return null; 631 738 if (value == 0) { 632 739 return "Self timer not used"; … … 637 744 } 638 745 639 public String getMacroModeDescription() throws MetadataException 640 { 641 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_MACRO_MODE)) return null; 642 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_MACRO_MODE); 746 @Nullable 747 public String getMacroModeDescription() 748 { 749 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_MACRO_MODE); 750 if (value==null) 751 return null; 643 752 switch (value) { 644 753 case 1: … … 651 760 } 652 761 653 public String getQualityDescription() throws MetadataException 654 { 655 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_QUALITY)) return null; 656 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_QUALITY); 762 @Nullable 763 public String getQualityDescription() 764 { 765 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_QUALITY); 766 if (value==null) 767 return null; 657 768 switch (value) { 658 769 case 2: … … 667 778 } 668 779 669 public String getDigitalZoomDescription() throws MetadataException 670 { 671 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_DIGITAL_ZOOM)) return null; 672 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_DIGITAL_ZOOM); 780 @Nullable 781 public String getDigitalZoomDescription() 782 { 783 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_DIGITAL_ZOOM); 784 if (value==null) 785 return null; 673 786 switch (value) { 674 787 case 0: … … 683 796 } 684 797 685 public String getFocusTypeDescription() throws MetadataException 686 { 687 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_TYPE)) return null; 688 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_TYPE); 798 @Nullable 799 public String getFocusTypeDescription() 800 { 801 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FOCUS_TYPE); 802 if (value==null) 803 return null; 689 804 switch (value) { 690 805 case 0: … … 701 816 } 702 817 703 public String getFlashActivityDescription() throws MetadataException 704 { 705 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_ACTIVITY)) return null; 706 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_ACTIVITY); 818 @Nullable 819 public String getFlashActivityDescription() 820 { 821 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_FLASH_ACTIVITY); 822 if (value==null) 823 return null; 707 824 switch (value) { 708 825 case 0: -
trunk/src/com/drew/metadata/exif/CanonMakernoteDirectory.java
r4231 r6127 1 1 /* 2 * This is public domain software - that is, you can do whatever you want 3 * with it, and include it software that is licensed under the GNU or the 4 * BSD license, or whatever other licence you choose, including proprietary 5 * closed source licenses. I do ask that you leave this header in tact. 2 * Copyright 2002-2012 Drew Noakes 6 3 * 7 * If you make modifications to this code that you think would benefit the 8 * wider community, please send me a copy and I'll post it on my site. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 9 7 * 10 * If you make use of this code, I'd appreciate hearing about it. 11 * drew@drewnoakes.com 12 * Latest version of this software kept at 13 * http://drewnoakes.com/ 8 * http://www.apache.org/licenses/LICENSE-2.0 14 9 * 15 * Created by dnoakes on 27-Nov-2002 10:10:47 using IntelliJ IDEA. 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * More information about this project is available at: 17 * 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 16 20 */ 17 21 package com.drew.metadata.exif; 18 22 23 import com.drew.lang.annotations.NotNull; 19 24 import com.drew.metadata.Directory; 20 25 … … 27 32 * 28 33 * Many tag definitions explained here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/canon_mn.html 34 * 35 * @author Drew Noakes http://drewnoakes.com 29 36 */ 30 37 public class CanonMakernoteDirectory extends Directory 31 38 { 32 // CANON cameras have some funny bespoke fields that need further processing... 33 public static final int TAG_CANON_CAMERA_STATE_1 = 0x0001; 34 public static final int TAG_CANON_CAMERA_STATE_2 = 0x0004; 35 36 public static final int TAG_CANON_IMAGE_TYPE = 0x0006; 37 public static final int TAG_CANON_FIRMWARE_VERSION = 0x0007; 38 public static final int TAG_CANON_IMAGE_NUMBER = 0x0008; 39 public static final int TAG_CANON_OWNER_NAME = 0x0009; 40 /** 41 * To display serial number as on camera use: printf( "%04X%05d", highbyte, lowbyte ) 42 * TODO handle this in CanonMakernoteDescriptor 43 */ 44 public static final int TAG_CANON_SERIAL_NUMBER = 0x000C; 45 public static final int TAG_CANON_UNKNOWN_1 = 0x000D; 46 public static final int TAG_CANON_CUSTOM_FUNCTIONS = 0x000F; 47 48 // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment 49 /** 50 * 1 = Macro 51 * 2 = Normal 52 */ 53 public static final int TAG_CANON_STATE1_MACRO_MODE = 0xC101; 54 public static final int TAG_CANON_STATE1_SELF_TIMER_DELAY = 0xC102; 55 /** 56 * 2 = Normal 57 * 3 = Fine 58 * 5 = Superfine 59 */ 60 public static final int TAG_CANON_STATE1_QUALITY = 0xC103; 61 /** 62 * 0 = Flash Not Fired 63 * 1 = Auto 64 * 2 = On 65 * 3 = Red Eye Reduction 66 * 4 = Slow Synchro 67 * 5 = Auto + Red Eye Reduction 68 * 6 = On + Red Eye Reduction 69 * 16 = External Flash 70 */ 71 public static final int TAG_CANON_STATE1_FLASH_MODE = 0xC104; 72 /** 73 * 0 = Single Frame or Timer Mode 74 * 1 = Continuous 75 */ 76 public static final int TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE = 0xC105; 77 public static final int TAG_CANON_STATE1_UNKNOWN_2 = 0xC106; 78 /** 79 * 0 = One-Shot 80 * 1 = AI Servo 81 * 2 = AI Focus 82 * 3 = Manual Focus 83 * 4 = Single 84 * 5 = Continuous 85 * 6 = Manual Focus 86 */ 87 public static final int TAG_CANON_STATE1_FOCUS_MODE_1 = 0xC107; 88 public static final int TAG_CANON_STATE1_UNKNOWN_3 = 0xC108; 89 public static final int TAG_CANON_STATE1_UNKNOWN_4 = 0xC109; 90 /** 91 * 0 = Large 92 * 1 = Medium 93 * 2 = Small 94 */ 95 public static final int TAG_CANON_STATE1_IMAGE_SIZE = 0xC10A; 96 /** 97 * 0 = Full Auto 98 * 1 = Manual 99 * 2 = Landscape 100 * 3 = Fast Shutter 101 * 4 = Slow Shutter 102 * 5 = Night 103 * 6 = Black & White 104 * 7 = Sepia 105 * 8 = Portrait 106 * 9 = Sports 107 * 10 = Macro / Close-Up 108 * 11 = Pan Focus 109 */ 110 public static final int TAG_CANON_STATE1_EASY_SHOOTING_MODE = 0xC10B; 111 /** 112 * 0 = No Digital Zoom 113 * 1 = 2x 114 * 2 = 4x 115 */ 116 public static final int TAG_CANON_STATE1_DIGITAL_ZOOM = 0xC10C; 117 /** 118 * 0 = Normal 119 * 1 = High 120 * 65535 = Low 121 */ 122 public static final int TAG_CANON_STATE1_CONTRAST = 0xC10D; 123 /** 124 * 0 = Normal 125 * 1 = High 126 * 65535 = Low 127 */ 128 public static final int TAG_CANON_STATE1_SATURATION = 0xC10E; 129 /** 130 * 0 = Normal 131 * 1 = High 132 * 65535 = Low 133 */ 134 public static final int TAG_CANON_STATE1_SHARPNESS = 0xC10F; 135 /** 136 * 0 = Check ISOSpeedRatings EXIF tag for ISO Speed 137 * 15 = Auto ISO 138 * 16 = ISO 50 139 * 17 = ISO 100 140 * 18 = ISO 200 141 * 19 = ISO 400 142 */ 143 public static final int TAG_CANON_STATE1_ISO = 0xC110; 144 /** 145 * 3 = Evaluative 146 * 4 = Partial 147 * 5 = Centre Weighted 148 */ 149 public static final int TAG_CANON_STATE1_METERING_MODE = 0xC111; 150 /** 151 * 0 = Manual 152 * 1 = Auto 153 * 3 = Close-up (Macro) 154 * 8 = Locked (Pan Mode) 155 */ 156 public static final int TAG_CANON_STATE1_FOCUS_TYPE = 0xC112; 157 /** 158 * 12288 = None (Manual Focus) 159 * 12289 = Auto Selected 160 * 12290 = Right 161 * 12291 = Centre 162 * 12292 = Left 163 */ 164 public static final int TAG_CANON_STATE1_AF_POINT_SELECTED = 0xC113; 165 /** 166 * 0 = Easy Shooting (See Easy Shooting Mode) 167 * 1 = Program 168 * 2 = Tv-Priority 169 * 3 = Av-Priority 170 * 4 = Manual 171 * 5 = A-DEP 172 */ 173 public static final int TAG_CANON_STATE1_EXPOSURE_MODE = 0xC114; 174 public static final int TAG_CANON_STATE1_UNKNOWN_7 = 0xC115; 175 public static final int TAG_CANON_STATE1_UNKNOWN_8 = 0xC116; 176 public static final int TAG_CANON_STATE1_LONG_FOCAL_LENGTH = 0xC117; 177 public static final int TAG_CANON_STATE1_SHORT_FOCAL_LENGTH = 0xC118; 178 public static final int TAG_CANON_STATE1_FOCAL_UNITS_PER_MM = 0xC119; 179 public static final int TAG_CANON_STATE1_UNKNOWN_9 = 0xC11A; 180 public static final int TAG_CANON_STATE1_UNKNOWN_10 = 0xC11B; 181 /** 182 * 0 = Flash Did Not Fire 183 * 1 = Flash Fired 184 */ 185 public static final int TAG_CANON_STATE1_FLASH_ACTIVITY = 0xC11C; 186 public static final int TAG_CANON_STATE1_FLASH_DETAILS = 0xC11D; 187 public static final int TAG_CANON_STATE1_UNKNOWN_12 = 0xC11E; 188 public static final int TAG_CANON_STATE1_UNKNOWN_13 = 0xC11F; 189 /** 190 * 0 = Focus Mode: Single 191 * 1 = Focus Mode: Continuous 192 */ 193 public static final int TAG_CANON_STATE1_FOCUS_MODE_2 = 0xC120; 194 195 /** 196 * 0 = Auto 197 * 1 = Sunny 198 * 2 = Cloudy 199 * 3 = Tungsten 200 * 4 = Flourescent 201 * 5 = Flash 202 * 6 = Custom 203 */ 204 public static final int TAG_CANON_STATE2_WHITE_BALANCE = 0xC207; 205 public static final int TAG_CANON_STATE2_SEQUENCE_NUMBER = 0xC209; 206 public static final int TAG_CANON_STATE2_AF_POINT_USED = 0xC20E; 207 /** 208 * The value of this tag may be translated into a flash bias value, in EV. 209 * 210 * 0xffc0 = -2 EV 211 * 0xffcc = -1.67 EV 212 * 0xffd0 = -1.5 EV 213 * 0xffd4 = -1.33 EV 214 * 0xffe0 = -1 EV 215 * 0xffec = -0.67 EV 216 * 0xfff0 = -0.5 EV 217 * 0xfff4 = -0.33 EV 218 * 0x0000 = 0 EV 219 * 0x000c = 0.33 EV 220 * 0x0010 = 0.5 EV 221 * 0x0014 = 0.67 EV 222 * 0x0020 = 1 EV 223 * 0x002c = 1.33 EV 224 * 0x0030 = 1.5 EV 225 * 0x0034 = 1.67 EV 226 * 0x0040 = 2 EV 227 */ 228 public static final int TAG_CANON_STATE2_FLASH_BIAS = 0xC20F; 229 public static final int TAG_CANON_STATE2_AUTO_EXPOSURE_BRACKETING = 0xC210; 230 public static final int TAG_CANON_STATE2_AEB_BRACKET_VALUE = 0xC211; 231 public static final int TAG_CANON_STATE2_SUBJECT_DISTANCE = 0xC213; 232 233 /** 234 * Long Exposure Noise Reduction 235 * 0 = Off 236 * 1 = On 237 */ 238 public static final int TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION = 0xC301; 239 240 /** 241 * Shutter/Auto Exposure-lock buttons 242 * 0 = AF/AE lock 243 * 1 = AE lock/AF 244 * 2 = AF/AF lock 245 * 3 = AE+release/AE+AF 246 */ 247 public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS = 0xC302; 248 249 /** 250 * Mirror lockup 251 * 0 = Disable 252 * 1 = Enable 253 */ 254 public static final int TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP = 0xC303; 255 256 /** 257 * Tv/Av and exposure level 258 * 0 = 1/2 stop 259 * 1 = 1/3 stop 260 */ 261 public static final int TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL = 0xC304; 262 263 /** 264 * AF-assist light 265 * 0 = On (Auto) 266 * 1 = Off 267 */ 268 public static final int TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT = 0xC305; 269 270 /** 271 * Shutter speed in Av mode 272 * 0 = Automatic 273 * 1 = 1/200 (fixed) 274 */ 275 public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE = 0xC306; 276 277 /** 278 * Auto-Exposure Bracketting sequence/auto cancellation 279 * 0 = 0,-,+ / Enabled 280 * 1 = 0,-,+ / Disabled 281 * 2 = -,0,+ / Enabled 282 * 3 = -,0,+ / Disabled 283 */ 284 public static final int TAG_CANON_CUSTOM_FUNCTION_BRACKETTING = 0xC307; 285 286 /** 287 * Shutter Curtain Sync 288 * 0 = 1st Curtain Sync 289 * 1 = 2nd Curtain Sync 290 */ 291 public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC = 0xC308; 292 293 /** 294 * Lens Auto-Focus stop button Function Switch 295 * 0 = AF stop 296 * 1 = Operate AF 297 * 2 = Lock AE and start timer 298 */ 299 public static final int TAG_CANON_CUSTOM_FUNCTION_AF_STOP = 0xC309; 300 301 /** 302 * Auto reduction of fill flash 303 * 0 = Enable 304 * 1 = Disable 305 */ 306 public static final int TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION = 0xC30A; 307 308 /** 309 * Menu button return position 310 * 0 = Top 311 * 1 = Previous (volatile) 312 * 2 = Previous 313 */ 314 public static final int TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN = 0xC30B; 315 316 /** 317 * SET button function when shooting 318 * 0 = Not Assigned 319 * 1 = Change Quality 320 * 2 = Change ISO Speed 321 * 3 = Select Parameters 322 */ 323 public static final int TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION = 0xC30C; 324 325 /** 326 * Sensor cleaning 327 * 0 = Disable 328 * 1 = Enable 329 */ 330 public static final int TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING = 0xC30D; 39 // These TAG_*_ARRAY Exif tags map to arrays of int16 values which are split out into separate 'fake' tags. 40 // When an attempt is made to set one of these on the directory, it is split and the corresponding offset added to the tagType. 41 // So the resulting tag is the offset + the index into the array. 42 43 private static final int TAG_CAMERA_SETTINGS_ARRAY = 0x0001; 44 private static final int TAG_FOCAL_LENGTH_ARRAY = 0x0002; 45 private static final int TAG_SHOT_INFO_ARRAY = 0x0004; 46 private static final int TAG_PANORAMA_ARRAY = 0x0005; 47 48 public static final int TAG_CANON_IMAGE_TYPE = 0x0006; 49 public static final int TAG_CANON_FIRMWARE_VERSION = 0x0007; 50 public static final int TAG_CANON_IMAGE_NUMBER = 0x0008; 51 public static final int TAG_CANON_OWNER_NAME = 0x0009; 52 public static final int TAG_CANON_SERIAL_NUMBER = 0x000C; 53 public static final int TAG_CAMERA_INFO_ARRAY = 0x000D; // depends upon model, so leave for now 54 public static final int TAG_CANON_FILE_LENGTH = 0x000E; 55 public static final int TAG_CANON_CUSTOM_FUNCTIONS_ARRAY = 0x000F; // depends upon model, so leave for now 56 public static final int TAG_MODEL_ID = 0x0010; 57 public static final int TAG_MOVIE_INFO_ARRAY = 0x0011; // not currently decoded as not sure we see it in still images 58 private static final int TAG_AF_INFO_ARRAY = 0x0012; // not currently decoded 59 public static final int TAG_THUMBNAIL_IMAGE_VALID_AREA = 0x0013; 60 public static final int TAG_SERIAL_NUMBER_FORMAT = 0x0015; 61 public static final int TAG_SUPER_MACRO = 0x001A; 62 public static final int TAG_DATE_STAMP_MODE = 0x001C; 63 public static final int TAG_MY_COLORS = 0x001D; 64 public static final int TAG_FIRMWARE_REVISION = 0x001E; 65 public static final int TAG_CATEGORIES = 0x0023; 66 public static final int TAG_FACE_DETECT_ARRAY_1 = 0x0024; 67 public static final int TAG_FACE_DETECT_ARRAY_2 = 0x0025; 68 public static final int TAG_AF_INFO_ARRAY_2 = 0x0026; 69 public static final int TAG_IMAGE_UNIQUE_ID = 0x0028; 70 public static final int TAG_RAW_DATA_OFFSET = 0x0081; 71 public static final int TAG_ORIGINAL_DECISION_DATA_OFFSET = 0x0083; 72 public static final int TAG_CUSTOM_FUNCTIONS_1D_ARRAY = 0x0090; // not currently decoded 73 public static final int TAG_PERSONAL_FUNCTIONS_ARRAY = 0x0091; // not currently decoded 74 public static final int TAG_PERSONAL_FUNCTION_VALUES_ARRAY = 0x0092; // not currently decoded 75 public static final int TAG_FILE_INFO_ARRAY = 0x0093; // not currently decoded 76 public static final int TAG_AF_POINTS_IN_FOCUS_1D = 0x0094; 77 public static final int TAG_LENS_MODEL = 0x0095; 78 public static final int TAG_SERIAL_INFO_ARRAY = 0x0096; // not currently decoded 79 public static final int TAG_DUST_REMOVAL_DATA = 0x0097; 80 public static final int TAG_CROP_INFO = 0x0098; // not currently decoded 81 public static final int TAG_CUSTOM_FUNCTIONS_ARRAY_2 = 0x0099; // not currently decoded 82 public static final int TAG_ASPECT_INFO_ARRAY = 0x009A; // not currently decoded 83 public static final int TAG_PROCESSING_INFO_ARRAY = 0x00A0; // not currently decoded 84 public static final int TAG_TONE_CURVE_TABLE = 0x00A1; 85 public static final int TAG_SHARPNESS_TABLE = 0x00A2; 86 public static final int TAG_SHARPNESS_FREQ_TABLE = 0x00A3; 87 public static final int TAG_WHITE_BALANCE_TABLE = 0x00A4; 88 public static final int TAG_COLOR_BALANCE_ARRAY = 0x00A9; // not currently decoded 89 public static final int TAG_MEASURED_COLOR_ARRAY = 0x00AA; // not currently decoded 90 public static final int TAG_COLOR_TEMPERATURE = 0x00AE; 91 public static final int TAG_CANON_FLAGS_ARRAY = 0x00B0; // not currently decoded 92 public static final int TAG_MODIFIED_INFO_ARRAY = 0x00B1; // not currently decoded 93 public static final int TAG_TONE_CURVE_MATCHING = 0x00B2; 94 public static final int TAG_WHITE_BALANCE_MATCHING = 0x00B3; 95 public static final int TAG_COLOR_SPACE = 0x00B4; 96 public static final int TAG_PREVIEW_IMAGE_INFO_ARRAY = 0x00B6; // not currently decoded 97 public static final int TAG_VRD_OFFSET = 0x00D0; 98 public static final int TAG_SENSOR_INFO_ARRAY = 0x00E0; // not currently decoded 99 public static final int TAG_COLOR_DATA_ARRAY_2 = 0x4001; // depends upon camera model, not currently decoded 100 public static final int TAG_COLOR_INFO_ARRAY_2 = 0x4003; // not currently decoded 101 public static final int TAG_CUSTOM_PICTURE_STYLE_FILE_NAME = 0x4010; 102 public static final int TAG_COLOR_INFO_ARRAY = 0x4013; // not currently decoded 103 public static final int TAG_VIGNETTING_CORRECTION_ARRAY_1 = 0x4015; // not currently decoded 104 public static final int TAG_VIGNETTING_CORRECTION_ARRAY_2 = 0x4016; // not currently decoded 105 public static final int TAG_LIGHTING_OPTIMIZER_ARRAY = 0x4018; // not currently decoded 106 public static final int TAG_LENS_INFO_ARRAY = 0x4019; // not currently decoded 107 public static final int TAG_AMBIANCE_INFO_ARRAY = 0x4020; // not currently decoded 108 public static final int TAG_FILTER_INFO_ARRAY = 0x4024; // not currently decoded 109 110 public final static class CameraSettings 111 { 112 // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment 113 private static final int OFFSET = 0xC100; 114 115 /** 116 * 1 = Macro 117 * 2 = Normal 118 */ 119 public static final int TAG_MACRO_MODE = OFFSET + 0x01; 120 public static final int TAG_SELF_TIMER_DELAY = OFFSET + 0x02; 121 /** 122 * 2 = Normal 123 * 3 = Fine 124 * 5 = Superfine 125 */ 126 public static final int TAG_QUALITY = OFFSET + 0x03; 127 /** 128 * 0 = Flash Not Fired 129 * 1 = Auto 130 * 2 = On 131 * 3 = Red Eye Reduction 132 * 4 = Slow Synchro 133 * 5 = Auto + Red Eye Reduction 134 * 6 = On + Red Eye Reduction 135 * 16 = External Flash 136 */ 137 public static final int TAG_FLASH_MODE = OFFSET + 0x04; 138 /** 139 * 0 = Single Frame or Timer Mode 140 * 1 = Continuous 141 */ 142 public static final int TAG_CONTINUOUS_DRIVE_MODE = OFFSET + 0x05; 143 public static final int TAG_UNKNOWN_2 = OFFSET + 0x06; 144 /** 145 * 0 = One-Shot 146 * 1 = AI Servo 147 * 2 = AI Focus 148 * 3 = Manual Focus 149 * 4 = Single 150 * 5 = Continuous 151 * 6 = Manual Focus 152 */ 153 public static final int TAG_FOCUS_MODE_1 = OFFSET + 0x07; 154 public static final int TAG_UNKNOWN_3 = OFFSET + 0x08; 155 public static final int TAG_UNKNOWN_4 = OFFSET + 0x09; 156 /** 157 * 0 = Large 158 * 1 = Medium 159 * 2 = Small 160 */ 161 public static final int TAG_IMAGE_SIZE = OFFSET + 0x0A; 162 /** 163 * 0 = Full Auto 164 * 1 = Manual 165 * 2 = Landscape 166 * 3 = Fast Shutter 167 * 4 = Slow Shutter 168 * 5 = Night 169 * 6 = Black & White 170 * 7 = Sepia 171 * 8 = Portrait 172 * 9 = Sports 173 * 10 = Macro / Close-Up 174 * 11 = Pan Focus 175 */ 176 public static final int TAG_EASY_SHOOTING_MODE = OFFSET + 0x0B; 177 /** 178 * 0 = No Digital Zoom 179 * 1 = 2x 180 * 2 = 4x 181 */ 182 public static final int TAG_DIGITAL_ZOOM = OFFSET + 0x0C; 183 /** 184 * 0 = Normal 185 * 1 = High 186 * 65535 = Low 187 */ 188 public static final int TAG_CONTRAST = OFFSET + 0x0D; 189 /** 190 * 0 = Normal 191 * 1 = High 192 * 65535 = Low 193 */ 194 public static final int TAG_SATURATION = OFFSET + 0x0E; 195 /** 196 * 0 = Normal 197 * 1 = High 198 * 65535 = Low 199 */ 200 public static final int TAG_SHARPNESS = OFFSET + 0x0F; 201 /** 202 * 0 = Check ISOSpeedRatings EXIF tag for ISO Speed 203 * 15 = Auto ISO 204 * 16 = ISO 50 205 * 17 = ISO 100 206 * 18 = ISO 200 207 * 19 = ISO 400 208 */ 209 public static final int TAG_ISO = OFFSET + 0x10; 210 /** 211 * 3 = Evaluative 212 * 4 = Partial 213 * 5 = Centre Weighted 214 */ 215 public static final int TAG_METERING_MODE = OFFSET + 0x11; 216 /** 217 * 0 = Manual 218 * 1 = Auto 219 * 3 = Close-up (Macro) 220 * 8 = Locked (Pan Mode) 221 */ 222 public static final int TAG_FOCUS_TYPE = OFFSET + 0x12; 223 /** 224 * 12288 = None (Manual Focus) 225 * 12289 = Auto Selected 226 * 12290 = Right 227 * 12291 = Centre 228 * 12292 = Left 229 */ 230 public static final int TAG_AF_POINT_SELECTED = OFFSET + 0x13; 231 /** 232 * 0 = Easy Shooting (See Easy Shooting Mode) 233 * 1 = Program 234 * 2 = Tv-Priority 235 * 3 = Av-Priority 236 * 4 = Manual 237 * 5 = A-DEP 238 */ 239 public static final int TAG_EXPOSURE_MODE = OFFSET + 0x14; 240 public static final int TAG_UNKNOWN_7 = OFFSET + 0x15; 241 public static final int TAG_UNKNOWN_8 = OFFSET + 0x16; 242 public static final int TAG_LONG_FOCAL_LENGTH = OFFSET + 0x17; 243 public static final int TAG_SHORT_FOCAL_LENGTH = OFFSET + 0x18; 244 public static final int TAG_FOCAL_UNITS_PER_MM = OFFSET + 0x19; 245 public static final int TAG_UNKNOWN_9 = OFFSET + 0x1A; 246 public static final int TAG_UNKNOWN_10 = OFFSET + 0x1B; 247 /** 248 * 0 = Flash Did Not Fire 249 * 1 = Flash Fired 250 */ 251 public static final int TAG_FLASH_ACTIVITY = OFFSET + 0x1C; 252 public static final int TAG_FLASH_DETAILS = OFFSET + 0x1D; 253 public static final int TAG_UNKNOWN_12 = OFFSET + 0x1E; 254 public static final int TAG_UNKNOWN_13 = OFFSET + 0x1F; 255 /** 256 * 0 = Focus Mode: Single 257 * 1 = Focus Mode: Continuous 258 */ 259 public static final int TAG_FOCUS_MODE_2 = OFFSET + 0x20; 260 } 261 262 public final static class FocalLength 263 { 264 // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment 265 266 private static final int OFFSET = 0xC200; 267 268 /** 269 * 0 = Auto 270 * 1 = Sunny 271 * 2 = Cloudy 272 * 3 = Tungsten 273 * 4 = Florescent 274 * 5 = Flash 275 * 6 = Custom 276 */ 277 public static final int TAG_WHITE_BALANCE = OFFSET + 0x07; 278 public static final int TAG_SEQUENCE_NUMBER = OFFSET + 0x09; 279 public static final int TAG_AF_POINT_USED = OFFSET + 0x0E; 280 /** 281 * The value of this tag may be translated into a flash bias value, in EV. 282 * 283 * 0xffc0 = -2 EV 284 * 0xffcc = -1.67 EV 285 * 0xffd0 = -1.5 EV 286 * 0xffd4 = -1.33 EV 287 * 0xffe0 = -1 EV 288 * 0xffec = -0.67 EV 289 * 0xfff0 = -0.5 EV 290 * 0xfff4 = -0.33 EV 291 * 0x0000 = 0 EV 292 * 0x000c = 0.33 EV 293 * 0x0010 = 0.5 EV 294 * 0x0014 = 0.67 EV 295 * 0x0020 = 1 EV 296 * 0x002c = 1.33 EV 297 * 0x0030 = 1.5 EV 298 * 0x0034 = 1.67 EV 299 * 0x0040 = 2 EV 300 */ 301 public static final int TAG_FLASH_BIAS = OFFSET + 0x0F; 302 public static final int TAG_AUTO_EXPOSURE_BRACKETING = OFFSET + 0x10; 303 public static final int TAG_AEB_BRACKET_VALUE = OFFSET + 0x11; 304 public static final int TAG_SUBJECT_DISTANCE = OFFSET + 0x13; 305 } 306 307 public final static class ShotInfo 308 { 309 // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment 310 311 private static final int OFFSET = 0xC400; 312 313 public static final int TAG_AUTO_ISO = OFFSET + 1; 314 public static final int TAG_BASE_ISO = OFFSET + 2; 315 public static final int TAG_MEASURED_EV = OFFSET + 3; 316 public static final int TAG_TARGET_APERTURE = OFFSET + 4; 317 public static final int TAG_TARGET_EXPOSURE_TIME = OFFSET + 5; 318 public static final int TAG_EXPOSURE_COMPENSATION = OFFSET + 6; 319 public static final int TAG_WHITE_BALANCE = OFFSET + 7; 320 public static final int TAG_SLOW_SHUTTER = OFFSET + 8; 321 public static final int TAG_SEQUENCE_NUMBER = OFFSET + 9; 322 public static final int TAG_OPTICAL_ZOOM_CODE = OFFSET + 10; 323 public static final int TAG_CAMERA_TEMPERATURE = OFFSET + 12; 324 public static final int TAG_FLASH_GUIDE_NUMBER = OFFSET + 13; 325 public static final int TAG_AF_POINTS_IN_FOCUS = OFFSET + 14; 326 public static final int TAG_FLASH_EXPOSURE_BRACKETING = OFFSET + 15; 327 public static final int TAG_AUTO_EXPOSURE_BRACKETING = OFFSET + 16; 328 public static final int TAG_AEB_BRACKET_VALUE = OFFSET + 17; 329 public static final int TAG_CONTROL_MODE = OFFSET + 18; 330 public static final int TAG_FOCUS_DISTANCE_UPPER = OFFSET + 19; 331 public static final int TAG_FOCUS_DISTANCE_LOWER = OFFSET + 20; 332 public static final int TAG_F_NUMBER = OFFSET + 21; 333 public static final int TAG_EXPOSURE_TIME = OFFSET + 22; 334 public static final int TAG_MEASURED_EV_2 = OFFSET + 23; 335 public static final int TAG_BULB_DURATION = OFFSET + 24; 336 public static final int TAG_CAMERA_TYPE = OFFSET + 26; 337 public static final int TAG_AUTO_ROTATE = OFFSET + 27; 338 public static final int TAG_ND_FILTER = OFFSET + 28; 339 public static final int TAG_SELF_TIMER_2 = OFFSET + 29; 340 public static final int TAG_FLASH_OUTPUT = OFFSET + 33; 341 } 342 343 public final static class Panorama 344 { 345 // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment 346 347 private static final int OFFSET = 0xC500; 348 349 public static final int TAG_PANORAMA_FRAME_NUMBER = OFFSET + 2; 350 public static final int TAG_PANORAMA_DIRECTION = OFFSET + 5; 351 } 352 353 public final static class AFInfo 354 { 355 // These 'sub'-tag values have been created for consistency -- they don't exist within the exif segment 356 357 private static final int OFFSET = 0xD200; 358 359 public static final int TAG_NUM_AF_POINTS = OFFSET; 360 public static final int TAG_VALID_AF_POINTS = OFFSET + 1; 361 public static final int TAG_IMAGE_WIDTH = OFFSET + 2; 362 public static final int TAG_IMAGE_HEIGHT = OFFSET + 3; 363 public static final int TAG_AF_IMAGE_WIDTH = OFFSET + 4; 364 public static final int TAG_AF_IMAGE_HEIGHT = OFFSET + 5; 365 public static final int TAG_AF_AREA_WIDTH = OFFSET + 6; 366 public static final int TAG_AF_AREA_HEIGHT = OFFSET + 7; 367 public static final int TAG_AF_AREA_X_POSITIONS = OFFSET + 8; 368 public static final int TAG_AF_AREA_Y_POSITIONS = OFFSET + 9; 369 public static final int TAG_AF_POINTS_IN_FOCUS = OFFSET + 10; 370 public static final int TAG_PRIMARY_AF_POINT_1 = OFFSET + 11; 371 public static final int TAG_PRIMARY_AF_POINT_2 = OFFSET + 12; // not sure why there are two of these 372 } 373 374 // /** 375 // * Long Exposure Noise Reduction 376 // * 0 = Off 377 // * 1 = On 378 // */ 379 // public static final int TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION = 0xC301; 380 // 381 // /** 382 // * Shutter/Auto Exposure-lock buttons 383 // * 0 = AF/AE lock 384 // * 1 = AE lock/AF 385 // * 2 = AF/AF lock 386 // * 3 = AE+release/AE+AF 387 // */ 388 // public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS = 0xC302; 389 // 390 // /** 391 // * Mirror lockup 392 // * 0 = Disable 393 // * 1 = Enable 394 // */ 395 // public static final int TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP = 0xC303; 396 // 397 // /** 398 // * Tv/Av and exposure level 399 // * 0 = 1/2 stop 400 // * 1 = 1/3 stop 401 // */ 402 // public static final int TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL = 0xC304; 403 // 404 // /** 405 // * AF-assist light 406 // * 0 = On (Auto) 407 // * 1 = Off 408 // */ 409 // public static final int TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT = 0xC305; 410 // 411 // /** 412 // * Shutter speed in Av mode 413 // * 0 = Automatic 414 // * 1 = 1/200 (fixed) 415 // */ 416 // public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE = 0xC306; 417 // 418 // /** 419 // * Auto-Exposure Bracketting sequence/auto cancellation 420 // * 0 = 0,-,+ / Enabled 421 // * 1 = 0,-,+ / Disabled 422 // * 2 = -,0,+ / Enabled 423 // * 3 = -,0,+ / Disabled 424 // */ 425 // public static final int TAG_CANON_CUSTOM_FUNCTION_BRACKETTING = 0xC307; 426 // 427 // /** 428 // * Shutter Curtain Sync 429 // * 0 = 1st Curtain Sync 430 // * 1 = 2nd Curtain Sync 431 // */ 432 // public static final int TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC = 0xC308; 433 // 434 // /** 435 // * Lens Auto-Focus stop button Function Switch 436 // * 0 = AF stop 437 // * 1 = Operate AF 438 // * 2 = Lock AE and start timer 439 // */ 440 // public static final int TAG_CANON_CUSTOM_FUNCTION_AF_STOP = 0xC309; 441 // 442 // /** 443 // * Auto reduction of fill flash 444 // * 0 = Enable 445 // * 1 = Disable 446 // */ 447 // public static final int TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION = 0xC30A; 448 // 449 // /** 450 // * Menu button return position 451 // * 0 = Top 452 // * 1 = Previous (volatile) 453 // * 2 = Previous 454 // */ 455 // public static final int TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN = 0xC30B; 456 // 457 // /** 458 // * SET button function when shooting 459 // * 0 = Not Assigned 460 // * 1 = Change Quality 461 // * 2 = Change ISO Speed 462 // * 3 = Select Parameters 463 // */ 464 // public static final int TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION = 0xC30C; 465 // 466 // /** 467 // * Sensor cleaning 468 // * 0 = Disable 469 // * 1 = Enable 470 // */ 471 // public static final int TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING = 0xC30D; 331 472 332 473 // 9 A B C D E F 10 11 12 13 333 474 // 9 10 11 12 13 14 15 16 17 18 19 334 protected static final HashMap _tagNameMap = new HashMap(); 475 @NotNull 476 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 335 477 336 478 static 337 479 { 338 _tagNameMap.put(new Integer(TAG_CANON_FIRMWARE_VERSION), "Firmware Version"); 339 _tagNameMap.put(new Integer(TAG_CANON_IMAGE_NUMBER), "Image Number"); 340 _tagNameMap.put(new Integer(TAG_CANON_IMAGE_TYPE), "Image Type"); 341 _tagNameMap.put(new Integer(TAG_CANON_OWNER_NAME), "Owner Name"); 342 _tagNameMap.put(new Integer(TAG_CANON_UNKNOWN_1), "Makernote Unknown 1"); 343 _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTIONS), "Custom Functions"); 344 _tagNameMap.put(new Integer(TAG_CANON_SERIAL_NUMBER), "Camera Serial Number"); 345 _tagNameMap.put(new Integer(TAG_CANON_STATE1_AF_POINT_SELECTED), "AF Point Selected"); 346 _tagNameMap.put(new Integer(TAG_CANON_STATE1_CONTINUOUS_DRIVE_MODE), "Continuous Drive Mode"); 347 _tagNameMap.put(new Integer(TAG_CANON_STATE1_CONTRAST), "Contrast"); 348 _tagNameMap.put(new Integer(TAG_CANON_STATE1_EASY_SHOOTING_MODE), "Easy Shooting Mode"); 349 _tagNameMap.put(new Integer(TAG_CANON_STATE1_EXPOSURE_MODE), "Exposure Mode"); 350 _tagNameMap.put(new Integer(TAG_CANON_STATE1_FLASH_DETAILS), "Flash Details"); 351 _tagNameMap.put(new Integer(TAG_CANON_STATE1_FLASH_MODE), "Flash Mode"); 352 _tagNameMap.put(new Integer(TAG_CANON_STATE1_FOCAL_UNITS_PER_MM), "Focal Units per mm"); 353 _tagNameMap.put(new Integer(TAG_CANON_STATE1_FOCUS_MODE_1), "Focus Mode"); 354 _tagNameMap.put(new Integer(TAG_CANON_STATE1_FOCUS_MODE_2), "Focus Mode"); 355 _tagNameMap.put(new Integer(TAG_CANON_STATE1_IMAGE_SIZE), "Image Size"); 356 _tagNameMap.put(new Integer(TAG_CANON_STATE1_ISO), "Iso"); 357 _tagNameMap.put(new Integer(TAG_CANON_STATE1_LONG_FOCAL_LENGTH), "Long Focal Length"); 358 _tagNameMap.put(new Integer(TAG_CANON_STATE1_MACRO_MODE), "Macro Mode"); 359 _tagNameMap.put(new Integer(TAG_CANON_STATE1_METERING_MODE), "Metering Mode"); 360 _tagNameMap.put(new Integer(TAG_CANON_STATE1_SATURATION), "Saturation"); 361 _tagNameMap.put(new Integer(TAG_CANON_STATE1_SELF_TIMER_DELAY), "Self Timer Delay"); 362 _tagNameMap.put(new Integer(TAG_CANON_STATE1_SHARPNESS), "Sharpness"); 363 _tagNameMap.put(new Integer(TAG_CANON_STATE1_SHORT_FOCAL_LENGTH), "Short Focal Length"); 364 _tagNameMap.put(new Integer(TAG_CANON_STATE1_QUALITY), "Quality"); 365 _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_2), "Unknown Camera State 2"); 366 _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_3), "Unknown Camera State 3"); 367 _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_4), "Unknown Camera State 4"); 368 _tagNameMap.put(new Integer(TAG_CANON_STATE1_DIGITAL_ZOOM), "Digital Zoom"); 369 _tagNameMap.put(new Integer(TAG_CANON_STATE1_FOCUS_TYPE), "Focus Type"); 370 _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_7), "Unknown Camera State 7"); 371 _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_8), "Unknown Camera State 8"); 372 _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_9), "Unknown Camera State 9"); 373 _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_10), "Unknown Camera State 10"); 374 _tagNameMap.put(new Integer(TAG_CANON_STATE1_FLASH_ACTIVITY), "Flash Activity"); 375 _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_12), "Unknown Camera State 12"); 376 _tagNameMap.put(new Integer(TAG_CANON_STATE1_UNKNOWN_13), "Unknown Camera State 13"); 377 _tagNameMap.put(new Integer(TAG_CANON_STATE2_WHITE_BALANCE), "White Balance"); 378 _tagNameMap.put(new Integer(TAG_CANON_STATE2_SEQUENCE_NUMBER), "Sequence Number"); 379 _tagNameMap.put(new Integer(TAG_CANON_STATE2_AF_POINT_USED), "AF Point Used"); 380 _tagNameMap.put(new Integer(TAG_CANON_STATE2_FLASH_BIAS), "Flash Bias"); 381 _tagNameMap.put(new Integer(TAG_CANON_STATE2_AUTO_EXPOSURE_BRACKETING), "Auto Exposure Bracketing"); 382 _tagNameMap.put(new Integer(TAG_CANON_STATE2_AEB_BRACKET_VALUE), "AEB Bracket Value"); 383 _tagNameMap.put(new Integer(TAG_CANON_STATE2_SUBJECT_DISTANCE), "Subject Distance"); 384 385 _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION), "Long Exposure Noise Reduction"); 386 _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS), "Shutter/Auto Exposure-lock Buttons"); 387 _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP), "Mirror Lockup"); 388 _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL), "Tv/Av And Exposure Level"); 389 _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT), "AF-Assist Light"); 390 _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE), "Shutter Speed in Av Mode"); 391 _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_BRACKETTING), "Auto-Exposure Bracketting Sequence/Auto Cancellation"); 392 _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC), "Shutter Curtain Sync"); 393 _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_AF_STOP), "Lens Auto-Focus Stop Button Function Switch"); 394 _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION), "Auto Reduction of Fill Flash"); 395 _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN), "Menu Button Return Position"); 396 _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION), "SET Button Function When Shooting"); 397 _tagNameMap.put(new Integer(TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING), "Sensor Cleaning"); 480 _tagNameMap.put(TAG_CANON_FIRMWARE_VERSION, "Firmware Version"); 481 _tagNameMap.put(TAG_CANON_IMAGE_NUMBER, "Image Number"); 482 _tagNameMap.put(TAG_CANON_IMAGE_TYPE, "Image Type"); 483 _tagNameMap.put(TAG_CANON_OWNER_NAME, "Owner Name"); 484 _tagNameMap.put(TAG_CANON_SERIAL_NUMBER, "Camera Serial Number"); 485 _tagNameMap.put(TAG_CAMERA_INFO_ARRAY, "Camera Info Array"); 486 _tagNameMap.put(TAG_CANON_FILE_LENGTH, "File Length"); 487 _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTIONS_ARRAY, "Custom Functions"); 488 _tagNameMap.put(TAG_MODEL_ID, "Canon Model ID"); 489 _tagNameMap.put(TAG_MOVIE_INFO_ARRAY, "Movie Info Array"); 490 491 _tagNameMap.put(CameraSettings.TAG_AF_POINT_SELECTED, "AF Point Selected"); 492 _tagNameMap.put(CameraSettings.TAG_CONTINUOUS_DRIVE_MODE, "Continuous Drive Mode"); 493 _tagNameMap.put(CameraSettings.TAG_CONTRAST, "Contrast"); 494 _tagNameMap.put(CameraSettings.TAG_EASY_SHOOTING_MODE, "Easy Shooting Mode"); 495 _tagNameMap.put(CameraSettings.TAG_EXPOSURE_MODE, "Exposure Mode"); 496 _tagNameMap.put(CameraSettings.TAG_FLASH_DETAILS, "Flash Details"); 497 _tagNameMap.put(CameraSettings.TAG_FLASH_MODE, "Flash Mode"); 498 _tagNameMap.put(CameraSettings.TAG_FOCAL_UNITS_PER_MM, "Focal Units per mm"); 499 _tagNameMap.put(CameraSettings.TAG_FOCUS_MODE_1, "Focus Mode"); 500 _tagNameMap.put(CameraSettings.TAG_FOCUS_MODE_2, "Focus Mode"); 501 _tagNameMap.put(CameraSettings.TAG_IMAGE_SIZE, "Image Size"); 502 _tagNameMap.put(CameraSettings.TAG_ISO, "Iso"); 503 _tagNameMap.put(CameraSettings.TAG_LONG_FOCAL_LENGTH, "Long Focal Length"); 504 _tagNameMap.put(CameraSettings.TAG_MACRO_MODE, "Macro Mode"); 505 _tagNameMap.put(CameraSettings.TAG_METERING_MODE, "Metering Mode"); 506 _tagNameMap.put(CameraSettings.TAG_SATURATION, "Saturation"); 507 _tagNameMap.put(CameraSettings.TAG_SELF_TIMER_DELAY, "Self Timer Delay"); 508 _tagNameMap.put(CameraSettings.TAG_SHARPNESS, "Sharpness"); 509 _tagNameMap.put(CameraSettings.TAG_SHORT_FOCAL_LENGTH, "Short Focal Length"); 510 _tagNameMap.put(CameraSettings.TAG_QUALITY, "Quality"); 511 _tagNameMap.put(CameraSettings.TAG_UNKNOWN_2, "Unknown Camera Setting 2"); 512 _tagNameMap.put(CameraSettings.TAG_UNKNOWN_3, "Unknown Camera Setting 3"); 513 _tagNameMap.put(CameraSettings.TAG_UNKNOWN_4, "Unknown Camera Setting 4"); 514 _tagNameMap.put(CameraSettings.TAG_DIGITAL_ZOOM, "Digital Zoom"); 515 _tagNameMap.put(CameraSettings.TAG_FOCUS_TYPE, "Focus Type"); 516 _tagNameMap.put(CameraSettings.TAG_UNKNOWN_7, "Unknown Camera Setting 7"); 517 _tagNameMap.put(CameraSettings.TAG_UNKNOWN_8, "Unknown Camera Setting 8"); 518 _tagNameMap.put(CameraSettings.TAG_UNKNOWN_9, "Unknown Camera Setting 9"); 519 _tagNameMap.put(CameraSettings.TAG_UNKNOWN_10, "Unknown Camera Setting 10"); 520 _tagNameMap.put(CameraSettings.TAG_FLASH_ACTIVITY, "Flash Activity"); 521 _tagNameMap.put(CameraSettings.TAG_UNKNOWN_12, "Unknown Camera Setting 12"); 522 _tagNameMap.put(CameraSettings.TAG_UNKNOWN_13, "Unknown Camera Setting 13"); 523 524 _tagNameMap.put(FocalLength.TAG_WHITE_BALANCE, "White Balance"); 525 _tagNameMap.put(FocalLength.TAG_SEQUENCE_NUMBER, "Sequence Number"); 526 _tagNameMap.put(FocalLength.TAG_AF_POINT_USED, "AF Point Used"); 527 _tagNameMap.put(FocalLength.TAG_FLASH_BIAS, "Flash Bias"); 528 _tagNameMap.put(FocalLength.TAG_AUTO_EXPOSURE_BRACKETING, "Auto Exposure Bracketing"); 529 _tagNameMap.put(FocalLength.TAG_AEB_BRACKET_VALUE, "AEB Bracket Value"); 530 _tagNameMap.put(FocalLength.TAG_SUBJECT_DISTANCE, "Subject Distance"); 531 532 _tagNameMap.put(ShotInfo.TAG_AUTO_ISO, "Auto ISO"); 533 _tagNameMap.put(ShotInfo.TAG_BASE_ISO, "Base ISO"); 534 _tagNameMap.put(ShotInfo.TAG_MEASURED_EV, "Measured EV"); 535 _tagNameMap.put(ShotInfo.TAG_TARGET_APERTURE, "Target Aperture"); 536 _tagNameMap.put(ShotInfo.TAG_TARGET_EXPOSURE_TIME, "Target Exposure Time"); 537 _tagNameMap.put(ShotInfo.TAG_EXPOSURE_COMPENSATION, "Exposure Compensation"); 538 _tagNameMap.put(ShotInfo.TAG_WHITE_BALANCE, "White Balance"); 539 _tagNameMap.put(ShotInfo.TAG_SLOW_SHUTTER, "Slow Shutter"); 540 _tagNameMap.put(ShotInfo.TAG_SEQUENCE_NUMBER, "Sequence Number"); 541 _tagNameMap.put(ShotInfo.TAG_OPTICAL_ZOOM_CODE, "Optical Zoom Code"); 542 _tagNameMap.put(ShotInfo.TAG_CAMERA_TEMPERATURE, "Camera Temperature"); 543 _tagNameMap.put(ShotInfo.TAG_FLASH_GUIDE_NUMBER, "Flash Guide Number"); 544 _tagNameMap.put(ShotInfo.TAG_AF_POINTS_IN_FOCUS, "AF Points in Focus"); 545