Ticket #8895: upgrade-metadata-extractor3.patch
File upgrade-metadata-extractor3.patch, 970.1 KB (added by , 12 years ago) |
---|
-
src/com/drew/imaging/ImageProcessingException.java
1 /* 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/ 20 */ 21 package com.drew.imaging; 22 23 import com.drew.lang.CompoundException; 24 import com.drew.lang.annotations.Nullable; 25 26 /** 27 * An exception class thrown upon an unexpected condition that was fatal for the processing of an image. 28 * 29 * @author Drew Noakes http://drewnoakes.com 30 */ 31 public class ImageProcessingException extends CompoundException 32 { 33 private static final long serialVersionUID = -9115669182209912676L; 34 35 public ImageProcessingException(@Nullable String message) 36 { 37 super(message); 38 } 39 40 public ImageProcessingException(@Nullable String message, @Nullable Throwable cause) 41 { 42 super(message, cause); 43 } 44 45 public ImageProcessingException(@Nullable Throwable cause) 46 { 47 super(cause); 48 } 49 } -
src/com/drew/imaging/ImageProcessingException.java
-
src/com/drew/imaging/jpeg/JpegMetadataReader.java
Modification de propriétés sur src/com/drew/imaging/ImageProcessingException.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
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 29 32 import java.io.File; 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{} 41 46 // public static Metadata readMetadata(IIOImage image) 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 51 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 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, continue64 // TODO lodge error message within exif directory?65 }66 73 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? 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; 72 88 } 73 89 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 } 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); 80 94 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 } 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); 87 99 88 return metadata; 89 } 90 91 private JpegMetadataReader() 92 { 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); 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); 103 102 } 104 103 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 } 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); 118 111 } 119 if (directory.hasErrors()) {120 Iterator errors = directory.getErrors();121 while (errors.hasNext()) {122 System.out.println("ERROR: " + errors.next());123 }124 }125 112 } 126 113 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 } 114 return metadata; 140 115 } 116 117 private JpegMetadataReader() throws Exception 118 { 119 throw new Exception("Not intended for instantiation"); 120 } 141 121 } 122 -
src/com/drew/imaging/jpeg/JpegProcessingException.java
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); 43 48 } -
src/com/drew/imaging/jpeg/JpegSegmentData.java
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.jpeg; 16 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 25 17 26 import java.io.*; 18 27 import java.util.ArrayList; 19 28 import java.util.HashMap; … … 21 30 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; 46 @NotNull 47 private final HashMap<Byte, List<byte[]>> _segmentDataMap = new HashMap<Byte, List<byte[]>>(10); 33 48 34 public JpegSegmentData() 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) 35 56 { 36 _segmentDataMap = new HashMap(10); 37 } 38 39 public void addSegment(byte segmentMarker, byte[] segmentBytes) 40 { 41 List segmentList = getOrCreateSegmentList(segmentMarker); 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 { 47 69 return getSegment(segmentMarker, 0); 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);87 return segmentList.get(occurrence); 58 88 } 59 89 60 public int getSegmentCount(byte segmentMarker) 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) 61 98 { 62 final List segmentList = getSegmentList(segmentMarker); 63 if (segmentList==null) 64 return 0; 65 else 66 return segmentList.size(); 99 final List<byte[]> segmentList = getSegmentList(segmentMarker); 100 return segmentList==null ? new ArrayList<byte[]>() : segmentList; 67 101 } 68 102 69 public void removeSegmentOccurrence(byte segmentMarker, int occurrence) 103 @Nullable 104 public List<byte[]> getSegmentList(byte segmentMarker) 70 105 { 71 final List segmentList = (List)_segmentDataMap.get(new Byte(segmentMarker)); 72 segmentList.remove(occurrence); 106 return _segmentDataMap.get(Byte.valueOf(segmentMarker)); 73 107 } 74 108 75 public void removeSegment(byte segmentMarker) 109 @NotNull 110 private List<byte[]> getOrCreateSegmentList(byte segmentMarker) 76 111 { 77 _segmentDataMap.remove(new Byte(segmentMarker)); 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; 78 120 } 79 121 80 private List getSegmentList(byte segmentMarker) 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 */ 127 public int getSegmentCount(byte segmentMarker) 81 128 { 82 return (List)_segmentDataMap.get(new Byte(segmentMarker)); 129 final List<byte[]> segmentList = getSegmentList(segmentMarker); 130 return segmentList == null ? 0 : segmentList.size(); 83 131 } 84 132 85 private List getOrCreateSegmentList(byte segmentMarker) 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" }) 140 public void removeSegmentOccurrence(byte segmentMarker, int occurrence) 86 141 { 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; 142 final List<byte[]> segmentList = _segmentDataMap.get(Byte.valueOf(segmentMarker)); 143 segmentList.remove(occurrence); 96 144 } 97 145 146 /** 147 * Removes all segments from the collection having the specified marker. 148 * @param segmentMarker identifies the required segment 149 */ 150 public void removeSegment(byte segmentMarker) 151 { 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));162 return _segmentDataMap.containsKey(Byte.valueOf(segmentMarker)); 101 163 } 102 164 103 public static void ToFile(File file, JpegSegmentData segmentData) throws IOException 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 104 172 { 105 ObjectOutputStream outputStream = null;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();181 if (fileOutputStream!=null) 182 fileOutputStream.close(); 115 183 } 116 184 } 117 185 118 public static JpegSegmentData FromFile(File file) throws IOException, ClassNotFoundException 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; 121 197 try -
src/com/drew/imaging/jpeg/JpegSegmentReader.java
1 1 /* 2 * JpegSegmentReader.java2 * Copyright 2002-2012 Drew Noakes 3 3 * 4 * This class written by Drew Noakes, in accordance with the Jpeg specification. 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 5 7 * 6 * This is public domain software - that is, you can do whatever you want 7 * with it, and include it software that is licensed under the GNU or the 8 * BSD license, or whatever other licence you choose, including proprietary 9 * closed source licenses. I do ask that you leave this header in tact. 8 * http://www.apache.org/licenses/LICENSE-2.0 10 9 * 11 * If you make modifications to this code that you think would benefit the 12 * wider community, please send me a copy and I'll post it on my site. 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. 13 15 * 14 * If you make use of this code, I'd appreciate hearing about it. 15 * drew@drewnoakes.com 16 * Latest version of this software kept at 17 * http://drewnoakes.com/ 16 * More information about this project is available at: 18 17 * 19 * Created by dnoakes on 04-Nov-2002 00:54:00 using IntelliJ IDEA 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 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' 35 37 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; 38 @NotNull 39 private final JpegSegmentData _segmentData; 42 40 43 private JpegSegmentData _segmentData;44 45 41 /** 46 * Private, because this segment crashes my algorithm, and searching for 47 * it doesn't work (yet). 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; 50 45 … … 53 48 */ 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. */ 61 56 public static final byte SEGMENT_APP2 = (byte)0xE2; … … 73 68 public static final byte SEGMENT_APP8 = (byte)0xE8; 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. */ 89 84 public static final byte SEGMENT_SOI = (byte)0xD8; … … 100 95 * Creates a JpegSegmentReader for a specific file. 101 96 * @param file the Jpeg file to read segments from 102 97 */ 103 public JpegSegmentReader(File file) throws JpegProcessingException 98 @SuppressWarnings({ "ConstantConditions" }) 99 public JpegSegmentReader(@NotNull File file) throws JpegProcessingException, IOException 104 100 { 105 _file = file; 106 _data = null; 107 _stream = null; 101 if (file==null) 102 throw new NullPointerException(); 108 103 109 readSegments(); 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 112 114 /** 113 115 * Creates a JpegSegmentReader for a byte array. 114 116 * @param fileContents the byte array containing Jpeg data 115 117 */ 116 public JpegSegmentReader(byte[] fileContents) throws JpegProcessingException 118 @SuppressWarnings({ "ConstantConditions" }) 119 public JpegSegmentReader(@NotNull byte[] fileContents) throws JpegProcessingException 117 120 { 118 _file = null; 119 _data = fileContents; 120 _stream = null; 121 if (fileContents==null) 122 throw new NullPointerException(); 121 123 122 readSegments(); 124 BufferedInputStream stream = new BufferedInputStream(new ByteArrayInputStream(fileContents)); 125 _segmentData = readSegments(stream, false); 123 126 } 124 127 125 public JpegSegmentReader(InputStream in) throws JpegProcessingException 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 126 134 { 127 _stream = in; 128 _file = null; 129 _data = null; 130 131 readSegments(); 132 } 135 if (inputStream==null) 136 throw new NullPointerException(); 133 137 134 public JpegSegmentReader(JpegSegmentData segmentData) 135 { 136 _file = null; 137 _data = null; 138 _stream = null; 138 BufferedInputStream bufferedInputStream = inputStream instanceof BufferedInputStream 139 ? (BufferedInputStream)inputStream 140 : new BufferedInputStream(inputStream); 139 141 140 _segmentData = segmentData;142 _segmentData = readSegments(bufferedInputStream, waitForBytes); 141 143 } 142 144 143 145 /** … … 145 147 * a byte array. 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 IOExceptions150 150 */ 151 public byte[] readSegment(byte segmentMarker) throws JpegProcessingException 151 @Nullable 152 public byte[] readSegment(byte segmentMarker) 152 153 { 153 154 return readSegment(segmentMarker, 0); 154 155 } 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 { 165 167 return _segmentData.getSegment(segmentMarker, occurrence); 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 { 170 189 return _segmentData.getSegmentCount(segmentMarker); 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 { 175 199 return _segmentData; 176 200 } 177 201 178 private void readSegments() throws JpegProcessingException 202 @NotNull 203 private JpegSegmentData readSegments(@NotNull final BufferedInputStream jpegInputStream, boolean waitForBytes) throws JpegProcessingException 179 204 { 180 _segmentData = new JpegSegmentData();205 JpegSegmentData segmentData = new JpegSegmentData(); 181 206 182 BufferedInputStream inStream = getJpegInputStream();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)); 195 227 } 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)) { 215 249 // The 'Start-Of-Scan' segment's length doesn't include the image data, instead would 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 } 239 270 } 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 273 private boolean checkForBytesOnStream(@NotNull BufferedInputStream stream, int bytesNeeded, boolean waitForBytes) throws IOException 249 274 { 250 if (_stream!=null) {251 if (_stream instanceof BufferedInputStream) { 252 return (BufferedInputStream) _stream;253 } else {254 return new BufferedInputStream(_stream); 255 }256 }257 InputStream inputStream;258 if (_data == null) {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; 259 284 try { 260 inputStream = new FileInputStream(_file);261 } catch ( FileNotFoundException e) {262 throw new JpegProcessingException("Jpeg file does not exist", e);285 Thread.sleep(100); 286 } catch (InterruptedException e) { 287 // continue 263 288 } 264 } else { 265 inputStream = new ByteArrayInputStream(_data); 289 count--; 266 290 } 267 return new BufferedInputStream(inputStream);291 return false; 268 292 } 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; 282 } 283 } 284 Pas de retour chariot à la fin du fichier 293 } -
src/com/drew/imaging/PhotographicConversions.java
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; 16 22 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 31 42 */ 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 } 41 50 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 46 56 */ 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 } -
src/com/drew/lang/annotations/NotNull.java
1 /* 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/ 20 */ 21 22 package com.drew.lang.annotations; 23 24 /** 25 * @author Drew Noakes http://drewnoakes.com 26 */ 27 public @interface NotNull 28 { 29 } -
src/com/drew/lang/annotations/NotNull.java
-
src/com/drew/lang/annotations/Nullable.java
Modification de propriétés sur src/com/drew/lang/annotations/NotNull.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
1 /* 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/ 20 */ 21 22 package com.drew.lang.annotations; 23 24 /** 25 * @author Drew Noakes http://drewnoakes.com 26 */ 27 public @interface Nullable 28 { 29 } -
src/com/drew/lang/annotations/Nullable.java
-
src/com/drew/lang/annotations/SuppressWarnings.java
Modification de propriétés sur src/com/drew/lang/annotations/Nullable.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
1 /* 2 * Copyright 2002-2011 Andreas Ziermann 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 */ 21 22 package com.drew.lang.annotations; 23 24 /** 25 * Used to suppress specific code analysis warnings produced by the Findbugs tool. 26 * 27 * @author Andreas Ziermann 28 */ 29 public @interface SuppressWarnings 30 { 31 /** 32 * The name of the warning to be suppressed. 33 * @return The name of the warning to be suppressed. 34 */ 35 @NotNull String value(); 36 37 /** 38 * An explanation of why it is valid to suppress the warning in a particular situation/context. 39 * @return An explanation of why it is valid to suppress the warning in a particular situation/context. 40 */ 41 @NotNull String justification(); 42 } -
src/com/drew/lang/annotations/SuppressWarnings.java
-
src/com/drew/lang/BufferBoundsException.java
Modification de propriétés sur src/com/drew/lang/annotations/SuppressWarnings.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
1 /* 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/ 20 */ 21 22 package com.drew.lang; 23 24 import com.drew.lang.annotations.NotNull; 25 26 import java.io.IOException; 27 28 /** 29 * A checked replacement for IndexOutOfBoundsException. Used by BufferReader. 30 * 31 * @author Drew Noakes http://drewnoakes.com 32 */ 33 public final class BufferBoundsException extends Exception 34 { 35 private static final long serialVersionUID = 2911102837808946396L; 36 37 public BufferBoundsException(@NotNull byte[] buffer, int index, int bytesRequested) 38 { 39 super(getMessage(buffer, index, bytesRequested)); 40 } 41 42 public BufferBoundsException(final String message) 43 { 44 super(message); 45 } 46 47 public BufferBoundsException(final String message, final IOException innerException) 48 { 49 super(message, innerException); 50 } 51 52 private static String getMessage(@NotNull byte[] buffer, int index, int bytesRequested) 53 { 54 if (index < 0) 55 return String.format("Attempt to read from buffer using a negative index (%s)", index); 56 57 return String.format("Attempt to read %d byte%s from beyond end of buffer (requested index: %d, max index: %d)", 58 bytesRequested, bytesRequested==1?"":"s", index, buffer.length - 1); 59 } 60 } -
src/com/drew/lang/BufferBoundsException.java
-
src/com/drew/lang/BufferReader.java
Modification de propriétés sur src/com/drew/lang/BufferBoundsException.java ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
1 /* 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/ 20 */ 21 22 package com.drew.lang; 23 24 import com.drew.lang.annotations.NotNull; 25 26 public interface BufferReader 27 { 28 /** 29 * Returns the length of the buffer. This value represents the total number of bytes in the underlying source. 30 * 31 * @return The number of bytes in the buffer. 32 */ 33 long getLength(); 34 35 /** 36 * Sets the endianness of this reader. 37 * <ul> 38 * <li><code>true</code> for Motorola (or big) endianness</li> 39 * <li><code>false</code> for Intel (or little) endianness</li> 40 * </ul> 41 * 42 * @param motorolaByteOrder <code>true</code> for motorola/big endian, <code>false</code> for intel/little endian 43 */ 44 void setMotorolaByteOrder(boolean motorolaByteOrder); 45 46 /** 47 * Gets the endianness of this reader. 48 * <ul> 49 * <li><code>true</code> for Motorola (or big) endianness</li> 50 * <li><code>false</code> for Intel (or little) endianness</li> 51 * </ul> 52 */ 53 boolean isMotorolaByteOrder(); 54 55 /** 56 * Returns an unsigned 8-bit int calculated from one byte of data at the specified index. 57 * 58 * @param index position within the data buffer to read byte 59 * @return the 8 bit int value, between 0 and 255 60 * @throws BufferBoundsException the buffer does not contain enough bytes to service the request, or index is negative 61 */ 62 short getUInt8(int index) throws BufferBoundsException; 63 64 /** 65 * Returns a signed 8-bit int calculated from one byte of data at the specified index. 66 * 67 * @param index position within the data buffer to read byte 68 * @return the 8 bit int value, between 0x00 and 0xFF 69 * @throws BufferBoundsException the buffer does not contain enough bytes to service the request, or index is negative 70 */ 71 byte getInt8(int index) throws BufferBoundsException; 72 73 /** 74 * Returns an unsigned 16-bit int calculated from two bytes of data at the specified index. 75 * 76 * @param index position within the data buffer to read first byte 77 * @return the 16 bit int value, between 0x0000 and 0xFFFF 78 * @throws BufferBoundsException the buffer does not contain enough bytes to service the request, or index is negative 79 */ 80 int getUInt16(int index) throws BufferBoundsException; 81 82 /** 83 * Returns a signed 16-bit int calculated from two bytes of data at the specified index (MSB, LSB). 84 * 85 * @param index position within the data buffer to read first byte 86 * @return the 16 bit int value, between 0x0000 and 0xFFFF 87 * @throws BufferBoundsException the buffer does not contain enough bytes to service the request, or index is negative 88 */ 89 short getInt16(int index) throws BufferBoundsException; 90 91 /** 92 * Get a 32-bit unsigned integer from the buffer, returning it as a long. 93 * 94 * @param index position within the data buffer to read first byte 95 * @return the unsigned 32-bit int value as a long, between 0x00000000 and 0xFFFFFFFF 96 * @throws BufferBoundsException the buffer does not contain enough bytes to service the request, or index is negative 97 */ 98 long getUInt32(int index) throws BufferBoundsException; 99 100 /** 101 * Returns a signed 32-bit integer from four bytes of data at the specified index the buffer. 102 * 103 * @param index position within the data buffer to read first byte 104 * @return the signed 32 bit int value, between 0x00000000 and 0xFFFFFFFF 105 * @throws BufferBoundsException the buffer does not contain enough bytes to service the request, or index is negative 106 */ 107 int getInt32(int index) throws BufferBoundsException; 108 109 /** 110 * Get a signed 64-bit integer from the buffer. 111 * 112 * @param index position within the data buffer to read first byte 113 * @return the 64 bit int value, between 0x0000000000000000 and 0xFFFFFFFFFFFFFFFF 114 * @throws BufferBoundsException the buffer does not contain enough bytes to service the request, or index is negative 115 */ 116 long getInt64(int index) throws BufferBoundsException; 117 118 float getS15Fixed16(int index) throws BufferBoundsException; 119 120 float getFloat32(int index) throws BufferBoundsException; 121 122 double getDouble64(int index) throws BufferBoundsException; 123 124 @NotNull 125 byte[] getBytes(int index, int count) throws BufferBoundsException; 126 127 @NotNull 128 String getString(int index, int bytesRequested) throws BufferBoundsException; 129 130 @NotNull 131 String getString(int index, int bytesRequested, String charset) throws BufferBoundsException; 132 133 /** 134 * Creates a String from the _data buffer starting at the specified index, 135 * and ending where <code>byte=='\0'</code> or where <code>length==maxLength</code>. 136 * 137 * @param index The index within the buffer at which to start reading the string. 138 * @param maxLengthBytes The maximum number of bytes to read. If a zero-byte is not reached within this limit, 139 * reading will stop and the string will be truncated to this length. 140 * @return The read string. 141 * @throws BufferBoundsException The buffer does not contain enough bytes to satisfy this request. 142 */ 143 @NotNull 144 String getNullTerminatedString(int index, int maxLengthBytes) throws BufferBoundsException; 145 } -
src/com/drew/lang/BufferReader.java
-
src/com/drew/lang/ByteArrayReader.java
Modification de propriétés sur src/com/drew/lang/BufferReader.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
1 /* 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/ 20 */ 21 22 package com.drew.lang; 23 24 import com.drew.lang.annotations.NotNull; 25 26 import java.io.UnsupportedEncodingException; 27 28 /** 29 * Provides methods to read specific values from a byte array, with a consistent, checked exception structure for 30 * issues. 31 * <p/> 32 * By default, the reader operates with Motorola byte order (big endianness). This can be changed by calling 33 * {@see setMotorolaByteOrder(boolean)}. 34 * 35 * @author Drew Noakes http://drewnoakes.com 36 * */ 37 public class ByteArrayReader implements BufferReader 38 { 39 @NotNull 40 private final byte[] _buffer; 41 private boolean _isMotorolaByteOrder = true; 42 43 @SuppressWarnings({ "ConstantConditions" }) 44 @com.drew.lang.annotations.SuppressWarnings(value = "EI_EXPOSE_REP2", justification = "Design intent") 45 public ByteArrayReader(@NotNull byte[] buffer) 46 { 47 if (buffer == null) 48 throw new NullPointerException(); 49 50 _buffer = buffer; 51 } 52 53 @Override 54 public long getLength() 55 { 56 return _buffer.length; 57 } 58 59 60 @Override 61 public void setMotorolaByteOrder(boolean motorolaByteOrder) 62 { 63 _isMotorolaByteOrder = motorolaByteOrder; 64 } 65 66 @Override 67 public boolean isMotorolaByteOrder() 68 { 69 return _isMotorolaByteOrder; 70 } 71 72 @Override 73 public short getUInt8(int index) throws BufferBoundsException 74 { 75 checkBounds(index, 1); 76 77 return (short) (_buffer[index] & 255); 78 } 79 80 @Override 81 public byte getInt8(int index) throws BufferBoundsException 82 { 83 checkBounds(index, 1); 84 85 return _buffer[index]; 86 } 87 88 @Override 89 public int getUInt16(int index) throws BufferBoundsException 90 { 91 checkBounds(index, 2); 92 93 if (_isMotorolaByteOrder) { 94 // Motorola - MSB first 95 return (_buffer[index ] << 8 & 0xFF00) | 96 (_buffer[index + 1] & 0xFF); 97 } else { 98 // Intel ordering - LSB first 99 return (_buffer[index + 1] << 8 & 0xFF00) | 100 (_buffer[index ] & 0xFF); 101 } 102 } 103 104 @Override 105 public short getInt16(int index) throws BufferBoundsException 106 { 107 checkBounds(index, 2); 108 109 if (_isMotorolaByteOrder) { 110 // Motorola - MSB first 111 return (short) (((short)_buffer[index ] << 8 & (short)0xFF00) | 112 ((short)_buffer[index + 1] & (short)0xFF)); 113 } else { 114 // Intel ordering - LSB first 115 return (short) (((short)_buffer[index + 1] << 8 & (short)0xFF00) | 116 ((short)_buffer[index ] & (short)0xFF)); 117 } 118 } 119 120 @Override 121 public long getUInt32(int index) throws BufferBoundsException 122 { 123 checkBounds(index, 4); 124 125 if (_isMotorolaByteOrder) { 126 // Motorola - MSB first (big endian) 127 return (((long)_buffer[index ]) << 24 & 0xFF000000L) | 128 (((long)_buffer[index + 1]) << 16 & 0xFF0000L) | 129 (((long)_buffer[index + 2]) << 8 & 0xFF00L) | 130 (((long)_buffer[index + 3]) & 0xFFL); 131 } else { 132 // Intel ordering - LSB first (little endian) 133 return (((long)_buffer[index + 3]) << 24 & 0xFF000000L) | 134 (((long)_buffer[index + 2]) << 16 & 0xFF0000L) | 135 (((long)_buffer[index + 1]) << 8 & 0xFF00L) | 136 (((long)_buffer[index ]) & 0xFFL); 137 } 138 } 139 140 @Override 141 public int getInt32(int index) throws BufferBoundsException 142 { 143 checkBounds(index, 4); 144 145 if (_isMotorolaByteOrder) { 146 // Motorola - MSB first (big endian) 147 return (_buffer[index ] << 24 & 0xFF000000) | 148 (_buffer[index + 1] << 16 & 0xFF0000) | 149 (_buffer[index + 2] << 8 & 0xFF00) | 150 (_buffer[index + 3] & 0xFF); 151 } else { 152 // Intel ordering - LSB first (little endian) 153 return (_buffer[index + 3] << 24 & 0xFF000000) | 154 (_buffer[index + 2] << 16 & 0xFF0000) | 155 (_buffer[index + 1] << 8 & 0xFF00) | 156 (_buffer[index ] & 0xFF); 157 } 158 } 159 160 @Override 161 public long getInt64(int index) throws BufferBoundsException 162 { 163 checkBounds(index, 8); 164 165 if (_isMotorolaByteOrder) { 166 // Motorola - MSB first 167 return ((long)_buffer[index ] << 56 & 0xFF00000000000000L) | 168 ((long)_buffer[index + 1] << 48 & 0xFF000000000000L) | 169 ((long)_buffer[index + 2] << 40 & 0xFF0000000000L) | 170 ((long)_buffer[index + 3] << 32 & 0xFF00000000L) | 171 ((long)_buffer[index + 4] << 24 & 0xFF000000L) | 172 ((long)_buffer[index + 5] << 16 & 0xFF0000L) | 173 ((long)_buffer[index + 6] << 8 & 0xFF00L) | 174 ((long)_buffer[index + 7] & 0xFFL); 175 } else { 176 // Intel ordering - LSB first 177 return ((long)_buffer[index + 7] << 56 & 0xFF00000000000000L) | 178 ((long)_buffer[index + 6] << 48 & 0xFF000000000000L) | 179 ((long)_buffer[index + 5] << 40 & 0xFF0000000000L) | 180 ((long)_buffer[index + 4] << 32 & 0xFF00000000L) | 181 ((long)_buffer[index + 3] << 24 & 0xFF000000L) | 182 ((long)_buffer[index + 2] << 16 & 0xFF0000L) | 183 ((long)_buffer[index + 1] << 8 & 0xFF00L) | 184 ((long)_buffer[index ] & 0xFFL); 185 } 186 } 187 188 @Override 189 public float getS15Fixed16(int index) throws BufferBoundsException 190 { 191 checkBounds(index, 4); 192 193 if (_isMotorolaByteOrder) { 194 float res = (_buffer[index ] & 255) << 8 | 195 (_buffer[index + 1] & 255); 196 int d = (_buffer[index + 2] & 255) << 8 | 197 (_buffer[index + 3] & 255); 198 return (float)(res + d/65536.0); 199 } else { 200 // this particular branch is untested 201 float res = (_buffer[index + 3] & 255) << 8 | 202 (_buffer[index + 2] & 255); 203 int d = (_buffer[index + 1] & 255) << 8 | 204 (_buffer[index ] & 255); 205 return (float)(res + d/65536.0); 206 } 207 } 208 209 @Override 210 public float getFloat32(int index) throws BufferBoundsException 211 { 212 return Float.intBitsToFloat(getInt32(index)); 213 } 214 215 @Override 216 public double getDouble64(int index) throws BufferBoundsException 217 { 218 return Double.longBitsToDouble(getInt64(index)); 219 } 220 221 @Override 222 @NotNull 223 public byte[] getBytes(int index, int count) throws BufferBoundsException 224 { 225 checkBounds(index, count); 226 227 byte[] bytes = new byte[count]; 228 System.arraycopy(_buffer, index, bytes, 0, count); 229 return bytes; 230 } 231 232 @Override 233 @NotNull 234 public String getString(int index, int bytesRequested) throws BufferBoundsException 235 { 236 return new String(getBytes(index, bytesRequested)); 237 } 238 239 @Override 240 @NotNull 241 public String getString(int index, int bytesRequested, String charset) throws BufferBoundsException 242 { 243 byte[] bytes = getBytes(index, bytesRequested); 244 try { 245 return new String(bytes, charset); 246 } catch (UnsupportedEncodingException e) { 247 return new String(bytes); 248 } 249 } 250 251 @Override 252 @NotNull 253 public String getNullTerminatedString(int index, int maxLengthBytes) throws BufferBoundsException 254 { 255 // NOTE currently only really suited to single-byte character strings 256 257 checkBounds(index, maxLengthBytes); 258 259 // Check for null terminators 260 int length = 0; 261 while ((index + length) < _buffer.length && _buffer[index + length] != '\0' && length < maxLengthBytes) 262 length++; 263 264 byte[] bytes = getBytes(index, length); 265 return new String(bytes); 266 } 267 268 private void checkBounds(final int index, final int bytesRequested) throws BufferBoundsException 269 { 270 if (bytesRequested < 0 || index < 0 || (long)index + (long)bytesRequested - 1L >= (long)_buffer.length) 271 throw new BufferBoundsException(_buffer, index, bytesRequested); 272 } 273 } -
src/com/drew/lang/ByteArrayReader.java
-
src/com/drew/lang/CompoundException.java
Modification de propriétés sur src/com/drew/lang/ByteArrayReader.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
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; 16 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 25 17 26 import java.io.PrintStream; 18 27 import java.io.PrintWriter; 19 28 … … 21 30 * Represents a compound exception, as modelled in JDK 1.4, but 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 } 80 96 81 97 public void printStackTrace() 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 } 89 105 } -
src/com/drew/lang/GeoLocation.java
1 /* 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/ 20 */ 21 22 package com.drew.lang; 23 24 import com.drew.lang.annotations.NotNull; 25 import com.drew.lang.annotations.Nullable; 26 27 /** 28 * Represents a latitude and longitude pair, giving a position on earth in spherical coordinates. 29 * Values of latitude and longitude are given in degrees. 30 * This type is immutable. 31 */ 32 public final class GeoLocation 33 { 34 private final double _latitude; 35 private final double _longitude; 36 37 /** 38 * Instantiates a new instance of {@link GeoLocation}. 39 * 40 * @param latitude the latitude, in degrees 41 * @param longitude the longitude, in degrees 42 */ 43 public GeoLocation(double latitude, double longitude) 44 { 45 _latitude = latitude; 46 _longitude = longitude; 47 } 48 49 /** 50 * @return the latitudinal angle of this location, in degrees. 51 */ 52 public double getLatitude() 53 { 54 return _latitude; 55 } 56 57 /** 58 * @return the longitudinal angle of this location, in degrees. 59 */ 60 public double getLongitude() 61 { 62 return _longitude; 63 } 64 65 /** 66 * @return true, if both latitude and longitude are equal to zero 67 */ 68 public boolean isZero() 69 { 70 return _latitude == 0 && _longitude == 0; 71 } 72 73 /** 74 * Converts a decimal degree angle into its corresponding DMS (degrees-minutes-seconds) representation as a string, 75 * of format: {@code -1° 23' 4.56"} 76 */ 77 @NotNull 78 public static String decimalToDegreesMinutesSecondsString(double decimal) 79 { 80 double[] dms = decimalToDegreesMinutesSeconds(decimal); 81 return dms[0] + "° " + dms[1] + "' " + dms[2] + '"'; 82 } 83 84 /** 85 * Converts a decimal degree angle into its corresponding DMS (degrees-minutes-seconds) component values, as 86 * a double array. 87 */ 88 @NotNull 89 public static double[] decimalToDegreesMinutesSeconds(double decimal) 90 { 91 int d = (int)decimal; 92 double m = Math.abs((decimal % 1) * 60); 93 double s = (m % 1) * 60; 94 return new double[] { d, (int)m, s}; 95 } 96 97 /** 98 * Converts DMS (degrees-minutes-seconds) rational values, as given in {@link com.drew.metadata.exif.GpsDirectory}, 99 * into a single value in degrees, as a double. 100 */ 101 @Nullable 102 public static Double degreesMinutesSecondsToDecimal(@NotNull final Rational degs, @NotNull final Rational mins, @NotNull final Rational secs, final boolean isNegative) 103 { 104 double decimal = Math.abs(degs.doubleValue()) 105 + mins.doubleValue() / 60.0d 106 + secs.doubleValue() / 3600.0d; 107 108 if (Double.isNaN(decimal)) 109 return null; 110 111 if (isNegative) 112 decimal *= -1; 113 114 return decimal; 115 } 116 117 @Override 118 public boolean equals(final Object o) 119 { 120 if (this == o) return true; 121 if (o == null || getClass() != o.getClass()) return false; 122 GeoLocation that = (GeoLocation) o; 123 if (Double.compare(that._latitude, _latitude) != 0) return false; 124 if (Double.compare(that._longitude, _longitude) != 0) return false; 125 return true; 126 } 127 128 @Override 129 public int hashCode() 130 { 131 int result; 132 long temp; 133 temp = _latitude != +0.0d ? Double.doubleToLongBits(_latitude) : 0L; 134 result = (int) (temp ^ (temp >>> 32)); 135 temp = _longitude != +0.0d ? Double.doubleToLongBits(_longitude) : 0L; 136 result = 31 * result + (int) (temp ^ (temp >>> 32)); 137 return result; 138 } 139 140 /** 141 * @return a string representation of this location, of format: {@code 1.23, 4.56} 142 */ 143 @Override 144 @NotNull 145 public String toString() 146 { 147 return _latitude + ", " + _longitude; 148 } 149 150 /** 151 * @return a string representation of this location, of format: {@code -1° 23' 4.56", 54° 32' 1.92"} 152 */ 153 @NotNull 154 public String toDMSString() 155 { 156 return decimalToDegreesMinutesSecondsString(_latitude) + ", " + decimalToDegreesMinutesSecondsString(_longitude); 157 } 158 } -
src/com/drew/lang/GeoLocation.java
-
src/com/drew/lang/NullOutputStream.java
Modification de propriétés sur src/com/drew/lang/GeoLocation.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
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; 18 22 19 23 import java.io.IOException; 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 { 24 33 public NullOutputStream() -
src/com/drew/lang/Rational.java
1 1 /* 2 * Rational.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 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 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; 34 23 24 import com.drew.lang.annotations.NotNull; 25 import com.drew.lang.annotations.Nullable; 26 35 27 import java.io.Serializable; 36 28 37 29 /** 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; 37 private static final long serialVersionUID = 510688928138848770L; 49 38 50 /** 51 * Holds the denominator. 52 */ 53 private final int denominator; 39 /** Holds the numerator. */ 40 private final long _numerator; 54 41 55 private int maxSimplificationCalculations = 1000; 42 /** Holds the denominator. */ 43 private final long _denominator; 56 44 57 45 /** 58 46 * Creates a new instance of Rational. Rational objects are immutable, so 59 47 * once you've set your numerator and denominator values here, you're stuck 60 48 * with them! 61 49 */ 62 public Rational( int numerator, intdenominator)50 public Rational(long numerator, long denominator) 63 51 { 64 this.numerator = numerator;65 this.denominator = denominator;52 _numerator = numerator; 53 _denominator = denominator; 66 54 } 67 55 68 56 /** 69 57 * Returns the value of the specified number as a <code>double</code>. 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 80 68 /** 81 69 * Returns the value of the specified number as a <code>float</code>. 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 92 80 /** … … 94 82 * This may involve rounding or truncation. This implementation simply 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 105 93 /** … … 107 95 * This may involve rounding or truncation. This implementation simply 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 118 106 /** … … 120 108 * This may involve rounding or truncation. This implementation simply 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 131 119 /** … … 133 121 * This may involve rounding or truncation. This implementation simply 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();129 return (short) doubleValue(); 142 130 } 143 131 144 132 145 /** 146 * Returns the denominator. 147 */ 148 public final int getDenominator() 133 /** Returns the denominator. */ 134 public final long getDenominator() 149 135 { 150 return this. denominator;136 return this._denominator; 151 137 } 152 138 153 /** 154 * Returns the numerator. 155 */ 156 public final int getNumerator() 139 /** Returns the numerator. */ 140 public final long getNumerator() 157 141 { 158 return this. numerator;142 return this._numerator; 159 143 } 160 144 161 145 /** 162 * Returns the reciprocal value of this obejct as a new Rational. 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);153 return new Rational(this._denominator, this._numerator); 168 154 } 169 155 170 /** 171 * Checks if this rational number is an Integer, either positive or negative. 172 */ 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. 166 * 167 * @return a string representation of the object. 188 168 */ 169 @NotNull 189 170 public String toString() 190 171 { 191 return numerator + "/" +denominator;172 return _numerator + "/" + _denominator; 192 173 } 193 174 194 /** 195 * Returns the simplest represenation of this Rational's value possible. 196 */ 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 { 208 188 Rational simplifiedInstance = getSimplifiedInstance(); … … 219 199 /** 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 } 229 211 230 212 /** 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) 220 @Override 221 public boolean equals(@Nullable Object obj) 238 222 { 239 if ( !(obj instanceof Rational)) {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(); 244 227 } 245 228 229 @Override 230 public int hashCode() 231 { 232 return (23 * (int)_denominator) + (int)_numerator; 233 } 234 246 235 /** 247 236 * <p> 248 237 * Simplifies the Rational number.</p> … … 251 240 * <p> 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 257 246 * it's simpler to check if both d & n are divisible by all numbers from 2 -> … … 264 253 * 4 Math.min(denominator, numerator) - 1 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 { 277 268 if (tooComplexForSimplification()) { 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 } 289 280 return this; 290 281 } 291 } 292 Pas de retour chariot à la fin du fichier 282 } -
src/com/drew/lang/StringUtil.java
1 /* 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/ 20 */ 21 22 package com.drew.lang; 23 24 import com.drew.lang.annotations.NotNull; 25 26 import java.util.Iterator; 27 28 /** @author Drew Noakes http://drewnoakes.com */ 29 public class StringUtil 30 { 31 public static String join(@NotNull Iterable<? extends CharSequence> strings, @NotNull String delimiter) 32 { 33 int capacity = 0; 34 int delimLength = delimiter.length(); 35 36 Iterator<? extends CharSequence> iter = strings.iterator(); 37 if (iter.hasNext()) 38 capacity += iter.next().length() + delimLength; 39 40 StringBuilder buffer = new StringBuilder(capacity); 41 iter = strings.iterator(); 42 if (iter.hasNext()) { 43 buffer.append(iter.next()); 44 while (iter.hasNext()) { 45 buffer.append(delimiter); 46 buffer.append(iter.next()); 47 } 48 } 49 return buffer.toString(); 50 } 51 52 public static <T extends CharSequence> String join(@NotNull T[] strings, @NotNull String delimiter) 53 { 54 int capacity = 0; 55 int delimLength = delimiter.length(); 56 for (T value : strings) 57 capacity += value.length() + delimLength; 58 59 StringBuilder buffer = new StringBuilder(capacity); 60 boolean first = true; 61 for (T value : strings) { 62 if (!first) { 63 buffer.append(delimiter); 64 } else { 65 first = false; 66 } 67 buffer.append(value); 68 } 69 return buffer.toString(); 70 } 71 } -
src/com/drew/lang/StringUtil.java
-
src/com/drew/metadata/Age.java
Modification de propriétés sur src/com/drew/lang/StringUtil.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
1 /* 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/ 20 */ 21 22 package com.drew.metadata; 23 24 import com.drew.lang.annotations.NotNull; 25 import com.drew.lang.annotations.Nullable; 26 27 /** 28 * Represents an age in years, months, days, hours, minutes and seconds. 29 * <p/> 30 * Used by certain Panasonic cameras which have face recognition features. 31 * 32 * @author Drew Noakes http://drewnoakes.com 33 */ 34 public class Age 35 { 36 private int _years; 37 private int _months; 38 private int _days; 39 private int _hours; 40 private int _minutes; 41 private int _seconds; 42 43 /** 44 * Parses an age object from the string format used by Panasonic cameras: 45 * <code>0031:07:15 00:00:00</code> 46 * @param s The String in format <code>0031:07:15 00:00:00</code>. 47 * @return The parsed Age object, or null if the value could not be parsed 48 */ 49 @Nullable 50 public static Age fromPanasonicString(@NotNull String s) 51 { 52 if (s == null) 53 throw new NullPointerException(); 54 55 if (s.length() != 19 || s.startsWith("9999:99:99")) 56 return null; 57 58 try { 59 int years = Integer.parseInt(s.substring(0, 4)); 60 int months = Integer.parseInt(s.substring(5, 7)); 61 int days = Integer.parseInt(s.substring(8, 10)); 62 int hours = Integer.parseInt(s.substring(11, 13)); 63 int minutes = Integer.parseInt(s.substring(14, 16)); 64 int seconds = Integer.parseInt(s.substring(17, 19)); 65 66 return new Age(years, months, days, hours, minutes, seconds); 67 } 68 catch (NumberFormatException ignored) 69 { 70 return null; 71 } 72 } 73 74 public Age(int years, int months, int days, int hours, int minutes, int seconds) 75 { 76 _years = years; 77 _months = months; 78 _days = days; 79 _hours = hours; 80 _minutes = minutes; 81 _seconds = seconds; 82 } 83 84 public int getYears() 85 { 86 return _years; 87 } 88 89 public int getMonths() 90 { 91 return _months; 92 } 93 94 public int getDays() 95 { 96 return _days; 97 } 98 99 public int getHours() 100 { 101 return _hours; 102 } 103 104 public int getMinutes() 105 { 106 return _minutes; 107 } 108 109 public int getSeconds() 110 { 111 return _seconds; 112 } 113 114 @Override 115 public String toString() 116 { 117 return String.format("%04d:%02d:%02d %02d:%02d:%02d", _years, _months, _days, _hours, _minutes, _seconds); 118 } 119 120 public String toFriendlyString() 121 { 122 StringBuilder result = new StringBuilder(); 123 appendAgePart(result, _years, "year"); 124 appendAgePart(result, _months, "month"); 125 appendAgePart(result, _days, "day"); 126 appendAgePart(result, _hours, "hour"); 127 appendAgePart(result, _minutes, "minute"); 128 appendAgePart(result, _seconds, "second"); 129 return result.toString(); 130 } 131 132 private static void appendAgePart(StringBuilder result, final int num, final String singularName) 133 { 134 if (num == 0) 135 return; 136 if (result.length()!=0) 137 result.append(' '); 138 result.append(num).append(' ').append(singularName); 139 if (num != 1) 140 result.append('s'); 141 } 142 143 @Override 144 public boolean equals(Object o) 145 { 146 if (this == o) return true; 147 if (o == null || getClass() != o.getClass()) return false; 148 149 Age age = (Age)o; 150 151 if (_days != age._days) return false; 152 if (_hours != age._hours) return false; 153 if (_minutes != age._minutes) return false; 154 if (_months != age._months) return false; 155 if (_seconds != age._seconds) return false; 156 if (_years != age._years) return false; 157 158 return true; 159 } 160 161 @Override 162 public int hashCode() 163 { 164 int result = _years; 165 result = 31 * result + _months; 166 result = 31 * result + _days; 167 result = 31 * result + _hours; 168 result = 31 * result + _minutes; 169 result = 31 * result + _seconds; 170 return result; 171 } 172 } -
src/com/drew/metadata/Age.java
-
src/com/drew/metadata/DefaultTagDescriptor.java
Modification de propriétés sur src/com/drew/metadata/Age.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
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 { 31 47 String hex = Integer.toHexString(tagType).toUpperCase(); 32 48 while (hex.length() < 4) hex = "0" + hex; 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 } -
src/com/drew/metadata/Directory.java
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; 24 import com.drew.lang.annotations.NotNull; 25 import com.drew.lang.annotations.Nullable; 26 import com.drew.lang.annotations.SuppressWarnings; 20 27 21 import java.io. Serializable;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; 43 // TODO get Array methods need to return cloned data, to maintain this directory's integrity 39 44 40 /** 41 * The descriptor used to interperet tag values. 42 */ 43 protected TagDescriptor _descriptor; 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 /** 46 50 * A convenient list holding tag values in the order in which they were stored. 47 51 * This is used for creation of an iterator, and for counting the number of 48 52 * defined tags. 49 53 */ 50 protected final List _definedTagList; 54 @NotNull 55 protected final Collection<Tag> _definedTagList = new ArrayList<Tag>(); 51 56 52 private List _errorList; 57 @NotNull 58 private final Collection<String> _errorList = new ArrayList<String>(4); 53 59 60 /** The descriptor used to interpret tag values. */ 61 protected TagDescriptor _descriptor; 62 54 63 // ABSTRACT METHODS 55 64 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(); 78 @NotNull 79 protected abstract HashMap<Integer, String> getTagNameMap(); 67 80 68 // CONSTRUCTORS 81 protected Directory() 82 {} 69 83 70 /**71 * Creates a new Directory.72 */73 public Directory()74 {75 _tagMap = new HashMap();76 _definedTagList = new ArrayList();77 }78 79 84 // VARIOUS METHODS 80 85 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() 103 @NotNull 104 public Collection<Tag> getTags() 96 105 { 97 return _definedTagList .iterator();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 */ 104 114 public int getTagCount() … … 107 117 } 108 118 109 119 /** 110 * Sets the descriptor used to interperet tag values. 111 * @param descriptor the descriptor used to interperet tag values 120 * Sets the descriptor used to interpret tag values. 121 * 122 * @param descriptor the descriptor used to interpret tag values 112 123 */ 113 public void setDescriptor(TagDescriptor descriptor) 124 @java.lang.SuppressWarnings({ "ConstantConditions" }) 125 public void setDescriptor(@NotNull TagDescriptor descriptor) 114 126 { 115 if (descriptor ==null) {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) 132 /** 133 * Registers an error message with this directory. 134 * 135 * @param message an error message. 136 */ 137 public void addError(@NotNull String message) 122 138 { 123 if (_errorList==null) {124 _errorList = new ArrayList();125 }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);149 return _errorList.size() > 0; 132 150 } 133 151 134 public Iterator getErrors() 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() 135 159 { 136 return _errorList .iterator();160 return _errorList; 137 161 } 138 162 163 /** Returns the count of error messages in this directory. */ 139 164 public int getErrorCount() 140 165 { 141 166 return _errorList.size(); … … 144 169 // TAG SETTERS 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));179 setObject(tagType, value); 154 180 } 155 181 156 182 /** 157 * Sets a double value for the specified tag. 158 * @param tagType the tag's value as an int 159 * @param value the value for the specified tag as a double 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 160 187 */ 161 public void set Double(int tagType, double value)188 public void setIntArray(int tagType, @NotNull int[] ints) 162 189 { 163 setObject (tagType, new Double(value));190 setObjectArray(tagType, ints); 164 191 } 165 192 166 193 /** 167 * Sets a float value for the specified tag. 194 * Sets a <code>float</code> value for the specified tag. 195 * 168 196 * @param tagType the tag's value as an int 169 * @param value the value for the specified tag as a float197 * @param value the value for the specified tag as a float 170 198 */ 171 199 public void setFloat(int tagType, float value) 172 200 { 173 setObject(tagType, new Float(value));201 setObject(tagType, value); 174 202 } 175 203 176 204 /** 177 * Sets an int value for the specified tag. 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 * 178 218 * @param tagType the tag's value as an int 179 * @param value the value for the specified tag as a String219 * @param value the value for the specified tag as a double 180 220 */ 181 public void set String(int tagType, Stringvalue)221 public void setDouble(int tagType, double value) 182 222 { 183 223 setObject(tagType, value); 184 224 } 185 225 186 226 /** 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 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 190 231 */ 191 public void set Boolean(int tagType, boolean value)232 public void setDoubleArray(int tagType, @NotNull double[] doubles) 192 233 { 193 setObject (tagType, new Boolean(value));234 setObjectArray(tagType, doubles); 194 235 } 195 236 196 237 /** 197 * Sets a long value for the specified tag. 238 * Sets a <code>String</code> value for the specified tag. 239 * 198 240 * @param tagType the tag's value as an int 199 * @param value the value for the specified tag as a long241 * @param value the value for the specified tag as a String 200 242 */ 201 public void setLong(int tagType, long value) 243 @java.lang.SuppressWarnings({ "ConstantConditions" }) 244 public void setString(int tagType, @NotNull String value) 202 245 { 203 setObject(tagType, new Long(value)); 246 if (value == null) 247 throw new NullPointerException("cannot set a null String"); 248 setObject(tagType, value); 204 249 } 205 250 206 251 /** 207 * Sets a java.util.Date value for the specified tag. 252 * Sets a <code>String[]</code> (array) for the specified tag. 253 * 254 * @param tagType the tag identifier 255 * @param strings the String array to store 256 */ 257 public void setStringArray(int tagType, @NotNull String[] strings) 258 { 259 setObjectArray(tagType, strings); 260 } 261 262 /** 263 * Sets a <code>boolean</code> value for the specified tag. 264 * 208 265 * @param tagType the tag's value as an int 209 * @param value the value for the specified tag as a java.util.Date266 * @param value the value for the specified tag as a boolean 210 267 */ 211 public void set Date(int tagType, java.util.Datevalue)268 public void setBoolean(int tagType, boolean value) 212 269 { 213 270 setObject(tagType, value); 214 271 } 215 272 216 273 /** 217 * Sets a Rational value for the specified tag. 274 * Sets a <code>long</code> value for the specified tag. 275 * 218 276 * @param tagType the tag's value as an int 219 * @param rational rational number277 * @param value the value for the specified tag as a long 220 278 */ 221 public void set Rational(int tagType, Rational rational)279 public void setLong(int tagType, long value) 222 280 { 223 setObject(tagType, rational);281 setObject(tagType, value); 224 282 } 225 283 226 284 /** 227 * Sets a Rational array for the specified tag. 228 * @param tagType the tag identifier 229 * @param rationals the Rational array to store 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 230 289 */ 231 public void set RationalArray(int tagType, Rational[] rationals)290 public void setDate(int tagType, @NotNull java.util.Date value) 232 291 { 233 setObject Array(tagType, rationals);292 setObject(tagType, value); 234 293 } 235 294 236 295 /** 237 * Sets an int array for the specified tag. 238 * @param tagType the tag identifier 239 * @param ints the int array to store 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 240 300 */ 241 public void set IntArray(int tagType, int[] ints)301 public void setRational(int tagType, @NotNull Rational rational) 242 302 { 243 setObject Array(tagType, ints);303 setObject(tagType, rational); 244 304 } 245 305 246 306 /** 247 * Sets a byte array for the specified tag. 248 * @param tagType the tag identifier 249 * @param bytes the byte array to store 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 250 311 */ 251 public void set ByteArray(int tagType, byte[] bytes)312 public void setRationalArray(int tagType, @NotNull Rational[] rationals) 252 313 { 253 setObjectArray(tagType, bytes);314 setObjectArray(tagType, rationals); 254 315 } 255 316 256 317 /** 257 * Sets a String array for the specified tag. 318 * Sets a <code>byte[]</code> (array) for the specified tag. 319 * 258 320 * @param tagType the tag identifier 259 * @param strings the Stringarray to store321 * @param bytes the byte array to store 260 322 */ 261 public void set StringArray(int tagType, String[] strings)323 public void setByteArray(int tagType, @NotNull byte[] bytes) 262 324 { 263 setObjectArray(tagType, strings);325 setObjectArray(tagType, bytes); 264 326 } 265 327 266 328 /** 267 * Private helper method, containing common functionality for all 'add'268 * methods.329 * Sets a <code>Object</code> for the specified tag. 330 * 269 331 * @param tagType the tag's value as an int 270 * @param value the value for the specified tag332 * @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) 335 @java.lang.SuppressWarnings( { "ConstantConditions", "UnnecessaryBoxing" }) 336 public void setObject(int tagType, @NotNull Object value) 274 337 { 275 if (value ==null) {338 if (value == null) 276 339 throw new NullPointerException("cannot set a null object"); 277 }278 340 279 Integer key = new Integer(tagType); 280 if (!_tagMap.containsKey(key)) { 341 if (!_tagMap.containsKey(Integer.valueOf(tagType))) { 281 342 _definedTagList.add(new Tag(tagType, this)); 282 343 } 283 _tagMap.put(key, value); 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); 284 350 } 285 351 286 352 /** 287 * Private helper method, containing common functionality for all 'add...Array'288 * methods.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 tag356 * @param array the array of values for the specified tag 291 357 */ 292 public void setObjectArray(int tagType, Object array)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 295 361 setObject(tagType, array); … … 298 364 // TAG GETTERS 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 { 383 Integer integer = getInteger(tagType); 384 if (integer!=null) 385 return integer; 386 305 387 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) { 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); 311 419 } catch (NumberFormatException nfe) { … … 313 421 String s = (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; 321 429 } … … 323 431 return ((Number)o).intValue(); 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() + "'.");445 return null; 338 446 } 339 447 340 // TODO get Array methods need to return cloned data, to maintain this directory's integrity341 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[] 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. 349 454 */ 350 public String[] getStringArray(int tagType) throws MetadataException 455 @Nullable 456 public String[] getStringArray(int tagType) 351 457 { 352 458 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[]) {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 to391 * an int array392 493 */ 393 public int[] getIntArray(int tagType) throws MetadataException 494 @Nullable 495 public int[] getIntArray(int tagType) 394 496 { 395 497 Object o = getObject(tagType); 396 if (o ==null) {397 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");398 } elseif (o instanceof Rational[]) {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 to432 * a byte array433 539 */ 434 public byte[] getByteArray(int tagType) throws MetadataException 540 @Nullable 541 public byte[] getByteArray(int tagType) 435 542 { 436 543 Object o = getObject(tagType); 437 if (o ==null) {438 throw new MetadataException("Tag " + getTagName(tagType) + " has not been set -- check using containsTag() first");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 } 445 552 return bytes; … … 448 555 } else if (o instanceof int[]) { 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() + "'."); 570 if (o instanceof Integer) 571 return new byte[] { ((Integer)o).byteValue() }; 572 573 return null; 464 574 } 465 575 466 /** 467 * Returns the specified tag's value as a double, if possible. 468 */ 576 /** Returns the specified tag's value as a double, if possible. */ 469 577 public double getDouble(int tagType) throws MetadataException 470 578 { 579 Double value = getDoubleObject(tagType); 580 if (value!=null) 581 return value; 471 582 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) { 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);598 return null; 479 599 } 480 } else if (o instanceof Number) { 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() + "'.");603 604 return null; 484 605 } 485 606 486 /** 487 * Returns the specified tag's value as a float, if possible. 488 */ 607 /** Returns the specified tag's value as a float, if possible. */ 489 608 public float getFloat(int tagType) throws MetadataException 490 609 { 610 Float value = getFloatObject(tagType); 611 if (value!=null) 612 return value; 491 613 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) { 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);630 return null; 499 631 } 500 } else if (o instanceof Number) { 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() + "'."); 635 return null; 504 636 } 505 637 506 /** 507 * Returns the specified tag's value as a long, if possible. 508 */ 638 /** Returns the specified tag's value as a long, if possible. */ 509 639 public long getLong(int tagType) throws MetadataException 510 640 { 641 Long value = getLongObject(tagType); 642 if (value!=null) 643 return value; 511 644 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) { 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);661 return null; 519 662 } 520 } else if (o instanceof Number) { 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() + "'."); 666 return null; 524 667 } 525 668 526 /** 527 * Returns the specified tag's value as a boolean, if possible. 528 */ 669 /** Returns the specified tag's value as a boolean, if possible. */ 529 670 public boolean getBoolean(int tagType) throws MetadataException 530 671 { 672 Boolean value = getBooleanObject(tagType); 673 if (value!=null) 674 return value; 531 675 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) { 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);695 return null; 541 696 } 542 } else if (o instanceof Number) {543 return (((Number)o).doubleValue()!=0);544 697 } 545 throw new MetadataException("Tag '" + tagType + "' cannot be cast to a boolean. It is of type '" + o.getClass() + "'."); 698 if (o instanceof Number) 699 return (((Number)o).doubleValue() != 0); 700 return null; 546 701 } 547 702 548 703 /** 549 * Returns the specified tag's value as a java.util.Date, if possible. 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. 550 708 */ 551 public java.util.Date getDate(int tagType) throws MetadataException 709 @Nullable 710 public java.util.Date getDate(int tagType) 552 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 { 553 725 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) { 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() + "'.");755 return null; 578 756 } 579 757 580 /** 581 * Returns the specified tag's value as a Rational, if possible. 582 */ 583 public Rational getRational(int tagType) throws MetadataException 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) 584 761 { 585 762 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) { 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() + "'."); 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; 592 777 } 593 778 594 public Rational[] getRationalArray(int tagType) throws MetadataException 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) 595 782 { 596 783 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[]) { 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 605 793 /** 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 617 807 if (o instanceof Rational) 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));837 addError("Unexpected array component type: " + componentType.getName()); 635 838 } 636 return s buffer.toString();839 return string.toString(); 637 840 } 638 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 null866 * @return the tag's value as an Object if available, else <code>null</code> 646 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 652 875 // OTHER METHODS 653 876 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 673 897 /** 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 error679 * occurs during calculation of the description within the Descriptor680 903 */ 681 public String getDescription(int tagType) throws MetadataException 904 @Nullable 905 public String getDescription(int tagType) 682 906 { 683 if (_descriptor==null) { 684 throw new MetadataException("a descriptor must be set using setDescriptor(...) before descriptions can be provided"); 685 } 686 907 assert(_descriptor != null); 687 908 return _descriptor.getDescription(tagType); 688 909 } 689 910 } -
src/com/drew/metadata/exif/CanonMakernoteDescriptor.java
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(); 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(); 114 127 default: 115 return _directory.getString(tagType);128 return super.getDescription(tagType); 116 129 } 117 130 } 118 131 119 public String getLongExposureNoiseReductionDescription() throws MetadataException 132 @Nullable 133 public String getSerialNumberDescription() 120 134 { 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); 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"; 125 150 case 1: return "On"; 126 151 default: return "Unknown (" + value + ")"; 127 152 } 128 153 } 129 public String getShutterAutoExposureLockButtonDescription() throws MetadataException 154 155 @Nullable 156 public String getShutterAutoExposureLockButtonDescription() 130 157 { 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); 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"; 135 163 case 1: return "AE lock/AF"; … … 138 166 default: return "Unknown (" + value + ")"; 139 167 } 140 168 } 141 public String getMirrorLockupDescription() throws MetadataException 169 170 @Nullable 171 public String getMirrorLockupDescription() 142 172 { 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); 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"; 147 178 case 1: return "Enabled"; 148 179 default: return "Unknown (" + value + ")"; 149 180 } 150 181 } 151 public String getTvAndAvExposureLevelDescription() throws MetadataException 182 183 @Nullable 184 public String getTvAndAvExposureLevelDescription() 152 185 { 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); 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"; 157 191 case 1: return "1/3 stop"; 158 192 default: return "Unknown (" + value + ")"; 159 193 } 160 194 } 161 public String getAutoFocusAssistLightDescription() throws MetadataException 195 196 @Nullable 197 public String getAutoFocusAssistLightDescription() 162 198 { 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); 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)"; 167 204 case 1: return "Off"; 168 205 default: return "Unknown (" + value + ")"; 169 206 } 170 207 } 171 public String getShutterSpeedInAvModeDescription() throws MetadataException 208 209 @Nullable 210 public String getShutterSpeedInAvModeDescription() 172 211 { 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); 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"; 177 217 case 1: return "1/200 (fixed)"; 178 218 default: return "Unknown (" + value + ")"; 179 219 } 180 220 } 181 public String getAutoExposureBrackettingSequenceAndAutoCancellationDescription() throws MetadataException 221 222 @Nullable 223 public String getAutoExposureBrackettingSequenceAndAutoCancellationDescription() 182 224 { 183 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING)) return null; 184 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_CUSTOM_FUNCTION_BRACKETTING); 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"; 187 230 case 1: return "0,-,+ / Disabled"; … … 190 233 default: return "Unknown (" + value + ")"; 191 234 } 192 235 } 193 public String getShutterCurtainSyncDescription() throws MetadataException 236 237 @Nullable 238 public String getShutterCurtainSyncDescription() 194 239 { 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); 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"; 199 245 case 1: return "2nd Curtain Sync"; 200 246 default: return "Unknown (" + value + ")"; 201 247 } 202 248 } 203 public String getLensAutoFocusStopButtonDescription() throws MetadataException 249 250 @Nullable 251 public String getLensAutoFocusStopButtonDescription() 204 252 { 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); 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"; 209 258 case 1: return "Operate AF"; … … 211 260 default: return "Unknown (" + value + ")"; 212 261 } 213 262 } 214 public String getFillFlashReductionDescription() throws MetadataException 263 264 @Nullable 265 public String getFillFlashReductionDescription() 215 266 { 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); 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"; 220 272 case 1: return "Disabled"; 221 273 default: return "Unknown (" + value + ")"; 222 274 } 223 275 } 224 public String getMenuButtonReturnPositionDescription() throws MetadataException 276 277 @Nullable 278 public String getMenuButtonReturnPositionDescription() 225 279 { 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); 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"; 230 285 case 1: return "Previous (volatile)"; … … 232 287 default: return "Unknown (" + value + ")"; 233 288 } 234 289 } 235 public String getSetButtonFunctionWhenShootingDescription() throws MetadataException 290 291 @Nullable 292 public String getSetButtonFunctionWhenShootingDescription() 236 293 { 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); 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"; 241 299 case 1: return "Change Quality"; … … 244 302 default: return "Unknown (" + value + ")"; 245 303 } 246 304 } 247 public String getSensorCleaningDescription() throws MetadataException 305 306 @Nullable 307 public String getSensorCleaningDescription() 248 308 { 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); 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"; 253 314 case 1: return "Enabled"; 254 315 default: return "Unknown (" + value + ")"; 255 316 } 256 317 } 318 */ 257 319 258 public String getFlashBiasDescription() throws MetadataException 320 @Nullable 321 public String getFlashBiasDescription() 259 322 { 260 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE2_FLASH_BIAS)) return null;323 Integer value = _directory.getInteger(CanonMakernoteDirectory.FocalLength.TAG_FLASH_BIAS); 261 324 262 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE2_FLASH_BIAS); 325 if (value==null) 326 return null; 263 327 264 328 boolean isNegative = false; 265 329 if (value > 0xF000) … … 277 341 return ((isNegative) ? "-" : "") + Float.toString(value / 32f) + " EV"; 278 342 } 279 343 280 public String getAfPointUsedDescription() throws MetadataException 344 @Nullable 345 public String getAfPointUsedDescription() 281 346 { 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); 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"; 286 352 } else if ((value & 0x7) == 1) { … … 292 358 } 293 359 } 294 360 295 public String getWhiteBalanceDescription() throws MetadataException 361 @Nullable 362 public String getWhiteBalanceDescription() 296 363 { 297 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE2_WHITE_BALANCE)) return null; 298 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE2_WHITE_BALANCE); 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: 301 369 return "Auto"; … … 306 374 case 3: 307 375 return "Tungsten"; 308 376 case 4: 309 return "Flo urescent";377 return "Florescent"; 310 378 case 5: 311 379 return "Flash"; 312 380 case 6: … … 316 384 } 317 385 } 318 386 319 public String getFocusMode2Description() throws MetadataException 387 @Nullable 388 public String getFocusMode2Description() 320 389 { 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); 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: 325 395 return "Single"; … … 330 400 } 331 401 } 332 402 333 public String getFlashDetailsDescription() throws MetadataException 403 @Nullable 404 public String getFlashDetailsDescription() 334 405 { 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) { 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 } 349 421 return "Unknown (" + value + ")"; 350 422 } 351 423 352 public String getFocalUnitsPerMillimetreDescription() throws MetadataException 424 @Nullable 425 public String getFocalUnitsPerMillimetreDescription() 353 426 { 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); 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); 358 432 } else { … … 360 434 } 361 435 } 362 436 363 public String getShortFocalLengthDescription() throws MetadataException 437 @Nullable 438 public String getShortFocalLengthDescription() 364 439 { 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); 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 447 @Nullable 448 public String getLongFocalLengthDescription() 372 449 { 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); 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 457 @Nullable 458 public String getExposureModeDescription() 380 459 { 381 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_EXPOSURE_MODE)) return null; 382 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_EXPOSURE_MODE); 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: 385 465 return "Easy shooting"; … … 398 478 } 399 479 } 400 480 401 public String getAfPointSelectedDescription() throws MetadataException 481 @Nullable 482 public String getAfPointSelectedDescription() 402 483 { 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); 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: 407 489 return "None (MF)"; … … 418 500 } 419 501 } 420 502 421 public String getMeteringModeDescription() throws MetadataException 503 @Nullable 504 public String getMeteringModeDescription() 422 505 { 423 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_METERING_MODE)) return null; 424 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_METERING_MODE); 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: 427 511 return "Evaluative"; … … 434 518 } 435 519 } 436 520 437 public String getIsoDescription() throws MetadataException 521 @Nullable 522 public String getIsoDescription() 438 523 { 439 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_ISO)) return null; 440 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_ISO); 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: 443 535 return "Not specified (see ISOSpeedRatings tag)"; … … 456 548 } 457 549 } 458 550 459 public String getSharpnessDescription() throws MetadataException 551 @Nullable 552 public String getSharpnessDescription() 460 553 { 461 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SHARPNESS)) return null; 462 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SHARPNESS); 554 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SHARPNESS); 555 if (value==null) 556 return null; 463 557 switch (value) { 464 558 case 0xFFFF: 465 559 return "Low"; … … 472 566 } 473 567 } 474 568 475 public String getSaturationDescription() throws MetadataException 569 @Nullable 570 public String getSaturationDescription() 476 571 { 477 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_SATURATION)) return null; 478 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_SATURATION); 572 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_SATURATION); 573 if (value==null) 574 return null; 479 575 switch (value) { 480 576 case 0xFFFF: 481 577 return "Low"; … … 488 584 } 489 585 } 490 586 491 public String getContrastDescription() throws MetadataException 587 @Nullable 588 public String getContrastDescription() 492 589 { 493 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTRAST)) return null; 494 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_CONTRAST); 590 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_CONTRAST); 591 if (value==null) 592 return null; 495 593 switch (value) { 496 594 case 0xFFFF: 497 595 return "Low"; … … 504 602 } 505 603 } 506 604 507 public String getEasyShootingModeDescription() throws MetadataException 605 @Nullable 606 public String getEasyShootingModeDescription() 508 607 { 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); 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: 513 613 return "Full auto"; … … 538 638 } 539 639 } 540 640 541 public String getImageSizeDescription() throws MetadataException 641 @Nullable 642 public String getImageSizeDescription() 542 643 { 543 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_IMAGE_SIZE)) return null; 544 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_IMAGE_SIZE); 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: 547 649 return "Large"; … … 554 656 } 555 657 } 556 658 557 public String getFocusMode1Description() throws MetadataException 659 @Nullable 660 public String getFocusMode1Description() 558 661 { 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); 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: 563 667 return "One-shot"; … … 579 683 } 580 684 } 581 685 582 public String getContinuousDriveModeDescription() throws MetadataException 686 @Nullable 687 public String getContinuousDriveModeDescription() 583 688 { 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); 689 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_CONTINUOUS_DRIVE_MODE); 690 if (value==null) 691 return null; 586 692 switch (value) { 587 693 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 } 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 699 } 700 return "Unknown (" + value + ")"; 598 701 } 599 702 600 public String getFlashModeDescription() throws MetadataException 703 @Nullable 704 public String getFlashModeDescription() 601 705 { 602 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_MODE)) return null; 603 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_MODE); 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: 606 711 return "No flash fired"; … … 618 723 return "On and red-eye reduction"; 619 724 case 16: 620 725 // note: this value not set on Canon D30 621 return "Exte nal flash";726 return "External flash"; 622 727 default: 623 728 return "Unknown (" + value + ")"; 624 729 } 625 730 } 626 731 627 public String getSelfTimerDelayDescription() throws MetadataException 732 @Nullable 733 public String getSelfTimerDelayDescription() 628 734 { 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); 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"; 633 740 } else { … … 636 743 } 637 744 } 638 745 639 public String getMacroModeDescription() throws MetadataException 746 @Nullable 747 public String getMacroModeDescription() 640 748 { 641 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_MACRO_MODE)) return null; 642 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_MACRO_MODE); 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: 645 754 return "Macro"; … … 650 759 } 651 760 } 652 761 653 public String getQualityDescription() throws MetadataException 762 @Nullable 763 public String getQualityDescription() 654 764 { 655 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_QUALITY)) return null; 656 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_QUALITY); 765 Integer value = _directory.getInteger(CanonMakernoteDirectory.CameraSettings.TAG_QUALITY); 766 if (value==null) 767 return null; 657 768 switch (value) { 658 769 case 2: 659 770 return "Normal"; … … 666 777 } 667 778 } 668 779 669 public String getDigitalZoomDescription() throws MetadataException 780 @Nullable 781 public String getDigitalZoomDescription() 670 782 { 671 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_DIGITAL_ZOOM)) return null; 672 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_DIGITAL_ZOOM); 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: 675 788 return "No digital zoom"; … … 682 795 } 683 796 } 684 797 685 public String getFocusTypeDescription() throws MetadataException 798 @Nullable 799 public String getFocusTypeDescription() 686 800 { 687 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_TYPE)) return null; 688 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FOCUS_TYPE); 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: 691 806 return "Manual"; … … 700 815 } 701 816 } 702 817 703 public String getFlashActivityDescription() throws MetadataException 818 @Nullable 819 public String getFlashActivityDescription() 704 820 { 705 if (!_directory.containsTag(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_ACTIVITY)) return null; 706 int value = _directory.getInt(CanonMakernoteDirectory.TAG_CANON_STATE1_FLASH_ACTIVITY); 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: 709 826 return "Flash did not fire"; -
src/com/drew/metadata/exif/CanonMakernoteDirectory.java
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 21 26 import java.util.HashMap; … … 26 31 * Thanks to Bill Richards for his contribution to this makernote directory. 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;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. 35 42 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; 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 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; 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 194 109 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; 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; 232 114 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; 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 239 265 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; 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 248 310 249 /** 250 * Mirror lockup 251 * 0 = Disable 252 * 1 = Enable 253 */ 254 public static final int TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP = 0xC303; 311 private static final int OFFSET = 0xC400; 255 312 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; 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 } 262 342 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; 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 269 346 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; 347 private static final int OFFSET = 0xC500; 276 348 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; 349 public static final int TAG_PANORAMA_FRAME_NUMBER = OFFSET + 2; 350 public static final int TAG_PANORAMA_DIRECTION = OFFSET + 5; 351 } 285 352 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; 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 292 356 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; 357 private static final int OFFSET = 0xD200; 300 358 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; 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 } 307 373 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; 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; 315 472 316 /**317 * SET button function when shooting318 * 0 = Not Assigned319 * 1 = Change Quality320 * 2 = Change ISO Speed321 * 3 = Select Parameters322 */323 public static final int TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION = 0xC30C;324 325 /**326 * Sensor cleaning327 * 0 = Disable328 * 1 = Enable329 */330 public static final int TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING = 0xC30D;331 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"); 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"); 384 490 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"); 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 _tagNameMap.put(ShotInfo.TAG_FLASH_EXPOSURE_BRACKETING, "Flash Exposure Compensation"); 546 _tagNameMap.put(ShotInfo.TAG_AUTO_EXPOSURE_BRACKETING, "Auto Exposure Bracketing"); 547 _tagNameMap.put(ShotInfo.TAG_AEB_BRACKET_VALUE, "AEB Bracket Value"); 548 _tagNameMap.put(ShotInfo.TAG_CONTROL_MODE, "Control Mode"); 549 _tagNameMap.put(ShotInfo.TAG_FOCUS_DISTANCE_UPPER, "Focus Distance Upper"); 550 _tagNameMap.put(ShotInfo.TAG_FOCUS_DISTANCE_LOWER, "Focus Distance Lower"); 551 _tagNameMap.put(ShotInfo.TAG_F_NUMBER, "F Number"); 552 _tagNameMap.put(ShotInfo.TAG_EXPOSURE_TIME, "Exposure Time"); 553 _tagNameMap.put(ShotInfo.TAG_MEASURED_EV_2, "Measured EV 2"); 554 _tagNameMap.put(ShotInfo.TAG_BULB_DURATION, "Bulb Duration"); 555 _tagNameMap.put(ShotInfo.TAG_CAMERA_TYPE, "Camera Type"); 556 _tagNameMap.put(ShotInfo.TAG_AUTO_ROTATE, "Auto Rotate"); 557 _tagNameMap.put(ShotInfo.TAG_ND_FILTER, "ND Filter"); 558 _tagNameMap.put(ShotInfo.TAG_SELF_TIMER_2, "Self Timer 2"); 559 _tagNameMap.put(ShotInfo.TAG_FLASH_OUTPUT, "Flash Output"); 560 561 _tagNameMap.put(Panorama.TAG_PANORAMA_FRAME_NUMBER, "Panorama Frame Number"); 562 _tagNameMap.put(Panorama.TAG_PANORAMA_DIRECTION, "Panorama Direction"); 563 564 _tagNameMap.put(AFInfo.TAG_NUM_AF_POINTS, "AF Point Count"); 565 _tagNameMap.put(AFInfo.TAG_VALID_AF_POINTS, "Valid AF Point Count"); 566 _tagNameMap.put(AFInfo.TAG_IMAGE_WIDTH, "Image Width"); 567 _tagNameMap.put(AFInfo.TAG_IMAGE_HEIGHT, "Image Height"); 568 _tagNameMap.put(AFInfo.TAG_AF_IMAGE_WIDTH, "AF Image Width"); 569 _tagNameMap.put(AFInfo.TAG_AF_IMAGE_HEIGHT, "AF Image Height"); 570 _tagNameMap.put(AFInfo.TAG_AF_AREA_WIDTH, "AF Area Width"); 571 _tagNameMap.put(AFInfo.TAG_AF_AREA_HEIGHT, "AF Area Height"); 572 _tagNameMap.put(AFInfo.TAG_AF_AREA_X_POSITIONS, "AF Area X Positions"); 573 _tagNameMap.put(AFInfo.TAG_AF_AREA_Y_POSITIONS, "AF Area Y Positions"); 574 _tagNameMap.put(AFInfo.TAG_AF_POINTS_IN_FOCUS, "AF Points in Focus Count"); 575 _tagNameMap.put(AFInfo.TAG_PRIMARY_AF_POINT_1, "Primary AF Point 1"); 576 _tagNameMap.put(AFInfo.TAG_PRIMARY_AF_POINT_2, "Primary AF Point 2"); 577 578 // _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_LONG_EXPOSURE_NOISE_REDUCTION, "Long Exposure Noise Reduction"); 579 // _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_AUTO_EXPOSURE_LOCK_BUTTONS, "Shutter/Auto Exposure-lock Buttons"); 580 // _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_MIRROR_LOCKUP, "Mirror Lockup"); 581 // _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_TV_AV_AND_EXPOSURE_LEVEL, "Tv/Av And Exposure Level"); 582 // _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_AF_ASSIST_LIGHT, "AF-Assist Light"); 583 // _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_SPEED_IN_AV_MODE, "Shutter Speed in Av Mode"); 584 // _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_BRACKETTING, "Auto-Exposure Bracketting Sequence/Auto Cancellation"); 585 // _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SHUTTER_CURTAIN_SYNC, "Shutter Curtain Sync"); 586 // _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_AF_STOP, "Lens Auto-Focus Stop Button Function Switch"); 587 // _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_FILL_FLASH_REDUCTION, "Auto Reduction of Fill Flash"); 588 // _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_MENU_BUTTON_RETURN, "Menu Button Return Position"); 589 // _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SET_BUTTON_FUNCTION, "SET Button Function When Shooting"); 590 // _tagNameMap.put(TAG_CANON_CUSTOM_FUNCTION_SENSOR_CLEANING, "Sensor Cleaning"); 591 592 _tagNameMap.put(TAG_THUMBNAIL_IMAGE_VALID_AREA, "Thumbnail Image Valid Area"); 593 _tagNameMap.put(TAG_SERIAL_NUMBER_FORMAT, "Serial Number Format"); 594 _tagNameMap.put(TAG_SUPER_MACRO, "Super Macro"); 595 _tagNameMap.put(TAG_DATE_STAMP_MODE, "Date Stamp Mode"); 596 _tagNameMap.put(TAG_MY_COLORS, "My Colors"); 597 _tagNameMap.put(TAG_FIRMWARE_REVISION, "Firmware Revision"); 598 _tagNameMap.put(TAG_CATEGORIES, "Categories"); 599 _tagNameMap.put(TAG_FACE_DETECT_ARRAY_1, "Face Detect Array 1"); 600 _tagNameMap.put(TAG_FACE_DETECT_ARRAY_2, "Face Detect Array 2"); 601 _tagNameMap.put(TAG_AF_INFO_ARRAY_2, "AF Info Array 2"); 602 _tagNameMap.put(TAG_IMAGE_UNIQUE_ID, "Image Unique ID"); 603 _tagNameMap.put(TAG_RAW_DATA_OFFSET, "Raw Data Offset"); 604 _tagNameMap.put(TAG_ORIGINAL_DECISION_DATA_OFFSET, "Original Decision Data Offset"); 605 _tagNameMap.put(TAG_CUSTOM_FUNCTIONS_1D_ARRAY, "Custom Functions (1D) Array"); 606 _tagNameMap.put(TAG_PERSONAL_FUNCTIONS_ARRAY, "Personal Functions Array"); 607 _tagNameMap.put(TAG_PERSONAL_FUNCTION_VALUES_ARRAY, "Personal Function Values Array"); 608 _tagNameMap.put(TAG_FILE_INFO_ARRAY, "File Info Array"); 609 _tagNameMap.put(TAG_AF_POINTS_IN_FOCUS_1D, "AF Points in Focus (1D)"); 610 _tagNameMap.put(TAG_LENS_MODEL, "Lens Model"); 611 _tagNameMap.put(TAG_SERIAL_INFO_ARRAY, "Serial Info Array"); 612 _tagNameMap.put(TAG_DUST_REMOVAL_DATA, "Dust Removal Data"); 613 _tagNameMap.put(TAG_CROP_INFO, "Crop Info"); 614 _tagNameMap.put(TAG_CUSTOM_FUNCTIONS_ARRAY_2, "Custom Functions Array 2"); 615 _tagNameMap.put(TAG_ASPECT_INFO_ARRAY, "Aspect Information Array"); 616 _tagNameMap.put(TAG_PROCESSING_INFO_ARRAY, "Processing Information Array"); 617 _tagNameMap.put(TAG_TONE_CURVE_TABLE, "Tone Curve Table"); 618 _tagNameMap.put(TAG_SHARPNESS_TABLE, "Sharpness Table"); 619 _tagNameMap.put(TAG_SHARPNESS_FREQ_TABLE, "Sharpness Frequency Table"); 620 _tagNameMap.put(TAG_WHITE_BALANCE_TABLE, "White Balance Table"); 621 _tagNameMap.put(TAG_COLOR_BALANCE_ARRAY, "Color Balance Array"); 622 _tagNameMap.put(TAG_MEASURED_COLOR_ARRAY, "Measured Color Array"); 623 _tagNameMap.put(TAG_COLOR_TEMPERATURE, "Color Temperature"); 624 _tagNameMap.put(TAG_CANON_FLAGS_ARRAY, "Canon Flags Array"); 625 _tagNameMap.put(TAG_MODIFIED_INFO_ARRAY, "Modified Information Array"); 626 _tagNameMap.put(TAG_TONE_CURVE_MATCHING, "Tone Curve Matching"); 627 _tagNameMap.put(TAG_WHITE_BALANCE_MATCHING, "White Balance Matching"); 628 _tagNameMap.put(TAG_COLOR_SPACE, "Color Space"); 629 _tagNameMap.put(TAG_PREVIEW_IMAGE_INFO_ARRAY, "Preview Image Info Array"); 630 _tagNameMap.put(TAG_VRD_OFFSET, "VRD Offset"); 631 _tagNameMap.put(TAG_SENSOR_INFO_ARRAY, "Sensor Information Array"); 632 _tagNameMap.put(TAG_COLOR_DATA_ARRAY_2, "Color Data Array 1"); 633 _tagNameMap.put(TAG_COLOR_INFO_ARRAY_2, "Color Data Array 2"); 634 _tagNameMap.put(TAG_CUSTOM_PICTURE_STYLE_FILE_NAME, "Custom Picture Style File Name"); 635 _tagNameMap.put(TAG_COLOR_INFO_ARRAY, "Color Info Array"); 636 _tagNameMap.put(TAG_VIGNETTING_CORRECTION_ARRAY_1, "Vignetting Correction Array 1"); 637 _tagNameMap.put(TAG_VIGNETTING_CORRECTION_ARRAY_2, "Vignetting Correction Array 2"); 638 _tagNameMap.put(TAG_LIGHTING_OPTIMIZER_ARRAY, "Lighting Optimizer Array"); 639 _tagNameMap.put(TAG_LENS_INFO_ARRAY, "Lens Info Array"); 640 _tagNameMap.put(TAG_AMBIANCE_INFO_ARRAY, "Ambiance Info Array"); 641 _tagNameMap.put(TAG_FILTER_INFO_ARRAY, "Filter Info Array"); 398 642 } 399 643 400 644 public CanonMakernoteDirectory() … … 402 646 this.setDescriptor(new CanonMakernoteDescriptor(this)); 403 647 } 404 648 649 @NotNull 405 650 public String getName() 406 651 { 407 652 return "Canon Makernote"; 408 653 } 409 654 410 protected HashMap getTagNameMap() 655 @NotNull 656 protected HashMap<Integer, String> getTagNameMap() 411 657 { 412 658 return _tagNameMap; 413 659 } 414 660 415 /** 416 * We need special handling for selected tags. 417 * @param tagType 418 * @param ints 419 */ 420 public void setIntArray(int tagType, int[] ints) 661 @Override 662 public void setIntArray(int tagType, @NotNull int[] ints) 421 663 { 422 if (tagType == TAG_CANON_CAMERA_STATE_1) { 423 // this single tag has multiple values within 424 int subTagTypeBase = 0xC100; 425 // we intentionally skip the first array member 426 for (int i = 1; i < ints.length; i++) { 427 setInt(subTagTypeBase + i, ints[i]); 428 } 429 } else if (tagType == TAG_CANON_CAMERA_STATE_2) { 430 // this single tag has multiple values within 431 int subTagTypeBase = 0xC200; 432 // we intentionally skip the first array member 433 for (int i = 1; i < ints.length; i++) { 434 setInt(subTagTypeBase + i, ints[i]); 435 } 436 } if (tagType == TAG_CANON_CUSTOM_FUNCTIONS) { 437 // this single tag has multiple values within 438 int subTagTypeBase = 0xC300; 439 // we intentionally skip the first array member 440 for (int i = 1; i < ints.length; i++) { 441 setInt(subTagTypeBase + i + 1, ints[i] & 0x0F); 442 } 443 } else { 444 // no special handling... 445 super.setIntArray(tagType, ints); 664 // TODO is there some way to drop out 'null' or 'zero' values that are present in the array to reduce the noise? 665 666 // Certain Canon tags contain arrays of values that we split into 'fake' tags as each 667 // index in the array has its own meaning and decoding. 668 // Pick those tags out here and throw away the original array. 669 // Otherwise just add as usual. 670 switch (tagType) { 671 case TAG_CAMERA_SETTINGS_ARRAY: 672 for (int i = 0; i < ints.length; i++) 673 setInt(CameraSettings.OFFSET + i, ints[i]); 674 break; 675 case TAG_FOCAL_LENGTH_ARRAY: 676 for (int i = 0; i < ints.length; i++) 677 setInt(FocalLength.OFFSET + i, ints[i]); 678 break; 679 case TAG_SHOT_INFO_ARRAY: 680 for (int i = 0; i < ints.length; i++) 681 setInt(ShotInfo.OFFSET + i, ints[i]); 682 break; 683 case TAG_PANORAMA_ARRAY: 684 for (int i = 0; i < ints.length; i++) 685 setInt(Panorama.OFFSET + i, ints[i]); 686 break; 687 // TODO the interpretation of the custom functions tag depends upon the camera model 688 // case TAG_CANON_CUSTOM_FUNCTIONS_ARRAY: 689 // int subTagTypeBase = 0xC300; 690 // // we intentionally skip the first array member 691 // for (int i = 1; i < ints.length; i++) 692 // setInt(subTagTypeBase + i + 1, ints[i] & 0x0F); 693 // break; 694 case TAG_AF_INFO_ARRAY: 695 for (int i = 0; i < ints.length; i++) 696 setInt(AFInfo.OFFSET + i, ints[i]); 697 break; 698 default: 699 // no special handling... 700 super.setIntArray(tagType, ints); 701 break; 446 702 } 447 703 } 448 704 } -
src/com/drew/metadata/exif/CasioType1MakernoteDescriptor.java
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>CasioType1MakernoteDirectory</code>. 24 29 * 30 * @author Drew Noakes http://drewnoakes.com 25 31 */ 26 public class CasioType1MakernoteDescriptor extends TagDescriptor 32 public class CasioType1MakernoteDescriptor extends TagDescriptor<CasioType1MakernoteDirectory> 27 33 { 28 public CasioType1MakernoteDescriptor( Directory directory)34 public CasioType1MakernoteDescriptor(@NotNull CasioType1MakernoteDirectory 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 43 case CasioType1MakernoteDirectory.TAG_CASIO_RECORDING_MODE: … … 58 65 case CasioType1MakernoteDirectory.TAG_CASIO_CCD_SENSITIVITY: 59 66 return getCcdSensitivityDescription(); 60 67 default: 61 return _directory.getString(tagType);68 return super.getDescription(tagType); 62 69 } 63 70 } 64 71 65 public String getCcdSensitivityDescription() throws MetadataException 72 @Nullable 73 public String getCcdSensitivityDescription() 66 74 { 67 if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_CCD_SENSITIVITY)) return null; 68 int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_CCD_SENSITIVITY); 75 Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_CCD_SENSITIVITY); 76 77 if (value == null) 78 return null; 79 69 80 switch (value) { 70 81 // these four for QV3000 71 82 case 64: … … 86 97 } 87 98 } 88 99 89 public String getSaturationDescription() throws MetadataException 100 @Nullable 101 public String getSaturationDescription() 90 102 { 91 if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_SATURATION)) return null; 92 int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_SATURATION); 103 Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_SATURATION); 104 105 if (value == null) 106 return null; 107 93 108 switch (value) { 94 109 case 0: 95 110 return "Normal"; … … 102 117 } 103 118 } 104 119 105 public String getContrastDescription() throws MetadataException 120 @Nullable 121 public String getContrastDescription() 106 122 { 107 if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_CONTRAST)) return null; 108 int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_CONTRAST); 123 Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_CONTRAST); 124 125 if (value == null) 126 return null; 127 109 128 switch (value) { 110 129 case 0: 111 130 return "Normal"; … … 118 137 } 119 138 } 120 139 121 public String getSharpnessDescription() throws MetadataException 140 @Nullable 141 public String getSharpnessDescription() 122 142 { 123 if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_SHARPNESS)) return null; 124 int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_SHARPNESS); 143 Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_SHARPNESS); 144 145 if (value == null) 146 return null; 147 125 148 switch (value) { 126 149 case 0: 127 150 return "Normal"; … … 134 157 } 135 158 } 136 159 137 public String getDigitalZoomDescription() throws MetadataException 160 @Nullable 161 public String getDigitalZoomDescription() 138 162 { 139 if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_DIGITAL_ZOOM)) return null; 140 int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_DIGITAL_ZOOM); 163 Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_DIGITAL_ZOOM); 164 165 if (value == null) 166 return null; 167 141 168 switch (value) { 142 169 case 0x10000: 143 170 return "No digital zoom"; … … 152 179 } 153 180 } 154 181 155 public String getWhiteBalanceDescription() throws MetadataException 182 @Nullable 183 public String getWhiteBalanceDescription() 156 184 { 157 if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_WHITE_BALANCE)) return null; 158 int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_WHITE_BALANCE); 185 Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_WHITE_BALANCE); 186 187 if (value == null) 188 return null; 189 159 190 switch (value) { 160 191 case 1: 161 192 return "Auto"; … … 164 195 case 3: 165 196 return "Daylight"; 166 197 case 4: 167 return "Flo urescent";198 return "Florescent"; 168 199 case 5: 169 200 return "Shade"; 170 201 case 129: … … 174 205 } 175 206 } 176 207 177 public String getObjectDistanceDescription() throws MetadataException 208 @Nullable 209 public String getObjectDistanceDescription() 178 210 { 179 if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_OBJECT_DISTANCE)) return null; 180 int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_OBJECT_DISTANCE); 211 Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_OBJECT_DISTANCE); 212 213 if (value == null) 214 return null; 215 181 216 return value + " mm"; 182 217 } 183 218 184 public String getFlashIntensityDescription() throws MetadataException 219 @Nullable 220 public String getFlashIntensityDescription() 185 221 { 186 if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_INTENSITY)) return null; 187 int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_INTENSITY); 222 Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_INTENSITY); 223 224 if (value == null) 225 return null; 226 188 227 switch (value) { 189 228 case 11: 190 229 return "Weak"; … … 197 236 } 198 237 } 199 238 200 public String getFlashModeDescription() throws MetadataException 239 @Nullable 240 public String getFlashModeDescription() 201 241 { 202 if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_MODE)) return null; 203 int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_MODE); 242 Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_FLASH_MODE); 243 244 if (value == null) 245 return null; 246 204 247 switch (value) { 205 248 case 1: 206 249 return "Auto"; … … 217 260 } 218 261 } 219 262 220 public String getFocusingModeDescription() throws MetadataException 263 @Nullable 264 public String getFocusingModeDescription() 221 265 { 222 if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_FOCUSING_MODE)) return null; 223 int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_FOCUSING_MODE); 266 Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_FOCUSING_MODE); 267 268 if (value == null) 269 return null; 270 224 271 switch (value) { 225 272 case 2: 226 273 return "Macro"; … … 235 282 } 236 283 } 237 284 238 public String getQualityDescription() throws MetadataException 285 @Nullable 286 public String getQualityDescription() 239 287 { 240 if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_QUALITY)) return null; 241 int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_QUALITY); 288 Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_QUALITY); 289 290 if (value == null) 291 return null; 292 242 293 switch (value) { 243 294 case 1: 244 295 return "Economy"; … … 251 302 } 252 303 } 253 304 254 public String getRecordingModeDescription() throws MetadataException 305 @Nullable 306 public String getRecordingModeDescription() 255 307 { 256 if (!_directory.containsTag(CasioType1MakernoteDirectory.TAG_CASIO_RECORDING_MODE)) return null; 257 int value = _directory.getInt(CasioType1MakernoteDirectory.TAG_CASIO_RECORDING_MODE); 308 Integer value = _directory.getInteger(CasioType1MakernoteDirectory.TAG_CASIO_RECORDING_MODE); 309 310 if (value == null) 311 return null; 312 258 313 switch (value) { 259 314 case 1: 260 315 return "Single shutter"; -
src/com/drew/metadata/exif/CasioType1MakernoteDirectory.java
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 21 26 import java.util.HashMap; 22 27 23 28 /** 29 * Describes tags specific to Casio (type 1) cameras. 30 * 24 31 * A standard TIFF IFD directory but always uses Motorola (Big-Endian) Byte Alignment. 25 32 * Makernote data begins immediately (no header). 33 * 34 * @author Drew Noakes http://drewnoakes.com 26 35 */ 27 36 public class CasioType1MakernoteDirectory extends Directory 28 37 { … … 47 56 public static final int TAG_CASIO_UNKNOWN_8 = 0x0013; 48 57 public static final int TAG_CASIO_CCD_SENSITIVITY = 0x0014; 49 58 50 protected static final HashMap<Integer, String> tagNameMap = new HashMap<Integer, String>(); 59 @NotNull 60 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 51 61 52 62 static 53 63 { 54 tagNameMap.put(new Integer(TAG_CASIO_CCD_SENSITIVITY), "CCD Sensitivity");55 tagNameMap.put(new Integer(TAG_CASIO_CONTRAST), "Contrast");56 tagNameMap.put(new Integer(TAG_CASIO_DIGITAL_ZOOM), "Digital Zoom");57 tagNameMap.put(new Integer(TAG_CASIO_FLASH_INTENSITY), "Flash Intensity");58 tagNameMap.put(new Integer(TAG_CASIO_FLASH_MODE), "Flash Mode");59 tagNameMap.put(new Integer(TAG_CASIO_FOCUSING_MODE), "Focussing Mode");60 tagNameMap.put(new Integer(TAG_CASIO_OBJECT_DISTANCE), "Object Distance");61 tagNameMap.put(new Integer(TAG_CASIO_QUALITY), "Quality");62 tagNameMap.put(new Integer(TAG_CASIO_RECORDING_MODE), "Recording Mode");63 tagNameMap.put(new Integer(TAG_CASIO_SATURATION), "Saturation");64 tagNameMap.put(new Integer(TAG_CASIO_SHARPNESS), "Sharpness");65 tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_1), "Makernote Unknown 1");66 tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_2), "Makernote Unknown 2");67 tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_3), "Makernote Unknown 3");68 tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_4), "Makernote Unknown 4");69 tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_5), "Makernote Unknown 5");70 tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_6), "Makernote Unknown 6");71 tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_7), "Makernote Unknown 7");72 tagNameMap.put(new Integer(TAG_CASIO_UNKNOWN_8), "Makernote Unknown 8");73 tagNameMap.put(new Integer(TAG_CASIO_WHITE_BALANCE), "White Balance");64 _tagNameMap.put(TAG_CASIO_CCD_SENSITIVITY, "CCD Sensitivity"); 65 _tagNameMap.put(TAG_CASIO_CONTRAST, "Contrast"); 66 _tagNameMap.put(TAG_CASIO_DIGITAL_ZOOM, "Digital Zoom"); 67 _tagNameMap.put(TAG_CASIO_FLASH_INTENSITY, "Flash Intensity"); 68 _tagNameMap.put(TAG_CASIO_FLASH_MODE, "Flash Mode"); 69 _tagNameMap.put(TAG_CASIO_FOCUSING_MODE, "Focusing Mode"); 70 _tagNameMap.put(TAG_CASIO_OBJECT_DISTANCE, "Object Distance"); 71 _tagNameMap.put(TAG_CASIO_QUALITY, "Quality"); 72 _tagNameMap.put(TAG_CASIO_RECORDING_MODE, "Recording Mode"); 73 _tagNameMap.put(TAG_CASIO_SATURATION, "Saturation"); 74 _tagNameMap.put(TAG_CASIO_SHARPNESS, "Sharpness"); 75 _tagNameMap.put(TAG_CASIO_UNKNOWN_1, "Makernote Unknown 1"); 76 _tagNameMap.put(TAG_CASIO_UNKNOWN_2, "Makernote Unknown 2"); 77 _tagNameMap.put(TAG_CASIO_UNKNOWN_3, "Makernote Unknown 3"); 78 _tagNameMap.put(TAG_CASIO_UNKNOWN_4, "Makernote Unknown 4"); 79 _tagNameMap.put(TAG_CASIO_UNKNOWN_5, "Makernote Unknown 5"); 80 _tagNameMap.put(TAG_CASIO_UNKNOWN_6, "Makernote Unknown 6"); 81 _tagNameMap.put(TAG_CASIO_UNKNOWN_7, "Makernote Unknown 7"); 82 _tagNameMap.put(TAG_CASIO_UNKNOWN_8, "Makernote Unknown 8"); 83 _tagNameMap.put(TAG_CASIO_WHITE_BALANCE, "White Balance"); 74 84 } 75 85 76 86 public CasioType1MakernoteDirectory() … … 78 88 this.setDescriptor(new CasioType1MakernoteDescriptor(this)); 79 89 } 80 90 91 @NotNull 81 92 public String getName() 82 93 { 83 94 return "Casio Makernote"; 84 95 } 85 96 86 protected HashMap getTagNameMap() 97 @NotNull 98 protected HashMap<Integer, String> getTagNameMap() 87 99 { 88 return tagNameMap;100 return _tagNameMap; 89 101 } 90 102 } -
src/com/drew/metadata/exif/CasioType2MakernoteDescriptor.java
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>CasioType2MakernoteDirectory</code>. 24 29 * 30 * @author Drew Noakes http://drewnoakes.com 25 31 */ 26 public class CasioType2MakernoteDescriptor extends TagDescriptor 32 public class CasioType2MakernoteDescriptor extends TagDescriptor<CasioType2MakernoteDirectory> 27 33 { 28 public CasioType2MakernoteDescriptor( Directory directory)34 public CasioType2MakernoteDescriptor(@NotNull CasioType2MakernoteDirectory 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 43 case CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS: … … 90 97 case CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FILTER: 91 98 return getFilterDescription(); 92 99 default: 93 return _directory.getString(tagType);100 return super.getDescription(tagType); 94 101 } 95 102 } 96 103 97 public String getFilterDescription() throws MetadataException 104 @Nullable 105 public String getFilterDescription() 98 106 { 99 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FILTER)) return null; 100 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FILTER); 107 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FILTER); 108 if (value==null) 109 return null; 101 110 switch (value) { 102 111 case 0: 103 112 return "Off"; … … 106 115 } 107 116 } 108 117 109 public String getEnhancementDescription() throws MetadataException 118 @Nullable 119 public String getEnhancementDescription() 110 120 { 111 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ENHANCEMENT)) return null; 112 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ENHANCEMENT); 121 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ENHANCEMENT); 122 if (value==null) 123 return null; 113 124 switch (value) { 114 125 case 0: 115 126 return "Off"; … … 118 129 } 119 130 } 120 131 121 public String getColourModeDescription() throws MetadataException 132 @Nullable 133 public String getColourModeDescription() 122 134 { 123 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_COLOUR_MODE)) return null; 124 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_COLOUR_MODE); 135 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_COLOUR_MODE); 136 if (value==null) 137 return null; 125 138 switch (value) { 126 139 case 0: 127 140 return "Off"; … … 130 143 } 131 144 } 132 145 133 public String getCcdIsoSensitivityDescription() throws MetadataException 146 @Nullable 147 public String getCcdIsoSensitivityDescription() 134 148 { 135 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CCD_ISO_SENSITIVITY)) return null; 136 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CCD_ISO_SENSITIVITY); 149 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CCD_ISO_SENSITIVITY); 150 if (value==null) 151 return null; 137 152 switch (value) { 138 153 case 0: 139 154 return "Off"; … … 144 159 } 145 160 } 146 161 147 public String getBestShotModeDescription() throws MetadataException 162 @Nullable 163 public String getBestShotModeDescription() 148 164 { 149 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_BESTSHOT_MODE)) return null; 150 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_BESTSHOT_MODE); 165 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_BESTSHOT_MODE); 166 if (value==null) 167 return null; 151 168 switch (value) { 152 169 default: 153 170 return "Unknown (" + value + ")"; 154 171 } 155 172 } 156 173 174 @Nullable 157 175 public String getTimeZoneDescription() 158 176 { 159 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_TIME_ZONE)) return null;160 177 return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_TIME_ZONE); 161 178 } 162 179 163 public String getFocusMode2Description() throws MetadataException 180 @Nullable 181 public String getFocusMode2Description() 164 182 { 165 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_2)) return null; 166 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_2); 183 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_2); 184 if (value==null) 185 return null; 167 186 switch (value) { 168 187 case 1: 169 188 return "Fixation"; … … 174 193 } 175 194 } 176 195 177 public String getQualityDescription() throws MetadataException 196 @Nullable 197 public String getQualityDescription() 178 198 { 179 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY)) return null; 180 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY); 199 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY); 200 if (value==null) 201 return null; 181 202 switch (value) { 182 203 case 3: 183 204 return "Fine"; … … 186 207 } 187 208 } 188 209 189 public String getSelfTimerDescription() throws MetadataException 210 @Nullable 211 public String getSelfTimerDescription() 190 212 { 191 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SELF_TIMER)) return null; 192 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SELF_TIMER); 213 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SELF_TIMER); 214 if (value==null) 215 return null; 193 216 switch (value) { 194 217 case 1: 195 218 return "Off"; … … 198 221 } 199 222 } 200 223 201 public String getRecordModeDescription() throws MetadataException 224 @Nullable 225 public String getRecordModeDescription() 202 226 { 203 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_RECORD_MODE)) return null; 204 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_RECORD_MODE); 227 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_RECORD_MODE); 228 if (value==null) 229 return null; 205 230 switch (value) { 206 231 case 2: 207 232 return "Normal"; … … 210 235 } 211 236 } 212 237 213 public String getFlashDistanceDescription() throws MetadataException 238 @Nullable 239 public String getFlashDistanceDescription() 214 240 { 215 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FLASH_DISTANCE)) return null; 216 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FLASH_DISTANCE); 241 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FLASH_DISTANCE); 242 if (value==null) 243 return null; 217 244 switch (value) { 218 245 case 0: 219 246 return "Off"; … … 222 249 } 223 250 } 224 251 225 public String getObjectDistanceDescription() throws MetadataException 252 @Nullable 253 public String getObjectDistanceDescription() 226 254 { 227 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_OBJECT_DISTANCE)) return null; 228 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_OBJECT_DISTANCE); 255 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_OBJECT_DISTANCE); 256 if (value==null) 257 return null; 229 258 return Integer.toString(value) + " mm"; 230 259 } 231 260 232 public String getWhiteBalance2Description() throws MetadataException 261 @Nullable 262 public String getWhiteBalance2Description() 233 263 { 234 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_2)) return null; 235 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_2); 264 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_2); 265 if (value==null) 266 return null; 236 267 switch (value) { 237 268 case 0: 238 269 return "Manual"; … … 247 278 } 248 279 } 249 280 281 @Nullable 250 282 public String getWhiteBalanceBiasDescription() 251 283 { 252 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_BIAS)) return null;253 284 return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_BIAS); 254 285 } 255 286 256 public String getCasioPreviewThumbnailDescription() throws MetadataException 287 @Nullable 288 public String getCasioPreviewThumbnailDescription() 257 289 { 258 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CASIO_PREVIEW_THUMBNAIL)) return null;259 290 final byte[] bytes = _directory.getByteArray(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CASIO_PREVIEW_THUMBNAIL); 291 if (bytes==null) 292 return null; 260 293 return "<" + bytes.length + " bytes of image data>"; 261 294 } 262 295 296 @Nullable 263 297 public String getPrintImageMatchingInfoDescription() 264 298 { 265 299 // TODO research PIM specification http://www.ozhiker.com/electronics/pjmt/jpeg_info/pim.html 266 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_PRINT_IMAGE_MATCHING_INFO)) return null;267 300 return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_PRINT_IMAGE_MATCHING_INFO); 268 301 } 269 302 270 public String getSharpnessDescription() throws MetadataException 303 @Nullable 304 public String getSharpnessDescription() 271 305 { 272 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SHARPNESS)) return null; 273 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SHARPNESS); 306 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SHARPNESS); 307 if (value==null) 308 return null; 274 309 switch (value) { 275 310 case 0: 276 311 return "-1"; … … 283 318 } 284 319 } 285 320 286 public String getContrastDescription() throws MetadataException 321 @Nullable 322 public String getContrastDescription() 287 323 { 288 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CONTRAST)) return null; 289 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CONTRAST); 324 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_CONTRAST); 325 if (value==null) 326 return null; 290 327 switch (value) { 291 328 case 0: 292 329 return "-1"; … … 299 336 } 300 337 } 301 338 302 public String getSaturationDescription() throws MetadataException 339 @Nullable 340 public String getSaturationDescription() 303 341 { 304 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SATURATION)) return null; 305 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SATURATION); 342 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_SATURATION); 343 if (value==null) 344 return null; 306 345 switch (value) { 307 346 case 0: 308 347 return "-1"; … … 315 354 } 316 355 } 317 356 318 public String getFocalLengthDescription() throws MetadataException 357 @Nullable 358 public String getFocalLengthDescription() 319 359 { 320 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCAL_LENGTH)) return null; 321 double value = _directory.getDouble(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCAL_LENGTH); 360 Double value = _directory.getDoubleObject(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCAL_LENGTH); 361 if (value==null) 362 return null; 322 363 return Double.toString(value / 10d) + " mm"; 323 364 } 324 365 325 public String getWhiteBalance1Description() throws MetadataException 366 @Nullable 367 public String getWhiteBalance1Description() 326 368 { 327 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_1)) return null; 328 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_1); 369 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_WHITE_BALANCE_1); 370 if (value==null) 371 return null; 329 372 switch (value) { 330 373 case 0: 331 374 return "Auto"; … … 336 379 case 3: 337 380 return "Tungsten"; 338 381 case 4: 339 return "Flo urescent";382 return "Florescent"; 340 383 case 5: 341 384 return "Manual"; 342 385 default: … … 344 387 } 345 388 } 346 389 347 public String getIsoSensitivityDescription() throws MetadataException 390 @Nullable 391 public String getIsoSensitivityDescription() 348 392 { 349 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ISO_SENSITIVITY)) return null; 350 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ISO_SENSITIVITY); 393 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_ISO_SENSITIVITY); 394 if (value==null) 395 return null; 351 396 switch (value) { 352 397 case 3: 353 398 return "50"; … … 362 407 } 363 408 } 364 409 365 public String getFocusMode1Description() throws MetadataException 410 @Nullable 411 public String getFocusMode1Description() 366 412 { 367 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_1)) return null; 368 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_1); 413 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_FOCUS_MODE_1); 414 if (value==null) 415 return null; 369 416 switch (value) { 370 417 case 0: 371 418 return "Normal"; … … 376 423 } 377 424 } 378 425 379 public String getImageSizeDescription() throws MetadataException 426 @Nullable 427 public String getImageSizeDescription() 380 428 { 381 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_IMAGE_SIZE)) return null; 382 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_IMAGE_SIZE); 429 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_IMAGE_SIZE); 430 if (value==null) 431 return null; 383 432 switch (value) { 384 433 case 0: return "640 x 480 pixels"; 385 434 case 4: return "1600 x 1200 pixels"; … … 392 441 } 393 442 } 394 443 395 public String getQualityModeDescription() throws MetadataException 444 @Nullable 445 public String getQualityModeDescription() 396 446 { 397 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY_MODE)) return null; 398 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY_MODE); 447 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_QUALITY_MODE); 448 if (value==null) 449 return null; 399 450 switch (value) { 400 451 case 1: 401 452 return "Fine"; … … 406 457 } 407 458 } 408 459 460 @Nullable 409 461 public String getThumbnailOffsetDescription() 410 462 { 411 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_OFFSET)) return null;412 463 return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_OFFSET); 413 464 } 414 465 415 public String getThumbnailSizeDescription() throws MetadataException 466 @Nullable 467 public String getThumbnailSizeDescription() 416 468 { 417 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_SIZE)) return null; 418 int value = _directory.getInt(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_SIZE); 469 Integer value = _directory.getInteger(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_SIZE); 470 if (value==null) 471 return null; 419 472 return Integer.toString(value) + " bytes"; 420 473 } 421 474 422 public String getThumbnailDimensionsDescription() throws MetadataException 475 @Nullable 476 public String getThumbnailDimensionsDescription() 423 477 { 424 if (!_directory.containsTag(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS)) return null;425 478 int[] dimensions = _directory.getIntArray(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS); 426 if (dimensions .length!=2)479 if (dimensions==null || dimensions.length!=2) 427 480 return _directory.getString(CasioType2MakernoteDirectory.TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS); 428 481 return dimensions[0] + " x " + dimensions[1] + " pixels"; 429 482 } 430 } 431 Pas de retour chariot à la fin du fichier 483 } -
src/com/drew/metadata/exif/CasioType2MakernoteDirectory.java
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 21 26 import java.util.HashMap; 22 27 23 28 /** 29 * Describes tags specific to Casio (type 2) cameras. 30 * 24 31 * A standard TIFF IFD directory but always uses Motorola (Big-Endian) Byte Alignment. 25 32 * Makernote data begins after a 6-byte header: "QVC\x00\x00\x00" 33 * 34 * @author Drew Noakes http://drewnoakes.com 26 35 */ 27 36 public class CasioType2MakernoteDirectory extends Directory 28 37 { … … 166 175 */ 167 176 public static final int TAG_CASIO_TYPE2_FILTER = 0x3017; 168 177 169 protected static final HashMap tagNameMap = new HashMap(); 178 @NotNull 179 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 170 180 171 181 static 172 182 { 173 // TODO add names174 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS), "Thumbnail Dimensions");175 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_THUMBNAIL_SIZE), "Thumbnail Size");176 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_THUMBNAIL_OFFSET), "Thumbnail Offset");177 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_QUALITY_MODE), "Quality Mode");178 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_IMAGE_SIZE), "Image Size");179 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_FOCUS_MODE_1), "Focus Mode");180 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_ISO_SENSITIVITY), "ISO Sensitivity");181 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_WHITE_BALANCE_1), "White Balance");182 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_FOCAL_LENGTH), "Focal Length");183 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_SATURATION), "Saturation");184 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_CONTRAST), "Contrast");185 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_SHARPNESS), "Sharpness");186 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_PRINT_IMAGE_MATCHING_INFO), "Print Image Matching (PIM) Info");187 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_CASIO_PREVIEW_THUMBNAIL), "Casio Preview Thumbnail");188 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_WHITE_BALANCE_BIAS), "White Balance Bias");189 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_WHITE_BALANCE_2), "White Balance");190 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_OBJECT_DISTANCE), "Object Distance");191 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_FLASH_DISTANCE), "Flash Distance");192 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_RECORD_MODE), "Record Mode");193 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_SELF_TIMER), "Self Timer");194 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_QUALITY), "Quality");195 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_FOCUS_MODE_2), "Focus Mode");196 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_TIME_ZONE), "Time Zone");197 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_BESTSHOT_MODE), "BestShot Mode");198 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_CCD_ISO_SENSITIVITY), "CCD ISO Sensitivity");199 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_COLOUR_MODE), "Colour Mode");200 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_ENHANCEMENT), "Enhancement");201 tagNameMap.put(new Integer(TAG_CASIO_TYPE2_FILTER), "Filter");183 // TODO add missing names 184 _tagNameMap.put(TAG_CASIO_TYPE2_THUMBNAIL_DIMENSIONS, "Thumbnail Dimensions"); 185 _tagNameMap.put(TAG_CASIO_TYPE2_THUMBNAIL_SIZE, "Thumbnail Size"); 186 _tagNameMap.put(TAG_CASIO_TYPE2_THUMBNAIL_OFFSET, "Thumbnail Offset"); 187 _tagNameMap.put(TAG_CASIO_TYPE2_QUALITY_MODE, "Quality Mode"); 188 _tagNameMap.put(TAG_CASIO_TYPE2_IMAGE_SIZE, "Image Size"); 189 _tagNameMap.put(TAG_CASIO_TYPE2_FOCUS_MODE_1, "Focus Mode"); 190 _tagNameMap.put(TAG_CASIO_TYPE2_ISO_SENSITIVITY, "ISO Sensitivity"); 191 _tagNameMap.put(TAG_CASIO_TYPE2_WHITE_BALANCE_1, "White Balance"); 192 _tagNameMap.put(TAG_CASIO_TYPE2_FOCAL_LENGTH, "Focal Length"); 193 _tagNameMap.put(TAG_CASIO_TYPE2_SATURATION, "Saturation"); 194 _tagNameMap.put(TAG_CASIO_TYPE2_CONTRAST, "Contrast"); 195 _tagNameMap.put(TAG_CASIO_TYPE2_SHARPNESS, "Sharpness"); 196 _tagNameMap.put(TAG_CASIO_TYPE2_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info"); 197 _tagNameMap.put(TAG_CASIO_TYPE2_CASIO_PREVIEW_THUMBNAIL, "Casio Preview Thumbnail"); 198 _tagNameMap.put(TAG_CASIO_TYPE2_WHITE_BALANCE_BIAS, "White Balance Bias"); 199 _tagNameMap.put(TAG_CASIO_TYPE2_WHITE_BALANCE_2, "White Balance"); 200 _tagNameMap.put(TAG_CASIO_TYPE2_OBJECT_DISTANCE, "Object Distance"); 201 _tagNameMap.put(TAG_CASIO_TYPE2_FLASH_DISTANCE, "Flash Distance"); 202 _tagNameMap.put(TAG_CASIO_TYPE2_RECORD_MODE, "Record Mode"); 203 _tagNameMap.put(TAG_CASIO_TYPE2_SELF_TIMER, "Self Timer"); 204 _tagNameMap.put(TAG_CASIO_TYPE2_QUALITY, "Quality"); 205 _tagNameMap.put(TAG_CASIO_TYPE2_FOCUS_MODE_2, "Focus Mode"); 206 _tagNameMap.put(TAG_CASIO_TYPE2_TIME_ZONE, "Time Zone"); 207 _tagNameMap.put(TAG_CASIO_TYPE2_BESTSHOT_MODE, "BestShot Mode"); 208 _tagNameMap.put(TAG_CASIO_TYPE2_CCD_ISO_SENSITIVITY, "CCD ISO Sensitivity"); 209 _tagNameMap.put(TAG_CASIO_TYPE2_COLOUR_MODE, "Colour Mode"); 210 _tagNameMap.put(TAG_CASIO_TYPE2_ENHANCEMENT, "Enhancement"); 211 _tagNameMap.put(TAG_CASIO_TYPE2_FILTER, "Filter"); 202 212 } 203 213 204 214 public CasioType2MakernoteDirectory() … … 206 216 this.setDescriptor(new CasioType2MakernoteDescriptor(this)); 207 217 } 208 218 219 @NotNull 209 220 public String getName() 210 221 { 211 222 return "Casio Makernote"; 212 223 } 213 224 214 protected HashMap getTagNameMap() 225 @NotNull 226 protected HashMap<Integer, String> getTagNameMap() 215 227 { 216 return tagNameMap;228 return _tagNameMap; 217 229 } 218 230 } -
src/com/drew/metadata/exif/DataFormat.java
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/ 14 */ 15 package com.drew.metadata.exif; 16 17 import com.drew.metadata.MetadataException; 18 19 /** 20 * An enumeration of data formats used in the TIFF IFDs. 21 */ 22 public class DataFormat 23 { 24 public static final DataFormat BYTE = new DataFormat("BYTE", 1); 25 public static final DataFormat STRING = new DataFormat("STRING", 2); 26 public static final DataFormat USHORT = new DataFormat("USHORT", 3); 27 public static final DataFormat ULONG = new DataFormat("ULONG", 4); 28 public static final DataFormat URATIONAL = new DataFormat("URATIONAL", 5); 29 public static final DataFormat SBYTE = new DataFormat("SBYTE", 6); 30 public static final DataFormat UNDEFINED = new DataFormat("UNDEFINED", 7); 31 public static final DataFormat SSHORT = new DataFormat("SSHORT", 8); 32 public static final DataFormat SLONG = new DataFormat("SLONG", 9); 33 public static final DataFormat SRATIONAL = new DataFormat("SRATIONAL", 10); 34 public static final DataFormat SINGLE = new DataFormat("SINGLE", 11); 35 public static final DataFormat DOUBLE = new DataFormat("DOUBLE", 12); 36 37 private final String myName; 38 private final int value; 39 40 public static DataFormat fromValue(int value) throws MetadataException 41 { 42 switch (value) 43 { 44 case 1: return BYTE; 45 case 2: return STRING; 46 case 3: return USHORT; 47 case 4: return ULONG; 48 case 5: return URATIONAL; 49 case 6: return SBYTE; 50 case 7: return UNDEFINED; 51 case 8: return SSHORT; 52 case 9: return SLONG; 53 case 10: return SRATIONAL; 54 case 11: return SINGLE; 55 case 12: return DOUBLE; 56 } 57 58 throw new MetadataException("value '"+value+"' does not represent a known data format."); 59 } 60 61 private DataFormat(String name, int value) 62 { 63 myName = name; 64 this.value = value; 65 } 66 67 public int getValue() 68 { 69 return value; 70 } 71 72 public String toString() 73 { 74 return myName; 75 } 76 } 1 /* 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/ 20 */ 21 package com.drew.metadata.exif; 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.metadata.MetadataException; 25 26 /** 27 * An enumeration of data formats used in the TIFF IFDs. 28 * 29 * @author Drew Noakes http://drewnoakes.com 30 */ 31 public class DataFormat 32 { 33 @NotNull public static final DataFormat BYTE = new DataFormat("BYTE", 1); 34 @NotNull public static final DataFormat STRING = new DataFormat("STRING", 2); 35 @NotNull public static final DataFormat USHORT = new DataFormat("USHORT", 3); 36 @NotNull public static final DataFormat ULONG = new DataFormat("ULONG", 4); 37 @NotNull public static final DataFormat URATIONAL = new DataFormat("URATIONAL", 5); 38 @NotNull public static final DataFormat SBYTE = new DataFormat("SBYTE", 6); 39 @NotNull public static final DataFormat UNDEFINED = new DataFormat("UNDEFINED", 7); 40 @NotNull public static final DataFormat SSHORT = new DataFormat("SSHORT", 8); 41 @NotNull public static final DataFormat SLONG = new DataFormat("SLONG", 9); 42 @NotNull public static final DataFormat SRATIONAL = new DataFormat("SRATIONAL", 10); 43 @NotNull public static final DataFormat SINGLE = new DataFormat("SINGLE", 11); 44 @NotNull public static final DataFormat DOUBLE = new DataFormat("DOUBLE", 12); 45 46 @NotNull private final String _name; 47 private final int _value; 48 49 @NotNull 50 public static DataFormat fromValue(int value) throws MetadataException 51 { 52 switch (value) 53 { 54 case 1: return BYTE; 55 case 2: return STRING; 56 case 3: return USHORT; 57 case 4: return ULONG; 58 case 5: return URATIONAL; 59 case 6: return SBYTE; 60 case 7: return UNDEFINED; 61 case 8: return SSHORT; 62 case 9: return SLONG; 63 case 10: return SRATIONAL; 64 case 11: return SINGLE; 65 case 12: return DOUBLE; 66 } 67 68 throw new MetadataException("value '"+value+"' does not represent a known data format."); 69 } 70 71 private DataFormat(@NotNull String name, int value) 72 { 73 _name = name; 74 _value = value; 75 } 76 77 public int getValue() 78 { 79 return _value; 80 } 81 82 @NotNull 83 public String toString() 84 { 85 return _name; 86 } 87 } -
src/com/drew/metadata/exif/ExifDescriptor.java
1 /*2 * ExifDescriptor.java3 *4 * This is public domain software - that is, you can do whatever you want5 * with it, and include it software that is licensed under the GNU or the6 * BSD license, or whatever other licence you choose, including proprietary7 * closed source licenses. I do ask that you leave this header in tact.8 *9 * If you make modifications to this code that you think would benefit the10 * wider community, please send me a copy and I'll post it on my site.11 *12 * If you make use of this code, I'd appreciate hearing about it.13 * drew@drewnoakes.com14 * Latest version of this software kept at15 * http://drewnoakes.com/16 *17 * Created by dnoakes on 12-Nov-2002 22:27:15 using IntelliJ IDEA.18 */19 package com.drew.metadata.exif;20 21 import com.drew.imaging.PhotographicConversions;22 import com.drew.lang.Rational;23 import com.drew.metadata.Directory;24 import com.drew.metadata.MetadataException;25 import com.drew.metadata.TagDescriptor;26 27 import java.io.UnsupportedEncodingException;28 import java.text.DecimalFormat;29 30 /**31 * Contains all logic for the presentation of raw Exif data, as stored in ExifDirectory. Use32 * this class to provide human-readable descriptions of tag values.33 */34 public class ExifDescriptor extends TagDescriptor35 {36 /**37 * Dictates whether rational values will be represented in decimal format in instances38 * where decimal notation is elegant (such as 1/2 -> 0.5, but not 1/3).39 */40 private boolean _allowDecimalRepresentationOfRationals = true;41 42 private static final java.text.DecimalFormat SimpleDecimalFormatter = new DecimalFormat("0.#");43 44 public ExifDescriptor(Directory directory)45 {46 super(directory);47 }48 49 // Note for the potential addition of brightness presentation in eV:50 // Brightness of taken subject. To calculate Exposure(Ev) from BrigtnessValue(Bv),51 // you must add SensitivityValue(Sv).52 // Ev=BV+Sv Sv=log2(ISOSpeedRating/3.125)53 // ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32.54 55 /**56 * Returns a descriptive value of the the specified tag for this image.57 * Where possible, known values will be substituted here in place of the raw58 * tokens actually kept in the Exif segment. If no substitution is59 * available, the value provided by getString(int) will be returned.60 * @param tagType the tag to find a description for61 * @return a description of the image's value for the specified tag, or62 * <code>null</code> if the tag hasn't been defined.63 */64 public String getDescription(int tagType) throws MetadataException65 {66 switch (tagType) {67 case ExifDirectory.TAG_ORIENTATION:68 return getOrientationDescription();69 case ExifDirectory.TAG_NEW_SUBFILE_TYPE:70 return getNewSubfileTypeDescription();71 case ExifDirectory.TAG_SUBFILE_TYPE:72 return getSubfileTypeDescription();73 case ExifDirectory.TAG_THRESHOLDING:74 return getThresholdingDescription();75 case ExifDirectory.TAG_FILL_ORDER:76 return getFillOrderDescription();77 case ExifDirectory.TAG_RESOLUTION_UNIT:78 return getResolutionDescription();79 case ExifDirectory.TAG_YCBCR_POSITIONING:80 return getYCbCrPositioningDescription();81 case ExifDirectory.TAG_EXPOSURE_TIME:82 return getExposureTimeDescription();83 case ExifDirectory.TAG_SHUTTER_SPEED:84 return getShutterSpeedDescription();85 case ExifDirectory.TAG_FNUMBER:86 return getFNumberDescription();87 case ExifDirectory.TAG_X_RESOLUTION:88 return getXResolutionDescription();89 case ExifDirectory.TAG_Y_RESOLUTION:90 return getYResolutionDescription();91 case ExifDirectory.TAG_THUMBNAIL_OFFSET:92 return getThumbnailOffsetDescription();93 case ExifDirectory.TAG_THUMBNAIL_LENGTH:94 return getThumbnailLengthDescription();95 case ExifDirectory.TAG_COMPRESSION_LEVEL:96 return getCompressionLevelDescription();97 case ExifDirectory.TAG_SUBJECT_DISTANCE:98 return getSubjectDistanceDescription();99 case ExifDirectory.TAG_METERING_MODE:100 return getMeteringModeDescription();101 case ExifDirectory.TAG_WHITE_BALANCE:102 return getWhiteBalanceDescription();103 case ExifDirectory.TAG_FLASH:104 return getFlashDescription();105 case ExifDirectory.TAG_FOCAL_LENGTH:106 return getFocalLengthDescription();107 case ExifDirectory.TAG_COLOR_SPACE:108 return getColorSpaceDescription();109 case ExifDirectory.TAG_EXIF_IMAGE_WIDTH:110 return getExifImageWidthDescription();111 case ExifDirectory.TAG_EXIF_IMAGE_HEIGHT:112 return getExifImageHeightDescription();113 case ExifDirectory.TAG_FOCAL_PLANE_UNIT:114 return getFocalPlaneResolutionUnitDescription();115 case ExifDirectory.TAG_FOCAL_PLANE_X_RES:116 return getFocalPlaneXResolutionDescription();117 case ExifDirectory.TAG_FOCAL_PLANE_Y_RES:118 return getFocalPlaneYResolutionDescription();119 case ExifDirectory.TAG_THUMBNAIL_IMAGE_WIDTH:120 return getThumbnailImageWidthDescription();121 case ExifDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT:122 return getThumbnailImageHeightDescription();123 case ExifDirectory.TAG_BITS_PER_SAMPLE:124 return getBitsPerSampleDescription();125 case ExifDirectory.TAG_COMPRESSION:126 return getCompressionDescription();127 case ExifDirectory.TAG_PHOTOMETRIC_INTERPRETATION:128 return getPhotometricInterpretationDescription();129 case ExifDirectory.TAG_ROWS_PER_STRIP:130 return getRowsPerStripDescription();131 case ExifDirectory.TAG_STRIP_BYTE_COUNTS:132 return getStripByteCountsDescription();133 case ExifDirectory.TAG_SAMPLES_PER_PIXEL:134 return getSamplesPerPixelDescription();135 case ExifDirectory.TAG_PLANAR_CONFIGURATION:136 return getPlanarConfigurationDescription();137 case ExifDirectory.TAG_YCBCR_SUBSAMPLING:138 return getYCbCrSubsamplingDescription();139 case ExifDirectory.TAG_EXPOSURE_PROGRAM:140 return getExposureProgramDescription();141 case ExifDirectory.TAG_APERTURE:142 return getApertureValueDescription();143 case ExifDirectory.TAG_MAX_APERTURE:144 return getMaxApertureValueDescription();145 case ExifDirectory.TAG_SENSING_METHOD:146 return getSensingMethodDescription();147 case ExifDirectory.TAG_EXPOSURE_BIAS:148 return getExposureBiasDescription();149 case ExifDirectory.TAG_FILE_SOURCE:150 return getFileSourceDescription();151 case ExifDirectory.TAG_SCENE_TYPE:152 return getSceneTypeDescription();153 case ExifDirectory.TAG_COMPONENTS_CONFIGURATION:154 return getComponentConfigurationDescription();155 case ExifDirectory.TAG_EXIF_VERSION:156 return getExifVersionDescription();157 case ExifDirectory.TAG_FLASHPIX_VERSION:158 return getFlashPixVersionDescription();159 case ExifDirectory.TAG_REFERENCE_BLACK_WHITE:160 return getReferenceBlackWhiteDescription();161 case ExifDirectory.TAG_ISO_EQUIVALENT:162 return getIsoEquivalentDescription();163 case ExifDirectory.TAG_THUMBNAIL_DATA:164 return getThumbnailDescription();165 case ExifDirectory.TAG_USER_COMMENT:166 return getUserCommentDescription();167 case ExifDirectory.TAG_CUSTOM_RENDERED:168 return getCustomRenderedDescription();169 case ExifDirectory.TAG_EXPOSURE_MODE:170 return getExposureModeDescription();171 case ExifDirectory.TAG_WHITE_BALANCE_MODE:172 return getWhiteBalanceModeDescription();173 case ExifDirectory.TAG_DIGITAL_ZOOM_RATIO:174 return getDigitalZoomRatioDescription();175 case ExifDirectory.TAG_35MM_FILM_EQUIV_FOCAL_LENGTH:176 return get35mmFilmEquivFocalLengthDescription();177 case ExifDirectory.TAG_SCENE_CAPTURE_TYPE:178 return getSceneCaptureTypeDescription();179 case ExifDirectory.TAG_GAIN_CONTROL:180 return getGainControlDescription();181 case ExifDirectory.TAG_CONTRAST:182 return getContrastDescription();183 case ExifDirectory.TAG_SATURATION:184 return getSaturationDescription();185 case ExifDirectory.TAG_SHARPNESS:186 return getSharpnessDescription();187 case ExifDirectory.TAG_SUBJECT_DISTANCE_RANGE:188 return getSubjectDistanceRangeDescription();189 190 case ExifDirectory.TAG_WIN_AUTHOR:191 return getWindowsAuthorDescription();192 case ExifDirectory.TAG_WIN_COMMENT:193 return getWindowsCommentDescription();194 case ExifDirectory.TAG_WIN_KEYWORDS:195 return getWindowsKeywordsDescription();196 case ExifDirectory.TAG_WIN_SUBJECT:197 return getWindowsSubjectDescription();198 case ExifDirectory.TAG_WIN_TITLE:199 return getWindowsTitleDescription();200 default:201 return _directory.getString(tagType);202 }203 }204 205 public String getNewSubfileTypeDescription() throws MetadataException206 {207 if (!_directory.containsTag(ExifDirectory.TAG_NEW_SUBFILE_TYPE)) return null;208 switch (_directory.getInt(ExifDirectory.TAG_NEW_SUBFILE_TYPE)) {209 case 1: return "Full-resolution image";210 case 2: return "Reduced-resolution image";211 case 3: return "Single page of multi-page reduced-resolution image";212 case 4: return "Transparency mask";213 case 5: return "Transparency mask of reduced-resolution image";214 case 6: return "Transparency mask of multi-page image";215 case 7: return "Transparency mask of reduced-resolution multi-page image";216 default:217 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_NEW_SUBFILE_TYPE) + ")";218 }219 }220 221 public String getSubfileTypeDescription() throws MetadataException222 {223 if (!_directory.containsTag(ExifDirectory.TAG_SUBFILE_TYPE)) return null;224 switch (_directory.getInt(ExifDirectory.TAG_SUBFILE_TYPE)) {225 case 1: return "Full-resolution image";226 case 2: return "Reduced-resolution image";227 case 3: return "Single page of multi-page image";228 default:229 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_SUBFILE_TYPE) + ")";230 }231 }232 233 public String getThresholdingDescription() throws MetadataException234 {235 if (!_directory.containsTag(ExifDirectory.TAG_THRESHOLDING)) return null;236 switch (_directory.getInt(ExifDirectory.TAG_THRESHOLDING)) {237 case 1: return "No dithering or halftoning";238 case 2: return "Ordered dither or halftone";239 case 3: return "Randomized dither";240 default:241 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_THRESHOLDING) + ")";242 }243 }244 245 public String getFillOrderDescription() throws MetadataException246 {247 if (!_directory.containsTag(ExifDirectory.TAG_FILL_ORDER)) return null;248 switch (_directory.getInt(ExifDirectory.TAG_FILL_ORDER)) {249 case 1: return "Normal";250 case 2: return "Reversed";251 default:252 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_FILL_ORDER) + ")";253 }254 }255 256 public String getSubjectDistanceRangeDescription() throws MetadataException257 {258 if (!_directory.containsTag(ExifDirectory.TAG_SUBJECT_DISTANCE_RANGE)) return null;259 switch (_directory.getInt(ExifDirectory.TAG_SUBJECT_DISTANCE_RANGE)) {260 case 0:261 return "Unknown";262 case 1:263 return "Macro";264 case 2:265 return "Close view";266 case 3:267 return "Distant view";268 default:269 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_SUBJECT_DISTANCE_RANGE) + ")";270 }271 }272 273 public String getSharpnessDescription() throws MetadataException274 {275 if (!_directory.containsTag(ExifDirectory.TAG_SHARPNESS)) return null;276 switch (_directory.getInt(ExifDirectory.TAG_SHARPNESS)) {277 case 0:278 return "None";279 case 1:280 return "Low";281 case 2:282 return "Hard";283 default:284 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_SHARPNESS) + ")";285 }286 }287 288 public String getSaturationDescription() throws MetadataException289 {290 if (!_directory.containsTag(ExifDirectory.TAG_SATURATION)) return null;291 switch (_directory.getInt(ExifDirectory.TAG_SATURATION)) {292 case 0:293 return "None";294 case 1:295 return "Low saturation";296 case 2:297 return "High saturation";298 default:299 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_SATURATION) + ")";300 }301 }302 303 public String getContrastDescription() throws MetadataException304 {305 if (!_directory.containsTag(ExifDirectory.TAG_CONTRAST)) return null;306 switch (_directory.getInt(ExifDirectory.TAG_CONTRAST)) {307 case 0:308 return "None";309 case 1:310 return "Soft";311 case 2:312 return "Hard";313 default:314 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_CONTRAST) + ")";315 }316 }317 318 public String getGainControlDescription() throws MetadataException319 {320 if (!_directory.containsTag(ExifDirectory.TAG_GAIN_CONTROL)) return null;321 switch (_directory.getInt(ExifDirectory.TAG_GAIN_CONTROL)) {322 case 0:323 return "None";324 case 1:325 return "Low gain up";326 case 2:327 return "Low gain down";328 case 3:329 return "High gain up";330 case 4:331 return "High gain down";332 default:333 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_GAIN_CONTROL) + ")";334 }335 }336 337 public String getSceneCaptureTypeDescription() throws MetadataException338 {339 if (!_directory.containsTag(ExifDirectory.TAG_SCENE_CAPTURE_TYPE)) return null;340 switch (_directory.getInt(ExifDirectory.TAG_SCENE_CAPTURE_TYPE)) {341 case 0:342 return "Standard";343 case 1:344 return "Landscape";345 case 2:346 return "Portrait";347 case 3:348 return "Night scene";349 default:350 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_SCENE_CAPTURE_TYPE) + ")";351 }352 }353 354 public String get35mmFilmEquivFocalLengthDescription() throws MetadataException355 {356 if (!_directory.containsTag(ExifDirectory.TAG_35MM_FILM_EQUIV_FOCAL_LENGTH)) return null;357 int equivalentFocalLength = _directory.getInt(ExifDirectory.TAG_35MM_FILM_EQUIV_FOCAL_LENGTH);358 359 if (equivalentFocalLength==0)360 return "Unknown";361 else362 return SimpleDecimalFormatter.format(equivalentFocalLength) + "mm";363 }364 365 public String getDigitalZoomRatioDescription() throws MetadataException366 {367 if (!_directory.containsTag(ExifDirectory.TAG_DIGITAL_ZOOM_RATIO)) return null;368 Rational rational = _directory.getRational(ExifDirectory.TAG_DIGITAL_ZOOM_RATIO);369 if (rational.getNumerator()==0)370 return "Digital zoom not used.";371 372 return SimpleDecimalFormatter.format(rational.doubleValue());373 }374 375 public String getWhiteBalanceModeDescription() throws MetadataException376 {377 if (!_directory.containsTag(ExifDirectory.TAG_WHITE_BALANCE_MODE)) return null;378 switch (_directory.getInt(ExifDirectory.TAG_WHITE_BALANCE_MODE)) {379 case 0:380 return "Auto white balance";381 case 1:382 return "Manual white balance";383 default:384 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_WHITE_BALANCE_MODE) + ")";385 }386 }387 388 public String getExposureModeDescription() throws MetadataException389 {390 if (!_directory.containsTag(ExifDirectory.TAG_EXPOSURE_MODE)) return null;391 switch (_directory.getInt(ExifDirectory.TAG_EXPOSURE_MODE)) {392 case 0:393 return "Auto exposure";394 case 1:395 return "Manual exposure";396 case 2:397 return "Auto bracket";398 default:399 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_EXPOSURE_MODE) + ")";400 }401 }402 403 public String getCustomRenderedDescription() throws MetadataException404 {405 if (!_directory.containsTag(ExifDirectory.TAG_CUSTOM_RENDERED)) return null;406 switch (_directory.getInt(ExifDirectory.TAG_CUSTOM_RENDERED)) {407 case 0:408 return "Normal process";409 case 1:410 return "Custom process";411 default:412 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_CUSTOM_RENDERED) + ")";413 }414 }415 416 public String getUserCommentDescription() throws MetadataException417 {418 if (!_directory.containsTag(ExifDirectory.TAG_USER_COMMENT)) return null;419 420 byte[] commentBytes = _directory.getByteArray(ExifDirectory.TAG_USER_COMMENT);421 422 if (commentBytes.length==0)423 return "";424 425 final String[] encodingNames = new String[] { "ASCII", "UNICODE", "JIS" };426 427 if (commentBytes.length>=10)428 {429 String encodingRegion = new String(commentBytes, 0, 10);430 431 // try each encoding name432 for (int i = 0; i<encodingNames.length; i++) {433 String encodingName = encodingNames[i];434 if (encodingRegion.startsWith(encodingName))435 {436 // remove the null characters (and any spaces) commonly present after the encoding name437 for (int j = encodingName.length(); j<10; j++) {438 byte b = commentBytes[j];439 if (b!='\0' && b!=' ') {440 if (encodingName.equals("UNICODE")) {441 try {442 return new String(commentBytes, j, commentBytes.length - j, "UTF-16LE").trim();443 }444 catch (UnsupportedEncodingException ex) {445 return null;446 }447 }448 return new String(commentBytes, j, commentBytes.length - j).trim();449 }450 }451 return new String(commentBytes, 10, commentBytes.length - 10).trim();452 }453 }454 }455 456 // special handling fell through, return a plain string representation457 return new String(commentBytes).trim();458 }459 460 public String getThumbnailDescription() throws MetadataException461 {462 if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_DATA)) return null;463 int[] thumbnailBytes = _directory.getIntArray(ExifDirectory.TAG_THUMBNAIL_DATA);464 return "[" + thumbnailBytes.length + " bytes of thumbnail data]";465 }466 467 public String getIsoEquivalentDescription() throws MetadataException468 {469 if (!_directory.containsTag(ExifDirectory.TAG_ISO_EQUIVALENT)) return null;470 int isoEquiv = _directory.getInt(ExifDirectory.TAG_ISO_EQUIVALENT);471 if (isoEquiv < 50) {472 isoEquiv *= 200;473 }474 return Integer.toString(isoEquiv);475 }476 477 public String getReferenceBlackWhiteDescription() throws MetadataException478 {479 if (!_directory.containsTag(ExifDirectory.TAG_REFERENCE_BLACK_WHITE)) return null;480 int[] ints = _directory.getIntArray(ExifDirectory.TAG_REFERENCE_BLACK_WHITE);481 int blackR = ints[0];482 int whiteR = ints[1];483 int blackG = ints[2];484 int whiteG = ints[3];485 int blackB = ints[4];486 int whiteB = ints[5];487 String pos = "[" + blackR + "," + blackG + "," + blackB + "] " +488 "[" + whiteR + "," + whiteG + "," + whiteB + "]";489 return pos;490 }491 492 public String getExifVersionDescription() throws MetadataException493 {494 if (!_directory.containsTag(ExifDirectory.TAG_EXIF_VERSION)) return null;495 int[] ints = _directory.getIntArray(ExifDirectory.TAG_EXIF_VERSION);496 return ExifDescriptor.convertBytesToVersionString(ints);497 }498 499 public String getFlashPixVersionDescription() throws MetadataException500 {501 if (!_directory.containsTag(ExifDirectory.TAG_FLASHPIX_VERSION)) return null;502 int[] ints = _directory.getIntArray(ExifDirectory.TAG_FLASHPIX_VERSION);503 return ExifDescriptor.convertBytesToVersionString(ints);504 }505 506 public String getSceneTypeDescription() throws MetadataException507 {508 if (!_directory.containsTag(ExifDirectory.TAG_SCENE_TYPE)) return null;509 int sceneType = _directory.getInt(ExifDirectory.TAG_SCENE_TYPE);510 if (sceneType == 1) {511 return "Directly photographed image";512 } else {513 return "Unknown (" + sceneType + ")";514 }515 }516 517 public String getFileSourceDescription() throws MetadataException518 {519 if (!_directory.containsTag(ExifDirectory.TAG_FILE_SOURCE)) return null;520 int fileSource = _directory.getInt(ExifDirectory.TAG_FILE_SOURCE);521 if (fileSource == 3) {522 return "Digital Still Camera (DSC)";523 } else {524 return "Unknown (" + fileSource + ")";525 }526 }527 528 public String getExposureBiasDescription() throws MetadataException529 {530 if (!_directory.containsTag(ExifDirectory.TAG_EXPOSURE_BIAS)) return null;531 Rational exposureBias = _directory.getRational(ExifDirectory.TAG_EXPOSURE_BIAS);532 return exposureBias.toSimpleString(true) + " EV";533 }534 535 public String getMaxApertureValueDescription() throws MetadataException536 {537 if (!_directory.containsTag(ExifDirectory.TAG_MAX_APERTURE)) return null;538 double aperture = _directory.getDouble(ExifDirectory.TAG_MAX_APERTURE);539 double fStop = PhotographicConversions.apertureToFStop(aperture);540 return "F" + SimpleDecimalFormatter.format(fStop);541 }542 543 public String getApertureValueDescription() throws MetadataException544 {545 if (!_directory.containsTag(ExifDirectory.TAG_APERTURE)) return null;546 double aperture = _directory.getDouble(ExifDirectory.TAG_APERTURE);547 double fStop = PhotographicConversions.apertureToFStop(aperture);548 return "F" + SimpleDecimalFormatter.format(fStop);549 }550 551 public String getExposureProgramDescription() throws MetadataException552 {553 if (!_directory.containsTag(ExifDirectory.TAG_EXPOSURE_PROGRAM)) return null;554 // '1' means manual control, '2' program normal, '3' aperture priority,555 // '4' shutter priority, '5' program creative (slow program),556 // '6' program action(high-speed program), '7' portrait mode, '8' landscape mode.557 switch (_directory.getInt(ExifDirectory.TAG_EXPOSURE_PROGRAM)) {558 case 1: return "Manual control";559 case 2: return "Program normal";560 case 3: return "Aperture priority";561 case 4: return "Shutter priority";562 case 5: return "Program creative (slow program)";563 case 6: return "Program action (high-speed program)";564 case 7: return "Portrait mode";565 case 8: return "Landscape mode";566 default:567 return "Unknown program (" + _directory.getInt(ExifDirectory.TAG_EXPOSURE_PROGRAM) + ")";568 }569 }570 571 public String getYCbCrSubsamplingDescription() throws MetadataException572 {573 if (!_directory.containsTag(ExifDirectory.TAG_YCBCR_SUBSAMPLING)) return null;574 int[] positions = _directory.getIntArray(ExifDirectory.TAG_YCBCR_SUBSAMPLING);575 if (positions[0] == 2 && positions[1] == 1) {576 return "YCbCr4:2:2";577 } else if (positions[0] == 2 && positions[1] == 2) {578 return "YCbCr4:2:0";579 } else {580 return "(Unknown)";581 }582 }583 584 public String getPlanarConfigurationDescription() throws MetadataException585 {586 if (!_directory.containsTag(ExifDirectory.TAG_PLANAR_CONFIGURATION)) return null;587 // When image format is no compression YCbCr, this value shows byte aligns of YCbCr588 // data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for each subsampling589 // pixel. If value is '2', Y/Cb/Cr value is separated and stored to Y plane/Cb plane/Cr590 // plane format.591 592 switch (_directory.getInt(ExifDirectory.TAG_PLANAR_CONFIGURATION)) {593 case 1: return "Chunky (contiguous for each subsampling pixel)";594 case 2: return "Separate (Y-plane/Cb-plane/Cr-plane format)";595 default:596 return "Unknown configuration";597 }598 }599 600 public String getSamplesPerPixelDescription()601 {602 if (!_directory.containsTag(ExifDirectory.TAG_SAMPLES_PER_PIXEL)) return null;603 return _directory.getString(ExifDirectory.TAG_SAMPLES_PER_PIXEL) + " samples/pixel";604 }605 606 public String getRowsPerStripDescription()607 {608 if (!_directory.containsTag(ExifDirectory.TAG_ROWS_PER_STRIP)) return null;609 return _directory.getString(ExifDirectory.TAG_ROWS_PER_STRIP) + " rows/strip";610 }611 612 public String getStripByteCountsDescription()613 {614 if (!_directory.containsTag(ExifDirectory.TAG_STRIP_BYTE_COUNTS)) return null;615 return _directory.getString(ExifDirectory.TAG_STRIP_BYTE_COUNTS) + " bytes";616 }617 618 public String getPhotometricInterpretationDescription() throws MetadataException619 {620 if (!_directory.containsTag(ExifDirectory.TAG_PHOTOMETRIC_INTERPRETATION)) return null;621 // Shows the color space of the image data components622 switch (_directory.getInt(ExifDirectory.TAG_PHOTOMETRIC_INTERPRETATION)) {623 case 0: return "WhiteIsZero";624 case 1: return "BlackIsZero";625 case 2: return "RGB";626 case 3: return "RGB Palette";627 case 4: return "Transparency Mask";628 case 5: return "CMYK";629 case 6: return "YCbCr";630 case 8: return "CIELab";631 case 9: return "ICCLab";632 case 10: return "ITULab";633 case 32803: return "Color Filter Array";634 case 32844: return "Pixar LogL";635 case 32845: return "Pixar LogLuv";636 case 32892: return "Linear Raw";637 default:638 return "Unknown colour space";639 }640 }641 642 public String getCompressionDescription() throws MetadataException643 {644 if (!_directory.containsTag(ExifDirectory.TAG_COMPRESSION)) return null;645 switch (_directory.getInt(ExifDirectory.TAG_COMPRESSION)) {646 case 1: return "Uncompressed";647 case 2: return "CCITT 1D";648 case 3: return "T4/Group 3 Fax";649 case 4: return "T6/Group 4 Fax";650 case 5: return "LZW";651 case 6: return "JPEG (old-style)";652 case 7: return "JPEG";653 case 8: return "Adobe Deflate";654 case 9: return "JBIG B&W";655 case 10: return "JBIG Color";656 case 32766: return "Next";657 case 32771: return "CCIRLEW";658 case 32773: return "PackBits";659 case 32809: return "Thunderscan";660 case 32895: return "IT8CTPAD";661 case 32896: return "IT8LW";662 case 32897: return "IT8MP";663 case 32898: return "IT8BL";664 case 32908: return "PixarFilm";665 case 32909: return "PixarLog";666 case 32946: return "Deflate";667 case 32947: return "DCS";668 case 32661: return "JBIG";669 case 32676: return "SGILog";670 case 32677: return "SGILog24";671 case 32712: return "JPEG 2000";672 case 32713: return "Nikon NEF Compressed";673 default:674 return "Unknown compression";675 }676 }677 678 public String getBitsPerSampleDescription()679 {680 if (!_directory.containsTag(ExifDirectory.TAG_BITS_PER_SAMPLE)) return null;681 return _directory.getString(ExifDirectory.TAG_BITS_PER_SAMPLE) + " bits/component/pixel";682 }683 684 public String getThumbnailImageWidthDescription()685 {686 if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_IMAGE_WIDTH)) return null;687 return _directory.getString(ExifDirectory.TAG_THUMBNAIL_IMAGE_WIDTH) + " pixels";688 }689 690 public String getThumbnailImageHeightDescription()691 {692 if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT)) return null;693 return _directory.getString(ExifDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT) + " pixels";694 }695 696 public String getFocalPlaneXResolutionDescription() throws MetadataException697 {698 if (!_directory.containsTag(ExifDirectory.TAG_FOCAL_PLANE_X_RES)) return null;699 Rational rational = _directory.getRational(ExifDirectory.TAG_FOCAL_PLANE_X_RES);700 return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) + " " +701 getFocalPlaneResolutionUnitDescription().toLowerCase();702 }703 704 public String getFocalPlaneYResolutionDescription() throws MetadataException705 {706 if (!_directory.containsTag(ExifDirectory.TAG_FOCAL_PLANE_Y_RES)) return null;707 Rational rational = _directory.getRational(ExifDirectory.TAG_FOCAL_PLANE_Y_RES);708 return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) + " " +709 getFocalPlaneResolutionUnitDescription().toLowerCase();710 }711 712 public String getFocalPlaneResolutionUnitDescription() throws MetadataException713 {714 if (!_directory.containsTag(ExifDirectory.TAG_FOCAL_PLANE_UNIT)) return null;715 // Unit of FocalPlaneXResoluton/FocalPlaneYResolution. '1' means no-unit,716 // '2' inch, '3' centimeter.717 switch (_directory.getInt(ExifDirectory.TAG_FOCAL_PLANE_UNIT)) {718 case 1:719 return "(No unit)";720 case 2:721 return "Inches";722 case 3:723 return "cm";724 default:725 return "";726 }727 }728 729 public String getExifImageWidthDescription() throws MetadataException730 {731 if (!_directory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_WIDTH)) return null;732 return _directory.getInt(ExifDirectory.TAG_EXIF_IMAGE_WIDTH) + " pixels";733 }734 735 public String getExifImageHeightDescription() throws MetadataException736 {737 if (!_directory.containsTag(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT)) return null;738 return _directory.getInt(ExifDirectory.TAG_EXIF_IMAGE_HEIGHT) + " pixels";739 }740 741 public String getColorSpaceDescription() throws MetadataException742 {743 if (!_directory.containsTag(ExifDirectory.TAG_COLOR_SPACE)) return null;744 int colorSpace = _directory.getInt(ExifDirectory.TAG_COLOR_SPACE);745 if (colorSpace == 1) {746 return "sRGB";747 } else if (colorSpace == 65535) {748 return "Undefined";749 } else {750 return "Unknown";751 }752 }753 754 public String getFocalLengthDescription() throws MetadataException755 {756 if (!_directory.containsTag(ExifDirectory.TAG_FOCAL_LENGTH)) return null;757 java.text.DecimalFormat formatter = new DecimalFormat("0.0##");758 Rational focalLength = _directory.getRational(ExifDirectory.TAG_FOCAL_LENGTH);759 return formatter.format(focalLength.doubleValue()) + " mm";760 }761 762 public String getFlashDescription() throws MetadataException763 {764 if (!_directory.containsTag(ExifDirectory.TAG_FLASH)) return null;765 766 /*767 * This is a bitmask.768 * 0 = flash fired769 * 1 = return detected770 * 2 = return able to be detected771 * 3 = unknown772 * 4 = auto used773 * 5 = unknown774 * 6 = red eye reduction used775 */776 777 int val = _directory.getInt(ExifDirectory.TAG_FLASH);778 779 StringBuffer sb = new StringBuffer();780 781 if ((val & 0x1)!=0)782 sb.append("Flash fired");783 else784 sb.append("Flash did not fire");785 786 // check if we're able to detect a return, before we mention it787 if ((val & 0x4)!=0)788 {789 if ((val & 0x2)!=0)790 sb.append(", return detected");791 else792 sb.append(", return not detected");793 }794 795 if ((val & 0x10)!=0)796 sb.append(", auto");797 798 if ((val & 0x40)!=0)799 sb.append(", red-eye reduction");800 801 return sb.toString();802 }803 804 public String getWhiteBalanceDescription() throws MetadataException805 {806 if (!_directory.containsTag(ExifDirectory.TAG_WHITE_BALANCE)) return null;807 // '0' means unknown, '1' daylight, '2' fluorescent, '3' tungsten, '10' flash,808 // '17' standard light A, '18' standard light B, '19' standard light C, '20' D55,809 // '21' D65, '22' D75, '255' other.810 switch (_directory.getInt(ExifDirectory.TAG_WHITE_BALANCE)) {811 case 0:812 return "Unknown";813 case 1:814 return "Daylight";815 case 2:816 return "Flourescent";817 case 3:818 return "Tungsten";819 case 10:820 return "Flash";821 case 17:822 return "Standard light";823 case 18:824 return "Standard light (B)";825 case 19:826 return "Standard light (C)";827 case 20:828 return "D55";829 case 21:830 return "D65";831 case 22:832 return "D75";833 case 255:834 return "(Other)";835 default:836 return "Unknown (" + _directory.getInt(ExifDirectory.TAG_WHITE_BALANCE) + ")";837 }838 }839 840 public String getMeteringModeDescription() throws MetadataException841 {842 if (!_directory.containsTag(ExifDirectory.TAG_METERING_MODE)) return null;843 // '0' means unknown, '1' average, '2' center weighted average, '3' spot844 // '4' multi-spot, '5' multi-segment, '6' partial, '255' other845 int meteringMode = _directory.getInt(ExifDirectory.TAG_METERING_MODE);846 switch (meteringMode) {847 case 0:848 return "Unknown";849 case 1:850 return "Average";851 case 2:852 return "Center weighted average";853 case 3:854 return "Spot";855 case 4:856 return "Multi-spot";857 case 5:858 return "Multi-segment";859 case 6:860 return "Partial";861 case 255:862 return "(Other)";863 default:864 return "";865 }866 }867 868 public String getSubjectDistanceDescription() throws MetadataException869 {870 if (!_directory.containsTag(ExifDirectory.TAG_SUBJECT_DISTANCE)) return null;871 Rational distance = _directory.getRational(ExifDirectory.TAG_SUBJECT_DISTANCE);872 java.text.DecimalFormat formatter = new DecimalFormat("0.0##");873 return formatter.format(distance.doubleValue()) + " metres";874 }875 876 public String getCompressionLevelDescription() throws MetadataException877 {878 if (!_directory.containsTag(ExifDirectory.TAG_COMPRESSION_LEVEL)) return null;879 Rational compressionRatio = _directory.getRational(ExifDirectory.TAG_COMPRESSION_LEVEL);880 String ratio = compressionRatio.toSimpleString(_allowDecimalRepresentationOfRationals);881 if (compressionRatio.isInteger() && compressionRatio.intValue() == 1) {882 return ratio + " bit/pixel";883 } else {884 return ratio + " bits/pixel";885 }886 }887 888 public String getThumbnailLengthDescription()889 {890 if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_LENGTH)) return null;891 return _directory.getString(ExifDirectory.TAG_THUMBNAIL_LENGTH) + " bytes";892 }893 894 public String getThumbnailOffsetDescription()895 {896 if (!_directory.containsTag(ExifDirectory.TAG_THUMBNAIL_OFFSET)) return null;897 return _directory.getString(ExifDirectory.TAG_THUMBNAIL_OFFSET) + " bytes";898 }899 900 public String getYResolutionDescription() throws MetadataException901 {902 if (!_directory.containsTag(ExifDirectory.TAG_Y_RESOLUTION)) return null;903 Rational resolution = _directory.getRational(ExifDirectory.TAG_Y_RESOLUTION);904 return resolution.toSimpleString(_allowDecimalRepresentationOfRationals) +905 " dots per " +906 getResolutionDescription().toLowerCase();907 }908 909 public String getXResolutionDescription() throws MetadataException910 {911 if (!_directory.containsTag(ExifDirectory.TAG_X_RESOLUTION)) return null;912 Rational resolution = _directory.getRational(ExifDirectory.TAG_X_RESOLUTION);913 return resolution.toSimpleString(_allowDecimalRepresentationOfRationals) +914 " dots per " +915 getResolutionDescription().toLowerCase();916 }917 918 public String getExposureTimeDescription()919 {920 if (!_directory.containsTag(ExifDirectory.TAG_EXPOSURE_TIME)) return null;921 return _directory.getString(ExifDirectory.TAG_EXPOSURE_TIME) + " sec";922 }923 924 public String getShutterSpeedDescription() throws MetadataException925 {926 // I believe this method to now be stable, but am leaving some alternative snippets of927 // code in here, to assist anyone who's looking into this (given that I don't have a public CVS).928 929 if (!_directory.containsTag(ExifDirectory.TAG_SHUTTER_SPEED)) return null;930 // float apexValue = _directory.getFloat(ExifDirectory.TAG_SHUTTER_SPEED);931 // int apexPower = (int)Math.pow(2.0, apexValue);932 // return "1/" + apexPower + " sec";933 // TODO test this method934 // thanks to Mark Edwards for spotting and patching a bug in the calculation of this935 // description (spotted bug using a Canon EOS 300D)936 // thanks also to Gli Blr for spotting this bug937 float apexValue = _directory.getFloat(ExifDirectory.TAG_SHUTTER_SPEED);938 if (apexValue<=1) {939 float apexPower = (float)(1/(Math.exp(apexValue*Math.log(2))));940 long apexPower10 = Math.round((double)apexPower * 10.0);941 float fApexPower = (float) apexPower10 / 10.0f;942 return fApexPower + " sec";943 } else {944 int apexPower = (int)((Math.exp(apexValue*Math.log(2))));945 return "1/" + apexPower + " sec";946 }947 948 /*949 // This alternative implementation offered by Bill Richards950 // TODO determine which is the correct / more-correct implementation951 double apexValue = _directory.getDouble(ExifDirectory.TAG_SHUTTER_SPEED);952 double apexPower = Math.pow(2.0, apexValue);953 954 StringBuffer sb = new StringBuffer();955 if (apexPower > 1)956 apexPower = Math.floor(apexPower);957 958 if (apexPower < 1) {959 sb.append((int)Math.round(1/apexPower));960 } else {961 sb.append("1/");962 sb.append((int)apexPower);963 }964 sb.append(" sec");965 return sb.toString();966 */967 968 }969 970 public String getFNumberDescription() throws MetadataException971 {972 if (!_directory.containsTag(ExifDirectory.TAG_FNUMBER)) return null;973 Rational fNumber = _directory.getRational(ExifDirectory.TAG_FNUMBER);974 return "F" + SimpleDecimalFormatter.format(fNumber.doubleValue());975 }976 977 public String getYCbCrPositioningDescription() throws MetadataException978 {979 if (!_directory.containsTag(ExifDirectory.TAG_YCBCR_POSITIONING)) return null;980 int yCbCrPosition = _directory.getInt(ExifDirectory.TAG_YCBCR_POSITIONING);981 switch (yCbCrPosition) {982 case 1: return "Center of pixel array";983 case 2: return "Datum point";984 default:985 return String.valueOf(yCbCrPosition);986 }987 }988 989 public String getOrientationDescription() throws MetadataException990 {991 if (!_directory.containsTag(ExifDirectory.TAG_ORIENTATION)) return null;992 int orientation = _directory.getInt(ExifDirectory.TAG_ORIENTATION);993 switch (orientation) {994 case 1: return "Top, left side (Horizontal / normal)";995 case 2: return "Top, right side (Mirror horizontal)";996 case 3: return "Bottom, right side (Rotate 180)";997 case 4: return "Bottom, left side (Mirror vertical)";998 case 5: return "Left side, top (Mirror horizontal and rotate 270 CW)";999 case 6: return "Right side, top (Rotate 90 CW)";1000 case 7: return "Right side, bottom (Mirror horizontal and rotate 90 CW)";1001 case 8: return "Left side, bottom (Rotate 270 CW)";1002 default:1003 return String.valueOf(orientation);1004 }1005 }1006 1007 public String getResolutionDescription() throws MetadataException1008 {1009 if (!_directory.containsTag(ExifDirectory.TAG_RESOLUTION_UNIT)) return "";1010 // '1' means no-unit, '2' means inch, '3' means centimeter. Default value is '2'(inch)1011 int resolutionUnit = _directory.getInt(ExifDirectory.TAG_RESOLUTION_UNIT);1012 switch (resolutionUnit) {1013 case 1: return "(No unit)";1014 case 2: return "Inch";1015 case 3: return "cm";1016 default:1017 return "";1018 }1019 }1020 1021 public String getSensingMethodDescription() throws MetadataException1022 {1023 if (!_directory.containsTag(ExifDirectory.TAG_SENSING_METHOD)) return null;1024 // '1' Not defined, '2' One-chip color area sensor, '3' Two-chip color area sensor1025 // '4' Three-chip color area sensor, '5' Color sequential area sensor1026 // '7' Trilinear sensor '8' Color sequential linear sensor, 'Other' reserved1027 int sensingMethod = _directory.getInt(ExifDirectory.TAG_SENSING_METHOD);1028 switch (sensingMethod) {1029 case 1:1030 return "(Not defined)";1031 case 2:1032 return "One-chip color area sensor";1033 case 3:1034 return "Two-chip color area sensor";1035 case 4:1036 return "Three-chip color area sensor";1037 case 5:1038 return "Color sequential area sensor";1039 case 7:1040 return "Trilinear sensor";1041 case 8:1042 return "Color sequential linear sensor";1043 default:1044 return "";1045 }1046 }1047 1048 public String getComponentConfigurationDescription() throws MetadataException1049 {1050 int[] components = _directory.getIntArray(ExifDirectory.TAG_COMPONENTS_CONFIGURATION);1051 String[] componentStrings = {"", "Y", "Cb", "Cr", "R", "G", "B"};1052 StringBuffer componentConfig = new StringBuffer();1053 for (int i = 0; i < Math.min(4, components.length); i++) {1054 int j = components[i];1055 if (j > 0 && j < componentStrings.length) {1056 componentConfig.append(componentStrings[j]);1057 }1058 }1059 return componentConfig.toString();1060 }1061 1062 /**1063 * Takes a series of 4 bytes from the specified offset, and converts these to a1064 * well-known version number, where possible. For example, (hex) 30 32 31 30 == 2.10).1065 * @param components the four version values1066 * @return the version as a string of form 2.101067 */1068 public static String convertBytesToVersionString(int[] components)1069 {1070 StringBuffer version = new StringBuffer();1071 for (int i = 0; i < 4 && i < components.length; i++) {1072 if (i == 2) version.append('.');1073 String digit = String.valueOf((char)components[i]);1074 if (i == 0 && "0".equals(digit)) continue;1075 version.append(digit);1076 }1077 return version.toString();1078 }1079 1080 /**1081 * The Windows specific tags uses plain Unicode1082 */1083 private String getUnicodeDescription(int tag) throws MetadataException1084 {1085 if (!_directory.containsTag(tag)) return null;1086 byte[] commentBytes = _directory.getByteArray(tag);1087 try {1088 // decode the unicode string1089 // trim it, as i'm seeing a junk character on the end1090 return new String(commentBytes, "UTF-16LE").trim();1091 }1092 catch (UnsupportedEncodingException ex) {1093 return null;1094 }1095 }1096 1097 public String getWindowsAuthorDescription() throws MetadataException1098 {1099 return getUnicodeDescription(ExifDirectory.TAG_WIN_AUTHOR);1100 }1101 1102 public String getWindowsCommentDescription() throws MetadataException1103 {1104 return getUnicodeDescription(ExifDirectory.TAG_WIN_COMMENT);1105 }1106 1107 public String getWindowsKeywordsDescription() throws MetadataException1108 {1109 return getUnicodeDescription(ExifDirectory.TAG_WIN_KEYWORDS);1110 }1111 1112 public String getWindowsTitleDescription() throws MetadataException1113 {1114 return getUnicodeDescription(ExifDirectory.TAG_WIN_TITLE);1115 }1116 1117 public String getWindowsSubjectDescription() throws MetadataException1118 {1119 return getUnicodeDescription(ExifDirectory.TAG_WIN_SUBJECT);1120 }1121 } -
src/com/drew/metadata/exif/ExifDirectory.java
1 /*2 * ExifDirectory.java3 *4 * This is public domain software - that is, you can do whatever you want5 * with it, and include it software that is licensed under the GNU or the6 * BSD license, or whatever other licence you choose, including proprietary7 * closed source licenses. I do ask that you leave this header in tact.8 *9 * If you make modifications to this code that you think would benefit the10 * wider community, please send me a copy and I'll post it on my site.11 *12 * If you make use of this code, I'd appreciate hearing about it.13 * drew@drewnoakes.com14 * Latest version of this software kept at15 * http://drewnoakes.com/16 *17 * Created by dnoakes on 25-Nov-2002 20:41:00 using IntelliJ IDEA.18 */19 package com.drew.metadata.exif;20 21 import com.drew.metadata.Directory;22 import com.drew.metadata.MetadataException;23 24 import java.io.FileOutputStream;25 import java.io.IOException;26 import java.util.HashMap;27 28 /**29 *30 */31 public class ExifDirectory extends Directory32 {33 // TODO do these tags belong in the exif directory?34 public static final int TAG_SUB_IFDS = 0x014A;35 public static final int TAG_GPS_INFO = 0x8825;36 37 /**38 * The actual aperture value of lens when the image was taken. Unit is APEX.39 * To convert this value to ordinary F-number (F-stop), calculate this value's40 * power of root 2 (=1.4142). For example, if the ApertureValue is '5',41 * F-number is 1.4142^5 = F5.6.42 */43 public static final int TAG_APERTURE = 0x9202;44 /**45 * When image format is no compression, this value shows the number of bits46 * per component for each pixel. Usually this value is '8,8,8'.47 */48 public static final int TAG_BITS_PER_SAMPLE = 0x0102;49 /**50 * Shows compression method for Thumbnail.51 * 1 = Uncompressed52 * 2 = CCITT 1D53 * 3 = T4/Group 3 Fax54 * 4 = T6/Group 4 Fax55 * 5 = LZW56 * 6 = JPEG (old-style)57 * 7 = JPEG58 * 8 = Adobe Deflate59 * 9 = JBIG B&W60 * 10 = JBIG Color61 * 32766 = Next62 * 32771 = CCIRLEW63 * 32773 = PackBits64 * 32809 = Thunderscan65 * 32895 = IT8CTPAD66 * 32896 = IT8LW67 * 32897 = IT8MP68 * 32898 = IT8BL69 * 32908 = PixarFilm70 * 32909 = PixarLog71 * 32946 = Deflate72 * 32947 = DCS73 * 34661 = JBIG74 * 34676 = SGILog75 * 34677 = SGILog2476 * 34712 = JPEG 200077 * 34713 = Nikon NEF Compressed78 */79 public static final int TAG_COMPRESSION = 0x0103;80 public static final int COMPRESSION_NONE = 1;81 public static final int COMPRESSION_JPEG = 6;82 83 /**84 * Shows the color space of the image data components.85 * 0 = WhiteIsZero86 * 1 = BlackIsZero87 * 2 = RGB88 * 3 = RGB Palette89 * 4 = Transparency Mask90 * 5 = CMYK91 * 6 = YCbCr92 * 8 = CIELab93 * 9 = ICCLab94 * 10 = ITULab95 * 32803 = Color Filter Array96 * 32844 = Pixar LogL97 * 32845 = Pixar LogLuv98 * 34892 = Linear Raw99 */100 public static final int TAG_PHOTOMETRIC_INTERPRETATION = 0x0106;101 /**102 * 1 = No dithering or halftoning103 * 2 = Ordered dither or halftone104 * 3 = Randomized dither105 */106 public static final int TAG_THRESHOLDING = 0x0107;107 public static final int PHOTOMETRIC_INTERPRETATION_MONOCHROME = 1;108 public static final int PHOTOMETRIC_INTERPRETATION_RGB = 2;109 public static final int PHOTOMETRIC_INTERPRETATION_YCBCR = 6;110 111 /** The position in the file of raster data. */112 public static final int TAG_STRIP_OFFSETS = 0x0111;113 /** Each pixel is composed of this many samples. */114 public static final int TAG_SAMPLES_PER_PIXEL = 0x0115;115 /** The raster is codified by a single block of data holding this many rows. */116 public static final int TAG_ROWS_PER_STRIP = 0x116;117 /** The size of the raster data in bytes. */118 public static final int TAG_STRIP_BYTE_COUNTS = 0x0117;119 public static final int TAG_MIN_SAMPLE_VALUE = 0x0118;120 public static final int TAG_MAX_SAMPLE_VALUE = 0x0119;121 /**122 * When image format is no compression YCbCr, this value shows byte aligns of123 * YCbCr data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for124 * each subsampling pixel. If value is '2', Y/Cb/Cr value is separated and125 * stored to Y plane/Cb plane/Cr plane format.126 */127 public static final int TAG_PLANAR_CONFIGURATION = 0x011C;128 public static final int TAG_YCBCR_SUBSAMPLING = 0x0212;129 public static final int TAG_IMAGE_DESCRIPTION = 0x010E;130 public static final int TAG_SOFTWARE = 0x0131;131 public static final int TAG_DATETIME = 0x0132;132 public static final int TAG_WHITE_POINT = 0x013E;133 public static final int TAG_PRIMARY_CHROMATICITIES = 0x013F;134 public static final int TAG_YCBCR_COEFFICIENTS = 0x0211;135 public static final int TAG_REFERENCE_BLACK_WHITE = 0x0214;136 public static final int TAG_COPYRIGHT = 0x8298;137 138 /**139 * The new subfile type tag.140 * 0 = Full-resolution Image141 * 1 = Reduced-resolution image142 * 2 = Single page of multi-page image143 * 3 = Single page of multi-page reduced-resolution image144 * 4 = Transparency mask145 * 5 = Transparency mask of reduced-resolution image146 * 6 = Transparency mask of multi-page image147 * 7 = Transparency mask of reduced-resolution multi-page image148 */149 public static final int TAG_NEW_SUBFILE_TYPE = 0x00FE;150 /**151 * The old subfile type tag.152 * 1 = Full-resolution image (Main image)153 * 2 = Reduced-resolution image (Thumbnail)154 * 3 = Single page of multi-page image155 */156 public static final int TAG_SUBFILE_TYPE = 0x00FF;157 public static final int TAG_TRANSFER_FUNCTION = 0x012D;158 public static final int TAG_ARTIST = 0x013B;159 public static final int TAG_PREDICTOR = 0x013D;160 public static final int TAG_TILE_WIDTH = 0x0142;161 public static final int TAG_TILE_LENGTH = 0x0143;162 public static final int TAG_TILE_OFFSETS = 0x0144;163 public static final int TAG_TILE_BYTE_COUNTS = 0x0145;164 public static final int TAG_JPEG_TABLES = 0x015B;165 public static final int TAG_CFA_REPEAT_PATTERN_DIM = 0x828D;166 /** There are two definitions for CFA pattern, I don't know the difference... */167 public static final int TAG_CFA_PATTERN_2 = 0x828E;168 public static final int TAG_BATTERY_LEVEL = 0x828F;169 public static final int TAG_IPTC_NAA = 0x83BB;170 public static final int TAG_INTER_COLOR_PROFILE = 0x8773;171 public static final int TAG_SPECTRAL_SENSITIVITY = 0x8824;172 public static final int TAG_OECF = 0x8828;173 public static final int TAG_INTERLACE = 0x8829;174 public static final int TAG_TIME_ZONE_OFFSET = 0x882A;175 public static final int TAG_SELF_TIMER_MODE = 0x882B;176 public static final int TAG_FLASH_ENERGY = 0x920B;177 public static final int TAG_SPATIAL_FREQ_RESPONSE = 0x920C;178 public static final int TAG_NOISE = 0x920D;179 public static final int TAG_IMAGE_NUMBER = 0x9211;180 public static final int TAG_SECURITY_CLASSIFICATION = 0x9212;181 public static final int TAG_IMAGE_HISTORY = 0x9213;182 public static final int TAG_SUBJECT_LOCATION = 0x9214;183 /** There are two definitions for exposure index, I don't know the difference... */184 public static final int TAG_EXPOSURE_INDEX_2 = 0x9215;185 public static final int TAG_TIFF_EP_STANDARD_ID = 0x9216;186 public static final int TAG_FLASH_ENERGY_2 = 0xA20B;187 public static final int TAG_SPATIAL_FREQ_RESPONSE_2 = 0xA20C;188 public static final int TAG_SUBJECT_LOCATION_2 = 0xA214;189 public static final int TAG_MAKE = 0x010F;190 public static final int TAG_MODEL = 0x0110;191 public static final int TAG_ORIENTATION = 0x0112;192 public static final int TAG_X_RESOLUTION = 0x011A;193 public static final int TAG_Y_RESOLUTION = 0x011B;194 public static final int TAG_PAGE_NAME = 0x011D;195 public static final int TAG_RESOLUTION_UNIT = 0x0128;196 public static final int TAG_THUMBNAIL_OFFSET = 0x0201;197 public static final int TAG_THUMBNAIL_LENGTH = 0x0202;198 public static final int TAG_YCBCR_POSITIONING = 0x0213;199 /**200 * Exposure time (reciprocal of shutter speed). Unit is second.201 */202 public static final int TAG_EXPOSURE_TIME = 0x829A;203 /**204 * The actual F-number(F-stop) of lens when the image was taken.205 */206 public static final int TAG_FNUMBER = 0x829D;207 /**208 * Exposure program that the camera used when image was taken. '1' means209 * manual control, '2' program normal, '3' aperture priority, '4' shutter210 * priority, '5' program creative (slow program), '6' program action211 * (high-speed program), '7' portrait mode, '8' landscape mode.212 */213 public static final int TAG_EXPOSURE_PROGRAM = 0x8822;214 public static final int TAG_ISO_EQUIVALENT = 0x8827;215 public static final int TAG_EXIF_VERSION = 0x9000;216 public static final int TAG_DATETIME_ORIGINAL = 0x9003;217 public static final int TAG_DATETIME_DIGITIZED = 0x9004;218 public static final int TAG_COMPONENTS_CONFIGURATION = 0x9101;219 /**220 * Average (rough estimate) compression level in JPEG bits per pixel.221 * */222 public static final int TAG_COMPRESSION_LEVEL = 0x9102;223 /**224 * Shutter speed by APEX value. To convert this value to ordinary 'Shutter Speed';225 * calculate this value's power of 2, then reciprocal. For example, if the226 * ShutterSpeedValue is '4', shutter speed is 1/(24)=1/16 second.227 */228 public static final int TAG_SHUTTER_SPEED = 0x9201;229 public static final int TAG_BRIGHTNESS_VALUE = 0x9203;230 public static final int TAG_EXPOSURE_BIAS = 0x9204;231 /**232 * Maximum aperture value of lens. You can convert to F-number by calculating233 * power of root 2 (same process of ApertureValue:0x9202).234 * The actual aperture value of lens when the image was taken. To convert this235 * value to ordinary f-number(f-stop), calculate the value's power of root 2236 * (=1.4142). For example, if the ApertureValue is '5', f-number is 1.41425^5 = F5.6.237 */238 public static final int TAG_MAX_APERTURE = 0x9205;239 /**240 * Indicates the distance the autofocus camera is focused to. Tends to be less accurate as distance increases.241 */242 public static final int TAG_SUBJECT_DISTANCE = 0x9206;243 /**244 * Exposure metering method. '0' means unknown, '1' average, '2' center245 * weighted average, '3' spot, '4' multi-spot, '5' multi-segment, '6' partial,246 * '255' other.247 */248 public static final int TAG_METERING_MODE = 0x9207;249 250 public static final int TAG_LIGHT_SOURCE = 0x9208;251 /**252 * White balance (aka light source). '0' means unknown, '1' daylight,253 * '2' fluorescent, '3' tungsten, '10' flash, '17' standard light A,254 * '18' standard light B, '19' standard light C, '20' D55, '21' D65,255 * '22' D75, '255' other.256 */257 public static final int TAG_WHITE_BALANCE = 0x9208;258 /**259 * 0x0 = 0000000 = No Flash260 * 0x1 = 0000001 = Fired261 * 0x5 = 0000101 = Fired, Return not detected262 * 0x7 = 0000111 = Fired, Return detected263 * 0x9 = 0001001 = On264 * 0xd = 0001101 = On, Return not detected265 * 0xf = 0001111 = On, Return detected266 * 0x10 = 0010000 = Off267 * 0x18 = 0011000 = Auto, Did not fire268 * 0x19 = 0011001 = Auto, Fired269 * 0x1d = 0011101 = Auto, Fired, Return not detected270 * 0x1f = 0011111 = Auto, Fired, Return detected271 * 0x20 = 0100000 = No flash function272 * 0x41 = 1000001 = Fired, Red-eye reduction273 * 0x45 = 1000101 = Fired, Red-eye reduction, Return not detected274 * 0x47 = 1000111 = Fired, Red-eye reduction, Return detected275 * 0x49 = 1001001 = On, Red-eye reduction276 * 0x4d = 1001101 = On, Red-eye reduction, Return not detected277 * 0x4f = 1001111 = On, Red-eye reduction, Return detected278 * 0x59 = 1011001 = Auto, Fired, Red-eye reduction279 * 0x5d = 1011101 = Auto, Fired, Red-eye reduction, Return not detected280 * 0x5f = 1011111 = Auto, Fired, Red-eye reduction, Return detected281 * 6543210 (positions)282 *283 * This is a bitmask.284 * 0 = flash fired285 * 1 = return detected286 * 2 = return able to be detected287 * 3 = unknown288 * 4 = auto used289 * 5 = unknown290 * 6 = red eye reduction used291 */292 public static final int TAG_FLASH = 0x9209;293 /**294 * Focal length of lens used to take image. Unit is millimeter.295 * Nice digital cameras actually save the focal length as a function of how far they are zoomed in.296 */297 public static final int TAG_FOCAL_LENGTH = 0x920A;298 public static final int TAG_USER_COMMENT = 0x9286;299 public static final int TAG_SUBSECOND_TIME = 0x9290;300 public static final int TAG_SUBSECOND_TIME_ORIGINAL = 0x9291;301 public static final int TAG_SUBSECOND_TIME_DIGITIZED = 0x9292;302 public static final int TAG_FLASHPIX_VERSION = 0xA000;303 /**304 * Defines Color Space. DCF image must use sRGB color space so value is305 * always '1'. If the picture uses the other color space, value is306 * '65535':Uncalibrated.307 */308 public static final int TAG_COLOR_SPACE = 0xA001;309 public static final int TAG_EXIF_IMAGE_WIDTH = 0xA002;310 public static final int TAG_EXIF_IMAGE_HEIGHT = 0xA003;311 public static final int TAG_RELATED_SOUND_FILE = 0xA004;312 public static final int TAG_FOCAL_PLANE_X_RES = 0xA20E;313 public static final int TAG_FOCAL_PLANE_Y_RES = 0xA20F;314 /**315 * Unit of FocalPlaneXResoluton/FocalPlaneYResolution. '1' means no-unit,316 * '2' inch, '3' centimeter.317 *318 * Note: Some of Fujifilm's digicam(e.g.FX2700,FX2900,Finepix4700Z/40i etc)319 * uses value '3' so it must be 'centimeter', but it seems that they use a320 * '8.3mm?'(1/3in.?) to their ResolutionUnit. Fuji's BUG? Finepix4900Z has321 * been changed to use value '2' but it doesn't match to actual value also.322 */323 public static final int TAG_FOCAL_PLANE_UNIT = 0xA210;324 public static final int TAG_EXPOSURE_INDEX = 0xA215;325 public static final int TAG_SENSING_METHOD = 0xA217;326 public static final int TAG_FILE_SOURCE = 0xA300;327 public static final int TAG_SCENE_TYPE = 0xA301;328 public static final int TAG_CFA_PATTERN = 0xA302;329 330 // these tags new with Exif 2.2 (?) [A401 - A4331 /**332 * This tag indicates the use of special processing on image data, such as rendering333 * geared to output. When special processing is performed, the reader is expected to334 * disable or minimize any further processing.335 * Tag = 41985 (A401.H)336 * Type = SHORT337 * Count = 1338 * Default = 0339 * 0 = Normal process340 * 1 = Custom process341 * Other = reserved342 */343 public static final int TAG_CUSTOM_RENDERED = 0xA401;344 345 /**346 * This tag indicates the exposure mode set when the image was shot. In auto-bracketing347 * mode, the camera shoots a series of frames of the same scene at different exposure settings.348 * Tag = 41986 (A402.H)349 * Type = SHORT350 * Count = 1351 * Default = none352 * 0 = Auto exposure353 * 1 = Manual exposure354 * 2 = Auto bracket355 * Other = reserved356 */357 public static final int TAG_EXPOSURE_MODE = 0xA402;358 359 /**360 * This tag indicates the white balance mode set when the image was shot.361 * Tag = 41987 (A403.H)362 * Type = SHORT363 * Count = 1364 * Default = none365 * 0 = Auto white balance366 * 1 = Manual white balance367 * Other = reserved368 */369 public static final int TAG_WHITE_BALANCE_MODE = 0xA403;370 371 /**372 * This tag indicates the digital zoom ratio when the image was shot. If the373 * numerator of the recorded value is 0, this indicates that digital zoom was374 * not used.375 * Tag = 41988 (A404.H)376 * Type = RATIONAL377 * Count = 1378 * Default = none379 */380 public static final int TAG_DIGITAL_ZOOM_RATIO = 0xA404;381 382 /**383 * This tag indicates the equivalent focal length assuming a 35mm film camera,384 * in mm. A value of 0 means the focal length is unknown. Note that this tag385 * differs from the FocalLength tag.386 * Tag = 41989 (A405.H)387 * Type = SHORT388 * Count = 1389 * Default = none390 */391 public static final int TAG_35MM_FILM_EQUIV_FOCAL_LENGTH = 0xA405;392 393 /**394 * This tag indicates the type of scene that was shot. It can also be used to395 * record the mode in which the image was shot. Note that this differs from396 * the scene type (SceneType) tag.397 * Tag = 41990 (A406.H)398 * Type = SHORT399 * Count = 1400 * Default = 0401 * 0 = Standard402 * 1 = Landscape403 * 2 = Portrait404 * 3 = Night scene405 * Other = reserved406 */407 public static final int TAG_SCENE_CAPTURE_TYPE = 0xA406;408 409 /**410 * This tag indicates the degree of overall image gain adjustment.411 * Tag = 41991 (A407.H)412 * Type = SHORT413 * Count = 1414 * Default = none415 * 0 = None416 * 1 = Low gain up417 * 2 = High gain up418 * 3 = Low gain down419 * 4 = High gain down420 * Other = reserved421 */422 public static final int TAG_GAIN_CONTROL = 0xA407;423 424 /**425 * This tag indicates the direction of contrast processing applied by the camera426 * when the image was shot.427 * Tag = 41992 (A408.H)428 * Type = SHORT429 * Count = 1430 * Default = 0431 * 0 = Normal432 * 1 = Soft433 * 2 = Hard434 * Other = reserved435 */436 public static final int TAG_CONTRAST = 0xA408;437 438 /**439 * This tag indicates the direction of saturation processing applied by the camera440 * when the image was shot.441 * Tag = 41993 (A409.H)442 * Type = SHORT443 * Count = 1444 * Default = 0445 * 0 = Normal446 * 1 = Low saturation447 * 2 = High saturation448 * Other = reserved449 */450 public static final int TAG_SATURATION = 0xA409;451 452 /**453 * This tag indicates the direction of sharpness processing applied by the camera454 * when the image was shot.455 * Tag = 41994 (A40A.H)456 * Type = SHORT457 * Count = 1458 * Default = 0459 * 0 = Normal460 * 1 = Soft461 * 2 = Hard462 * Other = reserved463 */464 public static final int TAG_SHARPNESS = 0xA40A;465 466 // TODO support this tag (I haven't seen a camera's actual implementation of this yet)467 468 /**469 * This tag indicates information on the picture-taking conditions of a particular470 * camera model. The tag is used only to indicate the picture-taking conditions in471 * the reader.472 * Tag = 41995 (A40B.H)473 * Type = UNDEFINED474 * Count = Any475 * Default = none476 *477 * The information is recorded in the format shown below. The data is recorded478 * in Unicode using SHORT type for the number of display rows and columns and479 * UNDEFINED type for the camera settings. The Unicode (UCS-2) string including480 * Signature is NULL terminated. The specifics of the Unicode string are as given481 * in ISO/IEC 10464-1.482 *483 * Length Type Meaning484 * ------+-----------+------------------485 * 2 SHORT Display columns486 * 2 SHORT Display rows487 * Any UNDEFINED Camera setting-1488 * Any UNDEFINED Camera setting-2489 * : : :490 * Any UNDEFINED Camera setting-n491 */492 public static final int TAG_DEVICE_SETTING_DESCRIPTION = 0xA40B;493 494 /**495 * This tag indicates the distance to the subject.496 * Tag = 41996 (A40C.H)497 * Type = SHORT498 * Count = 1499 * Default = none500 * 0 = unknown501 * 1 = Macro502 * 2 = Close view503 * 3 = Distant view504 * Other = reserved505 */506 public static final int TAG_SUBJECT_DISTANCE_RANGE = 0xA40C;507 508 /**509 * The image title, as used by Windows XP.510 */511 public static final int TAG_WIN_TITLE = 0x9C9B;512 513 /**514 * The image comment, as used by Windows XP.515 */516 public static final int TAG_WIN_COMMENT = 0x9C9C;517 518 /**519 * The image author, as used by Windows XP (called Artist in the Windows shell).520 */521 public static final int TAG_WIN_AUTHOR = 0x9C9D;522 523 /**524 * The image keywords, as used by Windows XP.525 */526 public static final int TAG_WIN_KEYWORDS = 0x9C9E;527 528 /**529 * The image subject, as used by Windows XP.530 */531 public static final int TAG_WIN_SUBJECT = 0x9C9F;532 533 /**534 * This tag indicates an identifier assigned uniquely to each image. It is535 * recorded as an ASCII string equivalent to hexadecimal notation and 128-bit536 * fixed length.537 * Tag = 42016 (A420.H)538 * Type = ASCII539 * Count = 33540 * Default = none541 */542 public static final int TAG_IMAGE_UNIQUE_ID = 0xA420;543 544 public static final int TAG_THUMBNAIL_IMAGE_WIDTH = 0x0100;545 public static final int TAG_THUMBNAIL_IMAGE_HEIGHT = 0x0101;546 public static final int TAG_THUMBNAIL_DATA = 0xF001;547 548 /**549 * 1 = Normal550 * 2 = Reversed551 */552 public static final int TAG_FILL_ORDER = 0x010A;553 public static final int TAG_DOCUMENT_NAME = 0x010D;554 555 protected static final HashMap tagNameMap = new HashMap();556 557 static558 {559 tagNameMap.put(new Integer(TAG_FILL_ORDER), "Fill Order");560 tagNameMap.put(new Integer(TAG_DOCUMENT_NAME), "Document Name");561 tagNameMap.put(new Integer(0x1000), "Related Image File Format");562 tagNameMap.put(new Integer(0x1001), "Related Image Width");563 tagNameMap.put(new Integer(0x1002), "Related Image Length");564 tagNameMap.put(new Integer(0x0156), "Transfer Range");565 tagNameMap.put(new Integer(0x0200), "JPEG Proc");566 tagNameMap.put(new Integer(0x8769), "Exif Offset");567 tagNameMap.put(new Integer(TAG_COMPRESSION_LEVEL), "Compressed Bits Per Pixel");568 tagNameMap.put(new Integer(0x927C), "Maker Note");569 tagNameMap.put(new Integer(0xA005), "Interoperability Offset");570 571 tagNameMap.put(new Integer(TAG_NEW_SUBFILE_TYPE), "New Subfile Type");572 tagNameMap.put(new Integer(TAG_SUBFILE_TYPE), "Subfile Type");573 tagNameMap.put(new Integer(TAG_THUMBNAIL_IMAGE_WIDTH), "Thumbnail Image Width");574 tagNameMap.put(new Integer(TAG_THUMBNAIL_IMAGE_HEIGHT), "Thumbnail Image Height");575 tagNameMap.put(new Integer(TAG_BITS_PER_SAMPLE), "Bits Per Sample");576 tagNameMap.put(new Integer(TAG_COMPRESSION), "Compression");577 tagNameMap.put(new Integer(TAG_PHOTOMETRIC_INTERPRETATION), "Photometric Interpretation");578 tagNameMap.put(new Integer(TAG_THRESHOLDING), "Thresholding");579 tagNameMap.put(new Integer(TAG_IMAGE_DESCRIPTION), "Image Description");580 tagNameMap.put(new Integer(TAG_MAKE), "Make");581 tagNameMap.put(new Integer(TAG_MODEL), "Model");582 tagNameMap.put(new Integer(TAG_STRIP_OFFSETS), "Strip Offsets");583 tagNameMap.put(new Integer(TAG_ORIENTATION), "Orientation");584 tagNameMap.put(new Integer(TAG_SAMPLES_PER_PIXEL), "Samples Per Pixel");585 tagNameMap.put(new Integer(TAG_ROWS_PER_STRIP), "Rows Per Strip");586 tagNameMap.put(new Integer(TAG_STRIP_BYTE_COUNTS), "Strip Byte Counts");587 tagNameMap.put(new Integer(TAG_X_RESOLUTION), "X Resolution");588 tagNameMap.put(new Integer(TAG_Y_RESOLUTION), "Y Resolution");589 tagNameMap.put(new Integer(TAG_PAGE_NAME), "Page Name");590 tagNameMap.put(new Integer(TAG_PLANAR_CONFIGURATION), "Planar Configuration");591 tagNameMap.put(new Integer(TAG_RESOLUTION_UNIT), "Resolution Unit");592 tagNameMap.put(new Integer(TAG_TRANSFER_FUNCTION), "Transfer Function");593 tagNameMap.put(new Integer(TAG_SOFTWARE), "Software");594 tagNameMap.put(new Integer(TAG_DATETIME), "Date/Time");595 tagNameMap.put(new Integer(TAG_ARTIST), "Artist");596 tagNameMap.put(new Integer(TAG_PREDICTOR), "Predictor");597 tagNameMap.put(new Integer(TAG_WHITE_POINT), "White Point");598 tagNameMap.put(new Integer(TAG_PRIMARY_CHROMATICITIES), "Primary Chromaticities");599 tagNameMap.put(new Integer(TAG_TILE_WIDTH), "Tile Width");600 tagNameMap.put(new Integer(TAG_TILE_LENGTH), "Tile Length");601 tagNameMap.put(new Integer(TAG_TILE_OFFSETS), "Tile Offsets");602 tagNameMap.put(new Integer(TAG_TILE_BYTE_COUNTS), "Tile Byte Counts");603 tagNameMap.put(new Integer(TAG_SUB_IFDS), "Sub IFDs");604 tagNameMap.put(new Integer(TAG_JPEG_TABLES), "JPEG Tables");605 tagNameMap.put(new Integer(TAG_THUMBNAIL_OFFSET), "Thumbnail Offset");606 tagNameMap.put(new Integer(TAG_THUMBNAIL_LENGTH), "Thumbnail Length");607 tagNameMap.put(new Integer(TAG_THUMBNAIL_DATA), "Thumbnail Data");608 tagNameMap.put(new Integer(TAG_YCBCR_COEFFICIENTS), "YCbCr Coefficients");609 tagNameMap.put(new Integer(TAG_YCBCR_SUBSAMPLING), "YCbCr Sub-Sampling");610 tagNameMap.put(new Integer(TAG_YCBCR_POSITIONING), "YCbCr Positioning");611 tagNameMap.put(new Integer(TAG_REFERENCE_BLACK_WHITE), "Reference Black/White");612 tagNameMap.put(new Integer(TAG_CFA_REPEAT_PATTERN_DIM), "CFA Repeat Pattern Dim");613 tagNameMap.put(new Integer(TAG_CFA_PATTERN_2), "CFA Pattern");614 tagNameMap.put(new Integer(TAG_BATTERY_LEVEL), "Battery Level");615 tagNameMap.put(new Integer(TAG_COPYRIGHT), "Copyright");616 tagNameMap.put(new Integer(TAG_EXPOSURE_TIME), "Exposure Time");617 tagNameMap.put(new Integer(TAG_FNUMBER), "F-Number");618 tagNameMap.put(new Integer(TAG_IPTC_NAA), "IPTC/NAA");619 tagNameMap.put(new Integer(TAG_INTER_COLOR_PROFILE), "Inter Color Profile");620 tagNameMap.put(new Integer(TAG_EXPOSURE_PROGRAM), "Exposure Program");621 tagNameMap.put(new Integer(TAG_SPECTRAL_SENSITIVITY), "Spectral Sensitivity");622 tagNameMap.put(new Integer(TAG_GPS_INFO), "GPS Info");623 tagNameMap.put(new Integer(TAG_ISO_EQUIVALENT), "ISO Speed Ratings");624 tagNameMap.put(new Integer(TAG_OECF), "OECF");625 tagNameMap.put(new Integer(TAG_INTERLACE), "Interlace");626 tagNameMap.put(new Integer(TAG_TIME_ZONE_OFFSET), "Time Zone Offset");627 tagNameMap.put(new Integer(TAG_SELF_TIMER_MODE), "Self Timer Mode");628 tagNameMap.put(new Integer(TAG_EXIF_VERSION), "Exif Version");629 tagNameMap.put(new Integer(TAG_DATETIME_ORIGINAL), "Date/Time Original");630 tagNameMap.put(new Integer(TAG_DATETIME_DIGITIZED), "Date/Time Digitized");631 tagNameMap.put(new Integer(TAG_COMPONENTS_CONFIGURATION), "Components Configuration");632 tagNameMap.put(new Integer(TAG_SHUTTER_SPEED), "Shutter Speed Value");633 tagNameMap.put(new Integer(TAG_APERTURE), "Aperture Value");634 tagNameMap.put(new Integer(TAG_BRIGHTNESS_VALUE), "Brightness Value");635 tagNameMap.put(new Integer(TAG_EXPOSURE_BIAS), "Exposure Bias Value");636 tagNameMap.put(new Integer(TAG_MAX_APERTURE), "Max Aperture Value");637 tagNameMap.put(new Integer(TAG_SUBJECT_DISTANCE), "Subject Distance");638 tagNameMap.put(new Integer(TAG_METERING_MODE), "Metering Mode");639 tagNameMap.put(new Integer(TAG_WHITE_BALANCE), "Light Source");640 tagNameMap.put(new Integer(TAG_FLASH), "Flash");641 tagNameMap.put(new Integer(TAG_FOCAL_LENGTH), "Focal Length");642 tagNameMap.put(new Integer(TAG_FLASH_ENERGY), "Flash Energy");643 tagNameMap.put(new Integer(TAG_SPATIAL_FREQ_RESPONSE), "Spatial Frequency Response");644 tagNameMap.put(new Integer(TAG_NOISE), "Noise");645 tagNameMap.put(new Integer(TAG_IMAGE_NUMBER), "Image Number");646 tagNameMap.put(new Integer(TAG_SECURITY_CLASSIFICATION), "Security Classification");647 tagNameMap.put(new Integer(TAG_IMAGE_HISTORY), "Image History");648 tagNameMap.put(new Integer(TAG_SUBJECT_LOCATION), "Subject Location");649 tagNameMap.put(new Integer(TAG_EXPOSURE_INDEX), "Exposure Index");650 tagNameMap.put(new Integer(TAG_TIFF_EP_STANDARD_ID), "TIFF/EP Standard ID");651 tagNameMap.put(new Integer(TAG_USER_COMMENT), "User Comment");652 tagNameMap.put(new Integer(TAG_SUBSECOND_TIME), "Sub-Sec Time");653 tagNameMap.put(new Integer(TAG_SUBSECOND_TIME_ORIGINAL), "Sub-Sec Time Original");654 tagNameMap.put(new Integer(TAG_SUBSECOND_TIME_DIGITIZED), "Sub-Sec Time Digitized");655 tagNameMap.put(new Integer(TAG_FLASHPIX_VERSION), "FlashPix Version");656 tagNameMap.put(new Integer(TAG_COLOR_SPACE), "Color Space");657 tagNameMap.put(new Integer(TAG_EXIF_IMAGE_WIDTH), "Exif Image Width");658 tagNameMap.put(new Integer(TAG_EXIF_IMAGE_HEIGHT), "Exif Image Height");659 tagNameMap.put(new Integer(TAG_RELATED_SOUND_FILE), "Related Sound File");660 // 0x920B in TIFF/EP661 tagNameMap.put(new Integer(TAG_FLASH_ENERGY_2), "Flash Energy");662 // 0x920C in TIFF/EP663 tagNameMap.put(new Integer(TAG_SPATIAL_FREQ_RESPONSE_2), "Spatial Frequency Response");664 // 0x920E in TIFF/EP665 tagNameMap.put(new Integer(TAG_FOCAL_PLANE_X_RES), "Focal Plane X Resolution");666 // 0x920F in TIFF/EP667 tagNameMap.put(new Integer(TAG_FOCAL_PLANE_Y_RES), "Focal Plane Y Resolution");668 // 0x9210 in TIFF/EP669 tagNameMap.put(new Integer(TAG_FOCAL_PLANE_UNIT), "Focal Plane Resolution Unit");670 // 0x9214 in TIFF/EP671 tagNameMap.put(new Integer(TAG_SUBJECT_LOCATION_2), "Subject Location");672 // 0x9215 in TIFF/EP673 tagNameMap.put(new Integer(TAG_EXPOSURE_INDEX_2), "Exposure Index");674 // 0x9217 in TIFF/EP675 tagNameMap.put(new Integer(TAG_SENSING_METHOD), "Sensing Method");676 tagNameMap.put(new Integer(TAG_FILE_SOURCE), "File Source");677 tagNameMap.put(new Integer(TAG_SCENE_TYPE), "Scene Type");678 tagNameMap.put(new Integer(TAG_CFA_PATTERN), "CFA Pattern");679 680 tagNameMap.put(new Integer(TAG_CUSTOM_RENDERED), "Custom Rendered");681 tagNameMap.put(new Integer(TAG_EXPOSURE_MODE), "Exposure Mode");682 tagNameMap.put(new Integer(TAG_WHITE_BALANCE_MODE), "White Balance");683 tagNameMap.put(new Integer(TAG_DIGITAL_ZOOM_RATIO), "Digital Zoom Ratio");684 tagNameMap.put(new Integer(TAG_35MM_FILM_EQUIV_FOCAL_LENGTH), "Focal Length 35");685 tagNameMap.put(new Integer(TAG_SCENE_CAPTURE_TYPE), "Scene Capture Type");686 tagNameMap.put(new Integer(TAG_GAIN_CONTROL), "Gain Control");687 tagNameMap.put(new Integer(TAG_CONTRAST), "Contrast");688 tagNameMap.put(new Integer(TAG_SATURATION), "Saturation");689 tagNameMap.put(new Integer(TAG_SHARPNESS), "Sharpness");690 tagNameMap.put(new Integer(TAG_DEVICE_SETTING_DESCRIPTION), "Device Setting Description");691 tagNameMap.put(new Integer(TAG_SUBJECT_DISTANCE_RANGE), "Subject Distance Range");692 693 tagNameMap.put(new Integer(TAG_WIN_AUTHOR), "Windows XP Author");694 tagNameMap.put(new Integer(TAG_WIN_COMMENT), "Windows XP Comment");695 tagNameMap.put(new Integer(TAG_WIN_KEYWORDS), "Windows XP Keywords");696 tagNameMap.put(new Integer(TAG_WIN_SUBJECT), "Windows XP Subject");697 tagNameMap.put(new Integer(TAG_WIN_TITLE), "Windows XP Title");698 699 tagNameMap.put(new Integer(TAG_MIN_SAMPLE_VALUE), "Minimum sample value");700 tagNameMap.put(new Integer(TAG_MAX_SAMPLE_VALUE), "Maximum sample value");701 }702 703 public ExifDirectory()704 {705 this.setDescriptor(new ExifDescriptor(this));706 }707 708 public String getName()709 {710 return "Exif";711 }712 713 protected HashMap getTagNameMap()714 {715 return tagNameMap;716 }717 718 public byte[] getThumbnailData() throws MetadataException719 {720 if (!containsThumbnail())721 return null;722 723 return this.getByteArray(ExifDirectory.TAG_THUMBNAIL_DATA);724 }725 726 public void writeThumbnail(String filename) throws MetadataException, IOException727 {728 byte[] data = getThumbnailData();729 730 if (data==null)731 throw new MetadataException("No thumbnail data exists.");732 733 FileOutputStream stream = null;734 try {735 stream = new FileOutputStream(filename);736 stream.write(data);737 } finally {738 if (stream!=null)739 stream.close();740 }741 }742 743 /*744 // This thumbnail extraction code is not complete, and is included to assist anyone who feels like looking into745 // it. Please share any progress with the original author, and hence the community. Thanks.746 747 /**748 *749 * @return750 * @throws MetadataException751 * /752 public Image getThumbnailImage() throws MetadataException753 {754 if (!containsThumbnail())755 return null;756 757 int compression = 0;758 try {759 compression = this.getInt(ExifDirectory.TAG_COMPRESSION);760 } catch (Throwable e) {761 this.addError("Unable to determine thumbnail type " + e.getMessage());762 }763 764 final byte[] thumbnailBytes = getThumbnailData();765 766 if (compression == ExifDirectory.COMPRESSION_JPEG)767 {768 // JPEG Thumbnail769 // operate directly on thumbnailBytes770 // try {771 // int offset = this.getInt(ExifDirectory.TAG_THUMBNAIL_OFFSET);772 // int length = this.getInt(ExifDirectory.TAG_THUMBNAIL_LENGTH);773 // byte[] result = new byte[length];774 // for (int i = 0; i<result.length; i++) {775 // result[i] = _data[tiffHeaderOffset + offset + i];776 // }777 // this.setByteArray(ExifDirectory.TAG_THUMBNAIL_DATA, result);778 // } catch (Throwable e) {779 // this.addError("Unable to extract thumbnail: " + e.getMessage());780 // }781 // TODO decode the JPEG bytes as an image782 return null; //new Image();783 }784 else if (compression == ExifDirectory.COMPRESSION_NONE)785 {786 // uncompressed thumbnail (raw RGB data)787 if (!this.containsTag(ExifDirectory.TAG_PHOTOMETRIC_INTERPRETATION))788 return null;789 790 try791 {792 // If the image is RGB format, then convert it to a bitmap793 final int photometricInterpretation = this.getInt(ExifDirectory.TAG_PHOTOMETRIC_INTERPRETATION);794 if (photometricInterpretation == ExifDirectory.PHOTOMETRIC_INTERPRETATION_RGB)795 {796 // RGB797 Image image = createImageFromRawRgb(thumbnailBytes);798 return image;799 }800 else if (photometricInterpretation == ExifDirectory.PHOTOMETRIC_INTERPRETATION_YCBCR)801 {802 // YCbCr803 Image image = createImageFromRawYCbCr(thumbnailBytes);804 return image;805 }806 else if (photometricInterpretation == ExifDirectory.PHOTOMETRIC_INTERPRETATION_MONOCHROME)807 {808 // Monochrome809 // TODO810 return null;811 }812 } catch (Throwable e) {813 this.addError("Unable to extract thumbnail: " + e.getMessage());814 }815 }816 return null;817 }818 819 /**820 * Handle the YCbCr thumbnail encoding used by Ricoh RDC4200/4300, Fuji DS-7/300 and DX-5/7/9 cameras.821 *822 * At DX-5/7/9, YCbCrSubsampling(0x0212) has values of '2,1', PlanarConfiguration(0x011c) has a value '1'. So the823 * data align of this image is below.824 *825 * Y(0,0),Y(1,0),Cb(0,0),Cr(0,0), Y(2,0),Y(3,0),Cb(2,0),Cr(3.0), Y(4,0),Y(5,0),Cb(4,0),Cr(4,0). . . .826 *827 * The numerics in parenthesis are pixel coordinates. DX series' YCbCrCoefficients(0x0211) has values '0.299/0.587/0.114',828 * ReferenceBlackWhite(0x0214) has values '0,255,128,255,128,255'. Therefore to convert from Y/Cb/Cr to RGB is;829 *830 * B(0,0)=(Cb-128)*(2-0.114*2)+Y(0,0)831 * R(0,0)=(Cr-128)*(2-0.299*2)+Y(0,0)832 * G(0,0)=(Y(0,0)-0.114*B(0,0)-0.299*R(0,0))/0.587833 *834 * Horizontal subsampling is a value '2', so you can calculate B(1,0)/R(1,0)/G(1,0) by using the Y(1,0) and Cr(0,0)/Cb(0,0).835 * Repeat this conversion by value of ImageWidth(0x0100) and ImageLength(0x0101).836 *837 * @param thumbnailBytes838 * @return839 * @throws com.drew.metadata.MetadataException840 * /841 private Image createImageFromRawYCbCr(byte[] thumbnailBytes) throws MetadataException842 {843 /*844 Y = 0.257R + 0.504G + 0.098B + 16845 Cb = -0.148R - 0.291G + 0.439B + 128846 Cr = 0.439R - 0.368G - 0.071B + 128847 848 G = 1.164(Y-16) - 0.391(Cb-128) - 0.813(Cr-128)849 R = 1.164(Y-16) + 1.596(Cr-128)850 B = 1.164(Y-16) + 2.018(Cb-128)851 852 R, G and B range from 0 to 255.853 Y ranges from 16 to 235.854 Cb and Cr range from 16 to 240.855 856 http://www.faqs.org/faqs/graphics/colorspace-faq/857 * /858 859 int length = thumbnailBytes.length; // this.getInt(ExifDirectory.TAG_STRIP_BYTE_COUNTS);860 final int imageWidth = this.getInt(ExifDirectory.TAG_THUMBNAIL_IMAGE_WIDTH);861 final int imageHeight = this.getInt(ExifDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT);862 // final int headerLength = 54;863 // byte[] result = new byte[length + headerLength];864 // // Add a windows BMP header described:865 // // http://www.onicos.com/staff/iz/formats/bmp.html866 // result[0] = 'B';867 // result[1] = 'M'; // File Type identifier868 // result[3] = (byte)(result.length / 256);869 // result[2] = (byte)result.length;870 // result[10] = (byte)headerLength;871 // result[14] = 40; // MS Windows BMP header872 // result[18] = (byte)imageWidth;873 // result[22] = (byte)imageHeight;874 // result[26] = 1; // 1 Plane875 // result[28] = 24; // Colour depth876 // result[34] = (byte)length;877 // result[35] = (byte)(length / 256);878 879 final BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);880 881 // order is YCbCr and image is upside down, bitmaps are BGR882 //// for (int i = headerLength, dataOffset = length; i<result.length; i += 3, dataOffset -= 3)883 // {884 // final int y = thumbnailBytes[dataOffset - 2] & 0xFF;885 // final int cb = thumbnailBytes[dataOffset - 1] & 0xFF;886 // final int cr = thumbnailBytes[dataOffset] & 0xFF;887 // if (y<16 || y>235 || cb<16 || cb>240 || cr<16 || cr>240)888 // "".toString();889 //890 // int g = (int)(1.164*(y-16) - 0.391*(cb-128) - 0.813*(cr-128));891 // int r = (int)(1.164*(y-16) + 1.596*(cr-128));892 // int b = (int)(1.164*(y-16) + 2.018*(cb-128));893 //894 //// result[i] = (byte)b;895 //// result[i + 1] = (byte)g;896 //// result[i + 2] = (byte)r;897 //898 // // TODO compose the image here899 // image.setRGB(1, 2, 3);900 // }901 902 return image;903 }904 905 /**906 * Creates a thumbnail image in (Windows) BMP format from raw RGB data.907 * @param thumbnailBytes908 * @return909 * @throws com.drew.metadata.MetadataException910 * /911 private Image createImageFromRawRgb(byte[] thumbnailBytes) throws MetadataException912 {913 final int length = thumbnailBytes.length; // this.getInt(ExifDirectory.TAG_STRIP_BYTE_COUNTS);914 final int imageWidth = this.getInt(ExifDirectory.TAG_THUMBNAIL_IMAGE_WIDTH);915 final int imageHeight = this.getInt(ExifDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT);916 // final int headerlength = 54;917 // final byte[] result = new byte[length + headerlength];918 // // Add a windows BMP header described:919 // // http://www.onicos.com/staff/iz/formats/bmp.html920 // result[0] = 'B';921 // result[1] = 'M'; // File Type identifier922 // result[3] = (byte)(result.length / 256);923 // result[2] = (byte)result.length;924 // result[10] = (byte)headerlength;925 // result[14] = 40; // MS Windows BMP header926 // result[18] = (byte)imageWidth;927 // result[22] = (byte)imageHeight;928 // result[26] = 1; // 1 Plane929 // result[28] = 24; // Colour depth930 // result[34] = (byte)length;931 // result[35] = (byte)(length / 256);932 933 final BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);934 935 // order is RGB and image is upside down, bitmaps are BGR936 // for (int i = headerlength, dataOffset = length; i<result.length; i += 3, dataOffset -= 3)937 // {938 // byte b = thumbnailBytes[dataOffset - 2];939 // byte g = thumbnailBytes[dataOffset - 1];940 // byte r = thumbnailBytes[dataOffset];941 //942 // // TODO compose the image here943 // image.setRGB(1, 2, 3);944 // }945 946 return image;947 }948 */949 950 public boolean containsThumbnail()951 {952 return containsTag(ExifDirectory.TAG_THUMBNAIL_DATA);953 }954 } -
src/com/drew/metadata/exif/ExifIFD0Descriptor.java
1 /* 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/ 20 */ 21 22 package com.drew.metadata.exif; 23 24 import com.drew.lang.Rational; 25 import com.drew.lang.annotations.NotNull; 26 import com.drew.lang.annotations.Nullable; 27 import com.drew.metadata.TagDescriptor; 28 29 import java.io.UnsupportedEncodingException; 30 31 /** 32 * Provides human-readable string representations of tag values stored in a <code>ExifIFD0Directory</code>. 33 * 34 * @author Drew Noakes http://drewnoakes.com 35 */ 36 public class ExifIFD0Descriptor extends TagDescriptor<ExifIFD0Directory> 37 { 38 /** 39 * Dictates whether rational values will be represented in decimal format in instances 40 * where decimal notation is elegant (such as 1/2 -> 0.5, but not 1/3). 41 */ 42 private final boolean _allowDecimalRepresentationOfRationals = true; 43 44 public ExifIFD0Descriptor(@NotNull ExifIFD0Directory directory) 45 { 46 super(directory); 47 } 48 49 // Note for the potential addition of brightness presentation in eV: 50 // Brightness of taken subject. To calculate Exposure(Ev) from BrightnessValue(Bv), 51 // you must add SensitivityValue(Sv). 52 // Ev=BV+Sv Sv=log2(ISOSpeedRating/3.125) 53 // ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32. 54 55 /** 56 * Returns a descriptive value of the the specified tag for this image. 57 * Where possible, known values will be substituted here in place of the raw 58 * tokens actually kept in the Exif segment. If no substitution is 59 * available, the value provided by getString(int) will be returned. 60 * @param tagType the tag to find a description for 61 * @return a description of the image's value for the specified tag, or 62 * <code>null</code> if the tag hasn't been defined. 63 */ 64 @Nullable 65 public String getDescription(int tagType) 66 { 67 switch (tagType) { 68 case ExifIFD0Directory.TAG_RESOLUTION_UNIT: 69 return getResolutionDescription(); 70 case ExifIFD0Directory.TAG_YCBCR_POSITIONING: 71 return getYCbCrPositioningDescription(); 72 case ExifIFD0Directory.TAG_X_RESOLUTION: 73 return getXResolutionDescription(); 74 case ExifIFD0Directory.TAG_Y_RESOLUTION: 75 return getYResolutionDescription(); 76 case ExifIFD0Directory.TAG_REFERENCE_BLACK_WHITE: 77 return getReferenceBlackWhiteDescription(); 78 case ExifIFD0Directory.TAG_ORIENTATION: 79 return getOrientationDescription(); 80 81 case ExifIFD0Directory.TAG_WIN_AUTHOR: 82 return getWindowsAuthorDescription(); 83 case ExifIFD0Directory.TAG_WIN_COMMENT: 84 return getWindowsCommentDescription(); 85 case ExifIFD0Directory.TAG_WIN_KEYWORDS: 86 return getWindowsKeywordsDescription(); 87 case ExifIFD0Directory.TAG_WIN_SUBJECT: 88 return getWindowsSubjectDescription(); 89 case ExifIFD0Directory.TAG_WIN_TITLE: 90 return getWindowsTitleDescription(); 91 92 default: 93 return super.getDescription(tagType); 94 } 95 } 96 97 @Nullable 98 public String getReferenceBlackWhiteDescription() 99 { 100 int[] ints = _directory.getIntArray(ExifIFD0Directory.TAG_REFERENCE_BLACK_WHITE); 101 if (ints==null) 102 return null; 103 int blackR = ints[0]; 104 int whiteR = ints[1]; 105 int blackG = ints[2]; 106 int whiteG = ints[3]; 107 int blackB = ints[4]; 108 int whiteB = ints[5]; 109 return "[" + blackR + "," + blackG + "," + blackB + "] " + 110 "[" + whiteR + "," + whiteG + "," + whiteB + "]"; 111 } 112 113 @Nullable 114 public String getYResolutionDescription() 115 { 116 Rational value = _directory.getRational(ExifIFD0Directory.TAG_Y_RESOLUTION); 117 if (value==null) 118 return null; 119 final String unit = getResolutionDescription(); 120 return value.toSimpleString(_allowDecimalRepresentationOfRationals) + 121 " dots per " + 122 (unit==null ? "unit" : unit.toLowerCase()); 123 } 124 125 @Nullable 126 public String getXResolutionDescription() 127 { 128 Rational value = _directory.getRational(ExifIFD0Directory.TAG_X_RESOLUTION); 129 if (value==null) 130 return null; 131 final String unit = getResolutionDescription(); 132 return value.toSimpleString(_allowDecimalRepresentationOfRationals) + 133 " dots per " + 134 (unit==null ? "unit" : unit.toLowerCase()); 135 } 136 137 @Nullable 138 public String getYCbCrPositioningDescription() 139 { 140 Integer value = _directory.getInteger(ExifIFD0Directory.TAG_YCBCR_POSITIONING); 141 if (value==null) 142 return null; 143 switch (value) { 144 case 1: return "Center of pixel array"; 145 case 2: return "Datum point"; 146 default: 147 return String.valueOf(value); 148 } 149 } 150 151 @Nullable 152 public String getOrientationDescription() 153 { 154 Integer value = _directory.getInteger(ExifIFD0Directory.TAG_ORIENTATION); 155 if (value==null) 156 return null; 157 switch (value) { 158 case 1: return "Top, left side (Horizontal / normal)"; 159 case 2: return "Top, right side (Mirror horizontal)"; 160 case 3: return "Bottom, right side (Rotate 180)"; 161 case 4: return "Bottom, left side (Mirror vertical)"; 162 case 5: return "Left side, top (Mirror horizontal and rotate 270 CW)"; 163 case 6: return "Right side, top (Rotate 90 CW)"; 164 case 7: return "Right side, bottom (Mirror horizontal and rotate 90 CW)"; 165 case 8: return "Left side, bottom (Rotate 270 CW)"; 166 default: 167 return String.valueOf(value); 168 } 169 } 170 171 @Nullable 172 public String getResolutionDescription() 173 { 174 // '1' means no-unit, '2' means inch, '3' means centimeter. Default value is '2'(inch) 175 Integer value = _directory.getInteger(ExifIFD0Directory.TAG_RESOLUTION_UNIT); 176 if (value==null) 177 return null; 178 switch (value) { 179 case 1: return "(No unit)"; 180 case 2: return "Inch"; 181 case 3: return "cm"; 182 default: 183 return ""; 184 } 185 } 186 187 /** The Windows specific tags uses plain Unicode. */ 188 @Nullable 189 private String getUnicodeDescription(int tag) 190 { 191 byte[] commentBytes = _directory.getByteArray(tag); 192 if (commentBytes==null) 193 return null; 194 try { 195 // Decode the unicode string and trim the unicode zero "\0" from the end. 196 return new String(commentBytes, "UTF-16LE").trim(); 197 } 198 catch (UnsupportedEncodingException ex) { 199 return null; 200 } 201 } 202 203 @Nullable 204 public String getWindowsAuthorDescription() 205 { 206 return getUnicodeDescription(ExifIFD0Directory.TAG_WIN_AUTHOR); 207 } 208 209 @Nullable 210 public String getWindowsCommentDescription() 211 { 212 return getUnicodeDescription(ExifIFD0Directory.TAG_WIN_COMMENT); 213 } 214 215 @Nullable 216 public String getWindowsKeywordsDescription() 217 { 218 return getUnicodeDescription(ExifIFD0Directory.TAG_WIN_KEYWORDS); 219 } 220 221 @Nullable 222 public String getWindowsTitleDescription() 223 { 224 return getUnicodeDescription(ExifIFD0Directory.TAG_WIN_TITLE); 225 } 226 227 @Nullable 228 public String getWindowsSubjectDescription() 229 { 230 return getUnicodeDescription(ExifIFD0Directory.TAG_WIN_SUBJECT); 231 } 232 } -
src/com/drew/metadata/exif/ExifIFD0Descriptor.java
-
src/com/drew/metadata/exif/ExifIFD0Directory.java
Modification de propriétés sur src/com/drew/metadata/exif/ExifIFD0Descriptor.java ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
1 /* 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/ 20 */ 21 22 package com.drew.metadata.exif; 23 24 import com.drew.lang.annotations.NotNull; 25 import com.drew.metadata.Directory; 26 27 import java.util.HashMap; 28 29 /** 30 * Describes Exif tags from the IFD0 directory. 31 * 32 * @author Drew Noakes http://drewnoakes.com 33 */ 34 public class ExifIFD0Directory extends Directory 35 { 36 public static final int TAG_IMAGE_DESCRIPTION = 0x010E; 37 public static final int TAG_MAKE = 0x010F; 38 public static final int TAG_MODEL = 0x0110; 39 public static final int TAG_ORIENTATION = 0x0112; 40 public static final int TAG_X_RESOLUTION = 0x011A; 41 public static final int TAG_Y_RESOLUTION = 0x011B; 42 public static final int TAG_RESOLUTION_UNIT = 0x0128; 43 public static final int TAG_SOFTWARE = 0x0131; 44 public static final int TAG_DATETIME = 0x0132; 45 public static final int TAG_ARTIST = 0x013B; 46 public static final int TAG_WHITE_POINT = 0x013E; 47 public static final int TAG_PRIMARY_CHROMATICITIES = 0x013F; 48 public static final int TAG_YCBCR_COEFFICIENTS = 0x0211; 49 public static final int TAG_YCBCR_POSITIONING = 0x0213; 50 public static final int TAG_REFERENCE_BLACK_WHITE = 0x0214; 51 public static final int TAG_COPYRIGHT = 0x8298; 52 53 /** The image title, as used by Windows XP. */ 54 public static final int TAG_WIN_TITLE = 0x9C9B; 55 /** The image comment, as used by Windows XP. */ 56 public static final int TAG_WIN_COMMENT = 0x9C9C; 57 /** The image author, as used by Windows XP (called Artist in the Windows shell). */ 58 public static final int TAG_WIN_AUTHOR = 0x9C9D; 59 /** The image keywords, as used by Windows XP. */ 60 public static final int TAG_WIN_KEYWORDS = 0x9C9E; 61 /** The image subject, as used by Windows XP. */ 62 public static final int TAG_WIN_SUBJECT = 0x9C9F; 63 64 @NotNull 65 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 66 67 static 68 { 69 _tagNameMap.put(TAG_IMAGE_DESCRIPTION, "Image Description"); 70 _tagNameMap.put(TAG_MAKE, "Make"); 71 _tagNameMap.put(TAG_MODEL, "Model"); 72 _tagNameMap.put(TAG_ORIENTATION, "Orientation"); 73 _tagNameMap.put(TAG_X_RESOLUTION, "X Resolution"); 74 _tagNameMap.put(TAG_Y_RESOLUTION, "Y Resolution"); 75 _tagNameMap.put(TAG_RESOLUTION_UNIT, "Resolution Unit"); 76 _tagNameMap.put(TAG_SOFTWARE, "Software"); 77 _tagNameMap.put(TAG_DATETIME, "Date/Time"); 78 _tagNameMap.put(TAG_ARTIST, "Artist"); 79 _tagNameMap.put(TAG_WHITE_POINT, "White Point"); 80 _tagNameMap.put(TAG_PRIMARY_CHROMATICITIES, "Primary Chromaticities"); 81 _tagNameMap.put(TAG_YCBCR_COEFFICIENTS, "YCbCr Coefficients"); 82 _tagNameMap.put(TAG_YCBCR_POSITIONING, "YCbCr Positioning"); 83 _tagNameMap.put(TAG_REFERENCE_BLACK_WHITE, "Reference Black/White"); 84 _tagNameMap.put(TAG_COPYRIGHT, "Copyright"); 85 86 _tagNameMap.put(TAG_WIN_AUTHOR, "Windows XP Author"); 87 _tagNameMap.put(TAG_WIN_COMMENT, "Windows XP Comment"); 88 _tagNameMap.put(TAG_WIN_KEYWORDS, "Windows XP Keywords"); 89 _tagNameMap.put(TAG_WIN_SUBJECT, "Windows XP Subject"); 90 _tagNameMap.put(TAG_WIN_TITLE, "Windows XP Title"); 91 } 92 93 public ExifIFD0Directory() 94 { 95 this.setDescriptor(new ExifIFD0Descriptor(this)); 96 } 97 98 @NotNull 99 public String getName() 100 { 101 return "Exif IFD0"; 102 } 103 104 @NotNull 105 protected HashMap<Integer, String> getTagNameMap() 106 { 107 return _tagNameMap; 108 } 109 } -
src/com/drew/metadata/exif/ExifIFD0Directory.java
-
src/com/drew/metadata/exif/ExifInteropDescriptor.java
Modification de propriétés sur src/com/drew/metadata/exif/ExifIFD0Directory.java ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
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 22:27:34 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>ExifInteropDirectory</code>. 24 29 * 30 * @author Drew Noakes http://drewnoakes.com 25 31 */ 26 public class ExifInteropDescriptor extends TagDescriptor 32 public class ExifInteropDescriptor extends TagDescriptor<ExifInteropDirectory> 27 33 { 28 public ExifInteropDescriptor( Directory directory)34 public ExifInteropDescriptor(@NotNull ExifInteropDirectory 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 43 case ExifInteropDirectory.TAG_INTEROP_INDEX: … … 38 45 case ExifInteropDirectory.TAG_INTEROP_VERSION: 39 46 return getInteropVersionDescription(); 40 47 default: 41 return _directory.getString(tagType);48 return super.getDescription(tagType); 42 49 } 43 50 } 44 51 45 public String getInteropVersionDescription() throws MetadataException 52 @Nullable 53 public String getInteropVersionDescription() 46 54 { 47 if (!_directory.containsTag(ExifInteropDirectory.TAG_INTEROP_VERSION)) return null;48 55 int[] ints = _directory.getIntArray(ExifInteropDirectory.TAG_INTEROP_VERSION); 49 return ExifDescriptor.convertBytesToVersionString(ints);56 return convertBytesToVersionString(ints, 2); 50 57 } 51 58 59 @Nullable 52 60 public String getInteropIndexDescription() 53 61 { 54 if (!_directory.containsTag(ExifInteropDirectory.TAG_INTEROP_INDEX)) return null; 55 String interopIndex = _directory.getString(ExifInteropDirectory.TAG_INTEROP_INDEX).trim(); 56 if ("R98".equalsIgnoreCase(interopIndex)) { 57 return "Recommended Exif Interoperability Rules (ExifR98)"; 58 } else { 59 return "Unknown (" + interopIndex + ")"; 60 } 62 String value = _directory.getString(ExifInteropDirectory.TAG_INTEROP_INDEX); 63 64 if (value==null) 65 return null; 66 67 return "R98".equalsIgnoreCase(value.trim()) 68 ? "Recommended Exif Interoperability Rules (ExifR98)" 69 : "Unknown (" + value + ")"; 61 70 } 62 71 } -
src/com/drew/metadata/exif/ExifInteropDirectory.java
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 10:58:13 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 21 26 import java.util.HashMap; 22 27 23 28 /** 29 * Describes Exif interoperability tags. 24 30 * 31 * @author Drew Noakes http://drewnoakes.com 25 32 */ 26 33 public class ExifInteropDirectory extends Directory 27 34 { … … 31 38 public static final int TAG_RELATED_IMAGE_WIDTH = 0x1001; 32 39 public static final int TAG_RELATED_IMAGE_LENGTH = 0x1002; 33 40 34 protected static final HashMap tagNameMap; 41 @NotNull 42 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 35 43 36 44 static 37 45 { 38 tagNameMap = new HashMap(); 39 tagNameMap.put(new Integer(TAG_INTEROP_INDEX), "Interoperability Index"); 40 tagNameMap.put(new Integer(TAG_INTEROP_VERSION), "Interoperability Version"); 41 tagNameMap.put(new Integer(TAG_RELATED_IMAGE_FILE_FORMAT), "Related Image File Format"); 42 tagNameMap.put(new Integer(TAG_RELATED_IMAGE_WIDTH), "Related Image Width"); 43 tagNameMap.put(new Integer(TAG_RELATED_IMAGE_LENGTH), "Related Image Length"); 46 _tagNameMap.put(TAG_INTEROP_INDEX, "Interoperability Index"); 47 _tagNameMap.put(TAG_INTEROP_VERSION, "Interoperability Version"); 48 _tagNameMap.put(TAG_RELATED_IMAGE_FILE_FORMAT, "Related Image File Format"); 49 _tagNameMap.put(TAG_RELATED_IMAGE_WIDTH, "Related Image Width"); 50 _tagNameMap.put(TAG_RELATED_IMAGE_LENGTH, "Related Image Length"); 44 51 } 45 52 46 53 public ExifInteropDirectory() … … 48 55 this.setDescriptor(new ExifInteropDescriptor(this)); 49 56 } 50 57 58 @NotNull 51 59 public String getName() 52 60 { 53 61 return "Interoperability"; 54 62 } 55 63 56 protected HashMap getTagNameMap() 64 @NotNull 65 protected HashMap<Integer, String> getTagNameMap() 57 66 { 58 return tagNameMap;67 return _tagNameMap; 59 68 } 60 69 } -
src/com/drew/metadata/exif/ExifProcessingException.java
1 /*2 * ExifProcessingException.java3 *4 * This class is public domain software - that is, you can do whatever you want5 * with it, and include it software that is licensed under the GNU or the6 * BSD license, or whatever other licence you choose, including proprietary7 * closed source licenses. I do ask that you leave this header in tact.8 *9 * If you make modifications to this code that you think would benefit the10 * wider community, please send me a copy and I'll post it on my site.11 *12 * If you make use of this code, I'd appreciate hearing about it.13 * drew@drewnoakes.com14 * Latest version of this software kept at15 * http://drewnoakes.com/16 *17 * Created on 29 April 2002, 00:3318 */19 package com.drew.metadata.exif;20 21 import com.drew.metadata.MetadataException;22 23 /**24 * The exception type raised during reading of Exif data in the instance of25 * unexpected data conditions.26 * @author Drew Noakes http://drewnoakes.com27 */28 public class ExifProcessingException extends MetadataException29 {30 /**31 * Constructs an instance of <code>ExifProcessingException</code> with the32 * specified detail message.33 * @param message the detail message34 */35 public ExifProcessingException(String message)36 {37 super(message);38 }39 40 /**41 * Constructs an instance of <code>ExifProcessingException</code> with the42 * specified detail message and inner exception.43 * @param message the detail message44 * @param cause an inner exception45 */46 public ExifProcessingException(String message, Throwable cause)47 {48 super(message, cause);49 }50 } -
src/com/drew/metadata/exif/ExifReader.java
1 1 /* 2 * EXIFExtractor.java2 * Copyright 2002-2012 Drew Noakes 3 3 * 4 * This class based upon code from Jhead, a C program for extracting and5 * manipulating the Exif data within files written by Matthias Wandel.6 * http://www.sentex.net/~mwandel/jhead/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 7 * 8 * Jhead is public domain software - that is, you can do whatever you want 9 * with it, and include it software that is licensed under the GNU or the 10 * BSD license, or whatever other licence you choose, including proprietary 11 * closed source licenses. Similarly, I release this Java version under the 12 * same license, though I do ask that you leave this header in tact. 8 * http://www.apache.org/licenses/LICENSE-2.0 13 9 * 14 * If you make modifications to this code that you think would benefit the 15 * wider community, please send me a copy and I'll post it on my site. Unlike 16 * Jhead, this code (as it stands) only supports reading of Exif data - no 17 * manipulation, and no thumbnail stuff. 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. 18 15 * 19 * If you make use of this code, I'd appreciate hearing about it. 20 * drew.noakes@drewnoakes.com 21 * Latest version of this software kept at 22 * http://drewnoakes.com/ 16 * More information about this project is available at: 23 17 * 24 * Created on 28 April 2002, 23:54 25 * Modified 04 Aug 2002 26 * - Renamed constants to be inline with changes to ExifTagValues interface 27 * - Substituted usage of JDK 1.4 features (java.nio package) 28 * Modified 29 Oct 2002 (v1.2) 29 * - Proper traversing of Exif file structure and complete refactor & tidy of 30 * the codebase (a few unnoticed bugs removed) 31 * - Reads makernote data for 6 families of camera (5 makes) 32 * - Tags now stored in directories... use the IFD_* constants to refer to the 33 * image file directory you require (Exif, Interop, GPS and Makernote*) -- 34 * this avoids collisions where two tags share the same code 35 * - Takes componentCount of unknown tags into account 36 * - Now understands GPS tags (thanks to Colin Briton for his help with this) 37 * - Some other bug fixes, pointed out by users around the world. Thanks! 38 * Modified 27 Nov 2002 (v2.0) 39 * - Renamed to ExifReader 40 * - Moved to new package com.drew.metadata.exif 41 * Modified since, however changes have not been logged. See release notes for 42 * library-wide modifications. 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 43 20 */ 44 21 package com.drew.metadata.exif; 45 22 46 import com.drew.imaging.jpeg.JpegProcessingException; 47 import com.drew.imaging.jpeg.JpegSegmentData; 48 import com.drew.imaging.jpeg.JpegSegmentReader; 23 import com.drew.lang.BufferBoundsException; 24 import com.drew.lang.BufferReader; 49 25 import com.drew.lang.Rational; 26 import com.drew.lang.annotations.NotNull; 50 27 import com.drew.metadata.Directory; 51 28 import com.drew.metadata.Metadata; 52 29 import com.drew.metadata.MetadataReader; 53 30 54 import java.io.File; 55 import java.io.InputStream; 56 import java.util.HashMap; 31 import java.util.HashSet; 32 import java.util.Set; 57 33 58 34 /** 59 * Extracts Exif data from a JPEG header segment, providing information about the60 * camera/scanner/capture device (if available). Information is encapsulated in61 * an <code>Metadata</code> object.62 * @author 35 * Decodes Exif binary data, populating a {@link Metadata} object with tag values in {@link ExifSubIFDDirectory}, 36 * {@link ExifThumbnailDirectory}, {@link ExifInteropDirectory}, {@link GpsDirectory} and one of the many camera makernote directories. 37 * 38 * @author Drew Noakes http://drewnoakes.com 63 39 */ 64 40 public class ExifReader implements MetadataReader 65 41 { 66 /** 67 * The JPEG segment as an array of bytes. 68 */ 69 private final byte[] _data; 42 // TODO extract a reusable TiffReader from this class with hooks for special tag handling and subdir following 43 44 /** The number of bytes used per format descriptor. */ 45 @NotNull 46 private static final int[] BYTES_PER_FORMAT = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8 }; 70 47 71 /** 72 * Represents the native byte ordering used in the JPEG segment. If true, 73 * then we're using Motorolla ordering (Big endian), else we're using Intel 74 * ordering (Little endian). 75 */ 76 private boolean _isMotorollaByteOrder; 77 78 /** 79 * Bean instance to store information about the image and camera/scanner/capture 80 * device. 81 */ 82 private Metadata _metadata; 83 84 /** 85 * The number of bytes used per format descriptor. 86 */ 87 private static final int[] BYTES_PER_FORMAT = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8}; 88 89 /** 90 * The number of formats known. 91 */ 48 /** The number of formats known. */ 92 49 private static final int MAX_FORMAT_CODE = 12; 93 50 94 51 // Format types 95 // Note: Cannot use the DataFormat enumeration in the case statement that uses these tags.96 / / Is there a better way?52 // TODO use an enum for these? 53 /** An 8-bit unsigned integer. */ 97 54 private static final int FMT_BYTE = 1; 55 /** A fixed-length character string. */ 98 56 private static final int FMT_STRING = 2; 57 /** An unsigned 16-bit integer. */ 99 58 private static final int FMT_USHORT = 3; 59 /** An unsigned 32-bit integer. */ 100 60 private static final int FMT_ULONG = 4; 101 61 private static final int FMT_URATIONAL = 5; 62 /** An 8-bit signed integer. */ 102 63 private static final int FMT_SBYTE = 6; 103 64 private static final int FMT_UNDEFINED = 7; 65 /** A signed 16-bit integer. */ 104 66 private static final int FMT_SSHORT = 8; 67 /** A signed 32-bit integer. */ 105 68 private static final int FMT_SLONG = 9; 106 69 private static final int FMT_SRATIONAL = 10; 70 /** A 32-bit floating point number. */ 107 71 private static final int FMT_SINGLE = 11; 72 /** A 64-bit floating point number. */ 108 73 private static final int FMT_DOUBLE = 12; 109 74 110 public static final int TAG_EXIF_OFFSET = 0x8769; 75 /** This tag is a pointer to the Exif SubIFD. */ 76 public static final int TAG_EXIF_SUB_IFD_OFFSET = 0x8769; 77 /** This tag is a pointer to the Exif Interop IFD. */ 111 78 public static final int TAG_INTEROP_OFFSET = 0xA005; 79 /** This tag is a pointer to the Exif GPS IFD. */ 112 80 public static final int TAG_GPS_INFO_OFFSET = 0x8825; 113 public static final int TAG_MAKER_NOTE = 0x927C; 81 /** This tag is a pointer to the Exif Makernote IFD. */ 82 public static final int TAG_MAKER_NOTE_OFFSET = 0x927C; 114 83 115 84 public static final int TIFF_HEADER_START_OFFSET = 6; 116 85 117 86 /** 118 * Creates an ExifReader for a JpegSegmentData object. 119 * @param segmentData 87 * Performs the Exif data extraction, adding found values to the specified 88 * instance of <code>Metadata</code>. 89 * 90 * @param reader The buffer reader from which Exif data should be read. 91 * @param metadata The Metadata object into which extracted values should be merged. 120 92 */ 121 public ExifReader(JpegSegmentData segmentData)93 public void extract(@NotNull final BufferReader reader, @NotNull Metadata metadata) 122 94 { 123 this(segmentData.getSegment(JpegSegmentReader.SEGMENT_APP1)); 124 } 95 final ExifSubIFDDirectory directory = metadata.getOrCreateDirectory(ExifSubIFDDirectory.class); 125 96 126 /** 127 * Creates an ExifReader for a Jpeg file. 128 * @param file 129 * @throws JpegProcessingException 130 */ 131 public ExifReader(File file) throws JpegProcessingException 132 { 133 this(new JpegSegmentReader(file).readSegment(JpegSegmentReader.SEGMENT_APP1)); 134 } 97 // check for the header length 98 if (reader.getLength() <= 14) { 99 directory.addError("Exif data segment must contain at least 14 bytes"); 100 return; 101 } 135 102 136 /** 137 * Creates an ExifReader for a Jpeg stream. 138 * @param is JPEG stream. Stream will be closed. 139 */ 140 public ExifReader(InputStream is) throws JpegProcessingException 141 { 142 this(new JpegSegmentReader(is).readSegment(JpegSegmentReader.SEGMENT_APP1)); 143 } 103 // check for the header preamble 104 try { 105 if (!reader.getString(0, 6).equals("Exif\0\0")) { 106 directory.addError("Exif data segment doesn't begin with 'Exif'"); 107 return; 108 } 144 109 145 /** 146 * Creates an ExifReader for the given JPEG header segment. 147 */ 148 public ExifReader(byte[] data) 149 { 150 _data = data; 110 extractIFD(metadata, metadata.getOrCreateDirectory(ExifIFD0Directory.class), TIFF_HEADER_START_OFFSET, reader); 111 } catch (BufferBoundsException e) { 112 directory.addError("Exif data segment ended prematurely"); 113 } 151 114 } 152 115 153 116 /** 154 * Performs the Exif data extraction, returning a new instance of <code>Metadata</code>. 155 */ 156 public Metadata extract() 157 { 158 return extract(new Metadata()); 159 } 160 161 /** 162 * Performs the Exif data extraction, adding found values to the specified 117 * Performs the Exif data extraction on a TIFF/RAW, adding found values to the specified 163 118 * instance of <code>Metadata</code>. 119 * 120 * @param reader The BufferReader from which TIFF data should be read. 121 * @param metadata The Metadata object into which extracted values should be merged. 164 122 */ 165 public Metadata extract(Metadata metadata)123 public void extractTiff(@NotNull BufferReader reader, @NotNull Metadata metadata) 166 124 { 167 _metadata = metadata; 168 if (_data==null) 169 return _metadata; 125 final ExifIFD0Directory directory = metadata.getOrCreateDirectory(ExifIFD0Directory.class); 170 126 171 // once we know there's some data, create the directory and start working on it 172 ExifDirectory directory = (ExifDirectory)_metadata.getDirectory(ExifDirectory.class); 173 174 // check for the header length 175 if (_data.length<=14) { 176 directory.addError("Exif data segment must contain at least 14 bytes"); 177 return _metadata; 127 try { 128 extractIFD(metadata, directory, 0, reader); 129 } catch (BufferBoundsException e) { 130 directory.addError("Exif data segment ended prematurely"); 178 131 } 132 } 179 133 180 // check for the header preamble 181 if (!"Exif\0\0".equals(new String(_data, 0, 6))) { 182 directory.addError("Exif data segment doesn't begin with 'Exif'"); 183 return _metadata; 184 } 134 private void extractIFD(@NotNull Metadata metadata, @NotNull final ExifIFD0Directory directory, int tiffHeaderOffset, @NotNull BufferReader reader) throws BufferBoundsException 135 { 136 // this should be either "MM" or "II" 137 String byteOrderIdentifier = reader.getString(tiffHeaderOffset, 2); 185 138 186 // this should be either "MM" or "II" 187 String byteOrderIdentifier = new String(_data, 6, 2); 188 if (!setByteOrder(byteOrderIdentifier)) { 139 if ("MM".equals(byteOrderIdentifier)) { 140 reader.setMotorolaByteOrder(true); 141 } else if ("II".equals(byteOrderIdentifier)) { 142 reader.setMotorolaByteOrder(false); 143 } else { 189 144 directory.addError("Unclear distinction between Motorola/Intel byte ordering: " + byteOrderIdentifier); 190 return _metadata;145 return; 191 146 } 192 147 193 148 // Check the next two values for correctness. 194 if (get16Bits(8)!=0x2a) { 195 directory.addError("Invalid Exif start - should have 0x2A at offset 8 in Exif header"); 196 return _metadata; 149 final int tiffMarker = reader.getUInt16(2 + tiffHeaderOffset); 150 151 final int standardTiffMarker = 0x002A; 152 final int olympusRawTiffMarker = 0x4F52; // for ORF files 153 final int panasonicRawTiffMarker = 0x0055; // for RW2 files 154 155 if (tiffMarker != standardTiffMarker && tiffMarker != olympusRawTiffMarker && tiffMarker != panasonicRawTiffMarker) { 156 directory.addError("Unexpected TIFF marker after byte order identifier: 0x" + Integer.toHexString(tiffMarker)); 157 return; 197 158 } 198 159 199 int firstDirectoryOffset = get32Bits(10) + TIFF_HEADER_START_OFFSET;160 int firstDirectoryOffset = reader.getInt32(4 + tiffHeaderOffset) + tiffHeaderOffset; 200 161 201 // David Ekholm sent a ndigital camera image that has this problem202 if (firstDirectoryOffset >=_data.length- 1) {162 // David Ekholm sent a digital camera image that has this problem 163 if (firstDirectoryOffset >= reader.getLength() - 1) { 203 164 directory.addError("First exif directory offset is beyond end of Exif data segment"); 204 165 // First directory normally starts 14 bytes in -- try it here and catch another error in the worst case 205 166 firstDirectoryOffset = 14; 206 167 } 207 168 208 HashMap processedDirectoryOffsets = new HashMap();169 Set<Integer> processedDirectoryOffsets = new HashSet<Integer>(); 209 170 210 // 0th IFD (we merge with Exif IFD) 211 processDirectory(directory, processedDirectoryOffsets, firstDirectoryOffset, TIFF_HEADER_START_OFFSET); 171 processDirectory(directory, processedDirectoryOffsets, firstDirectoryOffset, tiffHeaderOffset, metadata, reader); 212 172 213 173 // after the extraction process, if we have the correct tags, we may be able to store thumbnail information 214 storeThumbnailBytes(directory, TIFF_HEADER_START_OFFSET); 215 216 return _metadata; 217 } 218 219 private void storeThumbnailBytes(ExifDirectory exifDirectory, int tiffHeaderOffset) 220 { 221 if (!exifDirectory.containsTag(ExifDirectory.TAG_COMPRESSION)) 222 return; 223 224 if (!exifDirectory.containsTag(ExifDirectory.TAG_THUMBNAIL_LENGTH) || 225 !exifDirectory.containsTag(ExifDirectory.TAG_THUMBNAIL_OFFSET)) 226 return; 227 228 try { 229 int offset = exifDirectory.getInt(ExifDirectory.TAG_THUMBNAIL_OFFSET); 230 int length = exifDirectory.getInt(ExifDirectory.TAG_THUMBNAIL_LENGTH); 231 byte[] result = new byte[length]; 232 for (int i = 0; i<result.length; i++) { 233 result[i] = _data[tiffHeaderOffset + offset + i]; 174 ExifThumbnailDirectory thumbnailDirectory = metadata.getDirectory(ExifThumbnailDirectory.class); 175 if (thumbnailDirectory!=null && thumbnailDirectory.containsTag(ExifThumbnailDirectory.TAG_THUMBNAIL_COMPRESSION)) { 176 Integer offset = thumbnailDirectory.getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_OFFSET); 177 Integer length = thumbnailDirectory.getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_LENGTH); 178 if (offset != null && length != null) { 179 try { 180 byte[] thumbnailData = reader.getBytes(tiffHeaderOffset + offset, length); 181 thumbnailDirectory.setThumbnailData(thumbnailData); 182 } catch (BufferBoundsException ex) { 183 directory.addError("Invalid thumbnail data specification: " + ex.getMessage()); 184 } 234 185 } 235 exifDirectory.setByteArray(ExifDirectory.TAG_THUMBNAIL_DATA, result);236 } catch (Throwable e) {237 exifDirectory.addError("Unable to extract thumbnail: " + e.getMessage());238 186 } 239 187 } 240 188 241 private boolean setByteOrder(String byteOrderIdentifier)242 {243 if ("MM".equals(byteOrderIdentifier)) {244 _isMotorollaByteOrder = true;245 } else if ("II".equals(byteOrderIdentifier)) {246 _isMotorollaByteOrder = false;247 } else {248 return false;249 }250 return true;251 }252 253 189 /** 254 190 * Process one of the nested Tiff IFD directories. 191 * <p/> 192 * Header 255 193 * 2 bytes: number of tags 256 * for each tag 257 * 2 bytes: tag type 258 * 2 bytes: format code 259 * 4 bytes: component count 194 * <p/> 195 * Then for each tag 196 * 2 bytes: tag type 197 * 2 bytes: format code 198 * 4 bytes: component count 260 199 */ 261 private void processDirectory( Directory directory, HashMap processedDirectoryOffsets, int dirStartOffset, int tiffHeaderOffset)200 private void processDirectory(@NotNull Directory directory, @NotNull Set<Integer> processedDirectoryOffsets, int dirStartOffset, int tiffHeaderOffset, @NotNull final Metadata metadata, @NotNull final BufferReader reader) throws BufferBoundsException 262 201 { 263 202 // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist 264 if (processedDirectoryOffsets.contains Key(new Integer(dirStartOffset)))203 if (processedDirectoryOffsets.contains(Integer.valueOf(dirStartOffset))) 265 204 return; 266 205 267 206 // remember that we've visited this directory so that we don't visit it again later 268 processedDirectoryOffsets. put(new Integer(dirStartOffset), "processed");207 processedDirectoryOffsets.add(dirStartOffset); 269 208 270 if (dirStartOffset >=_data.length || dirStartOffset<0) {271 directory.addError("Ignored directory marked to start outside data seg ement");209 if (dirStartOffset >= reader.getLength() || dirStartOffset < 0) { 210 directory.addError("Ignored directory marked to start outside data segment"); 272 211 return; 273 212 } 274 213 275 if (!isDirectoryLengthValid(dirStartOffset, tiffHeaderOffset)) { 214 // First two bytes in the IFD are the number of tags in this directory 215 int dirTagCount = reader.getUInt16(dirStartOffset); 216 217 int dirLength = (2 + (12 * dirTagCount) + 4); 218 if (dirLength + dirStartOffset > reader.getLength()) { 276 219 directory.addError("Illegally sized directory"); 277 220 return; 278 221 } 279 222 280 // First two bytes in the IFD are the number of tags in this directory281 int dirTagCount = get16Bits(dirStartOffset);282 283 223 // Handle each tag in this directory 284 for (int tagNumber = 0; tagNumber<dirTagCount; tagNumber++) 285 { 224 for (int tagNumber = 0; tagNumber < dirTagCount; tagNumber++) { 286 225 final int tagOffset = calculateTagOffset(dirStartOffset, tagNumber); 287 226 288 227 // 2 bytes for the tag type 289 final int tagType = get16Bits(tagOffset);228 final int tagType = reader.getUInt16(tagOffset); 290 229 291 230 // 2 bytes for the format code 292 final int formatCode = get16Bits(tagOffset + 2); 293 if (formatCode<1 || formatCode>MAX_FORMAT_CODE) { 294 directory.addError("Invalid format code: " + formatCode); 295 continue; 231 final int formatCode = reader.getUInt16(tagOffset + 2); 232 if (formatCode < 1 || formatCode > MAX_FORMAT_CODE) { 233 // This error suggests that we are processing at an incorrect index and will generate 234 // rubbish until we go out of bounds (which may be a while). Exit now. 235 directory.addError("Invalid TIFF tag format code: " + formatCode); 236 return; 296 237 } 297 238 298 239 // 4 bytes dictate the number of components in this tag's data 299 final int componentCount = get32Bits(tagOffset + 4);300 if (componentCount <0) {301 directory.addError("Negative component count in EXIF");240 final int componentCount = reader.getInt32(tagOffset + 4); 241 if (componentCount < 0) { 242 directory.addError("Negative TIFF tag component count"); 302 243 continue; 303 244 } 304 245 // each component may have more than one byte... calculate the total number of bytes 305 246 final int byteCount = componentCount * BYTES_PER_FORMAT[formatCode]; 306 final int tagValueOffset = calculateTagValueOffset(byteCount, tagOffset, tiffHeaderOffset); 307 if (tagValueOffset<0 || tagValueOffset > _data.length) { 308 directory.addError("Illegal pointer offset value in EXIF"); 247 final int tagValueOffset; 248 if (byteCount > 4) { 249 // If it's bigger than 4 bytes, the dir entry contains an offset. 250 // dirEntryOffset must be passed, as some makernote implementations (e.g. FujiFilm) incorrectly use an 251 // offset relative to the start of the makernote itself, not the TIFF segment. 252 final int offsetVal = reader.getInt32(tagOffset + 8); 253 if (offsetVal + byteCount > reader.getLength()) { 254 // Bogus pointer offset and / or byteCount value 255 directory.addError("Illegal TIFF tag pointer offset"); 256 continue; 257 } 258 tagValueOffset = tiffHeaderOffset + offsetVal; 259 } else { 260 // 4 bytes or less and value is in the dir entry itself 261 tagValueOffset = tagOffset + 8; 262 } 263 264 if (tagValueOffset < 0 || tagValueOffset > reader.getLength()) { 265 directory.addError("Illegal TIFF tag pointer offset"); 309 266 continue; 310 267 } 311 268 312 269 // Check that this tag isn't going to allocate outside the bounds of the data array. 313 270 // This addresses an uncommon OutOfMemoryError. 314 if (byteCount < 0 || tagValueOffset + byteCount > _data.length) 315 { 271 if (byteCount < 0 || tagValueOffset + byteCount > reader.getLength()) { 316 272 directory.addError("Illegal number of bytes: " + byteCount); 317 273 continue; 318 274 } 319 275 320 // Calculate the value as an offset for cases where the tag represents directory321 final int subdirOffset = tiffHeaderOffset + get32Bits(tagValueOffset);322 323 276 switch (tagType) { 324 case TAG_EXIF_OFFSET: 325 processDirectory(_metadata.getDirectory(ExifDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset); 277 case TAG_EXIF_SUB_IFD_OFFSET: { 278 final int subdirOffset = tiffHeaderOffset + reader.getInt32(tagValueOffset); 279 processDirectory(metadata.getOrCreateDirectory(ExifSubIFDDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader); 326 280 continue; 327 case TAG_INTEROP_OFFSET: 328 processDirectory(_metadata.getDirectory(ExifInteropDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset); 281 } 282 case TAG_INTEROP_OFFSET: { 283 final int subdirOffset = tiffHeaderOffset + reader.getInt32(tagValueOffset); 284 processDirectory(metadata.getOrCreateDirectory(ExifInteropDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader); 329 285 continue; 330 case TAG_GPS_INFO_OFFSET: 331 processDirectory(_metadata.getDirectory(GpsDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset); 286 } 287 case TAG_GPS_INFO_OFFSET: { 288 final int subdirOffset = tiffHeaderOffset + reader.getInt32(tagValueOffset); 289 processDirectory(metadata.getOrCreateDirectory(GpsDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader); 332 290 continue; 333 case TAG_MAKER_NOTE: 334 processMakerNote(tagValueOffset, processedDirectoryOffsets, tiffHeaderOffset); 291 } 292 case TAG_MAKER_NOTE_OFFSET: { 293 processMakerNote(tagValueOffset, processedDirectoryOffsets, tiffHeaderOffset, metadata, reader); 335 294 continue; 336 default: 337 processTag(directory, tagType, tagValueOffset, componentCount, formatCode); 295 } 296 default: { 297 processTag(directory, tagType, tagValueOffset, componentCount, formatCode, reader); 338 298 break; 299 } 339 300 } 340 301 } 341 302 342 303 // at the end of each IFD is an optional link to the next IFD 343 304 final int finalTagOffset = calculateTagOffset(dirStartOffset, dirTagCount); 344 int nextDirectoryOffset = get32Bits(finalTagOffset);345 if (nextDirectoryOffset !=0) {305 int nextDirectoryOffset = reader.getInt32(finalTagOffset); 306 if (nextDirectoryOffset != 0) { 346 307 nextDirectoryOffset += tiffHeaderOffset; 347 if (nextDirectoryOffset >=_data.length) {308 if (nextDirectoryOffset >= reader.getLength()) { 348 309 // Last 4 bytes of IFD reference another IFD with an address that is out of bounds 349 310 // Note this could have been caused by jhead 1.3 cropping too much 350 311 return; … … 352 313 // Last 4 bytes of IFD reference another IFD with an address that is before the start of this directory 353 314 return; 354 315 } 355 // the next directory is of same type as this one 356 processDirectory(directory, processedDirectoryOffsets, nextDirectoryOffset, tiffHeaderOffset); 316 // TODO in Exif, the only known 'follower' IFD is the thumbnail one, however this may not be the case 317 final ExifThumbnailDirectory nextDirectory = metadata.getOrCreateDirectory(ExifThumbnailDirectory.class); 318 processDirectory(nextDirectory, processedDirectoryOffsets, nextDirectoryOffset, tiffHeaderOffset, metadata, reader); 357 319 } 358 320 } 359 321 360 private void processMakerNote(int subdirOffset, HashMap processedDirectoryOffsets, int tiffHeaderOffset)322 private void processMakerNote(int subdirOffset, @NotNull Set<Integer> processedDirectoryOffsets, int tiffHeaderOffset, @NotNull final Metadata metadata, @NotNull BufferReader reader) throws BufferBoundsException 361 323 { 362 324 // Determine the camera model and makernote format 363 Directory exifDirectory = _metadata.getDirectory(ExifDirectory.class);325 Directory ifd0Directory = metadata.getDirectory(ExifIFD0Directory.class); 364 326 365 if ( exifDirectory==null)327 if (ifd0Directory==null) 366 328 return; 367 329 368 String cameraModel = exifDirectory.getString(ExifDirectory.TAG_MAKE); 369 final String firstTwoChars = new String(_data, subdirOffset, 2); 370 final String firstThreeChars = new String(_data, subdirOffset, 3); 371 final String firstFourChars = new String(_data, subdirOffset, 4); 372 final String firstFiveChars = new String(_data, subdirOffset, 5); 373 final String firstSixChars = new String(_data, subdirOffset, 6); 374 final String firstSevenChars = new String(_data, subdirOffset, 7); 375 final String firstEightChars = new String(_data, subdirOffset, 8); 376 if ("OLYMP".equals(firstFiveChars) || "EPSON".equals(firstFiveChars) || "AGFA".equals(firstFourChars)) 377 { 330 String cameraModel = ifd0Directory.getString(ExifIFD0Directory.TAG_MAKE); 331 332 //final String firstTwoChars = reader.getString(subdirOffset, 2); 333 final String firstThreeChars = reader.getString(subdirOffset, 3); 334 final String firstFourChars = reader.getString(subdirOffset, 4); 335 final String firstFiveChars = reader.getString(subdirOffset, 5); 336 final String firstSixChars = reader.getString(subdirOffset, 6); 337 final String firstSevenChars = reader.getString(subdirOffset, 7); 338 final String firstEightChars = reader.getString(subdirOffset, 8); 339 final String firstTwelveChars = reader.getString(subdirOffset, 12); 340 341 if ("OLYMP".equals(firstFiveChars) || "EPSON".equals(firstFiveChars) || "AGFA".equals(firstFourChars)) { 378 342 // Olympus Makernote 379 // Epson and Agfa use Olypus maker note standard, see: 380 // http://www.ozhiker.com/electronics/pjmt/jpeg_info/ 381 processDirectory(_metadata.getDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset); 382 } 383 else if (cameraModel!=null && cameraModel.trim().toUpperCase().startsWith("NIKON")) 384 { 385 if ("Nikon".equals(firstFiveChars)) 386 { 343 // Epson and Agfa use Olympus maker note standard: http://www.ozhiker.com/electronics/pjmt/jpeg_info/ 344 processDirectory(metadata.getOrCreateDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset, metadata, reader); 345 } else if (cameraModel != null && cameraModel.trim().toUpperCase().startsWith("NIKON")) { 346 if ("Nikon".equals(firstFiveChars)) { 387 347 /* There are two scenarios here: 388 348 * Type 1: ** 389 349 * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon........... … … 392 352 * :0000: 4E 69 6B 6F 6E 00 02 00-00 00 4D 4D 00 2A 00 00 Nikon....MM.*... 393 353 * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200 394 354 */ 395 if (_data[subdirOffset+6]==1) 396 processDirectory(_metadata.getDirectory(NikonType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset); 397 else if (_data[subdirOffset+6]==2) 398 processDirectory(_metadata.getDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 18, subdirOffset + 10); 399 else 400 exifDirectory.addError("Unsupported makernote data ignored."); 401 } 402 else 403 { 355 switch (reader.getUInt8(subdirOffset + 6)) { 356 case 1: 357 processDirectory(metadata.getOrCreateDirectory(NikonType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset, metadata, reader); 358 break; 359 case 2: 360 processDirectory(metadata.getOrCreateDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 18, subdirOffset + 10, metadata, reader); 361 break; 362 default: 363 ifd0Directory.addError("Unsupported Nikon makernote data ignored."); 364 break; 365 } 366 } else { 404 367 // The IFD begins with the first MakerNote byte (no ASCII name). This occurs with CoolPix 775, E990 and D1 models. 405 processDirectory( _metadata.getDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);368 processDirectory(metadata.getOrCreateDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader); 406 369 } 407 } 408 else if ("SONY CAM".equals(firstEightChars) || "SONY DSC".equals(firstEightChars)) 409 { 410 processDirectory(_metadata.getDirectory(SonyMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset); 411 } 412 else if ("KDK".equals(firstThreeChars)) 413 { 414 processDirectory(_metadata.getDirectory(KodakMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 20, tiffHeaderOffset); 415 } 416 else if ("Canon".equalsIgnoreCase(cameraModel)) 417 { 418 processDirectory(_metadata.getDirectory(CanonMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset); 419 } 420 else if (cameraModel!=null && cameraModel.toUpperCase().startsWith("CASIO")) 421 { 370 } else if ("SONY CAM".equals(firstEightChars) || "SONY DSC".equals(firstEightChars)) { 371 processDirectory(metadata.getOrCreateDirectory(SonyType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset, metadata, reader); 372 } else if ("SIGMA\u0000\u0000\u0000".equals(firstEightChars) || "FOVEON\u0000\u0000".equals(firstEightChars)) { 373 processDirectory(metadata.getOrCreateDirectory(SigmaMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 10, tiffHeaderOffset, metadata, reader); 374 } else if ("SEMC MS\u0000\u0000\u0000\u0000\u0000".equals(firstTwelveChars)) { 375 // force MM for this directory 376 boolean isMotorola = reader.isMotorolaByteOrder(); 377 reader.setMotorolaByteOrder(true); 378 // skip 12 byte header + 2 for "MM" + 6 379 processDirectory(metadata.getOrCreateDirectory(SonyType6MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 20, tiffHeaderOffset, metadata, reader); 380 reader.setMotorolaByteOrder(isMotorola); 381 } else if ("KDK".equals(firstThreeChars)) { 382 processDirectory(metadata.getOrCreateDirectory(KodakMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 20, tiffHeaderOffset, metadata, reader); 383 } else if ("Canon".equalsIgnoreCase(cameraModel)) { 384 processDirectory(metadata.getOrCreateDirectory(CanonMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader); 385 } else if (cameraModel != null && cameraModel.toUpperCase().startsWith("CASIO")) { 422 386 if ("QVC\u0000\u0000\u0000".equals(firstSixChars)) 423 processDirectory( _metadata.getDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, tiffHeaderOffset);387 processDirectory(metadata.getOrCreateDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, tiffHeaderOffset, metadata, reader); 424 388 else 425 processDirectory(_metadata.getDirectory(CasioType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset); 426 } 427 else if ("FUJIFILM".equals(firstEightChars) || "Fujifilm".equalsIgnoreCase(cameraModel)) 428 { 429 // TODO make this field a passed parameter, to avoid threading issues 430 boolean byteOrderBefore = _isMotorollaByteOrder; 389 processDirectory(metadata.getOrCreateDirectory(CasioType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader); 390 } else if ("FUJIFILM".equals(firstEightChars) || "Fujifilm".equalsIgnoreCase(cameraModel)) { 391 boolean byteOrderBefore = reader.isMotorolaByteOrder(); 431 392 // bug in fujifilm makernote ifd means we temporarily use Intel byte ordering 432 _isMotorollaByteOrder = false;393 reader.setMotorolaByteOrder(false); 433 394 // the 4 bytes after "FUJIFILM" in the makernote point to the start of the makernote 434 395 // IFD, though the offset is relative to the start of the makernote, not the TIFF 435 396 // header (like everywhere else) 436 int ifdStart = subdirOffset + get32Bits(subdirOffset + 8); 437 processDirectory(_metadata.getDirectory(FujifilmMakernoteDirectory.class), processedDirectoryOffsets, ifdStart, tiffHeaderOffset); 438 _isMotorollaByteOrder = byteOrderBefore; 439 } 440 else if (cameraModel!=null && cameraModel.toUpperCase().startsWith("MINOLTA")) 441 { 397 int ifdStart = subdirOffset + reader.getInt32(subdirOffset + 8); 398 processDirectory(metadata.getOrCreateDirectory(FujifilmMakernoteDirectory.class), processedDirectoryOffsets, ifdStart, tiffHeaderOffset, metadata, reader); 399 reader.setMotorolaByteOrder(byteOrderBefore); 400 } else if (cameraModel != null && cameraModel.toUpperCase().startsWith("MINOLTA")) { 442 401 // Cases seen with the model starting with MINOLTA in capitals seem to have a valid Olympus makernote 443 402 // area that commences immediately. 444 processDirectory(_metadata.getDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset); 445 } 446 else if ("KC".equals(firstTwoChars) || "MINOL".equals(firstFiveChars) || "MLY".equals(firstThreeChars) || "+M+M+M+M".equals(firstEightChars)) 447 { 448 // This Konica data is not understood. Header identified in accordance with information at this site: 449 // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html 450 // TODO determine how to process the information described at the above website 451 exifDirectory.addError("Unsupported Konica/Minolta data ignored."); 452 } 453 else if ("KYOCERA".equals(firstSevenChars)) 454 { 403 processDirectory(metadata.getOrCreateDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader); 404 } else if ("KYOCERA".equals(firstSevenChars)) { 455 405 // http://www.ozhiker.com/electronics/pjmt/jpeg_info/kyocera_mn.html 456 processDirectory(_metadata.getDirectory(KyoceraMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 22, tiffHeaderOffset); 457 } 458 else if ("Panasonic\u0000\u0000\u0000".equals(new String(_data, subdirOffset, 12))) 459 { 406 processDirectory(metadata.getOrCreateDirectory(KyoceraMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 22, tiffHeaderOffset, metadata, reader); 407 } else if ("Panasonic\u0000\u0000\u0000".equals(reader.getString(subdirOffset, 12))) { 460 408 // NON-Standard TIFF IFD Data using Panasonic Tags. There is no Next-IFD pointer after the IFD 461 409 // Offsets are relative to the start of the TIFF header at the beginning of the EXIF segment 462 410 // more information here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html 463 processDirectory(_metadata.getDirectory(PanasonicMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset); 464 } 465 else if ("AOC\u0000".equals(firstFourChars)) 466 { 411 processDirectory(metadata.getOrCreateDirectory(PanasonicMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset, metadata, reader); 412 } else if ("AOC\u0000".equals(firstFourChars)) { 467 413 // NON-Standard TIFF IFD Data using Casio Type 2 Tags 468 414 // IFD has no Next-IFD pointer at end of IFD, and 469 415 // Offsets are relative to the start of the current IFD tag, not the TIFF header 470 416 // Observed for: 471 417 // - Pentax ist D 472 processDirectory(_metadata.getDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, subdirOffset); 473 } 474 else if (cameraModel!=null && (cameraModel.toUpperCase().startsWith("PENTAX") || cameraModel.toUpperCase().startsWith("ASAHI"))) 475 { 418 processDirectory(metadata.getOrCreateDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, subdirOffset, metadata, reader); 419 } else if (cameraModel != null && (cameraModel.toUpperCase().startsWith("PENTAX") || cameraModel.toUpperCase().startsWith("ASAHI"))) { 476 420 // NON-Standard TIFF IFD Data using Pentax Tags 477 421 // IFD has no Next-IFD pointer at end of IFD, and 478 422 // Offsets are relative to the start of the current IFD tag, not the TIFF header 479 423 // Observed for: 480 424 // - PENTAX Optio 330 481 425 // - PENTAX Optio 430 482 processDirectory(_metadata.getDirectory(PentaxMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, subdirOffset); 483 } 484 else 485 { 426 processDirectory(metadata.getOrCreateDirectory(PentaxMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, subdirOffset, metadata, reader); 427 // } else if ("KC".equals(firstTwoChars) || "MINOL".equals(firstFiveChars) || "MLY".equals(firstThreeChars) || "+M+M+M+M".equals(firstEightChars)) { 428 // // This Konica data is not understood. Header identified in accordance with information at this site: 429 // // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html 430 // // TODO add support for minolta/konica cameras 431 // exifDirectory.addError("Unsupported Konica/Minolta data ignored."); 432 } else { 486 433 // TODO how to store makernote data when it's not from a supported camera model? 487 434 // this is difficult as the starting offset is not known. we could look for it... 488 exifDirectory.addError("Unsupported makernote data ignored.");489 435 } 490 436 } 491 437 492 private boolean isDirectoryLengthValid(int dirStartOffset, int tiffHeaderOffset)438 private void processTag(@NotNull Directory directory, int tagType, int tagValueOffset, int componentCount, int formatCode, @NotNull final BufferReader reader) throws BufferBoundsException 493 439 { 494 int dirTagCount = get16Bits(dirStartOffset);495 int dirLength = (2 + (12 * dirTagCount) + 4);496 if (dirLength + dirStartOffset + tiffHeaderOffset>=_data.length) {497 // Note: Files that had thumbnails trimmed with jhead 1.3 or earlier might trigger this498 return false;499 }500 return true;501 }502 503 private void processTag(Directory directory, int tagType, int tagValueOffset, int componentCount, int formatCode)504 {505 440 // Directory simply stores raw values 506 441 // The display side uses a Descriptor class per directory to turn the raw values into 'pretty' descriptions 507 switch (formatCode) 508 { 442 switch (formatCode) { 509 443 case FMT_UNDEFINED: 510 444 // this includes exif user comments 511 final byte[] tagBytes = new byte[componentCount]; 512 final int byteCount = componentCount * BYTES_PER_FORMAT[formatCode]; 513 for (int i=0; i<byteCount; i++) 514 tagBytes[i] = _data[tagValueOffset + i]; 515 directory.setByteArray(tagType, tagBytes); 445 directory.setByteArray(tagType, reader.getBytes(tagValueOffset, componentCount)); 516 446 break; 517 447 case FMT_STRING: 518 directory.setString(tagType, readString(tagValueOffset, componentCount)); 448 String string = reader.getNullTerminatedString(tagValueOffset, componentCount); 449 directory.setString(tagType, string); 450 /* 451 // special handling for certain known tags, proposed by Yuri Binev but left out for now, 452 // as it gives the false impression that the image was captured in the same timezone 453 // in which the string is parsed 454 if (tagType==ExifSubIFDDirectory.TAG_DATETIME || 455 tagType==ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL || 456 tagType==ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED) { 457 String[] datePatterns = { 458 "yyyy:MM:dd HH:mm:ss", 459 "yyyy:MM:dd HH:mm", 460 "yyyy-MM-dd HH:mm:ss", 461 "yyyy-MM-dd HH:mm"}; 462 for (String datePattern : datePatterns) { 463 try { 464 DateFormat parser = new SimpleDateFormat(datePattern); 465 Date date = parser.parse(string); 466 directory.setDate(tagType, date); 467 break; 468 } catch (ParseException ex) { 469 // simply try the next pattern 470 } 471 } 472 } 473 */ 519 474 break; 520 475 case FMT_SRATIONAL: 476 if (componentCount == 1) { 477 directory.setRational(tagType, new Rational(reader.getInt32(tagValueOffset), reader.getInt32(tagValueOffset + 4))); 478 } else if (componentCount > 1) { 479 Rational[] rationals = new Rational[componentCount]; 480 for (int i = 0; i < componentCount; i++) 481 rationals[i] = new Rational(reader.getInt32(tagValueOffset + (8 * i)), reader.getInt32(tagValueOffset + 4 + (8 * i))); 482 directory.setRationalArray(tagType, rationals); 483 } 484 break; 521 485 case FMT_URATIONAL: 522 if (componentCount==1) { 523 Rational rational = new Rational(get32Bits(tagValueOffset), get32Bits(tagValueOffset + 4)); 524 directory.setRational(tagType, rational); 525 } else { 486 if (componentCount == 1) { 487 directory.setRational(tagType, new Rational(reader.getUInt32(tagValueOffset), reader.getUInt32(tagValueOffset + 4))); 488 } else if (componentCount > 1) { 526 489 Rational[] rationals = new Rational[componentCount]; 527 for (int i = 0; i <componentCount; i++)528 rationals[i] = new Rational( get32Bits(tagValueOffset + (8 * i)), get32Bits(tagValueOffset + 4 + (8 * i)));490 for (int i = 0; i < componentCount; i++) 491 rationals[i] = new Rational(reader.getUInt32(tagValueOffset + (8 * i)), reader.getUInt32(tagValueOffset + 4 + (8 * i))); 529 492 directory.setRationalArray(tagType, rationals); 530 493 } 531 494 break; 495 case FMT_SINGLE: 496 if (componentCount == 1) { 497 directory.setFloat(tagType, reader.getFloat32(tagValueOffset)); 498 } else { 499 float[] floats = new float[componentCount]; 500 for (int i = 0; i < componentCount; i++) 501 floats[i] = reader.getFloat32(tagValueOffset + (i * 4)); 502 directory.setFloatArray(tagType, floats); 503 } 504 break; 505 case FMT_DOUBLE: 506 if (componentCount == 1) { 507 directory.setDouble(tagType, reader.getDouble64(tagValueOffset)); 508 } else { 509 double[] doubles = new double[componentCount]; 510 for (int i = 0; i < componentCount; i++) 511 doubles[i] = reader.getDouble64(tagValueOffset + (i * 4)); 512 directory.setDoubleArray(tagType, doubles); 513 } 514 break; 515 516 // 517 // Note that all integral types are stored as int32 internally (the largest supported by TIFF) 518 // 519 532 520 case FMT_SBYTE: 521 if (componentCount == 1) { 522 directory.setInt(tagType, reader.getInt8(tagValueOffset)); 523 } else { 524 int[] bytes = new int[componentCount]; 525 for (int i = 0; i < componentCount; i++) 526 bytes[i] = reader.getInt8(tagValueOffset + i); 527 directory.setIntArray(tagType, bytes); 528 } 529 break; 533 530 case FMT_BYTE: 534 if (componentCount==1) { 535 // this may need to be a byte, but I think casting to int is fine 536 int b = _data[tagValueOffset]; 537 directory.setInt(tagType, b); 531 if (componentCount == 1) { 532 directory.setInt(tagType, reader.getUInt8(tagValueOffset)); 538 533 } else { 539 534 int[] bytes = new int[componentCount]; 540 for (int i = 0; i <componentCount; i++)541 bytes[i] = _data[tagValueOffset + i];535 for (int i = 0; i < componentCount; i++) 536 bytes[i] = reader.getUInt8(tagValueOffset + i); 542 537 directory.setIntArray(tagType, bytes); 543 538 } 544 539 break; 545 case FMT_SINGLE: 546 case FMT_DOUBLE: 547 if (componentCount==1) { 548 int i = _data[tagValueOffset]; 540 case FMT_USHORT: 541 if (componentCount == 1) { 542 int i = reader.getUInt16(tagValueOffset); 549 543 directory.setInt(tagType, i); 550 544 } else { 551 545 int[] ints = new int[componentCount]; 552 for (int i = 0; i <componentCount; i++)553 ints[i] = _data[tagValueOffset + i];546 for (int i = 0; i < componentCount; i++) 547 ints[i] = reader.getUInt16(tagValueOffset + (i * 2)); 554 548 directory.setIntArray(tagType, ints); 555 549 } 556 550 break; 557 case FMT_USHORT:558 551 case FMT_SSHORT: 559 if (componentCount ==1) {560 int i = get16Bits(tagValueOffset);552 if (componentCount == 1) { 553 int i = reader.getInt16(tagValueOffset); 561 554 directory.setInt(tagType, i); 562 555 } else { 563 556 int[] ints = new int[componentCount]; 564 for (int i = 0; i <componentCount; i++)565 ints[i] = get16Bits(tagValueOffset + (i * 2));557 for (int i = 0; i < componentCount; i++) 558 ints[i] = reader.getInt16(tagValueOffset + (i * 2)); 566 559 directory.setIntArray(tagType, ints); 567 560 } 568 561 break; 569 562 case FMT_SLONG: 570 563 case FMT_ULONG: 571 if (componentCount==1) { 572 int i = get32Bits(tagValueOffset); 564 // NOTE 'long' in this case means 32 bit, not 64 565 if (componentCount == 1) { 566 int i = reader.getInt32(tagValueOffset); 573 567 directory.setInt(tagType, i); 574 568 } else { 575 569 int[] ints = new int[componentCount]; 576 for (int i = 0; i <componentCount; i++)577 ints[i] = get32Bits(tagValueOffset + (i * 4));570 for (int i = 0; i < componentCount; i++) 571 ints[i] = reader.getInt32(tagValueOffset + (i * 4)); 578 572 directory.setIntArray(tagType, ints); 579 573 } 580 574 break; … … 583 577 } 584 578 } 585 579 586 private int calculateTagValueOffset(int byteCount, int dirEntryOffset, int tiffHeaderOffset)587 {588 if (byteCount>4) {589 // If its bigger than 4 bytes, the dir entry contains an offset.590 // dirEntryOffset must be passed, as some makernote implementations (e.g. FujiFilm) incorrectly use an591 // offset relative to the start of the makernote itself, not the TIFF segment.592 final int offsetVal = get32Bits(dirEntryOffset + 8);593 if (offsetVal + byteCount>_data.length) {594 // Bogus pointer offset and / or bytecount value595 return -1; // signal error596 }597 return tiffHeaderOffset + offsetVal;598 } else {599 // 4 bytes or less and value is in the dir entry itself600 return dirEntryOffset + 8;601 }602 }603 604 580 /** 605 * Creates a String from the _data buffer starting at the specified offset,606 * and ending where byte=='\0' or where length==maxLength.607 */608 private String readString(int offset, int maxLength)609 {610 int length = 0;611 while ((offset + length)<_data.length && _data[offset + length]!='\0' && length<maxLength)612 length++;613 614 return new String(_data, offset, length);615 }616 617 /**618 581 * Determine the offset at which a given InteropArray entry begins within the specified IFD. 582 * 619 583 * @param dirStartOffset the offset at which the IFD starts 620 * @param entryNumber the zero-based entry number584 * @param entryNumber the zero-based entry number 621 585 */ 622 586 private int calculateTagOffset(int dirStartOffset, int entryNumber) 623 587 { … … 625 589 // each entry is 12 bytes, so we skip 12 * the number seen so far 626 590 return dirStartOffset + 2 + (12 * entryNumber); 627 591 } 628 629 /**630 * Get a 16 bit value from file's native byte order. Between 0x0000 and 0xFFFF.631 */632 private int get16Bits(int offset)633 {634 if (offset<0 || offset+2>_data.length)635 throw new ArrayIndexOutOfBoundsException("attempt to read data outside of exif segment (index " + offset + " where max index is " + (_data.length - 1) + ")");636 637 if (_isMotorollaByteOrder) {638 // Motorola - MSB first639 return (_data[offset] << 8 & 0xFF00) | (_data[offset + 1] & 0xFF);640 } else {641 // Intel ordering - LSB first642 return (_data[offset + 1] << 8 & 0xFF00) | (_data[offset] & 0xFF);643 }644 }645 646 /**647 * Get a 32 bit value from file's native byte order.648 */649 private int get32Bits(int offset)650 {651 if (offset<0 || offset+4>_data.length)652 throw new ArrayIndexOutOfBoundsException("attempt to read data outside of exif segment (index " + offset + " where max index is " + (_data.length - 1) + ")");653 654 if (_isMotorollaByteOrder) {655 // Motorola - MSB first656 return (_data[offset] << 24 & 0xFF000000) |657 (_data[offset + 1] << 16 & 0xFF0000) |658 (_data[offset + 2] << 8 & 0xFF00) |659 (_data[offset + 3] & 0xFF);660 } else {661 // Intel ordering - LSB first662 return (_data[offset + 3] << 24 & 0xFF000000) |663 (_data[offset + 2] << 16 & 0xFF0000) |664 (_data[offset + 1] << 8 & 0xFF00) |665 (_data[offset] & 0xFF);666 }667 }668 592 } -
src/com/drew/metadata/exif/ExifSubIFDDescriptor.java
1 /* 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/ 20 */ 21 package com.drew.metadata.exif; 22 23 import com.drew.imaging.PhotographicConversions; 24 import com.drew.lang.Rational; 25 import com.drew.lang.annotations.NotNull; 26 import com.drew.lang.annotations.Nullable; 27 import com.drew.metadata.TagDescriptor; 28 29 import java.io.UnsupportedEncodingException; 30 import java.text.DecimalFormat; 31 import java.util.HashMap; 32 import java.util.Map; 33 34 /** 35 * Provides human-readable string representations of tag values stored in a <code>ExifSubIFDDirectory</code>. 36 * 37 * @author Drew Noakes http://drewnoakes.com 38 */ 39 public class ExifSubIFDDescriptor extends TagDescriptor<ExifSubIFDDirectory> 40 { 41 /** 42 * Dictates whether rational values will be represented in decimal format in instances 43 * where decimal notation is elegant (such as 1/2 -> 0.5, but not 1/3). 44 */ 45 private final boolean _allowDecimalRepresentationOfRationals = true; 46 47 @NotNull 48 private static final java.text.DecimalFormat SimpleDecimalFormatter = new DecimalFormat("0.#"); 49 50 public ExifSubIFDDescriptor(@NotNull ExifSubIFDDirectory directory) 51 { 52 super(directory); 53 } 54 55 // Note for the potential addition of brightness presentation in eV: 56 // Brightness of taken subject. To calculate Exposure(Ev) from BrightnessValue(Bv), 57 // you must add SensitivityValue(Sv). 58 // Ev=BV+Sv Sv=log2(ISOSpeedRating/3.125) 59 // ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32. 60 61 /** 62 * Returns a descriptive value of the the specified tag for this image. 63 * Where possible, known values will be substituted here in place of the raw 64 * tokens actually kept in the Exif segment. If no substitution is 65 * available, the value provided by getString(int) will be returned. 66 * @param tagType the tag to find a description for 67 * @return a description of the image's value for the specified tag, or 68 * <code>null</code> if the tag hasn't been defined. 69 */ 70 @Nullable 71 public String getDescription(int tagType) 72 { 73 switch (tagType) { 74 case ExifSubIFDDirectory.TAG_NEW_SUBFILE_TYPE: 75 return getNewSubfileTypeDescription(); 76 case ExifSubIFDDirectory.TAG_SUBFILE_TYPE: 77 return getSubfileTypeDescription(); 78 case ExifSubIFDDirectory.TAG_THRESHOLDING: 79 return getThresholdingDescription(); 80 case ExifSubIFDDirectory.TAG_FILL_ORDER: 81 return getFillOrderDescription(); 82 case ExifSubIFDDirectory.TAG_EXPOSURE_TIME: 83 return getExposureTimeDescription(); 84 case ExifSubIFDDirectory.TAG_SHUTTER_SPEED: 85 return getShutterSpeedDescription(); 86 case ExifSubIFDDirectory.TAG_FNUMBER: 87 return getFNumberDescription(); 88 case ExifSubIFDDirectory.TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL: 89 return getCompressedAverageBitsPerPixelDescription(); 90 case ExifSubIFDDirectory.TAG_SUBJECT_DISTANCE: 91 return getSubjectDistanceDescription(); 92 case ExifSubIFDDirectory.TAG_METERING_MODE: 93 return getMeteringModeDescription(); 94 case ExifSubIFDDirectory.TAG_WHITE_BALANCE: 95 return getWhiteBalanceDescription(); 96 case ExifSubIFDDirectory.TAG_FLASH: 97 return getFlashDescription(); 98 case ExifSubIFDDirectory.TAG_FOCAL_LENGTH: 99 return getFocalLengthDescription(); 100 case ExifSubIFDDirectory.TAG_COLOR_SPACE: 101 return getColorSpaceDescription(); 102 case ExifSubIFDDirectory.TAG_EXIF_IMAGE_WIDTH: 103 return getExifImageWidthDescription(); 104 case ExifSubIFDDirectory.TAG_EXIF_IMAGE_HEIGHT: 105 return getExifImageHeightDescription(); 106 case ExifSubIFDDirectory.TAG_FOCAL_PLANE_UNIT: 107 return getFocalPlaneResolutionUnitDescription(); 108 case ExifSubIFDDirectory.TAG_FOCAL_PLANE_X_RES: 109 return getFocalPlaneXResolutionDescription(); 110 case ExifSubIFDDirectory.TAG_FOCAL_PLANE_Y_RES: 111 return getFocalPlaneYResolutionDescription(); 112 case ExifSubIFDDirectory.TAG_BITS_PER_SAMPLE: 113 return getBitsPerSampleDescription(); 114 case ExifSubIFDDirectory.TAG_PHOTOMETRIC_INTERPRETATION: 115 return getPhotometricInterpretationDescription(); 116 case ExifSubIFDDirectory.TAG_ROWS_PER_STRIP: 117 return getRowsPerStripDescription(); 118 case ExifSubIFDDirectory.TAG_STRIP_BYTE_COUNTS: 119 return getStripByteCountsDescription(); 120 case ExifSubIFDDirectory.TAG_SAMPLES_PER_PIXEL: 121 return getSamplesPerPixelDescription(); 122 case ExifSubIFDDirectory.TAG_PLANAR_CONFIGURATION: 123 return getPlanarConfigurationDescription(); 124 case ExifSubIFDDirectory.TAG_YCBCR_SUBSAMPLING: 125 return getYCbCrSubsamplingDescription(); 126 case ExifSubIFDDirectory.TAG_EXPOSURE_PROGRAM: 127 return getExposureProgramDescription(); 128 case ExifSubIFDDirectory.TAG_APERTURE: 129 return getApertureValueDescription(); 130 case ExifSubIFDDirectory.TAG_MAX_APERTURE: 131 return getMaxApertureValueDescription(); 132 case ExifSubIFDDirectory.TAG_SENSING_METHOD: 133 return getSensingMethodDescription(); 134 case ExifSubIFDDirectory.TAG_EXPOSURE_BIAS: 135 return getExposureBiasDescription(); 136 case ExifSubIFDDirectory.TAG_FILE_SOURCE: 137 return getFileSourceDescription(); 138 case ExifSubIFDDirectory.TAG_SCENE_TYPE: 139 return getSceneTypeDescription(); 140 case ExifSubIFDDirectory.TAG_COMPONENTS_CONFIGURATION: 141 return getComponentConfigurationDescription(); 142 case ExifSubIFDDirectory.TAG_EXIF_VERSION: 143 return getExifVersionDescription(); 144 case ExifSubIFDDirectory.TAG_FLASHPIX_VERSION: 145 return getFlashPixVersionDescription(); 146 case ExifSubIFDDirectory.TAG_ISO_EQUIVALENT: 147 return getIsoEquivalentDescription(); 148 case ExifSubIFDDirectory.TAG_USER_COMMENT: 149 return getUserCommentDescription(); 150 case ExifSubIFDDirectory.TAG_CUSTOM_RENDERED: 151 return getCustomRenderedDescription(); 152 case ExifSubIFDDirectory.TAG_EXPOSURE_MODE: 153 return getExposureModeDescription(); 154 case ExifSubIFDDirectory.TAG_WHITE_BALANCE_MODE: 155 return getWhiteBalanceModeDescription(); 156 case ExifSubIFDDirectory.TAG_DIGITAL_ZOOM_RATIO: 157 return getDigitalZoomRatioDescription(); 158 case ExifSubIFDDirectory.TAG_35MM_FILM_EQUIV_FOCAL_LENGTH: 159 return get35mmFilmEquivFocalLengthDescription(); 160 case ExifSubIFDDirectory.TAG_SCENE_CAPTURE_TYPE: 161 return getSceneCaptureTypeDescription(); 162 case ExifSubIFDDirectory.TAG_GAIN_CONTROL: 163 return getGainControlDescription(); 164 case ExifSubIFDDirectory.TAG_CONTRAST: 165 return getContrastDescription(); 166 case ExifSubIFDDirectory.TAG_SATURATION: 167 return getSaturationDescription(); 168 case ExifSubIFDDirectory.TAG_SHARPNESS: 169 return getSharpnessDescription(); 170 case ExifSubIFDDirectory.TAG_SUBJECT_DISTANCE_RANGE: 171 return getSubjectDistanceRangeDescription(); 172 default: 173 return super.getDescription(tagType); 174 } 175 } 176 177 @Nullable 178 public String getNewSubfileTypeDescription() 179 { 180 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_NEW_SUBFILE_TYPE); 181 if (value==null) 182 return null; 183 switch (value) { 184 case 1: return "Full-resolution image"; 185 case 2: return "Reduced-resolution image"; 186 case 3: return "Single page of multi-page reduced-resolution image"; 187 case 4: return "Transparency mask"; 188 case 5: return "Transparency mask of reduced-resolution image"; 189 case 6: return "Transparency mask of multi-page image"; 190 case 7: return "Transparency mask of reduced-resolution multi-page image"; 191 default: 192 return "Unknown (" + value + ")"; 193 } 194 } 195 196 @Nullable 197 public String getSubfileTypeDescription() 198 { 199 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_SUBFILE_TYPE); 200 if (value==null) 201 return null; 202 switch (value) { 203 case 1: return "Full-resolution image"; 204 case 2: return "Reduced-resolution image"; 205 case 3: return "Single page of multi-page image"; 206 default: 207 return "Unknown (" + value + ")"; 208 } 209 } 210 211 @Nullable 212 public String getThresholdingDescription() 213 { 214 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_THRESHOLDING); 215 if (value==null) 216 return null; 217 switch (value) { 218 case 1: return "No dithering or halftoning"; 219 case 2: return "Ordered dither or halftone"; 220 case 3: return "Randomized dither"; 221 default: 222 return "Unknown (" + value + ")"; 223 } 224 } 225 226 @Nullable 227 public String getFillOrderDescription() 228 { 229 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_FILL_ORDER); 230 if (value==null) 231 return null; 232 switch (value) { 233 case 1: return "Normal"; 234 case 2: return "Reversed"; 235 default: 236 return "Unknown (" + value + ")"; 237 } 238 } 239 240 @Nullable 241 public String getSubjectDistanceRangeDescription() 242 { 243 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_SUBJECT_DISTANCE_RANGE); 244 if (value==null) 245 return null; 246 switch (value) { 247 case 0: return "Unknown"; 248 case 1: return "Macro"; 249 case 2: return "Close view"; 250 case 3: return "Distant view"; 251 default: 252 return "Unknown (" + value + ")"; 253 } 254 } 255 256 @Nullable 257 public String getSharpnessDescription() 258 { 259 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_SHARPNESS); 260 if (value==null) 261 return null; 262 switch (value) { 263 case 0: return "None"; 264 case 1: return "Low"; 265 case 2: return "Hard"; 266 default: 267 return "Unknown (" + value + ")"; 268 } 269 } 270 271 @Nullable 272 public String getSaturationDescription() 273 { 274 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_SATURATION); 275 if (value==null) 276 return null; 277 switch (value) { 278 case 0: return "None"; 279 case 1: return "Low saturation"; 280 case 2: return "High saturation"; 281 default: 282 return "Unknown (" + value + ")"; 283 } 284 } 285 286 @Nullable 287 public String getContrastDescription() 288 { 289 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_CONTRAST); 290 if (value==null) 291 return null; 292 switch (value) { 293 case 0: return "None"; 294 case 1: return "Soft"; 295 case 2: return "Hard"; 296 default: 297 return "Unknown (" + value + ")"; 298 } 299 } 300 301 @Nullable 302 public String getGainControlDescription() 303 { 304 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_GAIN_CONTROL); 305 if (value==null) 306 return null; 307 switch (value) { 308 case 0: return "None"; 309 case 1: return "Low gain up"; 310 case 2: return "Low gain down"; 311 case 3: return "High gain up"; 312 case 4: return "High gain down"; 313 default: 314 return "Unknown (" + value + ")"; 315 } 316 } 317 318 @Nullable 319 public String getSceneCaptureTypeDescription() 320 { 321 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_SCENE_CAPTURE_TYPE); 322 if (value==null) 323 return null; 324 switch (value) { 325 case 0: return "Standard"; 326 case 1: return "Landscape"; 327 case 2: return "Portrait"; 328 case 3: return "Night scene"; 329 default: 330 return "Unknown (" + value + ")"; 331 } 332 } 333 334 @Nullable 335 public String get35mmFilmEquivFocalLengthDescription() 336 { 337 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_35MM_FILM_EQUIV_FOCAL_LENGTH); 338 if (value==null) 339 return null; 340 if (value==0) 341 return "Unknown"; 342 else 343 return SimpleDecimalFormatter.format(value) + "mm"; 344 } 345 346 @Nullable 347 public String getDigitalZoomRatioDescription() 348 { 349 Rational value = _directory.getRational(ExifSubIFDDirectory.TAG_DIGITAL_ZOOM_RATIO); 350 if (value==null) 351 return null; 352 if (value.getNumerator()==0) 353 return "Digital zoom not used."; 354 return SimpleDecimalFormatter.format(value.doubleValue()); 355 } 356 357 @Nullable 358 public String getWhiteBalanceModeDescription() 359 { 360 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_WHITE_BALANCE_MODE); 361 if (value==null) 362 return null; 363 switch (value) { 364 case 0: return "Auto white balance"; 365 case 1: return "Manual white balance"; 366 default: 367 return "Unknown (" + value + ")"; 368 } 369 } 370 371 @Nullable 372 public String getExposureModeDescription() 373 { 374 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_EXPOSURE_MODE); 375 if (value==null) 376 return null; 377 switch (value) { 378 case 0: return "Auto exposure"; 379 case 1: return "Manual exposure"; 380 case 2: return "Auto bracket"; 381 default: 382 return "Unknown (" + value + ")"; 383 } 384 } 385 386 @Nullable 387 public String getCustomRenderedDescription() 388 { 389 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_CUSTOM_RENDERED); 390 if (value==null) 391 return null; 392 switch (value) { 393 case 0: return "Normal process"; 394 case 1: return "Custom process"; 395 default: 396 return "Unknown (" + value + ")"; 397 } 398 } 399 400 @Nullable 401 public String getUserCommentDescription() 402 { 403 byte[] commentBytes = _directory.getByteArray(ExifSubIFDDirectory.TAG_USER_COMMENT); 404 if (commentBytes==null) 405 return null; 406 if (commentBytes.length == 0) 407 return ""; 408 409 final Map<String, String> encodingMap = new HashMap<String, String>(); 410 encodingMap.put("ASCII", System.getProperty("file.encoding")); // Someone suggested "ISO-8859-1". 411 encodingMap.put("UNICODE", "UTF-16LE"); 412 encodingMap.put("JIS", "Shift-JIS"); // We assume this charset for now. Another suggestion is "JIS". 413 414 try { 415 if (commentBytes.length >= 10) { 416 String firstTenBytesString = new String(commentBytes, 0, 10); 417 418 // try each encoding name 419 for (Map.Entry<String, String> pair : encodingMap.entrySet()) { 420 String encodingName = pair.getKey(); 421 String charset = pair.getValue(); 422 if (firstTenBytesString.startsWith(encodingName)) { 423 // skip any null or blank characters commonly present after the encoding name, up to a limit of 10 from the start 424 for (int j = encodingName.length(); j < 10; j++) { 425 byte b = commentBytes[j]; 426 if (b != '\0' && b != ' ') 427 return new String(commentBytes, j, commentBytes.length - j, charset).trim(); 428 } 429 return new String(commentBytes, 10, commentBytes.length - 10, charset).trim(); 430 } 431 } 432 } 433 // special handling fell through, return a plain string representation 434 return new String(commentBytes, System.getProperty("file.encoding")).trim(); 435 } catch (UnsupportedEncodingException ex) { 436 return null; 437 } 438 } 439 440 @Nullable 441 public String getIsoEquivalentDescription() 442 { 443 // Have seen an exception here from files produced by ACDSEE that stored an int[] here with two values 444 Integer isoEquiv = _directory.getInteger(ExifSubIFDDirectory.TAG_ISO_EQUIVALENT); 445 if (isoEquiv==null) 446 return null; 447 // There used to be a check here that multiplied ISO values < 50 by 200. 448 // Issue 36 shows a smart-phone image from a Samsung Galaxy S2 with ISO-40. 449 return Integer.toString(isoEquiv); 450 } 451 452 @Nullable 453 public String getExifVersionDescription() 454 { 455 int[] ints = _directory.getIntArray(ExifSubIFDDirectory.TAG_EXIF_VERSION); 456 if (ints==null) 457 return null; 458 return ExifSubIFDDescriptor.convertBytesToVersionString(ints, 2); 459 } 460 461 @Nullable 462 public String getFlashPixVersionDescription() 463 { 464 int[] ints = _directory.getIntArray(ExifSubIFDDirectory.TAG_FLASHPIX_VERSION); 465 if (ints==null) 466 return null; 467 return ExifSubIFDDescriptor.convertBytesToVersionString(ints, 2); 468 } 469 470 @Nullable 471 public String getSceneTypeDescription() 472 { 473 Integer sceneType = _directory.getInteger(ExifSubIFDDirectory.TAG_SCENE_TYPE); 474 if (sceneType==null) 475 return null; 476 return sceneType == 1 477 ? "Directly photographed image" 478 : "Unknown (" + sceneType + ")"; 479 } 480 481 @Nullable 482 public String getFileSourceDescription() 483 { 484 Integer fileSource = _directory.getInteger(ExifSubIFDDirectory.TAG_FILE_SOURCE); 485 if (fileSource==null) 486 return null; 487 return fileSource == 3 488 ? "Digital Still Camera (DSC)" 489 : "Unknown (" + fileSource + ")"; 490 } 491 492 @Nullable 493 public String getExposureBiasDescription() 494 { 495 Rational value = _directory.getRational(ExifSubIFDDirectory.TAG_EXPOSURE_BIAS); 496 if (value==null) 497 return null; 498 return value.toSimpleString(true) + " EV"; 499 } 500 501 @Nullable 502 public String getMaxApertureValueDescription() 503 { 504 Double aperture = _directory.getDoubleObject(ExifSubIFDDirectory.TAG_MAX_APERTURE); 505 if (aperture==null) 506 return null; 507 double fStop = PhotographicConversions.apertureToFStop(aperture); 508 return "F" + SimpleDecimalFormatter.format(fStop); 509 } 510 511 @Nullable 512 public String getApertureValueDescription() 513 { 514 Double aperture = _directory.getDoubleObject(ExifSubIFDDirectory.TAG_APERTURE); 515 if (aperture==null) 516 return null; 517 double fStop = PhotographicConversions.apertureToFStop(aperture); 518 return "F" + SimpleDecimalFormatter.format(fStop); 519 } 520 521 @Nullable 522 public String getExposureProgramDescription() 523 { 524 // '1' means manual control, '2' program normal, '3' aperture priority, 525 // '4' shutter priority, '5' program creative (slow program), 526 // '6' program action(high-speed program), '7' portrait mode, '8' landscape mode. 527 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_EXPOSURE_PROGRAM); 528 if (value==null) 529 return null; 530 switch (value) { 531 case 1: return "Manual control"; 532 case 2: return "Program normal"; 533 case 3: return "Aperture priority"; 534 case 4: return "Shutter priority"; 535 case 5: return "Program creative (slow program)"; 536 case 6: return "Program action (high-speed program)"; 537 case 7: return "Portrait mode"; 538 case 8: return "Landscape mode"; 539 default: 540 return "Unknown program (" + value + ")"; 541 } 542 } 543 544 @Nullable 545 public String getYCbCrSubsamplingDescription() 546 { 547 int[] positions = _directory.getIntArray(ExifSubIFDDirectory.TAG_YCBCR_SUBSAMPLING); 548 if (positions==null) 549 return null; 550 if (positions[0] == 2 && positions[1] == 1) { 551 return "YCbCr4:2:2"; 552 } else if (positions[0] == 2 && positions[1] == 2) { 553 return "YCbCr4:2:0"; 554 } else { 555 return "(Unknown)"; 556 } 557 } 558 559 @Nullable 560 public String getPlanarConfigurationDescription() 561 { 562 // When image format is no compression YCbCr, this value shows byte aligns of YCbCr 563 // data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for each subsampling 564 // pixel. If value is '2', Y/Cb/Cr value is separated and stored to Y plane/Cb plane/Cr 565 // plane format. 566 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_PLANAR_CONFIGURATION); 567 if (value==null) 568 return null; 569 switch (value) { 570 case 1: return "Chunky (contiguous for each subsampling pixel)"; 571 case 2: return "Separate (Y-plane/Cb-plane/Cr-plane format)"; 572 default: 573 return "Unknown configuration"; 574 } 575 } 576 577 @Nullable 578 public String getSamplesPerPixelDescription() 579 { 580 String value = _directory.getString(ExifSubIFDDirectory.TAG_SAMPLES_PER_PIXEL); 581 return value==null ? null : value + " samples/pixel"; 582 } 583 584 @Nullable 585 public String getRowsPerStripDescription() 586 { 587 final String value = _directory.getString(ExifSubIFDDirectory.TAG_ROWS_PER_STRIP); 588 return value==null ? null : value + " rows/strip"; 589 } 590 591 @Nullable 592 public String getStripByteCountsDescription() 593 { 594 final String value = _directory.getString(ExifSubIFDDirectory.TAG_STRIP_BYTE_COUNTS); 595 return value==null ? null : value + " bytes"; 596 } 597 598 @Nullable 599 public String getPhotometricInterpretationDescription() 600 { 601 // Shows the color space of the image data components 602 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_PHOTOMETRIC_INTERPRETATION); 603 if (value==null) 604 return null; 605 switch (value) { 606 case 0: return "WhiteIsZero"; 607 case 1: return "BlackIsZero"; 608 case 2: return "RGB"; 609 case 3: return "RGB Palette"; 610 case 4: return "Transparency Mask"; 611 case 5: return "CMYK"; 612 case 6: return "YCbCr"; 613 case 8: return "CIELab"; 614 case 9: return "ICCLab"; 615 case 10: return "ITULab"; 616 case 32803: return "Color Filter Array"; 617 case 32844: return "Pixar LogL"; 618 case 32845: return "Pixar LogLuv"; 619 case 32892: return "Linear Raw"; 620 default: 621 return "Unknown colour space"; 622 } 623 } 624 625 @Nullable 626 public String getBitsPerSampleDescription() 627 { 628 String value = _directory.getString(ExifSubIFDDirectory.TAG_BITS_PER_SAMPLE); 629 return value==null ? null : value + " bits/component/pixel"; 630 } 631 632 @Nullable 633 public String getFocalPlaneXResolutionDescription() 634 { 635 Rational rational = _directory.getRational(ExifSubIFDDirectory.TAG_FOCAL_PLANE_X_RES); 636 if (rational==null) 637 return null; 638 final String unit = getFocalPlaneResolutionUnitDescription(); 639 return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) 640 + (unit==null ? "" : " " + unit.toLowerCase()); 641 } 642 643 @Nullable 644 public String getFocalPlaneYResolutionDescription() 645 { 646 Rational rational = _directory.getRational(ExifSubIFDDirectory.TAG_FOCAL_PLANE_Y_RES); 647 if (rational==null) 648 return null; 649 final String unit = getFocalPlaneResolutionUnitDescription(); 650 return rational.getReciprocal().toSimpleString(_allowDecimalRepresentationOfRationals) 651 + (unit==null ? "" : " " + unit.toLowerCase()); 652 } 653 654 @Nullable 655 public String getFocalPlaneResolutionUnitDescription() 656 { 657 // Unit of FocalPlaneXResolution/FocalPlaneYResolution. '1' means no-unit, 658 // '2' inch, '3' centimeter. 659 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_FOCAL_PLANE_UNIT); 660 if (value==null) 661 return null; 662 switch (value) { 663 case 1: return "(No unit)"; 664 case 2: return "Inches"; 665 case 3: return "cm"; 666 default: 667 return ""; 668 } 669 } 670 671 @Nullable 672 public String getExifImageWidthDescription() 673 { 674 final Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_EXIF_IMAGE_WIDTH); 675 if (value==null) 676 return null; 677 return value + " pixels"; 678 } 679 680 @Nullable 681 public String getExifImageHeightDescription() 682 { 683 final Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_EXIF_IMAGE_HEIGHT); 684 if (value==null) 685 return null; 686 return value + " pixels"; 687 } 688 689 @Nullable 690 public String getColorSpaceDescription() 691 { 692 final Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_COLOR_SPACE); 693 if (value==null) 694 return null; 695 if (value == 1) 696 return "sRGB"; 697 if (value == 65535) 698 return "Undefined"; 699 return "Unknown"; 700 } 701 702 @Nullable 703 public String getFocalLengthDescription() 704 { 705 Rational value = _directory.getRational(ExifSubIFDDirectory.TAG_FOCAL_LENGTH); 706 if (value==null) 707 return null; 708 java.text.DecimalFormat formatter = new DecimalFormat("0.0##"); 709 return formatter.format(value.doubleValue()) + " mm"; 710 } 711 712 @Nullable 713 public String getFlashDescription() 714 { 715 /* 716 * This is a bitmask. 717 * 0 = flash fired 718 * 1 = return detected 719 * 2 = return able to be detected 720 * 3 = unknown 721 * 4 = auto used 722 * 5 = unknown 723 * 6 = red eye reduction used 724 */ 725 726 final Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_FLASH); 727 728 if (value==null) 729 return null; 730 731 StringBuilder sb = new StringBuilder(); 732 733 if ((value & 0x1)!=0) 734 sb.append("Flash fired"); 735 else 736 sb.append("Flash did not fire"); 737 738 // check if we're able to detect a return, before we mention it 739 if ((value & 0x4)!=0) 740 { 741 if ((value & 0x2)!=0) 742 sb.append(", return detected"); 743 else 744 sb.append(", return not detected"); 745 } 746 747 if ((value & 0x10)!=0) 748 sb.append(", auto"); 749 750 if ((value & 0x40)!=0) 751 sb.append(", red-eye reduction"); 752 753 return sb.toString(); 754 } 755 756 @Nullable 757 public String getWhiteBalanceDescription() 758 { 759 // '0' means unknown, '1' daylight, '2' fluorescent, '3' tungsten, '10' flash, 760 // '17' standard light A, '18' standard light B, '19' standard light C, '20' D55, 761 // '21' D65, '22' D75, '255' other. 762 final Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_WHITE_BALANCE); 763 if (value==null) 764 return null; 765 switch (value) { 766 case 0: return "Unknown"; 767 case 1: return "Daylight"; 768 case 2: return "Florescent"; 769 case 3: return "Tungsten"; 770 case 10: return "Flash"; 771 case 17: return "Standard light"; 772 case 18: return "Standard light (B)"; 773 case 19: return "Standard light (C)"; 774 case 20: return "D55"; 775 case 21: return "D65"; 776 case 22: return "D75"; 777 case 255: return "(Other)"; 778 default: 779 return "Unknown (" + value + ")"; 780 } 781 } 782 783 @Nullable 784 public String getMeteringModeDescription() 785 { 786 // '0' means unknown, '1' average, '2' center weighted average, '3' spot 787 // '4' multi-spot, '5' multi-segment, '6' partial, '255' other 788 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_METERING_MODE); 789 if (value==null) 790 return null; 791 switch (value) { 792 case 0: return "Unknown"; 793 case 1: return "Average"; 794 case 2: return "Center weighted average"; 795 case 3: return "Spot"; 796 case 4: return "Multi-spot"; 797 case 5: return "Multi-segment"; 798 case 6: return "Partial"; 799 case 255: return "(Other)"; 800 default: 801 return ""; 802 } 803 } 804 805 @Nullable 806 public String getSubjectDistanceDescription() 807 { 808 Rational value = _directory.getRational(ExifSubIFDDirectory.TAG_SUBJECT_DISTANCE); 809 if (value==null) 810 return null; 811 java.text.DecimalFormat formatter = new DecimalFormat("0.0##"); 812 return formatter.format(value.doubleValue()) + " metres"; 813 } 814 815 @Nullable 816 public String getCompressedAverageBitsPerPixelDescription() 817 { 818 Rational value = _directory.getRational(ExifSubIFDDirectory.TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL); 819 if (value==null) 820 return null; 821 String ratio = value.toSimpleString(_allowDecimalRepresentationOfRationals); 822 if (value.isInteger() && value.intValue() == 1) { 823 return ratio + " bit/pixel"; 824 } else { 825 return ratio + " bits/pixel"; 826 } 827 } 828 829 @Nullable 830 public String getExposureTimeDescription() 831 { 832 String value = _directory.getString(ExifSubIFDDirectory.TAG_EXPOSURE_TIME); 833 return value==null ? null : value + " sec"; 834 } 835 836 @Nullable 837 public String getShutterSpeedDescription() 838 { 839 // I believe this method to now be stable, but am leaving some alternative snippets of 840 // code in here, to assist anyone who's looking into this (given that I don't have a public CVS). 841 842 // float apexValue = _directory.getFloat(ExifSubIFDDirectory.TAG_SHUTTER_SPEED); 843 // int apexPower = (int)Math.pow(2.0, apexValue); 844 // return "1/" + apexPower + " sec"; 845 // TODO test this method 846 // thanks to Mark Edwards for spotting and patching a bug in the calculation of this 847 // description (spotted bug using a Canon EOS 300D) 848 // thanks also to Gli Blr for spotting this bug 849 Float apexValue = _directory.getFloatObject(ExifSubIFDDirectory.TAG_SHUTTER_SPEED); 850 if (apexValue==null) 851 return null; 852 if (apexValue<=1) { 853 float apexPower = (float)(1/(Math.exp(apexValue*Math.log(2)))); 854 long apexPower10 = Math.round((double)apexPower * 10.0); 855 float fApexPower = (float) apexPower10 / 10.0f; 856 return fApexPower + " sec"; 857 } else { 858 int apexPower = (int)((Math.exp(apexValue*Math.log(2)))); 859 return "1/" + apexPower + " sec"; 860 } 861 862 /* 863 // This alternative implementation offered by Bill Richards 864 // TODO determine which is the correct / more-correct implementation 865 double apexValue = _directory.getDouble(ExifSubIFDDirectory.TAG_SHUTTER_SPEED); 866 double apexPower = Math.pow(2.0, apexValue); 867 868 StringBuffer sb = new StringBuffer(); 869 if (apexPower > 1) 870 apexPower = Math.floor(apexPower); 871 872 if (apexPower < 1) { 873 sb.append((int)Math.round(1/apexPower)); 874 } else { 875 sb.append("1/"); 876 sb.append((int)apexPower); 877 } 878 sb.append(" sec"); 879 return sb.toString(); 880 */ 881 882 } 883 884 @Nullable 885 public String getFNumberDescription() 886 { 887 Rational value = _directory.getRational(ExifSubIFDDirectory.TAG_FNUMBER); 888 if (value==null) 889 return null; 890 return "F" + SimpleDecimalFormatter.format(value.doubleValue()); 891 } 892 893 @Nullable 894 public String getSensingMethodDescription() 895 { 896 // '1' Not defined, '2' One-chip color area sensor, '3' Two-chip color area sensor 897 // '4' Three-chip color area sensor, '5' Color sequential area sensor 898 // '7' Trilinear sensor '8' Color sequential linear sensor, 'Other' reserved 899 Integer value = _directory.getInteger(ExifSubIFDDirectory.TAG_SENSING_METHOD); 900 if (value==null) 901 return null; 902 switch (value) { 903 case 1: return "(Not defined)"; 904 case 2: return "One-chip color area sensor"; 905 case 3: return "Two-chip color area sensor"; 906 case 4: return "Three-chip color area sensor"; 907 case 5: return "Color sequential area sensor"; 908 case 7: return "Trilinear sensor"; 909 case 8: return "Color sequential linear sensor"; 910 default: 911 return ""; 912 } 913 } 914 915 @Nullable 916 public String getComponentConfigurationDescription() 917 { 918 int[] components = _directory.getIntArray(ExifSubIFDDirectory.TAG_COMPONENTS_CONFIGURATION); 919 if (components==null) 920 return null; 921 String[] componentStrings = {"", "Y", "Cb", "Cr", "R", "G", "B"}; 922 StringBuilder componentConfig = new StringBuilder(); 923 for (int i = 0; i < Math.min(4, components.length); i++) { 924 int j = components[i]; 925 if (j > 0 && j < componentStrings.length) { 926 componentConfig.append(componentStrings[j]); 927 } 928 } 929 return componentConfig.toString(); 930 } 931 } -
src/com/drew/metadata/exif/ExifSubIFDDescriptor.java
-
src/com/drew/metadata/exif/ExifSubIFDDirectory.java
Modification de propriétés sur src/com/drew/metadata/exif/ExifSubIFDDescriptor.java ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
1 /* 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/ 20 */ 21 package com.drew.metadata.exif; 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.metadata.Directory; 25 26 import java.util.HashMap; 27 28 /** 29 * Describes Exif tags from the SubIFD directory. 30 * 31 * @author Drew Noakes http://drewnoakes.com 32 */ 33 public class ExifSubIFDDirectory extends Directory 34 { 35 /** 36 * The actual aperture value of lens when the image was taken. Unit is APEX. 37 * To convert this value to ordinary F-number (F-stop), calculate this value's 38 * power of root 2 (=1.4142). For example, if the ApertureValue is '5', 39 * F-number is 1.4142^5 = F5.6. 40 */ 41 public static final int TAG_APERTURE = 0x9202; 42 /** 43 * When image format is no compression, this value shows the number of bits 44 * per component for each pixel. Usually this value is '8,8,8'. 45 */ 46 public static final int TAG_BITS_PER_SAMPLE = 0x0102; 47 48 /** 49 * Shows the color space of the image data components. 50 * 0 = WhiteIsZero 51 * 1 = BlackIsZero 52 * 2 = RGB 53 * 3 = RGB Palette 54 * 4 = Transparency Mask 55 * 5 = CMYK 56 * 6 = YCbCr 57 * 8 = CIELab 58 * 9 = ICCLab 59 * 10 = ITULab 60 * 32803 = Color Filter Array 61 * 32844 = Pixar LogL 62 * 32845 = Pixar LogLuv 63 * 34892 = Linear Raw 64 */ 65 public static final int TAG_PHOTOMETRIC_INTERPRETATION = 0x0106; 66 67 /** 68 * 1 = No dithering or halftoning 69 * 2 = Ordered dither or halftone 70 * 3 = Randomized dither 71 */ 72 public static final int TAG_THRESHOLDING = 0x0107; 73 74 /** 75 * 1 = Normal 76 * 2 = Reversed 77 */ 78 public static final int TAG_FILL_ORDER = 0x010A; 79 public static final int TAG_DOCUMENT_NAME = 0x010D; 80 81 /** The position in the file of raster data. */ 82 public static final int TAG_STRIP_OFFSETS = 0x0111; 83 /** Each pixel is composed of this many samples. */ 84 public static final int TAG_SAMPLES_PER_PIXEL = 0x0115; 85 /** The raster is codified by a single block of data holding this many rows. */ 86 public static final int TAG_ROWS_PER_STRIP = 0x116; 87 /** The size of the raster data in bytes. */ 88 public static final int TAG_STRIP_BYTE_COUNTS = 0x0117; 89 public static final int TAG_MIN_SAMPLE_VALUE = 0x0118; 90 public static final int TAG_MAX_SAMPLE_VALUE = 0x0119; 91 /** 92 * When image format is no compression YCbCr, this value shows byte aligns of 93 * YCbCr data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for 94 * each subsampling pixel. If value is '2', Y/Cb/Cr value is separated and 95 * stored to Y plane/Cb plane/Cr plane format. 96 */ 97 public static final int TAG_PLANAR_CONFIGURATION = 0x011C; 98 public static final int TAG_YCBCR_SUBSAMPLING = 0x0212; 99 100 /** 101 * The new subfile type tag. 102 * 0 = Full-resolution Image 103 * 1 = Reduced-resolution image 104 * 2 = Single page of multi-page image 105 * 3 = Single page of multi-page reduced-resolution image 106 * 4 = Transparency mask 107 * 5 = Transparency mask of reduced-resolution image 108 * 6 = Transparency mask of multi-page image 109 * 7 = Transparency mask of reduced-resolution multi-page image 110 */ 111 public static final int TAG_NEW_SUBFILE_TYPE = 0x00FE; 112 /** 113 * The old subfile type tag. 114 * 1 = Full-resolution image (Main image) 115 * 2 = Reduced-resolution image (Thumbnail) 116 * 3 = Single page of multi-page image 117 */ 118 public static final int TAG_SUBFILE_TYPE = 0x00FF; 119 public static final int TAG_TRANSFER_FUNCTION = 0x012D; 120 public static final int TAG_PREDICTOR = 0x013D; 121 public static final int TAG_TILE_WIDTH = 0x0142; 122 public static final int TAG_TILE_LENGTH = 0x0143; 123 public static final int TAG_TILE_OFFSETS = 0x0144; 124 public static final int TAG_TILE_BYTE_COUNTS = 0x0145; 125 public static final int TAG_JPEG_TABLES = 0x015B; 126 public static final int TAG_CFA_REPEAT_PATTERN_DIM = 0x828D; 127 /** There are two definitions for CFA pattern, I don't know the difference... */ 128 public static final int TAG_CFA_PATTERN_2 = 0x828E; 129 public static final int TAG_BATTERY_LEVEL = 0x828F; 130 public static final int TAG_IPTC_NAA = 0x83BB; 131 public static final int TAG_INTER_COLOR_PROFILE = 0x8773; 132 public static final int TAG_SPECTRAL_SENSITIVITY = 0x8824; 133 /** 134 * Indicates the Opto-Electric Conversion Function (OECF) specified in ISO 14524. 135 * <p/> 136 * OECF is the relationship between the camera optical input and the image values. 137 * <p/> 138 * The values are: 139 * <ul> 140 * <li>Two shorts, indicating respectively number of columns, and number of rows.</li> 141 * <li>For each column, the column name in a null-terminated ASCII string.</li> 142 * <li>For each cell, an SRATIONAL value.</li> 143 * </ul> 144 */ 145 public static final int TAG_OPTO_ELECTRIC_CONVERSION_FUNCTION = 0x8828; 146 public static final int TAG_INTERLACE = 0x8829; 147 public static final int TAG_TIME_ZONE_OFFSET = 0x882A; 148 public static final int TAG_SELF_TIMER_MODE = 0x882B; 149 public static final int TAG_FLASH_ENERGY = 0x920B; 150 public static final int TAG_SPATIAL_FREQ_RESPONSE = 0x920C; 151 public static final int TAG_NOISE = 0x920D; 152 public static final int TAG_IMAGE_NUMBER = 0x9211; 153 public static final int TAG_SECURITY_CLASSIFICATION = 0x9212; 154 public static final int TAG_IMAGE_HISTORY = 0x9213; 155 public static final int TAG_SUBJECT_LOCATION = 0x9214; 156 /** There are two definitions for exposure index, I don't know the difference... */ 157 public static final int TAG_EXPOSURE_INDEX_2 = 0x9215; 158 public static final int TAG_TIFF_EP_STANDARD_ID = 0x9216; 159 public static final int TAG_FLASH_ENERGY_2 = 0xA20B; 160 public static final int TAG_SPATIAL_FREQ_RESPONSE_2 = 0xA20C; 161 public static final int TAG_SUBJECT_LOCATION_2 = 0xA214; 162 public static final int TAG_PAGE_NAME = 0x011D; 163 /** 164 * Exposure time (reciprocal of shutter speed). Unit is second. 165 */ 166 public static final int TAG_EXPOSURE_TIME = 0x829A; 167 /** 168 * The actual F-number(F-stop) of lens when the image was taken. 169 */ 170 public static final int TAG_FNUMBER = 0x829D; 171 /** 172 * Exposure program that the camera used when image was taken. '1' means 173 * manual control, '2' program normal, '3' aperture priority, '4' shutter 174 * priority, '5' program creative (slow program), '6' program action 175 * (high-speed program), '7' portrait mode, '8' landscape mode. 176 */ 177 public static final int TAG_EXPOSURE_PROGRAM = 0x8822; 178 public static final int TAG_ISO_EQUIVALENT = 0x8827; 179 public static final int TAG_EXIF_VERSION = 0x9000; 180 public static final int TAG_DATETIME_ORIGINAL = 0x9003; 181 public static final int TAG_DATETIME_DIGITIZED = 0x9004; 182 public static final int TAG_COMPONENTS_CONFIGURATION = 0x9101; 183 /** 184 * Average (rough estimate) compression level in JPEG bits per pixel. 185 * */ 186 public static final int TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL = 0x9102; 187 /** 188 * Shutter speed by APEX value. To convert this value to ordinary 'Shutter Speed'; 189 * calculate this value's power of 2, then reciprocal. For example, if the 190 * ShutterSpeedValue is '4', shutter speed is 1/(24)=1/16 second. 191 */ 192 public static final int TAG_SHUTTER_SPEED = 0x9201; 193 public static final int TAG_BRIGHTNESS_VALUE = 0x9203; 194 public static final int TAG_EXPOSURE_BIAS = 0x9204; 195 /** 196 * Maximum aperture value of lens. You can convert to F-number by calculating 197 * power of root 2 (same process of ApertureValue:0x9202). 198 * The actual aperture value of lens when the image was taken. To convert this 199 * value to ordinary f-number(f-stop), calculate the value's power of root 2 200 * (=1.4142). For example, if the ApertureValue is '5', f-number is 1.41425^5 = F5.6. 201 */ 202 public static final int TAG_MAX_APERTURE = 0x9205; 203 /** 204 * Indicates the distance the autofocus camera is focused to. Tends to be less accurate as distance increases. 205 */ 206 public static final int TAG_SUBJECT_DISTANCE = 0x9206; 207 /** 208 * Exposure metering method. '0' means unknown, '1' average, '2' center 209 * weighted average, '3' spot, '4' multi-spot, '5' multi-segment, '6' partial, 210 * '255' other. 211 */ 212 public static final int TAG_METERING_MODE = 0x9207; 213 214 public static final int TAG_LIGHT_SOURCE = 0x9208; 215 /** 216 * White balance (aka light source). '0' means unknown, '1' daylight, 217 * '2' fluorescent, '3' tungsten, '10' flash, '17' standard light A, 218 * '18' standard light B, '19' standard light C, '20' D55, '21' D65, 219 * '22' D75, '255' other. 220 */ 221 public static final int TAG_WHITE_BALANCE = 0x9208; 222 /** 223 * 0x0 = 0000000 = No Flash 224 * 0x1 = 0000001 = Fired 225 * 0x5 = 0000101 = Fired, Return not detected 226 * 0x7 = 0000111 = Fired, Return detected 227 * 0x9 = 0001001 = On 228 * 0xd = 0001101 = On, Return not detected 229 * 0xf = 0001111 = On, Return detected 230 * 0x10 = 0010000 = Off 231 * 0x18 = 0011000 = Auto, Did not fire 232 * 0x19 = 0011001 = Auto, Fired 233 * 0x1d = 0011101 = Auto, Fired, Return not detected 234 * 0x1f = 0011111 = Auto, Fired, Return detected 235 * 0x20 = 0100000 = No flash function 236 * 0x41 = 1000001 = Fired, Red-eye reduction 237 * 0x45 = 1000101 = Fired, Red-eye reduction, Return not detected 238 * 0x47 = 1000111 = Fired, Red-eye reduction, Return detected 239 * 0x49 = 1001001 = On, Red-eye reduction 240 * 0x4d = 1001101 = On, Red-eye reduction, Return not detected 241 * 0x4f = 1001111 = On, Red-eye reduction, Return detected 242 * 0x59 = 1011001 = Auto, Fired, Red-eye reduction 243 * 0x5d = 1011101 = Auto, Fired, Red-eye reduction, Return not detected 244 * 0x5f = 1011111 = Auto, Fired, Red-eye reduction, Return detected 245 * 6543210 (positions) 246 * 247 * This is a bitmask. 248 * 0 = flash fired 249 * 1 = return detected 250 * 2 = return able to be detected 251 * 3 = unknown 252 * 4 = auto used 253 * 5 = unknown 254 * 6 = red eye reduction used 255 */ 256 public static final int TAG_FLASH = 0x9209; 257 /** 258 * Focal length of lens used to take image. Unit is millimeter. 259 * Nice digital cameras actually save the focal length as a function of how far they are zoomed in. 260 */ 261 public static final int TAG_FOCAL_LENGTH = 0x920A; 262 public static final int TAG_USER_COMMENT = 0x9286; 263 public static final int TAG_SUBSECOND_TIME = 0x9290; 264 public static final int TAG_SUBSECOND_TIME_ORIGINAL = 0x9291; 265 public static final int TAG_SUBSECOND_TIME_DIGITIZED = 0x9292; 266 public static final int TAG_FLASHPIX_VERSION = 0xA000; 267 /** 268 * Defines Color Space. DCF image must use sRGB color space so value is 269 * always '1'. If the picture uses the other color space, value is 270 * '65535':Uncalibrated. 271 */ 272 public static final int TAG_COLOR_SPACE = 0xA001; 273 public static final int TAG_EXIF_IMAGE_WIDTH = 0xA002; 274 public static final int TAG_EXIF_IMAGE_HEIGHT = 0xA003; 275 public static final int TAG_RELATED_SOUND_FILE = 0xA004; 276 public static final int TAG_FOCAL_PLANE_X_RES = 0xA20E; 277 public static final int TAG_FOCAL_PLANE_Y_RES = 0xA20F; 278 /** 279 * Unit of FocalPlaneXResolution/FocalPlaneYResolution. '1' means no-unit, 280 * '2' inch, '3' centimeter. 281 * 282 * Note: Some of Fujifilm's digicam(e.g.FX2700,FX2900,Finepix4700Z/40i etc) 283 * uses value '3' so it must be 'centimeter', but it seems that they use a 284 * '8.3mm?'(1/3in.?) to their ResolutionUnit. Fuji's BUG? Finepix4900Z has 285 * been changed to use value '2' but it doesn't match to actual value also. 286 */ 287 public static final int TAG_FOCAL_PLANE_UNIT = 0xA210; 288 public static final int TAG_EXPOSURE_INDEX = 0xA215; 289 public static final int TAG_SENSING_METHOD = 0xA217; 290 public static final int TAG_FILE_SOURCE = 0xA300; 291 public static final int TAG_SCENE_TYPE = 0xA301; 292 public static final int TAG_CFA_PATTERN = 0xA302; 293 294 // these tags new with Exif 2.2 (?) [A401 - A4 295 /** 296 * This tag indicates the use of special processing on image data, such as rendering 297 * geared to output. When special processing is performed, the reader is expected to 298 * disable or minimize any further processing. 299 * Tag = 41985 (A401.H) 300 * Type = SHORT 301 * Count = 1 302 * Default = 0 303 * 0 = Normal process 304 * 1 = Custom process 305 * Other = reserved 306 */ 307 public static final int TAG_CUSTOM_RENDERED = 0xA401; 308 309 /** 310 * This tag indicates the exposure mode set when the image was shot. In auto-bracketing 311 * mode, the camera shoots a series of frames of the same scene at different exposure settings. 312 * Tag = 41986 (A402.H) 313 * Type = SHORT 314 * Count = 1 315 * Default = none 316 * 0 = Auto exposure 317 * 1 = Manual exposure 318 * 2 = Auto bracket 319 * Other = reserved 320 */ 321 public static final int TAG_EXPOSURE_MODE = 0xA402; 322 323 /** 324 * This tag indicates the white balance mode set when the image was shot. 325 * Tag = 41987 (A403.H) 326 * Type = SHORT 327 * Count = 1 328 * Default = none 329 * 0 = Auto white balance 330 * 1 = Manual white balance 331 * Other = reserved 332 */ 333 public static final int TAG_WHITE_BALANCE_MODE = 0xA403; 334 335 /** 336 * This tag indicates the digital zoom ratio when the image was shot. If the 337 * numerator of the recorded value is 0, this indicates that digital zoom was 338 * not used. 339 * Tag = 41988 (A404.H) 340 * Type = RATIONAL 341 * Count = 1 342 * Default = none 343 */ 344 public static final int TAG_DIGITAL_ZOOM_RATIO = 0xA404; 345 346 /** 347 * This tag indicates the equivalent focal length assuming a 35mm film camera, 348 * in mm. A value of 0 means the focal length is unknown. Note that this tag 349 * differs from the FocalLength tag. 350 * Tag = 41989 (A405.H) 351 * Type = SHORT 352 * Count = 1 353 * Default = none 354 */ 355 public static final int TAG_35MM_FILM_EQUIV_FOCAL_LENGTH = 0xA405; 356 357 /** 358 * This tag indicates the type of scene that was shot. It can also be used to 359 * record the mode in which the image was shot. Note that this differs from 360 * the scene type (SceneType) tag. 361 * Tag = 41990 (A406.H) 362 * Type = SHORT 363 * Count = 1 364 * Default = 0 365 * 0 = Standard 366 * 1 = Landscape 367 * 2 = Portrait 368 * 3 = Night scene 369 * Other = reserved 370 */ 371 public static final int TAG_SCENE_CAPTURE_TYPE = 0xA406; 372 373 /** 374 * This tag indicates the degree of overall image gain adjustment. 375 * Tag = 41991 (A407.H) 376 * Type = SHORT 377 * Count = 1 378 * Default = none 379 * 0 = None 380 * 1 = Low gain up 381 * 2 = High gain up 382 * 3 = Low gain down 383 * 4 = High gain down 384 * Other = reserved 385 */ 386 public static final int TAG_GAIN_CONTROL = 0xA407; 387 388 /** 389 * This tag indicates the direction of contrast processing applied by the camera 390 * when the image was shot. 391 * Tag = 41992 (A408.H) 392 * Type = SHORT 393 * Count = 1 394 * Default = 0 395 * 0 = Normal 396 * 1 = Soft 397 * 2 = Hard 398 * Other = reserved 399 */ 400 public static final int TAG_CONTRAST = 0xA408; 401 402 /** 403 * This tag indicates the direction of saturation processing applied by the camera 404 * when the image was shot. 405 * Tag = 41993 (A409.H) 406 * Type = SHORT 407 * Count = 1 408 * Default = 0 409 * 0 = Normal 410 * 1 = Low saturation 411 * 2 = High saturation 412 * Other = reserved 413 */ 414 public static final int TAG_SATURATION = 0xA409; 415 416 /** 417 * This tag indicates the direction of sharpness processing applied by the camera 418 * when the image was shot. 419 * Tag = 41994 (A40A.H) 420 * Type = SHORT 421 * Count = 1 422 * Default = 0 423 * 0 = Normal 424 * 1 = Soft 425 * 2 = Hard 426 * Other = reserved 427 */ 428 public static final int TAG_SHARPNESS = 0xA40A; 429 430 // TODO support this tag (I haven't seen a camera's actual implementation of this yet) 431 432 /** 433 * This tag indicates information on the picture-taking conditions of a particular 434 * camera model. The tag is used only to indicate the picture-taking conditions in 435 * the reader. 436 * Tag = 41995 (A40B.H) 437 * Type = UNDEFINED 438 * Count = Any 439 * Default = none 440 * 441 * The information is recorded in the format shown below. The data is recorded 442 * in Unicode using SHORT type for the number of display rows and columns and 443 * UNDEFINED type for the camera settings. The Unicode (UCS-2) string including 444 * Signature is NULL terminated. The specifics of the Unicode string are as given 445 * in ISO/IEC 10464-1. 446 * 447 * Length Type Meaning 448 * ------+-----------+------------------ 449 * 2 SHORT Display columns 450 * 2 SHORT Display rows 451 * Any UNDEFINED Camera setting-1 452 * Any UNDEFINED Camera setting-2 453 * : : : 454 * Any UNDEFINED Camera setting-n 455 */ 456 public static final int TAG_DEVICE_SETTING_DESCRIPTION = 0xA40B; 457 458 /** 459 * This tag indicates the distance to the subject. 460 * Tag = 41996 (A40C.H) 461 * Type = SHORT 462 * Count = 1 463 * Default = none 464 * 0 = unknown 465 * 1 = Macro 466 * 2 = Close view 467 * 3 = Distant view 468 * Other = reserved 469 */ 470 public static final int TAG_SUBJECT_DISTANCE_RANGE = 0xA40C; 471 472 /** 473 * This tag indicates an identifier assigned uniquely to each image. It is 474 * recorded as an ASCII string equivalent to hexadecimal notation and 128-bit 475 * fixed length. 476 * Tag = 42016 (A420.H) 477 * Type = ASCII 478 * Count = 33 479 * Default = none 480 */ 481 public static final int TAG_IMAGE_UNIQUE_ID = 0xA420; 482 483 /** String. */ 484 public static final int TAG_CAMERA_OWNER_NAME = 0xA430; 485 /** String. */ 486 public static final int TAG_BODY_SERIAL_NUMBER = 0xA431; 487 /** An array of four Rational64u numbers giving focal and aperture ranges. */ 488 public static final int TAG_LENS_SPECIFICATION = 0xA432; 489 /** String. */ 490 public static final int TAG_LENS_MAKE = 0xA433; 491 /** String. */ 492 public static final int TAG_LENS_MODEL = 0xA434; 493 /** String. */ 494 public static final int TAG_LENS_SERIAL_NUMBER = 0xA435; 495 /** Rational64u. */ 496 public static final int TAG_GAMMA = 0xA500; 497 498 public static final int TAG_LENS = 0xFDEA; 499 500 @NotNull 501 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 502 503 static 504 { 505 _tagNameMap.put(TAG_FILL_ORDER, "Fill Order"); 506 _tagNameMap.put(TAG_DOCUMENT_NAME, "Document Name"); 507 // TODO why don't these tags have fields associated with them? 508 _tagNameMap.put(0x1000, "Related Image File Format"); 509 _tagNameMap.put(0x1001, "Related Image Width"); 510 _tagNameMap.put(0x1002, "Related Image Length"); 511 _tagNameMap.put(0x0156, "Transfer Range"); 512 _tagNameMap.put(0x0200, "JPEG Proc"); 513 _tagNameMap.put(TAG_COMPRESSED_AVERAGE_BITS_PER_PIXEL, "Compressed Bits Per Pixel"); 514 _tagNameMap.put(0x927C, "Maker Note"); 515 _tagNameMap.put(0xA005, "Interoperability Offset"); 516 517 _tagNameMap.put(TAG_NEW_SUBFILE_TYPE, "New Subfile Type"); 518 _tagNameMap.put(TAG_SUBFILE_TYPE, "Subfile Type"); 519 _tagNameMap.put(TAG_BITS_PER_SAMPLE, "Bits Per Sample"); 520 _tagNameMap.put(TAG_PHOTOMETRIC_INTERPRETATION, "Photometric Interpretation"); 521 _tagNameMap.put(TAG_THRESHOLDING, "Thresholding"); 522 _tagNameMap.put(TAG_STRIP_OFFSETS, "Strip Offsets"); 523 _tagNameMap.put(TAG_SAMPLES_PER_PIXEL, "Samples Per Pixel"); 524 _tagNameMap.put(TAG_ROWS_PER_STRIP, "Rows Per Strip"); 525 _tagNameMap.put(TAG_STRIP_BYTE_COUNTS, "Strip Byte Counts"); 526 _tagNameMap.put(TAG_PAGE_NAME, "Page Name"); 527 _tagNameMap.put(TAG_PLANAR_CONFIGURATION, "Planar Configuration"); 528 _tagNameMap.put(TAG_TRANSFER_FUNCTION, "Transfer Function"); 529 _tagNameMap.put(TAG_PREDICTOR, "Predictor"); 530 _tagNameMap.put(TAG_TILE_WIDTH, "Tile Width"); 531 _tagNameMap.put(TAG_TILE_LENGTH, "Tile Length"); 532 _tagNameMap.put(TAG_TILE_OFFSETS, "Tile Offsets"); 533 _tagNameMap.put(TAG_TILE_BYTE_COUNTS, "Tile Byte Counts"); 534 _tagNameMap.put(TAG_JPEG_TABLES, "JPEG Tables"); 535 _tagNameMap.put(TAG_YCBCR_SUBSAMPLING, "YCbCr Sub-Sampling"); 536 _tagNameMap.put(TAG_CFA_REPEAT_PATTERN_DIM, "CFA Repeat Pattern Dim"); 537 _tagNameMap.put(TAG_CFA_PATTERN_2, "CFA Pattern"); 538 _tagNameMap.put(TAG_BATTERY_LEVEL, "Battery Level"); 539 _tagNameMap.put(TAG_EXPOSURE_TIME, "Exposure Time"); 540 _tagNameMap.put(TAG_FNUMBER, "F-Number"); 541 _tagNameMap.put(TAG_IPTC_NAA, "IPTC/NAA"); 542 _tagNameMap.put(TAG_INTER_COLOR_PROFILE, "Inter Color Profile"); 543 _tagNameMap.put(TAG_EXPOSURE_PROGRAM, "Exposure Program"); 544 _tagNameMap.put(TAG_SPECTRAL_SENSITIVITY, "Spectral Sensitivity"); 545 _tagNameMap.put(TAG_ISO_EQUIVALENT, "ISO Speed Ratings"); 546 _tagNameMap.put(TAG_OPTO_ELECTRIC_CONVERSION_FUNCTION, "Opto-electric Conversion Function (OECF)"); 547 _tagNameMap.put(TAG_INTERLACE, "Interlace"); 548 _tagNameMap.put(TAG_TIME_ZONE_OFFSET, "Time Zone Offset"); 549 _tagNameMap.put(TAG_SELF_TIMER_MODE, "Self Timer Mode"); 550 _tagNameMap.put(TAG_EXIF_VERSION, "Exif Version"); 551 _tagNameMap.put(TAG_DATETIME_ORIGINAL, "Date/Time Original"); 552 _tagNameMap.put(TAG_DATETIME_DIGITIZED, "Date/Time Digitized"); 553 _tagNameMap.put(TAG_COMPONENTS_CONFIGURATION, "Components Configuration"); 554 _tagNameMap.put(TAG_SHUTTER_SPEED, "Shutter Speed Value"); 555 _tagNameMap.put(TAG_APERTURE, "Aperture Value"); 556 _tagNameMap.put(TAG_BRIGHTNESS_VALUE, "Brightness Value"); 557 _tagNameMap.put(TAG_EXPOSURE_BIAS, "Exposure Bias Value"); 558 _tagNameMap.put(TAG_MAX_APERTURE, "Max Aperture Value"); 559 _tagNameMap.put(TAG_SUBJECT_DISTANCE, "Subject Distance"); 560 _tagNameMap.put(TAG_METERING_MODE, "Metering Mode"); 561 _tagNameMap.put(TAG_LIGHT_SOURCE, "Light Source"); 562 _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance"); 563 _tagNameMap.put(TAG_FLASH, "Flash"); 564 _tagNameMap.put(TAG_FOCAL_LENGTH, "Focal Length"); 565 _tagNameMap.put(TAG_FLASH_ENERGY, "Flash Energy"); 566 _tagNameMap.put(TAG_SPATIAL_FREQ_RESPONSE, "Spatial Frequency Response"); 567 _tagNameMap.put(TAG_NOISE, "Noise"); 568 _tagNameMap.put(TAG_IMAGE_NUMBER, "Image Number"); 569 _tagNameMap.put(TAG_SECURITY_CLASSIFICATION, "Security Classification"); 570 _tagNameMap.put(TAG_IMAGE_HISTORY, "Image History"); 571 _tagNameMap.put(TAG_SUBJECT_LOCATION, "Subject Location"); 572 _tagNameMap.put(TAG_EXPOSURE_INDEX, "Exposure Index"); 573 _tagNameMap.put(TAG_TIFF_EP_STANDARD_ID, "TIFF/EP Standard ID"); 574 _tagNameMap.put(TAG_USER_COMMENT, "User Comment"); 575 _tagNameMap.put(TAG_SUBSECOND_TIME, "Sub-Sec Time"); 576 _tagNameMap.put(TAG_SUBSECOND_TIME_ORIGINAL, "Sub-Sec Time Original"); 577 _tagNameMap.put(TAG_SUBSECOND_TIME_DIGITIZED, "Sub-Sec Time Digitized"); 578 _tagNameMap.put(TAG_FLASHPIX_VERSION, "FlashPix Version"); 579 _tagNameMap.put(TAG_COLOR_SPACE, "Color Space"); 580 _tagNameMap.put(TAG_EXIF_IMAGE_WIDTH, "Exif Image Width"); 581 _tagNameMap.put(TAG_EXIF_IMAGE_HEIGHT, "Exif Image Height"); 582 _tagNameMap.put(TAG_RELATED_SOUND_FILE, "Related Sound File"); 583 // 0x920B in TIFF/EP 584 _tagNameMap.put(TAG_FLASH_ENERGY_2, "Flash Energy"); 585 // 0x920C in TIFF/EP 586 _tagNameMap.put(TAG_SPATIAL_FREQ_RESPONSE_2, "Spatial Frequency Response"); 587 // 0x920E in TIFF/EP 588 _tagNameMap.put(TAG_FOCAL_PLANE_X_RES, "Focal Plane X Resolution"); 589 // 0x920F in TIFF/EP 590 _tagNameMap.put(TAG_FOCAL_PLANE_Y_RES, "Focal Plane Y Resolution"); 591 // 0x9210 in TIFF/EP 592 _tagNameMap.put(TAG_FOCAL_PLANE_UNIT, "Focal Plane Resolution Unit"); 593 // 0x9214 in TIFF/EP 594 _tagNameMap.put(TAG_SUBJECT_LOCATION_2, "Subject Location"); 595 // 0x9215 in TIFF/EP 596 _tagNameMap.put(TAG_EXPOSURE_INDEX_2, "Exposure Index"); 597 // 0x9217 in TIFF/EP 598 _tagNameMap.put(TAG_SENSING_METHOD, "Sensing Method"); 599 _tagNameMap.put(TAG_FILE_SOURCE, "File Source"); 600 _tagNameMap.put(TAG_SCENE_TYPE, "Scene Type"); 601 _tagNameMap.put(TAG_CFA_PATTERN, "CFA Pattern"); 602 603 _tagNameMap.put(TAG_CUSTOM_RENDERED, "Custom Rendered"); 604 _tagNameMap.put(TAG_EXPOSURE_MODE, "Exposure Mode"); 605 _tagNameMap.put(TAG_WHITE_BALANCE_MODE, "White Balance Mode"); 606 _tagNameMap.put(TAG_DIGITAL_ZOOM_RATIO, "Digital Zoom Ratio"); 607 _tagNameMap.put(TAG_35MM_FILM_EQUIV_FOCAL_LENGTH, "Focal Length 35"); 608 _tagNameMap.put(TAG_SCENE_CAPTURE_TYPE, "Scene Capture Type"); 609 _tagNameMap.put(TAG_GAIN_CONTROL, "Gain Control"); 610 _tagNameMap.put(TAG_CONTRAST, "Contrast"); 611 _tagNameMap.put(TAG_SATURATION, "Saturation"); 612 _tagNameMap.put(TAG_SHARPNESS, "Sharpness"); 613 _tagNameMap.put(TAG_DEVICE_SETTING_DESCRIPTION, "Device Setting Description"); 614 _tagNameMap.put(TAG_SUBJECT_DISTANCE_RANGE, "Subject Distance Range"); 615 _tagNameMap.put(TAG_IMAGE_UNIQUE_ID, "Unique Image ID"); 616 617 _tagNameMap.put(TAG_CAMERA_OWNER_NAME, "Camera Owner Name"); 618 _tagNameMap.put(TAG_BODY_SERIAL_NUMBER, "Body Serial Number"); 619 _tagNameMap.put(TAG_LENS_SPECIFICATION, "Lens Specification"); 620 _tagNameMap.put(TAG_LENS_MAKE, "Lens Make"); 621 _tagNameMap.put(TAG_LENS_MODEL, "Lens Model"); 622 _tagNameMap.put(TAG_LENS_SERIAL_NUMBER, "Lens Serial Number"); 623 _tagNameMap.put(TAG_GAMMA, "Gamma"); 624 625 _tagNameMap.put(TAG_MIN_SAMPLE_VALUE, "Minimum sample value"); 626 _tagNameMap.put(TAG_MAX_SAMPLE_VALUE, "Maximum sample value"); 627 628 _tagNameMap.put(TAG_LENS, "Lens"); 629 } 630 631 public ExifSubIFDDirectory() 632 { 633 this.setDescriptor(new ExifSubIFDDescriptor(this)); 634 } 635 636 @NotNull 637 public String getName() 638 { 639 return "Exif SubIFD"; 640 } 641 642 @NotNull 643 protected HashMap<Integer, String> getTagNameMap() 644 { 645 return _tagNameMap; 646 } 647 } -
src/com/drew/metadata/exif/ExifSubIFDDirectory.java
-
src/com/drew/metadata/exif/ExifThumbnailDescriptor.java
Modification de propriétés sur src/com/drew/metadata/exif/ExifSubIFDDirectory.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
1 /* 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/ 20 */ 21 22 package com.drew.metadata.exif; 23 24 import com.drew.lang.Rational; 25 import com.drew.lang.annotations.NotNull; 26 import com.drew.lang.annotations.Nullable; 27 import com.drew.metadata.TagDescriptor; 28 29 /** 30 * Provides human-readable string representations of tag values stored in a <code>ExifThumbnailDirectory</code>. 31 * 32 * @author Drew Noakes http://drewnoakes.com 33 */ 34 public class ExifThumbnailDescriptor extends TagDescriptor<ExifThumbnailDirectory> 35 { 36 /** 37 * Dictates whether rational values will be represented in decimal format in instances 38 * where decimal notation is elegant (such as 1/2 -> 0.5, but not 1/3). 39 */ 40 private final boolean _allowDecimalRepresentationOfRationals = true; 41 42 public ExifThumbnailDescriptor(@NotNull ExifThumbnailDirectory directory) 43 { 44 super(directory); 45 } 46 47 // Note for the potential addition of brightness presentation in eV: 48 // Brightness of taken subject. To calculate Exposure(Ev) from BrightnessValue(Bv), 49 // you must add SensitivityValue(Sv). 50 // Ev=BV+Sv Sv=log2(ISOSpeedRating/3.125) 51 // ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32. 52 53 /** 54 * Returns a descriptive value of the the specified tag for this image. 55 * Where possible, known values will be substituted here in place of the raw 56 * tokens actually kept in the Exif segment. If no substitution is 57 * available, the value provided by getString(int) will be returned. 58 * @param tagType the tag to find a description for 59 * @return a description of the image's value for the specified tag, or 60 * <code>null</code> if the tag hasn't been defined. 61 */ 62 @Nullable 63 public String getDescription(int tagType) 64 { 65 switch (tagType) { 66 case ExifThumbnailDirectory.TAG_ORIENTATION: 67 return getOrientationDescription(); 68 case ExifThumbnailDirectory.TAG_RESOLUTION_UNIT: 69 return getResolutionDescription(); 70 case ExifThumbnailDirectory.TAG_YCBCR_POSITIONING: 71 return getYCbCrPositioningDescription(); 72 case ExifThumbnailDirectory.TAG_X_RESOLUTION: 73 return getXResolutionDescription(); 74 case ExifThumbnailDirectory.TAG_Y_RESOLUTION: 75 return getYResolutionDescription(); 76 case ExifThumbnailDirectory.TAG_THUMBNAIL_OFFSET: 77 return getThumbnailOffsetDescription(); 78 case ExifThumbnailDirectory.TAG_THUMBNAIL_LENGTH: 79 return getThumbnailLengthDescription(); 80 case ExifThumbnailDirectory.TAG_THUMBNAIL_IMAGE_WIDTH: 81 return getThumbnailImageWidthDescription(); 82 case ExifThumbnailDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT: 83 return getThumbnailImageHeightDescription(); 84 case ExifThumbnailDirectory.TAG_BITS_PER_SAMPLE: 85 return getBitsPerSampleDescription(); 86 case ExifThumbnailDirectory.TAG_THUMBNAIL_COMPRESSION: 87 return getCompressionDescription(); 88 case ExifThumbnailDirectory.TAG_PHOTOMETRIC_INTERPRETATION: 89 return getPhotometricInterpretationDescription(); 90 case ExifThumbnailDirectory.TAG_ROWS_PER_STRIP: 91 return getRowsPerStripDescription(); 92 case ExifThumbnailDirectory.TAG_STRIP_BYTE_COUNTS: 93 return getStripByteCountsDescription(); 94 case ExifThumbnailDirectory.TAG_SAMPLES_PER_PIXEL: 95 return getSamplesPerPixelDescription(); 96 case ExifThumbnailDirectory.TAG_PLANAR_CONFIGURATION: 97 return getPlanarConfigurationDescription(); 98 case ExifThumbnailDirectory.TAG_YCBCR_SUBSAMPLING: 99 return getYCbCrSubsamplingDescription(); 100 case ExifThumbnailDirectory.TAG_REFERENCE_BLACK_WHITE: 101 return getReferenceBlackWhiteDescription(); 102 default: 103 return super.getDescription(tagType); 104 } 105 } 106 107 @Nullable 108 public String getReferenceBlackWhiteDescription() 109 { 110 int[] ints = _directory.getIntArray(ExifThumbnailDirectory.TAG_REFERENCE_BLACK_WHITE); 111 if (ints==null) 112 return null; 113 int blackR = ints[0]; 114 int whiteR = ints[1]; 115 int blackG = ints[2]; 116 int whiteG = ints[3]; 117 int blackB = ints[4]; 118 int whiteB = ints[5]; 119 return "[" + blackR + "," + blackG + "," + blackB + "] " + 120 "[" + whiteR + "," + whiteG + "," + whiteB + "]"; 121 } 122 123 @Nullable 124 public String getYCbCrSubsamplingDescription() 125 { 126 int[] positions = _directory.getIntArray(ExifThumbnailDirectory.TAG_YCBCR_SUBSAMPLING); 127 if (positions==null || positions.length < 2) 128 return null; 129 if (positions[0] == 2 && positions[1] == 1) { 130 return "YCbCr4:2:2"; 131 } else if (positions[0] == 2 && positions[1] == 2) { 132 return "YCbCr4:2:0"; 133 } else { 134 return "(Unknown)"; 135 } 136 } 137 138 @Nullable 139 public String getPlanarConfigurationDescription() 140 { 141 // When image format is no compression YCbCr, this value shows byte aligns of YCbCr 142 // data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for each subsampling 143 // pixel. If value is '2', Y/Cb/Cr value is separated and stored to Y plane/Cb plane/Cr 144 // plane format. 145 Integer value = _directory.getInteger(ExifThumbnailDirectory.TAG_PLANAR_CONFIGURATION); 146 if (value==null) 147 return null; 148 switch (value) { 149 case 1: return "Chunky (contiguous for each subsampling pixel)"; 150 case 2: return "Separate (Y-plane/Cb-plane/Cr-plane format)"; 151 default: 152 return "Unknown configuration"; 153 } 154 } 155 156 @Nullable 157 public String getSamplesPerPixelDescription() 158 { 159 String value = _directory.getString(ExifThumbnailDirectory.TAG_SAMPLES_PER_PIXEL); 160 return value==null ? null : value + " samples/pixel"; 161 } 162 163 @Nullable 164 public String getRowsPerStripDescription() 165 { 166 final String value = _directory.getString(ExifThumbnailDirectory.TAG_ROWS_PER_STRIP); 167 return value==null ? null : value + " rows/strip"; 168 } 169 170 @Nullable 171 public String getStripByteCountsDescription() 172 { 173 final String value = _directory.getString(ExifThumbnailDirectory.TAG_STRIP_BYTE_COUNTS); 174 return value==null ? null : value + " bytes"; 175 } 176 177 @Nullable 178 public String getPhotometricInterpretationDescription() 179 { 180 // Shows the color space of the image data components 181 Integer value = _directory.getInteger(ExifThumbnailDirectory.TAG_PHOTOMETRIC_INTERPRETATION); 182 if (value==null) 183 return null; 184 switch (value) { 185 case 0: return "WhiteIsZero"; 186 case 1: return "BlackIsZero"; 187 case 2: return "RGB"; 188 case 3: return "RGB Palette"; 189 case 4: return "Transparency Mask"; 190 case 5: return "CMYK"; 191 case 6: return "YCbCr"; 192 case 8: return "CIELab"; 193 case 9: return "ICCLab"; 194 case 10: return "ITULab"; 195 case 32803: return "Color Filter Array"; 196 case 32844: return "Pixar LogL"; 197 case 32845: return "Pixar LogLuv"; 198 case 32892: return "Linear Raw"; 199 default: 200 return "Unknown colour space"; 201 } 202 } 203 204 @Nullable 205 public String getCompressionDescription() 206 { 207 Integer value = _directory.getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_COMPRESSION); 208 if (value==null) 209 return null; 210 switch (value) { 211 case 1: return "Uncompressed"; 212 case 2: return "CCITT 1D"; 213 case 3: return "T4/Group 3 Fax"; 214 case 4: return "T6/Group 4 Fax"; 215 case 5: return "LZW"; 216 case 6: return "JPEG (old-style)"; 217 case 7: return "JPEG"; 218 case 8: return "Adobe Deflate"; 219 case 9: return "JBIG B&W"; 220 case 10: return "JBIG Color"; 221 case 32766: return "Next"; 222 case 32771: return "CCIRLEW"; 223 case 32773: return "PackBits"; 224 case 32809: return "Thunderscan"; 225 case 32895: return "IT8CTPAD"; 226 case 32896: return "IT8LW"; 227 case 32897: return "IT8MP"; 228 case 32898: return "IT8BL"; 229 case 32908: return "PixarFilm"; 230 case 32909: return "PixarLog"; 231 case 32946: return "Deflate"; 232 case 32947: return "DCS"; 233 case 32661: return "JBIG"; 234 case 32676: return "SGILog"; 235 case 32677: return "SGILog24"; 236 case 32712: return "JPEG 2000"; 237 case 32713: return "Nikon NEF Compressed"; 238 default: 239 return "Unknown compression"; 240 } 241 } 242 243 @Nullable 244 public String getBitsPerSampleDescription() 245 { 246 String value = _directory.getString(ExifThumbnailDirectory.TAG_BITS_PER_SAMPLE); 247 return value==null ? null : value + " bits/component/pixel"; 248 } 249 250 @Nullable 251 public String getThumbnailImageWidthDescription() 252 { 253 String value = _directory.getString(ExifThumbnailDirectory.TAG_THUMBNAIL_IMAGE_WIDTH); 254 return value==null ? null : value + " pixels"; 255 } 256 257 @Nullable 258 public String getThumbnailImageHeightDescription() 259 { 260 String value = _directory.getString(ExifThumbnailDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT); 261 return value==null ? null : value + " pixels"; 262 } 263 264 @Nullable 265 public String getThumbnailLengthDescription() 266 { 267 String value = _directory.getString(ExifThumbnailDirectory.TAG_THUMBNAIL_LENGTH); 268 return value==null ? null : value + " bytes"; 269 } 270 271 @Nullable 272 public String getThumbnailOffsetDescription() 273 { 274 String value = _directory.getString(ExifThumbnailDirectory.TAG_THUMBNAIL_OFFSET); 275 return value==null ? null : value + " bytes"; 276 } 277 278 @Nullable 279 public String getYResolutionDescription() 280 { 281 Rational value = _directory.getRational(ExifThumbnailDirectory.TAG_Y_RESOLUTION); 282 if (value==null) 283 return null; 284 final String unit = getResolutionDescription(); 285 return value.toSimpleString(_allowDecimalRepresentationOfRationals) + 286 " dots per " + 287 (unit==null ? "unit" : unit.toLowerCase()); 288 } 289 290 @Nullable 291 public String getXResolutionDescription() 292 { 293 Rational value = _directory.getRational(ExifThumbnailDirectory.TAG_X_RESOLUTION); 294 if (value==null) 295 return null; 296 final String unit = getResolutionDescription(); 297 return value.toSimpleString(_allowDecimalRepresentationOfRationals) + 298 " dots per " + 299 (unit==null ? "unit" : unit.toLowerCase()); 300 } 301 302 @Nullable 303 public String getYCbCrPositioningDescription() 304 { 305 Integer value = _directory.getInteger(ExifThumbnailDirectory.TAG_YCBCR_POSITIONING); 306 if (value==null) 307 return null; 308 switch (value) { 309 case 1: return "Center of pixel array"; 310 case 2: return "Datum point"; 311 default: 312 return String.valueOf(value); 313 } 314 } 315 316 @Nullable 317 public String getOrientationDescription() 318 { 319 Integer value = _directory.getInteger(ExifThumbnailDirectory.TAG_ORIENTATION); 320 if (value==null) 321 return null; 322 switch (value) { 323 case 1: return "Top, left side (Horizontal / normal)"; 324 case 2: return "Top, right side (Mirror horizontal)"; 325 case 3: return "Bottom, right side (Rotate 180)"; 326 case 4: return "Bottom, left side (Mirror vertical)"; 327 case 5: return "Left side, top (Mirror horizontal and rotate 270 CW)"; 328 case 6: return "Right side, top (Rotate 90 CW)"; 329 case 7: return "Right side, bottom (Mirror horizontal and rotate 90 CW)"; 330 case 8: return "Left side, bottom (Rotate 270 CW)"; 331 default: 332 return String.valueOf(value); 333 } 334 } 335 336 @Nullable 337 public String getResolutionDescription() 338 { 339 // '1' means no-unit, '2' means inch, '3' means centimeter. Default value is '2'(inch) 340 Integer value = _directory.getInteger(ExifThumbnailDirectory.TAG_RESOLUTION_UNIT); 341 if (value==null) 342 return null; 343 switch (value) { 344 case 1: return "(No unit)"; 345 case 2: return "Inch"; 346 case 3: return "cm"; 347 default: 348 return ""; 349 } 350 } 351 } -
src/com/drew/metadata/exif/ExifThumbnailDescriptor.java
-
src/com/drew/metadata/exif/ExifThumbnailDirectory.java
Modification de propriétés sur src/com/drew/metadata/exif/ExifThumbnailDescriptor.java ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
1 /* 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/ 20 */ 21 22 package com.drew.metadata.exif; 23 24 import com.drew.lang.annotations.NotNull; 25 import com.drew.lang.annotations.Nullable; 26 import com.drew.metadata.Directory; 27 import com.drew.metadata.MetadataException; 28 29 import java.io.FileOutputStream; 30 import java.io.IOException; 31 import java.util.HashMap; 32 33 /** 34 * One of several Exif directories. Otherwise known as IFD1, this directory holds information about an embedded thumbnail image. 35 * 36 * @author Drew Noakes http://drewnoakes.com 37 */ 38 public class ExifThumbnailDirectory extends Directory 39 { 40 public static final int TAG_THUMBNAIL_IMAGE_WIDTH = 0x0100; 41 public static final int TAG_THUMBNAIL_IMAGE_HEIGHT = 0x0101; 42 43 /** 44 * When image format is no compression, this value shows the number of bits 45 * per component for each pixel. Usually this value is '8,8,8'. 46 */ 47 public static final int TAG_BITS_PER_SAMPLE = 0x0102; 48 49 /** 50 * Shows compression method for Thumbnail. 51 * 1 = Uncompressed 52 * 2 = CCITT 1D 53 * 3 = T4/Group 3 Fax 54 * 4 = T6/Group 4 Fax 55 * 5 = LZW 56 * 6 = JPEG (old-style) 57 * 7 = JPEG 58 * 8 = Adobe Deflate 59 * 9 = JBIG B&W 60 * 10 = JBIG Color 61 * 32766 = Next 62 * 32771 = CCIRLEW 63 * 32773 = PackBits 64 * 32809 = Thunderscan 65 * 32895 = IT8CTPAD 66 * 32896 = IT8LW 67 * 32897 = IT8MP 68 * 32898 = IT8BL 69 * 32908 = PixarFilm 70 * 32909 = PixarLog 71 * 32946 = Deflate 72 * 32947 = DCS 73 * 34661 = JBIG 74 * 34676 = SGILog 75 * 34677 = SGILog24 76 * 34712 = JPEG 2000 77 * 34713 = Nikon NEF Compressed 78 */ 79 public static final int TAG_THUMBNAIL_COMPRESSION = 0x0103; 80 81 /** 82 * Shows the color space of the image data components. 83 * 0 = WhiteIsZero 84 * 1 = BlackIsZero 85 * 2 = RGB 86 * 3 = RGB Palette 87 * 4 = Transparency Mask 88 * 5 = CMYK 89 * 6 = YCbCr 90 * 8 = CIELab 91 * 9 = ICCLab 92 * 10 = ITULab 93 * 32803 = Color Filter Array 94 * 32844 = Pixar LogL 95 * 32845 = Pixar LogLuv 96 * 34892 = Linear Raw 97 */ 98 public static final int TAG_PHOTOMETRIC_INTERPRETATION = 0x0106; 99 100 /** The position in the file of raster data. */ 101 public static final int TAG_STRIP_OFFSETS = 0x0111; 102 public static final int TAG_ORIENTATION = 0x0112; 103 /** Each pixel is composed of this many samples. */ 104 public static final int TAG_SAMPLES_PER_PIXEL = 0x0115; 105 /** The raster is codified by a single block of data holding this many rows. */ 106 public static final int TAG_ROWS_PER_STRIP = 0x116; 107 /** The size of the raster data in bytes. */ 108 public static final int TAG_STRIP_BYTE_COUNTS = 0x0117; 109 /** 110 * When image format is no compression YCbCr, this value shows byte aligns of 111 * YCbCr data. If value is '1', Y/Cb/Cr value is chunky format, contiguous for 112 * each subsampling pixel. If value is '2', Y/Cb/Cr value is separated and 113 * stored to Y plane/Cb plane/Cr plane format. 114 */ 115 public static final int TAG_X_RESOLUTION = 0x011A; 116 public static final int TAG_Y_RESOLUTION = 0x011B; 117 public static final int TAG_PLANAR_CONFIGURATION = 0x011C; 118 public static final int TAG_RESOLUTION_UNIT = 0x0128; 119 /** The offset to thumbnail image bytes. */ 120 public static final int TAG_THUMBNAIL_OFFSET = 0x0201; 121 /** The size of the thumbnail image data in bytes. */ 122 public static final int TAG_THUMBNAIL_LENGTH = 0x0202; 123 public static final int TAG_YCBCR_COEFFICIENTS = 0x0211; 124 public static final int TAG_YCBCR_SUBSAMPLING = 0x0212; 125 public static final int TAG_YCBCR_POSITIONING = 0x0213; 126 public static final int TAG_REFERENCE_BLACK_WHITE = 0x0214; 127 128 @NotNull 129 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 130 131 static 132 { 133 _tagNameMap.put(TAG_THUMBNAIL_IMAGE_WIDTH, "Thumbnail Image Width"); 134 _tagNameMap.put(TAG_THUMBNAIL_IMAGE_HEIGHT, "Thumbnail Image Height"); 135 _tagNameMap.put(TAG_BITS_PER_SAMPLE, "Bits Per Sample"); 136 _tagNameMap.put(TAG_THUMBNAIL_COMPRESSION, "Thumbnail Compression"); 137 _tagNameMap.put(TAG_PHOTOMETRIC_INTERPRETATION, "Photometric Interpretation"); 138 _tagNameMap.put(TAG_STRIP_OFFSETS, "Strip Offsets"); 139 _tagNameMap.put(TAG_ORIENTATION, "Orientation"); 140 _tagNameMap.put(TAG_SAMPLES_PER_PIXEL, "Samples Per Pixel"); 141 _tagNameMap.put(TAG_ROWS_PER_STRIP, "Rows Per Strip"); 142 _tagNameMap.put(TAG_STRIP_BYTE_COUNTS, "Strip Byte Counts"); 143 _tagNameMap.put(TAG_X_RESOLUTION, "X Resolution"); 144 _tagNameMap.put(TAG_Y_RESOLUTION, "Y Resolution"); 145 _tagNameMap.put(TAG_PLANAR_CONFIGURATION, "Planar Configuration"); 146 _tagNameMap.put(TAG_RESOLUTION_UNIT, "Resolution Unit"); 147 _tagNameMap.put(TAG_THUMBNAIL_OFFSET, "Thumbnail Offset"); 148 _tagNameMap.put(TAG_THUMBNAIL_LENGTH, "Thumbnail Length"); 149 _tagNameMap.put(TAG_YCBCR_COEFFICIENTS, "YCbCr Coefficients"); 150 _tagNameMap.put(TAG_YCBCR_SUBSAMPLING, "YCbCr Sub-Sampling"); 151 _tagNameMap.put(TAG_YCBCR_POSITIONING, "YCbCr Positioning"); 152 _tagNameMap.put(TAG_REFERENCE_BLACK_WHITE, "Reference Black/White"); 153 } 154 155 @Nullable 156 private byte[] _thumbnailData; 157 158 public ExifThumbnailDirectory() 159 { 160 this.setDescriptor(new ExifThumbnailDescriptor(this)); 161 } 162 163 @NotNull 164 public String getName() 165 { 166 return "Exif Thumbnail"; 167 } 168 169 @NotNull 170 protected HashMap<Integer, String> getTagNameMap() 171 { 172 return _tagNameMap; 173 } 174 175 public boolean hasThumbnailData() 176 { 177 return _thumbnailData != null; 178 } 179 180 @Nullable 181 public byte[] getThumbnailData() 182 { 183 return _thumbnailData; 184 } 185 186 public void setThumbnailData(@Nullable byte[] data) 187 { 188 _thumbnailData = data; 189 } 190 191 public void writeThumbnail(@NotNull String filename) throws MetadataException, IOException 192 { 193 byte[] data = _thumbnailData; 194 195 if (data==null) 196 throw new MetadataException("No thumbnail data exists."); 197 198 FileOutputStream stream = null; 199 try { 200 stream = new FileOutputStream(filename); 201 stream.write(data); 202 } finally { 203 if (stream!=null) 204 stream.close(); 205 } 206 } 207 208 /* 209 // This thumbnail extraction code is not complete, and is included to assist anyone who feels like looking into 210 // it. Please share any progress with the original author, and hence the community. Thanks. 211 212 public Image getThumbnailImage() throws MetadataException 213 { 214 if (!hasThumbnailData()) 215 return null; 216 217 int compression = 0; 218 try { 219 compression = this.getInt(ExifSubIFDDirectory.TAG_COMPRESSION); 220 } catch (Throwable e) { 221 this.addError("Unable to determine thumbnail type " + e.getMessage()); 222 } 223 224 final byte[] thumbnailBytes = getThumbnailData(); 225 226 if (compression == ExifSubIFDDirectory.COMPRESSION_JPEG) 227 { 228 // JPEG Thumbnail 229 // operate directly on thumbnailBytes 230 return decodeBytesAsImage(thumbnailBytes); 231 } 232 else if (compression == ExifSubIFDDirectory.COMPRESSION_NONE) 233 { 234 // uncompressed thumbnail (raw RGB data) 235 if (!this.containsTag(ExifSubIFDDirectory.TAG_PHOTOMETRIC_INTERPRETATION)) 236 return null; 237 238 try 239 { 240 // If the image is RGB format, then convert it to a bitmap 241 final int photometricInterpretation = this.getInt(ExifSubIFDDirectory.TAG_PHOTOMETRIC_INTERPRETATION); 242 if (photometricInterpretation == ExifSubIFDDirectory.PHOTOMETRIC_INTERPRETATION_RGB) 243 { 244 // RGB 245 Image image = createImageFromRawRgb(thumbnailBytes); 246 return image; 247 } 248 else if (photometricInterpretation == ExifSubIFDDirectory.PHOTOMETRIC_INTERPRETATION_YCBCR) 249 { 250 // YCbCr 251 Image image = createImageFromRawYCbCr(thumbnailBytes); 252 return image; 253 } 254 else if (photometricInterpretation == ExifSubIFDDirectory.PHOTOMETRIC_INTERPRETATION_MONOCHROME) 255 { 256 // Monochrome 257 return null; 258 } 259 } catch (Throwable e) { 260 this.addError("Unable to extract thumbnail: " + e.getMessage()); 261 } 262 } 263 return null; 264 } 265 266 /** 267 * Handle the YCbCr thumbnail encoding used by Ricoh RDC4200/4300, Fuji DS-7/300 and DX-5/7/9 cameras. 268 * 269 * At DX-5/7/9, YCbCrSubsampling(0x0212) has values of '2,1', PlanarConfiguration(0x011c) has a value '1'. So the 270 * data align of this image is below. 271 * 272 * Y(0,0),Y(1,0),Cb(0,0),Cr(0,0), Y(2,0),Y(3,0),Cb(2,0),Cr(3.0), Y(4,0),Y(5,0),Cb(4,0),Cr(4,0). . . . 273 * 274 * The numbers in parenthesis are pixel coordinates. DX series' YCbCrCoefficients(0x0211) has values '0.299/0.587/0.114', 275 * ReferenceBlackWhite(0x0214) has values '0,255,128,255,128,255'. Therefore to convert from Y/Cb/Cr to RGB is; 276 * 277 * B(0,0)=(Cb-128)*(2-0.114*2)+Y(0,0) 278 * R(0,0)=(Cr-128)*(2-0.299*2)+Y(0,0) 279 * G(0,0)=(Y(0,0)-0.114*B(0,0)-0.299*R(0,0))/0.587 280 * 281 * Horizontal subsampling is a value '2', so you can calculate B(1,0)/R(1,0)/G(1,0) by using the Y(1,0) and Cr(0,0)/Cb(0,0). 282 * Repeat this conversion by value of ImageWidth(0x0100) and ImageLength(0x0101). 283 * 284 * @param thumbnailBytes 285 * @return 286 * @throws com.drew.metadata.MetadataException 287 * / 288 private Image createImageFromRawYCbCr(byte[] thumbnailBytes) throws MetadataException 289 { 290 /* 291 Y = 0.257R + 0.504G + 0.098B + 16 292 Cb = -0.148R - 0.291G + 0.439B + 128 293 Cr = 0.439R - 0.368G - 0.071B + 128 294 295 G = 1.164(Y-16) - 0.391(Cb-128) - 0.813(Cr-128) 296 R = 1.164(Y-16) + 1.596(Cr-128) 297 B = 1.164(Y-16) + 2.018(Cb-128) 298 299 R, G and B range from 0 to 255. 300 Y ranges from 16 to 235. 301 Cb and Cr range from 16 to 240. 302 303 http://www.faqs.org/faqs/graphics/colorspace-faq/ 304 * / 305 306 int length = thumbnailBytes.length; // this.getInt(ExifSubIFDDirectory.TAG_STRIP_BYTE_COUNTS); 307 final int imageWidth = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_WIDTH); 308 final int imageHeight = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT); 309 // final int headerLength = 54; 310 // byte[] result = new byte[length + headerLength]; 311 // // Add a windows BMP header described: 312 // // http://www.onicos.com/staff/iz/formats/bmp.html 313 // result[0] = 'B'; 314 // result[1] = 'M'; // File Type identifier 315 // result[3] = (byte)(result.length / 256); 316 // result[2] = (byte)result.length; 317 // result[10] = (byte)headerLength; 318 // result[14] = 40; // MS Windows BMP header 319 // result[18] = (byte)imageWidth; 320 // result[22] = (byte)imageHeight; 321 // result[26] = 1; // 1 Plane 322 // result[28] = 24; // Colour depth 323 // result[34] = (byte)length; 324 // result[35] = (byte)(length / 256); 325 326 final BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB); 327 328 // order is YCbCr and image is upside down, bitmaps are BGR 329 //// for (int i = headerLength, dataOffset = length; i<result.length; i += 3, dataOffset -= 3) 330 // { 331 // final int y = thumbnailBytes[dataOffset - 2] & 0xFF; 332 // final int cb = thumbnailBytes[dataOffset - 1] & 0xFF; 333 // final int cr = thumbnailBytes[dataOffset] & 0xFF; 334 // if (y<16 || y>235 || cb<16 || cb>240 || cr<16 || cr>240) 335 // "".toString(); 336 // 337 // int g = (int)(1.164*(y-16) - 0.391*(cb-128) - 0.813*(cr-128)); 338 // int r = (int)(1.164*(y-16) + 1.596*(cr-128)); 339 // int b = (int)(1.164*(y-16) + 2.018*(cb-128)); 340 // 341 //// result[i] = (byte)b; 342 //// result[i + 1] = (byte)g; 343 //// result[i + 2] = (byte)r; 344 // 345 // // TODO compose the image here 346 // image.setRGB(1, 2, 3); 347 // } 348 349 return image; 350 } 351 352 /** 353 * Creates a thumbnail image in (Windows) BMP format from raw RGB data. 354 * @param thumbnailBytes 355 * @return 356 * @throws com.drew.metadata.MetadataException 357 * / 358 private Image createImageFromRawRgb(byte[] thumbnailBytes) throws MetadataException 359 { 360 final int length = thumbnailBytes.length; // this.getInt(ExifSubIFDDirectory.TAG_STRIP_BYTE_COUNTS); 361 final int imageWidth = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_WIDTH); 362 final int imageHeight = this.getInt(ExifSubIFDDirectory.TAG_THUMBNAIL_IMAGE_HEIGHT); 363 // final int headerLength = 54; 364 // final byte[] result = new byte[length + headerLength]; 365 // // Add a windows BMP header described: 366 // // http://www.onicos.com/staff/iz/formats/bmp.html 367 // result[0] = 'B'; 368 // result[1] = 'M'; // File Type identifier 369 // result[3] = (byte)(result.length / 256); 370 // result[2] = (byte)result.length; 371 // result[10] = (byte)headerLength; 372 // result[14] = 40; // MS Windows BMP header 373 // result[18] = (byte)imageWidth; 374 // result[22] = (byte)imageHeight; 375 // result[26] = 1; // 1 Plane 376 // result[28] = 24; // Colour depth 377 // result[34] = (byte)length; 378 // result[35] = (byte)(length / 256); 379 380 final BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB); 381 382 // order is RGB and image is upside down, bitmaps are BGR 383 // for (int i = headerLength, dataOffset = length; i<result.length; i += 3, dataOffset -= 3) 384 // { 385 // byte b = thumbnailBytes[dataOffset - 2]; 386 // byte g = thumbnailBytes[dataOffset - 1]; 387 // byte r = thumbnailBytes[dataOffset]; 388 // 389 // // TODO compose the image here 390 // image.setRGB(1, 2, 3); 391 // } 392 393 return image; 394 } 395 */ 396 } -
src/com/drew/metadata/exif/ExifThumbnailDirectory.java
-
src/com/drew/metadata/exif/FujifilmMakernoteDescriptor.java
Modification de propriétés sur src/com/drew/metadata/exif/ExifThumbnailDirectory.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
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 23 import com.drew.lang.Rational; 20 import com.drew. metadata.Directory;21 import com.drew. metadata.MetadataException;24 import com.drew.lang.annotations.NotNull; 25 import com.drew.lang.annotations.Nullable; 22 26 import com.drew.metadata.TagDescriptor; 23 27 24 28 /** 29 * Provides human-readable string representations of tag values stored in a <code>FujifilmMakernoteDirectory</code>. 30 * <p/> 25 31 * Fujifilm's digicam added the MakerNote tag from the Year2000's model (e.g.Finepix1400, 26 32 * Finepix4700). It uses IFD format and start from ASCII character 'FUJIFILM', and next 4 27 33 * bytes(value 0x000c) points the offset to first IFD entry. Example of actual data 28 34 * structure is shown below. 29 * 35 * <p/> 36 * <pre><code> 30 37 * :0000: 46 55 4A 49 46 49 4C 4D-0C 00 00 00 0F 00 00 00 :0000: FUJIFILM........ 31 38 * :0010: 07 00 04 00 00 00 30 31-33 30 00 10 02 00 08 00 :0010: ......0130...... 32 * 39 * </code></pre> 40 * <p/> 33 41 * There are two big differences to the other manufacturers. 34 42 * - Fujifilm's Exif data uses Motorola align, but MakerNote ignores it and uses Intel 35 43 * align. 36 44 * - The other manufacturer's MakerNote counts the "offset to data" from the first byte 37 45 * of TIFF header (same as the other IFD), but Fujifilm counts it from the first byte 38 46 * of MakerNote itself. 47 * 48 * @author Drew Noakes http://drewnoakes.com 39 49 */ 40 public class FujifilmMakernoteDescriptor extends TagDescriptor 50 public class FujifilmMakernoteDescriptor extends TagDescriptor<FujifilmMakernoteDirectory> 41 51 { 42 public FujifilmMakernoteDescriptor( Directory directory)52 public FujifilmMakernoteDescriptor(@NotNull FujifilmMakernoteDirectory directory) 43 53 { 44 54 super(directory); 45 55 } 46 56 47 public String getDescription(int tagType) throws MetadataException 57 @Nullable 58 public String getDescription(int tagType) 48 59 { 49 60 switch (tagType) { 50 61 case FujifilmMakernoteDirectory.TAG_FUJIFILM_SHARPNESS: 51 62 return getSharpnessDescription(); 52 63 case FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE: 53 64 return getWhiteBalanceDescription(); 54 case FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR :65 case FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR_SATURATION: 55 66 return getColorDescription(); 56 67 case FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE: 57 68 return getToneDescription(); … … 63 74 return getMacroDescription(); 64 75 case FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_MODE: 65 76 return getFocusModeDescription(); 66 case FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCH RO:77 case FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCH: 67 78 return getSlowSyncDescription(); 68 79 case FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE: 69 80 return getPictureModeDescription(); … … 76 87 case FujifilmMakernoteDirectory.TAG_FUJIFILM_AE_WARNING: 77 88 return getAutoExposureWarningDescription(); 78 89 default: 79 return _directory.getString(tagType);90 return super.getDescription(tagType); 80 91 } 81 92 } 82 93 83 public String getAutoExposureWarningDescription() throws MetadataException 94 @Nullable 95 public String getSharpnessDescription() 84 96 { 85 if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_AE_WARNING)) return null; 86 int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_AE_WARNING); 97 final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_SHARPNESS); 98 if (value==null) 99 return null; 87 100 switch (value) { 88 case 0:89 return "AE good";90 101 case 1: 91 return "Over exposed (>1/1000s @ F11)"; 102 return "Softest"; 103 case 2: 104 return "Soft"; 105 case 3: 106 return "Normal"; 107 case 4: 108 return "Hard"; 109 case 5: 110 return "Hardest"; 92 111 default: 93 112 return "Unknown (" + value + ")"; 94 113 } 95 114 } 96 115 97 public String getFocusWarningDescription() throws MetadataException 116 @Nullable 117 public String getWhiteBalanceDescription() 98 118 { 99 if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_WARNING)) return null; 100 int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_WARNING); 119 final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE); 120 if (value==null) 121 return null; 101 122 switch (value) { 102 123 case 0: 103 return "Auto focus good"; 104 case 1: 105 return "Out of focus"; 124 return "Auto"; 125 case 256: 126 return "Daylight"; 127 case 512: 128 return "Cloudy"; 129 case 768: 130 return "DaylightColor-fluorescence"; 131 case 769: 132 return "DaywhiteColor-fluorescence"; 133 case 770: 134 return "White-fluorescence"; 135 case 1024: 136 return "Incandescence"; 137 case 3840: 138 return "Custom white balance"; 106 139 default: 107 140 return "Unknown (" + value + ")"; 108 141 } 109 142 } 110 143 111 public String getBlurWarningDescription() throws MetadataException 144 @Nullable 145 public String getColorDescription() 112 146 { 113 if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_BLUR_WARNING)) return null; 114 int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_BLUR_WARNING); 147 final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR_SATURATION); 148 if (value==null) 149 return null; 115 150 switch (value) { 116 151 case 0: 117 return "No blur warning"; 118 case 1: 119 return "Blur warning"; 152 return "Normal (STD)"; 153 case 256: 154 return "High (HARD)"; 155 case 512: 156 return "Low (ORG)"; 120 157 default: 121 158 return "Unknown (" + value + ")"; 122 159 } 123 160 } 124 161 125 public String getContinuousTakingOrAutoBrackettingDescription() throws MetadataException 162 @Nullable 163 public String getToneDescription() 126 164 { 127 if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING)) return null; 128 int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING); 165 final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE); 166 if (value==null) 167 return null; 129 168 switch (value) { 130 169 case 0: 131 return "Off"; 132 case 1: 133 return "On"; 170 return "Normal (STD)"; 171 case 256: 172 return "High (HARD)"; 173 case 512: 174 return "Low (ORG)"; 134 175 default: 135 176 return "Unknown (" + value + ")"; 136 177 } 137 178 } 138 179 139 public String getPictureModeDescription() throws MetadataException 180 @Nullable 181 public String getFlashModeDescription() 140 182 { 141 if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE)) return null; 142 int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE); 183 final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_MODE); 184 if (value==null) 185 return null; 143 186 switch (value) { 144 187 case 0: 145 188 return "Auto"; 146 189 case 1: 147 return " Portrait scene";190 return "On"; 148 191 case 2: 149 return "Landscape scene"; 150 case 4: 151 return "Sports scene"; 152 case 5: 153 return "Night scene"; 154 case 6: 155 return "Program AE"; 156 case 256: 157 return "Aperture priority AE"; 158 case 512: 159 return "Shutter priority AE"; 160 case 768: 161 return "Manual exposure"; 192 return "Off"; 193 case 3: 194 return "Red-eye reduction"; 162 195 default: 163 196 return "Unknown (" + value + ")"; 164 197 } 165 198 } 166 199 167 public String getSlowSyncDescription() throws MetadataException 200 @Nullable 201 public String getFlashStrengthDescription() 168 202 { 169 if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCHRO)) return null; 170 int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCHRO); 171 switch (value) { 172 case 0: 173 return "Off"; 174 case 1: 175 return "On"; 176 default: 177 return "Unknown (" + value + ")"; 178 } 203 Rational value = _directory.getRational(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_STRENGTH); 204 if (value==null) 205 return null; 206 return value.toSimpleString(false) + " EV (Apex)"; 179 207 } 180 208 181 public String getFocusModeDescription() throws MetadataException 209 @Nullable 210 public String getMacroDescription() 182 211 { 183 if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_MODE)) return null; 184 int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_MODE); 185 switch (value) { 186 case 0: 187 return "Auto focus"; 188 case 1: 189 return "Manual focus"; 190 default: 191 return "Unknown (" + value + ")"; 192 } 212 return getOnOffDescription(FujifilmMakernoteDirectory.TAG_FUJIFILM_MACRO); 193 213 } 194 214 195 public String getMacroDescription() throws MetadataException 215 @Nullable 216 public String getFocusModeDescription() 196 217 { 197 if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_MACRO)) return null; 198 int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_MACRO); 218 final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_MODE); 219 if (value==null) 220 return null; 199 221 switch (value) { 200 222 case 0: 201 return " Off";223 return "Auto focus"; 202 224 case 1: 203 return " On";225 return "Manual focus"; 204 226 default: 205 227 return "Unknown (" + value + ")"; 206 228 } 207 229 } 208 230 209 public String getFlashStrengthDescription() throws MetadataException 231 @Nullable 232 public String getSlowSyncDescription() 210 233 { 211 if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_STRENGTH)) return null; 212 Rational value = _directory.getRational(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_STRENGTH); 213 return value.toSimpleString(false) + " EV (Apex)"; 234 return getOnOffDescription(FujifilmMakernoteDirectory.TAG_FUJIFILM_SLOW_SYNCH); 214 235 } 215 236 216 public String getFlashModeDescription() throws MetadataException 237 @Nullable 238 public String getPictureModeDescription() 217 239 { 218 if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_MODE)) return null; 219 int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_FLASH_MODE); 240 final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_PICTURE_MODE); 241 if (value==null) 242 return null; 220 243 switch (value) { 221 244 case 0: 222 245 return "Auto"; 223 246 case 1: 224 return " On";247 return "Portrait scene"; 225 248 case 2: 226 return "Off"; 227 case 3: 228 return "Red-eye reduction"; 249 return "Landscape scene"; 250 case 4: 251 return "Sports scene"; 252 case 5: 253 return "Night scene"; 254 case 6: 255 return "Program AE"; 256 case 256: 257 return "Aperture priority AE"; 258 case 512: 259 return "Shutter priority AE"; 260 case 768: 261 return "Manual exposure"; 229 262 default: 230 263 return "Unknown (" + value + ")"; 231 264 } 232 265 } 233 266 234 public String getToneDescription() throws MetadataException 267 @Nullable 268 public String getContinuousTakingOrAutoBrackettingDescription() 235 269 { 236 if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE)) return null; 237 int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_TONE); 270 return getOnOffDescription(FujifilmMakernoteDirectory.TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING); 271 } 272 273 @Nullable 274 public String getBlurWarningDescription() 275 { 276 final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_BLUR_WARNING); 277 if (value==null) 278 return null; 238 279 switch (value) { 239 280 case 0: 240 return "Normal (STD)"; 241 case 256: 242 return "High (HARD)"; 243 case 512: 244 return "Low (ORG)"; 281 return "No blur warning"; 282 case 1: 283 return "Blur warning"; 245 284 default: 246 285 return "Unknown (" + value + ")"; 247 286 } 248 287 } 249 288 250 public String getColorDescription() throws MetadataException 289 @Nullable 290 public String getFocusWarningDescription() 251 291 { 252 if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR)) return null; 253 int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_COLOR); 292 final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_FOCUS_WARNING); 293 if (value==null) 294 return null; 254 295 switch (value) { 255 296 case 0: 256 return "Normal (STD)"; 257 case 256: 258 return "High"; 259 case 512: 260 return "Low (ORG)"; 297 return "Auto focus good"; 298 case 1: 299 return "Out of focus"; 261 300 default: 262 301 return "Unknown (" + value + ")"; 263 302 } 264 303 } 265 304 266 public String getWhiteBalanceDescription() throws MetadataException 305 @Nullable 306 public String getAutoExposureWarningDescription() 267 307 { 268 if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE)) return null; 269 int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_WHITE_BALANCE); 308 final Integer value = _directory.getInteger(FujifilmMakernoteDirectory.TAG_FUJIFILM_AE_WARNING); 309 if (value==null) 310 return null; 270 311 switch (value) { 271 312 case 0: 272 return "Auto"; 273 case 256: 274 return "Daylight"; 275 case 512: 276 return "Cloudy"; 277 case 768: 278 return "DaylightColor-fluorescence"; 279 case 769: 280 return "DaywhiteColor-fluorescence"; 281 case 770: 282 return "White-fluorescence"; 283 case 1024: 284 return "Incandenscense"; 285 case 3840: 286 return "Custom white balance"; 313 return "AE good"; 314 case 1: 315 return "Over exposed (>1/1000s @ F11)"; 287 316 default: 288 317 return "Unknown (" + value + ")"; 289 318 } 290 319 } 291 320 292 public String getSharpnessDescription() throws MetadataException 321 322 @Nullable 323 private String getOnOffDescription(final int tagType) 293 324 { 294 if (!_directory.containsTag(FujifilmMakernoteDirectory.TAG_FUJIFILM_SHARPNESS)) return null; 295 int value = _directory.getInt(FujifilmMakernoteDirectory.TAG_FUJIFILM_SHARPNESS); 325 final Integer value = _directory.getInteger(tagType); 326 if (value==null) 327 return null; 296 328 switch (value) { 329 case 0: 330 return "Off"; 297 331 case 1: 298 return "Softest"; 299 case 2: 300 return "Soft"; 301 case 3: 302 return "Normal"; 303 case 4: 304 return "Hard"; 305 case 5: 306 return "Hardest"; 332 return "On"; 307 333 default: 308 334 return "Unknown (" + value + ")"; 309 335 } -
src/com/drew/metadata/exif/FujifilmMakernoteDirectory.java
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 21 26 import java.util.HashMap; 22 27 23 28 /** 29 * Describes tags specific to Fujifilm cameras. 24 30 * 31 * @author Drew Noakes http://drewnoakes.com 25 32 */ 26 33 public class FujifilmMakernoteDirectory extends Directory 27 34 { 28 35 public static final int TAG_FUJIFILM_MAKERNOTE_VERSION = 0x0000; 29 public static final int TAG_FUJIFILM_QUALITY = 0x1000; 30 public static final int TAG_FUJIFILM_SHARPNESS = 0x1001; 31 public static final int TAG_FUJIFILM_WHITE_BALANCE = 0x1002; 32 public static final int TAG_FUJIFILM_COLOR = 0x1003;33 public static final int TAG_FUJIFILM_TONE = 0x1004; 34 public static final int TAG_FUJIFILM_FLASH_MODE = 0x1010; 35 public static final int TAG_FUJIFILM_FLASH_STRENGTH = 0x1011; 36 public static final int TAG_FUJIFILM_MACRO = 0x1020; 37 public static final int TAG_FUJIFILM_FOCUS_MODE = 0x1021; 38 public static final int TAG_FUJIFILM_SLOW_SYNCH RO = 0x1030;39 public static final int TAG_FUJIFILM_PICTURE_MODE = 0x1031; 40 public static final int TAG_FUJIFILM_UNKNOWN_1 = 0x1032; 41 public static final int TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING = 0x1100; 42 public static final int TAG_FUJIFILM_UNKNOWN_2 = 0x1200; 43 public static final int TAG_FUJIFILM_BLUR_WARNING = 0x1300; 44 public static final int TAG_FUJIFILM_FOCUS_WARNING = 0x1301; 45 public static final int TAG_FUJIFILM_AE_WARNING = 0x1302; 36 public static final int TAG_FUJIFILM_QUALITY = 0x1000; // 4096 37 public static final int TAG_FUJIFILM_SHARPNESS = 0x1001; // 4097 38 public static final int TAG_FUJIFILM_WHITE_BALANCE = 0x1002; // 4098 39 public static final int TAG_FUJIFILM_COLOR_SATURATION = 0x1003; // 4099 40 public static final int TAG_FUJIFILM_TONE = 0x1004; // 4100 41 public static final int TAG_FUJIFILM_FLASH_MODE = 0x1010; // 4112 42 public static final int TAG_FUJIFILM_FLASH_STRENGTH = 0x1011; // 4113 43 public static final int TAG_FUJIFILM_MACRO = 0x1020; // 4128 44 public static final int TAG_FUJIFILM_FOCUS_MODE = 0x1021; // 4129 45 public static final int TAG_FUJIFILM_SLOW_SYNCH = 0x1030; // 4144 46 public static final int TAG_FUJIFILM_PICTURE_MODE = 0x1031; // 4145 47 public static final int TAG_FUJIFILM_UNKNOWN_1 = 0x1032; // 4146 48 public static final int TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING = 0x1100; // 4352 49 public static final int TAG_FUJIFILM_UNKNOWN_2 = 0x1200; // 4608 50 public static final int TAG_FUJIFILM_BLUR_WARNING = 0x1300; // 4864 51 public static final int TAG_FUJIFILM_FOCUS_WARNING = 0x1301; // 4865 52 public static final int TAG_FUJIFILM_AE_WARNING = 0x1302; // 4866 46 53 47 protected static final HashMap tagNameMap = new HashMap(); 54 @NotNull 55 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 48 56 49 57 static 50 58 { 51 tagNameMap.put(new Integer(TAG_FUJIFILM_AE_WARNING), "AE Warning");52 tagNameMap.put(new Integer(TAG_FUJIFILM_BLUR_WARNING), "Blur Warning");53 tagNameMap.put(new Integer(TAG_FUJIFILM_COLOR), "Color");54 tagNameMap.put(new Integer(TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING), "Continuous Taking Or Auto Bracketting");55 tagNameMap.put(new Integer(TAG_FUJIFILM_FLASH_MODE), "Flash Mode");56 tagNameMap.put(new Integer(TAG_FUJIFILM_FLASH_STRENGTH), "Flash Strength");57 tagNameMap.put(new Integer(TAG_FUJIFILM_FOCUS_MODE), "FocusMode");58 tagNameMap.put(new Integer(TAG_FUJIFILM_FOCUS_WARNING), "Focus Warning");59 tagNameMap.put(new Integer(TAG_FUJIFILM_MACRO), "Macro");60 tagNameMap.put(new Integer(TAG_FUJIFILM_MAKERNOTE_VERSION), "Makernote Version");61 tagNameMap.put(new Integer(TAG_FUJIFILM_PICTURE_MODE), "Picture Mode");62 tagNameMap.put(new Integer(TAG_FUJIFILM_QUALITY), "Quality");63 tagNameMap.put(new Integer(TAG_FUJIFILM_SHARPNESS), "Sharpness");64 tagNameMap.put(new Integer(TAG_FUJIFILM_SLOW_SYNCHRO), "Slow Synchro");65 tagNameMap.put(new Integer(TAG_FUJIFILM_TONE), "Tone");66 tagNameMap.put(new Integer(TAG_FUJIFILM_UNKNOWN_1), "Makernote Unknown 1");67 tagNameMap.put(new Integer(TAG_FUJIFILM_UNKNOWN_2), "Makernote Unknown 2");68 tagNameMap.put(new Integer(TAG_FUJIFILM_WHITE_BALANCE), "White Balance");59 _tagNameMap.put(TAG_FUJIFILM_MAKERNOTE_VERSION, "Makernote Version"); 60 _tagNameMap.put(TAG_FUJIFILM_QUALITY, "Quality"); 61 _tagNameMap.put(TAG_FUJIFILM_SHARPNESS, "Sharpness"); 62 _tagNameMap.put(TAG_FUJIFILM_WHITE_BALANCE, "White Balance"); 63 _tagNameMap.put(TAG_FUJIFILM_COLOR_SATURATION, "Color Saturation"); 64 _tagNameMap.put(TAG_FUJIFILM_TONE, "Tone (Contrast)"); 65 _tagNameMap.put(TAG_FUJIFILM_FLASH_MODE, "Flash Mode"); 66 _tagNameMap.put(TAG_FUJIFILM_FLASH_STRENGTH, "Flash Strength"); 67 _tagNameMap.put(TAG_FUJIFILM_MACRO, "Macro"); 68 _tagNameMap.put(TAG_FUJIFILM_FOCUS_MODE, "Focus Mode"); 69 _tagNameMap.put(TAG_FUJIFILM_SLOW_SYNCH, "Slow Synch"); 70 _tagNameMap.put(TAG_FUJIFILM_PICTURE_MODE, "Picture Mode"); 71 _tagNameMap.put(TAG_FUJIFILM_UNKNOWN_1, "Makernote Unknown 1"); 72 _tagNameMap.put(TAG_FUJIFILM_CONTINUOUS_TAKING_OR_AUTO_BRACKETTING, "Continuous Taking Or Auto Bracketting"); 73 _tagNameMap.put(TAG_FUJIFILM_UNKNOWN_2, "Makernote Unknown 2"); 74 _tagNameMap.put(TAG_FUJIFILM_BLUR_WARNING, "Blur Warning"); 75 _tagNameMap.put(TAG_FUJIFILM_FOCUS_WARNING, "Focus Warning"); 76 _tagNameMap.put(TAG_FUJIFILM_AE_WARNING, "AE Warning"); 69 77 } 70 78 71 79 public FujifilmMakernoteDirectory() … … 73 81 this.setDescriptor(new FujifilmMakernoteDescriptor(this)); 74 82 } 75 83 84 @NotNull 76 85 public String getName() 77 86 { 78 87 return "FujiFilm Makernote"; 79 88 } 80 89 81 protected HashMap getTagNameMap() 90 @NotNull 91 protected HashMap<Integer, String> getTagNameMap() 82 92 { 83 return tagNameMap;93 return _tagNameMap; 84 94 } 85 95 } -
src/com/drew/metadata/exif/GpsDescriptor.java
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 22:27:52 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.GeoLocation; 19 24 import com.drew.lang.Rational; 20 import com.drew. metadata.Directory;21 import com.drew. metadata.MetadataException;25 import com.drew.lang.annotations.NotNull; 26 import com.drew.lang.annotations.Nullable; 22 27 import com.drew.metadata.TagDescriptor; 23 28 29 import java.text.DecimalFormat; 30 24 31 /** 32 * Provides human-readable string representations of tag values stored in a <code>GpsDirectory</code>. 25 33 * 34 * @author Drew Noakes http://drewnoakes.com 26 35 */ 27 public class GpsDescriptor extends TagDescriptor 36 public class GpsDescriptor extends TagDescriptor<GpsDirectory> 28 37 { 29 public GpsDescriptor( Directory directory)38 public GpsDescriptor(@NotNull GpsDirectory directory) 30 39 { 31 40 super(directory); 32 41 } 33 42 34 public String getDescription(int tagType) throws MetadataException 43 @Nullable 44 public String getDescription(int tagType) 35 45 { 36 46 switch (tagType) { 47 case GpsDirectory.TAG_GPS_VERSION_ID: 48 return getGpsVersionIdDescription(); 37 49 case GpsDirectory.TAG_GPS_ALTITUDE: 38 50 return getGpsAltitudeDescription(); 39 51 case GpsDirectory.TAG_GPS_ALTITUDE_REF: … … 56 68 return getGpsDestinationReferenceDescription(); 57 69 case GpsDirectory.TAG_GPS_TIME_STAMP: 58 70 return getGpsTimeStampDescription(); 71 case GpsDirectory.TAG_GPS_LONGITUDE: 59 72 // three rational numbers -- displayed in HH"MM"SS.ss 60 case GpsDirectory.TAG_GPS_LONGITUDE:61 73 return getGpsLongitudeDescription(); 62 74 case GpsDirectory.TAG_GPS_LATITUDE: 75 // three rational numbers -- displayed in HH"MM"SS.ss 63 76 return getGpsLatitudeDescription(); 77 case GpsDirectory.TAG_GPS_DIFFERENTIAL: 78 return getGpsDifferentialDescription(); 64 79 default: 65 return _directory.getString(tagType);80 return super.getDescription(tagType); 66 81 } 67 82 } 68 83 69 public String getGpsLatitudeDescription() throws MetadataException 84 @Nullable 85 private String getGpsVersionIdDescription() 70 86 { 71 if (!_directory.containsTag(GpsDirectory.TAG_GPS_LATITUDE)) return null; 72 return getHoursMinutesSecondsDescription(GpsDirectory.TAG_GPS_LATITUDE); 87 return convertBytesToVersionString(_directory.getIntArray(GpsDirectory.TAG_GPS_VERSION_ID), 1); 73 88 } 74 89 75 public String getGpsLongitudeDescription() throws MetadataException 90 @Nullable 91 public String getGpsLatitudeDescription() 76 92 { 77 if (!_directory.containsTag(GpsDirectory.TAG_GPS_LONGITUDE)) return null; 78 return getHoursMinutesSecondsDescription(GpsDirectory.TAG_GPS_LONGITUDE); 93 GeoLocation location = _directory.getGeoLocation(); 94 95 if (location == null) 96 return null; 97 98 return GeoLocation.decimalToDegreesMinutesSecondsString(location.getLatitude()); 79 99 } 80 100 81 public String getHoursMinutesSecondsDescription(int tagType) throws MetadataException 101 @Nullable 102 public String getGpsLongitudeDescription() 82 103 { 83 Rational[] components = _directory.getRationalArray(tagType); 84 // TODO create an HoursMinutesSecods class ?? 85 int deg = components[0].intValue(); 86 float min = components[1].floatValue(); 87 float sec = components[2].floatValue(); 88 // carry fractions of minutes into seconds -- thanks Colin Briton 89 sec += (min % 1) * 60; 90 return String.valueOf(deg) + "\"" + String.valueOf((int)min) + "'" + String.valueOf(sec); 104 GeoLocation location = _directory.getGeoLocation(); 105 106 if (location == null) 107 return null; 108 109 return GeoLocation.decimalToDegreesMinutesSecondsString(location.getLongitude()); 91 110 } 92 111 93 public String getGpsTimeStampDescription() throws MetadataException 112 @Nullable 113 public String getGpsTimeStampDescription() 94 114 { 95 115 // time in hour, min, sec 96 if (!_directory.containsTag(GpsDirectory.TAG_GPS_TIME_STAMP)) return null;97 116 int[] timeComponents = _directory.getIntArray(GpsDirectory.TAG_GPS_TIME_STAMP); 98 StringBuffer sbuffer = new StringBuffer(); 99 sbuffer.append(timeComponents[0]); 100 sbuffer.append(":"); 101 sbuffer.append(timeComponents[1]); 102 sbuffer.append(":"); 103 sbuffer.append(timeComponents[2]); 104 sbuffer.append(" UTC"); 105 return sbuffer.toString(); 117 if (timeComponents==null) 118 return null; 119 StringBuilder description = new StringBuilder(); 120 description.append(timeComponents[0]); 121 description.append(":"); 122 description.append(timeComponents[1]); 123 description.append(":"); 124 description.append(timeComponents[2]); 125 description.append(" UTC"); 126 return description.toString(); 106 127 } 107 128 129 @Nullable 108 130 public String getGpsDestinationReferenceDescription() 109 131 { 110 if (!_directory.containsTag(GpsDirectory.TAG_GPS_DEST_DISTANCE_REF)) return null; 111 String destRef = _directory.getString(GpsDirectory.TAG_GPS_DEST_DISTANCE_REF).trim(); 112 if ("K".equalsIgnoreCase(destRef)) { 132 final String value = _directory.getString(GpsDirectory.TAG_GPS_DEST_DISTANCE_REF); 133 if (value==null) 134 return null; 135 String distanceRef = value.trim(); 136 if ("K".equalsIgnoreCase(distanceRef)) { 113 137 return "kilometers"; 114 } else if ("M".equalsIgnoreCase(d estRef)) {138 } else if ("M".equalsIgnoreCase(distanceRef)) { 115 139 return "miles"; 116 } else if ("N".equalsIgnoreCase(d estRef)) {140 } else if ("N".equalsIgnoreCase(distanceRef)) { 117 141 return "knots"; 118 142 } else { 119 return "Unknown (" + d estRef + ")";143 return "Unknown (" + distanceRef + ")"; 120 144 } 121 145 } 122 146 147 @Nullable 123 148 public String getGpsDirectionDescription(int tagType) 124 149 { 125 if (!_directory.containsTag(tagType)) return null; 126 String gpsDirection = _directory.getString(tagType).trim(); 127 return gpsDirection + " degrees"; 150 Rational angle = _directory.getRational(tagType); 151 // provide a decimal version of rational numbers in the description, to avoid strings like "35334/199 degrees" 152 String value = angle != null 153 ? new DecimalFormat("0.##").format(angle.doubleValue()) 154 : _directory.getString(tagType); 155 if (value==null || value.trim().length()==0) 156 return null; 157 return value.trim() + " degrees"; 128 158 } 129 159 160 @Nullable 130 161 public String getGpsDirectionReferenceDescription(int tagType) 131 162 { 132 if (!_directory.containsTag(tagType)) return null; 133 String gpsDistRef = _directory.getString(tagType).trim(); 163 final String value = _directory.getString(tagType); 164 if (value==null) 165 return null; 166 String gpsDistRef = value.trim(); 134 167 if ("T".equalsIgnoreCase(gpsDistRef)) { 135 168 return "True direction"; 136 169 } else if ("M".equalsIgnoreCase(gpsDistRef)) { … … 140 173 } 141 174 } 142 175 176 @Nullable 143 177 public String getGpsSpeedRefDescription() 144 178 { 145 if (!_directory.containsTag(GpsDirectory.TAG_GPS_SPEED_REF)) return null; 146 String gpsSpeedRef = _directory.getString(GpsDirectory.TAG_GPS_SPEED_REF).trim(); 179 final String value = _directory.getString(GpsDirectory.TAG_GPS_SPEED_REF); 180 if (value==null) 181 return null; 182 String gpsSpeedRef = value.trim(); 147 183 if ("K".equalsIgnoreCase(gpsSpeedRef)) { 148 184 return "kph"; 149 185 } else if ("M".equalsIgnoreCase(gpsSpeedRef)) { … … 155 191 } 156 192 } 157 193 194 @Nullable 158 195 public String getGpsMeasureModeDescription() 159 196 { 160 if (!_directory.containsTag(GpsDirectory.TAG_GPS_MEASURE_MODE)) return null; 161 String gpsSpeedMeasureMode = _directory.getString(GpsDirectory.TAG_GPS_MEASURE_MODE).trim(); 197 final String value = _directory.getString(GpsDirectory.TAG_GPS_MEASURE_MODE); 198 if (value==null) 199 return null; 200 String gpsSpeedMeasureMode = value.trim(); 162 201 if ("2".equalsIgnoreCase(gpsSpeedMeasureMode)) { 163 202 return "2-dimensional measurement"; 164 203 } else if ("3".equalsIgnoreCase(gpsSpeedMeasureMode)) { … … 168 207 } 169 208 } 170 209 210 @Nullable 171 211 public String getGpsStatusDescription() 172 212 { 173 if (!_directory.containsTag(GpsDirectory.TAG_GPS_STATUS)) return null; 174 String gpsStatus = _directory.getString(GpsDirectory.TAG_GPS_STATUS).trim(); 213 final String value = _directory.getString(GpsDirectory.TAG_GPS_STATUS); 214 if (value==null) 215 return null; 216 String gpsStatus = value.trim(); 175 217 if ("A".equalsIgnoreCase(gpsStatus)) { 176 return " Measurement in progess";218 return "Active (Measurement in progress)"; 177 219 } else if ("V".equalsIgnoreCase(gpsStatus)) { 178 return " Measurement Interoperability";220 return "Void (Measurement Interoperability)"; 179 221 } else { 180 222 return "Unknown (" + gpsStatus + ")"; 181 223 } 182 224 } 183 225 184 public String getGpsAltitudeRefDescription() throws MetadataException 226 @Nullable 227 public String getGpsAltitudeRefDescription() 185 228 { 186 if (!_directory.containsTag(GpsDirectory.TAG_GPS_ALTITUDE_REF)) return null; 187 int alititudeRef = _directory.getInt(GpsDirectory.TAG_GPS_ALTITUDE_REF); 188 if (alititudeRef == 0) { 229 Integer value = _directory.getInteger(GpsDirectory.TAG_GPS_ALTITUDE_REF); 230 if (value==null) 231 return null; 232 if (value == 0) 189 233 return "Sea level"; 190 } else {191 return " Unknown (" + alititudeRef + ")";192 }234 if (value == 1) 235 return "Below sea level"; 236 return "Unknown (" + value + ")"; 193 237 } 194 238 195 public String getGpsAltitudeDescription() throws MetadataException 239 @Nullable 240 public String getGpsAltitudeDescription() 196 241 { 197 if (!_directory.containsTag(GpsDirectory.TAG_GPS_ALTITUDE)) return null; 198 String alititude = _directory.getRational(GpsDirectory.TAG_GPS_ALTITUDE).toSimpleString(true); 199 return alititude + " metres"; 242 final Rational value = _directory.getRational(GpsDirectory.TAG_GPS_ALTITUDE); 243 if (value==null) 244 return null; 245 return value.intValue() + " metres"; 200 246 } 247 248 @Nullable 249 public String getGpsDifferentialDescription() 250 { 251 final Integer value = _directory.getInteger(GpsDirectory.TAG_GPS_DIFFERENTIAL); 252 if (value==null) 253 return null; 254 if (value == 0) 255 return "No Correction"; 256 if (value == 1) 257 return "Differential Corrected"; 258 return "Unknown (" + value + ")"; 259 } 260 261 @Nullable 262 public String getDegreesMinutesSecondsDescription() 263 { 264 GeoLocation location = _directory.getGeoLocation(); 265 266 if (location == null) 267 return null; 268 269 return location.toDMSString(); 270 } 201 271 } -
src/com/drew/metadata/exif/GpsDirectory.java
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:00:52 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.GeoLocation; 24 import com.drew.lang.Rational; 25 import com.drew.lang.annotations.NotNull; 26 import com.drew.lang.annotations.Nullable; 19 27 import com.drew.metadata.Directory; 20 28 21 29 import java.util.HashMap; 22 30 23 31 /** 32 * Describes Exif tags that contain Global Positioning System (GPS) data. 24 33 * 34 * @author Drew Noakes http://drewnoakes.com 25 35 */ 26 36 public class GpsDirectory extends Directory 27 37 { … … 80 90 /** Distance to destination GPSDestDistance 26 1A RATIONAL 1 */ 81 91 public static final int TAG_GPS_DEST_DISTANCE = 0x001A; 82 92 83 protected static final HashMap tagNameMap = new HashMap(); 93 /** Values of "GPS", "CELLID", "WLAN" or "MANUAL" by the EXIF spec. */ 94 public static final int TAG_GPS_PROCESSING_METHOD = 0x001B; 95 public static final int TAG_GPS_AREA_INFORMATION = 0x001C; 96 public static final int TAG_GPS_DATE_STAMP = 0x001D; 97 public static final int TAG_GPS_DIFFERENTIAL = 0x001E; 84 98 99 @NotNull 100 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 101 85 102 static 86 103 { 87 tagNameMap.put(new Integer(TAG_GPS_VERSION_ID), "GPS Version ID"); 88 tagNameMap.put(new Integer(TAG_GPS_LATITUDE_REF), "GPS Latitude Ref"); 89 tagNameMap.put(new Integer(TAG_GPS_LATITUDE), "GPS Latitude"); 90 tagNameMap.put(new Integer(TAG_GPS_LONGITUDE_REF), "GPS Longitude Ref"); 91 tagNameMap.put(new Integer(TAG_GPS_LONGITUDE), "GPS Longitude"); 92 tagNameMap.put(new Integer(TAG_GPS_ALTITUDE_REF), "GPS Altitude Ref"); 93 tagNameMap.put(new Integer(TAG_GPS_ALTITUDE), "GPS Altitude"); 94 tagNameMap.put(new Integer(TAG_GPS_TIME_STAMP), "GPS Time-Stamp"); 95 tagNameMap.put(new Integer(TAG_GPS_SATELLITES), "GPS Satellites"); 96 tagNameMap.put(new Integer(TAG_GPS_STATUS), "GPS Status"); 97 tagNameMap.put(new Integer(TAG_GPS_MEASURE_MODE), "GPS Measure Mode"); 98 tagNameMap.put(new Integer(TAG_GPS_DOP), "GPS DOP"); 99 tagNameMap.put(new Integer(TAG_GPS_SPEED_REF), "GPS Speed Ref"); 100 tagNameMap.put(new Integer(TAG_GPS_SPEED), "GPS Speed"); 101 tagNameMap.put(new Integer(TAG_GPS_TRACK_REF), "GPS Track Ref"); 102 tagNameMap.put(new Integer(TAG_GPS_TRACK), "GPS Track"); 103 tagNameMap.put(new Integer(TAG_GPS_IMG_DIRECTION_REF), "GPS Img Direction Ref"); 104 tagNameMap.put(new Integer(TAG_GPS_IMG_DIRECTION_REF), "GPS Img Direction"); 105 tagNameMap.put(new Integer(TAG_GPS_MAP_DATUM), "GPS Map Datum"); 106 tagNameMap.put(new Integer(TAG_GPS_DEST_LATITUDE_REF), "GPS Dest Latitude Ref"); 107 tagNameMap.put(new Integer(TAG_GPS_DEST_LATITUDE), "GPS Dest Latitude"); 108 tagNameMap.put(new Integer(TAG_GPS_DEST_LONGITUDE_REF), "GPS Dest Longitude Ref"); 109 tagNameMap.put(new Integer(TAG_GPS_DEST_LONGITUDE), "GPS Dest Longitude"); 110 tagNameMap.put(new Integer(TAG_GPS_DEST_BEARING_REF), "GPS Dest Bearing Ref"); 111 tagNameMap.put(new Integer(TAG_GPS_DEST_BEARING), "GPS Dest Bearing"); 112 tagNameMap.put(new Integer(TAG_GPS_DEST_DISTANCE_REF), "GPS Dest Distance Ref"); 113 tagNameMap.put(new Integer(TAG_GPS_DEST_DISTANCE), "GPS Dest Distance"); 104 _tagNameMap.put(TAG_GPS_VERSION_ID, "GPS Version ID"); 105 _tagNameMap.put(TAG_GPS_LATITUDE_REF, "GPS Latitude Ref"); 106 _tagNameMap.put(TAG_GPS_LATITUDE, "GPS Latitude"); 107 _tagNameMap.put(TAG_GPS_LONGITUDE_REF, "GPS Longitude Ref"); 108 _tagNameMap.put(TAG_GPS_LONGITUDE, "GPS Longitude"); 109 _tagNameMap.put(TAG_GPS_ALTITUDE_REF, "GPS Altitude Ref"); 110 _tagNameMap.put(TAG_GPS_ALTITUDE, "GPS Altitude"); 111 _tagNameMap.put(TAG_GPS_TIME_STAMP, "GPS Time-Stamp"); 112 _tagNameMap.put(TAG_GPS_SATELLITES, "GPS Satellites"); 113 _tagNameMap.put(TAG_GPS_STATUS, "GPS Status"); 114 _tagNameMap.put(TAG_GPS_MEASURE_MODE, "GPS Measure Mode"); 115 _tagNameMap.put(TAG_GPS_DOP, "GPS DOP"); 116 _tagNameMap.put(TAG_GPS_SPEED_REF, "GPS Speed Ref"); 117 _tagNameMap.put(TAG_GPS_SPEED, "GPS Speed"); 118 _tagNameMap.put(TAG_GPS_TRACK_REF, "GPS Track Ref"); 119 _tagNameMap.put(TAG_GPS_TRACK, "GPS Track"); 120 _tagNameMap.put(TAG_GPS_IMG_DIRECTION_REF, "GPS Img Direction Ref"); 121 _tagNameMap.put(TAG_GPS_IMG_DIRECTION, "GPS Img Direction"); 122 _tagNameMap.put(TAG_GPS_MAP_DATUM, "GPS Map Datum"); 123 _tagNameMap.put(TAG_GPS_DEST_LATITUDE_REF, "GPS Dest Latitude Ref"); 124 _tagNameMap.put(TAG_GPS_DEST_LATITUDE, "GPS Dest Latitude"); 125 _tagNameMap.put(TAG_GPS_DEST_LONGITUDE_REF, "GPS Dest Longitude Ref"); 126 _tagNameMap.put(TAG_GPS_DEST_LONGITUDE, "GPS Dest Longitude"); 127 _tagNameMap.put(TAG_GPS_DEST_BEARING_REF, "GPS Dest Bearing Ref"); 128 _tagNameMap.put(TAG_GPS_DEST_BEARING, "GPS Dest Bearing"); 129 _tagNameMap.put(TAG_GPS_DEST_DISTANCE_REF, "GPS Dest Distance Ref"); 130 _tagNameMap.put(TAG_GPS_DEST_DISTANCE, "GPS Dest Distance"); 131 _tagNameMap.put(TAG_GPS_PROCESSING_METHOD, "GPS Processing Method"); 132 _tagNameMap.put(TAG_GPS_AREA_INFORMATION, "GPS Area Information"); 133 _tagNameMap.put(TAG_GPS_DATE_STAMP, "GPS Date Stamp"); 134 _tagNameMap.put(TAG_GPS_DIFFERENTIAL, "GPS Differential"); 114 135 } 115 136 116 137 public GpsDirectory() … … 118 139 this.setDescriptor(new GpsDescriptor(this)); 119 140 } 120 141 142 @NotNull 121 143 public String getName() 122 144 { 123 145 return "GPS"; 124 146 } 125 147 126 protected HashMap getTagNameMap() 148 @NotNull 149 protected HashMap<Integer, String> getTagNameMap() 127 150 { 128 return tagNameMap;151 return _tagNameMap; 129 152 } 153 154 /** 155 * Parses various tags in an attempt to obtain a single object representing the latitude and longitude 156 * at which this image was captured. 157 * 158 * @return The geographical location of this image, if possible, otherwise null 159 */ 160 @Nullable 161 public GeoLocation getGeoLocation() 162 { 163 Rational[] latitudes = getRationalArray(GpsDirectory.TAG_GPS_LATITUDE); 164 Rational[] longitudes = getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE); 165 String latitudeRef = getString(GpsDirectory.TAG_GPS_LATITUDE_REF); 166 String longitudeRef = getString(GpsDirectory.TAG_GPS_LONGITUDE_REF); 167 168 // Make sure we have the required values 169 if (latitudes == null || latitudes.length != 3) 170 return null; 171 if (longitudes == null || longitudes.length != 3) 172 return null; 173 if (latitudeRef == null || longitudeRef == null) 174 return null; 175 176 Double lat = GeoLocation.degreesMinutesSecondsToDecimal(latitudes[0], latitudes[1], latitudes[2], latitudeRef.equalsIgnoreCase("S")); 177 Double lon = GeoLocation.degreesMinutesSecondsToDecimal(longitudes[0], longitudes[1], longitudes[2], longitudeRef.equalsIgnoreCase("W")); 178 179 // This can return null, in cases where the conversion was not possible 180 if (lat == null || lon == null) 181 return null; 182 183 return new GeoLocation(lat, lon); 184 } 130 185 } -
src/com/drew/metadata/exif/KodakMakernoteDescriptor.java
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.exif; 16 22 17 import com.drew.metadata.Directory; 18 import com.drew.metadata.MetadataException; 23 import com.drew.lang.annotations.NotNull; 19 24 import com.drew.metadata.TagDescriptor; 20 25 21 26 /** 22 * Provides human-readable string versions of the tags stored in a KodakMakernoteDirectory. 27 * Provides human-readable string representations of tag values stored in a <code>KodakMakernoteDirectory</code>. 28 * 23 29 * Thanks to David Carson for the initial version of this class. 30 * 31 * @author Drew Noakes http://drewnoakes.com 24 32 */ 25 public class KodakMakernoteDescriptor extends TagDescriptor 33 public class KodakMakernoteDescriptor extends TagDescriptor<KodakMakernoteDirectory> 26 34 { 27 public KodakMakernoteDescriptor(Directory directory) 28 { 29 super(directory); 30 } 31 32 public String getDescription(int tagType) throws MetadataException 35 public KodakMakernoteDescriptor(@NotNull KodakMakernoteDirectory directory) 33 36 { 34 return _directory.getString(tagType);35 37 super(directory); 38 } 36 39 } -
src/com/drew/metadata/exif/KodakMakernoteDirectory.java
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.exif; 16 22 23 import com.drew.lang.annotations.NotNull; 17 24 import com.drew.metadata.Directory; 18 25 19 26 import java.util.HashMap; 20 27 21 28 /** 22 29 * Describes tags specific to Kodak cameras. 30 * 31 * @author Drew Noakes http://drewnoakes.com 23 32 */ 24 33 public class KodakMakernoteDirectory extends Directory 25 34 { 26 protected static final HashMap _tagNameMap = new HashMap(); 27 28 public String getName() 35 @NotNull 36 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 37 38 public KodakMakernoteDirectory() 29 39 { 30 return "Kodak Makernote";31 40 this.setDescriptor(new KodakMakernoteDescriptor(this)); 41 } 32 42 33 protected HashMap getTagNameMap() 43 @NotNull 44 public String getName() 34 45 { 35 return _tagNameMap; 36 } 46 return "Kodak Makernote"; 47 } 48 49 @NotNull 50 protected HashMap<Integer, String> getTagNameMap() 51 { 52 return _tagNameMap; 53 } 37 54 } -
src/com/drew/metadata/exif/KyoceraMakernoteDescriptor.java
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.exif; 16 22 17 import com.drew. metadata.Directory;18 import com.drew. metadata.MetadataException;23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 19 25 import com.drew.metadata.TagDescriptor; 20 26 21 27 /** 22 * Provides human-readable string versions of the tags stored in a KyoceraMakernoteDirectory.23 * 28 * Provides human-readable string representations of tag values stored in a <code>KyoceraMakernoteDirectory</code>. 29 * <p/> 24 30 * Some information about this makernote taken from here: 25 31 * http://www.ozhiker.com/electronics/pjmt/jpeg_info/kyocera_mn.html 26 * 32 * <p/> 27 33 * Most manufacturer's MakerNote counts the "offset to data" from the first byte 28 34 * of TIFF header (same as the other IFD), but Kyocera (along with Fujifilm) counts 29 35 * it from the first byte of MakerNote itself. 36 * 37 * @author Drew Noakes http://drewnoakes.com 30 38 */ 31 public class KyoceraMakernoteDescriptor extends TagDescriptor 39 public class KyoceraMakernoteDescriptor extends TagDescriptor<KyoceraMakernoteDirectory> 32 40 { 33 public KyoceraMakernoteDescriptor( Directory directory)41 public KyoceraMakernoteDescriptor(@NotNull KyoceraMakernoteDirectory directory) 34 42 { 35 43 super(directory); 36 44 } 37 45 38 public String getDescription(int tagType) throws MetadataException 46 @Nullable 47 public String getDescription(int tagType) 39 48 { 40 49 switch (tagType) { 41 50 case KyoceraMakernoteDirectory.TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO: … … 43 52 case KyoceraMakernoteDirectory.TAG_KYOCERA_PROPRIETARY_THUMBNAIL: 44 53 return getProprietaryThumbnailDataDescription(); 45 54 default: 46 return _directory.getString(tagType);55 return super.getDescription(tagType); 47 56 } 48 57 } 49 58 50 public String getPrintImageMatchingInfoDescription() throws MetadataException 59 @Nullable 60 public String getPrintImageMatchingInfoDescription() 51 61 { 52 if (!_directory.containsTag(KyoceraMakernoteDirectory.TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO)) return null;53 62 byte[] bytes = _directory.getByteArray(KyoceraMakernoteDirectory.TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO); 63 if (bytes==null) 64 return null; 54 65 return "(" + bytes.length + " bytes)"; 55 66 } 56 67 57 public String getProprietaryThumbnailDataDescription() throws MetadataException 68 @Nullable 69 public String getProprietaryThumbnailDataDescription() 58 70 { 59 if (!_directory.containsTag(KyoceraMakernoteDirectory.TAG_KYOCERA_PROPRIETARY_THUMBNAIL)) return null;60 71 byte[] bytes = _directory.getByteArray(KyoceraMakernoteDirectory.TAG_KYOCERA_PROPRIETARY_THUMBNAIL); 72 if (bytes==null) 73 return null; 61 74 return "(" + bytes.length + " bytes)"; 62 75 } 63 76 } -
src/com/drew/metadata/exif/KyoceraMakernoteDirectory.java
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 21 26 import java.util.HashMap; 22 27 23 28 /** 29 * Describes tags specific to Kyocera and Contax cameras. 24 30 * 31 * @author Drew Noakes http://drewnoakes.com 25 32 */ 26 33 public class KyoceraMakernoteDirectory extends Directory 27 34 { 28 35 public static final int TAG_KYOCERA_PROPRIETARY_THUMBNAIL = 0x0001; 29 36 public static final int TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO = 0x0E00; 30 37 31 protected static final HashMap tagNameMap = new HashMap(); 38 @NotNull 39 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 32 40 33 41 static 34 42 { 35 tagNameMap.put(new Integer(TAG_KYOCERA_PROPRIETARY_THUMBNAIL), "Proprietary Thumbnail Format Data");36 tagNameMap.put(new Integer(TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO), "Print Image Matching (PIM) Info");43 _tagNameMap.put(TAG_KYOCERA_PROPRIETARY_THUMBNAIL, "Proprietary Thumbnail Format Data"); 44 _tagNameMap.put(TAG_KYOCERA_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info"); 37 45 } 38 46 39 47 public KyoceraMakernoteDirectory() … … 41 49 this.setDescriptor(new KyoceraMakernoteDescriptor(this)); 42 50 } 43 51 52 @NotNull 44 53 public String getName() 45 54 { 46 55 return "Kyocera/Contax Makernote"; 47 56 } 48 57 49 protected HashMap getTagNameMap() 58 @NotNull 59 protected HashMap<Integer, String> getTagNameMap() 50 60 { 51 return tagNameMap;61 return _tagNameMap; 52 62 } 53 63 } -
src/com/drew/metadata/exif/NikonType1MakernoteDescriptor.java
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.exif; 16 22 17 23 import com.drew.lang.Rational; 18 import com.drew. metadata.Directory;19 import com.drew. metadata.MetadataException;24 import com.drew.lang.annotations.NotNull; 25 import com.drew.lang.annotations.Nullable; 20 26 import com.drew.metadata.TagDescriptor; 21 27 22 28 /** 23 * Provides human-readable string versions of the tags stored in a NikonType1MakernoteDirectory. 29 * Provides human-readable string representations of tag values stored in a <code>NikonType1MakernoteDirectory</code>. 30 * <p/> 24 31 * Type-1 is for E-Series cameras prior to (not including) E990. For example: E700, E800, E900, 25 32 * E900S, E910, E950. 26 * 33 * <p/> 27 34 * MakerNote starts from ASCII string "Nikon". Data format is the same as IFD, but it starts from 28 35 * offset 0x08. This is the same as Olympus except start string. Example of actual data 29 36 * structure is shown below. … … 31 38 * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon........... 32 39 * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................ 33 40 * </code></pre> 41 * 42 * @author Drew Noakes http://drewnoakes.com 34 43 */ 35 public class NikonType1MakernoteDescriptor extends TagDescriptor 44 public class NikonType1MakernoteDescriptor extends TagDescriptor<NikonType1MakernoteDirectory> 36 45 { 37 public NikonType1MakernoteDescriptor( Directory directory)46 public NikonType1MakernoteDescriptor(@NotNull NikonType1MakernoteDirectory directory) 38 47 { 39 48 super(directory); 40 49 } 41 50 42 public String getDescription(int tagType) throws MetadataException 51 @Nullable 52 public String getDescription(int tagType) 43 53 { 44 54 switch (tagType) { 45 55 case NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_QUALITY: … … 59 69 case NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CONVERTER: 60 70 return getConverterDescription(); 61 71 default: 62 return _directory.getString(tagType);72 return super.getDescription(tagType); 63 73 } 64 74 } 65 75 66 public String getConverterDescription() throws MetadataException 76 @Nullable 77 public String getConverterDescription() 67 78 { 68 if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CONVERTER)) return null; 69 int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CONVERTER); 79 Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CONVERTER); 80 if (value == null) 81 return null; 70 82 switch (value) { 71 83 case 0: 72 84 return "None"; … … 77 89 } 78 90 } 79 91 80 public String getDigitalZoomDescription() throws MetadataException 92 @Nullable 93 public String getDigitalZoomDescription() 81 94 { 82 if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_DIGITAL_ZOOM)) return null;83 95 Rational value = _directory.getRational(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_DIGITAL_ZOOM); 96 if (value == null) 97 return null; 84 98 if (value.getNumerator() == 0) { 85 99 return "No digital zoom"; 86 100 } 87 101 return value.toSimpleString(true) + "x digital zoom"; 88 102 } 89 103 90 public String getFocusDescription() throws MetadataException 104 @Nullable 105 public String getFocusDescription() 91 106 { 92 if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_FOCUS)) return null;93 107 Rational value = _directory.getRational(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_FOCUS); 108 if (value == null) 109 return null; 94 110 if (value.getNumerator() == 1 && value.getDenominator() == 0) { 95 111 return "Infinite"; 96 112 } 97 113 return value.toSimpleString(true); 98 114 } 99 115 100 public String getWhiteBalanceDescription() throws MetadataException 116 @Nullable 117 public String getWhiteBalanceDescription() 101 118 { 102 if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_WHITE_BALANCE)) return null; 103 int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_WHITE_BALANCE); 119 Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_WHITE_BALANCE); 120 if (value == null) 121 return null; 104 122 switch (value) { 105 123 case 0: 106 124 return "Auto"; … … 109 127 case 2: 110 128 return "Daylight"; 111 129 case 3: 112 return "Incandescen se";130 return "Incandescence"; 113 131 case 4: 114 return "Flo urescence";132 return "Florescence"; 115 133 case 5: 116 134 return "Cloudy"; 117 135 case 6: … … 121 139 } 122 140 } 123 141 124 public String getCcdSensitivityDescription() throws MetadataException 142 @Nullable 143 public String getCcdSensitivityDescription() 125 144 { 126 if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CCD_SENSITIVITY)) return null; 127 int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CCD_SENSITIVITY); 145 Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_CCD_SENSITIVITY); 146 if (value == null) 147 return null; 128 148 switch (value) { 129 149 case 0: 130 150 return "ISO80"; … … 139 159 } 140 160 } 141 161 142 public String getImageAdjustmentDescription() throws MetadataException 162 @Nullable 163 public String getImageAdjustmentDescription() 143 164 { 144 if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT)) return null; 145 int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT); 165 Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT); 166 if (value == null) 167 return null; 146 168 switch (value) { 147 169 case 0: 148 170 return "Normal"; … … 159 181 } 160 182 } 161 183 162 public String getColorModeDescription() throws MetadataException 184 @Nullable 185 public String getColorModeDescription() 163 186 { 164 if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_COLOR_MODE)) return null; 165 int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_COLOR_MODE); 187 Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_COLOR_MODE); 188 if (value == null) 189 return null; 166 190 switch (value) { 167 191 case 1: 168 192 return "Color"; … … 173 197 } 174 198 } 175 199 176 public String getQualityDescription() throws MetadataException 200 @Nullable 201 public String getQualityDescription() 177 202 { 178 if (!_directory.containsTag(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_QUALITY)) return null; 179 int value = _directory.getInt(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_QUALITY); 203 Integer value = _directory.getInteger(NikonType1MakernoteDirectory.TAG_NIKON_TYPE1_QUALITY); 204 if (value == null) 205 return null; 180 206 switch (value) { 181 207 case 1: 182 208 return "VGA Basic"; -
src/com/drew/metadata/exif/NikonType1MakernoteDirectory.java
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 21 26 import java.util.HashMap; 22 27 23 28 /** 24 * Contains values specific to Nikoncameras. Type-1 is for E-Series cameras prior to (not including) E990.29 * Describes tags specific to Nikon (type 1) cameras. Type-1 is for E-Series cameras prior to (not including) E990. 25 30 * 26 31 * There are 3 formats of Nikon's MakerNote. MakerNote of E700/E800/E900/E900S/E910/E950 27 32 * starts from ASCII string "Nikon". Data format is the same as IFD, but it starts from … … 31 36 * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon........... 32 37 * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................ 33 38 * </code></pre> 39 * 40 * @author Drew Noakes http://drewnoakes.com 34 41 */ 35 42 public class NikonType1MakernoteDirectory extends Directory 36 43 { … … 46 53 public static final int TAG_NIKON_TYPE1_CONVERTER = 0x000B; 47 54 public static final int TAG_NIKON_TYPE1_UNKNOWN_3 = 0x0F00; 48 55 49 protected static final HashMap _tagNameMap = new HashMap(); 56 @NotNull 57 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 50 58 51 59 static 52 60 { 53 _tagNameMap.put( new Integer(TAG_NIKON_TYPE1_CCD_SENSITIVITY), "CCD Sensitivity");54 _tagNameMap.put( new Integer(TAG_NIKON_TYPE1_COLOR_MODE), "Color Mode");55 _tagNameMap.put( new Integer(TAG_NIKON_TYPE1_DIGITAL_ZOOM), "Digital Zoom");56 _tagNameMap.put( new Integer(TAG_NIKON_TYPE1_CONVERTER), "Fisheye Converter");57 _tagNameMap.put( new Integer(TAG_NIKON_TYPE1_FOCUS), "Focus");58 _tagNameMap.put( new Integer(TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT), "Image Adjustment");59 _tagNameMap.put( new Integer(TAG_NIKON_TYPE1_QUALITY), "Quality");60 _tagNameMap.put( new Integer(TAG_NIKON_TYPE1_UNKNOWN_1), "Makernote Unknown 1");61 _tagNameMap.put( new Integer(TAG_NIKON_TYPE1_UNKNOWN_2), "Makernote Unknown 2");62 _tagNameMap.put( new Integer(TAG_NIKON_TYPE1_UNKNOWN_3), "Makernote Unknown 3");63 _tagNameMap.put( new Integer(TAG_NIKON_TYPE1_WHITE_BALANCE), "White Balance");61 _tagNameMap.put(TAG_NIKON_TYPE1_CCD_SENSITIVITY, "CCD Sensitivity"); 62 _tagNameMap.put(TAG_NIKON_TYPE1_COLOR_MODE, "Color Mode"); 63 _tagNameMap.put(TAG_NIKON_TYPE1_DIGITAL_ZOOM, "Digital Zoom"); 64 _tagNameMap.put(TAG_NIKON_TYPE1_CONVERTER, "Fisheye Converter"); 65 _tagNameMap.put(TAG_NIKON_TYPE1_FOCUS, "Focus"); 66 _tagNameMap.put(TAG_NIKON_TYPE1_IMAGE_ADJUSTMENT, "Image Adjustment"); 67 _tagNameMap.put(TAG_NIKON_TYPE1_QUALITY, "Quality"); 68 _tagNameMap.put(TAG_NIKON_TYPE1_UNKNOWN_1, "Makernote Unknown 1"); 69 _tagNameMap.put(TAG_NIKON_TYPE1_UNKNOWN_2, "Makernote Unknown 2"); 70 _tagNameMap.put(TAG_NIKON_TYPE1_UNKNOWN_3, "Makernote Unknown 3"); 71 _tagNameMap.put(TAG_NIKON_TYPE1_WHITE_BALANCE, "White Balance"); 64 72 } 65 73 66 74 public NikonType1MakernoteDirectory() … … 68 76 this.setDescriptor(new NikonType1MakernoteDescriptor(this)); 69 77 } 70 78 79 @NotNull 71 80 public String getName() 72 81 { 73 82 return "Nikon Makernote"; 74 83 } 75 84 76 protected HashMap getTagNameMap() 85 @NotNull 86 protected HashMap<Integer, String> getTagNameMap() 77 87 { 78 88 return _tagNameMap; 79 89 } -
src/com/drew/metadata/exif/NikonType2MakernoteDescriptor.java
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/ 14 */ 15 package com.drew.metadata.exif; 16 17 import com.drew.lang.Rational; 18 import com.drew.metadata.Directory; 19 import com.drew.metadata.MetadataException; 20 import com.drew.metadata.TagDescriptor; 21 22 import java.text.DecimalFormat; 23 24 /** 25 * Provides human-readable string versions of the tags stored in a NikonType2MakernoteDirectory. 26 * Type-2 applies to the E990 and D-series cameras such as the D1, D70 and D100. 27 */ 28 public class NikonType2MakernoteDescriptor extends TagDescriptor 29 { 30 public NikonType2MakernoteDescriptor(Directory directory) 31 { 32 super(directory); 33 } 34 35 private NikonType2MakernoteDirectory getMakernoteDirectory() 36 { 37 return (NikonType2MakernoteDirectory)_directory; 38 } 39 40 public String getDescription(int tagType) throws MetadataException 41 { 42 switch (tagType) 43 { 44 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS: 45 return getLensDescription(); 46 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT: 47 return getHueAdjustmentDescription(); 48 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_COLOR_MODE: 49 return getColorModeDescription(); 50 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION: 51 return getAutoFlashCompensationDescription(); 52 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1: 53 return getIsoSettingDescription(); 54 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM: 55 return getDigitalZoomDescription(); 56 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION: 57 return getAutoFocusPositionDescription(); 58 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION: 59 return getAutoFirmwareVersionDescription(); 60 default: 61 return _directory.getString(tagType); 62 } 63 } 64 65 public String getAutoFocusPositionDescription() throws MetadataException 66 { 67 if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION)) return null; 68 int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION); 69 if (values.length != 4 || values[0] != 0 || values[2] != 0 || values[3] != 0) { 70 return "Unknown (" + _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION) + ")"; 71 } 72 switch (values[1]) { 73 case 0: 74 return "Centre"; 75 case 1: 76 return "Top"; 77 case 2: 78 return "Bottom"; 79 case 3: 80 return "Left"; 81 case 4: 82 return "Right"; 83 default: 84 return "Unknown (" + values[1] + ")"; 85 } 86 } 87 88 public String getDigitalZoomDescription() throws MetadataException 89 { 90 if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM)) return null; 91 Rational rational = _directory.getRational(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM); 92 if (rational.intValue() == 1) { 93 return "No digital zoom"; 94 } 95 return rational.toSimpleString(true) + "x digital zoom"; 96 } 97 98 public String getIsoSettingDescription() throws MetadataException 99 { 100 if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1)) return null; 101 int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1); 102 if (values[0] != 0 || values[1] == 0) { 103 return "Unknown (" + _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1) + ")"; 104 } 105 return "ISO " + values[1]; 106 } 107 108 public String getAutoFlashCompensationDescription() throws MetadataException 109 { 110 Rational ev = getMakernoteDirectory().getAutoFlashCompensation(); 111 112 if (ev==null) 113 return "Unknown"; 114 115 DecimalFormat decimalFormat = new DecimalFormat("0.##"); 116 return decimalFormat.format(ev.floatValue()) + " EV"; 117 } 118 119 public String getLensDescription() throws MetadataException 120 { 121 if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS)) 122 return null; 123 124 Rational[] lensValues = _directory.getRationalArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS); 125 126 if (lensValues.length!=4) 127 return _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS); 128 129 StringBuffer description = new StringBuffer(); 130 description.append(lensValues[0].intValue()); 131 description.append('-'); 132 description.append(lensValues[1].intValue()); 133 description.append("mm f/"); 134 description.append(lensValues[2].floatValue()); 135 description.append('-'); 136 description.append(lensValues[3].floatValue()); 137 138 return description.toString(); 139 } 140 141 public String getHueAdjustmentDescription() 142 { 143 if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT)) 144 return null; 145 146 return _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT) + " degrees"; 147 } 148 149 public String getColorModeDescription() 150 { 151 if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_COLOR_MODE)) 152 return null; 153 154 String raw = _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_COLOR_MODE); 155 if (raw.startsWith("MODE1")) 156 return "Mode I (sRGB)"; 157 158 return raw; 159 } 160 161 public String getAutoFirmwareVersionDescription() throws MetadataException 162 { 163 if (!_directory.containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION)) 164 return null; 165 166 int[] ints = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION); 167 return ExifDescriptor.convertBytesToVersionString(ints); 168 } 169 } 1 /* 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/ 20 */ 21 package com.drew.metadata.exif; 22 23 import com.drew.lang.Rational; 24 import com.drew.lang.StringUtil; 25 import com.drew.lang.annotations.NotNull; 26 import com.drew.lang.annotations.Nullable; 27 import com.drew.metadata.TagDescriptor; 28 29 import java.text.DecimalFormat; 30 import java.util.ArrayList; 31 import java.util.Collection; 32 import java.util.Date; 33 34 /** 35 * Provides human-readable string representations of tag values stored in a <code>NikonType2MakernoteDirectory</code>. 36 * 37 * Type-2 applies to the E990 and D-series cameras such as the D1, D70 and D100. 38 * 39 * @author Drew Noakes http://drewnoakes.com 40 */ 41 public class NikonType2MakernoteDescriptor extends TagDescriptor<NikonType2MakernoteDirectory> 42 { 43 public NikonType2MakernoteDescriptor(@NotNull NikonType2MakernoteDirectory directory) 44 { 45 super(directory); 46 } 47 48 @Nullable 49 public String getDescription(int tagType) 50 { 51 switch (tagType) 52 { 53 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_PROGRAM_SHIFT: 54 return getProgramShiftDescription(); 55 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_EXPOSURE_DIFFERENCE: 56 return getExposureDifferenceDescription(); 57 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS: 58 return getLensDescription(); 59 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT: 60 return getHueAdjustmentDescription(); 61 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_COLOR_MODE: 62 return getColorModeDescription(); 63 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION: 64 return getAutoFlashCompensationDescription(); 65 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_EXPOSURE_COMPENSATION: 66 return getFlashExposureCompensationDescription(); 67 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_BRACKET_COMPENSATION: 68 return getFlashBracketCompensationDescription(); 69 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_EXPOSURE_TUNING: 70 return getExposureTuningDescription(); 71 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS_STOPS: 72 return getLensStopsDescription(); 73 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_COLOR_SPACE: 74 return getColorSpaceDescription(); 75 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ACTIVE_D_LIGHTING: 76 return getActiveDLightingDescription(); 77 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_VIGNETTE_CONTROL: 78 return getVignetteControlDescription(); 79 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1: 80 return getIsoSettingDescription(); 81 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM: 82 return getDigitalZoomDescription(); 83 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_USED: 84 return getFlashUsedDescription(); 85 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION: 86 return getAutoFocusPositionDescription(); 87 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION: 88 return getFirmwareVersionDescription(); 89 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS_TYPE: 90 return getLensTypeDescription(); 91 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_SHOOTING_MODE: 92 return getShootingModeDescription(); 93 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_NEF_COMPRESSION: 94 return getNEFCompressionDescription(); 95 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_HIGH_ISO_NOISE_REDUCTION: 96 return getHighISONoiseReductionDescription(); 97 case NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_POWER_UP_TIME: 98 return getPowerUpTimeDescription(); 99 default: 100 return super.getDescription(tagType); 101 } 102 } 103 104 @Nullable 105 public String getPowerUpTimeDescription() 106 { 107 Long value = _directory.getLongObject(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_POWER_UP_TIME); 108 if (value==null) 109 return null; // TODO have observed a byte[8] here which is likely some kind of date (ticks as long?) 110 return new Date(value).toString(); 111 } 112 113 @Nullable 114 public String getHighISONoiseReductionDescription() 115 { 116 Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_HIGH_ISO_NOISE_REDUCTION); 117 if (value==null) 118 return null; 119 switch (value) { 120 case 0: return "Off"; 121 case 1: return "Minimal"; 122 case 2: return "Low"; 123 case 4: return "Normal"; 124 case 6: return "High"; 125 default: return "Unknown (" + value + ")"; 126 } 127 } 128 129 @Nullable 130 public String getFlashUsedDescription() 131 { 132 Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_USED); 133 if (value==null) 134 return null; 135 switch (value) { 136 case 0: return "Flash Not Used"; 137 case 1: return "Manual Flash"; 138 case 3: return "Flash Not Ready"; 139 case 7: return "External Flash"; 140 case 8: return "Fired, Commander Mode"; 141 case 9: return "Fired, TTL Mode"; 142 default: return "Unknown (" + value + ")"; 143 } 144 } 145 146 @Nullable 147 public String getNEFCompressionDescription() 148 { 149 Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_NEF_COMPRESSION); 150 if (value==null) 151 return null; 152 switch (value) { 153 case 1: return "Lossy (Type 1)"; 154 case 3: return "Uncompressed"; 155 case 7: return "Lossless"; 156 case 8: return "Lossy (Type 2)"; 157 default: return "Unknown (" + value + ")"; 158 } 159 } 160 161 @Nullable 162 public String getShootingModeDescription() 163 { 164 Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_SHOOTING_MODE); 165 if (value==null) 166 return null; 167 Collection<String> bits = new ArrayList<String>(); 168 169 if ((value&1)==1) 170 bits.add("Continuous"); 171 else 172 bits.add("Single Frame"); 173 174 if ((value&2)==2) 175 bits.add("Delay"); 176 // Don't know about 3 177 if ((value&8)==8) 178 bits.add("PC Control"); 179 if ((value&16)==16) 180 bits.add("Exposure Bracketing"); 181 if ((value&32)==32) 182 bits.add("Auto ISO"); 183 if ((value&64)==64) 184 bits.add("White-Balance Bracketing"); 185 if ((value&128)==128) 186 bits.add("IR Control"); 187 188 return StringUtil.join(bits, ", "); 189 } 190 191 @Nullable 192 public String getLensTypeDescription() 193 { 194 Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS_TYPE); 195 if (value==null) 196 return null; 197 198 Collection<String> bits = new ArrayList<String>(); 199 200 // TODO validate these values, as 14 is labelled as AF-C elsewhere but appears here as AF-D-G-VR 201 202 if ((value&1)==1) 203 bits.add("MF"); 204 else 205 bits.add("AF"); 206 207 if ((value&2)==2) 208 bits.add("D"); 209 210 if ((value&4)==4) 211 bits.add("G"); 212 213 if ((value&8)==8) 214 bits.add("VR"); 215 216 return StringUtil.join(bits, ", "); 217 } 218 219 @Nullable 220 public String getColorSpaceDescription() 221 { 222 Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_COLOR_SPACE); 223 if (value==null) 224 return null; 225 switch (value) { 226 case 1: return "sRGB"; 227 case 2: return "Adobe RGB"; 228 default: return "Unknown (" + value + ")"; 229 } 230 } 231 232 @Nullable 233 public String getActiveDLightingDescription() 234 { 235 Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ACTIVE_D_LIGHTING); 236 if (value==null) 237 return null; 238 switch (value) { 239 case 0: return "Off"; 240 case 1: return "Light"; 241 case 3: return "Normal"; 242 case 5: return "High"; 243 case 7: return "Extra High"; 244 case 65535: return "Auto"; 245 default: return "Unknown (" + value + ")"; 246 } 247 } 248 249 @Nullable 250 public String getVignetteControlDescription() 251 { 252 Integer value = _directory.getInteger(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_VIGNETTE_CONTROL); 253 if (value==null) 254 return null; 255 switch (value) { 256 case 0: return "Off"; 257 case 1: return "Low"; 258 case 3: return "Normal"; 259 case 5: return "High"; 260 default: return "Unknown (" + value + ")"; 261 } 262 } 263 264 @Nullable 265 public String getAutoFocusPositionDescription() 266 { 267 int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION); 268 if (values==null) 269 return null; 270 if (values.length != 4 || values[0] != 0 || values[2] != 0 || values[3] != 0) { 271 return "Unknown (" + _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AF_FOCUS_POSITION) + ")"; 272 } 273 switch (values[1]) { 274 case 0: 275 return "Centre"; 276 case 1: 277 return "Top"; 278 case 2: 279 return "Bottom"; 280 case 3: 281 return "Left"; 282 case 4: 283 return "Right"; 284 default: 285 return "Unknown (" + values[1] + ")"; 286 } 287 } 288 289 @Nullable 290 public String getDigitalZoomDescription() 291 { 292 Rational value = _directory.getRational(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_DIGITAL_ZOOM); 293 if (value==null) 294 return null; 295 return value.intValue() == 1 296 ? "No digital zoom" 297 : value.toSimpleString(true) + "x digital zoom"; 298 } 299 300 @Nullable 301 public String getProgramShiftDescription() 302 { 303 int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_PROGRAM_SHIFT); 304 return getEVDescription(values); 305 } 306 307 @Nullable 308 public String getExposureDifferenceDescription() 309 { 310 int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_EXPOSURE_DIFFERENCE); 311 return getEVDescription(values); 312 } 313 314 @NotNull 315 public String getAutoFlashCompensationDescription() 316 { 317 int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION); 318 return getEVDescription(values); 319 } 320 321 @NotNull 322 public String getFlashExposureCompensationDescription() 323 { 324 int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_EXPOSURE_COMPENSATION); 325 return getEVDescription(values); 326 } 327 328 @NotNull 329 public String getFlashBracketCompensationDescription() 330 { 331 int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FLASH_BRACKET_COMPENSATION); 332 return getEVDescription(values); 333 } 334 335 @NotNull 336 public String getExposureTuningDescription() 337 { 338 int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_EXPOSURE_TUNING); 339 return getEVDescription(values); 340 } 341 342 @NotNull 343 public String getLensStopsDescription() 344 { 345 int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS_STOPS); 346 return getEVDescription(values); 347 } 348 349 @Nullable 350 private static String getEVDescription(@Nullable int[] values) 351 { 352 if (values==null) 353 return null; 354 if (values.length<3 || values[2]==0) 355 return null; 356 final DecimalFormat decimalFormat = new DecimalFormat("0.##"); 357 double ev = values[0] * values[1] / (double)values[2]; 358 return decimalFormat.format(ev) + " EV"; 359 } 360 361 @Nullable 362 public String getIsoSettingDescription() 363 { 364 int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1); 365 if (values==null) 366 return null; 367 if (values[0] != 0 || values[1] == 0) 368 return "Unknown (" + _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_ISO_1) + ")"; 369 return "ISO " + values[1]; 370 } 371 372 @Nullable 373 public String getLensDescription() 374 { 375 Rational[] values = _directory.getRationalArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS); 376 377 if (values==null) 378 return null; 379 380 if (values.length<4) 381 return _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_LENS); 382 383 StringBuilder description = new StringBuilder(); 384 description.append(values[0].intValue()); 385 description.append('-'); 386 description.append(values[1].intValue()); 387 description.append("mm f/"); 388 description.append(values[2].floatValue()); 389 description.append('-'); 390 description.append(values[3].floatValue()); 391 392 return description.toString(); 393 } 394 395 @Nullable 396 public String getHueAdjustmentDescription() 397 { 398 final String value = _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT); 399 if (value==null) 400 return null; 401 return value + " degrees"; 402 } 403 404 @Nullable 405 public String getColorModeDescription() 406 { 407 String value = _directory.getString(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_CAMERA_COLOR_MODE); 408 if (value==null) 409 return null; 410 if (value.startsWith("MODE1")) 411 return "Mode I (sRGB)"; 412 return value; 413 } 414 415 @Nullable 416 public String getFirmwareVersionDescription() 417 { 418 int[] values = _directory.getIntArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_FIRMWARE_VERSION); 419 if (values==null) 420 return null; 421 return ExifSubIFDDescriptor.convertBytesToVersionString(values, 2); 422 } 423 } -
src/com/drew/metadata/exif/NikonType2MakernoteDirectory.java
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 * metadata_extractor [at] drewnoakes [dot] com 12 * Latest version of this software kept at 13 * http://drewnoakes.com/ 14 * 15 * Created by dnoakes on 3-Oct-2002 10:10:47 using IntelliJ IDEA. 16 */ 17 package com.drew.metadata.exif; 18 19 import com.drew.lang.Rational; 20 import com.drew.metadata.Directory; 21 import com.drew.metadata.MetadataException; 22 23 import java.util.HashMap; 24 25 /** 26 * Describes tags specific to Nikon (type 2) cameras. Type-2 applies to the E990 and D-series cameras such as the E990, D1, 27 * D70 and D100. 28 * 29 * Thanks to Fabrizio Giudici for publishing his reverse-engineering of the D100 makernote data. 30 * http://www.timelesswanderings.net/equipment/D100/NEF.html 31 * 32 * Note that the camera implements image protection (locking images) via the file's 'readonly' attribute. Similarly 33 * image hiding uses the 'hidden' attribute (observed on the D70). Consequently, these values are not available here. 34 * 35 * Additional sample images have been observed, and their tag values recorded in javadoc comments for each tag's field. 36 * New tags have subsequently been added since Fabrizio's observations. 37 * 38 * In earlier models (such as the E990 and D1), this directory begins at the first byte of the makernote IFD. In 39 * later models, the IFD was given the standard prefix to indicate the camera models (most other manufacturers also 40 * provide this prefix to aid in software decoding). 41 */ 42 public class NikonType2MakernoteDirectory extends Directory 43 { 44 /** 45 * Values observed 46 * - 0200 (D70) 47 * - 0200 (D1X) 48 */ 49 public static final int TAG_NIKON_TYPE2_FIRMWARE_VERSION = 0x0001; 50 51 /** 52 * Values observed 53 * - 0 250 54 * - 0 400 55 */ 56 public static final int TAG_NIKON_TYPE2_ISO_1 = 0x0002; 57 58 /** 59 * Values observed 60 * - COLOR (seen in the D1X) 61 */ 62 public static final int TAG_NIKON_TYPE2_COLOR_MODE = 0x0003; 63 64 /** 65 * Values observed 66 * - FILE 67 * - RAW 68 * - NORMAL 69 * - FINE 70 */ 71 public static final int TAG_NIKON_TYPE2_QUALITY_AND_FILE_FORMAT = 0x0004; 72 73 /** 74 * The white balance as set in the camera. 75 * 76 * Values observed 77 * - AUTO 78 * - SUNNY (D70) 79 * - FLASH (D1X) 80 * (presumably also SHADOW / INCANDESCENT / FLUORESCENT / CLOUDY) 81 */ 82 public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE = 0x0005; 83 84 /** 85 * The sharpening as set in the camera. 86 * 87 * Values observed 88 * - AUTO 89 * - NORMAL (D70) 90 * - NONE (D1X) 91 */ 92 public static final int TAG_NIKON_TYPE2_CAMERA_SHARPENING = 0x0006; 93 94 /** 95 * The auto-focus type used by the camera. 96 * 97 * Values observed 98 * - AF-S 99 * - AF-C 100 * - MANUAL 101 */ 102 public static final int TAG_NIKON_TYPE2_AF_TYPE = 0x0007; 103 104 /** 105 * Values observed 106 * - NORMAL 107 * - RED-EYE 108 * 109 * Note: when TAG_NIKON_TYPE2_AUTO_FLASH_MODE is blank, Nikon Browser displays "Flash Sync Mode: Not Attached" 110 */ 111 public static final int TAG_NIKON_TYPE2_FLASH_SYNC_MODE = 0x0008; 112 113 /** 114 * Values observed 115 * - Built-in,TTL 116 * - Optional,TTL (with speedlight SB800, flash sync mode as NORMAL. NikonBrowser reports Auto Flash Comp: 0 EV -- which tag is that?) (D70) 117 * - NEW_TTL (Nikon Browser interprets as "D-TTL") 118 * - (blank -- accompanied FlashSyncMode of NORMAL) (D70) 119 */ 120 public static final int TAG_NIKON_TYPE2_AUTO_FLASH_MODE = 0x0009; 121 122 /** 123 * Added during merge of Type2 & Type3. May apply to earlier models, such as E990 and D1. 124 */ 125 public static final int TAG_NIKON_TYPE2_UNKNOWN_34 = 0x000A; 126 127 /** 128 * Values observed 129 * - 0 130 */ 131 public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_FINE = 0x000B; 132 133 /** 134 * The first two numbers are coefficients to multiply red and blue channels according to white balance as set in the 135 * camera. The meaning of the third and the fourth numbers is unknown. 136 * 137 * Values observed 138 * - 2.25882352 1.76078431 0.0 0.0 139 * - 10242/1 34305/1 0/1 0/1 140 * - 234765625/100000000 1140625/1000000 1/1 1/1 141 */ 142 public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_RB_COEFF = 0x000C; 143 144 /** 145 * Values observed 146 * - 0,1,6,0 (hex) 147 */ 148 public static final int TAG_NIKON_TYPE2_UNKNOWN_1 = 0x000D; 149 150 /** 151 * Values observed 152 * - 0,1,c,0 (hex) 153 */ 154 public static final int TAG_NIKON_TYPE2_UNKNOWN_2 = 0x000E; 155 156 /** 157 * Added during merge of Type2 & Type3. May apply to earlier models, such as E990 and D1. 158 */ 159 public static final int TAG_NIKON_TYPE2_ISO_SELECTION = 0x000F; 160 161 /** 162 * Added during merge of Type2 & Type3. May apply to earlier models, such as E990 and D1. 163 */ 164 public static final int TAG_NIKON_TYPE2_DATA_DUMP = 0x0010; 165 166 /** 167 * Values observed 168 * - 914 169 * - 1379 (D70) 170 * - 2781 (D1X) 171 * - 6942 (D100) 172 */ 173 public static final int TAG_NIKON_TYPE2_UNKNOWN_3 = 0x0011; 174 175 /** 176 * Values observed 177 * - (no value -- blank) 178 */ 179 public static final int TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION = 0x0012; 180 181 /** 182 * Values observed 183 * - 0 250 184 * - 0 400 185 */ 186 public static final int TAG_NIKON_TYPE2_ISO_2 = 0x0013; 187 188 /** 189 * Values observed 190 * - 0 0 49163 53255 191 * - 0 0 3008 2000 (the image dimensions were 3008x2000) (D70) 192 */ 193 public static final int TAG_NIKON_TYPE2_UNKNOWN_21 = 0x0016; 194 195 /** 196 * Values observed 197 * - (blank) 198 */ 199 public static final int TAG_NIKON_TYPE2_UNKNOWN_22 = 0x0017; 200 201 /** 202 * Values observed 203 * - (blank) 204 */ 205 public static final int TAG_NIKON_TYPE2_UNKNOWN_23 = 0x0018; 206 207 /** 208 * Values observed 209 * - 0 210 */ 211 public static final int TAG_NIKON_TYPE2_UNKNOWN_24 = 0x0019; 212 213 /** 214 * Added during merge of Type2 & Type3. May apply to earlier models, such as E990 and D1. 215 */ 216 public static final int TAG_NIKON_TYPE2_IMAGE_ADJUSTMENT = 0x0080; 217 218 /** 219 * The tone compensation as set in the camera. 220 * 221 * Values observed 222 * - AUTO 223 * - NORMAL (D1X, D100) 224 */ 225 public static final int TAG_NIKON_TYPE2_CAMERA_TONE_COMPENSATION = 0x0081; 226 227 /** 228 * Added during merge of Type2 & Type3. May apply to earlier models, such as E990 and D1. 229 */ 230 public static final int TAG_NIKON_TYPE2_ADAPTER = 0x0082; 231 232 /** 233 * Values observed 234 * - 6 235 * - 6 (D70) 236 * - 2 (D1X) 237 */ 238 public static final int TAG_NIKON_TYPE2_UNKNOWN_4 = 0x0083; 239 240 /** 241 * A pair of focal/max-fstop values that describe the lens used. 242 * 243 * Values observed 244 * - 180.0,180.0,2.8,2.8 (D100) 245 * - 240/10 850/10 35/10 45/10 246 * - 18-70mm f/3.5-4.5 (D70) 247 * - 17-35mm f/2.8-2.8 (D1X) 248 * - 70-200mm f/2.8-2.8 (D70) 249 * 250 * Nikon Browser identifies the lens as "18-70mm F/3.5-4.5 G" which 251 * is identical to metadata extractor, except for the "G". This must 252 * be coming from another tag... 253 */ 254 public static final int TAG_NIKON_TYPE2_LENS = 0x0084; 255 256 /** 257 * Added during merge of Type2 & Type3. May apply to earlier models, such as E990 and D1. 258 */ 259 public static final int TAG_NIKON_TYPE2_MANUAL_FOCUS_DISTANCE = 0x0085; 260 261 /** 262 * Added during merge of Type2 & Type3. May apply to earlier models, such as E990 and D1. 263 */ 264 public static final int TAG_NIKON_TYPE2_DIGITAL_ZOOM = 0x0086; 265 266 /** 267 * Values observed 268 * - 0 269 * - 9 270 * - 3 (D1X) 271 */ 272 public static final int TAG_NIKON_TYPE2_UNKNOWN_5 = 0x0087; 273 274 /** 275 * Values observed 276 * - 277 */ 278 public static final int TAG_NIKON_TYPE2_AF_FOCUS_POSITION = 0x0088; 279 280 /** 281 * Values observed 282 * - 0 283 * - 1 284 */ 285 public static final int TAG_NIKON_TYPE2_UNKNOWN_7 = 0x0089; 286 287 /** 288 * Values observed 289 * - 0 290 * - 0 291 */ 292 public static final int TAG_NIKON_TYPE2_UNKNOWN_20 = 0x008A; 293 294 /** 295 * Values observed 296 * - 48,1,c,0 (hex) (D100) 297 * - @ <hex> 298 */ 299 public static final int TAG_NIKON_TYPE2_UNKNOWN_8 = 0x008B; 300 301 /** 302 * Unknown. Fabrizio believes this may be a lookup table for the user-defined curve. 303 * 304 * Values observed 305 * - (blank) (D1X) 306 */ 307 public static final int TAG_NIKON_TYPE2_UNKNOWN_9 = 0x008C; 308 309 /** 310 * The color space as set in the camera. 311 * 312 * Values observed 313 * - MODE1 314 * - Mode I (sRGB) (D70) 315 * - MODE2 (D1X, D100) 316 */ 317 public static final int TAG_NIKON_TYPE2_CAMERA_COLOR_MODE = 0x008D; 318 319 /** 320 * Values observed 321 * - NATURAL 322 * - SPEEDLIGHT (D70, D1X) 323 */ 324 public static final int TAG_NIKON_TYPE2_LIGHT_SOURCE = 0x0090; 325 326 /** 327 * Values observed 328 * - 0100 <hex> 329 * - 0103 (D70) 330 * - 0100 (D1X) 331 */ 332 public static final int TAG_NIKON_TYPE2_UNKNOWN_11 = 0x0091; 333 334 /** 335 * The hue adjustment as set in the camera. 336 * 337 * Values observed 338 * - 0 339 */ 340 public static final int TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT = 0x0092; 341 342 /** 343 * Values observed 344 * - OFF 345 */ 346 public static final int TAG_NIKON_TYPE2_NOISE_REDUCTION = 0x0095; 347 348 /** 349 * Values observed 350 * - 0100 <hex> 351 * - 0103 <hex> 352 */ 353 public static final int TAG_NIKON_TYPE2_UNKNOWN_12 = 0x0097; 354 355 /** 356 * Values observed 357 * - 0100 <hex> 358 * - 0101 <hex> 359 * - 0100 <hex> (D1X) 360 * - 30,31,30,30,0,0,b,48,7c,7c,24,24,5,15,24,0,0,0,0,0 (hex) (D100) 361 */ 362 public static final int TAG_NIKON_TYPE2_UNKNOWN_13 = 0x0098; 363 364 /** 365 * Values observed 366 * - 2014 662 (D1X) 367 * - 1517,1012 (D100) 368 */ 369 public static final int TAG_NIKON_TYPE2_UNKNOWN_14 = 0x0099; 370 371 /** 372 * Values observed 373 * - 78/10 78/10 374 * - 78/10 78/10 (D70) 375 * - 59/10 59/5 (D1X) 376 * - 7.8,7.8 (D100) 377 */ 378 public static final int TAG_NIKON_TYPE2_UNKNOWN_15 = 0x009A; 379 380 /** 381 * Values observed 382 * - NO= 00002539 383 */ 384 public static final int TAG_NIKON_TYPE2_UNKNOWN_25 = 0x00A0; 385 386 /** 387 * Values observed 388 * - 1564851 389 */ 390 public static final int TAG_NIKON_TYPE2_UNKNOWN_26 = 0x00A2; 391 392 /** 393 * Values observed 394 * - 0 395 */ 396 public static final int TAG_NIKON_TYPE2_UNKNOWN_27 = 0x00A3; 397 398 /** 399 * This appears to be a sequence number to indentify the exposure. This value seems to increment 400 * for consecutive exposures (observed on D70). 401 * 402 * Values observed 403 * - 5062 404 */ 405 public static final int TAG_NIKON_TYPE2_EXPOSURE_SEQUENCE_NUMBER = 0x00A7; 406 407 /** 408 * Values observed 409 * - 0100 (D70) 410 */ 411 public static final int TAG_NIKON_TYPE2_UNKNOWN_32 = 0x00A8; 412 413 /** 414 * Values observed 415 * - NORMAL (D70) 416 */ 417 public static final int TAG_NIKON_TYPE2_UNKNOWN_33 = 0x00A9; 418 419 /** 420 * Nikon Browser suggests this value represents Saturation... 421 * Values observed 422 * - NORMAL (D70) 423 */ 424 public static final int TAG_NIKON_TYPE2_UNKNOWN_29 = 0x00AA; 425 426 /** 427 * Values observed 428 * - AUTO (D70) 429 * - (blank) (D70) 430 */ 431 public static final int TAG_NIKON_TYPE2_UNKNOWN_30 = 0x00AB; 432 433 /** 434 * Data about changes set by Nikon Capture Editor. 435 * 436 * Values observed 437 */ 438 public static final int TAG_NIKON_TYPE2_CAPTURE_EDITOR_DATA = 0x0E01; 439 440 /** 441 * Values observed 442 * - 1473 443 * - 7036 (D100) 444 */ 445 public static final int TAG_NIKON_TYPE2_UNKNOWN_16 = 0x0E10; 446 447 protected static final HashMap _tagNameMap = new HashMap(); 448 449 static 450 { 451 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_FIRMWARE_VERSION), "Firmware Version"); 452 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_ISO_1), "ISO"); 453 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_QUALITY_AND_FILE_FORMAT), "Quality & File Format"); 454 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE), "White Balance"); 455 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_SHARPENING), "Sharpening"); 456 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_AF_TYPE), "AF Type"); 457 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_FINE), "White Balance Fine"); 458 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_RB_COEFF), "White Balance RB Coefficients"); 459 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_ISO_2), "ISO"); 460 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_ISO_SELECTION), "ISO Selection"); 461 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_DATA_DUMP), "Data Dump"); 462 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_IMAGE_ADJUSTMENT), "Image Adjustment"); 463 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_TONE_COMPENSATION), "Tone Compensation"); 464 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_ADAPTER), "Adapter"); 465 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_LENS), "Lens"); 466 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_MANUAL_FOCUS_DISTANCE), "Manual Focus Distance"); 467 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_DIGITAL_ZOOM), "Digital Zoom"); 468 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_COLOR_MODE), "Colour Mode"); 469 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT), "Camera Hue Adjustment"); 470 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_NOISE_REDUCTION), "Noise Reduction"); 471 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_CAPTURE_EDITOR_DATA), "Capture Editor Data"); 472 473 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_1), "Unknown 01"); 474 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_2), "Unknown 02"); 475 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_3), "Unknown 03"); 476 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_4), "Unknown 04"); 477 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_5), "Unknown 05"); 478 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_AF_FOCUS_POSITION), "AF Focus Position"); 479 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_7), "Unknown 07"); 480 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_8), "Unknown 08"); 481 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_9), "Unknown 09"); 482 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_LIGHT_SOURCE), "Light source"); 483 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_11), "Unknown 11"); 484 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_12), "Unknown 12"); 485 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_13), "Unknown 13"); 486 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_14), "Unknown 14"); 487 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_15), "Unknown 15"); 488 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_16), "Unknown 16"); 489 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_FLASH_SYNC_MODE), "Flash Sync Mode"); 490 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_AUTO_FLASH_MODE), "Auto Flash Mode"); 491 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION), "Auto Flash Compensation"); 492 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_EXPOSURE_SEQUENCE_NUMBER), "Exposure Sequence Number"); 493 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_COLOR_MODE), "Color Mode"); 494 495 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_20), "Unknown 20"); 496 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_21), "Unknown 21"); 497 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_22), "Unknown 22"); 498 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_23), "Unknown 23"); 499 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_24), "Unknown 24"); 500 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_25), "Unknown 25"); 501 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_26), "Unknown 26"); 502 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_27), "Unknown 27"); 503 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_29), "Unknown 29"); 504 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_30), "Unknown 30"); 505 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_32), "Unknown 32"); 506 _tagNameMap.put(new Integer(TAG_NIKON_TYPE2_UNKNOWN_33), "Unknown 33"); 507 } 508 509 public NikonType2MakernoteDirectory() 510 { 511 this.setDescriptor(new NikonType2MakernoteDescriptor(this)); 512 } 513 514 public Rational getAutoFlashCompensation() throws MetadataException 515 { 516 if (!containsTag(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION)) 517 return null; 518 519 byte[] bytes = getByteArray(NikonType2MakernoteDirectory.TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION); 520 return CalculateFlashCompensationFromBytes(bytes); 521 } 522 523 public static Rational CalculateFlashCompensationFromBytes(byte[] bytes) 524 { 525 if (bytes.length==3) 526 { 527 byte denominator = bytes[2]; 528 int numerator = (int)bytes[0] * bytes[1]; 529 return new Rational(numerator, denominator); 530 } 531 return null; 532 } 533 534 public String getName() 535 { 536 return "Nikon Makernote"; 537 } 538 539 protected HashMap getTagNameMap() 540 { 541 return _tagNameMap; 542 } 543 } 1 /* 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/ 20 */ 21 package com.drew.metadata.exif; 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.metadata.Directory; 25 26 import java.util.HashMap; 27 28 /** 29 * Describes tags specific to Nikon (type 2) cameras. Type-2 applies to the E990 and D-series cameras such as the E990, D1, 30 * D70 and D100. 31 * <p/> 32 * Thanks to Fabrizio Giudici for publishing his reverse-engineering of the D100 makernote data. 33 * http://www.timelesswanderings.net/equipment/D100/NEF.html 34 * <p/> 35 * Note that the camera implements image protection (locking images) via the file's 'readonly' attribute. Similarly 36 * image hiding uses the 'hidden' attribute (observed on the D70). Consequently, these values are not available here. 37 * <p/> 38 * Additional sample images have been observed, and their tag values recorded in javadoc comments for each tag's field. 39 * New tags have subsequently been added since Fabrizio's observations. 40 * <p/> 41 * In earlier models (such as the E990 and D1), this directory begins at the first byte of the makernote IFD. In 42 * later models, the IFD was given the standard prefix to indicate the camera models (most other manufacturers also 43 * provide this prefix to aid in software decoding). 44 * 45 * @author Drew Noakes http://drewnoakes.com 46 */ 47 public class NikonType2MakernoteDirectory extends Directory 48 { 49 /** 50 * Values observed 51 * - 0200 (D70) 52 * - 0200 (D1X) 53 */ 54 public static final int TAG_NIKON_TYPE2_FIRMWARE_VERSION = 0x0001; 55 56 /** 57 * Values observed 58 * - 0 250 59 * - 0 400 60 */ 61 public static final int TAG_NIKON_TYPE2_ISO_1 = 0x0002; 62 63 /** 64 * The camera's color mode, as an uppercase string. Examples include: 65 * <ul> 66 * <li><code>B & W</code></li> 67 * <li><code>COLOR</code></li> 68 * <li><code>COOL</code></li> 69 * <li><code>SEPIA</code></li> 70 * <li><code>VIVID</code></li> 71 * </ul> 72 */ 73 public static final int TAG_NIKON_TYPE2_COLOR_MODE = 0x0003; 74 75 /** 76 * The camera's quality setting, as an uppercase string. Examples include: 77 * <ul> 78 * <li><code>BASIC</code></li> 79 * <li><code>FINE</code></li> 80 * <li><code>NORMAL</code></li> 81 * <li><code>RAW</code></li> 82 * <li><code>RAW2.7M</code></li> 83 * </ul> 84 */ 85 public static final int TAG_NIKON_TYPE2_QUALITY_AND_FILE_FORMAT = 0x0004; 86 87 /** 88 * The camera's white balance setting, as an uppercase string. Examples include: 89 * 90 * <ul> 91 * <li><code>AUTO</code></li> 92 * <li><code>CLOUDY</code></li> 93 * <li><code>FLASH</code></li> 94 * <li><code>FLUORESCENT</code></li> 95 * <li><code>INCANDESCENT</code></li> 96 * <li><code>PRESET</code></li> 97 * <li><code>PRESET0</code></li> 98 * <li><code>PRESET1</code></li> 99 * <li><code>PRESET3</code></li> 100 * <li><code>SUNNY</code></li> 101 * <li><code>WHITE PRESET</code></li> 102 * <li><code>4350K</code></li> 103 * <li><code>5000K</code></li> 104 * <li><code>DAY WHITE FL</code></li> 105 * <li><code>SHADE</code></li> 106 * </ul> 107 */ 108 public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE = 0x0005; 109 110 /** 111 * The camera's sharpening setting, as an uppercase string. Examples include: 112 * 113 * <ul> 114 * <li><code>AUTO</code></li> 115 * <li><code>HIGH</code></li> 116 * <li><code>LOW</code></li> 117 * <li><code>NONE</code></li> 118 * <li><code>NORMAL</code></li> 119 * <li><code>MED.H</code></li> 120 * <li><code>MED.L</code></li> 121 * </ul> 122 */ 123 public static final int TAG_NIKON_TYPE2_CAMERA_SHARPENING = 0x0006; 124 125 /** 126 * The camera's auto-focus mode, as an uppercase string. Examples include: 127 * 128 * <ul> 129 * <li><code>AF-C</code></li> 130 * <li><code>AF-S</code></li> 131 * <li><code>MANUAL</code></li> 132 * <li><code>AF-A</code></li> 133 * </ul> 134 */ 135 public static final int TAG_NIKON_TYPE2_AF_TYPE = 0x0007; 136 137 /** 138 * The camera's flash setting, as an uppercase string. Examples include: 139 * 140 * <ul> 141 * <li><code></code></li> 142 * <li><code>NORMAL</code></li> 143 * <li><code>RED-EYE</code></li> 144 * <li><code>SLOW</code></li> 145 * <li><code>NEW_TTL</code></li> 146 * <li><code>REAR</code></li> 147 * <li><code>REAR SLOW</code></li> 148 * </ul> 149 * Note: when TAG_NIKON_TYPE2_AUTO_FLASH_MODE is blank (whitespace), Nikon Browser displays "Flash Sync Mode: Not Attached" 150 */ 151 public static final int TAG_NIKON_TYPE2_FLASH_SYNC_MODE = 0x0008; 152 153 /** 154 * The type of flash used in the photograph, as a string. Examples include: 155 * 156 * <ul> 157 * <li><code></code></li> 158 * <li><code>Built-in,TTL</code></li> 159 * <li><code>NEW_TTL</code> Nikon Browser interprets as "D-TTL"</li> 160 * <li><code>Built-in,M</code></li> 161 * <li><code>Optional,TTL</code> with speedlight SB800, flash sync mode as "NORMAL"</li> 162 * </ul> 163 */ 164 public static final int TAG_NIKON_TYPE2_AUTO_FLASH_MODE = 0x0009; 165 166 /** 167 * An unknown tag, as a rational. Several values given here: 168 * http://gvsoft.homedns.org/exif/makernote-nikon-type2.html#0x000b 169 */ 170 public static final int TAG_NIKON_TYPE2_UNKNOWN_34 = 0x000A; 171 172 /** 173 * The camera's white balance bias setting, as an uint16 array having either one or two elements. 174 * 175 * <ul> 176 * <li><code>0</code></li> 177 * <li><code>1</code></li> 178 * <li><code>-3</code></li> 179 * <li><code>-2</code></li> 180 * <li><code>-1</code></li> 181 * <li><code>0,0</code></li> 182 * <li><code>1,0</code></li> 183 * <li><code>5,-5</code></li> 184 * </ul> 185 */ 186 public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_FINE = 0x000B; 187 188 /** 189 * The first two numbers are coefficients to multiply red and blue channels according to white balance as set in the 190 * camera. The meaning of the third and the fourth numbers is unknown. 191 * 192 * Values observed 193 * - 2.25882352 1.76078431 0.0 0.0 194 * - 10242/1 34305/1 0/1 0/1 195 * - 234765625/100000000 1140625/1000000 1/1 1/1 196 */ 197 public static final int TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_RB_COEFF = 0x000C; 198 199 /** 200 * The camera's program shift setting, as an array of four integers. 201 * The value, in EV, is calculated as <code>a*b/c</code>. 202 * 203 * <ul> 204 * <li><code>0,1,3,0</code> = 0 EV</li> 205 * <li><code>1,1,3,0</code> = 0.33 EV</li> 206 * <li><code>-3,1,3,0</code> = -1 EV</li> 207 * <li><code>1,1,2,0</code> = 0.5 EV</li> 208 * <li><code>2,1,6,0</code> = 0.33 EV</li> 209 * </ul> 210 */ 211 public static final int TAG_NIKON_TYPE2_PROGRAM_SHIFT = 0x000D; 212 213 /** 214 * The exposure difference, as an array of four integers. 215 * The value, in EV, is calculated as <code>a*b/c</code>. 216 * 217 * <ul> 218 * <li><code>-105,1,12,0</code> = -8.75 EV</li> 219 * <li><code>-72,1,12,0</code> = -6.00 EV</li> 220 * <li><code>-11,1,12,0</code> = -0.92 EV</li> 221 * </ul> 222 */ 223 public static final int TAG_NIKON_TYPE2_EXPOSURE_DIFFERENCE = 0x000E; 224 225 /** 226 * The camera's ISO mode, as an uppercase string. 227 * 228 * <ul> 229 * <li><code>AUTO</code></code></li> 230 * <li><code>MANUAL</code></li> 231 * </ul> 232 */ 233 public static final int TAG_NIKON_TYPE2_ISO_MODE = 0x000F; 234 235 /** 236 * Added during merge of Type2 & Type3. May apply to earlier models, such as E990 and D1. 237 */ 238 public static final int TAG_NIKON_TYPE2_DATA_DUMP = 0x0010; 239 240 /** 241 * Preview to another IFD (?) 242 * <p/> 243 * Details here: http://gvsoft.homedns.org/exif/makernote-nikon-2-tag0x0011.html 244 * // TODO if this is another IFD, decode it 245 */ 246 public static final int TAG_NIKON_TYPE2_PREVIEW_IFD = 0x0011; 247 248 /** 249 * The flash compensation, as an array of four integers. 250 * The value, in EV, is calculated as <code>a*b/c</code>. 251 * 252 * <ul> 253 * <li><code>-18,1,6,0</code> = -3 EV</li> 254 * <li><code>4,1,6,0</code> = 0.67 EV</li> 255 * <li><code>6,1,6,0</code> = 1 EV</li> 256 * </ul> 257 */ 258 public static final int TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION = 0x0012; 259 260 /** 261 * The requested ISO value, as an array of two integers. 262 * 263 * <ul> 264 * <li><code>0,0</code></li> 265 * <li><code>0,125</code></li> 266 * <li><code>1,2500</code></li> 267 * </ul> 268 */ 269 public static final int TAG_NIKON_TYPE2_ISO_REQUESTED = 0x0013; 270 271 /** 272 * Defines the photo corner coordinates, in 8 bytes. Treated as four 16-bit integers, they 273 * decode as: top-left (x,y); bot-right (x,y) 274 * - 0 0 49163 53255 275 * - 0 0 3008 2000 (the image dimensions were 3008x2000) (D70) 276 * <ul> 277 * <li><code>0,0,4288,2848</code> The max resolution of the D300 camera</li> 278 * <li><code>0,0,3008,2000</code> The max resolution of the D70 camera</li> 279 * <li><code>0,0,4256,2832</code> The max resolution of the D3 camera</li> 280 * </ul> 281 */ 282 public static final int TAG_NIKON_TYPE2_IMAGE_BOUNDARY = 0x0016; 283 284 /** 285 * The flash exposure compensation, as an array of four integers. 286 * The value, in EV, is calculated as <code>a*b/c</code>. 287 * 288 * <ul> 289 * <li><code>0,0,0,0</code> = 0 EV</li> 290 * <li><code>0,1,6,0</code> = 0 EV</li> 291 * <li><code>4,1,6,0</code> = 0.67 EV</li> 292 * </ul> 293 */ 294 public static final int TAG_NIKON_TYPE2_FLASH_EXPOSURE_COMPENSATION = 0x0017; 295 296 /** 297 * The flash bracket compensation, as an array of four integers. 298 * The value, in EV, is calculated as <code>a*b/c</code>. 299 * 300 * <ul> 301 * <li><code>0,0,0,0</code> = 0 EV</li> 302 * <li><code>0,1,6,0</code> = 0 EV</li> 303 * <li><code>4,1,6,0</code> = 0.67 EV</li> 304 * </ul> 305 */ 306 public static final int TAG_NIKON_TYPE2_FLASH_BRACKET_COMPENSATION = 0x0018; 307 308 /** 309 * The AE bracket compensation, as a rational number. 310 * 311 * <ul> 312 * <li><code>0/0</code></li> 313 * <li><code>0/1</code></li> 314 * <li><code>0/6</code></li> 315 * <li><code>4/6</code></li> 316 * <li><code>6/6</code></li> 317 * </ul> 318 */ 319 public static final int TAG_NIKON_TYPE2_AE_BRACKET_COMPENSATION = 0x0019; 320 321 /** 322 * Flash mode, as a string. 323 * 324 * <ul> 325 * <li><code></code></li> 326 * <li><code>Red Eye Reduction</code></li> 327 * <li><code>D-Lighting</code></li> 328 * <li><code>Distortion control</code></li> 329 * </ul> 330 */ 331 public static final int TAG_NIKON_TYPE2_FLASH_MODE = 0x001a; 332 333 public static final int TAG_NIKON_TYPE2_CROP_HIGH_SPEED = 0x001b; 334 public static final int TAG_NIKON_TYPE2_EXPOSURE_TUNING = 0x001c; 335 336 /** 337 * The camera's serial number, as a string. 338 * Note that D200 is always blank, and D50 is always <code>"D50"</code>. 339 */ 340 public static final int TAG_NIKON_TYPE2_CAMERA_SERIAL_NUMBER = 0x001d; 341 342 /** 343 * The camera's color space setting. 344 * 345 * <ul> 346 * <li><code>1</code> sRGB</li> 347 * <li><code>2</code> Adobe RGB</li> 348 * </ul> 349 */ 350 public static final int TAG_NIKON_TYPE2_COLOR_SPACE = 0x001e; 351 public static final int TAG_NIKON_TYPE2_VR_INFO = 0x001f; 352 public static final int TAG_NIKON_TYPE2_IMAGE_AUTHENTICATION = 0x0020; 353 public static final int TAG_NIKON_TYPE2_UNKNOWN_35 = 0x0021; 354 355 /** 356 * The active D-Lighting setting. 357 * 358 * <ul> 359 * <li><code>0</code> Off</li> 360 * <li><code>1</code> Low</li> 361 * <li><code>3</code> Normal</li> 362 * <li><code>5</code> High</li> 363 * <li><code>7</code> Extra High</li> 364 * <li><code>65535</code> Auto</li> 365 * </ul> 366 */ 367 public static final int TAG_NIKON_TYPE2_ACTIVE_D_LIGHTING = 0x0022; 368 public static final int TAG_NIKON_TYPE2_PICTURE_CONTROL = 0x0023; 369 public static final int TAG_NIKON_TYPE2_WORLD_TIME = 0x0024; 370 public static final int TAG_NIKON_TYPE2_ISO_INFO = 0x0025; 371 public static final int TAG_NIKON_TYPE2_UNKNOWN_36 = 0x0026; 372 public static final int TAG_NIKON_TYPE2_UNKNOWN_37 = 0x0027; 373 public static final int TAG_NIKON_TYPE2_UNKNOWN_38 = 0x0028; 374 public static final int TAG_NIKON_TYPE2_UNKNOWN_39 = 0x0029; 375 376 /** 377 * The camera's vignette control setting. 378 * 379 * <ul> 380 * <li><code>0</code> Off</li> 381 * <li><code>1</code> Low</li> 382 * <li><code>3</code> Normal</li> 383 * <li><code>5</code> High</li> 384 * </ul> 385 */ 386 public static final int TAG_NIKON_TYPE2_VIGNETTE_CONTROL = 0x002a; 387 public static final int TAG_NIKON_TYPE2_UNKNOWN_40 = 0x002b; 388 public static final int TAG_NIKON_TYPE2_UNKNOWN_41 = 0x002c; 389 public static final int TAG_NIKON_TYPE2_UNKNOWN_42 = 0x002d; 390 public static final int TAG_NIKON_TYPE2_UNKNOWN_43 = 0x002e; 391 public static final int TAG_NIKON_TYPE2_UNKNOWN_44 = 0x002f; 392 public static final int TAG_NIKON_TYPE2_UNKNOWN_45 = 0x0030; 393 public static final int TAG_NIKON_TYPE2_UNKNOWN_46 = 0x0031; 394 395 /** 396 * The camera's image adjustment setting, as a string. 397 * 398 * <ul> 399 * <li><code>AUTO</code></li> 400 * <li><code>CONTRAST(+)</code></li> 401 * <li><code>CONTRAST(-)</code></li> 402 * <li><code>NORMAL</code></li> 403 * <li><code>B & W</code></li> 404 * <li><code>BRIGHTNESS(+)</code></li> 405 * <li><code>BRIGHTNESS(-)</code></li> 406 * <li><code>SEPIA</code></li> 407 * </ul> 408 */ 409 public static final int TAG_NIKON_TYPE2_IMAGE_ADJUSTMENT = 0x0080; 410 411 /** 412 * The camera's tone compensation setting, as a string. 413 * 414 * <ul> 415 * <li><code>NORMAL</code></li> 416 * <li><code>LOW</code></li> 417 * <li><code>MED.L</code></li> 418 * <li><code>MED.H</code></li> 419 * <li><code>HIGH</code></li> 420 * <li><code>AUTO</code></li> 421 * </ul> 422 */ 423 public static final int TAG_NIKON_TYPE2_CAMERA_TONE_COMPENSATION = 0x0081; 424 425 /** 426 * A description of any auxiliary lens, as a string. 427 * 428 * <ul> 429 * <li><code>OFF</code></li> 430 * <li><code>FISHEYE 1</code></li> 431 * <li><code>FISHEYE 2</code></li> 432 * <li><code>TELEPHOTO 2</code></li> 433 * <li><code>WIDE ADAPTER</code></li> 434 * </ul> 435 */ 436 public static final int TAG_NIKON_TYPE2_ADAPTER = 0x0082; 437 438 /** 439 * The type of lens used, as a byte. 440 * 441 * <ul> 442 * <li><code>0x00</code> AF</li> 443 * <li><code>0x01</code> MF</li> 444 * <li><code>0x02</code> D</li> 445 * <li><code>0x06</code> G, D</li> 446 * <li><code>0x08</code> VR</li> 447 * <li><code>0x0a</code> VR, D</li> 448 * <li><code>0x0e</code> VR, G, D</li> 449 * </ul> 450 */ 451 public static final int TAG_NIKON_TYPE2_LENS_TYPE = 0x0083; 452 453 /** 454 * A pair of focal/max-fstop values that describe the lens used. 455 * 456 * Values observed 457 * - 180.0,180.0,2.8,2.8 (D100) 458 * - 240/10 850/10 35/10 45/10 459 * - 18-70mm f/3.5-4.5 (D70) 460 * - 17-35mm f/2.8-2.8 (D1X) 461 * - 70-200mm f/2.8-2.8 (D70) 462 * 463 * Nikon Browser identifies the lens as "18-70mm F/3.5-4.5 G" which 464 * is identical to metadata extractor, except for the "G". This must 465 * be coming from another tag... 466 */ 467 public static final int TAG_NIKON_TYPE2_LENS = 0x0084; 468 469 /** 470 * Added during merge of Type2 & Type3. May apply to earlier models, such as E990 and D1. 471 */ 472 public static final int TAG_NIKON_TYPE2_MANUAL_FOCUS_DISTANCE = 0x0085; 473 474 /** 475 * The amount of digital zoom used. 476 */ 477 public static final int TAG_NIKON_TYPE2_DIGITAL_ZOOM = 0x0086; 478 479 /** 480 * Whether the flash was used in this image. 481 * 482 * <ul> 483 * <li><code>0</code> Flash Not Used</li> 484 * <li><code>1</code> Manual Flash</li> 485 * <li><code>3</code> Flash Not Ready</li> 486 * <li><code>7</code> External Flash</li> 487 * <li><code>8</code> Fired, Commander Mode</li> 488 * <li><code>9</code> Fired, TTL Mode</li> 489 * </ul> 490 */ 491 public static final int TAG_NIKON_TYPE2_FLASH_USED = 0x0087; 492 493 /** 494 * The position of the autofocus target. 495 */ 496 public static final int TAG_NIKON_TYPE2_AF_FOCUS_POSITION = 0x0088; 497 498 /** 499 * The camera's shooting mode. 500 * <p/> 501 * A bit-array with: 502 * <ul> 503 * <li><code>0</code> Single Frame</li> 504 * <li><code>1</code> Continuous</li> 505 * <li><code>2</code> Delay</li> 506 * <li><code>8</code> PC Control</li> 507 * <li><code>16</code> Exposure Bracketing</li> 508 * <li><code>32</code> Auto ISO</li> 509 * <li><code>64</code> White-Balance Bracketing</li> 510 * <li><code>128</code> IR Control</li> 511 * </ul> 512 */ 513 public static final int TAG_NIKON_TYPE2_SHOOTING_MODE = 0x0089; 514 515 public static final int TAG_NIKON_TYPE2_UNKNOWN_20 = 0x008A; 516 517 /** 518 * Lens stops, as an array of four integers. 519 * The value, in EV, is calculated as <code>a*b/c</code>. 520 * 521 * <ul> 522 * <li><code>64,1,12,0</code> = 5.33 EV</li> 523 * <li><code>72,1,12,0</code> = 6 EV</li> 524 * </ul> 525 */ 526 public static final int TAG_NIKON_TYPE2_LENS_STOPS = 0x008B; 527 528 public static final int TAG_NIKON_TYPE2_CONTRAST_CURVE = 0x008C; 529 530 /** 531 * The color space as set in the camera, as a string. 532 * 533 * <ul> 534 * <li><code>MODE1</code> = Mode 1 (sRGB)</li> 535 * <li><code>MODE1a</code> = Mode 1 (sRGB)</li> 536 * <li><code>MODE2</code> = Mode 2 (Adobe RGB)</li> 537 * <li><code>MODE3</code> = Mode 2 (sRGB): Higher Saturation</li> 538 * <li><code>MODE3a</code> = Mode 2 (sRGB): Higher Saturation</li> 539 * <li><code>B & W</code> = B & W</li> 540 * </ul> 541 */ 542 public static final int TAG_NIKON_TYPE2_CAMERA_COLOR_MODE = 0x008D; 543 public static final int TAG_NIKON_TYPE2_UNKNOWN_47 = 0x008E; 544 545 /** 546 * The camera's scene mode, as a string. Examples include: 547 * <ul> 548 * <li><code>BEACH/SNOW</code></li> 549 * <li><code>CLOSE UP</code></li> 550 * <li><code>NIGHT PORTRAIT</code></li> 551 * <li><code>PORTRAIT</code></li> 552 * <li><code>ANTI-SHAKE</code></li> 553 * <li><code>BACK LIGHT</code></li> 554 * <li><code>BEST FACE</code></li> 555 * <li><code>BEST</code></li> 556 * <li><code>COPY</code></li> 557 * <li><code>DAWN/DUSK</code></li> 558 * <li><code>FACE-PRIORITY</code></li> 559 * <li><code>FIREWORKS</code></li> 560 * <li><code>FOOD</code></li> 561 * <li><code>HIGH SENS.</code></li> 562 * <li><code>LAND SCAPE</code></li> 563 * <li><code>MUSEUM</code></li> 564 * <li><code>PANORAMA ASSIST</code></li> 565 * <li><code>PARTY/INDOOR</code></li> 566 * <li><code>SCENE AUTO</code></li> 567 * <li><code>SMILE</code></li> 568 * <li><code>SPORT</code></li> 569 * <li><code>SPORT CONT.</code></li> 570 * <li><code>SUNSET</code></li> 571 * </ul> 572 */ 573 public static final int TAG_NIKON_TYPE2_SCENE_MODE = 0x008F; 574 575 /** 576 * The lighting type, as a string. Examples include: 577 * <ul> 578 * <li><code></code></li> 579 * <li><code>NATURAL</code></li> 580 * <li><code>SPEEDLIGHT</code></li> 581 * <li><code>COLORED</code></li> 582 * <li><code>MIXED</code></li> 583 * <li><code>NORMAL</code></li> 584 * </ul> 585 */ 586 public static final int TAG_NIKON_TYPE2_LIGHT_SOURCE = 0x0090; 587 588 /** 589 * Advertised as ASCII, but actually isn't. A variable number of bytes (eg. 18 to 533). Actual number of bytes 590 * appears fixed for a given camera model. 591 */ 592 public static final int TAG_NIKON_TYPE2_SHOT_INFO = 0x0091; 593 594 /** 595 * The hue adjustment as set in the camera. Values observed are either 0 or 3. 596 */ 597 public static final int TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT = 0x0092; 598 /** 599 * The NEF (RAW) compression. Examples include: 600 * <ul> 601 * <li><code>1</code> Lossy (Type 1)</li> 602 * <li><code>2</code> Uncompressed</li> 603 * <li><code>3</code> Lossless</li> 604 * <li><code>4</code> Lossy (Type 2)</li> 605 * </ul> 606 */ 607 public static final int TAG_NIKON_TYPE2_NEF_COMPRESSION = 0x0093; 608 609 /** 610 * The saturation level, as a signed integer. Examples include: 611 * <ul> 612 * <li><code>+3</code></li> 613 * <li><code>+2</code></li> 614 * <li><code>+1</code></li> 615 * <li><code>0</code> Normal</li> 616 * <li><code>-1</code></li> 617 * <li><code>-2</code></li> 618 * <li><code>-3</code> (B&W)</li> 619 * </ul> 620 */ 621 public static final int TAG_NIKON_TYPE2_SATURATION = 0x0094; 622 623 /** 624 * The type of noise reduction, as a string. Examples include: 625 * <ul> 626 * <li><code>OFF</code></li> 627 * <li><code>FPNR</code></li> 628 * </ul> 629 */ 630 public static final int TAG_NIKON_TYPE2_NOISE_REDUCTION = 0x0095; 631 public static final int TAG_NIKON_TYPE2_LINEARIZATION_TABLE = 0x0096; 632 public static final int TAG_NIKON_TYPE2_COLOR_BALANCE = 0x0097; 633 public static final int TAG_NIKON_TYPE2_LENS_DATA = 0x0098; 634 635 /** The NEF (RAW) thumbnail size, as an integer array with two items representing [width,height]. */ 636 public static final int TAG_NIKON_TYPE2_NEF_THUMBNAIL_SIZE = 0x0099; 637 638 /** The sensor pixel size, as a pair of rational numbers. */ 639 public static final int TAG_NIKON_TYPE2_SENSOR_PIXEL_SIZE = 0x009A; 640 public static final int TAG_NIKON_TYPE2_UNKNOWN_10 = 0x009B; 641 public static final int TAG_NIKON_TYPE2_SCENE_ASSIST = 0x009C; 642 public static final int TAG_NIKON_TYPE2_UNKNOWN_11 = 0x009D; 643 public static final int TAG_NIKON_TYPE2_RETOUCH_HISTORY = 0x009E; 644 public static final int TAG_NIKON_TYPE2_UNKNOWN_12 = 0x009F; 645 646 /** 647 * The camera serial number, as a string. 648 * <ul> 649 * <li><code>NO= 00002539</code></li> 650 * <li><code>NO= -1000d71</code></li> 651 * <li><code>PKG597230621263</code></li> 652 * <li><code>PKG5995671330625116</code></li> 653 * <li><code>PKG49981281631130677</code></li> 654 * <li><code>BU672230725063</code></li> 655 * <li><code>NO= 200332c7</code></li> 656 * <li><code>NO= 30045efe</code></li> 657 * </ul> 658 */ 659 public static final int TAG_NIKON_TYPE2_CAMERA_SERIAL_NUMBER_2 = 0x00A0; 660 661 public static final int TAG_NIKON_TYPE2_IMAGE_DATA_SIZE = 0x00A2; 662 663 public static final int TAG_NIKON_TYPE2_UNKNOWN_27 = 0x00A3; 664 public static final int TAG_NIKON_TYPE2_UNKNOWN_28 = 0x00A4; 665 public static final int TAG_NIKON_TYPE2_IMAGE_COUNT = 0x00A5; 666 public static final int TAG_NIKON_TYPE2_DELETED_IMAGE_COUNT = 0x00A6; 667 668 /** The number of total shutter releases. This value increments for each exposure (observed on D70). */ 669 public static final int TAG_NIKON_TYPE2_EXPOSURE_SEQUENCE_NUMBER = 0x00A7; 670 671 public static final int TAG_NIKON_TYPE2_FLASH_INFO = 0x00A8; 672 /** 673 * The camera's image optimisation, as a string. 674 * <ul> 675 * <li><code></code></li> 676 * <li><code>NORMAL</code></li> 677 * <li><code>CUSTOM</code></li> 678 * <li><code>BLACK AND WHITE</code></li> 679 * <li><code>LAND SCAPE</code></li> 680 * <li><code>MORE VIVID</code></li> 681 * <li><code>PORTRAIT</code></li> 682 * <li><code>SOFT</code></li> 683 * <li><code>VIVID</code></li> 684 * </ul> 685 */ 686 public static final int TAG_NIKON_TYPE2_IMAGE_OPTIMISATION = 0x00A9; 687 688 /** 689 * The camera's saturation level, as a string. 690 * <ul> 691 * <li><code></code></li> 692 * <li><code>NORMAL</code></li> 693 * <li><code>AUTO</code></li> 694 * <li><code>ENHANCED</code></li> 695 * <li><code>MODERATE</code></li> 696 * </ul> 697 */ 698 public static final int TAG_NIKON_TYPE2_SATURATION_2 = 0x00AA; 699 700 /** 701 * The camera's digital vari-program setting, as a string. 702 * <ul> 703 * <li><code></code></li> 704 * <li><code>AUTO</code></li> 705 * <li><code>AUTO(FLASH OFF)</code></li> 706 * <li><code>CLOSE UP</code></li> 707 * <li><code>LANDSCAPE</code></li> 708 * <li><code>NIGHT PORTRAIT</code></li> 709 * <li><code>PORTRAIT</code></li> 710 * <li><code>SPORT</code></li> 711 * </ul> 712 */ 713 public static final int TAG_NIKON_TYPE2_DIGITAL_VARI_PROGRAM = 0x00AB; 714 715 /** 716 * The camera's digital vari-program setting, as a string. 717 * <ul> 718 * <li><code></code></li> 719 * <li><code>VR-ON</code></li> 720 * <li><code>VR-OFF</code></li> 721 * <li><code>VR-HYBRID</code></li> 722 * <li><code>VR-ACTIVE</code></li> 723 * </ul> 724 */ 725 public static final int TAG_NIKON_TYPE2_IMAGE_STABILISATION = 0x00AC; 726 727 /** 728 * The camera's digital vari-program setting, as a string. 729 * <ul> 730 * <li><code></code></li> 731 * <li><code>HYBRID</code></li> 732 * <li><code>STANDARD</code></li> 733 * </ul> 734 */ 735 public static final int TAG_NIKON_TYPE2_AF_RESPONSE = 0x00AD; 736 public static final int TAG_NIKON_TYPE2_UNKNOWN_29 = 0x00AE; 737 public static final int TAG_NIKON_TYPE2_UNKNOWN_30 = 0x00AF; 738 public static final int TAG_NIKON_TYPE2_MULTI_EXPOSURE = 0x00B0; 739 740 /** 741 * The camera's high ISO noise reduction setting, as an integer. 742 * <ul> 743 * <li><code>0</code> Off</li> 744 * <li><code>1</code> Minimal</li> 745 * <li><code>2</code> Low</li> 746 * <li><code>4</code> Normal</li> 747 * <li><code>6</code> High</li> 748 * </ul> 749 */ 750 public static final int TAG_NIKON_TYPE2_HIGH_ISO_NOISE_REDUCTION = 0x00B1; 751 public static final int TAG_NIKON_TYPE2_UNKNOWN_31 = 0x00B2; 752 public static final int TAG_NIKON_TYPE2_UNKNOWN_32 = 0x00B3; 753 public static final int TAG_NIKON_TYPE2_UNKNOWN_33 = 0x00B4; 754 public static final int TAG_NIKON_TYPE2_UNKNOWN_48 = 0x00B5; 755 public static final int TAG_NIKON_TYPE2_POWER_UP_TIME = 0x00B6; 756 public static final int TAG_NIKON_TYPE2_AF_INFO_2 = 0x00B7; 757 public static final int TAG_NIKON_TYPE2_FILE_INFO = 0x00B8; 758 public static final int TAG_NIKON_TYPE2_AF_TUNE = 0x00B9; 759 public static final int TAG_NIKON_TYPE2_UNKNOWN_49 = 0x00BB; 760 public static final int TAG_NIKON_TYPE2_UNKNOWN_50 = 0x00BD; 761 public static final int TAG_NIKON_TYPE2_UNKNOWN_51 = 0x0103; 762 public static final int TAG_NIKON_TYPE2_PRINT_IM = 0x0E00; 763 764 /** 765 * Data about changes set by Nikon Capture Editor. 766 * 767 * Values observed 768 */ 769 public static final int TAG_NIKON_TYPE2_NIKON_CAPTURE_DATA = 0x0E01; 770 public static final int TAG_NIKON_TYPE2_UNKNOWN_52 = 0x0E05; 771 public static final int TAG_NIKON_TYPE2_UNKNOWN_53 = 0x0E08; 772 public static final int TAG_NIKON_TYPE2_NIKON_CAPTURE_VERSION = 0x0E09; 773 public static final int TAG_NIKON_TYPE2_NIKON_CAPTURE_OFFSETS = 0x0E0E; 774 public static final int TAG_NIKON_TYPE2_NIKON_SCAN = 0x0E10; 775 public static final int TAG_NIKON_TYPE2_UNKNOWN_54 = 0x0E19; 776 public static final int TAG_NIKON_TYPE2_NEF_BIT_DEPTH = 0x0E22; 777 public static final int TAG_NIKON_TYPE2_UNKNOWN_55 = 0x0E23; 778 779 @NotNull 780 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 781 782 static 783 { 784 _tagNameMap.put(TAG_NIKON_TYPE2_FIRMWARE_VERSION, "Firmware Version"); 785 _tagNameMap.put(TAG_NIKON_TYPE2_ISO_1, "ISO"); 786 _tagNameMap.put(TAG_NIKON_TYPE2_QUALITY_AND_FILE_FORMAT, "Quality & File Format"); 787 _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE, "White Balance"); 788 _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_SHARPENING, "Sharpening"); 789 _tagNameMap.put(TAG_NIKON_TYPE2_AF_TYPE, "AF Type"); 790 _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_FINE, "White Balance Fine"); 791 _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_WHITE_BALANCE_RB_COEFF, "White Balance RB Coefficients"); 792 _tagNameMap.put(TAG_NIKON_TYPE2_ISO_REQUESTED, "ISO"); 793 _tagNameMap.put(TAG_NIKON_TYPE2_ISO_MODE, "ISO Mode"); 794 _tagNameMap.put(TAG_NIKON_TYPE2_DATA_DUMP, "Data Dump"); 795 796 _tagNameMap.put(TAG_NIKON_TYPE2_PROGRAM_SHIFT, "Program Shift"); 797 _tagNameMap.put(TAG_NIKON_TYPE2_EXPOSURE_DIFFERENCE, "Exposure Difference"); 798 _tagNameMap.put(TAG_NIKON_TYPE2_PREVIEW_IFD, "Preview IFD"); 799 _tagNameMap.put(TAG_NIKON_TYPE2_LENS_TYPE, "Lens Type"); 800 _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_USED, "Flash Used"); 801 _tagNameMap.put(TAG_NIKON_TYPE2_AF_FOCUS_POSITION, "AF Focus Position"); 802 _tagNameMap.put(TAG_NIKON_TYPE2_SHOOTING_MODE, "Shooting Mode"); 803 _tagNameMap.put(TAG_NIKON_TYPE2_LENS_STOPS, "Lens Stops"); 804 _tagNameMap.put(TAG_NIKON_TYPE2_CONTRAST_CURVE, "Contrast Curve"); 805 _tagNameMap.put(TAG_NIKON_TYPE2_LIGHT_SOURCE, "Light source"); 806 _tagNameMap.put(TAG_NIKON_TYPE2_SHOT_INFO, "Shot Info"); 807 _tagNameMap.put(TAG_NIKON_TYPE2_COLOR_BALANCE, "Color Balance"); 808 _tagNameMap.put(TAG_NIKON_TYPE2_LENS_DATA, "Lens Data"); 809 _tagNameMap.put(TAG_NIKON_TYPE2_NEF_THUMBNAIL_SIZE, "NEF Thumbnail Size"); 810 _tagNameMap.put(TAG_NIKON_TYPE2_SENSOR_PIXEL_SIZE, "Sensor Pixel Size"); 811 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_10, "Unknown 10"); 812 _tagNameMap.put(TAG_NIKON_TYPE2_SCENE_ASSIST, "Scene Assist"); 813 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_11, "Unknown 11"); 814 _tagNameMap.put(TAG_NIKON_TYPE2_RETOUCH_HISTORY, "Retouch History"); 815 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_12, "Unknown 12"); 816 _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_SYNC_MODE, "Flash Sync Mode"); 817 _tagNameMap.put(TAG_NIKON_TYPE2_AUTO_FLASH_MODE, "Auto Flash Mode"); 818 _tagNameMap.put(TAG_NIKON_TYPE2_AUTO_FLASH_COMPENSATION, "Auto Flash Compensation"); 819 _tagNameMap.put(TAG_NIKON_TYPE2_EXPOSURE_SEQUENCE_NUMBER, "Exposure Sequence Number"); 820 _tagNameMap.put(TAG_NIKON_TYPE2_COLOR_MODE, "Color Mode"); 821 822 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_20, "Unknown 20"); 823 _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_BOUNDARY, "Image Boundary"); 824 _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_EXPOSURE_COMPENSATION, "Flash Exposure Compensation"); 825 _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_BRACKET_COMPENSATION, "Flash Bracket Compensation"); 826 _tagNameMap.put(TAG_NIKON_TYPE2_AE_BRACKET_COMPENSATION, "AE Bracket Compensation"); 827 _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_MODE, "Flash Mode"); 828 _tagNameMap.put(TAG_NIKON_TYPE2_CROP_HIGH_SPEED, "Crop High Speed"); 829 _tagNameMap.put(TAG_NIKON_TYPE2_EXPOSURE_TUNING, "Exposure Tuning"); 830 _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_SERIAL_NUMBER, "Camera Serial Number"); 831 _tagNameMap.put(TAG_NIKON_TYPE2_COLOR_SPACE, "Color Space"); 832 _tagNameMap.put(TAG_NIKON_TYPE2_VR_INFO, "VR Info"); 833 _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_AUTHENTICATION, "Image Authentication"); 834 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_35, "Unknown 35"); 835 _tagNameMap.put(TAG_NIKON_TYPE2_ACTIVE_D_LIGHTING, "Active D-Lighting"); 836 _tagNameMap.put(TAG_NIKON_TYPE2_PICTURE_CONTROL, "Picture Control"); 837 _tagNameMap.put(TAG_NIKON_TYPE2_WORLD_TIME, "World Time"); 838 _tagNameMap.put(TAG_NIKON_TYPE2_ISO_INFO, "ISO Info"); 839 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_36, "Unknown 36"); 840 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_37, "Unknown 37"); 841 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_38, "Unknown 38"); 842 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_39, "Unknown 39"); 843 _tagNameMap.put(TAG_NIKON_TYPE2_VIGNETTE_CONTROL, "Vignette Control"); 844 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_40, "Unknown 40"); 845 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_41, "Unknown 41"); 846 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_42, "Unknown 42"); 847 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_43, "Unknown 43"); 848 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_44, "Unknown 44"); 849 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_45, "Unknown 45"); 850 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_46, "Unknown 46"); 851 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_47, "Unknown 47"); 852 _tagNameMap.put(TAG_NIKON_TYPE2_SCENE_MODE, "Scene Mode"); 853 854 _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_SERIAL_NUMBER_2, "Camera Serial Number"); 855 _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_DATA_SIZE, "Image Data Size"); 856 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_27, "Unknown 27"); 857 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_28, "Unknown 28"); 858 _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_COUNT, "Image Count"); 859 _tagNameMap.put(TAG_NIKON_TYPE2_DELETED_IMAGE_COUNT, "Deleted Image Count"); 860 _tagNameMap.put(TAG_NIKON_TYPE2_SATURATION_2, "Saturation"); 861 _tagNameMap.put(TAG_NIKON_TYPE2_DIGITAL_VARI_PROGRAM, "Digital Vari Program"); 862 _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_STABILISATION, "Image Stabilisation"); 863 _tagNameMap.put(TAG_NIKON_TYPE2_AF_RESPONSE, "AF Response"); 864 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_29, "Unknown 29"); 865 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_30, "Unknown 30"); 866 _tagNameMap.put(TAG_NIKON_TYPE2_MULTI_EXPOSURE, "Multi Exposure"); 867 _tagNameMap.put(TAG_NIKON_TYPE2_HIGH_ISO_NOISE_REDUCTION, "High ISO Noise Reduction"); 868 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_31, "Unknown 31"); 869 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_32, "Unknown 32"); 870 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_33, "Unknown 33"); 871 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_48, "Unknown 48"); 872 _tagNameMap.put(TAG_NIKON_TYPE2_POWER_UP_TIME, "Power Up Time"); 873 _tagNameMap.put(TAG_NIKON_TYPE2_AF_INFO_2, "AF Info 2"); 874 _tagNameMap.put(TAG_NIKON_TYPE2_FILE_INFO, "File Info"); 875 _tagNameMap.put(TAG_NIKON_TYPE2_AF_TUNE, "AF Tune"); 876 _tagNameMap.put(TAG_NIKON_TYPE2_FLASH_INFO, "Flash Info"); 877 _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_OPTIMISATION, "Image Optimisation"); 878 879 _tagNameMap.put(TAG_NIKON_TYPE2_IMAGE_ADJUSTMENT, "Image Adjustment"); 880 _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_TONE_COMPENSATION, "Tone Compensation"); 881 _tagNameMap.put(TAG_NIKON_TYPE2_ADAPTER, "Adapter"); 882 _tagNameMap.put(TAG_NIKON_TYPE2_LENS, "Lens"); 883 _tagNameMap.put(TAG_NIKON_TYPE2_MANUAL_FOCUS_DISTANCE, "Manual Focus Distance"); 884 _tagNameMap.put(TAG_NIKON_TYPE2_DIGITAL_ZOOM, "Digital Zoom"); 885 _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_COLOR_MODE, "Colour Mode"); 886 _tagNameMap.put(TAG_NIKON_TYPE2_CAMERA_HUE_ADJUSTMENT, "Camera Hue Adjustment"); 887 _tagNameMap.put(TAG_NIKON_TYPE2_NEF_COMPRESSION, "NEF Compression"); 888 _tagNameMap.put(TAG_NIKON_TYPE2_SATURATION, "Saturation"); 889 _tagNameMap.put(TAG_NIKON_TYPE2_NOISE_REDUCTION, "Noise Reduction"); 890 _tagNameMap.put(TAG_NIKON_TYPE2_LINEARIZATION_TABLE, "Linearization Table"); 891 _tagNameMap.put(TAG_NIKON_TYPE2_NIKON_CAPTURE_DATA, "Nikon Capture Data"); 892 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_49, "Unknown 49"); 893 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_50, "Unknown 50"); 894 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_51, "Unknown 51"); 895 _tagNameMap.put(TAG_NIKON_TYPE2_PRINT_IM, "Print IM"); 896 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_52, "Unknown 52"); 897 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_53, "Unknown 53"); 898 _tagNameMap.put(TAG_NIKON_TYPE2_NIKON_CAPTURE_VERSION, "Nikon Capture Version"); 899 _tagNameMap.put(TAG_NIKON_TYPE2_NIKON_CAPTURE_OFFSETS, "Nikon Capture Offsets"); 900 _tagNameMap.put(TAG_NIKON_TYPE2_NIKON_SCAN, "Nikon Scan"); 901 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_54, "Unknown 54"); 902 _tagNameMap.put(TAG_NIKON_TYPE2_NEF_BIT_DEPTH, "NEF Bit Depth"); 903 _tagNameMap.put(TAG_NIKON_TYPE2_UNKNOWN_55, "Unknown 55"); 904 } 905 906 public NikonType2MakernoteDirectory() 907 { 908 this.setDescriptor(new NikonType2MakernoteDescriptor(this)); 909 } 910 911 @NotNull 912 public String getName() 913 { 914 return "Nikon Makernote"; 915 } 916 917 @NotNull 918 protected HashMap<Integer, String> getTagNameMap() 919 { 920 return _tagNameMap; 921 } 922 } -
src/com/drew/metadata/exif/OlympusMakernoteDescriptor.java
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.exif; 16 22 17 import com.drew. metadata.Directory;18 import com.drew. metadata.MetadataException;23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 19 25 import com.drew.metadata.TagDescriptor; 20 26 21 27 /** 22 * Provides human-readable string versions of the tags stored in an OlympusMakernoteDirectory. 28 * Provides human-readable string representations of tag values stored in a <code>OlympusMakernoteDirectory</code>. 29 * 30 * @author Drew Noakes http://drewnoakes.com 23 31 */ 24 public class OlympusMakernoteDescriptor extends TagDescriptor 32 public class OlympusMakernoteDescriptor extends TagDescriptor<OlympusMakernoteDirectory> 25 33 { 26 public OlympusMakernoteDescriptor( Directory directory)34 public OlympusMakernoteDescriptor(@NotNull OlympusMakernoteDirectory directory) 27 35 { 28 36 super(directory); 29 37 } 30 38 31 public String getDescription(int tagType) throws MetadataException 39 @Nullable 40 public String getDescription(int tagType) 32 41 { 33 42 switch (tagType) { 34 43 case OlympusMakernoteDirectory.TAG_OLYMPUS_SPECIAL_MODE: … … 40 49 case OlympusMakernoteDirectory.TAG_OLYMPUS_DIGI_ZOOM_RATIO: 41 50 return getDigiZoomRatioDescription(); 42 51 default: 43 return _directory.getString(tagType);52 return super.getDescription(tagType); 44 53 } 45 54 } 46 55 47 public String getDigiZoomRatioDescription() throws MetadataException 56 @Nullable 57 public String getDigiZoomRatioDescription() 48 58 { 49 if (!_directory.containsTag(OlympusMakernoteDirectory.TAG_OLYMPUS_DIGI_ZOOM_RATIO)) return null; 50 int value = _directory.getInt(OlympusMakernoteDirectory.TAG_OLYMPUS_DIGI_ZOOM_RATIO); 59 Integer value = _directory.getInteger(OlympusMakernoteDirectory.TAG_OLYMPUS_DIGI_ZOOM_RATIO); 60 if (value==null) 61 return null; 51 62 switch (value) { 52 63 case 0: 53 64 return "Normal"; … … 58 69 } 59 70 } 60 71 61 public String getMacroModeDescription() throws MetadataException 72 @Nullable 73 public String getMacroModeDescription() 62 74 { 63 if (!_directory.containsTag(OlympusMakernoteDirectory.TAG_OLYMPUS_MACRO_MODE)) return null; 64 int value = _directory.getInt(OlympusMakernoteDirectory.TAG_OLYMPUS_MACRO_MODE); 75 Integer value = _directory.getInteger(OlympusMakernoteDirectory.TAG_OLYMPUS_MACRO_MODE); 76 if (value==null) 77 return null; 65 78 switch (value) { 66 79 case 0: 67 80 return "Normal (no macro)"; … … 72 85 } 73 86 } 74 87 75 public String getJpegQualityDescription() throws MetadataException 88 @Nullable 89 public String getJpegQualityDescription() 76 90 { 77 if (!_directory.containsTag(OlympusMakernoteDirectory.TAG_OLYMPUS_JPEG_QUALITY)) return null; 78 int value = _directory.getInt(OlympusMakernoteDirectory.TAG_OLYMPUS_JPEG_QUALITY); 91 Integer value = _directory.getInteger(OlympusMakernoteDirectory.TAG_OLYMPUS_JPEG_QUALITY); 92 if (value==null) 93 return null; 79 94 switch (value) { 80 95 case 1: 81 96 return "SQ"; … … 88 103 } 89 104 } 90 105 91 public String getSpecialModeDescription() throws MetadataException 106 @Nullable 107 public String getSpecialModeDescription() 92 108 { 93 if (!_directory.containsTag(OlympusMakernoteDirectory.TAG_OLYMPUS_SPECIAL_MODE)) return null;94 109 int[] values = _directory.getIntArray(OlympusMakernoteDirectory.TAG_OLYMPUS_SPECIAL_MODE); 95 StringBuffer desc = new StringBuffer(); 110 if (values==null) 111 return null; 112 if (values.length < 1) 113 return ""; 114 StringBuilder desc = new StringBuilder(); 96 115 switch (values[0]) { 97 116 case 0: 98 117 desc.append("Normal picture taking mode"); … … 110 129 desc.append("Unknown picture taking mode"); 111 130 break; 112 131 } 132 133 if (values.length < 2) 134 return desc.toString(); 113 135 desc.append(" - "); 114 136 switch (values[1]) { 115 137 case 0: 116 138 desc.append("Unknown sequence number"); 117 139 break; 118 140 case 1: 119 desc.append("1st in a sequ nce");141 desc.append("1st in a sequence"); 120 142 break; 121 143 case 2: 122 144 desc.append("2nd in a sequence"); … … 129 151 desc.append("th in a sequence"); 130 152 break; 131 153 } 154 if (values.length < 3) 155 return desc.toString(); 156 desc.append(" - "); 132 157 switch (values[2]) { 133 158 case 1: 134 159 desc.append("Left to right panorama direction"); -
src/com/drew/metadata/exif/OlympusMakernoteDirectory.java
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 21 26 import java.util.HashMap; 22 27 23 28 /** 24 * The Olympus makernote is used by many manufacturers, and as such contains some tags that appear specific to 25 * those manufacturers. Other users include Konica, Minolta and Epson. 29 * The Olympus makernote is used by many manufacturers (Konica, Minolta and Epson...), and as such contains some tags 30 * that appear specific to those manufacturers. 31 * 32 * @author Drew Noakes http://drewnoakes.com 26 33 */ 27 34 public class OlympusMakernoteDirectory extends Directory 28 35 { 29 /** 30 * Used by Konica / Minolta cameras. 31 */ 36 /** Used by Konica / Minolta cameras. */ 32 37 public static final int TAG_OLYMPUS_MAKERNOTE_VERSION = 0x0000; 33 34 /** 35 * Used by Konica / Minolta cameras. 36 */ 38 /** Used by Konica / Minolta cameras. */ 37 39 public static final int TAG_OLYMPUS_CAMERA_SETTINGS_1 = 0x0001; 38 39 /** 40 * Alternate Camera Settings Tag. Used by Konica / Minolta cameras. 41 */ 40 /** Alternate Camera Settings Tag. Used by Konica / Minolta cameras. */ 42 41 public static final int TAG_OLYMPUS_CAMERA_SETTINGS_2 = 0x0003; 43 44 /** 45 * Used by Konica / Minolta cameras. 46 */ 42 /** Used by Konica / Minolta cameras. */ 47 43 public static final int TAG_OLYMPUS_COMPRESSED_IMAGE_SIZE = 0x0040; 48 49 /** 50 * Used by Konica / Minolta cameras. 51 */ 44 /** Used by Konica / Minolta cameras. */ 52 45 public static final int TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_1 = 0x0081; 53 54 /** 55 * Alternate Thumbnail Offset. Used by Konica / Minolta cameras. 56 */ 46 /** Alternate Thumbnail Offset. Used by Konica / Minolta cameras. */ 57 47 public static final int TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_2 = 0x0088; 58 59 /** 60 * Length of thumbnail in bytes. Used by Konica / Minolta cameras. 61 */ 48 /** Length of thumbnail in bytes. Used by Konica / Minolta cameras. */ 62 49 public static final int TAG_OLYMPUS_MINOLTA_THUMBNAIL_LENGTH = 0x0089; 63 50 64 51 /** … … 83 70 84 71 /** 85 72 * Not 100% sure about this tag. 86 * 73 * <p/> 87 74 * Used by Konica / Minolta cameras. 88 75 * 0 = Raw 89 76 * 1 = Super Fine … … 118 105 */ 119 106 public static final int TAG_OLYMPUS_MACRO_MODE = 0x0202; 120 107 121 /**122 *123 */124 108 public static final int TAG_OLYMPUS_UNKNOWN_1 = 0x0203; 125 109 126 /** 127 * Zoom Factor (0 or 1 = normal) 128 */ 110 /** Zoom Factor (0 or 1 = normal) */ 129 111 public static final int TAG_OLYMPUS_DIGI_ZOOM_RATIO = 0x0204; 130 131 /**132 *133 */134 112 public static final int TAG_OLYMPUS_UNKNOWN_2 = 0x0205; 135 136 /**137 *138 */139 113 public static final int TAG_OLYMPUS_UNKNOWN_3 = 0x0206; 140 141 /**142 *143 */144 114 public static final int TAG_OLYMPUS_FIRMWARE_VERSION = 0x0207; 145 146 /**147 *148 */149 115 public static final int TAG_OLYMPUS_PICT_INFO = 0x0208; 150 151 /**152 *153 */154 116 public static final int TAG_OLYMPUS_CAMERA_ID = 0x0209; 155 117 156 118 /** … … 165 127 */ 166 128 public static final int TAG_OLYMPUS_IMAGE_HEIGHT = 0x020C; 167 129 168 /** 169 * A string. Used by Epson cameras. 170 */ 130 /** A string. Used by Epson cameras. */ 171 131 public static final int TAG_OLYMPUS_ORIGINAL_MANUFACTURER_MODEL = 0x020D; 172 132 173 133 /** … … 176 136 */ 177 137 public static final int TAG_OLYMPUS_PRINT_IMAGE_MATCHING_INFO = 0x0E00; 178 138 179 /**180 *181 */182 139 public static final int TAG_OLYMPUS_DATA_DUMP = 0x0F00; 183 184 /**185 *186 */187 140 public static final int TAG_OLYMPUS_FLASH_MODE = 0x1004; 188 189 /**190 *191 */192 141 public static final int TAG_OLYMPUS_BRACKET = 0x1006; 193 194 /**195 *196 */197 142 public static final int TAG_OLYMPUS_FOCUS_MODE = 0x100B; 198 199 /**200 *201 */202 143 public static final int TAG_OLYMPUS_FOCUS_DISTANCE = 0x100C; 203 204 /**205 *206 */207 144 public static final int TAG_OLYMPUS_ZOOM = 0x100D; 208 209 /**210 *211 */212 145 public static final int TAG_OLYMPUS_MACRO_FOCUS = 0x100E; 213 214 /**215 *216 */217 146 public static final int TAG_OLYMPUS_SHARPNESS = 0x100F; 218 219 /**220 *221 */222 147 public static final int TAG_OLYMPUS_COLOUR_MATRIX = 0x1011; 223 224 /**225 *226 */227 148 public static final int TAG_OLYMPUS_BLACK_LEVEL = 0x1012; 228 229 /**230 *231 */232 149 public static final int TAG_OLYMPUS_WHITE_BALANCE = 0x1015; 233 234 /**235 *236 */237 150 public static final int TAG_OLYMPUS_RED_BIAS = 0x1017; 238 239 /**240 *241 */242 151 public static final int TAG_OLYMPUS_BLUE_BIAS = 0x1018; 243 244 /**245 *246 */247 152 public static final int TAG_OLYMPUS_SERIAL_NUMBER = 0x101A; 248 249 /**250 *251 */252 153 public static final int TAG_OLYMPUS_FLASH_BIAS = 0x1023; 253 254 /**255 *256 */257 154 public static final int TAG_OLYMPUS_CONTRAST = 0x1029; 258 259 /**260 *261 */262 155 public static final int TAG_OLYMPUS_SHARPNESS_FACTOR = 0x102A; 263 264 /**265 *266 */267 156 public static final int TAG_OLYMPUS_COLOUR_CONTROL = 0x102B; 268 269 /**270 *271 */272 157 public static final int TAG_OLYMPUS_VALID_BITS = 0x102C; 273 274 /**275 *276 */277 158 public static final int TAG_OLYMPUS_CORING_FILTER = 0x102D; 278 279 /**280 *281 */282 159 public static final int TAG_OLYMPUS_FINAL_WIDTH = 0x102E; 283 284 /**285 *286 */287 160 public static final int TAG_OLYMPUS_FINAL_HEIGHT = 0x102F; 288 289 /**290 *291 */292 161 public static final int TAG_OLYMPUS_COMPRESSION_RATIO = 0x1034; 293 162 294 protected static final HashMap tagNameMap = new HashMap(); 163 @NotNull 164 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 295 165 296 static 297 {298 tagNameMap.put(new Integer(TAG_OLYMPUS_SPECIAL_MODE), "Special Mode");299 tagNameMap.put(new Integer(TAG_OLYMPUS_JPEG_QUALITY), "Jpeg Quality");300 tagNameMap.put(new Integer(TAG_OLYMPUS_MACRO_MODE), "Macro");301 tagNameMap.put(new Integer(TAG_OLYMPUS_UNKNOWN_1), "Makernote Unknown 1");302 tagNameMap.put(new Integer(TAG_OLYMPUS_DIGI_ZOOM_RATIO), "DigiZoom Ratio");303 tagNameMap.put(new Integer(TAG_OLYMPUS_UNKNOWN_2), "Makernote Unknown 2");304 tagNameMap.put(new Integer(TAG_OLYMPUS_UNKNOWN_3), "Makernote Unknown 3");305 tagNameMap.put(new Integer(TAG_OLYMPUS_FIRMWARE_VERSION), "Firmware Version");306 tagNameMap.put(new Integer(TAG_OLYMPUS_PICT_INFO), "Pict Info");307 tagNameMap.put(new Integer(TAG_OLYMPUS_CAMERA_ID), "Camera Id");308 tagNameMap.put(new Integer(TAG_OLYMPUS_DATA_DUMP), "Data Dump");309 tagNameMap.put(new Integer(TAG_OLYMPUS_MAKERNOTE_VERSION), "Makernote Version");310 tagNameMap.put(new Integer(TAG_OLYMPUS_CAMERA_SETTINGS_1), "Camera Settings");311 tagNameMap.put(new Integer(TAG_OLYMPUS_CAMERA_SETTINGS_2), "Camera Settings");312 tagNameMap.put(new Integer(TAG_OLYMPUS_COMPRESSED_IMAGE_SIZE), "Compressed Image Size");313 tagNameMap.put(new Integer(TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_1), "Thumbnail Offset");314 tagNameMap.put(new Integer(TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_2), "Thumbnail Offset");315 tagNameMap.put(new Integer(TAG_OLYMPUS_MINOLTA_THUMBNAIL_LENGTH), "Thumbnail Length");316 tagNameMap.put(new Integer(TAG_OLYMPUS_COLOUR_MODE), "Colour Mode");317 tagNameMap.put(new Integer(TAG_OLYMPUS_IMAGE_QUALITY_1), "Image Quality");318 tagNameMap.put(new Integer(TAG_OLYMPUS_IMAGE_QUALITY_2), "Image Quality");319 tagNameMap.put(new Integer(TAG_OLYMPUS_IMAGE_HEIGHT), "Image Height");320 tagNameMap.put(new Integer(TAG_OLYMPUS_ORIGINAL_MANUFACTURER_MODEL), "Original Manufacturer Model");321 tagNameMap.put(new Integer(TAG_OLYMPUS_PRINT_IMAGE_MATCHING_INFO), "Print Image Matching (PIM) Info");322 tagNameMap.put(new Integer(TAG_OLYMPUS_FLASH_MODE), "Flash Mode");323 tagNameMap.put(new Integer(TAG_OLYMPUS_BRACKET), "Bracket");324 tagNameMap.put(new Integer(TAG_OLYMPUS_FOCUS_MODE), "Focus Mode");325 tagNameMap.put(new Integer(TAG_OLYMPUS_FOCUS_DISTANCE), "Focus Distance");326 tagNameMap.put(new Integer(TAG_OLYMPUS_ZOOM), "Zoom");327 tagNameMap.put(new Integer(TAG_OLYMPUS_MACRO_FOCUS), "Macro Focus");328 tagNameMap.put(new Integer(TAG_OLYMPUS_SHARPNESS), "Sharpness");329 tagNameMap.put(new Integer(TAG_OLYMPUS_COLOUR_MATRIX), "Colour Matrix");330 tagNameMap.put(new Integer(TAG_OLYMPUS_BLACK_LEVEL), "Black Level");331 tagNameMap.put(new Integer(TAG_OLYMPUS_WHITE_BALANCE), "White Balance");332 tagNameMap.put(new Integer(TAG_OLYMPUS_RED_BIAS), "Red Bias");333 tagNameMap.put(new Integer(TAG_OLYMPUS_BLUE_BIAS), "Blue Bias");334 tagNameMap.put(new Integer(TAG_OLYMPUS_SERIAL_NUMBER), "Serial Number");335 tagNameMap.put(new Integer(TAG_OLYMPUS_FLASH_BIAS), "Flash Bias");336 tagNameMap.put(new Integer(TAG_OLYMPUS_CONTRAST), "Contrast");337 tagNameMap.put(new Integer(TAG_OLYMPUS_SHARPNESS_FACTOR), "Sharpness Factor");338 tagNameMap.put(new Integer(TAG_OLYMPUS_COLOUR_CONTROL), "Colour Control");339 tagNameMap.put(new Integer(TAG_OLYMPUS_VALID_BITS), "Valid Bits");340 tagNameMap.put(new Integer(TAG_OLYMPUS_CORING_FILTER), "Coring Filter");341 tagNameMap.put(new Integer(TAG_OLYMPUS_FINAL_WIDTH), "Final Width");342 tagNameMap.put(new Integer(TAG_OLYMPUS_FINAL_HEIGHT), "Final Height");343 tagNameMap.put(new Integer(TAG_OLYMPUS_COMPRESSION_RATIO), "Compression Ratio");166 static { 167 _tagNameMap.put(TAG_OLYMPUS_SPECIAL_MODE, "Special Mode"); 168 _tagNameMap.put(TAG_OLYMPUS_JPEG_QUALITY, "Jpeg Quality"); 169 _tagNameMap.put(TAG_OLYMPUS_MACRO_MODE, "Macro"); 170 _tagNameMap.put(TAG_OLYMPUS_UNKNOWN_1, "Makernote Unknown 1"); 171 _tagNameMap.put(TAG_OLYMPUS_DIGI_ZOOM_RATIO, "DigiZoom Ratio"); 172 _tagNameMap.put(TAG_OLYMPUS_UNKNOWN_2, "Makernote Unknown 2"); 173 _tagNameMap.put(TAG_OLYMPUS_UNKNOWN_3, "Makernote Unknown 3"); 174 _tagNameMap.put(TAG_OLYMPUS_FIRMWARE_VERSION, "Firmware Version"); 175 _tagNameMap.put(TAG_OLYMPUS_PICT_INFO, "Pict Info"); 176 _tagNameMap.put(TAG_OLYMPUS_CAMERA_ID, "Camera Id"); 177 _tagNameMap.put(TAG_OLYMPUS_DATA_DUMP, "Data Dump"); 178 _tagNameMap.put(TAG_OLYMPUS_MAKERNOTE_VERSION, "Makernote Version"); 179 _tagNameMap.put(TAG_OLYMPUS_CAMERA_SETTINGS_1, "Camera Settings"); 180 _tagNameMap.put(TAG_OLYMPUS_CAMERA_SETTINGS_2, "Camera Settings"); 181 _tagNameMap.put(TAG_OLYMPUS_COMPRESSED_IMAGE_SIZE, "Compressed Image Size"); 182 _tagNameMap.put(TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_1, "Thumbnail Offset"); 183 _tagNameMap.put(TAG_OLYMPUS_MINOLTA_THUMBNAIL_OFFSET_2, "Thumbnail Offset"); 184 _tagNameMap.put(TAG_OLYMPUS_MINOLTA_THUMBNAIL_LENGTH, "Thumbnail Length"); 185 _tagNameMap.put(TAG_OLYMPUS_COLOUR_MODE, "Colour Mode"); 186 _tagNameMap.put(TAG_OLYMPUS_IMAGE_QUALITY_1, "Image Quality"); 187 _tagNameMap.put(TAG_OLYMPUS_IMAGE_QUALITY_2, "Image Quality"); 188 _tagNameMap.put(TAG_OLYMPUS_IMAGE_HEIGHT, "Image Height"); 189 _tagNameMap.put(TAG_OLYMPUS_IMAGE_WIDTH, "Image Width"); 190 _tagNameMap.put(TAG_OLYMPUS_ORIGINAL_MANUFACTURER_MODEL, "Original Manufacturer Model"); 191 _tagNameMap.put(TAG_OLYMPUS_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info"); 192 _tagNameMap.put(TAG_OLYMPUS_FLASH_MODE, "Flash Mode"); 193 _tagNameMap.put(TAG_OLYMPUS_BRACKET, "Bracket"); 194 _tagNameMap.put(TAG_OLYMPUS_FOCUS_MODE, "Focus Mode"); 195 _tagNameMap.put(TAG_OLYMPUS_FOCUS_DISTANCE, "Focus Distance"); 196 _tagNameMap.put(TAG_OLYMPUS_ZOOM, "Zoom"); 197 _tagNameMap.put(TAG_OLYMPUS_MACRO_FOCUS, "Macro Focus"); 198 _tagNameMap.put(TAG_OLYMPUS_SHARPNESS, "Sharpness"); 199 _tagNameMap.put(TAG_OLYMPUS_COLOUR_MATRIX, "Colour Matrix"); 200 _tagNameMap.put(TAG_OLYMPUS_BLACK_LEVEL, "Black Level"); 201 _tagNameMap.put(TAG_OLYMPUS_WHITE_BALANCE, "White Balance"); 202 _tagNameMap.put(TAG_OLYMPUS_RED_BIAS, "Red Bias"); 203 _tagNameMap.put(TAG_OLYMPUS_BLUE_BIAS, "Blue Bias"); 204 _tagNameMap.put(TAG_OLYMPUS_SERIAL_NUMBER, "Serial Number"); 205 _tagNameMap.put(TAG_OLYMPUS_FLASH_BIAS, "Flash Bias"); 206 _tagNameMap.put(TAG_OLYMPUS_CONTRAST, "Contrast"); 207 _tagNameMap.put(TAG_OLYMPUS_SHARPNESS_FACTOR, "Sharpness Factor"); 208 _tagNameMap.put(TAG_OLYMPUS_COLOUR_CONTROL, "Colour Control"); 209 _tagNameMap.put(TAG_OLYMPUS_VALID_BITS, "Valid Bits"); 210 _tagNameMap.put(TAG_OLYMPUS_CORING_FILTER, "Coring Filter"); 211 _tagNameMap.put(TAG_OLYMPUS_FINAL_WIDTH, "Final Width"); 212 _tagNameMap.put(TAG_OLYMPUS_FINAL_HEIGHT, "Final Height"); 213 _tagNameMap.put(TAG_OLYMPUS_COMPRESSION_RATIO, "Compression Ratio"); 344 214 } 345 215 346 216 public OlympusMakernoteDirectory() … … 348 218 this.setDescriptor(new OlympusMakernoteDescriptor(this)); 349 219 } 350 220 221 @NotNull 351 222 public String getName() 352 223 { 353 224 return "Olympus Makernote"; 354 225 } 355 226 356 protected HashMap getTagNameMap() 227 @NotNull 228 protected HashMap<Integer, String> getTagNameMap() 357 229 { 358 return tagNameMap;230 return _tagNameMap; 359 231 } 360 232 } -
src/com/drew/metadata/exif/PanasonicMakernoteDescriptor.java
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.exif; 16 22 17 import com.drew.metadata.Directory; 18 import com.drew.metadata.MetadataException; 23 import com.drew.lang.BufferBoundsException; 24 import com.drew.lang.BufferReader; 25 import com.drew.lang.ByteArrayReader; 26 import com.drew.lang.annotations.NotNull; 27 import com.drew.lang.annotations.Nullable; 28 import com.drew.metadata.Age; 29 import com.drew.metadata.Face; 19 30 import com.drew.metadata.TagDescriptor; 20 31 32 import java.io.UnsupportedEncodingException; 33 21 34 /** 22 * Provides human-readable string versions of the tags stored in a PanasonicMakernoteDirectory. 35 * Provides human-readable string representations of tag values stored in a <code>PanasonicMakernoteDirectory</code>. 36 * <p/> 37 * Some information about this makernote taken from here: 38 * <ul> 39 * <li><a href="http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html">http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html</a></li> 40 * <li><a href="http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Panasonic.html">http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Panasonic.html</a></li> 41 * </ul> 23 42 * 24 * Some information about this makernote taken from here: 25 * http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html 43 * @author Drew Noakes http://drewnoakes.com, Philipp Sandhaus 26 44 */ 27 public class PanasonicMakernoteDescriptor extends TagDescriptor 45 public class PanasonicMakernoteDescriptor extends TagDescriptor<PanasonicMakernoteDirectory> 28 46 { 29 public PanasonicMakernoteDescriptor( Directory directory)47 public PanasonicMakernoteDescriptor(@NotNull PanasonicMakernoteDirectory directory) 30 48 { 31 49 super(directory); 32 50 } 33 51 34 public String getDescription(int tagType) throws MetadataException 52 @Nullable 53 public String getDescription(int tagType) 35 54 { 36 switch (tagType) 37 { 38 case PanasonicMakernoteDirectory.TAG_PANASONIC_MACRO_MODE: 55 switch (tagType) { 56 case PanasonicMakernoteDirectory.TAG_QUALITY_MODE: 57 return getQualityModeDescription(); 58 case PanasonicMakernoteDirectory.TAG_VERSION: 59 return getVersionDescription(); 60 case PanasonicMakernoteDirectory.TAG_WHITE_BALANCE: 61 return getWhiteBalanceDescription(); 62 case PanasonicMakernoteDirectory.TAG_FOCUS_MODE: 63 return getFocusModeDescription(); 64 case PanasonicMakernoteDirectory.TAG_AF_AREA_MODE: 65 return getAfAreaModeDescription(); 66 case PanasonicMakernoteDirectory.TAG_IMAGE_STABILIZATION: 67 return getImageStabilizationDescription(); 68 case PanasonicMakernoteDirectory.TAG_MACRO_MODE: 39 69 return getMacroModeDescription(); 40 case PanasonicMakernoteDirectory.TAG_ PANASONIC_RECORD_MODE:70 case PanasonicMakernoteDirectory.TAG_RECORD_MODE: 41 71 return getRecordModeDescription(); 42 case PanasonicMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO: 72 case PanasonicMakernoteDirectory.TAG_AUDIO: 73 return getAudioDescription(); 74 case PanasonicMakernoteDirectory.TAG_UNKNOWN_DATA_DUMP: 75 return getUnknownDataDumpDescription(); 76 case PanasonicMakernoteDirectory.TAG_COLOR_EFFECT: 77 return getColorEffectDescription(); 78 case PanasonicMakernoteDirectory.TAG_UPTIME: 79 return getUptimeDescription(); 80 case PanasonicMakernoteDirectory.TAG_BURST_MODE: 81 return getBurstModeDescription(); 82 case PanasonicMakernoteDirectory.TAG_CONTRAST_MODE: 83 return getContrastModeDescription(); 84 case PanasonicMakernoteDirectory.TAG_NOISE_REDUCTION: 85 return getNoiseReductionDescription(); 86 case PanasonicMakernoteDirectory.TAG_SELF_TIMER: 87 return getSelfTimerDescription(); 88 case PanasonicMakernoteDirectory.TAG_ROTATION: 89 return getRotationDescription(); 90 case PanasonicMakernoteDirectory.TAG_AF_ASSIST_LAMP: 91 return getAfAssistLampDescription(); 92 case PanasonicMakernoteDirectory.TAG_COLOR_MODE: 93 return getColorModeDescription(); 94 case PanasonicMakernoteDirectory.TAG_OPTICAL_ZOOM_MODE: 95 return getOpticalZoomModeDescription(); 96 case PanasonicMakernoteDirectory.TAG_CONVERSION_LENS: 97 return getConversionLensDescription(); 98 case PanasonicMakernoteDirectory.TAG_CONTRAST: 99 return getContrastDescription(); 100 case PanasonicMakernoteDirectory.TAG_WORLD_TIME_LOCATION: 101 return getWorldTimeLocationDescription(); 102 case PanasonicMakernoteDirectory.TAG_ADVANCED_SCENE_MODE: 103 return getAdvancedSceneModeDescription(); 104 case PanasonicMakernoteDirectory.TAG_FACE_DETECTION_INFO: 105 return getDetectedFacesDescription(); 106 case PanasonicMakernoteDirectory.TAG_TRANSFORM: 107 return getTransformDescription(); 108 case PanasonicMakernoteDirectory.TAG_TRANSFORM_1: 109 return getTransform1Description(); 110 case PanasonicMakernoteDirectory.TAG_INTELLIGENT_EXPOSURE: 111 return getIntelligentExposureDescription(); 112 case PanasonicMakernoteDirectory.TAG_FLASH_WARNING: 113 return getFlashWarningDescription(); 114 case PanasonicMakernoteDirectory.TAG_COUNTRY: 115 return getCountryDescription(); 116 case PanasonicMakernoteDirectory.TAG_STATE: 117 return getStateDescription(); 118 case PanasonicMakernoteDirectory.TAG_CITY: 119 return getCityDescription(); 120 case PanasonicMakernoteDirectory.TAG_LANDMARK: 121 return getLandmarkDescription(); 122 case PanasonicMakernoteDirectory.TAG_INTELLIGENT_RESOLUTION: 123 return getIntelligentResolutionDescription(); 124 case PanasonicMakernoteDirectory.TAG_FACE_RECOGNITION_INFO: 125 return getRecognizedFacesDescription(); 126 case PanasonicMakernoteDirectory.TAG_PRINT_IMAGE_MATCHING_INFO: 43 127 return getPrintImageMatchingInfoDescription(); 128 case PanasonicMakernoteDirectory.TAG_SCENE_MODE: 129 return getSceneModeDescription(); 130 case PanasonicMakernoteDirectory.TAG_FLASH_FIRED: 131 return getFlashFiredDescription(); 132 case PanasonicMakernoteDirectory.TAG_TEXT_STAMP: 133 return getTextStampDescription(); 134 case PanasonicMakernoteDirectory.TAG_TEXT_STAMP_1: 135 return getTextStamp1Description(); 136 case PanasonicMakernoteDirectory.TAG_TEXT_STAMP_2: 137 return getTextStamp2Description(); 138 case PanasonicMakernoteDirectory.TAG_TEXT_STAMP_3: 139 return getTextStamp3Description(); 140 case PanasonicMakernoteDirectory.TAG_MAKERNOTE_VERSION: 141 return getMakernoteVersionDescription(); 142 case PanasonicMakernoteDirectory.TAG_EXIF_VERSION: 143 return getExifVersionDescription(); 144 case PanasonicMakernoteDirectory.TAG_INTERNAL_SERIAL_NUMBER: 145 return getInternalSerialNumberDescription(); 146 case PanasonicMakernoteDirectory.TAG_TITLE: 147 return getTitleDescription(); 148 case PanasonicMakernoteDirectory.TAG_BABY_NAME: 149 return getBabyNameDescription(); 150 case PanasonicMakernoteDirectory.TAG_LOCATION: 151 return getLocationDescription(); 152 case PanasonicMakernoteDirectory.TAG_BABY_AGE: 153 return getBabyAgeDescription(); 154 case PanasonicMakernoteDirectory.TAG_BABY_AGE_1: 155 return getBabyAge1Description(); 156 default: 157 return super.getDescription(tagType); 158 } 159 } 160 161 @Nullable 162 public String getPrintImageMatchingInfoDescription() 163 { 164 byte[] values = _directory.getByteArray(PanasonicMakernoteDirectory.TAG_PRINT_IMAGE_MATCHING_INFO); 165 if (values == null) 166 return null; 167 return "(" + values.length + " bytes)"; 168 } 169 170 @Nullable 171 public String getTextStampDescription() 172 { 173 return getOnOffDescription(PanasonicMakernoteDirectory.TAG_TEXT_STAMP); 174 } 175 176 @Nullable 177 public String getTextStamp1Description() 178 { 179 return getOnOffDescription(PanasonicMakernoteDirectory.TAG_TEXT_STAMP_1); 180 } 181 182 @Nullable 183 public String getTextStamp2Description() 184 { 185 return getOnOffDescription(PanasonicMakernoteDirectory.TAG_TEXT_STAMP_2); 186 } 187 188 @Nullable 189 public String getTextStamp3Description() 190 { 191 return getOnOffDescription(PanasonicMakernoteDirectory.TAG_TEXT_STAMP_3); 192 } 193 194 @Nullable 195 public String getMacroModeDescription() 196 { 197 return getOnOffDescription(PanasonicMakernoteDirectory.TAG_MACRO_MODE); 198 } 199 200 @Nullable 201 public String getFlashFiredDescription() 202 { 203 return getOnOffDescription(PanasonicMakernoteDirectory.TAG_FLASH_FIRED); 204 } 205 206 @Nullable 207 public String getImageStabilizationDescription() 208 { 209 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_IMAGE_STABILIZATION); 210 if (value == null) 211 return null; 212 switch (value) { 213 case 2: 214 return "On, Mode 1"; 215 case 3: 216 return "Off"; 217 case 4: 218 return "On, Mode 2"; 44 219 default: 45 return _directory.getString(tagType);220 return "Unknown (" + value + ")"; 46 221 } 47 222 } 48 223 49 public String getPrintImageMatchingInfoDescription() throws MetadataException 224 @Nullable 225 public String getAudioDescription() 50 226 { 51 if (!_directory.containsTag(PanasonicMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO)) return null; 52 byte[] bytes = _directory.getByteArray(PanasonicMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO); 53 return "(" + bytes.length + " bytes)"; 227 return getOnOffDescription(PanasonicMakernoteDirectory.TAG_AUDIO); 54 228 } 55 229 56 public String getMacroModeDescription() throws MetadataException 230 @Nullable 231 public String getTransformDescription() 57 232 { 58 if (!_directory.containsTag(PanasonicMakernoteDirectory.TAG_PANASONIC_MACRO_MODE)) return null; 59 int value = _directory.getInt(PanasonicMakernoteDirectory.TAG_PANASONIC_MACRO_MODE); 233 return getTransformDescription(PanasonicMakernoteDirectory.TAG_TRANSFORM); 234 } 235 236 @Nullable 237 public String getTransform1Description() 238 { 239 return getTransformDescription(PanasonicMakernoteDirectory.TAG_TRANSFORM_1); 240 } 241 242 @Nullable 243 private String getTransformDescription(int tag) 244 { 245 byte[] values = _directory.getByteArray(tag); 246 if (values == null) 247 return null; 248 249 BufferReader reader = new ByteArrayReader(values); 250 251 try 252 { 253 int val1 = reader.getUInt16(0); 254 int val2 = reader.getUInt16(2); 255 256 if (val1 == -1 && val2 == 1) 257 return "Slim Low"; 258 if (val1 == -3 && val2 == 2) 259 return "Slim High"; 260 if (val1 == 0 && val2 == 0) 261 return "Off"; 262 if (val1 == 1 && val2 == 1) 263 return "Stretch Low"; 264 if (val1 == 3 && val2 == 2) 265 return "Stretch High"; 266 267 return "Unknown (" + val1 + " " + val2 + ")"; 268 } catch (BufferBoundsException e) { 269 return null ; 270 } 271 } 272 273 @Nullable 274 public String getIntelligentExposureDescription() 275 { 276 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_INTELLIGENT_EXPOSURE); 277 if (value == null) 278 return null; 60 279 switch (value) { 280 case 0: 281 return "Off"; 61 282 case 1: 283 return "Low"; 284 case 2: 285 return "Standard"; 286 case 3: 287 return "High"; 288 289 default: 290 return "Unknown (" + value + ")"; 291 } 292 } 293 294 @Nullable 295 public String getFlashWarningDescription() 296 { 297 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_FLASH_WARNING); 298 if (value == null) 299 return null; 300 switch (value) { 301 case 0: 302 return "No"; 303 case 1: 304 return "Yes (Flash required but disabled)"; 305 default: 306 return "Unknown (" + value + ")"; 307 } 308 } 309 310 @Nullable 311 public String getCountryDescription() 312 { 313 return getTextDescription(PanasonicMakernoteDirectory.TAG_COUNTRY); 314 } 315 316 @Nullable 317 public String getStateDescription() 318 { 319 return getTextDescription(PanasonicMakernoteDirectory.TAG_STATE); 320 } 321 322 @Nullable 323 public String getCityDescription() 324 { 325 return getTextDescription(PanasonicMakernoteDirectory.TAG_CITY); 326 } 327 328 @Nullable 329 public String getLandmarkDescription() 330 { 331 return getTextDescription(PanasonicMakernoteDirectory.TAG_LANDMARK); 332 } 333 334 @Nullable 335 public String getTitleDescription() 336 { 337 return getTextDescription(PanasonicMakernoteDirectory.TAG_TITLE); 338 } 339 340 @Nullable 341 public String getBabyNameDescription() 342 { 343 return getTextDescription(PanasonicMakernoteDirectory.TAG_BABY_NAME); 344 } 345 346 @Nullable 347 public String getLocationDescription() 348 { 349 return getTextDescription(PanasonicMakernoteDirectory.TAG_LOCATION); 350 } 351 352 @Nullable 353 public String getIntelligentResolutionDescription() 354 { 355 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_INTELLIGENT_RESOLUTION); 356 if (value == null) 357 return null; 358 switch (value) { 359 case 0: 360 return "Off"; 361 case 2: 362 return "Auto"; 363 case 3: 62 364 return "On"; 365 default: 366 return "Unknown (" + value + ")"; 367 } 368 } 369 370 @Nullable 371 public String getContrastDescription() 372 { 373 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_CONTRAST); 374 if (value == null) 375 return null; 376 switch (value) { 377 case 0: 378 return "Normal"; 379 default: 380 return "Unknown (" + value + ")"; 381 } 382 } 383 384 @Nullable 385 public String getWorldTimeLocationDescription() 386 { 387 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_WORLD_TIME_LOCATION); 388 if (value == null) 389 return null; 390 switch (value) { 391 case 1: 392 return "Home"; 63 393 case 2: 394 return "Destination"; 395 default: 396 return "Unknown (" + value + ")"; 397 } 398 } 399 400 @Nullable 401 public String getAdvancedSceneModeDescription() 402 { 403 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_ADVANCED_SCENE_MODE); 404 if (value == null) 405 return null; 406 switch (value) { 407 case 1: 408 return "Normal"; 409 case 2: 410 return "Outdoor/Illuminations/Flower/HDR Art"; 411 case 3: 412 return "Indoor/Architecture/Objects/HDR B&W"; 413 case 4: 414 return "Creative"; 415 case 5: 416 return "Auto"; 417 case 7: 418 return "Expressive"; 419 case 8: 420 return "Retro"; 421 case 9: 422 return "Pure"; 423 case 10: 424 return "Elegant"; 425 case 12: 426 return "Monochrome"; 427 case 13: 428 return "Dynamic Art"; 429 case 14: 430 return "Silhouette"; 431 default: 432 return "Unknown (" + value + ")"; 433 } 434 } 435 436 @Nullable 437 public String getUnknownDataDumpDescription() 438 { 439 byte[] value = _directory.getByteArray(PanasonicMakernoteDirectory.TAG_UNKNOWN_DATA_DUMP); 440 if (value == null) 441 return null; 442 return "[" + value.length + " bytes]"; 443 } 444 445 @Nullable 446 public String getColorEffectDescription() 447 { 448 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_COLOR_EFFECT); 449 if (value == null) 450 return null; 451 switch (value) { 452 case 1: 64 453 return "Off"; 454 case 2: 455 return "Warm"; 456 case 3: 457 return "Cool"; 458 case 4: 459 return "Black & White"; 460 case 5: 461 return "Sepia"; 65 462 default: 66 463 return "Unknown (" + value + ")"; 67 464 } 68 465 } 69 466 70 public String getRecordModeDescription() throws MetadataException 467 @Nullable 468 public String getUptimeDescription() 71 469 { 72 if (!_directory.containsTag(PanasonicMakernoteDirectory.TAG_PANASONIC_RECORD_MODE)) return null; 73 int value = _directory.getInt(PanasonicMakernoteDirectory.TAG_PANASONIC_RECORD_MODE); 470 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_UPTIME); 471 if (value == null) 472 return null; 473 return value / 100f + " s"; 474 } 475 476 @Nullable 477 public String getBurstModeDescription() 478 { 479 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_BURST_MODE); 480 if (value == null) 481 return null; 74 482 switch (value) { 483 case 0: 484 return "Off"; 75 485 case 1: 486 return "On"; 487 case 2: 488 return "Infinite"; 489 case 4: 490 return "Unlimited"; 491 default: 492 return "Unknown (" + value + ")"; 493 } 494 } 495 496 @Nullable 497 public String getContrastModeDescription() 498 { 499 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_CONTRAST_MODE); 500 if (value == null) 501 return null; 502 switch (value) { 503 case 0x0: 76 504 return "Normal"; 505 case 0x1: 506 return "Low"; 507 case 0x2: 508 return "High"; 509 case 0x6: 510 return "Medium Low"; 511 case 0x7: 512 return "Medium High"; 513 case 0x100: 514 return "Low"; 515 case 0x110: 516 return "Normal"; 517 case 0x120: 518 return "High"; 519 520 default: 521 return "Unknown (" + value + ")"; 522 } 523 } 524 525 @Nullable 526 public String getNoiseReductionDescription() 527 { 528 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_NOISE_REDUCTION); 529 if (value == null) 530 return null; 531 switch (value) { 532 case 0: 533 return "Standard (0)"; 534 case 1: 535 return "Low (-1)"; 77 536 case 2: 537 return "High (+1)"; 538 case 3: 539 return "Lowest (-2)"; 540 case 4: 541 return "Highest (+2)"; 542 default: 543 return "Unknown (" + value + ")"; 544 } 545 } 546 547 548 @Nullable 549 public String getSelfTimerDescription() 550 { 551 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_SELF_TIMER); 552 if (value == null) 553 return null; 554 switch (value) { 555 case 1: 556 return "Off"; 557 case 2: 558 return "10 s"; 559 case 3: 560 return "2 s"; 561 default: 562 return "Unknown (" + value + ")"; 563 } 564 } 565 566 @Nullable 567 public String getRotationDescription() 568 { 569 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_ROTATION); 570 if (value == null) 571 return null; 572 switch (value) { 573 case 1: 574 return "Horizontal"; 575 case 3: 576 return "Rotate 180"; 577 case 6: 578 return "Rotate 90 CW"; 579 case 8: 580 return "Rotate 270 CW"; 581 default: 582 return "Unknown (" + value + ")"; 583 } 584 } 585 586 @Nullable 587 public String getAfAssistLampDescription() 588 { 589 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_AF_ASSIST_LAMP); 590 if (value == null) 591 return null; 592 switch (value) { 593 case 1: 594 return "Fired"; 595 case 2: 596 return "Enabled but not used"; 597 case 3: 598 return "Disabled but required"; 599 case 4: 600 return "Disabled and not required"; 601 default: 602 return "Unknown (" + value + ")"; 603 } 604 } 605 606 @Nullable 607 public String getColorModeDescription() 608 { 609 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_COLOR_MODE); 610 if (value == null) 611 return null; 612 switch (value) { 613 case 0: 614 return "Normal"; 615 case 1: 616 return "Natural"; 617 case 2: 618 return "Vivid"; 619 default: 620 return "Unknown (" + value + ")"; 621 } 622 } 623 624 @Nullable 625 public String getOpticalZoomModeDescription() 626 { 627 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_OPTICAL_ZOOM_MODE); 628 if (value == null) 629 return null; 630 switch (value) { 631 case 1: 632 return "Standard"; 633 case 2: 634 return "Extended"; 635 default: 636 return "Unknown (" + value + ")"; 637 } 638 } 639 640 @Nullable 641 public String getConversionLensDescription() 642 { 643 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_CONVERSION_LENS); 644 if (value == null) 645 return null; 646 switch (value) { 647 case 1: 648 return "Off"; 649 case 2: 650 return "Wide"; 651 case 3: 652 return "Telephoto"; 653 case 4: 654 return "Macro"; 655 default: 656 return "Unknown (" + value + ")"; 657 } 658 } 659 660 @Nullable 661 public String getDetectedFacesDescription() 662 { 663 return buildFacesDescription(_directory.getDetectedFaces()); 664 } 665 666 @Nullable 667 public String getRecognizedFacesDescription() 668 { 669 return buildFacesDescription(_directory.getRecognizedFaces()); 670 } 671 672 @Nullable 673 private String buildFacesDescription(@Nullable Face[] faces) 674 { 675 if (faces == null) 676 return null; 677 678 StringBuilder result = new StringBuilder(); 679 680 for (int i = 0; i < faces.length; i++) 681 result.append("Face ").append(i + 1).append(": ").append(faces[i].toString()).append("\n"); 682 683 if (result.length() > 0) 684 return result.substring(0, result.length() - 1); 685 686 return null; 687 } 688 689 @Nullable 690 public String getRecordModeDescription() 691 { 692 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_RECORD_MODE); 693 if (value == null) 694 return null; 695 switch (value) { 696 case 1: 697 return "Normal"; 698 case 2: 78 699 return "Portrait"; 700 case 3: 701 return "Scenery"; 702 case 4: 703 return "Sports"; 704 case 5: 705 return "Night Portrait"; 706 case 6: 707 return "Program"; 708 case 7: 709 return "Aperture Priority"; 710 case 8: 711 return "Shutter Priority"; 79 712 case 9: 80 713 return "Macro"; 714 case 10: 715 return "Spot"; 716 case 11: 717 return "Manual"; 718 case 12: 719 return "Movie Preview"; 720 case 13: 721 return "Panning"; 722 case 14: 723 return "Simple"; 724 case 15: 725 return "Color Effects"; 726 case 16: 727 return "Self Portrait"; 728 case 17: 729 return "Economy"; 730 case 18: 731 return "Fireworks"; 732 case 19: 733 return "Party"; 734 case 20: 735 return "Snow"; 736 case 21: 737 return "Night Scenery"; 738 case 22: 739 return "Food"; 740 case 23: 741 return "Baby"; 742 case 24: 743 return "Soft Skin"; 744 case 25: 745 return "Candlelight"; 746 case 26: 747 return "Starry Night"; 748 case 27: 749 return "High Sensitivity"; 750 case 28: 751 return "Panorama Assist"; 752 case 29: 753 return "Underwater"; 754 case 30: 755 return "Beach"; 756 case 31: 757 return "Aerial Photo"; 758 case 32: 759 return "Sunset"; 760 case 33: 761 return "Pet"; 762 case 34: 763 return "Intelligent ISO"; 764 case 35: 765 return "Clipboard"; 766 case 36: 767 return "High Speed Continuous Shooting"; 768 case 37: 769 return "Intelligent Auto"; 770 case 39: 771 return "Multi-aspect"; 772 case 41: 773 return "Transform"; 774 case 42: 775 return "Flash Burst"; 776 case 43: 777 return "Pin Hole"; 778 case 44: 779 return "Film Grain"; 780 case 45: 781 return "My Color"; 782 case 46: 783 return "Photo Frame"; 784 case 51: 785 return "HDR"; 81 786 default: 82 787 return "Unknown (" + value + ")"; 83 788 } 84 789 } 790 791 @Nullable 792 public String getSceneModeDescription() 793 { 794 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_SCENE_MODE); 795 if (value == null) 796 return null; 797 switch (value) { 798 case 1: 799 return "Normal"; 800 case 2: 801 return "Portrait"; 802 case 3: 803 return "Scenery"; 804 case 4: 805 return "Sports"; 806 case 5: 807 return "Night Portrait"; 808 case 6: 809 return "Program"; 810 case 7: 811 return "Aperture Priority"; 812 case 8: 813 return "Shutter Priority"; 814 case 9: 815 return "Macro"; 816 case 10: 817 return "Spot"; 818 case 11: 819 return "Manual"; 820 case 12: 821 return "Movie Preview"; 822 case 13: 823 return "Panning"; 824 case 14: 825 return "Simple"; 826 case 15: 827 return "Color Effects"; 828 case 16: 829 return "Self Portrait"; 830 case 17: 831 return "Economy"; 832 case 18: 833 return "Fireworks"; 834 case 19: 835 return "Party"; 836 case 20: 837 return "Snow"; 838 case 21: 839 return "Night Scenery"; 840 case 22: 841 return "Food"; 842 case 23: 843 return "Baby"; 844 case 24: 845 return "Soft Skin"; 846 case 25: 847 return "Candlelight"; 848 case 26: 849 return "Starry Night"; 850 case 27: 851 return "High Sensitivity"; 852 case 28: 853 return "Panorama Assist"; 854 case 29: 855 return "Underwater"; 856 case 30: 857 return "Beach"; 858 case 31: 859 return "Aerial Photo"; 860 case 32: 861 return "Sunset"; 862 case 33: 863 return "Pet"; 864 case 34: 865 return "Intelligent ISO"; 866 case 35: 867 return "Clipboard"; 868 case 36: 869 return "High Speed Continuous Shooting"; 870 case 37: 871 return "Intelligent Auto"; 872 case 39: 873 return "Multi-aspect"; 874 case 41: 875 return "Transform"; 876 case 42: 877 return "Flash Burst"; 878 case 43: 879 return "Pin Hole"; 880 case 44: 881 return "Film Grain"; 882 case 45: 883 return "My Color"; 884 case 46: 885 return "Photo Frame"; 886 case 51: 887 return "HDR"; 888 default: 889 return "Unknown (" + value + ")"; 890 } 891 } 892 893 @Nullable 894 public String getFocusModeDescription() 895 { 896 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_FOCUS_MODE); 897 if (value == null) 898 return null; 899 switch (value) { 900 case 1: 901 return "Auto"; 902 case 2: 903 return "Manual"; 904 case 4: 905 return "Auto, Focus Button"; 906 case 5: 907 return "Auto, Continuous"; 908 default: 909 return "Unknown (" + value + ")"; 910 } 911 } 912 913 @Nullable 914 public String getAfAreaModeDescription() 915 { 916 int[] value = _directory.getIntArray(PanasonicMakernoteDirectory.TAG_AF_AREA_MODE); 917 if (value == null || value.length < 2) 918 return null; 919 switch (value[0]) { 920 case 0: 921 switch (value[1]) { 922 case 1: 923 return "Spot Mode On"; 924 case 16: 925 return "Spot Mode Off"; 926 default: 927 return "Unknown (" + value[0] + " " + value[1] + ")"; 928 } 929 case 1: 930 switch (value[1]) { 931 case 0: 932 return "Spot Focusing"; 933 case 1: 934 return "5-area"; 935 default: 936 return "Unknown (" + value[0] + " " + value[1] + ")"; 937 } 938 case 16: 939 switch (value[1]) { 940 case 0: 941 return "1-area"; 942 case 16: 943 return "1-area (high speed)"; 944 default: 945 return "Unknown (" + value[0] + " " + value[1] + ")"; 946 } 947 case 32: 948 switch (value[1]) { 949 case 0: 950 return "Auto or Face Detect"; 951 case 1: 952 return "3-area (left)"; 953 case 2: 954 return "3-area (center)"; 955 case 3: 956 return "3-area (right)"; 957 default: 958 return "Unknown (" + value[0] + " " + value[1] + ")"; 959 } 960 case 64: 961 return "Face Detect"; 962 default: 963 return "Unknown (" + value[0] + " " + value[1] + ")"; 964 } 965 } 966 967 @Nullable 968 public String getQualityModeDescription() 969 { 970 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_QUALITY_MODE); 971 if (value == null) 972 return null; 973 switch (value) { 974 case 2: 975 return "High"; 976 case 3: 977 return "Normal"; 978 case 6: 979 return "Very High"; 980 case 7: 981 return "Raw"; 982 case 9: 983 return "Motion Picture"; 984 default: 985 return "Unknown (" + value + ")"; 986 } 987 } 988 989 @Nullable 990 public String getVersionDescription() 991 { 992 return convertBytesToVersionString(_directory.getIntArray(PanasonicMakernoteDirectory.TAG_VERSION), 2); 993 } 994 995 @Nullable 996 public String getMakernoteVersionDescription() 997 { 998 return convertBytesToVersionString(_directory.getIntArray(PanasonicMakernoteDirectory.TAG_MAKERNOTE_VERSION), 2); 999 } 1000 1001 @Nullable 1002 public String getExifVersionDescription() 1003 { 1004 return convertBytesToVersionString(_directory.getIntArray(PanasonicMakernoteDirectory.TAG_EXIF_VERSION), 2); 1005 } 1006 1007 @Nullable 1008 public String getInternalSerialNumberDescription() 1009 { 1010 final byte[] bytes = _directory.getByteArray(PanasonicMakernoteDirectory.TAG_INTERNAL_SERIAL_NUMBER); 1011 1012 if (bytes==null) 1013 return null; 1014 1015 int length = bytes.length; 1016 for (int index = 0; index < bytes.length; index++) { 1017 int i = bytes[index] & 0xFF; 1018 if (i == 0 || i > 0x7F) { 1019 length = index; 1020 break; 1021 } 1022 } 1023 1024 return new String(bytes, 0, length); 1025 } 1026 1027 @Nullable 1028 public String getWhiteBalanceDescription() 1029 { 1030 Integer value = _directory.getInteger(PanasonicMakernoteDirectory.TAG_WHITE_BALANCE); 1031 if (value == null) 1032 return null; 1033 switch (value) { 1034 case 1: 1035 return "Auto"; 1036 case 2: 1037 return "Daylight"; 1038 case 3: 1039 return "Cloudy"; 1040 case 4: 1041 return "Incandescent"; 1042 case 5: 1043 return "Manual"; 1044 case 8: 1045 return "Flash"; 1046 case 10: 1047 return "Black & White"; 1048 case 11: 1049 return "Manual"; 1050 case 12: 1051 return "Shade"; 1052 default: 1053 return "Unknown (" + value + ")"; 1054 } 1055 } 1056 1057 @Nullable 1058 public String getBabyAgeDescription() 1059 { 1060 final Age age = _directory.getAge(PanasonicMakernoteDirectory.TAG_BABY_AGE); 1061 if (age==null) 1062 return null; 1063 return age.toFriendlyString(); 1064 } 1065 1066 @Nullable 1067 public String getBabyAge1Description() 1068 { 1069 final Age age = _directory.getAge(PanasonicMakernoteDirectory.TAG_BABY_AGE_1); 1070 if (age==null) 1071 return null; 1072 return age.toFriendlyString(); 1073 } 1074 1075 @Nullable 1076 private String getTextDescription(int tag) 1077 { 1078 byte[] values = _directory.getByteArray(tag); 1079 if (values == null) 1080 return null; 1081 try { 1082 return new String(values, "ASCII").trim(); 1083 } catch (UnsupportedEncodingException e) { 1084 return null; 1085 } 1086 } 1087 1088 @Nullable 1089 private String getOnOffDescription(int tag) 1090 { 1091 Integer value = _directory.getInteger(tag); 1092 if (value == null) 1093 return null; 1094 switch (value) { 1095 case 1: 1096 return "Off"; 1097 case 2: 1098 return "On"; 1099 default: 1100 return "Unknown (" + value + ")"; 1101 } 1102 } 85 1103 } -
src/com/drew/metadata/exif/PanasonicMakernoteDirectory.java
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.BufferBoundsException; 24 import com.drew.lang.BufferReader; 25 import com.drew.lang.ByteArrayReader; 26 import com.drew.lang.annotations.NotNull; 27 import com.drew.lang.annotations.Nullable; 28 import com.drew.metadata.Age; 19 29 import com.drew.metadata.Directory; 30 import com.drew.metadata.Face; 20 31 21 32 import java.util.HashMap; 22 33 23 34 /** 35 * Describes tags specific to Panasonic and Leica cameras. 24 36 * 37 * @author Drew Noakes http://drewnoakes.com, Philipp Sandhaus 25 38 */ 26 39 public class PanasonicMakernoteDirectory extends Directory 27 40 { 28 public static final int TAG_PANASONIC_QUALITY_MODE = 0x0001; 29 public static final int TAG_PANASONIC_VERSION = 0x0002; 41 30 42 /** 31 * 1 = On 32 * 2 = Off 43 * <br> 44 * 2 = High <br> 45 * 3 = Normal <br> 46 * 6 = Very High <br> 47 * 7 = Raw <br> 48 * 9 = Motion Picture <br> 33 49 */ 34 public static final int TAG_PANASONIC_MACRO_MODE = 0x001C; 50 public static final int TAG_QUALITY_MODE = 0x0001; 51 public static final int TAG_VERSION = 0x0002; 52 53 /** 54 * <br> 55 * 1 = Auto <br> 56 * 2 = Daylight <br> 57 * 3 = Cloudy <br> 58 * 4 = Incandescent <br> 59 * 5 = Manual <br> 60 * 8 = Flash <br> 61 * 10 = Black & White <br> 62 * 11 = Manual <br> 63 * 12 = Shade <br> 64 */ 65 public static final int TAG_WHITE_BALANCE = 0x0003; 66 67 68 /** 69 * <br> 70 * 1 = Auto <br> 71 * 2 = Manual <br> 72 * 4 = Auto, Focus Button <br> 73 * 5 = Auto, Continuous <br> 74 */ 75 public static final int TAG_FOCUS_MODE = 0x0007; 76 35 77 /** 36 * 1 = Normal 37 * 2 = Portrait 38 * 9 = Macro 78 * <br> 79 * 2 bytes <br> 80 * (DMC-FZ10) <br> 81 * '0 1' = Spot Mode On <br> 82 * '0 16' = Spot Mode Off <br> 83 * '(other models) <br> 84 * 16 = Normal? <br> 85 * '0 1' = 9-area <br> 86 * '0 16' = 3-area (high speed) <br> 87 * '1 0' = Spot Focusing <br> 88 * '1 1' = 5-area <br> 89 * '16 0' = 1-area <br> 90 * '16 16' = 1-area (high speed) <br> 91 * '32 0' = Auto or Face Detect <br> 92 * '32 1' = 3-area (left)? <br> 93 * '32 2' = 3-area (center)? <br> 94 * '32 3' = 3-area (right)? <br> 95 * '64 0' = Face Detect <br> 39 96 */ 40 public static final int TAG_PANASONIC_RECORD_MODE = 0x001F; 41 public static final int TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO = 0x0E00; 97 public static final int TAG_AF_AREA_MODE = 0x000f; 42 98 43 protected static final HashMap tagNameMap = new HashMap(); 99 /** 100 * <br> 101 * 2 = On, Mode 1 <br> 102 * 3 = Off <br> 103 * 4 = On, Mode 2 <br> 104 */ 105 public static final int TAG_IMAGE_STABILIZATION = 0x001a; 44 106 107 /** 108 * <br> 109 * 1 = On <br> 110 * 2 = Off <br> 111 */ 112 public static final int TAG_MACRO_MODE = 0x001C; 113 114 /** 115 * <br> 116 * 1 = Normal <br> 117 * 2 = Portrait <br> 118 * 3 = Scenery <br> 119 * 4 = Sports <br> 120 * 5 = Night Portrait <br> 121 * 6 = Program <br> 122 * 7 = Aperture Priority <br> 123 * 8 = Shutter Priority <br> 124 * 9 = Macro <br> 125 * 10= Spot <br> 126 * 11= Manual <br> 127 * 12= Movie Preview <br> 128 * 13= Panning <br> 129 * 14= Simple <br> 130 * 15= Color Effects <br> 131 * 16= Self Portrait <br> 132 * 17= Economy <br> 133 * 18= Fireworks <br> 134 * 19= Party <br> 135 * 20= Snow <br> 136 * 21= Night Scenery <br> 137 * 22= Food <br> 138 * 23= Baby <br> 139 * 24= Soft Skin <br> 140 * 25= Candlelight <br> 141 * 26= Starry Night <br> 142 * 27= High Sensitivity <br> 143 * 28= Panorama Assist <br> 144 * 29= Underwater <br> 145 * 30= Beach <br> 146 * 31= Aerial Photo <br> 147 * 32= Sunset <br> 148 * 33= Pet <br> 149 * 34= Intelligent ISO <br> 150 * 35= Clipboard <br> 151 * 36= High Speed Continuous Shooting <br> 152 * 37= Intelligent Auto <br> 153 * 39= Multi-aspect <br> 154 * 41= Transform <br> 155 * 42= Flash Burst <br> 156 * 43= Pin Hole <br> 157 * 44= Film Grain <br> 158 * 45= My Color <br> 159 * 46= Photo Frame <br> 160 * 51= HDR <br> 161 */ 162 public static final int TAG_RECORD_MODE = 0x001F; 163 164 /** 165 * 1 = Yes <br> 166 * 2 = No <br> 167 */ 168 public static final int TAG_AUDIO = 0x0020; 169 170 /** 171 * No idea, what this is 172 */ 173 public static final int TAG_UNKNOWN_DATA_DUMP = 0x0021; 174 175 public static final int TAG_WHITE_BALANCE_BIAS = 0x0023; 176 public static final int TAG_FLASH_BIAS = 0x0024; 177 178 /** 179 * this number is unique, and contains the date of manufacture, 180 * but is not the same as the number printed on the camera body 181 */ 182 public static final int TAG_INTERNAL_SERIAL_NUMBER = 0x0025; 183 184 /** 185 * Panasonic Exif Version 186 */ 187 public static final int TAG_EXIF_VERSION = 0x0026; 188 189 190 /** 191 * 1 = Off <br> 192 * 2 = Warm <br> 193 * 3 = Cool <br> 194 * 4 = Black & White <br> 195 * 5 = Sepia <br> 196 */ 197 public static final int TAG_COLOR_EFFECT = 0x0028; 198 199 /** 200 * 4 Bytes <br> 201 * Time in 1/100 s from when the camera was powered on to when the 202 * image is written to memory card 203 */ 204 public static final int TAG_UPTIME = 0x0029; 205 206 207 /** 208 * 0 = Off <br> 209 * 1 = On <br> 210 * 2 = Infinite <br> 211 * 4 = Unlimited <br> 212 */ 213 public static final int TAG_BURST_MODE = 0x002a; 214 215 public static final int TAG_SEQUENCE_NUMBER = 0x002b; 216 217 /** 218 * (this decoding seems to work for some models such as the LC1, LX2, FZ7, FZ8, FZ18 and FZ50, but may not be correct for other models such as the FX10, G1, L1, L10 and LC80) <br> 219 * 0x0 = Normal <br> 220 * 0x1 = Low <br> 221 * 0x2 = High <br> 222 * 0x6 = Medium Low <br> 223 * 0x7 = Medium High <br> 224 * 0x100 = Low <br> 225 * 0x110 = Normal <br> 226 * 0x120 = High <br> 227 * (these values are used by the GF1) <br> 228 * 0 = -2 <br> 229 * 1 = -1 <br> 230 * 2 = Normal <br> 231 * 3 = +1 <br> 232 * 4 = +2 <br> 233 * 7 = Nature (Color Film) <br> 234 * 12 = Smooth (Color Film) or Pure (My Color) <br> 235 * 17 = Dynamic (B&W Film) <br> 236 * 22 = Smooth (B&W Film) <br> 237 * 27 = Dynamic (Color Film) <br> 238 * 32 = Vibrant (Color Film) or Expressive (My Color) <br> 239 * 33 = Elegant (My Color) <br> 240 * 37 = Nostalgic (Color Film) <br> 241 * 41 = Dynamic Art (My Color) <br> 242 * 42 = Retro (My Color) <br> 243 */ 244 public static final int TAG_CONTRAST_MODE = 0x002c; 245 246 247 /** 248 * 0 = Standard <br> 249 * 1 = Low (-1) <br> 250 * 2 = High (+1) <br> 251 * 3 = Lowest (-2) <br> 252 * 4 = Highest (+2) <br> 253 */ 254 public static final int TAG_NOISE_REDUCTION = 0x002d; 255 256 /** 257 * 1 = Off <br> 258 * 2 = 10 s <br> 259 * 3 = 2 s <br> 260 */ 261 public static final int TAG_SELF_TIMER = 0x002e; 262 263 /** 264 * 1 = 0 DG <br> 265 * 3 = 180 DG <br> 266 * 6 = 90 DG <br> 267 * 8 = 270 DG <br> 268 */ 269 public static final int TAG_ROTATION = 0x0030; 270 271 /** 272 * 1 = Fired <br> 273 * 2 = Enabled nut not used <br> 274 * 3 = Disabled but required <br> 275 * 4 = Disabled and not required 276 */ 277 public static final int TAG_AF_ASSIST_LAMP = 0x0031; 278 279 /** 280 * 0 = Normal <br> 281 * 1 = Natural<br> 282 * 2 = Vivid 283 * 284 */ 285 public static final int TAG_COLOR_MODE = 0x0032; 286 287 public static final int TAG_BABY_AGE = 0x0033; 288 289 /** 290 * 1 = Standard <br> 291 * 2 = Extended 292 */ 293 public static final int TAG_OPTICAL_ZOOM_MODE = 0x0034; 294 295 /** 296 * 1 = Off <br> 297 * 2 = Wide <br> 298 * 3 = Telephoto <br> 299 * 4 = Macro 300 */ 301 public static final int TAG_CONVERSION_LENS = 0x0035; 302 303 public static final int TAG_TRAVEL_DAY = 0x0036; 304 305 /** 306 * 0 = Normal 307 */ 308 public static final int TAG_CONTRAST = 0x0039; 309 310 /** 311 * <br> 312 * 1 = Home <br> 313 * 2 = Destination 314 */ 315 public static final int TAG_WORLD_TIME_LOCATION = 0x003a; 316 317 /** 318 * 1 = Off <br> 319 * 2 = On 320 */ 321 public static final int TAG_TEXT_STAMP = 0x003b; 322 323 public static final int TAG_PROGRAM_ISO = 0x003c; 324 325 /** 326 * <br> 327 * 1 = Normal <br> 328 * 2 = Outdoor/Illuminations/Flower/HDR Art <br> 329 * 3 = Indoor/Architecture/Objects/HDR B&W <br> 330 * 4 = Creative <br> 331 * 5 = Auto <br> 332 * 7 = Expressive <br> 333 * 8 = Retro <br> 334 * 9 = Pure <br> 335 * 10 = Elegant <br> 336 * 12 = Monochrome <br> 337 * 13 = Dynamic Art <br> 338 * 14 = Silhouette <br> 339 */ 340 public static final int TAG_ADVANCED_SCENE_MODE = 0x003d; 341 342 /** 343 * 1 = Off <br> 344 * 2 = On 345 */ 346 public static final int TAG_TEXT_STAMP_1 = 0x003e; 347 348 public static final int TAG_FACES_DETECTED = 0x003f; 349 350 public static final int TAG_SATURATION = 0x0040; 351 public static final int TAG_SHARPNESS = 0x0041; 352 public static final int TAG_FILM_MODE = 0x0042; 353 354 /** 355 * WB adjust AB. Positive is a shift toward blue. 356 */ 357 public static final int TAG_WB_ADJUST_AB = 0x0046; 358 /** 359 * WB adjust GM. Positive is a shift toward green. 360 */ 361 public static final int TAG_WB_ADJUST_GM = 0x0047; 362 363 364 public static final int TAG_AF_POINT_POSITION = 0x004d; 365 366 367 /** 368 * <br> 369 * Integer (16Bit) Indexes: <br> 370 * 0 Number Face Positions (maybe less than Faces Detected) <br> 371 * 1-4 Face Position 1 <br> 372 * 5-8 Face Position 2 <br> 373 * and so on <br> 374 * <br> 375 * The four Integers are interpreted as follows: <br> 376 * (XYWH) X,Y Center of Face, (W,H) Width and Height <br> 377 * All values are in respect to double the size of the thumbnail image <br> 378 * 379 */ 380 public static final int TAG_FACE_DETECTION_INFO = 0x004e; 381 public static final int TAG_LENS_TYPE = 0x0051; 382 public static final int TAG_LENS_SERIAL_NUMBER = 0x0052; 383 public static final int TAG_ACCESSORY_TYPE = 0x0053; 384 385 /** 386 * (decoded as two 16-bit signed integers) 387 * '-1 1' = Slim Low 388 * '-3 2' = Slim High 389 * '0 0' = Off 390 * '1 1' = Stretch Low 391 * '3 2' = Stretch High 392 */ 393 public static final int TAG_TRANSFORM = 0x0059; 394 395 /** 396 * 0 = Off <br> 397 * 1 = Low <br> 398 * 2 = Standard <br> 399 * 3 = High 400 */ 401 public static final int TAG_INTELLIGENT_EXPOSURE = 0x005d; 402 403 /** 404 * Info at http://www.ozhiker.com/electronics/pjmt/jpeg_info/pim.html 405 * 406 */ 407 public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00; 408 409 410 411 /** 412 * Byte Indexes: <br> 413 * 0 Int (2 Byte) Number of Recognized Faces <br> 414 * 4 String(20 Byte) Recognized Face 1 Name <br> 415 * 24 4 Int (8 Byte) Recognized Face 1 Position (Same Format as Face Detection) <br> 416 * 32 String(20 Byte) Recognized Face 1 Age <br> 417 * 52 String(20 Byte) Recognized Face 2 Name <br> 418 * 72 4 Int (8 Byte) Recognized Face 2 Position (Same Format as Face Detection) <br> 419 * 80 String(20 Byte) Recognized Face 2 Age <br> 420 * <br> 421 * And so on <br> 422 * <br> 423 * The four Integers are interpreted as follows: <br> 424 * (XYWH) X,Y Center of Face, (W,H) Width and Height <br> 425 * All values are in respect to double the size of the thumbnail image <br> 426 * 427 */ 428 public static final int TAG_FACE_RECOGNITION_INFO = 0x0061; 429 430 /** 431 * 0 = No <br> 432 * 1 = Yes 433 */ 434 public static final int TAG_FLASH_WARNING = 0x0062; 435 public static final int TAG_RECOGNIZED_FACE_FLAGS = 0x0063; 436 public static final int TAG_TITLE = 0x0065; 437 public static final int TAG_BABY_NAME = 0x0066; 438 public static final int TAG_LOCATION = 0x0067; 439 public static final int TAG_COUNTRY = 0x0069; 440 public static final int TAG_STATE = 0x006b; 441 public static final int TAG_CITY = 0x006d; 442 public static final int TAG_LANDMARK = 0x006f; 443 444 /** 445 * 0 = Off <br> 446 * 2 = Auto <br> 447 * 3 = On 448 */ 449 public static final int TAG_INTELLIGENT_RESOLUTION = 0x0070; 450 451 public static final int TAG_MAKERNOTE_VERSION = 0x8000; 452 public static final int TAG_SCENE_MODE = 0x8001; 453 public static final int TAG_WB_RED_LEVEL = 0x8004; 454 public static final int TAG_WB_GREEN_LEVEL = 0x8005; 455 public static final int TAG_WB_BLUE_LEVEL = 0x8006; 456 public static final int TAG_FLASH_FIRED = 0x8007; 457 public static final int TAG_TEXT_STAMP_2 = 0x8008; 458 public static final int TAG_TEXT_STAMP_3 = 0x8009; 459 public static final int TAG_BABY_AGE_1 = 0x8010; 460 461 /** 462 * (decoded as two 16-bit signed integers) 463 * '-1 1' = Slim Low 464 * '-3 2' = Slim High 465 * '0 0' = Off 466 * '1 1' = Stretch Low 467 * '3 2' = Stretch High 468 */ 469 public static final int TAG_TRANSFORM_1 = 0x8012; 470 471 @NotNull 472 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 473 45 474 static 46 475 { 47 tagNameMap.put(new Integer(TAG_PANASONIC_QUALITY_MODE), "Quality Mode"); 48 tagNameMap.put(new Integer(TAG_PANASONIC_VERSION), "Version"); 49 tagNameMap.put(new Integer(TAG_PANASONIC_MACRO_MODE), "Macro Mode"); 50 tagNameMap.put(new Integer(TAG_PANASONIC_RECORD_MODE), "Record Mode"); 51 tagNameMap.put(new Integer(TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO), "Print Image Matching (PIM) Info"); 476 _tagNameMap.put(TAG_QUALITY_MODE, "Quality Mode"); 477 _tagNameMap.put(TAG_VERSION, "Version"); 478 _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance"); 479 _tagNameMap.put(TAG_FOCUS_MODE, "Focus Mode"); 480 _tagNameMap.put(TAG_AF_AREA_MODE, "AF Area Mode"); 481 _tagNameMap.put(TAG_IMAGE_STABILIZATION, "Image Stabilization"); 482 _tagNameMap.put(TAG_MACRO_MODE, "Macro Mode"); 483 _tagNameMap.put(TAG_RECORD_MODE, "Record Mode"); 484 _tagNameMap.put(TAG_AUDIO, "Audio"); 485 _tagNameMap.put(TAG_INTERNAL_SERIAL_NUMBER, "Internal Serial Number"); 486 _tagNameMap.put(TAG_UNKNOWN_DATA_DUMP, "Unknown Data Dump"); 487 _tagNameMap.put(TAG_WHITE_BALANCE_BIAS, "White Balance Bias"); 488 _tagNameMap.put(TAG_FLASH_BIAS, "Flash Bias"); 489 _tagNameMap.put(TAG_EXIF_VERSION, "Exif Version"); 490 _tagNameMap.put(TAG_COLOR_EFFECT, "Color Effect"); 491 _tagNameMap.put(TAG_UPTIME, "Camera Uptime"); 492 _tagNameMap.put(TAG_BURST_MODE, "Burst Mode"); 493 _tagNameMap.put(TAG_SEQUENCE_NUMBER, "Sequence Number"); 494 _tagNameMap.put(TAG_CONTRAST_MODE, "Contrast Mode"); 495 _tagNameMap.put(TAG_NOISE_REDUCTION, "Noise Reduction"); 496 _tagNameMap.put(TAG_SELF_TIMER, "Self Timer"); 497 _tagNameMap.put(TAG_ROTATION, "Rotation"); 498 _tagNameMap.put(TAG_AF_ASSIST_LAMP, "AF Assist Lamp"); 499 _tagNameMap.put(TAG_COLOR_MODE, "Color Mode"); 500 _tagNameMap.put(TAG_BABY_AGE, "Baby Age"); 501 _tagNameMap.put(TAG_OPTICAL_ZOOM_MODE, "Optical Zoom Mode"); 502 _tagNameMap.put(TAG_CONVERSION_LENS, "Conversion Lens"); 503 _tagNameMap.put(TAG_TRAVEL_DAY, "Travel Day"); 504 _tagNameMap.put(TAG_CONTRAST, "Contrast"); 505 _tagNameMap.put(TAG_WORLD_TIME_LOCATION, "World Time Location"); 506 _tagNameMap.put(TAG_TEXT_STAMP, "Text Stamp"); 507 _tagNameMap.put(TAG_PROGRAM_ISO, "Program ISO"); 508 _tagNameMap.put(TAG_ADVANCED_SCENE_MODE, "Advanced Scene Mode"); 509 _tagNameMap.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info"); 510 _tagNameMap.put(TAG_FACES_DETECTED, "Number of Detected Faces"); 511 _tagNameMap.put(TAG_SATURATION, "Saturation"); 512 _tagNameMap.put(TAG_SHARPNESS, "Sharpness"); 513 _tagNameMap.put(TAG_FILM_MODE, "Film Mode"); 514 _tagNameMap.put(TAG_WB_ADJUST_AB, "White Balance Adjust (AB)"); 515 _tagNameMap.put(TAG_WB_ADJUST_GM, "White Balance Adjust (GM)"); 516 _tagNameMap.put(TAG_AF_POINT_POSITION, "Af Point Position"); 517 _tagNameMap.put(TAG_FACE_DETECTION_INFO, "Face Detection Info"); 518 _tagNameMap.put(TAG_LENS_TYPE, "Lens Type"); 519 _tagNameMap.put(TAG_LENS_SERIAL_NUMBER, "Lens Serial Number"); 520 _tagNameMap.put(TAG_ACCESSORY_TYPE, "Accessory Type"); 521 _tagNameMap.put(TAG_TRANSFORM, "Transform"); 522 _tagNameMap.put(TAG_INTELLIGENT_EXPOSURE, "Intelligent Exposure"); 523 _tagNameMap.put(TAG_FACE_RECOGNITION_INFO, "Face Recognition Info"); 524 _tagNameMap.put(TAG_FLASH_WARNING, "Flash Warning"); 525 _tagNameMap.put(TAG_RECOGNIZED_FACE_FLAGS, "Recognized Face Flags"); 526 _tagNameMap.put(TAG_TITLE, "Title"); 527 _tagNameMap.put(TAG_BABY_NAME, "Baby Name"); 528 _tagNameMap.put(TAG_LOCATION, "Location"); 529 _tagNameMap.put(TAG_COUNTRY, "Country"); 530 _tagNameMap.put(TAG_STATE, "State"); 531 _tagNameMap.put(TAG_CITY, "City"); 532 _tagNameMap.put(TAG_LANDMARK, "Landmark"); 533 _tagNameMap.put(TAG_INTELLIGENT_RESOLUTION, "Intelligent Resolution"); 534 _tagNameMap.put(TAG_MAKERNOTE_VERSION, "Makernote Version"); 535 _tagNameMap.put(TAG_SCENE_MODE, "Scene Mode"); 536 _tagNameMap.put(TAG_WB_RED_LEVEL, "White Balance (Red)"); 537 _tagNameMap.put(TAG_WB_GREEN_LEVEL, "White Balance (Green)"); 538 _tagNameMap.put(TAG_WB_BLUE_LEVEL, "White Balance (Blue)"); 539 _tagNameMap.put(TAG_FLASH_FIRED, "Flash Fired"); 540 _tagNameMap.put(TAG_TEXT_STAMP_1, "Text Stamp 1"); 541 _tagNameMap.put(TAG_TEXT_STAMP_2, "Text Stamp 2"); 542 _tagNameMap.put(TAG_TEXT_STAMP_3, "Text Stamp 3"); 543 _tagNameMap.put(TAG_BABY_AGE_1, "Baby Age 1"); 544 _tagNameMap.put(TAG_TRANSFORM_1, "Transform 1"); 52 545 } 53 546 54 547 public PanasonicMakernoteDirectory() … … 56 549 this.setDescriptor(new PanasonicMakernoteDescriptor(this)); 57 550 } 58 551 552 @NotNull 59 553 public String getName() 60 554 { 61 555 return "Panasonic Makernote"; 62 556 } 63 557 64 protected HashMap getTagNameMap() 558 @NotNull 559 protected HashMap<Integer, String> getTagNameMap() 65 560 { 66 return tagNameMap;561 return _tagNameMap; 67 562 } 563 564 @Nullable 565 public Face[] getDetectedFaces() 566 { 567 byte[] bytes = getByteArray(PanasonicMakernoteDirectory.TAG_FACE_DETECTION_INFO); 568 if (bytes==null) 569 return null; 570 571 BufferReader reader = new ByteArrayReader(bytes); 572 reader.setMotorolaByteOrder(false); 573 574 try { 575 int faceCount = reader.getUInt16(0); 576 if (faceCount==0) 577 return null; 578 Face[] faces = new Face[faceCount]; 579 580 for (int i = 0; i < faceCount; i++) { 581 int offset = 2 + i * 8; 582 faces[i] = new Face( 583 reader.getUInt16(offset), 584 reader.getUInt16(offset + 2), 585 reader.getUInt16(offset + 4), 586 reader.getUInt16(offset + 6) 587 , null, null); 588 } 589 return faces; 590 } catch (BufferBoundsException e) { 591 return null; 592 } 593 } 594 595 @Nullable 596 public Face[] getRecognizedFaces() 597 { 598 byte[] bytes = getByteArray(PanasonicMakernoteDirectory.TAG_FACE_RECOGNITION_INFO); 599 if (bytes == null) 600 return null; 601 602 BufferReader reader = new ByteArrayReader(bytes); 603 reader.setMotorolaByteOrder(false); 604 605 try { 606 int faceCount = reader.getUInt16(0); 607 if (faceCount==0) 608 return null; 609 Face[] faces = new Face[faceCount]; 610 611 for (int i = 0; i < faceCount; i++) { 612 int offset = 4 + i * 44; 613 String name = reader.getString(offset, 20, "ASCII").trim(); 614 String age = reader.getString(offset + 28, 20, "ASCII").trim(); 615 faces[i] = new Face( 616 reader.getUInt16(offset + 20), 617 reader.getUInt16(offset + 22), 618 reader.getUInt16(offset + 24), 619 reader.getUInt16(offset + 26), 620 name, 621 Age.fromPanasonicString(age)); 622 } 623 return faces; 624 } catch (BufferBoundsException e) { 625 return null; 626 } 627 } 628 629 /** 630 * Attempts to convert the underlying string value (as stored in the directory) into an Age object. 631 * @param tag The tag identifier. 632 * @return The parsed Age object, or null if the tag was empty of the value unable to be parsed. 633 */ 634 @Nullable 635 public Age getAge(int tag) 636 { 637 final String ageString = getString(tag); 638 if (ageString==null) 639 return null; 640 return Age.fromPanasonicString(ageString); 641 } 68 642 } -
src/com/drew/metadata/exif/PentaxMakernoteDescriptor.java
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.exif; 16 22 17 import com.drew. metadata.Directory;18 import com.drew. metadata.MetadataException;23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 19 25 import com.drew.metadata.TagDescriptor; 20 26 21 27 /** 22 * Provides human-readable string versions of the tags stored in PentaxMakernoteDirectory.23 * 28 * Provides human-readable string representations of tag values stored in a <code>PentaxMakernoteDirectory</code>. 29 * <p/> 24 30 * Some information about this makernote taken from here: 25 31 * http://www.ozhiker.com/electronics/pjmt/jpeg_info/pentax_mn.html 32 * 33 * @author Drew Noakes http://drewnoakes.com 26 34 */ 27 public class PentaxMakernoteDescriptor extends TagDescriptor 35 public class PentaxMakernoteDescriptor extends TagDescriptor<PentaxMakernoteDirectory> 28 36 { 29 public PentaxMakernoteDescriptor( Directory directory)37 public PentaxMakernoteDescriptor(@NotNull PentaxMakernoteDirectory directory) 30 38 { 31 39 super(directory); 32 40 } 33 41 34 public String getDescription(int tagType) throws MetadataException 42 @Nullable 43 public String getDescription(int tagType) 35 44 { 36 45 switch (tagType) 37 46 { … … 58 67 case PentaxMakernoteDirectory.TAG_PENTAX_COLOUR: 59 68 return getColourDescription(); 60 69 default: 61 return _directory.getString(tagType);70 return super.getDescription(tagType); 62 71 } 63 72 } 64 73 65 public String getColourDescription() throws MetadataException 74 @Nullable 75 public String getColourDescription() 66 76 { 67 if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_COLOUR)) return null; 68 int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_COLOUR); 77 Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_COLOUR); 78 if (value==null) 79 return null; 69 80 switch (value) 70 81 { 71 82 case 1: return "Normal"; … … 75 86 } 76 87 } 77 88 78 public String getIsoSpeedDescription() throws MetadataException 89 @Nullable 90 public String getIsoSpeedDescription() 79 91 { 80 if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_ISO_SPEED)) return null; 81 int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_ISO_SPEED); 92 Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_ISO_SPEED); 93 if (value==null) 94 return null; 82 95 switch (value) 83 96 { 84 97 // TODO there must be other values which aren't catered for here … … 90 103 } 91 104 } 92 105 93 public String getSaturationDescription() throws MetadataException 106 @Nullable 107 public String getSaturationDescription() 94 108 { 95 if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_SATURATION)) return null; 96 int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_SATURATION); 109 Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_SATURATION); 110 if (value==null) 111 return null; 97 112 switch (value) 98 113 { 99 114 case 0: return "Normal"; … … 103 118 } 104 119 } 105 120 106 public String getContrastDescription() throws MetadataException 121 @Nullable 122 public String getContrastDescription() 107 123 { 108 if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_CONTRAST)) return null; 109 int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_CONTRAST); 124 Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_CONTRAST); 125 if (value==null) 126 return null; 110 127 switch (value) 111 128 { 112 129 case 0: return "Normal"; … … 116 133 } 117 134 } 118 135 119 public String getSharpnessDescription() throws MetadataException 136 @Nullable 137 public String getSharpnessDescription() 120 138 { 121 if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_SHARPNESS)) return null; 122 int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_SHARPNESS); 139 Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_SHARPNESS); 140 if (value==null) 141 return null; 123 142 switch (value) 124 143 { 125 144 case 0: return "Normal"; … … 129 148 } 130 149 } 131 150 132 public String getDigitalZoomDescription() throws MetadataException 151 @Nullable 152 public String getDigitalZoomDescription() 133 153 { 134 if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_DIGITAL_ZOOM)) return null; 135 float value = _directory.getFloat(PentaxMakernoteDirectory.TAG_PENTAX_DIGITAL_ZOOM); 154 Float value = _directory.getFloatObject(PentaxMakernoteDirectory.TAG_PENTAX_DIGITAL_ZOOM); 155 if (value==null) 156 return null; 136 157 if (value==0) 137 158 return "Off"; 138 159 return Float.toString(value); 139 160 } 140 161 141 public String getWhiteBalanceDescription() throws MetadataException 162 @Nullable 163 public String getWhiteBalanceDescription() 142 164 { 143 if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_WHITE_BALANCE)) return null; 144 int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_WHITE_BALANCE); 165 Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_WHITE_BALANCE); 166 if (value==null) 167 return null; 145 168 switch (value) 146 169 { 147 170 case 0: return "Auto"; … … 154 177 } 155 178 } 156 179 157 public String getFlashModeDescription() throws MetadataException 180 @Nullable 181 public String getFlashModeDescription() 158 182 { 159 if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_FLASH_MODE)) return null; 160 int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_FLASH_MODE); 183 Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_FLASH_MODE); 184 if (value==null) 185 return null; 161 186 switch (value) 162 187 { 163 188 case 1: return "Auto"; … … 168 193 } 169 194 } 170 195 171 public String getFocusModeDescription() throws MetadataException 196 @Nullable 197 public String getFocusModeDescription() 172 198 { 173 if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_FOCUS_MODE)) return null; 174 int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_FOCUS_MODE); 199 Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_FOCUS_MODE); 200 if (value==null) 201 return null; 175 202 switch (value) 176 203 { 177 204 case 2: return "Custom"; … … 180 207 } 181 208 } 182 209 183 public String getQualityLevelDescription() throws MetadataException 210 @Nullable 211 public String getQualityLevelDescription() 184 212 { 185 if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_QUALITY_LEVEL)) return null; 186 int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_QUALITY_LEVEL); 213 Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_QUALITY_LEVEL); 214 if (value==null) 215 return null; 187 216 switch (value) 188 217 { 189 218 case 0: return "Good"; … … 193 222 } 194 223 } 195 224 196 public String getCaptureModeDescription() throws MetadataException 225 @Nullable 226 public String getCaptureModeDescription() 197 227 { 198 if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PENTAX_CAPTURE_MODE)) return null; 199 int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PENTAX_CAPTURE_MODE); 228 Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PENTAX_CAPTURE_MODE); 229 if (value==null) 230 return null; 200 231 switch (value) 201 232 { 202 233 case 1: return "Auto"; … … 208 239 } 209 240 210 241 /* 211 public String getPrintImageMatchingInfoDescription() throws MetadataException242 public String getPrintImageMatchingInfoDescription() 212 243 { 213 if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO)) return null;214 244 byte[] bytes = _directory.getByteArray(PentaxMakernoteDirectory.TAG_PANASONIC_PRINT_IMAGE_MATCHING_INFO); 245 if (bytes==null) 246 return null; 215 247 return "(" + bytes.length + " bytes)"; 216 248 } 217 249 218 public String getMacroModeDescription() throws MetadataException250 public String getMacroModeDescription() 219 251 { 220 if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PANASONIC_MACRO_MODE)) return null; 221 int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PANASONIC_MACRO_MODE); 252 Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PANASONIC_MACRO_MODE); 253 if (value==null) 254 return null; 222 255 switch (value) { 223 256 case 1: 224 257 return "On"; … … 229 262 } 230 263 } 231 264 232 public String getRecordModeDescription() throws MetadataException265 public String getRecordModeDescription() 233 266 { 234 if (!_directory.containsTag(PentaxMakernoteDirectory.TAG_PANASONIC_RECORD_MODE)) return null; 235 int value = _directory.getInt(PentaxMakernoteDirectory.TAG_PANASONIC_RECORD_MODE); 267 Integer value = _directory.getInteger(PentaxMakernoteDirectory.TAG_PANASONIC_RECORD_MODE); 268 if (value==null) 269 return null; 236 270 switch (value) { 237 271 case 1: 238 272 return "Normal"; -
src/com/drew/metadata/exif/PentaxMakernoteDirectory.java
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 21 26 import java.util.HashMap; 22 27 23 28 /** 24 * Directory for metadata specific to Pentax and Asahi cameras. 29 * Describes tags specific to Pentax and Asahi cameras. 30 * 31 * @author Drew Noakes http://drewnoakes.com 25 32 */ 26 33 public class PentaxMakernoteDirectory extends Directory 27 34 { … … 121 128 */ 122 129 public static final int TAG_PENTAX_DAYLIGHT_SAVINGS = 0x1001; 123 130 124 protected static final HashMap tagNameMap = new HashMap(); 131 @NotNull 132 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 125 133 126 134 static 127 135 { 128 tagNameMap.put(new Integer(TAG_PENTAX_CAPTURE_MODE), "Capture Mode");129 tagNameMap.put(new Integer(TAG_PENTAX_QUALITY_LEVEL), "Quality Level");130 tagNameMap.put(new Integer(TAG_PENTAX_FOCUS_MODE), "Focus Mode");131 tagNameMap.put(new Integer(TAG_PENTAX_FLASH_MODE), "Flash Mode");132 tagNameMap.put(new Integer(TAG_PENTAX_WHITE_BALANCE), "White Balance");133 tagNameMap.put(new Integer(TAG_PENTAX_DIGITAL_ZOOM), "Digital Zoom");134 tagNameMap.put(new Integer(TAG_PENTAX_SHARPNESS), "Sharpness");135 tagNameMap.put(new Integer(TAG_PENTAX_CONTRAST), "Contrast");136 tagNameMap.put(new Integer(TAG_PENTAX_SATURATION), "Saturation");137 tagNameMap.put(new Integer(TAG_PENTAX_ISO_SPEED), "ISO Speed");138 tagNameMap.put(new Integer(TAG_PENTAX_COLOUR), "Colour");139 tagNameMap.put(new Integer(TAG_PENTAX_PRINT_IMAGE_MATCHING_INFO), "Print Image Matching (PIM) Info");140 tagNameMap.put(new Integer(TAG_PENTAX_TIME_ZONE), "Time Zone");141 tagNameMap.put(new Integer(TAG_PENTAX_DAYLIGHT_SAVINGS), "Daylight Savings");136 _tagNameMap.put(TAG_PENTAX_CAPTURE_MODE, "Capture Mode"); 137 _tagNameMap.put(TAG_PENTAX_QUALITY_LEVEL, "Quality Level"); 138 _tagNameMap.put(TAG_PENTAX_FOCUS_MODE, "Focus Mode"); 139 _tagNameMap.put(TAG_PENTAX_FLASH_MODE, "Flash Mode"); 140 _tagNameMap.put(TAG_PENTAX_WHITE_BALANCE, "White Balance"); 141 _tagNameMap.put(TAG_PENTAX_DIGITAL_ZOOM, "Digital Zoom"); 142 _tagNameMap.put(TAG_PENTAX_SHARPNESS, "Sharpness"); 143 _tagNameMap.put(TAG_PENTAX_CONTRAST, "Contrast"); 144 _tagNameMap.put(TAG_PENTAX_SATURATION, "Saturation"); 145 _tagNameMap.put(TAG_PENTAX_ISO_SPEED, "ISO Speed"); 146 _tagNameMap.put(TAG_PENTAX_COLOUR, "Colour"); 147 _tagNameMap.put(TAG_PENTAX_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching (PIM) Info"); 148 _tagNameMap.put(TAG_PENTAX_TIME_ZONE, "Time Zone"); 149 _tagNameMap.put(TAG_PENTAX_DAYLIGHT_SAVINGS, "Daylight Savings"); 142 150 } 143 151 144 152 public PentaxMakernoteDirectory() … … 146 154 this.setDescriptor(new PentaxMakernoteDescriptor(this)); 147 155 } 148 156 157 @NotNull 149 158 public String getName() 150 159 { 151 160 return "Pentax Makernote"; 152 161 } 153 162 154 protected HashMap getTagNameMap() 163 @NotNull 164 protected HashMap<Integer, String> getTagNameMap() 155 165 { 156 return tagNameMap;166 return _tagNameMap; 157 167 } 158 168 } -
src/com/drew/metadata/exif/SigmaMakernoteDescriptor.java
1 /* 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/ 20 */ 21 22 package com.drew.metadata.exif; 23 24 import com.drew.lang.annotations.NotNull; 25 import com.drew.metadata.TagDescriptor; 26 27 /** 28 * Provides human-readable string representations of tag values stored in a <code>SigmaMakernoteDirectory</code>. 29 * 30 * @author Drew Noakes http://drewnoakes.com 31 */ 32 public class SigmaMakernoteDescriptor extends TagDescriptor<SigmaMakernoteDirectory> 33 { 34 public SigmaMakernoteDescriptor(@NotNull SigmaMakernoteDirectory directory) 35 { 36 super(directory); 37 } 38 } -
src/com/drew/metadata/exif/SigmaMakernoteDescriptor.java
-
src/com/drew/metadata/exif/SigmaMakernoteDirectory.java
Modification de propriétés sur src/com/drew/metadata/exif/SigmaMakernoteDescriptor.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
1 /* 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/ 20 */ 21 22 package com.drew.metadata.exif; 23 24 import com.drew.lang.annotations.NotNull; 25 import com.drew.metadata.Directory; 26 27 import java.util.HashMap; 28 29 /** 30 * Describes tags specific to Sigma / Foveon cameras. 31 * 32 * @author Drew Noakes http://drewnoakes.com 33 */ 34 public class SigmaMakernoteDirectory extends Directory 35 { 36 public static final int TAG_SERIAL_NUMBER = 2; 37 public static final int TAG_DRIVE_MODE = 3; 38 public static final int TAG_RESOLUTION_MODE = 4; 39 public static final int TAG_AUTO_FOCUS_MODE = 5; 40 public static final int TAG_FOCUS_SETTING = 6; 41 public static final int TAG_WHITE_BALANCE = 7; 42 public static final int TAG_EXPOSURE_MODE = 8; 43 public static final int TAG_METERING_MODE = 9; 44 public static final int TAG_LENS_RANGE = 10; 45 public static final int TAG_COLOR_SPACE = 11; 46 public static final int TAG_EXPOSURE = 12; 47 public static final int TAG_CONTRAST = 13; 48 public static final int TAG_SHADOW = 14; 49 public static final int TAG_HIGHLIGHT = 15; 50 public static final int TAG_SATURATION = 16; 51 public static final int TAG_SHARPNESS = 17; 52 public static final int TAG_FILL_LIGHT = 18; 53 public static final int TAG_COLOR_ADJUSTMENT = 20; 54 public static final int TAG_ADJUSTMENT_MODE = 21; 55 public static final int TAG_QUALITY = 22; 56 public static final int TAG_FIRMWARE = 23; 57 public static final int TAG_SOFTWARE = 24; 58 public static final int TAG_AUTO_BRACKET = 25; 59 60 @NotNull 61 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 62 63 static 64 { 65 _tagNameMap.put(TAG_SERIAL_NUMBER, "Serial Number"); 66 _tagNameMap.put(TAG_DRIVE_MODE, "Drive Mode"); 67 _tagNameMap.put(TAG_RESOLUTION_MODE, "Resolution Mode"); 68 _tagNameMap.put(TAG_AUTO_FOCUS_MODE, "Auto Focus Mode"); 69 _tagNameMap.put(TAG_FOCUS_SETTING, "Focus Setting"); 70 _tagNameMap.put(TAG_WHITE_BALANCE, "White Balance"); 71 _tagNameMap.put(TAG_EXPOSURE_MODE, "Exposure Mode"); 72 _tagNameMap.put(TAG_METERING_MODE, "Metering Mode"); 73 _tagNameMap.put(TAG_LENS_RANGE, "Lens Range"); 74 _tagNameMap.put(TAG_COLOR_SPACE, "Color Space"); 75 _tagNameMap.put(TAG_EXPOSURE, "Exposure"); 76 _tagNameMap.put(TAG_CONTRAST, "Contrast"); 77 _tagNameMap.put(TAG_SHADOW, "Shadow"); 78 _tagNameMap.put(TAG_HIGHLIGHT, "Highlight"); 79 _tagNameMap.put(TAG_SATURATION, "Saturation"); 80 _tagNameMap.put(TAG_SHARPNESS, "Sharpness"); 81 _tagNameMap.put(TAG_FILL_LIGHT, "Fill Light"); 82 _tagNameMap.put(TAG_COLOR_ADJUSTMENT, "Color Adjustment"); 83 _tagNameMap.put(TAG_ADJUSTMENT_MODE, "Adjustment Mode"); 84 _tagNameMap.put(TAG_QUALITY, "Quality"); 85 _tagNameMap.put(TAG_FIRMWARE, "Firmware"); 86 _tagNameMap.put(TAG_SOFTWARE, "Software"); 87 _tagNameMap.put(TAG_AUTO_BRACKET, "Auto Bracket"); 88 } 89 90 91 public SigmaMakernoteDirectory() 92 { 93 this.setDescriptor(new SigmaMakernoteDescriptor(this)); 94 } 95 96 @NotNull 97 public String getName() 98 { 99 return "Sigma Makernote"; 100 } 101 102 @NotNull 103 protected HashMap<Integer, String> getTagNameMap() 104 { 105 return _tagNameMap; 106 } 107 } -
src/com/drew/metadata/exif/SigmaMakernoteDirectory.java
-
src/com/drew/metadata/exif/SonyMakernoteDescriptor.java
Modification de propriétés sur src/com/drew/metadata/exif/SigmaMakernoteDirectory.java ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
1 /*2 * This is public domain software - that is, you can do whatever you want3 * with it, and include it software that is licensed under the GNU or the4 * BSD license, or whatever other licence you choose, including proprietary5 * 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 the8 * 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.com12 * Latest version of this software kept at13 * http://drewnoakes.com/14 */15 package com.drew.metadata.exif;16 17 import com.drew.metadata.Directory;18 import com.drew.metadata.MetadataException;19 import com.drew.metadata.TagDescriptor;20 21 /**22 * Provides human-readable string versions of the tags stored in a SonyMakernoteDirectory.23 * Thanks to David Carson for the initial version of this class.24 */25 public class SonyMakernoteDescriptor extends TagDescriptor26 {27 public SonyMakernoteDescriptor(Directory directory)28 {29 super(directory);30 }31 32 public String getDescription(int tagType) throws MetadataException33 {34 return _directory.getString(tagType);35 }36 } -
src/com/drew/metadata/exif/SonyMakernoteDirectory.java
1 /*2 * This is public domain software - that is, you can do whatever you want3 * with it, and include it software that is licensed under the GNU or the4 * BSD license, or whatever other licence you choose, including proprietary5 * 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 the8 * 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.com12 * Latest version of this software kept at13 * http://drewnoakes.com/14 */15 package com.drew.metadata.exif;16 17 import com.drew.metadata.Directory;18 19 import java.util.HashMap;20 21 /**22 * Describes tags specific to Sony cameras.23 */24 public class SonyMakernoteDirectory extends Directory25 {26 protected static final HashMap _tagNameMap = new HashMap();27 28 public String getName()29 {30 return "Sony Makernote";31 }32 33 protected HashMap getTagNameMap()34 {35 return _tagNameMap;36 }37 } -
src/com/drew/metadata/exif/SonyType1MakernoteDescriptor.java
1 /* 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/ 20 */ 21 package com.drew.metadata.exif; 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 25 import com.drew.metadata.TagDescriptor; 26 27 /** 28 * Provides human-readable string representations of tag values stored in a <code>SonyType1MakernoteDirectory</code>. 29 * Thanks to David Carson for the initial version of this class. 30 * 31 * @author Drew Noakes http://drewnoakes.com 32 */ 33 public class SonyType1MakernoteDescriptor extends TagDescriptor<SonyType1MakernoteDirectory> 34 { 35 public SonyType1MakernoteDescriptor(@NotNull SonyType1MakernoteDirectory directory) 36 { 37 super(directory); 38 } 39 40 @Nullable 41 public String getDescription(int tagType) 42 { 43 switch (tagType) { 44 case SonyType1MakernoteDirectory.TAG_COLOR_TEMPERATURE: 45 return getColorTemperatureDescription(); 46 case SonyType1MakernoteDirectory.TAG_SCENE_MODE: 47 return getSceneModeDescription(); 48 case SonyType1MakernoteDirectory.TAG_ZONE_MATCHING: 49 return getZoneMatchingDescription(); 50 case SonyType1MakernoteDirectory.TAG_DYNAMIC_RANGE_OPTIMISER: 51 return getDynamicRangeOptimizerDescription(); 52 case SonyType1MakernoteDirectory.TAG_IMAGE_STABILISATION: 53 return getImageStabilizationDescription(); 54 // Unfortunately it seems that there is no definite mapping between a lens ID and a lens model 55 // http://gvsoft.homedns.org/exif/makernote-sony-type1.html#0xb027 56 // case SonyType1MakernoteDirectory.TAG_LENS_ID: 57 // return getLensIDDescription(); 58 case SonyType1MakernoteDirectory.TAG_COLOR_MODE: 59 return getColorModeDescription(); 60 case SonyType1MakernoteDirectory.TAG_MACRO: 61 return getMacroDescription(); 62 case SonyType1MakernoteDirectory.TAG_EXPOSURE_MODE: 63 return getExposureModeDescription(); 64 case SonyType1MakernoteDirectory.TAG_QUALITY: 65 return getQualityDescription(); 66 case SonyType1MakernoteDirectory.TAG_ANTI_BLUR: 67 return getAntiBlurDescription(); 68 case SonyType1MakernoteDirectory.TAG_LONG_EXPOSURE_NOISE_REDUCTION: 69 return getLongExposureNoiseReductionDescription(); 70 default: 71 return super.getDescription(tagType); 72 } 73 } 74 75 @Nullable 76 public String getColorTemperatureDescription() 77 { 78 Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_COLOR_TEMPERATURE); 79 if (value==null) 80 return null; 81 if (value==0) 82 return "Auto"; 83 int kelvin = ((value & 0x00FF0000) >> 8) | ((value & 0xFF000000) >> 24); 84 return String.format("%d K", kelvin); 85 } 86 87 @Nullable 88 public String getSceneModeDescription() 89 { 90 Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_SCENE_MODE); 91 if (value==null) 92 return null; 93 switch (value){ 94 case 0: return "Standard"; 95 case 1: return "Portrait"; 96 case 2: return "Text"; 97 case 3: return "Night Scene"; 98 case 4: return "Sunset"; 99 case 5: return "Sports"; 100 case 6: return "Landscape"; 101 case 7: return "Night Portrait"; 102 case 8: return "Macro"; 103 case 9: return "Super Macro"; 104 case 16: return "Auto"; 105 case 17: return "Night View/Portrait"; 106 default: return String.format("Unknown (%d)", value); 107 } 108 } 109 110 @Nullable 111 public String getZoneMatchingDescription() 112 { 113 Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_ZONE_MATCHING); 114 if (value==null) 115 return null; 116 switch (value){ 117 case 0: return "ISO Setting Used"; 118 case 1: return "High Key"; 119 case 2: return "Low Key"; 120 default: return String.format("Unknown (%d)", value); 121 } 122 } 123 124 @Nullable 125 public String getDynamicRangeOptimizerDescription() 126 { 127 Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_DYNAMIC_RANGE_OPTIMISER); 128 if (value==null) 129 return null; 130 switch (value){ 131 case 0: return "Off"; 132 case 1: return "Standard"; 133 case 2: return "Advanced Auto"; 134 case 8: return "Advanced LV1"; 135 case 9: return "Advanced LV2"; 136 case 10: return "Advanced LV3"; 137 case 11: return "Advanced LV4"; 138 case 12: return "Advanced LV5"; 139 default: return String.format("Unknown (%d)", value); 140 } 141 } 142 143 @Nullable 144 public String getImageStabilizationDescription() 145 { 146 Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_IMAGE_STABILISATION); 147 if (value==null) 148 return null; 149 // Different non-zero 'on' values have been observed. Do they mean different things? 150 return value == 0 ? "Off" : "On"; 151 } 152 153 @Nullable 154 public String getColorModeDescription() 155 { 156 Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_COLOR_MODE); 157 if (value==null) 158 return null; 159 switch (value){ 160 case 0: return "Standard"; 161 case 1: return "Vivid"; 162 case 2: return "Portrait"; 163 case 3: return "Landscape"; 164 case 4: return "Sunset"; 165 case 5: return "Night Portrait"; 166 case 6: return "Black & White"; 167 case 7: return "Adobe RGB"; 168 case 12: 169 case 100: return "Neutral"; 170 case 101: return "Clear"; 171 case 102: return "Deep"; 172 case 103: return "Light"; 173 case 104: return "Night View"; 174 case 105: return "Autumn Leaves"; 175 default: return String.format("Unknown (%d)", value); 176 } 177 } 178 179 @Nullable 180 public String getMacroDescription() 181 { 182 Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_MACRO); 183 if (value==null) 184 return null; 185 switch (value){ 186 case 0: return "Off"; 187 case 1: return "On"; 188 case 2: return "Magnifying Glass/Super Macro"; 189 case 0xFFFF: return "N/A"; 190 default: return String.format("Unknown (%d)", value); 191 } 192 } 193 194 @Nullable 195 public String getExposureModeDescription() 196 { 197 Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_EXPOSURE_MODE); 198 if (value==null) 199 return null; 200 switch (value){ 201 case 0: return "Auto"; 202 case 5: return "Landscape"; 203 case 6: return "Program"; 204 case 7: return "Aperture Priority"; 205 case 8: return "Shutter Priority"; 206 case 9: return "Night Scene"; 207 case 15: return "Manual"; 208 case 0xFFFF: return "N/A"; 209 default: return String.format("Unknown (%d)", value); 210 } 211 } 212 213 @Nullable 214 public String getQualityDescription() 215 { 216 Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_QUALITY); 217 if (value==null) 218 return null; 219 switch (value){ 220 case 0: return "Normal"; 221 case 1: return "Fine"; 222 case 0xFFFF: return "N/A"; 223 default: return String.format("Unknown (%d)", value); 224 } 225 } 226 227 @Nullable 228 public String getAntiBlurDescription() 229 { 230 Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_ANTI_BLUR); 231 if (value==null) 232 return null; 233 switch (value){ 234 case 0: return "Off"; 235 case 1: return "On (Continuous)"; 236 case 2: return "On (Shooting)"; 237 case 0xFFFF: return "N/A"; 238 default: return String.format("Unknown (%d)", value); 239 } 240 } 241 242 @Nullable 243 public String getLongExposureNoiseReductionDescription() 244 { 245 Integer value = _directory.getInteger(SonyType1MakernoteDirectory.TAG_LONG_EXPOSURE_NOISE_REDUCTION); 246 if (value==null) 247 return null; 248 switch (value){ 249 case 0: return "Off"; 250 case 1: return "On"; 251 case 0xFFFF: return "N/A"; 252 default: return String.format("Unknown (%d)", value); 253 } 254 } 255 } -
src/com/drew/metadata/exif/SonyType1MakernoteDescriptor.java
-
src/com/drew/metadata/exif/SonyType1MakernoteDirectory.java
Modification de propriétés sur src/com/drew/metadata/exif/SonyType1MakernoteDescriptor.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
1 /* 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/ 20 */ 21 package com.drew.metadata.exif; 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.metadata.Directory; 25 26 import java.util.HashMap; 27 28 /** 29 * Describes tags specific to Sony cameras that use the Sony Type 1 makernote tags. 30 * 31 * @author Drew Noakes http://drewnoakes.com 32 */ 33 public class SonyType1MakernoteDirectory extends Directory 34 { 35 public static final int TAG_PRINT_IMAGE_MATCHING_INFO = 0x0E00; 36 public static final int TAG_PREVIEW_IMAGE = 0x2001; 37 public static final int TAG_COLOR_MODE_SETTING = 0xb020; 38 public static final int TAG_COLOR_TEMPERATURE = 0xb021; 39 public static final int TAG_SCENE_MODE = 0xb023; 40 public static final int TAG_ZONE_MATCHING = 0xb024; 41 public static final int TAG_DYNAMIC_RANGE_OPTIMISER = 0xb025; 42 public static final int TAG_IMAGE_STABILISATION = 0xb026; 43 public static final int TAG_LENS_ID = 0xb027; 44 public static final int TAG_MINOLTA_MAKER_NOTE = 0xb028; 45 public static final int TAG_COLOR_MODE = 0xb029; 46 public static final int TAG_MACRO = 0xb040; 47 public static final int TAG_EXPOSURE_MODE = 0xb041; 48 public static final int TAG_QUALITY = 0xb047; 49 public static final int TAG_ANTI_BLUR = 0xb04B; 50 public static final int TAG_LONG_EXPOSURE_NOISE_REDUCTION = 0xb04E; 51 public static final int TAG_NO_PRINT = 0xFFFF; 52 53 @NotNull 54 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 55 56 static 57 { 58 _tagNameMap.put(TAG_PRINT_IMAGE_MATCHING_INFO, "Print Image Matching Info"); 59 _tagNameMap.put(TAG_PREVIEW_IMAGE, "Preview Image"); 60 _tagNameMap.put(TAG_COLOR_MODE_SETTING, "Color Mode Setting"); 61 _tagNameMap.put(TAG_COLOR_TEMPERATURE, "Color Temperature"); 62 _tagNameMap.put(TAG_SCENE_MODE, "Scene Mode"); 63 _tagNameMap.put(TAG_ZONE_MATCHING, "Zone Matching"); 64 _tagNameMap.put(TAG_DYNAMIC_RANGE_OPTIMISER, "Dynamic Range Optimizer"); 65 _tagNameMap.put(TAG_IMAGE_STABILISATION, "Image Stabilisation"); 66 _tagNameMap.put(TAG_LENS_ID, "Lens ID"); 67 _tagNameMap.put(TAG_MINOLTA_MAKER_NOTE, "Minolta Maker Note"); 68 _tagNameMap.put(TAG_COLOR_MODE, "Color Mode"); 69 _tagNameMap.put(TAG_MACRO, "Macro"); 70 _tagNameMap.put(TAG_EXPOSURE_MODE, "Exposure Mode"); 71 _tagNameMap.put(TAG_QUALITY, "Quality"); 72 _tagNameMap.put(TAG_ANTI_BLUR, "Anti Blur"); 73 _tagNameMap.put(TAG_LONG_EXPOSURE_NOISE_REDUCTION, "Long Exposure Noise Reduction"); 74 _tagNameMap.put(TAG_NO_PRINT, "No Print"); 75 } 76 77 public SonyType1MakernoteDirectory() 78 { 79 this.setDescriptor(new SonyType1MakernoteDescriptor(this)); 80 } 81 82 @NotNull 83 public String getName() 84 { 85 return "Sony Makernote"; 86 } 87 88 @NotNull 89 protected HashMap<Integer, String> getTagNameMap() 90 { 91 return _tagNameMap; 92 } 93 } -
src/com/drew/metadata/exif/SonyType1MakernoteDirectory.java
-
src/com/drew/metadata/exif/SonyType6MakernoteDescriptor.java
Modification de propriétés sur src/com/drew/metadata/exif/SonyType1MakernoteDirectory.java ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
1 /* 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/ 20 */ 21 22 package com.drew.metadata.exif; 23 24 import com.drew.lang.annotations.NotNull; 25 import com.drew.lang.annotations.Nullable; 26 import com.drew.metadata.TagDescriptor; 27 28 /** 29 * Provides human-readable string representations of tag values stored in a <code>SonyType6MakernoteDirectory</code>. 30 * 31 * @author Drew Noakes http://drewnoakes.com 32 */ 33 public class SonyType6MakernoteDescriptor extends TagDescriptor<SonyType6MakernoteDirectory> 34 { 35 public SonyType6MakernoteDescriptor(@NotNull SonyType6MakernoteDirectory directory) 36 { 37 super(directory); 38 } 39 40 @Nullable 41 public String getDescription(int tagType) 42 { 43 switch (tagType) { 44 case SonyType6MakernoteDirectory.TAG_MAKER_NOTE_THUMB_VERSION: 45 return getMakerNoteThumbVersionDescription(); 46 default: 47 return super.getDescription(tagType); 48 } 49 } 50 51 @Nullable 52 public String getMakerNoteThumbVersionDescription() 53 { 54 int[] values = _directory.getIntArray(SonyType6MakernoteDirectory.TAG_MAKER_NOTE_THUMB_VERSION); 55 return convertBytesToVersionString(values, 2); 56 } 57 } -
src/com/drew/metadata/exif/SonyType6MakernoteDescriptor.java
-
src/com/drew/metadata/exif/SonyType6MakernoteDirectory.java
Modification de propriétés sur src/com/drew/metadata/exif/SonyType6MakernoteDescriptor.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
1 /* 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/ 20 */ 21 22 package com.drew.metadata.exif; 23 24 import com.drew.lang.annotations.NotNull; 25 import com.drew.metadata.Directory; 26 27 import java.util.HashMap; 28 29 /** 30 * Describes tags specific to Sony cameras that use the Sony Type 6 makernote tags. 31 * 32 * @author Drew Noakes http://drewnoakes.com 33 */ 34 public class SonyType6MakernoteDirectory extends Directory 35 { 36 public static final int TAG_MAKER_NOTE_THUMB_OFFSET = 0x0513; 37 public static final int TAG_MAKER_NOTE_THUMB_LENGTH = 0x0514; 38 public static final int TAG_UNKNOWN_1 = 0x0515; 39 public static final int TAG_MAKER_NOTE_THUMB_VERSION = 0x2000; 40 41 @NotNull 42 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 43 44 static 45 { 46 _tagNameMap.put(TAG_MAKER_NOTE_THUMB_OFFSET, "Maker Note Thumb Offset"); 47 _tagNameMap.put(TAG_MAKER_NOTE_THUMB_LENGTH, "Maker Note Thumb Length"); 48 _tagNameMap.put(TAG_UNKNOWN_1, "Sony-6-0x0203"); 49 _tagNameMap.put(TAG_MAKER_NOTE_THUMB_VERSION, "Maker Note Thumb Version"); 50 } 51 52 public SonyType6MakernoteDirectory() 53 { 54 this.setDescriptor(new SonyType6MakernoteDescriptor(this)); 55 } 56 57 @NotNull 58 public String getName() 59 { 60 return "Sony Makernote"; 61 } 62 63 @NotNull 64 protected HashMap<Integer, String> getTagNameMap() 65 { 66 return _tagNameMap; 67 } 68 } -
src/com/drew/metadata/exif/SonyType6MakernoteDirectory.java
-
src/com/drew/metadata/Face.java
Modification de propriétés sur src/com/drew/metadata/exif/SonyType6MakernoteDirectory.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
1 /* 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/ 20 */ 21 package com.drew.metadata; 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 25 26 /** 27 * Class to hold information about a detected or recognized face in a photo. 28 * <p/> 29 * When a face is <em>detected</em>, the camera believes that a face is present at a given location in 30 * the image, but is not sure whose face it is. When a face is <em>recognised</em>, then the face is 31 * both detected and identified as belonging to a known person. 32 * 33 * @author Philipp Sandhaus, Drew Noakes 34 */ 35 public class Face 36 { 37 private final int _x; 38 private final int _y; 39 private final int _width; 40 private final int _height; 41 @Nullable 42 private final String _name; 43 @Nullable 44 private final Age _age; 45 46 public Face(int x, int y, int width, int height, @Nullable String name, @Nullable Age age) 47 { 48 _x = x; 49 _y = y; 50 _width = width; 51 _height = height; 52 _name = name; 53 _age = age; 54 } 55 56 public int getX() 57 { 58 return _x; 59 } 60 61 public int getY() 62 { 63 return _y; 64 } 65 66 public int getWidth() 67 { 68 return _width; 69 } 70 71 public int getHeight() 72 { 73 return _height; 74 } 75 76 @Nullable 77 public String getName() 78 { 79 return _name; 80 } 81 82 @Nullable 83 public Age getAge() 84 { 85 return _age; 86 } 87 88 @Override 89 public boolean equals(@Nullable Object o) 90 { 91 if (this == o) return true; 92 if (o == null || getClass() != o.getClass()) return false; 93 94 Face face = (Face)o; 95 96 if (_height != face._height) return false; 97 if (_width != face._width) return false; 98 if (_x != face._x) return false; 99 if (_y != face._y) return false; 100 if (_age != null ? !_age.equals(face._age) : face._age != null) return false; 101 if (_name != null ? !_name.equals(face._name) : face._name != null) return false; 102 103 return true; 104 } 105 106 @Override 107 public int hashCode() 108 { 109 int result = _x; 110 result = 31 * result + _y; 111 result = 31 * result + _width; 112 result = 31 * result + _height; 113 result = 31 * result + (_name != null ? _name.hashCode() : 0); 114 result = 31 * result + (_age != null ? _age.hashCode() : 0); 115 return result; 116 } 117 118 @NotNull 119 public String toString() 120 { 121 StringBuilder result = new StringBuilder(); 122 result.append("x: ").append(_x); 123 result.append(" y: ").append(_y); 124 result.append(" width: ").append(_width); 125 result.append(" height: ").append(_height); 126 if (_name != null) 127 result.append(" name: ").append(_name); 128 if (_age != null) 129 result.append(" age: ").append(_age.toFriendlyString()); 130 return result.toString(); 131 } 132 } -
src/com/drew/metadata/Face.java
-
src/com/drew/metadata/iptc/IptcDescriptor.java
Modification de propriétés sur src/com/drew/metadata/Face.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Date Author Id Revision HeadURL \ No newline at end of property
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 21-Nov-2002 17:58: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.iptc; 18 22 19 import com.drew.metadata.Directory; 23 import com.drew.lang.StringUtil; 24 import com.drew.lang.annotations.NotNull; 25 import com.drew.lang.annotations.Nullable; 20 26 import com.drew.metadata.TagDescriptor; 21 27 22 28 /** 29 * Provides human-readable string representations of tag values stored in a <code>IptcDirectory</code>. 30 * <p/> 31 * As the IPTC directory already stores values as strings, this class simply returns the tag's value. 23 32 * 33 * @author Drew Noakes http://drewnoakes.com 24 34 */ 25 public class IptcDescriptor extends TagDescriptor 35 public class IptcDescriptor extends TagDescriptor<IptcDirectory> 26 36 { 27 public IptcDescriptor( Directory directory)37 public IptcDescriptor(@NotNull IptcDirectory directory) 28 38 { 29 39 super(directory); 30 40 } 31 41 42 @Nullable 32 43 public String getDescription(int tagType) 33 44 { 34 return _directory.getString(tagType); 45 switch (tagType) { 46 case IptcDirectory.TAG_FILE_FORMAT: 47 return getFileFormatDescription(); 48 case IptcDirectory.TAG_KEYWORDS: 49 return getKeywordsDescription(); 50 default: 51 return super.getDescription(tagType); 52 } 35 53 } 54 55 @Nullable 56 public String getFileFormatDescription() 57 { 58 Integer value = _directory.getInteger(IptcDirectory.TAG_FILE_FORMAT); 59 if (value == null) 60 return null; 61 switch (value) { 62 case 0: return "No ObjectData"; 63 case 1: return "IPTC-NAA Digital Newsphoto Parameter Record"; 64 case 2: return "IPTC7901 Recommended Message Format"; 65 case 3: return "Tagged Image File Format (Adobe/Aldus Image data)"; 66 case 4: return "Illustrator (Adobe Graphics data)"; 67 case 5: return "AppleSingle (Apple Computer Inc)"; 68 case 6: return "NAA 89-3 (ANPA 1312)"; 69 case 7: return "MacBinary II"; 70 case 8: return "IPTC Unstructured Character Oriented File Format (UCOFF)"; 71 case 9: return "United Press International ANPA 1312 variant"; 72 case 10: return "United Press International Down-Load Message"; 73 case 11: return "JPEG File Interchange (JFIF)"; 74 case 12: return "Photo-CD Image-Pac (Eastman Kodak)"; 75 case 13: return "Bit Mapped Graphics File [.BMP] (Microsoft)"; 76 case 14: return "Digital Audio File [.WAV] (Microsoft & Creative Labs)"; 77 case 15: return "Audio plus Moving Video [.AVI] (Microsoft)"; 78 case 16: return "PC DOS/Windows Executable Files [.COM][.EXE]"; 79 case 17: return "Compressed Binary File [.ZIP] (PKWare Inc)"; 80 case 18: return "Audio Interchange File Format AIFF (Apple Computer Inc)"; 81 case 19: return "RIFF Wave (Microsoft Corporation)"; 82 case 20: return "Freehand (Macromedia/Aldus)"; 83 case 21: return "Hypertext Markup Language [.HTML] (The Internet Society)"; 84 case 22: return "MPEG 2 Audio Layer 2 (Musicom), ISO/IEC"; 85 case 23: return "MPEG 2 Audio Layer 3, ISO/IEC"; 86 case 24: return "Portable Document File [.PDF] Adobe"; 87 case 25: return "News Industry Text Format (NITF)"; 88 case 26: return "Tape Archive [.TAR]"; 89 case 27: return "Tidningarnas Telegrambyra NITF version (TTNITF DTD)"; 90 case 28: return "Ritzaus Bureau NITF version (RBNITF DTD)"; 91 case 29: return "Corel Draw [.CDR]"; 92 } 93 return String.format("Unknown (%d)", value); 94 } 95 96 @Nullable 97 public String getByLineDescription() 98 { 99 return _directory.getString(IptcDirectory.TAG_BY_LINE); 100 } 101 102 @Nullable 103 public String getByLineTitleDescription() 104 { 105 return _directory.getString(IptcDirectory.TAG_BY_LINE_TITLE); 106 } 107 108 @Nullable 109 public String getCaptionDescription() 110 { 111 return _directory.getString(IptcDirectory.TAG_CAPTION); 112 } 113 114 @Nullable 115 public String getCategoryDescription() 116 { 117 return _directory.getString(IptcDirectory.TAG_CATEGORY); 118 } 119 120 @Nullable 121 public String getCityDescription() 122 { 123 return _directory.getString(IptcDirectory.TAG_CITY); 124 } 125 126 @Nullable 127 public String getCopyrightNoticeDescription() 128 { 129 return _directory.getString(IptcDirectory.TAG_COPYRIGHT_NOTICE); 130 } 131 132 @Nullable 133 public String getCountryOrPrimaryLocationDescription() 134 { 135 return _directory.getString(IptcDirectory.TAG_COUNTRY_OR_PRIMARY_LOCATION_NAME); 136 } 137 138 @Nullable 139 public String getCreditDescription() 140 { 141 return _directory.getString(IptcDirectory.TAG_CREDIT); 142 } 143 144 @Nullable 145 public String getDateCreatedDescription() 146 { 147 return _directory.getString(IptcDirectory.TAG_DATE_CREATED); 148 } 149 150 @Nullable 151 public String getHeadlineDescription() 152 { 153 return _directory.getString(IptcDirectory.TAG_HEADLINE); 154 } 155 156 @Nullable 157 public String getKeywordsDescription() 158 { 159 final String[] keywords = _directory.getStringArray(IptcDirectory.TAG_KEYWORDS); 160 if (keywords==null) 161 return null; 162 return StringUtil.join(keywords, ";"); 163 } 164 165 @Nullable 166 public String getObjectNameDescription() 167 { 168 return _directory.getString(IptcDirectory.TAG_OBJECT_NAME); 169 } 170 171 @Nullable 172 public String getOriginalTransmissionReferenceDescription() 173 { 174 return _directory.getString(IptcDirectory.TAG_ORIGINAL_TRANSMISSION_REFERENCE); 175 } 176 177 @Nullable 178 public String getOriginatingProgramDescription() 179 { 180 return _directory.getString(IptcDirectory.TAG_ORIGINATING_PROGRAM); 181 } 182 183 @Nullable 184 public String getProvinceOrStateDescription() 185 { 186 return _directory.getString(IptcDirectory.TAG_PROVINCE_OR_STATE); 187 } 188 189 @Nullable 190 public String getRecordVersionDescription() 191 { 192 return _directory.getString(IptcDirectory.TAG_APPLICATION_RECORD_VERSION); 193 } 194 195 @Nullable 196 public String getReleaseDateDescription() 197 { 198 return _directory.getString(IptcDirectory.TAG_RELEASE_DATE); 199 } 200 201 @Nullable 202 public String getReleaseTimeDescription() 203 { 204 return _directory.getString(IptcDirectory.TAG_RELEASE_TIME); 205 } 206 207 @Nullable 208 public String getSourceDescription() 209 { 210 return _directory.getString(IptcDirectory.TAG_SOURCE); 211 } 212 213 @Nullable 214 public String getSpecialInstructionsDescription() 215 { 216 return _directory.getString(IptcDirectory.TAG_SPECIAL_INSTRUCTIONS); 217 } 218 219 @Nullable 220 public String getSupplementalCategoriesDescription() 221 { 222 return _directory.getString(IptcDirectory.TAG_SUPPLEMENTAL_CATEGORIES); 223 } 224 225 @Nullable 226 public String getTimeCreatedDescription() 227 { 228 return _directory.getString(IptcDirectory.TAG_TIME_CREATED); 229 } 230 231 @Nullable 232 public String getUrgencyDescription() 233 { 234 return _directory.getString(IptcDirectory.TAG_URGENCY); 235 } 236 237 @Nullable 238 public String getWriterDescription() 239 { 240 return _directory.getString(IptcDirectory.TAG_CAPTION_WRITER); 241 } 36 242 } -
src/com/drew/metadata/iptc/IptcDirectory.java
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 01:26: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.iptc; 18 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 19 25 import com.drew.metadata.Directory; 20 26 27 import java.util.Arrays; 21 28 import java.util.HashMap; 29 import java.util.List; 22 30 23 31 /** 32 * Describes tags used by the International Press Telecommunications Council (IPTC) metadata format. 24 33 * 34 * @author Drew Noakes http://drewnoakes.com 25 35 */ 26 36 public class IptcDirectory extends Directory 27 37 { 28 public static final int TAG_RECORD_VERSION = 0x0200; 29 public static final int TAG_CAPTION = 0x0278; 30 public static final int TAG_WRITER = 0x027a; 31 public static final int TAG_HEADLINE = 0x0269; 32 public static final int TAG_SPECIAL_INSTRUCTIONS = 0x0228; 33 public static final int TAG_BY_LINE = 0x0250; 34 public static final int TAG_BY_LINE_TITLE = 0x0255; 35 public static final int TAG_CREDIT = 0x026e; 36 public static final int TAG_SOURCE = 0x0273; 37 public static final int TAG_OBJECT_NAME = 0x0205; 38 public static final int TAG_DATE_CREATED = 0x0237; 39 public static final int TAG_CITY = 0x025a; 40 public static final int TAG_PROVINCE_OR_STATE = 0x025f; 41 public static final int TAG_COUNTRY_OR_PRIMARY_LOCATION = 0x0265; 42 public static final int TAG_ORIGINAL_TRANSMISSION_REFERENCE = 0x0267; 43 public static final int TAG_CATEGORY = 0x020f; 44 public static final int TAG_SUPPLEMENTAL_CATEGORIES = 0x0214; 45 public static final int TAG_URGENCY = 0x0200 | 10; 46 public static final int TAG_KEYWORDS = 0x0200 | 25; 47 public static final int TAG_COPYRIGHT_NOTICE = 0x0274; 48 public static final int TAG_RELEASE_DATE = 0x0200 | 30; 49 public static final int TAG_RELEASE_TIME = 0x0200 | 35; 50 public static final int TAG_TIME_CREATED = 0x0200 | 60; 51 public static final int TAG_ORIGINATING_PROGRAM = 0x0200 | 65; 38 // IPTC EnvelopeRecord Tags 39 public static final int TAG_ENVELOPE_RECORD_VERSION = 0x0100; // 0 + 0x0100 40 public static final int TAG_DESTINATION = 0x0105; // 5 41 public static final int TAG_FILE_FORMAT = 0x0114; // 20 42 public static final int TAG_FILE_VERSION = 0x0116; // 22 43 public static final int TAG_SERVICE_ID = 0x011E; // 30 44 public static final int TAG_ENVELOPE_NUMBER = 0x0128; // 40 45 public static final int TAG_PRODUCT_ID = 0x0132; // 50 46 public static final int TAG_ENVELOPE_PRIORITY = 0x013C; // 60 47 public static final int TAG_DATE_SENT = 0x0146; // 70 48 public static final int TAG_TIME_SENT = 0x0150; // 80 49 public static final int TAG_CODED_CHARACTER_SET = 0x015A; // 90 50 public static final int TAG_UNIQUE_OBJECT_NAME = 0x0164; // 100 51 public static final int TAG_ARM_IDENTIFIER = 0x0178; // 120 52 public static final int TAG_ARM_VERSION = 0x017a; // 122 52 53 53 protected static final HashMap tagNameMap = new HashMap(); 54 // IPTC ApplicationRecord Tags 55 public static final int TAG_APPLICATION_RECORD_VERSION = 0x0200; // 0 + 0x0200 56 public static final int TAG_OBJECT_TYPE_REFERENCE = 0x0203; // 3 57 public static final int TAG_OBJECT_ATTRIBUTE_REFERENCE = 0x0204; // 4 58 public static final int TAG_OBJECT_NAME = 0x0205; // 5 59 public static final int TAG_EDIT_STATUS = 0x0207; // 7 60 public static final int TAG_EDITORIAL_UPDATE = 0x0208; // 8 61 public static final int TAG_URGENCY = 0X020A; // 10 62 public static final int TAG_SUBJECT_REFERENCE = 0X020C; // 12 63 public static final int TAG_CATEGORY = 0x020F; // 15 64 public static final int TAG_SUPPLEMENTAL_CATEGORIES = 0x0214; // 20 65 public static final int TAG_FIXTURE_ID = 0x0216; // 22 66 public static final int TAG_KEYWORDS = 0x0219; // 25 67 public static final int TAG_CONTENT_LOCATION_CODE = 0x021A; // 26 68 public static final int TAG_CONTENT_LOCATION_NAME = 0x021B; // 27 69 public static final int TAG_RELEASE_DATE = 0X021E; // 30 70 public static final int TAG_RELEASE_TIME = 0x0223; // 35 71 public static final int TAG_EXPIRATION_DATE = 0x0225; // 37 72 public static final int TAG_EXPIRATION_TIME = 0x0226; // 38 73 public static final int TAG_SPECIAL_INSTRUCTIONS = 0x0228; // 40 74 public static final int TAG_ACTION_ADVISED = 0x022A; // 42 75 public static final int TAG_REFERENCE_SERVICE = 0x022D; // 45 76 public static final int TAG_REFERENCE_DATE = 0x022F; // 47 77 public static final int TAG_REFERENCE_NUMBER = 0x0232; // 50 78 public static final int TAG_DATE_CREATED = 0x0237; // 55 79 public static final int TAG_TIME_CREATED = 0X023C; // 60 80 public static final int TAG_DIGITAL_DATE_CREATED = 0x023E; // 62 81 public static final int TAG_DIGITAL_TIME_CREATED = 0x023F; // 63 82 public static final int TAG_ORIGINATING_PROGRAM = 0x0241; // 65 83 public static final int TAG_PROGRAM_VERSION = 0x0246; // 70 84 public static final int TAG_OBJECT_CYCLE = 0x024B; // 75 85 public static final int TAG_BY_LINE = 0x0250; // 80 86 public static final int TAG_BY_LINE_TITLE = 0x0255; // 85 87 public static final int TAG_CITY = 0x025A; // 90 88 public static final int TAG_SUB_LOCATION = 0x025C; // 92 89 public static final int TAG_PROVINCE_OR_STATE = 0x025F; // 95 90 public static final int TAG_COUNTRY_OR_PRIMARY_LOCATION_CODE = 0x0264; // 100 91 public static final int TAG_COUNTRY_OR_PRIMARY_LOCATION_NAME = 0x0265; // 101 92 public static final int TAG_ORIGINAL_TRANSMISSION_REFERENCE = 0x0267; // 103 93 public static final int TAG_HEADLINE = 0x0269; // 105 94 public static final int TAG_CREDIT = 0x026E; // 110 95 public static final int TAG_SOURCE = 0x0273; // 115 96 public static final int TAG_COPYRIGHT_NOTICE = 0x0274; // 116 97 public static final int TAG_CONTACT = 0x0276; // 118 98 public static final int TAG_CAPTION = 0x0278; // 120 99 public static final int TAG_LOCAL_CAPTION = 0x0279; // 121 100 public static final int TAG_CAPTION_WRITER = 0x027A; // 122 101 public static final int TAG_RASTERIZED_CAPTION = 0x027D; // 125 102 public static final int TAG_IMAGE_TYPE = 0x0282; // 130 103 public static final int TAG_IMAGE_ORIENTATION = 0x0283; // 131 104 public static final int TAG_LANGUAGE_IDENTIFIER = 0x0287; // 135 105 public static final int TAG_AUDIO_TYPE = 0x0296; // 150 106 public static final int TAG_AUDIO_SAMPLING_RATE = 0x0297; // 151 107 public static final int TAG_AUDIO_SAMPLING_RESOLUTION = 0x0298; // 152 108 public static final int TAG_AUDIO_DURATION = 0x0299; // 153 109 public static final int TAG_AUDIO_OUTCUE = 0x029A; // 154 54 110 111 public static final int TAG_JOB_ID = 0x02B8; // 184 112 public static final int TAG_MASTER_DOCUMENT_ID = 0x02B9; // 185 113 public static final int TAG_SHORT_DOCUMENT_ID = 0x02BA; // 186 114 public static final int TAG_UNIQUE_DOCUMENT_ID = 0x02BB; // 187 115 public static final int TAG_OWNER_ID = 0x02BC; // 188 116 117 public static final int TAG_OBJECT_PREVIEW_FILE_FORMAT = 0x02C8; // 200 118 public static final int TAG_OBJECT_PREVIEW_FILE_FORMAT_VERSION = 0x02C9; // 201 119 public static final int TAG_OBJECT_PREVIEW_DATA = 0x02CA; // 202 120 121 @NotNull 122 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 123 55 124 static 56 125 { 57 tagNameMap.put(new Integer(TAG_RECORD_VERSION), "Directory Version"); 58 tagNameMap.put(new Integer(TAG_CAPTION), "Caption/Abstract"); 59 tagNameMap.put(new Integer(TAG_WRITER), "Writer/Editor"); 60 tagNameMap.put(new Integer(TAG_HEADLINE), "Headline"); 61 tagNameMap.put(new Integer(TAG_SPECIAL_INSTRUCTIONS), "Special Instructions"); 62 tagNameMap.put(new Integer(TAG_BY_LINE), "By-line"); 63 tagNameMap.put(new Integer(TAG_BY_LINE_TITLE), "By-line Title"); 64 tagNameMap.put(new Integer(TAG_CREDIT), "Credit"); 65 tagNameMap.put(new Integer(TAG_SOURCE), "Source"); 66 tagNameMap.put(new Integer(TAG_OBJECT_NAME), "Object Name"); 67 tagNameMap.put(new Integer(TAG_DATE_CREATED), "Date Created"); 68 tagNameMap.put(new Integer(TAG_CITY), "City"); 69 tagNameMap.put(new Integer(TAG_PROVINCE_OR_STATE), "Province/State"); 70 tagNameMap.put(new Integer(TAG_COUNTRY_OR_PRIMARY_LOCATION), "Country/Primary Location"); 71 tagNameMap.put(new Integer(TAG_ORIGINAL_TRANSMISSION_REFERENCE), "Original Transmission Reference"); 72 tagNameMap.put(new Integer(TAG_CATEGORY), "Category"); 73 tagNameMap.put(new Integer(TAG_SUPPLEMENTAL_CATEGORIES), "Supplemental Category(s)"); 74 tagNameMap.put(new Integer(TAG_URGENCY), "Urgency"); 75 tagNameMap.put(new Integer(TAG_KEYWORDS), "Keywords"); 76 tagNameMap.put(new Integer(TAG_COPYRIGHT_NOTICE), "Copyright Notice"); 77 tagNameMap.put(new Integer(TAG_RELEASE_DATE), "Release Date"); 78 tagNameMap.put(new Integer(TAG_RELEASE_TIME), "Release Time"); 79 tagNameMap.put(new Integer(TAG_TIME_CREATED), "Time Created"); 80 tagNameMap.put(new Integer(TAG_ORIGINATING_PROGRAM), "Originating Program"); 126 _tagNameMap.put(TAG_ENVELOPE_RECORD_VERSION, "Enveloped Record Version"); 127 _tagNameMap.put(TAG_DESTINATION, "Destination"); 128 _tagNameMap.put(TAG_FILE_FORMAT, "File Format"); 129 _tagNameMap.put(TAG_FILE_VERSION, "File Version"); 130 _tagNameMap.put(TAG_SERVICE_ID, "Service Identifier"); 131 _tagNameMap.put(TAG_ENVELOPE_NUMBER, "Envelope Number"); 132 _tagNameMap.put(TAG_PRODUCT_ID, "Product Identifier"); 133 _tagNameMap.put(TAG_ENVELOPE_PRIORITY, "Envelope Priority"); 134 _tagNameMap.put(TAG_DATE_SENT, "Date Sent"); 135 _tagNameMap.put(TAG_TIME_SENT, "Time Sent"); 136 _tagNameMap.put(TAG_CODED_CHARACTER_SET, "Coded Character Set"); 137 _tagNameMap.put(TAG_UNIQUE_OBJECT_NAME, "Unique Object Name"); 138 _tagNameMap.put(TAG_ARM_IDENTIFIER, "ARM Identifier"); 139 _tagNameMap.put(TAG_ARM_VERSION, "ARM Version"); 140 141 _tagNameMap.put(TAG_APPLICATION_RECORD_VERSION, "Application Record Version"); 142 _tagNameMap.put(TAG_OBJECT_TYPE_REFERENCE, "Object Type Reference"); 143 _tagNameMap.put(TAG_OBJECT_ATTRIBUTE_REFERENCE, "Object Attribute Reference"); 144 _tagNameMap.put(TAG_OBJECT_NAME, "Object Name"); 145 _tagNameMap.put(TAG_EDIT_STATUS, "Edit Status"); 146 _tagNameMap.put(TAG_EDITORIAL_UPDATE, "Editorial Update"); 147 _tagNameMap.put(TAG_URGENCY, "Urgency"); 148 _tagNameMap.put(TAG_SUBJECT_REFERENCE, "Subject Reference"); 149 _tagNameMap.put(TAG_CATEGORY, "Category"); 150 _tagNameMap.put(TAG_SUPPLEMENTAL_CATEGORIES, "Supplemental Category(s)"); 151 _tagNameMap.put(TAG_FIXTURE_ID, "Fixture Identifier"); 152 _tagNameMap.put(TAG_KEYWORDS, "Keywords"); 153 _tagNameMap.put(TAG_CONTENT_LOCATION_CODE, "Content Location Code"); 154 _tagNameMap.put(TAG_CONTENT_LOCATION_NAME, "Content Location Name"); 155 _tagNameMap.put(TAG_RELEASE_DATE, "Release Date"); 156 _tagNameMap.put(TAG_RELEASE_TIME, "Release Time"); 157 _tagNameMap.put(TAG_EXPIRATION_DATE, "Expiration Date"); 158 _tagNameMap.put(TAG_EXPIRATION_TIME, "Expiration Time"); 159 _tagNameMap.put(TAG_SPECIAL_INSTRUCTIONS, "Special Instructions"); 160 _tagNameMap.put(TAG_ACTION_ADVISED, "Action Advised"); 161 _tagNameMap.put(TAG_REFERENCE_SERVICE, "Reference Service"); 162 _tagNameMap.put(TAG_REFERENCE_DATE, "Reference Date"); 163 _tagNameMap.put(TAG_REFERENCE_NUMBER, "Reference Number"); 164 _tagNameMap.put(TAG_DATE_CREATED, "Date Created"); 165 _tagNameMap.put(TAG_TIME_CREATED, "Time Created"); 166 _tagNameMap.put(TAG_DIGITAL_DATE_CREATED, "Digital Date Created"); 167 _tagNameMap.put(TAG_DIGITAL_TIME_CREATED, "Digital Time Created"); 168 _tagNameMap.put(TAG_ORIGINATING_PROGRAM, "Originating Program"); 169 _tagNameMap.put(TAG_PROGRAM_VERSION, "Program Version"); 170 _tagNameMap.put(TAG_OBJECT_CYCLE, "Object Cycle"); 171 _tagNameMap.put(TAG_BY_LINE, "By-line"); 172 _tagNameMap.put(TAG_BY_LINE_TITLE, "By-line Title"); 173 _tagNameMap.put(TAG_CITY, "City"); 174 _tagNameMap.put(TAG_SUB_LOCATION, "Sub-location"); 175 _tagNameMap.put(TAG_PROVINCE_OR_STATE, "Province/State"); 176 _tagNameMap.put(TAG_COUNTRY_OR_PRIMARY_LOCATION_CODE, "Country/Primary Location Code"); 177 _tagNameMap.put(TAG_COUNTRY_OR_PRIMARY_LOCATION_NAME, "Country/Primary Location Name"); 178 _tagNameMap.put(TAG_ORIGINAL_TRANSMISSION_REFERENCE, "Original Transmission Reference"); 179 _tagNameMap.put(TAG_HEADLINE, "Headline"); 180 _tagNameMap.put(TAG_CREDIT, "Credit"); 181 _tagNameMap.put(TAG_SOURCE, "Source"); 182 _tagNameMap.put(TAG_COPYRIGHT_NOTICE, "Copyright Notice"); 183 _tagNameMap.put(TAG_CONTACT, "Contact"); 184 _tagNameMap.put(TAG_CAPTION, "Caption/Abstract"); 185 _tagNameMap.put(TAG_LOCAL_CAPTION, "Local Caption"); 186 _tagNameMap.put(TAG_CAPTION_WRITER, "Caption Writer/Editor"); 187 _tagNameMap.put(TAG_RASTERIZED_CAPTION, "Rasterized Caption"); 188 _tagNameMap.put(TAG_IMAGE_TYPE, "Image Type"); 189 _tagNameMap.put(TAG_IMAGE_ORIENTATION, "Image Orientation"); 190 _tagNameMap.put(TAG_LANGUAGE_IDENTIFIER, "Language Identifier"); 191 _tagNameMap.put(TAG_AUDIO_TYPE, "Audio Type"); 192 _tagNameMap.put(TAG_AUDIO_SAMPLING_RATE, "Audio Sampling Rate"); 193 _tagNameMap.put(TAG_AUDIO_SAMPLING_RESOLUTION, "Audio Sampling Resolution"); 194 _tagNameMap.put(TAG_AUDIO_DURATION, "Audio Duration"); 195 _tagNameMap.put(TAG_AUDIO_OUTCUE, "Audio Outcue"); 196 197 _tagNameMap.put(TAG_JOB_ID, "Job Identifier"); 198 _tagNameMap.put(TAG_MASTER_DOCUMENT_ID, "Master Document Identifier"); 199 _tagNameMap.put(TAG_SHORT_DOCUMENT_ID, "Short Document Identifier"); 200 _tagNameMap.put(TAG_UNIQUE_DOCUMENT_ID, "Unique Document Identifier"); 201 _tagNameMap.put(TAG_OWNER_ID, "Owner Identifier"); 202 203 _tagNameMap.put(TAG_OBJECT_PREVIEW_FILE_FORMAT, "Object Data Preview File Format"); 204 _tagNameMap.put(TAG_OBJECT_PREVIEW_FILE_FORMAT_VERSION, "Object Data Preview File Format Version"); 205 _tagNameMap.put(TAG_OBJECT_PREVIEW_DATA, "Object Data Preview Data"); 81 206 } 82 207 83 208 public IptcDirectory() … … 85 210 this.setDescriptor(new IptcDescriptor(this)); 86 211 } 87 212 213 @NotNull 88 214 public String getName() 89 215 { 90 216 return "Iptc"; 91 217 } 92 218 93 protected HashMap getTagNameMap() 219 @NotNull 220 protected HashMap<Integer, String> getTagNameMap() 94 221 { 95 return tagNameMap;222 return _tagNameMap; 96 223 } 224 225 /** 226 * Returns any keywords contained in the IPTC data. This value may be <code>null</code>. 227 */ 228 @Nullable 229 public List<String> getKeywords() 230 { 231 final String[] array = getStringArray(IptcDirectory.TAG_KEYWORDS); 232 if (array==null) 233 return null; 234 return Arrays.asList(array); 235 } 97 236 } -
src/com/drew/metadata/iptc/IptcProcessingException.java
1 /*2 * ExifProcessingException.java3 *4 * This class is public domain software - that is, you can do whatever you want5 * with it, and include it software that is licensed under the GNU or the6 * BSD license, or whatever other licence you choose, including proprietary7 * closed source licenses. I do ask that you leave this header in tact.8 *9 * If you make modifications to this code that you think would benefit the10 * wider community, please send me a copy and I'll post it on my site.11 *12 * If you make use of this code, I'd appreciate hearing about it.13 * drew@drewnoakes.com14 * Latest version of this software kept at15 * http://drewnoakes.com/16 *17 * Created on 29 April 2002, 00:3318 */19 20 package com.drew.metadata.iptc;21 22 import com.drew.metadata.MetadataException;23 24 /**25 * The exception type raised during reading of Iptc data in the instance of26 * unexpected data conditions.27 * @author Drew Noakes http://drewnoakes.com28 */29 public class IptcProcessingException extends MetadataException30 {31 /**32 * Constructs an instance of <code>ExifProcessingException</code> with the33 * specified detail message.34 * @param message the detail message35 */36 public IptcProcessingException(String message)37 {38 super(message);39 }40 41 /**42 * Constructs an instance of <code>IptcProcessingException</code> with the43 * specified detail message and inner exception.44 * @param message the detail message45 * @param cause an inner exception46 */47 public IptcProcessingException(String message, Throwable cause)48 {49 super(message, cause);50 }51 } -
src/com/drew/metadata/iptc/IptcReader.java
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 19:00:03 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.iptc; 18 22 19 import com.drew.imaging.jpeg.JpegProcessingException; 20 import com.drew.imaging.jpeg.JpegSegmentReader; 23 import com.drew.lang.BufferBoundsException; 24 import com.drew.lang.BufferReader; 25 import com.drew.lang.annotations.NotNull; 21 26 import com.drew.metadata.Directory; 22 27 import com.drew.metadata.Metadata; 23 import com.drew.metadata.MetadataException;24 28 import com.drew.metadata.MetadataReader; 25 29 26 import java.io.File;27 import java.io.InputStream;28 30 import java.util.Date; 29 31 30 32 /** 33 * Decodes IPTC binary data, populating a <code>Metadata</code> object with tag values in an <code>IptcDirectory</code>. 31 34 * 35 * @author Drew Noakes http://drewnoakes.com 32 36 */ 33 37 public class IptcReader implements MetadataReader 34 38 { 39 // TODO consider breaking the IPTC section up into multiple directories and providing segregation of each IPTC directory 35 40 /* 36 41 public static final int DIRECTORY_IPTC = 2; 37 42 … … 45 50 public static final int DATA_RECORD = 8; 46 51 public static final int POST_DATA_RECORD = 9; 47 52 */ 48 /**49 * The Iptc data segment.50 */51 private final byte[] _data;52 53 53 /** 54 * Creates a new IptcReader for the specified Jpeg jpegFile. 55 */ 56 public IptcReader(File jpegFile) throws JpegProcessingException 54 /** Performs the IPTC data extraction, adding found values to the specified instance of <code>Metadata</code>. */ 55 public void extract(@NotNull final BufferReader reader, @NotNull final Metadata metadata) 57 56 { 58 this(new JpegSegmentReader(jpegFile).readSegment(JpegSegmentReader.SEGMENT_APPD)); 59 } 57 IptcDirectory directory = metadata.getOrCreateDirectory(IptcDirectory.class); 60 58 61 /** Creates an IptcReader for a JPEG stream. 62 * 63 * @param is JPEG stream. Stream will be closed. 64 */ 65 public IptcReader(InputStream is) throws JpegProcessingException 66 { 67 this(new JpegSegmentReader(is).readSegment(JpegSegmentReader.SEGMENT_APPD)); 68 } 59 int offset = 0; 69 60 70 public IptcReader(byte[] data) 71 { 72 _data = data; 73 } 74 75 /** 76 * Performs the Exif data extraction, returning a new instance of <code>Metadata</code>. 77 */ 78 public Metadata extract() 79 { 80 return extract(new Metadata()); 81 } 82 83 /** 84 * Performs the Exif data extraction, adding found values to the specified 85 * instance of <code>Metadata</code>. 86 */ 87 public Metadata extract(Metadata metadata) 88 { 89 if (_data == null) { 90 return metadata; 91 } 92 93 Directory directory = metadata.getDirectory(IptcDirectory.class); 94 95 // find start of data 96 int offset = 0; 61 /* 62 // find start-of-segment marker (potentially need to skip some ASCII photoshop header info) 97 63 try { 98 while (offset < _data.length - 1 && get32Bits(offset) != 0x1c02) {64 while (offset < data.length - 1 && reader.getUInt16(offset) != 0x1c01 && reader.getUInt16(offset) != 0x1c02) 99 65 offset++; 100 } 101 } catch (MetadataException e) { 102 directory.addError("Couldn't find start of Iptc data (invalid segment)"); 103 return metadata; 66 } catch (BufferBoundsException e) { 67 directory.addError("Couldn't find start of IPTC data (invalid segment)"); 68 return; 104 69 } 70 */ 105 71 106 72 // for each tag 107 while (offset < _data.length) { 73 while (offset < reader.getLength()) { 74 108 75 // identifies start of a tag 109 if (_data[offset] != 0x1c) { 76 short startByte; 77 try { 78 startByte = reader.getUInt8(offset); 79 } catch (BufferBoundsException e) { 80 directory.addError("Unable to read starting byte of IPTC tag"); 110 81 break; 111 82 } 83 84 if (startByte != 0x1c) { 85 directory.addError("Invalid start to IPTC tag"); 86 break; 87 } 88 112 89 // we need at least five bytes left to read a tag 113 if ((offset + 5) >= _data.length) { 90 if (offset + 5 >= reader.getLength()) { 91 directory.addError("Too few bytes remain for a valid IPTC tag"); 114 92 break; 115 93 } 116 94 … … 120 98 int tagType; 121 99 int tagByteCount; 122 100 try { 123 directoryType = _data[offset++]; 124 tagType = _data[offset++]; 125 tagByteCount = get32Bits(offset); 126 } catch (MetadataException e) { 127 directory.addError("Iptc data segment ended mid-way through tag descriptor"); 128 return metadata; 101 directoryType = reader.getUInt8(offset++); 102 tagType = reader.getUInt8(offset++); 103 tagByteCount = reader.getUInt16(offset); 104 offset += 2; 105 } catch (BufferBoundsException e) { 106 directory.addError("IPTC data segment ended mid-way through tag descriptor"); 107 return; 129 108 } 130 offset += 2; 131 if ( (offset + tagByteCount) > _data.length) {132 directory.addError(" data for tag extends beyond end of iptcsegment");109 110 if (offset + tagByteCount > reader.getLength()) { 111 directory.addError("Data for tag extends beyond end of IPTC segment"); 133 112 break; 134 113 } 135 114 136 processTag(directory, directoryType, tagType, offset, tagByteCount); 115 try { 116 processTag(reader, directory, directoryType, tagType, offset, tagByteCount); 117 } catch (BufferBoundsException e) { 118 directory.addError("Error processing IPTC tag"); 119 break; 120 } 121 137 122 offset += tagByteCount; 138 123 } 139 140 return metadata;141 124 } 142 125 143 /** 144 * Returns an int calculated from two bytes of data at the specified offset (MSB, LSB). 145 * @param offset position within the data buffer to read first byte 146 * @return the 32 bit int value, between 0x0000 and 0xFFFF 147 */ 148 private int get32Bits(int offset) throws MetadataException 126 private void processTag(@NotNull BufferReader reader, @NotNull Directory directory, int directoryType, int tagType, int offset, int tagByteCount) throws BufferBoundsException 149 127 { 150 if (offset >= _data.length) {151 throw new MetadataException("Attempt to read bytes from outside Iptc data buffer");152 }153 return ((_data[offset] & 255) << 8) | (_data[offset + 1] & 255);154 }155 156 /**157 * This method serves as marsheller of objects for dataset. It converts from IPTC158 * octets to relevant java object.159 */160 private void processTag(Directory directory, int directoryType, int tagType, int offset, int tagByteCount)161 {162 128 int tagIdentifier = tagType | (directoryType << 8); 163 129 164 130 switch (tagIdentifier) { 165 case IptcDirectory.TAG_ RECORD_VERSION:131 case IptcDirectory.TAG_APPLICATION_RECORD_VERSION: 166 132 // short 167 short shortValue = (short)((_data[offset] << 8) | _data[offset + 1]);133 int shortValue = reader.getUInt16(offset); 168 134 directory.setInt(tagIdentifier, shortValue); 169 135 return; 170 136 case IptcDirectory.TAG_URGENCY: 171 137 // byte 172 directory.setInt(tagIdentifier, _data[offset]);138 directory.setInt(tagIdentifier, reader.getUInt8(offset)); 173 139 return; 174 140 case IptcDirectory.TAG_RELEASE_DATE: 175 141 case IptcDirectory.TAG_DATE_CREATED: 176 142 // Date object 177 143 if (tagByteCount >= 8) { 178 String dateStr = new String(_data,offset, tagByteCount);144 String dateStr = reader.getString(offset, tagByteCount); 179 145 try { 180 146 int year = Integer.parseInt(dateStr.substring(0, 4)); 181 147 int month = Integer.parseInt(dateStr.substring(4, 6)) - 1; 182 148 int day = Integer.parseInt(dateStr.substring(6, 8)); 183 Date date = (new java.util.GregorianCalendar(year, month, day)).getTime();149 Date date = new java.util.GregorianCalendar(year, month, day).getTime(); 184 150 directory.setDate(tagIdentifier, date); 185 151 return; 186 152 } catch (NumberFormatException e) { … … 193 159 default: 194 160 // fall through 195 161 } 196 // If no special handling by now, treat it as a string 162 163 // If we haven't returned yet, treat it as a string 197 164 String str; 198 165 if (tagByteCount < 1) { 199 166 str = ""; 200 167 } else { 201 str = new String(_data, offset, tagByteCount);168 str = reader.getString(offset, tagByteCount, System.getProperty("file.encoding")); // "ISO-8859-1" 202 169 } 170 203 171 if (directory.containsTag(tagIdentifier)) { 204 String[] oldStrings; 172 // this fancy string[] business avoids using an ArrayList for performance reasons 173 String[] oldStrings = directory.getStringArray(tagIdentifier); 205 174 String[] newStrings; 206 try {207 oldStrings = directory.getStringArray(tagIdentifier);208 } catch (MetadataException e) {209 oldStrings = null;210 }211 175 if (oldStrings == null) { 212 176 newStrings = new String[1]; 213 177 } else { 214 178 newStrings = new String[oldStrings.length + 1]; 215 for (int i = 0; i < oldStrings.length; i++) { 216 newStrings[i] = oldStrings[i]; 217 } 179 System.arraycopy(oldStrings, 0, newStrings, 0, oldStrings.length); 218 180 } 219 181 newStrings[newStrings.length - 1] = str; 220 182 directory.setStringArray(tagIdentifier, newStrings); -
src/com/drew/metadata/jpeg/JpegCommentDescriptor.java
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/ 14 * 15 * Created by dnoakes on Oct 10, 2003 using IntelliJ IDEA. 16 */ 17 package com.drew.metadata.jpeg; 18 19 import com.drew.metadata.Directory; 20 import com.drew.metadata.TagDescriptor; 21 22 /** 23 * 24 * @author Drew Noakes http://drewnoakes.com 25 */ 26 public class JpegCommentDescriptor extends TagDescriptor 27 { 28 public JpegCommentDescriptor(Directory directory) 29 { 30 super(directory); 31 } 32 33 public String getDescription(int tagType) 34 { 35 return _directory.getString(tagType); 36 } 37 } 1 /* 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/ 20 */ 21 package com.drew.metadata.jpeg; 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 25 import com.drew.metadata.TagDescriptor; 26 27 /** 28 * Provides human-readable string representations of tag values stored in a <code>JpegCommentDirectory</code>. 29 * 30 * @author Drew Noakes http://drewnoakes.com 31 */ 32 public class JpegCommentDescriptor extends TagDescriptor<JpegCommentDirectory> 33 { 34 public JpegCommentDescriptor(@NotNull JpegCommentDirectory directory) 35 { 36 super(directory); 37 } 38 39 @Nullable 40 public String getJpegCommentDescription() 41 { 42 return _directory.getString(JpegCommentDirectory.TAG_JPEG_COMMENT); 43 } 44 } -
src/com/drew/metadata/jpeg/JpegCommentDirectory.java
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/ 14 * 15 * Created by dnoakes on Oct 10, 2003 using IntelliJ IDEA. 16 */ 17 package com.drew.metadata.jpeg; 18 19 import com.drew.metadata.Directory; 20 21 import java.util.HashMap; 22 23 /** 24 * 25 * @author Drew Noakes http://drewnoakes.com 26 */ 27 public class JpegCommentDirectory extends Directory { 28 29 /** This is in bits/sample, usually 8 (12 and 16 not supported by most software). */ 30 public static final int TAG_JPEG_COMMENT = 0; 31 32 protected static final HashMap tagNameMap = new HashMap(); 33 34 static { 35 tagNameMap.put(new Integer(TAG_JPEG_COMMENT), "Jpeg Comment"); 36 } 37 38 public JpegCommentDirectory() { 39 this.setDescriptor(new JpegCommentDescriptor(this)); 40 } 41 42 public String getName() { 43 return "JpegComment"; 44 } 45 46 protected HashMap getTagNameMap() { 47 return tagNameMap; 48 } 49 } 1 /* 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/ 20 */ 21 package com.drew.metadata.jpeg; 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.metadata.Directory; 25 26 import java.util.HashMap; 27 28 /** 29 * Describes tags used by a JPEG file comment. 30 * 31 * @author Drew Noakes http://drewnoakes.com 32 */ 33 public class JpegCommentDirectory extends Directory 34 { 35 /** 36 * This value does not apply to a particular standard. Rather, this value has been fabricated to maintain 37 * consistency with other directory types. 38 */ 39 public static final int TAG_JPEG_COMMENT = 0; 40 41 @NotNull 42 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 43 44 static { 45 _tagNameMap.put(TAG_JPEG_COMMENT, "Jpeg Comment"); 46 } 47 48 public JpegCommentDirectory() 49 { 50 this.setDescriptor(new JpegCommentDescriptor(this)); 51 } 52 53 @NotNull 54 public String getName() 55 { 56 return "JpegComment"; 57 } 58 59 @NotNull 60 protected HashMap<Integer, String> getTagNameMap() 61 { 62 return _tagNameMap; 63 } 64 } -
src/com/drew/metadata/jpeg/JpegCommentReader.java
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/ 14 * 15 * Created by dnoakes on Oct 10, 2003 using IntelliJ IDEA. 16 */ 17 package com.drew.metadata.jpeg; 18 19 import com.drew.imaging.jpeg.JpegProcessingException; 20 import com.drew.imaging.jpeg.JpegSegmentReader; 21 import com.drew.metadata.Metadata; 22 import com.drew.metadata.MetadataReader; 23 24 import java.io.File; 25 import java.io.InputStream; 26 27 /** 28 * 29 * @author Drew Noakes http://drewnoakes.com 30 */ 31 public class JpegCommentReader implements MetadataReader 32 { 33 /** 34 * The COM data segment. 35 */ 36 private final byte[] _data; 37 38 /** 39 * Creates a new JpegReader for the specified Jpeg jpegFile. 40 */ 41 public JpegCommentReader(File jpegFile) throws JpegProcessingException 42 { 43 this(new JpegSegmentReader(jpegFile).readSegment(JpegSegmentReader.SEGMENT_COM)); 44 } 45 46 /** Creates a JpegCommentReader for a JPEG stream. 47 * 48 * @param is JPEG stream. Stream will be closed. 49 */ 50 public JpegCommentReader(InputStream is) throws JpegProcessingException 51 { 52 this(new JpegSegmentReader(is).readSegment(JpegSegmentReader.SEGMENT_APPD)); 53 } 54 55 public JpegCommentReader(byte[] data) 56 { 57 _data = data; 58 } 59 60 /** 61 * Performs the Jpeg data extraction, returning a new instance of <code>Metadata</code>. 62 */ 63 public Metadata extract() 64 { 65 return extract(new Metadata()); 66 } 67 68 /** 69 * Performs the Jpeg data extraction, adding found values to the specified 70 * instance of <code>Metadata</code>. 71 */ 72 public Metadata extract(Metadata metadata) 73 { 74 if (_data==null) { 75 return metadata; 76 } 77 78 JpegCommentDirectory directory = (JpegCommentDirectory)metadata.getDirectory(JpegCommentDirectory.class); 79 80 directory.setString(JpegCommentDirectory.TAG_JPEG_COMMENT, new String(_data)); 81 82 return metadata; 83 } 84 } 1 /* 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/ 20 */ 21 package com.drew.metadata.jpeg; 22 23 import com.drew.lang.BufferBoundsException; 24 import com.drew.lang.BufferReader; 25 import com.drew.lang.annotations.NotNull; 26 import com.drew.metadata.Metadata; 27 import com.drew.metadata.MetadataReader; 28 29 /** 30 * Decodes the comment stored within Jpeg files, populating a <code>Metadata</code> object with tag values in a 31 * <code>JpegCommentDirectory</code>. 32 * 33 * @author Drew Noakes http://drewnoakes.com 34 */ 35 public class JpegCommentReader implements MetadataReader 36 { 37 /** 38 * Performs the Jpeg data extraction, adding found values to the specified 39 * instance of <code>Metadata</code>. 40 */ 41 public void extract(@NotNull final BufferReader reader, @NotNull Metadata metadata) 42 { 43 JpegCommentDirectory directory = metadata.getOrCreateDirectory(JpegCommentDirectory.class); 44 45 try { 46 directory.setString(JpegCommentDirectory.TAG_JPEG_COMMENT, reader.getString(0, (int)reader.getLength())); 47 } catch (BufferBoundsException e) { 48 directory.addError("Exception reading JPEG comment string"); 49 } 50 } 51 } -
src/com/drew/metadata/jpeg/JpegComponent.java
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/ 14 * 15 * Created by dnoakes on Oct 9, 17:04:07 using IntelliJ IDEA. 16 */ 17 package com.drew.metadata.jpeg; 18 19 import com.drew.metadata.MetadataException; 20 21 import java.io.Serializable; 22 23 /** 24 * Created by IntelliJ IDEA. 25 * User: dnoakes 26 * Date: 09-Oct-2003 27 * Time: 17:04:07 28 * To change this template use Options | File Templates. 29 */ 30 public class JpegComponent implements Serializable 31 { 32 private final int _componentId; 33 private final int _samplingFactorByte; 34 private final int _quantizationTableNumber; 35 36 public JpegComponent(int componentId, int samplingFactorByte, int quantizationTableNumber) 37 { 38 _componentId = componentId; 39 _samplingFactorByte = samplingFactorByte; 40 _quantizationTableNumber = quantizationTableNumber; 41 } 42 43 public int getComponentId() 44 { 45 return _componentId; 46 } 47 48 public String getComponentName() throws MetadataException 49 { 50 switch (_componentId) 51 { 52 case 1: 53 return "Y"; 54 case 2: 55 return "Cb"; 56 case 3: 57 return "Cr"; 58 case 4: 59 return "I"; 60 case 5: 61 return "Q"; 62 } 63 64 throw new MetadataException("Unsupported component id: " + _componentId); 65 } 66 67 public int getQuantizationTableNumber() 68 { 69 return _quantizationTableNumber; 70 } 71 72 public int getHorizontalSamplingFactor() 73 { 74 return _samplingFactorByte & 0x0F; 75 } 76 77 public int getVerticalSamplingFactor() 78 { 79 return (_samplingFactorByte>>4) & 0x0F; 80 } 81 } 1 /* 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/ 20 */ 21 package com.drew.metadata.jpeg; 22 23 import com.drew.lang.annotations.Nullable; 24 25 import java.io.Serializable; 26 27 /** 28 * Stores information about a Jpeg image component such as the component id, horiz/vert sampling factor and 29 * quantization table number. 30 * 31 * @author Drew Noakes http://drewnoakes.com 32 */ 33 public class JpegComponent implements Serializable 34 { 35 private static final long serialVersionUID = 61121257899091914L; 36 37 private final int _componentId; 38 private final int _samplingFactorByte; 39 private final int _quantizationTableNumber; 40 41 public JpegComponent(int componentId, int samplingFactorByte, int quantizationTableNumber) 42 { 43 _componentId = componentId; 44 _samplingFactorByte = samplingFactorByte; 45 _quantizationTableNumber = quantizationTableNumber; 46 } 47 48 public int getComponentId() 49 { 50 return _componentId; 51 } 52 53 /** 54 * Returns the component name (one of: Y, Cb, Cr, I, or Q) 55 * @return the component name 56 */ 57 @Nullable 58 public String getComponentName() 59 { 60 switch (_componentId) 61 { 62 case 1: 63 return "Y"; 64 case 2: 65 return "Cb"; 66 case 3: 67 return "Cr"; 68 case 4: 69 return "I"; 70 case 5: 71 return "Q"; 72 } 73 return null; 74 } 75 76 public int getQuantizationTableNumber() 77 { 78 return _quantizationTableNumber; 79 } 80 81 public int getHorizontalSamplingFactor() 82 { 83 return _samplingFactorByte & 0x0F; 84 } 85 86 public int getVerticalSamplingFactor() 87 { 88 return (_samplingFactorByte>>4) & 0x0F; 89 } 90 } -
src/com/drew/metadata/jpeg/JpegDescriptor.java
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.jpeg; 16 22 17 import com.drew. metadata.Directory;18 import com.drew. metadata.MetadataException;23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 19 25 import com.drew.metadata.TagDescriptor; 20 26 21 27 /** 22 28 * Provides human-readable string versions of the tags stored in a JpegDirectory. 23 29 * Thanks to Darrell Silver (www.darrellsilver.com) for the initial version of this class. 30 * 31 * @author Drew Noakes http://drewnoakes.com 24 32 */ 25 public class JpegDescriptor extends TagDescriptor 33 public class JpegDescriptor extends TagDescriptor<JpegDirectory> 26 34 { 27 public JpegDescriptor( Directory directory)35 public JpegDescriptor(@NotNull JpegDirectory directory) 28 36 { 29 37 super(directory); 30 38 } 31 39 32 public String getDescription(int tagType) throws MetadataException 40 @Nullable 41 public String getDescription(int tagType) 33 42 { 34 43 switch (tagType) 35 44 { 45 case JpegDirectory.TAG_JPEG_COMPRESSION_TYPE: 46 return getImageCompressionTypeDescription(); 36 47 case JpegDirectory.TAG_JPEG_COMPONENT_DATA_1: 37 48 return getComponentDataDescription(0); 38 49 case JpegDirectory.TAG_JPEG_COMPONENT_DATA_2: … … 47 58 return getImageHeightDescription(); 48 59 case JpegDirectory.TAG_JPEG_IMAGE_WIDTH: 49 60 return getImageWidthDescription(); 61 default: 62 return super.getDescription(tagType); 50 63 } 64 } 51 65 52 return _directory.getString(tagType); 66 @Nullable 67 public String getImageCompressionTypeDescription() 68 { 69 Integer value = _directory.getInteger(JpegDirectory.TAG_JPEG_COMPRESSION_TYPE); 70 if (value==null) 71 return null; 72 // Note there is no 2 or 12 73 switch (value) { 74 case 0: return "Baseline"; 75 case 1: return "Extended sequential, Huffman"; 76 case 2: return "Progressive, Huffman"; 77 case 3: return "Lossless, Huffman"; 78 case 5: return "Differential sequential, Huffman"; 79 case 6: return "Differential progressive, Huffman"; 80 case 7: return "Differential lossless, Huffman"; 81 case 8: return "Reserved for JPEG extensions"; 82 case 9: return "Extended sequential, arithmetic"; 83 case 10: return "Progressive, arithmetic"; 84 case 11: return "Lossless, arithmetic"; 85 case 13: return "Differential sequential, arithmetic"; 86 case 14: return "Differential progressive, arithmetic"; 87 case 15: return "Differential lossless, arithmetic"; 88 default: 89 return "Unknown type: "+ value; 90 } 53 91 } 54 92 @Nullable 55 93 public String getImageWidthDescription() 56 94 { 57 return _directory.getString(JpegDirectory.TAG_JPEG_IMAGE_WIDTH) + " pixels"; 95 final String value = _directory.getString(JpegDirectory.TAG_JPEG_IMAGE_WIDTH); 96 if (value==null) 97 return null; 98 return value + " pixels"; 58 99 } 59 100 101 @Nullable 60 102 public String getImageHeightDescription() 61 103 { 62 return _directory.getString(JpegDirectory.TAG_JPEG_IMAGE_HEIGHT) + " pixels"; 104 final String value = _directory.getString(JpegDirectory.TAG_JPEG_IMAGE_HEIGHT); 105 if (value==null) 106 return null; 107 return value + " pixels"; 63 108 } 64 109 110 @Nullable 65 111 public String getDataPrecisionDescription() 66 112 { 67 return _directory.getString(JpegDirectory.TAG_JPEG_DATA_PRECISION) + " bits"; 113 final String value = _directory.getString(JpegDirectory.TAG_JPEG_DATA_PRECISION); 114 if (value==null) 115 return null; 116 return value + " bits"; 68 117 } 69 118 70 public String getComponentDataDescription(int componentNumber) throws MetadataException 119 @Nullable 120 public String getComponentDataDescription(int componentNumber) 71 121 { 72 JpegComponent component = ((JpegDirectory)_directory).getComponent(componentNumber);122 JpegComponent value = _directory.getComponent(componentNumber); 73 123 74 if ( component==null)75 throw new MetadataException("No Jpeg component exists with number " + componentNumber);124 if (value==null) 125 return null; 76 126 77 StringBu ffer sb = new StringBuffer();78 sb.append( component.getComponentName());127 StringBuilder sb = new StringBuilder(); 128 sb.append(value.getComponentName()); 79 129 sb.append(" component: Quantization table "); 80 sb.append( component.getQuantizationTableNumber());130 sb.append(value.getQuantizationTableNumber()); 81 131 sb.append(", Sampling factors "); 82 sb.append( component.getHorizontalSamplingFactor());132 sb.append(value.getHorizontalSamplingFactor()); 83 133 sb.append(" horiz/"); 84 sb.append( component.getVerticalSamplingFactor());134 sb.append(value.getVerticalSamplingFactor()); 85 135 sb.append(" vert"); 86 136 return sb.toString(); 87 137 } -
src/com/drew/metadata/jpeg/JpegDirectory.java
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 on Aug 2, 2003. 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.jpeg; 18 22 23 import com.drew.lang.annotations.NotNull; 24 import com.drew.lang.annotations.Nullable; 19 25 import com.drew.metadata.Directory; 20 26 import com.drew.metadata.MetadataException; 21 27 … … 23 29 24 30 /** 25 31 * Directory of tags and values for the SOF0 Jpeg segment. This segment holds basic metadata about the image. 26 * @author Darrell Silver http://www.darrellsilver.com and Drew Noakes 32 * 33 * @author Darrell Silver http://www.darrellsilver.com and Drew Noakes http://drewnoakes.com 27 34 */ 28 public class JpegDirectory extends Directory { 35 public class JpegDirectory extends Directory 36 { 37 public static final int TAG_JPEG_COMPRESSION_TYPE = -3; 38 /** This is in bits/sample, usually 8 (12 and 16 not supported by most software). */ 39 public static final int TAG_JPEG_DATA_PRECISION = 0; 40 /** The image's height. Necessary for decoding the image, so it should always be there. */ 41 public static final int TAG_JPEG_IMAGE_HEIGHT = 1; 42 /** The image's width. Necessary for decoding the image, so it should always be there. */ 43 public static final int TAG_JPEG_IMAGE_WIDTH = 3; 44 /** 45 * Usually 1 = grey scaled, 3 = color YcbCr or YIQ, 4 = color CMYK 46 * Each component TAG_COMPONENT_DATA_[1-4], has the following meaning: 47 * component Id(1byte)(1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q), 48 * sampling factors (1byte) (bit 0-3 vertical., 4-7 horizontal.), 49 * quantization table number (1 byte). 50 * <p/> 51 * This info is from http://www.funducode.com/freec/Fileformats/format3/format3b.htm 52 */ 53 public static final int TAG_JPEG_NUMBER_OF_COMPONENTS = 5; 29 54 30 /** This is in bits/sample, usually 8 (12 and 16 not supported by most software). */31 public static final int TAG_JPEG_DATA_PRECISION = 0;32 /** The image's height. Necessary for decoding the image, so it should always be there. */33 public static final int TAG_JPEG_IMAGE_HEIGHT = 1;34 /** The image's width. Necessary for decoding the image, so it should always be there. */35 public static final int TAG_JPEG_IMAGE_WIDTH = 3;36 /** Usually 1 = grey scaled, 3 = color YcbCr or YIQ, 4 = color CMYK37 * Each component TAG_COMPONENT_DATA_[1-4], has the following meaning:38 * component Id(1byte)(1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q),39 * sampling factors (1byte) (bit 0-3 vertical., 4-7 horizontal.),40 * quantization table number (1 byte).41 * <p>42 * This info is from http://www.funducode.com/freec/Fileformats/format3/format3b.htm43 */44 public static final int TAG_JPEG_NUMBER_OF_COMPONENTS = 5;45 46 55 // NOTE! Component tag type int values must increment in steps of 1 47 56 48 /** the first of a possible 4 color components. Number of components specified in TAG_JPEG_NUMBER_OF_COMPONENTS.*/49 50 /** the second of a possible 4 color components. Number of components specified in TAG_JPEG_NUMBER_OF_COMPONENTS.*/51 52 /** the third of a possible 4 color components. Number of components specified in TAG_JPEG_NUMBER_OF_COMPONENTS.*/53 54 /** the fourth of a possible 4 color components. Number of components specified in TAG_JPEG_NUMBER_OF_COMPONENTS.*/55 57 /** the first of a possible 4 color components. Number of components specified in TAG_JPEG_NUMBER_OF_COMPONENTS. */ 58 public static final int TAG_JPEG_COMPONENT_DATA_1 = 6; 59 /** the second of a possible 4 color components. Number of components specified in TAG_JPEG_NUMBER_OF_COMPONENTS. */ 60 public static final int TAG_JPEG_COMPONENT_DATA_2 = 7; 61 /** the third of a possible 4 color components. Number of components specified in TAG_JPEG_NUMBER_OF_COMPONENTS. */ 62 public static final int TAG_JPEG_COMPONENT_DATA_3 = 8; 63 /** the fourth of a possible 4 color components. Number of components specified in TAG_JPEG_NUMBER_OF_COMPONENTS. */ 64 public static final int TAG_JPEG_COMPONENT_DATA_4 = 9; 56 65 57 protected static final HashMap tagNameMap = new HashMap(); 66 @NotNull 67 protected static final HashMap<Integer, String> _tagNameMap = new HashMap<Integer, String>(); 58 68 59 static { 60 tagNameMap.put(new Integer(TAG_JPEG_DATA_PRECISION), "Data Precision"); 61 tagNameMap.put(new Integer(TAG_JPEG_IMAGE_WIDTH), "Image Width"); 62 tagNameMap.put(new Integer(TAG_JPEG_IMAGE_HEIGHT), "Image Height"); 63 tagNameMap.put(new Integer(TAG_JPEG_NUMBER_OF_COMPONENTS), "Number of Components"); 64 tagNameMap.put(new Integer(TAG_JPEG_COMPONENT_DATA_1), "Component 1"); 65 tagNameMap.put(new Integer(TAG_JPEG_COMPONENT_DATA_2), "Component 2"); 66 tagNameMap.put(new Integer(TAG_JPEG_COMPONENT_DATA_3), "Component 3"); 67 tagNameMap.put(new Integer(TAG_JPEG_COMPONENT_DATA_4), "Component 4"); 68 } 69 static { 70 _tagNameMap.put(TAG_JPEG_COMPRESSION_TYPE, "Compression Type"); 71 _tagNameMap.put(TAG_JPEG_DATA_PRECISION, "Data Precision"); 72 _tagNameMap.put(TAG_JPEG_IMAGE_WIDTH, "Image Width"); 73 _tagNameMap.put(TAG_JPEG_IMAGE_HEIGHT, "Image Height"); 74 _tagNameMap.put(TAG_JPEG_NUMBER_OF_COMPONENTS, "Number of Components"); 75 _tagNameMap.put(TAG_JPEG_COMPONENT_DATA_1, "Component 1"); 76 _tagNameMap.put(TAG_JPEG_COMPONENT_DATA_2, "Component 2"); 77 _tagNameMap.put(TAG_JPEG_COMPONENT_DATA_3, "Component 3"); 78 _tagNameMap.put(TAG_JPEG_COMPONENT_DATA_4, "Component 4"); 79 } 69 80 70 public JpegDirectory() { 71 this.setDescriptor(new JpegDescriptor(this)); 72 } 81 public JpegDirectory() 82 { 83 this.setDescriptor(new JpegDescriptor(this)); 84 } 73 85 74 public String getName() { 75 return "Jpeg"; 76 } 86 @NotNull 87 public String getName() 88 { 89 return "Jpeg"; 90 } 77 91 78 protected HashMap getTagNameMap() { 79 return tagNameMap; 80 } 92 @NotNull 93 protected HashMap<Integer, String> getTagNameMap() 94 { 95 return _tagNameMap; 96 } 81 97 82 98 /** 83 *84 99 * @param componentNumber The zero-based index of the component. This number is normally between 0 and 3. 85 * Use getNumberOfComponents for bounds-checking.86 * @return 100 * Use getNumberOfComponents for bounds-checking. 101 * @return the JpegComponent having the specified number. 87 102 */ 103 @Nullable 88 104 public JpegComponent getComponent(int componentNumber) 89 105 { 90 106 int tagType = JpegDirectory.TAG_JPEG_COMPONENT_DATA_1 + componentNumber; 91 92 JpegComponent component = (JpegComponent)getObject(tagType); 93 94 return component; 107 return (JpegComponent)getObject(tagType); 95 108 } 96 109 97 110 public int getImageWidth() throws MetadataException -
src/com/drew/metadata/jpeg/JpegReader.java
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 Aug 2, 2003 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.jpeg; 18 22 19 import com.drew.imaging.jpeg.JpegProcessingException; 20 import com.drew.imaging.jpeg.JpegSegmentReader; 23 import com.drew.lang.BufferBoundsException; 24 import com.drew.lang.BufferReader; 25 import com.drew.lang.annotations.NotNull; 21 26 import com.drew.metadata.Metadata; 22 import com.drew.metadata.MetadataException;23 27 import com.drew.metadata.MetadataReader; 24 28 25 import java.io.File;26 import java.io.InputStream;27 28 29 /** 30 * Decodes Jpeg SOF0 data, populating a <code>Metadata</code> object with tag values in a <code>JpegDirectory</code>. 29 31 * 30 * @author Darrell Silver http://www.darrellsilver.com and Drew Noakes 32 * @author Darrell Silver http://www.darrellsilver.com and Drew Noakes http://drewnoakes.com 31 33 */ 32 34 public class JpegReader implements MetadataReader 33 35 { 34 36 /** 35 * The SOF0 data segment.36 */37 private final byte[] _data;38 39 /**40 * Creates a new JpegReader for the specified Jpeg jpegFile.41 */42 public JpegReader(File jpegFile) throws JpegProcessingException43 {44 this(new JpegSegmentReader(jpegFile).readSegment(JpegSegmentReader.SEGMENT_SOF0));45 }46 47 /** Creates a JpegReader for a JPEG stream.48 *49 * @param is JPEG stream. Stream will be closed.50 */51 public JpegReader(InputStream is) throws JpegProcessingException52 {53 this(new JpegSegmentReader(is).readSegment(JpegSegmentReader.SEGMENT_APPD));54 }55 56 public JpegReader(byte[] data)57 {58 _data = data;59 }60 61 /**62 * Performs the Jpeg data extraction, returning a new instance of <code>Metadata</code>.63 */64 public Metadata extract()65 {66 return extract(new Metadata());67 }68 69 /**70 37 * Performs the Jpeg data extraction, adding found values to the specified 71 38 * instance of <code>Metadata</code>. 72 39 */ 73 public Metadata extract(Metadata metadata)40 public void extract(@NotNull final BufferReader reader, @NotNull Metadata metadata) 74 41 { 75 if (_data==null) { 76 return metadata; 77 } 42 JpegDirectory directory = metadata.getOrCreateDirectory(JpegDirectory.class); 78 43 79 JpegDirectory directory = (JpegDirectory)metadata.getDirectory(JpegDirectory.class);80 81 44 try { 82 45 // data precision 83 int dataPrecision = get16Bits(JpegDirectory.TAG_JPEG_DATA_PRECISION);46 int dataPrecision = reader.getUInt8(JpegDirectory.TAG_JPEG_DATA_PRECISION); 84 47 directory.setInt(JpegDirectory.TAG_JPEG_DATA_PRECISION, dataPrecision); 85 48 86 49 // process height 87 int height = get32Bits(JpegDirectory.TAG_JPEG_IMAGE_HEIGHT);50 int height = reader.getUInt16(JpegDirectory.TAG_JPEG_IMAGE_HEIGHT); 88 51 directory.setInt(JpegDirectory.TAG_JPEG_IMAGE_HEIGHT, height); 89 52 90 53 // process width 91 int width = get32Bits(JpegDirectory.TAG_JPEG_IMAGE_WIDTH);54 int width = reader.getUInt16(JpegDirectory.TAG_JPEG_IMAGE_WIDTH); 92 55 directory.setInt(JpegDirectory.TAG_JPEG_IMAGE_WIDTH, width); 93 56 94 57 // number of components 95 int numberOfComponents = get16Bits(JpegDirectory.TAG_JPEG_NUMBER_OF_COMPONENTS);58 int numberOfComponents = reader.getUInt8(JpegDirectory.TAG_JPEG_NUMBER_OF_COMPONENTS); 96 59 directory.setInt(JpegDirectory.TAG_JPEG_NUMBER_OF_COMPONENTS, numberOfComponents); 97 60 98 61 // for each component, there are three bytes of data: … … 100 63 // 2 - Sampling factors: bit 0-3 vertical, 4-7 horizontal 101 64 // 3 - Quantization table number 102 65 int offset = 6; 103 for (int i=0; i<numberOfComponents; i++) 104 { 105 int componentId = get16Bits(offset++); 106 int samplingFactorByte = get16Bits(offset++); 107 int quantizationTableNumber = get16Bits(offset++); 66 for (int i = 0; i < numberOfComponents; i++) { 67 int componentId = reader.getUInt8(offset++); 68 int samplingFactorByte = reader.getUInt8(offset++); 69 int quantizationTableNumber = reader.getUInt8(offset++); 108 70 JpegComponent component = new JpegComponent(componentId, samplingFactorByte, quantizationTableNumber); 109 71 directory.setObject(JpegDirectory.TAG_JPEG_COMPONENT_DATA_1 + i, component); 110 72 } 111 73 112 } catch ( MetadataException me) {113 directory.addError( "MetadataException: " + me);74 } catch (BufferBoundsException ex) { 75 directory.addError(ex.getMessage()); 114 76 } 115 116 return metadata;117 77 } 118 119 /**120 * Returns an int calculated from two bytes of data at the specified offset (MSB, LSB).121 * @param offset position within the data buffer to read first byte122 * @return the 32 bit int value, between 0x0000 and 0xFFFF123 */124 private int get32Bits(int offset) throws MetadataException125 {126 if (offset+1>=_data.length) {127 throw new MetadataException("Attempt to read bytes from outside Jpeg segment data buffer");128 }129 130 return ((_data[offset] & 255) << 8) | (_data[offset + 1] & 255);131 }132 133 /**134 * Returns an int calculated from one byte of data at the specified offset.135 * @param offset position within the data buffer to read byte136 * @return the 16 bit int value, between 0x00 and 0xFF137 */138 private int get16Bits(int offset) throws MetadataException139 {140 if (offset>=_data.length) {141 throw new MetadataException("Attempt to read bytes from outside Jpeg segment data buffer");142 }143 144 return (_data[offset] & 255);145 }146 78 } -
src/com/drew/metadata/Metadata.java
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 { 42 @NotNull 43 private final Map<Class<? extends Directory>,Directory> _directoryByClass = new HashMap<Class<? extends Directory>, Directory>(); 44 43 45 /** 44 *45 */46 private final HashMap directoryMap;47 48 /**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(); 60 return _directoryList; 61 61 } 62 62 63 64 // OTHER METHODS65 66 63 /** 67 * Creates an Iterator over the tag types set against this image, preserving the order68 * in which they were set. Should the same tag have been set more than once, it's first69 * position is maintained, even though the final value is used.70 * @return an Iterator of tag types set for this image71 */72 public Iterator getDirectoryIterator()73 {74 return directoryList.iterator();75 }76 77 /**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 86 73 /** 87 74 * Returns a <code>Directory</code> of specified type. If this <code>Metadata</code> object already contains 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(); 105 95 } catch (Exception e) { 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; 112 103 } 113 104 114 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); 121 } 122 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); 123 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; 148 } 124 149 } -
src/com/drew/metadata/MetadataException.java
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); 39 48 } -
src/com/drew/metadata/MetadataReader.java
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 } -
src/com/drew/metadata/SampleUsage.java
1 /*2 * This is public domain software - that is, you can do whatever you want3 * with it, and include it software that is licensed under the GNU or the4 * BSD license, or whatever other licence you choose, including proprietary5 * 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 the8 * 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.com12 * Latest version of this software kept at13 * http://drewnoakes.com/14 *15 * Created by dnoakes on 05-Nov-2002 18:57:14 using IntelliJ IDEA.16 */17 package com.drew.metadata;18 19 import com.drew.imaging.jpeg.JpegMetadataReader;20 import com.drew.imaging.jpeg.JpegProcessingException;21 import com.drew.imaging.jpeg.JpegSegmentReader;22 import com.drew.metadata.exif.ExifReader;23 import com.drew.metadata.iptc.IptcReader;24 25 import java.awt.image.BufferedImage;26 import java.io.File;27 import java.io.FileInputStream;28 import java.io.FileNotFoundException;29 import java.io.IOException;30 import java.util.Iterator;31 32 /**33 *34 */35 public class SampleUsage36 {37 /**38 * Constructor which executes multiple sample usages, each of which return the same output. This class showcases39 * multiple usages of this metadata class library.40 * @param fileName path to a jpeg file upon which to operate41 */42 public SampleUsage(String fileName)43 {44 File jpegFile = new File(fileName);45 46 // There are multiple ways to get a Metadata object47 48 // Approach 149 // This approach reads all types of known Jpeg metadata (at present,50 // Exif and Iptc) in a single call. In most cases, this is the most51 // appropriate usage.52 try {53 Metadata metadata = JpegMetadataReader.readMetadata(jpegFile);54 printImageTags(1, metadata);55 } catch (JpegProcessingException e) {56 System.err.println("error 1a");57 }58 59 // Approach 260 // This approach shows using individual MetadataReader implementations61 // to read a file. This is less efficient than approach 1, as the file62 // is opened and read twice.63 try {64 Metadata metadata = new Metadata();65 new ExifReader(jpegFile).extract(metadata);66 new IptcReader(jpegFile).extract(metadata);67 printImageTags(2, metadata);68 } catch (JpegProcessingException jpe) {69 System.err.println("error 2a");70 }71 72 // Approach 373 // As fast as approach 1 (this is what goes on inside the JpegMetadataReader's74 // readMetadata() method), this code is handy if you want to look into other75 // Jpeg segments too.76 try {77 JpegSegmentReader segmentReader = new JpegSegmentReader(jpegFile);78 byte[] exifSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_APP1);79 byte[] iptcSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_APPD);80 Metadata metadata = new Metadata();81 new ExifReader(exifSegment).extract(metadata);82 new IptcReader(iptcSegment).extract(metadata);83 printImageTags(3, metadata);84 } catch (JpegProcessingException jpe) {85 System.err.println("error 3a");86 }87 }88 89 private void printImageTags(int approachCount, Metadata metadata)90 {91 System.out.println();92 System.out.println("*** APPROACH " + approachCount + " ***");93 System.out.println();94 // iterate over the exif data and print to System.out95 Iterator directories = metadata.getDirectoryIterator();96 while (directories.hasNext()) {97 Directory directory = (Directory)directories.next();98 Iterator tags = directory.getTagIterator();99 while (tags.hasNext()) {100 Tag tag = (Tag)tags.next();101 System.out.println(tag);102 }103 if (directory.hasErrors()) {104 Iterator errors = directory.getErrors();105 while (errors.hasNext()) {106 System.out.println("ERROR: " + errors.next());107 }108 }109 }110 }111 112 /**113 * Executes the sample usage program.114 * @param args command line parameters115 */116 public static void main(String[] args)117 {118 new SampleUsage("src/com/drew/metadata/test/withIptcExifGps.jpg");119 }120 } -
src/com/drew/metadata/Tag.java
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; 32 41 _directory = directory; … … 34 43 35 44 /** 36 45 * Gets the tag type as an int 46 * 37 47 * @return the tag type as an int 38 48 */ 39 49 public int getTagType() … … 44 54 /** 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 { 51 63 String hex = Integer.toHexString(_tagType); … … 56 68 /** 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); 64 78 } … … 66 80 /** 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 { 73 89 return _directory.getTagName(_tagType); … … 76 92 /** 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 { 83 101 return _directory.getName(); 84 102 } 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 } 101 117 } -
src/com/drew/metadata/TagDescriptor.java
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; 18 25 26 import java.lang.reflect.Array; 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; 31 43 } … … 33 45 /** 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 } -
src/org/openstreetmap/josm/gui/layer/geoimage/GeoImageLayer.java
68 68 import com.drew.metadata.Directory; 69 69 import com.drew.metadata.Metadata; 70 70 import com.drew.metadata.MetadataException; 71 import com.drew.metadata.exif.Exif Directory;71 import com.drew.metadata.exif.ExifIFD0Directory; 72 72 import com.drew.metadata.exif.GpsDirectory; 73 73 74 74 public class GeoImageLayer extends Layer implements PropertyChangeListener, JumpToMarkerLayer { … … 510 510 double deg; 511 511 double min, sec; 512 512 double lon, lat; 513 Metadata metadata = null; 514 Directory dirExif = null, dirGps = null; 513 Metadata metadata; 514 Directory dirExif; 515 GpsDirectory dirGps; 515 516 516 517 try { 517 518 metadata = JpegMetadataReader.readMetadata(e.getFile()); 518 dirExif = metadata.getDirectory(Exif Directory.class);519 dirExif = metadata.getDirectory(ExifIFD0Directory.class); 519 520 dirGps = metadata.getDirectory(GpsDirectory.class); 520 521 } catch (CompoundException p) { 521 522 e.setExifCoor(null); 522 523 e.setPos(null); 523 524 return; 525 } catch (IOException p) { 526 e.setExifCoor(null); 527 e.setPos(null); 528 return; 524 529 } 525 530 526 531 try { 527 int orientation = dirExif.getInt(Exif Directory.TAG_ORIENTATION);532 int orientation = dirExif.getInt(ExifIFD0Directory.TAG_ORIENTATION); 528 533 e.setExifOrientation(orientation); 529 534 } catch (MetadataException ex) { 530 535 } … … 543 548 // longitude 544 549 545 550 Rational[] components = dirGps.getRationalArray(GpsDirectory.TAG_GPS_LONGITUDE); 551 if (components != null) { 552 deg = components[0].doubleValue(); 553 min = components[1].doubleValue(); 554 sec = components[2].doubleValue(); 546 555 547 deg = components[0].doubleValue(); 548 min = components[1].doubleValue(); 549 sec = components[2].doubleValue(); 556 if (Double.isNaN(deg) && Double.isNaN(min) && Double.isNaN(sec)) 557 throw new IllegalArgumentException(); 550 558 551 if (Double.isNaN(deg) && Double.isNaN(min) && Double.isNaN(sec)) 552 throw new IllegalArgumentException(); 559 lon = (Double.isNaN(deg) ? 0 : deg + (Double.isNaN(min) ? 0 : (min / 60)) + (Double.isNaN(sec) ? 0 : (sec / 3600))); 553 560 554 lon = (Double.isNaN(deg) ? 0 : deg + (Double.isNaN(min) ? 0 : (min / 60)) + (Double.isNaN(sec) ? 0 : (sec / 3600))); 555 556 if (dirGps.getString(GpsDirectory.TAG_GPS_LONGITUDE_REF).charAt(0) == 'W') { 557 lon = -lon; 561 if (dirGps.getString(GpsDirectory.TAG_GPS_LONGITUDE_REF).charAt(0) == 'W') { 562 lon = -lon; 563 } 564 } else { 565 // Try to read lon/lat as double value (Nonstandard, created by some cameras -> #5220) 566 lon = dirGps.getDouble(GpsDirectory.TAG_GPS_LONGITUDE); 558 567 } 559 568 560 569 // latitude 561 570 562 571 components = dirGps.getRationalArray(GpsDirectory.TAG_GPS_LATITUDE); 572 if (components != null) { 573 deg = components[0].doubleValue(); 574 min = components[1].doubleValue(); 575 sec = components[2].doubleValue(); 563 576 564 deg = components[0].doubleValue(); 565 min = components[1].doubleValue(); 566 sec = components[2].doubleValue(); 577 if (Double.isNaN(deg) && Double.isNaN(min) && Double.isNaN(sec)) 578 throw new IllegalArgumentException(); 567 579 568 if (Double.isNaN(deg) && Double.isNaN(min) && Double.isNaN(sec)) 569 throw new IllegalArgumentException(); 580 lat = (Double.isNaN(deg) ? 0 : deg + (Double.isNaN(min) ? 0 : (min / 60)) + (Double.isNaN(sec) ? 0 : (sec / 3600))); 570 581 571 lat = (Double.isNaN(deg) ? 0 : deg + (Double.isNaN(min) ? 0 : (min / 60)) + (Double.isNaN(sec) ? 0 : (sec / 3600))); 582 if (Double.isNaN(lat)) 583 throw new IllegalArgumentException(); 572 584 573 if (Double.isNaN(lat))574 throw new IllegalArgumentException();575 576 if (dirGps.getString(GpsDirectory.TAG_GPS_LATITUDE_REF).charAt(0) == 'S'){577 lat = -lat;585 if (dirGps.getString(GpsDirectory.TAG_GPS_LATITUDE_REF).charAt(0) == 'S') { 586 lat = -lat; 587 } 588 } else { 589 lat = dirGps.getDouble(GpsDirectory.TAG_GPS_LATITUDE); 578 590 } 579 591 580 592 // Store values … … 582 594 e.setExifCoor(new LatLon(lat, lon)); 583 595 e.setPos(e.getExifCoor()); 584 596 585 } catch (CompoundException p) {586 // Try to read lon/lat as double value (Nonstandard, created by some cameras -> #5220)587 try {588 Double longitude = dirGps.getDouble(GpsDirectory.TAG_GPS_LONGITUDE);589 Double latitude = dirGps.getDouble(GpsDirectory.TAG_GPS_LATITUDE);590 if (longitude == null || latitude == null)591 throw new CompoundException("");592 593 // Store values594 595 e.setExifCoor(new LatLon(latitude, longitude));596 e.setPos(e.getExifCoor());597 } catch (CompoundException ex) {598 e.setExifCoor(null);599 e.setPos(null);600 }601 597 } catch (Exception ex) { // (other exceptions, e.g. #5271) 602 598 System.err.println("Error reading EXIF from file: "+ex); 603 599 e.setExifCoor(null); -
src/org/openstreetmap/josm/tools/ExifReader.java
2 2 package org.openstreetmap.josm.tools; 3 3 4 4 import java.io.File; 5 import java.io.IOException; 5 6 import java.text.ParseException; 6 7 import java.util.Date; 7 import java.util.Iterator;8 8 9 9 import com.drew.imaging.jpeg.JpegMetadataReader; 10 10 import com.drew.imaging.jpeg.JpegProcessingException; … … 12 12 import com.drew.metadata.Metadata; 13 13 import com.drew.metadata.MetadataException; 14 14 import com.drew.metadata.Tag; 15 import com.drew.metadata.exif.ExifDirectory; 15 import com.drew.metadata.exif.ExifIFD0Directory; 16 import com.drew.metadata.exif.ExifSubIFDDirectory; 16 17 17 18 /** 18 19 * Read out exif file information from a jpeg file … … 25 26 Metadata metadata = JpegMetadataReader.readMetadata(filename); 26 27 String dateStr = null; 27 28 OUTER: 28 for (Iterator<Directory> dirIt = metadata.getDirectoryIterator(); dirIt.hasNext();) { 29 for (Iterator<Tag> tagIt = dirIt.next().getTagIterator(); tagIt.hasNext();) { 30 Tag tag = tagIt.next(); 31 if (tag.getTagType() == ExifDirectory.TAG_DATETIME_ORIGINAL /* 0x9003 */) { 29 for (Directory dirIt : metadata.getDirectories()) { 30 for (Tag tag : dirIt.getTags()) { 31 if (tag.getTagType() == ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL /* 0x9003 */) { 32 32 dateStr = tag.getDescription(); 33 33 break OUTER; // prefer this tag 34 34 } 35 if (tag.getTagType() == Exif Directory.TAG_DATETIME /* 0x0132 */ ||36 tag.getTagType() == Exif Directory.TAG_DATETIME_DIGITIZED /* 0x9004 */) {35 if (tag.getTagType() == ExifIFD0Directory.TAG_DATETIME /* 0x0132 */ || 36 tag.getTagType() == ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED /* 0x9004 */) { 37 37 dateStr = tag.getDescription(); 38 38 } 39 39 } … … 54 54 Integer orientation = null; 55 55 try { 56 56 final Metadata metadata = JpegMetadataReader.readMetadata(filename); 57 final Directory dir = metadata.getDirectory(Exif Directory.class);58 orientation = dir.getInt(Exif Directory.TAG_ORIENTATION);57 final Directory dir = metadata.getDirectory(ExifIFD0Directory.class); 58 orientation = dir.getInt(ExifIFD0Directory.TAG_ORIENTATION); 59 59 } catch (JpegProcessingException e) { 60 60 e.printStackTrace(); 61 61 } catch (MetadataException e) { 62 62 e.printStackTrace(); 63 } catch (IOException e) { 64 e.printStackTrace(); 63 65 } 64 66 return orientation; 65 67 }